Compare commits

..

377 Commits

Author SHA1 Message Date
Matt Moyer
55bc3dee7f Merge pull request #543 from mattmoyer/fix-head-version-string-validation
Fix missing "v".
2021-03-31 14:54:26 -07:00
Ryan Richard
fdbeb213fb Merge pull request #540 from vmware-tanzu/prepare-supervisor-on-kind.sh
Add hack/prepare-supervisor-on-kind.sh
2021-03-31 13:47:32 -07:00
Ryan Richard
1817d6c751 Merge branch 'main' into prepare-supervisor-on-kind.sh 2021-03-31 13:47:13 -07:00
Matt Moyer
476cc98e5a Fix missing "v".
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-31 15:41:44 -05:00
Matt Moyer
4cbf4959f2 Merge pull request #542 from mattmoyer/fix-head-version-string-validation
Use "0.0.0" as our fake version instead of "?.?.?" to avoid a panic.
2021-03-31 13:36:22 -07:00
Matt Moyer
e4e4e686f6 Use "0.0.0" as our fake version instead of "?.?.?" to avoid a panic.
These values need to pass the validation in k8s.io/component-base/metrics: https://github.com/kubernetes/component-base/blob/v0.20.5/metrics/version_parser.go#L28-L50

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-31 15:03:40 -05:00
Ryan Richard
d5be37673a Merge branch 'main' into prepare-supervisor-on-kind.sh 2021-03-31 11:42:06 -07:00
Ryan Richard
7bb5657c4d Add hack/prepare-supervisor-on-kind.sh
A demo of running the Supervisor and Concierge on
a kind cluster. Can be used to quickly set up an
environment for manual testing.

Also added some missing copyright headers to other
hack scripts.
2021-03-31 11:39:10 -07:00
Matt Moyer
fe9f12a29c Merge pull request #539 from mattmoyer/upgrade-kubernetes-deps
Upgrade to prereleased Kubernetes v1.20.5++ dependencies.
2021-03-31 11:21:54 -07:00
Matt Moyer
bea75bb7ac Upgrade to prereleased Kubernetes v1.20.5++ dependencies.
These commits include security fixes (CVE-2021-3121) for code generated by github.com/gogo/protobuf.
We expect this fix to also land in v1.20.6, but we don't want to wait for it.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-31 12:53:41 -05:00
Matt Moyer
081de8da62 Merge pull request #538 from vmware-tanzu/dependabot/docker/debian-10.9-slim
Bump debian from 10.8-slim to 10.9-slim
2021-03-31 06:00:27 -07:00
dependabot[bot]
469f864de3 Bump debian from 10.8-slim to 10.9-slim
Bumps debian from 10.8-slim to 10.9-slim.

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-31 05:41:15 +00:00
Margo Crawford
dc510792c4 Merge pull request #536 from vmware-tanzu/secret-deletion-not-found-flake
Do not error when trying to delete the TLS secret and you get a not found
2021-03-30 15:46:32 -07:00
Margo Crawford
8b6fe0ac70 Fix lint error 2021-03-30 14:53:26 -07:00
Margo Crawford
d47603472d Do not error when trying to delete the TLS secret and you get a not found 2021-03-30 14:44:06 -07:00
Matt Moyer
d4baeff94e Merge pull request #534 from mattmoyer/deflake-categories-test-rate-limiting
Deflake TestGetPinnipedCategory.
2021-03-30 13:46:55 -07:00
Matt Moyer
210114dbe1 Merge pull request #535 from mattmoyer/deflake-impersonation-proxy-test-dns
Deflake TestImpersonationProxy (especially on EKS).
2021-03-30 12:31:44 -07:00
Matt Moyer
4ebd0f5f12 Deflake TestImpersonationProxy (especially on EKS).
This test could flake if the load balancer hostname was provisioned but is not yet resolving in DNS from the test process.

The fix is to retry this step for up to 5 minutes.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-30 13:48:53 -05:00
Matt Moyer
f02b39b80f Deflake TestGetPinnipedCategory.
This test could fail when the cluster was under heavy load. This could cause kubectl to emit "Throttling request took [...]" logs that triggered a failure in the test.

The fix is to ignore these innocuous warnings.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-30 13:38:33 -05:00
Margo Crawford
608be8332e Merge pull request #533 from vmware-tanzu/eks-load-balancer-annotation
Add annotation to make the idle timeout be over 1 hour rather than 1 minute
2021-03-30 11:12:54 -07:00
Margo Crawford
3742719427 Add annotation to make the idle timeout be over 1 hour rather than 1 minute
- Note that 4000 seconds is the maximum value that AWS allows.
2021-03-30 09:12:34 -07:00
Andrew Keesler
f00a02dcca Merge pull request #529 from microwavables/main
updated adopters.md instructions and included logos from VMware Tanzu…
2021-03-29 16:16:02 -04:00
Nanci Lancaster
017c891fb8 Merge branch 'main' into main 2021-03-29 12:29:25 -05:00
Margo Crawford
d8baa43903 Add new non-idle timeout integration test for impersonation proxy
Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-03-29 09:30:51 -07:00
Nanci Lancaster
5c741bc423 Merge pull request #1 from microwavables/Adding-in-TKG-and-TMC-to-Adopters
Updated adopters.md file to include TMC and TKG
2021-03-29 09:49:54 -05:00
Nanci Lancaster
e99e175ce2 Updated adopters.md file to include TMC and TKG
Based on Andrew Keesler's feedback, I added in TMC and TKG adopter components to the file.
2021-03-29 09:48:34 -05:00
Matt Moyer
003e3e3c4d Merge pull request #532 from vmware-tanzu/group-link
Update Google Group Link
2021-03-29 07:36:54 -07:00
Matt Moyer
391202c253 Merge pull request #517 from mattmoyer/deflake-supervisor-oidc-discovery-test
Tweak some assertions in TestSupervisorOIDCDiscovery.
2021-03-29 07:35:58 -07:00
Ryan Richard
95bb4c4be5 Fix concierge_impersonation_proxy_test.go on AKS
Also send the correct instance of `t` into a helper function which
makes assertions.
2021-03-26 19:32:46 -07:00
Mo Khan
32422f18f1 Update Google Group Link
Remove the user account prefix.
2021-03-26 22:11:16 -04:00
Mo Khan
d52f500b83 Merge pull request #531 from mattmoyer/remove-test-dumplogs-helper
Remove library.DumpLogs test helper.
2021-03-26 18:58:07 -04:00
Matt Moyer
defad3cdd7 Remove library.DumpLogs test helper.
We had this code that printed out pod logs when certain tests failed, but it is a bit cumbersome. We're removing it because we added a CI task that exports all pod logs after every CI run, which accomplishes the same thing and provides us a bunch more data.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-26 16:43:02 -05:00
Matt Moyer
c6d7724b67 In TestImpersonationProxy, instead of failing in this case just skip the test.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-26 16:28:33 -05:00
Ryan Richard
3359311228 concierge_impersonation_proxy_test.go: fix typo in previous commit 2021-03-26 09:49:49 -07:00
Ryan Richard
7e16619146 concierge_impersonation_proxy_test.go: handle TKGS test clusters
Handle any test cluster which supports load balancers but should
not automatically start the impersonator, e.g. TKGS clusters.
2021-03-26 09:28:42 -07:00
Ryan Richard
a084544f08 Add hasExternalLoadBalancerProvider to AKS/EKS capabilities files 2021-03-26 08:03:51 -07:00
Mo Khan
c2588cf035 Merge pull request #528 from enj/enj/i/impersonation-proxy-authz-user-extra
impersonation proxy: add RBAC to impersonate user extra and SAs
2021-03-26 00:37:24 -04:00
Monis Khan
2179c2879a impersonation proxy: add RBAC to impersonate user extra and SAs
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-25 22:21:57 -04:00
Margo Crawford
b6e217e13a Hardcode type "webhook" in concierge_impersonation_proxy_test.go
Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-03-25 17:19:47 -07:00
Margo Crawford
6f2882b831 Explicitly set the correct authenticator for impersonator test
Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-03-25 16:57:37 -07:00
Margo Crawford
cd6e48bfa8 Use a random password for the dex integration test user
Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-03-25 15:12:17 -07:00
Nanci Lancaster
818535f30d updated adopters.md instructions and included logos from VMware Tanzu and Kubeapps
Signed-off-by: Nanci Lancaster <nancil@vmware.com>
2021-03-25 16:17:41 -05:00
Margo Crawford
c0361645e2 Merge pull request #355 from vmware-tanzu/impersonation-proxy
Impersonation proxy
2021-03-25 13:19:18 -07:00
Margo Crawford
6bf8bfe9a8 Merge remote-tracking branch 'origin/main' into impersonation-proxy 2021-03-24 17:22:40 -07:00
Matt Moyer
ea130ea781 Merge pull request #525 from vmware-tanzu/microwavables-patch-1
Added kubeapps and vmware tanzu logos
2021-03-24 16:28:36 -07:00
Nanci Lancaster
03619fc878 Added kubeapps and vmware tanzu logos
these logos will be used for the adopters.md file
2021-03-24 18:03:57 -05:00
Matt Moyer
454348b2fd Merge pull request #524 from mattmoyer/allow-prebuilt-cli-binaries-for-testing
Allow running CLI-related integration tests with pre-built binary.
2021-03-23 16:19:50 -07:00
Matt Moyer
cda8bd6e26 Allow running CLI-related integration tests with pre-built binary.
This allows setting `$PINNIPED_TEST_CLI` to point at an existing `pinniped` CLI binary instead of having the test build one on-the-fly. This is more efficient when you're running the tests across many clusters as we do in CI.

Building the CLI from scratch in our CI environment takes 1.5-2 minutes, so this change should save nearly that much time on every test job.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-23 17:19:09 -05:00
Matt Moyer
c0d32f10b2 Add some test debug logging when running the CLI.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-23 12:07:34 -05:00
Matt Moyer
ce5b05f912 Add some debug logging to measure how long the CLI build takes.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-23 12:06:35 -05:00
Matt Moyer
176fb6a139 Authenticators are no longer namespaced, so clean up these test logs.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-23 10:33:05 -05:00
Matt Moyer
9501168265 Simplify TestCLIGetKubeconfigStaticToken now that there's only a single table case.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-23 10:26:04 -05:00
Matt Moyer
2e79664f3d Merge branch 'main' of github.com:vmware-tanzu/pinniped into impersonation-proxy 2021-03-23 09:05:13 -05:00
Andrew Keesler
e70788204b Merge pull request #516 from ankeesler/cli-docs
Add CLI command for generating docs
2021-03-23 09:58:47 -04:00
Andrew Keesler
f6646eb2b7 cmd/pinniped: add generate-markdown-help for generating CLI doc
This command is hidden. We want to use this to generate our CLI reference docs
upon release.

Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-23 09:35:58 -04:00
Ryan Richard
75cfda0ffe prepare-for-integration-tests.sh: Check Chrome and chromedriver versions
They usually need to match, or at least be close, so added some
code to help us remember to do that.
2021-03-22 16:54:22 -07:00
Andrew Keesler
bde54ef643 Merge remote-tracking branch 'main' into impersonation-proxy 2021-03-22 17:00:40 -04:00
Margo Crawford
d90398815b Nothing in parallel in the impersonation proxy integration test 2021-03-22 10:48:09 -07:00
Margo Crawford
7683a98792 Unparallelize run all the verbs and port-forward tests 2021-03-22 09:45:51 -07:00
Margo Crawford
d7e9568137 Unparallelize a couple 2021-03-22 09:43:40 -07:00
Ryan Richard
904086cbec fix a typo in some comments 2021-03-22 09:34:58 -07:00
Matt Moyer
5e95c25d4f Tweak some assertions in TestSupervisorOIDCDiscovery.
We've seen some test flakes caused by this test. Some small changes:

- Use a 30s timeout for each iteration of the test loop (so each iteration needs to check or fail more quickly).
- Log a bit more during the checks so we can diagnose what's going on.
- Increase the overall timeout from one minute to five minutes

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-22 11:33:02 -05:00
Ryan Richard
c9b1982767 Merge branch 'main' into impersonation-proxy 2021-03-22 09:27:18 -07:00
Matt Moyer
f69d095a69 Merge pull request #515 from mattmoyer/bump-kube-deps-1.20.5
Upgrade Kubernetes runtime libraries to v1.20.5.
2021-03-22 08:30:53 -07:00
Matt Moyer
1e7f2c7735 Upgrade Kubernetes runtime libraries to v0.20.5.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-22 09:33:29 -05:00
Andrew Keesler
9af75d23fb Merge pull request #514 from enj/enj/i/whoami_ctx
pinniped whoami: print correct cluster info when --kubeconfig-context is used
2021-03-22 09:22:45 -04:00
Margo Crawford
d0df2009ac Merge pull request #498 from vmware-tanzu/impersonation-proxy-docs
Impersonation proxy docs
2021-03-19 16:13:58 -07:00
Monis Khan
964d4889c4 pinniped whoami: print correct cluster info when --kubeconfig-context is used
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-19 18:42:40 -04:00
Margo Crawford
a537287601 Regenerate cli.md based on output of help message 2021-03-19 14:34:35 -07:00
Margo Crawford
fdfc854f8c Incorporating suggestions:
- a credential that is understood by -> a credential that can be used to
  authenticate to
- This is more neutral to whether its going directly to k8s
  or through the impersonation proxy
2021-03-19 14:06:20 -07:00
Margo Crawford
331fef8fae Tweaked some wording, updated the cli page 2021-03-19 14:06:20 -07:00
Margo Crawford
4470d3d2d1 Fix broken links to architecture page 2021-03-19 14:06:20 -07:00
Margo Crawford
698bffc2ad Naming changes 2021-03-19 14:06:20 -07:00
Margo Crawford
6ff3e42602 Add description of impersonation proxy strategy to docs 2021-03-19 14:06:20 -07:00
Ryan Richard
3e50b4e129 Add -sS to the curl command in concierge_impersonation_proxy_test.go 2021-03-19 13:23:28 -07:00
Ryan Richard
d856221f56 Edit some comments in concierge_impersonation_proxy_test.go 2021-03-19 13:19:17 -07:00
Monis Khan
f519f0cb09 impersonator: disallow clients from setting the X-Forwarded-For header
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-19 15:35:06 -04:00
Monis Khan
c03fe2d1fe Use http2 for all non-upgrade requests
Instead of using the LongRunningFunc to determine if we can safely
use http2, follow the same logic as the aggregation proxy and only
use http2 when the request is not an upgrade.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-19 13:45:58 -04:00
Andrew Keesler
2749044625 test/integration: unparallelize impersonation kubectl test
Maybe this will cut down on flakes we see in CI?

Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-19 13:31:28 -04:00
Andrew Keesler
f73c70d8f9 test/integration: use Ryan's 20x rule to harden simple access tests
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-19 13:18:10 -04:00
Andrew Keesler
ebd5e45fa6 test/integration: wait for convergence at end of impersonation test
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-19 12:54:37 -04:00
Andrew Keesler
6154883855 test/integration: add temporary debug 'kubectl attach' logging
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-19 10:42:11 -04:00
Andrew Keesler
ebe01a5aef test/integration: catch early 'kubectl attach' return
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-19 09:59:24 -04:00
Andrew Keesler
28d00ce67b Merge remote-tracking branch 'upstream/main' into impersonation-proxy 2021-03-18 20:13:49 -04:00
Mo Khan
50e4531215 Merge pull request #505 from enj/enj/i/jwt-go_cve
Move to github.com/form3tech-oss/jwt-go
2021-03-18 19:34:19 -04:00
Andrew Keesler
1a9922d050 test/integration: poll more quickly in f2a48aee
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-18 17:53:14 -04:00
Andrew Keesler
f2a48aee2b test/integration: increase timeout to a minute to see if it helps
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-18 17:48:00 -04:00
Monis Khan
d162cb9adf Move to github.com/form3tech-oss/jwt-go
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-18 16:56:04 -04:00
Andrew Keesler
14a28bec24 test/integration: fix second assertion from dae62929
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-18 16:34:30 -04:00
Andrew Keesler
dae62929e0 test/integration: error assertions pass w/ and w/o middleware
In the case where we are using middleware (e.g., when the api group is
different) in our kubeclient, these error messages have a "...middleware request
for..." bit in the middle.

Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-18 15:35:31 -04:00
Andrew Keesler
c22ac17dfe internal/concierge/impersonator: use http/2.0 as much as we can
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-18 15:35:31 -04:00
Ryan Richard
08c446a3e1 Use openssl to generate the test user password instead of /dev/urandom
Because it's more portable across different operating systems and
it is already pre-installed on MacOS.
2021-03-18 11:20:33 -07:00
Ryan Richard
bd8c243636 concierge_impersonation_proxy_test.go: small refactor 2021-03-18 10:46:27 -07:00
Ryan Richard
e4bf6e068f Add a comment to impersonator.go 2021-03-18 10:46:27 -07:00
Monis Khan
120e46b5f7 test/integration: fix race condition
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-18 11:27:52 -04:00
Andrew Keesler
257d69045d Reuse internal/concierge/scheme
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-18 10:40:59 -04:00
Andrew Keesler
05a188d4cd Merge remote-tracking branch 'upstream/main' into impersonation-proxy
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-18 10:36:28 -04:00
Monis Khan
205c22ddbe impersonator config: catch panics when running impersonator
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-18 10:28:28 -04:00
Andrew Keesler
aa79bc7609 internal/concierge/impersonator: ensure log statement is printed
When the frontend connection to our proxy is closed, the proxy falls through to
a panic(), which means the HTTP handler goroutine is killed, so we were not
seeing this log statement.

Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-18 10:14:11 -04:00
Andrew Keesler
a36914f5ca Merge pull request #476 from ankeesler/whoami-cli
cmd/pinniped: add whoami cli command
2021-03-18 09:46:48 -04:00
Andrew Keesler
cc8f0b623c test/integration: add pinniped whoami tests
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-18 08:56:35 -04:00
Andrew Keesler
de6837226e cmd/pinniped: add whoami command
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-18 08:56:34 -04:00
Matt Moyer
3a32833306 Merge pull request #503 from mattmoyer/rework-restart-assertions-helper
Rework integration test assertions for pod restarts.
2021-03-17 14:38:39 -07:00
Matt Moyer
74df6d138b Memoize library.IntegrationEnv so it's only constructed once per test.
This is probably a good idea regardless, but it also avoids an infinite recursion from IntegrationEnv() -> assertNoRestartsDuringTest() -> NewKubeclient() -> IntegrationEnv() -> ...

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-17 13:37:48 -05:00
Matt Moyer
0dd2b358fb Extend assertNoRestartsDuringTest to dump logs from containers that restarted.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-17 13:37:47 -05:00
Matt Moyer
6520c5a3a1 Extend library.DumpLogs() to dump logs from the previous container, if one exists.
This is important in case the container has crashed and has been restarted.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-17 11:46:40 -05:00
Matt Moyer
5a43a5d53a Remove library.AssertNoRestartsDuringTest and make that assertion implicit in library.IntegrationEnv.
This means we (hopefully) can't forget to include these assertions in any integration test.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-17 11:18:10 -05:00
Margo Crawford
897340860b Small refactor to impersonation proxy integration test 2021-03-16 16:57:46 -07:00
Matt Moyer
4d2035ab2a Merge branch 'main' of github.com:vmware-tanzu/pinniped into impersonation-proxy 2021-03-16 18:19:40 -05:00
Matt Moyer
d85135c12e Merge pull request #501 from mattmoyer/deflake-get-category-test
Improve the reliability of TestGetPinnipedCategory.
2021-03-16 16:18:22 -07:00
Matt Moyer
30a392b900 Improve the reliability of TestGetPinnipedCategory.
This test could flake in some rare scenarios. This change adds a bunch of retries, improves the debugging output if the tests fail, and puts all of the subtests in parallel which saves ~10s on my local machine.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-16 17:39:02 -05:00
Mo Khan
4ab3c64b70 Merge pull request #500 from mattmoyer/deflake-cert-rotation-test
Make TestAPIServingCertificateAutoCreationAndRotation more reliable.
2021-03-16 17:03:07 -04:00
Matt Moyer
2515b2d710 Make TestAPIServingCertificateAutoCreationAndRotation more reliable.
This test has occasionally flaked because it only waited for the APIService GET to finish, but did not wait for the controller to successfully update the target object.

The new code should be more patient and allow the controller up to 10s to perform the expected action.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-16 15:14:24 -05:00
Matt Moyer
10a1e29e15 Merge branch 'main' of github.com:vmware-tanzu/pinniped into impersonation-proxy
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-16 14:35:07 -05:00
Matt Moyer
2319606cd2 Fix some nits from the previous commit that I accidentally merged before fixing.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-16 14:24:13 -05:00
Matt Moyer
10168ab2e7 Merge pull request #499 from vmware-tanzu/add-anon-auth-capability
Describe "anonymousAuthenticationSupported" test cluster capability and add more managed cluster types.
2021-03-16 12:21:47 -07:00
Matt Moyer
c5b784465b Describe "anonymousAuthenticationSupported" test cluster capability and add more managed cluster types.
This new capability describes whether a cluster is expected to allow anonymous requests (most do since k8s 1.6.x, but AKS has it disabled).

