Add --concierge-use-impersonation-proxy to static login
- also renamed --use-impersonation-proxy to --concierge-use-impersonation-proxy
This commit is contained in:
parent
170b86d0c6
commit
2f891b4bfb
@ -115,7 +115,7 @@ func kubeconfigCommand(deps kubeconfigDeps) *cobra.Command {
|
|||||||
|
|
||||||
f.StringVar(&flags.concierge.caBundleData, "concierge-ca-bundle-data", "", "CA bundle to use when connecting to the concierge")
|
f.StringVar(&flags.concierge.caBundleData, "concierge-ca-bundle-data", "", "CA bundle to use when connecting to the concierge")
|
||||||
f.StringVar(&flags.concierge.endpoint, "concierge-endpoint", "", "API base for the Pinniped concierge endpoint")
|
f.StringVar(&flags.concierge.endpoint, "concierge-endpoint", "", "API base for the Pinniped concierge endpoint")
|
||||||
f.BoolVar(&flags.concierge.useImpersonationProxy, "use-impersonation-proxy", false, "Whether the concierge cluster uses an impersonation proxy")
|
f.BoolVar(&flags.concierge.useImpersonationProxy, "concierge-use-impersonation-proxy", false, "Whether the concierge cluster uses an impersonation proxy")
|
||||||
|
|
||||||
f.StringVar(&flags.oidc.issuer, "oidc-issuer", "", "OpenID Connect issuer URL (default: autodiscover)")
|
f.StringVar(&flags.oidc.issuer, "oidc-issuer", "", "OpenID Connect issuer URL (default: autodiscover)")
|
||||||
f.StringVar(&flags.oidc.clientID, "oidc-client-id", "pinniped-cli", "OpenID Connect client ID (default: autodiscover)")
|
f.StringVar(&flags.oidc.clientID, "oidc-client-id", "pinniped-cli", "OpenID Connect client ID (default: autodiscover)")
|
||||||
@ -296,7 +296,7 @@ func configureConcierge(authenticator metav1.Object, flags *getKubeconfigParams,
|
|||||||
)
|
)
|
||||||
if flags.concierge.useImpersonationProxy {
|
if flags.concierge.useImpersonationProxy {
|
||||||
execConfig.Args = append(execConfig.Args,
|
execConfig.Args = append(execConfig.Args,
|
||||||
"--use-impersonation-proxy",
|
"--concierge-use-impersonation-proxy",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -64,6 +64,7 @@ func TestGetKubeconfig(t *testing.T) {
|
|||||||
--concierge-ca-bundle-data string CA bundle to use when connecting to the concierge
|
--concierge-ca-bundle-data string CA bundle to use when connecting to the concierge
|
||||||
--concierge-endpoint string API base for the Pinniped concierge endpoint
|
--concierge-endpoint string API base for the Pinniped concierge endpoint
|
||||||
--concierge-namespace string Namespace in which the concierge was installed (default "pinniped-concierge")
|
--concierge-namespace string Namespace in which the concierge was installed (default "pinniped-concierge")
|
||||||
|
--concierge-use-impersonation-proxy Whether the concierge cluster uses an impersonation proxy
|
||||||
-h, --help help for kubeconfig
|
-h, --help help for kubeconfig
|
||||||
--kubeconfig string Path to kubeconfig file
|
--kubeconfig string Path to kubeconfig file
|
||||||
--kubeconfig-context string Kubeconfig context name (default: current active context)
|
--kubeconfig-context string Kubeconfig context name (default: current active context)
|
||||||
@ -78,7 +79,6 @@ func TestGetKubeconfig(t *testing.T) {
|
|||||||
--oidc-skip-browser During OpenID Connect login, skip opening the browser (just print the URL)
|
--oidc-skip-browser During OpenID Connect login, skip opening the browser (just print the URL)
|
||||||
--static-token string Instead of doing an OIDC-based login, specify a static token
|
--static-token string Instead of doing an OIDC-based login, specify a static token
|
||||||
--static-token-env string Instead of doing an OIDC-based login, read a static token from the environment
|
--static-token-env string Instead of doing an OIDC-based login, read a static token from the environment
|
||||||
--use-impersonation-proxy Whether the concierge cluster uses an impersonation proxy
|
|
||||||
`),
|
`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -515,7 +515,7 @@ func TestGetKubeconfig(t *testing.T) {
|
|||||||
"--kubeconfig", "./testdata/kubeconfig.yaml",
|
"--kubeconfig", "./testdata/kubeconfig.yaml",
|
||||||
"--concierge-ca-bundle-data", "blah", // TODO make this more realistic, maybe do some validation?
|
"--concierge-ca-bundle-data", "blah", // TODO make this more realistic, maybe do some validation?
|
||||||
"--concierge-endpoint", "https://impersonation-proxy-endpoint.test",
|
"--concierge-endpoint", "https://impersonation-proxy-endpoint.test",
|
||||||
"--use-impersonation-proxy",
|
"--concierge-use-impersonation-proxy",
|
||||||
},
|
},
|
||||||
conciergeObjects: []runtime.Object{
|
conciergeObjects: []runtime.Object{
|
||||||
&conciergev1alpha1.JWTAuthenticator{
|
&conciergev1alpha1.JWTAuthenticator{
|
||||||
@ -559,7 +559,7 @@ func TestGetKubeconfig(t *testing.T) {
|
|||||||
- --concierge-authenticator-type=jwt
|
- --concierge-authenticator-type=jwt
|
||||||
- --concierge-endpoint=https://impersonation-proxy-endpoint.test
|
- --concierge-endpoint=https://impersonation-proxy-endpoint.test
|
||||||
- --concierge-ca-bundle-data=blah
|
- --concierge-ca-bundle-data=blah
|
||||||
- --use-impersonation-proxy
|
- --concierge-use-impersonation-proxy
|
||||||
- --issuer=https://example.com/issuer
|
- --issuer=https://example.com/issuer
|
||||||
- --client-id=pinniped-cli
|
- --client-id=pinniped-cli
|
||||||
- --scopes=offline_access,openid,pinniped:request-audience
|
- --scopes=offline_access,openid,pinniped:request-audience
|
||||||
|
@ -101,7 +101,7 @@ func oidcLoginCommand(deps oidcLoginCommandDeps) *cobra.Command {
|
|||||||
cmd.Flags().StringVar(&flags.conciergeEndpoint, "concierge-endpoint", "", "API base for the Pinniped concierge endpoint")
|
cmd.Flags().StringVar(&flags.conciergeEndpoint, "concierge-endpoint", "", "API base for the Pinniped concierge endpoint")
|
||||||
cmd.Flags().StringVar(&flags.conciergeCABundle, "concierge-ca-bundle-data", "", "CA bundle to use when connecting to the concierge")
|
cmd.Flags().StringVar(&flags.conciergeCABundle, "concierge-ca-bundle-data", "", "CA bundle to use when connecting to the concierge")
|
||||||
cmd.Flags().StringVar(&flags.conciergeAPIGroupSuffix, "concierge-api-group-suffix", "pinniped.dev", "Concierge API group suffix")
|
cmd.Flags().StringVar(&flags.conciergeAPIGroupSuffix, "concierge-api-group-suffix", "pinniped.dev", "Concierge API group suffix")
|
||||||
cmd.Flags().BoolVar(&flags.useImpersonationProxy, "use-impersonation-proxy", false, "Whether the concierge cluster uses an impersonation proxy")
|
cmd.Flags().BoolVar(&flags.useImpersonationProxy, "concierge-use-impersonation-proxy", false, "Whether the concierge cluster uses an impersonation proxy")
|
||||||
|
|
||||||
mustMarkHidden(&cmd, "debug-session-cache")
|
mustMarkHidden(&cmd, "debug-session-cache")
|
||||||
mustMarkRequired(&cmd, "issuer")
|
mustMarkRequired(&cmd, "issuer")
|
||||||
@ -187,7 +187,8 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if concierge != nil && flags.useImpersonationProxy {
|
if concierge != nil && flags.useImpersonationProxy {
|
||||||
// TODO add the right header???
|
// Put the token into a TokenCredentialRequest
|
||||||
|
// put the TokenCredentialRequest in an ExecCredential
|
||||||
req, err := execCredentialForImpersonationProxy(token, flags)
|
req, err := execCredentialForImpersonationProxy(token, flags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -72,6 +72,7 @@ func TestLoginOIDCCommand(t *testing.T) {
|
|||||||
--concierge-ca-bundle-data string CA bundle to use when connecting to the concierge
|
--concierge-ca-bundle-data string CA bundle to use when connecting to the concierge
|
||||||
--concierge-endpoint string API base for the Pinniped concierge endpoint
|
--concierge-endpoint string API base for the Pinniped concierge endpoint
|
||||||
--concierge-namespace string Namespace in which the concierge was installed (default "pinniped-concierge")
|
--concierge-namespace string Namespace in which the concierge was installed (default "pinniped-concierge")
|
||||||
|
--concierge-use-impersonation-proxy Whether the concierge cluster uses an impersonation proxy
|
||||||
--enable-concierge Exchange the OIDC ID token with the Pinniped concierge during login
|
--enable-concierge Exchange the OIDC ID token with the Pinniped concierge during login
|
||||||
-h, --help help for oidc
|
-h, --help help for oidc
|
||||||
--issuer string OpenID Connect issuer URL
|
--issuer string OpenID Connect issuer URL
|
||||||
@ -80,7 +81,6 @@ func TestLoginOIDCCommand(t *testing.T) {
|
|||||||
--scopes strings OIDC scopes to request during login (default [offline_access,openid,pinniped:request-audience])
|
--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")
|
--session-cache string Path to session cache file (default "` + cfgDir + `/sessions.yaml")
|
||||||
--skip-browser Skip opening the browser (just print the URL)
|
--skip-browser Skip opening the browser (just print the URL)
|
||||||
--use-impersonation-proxy Whether the concierge cluster uses an impersonation proxy
|
|
||||||
`),
|
`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -210,14 +210,14 @@ func TestLoginOIDCCommand(t *testing.T) {
|
|||||||
"--client-id", "test-client-id",
|
"--client-id", "test-client-id",
|
||||||
"--issuer", "test-issuer",
|
"--issuer", "test-issuer",
|
||||||
"--enable-concierge",
|
"--enable-concierge",
|
||||||
"--use-impersonation-proxy",
|
"--concierge-use-impersonation-proxy",
|
||||||
"--concierge-authenticator-type", "webhook",
|
"--concierge-authenticator-type", "webhook",
|
||||||
"--concierge-authenticator-name", "test-authenticator",
|
"--concierge-authenticator-name", "test-authenticator",
|
||||||
"--concierge-endpoint", "https://127.0.0.1:1234/",
|
"--concierge-endpoint", "https://127.0.0.1:1234/",
|
||||||
"--concierge-ca-bundle-data", base64.StdEncoding.EncodeToString(testCA.Bundle()),
|
"--concierge-ca-bundle-data", base64.StdEncoding.EncodeToString(testCA.Bundle()),
|
||||||
},
|
},
|
||||||
wantOptionsCount: 3,
|
wantOptionsCount: 3,
|
||||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"` + impersonationProxyToken("test-id-token") + `"}}` + "\n",
|
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"` + impersonationProxyTestToken("test-id-token") + `"}}` + "\n",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@ -276,7 +276,7 @@ func TestLoginOIDCCommand(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func impersonationProxyToken(token string) string {
|
func impersonationProxyTestToken(token string) string {
|
||||||
reqJSON, _ := json.Marshal(&loginv1alpha1.TokenCredentialRequest{
|
reqJSON, _ := json.Marshal(&loginv1alpha1.TokenCredentialRequest{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: "pinniped-concierge",
|
Namespace: "pinniped-concierge",
|
||||||
|
@ -5,15 +5,22 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
clientauthv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
|
clientauthv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
|
||||||
|
|
||||||
|
authenticationv1alpha1 "go.pinniped.dev/generated/1.20/apis/concierge/authentication/v1alpha1"
|
||||||
|
loginv1alpha1 "go.pinniped.dev/generated/1.20/apis/concierge/login/v1alpha1"
|
||||||
"go.pinniped.dev/pkg/conciergeclient"
|
"go.pinniped.dev/pkg/conciergeclient"
|
||||||
"go.pinniped.dev/pkg/oidcclient/oidctypes"
|
"go.pinniped.dev/pkg/oidcclient/oidctypes"
|
||||||
)
|
)
|
||||||
@ -47,6 +54,7 @@ type staticLoginParams struct {
|
|||||||
conciergeEndpoint string
|
conciergeEndpoint string
|
||||||
conciergeCABundle string
|
conciergeCABundle string
|
||||||
conciergeAPIGroupSuffix string
|
conciergeAPIGroupSuffix string
|
||||||
|
useImpersonationProxy bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func staticLoginCommand(deps staticLoginDeps) *cobra.Command {
|
func staticLoginCommand(deps staticLoginDeps) *cobra.Command {
|
||||||
@ -68,6 +76,7 @@ func staticLoginCommand(deps staticLoginDeps) *cobra.Command {
|
|||||||
cmd.Flags().StringVar(&flags.conciergeEndpoint, "concierge-endpoint", "", "API base for the Pinniped concierge endpoint")
|
cmd.Flags().StringVar(&flags.conciergeEndpoint, "concierge-endpoint", "", "API base for the Pinniped concierge endpoint")
|
||||||
cmd.Flags().StringVar(&flags.conciergeCABundle, "concierge-ca-bundle-data", "", "CA bundle to use when connecting to the concierge")
|
cmd.Flags().StringVar(&flags.conciergeCABundle, "concierge-ca-bundle-data", "", "CA bundle to use when connecting to the concierge")
|
||||||
cmd.Flags().StringVar(&flags.conciergeAPIGroupSuffix, "concierge-api-group-suffix", "pinniped.dev", "Concierge API group suffix")
|
cmd.Flags().StringVar(&flags.conciergeAPIGroupSuffix, "concierge-api-group-suffix", "pinniped.dev", "Concierge API group suffix")
|
||||||
|
cmd.Flags().BoolVar(&flags.useImpersonationProxy, "concierge-use-impersonation-proxy", false, "Whether the concierge cluster uses an impersonation proxy")
|
||||||
cmd.RunE = func(cmd *cobra.Command, args []string) error { return runStaticLogin(cmd.OutOrStdout(), deps, flags) }
|
cmd.RunE = func(cmd *cobra.Command, args []string) error { return runStaticLogin(cmd.OutOrStdout(), deps, flags) }
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
@ -109,7 +118,7 @@ func runStaticLogin(out io.Writer, deps staticLoginDeps, flags staticLoginParams
|
|||||||
cred := tokenCredential(&oidctypes.Token{IDToken: &oidctypes.IDToken{Token: token}})
|
cred := tokenCredential(&oidctypes.Token{IDToken: &oidctypes.IDToken{Token: token}})
|
||||||
|
|
||||||
// Exchange that token with the concierge, if configured.
|
// Exchange that token with the concierge, if configured.
|
||||||
if concierge != nil {
|
if concierge != nil && !flags.useImpersonationProxy {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
@ -119,5 +128,58 @@ func runStaticLogin(out io.Writer, deps staticLoginDeps, flags staticLoginParams
|
|||||||
return fmt.Errorf("could not complete concierge credential exchange: %w", err)
|
return fmt.Errorf("could not complete concierge credential exchange: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if concierge != nil && flags.useImpersonationProxy {
|
||||||
|
// Put the token into a TokenCredentialRequest
|
||||||
|
// put the TokenCredentialRequest in an ExecCredential
|
||||||
|
req, err := execCredentialForImpersonationProxyStatic(token, flags)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return json.NewEncoder(out).Encode(req)
|
||||||
|
}
|
||||||
return json.NewEncoder(out).Encode(cred)
|
return json.NewEncoder(out).Encode(cred)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func execCredentialForImpersonationProxyStatic(token string, flags staticLoginParams) (*clientauthv1beta1.ExecCredential, error) {
|
||||||
|
// TODO maybe de-dup this with conciergeclient.go
|
||||||
|
var kind string
|
||||||
|
switch strings.ToLower(flags.conciergeAuthenticatorType) {
|
||||||
|
case "webhook":
|
||||||
|
kind = "WebhookAuthenticator"
|
||||||
|
case "jwt":
|
||||||
|
kind = "JWTAuthenticator"
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf(`invalid authenticator type: %q, supported values are "webhook" and "jwt"`, kind)
|
||||||
|
}
|
||||||
|
reqJSON, err := json.Marshal(&loginv1alpha1.TokenCredentialRequest{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: flags.conciergeNamespace,
|
||||||
|
},
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "TokenCredentialRequest",
|
||||||
|
APIVersion: loginv1alpha1.GroupName + "/v1alpha1",
|
||||||
|
},
|
||||||
|
Spec: loginv1alpha1.TokenCredentialRequestSpec{
|
||||||
|
Token: token, // TODO
|
||||||
|
Authenticator: corev1.TypedLocalObjectReference{
|
||||||
|
APIGroup: &authenticationv1alpha1.SchemeGroupVersion.Group,
|
||||||
|
Kind: kind,
|
||||||
|
Name: flags.conciergeAuthenticatorName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
encodedToken := base64.RawURLEncoding.EncodeToString(reqJSON)
|
||||||
|
cred := &clientauthv1beta1.ExecCredential{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "ExecCredential",
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
},
|
||||||
|
Status: &clientauthv1beta1.ExecCredentialStatus{
|
||||||
|
Token: encodedToken,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return cred, nil
|
||||||
|
}
|
||||||
|
@ -57,6 +57,7 @@ func TestLoginStaticCommand(t *testing.T) {
|
|||||||
--concierge-ca-bundle-data string CA bundle to use when connecting to the concierge
|
--concierge-ca-bundle-data string CA bundle to use when connecting to the concierge
|
||||||
--concierge-endpoint string API base for the Pinniped concierge endpoint
|
--concierge-endpoint string API base for the Pinniped concierge endpoint
|
||||||
--concierge-namespace string Namespace in which the concierge was installed (default "pinniped-concierge")
|
--concierge-namespace string Namespace in which the concierge was installed (default "pinniped-concierge")
|
||||||
|
--concierge-use-impersonation-proxy Whether the concierge cluster uses an impersonation proxy
|
||||||
--enable-concierge Exchange the token with the Pinniped concierge during login
|
--enable-concierge Exchange the token with the Pinniped concierge during login
|
||||||
-h, --help help for static
|
-h, --help help for static
|
||||||
--token string Static token to present during login
|
--token string Static token to present during login
|
||||||
@ -153,6 +154,18 @@ func TestLoginStaticCommand(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"token":"test-token"}}` + "\n",
|
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"token":"test-token"}}` + "\n",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "impersonation proxy success",
|
||||||
|
args: []string{
|
||||||
|
"--enable-concierge",
|
||||||
|
"--concierge-use-impersonation-proxy",
|
||||||
|
"--token", "test-token",
|
||||||
|
"--concierge-endpoint", "https://127.0.0.1/",
|
||||||
|
"--concierge-authenticator-type", "webhook",
|
||||||
|
"--concierge-authenticator-name", "test-authenticator",
|
||||||
|
},
|
||||||
|
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{},"status":{"token":"` + impersonationProxyTestToken("test-token") + `"}}` + "\n",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
tt := tt
|
tt := tt
|
||||||
|
Loading…
Reference in New Issue
Block a user