Skip to content

Commit

Permalink
Update webhook status integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
benjaminapetersen committed Mar 13, 2024
1 parent 106ac31 commit 1e6e159
Showing 10 changed files with 445 additions and 68 deletions.
Original file line number Diff line number Diff line change
@@ -39,24 +39,24 @@ import (
)

const (
controllerName = "webhookcachefiller-controller"
typeReady = "Ready"
typeTLSBundleValid = "TLSBundleValid"
typeTLSConnetionNegotiationValid = "TLSConnetionNegotiationValid"
typeEndpointURLValid = "EndpointURLValid"
typeAuthenticatorValid = "AuthenticatorValid"
reasonSuccess = "Success"
reasonNotReady = "NotReady"
reasonUnableToValidate = "UnableToValidate"
reasonUnableToCreateTempFile = "UnableToCreateTempFile"
reasonUnableToMarshallKubeconfig = "UnableToMarshallKubeconfig"
reasonUnableToLoadKubeconfig = "UnableToLoadKubeconfig"
reasonUnableToInstantiateWebhook = "UnableToInstantiateWebhook"
reasonInvalidTLSConfiguration = "InvalidTLSConfiguration"
reasonInvalidEndpointURL = "InvalidEndpointURL"
reasonInvalidEndpointURLScheme = "InvalidEndpointURLScheme"
reasonUnableToDialServer = "UnableToDialServer"
msgUnableToValidate = "unable to validate; see other conditions for details"
controllerName = "webhookcachefiller-controller"
typeReady = "Ready"
typeTLSConfigurationValid = "TLSConfigurationValid"
typeTLSConnectionNegotiationValid = "TLSConnectionNegotiationValid"
typeEndpointURLValid = "EndpointURLValid"
typeAuthenticatorValid = "AuthenticatorValid"
reasonSuccess = "Success"
reasonNotReady = "NotReady"
reasonUnableToValidate = "UnableToValidate"
reasonUnableToCreateTempFile = "UnableToCreateTempFile"
reasonUnableToMarshallKubeconfig = "UnableToMarshallKubeconfig"
reasonUnableToLoadKubeconfig = "UnableToLoadKubeconfig"
reasonUnableToInstantiateWebhook = "UnableToInstantiateWebhook"
reasonInvalidTLSConfiguration = "InvalidTLSConfiguration"
reasonInvalidEndpointURL = "InvalidEndpointURL"
reasonInvalidEndpointURLScheme = "InvalidEndpointURLScheme"
reasonUnableToDialServer = "UnableToDialServer"
msgUnableToValidate = "unable to validate; see other conditions for details"
)

