From 89315987941def84ee3a3aba40aa21d1a5eaf49c Mon Sep 17 00:00:00 2001 From: zihluwang Date: Wed, 2 Sep 2026 13:47:00 +0800 Subject: [PATCH] refactor(caddy): modularise deployment init Split the monolithic init-caddy.sh into four ordered, idempotent setup scripts for the Caddy deployment environment: - install-base-deps: install required apt packages - install-aws-cli: bootstrap AWS CLI v2 - install-caddy: fetch binary, create caddy:caddy, register systemd unit - prepare-caddy-cicd: create bot deploy user + SSH, hand over web root with ACLs, write site Caddyfile and start the service - Make the web root overridable via WEB_ROOT (default /var/www) --- Linux/install-aws-cli.sh | 32 +++++++ Linux/install-base-deps.sh | 23 +++++ Linux/{init-caddy.sh => install-caddy.sh} | 104 +++++---------------- Linux/prepare-caddy-cicd.sh | 106 ++++++++++++++++++++++ 4 files changed, 182 insertions(+), 83 deletions(-) create mode 100644 Linux/install-aws-cli.sh create mode 100644 Linux/install-base-deps.sh rename Linux/{init-caddy.sh => install-caddy.sh} (51%) create mode 100644 Linux/prepare-caddy-cicd.sh diff --git a/Linux/install-aws-cli.sh b/Linux/install-aws-cli.sh new file mode 100644 index 0000000..b05eaa0 --- /dev/null +++ b/Linux/install-aws-cli.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# +# AWS CLI Installation for Debian/Ubuntu +# Installs the AWS CLI v2 via the official installer bundle when it is not +# already available on the system. Required by install-caddy.sh for +# downloading the Caddy binary from S3-compatible storage. +# + +set -euo pipefail + +# Ensure script is executed with root privileges +if [ "$(id -u)" -ne 0 ]; then + echo "Error: This script must be run as root." >&2 + exit 1 +fi + +if command -v aws >/dev/null 2>&1; then + echo "==> aws-cli already installed at '$(command -v aws)'. Nothing to do." + exit 0 +fi + +echo "==> aws-cli not found, installing via official bundle..." +TMP_DIR=$(mktemp -d) +trap 'rm -rf "${TMP_DIR}"' EXIT + +curl -fsSL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "${TMP_DIR}/awscliv2.zip" +unzip -q "${TMP_DIR}/awscliv2.zip" -d "${TMP_DIR}" +"${TMP_DIR}/aws/install" + +echo "=========================================================" +echo " aws-cli installed successfully: $(command -v aws)" +echo "=========================================================" diff --git a/Linux/install-base-deps.sh b/Linux/install-base-deps.sh new file mode 100644 index 0000000..673601a --- /dev/null +++ b/Linux/install-base-deps.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# +# Base Dependencies Installation for Debian/Ubuntu +# Installs the packages required by the other server initialisation scripts. +# + +set -euo pipefail + +# Ensure script is executed with root privileges +if [ "$(id -u)" -ne 0 ]; then + echo "Error: This script must be run as root." >&2 + exit 1 +fi + +echo "==> Updating package index..." +apt-get update -qq + +echo "==> Installing base dependencies (acl, libcap2-bin, rsync, curl, unzip)..." +apt-get install -y acl libcap2-bin rsync curl unzip + +echo "=========================================================" +echo " Base dependencies installed successfully!" +echo "=========================================================" diff --git a/Linux/init-caddy.sh b/Linux/install-caddy.sh similarity index 51% rename from Linux/init-caddy.sh rename to Linux/install-caddy.sh index 55504d4..f87d7d9 100644 --- a/Linux/init-caddy.sh +++ b/Linux/install-caddy.sh @@ -1,12 +1,16 @@ #!/usr/bin/env bash # -# Caddy & Bot Deployment Environment Initialisation Script for Debian -# Supports automatic binary download from S3-compatible storage. +# Caddy Installation for Debian/Ubuntu +# Downloads the Caddy binary (from a local file or S3-compatible storage), +# creates the caddy:caddy system user and group, installs the binary with +# cap_net_bind_service, prepares the config directory and registers the +# systemd unit. The service itself is started by prepare-caddy-cicd.sh once +# the site Caddyfile and web root exist. # set -euo pipefail -# S3 and Binary Environment Variables (with fallback defaults) +# S3 and binary environment variables (with fallback defaults) S3_ENDPOINT="${S3_ENDPOINT:-}" S3_BUCKET="${S3_BUCKET:-}" S3_REGION="${S3_REGION:-us-east-1}" @@ -16,7 +20,6 @@ BIN_URI="${BIN_URI:-caddy-custom-linux-amd64}" BIN_SOURCE="./${BIN_URI}" BIN_DEST="/usr/local/bin/caddy" -WWW_ROOT="/var/www" CADDY_CONF_DIR="/etc/caddy" CADDY_DATA_DIR="/var/lib/caddy" @@ -26,21 +29,7 @@ if [ "$(id -u)" -ne 0 ]; then exit 1 fi -echo "==> [1/8] Installing base dependencies..." -apt-get update -qq -apt-get install -y acl libcap2-bin rsync curl unzip - -echo "==> [2/8] Ensuring aws-cli is installed..." -if ! command -v aws >/dev/null 2>&1; then - echo "aws-cli not found, installing via official bundle..." - TMP_DIR=$(mktemp -d) - curl -fsSL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "${TMP_DIR}/awscliv2.zip" - unzip -q "${TMP_DIR}/awscliv2.zip" -d "${TMP_DIR}" - "${TMP_DIR}/aws/install" - rm -rf "${TMP_DIR}" -fi - -echo "==> [3/8] Resolving Caddy binary..." +echo "==> [1/5] Resolving Caddy binary..." if [ ! -f "${BIN_SOURCE}" ]; then if [ -n "${S3_BUCKET}" ]; then echo "Local binary '${BIN_SOURCE}' not found. Downloading from S3 bucket '${S3_BUCKET}'..." @@ -65,7 +54,7 @@ else echo "Found local binary at '${BIN_SOURCE}', skipping S3 download." fi -echo "==> [4/8] Creating system users and groups..." +echo "==> [2/5] Creating caddy system user and group..." # Create caddy system user and group (no login shell, dedicated home) if ! getent group caddy >/dev/null 2>&1; then groupadd --system caddy @@ -81,72 +70,20 @@ if ! id -u caddy >/dev/null 2>&1; then caddy fi -# Create deployment user 'bot' -if ! id -u bot >/dev/null 2>&1; then - useradd -m -s /bin/bash bot -fi - -# Configure SSH directory for bot -install -d -m 700 -o bot -g bot /home/bot/.ssh -touch /home/bot/.ssh/authorized_keys -chown bot:bot /home/bot/.ssh/authorized_keys -chmod 600 /home/bot/.ssh/authorized_keys - -echo "==> [5/8] Installing Caddy binary and granting capabilities..." +echo "==> [3/5] Installing Caddy binary and granting capabilities..." install -m 750 -o root -g caddy "${BIN_SOURCE}" "${BIN_DEST}" # Grant capability to bind to privileged ports (<1024) without running as root setcap 'cap_net_bind_service=+ep' "${BIN_DEST}" -echo "==> [6/8] Setting up web root directory permissions (${WWW_ROOT})..." -mkdir -p "${WWW_ROOT}" - -# Set base ownership: bot (deployment) and caddy (web server group) -chown -R bot:caddy "${WWW_ROOT}" - -# Directory base permissions (SGID ensures newly created items inherit group 'caddy') -find "${WWW_ROOT}" -type d -exec chmod 2755 {} + -find "${WWW_ROOT}" -type f -exec chmod 644 {} + - -# Apply POSIX ACLs: bot has rwx, caddy has read/execute, with default inheritance -setfacl -R -m u:bot:rwx,u:caddy:rX "${WWW_ROOT}" -setfacl -R -d -m u:bot:rwx,u:caddy:rX "${WWW_ROOT}" - -echo "==> [7/8] Creating Caddy configuration and sample site..." +echo "==> [4/5] Preparing config directory (${CADDY_CONF_DIR})..." mkdir -p "${CADDY_CONF_DIR}" chown root:caddy "${CADDY_CONF_DIR}" chmod 755 "${CADDY_CONF_DIR}" -if [ ! -f "${CADDY_CONF_DIR}/Caddyfile" ]; then - cat <<'EOF' > "${CADDY_CONF_DIR}/Caddyfile" -:80 { - root * /var/www - file_server -} -EOF - chown root:caddy "${CADDY_CONF_DIR}/Caddyfile" - chmod 644 "${CADDY_CONF_DIR}/Caddyfile" -fi - -if [ ! -f "${WWW_ROOT}/index.html" ]; then - cat <<'EOF' > "${WWW_ROOT}/index.html" - - - - - Caddy Ready - - -

