Rework "pinniped get kubeconfig" so that --concierge-mode can be used even when auto-discovering other parameters.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
This commit is contained in:
parent
49ec16038c
commit
389cd3486b
@ -7,6 +7,8 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
)
|
||||
|
||||
// conciergeMode represents the method by which we should connect to the Concierge on a cluster during login.
|
||||
@ -25,7 +27,7 @@ func (c *conciergeMode) String() string {
|
||||
switch *c {
|
||||
case modeImpersonationProxy:
|
||||
return "ImpersonationProxy"
|
||||
case modeTokenCredentialRequestAPI, modeUnknown:
|
||||
case modeTokenCredentialRequestAPI:
|
||||
return "TokenCredentialRequestAPI"
|
||||
default:
|
||||
return "TokenCredentialRequestAPI"
|
||||
@ -51,3 +53,15 @@ func (c *conciergeMode) Set(s string) error {
|
||||
func (c *conciergeMode) Type() string {
|
||||
return "mode"
|
||||
}
|
||||
|
||||
// MatchesFrontend returns true iff the flag matches the type of the provided frontend.
|
||||
func (c *conciergeMode) MatchesFrontend(frontend *configv1alpha1.CredentialIssuerFrontend) bool {
|
||||
switch *c {
|
||||
case modeImpersonationProxy:
|
||||
return frontend.Type == configv1alpha1.ImpersonationProxyFrontendType
|
||||
case modeTokenCredentialRequestAPI:
|
||||
return frontend.Type == configv1alpha1.TokenCredentialRequestAPIFrontendType
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -7,17 +7,25 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
)
|
||||
|
||||
func TestConciergeModeFlag(t *testing.T) {
|
||||
var m conciergeMode
|
||||
require.Equal(t, "mode", m.Type())
|
||||
require.Equal(t, modeUnknown, m)
|
||||
require.NoError(t, m.Set(""))
|
||||
require.Equal(t, modeUnknown, m)
|
||||
require.EqualError(t, m.Set("foo"), `invalid mode "foo", valid modes are TokenCredentialRequestAPI and ImpersonationProxy`)
|
||||
require.True(t, m.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.TokenCredentialRequestAPIFrontendType}))
|
||||
require.True(t, m.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.ImpersonationProxyFrontendType}))
|
||||
|
||||
require.NoError(t, m.Set("TokenCredentialRequestAPI"))
|
||||
require.Equal(t, modeTokenCredentialRequestAPI, m)
|
||||
require.Equal(t, "TokenCredentialRequestAPI", m.String())
|
||||
require.True(t, m.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.TokenCredentialRequestAPIFrontendType}))
|
||||
require.False(t, m.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.ImpersonationProxyFrontendType}))
|
||||
|
||||
require.NoError(t, m.Set("tokencredentialrequestapi"))
|
||||
require.Equal(t, modeTokenCredentialRequestAPI, m)
|
||||
@ -26,6 +34,8 @@ func TestConciergeModeFlag(t *testing.T) {
|
||||
require.NoError(t, m.Set("ImpersonationProxy"))
|
||||
require.Equal(t, modeImpersonationProxy, m)
|
||||
require.Equal(t, "ImpersonationProxy", m.String())
|
||||
require.False(t, m.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.TokenCredentialRequestAPIFrontendType}))
|
||||
require.True(t, m.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.ImpersonationProxyFrontendType}))
|
||||
|
||||
require.NoError(t, m.Set("impersonationproxy"))
|
||||
require.Equal(t, modeImpersonationProxy, m)
|
||||
|
@ -283,43 +283,56 @@ func runGetKubeconfig(ctx context.Context, out io.Writer, deps kubeconfigDeps, f
|
||||
}
|
||||
|
||||
func configureConcierge(credentialIssuer *configv1alpha1.CredentialIssuer, authenticator metav1.Object, flags *getKubeconfigParams, v1Cluster *clientcmdapi.Cluster, oidcCABundle *string, execConfig *clientcmdapi.ExecConfig, log logr.Logger) error {
|
||||
var conciergeCABundleData []byte
|
||||
|
||||
// Autodiscover the --concierge-mode.
|
||||
if flags.concierge.mode == modeUnknown { //nolint:nestif
|
||||
strategyLoop:
|
||||
for _, strategy := range credentialIssuer.Status.Strategies {
|
||||
if strategy.Status != configv1alpha1.SuccessStrategyStatus || strategy.Frontend == nil {
|
||||
continue
|
||||
}
|
||||
switch strategy.Frontend.Type {
|
||||
case configv1alpha1.TokenCredentialRequestAPIFrontendType:
|
||||
log.Info("detected Concierge in TokenCredentialRequest API mode")
|
||||
flags.concierge.mode = modeTokenCredentialRequestAPI
|
||||
break strategyLoop
|
||||
case configv1alpha1.ImpersonationProxyFrontendType:
|
||||
frontend, err := getConciergeFrontend(credentialIssuer, flags.concierge.mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
flags.concierge.mode = modeImpersonationProxy
|
||||
flags.concierge.endpoint = strategy.Frontend.ImpersonationProxyInfo.Endpoint
|
||||
var err error
|
||||
conciergeCABundleData, err = base64.StdEncoding.DecodeString(strategy.Frontend.ImpersonationProxyInfo.CertificateAuthorityData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("autodiscovered Concierge CA bundle is invalid: %w", err)
|
||||
}
|
||||
log.Info("detected Concierge in impersonation proxy mode", "endpoint", strategy.Frontend.ImpersonationProxyInfo.Endpoint)
|
||||
break strategyLoop
|
||||
default:
|
||||
// Skip any unknown frontend types.
|
||||
}
|
||||
}
|
||||
if flags.concierge.mode == modeUnknown {
|
||||
// Fall back to deprecated field for backwards compatibility.
|
||||
if credentialIssuer.Status.KubeConfigInfo != nil {
|
||||
flags.concierge.mode = modeTokenCredentialRequestAPI
|
||||
} else {
|
||||
return fmt.Errorf("could not autodiscover --concierge-mode and none was provided")
|
||||
// Auto-set --concierge-mode if it wasn't explicitly set.
|
||||
if flags.concierge.mode == modeUnknown {
|
||||
switch frontend.Type {
|
||||
case configv1alpha1.TokenCredentialRequestAPIFrontendType:
|
||||
log.Info("discovered Concierge operating in TokenCredentialRequest API mode")
|
||||
flags.concierge.mode = modeTokenCredentialRequestAPI
|
||||
case configv1alpha1.ImpersonationProxyFrontendType:
|
||||
log.Info("discovered Concierge operating in impersonation proxy mode")
|
||||
flags.concierge.mode = modeImpersonationProxy
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-set --concierge-endpoint if it wasn't explicitly set.
|
||||
if flags.concierge.endpoint == "" {
|
||||
switch frontend.Type {
|
||||
case configv1alpha1.TokenCredentialRequestAPIFrontendType:
|
||||
flags.concierge.endpoint = v1Cluster.Server
|
||||
case configv1alpha1.ImpersonationProxyFrontendType:
|
||||
flags.concierge.endpoint = frontend.ImpersonationProxyInfo.Endpoint
|
||||
}
|
||||
log.Info("discovered Concierge endpoint", "endpoint", flags.concierge.endpoint)
|
||||
}
|
||||
|
||||
// Load specified --concierge-ca-bundle or autodiscover a value.
|
||||
var conciergeCABundleData []byte
|
||||
if flags.concierge.caBundlePath != "" {
|
||||
caBundleString, err := loadCABundlePaths([]string{flags.concierge.caBundlePath})
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read --concierge-ca-bundle: %w", err)
|
||||
}
|
||||
conciergeCABundleData = []byte(caBundleString)
|
||||
log.Info("loaded Concierge certificate authority bundle", "roots", countCACerts(conciergeCABundleData))
|
||||
} else {
|
||||
switch frontend.Type {
|
||||
case configv1alpha1.TokenCredentialRequestAPIFrontendType:
|
||||
conciergeCABundleData = v1Cluster.CertificateAuthorityData
|
||||
case configv1alpha1.ImpersonationProxyFrontendType:
|
||||
var err error
|
||||
conciergeCABundleData, err = base64.StdEncoding.DecodeString(frontend.ImpersonationProxyInfo.CertificateAuthorityData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("autodiscovered Concierge CA bundle is invalid: %w", err)
|
||||
}
|
||||
}
|
||||
log.Info("discovered Concierge certificate authority bundle", "roots", countCACerts(conciergeCABundleData))
|
||||
}
|
||||
|
||||
switch auth := authenticator.(type) {
|
||||
@ -342,13 +355,13 @@ func configureConcierge(credentialIssuer *configv1alpha1.CredentialIssuer, authe
|
||||
|
||||
// If the --oidc-issuer flag was not set explicitly, default it to the spec.issuer field of the JWTAuthenticator.
|
||||
if flags.oidc.issuer == "" {
|
||||
log.Info("detected OIDC issuer", "issuer", auth.Spec.Issuer)
|
||||
log.Info("discovered OIDC issuer", "issuer", auth.Spec.Issuer)
|
||||
flags.oidc.issuer = auth.Spec.Issuer
|
||||
}
|
||||
|
||||
// If the --oidc-request-audience flag was not set explicitly, default it to the spec.audience field of the JWTAuthenticator.
|
||||
if flags.oidc.requestAudience == "" {
|
||||
log.Info("detected OIDC audience", "audience", auth.Spec.Audience)
|
||||
log.Info("discovered OIDC audience", "audience", auth.Spec.Audience)
|
||||
flags.oidc.requestAudience = auth.Spec.Audience
|
||||
}
|
||||
|
||||
@ -359,29 +372,11 @@ func configureConcierge(credentialIssuer *configv1alpha1.CredentialIssuer, authe
|
||||
if err != nil {
|
||||
return fmt.Errorf("tried to autodiscover --oidc-ca-bundle, but JWTAuthenticator %s has invalid spec.tls.certificateAuthorityData: %w", auth.Name, err)
|
||||
}
|
||||
log.Info("detected OIDC CA bundle", "length", len(decoded))
|
||||
log.Info("discovered OIDC CA bundle", "roots", countCACerts(decoded))
|
||||
*oidcCABundle = string(decoded)
|
||||
}
|
||||
}
|
||||
|
||||
if flags.concierge.endpoint == "" {
|
||||
log.Info("detected concierge endpoint", "endpoint", v1Cluster.Server)
|
||||
flags.concierge.endpoint = v1Cluster.Server
|
||||
}
|
||||
|
||||
if conciergeCABundleData == nil {
|
||||
if flags.concierge.caBundlePath == "" {
|
||||
log.Info("detected concierge CA bundle", "length", len(v1Cluster.CertificateAuthorityData))
|
||||
conciergeCABundleData = v1Cluster.CertificateAuthorityData
|
||||
} else {
|
||||
caBundleString, err := loadCABundlePaths([]string{flags.concierge.caBundlePath})
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read --concierge-ca-bundle: %w", err)
|
||||
}
|
||||
conciergeCABundleData = []byte(caBundleString)
|
||||
}
|
||||
}
|
||||
|
||||
// Append the flags to configure the Concierge credential exchange at runtime.
|
||||
execConfig.Args = append(execConfig.Args,
|
||||
"--enable-concierge",
|
||||
@ -393,14 +388,53 @@ func configureConcierge(credentialIssuer *configv1alpha1.CredentialIssuer, authe
|
||||
"--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 {
|
||||
log.Info("switching kubeconfig cluster to point at impersonation proxy endpoint", "endpoint", flags.concierge.endpoint)
|
||||
v1Cluster.CertificateAuthorityData = conciergeCABundleData
|
||||
v1Cluster.Server = flags.concierge.endpoint
|
||||
// Point kubectl at the concierge endpoint.
|
||||
v1Cluster.Server = flags.concierge.endpoint
|
||||
v1Cluster.CertificateAuthorityData = conciergeCABundleData
|
||||
return nil
|
||||
}
|
||||
|
||||
func getConciergeFrontend(credentialIssuer *configv1alpha1.CredentialIssuer, mode conciergeMode) (*configv1alpha1.CredentialIssuerFrontend, error) {
|
||||
for _, strategy := range credentialIssuer.Status.Strategies {
|
||||
// Skip unhealthy strategies.
|
||||
if strategy.Status != configv1alpha1.SuccessStrategyStatus {
|
||||
continue
|
||||
}
|
||||
|
||||
// Backfill the .status.strategies[].frontend field from .status.kubeConfigInfo for backwards compatibility.
|
||||
if strategy.Type == configv1alpha1.KubeClusterSigningCertificateStrategyType && strategy.Frontend == nil && credentialIssuer.Status.KubeConfigInfo != nil {
|
||||
strategy = *strategy.DeepCopy()
|
||||
strategy.Frontend = &configv1alpha1.CredentialIssuerFrontend{
|
||||
Type: configv1alpha1.TokenCredentialRequestAPIFrontendType,
|
||||
TokenCredentialRequestAPIInfo: &configv1alpha1.TokenCredentialRequestAPIInfo{
|
||||
Server: credentialIssuer.Status.KubeConfigInfo.Server,
|
||||
CertificateAuthorityData: credentialIssuer.Status.KubeConfigInfo.CertificateAuthorityData,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// If the strategy frontend is still nil, skip.
|
||||
if strategy.Frontend == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip any unknown frontend types.
|
||||
switch strategy.Frontend.Type {
|
||||
case configv1alpha1.TokenCredentialRequestAPIFrontendType, configv1alpha1.ImpersonationProxyFrontendType:
|
||||
default:
|
||||
continue
|
||||
}
|
||||
// Skip strategies that don't match --concierge-mode.
|
||||
if !mode.MatchesFrontend(strategy.Frontend) {
|
||||
continue
|
||||
}
|
||||
return strategy.Frontend, nil
|
||||
}
|
||||
|
||||
return nil
|
||||
if mode == modeUnknown {
|
||||
return nil, fmt.Errorf("could not autodiscover --concierge-mode")
|
||||
}
|
||||
return nil, fmt.Errorf("could not find successful Concierge strategy matching --concierge-mode=%s", mode.String())
|
||||
}
|
||||
|
||||
func loadCABundlePaths(paths []string) (string, error) {
|
||||
@ -614,3 +648,9 @@ func validateKubeconfig(ctx context.Context, flags getKubeconfigParams, kubeconf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func countCACerts(pemData []byte) int {
|
||||
pool := x509.NewCertPool()
|
||||
pool.AppendCertsFromPEM(pemData)
|
||||
return len(pool.Subjects())
|
||||
}
|
||||
|
@ -36,8 +36,10 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
testOIDCCABundlePath := filepath.Join(tmpdir, "testca.pem")
|
||||
require.NoError(t, ioutil.WriteFile(testOIDCCABundlePath, testOIDCCA.Bundle(), 0600))
|
||||
|
||||
testConciergeCA, err := certauthority.New(pkix.Name{CommonName: "Test Concierge CA"}, 1*time.Hour)
|
||||
require.NoError(t, err)
|
||||
testConciergeCABundlePath := filepath.Join(tmpdir, "testconciergeca.pem")
|
||||
require.NoError(t, ioutil.WriteFile(testConciergeCABundlePath, []byte("test-concierge-ca"), 0600))
|
||||
require.NoError(t, ioutil.WriteFile(testConciergeCABundlePath, testConciergeCA.Bundle(), 0600))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -324,7 +326,7 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: could not autodiscover --concierge-mode and none was provided
|
||||
Error: could not autodiscover --concierge-mode
|
||||
`),
|
||||
},
|
||||
{
|
||||
@ -375,6 +377,8 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
},
|
||||
wantLogs: []string{
|
||||
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
|
||||
`"level"=0 "msg"="discovered Concierge operating in impersonation proxy mode"`,
|
||||
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://impersonation-endpoint"`,
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
@ -410,10 +414,10 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
},
|
||||
wantLogs: []string{
|
||||
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
|
||||
`"level"=0 "msg"="detected Concierge in TokenCredentialRequest API mode"`,
|
||||
`"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 WebhookAuthenticator" "name"="test-authenticator"`,
|
||||
`"level"=0 "msg"="detected concierge endpoint" "endpoint"="https://fake-server-url-value"`,
|
||||
`"level"=0 "msg"="detected concierge CA bundle" "length"=37`,
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
@ -433,11 +437,22 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
Server: "https://concierge-endpoint",
|
||||
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
|
||||
},
|
||||
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
|
||||
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
|
||||
Status: configv1alpha1.SuccessStrategyStatus,
|
||||
Reason: configv1alpha1.FetchedKeyStrategyReason,
|
||||
Message: "Successfully fetched key",
|
||||
LastUpdateTime: metav1.Now(),
|
||||
// Simulate a previous version of CredentialIssuer that's missing this Frontend field.
|
||||
Frontend: nil,
|
||||
}},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.JWTAuthenticator{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
|
||||
Spec: conciergev1alpha1.JWTAuthenticatorSpec{
|
||||
Issuer: "https://test-issuer.example.com",
|
||||
Audience: "some-test-audience",
|
||||
TLS: &conciergev1alpha1.TLSSpec{
|
||||
CertificateAuthorityData: "invalid-base64",
|
||||
},
|
||||
@ -446,9 +461,12 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
},
|
||||
wantLogs: []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"`,
|
||||
`"level"=0 "msg"="detected OIDC issuer" "issuer"=""`,
|
||||
`"level"=0 "msg"="detected OIDC audience" "audience"=""`,
|
||||
`"level"=0 "msg"="discovered OIDC issuer" "issuer"="https://test-issuer.example.com"`,
|
||||
`"level"=0 "msg"="discovered OIDC audience" "audience"="some-test-audience"`,
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
@ -469,10 +487,18 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
Server: "https://concierge-endpoint",
|
||||
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
|
||||
},
|
||||
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
|
||||
Type: configv1alpha1.ImpersonationProxyStrategyType,
|
||||
Status: configv1alpha1.SuccessStrategyStatus,
|
||||
Reason: configv1alpha1.ListeningStrategyReason,
|
||||
Frontend: &configv1alpha1.CredentialIssuerFrontend{
|
||||
Type: configv1alpha1.ImpersonationProxyFrontendType,
|
||||
ImpersonationProxyInfo: &configv1alpha1.ImpersonationProxyInfo{
|
||||
Endpoint: "https://impersonation-proxy-endpoint.example.com",
|
||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
|
||||
@ -496,19 +522,28 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
Server: "https://concierge-endpoint",
|
||||
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
|
||||
},
|
||||
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
|
||||
Type: configv1alpha1.ImpersonationProxyStrategyType,
|
||||
Status: configv1alpha1.SuccessStrategyStatus,
|
||||
Reason: configv1alpha1.ListeningStrategyReason,
|
||||
Frontend: &configv1alpha1.CredentialIssuerFrontend{
|
||||
Type: configv1alpha1.ImpersonationProxyFrontendType,
|
||||
ImpersonationProxyInfo: &configv1alpha1.ImpersonationProxyInfo{
|
||||
Endpoint: "https://impersonation-proxy-endpoint.example.com",
|
||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
|
||||
},
|
||||
wantLogs: []string{
|
||||
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
|
||||
`"level"=0 "msg"="discovered Concierge operating in impersonation proxy mode"`,
|
||||
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://impersonation-proxy-endpoint.example.com"`,
|
||||
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=1`,
|
||||
`"level"=0 "msg"="discovered WebhookAuthenticator" "name"="test-authenticator"`,
|
||||
`"level"=0 "msg"="detected concierge endpoint" "endpoint"="https://fake-server-url-value"`,
|
||||
`"level"=0 "msg"="detected concierge CA bundle" "length"=37`,
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
@ -536,19 +571,28 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
Server: "https://concierge-endpoint",
|
||||
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
|
||||
},
|
||||
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
|
||||
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
|
||||
Status: configv1alpha1.SuccessStrategyStatus,
|
||||
Reason: configv1alpha1.FetchedKeyStrategyReason,
|
||||
Frontend: &configv1alpha1.CredentialIssuerFrontend{
|
||||
Type: configv1alpha1.TokenCredentialRequestAPIFrontendType,
|
||||
TokenCredentialRequestAPIInfo: &configv1alpha1.TokenCredentialRequestAPIInfo{
|
||||
Server: "https://concierge-endpoint.example.com",
|
||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
|
||||
},
|
||||
wantLogs: []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 WebhookAuthenticator" "name"="test-authenticator"`,
|
||||
`"level"=0 "msg"="detected concierge endpoint" "endpoint"="https://fake-server-url-value"`,
|
||||
`"level"=0 "msg"="detected concierge CA bundle" "length"=37`,
|
||||
},
|
||||
wantStdout: here.Doc(`
|
||||
apiVersion: v1
|
||||
@ -597,19 +641,28 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
Server: "https://concierge-endpoint",
|
||||
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
|
||||
},
|
||||
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
|
||||
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
|
||||
Status: configv1alpha1.SuccessStrategyStatus,
|
||||
Reason: configv1alpha1.FetchedKeyStrategyReason,
|
||||
Frontend: &configv1alpha1.CredentialIssuerFrontend{
|
||||
Type: configv1alpha1.TokenCredentialRequestAPIFrontendType,
|
||||
TokenCredentialRequestAPIInfo: &configv1alpha1.TokenCredentialRequestAPIInfo{
|
||||
Server: "https://concierge-endpoint.example.com",
|
||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
|
||||
},
|
||||
wantLogs: []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 WebhookAuthenticator" "name"="test-authenticator"`,
|
||||
`"level"=0 "msg"="detected concierge endpoint" "endpoint"="https://fake-server-url-value"`,
|
||||
`"level"=0 "msg"="detected concierge CA bundle" "length"=37`,
|
||||
},
|
||||
wantStdout: here.Doc(`
|
||||
apiVersion: v1
|
||||
@ -657,10 +710,18 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
Server: "https://concierge-endpoint",
|
||||
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
|
||||
},
|
||||
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
|
||||
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
|
||||
Status: configv1alpha1.SuccessStrategyStatus,
|
||||
Reason: configv1alpha1.FetchedKeyStrategyReason,
|
||||
Frontend: &configv1alpha1.CredentialIssuerFrontend{
|
||||
Type: configv1alpha1.TokenCredentialRequestAPIFrontendType,
|
||||
TokenCredentialRequestAPIInfo: &configv1alpha1.TokenCredentialRequestAPIInfo{
|
||||
Server: "https://concierge-endpoint.example.com",
|
||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.JWTAuthenticator{
|
||||
@ -676,12 +737,13 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
},
|
||||
wantLogs: []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"`,
|
||||
`"level"=0 "msg"="detected OIDC issuer" "issuer"="https://example.com/issuer"`,
|
||||
`"level"=0 "msg"="detected OIDC audience" "audience"="test-audience"`,
|
||||
`"level"=0 "msg"="detected OIDC CA bundle" "length"=587`,
|
||||
`"level"=0 "msg"="detected concierge endpoint" "endpoint"="https://fake-server-url-value"`,
|
||||
`"level"=0 "msg"="detected concierge CA bundle" "length"=37`,
|
||||
`"level"=0 "msg"="discovered OIDC issuer" "issuer"="https://example.com/issuer"`,
|
||||
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
|
||||
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
|
||||
},
|
||||
wantStdout: here.Docf(`
|
||||
apiVersion: v1
|
||||
@ -731,7 +793,8 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
"--concierge-api-group-suffix", "tuna.io",
|
||||
"--concierge-authenticator-type", "webhook",
|
||||
"--concierge-authenticator-name", "test-authenticator",
|
||||
"--concierge-endpoint", "https://concierge-endpoint.example.com",
|
||||
"--concierge-mode", "TokenCredentialRequestAPI",
|
||||
"--concierge-endpoint", "https://explicit-concierge-endpoint.example.com",
|
||||
"--concierge-ca-bundle", testConciergeCABundlePath,
|
||||
"--oidc-issuer", "https://example.com/issuer",
|
||||
"--oidc-skip-browser",
|
||||
@ -746,22 +809,33 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
Server: "https://concierge-endpoint",
|
||||
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
|
||||
},
|
||||
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
|
||||
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
|
||||
Status: configv1alpha1.SuccessStrategyStatus,
|
||||
Reason: configv1alpha1.FetchedKeyStrategyReason,
|
||||
Frontend: &configv1alpha1.CredentialIssuerFrontend{
|
||||
Type: configv1alpha1.TokenCredentialRequestAPIFrontendType,
|
||||
TokenCredentialRequestAPIInfo: &configv1alpha1.TokenCredentialRequestAPIInfo{
|
||||
Server: "https://concierge-endpoint.example.com",
|
||||
CertificateAuthorityData: "dGVzdC10Y3ItYXBpLWNh",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.WebhookAuthenticator{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
|
||||
},
|
||||
},
|
||||
wantLogs: []string{
|
||||
`"level"=0 "msg"="loaded Concierge certificate authority bundle" "roots"=1`,
|
||||
},
|
||||
wantStdout: here.Docf(`
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
|
||||
server: https://fake-server-url-value
|
||||
certificate-authority-data: %s
|
||||
server: https://explicit-concierge-endpoint.example.com
|
||||
name: pinniped
|
||||
contexts:
|
||||
- context:
|
||||
@ -783,8 +857,8 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
- --concierge-api-group-suffix=tuna.io
|
||||
- --concierge-authenticator-name=test-authenticator
|
||||
- --concierge-authenticator-type=webhook
|
||||
- --concierge-endpoint=https://concierge-endpoint.example.com
|
||||
- --concierge-ca-bundle-data=dGVzdC1jb25jaWVyZ2UtY2E=
|
||||
- --concierge-endpoint=https://explicit-concierge-endpoint.example.com
|
||||
- --concierge-ca-bundle-data=%s
|
||||
- --concierge-mode=TokenCredentialRequestAPI
|
||||
- --issuer=https://example.com/issuer
|
||||
- --client-id=pinniped-cli
|
||||
@ -798,22 +872,58 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
command: '.../path/to/pinniped'
|
||||
env: []
|
||||
provideClusterInfo: true
|
||||
`, base64.StdEncoding.EncodeToString(testOIDCCA.Bundle())),
|
||||
`,
|
||||
base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
|
||||
base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
|
||||
base64.StdEncoding.EncodeToString(testOIDCCA.Bundle()),
|
||||
),
|
||||
wantAPIGroupSuffix: "tuna.io",
|
||||
},
|
||||
{
|
||||
name: "configure impersonation proxy with autodetected JWT authenticator",
|
||||
name: "configure impersonation proxy with autodiscovered JWT authenticator",
|
||||
args: []string{
|
||||
"--kubeconfig", "./testdata/kubeconfig.yaml",
|
||||
"--concierge-ca-bundle", testConciergeCABundlePath,
|
||||
"--concierge-endpoint", "https://impersonation-proxy-endpoint.test",
|
||||
"--concierge-mode", "ImpersonationProxy",
|
||||
"--skip-validation",
|
||||
},
|
||||
conciergeObjects: []runtime.Object{
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
Strategies: []configv1alpha1.CredentialIssuerStrategy{
|
||||
// This TokenCredentialRequestAPI strategy would normally be chosen, but
|
||||
// --concierge-mode=ImpersonationProxy should force it to be skipped.
|
||||
{
|
||||
Type: "SomeType",
|
||||
Status: configv1alpha1.SuccessStrategyStatus,
|
||||
Reason: "SomeReason",
|
||||
Message: "Some message",
|
||||
LastUpdateTime: metav1.Now(),
|
||||
Frontend: &configv1alpha1.CredentialIssuerFrontend{
|
||||
Type: configv1alpha1.TokenCredentialRequestAPIFrontendType,
|
||||
TokenCredentialRequestAPIInfo: &configv1alpha1.TokenCredentialRequestAPIInfo{
|
||||
Server: "https://token-credential-request-api-endpoint.test",
|
||||
CertificateAuthorityData: "dGVzdC10Y3ItYXBpLWNh",
|
||||
},
|
||||
},
|
||||
},
|
||||
// The endpoint and CA from this impersonation proxy strategy should be autodiscovered.
|
||||
{
|
||||
Type: "SomeOtherType",
|
||||
Status: configv1alpha1.SuccessStrategyStatus,
|
||||
Reason: "SomeOtherReason",
|
||||
Message: "Some other message",
|
||||
LastUpdateTime: metav1.Now(),
|
||||
Frontend: &configv1alpha1.CredentialIssuerFrontend{
|
||||
Type: configv1alpha1.ImpersonationProxyFrontendType,
|
||||
ImpersonationProxyInfo: &configv1alpha1.ImpersonationProxyInfo{
|
||||
Endpoint: "https://impersonation-proxy-endpoint.test",
|
||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&conciergev1alpha1.JWTAuthenticator{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
|
||||
@ -828,17 +938,18 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
},
|
||||
wantLogs: []string{
|
||||
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
|
||||
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://impersonation-proxy-endpoint.test"`,
|
||||
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=1`,
|
||||
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
|
||||
`"level"=0 "msg"="detected OIDC issuer" "issuer"="https://example.com/issuer"`,
|
||||
`"level"=0 "msg"="detected OIDC audience" "audience"="test-audience"`,
|
||||
`"level"=0 "msg"="detected OIDC CA bundle" "length"=587`,
|
||||
`"level"=0 "msg"="switching kubeconfig cluster to point at impersonation proxy endpoint" "endpoint"="https://impersonation-proxy-endpoint.test"`,
|
||||
`"level"=0 "msg"="discovered OIDC issuer" "issuer"="https://example.com/issuer"`,
|
||||
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
|
||||
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
|
||||
},
|
||||
wantStdout: here.Docf(`
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: dGVzdC1jb25jaWVyZ2UtY2E=
|
||||
certificate-authority-data: %s
|
||||
server: https://impersonation-proxy-endpoint.test
|
||||
name: pinniped
|
||||
contexts:
|
||||
@ -862,7 +973,7 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
- --concierge-authenticator-name=test-authenticator
|
||||
- --concierge-authenticator-type=jwt
|
||||
- --concierge-endpoint=https://impersonation-proxy-endpoint.test
|
||||
- --concierge-ca-bundle-data=dGVzdC1jb25jaWVyZ2UtY2E=
|
||||
- --concierge-ca-bundle-data=%s
|
||||
- --concierge-mode=ImpersonationProxy
|
||||
- --issuer=https://example.com/issuer
|
||||
- --client-id=pinniped-cli
|
||||
@ -872,10 +983,14 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
command: '.../path/to/pinniped'
|
||||
env: []
|
||||
provideClusterInfo: true
|
||||
`, base64.StdEncoding.EncodeToString(testOIDCCA.Bundle())),
|
||||
`,
|
||||
base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
|
||||
base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
|
||||
base64.StdEncoding.EncodeToString(testOIDCCA.Bundle()),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "autodetect impersonation proxy with autodetected JWT authenticator",
|
||||
name: "autodetect impersonation proxy with autodiscovered JWT authenticator",
|
||||
args: []string{
|
||||
"--kubeconfig", "./testdata/kubeconfig.yaml",
|
||||
"--skip-validation",
|
||||
@ -929,12 +1044,13 @@ func TestGetKubeconfig(t *testing.T) {
|
||||
},
|
||||
wantLogs: []string{
|
||||
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
|
||||
`"level"=0 "msg"="detected Concierge in impersonation proxy mode" "endpoint"="https://impersonation-proxy-endpoint.test"`,
|
||||
`"level"=0 "msg"="discovered Concierge operating in impersonation proxy mode"`,
|
||||
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://impersonation-proxy-endpoint.test"`,
|
||||
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
|
||||
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
|
||||
`"level"=0 "msg"="detected OIDC issuer" "issuer"="https://example.com/issuer"`,
|
||||
`"level"=0 "msg"="detected OIDC audience" "audience"="test-audience"`,
|
||||
`"level"=0 "msg"="detected OIDC CA bundle" "length"=587`,
|
||||
`"level"=0 "msg"="switching kubeconfig cluster to point at impersonation proxy endpoint" "endpoint"="https://impersonation-proxy-endpoint.test"`,
|
||||
`"level"=0 "msg"="discovered OIDC issuer" "issuer"="https://example.com/issuer"`,
|
||||
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
|
||||
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
|
||||
},
|
||||
wantStdout: here.Docf(`
|
||||
apiVersion: v1
|
||||
|
Loading…
Reference in New Issue
Block a user