171 lines
4.7 KiB
Bash
171 lines
4.7 KiB
Bash
#!/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
|