When automating user creation in Linux systems, many administrators encounter this common frustration: the useradd
command's -p
option expects an already-encrypted password rather than plaintext. Running:
useradd -d /home/dummy -g idiots -m -p 12345689 dummy
creates the user but makes authentication impossible since 12345689
gets treated as literal encrypted password data.
Method 1: Using chpasswd
The most reliable approach combines useradd
with chpasswd
:
#!/bin/bash
USERNAME="dummy"
PASSWORD="12345689"
useradd -d /home/$USERNAME -g idiots -m $USERNAME
echo "$USERNAME:$PASSWORD" | chpasswd
Method 2: Leveraging openssl for Encryption
When you need the encryption step in your script:
#!/bin/bash
USERNAME="dummy"
PASSWORD="12345689"
ENCRYPTED=$(openssl passwd -1 "$PASSWORD")
useradd -d /home/$USERNAME -g idiots -m -p "$ENCRYPTED" $USERNAME
Method 3: Using expect for Interactive Tools
For systems requiring passwd
interaction:
#!/bin/bash
USERNAME="dummy"
PASSWORD="12345689"
useradd -d /home/$USERNAME -g idiots -m $USERNAME
expect << EOF
spawn passwd $USERNAME
expect "New password:"
send "$PASSWORD\r"
expect "Retype new password:"
send "$PASSWORD\r"
expect eof
EOF
While these methods solve the technical problem, consider:
- Never store plaintext passwords in version control
- Use environment variables or secured credential stores
- Consider SSH key authentication instead for automation
- For production systems, use proper secrets management tools
For service accounts that shouldn't have password login:
useradd -r -s /usr/sbin/nologin -M service_account
This creates an account without a password or home directory, suitable for daemon processes.
When running useradd
with the -p
flag, the command expects an already-encrypted password string (typically from crypt
or similar hashing functions). This creates a chicken-and-egg problem for automation scripts where you need to:
- Create users programmatically
- Set initial passwords without human intervention
- Maintain script simplicity without external dependencies
The most robust approach uses chpasswd
which accepts plaintext passwords in a pipeline:
#!/bin/bash
# Create user with temporary disabled password
useradd -d /home/dummy -g idiots -m -s /bin/bash dummy
# Set password using chpasswd
echo "dummy:12345689" | chpasswd
Why this works better:
- No need to pre-compute password hashes
- Handles password complexity rules automatically
- Works across different Linux distributions
For systems where chpasswd
isn't available, you can generate the encrypted password on-the-fly:
#!/bin/bash
# Generate encrypted password
ENCRYPTED_PW=$(openssl passwd -6 "12345689")
# Create user with pre-hashed password
useradd -d /home/dummy -g idiots -m -p "$ENCRYPTED_PW" dummy
When implementing this in production scripts:
- Never store plaintext passwords in version control
- Consider using SSH keys instead of passwords when possible
- Set passwords to expire on first login (
passwd -e dummy
) - For Docker containers, use environment variables passed at runtime
Here's a robust implementation with error handling:
#!/bin/bash
USERNAME="dummy"
PASSWORD="ComplexPass123!"
GROUP="idiots"
# Check if user exists first
if id "$USERNAME" &>/dev/null; then
echo "User $USERNAME already exists" >&2
exit 1
fi
# Create user
if ! useradd -m -d "/home/$USERNAME" -g "$GROUP" -s /bin/bash "$USERNAME"; then
echo "Failed to create user $USERNAME" >&2
exit 1
fi
# Set password
if ! echo "$USERNAME:$PASSWORD" | chpasswd; then
echo "Failed to set password for $USERNAME" >&2
# Rollback user creation
userdel -r "$USERNAME"
exit 1
fi
echo "Successfully created user $USERNAME"