Skip to content

Commit

Permalink
Refresh TLS Client Certs on interval
Browse files Browse the repository at this point in the history
Currently, GRPC does not use `GetClientCertificate` and instead relies
on the GRPC `advancedtls` package.
[context](grpc/grpc-go#5791 (comment)).

This PR updates `base_client_factory.go` to use this API such that
certificate reloads work for GRPC clients. This flow is implemented by
converting a tls.Config struct intended for a client into a series of
grpc DialOptions which configure GRPC with the same TLS options.
Critically, this converts a `GetClientCertificate` callback into a
`GetIdentityCertificatesForClient` callback which GRPC will call
as-needed.

Fixes #162.
  • Loading branch information
Jack Beasley authored and EdSchouten committed Aug 16, 2024
1 parent 31e7018 commit d07a29c
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 4 deletions.
1 change: 1 addition & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ use_repo(
"org_golang_google_genproto_googleapis_bytestream",
"org_golang_google_genproto_googleapis_rpc",
"org_golang_google_grpc",
"org_golang_google_grpc_security_advancedtls",
"org_golang_google_protobuf",
"org_golang_x_lint",
"org_golang_x_oauth2",
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ require (
google.golang.org/genproto/googleapis/bytestream v0.0.0-20240617180043-68d350f18fd4
google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4
google.golang.org/grpc v1.64.0
google.golang.org/grpc/security/advancedtls v0.0.0-20240627175909-6126383d854c
google.golang.org/protobuf v1.34.2
mvdan.cc/gofumpt v0.6.0
)
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,10 @@ google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/grpc/examples v0.0.0-20201112215255-90f1b3ee835b h1:NuxyvVZoDfHZwYW9LD4GJiF5/nhiSyP4/InTrvw9Ibk=
google.golang.org/grpc/examples v0.0.0-20201112215255-90f1b3ee835b/go.mod h1:IBqQ7wSUJ2Ep09a8rMWFsg4fmI2r38zwsq8a0GgxXpM=
google.golang.org/grpc/security/advancedtls v0.0.0-20240627175909-6126383d854c h1:oophYnBkqNL+X/bC2su2EuSyCveUwfCiRurk1E3T5jc=
google.golang.org/grpc/security/advancedtls v0.0.0-20240627175909-6126383d854c/go.mod h1:o+s4go+e1PJ2AjuQMY5hU82W7lDlefjJA6FqEHRVHWk=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
1 change: 1 addition & 0 deletions pkg/grpc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ go_library(
"@org_golang_google_grpc//peer",
"@org_golang_google_grpc//reflection",
"@org_golang_google_grpc//status",
"@org_golang_google_grpc_security_advancedtls//:advancedtls",
"@org_golang_google_protobuf//encoding/prototext",
"@org_golang_google_protobuf//proto",
"@org_golang_google_protobuf//reflect/protoreflect",
Expand Down
48 changes: 44 additions & 4 deletions pkg/grpc/base_client_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package grpc

import (
"context"
"crypto/tls"
"math"
"net/http"
"net/url"

Expand All @@ -14,6 +16,7 @@ import (
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/oauth"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/security/advancedtls"
"google.golang.org/grpc/status"
)

Expand All @@ -35,6 +38,43 @@ func NewBaseClientFactory(dialer ClientDialer, unaryInterceptors []grpc.UnaryCli
}
}

func newClientDialOptionsFromTLSConfig(tlsConfig *tls.Config) ([]grpc.DialOption, error) {
if tlsConfig == nil {
return []grpc.DialOption{grpc.WithInsecure()}, nil
}

opts := advancedtls.Options{
MinTLSVersion: tlsConfig.MinVersion,
MaxTLSVersion: tlsConfig.MaxVersion,
CipherSuites: tlsConfig.CipherSuites,
IdentityOptions: advancedtls.IdentityCertificateOptions{
GetIdentityCertificatesForClient: tlsConfig.GetClientCertificate,
},
RootOptions: advancedtls.RootCertificateOptions{
RootCertificates: tlsConfig.RootCAs,
},
}
// advancedtls checks MinTLSVersion > MaxTLSVersion before applying
// defaults:
// https://github.com/grpc/grpc-go/blob/master/security/advancedtls/advancedtls.go#L243-L245
// If setting a default minimum, set math.MaxUint16 as the Max to get around
// this check.
if opts.MaxTLSVersion == 0 && opts.MinTLSVersion >= 0 {
opts.MaxTLSVersion = math.MaxUint16
}

tc, err := advancedtls.NewClientCreds(&opts)
if err != nil {
return nil, util.StatusWrapWithCode(err, codes.InvalidArgument, "Failed to configure GRPC client TLS")
}
dialOptions := []grpc.DialOption{grpc.WithTransportCredentials(tc)}
if tlsConfig.ServerName != "" {
dialOptions = append(dialOptions, grpc.WithAuthority(tlsConfig.ServerName))
}

return dialOptions, nil
}

func (cf baseClientFactory) NewClientFromConfiguration(config *configuration.ClientConfiguration) (grpc.ClientConnInterface, error) {
if config == nil {
return nil, status.Error(codes.InvalidArgument, "No gRPC client configuration provided")
Expand All @@ -56,11 +96,11 @@ func (cf baseClientFactory) NewClientFromConfiguration(config *configuration.Cli
if err != nil {
return nil, util.StatusWrap(err, "Failed to create TLS configuration")
}
if tlsConfig != nil {
dialOptions = append(dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
} else {
dialOptions = append(dialOptions, grpc.WithInsecure())
tlsDialOpts, err := newClientDialOptionsFromTLSConfig(tlsConfig)
if err != nil {
return nil, util.StatusWrap(err, "Failed to convert TLS configuration")
}
dialOptions = append(dialOptions, tlsDialOpts...)

if windowSize := config.InitialWindowSizeBytes; windowSize != 0 {
dialOptions = append(dialOptions, grpc.WithInitialWindowSize(windowSize))
Expand Down

0 comments on commit d07a29c

Please sign in to comment.