Skip to content

Commit

Permalink
verifier: Move registry to authenticator
Browse files Browse the repository at this point in the history
Since auth is related to the registry itself, this moves the registry
reference to the container authenticator struct instead.

Signed-off-by: Juan Antonio Osorio <ozz@stacklok.com>
  • Loading branch information
JAORMX committed May 13, 2024
1 parent 16c3e60 commit 032a560
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 42 deletions.
2 changes: 1 addition & 1 deletion cmd/dev/app/image/cmd_verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func runCmdVerify(cmd *cobra.Command, _ []string) error {
return fmt.Errorf("error getting sigstore verifier: %w", err)
}

res, err := artifactVerifier.Verify(context.Background(), verifyif.ArtifactTypeContainer, "",
res, err := artifactVerifier.Verify(context.Background(), verifyif.ArtifactTypeContainer,
owner.Value.String(), name.Value.String(), digest.Value.String())
if err != nil {
return fmt.Errorf("error verifying container: %w", err)
Expand Down
2 changes: 1 addition & 1 deletion internal/engine/ingester/artifact/artifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func (i *Ingest) getVerificationResult(
// Loop through all artifact versions that apply to this rule and get the provenance info for each
for _, artifactVersion := range versions {
// Try getting provenance info for the artifact version
results, err := artifactVerifier.Verify(ctx, verifyif.ArtifactTypeContainer, "",
results, err := artifactVerifier.Verify(ctx, verifyif.ArtifactTypeContainer,
artifact.Owner, artifact.Name, artifactVersion)
if err != nil {
// We consider err != nil as a fatal error, so we'll fail the rule evaluation here
Expand Down
12 changes: 6 additions & 6 deletions internal/engine/ingester/artifact/artifact_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func TestArtifactIngestMatching(t *testing.T) {
},
}, nil)
mockVerifier.EXPECT().
Verify(gomock.Any(), verifyif.ArtifactTypeContainer, verifyif.ArtifactRegistry(""), "stacklok", "matching-name", "sha256:1234").
Verify(gomock.Any(), verifyif.ArtifactTypeContainer, "stacklok", "matching-name", "sha256:1234").
Return([]verifyif.Result{
{
IsSigned: false,
Expand Down Expand Up @@ -138,7 +138,7 @@ func TestArtifactIngestMatching(t *testing.T) {
}, nil)

mockVerifier.EXPECT().
Verify(gomock.Any(), verifyif.ArtifactTypeContainer, verifyif.ArtifactRegistry(""), "stacklok", "matching-name-and-tag", "sha256:1234").
Verify(gomock.Any(), verifyif.ArtifactTypeContainer, "stacklok", "matching-name-and-tag", "sha256:1234").
Return([]verifyif.Result{
{
IsSigned: false,
Expand Down Expand Up @@ -299,7 +299,7 @@ func TestArtifactIngestMatching(t *testing.T) {
}, nil)

mockVerifier.EXPECT().
Verify(gomock.Any(), verifyif.ArtifactTypeContainer, verifyif.ArtifactRegistry(""), "stacklok", "matching-name-but-not-tags", "sha256:1234").
Verify(gomock.Any(), verifyif.ArtifactTypeContainer, "stacklok", "matching-name-but-not-tags", "sha256:1234").
Return([]verifyif.Result{
{
IsSigned: false,
Expand Down Expand Up @@ -353,7 +353,7 @@ func TestArtifactIngestMatching(t *testing.T) {
}, nil)

mockVerifier.EXPECT().
Verify(gomock.Any(), verifyif.ArtifactTypeContainer, verifyif.ArtifactRegistry(""), "stacklok", "matching-name", "sha256:1234").
Verify(gomock.Any(), verifyif.ArtifactTypeContainer, "stacklok", "matching-name", "sha256:1234").
Return([]verifyif.Result{
{
IsSigned: false,
Expand Down Expand Up @@ -390,7 +390,7 @@ func TestArtifactIngestMatching(t *testing.T) {
}, nil)

mockVerifier.EXPECT().
Verify(gomock.Any(), verifyif.ArtifactTypeContainer, verifyif.ArtifactRegistry(""), "stacklok", "matching-name", "sha256:1234").
Verify(gomock.Any(), verifyif.ArtifactTypeContainer, "stacklok", "matching-name", "sha256:1234").
Return([]verifyif.Result{
{
IsSigned: false,
Expand Down Expand Up @@ -428,7 +428,7 @@ func TestArtifactIngestMatching(t *testing.T) {
}, nil)

mockVerifier.EXPECT().
Verify(gomock.Any(), verifyif.ArtifactTypeContainer, verifyif.ArtifactRegistry(""), "stacklok", "matching-name", "sha256:1234").
Verify(gomock.Any(), verifyif.ArtifactTypeContainer, "stacklok", "matching-name", "sha256:1234").
Return([]verifyif.Result{
{
IsSigned: false,
Expand Down
42 changes: 36 additions & 6 deletions internal/verifier/sigstore/container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,28 @@ type AuthMethod func(auth *containerAuth)

// containerAuth is the authentication for the container
type containerAuth struct {
// Used if GH client is available
ghClient provifv1.GitHub
// Used if GH client is not available (any other provider)
concreteAuthn authn.Authenticator
// Registry to use
registry string
}

func (c *containerAuth) getAuthenticator(owner string) authn.Authenticator {
if c.ghClient != nil {
return c.ghClient.GetCredential().GetAsContainerAuthenticator(owner)
}
if c.concreteAuthn != nil {
return c.concreteAuthn
}
return authn.Anonymous
}

func newContainerAuth(authOpts ...AuthMethod) *containerAuth {
var auth containerAuth
auth := containerAuth{
registry: "ghcr.io",
}
for _, opt := range authOpts {
opt(&auth)
}
Expand All @@ -85,23 +95,43 @@ func WithGitHubClient(ghClient provifv1.GitHub) AuthMethod {
}
}

// WithAuthenticator sets the authenticator as an authentication option we want to use during verification
func WithAuthenticator(auth authn.Authenticator) AuthMethod {
return func(cauth *containerAuth) {
cauth.concreteAuthn = auth
}
}

// WithRegistry sets the registry as an authentication option we want to use during verification
func WithRegistry(registry string) AuthMethod {
return func(cauth *containerAuth) {
cauth.registry = registry
}
}

func (c *containerAuth) getRegistry() string {
return c.registry
}

// Verify verifies a container artifact using sigstore
// isSigned is true only if we were able to find a signature/attestation and it had everything needed to construct the
// sigstore bundle.
// isVerified is true only if we were able to verify the constructed bundle against the configured sigstore instance.
func Verify(
ctx context.Context,
sev *verify.SignedEntityVerifier,
registry, owner, artifact, version string,
owner, artifact, version string,
authOpts ...AuthMethod,
) ([]verifyif.Result, error) {
logger := zerolog.Ctx(ctx)

cauth := newContainerAuth(authOpts...)

logger.Info().
Str("imageRef", BuildImageRef(registry, owner, artifact, version)).
Str("imageRef", BuildImageRef(cauth.getRegistry(), owner, artifact, version)).
Msg("verifying container artifact")
// Construct the bundle(s) - OCI image or GitHub's attestation endpoint
bundles, err := getSigstoreBundles(ctx, registry, owner, artifact, version, newContainerAuth(authOpts...))
bundles, err := getSigstoreBundles(ctx, owner, artifact, version, cauth)
if err != nil && !errors.Is(err, ErrProvenanceNotFoundOrIncomplete) {
// We got some other unexpected error prior to querying for the signature/attestation
return nil, err
Expand Down Expand Up @@ -168,10 +198,10 @@ func getVerifiedResults(
// getSigstoreBundles returns the sigstore bundles, either through the OCI registry or the GitHub attestation endpoint
func getSigstoreBundles(
ctx context.Context,
registry, owner, artifact, version string,
owner, artifact, version string,
auth *containerAuth,
) ([]sigstoreBundle, error) {
imageRef := BuildImageRef(registry, owner, artifact, version)
imageRef := BuildImageRef(auth.getRegistry(), owner, artifact, version)
// Try to build a bundle from the OCI image reference
bundles, err := bundleFromOCIImage(ctx, imageRef, auth.getAuthenticator(owner))
if errors.Is(err, ErrProvenanceNotFoundOrIncomplete) && auth.ghClient != nil {
Expand Down
16 changes: 6 additions & 10 deletions internal/verifier/sigstore/sigstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,17 +147,17 @@ func verifierOptions(trustedRoot string) ([]verify.VerifierOption, error) {
}

// Verify verifies an artifact
func (s *Sigstore) Verify(ctx context.Context, artifactType verifyif.ArtifactType, registry verifyif.ArtifactRegistry,
func (s *Sigstore) Verify(ctx context.Context, artifactType verifyif.ArtifactType,
owner, artifact, version string) ([]verifyif.Result, error) {
var err error
var res []verifyif.Result
// Sanitize the input
sanitizeInput(&registry, &owner)
sanitizeInput(&owner)

// Process verification based on the artifact type
switch artifactType {
case verifyif.ArtifactTypeContainer:
res, err = s.VerifyContainer(ctx, string(registry), owner, artifact, version)
res, err = s.VerifyContainer(ctx, owner, artifact, version)
default:
err = fmt.Errorf("unknown artifact type: %s", artifactType)
}
Expand All @@ -166,17 +166,13 @@ func (s *Sigstore) Verify(ctx context.Context, artifactType verifyif.ArtifactTyp
}

// VerifyContainer verifies a container artifact using sigstore
func (s *Sigstore) VerifyContainer(ctx context.Context, registry, owner, artifact, version string) (
func (s *Sigstore) VerifyContainer(ctx context.Context, owner, artifact, version string) (
[]verifyif.Result, error) {
return container.Verify(ctx, s.verifier, registry, owner, artifact, version, s.authOpts...)
return container.Verify(ctx, s.verifier, owner, artifact, version, s.authOpts...)
}

// sanitizeInput sanitizes the input parameters
func sanitizeInput(registry *verifyif.ArtifactRegistry, owner *string) {
// Default the registry to GHCR for the time being
if *registry == "" {
*registry = verifyif.ArtifactRegistryGHCR
}
func sanitizeInput(owner *string) {
// (jaosorior): The owner can't be upper-cased, normalize the owner.
*owner = strings.ToLower(*owner)
}
Expand Down
16 changes: 8 additions & 8 deletions internal/verifier/verifyif/mock/verifyif.go

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

12 changes: 2 additions & 10 deletions internal/verifier/verifyif/verifyif.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,6 @@ import (

//go:generate go run go.uber.org/mock/mockgen -package mock_$GOPACKAGE -destination=./mock/$GOFILE -source=./$GOFILE

// ArtifactRegistry supported artifact registries
type ArtifactRegistry string

const (
// ArtifactRegistryGHCR is the GitHub Container Registry
ArtifactRegistryGHCR ArtifactRegistry = "ghcr.io"
)

// ArtifactType represents the type of artifact, i.e., container, npm, etc.
type ArtifactType string

Expand All @@ -49,8 +41,8 @@ type Result struct {

// ArtifactVerifier is the interface for artifact verifiers
type ArtifactVerifier interface {
Verify(ctx context.Context, artifactType ArtifactType, registry ArtifactRegistry,
Verify(ctx context.Context, artifactType ArtifactType,
owner, name, version string) ([]Result, error)
VerifyContainer(ctx context.Context,
registry, owner, artifact, version string) ([]Result, error)
owner, artifact, version string) ([]Result, error)
}

0 comments on commit 032a560

Please sign in to comment.