Caddy server is running

-

Static files root: /var/www

- - -EOF - chown bot:caddy "${WWW_ROOT}/index.html" - chmod 644 "${WWW_ROOT}/index.html" -fi - -echo "==> [8/8] Configuring systemd service and starting Caddy..." +echo "==> [5/5] Registering systemd service..." +# The unit is only registered here; prepare-caddy-cicd.sh starts it once the +# site Caddyfile and web root are in place. cat <<'EOF' > /etc/systemd/system/caddy.service [Unit] Description=Caddy @@ -173,12 +110,13 @@ WantedBy=multi-user.target EOF systemctl daemon-reload -systemctl enable --now caddy echo "=========================================================" -echo " Initialisation finished successfully!" +echo " Caddy installation finished successfully!" echo " - Binary: ${BIN_DEST} (Capabilities: cap_net_bind_service)" -echo " - Web Root: ${WWW_ROOT} (Managed by: bot, Read by: caddy)" -echo " - Config: ${CADDY_CONF_DIR}/Caddyfile" -echo " - Status: $(systemctl is-active caddy)" -echo "=========================================================" \ No newline at end of file +echo " - User/Group: caddy:caddy" +echo " - Config dir: ${CADDY_CONF_DIR}" +echo " - Service registered but not yet started." +echo " - Next: run prepare-caddy-cicd.sh to create the site config" +echo " and web root, then start Caddy." +echo "=========================================================" diff --git a/Linux/prepare-caddy-cicd.sh b/Linux/prepare-caddy-cicd.sh new file mode 100644 index 0000000..9e6ae85 --- /dev/null +++ b/Linux/prepare-caddy-cicd.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +# +# Caddy CI/CD Serving Preparation for Debian/Ubuntu +# Prepares the server so a CI/CD pipeline can publish content that Caddy +# serves: creates the deployment user 'bot' with its SSH access, creates and +# hands over the web root to bot:caddy with ACLs that keep it readable by the +# Caddy server, writes the default site Caddyfile and sample page, then starts +# the Caddy service. The web root defaults to /var/www and can be overridden +# by exporting the WEB_ROOT environment variable before running. +# + +set -euo pipefail + +# Web root served by Caddy; override by exporting WEB_ROOT before running +WEB_ROOT="${WEB_ROOT:-/var/www}" +CADDY_CONF_DIR="/etc/caddy" + +# Ensure script is executed with root privileges +if [ "$(id -u)" -ne 0 ]; then + echo "Error: This script must be run as root." >&2 + exit 1 +fi + +if [ ! -f /etc/systemd/system/caddy.service ]; then + echo "Error: caddy.service not found. Run install-caddy.sh first." >&2 + exit 1 +fi + +if ! getent group caddy >/dev/null 2>&1; then + echo "Error: group 'caddy' does not exist. Run install-caddy.sh first." >&2 + exit 1 +fi + +echo "==> [1/5] Creating deployment user 'bot'..." +# Create deployment user 'bot' +if ! id -u bot >/dev/null 2>&1; then + useradd -m -s /bin/bash bot +fi + +echo "==> [2/5] Configuring SSH directory for bot..." +# Configure SSH directory for bot +install -d -m 700 -o bot -g bot /home/bot/.ssh +touch /home/bot/.ssh/authorized_keys +chown bot:bot /home/bot/.ssh/authorized_keys +chmod 600 /home/bot/.ssh/authorized_keys + +echo "==> [3/5] Creating web root and handing it to bot (${WEB_ROOT})..." +if [ ! -d "${WEB_ROOT}" ]; then + mkdir -p "${WEB_ROOT}" + + cat < "${WEB_ROOT}/index.html" + + + + + Caddy Ready + + +

