DEV Community

Kalio Princewill
Kalio Princewill

Posted on • Updated on

Automating User Creation and Management with Bash Scripting: A Detailed Guide.

Introduction:

Linux, being a multi-user system, empowers administrators to create users and groups, each with specific permissions. When users are assigned to groups, they acquire the group's permissions. Effective user and group management is essential for system administration, from small personal servers to extensive enterprise networks. Streamlining the user creation process is crucial for ensuring security, organization, and efficiency, and Bash scripting provides a powerful means of automating these tasks.

This guide will walk you through creating a Bash script for managing user accounts on a Linux system.

The problem

Your company has employed many new developers. As a SysOps engineer, it's your responsibility to efficiently manage user accounts and their associated permissions. To streamline this process, you are tasked with writing a Bash script that automates the setup and configuration of new user accounts based on predefined criteria.

  • User and Group Definition: Users and their groups are defined in a text file that will be supplied to the script as an argument.

  • Home Directory Creation: A dedicated home directory should be created for each user

  • Secure Password Storage: User passwords should be stored securely in a file with path /car/secure/user_passwords.txt

  • Logging Actions: Logs of all actions should be logged to /var/log/user_management.log for auditing and troubleshooting purposes.

  • File Access Control: Only the owner, in this case root, should be able to access the user_password.txt file, ensuring that unauthorised access is prevented.

  • Error Handling: Errors should be gracefully handled

Prerequisites

To begin this tutorial, ensure you have the following:

  • A Linux system with administrative access.
  • Basic familiarity with Linux commands.

The Script:

The script, create_user.sh, automates user account creation, password encryption, group assignment, and logging activities on a Linux system. The goal is to streamline user management and ensure that all actions are securely recorded and auditable. Below is a detailed breakdown of each section of the script, including its purpose and functionality.

The shebang.

 #!/bin/bash
Enter fullscreen mode Exit fullscreen mode

This specifies the type of interpreter script will be run with. Since it is a "bash" script, it should be run with the Bourne Again Shell (Bash) interpreter. Also, some commands in the script may not be interpreted correctly outside of Bash.

Defining File Paths and Variables
Next, create some environment variables that will hold the text_file.txt, the logs file path (var/log/user_management.log), and password file path (/var/secure/user_passwords.csv).

LOG_FILE="/var/log/user_management.log"
PASSWORD_FILE_DIRECTORY="/var/secure"
PASSWORD_FILE="/var/secure/user_passwords.txt"
PASSWORD_ENCRYPTION_KEY="secure-all-things"
USERS_FILE=$1
Enter fullscreen mode Exit fullscreen mode
  • LOG_FILE: specifies where the log entries will be stored. Logging is crucial for tracking actions and diagnosing issues.

  • PASSWORD_FILE_DIRECTORY: defines the directory for storing user passwords securely.

  • PASSWORD_FILE: is the file where encrypted passwords will be saved.

  • PASSWORD_ENCRYPTION_KEY: is used to encrypt user passwords.

  • USERS_FILE: takes the first script argument, which should be the path to a file containing user data.

Next, create this file path and give them the right permission using the command below.

touch $LOG_FILE
mkdir -p /var/secure
chmod 700 /var/secure
touch $PASSWORD_FILE
chmod 600 $PASSWORD_FILE
Enter fullscreen mode Exit fullscreen mode

Details of the command in the codeblock are:
touch $LOG_FILE

  • touch: This command is used to create an empty file or update the access and modification times of an existing file.

  • $LOG_FILE: This variable holds the path("/var/log/user_management.log") to the log file.

mkdir -p /var/secure

  • mkdir: This command is used to create directories.

  • -p: This option allows the creation of parent directories as needed. If the directory already exists, it will not return an error.

  • /var/secure: This is the directory path to be created.

chmod 700 /var/secure

  • chmod: This command is used to change the permissions of files or directories.

  • 700: This permission setting means:

  • 7: The owner has read, write, and execute permissions.

  • 0: The group has no permissions.

  • 0: Others have no permissions.

  • /var/secure: The directory whose permissions are being modified.

chmod 600 $PASSWORD_FILE

  • chmod: This command changes the permissions of a file.

  • 600: This permission setting means:

  • 6: The owner has read and write permissions.

  • 0: The group has no permissions.

  • 0: Others have no permissions.

Checking for Sudo Privileges

if [ "$(id -u)" != "0" ]; then  
    echo "This script must be run with sudo. Exiting..."   
    exit 1
 fi
Enter fullscreen mode Exit fullscreen mode

This code ensures the script runs with root privileges by checking if the Effective User ID (EUID) is zero. In Linux, an EUID of 0 corresponds to the root user. If the script is not executed with administrative rights, it exits with an error message. This is because the script will perform actions that require elevated privileges, such as creating users and groups, setting passwords, creating files, etc.

