- Shell 82.8%
- Dockerfile 17.2%
| .gitignore | ||
| apache-ssl.conf | ||
| charlie-init.sh | ||
| charlie.Dockerfile | ||
| docker-compose.yml | ||
| Dockerfile | ||
| entrypoint.sh | ||
| gateway.conf | ||
| gen-certs.sh | ||
| README.md | ||
| run-test.sh | ||
OCM HTTP-Signature dual-stack — local E2E
Five containers on a shared docker network for end-to-end OCM federation testing:
alice.local— in-development branch (the dual-stack PR), HTTPS, reached through the gatewaybob.local— in-development branch (the dual-stack PR), HTTPS, reached through the gatewaycharlie.local— vanillanextcloud:apachepeer, HTTPS. Validates the draft-cavage fallback path: charlie does not advertisehttp-sig, so alice/bob must fall back when federating with it.eve—mitmproxyinterception proxy. All three NC instances route outbound HTTP(S) through eve, which signs with the same local CA so HTTPS interception is transparent. Web UI on host port 8081 lets you browse the captured federation wire traffic.gateway— nginx in stream mode withssl_preread. Owns host port 443 and routes by SNI (TLS pass-through, no certs here) to alice / bob / charlie on the docker network. Lets the NC instances pretend they're on standard ports without actually binding 443 themselves.
alice and bob share the same in-development source tree (bind-mounted
from $NC_SRC); charlie runs the upstream image unmodified.
Prerequisites
docker+docker compose- A local checkout of
nextcloud/serverwith the dual-stack branch.
One-time setup
-
Add hostnames to
/etc/hostsso the browser can reach all instances:127.0.0.1 alice.local bob.local charlie.local -
Generate the local CA and per-host certs:
./gen-certs.sh -
(Optional but recommended) trust the CA system-wide so your browser doesn't show TLS warnings:
sudo cp certs/ca.crt /usr/local/share/ca-certificates/ocm-e2e-local-ca.crt sudo update-ca-certificates # Then restart Firefox/Chrome/etc.Or import
certs/ca.crtdirectly into your browser's trust store. Or just click through the warning the first time you visit each site.
Bring it up
export NC_SRC=$HOME/sources/nextcloud/server
docker compose up -d --build
docker compose logs -f # watch first-run install
The first start runs occ maintenance:install per container (or the
upstream image's own installer for charlie), enables the federation apps,
and creates a non-admin user. Subsequent starts skip that.
Logging in
| URL | Login | Password |
|---|---|---|
| https://alice.local/ | admin |
admin1234 |
| https://alice.local/ | alice |
alice1234 |
| https://bob.local/ | admin |
admin1234 |
| https://bob.local/ | bob |
bob1234 |
| https://charlie.local/ | admin |
admin1234 |
| https://charlie.local/ | charlie |
charlie1234 |
| http://127.0.0.1:8081/ (eve) | — | ocm (mitmweb password) |
Federated share targets use the plain cloud-ID format:
- on alice, share to
bob@bob.localorcharlie@charlie.local - on bob, share to
alice@alice.localorcharlie@charlie.local - on charlie, share to
alice@alice.localorbob@bob.local
The gateway listens on host 443 and SNI-routes to the right backend, and
container-to-container federation goes directly across the docker network
(also on 443), so browser URLs, cloud IDs, and .well-known/ocm
endpoints are all just https://<host>/... with no ports anywhere.
Eve — watching the federation wire
eve runs mitmweb and is the outbound HTTP(S) proxy for all three NC
peers (set via proxy=eve:8080 in each instance's config). It uses the
same local CA as alice/bob/charlie, so HTTPS interception is transparent
to them — discovery, JWKS fetches, and /ocm/shares POSTs all show up
decrypted.
Open http://127.0.0.1:8081/ and log in with password ocm to browse
the captured flows. Filter on ~u /ocm/ or ~u /.well-known/ to narrow
to federation traffic.
Watching the dual stack work
Tail the receiver's apache + nextcloud log while sharing:
docker compose exec bob tail -f /var/log/apache2/access.log /var/www/html/data/nextcloud.log
Look for:
POST /ocm/shareswith200— share notification accepted- A
Signature-Input: ocm=…header on inbound — RFC 9421 path was used - A
Signature: keyId="…"header withoutSignature-Input— draft-cavage
The same headers are visible decrypted in eve's mitmweb UI, which is usually easier than grepping logs.
Cavage fallback paths
Two ways to exercise the draft-cavage path:
-
Federate with charlie. charlie runs the upstream image and never advertises
http-sigin its OCM capabilities, so alice/bob fall back to draft-cavage when sharing tocharlie@charlie.local. This is the realistic "talking to an unpatched peer" test. -
Capability flip on a dual-stack peer. Disable signing on bob to make it stop advertising
http-sigeven though it could:docker compose exec -u www-data bob php occ \ config:app:set core ocm_signed_request_disabled --value=1 --lazy # verify it's no longer in capabilities curl -sk https://bob.local/.well-known/ocm | jq .capabilities # undo docker compose exec -u www-data bob php occ \ config:app:delete core ocm_signed_request_disabled
Reset
docker compose down -v
Removes all per-container config and data. Keeps the certs and source.
Layout
| File | Purpose |
|---|---|
Dockerfile |
PHP 8.2 + Apache + sodium/openssl/etc., bind-mounts $NC_SRC (alice/bob) |
charlie.Dockerfile |
Extends nextcloud:apache with the shared SSL vhost and bakes the local CA into the system trust store |
apache-ssl.conf |
SSL vhost + http→https redirect (shared by all three NC peers) |
gateway.conf |
nginx stream/ssl_preread config: SNI → alice/bob/charlie:443 |
gen-certs.sh |
Local CA + per-host certs into ./certs/ (alice, bob, charlie, plus the mitm bundle for eve) |
docker-compose.yml |
alice, bob, charlie, eve, gateway services with hostnames, isolated config/data, cert mounts, and the shared ocm network |
entrypoint.sh |
First-run install + federation apps + test user + CA trust + outbound proxy (alice/bob) |
charlie-init.sh |
Equivalent before-starting hook for the upstream image: federation apps, eve proxy, CA trust, charlie test user |
run-test.sh |
Optional automated smoke (discovery + JWKS + capability flip) |
Notes
- Source is bind-mounted for alice and bob, so editing PHP files in
$NC_SRCis visible immediately to both containers (no rebuild). For changes to autoload maps orconfig/you may wantdocker compose exec alice apachectl graceful. Charlie is the upstream image and is not affected by$NC_SRCedits — that's the point. - HTTPS only on alice/bob/charlie —
overwriteprotocol=httpsandoverwritehost=<name>.local. The CA is installed viaupdate-ca-certificatesin each container so the JWKS HTTPS fetch verifies normally withoutallowSelfSignedCertificates. - The gateway does TLS pass-through, not termination — backends keep
their per-host certs and terminate TLS themselves. Nothing else binds
host port 443, so something else holding it (another local web stack,
podman, etc.) will block
docker compose up. - All three NC peers route outbound HTTP(S) through
eve:8080. Eve uses the same local CA, so interception is transparent. - This setup lives outside
nextcloud/serverso iteration on it doesn't bump the upstream test gate.