diff --git a/charts/csm-authorization/policies/snapshot-create-test.rego b/charts/csm-authorization/policies/snapshot-create-test.rego new file mode 100644 index 00000000..920fc4e9 --- /dev/null +++ b/charts/csm-authorization/policies/snapshot-create-test.rego @@ -0,0 +1,359 @@ +# Copyright © 2024 Dell Inc., or its subsidiaries. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package karavi.snapshot.create_test + +import data.karavi.snapshot.create + +import rego.v1 + + +test_snapshot_simple_request_allowed if { + content := { + "claims": { + "aud": "karavi", + "exp": 1615426023, + "group": "DevOpsGroup1", + "iss": "com.dell.karavi", + "roles": "us-east-1", + "sub": "karavi-tenant", + }, + "request": [{ + "name": "k8s-0fc0695994-snapshot", + "protectionDomainId": "6b2ffe6c00000000", + "storagePoolId": "ae376b0300000000", + "volumeSizeInKb": "8388608", + "volumeType": "ThinProvisioned", + "storagepool": "bronze", + }], + "storagesystemid": "2222", + "systemtype": "powerflex", + } + + role := {"us-east-1": {"system_types": {"powerflex": {"system_ids": {"2222": {"pool_quotas": {"bronze": "44000000"}}}}}}} + + create.allow with input as content with data.karavi.common.roles as role +} + +test_snapshot_multi_role_request_allowed if { + content := { + "claims": { + "aud": "karavi", + "exp": 1615426023, + "group": "DevOpsGroup1", + "iss": "com.dell.karavi", + "roles": "us-east-1,us-west-1", + "sub": "karavi-tenant", + }, + "request": [{ + "name": "k8s-0fc0695994-snapshot", + "protectionDomainId": "6b2ffe6c00000000", + "storagePoolId": "ae376b0300000000", + "volumeSizeInKb": "8388608", + "volumeType": "ThinProvisioned", + "storagepool": "bronze", + }], + "storagesystemid": "2222", + "systemtype": "powerflex", + } + + role := { + "us-east-1": {"system_types": {"powerflex": {"system_ids": {"2222": {"pool_quotas": {"bronze": "44000000"}}}}}}, + "us-west-1": {"system_types": {"powerflex": {"system_ids": {"2222": {"pool_quotas": {"bronze": 83886080}}}}}}, + } + + create.allow with input as content with data.karavi.common.roles as role +} + +test_snapshot_multi_request_allowed if { + content := { + "claims": { + "aud": "karavi", + "exp": 1615426023, + "group": "DevOpsGroup1", + "iss": "com.dell.karavi", + "roles": "us-east-1,us-west-1", + "sub": "karavi-tenant", + }, + "request": [ + { + "name": "k8s-0fc0695994-snapshot", + "protectionDomainId": "6b2ffe6c00000000", + "storagePoolId": "ae376b0300000000", + "volumeSizeInKb": "8388608", + "volumeType": "ThinProvisioned", + "storagepool": "bronze", + }, + { + "name": "k8s-0fc0695995-snapshot", + "protectionDomainId": "6b2ffe6c00000000", + "storagePoolId": "ae376b0300000000", + "volumeSizeInKb": "8388608", + "volumeType": "ThinProvisioned", + "storagepool": "bronze", + }, + ], + "storagesystemid": "2222", + "systemtype": "powerflex", + } + + role := {"us-east-1": {"system_types": {"powerflex": {"system_ids": {"2222": {"pool_quotas": {"bronze": "44000000"}}}}}}} + + create.allow with input as content with data.karavi.common.roles as role +} + +test_snapshot_multi_request_multi_role_allowed if { + content := { + "claims": { + "aud": "karavi", + "exp": 1615426023, + "group": "DevOpsGroup1", + "iss": "com.dell.karavi", + "roles": "us-east-1,us-west-1", + "sub": "karavi-tenant", + }, + "request": [ + { + "name": "k8s-0fc0695994-snapshot", + "protectionDomainId": "6b2ffe6c00000000", + "storagePoolId": "ae376b0300000000", + "volumeSizeInKb": "8388608", + "volumeType": "ThinProvisioned", + "storagepool": "bronze", + }, + { + "name": "k8s-0fc0695995-snapshot", + "protectionDomainId": "6b2ffe6c00000000", + "storagePoolId": "ae376b0300000000", + "volumeSizeInKb": "8388608", + "volumeType": "ThinProvisioned", + "storagepool": "silver", + }, + ], + "storagesystemid": "2222", + "systemtype": "powerflex", + } + + role := { + "us-east-1": {"system_types": {"powerflex": {"system_ids": {"2222": {"pool_quotas": {"bronze": "44000000", "silver": "88000000"}}}}}}, + "us-west-1": {"system_types": {"powerflex": {"system_ids": {"2222": {"pool_quotas": {"silver": 83886080}}}}}}, + } + + create.allow with input as content with data.karavi.common.roles as role +} + +test_snapshot_empty_request_allowed if { + content := { + "claims": { + "aud": "karavi", + "exp": 1615426023, + "group": "DevOpsGroup1", + "iss": "com.dell.karavi", + "roles": "us-east-1", + "sub": "karavi-tenant", + }, + "request": [], + "storagesystemid": "2222", + "systemtype": "powerflex", + } + + role := {"us-east-1": {"system_types": {"powerflex": {"system_ids": {"2222": {"pool_quotas": {"bronze": "44000000"}}}}}}} + + create.allow with input as content with data.karavi.common.roles as role +} + +test_snapshot_infinite_quota_allowed if { + content := { + "claims": { + "aud": "karavi", + "exp": 1615426023, + "group": "DevOpsGroup1", + "iss": "com.dell.karavi", + "roles": "us-east-1", + "sub": "karavi-tenant", + }, + "request": [ + { + "name": "k8s-0fc0695994-snapshot", + "protectionDomainId": "6b2ffe6c00000000", + "storagePoolId": "ae376b0300000000", + "volumeSizeInKb": "8388608", + "volumeType": "ThinProvisioned", + "storagepool": "bronze", + }, + { + "name": "k8s-0fc0695995-snapshot", + "protectionDomainId": "6b2ffe6c00000000", + "storagePoolId": "ae376b0300000000", + "volumeSizeInKb": "8388608", + "volumeType": "ThinProvisioned", + "storagepool": "bronze", + }, + ], + "storagesystemid": "2222", + "systemtype": "powerflex", + } + + role := {"us-east-1": {"system_types": {"powerflex": {"system_ids": {"2222": {"pool_quotas": {"bronze": 0}}}}}}} + + create.allow with input as content with data.karavi.common.roles as role +} + +test_snapshot_deny_role_with_insufficient_quota if { + content := { + "claims": { + "aud": "karavi", + "exp": 1615426023, + "group": "DevOpsGroup1", + "iss": "com.dell.karavi", + "roles": "us-east-1", + "sub": "karavi-tenant", + }, + "request": [ + { + "name": "k8s-0fc0695994-snapshot", + "protectionDomainId": "6b2ffe6c00000000", + "storagePoolId": "ae376b0300000000", + "volumeSizeInKb": "8388608", + "volumeType": "ThinProvisioned", + "storagepool": "bronze", + }, + { + "name": "k8s-0fc0695995-snapshot", + "protectionDomainId": "6b2ffe6c00000000", + "storagePoolId": "ae376b0300000000", + "volumeSizeInKb": "8388608", + "volumeType": "ThinProvisioned", + "storagepool": "bronze", + }, + ], + "storagesystemid": "2222", + "systemtype": "powerflex", + } + + role := {"us-east-1": {"system_types": {"powerflex": {"system_ids": {"2222": {"pool_quotas": {"bronze": "10"}}}}}}} + + result := create.deny with input as content with data.karavi.common.roles as role + + count(result) != 0 +} + +test_snapshot_deny_multiple_roles_with_not_permitted_pool if { + content := { + "claims": { + "aud": "karavi", + "exp": 1615426023, + "group": "DevOpsGroup1", + "iss": "com.dell.karavi", + "roles": "us-east-1,us-west-1", + "sub": "karavi-tenant", + }, + "request": [ + { + "name": "k8s-0fc0695994-snapshot", + "protectionDomainId": "6b2ffe6c00000000", + "storagePoolId": "ae376b0300000000", + "volumeSizeInKb": "8388608", + "volumeType": "ThinProvisioned", + "storagepool": "bronze", + }, + { + "name": "k8s-0fc0695995-snapshot", + "protectionDomainId": "6b2ffe6c00000000", + "storagePoolId": "ae376b0300000000", + "volumeSizeInKb": "8388608", + "volumeType": "ThinProvisioned", + "storagepool": "yellow", + }, + ], + "storagesystemid": "2222", + "systemtype": "powerflex", + } + + role := { + "us-east-1": {"system_types": {"powerflex": {"system_ids": {"2222": {"pool_quotas": {"bronze": "44000000"}}}}}}, + "us-west-1": {"system_types": {"powerflex": {"system_ids": {"2222": {"pool_quotas": {"silver": 4000000}}}}}}, + } + + result := create.deny with input as content with data.karavi.common.roles as role + + count(result) != 0 +} + +test_snapshot_deny_multiple_roles_with_insufficient_quota if { + content := { + "claims": { + "aud": "karavi", + "exp": 1615426023, + "group": "DevOpsGroup1", + "iss": "com.dell.karavi", + "roles": "us-east-1,us-west-1", + "sub": "karavi-tenant", + }, + "request": [ + { + "name": "k8s-0fc0695994-snapshot", + "protectionDomainId": "6b2ffe6c00000000", + "storagePoolId": "ae376b0300000000", + "volumeSizeInKb": "8388608", + "volumeType": "ThinProvisioned", + "storagepool": "bronze", + }, + { + "name": "k8s-0fc0695995-snapshot", + "protectionDomainId": "6b2ffe6c00000000", + "storagePoolId": "ae376b0300000000", + "volumeSizeInKb": "8388608", + "volumeType": "ThinProvisioned", + "storagepool": "silver", + }, + ], + "storagesystemid": "2222", + "systemtype": "powerflex", + } + + role := { + "us-east-1": {"system_types": {"powerflex": {"system_ids": {"2222": {"pool_quotas": {"bronze": "44000000"}}}}}}, + "us-west-1": {"system_types": {"powerflex": {"system_ids": {"2222": {"pool_quotas": {"silver": 4000000}}}}}}, + } + + result := create.deny with input as content with data.karavi.common.roles as role + + count(result) != 0 +} + +test_snapshot_deny_empty_roles if { + content := { + "claims": { + "aud": "karavi", + "exp": 1615426023, + "group": "DevOpsGroup1", + "iss": "com.dell.karavi", + "roles": "us-east-1", + "sub": "karavi-tenant", + }, + "request": [{ + "name": "k8s-0fc0695994-snapshot", + "protectionDomainId": "6b2ffe6c00000000", + "storagePoolId": "ae376b0300000000", + "volumeSizeInKb": "8388608", + "volumeType": "ThinProvisioned", + "storagepool": "bronze", + }], + "storagesystemid": "2222", + "systemtype": "powerflex", + } + + create.deny["no configured roles"] with input as content with data.karavi.common.roles as {} +} diff --git a/charts/csm-authorization/policies/snapshot-create.rego b/charts/csm-authorization/policies/snapshot-create.rego new file mode 100644 index 00000000..4b9872c3 --- /dev/null +++ b/charts/csm-authorization/policies/snapshot-create.rego @@ -0,0 +1,97 @@ +# Copyright © 2024 Dell Inc., or its subsidiaries. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package karavi.snapshot.create + +import data.karavi.common + +default allow := false + +allow { + count(permitted_roles) == count(input.request) + count(deny) == 0 +} + +# Deny if there are no roles found. +deny[msg] { + common.roles == {} + msg := sprintf("no configured roles", []) +} + +# Deny if claimed roles has no match for the request. +deny[msg] { + count(permitted_roles) != count(input.request) + + unpermitted_requests := [req | + element := input.request[_] + + not permitted_roles[element.name] + + req := element + ] + + msg := sprintf( + "no roles in [%s] allow the %s Kb request on %s/%s/%s for %s", + [ + input.claims.roles, + unpermitted_requests[_].volumeSizeInKb, + input.systemtype, + input.storagesystemid, + unpermitted_requests[_].storagepool, + unpermitted_requests[_].name, + ], + ) +} + +# No OR in OPA, multiple rules are needed. +size_is_valid(a, b) { + to_number(a) >= to_number(b) +} + +# No OR in OPA, multiple rules are needed. +size_is_valid(a, _) { + to_number(a) == 0 +} + +# Create a list of permitted roles. +permitted_roles[snapshot] := roles { + # Split the claimed roles by comma into an array. + claimed_roles := split(input.claims.roles, ",") + + # Iterate through the requests. + req := input.request[_] + + roles := [role | + sp := req.storagepool + size := req.volumeSizeInKb + + # Iterate through the roles in the request. + c_role := claimed_roles[_] + common.roles[c_role] + + system_ids := common.roles[c_role].system_types[input.systemtype].system_ids[input.storagesystemid] + pool_quota := system_ids.pool_quotas[sp] + + # Validate that the pool quota is valid. + size_is_valid(pool_quota, size) + + role := {"size": to_number(pool_quota), "storagepool": sp, "role": c_role} + ] + + # Ensure that the role list is not empty. + count(roles) != 0 + + # Set the snapshot name which creates an entry in the list. + snapshot := req.name +} diff --git a/charts/csm-authorization/templates/policies.yaml b/charts/csm-authorization/templates/policies.yaml index 838f7965..02c9e52b 100644 --- a/charts/csm-authorization/templates/policies.yaml +++ b/charts/csm-authorization/templates/policies.yaml @@ -53,3 +53,11 @@ metadata: namespace: {{ .Release.Namespace }} data: {{- (.Files.Glob "policies/sdc-approve.rego").AsConfig | nindent 2 }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: snapshot-create + namespace: {{ .Release.Namespace }} +data: + {{- (.Files.Glob "policies/snapshot-create.rego").AsConfig | nindent 2 }} diff --git a/charts/csm-authorization/templates/storage-service.yaml b/charts/csm-authorization/templates/storage-service.yaml index 537a6ae5..44b272fb 100644 --- a/charts/csm-authorization/templates/storage-service.yaml +++ b/charts/csm-authorization/templates/storage-service.yaml @@ -10,8 +10,14 @@ metadata: name: storage-service rules: - apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "patch","post"] + resources: ["secrets", "events"] + verbs: ["get", "patch","post", create] + - apiGroups: ["csm-authorization.storage.dell.com"] + resources: ["storages", "csmtenants", "csmroles"] + verbs: ["get", "list"] + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["create", "update", "get", "list"] --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 @@ -62,17 +68,33 @@ spec: - name: storage-service image: {{ required "Must provide the storage-service image." .Values.authorization.images.storageService }} imagePullPolicy: Always + env: + {{- $str := "" -}} + {{- $ns := include "custom.namespace" . -}} + {{- $replicas := .Values.redis.replicas | int }} + {{- $sentinel := .Values.redis.sentinel }} + {{- range $i, $e := until $replicas }} + {{- if $i }} + {{- $str = print $str "," -}} + {{- end }} + {{- $str = printf "%s%s-%d.%s.%s.svc.cluster.local:5000" $str $sentinel $i $sentinel $ns -}} + {{- end }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: redis-csm-secret + key: password args: - "--vault-address={{ .Values.vault.address }}" - "--vault-kv-engine-path={{ .Values.vault.kvEnginePath }}" - "--vault-role={{ .Values.vault.role }}" - "--vault-skip-certificate-validation={{ .Values.vault.skipCertificateValidation }}" + - "--redis-sentinel={{ $str }}" + - "--redis-password=$(REDIS_PASSWORD)" + - "--leader-election=true" ports: - containerPort: 50051 name: grpc - env: - - name: NAMESPACE - value: {{ include "custom.namespace" . }} volumeMounts: - name: config-volume mountPath: /etc/karavi-authorization/config