Skip to content

Commit

Permalink
Provide unit tests covering signed images verification (#220)
Browse files Browse the repository at this point in the history
* [#91] Provide unit tests covering signed images verification

---------

Signed-off-by: Dimitar Dimitrov <[email protected]>
  • Loading branch information
dimitar-dimitrow authored Dec 15, 2023
1 parent e361ff5 commit 92bc5e5
Show file tree
Hide file tree
Showing 6 changed files with 344 additions and 39 deletions.
45 changes: 29 additions & 16 deletions containerm/ctr/ctr_client_opts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@
package ctr

import (
"github.com/eclipse-kanto/container-management/containerm/containers/types"
"github.com/eclipse-kanto/container-management/containerm/log"
"testing"
"time"

"github.com/eclipse-kanto/container-management/containerm/containers/types"
"github.com/eclipse-kanto/container-management/containerm/log"

"github.com/eclipse-kanto/container-management/containerm/pkg/testutil"
)

Expand All @@ -35,27 +36,30 @@ const (
)

var (
regConfig = &RegistryConfig{
testRegConfig = &RegistryConfig{
IsInsecure: false,
Credentials: &AuthCredentials{
UserID: testUser,
Password: testPass,
},
Transport: nil,
}
testVerifierConfig = map[string]string{"testKey": "testValue", "testAnotherKey": "testAnotherValue"}

testOpt = &ctrOpts{
namespace: testNamespace,
connectionPath: testConnectionPath,
registryConfigs: map[string]*RegistryConfig{testHost: regConfig},
rootExec: testRootExec,
metaPath: testMetaPath,
imageDecKeys: testDecKeys,
imageDecRecipients: testDecRecipients,
runcRuntime: types.RuntimeTypeV2runcV2,
imageExpiry: testImageExpiry,
imageExpiryDisable: testImageExpiryDisable,
leaseID: testLeaseID,
namespace: testNamespace,
connectionPath: testConnectionPath,
registryConfigs: map[string]*RegistryConfig{testHost: testRegConfig},
rootExec: testRootExec,
metaPath: testMetaPath,
imageDecKeys: testDecKeys,
imageDecRecipients: testDecRecipients,
runcRuntime: types.RuntimeTypeV2runcV2,
imageExpiry: testImageExpiry,
imageExpiryDisable: testImageExpiryDisable,
leaseID: testLeaseID,
imageVerifierType: VerifierNotation,
imageVerifierConfig: testVerifierConfig,
}
)

Expand All @@ -72,18 +76,27 @@ func TestCtrOpts(t *testing.T) {
expectedOpts: &ctrOpts{},
expectedErr: log.NewErrorf("unexpected runc runtime = unknown"),
},
"test_ctr_opts_unexpected_image_verifier_type_error": {
opts: []ContainerOpts{
WithCtrImageVerifierType("unknown"),
},
expectedOpts: &ctrOpts{},
expectedErr: log.NewErrorf("unexpected image verifier type = unknown"),
},
"test_ctr_opts_no_error": {
opts: []ContainerOpts{WithCtrdConnectionPath(testConnectionPath),
WithCtrdNamespace(testNamespace),
WithCtrdRootExec(testRootExec),
WithCtrdMetaPath(testMetaPath),
WithCtrdRegistryConfigs(map[string]*RegistryConfig{testHost: regConfig}),
WithCtrdRegistryConfigs(map[string]*RegistryConfig{testHost: testRegConfig}),
WithCtrdImageDecryptKeys(testDecKeys...),
WithCtrdImageDecryptRecipients(testDecRecipients...),
WithCtrdRuncRuntime(string(types.RuntimeTypeV2runcV2)),
WithCtrdImageExpiry(testImageExpiry),
WithCtrdImageExpiryDisable(testImageExpiryDisable),
WithCtrdLeaseID(testLeaseID)},
WithCtrdLeaseID(testLeaseID),
WithCtrImageVerifierType(string(VerifierNotation)),
WithCtrImageVerifierConfig(testVerifierConfig)},
expectedOpts: testOpt,
},
}
Expand Down
62 changes: 62 additions & 0 deletions containerm/ctr/ctr_verifier_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions containerm/ctr/ctr_verifier_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) 2023 Contributors to the Eclipse Foundation
//
// See the NOTICE file(s) distributed with this work for additional
// information regarding copyright ownership.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0

