Programmatically Modify secure_path in Sudoers File for Automated EC2 Deployment


9 views

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