From 4753ff5850e4046bf19a54c4f3d5198c26284271 Mon Sep 17 00:00:00 2001 From: Patrick Sanders Date: Fri, 12 Mar 2021 10:55:02 -0800 Subject: [PATCH] Fix mTLS reload on failed credential requests (#50) --- cache/cache.go | 6 +- cmd/metadata.go | 90 ------------------ cmd/service.go | 8 +- creds/aws.go | 25 +---- creds/consoleme.go | 171 ++++++++++++++++++++--------------- creds/refreshable.go | 31 +++++-- creds/refreshable_test.go | 4 +- creds/types.go | 2 +- errors/errors.go | 11 ++- httpAuth/mtls/certificate.go | 33 ++++--- 10 files changed, 160 insertions(+), 221 deletions(-) delete mode 100644 cmd/metadata.go diff --git a/cache/cache.go b/cache/cache.go index 5cc8281..42e35a2 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -59,7 +59,7 @@ func (cc *CredentialCache) Get(role string, assumeChain []string) (*creds.Refres return nil, errors.NoCredentialsFoundInCache } -func (cc *CredentialCache) GetOrSet(client *creds.Client, role, region string, assumeChain []string) (*creds.RefreshableProvider, error) { +func (cc *CredentialCache) GetOrSet(client creds.HTTPClient, role, region string, assumeChain []string) (*creds.RefreshableProvider, error) { c, err := cc.Get(role, assumeChain) if err == nil { return c, nil @@ -74,7 +74,7 @@ func (cc *CredentialCache) GetOrSet(client *creds.Client, role, region string, a return c, nil } -func (cc *CredentialCache) SetDefault(client *creds.Client, role, region string, assumeChain []string) error { +func (cc *CredentialCache) SetDefault(client creds.HTTPClient, role, region string, assumeChain []string) error { _, err := cc.set(client, role, region, assumeChain) if err != nil { return err @@ -119,7 +119,7 @@ func (cc *CredentialCache) get(slug string) (*creds.RefreshableProvider, bool) { return c, ok } -func (cc *CredentialCache) set(client *creds.Client, role, region string, assumeChain []string) (*creds.RefreshableProvider, error) { +func (cc *CredentialCache) set(client creds.HTTPClient, role, region string, assumeChain []string) (*creds.RefreshableProvider, error) { c, err := creds.NewRefreshableProvider(client, role, region, assumeChain, false) if err != nil { return nil, fmt.Errorf("could not generate creds: %w", err) diff --git a/cmd/metadata.go b/cmd/metadata.go deleted file mode 100644 index be70334..0000000 --- a/cmd/metadata.go +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2020 Netflix, Inc. - * - * 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 cmd - -import ( - "fmt" - "net" - "net/http" - - "github.com/netflix/weep/cache" - - "github.com/spf13/viper" - - "github.com/gorilla/mux" - "github.com/netflix/weep/creds" - "github.com/netflix/weep/handlers" - "github.com/spf13/cobra" -) - -func init() { - metadataCmd.PersistentFlags().StringVarP(&metadataRegion, "region", "r", "us-east-1", "region of metadata service") - metadataCmd.PersistentFlags().StringVarP(&metadataListenAddr, "listen-address", "a", "127.0.0.1", "IP address for metadata service to listen on") - metadataCmd.PersistentFlags().IntVarP(&metadataListenPort, "port", "p", viper.GetInt("server.metadata_port"), "port for metadata service to listen on") - rootCmd.AddCommand(metadataCmd) -} - -var metadataCmd = &cobra.Command{ - Use: "imds [role_name]", - Aliases: []string{"metadata"}, - Short: "Run a local Instance Metadata Service (IMDS) endpoint that serves credentials", - Args: cobra.ExactArgs(1), - RunE: runMetadata, -} - -func runMetadata(cmd *cobra.Command, args []string) error { - role := args[0] - client, err := creds.GetClient() - if err != nil { - return err - } - err = cache.GlobalCache.SetDefault(client, role, metadataRegion, make([]string, 0)) - if err != nil { - return err - } - ipaddress := net.ParseIP(metadataListenAddr) - - if ipaddress == nil { - return fmt.Errorf("Invalid IP: %s", metadataListenAddr) - } - - listenAddr := fmt.Sprintf("%s:%d", ipaddress, metadataListenPort) - - router := mux.NewRouter() - router.HandleFunc("/healthcheck", handlers.HealthcheckHandler) - router.HandleFunc("/{version}/", handlers.CredentialServiceMiddleware(handlers.BaseVersionHandler)) - router.HandleFunc("/{version}/api/token", handlers.CredentialServiceMiddleware(handlers.TokenHandler)).Methods("PUT") - router.HandleFunc("/{version}/meta-data", handlers.CredentialServiceMiddleware(handlers.BaseHandler)) - router.HandleFunc("/{version}/meta-data/", handlers.CredentialServiceMiddleware(handlers.BaseHandler)) - router.HandleFunc("/{version}/meta-data/iam/info", handlers.CredentialServiceMiddleware(handlers.IamInfoHandler)) - router.HandleFunc("/{version}/meta-data/iam/security-credentials/", handlers.CredentialServiceMiddleware(handlers.RoleHandler)) - router.HandleFunc("/{version}/meta-data/iam/security-credentials/{role}", handlers.CredentialServiceMiddleware(handlers.CredentialsHandler)) - router.HandleFunc("/{version}/dynamic/instance-identity/document", handlers.CredentialServiceMiddleware(handlers.InstanceIdentityDocumentHandler)) - router.HandleFunc("/{path:.*}", handlers.CredentialServiceMiddleware(handlers.CustomHandler)) - - go func() { - log.Info("Starting weep meta-data service...") - log.Info("Server started on: ", listenAddr) - log.Info(http.ListenAndServe(listenAddr, router)) - }() - - // Check for interrupt signal and exit cleanly - <-shutdown - log.Print("Shutdown signal received, exiting weep meta-data service...") - - return nil -} diff --git a/cmd/service.go b/cmd/service.go index bf37e63..785ff83 100644 --- a/cmd/service.go +++ b/cmd/service.go @@ -61,14 +61,10 @@ func (p *program) run() { exitCode := 0 args := viper.GetStringSlice("service.args") switch command := viper.GetString("service.command"); command { + case "serve": case "ecs_credential_provider": - err := runWeepServer(nil, args) - if err != nil { - log.Error(err) - exitCode = 1 - } case "metadata": - err := runMetadata(nil, args) + err := runWeepServer(nil, args) if err != nil { log.Error(err) exitCode = 1 diff --git a/creds/aws.go b/creds/aws.go index c616922..5740148 100644 --- a/creds/aws.go +++ b/creds/aws.go @@ -26,16 +26,6 @@ import ( "github.com/aws/aws-sdk-go/service/sts" ) -// getAwsCredentials uses the provided Client to request credentials from ConsoleMe. -func getAwsCredentials(client *Client, role string, ipRestrict bool) (string, string, string, string, Time, error) { - tempCreds, err := client.GetRoleCredentials(role, ipRestrict) - if err != nil { - return "", "", "", "", Time{}, err - } - - return tempCreds.AccessKeyId, tempCreds.SecretAccessKey, tempCreds.SessionToken, tempCreds.RoleArn, tempCreds.Expiration, nil -} - // getSessionName returns the AWS session name, or defaults to weep if we can't find one. func getSessionName(session *sts.STS) string { identity, err := session.GetCallerIdentity(&sts.GetCallerIdentityInput{}) @@ -82,27 +72,20 @@ func getAssumeRoleCredentials(id, secret, token, roleArn string) (string, string // GetCredentialsC uses the provided Client to request credentials from ConsoleMe then // follows the provided chain of roles to assume. Roles are assumed in the order in which // they appear in the assumeRole slice. -func GetCredentialsC(client *Client, role string, ipRestrict bool, assumeRole []string) (*AwsCredentials, error) { - id, secret, token, roleArn, expiration, err := getAwsCredentials(client, role, ipRestrict) +func GetCredentialsC(client HTTPClient, role string, ipRestrict bool, assumeRole []string) (*AwsCredentials, error) { + resp, err := client.GetRoleCredentials(role, ipRestrict) if err != nil { return nil, err } for _, assumeRoleArn := range assumeRole { - id, secret, token, err = getAssumeRoleCredentials(id, secret, token, assumeRoleArn) + resp.AccessKeyId, resp.SecretAccessKey, resp.SessionToken, err = getAssumeRoleCredentials(resp.AccessKeyId, resp.SecretAccessKey, resp.SessionToken, assumeRoleArn) if err != nil { return nil, fmt.Errorf("role assumption failed for %s: %s", assumeRoleArn, err) } } - finalCreds := &AwsCredentials{ - AccessKeyId: id, - SecretAccessKey: secret, - SessionToken: token, - Expiration: expiration, - RoleArn: roleArn, - } - return finalCreds, nil + return resp, nil } // GetCredentials requests credentials from ConsoleMe then follows the provided chain of roles to diff --git a/creds/consoleme.go b/creds/consoleme.go index 332418e..cb77801 100644 --- a/creds/consoleme.go +++ b/creds/consoleme.go @@ -26,7 +26,7 @@ import ( "net/http" "net/url" "runtime" - "syscall" + "strconv" "time" "github.com/netflix/weep/metadata" @@ -52,13 +52,16 @@ type Account struct { // HTTPClient is the interface we expect HTTP clients to implement. type HTTPClient interface { - Do(*http.Request) (*http.Response, error) + Do(req *http.Request) (*http.Response, error) + GetRoleCredentials(role string, ipRestrict bool) (*AwsCredentials, error) + CloseIdleConnections() + buildRequest(string, string, io.Reader) (*http.Request, error) } // Client represents a ConsoleMe client. type Client struct { - Httpc HTTPClient - Host string + http.Client + Host string } // GetClient creates an authenticated ConsoleMe client @@ -98,7 +101,7 @@ func GetClient() (*Client, error) { // NewClient takes a ConsoleMe hostname and *http.Client, and returns a // ConsoleMe client that will talk to that ConsoleMe instance for AWS Credentials. -func NewClient(hostname string, httpc HTTPClient) (*Client, error) { +func NewClient(hostname string, httpc *http.Client) (*Client, error) { if len(hostname) == 0 { return nil, errors.New("hostname cannot be empty string") } @@ -108,8 +111,8 @@ func NewClient(hostname string, httpc HTTPClient) (*Client, error) { } c := &Client{ - Httpc: httpc, - Host: hostname, + Client: *httpc, + Host: hostname, } return c, nil @@ -117,24 +120,30 @@ func NewClient(hostname string, httpc HTTPClient) (*Client, error) { func (c *Client) buildRequest(method string, resource string, body io.Reader) (*http.Request, error) { urlStr := c.Host + "/api/v1" + resource + req, err := http.NewRequest(method, urlStr, body) + if err != nil { + return nil, err + } + req.Header.Set("User-Agent", userAgent) + req.Header.Add("Content-Type", "application/json") - return http.NewRequest(method, urlStr, body) + return req, nil } -// do invokes an HTTP request, and returns the response. This also sets the -// User-Agent of the client. -func (c *Client) do(req *http.Request) (*http.Response, error) { - req.Header.Set(("User-Agent"), userAgent) - req.Header.Add("Content-Type", "application/json") - - return c.Httpc.Do(req) +// CloseIdleConnections calls CloseIdleConnections() on the client's HTTP transport. +func (c *Client) CloseIdleConnections() { + transport, ok := c.Client.Transport.(*http.Transport) + if !ok { + // This is unlikely, but we'll fail out anyway. + return + } + transport.CloseIdleConnections() } // accounts returns all accounts, and allows you to filter the accounts by sub-resources // like: /accounts/service/support func (c *Client) Roles(showAll bool) ([]string, error) { - var cmCredentialErrorMessageType ConsolemeCredentialErrorMessageType - req, err := c.buildRequest(http.MethodGet, "/get_roles?all=true", nil) + req, err := c.buildRequest(http.MethodGet, "/get_roles", nil) if err != nil { return nil, errors.Wrap(err, "failed to build request") } @@ -146,34 +155,19 @@ func (c *Client) Roles(showAll bool) ([]string, error) { } req.URL.RawQuery = q.Encode() - resp, err := c.do(req) + resp, err := c.Do(req) if err != nil { return nil, errors.Wrap(err, "failed to action request") } defer resp.Body.Close() document, err := ioutil.ReadAll(resp.Body) - // Handle invalid_jwt error from ConsoleMe by deleting local credentials and asking user to retry - if resp.StatusCode == 403 { - if err := json.Unmarshal(document, &cmCredentialErrorMessageType); err != nil { - return nil, err - } - if cmCredentialErrorMessageType.Code == "invalid_jwt" { - log.Errorf("Authentication is invalid or has expired. Please restart weep to re-authenticate.") - err = challenge.DeleteLocalWeepCredentials() - if err != nil { - return nil, errors.Wrap(err, "Failed to delete invalid Weep jwt") - } - } - } - // Handle non-200 errors - if resp.StatusCode != 200 { - return nil, fmt.Errorf("unexpected HTTP status %s, want 200. Body: %s", resp.Status, string(document)) - } - if err != nil { return nil, errors.Wrap(err, "failed to read response body") } + if resp.StatusCode != http.StatusOK { + return nil, parseError(resp.StatusCode, document) + } var roles []string if err := json.Unmarshal(document, &roles); err != nil { @@ -183,9 +177,35 @@ func (c *Client) Roles(showAll bool) ([]string, error) { return roles, nil } +func parseError(statusCode int, rawErrorResponse []byte) error { + var errorResponse ConsolemeCredentialErrorMessageType + if err := json.Unmarshal(rawErrorResponse, &errorResponse); err != nil { + return errors.Wrap(err, "failed to unmarshal JSON") + } + + switch errorResponse.Code { + case "901": + return werrors.MultipleMatchingRoles + case "905": + return werrors.MutualTLSCertNeedsRefreshError + case "invalid_jwt": + log.Errorf("Authentication is invalid or has expired. Please restart weep to re-authenticate.") + err := challenge.DeleteLocalWeepCredentials() + if err != nil { + log.Errorf("failed to delete credentials: %v", err) + } + return werrors.InvalidJWT + default: + return fmt.Errorf("unexpected HTTP status %d, want 200. Response: %s", statusCode, string(rawErrorResponse)) + } +} + func (c *Client) GetRoleCredentials(role string, ipRestrict bool) (*AwsCredentials, error) { + return getRoleCredentialsFunc(c, role, ipRestrict) +} + +func getRoleCredentialsFunc(c HTTPClient, role string, ipRestrict bool) (*AwsCredentials, error) { var credentialsResponse ConsolemeCredentialResponseType - var cmCredentialErrorMessageType ConsolemeCredentialErrorMessageType cmCredRequest := ConsolemeCredentialRequestType{ RequestedRole: role, @@ -207,39 +227,19 @@ func (c *Client) GetRoleCredentials(role string, ipRestrict bool) (*AwsCredentia return credentialsResponse.Credentials, errors.Wrap(err, "failed to build request") } - resp, err := c.do(req) + resp, err := c.Do(req) if err != nil { return credentialsResponse.Credentials, errors.Wrap(err, "failed to action request") } defer resp.Body.Close() document, err := ioutil.ReadAll(resp.Body) - if resp.StatusCode != 200 { - if resp.StatusCode == 403 { - if err != nil { - return credentialsResponse.Credentials, errors.Wrap(err, "failed to read response body") - } - if err := json.Unmarshal(document, &cmCredentialErrorMessageType); err != nil { - return credentialsResponse.Credentials, errors.Wrap(err, "failed to unmarshal JSON") - } - if cmCredentialErrorMessageType.Code == "905" { - return credentialsResponse.Credentials, fmt.Errorf(viper.GetString("mtls_settings.old_cert_message")) - } - if cmCredentialErrorMessageType.Code == "invalid_jwt" { - log.Errorf("Authentication is invalid or has expired. Please restart weep to re-authenticate.") - err = challenge.DeleteLocalWeepCredentials() - if err != nil { - fmt.Println(err.Error()) - } - syscall.Exit(1) - } - } - return credentialsResponse.Credentials, fmt.Errorf("unexpected HTTP status %s, want 200. Response: %s", resp.Status, string(document)) - } - if err != nil { return credentialsResponse.Credentials, errors.Wrap(err, "failed to read response body") } + if resp.StatusCode != http.StatusOK { + return credentialsResponse.Credentials, parseError(resp.StatusCode, document) + } if err := json.Unmarshal(document, &credentialsResponse); err != nil { return credentialsResponse.Credentials, errors.Wrap(err, "failed to unmarshal JSON") @@ -269,27 +269,54 @@ func defaultTransport() *http.Transport { } type ClientMock struct { - DoFunc func(req *http.Request) (*http.Response, error) + DoFunc func(req *http.Request) (*http.Response, error) + GetRoleCredentialsFunc func(role string, ipRestrict bool) (*AwsCredentials, error) +} + +func (c *ClientMock) GetRoleCredentials(role string, ipRestrict bool) (*AwsCredentials, error) { + return getRoleCredentialsFunc(c, role, ipRestrict) +} + +func (c *ClientMock) CloseIdleConnections() {} + +func (c *ClientMock) buildRequest(string, string, io.Reader) (*http.Request, error) { + return &http.Request{}, nil } func (c *ClientMock) Do(req *http.Request) (*http.Response, error) { return c.DoFunc(req) } -func GetTestClient(responseBody interface{}) (*Client, error) { +func GetTestClient(responseBody interface{}) (HTTPClient, error) { + var responseCredentials *AwsCredentials + var responseCode = 200 + if c, ok := responseBody.(ConsolemeCredentialResponseType); ok { + responseCredentials = c.Credentials + } + if e, ok := responseBody.(ConsolemeCredentialErrorMessageType); ok { + code, err := strconv.Atoi(e.Code) + if err == nil { + responseCode = code + } + } resp, err := json.Marshal(responseBody) if err != nil { return nil, err } - client := &Client{ - Httpc: &ClientMock{ - DoFunc: func(*http.Request) (*http.Response, error) { - r := ioutil.NopCloser(bytes.NewReader(resp)) - return &http.Response{ - StatusCode: 200, - Body: r, - }, nil - }, + var client HTTPClient + client = &ClientMock{ + DoFunc: func(*http.Request) (*http.Response, error) { + r := ioutil.NopCloser(bytes.NewReader(resp)) + return &http.Response{ + StatusCode: responseCode, + Body: r, + }, nil + }, + GetRoleCredentialsFunc: func(role string, ipRestrict bool) (*AwsCredentials, error) { + if responseCredentials != nil { + return responseCredentials, nil + } + return &AwsCredentials{RoleArn: role}, nil }, } return client, nil diff --git a/creds/refreshable.go b/creds/refreshable.go index e7ba88b..dd9eed6 100644 --- a/creds/refreshable.go +++ b/creds/refreshable.go @@ -19,6 +19,8 @@ package creds import ( "time" + "github.com/spf13/viper" + "github.com/netflix/weep/errors" "github.com/aws/aws-sdk-go/aws/credentials" @@ -26,7 +28,7 @@ import ( // NewRefreshableProvider creates an AWS credential provider that will automatically refresh credentials // when they are close to expiring -func NewRefreshableProvider(client *Client, role, region string, assumeChain []string, noIpRestrict bool) (*RefreshableProvider, error) { +func NewRefreshableProvider(client HTTPClient, role, region string, assumeChain []string, noIpRestrict bool) (*RefreshableProvider, error) { rp := &RefreshableProvider{ Role: role, Region: region, @@ -84,21 +86,32 @@ func (rp *RefreshableProvider) refresh() error { rp.Lock() defer rp.Unlock() +RetryLoop: for i := 0; i < rp.retries; i++ { newCreds, err = GetCredentialsC(rp.client, rp.Role, rp.NoIpRestrict, rp.AssumeChain) - if err != nil { - log.Errorf("failed to get refreshed credentials: %s", err.Error()) + switch err { + case nil: + // Everything is happy, so we don't need to retry + break RetryLoop + case errors.MutualTLSCertNeedsRefreshError: + log.Error(viper.GetString("mtls_settings.old_cert_message")) + // Only prep for the next request and sleep if we have remaining retries if i != rp.retries-1 { - // only sleep if we have remaining retries + // The http.Client, with the best of intentions, will hold the connection open, + // meaning that an auto-updated cert won't be used by the client. + rp.client.CloseIdleConnections() time.Sleep(retryDelay) } - } else { - break + case errors.MultipleMatchingRoles: + return err + default: + log.Errorf("failed to get refreshed credentials: %s", err.Error()) + return err } } - if newCreds == nil { - log.Error("Unable to retrieve credentials from ConsoleMe") - return errors.CredentialRetrievalError + if err != nil { + log.Errorf("Unable to retrieve credentials from ConsoleMe: %v", err) + return err } rp.Expiration = newCreds.Expiration diff --git a/creds/refreshable_test.go b/creds/refreshable_test.go index 17bfd2a..fb22f3c 100644 --- a/creds/refreshable_test.go +++ b/creds/refreshable_test.go @@ -173,10 +173,10 @@ func TestRefreshableProvider_refresh(t *testing.T) { Retries: 2, RetryDelay: 1, CredentialResponse: ConsolemeCredentialErrorMessageType{ - Code: "403", + Code: "901", Message: "Nope", }, - ExpectedError: errors.CredentialRetrievalError, + ExpectedError: errors.MultipleMatchingRoles, ExpectedResult: nil, }, } diff --git a/creds/types.go b/creds/types.go index 8631ae8..f73dfa4 100644 --- a/creds/types.go +++ b/creds/types.go @@ -37,7 +37,7 @@ type AwsCredentials struct { type RefreshableProvider struct { sync.RWMutex value credentials.Value - client *Client + client HTTPClient retries int retryDelay int Expiration Time diff --git a/errors/errors.go b/errors/errors.go index 42ce4b8..7c53bc4 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -21,8 +21,11 @@ type Error string func (e Error) Error() string { return string(e) } const ( - NoCredentialsFoundInCache = Error("no credentials found in cache") - NoDefaultRoleSet = Error("no default role set") - CredentialGenerationFailed = Error("credential generation failed") - CredentialRetrievalError = Error("failed to retrieve credentials from broker") + NoCredentialsFoundInCache = Error("no credentials found in cache") + NoDefaultRoleSet = Error("no default role set") + CredentialGenerationFailed = Error("credential generation failed") + CredentialRetrievalError = Error("failed to retrieve credentials from broker") + InvalidJWT = Error("JWT is invalid") + MutualTLSCertNeedsRefreshError = Error("mTLS cert needs to be refreshed") + MultipleMatchingRoles = Error("more than one matching role for search string") ) diff --git a/httpAuth/mtls/certificate.go b/httpAuth/mtls/certificate.go index d6090f2..420c91c 100644 --- a/httpAuth/mtls/certificate.go +++ b/httpAuth/mtls/certificate.go @@ -23,9 +23,10 @@ import ( // reloading the certificate when a file change is detected. type wrappedCertificate struct { sync.RWMutex - certificate *tls.Certificate - certFile string - keyFile string + certificate *tls.Certificate + x509Certificate *x509.Certificate + certFile string + keyFile string } // newWrappedCertificate initializes and returns a wrappedCertificate that will auto- @@ -47,12 +48,22 @@ func newWrappedCertificate(certFile, keyFile string) (*wrappedCertificate, error // getCertificate is a function to be used as the GetClientCertificate member of a tls.Config func (wc *wrappedCertificate) getCertificate(clientHello *tls.CertificateRequestInfo) (*tls.Certificate, error) { + log.Debug("getCertificate called") wc.RLock() defer wc.RUnlock() return wc.certificate, nil } +func (wc *wrappedCertificate) watchExpiration() { + expiration := wc.x509Certificate.NotAfter + if expiration.Before(time.Now()) { + // cert is expired, set unhealthy + } else if expiration.Before(time.Now().Add(-6 * time.Hour)) { + // cert is expiring soon, log warning + } +} + // loadCertificate replaces certificate with a keypair loaded in from the filesystem. func (wc *wrappedCertificate) loadCertificate() { log.Debug("reloading mTLS certificate") @@ -64,6 +75,10 @@ func (wc *wrappedCertificate) loadCertificate() { return } wc.certificate = &cert + wc.x509Certificate, err = x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + log.Errorf("could not parse x509 certificate") + } wc.updateInstanceInfo() } @@ -95,7 +110,7 @@ func (wc *wrappedCertificate) autoRefresh() { log.Warn("problem with mTLS file watcher") return } - log.Infof("event received: %v", event) + log.Debugf("event received: %v", event) if event.Op&fsnotify.Write == fsnotify.Write { debounced(func() { wc.loadCertificate() }) } @@ -127,15 +142,7 @@ func (wc *wrappedCertificate) Fingerprint() string { } func (wc *wrappedCertificate) CreateTime() time.Time { - var createTime time.Time - x509cert, err := x509.ParseCertificate(wc.certificate.Certificate[0]) - if err != nil { - // TODO: handle this better - fmt.Printf("ow: %v\n", err) - return createTime - } - createTime = x509cert.NotBefore - return createTime + return wc.x509Certificate.NotBefore } // updateInstanceInfo makes a call to update the metadata package with the creation time