From 2f7113dc32d4f1f5375a1ae09b65be58f6801a15 Mon Sep 17 00:00:00 2001 From: Peter Matseykanets Date: Mon, 5 Feb 2024 14:07:24 -0500 Subject: [PATCH] [2.8] Fixes (#44334) * Add a check for specific fields we don't want in the headers (#372) * Split reconcileProjectAccessToGlobalResources * Add tests * Check APIGroup * Add tests and change cr client used * [2.8] Bump API-UI version #432 * Update norman and apiserver * Update RKE to 1.5.3 * Regenerate files after updating RKE to 1.5.3 * Update runc to 1.1.12 --------- Co-authored-by: Jonathan Crowther Co-authored-by: Ricardo Weir Co-authored-by: Michael Bolot --- go.mod | 10 +- go.sum | 20 +- package/Dockerfile | 2 +- pkg/apis/go.mod | 4 +- pkg/apis/go.sum | 8 +- pkg/auth/audit/audit.go | 8 +- pkg/auth/audit/audit_test.go | 22 ++ .../v3/zz_generated_aci_network_provider.go | 20 +- pkg/client/go.mod | 2 +- pkg/client/go.sum | 4 +- .../managementuser/rbac/handler_base_test.go | 199 +++++++++++--- .../managementuser/rbac/namespace_handler.go | 4 +- .../rbac/namespace_handler_test.go | 169 +++++++++--- .../rbac/project_handler_test.go | 39 +-- .../managementuser/rbac/prtb_handler.go | 17 +- .../rbac/reconcile_roletemplate.go | 117 ++++---- .../rbac/reconcile_roletemplate_test.go | 255 ++++++++++++++++++ .../rbac/roletemplate_handler.go | 16 +- pkg/settings/setting.go | 2 +- .../v3/zz_generated_aci_network_provider.go | 20 +- tests/v2/codecoverage/package/Dockerfile | 2 +- 21 files changed, 741 insertions(+), 199 deletions(-) create mode 100644 pkg/controllers/managementuser/rbac/reconcile_roletemplate_test.go diff --git a/go.mod b/go.mod index 8f4a11b3dcf..4b59b0ab1f1 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,8 @@ replace ( github.com/matryer/moq => github.com/rancher/moq v0.0.0-20200712062324-13d1f37d2d77 github.com/opencontainers/image-spec => github.com/opencontainers/image-spec v1.1.0-rc2 // needed for containers/image/v5 + github.com/opencontainers/runc => github.com/opencontainers/runc v1.1.12 + github.com/rancher/rancher/pkg/apis => ./pkg/apis github.com/rancher/rancher/pkg/client => ./pkg/client @@ -109,7 +111,7 @@ require ( github.com/prometheus/client_model v0.4.0 github.com/prometheus/common v0.44.0 github.com/rancher/aks-operator v1.2.0 - github.com/rancher/apiserver v0.0.0-20230831052300-120e615b17ba + github.com/rancher/apiserver v0.0.0-20240205154815-a3b9e3721c1b github.com/rancher/channelserver v0.5.1-0.20230719220800-0a37b73c7df8 github.com/rancher/dynamiclistener v0.3.6 github.com/rancher/eks-operator v1.3.0 @@ -118,10 +120,10 @@ require ( github.com/rancher/kubernetes-provider-detector v0.1.5 github.com/rancher/lasso v0.0.0-20230830164424-d684fdeb6f29 github.com/rancher/machine v0.15.0-rancher106 - github.com/rancher/norman v0.0.0-20230831160711-5de27f66385d + github.com/rancher/norman v0.0.0-20240205154641-a6a6cf569608 github.com/rancher/rancher/pkg/client v0.0.0 github.com/rancher/remotedialer v0.3.0 - github.com/rancher/rke v1.5.2 + github.com/rancher/rke v1.5.3 github.com/rancher/steve v0.0.0-20231016202603-993540401906 github.com/rancher/system-upgrade-controller/pkg/apis v0.0.0-20210727200656-10b094e30007 github.com/rancher/wrangler v1.1.1 @@ -269,7 +271,7 @@ require ( github.com/containerd/containerd v1.7.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/cyphar/filepath-securejoin v0.2.3 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/docker/cli v23.0.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect diff --git a/go.sum b/go.sum index 2c0452c65c5..9bbe2d39b4e 100644 --- a/go.sum +++ b/go.sum @@ -249,8 +249,8 @@ github.com/crewjam/saml v0.4.13 h1:TYHggH/hwP7eArqiXSJUvtOPNzQDyQ7vwmwEqlFWhMc= github.com/crewjam/saml v0.4.13/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA= github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 h1:vU+EP9ZuFUCYE0NYLwTSob+3LNEJATzNfP/DC7SWGWI= github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= -github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -918,8 +918,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM= -github.com/opencontainers/runc v1.1.9/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= +github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= +github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= github.com/opencontainers/runtime-spec v1.1.0-rc.1 h1:wHa9jroFfKGQqFHj0I1fMRKLl0pfj+ynAqBxo3v6u9w= github.com/opencontainers/runtime-spec v1.1.0-rc.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= @@ -1003,8 +1003,8 @@ github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPH github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rancher/aks-operator v1.2.0 h1:cNB84j23Ng7GUkqIt8I1TUfkpPdA5SQ2uyosPNJM5G4= github.com/rancher/aks-operator v1.2.0/go.mod h1:CIU0AgI4DHYKEG3P3tHyEM/5QEud7upDOiYL6j5D/qE= -github.com/rancher/apiserver v0.0.0-20230831052300-120e615b17ba h1:ceAHvddZkuNbUTuMgqxYAcUSQ/+YtJQO9Z1PHjmQZBY= -github.com/rancher/apiserver v0.0.0-20230831052300-120e615b17ba/go.mod h1:1m5KKYXq6iMZFQ5kiC9rBgVLfGRNR8E+lp88f5tEAsI= +github.com/rancher/apiserver v0.0.0-20240205154815-a3b9e3721c1b h1:dEKz4mxmTA9fGGRTPyhFHCOZV+Ap2s6I9l8mrLz+w5M= +github.com/rancher/apiserver v0.0.0-20240205154815-a3b9e3721c1b/go.mod h1:1m5KKYXq6iMZFQ5kiC9rBgVLfGRNR8E+lp88f5tEAsI= github.com/rancher/aws-iam-authenticator v0.5.9-0.20220713170329-78acb8c83863 h1:7cVEMgwyiVhLyu/Ywuw58mkkh9cWpFE3+X8IrWncBxU= github.com/rancher/aws-iam-authenticator v0.5.9-0.20220713170329-78acb8c83863/go.mod h1:6dId2LCc8oHqeBzP6E8ndp4DflhKTxYLb5ZXwI4YmFA= github.com/rancher/channelserver v0.5.1-0.20230719220800-0a37b73c7df8 h1:Dyg8vyYMBIImru9mo8EF40yRgNgGQQOpC/aA2vZ/SXY= @@ -1029,12 +1029,12 @@ github.com/rancher/machine v0.15.0-rancher106 h1:X7i+sqkvFilwTw0KQK4oiC813xm8UbL github.com/rancher/machine v0.15.0-rancher106/go.mod h1:nDJPIUiUO1a+HZ2lxpX95Djo7hJminwW6icga9FRc+w= github.com/rancher/moq v0.0.0-20200712062324-13d1f37d2d77 h1:k+vzmkZQsH06rZnDr+phskSixG9ByNj9gVdzHcc8nxw= github.com/rancher/moq v0.0.0-20200712062324-13d1f37d2d77/go.mod h1:wpITyDPTi/Na5h73XkbuEf2AP9fbgrIGqqxVzFhYD6U= -github.com/rancher/norman v0.0.0-20230831160711-5de27f66385d h1:Ft/iTH91TlE2oBGmpkdO4I8o8cvUmCnytdwu52a/tN4= -github.com/rancher/norman v0.0.0-20230831160711-5de27f66385d/go.mod h1:Sm2Xqai+aecgmJ86ygyEe+TdPMLkauEpykSstBAu4Ko= +github.com/rancher/norman v0.0.0-20240205154641-a6a6cf569608 h1:azL/n2grvuyGqmDvnpgRoH6mmpgodiGwjv1uZwiO7HE= +github.com/rancher/norman v0.0.0-20240205154641-a6a6cf569608/go.mod h1:Sm2Xqai+aecgmJ86ygyEe+TdPMLkauEpykSstBAu4Ko= github.com/rancher/remotedialer v0.3.0 h1:y1EO8JCsgZo0RcqTUp6U8FXcBAv27R+TLnWRcpvX1sM= github.com/rancher/remotedialer v0.3.0/go.mod h1:BwwztuvViX2JrLLUwDlsYt5DiyUwHLlzynRwkZLAY0Q= -github.com/rancher/rke v1.5.2 h1:e//fEtK2QIZ8Ok3d8oOrCPSQ2hVfJH46P46cvbfuS8U= -github.com/rancher/rke v1.5.2/go.mod h1:wZaVWzW46OTuGvyxgRHXGUyJ/QP0zOkKESO9hBOwTaY= +github.com/rancher/rke v1.5.3 h1:7mGn+NIL7KXk99NwWYBgoByh2+IfVCdws5ad3X/JIZY= +github.com/rancher/rke v1.5.3/go.mod h1:wZaVWzW46OTuGvyxgRHXGUyJ/QP0zOkKESO9hBOwTaY= github.com/rancher/steve v0.0.0-20231016202603-993540401906 h1:gToXZxM/5S5lze/vCpQs50PJ33QTGCOaJHzjYh6y1RE= github.com/rancher/steve v0.0.0-20231016202603-993540401906/go.mod h1:IAeZiWgZLSGGlYOUa3qj/G6i1eKl2LFuZ/DKb9mIrzw= github.com/rancher/system-upgrade-controller/pkg/apis v0.0.0-20210727200656-10b094e30007 h1:ru+mqGnxMmKeU0Q3XIDxkARvInDIqT1hH2amTcsjxI4= diff --git a/package/Dockerfile b/package/Dockerfile index d7143aab54f..d856cde1e99 100644 --- a/package/Dockerfile +++ b/package/Dockerfile @@ -178,7 +178,7 @@ ENV CATTLE_CLI_VERSION v2.8.0 ENV CATTLE_BASE_UI_BRAND= # Please update the api-ui-version in pkg/settings/settings.go when updating the version here. -ENV CATTLE_API_UI_VERSION 1.1.10 +ENV CATTLE_API_UI_VERSION 1.1.11 RUN mkdir -p /var/log/auditlog ENV AUDIT_LOG_PATH /var/log/auditlog/rancher-api-audit.log diff --git a/pkg/apis/go.mod b/pkg/apis/go.mod index d73d41f508c..7a327d7dafd 100644 --- a/pkg/apis/go.mod +++ b/pkg/apis/go.mod @@ -33,8 +33,8 @@ require ( github.com/rancher/eks-operator v1.3.0 github.com/rancher/fleet/pkg/apis v0.0.0-20231017140638-93432f288e79 github.com/rancher/gke-operator v1.2.0 - github.com/rancher/norman v0.0.0-20230831160711-5de27f66385d - github.com/rancher/rke v1.5.2 + github.com/rancher/norman v0.0.0-20240205154641-a6a6cf569608 + github.com/rancher/rke v1.5.3 github.com/rancher/wrangler v1.1.1 github.com/sirupsen/logrus v1.9.3 k8s.io/api v0.27.6 diff --git a/pkg/apis/go.sum b/pkg/apis/go.sum index 90d8f911efc..c4f5bca453b 100644 --- a/pkg/apis/go.sum +++ b/pkg/apis/go.sum @@ -523,10 +523,10 @@ github.com/rancher/gke-operator v1.2.0 h1:Byd7IYDlg8T6Dk+bqj8QOXOWnTH5+s9pRairBC github.com/rancher/gke-operator v1.2.0/go.mod h1:R6zrDS1ihOe+ai6cqDOT8JOcod7u4gHWdc1hToT6HP4= github.com/rancher/lasso v0.0.0-20230830164424-d684fdeb6f29 h1:+kige/h8/LnzWgPjB5NUIHz/pWiW/lFpqcTUkN5uulY= github.com/rancher/lasso v0.0.0-20230830164424-d684fdeb6f29/go.mod h1:kgk9kJVMj9FIrrXU0iyM6u/9Je4bEjPImqswkTVaKsQ= -github.com/rancher/norman v0.0.0-20230831160711-5de27f66385d h1:Ft/iTH91TlE2oBGmpkdO4I8o8cvUmCnytdwu52a/tN4= -github.com/rancher/norman v0.0.0-20230831160711-5de27f66385d/go.mod h1:Sm2Xqai+aecgmJ86ygyEe+TdPMLkauEpykSstBAu4Ko= -github.com/rancher/rke v1.5.2 h1:e//fEtK2QIZ8Ok3d8oOrCPSQ2hVfJH46P46cvbfuS8U= -github.com/rancher/rke v1.5.2/go.mod h1:wZaVWzW46OTuGvyxgRHXGUyJ/QP0zOkKESO9hBOwTaY= +github.com/rancher/norman v0.0.0-20240205154641-a6a6cf569608 h1:azL/n2grvuyGqmDvnpgRoH6mmpgodiGwjv1uZwiO7HE= +github.com/rancher/norman v0.0.0-20240205154641-a6a6cf569608/go.mod h1:Sm2Xqai+aecgmJ86ygyEe+TdPMLkauEpykSstBAu4Ko= +github.com/rancher/rke v1.5.3 h1:7mGn+NIL7KXk99NwWYBgoByh2+IfVCdws5ad3X/JIZY= +github.com/rancher/rke v1.5.3/go.mod h1:wZaVWzW46OTuGvyxgRHXGUyJ/QP0zOkKESO9hBOwTaY= github.com/rancher/wrangler v1.1.1-0.20230831050635-df1bd5aae9df h1:WJ+aaUICHPX8HeLmHE9JL/RFHhilMfcJlqmhgpc7gJU= github.com/rancher/wrangler v1.1.1-0.20230831050635-df1bd5aae9df/go.mod h1:4T80p+rLh2OLbjCjdExIjRHKNBgK9NUAd7eIU/gRPKk= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= diff --git a/pkg/auth/audit/audit.go b/pkg/auth/audit/audit.go index d9f18c62020..afd8dbe86ac 100644 --- a/pkg/auth/audit/audit.go +++ b/pkg/auth/audit/audit.go @@ -20,6 +20,7 @@ import ( "github.com/sirupsen/logrus" k8stypes "k8s.io/apimachinery/pkg/types" "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/utils/strings/slices" ) const ( @@ -50,8 +51,9 @@ var ( http.MethodPut: true, http.MethodPost: true, } - sensitiveRequestHeader = []string{"Cookie", "Authorization", "X-Api-Tunnel-Params", "X-Api-Tunnel-Token"} - sensitiveResponseHeader = []string{"Cookie", "Set-Cookie"} + sensitiveRequestHeader = []string{"Cookie", "Authorization", "X-Api-Tunnel-Params", "X-Api-Tunnel-Token", "X-Api-Auth-Header", "X-Amz-Security-Token"} + sensitiveResponseHeader = []string{"Cookie", "Set-Cookie", "X-Api-Set-Cookie-Header"} + sensitiveBodyFields = []string{"credentials", "applicationSecret", "oauthCredential", "serviceAccountCredential", "spKey", "spCert", "certificate", "privateKey"} // ErrUnsupportedEncoding is returned when the response encoding is unsupported ErrUnsupportedEncoding = fmt.Errorf("unsupported encoding") secretBaseType = regexp.MustCompile(".\"baseType\":\"([A-Za-z]*[S|s]ecret)\".") @@ -394,7 +396,7 @@ func (a *auditLog) redactMap(m map[string]interface{}) bool { for key := range m { switch val := m[key].(type) { case string: - if a.keysToRedactRegex.MatchString(key) { + if a.keysToRedactRegex.MatchString(key) || slices.Contains(sensitiveBodyFields, key) { changed = true m[key] = redacted } diff --git a/pkg/auth/audit/audit_test.go b/pkg/auth/audit/audit_test.go index 084ab946dec..67de7d64ab3 100644 --- a/pkg/auth/audit/audit_test.go +++ b/pkg/auth/audit/audit_test.go @@ -199,6 +199,11 @@ func (a *AuditTest) TestRedactSensitiveData() { want: []byte(fmt.Sprintf(`{"kubeConfig":"%s","namespace":"testns","secretName":"secret-name"}`, redacted)), uri: `asdf`, }, + { + name: "With items from sensitiveBodyFields", + input: []byte(`{"credentials": "{'fakeCredName': 'fakeCred'}", "applicationSecret": "fakeAppSecret", "oauthCredential": "fakeOauth", "serviceAccountCredential": "fakeSACred", "spKey": "fakeSPKey", "spCert": "fakeSPCERT", "certificate": "fakeCert", "privateKey": "fakeKey"}`), + want: []byte(fmt.Sprintf(`{"credentials": "%s", "applicationSecret": "%[1]s", "oauthCredential": "%[1]s", "serviceAccountCredential": "%[1]s", "spKey": "%[1]s", "spCert": "%[1]s", "certificate": "%[1]s", "privateKey": "%[1]s"}`, redacted)), + }, } for i := range tests { test := tests[i] @@ -437,6 +442,18 @@ func (a *AuditTest) TestFilterSensitiveHeader() { respHeader: http.Header{"Content-Type": []string{"application/json"}, "Content-Encoding": []string{"none"}}, expectedRespHeader: http.Header{"Content-Type": []string{"application/json"}, "Content-Encoding": []string{"none"}}, }, + { + name: "sensitive request header: \"X-Api-Auth-Header\"", + reqHeader: http.Header{"X-Api-Auth-Header": []string{"abcd"}}, + respHeader: http.Header{"Content-Type": []string{"application/json"}, "Content-Encoding": []string{"none"}}, + expectedRespHeader: http.Header{"Content-Type": []string{"application/json"}, "Content-Encoding": []string{"none"}}, + }, + { + name: "sensitive request header: \"X-Amz-Security-Token\"", + reqHeader: http.Header{"X-Amz-Security-Token": []string{"abcd"}}, + respHeader: http.Header{"Content-Type": []string{"application/json"}, "Content-Encoding": []string{"none"}}, + expectedRespHeader: http.Header{"Content-Type": []string{"application/json"}, "Content-Encoding": []string{"none"}}, + }, { name: "non-sensitive request header and sensitive request header: \"Cookie\"", reqHeader: http.Header{"Cookie": []string{"abcd"}, "User-Agent": []string{"useragent1"}}, @@ -454,6 +471,11 @@ func (a *AuditTest) TestFilterSensitiveHeader() { respHeader: http.Header{"Content-Type": []string{"application/json"}, "Content-Encoding": []string{"none"}, "Set-Cookie": []string{"abcd"}}, expectedRespHeader: http.Header{"Content-Type": []string{"application/json"}, "Content-Encoding": []string{"none"}}, }, + { + name: "sensitive response header: \"X-Api-Set-Cookie-Header\"", + respHeader: http.Header{"Content-Type": []string{"application/json"}, "Content-Encoding": []string{"none"}, "X-Api-Set-Cookie-Header": []string{"abcd"}}, + expectedRespHeader: http.Header{"Content-Type": []string{"application/json"}, "Content-Encoding": []string{"none"}}, + }, } writer.Level = LevelMetadata for i := range tests { diff --git a/pkg/client/generated/management/v3/zz_generated_aci_network_provider.go b/pkg/client/generated/management/v3/zz_generated_aci_network_provider.go index 5e626065e92..2a6360222d2 100644 --- a/pkg/client/generated/management/v3/zz_generated_aci_network_provider.go +++ b/pkg/client/generated/management/v3/zz_generated_aci_network_provider.go @@ -3,18 +3,15 @@ package client const ( AciNetworkProviderType = "aciNetworkProvider" AciNetworkProviderFieldAEP = "aep" - AciNetworkProviderFieldAccProvisionOperatorMemoryLimit = "accProvisionOperatorMemoryLimit" - AciNetworkProviderFieldAccProvisionOperatorMemoryRequest = "accProvisionOperatorMemoryRequest" AciNetworkProviderFieldAciContainersControllerMemoryLimit = "aciContainersControllerMemoryLimit" AciNetworkProviderFieldAciContainersControllerMemoryRequest = "aciContainersControllerMemoryRequest" AciNetworkProviderFieldAciContainersHostMemoryLimit = "aciContainersHostMemoryLimit" AciNetworkProviderFieldAciContainersHostMemoryRequest = "aciContainersHostMemoryRequest" AciNetworkProviderFieldAciContainersMemoryLimit = "aciContainersMemoryLimit" AciNetworkProviderFieldAciContainersMemoryRequest = "aciContainersMemoryRequest" - AciNetworkProviderFieldAciContainersOperatorMemoryLimit = "aciContainersOperatorMemoryLimit" - AciNetworkProviderFieldAciContainersOperatorMemoryRequest = "aciContainersOperatorMemoryRequest" AciNetworkProviderFieldAciMultipod = "aciMultipod" AciNetworkProviderFieldAciMultipodUbuntu = "aciMultipodUbuntu" + AciNetworkProviderFieldAddExternalContractToDefaultEpg = "addExternalContractToDefaultEpg" AciNetworkProviderFieldAddExternalSubnetsToRdconfig = "addExternalSubnetsToRdconfig" AciNetworkProviderFieldApicHosts = "apicHosts" AciNetworkProviderFieldApicRefreshTickerAdjust = "apicRefreshTickerAdjust" @@ -33,6 +30,7 @@ const ( AciNetworkProviderFieldDurationWaitForNetwork = "durationWaitForNetwork" AciNetworkProviderFieldDynamicExternalSubnet = "externDynamic" AciNetworkProviderFieldEnableEndpointSlice = "enableEndpointSlice" + AciNetworkProviderFieldEnableOpflexAgentReconnect = "enableOpflexAgentReconnect" AciNetworkProviderFieldEncapType = "encapType" AciNetworkProviderFieldEpRegistry = "epRegistry" AciNetworkProviderFieldGbpPodSubnet = "gbpPodSubnet" @@ -59,6 +57,7 @@ const ( AciNetworkProviderFieldNoPriorityClass = "noPriorityClass" AciNetworkProviderFieldNoWaitForServiceEpReadiness = "noWaitForServiceEpReadiness" AciNetworkProviderFieldNodePodIfEnable = "nodePodIfEnable" + AciNetworkProviderFieldNodeSnatRedirectExclude = "nodeSnatRedirectExclude" AciNetworkProviderFieldNodeSubnet = "nodeSubnet" AciNetworkProviderFieldOVSMemoryLimit = "ovsMemoryLimit" AciNetworkProviderFieldOVSMemoryRequest = "ovsMemoryRequest" @@ -68,10 +67,12 @@ const ( AciNetworkProviderFieldOpflexAgentOpflexAsyncjsonEnabled = "opflexAgentOpflexAsyncjsonEnabled" AciNetworkProviderFieldOpflexAgentOvsAsyncjsonEnabled = "opflexAgentOvsAsyncjsonEnabled" AciNetworkProviderFieldOpflexAgentPolicyRetryDelayTimer = "opflexAgentPolicyRetryDelayTimer" + AciNetworkProviderFieldOpflexAgentStatistics = "opflexAgentStatistics" AciNetworkProviderFieldOpflexClientSSL = "opflexClientSsl" AciNetworkProviderFieldOpflexDeviceDeleteTimeout = "opflexDeviceDeleteTimeout" AciNetworkProviderFieldOpflexDeviceReconnectWaitTimeout = "opflexDeviceReconnectWaitTimeout" AciNetworkProviderFieldOpflexMode = "opflexMode" + AciNetworkProviderFieldOpflexOpensslCompat = "opflexOpensslCompat" AciNetworkProviderFieldOpflexServerPort = "opflexServerPort" AciNetworkProviderFieldOverlayVRFName = "overlayVrfName" AciNetworkProviderFieldPBRTrackingNonSnat = "pbrTrackingNonSnat" @@ -95,6 +96,7 @@ const ( AciNetworkProviderFieldSystemIdentifier = "systemId" AciNetworkProviderFieldTenant = "tenant" AciNetworkProviderFieldToken = "token" + AciNetworkProviderFieldTolerationSeconds = "tolerationSeconds" AciNetworkProviderFieldUseAciAnywhereCRD = "useAciAnywhereCrd" AciNetworkProviderFieldUseAciCniPriorityClass = "useAciCniPriorityClass" AciNetworkProviderFieldUseClusterRole = "useClusterRole" @@ -110,18 +112,15 @@ const ( type AciNetworkProvider struct { AEP string `json:"aep,omitempty" yaml:"aep,omitempty"` - AccProvisionOperatorMemoryLimit string `json:"accProvisionOperatorMemoryLimit,omitempty" yaml:"accProvisionOperatorMemoryLimit,omitempty"` - AccProvisionOperatorMemoryRequest string `json:"accProvisionOperatorMemoryRequest,omitempty" yaml:"accProvisionOperatorMemoryRequest,omitempty"` AciContainersControllerMemoryLimit string `json:"aciContainersControllerMemoryLimit,omitempty" yaml:"aciContainersControllerMemoryLimit,omitempty"` AciContainersControllerMemoryRequest string `json:"aciContainersControllerMemoryRequest,omitempty" yaml:"aciContainersControllerMemoryRequest,omitempty"` AciContainersHostMemoryLimit string `json:"aciContainersHostMemoryLimit,omitempty" yaml:"aciContainersHostMemoryLimit,omitempty"` AciContainersHostMemoryRequest string `json:"aciContainersHostMemoryRequest,omitempty" yaml:"aciContainersHostMemoryRequest,omitempty"` AciContainersMemoryLimit string `json:"aciContainersMemoryLimit,omitempty" yaml:"aciContainersMemoryLimit,omitempty"` AciContainersMemoryRequest string `json:"aciContainersMemoryRequest,omitempty" yaml:"aciContainersMemoryRequest,omitempty"` - AciContainersOperatorMemoryLimit string `json:"aciContainersOperatorMemoryLimit,omitempty" yaml:"aciContainersOperatorMemoryLimit,omitempty"` - AciContainersOperatorMemoryRequest string `json:"aciContainersOperatorMemoryRequest,omitempty" yaml:"aciContainersOperatorMemoryRequest,omitempty"` AciMultipod string `json:"aciMultipod,omitempty" yaml:"aciMultipod,omitempty"` AciMultipodUbuntu string `json:"aciMultipodUbuntu,omitempty" yaml:"aciMultipodUbuntu,omitempty"` + AddExternalContractToDefaultEpg string `json:"addExternalContractToDefaultEpg,omitempty" yaml:"addExternalContractToDefaultEpg,omitempty"` AddExternalSubnetsToRdconfig string `json:"addExternalSubnetsToRdconfig,omitempty" yaml:"addExternalSubnetsToRdconfig,omitempty"` ApicHosts []string `json:"apicHosts,omitempty" yaml:"apicHosts,omitempty"` ApicRefreshTickerAdjust string `json:"apicRefreshTickerAdjust,omitempty" yaml:"apicRefreshTickerAdjust,omitempty"` @@ -140,6 +139,7 @@ type AciNetworkProvider struct { DurationWaitForNetwork string `json:"durationWaitForNetwork,omitempty" yaml:"durationWaitForNetwork,omitempty"` DynamicExternalSubnet string `json:"externDynamic,omitempty" yaml:"externDynamic,omitempty"` EnableEndpointSlice string `json:"enableEndpointSlice,omitempty" yaml:"enableEndpointSlice,omitempty"` + EnableOpflexAgentReconnect string `json:"enableOpflexAgentReconnect,omitempty" yaml:"enableOpflexAgentReconnect,omitempty"` EncapType string `json:"encapType,omitempty" yaml:"encapType,omitempty"` EpRegistry string `json:"epRegistry,omitempty" yaml:"epRegistry,omitempty"` GbpPodSubnet string `json:"gbpPodSubnet,omitempty" yaml:"gbpPodSubnet,omitempty"` @@ -166,6 +166,7 @@ type AciNetworkProvider struct { NoPriorityClass string `json:"noPriorityClass,omitempty" yaml:"noPriorityClass,omitempty"` NoWaitForServiceEpReadiness string `json:"noWaitForServiceEpReadiness,omitempty" yaml:"noWaitForServiceEpReadiness,omitempty"` NodePodIfEnable string `json:"nodePodIfEnable,omitempty" yaml:"nodePodIfEnable,omitempty"` + NodeSnatRedirectExclude []map[string]string `json:"nodeSnatRedirectExclude,omitempty" yaml:"nodeSnatRedirectExclude,omitempty"` NodeSubnet string `json:"nodeSubnet,omitempty" yaml:"nodeSubnet,omitempty"` OVSMemoryLimit string `json:"ovsMemoryLimit,omitempty" yaml:"ovsMemoryLimit,omitempty"` OVSMemoryRequest string `json:"ovsMemoryRequest,omitempty" yaml:"ovsMemoryRequest,omitempty"` @@ -175,10 +176,12 @@ type AciNetworkProvider struct { OpflexAgentOpflexAsyncjsonEnabled string `json:"opflexAgentOpflexAsyncjsonEnabled,omitempty" yaml:"opflexAgentOpflexAsyncjsonEnabled,omitempty"` OpflexAgentOvsAsyncjsonEnabled string `json:"opflexAgentOvsAsyncjsonEnabled,omitempty" yaml:"opflexAgentOvsAsyncjsonEnabled,omitempty"` OpflexAgentPolicyRetryDelayTimer string `json:"opflexAgentPolicyRetryDelayTimer,omitempty" yaml:"opflexAgentPolicyRetryDelayTimer,omitempty"` + OpflexAgentStatistics string `json:"opflexAgentStatistics,omitempty" yaml:"opflexAgentStatistics,omitempty"` OpflexClientSSL string `json:"opflexClientSsl,omitempty" yaml:"opflexClientSsl,omitempty"` OpflexDeviceDeleteTimeout string `json:"opflexDeviceDeleteTimeout,omitempty" yaml:"opflexDeviceDeleteTimeout,omitempty"` OpflexDeviceReconnectWaitTimeout string `json:"opflexDeviceReconnectWaitTimeout,omitempty" yaml:"opflexDeviceReconnectWaitTimeout,omitempty"` OpflexMode string `json:"opflexMode,omitempty" yaml:"opflexMode,omitempty"` + OpflexOpensslCompat string `json:"opflexOpensslCompat,omitempty" yaml:"opflexOpensslCompat,omitempty"` OpflexServerPort string `json:"opflexServerPort,omitempty" yaml:"opflexServerPort,omitempty"` OverlayVRFName string `json:"overlayVrfName,omitempty" yaml:"overlayVrfName,omitempty"` PBRTrackingNonSnat string `json:"pbrTrackingNonSnat,omitempty" yaml:"pbrTrackingNonSnat,omitempty"` @@ -202,6 +205,7 @@ type AciNetworkProvider struct { SystemIdentifier string `json:"systemId,omitempty" yaml:"systemId,omitempty"` Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` Token string `json:"token,omitempty" yaml:"token,omitempty"` + TolerationSeconds string `json:"tolerationSeconds,omitempty" yaml:"tolerationSeconds,omitempty"` UseAciAnywhereCRD string `json:"useAciAnywhereCrd,omitempty" yaml:"useAciAnywhereCrd,omitempty"` UseAciCniPriorityClass string `json:"useAciCniPriorityClass,omitempty" yaml:"useAciCniPriorityClass,omitempty"` UseClusterRole string `json:"useClusterRole,omitempty" yaml:"useClusterRole,omitempty"` diff --git a/pkg/client/go.mod b/pkg/client/go.mod index 3bb5e526687..047ba397bf6 100644 --- a/pkg/client/go.mod +++ b/pkg/client/go.mod @@ -5,7 +5,7 @@ go 1.20 replace k8s.io/client-go => github.com/rancher/client-go v1.27.4-rancher1 require ( - github.com/rancher/norman v0.0.0-20230831160711-5de27f66385d + github.com/rancher/norman v0.0.0-20240205154641-a6a6cf569608 k8s.io/apimachinery v0.27.4 ) diff --git a/pkg/client/go.sum b/pkg/client/go.sum index c4e6eb78e50..b178125fc0b 100644 --- a/pkg/client/go.sum +++ b/pkg/client/go.sum @@ -18,8 +18,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rancher/norman v0.0.0-20230831160711-5de27f66385d h1:Ft/iTH91TlE2oBGmpkdO4I8o8cvUmCnytdwu52a/tN4= -github.com/rancher/norman v0.0.0-20230831160711-5de27f66385d/go.mod h1:Sm2Xqai+aecgmJ86ygyEe+TdPMLkauEpykSstBAu4Ko= +github.com/rancher/norman v0.0.0-20240205154641-a6a6cf569608 h1:azL/n2grvuyGqmDvnpgRoH6mmpgodiGwjv1uZwiO7HE= +github.com/rancher/norman v0.0.0-20240205154641-a6a6cf569608/go.mod h1:Sm2Xqai+aecgmJ86ygyEe+TdPMLkauEpykSstBAu4Ko= github.com/rancher/wrangler v1.1.1-0.20230831050635-df1bd5aae9df h1:WJ+aaUICHPX8HeLmHE9JL/RFHhilMfcJlqmhgpc7gJU= github.com/rancher/wrangler v1.1.1-0.20230831050635-df1bd5aae9df/go.mod h1:4T80p+rLh2OLbjCjdExIjRHKNBgK9NUAd7eIU/gRPKk= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= diff --git a/pkg/controllers/managementuser/rbac/handler_base_test.go b/pkg/controllers/managementuser/rbac/handler_base_test.go index 5ccbebdd328..c280afa7f02 100644 --- a/pkg/controllers/managementuser/rbac/handler_base_test.go +++ b/pkg/controllers/managementuser/rbac/handler_base_test.go @@ -4,32 +4,180 @@ import ( "fmt" "testing" + apimgmtv3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3" v3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3" - fakes "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3/fakes" + "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3/fakes" + fakes2 "github.com/rancher/rancher/pkg/generated/norman/rbac.authorization.k8s.io/v1/fakes" "github.com/stretchr/testify/assert" + v1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime/schema" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" ) -var roles = map[string]*v3.RoleTemplate{ - "recursive1": { - RoleTemplateNames: []string{"recursive2"}, - }, - "recursive2": { - RoleTemplateNames: []string{"recursive1"}, - }, - "non-recursive": {}, - "inherit non-recursive": { - RoleTemplateNames: []string{"non-recursive"}, - }, +var ( + recursiveTestRoleTemplates = map[string]*v3.RoleTemplate{ + "recursive1": { + RoleTemplateNames: []string{"recursive2"}, + }, + "recursive2": { + RoleTemplateNames: []string{"recursive1"}, + }, + "non-recursive": {}, + "inherit non-recursive": { + RoleTemplateNames: []string{"non-recursive"}, + }, + } + createNSRoleTemplate = &v3.RoleTemplate{ + ObjectMeta: metav1.ObjectMeta{ + Name: "create-ns", + }, + Builtin: true, + Rules: []v1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"namespaces"}, + Verbs: []string{"create"}, + }, + }, + } +) + +type clientErrs struct { + getError error + updateError error + createError error } -func Test_gatherRoles(t *testing.T) { - manager := &manager{ +func setupManager(roleTemplates map[string]*v3.RoleTemplate, clusterRoles map[string]*v1.ClusterRole, roles map[string]*v1.Role, projects map[string]*v3.Project, crErrs, rtErrs, rErrs clientErrs) *manager { + return &manager{ rtLister: &fakes.RoleTemplateListerMock{ - GetFunc: roleListerGetFunc, + GetFunc: func(namespace string, name string) (*v3.RoleTemplate, error) { + if rtErrs.getError != nil { + return nil, rtErrs.getError + } + rt, ok := roleTemplates[name] + if !ok { + return nil, errors.NewNotFound(v3.RoleTemplateGroupVersionResource.GroupResource(), name) + } + return rt.DeepCopy(), nil + }, + ListFunc: func(namespace string, selector labels.Selector) ([]*v3.RoleTemplate, error) { + rts := make([]*v3.RoleTemplate, len(roleTemplates)) + for i := range roleTemplates { + rts = append(rts, roleTemplates[i]) + } + return rts, nil + }, }, + crLister: &fakes2.ClusterRoleListerMock{ + GetFunc: func(namespace string, name string) (*v1.ClusterRole, error) { + if crErrs.getError != nil { + return nil, crErrs.getError + } + cr, ok := clusterRoles[name] + if !ok { + return nil, errors.NewNotFound(v3.RoleTemplateGroupVersionResource.GroupResource(), name) + } + return cr.DeepCopy(), nil + }, + ListFunc: func(namespace string, selector labels.Selector) ([]*v1.ClusterRole, error) { + crs := make([]*v1.ClusterRole, len(roleTemplates)) + for i := range clusterRoles { + crs = append(crs, clusterRoles[i]) + } + return crs, nil + }, + }, + clusterRoles: &fakes2.ClusterRoleInterfaceMock{ + GetFunc: func(name string, opts metav1.GetOptions) (*v1.ClusterRole, error) { + if crErrs.getError != nil { + return nil, crErrs.getError + } + cr, ok := clusterRoles[name] + if !ok { + return nil, errors.NewNotFound(v3.RoleTemplateGroupVersionResource.GroupResource(), name) + } + return cr.DeepCopy(), nil + }, + UpdateFunc: func(cr *v1.ClusterRole) (*v1.ClusterRole, error) { + if crErrs.updateError != nil { + return nil, crErrs.updateError + } + _, ok := clusterRoles[cr.Name] + if !ok { + return nil, errors.NewNotFound(v3.RoleTemplateGroupVersionResource.GroupResource(), cr.Name) + } + clusterRoles[cr.Name] = cr + return clusterRoles[cr.Name].DeepCopy(), nil + }, + CreateFunc: func(cr *v1.ClusterRole) (*v1.ClusterRole, error) { + if crErrs.createError != nil { + return nil, crErrs.createError + } + _, ok := clusterRoles[cr.Name] + if ok { + return nil, errors.NewAlreadyExists(v3.RoleTemplateGroupVersionResource.GroupResource(), cr.Name) + } + clusterRoles[cr.Name] = cr + return clusterRoles[cr.Name].DeepCopy(), nil + }, + }, + rLister: &fakes2.RoleListerMock{ + GetFunc: func(namespace string, name string) (*v1.Role, error) { + if rErrs.getError != nil { + return nil, rErrs.getError + } + key := fmt.Sprintf("%s:%s", namespace, name) + r, ok := roles[key] + if !ok { + return nil, errors.NewNotFound(v3.RoleTemplateGroupVersionResource.GroupResource(), name) + } + return r.DeepCopy(), nil + }, + ListFunc: func(namespace string, selector labels.Selector) ([]*v1.Role, error) { + rs := make([]*v1.Role, len(roles)) + for i := range roles { + rs = append(rs, roles[i]) + } + return rs, nil + }, + }, + roles: &fakes2.RoleInterfaceMock{ + UpdateFunc: func(r *v1.Role) (*v1.Role, error) { + key := fmt.Sprintf("%s:%s", r.Namespace, r.Name) + _, ok := roles[key] + if ok { + return nil, errors.NewAlreadyExists(v3.RoleTemplateGroupVersionResource.GroupResource(), key) + } + roles[r.Name] = r + return roles[r.Name].DeepCopy(), nil + }, + GetNamespacedFunc: func(namespace string, name string, opts metav1.GetOptions) (*v1.Role, error) { + key := fmt.Sprintf("%s:%s", namespace, name) + r, ok := roles[key] + if !ok { + return nil, errors.NewNotFound(v3.RoleTemplateGroupVersionResource.GroupResource(), name) + } + return r.DeepCopy(), nil + }, + }, + projectLister: &fakes.ProjectListerMock{ + ListFunc: func(namespace string, selector labels.Selector) ([]*apimgmtv3.Project, error) { + rs := make([]*v3.Project, len(projects)) + for i := range projects { + rs = append(rs, projects[i]) + } + return rs, nil + }, + }, + clusterName: "testcluster", } +} + +func Test_gatherRoles(t *testing.T) { + m := setupManager(recursiveTestRoleTemplates, make(map[string]*v1.ClusterRole), make(map[string]*v1.Role), make(map[string]*v3.Project), clientErrs{}, clientErrs{}, clientErrs{}) + emptyRoleTemplates := make(map[string]*v3.RoleTemplate) type args struct { rt *v3.RoleTemplate @@ -44,7 +192,7 @@ func Test_gatherRoles(t *testing.T) { { name: "Non-recursive role, none inherited", args: args{ - rt: roles["non-recursive"], + rt: recursiveTestRoleTemplates["non-recursive"], roleTemplates: emptyRoleTemplates, depthCounter: 0, }, @@ -53,7 +201,7 @@ func Test_gatherRoles(t *testing.T) { { name: "Non-recursive role, inherits another", args: args{ - rt: roles["inherit non-recursive"], + rt: recursiveTestRoleTemplates["inherit non-recursive"], roleTemplates: emptyRoleTemplates, depthCounter: 0, }, @@ -62,7 +210,7 @@ func Test_gatherRoles(t *testing.T) { { name: "Recursive role", args: args{ - rt: roles["recursive1"], + rt: recursiveTestRoleTemplates["recursive1"], roleTemplates: emptyRoleTemplates, depthCounter: 0, }, @@ -72,7 +220,7 @@ func Test_gatherRoles(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := manager.gatherRoles(tt.args.rt, tt.args.roleTemplates, tt.args.depthCounter) + err := m.gatherRoles(tt.args.rt, tt.args.roleTemplates, tt.args.depthCounter) if tt.wantErr { assert.Error(t, err, "expected an error, received none") } else { @@ -81,14 +229,3 @@ func Test_gatherRoles(t *testing.T) { }) } } - -func roleListerGetFunc(ns, name string) (*v3.RoleTemplate, error) { - role, ok := roles[name] - if !ok { - return nil, errors.NewNotFound(schema.GroupResource{ - Group: v3.RoleTemplateGroupVersionKind.Group, - Resource: v3.RoleTemplateGroupVersionResource.Resource, - }, name) - } - return role, nil -} diff --git a/pkg/controllers/managementuser/rbac/namespace_handler.go b/pkg/controllers/managementuser/rbac/namespace_handler.go index a03fe9718cb..fc15fe4e683 100644 --- a/pkg/controllers/managementuser/rbac/namespace_handler.go +++ b/pkg/controllers/managementuser/rbac/namespace_handler.go @@ -334,7 +334,7 @@ func (n *nsLifecycle) reconcileNamespaceProjectClusterRole(ns *v1.Namespace) err return err } - roleCli := n.m.workload.RBAC.ClusterRoles("") + roleCli := n.m.clusterRoles nsInDesiredRole := false for _, c := range clusterRoles { cr, ok := c.(*rbacv1.ClusterRole) @@ -442,7 +442,7 @@ func (n *nsLifecycle) reconcileNamespaceProjectClusterRole(ns *v1.Namespace) err } func (m *manager) createProjectNSRole(roleName, verb, ns, projectName string) error { - roleCli := m.workload.RBAC.ClusterRoles("") + roleCli := m.clusterRoles cr := &rbacv1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/controllers/managementuser/rbac/namespace_handler_test.go b/pkg/controllers/managementuser/rbac/namespace_handler_test.go index 4dd2d3fc358..d937debb30f 100644 --- a/pkg/controllers/managementuser/rbac/namespace_handler_test.go +++ b/pkg/controllers/managementuser/rbac/namespace_handler_test.go @@ -6,11 +6,13 @@ import ( "github.com/rancher/rancher/pkg/apis/management.cattle.io" apisV3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3" + v3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3" "github.com/rancher/rancher/pkg/generated/norman/rbac.authorization.k8s.io/v1/fakes" - "github.com/rancher/rancher/pkg/types/config" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" + v1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/errors" apierror "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -177,31 +179,6 @@ func TestReconcileNamespaceProjectClusterRole(t *testing.T) { }, err: test.indexerError, } - fakeRBACInterface := &fakeRBAC{ - clusterRoleFake: fakes.ClusterRoleInterfaceMock{ - CreateFunc: func(in *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) { - newRoles = append(newRoles, in) - if test.createError != nil { - return nil, test.createError - } - return in, nil - }, - UpdateFunc: func(in *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) { - newRoles = append(newRoles, in) - if test.updateError != nil { - return nil, test.updateError - } - return in, nil - }, - DeleteFunc: func(name string, options *metav1.DeleteOptions) error { - deletedRoleNames = append(deletedRoleNames, name) - if test.deleteError != nil { - return test.deleteError - } - return nil - }, - }, - } fakeLister := &fakes.ClusterRoleListerMock{ GetFunc: func(namespace string, name string) (*rbacv1.ClusterRole, error) { if test.getError != nil { @@ -218,13 +195,34 @@ func TestReconcileNamespaceProjectClusterRole(t *testing.T) { }, name) }, } + fakeClusterRoles := &fakes.ClusterRoleInterfaceMock{ + CreateFunc: func(in *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) { + newRoles = append(newRoles, in) + if test.createError != nil { + return nil, test.createError + } + return in, nil + }, + UpdateFunc: func(in *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) { + newRoles = append(newRoles, in) + if test.updateError != nil { + return nil, test.updateError + } + return in, nil + }, + DeleteFunc: func(name string, options *metav1.DeleteOptions) error { + deletedRoleNames = append(deletedRoleNames, name) + if test.deleteError != nil { + return test.deleteError + } + return nil + }, + } lifecycle := nsLifecycle{ m: &manager{ - workload: &config.UserContext{ - RBAC: fakeRBACInterface, - }, - crLister: fakeLister, - crIndexer: &indexer, + crLister: fakeLister, + crIndexer: &indexer, + clusterRoles: fakeClusterRoles, }, } err := lifecycle.reconcileNamespaceProjectClusterRole(&corev1.Namespace{ @@ -253,6 +251,115 @@ func TestReconcileNamespaceProjectClusterRole(t *testing.T) { } +func TestCreateProjectNSRole(t *testing.T) { + t.Parallel() + crs := make(map[string]*v1.ClusterRole) + m := setupManager(make(map[string]*v3.RoleTemplate), crs, make(map[string]*v1.Role), make(map[string]*v3.Project), clientErrs{}, clientErrs{}, clientErrs{}) + type testCase struct { + description string + verb string + namespace string + projectName string + startingCR *v1.ClusterRole + expectedCR *v1.ClusterRole + isErrExpected bool + expectedErr string + } + testCases := []testCase{ + { + description: "create get role", + verb: "get", + projectName: "p-123xyz", + expectedCR: &v1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "p-123xyz-namespaces-readonly", + Annotations: map[string]string{ + projectNSAnn: "p-123xyz-namespaces-readonly", + }, + }, + }, + }, + { + description: "create edit role", + verb: "*", + projectName: "p-123xyz", + expectedCR: &v1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "p-123xyz-namespaces-edit", + Annotations: map[string]string{ + projectNSAnn: "p-123xyz-namespaces-edit", + }, + }, + Rules: []v1.PolicyRule{ + { + APIGroups: []string{"management.cattle.io"}, + Verbs: []string{"manage-namespaces"}, + Resources: []string{"projects"}, + ResourceNames: []string{"p-123xyz"}, + }, + }, + }, + }, + { + description: "do not change role if already exists and return AlreadyExists error", + verb: "*", + projectName: "p-123xyz", + expectedCR: &v1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "p-123xyz-namespaces-edit", + Annotations: map[string]string{ + projectNSAnn: "p-123xyz-namespaces-edit", + }, + }, + Rules: []v1.PolicyRule{ + { + APIGroups: []string{"management.cattle.io"}, + Verbs: []string{"manage-namespaces"}, + Resources: []string{"projects"}, + ResourceNames: []string{"p-123xyz"}, + }, + }, + }, + startingCR: &v1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "p-123xyz-namespaces-edit", + Annotations: map[string]string{ + projectNSAnn: "p-123xyz-namespaces-edit", + }, + }, + Rules: []v1.PolicyRule{ + { + APIGroups: []string{"management.cattle.io"}, + Verbs: []string{"manage-namespaces"}, + Resources: []string{"projects"}, + ResourceNames: []string{"p-123xyz"}, + }, + }, + }, + isErrExpected: true, + expectedErr: "roletemplates.management.cattle.io \"p-123xyz-namespaces-edit\" already exists", + }, + } + for _, test := range testCases { + if test.startingCR != nil { + crs[test.startingCR.Name] = test.startingCR + } + err := m.createProjectNSRole(fmt.Sprintf(projectNSGetClusterRoleNameFmt, test.projectName, projectNSVerbToSuffix[test.verb]), test.verb, test.namespace, test.projectName) + if test.isErrExpected { + assert.NotNil(t, err, test.description) + } else { + assert.Nil(t, err) + } + assert.Equal(t, test.expectedCR, crs[test.expectedCR.Name], test.description) + delete(crs, test.expectedCR.Name) + } + m = setupManager(make(map[string]*v3.RoleTemplate), crs, make(map[string]*v1.Role), make(map[string]*v3.Project), clientErrs{createError: errors.NewInternalError(fmt.Errorf("some error"))}, clientErrs{}, clientErrs{}) + description := "test should return non-AlreadyExists error" + err := m.createProjectNSRole(fmt.Sprintf(projectNSGetClusterRoleNameFmt, "p-123xyz", "edit"), "*", "", "p-123xyz") + assert.NotNil(t, err, description) + assert.Equal(t, "Internal error occurred: some error", err.Error(), description) +} + func createClusterRoleForProject(projectName string, namespace string, verb string) *rbacv1.ClusterRole { cr := createBaseClusterRoleForProject(projectName, verb) return addNamespaceToClusterRole(namespace, verb, cr) diff --git a/pkg/controllers/managementuser/rbac/project_handler_test.go b/pkg/controllers/managementuser/rbac/project_handler_test.go index 504ee8593e1..b0d1a0e767b 100644 --- a/pkg/controllers/managementuser/rbac/project_handler_test.go +++ b/pkg/controllers/managementuser/rbac/project_handler_test.go @@ -7,7 +7,6 @@ import ( v3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3" v1 "github.com/rancher/rancher/pkg/generated/norman/rbac.authorization.k8s.io/v1" "github.com/rancher/rancher/pkg/generated/norman/rbac.authorization.k8s.io/v1/fakes" - "github.com/rancher/rancher/pkg/types/config" "github.com/stretchr/testify/assert" rbacv1 "k8s.io/api/rbac/v1" apierror "k8s.io/apimachinery/pkg/api/errors" @@ -103,17 +102,13 @@ func TestCreate(t *testing.T) { }, name) }, }, - workload: &config.UserContext{ - RBAC: &fakeRBAC{ - clusterRoleFake: fakes.ClusterRoleInterfaceMock{ - CreateFunc: func(in *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) { - newCRs = append(newCRs, in) - if test.createErr != nil { - return nil, test.createErr - } - return in, nil - }, - }, + clusterRoles: &fakes.ClusterRoleInterfaceMock{ + CreateFunc: func(in *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) { + newCRs = append(newCRs, in) + if test.createErr != nil { + return nil, test.createErr + } + return in, nil }, }, }, @@ -288,6 +283,13 @@ func TestUpdated(t *testing.T) { }, }, clusterRoles: &fakes.ClusterRoleInterfaceMock{ + CreateFunc: func(in *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) { + newCRs = append(newCRs, in) + if test.createError != nil { + return nil, test.createError + } + return in, nil + }, UpdateFunc: func(in *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) { newCRs = append(newCRs, in) if test.updError != nil { @@ -296,19 +298,6 @@ func TestUpdated(t *testing.T) { return in, nil }, }, - workload: &config.UserContext{ - RBAC: &fakeRBAC{ - clusterRoleFake: fakes.ClusterRoleInterfaceMock{ - CreateFunc: func(in *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) { - newCRs = append(newCRs, in) - if test.createError != nil { - return nil, test.createError - } - return in, nil - }, - }, - }, - }, }, } _, err := lifecycle.Updated(project) diff --git a/pkg/controllers/managementuser/rbac/prtb_handler.go b/pkg/controllers/managementuser/rbac/prtb_handler.go index 5f2a66b8a56..4f37c569a86 100644 --- a/pkg/controllers/managementuser/rbac/prtb_handler.go +++ b/pkg/controllers/managementuser/rbac/prtb_handler.go @@ -3,6 +3,7 @@ package rbac import ( "reflect" "sort" + "strings" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" @@ -161,7 +162,11 @@ func (p *prtbLifecycle) ensurePRTBDelete(binding *v3.ProjectRoleTemplateBinding) } func (p *prtbLifecycle) reconcileProjectAccessToGlobalResources(binding *v3.ProjectRoleTemplateBinding, rts map[string]*v3.RoleTemplate) error { - _, err := p.m.reconcileProjectAccessToGlobalResources(binding, rts) + roles, err := p.m.ensureGlobalResourcesRolesForPRTB(parseProjectName(binding.ProjectName), rts) + if err != nil { + return err + } + _, err = p.m.reconcileProjectAccessToGlobalResources(binding, roles) if err != nil { return err } @@ -277,7 +282,7 @@ func (m *manager) checkForGlobalResourceRules(role *v3.RoleTemplate, resource st // Ensure the clusterRole used to grant access of global resources to users/groups in projects has appropriate rules for the given resource and verbs func (m *manager) reconcileRoleForProjectAccessToGlobalResource(resource string, rt *v3.RoleTemplate, newVerbs map[string]bool, baseRule rbacv1.PolicyRule) (string, error) { - clusterRoles := m.workload.RBAC.ClusterRoles("") + clusterRoles := m.clusterRoles roleName := rt.Name + "-promoted" if role, err := m.crLister.Get("", roleName); err == nil && role != nil { currentVerbs := map[string]bool{} @@ -465,3 +470,11 @@ func (p *prtbLifecycle) reconcilePRTBUserClusterLabels(binding *v3.ProjectRoleTe }) return retryErr } + +func parseProjectName(id string) string { + parts := strings.SplitN(id, ":", 2) + if len(parts) != 2 || len(parts[1]) == 0 { + return "" + } + return parts[1] +} diff --git a/pkg/controllers/managementuser/rbac/reconcile_roletemplate.go b/pkg/controllers/managementuser/rbac/reconcile_roletemplate.go index 0ac69836e85..1b8650768b2 100644 --- a/pkg/controllers/managementuser/rbac/reconcile_roletemplate.go +++ b/pkg/controllers/managementuser/rbac/reconcile_roletemplate.go @@ -2,7 +2,6 @@ package rbac import ( "fmt" - "strings" "github.com/rancher/norman/types/slice" v3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3" @@ -13,68 +12,13 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func (m *manager) reconcileProjectAccessToGlobalResources(binding *v3.ProjectRoleTemplateBinding, rts map[string]*v3.RoleTemplate) (map[string]bool, error) { - var role string - var createNSPerms bool - var roles []string - if parts := strings.SplitN(binding.ProjectName, ":", 2); len(parts) == 2 && len(parts[1]) > 0 { - projectName := parts[1] - var roleVerb, roleSuffix string - for _, r := range rts { - for _, rule := range r.Rules { - if slice.ContainsString(rule.Resources, "namespaces") && len(rule.ResourceNames) == 0 { - if slice.ContainsString(rule.Verbs, "*") || slice.ContainsString(rule.Verbs, "create") { - roleVerb = "*" - createNSPerms = true - break - } - } - - } - } - if roleVerb == "" { - roleVerb = "get" - } - roleSuffix = projectNSVerbToSuffix[roleVerb] - role = fmt.Sprintf(projectNSGetClusterRoleNameFmt, projectName, roleSuffix) - roles = append(roles, role) - - for _, rt := range rts { - for resource, baseRule := range globalResourceRulesNeededInProjects { - verbs, err := m.checkForGlobalResourceRules(rt, resource, baseRule) - if err != nil { - return nil, err - } - if len(verbs) > 0 { - roleName, err := m.reconcileRoleForProjectAccessToGlobalResource(resource, rt, verbs, baseRule) - if err != nil { - return nil, err - } - roles = append(roles, roleName) - } - } - } - } - +func (m *manager) reconcileProjectAccessToGlobalResources(binding *v3.ProjectRoleTemplateBinding, roles []string) (map[string]bool, error) { if len(roles) == 0 { return nil, nil } bindingCli := m.workload.RBAC.ClusterRoleBindings("") - if createNSPerms { - roles = append(roles, "create-ns") - if nsRole, _ := m.crLister.Get("", "create-ns"); nsRole == nil { - createNSRT, err := m.rtLister.Get("", "create-ns") - if err != nil { - return nil, err - } - if err := m.ensureRoles(map[string]*v3.RoleTemplate{"create-ns": createNSRT}); err != nil && !apierrors.IsAlreadyExists(err) { - return nil, err - } - } - } - rtbUID := pkgrbac.GetRTBLabel(binding.ObjectMeta) subject, err := pkgrbac.BuildSubjectFromRTB(binding) if err != nil { @@ -147,3 +91,62 @@ func (m *manager) reconcileProjectAccessToGlobalResources(binding *v3.ProjectRol return crbsToKeep, nil } + +// EnsureGlobalResourcesRolesForPRTB ensures that all necessary roles exist and contain the rules needed to +// enforce permissions described by RoleTemplate rules. A slice of strings indicating role names is returned. +func (m *manager) ensureGlobalResourcesRolesForPRTB(projectName string, rts map[string]*v3.RoleTemplate) ([]string, error) { + var role string + var roles []string + + if projectName == "" { + return nil, nil + } + + var roleVerb, roleSuffix string + for _, r := range rts { + for _, rule := range r.Rules { + hasNamespaceResources := slice.ContainsString(rule.Resources, "namespaces") || slice.ContainsString(rule.Resources, "*") + hasNamespaceGroup := slice.ContainsString(rule.APIGroups, "") || slice.ContainsString(rule.APIGroups, "*") + if hasNamespaceGroup && hasNamespaceResources && len(rule.ResourceNames) == 0 { + if slice.ContainsString(rule.Verbs, "*") || slice.ContainsString(rule.Verbs, "create") { + roleVerb = "*" + roles = append(roles, "create-ns") + if nsRole, _ := m.crLister.Get("", "create-ns"); nsRole == nil { + createNSRT, err := m.rtLister.Get("", "create-ns") + if err != nil { + return nil, err + } + if err := m.ensureRoles(map[string]*v3.RoleTemplate{"create-ns": createNSRT}); err != nil && !apierrors.IsAlreadyExists(err) { + return nil, err + } + } + break + } + } + + } + } + if roleVerb == "" { + roleVerb = "get" + } + roleSuffix = projectNSVerbToSuffix[roleVerb] + role = fmt.Sprintf(projectNSGetClusterRoleNameFmt, projectName, roleSuffix) + roles = append(roles, role) + + for _, rt := range rts { + for resource, baseRule := range globalResourceRulesNeededInProjects { + verbs, err := m.checkForGlobalResourceRules(rt, resource, baseRule) + if err != nil { + return nil, err + } + if len(verbs) > 0 { + roleName, err := m.reconcileRoleForProjectAccessToGlobalResource(resource, rt, verbs, baseRule) + if err != nil { + return nil, err + } + roles = append(roles, roleName) + } + } + } + return roles, nil +} diff --git a/pkg/controllers/managementuser/rbac/reconcile_roletemplate_test.go b/pkg/controllers/managementuser/rbac/reconcile_roletemplate_test.go new file mode 100644 index 00000000000..bfef67441cc --- /dev/null +++ b/pkg/controllers/managementuser/rbac/reconcile_roletemplate_test.go @@ -0,0 +1,255 @@ +package rbac + +import ( + "testing" + + "github.com/pkg/errors" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + + v3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3" + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestEnsureGlobalResourcesRolesForPRTB(t *testing.T) { + t.Parallel() + m := setupManager(map[string]*v3.RoleTemplate{"create-ns": createNSRoleTemplate}, make(map[string]*v1.ClusterRole), make(map[string]*v1.Role), make(map[string]*v3.Project), clientErrs{}, clientErrs{}, clientErrs{}) + type testCase struct { + description string + projectName string + roleTemplates map[string]*v3.RoleTemplate + expectedRoles []string + isErrExpected bool + } + testCases := []testCase{ + { + description: "global resource rule should grant namespace read", + projectName: "testproject", + expectedRoles: []string{"testproject-namespaces-readonly"}, + roleTemplates: map[string]*v3.RoleTemplate{ + "testrt1": { + ObjectMeta: metav1.ObjectMeta{ + Name: "testrt1", + }, + Rules: []v1.PolicyRule{ + { + Verbs: []string{"*"}, + APIGroups: []string{""}, + Resources: []string{"configmaps"}, + }, + }, + }, + }, + }, + { + description: "namespace create rule should grant create-ns and a namespaces-edit role", + projectName: "testproject", + expectedRoles: []string{"create-ns", "testproject-namespaces-edit"}, + roleTemplates: map[string]*v3.RoleTemplate{ + "testrt2": { + ObjectMeta: metav1.ObjectMeta{ + Name: "testrt2", + }, + Rules: []v1.PolicyRule{ + { + Verbs: []string{"create"}, + APIGroups: []string{""}, + Resources: []string{"namespaces"}, + }, + }, + }, + }, + }, + { + description: "namespace create rule for other API group should grant namespaces-read role only", + projectName: "testproject", + expectedRoles: []string{"testproject-namespaces-readonly"}, + roleTemplates: map[string]*v3.RoleTemplate{ + "testrt3": { + ObjectMeta: metav1.ObjectMeta{ + Name: "testrt3", + }, + Rules: []v1.PolicyRule{ + { + Verbs: []string{"create"}, + APIGroups: []string{"some.other.apigroup"}, + Resources: []string{"namespaces"}, + }, + }, + }, + }, + }, + { + description: "namespace * rule for other API group should grant namespaces-read role only", + projectName: "testproject", + expectedRoles: []string{"testproject-namespaces-readonly"}, + roleTemplates: map[string]*v3.RoleTemplate{ + "testrt4": { + ObjectMeta: metav1.ObjectMeta{ + Name: "testrt4", + }, + Rules: []v1.PolicyRule{ + { + Verbs: []string{"*"}, + APIGroups: []string{"some.other.apigroup"}, + Resources: []string{"namespaces"}, + }, + }, + }, + }, + }, + { + description: "global resource rule result in promoted role returned", + projectName: "testproject", + expectedRoles: []string{"testproject-namespaces-readonly", "testrt5-promoted"}, + roleTemplates: map[string]*v3.RoleTemplate{ + "testrt5": { + ObjectMeta: metav1.ObjectMeta{ + Name: "testrt5", + }, + Rules: []v1.PolicyRule{ + { + Verbs: []string{"*"}, + APIGroups: []string{"catalog.cattle.io"}, + Resources: []string{"clusterrepos"}, + }, + }, + }, + }, + }, + { + description: "empty project name will result in no roles returned", + projectName: "", + expectedRoles: nil, + roleTemplates: map[string]*v3.RoleTemplate{ + "testrt6": { + ObjectMeta: metav1.ObjectMeta{ + Name: "testrt6", + }, + Rules: []v1.PolicyRule{ + { + Verbs: []string{"*"}, + APIGroups: []string{"catalog.cattle.io"}, + Resources: []string{"clusterrepos"}, + }, + }, + }, + }, + }, + { + description: "* resources and non-core APIGroup should only result in namespace-readonly role", + projectName: "testproject", + expectedRoles: []string{"testproject-namespaces-readonly"}, + roleTemplates: map[string]*v3.RoleTemplate{ + "testrt7": { + ObjectMeta: metav1.ObjectMeta{ + Name: "testrt7", + }, + Rules: []v1.PolicyRule{ + { + Verbs: []string{"*"}, + APIGroups: []string{"some.other.apigroup"}, + Resources: []string{"*"}, + }, + }, + }, + }, + }, + { + description: "* resources and * APIGroup should only result in namespace-readonly and promoted role", + projectName: "testproject", + // at the time of adding these tests ensureGlobalResourceRoleForPRTB returns duplicate promoted roles + // names per applicable rule found in globalResourceRulesNeededInProjects. This is not incompatible with + // current reconcile logic but should be fixed in the future. + expectedRoles: []string{"create-ns", "testproject-namespaces-edit", "testrt8-promoted", "testrt8-promoted", "testrt8-promoted", "testrt8-promoted", "testrt8-promoted", "testrt8-promoted"}, + roleTemplates: map[string]*v3.RoleTemplate{ + "testrt8": { + ObjectMeta: metav1.ObjectMeta{ + Name: "testrt8", + }, + Rules: []v1.PolicyRule{ + { + Verbs: []string{"*"}, + APIGroups: []string{"*"}, + Resources: []string{"*"}, + }, + }, + }, + }, + }, + { + description: "* resources and core (\"\") APIGroup should only result in namespace-readonly and promoted role", + projectName: "testproject", + expectedRoles: []string{"create-ns", "testproject-namespaces-edit", "testrt9-promoted", "testrt9-promoted"}, + roleTemplates: map[string]*v3.RoleTemplate{ + "testrt9": { + ObjectMeta: metav1.ObjectMeta{ + Name: "testrt9", + }, + Rules: []v1.PolicyRule{ + { + Verbs: []string{"*"}, + APIGroups: []string{""}, + Resources: []string{"*"}, + }, + }, + }, + }, + }, + } + for _, test := range testCases { + test := test + t.Run(test.description, func(t *testing.T) { + t.Parallel() + roles, err := m.ensureGlobalResourcesRolesForPRTB(test.projectName, test.roleTemplates) + assert.Nil(t, err) + assert.Equal(t, test.expectedRoles, roles, test.description) + }) + } + + test := testCase{ + projectName: "testproject", + expectedRoles: []string{"create-ns", "testproject-namespaces-edit"}, + roleTemplates: map[string]*v3.RoleTemplate{ + "testrt": { + ObjectMeta: metav1.ObjectMeta{ + Name: "testrt", + }, + Rules: []v1.PolicyRule{ + { + Verbs: []string{"create"}, + APIGroups: []string{""}, + Resources: []string{"namespaces"}, + }, + }, + }, + }, + } + m = setupManager(map[string]*v3.RoleTemplate{"create-ns": createNSRoleTemplate}, make(map[string]*v1.ClusterRole), make(map[string]*v1.Role), make(map[string]*v3.Project), clientErrs{}, clientErrs{getError: errNotFound}, clientErrs{}) + test1 := test + test1.description = "error return when RoleTemplate client returns error" + t.Run(test.description, func(t *testing.T) { + t.Parallel() + _, err := m.ensureGlobalResourcesRolesForPRTB(test.projectName, test.roleTemplates) + assert.NotNil(t, err) + }) + m = setupManager(map[string]*v3.RoleTemplate{"create-ns": createNSRoleTemplate}, make(map[string]*v1.ClusterRole), make(map[string]*v1.Role), make(map[string]*v3.Project), clientErrs{}, clientErrs{}, clientErrs{createError: errAlreadyExist}) + test2 := test + test2.description = "error return when Role client returns error" + t.Run(test.description, func(t *testing.T) { + t.Parallel() + _, err := m.ensureGlobalResourcesRolesForPRTB(test.projectName, test.roleTemplates) + assert.NotNil(t, err) + }) + m = setupManager(map[string]*v3.RoleTemplate{"create-ns": createNSRoleTemplate}, make(map[string]*v1.ClusterRole), make(map[string]*v1.Role), make(map[string]*v3.Project), clientErrs{getError: apierrors.NewInternalError(errors.New("error"))}, clientErrs{}, clientErrs{}) + test3 := test + test3.description = "error return when ClusterRole client returns error and RoleTemplate is external" + test3.roleTemplates["testrt"].External = true + t.Run(test.description, func(t *testing.T) { + t.Parallel() + _, err := m.ensureGlobalResourcesRolesForPRTB(test.projectName, test.roleTemplates) + assert.NotNil(t, err) + }) +} diff --git a/pkg/controllers/managementuser/rbac/roletemplate_handler.go b/pkg/controllers/managementuser/rbac/roletemplate_handler.go index 92644a22e05..0b41976ff92 100644 --- a/pkg/controllers/managementuser/rbac/roletemplate_handler.go +++ b/pkg/controllers/managementuser/rbac/roletemplate_handler.go @@ -68,18 +68,18 @@ func (c *rtSync) sync(key string, obj *v3.RoleTemplate) (runtime.Object, error) } func (c *rtSync) syncRT(template *v3.RoleTemplate, usedInProjects bool, prtbs []interface{}, crtbs []interface{}) error { - roles := map[string]*v3.RoleTemplate{} - if err := c.m.gatherRoles(template, roles, 0); err != nil { + roleTemplates := map[string]*v3.RoleTemplate{} + if err := c.m.gatherRoles(template, roleTemplates, 0); err != nil { return err } - if err := c.m.ensureRoles(roles); err != nil { + if err := c.m.ensureRoles(roleTemplates); err != nil { return errors.Wrapf(err, "couldn't ensure roles") } rolesToKeep := make(map[string]bool) if usedInProjects { - for _, rt := range roles { + for _, rt := range roleTemplates { for resource, baseRule := range globalResourceRulesNeededInProjects { verbs, err := c.m.checkForGlobalResourceRules(rt, resource, baseRule) if err != nil { @@ -102,6 +102,10 @@ func (c *rtSync) syncRT(template *v3.RoleTemplate, usedInProjects bool, prtbs [] continue } + roles, err := c.m.ensureGlobalResourcesRolesForPRTB(parseProjectName(prtb.ProjectName), roleTemplates) + if err != nil { + return err + } crbsToKeep, err := c.m.reconcileProjectAccessToGlobalResources(prtb, roles) if err != nil { return err @@ -131,7 +135,7 @@ func (c *rtSync) syncRT(template *v3.RoleTemplate, usedInProjects bool, prtbs [] if !ns.DeletionTimestamp.IsZero() { continue } - if err := c.m.ensureProjectRoleBindings(ns.Name, roles, prtb); err != nil { + if err := c.m.ensureProjectRoleBindings(ns.Name, roleTemplates, prtb); err != nil { return errors.Wrapf(err, "couldn't ensure binding %v in %v", prtb.Name, ns.Name) } } @@ -142,7 +146,7 @@ func (c *rtSync) syncRT(template *v3.RoleTemplate, usedInProjects bool, prtbs [] if !ok { continue } - if err := c.m.ensureClusterBindings(roles, crtb); err != nil { + if err := c.m.ensureClusterBindings(roleTemplates, crtb); err != nil { return err } } diff --git a/pkg/settings/setting.go b/pkg/settings/setting.go index ca5540b49f4..52471df3921 100644 --- a/pkg/settings/setting.go +++ b/pkg/settings/setting.go @@ -108,7 +108,7 @@ var ( WhitelistDomain = NewSetting("whitelist-domain", "forums.rancher.com") WhitelistEnvironmentVars = NewSetting("whitelist-envvars", "HTTP_PROXY,HTTPS_PROXY,NO_PROXY") AuthUserInfoResyncCron = NewSetting("auth-user-info-resync-cron", "0 0 * * *") - APIUIVersion = NewSetting("api-ui-version", "1.1.10") // Please update the CATTLE_API_UI_VERSION in package/Dockerfile when updating the version here. + APIUIVersion = NewSetting("api-ui-version", "1.1.11") // Please update the CATTLE_API_UI_VERSION in package/Dockerfile when updating the version here. RotateCertsIfExpiringInDays = NewSetting("rotate-certs-if-expiring-in-days", "7") // 7 days ClusterTemplateEnforcement = NewSetting("cluster-template-enforcement", "false") InitialDockerRootDir = NewSetting("initial-docker-root-dir", "/var/lib/docker") diff --git a/tests/framework/clients/rancher/generated/management/v3/zz_generated_aci_network_provider.go b/tests/framework/clients/rancher/generated/management/v3/zz_generated_aci_network_provider.go index 5e626065e92..2a6360222d2 100644 --- a/tests/framework/clients/rancher/generated/management/v3/zz_generated_aci_network_provider.go +++ b/tests/framework/clients/rancher/generated/management/v3/zz_generated_aci_network_provider.go @@ -3,18 +3,15 @@ package client const ( AciNetworkProviderType = "aciNetworkProvider" AciNetworkProviderFieldAEP = "aep" - AciNetworkProviderFieldAccProvisionOperatorMemoryLimit = "accProvisionOperatorMemoryLimit" - AciNetworkProviderFieldAccProvisionOperatorMemoryRequest = "accProvisionOperatorMemoryRequest" AciNetworkProviderFieldAciContainersControllerMemoryLimit = "aciContainersControllerMemoryLimit" AciNetworkProviderFieldAciContainersControllerMemoryRequest = "aciContainersControllerMemoryRequest" AciNetworkProviderFieldAciContainersHostMemoryLimit = "aciContainersHostMemoryLimit" AciNetworkProviderFieldAciContainersHostMemoryRequest = "aciContainersHostMemoryRequest" AciNetworkProviderFieldAciContainersMemoryLimit = "aciContainersMemoryLimit" AciNetworkProviderFieldAciContainersMemoryRequest = "aciContainersMemoryRequest" - AciNetworkProviderFieldAciContainersOperatorMemoryLimit = "aciContainersOperatorMemoryLimit" - AciNetworkProviderFieldAciContainersOperatorMemoryRequest = "aciContainersOperatorMemoryRequest" AciNetworkProviderFieldAciMultipod = "aciMultipod" AciNetworkProviderFieldAciMultipodUbuntu = "aciMultipodUbuntu" + AciNetworkProviderFieldAddExternalContractToDefaultEpg = "addExternalContractToDefaultEpg" AciNetworkProviderFieldAddExternalSubnetsToRdconfig = "addExternalSubnetsToRdconfig" AciNetworkProviderFieldApicHosts = "apicHosts" AciNetworkProviderFieldApicRefreshTickerAdjust = "apicRefreshTickerAdjust" @@ -33,6 +30,7 @@ const ( AciNetworkProviderFieldDurationWaitForNetwork = "durationWaitForNetwork" AciNetworkProviderFieldDynamicExternalSubnet = "externDynamic" AciNetworkProviderFieldEnableEndpointSlice = "enableEndpointSlice" + AciNetworkProviderFieldEnableOpflexAgentReconnect = "enableOpflexAgentReconnect" AciNetworkProviderFieldEncapType = "encapType" AciNetworkProviderFieldEpRegistry = "epRegistry" AciNetworkProviderFieldGbpPodSubnet = "gbpPodSubnet" @@ -59,6 +57,7 @@ const ( AciNetworkProviderFieldNoPriorityClass = "noPriorityClass" AciNetworkProviderFieldNoWaitForServiceEpReadiness = "noWaitForServiceEpReadiness" AciNetworkProviderFieldNodePodIfEnable = "nodePodIfEnable" + AciNetworkProviderFieldNodeSnatRedirectExclude = "nodeSnatRedirectExclude" AciNetworkProviderFieldNodeSubnet = "nodeSubnet" AciNetworkProviderFieldOVSMemoryLimit = "ovsMemoryLimit" AciNetworkProviderFieldOVSMemoryRequest = "ovsMemoryRequest" @@ -68,10 +67,12 @@ const ( AciNetworkProviderFieldOpflexAgentOpflexAsyncjsonEnabled = "opflexAgentOpflexAsyncjsonEnabled" AciNetworkProviderFieldOpflexAgentOvsAsyncjsonEnabled = "opflexAgentOvsAsyncjsonEnabled" AciNetworkProviderFieldOpflexAgentPolicyRetryDelayTimer = "opflexAgentPolicyRetryDelayTimer" + AciNetworkProviderFieldOpflexAgentStatistics = "opflexAgentStatistics" AciNetworkProviderFieldOpflexClientSSL = "opflexClientSsl" AciNetworkProviderFieldOpflexDeviceDeleteTimeout = "opflexDeviceDeleteTimeout" AciNetworkProviderFieldOpflexDeviceReconnectWaitTimeout = "opflexDeviceReconnectWaitTimeout" AciNetworkProviderFieldOpflexMode = "opflexMode" + AciNetworkProviderFieldOpflexOpensslCompat = "opflexOpensslCompat" AciNetworkProviderFieldOpflexServerPort = "opflexServerPort" AciNetworkProviderFieldOverlayVRFName = "overlayVrfName" AciNetworkProviderFieldPBRTrackingNonSnat = "pbrTrackingNonSnat" @@ -95,6 +96,7 @@ const ( AciNetworkProviderFieldSystemIdentifier = "systemId" AciNetworkProviderFieldTenant = "tenant" AciNetworkProviderFieldToken = "token" + AciNetworkProviderFieldTolerationSeconds = "tolerationSeconds" AciNetworkProviderFieldUseAciAnywhereCRD = "useAciAnywhereCrd" AciNetworkProviderFieldUseAciCniPriorityClass = "useAciCniPriorityClass" AciNetworkProviderFieldUseClusterRole = "useClusterRole" @@ -110,18 +112,15 @@ const ( type AciNetworkProvider struct { AEP string `json:"aep,omitempty" yaml:"aep,omitempty"` - AccProvisionOperatorMemoryLimit string `json:"accProvisionOperatorMemoryLimit,omitempty" yaml:"accProvisionOperatorMemoryLimit,omitempty"` - AccProvisionOperatorMemoryRequest string `json:"accProvisionOperatorMemoryRequest,omitempty" yaml:"accProvisionOperatorMemoryRequest,omitempty"` AciContainersControllerMemoryLimit string `json:"aciContainersControllerMemoryLimit,omitempty" yaml:"aciContainersControllerMemoryLimit,omitempty"` AciContainersControllerMemoryRequest string `json:"aciContainersControllerMemoryRequest,omitempty" yaml:"aciContainersControllerMemoryRequest,omitempty"` AciContainersHostMemoryLimit string `json:"aciContainersHostMemoryLimit,omitempty" yaml:"aciContainersHostMemoryLimit,omitempty"` AciContainersHostMemoryRequest string `json:"aciContainersHostMemoryRequest,omitempty" yaml:"aciContainersHostMemoryRequest,omitempty"` AciContainersMemoryLimit string `json:"aciContainersMemoryLimit,omitempty" yaml:"aciContainersMemoryLimit,omitempty"` AciContainersMemoryRequest string `json:"aciContainersMemoryRequest,omitempty" yaml:"aciContainersMemoryRequest,omitempty"` - AciContainersOperatorMemoryLimit string `json:"aciContainersOperatorMemoryLimit,omitempty" yaml:"aciContainersOperatorMemoryLimit,omitempty"` - AciContainersOperatorMemoryRequest string `json:"aciContainersOperatorMemoryRequest,omitempty" yaml:"aciContainersOperatorMemoryRequest,omitempty"` AciMultipod string `json:"aciMultipod,omitempty" yaml:"aciMultipod,omitempty"` AciMultipodUbuntu string `json:"aciMultipodUbuntu,omitempty" yaml:"aciMultipodUbuntu,omitempty"` + AddExternalContractToDefaultEpg string `json:"addExternalContractToDefaultEpg,omitempty" yaml:"addExternalContractToDefaultEpg,omitempty"` AddExternalSubnetsToRdconfig string `json:"addExternalSubnetsToRdconfig,omitempty" yaml:"addExternalSubnetsToRdconfig,omitempty"` ApicHosts []string `json:"apicHosts,omitempty" yaml:"apicHosts,omitempty"` ApicRefreshTickerAdjust string `json:"apicRefreshTickerAdjust,omitempty" yaml:"apicRefreshTickerAdjust,omitempty"` @@ -140,6 +139,7 @@ type AciNetworkProvider struct { DurationWaitForNetwork string `json:"durationWaitForNetwork,omitempty" yaml:"durationWaitForNetwork,omitempty"` DynamicExternalSubnet string `json:"externDynamic,omitempty" yaml:"externDynamic,omitempty"` EnableEndpointSlice string `json:"enableEndpointSlice,omitempty" yaml:"enableEndpointSlice,omitempty"` + EnableOpflexAgentReconnect string `json:"enableOpflexAgentReconnect,omitempty" yaml:"enableOpflexAgentReconnect,omitempty"` EncapType string `json:"encapType,omitempty" yaml:"encapType,omitempty"` EpRegistry string `json:"epRegistry,omitempty" yaml:"epRegistry,omitempty"` GbpPodSubnet string `json:"gbpPodSubnet,omitempty" yaml:"gbpPodSubnet,omitempty"` @@ -166,6 +166,7 @@ type AciNetworkProvider struct { NoPriorityClass string `json:"noPriorityClass,omitempty" yaml:"noPriorityClass,omitempty"` NoWaitForServiceEpReadiness string `json:"noWaitForServiceEpReadiness,omitempty" yaml:"noWaitForServiceEpReadiness,omitempty"` NodePodIfEnable string `json:"nodePodIfEnable,omitempty" yaml:"nodePodIfEnable,omitempty"` + NodeSnatRedirectExclude []map[string]string `json:"nodeSnatRedirectExclude,omitempty" yaml:"nodeSnatRedirectExclude,omitempty"` NodeSubnet string `json:"nodeSubnet,omitempty" yaml:"nodeSubnet,omitempty"` OVSMemoryLimit string `json:"ovsMemoryLimit,omitempty" yaml:"ovsMemoryLimit,omitempty"` OVSMemoryRequest string `json:"ovsMemoryRequest,omitempty" yaml:"ovsMemoryRequest,omitempty"` @@ -175,10 +176,12 @@ type AciNetworkProvider struct { OpflexAgentOpflexAsyncjsonEnabled string `json:"opflexAgentOpflexAsyncjsonEnabled,omitempty" yaml:"opflexAgentOpflexAsyncjsonEnabled,omitempty"` OpflexAgentOvsAsyncjsonEnabled string `json:"opflexAgentOvsAsyncjsonEnabled,omitempty" yaml:"opflexAgentOvsAsyncjsonEnabled,omitempty"` OpflexAgentPolicyRetryDelayTimer string `json:"opflexAgentPolicyRetryDelayTimer,omitempty" yaml:"opflexAgentPolicyRetryDelayTimer,omitempty"` + OpflexAgentStatistics string `json:"opflexAgentStatistics,omitempty" yaml:"opflexAgentStatistics,omitempty"` OpflexClientSSL string `json:"opflexClientSsl,omitempty" yaml:"opflexClientSsl,omitempty"` OpflexDeviceDeleteTimeout string `json:"opflexDeviceDeleteTimeout,omitempty" yaml:"opflexDeviceDeleteTimeout,omitempty"` OpflexDeviceReconnectWaitTimeout string `json:"opflexDeviceReconnectWaitTimeout,omitempty" yaml:"opflexDeviceReconnectWaitTimeout,omitempty"` OpflexMode string `json:"opflexMode,omitempty" yaml:"opflexMode,omitempty"` + OpflexOpensslCompat string `json:"opflexOpensslCompat,omitempty" yaml:"opflexOpensslCompat,omitempty"` OpflexServerPort string `json:"opflexServerPort,omitempty" yaml:"opflexServerPort,omitempty"` OverlayVRFName string `json:"overlayVrfName,omitempty" yaml:"overlayVrfName,omitempty"` PBRTrackingNonSnat string `json:"pbrTrackingNonSnat,omitempty" yaml:"pbrTrackingNonSnat,omitempty"` @@ -202,6 +205,7 @@ type AciNetworkProvider struct { SystemIdentifier string `json:"systemId,omitempty" yaml:"systemId,omitempty"` Tenant string `json:"tenant,omitempty" yaml:"tenant,omitempty"` Token string `json:"token,omitempty" yaml:"token,omitempty"` + TolerationSeconds string `json:"tolerationSeconds,omitempty" yaml:"tolerationSeconds,omitempty"` UseAciAnywhereCRD string `json:"useAciAnywhereCrd,omitempty" yaml:"useAciAnywhereCrd,omitempty"` UseAciCniPriorityClass string `json:"useAciCniPriorityClass,omitempty" yaml:"useAciCniPriorityClass,omitempty"` UseClusterRole string `json:"useClusterRole,omitempty" yaml:"useClusterRole,omitempty"` diff --git a/tests/v2/codecoverage/package/Dockerfile b/tests/v2/codecoverage/package/Dockerfile index 04f2e8f83ce..ed8dcdc4ac9 100644 --- a/tests/v2/codecoverage/package/Dockerfile +++ b/tests/v2/codecoverage/package/Dockerfile @@ -178,7 +178,7 @@ ENV CATTLE_CLI_VERSION v2.8.0 ENV CATTLE_BASE_UI_BRAND= # Please update the api-ui-version in pkg/settings/settings.go when updating the version here. -ENV CATTLE_API_UI_VERSION 1.1.10 +ENV CATTLE_API_UI_VERSION 1.1.11 RUN mkdir -p /var/log/auditlog ENV AUDIT_LOG_PATH /var/log/auditlog/rancher-api-audit.log