From 7e6b3cf0946620f4c23560a437eaac07f1b50980 Mon Sep 17 00:00:00 2001 From: Israel Blancas Date: Fri, 15 Nov 2024 13:43:55 +0100 Subject: [PATCH] Add automatic RBAC creation for k8sobjects receiver (#3430) * Add automatic RBAC creation for k8sobjects receiver Signed-off-by: Israel Blancas * Fix documentation Signed-off-by: Israel Blancas * Move to v1beta1 Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas --- .chloggen/3429.yaml | 16 +++ internal/components/receivers/helpers.go | 3 + internal/components/receivers/k8sobjects.go | 49 +++++++ .../components/receivers/k8sobjects_test.go | 136 ++++++++++++++++++ .../receiver-k8sobjects/00-install.yaml | 4 + .../receiver-k8sobjects/01-assert.yaml | 31 ++++ .../receiver-k8sobjects/01-install.yaml | 22 +++ .../receiver-k8sobjects/chainsaw-test.yaml | 18 +++ 8 files changed, 279 insertions(+) create mode 100755 .chloggen/3429.yaml create mode 100644 internal/components/receivers/k8sobjects.go create mode 100644 internal/components/receivers/k8sobjects_test.go create mode 100644 tests/e2e-automatic-rbac/receiver-k8sobjects/00-install.yaml create mode 100644 tests/e2e-automatic-rbac/receiver-k8sobjects/01-assert.yaml create mode 100644 tests/e2e-automatic-rbac/receiver-k8sobjects/01-install.yaml create mode 100644 tests/e2e-automatic-rbac/receiver-k8sobjects/chainsaw-test.yaml diff --git a/.chloggen/3429.yaml b/.chloggen/3429.yaml new file mode 100755 index 0000000000..8845b8812c --- /dev/null +++ b/.chloggen/3429.yaml @@ -0,0 +1,16 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action) +component: collector + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Create RBAC rules for the k8sobjects receiver automatically. + +# One or more tracking issues related to the change +issues: [3429] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: diff --git a/internal/components/receivers/helpers.go b/internal/components/receivers/helpers.go index 568a92efc8..37cc710e39 100644 --- a/internal/components/receivers/helpers.go +++ b/internal/components/receivers/helpers.go @@ -143,6 +143,9 @@ var ( components.NewBuilder[k8seventsConfig]().WithName("k8s_events"). WithRbacGen(generatek8seventsRbacRules). MustBuild(), + components.NewBuilder[k8sobjectsConfig]().WithName("k8sobjects"). + WithRbacGen(generatek8sobjectsRbacRules). + MustBuild(), NewScraperParser("prometheus"), NewScraperParser("sshcheck"), NewScraperParser("cloudfoundry"), diff --git a/internal/components/receivers/k8sobjects.go b/internal/components/receivers/k8sobjects.go new file mode 100644 index 0000000000..10505ad35c --- /dev/null +++ b/internal/components/receivers/k8sobjects.go @@ -0,0 +1,49 @@ +// Copyright The OpenTelemetry Authors +// +// 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 receivers + +import ( + "github.com/go-logr/logr" + rbacv1 "k8s.io/api/rbac/v1" +) + +type k8sobjectsConfig struct { + Objects []k8sObject `yaml:"objects"` +} + +type k8sObject struct { + Name string `yaml:"name"` + Mode string `yaml:"mode"` + Group string `yaml:"group,omitempty"` +} + +func generatek8sobjectsRbacRules(_ logr.Logger, config k8sobjectsConfig) ([]rbacv1.PolicyRule, error) { + // https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/k8sobjectsreceiver#rbac + prs := []rbacv1.PolicyRule{} + for _, obj := range config.Objects { + permissions := []string{"list"} + if obj.Mode == "pull" && (obj.Name != "events" && obj.Name != "events.k8s.io") { + permissions = append(permissions, "get") + } else if obj.Mode == "watch" { + permissions = append(permissions, "watch") + } + prs = append(prs, rbacv1.PolicyRule{ + APIGroups: []string{obj.Group}, + Resources: []string{obj.Name}, + Verbs: permissions, + }) + } + return prs, nil +} diff --git a/internal/components/receivers/k8sobjects_test.go b/internal/components/receivers/k8sobjects_test.go new file mode 100644 index 0000000000..647882f572 --- /dev/null +++ b/internal/components/receivers/k8sobjects_test.go @@ -0,0 +1,136 @@ +// Copyright The OpenTelemetry Authors +// +// 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 receivers + +import ( + "testing" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + rbacv1 "k8s.io/api/rbac/v1" +) + +func Test_generatek8sobjectsRbacRules(t *testing.T) { + tests := []struct { + name string + config k8sobjectsConfig + want []rbacv1.PolicyRule + }{ + { + name: "basic watch mode", + config: k8sobjectsConfig{ + Objects: []k8sObject{ + { + Name: "pods", + Mode: "watch", + Group: "v1", + }, + }, + }, + want: []rbacv1.PolicyRule{ + { + APIGroups: []string{"v1"}, + Resources: []string{"pods"}, + Verbs: []string{"list", "watch"}, + }, + }, + }, + { + name: "pull mode with events", + config: k8sobjectsConfig{ + Objects: []k8sObject{ + { + Name: "events", + Mode: "pull", + Group: "v1", + }, + }, + }, + want: []rbacv1.PolicyRule{ + { + APIGroups: []string{"v1"}, + Resources: []string{"events"}, + Verbs: []string{"list"}, + }, + }, + }, + { + name: "pull mode with non-events", + config: k8sobjectsConfig{ + Objects: []k8sObject{ + { + Name: "pods", + Mode: "pull", + Group: "v1", + }, + }, + }, + want: []rbacv1.PolicyRule{ + { + APIGroups: []string{"v1"}, + Resources: []string{"pods"}, + Verbs: []string{"list", "get"}, + }, + }, + }, + { + name: "multiple objects", + config: k8sobjectsConfig{ + Objects: []k8sObject{ + { + Name: "pods", + Mode: "pull", + Group: "v1", + }, + { + Name: "events", + Mode: "pull", + Group: "v1", + }, + { + Name: "deployments", + Mode: "watch", + Group: "apps/v1", + }, + }, + }, + want: []rbacv1.PolicyRule{ + { + APIGroups: []string{"v1"}, + Resources: []string{"pods"}, + Verbs: []string{"list", "get"}, + }, + { + APIGroups: []string{"v1"}, + Resources: []string{"events"}, + Verbs: []string{"list"}, + }, + { + APIGroups: []string{"apps/v1"}, + Resources: []string{"deployments"}, + Verbs: []string{"list", "watch"}, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := generatek8sobjectsRbacRules(logr.Logger{}, tt.config) + assert.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/tests/e2e-automatic-rbac/receiver-k8sobjects/00-install.yaml b/tests/e2e-automatic-rbac/receiver-k8sobjects/00-install.yaml new file mode 100644 index 0000000000..76e8a59449 --- /dev/null +++ b/tests/e2e-automatic-rbac/receiver-k8sobjects/00-install.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: chainsaw-k8sobjects diff --git a/tests/e2e-automatic-rbac/receiver-k8sobjects/01-assert.yaml b/tests/e2e-automatic-rbac/receiver-k8sobjects/01-assert.yaml new file mode 100644 index 0000000000..5542960bbb --- /dev/null +++ b/tests/e2e-automatic-rbac/receiver-k8sobjects/01-assert.yaml @@ -0,0 +1,31 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: simplest-chainsaw-k8sobjects-cluster-role +rules: +- apiGroups: + - "" + resources: + - pods + verbs: + - list + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: chainsaw-k8sobjects.simplest + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: simplest-chainsaw-k8sobjects-collector + app.kubernetes.io/part-of: opentelemetry + name: simplest-chainsaw-k8sobjects-collector +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: simplest-chainsaw-k8sobjects-cluster-role +subjects: +- kind: ServiceAccount + name: simplest-collector + namespace: chainsaw-k8sobjects diff --git a/tests/e2e-automatic-rbac/receiver-k8sobjects/01-install.yaml b/tests/e2e-automatic-rbac/receiver-k8sobjects/01-install.yaml new file mode 100644 index 0000000000..fde02268ff --- /dev/null +++ b/tests/e2e-automatic-rbac/receiver-k8sobjects/01-install.yaml @@ -0,0 +1,22 @@ +apiVersion: opentelemetry.io/v1beta1 +kind: OpenTelemetryCollector +metadata: + name: simplest + namespace: chainsaw-k8sobjects +spec: + config: + receivers: + k8sobjects: + auth_type: serviceAccount + objects: + - name: pods + mode: pull + processors: + exporters: + debug: + service: + pipelines: + traces: + receivers: [k8sobjects] + processors: [] + exporters: [debug] diff --git a/tests/e2e-automatic-rbac/receiver-k8sobjects/chainsaw-test.yaml b/tests/e2e-automatic-rbac/receiver-k8sobjects/chainsaw-test.yaml new file mode 100644 index 0000000000..0cc38d9945 --- /dev/null +++ b/tests/e2e-automatic-rbac/receiver-k8sobjects/chainsaw-test.yaml @@ -0,0 +1,18 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/kyverno/chainsaw/main/.schemas/json/test-chainsaw-v1alpha1.json +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + creationTimestamp: null + name: receiver-k8sobjects +spec: + steps: + - name: create-namespace + try: + - apply: + file: 00-install.yaml + - name: pod-pull-config + try: + - apply: + file: 01-install.yaml + - assert: + file: 01-assert.yaml