Skip to content

Commit de651f3

Browse files
committed
Check for the permissions instead of using a CLI flag. #2588
Signed-off-by: Israel Blancas <[email protected]>
1 parent c65629b commit de651f3

File tree

6 files changed

+203
-79
lines changed

6 files changed

+203
-79
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
2+
change_type: enhancement
3+
4+
# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action)
5+
component: operator
6+
7+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
8+
note: Check the permissions for the SA running the OpenTelemetry Operator instead of using the create-rbac-permissions flag.
9+
10+
# One or more tracking issues related to the change
11+
issues: [2588]
12+
13+
# (Optional) One or more lines of additional information to render under the primary note.
14+
# These lines will be padded with 2 spaces and then inserted directly into the document.
15+
# Use pipe (|) for multiline entries.
16+
subtext:

apis/v1alpha1/collector_webhook.go

+1-25
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@ package v1alpha1
1717
import (
1818
"context"
1919
"fmt"
20-
"strings"
2120

2221
"github.com/go-logr/logr"
23-
v1 "k8s.io/api/authorization/v1"
2422
autoscalingv2 "k8s.io/api/autoscaling/v2"
2523
rbacv1 "k8s.io/api/rbac/v1"
2624
"k8s.io/apimachinery/pkg/runtime"
@@ -378,7 +376,7 @@ func (c CollectorWebhook) validateTargetAllocatorConfig(ctx context.Context, r *
378376
if subjectAccessReviews, err := c.reviewer.CheckPolicyRules(ctx, r.GetNamespace(), r.Spec.TargetAllocator.ServiceAccount, targetAllocatorCRPolicyRules...); err != nil {
379377
return nil, fmt.Errorf("unable to check rbac rules %w", err)
380378
} else if allowed, deniedReviews := rbac.AllSubjectAccessReviewsAllowed(subjectAccessReviews); !allowed {
381-
return warningsGroupedByResource(deniedReviews), nil
379+
return rbac.WarningsGroupedByResource(deniedReviews), nil
382380
}
383381
}
384382

@@ -426,28 +424,6 @@ func checkAutoscalerSpec(autoscaler *AutoscalerSpec) error {
426424
return nil
427425
}
428426

429-
// warningsGroupedByResource is a helper to take the missing permissions and format them as warnings.
430-
func warningsGroupedByResource(reviews []*v1.SubjectAccessReview) []string {
431-
fullResourceToVerbs := make(map[string][]string)
432-
for _, review := range reviews {
433-
if review.Spec.ResourceAttributes != nil {
434-
key := fmt.Sprintf("%s/%s", review.Spec.ResourceAttributes.Group, review.Spec.ResourceAttributes.Resource)
435-
if len(review.Spec.ResourceAttributes.Group) == 0 {
436-
key = review.Spec.ResourceAttributes.Resource
437-
}
438-
fullResourceToVerbs[key] = append(fullResourceToVerbs[key], review.Spec.ResourceAttributes.Verb)
439-
} else if review.Spec.NonResourceAttributes != nil {
440-
key := fmt.Sprintf("nonResourceURL: %s", review.Spec.NonResourceAttributes.Path)
441-
fullResourceToVerbs[key] = append(fullResourceToVerbs[key], review.Spec.NonResourceAttributes.Verb)
442-
}
443-
}
444-
var warnings []string
445-
for fullResource, verbs := range fullResourceToVerbs {
446-
warnings = append(warnings, fmt.Sprintf("missing the following rules for %s: [%s]", fullResource, strings.Join(verbs, ",")))
447-
}
448-
return warnings
449-
}
450-
451427
func SetupCollectorWebhook(mgr ctrl.Manager, cfg config.Config, reviewer *rbac.Reviewer) error {
452428
cvw := &CollectorWebhook{
453429
reviewer: reviewer,

config/manager/manager.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,10 @@ spec:
5252
requests:
5353
cpu: 100m
5454
memory: 64Mi
55+
env:
56+
- name: SERVICE_ACCOUNT_NAME
57+
valueFrom:
58+
fieldRef:
59+
fieldPath: spec.serviceAccountName
5560
serviceAccountName: controller-manager
5661
terminationGracePeriodSeconds: 10

internal/autodetect/operator.go

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package autodetect
16+
17+
import (
18+
"fmt"
19+
"os"
20+
)
21+
22+
func GetOperatorNamespace() (string, error) {
23+
nsBytes, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace")
24+
if err != nil {
25+
return "", err
26+
}
27+
return string(nsBytes), nil
28+
}
29+
30+
func GetOperatorServiceAccount() (string, error) {
31+
saEnvVar := "SERVICE_ACCOUNT_NAME"
32+
sa := os.Getenv(saEnvVar)
33+
if sa == "" {
34+
return sa, fmt.Errorf("%s env variable not found", saEnvVar)
35+
}
36+
return sa, nil
37+
}

internal/rbac/format.go

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package rbac
16+
17+
import (
18+
"fmt"
19+
"strings"
20+
21+
v1 "k8s.io/api/authorization/v1"
22+
)
23+
24+
// WarningsGroupedByResource is a helper to take the missing permissions and format them as warnings.
25+
func WarningsGroupedByResource(reviews []*v1.SubjectAccessReview) []string {
26+
fullResourceToVerbs := make(map[string][]string)
27+
for _, review := range reviews {
28+
if review.Spec.ResourceAttributes != nil {
29+
key := fmt.Sprintf("%s/%s", review.Spec.ResourceAttributes.Group, review.Spec.ResourceAttributes.Resource)
30+
if len(review.Spec.ResourceAttributes.Group) == 0 {
31+
key = review.Spec.ResourceAttributes.Resource
32+
}
33+
fullResourceToVerbs[key] = append(fullResourceToVerbs[key], review.Spec.ResourceAttributes.Verb)
34+
} else if review.Spec.NonResourceAttributes != nil {
35+
key := fmt.Sprintf("nonResourceURL: %s", review.Spec.NonResourceAttributes.Path)
36+
fullResourceToVerbs[key] = append(fullResourceToVerbs[key], review.Spec.NonResourceAttributes.Verb)
37+
}
38+
}
39+
var warnings []string
40+
for fullResource, verbs := range fullResourceToVerbs {
41+
warnings = append(warnings, fmt.Sprintf("missing the following rules for %s: [%s]", fullResource, strings.Join(verbs, ",")))
42+
}
43+
return warnings
44+
}

main.go

+100-54
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/spf13/pflag"
3030
colfeaturegate "go.opentelemetry.io/collector/featuregate"
3131
networkingv1 "k8s.io/api/networking/v1"
32+
rbacv1 "k8s.io/api/rbac/v1"
3233
k8sruntime "k8s.io/apimachinery/pkg/runtime"
3334
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
3435
"k8s.io/client-go/kubernetes"
@@ -134,7 +135,7 @@ func main() {
134135
pflag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
135136
"Enable leader election for controller manager. "+
136137
"Enabling this will ensure there is only one active controller manager.")
137-
pflag.BoolVar(&createRBACPermissions, "create-rbac-permissions", false, "Automatically create RBAC permissions needed by the processors")
138+
pflag.BoolVar(&createRBACPermissions, "create-rbac-permissions", false, "Automatically create RBAC permissions needed by the processors (deprecated)")
138139
pflag.BoolVar(&enableMultiInstrumentation, "enable-multi-instrumentation", false, "Controls whether the operator supports multi instrumentation")
139140
pflag.BoolVar(&enableApacheHttpdInstrumentation, constants.FlagApacheHttpd, true, "Controls whether the operator supports Apache HTTPD auto-instrumentation")
140141
pflag.BoolVar(&enableDotNetInstrumentation, constants.FlagDotNet, true, "Controls whether the operator supports dotnet auto-instrumentation")
@@ -188,54 +189,6 @@ func main() {
188189

189190
restConfig := ctrl.GetConfigOrDie()
190191

191-
// builds the operator's configuration
192-
ad, err := autodetect.New(restConfig)
193-
if err != nil {
194-
setupLog.Error(err, "failed to setup auto-detect routine")
195-
os.Exit(1)
196-
}
197-
198-
cfg := config.New(
199-
config.WithLogger(ctrl.Log.WithName("config")),
200-
config.WithVersion(v),
201-
config.WithCollectorImage(collectorImage),
202-
config.WithCreateRBACPermissions(createRBACPermissions),
203-
config.WithEnableMultiInstrumentation(enableMultiInstrumentation),
204-
config.WithEnableApacheHttpdInstrumentation(enableApacheHttpdInstrumentation),
205-
config.WithEnableDotNetInstrumentation(enableDotNetInstrumentation),
206-
config.WithEnableNginxInstrumentation(enableNginxInstrumentation),
207-
config.WithEnablePythonInstrumentation(enablePythonInstrumentation),
208-
config.WithTargetAllocatorImage(targetAllocatorImage),
209-
config.WithOperatorOpAMPBridgeImage(operatorOpAMPBridgeImage),
210-
config.WithAutoInstrumentationJavaImage(autoInstrumentationJava),
211-
config.WithAutoInstrumentationNodeJSImage(autoInstrumentationNodeJS),
212-
config.WithAutoInstrumentationPythonImage(autoInstrumentationPython),
213-
config.WithAutoInstrumentationDotNetImage(autoInstrumentationDotNet),
214-
config.WithAutoInstrumentationGoImage(autoInstrumentationGo),
215-
config.WithAutoInstrumentationApacheHttpdImage(autoInstrumentationApacheHttpd),
216-
config.WithAutoInstrumentationNginxImage(autoInstrumentationNginx),
217-
config.WithAutoDetect(ad),
218-
config.WithLabelFilters(labelsFilter),
219-
config.WithAnnotationFilters(annotationsFilter),
220-
)
221-
err = cfg.AutoDetect()
222-
if err != nil {
223-
setupLog.Error(err, "failed to autodetect config variables")
224-
}
225-
// Only add these to the scheme if they are available
226-
if cfg.PrometheusCRAvailability() == prometheus.Available {
227-
setupLog.Info("Prometheus CRDs are installed, adding to scheme.")
228-
utilruntime.Must(monitoringv1.AddToScheme(scheme))
229-
} else {
230-
setupLog.Info("Prometheus CRDs are not installed, skipping adding to scheme.")
231-
}
232-
if cfg.OpenShiftRoutesAvailability() == openshift.RoutesAvailable {
233-
setupLog.Info("Openshift CRDs are installed, adding to scheme.")
234-
utilruntime.Must(routev1.Install(scheme))
235-
} else {
236-
setupLog.Info("Openshift CRDs are not installed, skipping adding to scheme.")
237-
}
238-
239192
var namespaces map[string]cache.Config
240193
watchNamespace, found := os.LookupEnv("WATCH_NAMESPACE")
241194
if found {
@@ -284,17 +237,81 @@ func main() {
284237
os.Exit(1)
285238
}
286239

240+
clientset, clientErr := kubernetes.NewForConfig(mgr.GetConfig())
241+
if err != nil {
242+
setupLog.Error(clientErr, "failed to create kubernetes clientset")
243+
}
244+
245+
reviewer := rbac.NewReviewer(clientset)
246+
createRBACPermissions = true
287247
ctx := ctrl.SetupSignalHandler()
288-
err = addDependencies(ctx, mgr, cfg, v)
248+
w, err := checkRbacPermissions(reviewer, ctx)
289249
if err != nil {
290-
setupLog.Error(err, "failed to add/run bootstrap dependencies to the controller manager")
250+
createRBACPermissions = false
251+
setupLog.Info("the operator has not permissions to create rbac resources", "error", err, "serviceAccount")
252+
}
253+
if w != nil {
254+
createRBACPermissions = false
255+
setupLog.Info("the operator has not permissions to create rbac resources", "permissions", w)
256+
}
257+
258+
if createRBACPermissions {
259+
setupLog.Info("the operator has permissions to create rbac resources")
260+
}
261+
262+
// builds the operator's configuration
263+
ad, err := autodetect.New(restConfig)
264+
if err != nil {
265+
setupLog.Error(err, "failed to setup auto-detect routine")
291266
os.Exit(1)
292267
}
293-
clientset, clientErr := kubernetes.NewForConfig(mgr.GetConfig())
268+
269+
cfg := config.New(
270+
config.WithLogger(ctrl.Log.WithName("config")),
271+
config.WithVersion(v),
272+
config.WithCollectorImage(collectorImage),
273+
config.WithCreateRBACPermissions(createRBACPermissions),
274+
config.WithEnableMultiInstrumentation(enableMultiInstrumentation),
275+
config.WithEnableApacheHttpdInstrumentation(enableApacheHttpdInstrumentation),
276+
config.WithEnableDotNetInstrumentation(enableDotNetInstrumentation),
277+
config.WithEnableNginxInstrumentation(enableNginxInstrumentation),
278+
config.WithEnablePythonInstrumentation(enablePythonInstrumentation),
279+
config.WithTargetAllocatorImage(targetAllocatorImage),
280+
config.WithOperatorOpAMPBridgeImage(operatorOpAMPBridgeImage),
281+
config.WithAutoInstrumentationJavaImage(autoInstrumentationJava),
282+
config.WithAutoInstrumentationNodeJSImage(autoInstrumentationNodeJS),
283+
config.WithAutoInstrumentationPythonImage(autoInstrumentationPython),
284+
config.WithAutoInstrumentationDotNetImage(autoInstrumentationDotNet),
285+
config.WithAutoInstrumentationGoImage(autoInstrumentationGo),
286+
config.WithAutoInstrumentationApacheHttpdImage(autoInstrumentationApacheHttpd),
287+
config.WithAutoInstrumentationNginxImage(autoInstrumentationNginx),
288+
config.WithAutoDetect(ad),
289+
config.WithLabelFilters(labelsFilter),
290+
config.WithAnnotationFilters(annotationsFilter),
291+
)
292+
err = cfg.AutoDetect()
294293
if err != nil {
295-
setupLog.Error(clientErr, "failed to create kubernetes clientset")
294+
setupLog.Error(err, "failed to autodetect config variables")
295+
}
296+
// Only add these to the scheme if they are available
297+
if cfg.PrometheusCRAvailability() == prometheus.Available {
298+
setupLog.Info("Prometheus CRDs are installed, adding to scheme.")
299+
utilruntime.Must(monitoringv1.AddToScheme(scheme))
300+
} else {
301+
setupLog.Info("Prometheus CRDs are not installed, skipping adding to scheme.")
302+
}
303+
if cfg.OpenShiftRoutesAvailability() == openshift.RoutesAvailable {
304+
setupLog.Info("Openshift CRDs are installed, adding to scheme.")
305+
utilruntime.Must(routev1.Install(scheme))
306+
} else {
307+
setupLog.Info("Openshift CRDs are not installed, skipping adding to scheme.")
308+
}
309+
310+
err = addDependencies(ctx, mgr, cfg, v)
311+
if err != nil {
312+
setupLog.Error(err, "failed to add/run bootstrap dependencies to the controller manager")
313+
os.Exit(1)
296314
}
297-
reviewer := rbac.NewReviewer(clientset)
298315

299316
if err = controllers.NewReconciler(controllers.Params{
300317
Client: mgr.GetClient(),
@@ -410,3 +427,32 @@ func tlsConfigSetting(cfg *tls.Config, tlsOpt tlsConfig) {
410427
}
411428
cfg.CipherSuites = cipherSuiteIDs
412429
}
430+
431+
func checkRbacPermissions(reviewer *rbac.Reviewer, ctx context.Context) (admission.Warnings, error) {
432+
notPossibleToCheck := "unable to check rbac rules:"
433+
434+
namespace, err := autodetect.GetOperatorNamespace()
435+
if err != nil {
436+
return nil, fmt.Errorf("%s: %w", notPossibleToCheck, err)
437+
}
438+
439+
serviceAccount, err := autodetect.GetOperatorServiceAccount()
440+
if err != nil {
441+
return nil, fmt.Errorf("%s: %w", notPossibleToCheck, err)
442+
}
443+
444+
rules := []*rbacv1.PolicyRule{
445+
{
446+
APIGroups: []string{"rbac.authorization.k8s.io"},
447+
Resources: []string{"clusterrolebindings", "clusterroles"},
448+
Verbs: []string{"create", "delete", "get", "list", "patch", "update"},
449+
},
450+
}
451+
452+
if subjectAccessReviews, err := reviewer.CheckPolicyRules(ctx, serviceAccount, namespace, rules...); err != nil {
453+
return nil, fmt.Errorf("%s: %w", notPossibleToCheck, err)
454+
} else if allowed, deniedReviews := rbac.AllSubjectAccessReviewsAllowed(subjectAccessReviews); !allowed {
455+
return rbac.WarningsGroupedByResource(deniedReviews), nil
456+
}
457+
return nil, nil
458+
}

0 commit comments

Comments
 (0)