From e6bc0e72083eb88a6fac83ab02da2e4f2189d3c9 Mon Sep 17 00:00:00 2001 From: Hua Liu <58683130+liuh-80@users.noreply.github.com> Date: Wed, 20 Nov 2024 09:01:53 +0800 Subject: [PATCH] Add cert CRL support. (#269) Add cert CRL support. #### Why I did it Support certificate revocation list. #### How I did it Download CRL and verify cert with CRL. #### How to verify it Manually test: I1119 06:45:56.678454 139 server.go:201] Created Server on localhost:50052, read-only: true I1119 06:45:56.678478 139 telemetry.go:465] Auth Modes: cert I1119 06:45:56.678495 139 telemetry.go:466] Starting RPC server on address: localhost:50052 I1119 06:45:56.678532 139 telemetry.go:469] GNMI Server started serving I1119 06:46:14.936024 139 clientCertAuth.go:183] Get Crl Urls for cert: [] I1119 06:46:14.936363 139 clientCertAuth.go:224] Cert does not contains and CRL distribution points I1119 06:46:14.936375 139 server.go:278] authenticate user , roles [role1] I1119 06:46:21.524943 139 clientCertAuth.go:183] Get Crl Urls for cert: [http://10.250.0.102:1234/crl] I1119 06:46:21.526022 139 clientCertAuth.go:93] SearchCrlCache not found cache for url: http://10.250.0.102:1234/crl I1119 06:46:21.526138 139 clientCertAuth.go:158] Download CRL start: http://10.250.0.102:1234/crl I1119 06:46:21.533821 139 clientCertAuth.go:176] Download CRL: http://10.250.0.102:1234/crl successed I1119 06:46:21.534318 139 clientCertAuth.go:66] CrlExpired expireTime: Wed Nov 20 06:46:21 2024, now: Tue Nov 19 06:46:21 2024 I1119 06:46:21.534337 139 clientCertAuth.go:211] CreateStaticCRLProvider add crl: http://10.250.0.102:1234/crl content: [...] I1119 06:46:21.535269 139 clientCertAuth.go:244] VerifyCertCrl peer certificate revoked: no unrevoked chains found: map[2:1] I1119 06:46:21.535289 139 clientCertAuth.go:149] [TELEMETRY-2] Failed to verify cert with CRL; rpc error: code = Unauthenticated desc = Peer certificate revoked Add new UT. #### Work item tracking Microsoft ADO (number only): 27146924 #### Which release branch to backport (provide reason below if selected) - [ ] 201811 - [ ] 201911 - [ ] 202006 - [ ] 202012 - [ ] 202106 - [ ] 202111 #### Description for the changelog Add cert CRL support. #### Link to config_db schema for YANG module changes #### A picture of a cute animal (not mandatory but encouraged) --- Makefile | 2 +- gnmi_server/clientCertAuth.go | 204 ++++++++++++++++++++++++++- gnmi_server/crl_test.go | 251 ++++++++++++++++++++++++++++++++++ gnmi_server/server.go | 3 +- gnmi_server/server_test.go | 10 +- telemetry/telemetry.go | 7 + testdata/crl/revokedInt.pem | 58 ++++++++ testdata/crl/test.crl | 11 ++ testdata/crl/unrevoked.pem | 58 ++++++++ 9 files changed, 596 insertions(+), 8 deletions(-) create mode 100644 gnmi_server/crl_test.go create mode 100644 testdata/crl/revokedInt.pem create mode 100644 testdata/crl/test.crl create mode 100644 testdata/crl/unrevoked.pem diff --git a/Makefile b/Makefile index 039fed73..817d853f 100644 --- a/Makefile +++ b/Makefile @@ -70,7 +70,7 @@ go-deps-clean: sonic-gnmi: $(GO_DEPS) # advancetls 1.0.0 release need following patch to build by go-1.19 -# patch -d vendor -p0 < patches/0002-Fix-advance-tls-build-with-go-119.patch + patch -d vendor -p0 < patches/0002-Fix-advance-tls-build-with-go-119.patch # build service first which depends on advancetls ifeq ($(CROSS_BUILD_ENVIRON),y) $(GO) build -o ${GOBIN}/telemetry -mod=vendor $(BLD_FLAGS) github.com/sonic-net/sonic-gnmi/telemetry diff --git a/gnmi_server/clientCertAuth.go b/gnmi_server/clientCertAuth.go index 9a4a9a4f..48fecf3c 100644 --- a/gnmi_server/clientCertAuth.go +++ b/gnmi_server/clientCertAuth.go @@ -1,6 +1,11 @@ package gnmi import ( + "crypto/tls" + "crypto/x509" + "io" + "net/http" + "time" "github.com/sonic-net/sonic-gnmi/common_utils" "github.com/sonic-net/sonic-gnmi/swsscommon" "github.com/golang/glog" @@ -9,9 +14,103 @@ import ( "google.golang.org/grpc/credentials" "google.golang.org/grpc/peer" "google.golang.org/grpc/status" + "google.golang.org/grpc/security/advancedtls" ) -func ClientCertAuthenAndAuthor(ctx context.Context, serviceConfigTableName string) (context.Context, error) { +const DEFAULT_CRL_EXPIRE_DURATION time.Duration = 24 * 60* 60 * time.Second + +type Crl struct { + thisUpdate time.Time + nextUpdate time.Time + crl []byte +} + +// CRL content cache +var CrlCache map[string]*Crl = nil + +// CRL content cache +var CrlDxpireDuration time.Duration = DEFAULT_CRL_EXPIRE_DURATION + +func InitCrlCache() { + if CrlCache == nil { + CrlCache = make(map[string]*Crl) + } +} + +func ReleaseCrlCache() { + for mapkey, _ := range(CrlCache) { + delete(CrlCache, mapkey) + } +} + +func AppendCrlToCache(url string, rawCRL []byte) { + crl := new(Crl) + crl.thisUpdate = time.Now() + crl.nextUpdate = time.Now() + crl.crl = rawCRL + + CrlCache[url] = crl +} + +func GetCrlExpireDuration() time.Duration { + return CrlDxpireDuration +} + +func SetCrlExpireDuration(duration time.Duration) { + CrlDxpireDuration = duration +} + +func CrlExpired(crl *Crl) bool { + now := time.Now() + expireTime := crl.thisUpdate.Add(GetCrlExpireDuration()) + glog.Infof("CrlExpired expireTime: %s, now: %s", expireTime.Format(time.ANSIC), now.Format(time.ANSIC)) + // CRL expiresion policy follow the policy of Get-CRLFreshness command in following doc: + // https://learn.microsoft.com/en-us/archive/blogs/russellt/get-crlfreshness + // The policy are: + // 1. CRL expired when current time is after CRL expiresion time, which defined in "Next CRL Publish" extension. + // Because CRL cached in memory, GNMI support OnDemand CRL referesh by restart GNMI service. + return now.After(expireTime) +} + +func CrlNeedUpdate(crl *Crl) bool { + now := time.Now() + glog.Infof("CrlNeedUpdate nextUpdate: %s, now: %s", crl.nextUpdate.Format(time.ANSIC), now.Format(time.ANSIC)) + return now.After(crl.nextUpdate) +} + +func RemoveExpiredCrl() { + for mapkey, crl := range(CrlCache) { + if CrlExpired(crl) { + glog.Infof("RemoveExpiredCrl key: %s", mapkey) + delete(CrlCache, mapkey) + } + } +} + +func SearchCrlCache(url string) (bool, *Crl) { + crl, exist := CrlCache[url] + if !exist { + glog.Infof("SearchCrlCache not found cache for url: %s", url) + return false, nil + } + + if CrlExpired(crl) { + glog.Infof("SearchCrlCache crl expired: %s", url) + delete(CrlCache, url) + return false, nil + } + + if CrlNeedUpdate(crl) { + glog.Infof("SearchCrlCache crl need update: %s", url) + delete(CrlCache, url) + return false, nil + } + + glog.Infof("SearchCrlCache found cache for url: %s", url) + return true, crl +} + +func ClientCertAuthenAndAuthor(ctx context.Context, serviceConfigTableName string, enableCrl bool) (context.Context, error) { rc, ctx := common_utils.GetContext(ctx) p, ok := peer.FromContext(ctx) if !ok { @@ -44,9 +143,112 @@ func ClientCertAuthenAndAuthor(ctx context.Context, serviceConfigTableName strin } } + if enableCrl { + err := VerifyCertCrl(tlsAuth.State) + if err != nil { + glog.Infof("[%s] Failed to verify cert with CRL; %v", rc.ID, err) + return ctx, err + } + } + return ctx, nil } +func TryDownload(url string) bool { + glog.Infof("Download CRL start: %s", url) + resp, err := http.Get(url) + + if resp != nil { + defer resp.Body.Close() + } + + if err != nil || resp.StatusCode != http.StatusOK { + glog.Infof("Download CRL: %s failed: %v", url, err) + return false + } + + crlContent, err := io.ReadAll(resp.Body) + if err != nil { + glog.Infof("Download CRL: %s failed: %v", url, err) + return false + } + + glog.Infof("Download CRL: %s successed", url) + AppendCrlToCache(url, crlContent) + + return true +} + +func GetCrlUrls(cert x509.Certificate) []string { + glog.Infof("Get Crl Urls for cert: %v", cert.CRLDistributionPoints) + return cert.CRLDistributionPoints +} + +func DownloadNotCachedCrl(crlUrlArray []string) bool { + crlAvaliable := false + for _, crlUrl := range crlUrlArray{ + exist, _ := SearchCrlCache(crlUrl) + if exist { + crlAvaliable = true + } else { + downloaded := TryDownload(crlUrl) + if downloaded { + crlAvaliable = true + } + } + } + + return crlAvaliable +} + +func CreateStaticCRLProvider() *advancedtls.StaticCRLProvider { + crlArray := make([][]byte, 1) + for mapkey, item := range(CrlCache) { + if CrlExpired(item) { + glog.Infof("CreateStaticCRLProvider remove expired crl: %s", mapkey) + delete(CrlCache, mapkey) + } else { + glog.Infof("CreateStaticCRLProvider add crl: %s content: %v", mapkey, item.crl) + crlArray = append(crlArray, item.crl) + } + } + + return advancedtls.NewStaticCRLProvider(crlArray) +} + +func VerifyCertCrl(tlsConnState tls.ConnectionState) error { + InitCrlCache() + // Check if any CRL already exist in local + crlUriArray := GetCrlUrls(*tlsConnState.VerifiedChains[0][0]) + if len(crlUriArray) == 0 { + glog.Infof("Cert does not contains and CRL distribution points") + return nil + } + + crlAvaliable := DownloadNotCachedCrl(crlUriArray) + if !crlAvaliable { + // Every certificate will contain multiple CRL distribution points. + // If all CRLs are not available, the certificate validation should be blocked. + glog.Infof("VerifyCertCrl can't download CRL and verify cert: %v", crlUriArray) + return status.Errorf(codes.Unauthenticated, "Can't download CRL and verify cert") + } + + // Build CRL provider from cache and verify cert + crlProvider := CreateStaticCRLProvider() + err := advancedtls.CheckChainRevocation(tlsConnState.VerifiedChains, advancedtls.RevocationOptions{ + DenyUndetermined: false, + CRLProvider: crlProvider, + }) + + if err != nil { + glog.Infof("VerifyCertCrl peer certificate revoked: %v", err.Error()) + return status.Error(codes.Unauthenticated, "Peer certificate revoked") + } + + glog.Infof("VerifyCertCrl verify cert passed: %v", crlUriArray) + return nil +} + func PopulateAuthStructByCommonName(certCommonName string, auth *common_utils.AuthInfo, serviceConfigTableName string) error { if serviceConfigTableName == "" { return status.Errorf(codes.Unauthenticated, "Service config table name should not be empty") diff --git a/gnmi_server/crl_test.go b/gnmi_server/crl_test.go new file mode 100644 index 00000000..c4c53b19 --- /dev/null +++ b/gnmi_server/crl_test.go @@ -0,0 +1,251 @@ +package gnmi + +// server_test covers gNMI get, subscribe (stream and poll) test +// Prerequisite: redis-server should be running. +import ( + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "os" + "testing" + "time" + "github.com/agiledragon/gomonkey/v2" + "github.com/sonic-net/sonic-gnmi/common_utils" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestCrlExpireDuration(t *testing.T) { + duration := GetCrlExpireDuration() + if duration != DEFAULT_CRL_EXPIRE_DURATION { + t.Errorf("TestCrlExpireDuration test failed, default expire duration incorrect.") + } + + newDuration := 60 * time.Second + SetCrlExpireDuration(newDuration) + duration = GetCrlExpireDuration() + if duration != newDuration { + t.Errorf("TestCrlExpireDuration test failed, change expire duration failed.") + } +} + +func TestCrlCache(t *testing.T) { + InitCrlCache() + defer ReleaseCrlCache() + + rawCRL, _ := os.ReadFile("../testdata/crl/test.crl") + if rawCRL == nil { + t.Errorf("TestCrlCache test failed, crl file missing.") + } + AppendCrlToCache("http://test.crl.com/test.crl", rawCRL) + + // mock to make test CRL valied + mockCrlExpired := gomonkey.ApplyFunc(CrlExpired, func(crl *Crl) bool { + return true + }) + + // test CRL expired + exist, cacheItem := SearchCrlCache("http://test.crl.com/test.crl") + if exist { + t.Errorf("TestCrlCache test failed, crl should expired.") + } + + if cacheItem != nil { + t.Errorf("TestCrlCache test failed, crl content incorrect.") + } + + // test CRL expired and remove + AppendCrlToCache("http://test.crl.com/test.crl", rawCRL) + RemoveExpiredCrl() + _, exist = CrlCache["http://test.crl.com/test.crl"] + if exist { + t.Errorf("TestCrlCache test failed, expired crl should removed.") + } + + // test CRL does not exist + exist, cacheItem = SearchCrlCache("http://test.crl.com/notexist.crl") + if exist { + t.Errorf("TestCrlCache test failed, crl should not exist.") + } + + if cacheItem != nil { + t.Errorf("TestCrlCache test failed, crl content incorrect.") + } + + // mock to make test CRL valied + mockCrlExpired.Reset() + mockCrlNeedUpdate := gomonkey.ApplyFunc(CrlNeedUpdate, func(crl *Crl) bool { + return false + }) + defer mockCrlNeedUpdate.Reset() + + AppendCrlToCache("http://test.crl.com/test.crl", rawCRL) + exist, cacheItem = SearchCrlCache("http://test.crl.com/test.crl") + if !exist { + t.Errorf("TestCrlCache test failed, crl should exist.") + } + + if len(cacheItem.crl) != len(rawCRL) { + t.Errorf("TestCrlCache test failed, crl content incorrect.") + } +} + +func TestGetCrlUrls(t *testing.T) { + cert := x509.Certificate{ + Subject: pkix.Name{ + CommonName: "certname1", + }, + CRLDistributionPoints: []string{ + "http://test.crl.com/test.crl", + }, + } + + crlUrlArray := GetCrlUrls(cert) + + if len(crlUrlArray) != 1 { + t.Errorf("TestGetCrlUrls get incorrect CRLDistributionPoints.") + } + + if crlUrlArray[0] != "http://test.crl.com/test.crl" { + t.Errorf("TestGetCrlUrls get incorrect CRL.") + } +} + +func makeChain(name string) []*x509.Certificate { + certChain := make([]*x509.Certificate, 0) + + rest, err := os.ReadFile(name) + if err != nil { + fmt.Printf("makeChain ReadFile err: %s\n", err.Error()) + } + + for len(rest) > 0 { + var block *pem.Block + block, rest = pem.Decode(rest) + c, err := x509.ParseCertificate(block.Bytes) + if err != nil { + fmt.Printf("makeChain ParseCertificate err: %s\n", err.Error()) + } + certChain = append(certChain, c) + } + return certChain +} + +func CreateConnectionState(certPath string) tls.ConnectionState { + certChain := makeChain(certPath) + return tls.ConnectionState { + VerifiedChains: [][]*x509.Certificate { + certChain, + }, + } +} + +func TestVerifyCertCrl(t *testing.T) { + InitCrlCache() + defer ReleaseCrlCache() + + mockGetCrlUrls := gomonkey.ApplyFunc(GetCrlUrls, func(cert x509.Certificate) []string { + return []string{ "http://test.crl.com/test.crl" } + }) + defer mockGetCrlUrls.Reset() + + mockCrlExpired := gomonkey.ApplyFunc(CrlExpired, func(crl *Crl) bool { + return false + }) + defer mockCrlExpired.Reset() + + mockTryDownload := gomonkey.ApplyFunc(TryDownload, func(url string) bool { + rawCRL, _ := os.ReadFile("../testdata/crl/test.crl") + AppendCrlToCache("http://test.crl.com/test.crl", rawCRL) + return true + }) + defer mockTryDownload.Reset() + + // test revoked cert + tlsConnState := CreateConnectionState("../testdata/crl/revokedInt.pem") + err := VerifyCertCrl(tlsConnState) + if err == nil { + t.Errorf("TestVerifyCertCrl verify revoked cert failed.") + } + + // test valid cert + tlsConnState = CreateConnectionState("../testdata/crl/unrevoked.pem") + err = VerifyCertCrl(tlsConnState) + if err != nil { + t.Errorf("TestVerifyCertCrl verify unrevoked cert failed.") + } +} + + +func TestVerifyCertCrlWithDownloadFailed(t *testing.T) { + InitCrlCache() + defer ReleaseCrlCache() + + mockGetCrlUrls := gomonkey.ApplyFunc(GetCrlUrls, func(cert x509.Certificate) []string { + return []string{ "http://test.crl.com/test.crl" } + }) + defer mockGetCrlUrls.Reset() + + mockTryDownload := gomonkey.ApplyFunc(TryDownload, func(url string) bool { + return false + }) + defer mockTryDownload.Reset() + + // test valid cert,should failed because download CRL failed + tlsConnState := CreateConnectionState("../testdata/crl/unrevoked.pem") + err := VerifyCertCrl(tlsConnState) + if err == nil { + t.Errorf("TestVerifyCertCrl verify unrevoked cert should failed when CRL can't download.") + } +} + +func TestClientCertAuthenAndAuthorWithCrl(t *testing.T) { + // initialize err variable + err := status.Error(codes.Unauthenticated, "") + + // when config table is empty, will authorize with PopulateAuthStruct + mockpopulate := gomonkey.ApplyFunc(PopulateAuthStruct, func(username string, auth *common_utils.AuthInfo, r []string) error { + return nil + }) + defer mockpopulate.Reset() + + // mock for revoked cert + mockVerifyCertCrl := gomonkey.ApplyFunc(VerifyCertCrl, func(tlsConnState tls.ConnectionState) error { + return status.Error(codes.Unauthenticated, "Peer certificate revoked") + }) + + // check auth with nil cert name + ctx, cancel := CreateAuthorizationCtx() + ctx, err = ClientCertAuthenAndAuthor(ctx, "", true) + if err == nil { + t.Errorf("Auth with revoked cert should failed.") + } + + cancel() + mockVerifyCertCrl.Reset() + + // mock for unrevoked cert + mockVerifyCertCrl = gomonkey.ApplyFunc(VerifyCertCrl, func(tlsConnState tls.ConnectionState) error { + return nil + }) + + // check auth with nil cert name + ctx, cancel = CreateAuthorizationCtx() + ctx, err = ClientCertAuthenAndAuthor(ctx, "", true) + if err != nil { + t.Errorf("Auth with revoked cert should failed: %v", err) + } + + cancel() + mockVerifyCertCrl.Reset() +} + +func TestTryDownload(t *testing.T) { + // Use this test case for improve coverage + downloaded := TryDownload("http://127.0.0.1:1234/") + if downloaded != false { + t.Errorf("Download should failed: %v", downloaded) + } +} \ No newline at end of file diff --git a/gnmi_server/server.go b/gnmi_server/server.go index f3ec24ce..23dd817c 100644 --- a/gnmi_server/server.go +++ b/gnmi_server/server.go @@ -86,6 +86,7 @@ type Config struct { IdleConnDuration int ConfigTableName string Vrf string + EnableCrl bool } var AuthLock sync.Mutex @@ -263,7 +264,7 @@ func authenticate(config *Config, ctx context.Context) (context.Context, error) } } if !success && config.UserAuth.Enabled("cert") { - ctx, err = ClientCertAuthenAndAuthor(ctx, config.ConfigTableName) + ctx, err = ClientCertAuthenAndAuthor(ctx, config.ConfigTableName, config.EnableCrl) if err == nil { success = true } diff --git a/gnmi_server/server_test.go b/gnmi_server/server_test.go index b97e2f63..40a90abc 100644 --- a/gnmi_server/server_test.go +++ b/gnmi_server/server_test.go @@ -4490,7 +4490,7 @@ func CreateAuthorizationCtx() (context.Context, context.CancelFunc) { return ctx, cancel } - func TestClientCertAuthenAndAuthor(t *testing.T) { +func TestClientCertAuthenAndAuthor(t *testing.T) { if !swsscommon.SonicDBConfigIsInit() { swsscommon.SonicDBConfigInitialize() } @@ -4510,7 +4510,7 @@ func CreateAuthorizationCtx() (context.Context, context.CancelFunc) { // check auth with nil cert name ctx, cancel := CreateAuthorizationCtx() - ctx, err = ClientCertAuthenAndAuthor(ctx, "") + ctx, err = ClientCertAuthenAndAuthor(ctx, "", false) if err != nil { t.Errorf("CommonNameMatch with empty config table should success: %v", err) } @@ -4521,7 +4521,7 @@ func CreateAuthorizationCtx() (context.Context, context.CancelFunc) { ctx, cancel = CreateAuthorizationCtx() configDb.Flushdb() gnmiTable.Hset("certname1", "role", "role1") - ctx, err = ClientCertAuthenAndAuthor(ctx, "GNMI_CLIENT_CERT") + ctx, err = ClientCertAuthenAndAuthor(ctx, "GNMI_CLIENT_CERT", false) if err != nil { t.Errorf("CommonNameMatch with correct cert name should success: %v", err) } @@ -4533,7 +4533,7 @@ func CreateAuthorizationCtx() (context.Context, context.CancelFunc) { configDb.Flushdb() gnmiTable.Hset("certname1", "role", "role1") gnmiTable.Hset("certname2", "role", "role2") - ctx, err = ClientCertAuthenAndAuthor(ctx, "GNMI_CLIENT_CERT") + ctx, err = ClientCertAuthenAndAuthor(ctx, "GNMI_CLIENT_CERT", false) if err != nil { t.Errorf("CommonNameMatch with correct cert name should success: %v", err) } @@ -4544,7 +4544,7 @@ func CreateAuthorizationCtx() (context.Context, context.CancelFunc) { ctx, cancel = CreateAuthorizationCtx() configDb.Flushdb() gnmiTable.Hset("certname2", "role", "role2") - ctx, err = ClientCertAuthenAndAuthor(ctx, "GNMI_CLIENT_CERT") + ctx, err = ClientCertAuthenAndAuthor(ctx, "GNMI_CLIENT_CERT", false) if err == nil { t.Errorf("CommonNameMatch with invalid cert name should fail: %v", err) } diff --git a/telemetry/telemetry.go b/telemetry/telemetry.go index cb56e10c..f310a4d7 100644 --- a/telemetry/telemetry.go +++ b/telemetry/telemetry.go @@ -58,6 +58,8 @@ type TelemetryConfig struct { WithSaveOnSet *bool IdleConnDuration *int Vrf *string + EnableCrl *bool + CrlExpireDuration *int } func main() { @@ -167,6 +169,8 @@ func setupFlags(fs *flag.FlagSet) (*TelemetryConfig, *gnmi.Config, error) { WithSaveOnSet: fs.Bool("with-save-on-set", false, "Enables save-on-set."), IdleConnDuration: fs.Int("idle_conn_duration", 5, "Seconds before server closes idle connections"), Vrf: fs.String("vrf", "", "VRF name, when zmq_address belong on a VRF, need VRF name to bind ZMQ."), + EnableCrl: fs.Bool("enable_crl", false, "Enable certificate revocation list"), + CrlExpireDuration: fs.Int("crl_expire_duration", 86400, "Certificate revocation list cache expire duration"), } fs.Var(&telemetryCfg.UserAuth, "client_auth", "Client auth mode(s) - none,cert,password") @@ -230,6 +234,9 @@ func setupFlags(fs *flag.FlagSet) (*TelemetryConfig, *gnmi.Config, error) { cfg.IdleConnDuration = int(*telemetryCfg.IdleConnDuration) cfg.ConfigTableName = *telemetryCfg.ConfigTableName cfg.Vrf = *telemetryCfg.Vrf + cfg.EnableCrl = *telemetryCfg.EnableCrl + + gnmi.SetCrlExpireDuration(time.Duration(*telemetryCfg.CrlExpireDuration) * time.Second) // TODO: After other dependent projects are migrated to ZmqPort, remove ZmqAddress zmqAddress := *telemetryCfg.ZmqAddress diff --git a/testdata/crl/revokedInt.pem b/testdata/crl/revokedInt.pem new file mode 100644 index 00000000..8b7282ff --- /dev/null +++ b/testdata/crl/revokedInt.pem @@ -0,0 +1,58 @@ +-----BEGIN CERTIFICATE----- +MIIDAzCCAqmgAwIBAgITAWjKwm2dNQvkO62Jgyr5rAvVQzAKBggqhkjOPQQDAjCB +pTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1v +dW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZSBMTEMxJjARBgNVBAsTClByb2R1 +Y3Rpb24wEQYDVQQLEwpjYW1wdXMtc2xuMSwwKgYDVQQDEyNSb290IENBICgyMDIx +LTAyLTAyVDA3OjMxOjU0LTA4OjAwKTAgFw0yMTAyMDIxNTMxNTRaGA85OTk5MTIz +MTIzNTk1OVowgaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYw +FAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgTExDMSYwEQYD +VQQLEwpQcm9kdWN0aW9uMBEGA1UECxMKY2FtcHVzLXNsbjEsMCoGA1UEAxMjUm9v +dCBDQSAoMjAyMS0wMi0wMlQwNzozMTo1NC0wODowMCkwWTATBgcqhkjOPQIBBggq +hkjOPQMBBwNCAAQhA0/puhTtSxbVVHseVhL2z7QhpPyJs5Q4beKi7tpaYRDmVn6p +Phh+jbRzg8Qj4gKI/Q1rrdm4rKer63LHpdWdo4GzMIGwMA4GA1UdDwEB/wQEAwIB +BjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUeq/TQ959KbWk/um08jSTXogXpWUwHwYDVR0jBBgwFoAUeq/T +Q959KbWk/um08jSTXogXpWUwLgYDVR0RBCcwJYYjc3BpZmZlOi8vY2FtcHVzLXNs +bi5wcm9kLmdvb2dsZS5jb20wCgYIKoZIzj0EAwIDSAAwRQIgOSQZvyDPQwVOWnpF +zWvI+DS2yXIj/2T2EOvJz2XgcK4CIQCL0mh/+DxLiO4zzbInKr0mxpGSxSeZCUk7 +1ZF7AeLlbw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDizCCAzKgAwIBAgIUAK6BGFvOeQUak65aL+XAQhr5LrcwCgYIKoZIzj0EAwIw +gaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1N +b3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgTExDMSYwEQYDVQQLEwpQcm9k +dWN0aW9uMBEGA1UECxMKY2FtcHVzLXNsbjEsMCoGA1UEAxMjUm9vdCBDQSAoMjAy +MS0wMi0wMlQwNzozMTo1NC0wODowMCkwIBcNMjEwMjAyMTUzMTU0WhgPOTk5OTEy +MzEyMzU5NTlaMIGlMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEW +MBQGA1UEBxMNTW91bnRhaW4gVmlldzETMBEGA1UEChMKR29vZ2xlIExMQzEmMBEG +A1UECxMKUHJvZHVjdGlvbjARBgNVBAsTCmNhbXB1cy1zbG4xLDAqBgNVBAMTI25v +ZGUgQ0EgKDIwMjEtMDItMDJUMDc6MzE6NTQtMDg6MDApMFkwEwYHKoZIzj0CAQYI +KoZIzj0DAQcDQgAEye6UOlBos8Q3FFBiLahD9BaLTA18bO4MTPyv35T3lppvxD5X +U/AnEllOnx5OMtMjMBbIQjSkMbiQ9xNXoSqB6aOCATowggE2MA4GA1UdDwEB/wQE +AwIBBjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUhWfy0gWBmkh2GiaBgnZzlQsvOlIwHwYDVR0jBBgwFoAU +eq/TQ959KbWk/um08jSTXogXpWUwMwYDVR0RBCwwKoYoc3BpZmZlOi8vbm9kZS5j +YW1wdXMtc2xuLnByb2QuZ29vZ2xlLmNvbTA7BgNVHR4BAf8EMTAvoC0wK4YpY3Nj +cy10ZWFtLm5vZGUuY2FtcHVzLXNsbi5wcm9kLmdvb2dsZS5jb20wQgYDVR0fBDsw +OTA3oDWgM4YxaHR0cDovL3N0YXRpYy5jb3JwLmdvb2dsZS5jb20vY3JsL2NhbXB1 +cy1zbG4vbm9kZTAKBggqhkjOPQQDAgNHADBEAiA79rPu6ZO1/0qB6RxL7jVz1200 +UTo8ioB4itbTzMnJqAIgJqp/Rc8OhpsfzQX8XnIIkl+SewT+tOxJT1MHVNMlVhc= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIC0DCCAnWgAwIBAgITXQ2c/C27OGqk4Pbu+MNJlOtpYTAKBggqhkjOPQQDAjCB +pTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1v +dW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZSBMTEMxJjARBgNVBAsTClByb2R1 +Y3Rpb24wEQYDVQQLEwpjYW1wdXMtc2xuMSwwKgYDVQQDEyNub2RlIENBICgyMDIx +LTAyLTAyVDA3OjMxOjU0LTA4OjAwKTAgFw0yMTAyMDIxNTMxNTRaGA85OTk5MTIz +MTIzNTk1OVowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABN2/1le5d3hS/piw +hrNMHjd7gPEjzXwtuXQTzdV+aaeOf3ldnC6OnEF/bggym9MldQSJZLXPYSaoj430 +Vu5PRNejggEkMIIBIDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUH +AwIGCCsGAQUFBwMBMB0GA1UdDgQWBBTEewP3JgrJPekWWGGjChVqaMhaqTAfBgNV +HSMEGDAWgBSFZ/LSBYGaSHYaJoGCdnOVCy86UjBrBgNVHREBAf8EYTBfghZqemFi +MTIucHJvZC5nb29nbGUuY29thkVzcGlmZmU6Ly9jc2NzLXRlYW0ubm9kZS5jYW1w +dXMtc2xuLnByb2QuZ29vZ2xlLmNvbS9yb2xlL2JvcmctYWRtaW4tY28wQgYDVR0f +BDswOTA3oDWgM4YxaHR0cDovL3N0YXRpYy5jb3JwLmdvb2dsZS5jb20vY3JsL2Nh +bXB1cy1zbG4vbm9kZTAKBggqhkjOPQQDAgNJADBGAiEA9w4qp3nHpXo+6d7mZc69 +QoALfP5ynfBCArt8bAlToo8CIQCgc/lTfl2BtBko+7h/w6pKxLeuoQkvCL5gHFyK +LXE6vA== +-----END CERTIFICATE----- diff --git a/testdata/crl/test.crl b/testdata/crl/test.crl new file mode 100644 index 00000000..d37ad224 --- /dev/null +++ b/testdata/crl/test.crl @@ -0,0 +1,11 @@ +-----BEGIN X509 CRL----- +MIIBiDCCAS8CAQEwCgYIKoZIzj0EAwIwgaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQI +EwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpH +b29nbGUgTExDMSYwEQYDVQQLEwpQcm9kdWN0aW9uMBEGA1UECxMKY2FtcHVzLXNs +bjEsMCoGA1UEAxMjUm9vdCBDQSAoMjAyMS0wMi0wMlQwNzozMTo1NC0wODowMCkX +DTIxMDIwMjE1MzE1NFoXDTIxMDIwOTE1MzE1NFowJzAlAhQAroEYW855BRqTrlov +5cBCGvkutxcNMjEwMjAyMTUzMTU0WqAvMC0wHwYDVR0jBBgwFoAUeq/TQ959KbWk +/um08jSTXogXpWUwCgYDVR0UBAMCAQEwCgYIKoZIzj0EAwIDRwAwRAIgaSOIhJDg +wOLYlbXkmxW0cqy/AfOUNYbz5D/8/FfvhosCICftg7Vzlu0Nh83jikyjy+wtkiJt +ZYNvGFQ3Sp2L3A9e +-----END X509 CRL----- diff --git a/testdata/crl/unrevoked.pem b/testdata/crl/unrevoked.pem new file mode 100644 index 00000000..5c5fc58a --- /dev/null +++ b/testdata/crl/unrevoked.pem @@ -0,0 +1,58 @@ +-----BEGIN CERTIFICATE----- +MIIDBDCCAqqgAwIBAgIUALy864QhnkTdceLH52k2XVOe8IQwCgYIKoZIzj0EAwIw +gaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1N +b3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgTExDMSYwEQYDVQQLEwpQcm9k +dWN0aW9uMBEGA1UECxMKY2FtcHVzLXNsbjEsMCoGA1UEAxMjUm9vdCBDQSAoMjAy +MS0wMi0wMlQwNzozMDozNi0wODowMCkwIBcNMjEwMjAyMTUzMDM2WhgPOTk5OTEy +MzEyMzU5NTlaMIGlMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEW +MBQGA1UEBxMNTW91bnRhaW4gVmlldzETMBEGA1UEChMKR29vZ2xlIExMQzEmMBEG +A1UECxMKUHJvZHVjdGlvbjARBgNVBAsTCmNhbXB1cy1zbG4xLDAqBgNVBAMTI1Jv +b3QgQ0EgKDIwMjEtMDItMDJUMDc6MzA6MzYtMDg6MDApMFkwEwYHKoZIzj0CAQYI +KoZIzj0DAQcDQgAEYv/JS5hQ5kIgdKqYZWTKCO/6gloHAmIb1G8lmY0oXLXYNHQ4 +qHN7/pPtlcHQp0WK/hM8IGvgOUDoynA8mj0H9KOBszCBsDAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFPQNtnCIBcG4ReQgoVi0kPgTROseMB8GA1UdIwQYMBaAFPQN +tnCIBcG4ReQgoVi0kPgTROseMC4GA1UdEQQnMCWGI3NwaWZmZTovL2NhbXB1cy1z +bG4ucHJvZC5nb29nbGUuY29tMAoGCCqGSM49BAMCA0gAMEUCIQDwBn20DB4X/7Uk +Q5BR8JxQYUPxOfvuedjfeA8bPvQ2FwIgOEWa0cXJs1JxarILJeCXtdXvBgu6LEGQ +3Pk/bgz8Gek= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDizCCAzKgAwIBAgIUAM/6RKQ7Vke0i4xp5LaAqV73cmIwCgYIKoZIzj0EAwIw +gaUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1N +b3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgTExDMSYwEQYDVQQLEwpQcm9k +dWN0aW9uMBEGA1UECxMKY2FtcHVzLXNsbjEsMCoGA1UEAxMjUm9vdCBDQSAoMjAy +MS0wMi0wMlQwNzozMDozNi0wODowMCkwIBcNMjEwMjAyMTUzMDM2WhgPOTk5OTEy +MzEyMzU5NTlaMIGlMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEW +MBQGA1UEBxMNTW91bnRhaW4gVmlldzETMBEGA1UEChMKR29vZ2xlIExMQzEmMBEG +A1UECxMKUHJvZHVjdGlvbjARBgNVBAsTCmNhbXB1cy1zbG4xLDAqBgNVBAMTI25v +ZGUgQ0EgKDIwMjEtMDItMDJUMDc6MzA6MzYtMDg6MDApMFkwEwYHKoZIzj0CAQYI +KoZIzj0DAQcDQgAEllnhxmMYiUPUgRGmenbnm10gXpM94zHx3D1/HumPs6arjYuT +Zlhx81XL+g4bu4HII2qcGdP+Hqj/MMFNDI9z4aOCATowggE2MA4GA1UdDwEB/wQE +AwIBBjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUGOhXkmeT+CnWTt+ZbCS9OT9wX8gwHwYDVR0jBBgwFoAU +9A22cIgFwbhF5CChWLSQ+BNE6x4wMwYDVR0RBCwwKoYoc3BpZmZlOi8vbm9kZS5j +YW1wdXMtc2xuLnByb2QuZ29vZ2xlLmNvbTA7BgNVHR4BAf8EMTAvoC0wK4YpY3Nj +cy10ZWFtLm5vZGUuY2FtcHVzLXNsbi5wcm9kLmdvb2dsZS5jb20wQgYDVR0fBDsw +OTA3oDWgM4YxaHR0cDovL3N0YXRpYy5jb3JwLmdvb2dsZS5jb20vY3JsL2NhbXB1 +cy1zbG4vbm9kZTAKBggqhkjOPQQDAgNHADBEAiA86egqPw0qyapAeMGbHxrmYZYa +i5ARQsSKRmQixgYizQIgW+2iRWN6Kbqt4WcwpmGv/xDckdRXakF5Ign/WUDO5u4= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICzzCCAnWgAwIBAgITYjjKfYZUKQNUjNyF+hLDGpHJKTAKBggqhkjOPQQDAjCB +pTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1v +dW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZSBMTEMxJjARBgNVBAsTClByb2R1 +Y3Rpb24wEQYDVQQLEwpjYW1wdXMtc2xuMSwwKgYDVQQDEyNub2RlIENBICgyMDIx +LTAyLTAyVDA3OjMwOjM2LTA4OjAwKTAgFw0yMTAyMDIxNTMwMzZaGA85OTk5MTIz +MTIzNTk1OVowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD4r4+nCgZExYF8v +CLvGn0lY/cmam8mAkJDXRN2Ja2t+JwaTOptPmbbXft+1NTk5gCg5wB+FJCnaV3I/ +HaxEhBWjggEkMIIBIDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUH +AwIGCCsGAQUFBwMBMB0GA1UdDgQWBBTTCjXX1Txjc00tBg/5cFzpeCSKuDAfBgNV +HSMEGDAWgBQY6FeSZ5P4KdZO35lsJL05P3BfyDBrBgNVHREBAf8EYTBfghZqemFi +MTIucHJvZC5nb29nbGUuY29thkVzcGlmZmU6Ly9jc2NzLXRlYW0ubm9kZS5jYW1w +dXMtc2xuLnByb2QuZ29vZ2xlLmNvbS9yb2xlL2JvcmctYWRtaW4tY28wQgYDVR0f +BDswOTA3oDWgM4YxaHR0cDovL3N0YXRpYy5jb3JwLmdvb2dsZS5jb20vY3JsL2Nh +bXB1cy1zbG4vbm9kZTAKBggqhkjOPQQDAgNIADBFAiBq3URViNyMLpvzZHC1Y+4L ++35guyIJfjHu08P3S8/xswIhAJtWSQ1ZtozdOzGxg7GfUo4hR+5SP6rBTgIqXEfq +48fW +-----END CERTIFICATE-----