Merge branch 'main' into auth_handler_form_post_csp
This commit is contained in:
commit
98c45fefe9
47
ROADMAP.md
47
ROADMAP.md
@ -5,25 +5,19 @@
|
|||||||
###
|
###
|
||||||
**About this document**
|
**About this document**
|
||||||
|
|
||||||
This document provides a link to the[ Pinniped Project issues](https://github.com/vmware-tanzu/pinniped/issues) list that serves as the up to date description of items that are in the Pinniped release pipeline. Most items are gathered from the community or include a feedback loop with the community. This should serve as a reference point for Pinniped users and contributors to understand where the project is heading, and help determine if a contribution could be conflicting with a longer term plan.
|
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. [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?**
|
**How to help?**
|
||||||
|
|
||||||
Discussion on the roadmap can take place in threads under [Issues](https://github.com/vmware-tanzu/pinniped/issues) or in [community meetings](https://github.com/vmware-tanzu/pinniped/blob/main/CONTRIBUTING.md#meeting-with-the-maintainers). Please open and comment on an issue if you want to provide suggestions and feedback to an item in the roadmap. Please review the roadmap to avoid potential duplicated effort.
|
Discussion on the roadmap can take place in [community meetings](https://github.com/vmware-tanzu/pinniped/blob/main/CONTRIBUTING.md#meeting-with-the-maintainers). If you want to provide suggestions, use cases, and feedback to an item in the roadmap, please add them to the [meeting notes](https://hackmd.io/rd_kVJhjQfOvfAWzK8A3tQ) and we will discuss them during community meetings. Please review the roadmap to avoid potential duplicated effort.
|
||||||
|
|
||||||
|
|
||||||
###
|
|
||||||
**Need an idea for a contribution?**
|
|
||||||
|
|
||||||
We’ve 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 we’ve 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?**
|
**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.
|
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.
|
||||||
|
|
||||||
|
|
||||||
###
|
###
|
||||||
@ -33,34 +27,11 @@ The following table includes the current roadmap for Pinniped. If you have any q
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Last Updated: March 2022
|
Last Updated: May 2022
|
||||||
|Theme|Description|Timeline|
|
|Theme|Description|Timeline|
|
||||||
|--|--|--|
|
|--|--|--|
|
||||||
|Improving Security Posture|Support Audit logging of security events related to Authentication |May/June 2022|
|
|Improving Security Posture|Support Audit logging of security events related to Authentication |May/June 2022|
|
||||||
|Improving Usability|Support for integrating with UI/Dashboards |May/June 2022|
|
|Improving Usability|Support for integrating with UI/Dashboards |May/June 2022|
|
||||||
|Improving Security Posture|TLS hardening contd|June/July 2022|
|
|Improving Security Posture| Secrets Rotation and Management |Q3 2022|
|
||||||
|Multiple IDP support|Support multiple IDPs configured on a single Supervisor|Exploring/Ongoing|
|
|Improving Security Posture|Session Management |Q4 2022|
|
||||||
|Improving Security Posture|mTLS for Supervisor sessions |Exploring/Ongoing|
|
|Improving Security Posture|TLS hardening contd|Q4 2022|
|
||||||
|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|
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,6 +32,15 @@ import (
|
|||||||
"go.pinniped.dev/pkg/oidcclient/oidctypes"
|
"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() {
|
func init() {
|
||||||
loginCmd.AddCommand(oidcLoginCommand(oidcLoginCommandRealDeps()))
|
loginCmd.AddCommand(oidcLoginCommand(oidcLoginCommandRealDeps()))
|
||||||
@ -165,6 +174,7 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
|
|||||||
flowOpts, err := flowOptions(
|
flowOpts, err := flowOptions(
|
||||||
idpdiscoveryv1alpha1.IDPType(flags.upstreamIdentityProviderType),
|
idpdiscoveryv1alpha1.IDPType(flags.upstreamIdentityProviderType),
|
||||||
idpdiscoveryv1alpha1.IDPFlow(flags.upstreamIdentityProviderFlow),
|
idpdiscoveryv1alpha1.IDPFlow(flags.upstreamIdentityProviderFlow),
|
||||||
|
deps,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -250,9 +260,21 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
|
|||||||
return json.NewEncoder(cmd.OutOrStdout()).Encode(cred)
|
return json.NewEncoder(cmd.OutOrStdout()).Encode(cred)
|
||||||
}
|
}
|
||||||
|
|
||||||
func flowOptions(requestedIDPType idpdiscoveryv1alpha1.IDPType, requestedFlow idpdiscoveryv1alpha1.IDPFlow) ([]oidcclient.Option, error) {
|
func flowOptions(
|
||||||
|
requestedIDPType idpdiscoveryv1alpha1.IDPType,
|
||||||
|
requestedFlow idpdiscoveryv1alpha1.IDPFlow,
|
||||||
|
deps oidcLoginCommandDeps,
|
||||||
|
) ([]oidcclient.Option, error) {
|
||||||
useCLIFlow := []oidcclient.Option{oidcclient.WithCLISendingCredentials()}
|
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 {
|
switch requestedIDPType {
|
||||||
case idpdiscoveryv1alpha1.IDPTypeOIDC:
|
case idpdiscoveryv1alpha1.IDPTypeOIDC:
|
||||||
switch requestedFlow {
|
switch requestedFlow {
|
||||||
@ -262,19 +284,21 @@ func flowOptions(requestedIDPType idpdiscoveryv1alpha1.IDPType, requestedFlow id
|
|||||||
return nil, nil // browser authcode flow is the default Option, so don't need to return an Option here
|
return nil, nil // browser authcode flow is the default Option, so don't need to return an Option here
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"--upstream-identity-provider-flow value not recognized for identity provider type %q: %s (supported values: %s)",
|
"%s value not recognized for identity provider type %q: %s (supported values: %s)",
|
||||||
requestedIDPType, requestedFlow, strings.Join([]string{idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode.String(), idpdiscoveryv1alpha1.IDPFlowCLIPassword.String()}, ", "))
|
flowSource, requestedIDPType, requestedFlow,
|
||||||
|
strings.Join([]string{idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode.String(), idpdiscoveryv1alpha1.IDPFlowCLIPassword.String()}, ", "))
|
||||||
}
|
}
|
||||||
case idpdiscoveryv1alpha1.IDPTypeLDAP, idpdiscoveryv1alpha1.IDPTypeActiveDirectory:
|
case idpdiscoveryv1alpha1.IDPTypeLDAP, idpdiscoveryv1alpha1.IDPTypeActiveDirectory:
|
||||||
switch requestedFlow {
|
switch requestedFlow {
|
||||||
case idpdiscoveryv1alpha1.IDPFlowCLIPassword, "":
|
case idpdiscoveryv1alpha1.IDPFlowCLIPassword, "":
|
||||||
return useCLIFlow, nil
|
return useCLIFlow, nil
|
||||||
case idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode:
|
case idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode:
|
||||||
return nil, nil
|
return nil, nil // browser authcode flow is the default Option, so don't need to return an Option here
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"--upstream-identity-provider-flow value not recognized for identity provider type %q: %s (supported values: %s)",
|
"%s value not recognized for identity provider type %q: %s (supported values: %s)",
|
||||||
requestedIDPType, requestedFlow, strings.Join([]string{idpdiscoveryv1alpha1.IDPFlowCLIPassword.String(), idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode.String()}, ", "))
|
flowSource, requestedIDPType, requestedFlow,
|
||||||
|
strings.Join([]string{idpdiscoveryv1alpha1.IDPFlowCLIPassword.String(), idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode.String()}, ", "))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
// Surprisingly cobra does not support this kind of flag validation. See https://github.com/spf13/pflag/issues/236
|
// Surprisingly cobra does not support this kind of flag validation. See https://github.com/spf13/pflag/issues/236
|
||||||
|
@ -148,7 +148,7 @@ func TestLoginOIDCCommand(t *testing.T) {
|
|||||||
`),
|
`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid upstream type",
|
name: "invalid upstream type is an error",
|
||||||
args: []string{
|
args: []string{
|
||||||
"--issuer", "test-issuer",
|
"--issuer", "test-issuer",
|
||||||
"--upstream-identity-provider-type", "invalid",
|
"--upstream-identity-provider-type", "invalid",
|
||||||
@ -158,6 +158,18 @@ func TestLoginOIDCCommand(t *testing.T) {
|
|||||||
Error: --upstream-identity-provider-type value not recognized: invalid (supported values: oidc, ldap, activedirectory)
|
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",
|
name: "oidc upstream type with default flow is allowed",
|
||||||
args: []string{
|
args: []string{
|
||||||
@ -193,6 +205,32 @@ func TestLoginOIDCCommand(t *testing.T) {
|
|||||||
wantOptionsCount: 4,
|
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",
|
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",
|
name: "oidc upstream type with unsupported flow is an error",
|
||||||
args: []string{
|
args: []string{
|
||||||
@ -207,6 +245,21 @@ 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)
|
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",
|
name: "ldap upstream type with default flow is allowed",
|
||||||
args: []string{
|
args: []string{
|
||||||
@ -253,6 +306,32 @@ func TestLoginOIDCCommand(t *testing.T) {
|
|||||||
wantOptionsCount: 4,
|
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",
|
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",
|
name: "ldap upstream type with unsupported flow is an error",
|
||||||
args: []string{
|
args: []string{
|
||||||
@ -267,6 +346,21 @@ func TestLoginOIDCCommand(t *testing.T) {
|
|||||||
Error: --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": 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)
|
||||||
|
`),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "active directory upstream type with CLI flow is allowed",
|
name: "active directory upstream type with CLI flow is allowed",
|
||||||
args: []string{
|
args: []string{
|
||||||
@ -291,6 +385,32 @@ func TestLoginOIDCCommand(t *testing.T) {
|
|||||||
wantOptionsCount: 4,
|
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",
|
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",
|
name: "active directory upstream type with unsupported flow is an error",
|
||||||
args: []string{
|
args: []string{
|
||||||
@ -305,6 +425,21 @@ func TestLoginOIDCCommand(t *testing.T) {
|
|||||||
Error: --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": 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)
|
||||||
|
`),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "login error",
|
name: "login error",
|
||||||
args: []string{
|
args: []string{
|
||||||
@ -348,8 +483,8 @@ func TestLoginOIDCCommand(t *testing.T) {
|
|||||||
wantOptionsCount: 4,
|
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",
|
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{
|
wantLogs: []string{
|
||||||
nowStr + ` pinniped-login cmd/login_oidc.go:222 Performing OIDC login {"issuer": "test-issuer", "client id": "test-client-id"}`,
|
nowStr + ` pinniped-login cmd/login_oidc.go:232 Performing OIDC login {"issuer": "test-issuer", "client id": "test-client-id"}`,
|
||||||
nowStr + ` pinniped-login cmd/login_oidc.go:242 No concierge configured, skipping token credential exchange`,
|
nowStr + ` pinniped-login cmd/login_oidc.go:252 No concierge configured, skipping token credential exchange`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -378,10 +513,10 @@ func TestLoginOIDCCommand(t *testing.T) {
|
|||||||
wantOptionsCount: 11,
|
wantOptionsCount: 11,
|
||||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"token":"exchanged-token"}}` + "\n",
|
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"token":"exchanged-token"}}` + "\n",
|
||||||
wantLogs: []string{
|
wantLogs: []string{
|
||||||
nowStr + ` pinniped-login cmd/login_oidc.go:222 Performing OIDC login {"issuer": "test-issuer", "client id": "test-client-id"}`,
|
nowStr + ` pinniped-login cmd/login_oidc.go:232 Performing OIDC login {"issuer": "test-issuer", "client id": "test-client-id"}`,
|
||||||
nowStr + ` pinniped-login cmd/login_oidc.go:232 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:242 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:240 Successfully exchanged token for cluster credential.`,
|
nowStr + ` pinniped-login cmd/login_oidc.go:250 Successfully exchanged token for cluster credential.`,
|
||||||
nowStr + ` pinniped-login cmd/login_oidc.go:247 caching cluster credential for future use.`,
|
nowStr + ` pinniped-login cmd/login_oidc.go:257 caching cluster credential for future use.`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -125,6 +125,11 @@ will depend on which type of identity provider was configured.
|
|||||||
Unlike the optional flow for OIDC providers described above, this optional flow does not need to be configured in
|
Unlike the optional flow for OIDC providers described above, this optional flow does not need to be configured in
|
||||||
the LDAPIdentityProvider or ActiveDirectoryIdentityProvider resource, so it is always available for end-users.
|
the LDAPIdentityProvider or ActiveDirectoryIdentityProvider resource, so it is always available for end-users.
|
||||||
|
|
||||||
|
The flow selected by the `--upstream-identity-provider-flow` CLI flag may be overridden by using the
|
||||||
|
`PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW` environment variable for the CLI at runtime. This environment variable
|
||||||
|
may be set to the same values as the CLI flag (`browser_authcode` or `cli_password`). This allows a user to switch
|
||||||
|
flows based on their needs without editing their kubeconfig file.
|
||||||
|
|
||||||
Once the user completes authentication, the `kubectl` command will automatically continue and complete the user's requested command.
|
Once the user completes authentication, the `kubectl` command will automatically continue and complete the user's requested command.
|
||||||
For the example above, `kubectl` would list the cluster's namespaces.
|
For the example above, `kubectl` would list the cluster's namespaces.
|
||||||
|
|
||||||
|
@ -53,6 +53,15 @@ import (
|
|||||||
func TestE2EFullIntegration_Browser(t *testing.T) {
|
func TestE2EFullIntegration_Browser(t *testing.T) {
|
||||||
env := testlib.IntegrationEnv(t)
|
env := testlib.IntegrationEnv(t)
|
||||||
|
|
||||||
|
// Avoid allowing PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW to interfere with these tests.
|
||||||
|
originalFlowEnvVarValue, flowOverrideEnvVarSet := os.LookupEnv("PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW")
|
||||||
|
if flowOverrideEnvVarSet {
|
||||||
|
require.NoError(t, os.Unsetenv("PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW"))
|
||||||
|
t.Cleanup(func() {
|
||||||
|
require.NoError(t, os.Setenv("PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW", originalFlowEnvVarValue))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
topSetupCtx, cancelFunc := context.WithTimeout(context.Background(), 5*time.Minute)
|
topSetupCtx, cancelFunc := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||||
defer cancelFunc()
|
defer cancelFunc()
|
||||||
|
|
||||||
@ -1015,6 +1024,66 @@ func TestE2EFullIntegration_Browser(t *testing.T) {
|
|||||||
expectedGroups,
|
expectedGroups,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Add an LDAP upstream IDP and try using it to authenticate during kubectl commands, using the env var to choose the browser flow.
|
||||||
|
t.Run("with Supervisor LDAP upstream IDP and browser flow selected by env var override with with form_post automatic authcode delivery to CLI", func(t *testing.T) {
|
||||||
|
testCtx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||||
|
t.Cleanup(cancel)
|
||||||
|
|
||||||
|
tempDir := testutil.TempDir(t) // per-test tmp dir to avoid sharing files between tests
|
||||||
|
|
||||||
|
// Start a fresh browser driver because we don't want to share cookies between the various tests in this file.
|
||||||
|
page := browsertest.Open(t)
|
||||||
|
|
||||||
|
expectedUsername := env.SupervisorUpstreamLDAP.TestUserMailAttributeValue
|
||||||
|
expectedGroups := env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs
|
||||||
|
|
||||||
|
setupClusterForEndToEndLDAPTest(t, expectedUsername, env)
|
||||||
|
|
||||||
|
// Use a specific session cache for this test.
|
||||||
|
sessionCachePath := tempDir + "/test-sessions.yaml"
|
||||||
|
|
||||||
|
kubeconfigPath := runPinnipedGetKubeconfig(t, env, pinnipedExe, tempDir, []string{
|
||||||
|
"get", "kubeconfig",
|
||||||
|
"--concierge-api-group-suffix", env.APIGroupSuffix,
|
||||||
|
"--concierge-authenticator-type", "jwt",
|
||||||
|
"--concierge-authenticator-name", authenticator.Name,
|
||||||
|
"--oidc-skip-browser",
|
||||||
|
"--oidc-ca-bundle", testCABundlePath,
|
||||||
|
"--upstream-identity-provider-flow", "cli_password", // put cli_password in the kubeconfig, so we can override it with the env var
|
||||||
|
"--oidc-session-cache", sessionCachePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Override the --upstream-identity-provider-flow flag from the kubeconfig using the env var.
|
||||||
|
require.NoError(t, os.Setenv("PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW", "browser_authcode"))
|
||||||
|
t.Cleanup(func() {
|
||||||
|
require.NoError(t, os.Unsetenv("PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW"))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Run "kubectl get namespaces" which should trigger a browser login via the plugin.
|
||||||
|
kubectlCmd := exec.CommandContext(testCtx, "kubectl", "get", "namespace", "--kubeconfig", kubeconfigPath, "-v", "6")
|
||||||
|
kubectlCmd.Env = append(os.Environ(), env.ProxyEnv()...)
|
||||||
|
|
||||||
|
// Run the kubectl command, wait for the Pinniped CLI to print the authorization URL, and open it in the browser.
|
||||||
|
kubectlOutputChan := startKubectlAndOpenAuthorizationURLInBrowser(testCtx, t, kubectlCmd, page)
|
||||||
|
|
||||||
|
// Confirm that we got to the Supervisor's login page, fill out the form, and submit the form.
|
||||||
|
browsertest.LoginToUpstreamLDAP(t, page, downstream.Spec.Issuer,
|
||||||
|
expectedUsername, env.SupervisorUpstreamLDAP.TestUserPassword)
|
||||||
|
|
||||||
|
formpostExpectSuccessState(t, page)
|
||||||
|
|
||||||
|
requireKubectlGetNamespaceOutput(t, env, waitForKubectlOutput(t, kubectlOutputChan))
|
||||||
|
|
||||||
|
requireUserCanUseKubectlWithoutAuthenticatingAgain(testCtx, t, env,
|
||||||
|
downstream,
|
||||||
|
kubeconfigPath,
|
||||||
|
sessionCachePath,
|
||||||
|
pinnipedExe,
|
||||||
|
expectedUsername,
|
||||||
|
expectedGroups,
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func startKubectlAndOpenAuthorizationURLInBrowser(testCtx context.Context, t *testing.T, kubectlCmd *exec.Cmd, page *agouti.Page) chan string {
|
func startKubectlAndOpenAuthorizationURLInBrowser(testCtx context.Context, t *testing.T, kubectlCmd *exec.Cmd, page *agouti.Page) chan string {
|
||||||
|
Loading…
Reference in New Issue
Block a user