Files
automations/deployments/copyparty/deploy.sh
T
57_WolveandClaude Opus 5 76d2a098cb fix(copyparty): make the documented FTP_NAT re-run remedy actually work
The summary printed at the end of every run says passive FTPS behind NAT is
fixed by re-running with FTP_NAT=... That could never work: the ftp-nat
substitution sat inside `if [[ ! -f "$CONF" ]]`, so on any re-run the conf
existed, the substitution was skipped, and ftp-nat stayed commented out.

cfg/copyparty.conf is the ONLY route FTP_NAT has to the service -- nothing
in docker-compose.yml interpolates it and the copyparty service is given no
environment, so .env's copy is a record, not the live setting. The remedial
run logged "exists; leaving it alone" as a green [+], reported healthy, and
reprinted the same advice. Passive FTPS failed exactly as before, with
nothing to distinguish "you did it wrong" from "it didn't take" -- and
because the template's commented example carries a literal IP, grepping the
conf shows an ftp-nat line with an address in it.

Lift the application out of the creation guard so it runs against an
existing conf too, and match `#?` so an already-set value (ISP change, typo)
is corrected rather than only the commented template line. Warn instead of
guessing if no ftp-nat line exists at all -- appending at EOF would land in
[accounts]. Kept above the chown, since `sed -i` rewrites as root.

`|| true` on the current-value read is load-bearing: on a still-commented
conf the grep matches nothing, and under `set -o pipefail` that would abort
the script inside the assignment -- the exact trap fixed in bf52426.

Also restart copyparty when the conf changed: compose will not recreate a
service whose image and config are unchanged, so `up -d` alone would leave
the edit on disk and the old value in the running process. And record the
value in .env so the two files do not disagree about what is deployed.

Verified: commented template applies; identical value is a no-op; a
different value is corrected; a conf with no ftp-nat line warns without
corruption; empty FTP_NAT skips the block entirely.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 13:43:59 -05:00

623 lines
32 KiB
Bash

