No description
  • Shell 82.8%
  • Dockerfile 17.2%
Find a file
2026-05-06 15:14:57 +02:00
.gitignore first commit 2026-05-06 15:14:57 +02:00
apache-ssl.conf first commit 2026-05-06 15:14:57 +02:00
charlie-init.sh first commit 2026-05-06 15:14:57 +02:00
charlie.Dockerfile first commit 2026-05-06 15:14:57 +02:00
docker-compose.yml first commit 2026-05-06 15:14:57 +02:00
Dockerfile first commit 2026-05-06 15:14:57 +02:00
entrypoint.sh first commit 2026-05-06 15:14:57 +02:00
gateway.conf first commit 2026-05-06 15:14:57 +02:00
gen-certs.sh first commit 2026-05-06 15:14:57 +02:00
README.md first commit 2026-05-06 15:14:57 +02:00
run-test.sh first commit 2026-05-06 15:14:57 +02:00

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 gateway
  • bob.local — in-development branch (the dual-stack PR), HTTPS, reached through the gateway
  • charlie.local — vanilla nextcloud:apache peer, HTTPS. Validates the draft-cavage fallback path: charlie does not advertise http-sig, so alice/bob must fall back when federating with it.
  • evemitmproxy interception 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 with ssl_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/server with the dual-stack branch.

One-time setup

  1. Add hostnames to /etc/hosts so the browser can reach all instances:

    127.0.0.1 alice.local bob.local charlie.local
    
  2. Generate the local CA and per-host certs:

    ./gen-certs.sh
    
  3. (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.crt directly 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.local or charlie@charlie.local
  • on bob, share to alice@alice.local or charlie@charlie.local
  • on charlie, share to alice@alice.local or bob@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/shares with 200 — share notification accepted
  • A Signature-Input: ocm=… header on inbound — RFC 9421 path was used
  • A Signature: keyId="…" header without Signature-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:

  1. Federate with charlie. charlie runs the upstream image and never advertises http-sig in its OCM capabilities, so alice/bob fall back to draft-cavage when sharing to charlie@charlie.local. This is the realistic "talking to an unpatched peer" test.

  2. Capability flip on a dual-stack peer. Disable signing on bob to make it stop advertising http-sig even 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_SRC is visible immediately to both containers (no rebuild). For changes to autoload maps or config/ you may want docker compose exec alice apachectl graceful. Charlie is the upstream image and is not affected by $NC_SRC edits — that's the point.
  • HTTPS only on alice/bob/charlie — overwriteprotocol=https and overwritehost=<name>.local. The CA is installed via update-ca-certificates in each container so the JWKS HTTPS fetch verifies normally without allowSelfSignedCertificates.
  • 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/server so iteration on it doesn't bump the upstream test gate.