Files
automations/deployments/copyparty/README.md
T
57_WolveandClaude Opus 4.8 c00ca055f2 feat(copyparty): add file-server deployment with SFTP/FTPS + security-notices updater
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>
2026-06-29 15:56:24 -05:00

156 lines
6.7 KiB
Markdown

# copyparty
[copyparty](https://github.com/9001/copyparty) — a portable file server with
resumable uploads, dedup, WebDAV, **SFTP**, **FTPS**, media indexing and
thumbnails — behind Caddy with automatic Let's Encrypt TLS. Ships with a
**security-notices-aware updater** (`update.sh`).
The web UI / WebDAV is fronted by Caddy on 443; **SFTP and FTPS are served
directly by copyparty** (Caddy is HTTP-only and not in that path).
## Required `.env` values
| Variable | Notes |
|----------|-------|
| `COPYPARTY_DOMAIN` | Public hostname for the web UI (e.g. `files.example.com`). |
| `ACME_EMAIL` | Let's Encrypt registration email. |
| `DATA_DIR` | Host folder shared as the data root (mounted at `/w`). Point at a data disk. |
| `COPYPARTY_IMAGE` / `COPYPARTY_TAG` | Image edition (`copyparty/ac`) + pinned version. |
| `SFTP_PORT` / `FTPS_PORT` / `FTP_PASV_RANGE` | Direct-listener ports (`3922` / `3990` / `12000-12099`). |
| `FTP_NAT` | This host's public IP, for passive FTPS through NAT (blank if direct). |
| `UPDATE_POLICY` | `latest` (default) / `security` / `off` — see [Updates](#updates--security-notices). |
See [`.env.example`](.env.example) for the rest. Accounts and shares live in
[`copyparty.conf.example`](copyparty.conf.example) (deploy.sh generates the real
`cfg/copyparty.conf` with a random admin password on first run).
## Deploy
```bash
./automations.sh # Deploy on this host → deploy: copyparty
```
Or build + run the self-contained artifact:
```bash
./build.sh
scp deploy.sh root@host:
ssh root@host 'bash deploy.sh'
# non-interactive:
# COPYPARTY_DOMAIN=files.example.com ACME_EMAIL=me@example.com SKIP_PROMPTS=1 bash deploy.sh
```
Unattended provisioning: [`cloud-init.yml`](cloud-init.yml). On first run deploy.sh
pins `COPYPARTY_TAG` to the newest release, generates `cfg/copyparty.conf` (random
admin password, printed once) and a self-signed `cfg/ftps.pem`, opens the ports,
brings the stack up, and schedules `update.sh`.
## Accounts & shares
The default config is **private**: one `admin` account with full access to the
whole data root, nothing public. Edit `cfg/copyparty.conf` (commented examples
included) to add users or open read-only / anonymous-upload shares, then hot-reload:
```bash
docker compose exec copyparty kill -s USR1 1
```
## Real client IP (behind Caddy)
For the web UI, copyparty must see the genuine client IP (for its logs and abuse
bans), which means **both** sides are configured:
- **Caddy** is the internet edge, so it appends the real address to
`X-Forwarded-For` and also sets `X-Real-IP` (see [`Caddyfile`](Caddyfile)).
- **copyparty** trusts that upstream via `xff-src: lan` in
[`copyparty.conf.example`](copyparty.conf.example) — without it copyparty
ignores the header and logs/bans the Docker-network address.
`lan` (trust private ranges) is safe here because copyparty's HTTP port is never
published — only Caddy can reach it. If copyparty prints an `xff-src` warning at
startup, narrow it to the exact subnet it names. SFTP/FTPS are direct TCP, so
they already see the real client IP (no XFF involved).
## SFTP & FTPS
Both authenticate against the same `[accounts]` as the web UI.
```bash
sftp -P 3922 admin@files.example.com # SFTP (password auth enabled)
lftp -u admin -e 'set ftp:ssl-force true; set ssl:verify-certificate no' \
ftp://files.example.com:3990 # explicit-TLS FTPS
```
- **SFTP host key** is generated on first start and persisted under `/cfg`, so
clients don't get "host key changed" warnings across restarts.
- **FTPS cert** defaults to the self-signed `cfg/ftps.pem` (clients must accept
it). To serve a publicly-trusted cert, reuse Caddy's Let's Encrypt cert: mount
the `caddy-data` volume read-only into the copyparty service and point
copyparty at it, e.g.
```yaml
# docker-compose.yml, copyparty service:
volumes:
- caddy-data:/caddy:ro
```
```ini
# cfg/copyparty.conf [global]:
cert: /caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/files.example.com/files.example.com.crt
certkey: /caddy/caddy/certificates/acme-v02.api.letsencrypt.org-directory/files.example.com/files.example.com.key
```
(copyparty re-reads the cert on `kill -s USR1`; refresh after a renewal.)
- **Passive FTPS** needs the `FTP_PASV_RANGE` ports published (they are) **and**
`FTP_NAT` set to the server's public IP when clients connect through NAT.
- A published Docker port bypasses the host `INPUT` firewall, so set `BIND_ADDR`
to pin the listeners to a trusted interface on multi-homed hosts.
## Updates & security notices
copyparty doesn't self-update, but it publishes a machine-readable
**security-advisories** feed (the same one its built-in `--vc-url` check uses).
This deployment uses it on **two** levels:
1. **In-app** — `vc-url` + `vc-age` in `cfg/copyparty.conf` make copyparty itself
check whether the running version has a known advisory and warn in the log /
control panel. (`vc-exit`, opt-in, makes it shut down if vulnerable.)
2. **[`update.sh`](update.sh)** — reads the same feed to *act*, pinning the new
version in `.env` and recreating the container (with health-checked rollback).
```bash
./update.sh check # report current vs latest + any advisory; change nothing
./update.sh update # update now (TARGET_VERSION=1.20.11 to pin a version)
./update.sh install # (re)schedule the daily run / uninstall to stop
```
`UPDATE_POLICY` (in `.env` or `/etc/copyparty-update.conf`) controls the
scheduled `run`:
| Policy | Behaviour |
|--------|-----------|
| `latest` *(default)* | Update to the newest release whenever one exists. |
| `security` | Update **only** when the running version has a known advisory, to the patched release named in the feed. |
| `off` | Never change the running version (check/notify only). |
`VC_FEED` selects the feed (`advisories-panic` / `advisories` / `advisories-all`).
Update results reuse the ntfy config at `/etc/ssh-notify.conf` (same as the SSH
login notifier / host auto-update), when present.
## Files
| File | Purpose |
|---|---|
| `docker-compose.yml` | caddy + copyparty; publishes SFTP/FTPS/passive ports. |
| `Caddyfile` | TLS + reverse proxy for the web UI/WebDAV (real-IP headers). |
| `copyparty.conf.example` | Config template (globals, security check, SFTP/FTPS, accounts, volumes). |
| `update.sh` | Security-notices-aware container updater. |
| `.env.example` | Stack tunables. |
| `deploy.sh` / `build.sh` | Self-contained installer + archive embedder. |
| `cloud-init.yml` | Fresh-VM bootstrap (harden SSH, then deploy). |
## Notes
- No Anubis PoW gate — it would break WebDAV / API / upload clients.
- The web UI/WebDAV go through Caddy; SFTP/FTPS bypass it by design.
- DNS for `COPYPARTY_DOMAIN` must resolve to the host and 80/443 be reachable
before deploy for the LE cert to issue.