From 58000e265602ab88b35c544e8546c48cfaa73d3b Mon Sep 17 00:00:00 2001 From: Aaron Lew <64337293+aaronlew02@users.noreply.github.com> Date: Mon, 24 Nov 2025 19:58:08 -0500 Subject: [PATCH] Update e2e tests to use scaffolding containers Signed-off-by: Aaron Lew <64337293+aaronlew02@users.noreply.github.com> --- test/e2e_attach_test.go | 83 --------------- test/e2e_test.go | 228 +++++++++++++++++++--------------------- test/e2e_test.sh | 108 +++---------------- test/fakeoidc/go.mod | 7 -- test/fakeoidc/go.sum | 14 --- test/fakeoidc/main.go | 118 --------------------- test/helpers.go | 15 ++- 7 files changed, 129 insertions(+), 444 deletions(-) delete mode 100644 test/fakeoidc/go.mod delete mode 100644 test/fakeoidc/go.sum delete mode 100644 test/fakeoidc/main.go diff --git a/test/e2e_attach_test.go b/test/e2e_attach_test.go index 085351fec57..f157d5294ac 100644 --- a/test/e2e_attach_test.go +++ b/test/e2e_attach_test.go @@ -26,13 +26,11 @@ import ( "encoding/json" "encoding/pem" "fmt" - "net/http/httptest" "os" "path" "path/filepath" "strings" "testing" - "time" "github.com/go-openapi/strfmt" "github.com/google/go-cmp/cmp" @@ -43,15 +41,10 @@ import ( "github.com/sigstore/cosign/v3/cmd/cosign/cli/generate" "github.com/sigstore/cosign/v3/cmd/cosign/cli/options" cliverify "github.com/sigstore/cosign/v3/cmd/cosign/cli/verify" - "github.com/sigstore/cosign/v3/internal/pkg/cosign/tsa" - "github.com/sigstore/cosign/v3/internal/pkg/cosign/tsa/client" cert_test "github.com/sigstore/cosign/v3/internal/test" "github.com/sigstore/cosign/v3/pkg/cosign" "github.com/sigstore/cosign/v3/pkg/cosign/bundle" ociremote "github.com/sigstore/cosign/v3/pkg/oci/remote" - tsaclient "github.com/sigstore/timestamp-authority/v2/pkg/client" - "github.com/sigstore/timestamp-authority/v2/pkg/server" - "github.com/spf13/viper" ) func TestAttachSignature(t *testing.T) { @@ -167,82 +160,6 @@ func TestAttachSignature(t *testing.T) { must(verifyCmd.Exec(ctx, args), t) } -func TestAttachWithRFC3161Timestamp(t *testing.T) { - ctx := context.Background() - // TSA server needed to create timestamp - viper.Set("timestamp-signer", "memory") - viper.Set("timestamp-signer-hash", "sha256") - apiServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second) - server := httptest.NewServer(apiServer.GetHandler()) - t.Cleanup(server.Close) - - repo, stop := reg(t) - defer stop() - td := t.TempDir() - - imgName := path.Join(repo, "cosign-attach-timestamp-e2e") - - _, _, cleanup := mkimage(t, imgName) - defer cleanup() - - b := bytes.Buffer{} - must(generate.GenerateCmd(context.Background(), options.RegistryOptions{}, imgName, nil, &b), t) - - rootCert, rootKey, _ := cert_test.GenerateRootCa() - subCert, subKey, _ := cert_test.GenerateSubordinateCa(rootCert, rootKey) - leafCert, privKey, _ := cert_test.GenerateLeafCert("subject@mail.com", "oidc-issuer", subCert, subKey) - pemRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rootCert.Raw}) - pemSub := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: subCert.Raw}) - pemLeaf := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCert.Raw}) - - payloadref := mkfile(b.String(), td, t) - - h := sha256.Sum256(b.Bytes()) - signature, _ := privKey.Sign(rand.Reader, h[:], crypto.SHA256) - b64signature := base64.StdEncoding.EncodeToString(signature) - sigRef := mkfile(b64signature, td, t) - pemleafRef := mkfile(string(pemLeaf), td, t) - pemrootRef := mkfile(string(pemRoot), td, t) - - certchainRef := mkfile(string(append(pemSub, pemRoot...)), td, t) - - t.Setenv("SIGSTORE_ROOT_FILE", pemrootRef) - - tsclient, err := tsaclient.GetTimestampClient(server.URL) - if err != nil { - t.Error(err) - } - - chain, err := tsclient.Timestamp.GetTimestampCertChain(nil) - if err != nil { - t.Fatalf("unexpected error getting timestamp chain: %v", err) - } - - file, err := os.CreateTemp(os.TempDir(), "tempfile") - if err != nil { - t.Fatalf("error creating temp file: %v", err) - } - defer os.Remove(file.Name()) - _, err = file.WriteString(chain.Payload) - if err != nil { - t.Fatalf("error writing chain payload to temp file: %v", err) - } - - tsBytes, err := tsa.GetTimestampedSignature(signature, client.NewTSAClient(server.URL+"/api/v1/timestamp")) - if err != nil { - t.Fatalf("unexpected error creating timestamp: %v", err) - } - rfc3161TSRef := mkfile(string(tsBytes), td, t) - - // Upload it! - err = attach.SignatureCmd(ctx, options.RegistryOptions{}, sigRef, payloadref, pemleafRef, certchainRef, rfc3161TSRef, "", imgName) - if err != nil { - t.Fatal(err) - } - - must(verifyKeylessTSA(imgName, file.Name(), true, true), t) -} - func TestAttachWithRekorBundle(t *testing.T) { ctx := context.Background() diff --git a/test/e2e_test.go b/test/e2e_test.go index 60abf75ae47..b94374cb7ee 100644 --- a/test/e2e_test.go +++ b/test/e2e_test.go @@ -88,8 +88,6 @@ import ( "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/payload" tsaclient "github.com/sigstore/timestamp-authority/v2/pkg/client" - "github.com/sigstore/timestamp-authority/v2/pkg/server" - "github.com/spf13/viper" _ "k8s.io/client-go/plugin/pkg/client/auth" ) @@ -610,9 +608,8 @@ func trustedRootCmd(t *testing.T, downloadDirectory, tsaURL string) *trustedroot defer rekorFP.Close() must(downloadFile(rekorURL+"/api/v1/log/publicKey", rekorFP), t) ctfePath := filepath.Join(downloadDirectory, "ctfe.pub") - home, err := os.UserHomeDir() - must(err, t) - must(copyFile(filepath.Join(home, "fulcio", "config", "ctfe", "pubkey.pem"), ctfePath), t) + ctLogKey := os.Getenv("CT_LOG_KEY") + must(copyFile(ctLogKey, ctfePath), t) out := filepath.Join(downloadDirectory, "trusted_root.json") cmd := &trustedroot.CreateCmd{ CertChain: []string{caPath}, @@ -646,23 +643,17 @@ func prepareTrustedRootWithSelfSignedCertificate(t *testing.T, certPath, tsaURL } func TestSignVerifyWithTUFMirror(t *testing.T) { - home, err := os.UserHomeDir() // fulcio repo was downloaded to $HOME in e2e_test.sh - must(err, t) + ctLogKey := os.Getenv("CT_LOG_KEY") tufLocalCache := t.TempDir() t.Setenv("TUF_ROOT", tufLocalCache) tufMirror := t.TempDir() - viper.Set("timestamp-signer", "memory") - viper.Set("timestamp-signer-hash", "sha256") - tsaAPIServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second) - tsaServer := httptest.NewServer(tsaAPIServer.GetHandler()) - t.Cleanup(tsaServer.Close) tufServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.FileServer(http.Dir(tufMirror)).ServeHTTP(w, r) })) mirror := tufServer.URL - tsaLeaf, tsaInter, tsaRoot, err := downloadTSACerts(t.TempDir(), tsaServer.URL) + tsaLeaf, tsaInter, tsaRoot, err := downloadTSACerts(t.TempDir(), tsaURL) must(err, t) - trustedRoot := prepareTrustedRoot(t, tsaServer.URL) + trustedRoot := prepareTrustedRoot(t, tsaURL) tests := []struct { name string targets []targetInfo @@ -674,7 +665,7 @@ func TestSignVerifyWithTUFMirror(t *testing.T) { targets: []targetInfo{ { name: "ct.pub", - source: filepath.Join(home, "fulcio", "config", "ctfe", "pubkey.pem"), + source: ctLogKey, }, }, wantSignErr: true, @@ -692,7 +683,7 @@ func TestSignVerifyWithTUFMirror(t *testing.T) { }, { name: "ctfe.pub", - source: filepath.Join(home, "fulcio", "config", "ctfe", "pubkey.pem"), + source: ctLogKey, }, { name: "tsa_leaf.crt.pem", @@ -721,7 +712,7 @@ func TestSignVerifyWithTUFMirror(t *testing.T) { }, { name: "ctfe.pub", - source: filepath.Join(home, "fulcio", "config", "ctfe", "pubkey.pem"), + source: ctLogKey, }, { name: "tsaleaf.pem", @@ -759,7 +750,7 @@ func TestSignVerifyWithTUFMirror(t *testing.T) { { name: "cert-transparency.pem", usage: "CTFE", - source: filepath.Join(home, "fulcio", "config", "ctfe", "pubkey.pem"), + source: ctLogKey, }, { name: "tsaleaf.pem", @@ -816,7 +807,7 @@ func TestSignVerifyWithTUFMirror(t *testing.T) { RekorURL: rekorURL, IDToken: identityToken, SkipConfirmation: true, - TSAServerURL: tsaServer.URL + "/api/v1/timestamp", + TSAServerURL: tsaURL + "/api/v1/timestamp", } trustedMaterial, err := cosign.TrustedRoot() if err == nil { @@ -835,7 +826,7 @@ func TestSignVerifyWithTUFMirror(t *testing.T) { must(gotErr, t) // Verify an image - issuer := os.Getenv("OIDC_URL") + issuer := os.Getenv("ISSUER_URL") verifyCmd := cliverify.VerifyCommand{ CertVerifyOptions: options.CertVerifyOptions{ CertOidcIssuer: issuer, @@ -917,17 +908,12 @@ func TestSignAttestVerifyBlobWithSigningConfig(t *testing.T) { tufLocalCache := t.TempDir() t.Setenv("TUF_ROOT", tufLocalCache) tufMirror := t.TempDir() - viper.Set("timestamp-signer", "memory") - viper.Set("timestamp-signer-hash", "sha256") - tsaAPIServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second) - tsaServer := httptest.NewServer(tsaAPIServer.GetHandler()) - t.Cleanup(tsaServer.Close) tufServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.FileServer(http.Dir(tufMirror)).ServeHTTP(w, r) })) mirror := tufServer.URL - trustedRoot := prepareTrustedRoot(t, tsaServer.URL) - signingConfigStr := prepareSigningConfig(t, fulcioURL, rekorURL, "unused", tsaServer.URL+"/api/v1/timestamp") + trustedRoot := prepareTrustedRoot(t, tsaURL) + signingConfigStr := prepareSigningConfig(t, fulcioURL, rekorURL, "unused", tsaURL+"/api/v1/timestamp") sc, err := os.ReadFile(signingConfigStr) must(err, t) fmt.Println(string(sc)) @@ -981,7 +967,7 @@ func TestSignAttestVerifyBlobWithSigningConfig(t *testing.T) { must(err, t) // Verify a blob - issuer := os.Getenv("OIDC_URL") + issuer := os.Getenv("ISSUER_URL") verifyBlobCmd := cliverify.VerifyBlobCmd{ KeyOpts: ko, CertVerifyOptions: options.CertVerifyOptions{ @@ -1032,17 +1018,12 @@ func TestSignAttestVerifyContainerWithSigningConfig(t *testing.T) { tufLocalCache := t.TempDir() t.Setenv("TUF_ROOT", tufLocalCache) tufMirror := t.TempDir() - viper.Set("timestamp-signer", "memory") - viper.Set("timestamp-signer-hash", "sha256") - tsaAPIServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second) - tsaServer := httptest.NewServer(tsaAPIServer.GetHandler()) - t.Cleanup(tsaServer.Close) tufServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.FileServer(http.Dir(tufMirror)).ServeHTTP(w, r) })) mirror := tufServer.URL - trustedRoot := prepareTrustedRoot(t, tsaServer.URL) - signingConfigStr := prepareSigningConfig(t, fulcioURL, rekorURL, "unused", tsaServer.URL+"/api/v1/timestamp") + trustedRoot := prepareTrustedRoot(t, tsaURL) + signingConfigStr := prepareSigningConfig(t, fulcioURL, rekorURL, "unused", tsaURL+"/api/v1/timestamp") _, err := newTUF(tufMirror, []targetInfo{ { @@ -1096,7 +1077,7 @@ func TestSignAttestVerifyContainerWithSigningConfig(t *testing.T) { // Verify Fulcio-signed image cmd := cliverify.VerifyCommand{ CertVerifyOptions: options.CertVerifyOptions{ - CertOidcIssuer: os.Getenv("OIDC_URL"), + CertOidcIssuer: os.Getenv("ISSUER_URL"), CertIdentity: certID, }, NewBundleFormat: true, @@ -1124,7 +1105,7 @@ func TestSignAttestVerifyContainerWithSigningConfig(t *testing.T) { // Verify attestation verifyAttestation := cliverify.VerifyAttestationCommand{ CertVerifyOptions: options.CertVerifyOptions{ - CertOidcIssuer: os.Getenv("OIDC_URL"), + CertOidcIssuer: os.Getenv("ISSUER_URL"), CertIdentity: certID, }, CommonVerifyOptions: options.CommonVerifyOptions{ @@ -1140,11 +1121,6 @@ func TestSignAttestVerifyContainerWithSigningConfig(t *testing.T) { func TestSignVerifyContainerWithSigningConfigWithCertificate(t *testing.T) { tufLocalCache := t.TempDir() t.Setenv("TUF_ROOT", tufLocalCache) - viper.Set("timestamp-signer", "memory") - viper.Set("timestamp-signer-hash", "sha256") - tsaAPIServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second) - tsaServer := httptest.NewServer(tsaAPIServer.GetHandler()) - t.Cleanup(tsaServer.Close) tufMirror := t.TempDir() tufServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.FileServer(http.Dir(tufMirror)).ServeHTTP(w, r) @@ -1181,8 +1157,8 @@ func TestSignVerifyContainerWithSigningConfigWithCertificate(t *testing.T) { importKeyPath := filepath.Join(keysDir, "import-priv.key") must(os.WriteFile(importKeyPath, keys.PrivateBytes, 0o600), t) - trustedRoot := prepareTrustedRootWithSelfSignedCertificate(t, certPath, tsaServer.URL) - signingConfigStr := prepareSigningConfig(t, fulcioURL, rekorURL, "unused", tsaServer.URL+"/api/v1/timestamp") + trustedRoot := prepareTrustedRootWithSelfSignedCertificate(t, certPath, tsaURL) + signingConfigStr := prepareSigningConfig(t, fulcioURL, rekorURL, "unused", tsaURL+"/api/v1/timestamp") _, err = newTUF(tufMirror, []targetInfo{ { @@ -1330,17 +1306,12 @@ func TestSignVerifyWithSigningConfigWithKey(t *testing.T) { tufLocalCache := t.TempDir() t.Setenv("TUF_ROOT", tufLocalCache) tufMirror := t.TempDir() - viper.Set("timestamp-signer", "memory") - viper.Set("timestamp-signer-hash", "sha256") - tsaAPIServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second) - tsaServer := httptest.NewServer(tsaAPIServer.GetHandler()) - t.Cleanup(tsaServer.Close) tufServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.FileServer(http.Dir(tufMirror)).ServeHTTP(w, r) })) mirror := tufServer.URL - trustedRoot := prepareTrustedRoot(t, tsaServer.URL) - signingConfigStr := prepareSigningConfig(t, fulcioURL, rekorURL, "unused", tsaServer.URL+"/api/v1/timestamp") + trustedRoot := prepareTrustedRoot(t, tsaURL) + signingConfigStr := prepareSigningConfig(t, fulcioURL, rekorURL, "unused", tsaURL+"/api/v1/timestamp") _, err := newTUF(tufMirror, []targetInfo{ { @@ -1516,7 +1487,7 @@ func TestSignVerifyBundle(t *testing.T) { // Verify Fulcio-signed image cmd = cliverify.VerifyCommand{ CertVerifyOptions: options.CertVerifyOptions{ - CertOidcIssuer: os.Getenv("OIDC_URL"), + CertOidcIssuer: os.Getenv("ISSUER_URL"), CertIdentityRegexp: ".+", }, CommonVerifyOptions: options.CommonVerifyOptions{ @@ -1617,12 +1588,7 @@ func TestTrustedRootCreateFromDefaults(t *testing.T) { })) t.Cleanup(tufServer.Close) mirror := tufServer.URL - viper.Set("timestamp-signer", "memory") - viper.Set("timestamp-signer-hash", "sha256") - tsaAPIServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second) - tsaServer := httptest.NewServer(tsaAPIServer.GetHandler()) - t.Cleanup(tsaServer.Close) - trustedRoot := prepareTrustedRoot(t, tsaServer.URL) + trustedRoot := prepareTrustedRoot(t, tsaURL) _, err := newTUF(tufMirror, []targetInfo{ { @@ -1711,16 +1677,10 @@ func TestSigningConfigCreateFromDefaults(t *testing.T) { })) t.Cleanup(tufServer.Close) mirror := tufServer.URL - tsaURL := "https://tsa.example" oidcURL := "https://oidc.example" signingConfigStr := prepareSigningConfig(t, fulcioURL, rekorURL, oidcURL, tsaURL+"/api/v1/timestamp") // Trusted root is needed as well for initialization - viper.Set("timestamp-signer", "memory") - viper.Set("timestamp-signer-hash", "sha256") - tsaAPIServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second) - tsaServer := httptest.NewServer(tsaAPIServer.GetHandler()) - t.Cleanup(tsaServer.Close) - trustedRoot := prepareTrustedRoot(t, tsaServer.URL) + trustedRoot := prepareTrustedRoot(t, tsaURL) _, err := newTUF(tufMirror, []targetInfo{ { @@ -2313,13 +2273,6 @@ func TestAttestationReplace(t *testing.T) { } func TestAttestationRFC3161Timestamp(t *testing.T) { - // TSA server needed to create timestamp - viper.Set("timestamp-signer", "memory") - viper.Set("timestamp-signer-hash", "sha256") - apiServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second) - server := httptest.NewServer(apiServer.GetHandler()) - t.Cleanup(server.Close) - repo, stop := reg(t) defer stop() td := t.TempDir() @@ -2356,7 +2309,7 @@ func TestAttestationRFC3161Timestamp(t *testing.T) { PredicatePath: slsaAttestationPath, PredicateType: "slsaprovenance", Timeout: 30 * time.Second, - TSAServerURL: server.URL + "/api/v1/timestamp", + TSAServerURL: tsaURL + "/api/v1/timestamp", TlogUpload: false, RekorEntryType: "dsse", } @@ -2372,7 +2325,7 @@ func TestAttestationRFC3161Timestamp(t *testing.T) { t.Fatal(fmt.Errorf("expected len(attestations) == 1, got %d", len(attestations))) } - client, err := tsaclient.GetTimestampClient(server.URL) + client, err := tsaclient.GetTimestampClient(tsaURL) if err != nil { t.Error(err) } @@ -2408,13 +2361,6 @@ func TestAttestationRFC3161Timestamp(t *testing.T) { } func TestAttestationBlobRFC3161Timestamp(t *testing.T) { - // TSA server needed to create timestamp - viper.Set("timestamp-signer", "memory") - viper.Set("timestamp-signer-hash", "sha256") - apiServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second) - server := httptest.NewServer(apiServer.GetHandler()) - t.Cleanup(server.Close) - blob := "someblob" predicate := `{ "buildType": "x", "builder": { "id": "2" }, "recipe": {} }` predicateType := "slsaprovenance" @@ -2442,7 +2388,7 @@ func TestAttestationBlobRFC3161Timestamp(t *testing.T) { KeyRef: privKeyPath, BundlePath: bundlePath, NewBundleFormat: true, - TSAServerURL: server.URL + "/api/v1/timestamp", + TSAServerURL: tsaURL + "/api/v1/timestamp", PassFunc: passFunc, } @@ -2456,7 +2402,7 @@ func TestAttestationBlobRFC3161Timestamp(t *testing.T) { } must(attestBlobCmd.Exec(ctx, bp), t) - client, err := tsaclient.GetTimestampClient(server.URL) + client, err := tsaclient.GetTimestampClient(tsaURL) if err != nil { t.Error(err) } @@ -2517,12 +2463,6 @@ func TestAttestationBlobRFC3161Timestamp(t *testing.T) { func TestVerifyWithCARoots(t *testing.T) { ctx := context.Background() - // TSA server needed to create timestamp - viper.Set("timestamp-signer", "memory") - viper.Set("timestamp-signer-hash", "sha256") - apiServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second) - server := httptest.NewServer(apiServer.GetHandler()) - t.Cleanup(server.Close) repo, stop := reg(t) defer stop() @@ -2575,7 +2515,7 @@ func TestVerifyWithCARoots(t *testing.T) { pemrootBundleRef := mkfile(string(append(pemRoot, pemRoot02...)), td, t) pemsubBundleRef := mkfile(string(append(pemSub, pemSub02...)), td, t) - tsclient, err := tsaclient.GetTimestampClient(server.URL) + tsclient, err := tsaclient.GetTimestampClient(tsaURL) if err != nil { t.Error(err) } @@ -2595,7 +2535,7 @@ func TestVerifyWithCARoots(t *testing.T) { t.Fatalf("error writing chain payload to temp file: %v", err) } - tsBytes, err := tsa.GetTimestampedSignature(signature, client.NewTSAClient(server.URL+"/api/v1/timestamp")) + tsBytes, err := tsa.GetTimestampedSignature(signature, client.NewTSAClient(tsaURL+"/api/v1/timestamp")) if err != nil { t.Fatalf("unexpected error creating timestamp: %v", err) } @@ -2879,14 +2819,7 @@ func TestFulcioBundle(t *testing.T) { } func TestRFC3161Timestamp(t *testing.T) { - // TSA server needed to create timestamp - viper.Set("timestamp-signer", "memory") - viper.Set("timestamp-signer-hash", "sha256") - apiServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second) - server := httptest.NewServer(apiServer.GetHandler()) - t.Cleanup(server.Close) - - client, err := tsaclient.GetTimestampClient(server.URL) + client, err := tsaclient.GetTimestampClient(tsaURL) if err != nil { t.Error(err) } @@ -2920,7 +2853,7 @@ func TestRFC3161Timestamp(t *testing.T) { ko := options.KeyOpts{ KeyRef: privKeyPath, PassFunc: passFunc, - TSAServerURL: server.URL + "/api/v1/timestamp", + TSAServerURL: tsaURL + "/api/v1/timestamp", } so := options.SignOptions{ Upload: true, @@ -2940,14 +2873,7 @@ func TestRekorBundleAndRFC3161Timestamp(t *testing.T) { t.Fatal(err) } - // TSA server needed to create timestamp - viper.Set("timestamp-signer", "memory") - viper.Set("timestamp-signer-hash", "sha256") - apiServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second) - server := httptest.NewServer(apiServer.GetHandler()) - t.Cleanup(server.Close) - - client, err := tsaclient.GetTimestampClient(server.URL) + client, err := tsaclient.GetTimestampClient(tsaURL) if err != nil { t.Error(err) } @@ -2980,7 +2906,7 @@ func TestRekorBundleAndRFC3161Timestamp(t *testing.T) { ko := options.KeyOpts{ KeyRef: privKeyPath, PassFunc: passFunc, - TSAServerURL: server.URL + "/api/v1/timestamp", + TSAServerURL: tsaURL + "/api/v1/timestamp", RekorURL: rekorURL, SkipConfirmation: true, } @@ -2995,6 +2921,76 @@ func TestRekorBundleAndRFC3161Timestamp(t *testing.T) { must(verifyTSA(pubKeyPath, imgName, true, nil, "", file.Name(), false), t) } +func TestAttachWithRFC3161Timestamp(t *testing.T) { + ctx := context.Background() + + repo, stop := reg(t) + defer stop() + td := t.TempDir() + + imgName := path.Join(repo, "cosign-attach-timestamp-e2e") + + _, _, cleanup := mkimage(t, imgName) + defer cleanup() + + b := bytes.Buffer{} + must(generate.GenerateCmd(context.Background(), options.RegistryOptions{}, imgName, nil, &b), t) + + rootCert, rootKey, _ := cert_test.GenerateRootCa() + subCert, subKey, _ := cert_test.GenerateSubordinateCa(rootCert, rootKey) + leafCert, privKey, _ := cert_test.GenerateLeafCert("subject@mail.com", "oidc-issuer", subCert, subKey) + pemRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rootCert.Raw}) + pemSub := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: subCert.Raw}) + pemLeaf := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCert.Raw}) + + payloadref := mkfile(b.String(), td, t) + + h := sha256.Sum256(b.Bytes()) + signature, _ := privKey.Sign(rand.Reader, h[:], crypto.SHA256) + b64signature := base64.StdEncoding.EncodeToString(signature) + sigRef := mkfile(b64signature, td, t) + pemleafRef := mkfile(string(pemLeaf), td, t) + pemrootRef := mkfile(string(pemRoot), td, t) + + certchainRef := mkfile(string(append(pemSub, pemRoot...)), td, t) + + t.Setenv("SIGSTORE_ROOT_FILE", pemrootRef) + + tsclient, err := tsaclient.GetTimestampClient(tsaURL) + if err != nil { + t.Error(err) + } + + chain, err := tsclient.Timestamp.GetTimestampCertChain(nil) + if err != nil { + t.Fatalf("unexpected error getting timestamp chain: %v", err) + } + + file, err := os.CreateTemp(os.TempDir(), "tempfile") + if err != nil { + t.Fatalf("error creating temp file: %v", err) + } + defer os.Remove(file.Name()) + _, err = file.WriteString(chain.Payload) + if err != nil { + t.Fatalf("error writing chain payload to temp file: %v", err) + } + + tsBytes, err := tsa.GetTimestampedSignature(signature, client.NewTSAClient(tsaURL+"/api/v1/timestamp")) + if err != nil { + t.Fatalf("unexpected error creating timestamp: %v", err) + } + rfc3161TSRef := mkfile(string(tsBytes), td, t) + + // Upload it! + err = attach.SignatureCmd(ctx, options.RegistryOptions{}, sigRef, payloadref, pemleafRef, certchainRef, rfc3161TSRef, "", imgName) + if err != nil { + t.Fatal(err) + } + + must(verifyKeylessTSA(imgName, file.Name(), pemrootRef, true, true), t) +} + func TestDuplicateSign(t *testing.T) { td := t.TempDir() err := downloadAndSetEnv(t, rekorURL+"/api/v1/log/publicKey", env.VariableSigstoreRekorPublicKey.String(), td) @@ -3483,12 +3479,6 @@ func TestSignBlobRFC3161TimestampBundle(t *testing.T) { if err != nil { t.Fatal(err) } - // TSA server needed to create timestamp - viper.Set("timestamp-signer", "memory") - viper.Set("timestamp-signer-hash", "sha256") - apiServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second) - server := httptest.NewServer(apiServer.GetHandler()) - t.Cleanup(server.Close) blob := "someblob" bp := filepath.Join(td, blob) @@ -3499,7 +3489,7 @@ func TestSignBlobRFC3161TimestampBundle(t *testing.T) { t.Fatal(err) } - client, err := tsaclient.GetTimestampClient(server.URL) + client, err := tsaclient.GetTimestampClient(tsaURL) if err != nil { t.Error(err) } @@ -3542,7 +3532,7 @@ func TestSignBlobRFC3161TimestampBundle(t *testing.T) { PassFunc: passFunc, BundlePath: bundlePath, RFC3161TimestampPath: tsPath, - TSAServerURL: server.URL + "/api/v1/timestamp", + TSAServerURL: tsaURL + "/api/v1/timestamp", RekorURL: rekorURL, SkipConfirmation: true, } @@ -4449,7 +4439,7 @@ from %s `, signedImg1) withLowercaseDockerfile := mkfile(withLowercaseDockerfileContents, td, t) - issuer := os.Getenv("OIDC_URL") + issuer := os.Getenv("ISSUER_URL") tests := []struct { name string @@ -4578,7 +4568,7 @@ spec: unsignedManifestContents := fmt.Sprintf(manifestTemplate, "unsigned-img", unsignedImg) unsignedManifest := mkfileWithExt(unsignedManifestContents, td, ".yaml", t) - issuer := os.Getenv("OIDC_URL") + issuer := os.Getenv("ISSUER_URL") tests := []struct { name string diff --git a/test/e2e_test.sh b/test/e2e_test.sh index d3ccd5bb34e..99560c1a1d9 100755 --- a/test/e2e_test.sh +++ b/test/e2e_test.sh @@ -16,116 +16,36 @@ set -ex -docker_compose="docker compose" -if ! ${docker_compose} version >/dev/null 2>&1; then - docker_compose="docker-compose" -fi - -echo "setting up OIDC provider" -pushd ./test/fakeoidc -oidcimg=$(ko build main.go --local) -docker network ls | grep fulcio_default || docker network create fulcio_default --label "com.docker.compose.network=fulcio_default" -docker run -d --rm -p 8080:8080 --network fulcio_default --name fakeoidc $oidcimg -cleanup_oidc() { - echo "cleaning up oidc" - docker stop fakeoidc -} -trap cleanup_oidc EXIT -oidc_ip=$(docker inspect fakeoidc | jq -r '.[0].NetworkSettings.Networks.fulcio_default.IPAddress') -export OIDC_URL="http://${oidc_ip}:8080" -cat < /tmp/fulcio-config.json -{ - "OIDCIssuers": { - "$OIDC_URL": { - "IssuerURL": "$OIDC_URL", - "ClientID": "sigstore", - "Type": "email" - } - } -} -EOF +echo "downloading sigstore/scaffolding repository" +SCAFFOLDING_DIR=$(mktemp -d) +git clone https://github.com/sigstore/scaffolding.git "$SCAFFOLDING_DIR" +SCAFFOLDING_SETUP_DIR="$SCAFFOLDING_DIR/actions/setup-sigstore-env" + +echo "setting up sigstore test environment" +pushd "$SCAFFOLDING_SETUP_DIR" +source ./run-containers.sh popd -pushd $HOME - -echo "downloading service repos" -for repo in rekor fulcio; do - if [[ ! -d $repo ]]; then - git clone https://github.com/sigstore/${repo}.git - else - pushd $repo - git pull - popd - fi -done - -echo "starting services" -export FULCIO_METRICS_PORT=2113 -export FULCIO_CONFIG=/tmp/fulcio-config.json -for repo in rekor fulcio; do - pushd $repo - if [ "$repo" == "fulcio" ]; then - yq -i e '.networks={"default":{ "name":"fulcio_default","external":true }}' docker-compose.yml - yq -i e '.services.fulcio-server.networks=["default"]' docker-compose.yml - fi - ${docker_compose} up -d - echo -n "waiting up to 60 sec for system to start" - if [ "$repo" == "fulcio" ]; then - healthytotal=3 - elif [ "$repo" == "rekor" ]; then - healthytotal=5 - else - # handle no match in case another service is added - healthytotal=0 - fi - count=0 - until [ $(${docker_compose} ps | grep -c "(healthy)") == $healthytotal ]; - do - if [ $count -eq 18 ]; then - echo "! timeout reached" - exit 1 - else - echo -n "." - sleep 10 - let 'count+=1' - fi - done +cleanup() { + echo "cleaning up sigstore test environment" + pushd "$SCAFFOLDING_SETUP_DIR" + stop_services popd -done -cleanup_services() { - echo "cleaning up" - cleanup_oidc - for repo in rekor fulcio; do - pushd $HOME/$repo - ${docker_compose} down - popd - done + docker rm -f registry registry-2 || true } -trap cleanup_services EXIT +trap cleanup EXIT echo echo "running tests" - -popd go test -tags=e2e -v -race ./test/... # Test on a private registry echo "testing sign/verify/clean on private registry" -cleanup_registry() { - cleanup_services - docker rm -f registry -} -trap cleanup_registry EXIT docker run -d -p 5000:5000 --restart always -e REGISTRY_STORAGE_DELETE_ENABLED=true --name registry registry:latest export COSIGN_TEST_REPO=localhost:5000 go test -tags=e2e -v ./test/... -run TestSignVerifyClean # Test with signature in separate registry -cleanup() { - cleanup_registry - docker rm -f registry-2 -} -trap cleanup EXIT docker run -d -p 5001:5000 --restart always -e REGISTRY_STORAGE_DELETE_ENABLED=true --name registry-2 registry:latest export COSIGN_REPOSITORY=localhost:5001/hello go test -tags=e2e -v ./test/... -run TestSignVerifyWithRepoOverride diff --git a/test/fakeoidc/go.mod b/test/fakeoidc/go.mod deleted file mode 100644 index 8678ec46645..00000000000 --- a/test/fakeoidc/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -module github.com/sigstore/cosign/test/fakeoidc - -go 1.24.0 - -require github.com/go-jose/go-jose/v4 v4.0.5 - -require golang.org/x/crypto v0.45.0 // indirect diff --git a/test/fakeoidc/go.sum b/test/fakeoidc/go.sum deleted file mode 100644 index 9df61cd64db..00000000000 --- a/test/fakeoidc/go.sum +++ /dev/null @@ -1,14 +0,0 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= -github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/test/fakeoidc/main.go b/test/fakeoidc/main.go deleted file mode 100644 index 95dc357e7f8..00000000000 --- a/test/fakeoidc/main.go +++ /dev/null @@ -1,118 +0,0 @@ -// -// Copyright 2024 The Sigstore Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Mock OIDC server, based on https://github.com/sigstore/fulcio/blob/efec18aaed12d1f91eeaaba96e90f86170c2ada4/pkg/server/grpc_server_test.go#L2235 -package main - -import ( - "crypto/rand" - "crypto/rsa" - "encoding/json" - "fmt" - "log" - "net/http" - "time" - - "github.com/go-jose/go-jose/v4" - "github.com/go-jose/go-jose/v4/jwt" -) - -var ( - signer jose.Signer - jwk jose.JSONWebKey -) - -type config struct { - Issuer string `json:"issuer"` - JWKSURI string `json:"jwks_uri"` -} - -type customClaims struct { - Email string `json:"email"` - EmailVerified bool `json:"email_verified"` -} - -func init() { - pk, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - log.Fatal(err) - } - jwk = jose.JSONWebKey{ - Algorithm: string(jose.RS256), - Key: pk, - } - signer, err = jose.NewSigner(jose.SigningKey{ - Algorithm: jose.RS256, - Key: jwk.Key, - }, nil) - if err != nil { - log.Fatal(err) - } -} - -func token(w http.ResponseWriter, r *http.Request) { - log.Print("handling token") - token, err := jwt.Signed(signer).Claims(jwt.Claims{ - Issuer: fmt.Sprintf("http://%s", r.Host), - IssuedAt: jwt.NewNumericDate(time.Now()), - Expiry: jwt.NewNumericDate(time.Now().Add(30 * time.Minute)), - Subject: "foo@bar.com", - Audience: jwt.Audience{"sigstore"}, - }).Claims(customClaims{ - Email: "foo@bar.com", - EmailVerified: true, - }).Serialize() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - w.Write([]byte(token)) -} - -func keys(w http.ResponseWriter, r *http.Request) { - log.Print("handling keys") - keys, err := json.Marshal(jose.JSONWebKeySet{ - Keys: []jose.JSONWebKey{ - jwk.Public(), - }, - }) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - w.Header().Add("Content-type", "application/json") - w.Write(keys) -} - -func wellKnown(w http.ResponseWriter, r *http.Request) { - log.Print("handling discovery") - issuer := fmt.Sprintf("http://%s", r.Host) - cfg, err := json.Marshal(config{ - Issuer: issuer, - JWKSURI: issuer + "/keys", - }) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - w.Header().Add("Content-type", "application/json") - w.Write(cfg) -} - -func main() { - http.HandleFunc("/token", token) - http.HandleFunc("/keys", keys) - http.HandleFunc("/.well-known/openid-configuration", wellKnown) - if err := http.ListenAndServe(":8080", nil); err != nil { - log.Fatal(err) - } -} diff --git a/test/helpers.go b/test/helpers.go index 02c01ecccb0..2ef90070b30 100644 --- a/test/helpers.go +++ b/test/helpers.go @@ -36,7 +36,6 @@ import ( "net/http/httptest" "net/url" "os" - "path" "path/filepath" "testing" "time" @@ -65,6 +64,7 @@ import ( const ( rekorURL = "http://127.0.0.1:3000" fulcioURL = "http://127.0.0.1:5555" + tsaURL = "http://127.0.0.1:3004" certID = "foo@bar.com" ) @@ -151,12 +151,13 @@ var verifyTSA = func(keyRef, imageRef string, checkClaims bool, annotations map[ return cmd.Exec(context.Background(), args) } -var verifyKeylessTSA = func(imageRef string, tsaCertChain string, skipSCT bool, skipTlogVerify bool) error { //nolint: unused +var verifyKeylessTSA = func(imageRef, tsaCertChain, certChain string, skipSCT, skipTlogVerify bool) error { //nolint: unused cmd := cliverify.VerifyCommand{ CertVerifyOptions: options.CertVerifyOptions{ CertOidcIssuerRegexp: ".*", CertIdentityRegexp: ".*", }, + CertChain: certChain, RekorURL: rekorURL, HashAlgorithm: crypto.SHA256, TSACertChainPath: tsaCertChain, @@ -510,13 +511,9 @@ func registryClientOpts(ctx context.Context) []remote.Option { // setLocalEnv sets SIGSTORE_CT_LOG_PUBLIC_KEY_FILE, SIGSTORE_ROOT_FILE, and SIGSTORE_REKOR_PUBLIC_KEY for the locally running sigstore deployment. func setLocalEnv(t *testing.T, dir string) error { - // fulcio repo is downloaded to the user's home directory by e2e_test.sh - home, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("error getting home directory: %w", err) - } - t.Setenv(env.VariableSigstoreCTLogPublicKeyFile.String(), path.Join(home, "fulcio/config/ctfe/pubkey.pem")) - err = downloadAndSetEnv(t, fulcioURL+"/api/v1/rootCert", env.VariableSigstoreRootFile.String(), dir) + ctLogKey := os.Getenv("CT_LOG_KEY") //nolint: forbidigo + t.Setenv(env.VariableSigstoreCTLogPublicKeyFile.String(), ctLogKey) + err := downloadAndSetEnv(t, fulcioURL+"/api/v1/rootCert", env.VariableSigstoreRootFile.String(), dir) if err != nil { return fmt.Errorf("error setting %s env var: %w", env.VariableSigstoreRootFile.String(), err) }