From 2d2dd2bc476e9ff041d0d60a20e989dd5f1b12b2 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Fri, 10 Jul 2026 12:34:21 +0200 Subject: [PATCH] fix(deploy): provision the certs dir traversable by the nonroot gateway MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Ansible base-directory loop created /opt/scrabble/certs at mode 0750 (deploy:deploy), like config/dumps/images. The gateway (and backend) run as the distroless nonroot UID 65532 — not the deploy user — so the container cannot traverse a 0750 certs dir and fails at startup with "mtls: load server keypair: ... permission denied", crash-looping. This is latent: a long-running container holds the keypair in memory and never re-reads the file, so the misconfig only bites when a container restarts (a host reboot / redeploy). A hoster maintenance reboot exposed it on prod — the gateway came back crash-looping while the deploy could not SSH in mid-reboot. Split certs out of the 0750 loop and create it 0755 (traversable). The keys stay 0644 by design (the gateway compose relies on it); the host is single-tenant + SSH-access-controlled, so a traversable certs dir adds no meaningful exposure. The live prod host was already chmod-fixed by hand; this keeps the next provisioning run from re-tightening it. --- deploy/ansible/roles/common/tasks/main.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/deploy/ansible/roles/common/tasks/main.yml b/deploy/ansible/roles/common/tasks/main.yml index ee11431..a2da8ba 100644 --- a/deploy/ansible/roles/common/tasks/main.yml +++ b/deploy/ansible/roles/common/tasks/main.yml @@ -213,6 +213,20 @@ loop: - "" - config - - certs - dumps - images + +# The certs dir holds the reverse-mTLS bot-link keypair, bind-mounted into the gateway (and +# backend) which run as the distroless nonroot UID 65532 — not the deploy user. The dir must be +# traversable by "other" (0755) or the nonroot process cannot open the 0644 keypair and crash-loops +# at startup ("mtls: load server keypair: ... permission denied") — a latent failure that only bites +# on a container restart (e.g. a host reboot), not while a long-running container holds the keypair +# in memory. The keys themselves are 0644 by design (see the gateway compose); the host is +# single-tenant and SSH-access-controlled, so a traversable certs dir adds no meaningful exposure. +- name: Create the scrabble certs directory (traversable by the nonroot gateway UID) + ansible.builtin.file: + path: "{{ scrabble_base_dir }}/certs" + state: directory + owner: "{{ deploy_user }}" + group: "{{ deploy_user }}" + mode: "0755" -- 2.52.0