Following Secure File Transfers: Setting Up SFTP on Ubuntu 24.04, you may find that creating an SFTP user is a tedious task. Imagine you need to create SFTP accounts for a list of hundreds of users. It’s definitely inefficient if you keep creating them one by one manually. The caveat is that it involves human errors that take you time to figure out.

Therefore, having a script to automate this process not only ensures that every step is correct but also speeds up the user creation process. In this blog, I want to share a small script to help you speed this up.

Prerequisite

  • Should have an Ubuntu 24.04 server with SFTP configured. If you haven’t done it yet, please check out Step 1 to 5 in this blog.

Basically, your /etc/ssh/sshd_config file should have a configuration similar to:

# SFTP Configuration
Subsystem sftp internal-sftp # Use in-process SFTP server
Match Group sftp_users
ChrootDirectory /sftp_data/%u # Prevent user access to anything beyond their home folder
KbdInteractiveAuthentication yes # Enable this to allow enter password from CLI
PasswordAuthentication yes # Allow user access SFTP with plain-text password
X11Forwarding no # Disable X11 forwarding
AllowTcpForwarding no # Disable tunneling
AllowAgentForwarding no # Disable port forwarding
PermitTunnel no # Disable network tunneling
ForceCommand internal-sftp -d /upload # Force the connection to use the built-in SFTP server and go to /upload

Can change /sftp_data and /upload to your desired directories.

Create a Bash script

We will use a bash script to automate the creation of SFTP users. On your SFTP server, run:

sudo nano /usr/local/bin/create-sftp-user

And add the following content:

#!/usr/bin/env bash

# Usage: ./create-sftp-user.sh <username>
# 
#    Example: ./create_sftp_user.sh john
#

SCRIPT_NAME=$(basename "$0")

show_help() {
  echo "Usage: $SCRIPT_NAME [ARGS] <username>"
  echo
  echo "Arguments:"
  echo "  -p, --public-key  STRING  Set the public key to the user."
  echo "  -h, --help        Show help."
}

# Parse options with getopt
OPTIONS=$(getopt -o "p:h" \
  --long "public-key:" \
  -n "$0" -- "$@")

if [ $? -ne 0 ]; then
    echo "Try '$0 --help' for usage." >&2
    exit 1
fi

# Reorder arguments as parsed by getopt
eval set -- "$OPTIONS"
PUBLIC_KEY=""
while true; do
  case "$1" in
    -h|--help) show_help; exit 0 ;;
    -p|--public-key) PUBLIC_KEY="$2"; shift 2 ;;
    --) shift; break ;;
    *) echo "Invalid option: $1"; show_help; exit 1 ;;
  esac
done

