Gemountetes Secret ohne Variable finden, Compose-Block zusammenfassen #5

Merged
nexus merged 2 commits from feature/secret-by-convention into main 2026-08-04 20:27:55 +02:00
Owner

Antwort auf die Frage "wieso braucht es HETZNER_API_TOKEN_FILE in der .env?" — die Antwort war: es braucht sie eigentlich nicht.

Die Variable wiederholte nur den Mount

Sie nannte ein zweites Mal, was in der Mount-Zeile schon steht. Und genau diese Wiederholung ging schief: Man traegt den Host-Pfad ein, der Container meldet die Datei als fehlend, obwohl sie auf dem Host sichtbar daliegt.

Liegt jetzt weder Wert noch Pfad in der Umgebung, sucht das Programm eine Datei mit dem Namen der Variablen in /run/secrets, /var/run/secrets und /secrets. Das ist genau der Name, den Docker und Podman einem so benannten Secret geben — ein einfacher secrets:-Block funktioniert damit ganz ohne Umgebungsvariable.

    volumes:
      - ./hetzner_api_token:/run/secrets/hetzner_api_token:ro

Mehr ist nicht noetig. Eine gesetzte Variable gewinnt weiterhin, ein abweichendes Mount-Ziel bleibt also erreichbar.

Compose-Block zusammengefasst

Bisher war das Thema ueber die Datei verstreut: Mount unter volumes:, user: weiter oben, Erklaerung in der README. Jetzt steht alles in einem Block beieinander — was die beiden Seiten der Mount-Zeile bedeuten, dass die rechte so bleiben sollte, und die user:-Zeile direkt daneben statt zum Nachschlagen in der README.

Zur user:-Zeile

Braucht man nur, wenn die Datei fuer UID 65532 nicht ohnehin lesbar ist. Das Image laeuft nicht als root — wer die Datei als root:root 0600 liegen laesst, muss also entweder user: setzen oder die Rechte anpassen.

Getestet

  • Zwei neue Testfaelle: gemountetes Secret wird ohne Variable gefunden; eine gesetzte Variable gewinnt weiterhin
  • make lint test-race gruen, 0 Lint-Issues, Domain-Schranke gruen, keine toten Anker
Antwort auf die Frage "wieso braucht es `HETZNER_API_TOKEN_FILE` in der .env?" — die Antwort war: es braucht sie eigentlich nicht. ## Die Variable wiederholte nur den Mount Sie nannte ein zweites Mal, was in der Mount-Zeile schon steht. Und genau diese Wiederholung ging schief: Man traegt den Host-Pfad ein, der Container meldet die Datei als fehlend, obwohl sie auf dem Host sichtbar daliegt. Liegt jetzt weder Wert noch Pfad in der Umgebung, sucht das Programm eine Datei mit dem Namen der Variablen in `/run/secrets`, `/var/run/secrets` und `/secrets`. Das ist genau der Name, den Docker und Podman einem so benannten Secret geben — ein einfacher `secrets:`-Block funktioniert damit ganz ohne Umgebungsvariable. ```yaml volumes: - ./hetzner_api_token:/run/secrets/hetzner_api_token:ro ``` Mehr ist nicht noetig. Eine gesetzte Variable gewinnt weiterhin, ein abweichendes Mount-Ziel bleibt also erreichbar. ## Compose-Block zusammengefasst Bisher war das Thema ueber die Datei verstreut: Mount unter `volumes:`, `user:` weiter oben, Erklaerung in der README. Jetzt steht alles in einem Block beieinander — was die beiden Seiten der Mount-Zeile bedeuten, dass die rechte so bleiben sollte, und die `user:`-Zeile direkt daneben statt zum Nachschlagen in der README. ## Zur `user:`-Zeile Braucht man nur, wenn die Datei fuer UID 65532 nicht ohnehin lesbar ist. Das Image laeuft **nicht** als root — wer die Datei als `root:root 0600` liegen laesst, muss also entweder `user:` setzen oder die Rechte anpassen. ## Getestet - Zwei neue Testfaelle: gemountetes Secret wird ohne Variable gefunden; eine gesetzte Variable gewinnt weiterhin - `make lint test-race` gruen, 0 Lint-Issues, Domain-Schranke gruen, keine toten Anker
Find a secret mounted at the conventional path without being told
All checks were successful
CI / test (pull_request) Successful in 1m40s
CI / image (push) Successful in 38s
CI / test (push) Successful in 1m40s
CI / image (pull_request) Successful in 39s
612813d5f5
The variable only ever repeated what the mount already said, and repeating it
is where people got it wrong: the host path goes in, the container reports the
file as missing, and the file is plainly there on the host. That round trip has
now cost several attempts to get right.

So when neither the value nor the path is configured, a file named after the
variable in /run/secrets, /var/run/secrets or /secrets is used. That is the
name Docker and Podman give a secret declared under that name, so a plain
secrets: block works with no environment at all. An explicit variable still
wins, and pointing it somewhere unusual still works.

The compose file now carries the whole arrangement in one block instead of
scattering it: what the mount's two sides mean, that the right side should stay
put, and the user: line that goes with it, sitting right there rather than in
the README.
Fix the secret file location instead of configuring it
All checks were successful
CI / test (push) Successful in 1m36s
CI / test (pull_request) Successful in 1m40s
CI / image (push) Successful in 55s
CI / image (pull_request) Successful in 38s
7eddef7f5f
HETZNER_API_TOKEN_FILE is gone. It never carried information: the volume mount
already said where the file is, and the variable only said it a second time —
while making it easy to say it differently. Writing the host side of the mount
into it is the natural mistake, and the result is a container reporting a
missing file that is sitting in plain view on the host. That cost several
rounds to get right, which is evidence enough about the design.

The location is now /run/secrets/hetzner_api_token and nothing else. Mount the
file there and it is used; no path, no variable, nothing in .env. That is also
where a compose secrets: block puts it, so such a block works untouched.
/var/run/secrets and /secrets are searched too, for Kubernetes.

An undocumented escape hatch would be a trap, so the variable is removed from
the code as well, not just from the docs. Anyone needing a different path can
mount the file to the fixed one.
nexus merged commit 20fd7ef3c7 into main 2026-08-04 20:27:55 +02:00
nexus deleted branch feature/secret-by-convention 2026-08-04 20:27:55 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nexus/hetzner-ddns!5
No description provided.