mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-27 00:19:36 -05:00
a5021c52d3
* add ASK caching in joinservice Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * use cached ASK in Azure SEV-SNP attestation Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * update test charts Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * fix linter Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * fix typ Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * make caching mechanism less provider-specific Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * update buildfiles Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * add `omitempty` flag Co-authored-by: Daniel Weiße <66256922+daniel-weisse@users.noreply.github.com> * frontload certificate getter Co-authored-by: Daniel Weiße <66256922+daniel-weisse@users.noreply.github.com> * rename frontloaded function Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * pass cached certificates to constructor Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * fix race condition Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * fix marshalling of empty certs Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * fix validator usage Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * [wip] add certcache tests Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * add certcache tests Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * tidy Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * fix validator test Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * remove unused fields in validator Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * fix certificate precedence Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * use separate context Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * tidy Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * linter fixes Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * linter fixes Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * Remove unnecessary comment Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com> * use background context Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * Use error format directive Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com> * `azure` -> `Azure` Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com> * improve error messages Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * add x509 -> PEM util function Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * use crypto util functions Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * fix certificate replacement logic Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * only require ASK from certcache Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * tidy Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> * fix comment typo Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> --------- Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com> Co-authored-by: Daniel Weiße <66256922+daniel-weisse@users.noreply.github.com> Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com>
248 lines
6.0 KiB
Go
248 lines
6.0 KiB
Go
/*
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
package certcache
|
|
|
|
import (
|
|
"context"
|
|
"crypto/x509"
|
|
"testing"
|
|
|
|
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
|
"github.com/edgelesssys/constellation/v2/internal/crypto"
|
|
"github.com/edgelesssys/constellation/v2/internal/logger"
|
|
"github.com/edgelesssys/constellation/v2/joinservice/internal/certcache/testdata"
|
|
"github.com/google/go-sev-guest/abi"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
)
|
|
|
|
func TestCreateCertChainCache(t *testing.T) {
|
|
notFoundErr := k8serrors.NewNotFound(schema.GroupResource{}, "test")
|
|
|
|
testCases := map[string]struct {
|
|
kubeClient *stubKubeClient
|
|
kdsClient *stubKdsClient
|
|
expectedArk *x509.Certificate
|
|
expectedAsk *x509.Certificate
|
|
wantErr bool
|
|
}{
|
|
"available in configmap": {
|
|
kubeClient: &stubKubeClient{
|
|
askResponse: string(testdata.Ask),
|
|
arkResponse: string(testdata.Ark),
|
|
},
|
|
kdsClient: &stubKdsClient{},
|
|
expectedArk: mustParsePEM(testdata.Ark),
|
|
expectedAsk: mustParsePEM(testdata.Ask),
|
|
},
|
|
"query from kds": {
|
|
kubeClient: &stubKubeClient{
|
|
getConfigMapDataErr: notFoundErr,
|
|
},
|
|
kdsClient: &stubKdsClient{
|
|
askResponse: testdata.Ask,
|
|
arkResponse: testdata.Ark,
|
|
},
|
|
expectedArk: mustParsePEM(testdata.Ark),
|
|
expectedAsk: mustParsePEM(testdata.Ask),
|
|
},
|
|
"only replace uncached cert": {
|
|
kubeClient: &stubKubeClient{
|
|
askResponse: string(testdata.Ark), // on purpose
|
|
},
|
|
kdsClient: &stubKdsClient{
|
|
arkResponse: testdata.Ark,
|
|
askResponse: testdata.Ask,
|
|
},
|
|
expectedArk: mustParsePEM(testdata.Ark),
|
|
expectedAsk: mustParsePEM(testdata.Ark), // on purpose
|
|
},
|
|
"only ask available in configmap": {
|
|
kubeClient: &stubKubeClient{
|
|
askResponse: string(testdata.Ask),
|
|
},
|
|
kdsClient: &stubKdsClient{
|
|
arkResponse: testdata.Ark,
|
|
},
|
|
expectedArk: mustParsePEM(testdata.Ark),
|
|
expectedAsk: mustParsePEM(testdata.Ask),
|
|
},
|
|
"only ark available in configmap": {
|
|
kubeClient: &stubKubeClient{
|
|
arkResponse: string(testdata.Ark),
|
|
},
|
|
kdsClient: &stubKdsClient{
|
|
askResponse: testdata.Ask,
|
|
},
|
|
expectedArk: mustParsePEM(testdata.Ark),
|
|
expectedAsk: mustParsePEM(testdata.Ask),
|
|
},
|
|
"get config map data err": {
|
|
kubeClient: &stubKubeClient{
|
|
getConfigMapDataErr: assert.AnError,
|
|
},
|
|
wantErr: true,
|
|
},
|
|
"update configmap err": {
|
|
kubeClient: &stubKubeClient{
|
|
askResponse: string(testdata.Ask),
|
|
updateConfigMapErr: assert.AnError,
|
|
},
|
|
kdsClient: &stubKdsClient{
|
|
arkResponse: testdata.Ark,
|
|
},
|
|
wantErr: true,
|
|
},
|
|
"kds cert chain err": {
|
|
kubeClient: &stubKubeClient{
|
|
getConfigMapDataErr: notFoundErr,
|
|
},
|
|
kdsClient: &stubKdsClient{
|
|
certChainErr: assert.AnError,
|
|
},
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for name, tc := range testCases {
|
|
t.Run(name, func(t *testing.T) {
|
|
assert := assert.New(t)
|
|
require := require.New(t)
|
|
|
|
ctx := context.Background()
|
|
|
|
c := &Client{
|
|
attVariant: variant.Dummy{},
|
|
log: logger.NewTest(t),
|
|
kubeClient: tc.kubeClient,
|
|
kdsClient: tc.kdsClient,
|
|
}
|
|
|
|
ask, ark, err := c.createCertChainCache(ctx, abi.NoneReportSigner)
|
|
if tc.wantErr {
|
|
assert.Error(err)
|
|
} else {
|
|
require.NoError(err)
|
|
assert.Equal(tc.expectedArk, ark)
|
|
assert.Equal(tc.expectedAsk, ask)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type stubKdsClient struct {
|
|
askResponse []byte
|
|
arkResponse []byte
|
|
certChainErr error
|
|
}
|
|
|
|
func (c *stubKdsClient) CertChain(abi.ReportSigner) (ask, ark *x509.Certificate, err error) {
|
|
if c.askResponse != nil {
|
|
ask = mustParsePEM(c.askResponse)
|
|
}
|
|
if c.arkResponse != nil {
|
|
ark = mustParsePEM(c.arkResponse)
|
|
}
|
|
return ask, ark, c.certChainErr
|
|
}
|
|
|
|
func mustParsePEM(pemBytes []byte) *x509.Certificate {
|
|
cert, err := crypto.PemToX509Cert(pemBytes)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return cert
|
|
}
|
|
|
|
func TestGetCertChainCache(t *testing.T) {
|
|
testCases := map[string]struct {
|
|
kubeClient *stubKubeClient
|
|
expectedAsk *x509.Certificate
|
|
expectedArk *x509.Certificate
|
|
wantErr bool
|
|
}{
|
|
"success": {
|
|
kubeClient: &stubKubeClient{
|
|
askResponse: string(testdata.Ask),
|
|
arkResponse: string(testdata.Ark),
|
|
},
|
|
expectedAsk: mustParsePEM(testdata.Ask),
|
|
expectedArk: mustParsePEM(testdata.Ark),
|
|
},
|
|
"empty ask": {
|
|
kubeClient: &stubKubeClient{
|
|
askResponse: "",
|
|
arkResponse: string(testdata.Ark),
|
|
},
|
|
expectedAsk: nil,
|
|
expectedArk: mustParsePEM(testdata.Ark),
|
|
},
|
|
"empty ark": {
|
|
kubeClient: &stubKubeClient{
|
|
askResponse: string(testdata.Ask),
|
|
arkResponse: "",
|
|
},
|
|
expectedAsk: mustParsePEM(testdata.Ask),
|
|
expectedArk: nil,
|
|
},
|
|
"error getting config map data": {
|
|
kubeClient: &stubKubeClient{
|
|
getConfigMapDataErr: assert.AnError,
|
|
},
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for name, tc := range testCases {
|
|
t.Run(name, func(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
ctx := context.Background()
|
|
|
|
c := NewClient(logger.NewTest(t), tc.kubeClient, variant.Dummy{})
|
|
|
|
ask, ark, err := c.getCertChainCache(ctx)
|
|
if tc.wantErr {
|
|
assert.Error(err)
|
|
} else {
|
|
assert.NoError(err)
|
|
assert.Equal(tc.expectedArk, ark)
|
|
assert.Equal(tc.expectedAsk, ask)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type stubKubeClient struct {
|
|
askResponse string
|
|
arkResponse string
|
|
createConfigMapErr error
|
|
updateConfigMapErr error
|
|
getConfigMapDataErr error
|
|
}
|
|
|
|
func (s *stubKubeClient) CreateConfigMap(context.Context, string, map[string]string) error {
|
|
return s.createConfigMapErr
|
|
}
|
|
|
|
func (s *stubKubeClient) GetConfigMapData(_ context.Context, _ string, key string) (string, error) {
|
|
if key == constants.CertCacheAskKey {
|
|
return s.askResponse, s.getConfigMapDataErr
|
|
}
|
|
if key == constants.CertCacheArkKey {
|
|
return s.arkResponse, s.getConfigMapDataErr
|
|
}
|
|
return "", s.getConfigMapDataErr
|
|
}
|
|
|
|
func (s *stubKubeClient) UpdateConfigMap(context.Context, string, string, string) error {
|
|
return s.updateConfigMapErr
|
|
}
|