7. Deploy Action
7. Deploy Action
← Previous: 6. Authorization Model | Index | Next: 8. Group Synchronisation →
7.1 Why a Wrapped Action Exists
Deploying on the compose-based hosts means running docker compose inside /srv/pnats/<project>. The Docker daemon runs as root; anyone who can talk to it can ask it to do anything root can — mount / into a container and chroot into it, read any file, append to sudoers. Three one-line commands, no exploit. So "deploy but cannot write" is impossible while a person can reach Docker (Invariant 4).
pn-deploy lets a person trigger Docker to do a specific, pre-approved thing without ever talking to Docker. It is the only sudo rule Deployers and Editors possess, and it runs as the deploy service account — the sole member of the docker group.
7.2 Verbs and Who May Use Them
The wrapper mirrors the compose verbs engineers use; the arguments are validated rather than the verbs restricted.
| Verb | Deployer | Editor | Notes |
|---|---|---|---|
up [service…] [--build] [--ref TAG] | ✅ (--build only where the project allows it) | ✅ | pull/build then bring up |
restart [service…] | ✅ | ✅ | |
stop [service…] | ✅ | ✅ | |
pull / build [service…] | ✅ | ✅ | |
logs [service] [-f] / ps | ✅ | ✅ | |
rollback | ✅ | ✅ | to the previous image reference kept per project |
down | ❌ | ✅ (confirmation on prod) | removes the stack — an outage |
exec <service> <cmd> | ❌ | ✅ (I/O-logged) | a shell inside a container may be root inside it |
7.3 Validation — What Is Checked, Not Trusted
| Input | Check |
|---|---|
| Project | must exist in the root-owned allow-list (/etc/pn-deploy/projects): project → compose path → permitted verbs and options |
| Service names | must appear in the project's own compose configuration |
--ref | must be a well-formed tag or digest |
| Anything else | refused. There is no argument pass-through to Docker — no -v /:/host, no --privileged, ever |
| Concurrency | a per-project lock; the compose project name is already pinned, so no duplicate stacks |
| Environment | scrubbed; the wrapper's own configuration is root-owned and Ansible-managed, so an Operator cannot widen the allow-list — only a PR can |
7.4 Evidence
Every invocation records who (Entra UPN via SUDO_USER), project, verb, services, ref, and result to the journal → Sentinel, and posts a one-line note to #infra (the existing convention: "drop a note in #infra if you change shared state").
7.5 Developer Experience
Nobody should have to read this RFC to deploy. The design ships with:
| Element | Purpose |
|---|---|
dc alias, available inside /srv/pnats/<project> | maps the familiar docker compose … shape onto pn-deploy for the current project |
pn-deploy --help | explains every verb and why a refused thing is refused |
Companion quickstart in ovh-infra (docs/pn-deploy-quickstart.md) | plain-language guide in the style of arc-ssh-quickstart.md: old command → new command, common errors, who to ask |
| Today | After |
|---|---|
cd /srv/pnats/pnow-ats-v2 && docker compose up -d --build api | cd /srv/pnats/pnow-ats-v2 && dc up -d --build api |
docker compose logs -f worker | dc logs -f worker |
docker compose restart api | dc restart api |
No password prompt; same result; attributed to the person.
7.6 The Escape Hatch
For the small fraction of work the wrapper does not cover (a one-off docker run, inspecting the socket, anything exotic): activate PIM Admin once (≤4h, MFA + reason) and use raw docker as root for the window. Routine work needs no elevation; unusual work needs one activation. Deployers have no escape hatch by design.
7.7 Failure Modes
| Failure | Behaviour |
|---|---|
| Wrapper missing or misconfigured | deploys fail closed; no fallback to raw Docker for non-admins |
| Docker down | error surfaced to the caller |
| Lock held | wait/refuse with a clear message |
| Wrapper defect blocks a deploy | Editor uses the escape hatch; wrapper fixed by PR |
7.8 Scope
Applies to the compose-based classes only — uat, prod, erp. Kubernetes deploys remain GitOps (ArgoCD); no human deploys on k8s or pve hosts by hand, so those classes have no deploy action.
Document Navigation
| Previous | Index | Next |
|---|---|---|
| ← 6. Authorization Model | Table of Contents | 8. Group Synchronisation → |
End of Section 7