Skip to content

Commit

Permalink
Add test coverage for new builder signing feature.
Browse files Browse the repository at this point in the history
  • Loading branch information
stormqueen1990 committed Jun 12, 2023
1 parent 8374361 commit 4bcac5e
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 42 deletions.
5 changes: 5 additions & 0 deletions cmd/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import (
"strconv"
"time"

"github.com/pivotal/kpack/pkg/cosign"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/sign"
ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"

"github.com/Masterminds/semver/v3"
"go.uber.org/zap"
"golang.org/x/sync/errgroup"
Expand Down Expand Up @@ -200,6 +204,7 @@ func main() {
KpackVersion: cmd.Identifer,
LifecycleProvider: lifecycleProvider,
KeychainFactory: keychainFactory,
ImageSigner: cosign.NewImageSigner(log.New(os.Stdout, "", 0), sign.SignCmd, ociremote.SignatureTag),
}

buildController := build.NewController(ctx, options, k8sClient, buildInformer, podInformer, metadataRetriever, buildpodGenerator, keychainFactory, *injectedSidecarSupport)
Expand Down
27 changes: 16 additions & 11 deletions pkg/cnb/create_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@ package cnb

import (
"context"
"github.com/pivotal/kpack/pkg/cosign"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/sign"
ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
corev1 "k8s.io/api/core/v1"
"log"
"os"

"github.com/google/go-containerregistry/pkg/authn"
ggcrv1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/pivotal/kpack/pkg/cosign"
corev1 "k8s.io/api/core/v1"

buildapi "github.com/pivotal/kpack/pkg/apis/build/v1alpha2"
corev1alpha1 "github.com/pivotal/kpack/pkg/apis/core/v1alpha1"
Expand All @@ -31,11 +27,11 @@ type RemoteBuilderCreator struct {
LifecycleProvider LifecycleProvider
KpackVersion string
KeychainFactory registry.KeychainFactory
ImageSigner cosign.BuilderSigner
}

func (r *RemoteBuilderCreator) CreateBuilder(ctx context.Context, builderKeychain authn.Keychain, stackKeychain authn.Keychain, fetcher RemoteBuildpackFetcher, clusterStack *buildapi.ClusterStack, spec buildapi.BuilderSpec, signingSecrets []corev1.Secret) (buildapi.BuilderRecord, error) {
buildImage, _, err := r.RegistryClient.Fetch(stackKeychain, clusterStack.Status.BuildImage.LatestImage)
cosignSigner := cosign.NewImageSigner(log.New(os.Stdout, "", 0), sign.SignCmd, ociremote.SignatureTag)

if err != nil {
return buildapi.BuilderRecord{}, err
Expand Down Expand Up @@ -84,9 +80,18 @@ func (r *RemoteBuilderCreator) CreateBuilder(ctx context.Context, builderKeychai
return buildapi.BuilderRecord{}, err
}

signaturePaths, err := cosignSigner.SignBuilder(ctx, identifier, signingSecrets, builderKeychain)
if err != nil {
return buildapi.BuilderRecord{}, err
var (
signaturePaths = make([]buildapi.SignaturePath, 0)
signed = false
)

if len(signingSecrets) > 0 {
signaturePaths, err = r.ImageSigner.SignBuilder(ctx, identifier, signingSecrets, builderKeychain)
if err != nil {
return buildapi.BuilderRecord{}, err
}

signed = true
}

builder := buildapi.BuilderRecord{
Expand All @@ -100,7 +105,7 @@ func (r *RemoteBuilderCreator) CreateBuilder(ctx context.Context, builderKeychai
ObservedStackGeneration: clusterStack.Status.ObservedGeneration,
ObservedStoreGeneration: fetcher.ClusterStoreObservedGeneration(),
OS: config.OS,
Signed: err == nil,
Signed: signed,
SignaturePaths: signaturePaths,
}

Expand Down
67 changes: 67 additions & 0 deletions pkg/cnb/create_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) {
KpackVersion: "v1.2.3 (git sha: abcdefg123456)",
KeychainFactory: keychainFactory,
LifecycleProvider: lifecycleProvider,
ImageSigner: &fakeBuilderSigner{
signBuilder: func(ctx context.Context, s string, secrets []corev1.Secret, keychain authn.Keychain) ([]buildapi.SignaturePath, error) {
// no-op
return nil, nil
},
},
}

addBuildpack = func(t *testing.T, id, version, homepage, api string, stacks []corev1alpha1.BuildpackStack) {
Expand Down Expand Up @@ -862,9 +868,70 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) {
require.EqualError(t, err, "unsupported platform apis in kpack lifecycle: 0.1, 0.2, 0.999, expecting one of: 0.3, 0.4, 0.5, 0.6, 0.7, 0.8")
})
})

when("signing a builder image", func() {
it("does not return the signed flag set when no secrets were present", func() {
builderRecord, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterBuilderSpec, []corev1.Secret{})
require.NoError(t, err)
require.NotNil(t, builderRecord)

require.False(t, builderRecord.Signed)
})

it("returns an error if signing fails", func() {
subject.ImageSigner = &fakeBuilderSigner{
signBuilder: func(ctx context.Context, s string, secrets []corev1.Secret, keychain authn.Keychain) ([]buildapi.SignaturePath, error) {
return nil, fmt.Errorf("failed to sign builder")
},
}

fakeSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "cosign-creds",
Namespace: "test-namespace",
},
}

_, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterBuilderSpec, []corev1.Secret{fakeSecret})
require.Error(t, err)
})

