From f352db8072c54e70d9e32f618f28bcf496b42086 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Jul 2021 00:03:39 +0000 Subject: [PATCH 01/34] Bump github.com/creack/pty from 1.1.13 to 1.1.14 Bumps [github.com/creack/pty](https://github.com/creack/pty) from 1.1.13 to 1.1.14. - [Release notes](https://github.com/creack/pty/releases) - [Commits](https://github.com/creack/pty/compare/v1.1.13...v1.1.14) --- updated-dependencies: - dependency-name: github.com/creack/pty dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d2779c48..020b41b4 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.16 require ( github.com/MakeNowJust/heredoc/v2 v2.0.1 github.com/coreos/go-oidc/v3 v3.0.0 - github.com/creack/pty v1.1.13 + github.com/creack/pty v1.1.14 github.com/davecgh/go-spew v1.1.1 github.com/go-ldap/ldap/v3 v3.3.0 github.com/go-logr/logr v0.4.0 diff --git a/go.sum b/go.sum index 99c41de8..66195510 100644 --- a/go.sum +++ b/go.sum @@ -162,8 +162,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.13 h1:rTPnd/xocYRjutMfqide2zle1u96upp1gm6eUHKi7us= -github.com/creack/pty v1.1.13/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/creack/pty v1.1.14 h1:55VbUWoBxE1iTAh3B6JztD6xyQ06CvW/31oD6rYwrtY= +github.com/creack/pty v1.1.14/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cucumber/godog v0.8.1/go.mod h1:vSh3r/lM+psC1BPXvdkSEuNjmXfpVqrMGYAElF6hxnA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= From fc82fde5855f0cff81ee8497f82e90e8a855b1c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Jul 2021 00:03:48 +0000 Subject: [PATCH 02/34] Bump github.com/tdewolff/minify/v2 from 2.9.19 to 2.9.20 Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.19 to 2.9.20. - [Release notes](https://github.com/tdewolff/minify/releases) - [Commits](https://github.com/tdewolff/minify/compare/v2.9.19...v2.9.20) --- updated-dependencies: - dependency-name: github.com/tdewolff/minify/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d2779c48..035ec2ae 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/spf13/cobra v1.2.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.7.0 - github.com/tdewolff/minify/v2 v2.9.19 + github.com/tdewolff/minify/v2 v2.9.20 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 diff --git a/go.sum b/go.sum index 99c41de8..75cd7b43 100644 --- a/go.sum +++ b/go.sum @@ -1152,8 +1152,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/subosito/gotenv v1.1.1/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tdewolff/minify/v2 v2.9.19 h1:QdCQhIqCeBnSWU/4e0nJ8QJeQUNO5CC7jrH3xwETkVI= -github.com/tdewolff/minify/v2 v2.9.19/go.mod h1:PoDBts2L7sCwUT28vTAlozGeD6qxjrrihtin4bR/RMM= +github.com/tdewolff/minify/v2 v2.9.20 h1:Fut7w3T7nWfDOb/bOgyEvshQRRMt+xzi1T7spEEKXDw= +github.com/tdewolff/minify/v2 v2.9.20/go.mod h1:PoDBts2L7sCwUT28vTAlozGeD6qxjrrihtin4bR/RMM= github.com/tdewolff/parse/v2 v2.5.19 h1:Kjaj3KQOx/4elIxlBSglus4E2oMfdROphvbq2b+OBZ0= github.com/tdewolff/parse/v2 v2.5.19/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4= From 727035a2dc418b790a6724a4c627898af6b7fae4 Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Wed, 28 Jul 2021 08:09:20 -0500 Subject: [PATCH 03/34] Fix form_post CSS styling in Firefox and Safari. This functioned fine, but did not have the intended visual appearance when it came to how the text of the auth code wrapped inside the copy button in the manual flow. The new styling behaves correctly on at least Chrome, Firefox, and Safari on macOS. Signed-off-by: Matt Moyer --- internal/oidc/provider/formposthtml/form_post.css | 8 ++++---- internal/oidc/provider/formposthtml/formposthtml_test.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/oidc/provider/formposthtml/form_post.css b/internal/oidc/provider/formposthtml/form_post.css index c65c2fc7..e272f13d 100644 --- a/internal/oidc/provider/formposthtml/form_post.css +++ b/internal/oidc/provider/formposthtml/form_post.css @@ -44,9 +44,9 @@ button:active { } code { + display: block; word-wrap: break-word; - hyphens: auto; - hyphenate-character: ''; + word-break: break-all; font-size: 12px; font-family: monospace; color: #333; @@ -56,8 +56,8 @@ code { float: left; width: 36px; height: 36px; - padding-top: 2px; - padding-right: 10px; + margin-top: -3px; + margin-right: 10px; background-size: contain; background-repeat: no-repeat; /* diff --git a/internal/oidc/provider/formposthtml/formposthtml_test.go b/internal/oidc/provider/formposthtml/formposthtml_test.go index a8a1a929..f9929e71 100644 --- a/internal/oidc/provider/formposthtml/formposthtml_test.go +++ b/internal/oidc/provider/formposthtml/formposthtml_test.go @@ -29,7 +29,7 @@ var ( - + @@ -62,7 +62,7 @@ var ( // Our browser-based integration tests should find any incompatibilities. testExpectedCSP = `default-src 'none'; ` + `script-src 'sha256-U+tKnJ2oMSYKSxmSX3V2mPBN8xdr9JpampKAhbSo108='; ` + - `style-src 'sha256-TLAQE3UR2KpwP7AzMCE4iPDizh7zLPx9UXeK5ntuoRg='; ` + + `style-src 'sha256-CtfkX7m8x2UdGYvGgDq+6b6yIAQsASW9pbQK+sG8fNA='; ` + `img-src data:; ` + `connect-src *; ` + `frame-ancestors 'none'` From 8b4ed86071c33b3f7cc7756b8c45a69bd92358e9 Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Wed, 28 Jul 2021 09:26:19 -0400 Subject: [PATCH 04/34] certs_expirer: be specific about what secret to delete This change fixes a race that can occur because we have multiple writers with no leader election lock. 1. TestAPIServingCertificateAutoCreationAndRotation/automatic expires the current serving certificate 2. CertsExpirerController 1 deletes expired serving certificate 3. CertsExpirerController 2 starts deletion of expired serving certificate but has not done so yet 4. CertsManagerController 1 creates new serving certificate 5. TestAPIServingCertificateAutoCreationAndRotation/automatic records the new serving certificate 6. CertsExpirerController 2 finishes deletion, and thus deletes the newly created serving certificate instead of the old one 7. CertsManagerController 2 creates new serving certificate 8. TestAPIServingCertificateAutoCreationAndRotation/automatic keeps running and eventually times out because it is expecting the serving certificate created by CertsManagerController 2 to match the value it recorded from CertsManagerController 1 (which will never happen since that certificate was incorrectly deleted). Signed-off-by: Monis Khan --- internal/controller/apicerts/certs_expirer.go | 7 ++- .../controller/apicerts/certs_expirer_test.go | 56 ++++++++++++++++++- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/internal/controller/apicerts/certs_expirer.go b/internal/controller/apicerts/certs_expirer.go index 2b643c3e..c04d27bc 100644 --- a/internal/controller/apicerts/certs_expirer.go +++ b/internal/controller/apicerts/certs_expirer.go @@ -90,7 +90,12 @@ func (c *certsExpirerController) Sync(ctx controllerlib.Context) error { err := c.k8sClient. CoreV1(). Secrets(c.namespace). - Delete(ctx.Context, c.certsSecretResourceName, metav1.DeleteOptions{}) + Delete(ctx.Context, c.certsSecretResourceName, metav1.DeleteOptions{ + Preconditions: &metav1.Preconditions{ + UID: &secret.UID, + ResourceVersion: &secret.ResourceVersion, + }, + }) if err != nil { // Do return an error here so that the controller library will reschedule // us to try deleting this cert again. diff --git a/internal/controller/apicerts/certs_expirer_test.go b/internal/controller/apicerts/certs_expirer_test.go index 7e82819a..da508c55 100644 --- a/internal/controller/apicerts/certs_expirer_test.go +++ b/internal/controller/apicerts/certs_expirer_test.go @@ -18,8 +18,11 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" kubeinformers "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" kubernetesfake "k8s.io/client-go/kubernetes/fake" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" kubetesting "k8s.io/client-go/testing" "go.pinniped.dev/internal/controllerlib" @@ -223,14 +226,19 @@ func TestExpirerControllerSync(t *testing.T) { test.configKubeAPIClient(kubeAPIClient) } + testRV := "rv_001" + testUID := types.UID("uid_002") + kubeInformerClient := kubernetesfake.NewSimpleClientset() name := certsSecretResourceName namespace := "some-namespace" if test.fillSecretData != nil { secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, + Name: name, + Namespace: namespace, + ResourceVersion: testRV, + UID: testUID, }, Data: map[string][]byte{}, } @@ -245,10 +253,12 @@ func TestExpirerControllerSync(t *testing.T) { 0, ) + trackDeleteClient := &clientWrapper{Interface: kubeAPIClient, opts: &[]metav1.DeleteOptions{}} + c := NewCertsExpirerController( namespace, certsSecretResourceName, - kubeAPIClient, + trackDeleteClient, kubeInformers.Core().V1().Secrets(), controllerlib.WithInformer, test.renewBefore, @@ -285,6 +295,46 @@ func TestExpirerControllerSync(t *testing.T) { } acActions := kubeAPIClient.Actions() require.Equal(t, exActions, acActions) + + if test.wantDelete { + require.Len(t, *trackDeleteClient.opts, 1) + require.Equal(t, metav1.DeleteOptions{ + Preconditions: &metav1.Preconditions{ + UID: &testUID, + ResourceVersion: &testRV, + }, + }, (*trackDeleteClient.opts)[0]) + } else { + require.Len(t, *trackDeleteClient.opts, 0) + } }) } } + +type clientWrapper struct { + kubernetes.Interface + opts *[]metav1.DeleteOptions +} + +func (c *clientWrapper) CoreV1() corev1client.CoreV1Interface { + return &coreWrapper{CoreV1Interface: c.Interface.CoreV1(), opts: c.opts} +} + +type coreWrapper struct { + corev1client.CoreV1Interface + opts *[]metav1.DeleteOptions +} + +func (c *coreWrapper) Secrets(namespace string) corev1client.SecretInterface { + return &secretsWrapper{SecretInterface: c.CoreV1Interface.Secrets(namespace), opts: c.opts} +} + +type secretsWrapper struct { + corev1client.SecretInterface + opts *[]metav1.DeleteOptions +} + +func (s *secretsWrapper) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { + *s.opts = append(*s.opts, opts) + return s.SecretInterface.Delete(ctx, name, opts) +} From 5f679059d509d9eb1b5960b2745cb0d9f4828d7a Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Wed, 28 Jul 2021 11:57:18 -0500 Subject: [PATCH 05/34] Add ClusterIP service to impersonator-config-controller informer. Prior to this fix, this controller did not correctly react to changes to the ClusterIP service. It would still eventually react with a long delay due to our 5 minute resync interval. Signed-off-by: Matt Moyer --- .../impersonatorconfig/impersonator_config.go | 10 +++++++++- .../impersonator_config_test.go | 17 +++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/internal/controller/impersonatorconfig/impersonator_config.go b/internal/controller/impersonatorconfig/impersonator_config.go index d9d567dc..f5845013 100644 --- a/internal/controller/impersonatorconfig/impersonator_config.go +++ b/internal/controller/impersonatorconfig/impersonator_config.go @@ -143,7 +143,15 @@ func NewImpersonatorConfigController( withInformer( servicesInformer, pinnipedcontroller.SimpleFilterWithSingletonQueue(func(obj metav1.Object) bool { - return obj.GetNamespace() == namespace && obj.GetName() == generatedLoadBalancerServiceName + if obj.GetNamespace() != namespace { + return false + } + switch obj.GetName() { + case generatedLoadBalancerServiceName, generatedClusterIPServiceName: + return true + default: + return false + } }), controllerlib.InformerOption{}, ), diff --git a/internal/controller/impersonatorconfig/impersonator_config_test.go b/internal/controller/impersonatorconfig/impersonator_config_test.go index b5e2638c..33bd17ff 100644 --- a/internal/controller/impersonatorconfig/impersonator_config_test.go +++ b/internal/controller/impersonatorconfig/impersonator_config_test.go @@ -131,11 +131,12 @@ func TestImpersonatorConfigControllerOptions(t *testing.T) { when("watching Service objects", func() { var subject controllerlib.Filter - var target, wrongNamespace, wrongName, unrelated *corev1.Service + var targetLBService, targetClusterIPService, wrongNamespace, wrongName, unrelated *corev1.Service it.Before(func() { subject = servicesInformerFilter - target = &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: generatedLoadBalancerServiceName, Namespace: installedInNamespace}} + targetLBService = &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: generatedLoadBalancerServiceName, Namespace: installedInNamespace}} + targetClusterIPService = &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: generatedClusterIPServiceName, Namespace: installedInNamespace}} wrongNamespace = &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: generatedLoadBalancerServiceName, Namespace: "wrong-namespace"}} wrongName = &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "wrong-name", Namespace: installedInNamespace}} unrelated = &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "wrong-name", Namespace: "wrong-namespace"}} @@ -143,10 +144,14 @@ func TestImpersonatorConfigControllerOptions(t *testing.T) { when("the target Service changes", func() { it("returns true to trigger the sync method", func() { - r.True(subject.Add(target)) - r.True(subject.Update(target, unrelated)) - r.True(subject.Update(unrelated, target)) - r.True(subject.Delete(target)) + r.True(subject.Add(targetLBService)) + r.True(subject.Update(targetLBService, unrelated)) + r.True(subject.Update(unrelated, targetLBService)) + r.True(subject.Delete(targetLBService)) + r.True(subject.Add(targetClusterIPService)) + r.True(subject.Update(targetClusterIPService, unrelated)) + r.True(subject.Update(unrelated, targetClusterIPService)) + r.True(subject.Delete(targetClusterIPService)) }) }) From 48c8fabb5c0ab53716c5d7543c64c9e345ac6bc0 Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Wed, 28 Jul 2021 12:40:07 -0500 Subject: [PATCH 06/34] Fix backwards condition in E2E test assertion. Signed-off-by: Matt Moyer --- test/integration/e2e_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/e2e_test.go b/test/integration/e2e_test.go index 2a54a0a2..2086de3e 100644 --- a/test/integration/e2e_test.go +++ b/test/integration/e2e_test.go @@ -594,7 +594,7 @@ func requireKubectlGetNamespaceOutput(t *testing.T, env *testlib.TestEnv, kubect require.Greaterf(t, len(strings.Split(kubectlOutput, "\n")), 2, "expected some namespaces to be returned, got %q", kubectlOutput) require.Contains(t, kubectlOutput, fmt.Sprintf("\n%s ", env.ConciergeNamespace)) require.Contains(t, kubectlOutput, fmt.Sprintf("\n%s ", env.SupervisorNamespace)) - if len(env.ToolsNamespace) == 0 { + if len(env.ToolsNamespace) > 0 { require.Contains(t, kubectlOutput, fmt.Sprintf("\n%s ", env.ToolsNamespace)) } } From b42b1c111078116a2596e0a746fb5eb0ff21c502 Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Wed, 28 Jul 2021 12:51:11 -0500 Subject: [PATCH 07/34] Relax the timeout for TestLegacyPodCleaner a bit. This test is asynchronously waiting for the controller to do something, and in some of our test environments it will take a bit longer than we'd previously allowed. Signed-off-by: Matt Moyer --- test/integration/concierge_kubecertagent_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/concierge_kubecertagent_test.go b/test/integration/concierge_kubecertagent_test.go index 286a9ae9..4a756f94 100644 --- a/test/integration/concierge_kubecertagent_test.go +++ b/test/integration/concierge_kubecertagent_test.go @@ -95,7 +95,7 @@ func findSuccessfulStrategy(credentialIssuer *conciergev1alpha.CredentialIssuer, func TestLegacyPodCleaner(t *testing.T) { env := testlib.IntegrationEnv(t).WithCapability(testlib.ClusterSigningKeyIsAvailable) - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() kubeClient := testlib.NewKubernetesClientset(t) @@ -144,5 +144,5 @@ func TestLegacyPodCleaner(t *testing.T) { return true, nil } return false, err - }, 60*time.Second, 1*time.Second) + }, 2*time.Minute, 1*time.Second) } From d73093a694d06c7332c77823c5a996b3ab3c5988 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Wed, 28 Jul 2021 14:19:14 -0700 Subject: [PATCH 08/34] Avoid failures due to impersonation Service having unrelated annotations --- .../concierge_impersonation_proxy_test.go | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/test/integration/concierge_impersonation_proxy_test.go b/test/integration/concierge_impersonation_proxy_test.go index 634b6d68..ea305aa6 100644 --- a/test/integration/concierge_impersonation_proxy_test.go +++ b/test/integration/concierge_impersonation_proxy_test.go @@ -1478,6 +1478,10 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl t.Skip("only running when the cluster is meant to be using LoadBalancer services") } + // Use this string in all annotation keys added by this test, so the assertions can ignore annotation keys + // which might exist on the Service which are not related to this test. + recognizableAnnotationKeyString := "pinniped.dev" + // Grab the state of the CredentialIssuer prior to this test, so we can restore things back afterwards. previous, err := adminConciergeClient.ConfigV1alpha1().CredentialIssuers().Get(ctx, credentialIssuerName(env), metav1.GetOptions{}) require.NoError(t, err) @@ -1546,16 +1550,30 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl })) } - waitForServiceAnnotations := func(annotations map[string]string) { + waitForServiceAnnotations := func(wantAnnotations map[string]string, annotationKeyFilter string) { testlib.RequireEventuallyWithoutError(t, func() (bool, error) { service, err := adminClient.CoreV1().Services(env.ConciergeNamespace).Get(ctx, impersonationProxyLoadBalancerName(env), metav1.GetOptions{}) if err != nil { return false, err } - t.Logf("found Service %s of type %s with actual annotations %q; expected annotations %q", - service.Name, service.Spec.Type, service.Annotations, annotations) - return equality.Semantic.DeepEqual(service.Annotations, annotations), nil - }, 30*time.Second, 100*time.Millisecond) + filteredActualAnnotations := map[string]string{} + for k, v := range service.Annotations { + // We do want to pay attention to any annotation for which we intend to make an explicit assertion, + // e.g. "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout" which is from our + // default CredentialIssuer spec. + _, wantToMakeAssertionOnThisAnnotation := wantAnnotations[k] + // We do not want to pay attention to Service annotations added by other controllers, + // e.g. the "cloud.google.com/neg" annotation that is sometimes added by GKE on Services. + // These can come and go in time intervals outside of our control. + annotationContainsFilterString := strings.Contains(k, annotationKeyFilter) + if wantToMakeAssertionOnThisAnnotation || annotationContainsFilterString { + filteredActualAnnotations[k] = v + } + } + t.Logf("found Service %s of type %s with actual annotations %q; filtered by interesting keys results in %q; expected annotations %q", + service.Name, service.Spec.Type, service.Annotations, filteredActualAnnotations, wantAnnotations) + return equality.Semantic.DeepEqual(filteredActualAnnotations, wantAnnotations), nil + }, 1*time.Minute, 1*time.Second) } expectedAnnotations := func(credentialIssuerSpecAnnotations map[string]string, otherAnnotations map[string]string) map[string]string { @@ -1575,12 +1593,13 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl sort.Strings(credentialIssuerSpecAnnotationKeys) credentialIssuerSpecAnnotationKeysJSON, err := json.Marshal(credentialIssuerSpecAnnotationKeys) require.NoError(t, err) - expectedAnnotations["credentialissuer.pinniped.dev/annotation-keys"] = string(credentialIssuerSpecAnnotationKeysJSON) + // The name of this annotation key is decided by our controller. + expectedAnnotations["credentialissuer."+recognizableAnnotationKeyString+"/annotation-keys"] = string(credentialIssuerSpecAnnotationKeysJSON) return expectedAnnotations } otherActorAnnotations := map[string]string{ - "pinniped.dev/test-other-actor-" + testlib.RandHex(t, 8): "test-other-actor-" + testlib.RandHex(t, 8), + recognizableAnnotationKeyString + "/test-other-actor-" + testlib.RandHex(t, 8): "test-other-actor-" + testlib.RandHex(t, 8), } // Whatever happens, set the annotations back to the original value and expect the Service to be updated. @@ -1590,6 +1609,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl applyCredentialIssuerAnnotations(previous.Spec.ImpersonationProxy.Service.DeepCopy().Annotations) waitForServiceAnnotations( expectedAnnotations(previous.Spec.ImpersonationProxy.Service.DeepCopy().Annotations, map[string]string{}), + recognizableAnnotationKeyString, ) }) @@ -1598,14 +1618,17 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl updateServiceAnnotations(otherActorAnnotations) // Set a new annotation in the CredentialIssuer spec.impersonationProxy.service.annotations field. - newAnnotationKey := "pinniped.dev/test-" + testlib.RandHex(t, 8) + newAnnotationKey := recognizableAnnotationKeyString + "/test-" + testlib.RandHex(t, 8) newAnnotationValue := "test-" + testlib.RandHex(t, 8) updatedAnnotations := previous.Spec.ImpersonationProxy.Service.DeepCopy().Annotations updatedAnnotations[newAnnotationKey] = newAnnotationValue applyCredentialIssuerAnnotations(updatedAnnotations) // Expect them to be applied to the Service. - waitForServiceAnnotations(expectedAnnotations(updatedAnnotations, otherActorAnnotations)) + waitForServiceAnnotations( + expectedAnnotations(updatedAnnotations, otherActorAnnotations), + recognizableAnnotationKeyString, + ) }) t.Run("running impersonation proxy with ClusterIP service", func(t *testing.T) { From ca82609d1a1bc19d3abb34f3e8b2276647ef11b9 Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Wed, 28 Jul 2021 13:55:41 -0500 Subject: [PATCH 09/34] Create a site parameter and shortcode for "latestversion". This gives us a single line of YAML to edit when we want to bump our docs to the latest version number. Signed-off-by: Matt Moyer --- site/config.yaml | 1 + site/content/docs/howto/install-cli.md | 6 +++--- site/content/docs/howto/install-concierge.md | 14 +++++++------- site/content/docs/howto/install-supervisor.md | 12 ++++++------ site/content/docs/tutorials/concierge-only-demo.md | 2 +- .../pinniped/layouts/shortcodes/latestversion.html | 1 + 6 files changed, 19 insertions(+), 17 deletions(-) create mode 100644 site/themes/pinniped/layouts/shortcodes/latestversion.html diff --git a/site/config.yaml b/site/config.yaml index f3bdbe69..0c1355f5 100644 --- a/site/config.yaml +++ b/site/config.yaml @@ -7,6 +7,7 @@ params: github_url: "https://github.com/vmware-tanzu/pinniped" slack_url: "https://kubernetes.slack.com/messages/pinniped" community_url: "https://go.pinniped.dev/community" + latest_version: v0.9.2 pygmentsCodefences: true pygmentsStyle: "pygments" markup: diff --git a/site/content/docs/howto/install-cli.md b/site/content/docs/howto/install-cli.md index f1451337..c9ec8b7e 100644 --- a/site/content/docs/howto/install-cli.md +++ b/site/content/docs/howto/install-cli.md @@ -46,15 +46,15 @@ Click Open to allow the command to proceed. Choose your preferred [release](https://github.com/vmware-tanzu/pinniped/releases) version number and use it to replace the version number in the URL below. -For example, to install v0.9.2 on Linux/amd64: +For example, to install {{< latestversion >}} on Linux/amd64: ```sh -curl -Lso pinniped https://get.pinniped.dev/v0.9.2/pinniped-cli-linux-amd64 \ +curl -Lso pinniped https://get.pinniped.dev/{{< latestversion >}}/pinniped-cli-linux-amd64 \ && chmod +x pinniped \ && sudo mv pinniped /usr/local/bin/pinniped ``` -*Replace v0.9.2 with your preferred version number.* +*Replace {{< latestversion >}} with your preferred version number.* ## Next steps diff --git a/site/content/docs/howto/install-concierge.md b/site/content/docs/howto/install-concierge.md index c80a174f..b22a645e 100644 --- a/site/content/docs/howto/install-concierge.md +++ b/site/content/docs/howto/install-concierge.md @@ -17,7 +17,7 @@ You should have a [supported Kubernetes cluster]({{< ref "../reference/supported 1. Install the latest version of the Concierge into the `pinniped-concierge` namespace with default options: - `kubectl apply -f https://get.pinniped.dev/latest/install-pinniped-concierge.yaml` - + Warning: the default configuration may create a public LoadBalancer Service on your cluster. ## With specific version and default options @@ -26,9 +26,9 @@ Warning: the default configuration may create a public LoadBalancer Service on y 1. Install the Concierge into the `pinniped-concierge` namespace with default options: - - `kubectl apply -f https://get.pinniped.dev/v0.9.2/install-pinniped-concierge.yaml` + - `kubectl apply -f https://get.pinniped.dev/{{< latestversion >}}/install-pinniped-concierge.yaml` - *Replace v0.9.2 with your preferred version number.* + *Replace {{< latestversion >}} with your preferred version number.* ## With custom options @@ -43,16 +43,16 @@ Pinniped uses [ytt](https://carvel.dev/ytt/) from [Carvel](https://carvel.dev/) 1. Decide which release version you would like to install. All release versions are [listed on GitHub](https://github.com/vmware-tanzu/pinniped/releases). -1. Checkout your preferred version tag, e.g. `v0.9.2`. +1. Checkout your preferred version tag, e.g. `{{< latestversion >}}`. - - `git checkout v0.9.2` + - `git checkout {{< latestversion >}}` - *Replace v0.9.2 with your preferred version number.* + *Replace {{< latestversion >}} with your preferred version number.* 1. Customize configuration parameters: - Edit `values.yaml` with your custom values. - - Change the `image_tag` value to match your preferred version tag, e.g. `v0.9.2`. *Replace v0.9.2 with your preferred version number.* + - Change the `image_tag` value to match your preferred version tag, e.g. `{{< latestversion >}}`. *Replace {{< latestversion >}} with your preferred version number.* - See the [default values](http://github.com/vmware-tanzu/pinniped/tree/main/deploy/concierge/values.yaml) for documentation about individual configuration parameters. 1. Render templated YAML manifests: diff --git a/site/content/docs/howto/install-supervisor.md b/site/content/docs/howto/install-supervisor.md index ce2c8526..5d869507 100644 --- a/site/content/docs/howto/install-supervisor.md +++ b/site/content/docs/howto/install-supervisor.md @@ -25,9 +25,9 @@ You should have a supported Kubernetes cluster with working HTTPS ingress capabi 1. Install the Supervisor into the `pinniped-supervisor` namespace with default options: - - `kubectl apply -f https://get.pinniped.dev/v0.9.2/install-pinniped-supervisor.yaml` + - `kubectl apply -f https://get.pinniped.dev/{{< latestversion >}}/install-pinniped-supervisor.yaml` - *Replace v0.9.2 with your preferred version number.* + *Replace {{< latestversion >}} with your preferred version number.* ## With custom options @@ -42,16 +42,16 @@ Pinniped uses [ytt](https://carvel.dev/ytt/) from [Carvel](https://carvel.dev/) 1. Decide which release version you would like to install. All release versions are [listed on GitHub](https://github.com/vmware-tanzu/pinniped/releases). -1. Checkout your preferred version tag, e.g. `v0.9.2`. +1. Checkout your preferred version tag, e.g. `{{< latestversion >}}`. - - `git checkout v0.9.2` + - `git checkout {{< latestversion >}}` - *Replace v0.9.2 with your preferred version number.* + *Replace {{< latestversion >}} with your preferred version number.* 1. Customize configuration parameters: - Edit `values.yaml` with your custom values. - - Change the `image_tag` value to match your preferred version tag, e.g. `v0.9.2`. *Replace v0.9.2 with your preferred version number.* + - Change the `image_tag` value to match your preferred version tag, e.g. `{{< latestversion >}}`. *Replace {{< latestversion >}} with your preferred version number.* - See the [default values](http://github.com/vmware-tanzu/pinniped/tree/main/deploy/supervisor/values.yaml) for documentation about individual configuration parameters. 1. Render templated YAML manifests: diff --git a/site/content/docs/tutorials/concierge-only-demo.md b/site/content/docs/tutorials/concierge-only-demo.md index 15453416..7ef3502d 100644 --- a/site/content/docs/tutorials/concierge-only-demo.md +++ b/site/content/docs/tutorials/concierge-only-demo.md @@ -79,7 +79,7 @@ as the authenticator. see [deploy/local-user-authenticator/README.md](https://github.com/vmware-tanzu/pinniped/blob/main/deploy/local-user-authenticator/README.md) for instructions on how to deploy using `ytt`. - If you prefer to install a specific version, replace `latest` in the URL with the version number such as `v0.9.2`. + If you prefer to install a specific version, replace `latest` in the URL with the version number such as `{{< latestversion >}}`. 1. Create a test user named `pinny-the-seal` in the local-user-authenticator namespace. diff --git a/site/themes/pinniped/layouts/shortcodes/latestversion.html b/site/themes/pinniped/layouts/shortcodes/latestversion.html new file mode 100644 index 00000000..5344a32f --- /dev/null +++ b/site/themes/pinniped/layouts/shortcodes/latestversion.html @@ -0,0 +1 @@ +{{ .Site.Params.latest_version }} \ No newline at end of file From fd5ed2e5da67d436818dc0d799a26676e780af48 Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Wed, 28 Jul 2021 15:02:23 -0500 Subject: [PATCH 10/34] Rework "install" sections of our docs. - Remove all the "latest" links and replace them with our new shortcode so they point at the latest release in a more explicit way. This also eliminates one of the sections in our Concierge and Supervisor install guides, since you're always installing a specific version. - Provide instructions for installing with both kapp (one step) and kubectl (two steps for the Concierge). - Minor wording changes. Mainly we are now a bit less verbose about reminding people they can choose a different version (once per page instead of in each step). - When we give an example `kapp deploy` command, don't suggest `--yes` and `--diff-changes`. Users can still use these but it seems overly verbose for an example command. Signed-off-by: Matt Moyer --- site/content/docs/howto/install-cli.md | 10 ++--- site/content/docs/howto/install-concierge.md | 45 ++++++++++--------- site/content/docs/howto/install-supervisor.md | 36 +++++++-------- .../concierge-and-supervisor-demo.md | 12 +++-- .../docs/tutorials/concierge-only-demo.md | 12 ++--- .../layouts/shortcodes/buttonlink.html | 4 +- 6 files changed, 59 insertions(+), 60 deletions(-) diff --git a/site/content/docs/howto/install-cli.md b/site/content/docs/howto/install-cli.md index c9ec8b7e..aa5e388c 100644 --- a/site/content/docs/howto/install-cli.md +++ b/site/content/docs/howto/install-cli.md @@ -23,11 +23,11 @@ Use [Homebrew](https://brew.sh/) to install from the Pinniped [tap](https://gith Find the appropriate binary for your platform from the [latest release](https://github.com/vmware-tanzu/pinniped/releases/latest): -{{< buttonlink href="https://get.pinniped.dev/latest/pinniped-cli-darwin-amd64" >}}Download for macOS/amd64{{< buttonicon "download.png" >}}{{< /buttonlink >}} +{{< buttonlink filename="pinniped-cli-darwin-amd64" >}}Download {{< latestversion >}} for macOS/amd64{{< buttonicon "download.png" >}}{{< /buttonlink >}} -{{< buttonlink href="https://get.pinniped.dev/latest/pinniped-cli-linux-amd64" >}}Download for Linux/amd64{{< buttonicon "download.png" >}}{{< /buttonlink >}} +{{< buttonlink filename="pinniped-cli-linux-amd64" >}}Download {{< latestversion >}} for Linux/amd64{{< buttonicon "download.png" >}}{{< /buttonlink >}} -{{< buttonlink href="https://get.pinniped.dev/latest/pinniped-cli-windows-amd64.exe" >}}Download for Windows/amd64{{< buttonicon "download.png" >}}{{< /buttonlink >}} +{{< buttonlink filename="pinniped-cli-windows-amd64.exe" >}}Download {{< latestversion >}} for Windows/amd64{{< buttonicon "download.png" >}}{{< /buttonlink >}} You should put the command-line tool somewhere on your `$PATH`, such as `/usr/local/bin` on macOS/Linux. You'll also need to mark the file as executable. @@ -44,7 +44,7 @@ Click Open to allow the command to proceed. ## Install a specific version via script -Choose your preferred [release](https://github.com/vmware-tanzu/pinniped/releases) version number and use it to replace the version number in the URL below. +Choose your preferred [release](https://github.com/vmware-tanzu/pinniped/releases) and use it to replace the version number in the URL below. For example, to install {{< latestversion >}} on Linux/amd64: @@ -54,8 +54,6 @@ curl -Lso pinniped https://get.pinniped.dev/{{< latestversion >}}/pinniped-cli-l && sudo mv pinniped /usr/local/bin/pinniped ``` -*Replace {{< latestversion >}} with your preferred version number.* - ## Next steps Next, [install the Supervisor]({{< ref "install-supervisor.md" >}}) and/or [install the Concierge]({{< ref "install-concierge.md" >}})! diff --git a/site/content/docs/howto/install-concierge.md b/site/content/docs/howto/install-concierge.md index b22a645e..a2786ba1 100644 --- a/site/content/docs/howto/install-concierge.md +++ b/site/content/docs/howto/install-concierge.md @@ -12,29 +12,37 @@ menu: This guide shows you how to install the Pinniped Concierge. You should have a [supported Kubernetes cluster]({{< ref "../reference/supported-clusters" >}}). +In the examples below, you can replace *{{< latestversion >}}* with your preferred version number. +You can find a list of Pinniped releases [on GitHub](https://github.com/vmware-tanzu/pinniped/releases). + ## With default options +**Warning:** the default Concierge configuration may create a public LoadBalancer Service on your cluster if that is the default on your cloud provider. +If you'd prefer to customize the annotations or load balancer IP address, see the "With custom options" section below. + +### Using kapp + +1. Install the latest version of the Concierge into the `pinniped-concierge` namespace with default options using [kapp](https://carvel.dev/kapp/): + + - `kapp deploy --app pinniped-concierge--file https://get.pinniped.dev/{{< latestversion >}}/install-pinniped-concierge.yaml` + +### Using kubectl + +1. Install the latest version of the Concierge CustomResourceDefinitions: + + - `kubectl apply -f https://get.pinniped.dev/{{< latestversion >}}/install-pinniped-concierge-crds.yaml` + + This step is required so kubectl can validate the custom resources deployed in the next step. + 1. Install the latest version of the Concierge into the `pinniped-concierge` namespace with default options: - - `kubectl apply -f https://get.pinniped.dev/latest/install-pinniped-concierge.yaml` - -Warning: the default configuration may create a public LoadBalancer Service on your cluster. - -## With specific version and default options - -1. Choose your preferred [release](https://github.com/vmware-tanzu/pinniped/releases) version number and use it to replace the version number in the URL below. - -1. Install the Concierge into the `pinniped-concierge` namespace with default options: - - `kubectl apply -f https://get.pinniped.dev/{{< latestversion >}}/install-pinniped-concierge.yaml` - *Replace {{< latestversion >}} with your preferred version number.* - ## With custom options Pinniped uses [ytt](https://carvel.dev/ytt/) from [Carvel](https://carvel.dev/) as a templating system. -1. Install the `ytt` command-line tool using the instructions from the [Carvel documentation](https://carvel.dev/#whole-suite). +1. Install the `ytt` and `kapp` command-line tools using the instructions from the [Carvel documentation](https://carvel.dev/#whole-suite). 1. Clone the Pinniped GitHub repository and visit the `deploy/concierge` directory: @@ -47,12 +55,10 @@ Pinniped uses [ytt](https://carvel.dev/ytt/) from [Carvel](https://carvel.dev/) - `git checkout {{< latestversion >}}` - *Replace {{< latestversion >}} with your preferred version number.* - 1. Customize configuration parameters: - Edit `values.yaml` with your custom values. - - Change the `image_tag` value to match your preferred version tag, e.g. `{{< latestversion >}}`. *Replace {{< latestversion >}} with your preferred version number.* + - Change the `image_tag` value to match your preferred version tag, e.g. `{{< latestversion >}}`. - See the [default values](http://github.com/vmware-tanzu/pinniped/tree/main/deploy/concierge/values.yaml) for documentation about individual configuration parameters. 1. Render templated YAML manifests: @@ -61,12 +67,7 @@ Pinniped uses [ytt](https://carvel.dev/ytt/) from [Carvel](https://carvel.dev/) 1. Deploy the templated YAML manifests: - - *If you're using `kubectl`:* - - `ytt --file . | kubectl apply -f -` - - *If you're using [`kapp` from Carvel](https://carvel.dev/kapp/):* - - `ytt --file . | kapp deploy --yes --app pinniped-concierge --diff-changes --file -` + - `ytt --file . | kapp deploy --app pinniped-concierge --file -` ## Next steps diff --git a/site/content/docs/howto/install-supervisor.md b/site/content/docs/howto/install-supervisor.md index 5d869507..d2ca21e0 100644 --- a/site/content/docs/howto/install-supervisor.md +++ b/site/content/docs/howto/install-supervisor.md @@ -13,27 +13,28 @@ This guide shows you how to install the Pinniped Supervisor, which allows seamle You should have a supported Kubernetes cluster with working HTTPS ingress capabilities. +In the examples below, you can replace *{{< latestversion >}}* with your preferred version number. +You can find a list of Pinniped releases [on GitHub](https://github.com/vmware-tanzu/pinniped/releases). + ## With default options +### Using kapp + +1. Install the latest version of the Supervisor into the `pinniped-supervisor` namespace with default options using [kapp](https://carvel.dev/kapp/): + + - `kapp deploy --app pinniped-supervisor --file https://get.pinniped.dev/{{< latestversion >}}/install-pinniped-supervisor.yaml` + +### Using kubectl + 1. Install the latest version of the Supervisor into the `pinniped-supervisor` namespace with default options: - - `kubectl apply -f https://get.pinniped.dev/latest/install-pinniped-supervisor.yaml` - -## With specific version and default options - -1. Choose your preferred [release](https://github.com/vmware-tanzu/pinniped/releases) version number and use it to replace the version number in the URL below. - -1. Install the Supervisor into the `pinniped-supervisor` namespace with default options: - - `kubectl apply -f https://get.pinniped.dev/{{< latestversion >}}/install-pinniped-supervisor.yaml` - *Replace {{< latestversion >}} with your preferred version number.* - ## With custom options Pinniped uses [ytt](https://carvel.dev/ytt/) from [Carvel](https://carvel.dev/) as a templating system. -1. Install the `ytt` command-line tool using the instructions from the [Carvel documentation](https://carvel.dev/#whole-suite). +1. Install the `ytt` and `kapp` command-line tools using the instructions from the [Carvel documentation](https://carvel.dev/#whole-suite). 1. Clone the Pinniped GitHub repository and visit the `deploy/supervisor` directory: @@ -42,16 +43,14 @@ Pinniped uses [ytt](https://carvel.dev/ytt/) from [Carvel](https://carvel.dev/) 1. Decide which release version you would like to install. All release versions are [listed on GitHub](https://github.com/vmware-tanzu/pinniped/releases). -1. Checkout your preferred version tag, e.g. `{{< latestversion >}}`. +1. Checkout your preferred version tag, e.g. `{{< latestversion >}}`: - `git checkout {{< latestversion >}}` - *Replace {{< latestversion >}} with your preferred version number.* - 1. Customize configuration parameters: - Edit `values.yaml` with your custom values. - - Change the `image_tag` value to match your preferred version tag, e.g. `{{< latestversion >}}`. *Replace {{< latestversion >}} with your preferred version number.* + - Change the `image_tag` value to match your preferred version tag, e.g. `{{< latestversion >}}`. - See the [default values](http://github.com/vmware-tanzu/pinniped/tree/main/deploy/supervisor/values.yaml) for documentation about individual configuration parameters. 1. Render templated YAML manifests: @@ -60,12 +59,7 @@ Pinniped uses [ytt](https://carvel.dev/ytt/) from [Carvel](https://carvel.dev/) 1. Deploy the templated YAML manifests: - - *If you're using `kubectl`:* - - `ytt --file . | kubectl apply -f -` - - *If you're using [`kapp` from Carvel](https://carvel.dev/kapp/):* - - `ytt --file . | kapp deploy --yes --app pinniped-supervisor --diff-changes --file -` + `ytt --file . | kapp deploy --app pinniped-supervisor --file -` ## Next steps diff --git a/site/content/docs/tutorials/concierge-and-supervisor-demo.md b/site/content/docs/tutorials/concierge-and-supervisor-demo.md index 3d2ae8c2..8dcda1f5 100644 --- a/site/content/docs/tutorials/concierge-and-supervisor-demo.md +++ b/site/content/docs/tutorials/concierge-and-supervisor-demo.md @@ -144,12 +144,16 @@ to authenticate federated identities from the Supervisor. 1. Deploy the Pinniped Concierge. ```sh - kubectl apply \ - --context kind-pinniped-concierge \ - -f https://get.pinniped.dev/latest/install-pinniped-concierge.yaml + kubectl apply --context kind-pinniped-concierge \ + -f https://get.pinniped.dev/{{< latestversion >}}/install-pinniped-concierge-crds.yaml + kubectl apply --context kind-pinniped-concierge \ + -f https://get.pinniped.dev/{{< latestversion >}}/install-pinniped-concierge.yaml ``` - The `install-pinniped-concierge.yaml` file includes the default deployment options. + The `install-pinniped-concierge-crds.yaml` file contains the Concierge CustomResourceDefinitions. + These define the custom APIs that you use to configure and interact with the Concierge. + + The `install-pinniped-concierge.yaml` file includes the rest of the Concierge resources with default deployment options. If you would prefer to customize the available options, please see the [Concierge installation guide]({{< ref "../howto/install-concierge" >}}) for instructions on how to deploy using `ytt`. diff --git a/site/content/docs/tutorials/concierge-only-demo.md b/site/content/docs/tutorials/concierge-only-demo.md index 7ef3502d..de8eb88e 100644 --- a/site/content/docs/tutorials/concierge-only-demo.md +++ b/site/content/docs/tutorials/concierge-only-demo.md @@ -71,7 +71,7 @@ as the authenticator. an authenticator that works with your real identity provider, and therefore would not need to deploy or configure local-user-authenticator. ```sh - kubectl apply -f https://get.pinniped.dev/latest/install-local-user-authenticator.yaml + kubectl apply -f https://get.pinniped.dev/{{< latestversion >}}/install-local-user-authenticator.yaml ``` The `install-local-user-authenticator.yaml` file includes the default deployment options. @@ -79,8 +79,6 @@ as the authenticator. see [deploy/local-user-authenticator/README.md](https://github.com/vmware-tanzu/pinniped/blob/main/deploy/local-user-authenticator/README.md) for instructions on how to deploy using `ytt`. - If you prefer to install a specific version, replace `latest` in the URL with the version number such as `{{< latestversion >}}`. - 1. Create a test user named `pinny-the-seal` in the local-user-authenticator namespace. ```sh @@ -101,10 +99,14 @@ as the authenticator. 1. Deploy the Pinniped Concierge. ```sh - kubectl apply -f https://get.pinniped.dev/latest/install-pinniped-concierge.yaml + kubectl apply -f https://get.pinniped.dev/{{< latestversion >}}/install-pinniped-concierge-crds.yaml + kubectl apply -f https://get.pinniped.dev/{{< latestversion >}}/install-pinniped-concierge.yaml ``` - The `install-pinniped-concierge.yaml` file includes the default deployment options. + The `install-pinniped-concierge-crds.yaml` file contains the Concierge CustomResourceDefinitions. + These define the custom APIs that you use to configure and interact with the Concierge. + + The `install-pinniped-concierge.yaml` file includes the rest of the Concierge resources with default deployment options. If you would prefer to customize the available options, please see the [Concierge installation guide]({{< ref "../howto/install-concierge" >}}) for instructions on how to deploy using `ytt`. diff --git a/site/themes/pinniped/layouts/shortcodes/buttonlink.html b/site/themes/pinniped/layouts/shortcodes/buttonlink.html index 7895fc5e..ea46e14a 100644 --- a/site/themes/pinniped/layouts/shortcodes/buttonlink.html +++ b/site/themes/pinniped/layouts/shortcodes/buttonlink.html @@ -1,2 +1,2 @@ -{{- $href := .Get "href" -}} - \ No newline at end of file +{{- $filename := .Get "filename" -}} + \ No newline at end of file From 62afb34877da92c926bc5332ca94d5c186ab0ef3 Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Wed, 28 Jul 2021 16:09:15 -0500 Subject: [PATCH 11/34] Fix command typo and expand description of values.yaml a bit. Signed-off-by: Matt Moyer --- site/content/docs/howto/install-concierge.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/site/content/docs/howto/install-concierge.md b/site/content/docs/howto/install-concierge.md index a2786ba1..5c8afd31 100644 --- a/site/content/docs/howto/install-concierge.md +++ b/site/content/docs/howto/install-concierge.md @@ -24,7 +24,7 @@ If you'd prefer to customize the annotations or load balancer IP address, see th 1. Install the latest version of the Concierge into the `pinniped-concierge` namespace with default options using [kapp](https://carvel.dev/kapp/): - - `kapp deploy --app pinniped-concierge--file https://get.pinniped.dev/{{< latestversion >}}/install-pinniped-concierge.yaml` + - `kapp deploy --app pinniped-concierge --file https://get.pinniped.dev/{{< latestversion >}}/install-pinniped-concierge.yaml` ### Using kubectl @@ -61,6 +61,8 @@ Pinniped uses [ytt](https://carvel.dev/ytt/) from [Carvel](https://carvel.dev/) - Change the `image_tag` value to match your preferred version tag, e.g. `{{< latestversion >}}`. - See the [default values](http://github.com/vmware-tanzu/pinniped/tree/main/deploy/concierge/values.yaml) for documentation about individual configuration parameters. + For example, you can change the number of Concierge pods by setting `replicas` or apply custom annotations to the impersonation proxy service using `impersonation_proxy_spec`. + 1. Render templated YAML manifests: - `ytt --file .` From c3e037b24ef8f619b2e85c10f7d9e79a5a2dcae1 Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Thu, 29 Jul 2021 09:56:00 -0500 Subject: [PATCH 12/34] Fix a broken link in .../docs/howto/configure-supervisor.md. Signed-off-by: Matt Moyer --- site/content/docs/howto/configure-supervisor.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/docs/howto/configure-supervisor.md b/site/content/docs/howto/configure-supervisor.md index d20ab21e..89fd8d68 100644 --- a/site/content/docs/howto/configure-supervisor.md +++ b/site/content/docs/howto/configure-supervisor.md @@ -49,7 +49,7 @@ The most common ways are: and the service. For either of the first two options, if you installed using `ytt` then you can use -the related `service_*` options from [deploy/supervisor/values.yml](values.yaml) to create a Service. +the related `service_*` options from [deploy/supervisor/values.yml](https://github.com/vmware-tanzu/pinniped/blob/main/deploy/supervisor/values.yaml) to create a Service. If you installed using `install-supervisor.yaml` then you can create the Service separately after installing the Supervisor app. There is no `Ingress` included in the `ytt` templates, so if you choose to use an Ingress then you'll need to create that separately after installing the Supervisor app. From d23f3c94287ed325035d1316f17526a24f5b0e97 Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Thu, 29 Jul 2021 10:22:43 -0500 Subject: [PATCH 13/34] Update ROADMAP.md --- ROADMAP.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 0ac4d8f9..cf5c33ba 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -37,9 +37,9 @@ Last Updated: July 2021 Theme|Description|Timeline| |--|--|--| |Remote OIDC login support|Add support for logging in from remote hosts without web browsers in the Pinniped CLI and Supervisor|Jul 2021| -|AD Support|Extends upstream IDP protocols|Jul 2021| -|Wider Concierge cluster support|Support for more cluster types in the Concierge|Aug 2021| -|Multiple IDP support|Support multiple IDPs configured on a single Supervisor|Exploring/Ongoing| +|Active Directory Support|Extends upstream IDP protocols|Aug 2021| +|Multiple IDP support|Support multiple IDPs configured on a single Supervisor|Sept 2021| +|Wider Concierge cluster support|Support for more cluster types in the Concierge|Sept 2021| |Identity transforms|Support prefixing, filtering, or performing coarse-grained checks on upstream users and groups|Exploring/Ongoing| |Extended IDP support|Support more types of identity providers on the Supervisor|Exploring/Ongoing| |Improved Documentation|Reorganizing and improving Pinniped docs; new how-to guides and tutorials|Exploring/Ongoing| From 22be97eeda9ecfd42c0326ed7548e75c25b12d7c Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Thu, 29 Jul 2021 13:34:37 -0400 Subject: [PATCH 14/34] concierge_impersonation_proxy_test: check all forms of DNS Signed-off-by: Monis Khan --- .../concierge_impersonation_proxy_test.go | 50 +++++++++++++++++-- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/test/integration/concierge_impersonation_proxy_test.go b/test/integration/concierge_impersonation_proxy_test.go index ea305aa6..5e3c368f 100644 --- a/test/integration/concierge_impersonation_proxy_test.go +++ b/test/integration/concierge_impersonation_proxy_test.go @@ -1733,14 +1733,54 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl func ensureDNSResolves(t *testing.T, urlString string) { t.Helper() + parsedURL, err := url.Parse(urlString) require.NoError(t, err) - if net.ParseIP(parsedURL.Host) == nil { - testlib.RequireEventually(t, func(requireEventually *require.Assertions) { - _, err = net.LookupIP(parsedURL.Host) - requireEventually.NoError(err) - }, 5*time.Minute, 1*time.Second) + + host := parsedURL.Hostname() + + if net.ParseIP(host) != nil { + return // ignore IPs } + + var d net.Dialer + loggingDialer := func(ctx context.Context, network, address string) (net.Conn, error) { + t.Logf("dns lookup, network=%s address=%s", network, address) + conn, connErr := d.DialContext(ctx, network, address) + if connErr != nil { + t.Logf("dns lookup, err=%v", connErr) + } else { + local := conn.LocalAddr() + remote := conn.RemoteAddr() + t.Logf("dns lookup, local conn network=%s addr=%s", local.Network(), local.String()) + t.Logf("dns lookup, remote conn network=%s addr=%s", remote.Network(), remote.String()) + } + return conn, connErr + } + + goResolver := &net.Resolver{ + PreferGo: true, + StrictErrors: true, + Dial: loggingDialer, + } + notGoResolver := &net.Resolver{ + PreferGo: false, + StrictErrors: true, + Dial: loggingDialer, + } + + testlib.RequireEventually(t, func(requireEventually *require.Assertions) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + for _, resolver := range []*net.Resolver{goResolver, notGoResolver} { + resolver := resolver + + ips, ipErr := resolver.LookupIPAddr(ctx, host) + requireEventually.NoError(ipErr) + requireEventually.NotEmpty(ips) + } + }, 5*time.Minute, 1*time.Second) } func createTestNamespace(t *testing.T, adminClient kubernetes.Interface) string { From 1e32530d7bc81744a55c84d3dda4e05266b13022 Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Thu, 29 Jul 2021 17:49:16 -0500 Subject: [PATCH 15/34] Fix broken TTY after manual auth code prompt. This may be a temporary fix. It switches the manual auth code prompt to use `promptForValue()` instead of `promptForSecret()`. The `promptForSecret()` function no longer supports cancellation (the v0.9.2 behavior) and the method of cancelling in `promptForValue()` is now based on running the blocking read in a background goroutine, which is allowed to block forever or leak (which is not important for our CLI use case). This means that the authorization code is now visible in the user's terminal, but this is really not a big deal because of PKCE and the limited lifetime of an auth code. The main goroutine now correctly waits for the "manual prompt" goroutine to clean up, which now includes printing the extra newline that would normally have been entered by the user in the manual flow. The text of the manual login prompt is updated to be more concise and less scary (don't use the word "fail"). Signed-off-by: Matt Moyer --- pkg/oidcclient/login.go | 95 ++++++++++++++++++++---------------- pkg/oidcclient/login_test.go | 20 ++++---- test/integration/e2e_test.go | 4 +- 3 files changed, 66 insertions(+), 53 deletions(-) diff --git a/pkg/oidcclient/login.go b/pkg/oidcclient/login.go index c649f4fe..a1b5e0b6 100644 --- a/pkg/oidcclient/login.go +++ b/pkg/oidcclient/login.go @@ -18,6 +18,7 @@ import ( "os" "sort" "strings" + "sync" "time" "github.com/coreos/go-oidc/v3/oidc" @@ -112,7 +113,7 @@ type handlerState struct { getProvider func(*oauth2.Config, *oidc.Provider, *http.Client) provider.UpstreamOIDCIdentityProviderI validateIDToken func(ctx context.Context, provider *oidc.Provider, audience string, token string) (*oidc.IDToken, error) promptForValue func(ctx context.Context, promptLabel string) (string, error) - promptForSecret func(ctx context.Context, promptLabel string) (string, error) + promptForSecret func(promptLabel string) (string, error) callbacks chan callbackResult } @@ -275,7 +276,7 @@ func Login(issuer string, clientID string, opts ...Option) (*oidctypes.Token, er callbackPath: "/callback", ctx: context.Background(), logger: logr.Discard(), // discard logs unless a logger is specified - callbacks: make(chan callbackResult), + callbacks: make(chan callbackResult, 2), httpClient: http.DefaultClient, // Default implementations of external dependencies (to be mocked in tests). @@ -520,7 +521,7 @@ func (h *handlerState) getUsernameAndPassword() (string, string, error) { password := h.getEnv(defaultPasswordEnvVarName) if password == "" { - password, err = h.promptForSecret(h.ctx, defaultLDAPPasswordPrompt) + password, err = h.promptForSecret(defaultLDAPPasswordPrompt) if err != nil { return "", "", fmt.Errorf("error prompting for password: %w", err) } @@ -576,11 +577,13 @@ func (h *handlerState) webBrowserBasedAuth(authorizeOptions *[]oauth2.AuthCodeOp h.logger.V(debugLogLevel).Error(err, "could not open browser") } - ctx, cancel := context.WithCancel(h.ctx) - defer cancel() - // Prompt the user to visit the authorize URL, and to paste a manually-copied auth code (if possible). - h.promptForWebLogin(ctx, authorizeURL, os.Stderr) + ctx, cancel := context.WithCancel(h.ctx) + cleanupPrompt := h.promptForWebLogin(ctx, authorizeURL, os.Stderr) + defer func() { + cancel() + cleanupPrompt() + }() // Wait for either the web callback, a pasted auth code, or a timeout. select { @@ -594,26 +597,38 @@ func (h *handlerState) webBrowserBasedAuth(authorizeOptions *[]oauth2.AuthCodeOp } } -func (h *handlerState) promptForWebLogin(ctx context.Context, authorizeURL string, out io.Writer) { +func (h *handlerState) promptForWebLogin(ctx context.Context, authorizeURL string, out io.Writer) func() { _, _ = fmt.Fprintf(out, "Log in by visiting this link:\n\n %s\n\n", authorizeURL) // If stdin is not a TTY, print the URL but don't prompt for the manual paste, // since we have no way of reading it. if !h.isTTY(stdin()) { - return + return func() {} } // If the server didn't support response_mode=form_post, don't bother prompting for the manual // code because the user isn't going to have any easy way to manually copy it anyway. if !h.useFormPost { - return + return func() {} } // Launch the manual auth code prompt in a background goroutine, which will be cancelled // if the parent context is cancelled (when the login succeeds or times out). + var wg sync.WaitGroup + wg.Add(1) go func() { - code, err := h.promptForSecret(ctx, " If automatic login fails, paste your authorization code to login manually: ") + defer func() { + // Always emit a newline so the kubectl output is visually separated from the login prompts. + _, _ = fmt.Fprintln(os.Stderr) + + wg.Done() + }() + code, err := h.promptForValue(ctx, " Optionally, paste your authorization code: ") if err != nil { + // Print a visual marker to show the the prompt is no longer waiting for user input, plus a trailing + // newline that simulates the user having pressed "enter". + _, _ = fmt.Fprint(os.Stderr, "[...]\n") + h.callbacks <- callbackResult{err: fmt.Errorf("failed to prompt for manual authorization code: %v", err)} return } @@ -622,6 +637,7 @@ func (h *handlerState) promptForWebLogin(ctx context.Context, authorizeURL strin token, err := h.redeemAuthCode(ctx, code) h.callbacks <- callbackResult{token: token, err: err} }() + return wg.Wait } func promptForValue(ctx context.Context, promptLabel string) (string, error) { @@ -633,23 +649,30 @@ func promptForValue(ctx context.Context, promptLabel string) (string, error) { return "", fmt.Errorf("could not print prompt to stderr: %w", err) } - // If the context is canceled, set the read deadline on stdin so the read immediately finishes. - ctx, cancel := context.WithCancel(ctx) - defer cancel() + type readResult struct { + text string + err error + } + readResults := make(chan readResult) go func() { - <-ctx.Done() - _ = os.Stdin.SetReadDeadline(time.Now()) + text, err := bufio.NewReader(os.Stdin).ReadString('\n') + readResults <- readResult{text, err} + close(readResults) }() - text, err := bufio.NewReader(os.Stdin).ReadString('\n') - if err != nil { - return "", fmt.Errorf("could read input from stdin: %w", err) + // If the context is canceled, return immediately. The ReadString() operation will stay hung in the background + // goroutine indefinitely. + ctx, cancel := context.WithCancel(ctx) + defer cancel() + select { + case <-ctx.Done(): + return "", ctx.Err() + case r := <-readResults: + return strings.TrimSpace(r.text), r.err } - text = strings.TrimSpace(text) - return text, nil } -func promptForSecret(ctx context.Context, promptLabel string) (string, error) { +func promptForSecret(promptLabel string) (string, error) { if !term.IsTerminal(stdin()) { return "", errors.New("stdin is not connected to a terminal") } @@ -657,27 +680,17 @@ func promptForSecret(ctx context.Context, promptLabel string) (string, error) { if err != nil { return "", fmt.Errorf("could not print prompt to stderr: %w", err) } - - // If the context is canceled, set the read deadline on stdin so the read immediately finishes. - ctx, cancel := context.WithCancel(ctx) - defer cancel() - go func() { - <-ctx.Done() - _ = os.Stdin.SetReadDeadline(time.Now()) - - // term.ReadPassword swallows the newline that was typed by the user, so to - // avoid the next line of output from happening on same line as the password - // prompt, we need to print a newline. - // - // Even if the read was cancelled prematurely, we still want to echo a newline so whatever comes next - // on stderr is formatted correctly. - _, _ = fmt.Fprint(os.Stderr, "\n") - }() - password, err := term.ReadPassword(stdin()) if err != nil { return "", fmt.Errorf("could not read password: %w", err) } + // term.ReadPassword swallows the newline that was typed by the user, so to + // avoid the next line of output from happening on same line as the password + // prompt, we need to print a newline. + _, err = fmt.Fprint(os.Stderr, "\n") + if err != nil { + return "", fmt.Errorf("could not print newline to stderr: %w", err) + } return string(password), err } @@ -882,9 +895,9 @@ func (h *handlerState) serve(listener net.Listener) func() { } go func() { _ = srv.Serve(listener) }() return func() { - // Gracefully shut down the server, allowing up to 5 00ms for + // Gracefully shut down the server, allowing up to 100ms for // clients to receive any in-flight responses. - shutdownCtx, cancel := context.WithTimeout(h.ctx, 500*time.Millisecond) + shutdownCtx, cancel := context.WithTimeout(h.ctx, 100*time.Millisecond) _ = srv.Shutdown(shutdownCtx) cancel() } diff --git a/pkg/oidcclient/login_test.go b/pkg/oidcclient/login_test.go index 4edd1e9a..94dc24c4 100644 --- a/pkg/oidcclient/login_test.go +++ b/pkg/oidcclient/login_test.go @@ -251,7 +251,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.promptForValue = func(_ context.Context, promptLabel string) (string, error) { return "some-upstream-username", nil } - h.promptForSecret = func(_ context.Context, _ string) (string, error) { return "some-upstream-password", nil } + h.promptForSecret = func(_ string) (string, error) { return "some-upstream-password", nil } cache := &mockSessionCache{t: t, getReturnsToken: nil} cacheKey := SessionCacheKey{ @@ -541,7 +541,7 @@ func TestLogin(t *testing.T) { // nolint:gocyclo require.Equal(t, "form_post", parsed.Query().Get("response_mode")) return fmt.Errorf("some browser open error") } - h.promptForSecret = func(ctx context.Context, promptLabel string) (string, error) { + h.promptForValue = func(_ context.Context, promptLabel string) (string, error) { return "", fmt.Errorf("some prompt error") } return nil @@ -567,7 +567,7 @@ func TestLogin(t *testing.T) { // nolint:gocyclo require.Equal(t, "form_post", parsed.Query().Get("response_mode")) return nil } - h.promptForSecret = func(ctx context.Context, promptLabel string) (string, error) { + h.promptForValue = func(_ context.Context, promptLabel string) (string, error) { return "", fmt.Errorf("some prompt error") } return nil @@ -825,7 +825,7 @@ func TestLogin(t *testing.T) { // nolint:gocyclo opt: func(t *testing.T) Option { return func(h *handlerState) error { _ = defaultLDAPTestOpts(t, h, nil, nil) - h.promptForSecret = func(_ context.Context, _ string) (string, error) { return "", errors.New("some prompt error") } + h.promptForSecret = func(_ string) (string, error) { return "", errors.New("some prompt error") } return nil } }, @@ -1018,7 +1018,7 @@ func TestLogin(t *testing.T) { // nolint:gocyclo require.Equal(t, "Username: ", promptLabel) return "some-upstream-username", nil } - h.promptForSecret = func(_ context.Context, promptLabel string) (string, error) { + h.promptForSecret = func(promptLabel string) (string, error) { require.Equal(t, "Password: ", promptLabel) return "some-upstream-password", nil } @@ -1125,7 +1125,7 @@ func TestLogin(t *testing.T) { // nolint:gocyclo require.FailNow(t, fmt.Sprintf("saw unexpected prompt from the CLI: %q", promptLabel)) return "", nil } - h.promptForSecret = func(_ context.Context, promptLabel string) (string, error) { + h.promptForSecret = func(promptLabel string) (string, error) { require.FailNow(t, fmt.Sprintf("saw unexpected prompt from the CLI: %q", promptLabel)) return "", nil } @@ -1634,8 +1634,8 @@ func TestHandlePasteCallback(t *testing.T) { return func(h *handlerState) error { h.isTTY = func(fd int) bool { return true } h.useFormPost = true - h.promptForSecret = func(ctx context.Context, promptLabel string) (string, error) { - assert.Equal(t, " If automatic login fails, paste your authorization code to login manually: ", promptLabel) + h.promptForValue = func(_ context.Context, promptLabel string) (string, error) { + assert.Equal(t, " Optionally, paste your authorization code: ", promptLabel) return "", fmt.Errorf("some prompt error") } return nil @@ -1651,7 +1651,7 @@ func TestHandlePasteCallback(t *testing.T) { return func(h *handlerState) error { h.isTTY = func(fd int) bool { return true } h.useFormPost = true - h.promptForSecret = func(ctx context.Context, promptLabel string) (string, error) { + h.promptForValue = func(_ context.Context, promptLabel string) (string, error) { return "invalid", nil } h.oauth2Config = &oauth2.Config{RedirectURL: testRedirectURI} @@ -1675,7 +1675,7 @@ func TestHandlePasteCallback(t *testing.T) { return func(h *handlerState) error { h.isTTY = func(fd int) bool { return true } h.useFormPost = true - h.promptForSecret = func(ctx context.Context, promptLabel string) (string, error) { + h.promptForValue = func(_ context.Context, promptLabel string) (string, error) { return "valid", nil } h.oauth2Config = &oauth2.Config{RedirectURL: testRedirectURI} diff --git a/test/integration/e2e_test.go b/test/integration/e2e_test.go index 2086de3e..2a67726e 100644 --- a/test/integration/e2e_test.go +++ b/test/integration/e2e_test.go @@ -331,9 +331,9 @@ func TestE2EFullIntegration(t *testing.T) { // Wait for the subprocess to print the login prompt. t.Logf("waiting for CLI to output login URL and manual prompt") - output := readFromFileUntilStringIsSeen(t, ptyFile, "If automatic login fails, paste your authorization code to login manually: ") + output := readFromFileUntilStringIsSeen(t, ptyFile, "Optionally, paste your authorization code: ") require.Contains(t, output, "Log in by visiting this link:") - require.Contains(t, output, "If automatic login fails, paste your authorization code to login manually: ") + require.Contains(t, output, "Optionally, paste your authorization code: ") // Find the line with the login URL. var loginURL string From 7773fb8afe85b247a5586b85faa64f09ba426f7e Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Fri, 30 Jul 2021 15:30:56 -0500 Subject: [PATCH 16/34] Add v0.10.0 blog post. Signed-off-by: Matt Moyer --- ...-07-30-supporting-remote-oidc-workflows.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 site/content/posts/2021-07-30-supporting-remote-oidc-workflows.md diff --git a/site/content/posts/2021-07-30-supporting-remote-oidc-workflows.md b/site/content/posts/2021-07-30-supporting-remote-oidc-workflows.md new file mode 100644 index 00000000..7c7e2a27 --- /dev/null +++ b/site/content/posts/2021-07-30-supporting-remote-oidc-workflows.md @@ -0,0 +1,50 @@ +--- +title: "Pinniped v0.10.0: Managing OIDC Login Flows in Browserless Environments" +slug: supporting-remote-oidc-workflows +date: 2021-07-30 +author: Anjali Telang +image: https://images.unsplash.com/photo-1539830801588-496be8036aca?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=2250&q=80 +excerpt: "With the release of v0.10.0, Pinniped now supports Kubernetes clusters behind firewalls or in restricted environments" +tags: ['Matt Moyer', 'Anjali Telang', 'release'] +--- + +![seal on rock](https://images.unsplash.com/photo-1539830801588-496be8036aca?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=2250&q=80) +*Photo by [Jaddy Liu](https://unsplash.com/@saintjaddy) on [Unsplash](https://unsplash.com/s/photos/seal)* + +## Remote Host Environments and OIDC login flows + +Enterprise workloads on Kubernetes clusters often run in a restricted environment behind a firewall. In such a setup, the clusters can be accessed via servers sometimes called “SSH jump hosts”. These servers pose restrictions on what the users can execute and typically allow only command line access. Users can use command line utilities such as kubectl, pinniped CLI, etc. on these servers to access the Kubernetes clusters. However, this poses a problem for the OIDC login workflows since they require a browser to complete the authentication workflow. + +## Solution for Browserless clients + +In this release, we introduce the ability to use a manual workaround to complete the OIDC workflow in such restricted browserless environments by supporting `response_mode=form_post` in the Pinniped Supervisor. As described in the [OAuth 2.0 Form Post spec](https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html), the response parameters are “encoded as HTML form values that are auto-submitted in the User Agent, and thus are transmitted via the HTTP POST method to the Client”. To complete the authentication process, The Pinniped users can copy and paste the response from the HTML page hosted by the Pinniped Supervisor into the waiting CLI process on the Jump Host. + +You can find more details in our [design document](https://hackmd.io/Hx17ATt_QpGOdLH_7AH1jA). + +## Demo + +{{< youtube id="01QD8EbN_H8" title="New SSH jump host support in Pinniped v0.10.0" >}} + +## High level overview of the workflow + +### Prerequisites + +1. Pinniped Concierge is installed with JWTAuthenticator on the Kubernetes cluster. +2. Pinniped Supervisor is configured with OIDC Identity Provider on the Kubernetes cluster. +3. “Jump Host” server/machine with Kubectl and Pinniped CLI is installed but has no web-browser. +4. Desktop environment with a web browser for the User is available. +5. Kubeconfig pointing to the Kubernetes cluster is available. + +### Workflow + +1. User accesses the Jump Host via SSH. +2. User initiates a kubectl command with kubeconfig pointing to the cluster. +3. User is prompted to complete the login process using the Desktop web-browser. +4. User competes the web-browser OIDC workflow and gets an authorization response code. +5. User will copy-paste the authorization code into the Jump Host environment to complete the login. + +Additionally, the v0.10.0 includes support for non-interactive password based LDAP logins. This feature provides the ability for Jenkins as well as other CI/CD tools that use LDAP Identity Platforms to access the cluster with centralized service account identities from the LDAP directory + +We invite your suggestions and contributions to make Pinniped work across all flavors of Kubernetes. + +{{< community >}} From 65fa47cbcdcd923ce667c0d0d589478bd0e5058b Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Fri, 30 Jul 2021 16:35:38 -0500 Subject: [PATCH 17/34] Link to the release from our v0.10.0 blog post. Signed-off-by: Matt Moyer --- .../posts/2021-07-30-supporting-remote-oidc-workflows.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/content/posts/2021-07-30-supporting-remote-oidc-workflows.md b/site/content/posts/2021-07-30-supporting-remote-oidc-workflows.md index 7c7e2a27..4cbc596d 100644 --- a/site/content/posts/2021-07-30-supporting-remote-oidc-workflows.md +++ b/site/content/posts/2021-07-30-supporting-remote-oidc-workflows.md @@ -13,11 +13,11 @@ tags: ['Matt Moyer', 'Anjali Telang', 'release'] ## Remote Host Environments and OIDC login flows -Enterprise workloads on Kubernetes clusters often run in a restricted environment behind a firewall. In such a setup, the clusters can be accessed via servers sometimes called “SSH jump hosts”. These servers pose restrictions on what the users can execute and typically allow only command line access. Users can use command line utilities such as kubectl, pinniped CLI, etc. on these servers to access the Kubernetes clusters. However, this poses a problem for the OIDC login workflows since they require a browser to complete the authentication workflow. +Enterprise workloads on Kubernetes clusters often run in a restricted environment behind a firewall. In such a setup, the clusters can be accessed via servers sometimes called “SSH jump hosts”. These servers pose restrictions on what the users can execute and typically allow only command line access. Users can use command line utilities such as kubectl, `pinniped` CLI, etc. on these servers to access the Kubernetes clusters. However, this poses a problem for the OIDC login workflows since they require a browser to complete the authentication workflow. ## Solution for Browserless clients -In this release, we introduce the ability to use a manual workaround to complete the OIDC workflow in such restricted browserless environments by supporting `response_mode=form_post` in the Pinniped Supervisor. As described in the [OAuth 2.0 Form Post spec](https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html), the response parameters are “encoded as HTML form values that are auto-submitted in the User Agent, and thus are transmitted via the HTTP POST method to the Client”. To complete the authentication process, The Pinniped users can copy and paste the response from the HTML page hosted by the Pinniped Supervisor into the waiting CLI process on the Jump Host. +In the [v0.10.0 release](https://github.com/vmware-tanzu/pinniped/releases/tag/v0.10.0), we introduce the ability to use a manual workaround to complete the OIDC workflow in such restricted browserless environments by supporting `response_mode=form_post` in the Pinniped Supervisor. As described in the [OAuth 2.0 Form Post spec](https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html), the response parameters are “encoded as HTML form values that are auto-submitted in the User Agent, and thus are transmitted via the HTTP POST method to the Client”. To complete the authentication process, The Pinniped users can copy and paste the response from the HTML page hosted by the Pinniped Supervisor into the waiting CLI process on the Jump Host. You can find more details in our [design document](https://hackmd.io/Hx17ATt_QpGOdLH_7AH1jA). @@ -43,7 +43,7 @@ You can find more details in our [design document](https://hackmd.io/Hx17ATt_QpG 4. User competes the web-browser OIDC workflow and gets an authorization response code. 5. User will copy-paste the authorization code into the Jump Host environment to complete the login. -Additionally, the v0.10.0 includes support for non-interactive password based LDAP logins. This feature provides the ability for Jenkins as well as other CI/CD tools that use LDAP Identity Platforms to access the cluster with centralized service account identities from the LDAP directory +Additionally, the [v0.10.0 release](https://github.com/vmware-tanzu/pinniped/releases/tag/v0.10.0) includes support for non-interactive password based LDAP logins. This feature provides the ability for Jenkins as well as other CI/CD tools that use LDAP Identity Platforms to access the cluster with centralized service account identities from the LDAP directory We invite your suggestions and contributions to make Pinniped work across all flavors of Kubernetes. From ac7d65c4a8807deb006894db25f0db7bea63c693 Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Sun, 1 Aug 2021 18:19:53 -0400 Subject: [PATCH 18/34] concierge_impersonation_proxy_test: run slowly for EKS Signed-off-by: Monis Khan --- .../concierge_impersonation_proxy_test.go | 61 +++++++++++-------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/test/integration/concierge_impersonation_proxy_test.go b/test/integration/concierge_impersonation_proxy_test.go index 5e3c368f..2b5a0552 100644 --- a/test/integration/concierge_impersonation_proxy_test.go +++ b/test/integration/concierge_impersonation_proxy_test.go @@ -107,7 +107,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl impersonatorShouldHaveStartedAutomaticallyByDefault := !env.HasCapability(testlib.ClusterSigningKeyIsAvailable) clusterSupportsLoadBalancers := env.HasCapability(testlib.HasExternalLoadBalancerProvider) - ctx, cancel := context.WithTimeout(context.Background(), 20*time.Minute) + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) defer cancel() // Create a client using the admin kubeconfig. @@ -333,8 +333,13 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl ) } + if env.KubernetesDistribution == testlib.EKSDistro { + t.Log("eks: sleeping for 10 minutes to allow DNS propagation") + time.Sleep(10 * time.Minute) + } + t.Run("kubectl port-forward and keeping the connection open for over a minute (non-idle)", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) kubeconfigPath, envVarsWithProxy, _ := getImpersonationKubeconfig(t, env, impersonationProxyURL, impersonationProxyCACertPEM, credentialRequestSpecWithWorkingCredentials.Authenticator) // Run the kubectl port-forward command. @@ -392,7 +397,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl }) t.Run("kubectl port-forward and keeping the connection open for over a minute (idle)", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) kubeconfigPath, envVarsWithProxy, _ := getImpersonationKubeconfig(t, env, impersonationProxyURL, impersonationProxyCACertPEM, credentialRequestSpecWithWorkingCredentials.Authenticator) // Run the kubectl port-forward command. @@ -430,7 +435,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl }) t.Run("using and watching all the basic verbs", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) // Create a namespace, because it will be easier to exercise "deletecollection" if we have a namespace. namespaceName := createTestNamespace(t, adminClient) @@ -560,7 +565,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl }) t.Run("nested impersonation as a regular user is allowed if they have enough RBAC permissions", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) // Make a client which will send requests through the impersonation proxy and will also add // impersonate headers to the request. nestedImpersonationClient := newImpersonationProxyClient(t, impersonationProxyURL, impersonationProxyCACertPEM, @@ -633,7 +638,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl }) t.Run("nested impersonation as a cluster admin user is allowed", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) // Copy the admin credentials from the admin kubeconfig. adminClientRestConfig := testlib.NewClientConfig(t) clusterAdminCredentials := getCredForConfig(t, adminClientRestConfig) @@ -709,7 +714,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl }) t.Run("nested impersonation as a cluster admin fails on reserved key", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) adminClientRestConfig := testlib.NewClientConfig(t) clusterAdminCredentials := getCredForConfig(t, adminClientRestConfig) @@ -747,7 +752,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl // this works because impersonation cannot set UID and thus the final user info the proxy sees has no UID t.Run("nested impersonation as a service account is allowed if it has enough RBAC permissions", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) namespaceName := createTestNamespace(t, adminClient) saName, saToken, saUID := createServiceAccountToken(ctx, t, adminClient, namespaceName) nestedImpersonationClient := newImpersonationProxyClientWithCredentials(t, @@ -794,7 +799,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl }) t.Run("WhoAmIRequests and different kinds of authentication through the impersonation proxy", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) // Test using the TokenCredentialRequest for authentication. impersonationProxyPinnipedConciergeClient := newImpersonationProxyClient(t, impersonationProxyURL, impersonationProxyCACertPEM, nil, refreshCredential, @@ -981,7 +986,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl }) t.Run("kubectl as a client", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) kubeconfigPath, envVarsWithProxy, tempDir := getImpersonationKubeconfig(t, env, impersonationProxyURL, impersonationProxyCACertPEM, credentialRequestSpecWithWorkingCredentials.Authenticator) // Try "kubectl exec" through the impersonation proxy. @@ -1063,7 +1068,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl }) t.Run("websocket client", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) namespaceName := createTestNamespace(t, adminClient) impersonationRestConfig := impersonationProxyRestConfig( @@ -1142,7 +1147,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl }) t.Run("http2 client", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) namespaceName := createTestNamespace(t, adminClient) wantConfigMapLabelKey, wantConfigMapLabelValue := "some-label-key", "some-label-value" @@ -1235,7 +1240,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl }) t.Run("honors anonymous authentication of KAS", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) impersonationProxyAnonymousClient := newAnonymousImpersonationProxyClient( t, impersonationProxyURL, impersonationProxyCACertPEM, nil, @@ -1261,14 +1266,14 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl require.NoError(t, err) t.Run("anonymous authentication irrelevant", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) // - hit the token credential request endpoint with an empty body // - through the impersonation proxy // - should succeed as an invalid request whether anonymous authentication is enabled or disabled // - should not reject as unauthorized t.Run("token credential request", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) tkr, err := impersonationProxyAnonymousClient.PinnipedConcierge.LoginV1alpha1().TokenCredentialRequests(). Create(ctx, &loginv1alpha1.TokenCredentialRequest{ @@ -1289,7 +1294,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl // - healthz should succeed, anonymous users can request this endpoint // - healthz/log should fail, forbidden anonymous t.Run("non-resource request while impersonating anonymous - nested impersonation", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) whoami, errWho := impersonationProxyAdminRestClientAsAnonymous.Post().Body([]byte(`{}`)).AbsPath("/apis/identity.concierge." + env.APIGroupSuffix + "/v1alpha1/whoamirequests").DoRaw(ctx) require.NoError(t, errWho, testlib.Sdump(errWho)) @@ -1307,7 +1312,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl t.Run("anonymous authentication enabled", func(t *testing.T) { testlib.IntegrationEnv(t).WithCapability(testlib.AnonymousAuthenticationSupported) - t.Parallel() + parallelIfNotEKS(t) // anonymous auth enabled // - hit the healthz endpoint (non-resource endpoint) @@ -1315,7 +1320,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl // - should succeed 200 // - should respond "ok" t.Run("non-resource request", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) healthz, errHealth := impersonationProxyAnonymousRestClient.Get().AbsPath("/healthz").DoRaw(ctx) require.NoError(t, errHealth, testlib.Sdump(errHealth)) @@ -1327,7 +1332,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl // - should fail forbidden // - system:anonymous cannot get pods t.Run("resource", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) pod, err := impersonationProxyAnonymousClient.Kubernetes.CoreV1().Pods(metav1.NamespaceSystem). Get(ctx, "does-not-matter", metav1.GetOptions{}) @@ -1342,7 +1347,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl // - should succeed 200 // - should respond "you are system:anonymous" t.Run("pinniped resource request", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) whoAmI, err := impersonationProxyAnonymousClient.PinnipedConcierge.IdentityV1alpha1().WhoAmIRequests(). Create(ctx, &identityv1alpha1.WhoAmIRequest{}, metav1.CreateOptions{}) @@ -1360,14 +1365,14 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl t.Run("anonymous authentication disabled", func(t *testing.T) { testlib.IntegrationEnv(t).WithoutCapability(testlib.AnonymousAuthenticationSupported) - t.Parallel() + parallelIfNotEKS(t) // - hit the healthz endpoint (non-resource endpoint) // - through the impersonation proxy // - should fail unauthorized // - kube api server should reject it t.Run("non-resource request", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) healthz, err := impersonationProxyAnonymousRestClient.Get().AbsPath("/healthz").DoRaw(ctx) require.True(t, k8serrors.IsUnauthorized(err), testlib.Sdump(err)) @@ -1379,7 +1384,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl // - should fail unauthorized // - kube api server should reject it t.Run("resource", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) pod, err := impersonationProxyAnonymousClient.Kubernetes.CoreV1().Pods(metav1.NamespaceSystem). Get(ctx, "does-not-matter", metav1.GetOptions{}) @@ -1392,7 +1397,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl // - should fail unauthorized // - kube api server should reject it t.Run("pinniped resource request", func(t *testing.T) { - t.Parallel() + parallelIfNotEKS(t) whoAmI, err := impersonationProxyAnonymousClient.PinnipedConcierge.IdentityV1alpha1().WhoAmIRequests(). Create(ctx, &identityv1alpha1.WhoAmIRequest{}, metav1.CreateOptions{}) @@ -2326,3 +2331,11 @@ func getUIDAndExtraViaCSR(ctx context.Context, t *testing.T, uid string, client return outUID, csReq.Spec.Extra } + +func parallelIfNotEKS(t *testing.T) { + if testlib.IntegrationEnv(t).KubernetesDistribution == testlib.EKSDistro { + return + } + + t.Parallel() +} From a464c817110aed4df0dcf9a7009b6bbde0813ee5 Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Tue, 3 Aug 2021 09:21:54 -0500 Subject: [PATCH 19/34] Bump latest version on website. We accidentally missed this in the v0.10.0 release process. The new YAML field here should make it easier to automate this step, which seems like a really good idea. --- site/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/config.yaml b/site/config.yaml index 0c1355f5..92da536d 100644 --- a/site/config.yaml +++ b/site/config.yaml @@ -7,7 +7,7 @@ params: github_url: "https://github.com/vmware-tanzu/pinniped" slack_url: "https://kubernetes.slack.com/messages/pinniped" community_url: "https://go.pinniped.dev/community" - latest_version: v0.9.2 + latest_version: v0.10.0 pygmentsCodefences: true pygmentsStyle: "pygments" markup: From 58bbffded4b4dc02646a1aac77c7fb69b6ae17c8 Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Mon, 26 Jul 2021 11:18:43 -0500 Subject: [PATCH 20/34] Switch to a slimmer distroless base image. At a high level, it switches us to a distroless base container image, but that also includes several related bits: - Add a writable /tmp but make the rest of our filesystems read-only at runtime. - Condense our main server binaries into a single pinniped-server binary. This saves a bunch of space in the image due to duplicated library code. The correct behavior is dispatched based on `os.Args[0]`, and the `pinniped-server` binary is symlinked to `pinniped-concierge` and `pinniped-supervisor`. - Strip debug symbols from our binaries. These aren't really useful in a distroless image anyway and all the normal stuff you'd expect to work, such as stack traces, still does. - Add a separate `pinniped-concierge-kube-cert-agent` binary with "sleep" and "print" functionality instead of using builtin /bin/sleep and /bin/cat for the kube-cert-agent. This is split from the main server binary because the loading/init time of the main server binary was too large for the tiny resource footprint we established in our kube-cert-agent PodSpec. Using a separate binary eliminates this issue and the extra binary adds only around 1.5MiB of image size. - Switch the kube-cert-agent code to use a JSON `{"tls.crt": "", "tls.key": ""}` format. This is more robust to unexpected input formatting than the old code, which simply concatenated the files with some extra newlines and split on whitespace. - Update integration tests that made now-invalid assumptions about the `pinniped-server` image. Signed-off-by: Matt Moyer --- Dockerfile | 30 ++-- .../main.go | 55 ++++++++ .../main_test.go | 128 ++++++++++++++++++ .../testdata/test.crt | 17 +++ .../testdata/test.key | 27 ++++ cmd/pinniped-concierge/main.go | 35 ----- cmd/pinniped-server/main.go | 41 ++++++ cmd/pinniped-server/main_test.go | 72 ++++++++++ deploy/concierge/deployment.yaml | 12 +- .../local-user-authenticator/deployment.yaml | 4 +- deploy/supervisor/deployment.yaml | 8 +- internal/concierge/server/server.go | 23 ++++ .../controller/kubecertagent/kubecertagent.go | 29 ++-- .../kubecertagent/kubecertagent_test.go | 98 +++++++++++++- .../localuserauthenticator.go | 6 +- .../localuserauthenticator_test.go | 2 +- .../supervisor/server/server.go | 5 +- .../concierge_impersonation_proxy_test.go | 46 ++++--- test/testlib/client.go | 5 +- 19 files changed, 538 insertions(+), 105 deletions(-) create mode 100644 cmd/pinniped-concierge-kube-cert-agent/main.go create mode 100644 cmd/pinniped-concierge-kube-cert-agent/main_test.go create mode 100644 cmd/pinniped-concierge-kube-cert-agent/testdata/test.crt create mode 100644 cmd/pinniped-concierge-kube-cert-agent/testdata/test.key delete mode 100644 cmd/pinniped-concierge/main.go create mode 100644 cmd/pinniped-server/main.go create mode 100644 cmd/pinniped-server/main_test.go rename cmd/local-user-authenticator/main.go => internal/localuserauthenticator/localuserauthenticator.go (98%) rename cmd/local-user-authenticator/main_test.go => internal/localuserauthenticator/localuserauthenticator_test.go (99%) rename cmd/pinniped-supervisor/main.go => internal/supervisor/server/server.go (99%) diff --git a/Dockerfile b/Dockerfile index 8ff1e547..0bf8054a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,26 +16,18 @@ RUN \ --mount=type=cache,target=/cache/gocache \ --mount=type=cache,target=/cache/gomodcache \ mkdir out && \ - GOCACHE=/cache/gocache \ - GOMODCACHE=/cache/gomodcache \ - CGO_ENABLED=0 \ - GOOS=linux \ - GOARCH=amd64 \ - go build -v -ldflags "$(hack/get-ldflags.sh)" -o out \ - ./cmd/pinniped-concierge/... \ - ./cmd/pinniped-supervisor/... \ - ./cmd/local-user-authenticator/... + export GOCACHE=/cache/gocache GOMODCACHE=/cache/gomodcache CGO_ENABLED=0 GOOS=linux GOARCH=amd64 && \ + go build -v -ldflags "$(hack/get-ldflags.sh) -w -s" -o /usr/local/bin/pinniped-concierge-kube-cert-agent ./cmd/pinniped-concierge-kube-cert-agent/main.go && \ + go build -v -ldflags "$(hack/get-ldflags.sh) -w -s" -o /usr/local/bin/pinniped-server ./cmd/pinniped-server/main.go && \ + ln -s /usr/local/bin/pinniped-server /usr/local/bin/pinniped-concierge && \ + ln -s /usr/local/bin/pinniped-server /usr/local/bin/pinniped-supervisor && \ + ln -s /usr/local/bin/pinniped-server /usr/local/bin/local-user-authenticator -# Use a Debian slim image to grab a reasonable default CA bundle. -FROM debian:10.10-slim AS get-ca-bundle-env -RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/* /var/cache/debconf/* +# Use a distroless runtime image with CA certificates, timezone data, and not much else. +FROM gcr.io/distroless/static:nonroot@sha256:c9f9b040044cc23e1088772814532d90adadfa1b86dcba17d07cb567db18dc4e -# Use a runtime image based on Debian slim. -FROM debian:10.10-slim -COPY --from=get-ca-bundle-env /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt - -# Copy the binaries from the build-env stage. -COPY --from=build-env /work/out/ /usr/local/bin/ +# Copy the server binary from the build-env stage. +COPY --from=build-env /usr/local/bin /usr/local/bin # Document the ports EXPOSE 8080 8443 @@ -44,4 +36,4 @@ EXPOSE 8080 8443 USER 1001:1001 # Set the entrypoint -ENTRYPOINT ["/usr/local/bin/pinniped-concierge"] +ENTRYPOINT ["/usr/local/bin/pinniped-server"] diff --git a/cmd/pinniped-concierge-kube-cert-agent/main.go b/cmd/pinniped-concierge-kube-cert-agent/main.go new file mode 100644 index 00000000..0947fe84 --- /dev/null +++ b/cmd/pinniped-concierge-kube-cert-agent/main.go @@ -0,0 +1,55 @@ +// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Package main is the combined entrypoint for the Pinniped "kube-cert-agent" component. +package main + +import ( + "encoding/base64" + "encoding/json" + "io" + "io/ioutil" + "log" + "math" + "os" + "time" +) + +//nolint: gochecknoglobals // these are swapped during unit tests. +var ( + getenv = os.Getenv + fail = log.Fatalf + sleep = time.Sleep + out = io.Writer(os.Stdout) +) + +func main() { + if len(os.Args) < 2 { + fail("missing subcommand") + } + + switch os.Args[1] { + case "sleep": + sleep(math.MaxInt64) + case "print": + certBytes, err := ioutil.ReadFile(getenv("CERT_PATH")) + if err != nil { + fail("could not read CERT_PATH: %v", err) + } + keyBytes, err := ioutil.ReadFile(getenv("KEY_PATH")) + if err != nil { + fail("could not read KEY_PATH: %v", err) + } + if err := json.NewEncoder(out).Encode(&struct { + Cert string `json:"tls.crt"` + Key string `json:"tls.key"` + }{ + Cert: base64.StdEncoding.EncodeToString(certBytes), + Key: base64.StdEncoding.EncodeToString(keyBytes), + }); err != nil { + fail("failed to write output: %v", err) + } + default: + fail("invalid subcommand %q", os.Args[1]) + } +} diff --git a/cmd/pinniped-concierge-kube-cert-agent/main_test.go b/cmd/pinniped-concierge-kube-cert-agent/main_test.go new file mode 100644 index 00000000..f6d6d854 --- /dev/null +++ b/cmd/pinniped-concierge-kube-cert-agent/main_test.go @@ -0,0 +1,128 @@ +// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "bytes" + "fmt" + "log" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +type errWriter struct{} + +func (e errWriter) Write([]byte) (int, error) { return 0, fmt.Errorf("some write error") } + +func TestEntrypoint(t *testing.T) { + for _, tt := range []struct { + name string + args []string + env map[string]string + failOutput bool + wantSleep time.Duration + wantLog string + wantOutJSON string + wantFail bool + }{ + { + name: "missing args", + args: []string{}, + wantLog: "missing subcommand\n", + wantFail: true, + }, + { + name: "invalid subcommand", + args: []string{"/path/to/binary", "invalid"}, + wantLog: "invalid subcommand \"invalid\"\n", + wantFail: true, + }, + { + name: "valid sleep", + args: []string{"/path/to/binary", "sleep"}, + wantSleep: 2562047*time.Hour + 47*time.Minute + 16*time.Second + 854775807*time.Nanosecond, // math.MaxInt64 nanoseconds, approximately 290 years + }, + { + name: "missing cert file", + args: []string{"/path/to/binary", "print"}, + env: map[string]string{ + "CERT_PATH": "./does/not/exist", + "KEY_PATH": "./testdata/test.key", + }, + wantFail: true, + wantLog: "could not read CERT_PATH: open ./does/not/exist: no such file or directory\n", + }, + { + name: "missing key file", + args: []string{"/path/to/binary", "print"}, + env: map[string]string{ + "CERT_PATH": "./testdata/test.crt", + "KEY_PATH": "./does/not/exist", + }, + wantFail: true, + wantLog: "could not read KEY_PATH: open ./does/not/exist: no such file or directory\n", + }, + { + name: "fail to write output", + args: []string{"/path/to/binary", "print"}, + env: map[string]string{ + "CERT_PATH": "./testdata/test.crt", + "KEY_PATH": "./testdata/test.key", + }, + failOutput: true, + wantFail: true, + wantLog: "failed to write output: some write error\n", + }, + { + name: "successful print", + args: []string{"/path/to/binary", "print"}, + env: map[string]string{ + "CERT_PATH": "./testdata/test.crt", + "KEY_PATH": "./testdata/test.key", + }, + wantOutJSON: `{ + "tls.crt": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJd01EY3lOVEl4TURReE9Gb1hEVE13TURjeU16SXhNRFF4T0Zvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTDNLCmhZdjJnSVExRHd6aDJjV01pZCtvZkFudkxJZlYyWHY2MXZUTEdwclVJK1hVcUI0L2d0ZjZYNlVObjBMZXR0Mm4KZDhwNHd5N2h3NzNoVS9nZ2R2bVdKdnFCclNqYzNKR2Z5K2tqNjZmS1hYK1BUbGJMN1Fid2lSdmNTcUlYSVdsVgpsSEh4RUNXckVEOGpDdWx3L05WcWZvb2svaDVpTlVDVDl5c3dTSnIvMGZJbWlWbm9UbElvRVlHMmVDTmVqWjVjCmczOXVEM1pUcWQ5WnhXd1NMTG5JKzJrcEpuWkJQY2QxWlE4QVFxekRnWnRZUkNxYWNuNWdja1FVS1pXS1FseG8KRWZ0NmcxWEhKb3VBV0FadzdoRXRrMHY4ckcwL2VLRjd3YW14Rmk2QkZWbGJqV0JzQjRUOXJBcGJkQldUS2VDSgpIdjhmdjVSTUZTenBUM3V6VE84Q0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFDaDVSaGJ4cUplK1ovZ2MxN2NaaEtObWRpd3UKSTJwTHAzUUJmd3ZOK1dibWFqencvN3JZaFkwZDhKWVZUSnpYU0NQV2k2VUFLeEF0WE9MRjhXSUlmOWkzOW42Ugp1S09CR1cxNEZ6ekd5UkppRDNxYUcvSlR2RVcrU0xod2w2OE5kcjVMSFNuYnVnQXFxMzFhYmNReTZabDl2NUE4CkpLQzk3TGovU244cmo3b3BLeTRXM29xN05DUXNBYjB6aDRJbGxSRjZVdlNuSnlTZnNnN3hkWEhIcHhZREh0T1MKWGNPdTV5U1VJWlRnRmU5UmZlVVpsR1o1eG4wY2tNbFE3cVcyV3gxcTBPVld3NXVzNE50a0dxS3JIRzRUbjFYNwp1d28vWXl0bjVzRHhyRHYxL29paTZBWk9Dc1RQcmU0b0Qzd3o0bm1WekNWSmNncnFINFEyNGhUOFdOZz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=", + "tls.key": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBdmNxRmkvYUFoRFVQRE9IWnhZeUozNmg4Q2U4c2g5WFplL3JXOU1zYW10UWo1ZFNvCkhqK0MxL3BmcFEyZlF0NjIzYWQzeW5qREx1SER2ZUZUK0NCMitaWW0rb0d0S056Y2taL0w2U1BycDhwZGY0OU8KVnN2dEJ2Q0pHOXhLb2hjaGFWV1VjZkVRSmFzUVB5TUs2WEQ4MVdwK2lpVCtIbUkxUUpQM0t6Qkltdi9SOGlhSgpXZWhPVWlnUmdiWjRJMTZObmx5RGYyNFBkbE9wMzFuRmJCSXN1Y2o3YVNrbWRrRTl4M1ZsRHdCQ3JNT0JtMWhFCktwcHlmbUJ5UkJRcGxZcENYR2dSKzNxRFZjY21pNEJZQm5EdUVTMlRTL3lzYlQ5NG9YdkJxYkVXTG9FVldWdU4KWUd3SGhQMnNDbHQwRlpNcDRJa2UveCsvbEV3VkxPbFBlN05NN3dJREFRQUJBb0lCQUZDMXRVRW1ITlVjTTBCSgpNM0Q5S1F6Qis2M0YxbXdWbHgxUU9PVjFFZVZSM2NvNU94MVI2UFNyOXN5Y0ZHUTlqZ3FJMHpwNVRKZTlUcDZMCkdraGtsZlBoMU1Xbks5bzZ3bG56V0tYV3JycDJKbmkrbXBQeXVPUEFtcTRNYW5pdjJYZVArMGJST3dxcHlvanYKQUE3eUM3TStUSDIyNlpKR05WczNFVjkrY3dIbWwweXV6QmZJSm4vcnYvdzJnK1dSS00vTUMwUzdrMmQ4YlJsQQpOeWNLVkdBR0JoS1RsdGpvVllPZWg2YUhFcFNqSzh6ZmFlUGpvNWRZSnZvVklsaTYwWUNnY0pPVS84alhUK05wCjFGbTd0UnZBdGozcFVwMFNxZGFmMlJVemg5amZKcDJWRkNIdVNKNlRQcUFyT3lRb2p0TWNUSEYwVGlXN3hySFAKeE9DUklBRUNnWUVBd0dCUFU3dmR0aE1KQmcrT1JVb0dRUWFJdFRlSnZRd0lxSnZiS0Qyb3NwNGpoUzFkR1pCdwpXMzBHS0VjL2dkOEpOdE9xOUJCbk1pY1BGN2hrdHV5K2JTUHY0MVhQdWQ2N3JTU083VHN3MjBDMTBnRlJxMDZCCnpJSldGQVVxSzNJa3ZWYzNWRG10U0xTRG94NFFaL0JkcWFNbFE1eTVKQ3NDNWtUaG1rWkZsTzhDZ1lFQS9JOVgKWUhpNlJpb01KRTFmcU9ISkw0RERqbGV6bWN1UnJEN2ZFNUluS2J0SloySmhHWU9YL0MwS1huSFRPV1RDRHh4TgpGQnZwdkQ2WHY1bzNQaEI5WjZrMmZxdko0R1M4dXJrRy9LVTR4Y0MrYmFrKzlhdmE4b2FpU3FHMTZ6RDlOSDJQCmpKNjBOcmJMbDFKMHBVOWZpd3VGVlVLSjRoRFpPZk45UnFZZHlBRUNnWUFWd284V2hKaUdnTTZ6ZmN6MDczT1gKcFZxUFRQSHFqVkxwWjMrNXBJZlJkR3ZHSTZSMVFNNUV1dmFZVmI3TVBPTTQ3V1pYNXdjVk9DL1AyZzZpVmxNUAoyMUhHSUMyMzg0YTlCZmFZeE9vNDBxLytTaUhudzZDUTlta3dLSWxsa3Fxdk5BOVJHcGtNTVViMmkyOEZvcjJsCmM0dkNneGE2RFpkdFhuczZUUnFQeHdLQmdDZlk1Y3hPdi9UNkJWaGs3TWJVZU0ySjMxREIvWkF5VWhWL0Jlc3MKa0FsQmgxOU1ZazJJT1o2TDdLcmlBcFYzbERhV0hJTWp0RWtEQnlZdnlxOThJbzBNWVpDeXdmTXBjYTEwSytvSQpsMkI3L0krSXVHcENaeFVFc081ZGZUcFNUR0RQdnFwTkQ5bmlGVlVXcVZpN29UTnE2ZXA5eVF0bDVTQURqcXhxCjRTQUJBb0dBSW0waFVnMXd0Y1M0NmNHTHk2UElrUE01dG9jVFNnaHR6NHZGc3VrL2k0UUE5R0JvQk8yZ0g2dHkKK2tKSG1lYVh0MmRtZ3lTcDBRQVdpdDVVbGNlRXVtQjBOWG5BZEpaUXhlR1NGU3lZa0RXaHdYZDh3RGNlS28vMQpMZkNVNkRrOElOL1NzcHBWVVdYUTJybE9SdnhsckhlQ2lvOG8wa1M5WWl1NTVXTVlnNGc9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==" + }`, + }, + } { + tt := tt + t.Run(tt.name, func(t *testing.T) { + var logBuf bytes.Buffer + testLog := log.New(&logBuf, "", 0) + exited := "exiting via fatal" + fail = func(format string, v ...interface{}) { + testLog.Printf(format, v...) + panic(exited) + } + + var sawSleep time.Duration + sleep = func(d time.Duration) { sawSleep = d } + + var sawOutput bytes.Buffer + out = &sawOutput + if tt.failOutput { + out = &errWriter{} + } + + os.Args = tt.args + getenv = func(key string) string { return tt.env[key] } + if tt.wantFail { + require.PanicsWithValue(t, exited, main) + } else { + require.NotPanics(t, main) + } + require.Equal(t, tt.wantSleep.String(), sawSleep.String()) + require.Equal(t, tt.wantLog, logBuf.String()) + if tt.wantOutJSON == "" { + require.Empty(t, sawOutput.String()) + } else { + require.JSONEq(t, tt.wantOutJSON, sawOutput.String()) + } + }) + } +} diff --git a/cmd/pinniped-concierge-kube-cert-agent/testdata/test.crt b/cmd/pinniped-concierge-kube-cert-agent/testdata/test.crt new file mode 100644 index 00000000..796a7690 --- /dev/null +++ b/cmd/pinniped-concierge-kube-cert-agent/testdata/test.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICyDCCAbCgAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl +cm5ldGVzMB4XDTIwMDcyNTIxMDQxOFoXDTMwMDcyMzIxMDQxOFowFTETMBEGA1UE +AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3K +hYv2gIQ1Dwzh2cWMid+ofAnvLIfV2Xv61vTLGprUI+XUqB4/gtf6X6UNn0Lett2n +d8p4wy7hw73hU/ggdvmWJvqBrSjc3JGfy+kj66fKXX+PTlbL7QbwiRvcSqIXIWlV +lHHxECWrED8jCulw/NVqfook/h5iNUCT9yswSJr/0fImiVnoTlIoEYG2eCNejZ5c +g39uD3ZTqd9ZxWwSLLnI+2kpJnZBPcd1ZQ8AQqzDgZtYRCqacn5gckQUKZWKQlxo +Eft6g1XHJouAWAZw7hEtk0v8rG0/eKF7wamxFi6BFVlbjWBsB4T9rApbdBWTKeCJ +Hv8fv5RMFSzpT3uzTO8CAwEAAaMjMCEwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACh5RhbxqJe+Z/gc17cZhKNmdiwu +I2pLp3QBfwvN+Wbmajzw/7rYhY0d8JYVTJzXSCPWi6UAKxAtXOLF8WIIf9i39n6R +uKOBGW14FzzGyRJiD3qaG/JTvEW+SLhwl68Ndr5LHSnbugAqq31abcQy6Zl9v5A8 +JKC97Lj/Sn8rj7opKy4W3oq7NCQsAb0zh4IllRF6UvSnJySfsg7xdXHHpxYDHtOS +XcOu5ySUIZTgFe9RfeUZlGZ5xn0ckMlQ7qW2Wx1q0OVWw5us4NtkGqKrHG4Tn1X7 +uwo/Yytn5sDxrDv1/oii6AZOCsTPre4oD3wz4nmVzCVJcgrqH4Q24hT8WNg= +-----END CERTIFICATE----- diff --git a/cmd/pinniped-concierge-kube-cert-agent/testdata/test.key b/cmd/pinniped-concierge-kube-cert-agent/testdata/test.key new file mode 100644 index 00000000..7ad653ae --- /dev/null +++ b/cmd/pinniped-concierge-kube-cert-agent/testdata/test.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAvcqFi/aAhDUPDOHZxYyJ36h8Ce8sh9XZe/rW9MsamtQj5dSo +Hj+C1/pfpQ2fQt623ad3ynjDLuHDveFT+CB2+ZYm+oGtKNzckZ/L6SPrp8pdf49O +VsvtBvCJG9xKohchaVWUcfEQJasQPyMK6XD81Wp+iiT+HmI1QJP3KzBImv/R8iaJ +WehOUigRgbZ4I16NnlyDf24PdlOp31nFbBIsucj7aSkmdkE9x3VlDwBCrMOBm1hE +KppyfmByRBQplYpCXGgR+3qDVccmi4BYBnDuES2TS/ysbT94oXvBqbEWLoEVWVuN +YGwHhP2sClt0FZMp4Ike/x+/lEwVLOlPe7NM7wIDAQABAoIBAFC1tUEmHNUcM0BJ +M3D9KQzB+63F1mwVlx1QOOV1EeVR3co5Ox1R6PSr9sycFGQ9jgqI0zp5TJe9Tp6L +GkhklfPh1MWnK9o6wlnzWKXWrrp2Jni+mpPyuOPAmq4Maniv2XeP+0bROwqpyojv +AA7yC7M+TH226ZJGNVs3EV9+cwHml0yuzBfIJn/rv/w2g+WRKM/MC0S7k2d8bRlA +NycKVGAGBhKTltjoVYOeh6aHEpSjK8zfaePjo5dYJvoVIli60YCgcJOU/8jXT+Np +1Fm7tRvAtj3pUp0Sqdaf2RUzh9jfJp2VFCHuSJ6TPqArOyQojtMcTHF0TiW7xrHP +xOCRIAECgYEAwGBPU7vdthMJBg+ORUoGQQaItTeJvQwIqJvbKD2osp4jhS1dGZBw +W30GKEc/gd8JNtOq9BBnMicPF7hktuy+bSPv41XPud67rSSO7Tsw20C10gFRq06B +zIJWFAUqK3IkvVc3VDmtSLSDox4QZ/BdqaMlQ5y5JCsC5kThmkZFlO8CgYEA/I9X +YHi6RioMJE1fqOHJL4DDjlezmcuRrD7fE5InKbtJZ2JhGYOX/C0KXnHTOWTCDxxN +FBvpvD6Xv5o3PhB9Z6k2fqvJ4GS8urkG/KU4xcC+bak+9ava8oaiSqG16zD9NH2P +jJ60NrbLl1J0pU9fiwuFVUKJ4hDZOfN9RqYdyAECgYAVwo8WhJiGgM6zfcz073OX +pVqPTPHqjVLpZ3+5pIfRdGvGI6R1QM5EuvaYVb7MPOM47WZX5wcVOC/P2g6iVlMP +21HGIC2384a9BfaYxOo40q/+SiHnw6CQ9mkwKIllkqqvNA9RGpkMMUb2i28For2l +c4vCgxa6DZdtXns6TRqPxwKBgCfY5cxOv/T6BVhk7MbUeM2J31DB/ZAyUhV/Bess +kAlBh19MYk2IOZ6L7KriApV3lDaWHIMjtEkDByYvyq98Io0MYZCywfMpca10K+oI +l2B7/I+IuGpCZxUEsO5dfTpSTGDPvqpND9niFVUWqVi7oTNq6ep9yQtl5SADjqxq +4SABAoGAIm0hUg1wtcS46cGLy6PIkPM5tocTSghtz4vFsuk/i4QA9GBoBO2gH6ty ++kJHmeaXt2dmgySp0QAWit5UlceEumB0NXnAdJZQxeGSFSyYkDWhwXd8wDceKo/1 +LfCU6Dk8IN/SsppVUWXQ2rlORvxlrHeCio8o0kS9Yiu55WMYg4g= +-----END RSA PRIVATE KEY----- diff --git a/cmd/pinniped-concierge/main.go b/cmd/pinniped-concierge/main.go deleted file mode 100644 index e6fdf991..00000000 --- a/cmd/pinniped-concierge/main.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package main - -import ( - "os" - "time" - - genericapiserver "k8s.io/apiserver/pkg/server" - "k8s.io/client-go/pkg/version" - "k8s.io/client-go/rest" - "k8s.io/component-base/logs" - "k8s.io/klog/v2" - - "go.pinniped.dev/internal/concierge/server" -) - -func main() { - logs.InitLogs() - defer logs.FlushLogs() - - // Dump out the time since compile (mostly useful for benchmarking our local development cycle latency). - var timeSinceCompile time.Duration - if buildDate, err := time.Parse(time.RFC3339, version.Get().BuildDate); err == nil { - timeSinceCompile = time.Since(buildDate).Round(time.Second) - } - klog.Infof("Running %s at %#v (%s since build)", rest.DefaultKubernetesUserAgent(), version.Get(), timeSinceCompile) - - ctx := genericapiserver.SetupSignalContext() - - if err := server.New(ctx, os.Args[1:], os.Stdout, os.Stderr).Run(); err != nil { - klog.Fatal(err) - } -} diff --git a/cmd/pinniped-server/main.go b/cmd/pinniped-server/main.go new file mode 100644 index 00000000..dfb4b4ca --- /dev/null +++ b/cmd/pinniped-server/main.go @@ -0,0 +1,41 @@ +// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Package main is the combined entrypoint for all Pinniped server components. +// +// It dispatches to the appropriate Main() entrypoint based the name it is invoked as (os.Args[0]). In our server +// container image, this binary is symlinked to several names such as `/usr/local/bin/pinniped-concierge`. +package main + +import ( + "os" + "path/filepath" + + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/klog/v2" + + concierge "go.pinniped.dev/internal/concierge/server" + lua "go.pinniped.dev/internal/localuserauthenticator" + supervisor "go.pinniped.dev/internal/supervisor/server" +) + +//nolint: gochecknoglobals // these are swapped during unit tests. +var ( + fail = klog.Fatalf + subcommands = map[string]func(){ + "pinniped-concierge": concierge.Main, + "pinniped-supervisor": supervisor.Main, + "local-user-authenticator": lua.Main, + } +) + +func main() { + if len(os.Args) == 0 { + fail("missing os.Args") + } + binary := filepath.Base(os.Args[0]) + if subcommands[binary] == nil { + fail("must be invoked as one of %v, not %q", sets.StringKeySet(subcommands).List(), binary) + } + subcommands[binary]() +} diff --git a/cmd/pinniped-server/main_test.go b/cmd/pinniped-server/main_test.go new file mode 100644 index 00000000..6a1e1e68 --- /dev/null +++ b/cmd/pinniped-server/main_test.go @@ -0,0 +1,72 @@ +// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "bytes" + "log" + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestEntrypoint(t *testing.T) { + for _, tt := range []struct { + name string + args []string + wantOutput string + wantFail bool + wantArgs []string + }{ + { + name: "missing args", + args: []string{}, + wantOutput: "missing os.Args\n", + wantFail: true, + }, + { + name: "invalid subcommand", + args: []string{"/path/to/invalid", "some", "args"}, + wantOutput: "must be invoked as one of [another-test-binary valid-test-binary], not \"invalid\"\n", + wantFail: true, + }, + { + name: "valid", + args: []string{"/path/to/valid-test-binary", "foo", "bar"}, + wantArgs: []string{"/path/to/valid-test-binary", "foo", "bar"}, + }, + } { + tt := tt + t.Run(tt.name, func(t *testing.T) { + var logBuf bytes.Buffer + testLog := log.New(&logBuf, "", 0) + exited := "exiting via fatal" + fail = func(format string, v ...interface{}) { + testLog.Printf(format, v...) + panic(exited) + } + + // Make a test command that records os.Args when it's invoked. + var gotArgs []string + subcommands = map[string]func(){ + "valid-test-binary": func() { gotArgs = os.Args }, + "another-test-binary": func() {}, + } + + os.Args = tt.args + if tt.wantFail { + require.PanicsWithValue(t, exited, main) + } else { + require.NotPanics(t, main) + } + if tt.wantArgs != nil { + require.Equal(t, tt.wantArgs, gotArgs) + } + if tt.wantOutput != "" { + require.Equal(t, tt.wantOutput, logBuf.String()) + } + }) + } +} diff --git a/deploy/concierge/deployment.yaml b/deploy/concierge/deployment.yaml index abe8ba78..a516cfbf 100644 --- a/deploy/concierge/deployment.yaml +++ b/deploy/concierge/deployment.yaml @@ -116,6 +116,7 @@ spec: scheduler.alpha.kubernetes.io/critical-pod: "" spec: securityContext: + readOnlyRootFilesystem: true runAsUser: #@ data.values.run_as_user runAsGroup: #@ data.values.run_as_group serviceAccountName: #@ defaultResourceName() @@ -138,10 +139,13 @@ spec: limits: cpu: "100m" memory: "128Mi" - args: + command: + - pinniped-concierge - --config=/etc/config/pinniped.yaml - --downward-api-path=/etc/podinfo volumeMounts: + - name: tmp + mountPath: /tmp - name: config-volume mountPath: /etc/config - name: podinfo @@ -167,7 +171,12 @@ spec: periodSeconds: 10 failureThreshold: 3 volumes: + - name: tmp + emptyDir: + medium: Memory + sizeLimit: 100Mi - name: config-volume + readOnly: true configMap: name: #@ defaultResourceNameWithSuffix("config") - name: impersonation-proxy @@ -177,6 +186,7 @@ spec: - key: token path: token - name: podinfo + readOnly: true downwardAPI: items: - path: "labels" diff --git a/deploy/local-user-authenticator/deployment.yaml b/deploy/local-user-authenticator/deployment.yaml index 9845800a..258f1c5f 100644 --- a/deploy/local-user-authenticator/deployment.yaml +++ b/deploy/local-user-authenticator/deployment.yaml @@ -63,8 +63,8 @@ spec: image: #@ data.values.image_repo + ":" + data.values.image_tag #@ end imagePullPolicy: IfNotPresent - command: #! override the default entrypoint - - /usr/local/bin/local-user-authenticator + command: + - local-user-authenticator --- apiVersion: v1 kind: Service diff --git a/deploy/supervisor/deployment.yaml b/deploy/supervisor/deployment.yaml index 783f10aa..8778e2b6 100644 --- a/deploy/supervisor/deployment.yaml +++ b/deploy/supervisor/deployment.yaml @@ -65,6 +65,7 @@ spec: labels: #@ defaultLabel() spec: securityContext: + readOnlyRootFilesystem: true runAsUser: #@ data.values.run_as_user runAsGroup: #@ data.values.run_as_group serviceAccountName: #@ defaultResourceName() @@ -80,9 +81,8 @@ spec: image: #@ data.values.image_repo + ":" + data.values.image_tag #@ end imagePullPolicy: IfNotPresent - command: #! override the default entrypoint - - /usr/local/bin/pinniped-supervisor - args: + command: + - pinniped-supervisor - /etc/podinfo - /etc/config/pinniped.yaml resources: @@ -131,9 +131,11 @@ spec: failureThreshold: 3 volumes: - name: config-volume + readOnly: true configMap: name: #@ defaultResourceNameWithSuffix("static-config") - name: podinfo + readOnly: true downwardAPI: items: - path: "labels" diff --git a/internal/concierge/server/server.go b/internal/concierge/server/server.go index d5e51379..b0ff941c 100644 --- a/internal/concierge/server/server.go +++ b/internal/concierge/server/server.go @@ -8,6 +8,7 @@ import ( "context" "fmt" "io" + "os" "time" "github.com/spf13/cobra" @@ -16,6 +17,10 @@ import ( "k8s.io/apimachinery/pkg/runtime/serializer" genericapiserver "k8s.io/apiserver/pkg/server" genericoptions "k8s.io/apiserver/pkg/server/options" + "k8s.io/client-go/pkg/version" + "k8s.io/client-go/rest" + "k8s.io/component-base/logs" + "k8s.io/klog/v2" "go.pinniped.dev/internal/certauthority/dynamiccertauthority" "go.pinniped.dev/internal/concierge/apiserver" @@ -231,3 +236,21 @@ func getAggregatedAPIServerConfig( } return apiServerConfig, nil } + +func Main() { + logs.InitLogs() + defer logs.FlushLogs() + + // Dump out the time since compile (mostly useful for benchmarking our local development cycle latency). + var timeSinceCompile time.Duration + if buildDate, err := time.Parse(time.RFC3339, version.Get().BuildDate); err == nil { + timeSinceCompile = time.Since(buildDate).Round(time.Second) + } + klog.Infof("Running %s at %#v (%s since build)", rest.DefaultKubernetesUserAgent(), version.Get(), timeSinceCompile) + + ctx := genericapiserver.SetupSignalContext() + + if err := New(ctx, os.Args[1:], os.Stdout, os.Stderr).Run(); err != nil { + klog.Fatal(err) + } +} diff --git a/internal/controller/kubecertagent/kubecertagent.go b/internal/controller/kubecertagent/kubecertagent.go index 4bb7346c..089f41f8 100644 --- a/internal/controller/kubecertagent/kubecertagent.go +++ b/internal/controller/kubecertagent/kubecertagent.go @@ -8,6 +8,7 @@ package kubecertagent import ( "context" "encoding/base64" + "encoding/json" "fmt" "strings" "time" @@ -309,22 +310,30 @@ func (c *agentController) loadSigningKey(agentPod *corev1.Pod) error { } // Exec into the agent pod and cat out the certificate and the key. - combinedPEM, err := c.executor.Exec( - agentPod.Namespace, agentPod.Name, - "sh", "-c", "cat ${CERT_PATH}; echo; echo; cat ${KEY_PATH}", - ) + outputJSON, err := c.executor.Exec(agentPod.Namespace, agentPod.Name, "pinniped-concierge-kube-cert-agent", "print") if err != nil { return fmt.Errorf("could not exec into agent pod %s/%s: %w", agentPod.Namespace, agentPod.Name, err) } - // Split up the output by looking for the block of newlines. - var certPEM, keyPEM string - if parts := strings.Split(combinedPEM, "\n\n\n"); len(parts) == 2 { - certPEM, keyPEM = parts[0], parts[1] + // Parse and decode the JSON output from the "pinniped-concierge-kube-cert-agent print" command. + var output struct { + Cert string `json:"tls.crt"` + Key string `json:"tls.key"` + } + if err := json.Unmarshal([]byte(outputJSON), &output); err != nil { + return fmt.Errorf("failed to decode signing cert/key JSON from agent pod %s/%s: %w", agentPod.Namespace, agentPod.Name, err) + } + certPEM, err := base64.StdEncoding.DecodeString(output.Cert) + if err != nil { + return fmt.Errorf("failed to decode signing cert base64 from agent pod %s/%s: %w", agentPod.Namespace, agentPod.Name, err) + } + keyPEM, err := base64.StdEncoding.DecodeString(output.Key) + if err != nil { + return fmt.Errorf("failed to decode signing key base64 from agent pod %s/%s: %w", agentPod.Namespace, agentPod.Name, err) } // Load the certificate and key into the dynamic signer. - if err := c.dynamicCertProvider.SetCertKeyContent([]byte(certPEM), []byte(keyPEM)); err != nil { + if err := c.dynamicCertProvider.SetCertKeyContent(certPEM, keyPEM); err != nil { return fmt.Errorf("failed to set signing cert/key content from agent pod %s/%s: %w", agentPod.Namespace, agentPod.Name, err) } @@ -461,7 +470,7 @@ func (c *agentController) newAgentDeployment(controllerManagerPod *corev1.Pod) * Name: "sleeper", Image: c.cfg.ContainerImage, ImagePullPolicy: corev1.PullIfNotPresent, - Command: []string{"/bin/sleep", "infinity"}, + Command: []string{"pinniped-concierge-kube-cert-agent", "sleep"}, VolumeMounts: volumeMounts, Env: []corev1.EnvVar{ {Name: "CERT_PATH", Value: getContainerArgByName(controllerManagerPod, "cluster-signing-cert-file", "/etc/kubernetes/ca/ca.pem")}, diff --git a/internal/controller/kubecertagent/kubecertagent_test.go b/internal/controller/kubecertagent/kubecertagent_test.go index 049f95a6..119a5476 100644 --- a/internal/controller/kubecertagent/kubecertagent_test.go +++ b/internal/controller/kubecertagent/kubecertagent_test.go @@ -104,7 +104,7 @@ func TestAgentController(t *testing.T) { Containers: []corev1.Container{{ Name: "sleeper", Image: "pinniped-server-image", - Command: []string{"/bin/sleep", "infinity"}, + Command: []string{"pinniped-concierge-kube-cert-agent", "sleep"}, Env: []corev1.EnvVar{ {Name: "CERT_PATH", Value: "/path/to/signing.crt"}, {Name: "KEY_PATH", Value: "/path/to/signing.key"}, @@ -200,8 +200,8 @@ func TestAgentController(t *testing.T) { } mockExecSucceeds := func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) { - executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "sh", "-c", "cat ${CERT_PATH}; echo; echo; cat ${KEY_PATH}"). - Return("test-cert\n\n\ntest-key", nil) + executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). + Return(`{"tls.crt": "dGVzdC1jZXJ0", "tls.key": "dGVzdC1rZXk="}`, nil) // "test-cert" / "test-key" dynamicCert.SetCertKeyContent([]byte("test-cert"), []byte("test-key")). Return(nil) } @@ -573,7 +573,7 @@ func TestAgentController(t *testing.T) { validClusterInfoConfigMap, }, mocks: func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) { - executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "sh", "-c", "cat ${CERT_PATH}; echo; echo; cat ${KEY_PATH}"). + executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). Return("", fmt.Errorf("some exec error")). AnyTimes() }, @@ -589,6 +589,90 @@ func TestAgentController(t *testing.T) { LastUpdateTime: metav1.NewTime(now), }, }, + { + name: "deployment exists, configmap is valid, exec into agent pod returns invalid JSON", + pinnipedObjects: []runtime.Object{ + initialCredentialIssuer, + }, + kubeObjects: []runtime.Object{ + healthyKubeControllerManagerPod, + healthyAgentDeployment, + healthyAgentPod, + validClusterInfoConfigMap, + }, + mocks: func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) { + executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). + Return("bogus-data", nil). + AnyTimes() + }, + wantDistinctErrors: []string{ + `failed to decode signing cert/key JSON from agent pod concierge/pinniped-concierge-kube-cert-agent-xyz-1234: invalid character 'b' looking for beginning of value`, + }, + wantAgentDeployment: healthyAgentDeployment, + wantStrategy: &configv1alpha1.CredentialIssuerStrategy{ + Type: configv1alpha1.KubeClusterSigningCertificateStrategyType, + Status: configv1alpha1.ErrorStrategyStatus, + Reason: configv1alpha1.CouldNotFetchKeyStrategyReason, + Message: `failed to decode signing cert/key JSON from agent pod concierge/pinniped-concierge-kube-cert-agent-xyz-1234: invalid character 'b' looking for beginning of value`, + LastUpdateTime: metav1.NewTime(now), + }, + }, + { + name: "deployment exists, configmap is valid, exec into agent pod returns invalid cert base64", + pinnipedObjects: []runtime.Object{ + initialCredentialIssuer, + }, + kubeObjects: []runtime.Object{ + healthyKubeControllerManagerPod, + healthyAgentDeployment, + healthyAgentPod, + validClusterInfoConfigMap, + }, + mocks: func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) { + executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). + Return(`{"tls.crt": "invalid"}`, nil). + AnyTimes() + }, + wantDistinctErrors: []string{ + `failed to decode signing cert base64 from agent pod concierge/pinniped-concierge-kube-cert-agent-xyz-1234: illegal base64 data at input byte 4`, + }, + wantAgentDeployment: healthyAgentDeployment, + wantStrategy: &configv1alpha1.CredentialIssuerStrategy{ + Type: configv1alpha1.KubeClusterSigningCertificateStrategyType, + Status: configv1alpha1.ErrorStrategyStatus, + Reason: configv1alpha1.CouldNotFetchKeyStrategyReason, + Message: `failed to decode signing cert base64 from agent pod concierge/pinniped-concierge-kube-cert-agent-xyz-1234: illegal base64 data at input byte 4`, + LastUpdateTime: metav1.NewTime(now), + }, + }, + { + name: "deployment exists, configmap is valid, exec into agent pod returns invalid key base64", + pinnipedObjects: []runtime.Object{ + initialCredentialIssuer, + }, + kubeObjects: []runtime.Object{ + healthyKubeControllerManagerPod, + healthyAgentDeployment, + healthyAgentPod, + validClusterInfoConfigMap, + }, + mocks: func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) { + executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). + Return(`{"tls.crt": "dGVzdAo=", "tls.key": "invalid"}`, nil). + AnyTimes() + }, + wantDistinctErrors: []string{ + `failed to decode signing key base64 from agent pod concierge/pinniped-concierge-kube-cert-agent-xyz-1234: illegal base64 data at input byte 4`, + }, + wantAgentDeployment: healthyAgentDeployment, + wantStrategy: &configv1alpha1.CredentialIssuerStrategy{ + Type: configv1alpha1.KubeClusterSigningCertificateStrategyType, + Status: configv1alpha1.ErrorStrategyStatus, + Reason: configv1alpha1.CouldNotFetchKeyStrategyReason, + Message: `failed to decode signing key base64 from agent pod concierge/pinniped-concierge-kube-cert-agent-xyz-1234: illegal base64 data at input byte 4`, + LastUpdateTime: metav1.NewTime(now), + }, + }, { name: "deployment exists, configmap is valid, exec into agent pod returns bogus certs", pinnipedObjects: []runtime.Object{ @@ -601,10 +685,10 @@ func TestAgentController(t *testing.T) { validClusterInfoConfigMap, }, mocks: func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) { - executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "sh", "-c", "cat ${CERT_PATH}; echo; echo; cat ${KEY_PATH}"). - Return("bogus-data", nil). + executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). + Return(`{"tls.crt": "dGVzdC1jZXJ0", "tls.key": "dGVzdC1rZXk="}`, nil). // "test-cert" / "test-key" AnyTimes() - dynamicCert.SetCertKeyContent([]byte(""), []byte("")). + dynamicCert.SetCertKeyContent([]byte("test-cert"), []byte("test-key")). Return(fmt.Errorf("some dynamic cert error")). AnyTimes() }, diff --git a/cmd/local-user-authenticator/main.go b/internal/localuserauthenticator/localuserauthenticator.go similarity index 98% rename from cmd/local-user-authenticator/main.go rename to internal/localuserauthenticator/localuserauthenticator.go index 0dd3b42e..426530dc 100644 --- a/cmd/local-user-authenticator/main.go +++ b/internal/localuserauthenticator/localuserauthenticator.go @@ -1,13 +1,13 @@ // Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -// Package main provides a authentication webhook program. +// Package localuserauthenticator provides a authentication webhook program. // // This webhook is meant to be used in demo settings to play around with // Pinniped. As well, it can come in handy in integration tests. // // This webhook is NOT meant for use in production systems. -package main +package localuserauthenticator import ( "bytes" @@ -378,7 +378,7 @@ func run() error { return nil } -func main() { +func Main() { // Hardcode the logging level to debug, since this is a test app and it is very helpful to have // verbose logs to debug test failures. if err := plog.ValidateAndSetLogLevelGlobally(plog.LevelDebug); err != nil { diff --git a/cmd/local-user-authenticator/main_test.go b/internal/localuserauthenticator/localuserauthenticator_test.go similarity index 99% rename from cmd/local-user-authenticator/main_test.go rename to internal/localuserauthenticator/localuserauthenticator_test.go index 7bd9e64c..6a24e002 100644 --- a/cmd/local-user-authenticator/main_test.go +++ b/internal/localuserauthenticator/localuserauthenticator_test.go @@ -1,7 +1,7 @@ // Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package main +package localuserauthenticator import ( "bytes" diff --git a/cmd/pinniped-supervisor/main.go b/internal/supervisor/server/server.go similarity index 99% rename from cmd/pinniped-supervisor/main.go rename to internal/supervisor/server/server.go index 0cf897ae..b4ce7549 100644 --- a/cmd/pinniped-supervisor/main.go +++ b/internal/supervisor/server/server.go @@ -1,7 +1,8 @@ // Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package main +// Package server defines the entrypoint for the Pinniped Supervisor server. +package server import ( "context" @@ -371,7 +372,7 @@ func run(podInfo *downward.PodInfo, cfg *supervisor.Config) error { return nil } -func main() { +func Main() { logs.InitLogs() defer logs.FlushLogs() plog.RemoveKlogGlobalFlags() // move this whenever the below code gets refactored to use cobra diff --git a/test/integration/concierge_impersonation_proxy_test.go b/test/integration/concierge_impersonation_proxy_test.go index 2b5a0552..bced36fe 100644 --- a/test/integration/concierge_impersonation_proxy_test.go +++ b/test/integration/concierge_impersonation_proxy_test.go @@ -40,6 +40,7 @@ import ( rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/equality" k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructuredscheme" "k8s.io/apimachinery/pkg/labels" @@ -306,7 +307,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl // Get pods in concierge namespace and pick one. // this is for tests that require performing actions against a running pod. We use the concierge pod because we already have it handy. - // We want to make sure it's a concierge pod (not cert agent), because we need to be able to "exec echo" and port-forward a running port. + // We want to make sure it's a concierge pod (not cert agent), because we need to be able to port-forward a running port. pods, err := adminClient.CoreV1().Pods(env.ConciergeNamespace).List(ctx, metav1.ListOptions{}) require.NoError(t, err) require.Greater(t, len(pods.Items), 0) @@ -989,48 +990,53 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl parallelIfNotEKS(t) kubeconfigPath, envVarsWithProxy, tempDir := getImpersonationKubeconfig(t, env, impersonationProxyURL, impersonationProxyCACertPEM, credentialRequestSpecWithWorkingCredentials.Authenticator) + // Run a new test pod so we can interact with it using kubectl. We use a fresh pod here rather than the + // existing Concierge pod because we need more tools than we can get from a scratch/distroless base image. + runningTestPod := testlib.CreatePod(ctx, t, "impersonation-proxy", env.ConciergeNamespace, corev1.PodSpec{Containers: []corev1.Container{{ + Name: "impersonation-proxy-test", + Image: "debian:10.10-slim", + ImagePullPolicy: corev1.PullIfNotPresent, + Command: []string{"bash", "-c", `while true; do read VAR; echo "VAR: $VAR"; done`}, + Stdin: true, + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("16Mi"), + corev1.ResourceCPU: resource.MustParse("10m"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("16Mi"), + corev1.ResourceCPU: resource.MustParse("10m"), + }, + }, + }}}) + // Try "kubectl exec" through the impersonation proxy. echoString := "hello world" remoteEchoFile := fmt.Sprintf("/tmp/test-impersonation-proxy-echo-file-%d.txt", time.Now().Unix()) - stdout, err := runKubectl(t, kubeconfigPath, envVarsWithProxy, "exec", "--namespace", env.ConciergeNamespace, conciergePod.Name, "--", "bash", "-c", fmt.Sprintf(`echo "%s" | tee %s`, echoString, remoteEchoFile)) + stdout, err := runKubectl(t, kubeconfigPath, envVarsWithProxy, "exec", "--namespace", runningTestPod.Namespace, runningTestPod.Name, "--", "bash", "-c", fmt.Sprintf(`echo "%s" | tee %s`, echoString, remoteEchoFile)) require.NoError(t, err, `"kubectl exec" failed`) require.Equal(t, echoString+"\n", stdout) // run the kubectl cp command localEchoFile := filepath.Join(tempDir, filepath.Base(remoteEchoFile)) - _, err = runKubectl(t, kubeconfigPath, envVarsWithProxy, "cp", fmt.Sprintf("%s/%s:%s", env.ConciergeNamespace, conciergePod.Name, remoteEchoFile), localEchoFile) + _, err = runKubectl(t, kubeconfigPath, envVarsWithProxy, "cp", fmt.Sprintf("%s/%s:%s", runningTestPod.Namespace, runningTestPod.Name, remoteEchoFile), localEchoFile) require.NoError(t, err, `"kubectl cp" failed`) localEchoFileData, err := ioutil.ReadFile(localEchoFile) require.NoError(t, err) require.Equal(t, echoString+"\n", string(localEchoFileData)) - defer func() { - _, _ = runKubectl(t, kubeconfigPath, envVarsWithProxy, "exec", "--namespace", env.ConciergeNamespace, conciergePod.Name, "--", "rm", remoteEchoFile) // cleanup remote echo file - }() // run the kubectl logs command logLinesCount := 10 - stdout, err = runKubectl(t, kubeconfigPath, envVarsWithProxy, "logs", "--namespace", env.ConciergeNamespace, conciergePod.Name, fmt.Sprintf("--tail=%d", logLinesCount)) + stdout, err = runKubectl(t, kubeconfigPath, envVarsWithProxy, "logs", "--namespace", conciergePod.Namespace, conciergePod.Name, fmt.Sprintf("--tail=%d", logLinesCount)) require.NoError(t, err, `"kubectl logs" failed`) // Expect _approximately_ logLinesCount lines in the output // (we can't match 100% exactly due to https://github.com/kubernetes/kubernetes/issues/72628). require.InDeltaf(t, logLinesCount, strings.Count(stdout, "\n"), 1, "wanted %d newlines in kubectl logs output:\n%s", logLinesCount, stdout) // run the kubectl attach command - namespaceName := createTestNamespace(t, adminClient) - attachPod := testlib.CreatePod(ctx, t, "impersonation-proxy-attach", namespaceName, corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "impersonation-proxy-attach", - Image: conciergePod.Spec.Containers[0].Image, - Command: []string{"bash"}, - Args: []string{"-c", `while true; do read VAR; echo "VAR: $VAR"; done`}, - Stdin: true, - }, - }, - }) timeout, cancelFunc := context.WithTimeout(ctx, 2*time.Minute) defer cancelFunc() - attachCmd, attachStdout, attachStderr := kubectlCommand(timeout, t, kubeconfigPath, envVarsWithProxy, "attach", "--stdin=true", "--namespace", namespaceName, attachPod.Name, "-v=10") + attachCmd, attachStdout, attachStderr := kubectlCommand(timeout, t, kubeconfigPath, envVarsWithProxy, "attach", "--stdin=true", "--namespace", runningTestPod.Namespace, runningTestPod.Name, "-v=10") attachCmd.Env = envVarsWithProxy attachStdin, err := attachCmd.StdinPipe() require.NoError(t, err) diff --git a/test/testlib/client.go b/test/testlib/client.go index 71de190a..71396858 100644 --- a/test/testlib/client.go +++ b/test/testlib/client.go @@ -478,7 +478,8 @@ func CreatePod(ctx context.Context, t *testing.T, name, namespace string, spec c client := NewKubernetesClientset(t) pods := client.CoreV1().Pods(namespace) - ctx, cancel := context.WithTimeout(ctx, time.Minute) + const podCreateTimeout = 2 * time.Minute + ctx, cancel := context.WithTimeout(ctx, podCreateTimeout+time.Second) defer cancel() created, err := pods.Create(ctx, &corev1.Pod{ObjectMeta: testObjectMeta(t, name), Spec: spec}, metav1.CreateOptions{}) @@ -497,7 +498,7 @@ func CreatePod(ctx context.Context, t *testing.T, name, namespace string, spec c result, err = pods.Get(ctx, created.Name, metav1.GetOptions{}) requireEventually.NoError(err) requireEventually.Equal(corev1.PodRunning, result.Status.Phase) - }, 15*time.Second, 1*time.Second, "expected the Pod to go into phase %s", corev1.PodRunning) + }, podCreateTimeout, 1*time.Second, "expected the Pod to go into phase %s", corev1.PodRunning) return result } From 25b4d82d873686456bc80900bc3efa6b8ec0ef66 Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Mon, 9 Aug 2021 15:31:35 -0400 Subject: [PATCH 21/34] Bump to Go 1.16.7 and Kube v0.22.0 Signed-off-by: Monis Khan --- Dockerfile | 2 +- go.mod | 18 ++--- go.sum | 217 ++++++++++++++++++++++++++++++++--------------------- 3 files changed, 141 insertions(+), 96 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0bf8054a..b3b48fec 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ # Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 -FROM golang:1.16.6 as build-env +FROM golang:1.16.7 as build-env WORKDIR /work COPY . . diff --git a/go.mod b/go.mod index ebfb573c..da359069 100644 --- a/go.mod +++ b/go.mod @@ -10,14 +10,12 @@ require ( github.com/go-ldap/ldap/v3 v3.3.0 github.com/go-logr/logr v0.4.0 github.com/go-logr/stdr v0.4.0 - github.com/go-openapi/spec v0.20.3 // indirect github.com/gofrs/flock v0.8.1 github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.5.6 github.com/google/gofuzz v1.2.0 github.com/gorilla/securecookie v1.1.1 github.com/gorilla/websocket v1.4.2 - github.com/onsi/ginkgo v1.13.0 // indirect github.com/ory/fosite v0.40.2 github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 github.com/pkg/errors v0.9.1 @@ -26,22 +24,22 @@ require ( github.com/spf13/cobra v1.2.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.7.0 - github.com/tdewolff/minify/v2 v2.9.20 + github.com/tdewolff/minify/v2 v2.9.21 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/term v0.0.0-20210503060354-a79de5458b56 gopkg.in/square/go-jose.v2 v2.6.0 - k8s.io/api v0.21.3 - k8s.io/apimachinery v0.21.3 - k8s.io/apiserver v0.21.3 - k8s.io/client-go v0.21.3 - k8s.io/component-base v0.21.3 + k8s.io/api v0.22.0 + k8s.io/apimachinery v0.22.0 + k8s.io/apiserver v0.22.0 + k8s.io/client-go v0.22.0 + k8s.io/component-base v0.22.0 k8s.io/gengo v0.0.0-20210203185629-de9496dff47b k8s.io/klog/v2 v2.10.0 - k8s.io/kube-aggregator v0.21.3 - k8s.io/utils v0.0.0-20210521133846-da695404a2bc + k8s.io/kube-aggregator v0.22.0 + k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9 sigs.k8s.io/yaml v1.2.0 ) diff --git a/go.sum b/go.sum index 478703e1..41733e3c 100644 --- a/go.sum +++ b/go.sum @@ -40,18 +40,20 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.12 h1:gI8ytXbxMfI+IVbI9mP2JGCTXIuhHLgRlvQ9X4PsnHE= -github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= -github.com/Azure/go-autorest/autorest/adal v0.9.5 h1:Y3bBUV4rTuxenJJs41HU3qmqsb+auo+a3Lz+PlJPpL0= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM= +github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= +github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= @@ -86,6 +88,7 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -99,6 +102,8 @@ github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0 github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/aws/aws-sdk-go v1.23.19/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-xray-sdk-go v0.9.4/go.mod h1:XtMKdBQfpVut+tJEwI7+dJFRxxRdxHDyVNp2tHXRq04= +github.com/benbjohnson/clock v1.0.3 h1:vkLuvpK4fmtSCuo60+yC63p7y0BmQ8gm5ZXGuBCJyXg= +github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -114,6 +119,8 @@ github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QH github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= @@ -128,8 +135,9 @@ github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMe github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= github.com/cockroachdb/cockroach-go v0.0.0-20190925194419-606b3d062051/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= github.com/cockroachdb/cockroach-go v0.0.0-20200312223839-f565e4789405/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= +github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= @@ -147,13 +155,11 @@ github.com/coreos/go-oidc/v3 v3.0.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpA github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= @@ -198,21 +204,24 @@ github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= +github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/structs v1.0.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/form3tech-oss/jwt-go v0.0.0-20200915135329-9162a5abdbc0 h1:MlJ3VGb3dbhx8w0FzhPNHh9Di62kt7rLZaCUm5Avf8Y= github.com/form3tech-oss/jwt-go v0.0.0-20200915135329-9162a5abdbc0/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= @@ -224,10 +233,12 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ= github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= @@ -274,11 +285,8 @@ github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsd github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= -github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ= -github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= @@ -559,18 +567,19 @@ github.com/gofrs/uuid/v3 v3.1.2/go.mod h1:xPwMqoocQ1L5G6pXX5BcE7N5jlzn2o19oqAKxw github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/gddo v0.0.0-20180828051604-96d2a289f41e/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= github.com/golang/gddo v0.0.0-20190904175337-72a348e765d2/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -603,8 +612,9 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -645,8 +655,9 @@ github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/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/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= +github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gopherjs/gopherjs v0.0.0-20181004151105-1babbf986f6f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -659,7 +670,6 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.1.2/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -667,12 +677,11 @@ github.com/gotestyourself/gotestyourself v1.3.0/go.mod h1:zZKM6oeNM8k+FRljX1mnzV github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= @@ -691,7 +700,6 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= 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= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= @@ -751,12 +759,13 @@ github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhB github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= +github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -766,6 +775,7 @@ github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVY github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/karrick/godirwalk v1.7.5/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34= github.com/karrick/godirwalk v1.7.7/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34= @@ -815,7 +825,6 @@ github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= @@ -855,7 +864,6 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= @@ -885,7 +893,7 @@ github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2/go.mod h1:TjQg8pa4iejrUrjiz0MCtMV38jdMNW4doKSiBrEvCQQ= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= +github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -902,6 +910,7 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= @@ -911,7 +920,6 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/oleiade/reflections v1.0.1 h1:D1XO3LVEYroYskEsoSiGItp9RUxG6jWnCVvrqH0HHQM= github.com/oleiade/reflections v1.0.1/go.mod h1:rdFxbxq4QXVZWj0F+e9jqjDkc7dbp97vkRixKo2JR60= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -919,10 +927,9 @@ github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.9.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0 h1:M76yO2HkZASFjXL0HSoZJ1AYEmQxNJmY41Jx1zNUq1Y= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -1005,8 +1012,9 @@ github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prY github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -1015,16 +1023,17 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= @@ -1087,15 +1096,17 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= @@ -1117,7 +1128,7 @@ github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tL github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= +github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -1125,7 +1136,6 @@ github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmq github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -1138,6 +1148,7 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518/go.mod h1:CKI4AZ4XmGV240rTHfO0hfE83S6/a3/Q1siZJ/vXf7A= github.com/square/go-jose/v3 v3.0.0-20200630053402-0a67ce9b0693/go.mod h1:6hSY48PjDm4UObWmGLyJE9DxYVKTgR9kbCspXXJEhcU= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -1152,8 +1163,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/subosito/gotenv v1.1.1/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tdewolff/minify/v2 v2.9.20 h1:Fut7w3T7nWfDOb/bOgyEvshQRRMt+xzi1T7spEEKXDw= -github.com/tdewolff/minify/v2 v2.9.20/go.mod h1:PoDBts2L7sCwUT28vTAlozGeD6qxjrrihtin4bR/RMM= +github.com/tdewolff/minify/v2 v2.9.21 h1:nO4s1PEMy7aRjlIlbr3Jgr+bJby8QYuifa2Vs2f9lh4= +github.com/tdewolff/minify/v2 v2.9.21/go.mod h1:PoDBts2L7sCwUT28vTAlozGeD6qxjrrihtin4bR/RMM= github.com/tdewolff/parse/v2 v2.5.19 h1:Kjaj3KQOx/4elIxlBSglus4E2oMfdROphvbq2b+OBZ0= github.com/tdewolff/parse/v2 v2.5.19/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4= @@ -1169,9 +1180,9 @@ github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV github.com/tidwall/sjson v1.0.4/go.mod h1:bURseu1nuBkFpIES5cz6zBtjmYeOQmEESshn7VpF15Y= github.com/tidwall/sjson v1.1.5/go.mod h1:VuJzsZnTowhSxWdOgsAnb886i4AjEyTkk7tNtsL7EYE= github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= github.com/uber/jaeger-client-go v2.15.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= @@ -1181,7 +1192,6 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/unrolled/secure v0.0.0-20180918153822-f340ee86eb8b/go.mod h1:mnPT77IAdsi/kV7+Es7y+pXALeV3h7G6dQF6mNYjcLA= github.com/unrolled/secure v0.0.0-20181005190816-ff9db2ff917f/go.mod h1:mnPT77IAdsi/kV7+Es7y+pXALeV3h7G6dQF6mNYjcLA= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= @@ -1202,14 +1212,22 @@ go.elastic.co/apm/module/apmhttp v1.8.0/go.mod h1:9LPFlEON51/lRbnWDfqAWErihIiAFD go.elastic.co/apm/module/apmot v1.8.0/go.mod h1:Q5Xzabte8G/fkvDjr1jlDuOSUt9hkVWNZEHh6ZNaTjI= go.elastic.co/fastjson v1.0.0/go.mod h1:PmeUOMMtLHQr9ZS9J9owrAVg0FkaZDRZJEFTTGHtchs= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489 h1:1JFLBqwIgdyHN1ZtgjTBwO+blA6gVOmZurpiMEsETKo= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= +go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/etcd/api/v3 v3.5.0 h1:GsV3S+OfZEOCNXdtNkBSR7kgLobAa/SO6tCxRa0GAYw= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0 h1:2aQv6F436YnN7I4VbI8PPYrBhu+SmrTaADcf8Mi/6PU= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0 h1:ftQ0nOOHMcbMS3KIaDQ0g5Qcd6bhaBrQT6b89DfwLTs= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.etcd.io/etcd/client/v3 v3.5.0 h1:62Eh0XOro+rDwkrypAGDfgmNh5Joq+z+W9HZdlXMzek= +go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= +go.etcd.io/etcd/pkg/v3 v3.5.0 h1:ntrg6vvKRW26JRmHTE0iNlDgYK6JX3hg/4cD62X0ixk= +go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= +go.etcd.io/etcd/raft/v3 v3.5.0 h1:kw2TmO3yFTgE+F0mdKkG7xMxkit2duBDa2Hu6D/HMlw= +go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= +go.etcd.io/etcd/server/v3 v3.5.0 h1:jk8D/lwGEDlQU9kZXUFMSANkE22Sg5+mW27ip8xcF9E= +go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= @@ -1222,13 +1240,39 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= +go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 h1:sO4WKdPAudZGKPcpZT4MJn6JaDmpyLrMPDGGyA1SttE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.13.0/go.mod h1:TwTkyRaTam1pOIb2wxcAiC2hkMVbokXkt6DEt5nDkD8= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0 h1:Q3C9yzW6I9jqEc8sawxzxZmY48fs9u220KXq6d5s3XU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= go.opentelemetry.io/otel v0.13.0/go.mod h1:dlSNewoRYikTkotEnxdmuBHgzT+k/idJSfDv/FxEnOY= +go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g= +go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= +go.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg= +go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= +go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8= +go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= +go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw= +go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= +go.opentelemetry.io/otel/sdk v0.20.0 h1:JsxtGXd06J8jrnya7fdI/U/MR6yXA5DtbZy+qoHQlr8= +go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= +go.opentelemetry.io/otel/sdk/export/metric v0.20.0 h1:c5VRjxCXdQlx1HjzwGdQHzZaVI82b5EbBgOu2ljD92g= +go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= +go.opentelemetry.io/otel/sdk/metric v0.20.0 h1:7ao1wpzHRVKf0OQ7GIxiQJA6X7DLX9o14gmVon7mMK8= +go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= +go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw= +go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= +go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -1306,6 +1350,7 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -1315,7 +1360,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= @@ -1372,9 +1416,9 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= @@ -1475,14 +1519,17 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1490,9 +1537,10 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1507,12 +1555,12 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1568,6 +1616,7 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1677,6 +1726,7 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -1688,8 +1738,8 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1724,7 +1774,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= @@ -1779,33 +1828,32 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= -k8s.io/api v0.21.3 h1:cblWILbLO8ar+Fj6xdDGr603HRsf8Wu9E9rngJeprZQ= -k8s.io/api v0.21.3/go.mod h1:hUgeYHUbBp23Ue4qdX9tR8/ANi/g3ehylAqDn9NWVOg= -k8s.io/apimachinery v0.21.3 h1:3Ju4nvjCngxxMYby0BimUk+pQHPOQp3eCGChk5kfVII= -k8s.io/apimachinery v0.21.3/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI= -k8s.io/apiserver v0.21.3 h1:QxAgE1ZPQG5cPlHScHTnLxP9H/kU3zjH1Vnd8G+n5OI= -k8s.io/apiserver v0.21.3/go.mod h1:eDPWlZG6/cCCMj/JBcEpDoK+I+6i3r9GsChYBHSbAzU= -k8s.io/client-go v0.21.3 h1:J9nxZTOmvkInRDCzcSNQmPJbDYN/PjlxXT9Mos3HcLg= -k8s.io/client-go v0.21.3/go.mod h1:+VPhCgTsaFmGILxR/7E1N0S+ryO010QBeNCv5JwRGYU= -k8s.io/code-generator v0.21.3/go.mod h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo= -k8s.io/component-base v0.21.3 h1:4WuuXY3Npa+iFfi2aDRiOz+anhNvRfye0859ZgfC5Og= -k8s.io/component-base v0.21.3/go.mod h1:kkuhtfEHeZM6LkX0saqSK8PbdO7A0HigUngmhhrwfGQ= +k8s.io/api v0.22.0 h1:elCpMZ9UE8dLdYxr55E06TmSeji9I3KH494qH70/y+c= +k8s.io/api v0.22.0/go.mod h1:0AoXXqst47OI/L0oGKq9DG61dvGRPXs7X4/B7KyjBCU= +k8s.io/apimachinery v0.22.0 h1:CqH/BdNAzZl+sr3tc0D3VsK3u6ARVSo3GWyLmfIjbP0= +k8s.io/apimachinery v0.22.0/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= +k8s.io/apiserver v0.22.0 h1:KZh2asnRBjawLLfPOi6qiD+A2jaNt31HCnZG6AX3Qcs= +k8s.io/apiserver v0.22.0/go.mod h1:04kaIEzIQrTGJ5syLppQWvpkLJXQtJECHmae+ZGc/nc= +k8s.io/client-go v0.22.0 h1:sD6o9O6tCwUKCENw8v+HFsuAbq2jCu8cWC61/ydwA50= +k8s.io/client-go v0.22.0/go.mod h1:GUjIuXR5PiEv/RVK5OODUsm6eZk7wtSWZSaSJbpFdGg= +k8s.io/code-generator v0.22.0/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= +k8s.io/component-base v0.22.0 h1:ZTmX8hUqH9T9gc0mM42O+KDgtwTYbVTt2MwmLP0eK8A= +k8s.io/component-base v0.22.0/go.mod h1:SXj6Z+V6P6GsBhHZVbWCw9hFjUdUYnJerlhhPnYCBCg= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20210203185629-de9496dff47b h1:bAU8IlrMA6KbP0dIg/sVSJn95pDCUHDZx0DpTGrf2v4= k8s.io/gengo v0.0.0-20210203185629-de9496dff47b/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.10.0 h1:R2HDMDJsHVTHA2n4RjwbeYXdOcBymXdX/JRb1v0VGhE= k8s.io/klog/v2 v2.10.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/kube-aggregator v0.21.3 h1:jS/6ZZGPCkBQhzGGusAd2St+KP/FtQBCXOCOo3H7/U4= -k8s.io/kube-aggregator v0.21.3/go.mod h1:9OIUuR5KIsNZYP/Xsh4HBsaqbS7ICJpRz3XSKtKajRc= -k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 h1:vEx13qjvaZ4yfObSSXW7BrMc/KQBBT/Jyee8XtLf4x0= -k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210521133846-da695404a2bc h1:dx6VGe+PnOW/kD/2UV4aUSsRfJGd7+lcqgJ6Xg0HwUs= -k8s.io/utils v0.0.0-20210521133846-da695404a2bc/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/kube-aggregator v0.22.0 h1:he3plI8vlaPJxR9vsy/lL5ga1V8CoA8M8x1Bn8eTCeM= +k8s.io/kube-aggregator v0.22.0/go.mod h1:zHTepg0Q4tKzru7Pwg1QYHWrU/wrvIXM8hUdDAH66qg= +k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= +k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= +k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9 h1:imL9YgXQ9p7xmPzHFm/vVd/cF78jad+n4wK1ABwYtMM= +k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= @@ -1815,11 +1863,10 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.19 h1:0jaDAAxtqIrrqas4vtTqxct4xS5kHfRNycTRLTyJmVM= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.19/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22 h1:fmRfl9WJ4ApJn7LxNuED4m0t18qivVQOxP6aAYG9J6c= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= From d2891554a43cf7037a746cf008c0f6b8a8d105ae Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Mon, 9 Aug 2021 19:16:14 -0400 Subject: [PATCH 22/34] remove google.golang.org/grpc pin Signed-off-by: Monis Khan --- go.mod | 7 ++----- go.sum | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index da359069..5578b7f6 100644 --- a/go.mod +++ b/go.mod @@ -55,9 +55,6 @@ replace github.com/oleiade/reflections v1.0.0 => github.com/oleiade/reflections // https://golang.org/issues/26904 replace github.com/dgrijalva/jwt-go v3.2.0+incompatible => github.com/form3tech-oss/jwt-go v0.0.0-20200915135329-9162a5abdbc0 -// Pin gRPC back to v1.29.1 (the version required by Kubernetes), but also override a module that's only used in some tests. +// Pin a gRPC module that's only used in some tests. // This is required because sometime after v1.29.1, they moved this package into a separate module. -replace ( - google.golang.org/grpc => google.golang.org/grpc v1.29.1 - google.golang.org/grpc/examples => ./hack/dependencyhacks/grpcexamples/ -) +replace google.golang.org/grpc/examples => ./hack/dependencyhacks/grpcexamples/ diff --git a/go.sum b/go.sum index 41733e3c..620de4c7 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,5 @@ bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg= @@ -130,7 +131,10 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= github.com/cockroachdb/cockroach-go v0.0.0-20190925194419-606b3d062051/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= @@ -202,7 +206,13 @@ github.com/elazarl/goproxy v0.0.0-20181003060214-f58a169a71a5/go.mod h1:/Zj4wYkg github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -564,6 +574,7 @@ github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j github.com/gofrs/uuid v3.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid/v3 v3.1.2/go.mod h1:xPwMqoocQ1L5G6pXX5BcE7N5jlzn2o19oqAKxwZW/kI= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -817,6 +828,7 @@ github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/luna-duclos/instrumentedsql v0.0.0-20181127104832-b7d587d28109/go.mod h1:PWUIzhtavmOR965zfawVsHXbEuU1G29BPZ/CB3C7jXk= github.com/luna-duclos/instrumentedsql v1.1.2/go.mod h1:4LGbEqDnopzNAiyxPPDXhLspyunZxgPTMJBKtC6U0BQ= github.com/luna-duclos/instrumentedsql v1.1.3/go.mod h1:9J1njvFds+zN7y85EDhN9XNQLANWwZt2ULeIC8yMNYs= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= @@ -1340,6 +1352,7 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMk golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -1366,6 +1379,7 @@ golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180816102801-aaf60122140d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180921000356-2f5d2388922f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1453,6 +1467,7 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cO golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180831094639-fa5fdf94c789/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-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1586,6 +1601,7 @@ golang.org/x/tools v0.0.0-20181212172921-837e80568c09/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190102213336-ca9055ed7d04/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190104182027-498d95493402/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190111214448-fc1d57b08d7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190118193359-16909d206f00/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1695,6 +1711,7 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1750,8 +1767,32 @@ google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= From a027f1ae2c1a97ee4da940aca5dd62d2eea3cbe3 Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Mon, 9 Aug 2021 19:16:25 -0400 Subject: [PATCH 23/34] jwtcachefiller: update to use CAContentProvider Signed-off-by: Monis Khan --- .../jwtcachefiller/jwtcachefiller.go | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go index de1bf15c..66c96a06 100644 --- a/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go +++ b/internal/controller/authenticator/jwtcachefiller/jwtcachefiller.go @@ -7,14 +7,13 @@ package jwtcachefiller import ( "fmt" - "io/ioutil" - "os" "reflect" "github.com/go-logr/logr" "gopkg.in/square/go-jose.v2" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apiserver/pkg/authentication/authenticator" + "k8s.io/apiserver/pkg/server/dynamiccertificates" "k8s.io/apiserver/plugin/pkg/authenticator/token/oidc" "k8s.io/klog/v2" @@ -151,22 +150,13 @@ func newJWTAuthenticator(spec *auth1alpha1.JWTAuthenticatorSpec) (*jwtAuthentica return nil, fmt.Errorf("invalid TLS configuration: %w", err) } - var caFile string - if caBundle != nil { - temp, err := ioutil.TempFile("", "pinniped-jwkauthenticator-cafile-*") - if err != nil { - return nil, fmt.Errorf("unable to create temporary file: %w", err) + var caContentProvider oidc.CAContentProvider + if len(caBundle) != 0 { + var caContentProviderErr error + caContentProvider, caContentProviderErr = dynamiccertificates.NewStaticCAContent("ignored", caBundle) + if caContentProviderErr != nil { + return nil, caContentProviderErr // impossible since caBundle is validated already } - - // We can safely remove the temp file at the end of this function since oidc.New() reads the - // provided CA file and then forgets about it. - defer func() { _ = os.Remove(temp.Name()) }() - - if _, err := temp.Write(caBundle); err != nil { - return nil, fmt.Errorf("cannot write CA file: %w", err) - } - - caFile = temp.Name() } usernameClaim := spec.Claims.Username if usernameClaim == "" { @@ -183,7 +173,7 @@ func newJWTAuthenticator(spec *auth1alpha1.JWTAuthenticatorSpec) (*jwtAuthentica UsernameClaim: usernameClaim, GroupsClaim: groupsClaim, SupportedSigningAlgs: defaultSupportedSigningAlgos(), - CAFile: caFile, + CAContentProvider: caContentProvider, }) if err != nil { return nil, fmt.Errorf("could not initialize authenticator: %w", err) From 724acdca1dc567c1253d815325cf8405b41d190a Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Mon, 9 Aug 2021 19:16:50 -0400 Subject: [PATCH 24/34] Update tests for new CSR duration code Signed-off-by: Monis Khan --- test/integration/concierge_impersonation_proxy_test.go | 2 ++ test/integration/whoami_test.go | 1 + 2 files changed, 3 insertions(+) diff --git a/test/integration/concierge_impersonation_proxy_test.go b/test/integration/concierge_impersonation_proxy_test.go index bced36fe..66c53e13 100644 --- a/test/integration/concierge_impersonation_proxy_test.go +++ b/test/integration/concierge_impersonation_proxy_test.go @@ -965,6 +965,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl csrPEM, "", certificatesv1.KubeAPIServerClientSignerName, + nil, []certificatesv1.KeyUsage{certificatesv1.UsageClientAuth}, privateKey, ) @@ -2319,6 +2320,7 @@ func getUIDAndExtraViaCSR(ctx context.Context, t *testing.T, uid string, client csrPEM, "", certificatesv1.KubeAPIServerClientSignerName, + nil, []certificatesv1.KeyUsage{certificatesv1.UsageClientAuth}, privateKey, ) diff --git a/test/integration/whoami_test.go b/test/integration/whoami_test.go index 3a64db96..ffb17db6 100644 --- a/test/integration/whoami_test.go +++ b/test/integration/whoami_test.go @@ -274,6 +274,7 @@ func TestWhoAmI_CSR(t *testing.T) { csrPEM, "", certificatesv1.KubeAPIServerClientSignerName, + nil, []certificatesv1.KeyUsage{certificatesv1.UsageClientAuth}, privateKey, ) From 4a17e1e736043f210b145a716869c0d9721fd54c Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Mon, 9 Aug 2021 19:11:32 -0400 Subject: [PATCH 25/34] impersonator: update tests for new Impersonate-Uid code Signed-off-by: Monis Khan --- .../impersonator/impersonator_test.go | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/internal/concierge/impersonator/impersonator_test.go b/internal/concierge/impersonator/impersonator_test.go index a3b743bb..b67b3733 100644 --- a/internal/concierge/impersonator/impersonator_test.go +++ b/internal/concierge/impersonator/impersonator_test.go @@ -468,14 +468,18 @@ func TestImpersonator(t *testing.T) { header["Impersonate-Uid"] = []string{"root"} }, kubeAPIServerClientBearerTokenFile: "required-to-be-set", - wantError: "Internal error occurred: invalid impersonation", + wantError: "Internal error occurred: unimplemented functionality - unable to act as current user", wantAuthorizerAttributes: []authorizer.AttributesRecord{ { User: &user.DefaultInfo{Name: "test-admin", UID: "", Groups: []string{"test-group2", "system:masters", "system:authenticated"}, Extra: nil}, Verb: "impersonate", Namespace: "", APIGroup: "", APIVersion: "", Resource: "users", Subresource: "", Name: "some-other-username", ResourceRequest: true, Path: "", }, { - User: &user.DefaultInfo{Name: "some-other-username", UID: "", Groups: []string{"system:authenticated"}, Extra: map[string][]string{}}, + User: &user.DefaultInfo{Name: "test-admin", UID: "", Groups: []string{"test-group2", "system:masters", "system:authenticated"}, Extra: nil}, + Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "uids", Subresource: "", Name: "root", ResourceRequest: true, Path: "", + }, + { + User: &user.DefaultInfo{Name: "some-other-username", UID: "root", Groups: []string{"system:authenticated"}, Extra: map[string][]string{}}, Verb: "list", Namespace: "", APIGroup: "", APIVersion: "v1", Resource: "namespaces", Subresource: "", Name: "", ResourceRequest: true, Path: "/api/v1/namespaces", }, }, @@ -488,14 +492,18 @@ func TestImpersonator(t *testing.T) { header["imPerSoNaTE-uid"] = []string{"magic"} }, kubeAPIServerClientBearerTokenFile: "required-to-be-set", - wantError: "Internal error occurred: invalid impersonation", + wantError: "Internal error occurred: unimplemented functionality - unable to act as current user", wantAuthorizerAttributes: []authorizer.AttributesRecord{ { User: &user.DefaultInfo{Name: "test-admin", UID: "", Groups: []string{"test-group2", "system:masters", "system:authenticated"}, Extra: nil}, Verb: "impersonate", Namespace: "", APIGroup: "", APIVersion: "", Resource: "users", Subresource: "", Name: "some-other-username", ResourceRequest: true, Path: "", }, { - User: &user.DefaultInfo{Name: "some-other-username", UID: "", Groups: []string{"system:authenticated"}, Extra: map[string][]string{}}, + User: &user.DefaultInfo{Name: "test-admin", UID: "", Groups: []string{"test-group2", "system:masters", "system:authenticated"}, Extra: nil}, + Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "uids", Subresource: "", Name: "magic", ResourceRequest: true, Path: "", + }, + { + User: &user.DefaultInfo{Name: "some-other-username", UID: "magic", Groups: []string{"system:authenticated"}, Extra: map[string][]string{}}, Verb: "list", Namespace: "", APIGroup: "", APIVersion: "v1", Resource: "namespaces", Subresource: "", Name: "", ResourceRequest: true, Path: "/api/v1/namespaces", }, }, @@ -645,34 +653,24 @@ func TestImpersonator(t *testing.T) { }, }, { - name: "header canonicalization future UID header", + name: "header canonicalization future UID header", // no longer future as it exists in Kube v1.22 clientCert: newClientCert(t, ca, "test-username", []string{"test-group1", "test-group2"}), clientMutateHeaders: func(header http.Header) { header["imPerSonaTE-uid"] = []string{"007"} }, kubeAPIServerClientBearerTokenFile: "required-to-be-set", - wantError: "Internal error occurred: invalid impersonation", - wantAuthorizerAttributes: []authorizer.AttributesRecord{ - { - User: &user.DefaultInfo{Name: "test-username", UID: "", Groups: []string{"test-group1", "test-group2", "system:authenticated"}, Extra: nil}, - Verb: "list", Namespace: "", APIGroup: "", APIVersion: "v1", Resource: "namespaces", Subresource: "", Name: "", ResourceRequest: true, Path: "/api/v1/namespaces", - }, - }, + wantError: `an error on the server ("Internal Server Error: \"/api/v1/namespaces\": requested [{UID 007 authentication.k8s.io/v1 }] without impersonating a user") has prevented the request from succeeding (get namespaces)`, + wantAuthorizerAttributes: []authorizer.AttributesRecord{}, }, { - name: "future UID header", + name: "future UID header", // no longer future as it exists in Kube v1.22 clientCert: newClientCert(t, ca, "test-username", []string{"test-group1", "test-group2"}), clientMutateHeaders: func(header http.Header) { header["Impersonate-Uid"] = []string{"008"} }, kubeAPIServerClientBearerTokenFile: "required-to-be-set", - wantError: "Internal error occurred: invalid impersonation", - wantAuthorizerAttributes: []authorizer.AttributesRecord{ - { - User: &user.DefaultInfo{Name: "test-username", UID: "", Groups: []string{"test-group1", "test-group2", "system:authenticated"}, Extra: nil}, - Verb: "list", Namespace: "", APIGroup: "", APIVersion: "v1", Resource: "namespaces", Subresource: "", Name: "", ResourceRequest: true, Path: "/api/v1/namespaces", - }, - }, + wantError: `an error on the server ("Internal Server Error: \"/api/v1/namespaces\": requested [{UID 008 authentication.k8s.io/v1 }] without impersonating a user") has prevented the request from succeeding (get namespaces)`, + wantAuthorizerAttributes: []authorizer.AttributesRecord{}, }, } for _, tt := range tests { From 5678fc61965196e787e575e237910efaf7a549b4 Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Mon, 9 Aug 2021 19:14:10 -0400 Subject: [PATCH 26/34] login: update tests for new client exec code Signed-off-by: Monis Khan --- cmd/pinniped/cmd/login_oidc_test.go | 8 ++++---- cmd/pinniped/cmd/login_static_test.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/pinniped/cmd/login_oidc_test.go b/cmd/pinniped/cmd/login_oidc_test.go index 055dcec6..793c2595 100644 --- a/cmd/pinniped/cmd/login_oidc_test.go +++ b/cmd/pinniped/cmd/login_oidc_test.go @@ -160,7 +160,7 @@ func TestLoginOIDCCommand(t *testing.T) { "--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution }, wantOptionsCount: 4, - wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n", + wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n", }, { name: "ldap upstream type is allowed", @@ -171,7 +171,7 @@ func TestLoginOIDCCommand(t *testing.T) { "--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution }, wantOptionsCount: 5, - wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n", + wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n", }, { name: "login error", @@ -214,7 +214,7 @@ func TestLoginOIDCCommand(t *testing.T) { }, env: map[string]string{"PINNIPED_DEBUG": "true"}, wantOptionsCount: 4, - wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n", + wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n", wantLogs: []string{ "\"level\"=0 \"msg\"=\"Pinniped login: Performing OIDC login\" \"client id\"=\"test-client-id\" \"issuer\"=\"test-issuer\"", "\"level\"=0 \"msg\"=\"Pinniped login: No concierge configured, skipping token credential exchange\"", @@ -244,7 +244,7 @@ func TestLoginOIDCCommand(t *testing.T) { }, env: map[string]string{"PINNIPED_DEBUG": "true"}, wantOptionsCount: 11, - wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"token":"exchanged-token"}}` + "\n", + wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"token":"exchanged-token"}}` + "\n", wantLogs: []string{ "\"level\"=0 \"msg\"=\"Pinniped login: Performing OIDC login\" \"client id\"=\"test-client-id\" \"issuer\"=\"test-issuer\"", "\"level\"=0 \"msg\"=\"Pinniped login: Exchanging token for cluster credential\" \"authenticator name\"=\"test-authenticator\" \"authenticator type\"=\"webhook\" \"endpoint\"=\"https://127.0.0.1:1234/\"", diff --git a/cmd/pinniped/cmd/login_static_test.go b/cmd/pinniped/cmd/login_static_test.go index 7db88e32..ab9ebb28 100644 --- a/cmd/pinniped/cmd/login_static_test.go +++ b/cmd/pinniped/cmd/login_static_test.go @@ -119,7 +119,7 @@ func TestLoginStaticCommand(t *testing.T) { env: map[string]string{ "TEST_TOKEN_ENV": "test-token", }, - wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"token":"test-token"}}` + "\n", + wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"token":"test-token"}}` + "\n", }, { name: "concierge failure", @@ -159,7 +159,7 @@ func TestLoginStaticCommand(t *testing.T) { "--token", "test-token", }, env: map[string]string{"PINNIPED_DEBUG": "true"}, - wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"token":"test-token"}}` + "\n", + wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"token":"test-token"}}` + "\n", }, } for _, tt := range tests { From 34fd0ea2e2b94e4070212ef7925e944e904b8e47 Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Tue, 10 Aug 2021 00:03:33 -0400 Subject: [PATCH 27/34] impersonation proxy: assert nested UID impersonation is disallowed Signed-off-by: Monis Khan --- .../concierge_impersonation_proxy_test.go | 82 ++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/test/integration/concierge_impersonation_proxy_test.go b/test/integration/concierge_impersonation_proxy_test.go index 66c53e13..21e90f69 100644 --- a/test/integration/concierge_impersonation_proxy_test.go +++ b/test/integration/concierge_impersonation_proxy_test.go @@ -751,6 +751,79 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl }, err) }) + t.Run("nested impersonation as a cluster admin fails if UID impersonation is attempted", func(t *testing.T) { + parallelIfNotEKS(t) + adminClientRestConfig := testlib.NewClientConfig(t) + clusterAdminCredentials := getCredForConfig(t, adminClientRestConfig) + + nestedImpersonationUIDOnly := newImpersonationProxyConfigWithCredentials(t, + clusterAdminCredentials, impersonationProxyURL, impersonationProxyCACertPEM, nil, + ) + nestedImpersonationUIDOnly.Wrap(func(rt http.RoundTripper) http.RoundTripper { + return roundtripper.Func(func(r *http.Request) (*http.Response, error) { + r.Header.Set("iMperSONATE-uid", "some-awesome-uid") + return rt.RoundTrip(r) + }) + }) + + _, errUID := testlib.NewKubeclient(t, nestedImpersonationUIDOnly).Kubernetes.CoreV1().Secrets("foo").Get(ctx, "bar", metav1.GetOptions{}) + msg := `Internal Server Error: "/api/v1/namespaces/foo/secrets/bar": requested [{UID some-awesome-uid authentication.k8s.io/v1 }] without impersonating a user` + full := fmt.Sprintf(`an error on the server (%q) has prevented the request from succeeding (get secrets bar)`, msg) + require.EqualError(t, errUID, full) + require.True(t, k8serrors.IsInternalError(errUID), errUID) + require.Equal(t, &k8serrors.StatusError{ + ErrStatus: metav1.Status{ + Status: metav1.StatusFailure, + Code: http.StatusInternalServerError, + Reason: metav1.StatusReasonInternalError, + Details: &metav1.StatusDetails{ + Name: "bar", + Kind: "secrets", + Causes: []metav1.StatusCause{ + { + Type: metav1.CauseTypeUnexpectedServerResponse, + Message: msg, + }, + }, + }, + Message: full, + }, + }, errUID) + + nestedImpersonationUID := newImpersonationProxyConfigWithCredentials(t, + clusterAdminCredentials, impersonationProxyURL, impersonationProxyCACertPEM, + &rest.ImpersonationConfig{ + UserName: "other-user-to-impersonate", + Groups: []string{"system:masters"}, // impersonate system:masters so we get past authorization checks + }, + ) + nestedImpersonationUID.Wrap(func(rt http.RoundTripper) http.RoundTripper { + return roundtripper.Func(func(r *http.Request) (*http.Response, error) { + r.Header.Set("imperSONate-uiD", "some-fancy-uid") + return rt.RoundTrip(r) + }) + }) + + _, err := testlib.NewKubeclient(t, nestedImpersonationUID).Kubernetes.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, impersonationProxyTLSSecretName(env), metav1.GetOptions{}) + require.EqualError(t, err, "Internal error occurred: unimplemented functionality - unable to act as current user") + require.True(t, k8serrors.IsInternalError(err), err) + require.Equal(t, &k8serrors.StatusError{ + ErrStatus: metav1.Status{ + Status: metav1.StatusFailure, + Code: http.StatusInternalServerError, + Reason: metav1.StatusReasonInternalError, + Details: &metav1.StatusDetails{ + Causes: []metav1.StatusCause{ + { + Message: "unimplemented functionality - unable to act as current user", + }, + }, + }, + Message: "Internal error occurred: unimplemented functionality - unable to act as current user", + }, + }, err) + }) + // this works because impersonation cannot set UID and thus the final user info the proxy sees has no UID t.Run("nested impersonation as a service account is allowed if it has enough RBAC permissions", func(t *testing.T) { parallelIfNotEKS(t) @@ -2168,6 +2241,13 @@ func createTokenCredentialRequest( func newImpersonationProxyClientWithCredentials(t *testing.T, credentials *loginv1alpha1.ClusterCredential, impersonationProxyURL string, impersonationProxyCACertPEM []byte, nestedImpersonationConfig *rest.ImpersonationConfig) *kubeclient.Client { t.Helper() + kubeconfig := newImpersonationProxyConfigWithCredentials(t, credentials, impersonationProxyURL, impersonationProxyCACertPEM, nestedImpersonationConfig) + return testlib.NewKubeclient(t, kubeconfig) +} + +func newImpersonationProxyConfigWithCredentials(t *testing.T, credentials *loginv1alpha1.ClusterCredential, impersonationProxyURL string, impersonationProxyCACertPEM []byte, nestedImpersonationConfig *rest.ImpersonationConfig) *rest.Config { + t.Helper() + env := testlib.IntegrationEnv(t) clusterSupportsLoadBalancers := env.HasCapability(testlib.HasExternalLoadBalancerProvider) @@ -2177,7 +2257,7 @@ func newImpersonationProxyClientWithCredentials(t *testing.T, credentials *login // Prefer to go through a load balancer because that's how the impersonator is intended to be used in the real world. kubeconfig.Proxy = kubeconfigProxyFunc(t, env.Proxy) } - return testlib.NewKubeclient(t, kubeconfig) + return kubeconfig } func newAnonymousImpersonationProxyClient(t *testing.T, impersonationProxyURL string, impersonationProxyCACertPEM []byte, nestedImpersonationConfig *rest.ImpersonationConfig) *kubeclient.Client { From 592563124bb1aa0cb969c65242b51fd038c8ba51 Mon Sep 17 00:00:00 2001 From: anjalitelang <49958114+anjaltelang@users.noreply.github.com> Date: Thu, 12 Aug 2021 11:08:27 -0400 Subject: [PATCH 28/34] Update ROADMAP.md Updated the roadmap to reflect the Non-Interactive Password based Login support we delivered in July for LDAP and TBD in Aug for OIDC --- ROADMAP.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ROADMAP.md b/ROADMAP.md index cf5c33ba..4855c498 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -37,6 +37,8 @@ Last Updated: July 2021 Theme|Description|Timeline| |--|--|--| |Remote OIDC login support|Add support for logging in from remote hosts without web browsers in the Pinniped CLI and Supervisor|Jul 2021| +|Non-Interactive Password based LDAP logins |Support for non-interactive LDAP Logins via CLI using Environmental Variables |Jul 2021| +|Non-Interactive Password based OIDC logins |Support for non-interactive OIDC Logins via CLI using Password Grant |Aug 2021| |Active Directory Support|Extends upstream IDP protocols|Aug 2021| |Multiple IDP support|Support multiple IDPs configured on a single Supervisor|Sept 2021| |Wider Concierge cluster support|Support for more cluster types in the Concierge|Sept 2021| From 942c55cf517996dea0c3a5c46644941c71ee1148 Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Fri, 13 Aug 2021 20:19:32 -0400 Subject: [PATCH 29/34] cli: prevent browser output from breaking ExecCredential output This change updates the pinniped CLI entrypoint to prevent browser processes that we spawn from polluting our std out stream. For example, chrome will print the following message to std out: Opening in existing browser session. Which leads to the following incomprehensible error message from kubectl: Unable to connect to the server: getting credentials: decoding stdout: couldn't get version/kind; json parse error: json: cannot unmarshal string into Go value of type struct { APIVersion string "json:\"apiVersion,omitempty\""; Kind string "json:\"kind,omitempty\"" } This would only occur on the initial login when we opened the browser. Since credentials would be cached afterwards, kubectl would work as expected for future invocations as no browser was opened. I could not think of a good way to actually test this change. There is a clear gap in our integration tests - we never actually launch a browser in the exact same way a user does - we instead open a chrome driver at the login URL as a subprocess of the integration test binary and not the pinniped CLI. Thus even if the chrome driver was writing to std out, we would not notice any issues. It is also unclear if there is a good way to prevent future related bugs since std out is global to the process. Signed-off-by: Monis Khan --- cmd/pinniped/main.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/cmd/pinniped/main.go b/cmd/pinniped/main.go index 96213ec8..6b5a4611 100644 --- a/cmd/pinniped/main.go +++ b/cmd/pinniped/main.go @@ -3,7 +3,20 @@ package main -import "go.pinniped.dev/cmd/pinniped/cmd" +import ( + "os" + + "github.com/pkg/browser" + + "go.pinniped.dev/cmd/pinniped/cmd" +) + +//nolint: gochecknoinits +func init() { + // browsers like chrome like to write to our std out which breaks our JSON ExecCredential output + // thus we redirect the browser's std out to our std err + browser.Stdout = os.Stderr +} func main() { cmd.Execute() From 7a812ac5ed0f3b67dc121f8fbba662f03f16cba2 Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Tue, 10 Aug 2021 12:05:04 -0400 Subject: [PATCH 30/34] impersonatorconfig: only unload dynamiccert when proxy is disabled In the upstream dynamiccertificates package, we rely on two pieces of code: 1. DynamicServingCertificateController.newTLSContent which calls - clientCA.CurrentCABundleContent - servingCert.CurrentCertKeyContent 2. unionCAContent.VerifyOptions which calls - unionCAContent.CurrentCABundleContent This results in calls to our tlsServingCertDynamicCertProvider and impersonationSigningCertProvider. If we Unset these providers, we subtly break these consumers. At best this results in test slowness and flakes while we wait for reconcile loops to converge. At worst, it results in actual errors during runtime. For example, we previously would Unset the impersonationSigningCertProvider on any sync loop error (even a transient one caused by a network blip or a conflict between writes from different replicas of the concierge). This would cause us to transiently fail to issue new certificates from the token credential require API. It would also cause us to transiently fail to authenticate previously issued client certs (which results in occasional Unauthorized errors in CI). Signed-off-by: Monis Khan --- .../controller/apicerts/certs_expirer_test.go | 39 +-- .../impersonatorconfig/impersonator_config.go | 94 +++---- .../impersonator_config_test.go | 229 ++++++++++++++---- .../kubecertagent/kubecertagent_test.go | 1 - internal/testutil/delete.go | 47 ++++ .../concierge_impersonation_proxy_test.go | 69 +++++- 6 files changed, 344 insertions(+), 135 deletions(-) create mode 100644 internal/testutil/delete.go diff --git a/internal/controller/apicerts/certs_expirer_test.go b/internal/controller/apicerts/certs_expirer_test.go index da508c55..01040423 100644 --- a/internal/controller/apicerts/certs_expirer_test.go +++ b/internal/controller/apicerts/certs_expirer_test.go @@ -20,9 +20,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" kubeinformers "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" kubernetesfake "k8s.io/client-go/kubernetes/fake" - corev1client "k8s.io/client-go/kubernetes/typed/core/v1" kubetesting "k8s.io/client-go/testing" "go.pinniped.dev/internal/controllerlib" @@ -253,7 +251,8 @@ func TestExpirerControllerSync(t *testing.T) { 0, ) - trackDeleteClient := &clientWrapper{Interface: kubeAPIClient, opts: &[]metav1.DeleteOptions{}} + opts := &[]metav1.DeleteOptions{} + trackDeleteClient := testutil.NewDeleteOptionsRecorder(kubeAPIClient, opts) c := NewCertsExpirerController( namespace, @@ -297,44 +296,16 @@ func TestExpirerControllerSync(t *testing.T) { require.Equal(t, exActions, acActions) if test.wantDelete { - require.Len(t, *trackDeleteClient.opts, 1) + require.Len(t, *opts, 1) require.Equal(t, metav1.DeleteOptions{ Preconditions: &metav1.Preconditions{ UID: &testUID, ResourceVersion: &testRV, }, - }, (*trackDeleteClient.opts)[0]) + }, (*opts)[0]) } else { - require.Len(t, *trackDeleteClient.opts, 0) + require.Len(t, *opts, 0) } }) } } - -type clientWrapper struct { - kubernetes.Interface - opts *[]metav1.DeleteOptions -} - -func (c *clientWrapper) CoreV1() corev1client.CoreV1Interface { - return &coreWrapper{CoreV1Interface: c.Interface.CoreV1(), opts: c.opts} -} - -type coreWrapper struct { - corev1client.CoreV1Interface - opts *[]metav1.DeleteOptions -} - -func (c *coreWrapper) Secrets(namespace string) corev1client.SecretInterface { - return &secretsWrapper{SecretInterface: c.CoreV1Interface.Secrets(namespace), opts: c.opts} -} - -type secretsWrapper struct { - corev1client.SecretInterface - opts *[]metav1.DeleteOptions -} - -func (s *secretsWrapper) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - *s.opts = append(*s.opts, opts) - return s.SecretInterface.Delete(ctx, name, opts) -} diff --git a/internal/controller/impersonatorconfig/impersonator_config.go b/internal/controller/impersonatorconfig/impersonator_config.go index f5845013..c4296877 100644 --- a/internal/controller/impersonatorconfig/impersonator_config.go +++ b/internal/controller/impersonatorconfig/impersonator_config.go @@ -183,8 +183,6 @@ func (c *impersonatorConfigController) Sync(syncCtx controllerlib.Context) error Message: err.Error(), LastUpdateTime: metav1.NewTime(c.clock.Now()), } - // The impersonator is not ready, so clear the signer CA from the dynamic provider. - c.clearSignerCA() } err = utilerrors.NewAggregate([]error{err, issuerconfig.Update( @@ -281,27 +279,32 @@ func (c *impersonatorConfigController) doSync(syncCtx controllerlib.Context, cre nameInfo, err := c.findDesiredTLSCertificateName(impersonationSpec) if err != nil { - // Unexpected error while determining the name that should go into the certs, so clear any existing certs. - c.tlsServingCertDynamicCertProvider.UnsetCertKeyContent() return nil, err } var impersonationCA *certauthority.CA - if c.shouldHaveTLSSecret(impersonationSpec) { + if c.shouldHaveImpersonator(impersonationSpec) { if impersonationCA, err = c.ensureCASecretIsCreated(ctx); err != nil { return nil, err } if err = c.ensureTLSSecret(ctx, nameInfo, impersonationCA); err != nil { return nil, err } - } else if err = c.ensureTLSSecretIsRemoved(ctx); err != nil { - return nil, err + } else { + if err = c.ensureTLSSecretIsRemoved(ctx); err != nil { + return nil, err + } + c.clearTLSSecret() } credentialIssuerStrategyResult := c.doSyncResult(nameInfo, impersonationSpec, impersonationCA) - if err = c.loadSignerCA(credentialIssuerStrategyResult.Status); err != nil { - return nil, err + if c.shouldHaveImpersonator(impersonationSpec) { + if err = c.loadSignerCA(); err != nil { + return nil, err + } + } else { + c.clearSignerCA() } return credentialIssuerStrategyResult, nil @@ -350,20 +353,16 @@ func (c *impersonatorConfigController) shouldHaveClusterIPService(config *v1alph return c.shouldHaveImpersonator(config) && config.Service.Type == v1alpha1.ImpersonationProxyServiceTypeClusterIP } -func (c *impersonatorConfigController) shouldHaveTLSSecret(config *v1alpha1.ImpersonationProxySpec) bool { - return c.shouldHaveImpersonator(config) -} - -func (c *impersonatorConfigController) serviceExists(serviceName string) (bool, error) { - _, err := c.servicesInformer.Lister().Services(c.namespace).Get(serviceName) +func (c *impersonatorConfigController) serviceExists(serviceName string) (bool, *v1.Service, error) { + service, err := c.servicesInformer.Lister().Services(c.namespace).Get(serviceName) notFound := k8serrors.IsNotFound(err) if notFound { - return false, nil + return false, nil, nil } if err != nil { - return false, err + return false, nil, err } - return true, nil + return true, service, nil } func (c *impersonatorConfigController) tlsSecretExists() (bool, *v1.Secret, error) { @@ -477,7 +476,7 @@ func (c *impersonatorConfigController) ensureLoadBalancerIsStarted(ctx context.C } func (c *impersonatorConfigController) ensureLoadBalancerIsStopped(ctx context.Context) error { - running, err := c.serviceExists(c.generatedLoadBalancerServiceName) + running, service, err := c.serviceExists(c.generatedLoadBalancerServiceName) if err != nil { return err } @@ -488,7 +487,12 @@ func (c *impersonatorConfigController) ensureLoadBalancerIsStopped(ctx context.C c.infoLog.Info("deleting load balancer for impersonation proxy", "service", klog.KRef(c.namespace, c.generatedLoadBalancerServiceName), ) - err = c.k8sClient.CoreV1().Services(c.namespace).Delete(ctx, c.generatedLoadBalancerServiceName, metav1.DeleteOptions{}) + err = c.k8sClient.CoreV1().Services(c.namespace).Delete(ctx, c.generatedLoadBalancerServiceName, metav1.DeleteOptions{ + Preconditions: &metav1.Preconditions{ + UID: &service.UID, + ResourceVersion: &service.ResourceVersion, + }, + }) return utilerrors.FilterOut(err, k8serrors.IsNotFound) } @@ -517,7 +521,7 @@ func (c *impersonatorConfigController) ensureClusterIPServiceIsStarted(ctx conte } func (c *impersonatorConfigController) ensureClusterIPServiceIsStopped(ctx context.Context) error { - running, err := c.serviceExists(c.generatedClusterIPServiceName) + running, service, err := c.serviceExists(c.generatedClusterIPServiceName) if err != nil { return err } @@ -528,7 +532,12 @@ func (c *impersonatorConfigController) ensureClusterIPServiceIsStopped(ctx conte c.infoLog.Info("deleting cluster ip for impersonation proxy", "service", klog.KRef(c.namespace, c.generatedClusterIPServiceName), ) - err = c.k8sClient.CoreV1().Services(c.namespace).Delete(ctx, c.generatedClusterIPServiceName, metav1.DeleteOptions{}) + err = c.k8sClient.CoreV1().Services(c.namespace).Delete(ctx, c.generatedClusterIPServiceName, metav1.DeleteOptions{ + Preconditions: &metav1.Preconditions{ + UID: &service.UID, + ResourceVersion: &service.ResourceVersion, + }, + }) return utilerrors.FilterOut(err, k8serrors.IsNotFound) } @@ -942,7 +951,6 @@ func (c *impersonatorConfigController) loadTLSCertFromSecret(tlsSecret *v1.Secre keyPEM := tlsSecret.Data[v1.TLSPrivateKeyKey] if err := c.tlsServingCertDynamicCertProvider.SetCertKeyContent(certPEM, keyPEM); err != nil { - c.tlsServingCertDynamicCertProvider.UnsetCertKeyContent() return fmt.Errorf("could not parse TLS cert PEM data from Secret: %w", err) } @@ -955,39 +963,33 @@ func (c *impersonatorConfigController) loadTLSCertFromSecret(tlsSecret *v1.Secre } func (c *impersonatorConfigController) ensureTLSSecretIsRemoved(ctx context.Context) error { - tlsSecretExists, _, err := c.tlsSecretExists() + tlsSecretExists, secret, err := c.tlsSecretExists() if err != nil { return err } if !tlsSecretExists { return nil } - c.infoLog.Info("deleting TLS certificates for impersonation proxy", + c.infoLog.Info("deleting TLS serving certificate for impersonation proxy", "secret", klog.KRef(c.namespace, c.tlsSecretName), ) - err = c.k8sClient.CoreV1().Secrets(c.namespace).Delete(ctx, c.tlsSecretName, metav1.DeleteOptions{}) - notFound := k8serrors.IsNotFound(err) - if notFound { - // its okay if we tried to delete and we got a not found error. This probably means - // another instance of the concierge got here first so there's nothing to delete. - return nil - } - if err != nil { - return err - } - - c.tlsServingCertDynamicCertProvider.UnsetCertKeyContent() - - return nil + err = c.k8sClient.CoreV1().Secrets(c.namespace).Delete(ctx, c.tlsSecretName, metav1.DeleteOptions{ + Preconditions: &metav1.Preconditions{ + UID: &secret.UID, + ResourceVersion: &secret.ResourceVersion, + }, + }) + // it is okay if we tried to delete and we got a not found error. This probably means + // another instance of the concierge got here first so there's nothing to delete. + return utilerrors.FilterOut(err, k8serrors.IsNotFound) } -func (c *impersonatorConfigController) loadSignerCA(status v1alpha1.StrategyStatus) error { - // Clear it when the impersonator is not completely ready. - if status != v1alpha1.SuccessStrategyStatus { - c.clearSignerCA() - return nil - } +func (c *impersonatorConfigController) clearTLSSecret() { + c.debugLog.Info("clearing TLS serving certificate for impersonation proxy") + c.tlsServingCertDynamicCertProvider.UnsetCertKeyContent() +} +func (c *impersonatorConfigController) loadSignerCA() error { signingCertSecret, err := c.secretsInformer.Lister().Secrets(c.namespace).Get(c.impersonationSignerSecretName) if err != nil { return fmt.Errorf("could not load the impersonator's credential signing secret: %w", err) @@ -997,7 +999,7 @@ func (c *impersonatorConfigController) loadSignerCA(status v1alpha1.StrategyStat keyPEM := signingCertSecret.Data[apicerts.CACertificatePrivateKeySecretKey] if err := c.impersonationSigningCertProvider.SetCertKeyContent(certPEM, keyPEM); err != nil { - return fmt.Errorf("could not load the impersonator's credential signing secret: %w", err) + return fmt.Errorf("could not set the impersonator's credential signing secret: %w", err) } c.infoLog.Info("loading credential signing certificate for impersonation proxy", diff --git a/internal/controller/impersonatorconfig/impersonator_config_test.go b/internal/controller/impersonatorconfig/impersonator_config_test.go index 33bd17ff..af29711b 100644 --- a/internal/controller/impersonatorconfig/impersonator_config_test.go +++ b/internal/controller/impersonatorconfig/impersonator_config_test.go @@ -29,11 +29,14 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/clock" "k8s.io/apimachinery/pkg/util/intstr" kubeinformers "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" kubernetesfake "k8s.io/client-go/kubernetes/fake" coretesting "k8s.io/client-go/testing" + "k8s.io/utils/pointer" "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1" pinnipedfake "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake" @@ -266,6 +269,8 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { var subject controllerlib.Controller var kubeAPIClient *kubernetesfake.Clientset + var deleteOptions *[]metav1.DeleteOptions + var deleteOptionsRecorder kubernetes.Interface var pinnipedAPIClient *pinnipedfake.Clientset var pinnipedInformerClient *pinnipedfake.Clientset var pinnipedInformers pinnipedinformers.SharedInformerFactory @@ -275,6 +280,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { var cancelContextCancelFunc context.CancelFunc var syncContext *controllerlib.Context var frozenNow time.Time + var tlsServingCertDynamicCertProvider dynamiccert.Private var signingCertProvider dynamiccert.Provider var signingCACertPEM, signingCAKeyPEM []byte var signingCASecret *corev1.Secret @@ -418,6 +424,20 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { } } + var requireTLSSecretProviderHasLoadedCerts = func() { + actualCert, actualKey := tlsServingCertDynamicCertProvider.CurrentCertKeyContent() + r.NotEmpty(actualCert) + r.NotEmpty(actualKey) + _, err := tls.X509KeyPair(actualCert, actualKey) + r.NoError(err) + } + + var requireTLSSecretProviderIsEmpty = func() { + actualCert, actualKey := tlsServingCertDynamicCertProvider.CurrentCertKeyContent() + r.Nil(actualCert) + r.Nil(actualKey) + } + var requireTLSServerIsRunning = func(caCrt []byte, addr string, dnsOverrides map[string]string) { r.Greater(impersonatorFuncWasCalled, 0) @@ -469,6 +489,8 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { r.NoError(resp.Body.Close()) r.NoError(err) r.Equal(fakeServerResponseBody, string(body)) + + requireTLSSecretProviderHasLoadedCerts() } var requireTLSServerIsRunningWithoutCerts = func() { @@ -490,6 +512,8 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { }, 20*time.Second, 50*time.Millisecond) r.Error(err) r.Regexp(expectedErrorRegex, err.Error()) + + requireTLSSecretProviderIsEmpty() } var requireTLSServerIsNoLongerRunning = func() { @@ -508,10 +532,14 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { }, 20*time.Second, 50*time.Millisecond) r.Error(err) r.Regexp(expectedErrorRegex, err.Error()) + + requireTLSSecretProviderIsEmpty() } var requireTLSServerWasNeverStarted = func() { r.Equal(0, impersonatorFuncWasCalled) + + requireTLSSecretProviderIsEmpty() } // Defer starting the informers until the last possible moment so that the @@ -521,7 +549,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { subject = NewImpersonatorConfigController( installedInNamespace, credentialIssuerResourceName, - kubeAPIClient, + deleteOptionsRecorder, pinnipedAPIClient, pinnipedInformers.Config().V1alpha1().CredentialIssuers(), kubeInformers.Core().V1().Services(), @@ -538,6 +566,10 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { signingCertProvider, testLog, ) + controllerlib.TestWrap(t, subject, func(syncer controllerlib.Syncer) controllerlib.Syncer { + tlsServingCertDynamicCertProvider = syncer.(*impersonatorConfigController).tlsServingCertDynamicCertProvider + return syncer + }) // Set this at the last second to support calling subject.Name(). syncContext = &controllerlib.Context{ @@ -564,8 +596,10 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { var newSecretWithData = func(resourceName string, data map[string][]byte) *corev1.Secret { return &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: resourceName, - Namespace: installedInNamespace, + Name: resourceName, + Namespace: installedInNamespace, + UID: "uid-1234", // simulate KAS filling out UID and RV + ResourceVersion: "rv-5678", }, Data: data, } @@ -705,6 +739,14 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { var addObjectFromCreateActionToInformerAndWait = func(action coretesting.Action, informer controllerlib.InformerGetter) { createdObject, ok := action.(coretesting.CreateAction).GetObject().(kubeclient.Object) r.True(ok, "should have been able to cast this action's object to kubeclient.Object: %v", action) + + if secret, ok := createdObject.(*corev1.Secret); ok && len(secret.ResourceVersion) == 0 { + secret = secret.DeepCopy() + secret.UID = "uid-1234" // simulate KAS filling out UID and RV + secret.ResourceVersion = "rv-5678" + createdObject = secret + } + addObjectToKubeInformerAndWait(createdObject, informer) } @@ -986,6 +1028,18 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { r.Equal("delete", deleteAction.GetVerb()) r.Equal(tlsSecretName, deleteAction.GetName()) r.Equal("secrets", deleteAction.GetResource().Resource) + + // validate that we set delete preconditions correctly + r.NotEmpty(*deleteOptions) + for _, opt := range *deleteOptions { + uid := types.UID("uid-1234") + r.Equal(metav1.DeleteOptions{ + Preconditions: &metav1.Preconditions{ + UID: &uid, + ResourceVersion: pointer.String("rv-5678"), + }, + }, opt) + } } var requireCASecretWasCreated = func(action coretesting.Action) []byte { @@ -1064,6 +1118,8 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { kubeinformers.WithNamespace(installedInNamespace), ) kubeAPIClient = kubernetesfake.NewSimpleClientset() + deleteOptions = &[]metav1.DeleteOptions{} + deleteOptionsRecorder = testutil.NewDeleteOptionsRecorder(kubeAPIClient, deleteOptions) pinnipedAPIClient = pinnipedfake.NewSimpleClientset() frozenNow = time.Date(2021, time.March, 2, 7, 42, 0, 0, time.Local) signingCertProvider = dynamiccert.NewCA(name) @@ -1222,7 +1278,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1]) requireCASecretWasCreated(kubeAPIClient.Actions()[2]) requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) }) }) @@ -1241,7 +1297,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { requireNodesListed(kubeAPIClient.Actions()[0]) requireCASecretWasCreated(kubeAPIClient.Actions()[1]) requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) }) }) @@ -1260,7 +1316,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { requireNodesListed(kubeAPIClient.Actions()[0]) requireCASecretWasCreated(kubeAPIClient.Actions()[1]) requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) }) }) @@ -1451,10 +1507,10 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { errString := "could not find valid IP addresses or hostnames from load balancer some-namespace/some-service-resource-name" r.EqualError(runControllerSync(), errString) - r.Len(kubeAPIClient.Actions(), 1) // no new actions - requireTLSServerIsRunningWithoutCerts() + r.Len(kubeAPIClient.Actions(), 1) // no new actions + requireTLSServerIsRunning(caCrt, testServerAddr(), nil) // serving certificate is not unloaded in this case requireCredentialIssuer(newErrorStrategy(errString)) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) }) }) }) @@ -1510,7 +1566,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { requireCASecretWasCreated(kubeAPIClient.Actions()[2]) requireTLSServerIsRunningWithoutCerts() requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) }) it("returns an error when the impersonation TLS server fails to start", func() { @@ -1545,7 +1601,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { requireCASecretWasCreated(kubeAPIClient.Actions()[1]) requireTLSServerIsRunningWithoutCerts() requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) }) it("returns an error when the impersonation TLS server fails to start", func() { @@ -1685,7 +1741,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { requireCASecretWasCreated(kubeAPIClient.Actions()[2]) requireTLSServerIsRunningWithoutCerts() requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) }) }) @@ -1780,7 +1836,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { // Check that the server is running without certs. requireTLSServerIsRunningWithoutCerts() requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) }) }) @@ -2129,7 +2185,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { r.Len(kubeAPIClient.Actions(), 4) requireTLSSecretWasDeleted(kubeAPIClient.Actions()[3]) // tried to delete cert but failed requireCredentialIssuer(newErrorStrategy("error on tls secret delete")) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) }) }) }) @@ -2160,7 +2216,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1]) requireCASecretWasCreated(kubeAPIClient.Actions()[2]) requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) // load when enabled // Simulate the informer cache's background update from its watch. addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[1], kubeInformers.Core().V1().Services()) @@ -2178,7 +2234,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { r.Len(kubeAPIClient.Actions(), 4) requireServiceWasDeleted(kubeAPIClient.Actions()[3], loadBalancerServiceName) requireCredentialIssuer(newManuallyDisabledStrategy()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderIsEmpty() // only unload when disabled deleteServiceFromTracker(loadBalancerServiceName, kubeInformerClient) waitForObjectToBeDeletedFromInformer(loadBalancerServiceName, kubeInformers.Core().V1().Services()) @@ -2195,7 +2251,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { r.Len(kubeAPIClient.Actions(), 5) requireLoadBalancerWasCreated(kubeAPIClient.Actions()[4]) requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) // load again when enabled }) }) @@ -2226,7 +2282,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { requireClusterIPWasCreated(kubeAPIClient.Actions()[1]) requireCASecretWasCreated(kubeAPIClient.Actions()[2]) requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) // load when enabled // Simulate the informer cache's background update from its watch. addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[1], kubeInformers.Core().V1().Services()) @@ -2244,7 +2300,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { r.Len(kubeAPIClient.Actions(), 4) requireServiceWasDeleted(kubeAPIClient.Actions()[3], clusterIPServiceName) requireCredentialIssuer(newManuallyDisabledStrategy()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderIsEmpty() // only unload when disabled deleteServiceFromTracker(clusterIPServiceName, kubeInformerClient) waitForObjectToBeDeletedFromInformer(clusterIPServiceName, kubeInformers.Core().V1().Services()) @@ -2264,7 +2320,88 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { r.Len(kubeAPIClient.Actions(), 5) requireClusterIPWasCreated(kubeAPIClient.Actions()[4]) requireCredentialIssuer(newPendingStrategyWaitingForLB()) + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) // load again when enabled + }) + }) + + when("service type none with a hostname", func() { + const fakeHostname = "hello.com" + it.Before(func() { + addSecretToTrackers(signingCASecret, kubeInformerClient) + addCredentialIssuerToTrackers(v1alpha1.CredentialIssuer{ + ObjectMeta: metav1.ObjectMeta{Name: credentialIssuerResourceName}, + Spec: v1alpha1.CredentialIssuerSpec{ + ImpersonationProxy: &v1alpha1.ImpersonationProxySpec{ + Mode: v1alpha1.ImpersonationProxyModeEnabled, + ExternalEndpoint: fakeHostname, + Service: v1alpha1.ImpersonationProxyServiceSpec{ + Type: v1alpha1.ImpersonationProxyServiceTypeNone, + }, + }, + }, + }, pinnipedInformerClient, pinnipedAPIClient) + addNodeWithRoleToTracker("worker", kubeAPIClient) + }) + + it("starts the impersonator, then shuts it down, then starts it again", func() { + startInformersAndController() + + r.NoError(runControllerSync()) + r.Len(kubeAPIClient.Actions(), 3) + requireNodesListed(kubeAPIClient.Actions()[0]) + ca := requireCASecretWasCreated(kubeAPIClient.Actions()[1]) + requireTLSSecretWasCreated(kubeAPIClient.Actions()[2], ca) + requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()}) + requireCredentialIssuer(newSuccessStrategy(fakeHostname, ca)) + + // load when enabled + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) + requireTLSSecretProviderHasLoadedCerts() + + // Simulate the informer cache's background update from its watch. + addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[1], kubeInformers.Core().V1().Secrets()) + addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[2], kubeInformers.Core().V1().Secrets()) + + // Update the CredentialIssuer. + updateCredentialIssuerInInformerAndWait(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{ + ImpersonationProxy: &v1alpha1.ImpersonationProxySpec{ + Mode: v1alpha1.ImpersonationProxyModeDisabled, + }, + }, pinnipedInformers.Config().V1alpha1().CredentialIssuers()) + + r.NoError(runControllerSync()) + requireTLSServerIsNoLongerRunning() + r.Len(kubeAPIClient.Actions(), 4) + requireTLSSecretWasDeleted(kubeAPIClient.Actions()[3]) + requireCredentialIssuer(newManuallyDisabledStrategy()) + + // only unload when disabled requireSigningCertProviderIsEmpty() + requireTLSSecretProviderIsEmpty() + + deleteSecretFromTracker(tlsSecretName, kubeInformerClient) + waitForObjectToBeDeletedFromInformer(tlsSecretName, kubeInformers.Core().V1().Secrets()) + + // Update the CredentialIssuer again. + updateCredentialIssuerInInformerAndWait(credentialIssuerResourceName, v1alpha1.CredentialIssuerSpec{ + ImpersonationProxy: &v1alpha1.ImpersonationProxySpec{ + Mode: v1alpha1.ImpersonationProxyModeEnabled, + ExternalEndpoint: fakeHostname, + Service: v1alpha1.ImpersonationProxyServiceSpec{ + Type: v1alpha1.ImpersonationProxyServiceTypeNone, + }, + }, + }, pinnipedInformers.Config().V1alpha1().CredentialIssuers()) + + r.NoError(runControllerSync()) + requireTLSServerIsRunning(ca, fakeHostname, map[string]string{fakeHostname + httpsPort: testServerAddr()}) + r.Len(kubeAPIClient.Actions(), 5) + requireTLSSecretWasCreated(kubeAPIClient.Actions()[4], ca) + requireCredentialIssuer(newSuccessStrategy(fakeHostname, ca)) + + // load again when enabled + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) + requireTLSSecretProviderHasLoadedCerts() }) }) }) @@ -2315,9 +2452,9 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { r.Len(kubeAPIClient.Actions(), 5) requireLoadBalancerWasCreated(kubeAPIClient.Actions()[3]) requireTLSSecretWasDeleted(kubeAPIClient.Actions()[4]) // the Secret was deleted because it contained a cert with the wrong IP - requireTLSServerIsRunningWithoutCerts() + requireTLSServerIsRunning(ca, testServerAddr(), nil) // serving certificate is not unloaded in this case requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) // Simulate the informer cache's background update from its watch. addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[3], kubeInformers.Core().V1().Services()) @@ -2326,10 +2463,10 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { // The controller should be waiting for the load balancer's ingress to become available. r.NoError(runControllerSync()) - r.Len(kubeAPIClient.Actions(), 5) // no new actions while it is waiting for the load balancer's ingress - requireTLSServerIsRunningWithoutCerts() + r.Len(kubeAPIClient.Actions(), 5) // no new actions while it is waiting for the load balancer's ingress + requireTLSServerIsRunning(ca, testServerAddr(), nil) // serving certificate is not unloaded in this case requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) // Update the ingress of the LB in the informer's client and run Sync again. fakeIP := "127.0.0.123" @@ -2767,7 +2904,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { requireCASecretWasCreated(kubeAPIClient.Actions()[2]) requireTLSServerIsRunningWithoutCerts() requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) // Simulate the informer cache's background update from its watch. addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[1], kubeInformers.Core().V1().Services()) @@ -2777,7 +2914,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { r.Equal(1, impersonatorFuncWasCalled) // wasn't started a second time requireTLSServerIsRunningWithoutCerts() // still running requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) r.Len(kubeAPIClient.Actions(), 3) // no new API calls }) @@ -2790,7 +2927,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { ca := requireCASecretWasCreated(kubeAPIClient.Actions()[2]) requireTLSServerIsRunningWithoutCerts() requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) // Simulate the informer cache's background update from its watch. addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[1], kubeInformers.Core().V1().Services()) @@ -2827,7 +2964,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { ca := requireCASecretWasCreated(kubeAPIClient.Actions()[2]) requireTLSServerIsRunningWithoutCerts() requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) // Simulate the informer cache's background update from its watch. addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[1], kubeInformers.Core().V1().Services()) @@ -2918,6 +3055,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { when("the impersonator start function returned by the impersonatorFunc returns an error immediately", func() { it.Before(func() { + addSecretToTrackers(signingCASecret, kubeInformerClient) addNodeWithRoleToTracker("worker", kubeAPIClient) impersonatorFuncReturnedFuncError = errors.New("some immediate impersonator startup error") addCredentialIssuerToTrackers(v1alpha1.CredentialIssuer{ @@ -2948,7 +3086,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1]) requireCASecretWasCreated(kubeAPIClient.Actions()[2]) requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) // Simulate the informer cache's background update from its watch. addObjectFromCreateActionToInformerAndWait(kubeAPIClient.Actions()[1], kubeInformers.Core().V1().Services()) @@ -2966,7 +3104,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { // sync should be able to detect the error and return it. r.EqualError(runControllerSync(), "some immediate impersonator startup error") requireCredentialIssuer(newErrorStrategy("some immediate impersonator startup error")) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) // Next time the controller starts the server, the server will start successfully. impersonatorFuncReturnedFuncError = nil @@ -2976,12 +3114,13 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { r.NoError(runControllerSync()) requireTLSServerIsRunningWithoutCerts() requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) }) }) when("the impersonator server dies for no apparent reason after running for a while", func() { it.Before(func() { + addSecretToTrackers(signingCASecret, kubeInformerClient) addNodeWithRoleToTracker("worker", kubeAPIClient) addCredentialIssuerToTrackers(v1alpha1.CredentialIssuer{ ObjectMeta: metav1.ObjectMeta{Name: credentialIssuerResourceName}, @@ -3004,7 +3143,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { requireLoadBalancerWasCreated(kubeAPIClient.Actions()[1]) requireCASecretWasCreated(kubeAPIClient.Actions()[2]) requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) requireTLSServerIsRunningWithoutCerts() // Simulate the informer cache's background update from its watch. @@ -3026,7 +3165,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { // sync should be able to detect the error and return it. r.EqualError(runControllerSync(), "unexpected shutdown of proxy server") requireCredentialIssuer(newErrorStrategy("unexpected shutdown of proxy server")) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) // Next time the controller starts the server, the server should behave as normal. testHTTPServerInterruptCh = nil @@ -3036,7 +3175,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { r.NoError(runControllerSync()) requireTLSServerIsRunningWithoutCerts() requireCredentialIssuer(newPendingStrategyWaitingForLB()) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) }) }) @@ -3471,16 +3610,10 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { }, }, pinnipedInformerClient, pinnipedAPIClient) addNodeWithRoleToTracker("worker", kubeAPIClient) - tlsSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: tlsSecretName, - Namespace: installedInNamespace, - }, - Data: map[string][]byte{ - // "aGVsbG8gd29ybGQK" is "hello world" base64 encoded which is not a valid cert - corev1.TLSCertKey: []byte("-----BEGIN CERTIFICATE-----\naGVsbG8gd29ybGQK\n-----END CERTIFICATE-----\n"), - }, - } + tlsSecret := newSecretWithData(tlsSecretName, map[string][]byte{ + // "aGVsbG8gd29ybGQK" is "hello world" base64 encoded which is not a valid cert + corev1.TLSCertKey: []byte("-----BEGIN CERTIFICATE-----\naGVsbG8gd29ybGQK\n-----END CERTIFICATE-----\n"), + }) addSecretToTrackers(tlsSecret, kubeAPIClient, kubeInformerClient) }) @@ -3705,7 +3838,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { it("returns the error", func() { startInformersAndController() - errString := `could not load the impersonator's credential signing secret: TestImpersonatorConfigControllerSync: attempt to set invalid key pair: tls: failed to find any PEM data in certificate input` + errString := `could not set the impersonator's credential signing secret: TestImpersonatorConfigControllerSync: attempt to set invalid key pair: tls: failed to find any PEM data in certificate input` r.EqualError(runControllerSync(), errString) requireCredentialIssuer(newErrorStrategy(errString)) requireSigningCertProviderIsEmpty() @@ -3720,7 +3853,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { it("returns the error", func() { startInformersAndController() - errString := `could not load the impersonator's credential signing secret: TestImpersonatorConfigControllerSync: attempt to set invalid key pair: tls: failed to find any PEM data in certificate input` + errString := `could not set the impersonator's credential signing secret: TestImpersonatorConfigControllerSync: attempt to set invalid key pair: tls: failed to find any PEM data in certificate input` r.EqualError(runControllerSync(), errString) requireCredentialIssuer(newErrorStrategy(errString)) requireSigningCertProviderIsEmpty() @@ -3756,10 +3889,10 @@ func TestImpersonatorConfigControllerSync(t *testing.T) { addSecretToTrackers(updatedSigner, kubeInformerClient) waitForObjectToAppearInInformer(updatedSigner, kubeInformers.Core().V1().Secrets()) - errString := `could not load the impersonator's credential signing secret: TestImpersonatorConfigControllerSync: attempt to set invalid key pair: tls: failed to find any PEM data in certificate input` + errString := `could not set the impersonator's credential signing secret: TestImpersonatorConfigControllerSync: attempt to set invalid key pair: tls: failed to find any PEM data in certificate input` r.EqualError(runControllerSync(), errString) requireCredentialIssuer(newErrorStrategy(errString)) - requireSigningCertProviderIsEmpty() + requireSigningCertProviderHasLoadedCerts(signingCACertPEM, signingCAKeyPEM) }) }) }) diff --git a/internal/controller/kubecertagent/kubecertagent_test.go b/internal/controller/kubecertagent/kubecertagent_test.go index 119a5476..5d829002 100644 --- a/internal/controller/kubecertagent/kubecertagent_test.go +++ b/internal/controller/kubecertagent/kubecertagent_test.go @@ -961,7 +961,6 @@ func runControllerUntilQuiet(ctx context.Context, t *testing.T, controller contr errorStream := make(chan error) controllerlib.TestWrap(t, controller, func(syncer controllerlib.Syncer) controllerlib.Syncer { - controller.Name() return controllerlib.SyncFunc(func(ctx controllerlib.Context) error { err := syncer.Sync(ctx) errorStream <- err diff --git a/internal/testutil/delete.go b/internal/testutil/delete.go new file mode 100644 index 00000000..7a6a9fe5 --- /dev/null +++ b/internal/testutil/delete.go @@ -0,0 +1,47 @@ +// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package testutil + +import ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" +) + +func NewDeleteOptionsRecorder(client kubernetes.Interface, opts *[]metav1.DeleteOptions) kubernetes.Interface { + return &clientWrapper{ + Interface: client, + opts: opts, + } +} + +type clientWrapper struct { + kubernetes.Interface + opts *[]metav1.DeleteOptions +} + +func (c *clientWrapper) CoreV1() corev1client.CoreV1Interface { + return &coreWrapper{CoreV1Interface: c.Interface.CoreV1(), opts: c.opts} +} + +type coreWrapper struct { + corev1client.CoreV1Interface + opts *[]metav1.DeleteOptions +} + +func (c *coreWrapper) Secrets(namespace string) corev1client.SecretInterface { + return &secretsWrapper{SecretInterface: c.CoreV1Interface.Secrets(namespace), opts: c.opts} +} + +type secretsWrapper struct { + corev1client.SecretInterface + opts *[]metav1.DeleteOptions +} + +func (s *secretsWrapper) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { + *s.opts = append(*s.opts, opts) + return s.SecretInterface.Delete(ctx, name, opts) +} diff --git a/test/integration/concierge_impersonation_proxy_test.go b/test/integration/concierge_impersonation_proxy_test.go index 21e90f69..a4a514c6 100644 --- a/test/integration/concierge_impersonation_proxy_test.go +++ b/test/integration/concierge_impersonation_proxy_test.go @@ -197,7 +197,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl // We do this to ensure that future tests that use the impersonation proxy (e.g., // TestE2EFullIntegration) will start with a known-good state. if clusterSupportsLoadBalancers { - performImpersonatorDiscovery(ctx, t, env, adminConciergeClient) + performImpersonatorDiscovery(ctx, t, env, adminClient, adminConciergeClient, refreshCredential) } }) @@ -277,7 +277,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl // to discover the impersonator's URL and CA certificate. Until it has finished starting, it may not be included // in the strategies array or it may be included in an error state. It can be in an error state for // awhile when it is waiting for the load balancer to be assigned an ip/hostname. - impersonationProxyURL, impersonationProxyCACertPEM := performImpersonatorDiscovery(ctx, t, env, adminConciergeClient) + impersonationProxyURL, impersonationProxyCACertPEM := performImpersonatorDiscovery(ctx, t, env, adminClient, adminConciergeClient, refreshCredential) if !clusterSupportsLoadBalancers { // In this case, we specified the endpoint in the configmap, so check that it was reported correctly in the CredentialIssuer. require.Equal(t, "https://"+proxyServiceEndpoint, impersonationProxyURL) @@ -1422,7 +1422,7 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl require.Equal(t, &corev1.Pod{}, pod) }) - // - request to whoami (pinniped resource endpoing) + // - request to whoami (pinniped resource endpoint) // - through the impersonation proxy // - should succeed 200 // - should respond "you are system:anonymous" @@ -1733,10 +1733,10 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl // wait until the credential issuer is updated with the new url testlib.RequireEventuallyWithoutError(t, func() (bool, error) { - newImpersonationProxyURL, _ := performImpersonatorDiscovery(ctx, t, env, adminConciergeClient) + newImpersonationProxyURL, _ := performImpersonatorDiscoveryURL(ctx, t, env, adminConciergeClient) return newImpersonationProxyURL == "https://"+clusterIPServiceURL, nil }, 30*time.Second, 500*time.Millisecond) - newImpersonationProxyURL, newImpersonationProxyCACertPEM := performImpersonatorDiscovery(ctx, t, env, adminConciergeClient) + newImpersonationProxyURL, newImpersonationProxyCACertPEM := performImpersonatorDiscovery(ctx, t, env, adminClient, adminConciergeClient, refreshCredential) anonymousClient := newAnonymousImpersonationProxyClientWithProxy(t, newImpersonationProxyURL, newImpersonationProxyCACertPEM, nil).PinnipedConcierge refreshedCredentials := refreshCredentialHelper(t, anonymousClient) @@ -1941,7 +1941,64 @@ func expectedWhoAmIRequestResponse(username string, groups []string, extra map[s } } -func performImpersonatorDiscovery(ctx context.Context, t *testing.T, env *testlib.TestEnv, adminConciergeClient pinnipedconciergeclientset.Interface) (string, []byte) { +func performImpersonatorDiscovery(ctx context.Context, t *testing.T, env *testlib.TestEnv, + adminClient kubernetes.Interface, adminConciergeClient pinnipedconciergeclientset.Interface, + refreshCredential func(t *testing.T, impersonationProxyURL string, impersonationProxyCACertPEM []byte) *loginv1alpha1.ClusterCredential) (string, []byte) { + t.Helper() + + impersonationProxyURL, impersonationProxyCACertPEM := performImpersonatorDiscoveryURL(ctx, t, env, adminConciergeClient) + + if len(env.Proxy) == 0 { + t.Log("no test proxy is available, skipping readiness checks for concierge impersonation proxy pods") + return impersonationProxyURL, impersonationProxyCACertPEM + } + + impersonationProxyParsedURL, err := url.Parse(impersonationProxyURL) + require.NoError(t, err) + + expectedGroups := make([]string, 0, len(env.TestUser.ExpectedGroups)+1) // make sure we do not mutate env.TestUser.ExpectedGroups + expectedGroups = append(expectedGroups, env.TestUser.ExpectedGroups...) + expectedGroups = append(expectedGroups, "system:authenticated") + + // probe each pod directly for readiness since the concierge status is a lie - it just means a single pod is ready + testlib.RequireEventually(t, func(requireEventually *require.Assertions) { + pods, err := adminClient.CoreV1().Pods(env.ConciergeNamespace).List(ctx, + metav1.ListOptions{LabelSelector: "app=" + env.ConciergeAppName + ",!kube-cert-agent.pinniped.dev"}) // TODO replace with deployment.pinniped.dev=concierge + requireEventually.NoError(err) + requireEventually.Len(pods.Items, 2) // has to stay in sync with the defaults in our YAML + + for _, pod := range pods.Items { + t.Logf("checking if concierge impersonation proxy pod %q is ready", pod.Name) + + requireEventually.NotEmptyf(pod.Status.PodIP, "pod %q does not have an IP", pod.Name) + + credentials := refreshCredential(t, impersonationProxyURL, impersonationProxyCACertPEM).DeepCopy() + credentials.Token = "not a valid token" // demonstrates that client certs take precedence over tokens by setting both on the requests + + config := newImpersonationProxyConfigWithCredentials(t, credentials, impersonationProxyURL, impersonationProxyCACertPEM, nil) + config = rest.CopyConfig(config) + config.Proxy = kubeconfigProxyFunc(t, env.Proxy) // always use the proxy since we are talking directly to a pod IP + config.Host = "https://" + pod.Status.PodIP + ":8444" // hardcode the internal port - it should not change + config.TLSClientConfig.ServerName = impersonationProxyParsedURL.Hostname() // make SNI hostname TLS verification work even when using IP + + whoAmI, err := testlib.NewKubeclient(t, config).PinnipedConcierge.IdentityV1alpha1().WhoAmIRequests(). + Create(ctx, &identityv1alpha1.WhoAmIRequest{}, metav1.CreateOptions{}) + requireEventually.NoError(err) + requireEventually.Equal( + expectedWhoAmIRequestResponse( + env.TestUser.ExpectedUsername, + expectedGroups, + nil, + ), + whoAmI, + ) + } + }, 10*time.Minute, 10*time.Second) + + return impersonationProxyURL, impersonationProxyCACertPEM +} + +func performImpersonatorDiscoveryURL(ctx context.Context, t *testing.T, env *testlib.TestEnv, adminConciergeClient pinnipedconciergeclientset.Interface) (string, []byte) { t.Helper() var impersonationProxyURL string From e05a46b7f5e50dd0964d8fbae51c176d05cc6824 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Aug 2021 20:46:25 +0000 Subject: [PATCH 31/34] Bump github.com/go-ldap/ldap/v3 from 3.3.0 to 3.4.1 Bumps [github.com/go-ldap/ldap/v3](https://github.com/go-ldap/ldap) from 3.3.0 to 3.4.1. - [Release notes](https://github.com/go-ldap/ldap/releases) - [Commits](https://github.com/go-ldap/ldap/compare/v3.3.0...v3.4.1) --- updated-dependencies: - dependency-name: github.com/go-ldap/ldap/v3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5578b7f6..b5f20e9d 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/coreos/go-oidc/v3 v3.0.0 github.com/creack/pty v1.1.14 github.com/davecgh/go-spew v1.1.1 - github.com/go-ldap/ldap/v3 v3.3.0 + github.com/go-ldap/ldap/v3 v3.4.1 github.com/go-logr/logr v0.4.0 github.com/go-logr/stdr v0.4.0 github.com/gofrs/flock v0.8.1 diff --git a/go.sum b/go.sum index 620de4c7..fe9d15bc 100644 --- a/go.sum +++ b/go.sum @@ -244,8 +244,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ= -github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= +github.com/go-ldap/ldap/v3 v3.4.1 h1:fU/0xli6HY02ocbMuozHAYsaHLcnkLjvho2r5a34BUU= +github.com/go-ldap/ldap/v3 v3.4.1/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= From 66ddcf98d3693d5dbd854582c662a8c9d2ee809d Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Tue, 17 Aug 2021 09:44:40 -0400 Subject: [PATCH 32/34] Provide good defaults for NO_PROXY This change updates the default NO_PROXY for the supervisor to not proxy requests to the Kubernetes API and other Kubernetes endpoints such as Kubernetes services. It also adds https_proxy and no_proxy settings for the concierge with the same default. Signed-off-by: Monis Khan --- deploy/concierge/deployment.yaml | 9 +++++++++ deploy/concierge/values.yaml | 8 ++++++++ deploy/supervisor/deployment.yaml | 2 +- deploy/supervisor/values.yaml | 2 +- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/deploy/concierge/deployment.yaml b/deploy/concierge/deployment.yaml index a516cfbf..74b4ca5b 100644 --- a/deploy/concierge/deployment.yaml +++ b/deploy/concierge/deployment.yaml @@ -152,6 +152,15 @@ spec: mountPath: /etc/podinfo - name: impersonation-proxy mountPath: /var/run/secrets/impersonation-proxy.concierge.pinniped.dev/serviceaccount + env: + #@ if data.values.https_proxy: + - name: HTTPS_PROXY + value: #@ data.values.https_proxy + #@ end + #@ if data.values.https_proxy and data.values.no_proxy: + - name: NO_PROXY + value: #@ data.values.no_proxy + #@ end livenessProbe: httpGet: path: /healthz diff --git a/deploy/concierge/values.yaml b/deploy/concierge/values.yaml index f72ea4f9..a0e0750c 100644 --- a/deploy/concierge/values.yaml +++ b/deploy/concierge/values.yaml @@ -93,3 +93,11 @@ impersonation_proxy_spec: {service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "4000"} #! When mode LoadBalancer is set, this will set the LoadBalancer Service's Spec.LoadBalancerIP. load_balancer_ip: + +#! Set the standard golang HTTPS_PROXY and NO_PROXY environment variables on the Concierge containers. +#! These will be used when the Concierge makes backend-to-backend calls to authenticators using HTTPS, +#! e.g. when the Concierge fetches discovery documents, JWKS keys, and POSTs to token webhooks. +#! The Concierge never makes insecure HTTP calls, so there is no reason to set HTTP_PROXY. +#! Optional. +https_proxy: #! e.g. http://proxy.example.com +no_proxy: "$(KUBERNETES_SERVICE_HOST),169.254.169.254,127.0.0.1,localhost,.svc,.cluster.local" #! do not proxy Kubernetes endpoints diff --git a/deploy/supervisor/deployment.yaml b/deploy/supervisor/deployment.yaml index 8778e2b6..8ec9298b 100644 --- a/deploy/supervisor/deployment.yaml +++ b/deploy/supervisor/deployment.yaml @@ -107,7 +107,7 @@ spec: - name: HTTPS_PROXY value: #@ data.values.https_proxy #@ end - #@ if data.values.no_proxy: + #@ if data.values.https_proxy and data.values.no_proxy: - name: NO_PROXY value: #@ data.values.no_proxy #@ end diff --git a/deploy/supervisor/values.yaml b/deploy/supervisor/values.yaml index 041d8b70..ea3fa2a4 100644 --- a/deploy/supervisor/values.yaml +++ b/deploy/supervisor/values.yaml @@ -72,4 +72,4 @@ api_group_suffix: pinniped.dev #! The Supervisor never makes insecure HTTP calls, so there is no reason to set HTTP_PROXY. #! Optional. https_proxy: #! e.g. http://proxy.example.com -no_proxy: #! e.g. 127.0.0.1 +no_proxy: "$(KUBERNETES_SERVICE_HOST),169.254.169.254,127.0.0.1,localhost,.svc,.cluster.local" #! do not proxy Kubernetes endpoints From cf25c308cd841d398c3046bf46242f575cbe7476 Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Mon, 16 Aug 2021 20:47:08 -0400 Subject: [PATCH 33/34] test/integration: ignore restarts associated with test pods Signed-off-by: Monis Khan --- .../concierge_impersonation_proxy_test.go | 16 ++++++---------- test/integration/whoami_test.go | 16 ++++++---------- test/testlib/env.go | 4 ++-- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/test/integration/concierge_impersonation_proxy_test.go b/test/integration/concierge_impersonation_proxy_test.go index a4a514c6..7397f4d0 100644 --- a/test/integration/concierge_impersonation_proxy_test.go +++ b/test/integration/concierge_impersonation_proxy_test.go @@ -944,21 +944,17 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl return // stop test early since the token request API is not enabled on this cluster - other errors are caught below } - pod, err := kubeClient.Pods(namespaceName).Create(ctx, &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-impersonation-proxy-", - }, - Spec: corev1.PodSpec{ + pod := testlib.CreatePod(ctx, t, "impersonation-proxy", namespaceName, + corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "ignored-but-required", - Image: "does-not-matter", + Name: "ignored-but-required", + Image: "busybox", + Command: []string{"sh", "-c", "sleep 3600"}, }, }, ServiceAccountName: saName, - }, - }, metav1.CreateOptions{}) - require.NoError(t, err) + }) tokenRequestBadAudience, err := kubeClient.ServiceAccounts(namespaceName).CreateToken(ctx, saName, &authenticationv1.TokenRequest{ Spec: authenticationv1.TokenRequestSpec{ diff --git a/test/integration/whoami_test.go b/test/integration/whoami_test.go index ffb17db6..fe708613 100644 --- a/test/integration/whoami_test.go +++ b/test/integration/whoami_test.go @@ -164,21 +164,17 @@ func TestWhoAmI_ServiceAccount_TokenRequest(t *testing.T) { return // stop test early since the token request API is not enabled on this cluster - other errors are caught below } - pod, err := kubeClient.Pods(ns.Name).Create(ctx, &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-whoami-", - }, - Spec: corev1.PodSpec{ + pod := testlib.CreatePod(ctx, t, "whoami", ns.Name, + corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "ignored-but-required", - Image: "does-not-matter", + Name: "ignored-but-required", + Image: "busybox", + Command: []string{"sh", "-c", "sleep 3600"}, }, }, ServiceAccountName: sa.Name, - }, - }, metav1.CreateOptions{}) - require.NoError(t, err) + }) tokenRequestBadAudience, err := kubeClient.ServiceAccounts(ns.Name).CreateToken(ctx, sa.Name, &authenticationv1.TokenRequest{ Spec: authenticationv1.TokenRequestSpec{ diff --git a/test/testlib/env.go b/test/testlib/env.go index ea6834b6..523e2b69 100644 --- a/test/testlib/env.go +++ b/test/testlib/env.go @@ -142,8 +142,8 @@ func IntegrationEnv(t *testing.T) *TestEnv { memoizedTestEnvsByTest.Store(t, &result) // In every integration test, assert that no pods in our namespaces restart during the test. - assertNoRestartsDuringTest(t, result.ConciergeNamespace, "") - assertNoRestartsDuringTest(t, result.SupervisorNamespace, "") + assertNoRestartsDuringTest(t, result.ConciergeNamespace, "!pinniped.dev/test") + assertNoRestartsDuringTest(t, result.SupervisorNamespace, "!pinniped.dev/test") return &result } From e0901f4fe5daf511527fa9e345b0b1893fcdcedf Mon Sep 17 00:00:00 2001 From: Monis Khan Date: Mon, 16 Aug 2021 21:30:02 -0400 Subject: [PATCH 34/34] dynamiccert: prevent misuse of NewServingCert The Kube API server code that we use will cast inputs in an attempt to see if they implement optional interfaces. This change adds a simple wrapper struct to prevent such casts from causing us any issues. Signed-off-by: Monis Khan --- internal/dynamiccert/provider.go | 6 +++++- internal/dynamiccert/provider_test.go | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/internal/dynamiccert/provider.go b/internal/dynamiccert/provider.go index 4e74d118..d5c76847 100644 --- a/internal/dynamiccert/provider.go +++ b/internal/dynamiccert/provider.go @@ -55,7 +55,11 @@ type provider struct { // NewServingCert returns a Private that is go routine safe. // It can only hold key pairs that have IsCA=false. func NewServingCert(name string) Private { - return &provider{name: name} + return struct { + Private + }{ + Private: &provider{name: name}, + } } // NewCA returns a Provider that is go routine safe. diff --git a/internal/dynamiccert/provider_test.go b/internal/dynamiccert/provider_test.go index b2d1d168..fce2cd39 100644 --- a/internal/dynamiccert/provider_test.go +++ b/internal/dynamiccert/provider_test.go @@ -12,6 +12,7 @@ import ( "time" "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apiserver/pkg/server/dynamiccertificates" @@ -224,3 +225,19 @@ func poolSubjects(pool *x509.CertPool) [][]byte { } return pool.Subjects() } + +func TestNewServingCert(t *testing.T) { + got := NewServingCert("") + + ok1 := assert.Implements(fakeT{}, (*Private)(nil), got) + ok2 := assert.Implements(fakeT{}, (*Public)(nil), got) + ok3 := assert.Implements(fakeT{}, (*Provider)(nil), got) + + require.True(t, ok1, "NewServingCert must implement Private") + require.False(t, ok2, "NewServingCert must not implement Public") + require.False(t, ok3, "NewServingCert must not implement Provider") +} + +type fakeT struct{} + +func (fakeT) Errorf(string, ...interface{}) {}