Skip to content

Commit

Permalink
Add Multi-Samba User Mode (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
HanLiQL authored Oct 9, 2024
1 parent b1c290b commit 83b5e3f
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 66 deletions.
43 changes: 32 additions & 11 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,45 @@ docker run -it --rm -p 445:445 -e "USER=samba" -e "PASS=secret" -v "/home/exampl

## Configuration ⚙️

* ### How do I modify the credentials?
### How do I modify the credentials?

You can set the `USER` and `PASS` environment variables to modify the credentials from their default values: user `samba` with password `secret`.
You can set the `USER` and `PASS` environment variables to modify the credentials from their default values: user `samba` with password `secret`.

* ### How do I modify the permissions?
### How do I modify the permissions?

You can set `UID` and `GID` environment variables to change the user and group ID.
You can set `UID` and `GID` environment variables to change the user and group ID.

To mark the share as read-only, add the variable `RW: false`.
To mark the share as read-only, add the variable `RW: false`.

* ### How do I modify other settings?
### How do I modify other settings?

If you need more advanced features, you can completely override the default configuration by modifying the [smb.conf](https://github.com/dockur/samba/blob/master/smb.conf) file in this repo, and binding your custom config to the container like this:
If you need more advanced features, you can completely override the default configuration by modifying the [smb.conf](https://github.com/dockur/samba/blob/master/smb.conf) file in this repo, and binding your custom config to the container like this:

```yaml
volumes:
- /example/smb.conf:/etc/samba/smb.conf
```
```yaml
volumes:
- /example/smb.conf:/etc/samba/smb.conf
```
### How do I use multiple users?
If you want to use multiple Samba users, you can enable multi-user mode by binding the [smb_user.conf](https://github.com/dockur/samba/blob/master/smb_user.conf) file to the container as follows:
```yaml
volumes:
- /example/smb.conf:/etc/samba/smb.conf
- /example/smb_user.conf:/etc/samba/smb_user.conf
```
You can also modify the [smb.conf](https://github.com/dockur/samba/blob/master/smb.conf) to implement different Samba policies for different users.
> [!NOTE]
> In this mode, you will need to manage the ownership and permissions of the shared folders yourself.
>
> In the [smb_user.conf](https://github.com/dockur/samba/blob/master/smb_user.conf) file, user configurations must follow a specific format. Each line should contain the user information in the following order:
>```yaml
>username:uid:groupname:gid:password
>```
>Each line represents the configuration for a single user, and the parameters must be separated by colons.

## Stars 🌟
[![Stars](https://starchart.cc/dockur/samba.svg?variant=adaptive)](https://starchart.cc/dockur/samba)
Expand Down
170 changes: 115 additions & 55 deletions samba.sh
Original file line number Diff line number Diff line change
@@ -1,84 +1,144 @@
#!/usr/bin/env bash
set -Eeuo pipefail

# Set variables for group and share directory
group="smb"
share="/storage"
secret="/run/secrets/pass"
# This function checks for the existence of a specified Samba user and group. If the user does not exist,
# it creates a new user with the provided username, user ID (UID), group name, group ID (GID), and password.
# If the user already exists, it updates the user's UID and group association as necessary,
# and updates the password in the Samba database. The function ensures that the group also exists,
# creating it if necessary, and modifies the group ID if it differs from the provided value.
add_user() {
local username="$1"
local uid="$2"
local groupname="$3"
local gid="$4"
local password="$5"

# Check if the smb group exists, if not, create it
if ! getent group "$groupname" &>/dev/null; then
echo "Group $groupname does not exist, creating group..."
groupadd -o -g "$gid" "$groupname" || { echo "Failed to create group $groupname"; return 1; }
else
# Check if the gid right,if not, change it
local current_gid
current_gid=$(getent group "$groupname" | cut -d: -f3)
if [[ "$current_gid" != "$gid" ]]; then
echo "Group $groupname exists but GID differs, updating GID..."
groupmod -o -g "$gid" "$groupname" || { echo "Failed to update GID for group $groupname"; return 1; }
fi
fi

# Create shared directory
mkdir -p "$share" || { echo "Failed to create directory $share"; exit 1; }
# Check if the user already exists, if not, create it
if ! id "$username" &>/dev/null; then
echo "User $username does not exist, creating user..."
adduser -S -D -H -h /tmp -s /sbin/nologin -G "$groupname" -u "$uid" -g "Samba User" "$username" || { echo "Failed to create user $username"; return 1; }
else
# Check if the uid right,if not, change it
local current_uid
current_uid=$(id -u "$username")
if [[ "$current_uid" != "$uid" ]]; then
echo "User $username exists but UID differs, updating UID..."
usermod -o -u "$uid" "$username" || { echo "Failed to update UID for user $username"; return 1; }
fi

# Check if the smb group exists, if not, create it
if ! getent group "$group" &>/dev/null; then
groupadd "$group" || { echo "Failed to create group $group"; exit 1; }
fi
# Update user's group
usermod -g "$groupname" "$username" || { echo "Failed to update group for user $username"; return 1; }
fi

# Check if the user already exists, if not, create it
if ! id "$USER" &>/dev/null; then
adduser -S -D -H -h /tmp -s /sbin/nologin -G "$group" -g 'Samba User' "$USER" || { echo "Failed to create user $USER"; exit 1; }
fi
# Check if the user is a samba user
if pdbedit -L | grep -q "^$username:"; then
# if the user is a samba user, change its password
echo -e "$password\n$password" | smbpasswd -s "$username" || { echo "Failed to update Samba password for $username"; return 1; }
echo "Password for existing Samba user $username has been updated."
else
# if the user is not a samba user, create it and set a password
echo -e "$password\n$password" | smbpasswd -a -s "$username" || { echo "Failed to add Samba user $username"; return 1; }
echo "User $username has been added to Samba and password set."
fi
}

# Get the current user and group IDs
OldUID=$(id -u "$USER")
OldGID=$(getent group "$group" | cut -d: -f3)
# External config file
config="/etc/samba/smb.conf"
user_config="/etc/samba/smb_user.conf"

# Change the UID and GID of the user and group if necessary
if [[ "$OldUID" != "$UID" ]]; then
usermod -o -u "$UID" "$USER" || { echo "Failed to change UID for $USER"; exit 1; }
# Check if the user configuration file exists
if [[ -f "$user_config" ]] && [[ ! -f "$config" ]]; then
echo "File $config not found, disabling multi-user mode."
fi

if [[ "$OldGID" != "$GID" ]]; then
groupmod -o -g "$GID" "$group" || { echo "Failed to change GID for group $group"; exit 1; }
fi
# Check if multi-user mode is enabled
if [[ -f "$user_config" ]] && [[ -f "$config" ]]; then

# Check if an external config file was supplied
config="/etc/samba/smb.conf"
while read -r line; do

if [ -f "$config" ]; then
# Skip lines that are comments or empty
[[ "$line" =~ ^#.*$ || -z "$line" ]] && continue

# Inform the user we are using a custom configuration file.
echo "Using provided configuration file: $config."
# Split each line by colon and assign to variables
username=$(echo "$line" | cut -d':' -f1)
uid=$(echo "$line" | cut -d':' -f2)
groupname=$(echo "$line" | cut -d':' -f3)
gid=$(echo "$line" | cut -d':' -f4)
password=$(echo "$line" | cut -d':' -f5)

else
# Check if all required fields are present
if [[ -z "$username" || -z "$uid" || -z "$groupname" || -z "$gid" || -z "$password" ]]; then
echo "Skipping incomplete line: $line"
continue
fi

config="/etc/samba/smb.tmp"
template="/etc/samba/smb.default"
# Call the function with extracted values
add_user "$username" "$uid" "$groupname" "$gid" "$password"

# Generate a config file from template
rm -f "$config"
cp "$template" "$config"
done < "$user_config"

# Update force user and force group in smb.conf
sed -i "s/^\(\s*\)force user =.*/\1force user = $USER/" "$config"
sed -i "s/^\(\s*\)force group =.*/\1force group = $group/" "$config"
else

# Verify if the RW variable is equal to false (indicating read-only mode)
if [[ "$RW" == [Ff0]* ]]; then
# Set variables for group and share directory
group="smb"
share="/storage"
secret="/run/secrets/pass"

# Adjust settings in smb.conf to set share to read-only
sed -i "s/^\(\s*\)writable =.*/\1writable = no/" "$config"
sed -i "s/^\(\s*\)read only =.*/\1read only = yes/" "$config"
# Create shared directory
mkdir -p "$share" || { echo "Failed to create directory $share"; exit 1; }

else
# Check if the secret file exists and if its size is greater than zero
if [ -s "$secret" ]; then
PASS=$(cat "$secret")
fi

# Set permissions for share directory if new (empty), leave untouched if otherwise
if [ -z "$(ls -A "$share")" ]; then
chmod 0770 "$share" || { echo "Failed to set permissions for directory $share"; exit 1; }
chown "$USER:$group" "$share" || { echo "Failed to set ownership for directory $share"; exit 1; }
fi
add_user "$USER" "$UID" "$group" "$GID" "$PASS"

if [ -f "$config" ]; then
# Inform the user we are using a custom configuration file.
echo "Using provided configuration file: $config."
else
config="/etc/samba/smb.tmp"
template="/etc/samba/smb.default"

# Generate a config file from template
rm -f "$config"
cp "$template" "$config"

# Update force user and force group in smb.conf
sed -i "s/^\(\s*\)force user =.*/\1force user = $USER/" "$config"
sed -i "s/^\(\s*\)force group =.*/\1force group = $group/" "$config"

# Verify if the RW variable is equal to false (indicating read-only mode)
if [[ "$RW" == [Ff0]* ]]; then
# Adjust settings in smb.conf to set share to read-only
sed -i "s/^\(\s*\)writable =.*/\1writable = no/" "$config"
sed -i "s/^\(\s*\)read only =.*/\1read only = yes/" "$config"
else
# Set permissions for share directory if new (empty), leave untouched if otherwise
if [ -z "$(ls -A "$share")" ]; then
chmod 0770 "$share" || { echo "Failed to set permissions for directory $share"; exit 1; }
chown "$USER:$group" "$share" || { echo "Failed to set ownership for directory $share"; exit 1; }
fi
fi
fi
fi

# Check if the secret file exists and if its size is greater than zero
if [ -s "$secret" ]; then
PASS=$(cat "$secret")
fi

# Change Samba password
echo -e "$PASS\n$PASS" | smbpasswd -a -c "$config" -s "$USER" > /dev/null || { echo "Failed to change Samba password for $USER"; exit 1; }

# Start the Samba daemon with the following options:
# --foreground: Run in the foreground instead of daemonizing.
# --debug-stdout: Send debug output to stdout.
Expand Down
2 changes: 2 additions & 0 deletions smb_user.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#username:UID:groupname:GID:password
samba:1000:smb:1000:secret

0 comments on commit 83b5e3f

Please sign in to comment.