Validating Arguments
Next to ensure that the script is properly executed with an input file provided as an argument, this conditional check terminates the script if no argument is detected. Using $#to represent the number of arguments passed when executing the script, it validates whether no argument ($# equals zero) or more than one argument ($# is greater than or equal to 2) is given. If either condition is met, an error message is displayed, halting the script's execution.

if [ $# -eq 0 ]; then   
   echo "No file path provided."   
   echo "Usage: $0 <user-data-file-path>"   
   exit 1 
fi
Enter fullscreen mode Exit fullscreen mode

Also to verify if the provided file exists. If the file does not exist, the script exits with an error message.

if [ ! -e "$USERS_FILE" ]; then
  echo "The provided user's data file does not exist: $USERS_FILE"
  exit 1
fi
Enter fullscreen mode Exit fullscreen mode

Function Definitions
Next to ensure reusability and modularity in your code, copy and paste the several utility functions to your code
Check for Installed Package:

is_package_installed() {
  dpkg -s "$1" >/dev/null 2>&1
}
Enter fullscreen mode Exit fullscreen mode

This function checks if a package is installed on your machine using dpkg. It suppresses output and returns a status code.
Encrypt Password:

encrypt_password() {
  echo "$1" | openssl enc -aes-256-cbc -pbkdf2 -base64 -pass pass:"$2"
}
Enter fullscreen mode Exit fullscreen mode

The commnad uses openssl to encrypt a password. The password is encrypted using AES-256-CBC and a provided encryption key.
Set Bash as Default Shell:

set_bash_default_shell() {
  local user="$1"
  sudo chsh -s /bin/bash "$user"
}
Enter fullscreen mode Exit fullscreen mode

This function sets Bash as the default shell for the specified user.
Installing Required Packages:
To ensure you have the necessary packages to run the script, add the code-block

if ! is_package_installed openssl; then
  echo "openssl is not installed. Installing..."
  sudo apt-get update
  sudo apt-get install -y openssl
fi

if ! is_package_installed pwgen; then
  echo "pwgen is not installed. Installing..."
  sudo apt-get update
  sudo apt-get install -y pwgen
fi
Enter fullscreen mode Exit fullscreen mode

with the command above you have defined variables for the file paths corresponding to the log file, password file, and input file. Additionally, you have ensured that the script is being executed with superuser privileges.

The subsequent step involves iterating through each line in the input text file, separating these lines by usernames and groups, and then using the Bash script to create the users and assign them to their respective groups.
To set up these users and groups, incorporate the following commands into your create_users.sh script:

for line in "${lines[@]}"; do
    # Remove leading and trailing whitespaces
    line=$(echo "$line" | xargs)

    # Split line by ';' and store the second part
    IFS=';' read -r user groups <<< "$line"

    # Remove leading and trailing whitespaces from the second part
    groups=$(echo "$groups" | xargs)

    # Create a variable groupsArray that is an array from spliting the groups of each user
    IFS=',' read -ra groupsArray <<< "$groups"

    # Check if user exists
    if id "$user" &>/dev/null; then
        echo "User $user already exists. Skipping creation."
        continue
    fi

    # Generate a 6-character password using pwgen
    password=$(pwgen -sBv1 6 1)

    # Encrypt the password before storing it
    encrypted_password=$(encrypt_password "$password" "$PASSWORD_ENCRYPTION_KEY")

    # Store the encrypted password in the file
    echo "$user:$encrypted_password" >> "$PASSWORD_FILE"

    # Create the user with the generated password
    sudo useradd -m -p $(openssl passwd -6 "$password") "$user"

    # Set Bash as the default shell
    set_bash_default_shell "$user"

    # loop over each group in the groups array
    for group in "${groupsArray[@]}"; do
        group=$(echo "$group" | xargs)

        # Check if group exists, if not, create it
        if ! grep -q "^$group:" /etc/group; then
            sudo groupadd "$group"
            echo "Created group $group"
        fi

        # Add user to the group
        sudo usermod -aG "$group" "$user"
        echo "Added $user to $group"
    done

    echo "User $user created and password stored securely"
done
Enter fullscreen mode Exit fullscreen mode

The command above does the following:
Iterating Through Lines in the File

for line in "${lines[@]}"; do

Enter fullscreen mode Exit fullscreen mode

This command initiates a loop that iterates over each line in the lines array. Each element of the array lines, representing a line from the input file, is processed one by one and assigned to the line variable.

Removing Leading and Trailing Whitespace

line=$(echo "$line" | xargs)
Enter fullscreen mode Exit fullscreen mode

Here, echo "$line" | xargs removes any leading and trailing whitespace from the current line. This ensures that any extra spaces at the beginning or end of the line are trimmed off.

Splitting the Line into User and Groups

IFS=';' read -r user groups <<< "$line"
Enter fullscreen mode Exit fullscreen mode

This line splits the linevariable into two parts based on the ; delimiter. The first part is assigned to user, and the second part is assigned togroups. IFS=';'sets the Internal Field Separator to ;, which defines how the line is split.

Trimming Whitespace from Groups

groups=$(echo "$groups" | xargs)

Enter fullscreen mode Exit fullscreen mode

This command removes any leading and trailing whitespace from the groupsvariable. This is necessary to ensure that the group names are clean and free from extra spaces.

Splitting Groups into an Array

IFS=',' read -ra groupsArray <<< "$groups"

Enter fullscreen mode Exit fullscreen mode

Here, IFS=',' sets the Internal Field Separator to ,. The read -ra groupsArray splits the groups string into an array groupsArray based on commas, with each element representing a group.

Checking if User Already Exists

if id "$user" &>/dev/null; then
    echo "User $user already exists. Skipping creation."
    continue
fi
Enter fullscreen mode Exit fullscreen mode

This block checks if a user with the given user name already exists. The id "$user" &>/dev/null command attempts to fetch user details, and if successful (i.e., the user exists), it outputs nothing to /dev/null. If the user exists, a message is printed, and the continue command skips the rest of the loop for this user and moves to the next iteration.

Generating a 6-Character Password

password=$(pwgen -sBv1 6 1)
Enter fullscreen mode Exit fullscreen mode

This command generates a secure, random 6-character password using the pwgen tool. The -s flag ensures a secure password, -B avoids ambiguous characters, and -v1 specifies the length and quantity (1 password).
Encrypting the Password

encrypted_password=$(encrypt_password "$password" "$PASSWORD_ENCRYPTION_KEY")
Enter fullscreen mode Exit fullscreen mode

This line calls the encrypt_password function to encrypt the generated password. The encrypted password is stored in the encrypted_passwordvariable. The encryption key used is stored in the PASSWORD_ENCRYPTION_KEY variable.

Storing the Encrypted Password

echo "$user:$encrypted_password" >> "$PASSWORD_FILE"

Enter fullscreen mode Exit fullscreen mode

This command appends the username and encrypted password to the PASSWORD_FILE. The >> operator ensures that the data is added to the end of the file without overwriting existing content.
Creating the User with the Generated Password

sudo useradd -m -p $(openssl passwd -6 "$password") "$user"
Enter fullscreen mode Exit fullscreen mode

This line creates a new user with the username stored in user. The -m option creates the user's home directory. The password is hashed using openssl passwd -6 with the generated password, ensuring it is securely stored.

Setting Bash as the Default Shell

set_bash_default_shell "$user"
Enter fullscreen mode Exit fullscreen mode

This function call sets Bash as the default shell for the new user. The set_bash_default_shell function is assumed to be defined elsewhere in the script to change the user's shell to Bash.

Adding User to Specified Groups

for group in "${groupsArray[@]}"; do
    group=$(echo "$group" | xargs)
Enter fullscreen mode Exit fullscreen mode

This loop iterates through each group in the groupsArray array. Each group is cleaned of leading and trailing whitespace.
Checking and Creating Group if Necessary

if ! grep -q "^$group:" /etc/group; then
    sudo groupadd "$group"
    echo "Created group $group"
fi
Enter fullscreen mode Exit fullscreen mode

For each group, this block checks if the group exists in the /etc/group file. If not, the groupadd command creates the group, and a confirmation message is printed.

Adding User to the Group

sudo usermod -aG "$group" "$user"
echo "Added $user to $group"

Enter fullscreen mode Exit fullscreen mode

This command adds the user to the specified group using usermod -aG. The -aG option appends the user to the supplementary group without affecting membership in other groups. A message is printed confirming the addition.

With this, you’ve successfully developed a script that efficiently handles user and group management on your system. You can find the complete script on the Github Repo

Verifying Script Functionality

To verify that your script is functioning correctly, execute it in a terminal that supports Linux commands. Use the following command in your terminal to run the script:

Firstly, make your script executable, run the command below,

chmod +x create_users.sh
Enter fullscreen mode Exit fullscreen mode

then, run the script,

./create_user.sh ./text_file.txt
Enter fullscreen mode Exit fullscreen mode

In Conclusion

This article details the procedure for automating the creation of users and groups through a script. It incorporates several assumptions and compromises to balance security with usability. As an administrator, you can now leverage this script to streamline user onboarding within your organisation.

Top comments (0)