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

76 lines
2.4 KiB
Markdown

# 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:
```json
{"signed_key": "ssh-rsa-cert-v01@openssh.com AAAA..."}
```
Without it, the raw `signed_key` value plus a newline.
## Configuration
```yaml
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
```bash
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.