it("sets the signed flag when signing succeeds", func() {
fakeSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "cosign-creds",
Namespace: "test-namespace",
},
}

subject.ImageSigner = &fakeBuilderSigner{
signBuilder: func(ctx context.Context, s string, secrets []corev1.Secret, keychain authn.Keychain) ([]buildapi.SignaturePath, error) {
return []buildapi.SignaturePath{
{
KeyName: fmt.Sprintf("k8s://%s/%s", fakeSecret.Namespace, fakeSecret.Name),
Path: "registry.local/test-image:signature-tag",
},
}, nil
},
}

builderRecord, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterBuilderSpec, []corev1.Secret{fakeSecret})
require.NoError(t, err)
require.NotNil(t, builderRecord)
require.True(t, builderRecord.Signed)
})
})
})
}

type fakeBuilderSigner struct {
signBuilder func(context.Context, string, []corev1.Secret, authn.Keychain) ([]buildapi.SignaturePath, error)
}

func (s *fakeBuilderSigner) SignBuilder(ctx context.Context, imageReference string, signingSecrets []corev1.Secret, builderKeychain authn.Keychain) ([]buildapi.SignaturePath, error) {
return s.signBuilder(ctx, imageReference, signingSecrets, builderKeychain)
}

type fakeLifecycleProvider struct {
metadata LifecycleMetadata
layers map[string]v1.Layer
Expand Down
17 changes: 11 additions & 6 deletions pkg/cosign/image_signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ package cosign
import (
"context"
"fmt"
"io/ioutil"
"log"
"os"
"time"

"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/pivotal/kpack/pkg/apis/build/v1alpha2"
cosignremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
"io/ioutil"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sclient "k8s.io/client-go/kubernetes"
"log"
"os"
"time"

"github.com/buildpacks/lifecycle/platform"
"github.com/pkg/errors"
Expand All @@ -25,6 +26,10 @@ type SignFunc func(*options.RootOptions, options.KeyOpts, options.SignOptions, [

type FetchSignatureFunc func(name.Reference, ...cosignremote.Option) (name.Tag, error)

type BuilderSigner interface {
SignBuilder(context.Context, string, []corev1.Secret, authn.Keychain) ([]v1alpha2.SignaturePath, error)
}

type ImageSigner struct {
Logger *log.Logger
signFunc SignFunc
Expand Down Expand Up @@ -153,13 +158,13 @@ func (s *ImageSigner) SignBuilder(
}

if cosignRepository, ok := secret.Annotations[RepositoryAnnotationPrefix]; ok {
if err := os.Setenv(cosignRepositoryEnv, fmt.Sprintf("%s", cosignRepository)); err != nil {
if err := os.Setenv(cosignRepositoryEnv, cosignRepository); err != nil {
return nil, fmt.Errorf("failed setting %s env variable: %w", cosignRepositoryEnv, err)
}
}

if cosignDockerMediaType, ok := secret.Annotations[DockerMediaTypesAnnotationPrefix]; ok {
if err := os.Setenv(cosignDockerMediaTypesEnv, fmt.Sprintf("%s", cosignDockerMediaType)); err != nil {
if err := os.Setenv(cosignDockerMediaTypesEnv, cosignDockerMediaType); err != nil {
return nil, fmt.Errorf("failed setting %s env variable: %w", cosignDockerMediaTypesEnv, err)
}
}
Expand Down
Loading

0 comments on commit 4bcac5e

Please sign in to comment.