RPi5 Docker socket: upgrade to mTLS (2376) and wire the Terraform runner #33
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Background
Follow-up to #31 / PR #32, which added the
lyrathorpe-rpi5Docker host with thedaemon on plain TCP 2375, no TLS, protected only by a LAN-source nftables
rule. This issue tracks upgrading that to mutual TLS (mTLS) on 2376 and wiring
the client end into the OpenTofu/Terraform repo (
lyrathorpe/Terraform), whoseGitea Actions runner drives the Docker provider against this host.
Plain 2375 is root-equivalent to anyone who reaches the port. mTLS makes both
ends prove identity with certificates and removes the "trust the whole subnet"
assumption.
How the pieces connect today
system/machine/RPi5/docker.nix— dockerd exposed on 2376/2375 viathe systemd
docker.socketListenStream; firewall rule scoped to10.187.1.0/24.lyrathorpe/Terraformrootmain.tfhas an emptyprovider "docker" {}(env-driven)..gitea/workflows/apply.ymlandvalidate.ymlsetDOCKER_HOST: ${{ vars.DOCKER_HOST }}and runruns-on: ubuntu-latest, i.e. inside the act_runner's job containers. TheDocker provider already honours
DOCKER_HOST, and additionallyDOCKER_TLS_VERIFY+DOCKER_CERT_PATH— so no provider-block change isrequired if those env vars are set.
Certificates (prerequisite for both ends)
step-ca/cfssl/openssl). Keep the CA keyoffline.
will use in
DOCKER_HOST:lyrathorpe-rpi5, its FQDN if any, and its LANIP (an IP SAN is required if
DOCKER_HOSTuses an IP).extendedKeyUsage = serverAuth.extendedKeyUsage = clientAuth.revocation (CRL or short-lived certs). These are secrets — never place
private keys in the world-readable Nix store.
Server side —
nixfiles(system/machine/RPi5/docker.nix)be done via socket activation (
fd://); dockerd must own the listener:daemon.settings.hosts = [ "unix:///run/docker.sock" "tcp://0.0.0.0:2376" ]plus
tls = true; tlsverify = true; tlscacert/tlscert/tlskey = <paths>.daemon.jsonhostsconflicts with the unit's-H fd://. Either drop the-H fd://from
systemd.services.docker(ExecStartoverride) or disablesystemd.sockets.dockerso dockerd owns all listeners. Pick one; documentwhy. Remove the current
systemd.sockets.docker.socketConfig.ListenStreamTCP addition.
no secrets manager today; introduce one (sops-nix or agenix) and reference
the decrypted paths, or deploy the files to e.g.
/var/lib/docker-certsvia a non-Nix channel and point the daemon settings at them. Files must be
root-readable, key
0600.remove 2375 entirely. (
networking.firewall.extraInputRules.)system/machine/RPi5/README.mdanddocker.nixcomments: flip the"mTLS is the upgrade path" note to the implemented design; document the
cert paths and the secrets mechanism.
Client side —
lyrathorpe/Terraform(Gitea Actions runner)DOCKER_CA_PEM,DOCKER_CERT_PEM,DOCKER_KEY_PEM.apply.ymlandvalidate.yml, add a step that decodes those into adir (e.g.
$RUNNER_TEMP/docker-certs/{ca,cert,key}.pem,chmod 600 key.pem)and set the env:
-
DOCKER_HOST=tcp://lyrathorpe-rpi5:2376(update the existingvars.DOCKER_HOST; must match a SAN on the server cert)-
DOCKER_TLS_VERIFY=1-
DOCKER_CERT_PATH=$RUNNER_TEMP/docker-certsThe empty
provider "docker" {}block then needs no change.and bind-mount a read-only cert dir into job containers via the runner
config (
container.options/valid_volumesin the act_runner config),with the same three env vars. Choose secrets-in-workflow (portable) vs
mounted-volume (no secrets in job logs/files); document the choice.
2376 (routing/firewall from the runner's network to
10.187.1.0/24).(
Services/Docker/), wire the cert volume/secret there too.Acceptance criteria
docker -H tcp://lyrathorpe-rpi5:2376 --tlsverify infosucceeds; the same call without valid client certs is refused.
tofu plan/applyin the Terraform repo runs against the Pi over mTLSfrom CI with no plaintext Docker exposure.
Notes
nixfiles; client tasks inlyrathorpe/Terraform. Consider a sibling issue on that repo referencing thisone.
Client-side counterpart filed on the Terraform repo: lyrathorpe/Terraform#59