Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ jobs:
run: go test -tags=e2e,registry -v ./test/...
env:
COSIGN_TEST_REPO: insecure-registry.notlocal:5001
TUF_ROOT_JSON: ${{ github.workspace }}/root.json

- name: Setup local insecure OCI 1.1 registry
run: |
Expand Down Expand Up @@ -236,6 +237,23 @@ jobs:
env:
OCI11: yes
COSIGN_TEST_REPO: insecure-oci-registry.notlocal:5002
TUF_ROOT_JSON: ${{ github.workspace }}/root.json

- name: Set up local HTTP registry
run: |
docker run -d --restart=always \
--name $HTTP_REGISTRY_NAME \
-p $HTTP_REGISTRY_PORT:5000 registry:2.8.1
sudo echo "127.0.0.1 $HTTP_REGISTRY_NAME" | sudo tee -a /etc/hosts
env:
HTTP_REGISTRY_NAME: http-registry.notlocal
HTTP_REGISTRY_PORT: 5003

- name: Run HTTP registry tests
run: go test -tags=e2e,registry -v ./test/...
env:
COSIGN_TEST_REPO: http-registry.notlocal:5003
TUF_ROOT_JSON: ${{ github.workspace }}/root.json

- name: Collect diagnostics
if: ${{ failure() }}
Expand Down
4 changes: 4 additions & 0 deletions cmd/cosign/cli/attest/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"fmt"
"time"

"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"

