Serve Woodpecker pipeline secrets from Vault #67
No reviewers
Labels
No labels
blocked
bug
duplicate
effort
high
effort
low
effort
medium
effort
minimal
enhancement
help wanted
invalid
pr
feedback
pr
reviewing
question
status
backlog
status
document
status
done
status
implement
status
pending
status
planning
status
reviewing
waiting
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
tepichord/milner!67
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/woodpecker-vault-secret"
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?
Why
Woodpecker has no way to give a pipeline a secret that VSO manages. VSO writes
Kubernetes Secrets, and those reach a step only as environment variables of the
server or the agent, while a step's
from_secretresolves through Woodpecker'sown secret store or a secret-extension HTTP endpoint. Both are empty today —
which is how
annia-infra's merged-PR pipeline came to run with an unset$FORGEJO_TOKENand quietly do nothing.This adds the endpoint: lazysteff's
woodpecker-vault-secret-extension, pinned toupstream commit
a568dce(tag v0.2.4), plus its Vault credentials and theVaultStaticSecretthat carries them. A pipeline secret then has one home —Vault — at
secret/forgejo/token, the same PATozwill read.Pieces
woodpecker-secret-extension/Dockerfile— built by hand and pushed to zot(§9k step 3), because the image tag has to exist before the manifest that pins
it can merge.
ozalready has this shape, so the header documents the buildrather than adding a CI job.
gitops/.../apps/woodpecker/secret-extension.yaml— the extension's config(one rule: repository
tepichord/annia-infra, eventpull_request_closed,secret
FORGEJO_TOKENread fromforgejo/token), its Deployment(distroless non-root, read-only root, probes, requests and limits) and a
ClusterIP Service. No Ingress: only the server calls it.
gitops/.../secrets/woodpecker-secret-extension.yaml— the AppRolerole_id/secret_idand Woodpecker's Ed25519 public key, synced fromsecret/woodpecker/secret-extensionwith arolloutRestartTarget.apps/woodpecker/values.yaml—WOODPECKER_SECRET_EXTENSION_ENDPOINTandWOODPECKER_EXTENSIONS_ALLOWED_HOSTS: private(the allowed-host matchermatches the resolved ClusterIP, and
external— the default — refuses it).OPERATIONS.md§9k is the procedure that reproduces all of this, includingwhat a signing-key rotation does to it; §9g and §9c are updated for the new
consumer.
tests/test_gitops.pypins whatkustomizeand the policy testscannot see: the manifest tag against the Dockerfile, the server's endpoint
host and port against the rendered Service, and every
*_filein the configagainst the volume that mounts it.
Two things worth an explicit sign-off
allow_forks: true, and that is forced rather than chosen.Woodpecker cannot express "same-repo PR":
pipeline.from_forkisjson:"from_fork,omitempty", so a closed pull request in this repositoryomits the field and the extension reads the silence as forked. The gate that
actually holds is Woodpecker's own and sits upstream of the extension — a
repository's default approval mode is
forks, so a fork's pipeline is parkedin
blockedbefore any secret is fetched.tepichord/annia-inframust stayat
forksor stricter: atnone, this rule would hand the forge token to afork's pipeline, which runs the fork's own workflow file.
exception to the Kubernetes-auth rule in
AGENTS.md. The program supportsonly AppRole and static tokens, so it holds a
role_id/secret_idpair in aKubernetes Secret instead of projecting a service-account token. The policy
it gets is one KV path plus token self-lookup and renew.
Deployment order
The Vault half (§9k steps 1–2, root token: enable AppRole, write the policy and
role, mint the credentials, then write the PAT and the extension's three values)
has to happen before this merges. The extension authenticates to Vault before it serves and exits on a
failed login, so with the manifests merged first the pods crash-loop and the
appsKustomization never reaches Ready.The image is already in zot:
milner/woodpecker-secret-extension:0.2.4.Checks
kustomize buildandkubeconform -stricton both roots,conftest48/48(apps) and 32/32 (secrets),
hadolinton the Dockerfile,yamllinton thevalues,
pytest192 passed / 8 skipped.9k step 2 wrote the role_id as a literal in the same `kv put` as the public key, because only one value per invocation can come from the stdin that vault-0's wrapper shares with the token. That exception was documented but not needed: the three values write one per call, each from stdin, and the caveat goes away. The key fetch also gains `-f`. Without it a failed fetch or a proxy's error page is stored as the public key, and the extension then rejects every signature ("invalid signature") for a reason nothing in the KV entry explains.16102713e75eaf24d766