package ctr

import (
"context"
"testing"

"github.com/eclipse-kanto/container-management/containerm/containers/types"
"github.com/eclipse-kanto/container-management/containerm/pkg/testutil"
"github.com/notaryproject/notation-go/dir"
)

func TestContainerVerifier(t *testing.T) {
t.Run("unknown_verifier", func(t *testing.T) {
v, err := newContainerVerifier("unknown", nil, nil)
testutil.AssertNotNil(t, err)
testutil.AssertNil(t, v)
})
t.Run("skip_verifier", func(t *testing.T) {
v, err := newContainerVerifier(VerifierNone, nil, nil)
testutil.AssertNil(t, err)
testutil.AssertNotNil(t, v)
testutil.AssertNil(t, v.Verify(context.Background(), types.Image{}))
})
t.Run("notation_verifier", func(t *testing.T) {
config := map[string]string{
notationKeyConfigDir: "testConfigDir",
notationKeyLibexecDir: "testLibexecDir",
}
registryConfig := map[string]*RegistryConfig{
testHost: testRegConfig,
}

v, err := newContainerVerifier(VerifierNotation, config, registryConfig)
testutil.AssertNil(t, err)
testutil.AssertNotNil(t, v)
testutil.AssertNotNil(t, v.Verify(context.Background(), types.Image{})) // expected fail due to invalid config dir

nv := v.(*notationVerifier)
testutil.AssertEqual(t, registryConfig, nv.registryConfig)
testutil.AssertEqual(t, config[notationKeyConfigDir], dir.UserConfigDir)
testutil.AssertEqual(t, config[notationKeyLibexecDir], dir.UserLibexecDir)

})

}
40 changes: 28 additions & 12 deletions containerm/ctr/ctrd_client_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,17 +325,17 @@ func TestClientInternalPullImage(t *testing.T) {
DecryptConfig: &types.DecryptConfig{},
}
testCases := map[string]struct {
mockExec func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error)
mockExec func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error)
}{
"test_get_decrypt_cfg_error": {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) {
err := log.NewError("test error")
decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(nil, err)
return nil, err
},
},
"test_spi_get_image_error": {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) {
dc := &config.DecryptConfig{}
decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil)
err := log.NewError("test error")
Expand All @@ -344,7 +344,7 @@ func TestClientInternalPullImage(t *testing.T) {
},
},
"test_spi_get_image_available_check_auth_error": {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) {
dc := &config.DecryptConfig{}
decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil)
imageMock := mocksContainerd.NewMockImage(ctrl)
Expand All @@ -355,7 +355,7 @@ func TestClientInternalPullImage(t *testing.T) {
},
},
"test_spi_get_image_available_no_error": {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) {
dc := &config.DecryptConfig{}
decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil)
imageMock := mocksContainerd.NewMockImage(ctrl)
Expand All @@ -364,22 +364,34 @@ func TestClientInternalPullImage(t *testing.T) {
return imageMock, nil
},
},
"test_spi_get_image_not_available_verifier_error": {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) {
dc := &config.DecryptConfig{}
decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil)
spiMock.EXPECT().GetImage(gomock.Any(), testImageInfo.Name).Return(nil, errdefs.ErrNotFound)
err := log.NewError("test error")
verifierMock.EXPECT().Verify(gomock.Any(), testImageInfo).Return(err)
return nil, err
},
},
"test_spi_get_image_not_available_pull_error": {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) {
dc := &config.DecryptConfig{}
decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil)
spiMock.EXPECT().GetImage(gomock.Any(), testImageInfo.Name).Return(nil, errdefs.ErrNotFound)
verifierMock.EXPECT().Verify(gomock.Any(), testImageInfo).Return(nil)
regsResolverMock.EXPECT().ResolveImageRegistry(util.GetImageHost(testImageInfo.Name)).Return(nil)
err := log.NewError("test error")
spiMock.EXPECT().PullImage(gomock.Any(), testImageInfo.Name, matchers.MatchesResolverOpts(containerd.WithSchema1Conversion)).Return(nil, err)
return nil, err
},
},
"test_spi_get_image_not_available_check_auth_error": {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) {
dc := &config.DecryptConfig{}
decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil)
spiMock.EXPECT().GetImage(gomock.Any(), testImageInfo.Name).Return(nil, errdefs.ErrNotFound)
verifierMock.EXPECT().Verify(gomock.Any(), testImageInfo).Return(nil)
regsResolverMock.EXPECT().ResolveImageRegistry(util.GetImageHost(testImageInfo.Name)).Return(nil)
imageMock := mocksContainerd.NewMockImage(ctrl)
spiMock.EXPECT().PullImage(gomock.Any(), testImageInfo.Name, matchers.MatchesResolverOpts(containerd.WithSchema1Conversion)).Return(imageMock, nil)
Expand All @@ -389,10 +401,11 @@ func TestClientInternalPullImage(t *testing.T) {
},
},
"test_spi_get_image_not_available_gen_unpack_opts_error": {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) {
dc := &config.DecryptConfig{}
decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil)
spiMock.EXPECT().GetImage(gomock.Any(), testImageInfo.Name).Return(nil, errdefs.ErrNotFound)
verifierMock.EXPECT().Verify(gomock.Any(), testImageInfo).Return(nil)
regsResolverMock.EXPECT().ResolveImageRegistry(util.GetImageHost(testImageInfo.Name)).Return(nil)
imageMock := mocksContainerd.NewMockImage(ctrl)
spiMock.EXPECT().PullImage(gomock.Any(), testImageInfo.Name, matchers.MatchesResolverOpts(containerd.WithSchema1Conversion)).Return(imageMock, nil)
Expand All @@ -403,10 +416,11 @@ func TestClientInternalPullImage(t *testing.T) {
},
},
"test_spi_get_image_not_available_unpack_error": {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) {
dc := &config.DecryptConfig{}
decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil).Times(2)
spiMock.EXPECT().GetImage(gomock.Any(), testImageInfo.Name).Return(nil, errdefs.ErrNotFound)
verifierMock.EXPECT().Verify(gomock.Any(), testImageInfo).Return(nil)
regsResolverMock.EXPECT().ResolveImageRegistry(util.GetImageHost(testImageInfo.Name)).Return(nil)
imageMock := mocksContainerd.NewMockImage(ctrl)
spiMock.EXPECT().PullImage(gomock.Any(), testImageInfo.Name, matchers.MatchesResolverOpts(containerd.WithSchema1Conversion)).Return(imageMock, nil)
Expand All @@ -417,10 +431,11 @@ func TestClientInternalPullImage(t *testing.T) {
},
},
"test_spi_get_image_not_available_no_error": {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) {
mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) {
dc := &config.DecryptConfig{}
decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil).Times(2)
spiMock.EXPECT().GetImage(gomock.Any(), testImageInfo.Name).Return(nil, errdefs.ErrNotFound)
verifierMock.EXPECT().Verify(gomock.Any(), testImageInfo).Return(nil)
regsResolverMock.EXPECT().ResolveImageRegistry(util.GetImageHost(testImageInfo.Name)).Return(nil)
imageMock := mocksContainerd.NewMockImage(ctrl)
spiMock.EXPECT().PullImage(gomock.Any(), testImageInfo.Name, matchers.MatchesResolverOpts(containerd.WithSchema1Conversion)).Return(imageMock, nil)
Expand All @@ -439,13 +454,14 @@ func TestClientInternalPullImage(t *testing.T) {
decryptMgrMock := mocksCtrd.NewMockcontainerDecryptMgr(ctrl)
spiMock := mocksCtrd.NewMockcontainerdSpi(ctrl)
registriesResolverMock := mocksCtrd.NewMockcontainerImageRegistriesResolver(ctrl)
verifierMock := NewMockcontainerVerifier(ctrl)
ctrdClient := &containerdClient{
decMgr: decryptMgrMock,
spi: spiMock,
registriesResolver: registriesResolverMock,
verifier: &skipVerifier{},
verifier: verifierMock,
}
expectedImage, expectedErr := testCaseData.mockExec(decryptMgrMock, spiMock, registriesResolverMock, ctrl)
expectedImage, expectedErr := testCaseData.mockExec(decryptMgrMock, spiMock, registriesResolverMock, verifierMock, ctrl)
actualImage, actualErr := ctrdClient.pullImage(context.TODO(), testImageInfo)
testutil.AssertError(t, expectedErr, actualErr)
testutil.AssertEqual(t, expectedImage, actualImage)
Expand Down
Loading

0 comments on commit 92bc5e5

Please sign in to comment.