impersonation proxy: assert nested UID impersonation is disallowed
Signed-off-by: Monis Khan <mok@vmware.com>
This commit is contained in:
parent
5678fc6196
commit
34fd0ea2e2
@ -751,6 +751,79 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl
|
|||||||
}, err)
|
}, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("nested impersonation as a cluster admin fails if UID impersonation is attempted", func(t *testing.T) {
|
||||||
|
parallelIfNotEKS(t)
|
||||||
|
adminClientRestConfig := testlib.NewClientConfig(t)
|
||||||
|
clusterAdminCredentials := getCredForConfig(t, adminClientRestConfig)
|
||||||
|
|
||||||
|
nestedImpersonationUIDOnly := newImpersonationProxyConfigWithCredentials(t,
|
||||||
|
clusterAdminCredentials, impersonationProxyURL, impersonationProxyCACertPEM, nil,
|
||||||
|
)
|
||||||
|
nestedImpersonationUIDOnly.Wrap(func(rt http.RoundTripper) http.RoundTripper {
|
||||||
|
return roundtripper.Func(func(r *http.Request) (*http.Response, error) {
|
||||||
|
r.Header.Set("iMperSONATE-uid", "some-awesome-uid")
|
||||||
|
return rt.RoundTrip(r)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
_, errUID := testlib.NewKubeclient(t, nestedImpersonationUIDOnly).Kubernetes.CoreV1().Secrets("foo").Get(ctx, "bar", metav1.GetOptions{})
|
||||||
|
msg := `Internal Server Error: "/api/v1/namespaces/foo/secrets/bar": requested [{UID some-awesome-uid authentication.k8s.io/v1 }] without impersonating a user`
|
||||||
|
full := fmt.Sprintf(`an error on the server (%q) has prevented the request from succeeding (get secrets bar)`, msg)
|
||||||
|
require.EqualError(t, errUID, full)
|
||||||
|
require.True(t, k8serrors.IsInternalError(errUID), errUID)
|
||||||
|
require.Equal(t, &k8serrors.StatusError{
|
||||||
|
ErrStatus: metav1.Status{
|
||||||
|
Status: metav1.StatusFailure,
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
Reason: metav1.StatusReasonInternalError,
|
||||||
|
Details: &metav1.StatusDetails{
|
||||||
|
Name: "bar",
|
||||||
|
Kind: "secrets",
|
||||||
|
Causes: []metav1.StatusCause{
|
||||||
|
{
|
||||||
|
Type: metav1.CauseTypeUnexpectedServerResponse,
|
||||||
|
Message: msg,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Message: full,
|
||||||
|
},
|
||||||
|
}, errUID)
|
||||||
|
|
||||||
|
nestedImpersonationUID := newImpersonationProxyConfigWithCredentials(t,
|
||||||
|
clusterAdminCredentials, impersonationProxyURL, impersonationProxyCACertPEM,
|
||||||
|
&rest.ImpersonationConfig{
|
||||||
|
UserName: "other-user-to-impersonate",
|
||||||
|
Groups: []string{"system:masters"}, // impersonate system:masters so we get past authorization checks
|
||||||
|
},
|
||||||
|
)
|
||||||
|
nestedImpersonationUID.Wrap(func(rt http.RoundTripper) http.RoundTripper {
|
||||||
|
return roundtripper.Func(func(r *http.Request) (*http.Response, error) {
|
||||||
|
r.Header.Set("imperSONate-uiD", "some-fancy-uid")
|
||||||
|
return rt.RoundTrip(r)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := testlib.NewKubeclient(t, nestedImpersonationUID).Kubernetes.CoreV1().Secrets(env.ConciergeNamespace).Get(ctx, impersonationProxyTLSSecretName(env), metav1.GetOptions{})
|
||||||
|
require.EqualError(t, err, "Internal error occurred: unimplemented functionality - unable to act as current user")
|
||||||
|
require.True(t, k8serrors.IsInternalError(err), err)
|
||||||
|
require.Equal(t, &k8serrors.StatusError{
|
||||||
|
ErrStatus: metav1.Status{
|
||||||
|
Status: metav1.StatusFailure,
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
Reason: metav1.StatusReasonInternalError,
|
||||||
|
Details: &metav1.StatusDetails{
|
||||||
|
Causes: []metav1.StatusCause{
|
||||||
|
{
|
||||||
|
Message: "unimplemented functionality - unable to act as current user",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Message: "Internal error occurred: unimplemented functionality - unable to act as current user",
|
||||||
|
},
|
||||||
|
}, err)
|
||||||
|
})
|
||||||
|
|
||||||
// this works because impersonation cannot set UID and thus the final user info the proxy sees has no UID
|
// this works because impersonation cannot set UID and thus the final user info the proxy sees has no UID
|
||||||
t.Run("nested impersonation as a service account is allowed if it has enough RBAC permissions", func(t *testing.T) {
|
t.Run("nested impersonation as a service account is allowed if it has enough RBAC permissions", func(t *testing.T) {
|
||||||
parallelIfNotEKS(t)
|
parallelIfNotEKS(t)
|
||||||
@ -2168,6 +2241,13 @@ func createTokenCredentialRequest(
|
|||||||
func newImpersonationProxyClientWithCredentials(t *testing.T, credentials *loginv1alpha1.ClusterCredential, impersonationProxyURL string, impersonationProxyCACertPEM []byte, nestedImpersonationConfig *rest.ImpersonationConfig) *kubeclient.Client {
|
func newImpersonationProxyClientWithCredentials(t *testing.T, credentials *loginv1alpha1.ClusterCredential, impersonationProxyURL string, impersonationProxyCACertPEM []byte, nestedImpersonationConfig *rest.ImpersonationConfig) *kubeclient.Client {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
kubeconfig := newImpersonationProxyConfigWithCredentials(t, credentials, impersonationProxyURL, impersonationProxyCACertPEM, nestedImpersonationConfig)
|
||||||
|
return testlib.NewKubeclient(t, kubeconfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newImpersonationProxyConfigWithCredentials(t *testing.T, credentials *loginv1alpha1.ClusterCredential, impersonationProxyURL string, impersonationProxyCACertPEM []byte, nestedImpersonationConfig *rest.ImpersonationConfig) *rest.Config {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
env := testlib.IntegrationEnv(t)
|
env := testlib.IntegrationEnv(t)
|
||||||
clusterSupportsLoadBalancers := env.HasCapability(testlib.HasExternalLoadBalancerProvider)
|
clusterSupportsLoadBalancers := env.HasCapability(testlib.HasExternalLoadBalancerProvider)
|
||||||
|
|
||||||
@ -2177,7 +2257,7 @@ func newImpersonationProxyClientWithCredentials(t *testing.T, credentials *login
|
|||||||
// Prefer to go through a load balancer because that's how the impersonator is intended to be used in the real world.
|
// Prefer to go through a load balancer because that's how the impersonator is intended to be used in the real world.
|
||||||
kubeconfig.Proxy = kubeconfigProxyFunc(t, env.Proxy)
|
kubeconfig.Proxy = kubeconfigProxyFunc(t, env.Proxy)
|
||||||
}
|
}
|
||||||
return testlib.NewKubeclient(t, kubeconfig)
|
return kubeconfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAnonymousImpersonationProxyClient(t *testing.T, impersonationProxyURL string, impersonationProxyCACertPEM []byte, nestedImpersonationConfig *rest.ImpersonationConfig) *kubeclient.Client {
|
func newAnonymousImpersonationProxyClient(t *testing.T, impersonationProxyURL string, impersonationProxyCACertPEM []byte, nestedImpersonationConfig *rest.ImpersonationConfig) *kubeclient.Client {
|
||||||
|
Loading…
Reference in New Issue
Block a user