#!/usr/bin/env bash
#
# deploy.sh -- deploy copyparty (caddy + copyparty file server) on Alpine,
# Debian or Alma. Single-node; runs as root.
#
# What this does:
# 1. Installs docker + compose if missing.
# 2. Lays down the stack files in $STACK_DIR.
# 3. Generates cfg/copyparty.conf on first run (random admin password) and a
# self-signed cfg/ftps.pem for FTPS; neither is overwritten on re-run.
# 4. Generates .env on first run, pinning COPYPARTY_TAG to the newest release.
# 5. Prompts for required values not preset (COPYPARTY_DOMAIN, ACME_EMAIL).
# 6. Opens 80/443 + SFTP/FTPS/passive ports on the host firewall if present.
# 7. Pulls images, brings the stack up, waits for health.
# 8. Installs + schedules update.sh (security-notices-aware) unless disabled.
#
# Caddy fronts the web UI / WebDAV over TLS; SFTP and FTPS are published directly
# by copyparty (not proxied). copyparty trusts Caddy's X-Forwarded-For so logs
# and bans use the real client IP (see cfg/copyparty.conf `xff-src`).
#
# Idempotent: re-run to apply config changes / pull new images.
#
# Self-contained: docker-compose.yml, Caddyfile, copyparty.conf.example,
# .env.example and update.sh are embedded as a base64 tar.gz at the bottom of
# this file. Rebuild with build.sh after editing the loose source files.
#
# Usage:
# bash deploy.sh # interactive prompts
# COPYPARTY_DOMAIN=files.example.com ACME_EMAIL=me@x.com \
# SKIP_PROMPTS=1 bash deploy.sh # non-interactive
# DATA_DIR=/mnt/disk/files FTP_NAT=203.0.113.10 bash deploy.sh
# SKIP_DOCKER_INSTALL=1 bash deploy.sh
set -euo pipefail
: "${STACK_DIR:=/srv/copyparty}"
: "${SKIP_DOCKER_INSTALL:=0}"
: "${FORCE:=0}"
: "${SKIP_PROMPTS:=0}"
[[ "$SKIP_PROMPTS" == "1" ]] && FORCE=1
: "${COPYPARTY_DOMAIN:=}"
: "${ACME_EMAIL:=}"
: "${COPYPARTY_IMAGE:=copyparty/ac}"
: "${COPYPARTY_TAG:=latest}"
: "${PUID:=1000}"
: "${PGID:=1000}"
: "${DATA_DIR:=/srv/copyparty/data}"
: "${BIND_ADDR:=}"
: "${SFTP_PORT:=3922}"
: "${FTPS_PORT:=3990}"
: "${FTP_PASV_RANGE:=12000-12099}"
: "${FTP_NAT:=}"
: "${UPDATE_POLICY:=latest}"
: "${VC_FEED:=advisories}"
: "${COPYPARTY_AUTOUPDATE:=1}" # 0 = install update.sh but don't schedule it
: "${CADDY_TAG:=2-alpine}"
log() { printf '\033[1;32m[+]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[!]\033[0m %s\n' "$*" >&2; }
die() { printf '\033[1;31m[x]\033[0m %s\n' "$*" >&2; exit 1; }
[[ $EUID -eq 0 ]] || die "Run as root."
# ---------------------------------------------------------------------------
# OS detection + Docker install (Alpine / Debian / Alma). Inlined (this deploy.sh
# is self-contained / scp'd standalone) rather than sourced from oslib.sh.
# ---------------------------------------------------------------------------
osfam() {
local id="" like=""
if [[ -r /etc/os-release ]]; then
id="$(. /etc/os-release 2>/dev/null && echo "${ID:-}")"
like="$(. /etc/os-release 2>/dev/null && echo "${ID_LIKE:-}")"
fi
case " $id $like " in
*" alpine "*) echo alpine ;;
*" debian "*|*" ubuntu "*) echo debian ;;
*" rhel "*|*" fedora "*|*" centos "*) echo rhel ;;
*) echo "${id:-unknown}" ;;
esac
}
pkg_install() { # best-effort install of a package across the three families
case "$(osfam)" in
alpine) apk add -q "$@" || true ;;
debian) DEBIAN_FRONTEND=noninteractive apt-get install -y -qq "$@" || true ;;
rhel) dnf install -y -q "$@" || true ;;
esac
}
# fetch a URL to stdout with whatever's available (curl or wget).
fetch() {
if command -v curl >/dev/null 2>&1; then curl -fsSL "$1" 2>/dev/null
elif command -v wget >/dev/null 2>&1; then wget -qO- "$1" 2>/dev/null
fi
}
install_docker() {
if command -v docker >/dev/null 2>&1; then
log "Docker already installed: $(docker --version)"
else
log "Installing Docker (OS: $(osfam))..."
case "$(osfam)" in
alpine) apk add -q docker docker-cli-compose openrc ;;
debian|rhel) command -v curl >/dev/null 2>&1 || pkg_install curl
curl -fsSL https://get.docker.com | sh ;;
*) die "Unsupported OS for auto Docker install. Set SKIP_DOCKER_INSTALL=1 and install Docker yourself." ;;
esac
fi
if command -v rc-update >/dev/null 2>&1; then
rc-update add docker default >/dev/null 2>&1 || true
rc-service docker status >/dev/null 2>&1 || rc-service docker start
elif command -v systemctl >/dev/null 2>&1; then
systemctl enable --now docker >/dev/null 2>&1 || systemctl start docker || true
fi
}
open_web_ports() {
# Register web (80/443) + SFTP/FTPS + the passive-FTP range. Prefer the host
# firewall (harden-firewall.sh); else ufw/firewalld if active.
#
# NOTE: published Docker ports reach the host via nat/FORWARD and BYPASS the
# INPUT firewall, so this is belt-and-braces + self-documentation. Pin the
# real exposure with BIND_ADDR and copyparty's own access control.
local pasv_colon="${FTP_PASV_RANGE/-/:}" # 12000-12099 -> 12000:12099 (iptables/ufw/ports.d)
local pasv_hyphen="${FTP_PASV_RANGE}" # 12000-12099 (firewalld)
if [[ -d /etc/firewall/ports.d && -x /usr/local/sbin/firewall-apply ]]; then
log "Registering 80,443,${SFTP_PORT},${FTPS_PORT},${pasv_colon}/tcp with host firewall..."
printf '80/tcp\n443/tcp\n%s/tcp\n%s/tcp\n%s/tcp\n' \
"$SFTP_PORT" "$FTPS_PORT" "$pasv_colon" > /etc/firewall/ports.d/copyparty.rule
/usr/local/sbin/firewall-apply
elif command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q '^Status: active'; then
log "ufw active -- allowing web + SFTP/FTPS..."
local p
for p in 80/tcp 443/tcp "${SFTP_PORT}/tcp" "${FTPS_PORT}/tcp" "${pasv_colon}/tcp"; do
ufw allow "$p" >/dev/null
done
elif command -v firewall-cmd >/dev/null 2>&1 && firewall-cmd --state >/dev/null 2>&1; then
log "firewalld active -- allowing web + SFTP/FTPS..."
firewall-cmd -q --add-service=http --permanent
firewall-cmd -q --add-service=https --permanent
firewall-cmd -q --add-port="${SFTP_PORT}/tcp" --permanent
firewall-cmd -q --add-port="${FTPS_PORT}/tcp" --permanent
firewall-cmd -q --add-port="${pasv_hyphen}/tcp" --permanent
firewall-cmd -q --reload
fi
}
# ----------------------------------------------------------------------------
# Extract embedded archive
# ----------------------------------------------------------------------------
SCRIPT_DIR=$(mktemp -d -t copyparty-deploy.XXXXXX)
trap 'rm -rf "$SCRIPT_DIR"' EXIT
extract_archive() {
grep -a -A 9999999 '^__ARCHIVE_BELOW__$' "$0" \
| tail -n +2 \
| base64 -d \
| tar -xz -C "$SCRIPT_DIR"
}
if grep -q -a '^__ARCHIVE_BELOW__$' "$0"; then
log "Extracting embedded deployment files..."
extract_archive
else
die "No embedded archive found. Run build.sh to embed deployment files."
fi
EMBEDDED=(docker-compose.yml Caddyfile copyparty.conf.example .env.example update.sh)
for f in "${EMBEDDED[@]}"; do
[[ -f "$SCRIPT_DIR/$f" ]] || die "Embedded archive missing $f"
done
# ----------------------------------------------------------------------------
# Prompt for required vars
# ----------------------------------------------------------------------------
prompt() {
local varname="$1" message="$2"
local -n ref="$varname"
if [[ -z "${ref:-}" ]]; then
[[ "$SKIP_PROMPTS" == "1" ]] && die "$varname required (set it in the environment; running with SKIP_PROMPTS=1)."
read -r -p "$message: " ref
[[ -n "$ref" ]] || die "$varname required."
fi
}
prompt COPYPARTY_DOMAIN "Public hostname for the web UI (e.g. files.example.com)"
prompt ACME_EMAIL "Let's Encrypt email"
# ----------------------------------------------------------------------------
# Docker + firewall
# ----------------------------------------------------------------------------
if [[ "$SKIP_DOCKER_INSTALL" != "1" ]]; then
install_docker
fi
open_web_ports
# ----------------------------------------------------------------------------
# Stack directory + files
# ----------------------------------------------------------------------------
log "Setting up $STACK_DIR..."
install -d -m 0750 "$STACK_DIR"
install -m 0644 "$SCRIPT_DIR/docker-compose.yml" "$STACK_DIR/docker-compose.yml"
install -m 0644 "$SCRIPT_DIR/Caddyfile" "$STACK_DIR/Caddyfile"
install -m 0755 "$SCRIPT_DIR/update.sh" "$STACK_DIR/update.sh"
install -d -m 0750 "$STACK_DIR/cfg"
# Data root (bind-mounted at /w), owned by the runtime UID/GID.
install -d -m 0750 "$DATA_DIR"
chown "$PUID:$PGID" "$DATA_DIR"
ENV_FILE="$STACK_DIR/.env"
set_env() { # <KEY> <value>: update KEY in .env, or append if absent
local key="$1" val="$2" esc
esc=${val//\\/\\\\}; esc=${esc//|/\\|}; esc=${esc//&/\\&}
if grep -qE "^${key}=" "$ENV_FILE"; then
sed -i -e "s|^${key}=.*|${key}=${esc}|" "$ENV_FILE"
else
printf '%s=%s\n' "$key" "$val" >> "$ENV_FILE"
fi
}
# Pin COPYPARTY_TAG to the newest release so the running version is explicit.
if [[ "$COPYPARTY_TAG" == "latest" || -z "$COPYPARTY_TAG" ]]; then
_v="$(fetch "https://api.github.com/repos/9001/copyparty/releases/latest" \
| grep -oE '"tag_name"[[:space:]]*:[[:space:]]*"[^"]+"' | head -n1 \
| sed -E 's/.*"v?([0-9][^"]*)".*/\1/')"
if [[ -n "${_v:-}" ]]; then COPYPARTY_TAG="$_v"; log "Pinned COPYPARTY_TAG=${_v} (newest release)."
else warn "Could not resolve the newest release; using tag 'latest'."; COPYPARTY_TAG="latest"; fi
fi
# ----------------------------------------------------------------------------
# Generate copyparty.conf (admin password) + self-signed FTPS cert -- once.
# ----------------------------------------------------------------------------
CONF="$STACK_DIR/cfg/copyparty.conf"
if [[ ! -f "$CONF" ]]; then
ADMIN_PW="$(head -c 32 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' | head -c 24)"
log "Generating $CONF (admin account)..."
install -m 0640 "$SCRIPT_DIR/copyparty.conf.example" "$CONF"
sed -i "s|__ADMIN_PW__|${ADMIN_PW}|" "$CONF"
else
log "$CONF exists; keeping it."
fi
# FTP_NAT has exactly one route to the running service: this conf file. Nothing
# in docker-compose.yml interpolates it and the copyparty service is given no
# environment, so .env's copy is a record, not the live setting. The summary at
# the end tells operators to fix passive FTPS by re-running with FTP_NAT=... --
# so that has to apply to an EXISTING conf too, not only a freshly created one.
# Must stay above the chown further down, since `sed -i` rewrites as root.
CONF_CHANGED=0
if [[ -n "$FTP_NAT" ]]; then
# `|| true` is load-bearing: on a still-commented conf the grep matches
# nothing, and under `set -o pipefail` that would abort this assignment.
cur_nat="$(grep -E '^[[:space:]]*ftp-nat:' "$CONF" | head -n1 | sed -E 's/^[[:space:]]*ftp-nat:[[:space:]]*//; s/[[:space:]]*#.*$//; s/[[:space:]]*$//' || true)"
if [[ "$cur_nat" == "$FTP_NAT" ]]; then
: # already applied
elif grep -qE '^[[:space:]]*#?[[:space:]]*ftp-nat:' "$CONF"; then
log "Setting 'ftp-nat: ${FTP_NAT}' in $CONF (was: ${cur_nat:-unset})."
# `#?` so an already-set value is corrected, not just the commented
# template line.
sed -i -E "s|^[[:space:]]*#?[[:space:]]*ftp-nat:.*| ftp-nat: ${FTP_NAT}|" "$CONF"
CONF_CHANGED=1
else
warn "No ftp-nat line in $CONF to set. Add ' ftp-nat: ${FTP_NAT}' under [global] by hand."
fi
fi
# Read back the admin password for the summary (works on first run + re-runs).
# Strip the leading 'admin:', any trailing inline #comment, and surrounding space.
ADMIN_PW="$(grep -E '^[[:space:]]*admin:' "$CONF" | head -n1 \
| sed -E 's/^[[:space:]]*admin:[[:space:]]*//; s/[[:space:]]*#.*$//; s/[[:space:]]*$//')"
FTPS_PEM="$STACK_DIR/cfg/ftps.pem"
if [[ ! -f "$FTPS_PEM" ]]; then
command -v openssl >/dev/null 2>&1 || { log "Installing openssl for the FTPS cert..."; pkg_install openssl; }
command -v openssl >/dev/null 2>&1 || die "openssl is required to mint the FTPS cert; install it and re-run."
log "Generating self-signed FTPS certificate (CN=${COPYPARTY_DOMAIN})..."
_k="$(mktemp)"; _c="$(mktemp)"
if ! openssl req -x509 -newkey rsa:2048 -sha256 -days 3650 -nodes \
-keyout "$_k" -out "$_c" -subj "/CN=${COPYPARTY_DOMAIN}" \
-addext "subjectAltName=DNS:${COPYPARTY_DOMAIN}" 2>/dev/null; then
# older openssl without -addext
openssl req -x509 -newkey rsa:2048 -sha256 -days 3650 -nodes \
-keyout "$_k" -out "$_c" -subj "/CN=${COPYPARTY_DOMAIN}"
fi
cat "$_k" "$_c" > "$FTPS_PEM" # copyparty --cert wants key + chain concatenated
rm -f "$_k" "$_c"
chmod 0640 "$FTPS_PEM"
else
log "$FTPS_PEM exists; leaving it."
fi
# Everything copyparty (running as PUID) must read/write under /cfg.
chown -R "$PUID:$PGID" "$STACK_DIR/cfg"
# ----------------------------------------------------------------------------
# Seed .env (first run only)
# ----------------------------------------------------------------------------
if [[ ! -f "$ENV_FILE" ]]; then
log "Seeding $ENV_FILE..."
install -m 0600 "$SCRIPT_DIR/.env.example" "$ENV_FILE"
set_env COPYPARTY_DOMAIN "$COPYPARTY_DOMAIN"
set_env ACME_EMAIL "$ACME_EMAIL"
set_env COPYPARTY_IMAGE "$COPYPARTY_IMAGE"
set_env COPYPARTY_TAG "$COPYPARTY_TAG"
set_env PUID "$PUID"
set_env PGID "$PGID"
set_env DATA_DIR "$DATA_DIR"
set_env BIND_ADDR "$BIND_ADDR"
set_env SFTP_PORT "$SFTP_PORT"
set_env FTPS_PORT "$FTPS_PORT"
set_env FTP_PASV_RANGE "$FTP_PASV_RANGE"
set_env FTP_NAT "$FTP_NAT"
set_env UPDATE_POLICY "$UPDATE_POLICY"
set_env VC_FEED "$VC_FEED"
set_env CADDY_TAG "$CADDY_TAG"
else
log ".env exists; leaving it alone."
fi
# .env's FTP_NAT is a record rather than the live setting, but let it disagree
# with the conf and the next reader cannot tell which one is deployed.
if [[ -n "$FTP_NAT" ]]; then
cur_env_nat=$(sed -n 's/^FTP_NAT=//p' "$ENV_FILE" | tail -n1)
if [[ "$cur_env_nat" != "$FTP_NAT" ]]; then
set_env FTP_NAT "$FTP_NAT"
log "Recorded FTP_NAT=${FTP_NAT} in $ENV_FILE."
fi
fi
# Validate required values landed.
missing=()
for var in COPYPARTY_DOMAIN ACME_EMAIL; do
grep -E "^${var}=.+$" "$ENV_FILE" >/dev/null || missing+=("$var")
done
(( ${#missing[@]} == 0 )) || die "Missing values in $ENV_FILE: ${missing[*]}"
# ----------------------------------------------------------------------------
# Confirm
# ----------------------------------------------------------------------------
if [[ "$FORCE" != "1" ]]; then
cat <<EOF
About to pull images and start the copyparty stack from $STACK_DIR.
Caddy will request a Let's Encrypt cert for ${COPYPARTY_DOMAIN}. DNS for that
name must already point at this host, and ports 80/443 must be reachable from
the internet. SFTP (${SFTP_PORT}) and FTPS (${FTPS_PORT}) will also be opened.
Continue? [y/N]
EOF
read -r ans
[[ "${ans,,}" == "y" || "${ans,,}" == "yes" ]] || { warn "Aborted."; exit 0; }
fi
cd "$STACK_DIR"
log "Pulling images..."
docker compose pull
log "Starting stack..."
docker compose up -d --remove-orphans
# compose does not recreate a service whose image and compose config are
# unchanged, so a conf edit on a re-run would sit on disk while the running
# process keeps the old ftp-nat. Restart it explicitly.
if [[ "$CONF_CHANGED" == "1" ]]; then
log "copyparty.conf changed; restarting copyparty to apply it..."
docker compose restart copyparty
fi
# ----------------------------------------------------------------------------
# Wait for health
# ----------------------------------------------------------------------------
log "Waiting for services to become healthy (up to 120s)..."
deadline=$(( $(date +%s) + 120 ))
while (( $(date +%s) < deadline )); do
status=$(docker compose ps --format '{{.Service}} {{.Health}}' 2>/dev/null || true)
unhealthy=$(echo "$status" | awk '$2 != "healthy" && $2 != "" {print $1}')
if [[ -z "$unhealthy" && -n "$status" ]]; then
log "All services healthy."
break
fi
sleep 5
done
# ----------------------------------------------------------------------------
# Install + schedule the updater
# ----------------------------------------------------------------------------
if [[ "$COPYPARTY_AUTOUPDATE" != "0" ]]; then
log "Scheduling the copyparty updater (policy=${UPDATE_POLICY})..."
STACK_DIR="$STACK_DIR" UPDATE_POLICY="$UPDATE_POLICY" VC_FEED="$VC_FEED" \
bash "$STACK_DIR/update.sh" install || warn "Updater scheduling failed (non-fatal)."
else
log "COPYPARTY_AUTOUPDATE=0 -- update.sh installed but not scheduled."
fi
echo
log "Stack status:"
docker compose ps
echo
cat <<EOF
================================================================
DEPLOYED -- copyparty file server
Web UI: https://${COPYPARTY_DOMAIN}
SFTP: sftp -P ${SFTP_PORT} admin@${COPYPARTY_DOMAIN}
FTPS: explicit TLS on port ${FTPS_PORT}
lftp -u admin -e 'set ftp:ssl-force true; set ssl:verify-certificate no' \\
ftp://${COPYPARTY_DOMAIN}:${FTPS_PORT}
Stack dir: ${STACK_DIR}
Login: user 'admin' / password '${ADMIN_PW}'
(stored in ${STACK_DIR}/cfg/copyparty.conf)
Files: ${DATA_DIR} (mounted at /w; shared at the site root)
Notes:
* FTPS uses a self-signed cert (${STACK_DIR}/cfg/ftps.pem). Clients must accept
it, or swap in Caddy's Let's Encrypt cert -- see deployments/copyparty/README.md.
* Passive FTPS through NAT needs FTP_NAT set to this host's public IP (re-run
with FTP_NAT=..., or set ftp-nat in cfg/copyparty.conf).
* Real client IP comes from Caddy via X-Forwarded-For (trusted with xff-src).
Add users / open shares:
edit ${STACK_DIR}/cfg/copyparty.conf, then:
docker compose exec copyparty kill -s USR1 1 # hot-reload, no restart
Updates (security-notices aware):
${STACK_DIR}/update.sh check # current vs latest + any advisory match
${STACK_DIR}/update.sh update # update now (TARGET_VERSION=x.y.z to pin)
Scheduled daily; change UPDATE_POLICY (latest|security|off) in
${STACK_DIR}/.env or /etc/copyparty-update.conf, or: update.sh uninstall
Manage:
docker compose logs -f
docker compose pull && docker compose up -d # manual update
docker compose down # stop, keep data
docker compose down -v # stop, keep host data dir
Or just re-run this script -- it's idempotent.
================================================================
EOF
# IMPORTANT: do not put any code below this exit. Everything after the
# __ARCHIVE_BELOW__ marker is the embedded tar.gz payload (base64), appended by
# build.sh.
exit 0
__ARCHIVE_BELOW__
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
VKYylalMZSrT/036L64TOhwAoAAA