Files
certmanager/internal/pki/pki.go
T
unkin-agent b9633481a5
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Make certmanager a drop-in for the Python script
Puppet calls /usr/local/bin/certmanager from generate() with combine: true, so
the CLI must take the common name positionally and keep stderr clean or the
catalog compile dies on a parsejson error.

- Flatten the CLI: drop the pki subcommand, add -a/-i/-e/-c/--json
- Emit ca_certificate so vault.pp stops writing undef to that file
- Read output_path from under vault:, where Puppet writes it
- Bound every Vault request with a timeout; exit non-zero on failure
- Gate TLS verification on vault.tls_skip_verify (default true)
- Restrict auth to approle and kubernetes, no fallback between them
- Remove the SSH half; add Woodpecker CI, nfpm RPM and completions
2026-09-19 10:34:01 +10:00

58 lines
1.7 KiB
Go

package pki
import (
"fmt"
"strings"
"git.unkin.net/unkin/certmanager/internal/vault"
)
// CertificateResponse is the JSON document printed with --json and consumed by
// parsejson() in profiles::pki::vault. Field order fixes the key order.
type CertificateResponse struct {
Certificate string `json:"certificate"`
PrivateKey string `json:"private_key"`
FullChain string `json:"full_chain"`
CACertificate string `json:"ca_certificate"`
}
type issueRequest struct {
CommonName string `json:"common_name"`
AltNames string `json:"alt_names"`
IPSans string `json:"ip_sans"`
TTL string `json:"ttl"`
}
type issueResponse struct {
Data struct {
Certificate string `json:"certificate"`
PrivateKey string `json:"private_key"`
IssuingCA string `json:"issuing_ca"`
} `json:"data"`
}
// IssueCert requests a certificate from the Vault PKI role.
func IssueCert(client *vault.Client, mountPoint, roleName, commonName string, altNames, ipSans []string, expiryDays int) (*CertificateResponse, error) {
req := issueRequest{
CommonName: commonName,
AltNames: strings.Join(altNames, ","),
IPSans: strings.Join(ipSans, ","),
TTL: fmt.Sprintf("%dd", expiryDays),
}
var resp issueResponse
if err := client.Post(fmt.Sprintf("%s/issue/%s", mountPoint, roleName), req, &resp); err != nil {
return nil, err
}
if resp.Data.Certificate == "" || resp.Data.PrivateKey == "" {
return nil, fmt.Errorf("vault returned no certificate data for %q", commonName)
}
return &CertificateResponse{
Certificate: resp.Data.Certificate,
PrivateKey: resp.Data.PrivateKey,
FullChain: resp.Data.IssuingCA + "\n" + resp.Data.Certificate,
CACertificate: resp.Data.IssuingCA,
}, nil
}