Caddy server is running

+

Static files root: ${WEB_ROOT}

+ + +EOF +else + echo " '${WEB_ROOT}' already exists - leaving existing content untouched." +fi + +# Set base ownership: bot (deployment) and caddy (web server group) +chown -R bot:caddy "${WEB_ROOT}" + +# Directory base permissions (SGID ensures newly created items inherit group 'caddy') +find "${WEB_ROOT}" -type d -exec chmod 2755 {} + +find "${WEB_ROOT}" -type f -exec chmod 644 {} + + +# Apply POSIX ACLs: bot has rwx, caddy has read/execute, with default inheritance +setfacl -R -m u:bot:rwx,u:caddy:rX "${WEB_ROOT}" +setfacl -R -d -m u:bot:rwx,u:caddy:rX "${WEB_ROOT}" + +echo "==> [4/5] Writing site Caddyfile (${CADDY_CONF_DIR}/Caddyfile)..." +mkdir -p "${CADDY_CONF_DIR}" +chown root:caddy "${CADDY_CONF_DIR}" +chmod 755 "${CADDY_CONF_DIR}" + +if [ ! -f "${CADDY_CONF_DIR}/Caddyfile" ]; then + cat < "${CADDY_CONF_DIR}/Caddyfile" +:80 { + root * ${WEB_ROOT} + file_server +} +EOF + chown root:caddy "${CADDY_CONF_DIR}/Caddyfile" + chmod 644 "${CADDY_CONF_DIR}/Caddyfile" +else + echo " '${CADDY_CONF_DIR}/Caddyfile' already exists - leaving it untouched." +fi + +echo "==> [5/5] Starting Caddy service..." +systemctl enable --now caddy + +echo "=========================================================" +echo " Caddy CI/CD preparation finished successfully!" +echo " - Deployment user: bot (SSH keys accepted in authorized_keys)" +echo " - Web Root: ${WEB_ROOT} (owned by bot:caddy, readable by caddy)" +echo " - Config: ${CADDY_CONF_DIR}/Caddyfile" +echo " - Status: $(systemctl is-active caddy)" +echo "========================================================="