diff --git a/config/http_config.go b/config/http_config.go index 95b7711d..a124c74a 100644 --- a/config/http_config.go +++ b/config/http_config.go @@ -1476,7 +1476,7 @@ func (t *tlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { rt := t.rt t.mtx.RUnlock() if equal { - // The CA cert hasn't changed, use the existing RoundTripper. + // The TLS materials (CA, cert, key) haven't changed, use the existing RoundTripper. return rt.RoundTrip(req) } @@ -1484,10 +1484,7 @@ func (t *tlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { // The cert and key files are read separately by the client // using GetClientCertificate. tlsConfig := t.tlsConfig.Clone() - if !updateRootCA(tlsConfig, caData) { - if t.settings.CA == nil { - return nil, errors.New("unable to use specified CA cert: none configured") - } + if t.settings.CA != nil && !updateRootCA(tlsConfig, caData) { return nil, fmt.Errorf("unable to use specified CA cert %s", t.settings.CA.Description()) } rt, err = t.newRT(tlsConfig) diff --git a/config/http_config_test.go b/config/http_config_test.go index 060598b7..11fe800e 100644 --- a/config/http_config_test.go +++ b/config/http_config_test.go @@ -2064,11 +2064,13 @@ func TestModifyTLSCertificates(t *testing.T) { } } +// TestTLSRoundTripper_NoCAConfigured verifies that client cert rotation works +// when no ca_file is configured. Regression test for prometheus/prometheus#16622. func TestTLSRoundTripper_NoCAConfigured(t *testing.T) { bs := getCertificateBlobs(t) - tmpDir, err := os.MkdirTemp("", "tlspanic") - require.NoErrorf(t, err, "Failed to create tmp dir") + tmpDir, err := os.MkdirTemp("", "tlsnocacert") + require.NoError(t, err) defer os.RemoveAll(tmpDir) cert, key := filepath.Join(tmpDir, "cert"), filepath.Join(tmpDir, "key") @@ -2090,20 +2092,24 @@ func TestTLSRoundTripper_NoCAConfigured(t *testing.T) { writeCertificate(bs, ClientCertificatePath, cert) writeCertificate(bs, ClientKeyNoPassPath, key) c, err := NewClientFromConfig(cfg, "test") - require.NoErrorf(t, err, "Error creating HTTP Client: %v", err) + require.NoError(t, err) req, err := http.NewRequest(http.MethodGet, testServer.URL, nil) - require.NoErrorf(t, err, "Error creating HTTP request: %v", err) + require.NoError(t, err) r, err := c.Do(req) - require.NoErrorf(t, err, "Can't connect to the test server") + require.NoErrorf(t, err, "request should succeed before cert rotation") r.Body.Close() - err = os.WriteFile(cert, []byte("-----BEGIN GARBAGE-----\nabc\n-----END GARBAGE-----\n"), 0o664) - require.NoError(t, err) + // Rotate the cert/key files to different (but still valid) certs. + // Tthe next RoundTrip should rebuild the transport. + writeCertificate(bs, ServerCertificatePath, cert) + writeCertificate(bs, ServerKeyPath, key) - _, err = c.Do(req) - require.ErrorContainsf(t, err, "unable to use specified CA cert: none configured", "Expected error to mention missing CA cert") + // The request still succeeds after cert rotation. + r, err = c.Do(req) + require.NoErrorf(t, err, "request should succeed after cert rotation without ca_file") + r.Body.Close() } // loadHTTPConfigJSON parses the JSON input s into a HTTPClientConfig.