From 90e7343fb58477d537fc3527056dce6f1850db83 Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Tue, 19 Mar 2024 12:31:11 -0500 Subject: [PATCH] Add IPv6 test to WebhookAuthenticator ctrl tests Co-authored-by: Benjamin A. Petersen --- .../webhookcachefiller_test.go | 164 ++++++++++++------ 1 file changed, 113 insertions(+), 51 deletions(-) diff --git a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go index a786e2f39..8aa3a6e55 100644 --- a/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go +++ b/internal/controller/authenticator/webhookcachefiller/webhookcachefiller_test.go @@ -117,7 +117,7 @@ func TestController(t *testing.T) { }) localWithExampleDotComMux := http.NewServeMux() - localWithExampleDotComCertServer := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + hostLocalWithExampleDotComCertServer := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { tlsserver.AssertTLS(t, r, ptls.Default) localWithExampleDotComMux.ServeHTTP(w, r) }), func(thisServer *httptest.Server) { @@ -129,7 +129,7 @@ func TestController(t *testing.T) { }) goodMux := http.NewServeMux() - goodWebhookServer := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + hostGoodDefaultServingCertServer := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { tlsserver.AssertTLS(t, r, ptls.Default) goodMux.ServeHTTP(w, r) }), tlsserver.RecordTLSHello) @@ -142,14 +142,17 @@ func TestController(t *testing.T) { w.WriteHeader(http.StatusNotFound) fmt.Fprint(w, "404 nothing here") })) - goodEndpoint := goodWebhookServer.URL - goodEndpointBut404 := goodEndpoint + "/nothing/here" + + goodWebhookDefaultServingCertEndpoint := hostGoodDefaultServingCertServer.URL + goodWebhookDefaultServingCertEndpointBut404 := goodWebhookDefaultServingCertEndpoint + "/nothing/here" localhostURL, err := url.Parse(hostAsLocalhostWebhookServer.URL) require.NoError(t, err) - localhostEndpointURL := fmt.Sprintf("%s:%s", "https://localhost", localhostURL.Port()) - badEndpoint := "https://.café .com/café/café/café/coffee" + hostAs127001WebhookServerURL, err := url.Parse(hostAs127001WebhookServer.URL) + require.NoError(t, err) + + badEndpointInvalidURL := "https://.café .com/café/café/café/coffee" badEndpointNoHTTPS := "http://localhost" nowDoesntMatter := time.Date(1122, time.September, 33, 4, 55, 56, 778899, time.Local) @@ -160,39 +163,32 @@ func TestController(t *testing.T) { frozenTimeInThePast := metav1.NewTime(timeInThePast) goodWebhookAuthenticatorSpecWithCA := auth1alpha1.WebhookAuthenticatorSpec{ - Endpoint: goodEndpoint, - TLS: conciergetestutil.TLSSpecFromTLSConfig(goodWebhookServer.TLS), - } - localhostWebhookAuthenticatorSpecWithCA := auth1alpha1.WebhookAuthenticatorSpec{ - Endpoint: localhostEndpointURL, - TLS: &auth1alpha1.TLSSpec{ - // CA Bundle for validating the server's certs - CertificateAuthorityData: base64.StdEncoding.EncodeToString(caForLocalhostAsHostname.Bundle()), - }, + Endpoint: goodWebhookDefaultServingCertEndpoint, + TLS: conciergetestutil.TLSSpecFromTLSConfig(hostGoodDefaultServingCertServer.TLS), } localWithExampleDotComWeebhookAuthenticatorSpec := auth1alpha1.WebhookAuthenticatorSpec{ // CA for example.com, TLS serving cert for example.com, but endpoint is still localhost - Endpoint: localWithExampleDotComCertServer.URL, + Endpoint: hostLocalWithExampleDotComCertServer.URL, TLS: &auth1alpha1.TLSSpec{ // CA Bundle for example.com CertificateAuthorityData: base64.StdEncoding.EncodeToString(caForExampleDotCom.Bundle()), }, } goodWebhookAuthenticatorSpecWithoutCA := auth1alpha1.WebhookAuthenticatorSpec{ - Endpoint: goodEndpoint, + Endpoint: goodWebhookDefaultServingCertEndpoint, TLS: &auth1alpha1.TLSSpec{CertificateAuthorityData: ""}, } goodWebhookAuthenticatorSpecWith404Endpoint := auth1alpha1.WebhookAuthenticatorSpec{ - Endpoint: goodEndpointBut404, - TLS: conciergetestutil.TLSSpecFromTLSConfig(goodWebhookServer.TLS), + Endpoint: goodWebhookDefaultServingCertEndpointBut404, + TLS: conciergetestutil.TLSSpecFromTLSConfig(hostGoodDefaultServingCertServer.TLS), } badWebhookAuthenticatorSpecInvalidTLS := auth1alpha1.WebhookAuthenticatorSpec{ - Endpoint: goodEndpoint, + Endpoint: goodWebhookDefaultServingCertEndpoint, TLS: &auth1alpha1.TLSSpec{CertificateAuthorityData: "invalid base64-encoded data"}, } badWebhookAuthenticatorSpecGoodEndpointButUnknownCA := auth1alpha1.WebhookAuthenticatorSpec{ - Endpoint: goodEndpoint, + Endpoint: goodWebhookDefaultServingCertEndpoint, TLS: &auth1alpha1.TLSSpec{ CertificateAuthorityData: base64.StdEncoding.EncodeToString(pemServerCertForUnknownServer), }, @@ -405,7 +401,7 @@ func TestController(t *testing.T) { }, Spec: goodWebhookAuthenticatorSpecWithCA, Status: auth1alpha1.WebhookAuthenticatorStatus{ - Conditions: allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0), + Conditions: allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), Phase: "Ready", }, }, @@ -416,7 +412,7 @@ func TestController(t *testing.T) { "timestamp": "2099-08-08T13:57:36.123456Z", "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", - "endpoint": goodWebhookServer.URL, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhook": map[string]interface{}{ "name": "test-name", }, @@ -440,7 +436,7 @@ func TestController(t *testing.T) { Spec: goodWebhookAuthenticatorSpecWithCA, Status: auth1alpha1.WebhookAuthenticatorStatus{ Conditions: conditionstestutil.Replace( - allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 666), + allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 666), []metav1.Condition{ sadReadyCondition(frozenTimeInThePast, 777), happyEndpointURLValid(frozenTimeInThePast, 888), @@ -456,7 +452,7 @@ func TestController(t *testing.T) { "timestamp": "2099-08-08T13:57:36.123456Z", "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", - "endpoint": goodWebhookServer.URL, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhook": map[string]interface{}{ "name": "test-name", }, @@ -470,7 +466,7 @@ func TestController(t *testing.T) { Spec: goodWebhookAuthenticatorSpecWithCA, Status: auth1alpha1.WebhookAuthenticatorStatus{ Conditions: conditionstestutil.Replace( - allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0), + allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), []metav1.Condition{ happyEndpointURLValid(frozenTimeInThePast, 0), }, @@ -503,7 +499,7 @@ func TestController(t *testing.T) { "timestamp": "2099-08-08T13:57:36.123456Z", "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", - "endpoint": goodWebhookServer.URL, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhook": map[string]interface{}{ "name": "test-name", }, @@ -516,7 +512,7 @@ func TestController(t *testing.T) { }, Spec: goodWebhookAuthenticatorSpecWithCA, Status: auth1alpha1.WebhookAuthenticatorStatus{ - Conditions: allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0), + Conditions: allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), Phase: "Ready", }, }) @@ -548,7 +544,7 @@ func TestController(t *testing.T) { Spec: goodWebhookAuthenticatorSpecWithoutCA, Status: auth1alpha1.WebhookAuthenticatorStatus{ Conditions: conditionstestutil.Replace( - allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0), + allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), []metav1.Condition{ happyTLSConfigurationValidNoCA(frozenMetav1Now, 0), sadTLSConnectionNegotiationValid(frozenMetav1Now, 0), @@ -588,7 +584,7 @@ func TestController(t *testing.T) { Spec: badWebhookAuthenticatorSpecInvalidTLS, Status: auth1alpha1.WebhookAuthenticatorStatus{ Conditions: conditionstestutil.Replace( - allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0), + allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), []metav1.Condition{ sadTLSConfigurationValid(frozenMetav1Now, 0), unknownTLSConnectionNegotiationValid(frozenMetav1Now, 0), @@ -617,7 +613,7 @@ func TestController(t *testing.T) { Name: "test-name", }, Spec: auth1alpha1.WebhookAuthenticatorSpec{ - Endpoint: badEndpoint, + Endpoint: badEndpointInvalidURL, }, }, }, @@ -627,11 +623,11 @@ func TestController(t *testing.T) { Name: "test-name", }, Spec: auth1alpha1.WebhookAuthenticatorSpec{ - Endpoint: badEndpoint, + Endpoint: badEndpointInvalidURL, }, Status: auth1alpha1.WebhookAuthenticatorStatus{ Conditions: conditionstestutil.Replace( - allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0), + allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), []metav1.Condition{ happyTLSConfigurationValidNoCA(frozenMetav1Now, 0), sadEndpointURLValid("https://.café .com/café/café/café/coffee", frozenMetav1Now, 0), @@ -674,7 +670,7 @@ func TestController(t *testing.T) { }, Status: auth1alpha1.WebhookAuthenticatorStatus{ Conditions: conditionstestutil.Replace( - allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0), + allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), []metav1.Condition{ happyTLSConfigurationValidNoCA(frozenMetav1Now, 0), sadEndpointURLValidHTTPS("http://localhost", frozenMetav1Now, 0), @@ -715,7 +711,7 @@ func TestController(t *testing.T) { Spec: badWebhookAuthenticatorSpecGoodEndpointButUnknownCA, Status: auth1alpha1.WebhookAuthenticatorStatus{ Conditions: conditionstestutil.Replace( - allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0), + allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), []metav1.Condition{ unknownAuthenticatorValid(frozenMetav1Now, 0), sadReadyCondition(frozenMetav1Now, 0), @@ -754,7 +750,7 @@ func TestController(t *testing.T) { "timestamp": "2099-08-08T13:57:36.123456Z", "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", - "endpoint": goodEndpointBut404, + "endpoint": goodWebhookDefaultServingCertEndpointBut404, "webhook": map[string]interface{}{ "name": "test-name", }, @@ -767,7 +763,7 @@ func TestController(t *testing.T) { }, Spec: goodWebhookAuthenticatorSpecWith404Endpoint, Status: auth1alpha1.WebhookAuthenticatorStatus{ - Conditions: allHappyConditionsSuccess(goodEndpointBut404, frozenMetav1Now, 0), + Conditions: allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpointBut404, frozenMetav1Now, 0), Phase: "Ready", }, }) @@ -780,16 +776,22 @@ func TestController(t *testing.T) { }, wantCacheEntries: 1, }, { - name: "validateTLSNegotiation: localhost hostname instead of 127.0.0.1 should still dial correctly as dialer should handle hostnames as well as IPv4 and IPv6 addresses", + name: "validateTLSNegotiation: localhost hostname instead of 127.0.0.1 should still dial correctly as dialer should handle hostnames as well as IPv4", syncKey: controllerlib.Key{Name: "test-name"}, webhooks: []runtime.Object{ &auth1alpha1.WebhookAuthenticator{ ObjectMeta: metav1.ObjectMeta{ Name: "test-name", }, - Spec: localhostWebhookAuthenticatorSpecWithCA, + Spec: auth1alpha1.WebhookAuthenticatorSpec{ + Endpoint: fmt.Sprintf("%s:%s", "https://localhost", localhostURL.Port()), + TLS: &auth1alpha1.TLSSpec{ + // CA Bundle for validating the server's certs + CertificateAuthorityData: base64.StdEncoding.EncodeToString(caForLocalhostAsHostname.Bundle()), + }, + }, Status: auth1alpha1.WebhookAuthenticatorStatus{ - Conditions: allHappyConditionsSuccess(localhostEndpointURL, frozenMetav1Now, 0), + Conditions: allHappyConditionsSuccess(fmt.Sprintf("%s:%s", "https://localhost", localhostURL.Port()), frozenMetav1Now, 0), Phase: "Ready", }, }, @@ -800,7 +802,7 @@ func TestController(t *testing.T) { "timestamp": "2099-08-08T13:57:36.123456Z", "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", - "endpoint": localhostEndpointURL, + "endpoint": fmt.Sprintf("%s:%s", "https://localhost", localhostURL.Port()), "webhook": map[string]interface{}{ "name": "test-name", }, @@ -813,6 +815,66 @@ func TestController(t *testing.T) { } }, wantCacheEntries: 1, + }, { + name: "validateTLSNegotiation: localhost IPv6 address instead of 127.0.0.1 should still dial correctly as dialer should handle addresses", + syncKey: controllerlib.Key{Name: "test-name"}, + tlsDialerFunc: func(network string, addr string, config *tls.Config) (*tls.Conn, error) { + // We are dialing a different server here since CI doesn't have the linux IPv6 stack installed. + // This test proves that IPv6 addresses are parsed & handled correctly before we call tls.Dial in production code. + return tls.Dial(network, hostAs127001WebhookServerURL.Host, &tls.Config{ + MinVersion: tls.VersionTLS12, + RootCAs: caForLocalhostAs127001.Pool(), + }) + }, + webhooks: []runtime.Object{ + &auth1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: auth1alpha1.WebhookAuthenticatorSpec{ + Endpoint: fmt.Sprintf("%s:%s", "https://[0:0:0:0:0:0:0:1]", hostAs127001WebhookServerURL.Port()), + TLS: &auth1alpha1.TLSSpec{ + CertificateAuthorityData: base64.StdEncoding.EncodeToString(caForLocalhostAs127001.Bundle()), + }, + }, + }, + }, + wantLogs: []map[string]any{ + { + "level": "info", + "timestamp": "2099-08-08T13:57:36.123456Z", + "logger": "webhookcachefiller-controller", + "message": "added new webhook authenticator", + "endpoint": fmt.Sprintf("%s:%s", "https://[0:0:0:0:0:0:0:1]", hostAs127001WebhookServerURL.Port()), + "webhook": map[string]interface{}{ + "name": "test-name", + }, + }, + }, + wantActions: func() []coretesting.Action { + updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &auth1alpha1.WebhookAuthenticator{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: auth1alpha1.WebhookAuthenticatorSpec{ + Endpoint: fmt.Sprintf("%s:%s", "https://[0:0:0:0:0:0:0:1]", hostAs127001WebhookServerURL.Port()), + TLS: &auth1alpha1.TLSSpec{ + CertificateAuthorityData: base64.StdEncoding.EncodeToString(caForLocalhostAs127001.Bundle()), + }, + }, + Status: auth1alpha1.WebhookAuthenticatorStatus{ + Conditions: allHappyConditionsSuccess(fmt.Sprintf("%s:%s", "https://[0:0:0:0:0:0:0:1]", hostAs127001WebhookServerURL.Port()), frozenMetav1Now, 0), + Phase: "Ready", + }, + }) + updateStatusAction.Subresource = "status" + return []coretesting.Action{ + coretesting.NewListAction(webhookAuthenticatorGVR, webhookAuthenticatorGVK, "", metav1.ListOptions{}), + coretesting.NewWatchAction(webhookAuthenticatorGVR, "", metav1.ListOptions{}), + updateStatusAction, + } + }, + wantCacheEntries: 1, }, { name: "validateTLSNegotiation: localhost as IP address 127.0.0.1 should still dial correctly as dialer should handle hostnames as well as IPv4 and IPv6 addresses", syncKey: controllerlib.Key{Name: "test-name"}, @@ -862,7 +924,7 @@ func TestController(t *testing.T) { }, Spec: localWithExampleDotComWeebhookAuthenticatorSpec, Status: auth1alpha1.WebhookAuthenticatorStatus{ - Conditions: allHappyConditionsSuccess(localWithExampleDotComCertServer.URL, frozenMetav1Now, 0), + Conditions: allHappyConditionsSuccess(hostLocalWithExampleDotComCertServer.URL, frozenMetav1Now, 0), Phase: "Ready", }, }, @@ -875,7 +937,7 @@ func TestController(t *testing.T) { Spec: localWithExampleDotComWeebhookAuthenticatorSpec, Status: auth1alpha1.WebhookAuthenticatorStatus{ Conditions: conditionstestutil.Replace( - allHappyConditionsSuccess(localWithExampleDotComCertServer.URL, frozenMetav1Now, 0), + allHappyConditionsSuccess(hostLocalWithExampleDotComCertServer.URL, frozenMetav1Now, 0), []metav1.Condition{ sadTLSConnectionNegotiationNoIPSANs(frozenMetav1Now, 0), unknownAuthenticatorValid(frozenMetav1Now, 0), @@ -904,7 +966,7 @@ func TestController(t *testing.T) { }, Spec: goodWebhookAuthenticatorSpecWithCA, Status: auth1alpha1.WebhookAuthenticatorStatus{ - Conditions: allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0), + Conditions: allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), Phase: "Ready", }, }, @@ -915,7 +977,7 @@ func TestController(t *testing.T) { "timestamp": "2099-08-08T13:57:36.123456Z", "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", - "endpoint": goodWebhookServer.URL, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhook": map[string]interface{}{ "name": "test-name", }, @@ -939,7 +1001,7 @@ func TestController(t *testing.T) { Spec: goodWebhookAuthenticatorSpecWithCA, Status: auth1alpha1.WebhookAuthenticatorStatus{ Conditions: conditionstestutil.Replace( - allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0), + allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), []metav1.Condition{ sadReadyCondition(frozenMetav1Now, 0), }, @@ -954,7 +1016,7 @@ func TestController(t *testing.T) { "timestamp": "2099-08-08T13:57:36.123456Z", "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", - "endpoint": goodWebhookServer.URL, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhook": map[string]interface{}{ "name": "test-name", }, @@ -967,7 +1029,7 @@ func TestController(t *testing.T) { }, Spec: goodWebhookAuthenticatorSpecWithCA, Status: auth1alpha1.WebhookAuthenticatorStatus{ - Conditions: allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0), + Conditions: allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), Phase: "Ready", }, }) @@ -999,7 +1061,7 @@ func TestController(t *testing.T) { Spec: goodWebhookAuthenticatorSpecWithCA, Status: auth1alpha1.WebhookAuthenticatorStatus{ Conditions: conditionstestutil.Replace( - allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0), + allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), []metav1.Condition{ sadReadyCondition(frozenMetav1Now, 0), }, @@ -1014,7 +1076,7 @@ func TestController(t *testing.T) { "timestamp": "2099-08-08T13:57:36.123456Z", "logger": "webhookcachefiller-controller", "message": "added new webhook authenticator", - "endpoint": goodWebhookServer.URL, + "endpoint": goodWebhookDefaultServingCertEndpoint, "webhook": map[string]interface{}{ "name": "test-name", }, @@ -1032,7 +1094,7 @@ func TestController(t *testing.T) { }, Spec: goodWebhookAuthenticatorSpecWithCA, Status: auth1alpha1.WebhookAuthenticatorStatus{ - Conditions: allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0), + Conditions: allHappyConditionsSuccess(goodWebhookDefaultServingCertEndpoint, frozenMetav1Now, 0), Phase: "Ready", }, })