"github.com/sigstore/cosign/v3/cmd/cosign/cli/options"
Expand Down Expand Up @@ -86,6 +87,9 @@ func (c *AttestCommand) Exec(ctx context.Context, imageRef string) error {
if err != nil {
return err
}
if c.RegistryOptions.AllowHTTPRegistry || c.RegistryOptions.AllowInsecure {
ociremoteOpts = append(ociremoteOpts, ociremote.WithNameOptions(name.Insecure))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: so our RegistryOptions.AllowHTTPRegistry maps to name.Insecure which both mean "it's okay if the registry uses plain HTTP without TLS.

But my interpretation of RegistryOptions.AllowInsecure is that the registry is using TLS, but with a self-signed certificate (or at least a certificate that doesn't verify with the machine's certificate store).

🤷 Are there people actually using that configuration? Or is everyone just using an HTTP registry? I guess we'll find out! I didn't see a "use TLS but allow certificates that don't verify with the machine's certificate store" on https://pkg.go.dev/github.com/google/[email protected]/pkg/name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it didn't seem like go-containerregistry makes a distinction between them.

}
digest, err := ociremote.ResolveDigest(ref, ociremoteOpts...)
if err != nil {
return err
Expand Down
3 changes: 3 additions & 0 deletions cmd/cosign/cli/sign/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ func signDigestBundle(ctx context.Context, digest name.Digest, ko options.KeyOpt
if err != nil {
return fmt.Errorf("constructing client options: %w", err)
}
if regOpts.AllowHTTPRegistry || regOpts.AllowInsecure {
ociremoteOpts = append(ociremoteOpts, ociremote.WithNameOptions(name.Insecure))
}

if ko.SigningConfig != nil {
return signcommon.WriteNewBundleWithSigningConfig(ctx, ko, signOpts.Cert, signOpts.CertChain, payload, digest, types.CosignSignPredicateType, "", ko.SigningConfig, ko.TrustedMaterial, ociremoteOpts...)
Expand Down
7 changes: 5 additions & 2 deletions cmd/cosign/cli/verify/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) {
if err != nil {
return fmt.Errorf("constructing client options: %w", err)
}
if c.AllowHTTPRegistry || c.AllowInsecure {
c.NameOptions = append(c.NameOptions, name.Insecure)
}

co := &cosign.CheckOpts{
Annotations: c.Annotations.Annotations,
Expand All @@ -134,7 +137,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) {
if !c.LocalImage {
ref, err := name.ParseReference(images[0], c.NameOptions...)
if err == nil && c.NewBundleFormat {
newBundles, _, err := cosign.GetBundles(ctx, ref, co)
newBundles, _, err := cosign.GetBundles(ctx, ref, co, c.NameOptions...)
if len(newBundles) == 0 || err != nil {
co.NewBundleFormat = false
}
Expand Down Expand Up @@ -209,7 +212,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) {

if co.NewBundleFormat {
// OCI bundle always contains attestation
verified, bundleVerified, err = cosign.VerifyImageAttestations(ctx, ref, co)
verified, bundleVerified, err = cosign.VerifyImageAttestations(ctx, ref, co, c.NameOptions...)
if err != nil {
return err
}
Expand Down
7 changes: 5 additions & 2 deletions cmd/cosign/cli/verify/verify_attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e
if err != nil {
return fmt.Errorf("constructing client options: %w", err)
}
if c.AllowHTTPRegistry || c.AllowInsecure {
c.NameOptions = append(c.NameOptions, name.Insecure)
}

co := &cosign.CheckOpts{
RegistryClientOpts: ociremoteOpts,
Expand All @@ -119,7 +122,7 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e
if !c.LocalImage {
ref, err := name.ParseReference(images[0], c.NameOptions...)
if err == nil && c.NewBundleFormat {
newBundles, _, err := cosign.GetBundles(ctx, ref, co)
newBundles, _, err := cosign.GetBundles(ctx, ref, co, c.NameOptions...)
if len(newBundles) == 0 || err != nil {
co.NewBundleFormat = false
}
Expand Down Expand Up @@ -182,7 +185,7 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e
return err
}

verified, bundleVerified, err = cosign.VerifyImageAttestations(ctx, ref, co)
verified, bundleVerified, err = cosign.VerifyImageAttestations(ctx, ref, co, c.NameOptions...)
if err != nil {
return err
}
Expand Down
12 changes: 6 additions & 6 deletions pkg/cosign/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -1013,13 +1013,13 @@ func loadSignatureFromFile(ctx context.Context, sigRef string, signedImgRef name

// VerifyImageAttestations does all the main cosign checks in a loop, returning the verified attestations.
// If there were no valid attestations, we return an error.
func VerifyImageAttestations(ctx context.Context, signedImgRef name.Reference, co *CheckOpts) (checkedAttestations []oci.Signature, bundleVerified bool, err error) {
func VerifyImageAttestations(ctx context.Context, signedImgRef name.Reference, co *CheckOpts, nameOpts ...name.Option) (checkedAttestations []oci.Signature, bundleVerified bool, err error) {
// Enforce this up front.
if co.RootCerts == nil && co.SigVerifier == nil && co.TrustedMaterial == nil {
return nil, false, errors.New("one of verifier, root certs, or TrustedMaterial is required")
}
if co.NewBundleFormat {
return verifyImageAttestationsSigstoreBundle(ctx, signedImgRef, co)
return verifyImageAttestationsSigstoreBundle(ctx, signedImgRef, co, nameOpts...)
}

// This is a carefully optimized sequence for fetching the attestations of
Expand Down Expand Up @@ -1621,7 +1621,7 @@ func verifyImageSignaturesExperimentalOCI(ctx context.Context, signedImgRef name
return verifySignatures(ctx, sigs, h, co)
}

func GetBundles(_ context.Context, signedImgRef name.Reference, co *CheckOpts) ([]*sgbundle.Bundle, *v1.Hash, error) {
func GetBundles(_ context.Context, signedImgRef name.Reference, co *CheckOpts, nameOpts ...name.Option) ([]*sgbundle.Bundle, *v1.Hash, error) {
// This is a carefully optimized sequence for fetching the signatures of the
// entity that minimizes registry requests when supplied with a digest input
digest, err := ociremote.ResolveDigest(signedImgRef, co.RegistryClientOpts...)
Expand All @@ -1644,7 +1644,7 @@ func GetBundles(_ context.Context, signedImgRef name.Reference, co *CheckOpts) (
}
var bundles = make([]*sgbundle.Bundle, 0, len(index.Manifests))
for _, result := range index.Manifests {
st, err := name.ParseReference(fmt.Sprintf("%s@%s", digest.Repository, result.Digest.String()))
st, err := name.ParseReference(fmt.Sprintf("%s@%s", digest.Repository, result.Digest.String()), nameOpts...)
if err != nil {
return nil, nil, err
}
Expand All @@ -1667,8 +1667,8 @@ func GetBundles(_ context.Context, signedImgRef name.Reference, co *CheckOpts) (
}

// verifyImageAttestationsSigstoreBundle verifies attestations from attached sigstore bundles
func verifyImageAttestationsSigstoreBundle(ctx context.Context, signedImgRef name.Reference, co *CheckOpts) (checkedAttestations []oci.Signature, atLeastOneBundleVerified bool, err error) {
bundles, hash, err := GetBundles(ctx, signedImgRef, co)
func verifyImageAttestationsSigstoreBundle(ctx context.Context, signedImgRef name.Reference, co *CheckOpts, nameOpts ...name.Option) (checkedAttestations []oci.Signature, atLeastOneBundleVerified bool, err error) {
bundles, hash, err := GetBundles(ctx, signedImgRef, co, nameOpts...)
if err != nil {
return nil, false, err
}
Expand Down
7 changes: 4 additions & 3 deletions pkg/oci/remote/write.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ func WriteReferrer(d name.Digest, artifactType string, layers []v1.Layer, annota
Annotations: annotations,
}, artifactType}

targetRef, err := manifest.targetRef(o.TargetRepository)
targetRef, err := manifest.targetRef(o.TargetRepository, opts...)
if err != nil {
return fmt.Errorf("failed to create target reference: %w", err)
}
Expand Down Expand Up @@ -370,7 +370,8 @@ func (r referrerManifest) RawManifest() ([]byte, error) {
return json.Marshal(r)
}

func (r referrerManifest) targetRef(repo name.Repository) (name.Reference, error) {
func (r referrerManifest) targetRef(repo name.Repository, opts ...Option) (name.Reference, error) {
o := makeOptions(repo, opts...)
manifestBytes, err := r.RawManifest()
if err != nil {
return nil, err
Expand All @@ -379,7 +380,7 @@ func (r referrerManifest) targetRef(repo name.Repository) (name.Reference, error
if err != nil {
return nil, err
}
return name.ParseReference(fmt.Sprintf("%s/%s@%s", repo.RegistryStr(), repo.RepositoryStr(), digest.String()))
return name.ParseReference(fmt.Sprintf("%s/%s@%s", repo.RegistryStr(), repo.RepositoryStr(), digest.String()), o.NameOpts...)
}

func (r referrerManifest) MediaType() (types.MediaType, error) {
Expand Down
114 changes: 109 additions & 5 deletions test/e2e_insecure_registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,19 @@ import (
"net/http"
"os"
"path"
"path/filepath"
"testing"
"time"

"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/random"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/sigstore/cosign/v3/cmd/cosign/cli/attest"
"github.com/sigstore/cosign/v3/cmd/cosign/cli/initialize"
"github.com/sigstore/cosign/v3/cmd/cosign/cli/options"
"github.com/sigstore/cosign/v3/cmd/cosign/cli/sign"
cliverify "github.com/sigstore/cosign/v3/cmd/cosign/cli/verify"
"github.com/sigstore/cosign/v3/pkg/cosign/env"
"github.com/sigstore/cosign/v3/pkg/cosign"
ociremote "github.com/sigstore/cosign/v3/pkg/oci/remote"
)

Expand All @@ -56,21 +60,33 @@ func TestInsecureRegistry(t *testing.T) {
useOCI11 := os.Getenv("oci11Var") != ""

rekorURL := os.Getenv(rekorURLVar)
must(downloadAndSetEnv(t, rekorURL+"/api/v1/log/publicKey", env.VariableSigstoreRekorPublicKey.String(), td), t)

ctx := context.Background()
tufLocalCache := t.TempDir()
t.Setenv("TUF_ROOT", tufLocalCache)
rootPath := os.Getenv("TUF_ROOT_JSON")
mirror := os.Getenv("TUF_MIRROR")
must(initialize.DoInitialize(ctx, rootPath, mirror), t)

ko := options.KeyOpts{
KeyRef: privKey,
PassFunc: passFunc,
RekorURL: rekorURL,
SkipConfirmation: true,
}
trustedMaterial, err := cosign.TrustedRoot()
must(err, t)
ko.TrustedMaterial = trustedMaterial

// Sign without bundle format
so := options.SignOptions{
Upload: true,
TlogUpload: true,
}
mustErr(sign.SignCmd(ro, ko, so, []string{imgName}), t)
so.Registry = options.RegistryOptions{
AllowInsecure: true,
AllowInsecure: true,
AllowHTTPRegistry: true,
}
if useOCI11 {
so.RegistryExperimental = options.RegistryExperimentalOptions{
Expand All @@ -83,17 +99,105 @@ func TestInsecureRegistry(t *testing.T) {
KeyRef: pubKey,
CheckClaims: true,
RegistryOptions: options.RegistryOptions{
AllowInsecure: true,
AllowInsecure: true,
AllowHTTPRegistry: true,
},
}
if useOCI11 {
cmd.ExperimentalOCI11 = true
}
must(cmd.Exec(context.Background(), []string{imgName}), t)

// Sign new image with new bundle format
// (Must be a new image or the old bundle may be verified instead)
imgName = path.Join(repo, "cosign-registry-e2e-2")
cleanup2 := makeImageIndexWithInsecureRegistry(t, imgName)
defer cleanup2()

so.NewBundleFormat = true
must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
cmd.NewBundleFormat = true
must(cmd.Exec(context.Background(), []string{imgName}), t)
}

func TestAttestInsecureRegistry(t *testing.T) {
if os.Getenv("COSIGN_TEST_REPO") == "" {
t.Fatal("COSIGN_TEST_REPO must be set to an insecure registry for this test")
}
repo, stop := reg(t)
defer stop()
td := t.TempDir()

imgName := path.Join(repo, "cosign-registry-e2e")
cleanup := makeImageIndexWithInsecureRegistry(t, imgName)
defer cleanup()

_, privKey, pubKey := keypair(t, td)

rekorURL := os.Getenv(rekorURLVar)

ctx := context.Background()
tufLocalCache := t.TempDir()
t.Setenv("TUF_ROOT", tufLocalCache)
rootPath := os.Getenv("TUF_ROOT_JSON")
mirror := os.Getenv("TUF_MIRROR")
must(initialize.DoInitialize(ctx, rootPath, mirror), t)

ko := options.KeyOpts{
KeyRef: privKey,
PassFunc: passFunc,
RekorURL: rekorURL,
SkipConfirmation: true,
}
trustedMaterial, err := cosign.TrustedRoot()
must(err, t)
ko.TrustedMaterial = trustedMaterial

slsaAttestation := `{ "buildType": "x", "builder": { "id": "2" }, "recipe": {} }`
slsaAttestationPath := filepath.Join(td, "attestation.slsa.json")
if err := os.WriteFile(slsaAttestationPath, []byte(slsaAttestation), 0600); err != nil {
t.Fatal(err)
}

// Attest without bundle
attestCmd := attest.AttestCommand{
KeyOpts: ko,
PredicatePath: slsaAttestationPath,
PredicateType: "slsaprovenance",
Timeout: 30 * time.Second,
RekorEntryType: "dsse",
TlogUpload: true,
RegistryOptions: options.RegistryOptions{
AllowInsecure: true,
AllowHTTPRegistry: true,
},
}
must(attestCmd.Exec(ctx, imgName), t)
verifyAttestation := cliverify.VerifyAttestationCommand{
KeyRef: pubKey,
PredicateType: "slsaprovenance",
RegistryOptions: options.RegistryOptions{
AllowInsecure: true,
AllowHTTPRegistry: true,
},
}
must(verifyAttestation.Exec(ctx, []string{imgName}), t)

// Attest with new bundle
imgName = path.Join(repo, "cosign-registry-e2e-2")
cleanup2 := makeImageIndexWithInsecureRegistry(t, imgName)
defer cleanup2()

ko.NewBundleFormat = true
attestCmd.KeyOpts = ko
must(attestCmd.Exec(ctx, imgName), t)
verifyAttestation.CommonVerifyOptions.NewBundleFormat = true
verifyAttestation.IgnoreTlog = false
must(verifyAttestation.Exec(ctx, []string{imgName}), t)
}

func makeImageIndexWithInsecureRegistry(t *testing.T, n string) func() {
ref, err := name.ParseReference(n, name.WeakValidation)
ref, err := name.ParseReference(n, name.WeakValidation, name.Insecure)
if err != nil {
t.Fatal(err)
}
Expand Down
Loading