add gMSA remote storage test

This commit is contained in:
Xinqi Li 2022-02-21 23:21:26 -08:00
parent c4f42306f8
commit 65a4d1cbe7

View File

@ -32,6 +32,8 @@ limitations under the License.
// * the cluster comprises at least one Linux node that accepts workloads - it // * the cluster comprises at least one Linux node that accepts workloads - it
// can be the master, but any other Linux node is fine too. This is needed for // can be the master, but any other Linux node is fine too. This is needed for
// the webhook's pod. // the webhook's pod.
// * in order to run "can read and write file to remote folder" test case, a folder (e.g. "write_test") need to be created
// in that AD domain and it should be shared with that GMSA account.
// All these assumptions are fulfilled by an AKS extension when setting up the AKS // All these assumptions are fulfilled by an AKS extension when setting up the AKS
// cluster we run daily e2e tests against, but they do make running this test // cluster we run daily e2e tests against, but they do make running this test
// outside of that very specific context pretty hard. // outside of that very specific context pretty hard.
@ -44,12 +46,14 @@ import (
"os" "os"
"os/exec" "os/exec"
"path" "path"
"regexp"
"strings" "strings"
"time" "time"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1" rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/uuid"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
@ -81,6 +85,9 @@ const (
// The name of the expected domain // The name of the expected domain
gmsaDomain = "k8sgmsa.lan" gmsaDomain = "k8sgmsa.lan"
// The shared folder on the expected domain for file-writing test
gmsaSharedFolder = "write_test"
) )
var _ = SIGDescribe("[Feature:Windows] GMSA Full [Serial] [Slow]", func() { var _ = SIGDescribe("[Feature:Windows] GMSA Full [Serial] [Slow]", func() {
@ -163,6 +170,73 @@ var _ = SIGDescribe("[Feature:Windows] GMSA Full [Serial] [Slow]", func() {
return true return true
}, 1*time.Minute, 1*time.Second).Should(gomega.BeTrue()) }, 1*time.Minute, 1*time.Second).Should(gomega.BeTrue())
}) })
ginkgo.It("can read and write file to remote SMB folder", func() {
defer ginkgo.GinkgoRecover()
ginkgo.By("finding the worker node that fulfills this test's assumptions")
nodes := findPreconfiguredGmsaNodes(f.ClientSet)
if len(nodes) != 1 {
e2eskipper.Skipf("Expected to find exactly one node with the %q label, found %d", gmsaFullNodeLabel, len(nodes))
}
node := nodes[0]
ginkgo.By("retrieving the contents of the GMSACredentialSpec custom resource manifest from the node")
crdManifestContents := retrieveCRDManifestFileContents(f, node)
ginkgo.By("downloading the GMSA webhook deploy script")
deployScriptPath, err := downloadFile(gmsaWebhookDeployScriptURL)
defer func() { os.Remove(deployScriptPath) }()
if err != nil {
framework.Failf(err.Error())
}
ginkgo.By("deploying the GMSA webhook")
webhookCleanUp, err := deployGmsaWebhook(f, deployScriptPath)
defer webhookCleanUp()
if err != nil {
framework.Failf(err.Error())
}
ginkgo.By("creating the GMSA custom resource")
customResourceCleanup, err := createGmsaCustomResource(f.Namespace.Name, crdManifestContents)
defer customResourceCleanup()
if err != nil {
framework.Failf(err.Error())
}
ginkgo.By("creating an RBAC role to grant use access to that GMSA resource")
rbacRoleName, rbacRoleCleanup, err := createRBACRoleForGmsa(f)
defer rbacRoleCleanup()
if err != nil {
framework.Failf(err.Error())
}
ginkgo.By("creating a service account")
serviceAccountName := createServiceAccount(f)
ginkgo.By("binding the RBAC role to the service account")
bindRBACRoleToServiceAccount(f, serviceAccountName, rbacRoleName)
ginkgo.By("creating a pod using the GMSA cred spec")
podName := createPodWithGmsa(f, serviceAccountName)
ginkgo.By("getting the ip of GMSA domain")
gmsaDomainIP := getGmsaDomainIP(f, podName)
ginkgo.By("checking that file can be read and write from the remote folder successfully")
filePath := fmt.Sprintf("\\\\%s\\%s\\write-test-%s.txt", gmsaDomainIP, gmsaSharedFolder, string(uuid.NewUUID())[0:4])
gomega.Eventually(func() bool {
// The filePath is a remote folder, do not change the format of it
_, _ = runKubectlExecInNamespace(f.Namespace.Name, podName, "--", "powershell.exe", "-Command", "echo 'This is a test file.' > "+filePath)
output, err := runKubectlExecInNamespace(f.Namespace.Name, podName, "powershell.exe", "--", "cat", filePath)
if err != nil {
framework.Logf("unable to get file from AD server: %s", err)
return false
}
return strings.Contains(output, "This is a test file.")
}, 1*time.Minute, 1*time.Second).Should(gomega.BeTrue())
})
}) })
}) })
@ -420,3 +494,16 @@ func runKubectlExecInNamespace(namespace string, args ...string) (string, error)
namespaceOption := fmt.Sprintf("--namespace=%s", namespace) namespaceOption := fmt.Sprintf("--namespace=%s", namespace)
return framework.RunKubectl(namespace, append([]string{"exec", namespaceOption}, args...)...) return framework.RunKubectl(namespace, append([]string{"exec", namespaceOption}, args...)...)
} }
func getGmsaDomainIP(f *framework.Framework, podName string) string {
output, _ := runKubectlExecInNamespace(f.Namespace.Name, podName, "powershell.exe", "--", "nslookup", gmsaDomain)
re := regexp.MustCompile(`(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}`)
idx := strings.Index(output, gmsaDomain)
submatchall := re.FindAllString(output[idx:], -1)
if len(submatchall) < 1 {
framework.Logf("fail to get the ip of the gmsa domain")
return ""
}
return submatchall[0]
}