Linux User creation Bash script
Tags: bash, linux, devops, cloud
Published: 2024-07-01 13:01:09



> This blog post explores leveraging Docker containers to automate the user creation with a custom Bash script. This Dockerized approach not only streamlines user creation but also cuts the time needed into two, therefore saving time, improving consistency, and enhancing security.

## Scenario 

Your company has employed many new developers. As a SysOps engineer, write a bash script called create_users.sh that reads a text file containing the employee's usernames and group names, where each line is formatted as user;groups.
The script should create users and groups as specified, set up home directories with appropriate permissions and ownership, generate random passwords for the users, and log all actions to /var/log/user_management.log. Additionally, store the generated passwords securely in /var/secure/user_passwords.txt.

## Implementation steps
**1. Create the txt file containing the users and their groups: **
This text file acts as the schema for the user accounts. Each line in the file specifies a user and the groups (e.g., "admin" or "finance") they belong to. the semicolon separates the users from the groups. users.txt follows the format:

```
light;sudo,dev,www-data
ann;sudo
kosi;dev,www-data
evie;finance,hr
dolapo;marketing,sales
ify;it,security,network
```
**2. Create a Dockerfile:**
For increased consistency and effortless deployment, I containerized the user creation script using a Dockerfile. This Dockerfile defines the steps to prepare the environment and run the script within a containerized environment, ensuring a uniform user creation process. 

```
# Use an official Alpine as a base image
FROM alpine:latest

# Install necessary packages
RUN apk update && apk add --no-cache \
    sudo \
    openssl \
    bash \
    shadow \
    util-linux \
    vim

# Copy the script and users.txt into the container
COPY create_users.sh /usr/local/bin/create_users.sh
COPY users.txt /usr/local/bin/users.txt

# Make the script executable
RUN chmod +x /usr/local/bin/create_users.sh

# Specify script to run on container start
CMD ["bash", "/usr/local/bin/create_users.sh", "/usr/local/bin/users.txt"]
```
This Dockerfile installs essential tools (sudo, openssl, bash, etc.) via apk and copies create_users.sh and users.txt (file containing the employee usernames and group names) to /usr/local/bin/ (creating the directory if needed). It sets the script create_users.sh to execute upon container startup using CMD.

**3. Create the Script**
The bash script, create_users.sh, automates the process of creating users, assigning them to appropriate groups (e.g., sudo), setting home directory permissions, and logging all actions to a file for auditing purposes. With security at the forefront, this script avoids hardcoded credentials and potentially prompting for user input during execution.

```
#!/bin/bash

# Check if the script is run as root (superuser)
if [ "$(id -u)" -ne 0 ]; then
    echo "Please run this script as root or using sudo."
    exit 1
fi

# Check if the input file is provided
if [ -z "$1" ]; then
    echo "Usage: $0 <name-of-text-file>"
    exit 1
fi

INPUT_FILE="$1"
LOG_FILE="/var/log/user_management.log"
PASSWORD_FILE="/var/secure/user_passwords.csv"

# Create log and password files if they do not exist
touch $LOG_FILE
mkdir -p /var/secure
touch $PASSWORD_FILE
chmod 600 $PASSWORD_FILE

# Function to generate random passwords
generate_password() {
    openssl rand -base64 12
}

# Read the input file line by line
while IFS=';' read -r username groups; do
    username=$(echo "$username" | xargs)
    groups=$(echo "$groups" | xargs)

    # Check if user already exists
    if id "$username" &>/dev/null; then
        echo "User $username already exists, skipping..." | tee -a $LOG_FILE
        continue
    fi

    # Create a personal group for the user
    addgroup "$username"

    # Create the user with the personal group and home directory
    adduser -D -G "$username" -s /bin/bash "$username"

    # Add the user to additional groups if specified
    if [ -n "$groups" ]; then
        IFS=',' read -r -a group_array <<< "$groups"
        for group in "${group_array[@]}"; do
            group=$(echo "$group" | xargs)
            if ! getent group "$group" > /dev/null; then
                addgroup "$group"
            fi
            adduser "$username" "$group"
        done
    fi

    # Generate a random password for the user
    password=$(generate_password)

    # Set the user's password
    echo "$username:$password" | chpasswd

    # Log the actions
    echo "Created user $username with groups $groups and home directory" | tee -a $LOG_FILE

    # Store the username and password securely
    echo "$username,$password" >> $PASSWORD_FILE
done < "$INPUT_FILE"

echo "User creation process completed. Check $LOG_FILE for details."

```


