From 4bc8b92232613ef6eebca0f85fa1455ac4afa5cc Mon Sep 17 00:00:00 2001 From: priyacj Date: Fri, 19 Jul 2024 12:20:27 -0700 Subject: [PATCH 01/20] Added CERTZ2.1 testcases --- .../internal/setup_service/setup_service.go | 750 ++++++++++++++++++ .../server_certificates_test.go | 240 ++++++ 2 files changed, 990 insertions(+) create mode 100755 feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go create mode 100755 feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go diff --git a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go new file mode 100755 index 00000000000..6f75ba08eb9 --- /dev/null +++ b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go @@ -0,0 +1,750 @@ +// Copyright 2023 Google LLC +// +// 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 setupservice is scoped only to be used for scripts in path +// feature/security/gnsi/certz/tests/client_certificates +// Do not use elsewhere. +package setupservice + +import ( + context "context" + "crypto/tls" + "crypto/x509" + "encoding/pem" + "fmt" + "io" + "os" + "os/exec" + "testing" + "time" + + gnmipb "github.com/openconfig/gnmi/proto/gnmi" + spb "github.com/openconfig/gnoi/system" + authzpb "github.com/openconfig/gnsi/authz" + certzpb "github.com/openconfig/gnsi/certz" + gribipb "github.com/openconfig/gribi/v1/proto/service" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/knebind/creds" + + p4rtpb "github.com/p4lang/p4runtime/go/p4/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/status" +) + +var ( + username = "certzuser" + password = "certzpasswd" + sn = "role001.pop55.net.example.com" + servers []string +) + +type rpcCredentials struct { + *creds.UserPass +} + +func (r *rpcCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { + return map[string]string{ + "username": r.UserPass.Username, + "password": r.UserPass.Password, + }, nil +} + +func (r *rpcCredentials) RequireTransportSecurity() bool { + return true +} + +type entityType int8 + +const ( + // EntityTypeCertificateChain is type of entity of the certificate chain. + EntityTypeCertificateChain entityType = 0 + // EntityTypeTrustBundle is type of entity of the trust bundle. + EntityTypeTrustBundle entityType = 1 + // EntityTypeCRL is type of entity of the CRL. + EntityTypeCRL entityType = 2 + // EntityTypeAuthPolicy is type of entity of the auth policy. + EntityTypeAuthPolicy entityType = 3 +) + +// CertificateChainRequest is an input argument for the type definition for the CreateCertzChain. +type CertificateChainRequest struct { + RequestType entityType + ServerCertFile string + ServerKeyFile string + TrustBundleFile string +} + +// CreateCertzEntity function to create certificate entity of type certificate chain/trust bundle/CRL/Authpolicy. +func CreateCertzEntity(t *testing.T, typeOfEntity entityType, entityContent any, entityVersion string) certzpb.Entity { + + createdOnTime := time.Now() + varClock := uint64(createdOnTime.Unix()) + + switch typeOfEntity { + case EntityTypeCertificateChain: + + return certzpb.Entity{ + Version: entityVersion, + CreatedOn: varClock, + Entity: &certzpb.Entity_CertificateChain{CertificateChain: entityContent.(*certzpb.CertificateChain)}} + + case EntityTypeTrustBundle: + + return certzpb.Entity{ + Version: entityVersion, + CreatedOn: varClock, + Entity: &certzpb.Entity_TrustBundle{TrustBundle: entityContent.(*certzpb.CertificateChain)}} + + case EntityTypeCRL: + + return certzpb.Entity{ + Version: entityVersion, + CreatedOn: varClock, + Entity: &certzpb.Entity_CertificateRevocationListBundle{CertificateRevocationListBundle: entityContent.(*certzpb.CertificateRevocationListBundle)}} + + case EntityTypeAuthPolicy: + + return certzpb.Entity{ + Version: entityVersion, + CreatedOn: varClock, + Entity: &certzpb.Entity_AuthenticationPolicy{AuthenticationPolicy: entityContent.(*certzpb.AuthenticationPolicy)}} + + default: + t.Fatalf("Invalid entity type") + } + return certzpb.Entity{} +} + +// CreateCertzChain function to get the certificate chain of type certificate chain/trust bundle. +func CreateCertzChain(t *testing.T, certData CertificateChainRequest) certzpb.CertificateChain { + + switch certData.RequestType { + case EntityTypeCertificateChain: + if len(certData.ServerCertFile) == 0 { + t.Fatalf("Missing server certificate file for creating certificate chain object.") + } + serverCertContent, err := os.ReadFile(certData.ServerCertFile) + if err != nil { + t.Fatalf("Error reading Server Certificate file at: %v with error: %v", certData.ServerCertFile, err) + + } + if len(certData.ServerKeyFile) != 0 { + serverKeyContent, err := os.ReadFile(certData.ServerKeyFile) + if err != nil { + t.Fatalf("Error reading Server Key file at: %v with error: %v", certData.ServerKeyFile, err) + } + return certzpb.CertificateChain{Certificate: &certzpb.Certificate{ + Type: certzpb.CertificateType_CERTIFICATE_TYPE_X509, + Encoding: certzpb.CertificateEncoding_CERTIFICATE_ENCODING_PEM, + Certificate: serverCertContent, + PrivateKey: serverKeyContent}, Parent: nil} + } + return certzpb.CertificateChain{Certificate: &certzpb.Certificate{ + Type: certzpb.CertificateType_CERTIFICATE_TYPE_X509, + Encoding: certzpb.CertificateEncoding_CERTIFICATE_ENCODING_PEM, + Certificate: serverCertContent, + PrivateKey: nil}, Parent: nil} + + case EntityTypeTrustBundle: + if len(certData.TrustBundleFile) == 0 { + t.Fatalf("Missing trust bundle file for creating certificate chain object.") + } + trustBundleContent, err := os.ReadFile(certData.TrustBundleFile) + if err != nil { + t.Fatalf("Error reading trust bundle file at: %v with error: %v", certData.TrustBundleFile, err) + } + return certzpb.CertificateChain{Certificate: &certzpb.Certificate{ + Type: certzpb.CertificateType_CERTIFICATE_TYPE_X509, + Encoding: certzpb.CertificateEncoding_CERTIFICATE_ENCODING_PEM, + Certificate: trustBundleContent, + }, Parent: nil} + + default: + t.Fatalf("Invalid request type received.") + } + return certzpb.CertificateChain{} +} + +// CreateCertChainFromTrustBundle function to create the certificate chain from trust bundle. +func CreateCertChainFromTrustBundle(fileName string) *certzpb.CertificateChain { + pemData, err := os.ReadFile(fileName) + if err != nil { + return &certzpb.CertificateChain{} + } + var trust [][]byte + for { + var block *pem.Block + block, pemData = pem.Decode(pemData) + if block == nil { + break + } + if block.Type != "CERTIFICATE" { + continue + } + p := pem.EncodeToMemory(block) + if p == nil { + return &certzpb.CertificateChain{} + } + trust = append(trust, p) + } + if len(trust) > 0 { + var prevCert *certzpb.CertificateChain + var bundleToReturn *certzpb.CertificateChain + for i := len(trust) - 1; i >= 0; i-- { + if i == len(trust)-1 { + bundleToReturn = &certzpb.CertificateChain{Certificate: &certzpb.Certificate{ + Type: certzpb.CertificateType_CERTIFICATE_TYPE_X509, + Encoding: certzpb.CertificateEncoding_CERTIFICATE_ENCODING_PEM, + Certificate: trust[i], + }, Parent: nil} + prevCert = bundleToReturn + } else { + prevCert = bundleToReturn + bundleToReturn = &certzpb.CertificateChain{Certificate: &certzpb.Certificate{ + Type: certzpb.CertificateType_CERTIFICATE_TYPE_X509, + Encoding: certzpb.CertificateEncoding_CERTIFICATE_ENCODING_PEM, + Certificate: trust[i], + }, Parent: prevCert} + } + } + return bundleToReturn + } + return &certzpb.CertificateChain{} +} + +// CertzRotate function to request the client certificate rotation. +func CertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb.CertzClient, cert tls.Certificate, san, serverAddr, profileID string, entities ...*certzpb.Entity) bool { + if len(entities) == 0 { + t.Logf("At least one entity required for Rotate request.") + return false + } + uploadRequest := &certzpb.UploadRequest{Entities: entities} + rotateRequest := &certzpb.RotateCertificateRequest_Certificates{Certificates: uploadRequest} + rotateCertRequest := &certzpb.RotateCertificateRequest{ + ForceOverwrite: false, + SslProfileId: profileID, + RotateRequest: rotateRequest} + rotateRequestClient, err := certzClient.Rotate(context.Background()) + defer rotateRequestClient.CloseSend() + if err != nil { + t.Fatalf("Error creating rotate request client: %v", err) + } + err = rotateRequestClient.Send(rotateCertRequest) + if err != nil { + t.Fatalf("Error sending rotate request: %v", err) + } + rotateResponse := &certzpb.RotateCertificateResponse{} + for i := 0; i < 20; i++ { + rotateResponse, err = rotateRequestClient.Recv() + if err == nil { + break + } + t.Logf("Did not receive response ~ %vs after sending rotate request. Sleeping 10s to retry...", i*10) + time.Sleep(10 * time.Second) + } + if err != nil { + t.Logf("Error fetching rotate certificate response: %v", err) + return false + } + t.Logf("Received Rotate certificate response: %v", rotateResponse) + + finalizeRequest := &certzpb.RotateCertificateRequest_FinalizeRotation{FinalizeRotation: &certzpb.FinalizeRequest{}} + rotateCertRequest = &certzpb.RotateCertificateRequest{ + ForceOverwrite: false, + SslProfileId: profileID, + RotateRequest: finalizeRequest} + + err = rotateRequestClient.Send(rotateCertRequest) + if err != nil { + t.Fatalf("Error sending rotate finalize request: %v", err) + } + err = rotateRequestClient.CloseSend() + if err != nil { + t.Fatalf("Error sending rotate close send request: %v", err) + } + return true +} + +// ServerCertzRotate function to request the server certificate rotation. +func ServerCertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb.CertzClient, cert tls.Certificate, ctx context.Context, dut *ondatra.DUTDevice, san, serverAddr, profileID string, entities ...*certzpb.Entity) bool { + if len(entities) == 0 { + t.Logf("At least one entity required for Rotate request.") + return false + } + uploadRequest := &certzpb.UploadRequest{Entities: entities} + rotateRequest := &certzpb.RotateCertificateRequest_Certificates{Certificates: uploadRequest} + rotateCertRequest := &certzpb.RotateCertificateRequest{ + ForceOverwrite: false, + SslProfileId: profileID, + RotateRequest: rotateRequest} + rotateRequestClient, err := certzClient.Rotate(context.Background()) + defer rotateRequestClient.CloseSend() + if err != nil { + t.Fatalf("Error creating rotate request client: %v", err) + } + err = rotateRequestClient.Send(rotateCertRequest) + if err != nil { + t.Fatalf("Error sending rotate request: %v", err) + } + rotateResponse := &certzpb.RotateCertificateResponse{} + for i := 0; i < 6; i++ { + rotateResponse, err = rotateRequestClient.Recv() + if err == nil { + break + } + t.Logf("Did not receive response ~ %vs after sending rotate request. Sleeping 10s to retry...", i*10) + time.Sleep(10 * time.Second) + } + if err != nil { + t.Logf("Error fetching rotate certificate response: %v", err) + return false + } + t.Logf("Received Rotate certificate response: %v", rotateResponse) + + // Replace config with newly added ssl profile after successful rotate. + servers = gnmi.GetAll(t, dut, gnmi.OC().System().GrpcServerAny().Name().State()) + batch := gnmi.SetBatch{} + for _, server := range servers { + gnmi.BatchReplace(&batch, gnmi.OC().System().GrpcServer(server).CertificateId().Config(), profileID) + } + batch.Set(t, dut) + t.Logf("gNMI config is replaced with new ssl profile successfully.") + success := false + //Trying for 60s for the connection to succeed. + for i := 0; i < 6; i++ { + success = VerifyGnsi(t, caCert, context.Background(), san, serverAddr, username, password, cert) + if success { + break + } + if i != 10 { + t.Logf("gNSI service RPC did not succeed ~ %vs after rotate. Sleeping 10s to retry...", i*10) + } + time.Sleep(10 * time.Second) + } + if success { + finalizeRequest := &certzpb.RotateCertificateRequest_FinalizeRotation{FinalizeRotation: &certzpb.FinalizeRequest{}} + rotateCertRequest = &certzpb.RotateCertificateRequest{ + ForceOverwrite: false, + SslProfileId: profileID, + RotateRequest: finalizeRequest} + + err = rotateRequestClient.Send(rotateCertRequest) + if err != nil { + t.Fatalf("Error sending rotate finalize request: %v", err) + } + err = rotateRequestClient.CloseSend() + if err != nil { + t.Fatalf("Error sending rotate close send request: %v", err) + } + return true + } else { + t.Logf("gNSI service RPC did not succeed ~60s after rotate. Certz/Rotate failed. FinalizeRequest will not be sent") + return false + } +} + +// CertGeneration function to create test data for use in TLS tests. +func CertGeneration(dirPath string) error { + cmd := exec.Cmd{ + Path: "./mk_cas.sh", + Stdout: os.Stdout, + Stderr: os.Stderr, + } + cmd.Dir = dirPath + fmt.Printf("Executing cert generation command %v", cmd) + err := cmd.Start() + if err != nil { + fmt.Printf("unable to run cert generation command:%v", err) + return err + } + err = cmd.Wait() + if err != nil { + fmt.Printf("unable to run cert generation command:%v", err) + return err + } + return err +} + +// CertCleanup function to clean out the CA content under test_data. +func CertCleanup(dirPath string) error { + cmd := exec.Cmd{ + Path: "./cleanup.sh", + Stdout: os.Stdout, + Stderr: os.Stderr, + } + cmd.Dir = dirPath + fmt.Printf("Executing cleanup command") + err := cmd.Start() + if err != nil { + fmt.Printf("unable to run testdata cleanup command:%v", err) + return err + } + err = cmd.Wait() + if err != nil { + fmt.Printf("unable to run testdata cleanup command:%v", err) + return err + } + return err +} + +// ReadDecodeServerCertificate function to read and decode server certificates to extract the SAN +func ReadDecodeServerCertificate(t *testing.T, serverCertzFile string) (san string) { + sc, err := os.ReadFile(serverCertzFile) + if err != nil { + t.Fatalf("Failed to read certificate: %v", err) + } + block, _ := pem.Decode(sc) + if block == nil { + t.Fatalf("Failed to parse PEM block containing the public key.") + } + sCert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + t.Fatalf("Failed to parse certificate: %v", err) + } + san = sCert.DNSNames[0] + t.Logf("SAN :%s", san) + if sn != san { + t.Fatalf("Server name validation failed for %s.", serverCertzFile) + } + return san +} + +// VerifyGnsi function to validate the gNSI service RPC after successful rotation. +func VerifyGnsi(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, serverAddr, username, password string, cert tls.Certificate) bool { + + credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( + &tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: caCert, + ServerName: san, + }))} + creds := &rpcCredentials{&creds.UserPass{Username: username, Password: password}} + credOpts = append(credOpts, grpc.WithPerRPCCredentials(creds)) + target := fmt.Sprintf("%s:%d", serverAddr, 9339) + conn, err := grpc.NewClient(target, credOpts...) + if err != nil { + t.Errorf("VerifyGnsi:gRPC NewClient failed to %q with err %v", target, err) + return false + } + t.Logf("Connection state: %v.", conn.GetState().String()) + defer conn.Close() + + authzClient := authzpb.NewAuthzClient(conn) + rsp, err := authzClient.Get(ctx, &authzpb.GetRequest{}) + if err != nil { + statusError, _ := status.FromError(err) + if statusError.Code() == codes.FailedPrecondition { + t.Logf("Expected error FAILED_PRECONDITION seen for authz Get Request with err:%v.", err) + } else { + t.Logf("Unexpected error during authz Get Request with err:%v.", err) + return false + } + } + t.Logf("gNSI authz get response is %s", rsp) + conn.Close() + return true +} + +// VerifyGnoi function to validate the gNOI service RPC after successful rotation. +func VerifyGnoi(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, serverAddr, username, password string, cert tls.Certificate) bool { + + credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( + &tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: caCert, + ServerName: san, + }))} + creds := &rpcCredentials{&creds.UserPass{Username: username, Password: password}} + credOpts = append(credOpts, grpc.WithPerRPCCredentials(creds)) + target := fmt.Sprintf("%s:%d", serverAddr, 9339) + conn, err := grpc.NewClient(target, credOpts...) + if err != nil { + t.Errorf("VerifyGnoi : gRPC NewClient failed to %q with err %v", target, err) + return false + } + t.Logf("Connection state: %v.", conn.GetState().String()) + defer conn.Close() + + sysClient := spb.NewSystemClient(conn) + _, err = sysClient.Ping(ctx, &spb.PingRequest{}) + if err != nil { + t.Logf("Unable to connect gnoiClient %v", err) + return false + } + conn.Close() + return true +} + +// VerifyGnmi function to validate the gNMI service RPC after successful rotation. +func VerifyGnmi(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, serverAddr, username, password string, cert tls.Certificate) bool { + + credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( + &tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: caCert, + ServerName: san, + }))} + creds := &rpcCredentials{&creds.UserPass{Username: username, Password: password}} + credOpts = append(credOpts, grpc.WithPerRPCCredentials(creds)) + target := fmt.Sprintf("%s:%d", serverAddr, 9339) + conn, err := grpc.NewClient(target, credOpts...) + if err != nil { + t.Errorf("VerifyGnmi: gRPC NewClient failed to %q with err %v", target, err) + return false + } + t.Logf("Connection state: %v.", conn.GetState().String()) + defer conn.Close() + + gnmiClient := gnmipb.NewGNMIClient(conn) + t.Logf("Sending gNMI subscribe request.") + //stream, err := gnmiClient.Subscribe(ctx) + stream, err := gnmiClient.Subscribe(context.Background()) + defer stream.CloseSend() + + if err != nil { + t.Logf("gNMI subscribe request failed with err: %v", err) + return false + } + + sub := &gnmipb.SubscribeRequest{ + Request: &gnmipb.SubscribeRequest_Subscribe{ + Subscribe: &gnmipb.SubscriptionList{ + Subscription: []*gnmipb.Subscription{ + { + Path: &gnmipb.Path{ + Elem: []*gnmipb.PathElem{{Name: "system"}, {Name: "state"}, {Name: "software-version"}}, + }, + }, + }, + }, + }, + } + t.Logf("Sending gNMI subscribe request: %s", sub) + err = stream.Send(sub) + if err != nil { + t.Logf("Failed to subscribe with error: %s", err) + return false + } + response, err := stream.Recv() + if err != nil { + if err != io.EOF { + t.Logf("Error received from the server: %s", err) + return false + } + } + t.Logf("gNMI response: %s", response) + conn.Close() + return true +} + +// VerifyGribi function to validate the gRIBI service RPC after successful rotation. +func VerifyGribi(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, serverAddr, username, password string, cert tls.Certificate) bool { + + credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( + &tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: caCert, + ServerName: san, + }))} + creds := &rpcCredentials{&creds.UserPass{Username: username, Password: password}} + credOpts = append(credOpts, grpc.WithPerRPCCredentials(creds)) + target := fmt.Sprintf("%s:%d", serverAddr, 9340) + conn, err := grpc.NewClient(target, credOpts...) + if err != nil { + t.Errorf("VerifyGnmi: gRPC NewClient failed to %q with error:%v", target, err) + return false + } + t.Logf("Connection state: %v.", conn.GetState().String()) + defer conn.Close() + + gRibiClient := gribipb.NewGRIBIClient(conn) + _, err = gRibiClient.Get(ctx, &gribipb.GetRequest{}) + if err != nil { + t.Logf("Unable to connect GribiClient %v", err) + return false + } + conn.Close() + return true +} + +// VerifyP4rt function to validate the P4rt service RPC after successful rotation. +func VerifyP4rt(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, serverAddr, username, password string, cert tls.Certificate) bool { + + credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( + &tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: caCert, + ServerName: san, + }))} + creds := &rpcCredentials{&creds.UserPass{Username: username, Password: password}} + credOpts = append(credOpts, grpc.WithPerRPCCredentials(creds)) + target := fmt.Sprintf("%s:%d", serverAddr, 9559) + conn, err := grpc.NewClient(target, credOpts...) + if err != nil { + t.Errorf("VerifyP4rt : gRPC NewClient failed to %q", target) + } + t.Logf("Connection state: %v.", conn.GetState().String()) + defer conn.Close() + + p4RtClient := p4rtpb.NewP4RuntimeClient(conn) + _, err = p4RtClient.Capabilities(ctx, &p4rtpb.CapabilitiesRequest{}) + if err != nil { + t.Logf("Unable to connect P4rtClient %v", err) + return false + } + conn.Close() + return true +} + +// PreInitCheck function to do a validation of gNMI/gNOI/gRIBI/p4RT services before certz rotation. +func PreInitCheck(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice) bool { + + gnmiC, err := dut.RawAPIs().BindingDUT().DialGNMI(ctx) + if err != nil { + t.Fatalf("Failed to create gNMI Connection with error: %v", err) + } + t.Logf("Precheck:gNMI connection is successful %v", gnmiC) + gribiC, err := dut.RawAPIs().BindingDUT().DialGRIBI(ctx) + if err != nil { + t.Fatalf("Failed to create gRIBI Connection with error: %v", err) + } + t.Logf("Precheck:gRIBI connection is successful %v", gribiC) + gnoiC, err := dut.RawAPIs().BindingDUT().DialGNOI(ctx) + if err != nil { + t.Fatalf("Faield to create gNOI Connection with error: %v", err) + } + t.Logf("Precheck:gNOI connection is successful %v", gnoiC) + p4rtC, err := dut.RawAPIs().BindingDUT().DialP4RT(ctx) + if err != nil { + t.Fatalf("Failed to create p4RT Connection with error: %v", err) + } + t.Logf("Precheck:p4RT connection is successful %v", p4rtC) + return true +} + +// GetSslProfilelist function to fetch the existing ssl profiles on the device. +func GetSslProfilelist(ctx context.Context, t *testing.T, certzClient certzpb.CertzClient, certzGetReq *certzpb.GetProfileListRequest) { + getProfileResponse, err := certzClient.GetProfileList(ctx, certzGetReq) + if err != nil { + t.Fatalf("Get profile list request failed with %v!", err) + } + t.Logf("GetProfileResponse: %v", getProfileResponse) +} + +// PostValidationCheck function to do a validation of all services after certz rotation. +func PostValidationCheck(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, serverAddr, username, password string, cert tls.Certificate) bool { + + if !VerifyGnsi(t, caCert, ctx, san, serverAddr, username, password, cert) { + t.Fatalf("Failed with new gNSI Connection: got false, want true.") + } + t.Logf("New gNSI connection successfully completed.") + + if !VerifyGnoi(t, caCert, ctx, san, serverAddr, username, password, cert) { + t.Logf("Failed with new gNOI Connection: got false, want true") + return false + } + t.Logf("New gNOI connection successfully completed.") + + if !VerifyGribi(t, caCert, ctx, san, serverAddr, username, password, cert) { + t.Logf("Failed with new gRIBI Connection: got false, want true.") + return false + } + t.Logf("New gRIBI connection successfully completed.") + if !VerifyP4rt(t, caCert, ctx, san, serverAddr, username, password, cert) { + t.Logf("Failed with new P4rt Connection: got false, want true.") + return false + } + if !VerifyGnmi(t, caCert, ctx, san, serverAddr, username, password, cert) { + t.Logf("Failed with new gNMI Connection: got false, want true.") + return false + } + t.Logf("New gNMI connection successfully completed.") + return true +} + +// TestNewConnection function to validate the connection for any gRPC serviceafter successful rotation. +func TestNewConnection(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, serverAddr, username, password string, cert tls.Certificate) bool { + + credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( + &tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: caCert, + ServerName: san, + }))} + creds := &rpcCredentials{&creds.UserPass{Username: username, Password: password}} + credOpts = append(credOpts, grpc.WithPerRPCCredentials(creds)) + target := fmt.Sprintf("%s:%d", serverAddr, 9339) + conn, err := grpc.NewClient(target, credOpts...) + t.Logf("Err response: %v.", err) + if err != nil { + t.Logf("gRPC NewClient failed with mismatch certificate as expected to %q with error %s.", target, err) + return false + } + t.Logf("Connection state: %v.", conn.GetState().String()) + defer conn.Close() + + authzClient := authzpb.NewAuthzClient(conn) + t.Logf("Authzclient Get Request.") + rsp, err := authzClient.Get(ctx, &authzpb.GetRequest{}) + t.Logf("gNSI authz get response is %s", rsp) + if err != nil { + statusError, _ := status.FromError(err) + if statusError.Code() == codes.FailedPrecondition { + t.Logf("Expected error FAILED_PRECONDITION seen for authz Get Request.") + } else { + t.Logf("Unexpected error during authz Get Request :%v.", err) + return false + } + } + conn.Close() + return true +} + +// MismatchPostValidationCheck function to do a validation of all services after certz rotation. +func MismatchPostValidationCheck(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, serverAddr, username, password string, cert tls.Certificate) bool { + + result := VerifyGnsi(t, caCert, ctx, san, serverAddr, username, password, cert) + if result { + t.Errorf("FAIL:new gNSI Connection worked with mimsatch trustbundle - got %v, want false.", result) + } + t.Logf("PASS: new gNSI connection with mismatch trustbundle is not working as expected- got %v, want false.", result) + + result = VerifyGnoi(t, caCert, ctx, san, serverAddr, username, password, cert) + if result { + t.Errorf("FAIL:new gNOI Connection worked with mismatch trustbundle: got %v, want false", result) + } + t.Logf("PASS:New gNOI connection with mismatch trustbundle is not working as expected- got %v, want false.", result) + + result = VerifyGribi(t, caCert, ctx, san, serverAddr, username, password, cert) + if result { + t.Errorf("FAIL:new gRIBI Connection worked with mismatch trustbundle: got %v, want false", result) + } + t.Logf("PASS:New gRIBI connection with mismatch trustbundle is not working as expected- got %v, want false.", result) + + result = VerifyP4rt(t, caCert, ctx, san, serverAddr, username, password, cert) + if result { + t.Errorf("FAIL: new P4rt Connection worked with mismatch trustbundle: got %v, want false", result) + } + t.Logf("PASS:New P4rt connection with mismatch trustbundle is not working as expected- got %v, want false.", result) + + result = VerifyGnmi(t, caCert, ctx, san, serverAddr, username, password, cert) + if result { + t.Errorf("FAIL: new gNMI Connection worked with mismatch trustbundle: got %v, want false", result) + } + t.Logf("PASS:New gNMI connection with mismatch trustbundle is not working as expected- got %v, want false.", result) + return true +} diff --git a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go new file mode 100755 index 00000000000..d0143ce8de8 --- /dev/null +++ b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go @@ -0,0 +1,240 @@ +// Copyright 2023 Google LLC +// +// 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 server_certificates_test + +import ( + context "context" + "crypto/tls" + "crypto/x509" + "os" + "testing" + "time" + + setupService "github.com/openconfig/featureprofiles/feature/security/gnsi/certz/tests/internal/setup_service" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ygot/ygot" + + certzpb "github.com/openconfig/gnsi/certz" +) + +const ( + dirPath = "../../test_data/" +) + +var ( + testProfile = "newprofile" + serverAddr string + username = "certzuser" + password = "certzpasswd" + servers []string +) + +// createUser function to add an user in admin role. + +func createUser(t *testing.T, dut *ondatra.DUTDevice, user, pswd string) bool { + ocUser := &oc.System_Aaa_Authentication_User{ + Username: ygot.String(user), + Role: oc.AaaTypes_SYSTEM_DEFINED_ROLES_SYSTEM_ROLE_ADMIN, + Password: ygot.String(pswd), + } + res := gnmi.Update(t, dut, gnmi.OC().System().Aaa().Authentication().User(user).Config(), ocUser) + t.Logf("Update the user configuration:%v", res) + if res == nil { + t.Fatalf("Failed to create credentials.") + } + return true +} + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +// TestServerCertTcTwo tests the server certificates from a set of one CA are able to be validated and +// used for authentication to a device when used by a client connecting to each +// gRPC service. + +func TestServerCert(t *testing.T) { + + dut := ondatra.DUT(t, "dut") + serverAddr = dut.Name() + if !createUser(t, dut, username, password) { + t.Fatalf("Failed to create certz user.") + } + + t.Logf("Validation of all services that are using gRPC before certz rotation.") + if !setupService.PreInitCheck(context.Background(), t, dut) { + t.Fatalf("Failed in the preInit checks.") + } + + ctx := context.Background() + gnsiC, err := dut.RawAPIs().BindingDUT().DialGNSI(ctx) + if err != nil { + t.Fatalf("Failed to create gNSI Connection %v", err) + } + t.Logf("Precheck:gNSI connection is successful %v", gnsiC) + + t.Logf("Creation of test data.") + if setupService.CertGeneration(dirPath) != nil { + t.Fatalf("Failed to generate the testdata certificates.") + } + + certzClient := gnsiC.Certz() + t.Logf("Precheck:baseline ssl profile list") + setupService.GetSslProfilelist(ctx, t, certzClient, &certzpb.GetProfileListRequest{}) + t.Logf("Adding new empty ssl profile ID.") + addProfileResponse, err := certzClient.AddProfile(ctx, &certzpb.AddProfileRequest{SslProfileId: testProfile}) + if err != nil { + t.Fatalf("Add profile request failed with %v!", err) + } + t.Logf("AddProfileResponse: %v", addProfileResponse) + t.Logf("Getting the ssl profile list after new ssl profile addition.") + setupService.GetSslProfilelist(ctx, t, certzClient, &certzpb.GetProfileListRequest{}) + + cases := []struct { + desc string + serverCertFile string + serverKeyFile string + trustBundleFile string + clientCertFile string + clientKeyFile string + }{ + { + desc: "Certz2.1:Load server certificate of rsa keytype with 1 CA configuration", + serverCertFile: dirPath + "ca-01/server-rsa-a-cert.pem", + serverKeyFile: dirPath + "ca-01/server-rsa-a-key.pem", + trustBundleFile: dirPath + "ca-01/trust_bundle_01_rsa.pem", + clientCertFile: dirPath + "ca-01/client-rsa-a-cert.pem", + clientKeyFile: dirPath + "ca-01/client-rsa-a-key.pem", + }, + { + desc: "Certz2.1:Load server certificate of ecdsa keytype with 1 CA configuration", + serverCertFile: dirPath + "ca-01/server-ecdsa-a-cert.pem", + serverKeyFile: dirPath + "ca-01/server-ecdsa-a-key.pem", + trustBundleFile: dirPath + "ca-01/trust_bundle_01_ecdsa.pem", + clientCertFile: dirPath + "ca-01/client-ecdsa-a-cert.pem", + clientKeyFile: dirPath + "ca-01/client-ecdsa-a-key.pem", + }, + { + desc: "Certz2.1:Load server certificate of rsa keytype with 2 CA configuration", + serverCertFile: dirPath + "ca-02/server-rsa-a-cert.pem", + serverKeyFile: dirPath + "ca-02/server-rsa-a-key.pem", + trustBundleFile: dirPath + "ca-02/trust_bundle_02_rsa.pem", + clientCertFile: dirPath + "ca-02/client-rsa-a-cert.pem", + clientKeyFile: dirPath + "ca-02/client-rsa-a-key.pem", + }, + { + desc: "Certz2.1:Load server certificate of ecdsa keytype with 2 CA configuration", + serverCertFile: dirPath + "ca-02/server-ecdsa-a-cert.pem", + serverKeyFile: dirPath + "ca-02/server-ecdsa-a-key.pem", + trustBundleFile: dirPath + "ca-02/trust_bundle_02_ecdsa.pem", + clientCertFile: dirPath + "ca-02/client-ecdsa-a-cert.pem", + clientKeyFile: dirPath + "ca-02/client-ecdsa-a-key.pem", + }, + { + desc: "Certz2.1:Load server certificate of rsa keytype with 10CA configuration", + serverCertFile: dirPath + "ca-10/server-rsa-a-cert.pem", + serverKeyFile: dirPath + "ca-10/server-rsa-a-key.pem", + trustBundleFile: dirPath + "ca-10/trust_bundle_10_rsa.pem", + clientCertFile: dirPath + "ca-10/client-rsa-a-cert.pem", + clientKeyFile: dirPath + "ca-10/client-rsa-a-key.pem", + }, + { + desc: "Certz2.1:Load server certificate of ecdsa keytype with 10CA configuration", + serverCertFile: dirPath + "ca-10/server-ecdsa-a-cert.pem", + serverKeyFile: dirPath + "ca-10/server-ecdsa-a-key.pem", + trustBundleFile: dirPath + "ca-10/trust_bundle_10_ecdsa.pem", + clientCertFile: dirPath + "ca-10/client-ecdsa-a-cert.pem", + clientKeyFile: dirPath + "ca-10/client-ecdsa-a-key.pem", + }, + { + desc: "Certz2.1:Load server certificate of rsa keytype with 1000CA configuration", + serverCertFile: dirPath + "ca-1000/server-rsa-a-cert.pem", + serverKeyFile: dirPath + "ca-1000/server-rsa-a-key.pem", + trustBundleFile: dirPath + "ca-1000/trust_bundle_1000_rsa.pem", + clientCertFile: dirPath + "ca-1000/client-rsa-a-cert.pem", + clientKeyFile: dirPath + "ca-1000/client-rsa-a-key.pem", + }, + { + desc: "Certz2.1:Load server certificate of ecdsa keytype with 1000CA configuration", + serverCertFile: dirPath + "ca-1000/server-ecdsa-a-cert.pem", + serverKeyFile: dirPath + "ca-1000/server-ecdsa-a-key.pem", + trustBundleFile: dirPath + "ca-1000/trust_bundle_1000_ecdsa.pem", + clientCertFile: dirPath + "ca-1000/client-ecdsa-a-cert.pem", + clientKeyFile: dirPath + "ca-1000/client-ecdsa-a-key.pem", + }, + } + + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + + san := setupService.ReadDecodeServerCertificate(t, tc.serverCertFile) + serverCert := setupService.CreateCertzChain(t, setupService.CertificateChainRequest{ + RequestType: setupService.EntityTypeCertificateChain, + ServerCertFile: tc.serverCertFile, + ServerKeyFile: tc.serverKeyFile}) + serverCertEntity := setupService.CreateCertzEntity(t, setupService.EntityTypeCertificateChain, &serverCert, "servercert") + + trustCertChain := setupService.CreateCertChainFromTrustBundle(tc.trustBundleFile) + trustBundleEntity := setupService.CreateCertzEntity(t, setupService.EntityTypeTrustBundle, trustCertChain, "cabundle") + + cert, err := tls.LoadX509KeyPair(tc.clientCertFile, tc.clientKeyFile) + if err != nil { + t.Fatalf("Failed to load client cert: %v", err) + } + + cacert := x509.NewCertPool() + cacertBytes, err := os.ReadFile(tc.trustBundleFile) + if err != nil { + t.Fatalf("Failed to read ca bundle :%v", err) + } + if ok := cacert.AppendCertsFromPEM(cacertBytes); !ok { + t.Fatalf("Failed to parse %v", tc.trustBundleFile) + } + + certzClient := gnsiC.Certz() + success := setupService.CertzRotate(t, cacert, certzClient, cert, san, serverAddr, testProfile, &serverCertEntity, &trustBundleEntity) + if !success { + t.Fatalf("%s:Certz rotation failed.", tc.desc) + } + t.Logf("%s:Certz rotation completed!", tc.desc) + + // Replace config with newly added ssl profile after successful rotate. + servers = gnmi.GetAll(t, dut, gnmi.OC().System().GrpcServerAny().Name().State()) + batch := gnmi.SetBatch{} + for _, server := range servers { + gnmi.BatchReplace(&batch, gnmi.OC().System().GrpcServer(server).CertificateId().Config(), testProfile) + } + batch.Set(t, dut) + t.Logf("%s:replaced gNMI config with new ssl profile successfully.", tc.desc) + time.Sleep(10 * time.Second) + + // Verification check of the new connection with the newly rotated certificates. + t.Run("Verification of new connection after successful server certificate rotation", func(t *testing.T) { + if !setupService.PostValidationCheck(t, cacert, ctx, san, serverAddr, username, password, cert) { + t.Fatalf("%s postTestcase service validation failed after successful rotate.", tc.desc) + } + t.Logf("%s postTestcase service validation done after server certificate rotation!", tc.desc) + t.Logf("PASS: %s successfully completed!", tc.desc) + }) + }) + } + t.Logf("Cleanup of test data.") + if setupService.CertCleanup(dirPath) != nil { + t.Fatalf("could not run cert cleanup command.") + } +} From a7b604d3cf74b0f86185971a4034be158a5c50f8 Mon Sep 17 00:00:00 2001 From: priyacj Date: Fri, 19 Jul 2024 13:53:28 -0700 Subject: [PATCH 02/20] Updated README.md under server_certificates --- .../certz/tests/server_certificates/README.md | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100755 feature/security/gnsi/certz/tests/server_certificates/README.md diff --git a/feature/security/gnsi/certz/tests/server_certificates/README.md b/feature/security/gnsi/certz/tests/server_certificates/README.md new file mode 100755 index 00000000000..eed72366c9a --- /dev/null +++ b/feature/security/gnsi/certz/tests/server_certificates/README.md @@ -0,0 +1,111 @@ +# Server Certificate + +## Summary + +Servers must be able to validate a remote client's TLS certificate +and present a valid TLS certificate to the calling clients. + +## Baseline Setup + +### Input Args + +* the set of certificate testdata generated with the mk_cas.sh + script in featureprofiles/feature/security/gnsi/certz/test_data + +### DUT Service setup + +Configure the DUT to enable the following services (that are using gRPC) are +up and require using mTLS for authentication: + + * gNMI + * gNOI + * gNSI + * gRIBI + * P4RT + +Be prepared to load the relevant trust_bundle.pem file for each test +Certificate Authority(CA) under test on the DUT. Each CA has an RSA and ECDSA +form, both must be tested. + +## Tests + +### Certz-2.1 + +Perform these positive tests: + +Test that the server certificates from a set of one CA are able to be validated +and used for authentication to a device when used by a client conneting to each +gRPC service. + +Perform this for both RSA and ECDSA signed CA bundles and certificates. +Perform this for the permutation of 1, 2, 10, 1000 CA +trust_bundle configurations (## indicates the 1, 2, 10, 1000 CA testdata) + + 1) Load the correct key-type trust bundle onto the device and client system: + ca-##/trust_bundle_##_rsa.pem + ca-##/trust_bundle_##_ecdsa.pem + + 2) Load the correct key-type server certificate into the DUT services: + ca-##/server-rsa-key.pem + ca-##/server-rsa-cert.pem + ca-##/server-ecdsa-key.pem + ca-##/server-ecdsa-cert.pem + + 3) Load the correct key-type client certificate into the gRPC client: + ca-##/client-rsa-key.pem + ca-##/client-rsa-cert.pem + ca-##/client-ecdsa-key.pem + ca-##/client-ecdsa-cert.pem + + 4) Validate that the certificate is loaded and useful for inbound connections + to the server by clients. + + 5) Have the client connect to the services on the DUT. + + 6) Validate that the connection is established and the server's certificate + is trusted by the client. + + 7) Validate that the connection is established and the client's certificate is + validated and trusted by the server. + + +### Certz-2.2 + +Perform these negative tests, perform these tests with both the RSA and ECDSA +trust_bundles and certificates. + + 1) Load the correct key type trust_bundle from ca-02 on to the DUT: + ca-02/trust_bundle_02_rsa.pem + ca-02/trust_bundle_02_ecsda.pem + + 2) Load the correct key type client certificate from the ca-01 set into + the test gRPC client: + ca-01/client-rsa-key.pem + ca-01/client-rsa-cert.pem + ca-01/client-ecdsa-key.pem + ca-01/client-ecdsa-cert.pem + + 3) Validate that the certificate is loaded and useful for outbound + client connections. Connect to the service on the DUT. + + 4) Validate that the connection to the remote device is established, + validate the client certificate can not be used (is untrusted) by + service on the DUT. + + 5) Validate that the connection is properly torn down by the DUT. + +## OpenConfig Path and RPC Coverage + +The below yaml defines the OC paths intended to be covered by this test. OC paths used for test setup are not listed here. + +TODO(OCRPC): Record may not be correct or complete + +```yaml +rpcs: + gnsi: + certz.v1.Certz.GetProfileList: +``` + +## Minimum DUT Platform Requirement + +vRX From e2073ed17ca625d59483631d84c1833344c9718e Mon Sep 17 00:00:00 2001 From: priyacj Date: Fri, 19 Jul 2024 14:10:52 -0700 Subject: [PATCH 03/20] modified README --- feature/security/gnsi/certz/tests/server_certificates/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/security/gnsi/certz/tests/server_certificates/README.md b/feature/security/gnsi/certz/tests/server_certificates/README.md index eed72366c9a..8513740f500 100755 --- a/feature/security/gnsi/certz/tests/server_certificates/README.md +++ b/feature/security/gnsi/certz/tests/server_certificates/README.md @@ -1,4 +1,4 @@ -# Server Certificate +# CERTZ-2: Server Certificate ## Summary From c9798979ef325964ce4c526ced9aded00eaf8d59 Mon Sep 17 00:00:00 2001 From: priyacj Date: Fri, 19 Jul 2024 14:28:32 -0700 Subject: [PATCH 04/20] fixed rundata --- .../certz/tests/server_certificates/metadata.textproto | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 feature/security/gnsi/certz/tests/server_certificates/metadata.textproto diff --git a/feature/security/gnsi/certz/tests/server_certificates/metadata.textproto b/feature/security/gnsi/certz/tests/server_certificates/metadata.textproto new file mode 100644 index 00000000000..11c40113aba --- /dev/null +++ b/feature/security/gnsi/certz/tests/server_certificates/metadata.textproto @@ -0,0 +1,7 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "8ab4bc23-04ca-46fe-b055-c52c22251d83" +plan_id: "CERTZ-2" +description: "Server Certificate" +testbed: TESTBED_DUT_ATE_2LINKS From ddc2a66ad6fd416749227a7a1b95c8fd052571db Mon Sep 17 00:00:00 2001 From: priyacj Date: Tue, 23 Jul 2024 14:58:26 -0700 Subject: [PATCH 05/20] updated the README file --- feature/security/gnsi/certz/tests/server_certificates/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/feature/security/gnsi/certz/tests/server_certificates/README.md b/feature/security/gnsi/certz/tests/server_certificates/README.md index 8513740f500..965ef4f0100 100755 --- a/feature/security/gnsi/certz/tests/server_certificates/README.md +++ b/feature/security/gnsi/certz/tests/server_certificates/README.md @@ -104,6 +104,8 @@ TODO(OCRPC): Record may not be correct or complete rpcs: gnsi: certz.v1.Certz.GetProfileList: + certz.v1.Certz.AddProfile: + certz.v1.Certz.Rotate: ``` ## Minimum DUT Platform Requirement From 38b960e784937b81f9fd55113a6d60ee25a74306 Mon Sep 17 00:00:00 2001 From: priyacj Date: Tue, 23 Jul 2024 20:58:45 -0700 Subject: [PATCH 06/20] fixed permisison issue --- feature/security/gnsi/certz/tests/server_certificates/README.md | 0 .../certz/tests/server_certificates/server_certificates_test.go | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 feature/security/gnsi/certz/tests/server_certificates/README.md mode change 100755 => 100644 feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go diff --git a/feature/security/gnsi/certz/tests/server_certificates/README.md b/feature/security/gnsi/certz/tests/server_certificates/README.md old mode 100755 new mode 100644 diff --git a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go old mode 100755 new mode 100644 From 7b1be4e3a01dabb25be3699638c6584860514936 Mon Sep 17 00:00:00 2001 From: priyacj Date: Tue, 23 Jul 2024 21:14:03 -0700 Subject: [PATCH 07/20] fixed few givencomments --- .../gnsi/certz/tests/internal/setup_service/setup_service.go | 5 +++-- .../tests/server_certificates/server_certificates_test.go | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go index 6f75ba08eb9..acd4a702639 100755 --- a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go +++ b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go @@ -201,7 +201,9 @@ func CreateCertChainFromTrustBundle(fileName string) *certzpb.CertificateChain { } trust = append(trust, p) } - if len(trust) > 0 { + if len(trust) == 0 { + return &certzpb.CertificateChain{} + } else len(trust) > 0 { var prevCert *certzpb.CertificateChain var bundleToReturn *certzpb.CertificateChain for i := len(trust) - 1; i >= 0; i-- { @@ -223,7 +225,6 @@ func CreateCertChainFromTrustBundle(fileName string) *certzpb.CertificateChain { } return bundleToReturn } - return &certzpb.CertificateChain{} } // CertzRotate function to request the client certificate rotation. diff --git a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go index d0143ce8de8..ab7400c7057 100644 --- a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go +++ b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go @@ -67,7 +67,6 @@ func TestMain(m *testing.M) { // TestServerCertTcTwo tests the server certificates from a set of one CA are able to be validated and // used for authentication to a device when used by a client connecting to each // gRPC service. - func TestServerCert(t *testing.T) { dut := ondatra.DUT(t, "dut") @@ -221,7 +220,6 @@ func TestServerCert(t *testing.T) { } batch.Set(t, dut) t.Logf("%s:replaced gNMI config with new ssl profile successfully.", tc.desc) - time.Sleep(10 * time.Second) // Verification check of the new connection with the newly rotated certificates. t.Run("Verification of new connection after successful server certificate rotation", func(t *testing.T) { From de2ed06c91f17e137e6d756628b959be8a6158e8 Mon Sep 17 00:00:00 2001 From: priyacj Date: Tue, 23 Jul 2024 21:17:26 -0700 Subject: [PATCH 08/20] fixed the executable bit for this file --- .../gnsi/certz/tests/internal/setup_service/setup_service.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go diff --git a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go old mode 100755 new mode 100644 From 709d85aee81e84768e342926d7ca589871069bae Mon Sep 17 00:00:00 2001 From: priyacj Date: Tue, 23 Jul 2024 21:33:21 -0700 Subject: [PATCH 09/20] added comments --- .../certz/tests/internal/setup_service/setup_service.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go index acd4a702639..fe9871e0769 100644 --- a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go +++ b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go @@ -227,7 +227,7 @@ func CreateCertChainFromTrustBundle(fileName string) *certzpb.CertificateChain { } } -// CertzRotate function to request the client certificate rotation. +// CertzRotate function to request the client certificate rotation and returns true on successful rotation. func CertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb.CertzClient, cert tls.Certificate, san, serverAddr, profileID string, entities ...*certzpb.Entity) bool { if len(entities) == 0 { t.Logf("At least one entity required for Rotate request.") @@ -280,7 +280,7 @@ func CertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb.CertzC return true } -// ServerCertzRotate function to request the server certificate rotation. +// ServerCertzRotate function to request the server certificate rotation and returns true on successful rotation. func ServerCertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb.CertzClient, cert tls.Certificate, ctx context.Context, dut *ondatra.DUTDevice, san, serverAddr, profileID string, entities ...*certzpb.Entity) bool { if len(entities) == 0 { t.Logf("At least one entity required for Rotate request.") @@ -289,7 +289,7 @@ func ServerCertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb. uploadRequest := &certzpb.UploadRequest{Entities: entities} rotateRequest := &certzpb.RotateCertificateRequest_Certificates{Certificates: uploadRequest} rotateCertRequest := &certzpb.RotateCertificateRequest{ - ForceOverwrite: false, + ForceOverwrite: false SslProfileId: profileID, RotateRequest: rotateRequest} rotateRequestClient, err := certzClient.Rotate(context.Background()) @@ -402,7 +402,7 @@ func CertCleanup(dirPath string) error { return err } -// ReadDecodeServerCertificate function to read and decode server certificates to extract the SAN +// ReadDecodeServerCertificate function to read and decode server certificates to extract the SubjectAltName. func ReadDecodeServerCertificate(t *testing.T, serverCertzFile string) (san string) { sc, err := os.ReadFile(serverCertzFile) if err != nil { From c599cdc8594de6816a7daa701608743278d43c1f Mon Sep 17 00:00:00 2001 From: priyacj Date: Wed, 24 Jul 2024 07:33:25 -0700 Subject: [PATCH 10/20] added few comment lines --- .../certz/tests/internal/setup_service/setup_service.go | 7 ++++--- .../tests/server_certificates/server_certificates_test.go | 1 - 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go index fe9871e0769..d57dae4cc81 100644 --- a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go +++ b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go @@ -201,9 +201,10 @@ func CreateCertChainFromTrustBundle(fileName string) *certzpb.CertificateChain { } trust = append(trust, p) } + //a valid check for trust not empty if len(trust) == 0 { - return &certzpb.CertificateChain{} - } else len(trust) > 0 { + return &certzpb.CertificateChain{} + } else { var prevCert *certzpb.CertificateChain var bundleToReturn *certzpb.CertificateChain for i := len(trust) - 1; i >= 0; i-- { @@ -289,7 +290,7 @@ func ServerCertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb. uploadRequest := &certzpb.UploadRequest{Entities: entities} rotateRequest := &certzpb.RotateCertificateRequest_Certificates{Certificates: uploadRequest} rotateCertRequest := &certzpb.RotateCertificateRequest{ - ForceOverwrite: false + ForceOverwrite: false, SslProfileId: profileID, RotateRequest: rotateRequest} rotateRequestClient, err := certzClient.Rotate(context.Background()) diff --git a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go index ab7400c7057..20e569ec9d8 100644 --- a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go +++ b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go @@ -20,7 +20,6 @@ import ( "crypto/x509" "os" "testing" - "time" setupService "github.com/openconfig/featureprofiles/feature/security/gnsi/certz/tests/internal/setup_service" "github.com/openconfig/featureprofiles/internal/fptest" From c82cab669bc8cc34f85fd344febca1be1cbed5bd Mon Sep 17 00:00:00 2001 From: priyacj Date: Sun, 28 Jul 2024 09:07:36 -0700 Subject: [PATCH 11/20] added certz2.2 testcase --- .../internal/setup_service/setup_service.go | 243 +++++------------- .../server_certificates_test.go | 104 +++++--- 2 files changed, 133 insertions(+), 214 deletions(-) diff --git a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go index d57dae4cc81..583c4e5c01c 100644 --- a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go +++ b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go @@ -23,7 +23,8 @@ import ( "crypto/x509" "encoding/pem" "fmt" - "io" + + //"io" "os" "os/exec" "testing" @@ -37,7 +38,6 @@ import ( "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" "github.com/openconfig/ondatra/knebind/creds" - p4rtpb "github.com/p4lang/p4runtime/go/p4/v1" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -328,7 +328,7 @@ func ServerCertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb. success := false //Trying for 60s for the connection to succeed. for i := 0; i < 6; i++ { - success = VerifyGnsi(t, caCert, context.Background(), san, serverAddr, username, password, cert) + success = VerifyGnsi(t, caCert, san, serverAddr, username, password, cert) if success { break } @@ -360,44 +360,44 @@ func ServerCertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb. } // CertGeneration function to create test data for use in TLS tests. -func CertGeneration(dirPath string) error { +func CertGeneration(t *testing.T, dirPath string) error { cmd := exec.Cmd{ Path: "./mk_cas.sh", Stdout: os.Stdout, Stderr: os.Stderr, } cmd.Dir = dirPath - fmt.Printf("Executing cert generation command %v", cmd) + t.Logf("Executing cert generation command %v.", cmd) err := cmd.Start() if err != nil { - fmt.Printf("unable to run cert generation command:%v", err) + t.Logf("Cert generation command failed with error:%v.", err) return err } err = cmd.Wait() if err != nil { - fmt.Printf("unable to run cert generation command:%v", err) + t.Logf("Failed to run cert generation command during wait with error:%v.", err) return err } return err } // CertCleanup function to clean out the CA content under test_data. -func CertCleanup(dirPath string) error { +func CertCleanup(t *testing.T, dirPath string) error { cmd := exec.Cmd{ Path: "./cleanup.sh", Stdout: os.Stdout, Stderr: os.Stderr, } cmd.Dir = dirPath - fmt.Printf("Executing cleanup command") + t.Logf("Executing cleanup command") err := cmd.Start() if err != nil { - fmt.Printf("unable to run testdata cleanup command:%v", err) + t.Logf("Testdata cleanup command failed with error:%v.", err) return err } err = cmd.Wait() if err != nil { - fmt.Printf("unable to run testdata cleanup command:%v", err) + t.Logf("Testdata cleanup command failed during wait with the error:%v.", err) return err } return err @@ -407,7 +407,7 @@ func CertCleanup(dirPath string) error { func ReadDecodeServerCertificate(t *testing.T, serverCertzFile string) (san string) { sc, err := os.ReadFile(serverCertzFile) if err != nil { - t.Fatalf("Failed to read certificate: %v", err) + t.Fatalf("Failed to read certificate with error: %v.", err) } block, _ := pem.Decode(sc) if block == nil { @@ -415,19 +415,18 @@ func ReadDecodeServerCertificate(t *testing.T, serverCertzFile string) (san stri } sCert, err := x509.ParseCertificate(block.Bytes) if err != nil { - t.Fatalf("Failed to parse certificate: %v", err) + t.Fatalf("Failed to parse certificate with error: %v.", err) } san = sCert.DNSNames[0] - t.Logf("SAN :%s", san) + t.Logf("ServerAltName:%s.", san) if sn != san { - t.Fatalf("Server name validation failed for %s.", serverCertzFile) + t.Fatalf("ServerAltName validation failed for %s.", serverCertzFile) } return san } // VerifyGnsi function to validate the gNSI service RPC after successful rotation. -func VerifyGnsi(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, serverAddr, username, password string, cert tls.Certificate) bool { - +func VerifyGnsi(t *testing.T, caCert *x509.CertPool, san, serverAddr, username, password string, cert tls.Certificate) bool { credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( &tls.Config{ Certificates: []tls.Certificate{cert}, @@ -437,14 +436,15 @@ func VerifyGnsi(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, s creds := &rpcCredentials{&creds.UserPass{Username: username, Password: password}} credOpts = append(credOpts, grpc.WithPerRPCCredentials(creds)) target := fmt.Sprintf("%s:%d", serverAddr, 9339) + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() conn, err := grpc.NewClient(target, credOpts...) if err != nil { - t.Errorf("VerifyGnsi:gRPC NewClient failed to %q with err %v", target, err) + t.Errorf("%sVerifyGnsi:gRPC NewClient failed to %q with err %v", time.Now().String(), target, err) return false } t.Logf("Connection state: %v.", conn.GetState().String()) defer conn.Close() - authzClient := authzpb.NewAuthzClient(conn) rsp, err := authzClient.Get(ctx, &authzpb.GetRequest{}) if err != nil { @@ -462,8 +462,7 @@ func VerifyGnsi(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, s } // VerifyGnoi function to validate the gNOI service RPC after successful rotation. -func VerifyGnoi(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, serverAddr, username, password string, cert tls.Certificate) bool { - +func VerifyGnoi(t *testing.T, caCert *x509.CertPool, san, serverAddr, username, password string, cert tls.Certificate) bool { credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( &tls.Config{ Certificates: []tls.Certificate{cert}, @@ -473,14 +472,14 @@ func VerifyGnoi(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, s creds := &rpcCredentials{&creds.UserPass{Username: username, Password: password}} credOpts = append(credOpts, grpc.WithPerRPCCredentials(creds)) target := fmt.Sprintf("%s:%d", serverAddr, 9339) + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() conn, err := grpc.NewClient(target, credOpts...) if err != nil { t.Errorf("VerifyGnoi : gRPC NewClient failed to %q with err %v", target, err) return false } - t.Logf("Connection state: %v.", conn.GetState().String()) defer conn.Close() - sysClient := spb.NewSystemClient(conn) _, err = sysClient.Ping(ctx, &spb.PingRequest{}) if err != nil { @@ -492,8 +491,7 @@ func VerifyGnoi(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, s } // VerifyGnmi function to validate the gNMI service RPC after successful rotation. -func VerifyGnmi(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, serverAddr, username, password string, cert tls.Certificate) bool { - +func VerifyGnmi(t *testing.T, caCert *x509.CertPool, san, serverAddr, username, password string, cert tls.Certificate) bool { credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( &tls.Config{ Certificates: []tls.Certificate{cert}, @@ -503,59 +501,28 @@ func VerifyGnmi(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, s creds := &rpcCredentials{&creds.UserPass{Username: username, Password: password}} credOpts = append(credOpts, grpc.WithPerRPCCredentials(creds)) target := fmt.Sprintf("%s:%d", serverAddr, 9339) + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() conn, err := grpc.NewClient(target, credOpts...) if err != nil { t.Errorf("VerifyGnmi: gRPC NewClient failed to %q with err %v", target, err) return false } - t.Logf("Connection state: %v.", conn.GetState().String()) defer conn.Close() - gnmiClient := gnmipb.NewGNMIClient(conn) - t.Logf("Sending gNMI subscribe request.") - //stream, err := gnmiClient.Subscribe(ctx) - stream, err := gnmiClient.Subscribe(context.Background()) - defer stream.CloseSend() - - if err != nil { - t.Logf("gNMI subscribe request failed with err: %v", err) - return false - } - - sub := &gnmipb.SubscribeRequest{ - Request: &gnmipb.SubscribeRequest_Subscribe{ - Subscribe: &gnmipb.SubscriptionList{ - Subscription: []*gnmipb.Subscription{ - { - Path: &gnmipb.Path{ - Elem: []*gnmipb.PathElem{{Name: "system"}, {Name: "state"}, {Name: "software-version"}}, - }, - }, - }, - }, - }, - } - t.Logf("Sending gNMI subscribe request: %s", sub) - err = stream.Send(sub) + t.Logf("%s:Sending gNMI Capability request.", time.Now().String()) + response, err := gnmiClient.Capabilities(ctx, &gnmipb.CapabilityRequest{}) if err != nil { - t.Logf("Failed to subscribe with error: %s", err) + t.Logf("gNMI Capability request failed with err: %v", err) return false } - response, err := stream.Recv() - if err != nil { - if err != io.EOF { - t.Logf("Error received from the server: %s", err) - return false - } - } - t.Logf("gNMI response: %s", response) + t.Logf("VerifyGnmi:gNMI response: %s", response) conn.Close() return true } // VerifyGribi function to validate the gRIBI service RPC after successful rotation. -func VerifyGribi(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, serverAddr, username, password string, cert tls.Certificate) bool { - +func VerifyGribi(t *testing.T, caCert *x509.CertPool, san, serverAddr, username, password string, cert tls.Certificate) bool { credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( &tls.Config{ Certificates: []tls.Certificate{cert}, @@ -565,18 +532,18 @@ func VerifyGribi(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, creds := &rpcCredentials{&creds.UserPass{Username: username, Password: password}} credOpts = append(credOpts, grpc.WithPerRPCCredentials(creds)) target := fmt.Sprintf("%s:%d", serverAddr, 9340) + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() conn, err := grpc.NewClient(target, credOpts...) if err != nil { - t.Errorf("VerifyGnmi: gRPC NewClient failed to %q with error:%v", target, err) + t.Errorf("VerifyGribi: gRPC NewClient failed to %q with error:%v", target, err) return false } - t.Logf("Connection state: %v.", conn.GetState().String()) defer conn.Close() - gRibiClient := gribipb.NewGRIBIClient(conn) _, err = gRibiClient.Get(ctx, &gribipb.GetRequest{}) if err != nil { - t.Logf("Unable to connect GribiClient %v", err) + t.Logf("Failed to connect GribiClient with error:%v.", err) return false } conn.Close() @@ -584,8 +551,7 @@ func VerifyGribi(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, } // VerifyP4rt function to validate the P4rt service RPC after successful rotation. -func VerifyP4rt(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, serverAddr, username, password string, cert tls.Certificate) bool { - +func VerifyP4rt(t *testing.T, caCert *x509.CertPool, san, serverAddr, username, password string, cert tls.Certificate) bool { credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( &tls.Config{ Certificates: []tls.Certificate{cert}, @@ -595,46 +561,46 @@ func VerifyP4rt(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, s creds := &rpcCredentials{&creds.UserPass{Username: username, Password: password}} credOpts = append(credOpts, grpc.WithPerRPCCredentials(creds)) target := fmt.Sprintf("%s:%d", serverAddr, 9559) + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() conn, err := grpc.NewClient(target, credOpts...) if err != nil { - t.Errorf("VerifyP4rt : gRPC NewClient failed to %q", target) + t.Errorf("VerifyP4rt : gRPC NewClient failed to %q with error %v.", target, err) } - t.Logf("Connection state: %v.", conn.GetState().String()) defer conn.Close() - p4RtClient := p4rtpb.NewP4RuntimeClient(conn) _, err = p4RtClient.Capabilities(ctx, &p4rtpb.CapabilitiesRequest{}) if err != nil { - t.Logf("Unable to connect P4rtClient %v", err) + t.Logf("Failed to connect P4rtClient with error %v.", err) return false } conn.Close() return true } -// PreInitCheck function to do a validation of gNMI/gNOI/gRIBI/p4RT services before certz rotation. +// PreInitCheck function to dial gNMI/gNOI/gRIBI/p4RT services before certz rotation. func PreInitCheck(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice) bool { gnmiC, err := dut.RawAPIs().BindingDUT().DialGNMI(ctx) if err != nil { - t.Fatalf("Failed to create gNMI Connection with error: %v", err) + t.Fatalf("%s Failed to dial gNMI Connection with error: %v.", time.Now().String(), err) } - t.Logf("Precheck:gNMI connection is successful %v", gnmiC) + t.Logf("Precheck:gNMI dial is successful %v", gnmiC) gribiC, err := dut.RawAPIs().BindingDUT().DialGRIBI(ctx) if err != nil { - t.Fatalf("Failed to create gRIBI Connection with error: %v", err) + t.Fatalf("%s Failed to dial gRIBI Connection with error: %v.", time.Now().String(), err) } - t.Logf("Precheck:gRIBI connection is successful %v", gribiC) + t.Logf("Precheck:gRIBI dial is successful %v", gribiC) gnoiC, err := dut.RawAPIs().BindingDUT().DialGNOI(ctx) if err != nil { - t.Fatalf("Faield to create gNOI Connection with error: %v", err) + t.Fatalf("%s Failed to dial gNOI Connection with error: %v.", time.Now().String(), err) } - t.Logf("Precheck:gNOI connection is successful %v", gnoiC) + t.Logf("Precheck:gNOI dial is successful %v", gnoiC) p4rtC, err := dut.RawAPIs().BindingDUT().DialP4RT(ctx) if err != nil { - t.Fatalf("Failed to create p4RT Connection with error: %v", err) + t.Fatalf("%s Failed to dial p4RT Connection with error: %v.", time.Now().String(), err) } - t.Logf("Precheck:p4RT connection is successful %v", p4rtC) + t.Logf("Precheck:p4RT dial is successful %v", p4rtC) return true } @@ -648,105 +614,28 @@ func GetSslProfilelist(ctx context.Context, t *testing.T, certzClient certzpb.Ce } // PostValidationCheck function to do a validation of all services after certz rotation. -func PostValidationCheck(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, serverAddr, username, password string, cert tls.Certificate) bool { - - if !VerifyGnsi(t, caCert, ctx, san, serverAddr, username, password, cert) { - t.Fatalf("Failed with new gNSI Connection: got false, want true.") - } - t.Logf("New gNSI connection successfully completed.") - - if !VerifyGnoi(t, caCert, ctx, san, serverAddr, username, password, cert) { - t.Logf("Failed with new gNOI Connection: got false, want true") - return false - } - t.Logf("New gNOI connection successfully completed.") - - if !VerifyGribi(t, caCert, ctx, san, serverAddr, username, password, cert) { - t.Logf("Failed with new gRIBI Connection: got false, want true.") - return false - } - t.Logf("New gRIBI connection successfully completed.") - if !VerifyP4rt(t, caCert, ctx, san, serverAddr, username, password, cert) { - t.Logf("Failed with new P4rt Connection: got false, want true.") - return false - } - if !VerifyGnmi(t, caCert, ctx, san, serverAddr, username, password, cert) { - t.Logf("Failed with new gNMI Connection: got false, want true.") - return false - } - t.Logf("New gNMI connection successfully completed.") - return true -} - -// TestNewConnection function to validate the connection for any gRPC serviceafter successful rotation. -func TestNewConnection(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, serverAddr, username, password string, cert tls.Certificate) bool { - - credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( - &tls.Config{ - Certificates: []tls.Certificate{cert}, - RootCAs: caCert, - ServerName: san, - }))} - creds := &rpcCredentials{&creds.UserPass{Username: username, Password: password}} - credOpts = append(credOpts, grpc.WithPerRPCCredentials(creds)) - target := fmt.Sprintf("%s:%d", serverAddr, 9339) - conn, err := grpc.NewClient(target, credOpts...) - t.Logf("Err response: %v.", err) - if err != nil { - t.Logf("gRPC NewClient failed with mismatch certificate as expected to %q with error %s.", target, err) - return false - } - t.Logf("Connection state: %v.", conn.GetState().String()) - defer conn.Close() - - authzClient := authzpb.NewAuthzClient(conn) - t.Logf("Authzclient Get Request.") - rsp, err := authzClient.Get(ctx, &authzpb.GetRequest{}) - t.Logf("gNSI authz get response is %s", rsp) - if err != nil { - statusError, _ := status.FromError(err) - if statusError.Code() == codes.FailedPrecondition { - t.Logf("Expected error FAILED_PRECONDITION seen for authz Get Request.") - } else { - t.Logf("Unexpected error during authz Get Request :%v.", err) - return false - } +func PostValidationCheck(t *testing.T, caCert *x509.CertPool, expected_result bool, san, serverAddr, username, password string, cert tls.Certificate) bool { + t.Logf("%s:Verifying New gNSI connection.", time.Now().String()) + result := VerifyGnsi(t, caCert, san, serverAddr, username, password, cert) + if expected_result != result { + t.Fatalf("Failed with new gNSI Connection: got %v, want %v.", result, expected_result) } - conn.Close() - return true -} - -// MismatchPostValidationCheck function to do a validation of all services after certz rotation. -func MismatchPostValidationCheck(t *testing.T, caCert *x509.CertPool, ctx context.Context, san, serverAddr, username, password string, cert tls.Certificate) bool { - - result := VerifyGnsi(t, caCert, ctx, san, serverAddr, username, password, cert) - if result { - t.Errorf("FAIL:new gNSI Connection worked with mimsatch trustbundle - got %v, want false.", result) + t.Logf("%s:Verifying New gNOI connection.", time.Now().String()) + result = VerifyGnoi(t, caCert, san, serverAddr, username, password, cert) + if expected_result != result { + t.Fatalf("Failed with new gNOI Connection: got false, want %v", expected_result) } - t.Logf("PASS: new gNSI connection with mismatch trustbundle is not working as expected- got %v, want false.", result) - - result = VerifyGnoi(t, caCert, ctx, san, serverAddr, username, password, cert) - if result { - t.Errorf("FAIL:new gNOI Connection worked with mismatch trustbundle: got %v, want false", result) + t.Logf("%s:Verifying New gRIBI connection.", time.Now().String()) + if expected_result != VerifyGribi(t, caCert, san, serverAddr, username, password, cert) { + t.Fatalf("Failed with new gRIBI Connection: got false, want %v.", expected_result) } - t.Logf("PASS:New gNOI connection with mismatch trustbundle is not working as expected- got %v, want false.", result) - - result = VerifyGribi(t, caCert, ctx, san, serverAddr, username, password, cert) - if result { - t.Errorf("FAIL:new gRIBI Connection worked with mismatch trustbundle: got %v, want false", result) - } - t.Logf("PASS:New gRIBI connection with mismatch trustbundle is not working as expected- got %v, want false.", result) - - result = VerifyP4rt(t, caCert, ctx, san, serverAddr, username, password, cert) - if result { - t.Errorf("FAIL: new P4rt Connection worked with mismatch trustbundle: got %v, want false", result) + t.Logf("%s:Verifying New P4rt connection.", time.Now().String()) + if expected_result != VerifyP4rt(t, caCert, san, serverAddr, username, password, cert) { + t.Fatalf("Failed with new P4rt Connection: got false, want %v.", expected_result) } - t.Logf("PASS:New P4rt connection with mismatch trustbundle is not working as expected- got %v, want false.", result) - - result = VerifyGnmi(t, caCert, ctx, san, serverAddr, username, password, cert) - if result { - t.Errorf("FAIL: new gNMI Connection worked with mismatch trustbundle: got %v, want false", result) + t.Logf("%s:Verifying New gNMI connection.", time.Now().String()) + if expected_result != VerifyGnmi(t, caCert, san, serverAddr, username, password, cert) { + t.Fatalf("Failed with new gNMI Connection: got false, want %v.", expected_result) } - t.Logf("PASS:New gNMI connection with mismatch trustbundle is not working as expected- got %v, want false.", result) return true } diff --git a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go index 20e569ec9d8..61ea6fc6757 100644 --- a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go +++ b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go @@ -20,6 +20,7 @@ import ( "crypto/x509" "os" "testing" + "time" setupService "github.com/openconfig/featureprofiles/feature/security/gnsi/certz/tests/internal/setup_service" "github.com/openconfig/featureprofiles/internal/fptest" @@ -36,11 +37,12 @@ const ( ) var ( - testProfile = "newprofile" - serverAddr string - username = "certzuser" - password = "certzpasswd" - servers []string + testProfile = "newprofile" + serverAddr string + username = "certzuser" + password = "certzpasswd" + servers []string + expected_result bool ) // createUser function to add an user in admin role. @@ -71,36 +73,34 @@ func TestServerCert(t *testing.T) { dut := ondatra.DUT(t, "dut") serverAddr = dut.Name() if !createUser(t, dut, username, password) { - t.Fatalf("Failed to create certz user.") + t.Fatalf("%s: Failed to create certz user.", time.Now().String()) } - t.Logf("Validation of all services that are using gRPC before certz rotation.") if !setupService.PreInitCheck(context.Background(), t, dut) { - t.Fatalf("Failed in the preInit checks.") + t.Fatalf("%s: Failed in the preInit checks.", time.Now().String()) } ctx := context.Background() gnsiC, err := dut.RawAPIs().BindingDUT().DialGNSI(ctx) if err != nil { - t.Fatalf("Failed to create gNSI Connection %v", err) + t.Fatalf("%s: Failed to create gNSI Connection %v", time.Now().String(), err) } - t.Logf("Precheck:gNSI connection is successful %v", gnsiC) + t.Logf("%s Precheck:gNSI connection is successful %v", time.Now().String(), gnsiC) - t.Logf("Creation of test data.") - if setupService.CertGeneration(dirPath) != nil { - t.Fatalf("Failed to generate the testdata certificates.") + t.Logf("%s:Creation of test data.", time.Now().String()) + if setupService.CertGeneration(t, dirPath) != nil { + t.Fatalf("%s:Failed to generate the testdata certificates.", time.Now().String()) } - certzClient := gnsiC.Certz() - t.Logf("Precheck:baseline ssl profile list") + t.Logf("%s Precheck:checking baseline ssl profile list.", time.Now().String()) setupService.GetSslProfilelist(ctx, t, certzClient, &certzpb.GetProfileListRequest{}) - t.Logf("Adding new empty ssl profile ID.") + t.Logf("%s:Adding new empty ssl profile ID.", time.Now().String()) addProfileResponse, err := certzClient.AddProfile(ctx, &certzpb.AddProfileRequest{SslProfileId: testProfile}) if err != nil { - t.Fatalf("Add profile request failed with %v!", err) + t.Fatalf("%s:Add profile request failed with %v! ", time.Now().String(), err) } - t.Logf("AddProfileResponse: %v", addProfileResponse) - t.Logf("Getting the ssl profile list after new ssl profile addition.") + t.Logf("%s AddProfileResponse: %v", time.Now().String(), addProfileResponse) + t.Logf("%s: Getting the ssl profile list after new ssl profile addition.", time.Now().String()) setupService.GetSslProfilelist(ctx, t, certzClient, &certzpb.GetProfileListRequest{}) cases := []struct { @@ -110,6 +110,7 @@ func TestServerCert(t *testing.T) { trustBundleFile string clientCertFile string clientKeyFile string + mismatch bool }{ { desc: "Certz2.1:Load server certificate of rsa keytype with 1 CA configuration", @@ -175,6 +176,24 @@ func TestServerCert(t *testing.T) { clientCertFile: dirPath + "ca-1000/client-ecdsa-a-cert.pem", clientKeyFile: dirPath + "ca-1000/client-ecdsa-a-key.pem", }, + { + desc: "Certz2.2:Load the rsa trust_bundle from ca-02 with mismatching key type rsa server certificate from ca-01", + serverCertFile: dirPath + "ca-01/server-rsa-a-cert.pem", + serverKeyFile: dirPath + "ca-01/server-rsa-a-key.pem", + trustBundleFile: dirPath + "ca-02/trust_bundle_02_rsa.pem", + clientCertFile: dirPath + "ca-01/client-rsa-a-cert.pem", + clientKeyFile: dirPath + "ca-01/client-rsa-a-key.pem", + mismatch: true, + }, + { + desc: "Certz2.2:Load the ecdsa trust_bundle from ca-02 with mismatching key type ecdsa server certificate from ca-01", + serverCertFile: dirPath + "ca-01/server-ecdsa-a-cert.pem", + serverKeyFile: dirPath + "ca-01/server-ecdsa-a-key.pem", + trustBundleFile: dirPath + "ca-02/trust_bundle_02_ecdsa.pem", + clientCertFile: dirPath + "ca-01/client-ecdsa-a-cert.pem", + clientKeyFile: dirPath + "ca-01/client-ecdsa-a-key.pem", + mismatch: true, + }, } for _, tc := range cases { @@ -189,27 +208,25 @@ func TestServerCert(t *testing.T) { trustCertChain := setupService.CreateCertChainFromTrustBundle(tc.trustBundleFile) trustBundleEntity := setupService.CreateCertzEntity(t, setupService.EntityTypeTrustBundle, trustCertChain, "cabundle") - cert, err := tls.LoadX509KeyPair(tc.clientCertFile, tc.clientKeyFile) if err != nil { - t.Fatalf("Failed to load client cert: %v", err) + t.Fatalf("%s Failed to load client cert: %v", time.Now().String(), err) } - cacert := x509.NewCertPool() cacertBytes, err := os.ReadFile(tc.trustBundleFile) if err != nil { - t.Fatalf("Failed to read ca bundle :%v", err) + t.Fatalf("%s Failed to read ca bundle :%v", time.Now().String(), err) } if ok := cacert.AppendCertsFromPEM(cacertBytes); !ok { - t.Fatalf("Failed to parse %v", tc.trustBundleFile) + t.Fatalf("%s Failed to parse %v", time.Now().String(), tc.trustBundleFile) } certzClient := gnsiC.Certz() success := setupService.CertzRotate(t, cacert, certzClient, cert, san, serverAddr, testProfile, &serverCertEntity, &trustBundleEntity) if !success { - t.Fatalf("%s:Certz rotation failed.", tc.desc) + t.Fatalf("%s %s:Certz rotation failed.", time.Now().String(), tc.desc) } - t.Logf("%s:Certz rotation completed!", tc.desc) + t.Logf("%s %s:Certz rotation completed!", time.Now().String(), tc.desc) // Replace config with newly added ssl profile after successful rotate. servers = gnmi.GetAll(t, dut, gnmi.OC().System().GrpcServerAny().Name().State()) @@ -218,20 +235,33 @@ func TestServerCert(t *testing.T) { gnmi.BatchReplace(&batch, gnmi.OC().System().GrpcServer(server).CertificateId().Config(), testProfile) } batch.Set(t, dut) - t.Logf("%s:replaced gNMI config with new ssl profile successfully.", tc.desc) - - // Verification check of the new connection with the newly rotated certificates. - t.Run("Verification of new connection after successful server certificate rotation", func(t *testing.T) { - if !setupService.PostValidationCheck(t, cacert, ctx, san, serverAddr, username, password, cert) { - t.Fatalf("%s postTestcase service validation failed after successful rotate.", tc.desc) - } - t.Logf("%s postTestcase service validation done after server certificate rotation!", tc.desc) - t.Logf("PASS: %s successfully completed!", tc.desc) - }) + t.Logf("%s %s:replaced gNMI config with new ssl profile successfully.", time.Now().String(), tc.desc) + + switch tc.mismatch { + case true: + t.Run("Verification of new connection after mismatch trustbundle server certificate rotation", func(t *testing.T) { + result := setupService.PostValidationCheck(t, cacert, expected_result, san, serverAddr, username, password, cert) + if !result { + t.Fatalf("%s postTestcase service validation failed with mismatch trustbundle from ca-02 - got %v , want %v", tc.desc, result, expected_result) + } + t.Logf("postTestcase service validation done for mismatch trustbundle from ca-02!") + t.Logf("PASS: %s successfully completed!", tc.desc) + }) + case false: + expected_result := true + t.Run("Verification of new connection after successful server certificate rotation", func(t *testing.T) { + result := setupService.PostValidationCheck(t, cacert, expected_result, san, serverAddr, username, password, cert) + if !result { + t.Fatalf("%s postTestcase service validation failed after successful rotate -got %v, want %v .", tc.desc, result, expected_result) + } + t.Logf("%s postTestcase service validation done after server certificate rotation!", tc.desc) + t.Logf("PASS: %s successfully completed!", tc.desc) + }) + } }) } t.Logf("Cleanup of test data.") - if setupService.CertCleanup(dirPath) != nil { + if setupService.CertCleanup(t, dirPath) != nil { t.Fatalf("could not run cert cleanup command.") } } From b224384acf0bfbafc246a0f3257ed09286dce06b Mon Sep 17 00:00:00 2001 From: priyacj Date: Wed, 20 Nov 2024 12:47:13 -0800 Subject: [PATCH 12/20] fixed deprecated grpc.WithBlock:DialOption --- .../tests/internal/setup_service/setup_service.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go index 583c4e5c01c..6ea83342b4c 100644 --- a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go +++ b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go @@ -427,7 +427,7 @@ func ReadDecodeServerCertificate(t *testing.T, serverCertzFile string) (san stri // VerifyGnsi function to validate the gNSI service RPC after successful rotation. func VerifyGnsi(t *testing.T, caCert *x509.CertPool, san, serverAddr, username, password string, cert tls.Certificate) bool { - credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( + credOpts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS( &tls.Config{ Certificates: []tls.Certificate{cert}, RootCAs: caCert, @@ -463,7 +463,7 @@ func VerifyGnsi(t *testing.T, caCert *x509.CertPool, san, serverAddr, username, // VerifyGnoi function to validate the gNOI service RPC after successful rotation. func VerifyGnoi(t *testing.T, caCert *x509.CertPool, san, serverAddr, username, password string, cert tls.Certificate) bool { - credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( + credOpts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS( &tls.Config{ Certificates: []tls.Certificate{cert}, RootCAs: caCert, @@ -492,7 +492,7 @@ func VerifyGnoi(t *testing.T, caCert *x509.CertPool, san, serverAddr, username, // VerifyGnmi function to validate the gNMI service RPC after successful rotation. func VerifyGnmi(t *testing.T, caCert *x509.CertPool, san, serverAddr, username, password string, cert tls.Certificate) bool { - credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( + credOpts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS( &tls.Config{ Certificates: []tls.Certificate{cert}, RootCAs: caCert, @@ -523,7 +523,7 @@ func VerifyGnmi(t *testing.T, caCert *x509.CertPool, san, serverAddr, username, // VerifyGribi function to validate the gRIBI service RPC after successful rotation. func VerifyGribi(t *testing.T, caCert *x509.CertPool, san, serverAddr, username, password string, cert tls.Certificate) bool { - credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( + credOpts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS( &tls.Config{ Certificates: []tls.Certificate{cert}, RootCAs: caCert, @@ -552,7 +552,7 @@ func VerifyGribi(t *testing.T, caCert *x509.CertPool, san, serverAddr, username, // VerifyP4rt function to validate the P4rt service RPC after successful rotation. func VerifyP4rt(t *testing.T, caCert *x509.CertPool, san, serverAddr, username, password string, cert tls.Certificate) bool { - credOpts := []grpc.DialOption{grpc.WithBlock(), grpc.WithTransportCredentials(credentials.NewTLS( + credOpts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS( &tls.Config{ Certificates: []tls.Certificate{cert}, RootCAs: caCert, From da133b187a9700c7af1de712e029857da8e54a09 Mon Sep 17 00:00:00 2001 From: priyacj Date: Sun, 24 Nov 2024 22:06:12 -0800 Subject: [PATCH 13/20] fixed all the comments --- .../internal/setup_service/setup_service.go | 79 ++++--------------- .../server_certificates_test.go | 66 ++++++++-------- 2 files changed, 47 insertions(+), 98 deletions(-) diff --git a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go index 6ea83342b4c..5fa6bcc5d59 100644 --- a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go +++ b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go @@ -1,4 +1,4 @@ -// Copyright 2023 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,8 +23,6 @@ import ( "crypto/x509" "encoding/pem" "fmt" - - //"io" "os" "os/exec" "testing" @@ -50,6 +48,7 @@ var ( password = "certzpasswd" sn = "role001.pop55.net.example.com" servers []string + retries int ) type rpcCredentials struct { @@ -228,61 +227,8 @@ func CreateCertChainFromTrustBundle(fileName string) *certzpb.CertificateChain { } } -// CertzRotate function to request the client certificate rotation and returns true on successful rotation. -func CertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb.CertzClient, cert tls.Certificate, san, serverAddr, profileID string, entities ...*certzpb.Entity) bool { - if len(entities) == 0 { - t.Logf("At least one entity required for Rotate request.") - return false - } - uploadRequest := &certzpb.UploadRequest{Entities: entities} - rotateRequest := &certzpb.RotateCertificateRequest_Certificates{Certificates: uploadRequest} - rotateCertRequest := &certzpb.RotateCertificateRequest{ - ForceOverwrite: false, - SslProfileId: profileID, - RotateRequest: rotateRequest} - rotateRequestClient, err := certzClient.Rotate(context.Background()) - defer rotateRequestClient.CloseSend() - if err != nil { - t.Fatalf("Error creating rotate request client: %v", err) - } - err = rotateRequestClient.Send(rotateCertRequest) - if err != nil { - t.Fatalf("Error sending rotate request: %v", err) - } - rotateResponse := &certzpb.RotateCertificateResponse{} - for i := 0; i < 20; i++ { - rotateResponse, err = rotateRequestClient.Recv() - if err == nil { - break - } - t.Logf("Did not receive response ~ %vs after sending rotate request. Sleeping 10s to retry...", i*10) - time.Sleep(10 * time.Second) - } - if err != nil { - t.Logf("Error fetching rotate certificate response: %v", err) - return false - } - t.Logf("Received Rotate certificate response: %v", rotateResponse) - - finalizeRequest := &certzpb.RotateCertificateRequest_FinalizeRotation{FinalizeRotation: &certzpb.FinalizeRequest{}} - rotateCertRequest = &certzpb.RotateCertificateRequest{ - ForceOverwrite: false, - SslProfileId: profileID, - RotateRequest: finalizeRequest} - - err = rotateRequestClient.Send(rotateCertRequest) - if err != nil { - t.Fatalf("Error sending rotate finalize request: %v", err) - } - err = rotateRequestClient.CloseSend() - if err != nil { - t.Fatalf("Error sending rotate close send request: %v", err) - } - return true -} - -// ServerCertzRotate function to request the server certificate rotation and returns true on successful rotation. -func ServerCertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb.CertzClient, cert tls.Certificate, ctx context.Context, dut *ondatra.DUTDevice, san, serverAddr, profileID string, entities ...*certzpb.Entity) bool { +// CertzRotate function to request the server certificate rotation and returns true on successful rotation. +func CertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb.CertzClient, cert tls.Certificate, ctx context.Context, dut *ondatra.DUTDevice, san, serverAddr, profileID string, entities ...*certzpb.Entity) bool { if len(entities) == 0 { t.Logf("At least one entity required for Rotate request.") return false @@ -303,7 +249,8 @@ func ServerCertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb. t.Fatalf("Error sending rotate request: %v", err) } rotateResponse := &certzpb.RotateCertificateResponse{} - for i := 0; i < 6; i++ { + retries = 6 + for i := 0; i < retries; i++ { rotateResponse, err = rotateRequestClient.Recv() if err == nil { break @@ -321,13 +268,15 @@ func ServerCertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb. servers = gnmi.GetAll(t, dut, gnmi.OC().System().GrpcServerAny().Name().State()) batch := gnmi.SetBatch{} for _, server := range servers { + t.Logf("Server:%s", server) gnmi.BatchReplace(&batch, gnmi.OC().System().GrpcServer(server).CertificateId().Config(), profileID) } batch.Set(t, dut) - t.Logf("gNMI config is replaced with new ssl profile successfully.") + t.Logf("gNMI config is replaced with new ssl profile %s successfully.", profileID) + time.Sleep(30 * time.Second) //waiting 30s for gnmi config propagation success := false //Trying for 60s for the connection to succeed. - for i := 0; i < 6; i++ { + for i := 0; i < retries; i++ { success = VerifyGnsi(t, caCert, san, serverAddr, username, password, cert) if success { break @@ -354,7 +303,7 @@ func ServerCertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb. } return true } else { - t.Logf("gNSI service RPC did not succeed ~60s after rotate. Certz/Rotate failed. FinalizeRequest will not be sent") + t.Logf("gNSI service RPC did not succeed ~%d*10s after rotate. Certz/Rotate failed. FinalizeRequest will not be sent", retries) return false } } @@ -381,7 +330,7 @@ func CertGeneration(t *testing.T, dirPath string) error { return err } -// CertCleanup function to clean out the CA content under test_data. +// CertCleanup function to clean out the certificate content under test_data. func CertCleanup(t *testing.T, dirPath string) error { cmd := exec.Cmd{ Path: "./cleanup.sh", @@ -403,7 +352,7 @@ func CertCleanup(t *testing.T, dirPath string) error { return err } -// ReadDecodeServerCertificate function to read and decode server certificates to extract the SubjectAltName. +// ReadDecodeServerCertificate function to read and decode server certificates to extract the SubjectAltName and validate. func ReadDecodeServerCertificate(t *testing.T, serverCertzFile string) (san string) { sc, err := os.ReadFile(serverCertzFile) if err != nil { @@ -516,7 +465,7 @@ func VerifyGnmi(t *testing.T, caCert *x509.CertPool, san, serverAddr, username, t.Logf("gNMI Capability request failed with err: %v", err) return false } - t.Logf("VerifyGnmi:gNMI response: %s", response) + t.Logf("VerifyGnmi:gNMI response: %s", response.GNMIVersion) conn.Close() return true } diff --git a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go index 61ea6fc6757..60fe52d68d1 100644 --- a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go +++ b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go @@ -41,7 +41,6 @@ var ( serverAddr string username = "certzuser" password = "certzpasswd" - servers []string expected_result bool ) @@ -79,14 +78,12 @@ func TestServerCert(t *testing.T) { if !setupService.PreInitCheck(context.Background(), t, dut) { t.Fatalf("%s: Failed in the preInit checks.", time.Now().String()) } - ctx := context.Background() gnsiC, err := dut.RawAPIs().BindingDUT().DialGNSI(ctx) if err != nil { t.Fatalf("%s: Failed to create gNSI Connection %v", time.Now().String(), err) } t.Logf("%s Precheck:gNSI connection is successful %v", time.Now().String(), gnsiC) - t.Logf("%s:Creation of test data.", time.Now().String()) if setupService.CertGeneration(t, dirPath) != nil { t.Fatalf("%s:Failed to generate the testdata certificates.", time.Now().String()) @@ -102,7 +99,6 @@ func TestServerCert(t *testing.T) { t.Logf("%s AddProfileResponse: %v", time.Now().String(), addProfileResponse) t.Logf("%s: Getting the ssl profile list after new ssl profile addition.", time.Now().String()) setupService.GetSslProfilelist(ctx, t, certzClient, &certzpb.GetProfileListRequest{}) - cases := []struct { desc string serverCertFile string @@ -110,6 +106,7 @@ func TestServerCert(t *testing.T) { trustBundleFile string clientCertFile string clientKeyFile string + p7btrustBundle string mismatch bool }{ { @@ -117,6 +114,7 @@ func TestServerCert(t *testing.T) { serverCertFile: dirPath + "ca-01/server-rsa-a-cert.pem", serverKeyFile: dirPath + "ca-01/server-rsa-a-key.pem", trustBundleFile: dirPath + "ca-01/trust_bundle_01_rsa.pem", + p7btrustBundle: dirPath + "ca-01/ca-01/trust_bundle_01_rsa.p7b", clientCertFile: dirPath + "ca-01/client-rsa-a-cert.pem", clientKeyFile: dirPath + "ca-01/client-rsa-a-key.pem", }, @@ -125,6 +123,7 @@ func TestServerCert(t *testing.T) { serverCertFile: dirPath + "ca-01/server-ecdsa-a-cert.pem", serverKeyFile: dirPath + "ca-01/server-ecdsa-a-key.pem", trustBundleFile: dirPath + "ca-01/trust_bundle_01_ecdsa.pem", + p7btrustBundle: dirPath + "ca-01/trust_bundle_01_ecdsa.p7b", clientCertFile: dirPath + "ca-01/client-ecdsa-a-cert.pem", clientKeyFile: dirPath + "ca-01/client-ecdsa-a-key.pem", }, @@ -133,6 +132,7 @@ func TestServerCert(t *testing.T) { serverCertFile: dirPath + "ca-02/server-rsa-a-cert.pem", serverKeyFile: dirPath + "ca-02/server-rsa-a-key.pem", trustBundleFile: dirPath + "ca-02/trust_bundle_02_rsa.pem", + p7btrustBundle: dirPath + "ca-02/trust_bundle_02_rsa.p7b", clientCertFile: dirPath + "ca-02/client-rsa-a-cert.pem", clientKeyFile: dirPath + "ca-02/client-rsa-a-key.pem", }, @@ -141,6 +141,7 @@ func TestServerCert(t *testing.T) { serverCertFile: dirPath + "ca-02/server-ecdsa-a-cert.pem", serverKeyFile: dirPath + "ca-02/server-ecdsa-a-key.pem", trustBundleFile: dirPath + "ca-02/trust_bundle_02_ecdsa.pem", + p7btrustBundle: dirPath + "ca-02/trust_bundle_02_ecdsa.p7b", clientCertFile: dirPath + "ca-02/client-ecdsa-a-cert.pem", clientKeyFile: dirPath + "ca-02/client-ecdsa-a-key.pem", }, @@ -149,6 +150,7 @@ func TestServerCert(t *testing.T) { serverCertFile: dirPath + "ca-10/server-rsa-a-cert.pem", serverKeyFile: dirPath + "ca-10/server-rsa-a-key.pem", trustBundleFile: dirPath + "ca-10/trust_bundle_10_rsa.pem", + p7btrustBundle: dirPath + "ca-10/trust_bundle_10_rsa.p7b", clientCertFile: dirPath + "ca-10/client-rsa-a-cert.pem", clientKeyFile: dirPath + "ca-10/client-rsa-a-key.pem", }, @@ -157,6 +159,7 @@ func TestServerCert(t *testing.T) { serverCertFile: dirPath + "ca-10/server-ecdsa-a-cert.pem", serverKeyFile: dirPath + "ca-10/server-ecdsa-a-key.pem", trustBundleFile: dirPath + "ca-10/trust_bundle_10_ecdsa.pem", + p7btrustBundle: dirPath + "ca-10/trust_bundle_10_ecdsa.p7b", clientCertFile: dirPath + "ca-10/client-ecdsa-a-cert.pem", clientKeyFile: dirPath + "ca-10/client-ecdsa-a-key.pem", }, @@ -165,6 +168,7 @@ func TestServerCert(t *testing.T) { serverCertFile: dirPath + "ca-1000/server-rsa-a-cert.pem", serverKeyFile: dirPath + "ca-1000/server-rsa-a-key.pem", trustBundleFile: dirPath + "ca-1000/trust_bundle_1000_rsa.pem", + p7btrustBundle: dirPath + "ca-1000/trust_bundle_1000_rsa.p7b", clientCertFile: dirPath + "ca-1000/client-rsa-a-cert.pem", clientKeyFile: dirPath + "ca-1000/client-rsa-a-key.pem", }, @@ -173,6 +177,7 @@ func TestServerCert(t *testing.T) { serverCertFile: dirPath + "ca-1000/server-ecdsa-a-cert.pem", serverKeyFile: dirPath + "ca-1000/server-ecdsa-a-key.pem", trustBundleFile: dirPath + "ca-1000/trust_bundle_1000_ecdsa.pem", + p7btrustBundle: dirPath + "ca-1000/trust_bundle_1000_ecdsa.p7b", clientCertFile: dirPath + "ca-1000/client-ecdsa-a-cert.pem", clientKeyFile: dirPath + "ca-1000/client-ecdsa-a-key.pem", }, @@ -181,6 +186,7 @@ func TestServerCert(t *testing.T) { serverCertFile: dirPath + "ca-01/server-rsa-a-cert.pem", serverKeyFile: dirPath + "ca-01/server-rsa-a-key.pem", trustBundleFile: dirPath + "ca-02/trust_bundle_02_rsa.pem", + p7btrustBundle: dirPath + "ca-02/trust_bundle_02_rsa.p7b", clientCertFile: dirPath + "ca-01/client-rsa-a-cert.pem", clientKeyFile: dirPath + "ca-01/client-rsa-a-key.pem", mismatch: true, @@ -190,6 +196,7 @@ func TestServerCert(t *testing.T) { serverCertFile: dirPath + "ca-01/server-ecdsa-a-cert.pem", serverKeyFile: dirPath + "ca-01/server-ecdsa-a-key.pem", trustBundleFile: dirPath + "ca-02/trust_bundle_02_ecdsa.pem", + p7btrustBundle: dirPath + "ca-02/trust_bundle_02_ecdsa.p7b", clientCertFile: dirPath + "ca-01/client-ecdsa-a-cert.pem", clientKeyFile: dirPath + "ca-01/client-ecdsa-a-key.pem", mismatch: true, @@ -198,8 +205,7 @@ func TestServerCert(t *testing.T) { for _, tc := range cases { t.Run(tc.desc, func(t *testing.T) { - - san := setupService.ReadDecodeServerCertificate(t, tc.serverCertFile) + san := setupService.ReadDecodeServerCertificate(t, tc.serverCertFile) serverCert := setupService.CreateCertzChain(t, setupService.CertificateChainRequest{ RequestType: setupService.EntityTypeCertificateChain, ServerCertFile: tc.serverCertFile, @@ -220,48 +226,42 @@ func TestServerCert(t *testing.T) { if ok := cacert.AppendCertsFromPEM(cacertBytes); !ok { t.Fatalf("%s Failed to parse %v", time.Now().String(), tc.trustBundleFile) } - - certzClient := gnsiC.Certz() - success := setupService.CertzRotate(t, cacert, certzClient, cert, san, serverAddr, testProfile, &serverCertEntity, &trustBundleEntity) - if !success { - t.Fatalf("%s %s:Certz rotation failed.", time.Now().String(), tc.desc) - } - t.Logf("%s %s:Certz rotation completed!", time.Now().String(), tc.desc) - - // Replace config with newly added ssl profile after successful rotate. - servers = gnmi.GetAll(t, dut, gnmi.OC().System().GrpcServerAny().Name().State()) - batch := gnmi.SetBatch{} - for _, server := range servers { - gnmi.BatchReplace(&batch, gnmi.OC().System().GrpcServer(server).CertificateId().Config(), testProfile) - } - batch.Set(t, dut) - t.Logf("%s %s:replaced gNMI config with new ssl profile successfully.", time.Now().String(), tc.desc) - switch tc.mismatch { case true: - t.Run("Verification of new connection after mismatch trustbundle server certificate rotation", func(t *testing.T) { + expected_result = false + success := setupService.CertzRotate(t, cacert, certzClient, cert, ctx, dut, san, serverAddr, testProfile, &serverCertEntity, &trustBundleEntity) + if success { + t.Fatalf("%s:Server Certificate rotation failed.", tc.desc) + } + t.Logf("%s:Mismatch server certificate rotation failed as expected before finalize!", tc.desc) + t.Run("Verification of new connections with mismatch rotate of trustbundle.", func(t *testing.T) { result := setupService.PostValidationCheck(t, cacert, expected_result, san, serverAddr, username, password, cert) if !result { - t.Fatalf("%s postTestcase service validation failed with mismatch trustbundle from ca-02 - got %v , want %v", tc.desc, result, expected_result) + t.Fatalf("%s :postTestcase service validation failed after rotate- got %v, want %v", tc.desc, result, false) } - t.Logf("postTestcase service validation done for mismatch trustbundle from ca-02!") - t.Logf("PASS: %s successfully completed!", tc.desc) + t.Logf("%s postTestcase service validation done!", tc.desc) }) case false: - expected_result := true - t.Run("Verification of new connection after successful server certificate rotation", func(t *testing.T) { + expected_result = true + success := setupService.CertzRotate(t, cacert, certzClient, cert, ctx, dut, san, serverAddr, testProfile, &serverCertEntity, &trustBundleEntity) + if !success { + t.Fatalf("%s:Server Certificate rotation failed.", tc.desc) + } + t.Logf("%s:successfully completed server certificate rotation!", tc.desc) + // Verification check of the new connection post rotation. + t.Run("Verification of new connections after rotate ", func(t *testing.T) { result := setupService.PostValidationCheck(t, cacert, expected_result, san, serverAddr, username, password, cert) if !result { - t.Fatalf("%s postTestcase service validation failed after successful rotate -got %v, want %v .", tc.desc, result, expected_result) + t.Fatalf("%s :postTestcase service validation failed after rotate- got %v, want %v", tc.desc, result, true) } - t.Logf("%s postTestcase service validation done after server certificate rotation!", tc.desc) - t.Logf("PASS: %s successfully completed!", tc.desc) + t.Logf("%s postTestcase service validation done!", tc.desc) }) } }) + t.Logf("PASS: %s successfully completed!", tc.desc) } t.Logf("Cleanup of test data.") if setupService.CertCleanup(t, dirPath) != nil { - t.Fatalf("could not run cert cleanup command.") + t.Fatalf("could not run testdata cleanup command.") } } From ead1573214d58f5f93fdad56bd08d01c16246ad0 Mon Sep 17 00:00:00 2001 From: priyacj Date: Sun, 24 Nov 2024 22:11:46 -0800 Subject: [PATCH 14/20] fixed the else part --- .../internal/setup_service/setup_service.go | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go index 5fa6bcc5d59..fcbe15df0ff 100644 --- a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go +++ b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go @@ -203,28 +203,28 @@ func CreateCertChainFromTrustBundle(fileName string) *certzpb.CertificateChain { //a valid check for trust not empty if len(trust) == 0 { return &certzpb.CertificateChain{} - } else { - var prevCert *certzpb.CertificateChain - var bundleToReturn *certzpb.CertificateChain - for i := len(trust) - 1; i >= 0; i-- { - if i == len(trust)-1 { - bundleToReturn = &certzpb.CertificateChain{Certificate: &certzpb.Certificate{ - Type: certzpb.CertificateType_CERTIFICATE_TYPE_X509, - Encoding: certzpb.CertificateEncoding_CERTIFICATE_ENCODING_PEM, - Certificate: trust[i], - }, Parent: nil} - prevCert = bundleToReturn - } else { - prevCert = bundleToReturn - bundleToReturn = &certzpb.CertificateChain{Certificate: &certzpb.Certificate{ - Type: certzpb.CertificateType_CERTIFICATE_TYPE_X509, - Encoding: certzpb.CertificateEncoding_CERTIFICATE_ENCODING_PEM, - Certificate: trust[i], - }, Parent: prevCert} - } + } + var prevCert *certzpb.CertificateChain + var bundleToReturn *certzpb.CertificateChain + for i := len(trust) - 1; i >= 0; i-- { + if i == len(trust)-1 { + bundleToReturn = &certzpb.CertificateChain{Certificate: &certzpb.Certificate{ + Type: certzpb.CertificateType_CERTIFICATE_TYPE_X509, + Encoding: certzpb.CertificateEncoding_CERTIFICATE_ENCODING_PEM, + Certificate: trust[i], + }, Parent: nil} + prevCert = bundleToReturn + } else { + prevCert = bundleToReturn + bundleToReturn = &certzpb.CertificateChain{Certificate: &certzpb.Certificate{ + Type: certzpb.CertificateType_CERTIFICATE_TYPE_X509, + Encoding: certzpb.CertificateEncoding_CERTIFICATE_ENCODING_PEM, + Certificate: trust[i], + }, Parent: prevCert} } - return bundleToReturn } + return bundleToReturn + } // CertzRotate function to request the server certificate rotation and returns true on successful rotation. From 0033ed7ecbffbc25f0af079ea8d57628540bba9e Mon Sep 17 00:00:00 2001 From: priyacj Date: Mon, 25 Nov 2024 11:47:18 -0800 Subject: [PATCH 15/20] fixed the staticerror --- .../gnsi/certz/server_certificates/README.md | 111 ------------------ .../server_certificates_test.go | 5 +- 2 files changed, 3 insertions(+), 113 deletions(-) delete mode 100644 feature/security/gnsi/certz/server_certificates/README.md diff --git a/feature/security/gnsi/certz/server_certificates/README.md b/feature/security/gnsi/certz/server_certificates/README.md deleted file mode 100644 index eed72366c9a..00000000000 --- a/feature/security/gnsi/certz/server_certificates/README.md +++ /dev/null @@ -1,111 +0,0 @@ -# Server Certificate - -## Summary - -Servers must be able to validate a remote client's TLS certificate -and present a valid TLS certificate to the calling clients. - -## Baseline Setup - -### Input Args - -* the set of certificate testdata generated with the mk_cas.sh - script in featureprofiles/feature/security/gnsi/certz/test_data - -### DUT Service setup - -Configure the DUT to enable the following services (that are using gRPC) are -up and require using mTLS for authentication: - - * gNMI - * gNOI - * gNSI - * gRIBI - * P4RT - -Be prepared to load the relevant trust_bundle.pem file for each test -Certificate Authority(CA) under test on the DUT. Each CA has an RSA and ECDSA -form, both must be tested. - -## Tests - -### Certz-2.1 - -Perform these positive tests: - -Test that the server certificates from a set of one CA are able to be validated -and used for authentication to a device when used by a client conneting to each -gRPC service. - -Perform this for both RSA and ECDSA signed CA bundles and certificates. -Perform this for the permutation of 1, 2, 10, 1000 CA -trust_bundle configurations (## indicates the 1, 2, 10, 1000 CA testdata) - - 1) Load the correct key-type trust bundle onto the device and client system: - ca-##/trust_bundle_##_rsa.pem - ca-##/trust_bundle_##_ecdsa.pem - - 2) Load the correct key-type server certificate into the DUT services: - ca-##/server-rsa-key.pem - ca-##/server-rsa-cert.pem - ca-##/server-ecdsa-key.pem - ca-##/server-ecdsa-cert.pem - - 3) Load the correct key-type client certificate into the gRPC client: - ca-##/client-rsa-key.pem - ca-##/client-rsa-cert.pem - ca-##/client-ecdsa-key.pem - ca-##/client-ecdsa-cert.pem - - 4) Validate that the certificate is loaded and useful for inbound connections - to the server by clients. - - 5) Have the client connect to the services on the DUT. - - 6) Validate that the connection is established and the server's certificate - is trusted by the client. - - 7) Validate that the connection is established and the client's certificate is - validated and trusted by the server. - - -### Certz-2.2 - -Perform these negative tests, perform these tests with both the RSA and ECDSA -trust_bundles and certificates. - - 1) Load the correct key type trust_bundle from ca-02 on to the DUT: - ca-02/trust_bundle_02_rsa.pem - ca-02/trust_bundle_02_ecsda.pem - - 2) Load the correct key type client certificate from the ca-01 set into - the test gRPC client: - ca-01/client-rsa-key.pem - ca-01/client-rsa-cert.pem - ca-01/client-ecdsa-key.pem - ca-01/client-ecdsa-cert.pem - - 3) Validate that the certificate is loaded and useful for outbound - client connections. Connect to the service on the DUT. - - 4) Validate that the connection to the remote device is established, - validate the client certificate can not be used (is untrusted) by - service on the DUT. - - 5) Validate that the connection is properly torn down by the DUT. - -## OpenConfig Path and RPC Coverage - -The below yaml defines the OC paths intended to be covered by this test. OC paths used for test setup are not listed here. - -TODO(OCRPC): Record may not be correct or complete - -```yaml -rpcs: - gnsi: - certz.v1.Certz.GetProfileList: -``` - -## Minimum DUT Platform Requirement - -vRX diff --git a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go index 60fe52d68d1..44ac514df3f 100644 --- a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go +++ b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go @@ -1,4 +1,4 @@ -// Copyright 2023 Google LLC +// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -205,7 +205,8 @@ func TestServerCert(t *testing.T) { for _, tc := range cases { t.Run(tc.desc, func(t *testing.T) { - san := setupService.ReadDecodeServerCertificate(t, tc.serverCertFile) + + san := setupService.ReadDecodeServerCertificate(t, tc.serverCertFile) serverCert := setupService.CreateCertzChain(t, setupService.CertificateChainRequest{ RequestType: setupService.EntityTypeCertificateChain, ServerCertFile: tc.serverCertFile, From 6aa8f42dbdf13cddc2bc5a053544448bfa173afd Mon Sep 17 00:00:00 2001 From: priyacj Date: Mon, 6 Jan 2025 13:21:10 -0800 Subject: [PATCH 16/20] feedback comments are checkedin --- .../internal/setup_service/setup_service.go | 40 +++++++++---------- .../server_certificates_test.go | 4 +- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go index fcbe15df0ff..c25ab9ddbe1 100644 --- a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go +++ b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go @@ -55,7 +55,7 @@ type rpcCredentials struct { *creds.UserPass } -func (r *rpcCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { +func (r *rpcCredentials) GetRequestMetadata(_ context.Context, _ ...string) (map[string]string, error) { return map[string]string{ "username": r.UserPass.Username, "password": r.UserPass.Password, @@ -228,7 +228,7 @@ func CreateCertChainFromTrustBundle(fileName string) *certzpb.CertificateChain { } // CertzRotate function to request the server certificate rotation and returns true on successful rotation. -func CertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb.CertzClient, cert tls.Certificate, ctx context.Context, dut *ondatra.DUTDevice, san, serverAddr, profileID string, entities ...*certzpb.Entity) bool { +func CertzRotate(ctx context.Context, t *testing.T, caCert *x509.CertPool, certzClient certzpb.CertzClient, cert tls.Certificate, dut *ondatra.DUTDevice, san, serverAddr, profileID string, entities ...*certzpb.Entity) bool { if len(entities) == 0 { t.Logf("At least one entity required for Rotate request.") return false @@ -281,31 +281,27 @@ func CertzRotate(t *testing.T, caCert *x509.CertPool, certzClient certzpb.CertzC if success { break } - if i != 10 { - t.Logf("gNSI service RPC did not succeed ~ %vs after rotate. Sleeping 10s to retry...", i*10) - } + t.Logf("gNSI service RPC did not succeed ~ %vs after rotate. Sleeping 10s to retry...", i*10) time.Sleep(10 * time.Second) } - if success { - finalizeRequest := &certzpb.RotateCertificateRequest_FinalizeRotation{FinalizeRotation: &certzpb.FinalizeRequest{}} - rotateCertRequest = &certzpb.RotateCertificateRequest{ - ForceOverwrite: false, - SslProfileId: profileID, - RotateRequest: finalizeRequest} - - err = rotateRequestClient.Send(rotateCertRequest) - if err != nil { - t.Fatalf("Error sending rotate finalize request: %v", err) - } - err = rotateRequestClient.CloseSend() - if err != nil { - t.Fatalf("Error sending rotate close send request: %v", err) - } - return true - } else { + if !success { t.Logf("gNSI service RPC did not succeed ~%d*10s after rotate. Certz/Rotate failed. FinalizeRequest will not be sent", retries) return false } + finalizeRequest := &certzpb.RotateCertificateRequest_FinalizeRotation{FinalizeRotation: &certzpb.FinalizeRequest{}} + rotateCertRequest = &certzpb.RotateCertificateRequest{ + ForceOverwrite: false, + SslProfileId: profileID, + RotateRequest: finalizeRequest} + + if err := rotateRequestClient.Send(rotateCertRequest); err != nil { + t.Fatalf("Error sending rotate finalize request: %v", err) + } + + if err = rotateRequestClient.CloseSend(); err != nil { + t.Fatalf("Error sending rotate close send request: %v", err) + } + return true } // CertGeneration function to create test data for use in TLS tests. diff --git a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go index 44ac514df3f..9fb77fa9511 100644 --- a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go +++ b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go @@ -230,7 +230,7 @@ func TestServerCert(t *testing.T) { switch tc.mismatch { case true: expected_result = false - success := setupService.CertzRotate(t, cacert, certzClient, cert, ctx, dut, san, serverAddr, testProfile, &serverCertEntity, &trustBundleEntity) + success := setupService.CertzRotate(ctx, t, cacert, certzClient, cert, dut, san, serverAddr, testProfile, &serverCertEntity, &trustBundleEntity) if success { t.Fatalf("%s:Server Certificate rotation failed.", tc.desc) } @@ -244,7 +244,7 @@ func TestServerCert(t *testing.T) { }) case false: expected_result = true - success := setupService.CertzRotate(t, cacert, certzClient, cert, ctx, dut, san, serverAddr, testProfile, &serverCertEntity, &trustBundleEntity) + success := setupService.CertzRotate(ctx, t, cacert, certzClient, cert, dut, san, serverAddr, testProfile, &serverCertEntity, &trustBundleEntity) if !success { t.Fatalf("%s:Server Certificate rotation failed.", tc.desc) } From 59af27e58335db780a14372512973bf4824f1a68 Mon Sep 17 00:00:00 2001 From: priyacj Date: Mon, 6 Jan 2025 14:07:38 -0800 Subject: [PATCH 17/20] fixed the lints --- .../internal/setup_service/setup_service.go | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go index c25ab9ddbe1..f34f738c330 100644 --- a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go +++ b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go @@ -228,7 +228,7 @@ func CreateCertChainFromTrustBundle(fileName string) *certzpb.CertificateChain { } // CertzRotate function to request the server certificate rotation and returns true on successful rotation. -func CertzRotate(ctx context.Context, t *testing.T, caCert *x509.CertPool, certzClient certzpb.CertzClient, cert tls.Certificate, dut *ondatra.DUTDevice, san, serverAddr, profileID string, entities ...*certzpb.Entity) bool { +func CertzRotate(_ context.Context, t *testing.T, caCert *x509.CertPool, certzClient certzpb.CertzClient, cert tls.Certificate, dut *ondatra.DUTDevice, san, serverAddr, profileID string, entities ...*certzpb.Entity) bool { if len(entities) == 0 { t.Logf("At least one entity required for Rotate request.") return false @@ -335,17 +335,17 @@ func CertCleanup(t *testing.T, dirPath string) error { } cmd.Dir = dirPath t.Logf("Executing cleanup command") - err := cmd.Start() - if err != nil { - t.Logf("Testdata cleanup command failed with error:%v.", err) + if err := cmd.Start(); err != nil { + t.Errorf("failed to start cleanup command: %v", err) return err } - err = cmd.Wait() - if err != nil { - t.Logf("Testdata cleanup command failed during wait with the error:%v.", err) + + if err := cmd.Wait(); err != nil { + t.Errorf("failed to run cleanup command: %v", err) return err } - return err + t.Logf("testdata cleanup complete") + return nil } // ReadDecodeServerCertificate function to read and decode server certificates to extract the SubjectAltName and validate. @@ -559,28 +559,28 @@ func GetSslProfilelist(ctx context.Context, t *testing.T, certzClient certzpb.Ce } // PostValidationCheck function to do a validation of all services after certz rotation. -func PostValidationCheck(t *testing.T, caCert *x509.CertPool, expected_result bool, san, serverAddr, username, password string, cert tls.Certificate) bool { +func PostValidationCheck(t *testing.T, caCert *x509.CertPool, expectedResult bool, san, serverAddr, username, password string, cert tls.Certificate) bool { t.Logf("%s:Verifying New gNSI connection.", time.Now().String()) result := VerifyGnsi(t, caCert, san, serverAddr, username, password, cert) - if expected_result != result { - t.Fatalf("Failed with new gNSI Connection: got %v, want %v.", result, expected_result) + if expectedResult != result { + t.Fatalf("Failed with new gNSI Connection: got %v, want %v.", result, expectedResult) } t.Logf("%s:Verifying New gNOI connection.", time.Now().String()) result = VerifyGnoi(t, caCert, san, serverAddr, username, password, cert) - if expected_result != result { - t.Fatalf("Failed with new gNOI Connection: got false, want %v", expected_result) + if expectedResult != result { + t.Fatalf("Failed with new gNOI Connection: got false, want %v", expectedResult) } t.Logf("%s:Verifying New gRIBI connection.", time.Now().String()) - if expected_result != VerifyGribi(t, caCert, san, serverAddr, username, password, cert) { - t.Fatalf("Failed with new gRIBI Connection: got false, want %v.", expected_result) + if expectedResult != VerifyGribi(t, caCert, san, serverAddr, username, password, cert) { + t.Fatalf("Failed with new gRIBI Connection: got false, want %v.", expectedResult) } t.Logf("%s:Verifying New P4rt connection.", time.Now().String()) - if expected_result != VerifyP4rt(t, caCert, san, serverAddr, username, password, cert) { - t.Fatalf("Failed with new P4rt Connection: got false, want %v.", expected_result) + if expectedResult != VerifyP4rt(t, caCert, san, serverAddr, username, password, cert) { + t.Fatalf("Failed with new P4rt Connection: got false, want %v.", expectedResult) } t.Logf("%s:Verifying New gNMI connection.", time.Now().String()) - if expected_result != VerifyGnmi(t, caCert, san, serverAddr, username, password, cert) { - t.Fatalf("Failed with new gNMI Connection: got false, want %v.", expected_result) + if expectedResult != VerifyGnmi(t, caCert, san, serverAddr, username, password, cert) { + t.Fatalf("Failed with new gNMI Connection: got false, want %v.", expectedResult) } return true } From b020a8a499a21f19a3abe747bf6b8f1feb100d73 Mon Sep 17 00:00:00 2001 From: priyacj Date: Mon, 13 Jan 2025 11:58:22 -0800 Subject: [PATCH 18/20] fixed the feedback comments --- .../internal/setup_service/setup_service.go | 100 +++++++++--------- .../server_certificates_test.go | 2 +- 2 files changed, 52 insertions(+), 50 deletions(-) diff --git a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go index f34f738c330..cc83bd78041 100644 --- a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go +++ b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go @@ -25,6 +25,7 @@ import ( "fmt" "os" "os/exec" + "path/filepath" "testing" "time" @@ -79,14 +80,6 @@ const ( EntityTypeAuthPolicy entityType = 3 ) -// CertificateChainRequest is an input argument for the type definition for the CreateCertzChain. -type CertificateChainRequest struct { - RequestType entityType - ServerCertFile string - ServerKeyFile string - TrustBundleFile string -} - // CreateCertzEntity function to create certificate entity of type certificate chain/trust bundle/CRL/Authpolicy. func CreateCertzEntity(t *testing.T, typeOfEntity entityType, entityContent any, entityVersion string) certzpb.Entity { @@ -128,6 +121,14 @@ func CreateCertzEntity(t *testing.T, typeOfEntity entityType, entityContent any, return certzpb.Entity{} } +// CertificateChainRequest is an input argument for the type definition for the CreateCertzChain. +type CertificateChainRequest struct { + RequestType entityType + ServerCertFile string + ServerKeyFile string + TrustBundleFile string +} + // CreateCertzChain function to get the certificate chain of type certificate chain/trust bundle. func CreateCertzChain(t *testing.T, certData CertificateChainRequest) certzpb.CertificateChain { @@ -200,31 +201,33 @@ func CreateCertChainFromTrustBundle(fileName string) *certzpb.CertificateChain { } trust = append(trust, p) } - //a valid check for trust not empty + //To check if trust is an empty slice if len(trust) == 0 { return &certzpb.CertificateChain{} } - var prevCert *certzpb.CertificateChain - var bundleToReturn *certzpb.CertificateChain - for i := len(trust) - 1; i >= 0; i-- { - if i == len(trust)-1 { - bundleToReturn = &certzpb.CertificateChain{Certificate: &certzpb.Certificate{ - Type: certzpb.CertificateType_CERTIFICATE_TYPE_X509, - Encoding: certzpb.CertificateEncoding_CERTIFICATE_ENCODING_PEM, - Certificate: trust[i], - }, Parent: nil} - prevCert = bundleToReturn - } else { - prevCert = bundleToReturn - bundleToReturn = &certzpb.CertificateChain{Certificate: &certzpb.Certificate{ + // Create the first certificate chain object + bundleToReturn := &certzpb.CertificateChain{ + Certificate: &certzpb.Certificate{ + Type: certzpb.CertificateType_CERTIFICATE_TYPE_X509, + Encoding: certzpb.CertificateEncoding_CERTIFICATE_ENCODING_PEM, + Certificate: trust[len(trust)-1], + }, + Parent: nil, + } + prevCert := bundleToReturn + // Iterate over the remaining certificates to create the certificate chain + for i := len(trust) - 2; i >= 0; i-- { + parent := &certzpb.CertificateChain{ + Certificate: &certzpb.Certificate{ Type: certzpb.CertificateType_CERTIFICATE_TYPE_X509, Encoding: certzpb.CertificateEncoding_CERTIFICATE_ENCODING_PEM, Certificate: trust[i], - }, Parent: prevCert} + }, + Parent: prevCert, } + prevCert = parent } return bundleToReturn - } // CertzRotate function to request the server certificate rotation and returns true on successful rotation. @@ -306,41 +309,40 @@ func CertzRotate(_ context.Context, t *testing.T, caCert *x509.CertPool, certzCl // CertGeneration function to create test data for use in TLS tests. func CertGeneration(t *testing.T, dirPath string) error { - cmd := exec.Cmd{ - Path: "./mk_cas.sh", - Stdout: os.Stdout, - Stderr: os.Stderr, + genScript := filepath.Join(dirPath, "mk_cas.sh") + if _, err := os.Stat(genScript); os.IsNotExist(err) { + t.Logf("Cert generation script not found at: %v", genScript) + return nil } + + cmd := exec.Command(genScript) cmd.Dir = dirPath - t.Logf("Executing cert generation command %v.", cmd) - err := cmd.Start() - if err != nil { - t.Logf("Cert generation command failed with error:%v.", err) - return err - } - err = cmd.Wait() - if err != nil { - t.Logf("Failed to run cert generation command during wait with error:%v.", err) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + t.Logf("Executing cert generation command: %v", cmd.Args) + if err := cmd.Run(); err != nil { + t.Errorf("failed to run cert generation command: %v", err) return err } - return err + + t.Log("Cert generation complete") + return nil } -// CertCleanup function to clean out the certificate content under test_data. +// CertCleanup function to clean out the certificate content under test_data. func CertCleanup(t *testing.T, dirPath string) error { - cmd := exec.Cmd{ - Path: "./cleanup.sh", - Stdout: os.Stdout, - Stderr: os.Stderr, + cleanupScript := filepath.Join(dirPath, "cleanup.sh") + if _, err := os.Stat(cleanupScript); os.IsNotExist(err) { + t.Logf("Cleanup script not found at: %v", cleanupScript) + return nil } + cmd := exec.Command(cleanupScript) cmd.Dir = dirPath + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr t.Logf("Executing cleanup command") - if err := cmd.Start(); err != nil { - t.Errorf("failed to start cleanup command: %v", err) - return err - } - - if err := cmd.Wait(); err != nil { + if err := cmd.Run(); err != nil { t.Errorf("failed to run cleanup command: %v", err) return err } diff --git a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go index 9fb77fa9511..a9a2ae0f170 100644 --- a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go +++ b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go @@ -15,7 +15,7 @@ package server_certificates_test import ( - context "context" + "context" "crypto/tls" "crypto/x509" "os" From e4ba474ada0268be21e6a0de0cc68d472efe27f8 Mon Sep 17 00:00:00 2001 From: priyacj Date: Mon, 13 Jan 2025 14:23:45 -0800 Subject: [PATCH 19/20] incorporated the feedback --- .../internal/setup_service/setup_service.go | 56 +++++++++---------- .../server_certificates_test.go | 2 +- 2 files changed, 27 insertions(+), 31 deletions(-) mode change 100644 => 100755 feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go mode change 100644 => 100755 feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go diff --git a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go old mode 100644 new mode 100755 index cc83bd78041..242beabc52f --- a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go +++ b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go @@ -25,7 +25,6 @@ import ( "fmt" "os" "os/exec" - "path/filepath" "testing" "time" @@ -309,45 +308,42 @@ func CertzRotate(_ context.Context, t *testing.T, caCert *x509.CertPool, certzCl // CertGeneration function to create test data for use in TLS tests. func CertGeneration(t *testing.T, dirPath string) error { - genScript := filepath.Join(dirPath, "mk_cas.sh") - if _, err := os.Stat(genScript); os.IsNotExist(err) { - t.Logf("Cert generation script not found at: %v", genScript) - return nil + cmd := exec.Cmd{ + Path: "./mk_cas.sh", + Stdout: os.Stdout, + Stderr: os.Stderr, } - - cmd := exec.Command(genScript) cmd.Dir = dirPath - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - t.Logf("Executing cert generation command: %v", cmd.Args) - if err := cmd.Run(); err != nil { - t.Errorf("failed to run cert generation command: %v", err) - return err + t.Logf("Executing cert generation command %v.", cmd) + err := cmd.Start() + if err != nil { + t.Fatalf("Cert generation command failed with error:%v.", err) } - - t.Log("Cert generation complete") - return nil + err = cmd.Wait() + if err != nil { + t.Fatalf("Failed to run cert generation command during wait with error:%v.", err) + } + return err } -// CertCleanup function to clean out the certificate content under test_data. +// CertCleanup function to clean out the certificate content under test_data. func CertCleanup(t *testing.T, dirPath string) error { - cleanupScript := filepath.Join(dirPath, "cleanup.sh") - if _, err := os.Stat(cleanupScript); os.IsNotExist(err) { - t.Logf("Cleanup script not found at: %v", cleanupScript) - return nil + cmd := exec.Cmd{ + Path: "./cleanup.sh", + Stdout: os.Stdout, + Stderr: os.Stderr, } - cmd := exec.Command(cleanupScript) cmd.Dir = dirPath - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr t.Logf("Executing cleanup command") - if err := cmd.Run(); err != nil { - t.Errorf("failed to run cleanup command: %v", err) - return err + err := cmd.Start() + if err != nil { + t.Fatalf("Testdata cleanup command failed with error:%v.", err) + } + err = cmd.Wait() + if err != nil { + t.Fatalf("Testdata cleanup command failed during wait with the error:%v.", err) } - t.Logf("testdata cleanup complete") - return nil + return err } // ReadDecodeServerCertificate function to read and decode server certificates to extract the SubjectAltName and validate. diff --git a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go old mode 100644 new mode 100755 index a9a2ae0f170..0c0cce9d6d1 --- a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go +++ b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go @@ -37,7 +37,7 @@ const ( ) var ( - testProfile = "newprofile" + testProfile = "newprofile2" serverAddr string username = "certzuser" password = "certzpasswd" From 7dee6bb552e09059a481bf711f01e75c853a7fc2 Mon Sep 17 00:00:00 2001 From: priyacj Date: Tue, 14 Jan 2025 14:34:32 -0800 Subject: [PATCH 20/20] fixed the executable bit --- .../gnsi/certz/tests/internal/setup_service/setup_service.go | 0 .../certz/tests/server_certificates/server_certificates_test.go | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go mode change 100755 => 100644 feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go diff --git a/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go b/feature/security/gnsi/certz/tests/internal/setup_service/setup_service.go old mode 100755 new mode 100644 diff --git a/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go b/feature/security/gnsi/certz/tests/server_certificates/server_certificates_test.go old mode 100755 new mode 100644