feat: add nginx reload and validation wrappers with sudo permissions

This commit is contained in:
GW_MC
2025-12-22 17:18:36 +08:00
parent 6e85bda13f
commit 5cffb0a519
7 changed files with 323 additions and 13 deletions

View File

@@ -0,0 +1,170 @@
#!/bin/sh
set -eu
# This init script installs a minimal nginx reload wrapper and a sudoers
# entry so the `yanpm-agent` user can perform a controlled reload via sudo.
WRAPPER_PATH="/usr/local/sbin/yanpm-nginx-reload"
SUDOERS_PATH="/etc/sudoers.d/yanpm-agent"
AGENT_USER="${YANPM_AGENT_USER:-yanpm-agent}"
# validate wrapper
VALIDATE_PATH="/usr/local/sbin/yanpm-nginx-validate"
# validate file wrapper
VALIDATE_FILE_PATH="/usr/local/sbin/yanpm-nginx-validate-file"
echo "[cont-init.d] install-reload-wrapper: setting up nginx reload helper"
# find nginx binary
NGINX_BIN="$(command -v nginx || true)"
if [ -z "${NGINX_BIN}" ]; then
echo "Warning: nginx binary not found in PATH; wrapper will still be created but may fail at runtime." >&2
NGINX_BIN="/usr/sbin/nginx"
fi
# Create wrapper
mkdir -p /usr/local/sbin /etc/sudoers.d
cat > "${WRAPPER_PATH}" <<- 'EOF'
#!/bin/sh
exec "@NGINX_BIN@" -c /etc/nginx/nginx.conf -s reload
EOF
# Replace placeholder with actual path
sed -i "s|@NGINX_BIN@|${NGINX_BIN}|g" "${WRAPPER_PATH}" || true
chmod 0750 "${WRAPPER_PATH}"
chown root:root "${WRAPPER_PATH}" || true
#
#
#
# Create validate wrapper
cat > "${VALIDATE_PATH}" <<- 'EOF'
#!/bin/sh
exec "@NGINX_BIN@" -c /etc/nginx/nginx.conf -t
EOF
# Replace placeholder with actual path in validate wrapper
sed -i "s|@NGINX_BIN@|${NGINX_BIN}|g" "${VALIDATE_PATH}" || true
chmod 0750 "${VALIDATE_PATH}"
chown root:root "${VALIDATE_PATH}" || true
#
#
#
# Create validate file wrapper (secure)
cat > "${VALIDATE_FILE_PATH}" <<-'EOF'
#!/bin/sh
set -eu
if [ $# -ne 1 ]; then
echo "Usage: $0 <nginx-config-file>" >&2
exit 2
fi
INPUT="$1"
# Resolve absolute path
if command -v readlink >/dev/null 2>&1; then
TARGET="$(readlink -f -- "$INPUT" 2>/dev/null || true)"
elif command -v realpath >/dev/null 2>&1; then
TARGET="$(realpath -- "$INPUT" 2>/dev/null || true)"
else
echo "Error: no path resolver (readlink/realpath) available" >&2
exit 3
fi
if [ -z "$TARGET" ]; then
echo "Error: cannot resolve path: $INPUT" >&2
exit 4
fi
# Must be a regular file and not a symlink
if [ ! -f "$TARGET" ] || [ -L "$TARGET" ]; then
echo "Error: ${TARGET} is not a regular file" >&2
exit 5
fi
# must be created by agent user
AGENT_UID="$(id -u yanpm-agent 2>/dev/null || true)"
if [ -z "$AGENT_UID" ]; then
echo "Error: yanpm-agent user not found" >&2
exit 6
fi
FILE_UID="$(stat -c %u -- "$TARGET" 2>/dev/null || true)"
if [ "$FILE_UID" != "$AGENT_UID" ]; then
echo "Error: ${TARGET} not owned by yanpm-agent user" >&2
exit 7
fi
# Ensure file is not world-writable; allow typical 664 (rw-rw-r--)
if command -v stat >/dev/null 2>&1; then
MODE="$(stat -c %a -- "$TARGET" 2>/dev/null || true)"
if [ -n "$MODE" ]; then
OTHERS=$(( MODE % 10 ))
if [ $(( OTHERS & 2 )) -ne 0 ]; then
echo "Error: ${TARGET} is world-writable" >&2
exit 8
fi
fi
elif command -v find >/dev/null 2>&1; then
if find "$TARGET" -maxdepth 0 -perm /002 -print -quit >/dev/null 2>&1; then
echo "Error: ${TARGET} is world-writable" >&2
exit 8
fi
fi
exec "@NGINX_BIN@" -c "$TARGET" -t
EOF
# Replace placeholder with actual path in validate file wrapper
sed -i "s|@NGINX_BIN@|${NGINX_BIN}|g" "${VALIDATE_FILE_PATH}" || true
chmod 0750 "${VALIDATE_FILE_PATH}"
chown root:root "${VALIDATE_FILE_PATH}" || true
echo "Created wrapper: ${WRAPPER_PATH} (owned by root, mode 750)"
#
#
#
# Ensure sudoers entry exists allowing the agent to run only this wrapper as root
if command -v sudo >/dev/null 2>&1; then
echo "sudo present; creating sudoers entry"
cat > "${SUDOERS_PATH}" <<- EOF
# Allow ${AGENT_USER} to run the nginx reload and validate wrappers without a password
${AGENT_USER} ALL=(root) NOPASSWD: ${WRAPPER_PATH}, ${VALIDATE_PATH}, ${VALIDATE_FILE_PATH}
EOF
chmod 0440 "${SUDOERS_PATH}" || true
echo "Wrote sudoers entry: ${SUDOERS_PATH}"
else
echo "sudo not found; attempting to install"
if command -v apk >/dev/null 2>&1; then
apk add --no-cache sudo || true
elif command -v apt-get >/dev/null 2>&1; then
apt-get update || true
apt-get install -y sudo || true
elif command -v yum >/dev/null 2>&1; then
yum install -y sudo || true
else
echo "No known package manager to install sudo; please ensure sudo is available in the image." >&2
fi
if command -v sudo >/dev/null 2>&1; then
cat > "${SUDOERS_PATH}" <<- EOF
# Allow ${AGENT_USER} to run the nginx reload and validate wrappers without a password
${AGENT_USER} ALL=(root) NOPASSWD: ${WRAPPER_PATH}, ${VALIDATE_PATH}, ${VALIDATE_FILE_PATH}
EOF
chmod 0440 "${SUDOERS_PATH}" || true
echo "Installed sudo and wrote sudoers entry: ${SUDOERS_PATH}"
else
echo "Failed to install sudo; the agent will not be able to reload nginx via sudo." >&2
fi
fi
exit 0