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>
156 lines
6.7 KiB
Markdown
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.
|