# Sanity check for required argument
if [[ $# -ne 1 ]]; then
  echo "Invalid: Require to provide a username" 1>&2
  echo "Usage: $SCRIPT_NAME <username>" 1>&2
  exit 1
fi

USERNAME="$1"

# Sanity check for $USERNAME to avoid white space 
if [[ ! "$USERNAME" =~ ^[A-Za-z0-9._-]+$ ]]; then
  echo "ERROR: <username> must only contain letters, numbers, dots, underscores, or dashes (no spaces)"
  exit 1
fi


# Generate a random password that has:
#   - 16 character length
#   - at least 1 digit
#   - at least 1 special character
PASSWORD=$(tr -cd 'a-zA-Z0-9_+/%[]={}' < /dev/urandom | dd bs=1 count=16 2>/dev/null)

# Change these variables to your corresponding SFTP GROUP and DIRECTORY
SFTP_GROUP="sftp_users"
SFTP_BASE_DIR="/sftp_data"

# Ensure the SFTP_BASE_DIR directory is created and has its correct permissions
if [[ ! -d "$SFTP_BASE_DIR" ]]; then
  mkdir -p "$SFTP_BASE_DIR"
  chmod 701 "$SFTP_BASE_DIR"
fi

# Ensure the SFTP group exists
if ! getent group "$SFTP_GROUP" >/dev/null; then
  groupadd "$SFTP_GROUP"
fi

USER_SFTP_BASE_DIR="$SFTP_BASE_DIR/$USERNAME"
UPLOAD_DIR="$USER_SFTP_BASE_DIR/upload"
SSH_DIR="$UPLOAD_DIR/.ssh"
AUTHORIZED_KEYS_FILE="$SSH_DIR/authorized_keys"

# Create the user (if not exists)
if id "$USERNAME" &>/dev/null; then
  echo "User '$USERNAME' already exists."
  exit 0
fi

if [[ ! -d "$UPLOAD_DIR" ]]; then
  mkdir -p "$UPLOAD_DIR"
fi
# Set $UPLOAD_DIR become the user's home directory
useradd -d "$UPLOAD_DIR" -s /usr/sbin/nologin -g "$SFTP_GROUP" "$USERNAME"

# Set the generated password to the user and encrypt the password with the SHA512 crypt method
echo "$USERNAME:$PASSWORD" | chpasswd -c SHA512

# Record the created user to a file for reference - Comment this line if you don't need
echo "$USERNAME:$PASSWORD" >> /root/sftp_accounts.txt
chmod 600 /root/sftp_accounts.txt

# Set up public key for SSH authentication
if [[ -n "$PUBLIC_KEY" ]]; then
  if [[ ! -d "$SSH_DIR" ]]; then
    mkdir -p "$SSH_DIR"
  fi
  echo "$PUBLIC_KEY" | tee "$AUTHORIZED_KEYS_FILE" >/dev/null
fi

# Setup chroot jail directory
chown -R "root:$SFTP_GROUP" "$USER_SFTP_BASE_DIR"
chown -R "$USERNAME:$SFTP_GROUP" "$UPLOAD_DIR"

# Grant proper permissions to authorized_keys 
if [[ -n "$PUBLIC_KEY" ]]; then
  chmod 700 "$SSH_DIR"
  chmod 600 "$AUTHORIZED_KEYS_FILE"
fi

# Output credentials
echo "====================================="
echo "SFTP user created successfully!"
echo "Username: $USERNAME"
echo "Password: $PASSWORD"
echo "Directory: $UPLOAD_DIR"
echo "====================================="

Press Ctrl + O and press Enter to save the content. Then press Ctrl + X to exit.

NOTED: You can adjust the variables defined in the script according to what you want. For example, change these variables below if you changed in /etc/ssh/sshd_config:

  • SFTP_GROUP=”<change-to-your-SFTP-group>”
  • SFTP_BASE_DIR=”/path/to/your/SFTP/base/directory”

Grant executable permission to the script, the script should be executed as root only:

sudo chmod 700 /usr/local/bin/create-sftp-user

Create SFTP account with password authentication

To create a SFTP user with the script, simply run:

sudo create-sftp-user <username>

Example output:

The script automatically creates the specified <username> with a random password, and also set up user’s home directory as well as all corresponding directories needed for a SFTP user. <username> should only contain letters, numbers, dots, underscores, or dashes (no spaces).

If we have a list of users, we can add them to a file like /path/to/your/user_list.txt, it should have expected usernames:

john
daniel-1
michael.fraser
mc_kenzi
henry

To create SFTP accounts for this list, we run:

while read username; do
  sudo create-sftp-user "$username"
done < /path/to/your/user_list.txt

Output

I’ve set a command in the script to output the result to /root/sftp_accounts.txt file in case you want to record which user/passwords are created. Feel free to disable it or remove the output for login credentials if you don’t need them.

Now, from another machine that can connect to SFTP server, we can test the login credentials by run sftp <username>@<sftp-host-ip-or-dns> command and enter the corresponding password generated from the terminal.

Create SFTP account with SSH authentication

In the previous blog, we comment these two lines in /etc/ssh/sshd_config file to disable password authentication.

Match Group sftp_users  
...
# Comment these two lines to disable login by plain-text password
# KbdInteractiveAuthentication yes
# PasswordAuthentication yes

Ideally, I think if we choose to use SSH authentication, it’s best that we disable password authencation for best practice. However, this time, we can leave it uncommented, which means it supports both SSH keys and password authentication.

For the workflow of using create-sftp-user script, the user who wants to use SSH keys for authentication should send the administrator a public key (normally in /home/<your-username>/.ssh/<your-key-name>.pub on your client machine) so that the administrator can set up the SFTP account with the provided public key. Alternative, the administrator can generate an SSH key pair for the user but must send the private key back to the user so that they can use it to log in to the SFTP server. Please see the previous blog for this process.

I assume the user has sent administrator its public key, we can login the SFTP server, and run:

# Change the value of PUBLIC_KEY_STRING to your own public key string
PUBLIC_KEY_STRING="ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB....smY2Sw== <username>@<your-email>"
# Change "test-user-2" to your desired username
sudo create-sftp-user -p "$PUBLIC_KEY_STRING" test-user-2

That’s all we need. It creates a username with a random password and also sets this public key string to the authorized_keys file in the user’s home directory. In this example, it’s /sftp_data/test-user-2/upload/.ssh/authorized_keys.

And this stage, at your client machine (not SFTP server), if you haven’t configured /home/<your-username>/.ssh/config file to tell the machine to use SSH keys authentication. It still prefers to password authentication. To force the client use SSH keys authentication, run:

# Change the <parameters> below to your own. 
cat <<EOF > /home/<your-username>/.ssh/config
Host <IP-or-DNS-of-your-SFTP-server>
  Hostname <IP-or-DNS-of-your-SFTP-server>
  PreferredAuthentications publickey
  IdentityFile ~/.ssh/<your-private-key>
EOF

Now, you can sftp <username>@<sftp-host-ip-or-dns> again to test, it should login directly without prompting to enter password.

That’s all for this blog, hope it help you somewhat. Thanks for reading :).


Discover more from Turn DevOps Easier

Subscribe to get the latest posts sent to your email.

By Binh

Leave a Reply

Your email address will not be published. Required fields are marked *

Content on this page