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
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
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
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
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
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
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
}
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"
}
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"
}
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
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
The command above does the following:
Iterating Through Lines in the File
for line in "${lines[@]}"; do
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)
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"
This line splits the line
variable 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)
This command removes any leading and trailing whitespace from the groups
variable. 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"
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
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)
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")
This line calls the encrypt_password
function to encrypt the generated password. The encrypted password is stored in the encrypted_password
variable. The encryption key used is stored in the PASSWORD_ENCRYPTION_KEY
variable.
Storing the Encrypted Password
echo "$user:$encrypted_password" >> "$PASSWORD_FILE"
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"
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"
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)
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
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"
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
then, run the script,
./create_user.sh ./text_file.txt
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)