From 6f384d35dafc3222e787f70f4ed5a04bc857db7e Mon Sep 17 00:00:00 2001 From: Ashwin Venkatesh Date: Fri, 4 Mar 2022 17:44:20 -0500 Subject: [PATCH] Add tests to validate login using the authmethods issues the desired token. --- .../server-acl-init/command_test.go | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) diff --git a/control-plane/subcommand/server-acl-init/command_test.go b/control-plane/subcommand/server-acl-init/command_test.go index 16f87f6846..1bfd87415a 100644 --- a/control-plane/subcommand/server-acl-init/command_test.go +++ b/control-plane/subcommand/server-acl-init/command_test.go @@ -2405,6 +2405,160 @@ func TestRun_PoliciesAndBindingRulesACLLoginInSecondaryDatacenterDuringFederatio } } +// Test that server-acl-init used the local auth method to create the desired token. +// The test works by running the login command and then ensuring that: +// * The token returned has the correct policy for the component. +// * The token returned has the correct role for the component. +func TestRun_ValidateLoginToken_TokensCreatedByLocalAuthMethod(t *testing.T) { + t.Parallel() + + cases := []struct { + ComponentName string + TokenFlags []string + Roles []string + }{ + { + ComponentName: "controller", + TokenFlags: []string{"-create-controller-token"}, + Roles: []string{resourcePrefix + "-controller-acl-role"}, + }, + } + for _, c := range cases { + t.Run(c.ComponentName, func(t *testing.T) { + k8s, testSvr := completeSetup(t) + defer testSvr.Stop() + _, jwtToken := setUpK8sServiceAccount(t, k8s, ns) + k8sMockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + if r != nil && r.URL.Path == "/apis/authentication.k8s.io/v1/tokenreviews" && r.Method == "POST" { + w.Write([]byte(tokenReviewsResponse(fmt.Sprintf("%s-%s", resourcePrefix, c.ComponentName), ns))) + } + if r != nil && r.URL.Path == fmt.Sprintf("/api/v1/namespaces/%s/serviceaccounts/%s", ns, fmt.Sprintf("%s-%s", resourcePrefix, c.ComponentName)) && + r.Method == "GET" { + w.Write([]byte(serviceAccountGetResponse(fmt.Sprintf("%s-%s", resourcePrefix, c.ComponentName), ns))) + } + })) + t.Cleanup(k8sMockServer.Close) + + // Run the command. + ui := cli.NewMockUi() + cmd := Command{ + UI: ui, + clientset: k8s, + } + cmdArgs := append([]string{ + "-timeout=500ms", + "-resource-prefix=" + resourcePrefix, + "-k8s-namespace=" + ns, + "-auth-method-host=" + k8sMockServer.URL, + "-server-address", strings.Split(testSvr.HTTPAddr, ":")[0], + "-server-port", strings.Split(testSvr.HTTPAddr, ":")[1], + }, c.TokenFlags...) + cmd.init() + responseCode := cmd.Run(cmdArgs) + require.Equal(t, 0, responseCode, ui.ErrorWriter.String()) + + // Do the login. + req := &api.ACLLoginParams{ + AuthMethod: "release-name-" + componentAuthMethod, + BearerToken: jwtToken, + Meta: map[string]string{}, + } + tokenlessConfig := &api.Config{ + Address: testSvr.HTTPAddr, + } + client, err := api.NewClient(tokenlessConfig) + require.NoError(t, err) + tok, _, err := client.ACL().Login(req, &api.WriteOptions{}) + require.NoError(t, err) + require.Equal(t, len(tok.Roles), len(c.Roles)) + for _, role := range tok.Roles { + require.Contains(t, c.Roles, role.Name) + } + }) + } +} + +// Test that server-acl-init used the global auth method to create the desired token. +// The test works by running the login command and then ensuring that: +// * The token returned has the correct policy for the component. +// * The token returned has the correct role for the component. +func TestRun_ValidateLoginToken_TokensCreatedByGlobalAuthMethod(t *testing.T) { + t.Parallel() + + cases := []struct { + ComponentName string + TokenFlags []string + Roles []string + }{ + { + ComponentName: "controller", + TokenFlags: []string{"-create-controller-token"}, + Roles: []string{resourcePrefix + "-controller-acl-role-dc2"}, + }, + } + for _, c := range cases { + t.Run(c.ComponentName, func(t *testing.T) { + bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + tokenFile := common.WriteTempFile(t, bootToken) + k8s, _, consulHTTPAddr, cleanup := mockReplicatedSetup(t, bootToken) + defer cleanup() + + _, jwtToken := setUpK8sServiceAccount(t, k8s, ns) + k8sMockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", "application/json") + if r != nil && r.URL.Path == "/apis/authentication.k8s.io/v1/tokenreviews" && r.Method == "POST" { + w.Write([]byte(tokenReviewsResponse(fmt.Sprintf("%s-%s", resourcePrefix, c.ComponentName), ns))) + } + if r != nil && r.URL.Path == fmt.Sprintf("/api/v1/namespaces/%s/serviceaccounts/%s", ns, fmt.Sprintf("%s-%s", resourcePrefix, c.ComponentName)) && + r.Method == "GET" { + w.Write([]byte(serviceAccountGetResponse(fmt.Sprintf("%s-%s", resourcePrefix, c.ComponentName), ns))) + } + })) + t.Cleanup(k8sMockServer.Close) + + // Run the command. + ui := cli.NewMockUi() + cmd := Command{ + UI: ui, + clientset: k8s, + } + cmdArgs := append([]string{ + "-federation", + "-timeout=1m", + "-resource-prefix=" + resourcePrefix, + "-k8s-namespace=" + ns, + "-acl-replication-token-file", tokenFile, + "-auth-method-host=" + k8sMockServer.URL, + "-server-address", strings.Split(consulHTTPAddr, ":")[0], + "-server-port", strings.Split(consulHTTPAddr, ":")[1], + }, c.TokenFlags...) + cmd.init() + responseCode := cmd.Run(cmdArgs) + require.Equal(t, 0, responseCode, ui.ErrorWriter.String()) + + // Do the login. + req := &api.ACLLoginParams{ + AuthMethod: "release-name-" + componentAuthMethod + "-dc2", + BearerToken: jwtToken, + Meta: map[string]string{}, + } + tokenlessConfig := &api.Config{ + Address: consulHTTPAddr, + Datacenter: "dc1", + } + client, err := api.NewClient(tokenlessConfig) + require.NoError(t, err) + tok, _, err := client.ACL().Login(req, &api.WriteOptions{}) + require.NoError(t, err) + require.Equal(t, len(tok.Roles), len(c.Roles)) + for _, role := range tok.Roles { + require.Contains(t, c.Roles, role.Name) + } + }) + } +} + // Test that the component auth method gets created. func TestRun_ComponentAuthMethod(t *testing.T) { t.Parallel() @@ -2749,5 +2903,50 @@ func policyExists(t require.TestingT, name string, client *api.Client) *api.ACLP return policy } +func tokenReviewsResponse(name, ns string) string { + return fmt.Sprintf(`{ + "kind": "TokenReview", + "apiVersion": "authentication.k8s.io/v1", + "metadata": { + "creationTimestamp": null + }, + "spec": { + "token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImtoYWtpLWFyYWNobmlkLWNvbnN1bC1jb25uZWN0LWluamVjdG9yLWF1dGhtZXRob2Qtc3ZjLWFjY29obmRidiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJraGFraS1hcmFjaG5pZC1jb25zdWwtY29ubmVjdC1pbmplY3Rvci1hdXRobWV0aG9kLXN2Yy1hY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiN2U5NWUxMjktZTQ3My0xMWU5LThmYWEtNDIwMTBhODAwMTIyIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6a2hha2ktYXJhY2huaWQtY29uc3VsLWNvbm5lY3QtaW5qZWN0b3ItYXV0aG1ldGhvZC1zdmMtYWNjb3VudCJ9.Yi63MMtzh5MBWKKd3a7dzCJjTITE15ikFy_Tnpdk_AwdwA9J4AMSGEeHN5vWtCuuFjo_lMJqBBPHkK2AqbnoFUj9m5CopWyqICJQlvEOP4fUQ-Rc0W1P_JjU1rZERHG39b5TMLgKPQguyhaiZEJ6CjVtm9wUTagrgiuqYV2iUqLuF6SYNm6SrKtkPS-lqIO-u7C06wVk5m5uqwIVQNpZSIC_5Ls5aLmyZU3nHvH-V7E3HmBhVyZAB76jgKB0TyVX1IOskt9PDFarNtU3suZyCjvqC-UJA6sYeySe4dBNKsKlSZ6YuxUUmn1Rgv32YMdImnsWg8khf-zJvqgWk7B5EA" + }, + "status": { + "authenticated": true, + "user": { + "username": "system:serviceaccount:%s:%s", + "uid": "9ff51ff4-557e-11e9-9687-48e6c8b8ecb5", + "groups": [ + "system:serviceaccounts", + "system:serviceaccounts:%s", + "system:authenticated" + ] + } + } +}`, ns, name, ns) +} + +func serviceAccountGetResponse(name, ns string) string { + return fmt.Sprintf(`{ + "kind": "ServiceAccount", + "apiVersion": "v1", + "metadata": { + "name": "%s", + "namespace": "%s", + "selfLink": "/api/v1/namespaces/%s/serviceaccounts/%s", + "uid": "9ff51ff4-557e-11e9-9687-48e6c8b8ecb5", + "resourceVersion": "2101", + "creationTimestamp": "2019-04-02T19:36:34Z" + }, + "secrets": [ + { + "name": "%s-token-m9cvn" + } + ] +}`, name, ns, ns, name, name) +} + var serviceAccountCACert = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURDekNDQWZPZ0F3SUJBZ0lRS3pzN05qbDlIczZYYzhFWG91MjVoekFOQmdrcWhraUc5dzBCQVFzRkFEQXYKTVMwd0t3WURWUVFERXlRMU9XVTJaR00wTVMweU1EaG1MVFF3T1RVdFlUSTRPUzB4Wm1NM01EQmhZekZqWXpndwpIaGNOTVRrd05qQTNNVEF4TnpNeFdoY05NalF3TmpBMU1URXhOek14V2pBdk1TMHdLd1lEVlFRREV5UTFPV1UyClpHTTBNUzB5TURobUxUUXdPVFV0WVRJNE9TMHhabU0zTURCaFl6RmpZemd3Z2dFaU1BMEdDU3FHU0liM0RRRUIKQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUURaakh6d3FvZnpUcEdwYzBNZElDUzdldXZmdWpVS0UzUEMvYXBmREFnQgo0anpFRktBNzgvOStLVUd3L2MvMFNIZVNRaE4rYThnd2xIUm5BejFOSmNmT0lYeTRkd2VVdU9rQWlGeEg4cGh0CkVDd2tlTk83ejhEb1Y4Y2VtaW5DUkhHamFSbW9NeHBaN2cycFpBSk5aZVB4aTN5MWFOa0ZBWGU5Z1NVU2RqUloKUlhZa2E3d2gyQU85azJkbEdGQVlCK3Qzdld3SjZ0d2pHMFR0S1FyaFlNOU9kMS9vTjBFMDFMekJjWnV4a04xawo4Z2ZJSHk3Yk9GQ0JNMldURURXLzBhQXZjQVByTzhETHFESis2TWpjM3I3K3psemw4YVFzcGIwUzA4cFZ6a2k1CkR6Ly84M2t5dTBwaEp1aWo1ZUI4OFY3VWZQWHhYRi9FdFY2ZnZyTDdNTjRmQWdNQkFBR2pJekFoTUE0R0ExVWQKRHdFQi93UUVBd0lDQkRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFCdgpRc2FHNnFsY2FSa3RKMHpHaHh4SjUyTm5SVjJHY0lZUGVOM1p2MlZYZTNNTDNWZDZHMzJQVjdsSU9oangzS21BCi91TWg2TmhxQnpzZWtrVHowUHVDM3dKeU0yT0dvblZRaXNGbHF4OXNGUTNmVTJtSUdYQ2Ezd0M4ZS9xUDhCSFMKdzcvVmVBN2x6bWozVFFSRS9XMFUwWkdlb0F4bjliNkp0VDBpTXVjWXZQMGhYS1RQQldsbnpJaWphbVU1MHIyWQo3aWEwNjVVZzJ4VU41RkxYL3Z4T0EzeTRyanBraldvVlFjdTFwOFRaclZvTTNkc0dGV3AxMGZETVJpQUhUdk9ICloyM2pHdWs2cm45RFVIQzJ4UGozd0NUbWQ4U0dFSm9WMzFub0pWNWRWZVE5MHd1c1h6M3ZURzdmaWNLbnZIRlMKeHRyNVBTd0gxRHVzWWZWYUdIMk8KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=" var serviceAccountToken = "ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklpSjkuZXlKcGMzTWlPaUpyZFdKbGNtNWxkR1Z6TDNObGNuWnBZMlZoWTJOdmRXNTBJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5dVlXMWxjM0JoWTJVaU9pSmtaV1poZFd4MElpd2lhM1ZpWlhKdVpYUmxjeTVwYnk5elpYSjJhV05sWVdOamIzVnVkQzl6WldOeVpYUXVibUZ0WlNJNkltdG9ZV3RwTFdGeVlXTm9ibWxrTFdOdmJuTjFiQzFqYjI1dVpXTjBMV2x1YW1WamRHOXlMV0YxZEdodFpYUm9iMlF0YzNaakxXRmpZMjlvYm1SaWRpSXNJbXQxWW1WeWJtVjBaWE11YVc4dmMyVnlkbWxqWldGalkyOTFiblF2YzJWeWRtbGpaUzFoWTJOdmRXNTBMbTVoYldVaU9pSnJhR0ZyYVMxaGNtRmphRzVwWkMxamIyNXpkV3d0WTI5dWJtVmpkQzFwYm1wbFkzUnZjaTFoZFhSb2JXVjBhRzlrTFhOMll5MWhZMk52ZFc1MElpd2lhM1ZpWlhKdVpYUmxjeTVwYnk5elpYSjJhV05sWVdOamIzVnVkQzl6WlhKMmFXTmxMV0ZqWTI5MWJuUXVkV2xrSWpvaU4yVTVOV1V4TWprdFpUUTNNeTB4TVdVNUxUaG1ZV0V0TkRJd01UQmhPREF3TVRJeUlpd2ljM1ZpSWpvaWMzbHpkR1Z0T25ObGNuWnBZMlZoWTJOdmRXNTBPbVJsWm1GMWJIUTZhMmhoYTJrdFlYSmhZMmh1YVdRdFkyOXVjM1ZzTFdOdmJtNWxZM1F0YVc1cVpXTjBiM0l0WVhWMGFHMWxkR2h2WkMxemRtTXRZV05qYjNWdWRDSjkuWWk2M01NdHpoNU1CV0tLZDNhN2R6Q0pqVElURTE1aWtGeV9UbnBka19Bd2R3QTlKNEFNU0dFZUhONXZXdEN1dUZqb19sTUpxQkJQSGtLMkFxYm5vRlVqOW01Q29wV3lxSUNKUWx2RU9QNGZVUS1SYzBXMVBfSmpVMXJaRVJIRzM5YjVUTUxnS1BRZ3V5aGFpWkVKNkNqVnRtOXdVVGFncmdpdXFZVjJpVXFMdUY2U1lObTZTckt0a1BTLWxxSU8tdTdDMDZ3Vms1bTV1cXdJVlFOcFpTSUNfNUxzNWFMbXlaVTNuSHZILVY3RTNIbUJoVnlaQUI3NmpnS0IwVHlWWDFJT3NrdDlQREZhck50VTNzdVp5Q2p2cUMtVUpBNnNZZXlTZTRkQk5Lc0tsU1o2WXV4VVVtbjFSZ3YzMllNZEltbnNXZzhraGYtekp2cWdXazdCNUVB"