When writing administration scripts, we often face a security dilemma: while the script needs root privileges for some operations, most commands could (and should) run with normal user permissions. Running everything as root creates unnecessary security risks.
The traditional approach would be using su
to switch to a specific user account, but this introduces several problems:
# Problematic approach - requires knowing the username
su someuser -c "command"
Most Linux systems come with a built-in nobody
user specifically designed for running unprivileged operations. We can leverage this:
#!/bin/bash
# Privileged section
echo "Running as root"
# Switch to nobody for unprivileged operations
su nobody -s /bin/sh -c "echo 'Now running as nobody: \$(whoami)'"
# Back to root for privileged operations
echo "Back to root"
For more controlled environments, you could create and use a temporary user:
#!/bin/bash
# Create temporary user
TMP_USER="scriptuser_$(date +%s)"
useradd -M -s /bin/false $TMP_USER
# Use the temporary user
su $TMP_USER -c "echo 'Running as temporary user'"
# Cleanup
userdel $TMP_USER
For complex scripts, consider dropping privileges programmatically:
#!/bin/bash
drop_privileges() {
if [ "$(id -u)" -eq 0 ]; then
local target_user=${1:-nobody}
local target_gid=$(id -g $target_user)
local target_uid=$(id -u $target_user)
# Drop group privileges first
setgroups -g $target_gid
# Then drop user privileges
setuidgid $target_user sh -c "$2"
else
eval "$2"
fi
}
# Usage:
drop_privileges nobody "echo 'Running as nobody: \$(whoami)'"
- Always prefer built-in system users (nobody, daemon) when possible
- If creating temporary users, ensure proper cleanup even if the script fails
- Consider using
setuidgid
from daemontools for more controlled privilege dropping - Audit your script to ensure no privilege escalation paths exist
Here's how you might structure a package installation script:
#!/bin/bash
# Root-required operations
apt-get update
# Non-root operations
su nobody -s /bin/sh <<'ENDSCRIPT'
# Safe environment for downloading
wget https://example.com/package.tar.gz -O /tmp/package.tar.gz
tar xzf /tmp/package.tar.gz -C /tmp
ENDSCRIPT
# Back to root for installation
dpkg -i /tmp/package/*.deb
rm -rf /tmp/package*
When writing administrative shell scripts, we often face a dilemma: the script needs root privileges for certain operations, but running the entire script as root creates unnecessary security risks. Any vulnerability or error in non-privileged portions could potentially be exploited with elevated permissions.
The simplest solution is to use sudo -u
to temporarily switch to a less privileged user:
#!/bin/bash
# Privileged section
apt-get update
# Drop privileges
sudo -u nobody command_to_run_without_privileges
# Back to root
important_root_command
For more control, create a temporary restricted user:
#!/bin/bash
# Create restricted user
TEMP_USER="scriptuser_$(date +%s)"
useradd --system --shell /bin/false $TEMP_USER
# Use the user
sudo -u $TEMP_USER safe_command
# Clean up
userdel $TEMP_USER
For modern systems, consider capability-based privilege separation:
#!/bin/bash
# First part needs network admin capabilities
/sbin/setcap 'cap_net_admin=+ep' /path/to/network_tool
# Later sections run without capabilities
/sbin/setcap -r /path/to/network_tool
- Always minimize the time spent with elevated privileges
- Use
trap
to ensure cleanup even if script fails - Consider using
runuser
instead ofsudo
in some cases - Log all privilege transitions for security auditing
#!/bin/bash
# Root section
apt-get update
# Non-root section for logging
sudo -u nobody logger "Update completed at $(date)"
# Clean root operation
apt-get upgrade -y