Compare commits

..

1 Commits

Author SHA1 Message Date
Margo Crawford
8a4bbbfcbe Proof of concept: client-side logout
Signed-off-by: Margo Crawford <margaretc@vmware.com>
2022-03-04 17:01:26 -08:00
1222 changed files with 8857 additions and 248701 deletions

1
.gitattributes vendored
View File

@@ -1,3 +1,2 @@
*.go.tmpl linguist-language=Go
hack/Dockerfile_fips linguist-language=Dockerfile
generated/** linguist-generated

View File

@@ -12,8 +12,3 @@ updates:
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "docker"
directory: "/hack" # this should keep the FIPS dockerfile updated per https://github.com/dependabot/feedback/issues/145#issuecomment-414738498
schedule:
interval: "daily"

View File

@@ -2,10 +2,10 @@ name: "CodeQL"
on:
push:
branches: [ main, release*, dynamic_clients ]
branches: [ main, release* ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main, release*, dynamic_clients ]
branches: [ main, release* ]
schedule:
- cron: '39 13 * * 2'

View File

@@ -1,55 +0,0 @@
name: Scorecards supply-chain security
on:
# Only the default branch is supported.
branch_protection_rule:
schedule:
- cron: '29 11 * * 3'
push:
branches: [ main, release* ]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecards analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
actions: read
contents: read
steps:
- name: "Checkout code"
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # v3.0.0
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@c1aec4ac820532bab364f02a81873c555a0ba3a1 # v1.0.4
with:
results_file: results.sarif
results_format: sarif
# Read-only PAT token. To create it,
# follow the steps in https://github.com/ossf/scorecard-action#pat-token-creation.
repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
# Publish the results to enable scorecard badges. For more details, see
# https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories, `publish_results` will automatically be set to `false`,
# regardless of the value entered here.
publish_results: true
# Upload the results as artifacts (optional).
- name: "Upload artifact"
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # v1.0.26
with:
sarif_file: results.sarif

View File

@@ -8,13 +8,16 @@ linters:
disable-all: true
enable:
# default linters
- deadcode
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- structcheck
- typecheck
- unused
- varcheck
# additional linters for this project (we should disable these if they get annoying).
- asciicheck
@@ -31,7 +34,7 @@ linters:
- godot
- goheader
- goimports
- revive
- golint
- goprintffuncname
- gosec
- misspell
@@ -41,7 +44,7 @@ linters:
- nolintlint
- prealloc
- rowserrcheck
- exportloopref
- scopelint
- sqlclosecheck
- unconvert
- whitespace

View File

@@ -2,7 +2,7 @@
# On macOS, try `brew install pre-commit` and then run `pre-commit install`.
exclude: '^(site|generated)/'
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- repo: git://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
# TODO: find a version of this to validate ytt templates?
@@ -11,7 +11,6 @@ repos:
- id: check-json
- id: end-of-file-fixer
- id: trailing-whitespace
exclude: 'securetls*' # prevent the linter from running in this file because it's not smart enough not to trim the nmap test output.
- id: check-merge-conflict
- id: check-added-large-files
- id: check-byte-order-marker

View File

@@ -1,8 +1,5 @@
# Contributing to Pinniped
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.
Contributions to Pinniped are welcome. Here are some things to help you get started.
## Code of Conduct
@@ -17,13 +14,24 @@ See [SCOPE.md](./SCOPE.md) for some guidelines about what we consider in and out
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](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 ET.
Use [this Zoom Link](https://go.pinniped.dev/community/zoom)
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 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://go.pinniped.dev/community/slack).
Join our [Google Group](https://go.pinniped.dev/community/group) to receive updates and meeting invitations.
or in the Kubernetes Slack Workspace within the [#pinniped channel](https://kubernetes.slack.com/archives/C01BW364RJA).
## Issues
@@ -71,13 +79,6 @@ a Pull Request. For questions about the CLA process, see the
[FAQ](https://cla.vmware.com/faq) or submit a question through the GitHub issue
tracker.
## Learning about Pinniped
New to Pinniped?
- Start here to learn how to install and use Pinniped: [Learn to use Pinniped for federated authentication to Kubernetes clusters](https://pinniped.dev/docs/tutorials/concierge-and-supervisor-demo/)
- Start here to learn how to navigate the source code: [Code Walk-through](https://pinniped.dev/docs/reference/code-walkthrough/)
- Other more detailed documentation can be found at: [Pinniped Docs](https://pinniped.dev/docs/)
## Building
The [Dockerfile](Dockerfile) at the root of the repo can be used to build and
@@ -126,7 +127,7 @@ go build -o pinniped ./cmd/pinniped
On macOS, these tools can be installed with [Homebrew](https://brew.sh/) (assuming you have Chrome installed already):
```bash
brew install kind vmware-tanzu/carvel/ytt vmware-tanzu/carvel/kapp kubectl chromedriver nmap && brew cask install docker
brew install kind k14s/tap/ytt k14s/tap/kapp kubectl chromedriver nmap && brew cask install docker
```
1. Create a kind cluster, compile, create container images, and install Pinniped and supporting test dependencies using:
@@ -138,14 +139,9 @@ go build -o pinniped ./cmd/pinniped
1. Run the Pinniped integration tests:
```bash
ulimit -n 512 && source /tmp/integration-test-env && go test -v -count 1 -timeout 0 ./test/integration
source /tmp/integration-test-env && go test -v -count 1 -timeout 0 ./test/integration
```
To run specific integration tests, add the `-run` flag to the above command to specify a regexp for the test names.
Use a leading `/` on the regexp because the Pinniped integration tests are automatically nested under several parent tests
(see [integration/main_test.go](https://github.com/vmware-tanzu/pinniped/blob/main/test/integration/main_test.go)).
For example, to run an integration test called `TestE2E`, add `-run /TestE2E` to the command shown above.
1. After making production code changes, recompile, redeploy, and run tests again by repeating the same
commands described above. If there are only test code changes, then simply run the tests again.

View File

@@ -1,9 +1,9 @@
# syntax=docker/dockerfile:1
# syntax = docker/dockerfile:1.0-experimental
# Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
FROM golang:1.19.4 as build-env
FROM golang:1.17.7 as build-env
WORKDIR /work
COPY . .
@@ -24,13 +24,13 @@ RUN \
ln -s /usr/local/bin/pinniped-server /usr/local/bin/local-user-authenticator
# Use a distroless runtime image with CA certificates, timezone data, and not much else.
FROM gcr.io/distroless/static:nonroot@sha256:2a9e2b4fa771d31fe3346a873be845bfc2159695b9f90ca08e950497006ccc2e
FROM gcr.io/distroless/static:nonroot@sha256:80c956fb0836a17a565c43a4026c9c80b2013c83bea09f74fa4da195a59b7a99
# Copy the server binary from the build-env stage.
COPY --from=build-env /usr/local/bin /usr/local/bin
# Document the default server ports for the various server apps
EXPOSE 8443 8444 10250
EXPOSE 8080 8443 8444 10250
# Run as non-root for security posture
# Use the same non-root user as https://github.com/GoogleContainerTools/distroless/blob/fc3c4eaceb0518900f886aae90407c43be0a42d9/base/base.bzl#L9

View File

@@ -1,24 +1,24 @@
# Current Pinniped Maintainers
# Pinniped Maintainers
| Maintainer | GitHub ID | Affiliation |
|-----------------|-----------------------------------------------------------|------------------------------------------|
| Ben Petersen | [benjaminapetersen](https://github.com/benjaminapetersen) | [VMware](https://www.github.com/vmware/) |
| Ryan Richard | [cfryanr](https://github.com/cfryanr) | [VMware](https://www.github.com/vmware/) |
| Joshua T. Casey | [joshuatcasey](https://github.com/joshuatcasey) | [VMware](https://www.github.com/vmware/) |
This is the current list of maintainers for the Pinniped project.
| Maintainer | GitHub ID | Affiliation |
| --------------- | --------- | ----------- |
| Margo Crawford | [margocrawf](https://github.com/margocrawf) | [VMware](https://www.github.com/vmware/) |
| Mo Khan | [enj](https://github.com/enj) | [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
| Maintainer | GitHub ID |
|-------------------|---------------------------------------------------------|
| Andrew Keesler | [ankeesler](https://github.com/ankeesler) |
| Anjali Telang | [anjaltelang](https://github.com/anjaltelang) |
| Margo Crawford | [margocrawf](https://github.com/margocrawf) |
| Matt Moyer | [mattmoyer](https://github.com/mattmoyer) |
| Mo Khan | [enj](https://github.com/enj) |
| Pablo Schuhmacher | [pabloschuhmacher](https://github.com/pabloschuhmacher) |
* Andrew Keesler, [ankeesler](https://github.com/ankeesler)
* Pablo Schuhmacher, [pabloschuhmacher](https://github.com/pabloschuhmacher)
* Matt Moyer, [mattmoyer](https://github.com/mattmoyer)
## Pinniped Community Management
## Pinniped Contributors & Stakeholders
| Community Manager | GitHub ID |
|-------------------|---------------------------------------|
| Nigel Brown | [pnbrown](https://github.com/pnbrown) |
| Feature Area | Lead |
| ----------------------------- | :---------------------: |
| Technical Lead | Mo Khan (enj) |
| Product Management | Anjali Telang (anjaltelang) |
| Community Management | Nanci Lancaster (microwavables) |

View File

@@ -21,19 +21,29 @@ Care to kick the tires? It's easy to [install and try Pinniped](https://pinniped
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://go.pinniped.dev/community/slack).
Join our [Google Group](https://go.pinniped.dev/community/group) to receive updates and meeting invitations.
or in the Kubernetes Slack Workspace within the [#pinniped channel](https://kubernetes.slack.com/archives/C01BW364RJA).
## Contributions
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.
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 ET.
**Note:** Community meetings are currently paused until early 2022 as we wind down 2021!
Use [this Zoom Link](https://go.pinniped.dev/community/zoom) to attend and add any agenda items you wish to
discuss to [the notes document](https://go.pinniped.dev/community/agenda).
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).
@@ -47,4 +57,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-2022 the Pinniped contributors. All Rights Reserved.
Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.

View File

@@ -1,41 +1,68 @@
## Pinniped Project Roadmap
### About this document
## **Pinniped Project Roadmap**
This document provides a high-level overview of the next big features the maintainers are planning to work on. This
should serve as a reference point for Pinniped users and contributors to understand where the project is heading, and
help determine if a contribution could be conflicting with a longer term plan.
The [Pinniped project backlog](https://github.com/orgs/vmware-tanzu/projects/43/) is prioritized based on this roadmap,
and it provides a more granular view of what the maintainers are working on a day-to-day basis.
### How to help
###
**About this document**
Discussion on the roadmap is welcomed. If you want to provide suggestions, use cases, and feedback to an item in the
roadmap, please reach out to the maintainers using one of the methods described in the project's
[README.md](https://github.com/vmware-tanzu/pinniped#discussion).
[Contributions](https://github.com/vmware-tanzu/pinniped/blob/main/CONTRIBUTING.md) to Pinniped are also welcomed.
This document provides a link to the[ Pinniped Project issues](https://github.com/vmware-tanzu/pinniped/issues) list that serves as the up to date description of items that are in the Pinniped release pipeline. Most items are gathered from the community or include a feedback loop with the community. This should serve as a reference point for Pinniped users and contributors to understand where the project is heading, and help determine if a contribution could be conflicting with a longer term plan.
### How to add an item to the roadmap
One of the most important aspects in any open source community is the concept of proposals. Large changes to the
codebase and / or new features should be preceded by
a [proposal](https://github.com/vmware-tanzu/pinniped/tree/main/proposals) in our repo.
For smaller enhancements, you can open an issue to track that initiative or feature request.
We work with and rely on community feedback to focus our efforts to improve Pinniped and maintain a healthy roadmap.
###
**How to help?**
### Current Roadmap
Discussion on the roadmap can take place in threads under [Issues](https://github.com/vmware-tanzu/pinniped/issues) or in [community meetings](https://github.com/vmware-tanzu/pinniped/blob/main/CONTRIBUTING.md#meeting-with-the-maintainers). Please open and comment on an issue if you want to provide suggestions and feedback to an item in the roadmap. Please review the roadmap to avoid potential duplicated effort.
The following table includes the current roadmap for Pinniped. Please take the timelines and dates as proposals and
goals. Priorities and requirements change based on community feedback, roadblocks encountered, community contributions,
etc. If you depend on a specific item, we encourage you to reach out for updated status information, or help us deliver
that feature by [contributing](https://github.com/vmware-tanzu/pinniped/blob/main/CONTRIBUTING.md) to Pinniped.
Last Updated: Sept 2022
###
**Need an idea for a contribution?**
Weve created an [Opportunity Areas](https://github.com/vmware-tanzu/pinniped/discussions/483) discussion thread that outlines some areas we believe are excellent starting points for the community to get involved. In that discussion weve included specific work items that one might consider that also support the high-level items presented in our roadmap.
###
**How to add an item to the roadmap?**
Please open an issue to track any initiative on the roadmap of Pinniped (usually driven by new feature requests). We will work with and rely on our community to focus our efforts to improve Pinniped.
###
**Current Roadmap**
The following table includes the current roadmap for Pinniped. If you have any questions or would like to contribute to Pinniped, please attend a [community meeting](https://github.com/vmware-tanzu/pinniped/blob/main/CONTRIBUTING.md#meeting-with-the-maintainers) to discuss with our team. If you don't know where to start, we are always looking for contributors that will help us reduce technical, automation, and documentation debt. Please take the timelines & dates as proposals and goals. Priorities and requirements change based on community feedback, roadblocks encountered, community contributions, etc. If you depend on a specific item, we encourage you to attend community meetings to get updated status information, or help us deliver that feature by contributing to Pinniped.
Last Updated: Jan 2022
|Theme|Description|Timeline|
|--|--|--|
|Improving Usability|Dynamic Oauth Client Support for integrating with UI/Dashboards |Sept/Oct 2022|
|Improving Usability|Support for custom claim mappings in OIDCIdentityProvider |Q4 2022|
|Improving Usability|Support for Multiple Identity Providers |Q4 2022|
|Improving Security Posture|Support Audit logging of security events related to Authentication |Q4 2022|
|Improving Security Posture|Session Management |2022/2023|
|Improving Security Posture|Secrets Rotation and Management |2022/2023|
|Improving Security Posture|Support for refreshing LDAP/AD Group information |March 2022|
|Improving Security Posture|Support FIPS compliant Boring crypto libraries |March/April 2022|
|Multiple IDP support|Support multiple IDPs configured on a single Supervisor|April 2022|
|Improving Security Posture|TLS hardening |March/April 2022|
|Improving Security Posture|Support Audit logging of security events related to Authentication |April/May 2022|
|Improving Usability|Support for integrating with UI/Dashboards |June/July 2022|
|Improving Security Posture|mTLS for Supervisor sessions |Exploring/Ongoing|
|Improving Security Posture|Key management/rotation for Pinniped components with minimal downtime |Exploring/Ongoing|
|Improving Security Posture|Support for Session Logout |Exploring/Ongoing|
|Improving Security Posture|Support for Idle Session/ Inactivity timeout|Exploring/Ongoing|
|Improving Security Posture|Support for Max Concurrent Sessions|Exploring/Ongoing|
|Improving Security Posture|Support for configurable Session Length |Exploring/Ongoing|
|Improving Security Posture|Reject use of username and groups with system: prefix |Exploring/Ongoing|
|Improving Security Posture|Support for using external KMS for Supervisor signing keys |Exploring/Ongoing|
|Improving Security Posture|Client side use of Secure Enclaves for Session data |Exploring/Ongoing|
|Improving Security Posture|Enforce the use of HTTP Strict Transport (HSTS) |Exploring/Ongoing|
|Improving Security Posture|Assert that Pinniped runs under the restricted PSP version2 levels |Exploring/Ongoing|
|Wider Concierge cluster support|Support for OpenShift cluster types in the Concierge|Exploring/Ongoing|
|Identity transforms|Support prefixing, filtering, or performing coarse-grained checks on upstream users and groups|Exploring/Ongoing|
|CLI SSO|Support Kerberos based authentication on CLI |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|
|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

@@ -17,13 +17,11 @@ type WhoAmIRequest struct {
Status WhoAmIRequestStatus
}
// Spec is always empty for a WhoAmIRequest.
type WhoAmIRequestSpec struct {
// empty for now but we may add some config here in the future
// any such config must be safe in the context of an unauthenticated user
}
// Status is set by the server in the response to a WhoAmIRequest.
type WhoAmIRequestStatus struct {
// The current authenticated user, exactly as Kubernetes understands it.
KubernetesUserInfo KubernetesUserInfo
@@ -37,6 +35,6 @@ type WhoAmIRequestList struct {
metav1.TypeMeta
metav1.ListMeta
// Items is a list of WhoAmIRequest.
// Items is a list of WhoAmIRequest
Items []WhoAmIRequest
}

View File

@@ -20,13 +20,11 @@ type WhoAmIRequest struct {
Status WhoAmIRequestStatus `json:"status,omitempty"`
}
// Spec is always empty for a WhoAmIRequest.
type WhoAmIRequestSpec struct {
// empty for now but we may add some config here in the future
// any such config must be safe in the context of an unauthenticated user
}
// Status is set by the server in the response to a WhoAmIRequest.
type WhoAmIRequestStatus struct {
// The current authenticated user, exactly as Kubernetes understands it.
KubernetesUserInfo KubernetesUserInfo `json:"kubernetesUserInfo"`
@@ -40,6 +38,6 @@ type WhoAmIRequestList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
// Items is a list of WhoAmIRequest.
// Items is a list of WhoAmIRequest
Items []WhoAmIRequest `json:"items"`
}

View File

@@ -5,8 +5,7 @@ package login
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// ClusterCredential is the cluster-specific credential returned on a successful credential request. It
// contains either a valid bearer token or a valid TLS certificate and corresponding private key for the cluster.
// ClusterCredential is a credential (token or certificate) which is valid on the Kubernetes cluster.
type ClusterCredential struct {
// ExpirationTimestamp indicates a time when the provided credentials expire.
ExpirationTimestamp metav1.Time

View File

@@ -8,7 +8,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Specification of a TokenCredentialRequest, expected on requests to the Pinniped API.
type TokenCredentialRequestSpec struct {
// Bearer token supplied with the credential request.
Token string
@@ -17,9 +16,8 @@ type TokenCredentialRequestSpec struct {
Authenticator corev1.TypedLocalObjectReference
}
// Status of a TokenCredentialRequest, returned on responses to the Pinniped API.
type TokenCredentialRequestStatus struct {
// A Credential will be returned for a successful credential request.
// A ClusterCredential will be returned for a successful credential request.
// +optional
Credential *ClusterCredential
@@ -44,6 +42,6 @@ type TokenCredentialRequestList struct {
metav1.TypeMeta
metav1.ListMeta
// Items is a list of TokenCredentialRequest.
// Items is a list of TokenCredentialRequest
Items []TokenCredentialRequest
}

View File

@@ -8,7 +8,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Specification of a TokenCredentialRequest, expected on requests to the Pinniped API.
// TokenCredentialRequestSpec is the specification of a TokenCredentialRequest, expected on requests to the Pinniped API.
type TokenCredentialRequestSpec struct {
// Bearer token supplied with the credential request.
Token string `json:"token,omitempty"`
@@ -17,7 +17,7 @@ type TokenCredentialRequestSpec struct {
Authenticator corev1.TypedLocalObjectReference `json:"authenticator"`
}
// Status of a TokenCredentialRequest, returned on responses to the Pinniped API.
// TokenCredentialRequestStatus is the status of a TokenCredentialRequest, returned on responses to the Pinniped API.
type TokenCredentialRequestStatus struct {
// A Credential will be returned for a successful credential request.
// +optional
@@ -47,6 +47,5 @@ type TokenCredentialRequestList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
// Items is a list of TokenCredentialRequest.
Items []TokenCredentialRequest `json:"items"`
}

View File

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

View File

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

View File

@@ -1,50 +0,0 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package clientsecret
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// OIDCClientSecretRequest can be used to update the client secrets associated with an OIDCClient.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type OIDCClientSecretRequest struct {
metav1.TypeMeta
metav1.ObjectMeta // metadata.name must be set to the client ID
Spec OIDCClientSecretRequestSpec
// +optional
Status OIDCClientSecretRequestStatus
}
// Spec of the OIDCClientSecretRequest.
type OIDCClientSecretRequestSpec struct {
// Request a new client secret to for the OIDCClient referenced by the metadata.name field.
// +optional
GenerateNewSecret bool
// Revoke the old client secrets associated with the OIDCClient referenced by the metadata.name field.
// +optional
RevokeOldSecrets bool
}
// Status of the OIDCClientSecretRequest.
type OIDCClientSecretRequestStatus struct {
// The unencrypted OIDC Client Secret. This will only be shared upon creation and cannot be recovered if lost.
GeneratedSecret string
// The total number of client secrets associated with the OIDCClient referenced by the metadata.name field.
TotalClientSecrets int
}
// OIDCClientSecretRequestList is a list of OIDCClientSecretRequest objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type OIDCClientSecretRequestList struct {
metav1.TypeMeta
metav1.ListMeta
// Items is a list of OIDCClientSecretRequest.
Items []OIDCClientSecretRequest
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,53 +0,0 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// OIDCClientSecretRequest can be used to update the client secrets associated with an OIDCClient.
// +genclient
// +genclient:onlyVerbs=create
// +kubebuilder:subresource:status
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type OIDCClientSecretRequest struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"` // metadata.name must be set to the client ID
Spec OIDCClientSecretRequestSpec `json:"spec"`
// +optional
Status OIDCClientSecretRequestStatus `json:"status"`
}
// Spec of the OIDCClientSecretRequest.
type OIDCClientSecretRequestSpec struct {
// Request a new client secret to for the OIDCClient referenced by the metadata.name field.
// +optional
GenerateNewSecret bool `json:"generateNewSecret"`
// Revoke the old client secrets associated with the OIDCClient referenced by the metadata.name field.
// +optional
RevokeOldSecrets bool `json:"revokeOldSecrets"`
}
// Status of the OIDCClientSecretRequest.
type OIDCClientSecretRequestStatus struct {
// The unencrypted OIDC Client Secret. This will only be shared upon creation and cannot be recovered if lost.
GeneratedSecret string `json:"generatedSecret,omitempty"`
// The total number of client secrets associated with the OIDCClient referenced by the metadata.name field.
TotalClientSecrets int `json:"totalClientSecrets"`
}
// OIDCClientSecretRequestList is a list of OIDCClientSecretRequest objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type OIDCClientSecretRequestList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
// Items is a list of OIDCClientSecretRequest.
Items []OIDCClientSecretRequest `json:"items"`
}

View File

@@ -32,8 +32,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&FederationDomain{},
&FederationDomainList{},
&OIDCClient{},
&OIDCClientList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil

View File

@@ -31,9 +31,8 @@ type FederationDomainTLSSpec struct {
// SNI requests do not include port numbers, so all issuers with the same DNS hostname must use the same
// SecretName value even if they have different port numbers.
//
// SecretName is not required when you would like to use only the HTTP endpoints (e.g. when the HTTP listener is
// configured to listen on loopback interfaces or UNIX domain sockets for traffic from a service mesh sidecar).
// It is also not required when you would like all requests to this OIDC Provider's HTTPS endpoints to
// SecretName is not required when you would like to use only the HTTP endpoints (e.g. when terminating TLS at an
// Ingress). It is also not required when you would like all requests to this OIDC Provider's HTTPS endpoints to
// use the default TLS certificate, which is configured elsewhere.
//
// When your Issuer URL's host is an IP address, then this field is ignored. SNI does not work for IP addresses.

View File

@@ -1,75 +0,0 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// ConditionStatus is effectively an enum type for Condition.Status.
type ConditionStatus string
// These are valid condition statuses. "ConditionTrue" means a resource is in the condition.
// "ConditionFalse" means a resource is not in the condition. "ConditionUnknown" means kubernetes
// can't decide if a resource is in the condition or not. In the future, we could add other
// intermediate conditions, e.g. ConditionDegraded.
const (
ConditionTrue ConditionStatus = "True"
ConditionFalse ConditionStatus = "False"
ConditionUnknown ConditionStatus = "Unknown"
)
// 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.
type Condition struct {
// 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)
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation: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])$`
// +kubebuilder:validation:MaxLength=316
Type string `json:"type"`
// status of the condition, one of True, False, Unknown.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Enum=True;False;Unknown
Status ConditionStatus `json:"status"`
// 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.
// +optional
// +kubebuilder:validation:Minimum=0
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// 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.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Format=date-time
LastTransitionTime metav1.Time `json:"lastTransitionTime"`
// 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.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:MaxLength=1024
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$`
Reason string `json:"reason"`
// message is a human readable message indicating details about the transition.
// This may be an empty string.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:MaxLength=32768
Message string `json:"message"`
}

View File

@@ -1,122 +0,0 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
type OIDCClientPhase string
const (
// PhasePending is the default phase for newly-created OIDCClient resources.
PhasePending OIDCClientPhase = "Pending"
// PhaseReady is the phase for an OIDCClient resource in a healthy state.
PhaseReady OIDCClientPhase = "Ready"
// PhaseError is the phase for an OIDCClient in an unhealthy state.
PhaseError OIDCClientPhase = "Error"
)
// +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/`
type RedirectURI string
// +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange"
type GrantType string
// +kubebuilder:validation:Enum="openid";"offline_access";"username";"groups";"pinniped:request-audience"
type Scope string
// OIDCClientSpec is a struct that describes an OIDCClient.
type OIDCClientSpec struct {
// allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this
// client. Any other uris will be rejected.
// Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme.
// Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
// +listType=set
// +kubebuilder:validation:MinItems=1
AllowedRedirectURIs []RedirectURI `json:"allowedRedirectURIs"`
// allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this
// client.
//
// Must only contain the following values:
// - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to
// authenticate users. This grant must always be listed.
// - refresh_token: allows the client to perform refresh grants for the user to extend the user's session.
// This grant must be listed if allowedScopes lists offline_access.
// - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange,
// which is a step in the process to be able to get a cluster credential for the user.
// This grant must be listed if allowedScopes lists pinniped:request-audience.
// +listType=set
// +kubebuilder:validation:MinItems=1
AllowedGrantTypes []GrantType `json:"allowedGrantTypes"`
// allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client.
//
// Must only contain the following values:
// - openid: The client is allowed to request ID tokens. ID tokens only include the required claims by default (iss, sub, aud, exp, iat).
// This scope must always be listed.
// - offline_access: The client is allowed to request an initial refresh token during the authorization code grant flow.
// This scope must be listed if allowedGrantTypes lists refresh_token.
// - pinniped:request-audience: The client is allowed to request a new audience value during a RFC8693 token exchange,
// which is a step in the process to be able to get a cluster credential for the user.
// openid, username and groups scopes must be listed when this scope is present.
// This scope must be listed if allowedGrantTypes lists urn:ietf:params:oauth:grant-type:token-exchange.
// - username: The client is allowed to request that ID tokens contain the user's username.
// Without the username scope being requested and allowed, the ID token will not contain the user's username.
// - groups: The client is allowed to request that ID tokens contain the user's group membership,
// if their group membership is discoverable by the Supervisor.
// Without the groups scope being requested and allowed, the ID token will not contain groups.
// +listType=set
// +kubebuilder:validation:MinItems=1
AllowedScopes []Scope `json:"allowedScopes"`
}
// OIDCClientStatus is a struct that describes the actual state of an OIDCClient.
type OIDCClientStatus struct {
// phase summarizes the overall status of the OIDCClient.
// +kubebuilder:default=Pending
// +kubebuilder:validation:Enum=Pending;Ready;Error
Phase OIDCClientPhase `json:"phase,omitempty"`
// conditions represent the observations of an OIDCClient's current state.
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// totalClientSecrets is the current number of client secrets that are detected for this OIDCClient.
// +optional
TotalClientSecrets int32 `json:"totalClientSecrets"` // do not omitempty to allow it to show in the printer column even when it is 0
}
// OIDCClient describes the configuration of an OIDC client.
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:resource:categories=pinniped
// +kubebuilder:printcolumn:name="Privileged Scopes",type=string,JSONPath=`.spec.allowedScopes[?(@ == "pinniped:request-audience")]`
// +kubebuilder:printcolumn:name="Client Secrets",type=integer,JSONPath=`.status.totalClientSecrets`
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// +kubebuilder:subresource:status
type OIDCClient struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec of the OIDC client.
Spec OIDCClientSpec `json:"spec"`
// Status of the OIDC client.
Status OIDCClientStatus `json:"status,omitempty"`
}
// List of OIDCClient objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type OIDCClientList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []OIDCClient `json:"items"`
}

View File

@@ -15,68 +15,11 @@ const (
// 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 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 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"
// IDTokenClaimIssuer is name of the issuer claim defined by the OIDC spec.
IDTokenClaimIssuer = "iss"
// IDTokenClaimSubject is name of the subject claim defined by the OIDC spec.
IDTokenClaimSubject = "sub"
// IDTokenClaimAuthorizedParty is name of the authorized party claim defined by the OIDC spec.
IDTokenClaimAuthorizedParty = "azp"
// IDTokenClaimUsername is the name of a custom claim in the downstream ID token whose value will contain the user's
// username which was mapped from the upstream identity provider.
IDTokenClaimUsername = "username"
// IDTokenClaimGroups is the name of a custom claim in the downstream ID token whose value will contain the user's
// group names which were mapped from the upstream identity provider.
IDTokenClaimGroups = "groups"
// GrantTypeAuthorizationCode is the name of the grant type for authorization code flows defined by the OIDC spec.
GrantTypeAuthorizationCode = "authorization_code"
// GrantTypeRefreshToken is the name of the grant type for refresh flow defined by the OIDC spec.
GrantTypeRefreshToken = "refresh_token"
// GrantTypeTokenExchange is the name of a custom grant type for RFC8693 token exchanges.
GrantTypeTokenExchange = "urn:ietf:params:oauth:grant-type:token-exchange" //nolint:gosec // this is not a credential
// ScopeOpenID is name of the openid scope defined by the OIDC spec.
ScopeOpenID = "openid"
// ScopeOfflineAccess is name of the offline access scope defined by the OIDC spec, used for requesting refresh
// tokens.
ScopeOfflineAccess = "offline_access"
// ScopeEmail is name of the email scope defined by the OIDC spec.
ScopeEmail = "email"
// ScopeProfile is name of the profile scope defined by the OIDC spec.
ScopeProfile = "profile"
// ScopeUsername is the name of a custom scope that determines whether the username claim will be returned inside
// ID tokens.
ScopeUsername = "username"
// ScopeGroups is the name of a custom scope that determines whether the groups claim will be returned inside
// ID tokens.
ScopeGroups = "groups"
// ScopeRequestAudience is the name of a custom scope that determines whether a RFC8693 token exchange is allowed to
// be used to request a different audience.
ScopeRequestAudience = "pinniped:request-audience"
// ClientIDPinnipedCLI is the client ID of the statically defined public OIDC client which is used by the CLI.
ClientIDPinnipedCLI = "pinniped-cli"
// ClientIDRequiredOIDCClientPrefix is the required prefix for the metadata.name of OIDCClient CRs.
ClientIDRequiredOIDCClientPrefix = "client.oauth.pinniped.dev-"
)

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// 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.
@@ -8,16 +8,14 @@ import (
"encoding/base64"
"encoding/json"
"io"
"io/ioutil"
"log"
"math"
"os"
"time"
// this side effect import ensures that we use fipsonly crypto in fips_strict mode.
_ "go.pinniped.dev/internal/crypto/ptls"
)
//nolint:gochecknoglobals // these are swapped during unit tests.
//nolint: gochecknoglobals // these are swapped during unit tests.
var (
getenv = os.Getenv
fail = log.Fatalf
@@ -34,11 +32,11 @@ func main() {
case "sleep":
sleep(math.MaxInt64)
case "print":
certBytes, err := os.ReadFile(getenv("CERT_PATH"))
certBytes, err := ioutil.ReadFile(getenv("CERT_PATH"))
if err != nil {
fail("could not read CERT_PATH: %v", err)
}
keyBytes, err := os.ReadFile(getenv("KEY_PATH"))
keyBytes, err := ioutil.ReadFile(getenv("KEY_PATH"))
if err != nil {
fail("could not read KEY_PATH: %v", err)
}

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// 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.
@@ -8,23 +8,20 @@
package main
import (
"fmt"
"os"
"path/filepath"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/klog/v2"
// this side effect import ensures that we use fipsonly crypto in fips_strict mode.
concierge "go.pinniped.dev/internal/concierge/server"
_ "go.pinniped.dev/internal/crypto/ptls"
lua "go.pinniped.dev/internal/localuserauthenticator"
"go.pinniped.dev/internal/plog"
supervisor "go.pinniped.dev/internal/supervisor/server"
)
//nolint:gochecknoglobals // these are swapped during unit tests.
//nolint: gochecknoglobals // these are swapped during unit tests.
var (
fail = plog.Fatal
fail = klog.Fatalf
subcommands = map[string]func(){
"pinniped-concierge": concierge.Main,
"pinniped-supervisor": supervisor.Main,
@@ -34,11 +31,11 @@ var (
func main() {
if len(os.Args) == 0 {
fail(fmt.Errorf("missing os.Args"))
fail("missing os.Args")
}
binary := filepath.Base(os.Args[0])
if subcommands[binary] == nil {
fail(fmt.Errorf("must be invoked as one of %v, not %q", sets.StringKeySet(subcommands).List(), binary))
fail("must be invoked as one of %v, not %q", sets.StringKeySet(subcommands).List(), binary)
}
subcommands[binary]()
}

View File

@@ -43,11 +43,8 @@ func TestEntrypoint(t *testing.T) {
var logBuf bytes.Buffer
testLog := log.New(&logBuf, "", 0)
exited := "exiting via fatal"
fail = func(err error, keysAndValues ...interface{}) {
testLog.Print(err)
if len(keysAndValues) > 0 {
testLog.Print(keysAndValues...)
}
fail = func(format string, v ...interface{}) {
testLog.Printf(format, v...)
panic(exited)
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
@@ -7,7 +7,7 @@ import (
"github.com/spf13/cobra"
)
//nolint:gochecknoglobals
//nolint: gochecknoglobals
var alphaCmd = &cobra.Command{
Use: "alpha",
Short: "alpha",
@@ -16,7 +16,7 @@ var alphaCmd = &cobra.Command{
Hidden: true,
}
//nolint:gochecknoinits
//nolint: gochecknoinits
func init() {
rootCmd.AddCommand(alphaCmd)
}

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
@@ -8,7 +8,7 @@ import (
"crypto/x509"
"flag"
"fmt"
"os"
"io/ioutil"
"strings"
"github.com/spf13/pflag"
@@ -85,7 +85,7 @@ func (f *caBundleFlag) String() string {
}
func (f *caBundleFlag) Set(path string) error {
pem, err := os.ReadFile(path)
pem, err := ioutil.ReadFile(path)
if err != nil {
return fmt.Errorf("could not read CA bundle path: %w", err)
}

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
@@ -6,7 +6,7 @@ package cmd
import (
"bytes"
"fmt"
"os"
"io/ioutil"
"path/filepath"
"testing"
"time"
@@ -54,10 +54,10 @@ func TestCABundleFlag(t *testing.T) {
require.NoError(t, err)
tmpdir := testutil.TempDir(t)
emptyFilePath := filepath.Join(tmpdir, "empty")
require.NoError(t, os.WriteFile(emptyFilePath, []byte{}, 0600))
require.NoError(t, ioutil.WriteFile(emptyFilePath, []byte{}, 0600))
testCAPath := filepath.Join(tmpdir, "testca.pem")
require.NoError(t, os.WriteFile(testCAPath, testCA.Bundle(), 0600))
require.NoError(t, ioutil.WriteFile(testCAPath, testCA.Bundle(), 0600))
f := caBundleFlag{}
require.Equal(t, "path", f.Type())

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
@@ -14,7 +14,7 @@ import (
"github.com/spf13/cobra/doc"
)
//nolint:gochecknoinits
//nolint: gochecknoinits
func init() {
rootCmd.AddCommand(generateMarkdownHelpCommand())
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
@@ -7,10 +7,10 @@ import (
"github.com/spf13/cobra"
)
//nolint:gochecknoglobals
//nolint: gochecknoglobals
var getCmd = &cobra.Command{Use: "get", Short: "get"}
//nolint:gochecknoinits
//nolint: gochecknoinits
func init() {
rootCmd.AddCommand(getCmd)
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
@@ -10,13 +10,17 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"strings"
"time"
coreosoidc "github.com/coreos/go-oidc/v3/oidc"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/go-logr/logr"
"github.com/go-logr/stdr"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
@@ -27,28 +31,26 @@ 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"
oidcapi "go.pinniped.dev/generated/latest/apis/supervisor/oidc"
conciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
"go.pinniped.dev/internal/groupsuffix"
"go.pinniped.dev/internal/net/phttp"
"go.pinniped.dev/internal/plog"
)
type kubeconfigDeps struct {
getPathToSelf func() (string, error)
getClientset getConciergeClientsetFunc
log plog.MinLogger
log logr.Logger
}
func kubeconfigRealDeps() kubeconfigDeps {
return kubeconfigDeps{
getPathToSelf: os.Executable,
getClientset: getRealConciergeClientset,
log: plog.New(),
log: stdr.New(log.New(os.Stderr, "", 0)),
}
}
//nolint:gochecknoinits
//nolint: gochecknoinits
func init() {
getCmd.AddCommand(kubeconfigCommand(kubeconfigRealDeps()))
}
@@ -126,9 +128,9 @@ func kubeconfigCommand(deps kubeconfigDeps) *cobra.Command {
f.Var(&flags.concierge.mode, "concierge-mode", "Concierge mode of operation")
f.StringVar(&flags.oidc.issuer, "oidc-issuer", "", "OpenID Connect issuer URL (default: autodiscover)")
f.StringVar(&flags.oidc.clientID, "oidc-client-id", oidcapi.ClientIDPinnipedCLI, "OpenID Connect client ID (default: autodiscover)")
f.StringVar(&flags.oidc.clientID, "oidc-client-id", "pinniped-cli", "OpenID Connect client ID (default: autodiscover)")
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{oidcapi.ScopeOfflineAccess, oidcapi.ScopeOpenID, oidcapi.ScopeRequestAudience, oidcapi.ScopeUsername, oidcapi.ScopeGroups}, "OpenID Connect scopes to request during login")
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")
@@ -169,15 +171,11 @@ func kubeconfigCommand(deps kubeconfigDeps) *cobra.Command {
return cmd
}
//nolint:funlen
func runGetKubeconfig(ctx context.Context, out io.Writer, deps kubeconfigDeps, flags getKubeconfigParams) error {
ctx, cancel := context.WithTimeout(ctx, flags.timeout)
defer cancel()
// the log statements in this file assume that Info logs are unconditionally printed so we set the global level to info
if err := plog.ValidateAndSetLogLevelAndFormatGlobally(ctx, plog.LogSpec{Level: plog.LevelInfo, Format: plog.FormatCLI}); err != nil {
return err
}
// Validate api group suffix and immediately return an error if it is invalid.
if err := groupsuffix.Validate(flags.concierge.apiGroupSuffix); err != nil {
return fmt.Errorf("invalid API group suffix: %w", err)
@@ -236,7 +234,7 @@ func runGetKubeconfig(ctx context.Context, out io.Writer, deps kubeconfigDeps, f
// 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, deps.log); err != nil {
if err := discoverSupervisorUpstreamIDP(ctx, &flags); err != nil {
return err
}
}
@@ -330,9 +328,6 @@ func newExecConfig(deps kubeconfigDeps, flags getKubeconfigParams) (*clientcmdap
execConfig.Args = append(execConfig.Args, "--debug-session-cache")
}
if flags.oidc.requestAudience != "" {
if strings.Contains(flags.oidc.requestAudience, ".pinniped.dev") {
return nil, fmt.Errorf("request audience is not allowed to include the substring '.pinniped.dev': %s", flags.oidc.requestAudience)
}
execConfig.Args = append(execConfig.Args, "--request-audience="+flags.oidc.requestAudience)
}
if flags.oidc.upstreamIDPName != "" {
@@ -404,7 +399,7 @@ func waitForCredentialIssuer(ctx context.Context, clientset conciergeclientset.I
return credentialIssuer, nil
}
func discoverConciergeParams(credentialIssuer *configv1alpha1.CredentialIssuer, flags *getKubeconfigParams, v1Cluster *clientcmdapi.Cluster, log plog.MinLogger) error {
func discoverConciergeParams(credentialIssuer *configv1alpha1.CredentialIssuer, flags *getKubeconfigParams, v1Cluster *clientcmdapi.Cluster, log logr.Logger) error {
// Autodiscover the --concierge-mode.
frontend, err := getConciergeFrontend(credentialIssuer, flags.concierge.mode)
if err != nil {
@@ -452,7 +447,7 @@ func discoverConciergeParams(credentialIssuer *configv1alpha1.CredentialIssuer,
return nil
}
func logStrategies(credentialIssuer *configv1alpha1.CredentialIssuer, log plog.MinLogger) {
func logStrategies(credentialIssuer *configv1alpha1.CredentialIssuer, log logr.Logger) {
for _, strategy := range credentialIssuer.Status.Strategies {
log.Info("found CredentialIssuer strategy",
"type", strategy.Type,
@@ -463,7 +458,7 @@ func logStrategies(credentialIssuer *configv1alpha1.CredentialIssuer, log plog.M
}
}
func discoverAuthenticatorParams(authenticator metav1.Object, flags *getKubeconfigParams, log plog.MinLogger) error {
func discoverAuthenticatorParams(authenticator metav1.Object, flags *getKubeconfigParams, log logr.Logger) error {
switch auth := authenticator.(type) {
case *conciergev1alpha1.WebhookAuthenticator:
// If the --concierge-authenticator-type/--concierge-authenticator-name flags were not set explicitly, set
@@ -562,7 +557,7 @@ func newExecKubeconfig(cluster *clientcmdapi.Cluster, execConfig *clientcmdapi.E
}
}
func lookupCredentialIssuer(clientset conciergeclientset.Interface, name string, log plog.MinLogger) (*configv1alpha1.CredentialIssuer, error) {
func lookupCredentialIssuer(clientset conciergeclientset.Interface, name string, log logr.Logger) (*configv1alpha1.CredentialIssuer, error) {
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*20)
defer cancelFunc()
@@ -588,7 +583,7 @@ func lookupCredentialIssuer(clientset conciergeclientset.Interface, name string,
return result, nil
}
func lookupAuthenticator(clientset conciergeclientset.Interface, authType, authName string, log plog.MinLogger) (metav1.Object, error) {
func lookupAuthenticator(clientset conciergeclientset.Interface, authType, authName string, log logr.Logger) (metav1.Object, error) {
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*20)
defer cancelFunc()
@@ -649,7 +644,7 @@ func writeConfigAsYAML(out io.Writer, config clientcmdapi.Config) error {
return nil
}
func validateKubeconfig(ctx context.Context, flags getKubeconfigParams, kubeconfig clientcmdapi.Config, log plog.MinLogger) error {
func validateKubeconfig(ctx context.Context, flags getKubeconfigParams, kubeconfig clientcmdapi.Config, log logr.Logger) error {
if flags.skipValidate {
return nil
}
@@ -712,7 +707,7 @@ func validateKubeconfig(ctx context.Context, flags getKubeconfigParams, kubeconf
log.Info("validated connection to the cluster", "attempts", attempts)
return nil
}
log.Info("could not connect to cluster, retrying...", "error", err, "attempts", attempts, "remaining", time.Until(deadline).Round(time.Second).String())
log.Error(err, "could not connect to cluster, retrying...", "attempts", attempts, "remaining", time.Until(deadline).Round(time.Second).String())
}
}
}
@@ -732,7 +727,7 @@ func hasPendingStrategy(credentialIssuer *configv1alpha1.CredentialIssuer) bool
return false
}
func discoverSupervisorUpstreamIDP(ctx context.Context, flags *getKubeconfigParams, log plog.MinLogger) error {
func discoverSupervisorUpstreamIDP(ctx context.Context, flags *getKubeconfigParams) error {
httpClient, err := newDiscoveryHTTPClient(flags.oidc.caBundle)
if err != nil {
return err
@@ -764,7 +759,7 @@ func discoverSupervisorUpstreamIDP(ctx context.Context, flags *getKubeconfigPara
return err
}
selectedIDPFlow, err := selectUpstreamIDPFlow(discoveredIDPFlows, selectedIDPName, selectedIDPType, flags.oidc.upstreamIDPFlow, log)
selectedIDPFlow, err := selectUpstreamIDPFlow(discoveredIDPFlows, selectedIDPName, selectedIDPType, flags.oidc.upstreamIDPFlow)
if err != nil {
return err
}
@@ -787,7 +782,7 @@ func newDiscoveryHTTPClient(caBundleFlag caBundleFlag) (*http.Client, error) {
}
func discoverIDPsDiscoveryEndpointURL(ctx context.Context, issuer string, httpClient *http.Client) (string, error) {
discoveredProvider, err := coreosoidc.NewProvider(coreosoidc.ClientContext(ctx, httpClient), issuer)
discoveredProvider, err := oidc.NewProvider(oidc.ClientContext(ctx, httpClient), issuer)
if err != nil {
return "", fmt.Errorf("while fetching OIDC discovery data from issuer: %w", err)
}
@@ -818,7 +813,7 @@ func discoverAllAvailableSupervisorUpstreamIDPs(ctx context.Context, pinnipedIDP
return nil, fmt.Errorf("unable to fetch IDP discovery data from issuer: unexpected http response status: %s", response.Status)
}
rawBody, err := io.ReadAll(response.Body)
rawBody, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, fmt.Errorf("unable to fetch IDP discovery data from issuer: could not read response body: %w", err)
}
@@ -904,7 +899,7 @@ func selectUpstreamIDPNameAndType(pinnipedIDPs []idpdiscoveryv1alpha1.PinnipedID
}
}
func selectUpstreamIDPFlow(discoveredIDPFlows []idpdiscoveryv1alpha1.IDPFlow, selectedIDPName string, selectedIDPType idpdiscoveryv1alpha1.IDPType, specifiedFlow string, log plog.MinLogger) (idpdiscoveryv1alpha1.IDPFlow, error) {
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.
@@ -928,9 +923,10 @@ func selectUpstreamIDPFlow(discoveredIDPFlows []idpdiscoveryv1alpha1.IDPFlow, se
return discoveredIDPFlows[0], nil
default:
// The user did not specify a flow, and more than one was found.
log.Info("multiple client flows found, selecting first value as default",
"idpName", selectedIDPName, "idpType", selectedIDPType,
"selectedFlow", discoveredIDPFlows[0].String(), "availableFlows", discoveredIDPFlows)
return discoveredIDPFlows[0], nil
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

@@ -7,8 +7,8 @@ import (
"bytes"
"encoding/base64"
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"testing"
"time"
@@ -34,12 +34,12 @@ func TestGetKubeconfig(t *testing.T) {
require.NoError(t, err)
tmpdir := testutil.TempDir(t)
testOIDCCABundlePath := filepath.Join(tmpdir, "testca.pem")
require.NoError(t, os.WriteFile(testOIDCCABundlePath, testOIDCCA.Bundle(), 0600))
require.NoError(t, ioutil.WriteFile(testOIDCCABundlePath, testOIDCCA.Bundle(), 0600))
testConciergeCA, err := certauthority.New("Test Concierge CA", 1*time.Hour)
require.NoError(t, err)
testConciergeCABundlePath := filepath.Join(tmpdir, "testconciergeca.pem")
require.NoError(t, os.WriteFile(testConciergeCABundlePath, testConciergeCA.Bundle(), 0600))
require.NoError(t, ioutil.WriteFile(testConciergeCABundlePath, testConciergeCA.Bundle(), 0600))
credentialIssuer := func() runtime.Object {
return &configv1alpha1.CredentialIssuer{
@@ -142,7 +142,7 @@ func TestGetKubeconfig(t *testing.T) {
--oidc-issuer string OpenID Connect issuer URL (default: autodiscover)
--oidc-listen-port uint16 TCP port for localhost listener (authorization code flow only)
--oidc-request-audience string Request a token with an alternate audience using RFC8693 token exchange
--oidc-scopes strings OpenID Connect scopes to request during login (default [offline_access,openid,pinniped:request-audience,username,groups])
--oidc-scopes strings OpenID Connect scopes to request during login (default [offline_access,openid,pinniped:request-audience])
--oidc-session-cache string Path to OpenID Connect session cache file
--oidc-skip-browser During OpenID Connect login, skip opening the browser (just print the URL)
-o, --output string Output file path (default: stdout)
@@ -639,77 +639,6 @@ func TestGetKubeconfig(t *testing.T) {
return `Error: tried to autodiscover --oidc-ca-bundle, but JWTAuthenticator test-authenticator has invalid spec.tls.certificateAuthorityData: illegal base64 data at input byte 7` + "\n"
},
},
{
name: "autodetect JWT authenticator, invalid substring in audience",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
&conciergev1alpha1.JWTAuthenticator{
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
Spec: conciergev1alpha1.JWTAuthenticatorSpec{
Issuer: issuerURL,
Audience: "some-test-audience.pinniped.dev-invalid-substring",
TLS: &conciergev1alpha1.TLSSpec{
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(issuerCABundle)),
},
},
},
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="some-test-audience.pinniped.dev-invalid-substring"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: request audience is not allowed to include the substring '.pinniped.dev': some-test-audience.pinniped.dev-invalid-substring` + "\n"
},
},
{
name: "autodetect JWT authenticator, override audience value, invalid substring in audience override value",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--oidc-request-audience", "some-test-audience.pinniped.dev-invalid-substring",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: request audience is not allowed to include the substring '.pinniped.dev': some-test-audience.pinniped.dev-invalid-substring` + "\n"
},
},
{
name: "fail to get self-path",
args: func(issuerCABundle string, issuerURL string) []string {
@@ -1026,7 +955,7 @@ func TestGetKubeconfig(t *testing.T) {
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return fmt.Sprintf("Error: while fetching OIDC discovery data from issuer: Get \"%s/.well-known/openid-configuration\": %s\n", issuerURL, testutil.X509UntrustedCertError("Acme Co"))
return fmt.Sprintf("Error: while fetching OIDC discovery data from issuer: Get \"%s/.well-known/openid-configuration\": x509: certificate signed by unknown authority\n", issuerURL)
},
},
{
@@ -1332,52 +1261,13 @@ func TestGetKubeconfig(t *testing.T) {
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap", "flows": ["cli_password", "flow2"]}
{"name": "some-oidc-idp", "type": "oidc", "flows": ["flow1", "flow2"]}
]
}`),
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,username,groups
- --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: []
installHint: The pinniped CLI does not appear to be installed. See https://get.pinniped.dev/cli
for more details
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
wantLogs: func(_ string, _ string) []string {
return []string{`"level"=0 "msg"="multiple client flows found, selecting first value as default" ` +
`"availableFlows"=["cli_password","flow2"] "idpName"="some-ldap-idp" "idpType"="ldap" "selectedFlow"="cli_password"`}
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"
},
},
{
@@ -1567,7 +1457,7 @@ func TestGetKubeconfig(t *testing.T) {
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
command: '.../path/to/pinniped'
@@ -1648,7 +1538,7 @@ func TestGetKubeconfig(t *testing.T) {
- --credential-cache=/path/to/cache/dir/credentials.yaml
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --skip-browser
- --skip-listen
- --listen-port=1234
@@ -1766,7 +1656,7 @@ func TestGetKubeconfig(t *testing.T) {
- --concierge-ca-bundle-data=%s
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
command: '.../path/to/pinniped'
@@ -1875,7 +1765,7 @@ func TestGetKubeconfig(t *testing.T) {
- --concierge-ca-bundle-data=dGVzdC1jb25jaWVyZ2UtY2E=
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
command: '.../path/to/pinniped'
@@ -1952,7 +1842,7 @@ func TestGetKubeconfig(t *testing.T) {
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
- --upstream-identity-provider-name=some-ldap-idp
@@ -2031,7 +1921,7 @@ func TestGetKubeconfig(t *testing.T) {
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
- --upstream-identity-provider-name=some-oidc-idp
@@ -2108,7 +1998,7 @@ func TestGetKubeconfig(t *testing.T) {
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
command: '.../path/to/pinniped'
@@ -2181,7 +2071,7 @@ func TestGetKubeconfig(t *testing.T) {
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
command: '.../path/to/pinniped'
@@ -2261,7 +2151,7 @@ func TestGetKubeconfig(t *testing.T) {
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
command: '.../path/to/pinniped'
@@ -2336,7 +2226,7 @@ func TestGetKubeconfig(t *testing.T) {
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
- --upstream-identity-provider-name=some-oidc-idp
@@ -2419,7 +2309,7 @@ func TestGetKubeconfig(t *testing.T) {
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
- --upstream-identity-provider-name=some-oidc-idp
@@ -2479,7 +2369,7 @@ func TestGetKubeconfig(t *testing.T) {
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
@@ -2540,7 +2430,7 @@ func TestGetKubeconfig(t *testing.T) {
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
@@ -2601,7 +2491,7 @@ func TestGetKubeconfig(t *testing.T) {
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
@@ -2663,7 +2553,7 @@ func TestGetKubeconfig(t *testing.T) {
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
@@ -2725,7 +2615,7 @@ func TestGetKubeconfig(t *testing.T) {
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
@@ -2786,7 +2676,7 @@ func TestGetKubeconfig(t *testing.T) {
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
@@ -2846,7 +2736,7 @@ func TestGetKubeconfig(t *testing.T) {
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience,username,groups
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
@@ -2960,7 +2850,7 @@ func TestGetKubeconfig(t *testing.T) {
})
issuerEndpointPtr = &issuerEndpoint
testLog := testlogger.NewLegacy(t) //nolint:staticcheck // old test with lots of log statements
testLog := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
cmd := kubeconfigCommand(kubeconfigDeps{
getPathToSelf: func() (string, error) {
if tt.getPathToSelfErr != nil {

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
@@ -9,7 +9,7 @@ import (
"k8s.io/client-go/tools/auth/exec"
)
//nolint:gochecknoglobals
//nolint: gochecknoglobals
var loginCmd = &cobra.Command{
Use: "login",
Short: "login",
@@ -18,7 +18,7 @@ var loginCmd = &cobra.Command{
Hidden: true, // These commands are not really meant to be used directly by users, so it's confusing to have them discoverable.
}
//nolint:gochecknoinits
//nolint: gochecknoinits
func init() {
rootCmd.AddCommand(loginCmd)
}

View File

@@ -9,18 +9,20 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientauthv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
"k8s.io/klog/v2/klogr"
idpdiscoveryv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idpdiscovery/v1alpha1"
oidcapi "go.pinniped.dev/generated/latest/apis/supervisor/oidc"
"go.pinniped.dev/internal/execcredcache"
"go.pinniped.dev/internal/groupsuffix"
"go.pinniped.dev/internal/net/phttp"
@@ -31,16 +33,7 @@ import (
"go.pinniped.dev/pkg/oidcclient/oidctypes"
)
const (
// The user may override the flow selection made by `--upstream-identity-provider-flow` using an env var.
// This allows the user to override their default flow selected inside their Pinniped-compatible kubeconfig file.
// A user might want to use this env var, for example, to choose the "browser_authcode" flow when using a kubeconfig
// which specifies "cli_password" when using an IDE plugin where there is no interactive CLI available. This allows
// the user to use one kubeconfig file for both flows.
upstreamIdentityProviderFlowEnvVarName = "PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW"
)
//nolint:gochecknoinits
//nolint: gochecknoinits
func init() {
loginCmd.AddCommand(oidcLoginCommand(oidcLoginCommandRealDeps()))
}
@@ -97,9 +90,9 @@ func oidcLoginCommand(deps oidcLoginCommandDeps) *cobra.Command {
conciergeNamespace string // unused now
)
cmd.Flags().StringVar(&flags.issuer, "issuer", "", "OpenID Connect issuer URL")
cmd.Flags().StringVar(&flags.clientID, "client-id", oidcapi.ClientIDPinnipedCLI, "OpenID Connect client ID")
cmd.Flags().StringVar(&flags.clientID, "client-id", "pinniped-cli", "OpenID Connect client ID")
cmd.Flags().Uint16Var(&flags.listenPort, "listen-port", 0, "TCP port for localhost listener (authorization code flow only)")
cmd.Flags().StringSliceVar(&flags.scopes, "scopes", []string{oidcapi.ScopeOfflineAccess, oidcapi.ScopeOpenID, oidcapi.ScopeRequestAudience, oidcapi.ScopeUsername, oidcapi.ScopeGroups}, "OIDC scopes to request during login")
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")
@@ -132,7 +125,7 @@ func oidcLoginCommand(deps oidcLoginCommandDeps) *cobra.Command {
}
func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLoginFlags) error { //nolint:funlen
pLogger, err := SetLogLevel(cmd.Context(), deps.lookupEnv)
pLogger, err := SetLogLevel(deps.lookupEnv, "Pinniped login: ")
if err != nil {
plog.WarningErr("Received error while setting log level", err)
}
@@ -140,11 +133,11 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
// Initialize the session cache.
var sessionOptions []filesession.Option
// If the hidden --debug-session-cache option is passed, log all the errors from the session cache.
// If the hidden --debug-session-cache option is passed, log all the errors from the session cache with klog.
if flags.debugSessionCache {
logger := plog.WithName("session")
logger := klogr.New().WithName("session")
sessionOptions = append(sessionOptions, filesession.WithErrorReporter(func(err error) {
logger.Error("error during session cache operation", err)
logger.Error(err, "error during session cache operation")
}))
}
sessionCache := filesession.New(flags.sessionCachePath, sessionOptions...)
@@ -152,7 +145,7 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
// Initialize the login handler.
opts := []oidcclient.Option{
oidcclient.WithContext(cmd.Context()),
oidcclient.WithLogger(plog.Logr()), //nolint:staticcheck // old code with lots of log statements
oidcclient.WithLogger(klogr.New()),
oidcclient.WithScopes(flags.scopes),
oidcclient.WithSessionCache(sessionCache),
}
@@ -173,7 +166,6 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
flowOpts, err := flowOptions(
idpdiscoveryv1alpha1.IDPType(flags.upstreamIdentityProviderType),
idpdiscoveryv1alpha1.IDPFlow(flags.upstreamIdentityProviderFlow),
deps,
)
if err != nil {
return err
@@ -259,21 +251,9 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
return json.NewEncoder(cmd.OutOrStdout()).Encode(cred)
}
func flowOptions(
requestedIDPType idpdiscoveryv1alpha1.IDPType,
requestedFlow idpdiscoveryv1alpha1.IDPFlow,
deps oidcLoginCommandDeps,
) ([]oidcclient.Option, error) {
func flowOptions(requestedIDPType idpdiscoveryv1alpha1.IDPType, requestedFlow idpdiscoveryv1alpha1.IDPFlow) ([]oidcclient.Option, error) {
useCLIFlow := []oidcclient.Option{oidcclient.WithCLISendingCredentials()}
// If the env var is set to override the --upstream-identity-provider-type flag, then override it.
flowOverride, hasFlowOverride := deps.lookupEnv(upstreamIdentityProviderFlowEnvVarName)
flowSource := "--upstream-identity-provider-flow"
if hasFlowOverride {
requestedFlow = idpdiscoveryv1alpha1.IDPFlow(flowOverride)
flowSource = upstreamIdentityProviderFlowEnvVarName
}
switch requestedIDPType {
case idpdiscoveryv1alpha1.IDPTypeOIDC:
switch requestedFlow {
@@ -283,21 +263,19 @@ func flowOptions(
return nil, nil // browser authcode flow is the default Option, so don't need to return an Option here
default:
return nil, fmt.Errorf(
"%s value not recognized for identity provider type %q: %s (supported values: %s)",
flowSource, requestedIDPType, requestedFlow,
strings.Join([]string{idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode.String(), idpdiscoveryv1alpha1.IDPFlowCLIPassword.String()}, ", "))
"--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:
return nil, nil // browser authcode flow is the default Option, so don't need to return an Option here
fallthrough // not supported for LDAP providers, so fallthrough to error case
default:
return nil, fmt.Errorf(
"%s value not recognized for identity provider type %q: %s (supported values: %s)",
flowSource, requestedIDPType, requestedFlow,
strings.Join([]string{idpdiscoveryv1alpha1.IDPFlowCLIPassword.String(), idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode.String()}, ", "))
"--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
@@ -316,7 +294,7 @@ func flowOptions(
func makeClient(caBundlePaths []string, caBundleData []string) (*http.Client, error) {
pool := x509.NewCertPool()
for _, p := range caBundlePaths {
pem, err := os.ReadFile(p)
pem, err := ioutil.ReadFile(p)
if err != nil {
return nil, fmt.Errorf("could not read --ca-bundle: %w", err)
}
@@ -348,26 +326,22 @@ func tokenCredential(token *oidctypes.Token) *clientauthv1beta1.ExecCredential {
return &cred
}
func SetLogLevel(ctx context.Context, lookupEnv func(string) (string, bool)) (plog.Logger, error) {
func SetLogLevel(lookupEnv func(string) (string, bool), prefix string) (plog.Logger, error) {
debug, _ := lookupEnv("PINNIPED_DEBUG")
if debug == "true" {
err := plog.ValidateAndSetLogLevelAndFormatGlobally(ctx, plog.LogSpec{Level: plog.LevelDebug, Format: plog.FormatCLI})
err := plog.ValidateAndSetLogLevelGlobally(plog.LevelDebug)
if err != nil {
return nil, err
}
}
logger := plog.New().WithName("pinniped-login")
logger := plog.New(prefix)
return logger, nil
}
/*
mustGetConfigDir returns a directory that follows the XDG base directory convention:
$XDG_CONFIG_HOME defines the base directory relative to which user specific configuration files should
be stored. If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config should be used.
[1] https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
*/
// mustGetConfigDir returns a directory that follows the XDG base directory convention:
// $XDG_CONFIG_HOME defines the base directory relative to which user specific configuration files should
// be stored. If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config should be used.
// [1] https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
func mustGetConfigDir() string {
const xdgAppName = "pinniped"

View File

@@ -8,22 +8,20 @@ import (
"context"
"encoding/base64"
"fmt"
"os"
"io/ioutil"
"path/filepath"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientauthv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
clocktesting "k8s.io/utils/clock/testing"
"k8s.io/klog/v2"
"go.pinniped.dev/internal/certauthority"
"go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/testutil/testlogger"
"go.pinniped.dev/pkg/conciergeclient"
"go.pinniped.dev/pkg/oidcclient"
"go.pinniped.dev/pkg/oidcclient/oidctypes"
@@ -36,14 +34,10 @@ func TestLoginOIDCCommand(t *testing.T) {
require.NoError(t, err)
tmpdir := testutil.TempDir(t)
testCABundlePath := filepath.Join(tmpdir, "testca.pem")
require.NoError(t, os.WriteFile(testCABundlePath, testCA.Bundle(), 0600))
require.NoError(t, ioutil.WriteFile(testCABundlePath, testCA.Bundle(), 0600))
time1 := time.Date(3020, 10, 12, 13, 14, 15, 16, time.UTC)
now, err := time.Parse(time.RFC3339Nano, "2028-10-11T23:37:26.953313745Z")
require.NoError(t, err)
nowStr := now.Local().Format(time.RFC1123)
tests := []struct {
name string
args []string
@@ -80,7 +74,7 @@ func TestLoginOIDCCommand(t *testing.T) {
--issuer string OpenID Connect issuer URL
--listen-port uint16 TCP port for localhost listener (authorization code flow only)
--request-audience string Request a token with an alternate audience using RFC8693 token exchange
--scopes strings OIDC scopes to request during login (default [offline_access,openid,pinniped:request-audience,username,groups])
--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')
@@ -148,7 +142,7 @@ func TestLoginOIDCCommand(t *testing.T) {
`),
},
{
name: "invalid upstream type is an error",
name: "invalid upstream type",
args: []string{
"--issuer", "test-issuer",
"--upstream-identity-provider-type", "invalid",
@@ -158,18 +152,6 @@ func TestLoginOIDCCommand(t *testing.T) {
Error: --upstream-identity-provider-type value not recognized: invalid (supported values: oidc, ldap, activedirectory)
`),
},
{
name: "invalid upstream type when flow override env var is used is still an error",
args: []string{
"--issuer", "test-issuer",
"--upstream-identity-provider-type", "invalid",
},
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "browser_authcode"},
wantError: true,
wantStderr: here.Doc(`
Error: --upstream-identity-provider-type value not recognized: invalid (supported values: oidc, ldap, activedirectory)
`),
},
{
name: "oidc upstream type with default flow is allowed",
args: []string{
@@ -205,32 +187,6 @@ func TestLoginOIDCCommand(t *testing.T) {
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 CLI flow in flow override env var 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
},
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "cli_password"},
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 with browser flow in flow override env var 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
},
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "browser_authcode"},
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{
@@ -245,21 +201,6 @@ func TestLoginOIDCCommand(t *testing.T) {
Error: --upstream-identity-provider-flow value not recognized for identity provider type "oidc": foobar (supported values: browser_authcode, cli_password)
`),
},
{
name: "oidc upstream type with unsupported flow in flow override env var is an error",
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
},
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "foo"},
wantError: true,
wantStderr: here.Doc(`
Error: PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW value not recognized for identity provider type "oidc": foo (supported values: browser_authcode, cli_password)
`),
},
{
name: "ldap upstream type with default flow is allowed",
args: []string{
@@ -294,71 +235,18 @@ func TestLoginOIDCCommand(t *testing.T) {
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 browser_authcode flow is allowed",
args: []string{
"--issuer", "test-issuer",
"--client-id", "test-client-id",
"--upstream-identity-provider-type", "ldap",
"--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: "ldap upstream type with CLI flow in flow override env var is allowed",
args: []string{
"--issuer", "test-issuer",
"--client-id", "test-client-id",
"--upstream-identity-provider-type", "ldap",
"--upstream-identity-provider-flow", "browser_authcode",
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
},
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "cli_password"},
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 browser_authcode flow in flow override env var 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
},
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "browser_authcode"},
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: "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", "foo",
"--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": foo (supported values: cli_password, browser_authcode)
`),
},
{
name: "ldap upstream type with unsupported flow in flow override env var is an error",
args: []string{
"--issuer", "test-issuer",
"--client-id", "test-client-id",
"--upstream-identity-provider-type", "ldap",
"--upstream-identity-provider-flow", "browser_authcode",
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
},
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "foo"},
wantError: true,
wantStderr: here.Doc(`
Error: PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW value not recognized for identity provider type "ldap": foo (supported values: cli_password, browser_authcode)
Error: --upstream-identity-provider-flow value not recognized for identity provider type "ldap": browser_authcode (supported values: [cli_password])
`),
},
{
@@ -373,71 +261,18 @@ func TestLoginOIDCCommand(t *testing.T) {
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 browser_authcode is allowed",
args: []string{
"--issuer", "test-issuer",
"--client-id", "test-client-id",
"--upstream-identity-provider-type", "activedirectory",
"--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: "active directory upstream type with CLI flow in flow override env var is allowed",
args: []string{
"--issuer", "test-issuer",
"--client-id", "test-client-id",
"--upstream-identity-provider-type", "activedirectory",
"--upstream-identity-provider-flow", "browser_authcode",
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
},
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "cli_password"},
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 browser_authcode in flow override env var 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
},
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "browser_authcode"},
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: "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", "foo",
"--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": foo (supported values: cli_password, browser_authcode)
`),
},
{
name: "active directory upstream type with unsupported flow in flow override env var is an error",
args: []string{
"--issuer", "test-issuer",
"--client-id", "test-client-id",
"--upstream-identity-provider-type", "activedirectory",
"--upstream-identity-provider-flow", "browser_authcode",
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
},
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "foo"},
wantError: true,
wantStderr: here.Doc(`
Error: PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW value not recognized for identity provider type "activedirectory": foo (supported values: cli_password, browser_authcode)
Error: --upstream-identity-provider-flow value not recognized for identity provider type "activedirectory": browser_authcode (supported values: [cli_password])
`),
},
{
@@ -483,8 +318,8 @@ func TestLoginOIDCCommand(t *testing.T) {
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",
wantLogs: []string{
nowStr + ` pinniped-login cmd/login_oidc.go:231 Performing OIDC login {"issuer": "test-issuer", "client id": "test-client-id"}`,
nowStr + ` pinniped-login cmd/login_oidc.go:251 No concierge configured, skipping token credential exchange`,
"\"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\"",
},
},
{
@@ -513,20 +348,18 @@ func TestLoginOIDCCommand(t *testing.T) {
wantOptionsCount: 11,
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"token":"exchanged-token"}}` + "\n",
wantLogs: []string{
nowStr + ` pinniped-login cmd/login_oidc.go:231 Performing OIDC login {"issuer": "test-issuer", "client id": "test-client-id"}`,
nowStr + ` pinniped-login cmd/login_oidc.go:241 Exchanging token for cluster credential {"endpoint": "https://127.0.0.1:1234/", "authenticator type": "webhook", "authenticator name": "test-authenticator"}`,
nowStr + ` pinniped-login cmd/login_oidc.go:249 Successfully exchanged token for cluster credential.`,
nowStr + ` pinniped-login cmd/login_oidc.go:256 caching cluster credential for future use.`,
"\"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/\"",
"\"level\"=0 \"msg\"=\"Pinniped login: Successfully exchanged token for cluster credential.\"",
"\"level\"=0 \"msg\"=\"Pinniped login: caching cluster credential for future use.\"",
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
var buf bytes.Buffer
fakeClock := clocktesting.NewFakeClock(now)
ctx := plog.TestZapOverrides(context.Background(), t, &buf, nil, zap.WithClock(plog.ZapClock(fakeClock)))
testLogger := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
klog.SetLogger(testLogger.Logger)
var (
gotOptions []oidcclient.Option
)
@@ -571,7 +404,7 @@ func TestLoginOIDCCommand(t *testing.T) {
cmd.SetOut(&stdout)
cmd.SetErr(&stderr)
cmd.SetArgs(tt.args)
err = cmd.ExecuteContext(ctx)
err := cmd.Execute()
if tt.wantError {
require.Error(t, err)
} else {
@@ -581,15 +414,7 @@ func TestLoginOIDCCommand(t *testing.T) {
require.Equal(t, tt.wantStderr, stderr.String(), "unexpected stderr")
require.Len(t, gotOptions, tt.wantOptionsCount)
require.Equal(t, tt.wantLogs, logLines(buf.String()))
require.Equal(t, tt.wantLogs, testLogger.Lines())
})
}
}
func logLines(logs string) []string {
if len(logs) == 0 {
return nil
}
return strings.Split(strings.TrimSpace(logs), "\n")
}

View File

@@ -7,6 +7,7 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"time"
@@ -21,7 +22,7 @@ import (
"go.pinniped.dev/pkg/oidcclient/oidctypes"
)
//nolint:gochecknoinits
//nolint: gochecknoinits
func init() {
loginCmd.AddCommand(staticLoginCommand(staticLoginRealDeps()))
}
@@ -74,7 +75,7 @@ func staticLoginCommand(deps staticLoginDeps) *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.RunE = func(cmd *cobra.Command, args []string) error { return runStaticLogin(cmd, deps, flags) }
cmd.RunE = func(cmd *cobra.Command, args []string) error { return runStaticLogin(cmd.OutOrStdout(), deps, flags) }
mustMarkDeprecated(cmd, "concierge-namespace", "not needed anymore")
mustMarkHidden(cmd, "concierge-namespace")
@@ -82,9 +83,8 @@ func staticLoginCommand(deps staticLoginDeps) *cobra.Command {
return cmd
}
func runStaticLogin(cmd *cobra.Command, deps staticLoginDeps, flags staticLoginParams) error {
out := cmd.OutOrStdout()
pLogger, err := SetLogLevel(cmd.Context(), deps.lookupEnv)
func runStaticLogin(out io.Writer, deps staticLoginDeps, flags staticLoginParams) error {
pLogger, err := SetLogLevel(deps.lookupEnv, "Pinniped login: ")
if err != nil {
plog.WarningErr("Received error while setting log level", err)
}

View File

@@ -7,20 +7,21 @@ import (
"bytes"
"context"
"fmt"
"os"
"io/ioutil"
"path/filepath"
"testing"
"time"
"k8s.io/klog/v2"
"go.pinniped.dev/internal/testutil/testlogger"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientauthv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
clocktesting "k8s.io/utils/clock/testing"
"go.pinniped.dev/internal/certauthority"
"go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/pkg/conciergeclient"
)
@@ -32,11 +33,7 @@ func TestLoginStaticCommand(t *testing.T) {
require.NoError(t, err)
tmpdir := testutil.TempDir(t)
testCABundlePath := filepath.Join(tmpdir, "testca.pem")
require.NoError(t, os.WriteFile(testCABundlePath, testCA.Bundle(), 0600))
now, err := time.Parse(time.RFC3339Nano, "2038-12-07T23:37:26.953313745Z")
require.NoError(t, err)
nowStr := now.Local().Format(time.RFC1123)
require.NoError(t, ioutil.WriteFile(testCABundlePath, testCA.Bundle(), 0600))
tests := []struct {
name string
@@ -139,9 +136,7 @@ func TestLoginStaticCommand(t *testing.T) {
wantStderr: here.Doc(`
Error: could not complete Concierge credential exchange: some concierge error
`),
wantLogs: []string{
nowStr + ` pinniped-login cmd/login_static.go:147 exchanging static token for cluster credential {"endpoint": "https://127.0.0.1/", "authenticator type": "webhook", "authenticator name": "test-authenticator"}`,
},
wantLogs: []string{"\"level\"=0 \"msg\"=\"Pinniped login: exchanging static token for cluster credential\" \"authenticator name\"=\"test-authenticator\" \"authenticator type\"=\"webhook\" \"endpoint\"=\"https://127.0.0.1/\""},
},
{
name: "invalid API group suffix",
@@ -170,10 +165,8 @@ func TestLoginStaticCommand(t *testing.T) {
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
var buf bytes.Buffer
fakeClock := clocktesting.NewFakeClock(now)
ctx := plog.TestZapOverrides(context.Background(), t, &buf, nil, zap.WithClock(plog.ZapClock(fakeClock)))
testLogger := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
klog.SetLogger(testLogger.Logger)
cmd := staticLoginCommand(staticLoginDeps{
lookupEnv: func(s string) (string, bool) {
v, ok := tt.env[s]
@@ -201,7 +194,7 @@ func TestLoginStaticCommand(t *testing.T) {
cmd.SetOut(&stdout)
cmd.SetErr(&stderr)
cmd.SetArgs(tt.args)
err := cmd.ExecuteContext(ctx)
err := cmd.Execute()
if tt.wantError {
require.Error(t, err)
} else {
@@ -210,7 +203,7 @@ func TestLoginStaticCommand(t *testing.T) {
require.Equal(t, tt.wantStdout, stdout.String(), "unexpected stdout")
require.Equal(t, tt.wantStderr, stderr.String(), "unexpected stderr")
require.Equal(t, tt.wantLogs, logLines(buf.String()))
require.Equal(t, tt.wantLogs, testLogger.Lines())
})
}
}

153
cmd/pinniped/cmd/logout.go Normal file
View File

@@ -0,0 +1,153 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"errors"
"fmt"
"net/url"
"os"
"path/filepath"
"sort"
"go.pinniped.dev/internal/plog"
coreosoidc "github.com/coreos/go-oidc/v3/oidc"
"github.com/spf13/cobra"
"go.pinniped.dev/pkg/oidcclient"
"go.pinniped.dev/pkg/oidcclient/filesession"
)
//nolint: gochecknoinits
func init() {
rootCmd.AddCommand(newLogoutCommand())
}
type logoutFlags struct {
kubeconfigPath string
kubeconfigContextOverride string
}
// This implements client side logout-- i.e. deleting the cached tokens and certificates for a user
// without telling the supervisor to forget about the users tokens. From a user experience
// perspective these are identical, but it leaves orphaned tokens lying around that the supervisor
// won't garbage collect for up to 9 hours.
// Fosite supports token revocation requests ala https://tools.ietf.org/html/rfc7009#section-2.1
// with their TokenRevocationHandler, but we would also want to turn around and revoke the upstream
// tokens in the case of OIDC.
// That's something that could be done to improve security and stop storage from getting too
// big.
// It works by parsing the provided kubeconfig to get the arguments to pinniped login oidc,
// grabbing the issuer and the cache paths, then using that issuer to find and delete the entry
// in the session cache.
func newLogoutCommand() *cobra.Command {
cmd := &cobra.Command{
Args: cobra.NoArgs,
Use: "logout",
Short: "Terminate the current user's session.",
}
flags := &logoutFlags{}
cmd.Flags().StringVar(&flags.kubeconfigPath, "kubeconfig", os.Getenv("KUBECONFIG"), "Path to kubeconfig file")
cmd.Flags().StringVar(&flags.kubeconfigContextOverride, "kubeconfig-context", "", "Kubeconfig context name (default: current active context)")
cmd.RunE = func(cmd *cobra.Command, args []string) error {
return runLogout(flags)
}
return cmd
}
func runLogout(flags *logoutFlags) error {
pLogger, err := SetLogLevel(os.LookupEnv, "Pinniped logout: ")
if err != nil {
plog.WarningErr("Received error while setting log level", err)
}
clientConfig := newClientConfig(flags.kubeconfigPath, flags.kubeconfigContextOverride)
currentKubeConfig, err := clientConfig.RawConfig()
if err != nil {
return err
}
// start by getting the current context or another context if provided.
contextName := currentKubeConfig.CurrentContext
if len(flags.kubeconfigContextOverride) > 0 {
contextName = flags.kubeconfigContextOverride
}
kubeContext, ok := currentKubeConfig.Contexts[contextName]
if !ok {
return fmt.Errorf("couldn't find current context")
}
// then get the authinfo associated with that context.
authInfo := currentKubeConfig.AuthInfos[kubeContext.AuthInfo]
if authInfo == nil {
return fmt.Errorf("could not find auth info-- are you sure this is a Pinniped kubeconfig?")
}
// get the exec credential out of the authinfo and validate that it takes the shape of a pinniped login command.
exec := authInfo.Exec
if exec == nil {
return fmt.Errorf("could not find exec credential-- are you sure this is a Pinniped kubeconfig?")
}
execArgs := exec.Args
if execArgs == nil {
return fmt.Errorf("could not find exec credential arguments-- are you sure this is a Pinniped kubeconfig?")
}
// parse the arguments in the exec credential (which should be the pinniped login command).
loginCommand := oidcLoginCommand(oidcLoginCommandDeps{})
err = loginCommand.ParseFlags(execArgs)
if err != nil {
return err
}
// Get the issuer flag. If this doesn't exist we have no way to get in to the cache so we have to exit.
issuer := loginCommand.Flag("issuer").Value.String()
if issuer == "" {
return fmt.Errorf("could not find issuer-- are you sure this is a Pinniped kubeconfig?")
}
// Get the session cache. If it doesn't exist just use the default value.
sessionCachePath := loginCommand.Flag("session-cache").Value.String()
if sessionCachePath == "" {
sessionCachePath = filepath.Join(mustGetConfigDir(), "sessions.yaml")
}
// Get the credential cache. If it doesn't exist just use the default value.
credentialCachePath := loginCommand.Flag("credential-cache").Value.String()
if credentialCachePath == "" {
credentialCachePath = filepath.Join(mustGetConfigDir(), "credentials.yaml")
}
// TODO this should probably be a more targeted removal rather than the whole file...
// but that involves figuring out the cache key which is hard.
// Remove the credential cache that stores the users x509 certificates.
err = os.Remove(credentialCachePath)
// a not found error is fine and we should move on and try to delete the
// session cache if possible. Other errors might be a problem.
if err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
// Remove the cache entry for this issuer.
var sessionOptions []filesession.Option
sessionCache := filesession.New(sessionCachePath, sessionOptions...)
downstreamScopes := []string{coreosoidc.ScopeOfflineAccess, coreosoidc.ScopeOpenID, "pinniped:request-audience"}
sort.Strings(downstreamScopes)
sessionCacheKey := oidcclient.SessionCacheKey{
Issuer: issuer,
ClientID: "pinniped-cli",
Scopes: downstreamScopes,
RedirectURI: (&url.URL{Scheme: "http", Host: "localhost:0", Path: "/callback"}).String(),
}
deleted := sessionCache.DeleteToken(sessionCacheKey)
if deleted {
pLogger.Warning("Successfully logged out of session.")
} else {
// this is likely because you're already logged out, but you might still want to know.
pLogger.Warning("Could not find session to log out of.")
pLogger.Debug("debug info", "issuer", issuer, "session cache path", sessionCachePath, "credential cache path", credentialCachePath)
}
return nil
}

View File

@@ -4,14 +4,12 @@
package cmd
import (
"context"
"os"
"github.com/spf13/cobra"
"go.pinniped.dev/internal/plog"
)
//nolint:gochecknoglobals
//nolint: gochecknoglobals
var rootCmd = &cobra.Command{
Use: "pinniped",
Short: "pinniped",
@@ -21,11 +19,8 @@ var rootCmd = &cobra.Command{
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() error {
defer plog.Setup()()
// the context does not matter here because it is unused when CLI formatting is provided
if err := plog.ValidateAndSetLogLevelAndFormatGlobally(context.Background(), plog.LogSpec{Format: plog.FormatCLI}); err != nil {
return err
func Execute() {
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
return rootCmd.Execute()
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
@@ -10,7 +10,7 @@ import (
"k8s.io/component-base/version"
)
//nolint:gochecknoinits
//nolint: gochecknoinits
func init() {
rootCmd.AddCommand(newVersionCommand())
}

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
@@ -24,7 +24,7 @@ import (
"go.pinniped.dev/internal/here"
)
//nolint:gochecknoinits
//nolint: gochecknoinits
func init() {
rootCmd.AddCommand(newWhoamiCommand(getRealConciergeClientset))
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package main
@@ -9,11 +9,9 @@ import (
"github.com/pkg/browser"
"go.pinniped.dev/cmd/pinniped/cmd"
// this side effect import ensures that we use fipsonly crypto in fips_strict mode.
_ "go.pinniped.dev/internal/crypto/ptls"
)
//nolint:gochecknoinits
//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
@@ -21,7 +19,5 @@ func init() {
}
func main() {
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
cmd.Execute()
}

View File

@@ -1,9 +1,10 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
controller-gen.kubebuilder.io/version: v0.4.0
creationTimestamp: null
name: jwtauthenticators.authentication.concierge.pinniped.dev
spec:

View File

@@ -1,9 +1,10 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
controller-gen.kubebuilder.io/version: v0.4.0
creationTimestamp: null
name: webhookauthenticators.authentication.concierge.pinniped.dev
spec:

View File

@@ -1,9 +1,10 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
controller-gen.kubebuilder.io/version: v0.4.0
creationTimestamp: null
name: credentialissuers.config.concierge.pinniped.dev
spec:

View File

@@ -12,14 +12,7 @@ apiVersion: v1
kind: Namespace
metadata:
name: #@ data.values.namespace
labels:
_: #@ template.replace(labels())
#! When deploying onto a cluster which has PSAs enabled by default for namespaces,
#! effectively disable them for this namespace. The kube-cert-agent Deployment's pod
#! created by the Concierge in this namespace needs to be able to perform privileged
#! actions. The regular Concierge pod containers created by the Deployment below do
#! not need special privileges and are marked as such in their securityContext settings.
pod-security.kubernetes.io/enforce: privileged
labels: #@ labels()
#@ end
---
apiVersion: v1
@@ -93,14 +86,8 @@ data:
imagePullSecrets:
- image-pull-secret
(@ end @)
(@ if data.values.log_level or data.values.deprecated_log_format: @)
log:
(@ if data.values.log_level: @)
level: (@= getAndValidateLogLevel() @)
(@ end @)
(@ if data.values.deprecated_log_format: @)
format: (@= data.values.deprecated_log_format @)
(@ end @)
(@ if data.values.log_level: @)
logLevel: (@= getAndValidateLogLevel() @)
(@ end @)
---
#@ if data.values.image_pull_dockerconfigjson and data.values.image_pull_dockerconfigjson != "":
@@ -155,15 +142,6 @@ spec:
imagePullPolicy: IfNotPresent
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
allowPrivilegeEscalation: false
capabilities:
drop: [ "ALL" ]
#! seccompProfile was introduced in Kube v1.19. Using it on an older Kube version will result in a
#! kubectl validation error when installing via `kubectl apply`, which can be ignored using kubectl's
#! `--validate=false` flag. Note that installing via `kapp` does not complain about this validation error.
seccompProfile:
type: "RuntimeDefault"
resources:
requests:
cpu: "100m"

View File

@@ -54,10 +54,6 @@ api_serving_certificate_renew_before_seconds: 2160000
#! Specify the verbosity of logging: info ("nice to know" information), debug (developer
#! information), trace (timing information), all (kitchen sink).
log_level: #! By default, when this value is left unset, only warnings and errors are printed. There is no way to suppress warning and error logs.
#! Specify the format of logging: json (for machine parsable logs) and text (for legacy klog formatted logs).
#! By default, when this value is left unset, logs are formatted in json.
#! This configuration is deprecated and will be removed in a future release at which point logs will always be formatted as json.
deprecated_log_format:
run_as_user: 65532 #! run_as_user specifies the user ID that will own the process, see the Dockerfile for the reasoning behind this choice
run_as_group: 65532 #! run_as_group specifies the group ID that will own the process, see the Dockerfile for the reasoning behind this choice

View File

@@ -1,4 +1,4 @@
#! Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
#! SPDX-License-Identifier: Apache-2.0
#@ load("@ytt:data", "data")
@@ -65,17 +65,6 @@ spec:
imagePullPolicy: IfNotPresent
command:
- local-user-authenticator
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
allowPrivilegeEscalation: false
capabilities:
drop: [ "ALL" ]
#! seccompProfile was introduced in Kube v1.19. Using it on an older Kube version will result in a
#! kubectl validation error when installing via `kubectl apply`, which can be ignored using kubectl's
#! `--validate=false` flag. Note that installing via `kapp` does not complain about this validation error.
seccompProfile:
type: "RuntimeDefault"
---
apiVersion: v1
kind: Service

View File

@@ -1,9 +1,10 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
controller-gen.kubebuilder.io/version: v0.4.0
creationTimestamp: null
name: federationdomains.config.supervisor.pinniped.dev
spec:
@@ -76,13 +77,12 @@ spec:
so all issuers with the same DNS hostname must use the same
SecretName value even if they have different port numbers. \n
SecretName is not required when you would like to use only the
HTTP endpoints (e.g. when the HTTP listener is configured to
listen on loopback interfaces or UNIX domain sockets for traffic
from a service mesh sidecar). It is also not required when you
would like all requests to this OIDC Provider's HTTPS endpoints
to use the default TLS certificate, which is configured elsewhere.
\n When your Issuer URL's host is an IP address, then this field
is ignored. SNI does not work for IP addresses."
HTTP endpoints (e.g. when terminating TLS at an Ingress). It
is also not required when you would like all requests to this
OIDC Provider's HTTPS endpoints to use the default TLS certificate,
which is configured elsewhere. \n When your Issuer URL's host
is an IP address, then this field is ignored. SNI does not work
for IP addresses."
type: string
type: object
required:

View File

@@ -1,221 +0,0 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
creationTimestamp: null
name: oidcclients.config.supervisor.pinniped.dev
spec:
group: config.supervisor.pinniped.dev
names:
categories:
- pinniped
kind: OIDCClient
listKind: OIDCClientList
plural: oidcclients
singular: oidcclient
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .spec.allowedScopes[?(@ == "pinniped:request-audience")]
name: Privileged Scopes
type: string
- jsonPath: .status.totalClientSecrets
name: Client Secrets
type: integer
- jsonPath: .status.phase
name: Status
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1alpha1
schema:
openAPIV3Schema:
description: OIDCClient describes the configuration of an OIDC client.
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 of the OIDC client.
properties:
allowedGrantTypes:
description: "allowedGrantTypes is a list of the allowed grant_type
param values that should be accepted during OIDC flows with this
client. \n Must only contain the following values: - authorization_code:
allows the client to perform the authorization code grant flow,
i.e. allows the webapp to authenticate users. This grant must always
be listed. - refresh_token: allows the client to perform refresh
grants for the user to extend the user's session. This grant must
be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange:
allows the client to perform RFC8693 token exchange, which is a
step in the process to be able to get a cluster credential for the
user. This grant must be listed if allowedScopes lists pinniped:request-audience."
items:
enum:
- authorization_code
- refresh_token
- urn:ietf:params:oauth:grant-type:token-exchange
type: string
minItems: 1
type: array
x-kubernetes-list-type: set
allowedRedirectURIs:
description: allowedRedirectURIs is a list of the allowed redirect_uri
param values that should be accepted during OIDC flows with this
client. Any other uris will be rejected. Must be a URI with the
https scheme, unless the hostname is 127.0.0.1 or ::1 which may
use the http scheme. Port numbers are not required for 127.0.0.1
or ::1 and are ignored when checking for a matching redirect_uri.
items:
pattern: ^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/
type: string
minItems: 1
type: array
x-kubernetes-list-type: set
allowedScopes:
description: "allowedScopes is a list of the allowed scopes param
values that should be accepted during OIDC flows with this client.
\n Must only contain the following values: - openid: The client
is allowed to request ID tokens. ID tokens only include the required
claims by default (iss, sub, aud, exp, iat). This scope must always
be listed. - offline_access: The client is allowed to request an
initial refresh token during the authorization code grant flow.
This scope must be listed if allowedGrantTypes lists refresh_token.
- pinniped:request-audience: The client is allowed to request a
new audience value during a RFC8693 token exchange, which is a step
in the process to be able to get a cluster credential for the user.
openid, username and groups scopes must be listed when this scope
is present. This scope must be listed if allowedGrantTypes lists
urn:ietf:params:oauth:grant-type:token-exchange. - username: The
client is allowed to request that ID tokens contain the user's username.
Without the username scope being requested and allowed, the ID token
will not contain the user's username. - groups: The client is allowed
to request that ID tokens contain the user's group membership, if
their group membership is discoverable by the Supervisor. Without
the groups scope being requested and allowed, the ID token will
not contain groups."
items:
enum:
- openid
- offline_access
- username
- groups
- pinniped:request-audience
type: string
minItems: 1
type: array
x-kubernetes-list-type: set
required:
- allowedGrantTypes
- allowedRedirectURIs
- allowedScopes
type: object
status:
description: Status of the OIDC client.
properties:
conditions:
description: conditions represent the observations of an OIDCClient'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 OIDCClient.
enum:
- Pending
- Ready
- Error
type: string
totalClientSecrets:
description: totalClientSecrets is the current number of client secrets
that are detected for this OIDCClient.
format: int32
type: integer
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -1,4 +1,4 @@
#! Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
#! SPDX-License-Identifier: Apache-2.0
#@ load("@ytt:data", "data")
@@ -10,7 +10,6 @@
#@ "namespace",
#@ "defaultResourceName",
#@ "defaultResourceNameWithSuffix",
#@ "pinnipedDevAPIGroupWithPrefix",
#@ "getPinnipedConfigMapData",
#@ "hasUnixNetworkEndpoint",
#@ )
@@ -96,37 +95,12 @@ spec:
- /etc/config/pinniped.yaml
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
allowPrivilegeEscalation: false
capabilities:
drop: [ "ALL" ]
#! seccompProfile was introduced in Kube v1.19. Using it on an older Kube version will result in a
#! kubectl validation error when installing via `kubectl apply`, which can be ignored using kubectl's
#! `--validate=false` flag. Note that installing via `kapp` does not complain about this validation error.
seccompProfile:
type: "RuntimeDefault"
resources:
requests:
#! If OIDCClient CRs are being used, then the Supervisor needs enough CPU to run expensive bcrypt
#! operations inside the implementation of the token endpoint for any authcode flows performed by those
#! clients, so for that use case administrators may wish to increase the requests.cpu value to more
#! closely align with their anticipated needs. Increasing this value will cause Kubernetes to give more
#! available CPU to this process during times of high CPU contention. By default, don't ask for too much
#! because that would make it impossible to install the Pinniped Supervisor on small clusters.
#! Aside from performing bcrypts at the token endpoint for those clients, the Supervisor is not a
#! particularly CPU-intensive process.
cpu: "100m" #! by default, request one-tenth of a CPU
cpu: "100m"
memory: "128Mi"
limits:
#! By declaring a CPU limit that is not equal to the CPU request value, the Supervisor will be classified
#! by Kubernetes to have "burstable" quality of service.
#! See https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/#create-a-pod-that-gets-assigned-a-qos-class-of-burstable
#! If OIDCClient CRs are being used, and lots of simultaneous users have active sessions, then it is hard
#! pre-determine what the CPU limit should be for that use case. Guessing too low would cause the
#! pod's CPU usage to be throttled, resulting in poor performance. Guessing too high would allow clients
#! to cause the usage of lots of CPU resources. Administrators who have a good sense of anticipated usage
#! patterns may choose to set the requests.cpu and limits.cpu differently from these defaults.
cpu: "1000m" #! by default, throttle each pod's usage at 1 CPU
cpu: "100m"
memory: "128Mi"
volumeMounts:
- name: config-volume
@@ -141,6 +115,8 @@ spec:
readOnly: false #! writable to allow for socket use
#@ end
ports:
- containerPort: 8080
protocol: TCP
- containerPort: 8443
protocol: TCP
env:
@@ -200,37 +176,3 @@ spec:
labelSelector:
matchLabels: #@ deploymentPodLabel()
topologyKey: kubernetes.io/hostname
---
apiVersion: v1
kind: Service
metadata:
#! If name is changed, must also change names.apiService in the ConfigMap above and spec.service.name in the APIService below.
name: #@ defaultResourceNameWithSuffix("api")
namespace: #@ namespace()
labels: #@ labels()
#! prevent kapp from altering the selector of our services to match kubectl behavior
annotations:
kapp.k14s.io/disable-default-label-scoping-rules: ""
spec:
type: ClusterIP
selector: #@ deploymentPodLabel()
ports:
- protocol: TCP
port: 443
targetPort: 10250
---
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
name: #@ pinnipedDevAPIGroupWithPrefix("v1alpha1.clientsecret.supervisor")
labels: #@ labels()
spec:
version: v1alpha1
group: #@ pinnipedDevAPIGroupWithPrefix("clientsecret.supervisor")
groupPriorityMinimum: 9900
versionPriority: 15
#! caBundle: Do not include this key here. Starts out null, will be updated/owned by the golang code.
service:
name: #@ defaultResourceNameWithSuffix("api")
namespace: #@ namespace()
port: 443

View File

@@ -1,4 +1,4 @@
#! Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
#! SPDX-License-Identifier: Apache-2.0
#@ load("@ytt:data", "data")
@@ -50,19 +50,11 @@ _: #@ template.replace(data.values.custom_labels)
#@ "apiGroupSuffix": data.values.api_group_suffix,
#@ "names": {
#@ "defaultTLSCertificateSecret": defaultResourceNameWithSuffix("default-tls-certificate"),
#@ "apiService": defaultResourceNameWithSuffix("api"),
#@ },
#@ "labels": labels(),
#@ "insecureAcceptExternalUnencryptedHttpRequests": data.values.deprecated_insecure_accept_external_unencrypted_http_requests
#@ }
#@ if data.values.log_level or data.values.deprecated_log_format:
#@ config["log"] = {}
#@ end
#@ if data.values.log_level:
#@ config["log"]["level"] = getAndValidateLogLevel()
#@ end
#@ if data.values.deprecated_log_format:
#@ config["log"]["format"] = data.values.deprecated_log_format
#@ config["logLevel"] = getAndValidateLogLevel()
#@ end
#@ if data.values.endpoints:
#@ config["endpoints"] = data.values.endpoints

View File

@@ -1,9 +1,10 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
controller-gen.kubebuilder.io/version: v0.4.0
creationTimestamp: null
name: activedirectoryidentityproviders.idp.supervisor.pinniped.dev
spec:

View File

@@ -1,9 +1,10 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
controller-gen.kubebuilder.io/version: v0.4.0
creationTimestamp: null
name: ldapidentityproviders.idp.supervisor.pinniped.dev
spec:

View File

@@ -1,9 +1,10 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
controller-gen.kubebuilder.io/version: v0.4.0
creationTimestamp: null
name: oidcidentityproviders.idp.supervisor.pinniped.dev
spec:

View File

@@ -1,4 +1,4 @@
#! Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
#! SPDX-License-Identifier: Apache-2.0
#@ load("@ytt:data", "data")
@@ -24,14 +24,6 @@ rules:
- #@ pinnipedDevAPIGroupWithPrefix("config.supervisor")
resources: [federationdomains/status]
verbs: [get, patch, update]
- apiGroups:
- #@ pinnipedDevAPIGroupWithPrefix("config.supervisor")
resources: [oidcclients]
verbs: [get, list, watch]
- apiGroups:
- #@ pinnipedDevAPIGroupWithPrefix("config.supervisor")
resources: [oidcclients/status]
verbs: [get, patch, update]
- apiGroups:
- #@ pinnipedDevAPIGroupWithPrefix("idp.supervisor")
resources: [oidcidentityproviders]
@@ -82,71 +74,3 @@ roleRef:
kind: Role
name: #@ defaultResourceName()
apiGroup: rbac.authorization.k8s.io
#! Give permissions for a special configmap of CA bundles that is needed by aggregated api servers
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: #@ defaultResourceNameWithSuffix("extension-apiserver-authentication-reader")
namespace: kube-system
labels: #@ labels()
subjects:
- kind: ServiceAccount
name: #@ defaultResourceName()
namespace: #@ namespace()
roleRef:
kind: Role
name: extension-apiserver-authentication-reader
apiGroup: rbac.authorization.k8s.io
#! Give permissions for subjectaccessreviews, tokenreview that is needed by aggregated api servers
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: #@ defaultResourceName()
labels: #@ labels()
subjects:
- kind: ServiceAccount
name: #@ defaultResourceName()
namespace: #@ namespace()
roleRef:
kind: ClusterRole
name: system:auth-delegator
apiGroup: rbac.authorization.k8s.io
#! Give permission to various cluster-scoped objects
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: #@ defaultResourceNameWithSuffix("aggregated-api-server")
labels: #@ labels()
rules:
- apiGroups: [ "" ]
resources: [ namespaces ]
verbs: [ get, list, watch ]
- apiGroups: [ apiregistration.k8s.io ]
resources: [ apiservices ]
verbs: [ get, list, patch, update, watch ]
- apiGroups: [ admissionregistration.k8s.io ]
resources: [ validatingwebhookconfigurations, mutatingwebhookconfigurations ]
verbs: [ get, list, watch ]
- apiGroups: [ flowcontrol.apiserver.k8s.io ]
resources: [ flowschemas, prioritylevelconfigurations ]
verbs: [ get, list, watch ]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: #@ defaultResourceNameWithSuffix("aggregated-api-server")
labels: #@ labels()
subjects:
- kind: ServiceAccount
name: #@ defaultResourceName()
namespace: #@ namespace()
roleRef:
kind: ClusterRole
name: #@ defaultResourceNameWithSuffix("aggregated-api-server")
apiGroup: rbac.authorization.k8s.io

View File

@@ -1,24 +1,10 @@
#! Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
#! SPDX-License-Identifier: Apache-2.0
#@ load("@ytt:data", "data")
#@ load("@ytt:assert", "assert")
#@ load("helpers.lib.yaml", "labels", "deploymentPodLabel", "namespace", "defaultResourceName", "defaultResourceNameWithSuffix")
#@ if hasattr(data.values, "service_http_nodeport_port"):
#@ assert.fail('value "service_http_nodeport_port" has been renamed to "deprecated_service_http_nodeport_port" and will be removed in a future release')
#@ end
#@ if hasattr(data.values, "service_http_nodeport_nodeport"):
#@ assert.fail('value "service_http_nodeport_nodeport" has been renamed to "deprecated_service_http_nodeport_nodeport" and will be removed in a future release')
#@ end
#@ if hasattr(data.values, "service_http_loadbalancer_port"):
#@ assert.fail('value "service_http_loadbalancer_port" has been renamed to "deprecated_service_http_loadbalancer_port" and will be removed in a future release')
#@ end
#@ if hasattr(data.values, "service_http_clusterip_port"):
#@ assert.fail('value "service_http_clusterip_port" has been renamed to "deprecated_service_http_clusterip_port" and will be removed in a future release')
#@ end
#@ if data.values.deprecated_service_http_nodeport_port or data.values.service_https_nodeport_port:
#@ if data.values.service_http_nodeport_port or data.values.service_https_nodeport_port:
---
apiVersion: v1
kind: Service
@@ -33,13 +19,13 @@ spec:
type: NodePort
selector: #@ deploymentPodLabel()
ports:
#@ if data.values.deprecated_service_http_nodeport_port:
#@ if data.values.service_http_nodeport_port:
- name: http
protocol: TCP
port: #@ data.values.deprecated_service_http_nodeport_port
port: #@ data.values.service_http_nodeport_port
targetPort: 8080
#@ if data.values.deprecated_service_http_nodeport_nodeport:
nodePort: #@ data.values.deprecated_service_http_nodeport_nodeport
#@ if data.values.service_http_nodeport_nodeport:
nodePort: #@ data.values.service_http_nodeport_nodeport
#@ end
#@ end
#@ if data.values.service_https_nodeport_port:
@@ -53,7 +39,7 @@ spec:
#@ end
#@ end
#@ if data.values.deprecated_service_http_clusterip_port or data.values.service_https_clusterip_port:
#@ if data.values.service_http_clusterip_port or data.values.service_https_clusterip_port:
---
apiVersion: v1
kind: Service
@@ -68,10 +54,10 @@ spec:
type: ClusterIP
selector: #@ deploymentPodLabel()
ports:
#@ if data.values.deprecated_service_http_clusterip_port:
#@ if data.values.service_http_clusterip_port:
- name: http
protocol: TCP
port: #@ data.values.deprecated_service_http_clusterip_port
port: #@ data.values.service_http_clusterip_port
targetPort: 8080
#@ end
#@ if data.values.service_https_clusterip_port:
@@ -82,7 +68,7 @@ spec:
#@ end
#@ end
#@ if data.values.deprecated_service_http_loadbalancer_port or data.values.service_https_loadbalancer_port:
#@ if data.values.service_http_loadbalancer_port or data.values.service_https_loadbalancer_port:
---
apiVersion: v1
kind: Service
@@ -100,10 +86,10 @@ spec:
loadBalancerIP: #@ data.values.service_loadbalancer_ip
#@ end
ports:
#@ if data.values.deprecated_service_http_loadbalancer_port:
#@ if data.values.service_http_loadbalancer_port:
- name: http
protocol: TCP
port: #@ data.values.deprecated_service_http_loadbalancer_port
port: #@ data.values.service_http_loadbalancer_port
targetPort: 8080
#@ end
#@ if data.values.service_https_loadbalancer_port:

View File

@@ -1,4 +1,4 @@
#! Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
#! SPDX-License-Identifier: Apache-2.0
#@data/values
@@ -35,32 +35,27 @@ image_tag: latest
#! Optional.
image_pull_dockerconfigjson: #! e.g. {"auths":{"https://registry.example.com":{"username":"USERNAME","password":"PASSWORD","auth":"BASE64_ENCODED_USERNAME_COLON_PASSWORD"}}}
#! Specify how to expose the Supervisor app's HTTPS port as a Service.
#! Typically, you would set a value for only one of the following service types.
#! Setting any of these values means that a Service of that type will be created. They are all optional.
#! Specify how to expose the Supervisor app's HTTP and/or HTTPS ports as a Service.
#! Typically you would set a value for only one of the following service types, for either HTTP or HTTPS depending on your needs.
#! An HTTP service should not be exposed outside the cluster. It would not be secure to serve OIDC endpoints to end users via HTTP.
#! Setting any of these values means that a Service of that type will be created.
#! Note that all port numbers should be numbers (not strings), i.e. use ytt's `--data-value-yaml` instead of `--data-value`.
#! Several of these values have been deprecated and will be removed in a future release. Their names have been changed to
#! mark them as deprecated and to make it obvious upon upgrade to anyone who was using them that they have been deprecated.
deprecated_service_http_nodeport_port: #! will be removed in a future release; when specified, creates a NodePort Service with this `port` value, with port 8080 as its `targetPort`; e.g. 31234
deprecated_service_http_nodeport_nodeport: #! will be removed in a future release; the `nodePort` value of the NodePort Service, optional when `deprecated_service_http_nodeport_port` is specified; e.g. 31234
deprecated_service_http_loadbalancer_port: #! will be removed in a future release; when specified, creates a LoadBalancer Service with this `port` value, with port 8080 as its `targetPort`; e.g. 8443
deprecated_service_http_clusterip_port: #! will be removed in a future release; when specified, creates a ClusterIP Service with this `port` value, with port 8080 as its `targetPort`; e.g. 8443
service_http_nodeport_port: #! when specified, creates a NodePort Service with this `port` value, with port 8080 as its `targetPort`; e.g. 31234
service_http_nodeport_nodeport: #! the `nodePort` value of the NodePort Service, optional when `service_http_nodeport_port` is specified; e.g. 31234
service_http_loadbalancer_port: #! when specified, creates a LoadBalancer Service with this `port` value, with port 8080 as its `targetPort`; e.g. 8443
service_http_clusterip_port: #! when specified, creates a ClusterIP Service with this `port` value, with port 8080 as its `targetPort`; e.g. 8443
service_https_nodeport_port: #! when specified, creates a NodePort Service with this `port` value, with port 8443 as its `targetPort`; e.g. 31243
service_https_nodeport_nodeport: #! the `nodePort` value of the NodePort Service, optional when `service_https_nodeport_port` is specified; e.g. 31243
service_https_nodeport_nodeport: #! the `nodePort` value of the NodePort Service, optional when `service_http_nodeport_port` is specified; e.g. 31243
service_https_loadbalancer_port: #! when specified, creates a LoadBalancer Service with this `port` value, with port 8443 as its `targetPort`; e.g. 8443
service_https_clusterip_port: #! when specified, creates a ClusterIP Service with this `port` value, with port 8443 as its `targetPort`; e.g. 8443
#! The `loadBalancerIP` value of the LoadBalancer Service.
#! Ignored unless service_https_loadbalancer_port is provided.
#! Ignored unless service_http_loadbalancer_port and/or service_https_loadbalancer_port are provided.
#! Optional.
service_loadbalancer_ip: #! e.g. 1.2.3.4
#! Specify the verbosity of logging: info ("nice to know" information), debug (developer information), trace (timing information),
#! or all (kitchen sink). Do not use trace or all on production systems, as credentials may get logged.
#! Specify the verbosity of logging: info ("nice to know" information), debug (developer
#! information), trace (timing information), all (kitchen sink).
log_level: #! By default, when this value is left unset, only warnings and errors are printed. There is no way to suppress warning and error logs.
#! Specify the format of logging: json (for machine parsable logs) and text (for legacy klog formatted logs).
#! By default, when this value is left unset, logs are formatted in json.
#! This configuration is deprecated and will be removed in a future release at which point logs will always be formatted as json.
deprecated_log_format:
run_as_user: 65532 #! run_as_user specifies the user ID that will own the process, see the Dockerfile for the reasoning behind this choice
run_as_group: 65532 #! run_as_group specifies the group ID that will own the process, see the Dockerfile for the reasoning behind this choice
@@ -79,17 +74,17 @@ api_group_suffix: pinniped.dev
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
#! Control the HTTP and HTTPS listeners of the Supervisor.
#! Control the https and http listeners of the Supervisor.
#!
#! The schema of this config is as follows:
#!
#! endpoints:
#! https:
#! network: tcp | unix | disabled
#! address: host:port when network=tcp or /pinniped_socket/socketfile.sock when network=unix
#! address: interface:port when network=tcp or /pinniped_socket/socketfile.sock when network=unix
#! http:
#! network: same as above
#! address: same as above, except that when network=tcp then the address is only allowed to bind to loopback interfaces
#! address: same as above
#!
#! Setting network to disabled turns off that particular listener.
#! See https://pkg.go.dev/net#Listen and https://pkg.go.dev/net#Dial for a description of what can be
@@ -103,31 +98,23 @@ no_proxy: "$(KUBERNETES_SERVICE_HOST),169.254.169.254,127.0.0.1,localhost,.svc,.
#! network: tcp
#! address: :8443
#! http:
#! network: disabled
#! network: tcp
#! address: :8080
#!
#! These defaults mean: For HTTPS listening, bind to all interfaces using TCP on port 8443.
#! Disable HTTP listening by default.
#! These defaults mean: bind to all interfaces using TCP. Use port 8443 for https and 8080 for http.
#! The defaults will change over time. Users should explicitly set this value if they wish to avoid
#! any changes on upgrade.
#!
#! The HTTP listener can only be bound to loopback interfaces. This allows the listener to accept
#! traffic from within the pod, e.g. from a service mesh sidecar. The HTTP listener should not be
#! used to accept traffic from outside the pod, since that would mean that the network traffic could be
#! transmitted unencrypted. The HTTPS listener should be used instead to accept traffic from outside the pod.
#! Ingresses and load balancers that terminate TLS connections should re-encrypt the data and route traffic
#! to the HTTPS listener. Unix domain sockets may also be used for integrations with service meshes.
#! A future version of the Supervisor app may include a breaking change to adjust the default
#! behavior of the http listener to only listen on 127.0.0.1 (or perhaps even to be disabled).
#!
#! Changing the HTTPS port number must be accompanied by matching changes to the service and deployment
#! manifests. Changes to the HTTPS listener must be coordinated with the deployment health checks.
#! Binding the http listener to addresses other than 127.0.0.1 or ::1 is deprecated.
#!
#! Unix domain sockets are recommended for integrations with service meshes. Ingresses that terminate
#! TLS connections at the edge should re-encrypt the data and route traffic to the https listener.
#!
#! Changing the port numbers used must be accompanied with matching changes to the service and deployment
#! manifests. Changes to the https listener must be coordinated with the deployment health checks.
#!
#! Optional.
endpoints:
#! Optionally override the validation on the endpoints.http value which checks that only loopback interfaces are used.
#! When deprecated_insecure_accept_external_unencrypted_http_requests is true, the HTTP listener is allowed to bind to any
#! interface, including interfaces that are listening for traffic from outside the pod. This value is being introduced
#! to ease the transition to the new loopback interface validation for the HTTP port for any users who need more time
#! to change their ingress strategy to avoid using plain HTTP into the Supervisor pods.
#! This value is immediately deprecated upon its introduction. It will be removed in some future release, at which time
#! traffic from outside the pod will need to be sent to the HTTPS listener instead, with no simple workaround available.
#! Allowed values are true (boolean), "true" (string), false (boolean), and "false" (string). The default is false.
#! Optional.
deprecated_insecure_accept_external_unencrypted_http_requests: false

View File

@@ -1,4 +1,4 @@
#! Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
#! SPDX-License-Identifier: Apache-2.0
#@ load("@ytt:overlay", "overlay")
@@ -40,24 +40,3 @@ metadata:
name: #@ pinnipedDevAPIGroupWithPrefix("activedirectoryidentityproviders.idp.supervisor")
spec:
group: #@ pinnipedDevAPIGroupWithPrefix("idp.supervisor")
#@overlay/match by=overlay.subset({"kind": "CustomResourceDefinition", "metadata":{"name":"oidcclients.config.supervisor.pinniped.dev"}}), expects=1
---
metadata:
#@overlay/match missing_ok=True
labels: #@ labels()
name: #@ pinnipedDevAPIGroupWithPrefix("oidcclients.config.supervisor")
spec:
group: #@ pinnipedDevAPIGroupWithPrefix("config.supervisor")
versions:
#@overlay/match by=overlay.all, expects="1+"
- schema:
openAPIV3Schema:
#@overlay/match by=overlay.subset({"metadata":{"type":"object"}}), expects=1
properties:
metadata:
#@overlay/match missing_ok=True
properties:
name:
pattern: ^client\.oauth\.pinniped\.dev-
type: string

View File

@@ -6,8 +6,6 @@
.Packages
- xref:{anchor_prefix}-authentication-concierge-pinniped-dev-v1alpha1[$$authentication.concierge.pinniped.dev/v1alpha1$$]
- xref:{anchor_prefix}-clientsecret-supervisor-pinniped-dev-clientsecret[$$clientsecret.supervisor.pinniped.dev/clientsecret$$]
- xref:{anchor_prefix}-clientsecret-supervisor-pinniped-dev-v1alpha1[$$clientsecret.supervisor.pinniped.dev/v1alpha1$$]
- xref:{anchor_prefix}-config-concierge-pinniped-dev-v1alpha1[$$config.concierge.pinniped.dev/v1alpha1$$]
- xref:{anchor_prefix}-config-supervisor-pinniped-dev-v1alpha1[$$config.supervisor.pinniped.dev/v1alpha1$$]
- xref:{anchor_prefix}-identity-concierge-pinniped-dev-identity[$$identity.concierge.pinniped.dev/identity$$]
@@ -212,160 +210,6 @@ Status of a webhook authenticator.
[id="{anchor_prefix}-clientsecret-supervisor-pinniped-dev-clientsecret"]
=== clientsecret.supervisor.pinniped.dev/clientsecret
Package clientsecret is the internal version of the Pinniped client secret API.
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-clientsecret-oidcclientsecretrequest"]
==== OIDCClientSecretRequest
OIDCClientSecretRequest can be used to update the client secrets associated with an OIDCClient.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-clientsecret-oidcclientsecretrequestlist[$$OIDCClientSecretRequestList$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`name`* __string__ | Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names
| *`generateName`* __string__ | GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.
If this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).
Applied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency
| *`namespace`* __string__ | Namespace defines the space within each name must be unique. An empty namespace is equivalent to the "default" namespace, but "default" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.
Must be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces
| *`selfLink`* __string__ | SelfLink is a URL representing this object. Populated by the system. Read-only.
DEPRECATED Kubernetes will stop propagating this field in 1.20 release and the field is planned to be removed in 1.21 release.
| *`uid`* __UID__ | UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.
Populated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids
| *`resourceVersion`* __string__ | An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.
Populated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
| *`generation`* __integer__ | A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.
| *`creationTimestamp`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#time-v1-meta[$$Time$$]__ | CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.
Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
| *`deletionTimestamp`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#time-v1-meta[$$Time$$]__ | DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.
Populated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
| *`deletionGracePeriodSeconds`* __integer__ | Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.
| *`labels`* __object (keys:string, values:string)__ | Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels
| *`annotations`* __object (keys:string, values:string)__ | Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations
| *`ownerReferences`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#ownerreference-v1-meta[$$OwnerReference$$] array__ | List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.
| *`finalizers`* __string array__ | Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.
| *`clusterName`* __string__ | The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.
| *`managedFields`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#managedfieldsentry-v1-meta[$$ManagedFieldsEntry$$] array__ | ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like "ci-cd". The set of fields is always in the version that the workflow used when modifying the object.
| *`Spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-clientsecret-oidcclientsecretrequestspec[$$OIDCClientSecretRequestSpec$$]__ |
| *`Status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-clientsecret-oidcclientsecretrequeststatus[$$OIDCClientSecretRequestStatus$$]__ |
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-clientsecret-oidcclientsecretrequestspec"]
==== OIDCClientSecretRequestSpec
Spec of the OIDCClientSecretRequest.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-clientsecret-oidcclientsecretrequest[$$OIDCClientSecretRequest$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`GenerateNewSecret`* __boolean__ | Request a new client secret to for the OIDCClient referenced by the metadata.name field.
| *`RevokeOldSecrets`* __boolean__ | Revoke the old client secrets associated with the OIDCClient referenced by the metadata.name field.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-clientsecret-oidcclientsecretrequeststatus"]
==== OIDCClientSecretRequestStatus
Status of the OIDCClientSecretRequest.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-clientsecret-oidcclientsecretrequest[$$OIDCClientSecretRequest$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`GeneratedSecret`* __string__ | The unencrypted OIDC Client Secret. This will only be shared upon creation and cannot be recovered if lost.
| *`TotalClientSecrets`* __integer__ | The total number of client secrets associated with the OIDCClient referenced by the metadata.name field.
|===
[id="{anchor_prefix}-clientsecret-supervisor-pinniped-dev-v1alpha1"]
=== clientsecret.supervisor.pinniped.dev/v1alpha1
Package v1alpha1 is the v1alpha1 version of the Pinniped client secret API.
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-clientsecret-v1alpha1-oidcclientsecretrequest"]
==== OIDCClientSecretRequest
OIDCClientSecretRequest can be used to update the client secrets associated with an OIDCClient.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-clientsecret-v1alpha1-oidcclientsecretrequestlist[$$OIDCClientSecretRequestList$$]
****
[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-clientsecret-v1alpha1-oidcclientsecretrequestspec[$$OIDCClientSecretRequestSpec$$]__ |
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-clientsecret-v1alpha1-oidcclientsecretrequeststatus[$$OIDCClientSecretRequestStatus$$]__ |
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-clientsecret-v1alpha1-oidcclientsecretrequestspec"]
==== OIDCClientSecretRequestSpec
Spec of the OIDCClientSecretRequest.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-clientsecret-v1alpha1-oidcclientsecretrequest[$$OIDCClientSecretRequest$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`generateNewSecret`* __boolean__ | Request a new client secret to for the OIDCClient referenced by the metadata.name field.
| *`revokeOldSecrets`* __boolean__ | Revoke the old client secrets associated with the OIDCClient referenced by the metadata.name field.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-clientsecret-v1alpha1-oidcclientsecretrequeststatus"]
==== OIDCClientSecretRequestStatus
Status of the OIDCClientSecretRequest.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-clientsecret-v1alpha1-oidcclientsecretrequest[$$OIDCClientSecretRequest$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`generatedSecret`* __string__ | The unencrypted OIDC Client Secret. This will only be shared upon creation and cannot be recovered if lost.
| *`totalClientSecrets`* __integer__ | The total number of client secrets associated with the OIDCClient referenced by the metadata.name field.
|===
[id="{anchor_prefix}-config-concierge-pinniped-dev-v1alpha1"]
=== config.concierge.pinniped.dev/v1alpha1
@@ -597,28 +441,6 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped supervisor configuratio
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-config-v1alpha1-condition"]
==== Condition
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.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-config-v1alpha1-oidcclientstatus[$$OIDCClientStatus$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`type`* __string__ | 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)
| *`status`* __ConditionStatus__ | status of the condition, one of True, False, Unknown.
| *`observedGeneration`* __integer__ | 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.
| *`lastTransitionTime`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#time-v1-meta[$$Time$$]__ | 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.
| *`reason`* __string__ | 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.
| *`message`* __string__ | message is a human readable message indicating details about the transition. This may be an empty string.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-config-v1alpha1-federationdomain"]
==== FederationDomain
@@ -716,73 +538,11 @@ FederationDomainTLSSpec is a struct that describes the TLS configuration for an
| *`secretName`* __string__ | SecretName is an optional name of a Secret in the same namespace, of type `kubernetes.io/tls`, which contains the TLS serving certificate for the HTTPS endpoints served by this FederationDomain. When provided, the TLS Secret named here must contain keys named `tls.crt` and `tls.key` that contain the certificate and private key to use for TLS.
Server Name Indication (SNI) is an extension to the Transport Layer Security (TLS) supported by all major browsers.
SecretName is required if you would like to use different TLS certificates for issuers of different hostnames. SNI requests do not include port numbers, so all issuers with the same DNS hostname must use the same SecretName value even if they have different port numbers.
SecretName is not required when you would like to use only the HTTP endpoints (e.g. when the HTTP listener is configured to listen on loopback interfaces or UNIX domain sockets for traffic from a service mesh sidecar). It is also not required when you would like all requests to this OIDC Provider's HTTPS endpoints to use the default TLS certificate, which is configured elsewhere.
SecretName is not required when you would like to use only the HTTP endpoints (e.g. when terminating TLS at an Ingress). It is also not required when you would like all requests to this OIDC Provider's HTTPS endpoints to use the default TLS certificate, which is configured elsewhere.
When your Issuer URL's host is an IP address, then this field is ignored. SNI does not work for IP addresses.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-config-v1alpha1-oidcclient"]
==== OIDCClient
OIDCClient describes the configuration of an OIDC client.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-config-v1alpha1-oidcclientlist[$$OIDCClientList$$]
****
[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-config-v1alpha1-oidcclientspec[$$OIDCClientSpec$$]__ | Spec of the OIDC client.
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-config-v1alpha1-oidcclientstatus[$$OIDCClientStatus$$]__ | Status of the OIDC client.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-config-v1alpha1-oidcclientspec"]
==== OIDCClientSpec
OIDCClientSpec is a struct that describes an OIDCClient.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-config-v1alpha1-oidcclient[$$OIDCClient$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`allowedRedirectURIs`* __RedirectURI array__ | allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this client. Any other uris will be rejected. Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme. Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
| *`allowedGrantTypes`* __GrantType array__ | allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this client.
Must only contain the following values: - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to authenticate users. This grant must always be listed. - refresh_token: allows the client to perform refresh grants for the user to extend the user's session. This grant must be listed if allowedScopes lists offline_access. - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. This grant must be listed if allowedScopes lists pinniped:request-audience.
| *`allowedScopes`* __Scope array__ | allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client.
Must only contain the following values: - openid: The client is allowed to request ID tokens. ID tokens only include the required claims by default (iss, sub, aud, exp, iat). This scope must always be listed. - offline_access: The client is allowed to request an initial refresh token during the authorization code grant flow. This scope must be listed if allowedGrantTypes lists refresh_token. - pinniped:request-audience: The client is allowed to request a new audience value during a RFC8693 token exchange, which is a step in the process to be able to get a cluster credential for the user. openid, username and groups scopes must be listed when this scope is present. This scope must be listed if allowedGrantTypes lists urn:ietf:params:oauth:grant-type:token-exchange. - username: The client is allowed to request that ID tokens contain the user's username. Without the username scope being requested and allowed, the ID token will not contain the user's username. - groups: The client is allowed to request that ID tokens contain the user's group membership, if their group membership is discoverable by the Supervisor. Without the groups scope being requested and allowed, the ID token will not contain groups.
|===
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-config-v1alpha1-oidcclientstatus"]
==== OIDCClientStatus
OIDCClientStatus is a struct that describes the actual state of an OIDCClient.
.Appears In:
****
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-config-v1alpha1-oidcclient[$$OIDCClient$$]
****
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`phase`* __OIDCClientPhase__ | phase summarizes the overall status of the OIDCClient.
| *`conditions`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-config-v1alpha1-condition[$$Condition$$] array__ | conditions represent the observations of an OIDCClient's current state.
| *`totalClientSecrets`* __integer__ | totalClientSecrets is the current number of client secrets that are detected for this OIDCClient.
|===
[id="{anchor_prefix}-identity-concierge-pinniped-dev-identity"]
=== identity.concierge.pinniped.dev/identity
@@ -854,30 +614,7 @@ WhoAmIRequest submits a request to echo back the current authenticated user.
[cols="25a,75a", options="header"]
|===
| Field | Description
| *`name`* __string__ | Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names
| *`generateName`* __string__ | GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.
If this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).
Applied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency
| *`namespace`* __string__ | Namespace defines the space within each name must be unique. An empty namespace is equivalent to the "default" namespace, but "default" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.
Must be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces
| *`selfLink`* __string__ | SelfLink is a URL representing this object. Populated by the system. Read-only.
DEPRECATED Kubernetes will stop propagating this field in 1.20 release and the field is planned to be removed in 1.21 release.
| *`uid`* __UID__ | UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.
Populated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids
| *`resourceVersion`* __string__ | An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.
Populated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
| *`generation`* __integer__ | A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.
| *`creationTimestamp`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#time-v1-meta[$$Time$$]__ | CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.
Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
| *`deletionTimestamp`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#time-v1-meta[$$Time$$]__ | DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.
Populated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
| *`deletionGracePeriodSeconds`* __integer__ | Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.
| *`labels`* __object (keys:string, values:string)__ | Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels
| *`annotations`* __object (keys:string, values:string)__ | Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations
| *`ownerReferences`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#ownerreference-v1-meta[$$OwnerReference$$] array__ | List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.
| *`finalizers`* __string array__ | Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list.
| *`clusterName`* __string__ | The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.
| *`managedFields`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#managedfieldsentry-v1-meta[$$ManagedFieldsEntry$$] array__ | ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like "ci-cd". The set of fields is always in the version that the workflow used when modifying the object.
| *`ObjectMeta`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#objectmeta-v1-meta[$$ObjectMeta$$]__ |
| *`Spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-whoamirequestspec[$$WhoAmIRequestSpec$$]__ |
| *`Status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-whoamirequeststatus[$$WhoAmIRequestStatus$$]__ |
|===
@@ -890,7 +627,7 @@ WhoAmIRequest submits a request to echo back the current authenticated user.
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-whoamirequeststatus"]
==== WhoAmIRequestStatus
Status is set by the server in the response to a WhoAmIRequest.
.Appears In:
****
@@ -989,7 +726,7 @@ WhoAmIRequest submits a request to echo back the current authenticated user.
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-identity-v1alpha1-whoamirequeststatus"]
==== WhoAmIRequestStatus
Status is set by the server in the response to a WhoAmIRequest.
.Appears In:
****
@@ -1562,7 +1299,7 @@ TokenCredentialRequest submits an IDP-specific credential to Pinniped in exchang
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-login-v1alpha1-tokencredentialrequestspec"]
==== TokenCredentialRequestSpec
Specification of a TokenCredentialRequest, expected on requests to the Pinniped API.
TokenCredentialRequestSpec is the specification of a TokenCredentialRequest, expected on requests to the Pinniped API.
.Appears In:
****
@@ -1580,7 +1317,7 @@ Specification of a TokenCredentialRequest, expected on requests to the Pinniped
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-login-v1alpha1-tokencredentialrequeststatus"]
==== TokenCredentialRequestStatus
Status of a TokenCredentialRequest, returned on responses to the Pinniped API.
TokenCredentialRequestStatus is the status of a TokenCredentialRequest, returned on responses to the Pinniped API.
.Appears In:
****

View File

@@ -17,13 +17,11 @@ type WhoAmIRequest struct {
Status WhoAmIRequestStatus
}
// Spec is always empty for a WhoAmIRequest.
type WhoAmIRequestSpec struct {
// empty for now but we may add some config here in the future
// any such config must be safe in the context of an unauthenticated user
}
// Status is set by the server in the response to a WhoAmIRequest.
type WhoAmIRequestStatus struct {
// The current authenticated user, exactly as Kubernetes understands it.
KubernetesUserInfo KubernetesUserInfo
@@ -37,6 +35,6 @@ type WhoAmIRequestList struct {
metav1.TypeMeta
metav1.ListMeta
// Items is a list of WhoAmIRequest.
// Items is a list of WhoAmIRequest
Items []WhoAmIRequest
}

View File

@@ -20,13 +20,11 @@ type WhoAmIRequest struct {
Status WhoAmIRequestStatus `json:"status,omitempty"`
}
// Spec is always empty for a WhoAmIRequest.
type WhoAmIRequestSpec struct {
// empty for now but we may add some config here in the future
// any such config must be safe in the context of an unauthenticated user
}
// Status is set by the server in the response to a WhoAmIRequest.
type WhoAmIRequestStatus struct {
// The current authenticated user, exactly as Kubernetes understands it.
KubernetesUserInfo KubernetesUserInfo `json:"kubernetesUserInfo"`
@@ -40,6 +38,6 @@ type WhoAmIRequestList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
// Items is a list of WhoAmIRequest.
// Items is a list of WhoAmIRequest
Items []WhoAmIRequest `json:"items"`
}

View File

@@ -5,8 +5,7 @@ package login
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// ClusterCredential is the cluster-specific credential returned on a successful credential request. It
// contains either a valid bearer token or a valid TLS certificate and corresponding private key for the cluster.
// ClusterCredential is a credential (token or certificate) which is valid on the Kubernetes cluster.
type ClusterCredential struct {
// ExpirationTimestamp indicates a time when the provided credentials expire.
ExpirationTimestamp metav1.Time

View File

@@ -8,7 +8,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Specification of a TokenCredentialRequest, expected on requests to the Pinniped API.
type TokenCredentialRequestSpec struct {
// Bearer token supplied with the credential request.
Token string
@@ -17,9 +16,8 @@ type TokenCredentialRequestSpec struct {
Authenticator corev1.TypedLocalObjectReference
}
// Status of a TokenCredentialRequest, returned on responses to the Pinniped API.
type TokenCredentialRequestStatus struct {
// A Credential will be returned for a successful credential request.
// A ClusterCredential will be returned for a successful credential request.
// +optional
Credential *ClusterCredential
@@ -44,6 +42,6 @@ type TokenCredentialRequestList struct {
metav1.TypeMeta
metav1.ListMeta
// Items is a list of TokenCredentialRequest.
// Items is a list of TokenCredentialRequest
Items []TokenCredentialRequest
}

View File

@@ -8,7 +8,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Specification of a TokenCredentialRequest, expected on requests to the Pinniped API.
// TokenCredentialRequestSpec is the specification of a TokenCredentialRequest, expected on requests to the Pinniped API.
type TokenCredentialRequestSpec struct {
// Bearer token supplied with the credential request.
Token string `json:"token,omitempty"`
@@ -17,7 +17,7 @@ type TokenCredentialRequestSpec struct {
Authenticator corev1.TypedLocalObjectReference `json:"authenticator"`
}
// Status of a TokenCredentialRequest, returned on responses to the Pinniped API.
// TokenCredentialRequestStatus is the status of a TokenCredentialRequest, returned on responses to the Pinniped API.
type TokenCredentialRequestStatus struct {
// A Credential will be returned for a successful credential request.
// +optional
@@ -47,6 +47,5 @@ type TokenCredentialRequestList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
// Items is a list of TokenCredentialRequest.
Items []TokenCredentialRequest `json:"items"`
}

View File

@@ -4,6 +4,6 @@ module go.pinniped.dev/generated/1.17/apis
go 1.13
require (
k8s.io/api v0.17.17
k8s.io/apimachinery v0.17.17
k8s.io/api v0.17.16
k8s.io/apimachinery v0.17.16
)

View File

@@ -91,10 +91,10 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
k8s.io/api v0.17.17 h1:S+Yv5pdfvy9OG1t148zMFk3/l/VYpF1N4j5Y/q8IMdg=
k8s.io/api v0.17.17/go.mod h1:kk4nQM0EVx+BEY7o8CN5YL99CWmWEQ2a4NCak58yB6E=
k8s.io/apimachinery v0.17.17 h1:HMpFl9yqNI5G2+2WllKOe2XYLkCyaWzfXvk7SosyVko=
k8s.io/apimachinery v0.17.17/go.mod h1:T54ZSpncArE25c5r2PbUPsLeTpkPWY/ivafigSX6+xk=
k8s.io/api v0.17.16 h1:whKfZJJp9m5fklRnlvO8+mJzpXat0gX0n+90d1hWTu0=
k8s.io/api v0.17.16/go.mod h1:W8uKRxJeYRlAbWuk4CZv6BzuC7KuZnB6bSTPI7Pi8no=
k8s.io/apimachinery v0.17.16 h1:A9HqHhUGgUNwki1c1lY6w773WMY1Qx/jR3r9baX1unQ=
k8s.io/apimachinery v0.17.16/go.mod h1:T54ZSpncArE25c5r2PbUPsLeTpkPWY/ivafigSX6+xk=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=

View File

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

View File

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

View File

@@ -1,50 +0,0 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package clientsecret
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// OIDCClientSecretRequest can be used to update the client secrets associated with an OIDCClient.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type OIDCClientSecretRequest struct {
metav1.TypeMeta
metav1.ObjectMeta // metadata.name must be set to the client ID
Spec OIDCClientSecretRequestSpec
// +optional
Status OIDCClientSecretRequestStatus
}
// Spec of the OIDCClientSecretRequest.
type OIDCClientSecretRequestSpec struct {
// Request a new client secret to for the OIDCClient referenced by the metadata.name field.
// +optional
GenerateNewSecret bool
// Revoke the old client secrets associated with the OIDCClient referenced by the metadata.name field.
// +optional
RevokeOldSecrets bool
}
// Status of the OIDCClientSecretRequest.
type OIDCClientSecretRequestStatus struct {
// The unencrypted OIDC Client Secret. This will only be shared upon creation and cannot be recovered if lost.
GeneratedSecret string
// The total number of client secrets associated with the OIDCClient referenced by the metadata.name field.
TotalClientSecrets int
}
// OIDCClientSecretRequestList is a list of OIDCClientSecretRequest objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type OIDCClientSecretRequestList struct {
metav1.TypeMeta
metav1.ListMeta
// Items is a list of OIDCClientSecretRequest.
Items []OIDCClientSecretRequest
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,53 +0,0 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// OIDCClientSecretRequest can be used to update the client secrets associated with an OIDCClient.
// +genclient
// +genclient:onlyVerbs=create
// +kubebuilder:subresource:status
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type OIDCClientSecretRequest struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"` // metadata.name must be set to the client ID
Spec OIDCClientSecretRequestSpec `json:"spec"`
// +optional
Status OIDCClientSecretRequestStatus `json:"status"`
}
// Spec of the OIDCClientSecretRequest.
type OIDCClientSecretRequestSpec struct {
// Request a new client secret to for the OIDCClient referenced by the metadata.name field.
// +optional
GenerateNewSecret bool `json:"generateNewSecret"`
// Revoke the old client secrets associated with the OIDCClient referenced by the metadata.name field.
// +optional
RevokeOldSecrets bool `json:"revokeOldSecrets"`
}
// Status of the OIDCClientSecretRequest.
type OIDCClientSecretRequestStatus struct {
// The unencrypted OIDC Client Secret. This will only be shared upon creation and cannot be recovered if lost.
GeneratedSecret string `json:"generatedSecret,omitempty"`
// The total number of client secrets associated with the OIDCClient referenced by the metadata.name field.
TotalClientSecrets int `json:"totalClientSecrets"`
}
// OIDCClientSecretRequestList is a list of OIDCClientSecretRequest objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type OIDCClientSecretRequestList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
// Items is a list of OIDCClientSecretRequest.
Items []OIDCClientSecretRequest `json:"items"`
}

View File

@@ -1,165 +0,0 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by conversion-gen. DO NOT EDIT.
package v1alpha1
import (
unsafe "unsafe"
clientsecret "go.pinniped.dev/generated/1.17/apis/supervisor/clientsecret"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
)
func init() {
localSchemeBuilder.Register(RegisterConversions)
}
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*OIDCClientSecretRequest)(nil), (*clientsecret.OIDCClientSecretRequest)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_OIDCClientSecretRequest_To_clientsecret_OIDCClientSecretRequest(a.(*OIDCClientSecretRequest), b.(*clientsecret.OIDCClientSecretRequest), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*clientsecret.OIDCClientSecretRequest)(nil), (*OIDCClientSecretRequest)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_clientsecret_OIDCClientSecretRequest_To_v1alpha1_OIDCClientSecretRequest(a.(*clientsecret.OIDCClientSecretRequest), b.(*OIDCClientSecretRequest), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*OIDCClientSecretRequestList)(nil), (*clientsecret.OIDCClientSecretRequestList)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_OIDCClientSecretRequestList_To_clientsecret_OIDCClientSecretRequestList(a.(*OIDCClientSecretRequestList), b.(*clientsecret.OIDCClientSecretRequestList), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*clientsecret.OIDCClientSecretRequestList)(nil), (*OIDCClientSecretRequestList)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_clientsecret_OIDCClientSecretRequestList_To_v1alpha1_OIDCClientSecretRequestList(a.(*clientsecret.OIDCClientSecretRequestList), b.(*OIDCClientSecretRequestList), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*OIDCClientSecretRequestSpec)(nil), (*clientsecret.OIDCClientSecretRequestSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_OIDCClientSecretRequestSpec_To_clientsecret_OIDCClientSecretRequestSpec(a.(*OIDCClientSecretRequestSpec), b.(*clientsecret.OIDCClientSecretRequestSpec), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*clientsecret.OIDCClientSecretRequestSpec)(nil), (*OIDCClientSecretRequestSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_clientsecret_OIDCClientSecretRequestSpec_To_v1alpha1_OIDCClientSecretRequestSpec(a.(*clientsecret.OIDCClientSecretRequestSpec), b.(*OIDCClientSecretRequestSpec), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*OIDCClientSecretRequestStatus)(nil), (*clientsecret.OIDCClientSecretRequestStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_OIDCClientSecretRequestStatus_To_clientsecret_OIDCClientSecretRequestStatus(a.(*OIDCClientSecretRequestStatus), b.(*clientsecret.OIDCClientSecretRequestStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*clientsecret.OIDCClientSecretRequestStatus)(nil), (*OIDCClientSecretRequestStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_clientsecret_OIDCClientSecretRequestStatus_To_v1alpha1_OIDCClientSecretRequestStatus(a.(*clientsecret.OIDCClientSecretRequestStatus), b.(*OIDCClientSecretRequestStatus), scope)
}); err != nil {
return err
}
return nil
}
func autoConvert_v1alpha1_OIDCClientSecretRequest_To_clientsecret_OIDCClientSecretRequest(in *OIDCClientSecretRequest, out *clientsecret.OIDCClientSecretRequest, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_v1alpha1_OIDCClientSecretRequestSpec_To_clientsecret_OIDCClientSecretRequestSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := Convert_v1alpha1_OIDCClientSecretRequestStatus_To_clientsecret_OIDCClientSecretRequestStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
// Convert_v1alpha1_OIDCClientSecretRequest_To_clientsecret_OIDCClientSecretRequest is an autogenerated conversion function.
func Convert_v1alpha1_OIDCClientSecretRequest_To_clientsecret_OIDCClientSecretRequest(in *OIDCClientSecretRequest, out *clientsecret.OIDCClientSecretRequest, s conversion.Scope) error {
return autoConvert_v1alpha1_OIDCClientSecretRequest_To_clientsecret_OIDCClientSecretRequest(in, out, s)
}
func autoConvert_clientsecret_OIDCClientSecretRequest_To_v1alpha1_OIDCClientSecretRequest(in *clientsecret.OIDCClientSecretRequest, out *OIDCClientSecretRequest, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_clientsecret_OIDCClientSecretRequestSpec_To_v1alpha1_OIDCClientSecretRequestSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := Convert_clientsecret_OIDCClientSecretRequestStatus_To_v1alpha1_OIDCClientSecretRequestStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
// Convert_clientsecret_OIDCClientSecretRequest_To_v1alpha1_OIDCClientSecretRequest is an autogenerated conversion function.
func Convert_clientsecret_OIDCClientSecretRequest_To_v1alpha1_OIDCClientSecretRequest(in *clientsecret.OIDCClientSecretRequest, out *OIDCClientSecretRequest, s conversion.Scope) error {
return autoConvert_clientsecret_OIDCClientSecretRequest_To_v1alpha1_OIDCClientSecretRequest(in, out, s)
}
func autoConvert_v1alpha1_OIDCClientSecretRequestList_To_clientsecret_OIDCClientSecretRequestList(in *OIDCClientSecretRequestList, out *clientsecret.OIDCClientSecretRequestList, s conversion.Scope) error {
out.ListMeta = in.ListMeta
out.Items = *(*[]clientsecret.OIDCClientSecretRequest)(unsafe.Pointer(&in.Items))
return nil
}
// Convert_v1alpha1_OIDCClientSecretRequestList_To_clientsecret_OIDCClientSecretRequestList is an autogenerated conversion function.
func Convert_v1alpha1_OIDCClientSecretRequestList_To_clientsecret_OIDCClientSecretRequestList(in *OIDCClientSecretRequestList, out *clientsecret.OIDCClientSecretRequestList, s conversion.Scope) error {
return autoConvert_v1alpha1_OIDCClientSecretRequestList_To_clientsecret_OIDCClientSecretRequestList(in, out, s)
}
func autoConvert_clientsecret_OIDCClientSecretRequestList_To_v1alpha1_OIDCClientSecretRequestList(in *clientsecret.OIDCClientSecretRequestList, out *OIDCClientSecretRequestList, s conversion.Scope) error {
out.ListMeta = in.ListMeta
out.Items = *(*[]OIDCClientSecretRequest)(unsafe.Pointer(&in.Items))
return nil
}
// Convert_clientsecret_OIDCClientSecretRequestList_To_v1alpha1_OIDCClientSecretRequestList is an autogenerated conversion function.
func Convert_clientsecret_OIDCClientSecretRequestList_To_v1alpha1_OIDCClientSecretRequestList(in *clientsecret.OIDCClientSecretRequestList, out *OIDCClientSecretRequestList, s conversion.Scope) error {
return autoConvert_clientsecret_OIDCClientSecretRequestList_To_v1alpha1_OIDCClientSecretRequestList(in, out, s)
}
func autoConvert_v1alpha1_OIDCClientSecretRequestSpec_To_clientsecret_OIDCClientSecretRequestSpec(in *OIDCClientSecretRequestSpec, out *clientsecret.OIDCClientSecretRequestSpec, s conversion.Scope) error {
out.GenerateNewSecret = in.GenerateNewSecret
out.RevokeOldSecrets = in.RevokeOldSecrets
return nil
}
// Convert_v1alpha1_OIDCClientSecretRequestSpec_To_clientsecret_OIDCClientSecretRequestSpec is an autogenerated conversion function.
func Convert_v1alpha1_OIDCClientSecretRequestSpec_To_clientsecret_OIDCClientSecretRequestSpec(in *OIDCClientSecretRequestSpec, out *clientsecret.OIDCClientSecretRequestSpec, s conversion.Scope) error {
return autoConvert_v1alpha1_OIDCClientSecretRequestSpec_To_clientsecret_OIDCClientSecretRequestSpec(in, out, s)
}
func autoConvert_clientsecret_OIDCClientSecretRequestSpec_To_v1alpha1_OIDCClientSecretRequestSpec(in *clientsecret.OIDCClientSecretRequestSpec, out *OIDCClientSecretRequestSpec, s conversion.Scope) error {
out.GenerateNewSecret = in.GenerateNewSecret
out.RevokeOldSecrets = in.RevokeOldSecrets
return nil
}
// Convert_clientsecret_OIDCClientSecretRequestSpec_To_v1alpha1_OIDCClientSecretRequestSpec is an autogenerated conversion function.
func Convert_clientsecret_OIDCClientSecretRequestSpec_To_v1alpha1_OIDCClientSecretRequestSpec(in *clientsecret.OIDCClientSecretRequestSpec, out *OIDCClientSecretRequestSpec, s conversion.Scope) error {
return autoConvert_clientsecret_OIDCClientSecretRequestSpec_To_v1alpha1_OIDCClientSecretRequestSpec(in, out, s)
}
func autoConvert_v1alpha1_OIDCClientSecretRequestStatus_To_clientsecret_OIDCClientSecretRequestStatus(in *OIDCClientSecretRequestStatus, out *clientsecret.OIDCClientSecretRequestStatus, s conversion.Scope) error {
out.GeneratedSecret = in.GeneratedSecret
out.TotalClientSecrets = in.TotalClientSecrets
return nil
}
// Convert_v1alpha1_OIDCClientSecretRequestStatus_To_clientsecret_OIDCClientSecretRequestStatus is an autogenerated conversion function.
func Convert_v1alpha1_OIDCClientSecretRequestStatus_To_clientsecret_OIDCClientSecretRequestStatus(in *OIDCClientSecretRequestStatus, out *clientsecret.OIDCClientSecretRequestStatus, s conversion.Scope) error {
return autoConvert_v1alpha1_OIDCClientSecretRequestStatus_To_clientsecret_OIDCClientSecretRequestStatus(in, out, s)
}
func autoConvert_clientsecret_OIDCClientSecretRequestStatus_To_v1alpha1_OIDCClientSecretRequestStatus(in *clientsecret.OIDCClientSecretRequestStatus, out *OIDCClientSecretRequestStatus, s conversion.Scope) error {
out.GeneratedSecret = in.GeneratedSecret
out.TotalClientSecrets = in.TotalClientSecrets
return nil
}
// Convert_clientsecret_OIDCClientSecretRequestStatus_To_v1alpha1_OIDCClientSecretRequestStatus is an autogenerated conversion function.
func Convert_clientsecret_OIDCClientSecretRequestStatus_To_v1alpha1_OIDCClientSecretRequestStatus(in *clientsecret.OIDCClientSecretRequestStatus, out *OIDCClientSecretRequestStatus, s conversion.Scope) error {
return autoConvert_clientsecret_OIDCClientSecretRequestStatus_To_v1alpha1_OIDCClientSecretRequestStatus(in, out, s)
}

View File

@@ -1,106 +0,0 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1alpha1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OIDCClientSecretRequest) DeepCopyInto(out *OIDCClientSecretRequest) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
out.Status = in.Status
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCClientSecretRequest.
func (in *OIDCClientSecretRequest) DeepCopy() *OIDCClientSecretRequest {
if in == nil {
return nil
}
out := new(OIDCClientSecretRequest)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *OIDCClientSecretRequest) 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 *OIDCClientSecretRequestList) DeepCopyInto(out *OIDCClientSecretRequestList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]OIDCClientSecretRequest, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCClientSecretRequestList.
func (in *OIDCClientSecretRequestList) DeepCopy() *OIDCClientSecretRequestList {
if in == nil {
return nil
}
out := new(OIDCClientSecretRequestList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *OIDCClientSecretRequestList) 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 *OIDCClientSecretRequestSpec) DeepCopyInto(out *OIDCClientSecretRequestSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCClientSecretRequestSpec.
func (in *OIDCClientSecretRequestSpec) DeepCopy() *OIDCClientSecretRequestSpec {
if in == nil {
return nil
}
out := new(OIDCClientSecretRequestSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OIDCClientSecretRequestStatus) DeepCopyInto(out *OIDCClientSecretRequestStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCClientSecretRequestStatus.
func (in *OIDCClientSecretRequestStatus) DeepCopy() *OIDCClientSecretRequestStatus {
if in == nil {
return nil
}
out := new(OIDCClientSecretRequestStatus)
in.DeepCopyInto(out)
return out
}

View File

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

View File

@@ -1,106 +0,0 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Code generated by deepcopy-gen. DO NOT EDIT.
package clientsecret
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 *OIDCClientSecretRequest) DeepCopyInto(out *OIDCClientSecretRequest) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
out.Status = in.Status
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCClientSecretRequest.
func (in *OIDCClientSecretRequest) DeepCopy() *OIDCClientSecretRequest {
if in == nil {
return nil
}
out := new(OIDCClientSecretRequest)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *OIDCClientSecretRequest) 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 *OIDCClientSecretRequestList) DeepCopyInto(out *OIDCClientSecretRequestList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]OIDCClientSecretRequest, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCClientSecretRequestList.
func (in *OIDCClientSecretRequestList) DeepCopy() *OIDCClientSecretRequestList {
if in == nil {
return nil
}
out := new(OIDCClientSecretRequestList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *OIDCClientSecretRequestList) 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 *OIDCClientSecretRequestSpec) DeepCopyInto(out *OIDCClientSecretRequestSpec) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCClientSecretRequestSpec.
func (in *OIDCClientSecretRequestSpec) DeepCopy() *OIDCClientSecretRequestSpec {
if in == nil {
return nil
}
out := new(OIDCClientSecretRequestSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OIDCClientSecretRequestStatus) DeepCopyInto(out *OIDCClientSecretRequestStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCClientSecretRequestStatus.
func (in *OIDCClientSecretRequestStatus) DeepCopy() *OIDCClientSecretRequestStatus {
if in == nil {
return nil
}
out := new(OIDCClientSecretRequestStatus)
in.DeepCopyInto(out)
return out
}

View File

@@ -32,8 +32,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&FederationDomain{},
&FederationDomainList{},
&OIDCClient{},
&OIDCClientList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil

View File

@@ -31,9 +31,8 @@ type FederationDomainTLSSpec struct {
// SNI requests do not include port numbers, so all issuers with the same DNS hostname must use the same
// SecretName value even if they have different port numbers.
//
// SecretName is not required when you would like to use only the HTTP endpoints (e.g. when the HTTP listener is
// configured to listen on loopback interfaces or UNIX domain sockets for traffic from a service mesh sidecar).
// It is also not required when you would like all requests to this OIDC Provider's HTTPS endpoints to
// SecretName is not required when you would like to use only the HTTP endpoints (e.g. when terminating TLS at an
// Ingress). It is also not required when you would like all requests to this OIDC Provider's HTTPS endpoints to
// use the default TLS certificate, which is configured elsewhere.
//
// When your Issuer URL's host is an IP address, then this field is ignored. SNI does not work for IP addresses.

View File

@@ -1,75 +0,0 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// ConditionStatus is effectively an enum type for Condition.Status.
type ConditionStatus string
// These are valid condition statuses. "ConditionTrue" means a resource is in the condition.
// "ConditionFalse" means a resource is not in the condition. "ConditionUnknown" means kubernetes
// can't decide if a resource is in the condition or not. In the future, we could add other
// intermediate conditions, e.g. ConditionDegraded.
const (
ConditionTrue ConditionStatus = "True"
ConditionFalse ConditionStatus = "False"
ConditionUnknown ConditionStatus = "Unknown"
)
// 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.
type Condition struct {
// 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)
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation: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])$`
// +kubebuilder:validation:MaxLength=316
Type string `json:"type"`
// status of the condition, one of True, False, Unknown.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Enum=True;False;Unknown
Status ConditionStatus `json:"status"`
// 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.
// +optional
// +kubebuilder:validation:Minimum=0
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// 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.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Format=date-time
LastTransitionTime metav1.Time `json:"lastTransitionTime"`
// 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.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:MaxLength=1024
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$`
Reason string `json:"reason"`
// message is a human readable message indicating details about the transition.
// This may be an empty string.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:MaxLength=32768
Message string `json:"message"`
}

View File

@@ -1,122 +0,0 @@
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
type OIDCClientPhase string
const (
// PhasePending is the default phase for newly-created OIDCClient resources.
PhasePending OIDCClientPhase = "Pending"
// PhaseReady is the phase for an OIDCClient resource in a healthy state.
PhaseReady OIDCClientPhase = "Ready"
// PhaseError is the phase for an OIDCClient in an unhealthy state.
PhaseError OIDCClientPhase = "Error"
)
// +kubebuilder:validation:Pattern=`^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/`
type RedirectURI string
// +kubebuilder:validation:Enum="authorization_code";"refresh_token";"urn:ietf:params:oauth:grant-type:token-exchange"
type GrantType string
// +kubebuilder:validation:Enum="openid";"offline_access";"username";"groups";"pinniped:request-audience"
type Scope string
// OIDCClientSpec is a struct that describes an OIDCClient.
type OIDCClientSpec struct {
// allowedRedirectURIs is a list of the allowed redirect_uri param values that should be accepted during OIDC flows with this
// client. Any other uris will be rejected.
// Must be a URI with the https scheme, unless the hostname is 127.0.0.1 or ::1 which may use the http scheme.
// Port numbers are not required for 127.0.0.1 or ::1 and are ignored when checking for a matching redirect_uri.
// +listType=set
// +kubebuilder:validation:MinItems=1
AllowedRedirectURIs []RedirectURI `json:"allowedRedirectURIs"`
// allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this
// client.
//
// Must only contain the following values:
// - authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to
// authenticate users. This grant must always be listed.
// - refresh_token: allows the client to perform refresh grants for the user to extend the user's session.
// This grant must be listed if allowedScopes lists offline_access.
// - urn:ietf:params:oauth:grant-type:token-exchange: allows the client to perform RFC8693 token exchange,
// which is a step in the process to be able to get a cluster credential for the user.
// This grant must be listed if allowedScopes lists pinniped:request-audience.
// +listType=set
// +kubebuilder:validation:MinItems=1
AllowedGrantTypes []GrantType `json:"allowedGrantTypes"`
// allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client.
//
// Must only contain the following values:
// - openid: The client is allowed to request ID tokens. ID tokens only include the required claims by default (iss, sub, aud, exp, iat).
// This scope must always be listed.
// - offline_access: The client is allowed to request an initial refresh token during the authorization code grant flow.
// This scope must be listed if allowedGrantTypes lists refresh_token.
// - pinniped:request-audience: The client is allowed to request a new audience value during a RFC8693 token exchange,
// which is a step in the process to be able to get a cluster credential for the user.
// openid, username and groups scopes must be listed when this scope is present.
// This scope must be listed if allowedGrantTypes lists urn:ietf:params:oauth:grant-type:token-exchange.
// - username: The client is allowed to request that ID tokens contain the user's username.
// Without the username scope being requested and allowed, the ID token will not contain the user's username.
// - groups: The client is allowed to request that ID tokens contain the user's group membership,
// if their group membership is discoverable by the Supervisor.
// Without the groups scope being requested and allowed, the ID token will not contain groups.
// +listType=set
// +kubebuilder:validation:MinItems=1
AllowedScopes []Scope `json:"allowedScopes"`
}
// OIDCClientStatus is a struct that describes the actual state of an OIDCClient.
type OIDCClientStatus struct {
// phase summarizes the overall status of the OIDCClient.
// +kubebuilder:default=Pending
// +kubebuilder:validation:Enum=Pending;Ready;Error
Phase OIDCClientPhase `json:"phase,omitempty"`
// conditions represent the observations of an OIDCClient's current state.
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// totalClientSecrets is the current number of client secrets that are detected for this OIDCClient.
// +optional
TotalClientSecrets int32 `json:"totalClientSecrets"` // do not omitempty to allow it to show in the printer column even when it is 0
}
// OIDCClient describes the configuration of an OIDC client.
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:resource:categories=pinniped
// +kubebuilder:printcolumn:name="Privileged Scopes",type=string,JSONPath=`.spec.allowedScopes[?(@ == "pinniped:request-audience")]`
// +kubebuilder:printcolumn:name="Client Secrets",type=integer,JSONPath=`.status.totalClientSecrets`
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
// +kubebuilder:subresource:status
type OIDCClient struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Spec of the OIDC client.
Spec OIDCClientSpec `json:"spec"`
// Status of the OIDC client.
Status OIDCClientStatus `json:"status,omitempty"`
}
// List of OIDCClient objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type OIDCClientList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []OIDCClient `json:"items"`
}

View File

@@ -12,23 +12,6 @@ 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 *Condition) DeepCopyInto(out *Condition) {
*out = *in
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Condition.
func (in *Condition) DeepCopy() *Condition {
if in == nil {
return nil
}
out := new(Condition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FederationDomain) DeepCopyInto(out *FederationDomain) {
*out = *in
@@ -167,118 +150,3 @@ func (in *FederationDomainTLSSpec) DeepCopy() *FederationDomainTLSSpec {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OIDCClient) DeepCopyInto(out *OIDCClient) {
*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 OIDCClient.
func (in *OIDCClient) DeepCopy() *OIDCClient {
if in == nil {
return nil
}
out := new(OIDCClient)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *OIDCClient) 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 *OIDCClientList) DeepCopyInto(out *OIDCClientList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]OIDCClient, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCClientList.
func (in *OIDCClientList) DeepCopy() *OIDCClientList {
if in == nil {
return nil
}
out := new(OIDCClientList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *OIDCClientList) 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 *OIDCClientSpec) DeepCopyInto(out *OIDCClientSpec) {
*out = *in
if in.AllowedRedirectURIs != nil {
in, out := &in.AllowedRedirectURIs, &out.AllowedRedirectURIs
*out = make([]RedirectURI, len(*in))
copy(*out, *in)
}
if in.AllowedGrantTypes != nil {
in, out := &in.AllowedGrantTypes, &out.AllowedGrantTypes
*out = make([]GrantType, len(*in))
copy(*out, *in)
}
if in.AllowedScopes != nil {
in, out := &in.AllowedScopes, &out.AllowedScopes
*out = make([]Scope, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCClientSpec.
func (in *OIDCClientSpec) DeepCopy() *OIDCClientSpec {
if in == nil {
return nil
}
out := new(OIDCClientSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OIDCClientStatus) DeepCopyInto(out *OIDCClientStatus) {
*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 OIDCClientStatus.
func (in *OIDCClientStatus) DeepCopy() *OIDCClientStatus {
if in == nil {
return nil
}
out := new(OIDCClientStatus)
in.DeepCopyInto(out)
return out
}

View File

@@ -15,68 +15,11 @@ const (
// 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 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 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"
// IDTokenClaimIssuer is name of the issuer claim defined by the OIDC spec.
IDTokenClaimIssuer = "iss"
// IDTokenClaimSubject is name of the subject claim defined by the OIDC spec.
IDTokenClaimSubject = "sub"
// IDTokenClaimAuthorizedParty is name of the authorized party claim defined by the OIDC spec.
IDTokenClaimAuthorizedParty = "azp"
// IDTokenClaimUsername is the name of a custom claim in the downstream ID token whose value will contain the user's
// username which was mapped from the upstream identity provider.
IDTokenClaimUsername = "username"
// IDTokenClaimGroups is the name of a custom claim in the downstream ID token whose value will contain the user's
// group names which were mapped from the upstream identity provider.
IDTokenClaimGroups = "groups"
// GrantTypeAuthorizationCode is the name of the grant type for authorization code flows defined by the OIDC spec.
GrantTypeAuthorizationCode = "authorization_code"
// GrantTypeRefreshToken is the name of the grant type for refresh flow defined by the OIDC spec.
GrantTypeRefreshToken = "refresh_token"
// GrantTypeTokenExchange is the name of a custom grant type for RFC8693 token exchanges.
GrantTypeTokenExchange = "urn:ietf:params:oauth:grant-type:token-exchange" //nolint:gosec // this is not a credential
// ScopeOpenID is name of the openid scope defined by the OIDC spec.
ScopeOpenID = "openid"
// ScopeOfflineAccess is name of the offline access scope defined by the OIDC spec, used for requesting refresh
// tokens.
ScopeOfflineAccess = "offline_access"
// ScopeEmail is name of the email scope defined by the OIDC spec.
ScopeEmail = "email"
// ScopeProfile is name of the profile scope defined by the OIDC spec.
ScopeProfile = "profile"
// ScopeUsername is the name of a custom scope that determines whether the username claim will be returned inside
// ID tokens.
ScopeUsername = "username"
// ScopeGroups is the name of a custom scope that determines whether the groups claim will be returned inside
// ID tokens.
ScopeGroups = "groups"
// ScopeRequestAudience is the name of a custom scope that determines whether a RFC8693 token exchange is allowed to
// be used to request a different audience.
ScopeRequestAudience = "pinniped:request-audience"
// ClientIDPinnipedCLI is the client ID of the statically defined public OIDC client which is used by the CLI.
ClientIDPinnipedCLI = "pinniped-cli"
// ClientIDRequiredOIDCClientPrefix is the required prefix for the metadata.name of OIDCClient CRs.
ClientIDRequiredOIDCClientPrefix = "client.oauth.pinniped.dev-"
)

View File

@@ -30,14 +30,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:
//
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
//
// kclientset, _ := kubernetes.NewForConfig(c)
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
// kclientset, _ := kubernetes.NewForConfig(c)
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
//
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly.

View File

@@ -30,14 +30,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:
//
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
// import (
// "k8s.io/client-go/kubernetes"
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
// )
//
// kclientset, _ := kubernetes.NewForConfig(c)
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
// kclientset, _ := kubernetes.NewForConfig(c)
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
//
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly.

File diff suppressed because it is too large Load Diff

View File

@@ -4,11 +4,9 @@ module go.pinniped.dev/generated/1.17/client
go 1.13
require (
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501
go.pinniped.dev/generated/1.17/apis v0.0.0
k8s.io/apimachinery v0.17.17
k8s.io/client-go v0.17.17
k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29
k8s.io/apimachinery v0.17.16
k8s.io/client-go v0.17.16
)
replace go.pinniped.dev/generated/1.17/apis => ../apis

View File

@@ -10,9 +10,7 @@ github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6L
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/PuerkitoBio/purell v1.0.0 h1:0GoNN3taZV6QI81IXgCbxMyEaJDXMSIjArYBCYzVVvs=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2 h1:JCHLVE3B+kJde7bIEo5N4J+ZbLhp0J1Fs+ulyRws4gE=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -21,7 +19,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
@@ -29,13 +26,9 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1 h1:wSt/4CYxs70xbATrGXhokKF1i0tZjENLOo1ioIO13zk=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9 h1:tF+augKRWlWx0J0B7ZyyKSiTyV6E1zZe+7b3qQlcEf8=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501 h1:C1JKChikHGpXwT5UQDFaryIpDtyyGL/CR6C2kB7F1oc=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87 h1:zP3nY8Tk2E6RTkqGYrarZXuzh+ffyLDljLxCy1iJw80=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
@@ -81,7 +74,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a h1:TpvdAwDAt1K4ANVOfcihouRdvP+MgAfDWwBuct4l6ZY=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
@@ -184,12 +176,12 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.17.17 h1:S+Yv5pdfvy9OG1t148zMFk3/l/VYpF1N4j5Y/q8IMdg=
k8s.io/api v0.17.17/go.mod h1:kk4nQM0EVx+BEY7o8CN5YL99CWmWEQ2a4NCak58yB6E=
k8s.io/apimachinery v0.17.17 h1:HMpFl9yqNI5G2+2WllKOe2XYLkCyaWzfXvk7SosyVko=
k8s.io/apimachinery v0.17.17/go.mod h1:T54ZSpncArE25c5r2PbUPsLeTpkPWY/ivafigSX6+xk=
k8s.io/client-go v0.17.17 h1:5jTDCwRXCKJwmPvtgTFgCSMIzdyAOUyPmSU3PHIuVVY=
k8s.io/client-go v0.17.17/go.mod h1:IpXd6i0FlhG3fJ+UuEWMfTUaDw6TlmMkpjmJrmbY6tY=
k8s.io/api v0.17.16 h1:whKfZJJp9m5fklRnlvO8+mJzpXat0gX0n+90d1hWTu0=
k8s.io/api v0.17.16/go.mod h1:W8uKRxJeYRlAbWuk4CZv6BzuC7KuZnB6bSTPI7Pi8no=
k8s.io/apimachinery v0.17.16 h1:A9HqHhUGgUNwki1c1lY6w773WMY1Qx/jR3r9baX1unQ=
k8s.io/apimachinery v0.17.16/go.mod h1:T54ZSpncArE25c5r2PbUPsLeTpkPWY/ivafigSX6+xk=
k8s.io/client-go v0.17.16 h1:5g4fmARsp1VFn8tSRZxYpk/DdrT4eGFF/ydYR1tW3VM=
k8s.io/client-go v0.17.16/go.mod h1:TwGfS07/0RyVp+PjSZEg9piBGveZ+hEg9zMUBg1Upbo=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=

View File

@@ -8,7 +8,6 @@ package versioned
import (
"fmt"
clientsecretv1alpha1 "go.pinniped.dev/generated/1.17/client/supervisor/clientset/versioned/typed/clientsecret/v1alpha1"
configv1alpha1 "go.pinniped.dev/generated/1.17/client/supervisor/clientset/versioned/typed/config/v1alpha1"
idpv1alpha1 "go.pinniped.dev/generated/1.17/client/supervisor/clientset/versioned/typed/idp/v1alpha1"
discovery "k8s.io/client-go/discovery"
@@ -18,7 +17,6 @@ import (
type Interface interface {
Discovery() discovery.DiscoveryInterface
ClientsecretV1alpha1() clientsecretv1alpha1.ClientsecretV1alpha1Interface
ConfigV1alpha1() configv1alpha1.ConfigV1alpha1Interface
IDPV1alpha1() idpv1alpha1.IDPV1alpha1Interface
}
@@ -27,14 +25,8 @@ type Interface interface {
// version included in a Clientset.
type Clientset struct {
*discovery.DiscoveryClient
clientsecretV1alpha1 *clientsecretv1alpha1.ClientsecretV1alpha1Client
configV1alpha1 *configv1alpha1.ConfigV1alpha1Client
iDPV1alpha1 *idpv1alpha1.IDPV1alpha1Client
}
// ClientsecretV1alpha1 retrieves the ClientsecretV1alpha1Client
func (c *Clientset) ClientsecretV1alpha1() clientsecretv1alpha1.ClientsecretV1alpha1Interface {
return c.clientsecretV1alpha1
configV1alpha1 *configv1alpha1.ConfigV1alpha1Client
iDPV1alpha1 *idpv1alpha1.IDPV1alpha1Client
}
// ConfigV1alpha1 retrieves the ConfigV1alpha1Client
@@ -68,10 +60,6 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
}
var cs Clientset
var err error
cs.clientsecretV1alpha1, err = clientsecretv1alpha1.NewForConfig(&configShallowCopy)
if err != nil {
return nil, err
}
cs.configV1alpha1, err = configv1alpha1.NewForConfig(&configShallowCopy)
if err != nil {
return nil, err
@@ -92,7 +80,6 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *Clientset {
var cs Clientset
cs.clientsecretV1alpha1 = clientsecretv1alpha1.NewForConfigOrDie(c)
cs.configV1alpha1 = configv1alpha1.NewForConfigOrDie(c)
cs.iDPV1alpha1 = idpv1alpha1.NewForConfigOrDie(c)
@@ -103,7 +90,6 @@ func NewForConfigOrDie(c *rest.Config) *Clientset {
// New creates a new Clientset for the given RESTClient.
func New(c rest.Interface) *Clientset {
var cs Clientset
cs.clientsecretV1alpha1 = clientsecretv1alpha1.New(c)
cs.configV1alpha1 = configv1alpha1.New(c)
cs.iDPV1alpha1 = idpv1alpha1.New(c)

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