When launching Amazon EC2 instances, user-data scripts execute with root privileges by default. While this provides broad system access, it creates security concerns for long-running processes. The Amazon Linux 2012.03 AMI (and subsequent versions) don't natively support non-root execution of user-data scripts.
Here are three reliable approaches to run processes as non-root users:
#!/bin/bash
# Method 1: Explicit user switching
useradd -m appuser
su - appuser -c "/path/to/your/script.sh"
For production environments, consider using process managers:
#!/bin/bash
# Create systemd service running as non-root
cat > /etc/systemd/system/myapp.service << EOF
[Unit]
Description=My Application
[Service]
User=appuser
ExecStart=/usr/bin/python3 /opt/app/main.py
[Install]
WantedBy=multi-user.target
EOF
systemctl enable myapp
Combine these techniques with AWS IAM roles for secure credential management:
#!/bin/bash
# Create restricted credentials directory
mkdir -p /home/appuser/.aws
chown -R appuser:appuser /home/appuser/.aws
cat > /home/appuser/.aws/config << EOF
[default]
region = us-west-2
credential_process = /usr/bin/aws-credentials-helper
EOF
- Always set proper file permissions (chmod/chown)
- Use AWS Systems Manager for secret management
- Implement least privilege IAM policies
- Monitor processes with CloudWatch
If encountering permission issues:
# Check process ownership
ps aux | grep your_process
# Verify file permissions
namei -l /path/to/resource
When deploying applications on Amazon EC2 instances through user-data scripts, all commands execute with root privileges by default. While convenient for system configuration, this poses security risks for long-running application processes. The Amazon Linux 2012.03 AMI (and subsequent versions) maintain this behavior for backward compatibility.
The simplest method involves using sudo -u
to switch user context:
#!/bin/bash
# Create application user if not exists
id -u appuser &>/dev/null || useradd -m appuser
# Launch process as non-root
sudo -u appuser /path/to/your/application --daemonize
For production environments, consider integrating with systemd or supervisor:
#!/bin/bash
cat > /etc/systemd/system/myapp.service << EOF
[Unit]
Description=My Application
After=network.target
[Service]
User=appuser
Group=appgroup
ExecStart=/usr/bin/python3 /opt/app/main.py
Restart=always
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable myapp
systemctl start myapp
For complex deployments, create an isolated environment:
#!/bin/bash
APP_USER="deployer"
APP_HOME="/opt/application"
useradd -r -m -d "$APP_HOME" -s /bin/bash "$APP_USER"
mkdir -p "$APP_HOME"/{bin,logs,tmp}
chown -R "$APP_USER":"$APP_USER" "$APP_HOME"
# Switch context using su -c
su - "$APP_USER" -c "/opt/application/bin/start_server.sh"
Always follow these practices when downgrading privileges:
- Set proper directory permissions (750 for sensitive locations)
- Use
chmod
to restrict executable permissions - Consider implementing Linux capabilities instead of full root
- Log all privilege transitions for auditing
If processes fail to start:
# Check user permissions
sudo -u appuser id
sudo -u appuser ls -l /path/to/resource
# Verify environment variables
sudo -u appuser env
# Test process startup manually
sudo -u appuser /path/to/binary --dry-run