mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-07-22 07:52:24 +00:00
Merge pull request #1924 from vmware-tanzu/jtc/merge-main-5fe94c4e-into-github
Merge main (at 5fe94c4e) into `github_identity_provider`
This commit is contained in:
+3
-3
@@ -1,10 +1,10 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
|
||||
# Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
ARG BUILD_IMAGE=golang:1.22.1@sha256:0b55ab82ac2a54a6f8f85ec8b943b9e470c39e32c109b766bbc1b801f3fa8d3b
|
||||
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:55c636171053dbc8ae07a280023bd787d2921f10e569f3e319f1539076dbba11
|
||||
ARG BUILD_IMAGE=golang:1.22.2@sha256:450e3822c7a135e1463cd83e51c8e2eb03b86a02113c89424e6f0f8344bb4168
|
||||
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:f41b84cda410b05cc690c2e33d1973a31c6165a2721e2b5343aab50fecb63441
|
||||
|
||||
# Prepare to cross-compile by always running the build stage in the build platform, not the target platform.
|
||||
FROM --platform=$BUILDPLATFORM $BUILD_IMAGE as build-env
|
||||
|
||||
@@ -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 cmd
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
kubetesting "k8s.io/client-go/testing"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
conciergev1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
|
||||
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
@@ -27,6 +28,7 @@ import (
|
||||
"go.pinniped.dev/internal/here"
|
||||
"go.pinniped.dev/internal/testutil"
|
||||
"go.pinniped.dev/internal/testutil/testlogger"
|
||||
"go.pinniped.dev/internal/testutil/tlsserver"
|
||||
)
|
||||
|
||||
func TestGetKubeconfig(t *testing.T) {
|
||||
@@ -3198,7 +3200,7 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var issuerEndpointPtr *string
|
||||
issuerCABundle, issuerEndpoint := testutil.TLSTestServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
testServer, testServerCA := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
switch r.URL.Path {
|
||||
case "/.well-known/openid-configuration":
|
||||
@@ -3226,8 +3228,8 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
default:
|
||||
t.Fatalf("tried to call issuer at a path that wasn't one of the expected discovery endpoints.")
|
||||
}
|
||||
})
|
||||
issuerEndpointPtr = &issuerEndpoint
|
||||
}), nil)
|
||||
issuerEndpointPtr = ptr.To(testServer.URL)
|
||||
|
||||
testLog := testlogger.NewLegacy(t) //nolint:staticcheck // old test with lots of log statements
|
||||
cmd := kubeconfigCommand(kubeconfigDeps{
|
||||
@@ -3248,7 +3250,7 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
}
|
||||
fake := fakeconciergeclientset.NewSimpleClientset()
|
||||
if tt.conciergeObjects != nil {
|
||||
fake = fakeconciergeclientset.NewSimpleClientset(tt.conciergeObjects(issuerCABundle, issuerEndpoint)...)
|
||||
fake = fakeconciergeclientset.NewSimpleClientset(tt.conciergeObjects(string(testServerCA), testServer.URL)...)
|
||||
}
|
||||
if len(tt.conciergeReactions) > 0 {
|
||||
fake.ReactionChain = append(tt.conciergeReactions, fake.ReactionChain...)
|
||||
@@ -3263,7 +3265,7 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
cmd.SetOut(&stdout)
|
||||
cmd.SetErr(&stderr)
|
||||
|
||||
cmd.SetArgs(tt.args(issuerCABundle, issuerEndpoint))
|
||||
cmd.SetArgs(tt.args(string(testServerCA), testServer.URL))
|
||||
|
||||
err := cmd.Execute()
|
||||
if tt.wantError {
|
||||
@@ -3274,19 +3276,19 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
|
||||
var expectedLogs []string
|
||||
if tt.wantLogs != nil {
|
||||
expectedLogs = tt.wantLogs(issuerCABundle, issuerEndpoint)
|
||||
expectedLogs = tt.wantLogs(string(testServerCA), testServer.URL)
|
||||
}
|
||||
testLog.Expect(expectedLogs)
|
||||
|
||||
expectedStdout := ""
|
||||
if tt.wantStdout != nil {
|
||||
expectedStdout = tt.wantStdout(issuerCABundle, issuerEndpoint)
|
||||
expectedStdout = tt.wantStdout(string(testServerCA), testServer.URL)
|
||||
}
|
||||
require.Equal(t, expectedStdout, stdout.String(), "unexpected stdout")
|
||||
|
||||
actualStderr := stderr.String()
|
||||
if tt.wantStderr != nil {
|
||||
testutil.RequireErrorString(t, actualStderr, tt.wantStderr(issuerCABundle, issuerEndpoint))
|
||||
testutil.RequireErrorString(t, actualStderr, tt.wantStderr(string(testServerCA), testServer.URL))
|
||||
} else {
|
||||
require.Empty(t, actualStderr, "unexpected stderr")
|
||||
}
|
||||
|
||||
@@ -29,17 +29,21 @@ replace go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttpt
|
||||
// This is an indirect dep which has CVE-2024-24786, so replace it with a fixed version
|
||||
replace google.golang.org/protobuf => google.golang.org/protobuf v1.33.0
|
||||
|
||||
// https://github.com/coreos/go-oidc/releases/tag/v3.10.0 starts to use https://github.com/go-jose/go-jose/releases/tag/v4.0.0.
|
||||
// Unfortunately this has breaking changes.
|
||||
replace github.com/coreos/go-oidc/v3 => github.com/coreos/go-oidc/v3 v3.9.0
|
||||
|
||||
require (
|
||||
github.com/MakeNowJust/heredoc/v2 v2.0.1
|
||||
github.com/chromedp/cdproto v0.0.0-20240312231614-1e5096e63154
|
||||
github.com/chromedp/cdproto v0.0.0-20240328024531-fe04f09ede24
|
||||
github.com/chromedp/chromedp v0.9.5
|
||||
github.com/coreos/go-oidc/v3 v3.9.0
|
||||
github.com/coreos/go-oidc/v3 v3.10.0
|
||||
github.com/coreos/go-semver v0.3.1
|
||||
github.com/creack/pty v1.1.21
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/felixge/httpsnoop v1.0.4
|
||||
github.com/go-jose/go-jose/v3 v3.0.3
|
||||
github.com/go-ldap/ldap/v3 v3.4.6
|
||||
github.com/go-ldap/ldap/v3 v3.4.7
|
||||
github.com/go-logr/logr v1.4.1
|
||||
github.com/go-logr/stdr v1.2.2
|
||||
github.com/go-logr/zapr v1.3.0
|
||||
@@ -52,7 +56,7 @@ require (
|
||||
github.com/gorilla/websocket v1.5.1
|
||||
github.com/joshlf/go-acl v0.0.0-20200411065538-eae00ae38531
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
||||
github.com/ory/fosite v0.46.1
|
||||
github.com/ory/fosite v0.46.2-0.20240403135905-5e039ca9eef1
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sclevine/spec v1.4.0
|
||||
@@ -62,11 +66,11 @@ require (
|
||||
github.com/tdewolff/minify/v2 v2.20.19
|
||||
go.uber.org/mock v0.4.0
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/crypto v0.21.0
|
||||
golang.org/x/net v0.22.0
|
||||
golang.org/x/oauth2 v0.18.0
|
||||
golang.org/x/sync v0.6.0
|
||||
golang.org/x/term v0.18.0
|
||||
golang.org/x/crypto v0.22.0
|
||||
golang.org/x/net v0.24.0
|
||||
golang.org/x/oauth2 v0.19.0
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/term v0.19.0
|
||||
golang.org/x/text v0.14.0
|
||||
k8s.io/api v0.29.3
|
||||
k8s.io/apiextensions-apiserver v0.29.3
|
||||
@@ -74,10 +78,10 @@ require (
|
||||
k8s.io/apiserver v0.29.3
|
||||
k8s.io/client-go v0.29.3
|
||||
k8s.io/component-base v0.29.3
|
||||
k8s.io/gengo v0.0.0-20240310015720-9cff6334dab4
|
||||
k8s.io/gengo v0.0.0-20240404160639-a0386bf69313
|
||||
k8s.io/klog/v2 v2.120.1
|
||||
k8s.io/kube-aggregator v0.29.3
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340
|
||||
k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3
|
||||
k8s.io/utils v0.0.0-20240310230437-4693a0247e57
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
@@ -182,10 +186,9 @@ require (
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect
|
||||
golang.org/x/mod v0.15.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.18.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
|
||||
@@ -50,8 +50,8 @@ github.com/MakeNowJust/heredoc/v2 v2.0.1/go.mod h1:6/2Abh5s+hc3g9nbWLe9ObDIOhaRr
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA=
|
||||
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18=
|
||||
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
@@ -70,8 +70,8 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||
github.com/chromedp/cdproto v0.0.0-20240312231614-1e5096e63154 h1:jeAmkzyOAQBPRmZMhX+i/CJv0VViLkHk1nF0qx8s0Mk=
|
||||
github.com/chromedp/cdproto v0.0.0-20240312231614-1e5096e63154/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||
github.com/chromedp/cdproto v0.0.0-20240328024531-fe04f09ede24 h1:XLG3KlHtG6Wg75ed/daLltJtcj8VXjw7F9mzYenzFL0=
|
||||
github.com/chromedp/cdproto v0.0.0-20240328024531-fe04f09ede24/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||
github.com/chromedp/chromedp v0.9.5 h1:viASzruPJOiThk7c5bueOUY91jGLJVximoEMGoH93rg=
|
||||
github.com/chromedp/chromedp v0.9.5/go.mod h1:D4I2qONslauw/C7INoCir1BJkSwBYMyZgx8X276z3+Y=
|
||||
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
|
||||
@@ -152,8 +152,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A=
|
||||
github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc=
|
||||
github.com/go-ldap/ldap/v3 v3.4.7 h1:3Hbd7mIB1qjd3Ra59fI3JYea/t5kykFu2CVHBca9koE=
|
||||
github.com/go-ldap/ldap/v3 v3.4.7/go.mod h1:qS3Sjlu76eHfHGpUdWkAXQTw4beih+cHsco2jXlIXrk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -232,7 +232,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@@ -277,15 +276,16 @@ github.com/google/pprof v0.0.0-20221010195024-131d412537ea h1:R3VfsTXMMK4JCWZDdx
|
||||
github.com/google/pprof v0.0.0-20221010195024-131d412537ea/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
@@ -304,6 +304,9 @@ github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXc
|
||||
github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
@@ -355,6 +358,18 @@ github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv
|
||||
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jandelgado/gcov2lcov v1.0.5 h1:rkBt40h0CVK4oCb8Dps950gvfd1rYvQ8+cWa346lVU0=
|
||||
github.com/jandelgado/gcov2lcov v1.0.5/go.mod h1:NnSxK6TMlg1oGDBfGelGbjgorT5/L3cchlbtgFYZSss=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
|
||||
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
|
||||
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
||||
@@ -455,8 +470,8 @@ github.com/openzipkin/zipkin-go v0.4.1 h1:kNd/ST2yLLWhaWrkgchya40TJabe8Hioj9udfP
|
||||
github.com/openzipkin/zipkin-go v0.4.1/go.mod h1:qY0VqDSN1pOBN94dBc6w2GJlWLiovAyg7Qt6/I9HecM=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
||||
github.com/ory/fosite v0.46.1 h1:VC8h83cbWx7K5r/VToDldSC+317sKFqJjLOPB4Ns4AY=
|
||||
github.com/ory/fosite v0.46.1/go.mod h1:fkMPsnm/UjiefE9dE9CdZQGOH48TWJLIzUcdGIXg8Kk=
|
||||
github.com/ory/fosite v0.46.2-0.20240403135905-5e039ca9eef1 h1:Ev2BRtVe54kwAQ0dEEmdJIZHbJSpQdpOluEdrPae+sM=
|
||||
github.com/ory/fosite v0.46.2-0.20240403135905-5e039ca9eef1/go.mod h1:1L248mlkShpxI2qi2RABiEtf86jFH414HvAERTpgEWM=
|
||||
github.com/ory/go-acc v0.2.9-0.20230103102148-6b1c9a70dbbe h1:rvu4obdvqR0fkSIJ8IfgzKOWwZ5kOT2UNfLq81Qk7rc=
|
||||
github.com/ory/go-acc v0.2.9-0.20230103102148-6b1c9a70dbbe/go.mod h1:z4n3u6as84LbV4YmgjHhnwtccQqzf4cZlSk9f1FhygI=
|
||||
github.com/ory/go-convenience v0.1.0 h1:zouLKfF2GoSGnJwGq+PE/nJAE6dj2Zj5QlTgmMTsTS8=
|
||||
@@ -664,10 +679,11 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -747,10 +763,13 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -760,8 +779,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
|
||||
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
|
||||
golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg=
|
||||
golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -776,8 +795,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -833,11 +852,11 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -845,10 +864,10 @@ golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuX
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -857,10 +876,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -961,8 +978,6 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
@@ -1068,8 +1083,8 @@ k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg=
|
||||
k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0=
|
||||
k8s.io/component-base v0.29.3 h1:Oq9/nddUxlnrCuuR2K/jp6aflVvc0uDvxMzAWxnGzAo=
|
||||
k8s.io/component-base v0.29.3/go.mod h1:Yuj33XXjuOk2BAaHsIGHhCKZQAgYKhqIxIjIr2UXYio=
|
||||
k8s.io/gengo v0.0.0-20240310015720-9cff6334dab4 h1:v8TcVKY4HxDaiNH8xnHEEGfx80twQJL9cOQuhd55fcY=
|
||||
k8s.io/gengo v0.0.0-20240310015720-9cff6334dab4/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
||||
k8s.io/gengo v0.0.0-20240404160639-a0386bf69313 h1:wBIDZID8ju9pwOiLlV22YYKjFGtiNSWgHf5CnKLRUuM=
|
||||
k8s.io/gengo v0.0.0-20240404160639-a0386bf69313/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
||||
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
|
||||
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
@@ -1077,8 +1092,8 @@ k8s.io/kms v0.29.3 h1:ReljsAUhYlm2spdT4yXmY+9a8x8dc/OT4mXvwQPPteQ=
|
||||
k8s.io/kms v0.29.3/go.mod h1:TBGbJKpRUMk59neTMDMddjIDL+D4HuFUbpuiuzmOPg0=
|
||||
k8s.io/kube-aggregator v0.29.3 h1:5KvTyFN8sQq2imq8tMAHWEKoE64Zg9WSMaGX78KV6ps=
|
||||
k8s.io/kube-aggregator v0.29.3/go.mod h1:xGJqV/SJJ1fbwTGfQLAZfwgqX1EMoaqfotDTkDrqqSk=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
|
||||
k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3 h1:SbdLaI6mM6ffDSJCadEaD4IkuPzepLDGlkd2xV0t1uA=
|
||||
k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
|
||||
k8s.io/utils v0.0.0-20240310230437-4693a0247e57 h1:gbqbevonBh57eILzModw6mrkbwM0gQBEuevE/AaBsHY=
|
||||
k8s.io/utils v0.0.0-20240310230437-4693a0247e57/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Copyright 2022-2023 the Pinniped contributors. All Rights Reserved.
|
||||
# Copyright 2022-2024 the Pinniped contributors. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# this dockerfile is used to produce a binary of Pinniped that uses
|
||||
@@ -16,8 +16,8 @@
|
||||
# See https://go.googlesource.com/go/+/dev.boringcrypto/README.boringcrypto.md
|
||||
# and https://kupczynski.info/posts/fips-golang/ for details.
|
||||
|
||||
ARG BUILD_IMAGE=golang:1.22.1@sha256:0b55ab82ac2a54a6f8f85ec8b943b9e470c39e32c109b766bbc1b801f3fa8d3b
|
||||
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:55c636171053dbc8ae07a280023bd787d2921f10e569f3e319f1539076dbba11
|
||||
ARG BUILD_IMAGE=golang:1.22.2@sha256:450e3822c7a135e1463cd83e51c8e2eb03b86a02113c89424e6f0f8344bb4168
|
||||
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:f41b84cda410b05cc690c2e33d1973a31c6165a2721e2b5343aab50fecb63441
|
||||
|
||||
# This is not currently using --platform to prepare to cross-compile because we use gcc below to build
|
||||
# platform-specific GCO code. This makes multi-arch builds slow due to target platform emulation.
|
||||
|
||||
@@ -4,4 +4,4 @@ go 1.21.3
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require golang.org/x/mod v0.16.0
|
||||
require golang.org/x/mod v0.17.0
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
||||
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
|
||||
@@ -692,7 +692,7 @@ func TestImpersonator(t *testing.T) {
|
||||
// will proxy incoming calls to this fake server.
|
||||
testKubeAPIServerWasCalled := false
|
||||
var testKubeAPIServerSawHeaders http.Header
|
||||
testKubeAPIServer := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
testKubeAPIServer, testKubeAPIServerCA := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
tlsConfigFunc := func(rootCAs *x509.CertPool) *tls.Config {
|
||||
// Requests to get configmaps, flowcontrol requests, and healthz requests
|
||||
// are not done by our http round trippers that specify only one protocol
|
||||
@@ -815,7 +815,7 @@ func TestImpersonator(t *testing.T) {
|
||||
// Create the client config that the impersonation server should use to talk to the Kube API server.
|
||||
testKubeAPIServerKubeconfig := rest.Config{
|
||||
Host: testKubeAPIServer.URL,
|
||||
TLSClientConfig: rest.TLSClientConfig{CAData: tlsserver.TLSTestServerCA(testKubeAPIServer)},
|
||||
TLSClientConfig: rest.TLSClientConfig{CAData: testKubeAPIServerCA},
|
||||
}
|
||||
|
||||
// Punch out just enough stuff to make New actually run without error.
|
||||
@@ -1806,7 +1806,7 @@ func TestImpersonatorHTTPHandler(t *testing.T) {
|
||||
|
||||
testKubeAPIServerWasCalled := false
|
||||
testKubeAPIServerSawHeaders := http.Header{}
|
||||
testKubeAPIServer := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
testKubeAPIServer, testKubeAPIServerCA := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
tlsConfigFunc := func(rootCAs *x509.CertPool) *tls.Config {
|
||||
// Requests to get configmaps, flowcontrol requests, and healthz requests
|
||||
// are not done by our http round trippers that specify only one protocol
|
||||
@@ -1842,7 +1842,7 @@ func TestImpersonatorHTTPHandler(t *testing.T) {
|
||||
testKubeAPIServerKubeconfig := rest.Config{
|
||||
Host: testKubeAPIServer.URL,
|
||||
BearerToken: "some-service-account-token",
|
||||
TLSClientConfig: rest.TLSClientConfig{CAData: tlsserver.TLSTestServerCA(testKubeAPIServer)},
|
||||
TLSClientConfig: rest.TLSClientConfig{CAData: testKubeAPIServerCA},
|
||||
}
|
||||
if tt.restConfig == nil {
|
||||
tt.restConfig = &testKubeAPIServerKubeconfig
|
||||
|
||||
@@ -66,7 +66,6 @@ const (
|
||||
reasonInvalidTLSConfiguration = "InvalidTLSConfiguration"
|
||||
reasonInvalidDiscoveryProbe = "InvalidDiscoveryProbe"
|
||||
reasonInvalidAuthenticator = "InvalidAuthenticator"
|
||||
reasonInvalidTokenSigningFailure = "InvalidTokenSigningFailure"
|
||||
reasonInvalidCouldNotFetchJWKS = "InvalidCouldNotFetchJWKS"
|
||||
|
||||
msgUnableToValidate = "unable to validate; see other conditions for details"
|
||||
|
||||
@@ -76,7 +76,7 @@ func TestController(t *testing.T) {
|
||||
distributedGroups := []string{"some-distributed-group-1", "some-distributed-group-2"}
|
||||
|
||||
goodMux := http.NewServeMux()
|
||||
goodOIDCIssuerServer := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
goodOIDCIssuerServer, _ := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
tlsserver.AssertTLS(t, r, ptls.Default)
|
||||
goodMux.ServeHTTP(w, r)
|
||||
}), tlsserver.RecordTLSHello)
|
||||
@@ -174,7 +174,7 @@ func TestController(t *testing.T) {
|
||||
}))
|
||||
|
||||
badMuxInvalidJWKSURI := http.NewServeMux()
|
||||
badOIDCIssuerServerInvalidJWKSURI := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
badOIDCIssuerServerInvalidJWKSURI, _ := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
tlsserver.AssertTLS(t, r, ptls.Default)
|
||||
badMuxInvalidJWKSURI.ServeHTTP(w, r)
|
||||
}), tlsserver.RecordTLSHello)
|
||||
@@ -185,7 +185,7 @@ func TestController(t *testing.T) {
|
||||
}))
|
||||
|
||||
badMuxInvalidJWKSURIScheme := http.NewServeMux()
|
||||
badOIDCIssuerServerInvalidJWKSURIScheme := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
badOIDCIssuerServerInvalidJWKSURIScheme, _ := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
tlsserver.AssertTLS(t, r, ptls.Default)
|
||||
badMuxInvalidJWKSURIScheme.ServeHTTP(w, r)
|
||||
}), tlsserver.RecordTLSHello)
|
||||
@@ -196,7 +196,7 @@ func TestController(t *testing.T) {
|
||||
}))
|
||||
|
||||
jwksFetchShouldFailMux := http.NewServeMux()
|
||||
jwksFetchShouldFailServer := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
jwksFetchShouldFailServer, _ := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
tlsserver.AssertTLS(t, r, ptls.Default)
|
||||
jwksFetchShouldFailMux.ServeHTTP(w, r)
|
||||
}), tlsserver.RecordTLSHello)
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
k8sauthv1beta1 "k8s.io/api/authentication/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
@@ -19,10 +19,8 @@ import (
|
||||
errorsutil "k8s.io/apimachinery/pkg/util/errors"
|
||||
k8snetutil "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
webhookutil "k8s.io/apiserver/pkg/util/webhook"
|
||||
"k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/utils/clock"
|
||||
|
||||
@@ -33,7 +31,9 @@ import (
|
||||
"go.pinniped.dev/internal/controller/authenticator/authncache"
|
||||
"go.pinniped.dev/internal/controller/conditionsutil"
|
||||
"go.pinniped.dev/internal/controllerlib"
|
||||
"go.pinniped.dev/internal/crypto/ptls"
|
||||
"go.pinniped.dev/internal/endpointaddr"
|
||||
"go.pinniped.dev/internal/kubeclient"
|
||||
"go.pinniped.dev/internal/plog"
|
||||
)
|
||||
|
||||
@@ -47,9 +47,7 @@ const (
|
||||
reasonSuccess = "Success"
|
||||
reasonNotReady = "NotReady"
|
||||
reasonUnableToValidate = "UnableToValidate"
|
||||
reasonUnableToCreateTempFile = "UnableToCreateTempFile"
|
||||
reasonUnableToMarshallKubeconfig = "UnableToMarshallKubeconfig"
|
||||
reasonUnableToLoadKubeconfig = "UnableToLoadKubeconfig"
|
||||
reasonUnableToCreateClient = "UnableToCreateClient"
|
||||
reasonUnableToInstantiateWebhook = "UnableToInstantiateWebhook"
|
||||
reasonInvalidTLSConfiguration = "InvalidTLSConfiguration"
|
||||
reasonInvalidEndpointURL = "InvalidEndpointURL"
|
||||
@@ -65,18 +63,16 @@ func New(
|
||||
webhooks authinformers.WebhookAuthenticatorInformer,
|
||||
clock clock.Clock,
|
||||
log plog.Logger,
|
||||
tlsDialerFunc func(network string, addr string, config *tls.Config) (*tls.Conn, error),
|
||||
) controllerlib.Controller {
|
||||
return controllerlib.New(
|
||||
controllerlib.Config{
|
||||
Name: controllerName,
|
||||
Syncer: &webhookCacheFillerController{
|
||||
cache: cache,
|
||||
client: client,
|
||||
webhooks: webhooks,
|
||||
clock: clock,
|
||||
log: log.WithName(controllerName),
|
||||
tlsDialerFunc: tlsDialerFunc,
|
||||
cache: cache,
|
||||
client: client,
|
||||
webhooks: webhooks,
|
||||
clock: clock,
|
||||
log: log.WithName(controllerName),
|
||||
},
|
||||
},
|
||||
controllerlib.WithInformer(
|
||||
@@ -88,12 +84,11 @@ func New(
|
||||
}
|
||||
|
||||
type webhookCacheFillerController struct {
|
||||
cache *authncache.Cache
|
||||
webhooks authinformers.WebhookAuthenticatorInformer
|
||||
client conciergeclientset.Interface
|
||||
clock clock.Clock
|
||||
log plog.Logger
|
||||
tlsDialerFunc func(network string, addr string, config *tls.Config) (*tls.Conn, error)
|
||||
cache *authncache.Cache
|
||||
webhooks authinformers.WebhookAuthenticatorInformer
|
||||
client conciergeclientset.Interface
|
||||
clock clock.Clock
|
||||
log plog.Logger
|
||||
}
|
||||
|
||||
// Sync implements controllerlib.Syncer.
|
||||
@@ -104,25 +99,25 @@ func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
// no unit test for this failure
|
||||
return fmt.Errorf("failed to get WebhookAuthenticator %s/%s: %w", ctx.Key.Namespace, ctx.Key.Name, err)
|
||||
}
|
||||
|
||||
conditions := make([]*metav1.Condition, 0)
|
||||
specCopy := obj.Spec.DeepCopy()
|
||||
var errs []error
|
||||
|
||||
certPool, pemBytes, conditions, tlsBundleOk := c.validateTLSBundle(specCopy.TLS, conditions)
|
||||
endpointHostPort, conditions, endpointOk := c.validateEndpoint(specCopy.Endpoint, conditions)
|
||||
certPool, pemBytes, conditions, tlsBundleOk := c.validateTLSBundle(obj.Spec.TLS, conditions)
|
||||
endpointHostPort, conditions, endpointOk := c.validateEndpoint(obj.Spec.Endpoint, conditions)
|
||||
okSoFar := tlsBundleOk && endpointOk
|
||||
conditions, tlsNegotiateErr := c.validateConnection(certPool, endpointHostPort, conditions, okSoFar)
|
||||
errs = append(errs, tlsNegotiateErr)
|
||||
okSoFar = okSoFar && tlsNegotiateErr == nil
|
||||
|
||||
webhookAuthenticator, conditions, err := newWebhookAuthenticator(
|
||||
specCopy.Endpoint,
|
||||
// Note that we use the whole URL when constructing the webhook client,
|
||||
// not just the host and port that ew validated above. We need the path, etc.
|
||||
obj.Spec.Endpoint,
|
||||
pemBytes,
|
||||
os.CreateTemp,
|
||||
clientcmd.WriteToFile,
|
||||
conditions,
|
||||
okSoFar,
|
||||
)
|
||||
@@ -151,10 +146,8 @@ func (c *webhookCacheFillerController) Sync(ctx controllerlib.Context) error {
|
||||
// newWebhookAuthenticator creates a webhook from the provided API server url and caBundle
|
||||
// used to validate TLS connections.
|
||||
func newWebhookAuthenticator(
|
||||
endpoint string,
|
||||
endpointURL string,
|
||||
pemBytes []byte,
|
||||
tempfileFunc func(string, string) (*os.File, error),
|
||||
marshalFunc func(clientcmdapi.Config, string) error,
|
||||
conditions []*metav1.Condition,
|
||||
prereqOk bool,
|
||||
) (*webhook.WebhookTokenAuthenticator, []*metav1.Condition, error) {
|
||||
@@ -167,39 +160,6 @@ func newWebhookAuthenticator(
|
||||
})
|
||||
return nil, conditions, nil
|
||||
}
|
||||
temp, err := tempfileFunc("", "pinniped-webhook-kubeconfig-*")
|
||||
if err != nil {
|
||||
errText := "unable to create temporary file"
|
||||
msg := fmt.Sprintf("%s: %s", errText, err.Error())
|
||||
conditions = append(conditions, &metav1.Condition{
|
||||
Type: typeAuthenticatorValid,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: reasonUnableToCreateTempFile,
|
||||
Message: msg,
|
||||
})
|
||||
return nil, conditions, fmt.Errorf("%s: %w", errText, err)
|
||||
}
|
||||
defer func() { _ = os.Remove(temp.Name()) }()
|
||||
|
||||
cluster := &clientcmdapi.Cluster{Server: endpoint}
|
||||
cluster.CertificateAuthorityData = pemBytes
|
||||
|
||||
kubeconfig := clientcmdapi.NewConfig()
|
||||
kubeconfig.Clusters["anonymous-cluster"] = cluster
|
||||
kubeconfig.Contexts["anonymous"] = &clientcmdapi.Context{Cluster: "anonymous-cluster"}
|
||||
kubeconfig.CurrentContext = "anonymous"
|
||||
|
||||
if err := marshalFunc(*kubeconfig, temp.Name()); err != nil {
|
||||
errText := "unable to marshal kubeconfig"
|
||||
msg := fmt.Sprintf("%s: %s", errText, err.Error())
|
||||
conditions = append(conditions, &metav1.Condition{
|
||||
Type: typeAuthenticatorValid,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: reasonUnableToMarshallKubeconfig,
|
||||
Message: msg,
|
||||
})
|
||||
return nil, conditions, fmt.Errorf("%s: %w", errText, err)
|
||||
}
|
||||
|
||||
// We use v1beta1 instead of v1 since v1beta1 is more prevalent in our desired
|
||||
// integration points.
|
||||
@@ -214,40 +174,30 @@ func newWebhookAuthenticator(
|
||||
// custom proxy stuff used by the API server.
|
||||
var customDial k8snetutil.DialFunc
|
||||
|
||||
// TODO refactor this code to directly construct the rest.Config
|
||||
// ideally we would keep rest config generation contained to the kubeclient package
|
||||
// but this will require some form of a new WithTLSConfigFunc kubeclient.Option
|
||||
// ex:
|
||||
// _, caBundle, err := pinnipedauthenticator.CABundle(spec.TLS)
|
||||
// ...
|
||||
// restConfig := &rest.Config{
|
||||
// Host: spec.Endpoint,
|
||||
// TLSClientConfig: rest.TLSClientConfig{CAData: caBundle},
|
||||
// // copied from k8s.io/apiserver/pkg/util/webhook
|
||||
// Timeout: 30 * time.Second,
|
||||
// QPS: -1,
|
||||
// }
|
||||
// client, err := kubeclient.New(kubeclient.WithConfig(restConfig), kubeclient.WithTLSConfigFunc(ptls.Default))
|
||||
// ...
|
||||
// then use client.JSONConfig as clientConfig
|
||||
clientConfig, err := webhookutil.LoadKubeconfig(temp.Name(), customDial)
|
||||
restConfig := &rest.Config{
|
||||
Host: endpointURL,
|
||||
TLSClientConfig: rest.TLSClientConfig{CAData: pemBytes},
|
||||
|
||||
// The remainder of these settings are copied from webhookutil.LoadKubeconfig in k8s.io/apiserver/pkg/util/webhook.
|
||||
Dial: customDial,
|
||||
Timeout: 30 * time.Second,
|
||||
QPS: -1,
|
||||
}
|
||||
|
||||
client, err := kubeclient.New(kubeclient.WithConfig(restConfig), kubeclient.WithTLSConfigFunc(ptls.Default))
|
||||
if err != nil {
|
||||
// no unit test for this failure.
|
||||
errText := "unable to load kubeconfig"
|
||||
errText := "unable to create client for this webhook"
|
||||
msg := fmt.Sprintf("%s: %s", errText, err.Error())
|
||||
conditions = append(conditions, &metav1.Condition{
|
||||
Type: typeAuthenticatorValid,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: reasonUnableToLoadKubeconfig,
|
||||
Reason: reasonUnableToCreateClient,
|
||||
Message: msg,
|
||||
})
|
||||
return nil, conditions, fmt.Errorf("%s: %w", errText, err)
|
||||
}
|
||||
|
||||
// this uses a http client that does not honor our TLS config
|
||||
// TODO: fix when we pick up https://github.com/kubernetes/kubernetes/pull/106155
|
||||
// NOTE: looks like the above was merged on Mar 18, 2022
|
||||
webhookA, err := webhook.New(clientConfig, version, implicitAuds, *webhook.DefaultRetryBackoff())
|
||||
webhookAuthenticator, err := webhook.New(client.JSONConfig, version, implicitAuds, *webhook.DefaultRetryBackoff())
|
||||
if err != nil {
|
||||
// no unit test for this failure.
|
||||
errText := "unable to instantiate webhook"
|
||||
@@ -260,6 +210,7 @@ func newWebhookAuthenticator(
|
||||
})
|
||||
return nil, conditions, fmt.Errorf("%s: %w", errText, err)
|
||||
}
|
||||
|
||||
msg := "authenticator initialized"
|
||||
conditions = append(conditions, &metav1.Condition{
|
||||
Type: typeAuthenticatorValid,
|
||||
@@ -267,7 +218,8 @@ func newWebhookAuthenticator(
|
||||
Reason: reasonSuccess,
|
||||
Message: msg,
|
||||
})
|
||||
return webhookA, conditions, nil
|
||||
|
||||
return webhookAuthenticator, conditions, nil
|
||||
}
|
||||
|
||||
func (c *webhookCacheFillerController) validateConnection(certPool *x509.CertPool, endpointHostPort *endpointaddr.HostPort, conditions []*metav1.Condition, prereqOk bool) ([]*metav1.Condition, error) {
|
||||
@@ -281,11 +233,7 @@ func (c *webhookCacheFillerController) validateConnection(certPool *x509.CertPoo
|
||||
return conditions, nil
|
||||
}
|
||||
|
||||
conn, err := c.tlsDialerFunc("tcp", endpointHostPort.Endpoint(), &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
// If certPool is nil then RootCAs will be set to nil and TLS will use the host's root CA set automatically.
|
||||
RootCAs: certPool,
|
||||
})
|
||||
conn, err := tls.Dial("tcp", endpointHostPort.Endpoint(), ptls.Default(certPool))
|
||||
|
||||
if err != nil {
|
||||
errText := "cannot dial server"
|
||||
@@ -302,6 +250,7 @@ func (c *webhookCacheFillerController) validateConnection(certPool *x509.CertPoo
|
||||
// this error should never be significant
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
// no unit test for this failure
|
||||
c.log.Error("error closing dialer", err)
|
||||
}
|
||||
|
||||
@@ -309,7 +258,7 @@ func (c *webhookCacheFillerController) validateConnection(certPool *x509.CertPoo
|
||||
Type: typeWebhookConnectionValid,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: reasonSuccess,
|
||||
Message: "tls verified",
|
||||
Message: "successfully dialed webhook server",
|
||||
})
|
||||
return conditions, nil
|
||||
}
|
||||
@@ -380,7 +329,7 @@ func (c *webhookCacheFillerController) validateEndpoint(endpoint string, conditi
|
||||
Type: typeEndpointURLValid,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: reasonSuccess,
|
||||
Message: "endpoint is a valid URL",
|
||||
Message: "spec.endpoint is a valid URL",
|
||||
})
|
||||
return &endpointHostPort, conditions, true
|
||||
}
|
||||
|
||||
@@ -16,20 +16,17 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
coretesting "k8s.io/client-go/testing"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
clocktesting "k8s.io/utils/clock/testing"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
auth1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
|
||||
pinnipedfake "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake"
|
||||
@@ -91,56 +88,40 @@ func TestController(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
hostAsLocalhostMux := http.NewServeMux()
|
||||
hostAsLocalhostWebhookServer := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
tlsserver.AssertTLS(t, r, ptls.Default)
|
||||
hostAsLocalhostMux.ServeHTTP(w, r)
|
||||
}), func(thisServer *httptest.Server) {
|
||||
thisTLSConfig := ptls.Default(nil)
|
||||
thisTLSConfig.Certificates = []tls.Certificate{
|
||||
*hostAsLocalhostServingCert,
|
||||
}
|
||||
thisServer.TLS = thisTLSConfig
|
||||
hostAsLocalhostWebhookServer, _ := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// only expecting dials, which will not get into handler func
|
||||
}), func(s *httptest.Server) {
|
||||
s.TLS.Certificates = []tls.Certificate{*hostAsLocalhostServingCert}
|
||||
tlsserver.AssertEveryTLSHello(t, s, ptls.Default) // assert on every hello because we are only expecting dials
|
||||
})
|
||||
|
||||
hostAs127001Mux := http.NewServeMux()
|
||||
hostAs127001WebhookServer := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
tlsserver.AssertTLS(t, r, ptls.Default)
|
||||
hostAs127001Mux.ServeHTTP(w, r)
|
||||
}), func(thisServer *httptest.Server) {
|
||||
thisTLSConfig := ptls.Default(nil)
|
||||
thisTLSConfig.Certificates = []tls.Certificate{
|
||||
*hostAs127001ServingCert,
|
||||
}
|
||||
thisServer.TLS = thisTLSConfig
|
||||
hostAs127001WebhookServer, _ := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// only expecting dials, which will not get into handler func
|
||||
}), func(s *httptest.Server) {
|
||||
s.TLS.Certificates = []tls.Certificate{*hostAs127001ServingCert}
|
||||
tlsserver.AssertEveryTLSHello(t, s, ptls.Default) // assert on every hello because we are only expecting dials
|
||||
})
|
||||
|
||||
localWithExampleDotComMux := http.NewServeMux()
|
||||
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) {
|
||||
thisTLSConfig := ptls.Default(nil)
|
||||
thisTLSConfig.Certificates = []tls.Certificate{
|
||||
*localButExampleDotComServerCert,
|
||||
}
|
||||
thisServer.TLS = thisTLSConfig
|
||||
hostLocalWithExampleDotComCertServer, _ := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// only expecting dials, which will not get into handler func
|
||||
}), func(s *httptest.Server) {
|
||||
s.TLS.Certificates = []tls.Certificate{*localButExampleDotComServerCert}
|
||||
tlsserver.AssertEveryTLSHello(t, s, ptls.Default) // assert on every hello because we are only expecting dials
|
||||
})
|
||||
|
||||
goodMux := http.NewServeMux()
|
||||
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)
|
||||
goodMux.Handle("/some/webhook", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, err := fmt.Fprintf(w, `{"something": "%s"}`, "something-for-response")
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
goodMux.Handle("/nothing/here", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
hostLocalIPv6Server, ipv6CA := tlsserver.TestServerIPv6(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}), tlsserver.RecordTLSHello)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/nothing/here", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// note that we are only dialing, so we shouldn't actually get here
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
fmt.Fprint(w, "404 nothing here")
|
||||
_, _ = fmt.Fprint(w, "404 nothing here")
|
||||
}))
|
||||
hostGoodDefaultServingCertServer, _ := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mux.ServeHTTP(w, r)
|
||||
}), func(s *httptest.Server) {
|
||||
tlsserver.AssertEveryTLSHello(t, s, ptls.Default) // assert on every hello because we are only expecting dials
|
||||
})
|
||||
|
||||
goodWebhookDefaultServingCertEndpoint := hostGoodDefaultServingCertServer.URL
|
||||
goodWebhookDefaultServingCertEndpointBut404 := goodWebhookDefaultServingCertEndpoint + "/nothing/here"
|
||||
@@ -270,7 +251,7 @@ func TestController(t *testing.T) {
|
||||
ObservedGeneration: observedGeneration,
|
||||
LastTransitionTime: time,
|
||||
Reason: "Success",
|
||||
Message: "tls verified",
|
||||
Message: "successfully dialed webhook server",
|
||||
}
|
||||
}
|
||||
unknownWebhookConnectionValid := func(time metav1.Time, observedGeneration int64) metav1.Condition {
|
||||
@@ -321,7 +302,7 @@ func TestController(t *testing.T) {
|
||||
ObservedGeneration: observedGeneration,
|
||||
LastTransitionTime: time,
|
||||
Reason: "Success",
|
||||
Message: "endpoint is a valid URL",
|
||||
Message: "spec.endpoint is a valid URL",
|
||||
}
|
||||
}
|
||||
sadEndpointURLValid := func(issuer string, time metav1.Time, observedGeneration int64) metav1.Condition {
|
||||
@@ -383,14 +364,13 @@ func TestController(t *testing.T) {
|
||||
webhooks []runtime.Object
|
||||
// for modifying the clients to hack in arbitrary api responses
|
||||
configClient func(*pinnipedfake.Clientset)
|
||||
tlsDialerFunc func(network string, addr string, config *tls.Config) (*tls.Conn, error)
|
||||
wantSyncLoopErr testutil.RequireErrorStringFunc
|
||||
wantLogs []map[string]any
|
||||
wantActions func() []coretesting.Action
|
||||
wantCacheEntries int
|
||||
}{
|
||||
{
|
||||
name: "404: WebhookAuthenticator not found will abort sync loop, no status conditions",
|
||||
name: "Sync: WebhookAuthenticator not found will abort sync loop, no status conditions",
|
||||
syncKey: controllerlib.Key{Name: "test-name"},
|
||||
wantLogs: []map[string]any{
|
||||
{
|
||||
@@ -546,6 +526,63 @@ func TestController(t *testing.T) {
|
||||
},
|
||||
wantCacheEntries: 1,
|
||||
},
|
||||
{
|
||||
name: "Sync: valid WebhookAuthenticator with IPV6 and CA: will complete sync loop successfully with success conditions and ready phase",
|
||||
syncKey: controllerlib.Key{Name: "test-name"},
|
||||
webhooks: []runtime.Object{
|
||||
&auth1alpha1.WebhookAuthenticator{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-name",
|
||||
},
|
||||
Spec: func() auth1alpha1.WebhookAuthenticatorSpec {
|
||||
ipv6 := goodWebhookAuthenticatorSpecWithCA.DeepCopy()
|
||||
ipv6.Endpoint = hostLocalIPv6Server.URL
|
||||
ipv6.TLS = ptr.To(auth1alpha1.TLSSpec{
|
||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString(ipv6CA),
|
||||
})
|
||||
return *ipv6
|
||||
}(),
|
||||
},
|
||||
},
|
||||
wantLogs: []map[string]any{
|
||||
{
|
||||
"level": "info",
|
||||
"timestamp": "2099-08-08T13:57:36.123456Z",
|
||||
"logger": "webhookcachefiller-controller",
|
||||
"message": "added new webhook authenticator",
|
||||
"endpoint": hostLocalIPv6Server.URL,
|
||||
"webhook": map[string]interface{}{
|
||||
"name": "test-name",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantActions: func() []coretesting.Action {
|
||||
updateStatusAction := coretesting.NewUpdateAction(webhookAuthenticatorGVR, "", &auth1alpha1.WebhookAuthenticator{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-name",
|
||||
},
|
||||
Spec: func() auth1alpha1.WebhookAuthenticatorSpec {
|
||||
ipv6 := goodWebhookAuthenticatorSpecWithCA.DeepCopy()
|
||||
ipv6.Endpoint = hostLocalIPv6Server.URL
|
||||
ipv6.TLS = ptr.To(auth1alpha1.TLSSpec{
|
||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString(ipv6CA),
|
||||
})
|
||||
return *ipv6
|
||||
}(),
|
||||
Status: auth1alpha1.WebhookAuthenticatorStatus{
|
||||
Conditions: allHappyConditionsSuccess(hostLocalIPv6Server.URL, 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: "Sync: valid WebhookAuthenticator without CA: loop will fail to cache the authenticator, will write failed and unknown status conditions, and will enqueue resync",
|
||||
syncKey: controllerlib.Key{Name: "test-name"},
|
||||
@@ -716,9 +753,6 @@ func TestController(t *testing.T) {
|
||||
{
|
||||
name: "validateEndpoint: should error if endpoint cannot be parsed",
|
||||
syncKey: controllerlib.Key{Name: "test-name"},
|
||||
tlsDialerFunc: func(network string, addr string, config *tls.Config) (*tls.Conn, error) {
|
||||
return nil, errors.New("IPv6 test fake error")
|
||||
},
|
||||
webhooks: []runtime.Object{
|
||||
&auth1alpha1.WebhookAuthenticator{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -859,14 +893,14 @@ func TestController(t *testing.T) {
|
||||
Name: "test-name",
|
||||
},
|
||||
Spec: auth1alpha1.WebhookAuthenticatorSpec{
|
||||
Endpoint: fmt.Sprintf("%s:%s", "https://localhost", localhostURL.Port()),
|
||||
Endpoint: fmt.Sprintf("https://localhost:%s", localhostURL.Port()),
|
||||
TLS: &auth1alpha1.TLSSpec{
|
||||
// CA Bundle for validating the server's certs
|
||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString(caForLocalhostAsHostname.Bundle()),
|
||||
},
|
||||
},
|
||||
Status: auth1alpha1.WebhookAuthenticatorStatus{
|
||||
Conditions: allHappyConditionsSuccess(fmt.Sprintf("%s:%s", "https://localhost", localhostURL.Port()), frozenMetav1Now, 0),
|
||||
Conditions: allHappyConditionsSuccess(fmt.Sprintf("https://localhost:%s", localhostURL.Port()), frozenMetav1Now, 0),
|
||||
Phase: "Ready",
|
||||
},
|
||||
},
|
||||
@@ -877,7 +911,7 @@ func TestController(t *testing.T) {
|
||||
"timestamp": "2099-08-08T13:57:36.123456Z",
|
||||
"logger": "webhookcachefiller-controller",
|
||||
"message": "added new webhook authenticator",
|
||||
"endpoint": fmt.Sprintf("%s:%s", "https://localhost", localhostURL.Port()),
|
||||
"endpoint": fmt.Sprintf("https://localhost:%s", localhostURL.Port()),
|
||||
"webhook": map[string]interface{}{
|
||||
"name": "test-name",
|
||||
},
|
||||
@@ -894,14 +928,6 @@ func TestController(t *testing.T) {
|
||||
{
|
||||
name: "validateConnection: IPv6 address with port: should call dialer func with correct arguments",
|
||||
syncKey: controllerlib.Key{Name: "test-name"},
|
||||
tlsDialerFunc: func(network string, addr string, config *tls.Config) (*tls.Conn, error) {
|
||||
assert.Equal(t, "tcp", network)
|
||||
assert.Equal(t, "[0:0:0:0:0:0:0:1]:4242", addr)
|
||||
assert.True(t, caForLocalhostAs127001.Pool().Equal(config.RootCAs))
|
||||
assert.Equal(t, uint16(tls.VersionTLS12), config.MinVersion)
|
||||
|
||||
return nil, errors.New("IPv6 test fake error to skip real dial in prod code, this is actually success")
|
||||
},
|
||||
webhooks: []runtime.Object{
|
||||
&auth1alpha1.WebhookAuthenticator{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -930,7 +956,7 @@ func TestController(t *testing.T) {
|
||||
Conditions: conditionstestutil.Replace(
|
||||
allHappyConditionsSuccess("https://[0:0:0:0:0:0:0:1]:4242/some/fake/path", frozenMetav1Now, 0),
|
||||
[]metav1.Condition{
|
||||
sadWebhookConnectionValidWithMessage(frozenMetav1Now, 0, "cannot dial server: IPv6 test fake error to skip real dial in prod code, this is actually success"),
|
||||
sadWebhookConnectionValidWithMessage(frozenMetav1Now, 0, "cannot dial server: dial tcp [::1]:4242: connect: connection refused"),
|
||||
sadReadyCondition(frozenMetav1Now, 0),
|
||||
unknownAuthenticatorValid(frozenMetav1Now, 0),
|
||||
},
|
||||
@@ -945,20 +971,12 @@ func TestController(t *testing.T) {
|
||||
updateStatusAction,
|
||||
}
|
||||
},
|
||||
wantSyncLoopErr: testutil.WantExactErrorString(`cannot dial server: IPv6 test fake error to skip real dial in prod code, this is actually success`),
|
||||
wantSyncLoopErr: testutil.WantExactErrorString(`cannot dial server: dial tcp [::1]:4242: connect: connection refused`),
|
||||
wantCacheEntries: 0,
|
||||
},
|
||||
{
|
||||
name: "validateConnection: IPv6 address without port: should call dialer func with correct arguments",
|
||||
syncKey: controllerlib.Key{Name: "test-name"},
|
||||
tlsDialerFunc: func(network string, addr string, config *tls.Config) (*tls.Conn, error) {
|
||||
assert.Equal(t, "tcp", network)
|
||||
assert.Equal(t, "[0:0:0:0:0:0:0:1]:443", addr, "should add default port when port not provided")
|
||||
assert.True(t, caForLocalhostAs127001.Pool().Equal(config.RootCAs))
|
||||
assert.Equal(t, uint16(tls.VersionTLS12), config.MinVersion)
|
||||
|
||||
return nil, errors.New("IPv6 test fake error to skip real dial in prod code, this is actually success")
|
||||
},
|
||||
webhooks: []runtime.Object{
|
||||
&auth1alpha1.WebhookAuthenticator{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -987,7 +1005,7 @@ func TestController(t *testing.T) {
|
||||
Conditions: conditionstestutil.Replace(
|
||||
allHappyConditionsSuccess("https://[0:0:0:0:0:0:0:1]/some/fake/path", frozenMetav1Now, 0),
|
||||
[]metav1.Condition{
|
||||
sadWebhookConnectionValidWithMessage(frozenMetav1Now, 0, "cannot dial server: IPv6 test fake error to skip real dial in prod code, this is actually success"),
|
||||
sadWebhookConnectionValidWithMessage(frozenMetav1Now, 0, "cannot dial server: dial tcp [::1]:443: connect: connection refused"),
|
||||
sadReadyCondition(frozenMetav1Now, 0),
|
||||
unknownAuthenticatorValid(frozenMetav1Now, 0),
|
||||
},
|
||||
@@ -1002,7 +1020,7 @@ func TestController(t *testing.T) {
|
||||
updateStatusAction,
|
||||
}
|
||||
},
|
||||
wantSyncLoopErr: testutil.WantExactErrorString(`cannot dial server: IPv6 test fake error to skip real dial in prod code, this is actually success`),
|
||||
wantSyncLoopErr: testutil.WantExactErrorString(`cannot dial server: dial tcp [::1]:443: connect: connection refused`),
|
||||
wantCacheEntries: 0,
|
||||
},
|
||||
{
|
||||
@@ -1091,14 +1109,6 @@ func TestController(t *testing.T) {
|
||||
{
|
||||
name: "validateConnection: IPv6 address without port or brackets: should succeed since IPv6 brackets are optional without port",
|
||||
syncKey: controllerlib.Key{Name: "test-name"},
|
||||
tlsDialerFunc: func(network string, addr string, config *tls.Config) (*tls.Conn, error) {
|
||||
assert.Equal(t, "tcp", network)
|
||||
assert.Equal(t, "[0:0:0:0:0:0:0:1]:443", addr)
|
||||
assert.True(t, caForLocalhostAs127001.Pool().Equal(config.RootCAs))
|
||||
assert.Equal(t, uint16(tls.VersionTLS12), config.MinVersion)
|
||||
|
||||
return nil, errors.New("IPv6 test fake error to skip real dial in prod code, this is actually success")
|
||||
},
|
||||
webhooks: []runtime.Object{
|
||||
&auth1alpha1.WebhookAuthenticator{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -1127,7 +1137,7 @@ func TestController(t *testing.T) {
|
||||
Conditions: conditionstestutil.Replace(
|
||||
allHappyConditionsSuccess("https://0:0:0:0:0:0:0:1/some/fake/path", frozenMetav1Now, 0),
|
||||
[]metav1.Condition{
|
||||
sadWebhookConnectionValidWithMessage(frozenMetav1Now, 0, "cannot dial server: IPv6 test fake error to skip real dial in prod code, this is actually success"),
|
||||
sadWebhookConnectionValidWithMessage(frozenMetav1Now, 0, "cannot dial server: dial tcp [::1]:443: connect: connection refused"),
|
||||
sadReadyCondition(frozenMetav1Now, 0),
|
||||
unknownAuthenticatorValid(frozenMetav1Now, 0),
|
||||
},
|
||||
@@ -1142,7 +1152,7 @@ func TestController(t *testing.T) {
|
||||
updateStatusAction,
|
||||
}
|
||||
},
|
||||
wantSyncLoopErr: testutil.WantExactErrorString(`cannot dial server: IPv6 test fake error to skip real dial in prod code, this is actually success`),
|
||||
wantSyncLoopErr: testutil.WantExactErrorString(`cannot dial server: dial tcp [::1]:443: connect: connection refused`),
|
||||
wantCacheEntries: 0,
|
||||
},
|
||||
{
|
||||
@@ -1310,16 +1320,12 @@ func TestController(t *testing.T) {
|
||||
var log bytes.Buffer
|
||||
logger := plog.TestLogger(t, &log)
|
||||
|
||||
if tt.tlsDialerFunc == nil {
|
||||
tt.tlsDialerFunc = tls.Dial
|
||||
}
|
||||
controller := New(
|
||||
cache,
|
||||
pinnipedAPIClient,
|
||||
informers.Authentication().V1alpha1().WebhookAuthenticators(),
|
||||
frozenClock,
|
||||
logger,
|
||||
tt.tlsDialerFunc)
|
||||
logger)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
@@ -1356,52 +1362,61 @@ func TestController(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
if tt.wantActions != nil {
|
||||
if !assert.ElementsMatch(t, tt.wantActions(), pinnipedAPIClient.Actions()) {
|
||||
// cmp.Diff is superior to require.ElementsMatch in terms of readability here.
|
||||
// require.ElementsMatch will handle pointers better than require.Equal, but
|
||||
// the timestamps are still incredibly verbose.
|
||||
require.Fail(t, cmp.Diff(tt.wantActions(), pinnipedAPIClient.Actions()), "actions should be exactly the expected number of actions and also contain the correct resources")
|
||||
}
|
||||
} else {
|
||||
require.Fail(t, "wantActions is required for test "+tt.name)
|
||||
}
|
||||
|
||||
require.NotEmpty(t, tt.wantActions, "wantActions is required for test %s", tt.name)
|
||||
require.Equal(t, tt.wantActions(), pinnipedAPIClient.Actions())
|
||||
require.Equal(t, tt.wantCacheEntries, len(cache.Keys()), fmt.Sprintf("expected cache entries is incorrect. wanted:%d, got: %d, keys: %v", tt.wantCacheEntries, len(cache.Keys()), cache.Keys()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewWebhookAuthenticator(t *testing.T) {
|
||||
goodEndpoint := "https://example.com"
|
||||
server, serverCA := tlsserver.TestServerIPv4(t,
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Webhook clients should always use ptls.Default when making requests to the webhook. Assert that here.
|
||||
tlsserver.AssertTLS(t, r, ptls.Default)
|
||||
|
||||
testServerCABundle, testServerURL := testutil.TLSTestServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, string(body), "test-token")
|
||||
_, err = w.Write([]byte(`{}`))
|
||||
require.NoError(t, err)
|
||||
})
|
||||
// Loosely assert on the request body.
|
||||
body, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, string(body), "test-token")
|
||||
|
||||
// Write a realistic looking fake response for a successfully authenticated user, so we can tell that
|
||||
// this endpoint was actually called by the test below where it asserts on the fake user and group names.
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
responseBody := authenticationv1beta1.TokenReview{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "TokenReview",
|
||||
APIVersion: authenticationv1beta1.SchemeGroupVersion.String(),
|
||||
},
|
||||
Status: authenticationv1beta1.TokenReviewStatus{
|
||||
Authenticated: true,
|
||||
User: authenticationv1beta1.UserInfo{
|
||||
Username: "fake-username-from-server",
|
||||
Groups: []string{"fake-group-from-server-1", "fake-group-from-server-2"},
|
||||
},
|
||||
},
|
||||
}
|
||||
err = json.NewEncoder(w).Encode(responseBody)
|
||||
require.NoError(t, err)
|
||||
}),
|
||||
tlsserver.RecordTLSHello,
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
endpoint string
|
||||
pemBytes []byte
|
||||
tempFileFunc func(dir string, pattern string) (*os.File, error)
|
||||
marshallFunc func(config clientcmdapi.Config, filename string) error
|
||||
prereqOk bool
|
||||
wantConditions []*metav1.Condition
|
||||
wantWebhook bool
|
||||
wantErr string
|
||||
testCreatedWebhookWithFakeToken bool
|
||||
name string
|
||||
endpoint string
|
||||
pemBytes []byte
|
||||
prereqOk bool
|
||||
wantConditions []*metav1.Condition
|
||||
wantErr string
|
||||
wantWebhook bool // When true, we want a webhook client to have been successfully created.
|
||||
callWebhook bool // When true, really call the webhook endpoint using the created webhook client.
|
||||
}{
|
||||
{
|
||||
name: "prerequisites not ready, cannot create webhook authenticator",
|
||||
endpoint: "",
|
||||
pemBytes: []byte("irrelevant pem bytes"),
|
||||
tempFileFunc: os.CreateTemp,
|
||||
marshallFunc: clientcmd.WriteToFile,
|
||||
wantErr: "",
|
||||
name: "prerequisites not ready, cannot create webhook authenticator",
|
||||
endpoint: "",
|
||||
pemBytes: []byte("irrelevant pem bytes"),
|
||||
wantErr: "",
|
||||
wantConditions: []*metav1.Condition{{
|
||||
Type: "AuthenticatorValid",
|
||||
Status: "Unknown",
|
||||
@@ -1410,58 +1425,22 @@ func TestNewWebhookAuthenticator(t *testing.T) {
|
||||
}},
|
||||
prereqOk: false,
|
||||
}, {
|
||||
name: "temp file failure, cannot create webhook authenticator",
|
||||
endpoint: "",
|
||||
pemBytes: []byte("irrelevant pem bytes"),
|
||||
tempFileFunc: func(_ string, _ string) (*os.File, error) {
|
||||
return nil, fmt.Errorf("some temp file error")
|
||||
},
|
||||
marshallFunc: clientcmd.WriteToFile,
|
||||
prereqOk: true,
|
||||
wantConditions: []*metav1.Condition{{
|
||||
Type: "AuthenticatorValid",
|
||||
Status: "False",
|
||||
Reason: "UnableToCreateTempFile",
|
||||
Message: "unable to create temporary file: some temp file error",
|
||||
}},
|
||||
wantErr: "unable to create temporary file: some temp file error",
|
||||
}, {
|
||||
name: "marshal failure, cannot create webhook authenticator",
|
||||
endpoint: "",
|
||||
pemBytes: []byte("irrelevant pem bytes"),
|
||||
tempFileFunc: os.CreateTemp,
|
||||
marshallFunc: func(_ clientcmdapi.Config, _ string) error {
|
||||
return fmt.Errorf("some marshal error")
|
||||
},
|
||||
name: "invalid pem data, unable to parse bytes as PEM block",
|
||||
endpoint: "https://does-not-matter-will-not-be-used",
|
||||
pemBytes: []byte("invalid-bas64"),
|
||||
prereqOk: true,
|
||||
wantConditions: []*metav1.Condition{{
|
||||
Type: "AuthenticatorValid",
|
||||
Status: "False",
|
||||
Reason: "UnableToMarshallKubeconfig",
|
||||
Message: "unable to marshal kubeconfig: some marshal error",
|
||||
Reason: "UnableToCreateClient",
|
||||
Message: "unable to create client for this webhook: could not create secure client config: unable to load root certificates: unable to parse bytes as PEM block",
|
||||
}},
|
||||
wantErr: "unable to marshal kubeconfig: some marshal error",
|
||||
wantErr: "unable to create client for this webhook: could not create secure client config: unable to load root certificates: unable to parse bytes as PEM block",
|
||||
}, {
|
||||
name: "invalid pem data, unable to parse bytes as PEM block",
|
||||
endpoint: goodEndpoint,
|
||||
pemBytes: []byte("invalid-bas64"),
|
||||
tempFileFunc: os.CreateTemp,
|
||||
marshallFunc: clientcmd.WriteToFile,
|
||||
prereqOk: true,
|
||||
wantConditions: []*metav1.Condition{{
|
||||
Type: "AuthenticatorValid",
|
||||
Status: "False",
|
||||
Reason: "UnableToInstantiateWebhook",
|
||||
Message: "unable to instantiate webhook: unable to load root certificates: unable to parse bytes as PEM block",
|
||||
}},
|
||||
wantErr: "unable to instantiate webhook: unable to load root certificates: unable to parse bytes as PEM block",
|
||||
}, {
|
||||
name: "valid config with no TLS spec, webhook authenticator created",
|
||||
endpoint: goodEndpoint,
|
||||
pemBytes: nil,
|
||||
tempFileFunc: os.CreateTemp,
|
||||
marshallFunc: clientcmd.WriteToFile,
|
||||
prereqOk: true,
|
||||
name: "valid config with no PEM bytes, webhook authenticator created",
|
||||
endpoint: "https://does-not-matter-will-not-be-used",
|
||||
pemBytes: nil,
|
||||
prereqOk: true,
|
||||
wantConditions: []*metav1.Condition{{
|
||||
Type: "AuthenticatorValid",
|
||||
Status: "True",
|
||||
@@ -1470,19 +1449,18 @@ func TestNewWebhookAuthenticator(t *testing.T) {
|
||||
}},
|
||||
wantWebhook: true,
|
||||
}, {
|
||||
name: "success, webhook authenticator created",
|
||||
endpoint: testServerURL,
|
||||
pemBytes: []byte(testServerCABundle),
|
||||
tempFileFunc: os.CreateTemp,
|
||||
marshallFunc: clientcmd.WriteToFile,
|
||||
prereqOk: true,
|
||||
name: "valid config, webhook authenticator created, and test calling webhook server",
|
||||
endpoint: server.URL,
|
||||
pemBytes: serverCA,
|
||||
prereqOk: true,
|
||||
wantConditions: []*metav1.Condition{{
|
||||
Type: "AuthenticatorValid",
|
||||
Status: "True",
|
||||
Reason: "Success",
|
||||
Message: "authenticator initialized",
|
||||
}},
|
||||
testCreatedWebhookWithFakeToken: true,
|
||||
wantWebhook: true,
|
||||
callWebhook: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1491,12 +1469,14 @@ func TestNewWebhookAuthenticator(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
var conditions []*metav1.Condition
|
||||
webhook, conditions, err := newWebhookAuthenticator(tt.endpoint, tt.pemBytes, tt.tempFileFunc, tt.marshallFunc, conditions, tt.prereqOk)
|
||||
webhook, conditions, err := newWebhookAuthenticator(tt.endpoint, tt.pemBytes, conditions, tt.prereqOk)
|
||||
|
||||
require.Equal(t, tt.wantConditions, conditions)
|
||||
|
||||
if tt.wantWebhook {
|
||||
require.NotNil(t, webhook)
|
||||
} else {
|
||||
require.Nil(t, webhook)
|
||||
}
|
||||
|
||||
if tt.wantErr != "" {
|
||||
@@ -1505,11 +1485,12 @@ func TestNewWebhookAuthenticator(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
if tt.testCreatedWebhookWithFakeToken {
|
||||
if tt.callWebhook {
|
||||
authResp, isAuthenticated, err := webhook.AuthenticateToken(context.Background(), "test-token")
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, authResp)
|
||||
require.False(t, isAuthenticated)
|
||||
require.True(t, isAuthenticated)
|
||||
require.Equal(t, "fake-username-from-server", authResp.User.GetName())
|
||||
require.Equal(t, []string{"fake-group-from-server-1", "fake-group-from-server-2"}, authResp.User.GetGroups())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package kubecertagent provides controllers that ensure a pod (the kube-cert-agent), is
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -278,7 +279,13 @@ func (c *agentController) Sync(ctx controllerlib.Context) error {
|
||||
// If there are no healthy controller manager pods, we alert the user that we can't find the keypair via
|
||||
// the CredentialIssuer.
|
||||
if newestControllerManager == nil {
|
||||
err := fmt.Errorf("could not find a healthy kube-controller-manager pod (%s)", pluralize(controllerManagerPods))
|
||||
msg := fmt.Sprintf("could not find a healthy kube-controller-manager pod (%s)", pluralize(controllerManagerPods))
|
||||
if len(controllerManagerPods) == 0 {
|
||||
err = fmt.Errorf("%s: note that this error is the expected behavior for some cluster types, "+
|
||||
"including most cloud provider clusters (e.g. GKE, AKS, EKS)", msg)
|
||||
} else {
|
||||
err = errors.New(msg)
|
||||
}
|
||||
return c.failStrategyAndErr(ctx.Context, credIssuer, err, configv1alpha1.CouldNotFetchKeyStrategyReason)
|
||||
}
|
||||
|
||||
|
||||
@@ -270,13 +270,15 @@ func TestAgentController(t *testing.T) {
|
||||
},
|
||||
},
|
||||
wantDistinctErrors: []string{
|
||||
"could not find a healthy kube-controller-manager pod (0 candidates)",
|
||||
"could not find a healthy kube-controller-manager pod (0 candidates): " +
|
||||
"note that this error is the expected behavior for some cluster types, including most cloud provider clusters (e.g. GKE, AKS, EKS)",
|
||||
},
|
||||
wantStrategy: &configv1alpha1.CredentialIssuerStrategy{
|
||||
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
|
||||
Status: configv1alpha1.ErrorStrategyStatus,
|
||||
Reason: configv1alpha1.CouldNotFetchKeyStrategyReason,
|
||||
Message: "could not find a healthy kube-controller-manager pod (0 candidates)",
|
||||
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
|
||||
Status: configv1alpha1.ErrorStrategyStatus,
|
||||
Reason: configv1alpha1.CouldNotFetchKeyStrategyReason,
|
||||
Message: "could not find a healthy kube-controller-manager pod (0 candidates): " +
|
||||
"note that this error is the expected behavior for some cluster types, including most cloud provider clusters (e.g. GKE, AKS, EKS)",
|
||||
LastUpdateTime: metav1.NewTime(now),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kubecertagent
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
|
||||
func TestSecureTLS(t *testing.T) {
|
||||
var sawRequest bool
|
||||
server := tlsserver.TLSTestServer(t, http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
||||
server, serverCA := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
||||
tlsserver.AssertTLS(t, r, ptls.Secure)
|
||||
sawRequest = true
|
||||
}), tlsserver.RecordTLSHello)
|
||||
@@ -27,7 +27,7 @@ func TestSecureTLS(t *testing.T) {
|
||||
config := &rest.Config{
|
||||
Host: server.URL,
|
||||
TLSClientConfig: rest.TLSClientConfig{
|
||||
CAData: tlsserver.TLSTestServerCA(server),
|
||||
CAData: serverCA,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
+13
-14
@@ -11,7 +11,6 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -62,15 +61,15 @@ var (
|
||||
func TestController(t *testing.T) {
|
||||
require.Equal(t, 5, countExpectedConditions)
|
||||
|
||||
goodServer := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
goodServer, goodServerCA := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
}), tlsserver.RecordTLSHello)
|
||||
goodServerDomain, _ := strings.CutPrefix(goodServer.URL, "https://")
|
||||
goodServerCAB64 := base64.StdEncoding.EncodeToString(tlsserver.TLSTestServerCA(goodServer))
|
||||
goodServerCAB64 := base64.StdEncoding.EncodeToString(goodServerCA)
|
||||
|
||||
goodServerIPv6 := tlsserver.TLSTestIPv6Server(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
goodServerIPv6, goodServerIPv6CA := tlsserver.TestServerIPv6(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
}), tlsserver.RecordTLSHello)
|
||||
goodServerIPv6Domain, _ := strings.CutPrefix(goodServerIPv6.URL, "https://")
|
||||
goodServerIPv6CAB64 := base64.StdEncoding.EncodeToString(tlsserver.TLSTestServerCA(goodServerIPv6))
|
||||
goodServerIPv6CAB64 := base64.StdEncoding.EncodeToString(goodServerIPv6CA)
|
||||
|
||||
caForUnknownServer, err := certauthority.New("Some Unknown CA", time.Hour)
|
||||
require.NoError(t, err)
|
||||
@@ -366,7 +365,7 @@ func TestController(t *testing.T) {
|
||||
AllowedOrganizations: []string{"organization1", "org2"},
|
||||
OrganizationLoginPolicy: "OnlyUsersFromAllowedOrganizations",
|
||||
AuthorizationURL: fmt.Sprintf("https://%s/login/oauth/authorize", *validFilledOutIDP.Spec.GitHubAPI.Host),
|
||||
HttpClient: buildPretendHttpClient(t, goodServer),
|
||||
HttpClient: buildPretendHttpClient(t, goodServerCA),
|
||||
},
|
||||
},
|
||||
wantResultingUpstreams: []v1alpha1.GitHubIdentityProvider{
|
||||
@@ -413,7 +412,7 @@ func TestController(t *testing.T) {
|
||||
},
|
||||
OrganizationLoginPolicy: "AllGitHubUsers",
|
||||
AuthorizationURL: fmt.Sprintf("https://%s/login/oauth/authorize", goodServerDomain),
|
||||
HttpClient: buildPretendHttpClient(t, goodServer),
|
||||
HttpClient: buildPretendHttpClient(t, goodServerCA),
|
||||
},
|
||||
},
|
||||
wantResultingUpstreams: []v1alpha1.GitHubIdentityProvider{
|
||||
@@ -467,7 +466,7 @@ func TestController(t *testing.T) {
|
||||
},
|
||||
OrganizationLoginPolicy: "AllGitHubUsers",
|
||||
AuthorizationURL: fmt.Sprintf("https://%s/login/oauth/authorize", goodServerIPv6Domain),
|
||||
HttpClient: buildPretendHttpClient(t, goodServer),
|
||||
HttpClient: buildPretendHttpClient(t, goodServerCA),
|
||||
},
|
||||
},
|
||||
wantResultingUpstreams: []v1alpha1.GitHubIdentityProvider{
|
||||
@@ -547,7 +546,7 @@ func TestController(t *testing.T) {
|
||||
AllowedOrganizations: []string{"organization1", "org2"},
|
||||
OrganizationLoginPolicy: "OnlyUsersFromAllowedOrganizations",
|
||||
AuthorizationURL: fmt.Sprintf("https://%s/login/oauth/authorize", *validFilledOutIDP.Spec.GitHubAPI.Host),
|
||||
HttpClient: buildPretendHttpClient(t, goodServer),
|
||||
HttpClient: buildPretendHttpClient(t, goodServerCA),
|
||||
},
|
||||
{
|
||||
Name: "other-idp-name",
|
||||
@@ -562,7 +561,7 @@ func TestController(t *testing.T) {
|
||||
AllowedOrganizations: []string{"organization1", "org2"},
|
||||
OrganizationLoginPolicy: "OnlyUsersFromAllowedOrganizations",
|
||||
AuthorizationURL: fmt.Sprintf("https://%s/login/oauth/authorize", *validFilledOutIDP.Spec.GitHubAPI.Host),
|
||||
HttpClient: buildPretendHttpClient(t, goodServer),
|
||||
HttpClient: buildPretendHttpClient(t, goodServerCA),
|
||||
},
|
||||
},
|
||||
wantResultingUpstreams: []v1alpha1.GitHubIdentityProvider{
|
||||
@@ -1707,10 +1706,10 @@ func TestController(t *testing.T) {
|
||||
func TestController_WithExistingConditions(t *testing.T) {
|
||||
require.Equal(t, 5, countExpectedConditions)
|
||||
|
||||
goodServer := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
goodServer, goodServerCA := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
}), tlsserver.RecordTLSHello)
|
||||
goodServerDomain, _ := strings.CutPrefix(goodServer.URL, "https://")
|
||||
goodServerCAB64 := base64.StdEncoding.EncodeToString(tlsserver.TLSTestServerCA(goodServer))
|
||||
goodServerCAB64 := base64.StdEncoding.EncodeToString(goodServerCA)
|
||||
|
||||
oneHourAgo := metav1.Time{Time: time.Now().Add(-1 * time.Hour)}
|
||||
namespace := "existing-conditions-namespace"
|
||||
@@ -1878,9 +1877,9 @@ func TestController_WithExistingConditions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func buildPretendHttpClient(t *testing.T, server *httptest.Server) *http.Client {
|
||||
func buildPretendHttpClient(t *testing.T, ca []byte) *http.Client {
|
||||
t.Helper()
|
||||
rootCAs, err := cert.NewPoolFromBytes(tlsserver.TLSTestServerCA(server))
|
||||
rootCAs, err := cert.NewPoolFromBytes(ca)
|
||||
require.NoError(t, err)
|
||||
return buildHttpClient(rootCAs)
|
||||
}
|
||||
|
||||
+16
-15
@@ -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 oidcupstreamwatcher
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
"go.pinniped.dev/internal/testutil/oidctestutil"
|
||||
"go.pinniped.dev/internal/testutil/testlogger"
|
||||
"go.pinniped.dev/internal/testutil/tlsassertions"
|
||||
"go.pinniped.dev/internal/testutil/tlsserver"
|
||||
"go.pinniped.dev/internal/upstreamoidc"
|
||||
)
|
||||
|
||||
@@ -113,7 +114,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
earlier := metav1.NewTime(now.Add(-1 * time.Hour).UTC())
|
||||
|
||||
// Start another test server that answers discovery successfully.
|
||||
testIssuerCA, testIssuerURL := newTestIssuer(t)
|
||||
testIssuerURL, testIssuerCA := newTestIssuer(t)
|
||||
testIssuerCABase64 := base64.StdEncoding.EncodeToString([]byte(testIssuerCA))
|
||||
testIssuerAuthorizeURL, err := url.Parse("https://example.com/authorize")
|
||||
require.NoError(t, err)
|
||||
@@ -1529,7 +1530,7 @@ func normalizeOIDCUpstreams(upstreams []v1alpha1.OIDCIdentityProvider, now metav
|
||||
|
||||
func newTestIssuer(t *testing.T) (string, string) {
|
||||
mux := http.NewServeMux()
|
||||
caBundlePEM, testURL := testutil.TLSTestServer(t, mux.ServeHTTP)
|
||||
server, serverCA := tlsserver.TestServerIPv4(t, http.HandlerFunc(mux.ServeHTTP), nil)
|
||||
|
||||
type providerJSON struct {
|
||||
Issuer string `json:"issuer"`
|
||||
@@ -1543,7 +1544,7 @@ func newTestIssuer(t *testing.T) (string, string) {
|
||||
mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&providerJSON{
|
||||
Issuer: testURL,
|
||||
Issuer: server.URL,
|
||||
AuthURL: "https://example.com/authorize",
|
||||
RevocationURL: "https://example.com/revoke",
|
||||
TokenURL: "https://example.com/token",
|
||||
@@ -1554,7 +1555,7 @@ func newTestIssuer(t *testing.T) (string, string) {
|
||||
mux.HandleFunc("/valid-without-revocation/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&providerJSON{
|
||||
Issuer: testURL + "/valid-without-revocation",
|
||||
Issuer: server.URL + "/valid-without-revocation",
|
||||
AuthURL: "https://example.com/authorize",
|
||||
RevocationURL: "", // none
|
||||
TokenURL: "https://example.com/token",
|
||||
@@ -1565,7 +1566,7 @@ func newTestIssuer(t *testing.T) (string, string) {
|
||||
mux.HandleFunc("/invalid/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&providerJSON{
|
||||
Issuer: testURL + "/invalid",
|
||||
Issuer: server.URL + "/invalid",
|
||||
AuthURL: "%",
|
||||
TokenURL: "https://example.com/token",
|
||||
})
|
||||
@@ -1575,7 +1576,7 @@ func newTestIssuer(t *testing.T) (string, string) {
|
||||
mux.HandleFunc("/invalid-revocation-url/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&providerJSON{
|
||||
Issuer: testURL + "/invalid-revocation-url",
|
||||
Issuer: server.URL + "/invalid-revocation-url",
|
||||
AuthURL: "https://example.com/authorize",
|
||||
RevocationURL: "%",
|
||||
TokenURL: "https://example.com/token",
|
||||
@@ -1586,7 +1587,7 @@ func newTestIssuer(t *testing.T) (string, string) {
|
||||
mux.HandleFunc("/insecure/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&providerJSON{
|
||||
Issuer: testURL + "/insecure",
|
||||
Issuer: server.URL + "/insecure",
|
||||
AuthURL: "http://example.com/authorize",
|
||||
TokenURL: "https://example.com/token",
|
||||
})
|
||||
@@ -1596,7 +1597,7 @@ func newTestIssuer(t *testing.T) (string, string) {
|
||||
mux.HandleFunc("/insecure-revocation-url/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&providerJSON{
|
||||
Issuer: testURL + "/insecure-revocation-url",
|
||||
Issuer: server.URL + "/insecure-revocation-url",
|
||||
AuthURL: "https://example.com/authorize",
|
||||
RevocationURL: "http://example.com/revoke",
|
||||
TokenURL: "https://example.com/token",
|
||||
@@ -1607,7 +1608,7 @@ func newTestIssuer(t *testing.T) (string, string) {
|
||||
mux.HandleFunc("/insecure-token-url/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&providerJSON{
|
||||
Issuer: testURL + "/insecure-token-url",
|
||||
Issuer: server.URL + "/insecure-token-url",
|
||||
AuthURL: "https://example.com/authorize",
|
||||
RevocationURL: "https://example.com/revoke",
|
||||
TokenURL: "http://example.com/token",
|
||||
@@ -1619,7 +1620,7 @@ func newTestIssuer(t *testing.T) (string, string) {
|
||||
mux.HandleFunc("/missing-token-url/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&providerJSON{
|
||||
Issuer: testURL + "/missing-token-url",
|
||||
Issuer: server.URL + "/missing-token-url",
|
||||
AuthURL: "https://example.com/authorize",
|
||||
RevocationURL: "https://example.com/revoke",
|
||||
})
|
||||
@@ -1629,7 +1630,7 @@ func newTestIssuer(t *testing.T) (string, string) {
|
||||
mux.HandleFunc("/missing-auth-url/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&providerJSON{
|
||||
Issuer: testURL + "/missing-auth-url",
|
||||
Issuer: server.URL + "/missing-auth-url",
|
||||
RevocationURL: "https://example.com/revoke",
|
||||
TokenURL: "https://example.com/token",
|
||||
})
|
||||
@@ -1638,13 +1639,13 @@ func newTestIssuer(t *testing.T) (string, string) {
|
||||
// handle the four issuer with trailing slash configs
|
||||
|
||||
// valid case in= out=
|
||||
// handled above at the root of testURL
|
||||
// handled above at the root of server.URL
|
||||
|
||||
// valid case in=/ out=/
|
||||
mux.HandleFunc("/ends-with-slash/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&providerJSON{
|
||||
Issuer: testURL + "/ends-with-slash/",
|
||||
Issuer: server.URL + "/ends-with-slash/",
|
||||
AuthURL: "https://example.com/authorize",
|
||||
RevocationURL: "https://example.com/revoke",
|
||||
TokenURL: "https://example.com/token",
|
||||
@@ -1657,5 +1658,5 @@ func newTestIssuer(t *testing.T) (string, string) {
|
||||
// invalid case in=/ out=
|
||||
// can be tested using root endpoint
|
||||
|
||||
return caBundlePEM, testURL
|
||||
return server.URL, string(serverCA)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
package controllermanager
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -241,7 +240,6 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) { //nol
|
||||
informers.pinniped.Authentication().V1alpha1().WebhookAuthenticators(),
|
||||
clock.RealClock{},
|
||||
plog.New(),
|
||||
tls.Dial,
|
||||
),
|
||||
singletonWorker,
|
||||
).
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kubeclient
|
||||
@@ -36,7 +36,9 @@ type Client struct {
|
||||
}
|
||||
|
||||
func New(opts ...Option) (*Client, error) {
|
||||
c := &clientConfig{}
|
||||
c := &clientConfig{
|
||||
tlsConfigFunc: ptls.Secure, // Set a default value. Can be overridden by an Option.
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
@@ -51,16 +53,16 @@ func New(opts ...Option) (*Client, error) {
|
||||
WithConfig(inClusterConfig)(c) // make sure all writes to clientConfig flow through one code path
|
||||
}
|
||||
|
||||
secureKubeConfig, err := createSecureKubeConfig(c.config)
|
||||
ptlsKubeConfig, err := createPTLSKubeConfig(c.config, c.tlsConfigFunc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create secure client config: %w", err)
|
||||
}
|
||||
|
||||
// explicitly use json when talking to CRD APIs
|
||||
jsonKubeConfig := createJSONKubeConfig(secureKubeConfig)
|
||||
jsonKubeConfig := createJSONKubeConfig(ptlsKubeConfig)
|
||||
|
||||
// explicitly use protobuf when talking to built-in kube APIs
|
||||
protoKubeConfig := createProtoKubeConfig(secureKubeConfig)
|
||||
protoKubeConfig := createProtoKubeConfig(ptlsKubeConfig)
|
||||
|
||||
// Connect to the core Kubernetes API.
|
||||
k8sClient, err := kubernetes.NewForConfig(configWithWrapper(protoKubeConfig, kubescheme.Scheme, kubescheme.Codecs, c.middlewares, c.transportWrapper))
|
||||
@@ -120,25 +122,25 @@ func createProtoKubeConfig(kubeConfig *restclient.Config) *restclient.Config {
|
||||
return protoKubeConfig
|
||||
}
|
||||
|
||||
// createSecureKubeConfig returns a copy of the input config with the WrapTransport
|
||||
// createPTLSKubeConfig returns a copy of the input config with the WrapTransport
|
||||
// enhanced to use the secure TLS configuration of the ptls / phttp packages.
|
||||
func createSecureKubeConfig(kubeConfig *restclient.Config) (*restclient.Config, error) {
|
||||
secureKubeConfig := restclient.CopyConfig(kubeConfig)
|
||||
func createPTLSKubeConfig(kubeConfig *restclient.Config, tlsConfigFunc ptls.ConfigFunc) (*restclient.Config, error) {
|
||||
ptlsKubeConfig := restclient.CopyConfig(kubeConfig)
|
||||
|
||||
// by setting proxy to always be non-nil, we bust the client-go global TLS config cache.
|
||||
// this is required to make our wrapper function work without data races. the unit tests
|
||||
// associated with this code run in parallel to assert that we are not using the cache.
|
||||
// see k8s.io/client-go/transport.tlsConfigKey
|
||||
if secureKubeConfig.Proxy == nil {
|
||||
secureKubeConfig.Proxy = net.NewProxierWithNoProxyCIDR(http.ProxyFromEnvironment)
|
||||
if ptlsKubeConfig.Proxy == nil {
|
||||
ptlsKubeConfig.Proxy = net.NewProxierWithNoProxyCIDR(http.ProxyFromEnvironment)
|
||||
}
|
||||
|
||||
// make sure restclient.TLSConfigFor always returns a non-nil TLS config
|
||||
if len(secureKubeConfig.NextProtos) == 0 {
|
||||
secureKubeConfig.NextProtos = ptls.Secure(nil).NextProtos
|
||||
if len(ptlsKubeConfig.NextProtos) == 0 {
|
||||
ptlsKubeConfig.NextProtos = tlsConfigFunc(nil).NextProtos
|
||||
}
|
||||
|
||||
tlsConfigTest, err := restclient.TLSConfigFor(secureKubeConfig)
|
||||
tlsConfigTest, err := restclient.TLSConfigFor(ptlsKubeConfig)
|
||||
if err != nil {
|
||||
return nil, err // should never happen because our input config should always be valid
|
||||
}
|
||||
@@ -146,10 +148,10 @@ func createSecureKubeConfig(kubeConfig *restclient.Config) (*restclient.Config,
|
||||
return nil, fmt.Errorf("unexpected empty TLS config") // should never happen because we set NextProtos above
|
||||
}
|
||||
|
||||
secureKubeConfig.Wrap(func(rt http.RoundTripper) http.RoundTripper {
|
||||
ptlsKubeConfig.Wrap(func(rt http.RoundTripper) http.RoundTripper {
|
||||
defer func() {
|
||||
if err := AssertSecureTransport(rt); err != nil {
|
||||
panic(err) // not sure what the point of this function would be if it failed to make the config secure
|
||||
if err := assertTransport(rt, tlsConfigFunc); err != nil {
|
||||
panic(err) // not sure what the point of this function would be if it failed to make the config use the ptls settings
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -167,23 +169,23 @@ func createSecureKubeConfig(kubeConfig *restclient.Config) (*restclient.Config,
|
||||
}
|
||||
|
||||
// mutate the TLS config into our desired state before it is used
|
||||
ptls.Merge(ptls.Secure, tlsConfig)
|
||||
ptls.Merge(tlsConfigFunc, tlsConfig)
|
||||
|
||||
return rt // return the input transport since we mutated it in-place
|
||||
})
|
||||
|
||||
if err := AssertSecureConfig(secureKubeConfig); err != nil {
|
||||
if err := assertConfig(ptlsKubeConfig, tlsConfigFunc); err != nil {
|
||||
return nil, err // not sure what the point of this function would be if it failed to make the config secure
|
||||
}
|
||||
|
||||
return secureKubeConfig, nil
|
||||
return ptlsKubeConfig, nil
|
||||
}
|
||||
|
||||
// SecureAnonymousClientConfig has the same properties as restclient.AnonymousClientConfig
|
||||
// while still enforcing the secure TLS configuration of the ptls / phttp packages.
|
||||
func SecureAnonymousClientConfig(kubeConfig *restclient.Config) *restclient.Config {
|
||||
kubeConfig = restclient.AnonymousClientConfig(kubeConfig)
|
||||
secureKubeConfig, err := createSecureKubeConfig(kubeConfig)
|
||||
secureKubeConfig, err := createPTLSKubeConfig(kubeConfig, ptls.Secure)
|
||||
if err != nil {
|
||||
panic(err) // should never happen as this would only fail on invalid CA data, which would never work anyway
|
||||
}
|
||||
@@ -194,22 +196,30 @@ func SecureAnonymousClientConfig(kubeConfig *restclient.Config) *restclient.Conf
|
||||
}
|
||||
|
||||
func AssertSecureConfig(kubeConfig *restclient.Config) error {
|
||||
return assertConfig(kubeConfig, ptls.Secure)
|
||||
}
|
||||
|
||||
func assertConfig(kubeConfig *restclient.Config, tlsConfigFunc ptls.ConfigFunc) error {
|
||||
rt, err := restclient.TransportFor(kubeConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to build transport: %w", err)
|
||||
}
|
||||
|
||||
return AssertSecureTransport(rt)
|
||||
return assertTransport(rt, tlsConfigFunc)
|
||||
}
|
||||
|
||||
func AssertSecureTransport(rt http.RoundTripper) error {
|
||||
return assertTransport(rt, ptls.Secure)
|
||||
}
|
||||
|
||||
func assertTransport(rt http.RoundTripper, tlsConfigFunc ptls.ConfigFunc) error {
|
||||
tlsConfig, err := net.TLSClientConfig(rt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get TLS config: %w", err)
|
||||
}
|
||||
|
||||
tlsConfigCopy := tlsConfig.Clone()
|
||||
ptls.Merge(ptls.Secure, tlsConfigCopy) // only mutate the copy
|
||||
ptls.Merge(tlsConfigFunc, tlsConfigCopy) // only mutate the copy
|
||||
|
||||
//nolint:gosec // the empty TLS config here is not used
|
||||
if diff := cmp.Diff(tlsConfigCopy, tlsConfig,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kubeclient
|
||||
@@ -957,7 +957,7 @@ func TestUnwrap(t *testing.T) {
|
||||
|
||||
regularClient := makeClient(t, restConfig, func(_ *rest.Config) {})
|
||||
|
||||
testUnwrap(t, regularClient, serverSubjects)
|
||||
testUnwrap(t, regularClient, serverSubjects, ptls.Secure)
|
||||
})
|
||||
|
||||
t.Run("exec client", func(t *testing.T) {
|
||||
@@ -972,7 +972,7 @@ func TestUnwrap(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
testUnwrap(t, execClient, serverSubjects)
|
||||
testUnwrap(t, execClient, serverSubjects, ptls.Secure)
|
||||
})
|
||||
|
||||
t.Run("oidc client", func(t *testing.T) {
|
||||
@@ -988,90 +988,149 @@ func TestUnwrap(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
testUnwrap(t, oidcClient, serverSubjects)
|
||||
testUnwrap(t, oidcClient, serverSubjects, ptls.Secure)
|
||||
})
|
||||
|
||||
t.Run("regular client with ptls.Default", func(t *testing.T) {
|
||||
t.Parallel() // make sure to run in parallel to confirm that our client-go TLS cache busting works (i.e. assert no data races)
|
||||
|
||||
regularClient := makeClient(t, restConfig, func(_ *rest.Config) {}, WithTLSConfigFunc(ptls.Default))
|
||||
|
||||
testUnwrap(t, regularClient, serverSubjects, ptls.Default)
|
||||
})
|
||||
|
||||
t.Run("exec client with ptls.Default", func(t *testing.T) {
|
||||
t.Parallel() // make sure to run in parallel to confirm that our client-go TLS cache busting works (i.e. assert no data races)
|
||||
|
||||
execClient := makeClient(t, restConfig, func(config *rest.Config) {
|
||||
config.ExecProvider = &clientcmdapi.ExecConfig{
|
||||
Command: "echo",
|
||||
Args: []string{"pandas are awesome"},
|
||||
APIVersion: clientauthenticationv1.SchemeGroupVersion.String(),
|
||||
InteractiveMode: clientcmdapi.NeverExecInteractiveMode,
|
||||
}
|
||||
}, WithTLSConfigFunc(ptls.Default))
|
||||
|
||||
testUnwrap(t, execClient, serverSubjects, ptls.Default)
|
||||
})
|
||||
|
||||
t.Run("oidc client with ptls.Default", func(t *testing.T) {
|
||||
t.Parallel() // make sure to run in parallel to confirm that our client-go TLS cache busting works (i.e. assert no data races)
|
||||
|
||||
oidcClient := makeClient(t, restConfig, func(config *rest.Config) {
|
||||
config.AuthProvider = &clientcmdapi.AuthProviderConfig{
|
||||
Name: "oidc",
|
||||
Config: map[string]string{
|
||||
"idp-issuer-url": "https://pandas.local",
|
||||
"client-id": "walrus",
|
||||
},
|
||||
}
|
||||
}, WithTLSConfigFunc(ptls.Default))
|
||||
|
||||
testUnwrap(t, oidcClient, serverSubjects, ptls.Default)
|
||||
})
|
||||
}
|
||||
|
||||
func testUnwrap(t *testing.T, client *Client, serverSubjects [][]byte) {
|
||||
func testUnwrap(t *testing.T, client *Client, serverSubjects [][]byte, tlsConfigFuncForExpectedValues ptls.ConfigFunc) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rt http.RoundTripper
|
||||
name string
|
||||
rt http.RoundTripper
|
||||
wantConfigFunc ptls.ConfigFunc
|
||||
}{
|
||||
{
|
||||
name: "core v1",
|
||||
rt: extractTransport(client.Kubernetes.CoreV1()),
|
||||
name: "core v1",
|
||||
rt: extractTransport(client.Kubernetes.CoreV1()),
|
||||
wantConfigFunc: tlsConfigFuncForExpectedValues,
|
||||
},
|
||||
{
|
||||
name: "coordination v1",
|
||||
rt: extractTransport(client.Kubernetes.CoordinationV1()),
|
||||
name: "coordination v1",
|
||||
rt: extractTransport(client.Kubernetes.CoordinationV1()),
|
||||
wantConfigFunc: tlsConfigFuncForExpectedValues,
|
||||
},
|
||||
{
|
||||
name: "api registration v1",
|
||||
rt: extractTransport(client.Aggregation.ApiregistrationV1()),
|
||||
name: "api registration v1",
|
||||
rt: extractTransport(client.Aggregation.ApiregistrationV1()),
|
||||
wantConfigFunc: tlsConfigFuncForExpectedValues,
|
||||
},
|
||||
{
|
||||
name: "concierge login",
|
||||
rt: extractTransport(client.PinnipedConcierge.LoginV1alpha1()),
|
||||
name: "concierge login",
|
||||
rt: extractTransport(client.PinnipedConcierge.LoginV1alpha1()),
|
||||
wantConfigFunc: tlsConfigFuncForExpectedValues,
|
||||
},
|
||||
{
|
||||
name: "concierge config",
|
||||
rt: extractTransport(client.PinnipedConcierge.ConfigV1alpha1()),
|
||||
name: "concierge config",
|
||||
rt: extractTransport(client.PinnipedConcierge.ConfigV1alpha1()),
|
||||
wantConfigFunc: tlsConfigFuncForExpectedValues,
|
||||
},
|
||||
{
|
||||
name: "supervisor idp",
|
||||
rt: extractTransport(client.PinnipedSupervisor.IDPV1alpha1()),
|
||||
name: "supervisor idp",
|
||||
rt: extractTransport(client.PinnipedSupervisor.IDPV1alpha1()),
|
||||
wantConfigFunc: tlsConfigFuncForExpectedValues,
|
||||
},
|
||||
{
|
||||
name: "supervisor config",
|
||||
rt: extractTransport(client.PinnipedSupervisor.ConfigV1alpha1()),
|
||||
name: "supervisor config",
|
||||
rt: extractTransport(client.PinnipedSupervisor.ConfigV1alpha1()),
|
||||
wantConfigFunc: tlsConfigFuncForExpectedValues,
|
||||
},
|
||||
{
|
||||
name: "json config",
|
||||
rt: configToTransport(t, client.JSONConfig),
|
||||
name: "json config",
|
||||
rt: configToTransport(t, client.JSONConfig),
|
||||
wantConfigFunc: tlsConfigFuncForExpectedValues,
|
||||
},
|
||||
{
|
||||
name: "proto config",
|
||||
rt: configToTransport(t, client.ProtoConfig),
|
||||
name: "proto config",
|
||||
rt: configToTransport(t, client.ProtoConfig),
|
||||
wantConfigFunc: tlsConfigFuncForExpectedValues,
|
||||
},
|
||||
{
|
||||
name: "anonymous json config",
|
||||
rt: configToTransport(t, SecureAnonymousClientConfig(client.JSONConfig)),
|
||||
name: "anonymous json config",
|
||||
rt: configToTransport(t, SecureAnonymousClientConfig(client.JSONConfig)),
|
||||
wantConfigFunc: ptls.Secure, // SecureAnonymousClientConfig is always ptls.Secure
|
||||
},
|
||||
{
|
||||
name: "anonymous proto config",
|
||||
rt: configToTransport(t, SecureAnonymousClientConfig(client.ProtoConfig)),
|
||||
name: "anonymous proto config",
|
||||
rt: configToTransport(t, SecureAnonymousClientConfig(client.ProtoConfig)),
|
||||
wantConfigFunc: ptls.Secure, // SecureAnonymousClientConfig is always ptls.Secure
|
||||
},
|
||||
{
|
||||
name: "json config - no cache",
|
||||
rt: configToTransport(t, bustTLSCache(client.JSONConfig)),
|
||||
name: "json config - no cache",
|
||||
rt: configToTransport(t, bustTLSCache(client.JSONConfig)),
|
||||
wantConfigFunc: tlsConfigFuncForExpectedValues,
|
||||
},
|
||||
{
|
||||
name: "proto config - no cache",
|
||||
rt: configToTransport(t, bustTLSCache(client.ProtoConfig)),
|
||||
name: "proto config - no cache",
|
||||
rt: configToTransport(t, bustTLSCache(client.ProtoConfig)),
|
||||
wantConfigFunc: tlsConfigFuncForExpectedValues,
|
||||
},
|
||||
{
|
||||
name: "anonymous json config - no cache, inner bust",
|
||||
rt: configToTransport(t, SecureAnonymousClientConfig(bustTLSCache(client.JSONConfig))),
|
||||
name: "anonymous json config - no cache, inner bust",
|
||||
rt: configToTransport(t, SecureAnonymousClientConfig(bustTLSCache(client.JSONConfig))),
|
||||
wantConfigFunc: ptls.Secure, // SecureAnonymousClientConfig is always ptls.Secure
|
||||
},
|
||||
{
|
||||
name: "anonymous proto config - no cache, inner bust",
|
||||
rt: configToTransport(t, SecureAnonymousClientConfig(bustTLSCache(client.ProtoConfig))),
|
||||
name: "anonymous proto config - no cache, inner bust",
|
||||
rt: configToTransport(t, SecureAnonymousClientConfig(bustTLSCache(client.ProtoConfig))),
|
||||
wantConfigFunc: ptls.Secure, // SecureAnonymousClientConfig is always ptls.Secure
|
||||
},
|
||||
{
|
||||
name: "anonymous json config - no cache, double bust",
|
||||
rt: configToTransport(t, bustTLSCache(SecureAnonymousClientConfig(bustTLSCache(client.JSONConfig)))),
|
||||
name: "anonymous json config - no cache, double bust",
|
||||
rt: configToTransport(t, bustTLSCache(SecureAnonymousClientConfig(bustTLSCache(client.JSONConfig)))),
|
||||
wantConfigFunc: ptls.Secure, // SecureAnonymousClientConfig is always ptls.Secure
|
||||
},
|
||||
{
|
||||
name: "anonymous proto config - no cache, double bust",
|
||||
rt: configToTransport(t, bustTLSCache(SecureAnonymousClientConfig(bustTLSCache(client.ProtoConfig)))),
|
||||
name: "anonymous proto config - no cache, double bust",
|
||||
rt: configToTransport(t, bustTLSCache(SecureAnonymousClientConfig(bustTLSCache(client.ProtoConfig)))),
|
||||
wantConfigFunc: ptls.Secure, // SecureAnonymousClientConfig is always ptls.Secure
|
||||
},
|
||||
{
|
||||
name: "anonymous json config - no cache, outer bust",
|
||||
rt: configToTransport(t, bustTLSCache(SecureAnonymousClientConfig(client.JSONConfig))),
|
||||
name: "anonymous json config - no cache, outer bust",
|
||||
rt: configToTransport(t, bustTLSCache(SecureAnonymousClientConfig(client.JSONConfig))),
|
||||
wantConfigFunc: ptls.Secure, // SecureAnonymousClientConfig is always ptls.Secure
|
||||
},
|
||||
{
|
||||
name: "anonymous proto config - no cache, outer bust",
|
||||
rt: configToTransport(t, bustTLSCache(SecureAnonymousClientConfig(client.ProtoConfig))),
|
||||
name: "anonymous proto config - no cache, outer bust",
|
||||
rt: configToTransport(t, bustTLSCache(SecureAnonymousClientConfig(client.ProtoConfig))),
|
||||
wantConfigFunc: ptls.Secure, // SecureAnonymousClientConfig is always ptls.Secure
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -1083,11 +1142,11 @@ func testUnwrap(t *testing.T, client *Client, serverSubjects [][]byte) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, tlsConfig)
|
||||
|
||||
secureTLSConfig := ptls.Secure(nil)
|
||||
ptlsConfig := tt.wantConfigFunc(nil)
|
||||
|
||||
require.Equal(t, secureTLSConfig.MinVersion, tlsConfig.MinVersion)
|
||||
require.Equal(t, secureTLSConfig.CipherSuites, tlsConfig.CipherSuites)
|
||||
require.Equal(t, secureTLSConfig.NextProtos, tlsConfig.NextProtos)
|
||||
require.Equal(t, ptlsConfig.MinVersion, tlsConfig.MinVersion)
|
||||
require.Equal(t, ptlsConfig.CipherSuites, tlsConfig.CipherSuites)
|
||||
require.Equal(t, ptlsConfig.NextProtos, tlsConfig.NextProtos)
|
||||
|
||||
// x509.CertPool has some embedded functions that make it hard to compare so just look at the subjects
|
||||
//nolint:staticcheck // since we're not using .Subjects() to access the system pool
|
||||
@@ -1120,14 +1179,14 @@ func bustTLSCache(config *rest.Config) *rest.Config {
|
||||
return c
|
||||
}
|
||||
|
||||
func makeClient(t *testing.T, restConfig *rest.Config, f func(*rest.Config)) *Client {
|
||||
func makeClient(t *testing.T, restConfig *rest.Config, f func(*rest.Config), opts ...Option) *Client {
|
||||
t.Helper()
|
||||
|
||||
restConfig = rest.CopyConfig(restConfig)
|
||||
|
||||
f(restConfig)
|
||||
|
||||
client, err := New(WithConfig(restConfig))
|
||||
client, err := New(append([]Option{WithConfig(restConfig)}, opts...)...)
|
||||
require.NoError(t, err)
|
||||
|
||||
return client
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kubeclient
|
||||
@@ -6,12 +6,15 @@ package kubeclient
|
||||
import (
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/transport"
|
||||
|
||||
"go.pinniped.dev/internal/crypto/ptls"
|
||||
)
|
||||
|
||||
type Option func(*clientConfig)
|
||||
|
||||
type clientConfig struct {
|
||||
config *restclient.Config
|
||||
tlsConfigFunc ptls.ConfigFunc
|
||||
middlewares []Middleware
|
||||
transportWrapper transport.WrapperFunc
|
||||
}
|
||||
@@ -22,6 +25,15 @@ func WithConfig(config *restclient.Config) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithTLSConfigFunc will cause the client to use the provided configuration from the ptls package when the
|
||||
// client makes requests. For example, pass ptls.Default or ptls.Secure as the argument. When this Option
|
||||
// is not used, the client will default to using ptls.Secure.
|
||||
func WithTLSConfigFunc(tlsConfigFunc ptls.ConfigFunc) Option {
|
||||
return func(c *clientConfig) {
|
||||
c.tlsConfigFunc = tlsConfigFunc
|
||||
}
|
||||
}
|
||||
|
||||
func WithMiddleware(middleware Middleware) Option {
|
||||
return func(c *clientConfig) {
|
||||
if middleware == nil {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package phttp
|
||||
@@ -83,13 +83,13 @@ func TestClient(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var sawRequest bool
|
||||
server := tlsserver.TLSTestServer(t, http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
||||
server, serverCA := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
||||
tlsserver.AssertTLS(t, r, tt.configFunc)
|
||||
assertUserAgent(t, r)
|
||||
sawRequest = true
|
||||
}), tlsserver.RecordTLSHello)
|
||||
|
||||
rootCAs, err := cert.NewPoolFromBytes(tlsserver.TLSTestServerCA(server))
|
||||
rootCAs, err := cert.NewPoolFromBytes(serverCA)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := tt.clientFunc(rootCAs)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*
|
||||
@@ -58,7 +58,7 @@ func Start(t *testing.T, resources map[string]runtime.Object) (*httptest.Server,
|
||||
resources = make(map[string]runtime.Object)
|
||||
}
|
||||
|
||||
server := tlsserver.TLSTestServer(t, httperr.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||
server, serverCA := tlsserver.TestServerIPv4(t, httperr.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||
tlsserver.AssertTLS(t, r, ptls.Secure)
|
||||
|
||||
obj, err := decodeObj(r)
|
||||
@@ -84,7 +84,7 @@ func Start(t *testing.T, resources map[string]runtime.Object) (*httptest.Server,
|
||||
restConfig := &restclient.Config{
|
||||
Host: server.URL,
|
||||
TLSClientConfig: restclient.TLSClientConfig{
|
||||
CAData: tlsserver.TLSTestServerCA(server),
|
||||
CAData: serverCA,
|
||||
},
|
||||
}
|
||||
return server, restConfig
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.pinniped.dev/internal/crypto/ptls"
|
||||
"go.pinniped.dev/internal/testutil/tlsserver"
|
||||
)
|
||||
|
||||
// TLSTestServer starts a test server listening on a local port using a test CA. It returns the PEM CA bundle and the
|
||||
// URL of the listening server. The lifetime of the server is bound to the provided *testing.T.
|
||||
func TLSTestServer(t *testing.T, handler http.HandlerFunc) (caBundlePEM, url string) {
|
||||
t.Helper()
|
||||
|
||||
server := tlsserver.TLSTestServer(t, handler, nil)
|
||||
|
||||
return string(tlsserver.TLSTestServerCA(server)), server.URL
|
||||
}
|
||||
|
||||
func TLSTestServerWithCert(t *testing.T, handler http.HandlerFunc, certificate *tls.Certificate) (url string) {
|
||||
t.Helper()
|
||||
|
||||
c := ptls.Default(nil) // mimic API server config
|
||||
c.Certificates = []tls.Certificate{*certificate}
|
||||
|
||||
server := http.Server{
|
||||
TLSConfig: c,
|
||||
Handler: handler,
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
|
||||
serverShutdownChan := make(chan error)
|
||||
go func() {
|
||||
// Empty certFile and keyFile will use certs from Server.TLSConfig.
|
||||
serverShutdownChan <- server.ServeTLS(l, "", "")
|
||||
}()
|
||||
|
||||
t.Cleanup(func() {
|
||||
_ = server.Close()
|
||||
serveErr := <-serverShutdownChan
|
||||
if !errors.Is(serveErr, http.ErrServerClosed) {
|
||||
t.Log("Got an unexpected error while starting the fake http server!")
|
||||
require.NoError(t, serveErr)
|
||||
}
|
||||
})
|
||||
|
||||
return l.Addr().String()
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -30,7 +32,8 @@ const (
|
||||
helloKey
|
||||
)
|
||||
|
||||
func TLSTestIPv6Server(t *testing.T, handler http.Handler, f func(*httptest.Server)) *httptest.Server {
|
||||
// TestServerIPv6 returns a TLS-required server that listens at an IPv6 loopback.
|
||||
func TestServerIPv6(t *testing.T, handler http.Handler, f func(*httptest.Server)) (*httptest.Server, []byte) {
|
||||
t.Helper()
|
||||
|
||||
listener, err := net.Listen("tcp6", "[::1]:0")
|
||||
@@ -40,18 +43,18 @@ func TLSTestIPv6Server(t *testing.T, handler http.Handler, f func(*httptest.Serv
|
||||
Listener: listener,
|
||||
Config: &http.Server{Handler: handler}, //nolint:gosec //ReadHeaderTimeout is not needed for a localhost listener
|
||||
}
|
||||
|
||||
return tlsTestServer(t, server, f)
|
||||
return testServer(t, server, f)
|
||||
}
|
||||
|
||||
func TLSTestServer(t *testing.T, handler http.Handler, f func(*httptest.Server)) *httptest.Server {
|
||||
// TestServerIPv4 returns a TLS-required server that listens at an IPv4 loopback.
|
||||
func TestServerIPv4(t *testing.T, handler http.Handler, f func(*httptest.Server)) (*httptest.Server, []byte) {
|
||||
t.Helper()
|
||||
|
||||
server := httptest.NewUnstartedServer(handler)
|
||||
return tlsTestServer(t, server, f)
|
||||
return testServer(t, server, f)
|
||||
}
|
||||
|
||||
func tlsTestServer(t *testing.T, server *httptest.Server, f func(*httptest.Server)) *httptest.Server {
|
||||
func testServer(t *testing.T, server *httptest.Server, f func(*httptest.Server)) (*httptest.Server, []byte) {
|
||||
t.Helper()
|
||||
|
||||
server.TLS = ptls.Default(nil) // mimic API server config
|
||||
@@ -60,16 +63,48 @@ func tlsTestServer(t *testing.T, server *httptest.Server, f func(*httptest.Serve
|
||||
}
|
||||
server.StartTLS()
|
||||
t.Cleanup(server.Close)
|
||||
return server
|
||||
}
|
||||
|
||||
func TLSTestServerCA(server *httptest.Server) []byte {
|
||||
return pem.EncodeToMemory(&pem.Block{
|
||||
return server, pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: server.Certificate().Raw,
|
||||
})
|
||||
}
|
||||
|
||||
func TLSTestServerWithCert(t *testing.T, handler http.HandlerFunc, certificate *tls.Certificate) (url string) {
|
||||
t.Helper()
|
||||
|
||||
c := ptls.Default(nil) // mimic API server config
|
||||
c.Certificates = []tls.Certificate{*certificate}
|
||||
|
||||
server := http.Server{
|
||||
TLSConfig: c,
|
||||
Handler: handler,
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
|
||||
serverShutdownChan := make(chan error)
|
||||
go func() {
|
||||
// Empty certFile and keyFile will use certs from Server.TLSConfig.
|
||||
serverShutdownChan <- server.ServeTLS(l, "", "")
|
||||
}()
|
||||
|
||||
t.Cleanup(func() {
|
||||
_ = server.Close()
|
||||
serveErr := <-serverShutdownChan
|
||||
if !errors.Is(serveErr, http.ErrServerClosed) {
|
||||
t.Log("Got an unexpected error while starting the fake http server!")
|
||||
require.NoError(t, serveErr)
|
||||
}
|
||||
})
|
||||
|
||||
return l.Addr().String()
|
||||
}
|
||||
|
||||
// RecordTLSHello configures the server to record client TLS negotiation info onto each incoming request,
|
||||
// so that the details of the client's TLS settings can be asserted upon during request handling
|
||||
// using AssertTLS.
|
||||
func RecordTLSHello(server *httptest.Server) {
|
||||
server.Config.ConnContext = func(ctx context.Context, _ net.Conn) context.Context {
|
||||
return context.WithValue(ctx, mapKey, &sync.Map{})
|
||||
@@ -87,6 +122,29 @@ func RecordTLSHello(server *httptest.Server) {
|
||||
}
|
||||
}
|
||||
|
||||
// AssertEveryTLSHello can be used to make assertions about the client's TLS configuration
|
||||
// when a test expects the server to be dialed by the client, but does not expect that http
|
||||
// requests will be performed (and thus assertions cannot be made during request handling).
|
||||
// For example, a test could use this when the production code is only going to perform a
|
||||
// tls.Dial to test the connection to the server, without making any actual requests.
|
||||
func AssertEveryTLSHello(t *testing.T, server *httptest.Server, clientTLSConfigFunc ptls.ConfigFunc) {
|
||||
server.TLS.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||
t.Helper()
|
||||
|
||||
// We don't know yet at this point if the request will be an upgrade request, so as a shortcut,
|
||||
// we won't assert on that. When that a concern, use AssertTLS in the request handler instead.
|
||||
assertionsPassed := assertTLSOnHello(t, info, false, clientTLSConfigFunc)
|
||||
|
||||
if !assertionsPassed {
|
||||
t.Errorf("insecure client TLS hello detected during TLS negotiation")
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// AssertTLS makes assertions on a particular incoming http request, assuming that the server
|
||||
// was configured using RecordTLSHello.
|
||||
func AssertTLS(t *testing.T, r *http.Request, clientTLSConfigFunc ptls.ConfigFunc) {
|
||||
t.Helper()
|
||||
|
||||
@@ -99,6 +157,16 @@ func AssertTLS(t *testing.T, r *http.Request, clientTLSConfigFunc ptls.ConfigFun
|
||||
actualClientHello, ok := h.(*tls.ClientHelloInfo)
|
||||
require.True(t, ok)
|
||||
|
||||
assertionsPassed := assertTLSOnHello(t, actualClientHello, httpstream.IsUpgradeRequest(r), clientTLSConfigFunc)
|
||||
|
||||
if !assertionsPassed {
|
||||
t.Errorf("insecure TLS detected for %q %q %q upgrade=%v", r.Proto, r.Method, r.URL.String(), httpstream.IsUpgradeRequest(r))
|
||||
}
|
||||
}
|
||||
|
||||
func assertTLSOnHello(t *testing.T, actualClientHello *tls.ClientHelloInfo, isUpgradeRequest bool, clientTLSConfigFunc ptls.ConfigFunc) bool {
|
||||
t.Helper()
|
||||
|
||||
clientTLSConfig := clientTLSConfigFunc(nil)
|
||||
|
||||
var wantClientSupportedVersions []uint16
|
||||
@@ -122,7 +190,7 @@ func AssertTLS(t *testing.T, r *http.Request, clientTLSConfigFunc ptls.ConfigFun
|
||||
}
|
||||
|
||||
wantClientProtos := clientTLSConfig.NextProtos
|
||||
if httpstream.IsUpgradeRequest(r) {
|
||||
if isUpgradeRequest {
|
||||
wantClientProtos = clientTLSConfig.NextProtos[1:]
|
||||
}
|
||||
|
||||
@@ -131,10 +199,7 @@ func AssertTLS(t *testing.T, r *http.Request, clientTLSConfigFunc ptls.ConfigFun
|
||||
ok2 := assert.Equal(t, cipherSuiteIDsToStrings(wantClientSupportedCiphers), cipherSuiteIDsToStrings(actualClientHello.CipherSuites))
|
||||
ok3 := assert.Equal(t, wantClientProtos, actualClientHello.SupportedProtos)
|
||||
|
||||
if all := ok1 && ok2 && ok3; !all {
|
||||
t.Errorf("insecure TLS detected for %q %q %q upgrade=%v wantClientSupportedVersions=%v wantClientSupportedCiphers=%v wantClientProtos=%v",
|
||||
r.Proto, r.Method, r.URL.String(), httpstream.IsUpgradeRequest(r), ok1, ok2, ok3)
|
||||
}
|
||||
return ok1 && ok2 && ok3
|
||||
}
|
||||
|
||||
// appendIfNotAlreadyIncluded only adds the newItems to the list if they are not already included
|
||||
|
||||
@@ -326,7 +326,7 @@ func (p *Provider) dialTLS(ctx context.Context, addr endpointaddr.HostPort) (Con
|
||||
}
|
||||
|
||||
conn := ldap.NewConn(c, true)
|
||||
conn.Start()
|
||||
conn.Start() //nolint:staticcheck // will need a different approach soon
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
@@ -348,7 +348,7 @@ func (p *Provider) dialStartTLS(ctx context.Context, addr endpointaddr.HostPort)
|
||||
}
|
||||
|
||||
conn := ldap.NewConn(c, false)
|
||||
conn.Start()
|
||||
conn.Start() //nolint:staticcheck // will need a different approach soon
|
||||
err = conn.StartTLS(tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -2363,7 +2363,7 @@ func TestGetURL(t *testing.T) {
|
||||
|
||||
// Testing of host parsing, TLS negotiation, and CA bundle, etc. for the production code's dialer.
|
||||
func TestRealTLSDialing(t *testing.T) {
|
||||
testServer := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
|
||||
testServer, testServerCA := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}),
|
||||
func(server *httptest.Server) {
|
||||
tlsserver.RecordTLSHello(server)
|
||||
recordFunc := server.TLS.GetConfigForClient
|
||||
@@ -2378,14 +2378,13 @@ func TestRealTLSDialing(t *testing.T) {
|
||||
parsedURL, err := url.Parse(testServer.URL)
|
||||
require.NoError(t, err)
|
||||
testServerHostAndPort := parsedURL.Host
|
||||
testServerCABundle := tlsserver.TLSTestServerCA(testServer)
|
||||
|
||||
caForTestServerWithBadCertName, err := certauthority.New("Test CA", time.Hour)
|
||||
require.NoError(t, err)
|
||||
wrongIP := net.ParseIP("10.2.3.4")
|
||||
cert, err := caForTestServerWithBadCertName.IssueServerCert([]string{"wrong-dns-name"}, []net.IP{wrongIP}, time.Hour)
|
||||
require.NoError(t, err)
|
||||
testServerWithBadCertNameAddr := testutil.TLSTestServerWithCert(t, func(w http.ResponseWriter, r *http.Request) {}, cert)
|
||||
testServerWithBadCertNameAddr := tlsserver.TLSTestServerWithCert(t, func(w http.ResponseWriter, r *http.Request) {}, cert)
|
||||
|
||||
unusedPortGrabbingListener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
@@ -2406,7 +2405,7 @@ func TestRealTLSDialing(t *testing.T) {
|
||||
{
|
||||
name: "happy path",
|
||||
host: testServerHostAndPort,
|
||||
caBundle: testServerCABundle,
|
||||
caBundle: testServerCA,
|
||||
connProto: TLS,
|
||||
context: context.Background(),
|
||||
},
|
||||
@@ -2437,7 +2436,7 @@ func TestRealTLSDialing(t *testing.T) {
|
||||
{
|
||||
name: "invalid host with TLS",
|
||||
host: "this:is:not:a:valid:hostname",
|
||||
caBundle: testServerCABundle,
|
||||
caBundle: testServerCA,
|
||||
connProto: TLS,
|
||||
context: context.Background(),
|
||||
wantError: testutil.WantExactErrorString(`LDAP Result Code 200 "Network Error": host "this:is:not:a:valid:hostname" is not a valid hostname or IP address`),
|
||||
@@ -2445,7 +2444,7 @@ func TestRealTLSDialing(t *testing.T) {
|
||||
{
|
||||
name: "invalid host with StartTLS",
|
||||
host: "this:is:not:a:valid:hostname",
|
||||
caBundle: testServerCABundle,
|
||||
caBundle: testServerCA,
|
||||
connProto: StartTLS,
|
||||
context: context.Background(),
|
||||
wantError: testutil.WantExactErrorString(`LDAP Result Code 200 "Network Error": host "this:is:not:a:valid:hostname" is not a valid hostname or IP address`),
|
||||
@@ -2462,7 +2461,7 @@ func TestRealTLSDialing(t *testing.T) {
|
||||
name: "cannot connect to host",
|
||||
// This is assuming that this port was not reclaimed by another app since the test setup ran. Seems safe enough.
|
||||
host: recentlyClaimedHostAndPort,
|
||||
caBundle: testServerCABundle,
|
||||
caBundle: testServerCA,
|
||||
connProto: TLS,
|
||||
context: context.Background(),
|
||||
wantError: testutil.WantSprintfErrorString(`LDAP Result Code 200 "Network Error": dial tcp %s: connect: connection refused`, recentlyClaimedHostAndPort),
|
||||
@@ -2470,7 +2469,7 @@ func TestRealTLSDialing(t *testing.T) {
|
||||
{
|
||||
name: "pays attention to the passed context",
|
||||
host: testServerHostAndPort,
|
||||
caBundle: testServerCABundle,
|
||||
caBundle: testServerCA,
|
||||
connProto: TLS,
|
||||
context: alreadyCancelledContext,
|
||||
wantError: testutil.WantSprintfErrorString(`LDAP Result Code 200 "Network Error": dial tcp %s: operation was canceled`, testServerHostAndPort),
|
||||
@@ -2478,7 +2477,7 @@ func TestRealTLSDialing(t *testing.T) {
|
||||
{
|
||||
name: "unsupported connection protocol",
|
||||
host: testServerHostAndPort,
|
||||
caBundle: testServerCABundle,
|
||||
caBundle: testServerCA,
|
||||
connProto: "bad usage of this type",
|
||||
context: alreadyCancelledContext,
|
||||
wantError: testutil.WantExactErrorString(`LDAP Result Code 200 "Network Error": did not specify valid ConnectionProtocol`),
|
||||
|
||||
@@ -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 conciergeclient
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
|
||||
loginv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/login/v1alpha1"
|
||||
"go.pinniped.dev/internal/certauthority"
|
||||
"go.pinniped.dev/internal/testutil"
|
||||
"go.pinniped.dev/internal/testutil/tlsserver"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
@@ -163,12 +163,12 @@ func TestExchangeToken(t *testing.T) {
|
||||
t.Run("server error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Start a test server that returns only 500 errors.
|
||||
caBundle, endpoint := testutil.TLSTestServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
server, serverCA := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte("some server error"))
|
||||
})
|
||||
}), nil)
|
||||
|
||||
client, err := New(WithEndpoint(endpoint), WithCABundle(caBundle), WithAuthenticator("jwt", "test-authenticator"))
|
||||
client, err := New(WithEndpoint(server.URL), WithCABundle(string(serverCA)), WithAuthenticator("jwt", "test-authenticator"))
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := client.ExchangeToken(ctx, "test-token")
|
||||
@@ -180,15 +180,15 @@ func TestExchangeToken(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Start a test server that returns success but with an error message
|
||||
errorMessage := "some login failure"
|
||||
caBundle, endpoint := testutil.TLSTestServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
server, serverCA := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&loginv1alpha1.TokenCredentialRequest{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "login.concierge.pinniped.dev/v1alpha1", Kind: "TokenCredentialRequest"},
|
||||
Status: loginv1alpha1.TokenCredentialRequestStatus{Message: &errorMessage},
|
||||
})
|
||||
})
|
||||
}), nil)
|
||||
|
||||
client, err := New(WithEndpoint(endpoint), WithCABundle(caBundle), WithAuthenticator("jwt", "test-authenticator"))
|
||||
client, err := New(WithEndpoint(server.URL), WithCABundle(string(serverCA)), WithAuthenticator("jwt", "test-authenticator"))
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := client.ExchangeToken(ctx, "test-token")
|
||||
@@ -199,14 +199,14 @@ func TestExchangeToken(t *testing.T) {
|
||||
t.Run("login failure unknown error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Start a test server that returns without any error message but also without valid credentials
|
||||
caBundle, endpoint := testutil.TLSTestServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
server, serverCA := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(&loginv1alpha1.TokenCredentialRequest{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "login.concierge.pinniped.dev/v1alpha1", Kind: "TokenCredentialRequest"},
|
||||
})
|
||||
})
|
||||
}), nil)
|
||||
|
||||
client, err := New(WithEndpoint(endpoint), WithCABundle(caBundle), WithAuthenticator("jwt", "test-authenticator"))
|
||||
client, err := New(WithEndpoint(server.URL), WithCABundle(string(serverCA)), WithAuthenticator("jwt", "test-authenticator"))
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := client.ExchangeToken(ctx, "test-token")
|
||||
@@ -219,7 +219,7 @@ func TestExchangeToken(t *testing.T) {
|
||||
expires := metav1.NewTime(time.Now().Truncate(time.Second))
|
||||
|
||||
// Start a test server that returns successfully and asserts various properties of the request.
|
||||
caBundle, endpoint := testutil.TLSTestServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
server, serverCA := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, http.MethodPost, r.Method)
|
||||
require.Equal(t, "/apis/login.concierge.pinniped.dev/v1alpha1/tokencredentialrequests", r.URL.Path)
|
||||
require.Equal(t, "application/json", r.Header.Get("content-type"))
|
||||
@@ -257,9 +257,9 @@ func TestExchangeToken(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
}), nil)
|
||||
|
||||
client, err := New(WithEndpoint(endpoint), WithCABundle(caBundle), WithAuthenticator("webhook", "test-webhook"))
|
||||
client, err := New(WithEndpoint(server.URL), WithCABundle(string(serverCA)), WithAuthenticator("webhook", "test-webhook"))
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := client.ExchangeToken(ctx, "test-token")
|
||||
|
||||
@@ -66,10 +66,9 @@ func (m *mockSessionCache) PutToken(key SessionCacheKey, token *oidctypes.Token)
|
||||
m.sawPutTokens = append(m.sawPutTokens, token)
|
||||
}
|
||||
|
||||
func newClientForServer(server *httptest.Server) *http.Client {
|
||||
func buildHTTPClientForPEM(pemData []byte) *http.Client {
|
||||
pool := x509.NewCertPool()
|
||||
caPEMData := tlsserver.TLSTestServerCA(server)
|
||||
pool.AppendCertsFromPEM(caPEMData)
|
||||
pool.AppendCertsFromPEM(pemData)
|
||||
return phttp.Default(pool)
|
||||
}
|
||||
|
||||
@@ -91,13 +90,13 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
}
|
||||
|
||||
// Start a test server that returns 500 errors.
|
||||
errorServer := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
errorServer, errorServerCA := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "some discovery error", http.StatusInternalServerError)
|
||||
}), nil)
|
||||
|
||||
// Start a test server that returns discovery data with a broken response_modes_supported value.
|
||||
brokenResponseModeMux := http.NewServeMux()
|
||||
brokenResponseModeServer := tlsserver.TLSTestServer(t, brokenResponseModeMux, nil)
|
||||
brokenResponseModeServer, brokenResponseModeServerCA := tlsserver.TestServerIPv4(t, brokenResponseModeMux, nil)
|
||||
brokenResponseModeMux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
type providerJSON struct {
|
||||
@@ -116,7 +115,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
|
||||
// Start a test server that returns discovery data with a broken token URL.
|
||||
brokenTokenURLMux := http.NewServeMux()
|
||||
brokenTokenURLServer := tlsserver.TLSTestServer(t, brokenTokenURLMux, nil)
|
||||
brokenTokenURLServer, brokenTokenURLServerCA := tlsserver.TestServerIPv4(t, brokenTokenURLMux, nil)
|
||||
brokenTokenURLMux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
type providerJSON struct {
|
||||
@@ -135,7 +134,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
|
||||
// Start a test server that returns discovery data with an insecure token URL.
|
||||
insecureTokenURLMux := http.NewServeMux()
|
||||
insecureTokenURLServer := tlsserver.TLSTestServer(t, insecureTokenURLMux, nil)
|
||||
insecureTokenURLServer, insecureTokenURLServerCA := tlsserver.TestServerIPv4(t, insecureTokenURLMux, nil)
|
||||
insecureTokenURLMux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
type providerJSON struct {
|
||||
@@ -154,7 +153,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
|
||||
// Start a test server that returns discovery data with a broken authorize URL.
|
||||
brokenAuthURLMux := http.NewServeMux()
|
||||
brokenAuthURLServer := tlsserver.TLSTestServer(t, brokenAuthURLMux, nil)
|
||||
brokenAuthURLServer, brokenAuthURLServerCA := tlsserver.TestServerIPv4(t, brokenAuthURLMux, nil)
|
||||
brokenAuthURLMux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
type providerJSON struct {
|
||||
@@ -173,7 +172,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
|
||||
// Start a test server that returns discovery data with an insecure authorize URL.
|
||||
insecureAuthURLMux := http.NewServeMux()
|
||||
insecureAuthURLServer := tlsserver.TLSTestServer(t, insecureAuthURLMux, nil)
|
||||
insecureAuthURLServer, insecureAuthURLServerCA := tlsserver.TestServerIPv4(t, insecureAuthURLMux, nil)
|
||||
insecureAuthURLMux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("content-type", "application/json")
|
||||
type providerJSON struct {
|
||||
@@ -298,13 +297,13 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
|
||||
// Start a test server that returns a real discovery document and answers refresh requests.
|
||||
providerMux := http.NewServeMux()
|
||||
successServer := tlsserver.TLSTestServer(t, providerMux, nil)
|
||||
successServer, successServerCA := tlsserver.TestServerIPv4(t, providerMux, nil)
|
||||
providerMux.HandleFunc("/.well-known/openid-configuration", discoveryHandler(successServer, nil))
|
||||
providerMux.HandleFunc("/token", tokenHandler)
|
||||
|
||||
// Start a test server that returns a real discovery document and answers refresh requests, _and_ supports form_mode=post.
|
||||
formPostProviderMux := http.NewServeMux()
|
||||
formPostSuccessServer := tlsserver.TLSTestServer(t, formPostProviderMux, nil)
|
||||
formPostSuccessServer, formPostSuccessServerCA := tlsserver.TestServerIPv4(t, formPostProviderMux, nil)
|
||||
formPostProviderMux.HandleFunc("/.well-known/openid-configuration", discoveryHandler(formPostSuccessServer, []string{"query", "form_post"}))
|
||||
formPostProviderMux.HandleFunc("/token", tokenHandler)
|
||||
|
||||
@@ -339,7 +338,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithCLISendingCredentials()(h))
|
||||
require.NoError(t, WithUpstreamIdentityProvider("some-upstream-name", "ldap")(h))
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
|
||||
require.NoError(t, WithClient(&http.Client{
|
||||
Transport: roundtripper.Func(func(req *http.Request) (*http.Response, error) {
|
||||
@@ -436,7 +435,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
clientID: "test-client-id",
|
||||
opt: func(t *testing.T) Option {
|
||||
return func(h *handlerState) error {
|
||||
require.NoError(t, WithClient(newClientForServer(errorServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(errorServerCA))(h))
|
||||
cache := &mockSessionCache{t: t, getReturnsToken: &oidctypes.Token{
|
||||
IDToken: &oidctypes.IDToken{
|
||||
Token: "test-id-token",
|
||||
@@ -485,7 +484,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
name: "discovery failure due to 500 error",
|
||||
opt: func(t *testing.T) Option {
|
||||
return func(h *handlerState) error {
|
||||
require.NoError(t, WithClient(newClientForServer(errorServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(errorServerCA))(h))
|
||||
return nil
|
||||
}
|
||||
},
|
||||
@@ -497,7 +496,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
name: "discovery failure due to invalid response_modes_supported",
|
||||
opt: func(t *testing.T) Option {
|
||||
return func(h *handlerState) error {
|
||||
require.NoError(t, WithClient(newClientForServer(brokenResponseModeServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(brokenResponseModeServerCA))(h))
|
||||
return nil
|
||||
}
|
||||
},
|
||||
@@ -511,7 +510,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
clientID: "test-client-id",
|
||||
opt: func(t *testing.T) Option {
|
||||
return func(h *handlerState) error {
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
|
||||
h.getProvider = func(config *oauth2.Config, provider *oidc.Provider, client *http.Client) upstreamprovider.UpstreamOIDCIdentityProviderI {
|
||||
mock := mockUpstream(t)
|
||||
@@ -562,7 +561,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
clientID: "test-client-id",
|
||||
opt: func(t *testing.T) Option {
|
||||
return func(h *handlerState) error {
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
|
||||
h.getProvider = func(config *oauth2.Config, provider *oidc.Provider, client *http.Client) upstreamprovider.UpstreamOIDCIdentityProviderI {
|
||||
mock := mockUpstream(t)
|
||||
@@ -605,7 +604,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
clientID: "not-the-test-client-id",
|
||||
opt: func(t *testing.T) Option {
|
||||
return func(h *handlerState) error {
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
|
||||
cache := &mockSessionCache{t: t, getReturnsToken: &oidctypes.Token{
|
||||
IDToken: &oidctypes.IDToken{
|
||||
@@ -638,7 +637,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
name: "issuer has invalid token URL",
|
||||
opt: func(t *testing.T) Option {
|
||||
return func(h *handlerState) error {
|
||||
require.NoError(t, WithClient(newClientForServer(brokenTokenURLServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(brokenTokenURLServerCA))(h))
|
||||
return nil
|
||||
}
|
||||
},
|
||||
@@ -650,7 +649,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
name: "issuer has insecure token URL",
|
||||
opt: func(t *testing.T) Option {
|
||||
return func(h *handlerState) error {
|
||||
require.NoError(t, WithClient(newClientForServer(insecureTokenURLServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(insecureTokenURLServerCA))(h))
|
||||
return nil
|
||||
}
|
||||
},
|
||||
@@ -662,7 +661,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
name: "issuer has invalid authorize URL",
|
||||
opt: func(t *testing.T) Option {
|
||||
return func(h *handlerState) error {
|
||||
require.NoError(t, WithClient(newClientForServer(brokenAuthURLServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(brokenAuthURLServerCA))(h))
|
||||
return nil
|
||||
}
|
||||
},
|
||||
@@ -674,7 +673,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
name: "issuer has insecure authorize URL",
|
||||
opt: func(t *testing.T) Option {
|
||||
return func(h *handlerState) error {
|
||||
require.NoError(t, WithClient(newClientForServer(insecureAuthURLServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(insecureAuthURLServerCA))(h))
|
||||
return nil
|
||||
}
|
||||
},
|
||||
@@ -686,7 +685,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
name: "listen failure and non-tty stdin",
|
||||
opt: func(t *testing.T) Option {
|
||||
return func(h *handlerState) error {
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
h.listen = func(net string, addr string) (net.Listener, error) {
|
||||
assert.Equal(t, "tcp", net)
|
||||
assert.Equal(t, "localhost:0", addr)
|
||||
@@ -711,7 +710,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
h.generatePKCE = func() (pkce.Code, error) { return "test-pkce", nil }
|
||||
h.generateNonce = func() (nonce.Nonce, error) { return "test-nonce", nil }
|
||||
h.stdinIsTTY = func() bool { return true }
|
||||
require.NoError(t, WithClient(newClientForServer(formPostSuccessServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(formPostSuccessServerCA))(h))
|
||||
require.NoError(t, WithSkipListen()(h))
|
||||
h.openURL = func(authorizeURL string) error {
|
||||
parsed, err := url.Parse(authorizeURL)
|
||||
@@ -750,7 +749,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
h.generatePKCE = func() (pkce.Code, error) { return "test-pkce", nil }
|
||||
h.generateNonce = func() (nonce.Nonce, error) { return "test-nonce", nil }
|
||||
h.stdinIsTTY = func() bool { return true }
|
||||
require.NoError(t, WithClient(newClientForServer(formPostSuccessServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(formPostSuccessServerCA))(h))
|
||||
h.listen = func(string, string) (net.Listener, error) { return nil, fmt.Errorf("some listen error") }
|
||||
h.openURL = func(authorizeURL string) error {
|
||||
parsed, err := url.Parse(authorizeURL)
|
||||
@@ -790,7 +789,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
h.generateNonce = func() (nonce.Nonce, error) { return "test-nonce", nil }
|
||||
h.stdinIsTTY = func() bool { return true }
|
||||
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
|
||||
ctx, cancel := context.WithCancel(h.ctx)
|
||||
h.ctx = ctx
|
||||
@@ -824,7 +823,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
h.generatePKCE = func() (pkce.Code, error) { return "test-pkce", nil }
|
||||
h.generateNonce = func() (nonce.Nonce, error) { return "test-nonce", nil }
|
||||
h.stdinIsTTY = func() bool { return true }
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
h.openURL = func(_ string) error {
|
||||
go func() {
|
||||
h.callbacks <- callbackResult{err: fmt.Errorf("some callback error")}
|
||||
@@ -873,7 +872,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
})
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
|
||||
client := newClientForServer(successServer)
|
||||
client := buildHTTPClientForPEM(successServerCA)
|
||||
client.Timeout = 10 * time.Second
|
||||
require.NoError(t, WithClient(client)(h))
|
||||
|
||||
@@ -947,7 +946,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
})
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
|
||||
client := newClientForServer(successServer)
|
||||
client := buildHTTPClientForPEM(successServerCA)
|
||||
client.Timeout = 10 * time.Second
|
||||
require.NoError(t, WithClient(client)(h))
|
||||
|
||||
@@ -1013,7 +1012,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
})
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
|
||||
client := newClientForServer(successServer)
|
||||
client := buildHTTPClientForPEM(successServerCA)
|
||||
client.Timeout = 10 * time.Second
|
||||
require.NoError(t, WithClient(client)(h))
|
||||
|
||||
@@ -1091,7 +1090,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
})
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
|
||||
client := newClientForServer(successServer)
|
||||
client := buildHTTPClientForPEM(successServerCA)
|
||||
client.Timeout = 10 * time.Second
|
||||
require.NoError(t, WithClient(client)(h))
|
||||
|
||||
@@ -1182,7 +1181,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
})
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
|
||||
client := newClientForServer(formPostSuccessServer)
|
||||
client := buildHTTPClientForPEM(formPostSuccessServerCA)
|
||||
client.Timeout = 10 * time.Second
|
||||
require.NoError(t, WithClient(client)(h))
|
||||
|
||||
@@ -1258,7 +1257,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithUpstreamIdentityProvider("some-upstream-name", "oidc")(h))
|
||||
|
||||
client := newClientForServer(successServer)
|
||||
client := buildHTTPClientForPEM(successServerCA)
|
||||
client.Timeout = 10 * time.Second
|
||||
require.NoError(t, WithClient(client)(h))
|
||||
|
||||
@@ -1349,7 +1348,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
return func(h *handlerState) error {
|
||||
_ = defaultLDAPTestOpts(t, h, nil, nil)
|
||||
|
||||
client := newClientForServer(successServer)
|
||||
client := buildHTTPClientForPEM(successServerCA)
|
||||
client.Transport = roundtripper.Func(func(req *http.Request) (*http.Response, error) {
|
||||
switch req.URL.Scheme + "://" + req.URL.Host + req.URL.Path {
|
||||
case "https://" + successServer.Listener.Addr().String() + "/.well-known/openid-configuration":
|
||||
@@ -1567,7 +1566,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
require.True(t, authorizeRequestWasMade, "should have made an authorize request")
|
||||
})
|
||||
|
||||
client := newClientForServer(successServer)
|
||||
client := buildHTTPClientForPEM(successServerCA)
|
||||
client.Transport = roundtripper.Func(func(req *http.Request) (*http.Response, error) {
|
||||
switch req.URL.Scheme + "://" + req.URL.Host + req.URL.Path {
|
||||
case "https://" + successServer.Listener.Addr().String() + "/.well-known/openid-configuration":
|
||||
@@ -1671,7 +1670,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
require.True(t, authorizeRequestWasMade, "should have made an authorize request")
|
||||
})
|
||||
|
||||
client := newClientForServer(successServer)
|
||||
client := buildHTTPClientForPEM(successServerCA)
|
||||
client.Transport = roundtripper.Func(func(req *http.Request) (*http.Response, error) {
|
||||
switch req.URL.Scheme + "://" + req.URL.Host + req.URL.Path {
|
||||
case "https://" + successServer.Listener.Addr().String() + "/.well-known/openid-configuration":
|
||||
@@ -1778,7 +1777,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
require.True(t, authorizeRequestWasMade, "should have made an authorize request")
|
||||
})
|
||||
|
||||
client := newClientForServer(successServer)
|
||||
client := buildHTTPClientForPEM(successServerCA)
|
||||
client.Transport = roundtripper.Func(func(req *http.Request) (*http.Response, error) {
|
||||
switch req.URL.Scheme + "://" + req.URL.Host + req.URL.Path {
|
||||
case "https://" + successServer.Listener.Addr().String() + "/.well-known/openid-configuration":
|
||||
@@ -1842,7 +1841,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
}}, cache.sawGetKeys)
|
||||
require.Empty(t, cache.sawPutTokens)
|
||||
})
|
||||
require.NoError(t, WithClient(newClientForServer(errorServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(errorServerCA))(h))
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithRequestAudience("cluster-1234")(h))
|
||||
return nil
|
||||
@@ -1871,7 +1870,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
}}, cache.sawGetKeys)
|
||||
require.Empty(t, cache.sawPutTokens)
|
||||
})
|
||||
require.NoError(t, WithClient(newClientForServer(insecureTokenURLServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(insecureTokenURLServerCA))(h))
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithRequestAudience("cluster-1234")(h))
|
||||
return nil
|
||||
@@ -1900,7 +1899,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
}}, cache.sawGetKeys)
|
||||
require.Empty(t, cache.sawPutTokens)
|
||||
})
|
||||
require.NoError(t, WithClient(newClientForServer(brokenTokenURLServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(brokenTokenURLServerCA))(h))
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithRequestAudience("cluster-1234")(h))
|
||||
return nil
|
||||
@@ -1929,7 +1928,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
}}, cache.sawGetKeys)
|
||||
require.Empty(t, cache.sawPutTokens)
|
||||
})
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithRequestAudience("test-audience-produce-invalid-http-response")(h))
|
||||
return nil
|
||||
@@ -1958,7 +1957,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
}}, cache.sawGetKeys)
|
||||
require.Empty(t, cache.sawPutTokens)
|
||||
})
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithRequestAudience("test-audience-produce-http-400")(h))
|
||||
return nil
|
||||
@@ -1987,7 +1986,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
}}, cache.sawGetKeys)
|
||||
require.Empty(t, cache.sawPutTokens)
|
||||
})
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithRequestAudience("test-audience-produce-invalid-content-type")(h))
|
||||
return nil
|
||||
@@ -2016,7 +2015,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
}}, cache.sawGetKeys)
|
||||
require.Empty(t, cache.sawPutTokens)
|
||||
})
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithRequestAudience("test-audience-produce-wrong-content-type")(h))
|
||||
return nil
|
||||
@@ -2045,7 +2044,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
}}, cache.sawGetKeys)
|
||||
require.Empty(t, cache.sawPutTokens)
|
||||
})
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithRequestAudience("test-audience-produce-invalid-json")(h))
|
||||
return nil
|
||||
@@ -2074,7 +2073,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
}}, cache.sawGetKeys)
|
||||
require.Empty(t, cache.sawPutTokens)
|
||||
})
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithRequestAudience("test-audience-produce-invalid-tokentype")(h))
|
||||
return nil
|
||||
@@ -2103,7 +2102,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
}}, cache.sawGetKeys)
|
||||
require.Empty(t, cache.sawPutTokens)
|
||||
})
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithRequestAudience("test-audience-produce-invalid-issuedtokentype")(h))
|
||||
return nil
|
||||
@@ -2132,7 +2131,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
}}, cache.sawGetKeys)
|
||||
require.Empty(t, cache.sawPutTokens)
|
||||
})
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithRequestAudience("test-audience-produce-invalid-jwt")(h))
|
||||
return nil
|
||||
@@ -2161,7 +2160,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
}}, cache.sawGetKeys)
|
||||
require.Empty(t, cache.sawPutTokens)
|
||||
})
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithRequestAudience("test-audience")(h))
|
||||
|
||||
@@ -2204,7 +2203,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
}}, cache.sawGetKeys)
|
||||
require.Empty(t, cache.sawPutTokens)
|
||||
})
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithRequestAudience("request-this-test-audience")(h))
|
||||
|
||||
@@ -2256,7 +2255,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
Claims: map[string]interface{}{"aud": "test-custom-request-audience"},
|
||||
}, cache.sawPutTokens[0].IDToken)
|
||||
})
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithRequestAudience("test-custom-request-audience")(h))
|
||||
|
||||
@@ -2318,7 +2317,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
}}, cache.sawGetKeys)
|
||||
require.Empty(t, cache.sawPutTokens)
|
||||
})
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
require.NoError(t, WithSessionCache(cache)(h))
|
||||
require.NoError(t, WithRequestAudience("request-this-test-audience")(h))
|
||||
|
||||
@@ -2343,7 +2342,7 @@ func TestLogin(t *testing.T) { //nolint:gocyclo
|
||||
clientID: "test-client-id",
|
||||
opt: func(t *testing.T) Option {
|
||||
return func(h *handlerState) error {
|
||||
require.NoError(t, WithClient(newClientForServer(successServer))(h))
|
||||
require.NoError(t, WithClient(buildHTTPClientForPEM(successServerCA))(h))
|
||||
|
||||
cache := &mockSessionCache{t: t, getReturnsToken: &oidctypes.Token{
|
||||
IDToken: &oidctypes.IDToken{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package integration
|
||||
@@ -90,7 +90,8 @@ func TestCredentialIssuer(t *testing.T) {
|
||||
} else {
|
||||
require.Equal(t, configv1alpha1.ErrorStrategyStatus, actualStatusStrategy.Status)
|
||||
require.Equal(t, configv1alpha1.CouldNotFetchKeyStrategyReason, actualStatusStrategy.Reason)
|
||||
require.Contains(t, actualStatusStrategy.Message, "could not find a healthy kube-controller-manager pod (0 candidates)")
|
||||
require.Contains(t, actualStatusStrategy.Message, "could not find a healthy kube-controller-manager pod (0 candidates): "+
|
||||
"note that this error is the expected behavior for some cluster types, including most cloud provider clusters (e.g. GKE, AKS, EKS)")
|
||||
require.Nil(t, actualStatusKubeConfigInfo)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -276,7 +276,7 @@ func allSuccessfulWebhookAuthenticatorConditions() []metav1.Condition {
|
||||
Type: "EndpointURLValid",
|
||||
Status: "True",
|
||||
Reason: "Success",
|
||||
Message: "endpoint is a valid URL",
|
||||
Message: "spec.endpoint is a valid URL",
|
||||
},
|
||||
{
|
||||
Type: "Ready",
|
||||
@@ -294,7 +294,7 @@ func allSuccessfulWebhookAuthenticatorConditions() []metav1.Condition {
|
||||
Type: "WebhookConnectionValid",
|
||||
Status: "True",
|
||||
Reason: "Success",
|
||||
Message: "tls verified",
|
||||
Message: "successfully dialed webhook server",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build fips_strict
|
||||
@@ -23,14 +23,13 @@ import (
|
||||
func TestFIPSCipherSuites_Parallel(t *testing.T) {
|
||||
_ = testlib.IntegrationEnv(t)
|
||||
|
||||
server := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
server, ca := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// use the default fips config which contains a hard coded list of cipher suites
|
||||
// that should be equal to the default list of fips cipher suites.
|
||||
// assert that the client hello response has the same tls config as this test server.
|
||||
tlsserver.AssertTLS(t, r, ptls.Default)
|
||||
}), tlsserver.RecordTLSHello)
|
||||
|
||||
ca := tlsserver.TLSTestServerCA(server)
|
||||
pool, err := cert.NewPoolFromBytes(ca)
|
||||
require.NoError(t, err)
|
||||
// create a tls config that does not explicitly set cipher suites,
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
func TestSecureTLSPinnipedCLIToKAS_Parallel(t *testing.T) {
|
||||
_ = testlib.IntegrationEnv(t)
|
||||
|
||||
server := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
server, serverCA := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// pinniped CLI uses ptls.Secure when talking to KAS
|
||||
// in FIPS mode the distinction doesn't matter much because
|
||||
// each of the configs is a wrapper for the same base FIPS config
|
||||
@@ -33,15 +33,13 @@ func TestSecureTLSPinnipedCLIToKAS_Parallel(t *testing.T) {
|
||||
`"status":{"credential":{"token":"some-fancy-token"}}}`)
|
||||
}), tlsserver.RecordTLSHello)
|
||||
|
||||
ca := tlsserver.TLSTestServerCA(server)
|
||||
|
||||
pinnipedExe := testlib.PinnipedCLIPath(t)
|
||||
|
||||
stdout, stderr := runPinnipedCLI(t, nil, pinnipedExe, "login", "static",
|
||||
"--token", "does-not-matter",
|
||||
"--concierge-authenticator-type", "webhook",
|
||||
"--concierge-authenticator-name", "does-not-matter",
|
||||
"--concierge-ca-bundle-data", base64.StdEncoding.EncodeToString(ca),
|
||||
"--concierge-ca-bundle-data", base64.StdEncoding.EncodeToString(serverCA),
|
||||
"--concierge-endpoint", server.URL,
|
||||
"--enable-concierge",
|
||||
"--credential-cache", "",
|
||||
@@ -57,7 +55,7 @@ func TestSecureTLSPinnipedCLIToKAS_Parallel(t *testing.T) {
|
||||
func TestSecureTLSPinnipedCLIToSupervisor_Parallel(t *testing.T) {
|
||||
_ = testlib.IntegrationEnv(t)
|
||||
|
||||
server := tlsserver.TLSTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
server, serverCA := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// pinniped CLI uses ptls.Default when talking to supervisor
|
||||
// in FIPS mode the distinction doesn't matter much because
|
||||
// each of the configs is a wrapper for the same base FIPS config
|
||||
@@ -66,12 +64,10 @@ func TestSecureTLSPinnipedCLIToSupervisor_Parallel(t *testing.T) {
|
||||
fmt.Fprint(w, `{"issuer":"https://not-a-good-issuer"}`)
|
||||
}), tlsserver.RecordTLSHello)
|
||||
|
||||
ca := tlsserver.TLSTestServerCA(server)
|
||||
|
||||
pinnipedExe := testlib.PinnipedCLIPath(t)
|
||||
|
||||
stdout, stderr := runPinnipedCLI(&fakeT{T: t}, nil, pinnipedExe, "login", "oidc",
|
||||
"--ca-bundle-data", base64.StdEncoding.EncodeToString(ca),
|
||||
"--ca-bundle-data", base64.StdEncoding.EncodeToString(serverCA),
|
||||
"--issuer", server.URL,
|
||||
"--credential-cache", "",
|
||||
"--upstream-identity-provider-flow", "cli_password",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2022-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package testlib
|
||||
|
||||
@@ -199,7 +199,7 @@ func dialTLS(t *testing.T, env *TestEnv) *ldap.Conn {
|
||||
c, err := dialer.DialContext(context.Background(), "tcp", env.SupervisorUpstreamActiveDirectory.Host)
|
||||
require.NoError(t, err)
|
||||
conn := ldap.NewConn(c, true)
|
||||
conn.Start()
|
||||
conn.Start() //nolint:staticcheck // will need a different approach soon
|
||||
return conn
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user