For better understanding on the rationale behind this script

**- **Check if the executor has root privileges:** **
By performing a root privilege check at the outset, the script avoids permission-related issues that could arise without proper access because this script requires root privileges (sudo) to create users and groups, manage passwords, and modify system files.

```
if [ "$(id -u)" -ne 0 ]; then
    echo "Please run this script as root or using sudo."
    exit 1
fi

```

**- **Check that the correct input file is being used:**** 
This ensures that hat the script is provided with an input file (users.txt), which contains usernames and employee groups. If this file isn't used, the script can not proceed and informs the user about the correct usage format (script name followed by the file name).

```
if [ -z "$1" ]; then
    echo "Usage: $0 <name-of-text-file>"
    exit 1
fi
```

**- File and Directory Setup:**
 This step creates or verifies the existence of essential files: user_management.log for logging script actions and user_passwords.csv for encrypted password storage The password file has restricted permissions (chmod 600), this ensures that only the root user can access and modify its contents, once again prioritising security.

```
touch $LOG_FILE
mkdir -p /var/secure
touch $PASSWORD_FILE
chmod 600 $PASSWORD_FILE
4. Password Generation Function: This function capitalizes on openssl to generate secure passwords with sufficient length and complexity.
generate_password() {
    openssl rand -base64 12
}

```

**- User Creation Loop: **
This reads each line from the input file (users.txt). For each line, it checks if the user already exists. If it doesn't, it proceeds to create the user with their personal group and home directory. If it exists, it adds the user to additional groups listed in users.txt.

```
# Read the input file line by line
while IFS=';' read -r username groups; do
    username=$(echo "$username" | xargs)
    groups=$(echo "$groups" | xargs)

    # Check if user already exists
    if id "$username" &>/dev/null; then
        echo "User $username already exists, skipping..." | tee -a $LOG_FILE
        continue
    fi

    # Create a personal group for the user
    addgroup "$username"

    # Create the user with the personal group and home directory
    adduser -D -G "$username" -s /bin/bash "$username"

    # Add the user to additional groups if specified
    if [ -n "$groups" ]; then
        IFS=',' read -r -a group_array <<< "$groups"
        for group in "${group_array[@]}"; do
            group=$(echo "$group" | xargs)
            if ! getent group "$group" > /dev/null; then
                addgroup "$group"
            fi
            adduser "$username" "$group"
        done
    fi

```
**- Password Setting and Logging:** 
It first generates a random password using the generate_password function. This ensures that each user's password is unique and strong. The script then sets the user's password with chpasswd. As an important step, it logs the entire user creation process, including the username, associated groups, and home directory information, to the user_management.log file. This comprehensive trail allows for easy tracking and verification of all actions performed by the script for user creation.

```
# Generate a random password for the user
    password=$(generate_password)

    # Set the user's password
    echo "$username:$password" | chpasswd

    # Log the actions
    echo "Created user $username with groups $groups and home directory" | tee -a $LOG_FILE
```

**- Password Storage:** 
Each user's username is stored, and a secure password is generated in user_passwords.csv. This file is essential for securely managing and distributing user credentials while maintaining utmost secrecy.

```
# Store the username and password securely
    echo "$username,$password" >> $PASSWORD_FILE
done < "$INPUT_FILE"
```

**-  Completion Message:**
Notifies the user upon completion of the user creation process.

```
echo "User creation process completed. Check $LOG_FILE for details."

```

**4. Build the Docker Image and Run the Docker Container:** 
Using the docker run command to build a Docker image named "user_creation" using docker build -t user_creation ., to encapsulate our entire user creation environment.

```
docker build -t user_creation .
docker run --rm -it --name user_creation_container user_creation
```

**5. Copy Logs from Docker container to local machine:**
After the script finishes its work, we use docker cp to copy the log file and password file from the container. In ding this we have a copy on our local machine.

```
docker cp user_creation_container:/var/log/user_management.log .
cat user_management.log

docker cp user_creation_container:/var/secure/user_passwords.csv .
cat user_passwords.csv

```

**6. Final testing**

```
sudo bash create_users.sh users.txt
```

## Conclusion
With this, we have successfully automated user creation with a Bash script and streamlined the process with Docker. From defining user details in users.txt to containerized execution, we've done it all.
To be part of the program that provided this task scenario, visit their official websites to gain more insights
https://hng.tech/internship
https://hng.tech/hire