Files
sshsignhost/README.md
T
unkin-agent fde942d831
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/integration Pipeline was canceled
Drive releases with goreleaser and test against a real Vault
Replace the nfpm/build-rpm.sh release path with a single goreleaser
config producing the RPM, the raw sshsignhost-<os>-<arch> binaries the
puppetserver-compiler initContainer fetches, tarballs and checksums; the
tag pipeline runs goreleaser then PUTs the RPM to artifactapi.

Add an integration suite behind the 'integration' build tag that
provisions an ssh CA mount, host role and approle on a dev-mode Vault
and signs a host key through the built binary with Puppet's exact argv,
asserting an empty stderr and a decoded host certificate.
2026-09-19 12:13:39 +10:00

2.4 KiB

sshsignhost

Vault-backed SSH host certificate signing helper for Puppet.

Puppet's profiles::ssh::sign class calls the binary server-side during catalog compilation via generate(), then feeds its stdout to parsejson():

/usr/local/bin/sshsignhost --valid_principals "host,fqdn,ip" --ttl 87600h \
  --public_key "ssh-rsa AAAA..." --json

generate() runs with combine: true, so stderr is spliced into stdout. On success the process writes only the JSON document to stdout and nothing at all to stderr; any failure prints a message naming the cause to stderr and exits non-zero, which aborts the compile cleanly instead of yielding a parse error.

Usage

Flag Default Required
--public_key — yes
--valid_principals (comma-separated, forwarded to Vault verbatim) — yes
--ttl 87600h no
--json false no
--config /opt/sshsignhost/config.yaml no

With --json, stdout is exactly:

{"signed_key": "ssh-rsa-cert-v01@openssh.com AAAA..."}

Without it, the raw signed_key value plus a newline.

Configuration

vault:
  addr: 'https://vault.service.consul:8200'
  auth_method: 'approle'        # or 'kubernetes'
  mount_point: 'sshca'
  role_name: 'signhost'
  # auth_method: approle
  role_id: '...'
  approle_path: 'approle'
  # auth_method: kubernetes
  k8s_mount: 'k8s/au/syd1'
  k8s_role: 'puppet_sshsigner'
  jwt_path: '/var/run/secrets/kubernetes.io/serviceaccount/token'
  # optional
  tls_skip_verify: true         # default true
  timeout: '15s'                # default 15s

tls_skip_verify defaults to true to match the Python helper this replaces (verify=False); flip it to false once Vault's listeners present trusted certificates. timeout bounds every Vault call so a black-holed Vault cannot hang a catalog compile forever.

Build

make build             # -> dist/sshsignhost
make test
make integration-test  # signs a host key against a dev-mode Vault in a container
make snapshot          # goreleaser: binaries, RPM and archives into dist/

Releases are driven entirely by goreleaser on a v* tag: the RPM (/usr/local/bin/sshsignhost plus bash/zsh/fish completions) is pushed to the artifactapi rpm-internal yum repo, and the raw sshsignhost-<os>-<arch> binaries, tarballs and checksums.txt are attached to the Gitea release for the puppetserver-compiler initContainer to fetch.