Add --concierge-credential-issuer flag to "pinniped get kubeconfig" command.
This flag selects a CredentialIssuer to use when detecting what mode the Concierge is in on a cluster. If not specified, the command will look for a single CredentialIssuer. If there are multiple, then the flag is required. Signed-off-by: Matt Moyer <moyerm@vmware.com>
This commit is contained in:
parent
1c7c22352f
commit
f937ae2c07
@ -16,6 +16,7 @@ type conciergeMode int
|
||||
var _ flag.Value = new(conciergeMode)
|
||||
|
||||
const (
|
||||
modeUnknown conciergeMode = iota
|
||||
modeTokenCredentialRequestAPI conciergeMode = iota
|
||||
modeImpersonationProxy conciergeMode = iota
|
||||
)
|
||||
@ -32,6 +33,10 @@ func (c *conciergeMode) String() string {
|
||||
}
|
||||
|
||||
func (c *conciergeMode) Set(s string) error {
|
||||
if strings.EqualFold(s, "") {
|
||||
*c = modeUnknown
|
||||
return nil
|
||||
}
|
||||
if strings.EqualFold(s, "TokenCredentialRequestAPI") {
|
||||
*c = modeTokenCredentialRequestAPI
|
||||
return nil
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
func TestConciergeModeFlag(t *testing.T) {
|
||||
var m conciergeMode
|
||||
require.Equal(t, "mode", m.Type())
|
||||
require.Equal(t, modeTokenCredentialRequestAPI, m)
|
||||
require.Equal(t, modeUnknown, m)
|
||||
require.EqualError(t, m.Set("foo"), `invalid mode "foo", valid modes are TokenCredentialRequestAPI and ImpersonationProxy`)
|
||||
|
||||
require.NoError(t, m.Set("TokenCredentialRequestAPI"))
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth" // Adds handlers for various dynamic auth plugins in client-go
|
||||
|
||||
conciergev1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
|
||||
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
conciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
|
||||
"go.pinniped.dev/internal/groupsuffix"
|
||||
"go.pinniped.dev/internal/kubeclient"
|
||||
@ -74,6 +75,7 @@ type getKubeconfigOIDCParams struct {
|
||||
|
||||
type getKubeconfigConciergeParams struct {
|
||||
disabled bool
|
||||
credentialIssuer string
|
||||
authenticatorName string
|
||||
authenticatorType string
|
||||
apiGroupSuffix string
|
||||
@ -109,6 +111,7 @@ func kubeconfigCommand(deps kubeconfigDeps) *cobra.Command {
|
||||
|
||||
f.BoolVar(&flags.concierge.disabled, "no-concierge", false, "Generate a configuration which does not use the Concierge, but sends the credential to the cluster directly")
|
||||
f.StringVar(&namespace, "concierge-namespace", "pinniped-concierge", "Namespace in which the Concierge was installed")
|
||||
f.StringVar(&flags.concierge.credentialIssuer, "concierge-credential-issuer", "", "Concierge CredentialIssuer object to use for autodiscovery (default: autodiscover)")
|
||||
f.StringVar(&flags.concierge.authenticatorType, "concierge-authenticator-type", "", "Concierge authenticator type (e.g., 'webhook', 'jwt') (default: autodiscover)")
|
||||
f.StringVar(&flags.concierge.authenticatorName, "concierge-authenticator-name", "", "Concierge authenticator name (default: autodiscover)")
|
||||
f.StringVar(&flags.concierge.apiGroupSuffix, "concierge-api-group-suffix", groupsuffix.PinnipedDefaultSuffix, "Concierge API group suffix")
|
||||
@ -178,6 +181,11 @@ func runGetKubeconfig(out io.Writer, deps kubeconfigDeps, flags getKubeconfigPar
|
||||
}
|
||||
|
||||
if !flags.concierge.disabled {
|
||||
credentialIssuer, err := lookupCredentialIssuer(clientset, flags.concierge.credentialIssuer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authenticator, err := lookupAuthenticator(
|
||||
clientset,
|
||||
flags.concierge.authenticatorType,
|
||||
@ -186,7 +194,8 @@ func runGetKubeconfig(out io.Writer, deps kubeconfigDeps, flags getKubeconfigPar
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := configureConcierge(authenticator, &flags, cluster, &oidcCABundle, &execConfig); err != nil {
|
||||
|
||||
if err := configureConcierge(credentialIssuer, authenticator, &flags, cluster, &oidcCABundle, &execConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -209,7 +218,7 @@ func runGetKubeconfig(out io.Writer, deps kubeconfigDeps, flags getKubeconfigPar
|
||||
// Otherwise continue to parse the OIDC-related flags and output a config that runs `pinniped login oidc`.
|
||||
execConfig.Args = append([]string{"login", "oidc"}, execConfig.Args...)
|
||||
if flags.oidc.issuer == "" {
|
||||
return fmt.Errorf("could not autodiscover --oidc-issuer, and none was provided")
|
||||
return fmt.Errorf("could not autodiscover --oidc-issuer and none was provided")
|
||||
}
|
||||
execConfig.Args = append(execConfig.Args,
|
||||
"--issuer="+flags.oidc.issuer,
|
||||
@ -237,18 +246,30 @@ func runGetKubeconfig(out io.Writer, deps kubeconfigDeps, flags getKubeconfigPar
|
||||
return writeConfigAsYAML(out, newExecKubeconfig(cluster, &execConfig))
|
||||
}
|
||||
|
||||
func configureConcierge(authenticator metav1.Object, flags *getKubeconfigParams, v1Cluster *clientcmdapi.Cluster, oidcCABundle *string, execConfig *clientcmdapi.ExecConfig) error {
|
||||
func configureConcierge(credentialIssuer *configv1alpha1.CredentialIssuer, authenticator metav1.Object, flags *getKubeconfigParams, v1Cluster *clientcmdapi.Cluster, oidcCABundle *string, execConfig *clientcmdapi.ExecConfig) error {
|
||||
var conciergeCABundleData []byte
|
||||
|
||||
if flags.concierge.mode == modeImpersonationProxy {
|
||||
// TODO what to do if --use-impersonation-proxy is set but flags.concierge.caBundlePath is not???
|
||||
// TODO dont do this twice
|
||||
conciergeCaBundleData, err := loadCABundlePaths([]string{flags.concierge.caBundlePath})
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read --concierge-ca-bundle: %w", err)
|
||||
// Autodiscover the --concierge-mode.
|
||||
if flags.concierge.mode == modeUnknown {
|
||||
|
||||
if credentialIssuer.Status.KubeConfigInfo != nil {
|
||||
// Prefer the TokenCredentialRequest API if available.
|
||||
flags.concierge.mode = modeTokenCredentialRequestAPI
|
||||
} else if credentialIssuer.Status.ImpersonationProxyInfo != nil {
|
||||
// Otherwise prefer the impersonation proxy if it seems configured.
|
||||
flags.concierge.mode = modeImpersonationProxy
|
||||
} else {
|
||||
return fmt.Errorf("could not autodiscover --concierge-mode and none was provided")
|
||||
}
|
||||
}
|
||||
|
||||
v1Cluster.CertificateAuthorityData = []byte(conciergeCaBundleData)
|
||||
v1Cluster.Server = flags.concierge.endpoint
|
||||
if flags.concierge.mode == modeImpersonationProxy && credentialIssuer.Status.ImpersonationProxyInfo != nil {
|
||||
flags.concierge.endpoint = credentialIssuer.Status.ImpersonationProxyInfo.Endpoint
|
||||
var err error
|
||||
conciergeCABundleData, err = base64.StdEncoding.DecodeString(credentialIssuer.Status.ImpersonationProxyInfo.CertificateAuthorityData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("autodiscovered Concierge CA bundle is invalid: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
switch auth := authenticator.(type) {
|
||||
@ -292,15 +313,16 @@ func configureConcierge(authenticator metav1.Object, flags *getKubeconfigParams,
|
||||
flags.concierge.endpoint = v1Cluster.Server
|
||||
}
|
||||
|
||||
var encodedConciergeCaBundleData string
|
||||
if conciergeCABundleData == nil {
|
||||
if flags.concierge.caBundlePath == "" {
|
||||
encodedConciergeCaBundleData = base64.StdEncoding.EncodeToString(v1Cluster.CertificateAuthorityData)
|
||||
conciergeCABundleData = v1Cluster.CertificateAuthorityData
|
||||
} else {
|
||||
conciergeCaBundleData, err := loadCABundlePaths([]string{flags.concierge.caBundlePath})
|
||||
caBundleString, err := loadCABundlePaths([]string{flags.concierge.caBundlePath})
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read --concierge-ca-bundle: %w", err)
|
||||
}
|
||||
encodedConciergeCaBundleData = base64.StdEncoding.EncodeToString([]byte(conciergeCaBundleData))
|
||||
conciergeCABundleData = []byte(caBundleString)
|
||||
}
|
||||
}
|
||||
|
||||
// Append the flags to configure the Concierge credential exchange at runtime.
|
||||
@ -310,9 +332,16 @@ func configureConcierge(authenticator metav1.Object, flags *getKubeconfigParams,
|
||||
"--concierge-authenticator-name="+flags.concierge.authenticatorName,
|
||||
"--concierge-authenticator-type="+flags.concierge.authenticatorType,
|
||||
"--concierge-endpoint="+flags.concierge.endpoint,
|
||||
"--concierge-ca-bundle-data="+encodedConciergeCaBundleData,
|
||||
"--concierge-ca-bundle-data="+base64.StdEncoding.EncodeToString(conciergeCABundleData),
|
||||
"--concierge-mode="+flags.concierge.mode.String(),
|
||||
)
|
||||
|
||||
// If we're in impersonation proxy mode, the main server endpoint for the kubeconfig also needs to point to the proxy
|
||||
if flags.concierge.mode == modeImpersonationProxy {
|
||||
v1Cluster.CertificateAuthorityData = conciergeCABundleData
|
||||
v1Cluster.Server = flags.concierge.endpoint
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -343,6 +372,29 @@ func newExecKubeconfig(cluster *clientcmdapi.Cluster, execConfig *clientcmdapi.E
|
||||
}
|
||||
}
|
||||
|
||||
func lookupCredentialIssuer(clientset conciergeclientset.Interface, name string) (*configv1alpha1.CredentialIssuer, error) {
|
||||
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancelFunc()
|
||||
|
||||
// If the name is specified, get that object.
|
||||
if name != "" {
|
||||
return clientset.ConfigV1alpha1().CredentialIssuers().Get(ctx, name, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// Otherwise list all the available CredentialIssuers and hope there's just a single one
|
||||
results, err := clientset.ConfigV1alpha1().CredentialIssuers().List(ctx, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list CredentialIssuer objects for autodiscovery: %w", err)
|
||||
}
|
||||
if len(results.Items) == 0 {
|
||||
return nil, fmt.Errorf("no CredentialIssuers were found")
|
||||
}
|
||||
if len(results.Items) > 1 {
|
||||
return nil, fmt.Errorf("multiple CredentialIssuers were found, so the --concierge-credential-issuer flag must be specified")
|
||||
}
|
||||
return &results.Items[0], nil
|
||||
}
|
||||
|
||||
func lookupAuthenticator(clientset conciergeclientset.Interface, authType, authName string) (metav1.Object, error) {
|
||||
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancelFunc()
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
conciergev1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
|
||||
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
conciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
|
||||
fakeconciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake"
|
||||
"go.pinniped.dev/internal/certauthority"
|
||||
@ -65,6 +66,7 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
--concierge-authenticator-name string Concierge authenticator name (default: autodiscover)
|
||||
--concierge-authenticator-type string Concierge authenticator type (e.g., 'webhook', 'jwt') (default: autodiscover)
|
||||
--concierge-ca-bundle string Path to TLS certificate authority bundle (PEM format, optional, can be repeated) to use when connecting to the Concierge
|
||||
--concierge-credential-issuer string Concierge CredentialIssuer object to use for autodiscovery (default: autodiscover)
|
||||
--concierge-endpoint string API base for the Concierge endpoint
|
||||
--concierge-mode mode Concierge mode of operation (default TokenCredentialRequestAPI)
|
||||
-h, --help help for kubeconfig
|
||||
@ -134,6 +136,31 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
Error: could not configure Kubernetes client: some kube error
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "no credentialissuers",
|
||||
args: []string{
|
||||
"--kubeconfig", "./testdata/kubeconfig.yaml",
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: no CredentialIssuers were found
|
||||
`),
|
||||
},
|
||||
|
||||
{
|
||||
name: "credentialissuer not found",
|
||||
args: []string{
|
||||
"--kubeconfig", "./testdata/kubeconfig.yaml",
|
||||
"--concierge-credential-issuer", "does-not-exist",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: credentialissuers.config.concierge.pinniped.dev "does-not-exist" not found
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "webhook authenticator not found",
|
||||
args: []string{
|
||||
@ -141,6 +168,9 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
"--concierge-authenticator-type", "webhook",
|
||||
"--concierge-authenticator-name", "test-authenticator",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: webhookauthenticators.authentication.concierge.pinniped.dev "test-authenticator" not found
|
||||
@ -153,6 +183,9 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
"--concierge-authenticator-type", "jwt",
|
||||
"--concierge-authenticator-name", "test-authenticator",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: jwtauthenticators.authentication.concierge.pinniped.dev "test-authenticator" not found
|
||||
@ -165,6 +198,9 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
"--concierge-authenticator-type", "invalid",
|
||||
"--concierge-authenticator-name", "test-authenticator",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: invalid authenticator type "invalid", supported values are "webhook" and "jwt"
|
||||
@ -175,6 +211,9 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
args: []string{
|
||||
"--kubeconfig", "./testdata/kubeconfig.yaml",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
|
||||
},
|
||||
conciergeReactions: []kubetesting.Reactor{
|
||||
&kubetesting.SimpleReactor{
|
||||
Verb: "*",
|
||||
@ -194,6 +233,9 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
args: []string{
|
||||
"--kubeconfig", "./testdata/kubeconfig.yaml",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
|
||||
},
|
||||
conciergeReactions: []kubetesting.Reactor{
|
||||
&kubetesting.SimpleReactor{
|
||||
Verb: "*",
|
||||
@ -213,6 +255,9 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
args: []string{
|
||||
"--kubeconfig", "./testdata/kubeconfig.yaml",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: no authenticators were found
|
||||
@ -224,6 +269,7 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
"--kubeconfig", "./testdata/kubeconfig.yaml",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
|
||||
&conciergev1alpha1.JWTAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator-1"}},
|
||||
&conciergev1alpha1.JWTAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator-2"}},
|
||||
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator-3"}},
|
||||
@ -234,17 +280,62 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
Error: multiple authenticators were found, so the --concierge-authenticator-type/--concierge-authenticator-name flags must be specified
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "autodetect webhook authenticator, bad credential issuer with no status",
|
||||
args: []string{
|
||||
"--kubeconfig", "./testdata/kubeconfig.yaml",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
|
||||
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: could not autodiscover --concierge-mode and none was provided
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "autodetect webhook authenticator, bad credential issuer with invalid impersonation CA",
|
||||
args: []string{
|
||||
"--kubeconfig", "./testdata/kubeconfig.yaml",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
ImpersonationProxyInfo: &configv1alpha1.CredentialIssuerImpersonationProxyInfo{
|
||||
Endpoint: "https://impersonation-endpoint",
|
||||
CertificateAuthorityData: "invalid-base-64",
|
||||
},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: autodiscovered Concierge CA bundle is invalid: illegal base64 data at input byte 7
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "autodetect webhook authenticator, missing --oidc-issuer",
|
||||
args: []string{
|
||||
"--kubeconfig", "./testdata/kubeconfig.yaml",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
Server: "https://concierge-endpoint",
|
||||
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
|
||||
},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: could not autodiscover --oidc-issuer, and none was provided
|
||||
Error: could not autodiscover --oidc-issuer and none was provided
|
||||
`),
|
||||
},
|
||||
{
|
||||
@ -253,6 +344,15 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
"--kubeconfig", "./testdata/kubeconfig.yaml",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
Server: "https://concierge-endpoint",
|
||||
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
|
||||
},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.JWTAuthenticator{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
|
||||
Spec: conciergev1alpha1.JWTAuthenticatorSpec{
|
||||
@ -278,6 +378,15 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
"--concierge-mode", "ImpersonationProxy",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
Server: "https://concierge-endpoint",
|
||||
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
|
||||
},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
|
||||
},
|
||||
wantError: true,
|
||||
@ -293,6 +402,15 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
"--static-token-env", "TEST_TOKEN",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
Server: "https://concierge-endpoint",
|
||||
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
|
||||
},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
|
||||
},
|
||||
wantError: true,
|
||||
@ -317,6 +435,15 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
"--static-token", "test-token",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
Server: "https://concierge-endpoint",
|
||||
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
|
||||
},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
|
||||
},
|
||||
wantStdout: here.Doc(`
|
||||
@ -362,6 +489,15 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
"--static-token-env", "TEST_TOKEN",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
Server: "https://concierge-endpoint",
|
||||
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
|
||||
},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
|
||||
},
|
||||
wantStdout: here.Doc(`
|
||||
@ -406,6 +542,15 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
"--kubeconfig", "./testdata/kubeconfig.yaml",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
Server: "https://concierge-endpoint",
|
||||
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
|
||||
},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.JWTAuthenticator{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
|
||||
Spec: conciergev1alpha1.JWTAuthenticatorSpec{
|
||||
@ -473,6 +618,15 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
"--oidc-request-audience", "test-audience",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
Server: "https://concierge-endpoint",
|
||||
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
|
||||
},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.WebhookAuthenticator{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
|
||||
},
|
||||
@ -531,6 +685,76 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
"--concierge-mode", "ImpersonationProxy",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{},
|
||||
},
|
||||
&conciergev1alpha1.JWTAuthenticator{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
|
||||
Spec: conciergev1alpha1.JWTAuthenticatorSpec{
|
||||
Issuer: "https://example.com/issuer",
|
||||
Audience: "test-audience",
|
||||
TLS: &conciergev1alpha1.TLSSpec{
|
||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testOIDCCA.Bundle()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantStdout: here.Docf(`
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: dGVzdC1jb25jaWVyZ2UtY2E=
|
||||
server: https://impersonation-proxy-endpoint.test
|
||||
name: pinniped
|
||||
contexts:
|
||||
- context:
|
||||
cluster: pinniped
|
||||
user: pinniped
|
||||
name: pinniped
|
||||
current-context: pinniped
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: pinniped
|
||||
user:
|
||||
exec:
|
||||
apiVersion: client.authentication.k8s.io/v1beta1
|
||||
args:
|
||||
- login
|
||||
- oidc
|
||||
- --enable-concierge
|
||||
- --concierge-api-group-suffix=pinniped.dev
|
||||
- --concierge-authenticator-name=test-authenticator
|
||||
- --concierge-authenticator-type=jwt
|
||||
- --concierge-endpoint=https://impersonation-proxy-endpoint.test
|
||||
- --concierge-ca-bundle-data=dGVzdC1jb25jaWVyZ2UtY2E=
|
||||
- --concierge-mode=ImpersonationProxy
|
||||
- --issuer=https://example.com/issuer
|
||||
- --client-id=pinniped-cli
|
||||
- --scopes=offline_access,openid,pinniped:request-audience
|
||||
- --ca-bundle-data=%s
|
||||
- --request-audience=test-audience
|
||||
command: '.../path/to/pinniped'
|
||||
env: []
|
||||
provideClusterInfo: true
|
||||
`, base64.StdEncoding.EncodeToString(testOIDCCA.Bundle())),
|
||||
},
|
||||
{
|
||||
name: "autodetect impersonation proxy with autodetected JWT authenticator",
|
||||
args: []string{
|
||||
"--kubeconfig", "./testdata/kubeconfig.yaml",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
ImpersonationProxyInfo: &configv1alpha1.CredentialIssuerImpersonationProxyInfo{
|
||||
Endpoint: "https://impersonation-proxy-endpoint.test",
|
||||
CertificateAuthorityData: "dGVzdC1jb25jaWVyZ2UtY2E=",
|
||||
},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.JWTAuthenticator{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
|
||||
Spec: conciergev1alpha1.JWTAuthenticatorSpec{
|
||||
@ -604,7 +828,7 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
}
|
||||
fake := fakeconciergeclientset.NewSimpleClientset(tt.conciergeObjects...)
|
||||
if len(tt.conciergeReactions) > 0 {
|
||||
fake.ReactionChain = tt.conciergeReactions
|
||||
fake.ReactionChain = append(tt.conciergeReactions, fake.ReactionChain...)
|
||||
}
|
||||
return fake, nil
|
||||
},
|
||||
|
@ -190,7 +190,7 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
|
||||
|
||||
// The exact behavior depends on in which mode the Concierge is operating.
|
||||
switch flags.conciergeMode {
|
||||
case modeTokenCredentialRequestAPI:
|
||||
case modeUnknown, modeTokenCredentialRequestAPI:
|
||||
// do a credential exchange request
|
||||
cred, err := deps.exchangeToken(ctx, concierge, token.IDToken.Token)
|
||||
if err != nil {
|
||||
|
@ -122,7 +122,7 @@ func runStaticLogin(out io.Writer, deps staticLoginDeps, flags staticLoginParams
|
||||
|
||||
// If the concierge is enabled, we need to do extra steps.
|
||||
switch flags.conciergeMode {
|
||||
case modeTokenCredentialRequestAPI:
|
||||
case modeUnknown, modeTokenCredentialRequestAPI:
|
||||
// do a credential exchange request
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
Loading…
Reference in New Issue
Block a user