This commit also contains new capability YAML files for AKS and EKS, mostly to document publicly how we expect our tests to function in those environments.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-16 13:54:29 -05:00
Monis Khan
236dbdb2c4 impersonator: test UID impersonation and header canonicalization
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-16 13:00:51 -04:00
Ryan Richard
6887d0aca2 Repeat the method and url in the log line for the userinfo username 2021-03-15 17:12:03 -07:00
Margo Crawford
64e0dbb481 Sleep for 1 minute 10 seconds instead of a minute in timeout test 2021-03-15 16:33:47 -07:00
Ryan Richard
e47543233c Merge branch 'main' into impersonation-proxy 2021-03-15 16:28:25 -07:00
Ryan Richard
2460568be3 Add some debug logging 2021-03-15 16:26:51 -07:00
Ryan Richard
1b31489347 Add prepare-impersonator-on-kind.sh for manually starting impersonator
It takes a lot of manual steps to get ready to manually test the
impersonation proxy on a kind cluster, which makes it error prone,
so encapsulate them into a script to make it easier.
2021-03-15 16:26:51 -07:00
Ryan Richard
ab6452ace7 Remove linting from pre-commit because it is slow and messes up GoLand
It seems to confusing committing in the GoLand IDE.
2021-03-15 16:25:45 -07:00
Matt Moyer
c46aa1c29d Merge pull request #490 from vmware-tanzu/dependabot/docker/golang-1.16.2
Bump golang from 1.16.1 to 1.16.2
2021-03-15 15:08:03 -07:00
Margo Crawford
939ea30030 Make all tests but disable test parallelized
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-15 14:34:41 -07:00
Andrew Keesler
efd973fa17 Test waiting for a minute and keeping connection open
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-03-15 14:34:41 -07:00
Monis Khan
4f671f5dca dynamiccert: unit test with DynamicServingCertificateController
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-15 17:23:37 -04:00
Ryan Richard
a5384a6e38 Merge branch 'main' into impersonation-proxy 2021-03-15 13:06:36 -07:00
dependabot[bot]
e64f2fe7fb Bump golang from 1.16.1 to 1.16.2
Bumps golang from 1.16.1 to 1.16.2.

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-15 19:55:44 +00:00
Matt Moyer
035362f4d3 Merge pull request #494 from vmware-tanzu/dependabot/go_modules/k8s.io/klog/v2-2.8.0
Bump k8s.io/klog/v2 from 2.6.0 to 2.8.0
2021-03-15 12:54:46 -07:00
Ryan Richard
8065a8d2e6 TestKubeCertAgent waits for CredentialIssuer strategy to be successful
At the end of the test, wait for the KubeClusterSigningCertificate
strategy on the CredentialIssuer to go back to being healthy, to avoid
polluting other integration tests which follow this one.
2021-03-15 11:43:12 -07:00
Ryan Richard
e22ad6171a Fix a race detector warning by re-declaring err in a t.Cleanup() 2021-03-15 11:43:12 -07:00
dependabot[bot]
c2b0acf241 Bump k8s.io/klog/v2 from 2.6.0 to 2.8.0
Bumps [k8s.io/klog/v2](https://github.com/kubernetes/klog) from 2.6.0 to 2.8.0.
- [Release notes](https://github.com/kubernetes/klog/releases)
- [Changelog](https://github.com/kubernetes/klog/blob/master/RELEASE.md)
- [Commits](https://github.com/kubernetes/klog/compare/v2.6.0...v2.8.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-15 17:36:36 +00:00
Monis Khan
00694c9cb6 dynamiccert: split into serving cert and CA providers
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-15 12:24:07 -04:00
Matt Moyer
dc96f398da Merge pull request #497 from mattmoyer/ignore-local-user-authenticator-coverage
Ignore test coverage for local-user-authenticator.
2021-03-15 08:46:28 -07:00
Matt Moyer
755a87cdbb Ignore test coverage for local-user-authenticator.
This should ignore coverage changes in this test-only component, using the syntax described here: https://docs.codecov.io/docs/ignoring-paths.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-15 10:43:17 -05:00
Matt Moyer
c538a4e8e8 Merge pull request #495 from mattmoyer/add-golangci-lint-to-pre-commit-hooks
Add golangci-lint to .pre-commit-config.yaml.
2021-03-15 08:23:09 -07:00
Matt Moyer
41949d8e07 Add golangci-lint to .pre-commit-config.yaml.
This is the configuration for https://pre-commit.com/, which now also runs golangci-lint using the same version as CI (currently v1.33.0).

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-15 10:20:59 -05:00
Monis Khan
4c162be8bf impersonator: add comment about long running func
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-15 09:43:06 -04:00
Monis Khan
b530cef3b1 impersonator: encode proper API status on failure
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-13 20:25:23 -05:00
Ryan Richard
c82f568b2c certauthority.go: Refactor issuing client versus server certs
We were previously issuing both client certs and server certs with
both extended key usages included. Split the Issue*() methods into
separate methods for issuing server certs versus client certs so
they can have different extended key usages tailored for each use
case.

Also took the opportunity to clean up the parameters of the Issue*()
methods and New() methods to more closely match how we prefer to call
them. We were always only passing the common name part of the
pkix.Name to New(), so now the New() method just takes the common name
as a string. When making a server cert, we don't need to set the
deprecated common name field, so remove that param. When making a client
cert, we're always making it in the format expected by the Kube API
server, so just accept the username and group as parameters directly.
2021-03-12 16:09:37 -08:00
Monis Khan
5e4746e96b impersonator: match kube API server long running func
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-12 16:36:37 -05:00
Matt Moyer
077aa8a42e Fix a copy-paste typo in the ImpersonationProxyInfo JSON field name.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-12 13:24:05 -06:00
Margo Crawford
d509e7012e Add eventually loop to port-forward test
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-12 10:44:11 -08:00
Monis Khan
8c0bafd5be impersonator: prep work for future SA token support
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-12 10:47:07 -05:00
Monis Khan
12b13b1ea5 impersonator: wire in genericapiserver.Config
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-12 10:47:07 -05:00
Andrew Keesler
5b1dc0abdf test/integration: add some more debugging to kubectl impersonation test
I think this is nondeterministic...

Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-12 10:45:36 -05:00
Andrew Keesler
253e0f8e9a test/integration: TestImpersonationProxy/websocket_client passes on my machine now
I'm kinda surprised this is working with our current implementation of the
impersonator, but regardless this seems like a step forward.

Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-12 09:54:59 -05:00
Ryan Richard
87f2899047 impersonator_test.go: small refactor of previous commit 2021-03-11 17:24:52 -08:00
Ryan Richard
6ddf4c04e6 impersonator_test.go: Test failed and anonymous auth 2021-03-11 17:11:38 -08:00
Ryan Richard
1d68841c78 impersonator_test.go: Test one more thing and small refactors 2021-03-11 16:44:08 -08:00
Ryan Richard
f77c92560f Rewrite impersonator_test.go, add missing argument to IssuePEM()
The impersonator_test.go unit test now starts the impersonation
server and makes real HTTP requests against it using client-go.
It is backed by a fake Kube API server.

The CA IssuePEM() method was missing the argument to allow a slice
of IP addresses to be passed in.
2021-03-11 16:27:16 -08:00
Ryan Richard
c12a23725d Fix lint errors from a previous commit 2021-03-11 16:21:40 -08:00
Matt Moyer
d5beba354b Merge pull request #487 from vmware-tanzu/dependabot/docker/golang-1.16.1
Bump golang from 1.16.0 to 1.16.1
2021-03-11 16:12:07 -08:00
Andrew Keesler
71712b2d00 Add test for http2
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-03-11 15:49:49 -08:00
dependabot[bot]
ad3f04a982 Bump golang from 1.16.0 to 1.16.1
Bumps golang from 1.16.0 to 1.16.1.

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-11 22:25:17 +00:00
Matt Moyer
a52455504f Capitalize "Concierge" in these error messages as well, for consistency.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-11 16:24:20 -06:00
Matt Moyer
4f154100ff Remove "--concierge-mode" flag from "pinniped login [...]" commands.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-11 16:24:20 -06:00
Matt Moyer
d2d9b1e49e Stop outputting "--concierge-mode" from "pinniped get kubeconfig".
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-11 16:13:29 -06:00
Matt Moyer
c9ce067a0e Captialize "API" in this error message.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-11 16:11:46 -06:00
Pablo Schuhmacher
1af25552a0 Update ROADMAP.md 2021-03-11 13:58:34 -08:00
Matt Moyer
a64786a728 Fix TestCLIGetKubeconfigStaticToken for new CLI log output.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-11 15:48:04 -06:00
Monis Khan
2d28d1da19 Implement all optional methods in dynamic certs provider
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-11 16:24:08 -05:00
Matt Moyer
78fdc59d2d Merge branch 'main' of github.com:vmware-tanzu/pinniped into impersonation-proxy 2021-03-11 14:56:11 -06:00
Ryan Richard
29d7f406f7 Test double impersonation as the cluster admin 2021-03-11 12:53:27 -08:00
Matt Moyer
3449b896d6 Merge pull request #488 from mattmoyer/add-retries-for-supervisor-discovery-tests
Add retries to TestSupervisorTLSTerminationWithSNI and TestSupervisorOIDCDiscovery.
2021-03-11 12:22:22 -08:00
Margo Crawford
22ca2da1ff test/integration: add "kubectl attach" test to TestImpersonationProxy
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-11 15:10:16 -05:00
Matt Moyer
e98c6dfdd8 Add retries to TestSupervisorTLSTerminationWithSNI and TestSupervisorOIDCDiscovery.
These tests occasionally flake because of a conflict error such as:

```
    supervisor_discovery_test.go:105:
        	Error Trace:	supervisor_discovery_test.go:587
        	            				supervisor_discovery_test.go:105
        	Error:      	Received unexpected error:
        	            	Operation cannot be fulfilled on federationdomains.config.supervisor.pinniped.dev "test-oidc-provider-lvjfw": the object has been modified; please apply your changes to the latest version and try again
        	Test:       	TestSupervisorOIDCDiscovery
```

These retries should improve the reliability of the tests.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-11 13:18:15 -06:00
Andrew Keesler
fcd8c585c3 test/integration: update "kubectl port-forward" test to use non-privileged port
This was failing on our laptops because 443 is a privileged port.

Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-03-11 13:05:26 -05:00
Ryan Richard
a918e9fb97 concierge_impersonation_proxy_test.go: Fix lint error in previous commit 2021-03-11 10:04:24 -08:00
Ryan Richard
34accc3dee Test using a service account token to auth to the impersonator
Also make each t.Run use its own namespace to slight reduce the
interdependency between them.

Use t.Cleanup instead of defer in whoami_test.go just to be consistent
with other integration tests.
2021-03-11 10:01:17 -08:00
Ryan Richard
61d64fc4c6 Use ioutil.ReadFile instead of os.ReadFile
Because it works on older golang versions too.
2021-03-11 08:58:54 -08:00
Andrew Keesler
b793b9a17e test/integration: add 'kubectl logs' test to TestImpersonationProxy
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-11 10:42:28 -05:00
Monis Khan
7b1ecf79a6 Fix race between err chan send and re-queue
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-11 10:13:29 -05:00
Andrew Keesler
32b038c639 test/integration: add 'kubectl cp' test to TestImpersonationProxy
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-03-11 10:07:16 -05:00
Ryan Richard
d13bb07b3e Add integration test for using WhoAmIRequest through impersonator 2021-03-10 16:57:15 -08:00
Margo Crawford
24396b6af1 Use gorilla websocket library so squid proxy works 2021-03-10 16:03:52 -08:00
Ryan Richard
006dc8aa79 Small test refactor 2021-03-10 14:50:46 -08:00
Ryan Richard
2a2e2f532b Remove an integration test that is covered elsewhere now
The same coverage that was supplied by
TestCredentialRequest_OtherwiseValidRequestWithRealTokenShouldFailWhenTheClusterIsNotCapable
is now provided by an assertion at the end of TestImpersonationProxy,
so delete the duplicate test which was failing on GKE because the
impersonation proxy is now active by default on GKE.
2021-03-10 14:17:20 -08:00
Ryan Richard
1078bf4dfb Don't pass credentials when testing impersonation proxy port is closed
When testing that the impersonation proxy port was closed there
is no need to include credentials in the request. At the point when
we want to test that the impersonation proxy port is closed, it is
possible that we cannot perform a TokenCredentialRequest to get a
credential either.

Also add a new assertion that the TokenCredentialRequest stops handing
out credentials on clusters which have no successful strategies.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-10 13:08:15 -08:00
Matt Moyer
c14621428f Merge pull request #485 from vmware-tanzu/pabloschuhmacher-patch-2
Create ROADMAP.md
2021-03-10 12:43:55 -08:00
Monis Khan
6582c23edb Fix a race detector error in a unit test
Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-03-10 11:24:42 -08:00
Ryan Richard
0b300cbe42 Use TokenCredentialRequest instead of base64 token with impersonator
To make an impersonation request, first make a TokenCredentialRequest
to get a certificate. That cert will either be issued by the Kube
API server's CA or by a new CA specific to the impersonator. Either
way, you can then make a request to the impersonator and present
that client cert for auth and the impersonator will accept it and
make the impesonation call on your behalf.

The impersonator http handler now borrows some Kube library code
to handle request processing. This will allow us to more closely
mimic the behavior of a real API server, e.g. the client cert
auth will work exactly like the real API server.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-10 10:30:06 -08:00
Pablo Schuhmacher
876f0a55d8 Create ROADMAP.md in actual markdown
fixed the random html generated when converting the google doc to markdown
2021-03-09 18:41:40 -08:00
Margo Crawford
c853707889 Added integration test for using websockets via the impersonation proxy
Tested that this test passed when using the kube api server directly,
so it's just the impersonation proxy that must be improved.
2021-03-09 17:00:30 -08:00
Matt Moyer
005133fbfb Add more debug logging when waiting for pending strategies.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-09 16:56:53 -06:00
Matt Moyer
0cb1538b39 Fix linter warnings, including a bit of refactoring.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-09 15:16:46 -06:00
Matt Moyer
0abe10e6b2 Add new behavior to "pinniped get kubeconfig" to wait for pending strategies to become non-pending.
This behavior can be disabled with "--concierge-skip-wait".

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-09 14:50:35 -06:00
Margo Crawford
883b90923d Add integration test for kubectl port-forward with impersonation 2021-03-09 11:32:50 -08:00
Matt Moyer
d6a0dfa497 Add some debug logging when "pinniped get kubeconfig" fails to find a successful strategy.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-09 12:44:35 -06:00
Matt Moyer
29d5e43220 Fix minor typo in e2e_test.go.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-09 12:12:52 -06:00
Matt Moyer
eef1fd0c64 Merge pull request #481 from vmware-tanzu/dependabot/go_modules/github.com/ory/fosite-0.39.0
Bump github.com/ory/fosite from 0.38.0 to 0.39.0
2021-03-09 07:51:27 -06:00
dependabot[bot]
b2be83ee45 Bump github.com/ory/fosite from 0.38.0 to 0.39.0
Bumps [github.com/ory/fosite](https://github.com/ory/fosite) from 0.38.0 to 0.39.0.
- [Release notes](https://github.com/ory/fosite/releases)
- [Changelog](https://github.com/ory/fosite/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ory/fosite/compare/v0.38.0...v0.39.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-09 05:50:01 +00:00
Matt Moyer
b20a8358d3 Merge branch 'main' of github.com:vmware-tanzu/pinniped into impersonation-proxy 2021-03-08 15:16:40 -06:00
Matt Moyer
a58b460bcb Switch TestImpersonationProxy to get clients from library.NewKubeclient instead of directly from kubernetes.NewForConfig.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-08 15:03:34 -06:00
Matt Moyer
8fd6a71312 Use simpler prefix matching for impersonation headers.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-08 14:44:38 -06:00
Matt Moyer
6efbd81f75 Rename this flag types for consistency.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-08 14:33:38 -06:00
Matt Moyer
a059d8dfce Refactor "get kubeconfig" a bit more to clean things up.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-08 14:31:13 -06:00
Matt Moyer
8c0a073cb6 Fix this constant name to match its value.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-08 13:31:16 -06:00
Matt Moyer
389cd3486b Rework "pinniped get kubeconfig" so that --concierge-mode can be used even when auto-discovering other parameters.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-08 11:43:56 -06:00
Matt Moyer
eac108aee5 Merge pull request #478 from vmware-tanzu/prepare-script-macos-big-sur
Fix hack/prepare-for-integration-tests.sh for MacOS Big Sur
2021-03-08 10:02:52 -06:00
Ryan Richard
49ec16038c Add integration test for using "kubectl exec" through the impersonator
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-03-05 16:14:45 -08:00
Margo Crawford
4bd68b1fa1 Use LC_ALL=C instead of LC_CTYPE=C because it works on Big Sur
It also works on the slightly older MacOS Catalina.
This script is only used on development laptops, so hopefully
this will work for more laptop OS's now.

Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-03-05 15:25:52 -08:00
Matt Moyer
73419313ee Log when the validation eventually succeeds.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-05 16:59:43 -06:00
Matt Moyer
4750d7d7d2 The stderr from "pinniped get kubeconfig" is no longer empty.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-05 16:57:24 -06:00
Matt Moyer
ba0dc3bf52 Remove this test retry loop since the "get kubeconfig" step should now wait.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-05 16:39:57 -06:00
Matt Moyer
5d8594b285 Add validation step to "pinniped get kubeconfig".
This adds two new flags to "pinniped get kubeconfig": --skip-validation and --timeout.

By default, at the end of the kubeconfig generation process, we validate that we can reach the configured cluster. In the future this might also validate that the TokenCredentialRequest API is running, but for not it just verifies that the DNS name resolves, and the TLS connection is available on the given port.

If there is an error during this check, we block and retry for up to 10 minutes. This duration can be changed with --timeout an the entire process can be skipped with --skip-validation.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-05 16:35:42 -06:00
Matt Moyer
ce1b6303d9 Add an "--output" flag to "pinniped get kubeconfig".
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-05 15:53:30 -06:00
Matt Moyer
36bc679142 Add diagnostic logging to "pinniped get kubeconfig".
These stderr logs should help clarify all the autodetection logic that's happening in a particular run.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-05 15:52:17 -06:00
Matt Moyer
c4f6fd5b3c Add a bit nicer assertion helper in testutil/testlogger.
This makes output that's easier to copy-paste into the test. We could also make it ignore the order of key/value pairs in the future.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-05 15:49:45 -06:00
Matt Moyer
52f58477b8 Wait for the ELB to become available _before_ starting the kubectl command.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-05 09:32:49 -06:00
Matt Moyer
d848499176 Close this HTTP response body in TestE2EFullIntegration.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-05 08:45:25 -06:00
Matt Moyer
c3b7d21037 Be less picky about what error code is returned here.
The thing we're waiting for is mostly that DNS is resolving, the ELB is listening, and connections are making it to the proxy.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-05 08:40:29 -06:00
Matt Moyer
832bc2726e Merge pull request #477 from vmware-tanzu/dependabot/go_modules/github.com/google/go-cmp-0.5.5
Bump github.com/google/go-cmp from 0.5.4 to 0.5.5
2021-03-05 08:20:51 -06:00
dependabot[bot]
3833ba0430 Bump github.com/google/go-cmp from 0.5.4 to 0.5.5
Bumps [github.com/google/go-cmp](https://github.com/google/go-cmp) from 0.5.4 to 0.5.5.
- [Release notes](https://github.com/google/go-cmp/releases)
- [Commits](https://github.com/google/go-cmp/compare/v0.5.4...v0.5.5)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-05 05:59:54 +00:00
Ryan Richard
ec133b9743 Resolve some new linter errors 2021-03-04 17:44:01 -08:00
Ryan Richard
d8c6894cbc All controller unit tests should not cancel context until test is over
All controller unit tests were accidentally using a timeout context
for the informers, instead of a cancel context which stays alive until
each test is completely finished. There is no reason to risk
unpredictable behavior of a timeout being reached during an individual
test, even though with the previous 3 second timeout it could only be
reached on a machine which is running orders of magnitude slower than
usual, since each test usually runs in about 100-300 ms. Unfortunately,
sometimes our CI workers might get that slow.

This sparked a review of other usages of timeout contexts in other
tests, and all of them were increased to a minimum value of 1 minute,
under the rule of thumb that our tests will be more reliable on slow
machines if they "pass fast and fail slow".
2021-03-04 17:26:01 -08:00
Ryan Richard
b102aa8991 In unit test, wait for obj from informer instead of resource version
In impersonator_config_test.go, instead of waiting for the resource
version to appear in the informers, wait for the actual object to
appear.

This is an attempt to resolve flaky failures that only happen in CI,
but it also cleans up the test a bit by avoiding inventing fake resource
version numbers all over the test.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-04 17:26:01 -08:00
Ryan Richard
9eb97e2683 Use Eventually when making tls connections and avoid resource version 0
- Use `Eventually` when making tls connections because the production
  code's handling of starting and stopping the TLS server port
  has some async behavior.
- Don't use resource version "0" because that has special meaning
  in the informer libraries.
2021-03-04 17:26:01 -08:00
Matt Moyer
fea626b654 Remove this proxy-related test code that we ended up not needing.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-04 17:19:59 -06:00
Matt Moyer
16163b989b Use regular http.Client in this test.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-04 17:18:24 -06:00
Matt Moyer
165fce67af Use the unversioned REST client for this check.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-04 16:23:39 -06:00
Matt Moyer
6a8f377781 Fix a linter warning.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-04 16:16:03 -06:00
Matt Moyer
d24cf4b8a7 Go back to testing entirely through the proxy, but add a retry loop during the first connection.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-04 16:05:56 -06:00
Matt Moyer
34e15f03c3 Simplify const declarations in flag_types.go.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-04 15:21:50 -06:00
Matt Moyer
274e6281a8 Whoops, missed these fixes in test/library/env.go.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-04 15:21:49 -06:00
Matt Moyer
7146cb3880 Remove old debug-make-impersonation-token command.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-04 15:02:42 -06:00
Matt Moyer
9dfbe60253 Do the kubeconfig proxy environment injection, but actually render back out the YAML.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-04 14:41:20 -06:00
Matt Moyer
1734280a19 Merge branch 'main' of github.com:vmware-tanzu/pinniped into impersonation-proxy 2021-03-04 12:38:00 -06:00
Matt Moyer
9a0f75980d Set a special proxy environment just for the "pinniped login oidc" command in the E2E test.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-04 12:35:29 -06:00
Matt Moyer
ddd1d29e5d Fix "pinniped get kubeconfig" strategy detection to pick the _first_ working strategy.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-04 12:24:57 -06:00
Matt Moyer
03f09c6870 Allow TestE2EFullIntegration to run on clusters where only the impersonation proxy works (again).
This time, don't use the Squid proxy if the cluster supports real external load balancers (as in EKS/GKE/AKS).

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-04 12:04:52 -06:00
Matt Moyer
f99c186c55 Merge pull request #475 from vmware-tanzu/dependabot/go_modules/k8s.io/klog/v2-2.6.0
Bump k8s.io/klog/v2 from 2.5.0 to 2.6.0
2021-03-04 10:04:08 -06:00
dependabot[bot]
14b8def320 Bump k8s.io/klog/v2 from 2.5.0 to 2.6.0
Bumps [k8s.io/klog/v2](https://github.com/kubernetes/klog) from 2.5.0 to 2.6.0.
- [Release notes](https://github.com/kubernetes/klog/releases)
- [Changelog](https://github.com/kubernetes/klog/blob/master/RELEASE.md)
- [Commits](https://github.com/kubernetes/klog/compare/v2.5.0...v2.6.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-04 06:10:36 +00:00
Ryan Richard
5697adc36a Revert "Allow TestE2EFullIntegration to run on clusters where only the impersonation proxy works."
This reverts commit 7c9aff3278.
2021-03-03 17:24:10 -08:00
Ryan Richard
9c1c760f56 Always clean up the ConfigMap at the end of the impersonator int test
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-03-03 16:23:07 -08:00
Ryan Richard
48f2ae9eb4 Fix a typo in concierge_impersonation_proxy_test.go 2021-03-03 15:18:01 -08:00
Matt Moyer
7c9aff3278 Allow TestE2EFullIntegration to run on clusters where only the impersonation proxy works.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-03 17:05:46 -06:00
Ryan Richard
58607c7e81 Update TestCredentialIssuer int test to ignore ImpersonationProxy type 2021-03-03 14:19:24 -08:00
Ryan Richard
1b3103c9b5 Remove a nolint comment to satisfy the version of the linter used in CI 2021-03-03 13:37:03 -08:00
Ryan Richard
666c0b0e18 Use CredentialIssuer for URL/CA discovery in impersonator int test 2021-03-03 12:53:41 -08:00
Ryan Richard
f0fc84c922 Add new allowed values to field validations on CredentialIssuer
The new values are used by the impersonation proxy's status.
2021-03-03 12:53:41 -08:00
Ryan Richard
7b7901af36 Add -timeout 0 when describing how to run integration tests
Because otherwise `go test` will panic/crash your test if it takes
longer than 10 minutes, which is an annoying way for an integration
test to fail since it skips all of the t.Cleanup's.
2021-03-03 12:53:41 -08:00
Ryan Richard
57453773ea CONTRIBUTING.md: remove mention of Tilt, since it isn't working well 2021-03-03 12:53:41 -08:00
Matt Moyer
f4fcb9bde6 Sort CredentialIssuer strategies in preferred order.
This updates our issuerconfig.UpdateStrategy to sort strategies according to a weighted preference.
The TokenCredentialRequest API strategy is preffered, followed by impersonation proxy, followed by any other unknown types.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-03 14:03:27 -06:00
Margo Crawford
0799a538dc change FromString to Parse so TargetPort parses correctly 2021-03-03 11:12:37 -08:00
Mo Khan
4f700d4811 Merge pull request #473 from enj/enj/r/oidc_discovery_json
oidc discovery: encode metadata once and reuse
2021-03-03 14:12:33 -05:00
Monis Khan
d7edc41c24 oidc discovery: encode metadata once and reuse
Signed-off-by: Monis Khan <mok@vmware.com>
2021-03-03 13:37:43 -05:00
Ryan Richard
333a3ab4c2 impersonator_config_test.go: Add another unit test 2021-03-03 09:37:08 -08:00
Ryan Richard
730092f39c impersonator_config.go: refactor to clean up cert name handling 2021-03-03 09:22:35 -08:00
Ryan Richard
d3599c541b Fill in the frontend field of CredentialIssuer status for impersonator 2021-03-02 16:52:23 -08:00
Ryan Richard
454f35ccd6 Edit a comment on a type and run codegen 2021-03-02 16:52:23 -08:00
Margo Crawford
27daf0a2fe Increase timeout for creating load balancer in impersonation proxy test 2021-03-02 15:49:30 -08:00
Ryan Richard
8bf03257f4 Add new impersonation-related constants to api types and run codegen 2021-03-02 15:28:13 -08:00
Ryan Richard
1ad2c38509 Impersonation controller updates CredentialIssuer on every call to Sync
- This commit does not include the updates that we plan to make to
  the `status.strategies[].frontend` field of the CredentialIssuer.
  That will come in a future commit.
2021-03-02 15:28:13 -08:00
Ryan Richard
84cc42b2ca Remove tls field from the impersonator config
- Decided that we're not going to implement this now, although
  we may decide to add it in the future
2021-03-02 15:28:13 -08:00
Margo Crawford
4c68050706 Allow all headers besides impersonation-* through impersonation proxy 2021-03-02 15:01:13 -08:00
Matt Moyer
aa826a1579 Merge pull request #472 from mattmoyer/deflake-getpinnipedcategory-test
Make TestGetPinnipedCategory and TestKubeClientOwnerRef tests more resilient.
2021-03-02 16:42:23 -06:00
Matt Moyer
60f92d5fe2 Merge branch 'main' of github.com:vmware-tanzu/pinniped into impersonation-proxy
This is more than an automatic merge. It also includes a rewrite of the CredentialIssuer API impersonation proxy fields using the new structure, and updates to the CLI to account for that new API.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-02 16:06:19 -06:00
Matt Moyer
df27c2e1fc Use randomly generated API groups in TestKubeClientOwnerRef.
I think this is another aspect of the test flakes we're trying to fix. This matters especially for the "Multiple Pinnipeds" test environment where two copies of the test suite are running concurrently.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-02 15:48:13 -06:00
Matt Moyer
45f57939af Make TestGetPinnipedCategory more resilient.
If the test is run immediately after the Concierge is installed, the API server can still have broken discovery data and return an error on the first call.
This commit adds a retry loop to attempt this first kubectl command for up to 60s before declaring failure.
The subsequent tests should be covered by this as well since they are not run in parallel.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-02 15:40:17 -06:00
Matt Moyer
30f5f66090 Merge pull request #471 from vmware-tanzu/change-credentialissuer-strategies-api
Deprecate status.kubeConfigInfo field in CredentialIssuer and move this data into strategies field.
2021-03-02 15:39:41 -06:00
Matt Moyer
2a29303e3f Fix label handling in kubecertagent controllers.
These controllers were a bit inconsistent. There were cases where the controllers ran out of the expected order and the custom labels might not have been applied.

We should still plan to remove this label handling or move responsibility into the middleware layer, but this avoids any regression.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-02 13:59:46 -06:00
Matt Moyer
643c60fd7a Drop NewKubeConfigInfoPublisherController, start populating strategy frontend from kubecertagent execer controller.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-02 13:09:25 -06:00
Matt Moyer
7174f857d8 Add generated code.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-02 13:09:25 -06:00
Matt Moyer
0be2c0d40f Add CredentialIssuer "status.strategies[].frontend" field.
This field is a new tagged-union style field that describes how clients can connect using each successful strategy.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-02 13:09:25 -06:00
Ryan Richard
a75c2194bc Read the names of the impersonation-related resources from the config
They were previously temporarily hardcoded. Now they are set at deploy
time via the static ConfigMap in deployment.yaml
2021-03-02 09:31:24 -08:00
Ryan Richard
41140766f0 Add integration test which demonstrates double impersonation
We don't support using the impersonate headers through the impersonation
proxy yet, so this integration test is a negative test which asserts
that we get an error.
2021-03-01 17:53:26 -08:00
Ryan Richard
045c427317 Merge branch 'main' into impersonation-proxy 2021-03-01 17:03:56 -08:00
Ryan Richard
ac404af48f Add .DS_Store files to .gitignore 2021-03-01 17:03:05 -08:00
Ryan Richard
a2ecd05240 Impersonator config controller writes CA cert & key to different Secret
- The CA cert will end up in the end user's kubeconfig on their client
  machine, so if it changes they would need to fetch the new one and
  update their kubeconfig. Therefore, we should avoid changing it as
  much as possible.
- Now the controller writes the CA to a different Secret. It writes both
  the cert and the key so it can reuse them to create more TLS
  certificates in the future.
- For now, it only needs to make more TLS certificates if the old
  TLS cert Secret gets deleted or updated to be invalid. This allows
  for manual rotation of the TLS certs by simply deleting the Secret.
  In the future, we may want to implement some kind of auto rotation.
- For now, rotation of both the CA and TLS certs will also happen if
  you manually delete the CA Secret. However, this would cause the end
  users to immediately need to get the new CA into their kubeconfig,
  so this is not as elegant as a normal rotation flow where you would
  have a window of time where you have more than one CA.
2021-03-01 17:02:08 -08:00
Matt Moyer
a778a5ef81 Merge pull request #469 from mattmoyer/split-out-credentialissuer-status-helpers
Factor out issuerconfig.UpdateStrategy helper.
2021-03-01 18:14:16 -06:00
Matt Moyer
c94ee7188c Factor out issuerconfig.UpdateStrategy helper.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-01 16:21:10 -06:00
Matt Moyer
7ef6a02d0a Merge pull request #466 from mattmoyer/bump-dependencies
Upgrade Go and Go library dependencies.
2021-03-01 14:16:58 -06:00
Matt Moyer
c832cab8d0 Update internal/oidc/token_exchange.go for latest Fosite version.
The `fosite.TokenEndpointHandler` changed and now requires some additional methods.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-01 13:08:41 -06:00
Matt Moyer
234465789b Regenerate gomock mocks with v1.5.0.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-01 11:44:27 -06:00
dependabot[bot]
da6d69d807 Bump github.com/golang/mock from 1.4.4 to 1.5.0
Bumps [github.com/golang/mock](https://github.com/golang/mock) from 1.4.4 to 1.5.0.
- [Release notes](https://github.com/golang/mock/releases)
- [Changelog](https://github.com/golang/mock/blob/master/.goreleaser.yml)
- [Commits](https://github.com/golang/mock/compare/v1.4.4...v1.5.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-01 11:44:26 -06:00
dependabot[bot]
04ef7c5456 Bump github.com/ory/fosite from 0.36.0 to 0.38.0
Bumps [github.com/ory/fosite](https://github.com/ory/fosite) from 0.36.0 to 0.38.0.
- [Release notes](https://github.com/ory/fosite/releases)
- [Changelog](https://github.com/ory/fosite/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ory/fosite/compare/v0.36.0...v0.38.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-01 11:44:26 -06:00
dependabot[bot]
f05c3092b5 Bump github.com/go-openapi/spec from 0.19.9 to 0.20.3
Bumps [github.com/go-openapi/spec](https://github.com/go-openapi/spec) from 0.19.9 to 0.20.3.
- [Release notes](https://github.com/go-openapi/spec/releases)
- [Commits](https://github.com/go-openapi/spec/compare/v0.19.9...v0.20.3)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-01 11:44:26 -06:00
dependabot[bot]
2637dc00da Bump golang from 1.15.8 to 1.16.0
Bumps golang from 1.15.8 to 1.16.0.

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-01 11:44:26 -06:00
Matt Moyer
e8365d2c57 Merge pull request #467 from mattmoyer/fix-docs-title
Fix missing titles on website docs.
2021-03-01 11:35:56 -06:00
Matt Moyer
dd151b3f50 Fix missing titles on website docs.
Also fixes our sitemap to have correct `lastmod` times when built locally (it was already correct on Netlify).

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-03-01 11:31:27 -06:00
Ryan Richard
f1eeae8c71 Parse out ports from impersonation proxy endpoint config
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-02-26 15:01:38 -08:00
Ryan Richard
41e4a74b57 impersonator_config_test.go: more small refactoring of test helpers 2021-02-26 13:53:30 -08:00
Margo Crawford
fa49beb623 Change length of TLS certs and CA.
Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-02-26 12:05:17 -08:00
Margo Crawford
9bd206cedb impersonator_config_test.go: small refactor of test helpers
Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-02-26 11:27:19 -08:00
Ryan Richard
5b01e4be2d impersonator_config.go: handle more error cases
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-02-26 10:58:56 -08:00
Ryan Richard
bbbb40994d Prefer hostnames over IPs when making certs to match load balancer ingress
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-02-25 17:03:34 -08:00
Margo Crawford
f709da5569 Updated test assertions for new logger version
Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-02-25 15:18:36 -08:00
Margo Crawford
ccb17843c1 Fix some lint errors that resulted from merging main
Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-02-25 15:06:24 -08:00
Ryan Richard
f8111db5ff Merge branch 'main' into impersonation-proxy 2021-02-25 14:50:40 -08:00
Ryan Richard
3fcde8088c concierge_impersonation_proxy_test.go: Make it work on more clusters
Should work on cluster which have:
- load balancers not supported, has squid proxy (e.g. kind)
- load balancers supported, has squid proxy (e.g. EKS)
- load balancers supported, no squid proxy (e.g. GKE)

When testing with a load balancer, call the impersonation proxy through
the load balancer.

Also, added a new library.RequireNeverWithoutError() helper.

Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-02-25 14:40:18 -08:00
Matt Moyer
f937ae2c07 Add --concierge-credential-issuer flag to "pinniped get kubeconfig" command.
This flag selects a CredentialIssuer to use when detecting what mode the Concierge is in on a cluster. If not specified, the command will look for a single CredentialIssuer. If there are multiple, then the flag is required.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-25 14:31:51 -06:00
Matt Moyer
1c7c22352f Switch "get kubeconfig" flags to use --concierge-mode flag instead of boolean flag.
This is the same as the previous change to the login commands.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-25 14:31:51 -06:00
Ryan Richard
0cae72b391 Get hostname from load balancer ingress to use for impersonator certs
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-02-25 11:40:14 -08:00
Margo Crawford
9a8c80f20a Impersonator checks cert addresses when endpoint config is a hostname
Also update concierge_impersonation_proxy_test.go integration test
to use real TLS when calling the impersonator.

Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-02-25 10:27:19 -08:00
Matt Moyer
a42e3708aa Merge pull request #453 from mattmoyer/bump-dependencies
Bump a bunch of minor dependencies.
2021-02-25 09:33:53 -06:00
Matt Moyer
c8fc8a0b65 Reformat some log-based test assertions.
These are prone to breaking when stdr is upgraded because they rely on the exact ordering of keys in the log message. If we have more problems we can rewrite the assertions to be more robust, but for this time I'm just fixing them to match the new output.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-25 08:11:37 -06:00
Margo Crawford
8fc68a4b21 WIP improved cert management in impersonator config
- Allows Endpoint to be a hostname, not just an IP address

Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-02-24 17:08:58 -08:00
Margo Crawford
975d493b8a Fix some small lint errors
Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-02-24 16:09:15 -08:00
Ryan Richard
aee7a7a72b More WIP managing TLS secrets from the impersonation config controller
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-02-24 16:03:26 -08:00
Matt Moyer
a31c24e5a0 Bump a bunch of minor dependencies.
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.6.1 to 1.7.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.6.1...v1.7.0)

Bumps [github.com/go-logr/logr](https://github.com/go-logr/logr) from 0.3.0 to 0.4.0.
- [Release notes](https://github.com/go-logr/logr/releases)
- [Commits](https://github.com/go-logr/logr/compare/v0.3.0...v0.4.0)

Bumps [k8s.io/klog/v2](https://github.com/kubernetes/klog) from 2.4.0 to 2.5.0.
- [Release notes](https://github.com/kubernetes/klog/releases)
- [Changelog](https://github.com/kubernetes/klog/blob/master/RELEASE.md)
- [Commits](https://github.com/kubernetes/klog/compare/v2.4.0...v2.5.0)

Bumps [github.com/go-logr/stdr](https://github.com/go-logr/stdr) from 0.2.0 to 0.4.0.
- [Release notes](https://github.com/go-logr/stdr/releases)
- [Commits](https://github.com/go-logr/stdr/compare/v0.2.0...v0.4.0)

Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.1.1 to 1.1.3.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Changelog](https://github.com/spf13/cobra/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spf13/cobra/compare/v1.1.1...v1.1.3)

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-24 17:37:29 -06:00
Matt Moyer
943b0ff6ec Switch login flags to use --concierge-mode flag instead of boolean flag.
The login commands now expect either `--concierge-mode ImpersonationProxy` or `--concierge-mode TokenCredentialRequestAPI` (the default).

This is partly a style choice, but I also think it helps in case we need to add a third major mode of operation at some point.

I also cleaned up some other minor style items in the help text.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-24 17:09:08 -06:00
Ryan Richard
d42c533fbb WIP managing TLS secrets from the impersonation config controller
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-02-24 10:57:36 -08:00
Matt Moyer
4dbde4cf7f Fix TestImpersonationProxy on Kubernetes 1.20 with RootCAConfigMap.
There is a new feature in 1.20 that creates a ConfigMap by default in each namespace: https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.20.md#introducing-rootcaconfigmap

This broke this test because it assumed that all the ConfigMaps in the ephemeral test namespace were those created by the test code. The fix is to add a test label and rewrite our assertions to filter with it.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-24 12:08:41 -06:00
Matt Moyer
7be8927d5e Add generated code for new CredentialIssuer API fields.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-24 10:47:06 -06:00
Matt Moyer
96d7743eab Add CredentialIssuer API fields for impersonation proxy.
Adds a new optional `spec.impersonationProxyInfo` field to hold the URL and CA data for the impersonation proxy, as well as some additional status condition constants for describing the current status of the impersonation proxy.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-24 10:45:25 -06:00
Matt Moyer
2254f76b30 Fix a broken link, a typo, and tweak menu text.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-24 09:23:21 -06:00
Matt Moyer
852c1b7a27 Fix some copy-paste errors on install-supervisor.md.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-23 16:02:27 -06:00
Matt Moyer
522210adb6 Merge pull request #447 from mattmoyer/website-security-headers
Add security headers to the website.
2021-02-23 14:39:31 -06:00
Matt Moyer
a4089fcc72 Add security headers to the website.
The one bit of JS we have for the mobile menu needed some tweaking.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-23 14:38:05 -06:00
Matt Moyer
60034b39a3 Fix wording on website hero text.
Requested by @pabloschuhmacher as a small fix.
2021-02-23 12:17:26 -08:00
Matt Moyer
2f7c80a5e0 Merge pull request #446 from mattmoyer/more-website-tweaks
More website tweaks.
2021-02-23 14:13:27 -06:00
Matt Moyer
827e6e0dc0 More website tweaks.
These are some more changes that came up when Pablo and I were reviewing the previous docs PR.

In no particular order:

- Fix "related posts" on the blog section, and hide the section if there are none.

- Minor style changes to several pages (guided by various style guides).

- Redirect the root of get.pinniped.dev to our main page (shouldn't really be hit, but it's nice to do something).

- Add more mobile-friendly CSS for our docs.

- Reword the "getting started" CTA, and hide it on the docs pages (you're already there).

- Fix the "Learn how Pinniped provides identity services to Kubernetes" link on the landing page.

- Add a date to our blog post cards.

- Rewrite the hero text on the landing page.

- Fix the docs link for the "Get Started with Pinniped" button on the landing page.

- Rework the landing page grid text.

- Add Margo and Nanci to the team section and sort it alphabetically.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-23 14:03:37 -06:00
Margo Crawford
dac1c9939e concierge_impersonation_proxy_test.go: Test all the verbs
Also:
- Shut down the informer correctly in
  concierge_impersonation_proxy_test.go
- Remove the t.Failed() checks which avoid cleaning up after failed
  tests. This was inconsistent with how most of the tests work, and
  left cruft on clusters when a test failed.

Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-02-23 10:38:32 -08:00
Matt Moyer
a6d74ea876 Merge pull request #443 from mattmoyer/reorg-docs
Restructure website documentation
2021-02-23 11:12:32 -06:00
Matt Moyer
7a1d92a8d4 Restructure docs into new layout.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-23 11:11:07 -06:00
Matt Moyer
f2db76a0d5 Fix typo in multiple-pinnipeds post.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-23 11:11:07 -06:00
Matt Moyer
3721632de2 Move scope doc out of website to SCOPE.md.
This is contributor-focused, so we decided to move it into GitHub only for now.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-23 11:11:07 -06:00
Matt Moyer
4de949fe18 Rework docs sidebar to have some nesting.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-23 11:11:07 -06:00
Andrew Keesler
069b3fba37 Merge remote-tracking branch 'upstream/main' into impersonation-proxy
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-02-23 12:10:52 -05:00
Mo Khan
e74dd47b1d Merge pull request #439 from enj/enj/f/whoami_api
Add WhoAmIRequest Aggregated Virtual REST API
2021-02-23 10:40:38 -05:00
Monis Khan
6a9f57f83d TestWhoAmI: support older clusters (CSR and impersonation)
Signed-off-by: Monis Khan <mok@vmware.com>
2021-02-23 10:15:17 -05:00
Ryan Richard
80ff5c1f17 Fix bug which prevented watches from working through impersonator
Also:
- Changed base64 encoding of impersonator bearer tokens to use
  `base64.StdEncoding` to make it easier for users to manually
  create a token using the unix `base64` command
- Test the headers which are and are not passed through to the Kube API
  by the impersonator more carefully in the unit tests
- More WIP on concierge_impersonation_proxy_test.go

Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-02-22 17:23:11 -08:00
Monis Khan
aa22047a0f Generated
Signed-off-by: Monis Khan <mok@vmware.com>
2021-02-22 20:02:42 -05:00
Monis Khan
abc941097c Add WhoAmIRequest Aggregated Virtual REST API
This change adds a new virtual aggregated API that can be used by
any user to echo back who they are currently authenticated as.  This
has general utility to end users and can be used in tests to
validate if authentication was successful.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-02-22 20:02:41 -05:00
Monis Khan
62630d6449 getAggregatedAPIServerScheme: move group version logic internally
Signed-off-by: Monis Khan <mok@vmware.com>
2021-02-19 11:10:54 -05:00
Mo Khan
f228f022f5 Merge pull request #435 from enj/enj/c/bump_v0.20.4
Bump Kube deps to v0.20.4
2021-02-19 10:59:40 -05:00
Monis Khan
1c1decfaf1 Generated
Signed-off-by: Monis Khan <mok@vmware.com>
2021-02-19 10:33:10 -05:00
Monis Khan
7786c83b0d Bump kube deps to v0.20.4
Signed-off-by: Monis Khan <mok@vmware.com>
2021-02-19 10:26:53 -05:00
Mo Khan
41b75e6977 Merge pull request #431 from enj/enj-patch-1
concierge API service: update groupPriorityMinimum and versionPriority
2021-02-19 08:48:06 -05:00
Mo Khan
a54e1145a5 concierge API service: update groupPriorityMinimum and versionPriority
Copy over values that I have seen used in the past.
Signed-off-by: Monis Khan <mok@vmware.com>
2021-02-19 07:47:38 -05:00
Ryan Richard
b8592a361c Add some comments to concierge_impersonation_proxy_test.go
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-02-18 16:27:03 -08:00
Margo Crawford
19881e4d7f Increase how long we wait for loadbalancers to be deleted for int test
Also add some log messages which might help us debug issues like this
in the future.

Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-02-18 15:58:27 -08:00
Ryan Richard
126f9c0da3 certs_manager.go: Rename some local variables
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-02-18 11:16:34 -08:00
Margo Crawford
7a140bf63c concierge_impersonation_proxy_test.go: add an eventually loop
Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-02-18 11:08:13 -08:00
Ryan Richard
f5fedbb6b2 Add Service resource "delete" permission to Concierge RBAC
- Because the impersonation proxy config controller needs to be able
  to delete the load balancer which it created

Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-02-18 11:00:22 -08:00
Andrew Keesler
957cb2d56c Merge remote-tracking branch 'upstream/main' into impersonation-proxy
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-02-18 13:37:28 -05:00
Andrew Keesler
b3cdc438ce internal/concierge/impersonator: reuse kube bearertoken.Authenticator
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-02-18 10:13:24 -05:00
Margo Crawford
22a3e73bac impersonator_config_test.go: use require.Len() when applicable
Also fix a lint error in concierge_impersonation_proxy_test.go

Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-02-17 17:29:56 -08:00
Margo Crawford
0ad91c43f7 ImpersonationConfigController uses servicesinformer
This is a more reliable way to determine whether the load balancer
is already running.
Also added more unit tests for the load balancer.

Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-02-17 17:22:13 -08:00
Matt Moyer
2b208807a6 Merge pull request #426 from mattmoyer/website-accessibility-tweaks
Tweak website styles for accessibility.
2021-02-17 17:28:03 -06:00
Matt Moyer
25f841d063 Tweak website styles for accessibility.
Makes most of the fonts a bit bigger, increases contrast, fixes some nits about the spacing in numbered/bulletted lists, and adds some image alt texts.

Overall this improves our Lighthouse accessibility score from 71 to 95 and I think it's subjectively more readable.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-17 17:19:58 -06:00
Margo Crawford
10b769c676 Fixed integration tests for load balancer capabilities 2021-02-17 10:55:49 -08:00
Margo Crawford
67da840097 Add loadbalancer for impersonation proxy when needed 2021-02-16 15:57:02 -08:00
Matt Moyer
93d4581721 Workaround a bad module version to fix Dependabot.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-16 17:05:33 -06:00
Matt Moyer
0a7c5b0604 Merge pull request #403 from mattmoyer/add-latest-generated-package
Add "go.pinniped.dev/generated/latest" package that is not a nested module.
2021-02-16 15:30:48 -06:00
Matt Moyer
acbeb93f79 Don't lint generated code.
This wasn't needed before because the other code wasn't in the main module and golangci-lint won't cross a module boundary.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-16 13:18:18 -06:00
Matt Moyer
6565265bee Use new 'go.pinniped.dev/generated/latest' package.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-16 13:00:08 -06:00
Matt Moyer
b42a34d822 Add generated client code for 'latest'.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-16 12:34:33 -06:00
Matt Moyer
3ce3403b95 Update ./hack/update.sh to add a "latest" package.
This is just a copy of the newest Kubernetes version, but as a plain package and not a submodule.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-16 12:28:29 -06:00
Andrew Keesler
eb19980110 internal/concierge/impersonator: set user extra impersonation headers
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-02-16 09:26:47 -05:00
Andrew Keesler
c7905c6638 internal/concierge/impersonator: fail if impersonation headers set
If someone has already set impersonation headers in their request, then
we should fail loudly so the client knows that its existing impersonation
headers will not work.

Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-02-16 08:15:50 -05:00
Andrew Keesler
fdd8ef5835 internal/concierge/impersonator: handle custom login API group
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-02-16 07:55:09 -05:00
Andrew Keesler
25bc8dd8a9 test/integration: hopefully fix TestImpersonationProxy
I think we were assuming the name of our Concierge app, and getting lucky
because it was the name we use when testing locally (but not in CI).

Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-02-15 18:04:21 -05:00
Andrew Keesler
6512ab1351 internal/concierge/impersonator: don't care about namespace
Concierge APIs are no longer namespaced (see f015ad5852).

Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-02-15 17:11:59 -05:00
Ryan Richard
5cd60fa5f9 Move starting/stopping impersonation proxy server to a new controller
- Watch a configmap to read the configuration of the impersonation
  proxy and reconcile it.
- Implements "auto" mode by querying the API for control plane nodes.
- WIP: does not create a load balancer or proper TLS certificates yet.
  Those will come in future commits.

Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-02-11 17:25:52 -08:00
Andrew Keesler
fac571b51a Merge pull request #410 from ankeesler/update-copyright
generated: include 2021 in copyright
2021-02-11 12:26:31 -05:00
Andrew Keesler
9b87906a30 Merge remote-tracking branch 'upstream/main' into impersonation-proxy
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-02-11 11:03:33 -05:00
Andrew Keesler
c8b1f00107 generated: include 2021 in copyright
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-02-11 10:52:01 -05:00
Ryan Richard
e4c49c37b9 Merge branch 'main' into impersonation-proxy 2021-02-09 13:45:37 -08:00
Ryan Richard
268ca5b7f6 Add config structs in impersonator package
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-02-09 13:44:19 -08:00
Andrew Keesler
8697488126 internal/concierge/impersonator: use kubeconfig from kubeclient
Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-02-09 15:28:56 -05:00
Margo Crawford
dfcc2a1eb8 Introduce clusterhost package to determine whether a cluster has control plane nodes
Also added hasExternalLoadBalancerProvider key to cluster capabilities
for integration testing.

Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-02-09 11:16:01 -08:00
Andrew Keesler
812f5084a1 internal/concierge/impersonator: don't mutate ServeHTTP() req
I added that test helper to create an http.Request since I wanted to properly
initialize the http.Request's context.

Signed-off-by: Andrew Keesler <akeesler@vmware.com>
2021-02-09 13:25:32 -05:00
Margo Crawford
6b46bae6c6 Fixed integration test compile failures after rebase 2021-02-03 11:32:29 -08:00
Margo Crawford
23e8c35918 Revert "CredentialIssuer contains Impersonation Proxy spec"
This reverts commit 83bbd1fa9314508030ea9fcf26c6720212d65dc0.
2021-02-03 09:37:39 -08:00
Margo Crawford
ab60396ac4 CredentialIssuer contains Impersonation Proxy spec 2021-02-03 09:37:39 -08:00
Margo Crawford
343c275f46 Path to ci bundle rather than the actual value for get kubeconfig
Also changed a function param to a pointer
2021-02-03 09:37:36 -08:00
Margo Crawford
12e41d783f Refactored execCredentialForImpersonationProxy to be shared 2021-02-03 09:34:48 -08:00
Margo Crawford
2f891b4bfb Add --concierge-use-impersonation-proxy to static login
- also renamed --use-impersonation-proxy to
--concierge-use-impersonation-proxy
2021-02-03 09:34:47 -08:00
Margo Crawford
170b86d0c6 Add happy path test for login oidc 2021-02-03 09:34:47 -08:00
Margo Crawford
07b7b743b4 Impersonation proxy cli arguments 2021-02-03 09:34:39 -08:00
Matt Moyer
64aff7b983 Only log user ID, not user name/groups.
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-02-03 09:31:30 -08:00
Matt Moyer
1299231a48 Add integration test for impersonation proxy.
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-02-03 09:31:30 -08:00
Margo Crawford
b6abb022f6 Add initial implementation of impersonation proxy.
Signed-off-by: Margo Crawford <margaretc@vmware.com>
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-02-03 09:31:13 -08:00
862 changed files with 33118 additions and 4937 deletions

4
.github/codecov.yml vendored
View File

@@ -2,7 +2,7 @@ codecov:
strict_yaml_branch: main
require_ci_to_pass: no
notify:
wait_for_ci: no
wait_for_ci: no
coverage:
status:
project:
@@ -11,3 +11,5 @@ coverage:
patch:
default:
informational: true
ignore:
- cmd/local-user-authenticator/

5
.gitignore vendored
View File

@@ -14,8 +14,11 @@
# Dependency directories (remove the comment below to include it)
# vendor/
# goland
# GoLand
.idea
# Intermediate files used by Tilt
/hack/lib/tilt/build
# MacOS Desktop Services Store
.DS_Store

View File

@@ -1,6 +1,8 @@
# https://github.com/golangci/golangci-lint#config-file
run:
deadline: 1m
skip-dirs:
- generated
linters:
disable-all: true

View File

@@ -1,4 +1,6 @@
exclude: '^(generated|hack/lib/tilt/tilt_modules)/'
# This is a configuration for https://pre-commit.com/.
# On macOS, try `brew install pre-commit` and then run `pre-commit install`.
exclude: '^(site|generated|hack/lib/tilt/tilt_modules)/'
repos:
- repo: git://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0

View File

@@ -1,9 +1,32 @@
# Pinniped Adopters
These organizations are using Pinniped.
If you're using Pinniped and want to add your organization to this
list, [follow these directions](#adding-your-organization-to-the-list-of-adopters)!
* [VMware Tanzu](https://tanzu.vmware.com/) ([Tanzu Mission Control](https://tanzu.vmware.com/mission-control))
## Organizations using Pinniped
If you are using Pinniped and are not on this list, you can open a [pull
request](https://github.com/vmware-tanzu/pinniped/issues/new?template=feature-proposal.md)
to add yourself.
<a href="https://tanzu.vmware.com/tanzu" border="0" target="_blank"><img alt="vmware-tanzu" src="site/themes/pinniped/static/img/vmware-tanzu.svg" height="50"></a>
<a href="https://kubeapps.com/" border="0" target="_blank"><img alt="kubeapps" src="site/themes/pinniped/static/img/kubeapps.svg" height="50"></a>
## Solutions built with Pinniped
Below is a list of solutions where Pinniped is being used as a component.
**[Kubeapps](https://kubeapps.com/)**
Kubeapps uses Pinniped to [enable SSO authentication](https://github.com/kubeapps/kubeapps/blob/master/docs/user/using-an-OIDC-provider-with-pinniped.md) when running on clusters where SSO cannot be configured for the cluster API server.
**[VMware Tanzu Kubernetes Grid (TKG)](https://tanzu.vmware.com/kubernetes-grid)**
TKG uses Pinniped to provide a seamless SSO experience across management and workload clusters.
**[VMware Tanzu Mission Control (TMC)](https://tanzu.vmware.com/mission-control)**
TMC uses Pinniped to provide a uniform authentication experience across all attached clusters.
## Adding your organization to the list of adopters
If you are using Pinniped and would like to be included in the list of Pinniped Adopters, add an SVG version of your logo that is less than 150 KB to
the [img directory](https://github.com/vmware-tanzu/pinniped/tree/main/site/themes/pinniped/static/img) in this repo and submit a pull request with your change including 1-2 sentences describing how your organization is using Pinniped. Name the image file something that
reflects your company (e.g., if your company is called Acme, name the image acme.svg). Please feel free to send us a message in [#pinniped](https://kubernetes.slack.com/archives/C01BW364RJA) with any questions you may have.

View File

@@ -8,7 +8,7 @@ Please see the [Code of Conduct](./CODE_OF_CONDUCT.md).
## Project Scope
Learn about the [scope](https://pinniped.dev/docs/scope/) of the project.
See [SCOPE.md](./SCOPE.md) for some guidelines about what we consider in and out of scope for Pinniped.
## Community Meetings
@@ -96,36 +96,29 @@ docker build .
- [`kapp`](https://carvel.dev/#getting-started)
- [`kind`](https://kind.sigs.k8s.io/docs/user/quick-start)
- [`kubectl`](https://kubernetes.io/docs/tasks/tools/install-kubectl/)
- [`tilt`](https://docs.tilt.dev/install.html)
- [`ytt`](https://carvel.dev/#getting-started)
On macOS, these tools can be installed with [Homebrew](https://brew.sh/) (assuming you have Chrome installed already):
```bash
brew install kind tilt-dev/tap/tilt k14s/tap/ytt k14s/tap/kapp kubectl chromedriver && brew cask install docker
brew install kind k14s/tap/ytt k14s/tap/kapp kubectl chromedriver && brew cask install docker
```
1. Create a local Kubernetes cluster using `kind`:
1. Create a kind cluster, compile, create container images, and install Pinniped and supporting dependencies using:
```bash
./hack/kind-up.sh
./hack/prepare-for-integration-tests.sh
```
1. Install Pinniped and supporting dependencies using `tilt`:
```bash
./hack/tilt-up.sh
```
Tilt will continue running and live-updating the Pinniped deployment whenever the code changes.
1. Run the Pinniped integration tests:
```bash
source /tmp/integration-test-env && go test -v -count 1 ./test/integration
source /tmp/integration-test-env && go test -v -count 1 -timeout 0 ./test/integration
```
To uninstall the test environment, run `./hack/tilt-down.sh`.
1. After making production code changes, recompile, redeploy, and run tests again by repeating the same
commands described above. If there are only test code changes, then simply run the tests again.
To destroy the local Kubernetes cluster, run `./hack/kind-down.sh`.
### Observing Tests on the Continuous Integration Environment

View File

@@ -3,7 +3,7 @@
# Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
FROM golang:1.15.8 as build-env
FROM golang:1.16.2 as build-env
WORKDIR /work
COPY . .
@@ -27,11 +27,11 @@ RUN \
./cmd/local-user-authenticator/...
# Use a Debian slim image to grab a reasonable default CA bundle.
FROM debian:10.8-slim AS get-ca-bundle-env
FROM debian:10.9-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 runtime image based on Debian slim.
FROM debian:10.8-slim
FROM debian:10.9-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.

View File

@@ -10,7 +10,7 @@ install procedure across all types and origins of Kubernetes clusters,
declarative configuration via Kubernetes APIs, enterprise-grade integrations
with IDPs, and distribution-specific integration strategies.
### Example Use Cases
### Example use cases
* Your team uses a large enterprise IDP, and has many clusters that they
manage. Pinniped provides:
@@ -39,17 +39,15 @@ The Pinniped Concierge can be configured to hook into the Pinniped Supervisor's
federated credentials, or it can authenticate users directly via external IDP
credentials.
To learn more, see [architecture](https://pinniped.dev/docs/architecture/).
To learn more, see [architecture](https://pinniped.dev/docs/background/architecture/).
<img src="site/content/docs/img/pinniped_architecture_concierge_supervisor.svg" alt="Pinniped Architecture Sketch"/>
## Trying Pinniped
## Getting started with Pinniped
Care to kick the tires? It's easy to [install and try Pinniped](https://pinniped.dev/docs/demo/).
## Community Meetings
## Community meetings
Pinniped is better because of our contributors and maintainers. It is because of you that we can bring great software to the community. Please join us during our online community meetings, occuring every first and third Thursday of the month at 9AM PT / 12PM PT. Use [this Zoom Link](https://vmware.zoom.us/j/93798188973?pwd=T3pIMWxReEQvcWljNm1admRoZTFSZz09) to attend and add any agenda items you wish to discuss to [the notes document](https://hackmd.io/rd_kVJhjQfOvfAWzK8A3tQ?view). Join our [Google Group](https://groups.google.com/u/1/g/project-pinniped) to receive invites to this meeting.
Pinniped is better because of our contributors and maintainers. It is because of you that we can bring great software to the community. Please join us during our online community meetings, occurring every first and third Thursday of the month at 9 AM PT / 12 PM PT. Use [this Zoom Link](https://vmware.zoom.us/j/93798188973?pwd=T3pIMWxReEQvcWljNm1admRoZTFSZz09) to attend and add any agenda items you wish to discuss to [the notes document](https://hackmd.io/rd_kVJhjQfOvfAWzK8A3tQ?view). Join our [Google Group](https://groups.google.com/g/project-pinniped) to receive invites to this meeting.
If the meeting day falls on a US holiday, please consider that occurrence of the meeting to be canceled.
@@ -61,7 +59,7 @@ Got a question, comment, or idea? Please don't hesitate to reach out via the Git
Contributions are welcome. Before contributing, please see the [contributing guide](CONTRIBUTING.md).
## Reporting Security Vulnerabilities
## Reporting security vulnerabilities
Please follow the procedure described in [SECURITY.md](SECURITY.md).

50
ROADMAP.md Normal file
View File

@@ -0,0 +1,50 @@
## **Pinniped Project Roadmap**
###
**About this document**
This document provides a link to the[ Pinniped Project issues](https://github.com/vmware-tanzu/pinniped/issues) list that serves as the up to date description of items that are in the Pinniped release pipeline. Most items are gathered from the community or include a feedback loop with the community. This should serve as a reference point for Pinniped users and contributors to understand where the project is heading, and help determine if a contribution could be conflicting with a longer term plan.
###
**How to help?**
Discussion on the roadmap can take place in threads under [Issues](https://github.com/vmware-tanzu/pinniped/issues) or in [community meetings](https://github.com/vmware-tanzu/pinniped/blob/main/CONTRIBUTING.md#meeting-with-the-maintainers). Please open and comment on an issue if you want to provide suggestions and feedback to an item in the roadmap. Please review the roadmap to avoid potential duplicated effort.
###
**Need an idea for a contribution?**
Weve created an [Opportunity Areas](https://github.com/vmware-tanzu/pinniped/discussions/483) discussion thread that outlines some areas we believe are excellent starting points for the community to get involved. In that discussion weve included specific work items that one might consider that also support the high-level items presented in our roadmap.
###
**How to add an item to the roadmap?**
Please open an issue to track any initiative on the roadmap of Pinniped (usually driven by new feature requests). We will work with and rely on our community to focus our efforts to improve Pinniped.
###
**Current Roadmap**
The following table includes the current roadmap for Pinniped. If you have any questions or would like to contribute to Pinniped, please attend a [community meeting](https://github.com/vmware-tanzu/pinniped/blob/main/CONTRIBUTING.md#meeting-with-the-maintainers) to discuss with our team. If you don't know where to start, we are always looking for contributors that will help us reduce technical, automation, and documentation debt. Please take the timelines & dates as proposals and goals. Priorities and requirements change based on community feedback, roadblocks encountered, community contributions, etc. If you depend on a specific item, we encourage you to attend community meetings to get updated status information, or help us deliver that feature by contributing to Pinniped.
Last Updated: March 2021
Theme|Description|Timeline|
|--|--|--|
|Impersonation Proxy|Adds support for more types of clusters (managed services)|Mar 2021|
|LDAP Support|Extends upstream IDP protocols|Apr 2021|
|Device Code Flow|Add support for OAuth 2.0 Device Authorization Grant in the Pinniped CLI and Supervisor|Apr 2021|
|Improved Documentation|Reorganizing and improving Pinniped docs; new how-to guides and tutorials|May 2021|
|CLI Improvements|Improving CLI UX for setting up Supervisor IDPs|May 2021|
|Multiple IDPs|Support for multiple upstream IDPs to be configured simultaneously|Jun 2021|
|Improving Security Posture|Offer the best security posture for Kubernetes cluster authentication|Exploring/Ongoing|
|Improve our CI/CD systems|Upgrade tests; make Kind more efficient and reliable for CI ; Windows tests; performance tests; scale tests; soak tests|Exploring/Ongoing|
|Telemetry|Adding some useful phone home metrics as well as some vanity metrics|Exploring/Ongoing|
|Observability|Expose Pinniped metrics through Prometheus Integration|Exploring/Ongoing|

32
SCOPE.md Normal file
View File

@@ -0,0 +1,32 @@
# Project Scope
The Pinniped project is guided by the following principles.
- Pinniped lets you plug any external identity providers into Kubernetes.
These integrations follow enterprise-grade security principles.
- Pinniped is easy to install and use on any Kubernetes cluster via distribution-specific integration mechanisms.
- Pinniped uses a declarative configuration via Kubernetes APIs.
- Pinniped provides optimal user experience when authenticating to many clusters at one time.
- Pinniped provides enterprise-grade security posture via secure defaults and revocable or very short-lived credentials.
- Where possible, Pinniped will contribute ideas and code to upstream Kubernetes.
When contributing to Pinniped, please consider whether your contribution follows
these guiding principles.
## Out Of Scope
The following items are out of scope for the Pinniped project.
- Authorization.
- Standalone identity provider for general use.
- Machine-to-machine (service) identity.
- Running outside of Kubernetes.
## Roadmap
See our [open milestones][milestones] and the [`priority/backlog` label][backlog] for an idea about what's next on our roadmap.
For more details on proposing features and bugs, check out our [contributing](./CONTRIBUTING.md) doc.
[milestones]: https://github.com/vmware-tanzu/pinniped/milestones
[backlog]: https://github.com/vmware-tanzu/pinniped/labels/priority%2Fbacklog

View File

@@ -1,27 +1,39 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// +kubebuilder:validation:Enum=KubeClusterSigningCertificate
// +kubebuilder:validation:Enum=KubeClusterSigningCertificate;ImpersonationProxy
type StrategyType string
// +kubebuilder:validation:Enum=TokenCredentialRequestAPI;ImpersonationProxy
type FrontendType string
// +kubebuilder:validation:Enum=Success;Error
type StrategyStatus string
// +kubebuilder:validation:Enum=FetchedKey;CouldNotFetchKey
// +kubebuilder:validation:Enum=Listening;Pending;Disabled;ErrorDuringSetup;CouldNotFetchKey;CouldNotGetClusterInfo;FetchedKey
type StrategyReason string
const (
KubeClusterSigningCertificateStrategyType = StrategyType("KubeClusterSigningCertificate")
ImpersonationProxyStrategyType = StrategyType("ImpersonationProxy")
TokenCredentialRequestAPIFrontendType = FrontendType("TokenCredentialRequestAPI")
ImpersonationProxyFrontendType = FrontendType("ImpersonationProxy")
SuccessStrategyStatus = StrategyStatus("Success")
ErrorStrategyStatus = StrategyStatus("Error")
CouldNotFetchKeyStrategyReason = StrategyReason("CouldNotFetchKey")
FetchedKeyStrategyReason = StrategyReason("FetchedKey")
ListeningStrategyReason = StrategyReason("Listening")
PendingStrategyReason = StrategyReason("Pending")
DisabledStrategyReason = StrategyReason("Disabled")
ErrorDuringSetupStrategyReason = StrategyReason("ErrorDuringSetup")
CouldNotFetchKeyStrategyReason = StrategyReason("CouldNotFetchKey")
CouldNotGetClusterInfoStrategyReason = StrategyReason("CouldNotGetClusterInfo")
FetchedKeyStrategyReason = StrategyReason("FetchedKey")
)
// Status of a credential issuer.
@@ -30,6 +42,7 @@ type CredentialIssuerStatus struct {
Strategies []CredentialIssuerStrategy `json:"strategies"`
// Information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
// This field is deprecated and will be removed in a future version.
// +optional
KubeConfigInfo *CredentialIssuerKubeConfigInfo `json:"kubeConfigInfo,omitempty"`
}
@@ -63,6 +76,46 @@ type CredentialIssuerStrategy struct {
// When the status was last checked.
LastUpdateTime metav1.Time `json:"lastUpdateTime"`
// Frontend describes how clients can connect using this strategy.
Frontend *CredentialIssuerFrontend `json:"frontend,omitempty"`
}
type CredentialIssuerFrontend struct {
// Type describes which frontend mechanism clients can use with a strategy.
Type FrontendType `json:"type"`
// TokenCredentialRequestAPIInfo describes the parameters for the TokenCredentialRequest API on this Concierge.
// This field is only set when Type is "TokenCredentialRequestAPI".
TokenCredentialRequestAPIInfo *TokenCredentialRequestAPIInfo `json:"tokenCredentialRequestInfo,omitempty"`
// ImpersonationProxyInfo describes the parameters for the impersonation proxy on this Concierge.
// This field is only set when Type is "ImpersonationProxy".
ImpersonationProxyInfo *ImpersonationProxyInfo `json:"impersonationProxyInfo,omitempty"`
}
// TokenCredentialRequestAPIInfo describes the parameters for the TokenCredentialRequest API on this Concierge.
type TokenCredentialRequestAPIInfo struct {
// Server is the Kubernetes API server URL.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^https://|^http://`
Server string `json:"server"`
// CertificateAuthorityData is the base64-encoded Kubernetes API server CA bundle.
// +kubebuilder:validation:MinLength=1
CertificateAuthorityData string `json:"certificateAuthorityData"`
}
// ImpersonationProxyInfo describes the parameters for the impersonation proxy on this Concierge.
type ImpersonationProxyInfo struct {
// Endpoint is the HTTPS endpoint of the impersonation proxy.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^https://`
Endpoint string `json:"endpoint"`
// CertificateAuthorityData is the base64-encoded PEM CA bundle of the impersonation proxy.
// +kubebuilder:validation:MinLength=1
CertificateAuthorityData string `json:"certificateAuthorityData"`
}
// Describes the configuration status of a Pinniped credential issuer.

View File

@@ -0,0 +1,8 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// +k8s:deepcopy-gen=package
// +groupName=identity.concierge.pinniped.dev
// Package identity is the internal version of the Pinniped identity API.
package identity

View File

@@ -0,0 +1,38 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package identity
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const GroupName = "identity.concierge.pinniped.dev"
// SchemeGroupVersion is group version used to register these objects.
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind.
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns back a Group qualified GroupResource.
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to the given scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&WhoAmIRequest{},
&WhoAmIRequestList{},
)
return nil
}

View File

@@ -0,0 +1,37 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package identity
import "fmt"
// KubernetesUserInfo represents the current authenticated user, exactly as Kubernetes understands it.
// Copied from the Kubernetes token review API.
type KubernetesUserInfo struct {
// User is the UserInfo associated with the current user.
User UserInfo
// Audiences are audience identifiers chosen by the authenticator.
Audiences []string
}
// UserInfo holds the information about the user needed to implement the
// user.Info interface.
type UserInfo struct {
// The name that uniquely identifies this user among all active users.
Username string
// A unique value that identifies this user across time. If this user is
// deleted and another user by the same name is added, they will have
// different UIDs.
UID string
// The names of groups this user is a part of.
Groups []string
// Any additional information provided by the authenticator.
Extra map[string]ExtraValue
}
// ExtraValue masks the value so protobuf can generate
type ExtraValue []string
func (t ExtraValue) String() string {
return fmt.Sprintf("%v", []string(t))
}

View File

@@ -0,0 +1,40 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package identity
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// WhoAmIRequest submits a request to echo back the current authenticated user.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type WhoAmIRequest struct {
metav1.TypeMeta
metav1.ObjectMeta
Spec WhoAmIRequestSpec
Status WhoAmIRequestStatus
}
type WhoAmIRequestSpec struct {
// empty for now but we may add some config here in the future
// any such config must be safe in the context of an unauthenticated user
}
type WhoAmIRequestStatus struct {
// The current authenticated user, exactly as Kubernetes understands it.
KubernetesUserInfo KubernetesUserInfo
// We may add concierge specific information here in the future.
}
// WhoAmIRequestList is a list of WhoAmIRequest objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type WhoAmIRequestList struct {
metav1.TypeMeta
metav1.ListMeta
// Items is a list of WhoAmIRequest
Items []WhoAmIRequest
}

View File

@@ -0,0 +1,4 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1

View File

@@ -0,0 +1,12 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime"
)
func addDefaultingFuncs(scheme *runtime.Scheme) error {
return RegisterDefaults(scheme)
}

View File

@@ -0,0 +1,11 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// +k8s:openapi-gen=true
// +k8s:deepcopy-gen=package
// +k8s:conversion-gen=go.pinniped.dev/GENERATED_PKG/apis/concierge/identity
// +k8s:defaulter-gen=TypeMeta
// +groupName=identity.concierge.pinniped.dev
// Package v1alpha1 is the v1alpha1 version of the Pinniped identity API.
package v1alpha1

View File

@@ -0,0 +1,43 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const GroupName = "identity.concierge.pinniped.dev"
// SchemeGroupVersion is group version used to register these objects.
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
var (
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
func init() {
// We only register manually written functions here. The registration of the
// generated functions takes place in the generated files. The separation
// makes the code compile even when the generated files are missing.
localSchemeBuilder.Register(addKnownTypes, addDefaultingFuncs)
}
// Adds the list of known types to the given scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&WhoAmIRequest{},
&WhoAmIRequestList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource.
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}

View File

@@ -0,0 +1,41 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import "fmt"
// KubernetesUserInfo represents the current authenticated user, exactly as Kubernetes understands it.
// Copied from the Kubernetes token review API.
type KubernetesUserInfo struct {
// User is the UserInfo associated with the current user.
User UserInfo `json:"user"`
// Audiences are audience identifiers chosen by the authenticator.
// +optional
Audiences []string `json:"audiences,omitempty"`
}
// UserInfo holds the information about the user needed to implement the
// user.Info interface.
type UserInfo struct {
// The name that uniquely identifies this user among all active users.
Username string `json:"username"`
// A unique value that identifies this user across time. If this user is
// deleted and another user by the same name is added, they will have
// different UIDs.
// +optional
UID string `json:"uid,omitempty"`
// The names of groups this user is a part of.
// +optional
Groups []string `json:"groups,omitempty"`
// Any additional information provided by the authenticator.
// +optional
Extra map[string]ExtraValue `json:"extra,omitempty"`
}
// ExtraValue masks the value so protobuf can generate
type ExtraValue []string
func (t ExtraValue) String() string {
return fmt.Sprintf("%v", []string(t))
}

View File

@@ -0,0 +1,43 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// WhoAmIRequest submits a request to echo back the current authenticated user.
// +genclient
// +genclient:nonNamespaced
// +genclient:onlyVerbs=create
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type WhoAmIRequest struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec WhoAmIRequestSpec `json:"spec,omitempty"`
Status WhoAmIRequestStatus `json:"status,omitempty"`
}
type WhoAmIRequestSpec struct {
// empty for now but we may add some config here in the future
// any such config must be safe in the context of an unauthenticated user
}
type WhoAmIRequestStatus struct {
// The current authenticated user, exactly as Kubernetes understands it.
KubernetesUserInfo KubernetesUserInfo `json:"kubernetesUserInfo"`
// We may add concierge specific information here in the future.
}
// WhoAmIRequestList is a list of WhoAmIRequest objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type WhoAmIRequestList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
// Items is a list of WhoAmIRequest
Items []WhoAmIRequest `json:"items"`
}

View File

@@ -0,0 +1,14 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package validation
import (
"k8s.io/apimachinery/pkg/util/validation/field"
identityapi "go.pinniped.dev/GENERATED_PKG/apis/concierge/identity"
)
func ValidateWhoAmIRequest(whoAmIRequest *identityapi.WhoAmIRequest) field.ErrorList {
return nil // add validation for spec here if we expand it
}

View File

@@ -31,6 +31,7 @@ type TokenCredentialRequestStatus struct {
// TokenCredentialRequest submits an IDP-specific credential to Pinniped in exchange for a cluster-specific credential.
// +genclient
// +genclient:nonNamespaced
// +genclient:onlyVerbs=create
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type TokenCredentialRequest struct {
metav1.TypeMeta `json:",inline"`

View File

@@ -54,12 +54,12 @@ const (
)
type webhook struct {
certProvider dynamiccert.Provider
certProvider dynamiccert.Private
secretInformer corev1informers.SecretInformer
}
func newWebhook(
certProvider dynamiccert.Provider,
certProvider dynamiccert.Private,
secretInformer corev1informers.SecretInformer,
) *webhook {
return &webhook{
@@ -281,7 +281,7 @@ func respondWithAuthenticated(
func startControllers(
ctx context.Context,
dynamicCertProvider dynamiccert.Provider,
dynamicCertProvider dynamiccert.Private,
kubeClient kubernetes.Interface,
kubeInformers kubeinformers.SharedInformerFactory,
) {
@@ -328,7 +328,7 @@ func startControllers(
func startWebhook(
ctx context.Context,
l net.Listener,
dynamicCertProvider dynamiccert.Provider,
dynamicCertProvider dynamiccert.Private,
secretInformer corev1informers.SecretInformer,
) error {
return newWebhook(dynamicCertProvider, secretInformer).start(ctx, l)
@@ -355,7 +355,7 @@ func run() error {
kubeinformers.WithNamespace(namespace),
)
dynamicCertProvider := dynamiccert.New()
dynamicCertProvider := dynamiccert.NewServingCert("local-user-authenticator-tls-serving-certificate")
startControllers(ctx, dynamicCertProvider, client.Kubernetes, kubeInformers)
plog.Debug("controllers are ready")

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package main
@@ -8,7 +8,6 @@ import (
"context"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/json"
"fmt"
"io"
@@ -99,7 +98,7 @@ func TestWebhook(t *testing.T) {
},
}))
secretInformer := createSecretInformer(t, kubeClient)
secretInformer := createSecretInformer(ctx, t, kubeClient)
certProvider, caBundle, serverName := newCertProvider(t)
w := newWebhook(certProvider, secretInformer)
@@ -437,7 +436,7 @@ func TestWebhook(t *testing.T) {
}
}
func createSecretInformer(t *testing.T, kubeClient kubernetes.Interface) corev1informers.SecretInformer {
func createSecretInformer(ctx context.Context, t *testing.T, kubeClient kubernetes.Interface) corev1informers.SecretInformer {
t.Helper()
kubeInformers := kubeinformers.NewSharedInformerFactory(kubeClient, 0)
@@ -448,9 +447,6 @@ func createSecretInformer(t *testing.T, kubeClient kubernetes.Interface) corev1i
// informer factory before syncing it.
secretInformer.Informer()
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
kubeInformers.Start(ctx.Done())
informerTypesSynced := kubeInformers.WaitForCacheSync(ctx.Done())
@@ -462,22 +458,23 @@ func createSecretInformer(t *testing.T, kubeClient kubernetes.Interface) corev1i
// newClientProvider returns a dynamiccert.Provider configured
// with valid serving cert, the CA bundle that can be used to verify the serving
// cert, and the server name that can be used to verify the TLS peer.
func newCertProvider(t *testing.T) (dynamiccert.Provider, []byte, string) {
func newCertProvider(t *testing.T) (dynamiccert.Private, []byte, string) {
t.Helper()
serverName := "local-user-authenticator"
ca, err := certauthority.New(pkix.Name{CommonName: serverName + " CA"}, time.Hour*24)
ca, err := certauthority.New(serverName+" CA", time.Hour*24)
require.NoError(t, err)
cert, err := ca.Issue(pkix.Name{CommonName: serverName}, []string{serverName}, nil, time.Hour*24)
cert, err := ca.IssueServerCert([]string{serverName}, nil, time.Hour*24)
require.NoError(t, err)
certPEM, keyPEM, err := certauthority.ToPEM(cert)
require.NoError(t, err)
certProvider := dynamiccert.New()
certProvider.Set(certPEM, keyPEM)
certProvider := dynamiccert.NewServingCert(t.Name())
err = certProvider.SetCertKeyContent(certPEM, keyPEM)
require.NoError(t, err)
return certProvider, ca.Bundle(), serverName
}

View File

@@ -26,9 +26,9 @@ import (
"k8s.io/klog/v2"
"k8s.io/klog/v2/klogr"
configv1alpha1 "go.pinniped.dev/generated/1.20/apis/supervisor/config/v1alpha1"
pinnipedclientset "go.pinniped.dev/generated/1.20/client/supervisor/clientset/versioned"
pinnipedinformers "go.pinniped.dev/generated/1.20/client/supervisor/informers/externalversions"
configv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1"
pinnipedclientset "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned"
pinnipedinformers "go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions"
"go.pinniped.dev/internal/config/supervisor"
"go.pinniped.dev/internal/controller/supervisorconfig"
"go.pinniped.dev/internal/controller/supervisorconfig/generator"

View File

@@ -0,0 +1,106 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"bytes"
"crypto/x509"
"flag"
"fmt"
"io/ioutil"
"strings"
"github.com/spf13/pflag"
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
)
// conciergeModeFlag represents the method by which we should connect to the Concierge on a cluster during login.
// this is meant to be a valid flag.Value implementation.
type conciergeModeFlag int
var _ flag.Value = new(conciergeModeFlag)
const (
modeUnknown conciergeModeFlag = iota
modeTokenCredentialRequestAPI
modeImpersonationProxy
)
func (f *conciergeModeFlag) String() string {
switch *f {
case modeImpersonationProxy:
return "ImpersonationProxy"
case modeTokenCredentialRequestAPI:
return "TokenCredentialRequestAPI"
case modeUnknown:
fallthrough
default:
return "TokenCredentialRequestAPI"
}
}
func (f *conciergeModeFlag) Set(s string) error {
if strings.EqualFold(s, "") {
*f = modeUnknown
return nil
}
if strings.EqualFold(s, "TokenCredentialRequestAPI") {
*f = modeTokenCredentialRequestAPI
return nil
}
if strings.EqualFold(s, "ImpersonationProxy") {
*f = modeImpersonationProxy
return nil
}
return fmt.Errorf("invalid mode %q, valid modes are TokenCredentialRequestAPI and ImpersonationProxy", s)
}
func (f *conciergeModeFlag) Type() string {
return "mode"
}
// MatchesFrontend returns true iff the flag matches the type of the provided frontend.
func (f *conciergeModeFlag) MatchesFrontend(frontend *configv1alpha1.CredentialIssuerFrontend) bool {
switch *f {
case modeImpersonationProxy:
return frontend.Type == configv1alpha1.ImpersonationProxyFrontendType
case modeTokenCredentialRequestAPI:
return frontend.Type == configv1alpha1.TokenCredentialRequestAPIFrontendType
case modeUnknown:
fallthrough
default:
return true
}
}
// caBundlePathsVar represents a list of CA bundle paths, which load from disk when the flag is populated.
type caBundleFlag []byte
var _ pflag.Value = new(caBundleFlag)
func (f *caBundleFlag) String() string {
return string(*f)
}
func (f *caBundleFlag) Set(path string) error {
pem, err := ioutil.ReadFile(path)
if err != nil {
return fmt.Errorf("could not read CA bundle path: %w", err)
}
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(pem) {
return fmt.Errorf("failed to load any CA certificates from %q", path)
}
if len(*f) == 0 {
*f = pem
return nil
}
*f = bytes.Join([][]byte{*f, pem}, []byte("\n"))
return nil
}
func (f *caBundleFlag) Type() string {
return "path"
}

View File

@@ -0,0 +1,73 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"bytes"
"fmt"
"io/ioutil"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/require"
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
"go.pinniped.dev/internal/certauthority"
"go.pinniped.dev/internal/testutil"
)
func TestConciergeModeFlag(t *testing.T) {
var f conciergeModeFlag
require.Equal(t, "mode", f.Type())
require.Equal(t, modeUnknown, f)
require.NoError(t, f.Set(""))
require.Equal(t, modeUnknown, f)
require.EqualError(t, f.Set("foo"), `invalid mode "foo", valid modes are TokenCredentialRequestAPI and ImpersonationProxy`)
require.True(t, f.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.TokenCredentialRequestAPIFrontendType}))
require.True(t, f.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.ImpersonationProxyFrontendType}))
require.NoError(t, f.Set("TokenCredentialRequestAPI"))
require.Equal(t, modeTokenCredentialRequestAPI, f)
require.Equal(t, "TokenCredentialRequestAPI", f.String())
require.True(t, f.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.TokenCredentialRequestAPIFrontendType}))
require.False(t, f.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.ImpersonationProxyFrontendType}))
require.NoError(t, f.Set("tokencredentialrequestapi"))
require.Equal(t, modeTokenCredentialRequestAPI, f)
require.Equal(t, "TokenCredentialRequestAPI", f.String())
require.NoError(t, f.Set("ImpersonationProxy"))
require.Equal(t, modeImpersonationProxy, f)
require.Equal(t, "ImpersonationProxy", f.String())
require.False(t, f.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.TokenCredentialRequestAPIFrontendType}))
require.True(t, f.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.ImpersonationProxyFrontendType}))
require.NoError(t, f.Set("impersonationproxy"))
require.Equal(t, modeImpersonationProxy, f)
require.Equal(t, "ImpersonationProxy", f.String())
}
func TestCABundleFlag(t *testing.T) {
testCA, err := certauthority.New("Test CA", 1*time.Hour)
require.NoError(t, err)
tmpdir := testutil.TempDir(t)
emptyFilePath := filepath.Join(tmpdir, "empty")
require.NoError(t, ioutil.WriteFile(emptyFilePath, []byte{}, 0600))
testCAPath := filepath.Join(tmpdir, "testca.pem")
require.NoError(t, ioutil.WriteFile(testCAPath, testCA.Bundle(), 0600))
f := caBundleFlag{}
require.Equal(t, "path", f.Type())
require.Equal(t, "", f.String())
require.EqualError(t, f.Set("./does/not/exist"), "could not read CA bundle path: open ./does/not/exist: no such file or directory")
require.EqualError(t, f.Set(emptyFilePath), fmt.Sprintf("failed to load any CA certificates from %q", emptyFilePath))
require.NoError(t, f.Set(testCAPath))
require.Equal(t, 1, bytes.Count(f, []byte("BEGIN CERTIFICATE")))
require.NoError(t, f.Set(testCAPath))
require.Equal(t, 2, bytes.Count(f, []byte("BEGIN CERTIFICATE")))
}

View File

@@ -0,0 +1,114 @@
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"bufio"
"bytes"
"fmt"
"io"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
)
//nolint: gochecknoinits
func init() {
rootCmd.AddCommand(generateMarkdownHelpCommand())
}
func generateMarkdownHelpCommand() *cobra.Command {
return &cobra.Command{
Args: cobra.NoArgs,
Use: "generate-markdown-help",
Short: "Generate markdown help for the current set of non-hidden CLI commands",
SilenceUsage: true,
Hidden: true,
RunE: runGenerateMarkdownHelp,
}
}
func runGenerateMarkdownHelp(cmd *cobra.Command, _ []string) error {
var generated bytes.Buffer
if err := generate(&generated); err != nil {
return err
}
if err := write(cmd.OutOrStdout(), &generated, "###### Auto generated by spf13/cobra"); err != nil {
return err
}
return nil
}
func generate(w io.Writer) error {
if err := generateHeader(w); err != nil {
return err
}
if err := generateCommand(w, rootCmd); err != nil {
return err
}
return nil
}
func generateHeader(w io.Writer) error {
_, err := fmt.Fprintf(w, `---
title: Command-Line Options Reference
description: Reference for the `+"`pinniped`"+` command-line tool
cascade:
layout: docs
menu:
docs:
name: Command-Line Options
weight: 30
parent: reference
---
`)
return err
}
func generateCommand(w io.Writer, command *cobra.Command) error {
for _, command := range command.Commands() {
// if this node is hidden, don't traverse it or its descendents
if command.Hidden {
continue
}
// generate children
if err := generateCommand(w, command); err != nil {
return err
}
// generate self, but only if we are a command that people would run to do something interesting
if command.Run != nil || command.RunE != nil {
if err := doc.GenMarkdownCustom(command, w, func(_ string) string { return "" }); err != nil {
return err
}
}
}
return nil
}
func write(w io.Writer, r io.Reader, unwantedPrefixes ...string) error {
s := bufio.NewScanner(r)
for s.Scan() {
line := s.Text()
if !containsPrefix(line, unwantedPrefixes) {
if _, err := fmt.Fprintln(w, line); err != nil {
return err
}
}
}
return s.Err()
}
func containsPrefix(s string, prefixes []string) bool {
for _, prefix := range prefixes {
if strings.HasPrefix(s, prefix) {
return true
}
}
return false
}

View File

@@ -0,0 +1,43 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"k8s.io/client-go/tools/clientcmd"
conciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
"go.pinniped.dev/internal/groupsuffix"
"go.pinniped.dev/internal/kubeclient"
)
// getConciergeClientsetFunc is a function that can return a clientset for the Concierge API given a
// clientConfig and the apiGroupSuffix with which the API is running.
type getConciergeClientsetFunc func(clientConfig clientcmd.ClientConfig, apiGroupSuffix string) (conciergeclientset.Interface, error)
// getRealConciergeClientset returns a real implementation of a conciergeclientset.Interface.
func getRealConciergeClientset(clientConfig clientcmd.ClientConfig, apiGroupSuffix string) (conciergeclientset.Interface, error) {
restConfig, err := clientConfig.ClientConfig()
if err != nil {
return nil, err
}
client, err := kubeclient.New(
kubeclient.WithConfig(restConfig),
kubeclient.WithMiddleware(groupsuffix.New(apiGroupSuffix)),
)
if err != nil {
return nil, err
}
return client.PinnipedConcierge, nil
}
// newClientConfig returns a clientcmd.ClientConfig given an optional kubeconfig path override and
// an optional context override.
func newClientConfig(kubeconfigPathOverride string, currentContextName string) clientcmd.ClientConfig {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
loadingRules.ExplicitPath = kubeconfigPathOverride
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{
CurrentContext: currentContextName,
})
return clientConfig
}

View File

@@ -4,54 +4,46 @@
package cmd
import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"strings"
"time"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/go-logr/logr"
"github.com/go-logr/stdr"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
_ "k8s.io/client-go/plugin/pkg/client/auth" // Adds handlers for various dynamic auth plugins in client-go
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
_ "k8s.io/client-go/plugin/pkg/client/auth" // Adds handlers for various dynamic auth plugins in client-go
conciergev1alpha1 "go.pinniped.dev/generated/1.20/apis/concierge/authentication/v1alpha1"
conciergeclientset "go.pinniped.dev/generated/1.20/client/concierge/clientset/versioned"
conciergev1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
conciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
"go.pinniped.dev/internal/groupsuffix"
"go.pinniped.dev/internal/kubeclient"
)
type kubeconfigDeps struct {
getPathToSelf func() (string, error)
getClientset func(clientConfig clientcmd.ClientConfig, apiGroupSuffix string) (conciergeclientset.Interface, error)
getClientset getConciergeClientsetFunc
log logr.Logger
}
func kubeconfigRealDeps() kubeconfigDeps {
return kubeconfigDeps{
getPathToSelf: os.Executable,
getClientset: func(clientConfig clientcmd.ClientConfig, apiGroupSuffix string) (conciergeclientset.Interface, error) {
restConfig, err := clientConfig.ClientConfig()
if err != nil {
return nil, err
}
client, err := kubeclient.New(
kubeclient.WithConfig(restConfig),
kubeclient.WithMiddleware(groupsuffix.New(apiGroupSuffix)),
)
if err != nil {
return nil, err
}
return client.PinnipedConcierge, nil
},
getClientset: getRealConciergeClientset,
log: stdr.New(log.New(os.Stderr, "", 0)),
}
}
@@ -68,20 +60,28 @@ type getKubeconfigOIDCParams struct {
skipBrowser bool
sessionCachePath string
debugSessionCache bool
caBundlePaths []string
caBundle caBundleFlag
requestAudience string
}
type getKubeconfigConciergeParams struct {
disabled bool
credentialIssuer string
authenticatorName string
authenticatorType string
apiGroupSuffix string
caBundle caBundleFlag
endpoint string
mode conciergeModeFlag
skipWait bool
}
type getKubeconfigParams struct {
kubeconfigPath string
kubeconfigContextOverride string
skipValidate bool
timeout time.Duration
outputPath string
staticToken string
staticTokenEnvName string
oidc getKubeconfigOIDCParams
@@ -104,11 +104,17 @@ func kubeconfigCommand(deps kubeconfigDeps) *cobra.Command {
f.StringVar(&flags.staticToken, "static-token", "", "Instead of doing an OIDC-based login, specify a static token")
f.StringVar(&flags.staticTokenEnvName, "static-token-env", "", "Instead of doing an OIDC-based login, read a static token from the environment")
f.BoolVar(&flags.concierge.disabled, "no-concierge", false, "Generate a configuration which does not use the concierge, but sends the credential to the cluster directly")
f.StringVar(&namespace, "concierge-namespace", "pinniped-concierge", "Namespace in which the concierge was installed")
f.BoolVar(&flags.concierge.disabled, "no-concierge", false, "Generate a configuration which does not use the Concierge, but sends the credential to the cluster directly")
f.StringVar(&namespace, "concierge-namespace", "pinniped-concierge", "Namespace in which the Concierge was installed")
f.StringVar(&flags.concierge.credentialIssuer, "concierge-credential-issuer", "", "Concierge CredentialIssuer object to use for autodiscovery (default: autodiscover)")
f.StringVar(&flags.concierge.authenticatorType, "concierge-authenticator-type", "", "Concierge authenticator type (e.g., 'webhook', 'jwt') (default: autodiscover)")
f.StringVar(&flags.concierge.authenticatorName, "concierge-authenticator-name", "", "Concierge authenticator name (default: autodiscover)")
f.StringVar(&flags.concierge.apiGroupSuffix, "concierge-api-group-suffix", "pinniped.dev", "Concierge API group suffix")
f.StringVar(&flags.concierge.apiGroupSuffix, "concierge-api-group-suffix", groupsuffix.PinnipedDefaultSuffix, "Concierge API group suffix")
f.BoolVar(&flags.concierge.skipWait, "concierge-skip-wait", false, "Skip waiting for any pending Concierge strategies to become ready (default: false)")
f.Var(&flags.concierge.caBundle, "concierge-ca-bundle", "Path to TLS certificate authority bundle (PEM format, optional, can be repeated) to use when connecting to the Concierge")
f.StringVar(&flags.concierge.endpoint, "concierge-endpoint", "", "API base for the Concierge endpoint")
f.Var(&flags.concierge.mode, "concierge-mode", "Concierge mode of operation")
f.StringVar(&flags.oidc.issuer, "oidc-issuer", "", "OpenID Connect issuer URL (default: autodiscover)")
f.StringVar(&flags.oidc.clientID, "oidc-client-id", "pinniped-cli", "OpenID Connect client ID (default: autodiscover)")
@@ -116,26 +122,42 @@ func kubeconfigCommand(deps kubeconfigDeps) *cobra.Command {
f.StringSliceVar(&flags.oidc.scopes, "oidc-scopes", []string{oidc.ScopeOfflineAccess, oidc.ScopeOpenID, "pinniped:request-audience"}, "OpenID Connect scopes to request during login")
f.BoolVar(&flags.oidc.skipBrowser, "oidc-skip-browser", false, "During OpenID Connect login, skip opening the browser (just print the URL)")
f.StringVar(&flags.oidc.sessionCachePath, "oidc-session-cache", "", "Path to OpenID Connect session cache file")
f.StringSliceVar(&flags.oidc.caBundlePaths, "oidc-ca-bundle", nil, "Path to TLS certificate authority bundle (PEM format, optional, can be repeated)")
f.Var(&flags.oidc.caBundle, "oidc-ca-bundle", "Path to TLS certificate authority bundle (PEM format, optional, can be repeated)")
f.BoolVar(&flags.oidc.debugSessionCache, "oidc-debug-session-cache", false, "Print debug logs related to the OpenID Connect session cache")
f.StringVar(&flags.oidc.requestAudience, "oidc-request-audience", "", "Request a token with an alternate audience using RFC8693 token exchange")
f.StringVar(&flags.kubeconfigPath, "kubeconfig", os.Getenv("KUBECONFIG"), "Path to kubeconfig file")
f.StringVar(&flags.kubeconfigContextOverride, "kubeconfig-context", "", "Kubeconfig context name (default: current active context)")
f.BoolVar(&flags.skipValidate, "skip-validation", false, "Skip final validation of the kubeconfig (default: false)")
f.DurationVar(&flags.timeout, "timeout", 10*time.Minute, "Timeout for autodiscovery and validation")
f.StringVarP(&flags.outputPath, "output", "o", "", "Output file path (default: stdout)")
mustMarkHidden(cmd, "oidc-debug-session-cache")
mustMarkDeprecated(cmd, "concierge-namespace", "not needed anymore")
mustMarkHidden(cmd, "concierge-namespace")
cmd.RunE = func(cmd *cobra.Command, args []string) error { return runGetKubeconfig(cmd.OutOrStdout(), deps, flags) }
cmd.RunE = func(cmd *cobra.Command, args []string) error {
if flags.outputPath != "" {
out, err := os.Create(flags.outputPath)
if err != nil {
return fmt.Errorf("could not open output file: %w", err)
}
defer func() { _ = out.Close() }()
cmd.SetOut(out)
}
return runGetKubeconfig(cmd.Context(), cmd.OutOrStdout(), deps, flags)
}
return cmd
}
//nolint:funlen
func runGetKubeconfig(out io.Writer, deps kubeconfigDeps, flags getKubeconfigParams) error {
func runGetKubeconfig(ctx context.Context, out io.Writer, deps kubeconfigDeps, flags getKubeconfigParams) error {
ctx, cancel := context.WithTimeout(ctx, flags.timeout)
defer cancel()
// Validate api group suffix and immediately return an error if it is invalid.
if err := groupsuffix.Validate(flags.concierge.apiGroupSuffix); err != nil {
return fmt.Errorf("invalid api group suffix: %w", err)
return fmt.Errorf("invalid API group suffix: %w", err)
}
execConfig := clientcmdapi.ExecConfig{
@@ -151,11 +173,6 @@ func runGetKubeconfig(out io.Writer, deps kubeconfigDeps, flags getKubeconfigPar
}
execConfig.ProvideClusterInfo = true
oidcCABundle, err := loadCABundlePaths(flags.oidc.caBundlePaths)
if err != nil {
return fmt.Errorf("could not read --oidc-ca-bundle: %w", err)
}
clientConfig := newClientConfig(flags.kubeconfigPath, flags.kubeconfigContextOverride)
currentKubeConfig, err := clientConfig.RawConfig()
if err != nil {
@@ -171,17 +188,39 @@ func runGetKubeconfig(out io.Writer, deps kubeconfigDeps, flags getKubeconfigPar
}
if !flags.concierge.disabled {
credentialIssuer, err := waitForCredentialIssuer(ctx, clientset, flags, deps)
if err != nil {
return err
}
authenticator, err := lookupAuthenticator(
clientset,
flags.concierge.authenticatorType,
flags.concierge.authenticatorName,
deps.log,
)
if err != nil {
return err
}
if err := configureConcierge(authenticator, &flags, cluster, &oidcCABundle, &execConfig); err != nil {
if err := discoverConciergeParams(credentialIssuer, &flags, cluster, deps.log); err != nil {
return err
}
if err := discoverAuthenticatorParams(authenticator, &flags, deps.log); err != nil {
return err
}
// Append the flags to configure the Concierge credential exchange at runtime.
execConfig.Args = append(execConfig.Args,
"--enable-concierge",
"--concierge-api-group-suffix="+flags.concierge.apiGroupSuffix,
"--concierge-authenticator-name="+flags.concierge.authenticatorName,
"--concierge-authenticator-type="+flags.concierge.authenticatorType,
"--concierge-endpoint="+flags.concierge.endpoint,
"--concierge-ca-bundle-data="+base64.StdEncoding.EncodeToString(flags.concierge.caBundle),
)
// Point kubectl at the concierge endpoint.
cluster.Server = flags.concierge.endpoint
cluster.CertificateAuthorityData = flags.concierge.caBundle
}
// If one of the --static-* flags was passed, output a config that runs `pinniped login static`.
@@ -196,13 +235,18 @@ func runGetKubeconfig(out io.Writer, deps kubeconfigDeps, flags getKubeconfigPar
if flags.staticTokenEnvName != "" {
execConfig.Args = append(execConfig.Args, "--token-env="+flags.staticTokenEnvName)
}
return writeConfigAsYAML(out, newExecKubeconfig(cluster, &execConfig))
kubeconfig := newExecKubeconfig(cluster, &execConfig)
if err := validateKubeconfig(ctx, flags, kubeconfig, deps.log); err != nil {
return err
}
return writeConfigAsYAML(out, kubeconfig)
}
// Otherwise continue to parse the OIDC-related flags and output a config that runs `pinniped login oidc`.
execConfig.Args = append([]string{"login", "oidc"}, execConfig.Args...)
if flags.oidc.issuer == "" {
return fmt.Errorf("could not autodiscover --oidc-issuer, and none was provided")
return fmt.Errorf("could not autodiscover --oidc-issuer and none was provided")
}
execConfig.Args = append(execConfig.Args,
"--issuer="+flags.oidc.issuer,
@@ -215,8 +259,8 @@ func runGetKubeconfig(out io.Writer, deps kubeconfigDeps, flags getKubeconfigPar
if flags.oidc.listenPort != 0 {
execConfig.Args = append(execConfig.Args, "--listen-port="+strconv.Itoa(int(flags.oidc.listenPort)))
}
if oidcCABundle != "" {
execConfig.Args = append(execConfig.Args, "--ca-bundle-data="+base64.StdEncoding.EncodeToString([]byte(oidcCABundle)))
if len(flags.oidc.caBundle) != 0 {
execConfig.Args = append(execConfig.Args, "--ca-bundle-data="+base64.StdEncoding.EncodeToString(flags.oidc.caBundle))
}
if flags.oidc.sessionCachePath != "" {
execConfig.Args = append(execConfig.Args, "--session-cache="+flags.oidc.sessionCachePath)
@@ -227,15 +271,115 @@ func runGetKubeconfig(out io.Writer, deps kubeconfigDeps, flags getKubeconfigPar
if flags.oidc.requestAudience != "" {
execConfig.Args = append(execConfig.Args, "--request-audience="+flags.oidc.requestAudience)
}
return writeConfigAsYAML(out, newExecKubeconfig(cluster, &execConfig))
kubeconfig := newExecKubeconfig(cluster, &execConfig)
if err := validateKubeconfig(ctx, flags, kubeconfig, deps.log); err != nil {
return err
}
return writeConfigAsYAML(out, kubeconfig)
}
func configureConcierge(authenticator metav1.Object, flags *getKubeconfigParams, v1Cluster *clientcmdapi.Cluster, oidcCABundle *string, execConfig *clientcmdapi.ExecConfig) error {
func waitForCredentialIssuer(ctx context.Context, clientset conciergeclientset.Interface, flags getKubeconfigParams, deps kubeconfigDeps) (*configv1alpha1.CredentialIssuer, error) {
credentialIssuer, err := lookupCredentialIssuer(clientset, flags.concierge.credentialIssuer, deps.log)
if err != nil {
return nil, err
}
if !flags.concierge.skipWait {
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
deadline, _ := ctx.Deadline()
attempts := 1
for {
if !hasPendingStrategy(credentialIssuer) {
break
}
logStrategies(credentialIssuer, deps.log)
deps.log.Info("waiting for CredentialIssuer pending strategies to finish",
"attempts", attempts,
"remaining", time.Until(deadline).Round(time.Second).String(),
)
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-ticker.C:
credentialIssuer, err = lookupCredentialIssuer(clientset, flags.concierge.credentialIssuer, deps.log)
if err != nil {
return nil, err
}
}
}
}
return credentialIssuer, nil
}
func discoverConciergeParams(credentialIssuer *configv1alpha1.CredentialIssuer, flags *getKubeconfigParams, v1Cluster *clientcmdapi.Cluster, log logr.Logger) error {
// Autodiscover the --concierge-mode.
frontend, err := getConciergeFrontend(credentialIssuer, flags.concierge.mode)
if err != nil {
logStrategies(credentialIssuer, log)
return err
}
// Auto-set --concierge-mode if it wasn't explicitly set.
if flags.concierge.mode == modeUnknown {
switch frontend.Type {
case configv1alpha1.TokenCredentialRequestAPIFrontendType:
log.Info("discovered Concierge operating in TokenCredentialRequest API mode")
flags.concierge.mode = modeTokenCredentialRequestAPI
case configv1alpha1.ImpersonationProxyFrontendType:
log.Info("discovered Concierge operating in impersonation proxy mode")
flags.concierge.mode = modeImpersonationProxy
}
}
// Auto-set --concierge-endpoint if it wasn't explicitly set.
if flags.concierge.endpoint == "" {
switch frontend.Type {
case configv1alpha1.TokenCredentialRequestAPIFrontendType:
flags.concierge.endpoint = v1Cluster.Server
case configv1alpha1.ImpersonationProxyFrontendType:
flags.concierge.endpoint = frontend.ImpersonationProxyInfo.Endpoint
}
log.Info("discovered Concierge endpoint", "endpoint", flags.concierge.endpoint)
}
// Auto-set --concierge-ca-bundle if it wasn't explicitly set..
if len(flags.concierge.caBundle) == 0 {
switch frontend.Type {
case configv1alpha1.TokenCredentialRequestAPIFrontendType:
flags.concierge.caBundle = v1Cluster.CertificateAuthorityData
case configv1alpha1.ImpersonationProxyFrontendType:
data, err := base64.StdEncoding.DecodeString(frontend.ImpersonationProxyInfo.CertificateAuthorityData)
if err != nil {
return fmt.Errorf("autodiscovered Concierge CA bundle is invalid: %w", err)
}
flags.concierge.caBundle = data
}
log.Info("discovered Concierge certificate authority bundle", "roots", countCACerts(flags.concierge.caBundle))
}
return nil
}
func logStrategies(credentialIssuer *configv1alpha1.CredentialIssuer, log logr.Logger) {
for _, strategy := range credentialIssuer.Status.Strategies {
log.Info("found CredentialIssuer strategy",
"type", strategy.Type,
"status", strategy.Status,
"reason", strategy.Reason,
"message", strategy.Message,
)
}
}
func discoverAuthenticatorParams(authenticator metav1.Object, flags *getKubeconfigParams, log logr.Logger) error {
switch auth := authenticator.(type) {
case *conciergev1alpha1.WebhookAuthenticator:
// If the --concierge-authenticator-type/--concierge-authenticator-name flags were not set explicitly, set
// them to point at the discovered WebhookAuthenticator.
if flags.concierge.authenticatorType == "" && flags.concierge.authenticatorName == "" {
log.Info("discovered WebhookAuthenticator", "name", auth.Name)
flags.concierge.authenticatorType = "webhook"
flags.concierge.authenticatorName = auth.Name
}
@@ -243,56 +387,78 @@ func configureConcierge(authenticator metav1.Object, flags *getKubeconfigParams,
// If the --concierge-authenticator-type/--concierge-authenticator-name flags were not set explicitly, set
// them to point at the discovered JWTAuthenticator.
if flags.concierge.authenticatorType == "" && flags.concierge.authenticatorName == "" {
log.Info("discovered JWTAuthenticator", "name", auth.Name)
flags.concierge.authenticatorType = "jwt"
flags.concierge.authenticatorName = auth.Name
}
// If the --oidc-issuer flag was not set explicitly, default it to the spec.issuer field of the JWTAuthenticator.
if flags.oidc.issuer == "" {
log.Info("discovered OIDC issuer", "issuer", auth.Spec.Issuer)
flags.oidc.issuer = auth.Spec.Issuer
}
// If the --oidc-request-audience flag was not set explicitly, default it to the spec.audience field of the JWTAuthenticator.
if flags.oidc.requestAudience == "" {
log.Info("discovered OIDC audience", "audience", auth.Spec.Audience)
flags.oidc.requestAudience = auth.Spec.Audience
}
// If the --oidc-ca-bundle flags was not set explicitly, default it to the
// spec.tls.certificateAuthorityData field of the JWTAuthenticator.
if *oidcCABundle == "" && auth.Spec.TLS != nil && auth.Spec.TLS.CertificateAuthorityData != "" {
if len(flags.oidc.caBundle) == 0 && auth.Spec.TLS != nil && auth.Spec.TLS.CertificateAuthorityData != "" {
decoded, err := base64.StdEncoding.DecodeString(auth.Spec.TLS.CertificateAuthorityData)
if err != nil {
return fmt.Errorf("tried to autodiscover --oidc-ca-bundle, but JWTAuthenticator %s has invalid spec.tls.certificateAuthorityData: %w", auth.Name, err)
}
*oidcCABundle = string(decoded)
log.Info("discovered OIDC CA bundle", "roots", countCACerts(decoded))
flags.oidc.caBundle = decoded
}
}
// Append the flags to configure the Concierge credential exchange at runtime.
execConfig.Args = append(execConfig.Args,
"--enable-concierge",
"--concierge-api-group-suffix="+flags.concierge.apiGroupSuffix,
"--concierge-authenticator-name="+flags.concierge.authenticatorName,
"--concierge-authenticator-type="+flags.concierge.authenticatorType,
"--concierge-endpoint="+v1Cluster.Server,
"--concierge-ca-bundle-data="+base64.StdEncoding.EncodeToString(v1Cluster.CertificateAuthorityData),
)
return nil
}
func loadCABundlePaths(paths []string) (string, error) {
if len(paths) == 0 {
return "", nil
}
blobs := make([][]byte, 0, len(paths))
for _, p := range paths {
pem, err := ioutil.ReadFile(p)
if err != nil {
return "", err
func getConciergeFrontend(credentialIssuer *configv1alpha1.CredentialIssuer, mode conciergeModeFlag) (*configv1alpha1.CredentialIssuerFrontend, error) {
for _, strategy := range credentialIssuer.Status.Strategies {
// Skip unhealthy strategies.
if strategy.Status != configv1alpha1.SuccessStrategyStatus {
continue
}
blobs = append(blobs, pem)
// Backfill the .status.strategies[].frontend field from .status.kubeConfigInfo for backwards compatibility.
if strategy.Type == configv1alpha1.KubeClusterSigningCertificateStrategyType && strategy.Frontend == nil && credentialIssuer.Status.KubeConfigInfo != nil {
strategy = *strategy.DeepCopy()
strategy.Frontend = &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.TokenCredentialRequestAPIFrontendType,
TokenCredentialRequestAPIInfo: &configv1alpha1.TokenCredentialRequestAPIInfo{
Server: credentialIssuer.Status.KubeConfigInfo.Server,
CertificateAuthorityData: credentialIssuer.Status.KubeConfigInfo.CertificateAuthorityData,
},
}
}
// If the strategy frontend is still nil, skip.
if strategy.Frontend == nil {
continue
}
// Skip any unknown frontend types.
switch strategy.Frontend.Type {
case configv1alpha1.TokenCredentialRequestAPIFrontendType, configv1alpha1.ImpersonationProxyFrontendType:
default:
continue
}
// Skip strategies that don't match --concierge-mode.
if !mode.MatchesFrontend(strategy.Frontend) {
continue
}
return strategy.Frontend, nil
}
return string(bytes.Join(blobs, []byte("\n"))), nil
if mode == modeUnknown {
return nil, fmt.Errorf("could not autodiscover --concierge-mode")
}
return nil, fmt.Errorf("could not find successful Concierge strategy matching --concierge-mode=%s", mode.String())
}
func newExecKubeconfig(cluster *clientcmdapi.Cluster, execConfig *clientcmdapi.ExecConfig) clientcmdapi.Config {
@@ -307,7 +473,33 @@ func newExecKubeconfig(cluster *clientcmdapi.Cluster, execConfig *clientcmdapi.E
}
}
func lookupAuthenticator(clientset conciergeclientset.Interface, authType, authName string) (metav1.Object, error) {
func lookupCredentialIssuer(clientset conciergeclientset.Interface, name string, log logr.Logger) (*configv1alpha1.CredentialIssuer, error) {
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*20)
defer cancelFunc()
// If the name is specified, get that object.
if name != "" {
return clientset.ConfigV1alpha1().CredentialIssuers().Get(ctx, name, metav1.GetOptions{})
}
// Otherwise list all the available CredentialIssuers and hope there's just a single one
results, err := clientset.ConfigV1alpha1().CredentialIssuers().List(ctx, metav1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("failed to list CredentialIssuer objects for autodiscovery: %w", err)
}
if len(results.Items) == 0 {
return nil, fmt.Errorf("no CredentialIssuers were found")
}
if len(results.Items) > 1 {
return nil, fmt.Errorf("multiple CredentialIssuers were found, so the --concierge-credential-issuer flag must be specified")
}
result := &results.Items[0]
log.Info("discovered CredentialIssuer", "name", result.Name)
return result, nil
}
func lookupAuthenticator(clientset conciergeclientset.Interface, authType, authName string, log logr.Logger) (metav1.Object, error) {
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*20)
defer cancelFunc()
@@ -345,20 +537,17 @@ func lookupAuthenticator(clientset conciergeclientset.Interface, authType, authN
return nil, fmt.Errorf("no authenticators were found")
}
if len(results) > 1 {
for _, jwtAuth := range jwtAuths.Items {
log.Info("found JWTAuthenticator", "name", jwtAuth.Name)
}
for _, webhook := range webhooks.Items {
log.Info("found WebhookAuthenticator", "name", webhook.Name)
}
return nil, fmt.Errorf("multiple authenticators were found, so the --concierge-authenticator-type/--concierge-authenticator-name flags must be specified")
}
return results[0], nil
}
func newClientConfig(kubeconfigPathOverride string, currentContextName string) clientcmd.ClientConfig {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
loadingRules.ExplicitPath = kubeconfigPathOverride
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{
CurrentContext: currentContextName,
})
return clientConfig
}
func writeConfigAsYAML(out io.Writer, config clientcmdapi.Config) error {
output, err := clientcmd.Write(config)
if err != nil {
@@ -376,9 +565,101 @@ func copyCurrentClusterFromExistingKubeConfig(currentKubeConfig clientcmdapi.Con
if currentContextNameOverride != "" {
contextName = currentContextNameOverride
}
context := currentKubeConfig.Contexts[contextName]
if context == nil {
ctx := currentKubeConfig.Contexts[contextName]
if ctx == nil {
return nil, fmt.Errorf("no such context %q", contextName)
}
return currentKubeConfig.Clusters[context.Cluster], nil
return currentKubeConfig.Clusters[ctx.Cluster], nil
}
func validateKubeconfig(ctx context.Context, flags getKubeconfigParams, kubeconfig clientcmdapi.Config, log logr.Logger) error {
if flags.skipValidate {
return nil
}
kubeContext := kubeconfig.Contexts[kubeconfig.CurrentContext]
if kubeContext == nil {
return fmt.Errorf("invalid kubeconfig (no context)")
}
cluster := kubeconfig.Clusters[kubeContext.Cluster]
if cluster == nil {
return fmt.Errorf("invalid kubeconfig (no cluster)")
}
kubeconfigCA := x509.NewCertPool()
if !kubeconfigCA.AppendCertsFromPEM(cluster.CertificateAuthorityData) {
return fmt.Errorf("invalid kubeconfig (no certificateAuthorityData)")
}
httpClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
RootCAs: kubeconfigCA,
},
Proxy: http.ProxyFromEnvironment,
TLSHandshakeTimeout: 10 * time.Second,
},
Timeout: 10 * time.Second,
}
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
pingCluster := func() error {
reqCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(reqCtx, http.MethodGet, cluster.Server, nil)
if err != nil {
return fmt.Errorf("could not form request to validate cluster: %w", err)
}
resp, err := httpClient.Do(req)
if err != nil {
return err
}
_ = resp.Body.Close()
if resp.StatusCode >= 500 {
return fmt.Errorf("unexpected status code %d", resp.StatusCode)
}
return nil
}
err := pingCluster()
if err == nil {
log.Info("validated connection to the cluster")
return nil
}
log.Info("could not immediately connect to the cluster but it may be initializing, will retry until timeout")
deadline, _ := ctx.Deadline()
attempts := 0
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-ticker.C:
attempts++
err := pingCluster()
if err == nil {
log.Info("validated connection to the cluster", "attempts", attempts)
return nil
}
log.Error(err, "could not connect to cluster, retrying...", "attempts", attempts, "remaining", time.Until(deadline).Round(time.Second).String())
}
}
}
func countCACerts(pemData []byte) int {
pool := x509.NewCertPool()
pool.AppendCertsFromPEM(pemData)
return len(pool.Subjects())
}
func hasPendingStrategy(credentialIssuer *configv1alpha1.CredentialIssuer) bool {
for _, strategy := range credentialIssuer.Status.Strategies {
if strategy.Reason == configv1alpha1.PendingStrategyReason {
return true
}
}
return false
}

View File

@@ -5,7 +5,6 @@ package cmd
import (
"bytes"
"crypto/x509/pkix"
"encoding/base64"
"fmt"
"io/ioutil"
@@ -19,20 +18,27 @@ import (
kubetesting "k8s.io/client-go/testing"
"k8s.io/client-go/tools/clientcmd"
conciergev1alpha1 "go.pinniped.dev/generated/1.20/apis/concierge/authentication/v1alpha1"
conciergeclientset "go.pinniped.dev/generated/1.20/client/concierge/clientset/versioned"
fakeconciergeclientset "go.pinniped.dev/generated/1.20/client/concierge/clientset/versioned/fake"
conciergev1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
conciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
fakeconciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake"
"go.pinniped.dev/internal/certauthority"
"go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/testutil/testlogger"
)
func TestGetKubeconfig(t *testing.T) {
testCA, err := certauthority.New(pkix.Name{CommonName: "Test CA"}, 1*time.Hour)
testOIDCCA, err := certauthority.New("Test CA", 1*time.Hour)
require.NoError(t, err)
tmpdir := testutil.TempDir(t)
testCABundlePath := filepath.Join(tmpdir, "testca.pem")
require.NoError(t, ioutil.WriteFile(testCABundlePath, testCA.Bundle(), 0600))
testOIDCCABundlePath := filepath.Join(tmpdir, "testca.pem")
require.NoError(t, ioutil.WriteFile(testOIDCCABundlePath, testOIDCCA.Bundle(), 0600))
testConciergeCA, err := certauthority.New("Test Concierge CA", 1*time.Hour)
require.NoError(t, err)
testConciergeCABundlePath := filepath.Join(tmpdir, "testconciergeca.pem")
require.NoError(t, ioutil.WriteFile(testConciergeCABundlePath, testConciergeCA.Bundle(), 0600))
tests := []struct {
name string
@@ -42,6 +48,7 @@ func TestGetKubeconfig(t *testing.T) {
getClientsetErr error
conciergeObjects []runtime.Object
conciergeReactions []kubetesting.Reactor
wantLogs []string
wantError bool
wantStdout string
wantStderr string
@@ -61,11 +68,16 @@ func TestGetKubeconfig(t *testing.T) {
--concierge-api-group-suffix string Concierge API group suffix (default "pinniped.dev")
--concierge-authenticator-name string Concierge authenticator name (default: autodiscover)
--concierge-authenticator-type string Concierge authenticator type (e.g., 'webhook', 'jwt') (default: autodiscover)
--concierge-ca-bundle path Path to TLS certificate authority bundle (PEM format, optional, can be repeated) to use when connecting to the Concierge
--concierge-credential-issuer string Concierge CredentialIssuer object to use for autodiscovery (default: autodiscover)
--concierge-endpoint string API base for the Concierge endpoint
--concierge-mode mode Concierge mode of operation (default TokenCredentialRequestAPI)
--concierge-skip-wait Skip waiting for any pending Concierge strategies to become ready (default: false)
-h, --help help for kubeconfig
--kubeconfig string Path to kubeconfig file
--kubeconfig-context string Kubeconfig context name (default: current active context)
--no-concierge Generate a configuration which does not use the concierge, but sends the credential to the cluster directly
--oidc-ca-bundle strings Path to TLS certificate authority bundle (PEM format, optional, can be repeated)
--no-concierge Generate a configuration which does not use the Concierge, but sends the credential to the cluster directly
--oidc-ca-bundle path Path to TLS certificate authority bundle (PEM format, optional, can be repeated)
--oidc-client-id string OpenID Connect client ID (default: autodiscover) (default "pinniped-cli")
--oidc-issuer string OpenID Connect issuer URL (default: autodiscover)
--oidc-listen-port uint16 TCP port for localhost listener (authorization code flow only)
@@ -73,8 +85,11 @@ func TestGetKubeconfig(t *testing.T) {
--oidc-scopes strings OpenID Connect scopes to request during login (default [offline_access,openid,pinniped:request-audience])
--oidc-session-cache string Path to OpenID Connect session cache file
--oidc-skip-browser During OpenID Connect login, skip opening the browser (just print the URL)
-o, --output string Output file path (default: stdout)
--skip-validation Skip final validation of the kubeconfig (default: false)
--static-token string Instead of doing an OIDC-based login, specify a static token
--static-token-env string Instead of doing an OIDC-based login, read a static token from the environment
--timeout duration Timeout for autodiscovery and validation (default 10m0s)
`),
},
{
@@ -87,13 +102,24 @@ func TestGetKubeconfig(t *testing.T) {
`),
},
{
name: "invalid CA bundle paths",
name: "invalid OIDC CA bundle path",
args: []string{
"--oidc-ca-bundle", "./does/not/exist",
},
wantError: true,
wantStderr: here.Doc(`
Error: could not read --oidc-ca-bundle: open ./does/not/exist: no such file or directory
Error: invalid argument "./does/not/exist" for "--oidc-ca-bundle" flag: could not read CA bundle path: open ./does/not/exist: no such file or directory
`),
},
{
name: "invalid Concierge CA bundle",
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--concierge-ca-bundle", "./does/not/exist",
},
wantError: true,
wantStderr: here.Doc(`
Error: invalid argument "./does/not/exist" for "--concierge-ca-bundle" flag: could not read CA bundle path: open ./does/not/exist: no such file or directory
`),
},
{
@@ -128,6 +154,31 @@ func TestGetKubeconfig(t *testing.T) {
Error: could not configure Kubernetes client: some kube error
`),
},
{
name: "no credentialissuers",
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
},
wantError: true,
wantStderr: here.Doc(`
Error: no CredentialIssuers were found
`),
},
{
name: "credentialissuer not found",
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--concierge-credential-issuer", "does-not-exist",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
},
wantError: true,
wantStderr: here.Doc(`
Error: credentialissuers.config.concierge.pinniped.dev "does-not-exist" not found
`),
},
{
name: "webhook authenticator not found",
args: []string{
@@ -135,6 +186,12 @@ func TestGetKubeconfig(t *testing.T) {
"--concierge-authenticator-type", "webhook",
"--concierge-authenticator-name", "test-authenticator",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
},
wantError: true,
wantStderr: here.Doc(`
Error: webhookauthenticators.authentication.concierge.pinniped.dev "test-authenticator" not found
@@ -147,6 +204,12 @@ func TestGetKubeconfig(t *testing.T) {
"--concierge-authenticator-type", "jwt",
"--concierge-authenticator-name", "test-authenticator",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
},
wantError: true,
wantStderr: here.Doc(`
Error: jwtauthenticators.authentication.concierge.pinniped.dev "test-authenticator" not found
@@ -159,6 +222,12 @@ func TestGetKubeconfig(t *testing.T) {
"--concierge-authenticator-type", "invalid",
"--concierge-authenticator-name", "test-authenticator",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
},
wantError: true,
wantStderr: here.Doc(`
Error: invalid authenticator type "invalid", supported values are "webhook" and "jwt"
@@ -169,6 +238,12 @@ func TestGetKubeconfig(t *testing.T) {
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
},
conciergeReactions: []kubetesting.Reactor{
&kubetesting.SimpleReactor{
Verb: "*",
@@ -188,6 +263,9 @@ func TestGetKubeconfig(t *testing.T) {
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
},
conciergeReactions: []kubetesting.Reactor{
&kubetesting.SimpleReactor{
Verb: "*",
@@ -197,6 +275,9 @@ func TestGetKubeconfig(t *testing.T) {
},
},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
},
wantError: true,
wantStderr: here.Doc(`
Error: failed to list WebhookAuthenticator objects for autodiscovery: some list error
@@ -207,6 +288,12 @@ func TestGetKubeconfig(t *testing.T) {
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
},
wantError: true,
wantStderr: here.Doc(`
Error: no authenticators were found
@@ -218,27 +305,145 @@ func TestGetKubeconfig(t *testing.T) {
"--kubeconfig", "./testdata/kubeconfig.yaml",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
&conciergev1alpha1.JWTAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator-1"}},
&conciergev1alpha1.JWTAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator-2"}},
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator-3"}},
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator-4"}},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="found JWTAuthenticator" "name"="test-authenticator-1"`,
`"level"=0 "msg"="found JWTAuthenticator" "name"="test-authenticator-2"`,
`"level"=0 "msg"="found WebhookAuthenticator" "name"="test-authenticator-3"`,
`"level"=0 "msg"="found WebhookAuthenticator" "name"="test-authenticator-4"`,
},
wantError: true,
wantStderr: here.Doc(`
Error: multiple authenticators were found, so the --concierge-authenticator-type/--concierge-authenticator-name flags must be specified
`),
},
{
name: "autodetect webhook authenticator, bad credential issuer with only failing strategy",
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
Type: "SomeType",
Status: configv1alpha1.ErrorStrategyStatus,
Reason: "SomeReason",
Message: "Some message",
}},
},
},
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="found CredentialIssuer strategy" "message"="Some message" "reason"="SomeReason" "status"="Error" "type"="SomeType"`,
},
wantError: true,
wantStderr: here.Doc(`
Error: could not autodiscover --concierge-mode
`),
},
{
name: "autodetect webhook authenticator, bad credential issuer with invalid impersonation CA",
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
Strategies: []configv1alpha1.CredentialIssuerStrategy{
{
Type: "SomeBrokenType",
Status: configv1alpha1.ErrorStrategyStatus,
Reason: "SomeFailureReason",
Message: "Some error message",
LastUpdateTime: metav1.Now(),
},
{
Type: "SomeUnknownType",
Status: configv1alpha1.SuccessStrategyStatus,
Reason: "SomeReason",
Message: "Some error message",
LastUpdateTime: metav1.Now(),
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: "SomeUnknownFrontendType",
},
},
{
Type: "SomeType",
Status: configv1alpha1.SuccessStrategyStatus,
Reason: "SomeReason",
Message: "Some message",
LastUpdateTime: metav1.Now(),
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.ImpersonationProxyFrontendType,
ImpersonationProxyInfo: &configv1alpha1.ImpersonationProxyInfo{
Endpoint: "https://impersonation-endpoint",
CertificateAuthorityData: "invalid-base-64",
},
},
},
},
},
},
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in impersonation proxy mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://impersonation-endpoint"`,
},
wantError: true,
wantStderr: here.Doc(`
Error: autodiscovered Concierge CA bundle is invalid: illegal base64 data at input byte 7
`),
},
{
name: "autodetect webhook authenticator, missing --oidc-issuer",
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
Type: "SomeType",
Status: configv1alpha1.SuccessStrategyStatus,
Reason: "SomeReason",
Message: "Some message",
LastUpdateTime: metav1.Now(),
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.TokenCredentialRequestAPIFrontendType,
TokenCredentialRequestAPIInfo: &configv1alpha1.TokenCredentialRequestAPIInfo{
Server: "https://concierge-endpoint",
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
},
},
}},
},
},
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered WebhookAuthenticator" "name"="test-authenticator"`,
},
wantError: true,
wantStderr: here.Doc(`
Error: could not autodiscover --oidc-issuer, and none was provided
Error: could not autodiscover --oidc-issuer and none was provided
`),
},
{
@@ -247,15 +452,44 @@ func TestGetKubeconfig(t *testing.T) {
"--kubeconfig", "./testdata/kubeconfig.yaml",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
Server: "https://concierge-endpoint",
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
},
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
Status: configv1alpha1.SuccessStrategyStatus,
Reason: configv1alpha1.FetchedKeyStrategyReason,
Message: "Successfully fetched key",
LastUpdateTime: metav1.Now(),
// Simulate a previous version of CredentialIssuer that's missing this Frontend field.
Frontend: nil,
}},
},
},
&conciergev1alpha1.JWTAuthenticator{
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
Spec: conciergev1alpha1.JWTAuthenticatorSpec{
Issuer: "https://test-issuer.example.com",
Audience: "some-test-audience",
TLS: &conciergev1alpha1.TLSSpec{
CertificateAuthorityData: "invalid-base64",
},
},
},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
`"level"=0 "msg"="discovered OIDC issuer" "issuer"="https://test-issuer.example.com"`,
`"level"=0 "msg"="discovered OIDC audience" "audience"="some-test-audience"`,
},
wantError: true,
wantStderr: here.Doc(`
Error: tried to autodiscover --oidc-ca-bundle, but JWTAuthenticator test-authenticator has invalid spec.tls.certificateAuthorityData: illegal base64 data at input byte 7
@@ -269,21 +503,45 @@ func TestGetKubeconfig(t *testing.T) {
"--static-token-env", "TEST_TOKEN",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
Type: configv1alpha1.ImpersonationProxyStrategyType,
Status: configv1alpha1.SuccessStrategyStatus,
Reason: configv1alpha1.ListeningStrategyReason,
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.ImpersonationProxyFrontendType,
ImpersonationProxyInfo: &configv1alpha1.ImpersonationProxyInfo{
Endpoint: "https://impersonation-proxy-endpoint.example.com",
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
},
},
}},
},
},
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in impersonation proxy mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://impersonation-proxy-endpoint.example.com"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=1`,
`"level"=0 "msg"="discovered WebhookAuthenticator" "name"="test-authenticator"`,
},
wantError: true,
wantStderr: here.Doc(`
Error: only one of --static-token and --static-token-env can be specified
`),
},
{
name: "invalid api group suffix",
name: "invalid API group suffix",
args: []string{
"--concierge-api-group-suffix", ".starts.with.dot",
},
wantError: true,
wantStderr: here.Doc(`
Error: invalid api group suffix: a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')
Error: invalid API group suffix: a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')
`),
},
{
@@ -291,10 +549,35 @@ func TestGetKubeconfig(t *testing.T) {
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--static-token", "test-token",
"--skip-validation",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
Status: configv1alpha1.SuccessStrategyStatus,
Reason: configv1alpha1.FetchedKeyStrategyReason,
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.TokenCredentialRequestAPIFrontendType,
TokenCredentialRequestAPIInfo: &configv1alpha1.TokenCredentialRequestAPIInfo{
Server: "https://concierge-endpoint.example.com",
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
},
},
}},
},
},
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered WebhookAuthenticator" "name"="test-authenticator"`,
},
wantStdout: here.Doc(`
apiVersion: v1
clusters:
@@ -335,10 +618,35 @@ func TestGetKubeconfig(t *testing.T) {
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--static-token-env", "TEST_TOKEN",
"--skip-validation",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
Status: configv1alpha1.SuccessStrategyStatus,
Reason: configv1alpha1.FetchedKeyStrategyReason,
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.TokenCredentialRequestAPIFrontendType,
TokenCredentialRequestAPIInfo: &configv1alpha1.TokenCredentialRequestAPIInfo{
Server: "https://concierge-endpoint.example.com",
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
},
},
}},
},
},
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered WebhookAuthenticator" "name"="test-authenticator"`,
},
wantStdout: here.Doc(`
apiVersion: v1
clusters:
@@ -378,19 +686,47 @@ func TestGetKubeconfig(t *testing.T) {
name: "autodetect JWT authenticator",
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
Status: configv1alpha1.SuccessStrategyStatus,
Reason: configv1alpha1.FetchedKeyStrategyReason,
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.TokenCredentialRequestAPIFrontendType,
TokenCredentialRequestAPIInfo: &configv1alpha1.TokenCredentialRequestAPIInfo{
Server: "https://concierge-endpoint.example.com",
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
},
},
}},
},
},
&conciergev1alpha1.JWTAuthenticator{
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
Spec: conciergev1alpha1.JWTAuthenticatorSpec{
Issuer: "https://example.com/issuer",
Audience: "test-audience",
TLS: &conciergev1alpha1.TLSSpec{
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testCA.Bundle()),
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testOIDCCA.Bundle()),
},
},
},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
`"level"=0 "msg"="discovered OIDC issuer" "issuer"="https://example.com/issuer"`,
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
},
wantStdout: here.Docf(`
apiVersion: v1
clusters:
@@ -428,34 +764,57 @@ func TestGetKubeconfig(t *testing.T) {
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`, base64.StdEncoding.EncodeToString(testCA.Bundle())),
`, base64.StdEncoding.EncodeToString(testOIDCCA.Bundle())),
},
{
name: "autodetect nothing, set a bunch of options",
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--concierge-credential-issuer", "test-credential-issuer",
"--concierge-api-group-suffix", "tuna.io",
"--concierge-authenticator-type", "webhook",
"--concierge-authenticator-name", "test-authenticator",
"--concierge-mode", "TokenCredentialRequestAPI",
"--concierge-endpoint", "https://explicit-concierge-endpoint.example.com",
"--concierge-ca-bundle", testConciergeCABundlePath,
"--oidc-issuer", "https://example.com/issuer",
"--oidc-skip-browser",
"--oidc-listen-port", "1234",
"--oidc-ca-bundle", testCABundlePath,
"--oidc-ca-bundle", testOIDCCABundlePath,
"--oidc-session-cache", "/path/to/cache/dir/sessions.yaml",
"--oidc-debug-session-cache",
"--oidc-request-audience", "test-audience",
"--skip-validation",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
Status: configv1alpha1.SuccessStrategyStatus,
Reason: configv1alpha1.FetchedKeyStrategyReason,
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.TokenCredentialRequestAPIFrontendType,
TokenCredentialRequestAPIInfo: &configv1alpha1.TokenCredentialRequestAPIInfo{
Server: "https://concierge-endpoint.example.com",
CertificateAuthorityData: "dGVzdC10Y3ItYXBpLWNh",
},
},
}},
},
},
&conciergev1alpha1.WebhookAuthenticator{
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
},
},
wantLogs: nil,
wantStdout: here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
certificate-authority-data: %s
server: https://explicit-concierge-endpoint.example.com
name: pinniped
contexts:
- context:
@@ -477,8 +836,8 @@ func TestGetKubeconfig(t *testing.T) {
- --concierge-api-group-suffix=tuna.io
- --concierge-authenticator-name=test-authenticator
- --concierge-authenticator-type=webhook
- --concierge-endpoint=https://fake-server-url-value
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --concierge-endpoint=https://explicit-concierge-endpoint.example.com
- --concierge-ca-bundle-data=%s
- --issuer=https://example.com/issuer
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
@@ -491,13 +850,229 @@ func TestGetKubeconfig(t *testing.T) {
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`, base64.StdEncoding.EncodeToString(testCA.Bundle())),
`,
base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
base64.StdEncoding.EncodeToString(testOIDCCA.Bundle()),
),
wantAPIGroupSuffix: "tuna.io",
},
{
name: "configure impersonation proxy with autodiscovered JWT authenticator",
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--concierge-mode", "ImpersonationProxy",
"--skip-validation",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
Strategies: []configv1alpha1.CredentialIssuerStrategy{
// This TokenCredentialRequestAPI strategy would normally be chosen, but
// --concierge-mode=ImpersonationProxy should force it to be skipped.
{
Type: "SomeType",
Status: configv1alpha1.SuccessStrategyStatus,
Reason: "SomeReason",
Message: "Some message",
LastUpdateTime: metav1.Now(),
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.TokenCredentialRequestAPIFrontendType,
TokenCredentialRequestAPIInfo: &configv1alpha1.TokenCredentialRequestAPIInfo{
Server: "https://token-credential-request-api-endpoint.test",
CertificateAuthorityData: "dGVzdC10Y3ItYXBpLWNh",
},
},
},
// The endpoint and CA from this impersonation proxy strategy should be autodiscovered.
{
Type: "SomeOtherType",
Status: configv1alpha1.SuccessStrategyStatus,
Reason: "SomeOtherReason",
Message: "Some other message",
LastUpdateTime: metav1.Now(),
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.ImpersonationProxyFrontendType,
ImpersonationProxyInfo: &configv1alpha1.ImpersonationProxyInfo{
Endpoint: "https://impersonation-proxy-endpoint.test",
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
},
},
},
},
},
},
&conciergev1alpha1.JWTAuthenticator{
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
Spec: conciergev1alpha1.JWTAuthenticatorSpec{
Issuer: "https://example.com/issuer",
Audience: "test-audience",
TLS: &conciergev1alpha1.TLSSpec{
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testOIDCCA.Bundle()),
},
},
},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://impersonation-proxy-endpoint.test"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=1`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
`"level"=0 "msg"="discovered OIDC issuer" "issuer"="https://example.com/issuer"`,
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
},
wantStdout: here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: %s
server: https://impersonation-proxy-endpoint.test
name: pinniped
contexts:
- context:
cluster: pinniped
user: pinniped
name: pinniped
current-context: pinniped
kind: Config
preferences: {}
users:
- name: pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --enable-concierge
- --concierge-api-group-suffix=pinniped.dev
- --concierge-authenticator-name=test-authenticator
- --concierge-authenticator-type=jwt
- --concierge-endpoint=https://impersonation-proxy-endpoint.test
- --concierge-ca-bundle-data=%s
- --issuer=https://example.com/issuer
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
base64.StdEncoding.EncodeToString(testOIDCCA.Bundle()),
),
},
{
name: "autodetect impersonation proxy with autodiscovered JWT authenticator",
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
},
conciergeObjects: []runtime.Object{
&configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
Strategies: []configv1alpha1.CredentialIssuerStrategy{
{
Type: "SomeType",
Status: configv1alpha1.SuccessStrategyStatus,
Reason: "SomeReason",
Message: "Some message",
LastUpdateTime: metav1.Now(),
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.ImpersonationProxyFrontendType,
ImpersonationProxyInfo: &configv1alpha1.ImpersonationProxyInfo{
Endpoint: "https://impersonation-proxy-endpoint.test",
CertificateAuthorityData: "dGVzdC1jb25jaWVyZ2UtY2E=",
},
},
},
{
Type: "SomeOtherType",
Status: configv1alpha1.SuccessStrategyStatus,
Reason: "SomeOtherReason",
Message: "Some other message",
LastUpdateTime: metav1.Now(),
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.ImpersonationProxyFrontendType,
ImpersonationProxyInfo: &configv1alpha1.ImpersonationProxyInfo{
Endpoint: "https://some-other-impersonation-endpoint",
CertificateAuthorityData: "dGVzdC1jb25jaWVyZ2UtY2E=",
},
},
},
},
},
},
&conciergev1alpha1.JWTAuthenticator{
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
Spec: conciergev1alpha1.JWTAuthenticatorSpec{
Issuer: "https://example.com/issuer",
Audience: "test-audience",
TLS: &conciergev1alpha1.TLSSpec{
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testOIDCCA.Bundle()),
},
},
},
},
wantLogs: []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in impersonation proxy mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://impersonation-proxy-endpoint.test"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
`"level"=0 "msg"="discovered OIDC issuer" "issuer"="https://example.com/issuer"`,
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
},
wantStdout: here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: dGVzdC1jb25jaWVyZ2UtY2E=
server: https://impersonation-proxy-endpoint.test
name: pinniped
contexts:
- context:
cluster: pinniped
user: pinniped
name: pinniped
current-context: pinniped
kind: Config
preferences: {}
users:
- name: pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --enable-concierge
- --concierge-api-group-suffix=pinniped.dev
- --concierge-authenticator-name=test-authenticator
- --concierge-authenticator-type=jwt
- --concierge-endpoint=https://impersonation-proxy-endpoint.test
- --concierge-ca-bundle-data=dGVzdC1jb25jaWVyZ2UtY2E=
- --issuer=https://example.com/issuer
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`, base64.StdEncoding.EncodeToString(testOIDCCA.Bundle())),
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
testLog := testlogger.New(t)
cmd := kubeconfigCommand(kubeconfigDeps{
getPathToSelf: func() (string, error) {
if tt.getPathToSelfErr != nil {
@@ -516,10 +1091,11 @@ func TestGetKubeconfig(t *testing.T) {
}
fake := fakeconciergeclientset.NewSimpleClientset(tt.conciergeObjects...)
if len(tt.conciergeReactions) > 0 {
fake.ReactionChain = tt.conciergeReactions
fake.ReactionChain = append(tt.conciergeReactions, fake.ReactionChain...)
}
return fake, nil
},
log: testLog,
})
require.NotNil(t, cmd)
@@ -533,6 +1109,7 @@ func TestGetKubeconfig(t *testing.T) {
} else {
require.NoError(t, err)
}
testLog.Expect(tt.wantLogs)
require.Equal(t, tt.wantStdout, stdout.String(), "unexpected stdout")
require.Equal(t, tt.wantStderr, stderr.String(), "unexpected stderr")
})

View File

@@ -22,6 +22,7 @@ import (
clientauthv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
"k8s.io/klog/v2/klogr"
"go.pinniped.dev/internal/groupsuffix"
"go.pinniped.dev/pkg/conciergeclient"
"go.pinniped.dev/pkg/oidcclient"
"go.pinniped.dev/pkg/oidcclient/filesession"
@@ -84,16 +85,16 @@ func oidcLoginCommand(deps oidcLoginCommandDeps) *cobra.Command {
cmd.Flags().BoolVar(&flags.skipBrowser, "skip-browser", false, "Skip opening the browser (just print the URL)")
cmd.Flags().StringVar(&flags.sessionCachePath, "session-cache", filepath.Join(mustGetConfigDir(), "sessions.yaml"), "Path to session cache file")
cmd.Flags().StringSliceVar(&flags.caBundlePaths, "ca-bundle", nil, "Path to TLS certificate authority bundle (PEM format, optional, can be repeated)")
cmd.Flags().StringSliceVar(&flags.caBundleData, "ca-bundle-data", nil, "Base64 endcoded TLS certificate authority bundle (base64 encoded PEM format, optional, can be repeated)")
cmd.Flags().StringSliceVar(&flags.caBundleData, "ca-bundle-data", nil, "Base64 encoded TLS certificate authority bundle (base64 encoded PEM format, optional, can be repeated)")
cmd.Flags().BoolVar(&flags.debugSessionCache, "debug-session-cache", false, "Print debug logs related to the session cache")
cmd.Flags().StringVar(&flags.requestAudience, "request-audience", "", "Request a token with an alternate audience using RFC8693 token exchange")
cmd.Flags().BoolVar(&flags.conciergeEnabled, "enable-concierge", false, "Exchange the OIDC ID token with the Pinniped concierge during login")
cmd.Flags().StringVar(&conciergeNamespace, "concierge-namespace", "pinniped-concierge", "Namespace in which the concierge was installed")
cmd.Flags().BoolVar(&flags.conciergeEnabled, "enable-concierge", false, "Use the Concierge to login")
cmd.Flags().StringVar(&conciergeNamespace, "concierge-namespace", "pinniped-concierge", "Namespace in which the Concierge was installed")
cmd.Flags().StringVar(&flags.conciergeAuthenticatorType, "concierge-authenticator-type", "", "Concierge authenticator type (e.g., 'webhook', 'jwt')")
cmd.Flags().StringVar(&flags.conciergeAuthenticatorName, "concierge-authenticator-name", "", "Concierge authenticator name")
cmd.Flags().StringVar(&flags.conciergeEndpoint, "concierge-endpoint", "", "API base for the Pinniped concierge endpoint")
cmd.Flags().StringVar(&flags.conciergeCABundle, "concierge-ca-bundle-data", "", "CA bundle to use when connecting to the concierge")
cmd.Flags().StringVar(&flags.conciergeAPIGroupSuffix, "concierge-api-group-suffix", "pinniped.dev", "Concierge API group suffix")
cmd.Flags().StringVar(&flags.conciergeEndpoint, "concierge-endpoint", "", "API base for the Concierge endpoint")
cmd.Flags().StringVar(&flags.conciergeCABundle, "concierge-ca-bundle-data", "", "CA bundle to use when connecting to the Concierge")
cmd.Flags().StringVar(&flags.conciergeAPIGroupSuffix, "concierge-api-group-suffix", groupsuffix.PinnipedDefaultSuffix, "Concierge API group suffix")
mustMarkHidden(cmd, "debug-session-cache")
mustMarkRequired(cmd, "issuer")
@@ -143,7 +144,7 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
conciergeclient.WithAPIGroupSuffix(flags.conciergeAPIGroupSuffix),
)
if err != nil {
return fmt.Errorf("invalid concierge parameters: %w", err)
return fmt.Errorf("invalid Concierge parameters: %w", err)
}
}
@@ -171,17 +172,18 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
cred := tokenCredential(token)
// If the concierge was configured, exchange the credential for a separate short-lived, cluster-specific credential.
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if concierge != nil {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
cred, err = deps.exchangeToken(ctx, concierge, token.IDToken.Token)
if err != nil {
return fmt.Errorf("could not complete concierge credential exchange: %w", err)
return fmt.Errorf("could not complete Concierge credential exchange: %w", err)
}
}
return json.NewEncoder(cmd.OutOrStdout()).Encode(cred)
}
func makeClient(caBundlePaths []string, caBundleData []string) (*http.Client, error) {
pool := x509.NewCertPool()
for _, p := range caBundlePaths {

View File

@@ -6,7 +6,6 @@ package cmd
import (
"bytes"
"context"
"crypto/x509/pkix"
"encoding/base64"
"fmt"
"io/ioutil"
@@ -29,7 +28,7 @@ import (
func TestLoginOIDCCommand(t *testing.T) {
cfgDir := mustGetConfigDir()
testCA, err := certauthority.New(pkix.Name{CommonName: "Test CA"}, 1*time.Hour)
testCA, err := certauthority.New("Test CA", 1*time.Hour)
require.NoError(t, err)
tmpdir := testutil.TempDir(t)
testCABundlePath := filepath.Join(tmpdir, "testca.pem")
@@ -58,14 +57,14 @@ func TestLoginOIDCCommand(t *testing.T) {
Flags:
--ca-bundle strings Path to TLS certificate authority bundle (PEM format, optional, can be repeated)
--ca-bundle-data strings Base64 endcoded TLS certificate authority bundle (base64 encoded PEM format, optional, can be repeated)
--ca-bundle-data strings Base64 encoded TLS certificate authority bundle (base64 encoded PEM format, optional, can be repeated)
--client-id string OpenID Connect client ID (default "pinniped-cli")
--concierge-api-group-suffix string Concierge API group suffix (default "pinniped.dev")
--concierge-authenticator-name string Concierge authenticator name
--concierge-authenticator-type string Concierge authenticator type (e.g., 'webhook', 'jwt')
--concierge-ca-bundle-data string CA bundle to use when connecting to the concierge
--concierge-endpoint string API base for the Pinniped concierge endpoint
--enable-concierge Exchange the OIDC ID token with the Pinniped concierge during login
--concierge-ca-bundle-data string CA bundle to use when connecting to the Concierge
--concierge-endpoint string API base for the Concierge endpoint
--enable-concierge Use the Concierge to login
-h, --help help for oidc
--issuer string OpenID Connect issuer URL
--listen-port uint16 TCP port for localhost listener (authorization code flow only)
@@ -92,7 +91,7 @@ func TestLoginOIDCCommand(t *testing.T) {
},
wantError: true,
wantStderr: here.Doc(`
Error: invalid concierge parameters: endpoint must not be empty
Error: invalid Concierge parameters: endpoint must not be empty
`),
},
{
@@ -120,7 +119,7 @@ func TestLoginOIDCCommand(t *testing.T) {
`),
},
{
name: "invalid api group suffix",
name: "invalid API group suffix",
args: []string{
"--issuer", "test-issuer",
"--enable-concierge",
@@ -131,7 +130,7 @@ func TestLoginOIDCCommand(t *testing.T) {
},
wantError: true,
wantStderr: here.Doc(`
Error: invalid concierge parameters: invalid api group suffix: a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')
Error: invalid Concierge parameters: invalid API group suffix: a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')
`),
},
{
@@ -161,7 +160,7 @@ func TestLoginOIDCCommand(t *testing.T) {
wantOptionsCount: 3,
wantError: true,
wantStderr: here.Doc(`
Error: could not complete concierge credential exchange: some concierge error
Error: could not complete Concierge credential exchange: some concierge error
`),
},
{

View File

@@ -14,6 +14,7 @@ import (
"github.com/spf13/cobra"
clientauthv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
"go.pinniped.dev/internal/groupsuffix"
"go.pinniped.dev/pkg/conciergeclient"
"go.pinniped.dev/pkg/oidcclient/oidctypes"
)
@@ -61,13 +62,14 @@ func staticLoginCommand(deps staticLoginDeps) *cobra.Command {
)
cmd.Flags().StringVar(&flags.staticToken, "token", "", "Static token to present during login")
cmd.Flags().StringVar(&flags.staticTokenEnvName, "token-env", "", "Environment variable containing a static token")
cmd.Flags().BoolVar(&flags.conciergeEnabled, "enable-concierge", false, "Exchange the token with the Pinniped concierge during login")
cmd.Flags().StringVar(&conciergeNamespace, "concierge-namespace", "pinniped-concierge", "Namespace in which the concierge was installed")
cmd.Flags().BoolVar(&flags.conciergeEnabled, "enable-concierge", false, "Use the Concierge to login")
cmd.Flags().StringVar(&conciergeNamespace, "concierge-namespace", "pinniped-concierge", "Namespace in which the Concierge was installed")
cmd.Flags().StringVar(&flags.conciergeAuthenticatorType, "concierge-authenticator-type", "", "Concierge authenticator type (e.g., 'webhook', 'jwt')")
cmd.Flags().StringVar(&flags.conciergeAuthenticatorName, "concierge-authenticator-name", "", "Concierge authenticator name")
cmd.Flags().StringVar(&flags.conciergeEndpoint, "concierge-endpoint", "", "API base for the Pinniped concierge endpoint")
cmd.Flags().StringVar(&flags.conciergeCABundle, "concierge-ca-bundle-data", "", "CA bundle to use when connecting to the concierge")
cmd.Flags().StringVar(&flags.conciergeAPIGroupSuffix, "concierge-api-group-suffix", "pinniped.dev", "Concierge API group suffix")
cmd.Flags().StringVar(&flags.conciergeEndpoint, "concierge-endpoint", "", "API base for the Concierge endpoint")
cmd.Flags().StringVar(&flags.conciergeCABundle, "concierge-ca-bundle-data", "", "CA bundle to use when connecting to the Concierge")
cmd.Flags().StringVar(&flags.conciergeAPIGroupSuffix, "concierge-api-group-suffix", groupsuffix.PinnipedDefaultSuffix, "Concierge API group suffix")
cmd.RunE = func(cmd *cobra.Command, args []string) error { return runStaticLogin(cmd.OutOrStdout(), deps, flags) }
mustMarkDeprecated(cmd, "concierge-namespace", "not needed anymore")
@@ -91,7 +93,7 @@ func runStaticLogin(out io.Writer, deps staticLoginDeps, flags staticLoginParams
conciergeclient.WithAPIGroupSuffix(flags.conciergeAPIGroupSuffix),
)
if err != nil {
return fmt.Errorf("invalid concierge parameters: %w", err)
return fmt.Errorf("invalid Concierge parameters: %w", err)
}
}
@@ -111,7 +113,7 @@ func runStaticLogin(out io.Writer, deps staticLoginDeps, flags staticLoginParams
}
cred := tokenCredential(&oidctypes.Token{IDToken: &oidctypes.IDToken{Token: token}})
// Exchange that token with the concierge, if configured.
// If the concierge was configured, exchange the credential for a separate short-lived, cluster-specific credential.
if concierge != nil {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
@@ -119,7 +121,7 @@ func runStaticLogin(out io.Writer, deps staticLoginDeps, flags staticLoginParams
var err error
cred, err = deps.exchangeToken(ctx, concierge, token)
if err != nil {
return fmt.Errorf("could not complete concierge credential exchange: %w", err)
return fmt.Errorf("could not complete Concierge credential exchange: %w", err)
}
}
return json.NewEncoder(out).Encode(cred)

View File

@@ -6,7 +6,6 @@ package cmd
import (
"bytes"
"context"
"crypto/x509/pkix"
"fmt"
"io/ioutil"
"path/filepath"
@@ -24,7 +23,7 @@ import (
)
func TestLoginStaticCommand(t *testing.T) {
testCA, err := certauthority.New(pkix.Name{CommonName: "Test CA"}, 1*time.Hour)
testCA, err := certauthority.New("Test CA", 1*time.Hour)
require.NoError(t, err)
tmpdir := testutil.TempDir(t)
testCABundlePath := filepath.Join(tmpdir, "testca.pem")
@@ -54,9 +53,9 @@ func TestLoginStaticCommand(t *testing.T) {
--concierge-api-group-suffix string Concierge API group suffix (default "pinniped.dev")
--concierge-authenticator-name string Concierge authenticator name
--concierge-authenticator-type string Concierge authenticator type (e.g., 'webhook', 'jwt')
--concierge-ca-bundle-data string CA bundle to use when connecting to the concierge
--concierge-endpoint string API base for the Pinniped concierge endpoint
--enable-concierge Exchange the token with the Pinniped concierge during login
--concierge-ca-bundle-data string CA bundle to use when connecting to the Concierge
--concierge-endpoint string API base for the Concierge endpoint
--enable-concierge Use the Concierge to login
-h, --help help for static
--token string Static token to present during login
--token-env string Environment variable containing a static token
@@ -78,7 +77,7 @@ func TestLoginStaticCommand(t *testing.T) {
},
wantError: true,
wantStderr: here.Doc(`
Error: invalid concierge parameters: endpoint must not be empty
Error: invalid Concierge parameters: endpoint must not be empty
`),
},
{
@@ -126,11 +125,11 @@ func TestLoginStaticCommand(t *testing.T) {
conciergeErr: fmt.Errorf("some concierge error"),
wantError: true,
wantStderr: here.Doc(`
Error: could not complete concierge credential exchange: some concierge error
Error: could not complete Concierge credential exchange: some concierge error
`),
},
{
name: "invalid api group suffix",
name: "invalid API group suffix",
args: []string{
"--token", "test-token",
"--enable-concierge",
@@ -141,7 +140,7 @@ func TestLoginStaticCommand(t *testing.T) {
},
wantError: true,
wantStderr: here.Doc(`
Error: invalid concierge parameters: invalid api group suffix: a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')
Error: invalid Concierge parameters: invalid API group suffix: a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')
`),
},
{

191
cmd/pinniped/cmd/whoami.go Normal file
View File

@@ -0,0 +1,191 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"context"
"fmt"
"io"
"os"
"strings"
"time"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/client-go/tools/clientcmd"
identityv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/identity/v1alpha1"
conciergescheme "go.pinniped.dev/internal/concierge/scheme"
"go.pinniped.dev/internal/groupsuffix"
"go.pinniped.dev/internal/here"
)
//nolint: gochecknoinits
func init() {
rootCmd.AddCommand(newWhoamiCommand(getRealConciergeClientset))
}
type whoamiFlags struct {
outputFormat string // e.g., yaml, json, text
kubeconfigPath string
kubeconfigContextOverride string
apiGroupSuffix string
}
type clusterInfo struct {
name string
url string
}
func newWhoamiCommand(getClientset getConciergeClientsetFunc) *cobra.Command {
cmd := &cobra.Command{
Args: cobra.NoArgs, // do not accept positional arguments for this command
Use: "whoami",
Short: "Print information about the current user",
SilenceUsage: true,
}
flags := &whoamiFlags{}
// flags
f := cmd.Flags()
f.StringVarP(&flags.outputFormat, "output", "o", "text", "Output format (e.g., 'yaml', 'json', 'text')")
f.StringVar(&flags.kubeconfigPath, "kubeconfig", os.Getenv("KUBECONFIG"), "Path to kubeconfig file")
f.StringVar(&flags.kubeconfigContextOverride, "kubeconfig-context", "", "Kubeconfig context name (default: current active context)")
f.StringVar(&flags.apiGroupSuffix, "api-group-suffix", groupsuffix.PinnipedDefaultSuffix, "Concierge API group suffix")
cmd.RunE = func(cmd *cobra.Command, _ []string) error {
return runWhoami(cmd.OutOrStdout(), getClientset, flags)
}
return cmd
}
func runWhoami(output io.Writer, getClientset getConciergeClientsetFunc, flags *whoamiFlags) error {
clientConfig := newClientConfig(flags.kubeconfigPath, flags.kubeconfigContextOverride)
clientset, err := getClientset(clientConfig, flags.apiGroupSuffix)
if err != nil {
return fmt.Errorf("could not configure Kubernetes client: %w", err)
}
clusterInfo, err := getCurrentCluster(clientConfig, flags.kubeconfigContextOverride)
if err != nil {
return fmt.Errorf("could not get current cluster info: %w", err)
}
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*20)
defer cancelFunc()
whoAmI, err := clientset.IdentityV1alpha1().WhoAmIRequests().Create(ctx, &identityv1alpha1.WhoAmIRequest{}, metav1.CreateOptions{})
if err != nil {
hint := ""
if errors.IsNotFound(err) {
hint = " (is the Pinniped WhoAmI API running and healthy?)"
}
return fmt.Errorf("could not complete WhoAmIRequest%s: %w", hint, err)
}
if err := writeWhoamiOutput(output, flags, clusterInfo, whoAmI); err != nil {
return fmt.Errorf("could not write output: %w", err)
}
return nil
}
func getCurrentCluster(clientConfig clientcmd.ClientConfig, currentContextNameOverride string) (*clusterInfo, error) {
currentKubeConfig, err := clientConfig.RawConfig()
if err != nil {
return nil, err
}
contextName := currentKubeConfig.CurrentContext
if len(currentContextNameOverride) > 0 {
contextName = currentContextNameOverride
}
unknownClusterInfo := &clusterInfo{name: "???", url: "???"}
ctx, ok := currentKubeConfig.Contexts[contextName]
if !ok {
return unknownClusterInfo, nil
}
cluster, ok := currentKubeConfig.Clusters[ctx.Cluster]
if !ok {
return unknownClusterInfo, nil
}
return &clusterInfo{name: ctx.Cluster, url: cluster.Server}, nil
}
func writeWhoamiOutput(output io.Writer, flags *whoamiFlags, cInfo *clusterInfo, whoAmI *identityv1alpha1.WhoAmIRequest) error {
switch flags.outputFormat {
case "text":
return writeWhoamiOutputText(output, cInfo, whoAmI)
case "json":
return writeWhoamiOutputJSON(output, flags.apiGroupSuffix, whoAmI)
case "yaml":
return writeWhoamiOutputYAML(output, flags.apiGroupSuffix, whoAmI)
default:
return fmt.Errorf("unknown output format: %q", flags.outputFormat)
}
}
func writeWhoamiOutputText(output io.Writer, clusterInfo *clusterInfo, whoAmI *identityv1alpha1.WhoAmIRequest) error {
fmt.Fprint(output, here.Docf(`
Current cluster info:
Name: %s
URL: %s
Current user info:
Username: %s
Groups: %s
`, clusterInfo.name, clusterInfo.url, whoAmI.Status.KubernetesUserInfo.User.Username, prettyStrings(whoAmI.Status.KubernetesUserInfo.User.Groups)))
return nil
}
func writeWhoamiOutputJSON(output io.Writer, apiGroupSuffix string, whoAmI *identityv1alpha1.WhoAmIRequest) error {
return serialize(output, apiGroupSuffix, whoAmI, runtime.ContentTypeJSON)
}
func writeWhoamiOutputYAML(output io.Writer, apiGroupSuffix string, whoAmI *identityv1alpha1.WhoAmIRequest) error {
return serialize(output, apiGroupSuffix, whoAmI, runtime.ContentTypeYAML)
}
func serialize(output io.Writer, apiGroupSuffix string, whoAmI *identityv1alpha1.WhoAmIRequest, contentType string) error {
scheme, _, identityGV := conciergescheme.New(apiGroupSuffix)
codecs := serializer.NewCodecFactory(scheme)
respInfo, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), contentType)
if !ok {
return fmt.Errorf("unknown content type: %q", contentType)
}
// I have seen the pretty serializer be nil before, so this will hopefully protect against that
// corner.
serializer := respInfo.PrettySerializer
if serializer == nil {
serializer = respInfo.Serializer
}
// Ensure that these fields are set so that the JSON/YAML output tells the full story.
whoAmI.APIVersion = identityGV.String()
whoAmI.Kind = "WhoAmIRequest"
return serializer.Encode(whoAmI, output)
}
func prettyStrings(ss []string) string {
b := &strings.Builder{}
for i, s := range ss {
if i != 0 {
b.WriteString(", ")
}
b.WriteString(s)
}
return b.String()
}

View File

@@ -0,0 +1,327 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"bytes"
"testing"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
kubetesting "k8s.io/client-go/testing"
"k8s.io/client-go/tools/clientcmd"
identityv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/identity/v1alpha1"
conciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
fakeconciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake"
"go.pinniped.dev/internal/constable"
"go.pinniped.dev/internal/here"
)
func TestWhoami(t *testing.T) {
tests := []struct {
name string
args []string
groupsOverride []string
gettingClientsetErr error
callingAPIErr error
wantError bool
wantStdout, wantStderr string
}{
{
name: "help flag",
args: []string{"--help"},
wantStdout: here.Doc(`
Print information about the current user
Usage:
whoami [flags]
Flags:
--api-group-suffix string Concierge API group suffix (default "pinniped.dev")
-h, --help help for whoami
--kubeconfig string Path to kubeconfig file
--kubeconfig-context string Kubeconfig context name (default: current active context)
-o, --output string Output format (e.g., 'yaml', 'json', 'text') (default "text")
`),
},
{
name: "text output",
args: []string{"--kubeconfig", "testdata/kubeconfig.yaml"},
wantStdout: here.Doc(`
Current cluster info:
Name: kind-kind
URL: https://fake-server-url-value
Current user info:
Username: some-username
Groups: some-group-0, some-group-1
`),
},
{
name: "text output with long output flag",
args: []string{"--kubeconfig", "testdata/kubeconfig.yaml", "--output", "text"},
wantStdout: here.Doc(`
Current cluster info:
Name: kind-kind
URL: https://fake-server-url-value
Current user info:
Username: some-username
Groups: some-group-0, some-group-1
`),
},
{
name: "text output with 1 group",
args: []string{"--kubeconfig", "testdata/kubeconfig.yaml", "--output", "text"},
groupsOverride: []string{"some-group-0"},
wantStdout: here.Doc(`
Current cluster info:
Name: kind-kind
URL: https://fake-server-url-value
Current user info:
Username: some-username
Groups: some-group-0
`),
},
{
name: "text output with no groups",
args: []string{"--kubeconfig", "testdata/kubeconfig.yaml", "--output", "text"},
groupsOverride: []string{},
wantStdout: here.Doc(`
Current cluster info:
Name: kind-kind
URL: https://fake-server-url-value
Current user info:
Username: some-username
Groups:
`),
},
{
name: "json output",
args: []string{"--kubeconfig", "testdata/kubeconfig.yaml", "-o", "json"},
wantStdout: here.Doc(`
{
"kind": "WhoAmIRequest",
"apiVersion": "identity.concierge.pinniped.dev/v1alpha1",
"metadata": {
"creationTimestamp": null
},
"spec": {},
"status": {
"kubernetesUserInfo": {
"user": {
"username": "some-username",
"groups": [
"some-group-0",
"some-group-1"
]
}
}
}
}`),
},
{
name: "json output with api group suffix flag",
args: []string{"--kubeconfig", "testdata/kubeconfig.yaml", "-o", "json", "--api-group-suffix", "tuna.io"},
wantStdout: here.Doc(`
{
"kind": "WhoAmIRequest",
"apiVersion": "identity.concierge.tuna.io/v1alpha1",
"metadata": {
"creationTimestamp": null
},
"spec": {},
"status": {
"kubernetesUserInfo": {
"user": {
"username": "some-username",
"groups": [
"some-group-0",
"some-group-1"
]
}
}
}
}`),
},
{
name: "yaml output",
args: []string{"--kubeconfig", "testdata/kubeconfig.yaml", "-o", "yaml"},
wantStdout: here.Doc(`
apiVersion: identity.concierge.pinniped.dev/v1alpha1
kind: WhoAmIRequest
metadata:
creationTimestamp: null
spec: {}
status:
kubernetesUserInfo:
user:
groups:
- some-group-0
- some-group-1
username: some-username
`),
},
{
name: "yaml output with api group suffix",
args: []string{"--kubeconfig", "testdata/kubeconfig.yaml", "-o", "yaml", "--api-group-suffix", "tuna.io"},
wantStdout: here.Doc(`
apiVersion: identity.concierge.tuna.io/v1alpha1
kind: WhoAmIRequest
metadata:
creationTimestamp: null
spec: {}
status:
kubernetesUserInfo:
user:
groups:
- some-group-0
- some-group-1
username: some-username
`),
},
{
name: "extra args",
args: []string{"extra-arg"},
wantError: true,
wantStderr: "Error: unknown command \"extra-arg\" for \"whoami\"\n",
},
{
name: "cannot get cluster info",
args: []string{"--kubeconfig", "this-file-does-not-exist"},
wantError: true,
wantStderr: "Error: could not get current cluster info: stat this-file-does-not-exist: no such file or directory\n",
},
{
name: "different kubeconfig context, but same as current",
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--kubeconfig-context", "kind-kind",
},
wantStdout: here.Doc(`
Current cluster info:
Name: kind-kind
URL: https://fake-server-url-value
Current user info:
Username: some-username
Groups: some-group-0, some-group-1
`),
},
{
name: "different kubeconfig context, not current",
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--kubeconfig-context", "some-other-context",
},
wantStdout: here.Doc(`
Current cluster info:
Name: some-other-cluster
URL: https://some-other-fake-server-url-value
Current user info:
Username: some-username
Groups: some-group-0, some-group-1
`),
},
{
name: "invalid kubeconfig context prints ???",
args: []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--kubeconfig-context", "invalid",
},
wantStdout: here.Doc(`
Current cluster info:
Name: ???
URL: ???
Current user info:
Username: some-username
Groups: some-group-0, some-group-1
`),
},
{
name: "getting clientset fails",
gettingClientsetErr: constable.Error("some get clientset error"),
wantError: true,
wantStderr: "Error: could not configure Kubernetes client: some get clientset error\n",
},
{
name: "calling API fails",
callingAPIErr: constable.Error("some API error"),
wantError: true,
wantStderr: "Error: could not complete WhoAmIRequest: some API error\n",
},
{
name: "calling API fails because WhoAmI API is not installed",
callingAPIErr: errors.NewNotFound(identityv1alpha1.SchemeGroupVersion.WithResource("whoamirequests").GroupResource(), "whatever"),
wantError: true,
wantStderr: "Error: could not complete WhoAmIRequest (is the Pinniped WhoAmI API running and healthy?): whoamirequests.identity.concierge.pinniped.dev \"whatever\" not found\n",
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
getClientset := func(clientConfig clientcmd.ClientConfig, apiGroupSuffix string) (conciergeclientset.Interface, error) {
if test.gettingClientsetErr != nil {
return nil, test.gettingClientsetErr
}
clientset := fakeconciergeclientset.NewSimpleClientset()
clientset.PrependReactor("create", "whoamirequests", func(_ kubetesting.Action) (bool, runtime.Object, error) {
if test.callingAPIErr != nil {
return true, nil, test.callingAPIErr
}
groups := []string{"some-group-0", "some-group-1"}
if test.groupsOverride != nil {
groups = test.groupsOverride
}
return true, &identityv1alpha1.WhoAmIRequest{
Status: identityv1alpha1.WhoAmIRequestStatus{
KubernetesUserInfo: identityv1alpha1.KubernetesUserInfo{
User: identityv1alpha1.UserInfo{
Username: "some-username",
Groups: groups,
},
},
},
}, nil
})
return clientset, nil
}
cmd := newWhoamiCommand(getClientset)
stdout, stderr := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmd.SetOut(stdout)
cmd.SetErr(stderr)
cmd.SetArgs(test.args)
err := cmd.Execute()
if test.wantError {
require.Error(t, err)
} else {
require.NoError(t, err)
}
require.Equal(t, test.wantStdout, stdout.String())
require.Equal(t, test.wantStderr, stderr.String())
})
}
}

View File

@@ -1,39 +1,3 @@
# Deploying
# Pinniped Concierge Deployment
## Connecting Pinniped to an Identity Provider
If you would like to try Pinniped, but you don't have a compatible identity provider,
you can use Pinniped's test identity provider.
See [deploy/local-user-authenticator/README.md](../../deploy/local-user-authenticator/README.md)
for details.
## Installing the Latest Version with Default Options
```bash
kubectl apply -f https://get.pinniped.dev/latest/install-pinniped-concierge.yaml
```
## Installing a Specific Version with Default Options
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.
```bash
# Replace v0.4.1 with your preferred version in the URL below
kubectl apply -f https://get.pinniped.dev/v0.4.1/install-pinniped-concierge.yaml
```
## Installing with Custom Options
Creating your own deployment YAML file requires `ytt` from [Carvel](https://carvel.dev/) to template the YAML files
in the `deploy/concierge` directory.
Either [install `ytt`](https://get-ytt.io/) or use the [container image from Dockerhub](https://hub.docker.com/r/k14s/image/tags).
1. `git clone` this repo and `git checkout` the release version tag of the release that you would like to deploy.
1. The configuration options are in [deploy/concierge/values.yml](values.yaml).
Fill in the values in that file, or override those values using additional `ytt` command-line options in
the command below. Use the release version tag as the `image_tag` value.
2. In a terminal, cd to this `deploy/concierge` directory
3. To generate the final YAML files, run `ytt --file .`
4. Deploy the generated YAML using your preferred deployment tool, such as `kubectl` or [`kapp`](https://get-kapp.io/).
For example: `ytt --file . | kapp deploy --yes --app pinniped --diff-changes --file -`
See [the how-to guide for details](https://pinniped.dev/docs/howto/install-concierge/).

View File

@@ -40,7 +40,8 @@ spec:
properties:
kubeConfigInfo:
description: Information needed to form a valid Pinniped-based kubeconfig
using this credential issuer.
using this credential issuer. This field is deprecated and will
be removed in a future version.
properties:
certificateAuthorityData:
description: The K8s API server CA bundle.
@@ -62,6 +63,59 @@ spec:
description: Status of an integration strategy that was attempted
by Pinniped.
properties:
frontend:
description: Frontend describes how clients can connect using
this strategy.
properties:
impersonationProxyInfo:
description: ImpersonationProxyInfo describes the parameters
for the impersonation proxy on this Concierge. This field
is only set when Type is "ImpersonationProxy".
properties:
certificateAuthorityData:
description: CertificateAuthorityData is the base64-encoded
PEM CA bundle of the impersonation proxy.
minLength: 1
type: string
endpoint:
description: Endpoint is the HTTPS endpoint of the impersonation
proxy.
minLength: 1
pattern: ^https://
type: string
required:
- certificateAuthorityData
- endpoint
type: object
tokenCredentialRequestInfo:
description: TokenCredentialRequestAPIInfo describes the
parameters for the TokenCredentialRequest API on this
Concierge. This field is only set when Type is "TokenCredentialRequestAPI".
properties:
certificateAuthorityData:
description: CertificateAuthorityData is the base64-encoded
Kubernetes API server CA bundle.
minLength: 1
type: string
server:
description: Server is the Kubernetes API server URL.
minLength: 1
pattern: ^https://|^http://
type: string
required:
- certificateAuthorityData
- server
type: object
type:
description: Type describes which frontend mechanism clients
can use with a strategy.
enum:
- TokenCredentialRequestAPI
- ImpersonationProxy
type: string
required:
- type
type: object
lastUpdateTime:
description: When the status was last checked.
format: date-time
@@ -73,8 +127,13 @@ spec:
reason:
description: Reason for the current status.
enum:
- FetchedKey
- Listening
- Pending
- Disabled
- ErrorDuringSetup
- CouldNotFetchKey
- CouldNotGetClusterInfo
- FetchedKey
type: string
status:
description: Status of the attempted integration strategy.
@@ -86,6 +145,7 @@ spec:
description: Type of integration attempted.
enum:
- KubeClusterSigningCertificate
- ImpersonationProxy
type: string
required:
- lastUpdateTime

View File

@@ -42,6 +42,11 @@ data:
servingCertificateSecret: (@= defaultResourceNameWithSuffix("api-tls-serving-certificate") @)
credentialIssuer: (@= defaultResourceNameWithSuffix("config") @)
apiService: (@= defaultResourceNameWithSuffix("api") @)
impersonationConfigMap: (@= defaultResourceNameWithSuffix("impersonation-proxy-config") @)
impersonationLoadBalancerService: (@= defaultResourceNameWithSuffix("impersonation-proxy-load-balancer") @)
impersonationTLSCertificateSecret: (@= defaultResourceNameWithSuffix("impersonation-proxy-tls-serving-certificate") @)
impersonationCACertificateSecret: (@= defaultResourceNameWithSuffix("impersonation-proxy-ca-certificate") @)
impersonationSignerSecret: (@= defaultResourceNameWithSuffix("impersonation-proxy-signer-ca-certificate") @)
labels: (@= json.encode(labels()).rstrip() @)
kubeCertAgent:
namePrefix: (@= defaultResourceNameWithSuffix("kube-cert-agent-") @)
@@ -189,6 +194,20 @@ spec:
port: 443
targetPort: 8443
---
apiVersion: v1
kind: Service
metadata:
name: #@ defaultResourceNameWithSuffix("proxy")
namespace: #@ namespace()
labels: #@ labels()
spec:
type: ClusterIP
selector: #@ defaultLabel()
ports:
- protocol: TCP
port: 443
targetPort: 8444
---
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
@@ -197,8 +216,24 @@ metadata:
spec:
version: v1alpha1
group: #@ pinnipedDevAPIGroupWithPrefix("login.concierge")
groupPriorityMinimum: 2500
versionPriority: 10
groupPriorityMinimum: 9900
versionPriority: 15
#! caBundle: Do not include this key here. Starts out null, will be updated/owned by the golang code.
service:
name: #@ defaultResourceNameWithSuffix("api")
namespace: #@ namespace()
port: 443
---
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
name: #@ pinnipedDevAPIGroupWithPrefix("v1alpha1.identity.concierge")
labels: #@ labels()
spec:
version: v1alpha1
group: #@ pinnipedDevAPIGroupWithPrefix("identity.concierge")
groupPriorityMinimum: 9900
versionPriority: 15
#! caBundle: Do not include this key here. Starts out null, will be updated/owned by the golang code.
service:
name: #@ defaultResourceNameWithSuffix("api")

View File

@@ -31,6 +31,15 @@ rules:
resources: [ securitycontextconstraints ]
verbs: [ use ]
resourceNames: [ nonroot ]
- apiGroups: [ "" ]
resources: [ "users", "groups", "serviceaccounts" ]
verbs: [ "impersonate" ]
- apiGroups: [ "authentication.k8s.io" ]
resources: [ "*" ] #! What we really want is userextras/* but the RBAC authorizer only supports */subresource, not resource/*
verbs: [ "impersonate" ]
- apiGroups: [ "" ]
resources: [ nodes ]
verbs: [ list ]
- apiGroups:
- #@ pinnipedDevAPIGroupWithPrefix("config.concierge")
resources: [ credentialissuers ]
@@ -69,7 +78,7 @@ metadata:
rules:
- apiGroups: [ "" ]
resources: [ services ]
verbs: [ create, get, list, patch, update, watch ]
verbs: [ create, get, list, patch, update, watch, delete ]
- apiGroups: [ "" ]
resources: [ secrets ]
verbs: [ create, get, list, patch, update, watch, delete ]
@@ -81,9 +90,12 @@ rules:
- apiGroups: [ "" ]
resources: [ pods/exec ]
verbs: [ create ]
- apiGroups: [apps]
resources: [replicasets,deployments]
verbs: [get]
- apiGroups: [ apps ]
resources: [ replicasets,deployments ]
verbs: [ get ]
- apiGroups: [ "" ]
resources: [ configmaps ]
verbs: [ list, get, watch ]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
@@ -133,18 +145,22 @@ roleRef:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: #@ defaultResourceNameWithSuffix("create-token-credential-requests")
name: #@ defaultResourceNameWithSuffix("pre-authn-apis")
labels: #@ labels()
rules:
- apiGroups:
- #@ pinnipedDevAPIGroupWithPrefix("login.concierge")
resources: [ tokencredentialrequests ]
verbs: [ create ]
verbs: [ create, list ]
- apiGroups:
- #@ pinnipedDevAPIGroupWithPrefix("identity.concierge")
resources: [ whoamirequests ]
verbs: [ create, list ]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: #@ defaultResourceNameWithSuffix("create-token-credential-requests")
name: #@ defaultResourceNameWithSuffix("pre-authn-apis")
labels: #@ labels()
subjects:
- kind: Group
@@ -155,7 +171,7 @@ subjects:
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: #@ defaultResourceNameWithSuffix("create-token-credential-requests")
name: #@ defaultResourceNameWithSuffix("pre-authn-apis")
apiGroup: rbac.authorization.k8s.io
#! Give permissions for subjectaccessreviews, tokenreview that is needed by aggregated api servers

View File

@@ -55,8 +55,8 @@ api_serving_certificate_renew_before_seconds: 2160000
#! information), trace (timing information), all (kitchen sink).
log_level: #! By default, when this value is left unset, only warnings and errors are printed. There is no way to suppress warning and error logs.
run_as_user: 1001 #! run_as_user specifies the user ID that will own the local-user-authenticator process
run_as_group: 1001 #! run_as_group specifies the group ID that will own the local-user-authenticator process
run_as_user: 1001 #! run_as_user specifies the user ID that will own the process
run_as_group: 1001 #! run_as_group specifies the group ID that will own the process
#! Specify the API group suffix for all Pinniped API groups. By default, this is set to
#! pinniped.dev, so Pinniped API groups will look like foo.pinniped.dev,

View File

@@ -15,5 +15,5 @@ image_tag: latest
#! Optional.
image_pull_dockerconfigjson: #! e.g. {"auths":{"https://registry.example.com":{"username":"USERNAME","password":"PASSWORD","auth":"BASE64_ENCODED_USERNAME_COLON_PASSWORD"}}}
run_as_user: 1001 #! run_as_user specifies the user ID that will own the local-user-authenticator process
run_as_group: 1001 #! run_as_group specifies the group ID that will own the local-user-authenticator process
run_as_user: 1001 #! run_as_user specifies the user ID that will own the process
run_as_group: 1001 #! run_as_group specifies the group ID that will own the process

View File

@@ -1,184 +1,3 @@
# Deploying the Pinniped Supervisor
# Pinniped Supervisor Deployment
## What is the Pinniped Supervisor?
The Pinniped Supervisor app is a component of the Pinniped OIDC and Cluster Federation solutions.
It can be deployed when those features are needed.
## Installing the Latest Version with Default Options
```bash
kubectl apply -f https://get.pinniped.dev/latest/install-pinniped-supervisor.yaml
```
## Installing a Specific Version with Default Options
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.
```bash
# Replace v0.4.1 with your preferred version in the URL below
kubectl apply -f https://get.pinniped.dev/v0.4.1/install-pinniped-supervisor.yaml
```
## Installing with Custom Options
Creating your own deployment YAML file requires `ytt` from [Carvel](https://carvel.dev/) to template the YAML files
in the `deploy/supervisor` directory.
Either [install `ytt`](https://get-ytt.io/) or use the [container image from Dockerhub](https://hub.docker.com/r/k14s/image/tags).
1. `git clone` this repo and `git checkout` the release version tag of the release that you would like to deploy.
1. The configuration options are in [deploy/supervisor/values.yml](values.yaml).
Fill in the values in that file, or override those values using additional `ytt` command-line options in
the command below. Use the release version tag as the `image_tag` value.
2. In a terminal, cd to this `deploy/supervisor` directory
3. To generate the final YAML files, run `ytt --file .`
4. Deploy the generated YAML using your preferred deployment tool, such as `kubectl` or [`kapp`](https://get-kapp.io/).
For example: `ytt --file . | kapp deploy --yes --app pinniped-supervisor --diff-changes --file -`
## Configuring After Installing
### Exposing the Supervisor App as a Service
The Supervisor app's endpoints should be exposed as HTTPS endpoints with proper TLS certificates signed by a
Certificate Authority which will be trusted by your user's web browsers. Because there are
many ways to expose TLS services from a Kubernetes cluster, the Supervisor app leaves this up to the user.
The most common ways are:
1. Define an [`Ingress` resource](https://kubernetes.io/docs/concepts/services-networking/ingress/) with TLS certificates.
In this case, the ingress will terminate TLS. Typically, the ingress will then talk plain HTTP to its backend,
which would be a NodePort or LoadBalancer Service in front of the HTTP port 8080 of the Supervisor pods.
The required configuration of the Ingress is specific to your cluster's Ingress Controller, so please refer to the
documentation from your Kubernetes provider. If you are using a cluster from a cloud provider, then you'll probably
want to start with that provider's documentation. For example, if your cluster is a Google GKE cluster, refer to
the [GKE documentation for Ingress](https://cloud.google.com/kubernetes-engine/docs/concepts/ingress).
Otherwise, the Kubernetes documentation provides a list of popular
[Ingress Controllers](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/), including
[Contour](https://projectcontour.io/) and many others.
1. Or, define a [TCP LoadBalancer Service](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer)
which is a layer 4 load balancer and does not terminate TLS. In this case, the Supervisor app will need to be
configured with TLS certificates and will terminate the TLS connection itself (see the section about FederationDomain
below). The LoadBalancer Service should be configured to use the HTTPS port 443 of the Supervisor pods as its `targetPort`.
*Warning:* Do not expose the Supervisor's port 8080 to the public. It would not be secure for the OIDC protocol
to use HTTP, because the user's secret OIDC tokens would be transmitted across the network without encryption.
1. Or, expose the Supervisor app using a Kubernetes service mesh technology, e.g. [Istio](https://istio.io/).
Please see the documentation for your service mesh. Generally, the setup would be similar to the description
above for defining an ingress, expect the service mesh would probably provide both the ingress with TLS termination
and the service.
For either of the first two options mentioned above, if you installed using `ytt` then you can use
the related `service_*` options from [deploy/supervisor/values.yml](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.
#### Example: Using a LoadBalancer Service
This is an example of creating a LoadBalancer Service to expose port 8443 of the Supervisor app outside the cluster.
```yaml
apiVersion: v1
kind: Service
metadata:
name: pinniped-supervisor-loadbalancer
# Assuming that this is the namespace where the supervisor was installed. This is the default in install-supervisor.yaml.
namespace: pinniped-supervisor
spec:
type: LoadBalancer
selector:
# Assuming that this is how the supervisor pods are labeled. This is the default in install-supervisor.yaml.
app: pinniped-supervisor
ports:
- protocol: TCP
port: 443
targetPort: 8443
```
#### Example: Using a NodePort Service
A NodePort Service exposes the app as a port on the nodes of the cluster.
This is convenient for use with kind clusters, because kind can
[expose node ports as localhost ports on the host machine](https://kind.sigs.k8s.io/docs/user/configuration/#extra-port-mappings)
without requiring an Ingress, although
[kind also supports several Ingress Controllers](https://kind.sigs.k8s.io/docs/user/ingress).
A NodePort Service could also be used behind an Ingress which is terminating TLS.
For example:
```yaml
apiVersion: v1
kind: Service
metadata:
name: pinniped-supervisor-nodeport
# Assuming that this is the namespace where the supervisor was installed. This is the default in install-supervisor.yaml.
namespace: pinniped-supervisor
spec:
type: NodePort
selector:
# Assuming that this is how the supervisor pods are labeled. This is the default in install-supervisor.yaml.
app: pinniped-supervisor
ports:
- protocol: TCP
port: 80
targetPort: 8080
nodePort: 31234 # This is the port that you would forward to the kind host. Or omit this key for a random port.
```
### Configuring the Supervisor to Act as an OIDC Provider
The Supervisor can be configured as an OIDC provider by creating `FederationDomain` resources
in the same namespace where the Supervisor app was installed. For example:
```yaml
apiVersion: config.supervisor.pinniped.dev/v1alpha1
kind: FederationDomain
metadata:
name: my-provider
# Assuming that this is the namespace where the supervisor was installed. This is the default in install-supervisor.yaml.
namespace: pinniped-supervisor
spec:
# The hostname would typically match the DNS name of the public ingress or load balancer for the cluster.
# Any path can be specified, which allows a single hostname to have multiple different issuers. The path is optional.
issuer: https://my-issuer.example.com/any/path
# Optionally configure the name of a Secret in the same namespace, of type `kubernetes.io/tls`,
# which contains the TLS serving certificate for the HTTPS endpoints served by this OIDC Provider.
tls:
secretName: my-tls-cert-secret
```
#### Configuring TLS for the Supervisor OIDC Endpoints
If you have terminated TLS outside the app, for example using an Ingress with TLS certificates, then you do not need to
configure TLS certificates on the FederationDomain.
If you are using a LoadBalancer Service to expose the Supervisor app outside your cluster, then you will
also need to configure the Supervisor app to terminate TLS. There are two places to configure TLS certificates:
1. Each `FederationDomain` can be configured with TLS certificates, using the `spec.tls.secretName` field.
1. The default TLS certificate for all OIDC providers can be configured by creating a Secret called
`pinniped-supervisor-default-tls-certificate` in the same namespace in which the Supervisor was installed.
The default TLS certificate will be used for all OIDC providers which did not declare a `spec.tls.secretName`.
Also, the `spec.tls.secretName` will be ignored for incoming requests to the OIDC endpoints
that use an IP address as the host, so those requests will always present the default TLS certificates
to the client. When the request includes the hostname, and that hostname matches the hostname of an `Issuer`,
then the TLS certificate defined by the `spec.tls.secretName` will be used. If that issuer did not
define `spec.tls.secretName` then the default TLS certificate will be used. If neither exists,
then the client will get a TLS error because the server will not present any TLS certificate.
It is recommended that you have a DNS entry for your load balancer or Ingress, and that you configure the
OIDC provider's `Issuer` using that DNS hostname, and that the TLS certificate for that provider also
covers that same hostname.
You can create the certificate Secrets however you like, for example you could use [cert-manager](https://cert-manager.io/)
or `kubectl create secret tls`.
Keep in mind that your users will load some of these endpoints in their web browsers, so the TLS certificates
should be signed by a Certificate Authority that will be trusted by their browsers.
See [the how-to guide for details](https://pinniped.dev/docs/howto/install-supervisor/).

View File

@@ -57,8 +57,8 @@ service_loadbalancer_ip: #! e.g. 1.2.3.4
#! information), trace (timing information), all (kitchen sink).
log_level: #! By default, when this value is left unset, only warnings and errors are printed. There is no way to suppress warning and error logs.
run_as_user: 1001 #! run_as_user specifies the user ID that will own the local-user-authenticator process
run_as_group: 1001 #! run_as_group specifies the group ID that will own the local-user-authenticator process
run_as_user: 1001 #! run_as_user specifies the user ID that will own the process
run_as_group: 1001 #! run_as_group specifies the group ID that will own the process
#! Specify the API group suffix for all Pinniped API groups. By default, this is set to
#! pinniped.dev, so Pinniped API groups will look like foo.pinniped.dev,

View File

@@ -8,6 +8,8 @@
- xref:{anchor_prefix}-authentication-concierge-pinniped-dev-v1alpha1[$$authentication.concierge.pinniped.dev/v1alpha1$$]
- xref:{anchor_prefix}-config-concierge-pinniped-dev-v1alpha1[$$config.concierge.pinniped.dev/v1alpha1$$]
- xref:{anchor_prefix}-config-supervisor-pinniped-dev-v1alpha1[$$config.supervisor.pinniped.dev/v1alpha1$$]
- xref:{anchor_prefix}-identity-concierge-pinniped-dev-identity[$$identity.concierge.pinniped.dev/identity$$]
- xref:{anchor_prefix}-identity-concierge-pinniped-dev-v1alpha1[$$identity.concierge.pinniped.dev/v1alpha1$$]
- xref:{anchor_prefix}-idp-supervisor-pinniped-dev-v1alpha1[$$idp.supervisor.pinniped.dev/v1alpha1$$]
- xref:{anchor_prefix}-login-concierge-pinniped-dev-v1alpha1[$$login.concierge.pinniped.dev/v1alpha1$$]
@@ -234,6 +236,25 @@ Describes the configuration status of a Pinniped credential issuer.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuerfrontend"]
==== CredentialIssuerFrontend
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuerstrategy[$$CredentialIssuerStrategy$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`type`* __FrontendType__ | Type describes which frontend mechanism clients can use with a strategy.
| *`tokenCredentialRequestInfo`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-tokencredentialrequestapiinfo[$$TokenCredentialRequestAPIInfo$$]__ | TokenCredentialRequestAPIInfo describes the parameters for the TokenCredentialRequest API on this Concierge. This field is only set when Type is "TokenCredentialRequestAPI".
| *`impersonationProxyInfo`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-impersonationproxyinfo[$$ImpersonationProxyInfo$$]__ | ImpersonationProxyInfo describes the parameters for the impersonation proxy on this Concierge. This field is only set when Type is "ImpersonationProxy".
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuerkubeconfiginfo"]
==== CredentialIssuerKubeConfigInfo
@@ -268,7 +289,7 @@ Status of a credential issuer.
|===
| Field | Description
| *`strategies`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuerstrategy[$$CredentialIssuerStrategy$$] array__ | List of integration strategies that were attempted by Pinniped.
| *`kubeConfigInfo`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuerkubeconfiginfo[$$CredentialIssuerKubeConfigInfo$$]__ | Information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
| *`kubeConfigInfo`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuerkubeconfiginfo[$$CredentialIssuerKubeConfigInfo$$]__ | Information needed to form a valid Pinniped-based kubeconfig using this credential issuer. This field is deprecated and will be removed in a future version.
|===
@@ -290,6 +311,43 @@ Status of a credential issuer.
| *`reason`* __StrategyReason__ | Reason for the current status.
| *`message`* __string__ | Human-readable description of the current status.
| *`lastUpdateTime`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#time-v1-meta[$$Time$$]__ | When the status was last checked.
| *`frontend`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuerfrontend[$$CredentialIssuerFrontend$$]__ | Frontend describes how clients can connect using this strategy.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-impersonationproxyinfo"]
==== ImpersonationProxyInfo
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuerfrontend[$$CredentialIssuerFrontend$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`endpoint`* __string__ | Endpoint is the HTTPS endpoint of the impersonation proxy.
| *`certificateAuthorityData`* __string__ | CertificateAuthorityData is the base64-encoded PEM CA bundle of the impersonation proxy.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-tokencredentialrequestapiinfo"]
==== TokenCredentialRequestAPIInfo
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuerfrontend[$$CredentialIssuerFrontend$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`server`* __string__ | Server is the Kubernetes API server URL.
| *`certificateAuthorityData`* __string__ | CertificateAuthorityData is the base64-encoded Kubernetes API server CA bundle.
|===
@@ -404,6 +462,203 @@ FederationDomainTLSSpec is a struct that describes the TLS configuration for an
[id="{anchor_prefix}-identity-concierge-pinniped-dev-identity"]
=== identity.concierge.pinniped.dev/identity
Package identity is the internal version of the Pinniped identity API.
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-extravalue"]
==== ExtraValue
ExtraValue masks the value so protobuf can generate
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-userinfo[$$UserInfo$$]
****
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-kubernetesuserinfo"]
==== KubernetesUserInfo
KubernetesUserInfo represents the current authenticated user, exactly as Kubernetes understands it. Copied from the Kubernetes token review API.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-whoamirequeststatus[$$WhoAmIRequestStatus$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`User`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-userinfo[$$UserInfo$$]__ | User is the UserInfo associated with the current user.
| *`Audiences`* __string array__ | Audiences are audience identifiers chosen by the authenticator.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-userinfo"]
==== UserInfo
UserInfo holds the information about the user needed to implement the user.Info interface.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-kubernetesuserinfo[$$KubernetesUserInfo$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`Username`* __string__ | The name that uniquely identifies this user among all active users.
| *`UID`* __string__ | A unique value that identifies this user across time. If this user is deleted and another user by the same name is added, they will have different UIDs.
| *`Groups`* __string array__ | The names of groups this user is a part of.
| *`Extra`* __object (keys:string, values:string array)__ | Any additional information provided by the authenticator.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-whoamirequest"]
==== WhoAmIRequest
WhoAmIRequest submits a request to echo back the current authenticated user.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-whoamirequestlist[$$WhoAmIRequestList$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`ObjectMeta`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#objectmeta-v1-meta[$$ObjectMeta$$]__ |
| *`Spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-whoamirequestspec[$$WhoAmIRequestSpec$$]__ |
| *`Status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-whoamirequeststatus[$$WhoAmIRequestStatus$$]__ |
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-whoamirequeststatus"]
==== WhoAmIRequestStatus
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-whoamirequest[$$WhoAmIRequest$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`KubernetesUserInfo`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-kubernetesuserinfo[$$KubernetesUserInfo$$]__ | The current authenticated user, exactly as Kubernetes understands it.
|===
[id="{anchor_prefix}-identity-concierge-pinniped-dev-v1alpha1"]
=== identity.concierge.pinniped.dev/v1alpha1
Package v1alpha1 is the v1alpha1 version of the Pinniped identity API.
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-v1alpha1-extravalue"]
==== ExtraValue
ExtraValue masks the value so protobuf can generate
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-v1alpha1-userinfo[$$UserInfo$$]
****
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-v1alpha1-kubernetesuserinfo"]
==== KubernetesUserInfo
KubernetesUserInfo represents the current authenticated user, exactly as Kubernetes understands it. Copied from the Kubernetes token review API.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-v1alpha1-whoamirequeststatus[$$WhoAmIRequestStatus$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`user`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-v1alpha1-userinfo[$$UserInfo$$]__ | User is the UserInfo associated with the current user.
| *`audiences`* __string array__ | Audiences are audience identifiers chosen by the authenticator.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-v1alpha1-userinfo"]
==== UserInfo
UserInfo holds the information about the user needed to implement the user.Info interface.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-v1alpha1-kubernetesuserinfo[$$KubernetesUserInfo$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`username`* __string__ | The name that uniquely identifies this user among all active users.
| *`uid`* __string__ | A unique value that identifies this user across time. If this user is deleted and another user by the same name is added, they will have different UIDs.
| *`groups`* __string array__ | The names of groups this user is a part of.
| *`extra`* __object (keys:string, values:string array)__ | Any additional information provided by the authenticator.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-v1alpha1-whoamirequest"]
==== WhoAmIRequest
WhoAmIRequest submits a request to echo back the current authenticated user.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-v1alpha1-whoamirequestlist[$$WhoAmIRequestList$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-v1alpha1-whoamirequestspec[$$WhoAmIRequestSpec$$]__ |
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-v1alpha1-whoamirequeststatus[$$WhoAmIRequestStatus$$]__ |
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-v1alpha1-whoamirequeststatus"]
==== WhoAmIRequestStatus
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-v1alpha1-whoamirequest[$$WhoAmIRequest$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`kubernetesUserInfo`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-v1alpha1-kubernetesuserinfo[$$KubernetesUserInfo$$]__ | The current authenticated user, exactly as Kubernetes understands it.
|===
[id="{anchor_prefix}-idp-supervisor-pinniped-dev-v1alpha1"]
=== idp.supervisor.pinniped.dev/v1alpha1

View File

@@ -1,6 +1,6 @@
// +build !ignore_autogenerated
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by deepcopy-gen. DO NOT EDIT.

View File

@@ -1,27 +1,39 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// +kubebuilder:validation:Enum=KubeClusterSigningCertificate
// +kubebuilder:validation:Enum=KubeClusterSigningCertificate;ImpersonationProxy
type StrategyType string
// +kubebuilder:validation:Enum=TokenCredentialRequestAPI;ImpersonationProxy
type FrontendType string
// +kubebuilder:validation:Enum=Success;Error
type StrategyStatus string
// +kubebuilder:validation:Enum=FetchedKey;CouldNotFetchKey
// +kubebuilder:validation:Enum=Listening;Pending;Disabled;ErrorDuringSetup;CouldNotFetchKey;CouldNotGetClusterInfo;FetchedKey
type StrategyReason string
const (
KubeClusterSigningCertificateStrategyType = StrategyType("KubeClusterSigningCertificate")
ImpersonationProxyStrategyType = StrategyType("ImpersonationProxy")
TokenCredentialRequestAPIFrontendType = FrontendType("TokenCredentialRequestAPI")
ImpersonationProxyFrontendType = FrontendType("ImpersonationProxy")
SuccessStrategyStatus = StrategyStatus("Success")
ErrorStrategyStatus = StrategyStatus("Error")
CouldNotFetchKeyStrategyReason = StrategyReason("CouldNotFetchKey")
FetchedKeyStrategyReason = StrategyReason("FetchedKey")
ListeningStrategyReason = StrategyReason("Listening")
PendingStrategyReason = StrategyReason("Pending")
DisabledStrategyReason = StrategyReason("Disabled")
ErrorDuringSetupStrategyReason = StrategyReason("ErrorDuringSetup")
CouldNotFetchKeyStrategyReason = StrategyReason("CouldNotFetchKey")
CouldNotGetClusterInfoStrategyReason = StrategyReason("CouldNotGetClusterInfo")
FetchedKeyStrategyReason = StrategyReason("FetchedKey")
)
// Status of a credential issuer.
@@ -30,6 +42,7 @@ type CredentialIssuerStatus struct {
Strategies []CredentialIssuerStrategy `json:"strategies"`
// Information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
// This field is deprecated and will be removed in a future version.
// +optional
KubeConfigInfo *CredentialIssuerKubeConfigInfo `json:"kubeConfigInfo,omitempty"`
}
@@ -63,6 +76,46 @@ type CredentialIssuerStrategy struct {
// When the status was last checked.
LastUpdateTime metav1.Time `json:"lastUpdateTime"`
// Frontend describes how clients can connect using this strategy.
Frontend *CredentialIssuerFrontend `json:"frontend,omitempty"`
}
type CredentialIssuerFrontend struct {
// Type describes which frontend mechanism clients can use with a strategy.
Type FrontendType `json:"type"`
// TokenCredentialRequestAPIInfo describes the parameters for the TokenCredentialRequest API on this Concierge.
// This field is only set when Type is "TokenCredentialRequestAPI".
TokenCredentialRequestAPIInfo *TokenCredentialRequestAPIInfo `json:"tokenCredentialRequestInfo,omitempty"`
// ImpersonationProxyInfo describes the parameters for the impersonation proxy on this Concierge.
// This field is only set when Type is "ImpersonationProxy".
ImpersonationProxyInfo *ImpersonationProxyInfo `json:"impersonationProxyInfo,omitempty"`
}
// TokenCredentialRequestAPIInfo describes the parameters for the TokenCredentialRequest API on this Concierge.
type TokenCredentialRequestAPIInfo struct {
// Server is the Kubernetes API server URL.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^https://|^http://`
Server string `json:"server"`
// CertificateAuthorityData is the base64-encoded Kubernetes API server CA bundle.
// +kubebuilder:validation:MinLength=1
CertificateAuthorityData string `json:"certificateAuthorityData"`
}
// ImpersonationProxyInfo describes the parameters for the impersonation proxy on this Concierge.
type ImpersonationProxyInfo struct {
// Endpoint is the HTTPS endpoint of the impersonation proxy.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^https://`
Endpoint string `json:"endpoint"`
// CertificateAuthorityData is the base64-encoded PEM CA bundle of the impersonation proxy.
// +kubebuilder:validation:MinLength=1
CertificateAuthorityData string `json:"certificateAuthorityData"`
}
// Describes the configuration status of a Pinniped credential issuer.

View File

@@ -1,6 +1,6 @@
// +build !ignore_autogenerated
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by deepcopy-gen. DO NOT EDIT.
@@ -38,6 +38,32 @@ func (in *CredentialIssuer) DeepCopyObject() runtime.Object {
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CredentialIssuerFrontend) DeepCopyInto(out *CredentialIssuerFrontend) {
*out = *in
if in.TokenCredentialRequestAPIInfo != nil {
in, out := &in.TokenCredentialRequestAPIInfo, &out.TokenCredentialRequestAPIInfo
*out = new(TokenCredentialRequestAPIInfo)
**out = **in
}
if in.ImpersonationProxyInfo != nil {
in, out := &in.ImpersonationProxyInfo, &out.ImpersonationProxyInfo
*out = new(ImpersonationProxyInfo)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CredentialIssuerFrontend.
func (in *CredentialIssuerFrontend) DeepCopy() *CredentialIssuerFrontend {
if in == nil {
return nil
}
out := new(CredentialIssuerFrontend)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CredentialIssuerKubeConfigInfo) DeepCopyInto(out *CredentialIssuerKubeConfigInfo) {
*out = *in
@@ -119,6 +145,11 @@ func (in *CredentialIssuerStatus) DeepCopy() *CredentialIssuerStatus {
func (in *CredentialIssuerStrategy) DeepCopyInto(out *CredentialIssuerStrategy) {
*out = *in
in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime)
if in.Frontend != nil {
in, out := &in.Frontend, &out.Frontend
*out = new(CredentialIssuerFrontend)
(*in).DeepCopyInto(*out)
}
return
}
@@ -131,3 +162,35 @@ func (in *CredentialIssuerStrategy) DeepCopy() *CredentialIssuerStrategy {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ImpersonationProxyInfo) DeepCopyInto(out *ImpersonationProxyInfo) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImpersonationProxyInfo.
func (in *ImpersonationProxyInfo) DeepCopy() *ImpersonationProxyInfo {
if in == nil {
return nil
}
out := new(ImpersonationProxyInfo)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TokenCredentialRequestAPIInfo) DeepCopyInto(out *TokenCredentialRequestAPIInfo) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TokenCredentialRequestAPIInfo.
func (in *TokenCredentialRequestAPIInfo) DeepCopy() *TokenCredentialRequestAPIInfo {
if in == nil {
return nil
}
out := new(TokenCredentialRequestAPIInfo)
in.DeepCopyInto(out)
return out
}

View File

@@ -0,0 +1,8 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// +k8s:deepcopy-gen=package
// +groupName=identity.concierge.pinniped.dev
// Package identity is the internal version of the Pinniped identity API.
package identity

View File

@@ -0,0 +1,38 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package identity
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const GroupName = "identity.concierge.pinniped.dev"
// SchemeGroupVersion is group version used to register these objects.
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind.
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns back a Group qualified GroupResource.
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to the given scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&WhoAmIRequest{},
&WhoAmIRequestList{},
)
return nil
}

View File

@@ -0,0 +1,37 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package identity
import "fmt"
// KubernetesUserInfo represents the current authenticated user, exactly as Kubernetes understands it.
// Copied from the Kubernetes token review API.
type KubernetesUserInfo struct {
// User is the UserInfo associated with the current user.
User UserInfo
// Audiences are audience identifiers chosen by the authenticator.
Audiences []string
}
// UserInfo holds the information about the user needed to implement the
// user.Info interface.
type UserInfo struct {
// The name that uniquely identifies this user among all active users.
Username string
// A unique value that identifies this user across time. If this user is
// deleted and another user by the same name is added, they will have
// different UIDs.
UID string
// The names of groups this user is a part of.
Groups []string
// Any additional information provided by the authenticator.
Extra map[string]ExtraValue
}
// ExtraValue masks the value so protobuf can generate
type ExtraValue []string
func (t ExtraValue) String() string {
return fmt.Sprintf("%v", []string(t))
}

View File

@@ -0,0 +1,40 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package identity
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// WhoAmIRequest submits a request to echo back the current authenticated user.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type WhoAmIRequest struct {
metav1.TypeMeta
metav1.ObjectMeta
Spec WhoAmIRequestSpec
Status WhoAmIRequestStatus
}
type WhoAmIRequestSpec struct {
// empty for now but we may add some config here in the future
// any such config must be safe in the context of an unauthenticated user
}
type WhoAmIRequestStatus struct {
// The current authenticated user, exactly as Kubernetes understands it.
KubernetesUserInfo KubernetesUserInfo
// We may add concierge specific information here in the future.
}
// WhoAmIRequestList is a list of WhoAmIRequest objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type WhoAmIRequestList struct {
metav1.TypeMeta
metav1.ListMeta
// Items is a list of WhoAmIRequest
Items []WhoAmIRequest
}

View File

@@ -0,0 +1,4 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1

View File

@@ -0,0 +1,12 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime"
)
func addDefaultingFuncs(scheme *runtime.Scheme) error {
return RegisterDefaults(scheme)
}

View File

@@ -0,0 +1,11 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// +k8s:openapi-gen=true
// +k8s:deepcopy-gen=package
// +k8s:conversion-gen=go.pinniped.dev/generated/1.17/apis/concierge/identity
// +k8s:defaulter-gen=TypeMeta
// +groupName=identity.concierge.pinniped.dev
// Package v1alpha1 is the v1alpha1 version of the Pinniped identity API.
package v1alpha1

View File

@@ -0,0 +1,43 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const GroupName = "identity.concierge.pinniped.dev"
// SchemeGroupVersion is group version used to register these objects.
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
var (
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
func init() {
// We only register manually written functions here. The registration of the
// generated functions takes place in the generated files. The separation
// makes the code compile even when the generated files are missing.
localSchemeBuilder.Register(addKnownTypes, addDefaultingFuncs)
}
// Adds the list of known types to the given scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&WhoAmIRequest{},
&WhoAmIRequestList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource.
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}

View File

@@ -0,0 +1,41 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import "fmt"
// KubernetesUserInfo represents the current authenticated user, exactly as Kubernetes understands it.
// Copied from the Kubernetes token review API.
type KubernetesUserInfo struct {
// User is the UserInfo associated with the current user.
User UserInfo `json:"user"`
// Audiences are audience identifiers chosen by the authenticator.
// +optional
Audiences []string `json:"audiences,omitempty"`
}
// UserInfo holds the information about the user needed to implement the
// user.Info interface.
type UserInfo struct {
// The name that uniquely identifies this user among all active users.
Username string `json:"username"`
// A unique value that identifies this user across time. If this user is
// deleted and another user by the same name is added, they will have
// different UIDs.
// +optional
UID string `json:"uid,omitempty"`
// The names of groups this user is a part of.
// +optional
Groups []string `json:"groups,omitempty"`
// Any additional information provided by the authenticator.
// +optional
Extra map[string]ExtraValue `json:"extra,omitempty"`
}
// ExtraValue masks the value so protobuf can generate
type ExtraValue []string
func (t ExtraValue) String() string {
return fmt.Sprintf("%v", []string(t))
}

View File

@@ -0,0 +1,43 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// WhoAmIRequest submits a request to echo back the current authenticated user.
// +genclient
// +genclient:nonNamespaced
// +genclient:onlyVerbs=create
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type WhoAmIRequest struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec WhoAmIRequestSpec `json:"spec,omitempty"`
Status WhoAmIRequestStatus `json:"status,omitempty"`
}
type WhoAmIRequestSpec struct {
// empty for now but we may add some config here in the future
// any such config must be safe in the context of an unauthenticated user
}
type WhoAmIRequestStatus struct {
// The current authenticated user, exactly as Kubernetes understands it.
KubernetesUserInfo KubernetesUserInfo `json:"kubernetesUserInfo"`
// We may add concierge specific information here in the future.
}
// WhoAmIRequestList is a list of WhoAmIRequest objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type WhoAmIRequestList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
// Items is a list of WhoAmIRequest
Items []WhoAmIRequest `json:"items"`
}

View File

@@ -0,0 +1,234 @@
// +build !ignore_autogenerated
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by conversion-gen. DO NOT EDIT.
package v1alpha1
import (
unsafe "unsafe"
identity "go.pinniped.dev/generated/1.17/apis/concierge/identity"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
)
func init() {
localSchemeBuilder.Register(RegisterConversions)
}
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*KubernetesUserInfo)(nil), (*identity.KubernetesUserInfo)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_KubernetesUserInfo_To_identity_KubernetesUserInfo(a.(*KubernetesUserInfo), b.(*identity.KubernetesUserInfo), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*identity.KubernetesUserInfo)(nil), (*KubernetesUserInfo)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_identity_KubernetesUserInfo_To_v1alpha1_KubernetesUserInfo(a.(*identity.KubernetesUserInfo), b.(*KubernetesUserInfo), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*UserInfo)(nil), (*identity.UserInfo)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_UserInfo_To_identity_UserInfo(a.(*UserInfo), b.(*identity.UserInfo), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*identity.UserInfo)(nil), (*UserInfo)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_identity_UserInfo_To_v1alpha1_UserInfo(a.(*identity.UserInfo), b.(*UserInfo), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*WhoAmIRequest)(nil), (*identity.WhoAmIRequest)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_WhoAmIRequest_To_identity_WhoAmIRequest(a.(*WhoAmIRequest), b.(*identity.WhoAmIRequest), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*identity.WhoAmIRequest)(nil), (*WhoAmIRequest)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_identity_WhoAmIRequest_To_v1alpha1_WhoAmIRequest(a.(*identity.WhoAmIRequest), b.(*WhoAmIRequest), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*WhoAmIRequestList)(nil), (*identity.WhoAmIRequestList)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_WhoAmIRequestList_To_identity_WhoAmIRequestList(a.(*WhoAmIRequestList), b.(*identity.WhoAmIRequestList), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*identity.WhoAmIRequestList)(nil), (*WhoAmIRequestList)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_identity_WhoAmIRequestList_To_v1alpha1_WhoAmIRequestList(a.(*identity.WhoAmIRequestList), b.(*WhoAmIRequestList), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*WhoAmIRequestSpec)(nil), (*identity.WhoAmIRequestSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_WhoAmIRequestSpec_To_identity_WhoAmIRequestSpec(a.(*WhoAmIRequestSpec), b.(*identity.WhoAmIRequestSpec), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*identity.WhoAmIRequestSpec)(nil), (*WhoAmIRequestSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_identity_WhoAmIRequestSpec_To_v1alpha1_WhoAmIRequestSpec(a.(*identity.WhoAmIRequestSpec), b.(*WhoAmIRequestSpec), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*WhoAmIRequestStatus)(nil), (*identity.WhoAmIRequestStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_WhoAmIRequestStatus_To_identity_WhoAmIRequestStatus(a.(*WhoAmIRequestStatus), b.(*identity.WhoAmIRequestStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*identity.WhoAmIRequestStatus)(nil), (*WhoAmIRequestStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_identity_WhoAmIRequestStatus_To_v1alpha1_WhoAmIRequestStatus(a.(*identity.WhoAmIRequestStatus), b.(*WhoAmIRequestStatus), scope)
}); err != nil {
return err
}
return nil
}
func autoConvert_v1alpha1_KubernetesUserInfo_To_identity_KubernetesUserInfo(in *KubernetesUserInfo, out *identity.KubernetesUserInfo, s conversion.Scope) error {
if err := Convert_v1alpha1_UserInfo_To_identity_UserInfo(&in.User, &out.User, s); err != nil {
return err
}
out.Audiences = *(*[]string)(unsafe.Pointer(&in.Audiences))
return nil
}
// Convert_v1alpha1_KubernetesUserInfo_To_identity_KubernetesUserInfo is an autogenerated conversion function.
func Convert_v1alpha1_KubernetesUserInfo_To_identity_KubernetesUserInfo(in *KubernetesUserInfo, out *identity.KubernetesUserInfo, s conversion.Scope) error {
return autoConvert_v1alpha1_KubernetesUserInfo_To_identity_KubernetesUserInfo(in, out, s)
}
func autoConvert_identity_KubernetesUserInfo_To_v1alpha1_KubernetesUserInfo(in *identity.KubernetesUserInfo, out *KubernetesUserInfo, s conversion.Scope) error {
if err := Convert_identity_UserInfo_To_v1alpha1_UserInfo(&in.User, &out.User, s); err != nil {
return err
}
out.Audiences = *(*[]string)(unsafe.Pointer(&in.Audiences))
return nil
}
// Convert_identity_KubernetesUserInfo_To_v1alpha1_KubernetesUserInfo is an autogenerated conversion function.
func Convert_identity_KubernetesUserInfo_To_v1alpha1_KubernetesUserInfo(in *identity.KubernetesUserInfo, out *KubernetesUserInfo, s conversion.Scope) error {
return autoConvert_identity_KubernetesUserInfo_To_v1alpha1_KubernetesUserInfo(in, out, s)
}
func autoConvert_v1alpha1_UserInfo_To_identity_UserInfo(in *UserInfo, out *identity.UserInfo, s conversion.Scope) error {
out.Username = in.Username
out.UID = in.UID
out.Groups = *(*[]string)(unsafe.Pointer(&in.Groups))
out.Extra = *(*map[string]identity.ExtraValue)(unsafe.Pointer(&in.Extra))
return nil
}
// Convert_v1alpha1_UserInfo_To_identity_UserInfo is an autogenerated conversion function.
func Convert_v1alpha1_UserInfo_To_identity_UserInfo(in *UserInfo, out *identity.UserInfo, s conversion.Scope) error {
return autoConvert_v1alpha1_UserInfo_To_identity_UserInfo(in, out, s)
}
func autoConvert_identity_UserInfo_To_v1alpha1_UserInfo(in *identity.UserInfo, out *UserInfo, s conversion.Scope) error {
out.Username = in.Username
out.UID = in.UID
out.Groups = *(*[]string)(unsafe.Pointer(&in.Groups))
out.Extra = *(*map[string]ExtraValue)(unsafe.Pointer(&in.Extra))
return nil
}
// Convert_identity_UserInfo_To_v1alpha1_UserInfo is an autogenerated conversion function.
func Convert_identity_UserInfo_To_v1alpha1_UserInfo(in *identity.UserInfo, out *UserInfo, s conversion.Scope) error {
return autoConvert_identity_UserInfo_To_v1alpha1_UserInfo(in, out, s)
}
func autoConvert_v1alpha1_WhoAmIRequest_To_identity_WhoAmIRequest(in *WhoAmIRequest, out *identity.WhoAmIRequest, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_v1alpha1_WhoAmIRequestSpec_To_identity_WhoAmIRequestSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := Convert_v1alpha1_WhoAmIRequestStatus_To_identity_WhoAmIRequestStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
// Convert_v1alpha1_WhoAmIRequest_To_identity_WhoAmIRequest is an autogenerated conversion function.
func Convert_v1alpha1_WhoAmIRequest_To_identity_WhoAmIRequest(in *WhoAmIRequest, out *identity.WhoAmIRequest, s conversion.Scope) error {
return autoConvert_v1alpha1_WhoAmIRequest_To_identity_WhoAmIRequest(in, out, s)
}
func autoConvert_identity_WhoAmIRequest_To_v1alpha1_WhoAmIRequest(in *identity.WhoAmIRequest, out *WhoAmIRequest, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_identity_WhoAmIRequestSpec_To_v1alpha1_WhoAmIRequestSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := Convert_identity_WhoAmIRequestStatus_To_v1alpha1_WhoAmIRequestStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
// Convert_identity_WhoAmIRequest_To_v1alpha1_WhoAmIRequest is an autogenerated conversion function.
func Convert_identity_WhoAmIRequest_To_v1alpha1_WhoAmIRequest(in *identity.WhoAmIRequest, out *WhoAmIRequest, s conversion.Scope) error {
return autoConvert_identity_WhoAmIRequest_To_v1alpha1_WhoAmIRequest(in, out, s)
}
func autoConvert_v1alpha1_WhoAmIRequestList_To_identity_WhoAmIRequestList(in *WhoAmIRequestList, out *identity.WhoAmIRequestList, s conversion.Scope) error {
out.ListMeta = in.ListMeta
out.Items = *(*[]identity.WhoAmIRequest)(unsafe.Pointer(&in.Items))
return nil
}
// Convert_v1alpha1_WhoAmIRequestList_To_identity_WhoAmIRequestList is an autogenerated conversion function.
func Convert_v1alpha1_WhoAmIRequestList_To_identity_WhoAmIRequestList(in *WhoAmIRequestList, out *identity.WhoAmIRequestList, s conversion.Scope) error {
return autoConvert_v1alpha1_WhoAmIRequestList_To_identity_WhoAmIRequestList(in, out, s)
}
func autoConvert_identity_WhoAmIRequestList_To_v1alpha1_WhoAmIRequestList(in *identity.WhoAmIRequestList, out *WhoAmIRequestList, s conversion.Scope) error {
out.ListMeta = in.ListMeta
out.Items = *(*[]WhoAmIRequest)(unsafe.Pointer(&in.Items))
return nil
}
// Convert_identity_WhoAmIRequestList_To_v1alpha1_WhoAmIRequestList is an autogenerated conversion function.
func Convert_identity_WhoAmIRequestList_To_v1alpha1_WhoAmIRequestList(in *identity.WhoAmIRequestList, out *WhoAmIRequestList, s conversion.Scope) error {
return autoConvert_identity_WhoAmIRequestList_To_v1alpha1_WhoAmIRequestList(in, out, s)
}
func autoConvert_v1alpha1_WhoAmIRequestSpec_To_identity_WhoAmIRequestSpec(in *WhoAmIRequestSpec, out *identity.WhoAmIRequestSpec, s conversion.Scope) error {
return nil
}
// Convert_v1alpha1_WhoAmIRequestSpec_To_identity_WhoAmIRequestSpec is an autogenerated conversion function.
func Convert_v1alpha1_WhoAmIRequestSpec_To_identity_WhoAmIRequestSpec(in *WhoAmIRequestSpec, out *identity.WhoAmIRequestSpec, s conversion.Scope) error {
return autoConvert_v1alpha1_WhoAmIRequestSpec_To_identity_WhoAmIRequestSpec(in, out, s)
}
func autoConvert_identity_WhoAmIRequestSpec_To_v1alpha1_WhoAmIRequestSpec(in *identity.WhoAmIRequestSpec, out *WhoAmIRequestSpec, s conversion.Scope) error {
return nil
}
// Convert_identity_WhoAmIRequestSpec_To_v1alpha1_WhoAmIRequestSpec is an autogenerated conversion function.
func Convert_identity_WhoAmIRequestSpec_To_v1alpha1_WhoAmIRequestSpec(in *identity.WhoAmIRequestSpec, out *WhoAmIRequestSpec, s conversion.Scope) error {
return autoConvert_identity_WhoAmIRequestSpec_To_v1alpha1_WhoAmIRequestSpec(in, out, s)
}
func autoConvert_v1alpha1_WhoAmIRequestStatus_To_identity_WhoAmIRequestStatus(in *WhoAmIRequestStatus, out *identity.WhoAmIRequestStatus, s conversion.Scope) error {
if err := Convert_v1alpha1_KubernetesUserInfo_To_identity_KubernetesUserInfo(&in.KubernetesUserInfo, &out.KubernetesUserInfo, s); err != nil {
return err
}
return nil
}
// Convert_v1alpha1_WhoAmIRequestStatus_To_identity_WhoAmIRequestStatus is an autogenerated conversion function.
func Convert_v1alpha1_WhoAmIRequestStatus_To_identity_WhoAmIRequestStatus(in *WhoAmIRequestStatus, out *identity.WhoAmIRequestStatus, s conversion.Scope) error {
return autoConvert_v1alpha1_WhoAmIRequestStatus_To_identity_WhoAmIRequestStatus(in, out, s)
}
func autoConvert_identity_WhoAmIRequestStatus_To_v1alpha1_WhoAmIRequestStatus(in *identity.WhoAmIRequestStatus, out *WhoAmIRequestStatus, s conversion.Scope) error {
if err := Convert_identity_KubernetesUserInfo_To_v1alpha1_KubernetesUserInfo(&in.KubernetesUserInfo, &out.KubernetesUserInfo, s); err != nil {
return err
}
return nil
}
// Convert_identity_WhoAmIRequestStatus_To_v1alpha1_WhoAmIRequestStatus is an autogenerated conversion function.
func Convert_identity_WhoAmIRequestStatus_To_v1alpha1_WhoAmIRequestStatus(in *identity.WhoAmIRequestStatus, out *WhoAmIRequestStatus, s conversion.Scope) error {
return autoConvert_identity_WhoAmIRequestStatus_To_v1alpha1_WhoAmIRequestStatus(in, out, s)
}

View File

@@ -0,0 +1,184 @@
// +build !ignore_autogenerated
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1alpha1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in ExtraValue) DeepCopyInto(out *ExtraValue) {
{
in := &in
*out = make(ExtraValue, len(*in))
copy(*out, *in)
return
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraValue.
func (in ExtraValue) DeepCopy() ExtraValue {
if in == nil {
return nil
}
out := new(ExtraValue)
in.DeepCopyInto(out)
return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubernetesUserInfo) DeepCopyInto(out *KubernetesUserInfo) {
*out = *in
in.User.DeepCopyInto(&out.User)
if in.Audiences != nil {
in, out := &in.Audiences, &out.Audiences
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesUserInfo.
func (in *KubernetesUserInfo) DeepCopy() *KubernetesUserInfo {
if in == nil {
return nil
}
out := new(KubernetesUserInfo)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UserInfo) DeepCopyInto(out *UserInfo) {
*out = *in
if in.Groups != nil {
in, out := &in.Groups, &out.Groups
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Extra != nil {
in, out := &in.Extra, &out.Extra
*out = make(map[string]ExtraValue, len(*in))
for key, val := range *in {
var outVal []string
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = make(ExtraValue, len(*in))
copy(*out, *in)
}
(*out)[key] = outVal
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserInfo.
func (in *UserInfo) DeepCopy() *UserInfo {
if in == nil {
return nil
}
out := new(UserInfo)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WhoAmIRequest) DeepCopyInto(out *WhoAmIRequest) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WhoAmIRequest.
func (in *WhoAmIRequest) DeepCopy() *WhoAmIRequest {
if in == nil {
return nil
}
out := new(WhoAmIRequest)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *WhoAmIRequest) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WhoAmIRequestList) DeepCopyInto(out *WhoAmIRequestList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]WhoAmIRequest, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WhoAmIRequestList.
func (in *WhoAmIRequestList) DeepCopy() *WhoAmIRequestList {
if in == nil {
return nil
}
out := new(WhoAmIRequestList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *WhoAmIRequestList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WhoAmIRequestSpec) DeepCopyInto(out *WhoAmIRequestSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WhoAmIRequestSpec.
func (in *WhoAmIRequestSpec) DeepCopy() *WhoAmIRequestSpec {
if in == nil {
return nil
}
out := new(WhoAmIRequestSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WhoAmIRequestStatus) DeepCopyInto(out *WhoAmIRequestStatus) {
*out = *in
in.KubernetesUserInfo.DeepCopyInto(&out.KubernetesUserInfo)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WhoAmIRequestStatus.
func (in *WhoAmIRequestStatus) DeepCopy() *WhoAmIRequestStatus {
if in == nil {
return nil
}
out := new(WhoAmIRequestStatus)
in.DeepCopyInto(out)
return out
}

View File

@@ -0,0 +1,19 @@
// +build !ignore_autogenerated
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by defaulter-gen. DO NOT EDIT.
package v1alpha1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// RegisterDefaults adds defaulters functions to the given scheme.
// Public to allow building arbitrary schemes.
// All generated defaulters are covering - they call all nested defaulters.
func RegisterDefaults(scheme *runtime.Scheme) error {
return nil
}

View File

@@ -0,0 +1,14 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package validation
import (
"k8s.io/apimachinery/pkg/util/validation/field"
identityapi "go.pinniped.dev/generated/1.17/apis/concierge/identity"
)
func ValidateWhoAmIRequest(whoAmIRequest *identityapi.WhoAmIRequest) field.ErrorList {
return nil // add validation for spec here if we expand it
}

View File

@@ -0,0 +1,184 @@
// +build !ignore_autogenerated
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by deepcopy-gen. DO NOT EDIT.
package identity
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in ExtraValue) DeepCopyInto(out *ExtraValue) {
{
in := &in
*out = make(ExtraValue, len(*in))
copy(*out, *in)
return
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraValue.
func (in ExtraValue) DeepCopy() ExtraValue {
if in == nil {
return nil
}
out := new(ExtraValue)
in.DeepCopyInto(out)
return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubernetesUserInfo) DeepCopyInto(out *KubernetesUserInfo) {
*out = *in
in.User.DeepCopyInto(&out.User)
if in.Audiences != nil {
in, out := &in.Audiences, &out.Audiences
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesUserInfo.
func (in *KubernetesUserInfo) DeepCopy() *KubernetesUserInfo {
if in == nil {
return nil
}
out := new(KubernetesUserInfo)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UserInfo) DeepCopyInto(out *UserInfo) {
*out = *in
if in.Groups != nil {
in, out := &in.Groups, &out.Groups
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Extra != nil {
in, out := &in.Extra, &out.Extra
*out = make(map[string]ExtraValue, len(*in))
for key, val := range *in {
var outVal []string
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = make(ExtraValue, len(*in))
copy(*out, *in)
}
(*out)[key] = outVal
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserInfo.
func (in *UserInfo) DeepCopy() *UserInfo {
if in == nil {
return nil
}
out := new(UserInfo)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WhoAmIRequest) DeepCopyInto(out *WhoAmIRequest) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WhoAmIRequest.
func (in *WhoAmIRequest) DeepCopy() *WhoAmIRequest {
if in == nil {
return nil
}
out := new(WhoAmIRequest)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *WhoAmIRequest) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WhoAmIRequestList) DeepCopyInto(out *WhoAmIRequestList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]WhoAmIRequest, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WhoAmIRequestList.
func (in *WhoAmIRequestList) DeepCopy() *WhoAmIRequestList {
if in == nil {
return nil
}
out := new(WhoAmIRequestList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *WhoAmIRequestList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WhoAmIRequestSpec) DeepCopyInto(out *WhoAmIRequestSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WhoAmIRequestSpec.
func (in *WhoAmIRequestSpec) DeepCopy() *WhoAmIRequestSpec {
if in == nil {
return nil
}
out := new(WhoAmIRequestSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WhoAmIRequestStatus) DeepCopyInto(out *WhoAmIRequestStatus) {
*out = *in
in.KubernetesUserInfo.DeepCopyInto(&out.KubernetesUserInfo)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WhoAmIRequestStatus.
func (in *WhoAmIRequestStatus) DeepCopy() *WhoAmIRequestStatus {
if in == nil {
return nil
}
out := new(WhoAmIRequestStatus)
in.DeepCopyInto(out)
return out
}

View File

@@ -31,6 +31,7 @@ type TokenCredentialRequestStatus struct {
// TokenCredentialRequest submits an IDP-specific credential to Pinniped in exchange for a cluster-specific credential.
// +genclient
// +genclient:nonNamespaced
// +genclient:onlyVerbs=create
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type TokenCredentialRequest struct {
metav1.TypeMeta `json:",inline"`

View File

@@ -1,6 +1,6 @@
// +build !ignore_autogenerated
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by conversion-gen. DO NOT EDIT.

View File

@@ -1,6 +1,6 @@
// +build !ignore_autogenerated
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by deepcopy-gen. DO NOT EDIT.

View File

@@ -1,6 +1,6 @@
// +build !ignore_autogenerated
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by defaulter-gen. DO NOT EDIT.

View File

@@ -1,6 +1,6 @@
// +build !ignore_autogenerated
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by deepcopy-gen. DO NOT EDIT.

View File

@@ -1,6 +1,6 @@
// +build !ignore_autogenerated
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by deepcopy-gen. DO NOT EDIT.

View File

@@ -1,6 +1,6 @@
// +build !ignore_autogenerated
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by deepcopy-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.
@@ -10,6 +10,7 @@ import (
authenticationv1alpha1 "go.pinniped.dev/generated/1.17/client/concierge/clientset/versioned/typed/authentication/v1alpha1"
configv1alpha1 "go.pinniped.dev/generated/1.17/client/concierge/clientset/versioned/typed/config/v1alpha1"
identityv1alpha1 "go.pinniped.dev/generated/1.17/client/concierge/clientset/versioned/typed/identity/v1alpha1"
loginv1alpha1 "go.pinniped.dev/generated/1.17/client/concierge/clientset/versioned/typed/login/v1alpha1"
discovery "k8s.io/client-go/discovery"
rest "k8s.io/client-go/rest"
@@ -20,6 +21,7 @@ type Interface interface {
Discovery() discovery.DiscoveryInterface
AuthenticationV1alpha1() authenticationv1alpha1.AuthenticationV1alpha1Interface
ConfigV1alpha1() configv1alpha1.ConfigV1alpha1Interface
IdentityV1alpha1() identityv1alpha1.IdentityV1alpha1Interface
LoginV1alpha1() loginv1alpha1.LoginV1alpha1Interface
}
@@ -29,6 +31,7 @@ type Clientset struct {
*discovery.DiscoveryClient
authenticationV1alpha1 *authenticationv1alpha1.AuthenticationV1alpha1Client
configV1alpha1 *configv1alpha1.ConfigV1alpha1Client
identityV1alpha1 *identityv1alpha1.IdentityV1alpha1Client
loginV1alpha1 *loginv1alpha1.LoginV1alpha1Client
}
@@ -42,6 +45,11 @@ func (c *Clientset) ConfigV1alpha1() configv1alpha1.ConfigV1alpha1Interface {
return c.configV1alpha1
}
// IdentityV1alpha1 retrieves the IdentityV1alpha1Client
func (c *Clientset) IdentityV1alpha1() identityv1alpha1.IdentityV1alpha1Interface {
return c.identityV1alpha1
}
// LoginV1alpha1 retrieves the LoginV1alpha1Client
func (c *Clientset) LoginV1alpha1() loginv1alpha1.LoginV1alpha1Interface {
return c.loginV1alpha1
@@ -76,6 +84,10 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
if err != nil {
return nil, err
}
cs.identityV1alpha1, err = identityv1alpha1.NewForConfig(&configShallowCopy)
if err != nil {
return nil, err
}
cs.loginV1alpha1, err = loginv1alpha1.NewForConfig(&configShallowCopy)
if err != nil {
return nil, err
@@ -94,6 +106,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset {
var cs Clientset
cs.authenticationV1alpha1 = authenticationv1alpha1.NewForConfigOrDie(c)
cs.configV1alpha1 = configv1alpha1.NewForConfigOrDie(c)
cs.identityV1alpha1 = identityv1alpha1.NewForConfigOrDie(c)
cs.loginV1alpha1 = loginv1alpha1.NewForConfigOrDie(c)
cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
@@ -105,6 +118,7 @@ func New(c rest.Interface) *Clientset {
var cs Clientset
cs.authenticationV1alpha1 = authenticationv1alpha1.New(c)
cs.configV1alpha1 = configv1alpha1.New(c)
cs.identityV1alpha1 = identityv1alpha1.New(c)
cs.loginV1alpha1 = loginv1alpha1.New(c)
cs.DiscoveryClient = discovery.NewDiscoveryClient(c)

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.
@@ -11,6 +11,8 @@ import (
fakeauthenticationv1alpha1 "go.pinniped.dev/generated/1.17/client/concierge/clientset/versioned/typed/authentication/v1alpha1/fake"
configv1alpha1 "go.pinniped.dev/generated/1.17/client/concierge/clientset/versioned/typed/config/v1alpha1"
fakeconfigv1alpha1 "go.pinniped.dev/generated/1.17/client/concierge/clientset/versioned/typed/config/v1alpha1/fake"
identityv1alpha1 "go.pinniped.dev/generated/1.17/client/concierge/clientset/versioned/typed/identity/v1alpha1"
fakeidentityv1alpha1 "go.pinniped.dev/generated/1.17/client/concierge/clientset/versioned/typed/identity/v1alpha1/fake"
loginv1alpha1 "go.pinniped.dev/generated/1.17/client/concierge/clientset/versioned/typed/login/v1alpha1"
fakeloginv1alpha1 "go.pinniped.dev/generated/1.17/client/concierge/clientset/versioned/typed/login/v1alpha1/fake"
"k8s.io/apimachinery/pkg/runtime"
@@ -77,6 +79,11 @@ func (c *Clientset) ConfigV1alpha1() configv1alpha1.ConfigV1alpha1Interface {
return &fakeconfigv1alpha1.FakeConfigV1alpha1{Fake: &c.Fake}
}
// IdentityV1alpha1 retrieves the IdentityV1alpha1Client
func (c *Clientset) IdentityV1alpha1() identityv1alpha1.IdentityV1alpha1Interface {
return &fakeidentityv1alpha1.FakeIdentityV1alpha1{Fake: &c.Fake}
}
// LoginV1alpha1 retrieves the LoginV1alpha1Client
func (c *Clientset) LoginV1alpha1() loginv1alpha1.LoginV1alpha1Interface {
return &fakeloginv1alpha1.FakeLoginV1alpha1{Fake: &c.Fake}

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.
@@ -8,6 +8,7 @@ package fake
import (
authenticationv1alpha1 "go.pinniped.dev/generated/1.17/apis/concierge/authentication/v1alpha1"
configv1alpha1 "go.pinniped.dev/generated/1.17/apis/concierge/config/v1alpha1"
identityv1alpha1 "go.pinniped.dev/generated/1.17/apis/concierge/identity/v1alpha1"
loginv1alpha1 "go.pinniped.dev/generated/1.17/apis/concierge/login/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
@@ -22,6 +23,7 @@ var parameterCodec = runtime.NewParameterCodec(scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
authenticationv1alpha1.AddToScheme,
configv1alpha1.AddToScheme,
identityv1alpha1.AddToScheme,
loginv1alpha1.AddToScheme,
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.
@@ -8,6 +8,7 @@ package scheme
import (
authenticationv1alpha1 "go.pinniped.dev/generated/1.17/apis/concierge/authentication/v1alpha1"
configv1alpha1 "go.pinniped.dev/generated/1.17/apis/concierge/config/v1alpha1"
identityv1alpha1 "go.pinniped.dev/generated/1.17/apis/concierge/identity/v1alpha1"
loginv1alpha1 "go.pinniped.dev/generated/1.17/apis/concierge/login/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
@@ -22,6 +23,7 @@ var ParameterCodec = runtime.NewParameterCodec(Scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
authenticationv1alpha1.AddToScheme,
configv1alpha1.AddToScheme,
identityv1alpha1.AddToScheme,
loginv1alpha1.AddToScheme,
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -1,4 +1,4 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.

View File

@@ -0,0 +1,7 @@
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated typed clients.
package v1alpha1

View File

@@ -0,0 +1,7 @@
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.
// Package fake has the automatically generated clients.
package fake

View File

@@ -0,0 +1,27 @@
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v1alpha1 "go.pinniped.dev/generated/1.17/client/concierge/clientset/versioned/typed/identity/v1alpha1"
rest "k8s.io/client-go/rest"
testing "k8s.io/client-go/testing"
)
type FakeIdentityV1alpha1 struct {
*testing.Fake
}
func (c *FakeIdentityV1alpha1) WhoAmIRequests() v1alpha1.WhoAmIRequestInterface {
return &FakeWhoAmIRequests{c}
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeIdentityV1alpha1) RESTClient() rest.Interface {
var ret *rest.RESTClient
return ret
}

View File

@@ -0,0 +1,31 @@
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v1alpha1 "go.pinniped.dev/generated/1.17/apis/concierge/identity/v1alpha1"
schema "k8s.io/apimachinery/pkg/runtime/schema"
testing "k8s.io/client-go/testing"
)
// FakeWhoAmIRequests implements WhoAmIRequestInterface
type FakeWhoAmIRequests struct {
Fake *FakeIdentityV1alpha1
}
var whoamirequestsResource = schema.GroupVersionResource{Group: "identity.concierge.pinniped.dev", Version: "v1alpha1", Resource: "whoamirequests"}
var whoamirequestsKind = schema.GroupVersionKind{Group: "identity.concierge.pinniped.dev", Version: "v1alpha1", Kind: "WhoAmIRequest"}
// Create takes the representation of a whoAmIRequest and creates it. Returns the server's representation of the whoAmIRequest, and an error, if there is any.
func (c *FakeWhoAmIRequests) Create(whoAmIRequest *v1alpha1.WhoAmIRequest) (result *v1alpha1.WhoAmIRequest, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootCreateAction(whoamirequestsResource, whoAmIRequest), &v1alpha1.WhoAmIRequest{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.WhoAmIRequest), err
}

View File

@@ -0,0 +1,8 @@
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by client-gen. DO NOT EDIT.
package v1alpha1
type WhoAmIRequestExpansion interface{}

Some files were not shown because too many files have changed in this diff Show More