When automating EC2 instance setup through user data scripts, one common pain point is modifying the /etc/sudoers
file to include custom paths in secure_path
. Unlike regular configuration files, sudoers requires special handling due to its sensitive nature and syntax validation.
Attempting to edit sudoers directly with tools like sed
or awk
is problematic because:
- The file has strict permission requirements (0440)
- Syntax errors can lock you out of sudo access
- The closing quote makes pattern matching tricky
Here's a robust method to append :/usr/local/bin
to secure_path:
#!/bin/bash
# Create temporary sudoers file
TMP_FILE=$(mktemp)
cat /etc/sudoers > $TMP_FILE
# Modify secure_path
sed -i 's|secure_path="$.*$"|secure_path="\1:/usr/local/bin"|' $TMP_FILE
# Validate and install
if visudo -cf $TMP_FILE; then
chmod 0440 $TMP_FILE
mv $TMP_FILE /etc/sudoers
else
echo "Error: Invalid sudoers syntax" >&2
rm -f $TMP_FILE
exit 1
fi
For better maintainability, consider creating a separate file in /etc/sudoers.d/
:
echo 'Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin"' \
| sudo tee /etc/sudoers.d/secure_path_override
# Set correct permissions
sudo chmod 0440 /etc/sudoers.d/secure_path_override
When dealing with potentially missing secure_path declaration:
# Check if secure_path exists
if ! grep -q 'secure_path=' /etc/sudoers; then
echo 'Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin"' \
| sudo tee -a /etc/sudoers
else
# Use the modification approach shown earlier
...
fi
Here's how to incorporate this into your EC2 launch configuration:
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="==BOUNDARY=="
--==BOUNDARY==
Content-Type: text/x-shellscript; charset="us-ascii"
#!/bin/bash
# Install required packages
yum install -y git make gcc-c++
# Node.js installation
git clone https://github.com/nodejs/node.git
cd node
./configure
make -j$(nproc)
make install
# Modify sudoers
TMP_SUDOERS=$(mktemp)
cat <<'EOF' > $TMP_SUDOERS
$(cat /etc/sudoers)
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin"
EOF
visudo -cf $TMP_SUDOERS && mv $TMP_SUDOERS /etc/sudoers
--==BOUNDARY==--
When automating EC2 instance setup through user data scripts, we often need to modify system configurations like /etc/sudoers
. The particular challenge comes when we need to append paths to secure_path
, which controls the PATH environment variable for sudo commands.
Directly editing /etc/sudoers
with tools like sed
or awk
is dangerous because:
- The file has strict syntax requirements
- Any syntax error can lock you out of sudo privileges
- The file typically has read-only permissions (440)
Here's a robust method to append :/usr/local/bin
to secure_path without breaking the sudoers file:
#!/bin/bash
# Create temporary sudoers file
TMP_SUDOERS=$(mktemp)
cat /etc/sudoers > $TMP_SUDOERS
# Modify secure_path
sed -i '/^Defaults\s\+secure_path="/ s/"$/:\/usr\/local\/bin"/' $TMP_SUDOERS
# Validate and install
if visudo -cf $TMP_SUDOERS; then
chmod 440 $TMP_SUDOERS
chown root:root $TMP_SUDOERS
mv $TMP_SUDOERS /etc/sudoers
else
echo "Failed to modify sudoers file" >&2
rm -f $TMP_SUDOERS
exit 1
fi
A more modular approach is to create a new file in /etc/sudoers.d/
:
#!/bin/bash
# Create custom sudoers file
echo 'Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin"' > /etc/sudoers.d/99-secure-path
# Set proper permissions
chmod 440 /etc/sudoers.d/99-secure-path
chown root:root /etc/sudoers.d/99-secure-path
# Verify syntax
visudo -c || { rm -f /etc/sudoers.d/99-secure-path; exit 1; }
For more complex scenarios where the secure_path might contain special characters or multiple entries:
#!/bin/bash
# Extract current secure_path
CURRENT_PATH=$(grep -Po '^Defaults\s+secure_path="\K[^"]+' /etc/sudoers)
# Append new path if not already present
if [[ "$CURRENT_PATH" != *"/usr/local/bin"* ]]; then
NEW_PATH="${CURRENT_PATH}:/usr/local/bin"
# Create temporary file
TMP_FILE=$(mktemp)
awk -v new_path="$NEW_PATH" \
'/^Defaults\s+secure_path="/ {$0="Defaults\tsecure_path=\"" new_path "\""} 1' \
/etc/sudoers > $TMP_FILE
# Validate and install
if visudo -cf $TMP_FILE; then
mv $TMP_FILE /etc/sudoers
chmod 440 /etc/sudoers
else
rm -f $TMP_FILE
exit 1
fi
fi
When implementing this in EC2 user data:
- Always include error handling
- Log all operations to /var/log/cloud-init-output.log
- Consider wrapping the script in cloud-init directives
- Test thoroughly in a non-production environment first