certificates/pki/helm_test.go

222 lines
6.7 KiB
Go
Raw Normal View History

package pki
import (
"bytes"
"crypto/x509"
2022-10-13 22:09:32 +00:00
"encoding/json"
"os"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
2022-10-13 22:09:32 +00:00
"go.step.sm/crypto/jose"
"go.step.sm/linkedca"
"github.com/smallstep/certificates/cas/apiv1"
)
func TestPKI_WriteHelmTemplate(t *testing.T) {
type fields struct {
casOptions apiv1.Options
pkiOptions []Option
}
tests := []struct {
name string
fields fields
testFile string
wantErr bool
}{
{
name: "ok/simple",
fields: fields{
pkiOptions: []Option{
WithHelm(),
},
casOptions: apiv1.Options{
Type: "softcas",
IsCreator: true,
},
},
testFile: "testdata/helm/simple.yml",
wantErr: false,
},
2022-10-13 22:09:32 +00:00
{
name: "ok/with-provisioner",
fields: fields{
pkiOptions: []Option{
WithHelm(),
WithProvisioner("a-provisioner"),
},
casOptions: apiv1.Options{
Type: "softcas",
IsCreator: true,
},
},
testFile: "testdata/helm/with-provisioner.yml",
wantErr: false,
},
{
name: "ok/with-acme",
fields: fields{
pkiOptions: []Option{
WithHelm(),
WithACME(),
},
casOptions: apiv1.Options{
Type: "softcas",
IsCreator: true,
},
},
testFile: "testdata/helm/with-acme.yml",
wantErr: false,
},
{
name: "ok/with-admin",
fields: fields{
pkiOptions: []Option{
WithHelm(),
WithAdmin(),
},
casOptions: apiv1.Options{
Type: "softcas",
IsCreator: true,
},
},
testFile: "testdata/helm/with-admin.yml",
wantErr: false,
},
{
name: "ok/with-ssh",
fields: fields{
pkiOptions: []Option{
WithHelm(),
WithSSH(),
},
casOptions: apiv1.Options{
Type: "softcas",
IsCreator: true,
},
},
testFile: "testdata/helm/with-ssh.yml",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o := tt.fields.casOptions
opts := tt.fields.pkiOptions
// TODO(hs): invoking `New` doesn't perform all operations that are executed
// when `ca init --helm` is executed. The list of provisioners on the authority
// is not populated, for example, resulting in this test not being entirely
// realistic. Ideally this logic should be handled in one place and probably
// inside of the PKI initialization, but if that becomes messy, some more
// logic needs to be performed here to get the PKI instance in good shape.
p, err := New(o, opts...)
assert.NoError(t, err)
2022-10-13 22:09:32 +00:00
// setKeyPair sets a predefined JWK and a default JWK provisioner. This is one
2022-10-13 22:09:32 +00:00
// of the things performed in the `ca init` code that's not part of `New`, but
// performed after that in p.GenerateKeyPairs`. We're currently using the same
// JWK for every test to keep test variance small: we're not testing JWK generation
// here after all. It's a bit dangerous to redefine the function here, but it's
// the simplest way to make this fully testable without refactoring the init now.
// The password for the predefined encrypted key is \x01\x03\x03\x07.
setKeyPair(t, p)
// setFiles sets some static intermediate and root CA certificate bytes. It
// replaces the logic executed in `p.GenerateRootCertificate`, `p.WriteRootCertificate`,
// and `p.GenerateIntermediateCertificate`.
setFiles(t, p)
2022-10-13 22:09:32 +00:00
// setSSHSigningKeys sets predefined SSH user and host certificate and key bytes.
// This replaces the logic in `p.GenerateSSHSigningKeys`
setSSHSigningKeys(t, p)
w := &bytes.Buffer{}
if err := p.WriteHelmTemplate(w); (err != nil) != tt.wantErr {
t.Errorf("PKI.WriteHelmTemplate() error = %v, wantErr %v", err, tt.wantErr)
return
}
2022-10-13 22:09:32 +00:00
wantBytes, err := os.ReadFile(tt.testFile)
assert.NoError(t, err)
if diff := cmp.Diff(wantBytes, w.Bytes()); diff != "" {
t.Logf("Generated Helm template did not match reference %q\n", tt.testFile)
t.Errorf("Diff follows:\n%s\n", diff)
t.Errorf("Full output:\n%s\n", w.Bytes())
}
})
}
}
2022-10-13 22:09:32 +00:00
// setKeyPair sets a predefined JWK and a default JWK provisioner.
func setKeyPair(t *testing.T, p *PKI) {
2022-10-13 22:09:32 +00:00
t.Helper()
var err error
p.ottPublicKey, err = jose.ParseKey([]byte(`{"use":"sig","kty":"EC","kid":"zsUmysmDVoGJ71YoPHyZ-68tNihDaDaO5Mu7xX3M-_I","crv":"P-256","alg":"ES256","x":"Pqnua4CzqKz6ua41J3yeWZ1sRkGt0UlCkbHv8H2DGuY","y":"UhoZ_2ItDen9KQTcjay-ph-SBXH0mwqhHyvrrqIFDOI"}`))
if err != nil {
t.Fatal(err)
}
p.ottPrivateKey, err = jose.ParseEncrypted("eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiZjVvdGVRS2hvOXl4MmQtSGlMZi05QSJ9.eYA6tt3fNuUpoxKWDT7P0Lbn2juxhEbTxEnwEMbjlYLLQ3sxL-dYTA.ven-FhmdjlC9itH0.a2jRTarN9vPd6F_mWnNBlOn6KbfMjCApmci2t65XbAsLzYFzhI_79Ykm5ueMYTupWLTjBJctl-g51ZHmsSB55pStbpoyyLNAsUX2E1fTmHe-Ni8bRrspwLv15FoN1Xo1g0mpR-ufWIFxOsW-QIfnMmMIIkygVuHFXmg2tFpzTNNG5aS29K3dN2nyk0WJrdIq79hZSTqVkkBU25Yu3A46sgjcM86XcIJJ2XUEih_KWEa6T1YrkixGu96pebjVqbO0R6dbDckfPF7FqNnwPHVtb1ACFpEYoOJVIbUCMaARBpWsxYhjJZlEM__XA46l8snFQDkNY3CdN0p1_gF3ckA.JLmq9nmu1h9oUi1S8ZxYjA")
if err != nil {
t.Fatal(err)
}
var claims *linkedca.Claims
if p.options.enableSSH {
claims = &linkedca.Claims{
Ssh: &linkedca.SSHClaims{
Enabled: true,
},
}
}
// Add JWK provisioner to the configuration.
publicKey, err := json.Marshal(p.ottPublicKey)
if err != nil {
t.Fatal(err)
}
encryptedKey, err := p.ottPrivateKey.CompactSerialize()
if err != nil {
t.Fatal(err)
}
p.Authority.Provisioners = append(p.Authority.Provisioners, &linkedca.Provisioner{
Type: linkedca.Provisioner_JWK,
Name: p.options.provisioner,
Claims: claims,
Details: &linkedca.ProvisionerDetails{
Data: &linkedca.ProvisionerDetails_JWK{
JWK: &linkedca.JWKProvisioner{
PublicKey: publicKey,
EncryptedPrivateKey: []byte(encryptedKey),
},
},
},
})
}
// setFiles sets some static, gibberish intermediate and root CA certificate bytes.
func setFiles(t *testing.T, p *PKI) {
p.Files[p.Root[0]] = encodeCertificate(&x509.Certificate{Raw: []byte("these are just some fake root CA cert bytes")})
p.Files[p.RootKey[0]] = []byte("these are just some fake root CA key bytes")
p.Files[p.Intermediate] = encodeCertificate(&x509.Certificate{Raw: []byte("these are just some fake intermediate CA cert bytes")})
p.Files[p.IntermediateKey] = []byte("these are just some fake intermediate CA key bytes")
}
// setSSHSigningKeys sets some static, gibberish ssh user and host CA certificate and key bytes.
func setSSHSigningKeys(t *testing.T, p *PKI) {
if !p.options.enableSSH {
return
}
p.Files[p.Ssh.HostKey] = []byte("fake ssh host key bytes")
p.Files[p.Ssh.HostPublicKey] = []byte("fake ssh host cert bytes")
p.Files[p.Ssh.UserKey] = []byte("fake ssh user key bytes")
p.Files[p.Ssh.UserPublicKey] = []byte("fake ssh user cert bytes")
}