From b7d829f2db0ffe9f8dcf5f931e6a3c4438d847aa Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Wed, 13 Nov 2024 10:55:39 +0800 Subject: [PATCH] fix&test: discard error for NewFileCache & E2E test for CRL with cache (#1079) Fix: - discard error when NewFileCache failed: any cache errors should not block the verification process Test: - added `crl_server.py` script to run in the background as a mock CRL server - added a `gen_crl_testing_certs.sh` script for generating required certificate chain for testing CRL - added 8 E2E test cases for CRL and CRL cache - refactored E2E framework to allow add test key from different folders Bump: - updated ginkgo to v2.21.0 Resolves #1068 Signed-off-by: Junjie Gao --- cmd/notation/verify.go | 12 +- cmd/notation/verify_test.go | 13 - test/e2e/go.mod | 13 +- test/e2e/go.sum | 30 +- test/e2e/internal/notation/host.go | 21 +- test/e2e/internal/notation/key.go | 20 +- test/e2e/internal/utils/crl.go | 79 +++++ test/e2e/internal/utils/host.go | 10 +- test/e2e/run.sh | 11 +- test/e2e/scripts/crl_server.py | 81 +++++ test/e2e/scripts/gen_crl_testing_certs.sh | 231 +++++++++++++ test/e2e/suite/plugin/install.go | 4 +- test/e2e/suite/scenario/crl.go | 315 ++++++++++++++++++ test/e2e/suite/trustpolicy/trust_store.go | 2 +- .../e2e/suite/trustpolicy/trusted_identity.go | 2 +- .../config/crl/certchain_with_crl.pem | 76 +++++ test/e2e/testdata/config/crl/intermediate.crl | Bin 0 -> 487 bytes .../config/crl/intermediate_revoked.crl | Bin 0 -> 510 bytes test/e2e/testdata/config/crl/leaf.crl | Bin 0 -> 495 bytes test/e2e/testdata/config/crl/leaf.key | 28 ++ test/e2e/testdata/config/crl/leaf_expired.crl | Bin 0 -> 493 bytes test/e2e/testdata/config/crl/leaf_revoked.crl | Bin 0 -> 518 bytes test/e2e/testdata/config/crl/root.crt | 25 ++ 23 files changed, 910 insertions(+), 63 deletions(-) create mode 100644 test/e2e/internal/utils/crl.go create mode 100644 test/e2e/scripts/crl_server.py create mode 100755 test/e2e/scripts/gen_crl_testing_certs.sh create mode 100644 test/e2e/suite/scenario/crl.go create mode 100644 test/e2e/testdata/config/crl/certchain_with_crl.pem create mode 100644 test/e2e/testdata/config/crl/intermediate.crl create mode 100644 test/e2e/testdata/config/crl/intermediate_revoked.crl create mode 100644 test/e2e/testdata/config/crl/leaf.crl create mode 100644 test/e2e/testdata/config/crl/leaf.key create mode 100644 test/e2e/testdata/config/crl/leaf_expired.crl create mode 100644 test/e2e/testdata/config/crl/leaf_revoked.crl create mode 100644 test/e2e/testdata/config/crl/root.crt diff --git a/cmd/notation/verify.go b/cmd/notation/verify.go index 9fc776ce2..66cc0df01 100644 --- a/cmd/notation/verify.go +++ b/cmd/notation/verify.go @@ -246,11 +246,13 @@ func getVerifier(ctx context.Context) (notation.Verifier, error) { } fileCache, err := crl.NewFileCache(cacheRoot) if err != nil { - return nil, err - } - crlFetcher.Cache = &clicrl.CacheWithLog{ - Cache: fileCache, - DiscardCacheError: crlFetcher.DiscardCacheError, + // discard NewFileCache error as cache errors are not critical + fmt.Fprintf(os.Stderr, "Warning: %v\n", err) + } else { + crlFetcher.Cache = &clicrl.CacheWithLog{ + Cache: fileCache, + DiscardCacheError: crlFetcher.DiscardCacheError, + } } revocationCodeSigningValidator, err := revocation.NewWithOptions(revocation.Options{ OCSPHTTPClient: ocspHttpClient, diff --git a/cmd/notation/verify_test.go b/cmd/notation/verify_test.go index 4eb5e0e41..b68796436 100644 --- a/cmd/notation/verify_test.go +++ b/cmd/notation/verify_test.go @@ -19,7 +19,6 @@ import ( "os" "path/filepath" "reflect" - "runtime" "testing" "github.com/notaryproject/notation-go/dir" @@ -119,18 +118,6 @@ func TestGetVerifier(t *testing.T) { t.Fatalf("expected %s, but got %s", expectedErrMsg, err) } }) - - t.Run("failed to new crl file cache", func(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("skipping test on Windows") - } - dir.UserCacheDir = "/cache" - expectedErrMsg := "failed to create crl file cache: mkdir /cache: permission denied" - _, err := getVerifier(context.Background()) - if err == nil || err.Error() != expectedErrMsg { - t.Fatalf("expected %s, but got %s", expectedErrMsg, err) - } - }) } func dummyOCIPolicyDocument() trustpolicy.OCIDocument { diff --git a/test/e2e/go.mod b/test/e2e/go.mod index 9a4c19a23..1e0b2fd02 100644 --- a/test/e2e/go.mod +++ b/test/e2e/go.mod @@ -4,7 +4,8 @@ go 1.23 require ( github.com/notaryproject/notation-core-go v1.2.0-rc.1 - github.com/onsi/ginkgo/v2 v2.20.2 + github.com/notaryproject/notation-go v1.2.0-beta.1.0.20240926015724-84c2ec076201 + github.com/onsi/ginkgo/v2 v2.21.0 github.com/onsi/gomega v1.34.2 github.com/opencontainers/image-spec v1.1.0 oras.land/oras-go/v2 v2.5.0 @@ -15,16 +16,16 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect + github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect github.com/notaryproject/tspclient-go v0.2.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/veraison/go-cose v1.1.0 // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/net v0.28.0 // indirect + golang.org/x/net v0.30.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/text v0.17.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/tools v0.26.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/test/e2e/go.sum b/test/e2e/go.sum index d423acdd3..707618328 100644 --- a/test/e2e/go.sum +++ b/test/e2e/go.sum @@ -8,14 +8,16 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= 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/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/notaryproject/notation-core-go v1.2.0-rc.1 h1:VMFlG+9a1JoNAQ3M96g8iqCq0cDRtE7XBaiTD8Ouvqw= github.com/notaryproject/notation-core-go v1.2.0-rc.1/go.mod h1:b/70rA4OgOHlg0A7pb8zTWKJadFO6781zS3a37KHEJQ= +github.com/notaryproject/notation-go v1.2.0-beta.1.0.20240926015724-84c2ec076201 h1:2QBYa9Df+vMwMiaHaFqPoUiwfx5vcPEgM7KbusivTpw= +github.com/notaryproject/notation-go v1.2.0-beta.1.0.20240926015724-84c2ec076201/go.mod h1:F6zMQl3PhVdCsI1xlIjK66kCorUQhWkoMtlZdvJWxFI= github.com/notaryproject/tspclient-go v0.2.0 h1:g/KpQGmyk/h7j60irIRG1mfWnibNOzJ8WhLqAzuiQAQ= github.com/notaryproject/tspclient-go v0.2.0/go.mod h1:LGyA/6Kwd2FlM0uk8Vc5il3j0CddbWSHBj/4kxQDbjs= -github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= -github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -30,18 +32,18 @@ github.com/veraison/go-cose v1.1.0 h1:AalPS4VGiKavpAzIlBjrn7bhqXiXi4jbMYY/2+UC+4 github.com/veraison/go-cose v1.1.0/go.mod h1:7ziE85vSq4ScFTg6wyoMXjucIGOf4JkFEZi/an96Ct4= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/test/e2e/internal/notation/host.go b/test/e2e/internal/notation/host.go index 168015a2b..0ad17854f 100644 --- a/test/e2e/internal/notation/host.go +++ b/test/e2e/internal/notation/host.go @@ -123,7 +123,7 @@ func Opts(options ...utils.HostOption) []utils.HostOption { func BaseOptions() []utils.HostOption { return Opts( AuthOption("", ""), - AddKeyOption("e2e.key", "e2e.crt"), + AddKeyOption(filepath.Join(NotationE2ELocalKeysDir, "e2e.key"), filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), AddTrustPolicyOption("trustpolicy.json"), ) @@ -141,7 +141,7 @@ func TimestampOptions(verifyTimestamp string) []utils.HostOption { return Opts( AuthOption("", ""), - AddKeyOption("e2e.key", "e2e.crt"), + AddKeyOption(filepath.Join(NotationE2ELocalKeysDir, "e2e.key"), filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), AddTimestampTrustStoreOption("e2e", filepath.Join(NotationE2EConfigPath, "timestamp", "globalsignTSARoot.cer")), AddTimestampTrustStoreOption("e2e", filepath.Join(NotationE2EConfigPath, "timestamp", "DigiCertTSARootSHA384.cer")), @@ -149,10 +149,19 @@ func TimestampOptions(verifyTimestamp string) []utils.HostOption { ) } +func CRLOptions() []utils.HostOption { + return Opts( + AuthOption("", ""), + AddKeyOption(filepath.Join(NotationE2EConfigPath, "crl", "leaf.key"), filepath.Join(NotationE2EConfigPath, "crl", "certchain_with_crl.pem")), + AddTrustStoreOption("e2e", filepath.Join(NotationE2EConfigPath, "crl", "root.crt")), + AddTrustPolicyOption("trustpolicy.json"), + ) +} + func BaseOptionsWithExperimental() []utils.HostOption { return Opts( AuthOption("", ""), - AddKeyOption("e2e.key", "e2e.crt"), + AddKeyOption(filepath.Join(NotationE2ELocalKeysDir, "e2e.key"), filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), AddTrustPolicyOption("trustpolicy.json"), EnableExperimental(), @@ -163,7 +172,7 @@ func BaseOptionsWithExperimental() []utils.HostOption { // testing environment. func TestLoginOptions() []utils.HostOption { return Opts( - AddKeyOption("e2e.key", "e2e.crt"), + AddKeyOption(filepath.Join(NotationE2ELocalKeysDir, "e2e.key"), filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), AddTrustStoreOption("e2e", filepath.Join(NotationE2ELocalKeysDir, "e2e.crt")), AddTrustPolicyOption("trustpolicy.json"), AddConfigJsonOption("pass_credential_helper_config.json"), @@ -193,9 +202,9 @@ func AuthOption(username, password string) utils.HostOption { // AddKeyOption adds the test signingkeys.json, key and cert files to // the notation directory. -func AddKeyOption(keyName, certName string) utils.HostOption { +func AddKeyOption(keyPath, certPath string) utils.HostOption { return func(vhost *utils.VirtualHost) error { - return AddKeyPairs(vhost.AbsolutePath(NotationDirName), keyName, certName) + return AddKeyPairs(vhost.AbsolutePath(NotationDirName), keyPath, certPath) } } diff --git a/test/e2e/internal/notation/key.go b/test/e2e/internal/notation/key.go index d05003039..fd11a3852 100644 --- a/test/e2e/internal/notation/key.go +++ b/test/e2e/internal/notation/key.go @@ -53,35 +53,37 @@ type SigningKeys struct { // AddKeyPairs creates the signingkeys.json file and the localkeys directory // with e2e.key and e2e.crt -func AddKeyPairs(dir, keyName, certName string) error { +func AddKeyPairs(destNotationConfigDir, srcKeyPath, srcCertPath string) error { + keyName := filepath.Base(srcKeyPath) + certName := filepath.Base(srcCertPath) // create signingkeys.json files if err := saveJSON( - generateSigningKeys(dir), - filepath.Join(dir, SigningKeysFileName)); err != nil { + generateSigningKeys(destNotationConfigDir, keyName, certName), + filepath.Join(destNotationConfigDir, SigningKeysFileName)); err != nil { return err } // create localkeys directory - localKeysDir := filepath.Join(dir, LocalKeysDirName) + localKeysDir := filepath.Join(destNotationConfigDir, LocalKeysDirName) os.MkdirAll(localKeysDir, 0700) // copy key and cert files - if err := copyFile(filepath.Join(NotationE2ELocalKeysDir, keyName), filepath.Join(localKeysDir, "e2e.key")); err != nil { + if err := copyFile(srcKeyPath, filepath.Join(localKeysDir, keyName)); err != nil { return err } - return copyFile(filepath.Join(NotationE2ELocalKeysDir, certName), filepath.Join(localKeysDir, "e2e.crt")) + return copyFile(srcCertPath, filepath.Join(localKeysDir, certName)) } // generateSigningKeys generates the signingkeys.json for notation. -func generateSigningKeys(dir string) *SigningKeys { +func generateSigningKeys(dir, keyName, certName string) *SigningKeys { return &SigningKeys{ Default: "e2e", Keys: []KeySuite{ { Name: "e2e", X509KeyPair: &X509KeyPair{ - KeyPath: filepath.Join(dir, "localkeys", "e2e.key"), - CertificatePath: filepath.Join(dir, "localkeys", "e2e.crt"), + KeyPath: filepath.Join(dir, "localkeys", keyName), + CertificatePath: filepath.Join(dir, "localkeys", certName), }, }, }, diff --git a/test/e2e/internal/utils/crl.go b/test/e2e/internal/utils/crl.go new file mode 100644 index 000000000..36bc8d0f8 --- /dev/null +++ b/test/e2e/internal/utils/crl.go @@ -0,0 +1,79 @@ +// Copyright The Notary Project 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. + +package utils + +import ( + "fmt" + "net/http" +) + +// LeafCRLRevoke sends http post request to http://localhost:10086/leaf/revoke +func LeafCRLRevoke() error { + url := "http://localhost:10086/leaf/revoke" + resp, err := http.Post(url, "application/json", nil) + if err != nil { + return err + } + defer resp.Body.Close() + fmt.Printf("CRL of leaf certificate revoked with status code: %d\n", resp.StatusCode) + return nil +} + +// LeafCRLUnrevoke sends http post request to http://localhost:10086/leaf/unrevoke +func LeafCRLUnrevoke() error { + url := "http://localhost:10086/leaf/unrevoke" + resp, err := http.Post(url, "application/json", nil) + if err != nil { + return err + } + defer resp.Body.Close() + fmt.Printf("CRL of leaf certificate unrevoked with status code: %d\n", resp.StatusCode) + return nil +} + +// LeafCRLExpired sends http post request to http://localhost:10086/leaf/expired +func LeafCRLExpired() error { + url := "http://localhost:10086/leaf/expired" + resp, err := http.Post(url, "application/json", nil) + if err != nil { + return err + } + defer resp.Body.Close() + fmt.Printf("CRL of leaf certificate expired with status code: %d\n", resp.StatusCode) + return nil +} + +// IntermediateCRLRevoke sends http post request to http://localhost:10086/intermediate/revoke +func IntermediateCRLRevoke() error { + url := "http://localhost:10086/intermediate/revoke" + resp, err := http.Post(url, "application/json", nil) + if err != nil { + return nil + } + defer resp.Body.Close() + fmt.Printf("CRL of intermediate certificate revoked with status code: %d\n", resp.StatusCode) + return nil +} + +// IntermediateCRLUnrevoke sends http post request to http://localhost:10086/intermediate/unrevoke +func IntermediateCRLUnrevoke() error { + url := "http://localhost:10086/intermediate/unrevoke" + resp, err := http.Post(url, "application/json", nil) + if err != nil { + return nil + } + defer resp.Body.Close() + fmt.Printf("CRL of intermediate certificate unrevoked with status code: %d\n", resp.StatusCode) + return nil +} diff --git a/test/e2e/internal/utils/host.go b/test/e2e/internal/utils/host.go index f5a336856..605032767 100644 --- a/test/e2e/internal/utils/host.go +++ b/test/e2e/internal/utils/host.go @@ -38,7 +38,7 @@ func NewVirtualHost(binPath string, options ...HostOption) (*VirtualHost, error) vhost.userDir = ginkgo.GinkgoT().TempDir() // set user dir environment variables - vhost.UpdateEnv(UserConfigEnv(vhost.userDir)) + vhost.UpdateEnv(UserEnv(vhost.userDir)) // set options vhost.SetOption(options...) @@ -77,11 +77,13 @@ func (h *VirtualHost) SetOption(options ...HostOption) { // HostOption is a function to set the host configuration. type HostOption func(vhost *VirtualHost) error -// UserConfigEnv creates environment variable for changing -// user config dir (By setting $XDG_CONFIG_HOME). -func UserConfigEnv(dir string) map[string]string { +// UserEnv creates environment variable for changing +// user config dir by setting $XDG_CONFIG_HOME and user cache dir by +// setting $NOTATION_CACHE. +func UserEnv(dir string) map[string]string { // create and set user dir for linux return map[string]string{ "XDG_CONFIG_HOME": dir, + "NOTATION_CACHE": filepath.Join(dir, ".cache"), } } diff --git a/test/e2e/run.sh b/test/e2e/run.sh index 1d3559788..4a8e28f05 100755 --- a/test/e2e/run.sh +++ b/test/e2e/run.sh @@ -69,7 +69,7 @@ if [ ! -f "$NOTATION_E2E_OLD_BINARY_PATH" ]; then fi # install dependency -go install -mod=mod github.com/onsi/ginkgo/v2/ginkgo@v2.11.0 +go install -mod=mod github.com/onsi/ginkgo/v2/ginkgo@v2.21.0 # build e2e plugin and tar.gz PLUGIN_NAME=notation-e2e-plugin @@ -95,9 +95,16 @@ esac setup_registry +# run the CRL server in the background +python3 ./scripts/crl_server.py & +CRL_SERVER_PID=$! + # defer cleanup registry function cleanup { + echo "Cleaning up..." cleanup_registry + echo "Stopping CRL server..." + kill $CRL_SERVER_PID } trap cleanup EXIT @@ -111,4 +118,4 @@ export NOTATION_E2E_PLUGIN_TAR_GZ_PATH=$CWD/plugin/bin/$PLUGIN_NAME.tar.gz export NOTATION_E2E_MALICIOUS_PLUGIN_ARCHIVE_PATH=$CWD/testdata/malicious-plugin # run tests -ginkgo -r -p -v +ginkgo -r -p -v \ No newline at end of file diff --git a/test/e2e/scripts/crl_server.py b/test/e2e/scripts/crl_server.py new file mode 100644 index 000000000..dea91d699 --- /dev/null +++ b/test/e2e/scripts/crl_server.py @@ -0,0 +1,81 @@ +# Copyright The Notary Project 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. + +import http.server +import socketserver +import os + +PORT = 10086 +DATA_DIR = './testdata/config/crl' +leaf_crl = 'leaf.crl' +intermediate_crl = 'intermediate.crl' + + +class CRLRequestHandler(http.server.SimpleHTTPRequestHandler): + def do_GET(self): + global leaf_crl + global intermediate_crl + if self.path == '/leaf.crl': + file_path = os.path.join(DATA_DIR, leaf_crl) + self.crl_response(file_path) + elif self.path == '/intermediate.crl': + file_path = os.path.join(DATA_DIR, intermediate_crl) + self.crl_response(file_path) + else: + self.send_error(404, 'Not Found') + + def crl_response(self, file_path): + if os.path.exists(file_path): + self.send_response(200) + self.send_header('Content-Type', 'application/pkix-crl') + self.end_headers() + with open(file_path, 'rb') as f: + self.wfile.write(f.read()) + else: + self.send_error(404, 'File Not Found') + + def do_POST(self): + global leaf_crl + global intermediate_crl + if self.path == '/leaf/revoke': + leaf_crl = 'leaf_revoked.crl' + self.post_response() + elif self.path == '/leaf/unrevoke': + leaf_crl = 'leaf.crl' + self.post_response() + elif self.path == '/leaf/expired': + leaf_crl = 'leaf_expired.crl' + self.post_response() + elif self.path == '/intermediate/revoke': + intermediate_crl = 'intermediate_revoked.crl' + self.post_response() + elif self.path == '/intermediate/unrevoke': + intermediate_crl = 'intermediate.crl' + self.post_response() + else: + self.send_error(404, 'Not Found') + + def post_response(self): + self.send_response(201) + self.end_headers() + self.wfile.write(b'ok') + +class ReusableTCPServer(socketserver.TCPServer): + allow_reuse_address = True + +with ReusableTCPServer(('', PORT), CRLRequestHandler) as httpd: + print(f"Serving at port {PORT}") + try: + httpd.serve_forever() + finally: + httpd.server_close() \ No newline at end of file diff --git a/test/e2e/scripts/gen_crl_testing_certs.sh b/test/e2e/scripts/gen_crl_testing_certs.sh new file mode 100755 index 000000000..59d3a1457 --- /dev/null +++ b/test/e2e/scripts/gen_crl_testing_certs.sh @@ -0,0 +1,231 @@ +#!/bin/bash -ex +# Copyright The Notary Project 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. + +# This file include the script to generate testing certificates for CRL testing. +# The generated files are: +# - certchain_with_crl.pem: the fullchain file that includes the leaf +# certificate with CRL, intermediate certificate with invalid OCSP and valid +# CRL, and the root certificate. +# - leaf.crl: the CRL file that includes the revoked leaf certificate. +# - leaf.key: the private key of the leaf certificate. +# - leaf_revoked.crl: the CRL file that includes the revoked leaf certificate. +# - intermediate.crl: the CRL file that includes the intermediate certificate. +# - intermediate_revoked.crl: the CRL file that includes the revoked intermediate +# - root.crt: the root certificate. +# +# Note: The script will not run in the pipeline, but we need to keep it for +# future maintenance because generating those test certificates with CRL is not +# easy. + +# Create root CA configuration file +cat > root.cnf < demoCA/serial +echo '1002' > demoCA/crlnumber + +# Generate root private key +openssl genrsa -out root.key 2048 + +# Generate self-signed root certificate with extensions +openssl req -x509 -new -key root.key -sha256 -days 36500 -out root.crt \ + -config root.cnf -extensions v3_ca + +# Update intermediate.cnf to include [ca] and [CA_default] sections +cat > intermediate.cnf < intermediateCA/serial +echo '1000' > intermediateCA/crlnumber + +# Generate intermediate private key +openssl genrsa -out intermediate.key 2048 + +# Generate intermediate CSR +openssl req -new -key intermediate.key -out intermediate.csr -config intermediate.cnf + +# Sign intermediate certificate with root CA +openssl ca -config root.cnf -in intermediate.csr -out intermediate.crt -batch -extensions v3_intermediate_ca -extfile intermediate.cnf -notext + +# Update leaf.cnf to remove OCSP server +cat > leaf.cnf < certchain_with_crl.pem + +rm -rf leaf.csr leaf.crt leaf.cnf root.srl root.cnf root.key root.crl demoCA intermediate.csr intermediate.cnf intermediate.key intermediate.crt intermediateCA diff --git a/test/e2e/suite/plugin/install.go b/test/e2e/suite/plugin/install.go index 363801d44..746babf6a 100644 --- a/test/e2e/suite/plugin/install.go +++ b/test/e2e/suite/plugin/install.go @@ -154,8 +154,8 @@ var _ = Describe("notation plugin install", func() { It("with invalid plugin URL", func() { Host(nil, func(notation *utils.ExecOpts, _ *Artifact, vhost *utils.VirtualHost) { - notation.ExpectFailure().Exec("plugin", "install", "--url", "https://invalid", "--sha256sum", "abcd"). - MatchErrKeyWords("failed to download plugin from URL https://invalid") + notation.ExpectFailure().Exec("plugin", "install", "--url", "https://invalid.test", "--sha256sum", "abcd"). + MatchErrKeyWords("failed to download plugin from URL https://invalid.test") }) }) }) diff --git a/test/e2e/suite/scenario/crl.go b/test/e2e/suite/scenario/crl.go new file mode 100644 index 000000000..0c09850ba --- /dev/null +++ b/test/e2e/suite/scenario/crl.go @@ -0,0 +1,315 @@ +// Copyright The Notary Project 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. + +package scenario_test + +import ( + "context" + "net/http" + "os" + + crlcore "github.com/notaryproject/notation-core-go/revocation/crl" + "github.com/notaryproject/notation-go/verifier/crl" + . "github.com/notaryproject/notation/test/e2e/internal/notation" + "github.com/notaryproject/notation/test/e2e/internal/utils" + . "github.com/notaryproject/notation/test/e2e/suite/common" + . "github.com/onsi/ginkgo/v2" +) + +var _ = Describe("notation CRL revocation check", Serial, func() { + It("successfully completed with cache", func() { + Host(CRLOptions(), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { + notation.Exec("sign", artifact.ReferenceWithDigest()). + MatchKeyWords(SignSuccessfully) + + utils.LeafCRLUnrevoke() + utils.IntermediateCRLUnrevoke() + + // verify without cache + notation.Exec("verify", artifact.ReferenceWithDigest(), "-d"). + MatchKeyWords( + VerifySuccessfully, + ). + MatchErrKeyWords( + "CRL file cache miss", + "Retrieving crl bundle from file cache with key", + "Storing crl bundle to file cache with key", + "OCSP check failed with unknown error and fallback to CRL check for certificate #2", + ). + NoMatchErrKeyWords( + "is revoked", + ) + + // verify with cache + notation.Exec("verify", artifact.ReferenceWithDigest(), "-d"). + MatchKeyWords( + VerifySuccessfully, + ). + MatchErrKeyWords( + "Retrieving crl bundle from file cache with key", + "OCSP check failed with unknown error and fallback to CRL check for certificate #2", + ). + NoMatchErrKeyWords( + "CRL file cache miss", + "Storing crl bundle to file cache with key", + "is revoked", + ) + }) + }) + + It("failed with revoked leaf certificate", func() { + Host(CRLOptions(), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { + notation.Exec("sign", artifact.ReferenceWithDigest()). + MatchKeyWords(SignSuccessfully) + + utils.LeafCRLRevoke() + utils.IntermediateCRLUnrevoke() + + // verify without cache + notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-d"). + MatchErrKeyWords( + VerifyFailed, + "CRL file cache miss", + "Retrieving crl bundle from file cache with key", + "Storing crl bundle to file cache with key", + "is revoked", + "OCSP check failed with unknown error and fallback to CRL check for certificate #2", + ) + + // verify with cache + notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-d"). + MatchErrKeyWords( + VerifyFailed, + "Retrieving crl bundle from file cache with key", + "is revoked", + "OCSP check failed with unknown error and fallback to CRL check for certificate #2", + ). + NoMatchErrKeyWords( + "CRL file cache miss", + "Storing crl bundle to file cache with key", + ) + }) + }) + + It("failed with revoked intermediate certificate", func() { + Host(CRLOptions(), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { + notation.Exec("sign", artifact.ReferenceWithDigest()). + MatchKeyWords(SignSuccessfully) + + utils.LeafCRLUnrevoke() + utils.IntermediateCRLRevoke() + + // verify without cache + notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-d"). + MatchErrKeyWords( + VerifyFailed, + "CRL file cache miss", + "Retrieving crl bundle from file cache with key", + "Storing crl bundle to file cache with key", + "is revoked", + "OCSP check failed with unknown error and fallback to CRL check for certificate #2", + ) + + // verify with cache + notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-d"). + MatchErrKeyWords( + VerifyFailed, + "Retrieving crl bundle from file cache with key", + "is revoked", + "OCSP check failed with unknown error and fallback to CRL check for certificate #2", + ). + NoMatchErrKeyWords( + "CRL file cache miss", + "Storing crl bundle to file cache with key", + ) + }) + }) + + It("successfully completed with cache creation error in warning message", func() { + Host(CRLOptions(), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { + notation.Exec("sign", artifact.ReferenceWithDigest()). + MatchKeyWords(SignSuccessfully) + + utils.LeafCRLUnrevoke() + utils.IntermediateCRLUnrevoke() + + if err := os.MkdirAll(vhost.AbsolutePath(".cache"), 0500); err != nil { + Fail(err.Error()) + } + defer os.Chmod(vhost.AbsolutePath(".cache"), 0700) + + // verify without cache + notation.Exec("verify", artifact.ReferenceWithDigest(), "-d"). + MatchKeyWords( + VerifySuccessfully, + ). + MatchErrKeyWords( + "Warning: failed to create crl file cache", + "OCSP check failed with unknown error and fallback to CRL check for certificate #2", + `"GET" "http://localhost:10086/intermediate.crl"`, + `"GET" "http://localhost:10086/leaf.crl"`, + ). + NoMatchErrKeyWords( + "is revoked", + ) + }) + }) + + It("failed with revoked leaf certificate and cache creation error in warning message", func() { + Host(CRLOptions(), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { + notation.Exec("sign", artifact.ReferenceWithDigest()). + MatchKeyWords(SignSuccessfully) + + utils.LeafCRLRevoke() + utils.IntermediateCRLUnrevoke() + + if err := os.MkdirAll(vhost.AbsolutePath(".cache"), 0500); err != nil { + Fail(err.Error()) + } + defer os.Chmod(vhost.AbsolutePath(".cache"), 0700) + + // verify without cache + notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-d"). + MatchErrKeyWords( + VerifyFailed, + "Warning: failed to create crl file cache", + "OCSP check failed with unknown error and fallback to CRL check for certificate #2", + `"GET" "http://localhost:10086/intermediate.crl"`, + `"GET" "http://localhost:10086/leaf.crl"`, + "is revoked", + ) + }) + }) + + It("successfully completed with cache get and set error in debug log", func() { + Host(CRLOptions(), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { + notation.Exec("sign", artifact.ReferenceWithDigest()). + MatchKeyWords(SignSuccessfully) + + utils.LeafCRLUnrevoke() + utils.IntermediateCRLUnrevoke() + + // verify without cache + notation.Exec("verify", artifact.ReferenceWithDigest(), "-d"). + MatchKeyWords( + VerifySuccessfully, + ). + MatchErrKeyWords( + "CRL file cache miss", + "Retrieving crl bundle from file cache with key", + "Storing crl bundle to file cache with key", + "OCSP check failed with unknown error and fallback to CRL check for certificate #2", + ). + NoMatchErrKeyWords( + "is revoked", + ) + + utils.LeafCRLRevoke() + if err := os.Chmod(vhost.AbsolutePath(".cache", "crl"), 0000); err != nil { + Fail(err.Error()) + } + defer os.Chmod(vhost.AbsolutePath(".cache", "crl"), 0700) + + // verify with cache error + notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-d"). + MatchErrKeyWords( + VerifyFailed, + "failed to get crl bundle from file cache with key", + "failed to store crl bundle in file cache", + "/.cache/crl/eaf8bbfe35f6c2c8b136081de9a994f9515752b2e30b9a6889ae3128ea97656c: permission denied", + "OCSP check failed with unknown error and fallback to CRL check for certificate #2", + `"GET" "http://localhost:10086/intermediate.crl"`, + `"GET" "http://localhost:10086/leaf.crl"`, + "is revoked", + ) + }) + }) + + It("succesfully completed with the crl in the cache expired and a roundtrip", func() { + Host(CRLOptions(), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { + ctx := context.Background() + notation.Exec("sign", artifact.ReferenceWithDigest()). + MatchKeyWords(SignSuccessfully) + + utils.LeafCRLExpired() + // write expired CRL cache + fetcher, err := crlcore.NewHTTPFetcher(http.DefaultClient) + if err != nil { + Fail(err.Error()) + } + fetcher.Cache, err = crl.NewFileCache(vhost.AbsolutePath(".cache", "crl")) + if err != nil { + Fail(err.Error()) + } + _, err = fetcher.Fetch(ctx, "http://localhost:10086/leaf.crl") + if err != nil { + Fail(err.Error()) + } + + utils.LeafCRLUnrevoke() + utils.IntermediateCRLUnrevoke() + // verify without cache + notation.Exec("verify", artifact.ReferenceWithDigest(), "-d"). + MatchKeyWords( + VerifySuccessfully, + ). + MatchErrKeyWords( + "CRL bundle retrieved from file cache has expired at 2023-12-25", + `"GET" "http://localhost:10086/leaf.crl"`, + "Retrieving crl bundle from file cache with key", + "Storing crl bundle to file cache with key", + "OCSP check failed with unknown error and fallback to CRL check for certificate #2", + ). + NoMatchErrKeyWords( + "is revoked", + ) + }) + }) + + It("failed with crl in the cache expired and a roundtrip to download a revoked crl", func() { + Host(CRLOptions(), func(notation *utils.ExecOpts, artifact *Artifact, vhost *utils.VirtualHost) { + ctx := context.Background() + notation.Exec("sign", artifact.ReferenceWithDigest()). + MatchKeyWords(SignSuccessfully) + + utils.LeafCRLExpired() + // write expired CRL cache + fetcher, err := crlcore.NewHTTPFetcher(http.DefaultClient) + if err != nil { + Fail(err.Error()) + } + fetcher.Cache, err = crl.NewFileCache(vhost.AbsolutePath(".cache", "crl")) + if err != nil { + Fail(err.Error()) + } + _, err = fetcher.Fetch(ctx, "http://localhost:10086/leaf.crl") + if err != nil { + Fail(err.Error()) + } + + utils.LeafCRLRevoke() + utils.IntermediateCRLUnrevoke() + // verify without cache + notation.ExpectFailure().Exec("verify", artifact.ReferenceWithDigest(), "-d"). + MatchErrKeyWords( + VerifyFailed, + "CRL bundle retrieved from file cache has expired at 2023-12-25", + `"GET" "http://localhost:10086/leaf.crl"`, + "Retrieving crl bundle from file cache with key", + "Storing crl bundle to file cache with key", + "OCSP check failed with unknown error and fallback to CRL check for certificate #2", + "is revoked", + ) + }) + }) +}) diff --git a/test/e2e/suite/trustpolicy/trust_store.go b/test/e2e/suite/trustpolicy/trust_store.go index 736917493..d01a2fae0 100644 --- a/test/e2e/suite/trustpolicy/trust_store.go +++ b/test/e2e/suite/trustpolicy/trust_store.go @@ -70,7 +70,7 @@ var _ = Describe("notation trust policy trust store test", func() { It("multiple trust stores", func() { Host(nil, func(notation *utils.ExecOpts, artifact1 *Artifact, vhost *utils.VirtualHost) { // artifact1 signed with new_e2e.crt - OldNotation(AuthOption("", ""), AddKeyOption("e2e.key", "new_e2e.crt")). + OldNotation(AuthOption("", ""), AddKeyOption(filepath.Join(NotationE2ELocalKeysDir, "e2e.key"), filepath.Join(NotationE2ELocalKeysDir, "new_e2e.crt"))). Exec("sign", artifact1.ReferenceWithDigest(), "-v"). MatchKeyWords(SignSuccessfully) diff --git a/test/e2e/suite/trustpolicy/trusted_identity.go b/test/e2e/suite/trustpolicy/trusted_identity.go index 80ca06214..e37637340 100644 --- a/test/e2e/suite/trustpolicy/trusted_identity.go +++ b/test/e2e/suite/trustpolicy/trusted_identity.go @@ -77,7 +77,7 @@ var _ = Describe("notation trust policy trusted identity test", func() { It("with multiple trusted identity", func() { Host(nil, func(notation *utils.ExecOpts, artifact1 *Artifact, vhost *utils.VirtualHost) { // artifact1 signed with new_e2e.crt - OldNotation(AuthOption("", ""), AddKeyOption("e2e.key", "new_e2e.crt")). + OldNotation(AuthOption("", ""), AddKeyOption(filepath.Join(NotationE2ELocalKeysDir, "e2e.key"), filepath.Join(NotationE2ELocalKeysDir, "new_e2e.crt"))). Exec("sign", artifact1.ReferenceWithDigest(), "-v"). MatchKeyWords(SignSuccessfully) diff --git a/test/e2e/testdata/config/crl/certchain_with_crl.pem b/test/e2e/testdata/config/crl/certchain_with_crl.pem new file mode 100644 index 000000000..e9acff468 --- /dev/null +++ b/test/e2e/testdata/config/crl/certchain_with_crl.pem @@ -0,0 +1,76 @@ +-----BEGIN CERTIFICATE----- +MIID6DCCAtCgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwbjELMAkGA1UEBhMCVVMx +DjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5MRUwEwYDVQQKDAxPcmdhbml6 +YXRpb24xEDAOBgNVBAsMB09yZ1VuaXQxFzAVBgNVBAMMDkludGVybWVkaWF0ZUNB +MCAXDTI0MTExMTA1MzA1MVoYDzIxMjQxMDE4MDUzMDUxWjBoMQswCQYDVQQGEwJV +UzEOMAwGA1UECAwFU3RhdGUxDTALBgNVBAcMBENpdHkxFTATBgNVBAoMDE9yZ2Fu +aXphdGlvbjEQMA4GA1UECwwHT3JnVW5pdDERMA8GA1UEAwwITGVhZkNlcnQwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJEFmIvqnUe3iwJ7HFrgCAbnEf +dGG/8yRuPWxTDkeHYQl8pMag5ZeytvTJalO8gba3oK6QRxPYhlzM8i7Ik/R6FXf6 +ZhTGzO8rIRe4Sc5ibO5TJeyfxciwtWnK6RtQwDZu4fb4tNmqUg6ZqqPnq1K8ieZf +ROVWs9zglJj37SoLY+Caxj2VOm8/aB/2mF46kqGUCb56RRk1q87x8U8wOSNW6ono +XVu6zBqLNwx2RcqKdKApYtaitwKz9cuWPTDrUe4OfCWDSYiB48YY0jDcV27ed+K8 +VKy6jgUAsKkzPIf3RbEBuMD2mUybC0o0D9t0h/R7h0f8zXgkxkP4KXwmPRPvAgMB +AAGjgZMwgZAwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwMAYDVR0fBCkw +JzAloCOgIYYfaHR0cDovL2xvY2FsaG9zdDoxMDA4Ni9sZWFmLmNybDAdBgNVHQ4E +FgQU/kBZC5X59h9/r/npEe2JYotqXbYwHwYDVR0jBBgwFoAUbCnIbIecdpvXQrcQ +okSpumRjw+IwDQYJKoZIhvcNAQELBQADggEBAMlLKGXxQ9ENqG2+h83toBJvOmPs +iEOLIcgaIvbvrnIPjZNDDiLar5hX1LPhnj5yvjy7XzHGfmFJg2lP1wYpqfCXdX/4 +P0Yfl1iy6ELf2z+QzKSd0njnc3kIrb7S1yLmrtkFY75vBxnJlUUA7q05Qy95jcDZ +fIuH8kFKaLVoXbbkonSzz01tW7PNsYqrZXmXfacrLLH66B3rscJFsu0oruy25Opj ++yS+ySlp1aEdWURI6WBd/8P6wEvbqZP+wXJVsEEp21mX050J1ycmDOPExcYetUSN +rglPBogxoOZqU3IGx7SVq5ze8l1hepKULMjACqHgu9klGos64E4f+LMGcFs= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEszCCA5ugAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwZjELMAkGA1UEBhMCVVMx +DjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5MRUwEwYDVQQKDAxPcmdhbml6 +YXRpb24xEDAOBgNVBAsMB09yZ1VuaXQxDzANBgNVBAMMBlJvb3RDQTAgFw0yNDEx +MTEwNTMwNTFaGA8yMTI0MTAxODA1MzA1MVowbjELMAkGA1UEBhMCVVMxDjAMBgNV +BAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5MRUwEwYDVQQKDAxPcmdhbml6YXRpb24x +EDAOBgNVBAsMB09yZ1VuaXQxFzAVBgNVBAMMDkludGVybWVkaWF0ZUNBMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2O2N81+L0bel+CkBWeBBMh8Z/BRs +NBAsHlake1UlnJ12z0slRaSQfBK0cAHeRDAqBO/VkTF7bwsRR/Ypu8FAc64ayTUq +wWjNq5MU0HW80CmjyrYeXIC/BfzIOZC50IoGbQKrfbeJyP5qDWc41mOeZdyJbXtA +si17Ilwj/Q+seA58HSyZU1Iop4NDktsQpOr+XE9IWauu8xGy+ScQ92/3p4tNXYGI +7mZ1d/gD0KXs9sQ2klhmaH5OvubCQUGi7OX/yQWtJOmLUW7t7Ha4gmQYSG8jxOeT +vlt9Gv0OdiSA/ZvGn7qJfZ9NrjVQtw5j3sAxepNmoe3oh8lMZ/0ACevtcwIDAQAB +o4IBXzCCAVswEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFGwpyGyHnHab10K3EKJEqbpkY8PiMIGjBgNVHSMEgZswgZiAFKytgq/A +6Ar+6PeejGL+3SunIW8QoWqkaDBmMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFU3Rh +dGUxDTALBgNVBAcMBENpdHkxFTATBgNVBAoMDE9yZ2FuaXphdGlvbjEQMA4GA1UE +CwwHT3JnVW5pdDEPMA0GA1UEAwwGUm9vdENBghQzVmwrGMBu11fJ0xRPTvXlgfJL +MzA4BgNVHR8EMTAvMC2gK6AphidodHRwOi8vbG9jYWxob3N0OjEwMDg2L2ludGVy +bWVkaWF0ZS5jcmwwNgYIKwYBBQUHAQEEKjAoMCYGCCsGAQUFBzABhhpodHRwOi8v +bG9jYWxob3N0LnRlc3Qvb2NzcDANBgkqhkiG9w0BAQsFAAOCAQEAHu/rr14Eo/GT +Hhcj10SL4wjXkKAMsXva6oUrD/z8nqRiFDy9pIavvQwdfeAVris9QcV+Z48Vl28w +OLH8AMahfNsQhjKxLB2ME1lJ3F6kMbnd4za4ZeeXykd5eswGD0PKnJb5aRYSHc5+ +OtS072Nt9qiyKtU++s0GAStx+0KnQ3r/3uTGpFgZNH+UKI5GxehW3v0iBlB/bZIP +YRCnhY82aDnR7ZEVXARyXDFX7obw4IBS3IqjnKndYtKwQOPKUzDTVPUhs7e9oetI +Y1YlZH3FJ9lBV+MxEEsmEu+TPhy+qRaqr0QHz/7tebdsMEB+v2ile7Y704AJVGnh +6g0n6AsHBQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIERjCCAy6gAwIBAgIUM1ZsKxjAbtdXydMUT0715YHySzMwDQYJKoZIhvcNAQEL +BQAwZjELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xEDAOBgNVBAsMB09yZ1VuaXQxDzANBgNV +BAMMBlJvb3RDQTAgFw0yNDExMTEwNTMwNTFaGA8yMTI0MTAxODA1MzA1MVowZjEL +MAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5MRUwEwYD +VQQKDAxPcmdhbml6YXRpb24xEDAOBgNVBAsMB09yZ1VuaXQxDzANBgNVBAMMBlJv +b3RDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ46Go+Lo6yva6ZY +5N8UCkSt1nxnSfvwmSd358owqi6jO+hwv0jvrxSjzQyfke+tlfm3U/DLisyFOxny +RAYbhuM8PN5Fp3W5/og7nl5grhBQC3sps26QPVO7IvZBgfbkRCnC/LMdx4eeiUGZ +wc254KAh4/qYHMK+iqkAYg8DqLiWEhi1knYjNEWHCbeCnpj2vC+VhFNeTamR8bPd +U03xrZV9v/gZc3F+Ivwlxwb0QxDbXrYPc/5lixkQMYKQt8jzDl14TWeDbU589P31 +E3Y0CJSSDKczHXhh/cpN4lEGKircQynOdEEKf2z7FhvNVPH3tRh4ISmtl1lKrDk8 +drLBhKECAwEAAaOB6TCB5jAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUrK2Cr8DoCv7o956MYv7dK6chbxAwgaMGA1UdIwSBmzCBmIAU +rK2Cr8DoCv7o956MYv7dK6chbxChaqRoMGYxCzAJBgNVBAYTAlVTMQ4wDAYDVQQI +DAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UECgwMT3JnYW5pemF0aW9uMRAw +DgYDVQQLDAdPcmdVbml0MQ8wDQYDVQQDDAZSb290Q0GCFDNWbCsYwG7XV8nTFE9O +9eWB8kszMA0GCSqGSIb3DQEBCwUAA4IBAQArRoUqzUCjNaZWwtz8hhJYpKxrXK/M +MFsfj4FF6F9UgEZh+j9lAhQ1K9IoDKFKzcur3fO6dAPoWJm0P1vthCLMtFgeP7mm +d1H4BdtIiqWYTPtee8JgG2Mw8QdWaafb6WAdCOlAM01CWgR7aeL1cYDrarQLLXQ0 +KmbqNt4sPyv5orf/FjgHN8tUH2IKJo1NTrICmdP63Bal5kehh6lQYKytpmkqwcpA +HcagiCEvx5xWsZ/Mgnln8K8cg5xdV54bPssvxVPPKmLg/YzsqohjEiwsYoRBt4em +8Ul9fbaLugkoXtk7eCLpg7BJhhOOUZkiYjnmxZbkmVl7U79iFKrz73c5 +-----END CERTIFICATE----- diff --git a/test/e2e/testdata/config/crl/intermediate.crl b/test/e2e/testdata/config/crl/intermediate.crl new file mode 100644 index 0000000000000000000000000000000000000000..9f71b046c69f56b77ca115ba64fd56ba103d45e1 GIT binary patch literal 487 zcmXqLVtj1Sc!r6Q(SVnYQ>)FR?K>|cBR4C9L7E}A0Vf-CC<~h~Q)sXup8*eu!@JUsqI>4|xnRf#2;`FVx{27Dl4ZXR}^aA;m; zi6KAG9-s&_4_i=veu=ZAIIoe3ArKgt8XK4zMoI7+83F|i3@xAn3k(eO4CH}YWR+PY z48$5l)~sz>f8YhzzZc)<^(6hftG!$?UjXbDSrHZ%CME$UG>8b|}PU+^gKaWYEf981ez-nn2(RSipu` zIOfRs#9w4-%)hx0Wb}ky6gbTae7rt3wl2a{IH5spZ=2Mq9c@Z;RK%qk&oG9Dy-Qta z+c~YbGUnVyZI|cf@Bi2eKY6<3$>z)QR~7%|O%(3ry)nzUabwildX186-kFAT&OUCJ F002WYuaf`( literal 0 HcmV?d00001 diff --git a/test/e2e/testdata/config/crl/intermediate_revoked.crl b/test/e2e/testdata/config/crl/intermediate_revoked.crl new file mode 100644 index 0000000000000000000000000000000000000000..b0bd1b6e57585aeec0de9888fe2bf5f0d7a47f79 GIT binary patch literal 510 zcmXqLV*F*$_?U^2(SVnYQ>)FR?K>|cBR4C9L7E}A0Vf-CC<~h~Q)sXup8*eu!@JUsqI>4|xnRf#2;`FVx{27Dl4ZXR}^aA;m; zi6KAG9-s&_4_i=veu=ZAIIoe3ArKgt8XK4zMoI7+83F|i3@xAn20)XUm;{*6)GaVD z&@+$+nk1{tB4HrbAhKp{)A|E1xcHRR{;UO?P8gk-etN#^xtUWp zMny5CtlL-gv9RQ9z(u#e4+Q-_AKg>@tZ%hYzzf!s;tK^{1uHsieE-fS`{Lb7|2)FR?K>|cBR4C9L7pME0Vf-CC<~h~Q)sXup8*eu!@JUsqI>4|xnRf#2;`FVx{27Dl4ZXR}^aA;m; ziJ`cGC`g2vhtD&wB(*3vH6;^hoU@}iuaSu%5Ez&m8<-kJN$?vP0tE~VEuaDm3=H%P zuR4~@o*DRdUNdQPKkoUXHQPOn%pADeyQWi?4_*px7}a%BO}R!{Q>cWBCgwZFR?DA=47wv6J{wvCQgJ+H{_xBUAx@8_FUv9Vj$AAWyZJKOtGW}UE9C-*&>&Xz?d_i3}gnwnL~ L*vIipYLhzv5ofQM literal 0 HcmV?d00001 diff --git a/test/e2e/testdata/config/crl/leaf.key b/test/e2e/testdata/config/crl/leaf.key new file mode 100644 index 000000000..f986032f6 --- /dev/null +++ b/test/e2e/testdata/config/crl/leaf.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDJEFmIvqnUe3iw +J7HFrgCAbnEfdGG/8yRuPWxTDkeHYQl8pMag5ZeytvTJalO8gba3oK6QRxPYhlzM +8i7Ik/R6FXf6ZhTGzO8rIRe4Sc5ibO5TJeyfxciwtWnK6RtQwDZu4fb4tNmqUg6Z +qqPnq1K8ieZfROVWs9zglJj37SoLY+Caxj2VOm8/aB/2mF46kqGUCb56RRk1q87x +8U8wOSNW6onoXVu6zBqLNwx2RcqKdKApYtaitwKz9cuWPTDrUe4OfCWDSYiB48YY +0jDcV27ed+K8VKy6jgUAsKkzPIf3RbEBuMD2mUybC0o0D9t0h/R7h0f8zXgkxkP4 +KXwmPRPvAgMBAAECggEAG511wUzNPuRNzAVKVX1pDzqxVHcjx9J+we9eFeg5AHSI +8jLKydAkLi+9CNQ5jQNUflMA4XW0WZcBXvAHvJUUTReZbhhK0+UowjKI3fmxHB6c +UGo93icy7GZLkfVt7YDWLmjgXAIG1ULw7hUcREDpG6u7ACxnwqlFyx34W9uZ3Iy3 +AICABJISJdxs7SGceFgmDUQluCQGeSA7Ma7ye3wEb9TqGcCwJyA/Ny2D3y9lztjB +CE/obwKVbWLAMRwHo/K/bhNR1A/HjfZVcWfA4TPlmpTTtnJjN/wipzLPBDqA77YW +YbASsCRD7rroQDfB0/kAQKq463/ckGQafaugx/zsWQKBgQDtRAOlWh4Dji4Py8Qf +DBvEcYfR41xYlJ9tV1LN4mWip8fujYRK2nExglpb0s7VqqNEhb0YhK8soMnR7DYE +Whd7wGfkCw0RJRbkQcKs0ZgHJSQDH6C8QPLjFx8sJ8y1gdgF5KapF1SZ22dEiA4F +zRB6u2M2Mhq+O0WSRuU8qj271wKBgQDY8JFUeAJ3jdSH7MtC6cps+yinXucCdKa5 +ktw8XF5wIZNWpZAkSuDKSZ14Mmet5yTvP+hKsVzbXtfDw3Ay3he23dD8khEa4p87 +k/3hED9yO8ERcl7myjJwKFo1LbZLKiZXUbrjT/APqPHeqqWChSEdElI4vWYZq3Ho +hLhh6JolqQKBgQC5ehHfkTMlVGBtuLz4CF8skhO64CGwnX6D21+/0tvg64g/1CE2 +4szaByzb21AVd+9qAaJxKEhIeulKUX1oqYTyGNceTgVJAdWDWmT09GQEMeSVDIR6 +pcs5+zlDK1m8CmT1NmmShcp8/CceS72qi0P2rcZA96owS+V3FDTf17WqwQKBgQCU +mCNTsmguL8311LJdgPoQr/BmxFCDlBdnYTrNdY+SSs07OmiDE27p4IamABCYQ40+ ++41HLDmoYUx1YPcHQK0JquItPnfM7JGZI4Nwl8p9moRiSO7MMo9d16YCau9EiPh+ +MxNKBEUw1M3ncQexYTMPUK9VAhZfWtnJJK+PEN4VEQKBgQCqF68DEkLQPwZaf9WE +zMprwZZ6bUhQmwp1TqKgW7OcgHCDzbMnYGRMgChtg6hZkFyyQN9aFj0ki66XI449 +DSH8fb4mIkdL1pP4FkHClKyTYE6SbMYWhFEJrUdrZ4QwktUm/e68w/op2B7v2fup +q9wlKAeblH73bGWHEs6+QfMVLA== +-----END PRIVATE KEY----- diff --git a/test/e2e/testdata/config/crl/leaf_expired.crl b/test/e2e/testdata/config/crl/leaf_expired.crl new file mode 100644 index 0000000000000000000000000000000000000000..8e526887d7a681696a21a4ad4ffe4af2be009c63 GIT binary patch literal 493 zcmXqLVti@Pc!`OT(SVnYQ>)FR?K>|cBR4C9L7pME0Vf-CC<~h~Q)sXup8*eu!@JUsqI>4|xnRf#2;`FVx{27Dl4ZXR}^aA;m; ziJ`cGC`g2vhtD&wB(*3vH6;^hoU@}iuaU8#k&%gkg`ug5Q52MG3gs>^FwirQ2Wpg6 zW|1%uYY-{abB#0V(c5=>$*MioOilamh&P6TT_h{Q!otKPz<}l*<|al)hVq5wQOjSq z{xaIz@44ZR>C2>f-CFGzKLsaDd*uD4YUKp)itmAbE>Al?zPK86#VVKceO2!y9{b;2 zyX)3BhRD=DyJ#ekS^DSxf1A{$weL=Mi|+YTr)E;{|FtjwhaC6W&!x`s_&1#4Wn299 z|0UgM?P3Alh3_jlS4y_0Sq7!PVz`=hX2Z+#tg5xV+1^Rs0qYKJdA~pP=!}I|-6n<| zF=uJ0RlDcSe!FUDh)*qgh5;`9v=Uq^u)Z(s>G7a{5(Se13r*2HxD~dI5aP_ z#8BKo6ePmT!{?b-l3J9Tnvw}L&e>6%*T}>W2n0UCfdU4G7El2Lpbbn+ z0t{&C78n@l8OQ@olvQStFc51H$|wc_I0Q20*hQ$?n+5M{0Qt+SrHZ%paJOq zWNu<)Wa$5W-AmtacbMjx)yt~{cRydfWZlIQHJw+7|Cc!)erx_Y(e(C1o=>;FFId{^ zmQfXUX^AWE4Mt)OYz>z9{tU3&91AzY8du>V_MY? zze9!P$L;hN>KuD*z-^Js_P2EX literal 0 HcmV?d00001 diff --git a/test/e2e/testdata/config/crl/root.crt b/test/e2e/testdata/config/crl/root.crt new file mode 100644 index 000000000..a3008bfff --- /dev/null +++ b/test/e2e/testdata/config/crl/root.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIERjCCAy6gAwIBAgIUM1ZsKxjAbtdXydMUT0715YHySzMwDQYJKoZIhvcNAQEL +BQAwZjELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xEDAOBgNVBAsMB09yZ1VuaXQxDzANBgNV +BAMMBlJvb3RDQTAgFw0yNDExMTEwNTMwNTFaGA8yMTI0MTAxODA1MzA1MVowZjEL +MAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5MRUwEwYD +VQQKDAxPcmdhbml6YXRpb24xEDAOBgNVBAsMB09yZ1VuaXQxDzANBgNVBAMMBlJv +b3RDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ46Go+Lo6yva6ZY +5N8UCkSt1nxnSfvwmSd358owqi6jO+hwv0jvrxSjzQyfke+tlfm3U/DLisyFOxny +RAYbhuM8PN5Fp3W5/og7nl5grhBQC3sps26QPVO7IvZBgfbkRCnC/LMdx4eeiUGZ +wc254KAh4/qYHMK+iqkAYg8DqLiWEhi1knYjNEWHCbeCnpj2vC+VhFNeTamR8bPd +U03xrZV9v/gZc3F+Ivwlxwb0QxDbXrYPc/5lixkQMYKQt8jzDl14TWeDbU589P31 +E3Y0CJSSDKczHXhh/cpN4lEGKircQynOdEEKf2z7FhvNVPH3tRh4ISmtl1lKrDk8 +drLBhKECAwEAAaOB6TCB5jAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUrK2Cr8DoCv7o956MYv7dK6chbxAwgaMGA1UdIwSBmzCBmIAU +rK2Cr8DoCv7o956MYv7dK6chbxChaqRoMGYxCzAJBgNVBAYTAlVTMQ4wDAYDVQQI +DAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UECgwMT3JnYW5pemF0aW9uMRAw +DgYDVQQLDAdPcmdVbml0MQ8wDQYDVQQDDAZSb290Q0GCFDNWbCsYwG7XV8nTFE9O +9eWB8kszMA0GCSqGSIb3DQEBCwUAA4IBAQArRoUqzUCjNaZWwtz8hhJYpKxrXK/M +MFsfj4FF6F9UgEZh+j9lAhQ1K9IoDKFKzcur3fO6dAPoWJm0P1vthCLMtFgeP7mm +d1H4BdtIiqWYTPtee8JgG2Mw8QdWaafb6WAdCOlAM01CWgR7aeL1cYDrarQLLXQ0 +KmbqNt4sPyv5orf/FjgHN8tUH2IKJo1NTrICmdP63Bal5kehh6lQYKytpmkqwcpA +HcagiCEvx5xWsZ/Mgnln8K8cg5xdV54bPssvxVPPKmLg/YzsqohjEiwsYoRBt4em +8Ul9fbaLugkoXtk7eCLpg7BJhhOOUZkiYjnmxZbkmVl7U79iFKrz73c5 +-----END CERTIFICATE-----