stack_secrets reads KV v2 on the target host and writes the stack's .env at 0600, failing loudly on a sealed vault, a bad token, a missing field or an unsafe value. Adds an example OpenBao stack on app and moves webapp onto it; metrics keeps a hand-placed .env. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
56 lines
2.8 KiB
YAML
56 lines
2.8 KiB
YAML
# OpenBao: the secret store every other stack with a credential reads from.
|
|
#
|
|
# Loopback only. It is published to 127.0.0.1 on the host, is not on proxy-net,
|
|
# and sets no VIRTUAL_HOST and no LETSENCRYPT_HOST, so nothing outside the host
|
|
# can reach it. That is the whole reason the compose_stack role does its vault
|
|
# read from the host rather than from the control node. See docs/secrets.md.
|
|
services:
|
|
openbao:
|
|
container_name: openbao
|
|
# Pinned, per server/README.md — and more firmly than most: a restart seals
|
|
# the vault and needs a manual unseal afterwards, so moving this tag is a
|
|
# deliberate maintenance window rather than a routine update.
|
|
image: docker.io/openbao/openbao:2.6.2
|
|
restart: unless-stopped
|
|
ports:
|
|
# The bind address matters more than the port. Without the 127.0.0.1 the
|
|
# secret store is on every interface the host has.
|
|
- "127.0.0.1:8200:8200"
|
|
# The image's default command is `server -dev`, which starts an in-memory
|
|
# vault with a known root token. Override it — but with the subcommand
|
|
# alone: the entrypoint script already appends `-config=/openbao/config`
|
|
# whenever the first argument is `server`, so passing that flag again here
|
|
# loads the directory twice and OpenBao tries to bind the listener twice,
|
|
# failing with "address already in use".
|
|
command: server
|
|
volumes:
|
|
- ./config:/openbao/config:ro
|
|
- ./data:/openbao/file
|
|
environment:
|
|
# The bao CLI defaults to https://127.0.0.1:8200, but this listener has
|
|
# TLS disabled (openbao.hcl sets tls_disable = true), so every
|
|
# `docker exec ... bao ...` would otherwise fail with a TLS handshake
|
|
# error. Set here so every exec'd command inherits it without needing
|
|
# its own -e BAO_ADDR. See docs/secrets.md.
|
|
BAO_ADDR: http://127.0.0.1:8200
|
|
# ./config is mounted read-only, but the entrypoint's chown of
|
|
# /openbao/config runs on every start whenever its uid does not match
|
|
# openbao's, which it never will here since the payload is synced as
|
|
# the deploy user — and that chown would fail against a read-only
|
|
# mount anyway. The config chown is echo-guarded, but the entrypoint's
|
|
# matching chowns of /openbao/logs and /openbao/file are not, and the
|
|
# script runs under `set -e` as uid 100: an un-runnable-as-non-root
|
|
# chown there would exit the container rather than just log noise. This
|
|
# stack survives today only because openbao.yml's stack_dirs hands
|
|
# data/ to 100:1000 before first start, so the stat short-circuits
|
|
# every chown — SKIP_CHOWN makes that not load-bearing.
|
|
SKIP_CHOWN: "true"
|
|
# mlock, so the unsealed keyring is never paged out to disk.
|
|
cap_add:
|
|
- IPC_LOCK
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "1m"
|
|
max-file: "1"
|