From 92de712a7f53c70ea0580141198d29a6828455fe Mon Sep 17 00:00:00 2001 From: Philippe Boneff Date: Tue, 7 Jan 2025 18:23:18 +0000 Subject: [PATCH 1/2] put options to validate certs in an object --- cmd/gcp/main.go | 23 ++-- config.go | 58 ++++++++--- config_test.go | 271 +++++++++++++++++++++++++----------------------- 3 files changed, 205 insertions(+), 147 deletions(-) diff --git a/cmd/gcp/main.go b/cmd/gcp/main.go index 8c7437bf..0a10b27b 100644 --- a/cmd/gcp/main.go +++ b/cmd/gcp/main.go @@ -81,19 +81,30 @@ func main() { if err != nil { klog.Exitf("Can't create secret manager signer: %v", err) } - cpSigner, err := sctfe.NewCpSigner(signer, *origin, timeSource) + + chainValidationConfig := sctfe.ChainValidationConfig{ + RootsPemFile: *rootsPemFile, + RejectExpired: *rejectExpired, + RejectUnexpired: *rejectUnexpired, + ExtKeyUsages: *extKeyUsages, + RejectExtensions: *rejectExtensions, + NotAfterStart: notAfterStart.t, + NotAfterLimit: notAfterLimit.t, + } + + vCfg, err := sctfe.ValidateLogConfig(chainValidationConfig, *origin, signer) if err != nil { - klog.Exitf("Failed to create checkpoint signer: %v", err) + klog.Exitf("Failed to initialize log config: %v", err) } - storage, err := newGCPStorage(ctx, cpSigner) + cpSigner, err := sctfe.NewCpSigner(signer, vCfg.Origin, timeSource) if err != nil { - klog.Exitf("Failed to initiate storage backend: %v", err) + klog.Exitf("Failed to create checkpoint signer: %v", err) } - vCfg, err := sctfe.ValidateLogConfig(*origin, *rootsPemFile, *rejectExpired, *rejectUnexpired, *extKeyUsages, *rejectExtensions, notAfterStart.t, notAfterLimit.t, signer) + storage, err := newGCPStorage(ctx, cpSigner) if err != nil { - klog.Exitf("Invalid config: %v", err) + klog.Exitf("Failed to initiate storage backend: %v", err) } opts := sctfe.HandlerOptions{ diff --git a/config.go b/config.go index 00007f64..468e652b 100644 --- a/config.go +++ b/config.go @@ -29,6 +29,35 @@ import ( "k8s.io/klog/v2" ) +type ChainValidationConfig struct { + // Path to the file containing root certificates that are acceptable to the + // log. The certs are served through get-roots endpoint. + RootsPemFile string + // If RejectExpired is true then the certificate validity period will be + // checked against the current time during the validation of submissions. + // This will cause expired certificates to be rejected. + RejectExpired bool + // If RejectUnexpired is true then CTFE rejects certificates that are either + // currently valid or not yet valid. + RejectUnexpired bool + // If set, ExtKeyUsages will restrict the set of such usages that the + // server will accept. By default all are accepted. The values specified + // must be ones known to the x509 package, comma separated. + ExtKeyUsages string + // A comma separated list of X.509 extension OIDs, in dotted string form + // (e.g. "2.3.4.5") which, if present, should cause submissions to be + // rejected. + RejectExtensions string + // NotAfterStart defines the start of the range of acceptable NotAfter + // values, inclusive. + // Leaving this unset implies no lower bound to the range. + NotAfterStart *time.Time + // NotAfterLimit defines the end of the range of acceptable NotAfter values, + // exclusive. + // Leaving this unset implies no upper bound to the range. + NotAfterLimit *time.Time +} + // ValidatedLogConfig represents the LogConfig with the information that has // been successfully parsed as a result of validating it. type ValidatedLogConfig struct { @@ -49,17 +78,18 @@ type ValidatedLogConfig struct { // - Merge delays (if present) are correct. // // Returns the validated structures (useful to avoid double validation). -func ValidateLogConfig(origin string, rootsPemFile string, rejectExpired bool, rejectUnexpired bool, extKeyUsages string, rejectExtensions string, notAfterStart *time.Time, notAfterLimit *time.Time, signer crypto.Signer) (*ValidatedLogConfig, error) { +// TODO(phboneff): change the name of this function. +func ValidateLogConfig(cfg ChainValidationConfig, origin string, signer crypto.Signer) (*ValidatedLogConfig, error) { if origin == "" { return nil, errors.New("empty origin") } // Load the trusted roots. - if rootsPemFile == "" { + if cfg.RootsPemFile == "" { return nil, errors.New("empty rootsPemFile") } roots := x509util.NewPEMCertPool() - if err := roots.AppendCertsFromPEMFile(rootsPemFile); err != nil { + if err := roots.AppendCertsFromPEMFile(cfg.RootsPemFile); err != nil { return nil, fmt.Errorf("failed to read trusted roots: %v", err) } @@ -73,27 +103,27 @@ func ValidateLogConfig(origin string, rootsPemFile string, rejectExpired bool, r return nil, fmt.Errorf("unsupported key type: %v", keyType) } - if rejectExpired && rejectUnexpired { + if cfg.RejectExpired && cfg.RejectUnexpired { return nil, errors.New("configuration would reject all certificates") } // Validate the time interval. - if notAfterStart != nil && notAfterLimit != nil && (notAfterLimit).Before(*notAfterStart) { - return nil, fmt.Errorf("'Not After' limit %q before start %q", notAfterLimit.Format(time.RFC3339), notAfterStart.Format(time.RFC3339)) + if cfg.NotAfterStart != nil && cfg.NotAfterLimit != nil && (cfg.NotAfterLimit).Before(*cfg.NotAfterStart) { + return nil, fmt.Errorf("'Not After' limit %q before start %q", cfg.NotAfterLimit.Format(time.RFC3339), cfg.NotAfterStart.Format(time.RFC3339)) } validationOpts := CertValidationOpts{ trustedRoots: roots, - rejectExpired: rejectExpired, - rejectUnexpired: rejectUnexpired, - notAfterStart: notAfterStart, - notAfterLimit: notAfterLimit, + rejectExpired: cfg.RejectExpired, + rejectUnexpired: cfg.RejectUnexpired, + notAfterStart: cfg.NotAfterStart, + notAfterLimit: cfg.NotAfterLimit, } // Filter which extended key usages are allowed. lExtKeyUsages := []string{} - if extKeyUsages != "" { - lExtKeyUsages = strings.Split(extKeyUsages, ",") + if cfg.ExtKeyUsages != "" { + lExtKeyUsages = strings.Split(cfg.ExtKeyUsages, ",") } // Validate the extended key usages list. for _, kuStr := range lExtKeyUsages { @@ -112,8 +142,8 @@ func ValidateLogConfig(origin string, rootsPemFile string, rejectExpired bool, r } // Filter which extensions are rejected. var err error - if rejectExtensions != "" { - lRejectExtensions := strings.Split(rejectExtensions, ",") + if cfg.RejectExtensions != "" { + lRejectExtensions := strings.Split(cfg.RejectExtensions, ",") validationOpts.rejectExtIds, err = parseOIDs(lRejectExtensions) if err != nil { return nil, fmt.Errorf("failed to parse RejectExtensions: %v", err) diff --git a/config_test.go b/config_test.go index fff94d9e..0ca09bc7 100644 --- a/config_test.go +++ b/config_test.go @@ -33,20 +33,14 @@ func TestValidateLogConfig(t *testing.T) { t200 := time.Unix(200, 0) for _, tc := range []struct { - desc string - origin string - projectID string - bucket string - spannerDB string - wantErr string - rootsPemFile string - rejectExpired bool - rejectUnexpired bool - extKeyUsages string - rejectExtensions string - notAfterStart *time.Time - notAfterLimit *time.Time - signer crypto.Signer + desc string + origin string + projectID string + bucket string + spannerDB string + wantErr string + cvcfg ChainValidationConfig + signer crypto.Signer }{ { desc: "empty-origin", @@ -65,146 +59,169 @@ func TestValidateLogConfig(t *testing.T) { signer: signer, }, { - desc: "missing-root-cert", - wantErr: "failed to read trusted roots", - origin: "testlog", - projectID: "project", - bucket: "bucket", - spannerDB: "spanner", - rootsPemFile: "./testdata/bogus.cert", - signer: signer, + desc: "missing-root-cert", + wantErr: "failed to read trusted roots", + origin: "testlog", + projectID: "project", + bucket: "bucket", + spannerDB: "spanner", + cvcfg: ChainValidationConfig{ + RootsPemFile: "./testdata/bogus.cert", + }, + signer: signer, }, { - desc: "rejecting-all", - wantErr: "configuration would reject all certificates", - origin: "testlog", - projectID: "project", - bucket: "bucket", - spannerDB: "spanner", - rootsPemFile: "./testdata/fake-ca.cert", - rejectExpired: true, - rejectUnexpired: true, - signer: signer, + desc: "rejecting-all", + wantErr: "configuration would reject all certificates", + origin: "testlog", + projectID: "project", + bucket: "bucket", + spannerDB: "spanner", + cvcfg: ChainValidationConfig{ + RootsPemFile: "./testdata/fake-ca.cert", + RejectExpired: true, + RejectUnexpired: true}, + signer: signer, }, { - desc: "unknown-ext-key-usage-1", - wantErr: "unknown extended key usage", - origin: "testlog", - projectID: "project", - bucket: "bucket", - spannerDB: "spanner", - rootsPemFile: "./testdata/fake-ca.cert", - extKeyUsages: "wrong_usage", - signer: signer, + desc: "unknown-ext-key-usage-1", + wantErr: "unknown extended key usage", + origin: "testlog", + projectID: "project", + bucket: "bucket", + spannerDB: "spanner", + cvcfg: ChainValidationConfig{ + RootsPemFile: "./testdata/fake-ca.cert", + ExtKeyUsages: "wrong_usage"}, + signer: signer, }, { - desc: "unknown-ext-key-usage-2", - wantErr: "unknown extended key usage", - origin: "testlog", - projectID: "project", - bucket: "bucket", - spannerDB: "spanner", - rootsPemFile: "./testdata/fake-ca.cert", - extKeyUsages: "ClientAuth,ServerAuth,TimeStomping", - signer: signer, + desc: "unknown-ext-key-usage-2", + wantErr: "unknown extended key usage", + origin: "testlog", + projectID: "project", + bucket: "bucket", + spannerDB: "spanner", + cvcfg: ChainValidationConfig{ + RootsPemFile: "./testdata/fake-ca.cert", + ExtKeyUsages: "ClientAuth,ServerAuth,TimeStomping", + }, + signer: signer, }, { - desc: "unknown-ext-key-usage-3", - wantErr: "unknown extended key usage", - origin: "testlog", - projectID: "project", - bucket: "bucket", - spannerDB: "spanner", - rootsPemFile: "./testdata/fake-ca.cert", - extKeyUsages: "Any ", - signer: signer, + desc: "unknown-ext-key-usage-3", + wantErr: "unknown extended key usage", + origin: "testlog", + projectID: "project", + bucket: "bucket", + spannerDB: "spanner", + cvcfg: ChainValidationConfig{ + RootsPemFile: "./testdata/fake-ca.cert", + ExtKeyUsages: "Any ", + }, + signer: signer, }, { - desc: "unknown-reject-ext", - wantErr: "failed to parse RejectExtensions", - origin: "testlog", - projectID: "project", - bucket: "bucket", - spannerDB: "spanner", - rootsPemFile: "./testdata/fake-ca.cert", - rejectExtensions: "1.2.3.4,one.banana.two.bananas", - signer: signer, + desc: "unknown-reject-ext", + wantErr: "failed to parse RejectExtensions", + origin: "testlog", + projectID: "project", + bucket: "bucket", + spannerDB: "spanner", + cvcfg: ChainValidationConfig{ + RootsPemFile: "./testdata/fake-ca.cert", + RejectExtensions: "1.2.3.4,one.banana.two.bananas", + }, + signer: signer, }, { - desc: "limit-before-start", - wantErr: "before start", - origin: "testlog", - projectID: "project", - bucket: "bucket", - spannerDB: "spanner", - rootsPemFile: "./testdata/fake-ca.cert", - notAfterStart: &t200, - notAfterLimit: &t100, - signer: signer, + wantErr: "before start", + origin: "testlog", + projectID: "project", + bucket: "bucket", + spannerDB: "spanner", + cvcfg: ChainValidationConfig{ + RootsPemFile: "./testdata/fake-ca.cert", + NotAfterStart: &t200, + NotAfterLimit: &t100, + }, + signer: signer, }, { - desc: "ok", - origin: "testlog", - projectID: "project", - bucket: "bucket", - spannerDB: "spanner", - rootsPemFile: "./testdata/fake-ca.cert", - signer: signer, + desc: "ok", + origin: "testlog", + projectID: "project", + bucket: "bucket", + spannerDB: "spanner", + cvcfg: ChainValidationConfig{ + RootsPemFile: "./testdata/fake-ca.cert", + }, + signer: signer, }, { - desc: "ok-ext-key-usages", - origin: "testlog", - projectID: "project", - bucket: "bucket", - spannerDB: "spanner", - rootsPemFile: "./testdata/fake-ca.cert", - extKeyUsages: "ServerAuth,ClientAuth,OCSPSigning", - signer: signer, + desc: "ok-ext-key-usages", + origin: "testlog", + projectID: "project", + bucket: "bucket", + spannerDB: "spanner", + cvcfg: ChainValidationConfig{ + RootsPemFile: "./testdata/fake-ca.cert", + ExtKeyUsages: "ServerAuth,ClientAuth,OCSPSigning", + }, + signer: signer, }, { - desc: "ok-reject-ext", - origin: "testlog", - projectID: "project", - bucket: "bucket", - spannerDB: "spanner", - rootsPemFile: "./testdata/fake-ca.cert", - rejectExtensions: "1.2.3.4,5.6.7.8", - signer: signer, + desc: "ok-reject-ext", + origin: "testlog", + projectID: "project", + bucket: "bucket", + spannerDB: "spanner", + cvcfg: ChainValidationConfig{ + RootsPemFile: "./testdata/fake-ca.cert", + RejectExtensions: "1.2.3.4,5.6.7.8", + }, + signer: signer, }, { - desc: "ok-start-timestamp", - origin: "testlog", - projectID: "project", - bucket: "bucket", - spannerDB: "spanner", - rootsPemFile: "./testdata/fake-ca.cert", - notAfterStart: &t100, - signer: signer, + desc: "ok-start-timestamp", + origin: "testlog", + projectID: "project", + bucket: "bucket", + spannerDB: "spanner", + cvcfg: ChainValidationConfig{ + RootsPemFile: "./testdata/fake-ca.cert", + NotAfterStart: &t100, + }, + signer: signer, }, { - desc: "ok-limit-timestamp", - origin: "testlog", - projectID: "project", - bucket: "bucket", - spannerDB: "spanner", - rootsPemFile: "./testdata/fake-ca.cert", - notAfterStart: &t200, - signer: signer, + desc: "ok-limit-timestamp", + origin: "testlog", + projectID: "project", + bucket: "bucket", + spannerDB: "spanner", + cvcfg: ChainValidationConfig{ + RootsPemFile: "./testdata/fake-ca.cert", + NotAfterStart: &t200, + }, + signer: signer, }, { - desc: "ok-range-timestamp", - origin: "testlog", - projectID: "project", - bucket: "bucket", - spannerDB: "spanner", - rootsPemFile: "./testdata/fake-ca.cert", - notAfterStart: &t100, - notAfterLimit: &t200, - signer: signer, + desc: "ok-range-timestamp", + origin: "testlog", + projectID: "project", + bucket: "bucket", + spannerDB: "spanner", + cvcfg: ChainValidationConfig{ + RootsPemFile: "./testdata/fake-ca.cert", + NotAfterStart: &t100, + NotAfterLimit: &t200, + }, + signer: signer, }, } { t.Run(tc.desc, func(t *testing.T) { - vc, err := ValidateLogConfig(tc.origin, tc.rootsPemFile, tc.rejectExpired, tc.rejectUnexpired, tc.extKeyUsages, tc.rejectExtensions, tc.notAfterStart, tc.notAfterLimit, signer) + vc, err := ValidateLogConfig(tc.cvcfg, tc.origin, signer) if len(tc.wantErr) == 0 && err != nil { t.Errorf("ValidateLogConfig()=%v, want nil", err) } From 550696684913a339df6ce807474e02d00a167766 Mon Sep 17 00:00:00 2001 From: Philippe Boneff Date: Fri, 17 Jan 2025 12:26:38 +0000 Subject: [PATCH 2/2] address comments --- cmd/gcp/main.go | 6 +++--- config.go | 31 +++++++++++++++++-------------- config_test.go | 26 +++++++++++++------------- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/cmd/gcp/main.go b/cmd/gcp/main.go index 0a10b27b..7f4d5593 100644 --- a/cmd/gcp/main.go +++ b/cmd/gcp/main.go @@ -83,7 +83,7 @@ func main() { } chainValidationConfig := sctfe.ChainValidationConfig{ - RootsPemFile: *rootsPemFile, + RootsPEMFile: *rootsPemFile, RejectExpired: *rejectExpired, RejectUnexpired: *rejectUnexpired, ExtKeyUsages: *extKeyUsages, @@ -94,7 +94,7 @@ func main() { vCfg, err := sctfe.ValidateLogConfig(chainValidationConfig, *origin, signer) if err != nil { - klog.Exitf("Failed to initialize log config: %v", err) + klog.Exitf("Invalid log config: %v", err) } cpSigner, err := sctfe.NewCpSigner(signer, vCfg.Origin, timeSource) @@ -104,7 +104,7 @@ func main() { storage, err := newGCPStorage(ctx, cpSigner) if err != nil { - klog.Exitf("Failed to initiate storage backend: %v", err) + klog.Exitf("Failed to initialize storage backend: %v", err) } opts := sctfe.HandlerOptions{ diff --git a/config.go b/config.go index 468e652b..3ceaaaa2 100644 --- a/config.go +++ b/config.go @@ -29,24 +29,27 @@ import ( "k8s.io/klog/v2" ) +// ChainValidationConfig contains parameters to configure chain validation. type ChainValidationConfig struct { - // Path to the file containing root certificates that are acceptable to the - // log. The certs are served through get-roots endpoint. - RootsPemFile string - // If RejectExpired is true then the certificate validity period will be + // RootsPEMFile is the path to the file containing root certificates that + // are acceptable to the log. The certs are served through get-roots + // endpoint. + RootsPEMFile string + // RejectExpired controls if true then the certificate validity period will be // checked against the current time during the validation of submissions. // This will cause expired certificates to be rejected. RejectExpired bool - // If RejectUnexpired is true then CTFE rejects certificates that are either - // currently valid or not yet valid. + // RejectUnexpired controls if the SCTFE rejects certificates that are + // either currently valid or not yet valid. + // TODO(phboneff): evaluate whether we need to keep this one. RejectUnexpired bool - // If set, ExtKeyUsages will restrict the set of such usages that the - // server will accept. By default all are accepted. The values specified - // must be ones known to the x509 package, comma separated. + // ExtKeyUsages lists Extended Key Usage values that newly submitted + // certificates MUST contain. By default all are accepted. The + // values specified must be ones known to the x509 package, comma separated. ExtKeyUsages string - // A comma separated list of X.509 extension OIDs, in dotted string form - // (e.g. "2.3.4.5") which, if present, should cause submissions to be - // rejected. + // RejectExtensions lists X.509 extension OIDs that newly submitted + // certificates MUST NOT contain. Empty by default. Values must be + // specificed in dotted string form (e.g. "2.3.4.5"). RejectExtensions string // NotAfterStart defines the start of the range of acceptable NotAfter // values, inclusive. @@ -85,11 +88,11 @@ func ValidateLogConfig(cfg ChainValidationConfig, origin string, signer crypto.S } // Load the trusted roots. - if cfg.RootsPemFile == "" { + if cfg.RootsPEMFile == "" { return nil, errors.New("empty rootsPemFile") } roots := x509util.NewPEMCertPool() - if err := roots.AppendCertsFromPEMFile(cfg.RootsPemFile); err != nil { + if err := roots.AppendCertsFromPEMFile(cfg.RootsPEMFile); err != nil { return nil, fmt.Errorf("failed to read trusted roots: %v", err) } diff --git a/config_test.go b/config_test.go index 0ca09bc7..1c79c20d 100644 --- a/config_test.go +++ b/config_test.go @@ -66,7 +66,7 @@ func TestValidateLogConfig(t *testing.T) { bucket: "bucket", spannerDB: "spanner", cvcfg: ChainValidationConfig{ - RootsPemFile: "./testdata/bogus.cert", + RootsPEMFile: "./testdata/bogus.cert", }, signer: signer, }, @@ -78,7 +78,7 @@ func TestValidateLogConfig(t *testing.T) { bucket: "bucket", spannerDB: "spanner", cvcfg: ChainValidationConfig{ - RootsPemFile: "./testdata/fake-ca.cert", + RootsPEMFile: "./testdata/fake-ca.cert", RejectExpired: true, RejectUnexpired: true}, signer: signer, @@ -91,7 +91,7 @@ func TestValidateLogConfig(t *testing.T) { bucket: "bucket", spannerDB: "spanner", cvcfg: ChainValidationConfig{ - RootsPemFile: "./testdata/fake-ca.cert", + RootsPEMFile: "./testdata/fake-ca.cert", ExtKeyUsages: "wrong_usage"}, signer: signer, }, @@ -103,7 +103,7 @@ func TestValidateLogConfig(t *testing.T) { bucket: "bucket", spannerDB: "spanner", cvcfg: ChainValidationConfig{ - RootsPemFile: "./testdata/fake-ca.cert", + RootsPEMFile: "./testdata/fake-ca.cert", ExtKeyUsages: "ClientAuth,ServerAuth,TimeStomping", }, signer: signer, @@ -116,7 +116,7 @@ func TestValidateLogConfig(t *testing.T) { bucket: "bucket", spannerDB: "spanner", cvcfg: ChainValidationConfig{ - RootsPemFile: "./testdata/fake-ca.cert", + RootsPEMFile: "./testdata/fake-ca.cert", ExtKeyUsages: "Any ", }, signer: signer, @@ -129,7 +129,7 @@ func TestValidateLogConfig(t *testing.T) { bucket: "bucket", spannerDB: "spanner", cvcfg: ChainValidationConfig{ - RootsPemFile: "./testdata/fake-ca.cert", + RootsPEMFile: "./testdata/fake-ca.cert", RejectExtensions: "1.2.3.4,one.banana.two.bananas", }, signer: signer, @@ -141,7 +141,7 @@ func TestValidateLogConfig(t *testing.T) { bucket: "bucket", spannerDB: "spanner", cvcfg: ChainValidationConfig{ - RootsPemFile: "./testdata/fake-ca.cert", + RootsPEMFile: "./testdata/fake-ca.cert", NotAfterStart: &t200, NotAfterLimit: &t100, }, @@ -154,7 +154,7 @@ func TestValidateLogConfig(t *testing.T) { bucket: "bucket", spannerDB: "spanner", cvcfg: ChainValidationConfig{ - RootsPemFile: "./testdata/fake-ca.cert", + RootsPEMFile: "./testdata/fake-ca.cert", }, signer: signer, }, @@ -165,7 +165,7 @@ func TestValidateLogConfig(t *testing.T) { bucket: "bucket", spannerDB: "spanner", cvcfg: ChainValidationConfig{ - RootsPemFile: "./testdata/fake-ca.cert", + RootsPEMFile: "./testdata/fake-ca.cert", ExtKeyUsages: "ServerAuth,ClientAuth,OCSPSigning", }, signer: signer, @@ -177,7 +177,7 @@ func TestValidateLogConfig(t *testing.T) { bucket: "bucket", spannerDB: "spanner", cvcfg: ChainValidationConfig{ - RootsPemFile: "./testdata/fake-ca.cert", + RootsPEMFile: "./testdata/fake-ca.cert", RejectExtensions: "1.2.3.4,5.6.7.8", }, signer: signer, @@ -189,7 +189,7 @@ func TestValidateLogConfig(t *testing.T) { bucket: "bucket", spannerDB: "spanner", cvcfg: ChainValidationConfig{ - RootsPemFile: "./testdata/fake-ca.cert", + RootsPEMFile: "./testdata/fake-ca.cert", NotAfterStart: &t100, }, signer: signer, @@ -201,7 +201,7 @@ func TestValidateLogConfig(t *testing.T) { bucket: "bucket", spannerDB: "spanner", cvcfg: ChainValidationConfig{ - RootsPemFile: "./testdata/fake-ca.cert", + RootsPEMFile: "./testdata/fake-ca.cert", NotAfterStart: &t200, }, signer: signer, @@ -213,7 +213,7 @@ func TestValidateLogConfig(t *testing.T) { bucket: "bucket", spannerDB: "spanner", cvcfg: ChainValidationConfig{ - RootsPemFile: "./testdata/fake-ca.cert", + RootsPEMFile: "./testdata/fake-ca.cert", NotAfterStart: &t100, NotAfterLimit: &t200, },