Skip to content

Commit

Permalink
Refactor the httpClientForVcert function so that it can also be used …
Browse files Browse the repository at this point in the history
…for Venafi Cloud

Signed-off-by: Richard Wall <richard.wall@venafi.com>
  • Loading branch information
wallrj committed Mar 20, 2024
1 parent 95a347c commit b6a52d1
Showing 1 changed file with 51 additions and 11 deletions.
62 changes: 51 additions & 11 deletions pkg/issuer/venafi/client/venaficlient.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/Venafi/vcert/v5/pkg/venafi/cloud"
"github.com/Venafi/vcert/v5/pkg/venafi/tpp"
"github.com/go-logr/logr"
"k8s.io/utils/ptr"

internalinformers "github.com/cert-manager/cert-manager/internal/informers"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
Expand Down Expand Up @@ -162,7 +163,11 @@ func configForIssuer(iss cmapi.GenericIssuer, secretsLister internalinformers.Se
Password: password,
AccessToken: accessToken,
},
Client: httpClientForVcertTPP(tpp.CABundle),
Client: httpClientForVcert(&httpClientForVcertOptions{
UserAgent: ptr.To(userAgent),
CABundle: tpp.CABundle,
TLSRenegotiationSupport: ptr.To(tls.RenegotiateOnceAsClient),
}),
}
case venCfg.Cloud != nil:
cloud := venCfg.Cloud
Expand All @@ -186,6 +191,9 @@ func configForIssuer(iss cmapi.GenericIssuer, secretsLister internalinformers.Se
Credentials: &endpoint.Authentication{
APIKey: apiKey,
},
Client: httpClientForVcert(&httpClientForVcertOptions{
UserAgent: ptr.To(userAgent),
}),
}
default:
// API validation in webhook and in the ClusterIssuer and Issuer controller
Expand All @@ -201,9 +209,32 @@ func configForIssuer(iss cmapi.GenericIssuer, secretsLister internalinformers.Se

}

// httpClientForVcertTPP creates an HTTP client and customises it to allow client TLS renegotiation.
// httpClientForVcertOptions contains options for `httpClientForVcert`, to allow
// you to customize the HTTP client.
type httpClientForVcertOptions struct {
// UserAgent will add a User-Agent header to all HTTP requests.
UserAgent *string
// CABundle will override the CA certificates used to verify server
// certificates.
CABundle []byte
// TLSRenegotiationSupport will override the TLSRenegotiationSupport setting
// of the client.
TLSRenegotiationSupport *tls.RenegotiationSupport
}

// httpClientForVcert creates an HTTP client which matches the default HTTP client of vcert,
// but allows you to customize client TLS renegotiation, and User-Agent.
//
// Why is it necessary to create our own HTTP client for vcert?
//
// Here's why:
// 1. We need to customize the client TLS renegotiation setting when connecting
// to certain TPP servers.
// 2. We need to customize the User-Agent header for all HTTP requests to Venafi
// REST API endpoints.
// 3. The vcert package does not currently provide an easier way to change those
// settings.
//
// Why is it necessary to customize the client TLS renegotiation?
//
// 1. The TPP API server is served by Microsoft Windows Server and IIS.
// 2. IIS uses TLS-1.2 by default[1] and it uses a
Expand All @@ -228,16 +259,19 @@ func configForIssuer(iss cmapi.GenericIssuer, secretsLister internalinformers.Se
// because cert-manager establishes a new HTTPS connection for each API
// request and therefore should only ever need to renegotiate once in this
// scenario.
// 5. But overriding the HTTP client causes vcert to ignore the
//
// Why do we supply CA bundle in the HTTP client **and** in the vcert.Config?
//
// 1. Overriding the HTTP client causes vcert to ignore the
// `vcert.Config.ConnectionTrust` field, so we also have to set up the root
// CA trust pool ourselves.
// 6. And the value of RootCAs MUST be nil unless the user has supplied a
// 2. And the value of RootCAs MUST be nil unless the user has supplied a
// custom CA, because a nil value causes the Go HTTP client to load the
// system default root CAs.
//
// [1] TLS protocol version support in Microsoft Windows: https://learn.microsoft.com/en-us/windows/win32/secauthn/protocols-in-tls-ssl--schannel-ssp-#tls-protocol-version-support
// [2] Should I use SSL/TLS renegotiation?: https://security.stackexchange.com/a/24569
func httpClientForVcertTPP(caBundle []byte) *http.Client {
func httpClientForVcert(options *httpClientForVcertOptions) *http.Client {
// Copy vcert's default HTTP transport, which is mostly identical to the
// http.DefaultTransport settings in Go's stdlib.
// https://github.com/Venafi/vcert/blob/89645a7710a7b529765274cb60dc5e28066217a1/pkg/venafi/tpp/tpp.go#L481-L513
Expand All @@ -261,20 +295,26 @@ func httpClientForVcertTPP(caBundle []byte) *http.Client {
if tlsClientConfig == nil {
tlsClientConfig = &tls.Config{}
}
if len(caBundle) > 0 {
if len(options.CABundle) > 0 {
rootCAs := x509.NewCertPool()
rootCAs.AppendCertsFromPEM(caBundle)
rootCAs.AppendCertsFromPEM(options.CABundle)
tlsClientConfig.RootCAs = rootCAs
}
transport.TLSClientConfig = tlsClientConfig

// Enable TLS 1.2 renegotiation (see earlier comment for justification).
transport.TLSClientConfig.Renegotiation = tls.RenegotiateOnceAsClient
if options.TLSRenegotiationSupport != nil {
transport.TLSClientConfig.Renegotiation = *options.TLSRenegotiationSupport
}

var roundTripper http.RoundTripper = transport
if options.UserAgent != nil {
roundTripper = util.UserAgentRoundTripper(transport, *options.UserAgent)
}

// Copy vcert's initialization of the HTTP client, which overrides the default timeout.
// https://github.com/Venafi/vcert/blob/89645a7710a7b529765274cb60dc5e28066217a1/pkg/venafi/tpp/tpp.go#L481-L513
return &http.Client{
Transport: transport,
Transport: roundTripper,
Timeout: time.Second * 30,
}
}
Expand Down

0 comments on commit b6a52d1

Please sign in to comment.