Compare commits

...

303 Commits

Author SHA1 Message Date
Ryan Richard
b19af2e135 Merge pull request #829 from enj/enj/i/wait_shutdown
Ensure concierge and supervisor gracefully exit
2021-08-31 11:30:35 -07:00
Ryan Richard
883007aa1b Merge pull request #756 from vmware-tanzu/ad-identity-provider-docs
Document how to configure the ActiveDirectoryIdentityProvider
2021-08-31 10:48:25 -07:00
Monis Khan
0d285ce993 Ensure concierge and supervisor gracefully exit
Changes made to both components:

1. Logs are always flushed on process exit
2. Informer cache sync can no longer hang process start up forever

Changes made to concierge:

1. Add pre-shutdown hook that waits for controllers to exit cleanly
2. Informer caches are synced in post-start hook

Changes made to supervisor:

1. Add shutdown code that waits for controllers to exit cleanly
2. Add shutdown code that waits for active connections to become idle

Waiting for controllers to exit cleanly is critical as this allows
the leader election logic to release the lock on exit.  This reduces
the time needed for the next leader to be elected.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-30 20:29:52 -04:00
Matt Moyer
e43bd59688 Merge pull request #830 from mattmoyer/update-youtube-demo-link
Update YouTube demo link to our official page.
2021-08-30 14:30:15 -07:00
Matt Moyer
0c8d885c26 Update YouTube demo link to our official page.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-08-30 16:29:32 -05:00
Mo Khan
d2dfe3634a Merge pull request #828 from enj/enj/i/supervisor_graceful_exit
supervisor: ensure graceful exit
2021-08-28 13:40:13 -04:00
Monis Khan
5489f68e2f supervisor: ensure graceful exit
The kubelet will send the SIGTERM signal when it wants a process to
exit.  After a grace period, it will send the SIGKILL signal to
force the process to terminate.  The concierge has always handled
both SIGINT and SIGTERM as indicators for it to gracefully exit
(i.e. stop watches, controllers, etc).  This change updates the
supervisor to do the same (previously it only handled SIGINT).  This
is required to allow the leader election lock release logic to run.
Otherwise it can take a few minutes for new pods to acquire the
lease since they believe it is already held.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-28 11:23:11 -04:00
Ryan Richard
4eb500cc41 Merge pull request #826 from vmware-tanzu/simplify_readme
Simplify the main README.md to reduce duplication with website
2021-08-27 16:40:53 -07:00
Ryan Richard
871a9fb0c6 Simplify the main README.md to reduce duplication with website 2021-08-27 15:52:51 -07:00
Mo Khan
d580695faa Merge pull request #824 from enj/enj/t/disruptive_hang
test/integration: use short timeouts with distinct requests to prevent hangs
2021-08-27 16:38:39 -04:00
Monis Khan
ba80b691e1 test/integration: use short timeouts with distinct requests to prevent hangs
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-27 16:10:36 -04:00
Mo Khan
41c017c9da Merge pull request #821 from enj/enj/t/increase_disruptive_test_timeout
test/integration: increase timeout on disruptive tests
2021-08-27 15:24:43 -04:00
Monis Khan
5078cdbc90 test/integration: increase timeout on disruptive tests
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-27 14:56:51 -04:00
Margo Crawford
e5718351ba Merge pull request #695 from vmware-tanzu/active-directory-identity-provider
Active directory identity provider
2021-08-27 08:39:12 -07:00
Mo Khan
36ff0d52da Merge pull request #818 from enj/enj/i/bump_go1.17
Bump to Go 1.17.0
2021-08-27 10:30:51 -04:00
Monis Khan
ad3086b8f1 Downgrade go mod compat to 1.16 for golangci-lint
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-27 10:03:48 -04:00
Monis Khan
6c29f347b4 go 1.17 bump: fix unit test failures
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-27 09:46:58 -04:00
Monis Khan
a86949d0be Use go 1.17 module lazy loading
See https://golang.org/doc/go1.17#go-command for details.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-27 09:46:58 -04:00
Monis Khan
44f03af4b9 Bump to Go 1.17.0
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-27 09:00:49 -04:00
Mo Khan
ce5cfde11e Merge pull request #816 from enj/enj/i/bump_1.22.1
Bump Kube to v0.22.1
2021-08-27 08:40:23 -04:00
Monis Khan
40d70bf1fc Bump Kube to v0.22.1
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-27 07:36:12 -04:00
Margo Crawford
19100d68ef Merge branch 'main' of github.com:vmware-tanzu/pinniped into active-directory-identity-provider 2021-08-26 20:42:16 -07:00
Mo Khan
1d44aa945d Merge pull request #814 from mayankbh/topic/bmayank/inherit-hostnetwork
Allow use of hostNetwork for kube-cert-agent
2021-08-26 21:13:29 -04:00
Mayank Bhatt
68547f767d Copy hostNetwork field for kube-cert-agent
For clusters where the control plane nodes aren't running a CNI, the
kube-cert-agent pods deployed by concierge cannot be scheduled as they
don't know to use `hostNetwork: true`. This change allows embedding the
host network setting in the Concierge configuration. (by copying it from
the kube-controller-manager pod spec when generating the kube-cert-agent
Deployment)

Also fixed a stray double comma in one of the nearby tests.
2021-08-26 17:09:59 -07:00
Margo Crawford
44e5e9d8c9 Add sentence about api docs 2021-08-26 17:02:56 -07:00
Margo Crawford
43694777d5 Change some comments on API docs, fix lint error by ignoring it 2021-08-26 16:55:43 -07:00
Ryan Richard
f579b1cb9f Merge pull request #812 from vmware-tanzu/resources_section_web_site
Add "Resources" section to pinniped.dev web site
2021-08-26 16:23:36 -07:00
Margo Crawford
2d32e0fa7d Merge branch 'main' of github.com:vmware-tanzu/pinniped into active-directory-identity-provider 2021-08-26 16:21:08 -07:00
Margo Crawford
6f221678df Change sAMAccountName env vars to userPrincipalName
and add E2E ActiveDirectory test
also fixed regexes in supervisor_login_test to be anchored to the
beginning and end
2021-08-26 16:18:05 -07:00
Ryan Richard
e24040b0a9 add link to CNCF presentation slides 2021-08-26 15:52:04 -07:00
Mo Khan
1d269d2f6d Merge pull request #815 from enj/enj/t/integration_parallel_disruptive
test/integration: mark certain tests as disruptive
2021-08-26 17:32:14 -04:00
Monis Khan
d4a7f0b3e1 test/integration: mark certain tests as disruptive
This prevents them from running with any other test, including other
parallel tests.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-26 15:11:47 -04:00
Mo Khan
d22099ac33 Merge pull request #808 from enj/enj/t/integration_parallel
test/integration: run parallel tests concurrently with serial tests
2021-08-26 14:34:18 -04:00
Monis Khan
e2cf9f6b74 leader election test: approximate that followers have observed change
Instead of blindly waiting long enough for a disruptive change to
have been observed by the old leader and followers, we instead rely
on the approximation that checkOnlyLeaderCanWrite provides - i.e.
only a single actor believes they are the leader.  This does not
account for clients that were in the followers list before and after
the disruptive change, but it serves as a reasonable approximation.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-26 12:59:52 -04:00
Monis Khan
74daa1da64 test/integration: run parallel tests concurrently with serial tests
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-26 12:59:52 -04:00
Ryan Richard
475da05185 Merge pull request #810 from vmware-tanzu/docs_gitops_example
Install docs use more GitOps-friendly style
2021-08-25 16:46:58 -07:00
Ryan Richard
86bfd4f5e4 Number each install step using "1." 2021-08-25 16:37:36 -07:00
Ryan Richard
d453bf3403 Add "Resources" section to pinniped.dev web site 2021-08-25 16:25:53 -07:00
Mo Khan
2b9b034bd2 Merge pull request #811 from vmware-tanzu/test_shell_container_image
Replace one-off usages of busybox and debian images in integration tests
2021-08-25 19:13:13 -04:00
Ryan Richard
d20cab10b9 Replace one-off usages of busybox and debian images in integration tests
Those images that are pulled from Dockerhub will cause pull failures
on some test clusters due to Dockerhub rate limiting.

Because we already have some images that we use for testing, and
because those images are already pre-loaded onto our CI clusters
to make the tests faster, use one of those images and always specify
PullIfNotPresent to avoid pulling the image again during the integration
test.
2021-08-25 15:12:07 -07:00
Ryan Richard
399737e7c6 Install docs use more GitOps-friendly style 2021-08-25 14:33:48 -07:00
Margo Crawford
1c5a2b8892 Add a couple more unit tests 2021-08-25 11:33:42 -07:00
Mo Khan
c17e7bec49 Merge pull request #800 from enj/enj/i/leader_election_release
leader election: fix small race duration lease release
2021-08-25 10:29:19 -04:00
Monis Khan
c71ffdcd1e leader election: use better duration defaults
OpenShift has good defaults for these duration fields that we can
use instead of coming up with them ourselves:

e14e06ba8d/pkg/config/leaderelection/leaderelection.go (L87-L109)

Copied here for easy future reference:

// We want to be able to tolerate 60s of kube-apiserver disruption without causing pod restarts.
// We want the graceful lease re-acquisition fairly quick to avoid waits on new deployments and other rollouts.
// We want a single set of guidance for nearly every lease in openshift.  If you're special, we'll let you know.
// 1. clock skew tolerance is leaseDuration-renewDeadline == 30s
// 2. kube-apiserver downtime tolerance is == 78s
//      lastRetry=floor(renewDeadline/retryPeriod)*retryPeriod == 104
//      downtimeTolerance = lastRetry-retryPeriod == 78s
// 3. worst non-graceful lease acquisition is leaseDuration+retryPeriod == 163s
// 4. worst graceful lease acquisition is retryPeriod == 26s
if ret.LeaseDuration.Duration == 0 {
	ret.LeaseDuration.Duration = 137 * time.Second
}

if ret.RenewDeadline.Duration == 0 {
	// this gives 107/26=4 retries and allows for 137-107=30 seconds of clock skew
	// if the kube-apiserver is unavailable for 60s starting just before t=26 (the first renew),
	// then we will retry on 26s intervals until t=104 (kube-apiserver came back up at 86), and there will
	// be 33 seconds of extra time before the lease is lost.
	ret.RenewDeadline.Duration = 107 * time.Second
}
if ret.RetryPeriod.Duration == 0 {
	ret.RetryPeriod.Duration = 26 * time.Second
}

Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-24 16:21:53 -04:00
Margo Crawford
c590c8ff41 Merge branch 'main' of github.com:vmware-tanzu/pinniped into active-directory-identity-provider 2021-08-24 12:19:29 -07:00
Monis Khan
c0617ceda4 leader election: in-memory leader status is stopped before release
This change fixes a small race condition that occurred when the
current leader failed to renew its lease.  Before this change, the
leader would first release the lease via the Kube API and then would
update its in-memory status to reflect that change.  Now those
events occur in the reverse (i.e. correct) order.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-24 15:02:56 -04:00
Mo Khan
f7751d13fe Merge pull request #778 from vmware-tanzu/oidc_password_grant
Optionally allow OIDC password grant for CLI-based login experience
2021-08-24 13:02:07 -04:00
Mo Khan
3077034b2d Merge branch 'main' into oidc_password_grant 2021-08-24 12:23:52 -04:00
Mo Khan
89cef2ea6c Merge pull request #796 from enj/enj/i/leader_election_flake
leader election test: fix flake related to invalid assumption
2021-08-20 19:06:51 -04:00
Ryan Richard
211f4b23d1 Log auth endpoint errors with stack traces 2021-08-20 14:41:02 -07:00
Monis Khan
132ec0d2ad leader election test: fix flake related to invalid assumption
Even though a client may hold the leader election lock in the Kube
lease API, that does not mean it has had a chance to update its
internal state to reflect that.  Thus we retry the checks in
checkOnlyLeaderCanWrite a few times to allow the client to catch up.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-20 17:04:26 -04:00
Mo Khan
ae505d8009 Merge pull request #788 from enj/enj/i/leader_election
Add Leader Election Middleware
2021-08-20 12:58:27 -04:00
Monis Khan
c356710f1f Add leader election middleware
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-20 12:18:25 -04:00
Matt Moyer
b9d186e8a3 Merge pull request #786 from mattmoyer/cleanup-go-mod
Cleanup `go.mod` replace directives that are no longer needed.
2021-08-20 08:43:36 -07:00
Matt Moyer
03a8160a91 Remove replace directive for dgrijalva/jwt-go.
We no longer have a transitive dependency on this older repository, so we don't need the replace directive anymore.

There is a new fork of this that we should move to (https://github.com/golang-jwt/jwt), but we can't easily do that until a couple of our direct dependencies upgrade.

This is a revert of d162cb9adf.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-08-20 10:15:55 -05:00
Matt Moyer
f379eee7a3 Drop replace directive for oleiade/reflections.
This is reverting 8358c26107.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-08-20 10:15:55 -05:00
Matt Moyer
4f5312807b Undo dep hacks to work around gRPC example module.
This is essentially reverting 87c7e89b13.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-08-20 10:15:54 -05:00
Margo Crawford
cec3c2133a Update with new default values 2021-08-19 16:27:43 -07:00
Margo Crawford
05afae60c2 Review comments--
- Change list of attributeParsingOverrides to a map
- Add unit test for sAMAccountName as group name without the override
- Change some comments in the the type definition.
2021-08-19 14:21:18 -07:00
Ryan Richard
6239a567a8 remove one nolint:unparam comment 2021-08-19 10:57:00 -07:00
Ryan Richard
e4d418a076 Merge branch 'main' into oidc_password_grant 2021-08-19 10:55:54 -07:00
Ryan Richard
c4727d57c8 Merge pull request #789 from vmware-tanzu/remove_unparam_linter
Remove `unparam` linter
2021-08-19 10:55:04 -07:00
Ryan Richard
b4a39ba3c4 Remove unparam linter
We decided that this linter does not provide very useful feedback
for our project.
2021-08-19 10:20:24 -07:00
Ryan Richard
cf627a82cb Merge branch 'main' into oidc_password_grant 2021-08-19 10:00:11 -07:00
Ryan Richard
42d31a7085 Update login.md doc to mention OIDC CLI-based flow 2021-08-19 09:59:47 -07:00
anjalitelang
02b8ed7e0b Update ROADMAP.md
Removing features listed for July as they are shipped.
2021-08-19 12:19:31 -04:00
Margo Crawford
5e9087263d Increase timeout for activedirectoryidentityprovider to be loaded 2021-08-18 16:24:05 -07:00
Margo Crawford
a20aee5f18 Update test assertions to reflect userPrincipalName as username 2021-08-18 13:18:53 -07:00
Margo Crawford
1d18908055 Fix test error-- execcredential now has interactive:false
for activedirectoryidentityprovider test, which didn't exist on main
when #770 was merged to update the other tests to use 1.22.
2021-08-18 12:55:26 -07:00
Margo Crawford
1c5da35527 Merge remote-tracking branch 'origin' into active-directory-identity-provider 2021-08-18 12:44:12 -07:00
Ryan Richard
61c21d2977 Refactor some authorize and callback error handling, and add more tests 2021-08-18 12:06:46 -07:00
Margo Crawford
90e6298e29 Update text on CRD templates to reflect new defaults 2021-08-18 10:39:01 -07:00
Ryan Richard
04b8f0b455 Extract Supervisor authorize endpoint string constants into apis pkg 2021-08-18 10:20:33 -07:00
Margo Crawford
8657b0e3e7 Cleanup new group attribute behavior and add test coverage 2021-08-18 10:11:18 -07:00
Ryan Richard
0089540b07 Extract Supervisor IDP discovery endpoint string constants into apis pkg 2021-08-17 17:50:02 -07:00
Margo Crawford
26c47d564f Make new combined sAMAccountName@domain attribute the group name
Also change default username attribute to userPrincipalName
2021-08-17 16:53:26 -07:00
Ryan Richard
62c6d53a21 Merge branch 'main' into oidc_password_grant 2021-08-17 15:23:29 -07:00
Ryan Richard
96474b3d99 Extract Supervisor IDP discovery endpoint types into apis package 2021-08-17 15:23:03 -07:00
Ryan Richard
964d16110e Some refactors based on PR feedback from @enj 2021-08-17 13:14:09 -07:00
Matt Moyer
d57637ee56 Merge pull request #783 from enj/enj/t/ignore_test_pods
test/integration: ignore restarts associated with test pods
2021-08-17 11:00:19 -07:00
Mo Khan
8ce4bb6dc1 Merge pull request #784 from enj/enj/r/specific_private
dynamiccert: prevent misuse of NewServingCert
2021-08-17 13:56:23 -04:00
Ryan Richard
a7c88b599c Merge branch 'main' into oidc_password_grant 2021-08-17 10:45:00 -07:00
Monis Khan
e0901f4fe5 dynamiccert: prevent misuse of NewServingCert
The Kube API server code that we use will cast inputs in an attempt
to see if they implement optional interfaces.  This change adds a
simple wrapper struct to prevent such casts from causing us any
issues.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-17 12:58:32 -04:00
Monis Khan
cf25c308cd test/integration: ignore restarts associated with test pods
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-17 12:57:41 -04:00
Mo Khan
9d11be899c Merge pull request #785 from enj/enj/i/no_proxy_env
Provide good defaults for NO_PROXY
2021-08-17 12:55:12 -04:00
Monis Khan
66ddcf98d3 Provide good defaults for NO_PROXY
This change updates the default NO_PROXY for the supervisor to not
proxy requests to the Kubernetes API and other Kubernetes endpoints
such as Kubernetes services.

It also adds https_proxy and no_proxy settings for the concierge
with the same default.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-17 10:03:19 -04:00
Ryan Richard
3fb683f64e Update expected error message in e2e integration test 2021-08-16 15:40:34 -07:00
Ryan Richard
52409f86e8 Merge branch 'main' into oidc_password_grant 2021-08-16 15:17:55 -07:00
Ryan Richard
91c8a3ebed Extract private helper in auth_handler.go 2021-08-16 15:17:30 -07:00
Ryan Richard
52cb0bbc07 More unit tests and small error handling changes for OIDC password grant 2021-08-16 14:27:40 -07:00
Mo Khan
eb2a68fec0 Merge pull request #782 from vmware-tanzu/dependabot/go_modules/github.com/go-ldap/ldap/v3-3.4.1
Bump github.com/go-ldap/ldap/v3 from 3.3.0 to 3.4.1
2021-08-16 17:20:06 -04:00
dependabot[bot]
e05a46b7f5 Bump github.com/go-ldap/ldap/v3 from 3.3.0 to 3.4.1
Bumps [github.com/go-ldap/ldap/v3](https://github.com/go-ldap/ldap) from 3.3.0 to 3.4.1.
- [Release notes](https://github.com/go-ldap/ldap/releases)
- [Commits](https://github.com/go-ldap/ldap/compare/v3.3.0...v3.4.1)

---
updated-dependencies:
- dependency-name: github.com/go-ldap/ldap/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-16 20:46:25 +00:00
Mo Khan
46304c8137 Merge pull request #775 from enj/enj/i/dynamiccert_no_unload
impersonatorconfig: only unload dynamiccert when proxy is disabled
2021-08-16 16:36:03 -04:00
Monis Khan
7a812ac5ed impersonatorconfig: only unload dynamiccert when proxy is disabled
In the upstream dynamiccertificates package, we rely on two pieces
of code:

1. DynamicServingCertificateController.newTLSContent which calls
   - clientCA.CurrentCABundleContent
   - servingCert.CurrentCertKeyContent
2. unionCAContent.VerifyOptions which calls
   - unionCAContent.CurrentCABundleContent

This results in calls to our tlsServingCertDynamicCertProvider and
impersonationSigningCertProvider.  If we Unset these providers, we
subtly break these consumers.  At best this results in test slowness
and flakes while we wait for reconcile loops to converge.  At worst,
it results in actual errors during runtime.  For example, we
previously would Unset the impersonationSigningCertProvider on any
sync loop error (even a transient one caused by a network blip or
a conflict between writes from different replicas of the concierge).
This would cause us to transiently fail to issue new certificates
from the token credential require API.  It would also cause us to
transiently fail to authenticate previously issued client certs
(which results in occasional Unauthorized errors in CI).

Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-16 16:07:46 -04:00
Ryan Richard
71d6281e39 Merge branch 'main' into oidc_password_grant 2021-08-16 09:30:13 -07:00
Mo Khan
bb30569e41 Merge pull request #780 from enj/enj/i/browser_stderr
cli: prevent browser output from breaking ExecCredential output
2021-08-16 10:34:33 -04:00
Monis Khan
942c55cf51 cli: prevent browser output from breaking ExecCredential output
This change updates the pinniped CLI entrypoint to prevent browser
processes that we spawn from polluting our std out stream.

For example, chrome will print the following message to std out:

Opening in existing browser session.

Which leads to the following incomprehensible error message from
kubectl:

Unable to connect to the server: getting credentials:
decoding stdout: couldn't get version/kind; json parse error:
json: cannot unmarshal string into Go value of type struct
{ APIVersion string "json:\"apiVersion,omitempty\"";
  Kind string "json:\"kind,omitempty\"" }

This would only occur on the initial login when we opened the
browser.  Since credentials would be cached afterwards, kubectl
would work as expected for future invocations as no browser was
opened.

I could not think of a good way to actually test this change.  There
is a clear gap in our integration tests - we never actually launch a
browser in the exact same way a user does - we instead open a chrome
driver at the login URL as a subprocess of the integration test
binary and not the pinniped CLI.  Thus even if the chrome driver was
writing to std out, we would not notice any issues.

It is also unclear if there is a good way to prevent future related
bugs since std out is global to the process.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-16 09:13:57 -04:00
Ryan Richard
50085a505b First unit test for auth endpoint's password grant and related refactor 2021-08-12 17:53:14 -07:00
Ryan Richard
69964fc788 New unit tests updated for Kube 1.22 ExecCredential changes from main
After merging the new Kube 1.22 ExecCredential changes from main into
this feature branch, some of the new units test on this feature branch
needed to be update to account for the new ExecCredential "interactive"
field.
2021-08-12 13:35:56 -07:00
Ryan Richard
5b96d014b4 Merge branch 'main' into oidc_password_grant 2021-08-12 11:12:57 -07:00
Ryan Richard
84c3c3aa9c Optionally allow OIDC password grant for CLI-based login experience
- Add `AllowPasswordGrant` boolean field to OIDCIdentityProvider's spec
- The oidc upstream watcher controller copies the value of
  `AllowPasswordGrant` into the configuration of the cached provider
- Add password grant to the UpstreamOIDCIdentityProviderI interface
  which is implemented by the cached provider instance for use in the
  authorization endpoint
- Enhance the IDP discovery endpoint to return the supported "flows"
  for each IDP ("cli_password" and/or "browser_authcode")
- Enhance `pinniped get kubeconfig` to help the user choose the desired
  flow for the selected IDP, and to write the flow into the resulting
  kubeconfg
- Enhance `pinniped login oidc` to have a flow flag to tell it which
  client-side flow it should use for auth (CLI-based or browser-based)
- In the Dex config, allow the resource owner password grant, which Dex
  implements to also return ID tokens, for use in integration tests
- Enhance the authorize endpoint to perform password grant when
  requested by the incoming headers. This commit does not include unit
  tests for the enhancements to the authorize endpoint, which will come
  in the next commit
- Extract some shared helpers from the callback endpoint to share the
  code with the authorize endpoint
- Add new integration tests
2021-08-12 10:45:39 -07:00
anjalitelang
592563124b Update ROADMAP.md
Updated the roadmap to reflect the Non-Interactive Password based Login support we delivered in July for LDAP and TBD in Aug for OIDC
2021-08-12 11:08:27 -04:00
Mo Khan
c4d7e5c124 Merge pull request #770 from enj/enj/i/bump_1.22.0
Bump to Go 1.16.7 and Kube v0.22.0
2021-08-10 12:29:35 -04:00
Monis Khan
34fd0ea2e2 impersonation proxy: assert nested UID impersonation is disallowed
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-10 00:03:33 -04:00
Monis Khan
5678fc6196 login: update tests for new client exec code
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-09 19:16:55 -04:00
Monis Khan
4a17e1e736 impersonator: update tests for new Impersonate-Uid code
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-09 19:16:54 -04:00
Monis Khan
724acdca1d Update tests for new CSR duration code
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-09 19:16:50 -04:00
Monis Khan
a027f1ae2c jwtcachefiller: update to use CAContentProvider
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-09 19:16:25 -04:00
Monis Khan
d2891554a4 remove google.golang.org/grpc pin
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-09 19:16:14 -04:00
Monis Khan
25b4d82d87 Bump to Go 1.16.7 and Kube v0.22.0
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-09 15:32:13 -04:00
Mo Khan
01ddc7ac36 Merge pull request #738 from mattmoyer/distroless
Switch to a slimmer distroless base image
2021-08-09 15:31:17 -04:00
Matt Moyer
58bbffded4 Switch to a slimmer distroless base image.
At a high level, it switches us to a distroless base container image, but that also includes several related bits:

- Add a writable /tmp but make the rest of our filesystems read-only at runtime.

- Condense our main server binaries into a single pinniped-server binary. This saves a bunch of space in
  the image due to duplicated library code. The correct behavior is dispatched based on `os.Args[0]`, and
  the `pinniped-server` binary is symlinked to `pinniped-concierge` and `pinniped-supervisor`.

- Strip debug symbols from our binaries. These aren't really useful in a distroless image anyway and all the
  normal stuff you'd expect to work, such as stack traces, still does.

- Add a separate `pinniped-concierge-kube-cert-agent` binary with "sleep" and "print" functionality instead of
  using builtin /bin/sleep and /bin/cat for the kube-cert-agent. This is split from the main server binary
  because the loading/init time of the main server binary was too large for the tiny resource footprint we
  established in our kube-cert-agent PodSpec. Using a separate binary eliminates this issue and the extra
  binary adds only around 1.5MiB of image size.

- Switch the kube-cert-agent code to use a JSON `{"tls.crt": "<b64 cert>", "tls.key": "<b64 key>"}` format.
  This is more robust to unexpected input formatting than the old code, which simply concatenated the files
  with some extra newlines and split on whitespace.

- Update integration tests that made now-invalid assumptions about the `pinniped-server` image.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-08-09 15:05:13 -04:00
Matt Moyer
a464c81711 Bump latest version on website.
We accidentally missed this in the v0.10.0 release process. The new YAML field here should make it easier to automate this step, which seems like a really good idea.
2021-08-03 09:21:54 -05:00
Mo Khan
f18cbcd9a6 Merge pull request #763 from enj/enj/i/eks_slow_test
concierge_impersonation_proxy_test: run slowly for EKS
2021-08-01 18:21:54 -04:00
Monis Khan
ac7d65c4a8 concierge_impersonation_proxy_test: run slowly for EKS
Signed-off-by: Monis Khan <mok@vmware.com>
2021-08-01 18:19:53 -04:00
Matt Moyer
65fa47cbcd Link to the release from our v0.10.0 blog post.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-30 16:35:38 -05:00
Matt Moyer
a06b38d2cd Merge pull request #760 from mattmoyer/add-v0.10.0-blog-post
Add v0.10.0 blog post.
2021-07-30 16:21:36 -05:00
Matt Moyer
7773fb8afe Add v0.10.0 blog post.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-30 15:30:56 -05:00
Matt Moyer
f4badb3961 Merge pull request #758 from mattmoyer/use-plain-authcode-prompt
Fix broken TTY after manual auth code prompt.
2021-07-30 13:50:27 -05:00
Matt Moyer
1e32530d7b Fix broken TTY after manual auth code prompt.
This may be a temporary fix. It switches the manual auth code prompt to use `promptForValue()` instead of `promptForSecret()`. The `promptForSecret()` function no longer supports cancellation (the v0.9.2 behavior) and the method of cancelling in `promptForValue()` is now based on running the blocking read in a background goroutine, which is allowed to block forever or leak (which is not important for our CLI use case).

This means that the authorization code is now visible in the user's terminal, but this is really not a big deal because of PKCE and the limited lifetime of an auth code.

The main goroutine now correctly waits for the "manual prompt" goroutine to clean up, which now includes printing the extra newline that would normally have been entered by the user in the manual flow.

The text of the manual login prompt is updated to be more concise and less scary (don't use the word "fail").

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-30 12:45:44 -05:00
Matt Moyer
0ab8e14e4a Merge pull request #755 from mattmoyer/update-installation-docs
Update installation documentation
2021-07-29 17:54:23 -05:00
Mo Khan
f1109afa79 Merge pull request #757 from enj/enj/t/dns_hacks
concierge_impersonation_proxy_test: check all forms of DNS
2021-07-29 15:51:22 -04:00
Monis Khan
22be97eeda concierge_impersonation_proxy_test: check all forms of DNS
Signed-off-by: Monis Khan <mok@vmware.com>
2021-07-29 13:35:37 -04:00
Matt Moyer
d23f3c9428 Update ROADMAP.md 2021-07-29 10:22:43 -05:00
Matt Moyer
c3e037b24e Fix a broken link in .../docs/howto/configure-supervisor.md.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-29 09:56:00 -05:00
Matt Moyer
62afb34877 Fix command typo and expand description of values.yaml a bit.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-29 08:45:19 -05:00
Matt Moyer
fd5ed2e5da Rework "install" sections of our docs.
- Remove all the "latest" links and replace them with our new shortcode so they point at the latest release in a more explicit way.
  This also eliminates one of the sections in our Concierge and Supervisor install guides, since you're always installing a specific version.

- Provide instructions for installing with both kapp (one step) and kubectl (two steps for the Concierge).

- Minor wording changes. Mainly we are now a bit less verbose about reminding people they can choose a different version (once per page instead of in each step).

- When we give an example `kapp deploy` command, don't suggest `--yes` and `--diff-changes`.
  Users can still use these but it seems overly verbose for an example command.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-29 08:45:19 -05:00
Matt Moyer
ca82609d1a Create a site parameter and shortcode for "latestversion".
This gives us a single line of YAML to edit when we want to bump our docs to the latest version number.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-29 08:45:18 -05:00
Margo Crawford
a6dc5b912f Document how to configure the ActiveDirectoryIdentityProvider 2021-07-28 14:35:29 -07:00
Ryan Richard
d73093a694 Avoid failures due to impersonation Service having unrelated annotations 2021-07-28 14:19:14 -07:00
Matt Moyer
85560299e0 Merge pull request #754 from mattmoyer/fix-TestLegacyPodCleaner-flake
Relax the timeout for TestLegacyPodCleaner a bit.
2021-07-28 12:43:08 -06:00
Matt Moyer
b42b1c1110 Relax the timeout for TestLegacyPodCleaner a bit.
This test is asynchronously waiting for the controller to do something, and in some of our test environments it will take a bit longer than we'd previously allowed.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-28 13:08:57 -05:00
Matt Moyer
84733405d0 Merge pull request #753 from mattmoyer/fix-e2e-test-assertion
Fix backwards condition in E2E test assertion.
2021-07-28 12:07:00 -06:00
Matt Moyer
48c8fabb5c Fix backwards condition in E2E test assertion.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-28 12:40:07 -05:00
Matt Moyer
1f51159d22 Merge pull request #752 from mattmoyer/fix-impersonator-config-controller-informers
Add ClusterIP service to impersonator-config-controller informer.
2021-07-28 11:28:30 -06:00
Matt Moyer
5f679059d5 Add ClusterIP service to impersonator-config-controller informer.
Prior to this fix, this controller did not correctly react to changes to the ClusterIP service. It would still eventually react with a long delay due to our 5 minute resync interval.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-28 11:57:18 -05:00
Ryan Richard
8afbb4eb4f Merge pull request #744 from vmware-tanzu/dependabot/go_modules/github.com/tdewolff/minify/v2-2.9.20
Bump github.com/tdewolff/minify/v2 from 2.9.19 to 2.9.20
2021-07-28 09:08:22 -07:00
Ryan Richard
c9b4598fa0 Merge pull request #745 from vmware-tanzu/dependabot/go_modules/github.com/creack/pty-1.1.14
Bump github.com/creack/pty from 1.1.13 to 1.1.14
2021-07-28 09:08:03 -07:00
Mo Khan
ef33846d7d Merge pull request #747 from enj/enj/i/delete_race
certs_expirer: be specific about what secret to delete
2021-07-28 10:32:17 -04:00
Monis Khan
8b4ed86071 certs_expirer: be specific about what secret to delete
This change fixes a race that can occur because we have multiple
writers with no leader election lock.

1. TestAPIServingCertificateAutoCreationAndRotation/automatic
   expires the current serving certificate
2. CertsExpirerController 1 deletes expired serving certificate
3. CertsExpirerController 2 starts deletion of expired serving
   certificate but has not done so yet
4. CertsManagerController 1 creates new serving certificate
5. TestAPIServingCertificateAutoCreationAndRotation/automatic
   records the new serving certificate
6. CertsExpirerController 2 finishes deletion, and thus deletes the
   newly created serving certificate instead of the old one
7. CertsManagerController 2 creates new serving certificate
8. TestAPIServingCertificateAutoCreationAndRotation/automatic keeps
   running and eventually times out because it is expecting the
   serving certificate created by CertsManagerController 2 to match
   the value it recorded from CertsManagerController 1 (which will
   never happen since that certificate was incorrectly deleted).

Signed-off-by: Monis Khan <mok@vmware.com>
2021-07-28 09:56:05 -04:00
Mo Khan
8b74dd824b Merge pull request #748 from mattmoyer/fix-css-text-wrapping
Fix form_post CSS styling in Firefox and Safari.
2021-07-28 09:55:39 -04:00
Matt Moyer
727035a2dc Fix form_post CSS styling in Firefox and Safari.
This functioned fine, but did not have the intended visual appearance when it came to how the text of the auth code wrapped inside the copy button in the manual flow.

The new styling behaves correctly on at least Chrome, Firefox, and Safari on macOS.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-28 08:09:20 -05:00
dependabot[bot]
fc82fde585 Bump github.com/tdewolff/minify/v2 from 2.9.19 to 2.9.20
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.19 to 2.9.20.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.9.19...v2.9.20)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-28 00:03:48 +00:00
dependabot[bot]
f352db8072 Bump github.com/creack/pty from 1.1.13 to 1.1.14
Bumps [github.com/creack/pty](https://github.com/creack/pty) from 1.1.13 to 1.1.14.
- [Release notes](https://github.com/creack/pty/releases)
- [Commits](https://github.com/creack/pty/compare/v1.1.13...v1.1.14)

---
updated-dependencies:
- dependency-name: github.com/creack/pty
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-28 00:03:39 +00:00
Margo Crawford
f075d95183 Merge branch 'main' of github.com:vmware-tanzu/pinniped into active-directory-identity-provider 2021-07-27 15:16:52 -07:00
Matt Moyer
22a66c1192 Merge pull request #746 from mattmoyer/fix-windows-build
Fix CLI compilation on Windows.
2021-07-27 16:15:01 -06:00
Margo Crawford
474266f918 Merge branch 'main' of github.com:vmware-tanzu/pinniped into active-directory-identity-provider 2021-07-27 15:06:58 -07:00
Matt Moyer
8e8af51955 Fix CLI compilation on Windows.
It turns out that `syscall.Stdin` is of type `int` on Linux and macOS, but not on Windows (it's `syscall.Handle`). This should now be portable and do all the require type casting on every platform.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-27 16:10:05 -05:00
Ryan Richard
d5759c9951 Merge pull request #739 from vmware-tanzu/merge_impersonator_service_annotations
Carefully merge desired annotations into impersonation proxy Service
2021-07-27 12:40:31 -07:00
Ryan Richard
bf99348faf Merge branch 'main' into merge_impersonator_service_annotations 2021-07-27 12:40:02 -07:00
Mo Khan
2789af79f6 Merge pull request #742 from enj/enj/i/bump_1.21.3
Bump to Go 1.16.6 and Kube v0.21.3
2021-07-27 15:34:37 -04:00
Ryan Richard
71cae75758 Merge branch 'main' into merge_impersonator_service_annotations 2021-07-27 11:57:16 -07:00
Ryan Richard
90db3ad51b Merge pull request #730 from vmware-tanzu/cli_username_password_env_vars
LDAP logins via CLI read from `PINNIPED_USERNAME` and `PINNIPED_PASSWORD` env vars
2021-07-27 11:56:40 -07:00
Monis Khan
32c9aa5087 Bump to Go 1.16.6 and Kube v0.21.3
Signed-off-by: Monis Khan <mok@vmware.com>
2021-07-27 14:18:08 -04:00
Margo Crawford
bbaa820278 parsing objectGUID as human-readable string version 2021-07-27 11:08:23 -07:00
Margo Crawford
287a5d225a Change SearchBaseFound condition success reason to be a string constant 2021-07-27 10:23:05 -07:00
Ryan Richard
f17f7c0c6a Small refactors in impersonator_config.go suggested by @mattmoyer 2021-07-26 17:46:06 -07:00
Ryan Richard
54c5bcc9a1 Merge branch 'main' into merge_impersonator_service_annotations 2021-07-26 17:25:52 -07:00
Ryan Richard
58ab57201f Suppress lint errors 2021-07-26 17:20:49 -07:00
Ryan Richard
f4829178b3 Use sentence case for headers in docs
Following some common developer style guides such as
Google
https://developers.google.com/style/capitalization#capitalization-in-titles-and-headings
and Microsoft
https://docs.microsoft.com/en-us/style-guide/scannable-content/headings#formatting-headings
2021-07-26 17:18:44 -07:00
Ryan Richard
295f013580 Merge branch 'main' into cli_username_password_env_vars 2021-07-26 17:04:46 -07:00
Margo Crawford
53b58f65b2 Add integration test for wrong password with ldap 2021-07-26 16:32:46 -07:00
Margo Crawford
cc3875f048 PR feedback 2021-07-26 16:03:12 -07:00
Ryan Richard
d8e1521457 Merge pull request #741 from vmware-tanzu/dockerignore
dockerignore gets same contents as gitignore
2021-07-26 12:47:26 -07:00
Ryan Richard
e150111b27 dockerignore gets same contents as gitignore 2021-07-26 11:28:25 -07:00
Ryan Richard
9e27c28b39 Fix TestImpersonationProxy integration test changes from previous commit
Forgot to account for our new booking annotation on the impersonator's
Service.
2021-07-23 14:23:24 -07:00
Margo Crawford
5d23068690 Removed a todo that was resolved 2021-07-23 13:01:41 -07:00
Margo Crawford
1050f39789 Integration test deactivated ad account 2021-07-23 13:01:41 -07:00
Margo Crawford
00978c15f7 Update wording for ActiveDirectoryIdentityProvider crd 2021-07-23 13:01:41 -07:00
Margo Crawford
8ea1bd3dfb Make prepare-for-integration-tests active directory setup accessible for anyone 2021-07-23 13:01:41 -07:00
Margo Crawford
91085e68f9 Refactoring defaulting logic 2021-07-23 13:01:41 -07:00
Margo Crawford
f99f7be836 Default values for ad usersearch and groupsearch 2021-07-23 13:01:41 -07:00
Margo Crawford
890d9c3216 resolve some todos about error handling search base discovery results 2021-07-23 13:01:41 -07:00
Margo Crawford
cb0ee07b51 Fetch AD search base from defaultNamingContext when not specified 2021-07-23 13:01:41 -07:00
Margo Crawford
8e1d70562d Remove shared variables from ldap upstream observer 2021-07-23 13:01:41 -07:00
Margo Crawford
5d8d7246c2 Refactor active directory and ldap controllers to share almost everything
Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-07-23 13:01:41 -07:00
Ryan Richard
3b4f521596 Changed TestLDAPUpstream.TestUsernameAttributeName back to TestUserMailAttributeName
Also added TestUserSAMAccountNameValue

Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-07-23 13:01:40 -07:00
Margo Crawford
e5c8cbb3a4 One line fix for lint error. Forgot a period in a comment.
Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-07-23 13:01:40 -07:00
Margo Crawford
7696f4256d Move defaulting of ad username and uid attributes to controller
Now the controller uses upstreamldap so there is less duplication,
since they are very similar.

Signed-off-by: Ryan Richard <richardry@vmware.com>
2021-07-23 13:01:40 -07:00
Ryan Richard
aaa4861373 Custom API Group overlay for AD
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-07-23 13:01:40 -07:00
Margo Crawford
b3d0b28bd0 Integration test fixes, fixing objectGUID handling 2021-07-23 13:01:40 -07:00
Margo Crawford
5c283d941c Helper script for running active directory tests 2021-07-23 13:01:40 -07:00
Margo Crawford
94e90a5d26 groups related env variables for AD 2021-07-23 13:01:40 -07:00
Margo Crawford
be6f9f83ce RBAC rules for activedirectoryidentityprovider 2021-07-23 13:01:40 -07:00
Margo Crawford
3b8edb84a5 WIP on active directory integration test 2021-07-23 13:01:40 -07:00
Margo Crawford
8fb35c6569 Active Directory cli options 2021-07-23 13:01:40 -07:00
Margo Crawford
3899292e89 Advertise Active Directory idps 2021-07-23 13:01:40 -07:00
Margo Crawford
b06de69f6a ActiveDirectoryIdentityProvider
- Create CRD
- Create implementation of AD-specific user search defaults
2021-07-23 13:01:40 -07:00
Ryan Richard
ac4bc02817 Enhance integration test for CredentialIssuer spec annotations 2021-07-23 09:46:40 -07:00
Ryan Richard
708164b878 Carefully merge desired annotations into impersonation proxy Service
Don't overwrite annotations that might have come from a human user or
from some other non-Pinniped controller.
2021-07-22 17:09:50 -07:00
Ryan Richard
e30cf6e51a Merge branch 'main' into cli_username_password_env_vars 2021-07-22 09:29:03 -07:00
Matt Moyer
ee30b78117 Update ROADMAP.md
Bump "Wider Concierge cluster support" to August.
2021-07-22 10:30:45 -05:00
Ryan Richard
64aba7e703 Add new howto guide login.md 2021-07-21 12:10:47 -07:00
Matt Moyer
c6c3a80a86 Merge pull request #733 from mattmoyer/switch-tools-images
Switch to GHCR tools images for local tests, with `imagePullPolicy: IfNotPresent`.
2021-07-21 11:47:37 -06:00
Margo Crawford
a7af63ca3a Merge pull request #729 from rdimitrov/dimitrovr/add-dex-docs
Add documentation for configuring Supervisor with Dex and Github
2021-07-21 08:48:49 -07:00
Matt Moyer
ae72d30cec Switch to GHCR tools images for local tests, with imagePullPolicy: IfNotPresent.
This is more consistent with our CI environment.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-21 09:21:05 -05:00
Nanci Lancaster
fec59eb1bf Merge pull request #731 from microwavables/main
Removed Andrew Keesler, Pablo Schumaker from site, moved them to emeritus status on maintainers file,
2021-07-20 15:37:04 -07:00
Radoslav Dimitrov
f6273b0604 Update the Prerequisites section and add a note about the groups scope
Add Dex to the prerequisites and add a note that to query for the groups
scope the user must set the organizations Dex should search against.
Otherwise the groups claim would be empty. This is because of the format
group claims are represented, i.e. "org:team".

Signed-off-by: Radoslav Dimitrov <dimitrovr@vmware.com>
2021-07-20 13:49:45 +03:00
Ryan Richard
deb699a84a e2e test: PINNIPED_USERNAME/PINNIPED_PASSWORD env vars during LDAP login 2021-07-19 17:08:52 -07:00
Ryan Richard
cac45fd999 LDAP logins read from PINNIPED_USERNAME and PINNIPED_PASSWORD env vars
For CLI-based auth, such as with LDAP upstream identity providers, the
user may use these environment variables to avoid getting interactively
prompted for username and password.
2021-07-19 16:20:59 -07:00
Radoslav Dimitrov
0bdd1bc68f Add documentation for configuring Supervisor with Dex and Github
The following guide describes the process of configuring Supervisor
with Dex and identify users through their Github account. Issue #415

Signed-off-by: Radoslav Dimitrov <dimitrovr@vmware.com>
2021-07-19 16:00:43 +03:00
Mo Khan
4605846499 Merge pull request #724 from vmware-tanzu/fix_git_sha_in_version_info
Copy .git dir during Docker build; used to bake git sha into binary
2021-07-16 14:34:33 -04:00
Ryan Richard
4670890a82 Add .git dir to Docker; used to bake git sha into binary 2021-07-16 09:51:46 -07:00
Margo Crawford
d204b46c18 Merge pull request #721 from vmware-tanzu/resolve-load-balancer-dns
wait for lb dns to resolve in the impersonation proxy integration test
2021-07-15 17:02:08 -07:00
Ryan Richard
b3208f0ca6 wait for lb dns to resolve in the impersonation proxy integration test
this will hopefully fix some flakes where aws provisioned a host for the
load balancer but the tests weren't able to resolve it.

Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-07-15 16:39:15 -07:00
Ryan Richard
be7bf9c193 Merge pull request #718 from vmware-tanzu/workaround_for_flaky_unit_test
TestAgentController unit test is flaky, try to add workaround
2021-07-15 14:17:11 -07:00
Ryan Richard
2bba39d723 TestAgentController unit test is flaky, try to add workaround
TestAgentController really runs the controller and evaluates multiple
calls to the controller's Sync with real informers caching updates.
There is a large amount of non-determinism in this unit test, and it
does not always behave the same way. Because it makes assertions about
the specific errors that should be returned by Sync, it was not
accounting for some errors that are only returned by Sync once in a
while depending on the exact (unpredictable) order of operations.

This commit doesn't fix the non-determinism in the test, but rather
tries to work around it by also allowing other (undesired but
inevitable) error messages to appear in the list of actual error
messages returned by the calls to the Sync function.

Signed-off-by: Margo Crawford <margaretc@vmware.com>
2021-07-15 13:41:31 -07:00
anjalitelang
dc567d0d1f Update ROADMAP.md
Added https://github.com/vmware-tanzu/pinniped/issues/577 to Roadmap
2021-07-15 12:29:51 -04:00
Ryan Richard
143837c136 Merge pull request #714 from vmware-tanzu/ytt_install_doc_fix
ytt install docs suggest that you checkout the release tag
2021-07-14 12:52:23 -07:00
Ryan Richard
11eb18d348 ytt install docs suggest that you checkout the release tag
Previously, the ytt install docs suggested that you use ytt templates
from the HEAD of main with the container image from the latest public
release, which could result in a mismatch.
2021-07-14 10:59:51 -07:00
Ryan Richard
d5cf5b91d6 Merge pull request #711 from vmware-tanzu/e2e_test_clear_cookies
Clear the browser cookies between each TestE2EFullIntegration subtest
2021-07-13 16:43:57 -07:00
Ryan Richard
48b58e2fad Clear the browser cookies between each TestE2EFullIntegration test
It seems like page.ClearCookies() only clears cookies for the current
domain, so there doesn't seem to be a function to clear all browser
cookies. Instead, we'll just start a whole new browser each test.
They start fast enough that it shouldn't be a problem.
2021-07-13 16:20:02 -07:00
Ryan Richard
7ef3d42e01 Merge pull request #704 from mattmoyer/deflake-serving-certificate-rotation-test
Make TestAPIServingCertificateAutoCreationAndRotation less flaky.
2021-07-13 14:58:54 -07:00
Ryan Richard
33461ddc14 Merge branch 'main' into deflake-serving-certificate-rotation-test 2021-07-13 14:04:34 -07:00
Mo Khan
238c9e6743 Merge pull request #709 from vmware-tanzu/dependabot/go_modules/github.com/tdewolff/minify/v2-2.9.19
Bump github.com/tdewolff/minify/v2 from 2.9.18 to 2.9.19
2021-07-12 14:48:16 -04:00
dependabot[bot]
25cda4f3e6 Bump github.com/tdewolff/minify/v2 from 2.9.18 to 2.9.19
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.18 to 2.9.19.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.9.18...v2.9.19)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-12 01:20:59 +00:00
Matt Moyer
c71703e4db Merge pull request #707 from mattmoyer/fix-okta-cli-integration-test
Fix TestCLILoginOIDC when running against Okta, and lower CLI server shutdown timeout.
2021-07-09 14:30:19 -07:00
Matt Moyer
5527566a36 Fix TestCLILoginOIDC when running directly against Okta.
Our actual CLI code behaved correctly, but this test made some invalid assumptions about the "upstream" IDP we're testing. It assumed that the upstream didn't support `response_mode=form_post`, but Okta does. This means that when we end up on the localhost callback page, there are no URL query parameters.

Adjusting this regex makes the test pass as expected.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 16:29:42 -05:00
Matt Moyer
b6580b303a Reduce CLI callback shutdown timeout (5s -> 500ms).
I found that there are some situations with `response_mode=form_post` where Chrome will open additional speculative TCP connections. These connections will be idle so they block server shutdown until the (previously 5s) timeout. Lowering this to 500ms should be safe and makes any added latency at login much less noticeable.

More information about Chrome's TCP-level behavior here: https://bugs.chromium.org/p/chromium/issues/detail?id=116982#c5

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 16:29:29 -05:00
Matt Moyer
405a27ba90 Merge pull request #687 from mattmoyer/add-response-mode-form-post
Add support for "response_mode=form_post" in Supervisor and CLI.
2021-07-09 10:37:59 -07:00
Matt Moyer
43f66032a9 Extend TestE2EFullIntegration to test manual OIDC flow.
Using the same fake TTY trick we used to test LDAP login, this new subtest runs through the "manual"/"jump box" login flow. It runs the login with a `--skip-listen` flag set, causing the CLI to skip opening the localhost listener. We can then wait for the login URL to be printed, visit it with the browser and log in, and finally simulate "manually" copying the auth code from the browser and entering it into the waiting CLI prompt.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:45 -05:00
Matt Moyer
91a1fec5cf Add hidden --skip-listen flag for pinniped login oidc.
This flag is (for now) meant only to facilitate end-to-end testing, allowing us to force the "manual" login flow. If it ends up being useful we can un-hide it, but this seemed like the safest option to start with.

There is also a corresponding `--oidc-skip-listen` on the `pinniped get kubeconfig` command.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:44 -05:00
Matt Moyer
d0b37a7c90 Adjust TestFormPostHTML to work on Linux chromedriver.
For some reason our headless Chrome test setup behaves slightly differently on Linux and macOS hosts. On Linux, the emoji characters are not recognized as valid text, so they are URL encoded. This change updates the test to cope with both cases correctly.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:44 -05:00
Matt Moyer
5029495fdb Add manual paste flow to pinniped login oidc command.
This adds a new login flow that allows manually pasting the authorization code instead of receiving a browser-based callback.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:44 -05:00
Matt Moyer
ac6ff1a03c Deprecate oidcclient.WithBrowserOpen() option, add simpler oidcclient.WithSkipBrowserOpen().
This is a more restrictive library interface that more closely matches the use cases of our new form_post login flow.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:44 -05:00
Matt Moyer
95ee9f0b00 Add ctx params to promptForValue() and promptForSecret().
This allows the prompts to be cancelled, which we need to be able to do in the case where we prompt for a manually-pasted auth code but the automatic callback succeeds.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:44 -05:00
Matt Moyer
9fba8d2203 Adjust TestE2EFullIntegration for new form_post flow.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:44 -05:00
Matt Moyer
428f389c7d Add missing t.Helper() on RequireEventuallyf().
This gives us nicer test assertion failure messages.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:44 -05:00
Matt Moyer
71d4e05fb6 Add custom response_mode=form_post HTML template.
This is a new pacakge internal/oidc/provider/formposthtml containing a number of static files embedded using the relatively recent Go "//go:embed" functionality introduced in Go 1.16 (https://blog.golang.org/go1.16).

The Javascript and CSS files are minifiied and injected to make a single self-contained HTML response. There is a special Content-Security-Policy helper to calculate hash-based script-src and style-src rules.

This new code is covered by a new integration test that exercises the JS/HTML functionality in a real browser outside of the rest of the Supervisor.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:43 -05:00
Matt Moyer
1904f8ddc3 In browsertest.Open(), capture console INFO logs.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:43 -05:00
Matt Moyer
6b801056b5 Add testlib.RandBytes() helper.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:43 -05:00
Matt Moyer
674cd4a88c Adjust our securityheader pkg to support form_post.
Our Supervisor callback handler now needs to load JS and CSS from the provider endpoint, and this JS needs to make a `fetch()` call across origins (to post the form to the CLI callback). This requires a custom Content-Security-Policy compared to other pages we render.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:43 -05:00
Matt Moyer
7217cf4892 In form_post mode, expect params via POST'ed form.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:43 -05:00
Matt Moyer
40c931bdc5 When supported, use "response_mode=form_post" in client.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:43 -05:00
Matt Moyer
2823d4d1e3 Add "response_modes_supported" to Supervisor discovery response.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:43 -05:00
Matt Moyer
6d83ecb420 Unit test response_mode=form_post in internal/oidc/callback.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:43 -05:00
Matt Moyer
c27eb17f23 Add "response_mode=form_post" to CLI client.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 12:08:42 -05:00
Matt Moyer
58363bca2c Merge pull request #705 from mattmoyer/deflake-impersonation-websocket-test
Make TestImpersonationProxy less flaky.
2021-07-09 10:06:14 -07:00
Matt Moyer
3bf39797bb Merge pull request #706 from mattmoyer/fix-api-doc-comment
Fix typo in generated API docs (s/mode/type/).
2021-07-09 10:05:05 -07:00
Matt Moyer
3a840cee76 Make TestAPIServingCertificateAutoCreationAndRotation less flaky.
This test would occasionally flake for me when running locally. This change moves more of the assertions into the "eventually" loop, so they can temporarily fail as long as they converge on the expected values.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 11:29:02 -05:00
Matt Moyer
04e9897d51 Make TestImpersonationProxy less flaky.
This test did not tolerate this connection failing, which can happen for any number of flaky networking-related reasons. This change moves the connection setup into an "eventually" retry loop so it's allowed to fail temporarily as long as it eventually connects.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 11:28:33 -05:00
Matt Moyer
ff9095f9c4 Fix typo in generated API docs (s/mode/type/).
This CredentialIssuer field is called `spec.impersonationProxy.service.type`, not `spec.impersonationProxy.service.mode`.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-07-09 11:28:02 -05:00
Matt Moyer
2e18c88e33 Merge pull request #684 from christianang/oidc-upstream-watcher-supports-proxy
Add IPv6 support to FederationDomain spec.issuer field.
2021-07-09 09:14:39 -07:00
Matt Moyer
9f91c6c884 Merge branch 'main' into oidc-upstream-watcher-supports-proxy 2021-07-09 07:24:52 -07:00
Ryan Richard
59fd1997f4 Merge pull request #703 from vmware-tanzu/ldap-client-int-tests-only-on-kind
Run the LDAP client's integration tests only on Kind
2021-07-08 12:55:03 -07:00
Ryan Richard
74f3ce5dcd Merge branch 'main' into ldap-client-int-tests-only-on-kind 2021-07-08 12:54:56 -07:00
Ryan Richard
d403c8b44b Merge pull request #702 from vmware-tanzu/supervisor-https-proxy-fix-timeout
Fix broken upstream OIDC discovery timeout added in previous commit
2021-07-08 12:54:39 -07:00
Ryan Richard
e130da6daa Add unit test assertion for new OIDC client request timeout 2021-07-08 11:47:49 -07:00
Ryan Richard
2f7dbed321 Try increasing the "eventually" timeouts in one integration test
There were 10 second timeouts in
`TestAPIServingCertificateAutoCreationAndRotation` which fail often
on CI. Maybe increasing the timeouts will help?
2021-07-08 11:17:22 -07:00
Ryan Richard
709c10227f Run the LDAP client's integration tests only on Kind
TestSimultaneousLDAPRequestsOnSingleProvider proved to be unreliable
on AKS due to some kind of kubectl port-forward issue, so only
run the LDAP client's integration tests on Kind. They are testing
the integration between the client code and the OpenLDAP test server,
not testing anything about Kubernetes, so running only on Kind should
give us sufficient test coverage.
2021-07-08 11:10:53 -07:00
Ryan Richard
f0d120a6ca Fix broken upstream OIDC discovery timeout added in previous commit
After noticing that the upstream OIDC discovery calls can hang
indefinitely, I had tried to impose a one minute timeout on them
by giving them a timeout context. However, I hadn't noticed that the
context also gets passed into the JWKS fetching object, which gets
added to our cache and used later. Therefore the timeout context
was added to the cache and timed out while sitting in the cache,
causing later JWKS fetchers to fail.

This commit is trying again to impose a reasonable timeout on these
discovery and JWKS calls, but this time by using http.Client's Timeout
field, which is documented to be a timeout for *each* request/response
cycle, so hopefully this is a more appropriate way to impose a timeout
for this use case. The http.Client instance ends up in the cache on
the JWKS fetcher object, so the timeout should apply to each JWKS
request as well.

Requests that can hang forever are effectively a server-side resource
leak, which could theoretically be taken advantage of in a denial of
service attempt, so it would be nice to avoid having them.
2021-07-08 09:44:02 -07:00
Ryan Richard
1f5480cd5c Merge pull request #701 from vmware-tanzu/supervisor-https-proxy
Add `https_proxy` and `no_proxy` settings for the Supervisor
2021-07-07 14:57:38 -07:00
Ryan Richard
f1e63c55d4 Add https_proxy and no_proxy settings for the Supervisor
- Add new optional ytt params for the Supervisor deployment.
- When the Supervisor is making calls to an upstream OIDC provider,
  use these variables if they were provided.
- These settings are integration tested in the main CI pipeline by
  sometimes setting them on deployments in certain cases, and then
  letting the existing integration tests (e.g. TestE2EFullIntegration)
  provide the coverage, so there are no explicit changes to the
  integration tests themselves in this commit.
2021-07-07 12:50:13 -07:00
Matt Moyer
562951b77a Merge branch 'main' into oidc-upstream-watcher-supports-proxy 2021-07-06 11:30:09 -07:00
Matt Moyer
dbd2cb4563 Merge pull request #696 from vmware-tanzu/dependabot/go_modules/github.com/spf13/cobra-1.2.1
Bump github.com/spf13/cobra from 1.2.0 to 1.2.1
2021-07-06 07:04:02 -07:00
dependabot[bot]
1c746feafe Bump github.com/spf13/cobra from 1.2.0 to 1.2.1
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.2.0 to 1.2.1.
- [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.2.0...v1.2.1)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-05 01:28:57 +00:00
Ryan Richard
49683975ab Merge pull request #689 from vmware-tanzu/trivial_refactor
Extract some trivial helpers for identical code usages
2021-07-02 14:56:26 -07:00
Ryan Richard
4be26fc1a6 Merge branch 'main' into trivial_refactor 2021-07-02 13:48:27 -07:00
Matt Moyer
f590a3a88b Merge pull request #692 from vmware-tanzu/dependabot/go_modules/github.com/spf13/cobra-1.2.0
Bump github.com/spf13/cobra from 1.1.3 to 1.2.0
2021-07-02 07:24:55 -07:00
dependabot[bot]
e26486bd41 Bump github.com/spf13/cobra from 1.1.3 to 1.2.0
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.1.3 to 1.2.0.
- [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.3...v1.2.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-02 01:11:11 +00:00
Matt Moyer
5c2e890ecd Add "Extended IDP support" to the roadmap. 2021-07-01 10:48:13 -05:00
Matt Moyer
715cf7748a Add "Identity transforms" feature to roadmap. 2021-07-01 10:17:40 -05:00
Matt Moyer
e0456b4485 Update ROADMAP given current state of work 2021-07-01 10:05:30 -05:00
Ryan Richard
629bf61655 Extract some trivial helpers for identical code usages 2021-06-30 15:02:14 -07:00
Matt Moyer
738e6aa3cc Merge pull request #685 from vmware-tanzu/dependabot/go_modules/github.com/gofrs/flock-0.8.1
Bump github.com/gofrs/flock from 0.8.0 to 0.8.1
2021-06-30 10:53:39 -07:00
Guangyuan Wang
76dc39ac2d Use hostname instead of host and split on ":"
Co-authored-by: Christian Ang <angc@vmware.com>
Co-authored-by: Tyler Schultz <tschultz@vmware.com>
2021-06-28 23:03:05 +00:00
dependabot[bot]
43fee6bb94 Bump github.com/gofrs/flock from 0.8.0 to 0.8.1
Bumps [github.com/gofrs/flock](https://github.com/gofrs/flock) from 0.8.0 to 0.8.1.
- [Release notes](https://github.com/gofrs/flock/releases)
- [Commits](https://github.com/gofrs/flock/compare/v0.8.0...v0.8.1)

---
updated-dependencies:
- dependency-name: github.com/gofrs/flock
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-28 01:28:52 +00:00
Christian Ang
8026729c43 Use net.JoinHostPort instead of Sprintf
Co-authored-by: Guangyuan Wang <wguangyuan@vmware.com>
2021-06-24 23:19:11 +00:00
Guangyuan Wang
d19d63ad7d Set Proxy on oidc upstream watcher transport
- this allows the oidc upsream watcher to honor the
HTTP_PROXY,HTTPS_PROXY,NO_PROXY environment variables

Co-authored-by: Christian Ang <angc@vmware.com>
2021-06-24 22:35:16 +00:00
Mo Khan
a6141e911c Merge pull request #683 from enj/enj/i/credentialrequest_notafter
credentialrequest: use safer approximation for ExpirationTimestamp
2021-06-23 11:55:29 -04:00
Monis Khan
5ff2be973c credentialrequest: use safer approximation for ExpirationTimestamp
We want the value of time.Now() to be calculated before the call to
IssueClientCertPEM to prevent the ExpirationTimestamp from being
later than the notAfter timestamp on the issued certificate.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-06-23 11:07:00 -04:00
Matt Moyer
73201ba575 Merge pull request #682 from vmware-tanzu/dependabot/docker/debian-10.10-slim
Bump debian from 10.9-slim to 10.10-slim
2021-06-22 19:39:13 -07:00
dependabot[bot]
125d891cd5 Bump debian from 10.9-slim to 10.10-slim
Bumps debian from 10.9-slim to 10.10-slim.

---
updated-dependencies:
- dependency-name: debian
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-23 01:02:44 +00:00
Matt Moyer
682a47f739 Merge pull request #680 from mattmoyer/update-k8s-1.21.2
Update to Kubernetes 1.21.2 runtime components.
2021-06-22 10:23:14 -07:00
Matt Moyer
594e47efdf Update to Kubernetes 1.21.2 runtime components.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-06-22 11:55:22 -05:00
Mo Khan
f09a45382e Merge pull request #681 from enj/enj/i/fix_bad_name
Fix bad test package name
2021-06-22 12:54:29 -04:00
Monis Khan
d78b845575 Fix bad test package name
Signed-off-by: Monis Khan <mok@vmware.com>
2021-06-22 11:23:19 -04:00
Mo Khan
1929b47dda Merge pull request #674 from mattmoyer/new-eventual-assertion-helpers
Improve our integration test "Eventually" assertions.
2021-06-22 11:15:06 -04:00
Matt Moyer
3efa7bdcc2 Improve our integration test "Eventually" assertions.
This fixes some rare test flakes caused by a data race inherent in the way we use `assert.Eventually()` with extra variables for followup assertions. This function is tricky to use correctly because it runs the passed function in a separate goroutine, and you have no guarantee that any shared variables are in a coherent state when the `assert.Eventually()` call returns. Even if you add manual mutexes, it's tricky to get the semantics right. This has been a recurring pain point and the cause of several test flakes.

This change introduces a new `library.RequireEventually()` that works by internally constructing a per-loop `*require.Assertions` and running everything on a single goroutine (using `wait.PollImmediate()`). This makes it very easy to write eventual assertions.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-06-17 16:56:03 -05:00
Matt Moyer
6a9eb87c35 Update ROADMAP.md 2021-06-17 10:17:04 -05:00
Matt Moyer
3eba3e07c6 Merge pull request #669 from vmware-tanzu/dependabot/go_modules/github.com/golang/mock-1.6.0
Bump github.com/golang/mock from 1.5.0 to 1.6.0
2021-06-15 18:49:23 -07:00
dependabot[bot]
9f06869f76 Bump github.com/golang/mock from 1.5.0 to 1.6.0
Bumps [github.com/golang/mock](https://github.com/golang/mock) from 1.5.0 to 1.6.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.5.0...v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/golang/mock
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-16 01:22:04 +00:00
Matt Moyer
3f41261580 Merge pull request #673 from mattmoyer/refactor-static-client-struct-second-attempt
Use a custom type for our static CLI client.
2021-06-15 17:37:08 -07:00
Matt Moyer
551249fb69 Use a custom type for our static CLI client (smaller change).
Before this change, we used the `fosite.DefaultOpenIDConnectClient{}` struct, which implements the  `fosite.Client` and `fosite.OpenIDConnectClient` interfaces. For a future change, we also need to implement some additional optional interfaces, so we can no longer use the provided default types. Instead, we now use a custom `clientregistry.Client{}` struct, which implements all the requisite interfaces and can be extended to handle the new functionality (in a future change).

There is also a new `clientregistry.StaticRegistry{}` struct, which implements the `fosite.ClientManager` and looks up our single static client. We could potentially extend this in the future with a registry backed by Kubernetes API, for example.

This should be 100% refactor, with no user-observable change.

Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-06-15 15:31:48 -05:00
Mo Khan
1a610022cf Merge pull request #671 from enj/enj/i/eks_rbac
TestServiceAccountPermissions: handle extra permissions on EKS
2021-06-15 11:46:24 -04:00
Monis Khan
524ff21b7f TestServiceAccountPermissions: handle extra permissions on EKS
Signed-off-by: Monis Khan <mok@vmware.com>
2021-06-15 11:17:59 -04:00
Matt Moyer
913c140be8 Update the latest version number in the docs.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-06-15 09:46:51 -05:00
Mo Khan
e06c696bea Merge pull request #670 from enj/enj/f/impersonator_always_authz
impersonator: always authorize every request
2021-06-14 16:16:12 -04:00
Monis Khan
269db6b7c2 impersonator: always authorize every request
This change updates the impersonator to always authorize every
request instead of relying on the Kuberentes API server to perform
the check on the impersonated request.  This protects us from
scenarios where we fail to correctly impersonate the user due to
some bug in our proxy logic.  We still rely completely on the API
server to perform admission checks on the impersonated requests.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-06-14 12:53:09 -04:00
Monis Khan
addf632e7c impersonator: add docs regarding limited serivce account
Signed-off-by: Monis Khan <mok@vmware.com>
2021-06-11 15:37:55 -04:00
Mo Khan
87489da316 Merge pull request #667 from enj/enj/f/impersonator_distinct_sa
impersonator: run as a distinct SA with minimal permissions
2021-06-11 15:36:28 -04:00
Monis Khan
898f2bf942 impersonator: run as a distinct SA with minimal permissions
This change updates the impersonation proxy code to run as a
distinct service account that only has permission to impersonate
identities.  Thus any future vulnerability that causes the
impersonation headers to be dropped will fail closed instead of
escalating to the concierge's default service account which has
significantly more permissions.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-06-11 12:13:53 -04:00
Matt Moyer
918c50f6a7 Merge pull request #666 from vmware-tanzu/dependabot/go_modules/gopkg.in/square/go-jose.v2-2.6.0
Bump gopkg.in/square/go-jose.v2 from 2.5.1 to 2.6.0
2021-06-10 15:06:55 -07:00
Matt Moyer
9ca82116f1 Update ROADMAP.md 2021-06-10 12:45:23 -05:00
Matt Moyer
564c1f8ae5 Update ROADMAP.md 2021-06-10 10:27:20 -05:00
dependabot[bot]
c88aad873b Bump gopkg.in/square/go-jose.v2 from 2.5.1 to 2.6.0
Bumps [gopkg.in/square/go-jose.v2](https://github.com/square/go-jose) from 2.5.1 to 2.6.0.
- [Release notes](https://github.com/square/go-jose/releases)
- [Commits](https://github.com/square/go-jose/compare/v2.5.1...v2.6.0)

---
updated-dependencies:
- dependency-name: gopkg.in/square/go-jose.v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-08 05:41:45 +00:00
Mo Khan
9d27e6b4c6 Merge pull request #665 from enj/enj/i/impersonator_dead_code
impersonator: remove redundant deleteKnownImpersonationHeaders logic
2021-06-04 16:12:08 -04:00
Monis Khan
5b327a2b37 impersonator: remove redundant deleteKnownImpersonationHeaders logic
WithImpersonation already deletes impersonation headers and has done
so since the early days:

https://github.com/kubernetes/kubernetes/pull/36769

ensureNoImpersonationHeaders will still reject any request that has
impersonation headers set so we will always fail closed.

Signed-off-by: Monis Khan <mok@vmware.com>
2021-06-04 15:22:01 -04:00
Matt Moyer
7114988eec Merge pull request #663 from vmware-tanzu/dependabot/docker/golang-1.16.5
Bump golang from 1.16.4 to 1.16.5
2021-06-04 09:20:44 -05:00
Mo Khan
3a47060256 Merge pull request #645 from enj/enj/f/anon_impersonation_proxy
impersonator: honor anonymous authentication being disabled
2021-06-04 09:28:14 -04:00
Benjamin A. Petersen
492f6cfddf impersonator: honor anonymous authentication being disabled
When anonymous authentication is disabled, the impersonation proxy
will no longer authenticate anonymous requests other than calls to
the token credential request API (this API is used to retrieve
credentials and thus must be accessed anonymously).

Signed-off-by: Benjamin A. Petersen <ben@benjaminapetersen.me>
Signed-off-by: Monis Khan <mok@vmware.com>
2021-06-04 09:00:56 -04:00
dependabot[bot]
f417f706b9 Bump golang from 1.16.4 to 1.16.5
Bumps golang from 1.16.4 to 1.16.5.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-04 06:00:24 +00:00
Matt Moyer
02335e2ade Bump the latest version referenced in the docs.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
2021-06-03 17:25:32 -05:00
313 changed files with 23215 additions and 3658 deletions

View File

@@ -1,12 +1,23 @@
./.*
./*.md
./*.yaml
./apis
./deploy
./Dockerfile
./generated/1.1*
./internal/mocks
./LICENSE
./site/
./test
**/*_test.go
# This is effectively a copy of the .gitignore file.
# The whole git repo, including the .git directory, should get copied into the Docker build context,
# to enable the use of hack/get-ldflags.sh inside the Dockerfile.
# When you change the .gitignore file, please consider also changing this file.
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# GoLand
.idea
# MacOS Desktop Services Store
.DS_Store

6
.gitignore vendored
View File

@@ -1,3 +1,6 @@
# When you change this file, please consider also changing the .dockerignore file.
# See comments at the top of .dockerignore for more information.
# Binaries for programs and plugins
*.exe
*.exe~
@@ -11,9 +14,6 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# GoLand
.idea

View File

@@ -47,7 +47,6 @@ linters:
- scopelint
- sqlclosecheck
- unconvert
- unparam
- whitespace
issues:

View File

@@ -10,15 +10,28 @@ Please see the [Code of Conduct](./CODE_OF_CONDUCT.md).
See [SCOPE.md](./SCOPE.md) for some guidelines about what we consider in and out of scope for Pinniped.
## Roadmap
The near-term and mid-term roadmap for the work planned for the project [maintainers](MAINTAINERS.md) is documented in [ROADMAP.md](ROADMAP.md).
## 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 ET. 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](MAINTAINERS.md). 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.
## Discussion
Got a question, comment, or idea? Please don't hesitate to reach out via the GitHub [Discussions](https://github.com/vmware-tanzu/pinniped/discussions) tab at the top of this page or reach out in Kubernetes Slack Workspace within the [#pinniped channel](https://kubernetes.slack.com/archives/C01BW364RJA).
Got a question, comment, or idea? Please don't hesitate to reach out
via GitHub [Discussions](https://github.com/vmware-tanzu/pinniped/discussions),
GitHub [Issues](https://github.com/vmware-tanzu/pinniped/issues),
or in the Kubernetes Slack Workspace within the [#pinniped channel](https://kubernetes.slack.com/archives/C01BW364RJA).
## Issues
@@ -45,7 +58,7 @@ guidelines in the issue and pull request templates.
To suggest a feature, please first open an
[issue](https://github.com/vmware-tanzu/pinniped/issues/new?template=feature-proposal.md)
and tag it with `proposal`, or create a new [Discussion](https://github.com/vmware-tanzu/pinniped/discussions).
The project team will work with you on your feature request.
The project [maintainers](MAINTAINERS.md) will work with you on your feature request.
Once the feature request has been validated, a [pull request](https://github.com/vmware-tanzu/pinniped/compare)
can be opened to implement the feature.
@@ -53,6 +66,10 @@ can be opened to implement the feature.
For specifics on what to include in your feature request, please follow the
guidelines in the issue and pull request templates.
### Reporting security vulnerabilities
Please follow the procedure described in [SECURITY.md](SECURITY.md).
## CLA
We welcome contributions from everyone but we can only accept them if you sign

View File

@@ -3,7 +3,7 @@
# Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
FROM golang:1.16.4 as build-env
FROM golang:1.17.0 as build-env
WORKDIR /work
COPY . .
@@ -16,26 +16,18 @@ RUN \
--mount=type=cache,target=/cache/gocache \
--mount=type=cache,target=/cache/gomodcache \
mkdir out && \
GOCACHE=/cache/gocache \
GOMODCACHE=/cache/gomodcache \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64 \
go build -v -ldflags "$(hack/get-ldflags.sh)" -o out \
./cmd/pinniped-concierge/... \
./cmd/pinniped-supervisor/... \
./cmd/local-user-authenticator/...
export GOCACHE=/cache/gocache GOMODCACHE=/cache/gomodcache CGO_ENABLED=0 GOOS=linux GOARCH=amd64 && \
go build -v -ldflags "$(hack/get-ldflags.sh) -w -s" -o /usr/local/bin/pinniped-concierge-kube-cert-agent ./cmd/pinniped-concierge-kube-cert-agent/main.go && \
go build -v -ldflags "$(hack/get-ldflags.sh) -w -s" -o /usr/local/bin/pinniped-server ./cmd/pinniped-server/main.go && \
ln -s /usr/local/bin/pinniped-server /usr/local/bin/pinniped-concierge && \
ln -s /usr/local/bin/pinniped-server /usr/local/bin/pinniped-supervisor && \
ln -s /usr/local/bin/pinniped-server /usr/local/bin/local-user-authenticator
# Use a Debian slim image to grab a reasonable default CA bundle.
FROM debian:10.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 distroless runtime image with CA certificates, timezone data, and not much else.
FROM gcr.io/distroless/static:nonroot@sha256:c9f9b040044cc23e1088772814532d90adadfa1b86dcba17d07cb567db18dc4e
# Use a runtime image based on Debian slim.
FROM debian:10.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.
COPY --from=build-env /work/out/ /usr/local/bin/
# Copy the server binary from the build-env stage.
COPY --from=build-env /usr/local/bin /usr/local/bin
# Document the ports
EXPOSE 8080 8443
@@ -44,4 +36,4 @@ EXPOSE 8080 8443
USER 1001:1001
# Set the entrypoint
ENTRYPOINT ["/usr/local/bin/pinniped-concierge"]
ENTRYPOINT ["/usr/local/bin/pinniped-server"]

View File

@@ -4,16 +4,21 @@ This is the current list of maintainers for the Pinniped project.
| Maintainer | GitHub ID | Affiliation |
| --------------- | --------- | ----------- |
| Andrew Keesler | [ankeesler](https://github.com/ankeesler) | [VMware](https://www.github.com/vmware/) |
| Margo Crawford | [margocrawf](https://github.com/margocrawf) | [VMware](https://www.github.com/vmware/) |
| Matt Moyer | [mattmoyer](https://github.com/mattmoyer) | [VMware](https://www.github.com/vmware/) |
| Mo Khan | [enj](https://github.com/enj) | [VMware](https://www.github.com/vmware/) |
| Pablo Schuhmacher | [pabloschuhmacher](https://github.com/pabloschuhmacher) | [VMware](https://www.github.com/vmware/) |
| Anjali Telang | [anjaltelang](https://github.com/anjaltelang) | [VMware](https://www.github.com/vmware/) |
| Ryan Richard | [cfryanr](https://github.com/cfryanr) | [VMware](https://www.github.com/vmware/) |
## Emeritus Maintainers
* Andrew Keesler, [ankeesler](https://github.com/ankeesler)
* Pablo Schuhmacher, [pabloschuhmacher](https://github.com/pabloschuhmacher)
## Pinniped Contributors & Stakeholders
| Feature Area | Lead |
| ----------------------------- | :---------------------: |
| Technical Lead | Matt Moyer (mattmoyer) |
| Product Management | Pablo Schuhmacher (pabloschuhmacher) |
| Product Management | Anjali Telang (anjaltelang) |
| Community Management | Nanci Lancaster (microwavables) |

View File

@@ -1,63 +1,51 @@
<img src="site/content/docs/img/pinniped_logo.svg" alt="Pinniped Logo" width="100%"/>
<a href="https://pinniped.dev" target="_blank">
<img src="site/content/docs/img/pinniped_logo.svg" alt="Pinniped Logo" width="100%"/>
</a>
## Overview
Pinniped provides identity services to Kubernetes.
Pinniped allows cluster administrators to easily plug in external identity
providers (IDPs) into Kubernetes clusters. This is achieved via a uniform
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.
- Easily plug in external identity providers into Kubernetes clusters while offering a simple install and configuration experience. Leverage first class integration with Kubernetes and kubectl command-line.
- Give users a consistent, unified login experience across all your clusters, including on-premises and managed cloud environments.
- Securely integrate with an enterprise IDP using standard protocols or use secure, externally managed identities instead of relying on simple, shared credentials.
### Example use cases
* Your team uses a large enterprise IDP, and has many clusters that they
manage. Pinniped provides:
* Seamless and robust integration with the IDP
* Easy installation across clusters of any type and origin
* A simplified login flow across all clusters
* Your team shares a single cluster. Pinniped provides:
* Simple configuration to integrate an IDP
* Individual, revocable identities
### Architecture
The Pinniped Supervisor component offers identity federation to enable a user to
access multiple clusters with a single daily login to their external IDP. The
Pinniped Supervisor supports various external [IDP
types](https://github.com/vmware-tanzu/pinniped/tree/main/generated/1.20#k8s-api-idp-supervisor-pinniped-dev-v1alpha1).
The Pinniped Concierge component offers credential exchange to enable a user to
exchange an external credential for a short-lived, cluster-specific
credential. Pinniped supports various [authentication
methods](https://github.com/vmware-tanzu/pinniped/tree/main/generated/1.20#authenticationconciergepinnipeddevv1alpha1)
and implements different integration strategies for various Kubernetes
distributions to make authentication possible.
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/background/architecture/).
To learn more, please visit the Pinniped project's website, https://pinniped.dev.
## Getting started with Pinniped
Care to kick the tires? It's easy to [install and try Pinniped](https://pinniped.dev/docs/).
## 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, 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.
## Discussion
Got a question, comment, or idea? Please don't hesitate to reach out via the GitHub [Discussions](https://github.com/vmware-tanzu/pinniped/discussions) tab at the top of this page or reach out in Kubernetes Slack Workspace within the [#pinniped channel](https://kubernetes.slack.com/archives/C01BW364RJA).
Got a question, comment, or idea? Please don't hesitate to reach out
via GitHub [Discussions](https://github.com/vmware-tanzu/pinniped/discussions),
GitHub [Issues](https://github.com/vmware-tanzu/pinniped/issues),
or in the Kubernetes Slack Workspace within the [#pinniped channel](https://kubernetes.slack.com/archives/C01BW364RJA).
## Contributions
Contributions are welcome. Before contributing, please see the [contributing guide](CONTRIBUTING.md).
Want to get involved? Contributions are welcome.
Please see the [contributing guide](CONTRIBUTING.md) for more information about reporting bugs, requesting features,
building and testing the code, submitting PRs, and other contributor topics.
## Community meetings
Pinniped is better because of our contributors and [maintainers](MAINTAINERS.md). 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.
## Adopters
Some organizations and products using Pinniped are featured in [ADOPTERS.md](ADOPTERS.md).
Add your own organization or product [here](https://github.com/vmware-tanzu/pinniped/discussions/152).
## Reporting security vulnerabilities
@@ -67,4 +55,4 @@ Please follow the procedure described in [SECURITY.md](SECURITY.md).
Pinniped is open source and licensed under Apache License Version 2.0. See [LICENSE](LICENSE).
Copyright 2020 the Pinniped contributors. All Rights Reserved.
Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.

View File

@@ -33,19 +33,22 @@ The following table includes the current roadmap for Pinniped. If you have any q
Last Updated: April 2021
Last Updated: July 2021
Theme|Description|Timeline|
|--|--|--|
|LDAP Support|Extends upstream IDP protocols|May 2021|
|Improved Documentation|Reorganizing and improving Pinniped docs; new how-to guides and tutorials|May 2021|
|Device Code Flow|Add support for OAuth 2.0 Device Authorization Grant in the Pinniped CLI and Supervisor|Jun 2021|
|AD Support|Extends upstream IDP protocols|Jun 2021|
|Wider Concierge cluster support|Support for more cluster types in the Concierge|Jul 2021|
|Non-Interactive Password based OIDC logins |Support for non-interactive OIDC Logins via CLI using Password Grant |Aug 2021|
|Active Directory Support|Extends upstream IDP protocols|Aug 2021|
|Multiple IDP support|Support multiple IDPs configured on a single Supervisor|Sept 2021|
|Wider Concierge cluster support|Support for more cluster types in the Concierge|Sept 2021|
|Identity transforms|Support prefixing, filtering, or performing coarse-grained checks on upstream users and groups|Exploring/Ongoing|
|Extended IDP support|Support more types of identity providers on the Supervisor|Exploring/Ongoing|
|Improved Documentation|Reorganizing and improving Pinniped docs; new how-to guides and tutorials|Exploring/Ongoing|
|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|
|CLI Improvements|Improving CLI UX for setting up Supervisor IDPs|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|
|Device Code Flow|Add support for OAuth 2.0 Device Authorization Grant in the Pinniped CLI and Supervisor|Exploring/Ongoing|
|Supervisor with New Clients|Enable registering new clients with Supervisor|Exploring/Ongoing|

View File

@@ -96,7 +96,7 @@ type ImpersonationProxySpec struct {
// ExternalEndpoint describes the HTTPS endpoint where the proxy will be exposed. If not set, the proxy will
// be served using the external name of the LoadBalancer service or the cluster service DNS name.
//
// This field must be non-empty when spec.impersonationProxy.service.mode is "None".
// This field must be non-empty when spec.impersonationProxy.service.type is "None".
//
// +optional
ExternalEndpoint string `json:"externalEndpoint,omitempty"`

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 v1alpha1
@@ -34,6 +34,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&OIDCIdentityProviderList{},
&LDAPIdentityProvider{},
&LDAPIdentityProviderList{},
&ActiveDirectoryIdentityProvider{},
&ActiveDirectoryIdentityProviderList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil

View File

@@ -0,0 +1,182 @@
// 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"
)
type ActiveDirectoryIdentityProviderPhase string
const (
// ActiveDirectoryPhasePending is the default phase for newly-created ActiveDirectoryIdentityProvider resources.
ActiveDirectoryPhasePending ActiveDirectoryIdentityProviderPhase = "Pending"
// ActiveDirectoryPhaseReady is the phase for an ActiveDirectoryIdentityProvider resource in a healthy state.
ActiveDirectoryPhaseReady ActiveDirectoryIdentityProviderPhase = "Ready"
// ActiveDirectoryPhaseError is the phase for an ActiveDirectoryIdentityProvider in an unhealthy state.
ActiveDirectoryPhaseError ActiveDirectoryIdentityProviderPhase = "Error"
)
// Status of an Active Directory identity provider.
type ActiveDirectoryIdentityProviderStatus struct {
// Phase summarizes the overall status of the ActiveDirectoryIdentityProvider.
// +kubebuilder:default=Pending
// +kubebuilder:validation:Enum=Pending;Ready;Error
Phase ActiveDirectoryIdentityProviderPhase `json:"phase,omitempty"`
// Represents the observations of an identity provider's current state.
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
}
type ActiveDirectoryIdentityProviderBind struct {
// SecretName contains the name of a namespace-local Secret object that provides the username and
// password for an Active Directory bind user. This account will be used to perform LDAP searches. The Secret should be
// of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value
// should be the full dn (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com".
// The password must be non-empty.
// +kubebuilder:validation:MinLength=1
SecretName string `json:"secretName"`
}
type ActiveDirectoryIdentityProviderUserSearchAttributes struct {
// Username specifies the name of the attribute in Active Directory entry whose value shall become the username
// of the user after a successful authentication.
// Optional, when empty this defaults to "userPrincipalName".
// +optional
Username string `json:"username,omitempty"`
// UID specifies the name of the attribute in the ActiveDirectory entry which whose value shall be used to uniquely
// identify the user within this ActiveDirectory provider after a successful authentication.
// Optional, when empty this defaults to "objectGUID".
// +optional
UID string `json:"uid,omitempty"`
}
type ActiveDirectoryIdentityProviderGroupSearchAttributes struct {
// GroupName specifies the name of the attribute in the Active Directory entries whose value shall become a group name
// in the user's list of groups after a successful authentication.
// The value of this field is case-sensitive and must match the case of the attribute name returned by the ActiveDirectory
// server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn".
// Optional. When not specified, this defaults to a custom field that looks like "sAMAccountName@domain",
// where domain is constructed from the domain components of the group DN.
// +optional
GroupName string `json:"groupName,omitempty"`
}
type ActiveDirectoryIdentityProviderUserSearch struct {
// Base is the dn (distinguished name) that should be used as the search base when searching for users.
// E.g. "ou=users,dc=example,dc=com".
// Optional, when not specified it will be based on the result of a query for the defaultNamingContext
// (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse).
// The default behavior searches your entire domain for users.
// It may make sense to specify a subtree as a search base if you wish to exclude some users
// or to make searches faster.
// +optional
Base string `json:"base,omitempty"`
// Filter is the search filter which should be applied when searching for users. The pattern "{}" must occur
// in the filter at least once and will be dynamically replaced by the username for which the search is being run.
// E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see
// https://ldap.com/ldap-filters.
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
// Optional. When not specified, the default will be
// '(&(objectClass=person)(!(objectClass=computer))(!(showInAdvancedViewOnly=TRUE))(|(sAMAccountName={}")(mail={})(userPrincipalName={})(sAMAccountType=805306368))'
// This means that the user is a person, is not a computer, the sAMAccountType is for a normal user account,
// and is not shown in advanced view only
// (which would likely mean its a system created service account with advanced permissions).
// Also, either the sAMAccountName, the userPrincipalName, or the mail attribute matches the input username.
// +optional
Filter string `json:"filter,omitempty"`
// Attributes specifies how the user's information should be read from the ActiveDirectory entry which was found as
// the result of the user search.
// +optional
Attributes ActiveDirectoryIdentityProviderUserSearchAttributes `json:"attributes,omitempty"`
}
type ActiveDirectoryIdentityProviderGroupSearch struct {
// Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g.
// "ou=groups,dc=example,dc=com".
// Optional, when not specified it will be based on the result of a query for the defaultNamingContext
// (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse).
// The default behavior searches your entire domain for groups.
// It may make sense to specify a subtree as a search base if you wish to exclude some groups
// for security reasons or to make searches faster.
// +optional
Base string `json:"base,omitempty"`
// Filter is the ActiveDirectory search filter which should be applied when searching for groups for a user.
// The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the
// dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or
// "&(objectClass=groupOfNames)(member={})". For more information about ActiveDirectory filters, see
// https://ldap.com/ldap-filters.
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
// Optional. When not specified, the default will act as if the filter were specified as
// "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={})".
// This searches nested groups by default.
// Note that nested group search can be slow for some Active Directory servers. To disable it,
// you can set the filter to
// "(&(objectClass=group)(member={})"
// +optional
Filter string `json:"filter,omitempty"`
// Attributes specifies how the group's information should be read from each ActiveDirectory entry which was found as
// the result of the group search.
// +optional
Attributes ActiveDirectoryIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"`
}
// Spec for configuring an ActiveDirectory identity provider.
type ActiveDirectoryIdentityProviderSpec struct {
// Host is the hostname of this Active Directory identity provider, i.e., where to connect. For example: ldap.example.com:636.
// +kubebuilder:validation:MinLength=1
Host string `json:"host"`
// TLS contains the connection settings for how to establish the connection to the Host.
TLS *TLSSpec `json:"tls,omitempty"`
// Bind contains the configuration for how to provide access credentials during an initial bind to the ActiveDirectory server
// to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt.
Bind ActiveDirectoryIdentityProviderBind `json:"bind,omitempty"`
// UserSearch contains the configuration for searching for a user by name in Active Directory.
UserSearch ActiveDirectoryIdentityProviderUserSearch `json:"userSearch,omitempty"`
// GroupSearch contains the configuration for searching for a user's group membership in ActiveDirectory.
GroupSearch ActiveDirectoryIdentityProviderGroupSearch `json:"groupSearch,omitempty"`
}
// ActiveDirectoryIdentityProvider describes the configuration of an upstream Microsoft Active Directory identity provider.
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:resource:categories=pinniped;pinniped-idp;pinniped-idps
// +kubebuilder:printcolumn:name="Host",type=string,JSONPath=`.spec.host`
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// +kubebuilder:subresource:status
type ActiveDirectoryIdentityProvider struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec for configuring the identity provider.
Spec ActiveDirectoryIdentityProviderSpec `json:"spec"`
// Status of the identity provider.
Status ActiveDirectoryIdentityProviderStatus `json:"status,omitempty"`
}
// List of ActiveDirectoryIdentityProvider objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ActiveDirectoryIdentityProviderList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ActiveDirectoryIdentityProvider `json:"items"`
}

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 v1alpha1

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 v1alpha1
@@ -39,9 +39,31 @@ type OIDCIdentityProviderStatus struct {
// request parameters.
type OIDCAuthorizationConfig struct {
// AdditionalScopes are the scopes in addition to "openid" that will be requested as part of the authorization
// request flow with an OIDC identity provider. By default only the "openid" scope will be requested.
// request flow with an OIDC identity provider.
// In the case of a Resource Owner Password Credentials Grant flow, AdditionalScopes are the scopes
// in addition to "openid" that will be requested as part of the token request (see also the allowPasswordGrant field).
// By default, only the "openid" scope will be requested.
// +optional
AdditionalScopes []string `json:"additionalScopes,omitempty"`
// AllowPasswordGrant, when true, will allow the use of OAuth 2.0's Resource Owner Password Credentials Grant
// (see https://datatracker.ietf.org/doc/html/rfc6749#section-4.3) to authenticate to the OIDC provider using a
// username and password without a web browser, in addition to the usual browser-based OIDC Authorization Code Flow.
// The Resource Owner Password Credentials Grant is not officially part of the OIDC specification, so it may not be
// supported by your OIDC provider. If your OIDC provider supports returning ID tokens from a Resource Owner Password
// Credentials Grant token request, then you can choose to set this field to true. This will allow end users to choose
// to present their username and password to the kubectl CLI (using the Pinniped plugin) to authenticate to the
// cluster, without using a web browser to log in as is customary in OIDC Authorization Code Flow. This may be
// convenient for users, especially for identities from your OIDC provider which are not intended to represent a human
// actor, such as service accounts performing actions in a CI/CD environment. Even if your OIDC provider supports it,
// you may wish to disable this behavior by setting this field to false when you prefer to only allow users of this
// OIDCIdentityProvider to log in via the browser-based OIDC Authorization Code Flow. Using the Resource Owner Password
// Credentials Grant means that the Pinniped CLI and Pinniped Supervisor will directly handle your end users' passwords
// (similar to LDAPIdentityProvider), and you will not be able to require multi-factor authentication or use the other
// web-based login features of your OIDC provider during Resource Owner Password Credentials Grant logins.
// AllowPasswordGrant defaults to false.
// +optional
AllowPasswordGrant bool `json:"allowPasswordGrant,omitempty"`
}
// OIDCClaims provides a mapping from upstream claims into identities.

View File

@@ -0,0 +1,66 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
// IDPType are the strings that can be returned by the Supervisor identity provider discovery endpoint
// as the "type" of each returned identity provider.
type IDPType string
// IDPFlow are the strings that can be returned by the Supervisor identity provider discovery endpoint
// in the array of allowed client "flows" for each returned identity provider.
type IDPFlow string
const (
IDPTypeOIDC IDPType = "oidc"
IDPTypeLDAP IDPType = "ldap"
IDPTypeActiveDirectory IDPType = "activedirectory"
IDPFlowCLIPassword IDPFlow = "cli_password"
IDPFlowBrowserAuthcode IDPFlow = "browser_authcode"
)
// Equals is a convenience function for comparing an IDPType to a string.
func (r IDPType) Equals(s string) bool {
return string(r) == s
}
// String is a convenience function to convert an IDPType to a string.
func (r IDPType) String() string {
return string(r)
}
// Equals is a convenience function for comparing an IDPFlow to a string.
func (r IDPFlow) Equals(s string) bool {
return string(r) == s
}
// String is a convenience function to convert an IDPFlow to a string.
func (r IDPFlow) String() string {
return string(r)
}
// OIDCDiscoveryResponse is part of the response from a FederationDomain's OpenID Provider Configuration
// Document returned by the .well-known/openid-configuration endpoint. It ignores all the standard OpenID Provider
// configuration metadata and only picks out the portion related to Supervisor identity provider discovery.
type OIDCDiscoveryResponse struct {
SupervisorDiscovery OIDCDiscoveryResponseIDPEndpoint `json:"discovery.supervisor.pinniped.dev/v1alpha1"`
}
// OIDCDiscoveryResponseIDPEndpoint contains the URL for the identity provider discovery endpoint.
type OIDCDiscoveryResponseIDPEndpoint struct {
PinnipedIDPsEndpoint string `json:"pinniped_identity_providers_endpoint"`
}
// IDPDiscoveryResponse is the response of a FederationDomain's identity provider discovery endpoint.
type IDPDiscoveryResponse struct {
PinnipedIDPs []PinnipedIDP `json:"pinniped_identity_providers"`
}
// PinnipedIDP describes a single identity provider as included in the response of a FederationDomain's
// identity provider discovery endpoint.
type PinnipedIDP struct {
Name string `json:"name"`
Type IDPType `json:"type"`
Flows []IDPFlow `json:"flows,omitempty"`
}

View File

@@ -0,0 +1,25 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package oidc
// Constants related to the Supervisor FederationDomain's authorization and token endpoints.
const (
// AuthorizeUsernameHeaderName is the name of the HTTP header which can be used to transmit a username
// to the authorize endpoint when using a password flow, for example an OIDCIdentityProvider with a password grant
// or an LDAPIdentityProvider.
AuthorizeUsernameHeaderName = "Pinniped-Username"
// AuthorizePasswordHeaderName is the name of the HTTP header which can be used to transmit a password
// to the authorize endpoint when using a password flow, for example an OIDCIdentityProvider with a password grant
// or an LDAPIdentityProvider.
AuthorizePasswordHeaderName = "Pinniped-Password" //nolint:gosec // this is not a credential
// AuthorizeUpstreamIDPNameParamName is the name of the HTTP request parameter which can be used to help select which
// identity provider should be used for authentication by sending the name of the desired identity provider.
AuthorizeUpstreamIDPNameParamName = "pinniped_idp_name"
// AuthorizeUpstreamIDPTypeParamName is the name of the HTTP request parameter which can be used to help select which
// identity provider should be used for authentication by sending the type of the desired identity provider.
AuthorizeUpstreamIDPTypeParamName = "pinniped_idp_type"
)

View File

@@ -0,0 +1,55 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package main is the combined entrypoint for the Pinniped "kube-cert-agent" component.
package main
import (
"encoding/base64"
"encoding/json"
"io"
"io/ioutil"
"log"
"math"
"os"
"time"
)
//nolint: gochecknoglobals // these are swapped during unit tests.
var (
getenv = os.Getenv
fail = log.Fatalf
sleep = time.Sleep
out = io.Writer(os.Stdout)
)
func main() {
if len(os.Args) < 2 {
fail("missing subcommand")
}
switch os.Args[1] {
case "sleep":
sleep(math.MaxInt64)
case "print":
certBytes, err := ioutil.ReadFile(getenv("CERT_PATH"))
if err != nil {
fail("could not read CERT_PATH: %v", err)
}
keyBytes, err := ioutil.ReadFile(getenv("KEY_PATH"))
if err != nil {
fail("could not read KEY_PATH: %v", err)
}
if err := json.NewEncoder(out).Encode(&struct {
Cert string `json:"tls.crt"`
Key string `json:"tls.key"`
}{
Cert: base64.StdEncoding.EncodeToString(certBytes),
Key: base64.StdEncoding.EncodeToString(keyBytes),
}); err != nil {
fail("failed to write output: %v", err)
}
default:
fail("invalid subcommand %q", os.Args[1])
}
}

View File

@@ -0,0 +1,128 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package main
import (
"bytes"
"fmt"
"log"
"os"
"testing"
"time"
"github.com/stretchr/testify/require"
)
type errWriter struct{}
func (e errWriter) Write([]byte) (int, error) { return 0, fmt.Errorf("some write error") }
func TestEntrypoint(t *testing.T) {
for _, tt := range []struct {
name string
args []string
env map[string]string
failOutput bool
wantSleep time.Duration
wantLog string
wantOutJSON string
wantFail bool
}{
{
name: "missing args",
args: []string{},
wantLog: "missing subcommand\n",
wantFail: true,
},
{
name: "invalid subcommand",
args: []string{"/path/to/binary", "invalid"},
wantLog: "invalid subcommand \"invalid\"\n",
wantFail: true,
},
{
name: "valid sleep",
args: []string{"/path/to/binary", "sleep"},
wantSleep: 2562047*time.Hour + 47*time.Minute + 16*time.Second + 854775807*time.Nanosecond, // math.MaxInt64 nanoseconds, approximately 290 years
},
{
name: "missing cert file",
args: []string{"/path/to/binary", "print"},
env: map[string]string{
"CERT_PATH": "./does/not/exist",
"KEY_PATH": "./testdata/test.key",
},
wantFail: true,
wantLog: "could not read CERT_PATH: open ./does/not/exist: no such file or directory\n",
},
{
name: "missing key file",
args: []string{"/path/to/binary", "print"},
env: map[string]string{
"CERT_PATH": "./testdata/test.crt",
"KEY_PATH": "./does/not/exist",
},
wantFail: true,
wantLog: "could not read KEY_PATH: open ./does/not/exist: no such file or directory\n",
},
{
name: "fail to write output",
args: []string{"/path/to/binary", "print"},
env: map[string]string{
"CERT_PATH": "./testdata/test.crt",
"KEY_PATH": "./testdata/test.key",
},
failOutput: true,
wantFail: true,
wantLog: "failed to write output: some write error\n",
},
{
name: "successful print",
args: []string{"/path/to/binary", "print"},
env: map[string]string{
"CERT_PATH": "./testdata/test.crt",
"KEY_PATH": "./testdata/test.key",
},
wantOutJSON: `{
"tls.crt": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJd01EY3lOVEl4TURReE9Gb1hEVE13TURjeU16SXhNRFF4T0Zvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTDNLCmhZdjJnSVExRHd6aDJjV01pZCtvZkFudkxJZlYyWHY2MXZUTEdwclVJK1hVcUI0L2d0ZjZYNlVObjBMZXR0Mm4KZDhwNHd5N2h3NzNoVS9nZ2R2bVdKdnFCclNqYzNKR2Z5K2tqNjZmS1hYK1BUbGJMN1Fid2lSdmNTcUlYSVdsVgpsSEh4RUNXckVEOGpDdWx3L05WcWZvb2svaDVpTlVDVDl5c3dTSnIvMGZJbWlWbm9UbElvRVlHMmVDTmVqWjVjCmczOXVEM1pUcWQ5WnhXd1NMTG5JKzJrcEpuWkJQY2QxWlE4QVFxekRnWnRZUkNxYWNuNWdja1FVS1pXS1FseG8KRWZ0NmcxWEhKb3VBV0FadzdoRXRrMHY4ckcwL2VLRjd3YW14Rmk2QkZWbGJqV0JzQjRUOXJBcGJkQldUS2VDSgpIdjhmdjVSTUZTenBUM3V6VE84Q0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFDaDVSaGJ4cUplK1ovZ2MxN2NaaEtObWRpd3UKSTJwTHAzUUJmd3ZOK1dibWFqencvN3JZaFkwZDhKWVZUSnpYU0NQV2k2VUFLeEF0WE9MRjhXSUlmOWkzOW42Ugp1S09CR1cxNEZ6ekd5UkppRDNxYUcvSlR2RVcrU0xod2w2OE5kcjVMSFNuYnVnQXFxMzFhYmNReTZabDl2NUE4CkpLQzk3TGovU244cmo3b3BLeTRXM29xN05DUXNBYjB6aDRJbGxSRjZVdlNuSnlTZnNnN3hkWEhIcHhZREh0T1MKWGNPdTV5U1VJWlRnRmU5UmZlVVpsR1o1eG4wY2tNbFE3cVcyV3gxcTBPVld3NXVzNE50a0dxS3JIRzRUbjFYNwp1d28vWXl0bjVzRHhyRHYxL29paTZBWk9Dc1RQcmU0b0Qzd3o0bm1WekNWSmNncnFINFEyNGhUOFdOZz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=",
"tls.key": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBdmNxRmkvYUFoRFVQRE9IWnhZeUozNmg4Q2U4c2g5WFplL3JXOU1zYW10UWo1ZFNvCkhqK0MxL3BmcFEyZlF0NjIzYWQzeW5qREx1SER2ZUZUK0NCMitaWW0rb0d0S056Y2taL0w2U1BycDhwZGY0OU8KVnN2dEJ2Q0pHOXhLb2hjaGFWV1VjZkVRSmFzUVB5TUs2WEQ4MVdwK2lpVCtIbUkxUUpQM0t6Qkltdi9SOGlhSgpXZWhPVWlnUmdiWjRJMTZObmx5RGYyNFBkbE9wMzFuRmJCSXN1Y2o3YVNrbWRrRTl4M1ZsRHdCQ3JNT0JtMWhFCktwcHlmbUJ5UkJRcGxZcENYR2dSKzNxRFZjY21pNEJZQm5EdUVTMlRTL3lzYlQ5NG9YdkJxYkVXTG9FVldWdU4KWUd3SGhQMnNDbHQwRlpNcDRJa2UveCsvbEV3VkxPbFBlN05NN3dJREFRQUJBb0lCQUZDMXRVRW1ITlVjTTBCSgpNM0Q5S1F6Qis2M0YxbXdWbHgxUU9PVjFFZVZSM2NvNU94MVI2UFNyOXN5Y0ZHUTlqZ3FJMHpwNVRKZTlUcDZMCkdraGtsZlBoMU1Xbks5bzZ3bG56V0tYV3JycDJKbmkrbXBQeXVPUEFtcTRNYW5pdjJYZVArMGJST3dxcHlvanYKQUE3eUM3TStUSDIyNlpKR05WczNFVjkrY3dIbWwweXV6QmZJSm4vcnYvdzJnK1dSS00vTUMwUzdrMmQ4YlJsQQpOeWNLVkdBR0JoS1RsdGpvVllPZWg2YUhFcFNqSzh6ZmFlUGpvNWRZSnZvVklsaTYwWUNnY0pPVS84alhUK05wCjFGbTd0UnZBdGozcFVwMFNxZGFmMlJVemg5amZKcDJWRkNIdVNKNlRQcUFyT3lRb2p0TWNUSEYwVGlXN3hySFAKeE9DUklBRUNnWUVBd0dCUFU3dmR0aE1KQmcrT1JVb0dRUWFJdFRlSnZRd0lxSnZiS0Qyb3NwNGpoUzFkR1pCdwpXMzBHS0VjL2dkOEpOdE9xOUJCbk1pY1BGN2hrdHV5K2JTUHY0MVhQdWQ2N3JTU083VHN3MjBDMTBnRlJxMDZCCnpJSldGQVVxSzNJa3ZWYzNWRG10U0xTRG94NFFaL0JkcWFNbFE1eTVKQ3NDNWtUaG1rWkZsTzhDZ1lFQS9JOVgKWUhpNlJpb01KRTFmcU9ISkw0RERqbGV6bWN1UnJEN2ZFNUluS2J0SloySmhHWU9YL0MwS1huSFRPV1RDRHh4TgpGQnZwdkQ2WHY1bzNQaEI5WjZrMmZxdko0R1M4dXJrRy9LVTR4Y0MrYmFrKzlhdmE4b2FpU3FHMTZ6RDlOSDJQCmpKNjBOcmJMbDFKMHBVOWZpd3VGVlVLSjRoRFpPZk45UnFZZHlBRUNnWUFWd284V2hKaUdnTTZ6ZmN6MDczT1gKcFZxUFRQSHFqVkxwWjMrNXBJZlJkR3ZHSTZSMVFNNUV1dmFZVmI3TVBPTTQ3V1pYNXdjVk9DL1AyZzZpVmxNUAoyMUhHSUMyMzg0YTlCZmFZeE9vNDBxLytTaUhudzZDUTlta3dLSWxsa3Fxdk5BOVJHcGtNTVViMmkyOEZvcjJsCmM0dkNneGE2RFpkdFhuczZUUnFQeHdLQmdDZlk1Y3hPdi9UNkJWaGs3TWJVZU0ySjMxREIvWkF5VWhWL0Jlc3MKa0FsQmgxOU1ZazJJT1o2TDdLcmlBcFYzbERhV0hJTWp0RWtEQnlZdnlxOThJbzBNWVpDeXdmTXBjYTEwSytvSQpsMkI3L0krSXVHcENaeFVFc081ZGZUcFNUR0RQdnFwTkQ5bmlGVlVXcVZpN29UTnE2ZXA5eVF0bDVTQURqcXhxCjRTQUJBb0dBSW0waFVnMXd0Y1M0NmNHTHk2UElrUE01dG9jVFNnaHR6NHZGc3VrL2k0UUE5R0JvQk8yZ0g2dHkKK2tKSG1lYVh0MmRtZ3lTcDBRQVdpdDVVbGNlRXVtQjBOWG5BZEpaUXhlR1NGU3lZa0RXaHdYZDh3RGNlS28vMQpMZkNVNkRrOElOL1NzcHBWVVdYUTJybE9SdnhsckhlQ2lvOG8wa1M5WWl1NTVXTVlnNGc9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg=="
}`,
},
} {
tt := tt
t.Run(tt.name, func(t *testing.T) {
var logBuf bytes.Buffer
testLog := log.New(&logBuf, "", 0)
exited := "exiting via fatal"
fail = func(format string, v ...interface{}) {
testLog.Printf(format, v...)
panic(exited)
}
var sawSleep time.Duration
sleep = func(d time.Duration) { sawSleep = d }
var sawOutput bytes.Buffer
out = &sawOutput
if tt.failOutput {
out = &errWriter{}
}
os.Args = tt.args
getenv = func(key string) string { return tt.env[key] }
if tt.wantFail {
require.PanicsWithValue(t, exited, main)
} else {
require.NotPanics(t, main)
}
require.Equal(t, tt.wantSleep.String(), sawSleep.String())
require.Equal(t, tt.wantLog, logBuf.String())
if tt.wantOutJSON == "" {
require.Empty(t, sawOutput.String())
} else {
require.JSONEq(t, tt.wantOutJSON, sawOutput.String())
}
})
}
}

View File

@@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICyDCCAbCgAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl
cm5ldGVzMB4XDTIwMDcyNTIxMDQxOFoXDTMwMDcyMzIxMDQxOFowFTETMBEGA1UE
AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3K
hYv2gIQ1Dwzh2cWMid+ofAnvLIfV2Xv61vTLGprUI+XUqB4/gtf6X6UNn0Lett2n
d8p4wy7hw73hU/ggdvmWJvqBrSjc3JGfy+kj66fKXX+PTlbL7QbwiRvcSqIXIWlV
lHHxECWrED8jCulw/NVqfook/h5iNUCT9yswSJr/0fImiVnoTlIoEYG2eCNejZ5c
g39uD3ZTqd9ZxWwSLLnI+2kpJnZBPcd1ZQ8AQqzDgZtYRCqacn5gckQUKZWKQlxo
Eft6g1XHJouAWAZw7hEtk0v8rG0/eKF7wamxFi6BFVlbjWBsB4T9rApbdBWTKeCJ
Hv8fv5RMFSzpT3uzTO8CAwEAAaMjMCEwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB
/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACh5RhbxqJe+Z/gc17cZhKNmdiwu
I2pLp3QBfwvN+Wbmajzw/7rYhY0d8JYVTJzXSCPWi6UAKxAtXOLF8WIIf9i39n6R
uKOBGW14FzzGyRJiD3qaG/JTvEW+SLhwl68Ndr5LHSnbugAqq31abcQy6Zl9v5A8
JKC97Lj/Sn8rj7opKy4W3oq7NCQsAb0zh4IllRF6UvSnJySfsg7xdXHHpxYDHtOS
XcOu5ySUIZTgFe9RfeUZlGZ5xn0ckMlQ7qW2Wx1q0OVWw5us4NtkGqKrHG4Tn1X7
uwo/Yytn5sDxrDv1/oii6AZOCsTPre4oD3wz4nmVzCVJcgrqH4Q24hT8WNg=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAvcqFi/aAhDUPDOHZxYyJ36h8Ce8sh9XZe/rW9MsamtQj5dSo
Hj+C1/pfpQ2fQt623ad3ynjDLuHDveFT+CB2+ZYm+oGtKNzckZ/L6SPrp8pdf49O
VsvtBvCJG9xKohchaVWUcfEQJasQPyMK6XD81Wp+iiT+HmI1QJP3KzBImv/R8iaJ
WehOUigRgbZ4I16NnlyDf24PdlOp31nFbBIsucj7aSkmdkE9x3VlDwBCrMOBm1hE
KppyfmByRBQplYpCXGgR+3qDVccmi4BYBnDuES2TS/ysbT94oXvBqbEWLoEVWVuN
YGwHhP2sClt0FZMp4Ike/x+/lEwVLOlPe7NM7wIDAQABAoIBAFC1tUEmHNUcM0BJ
M3D9KQzB+63F1mwVlx1QOOV1EeVR3co5Ox1R6PSr9sycFGQ9jgqI0zp5TJe9Tp6L
GkhklfPh1MWnK9o6wlnzWKXWrrp2Jni+mpPyuOPAmq4Maniv2XeP+0bROwqpyojv
AA7yC7M+TH226ZJGNVs3EV9+cwHml0yuzBfIJn/rv/w2g+WRKM/MC0S7k2d8bRlA
NycKVGAGBhKTltjoVYOeh6aHEpSjK8zfaePjo5dYJvoVIli60YCgcJOU/8jXT+Np
1Fm7tRvAtj3pUp0Sqdaf2RUzh9jfJp2VFCHuSJ6TPqArOyQojtMcTHF0TiW7xrHP
xOCRIAECgYEAwGBPU7vdthMJBg+ORUoGQQaItTeJvQwIqJvbKD2osp4jhS1dGZBw
W30GKEc/gd8JNtOq9BBnMicPF7hktuy+bSPv41XPud67rSSO7Tsw20C10gFRq06B
zIJWFAUqK3IkvVc3VDmtSLSDox4QZ/BdqaMlQ5y5JCsC5kThmkZFlO8CgYEA/I9X
YHi6RioMJE1fqOHJL4DDjlezmcuRrD7fE5InKbtJZ2JhGYOX/C0KXnHTOWTCDxxN
FBvpvD6Xv5o3PhB9Z6k2fqvJ4GS8urkG/KU4xcC+bak+9ava8oaiSqG16zD9NH2P
jJ60NrbLl1J0pU9fiwuFVUKJ4hDZOfN9RqYdyAECgYAVwo8WhJiGgM6zfcz073OX
pVqPTPHqjVLpZ3+5pIfRdGvGI6R1QM5EuvaYVb7MPOM47WZX5wcVOC/P2g6iVlMP
21HGIC2384a9BfaYxOo40q/+SiHnw6CQ9mkwKIllkqqvNA9RGpkMMUb2i28For2l
c4vCgxa6DZdtXns6TRqPxwKBgCfY5cxOv/T6BVhk7MbUeM2J31DB/ZAyUhV/Bess
kAlBh19MYk2IOZ6L7KriApV3lDaWHIMjtEkDByYvyq98Io0MYZCywfMpca10K+oI
l2B7/I+IuGpCZxUEsO5dfTpSTGDPvqpND9niFVUWqVi7oTNq6ep9yQtl5SADjqxq
4SABAoGAIm0hUg1wtcS46cGLy6PIkPM5tocTSghtz4vFsuk/i4QA9GBoBO2gH6ty
+kJHmeaXt2dmgySp0QAWit5UlceEumB0NXnAdJZQxeGSFSyYkDWhwXd8wDceKo/1
LfCU6Dk8IN/SsppVUWXQ2rlORvxlrHeCio8o0kS9Yiu55WMYg4g=
-----END RSA PRIVATE KEY-----

View File

@@ -1,35 +0,0 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package main
import (
"os"
"time"
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/client-go/pkg/version"
"k8s.io/client-go/rest"
"k8s.io/component-base/logs"
"k8s.io/klog/v2"
"go.pinniped.dev/internal/concierge/server"
)
func main() {
logs.InitLogs()
defer logs.FlushLogs()
// Dump out the time since compile (mostly useful for benchmarking our local development cycle latency).
var timeSinceCompile time.Duration
if buildDate, err := time.Parse(time.RFC3339, version.Get().BuildDate); err == nil {
timeSinceCompile = time.Since(buildDate).Round(time.Second)
}
klog.Infof("Running %s at %#v (%s since build)", rest.DefaultKubernetesUserAgent(), version.Get(), timeSinceCompile)
ctx := genericapiserver.SetupSignalContext()
if err := server.New(ctx, os.Args[1:], os.Stdout, os.Stderr).Run(); err != nil {
klog.Fatal(err)
}
}

View File

@@ -0,0 +1,41 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package main is the combined entrypoint for all Pinniped server components.
//
// It dispatches to the appropriate Main() entrypoint based the name it is invoked as (os.Args[0]). In our server
// container image, this binary is symlinked to several names such as `/usr/local/bin/pinniped-concierge`.
package main
import (
"os"
"path/filepath"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/klog/v2"
concierge "go.pinniped.dev/internal/concierge/server"
lua "go.pinniped.dev/internal/localuserauthenticator"
supervisor "go.pinniped.dev/internal/supervisor/server"
)
//nolint: gochecknoglobals // these are swapped during unit tests.
var (
fail = klog.Fatalf
subcommands = map[string]func(){
"pinniped-concierge": concierge.Main,
"pinniped-supervisor": supervisor.Main,
"local-user-authenticator": lua.Main,
}
)
func main() {
if len(os.Args) == 0 {
fail("missing os.Args")
}
binary := filepath.Base(os.Args[0])
if subcommands[binary] == nil {
fail("must be invoked as one of %v, not %q", sets.StringKeySet(subcommands).List(), binary)
}
subcommands[binary]()
}

View File

@@ -0,0 +1,72 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package main
import (
"bytes"
"log"
"os"
"testing"
"github.com/stretchr/testify/require"
)
func TestEntrypoint(t *testing.T) {
for _, tt := range []struct {
name string
args []string
wantOutput string
wantFail bool
wantArgs []string
}{
{
name: "missing args",
args: []string{},
wantOutput: "missing os.Args\n",
wantFail: true,
},
{
name: "invalid subcommand",
args: []string{"/path/to/invalid", "some", "args"},
wantOutput: "must be invoked as one of [another-test-binary valid-test-binary], not \"invalid\"\n",
wantFail: true,
},
{
name: "valid",
args: []string{"/path/to/valid-test-binary", "foo", "bar"},
wantArgs: []string{"/path/to/valid-test-binary", "foo", "bar"},
},
} {
tt := tt
t.Run(tt.name, func(t *testing.T) {
var logBuf bytes.Buffer
testLog := log.New(&logBuf, "", 0)
exited := "exiting via fatal"
fail = func(format string, v ...interface{}) {
testLog.Printf(format, v...)
panic(exited)
}
// Make a test command that records os.Args when it's invoked.
var gotArgs []string
subcommands = map[string]func(){
"valid-test-binary": func() { gotArgs = os.Args },
"another-test-binary": func() {},
}
os.Args = tt.args
if tt.wantFail {
require.PanicsWithValue(t, exited, main)
} else {
require.NotPanics(t, main)
}
if tt.wantArgs != nil {
require.Equal(t, tt.wantArgs, gotArgs)
}
if tt.wantOutput != "" {
require.Equal(t, tt.wantOutput, logBuf.String())
}
})
}
}

View File

@@ -32,6 +32,7 @@ import (
conciergev1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
idpdiscoveryv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idpdiscovery/v1alpha1"
conciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
"go.pinniped.dev/internal/groupsuffix"
)
@@ -61,12 +62,14 @@ type getKubeconfigOIDCParams struct {
listenPort uint16
scopes []string
skipBrowser bool
skipListen bool
sessionCachePath string
debugSessionCache bool
caBundle caBundleFlag
requestAudience string
upstreamIDPName string
upstreamIDPType string
upstreamIDPFlow string
}
type getKubeconfigConciergeParams struct {
@@ -96,23 +99,6 @@ type getKubeconfigParams struct {
credentialCachePathSet bool
}
type supervisorOIDCDiscoveryResponseWithV1Alpha1 struct {
SupervisorDiscovery SupervisorDiscoveryResponseV1Alpha1 `json:"discovery.supervisor.pinniped.dev/v1alpha1"`
}
type SupervisorDiscoveryResponseV1Alpha1 struct {
PinnipedIDPsEndpoint string `json:"pinniped_identity_providers_endpoint"`
}
type supervisorIDPsDiscoveryResponseV1Alpha1 struct {
PinnipedIDPs []pinnipedIDPResponse `json:"pinniped_identity_providers"`
}
type pinnipedIDPResponse struct {
Name string `json:"name"`
Type string `json:"type"`
}
func kubeconfigCommand(deps kubeconfigDeps) *cobra.Command {
var (
cmd = &cobra.Command{
@@ -146,12 +132,14 @@ func kubeconfigCommand(deps kubeconfigDeps) *cobra.Command {
f.Uint16Var(&flags.oidc.listenPort, "oidc-listen-port", 0, "TCP port for localhost listener (authorization code flow only)")
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.BoolVar(&flags.oidc.skipListen, "oidc-skip-listen", false, "During OpenID Connect login, skip starting a localhost callback listener (manual copy/paste flow only)")
f.StringVar(&flags.oidc.sessionCachePath, "oidc-session-cache", "", "Path to OpenID Connect session cache file")
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.oidc.upstreamIDPName, "upstream-identity-provider-name", "", "The name of the upstream identity provider used during login with a Supervisor")
f.StringVar(&flags.oidc.upstreamIDPType, "upstream-identity-provider-type", "", "The type of the upstream identity provider used during login with a Supervisor (e.g. 'oidc', 'ldap')")
f.StringVar(&flags.oidc.upstreamIDPType, "upstream-identity-provider-type", "", fmt.Sprintf("The type of the upstream identity provider used during login with a Supervisor (e.g. '%s', '%s', '%s')", idpdiscoveryv1alpha1.IDPTypeOIDC, idpdiscoveryv1alpha1.IDPTypeLDAP, idpdiscoveryv1alpha1.IDPTypeActiveDirectory))
f.StringVar(&flags.oidc.upstreamIDPFlow, "upstream-identity-provider-flow", "", fmt.Sprintf("The type of client flow to use with the upstream identity provider during login with a Supervisor (e.g. '%s', '%s')", idpdiscoveryv1alpha1.IDPFlowCLIPassword, idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode))
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)")
@@ -161,6 +149,9 @@ func kubeconfigCommand(deps kubeconfigDeps) *cobra.Command {
f.StringVar(&flags.credentialCachePath, "credential-cache", "", "Path to cluster-specific credentials cache")
mustMarkHidden(cmd, "oidc-debug-session-cache")
// --oidc-skip-listen is mainly needed for testing. We'll leave it hidden until we have a non-testing use case.
mustMarkHidden(cmd, "oidc-skip-listen")
mustMarkDeprecated(cmd, "concierge-namespace", "not needed anymore")
mustMarkHidden(cmd, "concierge-namespace")
@@ -238,8 +229,10 @@ func runGetKubeconfig(ctx context.Context, out io.Writer, deps kubeconfigDeps, f
cluster.CertificateAuthorityData = flags.concierge.caBundle
}
// If there is an issuer, and if both upstream flags are not already set, then try to discover Supervisor upstream IDP.
if len(flags.oidc.issuer) > 0 && (flags.oidc.upstreamIDPType == "" || flags.oidc.upstreamIDPName == "") {
// If there is an issuer, and if any upstream IDP flags are not already set, then try to discover Supervisor upstream IDP details.
// When all the upstream IDP flags are set by the user, then skip discovery and don't validate their input. Maybe they know something
// that we can't know, like the name of an IDP that they are going to define in the future.
if len(flags.oidc.issuer) > 0 && (flags.oidc.upstreamIDPType == "" || flags.oidc.upstreamIDPName == "" || flags.oidc.upstreamIDPFlow == "") {
if err := discoverSupervisorUpstreamIDP(ctx, &flags); err != nil {
return err
}
@@ -317,6 +310,9 @@ func newExecConfig(deps kubeconfigDeps, flags getKubeconfigParams) (*clientcmdap
if flags.oidc.skipBrowser {
execConfig.Args = append(execConfig.Args, "--skip-browser")
}
if flags.oidc.skipListen {
execConfig.Args = append(execConfig.Args, "--skip-listen")
}
if flags.oidc.listenPort != 0 {
execConfig.Args = append(execConfig.Args, "--listen-port="+strconv.Itoa(int(flags.oidc.listenPort)))
}
@@ -338,6 +334,9 @@ func newExecConfig(deps kubeconfigDeps, flags getKubeconfigParams) (*clientcmdap
if flags.oidc.upstreamIDPType != "" {
execConfig.Args = append(execConfig.Args, "--upstream-identity-provider-type="+flags.oidc.upstreamIDPType)
}
if flags.oidc.upstreamIDPFlow != "" {
execConfig.Args = append(execConfig.Args, "--upstream-identity-provider-flow="+flags.oidc.upstreamIDPFlow)
}
return execConfig, nil
}
@@ -750,21 +749,31 @@ func discoverSupervisorUpstreamIDP(ctx context.Context, flags *getKubeconfigPara
return nil
}
upstreamIDPs, err := discoverAllAvailableSupervisorUpstreamIDPs(ctx, pinnipedIDPsEndpoint, httpClient)
discoveredUpstreamIDPs, err := discoverAllAvailableSupervisorUpstreamIDPs(ctx, pinnipedIDPsEndpoint, httpClient)
if err != nil {
return err
}
if len(upstreamIDPs) == 1 {
flags.oidc.upstreamIDPName = upstreamIDPs[0].Name
flags.oidc.upstreamIDPType = upstreamIDPs[0].Type
} else if len(upstreamIDPs) > 1 {
idpName, idpType, err := selectUpstreamIDP(upstreamIDPs, flags.oidc.upstreamIDPName, flags.oidc.upstreamIDPType)
if err != nil {
return err
}
flags.oidc.upstreamIDPName = idpName
flags.oidc.upstreamIDPType = idpType
if len(discoveredUpstreamIDPs) == 0 {
// Discovered that the Supervisor does not have any upstream IDPs defined. Continue without putting one into the
// kubeconfig. This kubeconfig will only work if the user defines one (and only one) OIDC IDP in the Supervisor
// later and wants to use the default client flow for OIDC (browser-based auth).
return nil
}
selectedIDPName, selectedIDPType, discoveredIDPFlows, err := selectUpstreamIDPNameAndType(discoveredUpstreamIDPs, flags.oidc.upstreamIDPName, flags.oidc.upstreamIDPType)
if err != nil {
return err
}
selectedIDPFlow, err := selectUpstreamIDPFlow(discoveredIDPFlows, selectedIDPName, selectedIDPType, flags.oidc.upstreamIDPFlow)
if err != nil {
return err
}
flags.oidc.upstreamIDPName = selectedIDPName
flags.oidc.upstreamIDPType = selectedIDPType.String()
flags.oidc.upstreamIDPFlow = selectedIDPFlow.String()
return nil
}
@@ -792,7 +801,7 @@ func discoverIDPsDiscoveryEndpointURL(ctx context.Context, issuer string, httpCl
return "", fmt.Errorf("while fetching OIDC discovery data from issuer: %w", err)
}
var body supervisorOIDCDiscoveryResponseWithV1Alpha1
var body idpdiscoveryv1alpha1.OIDCDiscoveryResponse
err = discoveredProvider.Claims(&body)
if err != nil {
return "", fmt.Errorf("while fetching OIDC discovery data from issuer: %w", err)
@@ -801,7 +810,7 @@ func discoverIDPsDiscoveryEndpointURL(ctx context.Context, issuer string, httpCl
return body.SupervisorDiscovery.PinnipedIDPsEndpoint, nil
}
func discoverAllAvailableSupervisorUpstreamIDPs(ctx context.Context, pinnipedIDPsEndpoint string, httpClient *http.Client) ([]pinnipedIDPResponse, error) {
func discoverAllAvailableSupervisorUpstreamIDPs(ctx context.Context, pinnipedIDPsEndpoint string, httpClient *http.Client) ([]idpdiscoveryv1alpha1.PinnipedIDP, error) {
request, err := http.NewRequestWithContext(ctx, http.MethodGet, pinnipedIDPsEndpoint, nil)
if err != nil {
return nil, fmt.Errorf("while forming request to IDP discovery URL: %w", err)
@@ -823,7 +832,7 @@ func discoverAllAvailableSupervisorUpstreamIDPs(ctx context.Context, pinnipedIDP
return nil, fmt.Errorf("unable to fetch IDP discovery data from issuer: could not read response body: %w", err)
}
var body supervisorIDPsDiscoveryResponseV1Alpha1
var body idpdiscoveryv1alpha1.IDPDiscoveryResponse
err = json.Unmarshal(rawBody, &body)
if err != nil {
return nil, fmt.Errorf("unable to fetch IDP discovery data from issuer: could not parse response JSON: %w", err)
@@ -832,53 +841,106 @@ func discoverAllAvailableSupervisorUpstreamIDPs(ctx context.Context, pinnipedIDP
return body.PinnipedIDPs, nil
}
func selectUpstreamIDP(pinnipedIDPs []pinnipedIDPResponse, idpName, idpType string) (string, string, error) {
func selectUpstreamIDPNameAndType(pinnipedIDPs []idpdiscoveryv1alpha1.PinnipedIDP, specifiedIDPName, specifiedIDPType string) (string, idpdiscoveryv1alpha1.IDPType, []idpdiscoveryv1alpha1.IDPFlow, error) {
pinnipedIDPsString, _ := json.Marshal(pinnipedIDPs)
var discoveredFlows []idpdiscoveryv1alpha1.IDPFlow
switch {
case idpType != "":
discoveredName := ""
case specifiedIDPName != "" && specifiedIDPType != "":
// The user specified both name and type, so check to see if there exists an exact match.
for _, idp := range pinnipedIDPs {
if idp.Type == idpType {
if idp.Name == specifiedIDPName && idp.Type.Equals(specifiedIDPType) {
return specifiedIDPName, idp.Type, idp.Flows, nil
}
}
return "", "", nil, fmt.Errorf(
"no Supervisor upstream identity providers with name %q of type %q were found. "+
"Found these upstreams: %s", specifiedIDPName, specifiedIDPType, pinnipedIDPsString)
case specifiedIDPType != "":
// The user specified only a type, so check if there is only one of that type found.
discoveredName := ""
var discoveredType idpdiscoveryv1alpha1.IDPType
for _, idp := range pinnipedIDPs {
if idp.Type.Equals(specifiedIDPType) {
if discoveredName != "" {
return "", "", fmt.Errorf(
"multiple Supervisor upstream identity providers of type \"%s\" were found,"+
" so the --upstream-identity-provider-name flag must be specified. "+
return "", "", nil, fmt.Errorf(
"multiple Supervisor upstream identity providers of type %q were found, "+
"so the --upstream-identity-provider-name flag must be specified. "+
"Found these upstreams: %s",
idpType, pinnipedIDPsString)
specifiedIDPType, pinnipedIDPsString)
}
discoveredName = idp.Name
discoveredType = idp.Type
discoveredFlows = idp.Flows
}
}
if discoveredName == "" {
return "", "", fmt.Errorf(
"no Supervisor upstream identity providers of type \"%s\" were found."+
" Found these upstreams: %s", idpType, pinnipedIDPsString)
return "", "", nil, fmt.Errorf(
"no Supervisor upstream identity providers of type %q were found. "+
"Found these upstreams: %s", specifiedIDPType, pinnipedIDPsString)
}
return discoveredName, idpType, nil
case idpName != "":
discoveredType := ""
return discoveredName, discoveredType, discoveredFlows, nil
case specifiedIDPName != "":
// The user specified only a name, so check if there is only one of that name found.
var discoveredType idpdiscoveryv1alpha1.IDPType
for _, idp := range pinnipedIDPs {
if idp.Name == idpName {
if idp.Name == specifiedIDPName {
if discoveredType != "" {
return "", "", fmt.Errorf(
"multiple Supervisor upstream identity providers with name \"%s\" were found,"+
" so the --upstream-identity-provider-type flag must be specified. Found these upstreams: %s",
idpName, pinnipedIDPsString)
return "", "", nil, fmt.Errorf(
"multiple Supervisor upstream identity providers with name %q were found, "+
"so the --upstream-identity-provider-type flag must be specified. Found these upstreams: %s",
specifiedIDPName, pinnipedIDPsString)
}
discoveredType = idp.Type
discoveredFlows = idp.Flows
}
}
if discoveredType == "" {
return "", "", fmt.Errorf(
"no Supervisor upstream identity providers with name \"%s\" were found."+
" Found these upstreams: %s", idpName, pinnipedIDPsString)
return "", "", nil, fmt.Errorf(
"no Supervisor upstream identity providers with name %q were found. "+
"Found these upstreams: %s", specifiedIDPName, pinnipedIDPsString)
}
return idpName, discoveredType, nil
return specifiedIDPName, discoveredType, discoveredFlows, nil
case len(pinnipedIDPs) == 1:
// The user did not specify any name or type, but there is only one found, so select it.
return pinnipedIDPs[0].Name, pinnipedIDPs[0].Type, pinnipedIDPs[0].Flows, nil
default:
return "", "", fmt.Errorf(
"multiple Supervisor upstream identity providers were found,"+
" so the --upstream-identity-provider-name/--upstream-identity-provider-type flags must be specified."+
" Found these upstreams: %s",
// The user did not specify any name or type, and there is more than one found.
return "", "", nil, fmt.Errorf(
"multiple Supervisor upstream identity providers were found, "+
"so the --upstream-identity-provider-name/--upstream-identity-provider-type flags must be specified. "+
"Found these upstreams: %s",
pinnipedIDPsString)
}
}
func selectUpstreamIDPFlow(discoveredIDPFlows []idpdiscoveryv1alpha1.IDPFlow, selectedIDPName string, selectedIDPType idpdiscoveryv1alpha1.IDPType, specifiedFlow string) (idpdiscoveryv1alpha1.IDPFlow, error) {
switch {
case len(discoveredIDPFlows) == 0:
// No flows listed by discovery means that we are talking to an old Supervisor from before this feature existed.
// If the user specified a flow on the CLI flag then use it without validation, otherwise skip flow selection
// and return empty string.
return idpdiscoveryv1alpha1.IDPFlow(specifiedFlow), nil
case specifiedFlow != "":
// The user specified a flow, so validate that it is available for the selected IDP.
for _, flow := range discoveredIDPFlows {
if flow.Equals(specifiedFlow) {
// Found it, so use it as specified by the user.
return flow, nil
}
}
return "", fmt.Errorf(
"no client flow %q for Supervisor upstream identity provider %q of type %q were found. "+
"Found these flows: %v",
specifiedFlow, selectedIDPName, selectedIDPType, discoveredIDPFlows)
case len(discoveredIDPFlows) == 1:
// The user did not specify a flow, but there is only one found, so select it.
return discoveredIDPFlows[0], nil
default:
// The user did not specify a flow, and more than one was found.
return "", fmt.Errorf(
"multiple client flows for Supervisor upstream identity provider %q of type %q were found, "+
"so the --upstream-identity-provider-flow flag must be specified. "+
"Found these flows: %v",
selectedIDPName, selectedIDPType, discoveredIDPFlows)
}
}

View File

@@ -149,8 +149,9 @@ func TestGetKubeconfig(t *testing.T) {
--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)
--upstream-identity-provider-flow string The type of client flow to use with the upstream identity provider during login with a Supervisor (e.g. 'cli_password', 'browser_authcode')
--upstream-identity-provider-name string The name of the upstream identity provider used during login with a Supervisor
--upstream-identity-provider-type string The type of the upstream identity provider used during login with a Supervisor (e.g. 'oidc', 'ldap')
--upstream-identity-provider-type string The type of the upstream identity provider used during login with a Supervisor (e.g. 'oidc', 'ldap', 'activedirectory')
`)
},
},
@@ -814,7 +815,7 @@ func TestGetKubeconfig(t *testing.T) {
},
},
{
name: "when IDP discovery document contains multiple pinniped_idps and no name or type flags are given",
name: "when IDP discovery document contains multiple IDPs and no name or type flags are given",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
@@ -1033,6 +1034,33 @@ func TestGetKubeconfig(t *testing.T) {
return `Error: while forming request to IDP discovery URL: parse "https%://illegal_url": first path segment in URL cannot contain colon` + "\n"
},
},
{
name: "supervisor upstream IDP discovery does not find matching IDP when name and type are both specified",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-name", "does-not-exist-idp",
"--upstream-identity-provider-type", "ldap",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap"},
{"name": "some-other-ldap-idp", "type": "ldap"}
]
}`),
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: no Supervisor upstream identity providers with name "does-not-exist-idp" of type "ldap" were found.` +
` Found these upstreams: [{"name":"some-ldap-idp","type":"ldap"},{"name":"some-other-ldap-idp","type":"ldap"}]` + "\n"
},
},
{
name: "supervisor upstream IDP discovery fails to resolve ambiguity when type is specified but name is not",
args: func(issuerCABundle string, issuerURL string) []string {
@@ -1091,7 +1119,7 @@ func TestGetKubeconfig(t *testing.T) {
},
},
{
name: "supervisor upstream IDP discovery fails to find any matching idps when type is specified but name is not",
name: "supervisor upstream IDP discovery fails to find any matching IDPs when type is specified but name is not",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
@@ -1117,7 +1145,32 @@ func TestGetKubeconfig(t *testing.T) {
},
},
{
name: "supervisor upstream IDP discovery fails to find any matching idps when name is specified but type is not",
name: "supervisor upstream IDP discovery fails to find any matching IDPs when type is specified but name is not and there is only one IDP found",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-type", "ldap",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-oidc-idp", "type": "oidc"}
]
}`),
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: no Supervisor upstream identity providers of type "ldap" were found.` +
` Found these upstreams: [{"name":"some-oidc-idp","type":"oidc"}]` + "\n"
},
},
{
name: "supervisor upstream IDP discovery fails to find any matching IDPs when name is specified but type is not",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
@@ -1142,6 +1195,80 @@ func TestGetKubeconfig(t *testing.T) {
` Found these upstreams: [{"name":"some-oidc-idp","type":"oidc"},{"name":"some-other-oidc-idp","type":"oidc"}]` + "\n"
},
},
{
name: "supervisor upstream IDP discovery fails to find any matching IDPs when name is specified but type is not and there is only one IDP found",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-name", "my-nonexistent-idp",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-oidc-idp", "type": "oidc"}
]
}`),
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: no Supervisor upstream identity providers with name "my-nonexistent-idp" were found.` +
` Found these upstreams: [{"name":"some-oidc-idp","type":"oidc"}]` + "\n"
},
},
{
name: "supervisor upstream IDP discovery when flow is specified but it does not match any flow returned by discovery",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-flow", "my-nonexistent-flow",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-oidc-idp", "type": "oidc", "flows": ["non-matching-flow-1", "non-matching-flow-2"]}
]
}`),
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: no client flow "my-nonexistent-flow" for Supervisor upstream identity provider "some-oidc-idp" of type "oidc" were found.` +
` Found these flows: [non-matching-flow-1 non-matching-flow-2]` + "\n"
},
},
{
name: "supervisor upstream IDP discovery when no flow is specified and more than one flow is returned by discovery",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-oidc-idp", "type": "oidc", "flows": ["flow1", "flow2"]}
]
}`),
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: multiple client flows for Supervisor upstream identity provider "some-oidc-idp" of type "oidc" were found, so the --upstream-identity-provider-flow flag must be specified.` +
` Found these flows: [flow1 flow2]` + "\n"
},
},
{
name: "valid static token",
args: func(issuerCABundle string, issuerURL string) []string {
@@ -1352,6 +1479,7 @@ func TestGetKubeconfig(t *testing.T) {
"--concierge-ca-bundle", testConciergeCABundlePath,
"--oidc-issuer", issuerURL,
"--oidc-skip-browser",
"--oidc-skip-listen",
"--oidc-listen-port", "1234",
"--oidc-ca-bundle", f.Name(),
"--oidc-session-cache", "/path/to/cache/dir/sessions.yaml",
@@ -1405,6 +1533,7 @@ func TestGetKubeconfig(t *testing.T) {
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --skip-browser
- --skip-listen
- --listen-port=1234
- --ca-bundle-data=%s
- --session-cache=/path/to/cache/dir/sessions.yaml
@@ -1533,7 +1662,7 @@ func TestGetKubeconfig(t *testing.T) {
},
},
{
name: "autodetect impersonation proxy with autodiscovered JWT authenticator",
name: "autodetect impersonation proxy with auto-discovered JWT authenticator",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
@@ -1956,7 +2085,7 @@ func TestGetKubeconfig(t *testing.T) {
}
}`, issuerURL)
},
idpsDiscoveryStatusCode: http.StatusBadRequest, // IDPs endpoint shouldn't be called by this test
idpsDiscoveryStatusCode: http.StatusBadRequest, // IDP discovery endpoint shouldn't be called by this test
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
@@ -2013,13 +2142,14 @@ func TestGetKubeconfig(t *testing.T) {
},
},
{
name: "when upstream idp related flags are sent, pass them through",
name: "when all upstream IDP related flags are sent, pass them through without performing IDP discovery",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--upstream-identity-provider-name=some-oidc-idp",
"--upstream-identity-provider-type=oidc",
"--upstream-identity-provider-flow=foobar",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
@@ -2028,7 +2158,7 @@ func TestGetKubeconfig(t *testing.T) {
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryStatusCode: http.StatusNotFound,
oidcDiscoveryStatusCode: http.StatusNotFound, // should not get called by the client in this case
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
@@ -2078,6 +2208,7 @@ func TestGetKubeconfig(t *testing.T) {
- --request-audience=test-audience
- --upstream-identity-provider-name=some-oidc-idp
- --upstream-identity-provider-type=oidc
- --upstream-identity-provider-flow=foobar
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
@@ -2087,13 +2218,14 @@ func TestGetKubeconfig(t *testing.T) {
},
},
{
name: "when upstream IDP related flags are sent, pass them through even when IDP discovery shows a different IDP",
name: "when all upstream IDP related flags are sent, pass them through even when IDP discovery shows a different IDP",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--upstream-identity-provider-name=some-oidc-idp",
"--upstream-identity-provider-type=oidc",
"--upstream-identity-provider-flow=foobar",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
@@ -2157,6 +2289,7 @@ func TestGetKubeconfig(t *testing.T) {
- --request-audience=test-audience
- --upstream-identity-provider-name=some-oidc-idp
- --upstream-identity-provider-type=oidc
- --upstream-identity-provider-flow=foobar
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
@@ -2338,6 +2471,244 @@ func TestGetKubeconfig(t *testing.T) {
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
}, // TODO make sure there are active directory tests for various flows
{
name: "supervisor upstream IDP discovery when both name and type are specified but flow is not and a matching IDP is found",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-name", "some-ldap-idp",
"--upstream-identity-provider-type", "ldap",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap"},
{"name": "some-oidc-idp", "type": "oidc"},
{"name": "some-other-oidc-idp", "type": "oidc"}
]
}`),
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "supervisor upstream IDP discovery when flow is specified and no flows were returned by discovery uses the specified flow",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-flow", "foobar",
"--upstream-identity-provider-type", "ldap",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap"},
{"name": "some-oidc-idp", "type": "oidc"},
{"name": "some-other-oidc-idp", "type": "oidc"}
]
}`),
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
- --upstream-identity-provider-flow=foobar
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "supervisor upstream IDP discovery when flow is specified and it matches a flow returned by discovery uses the specified flow",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-flow", "cli_password",
"--upstream-identity-provider-type", "ldap",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap", "flows": ["some_flow", "cli_password", "some_other_flow"]}
]
}`),
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
- --upstream-identity-provider-flow=cli_password
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "supervisor upstream IDP discovery when no flow is specified but there is only one flow returned by discovery uses the discovered flow",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-type", "ldap",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap", "flows": ["cli_password"]}
]
}`),
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
- --upstream-identity-provider-flow=cli_password
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
}
for _, tt := range tests {

View File

@@ -14,6 +14,7 @@ import (
"net/http"
"os"
"path/filepath"
"strings"
"time"
"github.com/coreos/go-oidc/v3/oidc"
@@ -23,6 +24,7 @@ import (
"k8s.io/client-go/transport"
"k8s.io/klog/v2/klogr"
idpdiscoveryv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idpdiscovery/v1alpha1"
"go.pinniped.dev/internal/execcredcache"
"go.pinniped.dev/internal/groupsuffix"
"go.pinniped.dev/internal/plog"
@@ -59,6 +61,7 @@ type oidcLoginFlags struct {
listenPort uint16
scopes []string
skipBrowser bool
skipListen bool
sessionCachePath string
caBundlePaths []string
caBundleData []string
@@ -73,6 +76,7 @@ type oidcLoginFlags struct {
credentialCachePath string
upstreamIdentityProviderName string
upstreamIdentityProviderType string
upstreamIdentityProviderFlow string
}
func oidcLoginCommand(deps oidcLoginCommandDeps) *cobra.Command {
@@ -91,6 +95,7 @@ func oidcLoginCommand(deps oidcLoginCommandDeps) *cobra.Command {
cmd.Flags().Uint16Var(&flags.listenPort, "listen-port", 0, "TCP port for localhost listener (authorization code flow only)")
cmd.Flags().StringSliceVar(&flags.scopes, "scopes", []string{oidc.ScopeOfflineAccess, oidc.ScopeOpenID, "pinniped:request-audience"}, "OIDC scopes to request during login")
cmd.Flags().BoolVar(&flags.skipBrowser, "skip-browser", false, "Skip opening the browser (just print the URL)")
cmd.Flags().BoolVar(&flags.skipListen, "skip-listen", false, "Skip starting a localhost callback listener (manual copy/paste flow only)")
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 encoded TLS certificate authority bundle (base64 encoded PEM format, optional, can be repeated)")
@@ -105,8 +110,11 @@ func oidcLoginCommand(deps oidcLoginCommandDeps) *cobra.Command {
cmd.Flags().StringVar(&flags.conciergeAPIGroupSuffix, "concierge-api-group-suffix", groupsuffix.PinnipedDefaultSuffix, "Concierge API group suffix")
cmd.Flags().StringVar(&flags.credentialCachePath, "credential-cache", filepath.Join(mustGetConfigDir(), "credentials.yaml"), "Path to cluster-specific credentials cache (\"\" disables the cache)")
cmd.Flags().StringVar(&flags.upstreamIdentityProviderName, "upstream-identity-provider-name", "", "The name of the upstream identity provider used during login with a Supervisor")
cmd.Flags().StringVar(&flags.upstreamIdentityProviderType, "upstream-identity-provider-type", "oidc", "The type of the upstream identity provider used during login with a Supervisor (e.g. 'oidc', 'ldap')")
cmd.Flags().StringVar(&flags.upstreamIdentityProviderType, "upstream-identity-provider-type", idpdiscoveryv1alpha1.IDPTypeOIDC.String(), fmt.Sprintf("The type of the upstream identity provider used during login with a Supervisor (e.g. '%s', '%s', '%s')", idpdiscoveryv1alpha1.IDPTypeOIDC, idpdiscoveryv1alpha1.IDPTypeLDAP, idpdiscoveryv1alpha1.IDPTypeActiveDirectory))
cmd.Flags().StringVar(&flags.upstreamIdentityProviderFlow, "upstream-identity-provider-flow", "", fmt.Sprintf("The type of client flow to use with the upstream identity provider during login with a Supervisor (e.g. '%s', '%s')", idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode, idpdiscoveryv1alpha1.IDPFlowCLIPassword))
// --skip-listen is mainly needed for testing. We'll leave it hidden until we have a non-testing use case.
mustMarkHidden(cmd, "skip-listen")
mustMarkHidden(cmd, "debug-session-cache")
mustMarkRequired(cmd, "issuer")
cmd.RunE = func(cmd *cobra.Command, args []string) error { return runOIDCLogin(cmd, deps, flags) }
@@ -156,17 +164,14 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
flags.upstreamIdentityProviderName, flags.upstreamIdentityProviderType))
}
switch flags.upstreamIdentityProviderType {
case "oidc":
// this is the default, so don't need to do anything
case "ldap":
opts = append(opts, oidcclient.WithCLISendingCredentials())
default:
// Surprisingly cobra does not support this kind of flag validation. See https://github.com/spf13/pflag/issues/236
return fmt.Errorf(
"--upstream-identity-provider-type value not recognized: %s (supported values: oidc, ldap)",
flags.upstreamIdentityProviderType)
flowOpts, err := flowOptions(
idpdiscoveryv1alpha1.IDPType(flags.upstreamIdentityProviderType),
idpdiscoveryv1alpha1.IDPFlow(flags.upstreamIdentityProviderFlow),
)
if err != nil {
return err
}
opts = append(opts, flowOpts...)
var concierge *conciergeclient.Client
if flags.conciergeEnabled {
@@ -182,12 +187,14 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
}
}
// --skip-browser replaces the default "browser open" function with one that prints to stderr.
// --skip-browser skips opening the browser.
if flags.skipBrowser {
opts = append(opts, oidcclient.WithBrowserOpen(func(url string) error {
cmd.PrintErr("Please log in: ", url, "\n")
return nil
}))
opts = append(opts, oidcclient.WithSkipBrowserOpen())
}
// --skip-listen skips starting the localhost callback listener.
if flags.skipListen {
opts = append(opts, oidcclient.WithSkipListen())
}
if len(flags.caBundlePaths) > 0 || len(flags.caBundleData) > 0 {
@@ -245,6 +252,46 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
return json.NewEncoder(cmd.OutOrStdout()).Encode(cred)
}
func flowOptions(requestedIDPType idpdiscoveryv1alpha1.IDPType, requestedFlow idpdiscoveryv1alpha1.IDPFlow) ([]oidcclient.Option, error) {
useCLIFlow := []oidcclient.Option{oidcclient.WithCLISendingCredentials()}
switch requestedIDPType {
case idpdiscoveryv1alpha1.IDPTypeOIDC:
switch requestedFlow {
case idpdiscoveryv1alpha1.IDPFlowCLIPassword:
return useCLIFlow, nil
case idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode, "":
return nil, nil // browser authcode flow is the default Option, so don't need to return an Option here
default:
return nil, fmt.Errorf(
"--upstream-identity-provider-flow value not recognized for identity provider type %q: %s (supported values: %s)",
requestedIDPType, requestedFlow, strings.Join([]string{idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode.String(), idpdiscoveryv1alpha1.IDPFlowCLIPassword.String()}, ", "))
}
case idpdiscoveryv1alpha1.IDPTypeLDAP, idpdiscoveryv1alpha1.IDPTypeActiveDirectory:
switch requestedFlow {
case idpdiscoveryv1alpha1.IDPFlowCLIPassword, "":
return useCLIFlow, nil
case idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode:
fallthrough // not supported for LDAP providers, so fallthrough to error case
default:
return nil, fmt.Errorf(
"--upstream-identity-provider-flow value not recognized for identity provider type %q: %s (supported values: %s)",
requestedIDPType, requestedFlow, []string{idpdiscoveryv1alpha1.IDPFlowCLIPassword.String()})
}
default:
// Surprisingly cobra does not support this kind of flag validation. See https://github.com/spf13/pflag/issues/236
return nil, fmt.Errorf(
"--upstream-identity-provider-type value not recognized: %s (supported values: %s)",
requestedIDPType,
strings.Join([]string{
idpdiscoveryv1alpha1.IDPTypeOIDC.String(),
idpdiscoveryv1alpha1.IDPTypeLDAP.String(),
idpdiscoveryv1alpha1.IDPTypeActiveDirectory.String(),
}, ", "),
)
}
}
func makeClient(caBundlePaths []string, caBundleData []string) (*http.Client, error) {
pool := x509.NewCertPool()
for _, p := range caBundlePaths {

View File

@@ -77,8 +77,9 @@ func TestLoginOIDCCommand(t *testing.T) {
--scopes strings OIDC scopes to request during login (default [offline_access,openid,pinniped:request-audience])
--session-cache string Path to session cache file (default "` + cfgDir + `/sessions.yaml")
--skip-browser Skip opening the browser (just print the URL)
--upstream-identity-provider-flow string The type of client flow to use with the upstream identity provider during login with a Supervisor (e.g. 'browser_authcode', 'cli_password')
--upstream-identity-provider-name string The name of the upstream identity provider used during login with a Supervisor
--upstream-identity-provider-type string The type of the upstream identity provider used during login with a Supervisor (e.g. 'oidc', 'ldap') (default "oidc")
--upstream-identity-provider-type string The type of the upstream identity provider used during login with a Supervisor (e.g. 'oidc', 'ldap', 'activedirectory') (default "oidc")
`),
},
{
@@ -148,11 +149,11 @@ func TestLoginOIDCCommand(t *testing.T) {
},
wantError: true,
wantStderr: here.Doc(`
Error: --upstream-identity-provider-type value not recognized: invalid (supported values: oidc, ldap)
Error: --upstream-identity-provider-type value not recognized: invalid (supported values: oidc, ldap, activedirectory)
`),
},
{
name: "oidc upstream type is allowed",
name: "oidc upstream type with default flow is allowed",
args: []string{
"--issuer", "test-issuer",
"--client-id", "test-client-id",
@@ -160,10 +161,48 @@ func TestLoginOIDCCommand(t *testing.T) {
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
},
wantOptionsCount: 4,
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
},
{
name: "ldap upstream type is allowed",
name: "oidc upstream type with CLI flow is allowed",
args: []string{
"--issuer", "test-issuer",
"--client-id", "test-client-id",
"--upstream-identity-provider-type", "oidc",
"--upstream-identity-provider-flow", "cli_password",
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
},
wantOptionsCount: 5,
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
},
{
name: "oidc upstream type with browser flow is allowed",
args: []string{
"--issuer", "test-issuer",
"--client-id", "test-client-id",
"--upstream-identity-provider-type", "oidc",
"--upstream-identity-provider-flow", "browser_authcode",
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
},
wantOptionsCount: 4,
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
},
{
name: "oidc upstream type with unsupported flow is an error",
args: []string{
"--issuer", "test-issuer",
"--client-id", "test-client-id",
"--upstream-identity-provider-type", "oidc",
"--upstream-identity-provider-flow", "foobar",
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
},
wantError: true,
wantStderr: here.Doc(`
Error: --upstream-identity-provider-flow value not recognized for identity provider type "oidc": foobar (supported values: browser_authcode, cli_password)
`),
},
{
name: "ldap upstream type with default flow is allowed",
args: []string{
"--issuer", "test-issuer",
"--client-id", "test-client-id",
@@ -171,7 +210,70 @@ func TestLoginOIDCCommand(t *testing.T) {
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
},
wantOptionsCount: 5,
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
},
{
name: "activedirectory upstream type with default flow is allowed",
args: []string{
"--issuer", "test-issuer",
"--client-id", "test-client-id",
"--upstream-identity-provider-type", "activedirectory",
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
},
wantOptionsCount: 5,
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
},
{
name: "ldap upstream type with CLI flow is allowed",
args: []string{
"--issuer", "test-issuer",
"--client-id", "test-client-id",
"--upstream-identity-provider-type", "ldap",
"--upstream-identity-provider-flow", "cli_password",
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
},
wantOptionsCount: 5,
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
},
{
name: "ldap upstream type with unsupported flow is an error",
args: []string{
"--issuer", "test-issuer",
"--client-id", "test-client-id",
"--upstream-identity-provider-type", "ldap",
"--upstream-identity-provider-flow", "browser_authcode", // "browser_authcode" is only supported for OIDC upstreams
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
},
wantError: true,
wantStderr: here.Doc(`
Error: --upstream-identity-provider-flow value not recognized for identity provider type "ldap": browser_authcode (supported values: [cli_password])
`),
},
{
name: "active directory upstream type with CLI flow is allowed",
args: []string{
"--issuer", "test-issuer",
"--client-id", "test-client-id",
"--upstream-identity-provider-type", "activedirectory",
"--upstream-identity-provider-flow", "cli_password",
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
},
wantOptionsCount: 5,
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
},
{
name: "active directory upstream type with unsupported flow is an error",
args: []string{
"--issuer", "test-issuer",
"--client-id", "test-client-id",
"--upstream-identity-provider-type", "activedirectory",
"--upstream-identity-provider-flow", "browser_authcode", // "browser_authcode" is only supported for OIDC upstreams
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
},
wantError: true,
wantStderr: here.Doc(`
Error: --upstream-identity-provider-flow value not recognized for identity provider type "activedirectory": browser_authcode (supported values: [cli_password])
`),
},
{
name: "login error",
@@ -214,7 +316,7 @@ func TestLoginOIDCCommand(t *testing.T) {
},
env: map[string]string{"PINNIPED_DEBUG": "true"},
wantOptionsCount: 4,
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
wantLogs: []string{
"\"level\"=0 \"msg\"=\"Pinniped login: Performing OIDC login\" \"client id\"=\"test-client-id\" \"issuer\"=\"test-issuer\"",
"\"level\"=0 \"msg\"=\"Pinniped login: No concierge configured, skipping token credential exchange\"",
@@ -226,6 +328,7 @@ func TestLoginOIDCCommand(t *testing.T) {
"--client-id", "test-client-id",
"--issuer", "test-issuer",
"--skip-browser",
"--skip-listen",
"--listen-port", "1234",
"--debug-session-cache",
"--request-audience", "cluster-1234",
@@ -242,8 +345,8 @@ func TestLoginOIDCCommand(t *testing.T) {
"--upstream-identity-provider-type", "ldap",
},
env: map[string]string{"PINNIPED_DEBUG": "true"},
wantOptionsCount: 10,
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"token":"exchanged-token"}}` + "\n",
wantOptionsCount: 11,
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"token":"exchanged-token"}}` + "\n",
wantLogs: []string{
"\"level\"=0 \"msg\"=\"Pinniped login: Performing OIDC login\" \"client id\"=\"test-client-id\" \"issuer\"=\"test-issuer\"",
"\"level\"=0 \"msg\"=\"Pinniped login: Exchanging token for cluster credential\" \"authenticator name\"=\"test-authenticator\" \"authenticator type\"=\"webhook\" \"endpoint\"=\"https://127.0.0.1:1234/\"",

View File

@@ -119,7 +119,7 @@ func TestLoginStaticCommand(t *testing.T) {
env: map[string]string{
"TEST_TOKEN_ENV": "test-token",
},
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"token":"test-token"}}` + "\n",
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"token":"test-token"}}` + "\n",
},
{
name: "concierge failure",
@@ -159,7 +159,7 @@ func TestLoginStaticCommand(t *testing.T) {
"--token", "test-token",
},
env: map[string]string{"PINNIPED_DEBUG": "true"},
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"token":"test-token"}}` + "\n",
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"token":"test-token"}}` + "\n",
},
}
for _, tt := range tests {

View File

@@ -3,7 +3,20 @@
package main
import "go.pinniped.dev/cmd/pinniped/cmd"
import (
"os"
"github.com/pkg/browser"
"go.pinniped.dev/cmd/pinniped/cmd"
)
//nolint: gochecknoinits
func init() {
// browsers like chrome like to write to our std out which breaks our JSON ExecCredential output
// thus we redirect the browser's std out to our std err
browser.Stdout = os.Stderr
}
func main() {
cmd.Execute()

View File

@@ -47,7 +47,7 @@ spec:
description: "ExternalEndpoint describes the HTTPS endpoint where
the proxy will be exposed. If not set, the proxy will be served
using the external name of the LoadBalancer service or the cluster
service DNS name. \n This field must be non-empty when spec.impersonationProxy.service.mode
service DNS name. \n This field must be non-empty when spec.impersonationProxy.service.type
is \"None\"."
type: string
mode:

View File

@@ -29,6 +29,18 @@ metadata:
labels: #@ labels()
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: #@ defaultResourceNameWithSuffix("impersonation-proxy")
namespace: #@ namespace()
labels: #@ labels()
annotations:
#! we need to create this service account before we create the secret
kapp.k14s.io/change-group: "impersonation-proxy.concierge.pinniped.dev/serviceaccount"
secrets: #! make sure the token controller does not create any other secrets
- name: #@ defaultResourceNameWithSuffix("impersonation-proxy")
---
apiVersion: v1
kind: ConfigMap
metadata:
name: #@ defaultResourceNameWithSuffix("config")
@@ -104,6 +116,7 @@ spec:
scheduler.alpha.kubernetes.io/critical-pod: ""
spec:
securityContext:
readOnlyRootFilesystem: true
runAsUser: #@ data.values.run_as_user
runAsGroup: #@ data.values.run_as_group
serviceAccountName: #@ defaultResourceName()
@@ -126,14 +139,28 @@ spec:
limits:
cpu: "100m"
memory: "128Mi"
args:
command:
- pinniped-concierge
- --config=/etc/config/pinniped.yaml
- --downward-api-path=/etc/podinfo
volumeMounts:
- name: tmp
mountPath: /tmp
- name: config-volume
mountPath: /etc/config
- name: podinfo
mountPath: /etc/podinfo
- name: impersonation-proxy
mountPath: /var/run/secrets/impersonation-proxy.concierge.pinniped.dev/serviceaccount
env:
#@ if data.values.https_proxy:
- name: HTTPS_PROXY
value: #@ data.values.https_proxy
#@ end
#@ if data.values.https_proxy and data.values.no_proxy:
- name: NO_PROXY
value: #@ data.values.no_proxy
#@ end
livenessProbe:
httpGet:
path: /healthz
@@ -153,10 +180,22 @@ spec:
periodSeconds: 10
failureThreshold: 3
volumes:
- name: tmp
emptyDir:
medium: Memory
sizeLimit: 100Mi
- name: config-volume
readOnly: true
configMap:
name: #@ defaultResourceNameWithSuffix("config")
- name: impersonation-proxy
secret:
secretName: #@ defaultResourceNameWithSuffix("impersonation-proxy")
items: #! make sure our pod does not start until the token controller has a chance to populate the secret
- key: token
path: token
- name: podinfo
readOnly: true
downwardAPI:
items:
- path: "labels"
@@ -265,3 +304,16 @@ spec:
loadBalancerIP: #@ data.values.impersonation_proxy_spec.service.load_balancer_ip
#@ end
annotations: #@ data.values.impersonation_proxy_spec.service.annotations
---
apiVersion: v1
kind: Secret
metadata:
name: #@ defaultResourceNameWithSuffix("impersonation-proxy")
namespace: #@ namespace()
labels: #@ labels()
annotations:
#! wait until the SA exists to create this secret so that the token controller does not delete it
#! we have this secret at the end so that kubectl will create the service account first
kapp.k14s.io/change-rule: "upsert after upserting impersonation-proxy.concierge.pinniped.dev/serviceaccount"
kubernetes.io/service-account.name: #@ defaultResourceNameWithSuffix("impersonation-proxy")
type: kubernetes.io/service-account-token

View File

@@ -28,12 +28,6 @@ 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 ]
@@ -64,6 +58,35 @@ roleRef:
name: #@ defaultResourceNameWithSuffix("aggregated-api-server")
apiGroup: rbac.authorization.k8s.io
#! Give minimal permissions to impersonation proxy service account
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: #@ defaultResourceNameWithSuffix("impersonation-proxy")
labels: #@ labels()
rules:
- 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" ]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: #@ defaultResourceNameWithSuffix("impersonation-proxy")
labels: #@ labels()
subjects:
- kind: ServiceAccount
name: #@ defaultResourceNameWithSuffix("impersonation-proxy")
namespace: #@ namespace()
roleRef:
kind: ClusterRole
name: #@ defaultResourceNameWithSuffix("impersonation-proxy")
apiGroup: rbac.authorization.k8s.io
#! Give permission to the kube-cert-agent Pod to run privileged.
---
apiVersion: rbac.authorization.k8s.io/v1
@@ -130,6 +153,9 @@ rules:
- apiGroups: [ "" ]
resources: [ configmaps ]
verbs: [ list, get, watch ]
- apiGroups: [ coordination.k8s.io ]
resources: [ leases ]
verbs: [ create, get, update ]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1

View File

@@ -93,3 +93,11 @@ impersonation_proxy_spec:
{service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "4000"}
#! When mode LoadBalancer is set, this will set the LoadBalancer Service's Spec.LoadBalancerIP.
load_balancer_ip:
#! Set the standard golang HTTPS_PROXY and NO_PROXY environment variables on the Concierge containers.
#! These will be used when the Concierge makes backend-to-backend calls to authenticators using HTTPS,
#! e.g. when the Concierge fetches discovery documents, JWKS keys, and POSTs to token webhooks.
#! The Concierge never makes insecure HTTP calls, so there is no reason to set HTTP_PROXY.
#! Optional.
https_proxy: #! e.g. http://proxy.example.com
no_proxy: "$(KUBERNETES_SERVICE_HOST),169.254.169.254,127.0.0.1,localhost,.svc,.cluster.local" #! do not proxy Kubernetes endpoints

View File

@@ -63,8 +63,8 @@ spec:
image: #@ data.values.image_repo + ":" + data.values.image_tag
#@ end
imagePullPolicy: IfNotPresent
command: #! override the default entrypoint
- /usr/local/bin/local-user-authenticator
command:
- local-user-authenticator
---
apiVersion: v1
kind: Service

View File

@@ -65,6 +65,7 @@ spec:
labels: #@ defaultLabel()
spec:
securityContext:
readOnlyRootFilesystem: true
runAsUser: #@ data.values.run_as_user
runAsGroup: #@ data.values.run_as_group
serviceAccountName: #@ defaultResourceName()
@@ -80,9 +81,8 @@ spec:
image: #@ data.values.image_repo + ":" + data.values.image_tag
#@ end
imagePullPolicy: IfNotPresent
command: #! override the default entrypoint
- /usr/local/bin/pinniped-supervisor
args:
command:
- pinniped-supervisor
- /etc/podinfo
- /etc/config/pinniped.yaml
resources:
@@ -102,6 +102,15 @@ spec:
protocol: TCP
- containerPort: 8443
protocol: TCP
env:
#@ if data.values.https_proxy:
- name: HTTPS_PROXY
value: #@ data.values.https_proxy
#@ end
#@ if data.values.https_proxy and data.values.no_proxy:
- name: NO_PROXY
value: #@ data.values.no_proxy
#@ end
livenessProbe:
httpGet:
path: /healthz
@@ -122,9 +131,11 @@ spec:
failureThreshold: 3
volumes:
- name: config-volume
readOnly: true
configMap:
name: #@ defaultResourceNameWithSuffix("static-config")
- name: podinfo
readOnly: true
downwardAPI:
items:
- path: "labels"

View File

@@ -0,0 +1,281 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.4.0
creationTimestamp: null
name: activedirectoryidentityproviders.idp.supervisor.pinniped.dev
spec:
group: idp.supervisor.pinniped.dev
names:
categories:
- pinniped
- pinniped-idp
- pinniped-idps
kind: ActiveDirectoryIdentityProvider
listKind: ActiveDirectoryIdentityProviderList
plural: activedirectoryidentityproviders
singular: activedirectoryidentityprovider
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .spec.host
name: Host
type: string
- jsonPath: .status.phase
name: Status
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1alpha1
schema:
openAPIV3Schema:
description: ActiveDirectoryIdentityProvider describes the configuration of
an upstream Microsoft Active Directory identity provider.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: Spec for configuring the identity provider.
properties:
bind:
description: Bind contains the configuration for how to provide access
credentials during an initial bind to the ActiveDirectory server
to be allowed to perform searches and binds to validate a user's
credentials during a user's authentication attempt.
properties:
secretName:
description: SecretName contains the name of a namespace-local
Secret object that provides the username and password for an
Active Directory bind user. This account will be used to perform
LDAP searches. The Secret should be of type "kubernetes.io/basic-auth"
which includes "username" and "password" keys. The username
value should be the full dn (distinguished name) of your bind
account, e.g. "cn=bind-account,ou=users,dc=example,dc=com".
The password must be non-empty.
minLength: 1
type: string
required:
- secretName
type: object
groupSearch:
description: GroupSearch contains the configuration for searching
for a user's group membership in ActiveDirectory.
properties:
attributes:
description: Attributes specifies how the group's information
should be read from each ActiveDirectory entry which was found
as the result of the group search.
properties:
groupName:
description: GroupName specifies the name of the attribute
in the Active Directory entries whose value shall become
a group name in the user's list of groups after a successful
authentication. The value of this field is case-sensitive
and must match the case of the attribute name returned by
the ActiveDirectory server in the user's entry. E.g. "cn"
for common name. Distinguished names can be used by specifying
lower-case "dn". Optional. When not specified, this defaults
to a custom field that looks like "sAMAccountName@domain",
where domain is constructed from the domain components of
the group DN.
type: string
type: object
base:
description: Base is the dn (distinguished name) that should be
used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com".
Optional, when not specified it will be based on the result
of a query for the defaultNamingContext (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse).
The default behavior searches your entire domain for groups.
It may make sense to specify a subtree as a search base if you
wish to exclude some groups for security reasons or to make
searches faster.
type: string
filter:
description: Filter is the ActiveDirectory search filter which
should be applied when searching for groups for a user. The
pattern "{}" must occur in the filter at least once and will
be dynamically replaced by the dn (distinguished name) of the
user entry found as a result of the user search. E.g. "member={}"
or "&(objectClass=groupOfNames)(member={})". For more information
about ActiveDirectory filters, see https://ldap.com/ldap-filters.
Note that the dn (distinguished name) is not an attribute of
an entry, so "dn={}" cannot be used. Optional. When not specified,
the default will act as if the filter were specified as "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={})".
This searches nested groups by default. Note that nested group
search can be slow for some Active Directory servers. To disable
it, you can set the filter to "(&(objectClass=group)(member={})"
type: string
type: object
host:
description: 'Host is the hostname of this Active Directory identity
provider, i.e., where to connect. For example: ldap.example.com:636.'
minLength: 1
type: string
tls:
description: TLS contains the connection settings for how to establish
the connection to the Host.
properties:
certificateAuthorityData:
description: X.509 Certificate Authority (base64-encoded PEM bundle).
If omitted, a default set of system roots will be trusted.
type: string
type: object
userSearch:
description: UserSearch contains the configuration for searching for
a user by name in Active Directory.
properties:
attributes:
description: Attributes specifies how the user's information should
be read from the ActiveDirectory entry which was found as the
result of the user search.
properties:
uid:
description: UID specifies the name of the attribute in the
ActiveDirectory entry which whose value shall be used to
uniquely identify the user within this ActiveDirectory provider
after a successful authentication. Optional, when empty
this defaults to "objectGUID".
type: string
username:
description: Username specifies the name of the attribute
in Active Directory entry whose value shall become the username
of the user after a successful authentication. Optional,
when empty this defaults to "userPrincipalName".
type: string
type: object
base:
description: Base is the dn (distinguished name) that should be
used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com".
Optional, when not specified it will be based on the result
of a query for the defaultNamingContext (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse).
The default behavior searches your entire domain for users.
It may make sense to specify a subtree as a search base if you
wish to exclude some users or to make searches faster.
type: string
filter:
description: Filter is the search filter which should be applied
when searching for users. The pattern "{}" must occur in the
filter at least once and will be dynamically replaced by the
username for which the search is being run. E.g. "mail={}" or
"&(objectClass=person)(uid={})". For more information about
LDAP filters, see https://ldap.com/ldap-filters. Note that the
dn (distinguished name) is not an attribute of an entry, so
"dn={}" cannot be used. Optional. When not specified, the default
will be '(&(objectClass=person)(!(objectClass=computer))(!(showInAdvancedViewOnly=TRUE))(|(sAMAccountName={}")(mail={})(userPrincipalName={})(sAMAccountType=805306368))'
This means that the user is a person, is not a computer, the
sAMAccountType is for a normal user account, and is not shown
in advanced view only (which would likely mean its a system
created service account with advanced permissions). Also, either
the sAMAccountName, the userPrincipalName, or the mail attribute
matches the input username.
type: string
type: object
required:
- host
type: object
status:
description: Status of the identity provider.
properties:
conditions:
description: Represents the observations of an identity provider's
current state.
items:
description: Condition status of a resource (mirrored from the metav1.Condition
type added in Kubernetes 1.19). In a future API version we can
switch to using the upstream type. See https://github.com/kubernetes/apimachinery/blob/v0.19.0/pkg/apis/meta/v1/types.go#L1353-L1413.
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
transitioned from one status to another. This should be when
the underlying condition changed. If that is not known, then
using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating
details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation
that the condition was set based upon. For instance, if .metadata.generation
is currently 12, but the .status.conditions[x].observedGeneration
is 9, the condition is out of date with respect to the current
state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating
the reason for the condition's last transition. Producers
of specific condition types may define expected values and
meanings for this field, and whether the values are considered
a guaranteed API. The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
--- Many .condition.type values are consistent across resources
like Available, but because arbitrary conditions can be useful
(see .node.status.conditions), the ability to deconflict is
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
x-kubernetes-list-map-keys:
- type
x-kubernetes-list-type: map
phase:
default: Pending
description: Phase summarizes the overall status of the ActiveDirectoryIdentityProvider.
enum:
- Pending
- Ready
- Error
type: string
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -59,11 +59,44 @@ spec:
additionalScopes:
description: AdditionalScopes are the scopes in addition to "openid"
that will be requested as part of the authorization request
flow with an OIDC identity provider. By default only the "openid"
scope will be requested.
flow with an OIDC identity provider. In the case of a Resource
Owner Password Credentials Grant flow, AdditionalScopes are
the scopes in addition to "openid" that will be requested as
part of the token request (see also the allowPasswordGrant field).
By default, only the "openid" scope will be requested.
items:
type: string
type: array
allowPasswordGrant:
description: AllowPasswordGrant, when true, will allow the use
of OAuth 2.0's Resource Owner Password Credentials Grant (see
https://datatracker.ietf.org/doc/html/rfc6749#section-4.3) to
authenticate to the OIDC provider using a username and password
without a web browser, in addition to the usual browser-based
OIDC Authorization Code Flow. The Resource Owner Password Credentials
Grant is not officially part of the OIDC specification, so it
may not be supported by your OIDC provider. If your OIDC provider
supports returning ID tokens from a Resource Owner Password
Credentials Grant token request, then you can choose to set
this field to true. This will allow end users to choose to present
their username and password to the kubectl CLI (using the Pinniped
plugin) to authenticate to the cluster, without using a web
browser to log in as is customary in OIDC Authorization Code
Flow. This may be convenient for users, especially for identities
from your OIDC provider which are not intended to represent
a human actor, such as service accounts performing actions in
a CI/CD environment. Even if your OIDC provider supports it,
you may wish to disable this behavior by setting this field
to false when you prefer to only allow users of this OIDCIdentityProvider
to log in via the browser-based OIDC Authorization Code Flow.
Using the Resource Owner Password Credentials Grant means that
the Pinniped CLI and Pinniped Supervisor will directly handle
your end users' passwords (similar to LDAPIdentityProvider),
and you will not be able to require multi-factor authentication
or use the other web-based login features of your OIDC provider
during Resource Owner Password Credentials Grant logins. AllowPasswordGrant
defaults to false.
type: boolean
type: object
claims:
description: Claims provides the names of token claims that will be

View File

@@ -40,6 +40,14 @@ rules:
- #@ pinnipedDevAPIGroupWithPrefix("idp.supervisor")
resources: [ldapidentityproviders/status]
verbs: [get, patch, update]
- apiGroups:
- #@ pinnipedDevAPIGroupWithPrefix("idp.supervisor")
resources: [activedirectoryidentityproviders]
verbs: [get, list, watch]
- apiGroups:
- #@ pinnipedDevAPIGroupWithPrefix("idp.supervisor")
resources: [activedirectoryidentityproviders/status]
verbs: [get, patch, update]
#! We want to be able to read pods/replicasets/deployment so we can learn who our deployment is to set
#! as an owner reference.
- apiGroups: [""]
@@ -48,6 +56,9 @@ rules:
- apiGroups: [apps]
resources: [replicasets,deployments]
verbs: [get]
- apiGroups: [ coordination.k8s.io ]
resources: [ leases ]
verbs: [ create, get, update ]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1

View File

@@ -65,3 +65,11 @@ run_as_group: 1001 #! run_as_group specifies the group ID that will own the proc
#! authentication.concierge.pinniped.dev, etc. As an example, if this is set to tuna.io, then
#! Pinniped API groups will look like foo.tuna.io. authentication.concierge.tuna.io, etc.
api_group_suffix: pinniped.dev
#! Set the standard golang HTTPS_PROXY and NO_PROXY environment variables on the Supervisor containers.
#! These will be used when the Supervisor makes backend-to-backend calls to upstream identity providers using HTTPS,
#! e.g. when the Supervisor fetches discovery documents, JWKS keys, and tokens from an upstream OIDC Provider.
#! The Supervisor never makes insecure HTTP calls, so there is no reason to set HTTP_PROXY.
#! Optional.
https_proxy: #! e.g. http://proxy.example.com
no_proxy: "$(KUBERNETES_SERVICE_HOST),169.254.169.254,127.0.0.1,localhost,.svc,.cluster.local" #! do not proxy Kubernetes endpoints

View File

@@ -31,3 +31,12 @@ metadata:
name: #@ pinnipedDevAPIGroupWithPrefix("ldapidentityproviders.idp.supervisor")
spec:
group: #@ pinnipedDevAPIGroupWithPrefix("idp.supervisor")
#@overlay/match by=overlay.subset({"kind": "CustomResourceDefinition", "metadata":{"name":"activedirectoryidentityproviders.idp.supervisor.pinniped.dev"}}), expects=1
---
metadata:
#@overlay/match missing_ok=True
labels: #@ labels()
name: #@ pinnipedDevAPIGroupWithPrefix("activedirectoryidentityproviders.idp.supervisor")
spec:
group: #@ pinnipedDevAPIGroupWithPrefix("idp.supervisor")

View File

@@ -411,7 +411,7 @@ ImpersonationProxyServiceSpec describes how the Concierge should provision a Ser
| *`mode`* __ImpersonationProxyMode__ | Mode configures whether the impersonation proxy should be started: - "disabled" explicitly disables the impersonation proxy. This is the default. - "enabled" explicitly enables the impersonation proxy. - "auto" enables or disables the impersonation proxy based upon the cluster in which it is running.
| *`service`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-impersonationproxyservicespec[$$ImpersonationProxyServiceSpec$$]__ | Service describes the configuration of the Service provisioned to expose the impersonation proxy to clients.
| *`externalEndpoint`* __string__ | ExternalEndpoint describes the HTTPS endpoint where the proxy will be exposed. If not set, the proxy will be served using the external name of the LoadBalancer service or the cluster service DNS name.
This field must be non-empty when spec.impersonationProxy.service.mode is "None".
This field must be non-empty when spec.impersonationProxy.service.type is "None".
|===
@@ -748,6 +748,157 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped supervisor identity pro
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovider"]
==== ActiveDirectoryIdentityProvider
ActiveDirectoryIdentityProvider describes the configuration of an upstream Microsoft Active Directory identity provider.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderlist[$$ActiveDirectoryIdentityProviderList$$]
****
[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-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec[$$ActiveDirectoryIdentityProviderSpec$$]__ | Spec for configuring the identity provider.
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderstatus[$$ActiveDirectoryIdentityProviderStatus$$]__ | Status of the identity provider.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderbind"]
==== ActiveDirectoryIdentityProviderBind
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec[$$ActiveDirectoryIdentityProviderSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`secretName`* __string__ | SecretName contains the name of a namespace-local Secret object that provides the username and password for an Active Directory bind user. This account will be used to perform LDAP searches. The Secret should be of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value should be the full dn (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". The password must be non-empty.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearch"]
==== ActiveDirectoryIdentityProviderGroupSearch
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec[$$ActiveDirectoryIdentityProviderSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`base`* __string__ | Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com". Optional, when not specified it will be based on the result of a query for the defaultNamingContext (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse). The default behavior searches your entire domain for groups. It may make sense to specify a subtree as a search base if you wish to exclude some groups for security reasons or to make searches faster.
| *`filter`* __string__ | Filter is the ActiveDirectory search filter which should be applied when searching for groups for a user. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})". For more information about ActiveDirectory filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the filter were specified as "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={})". This searches nested groups by default. Note that nested group search can be slow for some Active Directory servers. To disable it, you can set the filter to "(&(objectClass=group)(member={})"
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearchattributes[$$ActiveDirectoryIdentityProviderGroupSearchAttributes$$]__ | Attributes specifies how the group's information should be read from each ActiveDirectory entry which was found as the result of the group search.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearchattributes"]
==== ActiveDirectoryIdentityProviderGroupSearchAttributes
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearch[$$ActiveDirectoryIdentityProviderGroupSearch$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`groupName`* __string__ | GroupName specifies the name of the attribute in the Active Directory entries whose value shall become a group name in the user's list of groups after a successful authentication. The value of this field is case-sensitive and must match the case of the attribute name returned by the ActiveDirectory server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn". Optional. When not specified, this defaults to a custom field that looks like "sAMAccountName@domain", where domain is constructed from the domain components of the group DN.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec"]
==== ActiveDirectoryIdentityProviderSpec
Spec for configuring an ActiveDirectory identity provider.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovider[$$ActiveDirectoryIdentityProvider$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`host`* __string__ | Host is the hostname of this Active Directory identity provider, i.e., where to connect. For example: ldap.example.com:636.
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$]__ | TLS contains the connection settings for how to establish the connection to the Host.
| *`bind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderbind[$$ActiveDirectoryIdentityProviderBind$$]__ | Bind contains the configuration for how to provide access credentials during an initial bind to the ActiveDirectory server to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt.
| *`userSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderusersearch[$$ActiveDirectoryIdentityProviderUserSearch$$]__ | UserSearch contains the configuration for searching for a user by name in Active Directory.
| *`groupSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearch[$$ActiveDirectoryIdentityProviderGroupSearch$$]__ | GroupSearch contains the configuration for searching for a user's group membership in ActiveDirectory.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderstatus"]
==== ActiveDirectoryIdentityProviderStatus
Status of an Active Directory identity provider.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovider[$$ActiveDirectoryIdentityProvider$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`phase`* __ActiveDirectoryIdentityProviderPhase__ | Phase summarizes the overall status of the ActiveDirectoryIdentityProvider.
| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-condition[$$Condition$$] array__ | Represents the observations of an identity provider's current state.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderusersearch"]
==== ActiveDirectoryIdentityProviderUserSearch
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec[$$ActiveDirectoryIdentityProviderSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`base`* __string__ | Base is the dn (distinguished name) that should be used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com". Optional, when not specified it will be based on the result of a query for the defaultNamingContext (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse). The default behavior searches your entire domain for users. It may make sense to specify a subtree as a search base if you wish to exclude some users or to make searches faster.
| *`filter`* __string__ | Filter is the search filter which should be applied when searching for users. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the username for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will be '(&(objectClass=person)(!(objectClass=computer))(!(showInAdvancedViewOnly=TRUE))(|(sAMAccountName={}")(mail={})(userPrincipalName={})(sAMAccountType=805306368))' This means that the user is a person, is not a computer, the sAMAccountType is for a normal user account, and is not shown in advanced view only (which would likely mean its a system created service account with advanced permissions). Also, either the sAMAccountName, the userPrincipalName, or the mail attribute matches the input username.
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderusersearchattributes[$$ActiveDirectoryIdentityProviderUserSearchAttributes$$]__ | Attributes specifies how the user's information should be read from the ActiveDirectory entry which was found as the result of the user search.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderusersearchattributes"]
==== ActiveDirectoryIdentityProviderUserSearchAttributes
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderusersearch[$$ActiveDirectoryIdentityProviderUserSearch$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`username`* __string__ | Username specifies the name of the attribute in Active Directory entry whose value shall become the username of the user after a successful authentication. Optional, when empty this defaults to "userPrincipalName".
| *`uid`* __string__ | UID specifies the name of the attribute in the ActiveDirectory entry which whose value shall be used to uniquely identify the user within this ActiveDirectory provider after a successful authentication. Optional, when empty this defaults to "objectGUID".
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-condition"]
==== Condition
@@ -755,6 +906,7 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped supervisor identity pro
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderstatus[$$ActiveDirectoryIdentityProviderStatus$$]
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderstatus[$$LDAPIdentityProviderStatus$$]
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-oidcidentityproviderstatus[$$OIDCIdentityProviderStatus$$]
****
@@ -947,7 +1099,8 @@ OIDCAuthorizationConfig provides information about how to form the OAuth2 author
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`additionalScopes`* __string array__ | AdditionalScopes are the scopes in addition to "openid" that will be requested as part of the authorization request flow with an OIDC identity provider. By default only the "openid" scope will be requested.
| *`additionalScopes`* __string array__ | AdditionalScopes are the scopes in addition to "openid" that will be requested as part of the authorization request flow with an OIDC identity provider. In the case of a Resource Owner Password Credentials Grant flow, AdditionalScopes are the scopes in addition to "openid" that will be requested as part of the token request (see also the allowPasswordGrant field). By default, only the "openid" scope will be requested.
| *`allowPasswordGrant`* __boolean__ | AllowPasswordGrant, when true, will allow the use of OAuth 2.0's Resource Owner Password Credentials Grant (see https://datatracker.ietf.org/doc/html/rfc6749#section-4.3) to authenticate to the OIDC provider using a username and password without a web browser, in addition to the usual browser-based OIDC Authorization Code Flow. The Resource Owner Password Credentials Grant is not officially part of the OIDC specification, so it may not be supported by your OIDC provider. If your OIDC provider supports returning ID tokens from a Resource Owner Password Credentials Grant token request, then you can choose to set this field to true. This will allow end users to choose to present their username and password to the kubectl CLI (using the Pinniped plugin) to authenticate to the cluster, without using a web browser to log in as is customary in OIDC Authorization Code Flow. This may be convenient for users, especially for identities from your OIDC provider which are not intended to represent a human actor, such as service accounts performing actions in a CI/CD environment. Even if your OIDC provider supports it, you may wish to disable this behavior by setting this field to false when you prefer to only allow users of this OIDCIdentityProvider to log in via the browser-based OIDC Authorization Code Flow. Using the Resource Owner Password Credentials Grant means that the Pinniped CLI and Pinniped Supervisor will directly handle your end users' passwords (similar to LDAPIdentityProvider), and you will not be able to require multi-factor authentication or use the other web-based login features of your OIDC provider during Resource Owner Password Credentials Grant logins. AllowPasswordGrant defaults to false.
|===
@@ -1054,6 +1207,7 @@ Status of an OIDC identity provider.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec[$$ActiveDirectoryIdentityProviderSpec$$]
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec[$$LDAPIdentityProviderSpec$$]
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-oidcidentityproviderspec[$$OIDCIdentityProviderSpec$$]
****

View File

@@ -96,7 +96,7 @@ type ImpersonationProxySpec struct {
// ExternalEndpoint describes the HTTPS endpoint where the proxy will be exposed. If not set, the proxy will
// be served using the external name of the LoadBalancer service or the cluster service DNS name.
//
// This field must be non-empty when spec.impersonationProxy.service.mode is "None".
// This field must be non-empty when spec.impersonationProxy.service.type is "None".
//
// +optional
ExternalEndpoint string `json:"externalEndpoint,omitempty"`

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 v1alpha1
@@ -34,6 +34,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&OIDCIdentityProviderList{},
&LDAPIdentityProvider{},
&LDAPIdentityProviderList{},
&ActiveDirectoryIdentityProvider{},
&ActiveDirectoryIdentityProviderList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil

View File

@@ -0,0 +1,182 @@
// 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"
)
type ActiveDirectoryIdentityProviderPhase string
const (
// ActiveDirectoryPhasePending is the default phase for newly-created ActiveDirectoryIdentityProvider resources.
ActiveDirectoryPhasePending ActiveDirectoryIdentityProviderPhase = "Pending"
// ActiveDirectoryPhaseReady is the phase for an ActiveDirectoryIdentityProvider resource in a healthy state.
ActiveDirectoryPhaseReady ActiveDirectoryIdentityProviderPhase = "Ready"
// ActiveDirectoryPhaseError is the phase for an ActiveDirectoryIdentityProvider in an unhealthy state.
ActiveDirectoryPhaseError ActiveDirectoryIdentityProviderPhase = "Error"
)
// Status of an Active Directory identity provider.
type ActiveDirectoryIdentityProviderStatus struct {
// Phase summarizes the overall status of the ActiveDirectoryIdentityProvider.
// +kubebuilder:default=Pending
// +kubebuilder:validation:Enum=Pending;Ready;Error
Phase ActiveDirectoryIdentityProviderPhase `json:"phase,omitempty"`
// Represents the observations of an identity provider's current state.
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
}
type ActiveDirectoryIdentityProviderBind struct {
// SecretName contains the name of a namespace-local Secret object that provides the username and
// password for an Active Directory bind user. This account will be used to perform LDAP searches. The Secret should be
// of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value
// should be the full dn (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com".
// The password must be non-empty.
// +kubebuilder:validation:MinLength=1
SecretName string `json:"secretName"`
}
type ActiveDirectoryIdentityProviderUserSearchAttributes struct {
// Username specifies the name of the attribute in Active Directory entry whose value shall become the username
// of the user after a successful authentication.
// Optional, when empty this defaults to "userPrincipalName".
// +optional
Username string `json:"username,omitempty"`
// UID specifies the name of the attribute in the ActiveDirectory entry which whose value shall be used to uniquely
// identify the user within this ActiveDirectory provider after a successful authentication.
// Optional, when empty this defaults to "objectGUID".
// +optional
UID string `json:"uid,omitempty"`
}
type ActiveDirectoryIdentityProviderGroupSearchAttributes struct {
// GroupName specifies the name of the attribute in the Active Directory entries whose value shall become a group name
// in the user's list of groups after a successful authentication.
// The value of this field is case-sensitive and must match the case of the attribute name returned by the ActiveDirectory
// server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn".
// Optional. When not specified, this defaults to a custom field that looks like "sAMAccountName@domain",
// where domain is constructed from the domain components of the group DN.
// +optional
GroupName string `json:"groupName,omitempty"`
}
type ActiveDirectoryIdentityProviderUserSearch struct {
// Base is the dn (distinguished name) that should be used as the search base when searching for users.
// E.g. "ou=users,dc=example,dc=com".
// Optional, when not specified it will be based on the result of a query for the defaultNamingContext
// (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse).
// The default behavior searches your entire domain for users.
// It may make sense to specify a subtree as a search base if you wish to exclude some users
// or to make searches faster.
// +optional
Base string `json:"base,omitempty"`
// Filter is the search filter which should be applied when searching for users. The pattern "{}" must occur
// in the filter at least once and will be dynamically replaced by the username for which the search is being run.
// E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see
// https://ldap.com/ldap-filters.
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
// Optional. When not specified, the default will be
// '(&(objectClass=person)(!(objectClass=computer))(!(showInAdvancedViewOnly=TRUE))(|(sAMAccountName={}")(mail={})(userPrincipalName={})(sAMAccountType=805306368))'
// This means that the user is a person, is not a computer, the sAMAccountType is for a normal user account,
// and is not shown in advanced view only
// (which would likely mean its a system created service account with advanced permissions).
// Also, either the sAMAccountName, the userPrincipalName, or the mail attribute matches the input username.
// +optional
Filter string `json:"filter,omitempty"`
// Attributes specifies how the user's information should be read from the ActiveDirectory entry which was found as
// the result of the user search.
// +optional
Attributes ActiveDirectoryIdentityProviderUserSearchAttributes `json:"attributes,omitempty"`
}
type ActiveDirectoryIdentityProviderGroupSearch struct {
// Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g.
// "ou=groups,dc=example,dc=com".
// Optional, when not specified it will be based on the result of a query for the defaultNamingContext
// (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse).
// The default behavior searches your entire domain for groups.
// It may make sense to specify a subtree as a search base if you wish to exclude some groups
// for security reasons or to make searches faster.
// +optional
Base string `json:"base,omitempty"`
// Filter is the ActiveDirectory search filter which should be applied when searching for groups for a user.
// The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the
// dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or
// "&(objectClass=groupOfNames)(member={})". For more information about ActiveDirectory filters, see
// https://ldap.com/ldap-filters.
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
// Optional. When not specified, the default will act as if the filter were specified as
// "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={})".
// This searches nested groups by default.
// Note that nested group search can be slow for some Active Directory servers. To disable it,
// you can set the filter to
// "(&(objectClass=group)(member={})"
// +optional
Filter string `json:"filter,omitempty"`
// Attributes specifies how the group's information should be read from each ActiveDirectory entry which was found as
// the result of the group search.
// +optional
Attributes ActiveDirectoryIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"`
}
// Spec for configuring an ActiveDirectory identity provider.
type ActiveDirectoryIdentityProviderSpec struct {
// Host is the hostname of this Active Directory identity provider, i.e., where to connect. For example: ldap.example.com:636.
// +kubebuilder:validation:MinLength=1
Host string `json:"host"`
// TLS contains the connection settings for how to establish the connection to the Host.
TLS *TLSSpec `json:"tls,omitempty"`
// Bind contains the configuration for how to provide access credentials during an initial bind to the ActiveDirectory server
// to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt.
Bind ActiveDirectoryIdentityProviderBind `json:"bind,omitempty"`
// UserSearch contains the configuration for searching for a user by name in Active Directory.
UserSearch ActiveDirectoryIdentityProviderUserSearch `json:"userSearch,omitempty"`
// GroupSearch contains the configuration for searching for a user's group membership in ActiveDirectory.
GroupSearch ActiveDirectoryIdentityProviderGroupSearch `json:"groupSearch,omitempty"`
}
// ActiveDirectoryIdentityProvider describes the configuration of an upstream Microsoft Active Directory identity provider.
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:resource:categories=pinniped;pinniped-idp;pinniped-idps
// +kubebuilder:printcolumn:name="Host",type=string,JSONPath=`.spec.host`
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// +kubebuilder:subresource:status
type ActiveDirectoryIdentityProvider struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec for configuring the identity provider.
Spec ActiveDirectoryIdentityProviderSpec `json:"spec"`
// Status of the identity provider.
Status ActiveDirectoryIdentityProviderStatus `json:"status,omitempty"`
}
// List of ActiveDirectoryIdentityProvider objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ActiveDirectoryIdentityProviderList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ActiveDirectoryIdentityProvider `json:"items"`
}

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 v1alpha1

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 v1alpha1
@@ -39,9 +39,31 @@ type OIDCIdentityProviderStatus struct {
// request parameters.
type OIDCAuthorizationConfig struct {
// AdditionalScopes are the scopes in addition to "openid" that will be requested as part of the authorization
// request flow with an OIDC identity provider. By default only the "openid" scope will be requested.
// request flow with an OIDC identity provider.
// In the case of a Resource Owner Password Credentials Grant flow, AdditionalScopes are the scopes
// in addition to "openid" that will be requested as part of the token request (see also the allowPasswordGrant field).
// By default, only the "openid" scope will be requested.
// +optional
AdditionalScopes []string `json:"additionalScopes,omitempty"`
// AllowPasswordGrant, when true, will allow the use of OAuth 2.0's Resource Owner Password Credentials Grant
// (see https://datatracker.ietf.org/doc/html/rfc6749#section-4.3) to authenticate to the OIDC provider using a
// username and password without a web browser, in addition to the usual browser-based OIDC Authorization Code Flow.
// The Resource Owner Password Credentials Grant is not officially part of the OIDC specification, so it may not be
// supported by your OIDC provider. If your OIDC provider supports returning ID tokens from a Resource Owner Password
// Credentials Grant token request, then you can choose to set this field to true. This will allow end users to choose
// to present their username and password to the kubectl CLI (using the Pinniped plugin) to authenticate to the
// cluster, without using a web browser to log in as is customary in OIDC Authorization Code Flow. This may be
// convenient for users, especially for identities from your OIDC provider which are not intended to represent a human
// actor, such as service accounts performing actions in a CI/CD environment. Even if your OIDC provider supports it,
// you may wish to disable this behavior by setting this field to false when you prefer to only allow users of this
// OIDCIdentityProvider to log in via the browser-based OIDC Authorization Code Flow. Using the Resource Owner Password
// Credentials Grant means that the Pinniped CLI and Pinniped Supervisor will directly handle your end users' passwords
// (similar to LDAPIdentityProvider), and you will not be able to require multi-factor authentication or use the other
// web-based login features of your OIDC provider during Resource Owner Password Credentials Grant logins.
// AllowPasswordGrant defaults to false.
// +optional
AllowPasswordGrant bool `json:"allowPasswordGrant,omitempty"`
}
// OIDCClaims provides a mapping from upstream claims into identities.

View File

@@ -11,6 +11,196 @@ 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 *ActiveDirectoryIdentityProvider) DeepCopyInto(out *ActiveDirectoryIdentityProvider) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProvider.
func (in *ActiveDirectoryIdentityProvider) DeepCopy() *ActiveDirectoryIdentityProvider {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProvider)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ActiveDirectoryIdentityProvider) 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 *ActiveDirectoryIdentityProviderBind) DeepCopyInto(out *ActiveDirectoryIdentityProviderBind) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderBind.
func (in *ActiveDirectoryIdentityProviderBind) DeepCopy() *ActiveDirectoryIdentityProviderBind {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderBind)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderGroupSearch) DeepCopyInto(out *ActiveDirectoryIdentityProviderGroupSearch) {
*out = *in
out.Attributes = in.Attributes
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderGroupSearch.
func (in *ActiveDirectoryIdentityProviderGroupSearch) DeepCopy() *ActiveDirectoryIdentityProviderGroupSearch {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderGroupSearch)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderGroupSearchAttributes) DeepCopyInto(out *ActiveDirectoryIdentityProviderGroupSearchAttributes) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderGroupSearchAttributes.
func (in *ActiveDirectoryIdentityProviderGroupSearchAttributes) DeepCopy() *ActiveDirectoryIdentityProviderGroupSearchAttributes {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderGroupSearchAttributes)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderList) DeepCopyInto(out *ActiveDirectoryIdentityProviderList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ActiveDirectoryIdentityProvider, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderList.
func (in *ActiveDirectoryIdentityProviderList) DeepCopy() *ActiveDirectoryIdentityProviderList {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ActiveDirectoryIdentityProviderList) 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 *ActiveDirectoryIdentityProviderSpec) DeepCopyInto(out *ActiveDirectoryIdentityProviderSpec) {
*out = *in
if in.TLS != nil {
in, out := &in.TLS, &out.TLS
*out = new(TLSSpec)
**out = **in
}
out.Bind = in.Bind
out.UserSearch = in.UserSearch
out.GroupSearch = in.GroupSearch
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderSpec.
func (in *ActiveDirectoryIdentityProviderSpec) DeepCopy() *ActiveDirectoryIdentityProviderSpec {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderStatus) DeepCopyInto(out *ActiveDirectoryIdentityProviderStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderStatus.
func (in *ActiveDirectoryIdentityProviderStatus) DeepCopy() *ActiveDirectoryIdentityProviderStatus {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderUserSearch) DeepCopyInto(out *ActiveDirectoryIdentityProviderUserSearch) {
*out = *in
out.Attributes = in.Attributes
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderUserSearch.
func (in *ActiveDirectoryIdentityProviderUserSearch) DeepCopy() *ActiveDirectoryIdentityProviderUserSearch {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderUserSearch)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopyInto(out *ActiveDirectoryIdentityProviderUserSearchAttributes) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderUserSearchAttributes.
func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *ActiveDirectoryIdentityProviderUserSearchAttributes {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderUserSearchAttributes)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Condition) DeepCopyInto(out *Condition) {
*out = *in

View File

@@ -0,0 +1,66 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
// IDPType are the strings that can be returned by the Supervisor identity provider discovery endpoint
// as the "type" of each returned identity provider.
type IDPType string
// IDPFlow are the strings that can be returned by the Supervisor identity provider discovery endpoint
// in the array of allowed client "flows" for each returned identity provider.
type IDPFlow string
const (
IDPTypeOIDC IDPType = "oidc"
IDPTypeLDAP IDPType = "ldap"
IDPTypeActiveDirectory IDPType = "activedirectory"
IDPFlowCLIPassword IDPFlow = "cli_password"
IDPFlowBrowserAuthcode IDPFlow = "browser_authcode"
)
// Equals is a convenience function for comparing an IDPType to a string.
func (r IDPType) Equals(s string) bool {
return string(r) == s
}
// String is a convenience function to convert an IDPType to a string.
func (r IDPType) String() string {
return string(r)
}
// Equals is a convenience function for comparing an IDPFlow to a string.
func (r IDPFlow) Equals(s string) bool {
return string(r) == s
}
// String is a convenience function to convert an IDPFlow to a string.
func (r IDPFlow) String() string {
return string(r)
}
// OIDCDiscoveryResponse is part of the response from a FederationDomain's OpenID Provider Configuration
// Document returned by the .well-known/openid-configuration endpoint. It ignores all the standard OpenID Provider
// configuration metadata and only picks out the portion related to Supervisor identity provider discovery.
type OIDCDiscoveryResponse struct {
SupervisorDiscovery OIDCDiscoveryResponseIDPEndpoint `json:"discovery.supervisor.pinniped.dev/v1alpha1"`
}
// OIDCDiscoveryResponseIDPEndpoint contains the URL for the identity provider discovery endpoint.
type OIDCDiscoveryResponseIDPEndpoint struct {
PinnipedIDPsEndpoint string `json:"pinniped_identity_providers_endpoint"`
}
// IDPDiscoveryResponse is the response of a FederationDomain's identity provider discovery endpoint.
type IDPDiscoveryResponse struct {
PinnipedIDPs []PinnipedIDP `json:"pinniped_identity_providers"`
}
// PinnipedIDP describes a single identity provider as included in the response of a FederationDomain's
// identity provider discovery endpoint.
type PinnipedIDP struct {
Name string `json:"name"`
Type IDPType `json:"type"`
Flows []IDPFlow `json:"flows,omitempty"`
}

View File

@@ -0,0 +1,25 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package oidc
// Constants related to the Supervisor FederationDomain's authorization and token endpoints.
const (
// AuthorizeUsernameHeaderName is the name of the HTTP header which can be used to transmit a username
// to the authorize endpoint when using a password flow, for example an OIDCIdentityProvider with a password grant
// or an LDAPIdentityProvider.
AuthorizeUsernameHeaderName = "Pinniped-Username"
// AuthorizePasswordHeaderName is the name of the HTTP header which can be used to transmit a password
// to the authorize endpoint when using a password flow, for example an OIDCIdentityProvider with a password grant
// or an LDAPIdentityProvider.
AuthorizePasswordHeaderName = "Pinniped-Password" //nolint:gosec // this is not a credential
// AuthorizeUpstreamIDPNameParamName is the name of the HTTP request parameter which can be used to help select which
// identity provider should be used for authentication by sending the name of the desired identity provider.
AuthorizeUpstreamIDPNameParamName = "pinniped_idp_name"
// AuthorizeUpstreamIDPTypeParamName is the name of the HTTP request parameter which can be used to help select which
// identity provider should be used for authentication by sending the type of the desired identity provider.
AuthorizeUpstreamIDPTypeParamName = "pinniped_idp_type"
)

View File

@@ -0,0 +1,178 @@
// 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
import (
"time"
v1alpha1 "go.pinniped.dev/generated/1.17/apis/supervisor/idp/v1alpha1"
scheme "go.pinniped.dev/generated/1.17/client/supervisor/clientset/versioned/scheme"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
rest "k8s.io/client-go/rest"
)
// ActiveDirectoryIdentityProvidersGetter has a method to return a ActiveDirectoryIdentityProviderInterface.
// A group's client should implement this interface.
type ActiveDirectoryIdentityProvidersGetter interface {
ActiveDirectoryIdentityProviders(namespace string) ActiveDirectoryIdentityProviderInterface
}
// ActiveDirectoryIdentityProviderInterface has methods to work with ActiveDirectoryIdentityProvider resources.
type ActiveDirectoryIdentityProviderInterface interface {
Create(*v1alpha1.ActiveDirectoryIdentityProvider) (*v1alpha1.ActiveDirectoryIdentityProvider, error)
Update(*v1alpha1.ActiveDirectoryIdentityProvider) (*v1alpha1.ActiveDirectoryIdentityProvider, error)
UpdateStatus(*v1alpha1.ActiveDirectoryIdentityProvider) (*v1alpha1.ActiveDirectoryIdentityProvider, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v1alpha1.ActiveDirectoryIdentityProvider, error)
List(opts v1.ListOptions) (*v1alpha1.ActiveDirectoryIdentityProviderList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error)
ActiveDirectoryIdentityProviderExpansion
}
// activeDirectoryIdentityProviders implements ActiveDirectoryIdentityProviderInterface
type activeDirectoryIdentityProviders struct {
client rest.Interface
ns string
}
// newActiveDirectoryIdentityProviders returns a ActiveDirectoryIdentityProviders
func newActiveDirectoryIdentityProviders(c *IDPV1alpha1Client, namespace string) *activeDirectoryIdentityProviders {
return &activeDirectoryIdentityProviders{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the activeDirectoryIdentityProvider, and returns the corresponding activeDirectoryIdentityProvider object, and an error if there is any.
func (c *activeDirectoryIdentityProviders) Get(name string, options v1.GetOptions) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
result = &v1alpha1.ActiveDirectoryIdentityProvider{}
err = c.client.Get().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of ActiveDirectoryIdentityProviders that match those selectors.
func (c *activeDirectoryIdentityProviders) List(opts v1.ListOptions) (result *v1alpha1.ActiveDirectoryIdentityProviderList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1alpha1.ActiveDirectoryIdentityProviderList{}
err = c.client.Get().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested activeDirectoryIdentityProviders.
func (c *activeDirectoryIdentityProviders) Watch(opts v1.ListOptions) (watch.Interface, error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
opts.Watch = true
return c.client.Get().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch()
}
// Create takes the representation of a activeDirectoryIdentityProvider and creates it. Returns the server's representation of the activeDirectoryIdentityProvider, and an error, if there is any.
func (c *activeDirectoryIdentityProviders) Create(activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
result = &v1alpha1.ActiveDirectoryIdentityProvider{}
err = c.client.Post().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
Body(activeDirectoryIdentityProvider).
Do().
Into(result)
return
}
// Update takes the representation of a activeDirectoryIdentityProvider and updates it. Returns the server's representation of the activeDirectoryIdentityProvider, and an error, if there is any.
func (c *activeDirectoryIdentityProviders) Update(activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
result = &v1alpha1.ActiveDirectoryIdentityProvider{}
err = c.client.Put().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
Name(activeDirectoryIdentityProvider.Name).
Body(activeDirectoryIdentityProvider).
Do().
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *activeDirectoryIdentityProviders) UpdateStatus(activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
result = &v1alpha1.ActiveDirectoryIdentityProvider{}
err = c.client.Put().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
Name(activeDirectoryIdentityProvider.Name).
SubResource("status").
Body(activeDirectoryIdentityProvider).
Do().
Into(result)
return
}
// Delete takes name of the activeDirectoryIdentityProvider and deletes it. Returns an error if one occurs.
func (c *activeDirectoryIdentityProviders) Delete(name string, options *v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *activeDirectoryIdentityProviders) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
var timeout time.Duration
if listOptions.TimeoutSeconds != nil {
timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
VersionedParams(&listOptions, scheme.ParameterCodec).
Timeout(timeout).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched activeDirectoryIdentityProvider.
func (c *activeDirectoryIdentityProviders) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
result = &v1alpha1.ActiveDirectoryIdentityProvider{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}

View File

@@ -0,0 +1,127 @@
// 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/supervisor/idp/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakeActiveDirectoryIdentityProviders implements ActiveDirectoryIdentityProviderInterface
type FakeActiveDirectoryIdentityProviders struct {
Fake *FakeIDPV1alpha1
ns string
}
var activedirectoryidentityprovidersResource = schema.GroupVersionResource{Group: "idp.supervisor.pinniped.dev", Version: "v1alpha1", Resource: "activedirectoryidentityproviders"}
var activedirectoryidentityprovidersKind = schema.GroupVersionKind{Group: "idp.supervisor.pinniped.dev", Version: "v1alpha1", Kind: "ActiveDirectoryIdentityProvider"}
// Get takes name of the activeDirectoryIdentityProvider, and returns the corresponding activeDirectoryIdentityProvider object, and an error if there is any.
func (c *FakeActiveDirectoryIdentityProviders) Get(name string, options v1.GetOptions) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(activedirectoryidentityprovidersResource, c.ns, name), &v1alpha1.ActiveDirectoryIdentityProvider{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), err
}
// List takes label and field selectors, and returns the list of ActiveDirectoryIdentityProviders that match those selectors.
func (c *FakeActiveDirectoryIdentityProviders) List(opts v1.ListOptions) (result *v1alpha1.ActiveDirectoryIdentityProviderList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(activedirectoryidentityprovidersResource, activedirectoryidentityprovidersKind, c.ns, opts), &v1alpha1.ActiveDirectoryIdentityProviderList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1alpha1.ActiveDirectoryIdentityProviderList{ListMeta: obj.(*v1alpha1.ActiveDirectoryIdentityProviderList).ListMeta}
for _, item := range obj.(*v1alpha1.ActiveDirectoryIdentityProviderList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested activeDirectoryIdentityProviders.
func (c *FakeActiveDirectoryIdentityProviders) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(activedirectoryidentityprovidersResource, c.ns, opts))
}
// Create takes the representation of a activeDirectoryIdentityProvider and creates it. Returns the server's representation of the activeDirectoryIdentityProvider, and an error, if there is any.
func (c *FakeActiveDirectoryIdentityProviders) Create(activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(activedirectoryidentityprovidersResource, c.ns, activeDirectoryIdentityProvider), &v1alpha1.ActiveDirectoryIdentityProvider{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), err
}
// Update takes the representation of a activeDirectoryIdentityProvider and updates it. Returns the server's representation of the activeDirectoryIdentityProvider, and an error, if there is any.
func (c *FakeActiveDirectoryIdentityProviders) Update(activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(activedirectoryidentityprovidersResource, c.ns, activeDirectoryIdentityProvider), &v1alpha1.ActiveDirectoryIdentityProvider{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeActiveDirectoryIdentityProviders) UpdateStatus(activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider) (*v1alpha1.ActiveDirectoryIdentityProvider, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(activedirectoryidentityprovidersResource, "status", c.ns, activeDirectoryIdentityProvider), &v1alpha1.ActiveDirectoryIdentityProvider{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), err
}
// Delete takes name of the activeDirectoryIdentityProvider and deletes it. Returns an error if one occurs.
func (c *FakeActiveDirectoryIdentityProviders) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(activedirectoryidentityprovidersResource, c.ns, name), &v1alpha1.ActiveDirectoryIdentityProvider{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeActiveDirectoryIdentityProviders) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(activedirectoryidentityprovidersResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &v1alpha1.ActiveDirectoryIdentityProviderList{})
return err
}
// Patch applies the patch and returns the patched activeDirectoryIdentityProvider.
func (c *FakeActiveDirectoryIdentityProviders) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(activedirectoryidentityprovidersResource, c.ns, name, pt, data, subresources...), &v1alpha1.ActiveDirectoryIdentityProvider{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), err
}

View File

@@ -15,6 +15,10 @@ type FakeIDPV1alpha1 struct {
*testing.Fake
}
func (c *FakeIDPV1alpha1) ActiveDirectoryIdentityProviders(namespace string) v1alpha1.ActiveDirectoryIdentityProviderInterface {
return &FakeActiveDirectoryIdentityProviders{c, namespace}
}
func (c *FakeIDPV1alpha1) LDAPIdentityProviders(namespace string) v1alpha1.LDAPIdentityProviderInterface {
return &FakeLDAPIdentityProviders{c, namespace}
}

View File

@@ -5,6 +5,8 @@
package v1alpha1
type ActiveDirectoryIdentityProviderExpansion interface{}
type LDAPIdentityProviderExpansion interface{}
type OIDCIdentityProviderExpansion interface{}

View File

@@ -13,6 +13,7 @@ import (
type IDPV1alpha1Interface interface {
RESTClient() rest.Interface
ActiveDirectoryIdentityProvidersGetter
LDAPIdentityProvidersGetter
OIDCIdentityProvidersGetter
}
@@ -22,6 +23,10 @@ type IDPV1alpha1Client struct {
restClient rest.Interface
}
func (c *IDPV1alpha1Client) ActiveDirectoryIdentityProviders(namespace string) ActiveDirectoryIdentityProviderInterface {
return newActiveDirectoryIdentityProviders(c, namespace)
}
func (c *IDPV1alpha1Client) LDAPIdentityProviders(namespace string) LDAPIdentityProviderInterface {
return newLDAPIdentityProviders(c, namespace)
}

View File

@@ -45,6 +45,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
return &genericInformer{resource: resource.GroupResource(), informer: f.Config().V1alpha1().FederationDomains().Informer()}, nil
// Group=idp.supervisor.pinniped.dev, Version=v1alpha1
case idpv1alpha1.SchemeGroupVersion.WithResource("activedirectoryidentityproviders"):
return &genericInformer{resource: resource.GroupResource(), informer: f.IDP().V1alpha1().ActiveDirectoryIdentityProviders().Informer()}, nil
case idpv1alpha1.SchemeGroupVersion.WithResource("ldapidentityproviders"):
return &genericInformer{resource: resource.GroupResource(), informer: f.IDP().V1alpha1().LDAPIdentityProviders().Informer()}, nil
case idpv1alpha1.SchemeGroupVersion.WithResource("oidcidentityproviders"):

View File

@@ -0,0 +1,76 @@
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by informer-gen. DO NOT EDIT.
package v1alpha1
import (
time "time"
idpv1alpha1 "go.pinniped.dev/generated/1.17/apis/supervisor/idp/v1alpha1"
versioned "go.pinniped.dev/generated/1.17/client/supervisor/clientset/versioned"
internalinterfaces "go.pinniped.dev/generated/1.17/client/supervisor/informers/externalversions/internalinterfaces"
v1alpha1 "go.pinniped.dev/generated/1.17/client/supervisor/listers/idp/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
// ActiveDirectoryIdentityProviderInformer provides access to a shared informer and lister for
// ActiveDirectoryIdentityProviders.
type ActiveDirectoryIdentityProviderInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1alpha1.ActiveDirectoryIdentityProviderLister
}
type activeDirectoryIdentityProviderInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewActiveDirectoryIdentityProviderInformer constructs a new informer for ActiveDirectoryIdentityProvider type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewActiveDirectoryIdentityProviderInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredActiveDirectoryIdentityProviderInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredActiveDirectoryIdentityProviderInformer constructs a new informer for ActiveDirectoryIdentityProvider type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredActiveDirectoryIdentityProviderInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.IDPV1alpha1().ActiveDirectoryIdentityProviders(namespace).List(options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.IDPV1alpha1().ActiveDirectoryIdentityProviders(namespace).Watch(options)
},
},
&idpv1alpha1.ActiveDirectoryIdentityProvider{},
resyncPeriod,
indexers,
)
}
func (f *activeDirectoryIdentityProviderInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredActiveDirectoryIdentityProviderInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *activeDirectoryIdentityProviderInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&idpv1alpha1.ActiveDirectoryIdentityProvider{}, f.defaultInformer)
}
func (f *activeDirectoryIdentityProviderInformer) Lister() v1alpha1.ActiveDirectoryIdentityProviderLister {
return v1alpha1.NewActiveDirectoryIdentityProviderLister(f.Informer().GetIndexer())
}

View File

@@ -11,6 +11,8 @@ import (
// Interface provides access to all the informers in this group version.
type Interface interface {
// ActiveDirectoryIdentityProviders returns a ActiveDirectoryIdentityProviderInformer.
ActiveDirectoryIdentityProviders() ActiveDirectoryIdentityProviderInformer
// LDAPIdentityProviders returns a LDAPIdentityProviderInformer.
LDAPIdentityProviders() LDAPIdentityProviderInformer
// OIDCIdentityProviders returns a OIDCIdentityProviderInformer.
@@ -28,6 +30,11 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// ActiveDirectoryIdentityProviders returns a ActiveDirectoryIdentityProviderInformer.
func (v *version) ActiveDirectoryIdentityProviders() ActiveDirectoryIdentityProviderInformer {
return &activeDirectoryIdentityProviderInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// LDAPIdentityProviders returns a LDAPIdentityProviderInformer.
func (v *version) LDAPIdentityProviders() LDAPIdentityProviderInformer {
return &lDAPIdentityProviderInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}

View File

@@ -0,0 +1,81 @@
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by lister-gen. DO NOT EDIT.
package v1alpha1
import (
v1alpha1 "go.pinniped.dev/generated/1.17/apis/supervisor/idp/v1alpha1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// ActiveDirectoryIdentityProviderLister helps list ActiveDirectoryIdentityProviders.
type ActiveDirectoryIdentityProviderLister interface {
// List lists all ActiveDirectoryIdentityProviders in the indexer.
List(selector labels.Selector) (ret []*v1alpha1.ActiveDirectoryIdentityProvider, err error)
// ActiveDirectoryIdentityProviders returns an object that can list and get ActiveDirectoryIdentityProviders.
ActiveDirectoryIdentityProviders(namespace string) ActiveDirectoryIdentityProviderNamespaceLister
ActiveDirectoryIdentityProviderListerExpansion
}
// activeDirectoryIdentityProviderLister implements the ActiveDirectoryIdentityProviderLister interface.
type activeDirectoryIdentityProviderLister struct {
indexer cache.Indexer
}
// NewActiveDirectoryIdentityProviderLister returns a new ActiveDirectoryIdentityProviderLister.
func NewActiveDirectoryIdentityProviderLister(indexer cache.Indexer) ActiveDirectoryIdentityProviderLister {
return &activeDirectoryIdentityProviderLister{indexer: indexer}
}
// List lists all ActiveDirectoryIdentityProviders in the indexer.
func (s *activeDirectoryIdentityProviderLister) List(selector labels.Selector) (ret []*v1alpha1.ActiveDirectoryIdentityProvider, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1alpha1.ActiveDirectoryIdentityProvider))
})
return ret, err
}
// ActiveDirectoryIdentityProviders returns an object that can list and get ActiveDirectoryIdentityProviders.
func (s *activeDirectoryIdentityProviderLister) ActiveDirectoryIdentityProviders(namespace string) ActiveDirectoryIdentityProviderNamespaceLister {
return activeDirectoryIdentityProviderNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// ActiveDirectoryIdentityProviderNamespaceLister helps list and get ActiveDirectoryIdentityProviders.
type ActiveDirectoryIdentityProviderNamespaceLister interface {
// List lists all ActiveDirectoryIdentityProviders in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v1alpha1.ActiveDirectoryIdentityProvider, err error)
// Get retrieves the ActiveDirectoryIdentityProvider from the indexer for a given namespace and name.
Get(name string) (*v1alpha1.ActiveDirectoryIdentityProvider, error)
ActiveDirectoryIdentityProviderNamespaceListerExpansion
}
// activeDirectoryIdentityProviderNamespaceLister implements the ActiveDirectoryIdentityProviderNamespaceLister
// interface.
type activeDirectoryIdentityProviderNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all ActiveDirectoryIdentityProviders in the indexer for a given namespace.
func (s activeDirectoryIdentityProviderNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.ActiveDirectoryIdentityProvider, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1alpha1.ActiveDirectoryIdentityProvider))
})
return ret, err
}
// Get retrieves the ActiveDirectoryIdentityProvider from the indexer for a given namespace and name.
func (s activeDirectoryIdentityProviderNamespaceLister) Get(name string) (*v1alpha1.ActiveDirectoryIdentityProvider, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1alpha1.Resource("activedirectoryidentityprovider"), name)
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), nil
}

View File

@@ -5,6 +5,14 @@
package v1alpha1
// ActiveDirectoryIdentityProviderListerExpansion allows custom methods to be added to
// ActiveDirectoryIdentityProviderLister.
type ActiveDirectoryIdentityProviderListerExpansion interface{}
// ActiveDirectoryIdentityProviderNamespaceListerExpansion allows custom methods to be added to
// ActiveDirectoryIdentityProviderNamespaceLister.
type ActiveDirectoryIdentityProviderNamespaceListerExpansion interface{}
// LDAPIdentityProviderListerExpansion allows custom methods to be added to
// LDAPIdentityProviderLister.
type LDAPIdentityProviderListerExpansion interface{}

View File

@@ -47,7 +47,7 @@ spec:
description: "ExternalEndpoint describes the HTTPS endpoint where
the proxy will be exposed. If not set, the proxy will be served
using the external name of the LoadBalancer service or the cluster
service DNS name. \n This field must be non-empty when spec.impersonationProxy.service.mode
service DNS name. \n This field must be non-empty when spec.impersonationProxy.service.type
is \"None\"."
type: string
mode:

View File

@@ -0,0 +1,281 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.4.0
creationTimestamp: null
name: activedirectoryidentityproviders.idp.supervisor.pinniped.dev
spec:
group: idp.supervisor.pinniped.dev
names:
categories:
- pinniped
- pinniped-idp
- pinniped-idps
kind: ActiveDirectoryIdentityProvider
listKind: ActiveDirectoryIdentityProviderList
plural: activedirectoryidentityproviders
singular: activedirectoryidentityprovider
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .spec.host
name: Host
type: string
- jsonPath: .status.phase
name: Status
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1alpha1
schema:
openAPIV3Schema:
description: ActiveDirectoryIdentityProvider describes the configuration of
an upstream Microsoft Active Directory identity provider.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: Spec for configuring the identity provider.
properties:
bind:
description: Bind contains the configuration for how to provide access
credentials during an initial bind to the ActiveDirectory server
to be allowed to perform searches and binds to validate a user's
credentials during a user's authentication attempt.
properties:
secretName:
description: SecretName contains the name of a namespace-local
Secret object that provides the username and password for an
Active Directory bind user. This account will be used to perform
LDAP searches. The Secret should be of type "kubernetes.io/basic-auth"
which includes "username" and "password" keys. The username
value should be the full dn (distinguished name) of your bind
account, e.g. "cn=bind-account,ou=users,dc=example,dc=com".
The password must be non-empty.
minLength: 1
type: string
required:
- secretName
type: object
groupSearch:
description: GroupSearch contains the configuration for searching
for a user's group membership in ActiveDirectory.
properties:
attributes:
description: Attributes specifies how the group's information
should be read from each ActiveDirectory entry which was found
as the result of the group search.
properties:
groupName:
description: GroupName specifies the name of the attribute
in the Active Directory entries whose value shall become
a group name in the user's list of groups after a successful
authentication. The value of this field is case-sensitive
and must match the case of the attribute name returned by
the ActiveDirectory server in the user's entry. E.g. "cn"
for common name. Distinguished names can be used by specifying
lower-case "dn". Optional. When not specified, this defaults
to a custom field that looks like "sAMAccountName@domain",
where domain is constructed from the domain components of
the group DN.
type: string
type: object
base:
description: Base is the dn (distinguished name) that should be
used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com".
Optional, when not specified it will be based on the result
of a query for the defaultNamingContext (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse).
The default behavior searches your entire domain for groups.
It may make sense to specify a subtree as a search base if you
wish to exclude some groups for security reasons or to make
searches faster.
type: string
filter:
description: Filter is the ActiveDirectory search filter which
should be applied when searching for groups for a user. The
pattern "{}" must occur in the filter at least once and will
be dynamically replaced by the dn (distinguished name) of the
user entry found as a result of the user search. E.g. "member={}"
or "&(objectClass=groupOfNames)(member={})". For more information
about ActiveDirectory filters, see https://ldap.com/ldap-filters.
Note that the dn (distinguished name) is not an attribute of
an entry, so "dn={}" cannot be used. Optional. When not specified,
the default will act as if the filter were specified as "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={})".
This searches nested groups by default. Note that nested group
search can be slow for some Active Directory servers. To disable
it, you can set the filter to "(&(objectClass=group)(member={})"
type: string
type: object
host:
description: 'Host is the hostname of this Active Directory identity
provider, i.e., where to connect. For example: ldap.example.com:636.'
minLength: 1
type: string
tls:
description: TLS contains the connection settings for how to establish
the connection to the Host.
properties:
certificateAuthorityData:
description: X.509 Certificate Authority (base64-encoded PEM bundle).
If omitted, a default set of system roots will be trusted.
type: string
type: object
userSearch:
description: UserSearch contains the configuration for searching for
a user by name in Active Directory.
properties:
attributes:
description: Attributes specifies how the user's information should
be read from the ActiveDirectory entry which was found as the
result of the user search.
properties:
uid:
description: UID specifies the name of the attribute in the
ActiveDirectory entry which whose value shall be used to
uniquely identify the user within this ActiveDirectory provider
after a successful authentication. Optional, when empty
this defaults to "objectGUID".
type: string
username:
description: Username specifies the name of the attribute
in Active Directory entry whose value shall become the username
of the user after a successful authentication. Optional,
when empty this defaults to "userPrincipalName".
type: string
type: object
base:
description: Base is the dn (distinguished name) that should be
used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com".
Optional, when not specified it will be based on the result
of a query for the defaultNamingContext (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse).
The default behavior searches your entire domain for users.
It may make sense to specify a subtree as a search base if you
wish to exclude some users or to make searches faster.
type: string
filter:
description: Filter is the search filter which should be applied
when searching for users. The pattern "{}" must occur in the
filter at least once and will be dynamically replaced by the
username for which the search is being run. E.g. "mail={}" or
"&(objectClass=person)(uid={})". For more information about
LDAP filters, see https://ldap.com/ldap-filters. Note that the
dn (distinguished name) is not an attribute of an entry, so
"dn={}" cannot be used. Optional. When not specified, the default
will be '(&(objectClass=person)(!(objectClass=computer))(!(showInAdvancedViewOnly=TRUE))(|(sAMAccountName={}")(mail={})(userPrincipalName={})(sAMAccountType=805306368))'
This means that the user is a person, is not a computer, the
sAMAccountType is for a normal user account, and is not shown
in advanced view only (which would likely mean its a system
created service account with advanced permissions). Also, either
the sAMAccountName, the userPrincipalName, or the mail attribute
matches the input username.
type: string
type: object
required:
- host
type: object
status:
description: Status of the identity provider.
properties:
conditions:
description: Represents the observations of an identity provider's
current state.
items:
description: Condition status of a resource (mirrored from the metav1.Condition
type added in Kubernetes 1.19). In a future API version we can
switch to using the upstream type. See https://github.com/kubernetes/apimachinery/blob/v0.19.0/pkg/apis/meta/v1/types.go#L1353-L1413.
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
transitioned from one status to another. This should be when
the underlying condition changed. If that is not known, then
using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating
details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation
that the condition was set based upon. For instance, if .metadata.generation
is currently 12, but the .status.conditions[x].observedGeneration
is 9, the condition is out of date with respect to the current
state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating
the reason for the condition's last transition. Producers
of specific condition types may define expected values and
meanings for this field, and whether the values are considered
a guaranteed API. The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
--- Many .condition.type values are consistent across resources
like Available, but because arbitrary conditions can be useful
(see .node.status.conditions), the ability to deconflict is
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
x-kubernetes-list-map-keys:
- type
x-kubernetes-list-type: map
phase:
default: Pending
description: Phase summarizes the overall status of the ActiveDirectoryIdentityProvider.
enum:
- Pending
- Ready
- Error
type: string
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -59,11 +59,44 @@ spec:
additionalScopes:
description: AdditionalScopes are the scopes in addition to "openid"
that will be requested as part of the authorization request
flow with an OIDC identity provider. By default only the "openid"
scope will be requested.
flow with an OIDC identity provider. In the case of a Resource
Owner Password Credentials Grant flow, AdditionalScopes are
the scopes in addition to "openid" that will be requested as
part of the token request (see also the allowPasswordGrant field).
By default, only the "openid" scope will be requested.
items:
type: string
type: array
allowPasswordGrant:
description: AllowPasswordGrant, when true, will allow the use
of OAuth 2.0's Resource Owner Password Credentials Grant (see
https://datatracker.ietf.org/doc/html/rfc6749#section-4.3) to
authenticate to the OIDC provider using a username and password
without a web browser, in addition to the usual browser-based
OIDC Authorization Code Flow. The Resource Owner Password Credentials
Grant is not officially part of the OIDC specification, so it
may not be supported by your OIDC provider. If your OIDC provider
supports returning ID tokens from a Resource Owner Password
Credentials Grant token request, then you can choose to set
this field to true. This will allow end users to choose to present
their username and password to the kubectl CLI (using the Pinniped
plugin) to authenticate to the cluster, without using a web
browser to log in as is customary in OIDC Authorization Code
Flow. This may be convenient for users, especially for identities
from your OIDC provider which are not intended to represent
a human actor, such as service accounts performing actions in
a CI/CD environment. Even if your OIDC provider supports it,
you may wish to disable this behavior by setting this field
to false when you prefer to only allow users of this OIDCIdentityProvider
to log in via the browser-based OIDC Authorization Code Flow.
Using the Resource Owner Password Credentials Grant means that
the Pinniped CLI and Pinniped Supervisor will directly handle
your end users' passwords (similar to LDAPIdentityProvider),
and you will not be able to require multi-factor authentication
or use the other web-based login features of your OIDC provider
during Resource Owner Password Credentials Grant logins. AllowPasswordGrant
defaults to false.
type: boolean
type: object
claims:
description: Claims provides the names of token claims that will be

View File

@@ -411,7 +411,7 @@ ImpersonationProxyServiceSpec describes how the Concierge should provision a Ser
| *`mode`* __ImpersonationProxyMode__ | Mode configures whether the impersonation proxy should be started: - "disabled" explicitly disables the impersonation proxy. This is the default. - "enabled" explicitly enables the impersonation proxy. - "auto" enables or disables the impersonation proxy based upon the cluster in which it is running.
| *`service`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-impersonationproxyservicespec[$$ImpersonationProxyServiceSpec$$]__ | Service describes the configuration of the Service provisioned to expose the impersonation proxy to clients.
| *`externalEndpoint`* __string__ | ExternalEndpoint describes the HTTPS endpoint where the proxy will be exposed. If not set, the proxy will be served using the external name of the LoadBalancer service or the cluster service DNS name.
This field must be non-empty when spec.impersonationProxy.service.mode is "None".
This field must be non-empty when spec.impersonationProxy.service.type is "None".
|===
@@ -748,6 +748,157 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped supervisor identity pro
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovider"]
==== ActiveDirectoryIdentityProvider
ActiveDirectoryIdentityProvider describes the configuration of an upstream Microsoft Active Directory identity provider.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderlist[$$ActiveDirectoryIdentityProviderList$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec[$$ActiveDirectoryIdentityProviderSpec$$]__ | Spec for configuring the identity provider.
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderstatus[$$ActiveDirectoryIdentityProviderStatus$$]__ | Status of the identity provider.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderbind"]
==== ActiveDirectoryIdentityProviderBind
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec[$$ActiveDirectoryIdentityProviderSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`secretName`* __string__ | SecretName contains the name of a namespace-local Secret object that provides the username and password for an Active Directory bind user. This account will be used to perform LDAP searches. The Secret should be of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value should be the full dn (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". The password must be non-empty.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearch"]
==== ActiveDirectoryIdentityProviderGroupSearch
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec[$$ActiveDirectoryIdentityProviderSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`base`* __string__ | Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com". Optional, when not specified it will be based on the result of a query for the defaultNamingContext (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse). The default behavior searches your entire domain for groups. It may make sense to specify a subtree as a search base if you wish to exclude some groups for security reasons or to make searches faster.
| *`filter`* __string__ | Filter is the ActiveDirectory search filter which should be applied when searching for groups for a user. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})". For more information about ActiveDirectory filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the filter were specified as "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={})". This searches nested groups by default. Note that nested group search can be slow for some Active Directory servers. To disable it, you can set the filter to "(&(objectClass=group)(member={})"
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearchattributes[$$ActiveDirectoryIdentityProviderGroupSearchAttributes$$]__ | Attributes specifies how the group's information should be read from each ActiveDirectory entry which was found as the result of the group search.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearchattributes"]
==== ActiveDirectoryIdentityProviderGroupSearchAttributes
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearch[$$ActiveDirectoryIdentityProviderGroupSearch$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`groupName`* __string__ | GroupName specifies the name of the attribute in the Active Directory entries whose value shall become a group name in the user's list of groups after a successful authentication. The value of this field is case-sensitive and must match the case of the attribute name returned by the ActiveDirectory server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn". Optional. When not specified, this defaults to a custom field that looks like "sAMAccountName@domain", where domain is constructed from the domain components of the group DN.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec"]
==== ActiveDirectoryIdentityProviderSpec
Spec for configuring an ActiveDirectory identity provider.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovider[$$ActiveDirectoryIdentityProvider$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`host`* __string__ | Host is the hostname of this Active Directory identity provider, i.e., where to connect. For example: ldap.example.com:636.
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$]__ | TLS contains the connection settings for how to establish the connection to the Host.
| *`bind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderbind[$$ActiveDirectoryIdentityProviderBind$$]__ | Bind contains the configuration for how to provide access credentials during an initial bind to the ActiveDirectory server to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt.
| *`userSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderusersearch[$$ActiveDirectoryIdentityProviderUserSearch$$]__ | UserSearch contains the configuration for searching for a user by name in Active Directory.
| *`groupSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearch[$$ActiveDirectoryIdentityProviderGroupSearch$$]__ | GroupSearch contains the configuration for searching for a user's group membership in ActiveDirectory.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderstatus"]
==== ActiveDirectoryIdentityProviderStatus
Status of an Active Directory identity provider.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovider[$$ActiveDirectoryIdentityProvider$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`phase`* __ActiveDirectoryIdentityProviderPhase__ | Phase summarizes the overall status of the ActiveDirectoryIdentityProvider.
| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-condition[$$Condition$$] array__ | Represents the observations of an identity provider's current state.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderusersearch"]
==== ActiveDirectoryIdentityProviderUserSearch
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec[$$ActiveDirectoryIdentityProviderSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`base`* __string__ | Base is the dn (distinguished name) that should be used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com". Optional, when not specified it will be based on the result of a query for the defaultNamingContext (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse). The default behavior searches your entire domain for users. It may make sense to specify a subtree as a search base if you wish to exclude some users or to make searches faster.
| *`filter`* __string__ | Filter is the search filter which should be applied when searching for users. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the username for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will be '(&(objectClass=person)(!(objectClass=computer))(!(showInAdvancedViewOnly=TRUE))(|(sAMAccountName={}")(mail={})(userPrincipalName={})(sAMAccountType=805306368))' This means that the user is a person, is not a computer, the sAMAccountType is for a normal user account, and is not shown in advanced view only (which would likely mean its a system created service account with advanced permissions). Also, either the sAMAccountName, the userPrincipalName, or the mail attribute matches the input username.
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderusersearchattributes[$$ActiveDirectoryIdentityProviderUserSearchAttributes$$]__ | Attributes specifies how the user's information should be read from the ActiveDirectory entry which was found as the result of the user search.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderusersearchattributes"]
==== ActiveDirectoryIdentityProviderUserSearchAttributes
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderusersearch[$$ActiveDirectoryIdentityProviderUserSearch$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`username`* __string__ | Username specifies the name of the attribute in Active Directory entry whose value shall become the username of the user after a successful authentication. Optional, when empty this defaults to "userPrincipalName".
| *`uid`* __string__ | UID specifies the name of the attribute in the ActiveDirectory entry which whose value shall be used to uniquely identify the user within this ActiveDirectory provider after a successful authentication. Optional, when empty this defaults to "objectGUID".
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-condition"]
==== Condition
@@ -755,6 +906,7 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped supervisor identity pro
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderstatus[$$ActiveDirectoryIdentityProviderStatus$$]
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderstatus[$$LDAPIdentityProviderStatus$$]
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-oidcidentityproviderstatus[$$OIDCIdentityProviderStatus$$]
****
@@ -947,7 +1099,8 @@ OIDCAuthorizationConfig provides information about how to form the OAuth2 author
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`additionalScopes`* __string array__ | AdditionalScopes are the scopes in addition to "openid" that will be requested as part of the authorization request flow with an OIDC identity provider. By default only the "openid" scope will be requested.
| *`additionalScopes`* __string array__ | AdditionalScopes are the scopes in addition to "openid" that will be requested as part of the authorization request flow with an OIDC identity provider. In the case of a Resource Owner Password Credentials Grant flow, AdditionalScopes are the scopes in addition to "openid" that will be requested as part of the token request (see also the allowPasswordGrant field). By default, only the "openid" scope will be requested.
| *`allowPasswordGrant`* __boolean__ | AllowPasswordGrant, when true, will allow the use of OAuth 2.0's Resource Owner Password Credentials Grant (see https://datatracker.ietf.org/doc/html/rfc6749#section-4.3) to authenticate to the OIDC provider using a username and password without a web browser, in addition to the usual browser-based OIDC Authorization Code Flow. The Resource Owner Password Credentials Grant is not officially part of the OIDC specification, so it may not be supported by your OIDC provider. If your OIDC provider supports returning ID tokens from a Resource Owner Password Credentials Grant token request, then you can choose to set this field to true. This will allow end users to choose to present their username and password to the kubectl CLI (using the Pinniped plugin) to authenticate to the cluster, without using a web browser to log in as is customary in OIDC Authorization Code Flow. This may be convenient for users, especially for identities from your OIDC provider which are not intended to represent a human actor, such as service accounts performing actions in a CI/CD environment. Even if your OIDC provider supports it, you may wish to disable this behavior by setting this field to false when you prefer to only allow users of this OIDCIdentityProvider to log in via the browser-based OIDC Authorization Code Flow. Using the Resource Owner Password Credentials Grant means that the Pinniped CLI and Pinniped Supervisor will directly handle your end users' passwords (similar to LDAPIdentityProvider), and you will not be able to require multi-factor authentication or use the other web-based login features of your OIDC provider during Resource Owner Password Credentials Grant logins. AllowPasswordGrant defaults to false.
|===
@@ -1054,6 +1207,7 @@ Status of an OIDC identity provider.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec[$$ActiveDirectoryIdentityProviderSpec$$]
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec[$$LDAPIdentityProviderSpec$$]
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-oidcidentityproviderspec[$$OIDCIdentityProviderSpec$$]
****

View File

@@ -96,7 +96,7 @@ type ImpersonationProxySpec struct {
// ExternalEndpoint describes the HTTPS endpoint where the proxy will be exposed. If not set, the proxy will
// be served using the external name of the LoadBalancer service or the cluster service DNS name.
//
// This field must be non-empty when spec.impersonationProxy.service.mode is "None".
// This field must be non-empty when spec.impersonationProxy.service.type is "None".
//
// +optional
ExternalEndpoint string `json:"externalEndpoint,omitempty"`

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 v1alpha1
@@ -34,6 +34,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&OIDCIdentityProviderList{},
&LDAPIdentityProvider{},
&LDAPIdentityProviderList{},
&ActiveDirectoryIdentityProvider{},
&ActiveDirectoryIdentityProviderList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil

View File

@@ -0,0 +1,182 @@
// 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"
)
type ActiveDirectoryIdentityProviderPhase string
const (
// ActiveDirectoryPhasePending is the default phase for newly-created ActiveDirectoryIdentityProvider resources.
ActiveDirectoryPhasePending ActiveDirectoryIdentityProviderPhase = "Pending"
// ActiveDirectoryPhaseReady is the phase for an ActiveDirectoryIdentityProvider resource in a healthy state.
ActiveDirectoryPhaseReady ActiveDirectoryIdentityProviderPhase = "Ready"
// ActiveDirectoryPhaseError is the phase for an ActiveDirectoryIdentityProvider in an unhealthy state.
ActiveDirectoryPhaseError ActiveDirectoryIdentityProviderPhase = "Error"
)
// Status of an Active Directory identity provider.
type ActiveDirectoryIdentityProviderStatus struct {
// Phase summarizes the overall status of the ActiveDirectoryIdentityProvider.
// +kubebuilder:default=Pending
// +kubebuilder:validation:Enum=Pending;Ready;Error
Phase ActiveDirectoryIdentityProviderPhase `json:"phase,omitempty"`
// Represents the observations of an identity provider's current state.
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
}
type ActiveDirectoryIdentityProviderBind struct {
// SecretName contains the name of a namespace-local Secret object that provides the username and
// password for an Active Directory bind user. This account will be used to perform LDAP searches. The Secret should be
// of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value
// should be the full dn (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com".
// The password must be non-empty.
// +kubebuilder:validation:MinLength=1
SecretName string `json:"secretName"`
}
type ActiveDirectoryIdentityProviderUserSearchAttributes struct {
// Username specifies the name of the attribute in Active Directory entry whose value shall become the username
// of the user after a successful authentication.
// Optional, when empty this defaults to "userPrincipalName".
// +optional
Username string `json:"username,omitempty"`
// UID specifies the name of the attribute in the ActiveDirectory entry which whose value shall be used to uniquely
// identify the user within this ActiveDirectory provider after a successful authentication.
// Optional, when empty this defaults to "objectGUID".
// +optional
UID string `json:"uid,omitempty"`
}
type ActiveDirectoryIdentityProviderGroupSearchAttributes struct {
// GroupName specifies the name of the attribute in the Active Directory entries whose value shall become a group name
// in the user's list of groups after a successful authentication.
// The value of this field is case-sensitive and must match the case of the attribute name returned by the ActiveDirectory
// server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn".
// Optional. When not specified, this defaults to a custom field that looks like "sAMAccountName@domain",
// where domain is constructed from the domain components of the group DN.
// +optional
GroupName string `json:"groupName,omitempty"`
}
type ActiveDirectoryIdentityProviderUserSearch struct {
// Base is the dn (distinguished name) that should be used as the search base when searching for users.
// E.g. "ou=users,dc=example,dc=com".
// Optional, when not specified it will be based on the result of a query for the defaultNamingContext
// (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse).
// The default behavior searches your entire domain for users.
// It may make sense to specify a subtree as a search base if you wish to exclude some users
// or to make searches faster.
// +optional
Base string `json:"base,omitempty"`
// Filter is the search filter which should be applied when searching for users. The pattern "{}" must occur
// in the filter at least once and will be dynamically replaced by the username for which the search is being run.
// E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see
// https://ldap.com/ldap-filters.
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
// Optional. When not specified, the default will be
// '(&(objectClass=person)(!(objectClass=computer))(!(showInAdvancedViewOnly=TRUE))(|(sAMAccountName={}")(mail={})(userPrincipalName={})(sAMAccountType=805306368))'
// This means that the user is a person, is not a computer, the sAMAccountType is for a normal user account,
// and is not shown in advanced view only
// (which would likely mean its a system created service account with advanced permissions).
// Also, either the sAMAccountName, the userPrincipalName, or the mail attribute matches the input username.
// +optional
Filter string `json:"filter,omitempty"`
// Attributes specifies how the user's information should be read from the ActiveDirectory entry which was found as
// the result of the user search.
// +optional
Attributes ActiveDirectoryIdentityProviderUserSearchAttributes `json:"attributes,omitempty"`
}
type ActiveDirectoryIdentityProviderGroupSearch struct {
// Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g.
// "ou=groups,dc=example,dc=com".
// Optional, when not specified it will be based on the result of a query for the defaultNamingContext
// (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse).
// The default behavior searches your entire domain for groups.
// It may make sense to specify a subtree as a search base if you wish to exclude some groups
// for security reasons or to make searches faster.
// +optional
Base string `json:"base,omitempty"`
// Filter is the ActiveDirectory search filter which should be applied when searching for groups for a user.
// The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the
// dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or
// "&(objectClass=groupOfNames)(member={})". For more information about ActiveDirectory filters, see
// https://ldap.com/ldap-filters.
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
// Optional. When not specified, the default will act as if the filter were specified as
// "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={})".
// This searches nested groups by default.
// Note that nested group search can be slow for some Active Directory servers. To disable it,
// you can set the filter to
// "(&(objectClass=group)(member={})"
// +optional
Filter string `json:"filter,omitempty"`
// Attributes specifies how the group's information should be read from each ActiveDirectory entry which was found as
// the result of the group search.
// +optional
Attributes ActiveDirectoryIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"`
}
// Spec for configuring an ActiveDirectory identity provider.
type ActiveDirectoryIdentityProviderSpec struct {
// Host is the hostname of this Active Directory identity provider, i.e., where to connect. For example: ldap.example.com:636.
// +kubebuilder:validation:MinLength=1
Host string `json:"host"`
// TLS contains the connection settings for how to establish the connection to the Host.
TLS *TLSSpec `json:"tls,omitempty"`
// Bind contains the configuration for how to provide access credentials during an initial bind to the ActiveDirectory server
// to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt.
Bind ActiveDirectoryIdentityProviderBind `json:"bind,omitempty"`
// UserSearch contains the configuration for searching for a user by name in Active Directory.
UserSearch ActiveDirectoryIdentityProviderUserSearch `json:"userSearch,omitempty"`
// GroupSearch contains the configuration for searching for a user's group membership in ActiveDirectory.
GroupSearch ActiveDirectoryIdentityProviderGroupSearch `json:"groupSearch,omitempty"`
}
// ActiveDirectoryIdentityProvider describes the configuration of an upstream Microsoft Active Directory identity provider.
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:resource:categories=pinniped;pinniped-idp;pinniped-idps
// +kubebuilder:printcolumn:name="Host",type=string,JSONPath=`.spec.host`
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// +kubebuilder:subresource:status
type ActiveDirectoryIdentityProvider struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec for configuring the identity provider.
Spec ActiveDirectoryIdentityProviderSpec `json:"spec"`
// Status of the identity provider.
Status ActiveDirectoryIdentityProviderStatus `json:"status,omitempty"`
}
// List of ActiveDirectoryIdentityProvider objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ActiveDirectoryIdentityProviderList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ActiveDirectoryIdentityProvider `json:"items"`
}

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 v1alpha1

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 v1alpha1
@@ -39,9 +39,31 @@ type OIDCIdentityProviderStatus struct {
// request parameters.
type OIDCAuthorizationConfig struct {
// AdditionalScopes are the scopes in addition to "openid" that will be requested as part of the authorization
// request flow with an OIDC identity provider. By default only the "openid" scope will be requested.
// request flow with an OIDC identity provider.
// In the case of a Resource Owner Password Credentials Grant flow, AdditionalScopes are the scopes
// in addition to "openid" that will be requested as part of the token request (see also the allowPasswordGrant field).
// By default, only the "openid" scope will be requested.
// +optional
AdditionalScopes []string `json:"additionalScopes,omitempty"`
// AllowPasswordGrant, when true, will allow the use of OAuth 2.0's Resource Owner Password Credentials Grant
// (see https://datatracker.ietf.org/doc/html/rfc6749#section-4.3) to authenticate to the OIDC provider using a
// username and password without a web browser, in addition to the usual browser-based OIDC Authorization Code Flow.
// The Resource Owner Password Credentials Grant is not officially part of the OIDC specification, so it may not be
// supported by your OIDC provider. If your OIDC provider supports returning ID tokens from a Resource Owner Password
// Credentials Grant token request, then you can choose to set this field to true. This will allow end users to choose
// to present their username and password to the kubectl CLI (using the Pinniped plugin) to authenticate to the
// cluster, without using a web browser to log in as is customary in OIDC Authorization Code Flow. This may be
// convenient for users, especially for identities from your OIDC provider which are not intended to represent a human
// actor, such as service accounts performing actions in a CI/CD environment. Even if your OIDC provider supports it,
// you may wish to disable this behavior by setting this field to false when you prefer to only allow users of this
// OIDCIdentityProvider to log in via the browser-based OIDC Authorization Code Flow. Using the Resource Owner Password
// Credentials Grant means that the Pinniped CLI and Pinniped Supervisor will directly handle your end users' passwords
// (similar to LDAPIdentityProvider), and you will not be able to require multi-factor authentication or use the other
// web-based login features of your OIDC provider during Resource Owner Password Credentials Grant logins.
// AllowPasswordGrant defaults to false.
// +optional
AllowPasswordGrant bool `json:"allowPasswordGrant,omitempty"`
}
// OIDCClaims provides a mapping from upstream claims into identities.

View File

@@ -11,6 +11,196 @@ 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 *ActiveDirectoryIdentityProvider) DeepCopyInto(out *ActiveDirectoryIdentityProvider) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProvider.
func (in *ActiveDirectoryIdentityProvider) DeepCopy() *ActiveDirectoryIdentityProvider {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProvider)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ActiveDirectoryIdentityProvider) 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 *ActiveDirectoryIdentityProviderBind) DeepCopyInto(out *ActiveDirectoryIdentityProviderBind) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderBind.
func (in *ActiveDirectoryIdentityProviderBind) DeepCopy() *ActiveDirectoryIdentityProviderBind {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderBind)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderGroupSearch) DeepCopyInto(out *ActiveDirectoryIdentityProviderGroupSearch) {
*out = *in
out.Attributes = in.Attributes
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderGroupSearch.
func (in *ActiveDirectoryIdentityProviderGroupSearch) DeepCopy() *ActiveDirectoryIdentityProviderGroupSearch {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderGroupSearch)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderGroupSearchAttributes) DeepCopyInto(out *ActiveDirectoryIdentityProviderGroupSearchAttributes) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderGroupSearchAttributes.
func (in *ActiveDirectoryIdentityProviderGroupSearchAttributes) DeepCopy() *ActiveDirectoryIdentityProviderGroupSearchAttributes {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderGroupSearchAttributes)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderList) DeepCopyInto(out *ActiveDirectoryIdentityProviderList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ActiveDirectoryIdentityProvider, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderList.
func (in *ActiveDirectoryIdentityProviderList) DeepCopy() *ActiveDirectoryIdentityProviderList {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ActiveDirectoryIdentityProviderList) 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 *ActiveDirectoryIdentityProviderSpec) DeepCopyInto(out *ActiveDirectoryIdentityProviderSpec) {
*out = *in
if in.TLS != nil {
in, out := &in.TLS, &out.TLS
*out = new(TLSSpec)
**out = **in
}
out.Bind = in.Bind
out.UserSearch = in.UserSearch
out.GroupSearch = in.GroupSearch
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderSpec.
func (in *ActiveDirectoryIdentityProviderSpec) DeepCopy() *ActiveDirectoryIdentityProviderSpec {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderStatus) DeepCopyInto(out *ActiveDirectoryIdentityProviderStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderStatus.
func (in *ActiveDirectoryIdentityProviderStatus) DeepCopy() *ActiveDirectoryIdentityProviderStatus {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderUserSearch) DeepCopyInto(out *ActiveDirectoryIdentityProviderUserSearch) {
*out = *in
out.Attributes = in.Attributes
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderUserSearch.
func (in *ActiveDirectoryIdentityProviderUserSearch) DeepCopy() *ActiveDirectoryIdentityProviderUserSearch {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderUserSearch)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopyInto(out *ActiveDirectoryIdentityProviderUserSearchAttributes) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderUserSearchAttributes.
func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *ActiveDirectoryIdentityProviderUserSearchAttributes {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderUserSearchAttributes)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Condition) DeepCopyInto(out *Condition) {
*out = *in

View File

@@ -0,0 +1,66 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
// IDPType are the strings that can be returned by the Supervisor identity provider discovery endpoint
// as the "type" of each returned identity provider.
type IDPType string
// IDPFlow are the strings that can be returned by the Supervisor identity provider discovery endpoint
// in the array of allowed client "flows" for each returned identity provider.
type IDPFlow string
const (
IDPTypeOIDC IDPType = "oidc"
IDPTypeLDAP IDPType = "ldap"
IDPTypeActiveDirectory IDPType = "activedirectory"
IDPFlowCLIPassword IDPFlow = "cli_password"
IDPFlowBrowserAuthcode IDPFlow = "browser_authcode"
)
// Equals is a convenience function for comparing an IDPType to a string.
func (r IDPType) Equals(s string) bool {
return string(r) == s
}
// String is a convenience function to convert an IDPType to a string.
func (r IDPType) String() string {
return string(r)
}
// Equals is a convenience function for comparing an IDPFlow to a string.
func (r IDPFlow) Equals(s string) bool {
return string(r) == s
}
// String is a convenience function to convert an IDPFlow to a string.
func (r IDPFlow) String() string {
return string(r)
}
// OIDCDiscoveryResponse is part of the response from a FederationDomain's OpenID Provider Configuration
// Document returned by the .well-known/openid-configuration endpoint. It ignores all the standard OpenID Provider
// configuration metadata and only picks out the portion related to Supervisor identity provider discovery.
type OIDCDiscoveryResponse struct {
SupervisorDiscovery OIDCDiscoveryResponseIDPEndpoint `json:"discovery.supervisor.pinniped.dev/v1alpha1"`
}
// OIDCDiscoveryResponseIDPEndpoint contains the URL for the identity provider discovery endpoint.
type OIDCDiscoveryResponseIDPEndpoint struct {
PinnipedIDPsEndpoint string `json:"pinniped_identity_providers_endpoint"`
}
// IDPDiscoveryResponse is the response of a FederationDomain's identity provider discovery endpoint.
type IDPDiscoveryResponse struct {
PinnipedIDPs []PinnipedIDP `json:"pinniped_identity_providers"`
}
// PinnipedIDP describes a single identity provider as included in the response of a FederationDomain's
// identity provider discovery endpoint.
type PinnipedIDP struct {
Name string `json:"name"`
Type IDPType `json:"type"`
Flows []IDPFlow `json:"flows,omitempty"`
}

View File

@@ -0,0 +1,25 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package oidc
// Constants related to the Supervisor FederationDomain's authorization and token endpoints.
const (
// AuthorizeUsernameHeaderName is the name of the HTTP header which can be used to transmit a username
// to the authorize endpoint when using a password flow, for example an OIDCIdentityProvider with a password grant
// or an LDAPIdentityProvider.
AuthorizeUsernameHeaderName = "Pinniped-Username"
// AuthorizePasswordHeaderName is the name of the HTTP header which can be used to transmit a password
// to the authorize endpoint when using a password flow, for example an OIDCIdentityProvider with a password grant
// or an LDAPIdentityProvider.
AuthorizePasswordHeaderName = "Pinniped-Password" //nolint:gosec // this is not a credential
// AuthorizeUpstreamIDPNameParamName is the name of the HTTP request parameter which can be used to help select which
// identity provider should be used for authentication by sending the name of the desired identity provider.
AuthorizeUpstreamIDPNameParamName = "pinniped_idp_name"
// AuthorizeUpstreamIDPTypeParamName is the name of the HTTP request parameter which can be used to help select which
// identity provider should be used for authentication by sending the type of the desired identity provider.
AuthorizeUpstreamIDPTypeParamName = "pinniped_idp_type"
)

View File

@@ -0,0 +1,182 @@
// 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
import (
"context"
"time"
v1alpha1 "go.pinniped.dev/generated/1.18/apis/supervisor/idp/v1alpha1"
scheme "go.pinniped.dev/generated/1.18/client/supervisor/clientset/versioned/scheme"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
rest "k8s.io/client-go/rest"
)
// ActiveDirectoryIdentityProvidersGetter has a method to return a ActiveDirectoryIdentityProviderInterface.
// A group's client should implement this interface.
type ActiveDirectoryIdentityProvidersGetter interface {
ActiveDirectoryIdentityProviders(namespace string) ActiveDirectoryIdentityProviderInterface
}
// ActiveDirectoryIdentityProviderInterface has methods to work with ActiveDirectoryIdentityProvider resources.
type ActiveDirectoryIdentityProviderInterface interface {
Create(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.CreateOptions) (*v1alpha1.ActiveDirectoryIdentityProvider, error)
Update(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.UpdateOptions) (*v1alpha1.ActiveDirectoryIdentityProvider, error)
UpdateStatus(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.UpdateOptions) (*v1alpha1.ActiveDirectoryIdentityProvider, error)
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ActiveDirectoryIdentityProvider, error)
List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ActiveDirectoryIdentityProviderList, error)
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error)
ActiveDirectoryIdentityProviderExpansion
}
// activeDirectoryIdentityProviders implements ActiveDirectoryIdentityProviderInterface
type activeDirectoryIdentityProviders struct {
client rest.Interface
ns string
}
// newActiveDirectoryIdentityProviders returns a ActiveDirectoryIdentityProviders
func newActiveDirectoryIdentityProviders(c *IDPV1alpha1Client, namespace string) *activeDirectoryIdentityProviders {
return &activeDirectoryIdentityProviders{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the activeDirectoryIdentityProvider, and returns the corresponding activeDirectoryIdentityProvider object, and an error if there is any.
func (c *activeDirectoryIdentityProviders) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
result = &v1alpha1.ActiveDirectoryIdentityProvider{}
err = c.client.Get().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do(ctx).
Into(result)
return
}
// List takes label and field selectors, and returns the list of ActiveDirectoryIdentityProviders that match those selectors.
func (c *activeDirectoryIdentityProviders) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ActiveDirectoryIdentityProviderList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1alpha1.ActiveDirectoryIdentityProviderList{}
err = c.client.Get().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do(ctx).
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested activeDirectoryIdentityProviders.
func (c *activeDirectoryIdentityProviders) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
opts.Watch = true
return c.client.Get().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch(ctx)
}
// Create takes the representation of a activeDirectoryIdentityProvider and creates it. Returns the server's representation of the activeDirectoryIdentityProvider, and an error, if there is any.
func (c *activeDirectoryIdentityProviders) Create(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.CreateOptions) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
result = &v1alpha1.ActiveDirectoryIdentityProvider{}
err = c.client.Post().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
VersionedParams(&opts, scheme.ParameterCodec).
Body(activeDirectoryIdentityProvider).
Do(ctx).
Into(result)
return
}
// Update takes the representation of a activeDirectoryIdentityProvider and updates it. Returns the server's representation of the activeDirectoryIdentityProvider, and an error, if there is any.
func (c *activeDirectoryIdentityProviders) Update(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.UpdateOptions) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
result = &v1alpha1.ActiveDirectoryIdentityProvider{}
err = c.client.Put().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
Name(activeDirectoryIdentityProvider.Name).
VersionedParams(&opts, scheme.ParameterCodec).
Body(activeDirectoryIdentityProvider).
Do(ctx).
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *activeDirectoryIdentityProviders) UpdateStatus(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.UpdateOptions) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
result = &v1alpha1.ActiveDirectoryIdentityProvider{}
err = c.client.Put().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
Name(activeDirectoryIdentityProvider.Name).
SubResource("status").
VersionedParams(&opts, scheme.ParameterCodec).
Body(activeDirectoryIdentityProvider).
Do(ctx).
Into(result)
return
}
// Delete takes name of the activeDirectoryIdentityProvider and deletes it. Returns an error if one occurs.
func (c *activeDirectoryIdentityProviders) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
Name(name).
Body(&opts).
Do(ctx).
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *activeDirectoryIdentityProviders) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
var timeout time.Duration
if listOpts.TimeoutSeconds != nil {
timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
VersionedParams(&listOpts, scheme.ParameterCodec).
Timeout(timeout).
Body(&opts).
Do(ctx).
Error()
}
// Patch applies the patch and returns the patched activeDirectoryIdentityProvider.
func (c *activeDirectoryIdentityProviders) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
result = &v1alpha1.ActiveDirectoryIdentityProvider{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
Name(name).
SubResource(subresources...).
VersionedParams(&opts, scheme.ParameterCodec).
Body(data).
Do(ctx).
Into(result)
return
}

View File

@@ -0,0 +1,129 @@
// 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 (
"context"
v1alpha1 "go.pinniped.dev/generated/1.18/apis/supervisor/idp/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakeActiveDirectoryIdentityProviders implements ActiveDirectoryIdentityProviderInterface
type FakeActiveDirectoryIdentityProviders struct {
Fake *FakeIDPV1alpha1
ns string
}
var activedirectoryidentityprovidersResource = schema.GroupVersionResource{Group: "idp.supervisor.pinniped.dev", Version: "v1alpha1", Resource: "activedirectoryidentityproviders"}
var activedirectoryidentityprovidersKind = schema.GroupVersionKind{Group: "idp.supervisor.pinniped.dev", Version: "v1alpha1", Kind: "ActiveDirectoryIdentityProvider"}
// Get takes name of the activeDirectoryIdentityProvider, and returns the corresponding activeDirectoryIdentityProvider object, and an error if there is any.
func (c *FakeActiveDirectoryIdentityProviders) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(activedirectoryidentityprovidersResource, c.ns, name), &v1alpha1.ActiveDirectoryIdentityProvider{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), err
}
// List takes label and field selectors, and returns the list of ActiveDirectoryIdentityProviders that match those selectors.
func (c *FakeActiveDirectoryIdentityProviders) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ActiveDirectoryIdentityProviderList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(activedirectoryidentityprovidersResource, activedirectoryidentityprovidersKind, c.ns, opts), &v1alpha1.ActiveDirectoryIdentityProviderList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1alpha1.ActiveDirectoryIdentityProviderList{ListMeta: obj.(*v1alpha1.ActiveDirectoryIdentityProviderList).ListMeta}
for _, item := range obj.(*v1alpha1.ActiveDirectoryIdentityProviderList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested activeDirectoryIdentityProviders.
func (c *FakeActiveDirectoryIdentityProviders) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(activedirectoryidentityprovidersResource, c.ns, opts))
}
// Create takes the representation of a activeDirectoryIdentityProvider and creates it. Returns the server's representation of the activeDirectoryIdentityProvider, and an error, if there is any.
func (c *FakeActiveDirectoryIdentityProviders) Create(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.CreateOptions) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(activedirectoryidentityprovidersResource, c.ns, activeDirectoryIdentityProvider), &v1alpha1.ActiveDirectoryIdentityProvider{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), err
}
// Update takes the representation of a activeDirectoryIdentityProvider and updates it. Returns the server's representation of the activeDirectoryIdentityProvider, and an error, if there is any.
func (c *FakeActiveDirectoryIdentityProviders) Update(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.UpdateOptions) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(activedirectoryidentityprovidersResource, c.ns, activeDirectoryIdentityProvider), &v1alpha1.ActiveDirectoryIdentityProvider{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeActiveDirectoryIdentityProviders) UpdateStatus(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.UpdateOptions) (*v1alpha1.ActiveDirectoryIdentityProvider, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(activedirectoryidentityprovidersResource, "status", c.ns, activeDirectoryIdentityProvider), &v1alpha1.ActiveDirectoryIdentityProvider{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), err
}
// Delete takes name of the activeDirectoryIdentityProvider and deletes it. Returns an error if one occurs.
func (c *FakeActiveDirectoryIdentityProviders) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(activedirectoryidentityprovidersResource, c.ns, name), &v1alpha1.ActiveDirectoryIdentityProvider{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeActiveDirectoryIdentityProviders) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(activedirectoryidentityprovidersResource, c.ns, listOpts)
_, err := c.Fake.Invokes(action, &v1alpha1.ActiveDirectoryIdentityProviderList{})
return err
}
// Patch applies the patch and returns the patched activeDirectoryIdentityProvider.
func (c *FakeActiveDirectoryIdentityProviders) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(activedirectoryidentityprovidersResource, c.ns, name, pt, data, subresources...), &v1alpha1.ActiveDirectoryIdentityProvider{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), err
}

View File

@@ -15,6 +15,10 @@ type FakeIDPV1alpha1 struct {
*testing.Fake
}
func (c *FakeIDPV1alpha1) ActiveDirectoryIdentityProviders(namespace string) v1alpha1.ActiveDirectoryIdentityProviderInterface {
return &FakeActiveDirectoryIdentityProviders{c, namespace}
}
func (c *FakeIDPV1alpha1) LDAPIdentityProviders(namespace string) v1alpha1.LDAPIdentityProviderInterface {
return &FakeLDAPIdentityProviders{c, namespace}
}

View File

@@ -5,6 +5,8 @@
package v1alpha1
type ActiveDirectoryIdentityProviderExpansion interface{}
type LDAPIdentityProviderExpansion interface{}
type OIDCIdentityProviderExpansion interface{}

View File

@@ -13,6 +13,7 @@ import (
type IDPV1alpha1Interface interface {
RESTClient() rest.Interface
ActiveDirectoryIdentityProvidersGetter
LDAPIdentityProvidersGetter
OIDCIdentityProvidersGetter
}
@@ -22,6 +23,10 @@ type IDPV1alpha1Client struct {
restClient rest.Interface
}
func (c *IDPV1alpha1Client) ActiveDirectoryIdentityProviders(namespace string) ActiveDirectoryIdentityProviderInterface {
return newActiveDirectoryIdentityProviders(c, namespace)
}
func (c *IDPV1alpha1Client) LDAPIdentityProviders(namespace string) LDAPIdentityProviderInterface {
return newLDAPIdentityProviders(c, namespace)
}

View File

@@ -45,6 +45,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
return &genericInformer{resource: resource.GroupResource(), informer: f.Config().V1alpha1().FederationDomains().Informer()}, nil
// Group=idp.supervisor.pinniped.dev, Version=v1alpha1
case idpv1alpha1.SchemeGroupVersion.WithResource("activedirectoryidentityproviders"):
return &genericInformer{resource: resource.GroupResource(), informer: f.IDP().V1alpha1().ActiveDirectoryIdentityProviders().Informer()}, nil
case idpv1alpha1.SchemeGroupVersion.WithResource("ldapidentityproviders"):
return &genericInformer{resource: resource.GroupResource(), informer: f.IDP().V1alpha1().LDAPIdentityProviders().Informer()}, nil
case idpv1alpha1.SchemeGroupVersion.WithResource("oidcidentityproviders"):

View File

@@ -0,0 +1,77 @@
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by informer-gen. DO NOT EDIT.
package v1alpha1
import (
"context"
time "time"
idpv1alpha1 "go.pinniped.dev/generated/1.18/apis/supervisor/idp/v1alpha1"
versioned "go.pinniped.dev/generated/1.18/client/supervisor/clientset/versioned"
internalinterfaces "go.pinniped.dev/generated/1.18/client/supervisor/informers/externalversions/internalinterfaces"
v1alpha1 "go.pinniped.dev/generated/1.18/client/supervisor/listers/idp/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
// ActiveDirectoryIdentityProviderInformer provides access to a shared informer and lister for
// ActiveDirectoryIdentityProviders.
type ActiveDirectoryIdentityProviderInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1alpha1.ActiveDirectoryIdentityProviderLister
}
type activeDirectoryIdentityProviderInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewActiveDirectoryIdentityProviderInformer constructs a new informer for ActiveDirectoryIdentityProvider type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewActiveDirectoryIdentityProviderInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredActiveDirectoryIdentityProviderInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredActiveDirectoryIdentityProviderInformer constructs a new informer for ActiveDirectoryIdentityProvider type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredActiveDirectoryIdentityProviderInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.IDPV1alpha1().ActiveDirectoryIdentityProviders(namespace).List(context.TODO(), options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.IDPV1alpha1().ActiveDirectoryIdentityProviders(namespace).Watch(context.TODO(), options)
},
},
&idpv1alpha1.ActiveDirectoryIdentityProvider{},
resyncPeriod,
indexers,
)
}
func (f *activeDirectoryIdentityProviderInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredActiveDirectoryIdentityProviderInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *activeDirectoryIdentityProviderInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&idpv1alpha1.ActiveDirectoryIdentityProvider{}, f.defaultInformer)
}
func (f *activeDirectoryIdentityProviderInformer) Lister() v1alpha1.ActiveDirectoryIdentityProviderLister {
return v1alpha1.NewActiveDirectoryIdentityProviderLister(f.Informer().GetIndexer())
}

View File

@@ -11,6 +11,8 @@ import (
// Interface provides access to all the informers in this group version.
type Interface interface {
// ActiveDirectoryIdentityProviders returns a ActiveDirectoryIdentityProviderInformer.
ActiveDirectoryIdentityProviders() ActiveDirectoryIdentityProviderInformer
// LDAPIdentityProviders returns a LDAPIdentityProviderInformer.
LDAPIdentityProviders() LDAPIdentityProviderInformer
// OIDCIdentityProviders returns a OIDCIdentityProviderInformer.
@@ -28,6 +30,11 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// ActiveDirectoryIdentityProviders returns a ActiveDirectoryIdentityProviderInformer.
func (v *version) ActiveDirectoryIdentityProviders() ActiveDirectoryIdentityProviderInformer {
return &activeDirectoryIdentityProviderInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// LDAPIdentityProviders returns a LDAPIdentityProviderInformer.
func (v *version) LDAPIdentityProviders() LDAPIdentityProviderInformer {
return &lDAPIdentityProviderInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}

View File

@@ -0,0 +1,81 @@
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by lister-gen. DO NOT EDIT.
package v1alpha1
import (
v1alpha1 "go.pinniped.dev/generated/1.18/apis/supervisor/idp/v1alpha1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// ActiveDirectoryIdentityProviderLister helps list ActiveDirectoryIdentityProviders.
type ActiveDirectoryIdentityProviderLister interface {
// List lists all ActiveDirectoryIdentityProviders in the indexer.
List(selector labels.Selector) (ret []*v1alpha1.ActiveDirectoryIdentityProvider, err error)
// ActiveDirectoryIdentityProviders returns an object that can list and get ActiveDirectoryIdentityProviders.
ActiveDirectoryIdentityProviders(namespace string) ActiveDirectoryIdentityProviderNamespaceLister
ActiveDirectoryIdentityProviderListerExpansion
}
// activeDirectoryIdentityProviderLister implements the ActiveDirectoryIdentityProviderLister interface.
type activeDirectoryIdentityProviderLister struct {
indexer cache.Indexer
}
// NewActiveDirectoryIdentityProviderLister returns a new ActiveDirectoryIdentityProviderLister.
func NewActiveDirectoryIdentityProviderLister(indexer cache.Indexer) ActiveDirectoryIdentityProviderLister {
return &activeDirectoryIdentityProviderLister{indexer: indexer}
}
// List lists all ActiveDirectoryIdentityProviders in the indexer.
func (s *activeDirectoryIdentityProviderLister) List(selector labels.Selector) (ret []*v1alpha1.ActiveDirectoryIdentityProvider, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1alpha1.ActiveDirectoryIdentityProvider))
})
return ret, err
}
// ActiveDirectoryIdentityProviders returns an object that can list and get ActiveDirectoryIdentityProviders.
func (s *activeDirectoryIdentityProviderLister) ActiveDirectoryIdentityProviders(namespace string) ActiveDirectoryIdentityProviderNamespaceLister {
return activeDirectoryIdentityProviderNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// ActiveDirectoryIdentityProviderNamespaceLister helps list and get ActiveDirectoryIdentityProviders.
type ActiveDirectoryIdentityProviderNamespaceLister interface {
// List lists all ActiveDirectoryIdentityProviders in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v1alpha1.ActiveDirectoryIdentityProvider, err error)
// Get retrieves the ActiveDirectoryIdentityProvider from the indexer for a given namespace and name.
Get(name string) (*v1alpha1.ActiveDirectoryIdentityProvider, error)
ActiveDirectoryIdentityProviderNamespaceListerExpansion
}
// activeDirectoryIdentityProviderNamespaceLister implements the ActiveDirectoryIdentityProviderNamespaceLister
// interface.
type activeDirectoryIdentityProviderNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all ActiveDirectoryIdentityProviders in the indexer for a given namespace.
func (s activeDirectoryIdentityProviderNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.ActiveDirectoryIdentityProvider, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1alpha1.ActiveDirectoryIdentityProvider))
})
return ret, err
}
// Get retrieves the ActiveDirectoryIdentityProvider from the indexer for a given namespace and name.
func (s activeDirectoryIdentityProviderNamespaceLister) Get(name string) (*v1alpha1.ActiveDirectoryIdentityProvider, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1alpha1.Resource("activedirectoryidentityprovider"), name)
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), nil
}

View File

@@ -5,6 +5,14 @@
package v1alpha1
// ActiveDirectoryIdentityProviderListerExpansion allows custom methods to be added to
// ActiveDirectoryIdentityProviderLister.
type ActiveDirectoryIdentityProviderListerExpansion interface{}
// ActiveDirectoryIdentityProviderNamespaceListerExpansion allows custom methods to be added to
// ActiveDirectoryIdentityProviderNamespaceLister.
type ActiveDirectoryIdentityProviderNamespaceListerExpansion interface{}
// LDAPIdentityProviderListerExpansion allows custom methods to be added to
// LDAPIdentityProviderLister.
type LDAPIdentityProviderListerExpansion interface{}

View File

@@ -47,7 +47,7 @@ spec:
description: "ExternalEndpoint describes the HTTPS endpoint where
the proxy will be exposed. If not set, the proxy will be served
using the external name of the LoadBalancer service or the cluster
service DNS name. \n This field must be non-empty when spec.impersonationProxy.service.mode
service DNS name. \n This field must be non-empty when spec.impersonationProxy.service.type
is \"None\"."
type: string
mode:

View File

@@ -0,0 +1,281 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.4.0
creationTimestamp: null
name: activedirectoryidentityproviders.idp.supervisor.pinniped.dev
spec:
group: idp.supervisor.pinniped.dev
names:
categories:
- pinniped
- pinniped-idp
- pinniped-idps
kind: ActiveDirectoryIdentityProvider
listKind: ActiveDirectoryIdentityProviderList
plural: activedirectoryidentityproviders
singular: activedirectoryidentityprovider
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .spec.host
name: Host
type: string
- jsonPath: .status.phase
name: Status
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1alpha1
schema:
openAPIV3Schema:
description: ActiveDirectoryIdentityProvider describes the configuration of
an upstream Microsoft Active Directory identity provider.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: Spec for configuring the identity provider.
properties:
bind:
description: Bind contains the configuration for how to provide access
credentials during an initial bind to the ActiveDirectory server
to be allowed to perform searches and binds to validate a user's
credentials during a user's authentication attempt.
properties:
secretName:
description: SecretName contains the name of a namespace-local
Secret object that provides the username and password for an
Active Directory bind user. This account will be used to perform
LDAP searches. The Secret should be of type "kubernetes.io/basic-auth"
which includes "username" and "password" keys. The username
value should be the full dn (distinguished name) of your bind
account, e.g. "cn=bind-account,ou=users,dc=example,dc=com".
The password must be non-empty.
minLength: 1
type: string
required:
- secretName
type: object
groupSearch:
description: GroupSearch contains the configuration for searching
for a user's group membership in ActiveDirectory.
properties:
attributes:
description: Attributes specifies how the group's information
should be read from each ActiveDirectory entry which was found
as the result of the group search.
properties:
groupName:
description: GroupName specifies the name of the attribute
in the Active Directory entries whose value shall become
a group name in the user's list of groups after a successful
authentication. The value of this field is case-sensitive
and must match the case of the attribute name returned by
the ActiveDirectory server in the user's entry. E.g. "cn"
for common name. Distinguished names can be used by specifying
lower-case "dn". Optional. When not specified, this defaults
to a custom field that looks like "sAMAccountName@domain",
where domain is constructed from the domain components of
the group DN.
type: string
type: object
base:
description: Base is the dn (distinguished name) that should be
used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com".
Optional, when not specified it will be based on the result
of a query for the defaultNamingContext (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse).
The default behavior searches your entire domain for groups.
It may make sense to specify a subtree as a search base if you
wish to exclude some groups for security reasons or to make
searches faster.
type: string
filter:
description: Filter is the ActiveDirectory search filter which
should be applied when searching for groups for a user. The
pattern "{}" must occur in the filter at least once and will
be dynamically replaced by the dn (distinguished name) of the
user entry found as a result of the user search. E.g. "member={}"
or "&(objectClass=groupOfNames)(member={})". For more information
about ActiveDirectory filters, see https://ldap.com/ldap-filters.
Note that the dn (distinguished name) is not an attribute of
an entry, so "dn={}" cannot be used. Optional. When not specified,
the default will act as if the filter were specified as "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={})".
This searches nested groups by default. Note that nested group
search can be slow for some Active Directory servers. To disable
it, you can set the filter to "(&(objectClass=group)(member={})"
type: string
type: object
host:
description: 'Host is the hostname of this Active Directory identity
provider, i.e., where to connect. For example: ldap.example.com:636.'
minLength: 1
type: string
tls:
description: TLS contains the connection settings for how to establish
the connection to the Host.
properties:
certificateAuthorityData:
description: X.509 Certificate Authority (base64-encoded PEM bundle).
If omitted, a default set of system roots will be trusted.
type: string
type: object
userSearch:
description: UserSearch contains the configuration for searching for
a user by name in Active Directory.
properties:
attributes:
description: Attributes specifies how the user's information should
be read from the ActiveDirectory entry which was found as the
result of the user search.
properties:
uid:
description: UID specifies the name of the attribute in the
ActiveDirectory entry which whose value shall be used to
uniquely identify the user within this ActiveDirectory provider
after a successful authentication. Optional, when empty
this defaults to "objectGUID".
type: string
username:
description: Username specifies the name of the attribute
in Active Directory entry whose value shall become the username
of the user after a successful authentication. Optional,
when empty this defaults to "userPrincipalName".
type: string
type: object
base:
description: Base is the dn (distinguished name) that should be
used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com".
Optional, when not specified it will be based on the result
of a query for the defaultNamingContext (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse).
The default behavior searches your entire domain for users.
It may make sense to specify a subtree as a search base if you
wish to exclude some users or to make searches faster.
type: string
filter:
description: Filter is the search filter which should be applied
when searching for users. The pattern "{}" must occur in the
filter at least once and will be dynamically replaced by the
username for which the search is being run. E.g. "mail={}" or
"&(objectClass=person)(uid={})". For more information about
LDAP filters, see https://ldap.com/ldap-filters. Note that the
dn (distinguished name) is not an attribute of an entry, so
"dn={}" cannot be used. Optional. When not specified, the default
will be '(&(objectClass=person)(!(objectClass=computer))(!(showInAdvancedViewOnly=TRUE))(|(sAMAccountName={}")(mail={})(userPrincipalName={})(sAMAccountType=805306368))'
This means that the user is a person, is not a computer, the
sAMAccountType is for a normal user account, and is not shown
in advanced view only (which would likely mean its a system
created service account with advanced permissions). Also, either
the sAMAccountName, the userPrincipalName, or the mail attribute
matches the input username.
type: string
type: object
required:
- host
type: object
status:
description: Status of the identity provider.
properties:
conditions:
description: Represents the observations of an identity provider's
current state.
items:
description: Condition status of a resource (mirrored from the metav1.Condition
type added in Kubernetes 1.19). In a future API version we can
switch to using the upstream type. See https://github.com/kubernetes/apimachinery/blob/v0.19.0/pkg/apis/meta/v1/types.go#L1353-L1413.
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
transitioned from one status to another. This should be when
the underlying condition changed. If that is not known, then
using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating
details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation
that the condition was set based upon. For instance, if .metadata.generation
is currently 12, but the .status.conditions[x].observedGeneration
is 9, the condition is out of date with respect to the current
state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating
the reason for the condition's last transition. Producers
of specific condition types may define expected values and
meanings for this field, and whether the values are considered
a guaranteed API. The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
--- Many .condition.type values are consistent across resources
like Available, but because arbitrary conditions can be useful
(see .node.status.conditions), the ability to deconflict is
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
x-kubernetes-list-map-keys:
- type
x-kubernetes-list-type: map
phase:
default: Pending
description: Phase summarizes the overall status of the ActiveDirectoryIdentityProvider.
enum:
- Pending
- Ready
- Error
type: string
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -59,11 +59,44 @@ spec:
additionalScopes:
description: AdditionalScopes are the scopes in addition to "openid"
that will be requested as part of the authorization request
flow with an OIDC identity provider. By default only the "openid"
scope will be requested.
flow with an OIDC identity provider. In the case of a Resource
Owner Password Credentials Grant flow, AdditionalScopes are
the scopes in addition to "openid" that will be requested as
part of the token request (see also the allowPasswordGrant field).
By default, only the "openid" scope will be requested.
items:
type: string
type: array
allowPasswordGrant:
description: AllowPasswordGrant, when true, will allow the use
of OAuth 2.0's Resource Owner Password Credentials Grant (see
https://datatracker.ietf.org/doc/html/rfc6749#section-4.3) to
authenticate to the OIDC provider using a username and password
without a web browser, in addition to the usual browser-based
OIDC Authorization Code Flow. The Resource Owner Password Credentials
Grant is not officially part of the OIDC specification, so it
may not be supported by your OIDC provider. If your OIDC provider
supports returning ID tokens from a Resource Owner Password
Credentials Grant token request, then you can choose to set
this field to true. This will allow end users to choose to present
their username and password to the kubectl CLI (using the Pinniped
plugin) to authenticate to the cluster, without using a web
browser to log in as is customary in OIDC Authorization Code
Flow. This may be convenient for users, especially for identities
from your OIDC provider which are not intended to represent
a human actor, such as service accounts performing actions in
a CI/CD environment. Even if your OIDC provider supports it,
you may wish to disable this behavior by setting this field
to false when you prefer to only allow users of this OIDCIdentityProvider
to log in via the browser-based OIDC Authorization Code Flow.
Using the Resource Owner Password Credentials Grant means that
the Pinniped CLI and Pinniped Supervisor will directly handle
your end users' passwords (similar to LDAPIdentityProvider),
and you will not be able to require multi-factor authentication
or use the other web-based login features of your OIDC provider
during Resource Owner Password Credentials Grant logins. AllowPasswordGrant
defaults to false.
type: boolean
type: object
claims:
description: Claims provides the names of token claims that will be

View File

@@ -411,7 +411,7 @@ ImpersonationProxyServiceSpec describes how the Concierge should provision a Ser
| *`mode`* __ImpersonationProxyMode__ | Mode configures whether the impersonation proxy should be started: - "disabled" explicitly disables the impersonation proxy. This is the default. - "enabled" explicitly enables the impersonation proxy. - "auto" enables or disables the impersonation proxy based upon the cluster in which it is running.
| *`service`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-impersonationproxyservicespec[$$ImpersonationProxyServiceSpec$$]__ | Service describes the configuration of the Service provisioned to expose the impersonation proxy to clients.
| *`externalEndpoint`* __string__ | ExternalEndpoint describes the HTTPS endpoint where the proxy will be exposed. If not set, the proxy will be served using the external name of the LoadBalancer service or the cluster service DNS name.
This field must be non-empty when spec.impersonationProxy.service.mode is "None".
This field must be non-empty when spec.impersonationProxy.service.type is "None".
|===
@@ -748,6 +748,157 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped supervisor identity pro
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovider"]
==== ActiveDirectoryIdentityProvider
ActiveDirectoryIdentityProvider describes the configuration of an upstream Microsoft Active Directory identity provider.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderlist[$$ActiveDirectoryIdentityProviderList$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec[$$ActiveDirectoryIdentityProviderSpec$$]__ | Spec for configuring the identity provider.
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderstatus[$$ActiveDirectoryIdentityProviderStatus$$]__ | Status of the identity provider.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderbind"]
==== ActiveDirectoryIdentityProviderBind
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec[$$ActiveDirectoryIdentityProviderSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`secretName`* __string__ | SecretName contains the name of a namespace-local Secret object that provides the username and password for an Active Directory bind user. This account will be used to perform LDAP searches. The Secret should be of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value should be the full dn (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". The password must be non-empty.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearch"]
==== ActiveDirectoryIdentityProviderGroupSearch
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec[$$ActiveDirectoryIdentityProviderSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`base`* __string__ | Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com". Optional, when not specified it will be based on the result of a query for the defaultNamingContext (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse). The default behavior searches your entire domain for groups. It may make sense to specify a subtree as a search base if you wish to exclude some groups for security reasons or to make searches faster.
| *`filter`* __string__ | Filter is the ActiveDirectory search filter which should be applied when searching for groups for a user. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})". For more information about ActiveDirectory filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the filter were specified as "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={})". This searches nested groups by default. Note that nested group search can be slow for some Active Directory servers. To disable it, you can set the filter to "(&(objectClass=group)(member={})"
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearchattributes[$$ActiveDirectoryIdentityProviderGroupSearchAttributes$$]__ | Attributes specifies how the group's information should be read from each ActiveDirectory entry which was found as the result of the group search.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearchattributes"]
==== ActiveDirectoryIdentityProviderGroupSearchAttributes
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearch[$$ActiveDirectoryIdentityProviderGroupSearch$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`groupName`* __string__ | GroupName specifies the name of the attribute in the Active Directory entries whose value shall become a group name in the user's list of groups after a successful authentication. The value of this field is case-sensitive and must match the case of the attribute name returned by the ActiveDirectory server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn". Optional. When not specified, this defaults to a custom field that looks like "sAMAccountName@domain", where domain is constructed from the domain components of the group DN.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec"]
==== ActiveDirectoryIdentityProviderSpec
Spec for configuring an ActiveDirectory identity provider.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovider[$$ActiveDirectoryIdentityProvider$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`host`* __string__ | Host is the hostname of this Active Directory identity provider, i.e., where to connect. For example: ldap.example.com:636.
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$]__ | TLS contains the connection settings for how to establish the connection to the Host.
| *`bind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderbind[$$ActiveDirectoryIdentityProviderBind$$]__ | Bind contains the configuration for how to provide access credentials during an initial bind to the ActiveDirectory server to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt.
| *`userSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderusersearch[$$ActiveDirectoryIdentityProviderUserSearch$$]__ | UserSearch contains the configuration for searching for a user by name in Active Directory.
| *`groupSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovidergroupsearch[$$ActiveDirectoryIdentityProviderGroupSearch$$]__ | GroupSearch contains the configuration for searching for a user's group membership in ActiveDirectory.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderstatus"]
==== ActiveDirectoryIdentityProviderStatus
Status of an Active Directory identity provider.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityprovider[$$ActiveDirectoryIdentityProvider$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`phase`* __ActiveDirectoryIdentityProviderPhase__ | Phase summarizes the overall status of the ActiveDirectoryIdentityProvider.
| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-condition[$$Condition$$] array__ | Represents the observations of an identity provider's current state.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderusersearch"]
==== ActiveDirectoryIdentityProviderUserSearch
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec[$$ActiveDirectoryIdentityProviderSpec$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`base`* __string__ | Base is the dn (distinguished name) that should be used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com". Optional, when not specified it will be based on the result of a query for the defaultNamingContext (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse). The default behavior searches your entire domain for users. It may make sense to specify a subtree as a search base if you wish to exclude some users or to make searches faster.
| *`filter`* __string__ | Filter is the search filter which should be applied when searching for users. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the username for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will be '(&(objectClass=person)(!(objectClass=computer))(!(showInAdvancedViewOnly=TRUE))(|(sAMAccountName={}")(mail={})(userPrincipalName={})(sAMAccountType=805306368))' This means that the user is a person, is not a computer, the sAMAccountType is for a normal user account, and is not shown in advanced view only (which would likely mean its a system created service account with advanced permissions). Also, either the sAMAccountName, the userPrincipalName, or the mail attribute matches the input username.
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderusersearchattributes[$$ActiveDirectoryIdentityProviderUserSearchAttributes$$]__ | Attributes specifies how the user's information should be read from the ActiveDirectory entry which was found as the result of the user search.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderusersearchattributes"]
==== ActiveDirectoryIdentityProviderUserSearchAttributes
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderusersearch[$$ActiveDirectoryIdentityProviderUserSearch$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`username`* __string__ | Username specifies the name of the attribute in Active Directory entry whose value shall become the username of the user after a successful authentication. Optional, when empty this defaults to "userPrincipalName".
| *`uid`* __string__ | UID specifies the name of the attribute in the ActiveDirectory entry which whose value shall be used to uniquely identify the user within this ActiveDirectory provider after a successful authentication. Optional, when empty this defaults to "objectGUID".
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-condition"]
==== Condition
@@ -755,6 +906,7 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped supervisor identity pro
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderstatus[$$ActiveDirectoryIdentityProviderStatus$$]
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderstatus[$$LDAPIdentityProviderStatus$$]
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-oidcidentityproviderstatus[$$OIDCIdentityProviderStatus$$]
****
@@ -947,7 +1099,8 @@ OIDCAuthorizationConfig provides information about how to form the OAuth2 author
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`additionalScopes`* __string array__ | AdditionalScopes are the scopes in addition to "openid" that will be requested as part of the authorization request flow with an OIDC identity provider. By default only the "openid" scope will be requested.
| *`additionalScopes`* __string array__ | AdditionalScopes are the scopes in addition to "openid" that will be requested as part of the authorization request flow with an OIDC identity provider. In the case of a Resource Owner Password Credentials Grant flow, AdditionalScopes are the scopes in addition to "openid" that will be requested as part of the token request (see also the allowPasswordGrant field). By default, only the "openid" scope will be requested.
| *`allowPasswordGrant`* __boolean__ | AllowPasswordGrant, when true, will allow the use of OAuth 2.0's Resource Owner Password Credentials Grant (see https://datatracker.ietf.org/doc/html/rfc6749#section-4.3) to authenticate to the OIDC provider using a username and password without a web browser, in addition to the usual browser-based OIDC Authorization Code Flow. The Resource Owner Password Credentials Grant is not officially part of the OIDC specification, so it may not be supported by your OIDC provider. If your OIDC provider supports returning ID tokens from a Resource Owner Password Credentials Grant token request, then you can choose to set this field to true. This will allow end users to choose to present their username and password to the kubectl CLI (using the Pinniped plugin) to authenticate to the cluster, without using a web browser to log in as is customary in OIDC Authorization Code Flow. This may be convenient for users, especially for identities from your OIDC provider which are not intended to represent a human actor, such as service accounts performing actions in a CI/CD environment. Even if your OIDC provider supports it, you may wish to disable this behavior by setting this field to false when you prefer to only allow users of this OIDCIdentityProvider to log in via the browser-based OIDC Authorization Code Flow. Using the Resource Owner Password Credentials Grant means that the Pinniped CLI and Pinniped Supervisor will directly handle your end users' passwords (similar to LDAPIdentityProvider), and you will not be able to require multi-factor authentication or use the other web-based login features of your OIDC provider during Resource Owner Password Credentials Grant logins. AllowPasswordGrant defaults to false.
|===
@@ -1054,6 +1207,7 @@ Status of an OIDC identity provider.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-activedirectoryidentityproviderspec[$$ActiveDirectoryIdentityProviderSpec$$]
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec[$$LDAPIdentityProviderSpec$$]
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-oidcidentityproviderspec[$$OIDCIdentityProviderSpec$$]
****

View File

@@ -96,7 +96,7 @@ type ImpersonationProxySpec struct {
// ExternalEndpoint describes the HTTPS endpoint where the proxy will be exposed. If not set, the proxy will
// be served using the external name of the LoadBalancer service or the cluster service DNS name.
//
// This field must be non-empty when spec.impersonationProxy.service.mode is "None".
// This field must be non-empty when spec.impersonationProxy.service.type is "None".
//
// +optional
ExternalEndpoint string `json:"externalEndpoint,omitempty"`

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 v1alpha1
@@ -34,6 +34,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&OIDCIdentityProviderList{},
&LDAPIdentityProvider{},
&LDAPIdentityProviderList{},
&ActiveDirectoryIdentityProvider{},
&ActiveDirectoryIdentityProviderList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil

View File

@@ -0,0 +1,182 @@
// 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"
)
type ActiveDirectoryIdentityProviderPhase string
const (
// ActiveDirectoryPhasePending is the default phase for newly-created ActiveDirectoryIdentityProvider resources.
ActiveDirectoryPhasePending ActiveDirectoryIdentityProviderPhase = "Pending"
// ActiveDirectoryPhaseReady is the phase for an ActiveDirectoryIdentityProvider resource in a healthy state.
ActiveDirectoryPhaseReady ActiveDirectoryIdentityProviderPhase = "Ready"
// ActiveDirectoryPhaseError is the phase for an ActiveDirectoryIdentityProvider in an unhealthy state.
ActiveDirectoryPhaseError ActiveDirectoryIdentityProviderPhase = "Error"
)
// Status of an Active Directory identity provider.
type ActiveDirectoryIdentityProviderStatus struct {
// Phase summarizes the overall status of the ActiveDirectoryIdentityProvider.
// +kubebuilder:default=Pending
// +kubebuilder:validation:Enum=Pending;Ready;Error
Phase ActiveDirectoryIdentityProviderPhase `json:"phase,omitempty"`
// Represents the observations of an identity provider's current state.
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
}
type ActiveDirectoryIdentityProviderBind struct {
// SecretName contains the name of a namespace-local Secret object that provides the username and
// password for an Active Directory bind user. This account will be used to perform LDAP searches. The Secret should be
// of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value
// should be the full dn (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com".
// The password must be non-empty.
// +kubebuilder:validation:MinLength=1
SecretName string `json:"secretName"`
}
type ActiveDirectoryIdentityProviderUserSearchAttributes struct {
// Username specifies the name of the attribute in Active Directory entry whose value shall become the username
// of the user after a successful authentication.
// Optional, when empty this defaults to "userPrincipalName".
// +optional
Username string `json:"username,omitempty"`
// UID specifies the name of the attribute in the ActiveDirectory entry which whose value shall be used to uniquely
// identify the user within this ActiveDirectory provider after a successful authentication.
// Optional, when empty this defaults to "objectGUID".
// +optional
UID string `json:"uid,omitempty"`
}
type ActiveDirectoryIdentityProviderGroupSearchAttributes struct {
// GroupName specifies the name of the attribute in the Active Directory entries whose value shall become a group name
// in the user's list of groups after a successful authentication.
// The value of this field is case-sensitive and must match the case of the attribute name returned by the ActiveDirectory
// server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn".
// Optional. When not specified, this defaults to a custom field that looks like "sAMAccountName@domain",
// where domain is constructed from the domain components of the group DN.
// +optional
GroupName string `json:"groupName,omitempty"`
}
type ActiveDirectoryIdentityProviderUserSearch struct {
// Base is the dn (distinguished name) that should be used as the search base when searching for users.
// E.g. "ou=users,dc=example,dc=com".
// Optional, when not specified it will be based on the result of a query for the defaultNamingContext
// (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse).
// The default behavior searches your entire domain for users.
// It may make sense to specify a subtree as a search base if you wish to exclude some users
// or to make searches faster.
// +optional
Base string `json:"base,omitempty"`
// Filter is the search filter which should be applied when searching for users. The pattern "{}" must occur
// in the filter at least once and will be dynamically replaced by the username for which the search is being run.
// E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see
// https://ldap.com/ldap-filters.
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
// Optional. When not specified, the default will be
// '(&(objectClass=person)(!(objectClass=computer))(!(showInAdvancedViewOnly=TRUE))(|(sAMAccountName={}")(mail={})(userPrincipalName={})(sAMAccountType=805306368))'
// This means that the user is a person, is not a computer, the sAMAccountType is for a normal user account,
// and is not shown in advanced view only
// (which would likely mean its a system created service account with advanced permissions).
// Also, either the sAMAccountName, the userPrincipalName, or the mail attribute matches the input username.
// +optional
Filter string `json:"filter,omitempty"`
// Attributes specifies how the user's information should be read from the ActiveDirectory entry which was found as
// the result of the user search.
// +optional
Attributes ActiveDirectoryIdentityProviderUserSearchAttributes `json:"attributes,omitempty"`
}
type ActiveDirectoryIdentityProviderGroupSearch struct {
// Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g.
// "ou=groups,dc=example,dc=com".
// Optional, when not specified it will be based on the result of a query for the defaultNamingContext
// (see https://docs.microsoft.com/en-us/windows/win32/adschema/rootdse).
// The default behavior searches your entire domain for groups.
// It may make sense to specify a subtree as a search base if you wish to exclude some groups
// for security reasons or to make searches faster.
// +optional
Base string `json:"base,omitempty"`
// Filter is the ActiveDirectory search filter which should be applied when searching for groups for a user.
// The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the
// dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or
// "&(objectClass=groupOfNames)(member={})". For more information about ActiveDirectory filters, see
// https://ldap.com/ldap-filters.
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
// Optional. When not specified, the default will act as if the filter were specified as
// "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={})".
// This searches nested groups by default.
// Note that nested group search can be slow for some Active Directory servers. To disable it,
// you can set the filter to
// "(&(objectClass=group)(member={})"
// +optional
Filter string `json:"filter,omitempty"`
// Attributes specifies how the group's information should be read from each ActiveDirectory entry which was found as
// the result of the group search.
// +optional
Attributes ActiveDirectoryIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"`
}
// Spec for configuring an ActiveDirectory identity provider.
type ActiveDirectoryIdentityProviderSpec struct {
// Host is the hostname of this Active Directory identity provider, i.e., where to connect. For example: ldap.example.com:636.
// +kubebuilder:validation:MinLength=1
Host string `json:"host"`
// TLS contains the connection settings for how to establish the connection to the Host.
TLS *TLSSpec `json:"tls,omitempty"`
// Bind contains the configuration for how to provide access credentials during an initial bind to the ActiveDirectory server
// to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt.
Bind ActiveDirectoryIdentityProviderBind `json:"bind,omitempty"`
// UserSearch contains the configuration for searching for a user by name in Active Directory.
UserSearch ActiveDirectoryIdentityProviderUserSearch `json:"userSearch,omitempty"`
// GroupSearch contains the configuration for searching for a user's group membership in ActiveDirectory.
GroupSearch ActiveDirectoryIdentityProviderGroupSearch `json:"groupSearch,omitempty"`
}
// ActiveDirectoryIdentityProvider describes the configuration of an upstream Microsoft Active Directory identity provider.
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:resource:categories=pinniped;pinniped-idp;pinniped-idps
// +kubebuilder:printcolumn:name="Host",type=string,JSONPath=`.spec.host`
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// +kubebuilder:subresource:status
type ActiveDirectoryIdentityProvider struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec for configuring the identity provider.
Spec ActiveDirectoryIdentityProviderSpec `json:"spec"`
// Status of the identity provider.
Status ActiveDirectoryIdentityProviderStatus `json:"status,omitempty"`
}
// List of ActiveDirectoryIdentityProvider objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ActiveDirectoryIdentityProviderList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ActiveDirectoryIdentityProvider `json:"items"`
}

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 v1alpha1

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 v1alpha1
@@ -39,9 +39,31 @@ type OIDCIdentityProviderStatus struct {
// request parameters.
type OIDCAuthorizationConfig struct {
// AdditionalScopes are the scopes in addition to "openid" that will be requested as part of the authorization
// request flow with an OIDC identity provider. By default only the "openid" scope will be requested.
// request flow with an OIDC identity provider.
// In the case of a Resource Owner Password Credentials Grant flow, AdditionalScopes are the scopes
// in addition to "openid" that will be requested as part of the token request (see also the allowPasswordGrant field).
// By default, only the "openid" scope will be requested.
// +optional
AdditionalScopes []string `json:"additionalScopes,omitempty"`
// AllowPasswordGrant, when true, will allow the use of OAuth 2.0's Resource Owner Password Credentials Grant
// (see https://datatracker.ietf.org/doc/html/rfc6749#section-4.3) to authenticate to the OIDC provider using a
// username and password without a web browser, in addition to the usual browser-based OIDC Authorization Code Flow.
// The Resource Owner Password Credentials Grant is not officially part of the OIDC specification, so it may not be
// supported by your OIDC provider. If your OIDC provider supports returning ID tokens from a Resource Owner Password
// Credentials Grant token request, then you can choose to set this field to true. This will allow end users to choose
// to present their username and password to the kubectl CLI (using the Pinniped plugin) to authenticate to the
// cluster, without using a web browser to log in as is customary in OIDC Authorization Code Flow. This may be
// convenient for users, especially for identities from your OIDC provider which are not intended to represent a human
// actor, such as service accounts performing actions in a CI/CD environment. Even if your OIDC provider supports it,
// you may wish to disable this behavior by setting this field to false when you prefer to only allow users of this
// OIDCIdentityProvider to log in via the browser-based OIDC Authorization Code Flow. Using the Resource Owner Password
// Credentials Grant means that the Pinniped CLI and Pinniped Supervisor will directly handle your end users' passwords
// (similar to LDAPIdentityProvider), and you will not be able to require multi-factor authentication or use the other
// web-based login features of your OIDC provider during Resource Owner Password Credentials Grant logins.
// AllowPasswordGrant defaults to false.
// +optional
AllowPasswordGrant bool `json:"allowPasswordGrant,omitempty"`
}
// OIDCClaims provides a mapping from upstream claims into identities.

View File

@@ -11,6 +11,196 @@ 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 *ActiveDirectoryIdentityProvider) DeepCopyInto(out *ActiveDirectoryIdentityProvider) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProvider.
func (in *ActiveDirectoryIdentityProvider) DeepCopy() *ActiveDirectoryIdentityProvider {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProvider)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ActiveDirectoryIdentityProvider) 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 *ActiveDirectoryIdentityProviderBind) DeepCopyInto(out *ActiveDirectoryIdentityProviderBind) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderBind.
func (in *ActiveDirectoryIdentityProviderBind) DeepCopy() *ActiveDirectoryIdentityProviderBind {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderBind)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderGroupSearch) DeepCopyInto(out *ActiveDirectoryIdentityProviderGroupSearch) {
*out = *in
out.Attributes = in.Attributes
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderGroupSearch.
func (in *ActiveDirectoryIdentityProviderGroupSearch) DeepCopy() *ActiveDirectoryIdentityProviderGroupSearch {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderGroupSearch)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderGroupSearchAttributes) DeepCopyInto(out *ActiveDirectoryIdentityProviderGroupSearchAttributes) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderGroupSearchAttributes.
func (in *ActiveDirectoryIdentityProviderGroupSearchAttributes) DeepCopy() *ActiveDirectoryIdentityProviderGroupSearchAttributes {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderGroupSearchAttributes)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderList) DeepCopyInto(out *ActiveDirectoryIdentityProviderList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ActiveDirectoryIdentityProvider, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderList.
func (in *ActiveDirectoryIdentityProviderList) DeepCopy() *ActiveDirectoryIdentityProviderList {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ActiveDirectoryIdentityProviderList) 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 *ActiveDirectoryIdentityProviderSpec) DeepCopyInto(out *ActiveDirectoryIdentityProviderSpec) {
*out = *in
if in.TLS != nil {
in, out := &in.TLS, &out.TLS
*out = new(TLSSpec)
**out = **in
}
out.Bind = in.Bind
out.UserSearch = in.UserSearch
out.GroupSearch = in.GroupSearch
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderSpec.
func (in *ActiveDirectoryIdentityProviderSpec) DeepCopy() *ActiveDirectoryIdentityProviderSpec {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderStatus) DeepCopyInto(out *ActiveDirectoryIdentityProviderStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderStatus.
func (in *ActiveDirectoryIdentityProviderStatus) DeepCopy() *ActiveDirectoryIdentityProviderStatus {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderUserSearch) DeepCopyInto(out *ActiveDirectoryIdentityProviderUserSearch) {
*out = *in
out.Attributes = in.Attributes
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderUserSearch.
func (in *ActiveDirectoryIdentityProviderUserSearch) DeepCopy() *ActiveDirectoryIdentityProviderUserSearch {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderUserSearch)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopyInto(out *ActiveDirectoryIdentityProviderUserSearchAttributes) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActiveDirectoryIdentityProviderUserSearchAttributes.
func (in *ActiveDirectoryIdentityProviderUserSearchAttributes) DeepCopy() *ActiveDirectoryIdentityProviderUserSearchAttributes {
if in == nil {
return nil
}
out := new(ActiveDirectoryIdentityProviderUserSearchAttributes)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Condition) DeepCopyInto(out *Condition) {
*out = *in

View File

@@ -0,0 +1,66 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
// IDPType are the strings that can be returned by the Supervisor identity provider discovery endpoint
// as the "type" of each returned identity provider.
type IDPType string
// IDPFlow are the strings that can be returned by the Supervisor identity provider discovery endpoint
// in the array of allowed client "flows" for each returned identity provider.
type IDPFlow string
const (
IDPTypeOIDC IDPType = "oidc"
IDPTypeLDAP IDPType = "ldap"
IDPTypeActiveDirectory IDPType = "activedirectory"
IDPFlowCLIPassword IDPFlow = "cli_password"
IDPFlowBrowserAuthcode IDPFlow = "browser_authcode"
)
// Equals is a convenience function for comparing an IDPType to a string.
func (r IDPType) Equals(s string) bool {
return string(r) == s
}
// String is a convenience function to convert an IDPType to a string.
func (r IDPType) String() string {
return string(r)
}
// Equals is a convenience function for comparing an IDPFlow to a string.
func (r IDPFlow) Equals(s string) bool {
return string(r) == s
}
// String is a convenience function to convert an IDPFlow to a string.
func (r IDPFlow) String() string {
return string(r)
}
// OIDCDiscoveryResponse is part of the response from a FederationDomain's OpenID Provider Configuration
// Document returned by the .well-known/openid-configuration endpoint. It ignores all the standard OpenID Provider
// configuration metadata and only picks out the portion related to Supervisor identity provider discovery.
type OIDCDiscoveryResponse struct {
SupervisorDiscovery OIDCDiscoveryResponseIDPEndpoint `json:"discovery.supervisor.pinniped.dev/v1alpha1"`
}
// OIDCDiscoveryResponseIDPEndpoint contains the URL for the identity provider discovery endpoint.
type OIDCDiscoveryResponseIDPEndpoint struct {
PinnipedIDPsEndpoint string `json:"pinniped_identity_providers_endpoint"`
}
// IDPDiscoveryResponse is the response of a FederationDomain's identity provider discovery endpoint.
type IDPDiscoveryResponse struct {
PinnipedIDPs []PinnipedIDP `json:"pinniped_identity_providers"`
}
// PinnipedIDP describes a single identity provider as included in the response of a FederationDomain's
// identity provider discovery endpoint.
type PinnipedIDP struct {
Name string `json:"name"`
Type IDPType `json:"type"`
Flows []IDPFlow `json:"flows,omitempty"`
}

View File

@@ -0,0 +1,25 @@
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package oidc
// Constants related to the Supervisor FederationDomain's authorization and token endpoints.
const (
// AuthorizeUsernameHeaderName is the name of the HTTP header which can be used to transmit a username
// to the authorize endpoint when using a password flow, for example an OIDCIdentityProvider with a password grant
// or an LDAPIdentityProvider.
AuthorizeUsernameHeaderName = "Pinniped-Username"
// AuthorizePasswordHeaderName is the name of the HTTP header which can be used to transmit a password
// to the authorize endpoint when using a password flow, for example an OIDCIdentityProvider with a password grant
// or an LDAPIdentityProvider.
AuthorizePasswordHeaderName = "Pinniped-Password" //nolint:gosec // this is not a credential
// AuthorizeUpstreamIDPNameParamName is the name of the HTTP request parameter which can be used to help select which
// identity provider should be used for authentication by sending the name of the desired identity provider.
AuthorizeUpstreamIDPNameParamName = "pinniped_idp_name"
// AuthorizeUpstreamIDPTypeParamName is the name of the HTTP request parameter which can be used to help select which
// identity provider should be used for authentication by sending the type of the desired identity provider.
AuthorizeUpstreamIDPTypeParamName = "pinniped_idp_type"
)

View File

@@ -0,0 +1,182 @@
// 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
import (
"context"
"time"
v1alpha1 "go.pinniped.dev/generated/1.19/apis/supervisor/idp/v1alpha1"
scheme "go.pinniped.dev/generated/1.19/client/supervisor/clientset/versioned/scheme"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
rest "k8s.io/client-go/rest"
)
// ActiveDirectoryIdentityProvidersGetter has a method to return a ActiveDirectoryIdentityProviderInterface.
// A group's client should implement this interface.
type ActiveDirectoryIdentityProvidersGetter interface {
ActiveDirectoryIdentityProviders(namespace string) ActiveDirectoryIdentityProviderInterface
}
// ActiveDirectoryIdentityProviderInterface has methods to work with ActiveDirectoryIdentityProvider resources.
type ActiveDirectoryIdentityProviderInterface interface {
Create(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.CreateOptions) (*v1alpha1.ActiveDirectoryIdentityProvider, error)
Update(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.UpdateOptions) (*v1alpha1.ActiveDirectoryIdentityProvider, error)
UpdateStatus(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.UpdateOptions) (*v1alpha1.ActiveDirectoryIdentityProvider, error)
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ActiveDirectoryIdentityProvider, error)
List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ActiveDirectoryIdentityProviderList, error)
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error)
ActiveDirectoryIdentityProviderExpansion
}
// activeDirectoryIdentityProviders implements ActiveDirectoryIdentityProviderInterface
type activeDirectoryIdentityProviders struct {
client rest.Interface
ns string
}
// newActiveDirectoryIdentityProviders returns a ActiveDirectoryIdentityProviders
func newActiveDirectoryIdentityProviders(c *IDPV1alpha1Client, namespace string) *activeDirectoryIdentityProviders {
return &activeDirectoryIdentityProviders{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the activeDirectoryIdentityProvider, and returns the corresponding activeDirectoryIdentityProvider object, and an error if there is any.
func (c *activeDirectoryIdentityProviders) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
result = &v1alpha1.ActiveDirectoryIdentityProvider{}
err = c.client.Get().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do(ctx).
Into(result)
return
}
// List takes label and field selectors, and returns the list of ActiveDirectoryIdentityProviders that match those selectors.
func (c *activeDirectoryIdentityProviders) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ActiveDirectoryIdentityProviderList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1alpha1.ActiveDirectoryIdentityProviderList{}
err = c.client.Get().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do(ctx).
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested activeDirectoryIdentityProviders.
func (c *activeDirectoryIdentityProviders) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
opts.Watch = true
return c.client.Get().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch(ctx)
}
// Create takes the representation of a activeDirectoryIdentityProvider and creates it. Returns the server's representation of the activeDirectoryIdentityProvider, and an error, if there is any.
func (c *activeDirectoryIdentityProviders) Create(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.CreateOptions) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
result = &v1alpha1.ActiveDirectoryIdentityProvider{}
err = c.client.Post().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
VersionedParams(&opts, scheme.ParameterCodec).
Body(activeDirectoryIdentityProvider).
Do(ctx).
Into(result)
return
}
// Update takes the representation of a activeDirectoryIdentityProvider and updates it. Returns the server's representation of the activeDirectoryIdentityProvider, and an error, if there is any.
func (c *activeDirectoryIdentityProviders) Update(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.UpdateOptions) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
result = &v1alpha1.ActiveDirectoryIdentityProvider{}
err = c.client.Put().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
Name(activeDirectoryIdentityProvider.Name).
VersionedParams(&opts, scheme.ParameterCodec).
Body(activeDirectoryIdentityProvider).
Do(ctx).
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *activeDirectoryIdentityProviders) UpdateStatus(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.UpdateOptions) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
result = &v1alpha1.ActiveDirectoryIdentityProvider{}
err = c.client.Put().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
Name(activeDirectoryIdentityProvider.Name).
SubResource("status").
VersionedParams(&opts, scheme.ParameterCodec).
Body(activeDirectoryIdentityProvider).
Do(ctx).
Into(result)
return
}
// Delete takes name of the activeDirectoryIdentityProvider and deletes it. Returns an error if one occurs.
func (c *activeDirectoryIdentityProviders) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
Name(name).
Body(&opts).
Do(ctx).
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *activeDirectoryIdentityProviders) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
var timeout time.Duration
if listOpts.TimeoutSeconds != nil {
timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
VersionedParams(&listOpts, scheme.ParameterCodec).
Timeout(timeout).
Body(&opts).
Do(ctx).
Error()
}
// Patch applies the patch and returns the patched activeDirectoryIdentityProvider.
func (c *activeDirectoryIdentityProviders) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
result = &v1alpha1.ActiveDirectoryIdentityProvider{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("activedirectoryidentityproviders").
Name(name).
SubResource(subresources...).
VersionedParams(&opts, scheme.ParameterCodec).
Body(data).
Do(ctx).
Into(result)
return
}

View File

@@ -0,0 +1,129 @@
// 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 (
"context"
v1alpha1 "go.pinniped.dev/generated/1.19/apis/supervisor/idp/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakeActiveDirectoryIdentityProviders implements ActiveDirectoryIdentityProviderInterface
type FakeActiveDirectoryIdentityProviders struct {
Fake *FakeIDPV1alpha1
ns string
}
var activedirectoryidentityprovidersResource = schema.GroupVersionResource{Group: "idp.supervisor.pinniped.dev", Version: "v1alpha1", Resource: "activedirectoryidentityproviders"}
var activedirectoryidentityprovidersKind = schema.GroupVersionKind{Group: "idp.supervisor.pinniped.dev", Version: "v1alpha1", Kind: "ActiveDirectoryIdentityProvider"}
// Get takes name of the activeDirectoryIdentityProvider, and returns the corresponding activeDirectoryIdentityProvider object, and an error if there is any.
func (c *FakeActiveDirectoryIdentityProviders) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(activedirectoryidentityprovidersResource, c.ns, name), &v1alpha1.ActiveDirectoryIdentityProvider{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), err
}
// List takes label and field selectors, and returns the list of ActiveDirectoryIdentityProviders that match those selectors.
func (c *FakeActiveDirectoryIdentityProviders) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ActiveDirectoryIdentityProviderList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(activedirectoryidentityprovidersResource, activedirectoryidentityprovidersKind, c.ns, opts), &v1alpha1.ActiveDirectoryIdentityProviderList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1alpha1.ActiveDirectoryIdentityProviderList{ListMeta: obj.(*v1alpha1.ActiveDirectoryIdentityProviderList).ListMeta}
for _, item := range obj.(*v1alpha1.ActiveDirectoryIdentityProviderList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested activeDirectoryIdentityProviders.
func (c *FakeActiveDirectoryIdentityProviders) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(activedirectoryidentityprovidersResource, c.ns, opts))
}
// Create takes the representation of a activeDirectoryIdentityProvider and creates it. Returns the server's representation of the activeDirectoryIdentityProvider, and an error, if there is any.
func (c *FakeActiveDirectoryIdentityProviders) Create(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.CreateOptions) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(activedirectoryidentityprovidersResource, c.ns, activeDirectoryIdentityProvider), &v1alpha1.ActiveDirectoryIdentityProvider{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), err
}
// Update takes the representation of a activeDirectoryIdentityProvider and updates it. Returns the server's representation of the activeDirectoryIdentityProvider, and an error, if there is any.
func (c *FakeActiveDirectoryIdentityProviders) Update(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.UpdateOptions) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(activedirectoryidentityprovidersResource, c.ns, activeDirectoryIdentityProvider), &v1alpha1.ActiveDirectoryIdentityProvider{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeActiveDirectoryIdentityProviders) UpdateStatus(ctx context.Context, activeDirectoryIdentityProvider *v1alpha1.ActiveDirectoryIdentityProvider, opts v1.UpdateOptions) (*v1alpha1.ActiveDirectoryIdentityProvider, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(activedirectoryidentityprovidersResource, "status", c.ns, activeDirectoryIdentityProvider), &v1alpha1.ActiveDirectoryIdentityProvider{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), err
}
// Delete takes name of the activeDirectoryIdentityProvider and deletes it. Returns an error if one occurs.
func (c *FakeActiveDirectoryIdentityProviders) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(activedirectoryidentityprovidersResource, c.ns, name), &v1alpha1.ActiveDirectoryIdentityProvider{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeActiveDirectoryIdentityProviders) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(activedirectoryidentityprovidersResource, c.ns, listOpts)
_, err := c.Fake.Invokes(action, &v1alpha1.ActiveDirectoryIdentityProviderList{})
return err
}
// Patch applies the patch and returns the patched activeDirectoryIdentityProvider.
func (c *FakeActiveDirectoryIdentityProviders) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ActiveDirectoryIdentityProvider, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(activedirectoryidentityprovidersResource, c.ns, name, pt, data, subresources...), &v1alpha1.ActiveDirectoryIdentityProvider{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ActiveDirectoryIdentityProvider), err
}

View File

@@ -15,6 +15,10 @@ type FakeIDPV1alpha1 struct {
*testing.Fake
}
func (c *FakeIDPV1alpha1) ActiveDirectoryIdentityProviders(namespace string) v1alpha1.ActiveDirectoryIdentityProviderInterface {
return &FakeActiveDirectoryIdentityProviders{c, namespace}
}
func (c *FakeIDPV1alpha1) LDAPIdentityProviders(namespace string) v1alpha1.LDAPIdentityProviderInterface {
return &FakeLDAPIdentityProviders{c, namespace}
}

View File

@@ -5,6 +5,8 @@
package v1alpha1
type ActiveDirectoryIdentityProviderExpansion interface{}
type LDAPIdentityProviderExpansion interface{}
type OIDCIdentityProviderExpansion interface{}

View File

@@ -13,6 +13,7 @@ import (
type IDPV1alpha1Interface interface {
RESTClient() rest.Interface
ActiveDirectoryIdentityProvidersGetter
LDAPIdentityProvidersGetter
OIDCIdentityProvidersGetter
}
@@ -22,6 +23,10 @@ type IDPV1alpha1Client struct {
restClient rest.Interface
}
func (c *IDPV1alpha1Client) ActiveDirectoryIdentityProviders(namespace string) ActiveDirectoryIdentityProviderInterface {
return newActiveDirectoryIdentityProviders(c, namespace)
}
func (c *IDPV1alpha1Client) LDAPIdentityProviders(namespace string) LDAPIdentityProviderInterface {
return newLDAPIdentityProviders(c, namespace)
}

View File

@@ -45,6 +45,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
return &genericInformer{resource: resource.GroupResource(), informer: f.Config().V1alpha1().FederationDomains().Informer()}, nil
// Group=idp.supervisor.pinniped.dev, Version=v1alpha1
case idpv1alpha1.SchemeGroupVersion.WithResource("activedirectoryidentityproviders"):
return &genericInformer{resource: resource.GroupResource(), informer: f.IDP().V1alpha1().ActiveDirectoryIdentityProviders().Informer()}, nil
case idpv1alpha1.SchemeGroupVersion.WithResource("ldapidentityproviders"):
return &genericInformer{resource: resource.GroupResource(), informer: f.IDP().V1alpha1().LDAPIdentityProviders().Informer()}, nil
case idpv1alpha1.SchemeGroupVersion.WithResource("oidcidentityproviders"):

View File

@@ -0,0 +1,77 @@
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by informer-gen. DO NOT EDIT.
package v1alpha1
import (
"context"
time "time"
idpv1alpha1 "go.pinniped.dev/generated/1.19/apis/supervisor/idp/v1alpha1"
versioned "go.pinniped.dev/generated/1.19/client/supervisor/clientset/versioned"
internalinterfaces "go.pinniped.dev/generated/1.19/client/supervisor/informers/externalversions/internalinterfaces"
v1alpha1 "go.pinniped.dev/generated/1.19/client/supervisor/listers/idp/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
// ActiveDirectoryIdentityProviderInformer provides access to a shared informer and lister for
// ActiveDirectoryIdentityProviders.
type ActiveDirectoryIdentityProviderInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1alpha1.ActiveDirectoryIdentityProviderLister
}
type activeDirectoryIdentityProviderInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewActiveDirectoryIdentityProviderInformer constructs a new informer for ActiveDirectoryIdentityProvider type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewActiveDirectoryIdentityProviderInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredActiveDirectoryIdentityProviderInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredActiveDirectoryIdentityProviderInformer constructs a new informer for ActiveDirectoryIdentityProvider type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredActiveDirectoryIdentityProviderInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.IDPV1alpha1().ActiveDirectoryIdentityProviders(namespace).List(context.TODO(), options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.IDPV1alpha1().ActiveDirectoryIdentityProviders(namespace).Watch(context.TODO(), options)
},
},
&idpv1alpha1.ActiveDirectoryIdentityProvider{},
resyncPeriod,
indexers,
)
}
func (f *activeDirectoryIdentityProviderInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredActiveDirectoryIdentityProviderInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *activeDirectoryIdentityProviderInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&idpv1alpha1.ActiveDirectoryIdentityProvider{}, f.defaultInformer)
}
func (f *activeDirectoryIdentityProviderInformer) Lister() v1alpha1.ActiveDirectoryIdentityProviderLister {
return v1alpha1.NewActiveDirectoryIdentityProviderLister(f.Informer().GetIndexer())
}

View File

@@ -11,6 +11,8 @@ import (
// Interface provides access to all the informers in this group version.
type Interface interface {
// ActiveDirectoryIdentityProviders returns a ActiveDirectoryIdentityProviderInformer.
ActiveDirectoryIdentityProviders() ActiveDirectoryIdentityProviderInformer
// LDAPIdentityProviders returns a LDAPIdentityProviderInformer.
LDAPIdentityProviders() LDAPIdentityProviderInformer
// OIDCIdentityProviders returns a OIDCIdentityProviderInformer.
@@ -28,6 +30,11 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// ActiveDirectoryIdentityProviders returns a ActiveDirectoryIdentityProviderInformer.
func (v *version) ActiveDirectoryIdentityProviders() ActiveDirectoryIdentityProviderInformer {
return &activeDirectoryIdentityProviderInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// LDAPIdentityProviders returns a LDAPIdentityProviderInformer.
func (v *version) LDAPIdentityProviders() LDAPIdentityProviderInformer {
return &lDAPIdentityProviderInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}

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