diff --git a/deploy/charts/disco-agent/templates/configmap.yaml b/deploy/charts/disco-agent/templates/configmap.yaml index e88ae910..3d38b542 100644 --- a/deploy/charts/disco-agent/templates/configmap.yaml +++ b/deploy/charts/disco-agent/templates/configmap.yaml @@ -145,3 +145,17 @@ data: group: external-secrets.io version: v1 resource: clustersecretstores + - kind: k8s-dynamic + name: ark/secretproviderclasses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasses + - kind: k8s-dynamic + name: ark/secretproviderclasspodstatuses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasspodstatuses diff --git a/deploy/charts/disco-agent/templates/rbac.yaml b/deploy/charts/disco-agent/templates/rbac.yaml index 92bd1349..844ddf1e 100644 --- a/deploy/charts/disco-agent/templates/rbac.yaml +++ b/deploy/charts/disco-agent/templates/rbac.yaml @@ -140,3 +140,31 @@ subjects: - kind: ServiceAccount name: {{ include "disco-agent.serviceAccountName" . }} namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "disco-agent.fullname" . }}-csi-reader + labels: + {{- include "disco-agent.labels" . | nindent 4 }} +rules: + - apiGroups: ["secrets-store.csi.x-k8s.io"] + resources: + - secretproviderclasses + - secretproviderclasspodstatuses + verbs: ["get", "list", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "disco-agent.fullname" . }}-csi-reader + labels: + {{- include "disco-agent.labels" . | nindent 4 }} +roleRef: + kind: ClusterRole + name: {{ include "disco-agent.fullname" . }}-csi-reader + apiGroup: rbac.authorization.k8s.io +subjects: + - kind: ServiceAccount + name: {{ include "disco-agent.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} diff --git a/deploy/charts/disco-agent/tests/__snapshot__/configmap_test.yaml.snap b/deploy/charts/disco-agent/tests/__snapshot__/configmap_test.yaml.snap index 3ee3c884..998d3082 100644 --- a/deploy/charts/disco-agent/tests/__snapshot__/configmap_test.yaml.snap +++ b/deploy/charts/disco-agent/tests/__snapshot__/configmap_test.yaml.snap @@ -133,6 +133,20 @@ custom-cluster-description: group: external-secrets.io version: v1 resource: clustersecretstores + - kind: k8s-dynamic + name: ark/secretproviderclasses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasses + - kind: k8s-dynamic + name: ark/secretproviderclasspodstatuses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasspodstatuses kind: ConfigMap metadata: labels: @@ -278,6 +292,20 @@ custom-cluster-name: group: external-secrets.io version: v1 resource: clustersecretstores + - kind: k8s-dynamic + name: ark/secretproviderclasses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasses + - kind: k8s-dynamic + name: ark/secretproviderclasspodstatuses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasspodstatuses kind: ConfigMap metadata: labels: @@ -423,6 +451,20 @@ custom-period: group: external-secrets.io version: v1 resource: clustersecretstores + - kind: k8s-dynamic + name: ark/secretproviderclasses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasses + - kind: k8s-dynamic + name: ark/secretproviderclasspodstatuses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasspodstatuses kind: ConfigMap metadata: labels: @@ -568,6 +610,20 @@ defaults: group: external-secrets.io version: v1 resource: clustersecretstores + - kind: k8s-dynamic + name: ark/secretproviderclasses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasses + - kind: k8s-dynamic + name: ark/secretproviderclasspodstatuses + config: + resource-type: + group: secrets-store.csi.x-k8s.io + version: v1 + resource: secretproviderclasspodstatuses kind: ConfigMap metadata: labels: diff --git a/internal/cyberark/dataupload/dataupload.go b/internal/cyberark/dataupload/dataupload.go index 27e8e856..b3043c3b 100644 --- a/internal/cyberark/dataupload/dataupload.go +++ b/internal/cyberark/dataupload/dataupload.go @@ -88,6 +88,10 @@ type Snapshot struct { ClusterExternalSecrets []runtime.Object `json:"clusterexternalsecrets"` // ClusterSecretStores is a list of ClusterSecretStore resources in the cluster. ClusterSecretStores []runtime.Object `json:"clustersecretstores"` + // SecretProviderClasses is a list of SecretProviderClass resources in the cluster. + SecretProviderClasses []runtime.Object `json:"secretproviderclasses"` + // SecretProviderClassPodStatuses is a list of SecretProviderClassPodStatus resources in the cluster. + SecretProviderClassPodStatuses []runtime.Object `json:"secretproviderclasspodstatuses"` // Roles is a list of Role resources in the cluster. Roles []runtime.Object `json:"roles"` // ClusterRoles is a list of ClusterRole resources in the cluster. diff --git a/pkg/client/client_cyberark.go b/pkg/client/client_cyberark.go index 3232e7c6..6de22791 100644 --- a/pkg/client/client_cyberark.go +++ b/pkg/client/client_cyberark.go @@ -244,6 +244,12 @@ var defaultExtractorFunctions = map[string]func(*api.DataReading, *dataupload.Sn "ark/esoclustersecretstores": func(r *api.DataReading, s *dataupload.Snapshot) error { return extractResourceListFromReading(r, &s.ClusterSecretStores) }, + "ark/secretproviderclasses": func(r *api.DataReading, s *dataupload.Snapshot) error { + return extractResourceListFromReading(r, &s.SecretProviderClasses) + }, + "ark/secretproviderclasspodstatuses": func(r *api.DataReading, s *dataupload.Snapshot) error { + return extractResourceListFromReading(r, &s.SecretProviderClassPodStatuses) + }, } // convertDataReadings processes a list of DataReadings using the provided diff --git a/pkg/client/client_cyberark_convertdatareadings_test.go b/pkg/client/client_cyberark_convertdatareadings_test.go index 293629c9..de1a598b 100644 --- a/pkg/client/client_cyberark_convertdatareadings_test.go +++ b/pkg/client/client_cyberark_convertdatareadings_test.go @@ -815,6 +815,188 @@ func TestConvertDataReadings_ClusterSecretStores(t *testing.T) { assert.Equal(t, "aws-cluster-secret-store", css2.GetName()) } +// TestConvertDataReadings_SecretProviderClasses tests that secretproviderclasses are correctly converted. +func TestConvertDataReadings_SecretProviderClasses(t *testing.T) { + extractorFunctions := map[string]func(*api.DataReading, *dataupload.Snapshot) error{ + "ark/discovery": extractClusterIDAndServerVersionFromReading, + "ark/secretproviderclasses": func(reading *api.DataReading, snapshot *dataupload.Snapshot) error { + return extractResourceListFromReading(reading, &snapshot.SecretProviderClasses) + }, + } + + readings := []*api.DataReading{ + { + DataGatherer: "ark/discovery", + Data: &api.DiscoveryData{ + ClusterID: "test-cluster-id", + ServerVersion: &version.Info{ + GitVersion: "v1.21.0", + }, + }, + }, + { + DataGatherer: "ark/secretproviderclasses", + Data: &api.DynamicData{ + Items: []*api.GatheredResource{ + { + Resource: &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "secrets-store.csi.x-k8s.io/v1", + "kind": "SecretProviderClass", + "metadata": map[string]any{ + "name": "conjur-spc", + "namespace": "default", + }, + "spec": map[string]any{ + "provider": "conjur", + }, + }, + }, + }, + { + Resource: &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "secrets-store.csi.x-k8s.io/v1", + "kind": "SecretProviderClass", + "metadata": map[string]any{ + "name": "vault-spc", + "namespace": "default", + }, + "spec": map[string]any{ + "provider": "vault", + }, + }, + }, + }, + // Deleted secretproviderclass should be ignored + { + DeletedAt: api.Time{Time: time.Now()}, + Resource: &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "secrets-store.csi.x-k8s.io/v1", + "kind": "SecretProviderClass", + "metadata": map[string]any{ + "name": "deleted-spc", + "namespace": "default", + }, + }, + }, + }, + }, + }, + }, + } + + var snapshot dataupload.Snapshot + err := convertDataReadings(extractorFunctions, readings, &snapshot) + require.NoError(t, err) + + assert.Equal(t, "test-cluster-id", snapshot.ClusterID) + require.Len(t, snapshot.SecretProviderClasses, 2, "should have 2 secretproviderclasses (deleted one should be excluded)") + + spc1, ok := snapshot.SecretProviderClasses[0].(*unstructured.Unstructured) + require.True(t, ok, "secretproviderclass should be unstructured") + assert.Equal(t, "SecretProviderClass", spc1.GetKind()) + assert.Equal(t, "conjur-spc", spc1.GetName()) + + spc2, ok := snapshot.SecretProviderClasses[1].(*unstructured.Unstructured) + require.True(t, ok, "secretproviderclass should be unstructured") + assert.Equal(t, "SecretProviderClass", spc2.GetKind()) + assert.Equal(t, "vault-spc", spc2.GetName()) +} + +// TestConvertDataReadings_SecretProviderClassPodStatuses tests that secretproviderclasspodstatuses are correctly converted. +func TestConvertDataReadings_SecretProviderClassPodStatuses(t *testing.T) { + extractorFunctions := map[string]func(*api.DataReading, *dataupload.Snapshot) error{ + "ark/discovery": extractClusterIDAndServerVersionFromReading, + "ark/secretproviderclasspodstatuses": func(reading *api.DataReading, snapshot *dataupload.Snapshot) error { + return extractResourceListFromReading(reading, &snapshot.SecretProviderClassPodStatuses) + }, + } + + readings := []*api.DataReading{ + { + DataGatherer: "ark/discovery", + Data: &api.DiscoveryData{ + ClusterID: "test-cluster-id", + ServerVersion: &version.Info{ + GitVersion: "v1.21.0", + }, + }, + }, + { + DataGatherer: "ark/secretproviderclasspodstatuses", + Data: &api.DynamicData{ + Items: []*api.GatheredResource{ + { + Resource: &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "secrets-store.csi.x-k8s.io/v1", + "kind": "SecretProviderClassPodStatus", + "metadata": map[string]any{ + "name": "my-pod-conjur-spc", + "namespace": "default", + }, + "status": map[string]any{ + "mounted": true, + "podName": "my-pod", + }, + }, + }, + }, + { + Resource: &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "secrets-store.csi.x-k8s.io/v1", + "kind": "SecretProviderClassPodStatus", + "metadata": map[string]any{ + "name": "other-pod-conjur-spc", + "namespace": "default", + }, + "status": map[string]any{ + "mounted": false, + "podName": "other-pod", + }, + }, + }, + }, + // Deleted secretproviderclasspodstatus should be ignored + { + DeletedAt: api.Time{Time: time.Now()}, + Resource: &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "secrets-store.csi.x-k8s.io/v1", + "kind": "SecretProviderClassPodStatus", + "metadata": map[string]any{ + "name": "deleted-pod-spc", + "namespace": "default", + }, + }, + }, + }, + }, + }, + }, + } + + var snapshot dataupload.Snapshot + err := convertDataReadings(extractorFunctions, readings, &snapshot) + require.NoError(t, err) + + assert.Equal(t, "test-cluster-id", snapshot.ClusterID) + require.Len(t, snapshot.SecretProviderClassPodStatuses, 2, "should have 2 secretproviderclasspodstatuses (deleted one should be excluded)") + + spcps1, ok := snapshot.SecretProviderClassPodStatuses[0].(*unstructured.Unstructured) + require.True(t, ok, "secretproviderclasspodstatus should be unstructured") + assert.Equal(t, "SecretProviderClassPodStatus", spcps1.GetKind()) + assert.Equal(t, "my-pod-conjur-spc", spcps1.GetName()) + + spcps2, ok := snapshot.SecretProviderClassPodStatuses[1].(*unstructured.Unstructured) + require.True(t, ok, "secretproviderclasspodstatus should be unstructured") + assert.Equal(t, "SecretProviderClassPodStatus", spcps2.GetKind()) + assert.Equal(t, "other-pod-conjur-spc", spcps2.GetName()) +} + // TestConvertDataReadings_ServiceAccounts tests that serviceaccounts are correctly converted. func TestConvertDataReadings_ServiceAccounts(t *testing.T) { extractorFunctions := map[string]func(*api.DataReading, *dataupload.Snapshot) error{ diff --git a/pkg/client/client_cyberark_test.go b/pkg/client/client_cyberark_test.go index 4931c376..cdbdc0fb 100644 --- a/pkg/client/client_cyberark_test.go +++ b/pkg/client/client_cyberark_test.go @@ -91,6 +91,8 @@ var defaultDynamicDatagathererNames = []string{ "ark/esosecretstores", "ark/esoclusterexternalsecrets", "ark/esoclustersecretstores", + "ark/secretproviderclasses", + "ark/secretproviderclasspodstatuses", "ark/roles", "ark/clusterroles", "ark/rolebindings",