// New instantiates a new controllerlib.Controller which will populate the provided authncache.Cache.
@@ -281,7 +281,7 @@ func newWebhookAuthenticator(
func (c *webhookCacheFillerController) validateTLSNegotiation(certPool *x509.CertPool, endpointURL *url.URL, conditions []*metav1.Condition, prereqOk bool) ([]*metav1.Condition, error) {
if !prereqOk {
conditions = append(conditions, &metav1.Condition{
Type: typeTLSConnetionNegotiationValid,
Type: typeTLSConnectionNegotiationValid,
Status: metav1.ConditionUnknown,
Reason: reasonUnableToValidate,
Message: msgUnableToValidate,
@@ -307,7 +307,7 @@ func (c *webhookCacheFillerController) validateTLSNegotiation(certPool *x509.Cer
errText := "cannot dial server"
msg := fmt.Sprintf("%s: %s", errText, dialErr.Error())
conditions = append(conditions, &metav1.Condition{
Type: typeTLSConnetionNegotiationValid,
Type: typeTLSConnectionNegotiationValid,
Status: metav1.ConditionFalse,
Reason: reasonUnableToDialServer,
Message: msg,
@@ -322,7 +322,7 @@ func (c *webhookCacheFillerController) validateTLSNegotiation(certPool *x509.Cer
}

conditions = append(conditions, &metav1.Condition{
Type: typeTLSConnetionNegotiationValid,
Type: typeTLSConnectionNegotiationValid,
Status: metav1.ConditionTrue,
Reason: reasonSuccess,
Message: "tls verified",
@@ -335,7 +335,7 @@ func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *auth1alpha1.TL
if err != nil {
msg := fmt.Sprintf("%s: %s", "invalid TLS configuration", err.Error())
conditions = append(conditions, &metav1.Condition{
Type: typeTLSBundleValid,
Type: typeTLSConfigurationValid,
Status: metav1.ConditionFalse,
Reason: reasonInvalidTLSConfiguration,
Message: msg,
@@ -347,7 +347,7 @@ func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *auth1alpha1.TL
msg = "no CA bundle specified"
}
conditions = append(conditions, &metav1.Condition{
Type: typeTLSBundleValid,
Type: typeTLSConfigurationValid,
Status: metav1.ConditionTrue,
Reason: reasonSuccess,
Message: msg,
Original file line number Diff line number Diff line change
@@ -240,29 +240,29 @@ func TestController(t *testing.T) {
}
}

happyTLSBundleValidCAParsed := func(time metav1.Time, observedGeneration int64) metav1.Condition {
happyTLSConfigurationValidCAParsed := func(time metav1.Time, observedGeneration int64) metav1.Condition {
return metav1.Condition{
Type: "TLSBundleValid",
Type: "TLSConfigurationValid",
Status: "True",
ObservedGeneration: observedGeneration,
LastTransitionTime: time,
Reason: "Success",
Message: "successfully parsed specified CA bundle",
}
}
happyTLSBundleValidNoCA := func(time metav1.Time, observedGeneration int64) metav1.Condition {
happyTLSConfigurationValidNoCA := func(time metav1.Time, observedGeneration int64) metav1.Condition {
return metav1.Condition{
Type: "TLSBundleValid",
Type: "TLSConfigurationValid",
Status: "True",
ObservedGeneration: observedGeneration,
LastTransitionTime: time,
Reason: "Success",
Message: "no CA bundle specified",
}
}
sadTLSBundleValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
sadTLSConfigurationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
return metav1.Condition{
Type: "TLSBundleValid",
Type: "TLSConfigurationValid",
Status: "False",
ObservedGeneration: observedGeneration,
LastTransitionTime: time,
@@ -271,39 +271,39 @@ func TestController(t *testing.T) {
}
}

happyTLSConnetionNegotiationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
happyTLSConnectionNegotiationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
return metav1.Condition{
Type: "TLSConnetionNegotiationValid",
Type: "TLSConnectionNegotiationValid",
Status: "True",
ObservedGeneration: observedGeneration,
LastTransitionTime: time,
Reason: "Success",
Message: "tls verified",
}
}
unknownTLSConnetionNegotiationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
unknownTLSConnectionNegotiationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
return metav1.Condition{
Type: "TLSConnetionNegotiationValid",
Type: "TLSConnectionNegotiationValid",
Status: "Unknown",
ObservedGeneration: observedGeneration,
LastTransitionTime: time,
Reason: "UnableToValidate",
Message: "unable to validate; see other conditions for details",
}
}
sadTLSConnetionNegotiationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
sadTLSConnectionNegotiationValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
return metav1.Condition{
Type: "TLSConnetionNegotiationValid",
Type: "TLSConnectionNegotiationValid",
Status: "False",
ObservedGeneration: observedGeneration,
LastTransitionTime: time,
Reason: "UnableToDialServer",
Message: "cannot dial server: tls: failed to verify certificate: x509: certificate signed by unknown authority",
}
}
sadTLSConnetionNegotiationNoIPSANs := func(time metav1.Time, observedGeneration int64) metav1.Condition {
sadTLSConnectionNegotiationNoIPSANs := func(time metav1.Time, observedGeneration int64) metav1.Condition {
return metav1.Condition{
Type: "TLSConnetionNegotiationValid",
Type: "TLSConnectionNegotiationValid",
Status: "False",
ObservedGeneration: observedGeneration,
LastTransitionTime: time,
@@ -345,9 +345,9 @@ func TestController(t *testing.T) {

allHappyConditionsSuccess := func(endpoint string, someTime metav1.Time, observedGeneration int64) []metav1.Condition {
return conditionstestutil.SortByType([]metav1.Condition{
happyTLSBundleValidCAParsed(someTime, observedGeneration),
happyTLSConfigurationValidCAParsed(someTime, observedGeneration),
happyEndpointURLValid(someTime, observedGeneration),
happyTLSConnetionNegotiationValid(someTime, observedGeneration),
happyTLSConnectionNegotiationValid(someTime, observedGeneration),
happyAuthenticatorValid(someTime, observedGeneration),
happyReadyCondition(someTime, observedGeneration),
})
@@ -551,8 +551,8 @@ func TestController(t *testing.T) {
Conditions: conditionstestutil.Replace(
allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0),
[]metav1.Condition{
happyTLSBundleValidNoCA(frozenMetav1Now, 0),
sadTLSConnetionNegotiationValid(frozenMetav1Now, 0),
happyTLSConfigurationValidNoCA(frozenMetav1Now, 0),
sadTLSConnectionNegotiationValid(frozenMetav1Now, 0),
sadReadyCondition(frozenMetav1Now, 0),
unknownAuthenticatorValid(frozenMetav1Now, 0),
},
@@ -591,8 +591,8 @@ func TestController(t *testing.T) {
Conditions: conditionstestutil.Replace(
allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0),
[]metav1.Condition{
sadTLSBundleValid(frozenMetav1Now, 0),
unknownTLSConnetionNegotiationValid(frozenMetav1Now, 0),
sadTLSConfigurationValid(frozenMetav1Now, 0),
unknownTLSConnectionNegotiationValid(frozenMetav1Now, 0),
unknownAuthenticatorValid(frozenMetav1Now, 0),
sadReadyCondition(frozenMetav1Now, 0),
},
@@ -634,9 +634,9 @@ func TestController(t *testing.T) {
Conditions: conditionstestutil.Replace(
allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0),
[]metav1.Condition{
happyTLSBundleValidNoCA(frozenMetav1Now, 0),
happyTLSConfigurationValidNoCA(frozenMetav1Now, 0),
sadEndpointURLValid("https://.café .com/café/café/café/coffee", frozenMetav1Now, 0),
unknownTLSConnetionNegotiationValid(frozenMetav1Now, 0),
unknownTLSConnectionNegotiationValid(frozenMetav1Now, 0),
unknownAuthenticatorValid(frozenMetav1Now, 0),
sadReadyCondition(frozenMetav1Now, 0),
},
@@ -677,9 +677,9 @@ func TestController(t *testing.T) {
Conditions: conditionstestutil.Replace(
allHappyConditionsSuccess(goodEndpoint, frozenMetav1Now, 0),
[]metav1.Condition{
happyTLSBundleValidNoCA(frozenMetav1Now, 0),
happyTLSConfigurationValidNoCA(frozenMetav1Now, 0),
sadEndpointURLValidHTTPS("http://localhost", frozenMetav1Now, 0),
unknownTLSConnetionNegotiationValid(frozenMetav1Now, 0),
unknownTLSConnectionNegotiationValid(frozenMetav1Now, 0),
unknownAuthenticatorValid(frozenMetav1Now, 0),
sadReadyCondition(frozenMetav1Now, 0),
},
@@ -720,7 +720,7 @@ func TestController(t *testing.T) {
[]metav1.Condition{
unknownAuthenticatorValid(frozenMetav1Now, 0),
sadReadyCondition(frozenMetav1Now, 0),
sadTLSConnetionNegotiationValid(frozenMetav1Now, 0),
sadTLSConnectionNegotiationValid(frozenMetav1Now, 0),
},
),
Phase: "Error",
@@ -878,7 +878,7 @@ func TestController(t *testing.T) {
Conditions: conditionstestutil.Replace(
allHappyConditionsSuccess(localWithExampleDotComCertServer.URL, frozenMetav1Now, 0),
[]metav1.Condition{
sadTLSConnetionNegotiationNoIPSANs(frozenMetav1Now, 0),
sadTLSConnectionNegotiationNoIPSANs(frozenMetav1Now, 0),
unknownAuthenticatorValid(frozenMetav1Now, 0),
sadReadyCondition(frozenMetav1Now, 0),
},
28 changes: 28 additions & 0 deletions internal/testutil/conciergetestutil/tlstestutil.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package conciergetestutil

import (
"crypto/tls"
"encoding/base64"
"encoding/pem"

auth1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
)

func TlsSpecFromTLSConfig(tls *tls.Config) *auth1alpha1.TLSSpec {
pemData := make([]byte, 0)
for _, certificate := range tls.Certificates {
// this is the public part of the certificate, the private is the certificate.PrivateKey
for _, reallyCertificate := range certificate.Certificate {
pemData = append(pemData, pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: reallyCertificate,
})...)
}
}
return &auth1alpha1.TLSSpec{
CertificateAuthorityData: base64.StdEncoding.EncodeToString(pemData),
}
}
5 changes: 3 additions & 2 deletions test/integration/cli_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package integration

@@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer"
clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"

"go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
identityv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/identity/v1alpha1"
conciergescheme "go.pinniped.dev/internal/concierge/scheme"
"go.pinniped.dev/pkg/oidcclient"
@@ -42,7 +43,7 @@ func TestCLIGetKubeconfigStaticToken_Parallel(t *testing.T) {
ctx, cancelFunc := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancelFunc()

authenticator := testlib.CreateTestWebhookAuthenticator(ctx, t)
authenticator := testlib.CreateTestWebhookAuthenticator(ctx, t, nil, v1alpha1.WebhookAuthenticatorPhaseReady)

// Build pinniped CLI.
pinnipedExe := testlib.PinnipedCLIPath(t)
5 changes: 3 additions & 2 deletions test/integration/concierge_api_serving_certs_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package integration
@@ -12,6 +12,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"

"go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
loginv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/login/v1alpha1"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/test/testlib"
@@ -83,7 +84,7 @@ func TestAPIServingCertificateAutoCreationAndRotation_Disruptive(t *testing.T) {

// Create a testWebhook so we have a legitimate authenticator to pass to the
// TokenCredentialRequest API.
testWebhook := testlib.CreateTestWebhookAuthenticator(ctx, t)
testWebhook := testlib.CreateTestWebhookAuthenticator(ctx, t, nil, v1alpha1.WebhookAuthenticatorPhaseReady)

// Get the initial auto-generated version of the Secret.
secret, err := kubeClient.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, defaultServingCertResourceName, metav1.GetOptions{})
5 changes: 3 additions & 2 deletions test/integration/concierge_client_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package integration
@@ -11,6 +11,7 @@ import (

"github.com/stretchr/testify/require"

"go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
"go.pinniped.dev/internal/here"
"go.pinniped.dev/pkg/conciergeclient"
"go.pinniped.dev/test/testlib"
@@ -58,7 +59,7 @@ func TestClient(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

webhook := testlib.CreateTestWebhookAuthenticator(ctx, t)
webhook := testlib.CreateTestWebhookAuthenticator(ctx, t, nil, v1alpha1.WebhookAuthenticatorPhaseReady)

// Use an invalid certificate/key to validate that the ServerVersion API fails like we assume.
invalidClient := testlib.NewClientsetWithCertAndKey(t, testCert, testKey)
15 changes: 11 additions & 4 deletions test/integration/concierge_credentialrequest_test.go
Original file line number Diff line number Diff line change
@@ -60,8 +60,15 @@ func TestSuccessfulCredentialRequest_Browser(t *testing.T) {
token func(t *testing.T) (token string, username string, groups []string)
}{
{
name: "webhook",
authenticator: testlib.CreateTestWebhookAuthenticator,
name: "webhook",
authenticator: func(ctx context.Context, t *testing.T) corev1.TypedLocalObjectReference {
authenticator := testlib.CreateTestWebhookAuthenticator(ctx, t, nil, auth1alpha1.WebhookAuthenticatorPhaseReady)
return corev1.TypedLocalObjectReference{
APIGroup: &auth1alpha1.SchemeGroupVersion.Group,
Kind: "WebhookAuthenticator",
Name: authenticator.Name,
}
},
token: func(t *testing.T) (string, string, []string) {
return testlib.IntegrationEnv(t).TestUser.Token, env.TestUser.ExpectedUsername, env.TestUser.ExpectedGroups
},
@@ -148,7 +155,7 @@ func TestFailedCredentialRequestWhenTheRequestIsValidButTheTokenDoesNotAuthentic
// TokenCredentialRequest API.
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
testWebhook := testlib.CreateTestWebhookAuthenticator(ctx, t)
testWebhook := testlib.CreateTestWebhookAuthenticator(ctx, t, nil, auth1alpha1.WebhookAuthenticatorPhaseReady)

response, err := testlib.CreateTokenCredentialRequest(context.Background(), t,
loginv1alpha1.TokenCredentialRequestSpec{Token: "not a good token", Authenticator: testWebhook},
@@ -169,7 +176,7 @@ func TestCredentialRequest_ShouldFailWhenRequestDoesNotIncludeToken_Parallel(t *
// TokenCredentialRequest API.
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
testWebhook := testlib.CreateTestWebhookAuthenticator(ctx, t)
testWebhook := testlib.CreateTestWebhookAuthenticator(ctx, t, nil, auth1alpha1.WebhookAuthenticatorPhaseReady)

response, err := testlib.CreateTokenCredentialRequest(context.Background(), t,
loginv1alpha1.TokenCredentialRequestSpec{Token: "", Authenticator: testWebhook},
3 changes: 2 additions & 1 deletion test/integration/concierge_impersonation_proxy_test.go
Original file line number Diff line number Diff line change
@@ -61,6 +61,7 @@ import (
"k8s.io/client-go/util/retry"
"k8s.io/utils/ptr"

"go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
conciergev1alpha "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
identityv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/identity/v1alpha1"
loginv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/login/v1alpha1"
@@ -120,7 +121,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl
// Create a WebhookAuthenticator and prepare a TokenCredentialRequestSpec using the authenticator for use later.
credentialRequestSpecWithWorkingCredentials := loginv1alpha1.TokenCredentialRequestSpec{
Token: env.TestUser.Token,
Authenticator: testlib.CreateTestWebhookAuthenticator(ctx, t),
Authenticator: testlib.CreateTestWebhookAuthenticator(ctx, t, nil, v1alpha1.WebhookAuthenticatorPhaseReady),
}

// The address of the ClusterIP service that points at the impersonation proxy's port (used when there is no load balancer).
Loading

0 comments on commit 1e6e159

Please sign in to comment.