c00ca055f2
New deployments/copyparty/: copyparty (copyparty/ac) behind Caddy/LE for the web UI/WebDAV, plus its own SFTP (password auth) and FTPS listeners published directly. Ships update.sh, which drives container updates off copyparty's security-advisories API (api.copyparty.eu/advisories) -- policies latest|security|off. - Real client IP end-to-end: Caddy XFF/X-Real-IP + copyparty xff-src: lan. - SFTP host key + self-signed FTPS cert generated/persisted in /cfg; admin password generated on first deploy; conf auto-included via the image's % /cfg. - Firewall opens 80/443 + SFTP/FTPS + passive range (colon form for ports.d). - Wired into automations.sh, README, .gitignore; cloud-init for fresh VMs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
79 lines
3.1 KiB
YAML
79 lines
3.1 KiB
YAML
# copyparty stack -- caddy (TLS) + copyparty (portable file server).
|
|
#
|
|
# Topology:
|
|
# Browser/WebDAV --> caddy:443 --> copyparty:3923 (HTTP, internal)
|
|
# SFTP / FTPS --> copyparty:{3922,3990,passive} (direct, NOT via caddy)
|
|
#
|
|
# Caddy terminates TLS and reverse-proxies the web/WebDAV UI. copyparty's SFTP
|
|
# and FTPS listeners are their own TCP services, so they are published straight
|
|
# from the copyparty container -- Caddy is HTTP-only and not in that path.
|
|
#
|
|
# The image's baked-in config does `chdir /w` + `% /cfg` (include every *.conf in
|
|
# /cfg), so our ./cfg/copyparty.conf is picked up automatically -- no `command:`
|
|
# override needed. XDG_CONFIG_HOME=/cfg in the image, so generated SSH host keys
|
|
# live under /cfg and persist across restarts (the bind-mount is read-write).
|
|
|
|
name: copyparty
|
|
|
|
volumes:
|
|
caddy-data:
|
|
caddy-config:
|
|
|
|
services:
|
|
# ---------------------------------------------------------------------------
|
|
# Caddy -- TLS termination + reverse proxy for the web/WebDAV UI. The only
|
|
# service on 80/443. Auto-issues a Let's Encrypt cert for ${COPYPARTY_DOMAIN}.
|
|
# ---------------------------------------------------------------------------
|
|
caddy:
|
|
image: caddy:${CADDY_TAG:-2-alpine}
|
|
container_name: caddy
|
|
restart: unless-stopped
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
- "443:443/udp" # HTTP/3
|
|
volumes:
|
|
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
|
- caddy-data:/data
|
|
- caddy-config:/config
|
|
environment:
|
|
COPYPARTY_DOMAIN: "${COPYPARTY_DOMAIN}"
|
|
ACME_EMAIL: "${ACME_EMAIL}"
|
|
depends_on:
|
|
- copyparty
|
|
healthcheck:
|
|
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:2019/config/"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 10s
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# copyparty -- the file server. HTTP 3923 stays internal (Caddy fronts it);
|
|
# only the direct SFTP/FTPS listeners are published. Runs as PUID:PGID so
|
|
# files on the host bind-mount have sane ownership.
|
|
#
|
|
# A published Docker port BYPASSES the host INPUT firewall, so set BIND_ADDR
|
|
# to pin these listeners to a trusted interface if the box is multi-homed.
|
|
# ---------------------------------------------------------------------------
|
|
copyparty:
|
|
image: ${COPYPARTY_IMAGE:-copyparty/ac}:${COPYPARTY_TAG:-latest}
|
|
container_name: copyparty
|
|
restart: unless-stopped
|
|
user: "${PUID:-1000}:${PGID:-1000}"
|
|
ports:
|
|
- "${BIND_ADDR:-0.0.0.0}:${SFTP_PORT:-3922}:3922" # SFTP
|
|
- "${BIND_ADDR:-0.0.0.0}:${FTPS_PORT:-3990}:3990" # FTPS (explicit TLS)
|
|
- "${BIND_ADDR:-0.0.0.0}:${FTP_PASV_RANGE:-12000-12099}:${FTP_PASV_RANGE:-12000-12099}" # FTPS passive
|
|
volumes:
|
|
- ${DATA_DIR:-/srv/copyparty/data}:/w
|
|
- ./cfg:/cfg
|
|
healthcheck:
|
|
# copyparty's own python -- no extra deps in the image. A successful TCP
|
|
# connect to the HTTP listener means the server is up.
|
|
test: ["CMD", "python3", "-c", "import socket; socket.create_connection(('127.0.0.1',3923),3).close()"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 20s
|