Add integration test for using "kubectl exec" through the impersonator
Signed-off-by: Margo Crawford <margaretc@vmware.com>
This commit is contained in:
parent
73419313ee
commit
49ec16038c
@ -64,7 +64,7 @@ func TestCLIGetKubeconfigStaticToken(t *testing.T) {
|
||||
} {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
stdout, stderr := runPinnipedCLI(t, pinnipedExe, tt.args...)
|
||||
stdout, stderr := runPinnipedCLI(t, nil, pinnipedExe, tt.args...)
|
||||
require.Equal(t, tt.expectStderr, stderr)
|
||||
|
||||
// Even the deprecated command should now generate a kubeconfig with the new "pinniped login static" command.
|
||||
@ -99,12 +99,13 @@ func TestCLIGetKubeconfigStaticToken(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func runPinnipedCLI(t *testing.T, pinnipedExe string, args ...string) (string, string) {
|
||||
func runPinnipedCLI(t *testing.T, envVars []string, pinnipedExe string, args ...string) (string, string) {
|
||||
t.Helper()
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd := exec.Command(pinnipedExe, args...)
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
cmd.Env = envVars
|
||||
require.NoErrorf(t, cmd.Run(), "stderr:\n%s\n\nstdout:\n%s\n\n", stderr.String(), stdout.String())
|
||||
return stdout.String(), stderr.String()
|
||||
}
|
||||
|
@ -4,11 +4,17 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -28,6 +34,7 @@ import (
|
||||
"go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
"go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
|
||||
"go.pinniped.dev/internal/concierge/impersonator"
|
||||
"go.pinniped.dev/internal/testutil"
|
||||
"go.pinniped.dev/internal/testutil/impersonationtoken"
|
||||
"go.pinniped.dev/test/library"
|
||||
)
|
||||
@ -351,6 +358,89 @@ func TestImpersonationProxy(t *testing.T) {
|
||||
require.EqualError(t, err, expectedErr)
|
||||
})
|
||||
|
||||
t.Run("kubectl as a client", func(t *testing.T) {
|
||||
// Create an RBAC rule to allow this user to read/write everything.
|
||||
library.CreateTestClusterRoleBinding(t,
|
||||
rbacv1.Subject{Kind: rbacv1.UserKind, APIGroup: rbacv1.GroupName, Name: env.TestUser.ExpectedUsername},
|
||||
rbacv1.RoleRef{Kind: "ClusterRole", APIGroup: rbacv1.GroupName, Name: "edit"},
|
||||
)
|
||||
// Wait for the above RBAC rule to take effect.
|
||||
library.WaitForUserToHaveAccess(t, env.TestUser.ExpectedUsername, []string{}, &v1.ResourceAttributes{
|
||||
Verb: "get", Group: "", Version: "v1", Resource: "namespaces",
|
||||
})
|
||||
|
||||
pinnipedExe := library.PinnipedCLIPath(t)
|
||||
tempDir := testutil.TempDir(t)
|
||||
|
||||
var envVarsWithProxy []string
|
||||
if !env.HasCapability(library.HasExternalLoadBalancerProvider) {
|
||||
// Only if you don't have a load balancer, use the squid proxy when it's available.
|
||||
envVarsWithProxy = append(os.Environ(), env.ProxyEnv()...)
|
||||
}
|
||||
|
||||
// Get the kubeconfig.
|
||||
getKubeConfigCmd := []string{"get", "kubeconfig",
|
||||
"--concierge-api-group-suffix", env.APIGroupSuffix,
|
||||
"--oidc-skip-browser",
|
||||
"--static-token", env.TestUser.Token,
|
||||
// Force the use of impersonation proxy strategy, but let it auto-discover the endpoint and CA.
|
||||
"--concierge-mode", "ImpersonationProxy"}
|
||||
t.Log("Running:", pinnipedExe, getKubeConfigCmd)
|
||||
kubeconfigYAML, getKubeConfigStderr := runPinnipedCLI(t, envVarsWithProxy, pinnipedExe, getKubeConfigCmd...)
|
||||
// "pinniped get kubectl" prints some status messages to stderr
|
||||
t.Log(getKubeConfigStderr)
|
||||
// Make sure that the "pinniped get kubeconfig" auto-discovered the impersonation proxy and we're going to
|
||||
// make our kubectl requests through the impersonation proxy. Avoid using require.Contains because the error
|
||||
// message would contain credentials.
|
||||
require.True(t,
|
||||
strings.Contains(kubeconfigYAML, "server: "+impersonationProxyURL+"\n"),
|
||||
"the generated kubeconfig did not include the expected impersonation server address: %s",
|
||||
impersonationProxyURL,
|
||||
)
|
||||
require.True(t,
|
||||
strings.Contains(kubeconfigYAML, "- --concierge-ca-bundle-data="+base64.StdEncoding.EncodeToString(impersonationProxyCACertPEM)+"\n"),
|
||||
"the generated kubeconfig did not include the base64 encoded version of this expected impersonation CA cert: %s",
|
||||
impersonationProxyCACertPEM,
|
||||
)
|
||||
|
||||
// Write the kubeconfig to a temp file.
|
||||
kubeconfigPath := filepath.Join(tempDir, "kubeconfig.yaml")
|
||||
require.NoError(t, ioutil.WriteFile(kubeconfigPath, []byte(kubeconfigYAML), 0600))
|
||||
|
||||
// Func to run kubeconfig commands.
|
||||
kubectl := func(args ...string) (string, string, error) {
|
||||
timeout, cancelFunc := context.WithTimeout(ctx, 2*time.Minute)
|
||||
defer cancelFunc()
|
||||
|
||||
allArgs := append([]string{"--kubeconfig", kubeconfigPath}, args...)
|
||||
//nolint:gosec // we are not performing malicious argument injection against ourselves
|
||||
kubectlCmd := exec.CommandContext(timeout, "kubectl", allArgs...)
|
||||
var stdout, stderr bytes.Buffer
|
||||
kubectlCmd.Stdout = &stdout
|
||||
kubectlCmd.Stderr = &stderr
|
||||
kubectlCmd.Env = envVarsWithProxy
|
||||
|
||||
t.Log("starting kubectl subprocess: kubectl", strings.Join(allArgs, " "))
|
||||
err := kubectlCmd.Run()
|
||||
t.Logf("kubectl stdout output: %s", stdout.String())
|
||||
t.Logf("kubectl stderr output: %s", stderr.String())
|
||||
return stdout.String(), stderr.String(), err
|
||||
}
|
||||
|
||||
// Get pods in concierge namespace and pick one.
|
||||
// We don't actually care which pod, just want to see that we can "exec echo" in one of them.
|
||||
pods, err := adminClient.CoreV1().Pods(env.ConciergeNamespace).List(ctx, metav1.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, len(pods.Items), 0)
|
||||
podName := pods.Items[0].Name
|
||||
|
||||
// Try "kubectl exec" through the impersonation proxy.
|
||||
echoString := "hello world"
|
||||
stdout, _, err := kubectl("exec", "--namespace", env.ConciergeNamespace, podName, "--", "echo", echoString)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, stdout, echoString+"\n")
|
||||
})
|
||||
|
||||
// Update configuration to force the proxy to disabled mode
|
||||
configMap := configMapForConfig(t, env, impersonator.Config{Mode: impersonator.ModeDisabled})
|
||||
if env.HasCapability(library.HasExternalLoadBalancerProvider) {
|
||||
|
@ -147,7 +147,7 @@ func TestE2EFullIntegration(t *testing.T) {
|
||||
sessionCachePath := tempDir + "/sessions.yaml"
|
||||
|
||||
// Run "pinniped get kubeconfig" to get a kubeconfig YAML.
|
||||
kubeconfigYAML, stderr := runPinnipedCLI(t, pinnipedExe, "get", "kubeconfig",
|
||||
kubeconfigYAML, stderr := runPinnipedCLI(t, nil, pinnipedExe, "get", "kubeconfig",
|
||||
"--concierge-api-group-suffix", env.APIGroupSuffix,
|
||||
"--concierge-authenticator-type", "jwt",
|
||||
"--concierge-authenticator-name", authenticator.Name,
|
||||
|
Loading…
Reference in New Issue
Block a user