From 4e3aa8c0f1ce0abe961775d00e10fbab2fcace5d Mon Sep 17 00:00:00 2001 From: Jiawei Huang Date: Thu, 6 Jun 2024 08:39:10 -0700 Subject: [PATCH] [release-v1.32] Attach OpenShift SCC to Calico components (#3375) * Attach OpenShift SCC to Calico components This changeset attaches OpenShift SCC to Calico OSS and Enterprise components. The pre-defined `nonroot-v2` SCC is used for most of the components to limit access to cluster resources. Core components use pre-defined `privileged` SCC. Certain components like EgressGateway will use custom defined SCCs. * Render SCC resources when needed * Attach OpenShift nonroot-v2 SCC to Prometheus components This change attaches nonroot-v2 SCC to Tigera prometheus operator, prometheus, and prometheus service cluster roles. It is missed in [1]. [1] https://github.com/tigera/operator/pull/3357 --- api/v1/installation_types.go | 34 +++++- .../apiserver/apiserver_controller.go | 7 +- .../authentication_controller.go | 4 +- .../clusterconnection_controller.go | 5 +- .../compliance/compliance_controller.go | 28 ++--- .../egressgateway/egressgateway_controller.go | 33 +++--- .../installation/core_controller.go | 48 ++++---- .../installation/core_controller_test.go | 6 +- .../installation/windows_controller.go | 40 ++++--- .../intrusiondetection_controller.go | 41 +++---- .../intrusiondetection_controller_test.go | 42 +++---- .../logcollector/logcollector_controller.go | 6 +- .../logstorage/secrets/secret_controller.go | 27 +++-- pkg/controller/manager/manager_controller.go | 4 +- pkg/controller/migration/convert/core.go | 9 +- pkg/controller/monitor/monitor_controller.go | 4 +- .../policyrecommendation_controller.go | 34 +++--- pkg/controller/tiers/tiers_controller.go | 24 ++-- pkg/controller/utils/utils.go | 45 +++---- pkg/render/apiserver.go | 13 +- pkg/render/apiserver_test.go | 32 +++-- .../applicationlayer/applicationlayer.go | 74 +++++------- .../applicationlayer/applicationlayer_test.go | 18 ++- .../security_context_constraints.go | 67 +++++++++++ pkg/render/compliance.go | 91 ++++++++------ pkg/render/compliance_test.go | 64 ++++++++-- pkg/render/csi.go | 44 ++++--- pkg/render/csi_test.go | 20 +++- pkg/render/dex.go | 16 ++- pkg/render/dex_test.go | 25 +++- pkg/render/egressgateway/egressgateway.go | 59 ++++------ .../egressgateway/egressgateway_test.go | 12 +- pkg/render/fluentd.go | 14 ++- pkg/render/fluentd_test.go | 14 +-- pkg/render/guardian.go | 19 ++- pkg/render/guardian_test.go | 28 ++++- pkg/render/intrusion_detection.go | 30 +++-- pkg/render/intrusion_detection_test.go | 38 ++++-- pkg/render/intrusiondetection/dpi/dpi.go | 25 +++- pkg/render/intrusiondetection/dpi/dpi_test.go | 42 +++++-- .../kubecontrollers/kube-controllers.go | 17 ++- .../kubecontrollers/kube-controllers_test.go | 35 ++++-- pkg/render/logstorage.go | 111 +++++++++++------- pkg/render/logstorage/esgateway/esgateway.go | 15 ++- .../logstorage/esgateway/esgateway_test.go | 23 +++- .../esmetrics/elasticsearch_metrics.go | 43 ++++--- .../esmetrics/elasticsearch_metrics_test.go | 24 +++- pkg/render/logstorage/linseed/linseed.go | 21 +++- pkg/render/logstorage/linseed/linseed_test.go | 27 ++++- pkg/render/logstorage_test.go | 110 +++++++++++------ pkg/render/manager.go | 45 ++----- pkg/render/manager_test.go | 14 +-- pkg/render/monitor/monitor.go | 40 ++++++- pkg/render/monitor/monitor_test.go | 42 ++++++- pkg/render/namespaces.go | 9 +- pkg/render/namespaces_test.go | 6 +- pkg/render/node.go | 13 +- pkg/render/node_test.go | 17 ++- pkg/render/packet_capture_api.go | 16 ++- pkg/render/packet_capture_api_test.go | 27 ++++- pkg/render/policyrecommendation.go | 16 ++- pkg/render/policyrecommendation_test.go | 26 +++- pkg/render/testutils/policy.go | 14 +-- pkg/render/tiers/tiers.go | 6 +- pkg/render/tiers/tiers_test.go | 16 +-- pkg/render/typha.go | 7 +- pkg/render/typha_test.go | 6 +- pkg/render/windows.go | 4 +- 68 files changed, 1250 insertions(+), 686 deletions(-) create mode 100644 pkg/render/common/securitycontextconstraints/security_context_constraints.go diff --git a/api/v1/installation_types.go b/api/v1/installation_types.go index a200c0826e..87afe48578 100644 --- a/api/v1/installation_types.go +++ b/api/v1/installation_types.go @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2023-2024 Tigera, Inc. All rights reserved. /* Licensed under the Apache License, Version 2.0 (the "License"); @@ -301,6 +301,38 @@ var ( ProviderTKG Provider = "TKG" ) +func (p Provider) IsNone() bool { + return p == ProviderNone +} + +func (p Provider) IsAKS() bool { + return p == ProviderAKS +} + +func (p Provider) IsDockerEE() bool { + return p == ProviderDockerEE +} + +func (p Provider) IsEKS() bool { + return p == ProviderEKS +} + +func (p Provider) IsGKE() bool { + return p == ProviderGKE +} + +func (p Provider) IsOpenShift() bool { + return p == ProviderOpenShift +} + +func (p Provider) IsRKE2() bool { + return p == ProviderRKE2 +} + +func (p Provider) IsTKG() bool { + return p == ProviderTKG +} + // ProductVariant represents the variant of the product. // // One of: Calico, TigeraSecureEnterprise diff --git a/pkg/controller/apiserver/apiserver_controller.go b/pkg/controller/apiserver/apiserver_controller.go index 28ab11063b..d026151ad2 100644 --- a/pkg/controller/apiserver/apiserver_controller.go +++ b/pkg/controller/apiserver/apiserver_controller.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2020-2024 Tigera, Inc. 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. @@ -33,6 +33,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" + operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/common" "github.com/tigera/operator/pkg/common/validation" @@ -383,7 +384,7 @@ func (r *ReconcileAPIServer) Reconcile(ctx context.Context, request reconcile.Re AmazonCloudIntegration: amazon, TLSKeyPair: tlsSecret, PullSecrets: pullSecrets, - Openshift: r.provider == operatorv1.ProviderOpenShift, + OpenShift: r.provider.IsOpenShift(), TrustedBundle: trustedBundle, UsePSP: r.usePSP, MultiTenant: r.multiTenant, @@ -446,7 +447,7 @@ func (r *ReconcileAPIServer) Reconcile(ctx context.Context, request reconcile.Re packetCaptureApiCfg := &render.PacketCaptureApiConfiguration{ PullSecrets: pullSecrets, - Openshift: r.provider == operatorv1.ProviderOpenShift, + OpenShift: r.provider.IsOpenShift(), Installation: network, KeyValidatorConfig: keyValidatorConfig, ServerCertSecret: packetCaptureCertSecret, diff --git a/pkg/controller/authentication/authentication_controller.go b/pkg/controller/authentication/authentication_controller.go index bdc04f4ae3..7f51c0573c 100644 --- a/pkg/controller/authentication/authentication_controller.go +++ b/pkg/controller/authentication/authentication_controller.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2020-2024 Tigera, Inc. 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. @@ -329,7 +329,7 @@ func (r *ReconcileAuthentication) Reconcile(ctx context.Context, request reconci dexComponentCfg := &render.DexComponentConfiguration{ PullSecrets: pullSecrets, - Openshift: r.provider == oprv1.ProviderOpenShift, + OpenShift: r.provider.IsOpenShift(), Installation: install, DexConfig: dexCfg, ClusterDomain: r.clusterDomain, diff --git a/pkg/controller/clusterconnection/clusterconnection_controller.go b/pkg/controller/clusterconnection/clusterconnection_controller.go index 2202f3f376..5deed0597b 100644 --- a/pkg/controller/clusterconnection/clusterconnection_controller.go +++ b/pkg/controller/clusterconnection/clusterconnection_controller.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2020-2024 Tigera, Inc. 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. @@ -36,6 +36,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" + operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/common" "github.com/tigera/operator/pkg/controller/certificatemanager" @@ -346,7 +347,7 @@ func (r *ReconcileConnection) Reconcile(ctx context.Context, request reconcile.R URL: managementClusterConnection.Spec.ManagementClusterAddr, TunnelCAType: managementClusterConnection.Spec.TLS.CA, PullSecrets: pullSecrets, - Openshift: r.Provider == operatorv1.ProviderOpenShift, + OpenShift: r.Provider.IsOpenShift(), Installation: instl, TunnelSecret: tunnelSecret, TrustedCertBundle: trustedCertBundle, diff --git a/pkg/controller/compliance/compliance_controller.go b/pkg/controller/compliance/compliance_controller.go index 29b0f2bd4a..70e8a7e5c7 100644 --- a/pkg/controller/compliance/compliance_controller.go +++ b/pkg/controller/compliance/compliance_controller.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2020-2024 Tigera, Inc. 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. @@ -18,10 +18,20 @@ import ( "context" "fmt" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" - "github.com/tigera/operator/pkg/render/common/networkpolicy" operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/common" @@ -34,17 +44,8 @@ import ( "github.com/tigera/operator/pkg/render" rcertificatemanagement "github.com/tigera/operator/pkg/render/certificatemanagement" relasticsearch "github.com/tigera/operator/pkg/render/common/elasticsearch" + "github.com/tigera/operator/pkg/render/common/networkpolicy" "github.com/tigera/operator/pkg/tls/certificatemanagement" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/handler" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) const ResourceName = "compliance" @@ -445,7 +446,6 @@ func (r *ReconcileCompliance) Reconcile(ctx context.Context, request reconcile.R namespaceComp := render.NewPassthrough(render.CreateNamespace(render.ComplianceNamespace, network.KubernetesProvider, render.PSSPrivileged)) hasNoLicense := !utils.IsFeatureActive(license, common.ComplianceFeature) - openshift := r.provider == operatorv1.ProviderOpenShift complianceCfg := &render.ComplianceConfiguration{ ESSecrets: esSecrets, TrustedBundle: trustedBundle, @@ -457,7 +457,7 @@ func (r *ReconcileCompliance) Reconcile(ctx context.Context, request reconcile.R ReporterKeyPair: reporterKeyPair.Interface, ESClusterConfig: esClusterConfig, PullSecrets: pullSecrets, - Openshift: openshift, + OpenShift: r.provider.IsOpenShift(), ManagementCluster: managementCluster, ManagementClusterConnection: managementClusterConnection, KeyValidatorConfig: keyValidatorConfig, diff --git a/pkg/controller/egressgateway/egressgateway_controller.go b/pkg/controller/egressgateway/egressgateway_controller.go index e56df7eaff..98281ca00e 100644 --- a/pkg/controller/egressgateway/egressgateway_controller.go +++ b/pkg/controller/egressgateway/egressgateway_controller.go @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2023-2024 Tigera, Inc. 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. @@ -22,18 +22,8 @@ import ( "github.com/go-logr/logr" ocsv1 "github.com/openshift/api/security/v1" - operatorv1 "github.com/tigera/operator/api/v1" - crdv1 "github.com/tigera/operator/pkg/apis/crd.projectcalico.org/v1" - - "github.com/tigera/operator/pkg/components" - "github.com/tigera/operator/pkg/controller/options" - "github.com/tigera/operator/pkg/controller/status" - "github.com/tigera/operator/pkg/controller/utils" - "github.com/tigera/operator/pkg/controller/utils/imageset" - "github.com/tigera/operator/pkg/render" - rmeta "github.com/tigera/operator/pkg/render/common/meta" - "github.com/tigera/operator/pkg/render/egressgateway" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -47,7 +37,17 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - v1 "k8s.io/api/core/v1" + operatorv1 "github.com/tigera/operator/api/v1" + + crdv1 "github.com/tigera/operator/pkg/apis/crd.projectcalico.org/v1" + "github.com/tigera/operator/pkg/components" + "github.com/tigera/operator/pkg/controller/options" + "github.com/tigera/operator/pkg/controller/status" + "github.com/tigera/operator/pkg/controller/utils" + "github.com/tigera/operator/pkg/controller/utils/imageset" + "github.com/tigera/operator/pkg/render" + rmeta "github.com/tigera/operator/pkg/render/common/meta" + "github.com/tigera/operator/pkg/render/egressgateway" ) const ( @@ -163,7 +163,7 @@ func (r *ReconcileEgressGateway) Reconcile(ctx context.Context, request reconcil ch := utils.NewComponentHandler(log, r.client, r.scheme, nil) if len(egws) == 0 { var objects []client.Object - if r.provider == operatorv1.ProviderOpenShift { + if r.provider.IsOpenShift() { objects = append(objects, egressgateway.SecurityContextConstraints()) } if r.usePSP { @@ -205,7 +205,7 @@ func (r *ReconcileEgressGateway) Reconcile(ctx context.Context, request reconcil // In the case of OpenShift, we are using a single SCC. // Whenever a EGW resource is deleted, remove the corresponding user from the SCC // and update the resource. - if r.provider == operatorv1.ProviderOpenShift { + if r.provider.IsOpenShift() { scc, err := getOpenShiftSCC(ctx, r.client) if err != nil { reqLogger.Error(err, "Error querying SecurityContextConstraints") @@ -398,7 +398,6 @@ func (r *ReconcileEgressGateway) reconcileEgressGateway(ctx context.Context, egw } } - openshift := r.provider == operatorv1.ProviderOpenShift config := &egressgateway.Config{ PullSecrets: pullSecrets, Installation: installation, @@ -408,7 +407,7 @@ func (r *ReconcileEgressGateway) reconcileEgressGateway(ctx context.Context, egw VXLANVNI: egwVXLANVNI, IptablesBackend: ipTablesBackend, UsePSP: r.usePSP, - OpenShift: openshift, + OpenShift: r.provider.IsOpenShift(), NamespaceAndNames: namespaceAndNames, } diff --git a/pkg/controller/installation/core_controller.go b/pkg/controller/installation/core_controller.go index 5830cf1638..6552b1d5a1 100644 --- a/pkg/controller/installation/core_controller.go +++ b/pkg/controller/installation/core_controller.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2019-2024 Tigera, Inc. 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. @@ -132,7 +132,7 @@ func Add(mgr manager.Manager, opts options.AddOptions) error { c, err := controller.New("tigera-installation-controller", mgr, controller.Options{Reconciler: ri}) if err != nil { - return fmt.Errorf("Failed to create tigera-installation-controller: %w", err) + return fmt.Errorf("failed to create tigera-installation-controller: %w", err) } // Established deferred watches against the v3 API that should succeed after the Enterprise API Server becomes available. @@ -159,7 +159,7 @@ func Add(mgr manager.Manager, opts options.AddOptions) error { func newReconciler(mgr manager.Manager, opts options.AddOptions) (*ReconcileInstallation, error) { nm, err := migration.NewCoreNamespaceMigration(mgr.GetConfig()) if err != nil { - return nil, fmt.Errorf("Failed to initialize Namespace migration: %w", err) + return nil, fmt.Errorf("failed to initialize Namespace migration: %w", err) } statusManager := status.New(mgr.GetClient(), "calico", opts.KubernetesVersion) @@ -213,7 +213,7 @@ func add(c controller.Controller, r *ReconcileInstallation) error { return fmt.Errorf("tigera-installation-controller failed to watch calico Tigerastatus: %w", err) } - if r.autoDetectedProvider == operator.ProviderOpenShift { + if r.autoDetectedProvider.IsOpenShift() { // Watch for openshift network configuration as well. If we're running in OpenShift, we need to // merge this configuration with our own and the write back the status object. err = c.Watch(&source.Kind{Type: &configv1.Network{}}, &handler.EnqueueRequestForObject{}) @@ -410,7 +410,7 @@ func updateInstallationWithDefaults(ctx context.Context, client client.Client, i err = client.Get(ctx, key, awsNode) if err != nil { if !apierrors.IsNotFound(err) { - return fmt.Errorf("Unable to read aws-node daemonset: %s", err.Error()) + return fmt.Errorf("unable to read aws-node daemonset: %s", err.Error()) } awsNode = nil } @@ -438,7 +438,7 @@ func mergeAndFillDefaults(i *operator.Installation, o *configv1.Network, kubeadm } if awsNode != nil { if err := updateInstallationForAWSNode(i, awsNode); err != nil { - return fmt.Errorf("Could not resolve AWS node configuration: %s", err.Error()) + return fmt.Errorf("could not resolve AWS node configuration: %s", err.Error()) } } @@ -739,15 +739,15 @@ func fillDefaults(instance *operator.Installation) error { // If not specified by the user, set the flex volume plugin location based on platform. if len(instance.Spec.FlexVolumePath) == 0 { - if instance.Spec.KubernetesProvider == operator.ProviderOpenShift { + if instance.Spec.KubernetesProvider.IsOpenShift() { // In OpenShift 4.x, the location for flexvolume plugins has changed. // See: https://bugzilla.redhat.com/show_bug.cgi?id=1667606#c5 instance.Spec.FlexVolumePath = "/etc/kubernetes/kubelet-plugins/volume/exec/" - } else if instance.Spec.KubernetesProvider == operator.ProviderGKE { + } else if instance.Spec.KubernetesProvider.IsGKE() { instance.Spec.FlexVolumePath = "/home/kubernetes/flexvolume/" - } else if instance.Spec.KubernetesProvider == operator.ProviderAKS { + } else if instance.Spec.KubernetesProvider.IsAKS() { instance.Spec.FlexVolumePath = "/etc/kubernetes/volumeplugins/" - } else if instance.Spec.KubernetesProvider == operator.ProviderRKE2 { + } else if instance.Spec.KubernetesProvider.IsRKE2() { instance.Spec.FlexVolumePath = "/var/lib/kubelet/volumeplugins/" } else { instance.Spec.FlexVolumePath = "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/" @@ -777,15 +777,15 @@ func fillDefaults(instance *operator.Installation) error { // and updates the Installation CR accordingly. It returns an error if incompatible values are provided. func mergeProvider(cr *operator.Installation, provider operator.Provider) error { // If we detected one provider but user set provider to something else, throw an error - if provider != operator.ProviderNone && cr.Spec.KubernetesProvider != operator.ProviderNone && cr.Spec.KubernetesProvider != provider { - msg := "Installation spec.kubernetesProvider '%s' does not match auto-detected value '%s'" + if !provider.IsNone() && !cr.Spec.KubernetesProvider.IsNone() && cr.Spec.KubernetesProvider != provider { + msg := "installation spec.kubernetesProvider '%s' does not match auto-detected value '%s'" return fmt.Errorf(msg, cr.Spec.KubernetesProvider, provider) } // If we've reached this point, it means only one source of provider is being used - auto-detection or // user-provided, but not both. Or, it means that both have been specified but are the same. // If it's the CR provided one, then just use that. Otherwise, use the auto-detected one. - if cr.Spec.KubernetesProvider == operator.ProviderNone { + if cr.Spec.KubernetesProvider.IsNone() { cr.Spec.KubernetesProvider = provider } log.WithValues("provider", cr.Spec.KubernetesProvider).V(1).Info("Determined provider") @@ -984,7 +984,7 @@ func (r *ReconcileInstallation) Reconcile(ctx context.Context, request reconcile } else if err != nil { r.status.SetDegraded(operator.InternalServerError, "Error discovering Tigera Secure availability", err, reqLogger) } else { - r.status.SetDegraded(operator.InternalServerError, "Cannot deploy Tigera Secure", fmt.Errorf("Missing Tigera Secure custom resource definitions"), reqLogger) + r.status.SetDegraded(operator.InternalServerError, "Cannot deploy Tigera Secure", fmt.Errorf("missing Tigera Secure custom resource definitions"), reqLogger) } // Queue a retry. We don't want to watch the APIServer API since it might not exist and would cause @@ -1117,7 +1117,7 @@ func (r *ReconcileInstallation) Reconcile(ctx context.Context, request reconcile } openShiftOnAws := false - if instance.Spec.KubernetesProvider == operator.ProviderOpenShift { + if instance.Spec.KubernetesProvider.IsOpenShift() { openShiftOnAws, err = isOpenshiftOnAws(instance, ctx, r.client) if err != nil { r.status.SetDegraded(operator.ResourceReadError, "Error checking if OpenShift is on AWS", err, reqLogger) @@ -1273,7 +1273,7 @@ func (r *ReconcileInstallation) Reconcile(ctx context.Context, request reconcile } } - if instance.Spec.KubernetesProvider == operator.ProviderGKE { + if instance.Spec.KubernetesProvider.IsGKE() { // We do this only for GKE as other providers don't (yet?) // automatically add resource quota that constrains whether // Calico components that are marked cluster or node critical @@ -1364,7 +1364,7 @@ func (r *ReconcileInstallation) Reconcile(ctx context.Context, request reconcile Installation: &instance.Spec, Terminating: terminating, UsePSP: r.usePSP, - OpenShift: instance.Spec.KubernetesProvider == operator.ProviderOpenShift, + OpenShift: instance.Spec.KubernetesProvider.IsOpenShift(), } components = append(components, render.CSI(&csiCfg)) @@ -1465,7 +1465,7 @@ func (r *ReconcileInstallation) Reconcile(ctx context.Context, request reconcile } // We have successfully reconciled the Calico installation. - if instance.Spec.KubernetesProvider == operator.ProviderOpenShift { + if instance.Spec.KubernetesProvider.IsOpenShift() { openshiftConfig := &configv1.Network{} err = r.client.Get(ctx, types.NamespacedName{Name: openshiftNetworkConfig}, openshiftConfig) if err != nil { @@ -1503,7 +1503,7 @@ func (r *ReconcileInstallation) Reconcile(ctx context.Context, request reconcile // Write updated status. if statusMTU > math.MaxInt32 || statusMTU < 0 { - return reconcile.Result{}, errors.New("The MTU size should be between Max int32 (2147483647) and 0") + return reconcile.Result{}, errors.New("the MTU size should be between Max int32 (2147483647) and 0") } instance.Status.MTU = int32(statusMTU) // Variant and CalicoVersion must be updated at the same time. @@ -1656,7 +1656,7 @@ func (r *ReconcileInstallation) setDefaultsOnFelixConfiguration(install *operato // Determine the felix health port to use. Prefer the configuration from FelixConfiguration, // but default to 9099 (or 9199 on OpenShift). We will also write back whatever we select to FelixConfiguration. felixHealthPort := 9099 - if install.Spec.KubernetesProvider == operator.ProviderOpenShift { + if install.Spec.KubernetesProvider.IsOpenShift() { felixHealthPort = 9199 } if fc.Spec.HealthPort == nil { @@ -1727,7 +1727,7 @@ func (r *ReconcileInstallation) checkActive(log logr.Logger) (*corev1.ConfigMap, "my-namespace", common.OperatorNamespace(), "active-namespace", activeNs) osExitOverride(0) - return nil, fmt.Errorf("Returning error for test purposes") + return nil, fmt.Errorf("returning error for test purposes") } if cm == nil { @@ -1762,7 +1762,7 @@ func getConfigMap(client client.Client, cmName string) (*corev1.ConfigMap, error if apierrors.IsNotFound(err) { return nil, nil } - return nil, fmt.Errorf("Failed to read ConfigMap %q: %s", cmName, err) + return nil, fmt.Errorf("failed to read ConfigMap %q: %s", cmName, err) } return cm, nil } @@ -1783,13 +1783,13 @@ func getBirdTemplates(client client.Client) (map[string]string, error) { // by the KubernetesProvider on the installation and the infrastructure OpenShift // status. func isOpenshiftOnAws(install *operator.Installation, ctx context.Context, client client.Client) (bool, error) { - if install.Spec.KubernetesProvider != operator.ProviderOpenShift { + if !install.Spec.KubernetesProvider.IsOpenShift() { return false, nil } infra := configv1.Infrastructure{} // If configured to run in openshift, then also fetch the openshift configuration API. if err := client.Get(ctx, types.NamespacedName{Name: openshiftNetworkConfig}, &infra); err != nil { - return false, fmt.Errorf("Unable to read OpenShift infrastructure configuration: %s", err.Error()) + return false, fmt.Errorf("unable to read OpenShift infrastructure configuration: %s", err.Error()) } return (infra.Status.PlatformStatus.Type == "AWS"), nil } diff --git a/pkg/controller/installation/core_controller_test.go b/pkg/controller/installation/core_controller_test.go index adb0fd858a..aed4dda0ed 100644 --- a/pkg/controller/installation/core_controller_test.go +++ b/pkg/controller/installation/core_controller_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2019-2024 Tigera, Inc. 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. @@ -61,7 +61,7 @@ import ( "github.com/tigera/operator/test" ) -var mismatchedError = fmt.Errorf("Installation spec.kubernetesProvider 'DockerEnterprise' does not match auto-detected value 'OpenShift'") +var errMismatchedError = fmt.Errorf("installation spec.kubernetesProvider 'DockerEnterprise' does not match auto-detected value 'OpenShift'") type fakeNamespaceMigration struct{} @@ -96,7 +96,7 @@ var _ = Describe("Testing core-controller installation", func() { } }, table.Entry("Same detected/configured provider", operator.ProviderOpenShift, operator.ProviderOpenShift, nil), - table.Entry("Different detected/configured provider", operator.ProviderOpenShift, operator.ProviderDockerEE, mismatchedError), + table.Entry("Different detected/configured provider", operator.ProviderOpenShift, operator.ProviderDockerEE, errMismatchedError), table.Entry("Same detected/configured managed provider", operator.ProviderEKS, operator.ProviderEKS, nil), ) diff --git a/pkg/controller/installation/windows_controller.go b/pkg/controller/installation/windows_controller.go index 1570533bb8..bd74aca8b7 100644 --- a/pkg/controller/installation/windows_controller.go +++ b/pkg/controller/installation/windows_controller.go @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2023-2024 Tigera, Inc. 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. @@ -20,40 +20,42 @@ import ( "fmt" "reflect" + corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + configv1 "github.com/openshift/api/config/v1" - "github.com/tigera/operator/pkg/active" - "github.com/tigera/operator/pkg/common" - "github.com/tigera/operator/pkg/dns" - "github.com/tigera/operator/pkg/render" - "github.com/tigera/operator/pkg/render/monitor" - "github.com/tigera/operator/pkg/tls/certificatemanagement" apiv3 "github.com/tigera/api/pkg/apis/projectcalico/v3" + operatorv1 "github.com/tigera/operator/api/v1" + "github.com/tigera/operator/pkg/active" crdv1 "github.com/tigera/operator/pkg/apis/crd.projectcalico.org/v1" + "github.com/tigera/operator/pkg/common" "github.com/tigera/operator/pkg/controller/certificatemanager" "github.com/tigera/operator/pkg/controller/k8sapi" "github.com/tigera/operator/pkg/controller/options" "github.com/tigera/operator/pkg/controller/status" "github.com/tigera/operator/pkg/controller/utils" "github.com/tigera/operator/pkg/controller/utils/imageset" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/handler" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" + "github.com/tigera/operator/pkg/dns" + "github.com/tigera/operator/pkg/render" + "github.com/tigera/operator/pkg/render/monitor" + "github.com/tigera/operator/pkg/tls/certificatemanagement" ) var logw = logf.Log.WithName("controller_windows") @@ -82,7 +84,7 @@ func AddWindowsController(mgr manager.Manager, opts options.AddOptions) error { return fmt.Errorf("tigera-windows-controller failed to watch calico Tigerastatus: %w", err) } - if ri.autoDetectedProvider == operatorv1.ProviderOpenShift { + if ri.autoDetectedProvider.IsOpenShift() { // Watch for openshift network configuration as well. If we're running in OpenShift, we need to // merge this configuration with our own and the write back the status object. err = c.Watch(&source.Kind{Type: &configv1.Network{}}, &handler.EnqueueRequestForObject{}) diff --git a/pkg/controller/intrusiondetection/intrusiondetection_controller.go b/pkg/controller/intrusiondetection/intrusiondetection_controller.go index 20b7689423..828de56e6f 100644 --- a/pkg/controller/intrusiondetection/intrusiondetection_controller.go +++ b/pkg/controller/intrusiondetection/intrusiondetection_controller.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2020-2024 Tigera, Inc. 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. @@ -20,11 +20,26 @@ import ( esv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/elasticsearch/v1" - "github.com/tigera/operator/pkg/render/common/networkpolicy" - + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + storagev1 "k8s.io/api/storage/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" + operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/common" "github.com/tigera/operator/pkg/controller/certificatemanager" @@ -37,23 +52,9 @@ import ( "github.com/tigera/operator/pkg/render" rcertificatemanagement "github.com/tigera/operator/pkg/render/certificatemanagement" relasticsearch "github.com/tigera/operator/pkg/render/common/elasticsearch" + "github.com/tigera/operator/pkg/render/common/networkpolicy" "github.com/tigera/operator/pkg/render/intrusiondetection/dpi" "github.com/tigera/operator/pkg/tls/certificatemanagement" - batchv1 "k8s.io/api/batch/v1" - corev1 "k8s.io/api/core/v1" - storagev1 "k8s.io/api/storage/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/handler" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) const ResourceName = "intrusion-detection" @@ -495,7 +496,7 @@ func (r *ReconcileIntrusionDetection) Reconcile(ctx context.Context, request rec Installation: network, ESClusterConfig: esClusterConfig, PullSecrets: pullSecrets, - Openshift: r.provider == operatorv1.ProviderOpenShift, + OpenShift: r.provider.IsOpenShift(), ClusterDomain: r.clusterDomain, ESLicenseType: esLicenseType, ManagedCluster: isManagedCluster, @@ -541,7 +542,7 @@ func (r *ReconcileIntrusionDetection) Reconcile(ctx context.Context, request rec Installation: network, TyphaNodeTLS: typhaNodeTLS, PullSecrets: pullSecrets, - Openshift: r.provider == operatorv1.ProviderOpenShift, + OpenShift: r.provider.IsOpenShift(), ManagedCluster: isManagedCluster, ManagementCluster: isManagementCluster, HasNoLicense: hasNoLicense, diff --git a/pkg/controller/intrusiondetection/intrusiondetection_controller_test.go b/pkg/controller/intrusiondetection/intrusiondetection_controller_test.go index 41b4e1fc32..fe9c4d6607 100644 --- a/pkg/controller/intrusiondetection/intrusiondetection_controller_test.go +++ b/pkg/controller/intrusiondetection/intrusiondetection_controller_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020, 2022-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2020, 2022-2024 Tigera, Inc. 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. @@ -19,30 +19,11 @@ import ( "fmt" "time" - esv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/elasticsearch/v1" - - "github.com/tigera/operator/pkg/apis" - - "github.com/tigera/operator/pkg/controller/certificatemanager" - rtest "github.com/tigera/operator/pkg/render/common/test" - "k8s.io/apimachinery/pkg/api/resource" - "k8s.io/apimachinery/pkg/types" - - "github.com/tigera/operator/pkg/render/intrusiondetection/dpi" - . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/stretchr/testify/mock" - "github.com/tigera/operator/pkg/common" - "github.com/tigera/operator/pkg/components" - "github.com/tigera/operator/test" - v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" - operatorv1 "github.com/tigera/operator/api/v1" - "github.com/tigera/operator/pkg/controller/status" - "github.com/tigera/operator/pkg/controller/utils" - "github.com/tigera/operator/pkg/render" - relasticsearch "github.com/tigera/operator/pkg/render/common/elasticsearch" + esv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/elasticsearch/v1" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" @@ -55,6 +36,23 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" + + operatorv1 "github.com/tigera/operator/api/v1" + "github.com/tigera/operator/pkg/apis" + "github.com/tigera/operator/pkg/common" + "github.com/tigera/operator/pkg/components" + "github.com/tigera/operator/pkg/controller/certificatemanager" + "github.com/tigera/operator/pkg/controller/status" + "github.com/tigera/operator/pkg/controller/utils" + "github.com/tigera/operator/pkg/render" + relasticsearch "github.com/tigera/operator/pkg/render/common/elasticsearch" + rtest "github.com/tigera/operator/pkg/render/common/test" + "github.com/tigera/operator/pkg/render/intrusiondetection/dpi" + "github.com/tigera/operator/test" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/types" ) var _ = Describe("IntrusionDetection controller tests", func() { @@ -111,6 +109,7 @@ var _ = Describe("IntrusionDetection controller tests", func() { licenseAPIReady: &utils.ReadyFlag{}, dpiAPIReady: &utils.ReadyFlag{}, tierWatchReady: &utils.ReadyFlag{}, + usePSP: false, } // We start off with a 'standard' installation, with nothing special @@ -402,6 +401,7 @@ var _ = Describe("IntrusionDetection controller tests", func() { licenseAPIReady: readyFlag, dpiAPIReady: readyFlag, tierWatchReady: readyFlag, + usePSP: false, } }) diff --git a/pkg/controller/logcollector/logcollector_controller.go b/pkg/controller/logcollector/logcollector_controller.go index 1f62e072a9..e7596644a5 100644 --- a/pkg/controller/logcollector/logcollector_controller.go +++ b/pkg/controller/logcollector/logcollector_controller.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020,2022-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2020,2022-2024 Tigera, Inc. 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. @@ -533,7 +533,7 @@ func (r *ReconcileLogCollector) Reconcile(ctx context.Context, request reconcile var eksConfig *render.EksCloudwatchLogConfig var esClusterConfig *relasticsearch.ClusterConfig var eksLogForwarderKeyPair certificatemanagement.KeyPairInterface - if installation.KubernetesProvider == operatorv1.ProviderEKS { + if installation.KubernetesProvider.IsEKS() { log.Info("Managed kubernetes EKS found, getting necessary credentials and config") if instance.Spec.AdditionalSources != nil { if instance.Spec.AdditionalSources.EksCloudwatchLog != nil { @@ -601,7 +601,7 @@ func (r *ReconcileLogCollector) Reconcile(ctx context.Context, request reconcile TrustedBundle: trustedBundle, } - if installation.KubernetesProvider == operatorv1.ProviderEKS { + if installation.KubernetesProvider.IsEKS() { if instance.Spec.AdditionalSources != nil { if instance.Spec.AdditionalSources.EksCloudwatchLog != nil { certificateComponent.ServiceAccounts = append(certificateComponent.ServiceAccounts, render.EKSLogForwarderName) diff --git a/pkg/controller/logstorage/secrets/secret_controller.go b/pkg/controller/logstorage/secrets/secret_controller.go index 4842e819e2..d8debfbd27 100644 --- a/pkg/controller/logstorage/secrets/secret_controller.go +++ b/pkg/controller/logstorage/secrets/secret_controller.go @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2023-2024 Tigera, Inc. 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. @@ -18,6 +18,18 @@ import ( "context" "fmt" + "github.com/go-logr/logr" + + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/common" @@ -35,17 +47,6 @@ import ( "github.com/tigera/operator/pkg/render/logstorage/linseed" "github.com/tigera/operator/pkg/render/monitor" "github.com/tigera/operator/pkg/tls/certificatemanagement" - - "github.com/go-logr/logr" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/handler" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) var log = logf.Log.WithName("controller_logstorage_secrets") @@ -517,7 +518,7 @@ func (r *SecretSubController) collectUpstreamCerts(log logr.Logger, helper utils } func (r *SecretSubController) isEKSLogForwardingEnabled(install *operatorv1.InstallationSpec) bool { - if install.KubernetesProvider == operatorv1.ProviderEKS { + if install.KubernetesProvider.IsEKS() { instance := &operatorv1.LogCollector{} err := r.client.Get(context.Background(), utils.DefaultTSEEInstanceKey, instance) if err != nil { diff --git a/pkg/controller/manager/manager_controller.go b/pkg/controller/manager/manager_controller.go index 68711509bb..5ff22ad7c1 100644 --- a/pkg/controller/manager/manager_controller.go +++ b/pkg/controller/manager/manager_controller.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2020-2024 Tigera, Inc. 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. @@ -628,7 +628,7 @@ func (r *ReconcileManager) Reconcile(ctx context.Context, request reconcile.Requ TLSKeyPair: tlsSecret, VoltronLinseedKeyPair: linseedVoltronServerCert, PullSecrets: pullSecrets, - Openshift: r.provider == operatorv1.ProviderOpenShift, + OpenShift: r.provider.IsOpenShift(), Installation: installation, ManagementCluster: managementCluster, TunnelServerCert: tunnelServerCert, diff --git a/pkg/controller/migration/convert/core.go b/pkg/controller/migration/convert/core.go index b73658d176..26e0aef10e 100644 --- a/pkg/controller/migration/convert/core.go +++ b/pkg/controller/migration/convert/core.go @@ -1,4 +1,4 @@ -// Copyright (c) 2022-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2022-2024 Tigera, Inc. 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. @@ -21,8 +21,9 @@ import ( "strconv" "strings" - operatorv1 "github.com/tigera/operator/api/v1" corev1 "k8s.io/api/core/v1" + + operatorv1 "github.com/tigera/operator/api/v1" ) var toBeIgnoredAnnotationKeyRegExps []*regexp.Regexp @@ -321,7 +322,7 @@ func removeExpectedAnnotations(existing, ignoreWithValue map[string]string, toBe func handleNodeSelectors(c *components, install *operatorv1.Installation) error { // check calico-node nodeSelectors if c.node.Spec.Template.Spec.Affinity != nil { - if !(install.Spec.KubernetesProvider == operatorv1.ProviderAKS && reflect.DeepEqual(c.node.Spec.Template.Spec.Affinity, &corev1.Affinity{ + if !(install.Spec.KubernetesProvider.IsAKS() && reflect.DeepEqual(c.node.Spec.Template.Spec.Affinity, &corev1.Affinity{ NodeAffinity: &corev1.NodeAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ NodeSelectorTerms: []corev1.NodeSelectorTerm{{ @@ -333,7 +334,7 @@ func handleNodeSelectors(c *components, install *operatorv1.Installation) error }}, }, }, - })) && !(install.Spec.KubernetesProvider == operatorv1.ProviderEKS && reflect.DeepEqual(c.node.Spec.Template.Spec.Affinity, &corev1.Affinity{ + })) && !(install.Spec.KubernetesProvider.IsEKS() && reflect.DeepEqual(c.node.Spec.Template.Spec.Affinity, &corev1.Affinity{ NodeAffinity: &corev1.NodeAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ NodeSelectorTerms: []corev1.NodeSelectorTerm{{ diff --git a/pkg/controller/monitor/monitor_controller.go b/pkg/controller/monitor/monitor_controller.go index 514f6a01f2..40d65115e5 100644 --- a/pkg/controller/monitor/monitor_controller.go +++ b/pkg/controller/monitor/monitor_controller.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2021-2024 Tigera, Inc. 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. @@ -353,7 +353,7 @@ func (r *ReconcileMonitor) Reconcile(ctx context.Context, request reconcile.Requ ClientTLSSecret: clientTLSSecret, ClusterDomain: r.clusterDomain, TrustedCertBundle: trustedBundle, - Openshift: r.provider == operatorv1.ProviderOpenShift, + OpenShift: r.provider.IsOpenShift(), KubeControllerPort: kubeControllersMetricsPort, UsePSP: r.usePSP, } diff --git a/pkg/controller/policyrecommendation/policyrecommendation_controller.go b/pkg/controller/policyrecommendation/policyrecommendation_controller.go index 892cd01554..b2abb6b55b 100644 --- a/pkg/controller/policyrecommendation/policyrecommendation_controller.go +++ b/pkg/controller/policyrecommendation/policyrecommendation_controller.go @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2023-2024 Tigera, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in policy recommendation with the License. @@ -18,36 +18,36 @@ import ( "context" "fmt" - "github.com/tigera/operator/pkg/controller/tenancy" + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" - "github.com/go-logr/logr" v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" - "github.com/tigera/operator/pkg/render/common/networkpolicy" operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/common" "github.com/tigera/operator/pkg/controller/certificatemanager" "github.com/tigera/operator/pkg/controller/options" "github.com/tigera/operator/pkg/controller/status" + "github.com/tigera/operator/pkg/controller/tenancy" "github.com/tigera/operator/pkg/controller/utils" "github.com/tigera/operator/pkg/controller/utils/imageset" "github.com/tigera/operator/pkg/render" rcertificatemanagement "github.com/tigera/operator/pkg/render/certificatemanagement" relasticsearch "github.com/tigera/operator/pkg/render/common/elasticsearch" + "github.com/tigera/operator/pkg/render/common/networkpolicy" "github.com/tigera/operator/pkg/tls/certificatemanagement" - - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/handler" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" ) const ( @@ -340,7 +340,7 @@ func (r *ReconcilePolicyRecommendation) Reconcile(ctx context.Context, request r Installation: installation, ManagedCluster: isManagedCluster, PullSecrets: pullSecrets, - Openshift: r.provider == operatorv1.ProviderOpenShift, + OpenShift: r.provider.IsOpenShift(), UsePSP: r.usePSP, Namespace: helper.InstallNamespace(), } @@ -471,7 +471,7 @@ func (r *ReconcilePolicyRecommendation) createDefaultPolicyRecommendationScope(c prs.ObjectMeta.Name = "default" prs.Spec.NamespaceSpec.RecStatus = "Disabled" prs.Spec.NamespaceSpec.Selector = "!(projectcalico.org/name starts with 'tigera-') && !(projectcalico.org/name starts with 'calico-') && !(projectcalico.org/name starts with 'kube-')" - if r.provider == operatorv1.ProviderOpenShift { + if r.provider.IsOpenShift() { prs.Spec.NamespaceSpec.Selector += " && !(projectcalico.org/name starts with 'openshift-')" } diff --git a/pkg/controller/tiers/tiers_controller.go b/pkg/controller/tiers/tiers_controller.go index 5e08e0c62f..9a5fa761d9 100644 --- a/pkg/controller/tiers/tiers_controller.go +++ b/pkg/controller/tiers/tiers_controller.go @@ -1,4 +1,4 @@ -// Copyright (c) 2022-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2022-2024 Tigera, Inc. 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. @@ -22,15 +22,6 @@ import ( "strings" "github.com/go-logr/logr" - operatorv1 "github.com/tigera/operator/api/v1" - "github.com/tigera/operator/pkg/common" - "github.com/tigera/operator/pkg/controller/options" - "github.com/tigera/operator/pkg/controller/status" - "github.com/tigera/operator/pkg/controller/utils" - "github.com/tigera/operator/pkg/render" - rmeta "github.com/tigera/operator/pkg/render/common/meta" - "github.com/tigera/operator/pkg/render/common/networkpolicy" - "github.com/tigera/operator/pkg/render/tiers" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -43,6 +34,17 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" + + operatorv1 "github.com/tigera/operator/api/v1" + + "github.com/tigera/operator/pkg/common" + "github.com/tigera/operator/pkg/controller/options" + "github.com/tigera/operator/pkg/controller/status" + "github.com/tigera/operator/pkg/controller/utils" + "github.com/tigera/operator/pkg/render" + rmeta "github.com/tigera/operator/pkg/render/common/meta" + "github.com/tigera/operator/pkg/render/common/networkpolicy" + "github.com/tigera/operator/pkg/render/tiers" ) // The Tiers controller reconciles Tiers and NetworkPolicies that are shared across components or do not directly @@ -172,7 +174,7 @@ func (r *ReconcileTiers) Reconcile(ctx context.Context, request reconcile.Reques func (r *ReconcileTiers) prepareTiersConfig(ctx context.Context, reqLogger logr.Logger) (*tiers.Config, *reconcile.Result) { tiersConfig := tiers.Config{ - Openshift: r.provider == operatorv1.ProviderOpenShift, + OpenShift: r.provider.IsOpenShift(), DNSEgressCIDRs: tiers.DNSEgressCIDR{}, } diff --git a/pkg/controller/utils/utils.go b/pkg/controller/utils/utils.go index 2f5c5e4cd7..cbb6324735 100644 --- a/pkg/controller/utils/utils.go +++ b/pkg/controller/utils/utils.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2020-2024 Tigera, Inc. 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. @@ -26,16 +26,16 @@ import ( esv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/elasticsearch/v1" "github.com/go-logr/logr" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" - apierrors "k8s.io/apimachinery/pkg/api/errors" - kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/validation" "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/event" @@ -44,6 +44,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" + operatorv1 "github.com/tigera/operator/api/v1" crdv1 "github.com/tigera/operator/pkg/apis/crd.projectcalico.org/v1" "github.com/tigera/operator/pkg/common" @@ -232,7 +233,7 @@ func AddNamespacedWatch(c controller.Controller, obj client.Object, h handler.Ev func IsAPIServerReady(client client.Client, l logr.Logger) bool { instance, msg, err := GetAPIServer(context.Background(), client) if err != nil { - if kerrors.IsNotFound(err) { + if errors.IsNotFound(err) { l.V(3).Info("APIServer resource does not exist") return false } @@ -251,7 +252,7 @@ func LogStorageExists(ctx context.Context, cli client.Client) (bool, error) { instance := &operatorv1.LogStorage{} err := cli.Get(ctx, DefaultTSEEInstanceKey, instance) if err != nil { - if kerrors.IsNotFound(err) { + if errors.IsNotFound(err) { return false, nil } return false, err @@ -264,7 +265,7 @@ func GetLogCollector(ctx context.Context, cli client.Client) (*operatorv1.LogCol logCollector := &operatorv1.LogCollector{} err := cli.Get(ctx, DefaultTSEEInstanceKey, logCollector) if err != nil { - if kerrors.IsNotFound(err) { + if errors.IsNotFound(err) { return nil, nil } return nil, err @@ -310,22 +311,22 @@ func ValidateCertPair(client client.Client, namespace, certPairSecretName, keyNa if err != nil { // If the reason for the error is not found then that is acceptable // so return valid in that case. - statErr, ok := err.(*kerrors.StatusError) + statErr, ok := err.(*errors.StatusError) if ok && statErr.ErrStatus.Reason == metav1.StatusReasonNotFound { return nil, nil } else { - return nil, fmt.Errorf("Failed to read cert %q from datastore: %s", certPairSecretName, err) + return nil, fmt.Errorf("failed to read cert %q from datastore: %s", certPairSecretName, err) } } if keyName != "" { if val, ok := secret.Data[keyName]; !ok || len(val) == 0 { - return secret, fmt.Errorf("Secret %q does not have a field named %q", certPairSecretName, keyName) + return secret, fmt.Errorf("secret %q does not have a field named %q", certPairSecretName, keyName) } } if val, ok := secret.Data[certName]; !ok || len(val) == 0 { - return secret, fmt.Errorf("Secret %q does not have a field named %q", certPairSecretName, certName) + return secret, fmt.Errorf("secret %q does not have a field named %q", certPairSecretName, certName) } return secret, nil @@ -351,9 +352,9 @@ func GetK8sServiceEndPoint(client client.Client) (*corev1.ConfigMap, error) { func PopulateK8sServiceEndPoint(client client.Client) error { cm, err := GetK8sServiceEndPoint(client) if err != nil { - if !kerrors.IsNotFound(err) { + if !errors.IsNotFound(err) { // If the configmap is unavailable, do not return an error - return fmt.Errorf("Failed to read ConfigMap %q: %s", render.K8sSvcEndpointConfigMapName, err) + return fmt.Errorf("failed to read ConfigMap %q: %s", render.K8sSvcEndpointConfigMapName, err) } } else { k8sapi.Endpoint.Host = cm.Data["KUBERNETES_SERVICE_HOST"] @@ -382,7 +383,7 @@ func GetManagementCluster(ctx context.Context, c client.Client) (*operatorv1.Man err := c.Get(ctx, DefaultTSEEInstanceKey, managementCluster) if err != nil { - if kerrors.IsNotFound(err) { + if errors.IsNotFound(err) { return nil, nil } return nil, err @@ -397,7 +398,7 @@ func GetManagementClusterConnection(ctx context.Context, c client.Client) (*oper err := c.Get(ctx, DefaultTSEEInstanceKey, managementClusterConnection) if err != nil { - if kerrors.IsNotFound(err) { + if errors.IsNotFound(err) { return nil, nil } return nil, err @@ -444,7 +445,7 @@ func GetTenant(ctx context.Context, mt bool, cli client.Client, ns string) (*ope } if instance.Spec.ID == "" { - return nil, "", fmt.Errorf("Tenant %s/%s has no ID specified", ns, instance.Name) + return nil, "", fmt.Errorf("tenant %s/%s has no ID specified", ns, instance.Name) } return instance, instance.Spec.ID, nil } @@ -491,7 +492,7 @@ func GetInstallation(ctx context.Context, client client.Client) (operatorv1.Prod // update Installation with 'overlay' overlay := operatorv1.Installation{} if err := client.Get(ctx, OverlayInstanceKey, &overlay); err != nil { - if !apierrors.IsNotFound(err) { + if !errors.IsNotFound(err) { return instance.Status.Variant, nil, err } } else { @@ -522,7 +523,7 @@ func GetAPIServer(ctx context.Context, client client.Client) (*operatorv1.APISer if err == nil { return nil, "Duplicate configuration detected", - fmt.Errorf("Multiple APIServer CRs provided. To fix, run \"kubectl delete apiserver tigera-secure\"") + fmt.Errorf("multiple APIServer CRs provided. To fix, run \"kubectl delete apiserver tigera-secure\"") } } return instance, "", nil @@ -537,7 +538,7 @@ func GetElasticLicenseType(ctx context.Context, cli client.Client, logger logr.L } license, ok := cm.Data["eck_license_level"] if !ok { - return render.ElasticsearchLicenseTypeUnknown, fmt.Errorf("eck_license_level not available.") + return render.ElasticsearchLicenseTypeUnknown, fmt.Errorf("eck_license_level not available") } return StrToElasticLicenseType(license, logger), nil @@ -703,7 +704,7 @@ func GetKubeControllerMetricsPort(ctx context.Context, client client.Client) (in // Query the KubeControllersConfiguration object. We'll use this to help configure kube-controllers metric port. err := client.Get(ctx, types.NamespacedName{Name: "default"}, kubeControllersConfig) - if err != nil && !apierrors.IsNotFound(err) { + if err != nil && !errors.IsNotFound(err) { return 0, err } @@ -756,7 +757,7 @@ func GetDNSServiceIPs(ctx context.Context, client client.Client, provider operat // Default kubernetes dns service is named "kube-dns", but RKE2 is using a different name for the default // dns service i.e. "rke2-coredns-rke2-coredns". dnsServiceName := "kube-dns" - if provider == operatorv1.ProviderRKE2 { + if provider.IsRKE2() { dnsServiceName = "rke2-coredns-rke2-coredns" } @@ -774,9 +775,9 @@ func GetDNSServiceIPs(ctx context.Context, client client.Client, provider operat // This is "kube-dns" for most providers, but varies on OpenShift and RKE2. func GetDNSServiceName(provider operatorv1.Provider) types.NamespacedName { kubeDNSServiceName := types.NamespacedName{Name: "kube-dns", Namespace: "kube-system"} - if provider == operatorv1.ProviderOpenShift { + if provider.IsOpenShift() { kubeDNSServiceName = types.NamespacedName{Name: "dns-default", Namespace: "openshift-dns"} - } else if provider == operatorv1.ProviderRKE2 { + } else if provider.IsRKE2() { kubeDNSServiceName = types.NamespacedName{Name: "rke2-coredns-rke2-coredns", Namespace: "kube-system"} } return kubeDNSServiceName diff --git a/pkg/render/apiserver.go b/pkg/render/apiserver.go index 4866f7016e..eefea0091f 100644 --- a/pkg/render/apiserver.go +++ b/pkg/render/apiserver.go @@ -40,6 +40,7 @@ import ( "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" "github.com/tigera/operator/pkg/tls/certificatemanagement" ) @@ -116,7 +117,7 @@ type APIServerConfiguration struct { AmazonCloudIntegration *operatorv1.AmazonCloudIntegration TLSKeyPair certificatemanagement.KeyPairInterface PullSecrets []*corev1.Secret - Openshift bool + OpenShift bool TrustedBundle certificatemanagement.TrustedBundle MultiTenant bool @@ -420,7 +421,7 @@ func (c *apiServerComponent) apiServerServiceAccount() *corev1.ServiceAccount { func allowTigeraAPIServerPolicy(cfg *APIServerConfiguration) *v3.NetworkPolicy { egressRules := []v3.Rule{} - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.Openshift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.OpenShift) egressRules = append(egressRules, []v3.Rule{ { Action: v3.Allow, @@ -641,12 +642,12 @@ func (c *apiServerComponent) authClusterRole() (client.Object, client.Object) { }, } - if c.cfg.Installation.KubernetesProvider == operatorv1.ProviderOpenShift { + if c.cfg.OpenShift { rules = append(rules, rbacv1.PolicyRule{ APIGroups: []string{"security.openshift.io"}, Resources: []string{"securitycontextconstraints"}, Verbs: []string{"use"}, - ResourceNames: []string{PSSPrivileged}, + ResourceNames: []string{securitycontextconstraints.Privileged}, }) } @@ -1023,7 +1024,7 @@ func (c *apiServerComponent) apiServerDeployment() *appsv1.Deployment { func (c *apiServerComponent) hostNetwork() bool { hostNetwork := c.cfg.ForceHostNetwork - if (c.cfg.Installation.KubernetesProvider == operatorv1.ProviderEKS || c.cfg.Installation.KubernetesProvider == operatorv1.ProviderTKG) && + if (c.cfg.Installation.KubernetesProvider.IsEKS() || c.cfg.Installation.KubernetesProvider.IsTKG()) && c.cfg.Installation.CNI != nil && c.cfg.Installation.CNI.Type == operatorv1.PluginCalico { // Workaround the fact that webhooks don't work for non-host-networked pods @@ -1094,7 +1095,7 @@ func (c *apiServerComponent) apiServerContainer() corev1.Container { // In case of OpenShift, apiserver needs privileged access to write audit logs to host path volume. // Audit logs are owned by root on hosts so we need to be root user and group. Audit logs are supported only in Enterprise version. if c.cfg.Installation.Variant == operatorv1.TigeraSecureEnterprise { - apiServer.SecurityContext = securitycontext.NewRootContext(c.cfg.Openshift) + apiServer.SecurityContext = securitycontext.NewRootContext(c.cfg.OpenShift) } else { apiServer.SecurityContext = securitycontext.NewNonRootContext() } diff --git a/pkg/render/apiserver_test.go b/pkg/render/apiserver_test.go index fa3f317b42..beb92204f0 100644 --- a/pkg/render/apiserver_test.go +++ b/pkg/render/apiserver_test.go @@ -98,7 +98,7 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() { K8SServiceEndpoint: k8sapi.ServiceEndpoint{}, Installation: instance, APIServer: apiserver, - Openshift: openshift, + OpenShift: true, TLSKeyPair: kp, TrustedBundle: trustedBundle, UsePSP: true, @@ -146,7 +146,6 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() { kp, err := certificateManager.GetOrCreateKeyPair(cli, render.ProjectCalicoAPIServerTLSSecretName(instance.Variant), common.OperatorNamespace(), dnsNames) Expect(err).NotTo(HaveOccurred()) cfg.TLSKeyPair = kp - // APIServer(registry string, tlsKeyPair *corev1.Secret, pullSecrets []*corev1.Secret, openshift bool component, err := render.APIServer(cfg) Expect(err).To(BeNil(), "Expected APIServer to create successfully %s", err) Expect(component.ResolveImages(nil)).To(BeNil()) @@ -361,6 +360,23 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() { } }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift + cfg.Installation.Variant = operatorv1.TigeraSecureEnterprise + component, err := render.APIServer(cfg) + Expect(err).NotTo(HaveOccurred()) + Expect(component.ResolveImages(nil)).To(BeNil()) + resources, _ := component.Objects() + + role := rtest.GetResource(resources, "tigera-extension-apiserver-auth-access", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"privileged"}, + })) + }) + It("should render the env variable for queryserver when FIPS is enabled", func() { fipsEnabled := operatorv1.FIPSModeEnabled cfg.Installation.FIPSMode = &fipsEnabled @@ -954,7 +970,7 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() { DescribeTable("should render allow-tigera policy", func(scenario testutils.AllowTigeraScenario) { - cfg.Openshift = scenario.Openshift + cfg.OpenShift = scenario.OpenShift if scenario.ManagedCluster { cfg.ManagementClusterConnection = &operatorv1.ManagementClusterConnection{} } else { @@ -968,10 +984,10 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() { expectedPolicy := testutils.SelectPolicyByProvider(scenario, apiServerPolicy, apiServerPolicyForOCP) Expect(policy).To(Equal(expectedPolicy)) }, - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), - Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: false}), - Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), + Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: false}), + Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: true}), ) }) @@ -1532,7 +1548,7 @@ var _ = Describe("API server rendering tests (Calico)", func() { K8SServiceEndpoint: k8sapi.ServiceEndpoint{}, Installation: instance, APIServer: apiserver, - Openshift: openshift, + OpenShift: true, TLSKeyPair: kp, UsePSP: true, } diff --git a/pkg/render/applicationlayer/applicationlayer.go b/pkg/render/applicationlayer/applicationlayer.go index 90ab36a9b0..942c9b11bc 100644 --- a/pkg/render/applicationlayer/applicationlayer.go +++ b/pkg/render/applicationlayer/applicationlayer.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2021-2024 Tigera, Inc. 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. @@ -24,8 +24,6 @@ import ( "strings" "text/template" - ocsv1 "github.com/openshift/api/security/v1" - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" policyv1beta1 "k8s.io/api/policy/v1beta1" @@ -43,6 +41,7 @@ import ( "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" ) const ( @@ -171,21 +170,15 @@ func (c *component) Objects() ([]client.Object, []client.Object) { // Envoy & Dikastes Daemonset objs = append(objs, c.daemonset()) - if c.config.Installation.KubernetesProvider == operatorv1.ProviderDockerEE { + if c.config.Installation.KubernetesProvider.IsDockerEE() { objs = append(objs, c.clusterAdminClusterRoleBinding()) } - // If we're running on openshift, we need to add in an SCC. - if c.config.Installation.KubernetesProvider == operatorv1.ProviderOpenShift { - objs = append(objs, c.securityContextConstraints()) - } - - if c.config.UsePSP { - objs = append(objs, - c.role(), - c.roleBinding(), - c.podSecurityPolicy(), - ) + if c.config.UsePSP || c.config.Installation.KubernetesProvider.IsOpenShift() { + objs = append(objs, c.role(), c.roleBinding()) + if c.config.UsePSP { + objs = append(objs, c.podSecurityPolicy()) + } } return objs, nil @@ -532,20 +525,32 @@ func (c *component) clusterAdminClusterRoleBinding() *rbacv1.ClusterRoleBinding } func (c *component) role() *rbacv1.Role { + var rules []rbacv1.PolicyRule + if c.config.UsePSP { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"policy"}, + Resources: []string{"podsecuritypolicies"}, + Verbs: []string{"use"}, + ResourceNames: []string{PodSecurityPolicyName}, + }) + } + + if c.config.Installation.KubernetesProvider.IsOpenShift() { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.Privileged}, + }) + } + return &rbacv1.Role{ TypeMeta: metav1.TypeMeta{Kind: "Role", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{ Name: RoleName, Namespace: common.CalicoNamespace, }, - Rules: []rbacv1.PolicyRule{ - { - APIGroups: []string{"policy"}, - Resources: []string{"podsecuritypolicies"}, - Verbs: []string{"use"}, - ResourceNames: []string{PodSecurityPolicyName}, - }, - }, + Rules: rules, } } @@ -586,26 +591,3 @@ func (c *component) podSecurityPolicy() *policyv1beta1.PodSecurityPolicy { psp.Spec.Volumes = append(psp.Spec.Volumes, policyv1beta1.CSI, policyv1beta1.FlexVolume) return psp } - -// securityContextConstraints returns SCC needed for daemonset to run on Openshift. -func (c *component) securityContextConstraints() *ocsv1.SecurityContextConstraints { - privilegeEscalation := false - return &ocsv1.SecurityContextConstraints{ - TypeMeta: metav1.TypeMeta{Kind: "SecurityContextConstraints", APIVersion: "security.openshift.io/v1"}, - ObjectMeta: metav1.ObjectMeta{Name: common.CalicoNamespace}, - AllowHostDirVolumePlugin: false, - AllowHostIPC: true, - AllowHostNetwork: true, - AllowHostPID: false, - AllowHostPorts: false, - AllowPrivilegeEscalation: &privilegeEscalation, - AllowPrivilegedContainer: false, - FSGroup: ocsv1.FSGroupStrategyOptions{Type: ocsv1.FSGroupStrategyRunAsAny}, - RunAsUser: ocsv1.RunAsUserStrategyOptions{Type: ocsv1.RunAsUserStrategyRunAsAny}, - ReadOnlyRootFilesystem: true, - SELinuxContext: ocsv1.SELinuxContextStrategyOptions{Type: ocsv1.SELinuxStrategyMustRunAs}, - SupplementalGroups: ocsv1.SupplementalGroupsStrategyOptions{Type: ocsv1.SupplementalGroupsStrategyRunAsAny}, - Users: []string{fmt.Sprintf("system:serviceaccount:%s:%s", common.CalicoNamespace, APLName)}, - Volumes: []ocsv1.FSType{"*"}, - } -} diff --git a/pkg/render/applicationlayer/applicationlayer_test.go b/pkg/render/applicationlayer/applicationlayer_test.go index af2ebee573..4f73977d83 100644 --- a/pkg/render/applicationlayer/applicationlayer_test.go +++ b/pkg/render/applicationlayer/applicationlayer_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2021-2024 Tigera, Inc. 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. @@ -20,6 +20,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/common" @@ -214,6 +215,21 @@ var _ = Describe("Tigera Secure Application Layer rendering tests", func() { } }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift + component := applicationlayer.ApplicationLayer(cfg) + Expect(component.ResolveImages(nil)).To(BeNil()) + resources, _ := component.Objects() + + role := rtest.GetResource(resources, "application-layer", "calico-system", "rbac.authorization.k8s.io", "v1", "Role").(*rbacv1.Role) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"privileged"}, + })) + }) + It("should render with custom l7 collector configuration", func() { // create component with render the correct resources. // Should render the correct resources. diff --git a/pkg/render/common/securitycontextconstraints/security_context_constraints.go b/pkg/render/common/securitycontextconstraints/security_context_constraints.go new file mode 100644 index 0000000000..6c631815fb --- /dev/null +++ b/pkg/render/common/securitycontextconstraints/security_context_constraints.go @@ -0,0 +1,67 @@ +// Copyright (c) 2024 Tigera, Inc. 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 securitycontextconstraints + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + ocsv1 "github.com/openshift/api/security/v1" + + "github.com/tigera/operator/pkg/ptr" +) + +// Default OpenShift security context constraints (SCCs) defined in +// https://docs.openshift.com/container-platform/4.14/authentication/managing-security-context-constraints.html#default-sccs_configuring-internal-oauth +const ( + HostAccess = "hostaccess" + HostNetworkV2 = "hostnetwork-v2" + NonRootV2 = "nonroot-v2" + Privileged = "privileged" +) + +// NewNonRootSecurityContextConstraints is translated from the default security context constraints nonroot-v2. +func NewNonRootSecurityContextConstraints(name string, users []string) *ocsv1.SecurityContextConstraints { + return &ocsv1.SecurityContextConstraints{ + TypeMeta: metav1.TypeMeta{Kind: "SecurityContextConstraints", APIVersion: "security.openshift.io/v1"}, + ObjectMeta: metav1.ObjectMeta{Name: name}, + + AllowHostDirVolumePlugin: false, + AllowHostIPC: false, + AllowHostNetwork: false, + AllowHostPID: false, + AllowHostPorts: false, + AllowPrivilegeEscalation: ptr.BoolToPtr(false), + AllowPrivilegedContainer: false, + FSGroup: ocsv1.FSGroupStrategyOptions{Type: ocsv1.FSGroupStrategyRunAsAny}, + ReadOnlyRootFilesystem: false, + RequiredDropCapabilities: []corev1.Capability{"ALL"}, + RunAsUser: ocsv1.RunAsUserStrategyOptions{Type: ocsv1.RunAsUserStrategyMustRunAsNonRoot}, + SELinuxContext: ocsv1.SELinuxContextStrategyOptions{Type: ocsv1.SELinuxStrategyMustRunAs}, + SeccompProfiles: []string{"runtime/default"}, + SupplementalGroups: ocsv1.SupplementalGroupsStrategyOptions{Type: ocsv1.SupplementalGroupsStrategyRunAsAny}, + Users: users, + Volumes: []ocsv1.FSType{ + ocsv1.FSProjected, + ocsv1.FSTypeCSI, + ocsv1.FSTypeConfigMap, + ocsv1.FSTypeDownwardAPI, + ocsv1.FSTypeEmptyDir, + ocsv1.FSTypeEphemeral, + ocsv1.FSTypePersistentVolumeClaim, + ocsv1.FSTypeSecret, + }, + } +} diff --git a/pkg/render/compliance.go b/pkg/render/compliance.go index ccebf98771..2dc9b285fd 100644 --- a/pkg/render/compliance.go +++ b/pkg/render/compliance.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2019-2024 Tigera, Inc. 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. @@ -19,8 +19,6 @@ import ( "fmt" "strings" - ocsv1 "github.com/openshift/api/security/v1" - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" policyv1beta1 "k8s.io/api/policy/v1beta1" @@ -30,6 +28,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" + operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/components" "github.com/tigera/operator/pkg/render/common/authentication" @@ -40,6 +39,7 @@ import ( "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" "github.com/tigera/operator/pkg/tls/certificatemanagement" "github.com/tigera/operator/pkg/tls/certkeyusage" ) @@ -107,7 +107,7 @@ type ComplianceConfiguration struct { Installation *operatorv1.InstallationSpec ESClusterConfig *relasticsearch.ClusterConfig PullSecrets []*corev1.Secret - Openshift bool + OpenShift bool ManagementCluster *operatorv1.ManagementCluster ManagementClusterConnection *operatorv1.ManagementClusterConnection KeyValidatorConfig authentication.KeyValidatorConfig @@ -241,10 +241,6 @@ func (c *complianceComponent) Objects() ([]client.Object, []client.Object) { ) } - if c.cfg.Openshift { - complianceObjs = append(complianceObjs, c.complianceBenchmarkerSecurityContextConstraints()) - } - if c.cfg.UsePSP { complianceObjs = append(complianceObjs, c.complianceBenchmarkerPodSecurityPolicy(), @@ -257,7 +253,7 @@ func (c *complianceComponent) Objects() ([]client.Object, []client.Object) { // Need to grant cluster admin permissions in DockerEE to the controller since a pod starting pods with // host path volumes requires cluster admin permissions. - if c.cfg.Installation.KubernetesProvider == operatorv1.ProviderDockerEE { + if c.cfg.Installation.KubernetesProvider.IsDockerEE() { complianceObjs = append(complianceObjs, c.complianceControllerClusterAdminClusterRoleBinding()) } @@ -275,7 +271,6 @@ func (c *complianceComponent) Ready() bool { } var ( - complianceBoolTrue = true complianceReplicas int32 = 1 ) @@ -312,6 +307,15 @@ func (c *complianceComponent) complianceControllerRole() *rbacv1.Role { }) } + if c.cfg.OpenShift { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, + }) + } + return &rbacv1.Role{ TypeMeta: metav1.TypeMeta{Kind: "Role", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{Name: ComplianceControllerServiceAccount, Namespace: ComplianceNamespace}, @@ -542,6 +546,14 @@ func (c *complianceComponent) complianceReporterClusterRole() *rbacv1.ClusterRol ResourceNames: []string{"compliance-reporter"}, }) } + if c.cfg.OpenShift { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.HostAccess}, + }) + } return &rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{Name: ComplianceReporterServiceAccount}, @@ -668,7 +680,7 @@ func (c *complianceComponent) complianceReporterPodTemplate() *corev1.PodTemplat }, // On OpenShift reporter needs privileged access to write compliance reports to host path volume - SecurityContext: securitycontext.NewRootContext(c.cfg.Openshift), + SecurityContext: securitycontext.NewRootContext(c.cfg.OpenShift), VolumeMounts: volumeMounts, }, }, @@ -755,6 +767,14 @@ func (c *complianceComponent) complianceServerClusterRole() *rbacv1.ClusterRole }) } + if c.cfg.OpenShift { + clusterRole.Rules = append(clusterRole.Rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, + }) + } return clusterRole } @@ -970,6 +990,14 @@ func (c *complianceComponent) complianceSnapshotterClusterRole() *rbacv1.Cluster ResourceNames: []string{ComplianceSnapshotterName}, }) } + if c.cfg.OpenShift { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, + }) + } return &rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{Name: ComplianceSnapshotterServiceAccount}, @@ -1129,6 +1157,18 @@ func (c *complianceComponent) complianceBenchmarkerClusterRole() *rbacv1.Cluster ResourceNames: []string{"compliance-benchmarker"}, }) } + + if c.cfg.OpenShift { + rules = append(rules, + rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.HostAccess}, + }, + ) + } + return &rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{Name: ComplianceBenchmarkerServiceAccount}, @@ -1206,7 +1246,7 @@ func (c *complianceComponent) complianceBenchmarkerDaemonSet() *appsv1.DaemonSet } // benchmarker needs an extra host path volume mount for GKE for CIS benchmarks - if c.cfg.Installation.KubernetesProvider == operatorv1.ProviderGKE { + if c.cfg.Installation.KubernetesProvider.IsGKE() { volMounts = append(volMounts, corev1.VolumeMount{Name: "home-kubernetes", MountPath: "/home/kubernetes", ReadOnly: true}) vols = append(vols, corev1.Volume{ @@ -1286,29 +1326,6 @@ func (c *complianceComponent) complianceBenchmarkerDaemonSet() *appsv1.DaemonSet } } -func (c *complianceComponent) complianceBenchmarkerSecurityContextConstraints() *ocsv1.SecurityContextConstraints { - return &ocsv1.SecurityContextConstraints{ - TypeMeta: metav1.TypeMeta{Kind: "SecurityContextConstraints", APIVersion: "security.openshift.io/v1"}, - ObjectMeta: metav1.ObjectMeta{Name: ComplianceBenchmarkerServiceAccount}, - AllowHostDirVolumePlugin: true, - AllowHostIPC: false, - AllowHostNetwork: false, - AllowHostPID: true, - AllowHostPorts: false, - AllowPrivilegeEscalation: &complianceBoolTrue, - AllowPrivilegedContainer: true, - FSGroup: ocsv1.FSGroupStrategyOptions{Type: ocsv1.FSGroupStrategyRunAsAny}, - RunAsUser: ocsv1.RunAsUserStrategyOptions{Type: ocsv1.RunAsUserStrategyRunAsAny}, - ReadOnlyRootFilesystem: false, - SELinuxContext: ocsv1.SELinuxContextStrategyOptions{Type: ocsv1.SELinuxStrategyMustRunAs}, - SupplementalGroups: ocsv1.SupplementalGroupsStrategyOptions{Type: ocsv1.SupplementalGroupsStrategyRunAsAny}, - Users: []string{ - fmt.Sprintf("system:serviceaccount:%s:tigera-compliance-benchmarker", ComplianceNamespace), - }, - Volumes: []ocsv1.FSType{"*"}, - } -} - func (c *complianceComponent) complianceBenchmarkerPodSecurityPolicy() *policyv1beta1.PodSecurityPolicy { psp := podsecuritypolicy.NewBasePolicy("compliance-benchmarker") psp.Spec.Volumes = append(psp.Spec.Volumes, policyv1beta1.HostPath) @@ -1627,7 +1644,7 @@ func (c *complianceComponent) complianceAccessAllowTigeraNetworkPolicy() *v3.Net }, } - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, c.cfg.Openshift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, c.cfg.OpenShift) if c.cfg.ManagementClusterConnection == nil { egressRules = append(egressRules, v3.Rule{ @@ -1684,7 +1701,7 @@ func (c *complianceComponent) complianceServerAllowTigeraNetworkPolicy() *v3.Net }, } - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, c.cfg.Openshift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, c.cfg.OpenShift) egressRules = append(egressRules, []v3.Rule{ { diff --git a/pkg/render/compliance_test.go b/pkg/render/compliance_test.go index 42f808c42e..00c13ec68b 100644 --- a/pkg/render/compliance_test.go +++ b/pkg/render/compliance_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2019-2024 Tigera, Inc. 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. @@ -30,6 +30,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" + operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/apis" "github.com/tigera/operator/pkg/common" @@ -88,7 +89,7 @@ var _ = Describe("compliance rendering tests", func() { BenchmarkerKeyPair: benchmarkerKP, SnapshotterKeyPair: snapshotterKP, ESClusterConfig: relasticsearch.NewClusterConfig("cluster", 1, 1, 1), - Openshift: notOpenshift, + OpenShift: false, ClusterDomain: clusterDomain, TrustedBundle: bundle, UsePSP: true, @@ -108,6 +109,55 @@ var _ = Describe("compliance rendering tests", func() { } }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift + cfg.OpenShift = true + component, err := render.Compliance(cfg) + Expect(err).NotTo(HaveOccurred()) + Expect(component.ResolveImages(nil)).To(BeNil()) + resources, _ := component.Objects() + + clusterRole := rtest.GetResource(resources, "tigera-compliance-benchmarker", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(clusterRole.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"hostaccess"}, + })) + + role := rtest.GetResource(resources, "tigera-compliance-controller", "tigera-compliance", "rbac.authorization.k8s.io", "v1", "Role").(*rbacv1.Role) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"nonroot-v2"}, + })) + + clusterRole = rtest.GetResource(resources, "tigera-compliance-reporter", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(clusterRole.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"hostaccess"}, + })) + + clusterRole = rtest.GetResource(resources, "tigera-compliance-server", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(clusterRole.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"nonroot-v2"}, + })) + + clusterRole = rtest.GetResource(resources, "tigera-compliance-snapshotter", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(clusterRole.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"nonroot-v2"}, + })) + }) + It("should render the env variable for queryserver when FIPS is enabled", func() { fipsEnabled := operatorv1.FIPSModeEnabled cfg.Installation.FIPSMode = &fipsEnabled @@ -701,7 +751,7 @@ var _ = Describe("compliance rendering tests", func() { DescribeTable("should render allow-tigera policy", func(scenario testutils.AllowTigeraScenario) { - cfg.Openshift = scenario.Openshift + cfg.OpenShift = scenario.OpenShift if scenario.ManagedCluster { cfg.ManagementClusterConnection = &operatorv1.ManagementClusterConnection{} } else { @@ -717,10 +767,10 @@ var _ = Describe("compliance rendering tests", func() { Expect(policy).To(Equal(expectedPolicy)) } }, - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), - Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: false}), - Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), + Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: false}), + Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: true}), ) }) }) diff --git a/pkg/render/csi.go b/pkg/render/csi.go index 2406ba07a5..b689079ecc 100644 --- a/pkg/render/csi.go +++ b/pkg/render/csi.go @@ -1,4 +1,4 @@ -// Copyright (c) 2022-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2022-2024 Tigera, Inc. 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. @@ -33,6 +33,7 @@ import ( rmeta "github.com/tigera/operator/pkg/render/common/meta" "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" ) const ( @@ -314,20 +315,30 @@ func (c *csiComponent) podSecurityPolicy() *policyv1beta1.PodSecurityPolicy { } func (c *csiComponent) role() *rbacv1.Role { + var rules []rbacv1.PolicyRule + if c.cfg.UsePSP { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"policy"}, + Resources: []string{"podsecuritypolicies"}, + Verbs: []string{"use"}, + ResourceNames: []string{CSIDaemonSetName}, + }) + } + if c.cfg.OpenShift { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.Privileged}, + }) + } return &rbacv1.Role{ TypeMeta: metav1.TypeMeta{Kind: "Role", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{ Name: CSIDaemonSetName, Namespace: CSIDaemonSetNamespace, }, - Rules: []rbacv1.PolicyRule{ - { - APIGroups: []string{"policy"}, - Resources: []string{"podsecuritypolicies"}, - Verbs: []string{"use"}, - ResourceNames: []string{CSIDaemonSetName}, - }, - }, + Rules: rules, } } @@ -389,16 +400,11 @@ func (c *csiComponent) ResolveImages(is *operatorv1.ImageSet) error { func (c *csiComponent) Objects() (objsToCreate, objsToDelete []client.Object) { objs := []client.Object{c.csiDriver(), c.csiDaemonset()} - // create PSP and corresponding clusterrole if it allows, clusterroles are currently - // only for attaching the PSP to CSI's DaemonSet, do not render them if the PSPs - // are also not rendered - if c.cfg.UsePSP { - objs = append(objs, - c.serviceAccount(), - c.role(), - c.roleBinding(), - c.podSecurityPolicy(), - ) + if c.cfg.UsePSP || c.cfg.OpenShift { + objs = append(objs, c.serviceAccount(), c.role(), c.roleBinding()) + if c.cfg.UsePSP { + objs = append(objs, c.podSecurityPolicy()) + } } if c.cfg.Terminating || c.cfg.Installation.KubeletVolumePluginPath == "None" { diff --git a/pkg/render/csi_test.go b/pkg/render/csi_test.go index ac1b325ff4..6e4945cfe8 100644 --- a/pkg/render/csi_test.go +++ b/pkg/render/csi_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2022-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2022-2024 Tigera, Inc. 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. @@ -113,7 +113,7 @@ var _ = Describe("CSI rendering tests", func() { kind string }{ {name: "csi.tigera.io", ns: "", group: "storage", version: "v1", kind: "CSIDriver"}, - {name: "csi-node-driver", ns: common.CalicoNamespace, group: "apps", version: "v1", kind: "DaemonSet"}, + {name: "csi-node-driver", ns: "calico-system", group: "apps", version: "v1", kind: "DaemonSet"}, } comp := render.CSI(&cfg) Expect(comp.ResolveImages(nil)).To(BeNil()) @@ -196,6 +196,22 @@ var _ = Describe("CSI rendering tests", func() { Expect(ds.Spec.Template.Spec.ServiceAccountName).To(BeEmpty()) }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift + cfg.OpenShift = true + component := render.CSI(&cfg) + Expect(component.ResolveImages(nil)).To(BeNil()) + resources, _ := component.Objects() + + role := rtest.GetResource(resources, "csi-node-driver", "calico-system", "rbac.authorization.k8s.io", "v1", "Role").(*rbacv1.Role) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"privileged"}, + })) + }) + Context("With csi-node-driver DaemonSet overrides", func() { It("should handle csiNodeDriverDaemonSet overrides", func() { affinity := &corev1.Affinity{ diff --git a/pkg/render/dex.go b/pkg/render/dex.go index 6be7d7c949..fd86fafc9d 100644 --- a/pkg/render/dex.go +++ b/pkg/render/dex.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2019-2024 Tigera, Inc. 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. @@ -39,6 +39,7 @@ import ( "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" "github.com/tigera/operator/pkg/tls/certificatemanagement" ) @@ -64,7 +65,7 @@ func Dex(cfg *DexComponentConfiguration) Component { // DexComponentConfiguration contains all the config information needed to render the component. type DexComponentConfiguration struct { PullSecrets []*corev1.Secret - Openshift bool + OpenShift bool Installation *operatorv1.InstallationSpec DexConfig DexConfig ClusterDomain string @@ -183,6 +184,15 @@ func (c *dexComponent) clusterRole() client.Object { }) } + if c.cfg.OpenShift { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, + }) + } + return &rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{ @@ -385,7 +395,7 @@ func (c *dexComponent) configMap() *corev1.ConfigMap { func (c *dexComponent) allowTigeraNetworkPolicy() *v3.NetworkPolicy { egressRules := []v3.Rule{} - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, c.cfg.Openshift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, c.cfg.OpenShift) egressRules = append(egressRules, []v3.Rule{ { Action: v3.Allow, diff --git a/pkg/render/dex_test.go b/pkg/render/dex_test.go index cdb39a8a36..7cb9238a1b 100644 --- a/pkg/render/dex_test.go +++ b/pkg/render/dex_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2020-2024 Tigera, Inc. 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. @@ -23,6 +23,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -330,6 +331,22 @@ var _ = Describe("dex rendering tests", func() { } }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift + cfg.OpenShift = true + component := render.Dex(cfg) + Expect(component.ResolveImages(nil)).To(BeNil()) + resources, _ := component.Objects() + + role := rtest.GetResource(resources, "tigera-dex", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"nonroot-v2"}, + })) + }) + It("should not render PodAffinity when ControlPlaneReplicas is 1", func() { var replicas int32 = 1 cfg.Installation.ControlPlaneReplicas = &replicas @@ -366,7 +383,7 @@ var _ = Describe("dex rendering tests", func() { DescribeTable("should render allow-tigera policy", func(scenario testutils.AllowTigeraScenario) { - cfg.Openshift = scenario.Openshift + cfg.OpenShift = scenario.OpenShift component := render.Dex(cfg) resources, _ := component.Objects() @@ -375,8 +392,8 @@ var _ = Describe("dex rendering tests", func() { Expect(policy).To(Equal(expectedPolicy)) }, // Dex only renders in the presence of an Authentication CR, therefore does not have a config option for managed clusters. - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), ) }) }) diff --git a/pkg/render/egressgateway/egressgateway.go b/pkg/render/egressgateway/egressgateway.go index 64561b689d..ea78e9fc72 100644 --- a/pkg/render/egressgateway/egressgateway.go +++ b/pkg/render/egressgateway/egressgateway.go @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2023-2024 Tigera, Inc. 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. @@ -42,6 +42,7 @@ import ( "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" ) const ( @@ -108,25 +109,21 @@ func (c *component) Objects() ([]client.Object, []client.Object) { secret.ToRuntimeObjects(c.egwPullSecrets()...), c.egwServiceAccount(), ) - if c.config.OpenShift { - objectsToCreate = append(objectsToCreate, c.getSecurityContextConstraints()) - } var objectsToDelete []client.Object - if c.config.UsePSP { - objectsToCreate = append(objectsToCreate, - PodSecurityPolicy(), - c.egwRole(), - c.egwRoleBinding(), - ) + if c.config.UsePSP || c.config.OpenShift { + objectsToCreate = append(objectsToCreate, c.egwRole(), c.egwRoleBinding()) + if c.config.UsePSP { + objectsToCreate = append(objectsToCreate, PodSecurityPolicy()) + } + if c.config.OpenShift { + objectsToCreate = append(objectsToCreate, c.getSecurityContextConstraints()) + } } else { // It is possible to have multiple egress gateway resources in different namespaces. - // We only delete namespaced role and role binding here. The cluster-level psp is - // deleted in egressgateway_controller when no egress gateway is in the cluster. - objectsToDelete = append(objectsToDelete, - c.egwRole(), - c.egwRoleBinding(), - ) + // We only delete namespaced role and role binding here. The cluster-level psp and scc + // are deleted in egressgateway_controller when no egress gateway is in the cluster. + objectsToDelete = append(objectsToDelete, c.egwRole(), c.egwRoleBinding()) } objectsToCreate = append(objectsToCreate, c.egwDeployment()) @@ -365,13 +362,13 @@ func (c *component) egwRole() *rbacv1.Role { }, } - if c.config.Installation.KubernetesProvider == operatorv1.ProviderOpenShift { + if c.config.OpenShift { role.Rules = append(role.Rules, rbacv1.PolicyRule{ APIGroups: []string{"security.openshift.io"}, Resources: []string{"securitycontextconstraints"}, Verbs: []string{"use"}, - ResourceNames: []string{"privileged"}, + ResourceNames: []string{OpenShiftSCCName}, }, ) } @@ -501,26 +498,12 @@ func (c *component) getSecurityContextConstraints() *ocsv1.SecurityContextConstr } func SecurityContextConstraints() *ocsv1.SecurityContextConstraints { - return &ocsv1.SecurityContextConstraints{ - TypeMeta: metav1.TypeMeta{Kind: "SecurityContextConstraints", APIVersion: "security.openshift.io/v1"}, - ObjectMeta: metav1.ObjectMeta{Name: OpenShiftSCCName}, - AllowHostDirVolumePlugin: false, - AllowHostIPC: false, - AllowHostNetwork: false, - AllowHostPID: false, - AllowHostPorts: false, - AllowPrivilegeEscalation: ptr.BoolToPtr(true), - AllowPrivilegedContainer: true, - AllowedCapabilities: []corev1.Capability{corev1.Capability("NET_ADMIN"), corev1.Capability("NET_RAW")}, - FSGroup: ocsv1.FSGroupStrategyOptions{Type: ocsv1.FSGroupStrategyMustRunAs}, - RunAsUser: ocsv1.RunAsUserStrategyOptions{Type: ocsv1.RunAsUserStrategyRunAsAny}, - ReadOnlyRootFilesystem: false, - SELinuxContext: ocsv1.SELinuxContextStrategyOptions{Type: ocsv1.SELinuxStrategyMustRunAs}, - SupplementalGroups: ocsv1.SupplementalGroupsStrategyOptions{Type: ocsv1.SupplementalGroupsStrategyRunAsAny}, - Users: []string{}, - Volumes: []ocsv1.FSType{"*"}, - SeccompProfiles: []string{"runtime/default"}, - } + scc := securitycontextconstraints.NewNonRootSecurityContextConstraints(OpenShiftSCCName, []string{}) + scc.AllowPrivilegeEscalation = ptr.BoolToPtr(true) + scc.AllowPrivilegedContainer = true + scc.AllowedCapabilities = []corev1.Capability{corev1.Capability("NET_ADMIN"), corev1.Capability("NET_RAW")} + scc.ReadOnlyRootFilesystem = false + return scc } func concatString(arr []string) string { diff --git a/pkg/render/egressgateway/egressgateway_test.go b/pkg/render/egressgateway/egressgateway_test.go index b41c5b93b7..2cc2ca8106 100644 --- a/pkg/render/egressgateway/egressgateway_test.go +++ b/pkg/render/egressgateway/egressgateway_test.go @@ -141,8 +141,8 @@ var _ = Describe("Egress Gateway rendering tests", func() { IptablesBackend: "nft", }) resources, resToBeDeleted := component.Objects() - Expect(len(resources)).To(Equal(len(expectedResources))) - Expect(len(resToBeDeleted)).To(Equal(len(expectedResToBeDeleted))) + Expect(resources).To(HaveLen(len(expectedResources))) + Expect(resToBeDeleted).To(HaveLen(len(expectedResToBeDeleted))) for i, expectedRes := range expectedResources { rtest.ExpectResourceTypeAndObjectMetadata(resources[i], expectedRes.name, expectedRes.ns, expectedRes.group, expectedRes.version, expectedRes.kind) } @@ -273,7 +273,7 @@ var _ = Describe("Egress Gateway rendering tests", func() { VXLANPort: 4790, }) resources, _ := component.Objects() - Expect(len(resources)).To(Equal(2)) + Expect(resources).To(HaveLen(2)) dep := rtest.GetResource(resources, "egress-test", "test-ns", "apps", "v1", "Deployment").(*appsv1.Deployment) Expect(dep.Spec.Template.Spec.Containers[0].Resources).To(Equal(expectedResource)) elasticIPAnnotation := dep.Spec.Template.ObjectMeta.Annotations["cni.projectcalico.org/awsElasticIPs"] @@ -289,9 +289,9 @@ var _ = Describe("Egress Gateway rendering tests", func() { kind string }{ {"egress-test", "test-ns", "", "v1", "ServiceAccount"}, - {"tigera-egressgateway", "", "policy", "v1beta1", "PodSecurityPolicy"}, {"egress-test", "test-ns", rbac, "v1", "Role"}, {"egress-test", "test-ns", rbac, "v1", "RoleBinding"}, + {"tigera-egressgateway", "", "policy", "v1beta1", "PodSecurityPolicy"}, {"egress-test", "test-ns", "apps", "v1", "Deployment"}, } @@ -311,7 +311,7 @@ var _ = Describe("Egress Gateway rendering tests", func() { } }) - It("should create security context if platform is openshift", func() { + It("should create SecurityContextConstraints if platform is OpenShift", func() { expectedResources := []struct { name string ns string @@ -320,6 +320,8 @@ var _ = Describe("Egress Gateway rendering tests", func() { kind string }{ {"egress-test", "test-ns", "", "v1", "ServiceAccount"}, + {"egress-test", "test-ns", rbac, "v1", "Role"}, + {"egress-test", "test-ns", rbac, "v1", "RoleBinding"}, {"tigera-egressgateway", "", "security.openshift.io", "v1", "SecurityContextConstraints"}, {"egress-test", "test-ns", "apps", "v1", "Deployment"}, } diff --git a/pkg/render/fluentd.go b/pkg/render/fluentd.go index 02be936b2b..7409fe84c7 100644 --- a/pkg/render/fluentd.go +++ b/pkg/render/fluentd.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2019-2024 Tigera, Inc. 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. @@ -27,6 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" + operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/components" relasticsearch "github.com/tigera/operator/pkg/render/common/elasticsearch" @@ -36,6 +37,7 @@ import ( "github.com/tigera/operator/pkg/render/common/resourcequota" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" "github.com/tigera/operator/pkg/tls/certificatemanagement" "github.com/tigera/operator/pkg/tls/certkeyusage" "github.com/tigera/operator/pkg/url" @@ -277,7 +279,7 @@ func (c *fluentdComponent) Objects() ([]client.Object, []client.Object) { objs = append(objs, secret.ToRuntimeObjects(secret.CopyToNamespace(LogCollectorNamespace, c.cfg.PullSecrets...)...)...) objs = append(objs, c.metricsService()) - if c.cfg.Installation.KubernetesProvider == operatorv1.ProviderGKE { + if c.cfg.Installation.KubernetesProvider.IsGKE() { // We do this only for GKE as other providers don't (yet?) // automatically add resource quota that constrains whether // components that are marked cluster or node critical @@ -614,7 +616,7 @@ func (c *fluentdComponent) container() corev1.Container { ImagePullPolicy: ImagePullPolicy(), Env: envs, // On OpenShift Fluentd needs privileged access to access logs on host path volume - SecurityContext: c.securityContext(c.cfg.Installation.KubernetesProvider == operatorv1.ProviderOpenShift), + SecurityContext: c.securityContext(c.cfg.Installation.KubernetesProvider.IsOpenShift()), VolumeMounts: volumeMounts, StartupProbe: c.startup(), LivenessProbe: c.liveness(), @@ -1022,12 +1024,12 @@ func (c *fluentdComponent) fluentdClusterRole() *rbacv1.ClusterRole { ResourceNames: []string{c.fluentdName()}, }) } - if c.cfg.Installation.KubernetesProvider == operatorv1.ProviderOpenShift { + if c.cfg.Installation.KubernetesProvider.IsOpenShift() { role.Rules = append(role.Rules, rbacv1.PolicyRule{ APIGroups: []string{"security.openshift.io"}, Resources: []string{"securitycontextconstraints"}, Verbs: []string{"use"}, - ResourceNames: []string{PSSPrivileged}, + ResourceNames: []string{securitycontextconstraints.Privileged}, }) } return role @@ -1320,7 +1322,7 @@ func (c *fluentdComponent) allowTigeraPolicy() *v3.NetworkPolicy { NotPorts: networkpolicy.Ports(8444), }, }) - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, c.cfg.Installation.KubernetesProvider == operatorv1.ProviderOpenShift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, c.cfg.Installation.KubernetesProvider.IsOpenShift()) } egressRules = append(egressRules, v3.Rule{ Action: v3.Allow, diff --git a/pkg/render/fluentd_test.go b/pkg/render/fluentd_test.go index d5d6c13374..6c1a3563aa 100644 --- a/pkg/render/fluentd_test.go +++ b/pkg/render/fluentd_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2019-2024 Tigera, Inc. 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. @@ -91,7 +91,7 @@ var _ = Describe("Tigera Secure Fluentd rendering tests", func() { } }) - It("should render security context constrains properly when provider is openshift", func() { + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift component := render.Fluentd(cfg) Expect(component.ResolveImages(nil)).To(BeNil()) @@ -1074,7 +1074,7 @@ var _ = Describe("Tigera Secure Fluentd rendering tests", func() { DescribeTable("should render allow-tigera policy", func(scenario testutils.AllowTigeraScenario) { - if scenario.Openshift { + if scenario.OpenShift { cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift } else { cfg.Installation.KubernetesProvider = operatorv1.ProviderNone @@ -1088,10 +1088,10 @@ var _ = Describe("Tigera Secure Fluentd rendering tests", func() { expectedPolicy := getExpectedPolicy(scenario) Expect(policy).To(Equal(expectedPolicy)) }, - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), - Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: false}), - Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), + Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: false}), + Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: true}), ) }) }) diff --git a/pkg/render/guardian.go b/pkg/render/guardian.go index 916fd6b9c7..02b8797d4d 100644 --- a/pkg/render/guardian.go +++ b/pkg/render/guardian.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2020-2024 Tigera, Inc. 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. @@ -28,6 +28,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" + "github.com/tigera/api/pkg/lib/numorstring" operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/components" @@ -36,6 +37,7 @@ import ( "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" "github.com/tigera/operator/pkg/tls/certificatemanagement" ) @@ -83,7 +85,7 @@ func GuardianPolicy(cfg *GuardianConfiguration) (Component, error) { type GuardianConfiguration struct { URL string PullSecrets []*corev1.Secret - Openshift bool + OpenShift bool Installation *operatorv1.InstallationSpec TunnelSecret *corev1.Secret TrustedCertBundle certificatemanagement.TrustedBundle @@ -129,7 +131,7 @@ func (c *GuardianComponent) Objects() ([]client.Object, []client.Object) { // service account is always within the tigera-manager namespace - regardless of (multi)tenancy mode. CreateNamespace(ManagerNamespace, c.cfg.Installation.KubernetesProvider, PSSRestricted), managerServiceAccount(ManagerNamespace), - managerClusterRole(false, true, c.cfg.UsePSP, c.cfg.Installation.KubernetesProvider), + managerClusterRole(true, c.cfg.UsePSP, c.cfg.Installation.KubernetesProvider), managerClusterRoleBinding([]string{ManagerNamespace}), managerClusterWideSettingsGroup(), managerUserSpecificSettingsGroup(), @@ -220,6 +222,15 @@ func (c *GuardianComponent) clusterRole() *rbacv1.ClusterRole { }) } + if c.cfg.OpenShift { + policyRules = append(policyRules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, + }) + } + return &rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{ @@ -358,7 +369,7 @@ func guardianAllowTigeraPolicy(cfg *GuardianConfiguration) (*v3.NetworkPolicy, e Destination: PacketCaptureEntityRule, }, } - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.Openshift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.OpenShift) egressRules = append(egressRules, []v3.Rule{ { Action: v3.Allow, diff --git a/pkg/render/guardian_test.go b/pkg/render/guardian_test.go index cbb949f3d9..42553555d5 100644 --- a/pkg/render/guardian_test.go +++ b/pkg/render/guardian_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2020-2024 Tigera, Inc. 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. @@ -80,7 +80,7 @@ var _ = Describe("Rendering tests", func() { Installation: &i, TunnelSecret: secret, TrustedCertBundle: bundle, - Openshift: openshift, + OpenShift: openshift, } } @@ -174,7 +174,7 @@ var _ = Describe("Rendering tests", func() { }) It("should render PSP when flagged", func() { - cfg.Openshift = notOpenshift + cfg.OpenShift = false cfg.UsePSP = true component := render.Guardian(cfg) resources, _ := component.Objects() @@ -194,6 +194,22 @@ var _ = Describe("Rendering tests", func() { })) }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift + cfg.OpenShift = true + component := render.Guardian(cfg) + Expect(component.ResolveImages(nil)).To(BeNil()) + resources, _ := component.Objects() + + role := rtest.GetResource(resources, "tigera-guardian", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"nonroot-v2"}, + })) + }) + Context("GuardianPolicy component", func() { guardianPolicy := testutils.GetExpectedPolicyFromFile("./testutils/expected_policies/guardian.json") guardianPolicyForOCP := testutils.GetExpectedPolicyFromFile("./testutils/expected_policies/guardian_ocp.json") @@ -218,13 +234,13 @@ var _ = Describe("Rendering tests", func() { DescribeTable("should render allow-tigera policy", func(scenario testutils.AllowTigeraScenario) { - renderGuardianPolicy("127.0.0.1:1234", scenario.Openshift) + renderGuardianPolicy("127.0.0.1:1234", scenario.OpenShift) policy := testutils.GetAllowTigeraPolicyFromResources(policyName, resources) expectedPolicy := getExpectedPolicy(policyName, scenario) Expect(policy).To(Equal(expectedPolicy)) }, - Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: false}), - Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: true}), + Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: false}), + Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: true}), ) // The test matrix above validates against an IP-based management cluster address. diff --git a/pkg/render/intrusion_detection.go b/pkg/render/intrusion_detection.go index 85fdbc358f..f7d0417388 100644 --- a/pkg/render/intrusion_detection.go +++ b/pkg/render/intrusion_detection.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2019-2024 Tigera, Inc. 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. @@ -31,6 +31,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" + operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/components" relasticsearch "github.com/tigera/operator/pkg/render/common/elasticsearch" @@ -40,6 +41,7 @@ import ( "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" "github.com/tigera/operator/pkg/tls/certificatemanagement" "github.com/tigera/operator/pkg/tls/certkeyusage" "github.com/tigera/operator/pkg/url" @@ -124,7 +126,7 @@ type IntrusionDetectionConfiguration struct { Installation *operatorv1.InstallationSpec ESClusterConfig *relasticsearch.ClusterConfig PullSecrets []*corev1.Secret - Openshift bool + OpenShift bool ClusterDomain string ESLicenseType ElasticsearchLicenseType ManagedCluster bool @@ -523,7 +525,7 @@ func (c *intrusionDetectionComponent) intrusionDetectionClusterRole() *rbacv1.Cl // is the owner of the AD Cronjobs - Openshift blocks setting an // blockOwnerDeletion to true if an ownerReference refers to a resource // you can't set finalizers on" - if c.cfg.Openshift { + if c.cfg.OpenShift { managementRule = append(managementRule, rbacv1.PolicyRule{ APIGroups: []string{"apps"}, @@ -535,15 +537,17 @@ func (c *intrusionDetectionComponent) intrusionDetectionClusterRole() *rbacv1.Cl rules = append(rules, managementRule...) } - if c.cfg.Installation.KubernetesProvider == operatorv1.ProviderOpenShift { + if c.cfg.OpenShift { + sccName := securitycontextconstraints.NonRootV2 if c.syslogForwardingIsEnabled() { - rules = append(rules, rbacv1.PolicyRule{ - APIGroups: []string{"security.openshift.io"}, - Resources: []string{"securitycontextconstraints"}, - Verbs: []string{"use"}, - ResourceNames: []string{PSSPrivileged}, - }) + sccName = securitycontextconstraints.Privileged } + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{sccName}, + }) } return &rbacv1.ClusterRole{ @@ -785,7 +789,7 @@ func (c *intrusionDetectionComponent) intrusionDetectionControllerContainer() co // use privileged UID/GID 0. // On OpenShift, if we need the volume mount to hostpath volume for syslog forwarding, // then IDS controller needs privileged access to write event logs to that volume - sc = securitycontext.NewRootContext(c.cfg.Openshift) + sc = securitycontext.NewRootContext(c.cfg.OpenShift) } if c.cfg.ManagedCluster { @@ -1304,7 +1308,7 @@ func (c *intrusionDetectionComponent) intrusionDetectionControllerAllowTigeraPol }, }, } - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, c.cfg.Openshift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, c.cfg.OpenShift) if c.cfg.ManagedCluster { egressRules = append(egressRules, v3.Rule{ Action: v3.Allow, @@ -1360,7 +1364,7 @@ func (c *intrusionDetectionComponent) intrusionDetectionControllerAllowTigeraPol func (c *intrusionDetectionComponent) intrusionDetectionElasticsearchAllowTigeraPolicy() *v3.NetworkPolicy { egressRules := []v3.Rule{} - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, c.cfg.Openshift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, c.cfg.OpenShift) egressRules = append(egressRules, v3.Rule{ Action: v3.Allow, Protocol: &networkpolicy.TCPProtocol, diff --git a/pkg/render/intrusion_detection_test.go b/pkg/render/intrusion_detection_test.go index d62ff04b4c..f2dea90be6 100644 --- a/pkg/render/intrusion_detection_test.go +++ b/pkg/render/intrusion_detection_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2019-2024 Tigera, Inc. 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. @@ -102,7 +102,7 @@ var _ = Describe("Intrusion Detection rendering tests", func() { }) It("should render all resources for a default configuration", func() { - cfg.Openshift = notOpenshift + cfg.OpenShift = false component := render.IntrusionDetection(cfg) resources, _ := component.Objects() @@ -233,7 +233,7 @@ var _ = Describe("Intrusion Detection rendering tests", func() { }) It("should render finalizers rbac resources in the IDS ClusterRole for an Openshift management/standalone cluster", func() { - cfg.Openshift = openshift + cfg.OpenShift = true cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift cfg.ManagedCluster = false component := render.IntrusionDetection(cfg) @@ -258,7 +258,7 @@ var _ = Describe("Intrusion Detection rendering tests", func() { }, }, } - cfg.Openshift = notOpenshift + cfg.OpenShift = false component := render.IntrusionDetection(cfg) resources, _ := component.Objects() @@ -343,8 +343,8 @@ var _ = Describe("Intrusion Detection rendering tests", func() { })) }) - It("should not render intrusion-detection-es-job-installer and should disable GlobalAlert controller when cluster is managed", func() { - cfg.Openshift = notOpenshift + It("should disable GlobalAlert controller when cluster is managed", func() { + cfg.OpenShift = false cfg.ManagedCluster = managedCluster component := render.IntrusionDetection(cfg) @@ -433,6 +433,22 @@ var _ = Describe("Intrusion Detection rendering tests", func() { } }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift + cfg.OpenShift = true + component := render.IntrusionDetection(cfg) + Expect(component.ResolveImages(nil)).To(BeNil()) + resources, _ := component.Objects() + + role := rtest.GetResource(resources, "intrusion-detection-controller", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"nonroot-v2"}, + })) + }) + It("should apply controlPlaneNodeSelector correctly", func() { cfg.Installation = &operatorv1.InstallationSpec{ ControlPlaneNodeSelector: map[string]string{"foo": "bar"}, @@ -487,7 +503,7 @@ var _ = Describe("Intrusion Detection rendering tests", func() { DescribeTable("should render allow-tigera policy", func(scenario testutils.AllowTigeraScenario) { - cfg.Openshift = scenario.Openshift + cfg.OpenShift = scenario.OpenShift cfg.ManagedCluster = scenario.ManagedCluster component := render.IntrusionDetection(cfg) resources, _ := component.Objects() @@ -498,10 +514,10 @@ var _ = Describe("Intrusion Detection rendering tests", func() { Expect(policy).To(Equal(expectedPolicy)) } }, - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), - Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: false}), - Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), + Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: false}), + Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: true}), ) }) diff --git a/pkg/render/intrusiondetection/dpi/dpi.go b/pkg/render/intrusiondetection/dpi/dpi.go index e63eec0037..3adbe774da 100644 --- a/pkg/render/intrusiondetection/dpi/dpi.go +++ b/pkg/render/intrusiondetection/dpi/dpi.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2021-2024 Tigera, Inc. 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. @@ -17,7 +17,6 @@ package dpi import ( "fmt" - "github.com/tigera/operator/pkg/tls/certificatemanagement" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -26,6 +25,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" + operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/common" "github.com/tigera/operator/pkg/components" @@ -35,6 +35,8 @@ import ( "github.com/tigera/operator/pkg/render/common/networkpolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" + "github.com/tigera/operator/pkg/tls/certificatemanagement" ) const ( @@ -53,7 +55,7 @@ type DPIConfig struct { Installation *operatorv1.InstallationSpec TyphaNodeTLS *render.TyphaNodeTLS PullSecrets []*corev1.Secret - Openshift bool + OpenShift bool ManagedCluster bool ManagementCluster bool HasNoLicense bool @@ -61,6 +63,9 @@ type DPIConfig struct { ESClusterConfig *relasticsearch.ClusterConfig ClusterDomain string DPICertSecret certificatemanagement.KeyPairInterface + + // Whether the cluster supports pod security policies. + UsePSP bool } func DPI(cfg *DPIConfig) render.Component { @@ -194,7 +199,7 @@ func (d *dpiComponent) dpiDaemonset() *appsv1.DaemonSet { } func (d *dpiComponent) dpiContainer() corev1.Container { - sc := securitycontext.NewRootContext(d.cfg.Openshift) + sc := securitycontext.NewRootContext(d.cfg.OpenShift) sc.Capabilities.Add = []corev1.Capability{ "NET_ADMIN", "NET_RAW", @@ -395,7 +400,7 @@ func (d *dpiComponent) dpiClusterRole() *rbacv1.ClusterRole { }, }, } - if d.cfg.Installation.KubernetesProvider != operatorv1.ProviderOpenShift { + if d.cfg.UsePSP { // Allow access to the pod security policy in case this is enforced on the cluster role.Rules = append(role.Rules, rbacv1.PolicyRule{ APIGroups: []string{"policy"}, @@ -404,6 +409,14 @@ func (d *dpiComponent) dpiClusterRole() *rbacv1.ClusterRole { ResourceNames: []string{DeepPacketInspectionName}, }) } + if d.cfg.OpenShift { + role.Rules = append(role.Rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.Privileged}, + }) + } return role } @@ -469,7 +482,7 @@ func (d *dpiComponent) dpiAllowTigeraPolicy() *v3.NetworkPolicy { Destination: networkpolicy.KubeAPIServerServiceSelectorEntityRule, }, } - egressRules = networkpolicy.AppendServiceSelectorDNSEgressRules(egressRules, d.cfg.Openshift) + egressRules = networkpolicy.AppendServiceSelectorDNSEgressRules(egressRules, d.cfg.OpenShift) if d.cfg.ManagedCluster { egressRules = append(egressRules, v3.Rule{ diff --git a/pkg/render/intrusiondetection/dpi/dpi_test.go b/pkg/render/intrusiondetection/dpi/dpi_test.go index 7a712d340f..6fee084c98 100644 --- a/pkg/render/intrusiondetection/dpi/dpi_test.go +++ b/pkg/render/intrusiondetection/dpi/dpi_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2021-2024 Tigera, Inc. 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. @@ -221,7 +221,7 @@ var _ = Describe("DPI rendering tests", func() { Installation: installation, TyphaNodeTLS: typhaNodeTLS, PullSecrets: pullSecrets, - Openshift: false, + OpenShift: false, HasNoLicense: false, HasNoDPIResource: false, ESClusterConfig: esConfigMap, @@ -275,7 +275,7 @@ var _ = Describe("DPI rendering tests", func() { Installation: installation, TyphaNodeTLS: typhaNodeTLS, PullSecrets: pullSecrets, - Openshift: false, + OpenShift: false, HasNoLicense: false, HasNoDPIResource: false, ESClusterConfig: esConfigMap, @@ -322,7 +322,7 @@ var _ = Describe("DPI rendering tests", func() { Installation: installation, TyphaNodeTLS: typhaNodeTLS, PullSecrets: pullSecrets, - Openshift: false, + OpenShift: false, HasNoLicense: false, HasNoDPIResource: false, ESClusterConfig: esConfigMap, @@ -368,7 +368,7 @@ var _ = Describe("DPI rendering tests", func() { Installation: installation, TyphaNodeTLS: typhaNodeTLS, PullSecrets: pullSecrets, - Openshift: false, + OpenShift: false, HasNoLicense: false, HasNoDPIResource: true, ManagementCluster: true, @@ -415,7 +415,7 @@ var _ = Describe("DPI rendering tests", func() { } cfg.IntrusionDetection = ids2 - cfg.Openshift = true + cfg.OpenShift = true component := dpi.DPI(cfg) resources, _ := component.Objects() @@ -481,7 +481,7 @@ var _ = Describe("DPI rendering tests", func() { Installation: installation, TyphaNodeTLS: typhaNodeTLS, PullSecrets: pullSecrets, - Openshift: false, + OpenShift: false, HasNoLicense: false, HasNoDPIResource: true, ManagedCluster: true, @@ -518,7 +518,7 @@ var _ = Describe("DPI rendering tests", func() { Installation: installation, TyphaNodeTLS: typhaNodeTLS, PullSecrets: pullSecrets, - Openshift: false, + OpenShift: false, HasNoLicense: false, HasNoDPIResource: true, ManagementCluster: true, @@ -580,6 +580,22 @@ var _ = Describe("DPI rendering tests", func() { } }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift + cfg.OpenShift = true + component := dpi.DPI(cfg) + Expect(component.ResolveImages(nil)).To(BeNil()) + resources, _ := component.Objects() + + role := rtest.GetResource(resources, "tigera-dpi", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"privileged"}, + })) + }) + Context("allow-tigera rendering", func() { policyName := types.NamespacedName{Name: "allow-tigera.tigera-dpi", Namespace: "tigera-dpi"} @@ -596,7 +612,7 @@ var _ = Describe("DPI rendering tests", func() { DescribeTable("should render allow-tigera policy", func(scenario testutils.AllowTigeraScenario) { cfg.ManagedCluster = scenario.ManagedCluster - cfg.Openshift = scenario.Openshift + cfg.OpenShift = scenario.OpenShift component := dpi.DPI(cfg) resources, _ := component.Objects() @@ -604,10 +620,10 @@ var _ = Describe("DPI rendering tests", func() { expectedPolicy := getExpectedPolicy(scenario) Expect(policy).To(Equal(expectedPolicy)) }, - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), - Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: false}), - Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), + Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: false}), + Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: true}), ) }) }) diff --git a/pkg/render/kubecontrollers/kube-controllers.go b/pkg/render/kubecontrollers/kube-controllers.go index 55570158ff..e568a03a4b 100644 --- a/pkg/render/kubecontrollers/kube-controllers.go +++ b/pkg/render/kubecontrollers/kube-controllers.go @@ -27,6 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" + operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/components" "github.com/tigera/operator/pkg/controller/k8sapi" @@ -39,6 +40,7 @@ import ( "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" "github.com/tigera/operator/pkg/render/monitor" "github.com/tigera/operator/pkg/tls/certificatemanagement" ) @@ -244,7 +246,7 @@ func (c *kubeControllersComponent) Objects() ([]client.Object, []client.Object) c.controllersDeployment(), ) - if c.cfg.Installation.KubernetesProvider == operatorv1.ProviderOpenShift { + if c.cfg.Installation.KubernetesProvider.IsOpenShift() { objectsToCreate = append(objectsToCreate, c.controllersOCPFederationRoleBinding()) } objectsToDelete := []client.Object{} @@ -338,6 +340,15 @@ func kubeControllersRoleCommonRules(cfg *KubeControllersConfiguration, kubeContr }) } + if cfg.Installation.KubernetesProvider.IsOpenShift() { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, + }) + } + return rules } @@ -688,7 +699,7 @@ func (c *kubeControllersComponent) kubeControllersVolumes() []corev1.Volume { func kubeControllersAllowTigeraPolicy(cfg *KubeControllersConfiguration) *v3.NetworkPolicy { egressRules := []v3.Rule{} - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.Installation.KubernetesProvider == operatorv1.ProviderOpenShift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.Installation.KubernetesProvider.IsOpenShift()) egressRules = append(egressRules, []v3.Rule{ { Action: v3.Allow, @@ -748,7 +759,7 @@ func esKubeControllersAllowTigeraPolicy(cfg *KubeControllersConfiguration) *v3.N } egressRules := []v3.Rule{} - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.Installation.KubernetesProvider == operatorv1.ProviderOpenShift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.Installation.KubernetesProvider.IsOpenShift()) egressRules = append(egressRules, []v3.Rule{ { Action: v3.Allow, diff --git a/pkg/render/kubecontrollers/kube-controllers_test.go b/pkg/render/kubecontrollers/kube-controllers_test.go index 4db60bfb2e..195f66957c 100644 --- a/pkg/render/kubecontrollers/kube-controllers_test.go +++ b/pkg/render/kubecontrollers/kube-controllers_test.go @@ -150,6 +150,21 @@ var _ = Describe("kube-controllers rendering tests", func() { } }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift + component := kubecontrollers.NewCalicoKubeControllers(&cfg) + Expect(component.ResolveImages(nil)).To(BeNil()) + resources, _ := component.Objects() + + role := rtest.GetResource(resources, "calico-kube-controllers", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"nonroot-v2"}, + })) + }) + It("should render all resources for a custom configuration", func() { expectedResources := []struct { name string @@ -1003,7 +1018,7 @@ var _ = Describe("kube-controllers rendering tests", func() { DescribeTable("should render allow-tigera policy", func(scenario testutils.AllowTigeraScenario) { - if scenario.Openshift { + if scenario.OpenShift { cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift } else { cfg.Installation.KubernetesProvider = operatorv1.ProviderNone @@ -1028,10 +1043,10 @@ var _ = Describe("kube-controllers rendering tests", func() { ) Expect(policy).To(Equal(expectedPolicy)) }, - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), - Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: false}), - Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), + Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: false}), + Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: true}), ) It("policy should omit prometheus ingress rule when metrics port is 0", func() { @@ -1065,7 +1080,7 @@ var _ = Describe("kube-controllers rendering tests", func() { DescribeTable("should render allow-tigera policy", func(scenario testutils.AllowTigeraScenario) { - if scenario.Openshift { + if scenario.OpenShift { cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift } else { cfg.Installation.KubernetesProvider = operatorv1.ProviderNone @@ -1086,10 +1101,10 @@ var _ = Describe("kube-controllers rendering tests", func() { expectedPolicy := getExpectedPolicy(scenario) Expect(policy).To(Equal(expectedPolicy)) }, - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), - Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: false}), - Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), + Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: false}), + Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: true}), ) }) diff --git a/pkg/render/logstorage.go b/pkg/render/logstorage.go index 56d36beb9b..14084310c4 100644 --- a/pkg/render/logstorage.go +++ b/pkg/render/logstorage.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2020-2024 Tigera, Inc. 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. @@ -52,6 +52,7 @@ import ( "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" "github.com/tigera/operator/pkg/tls/certificatemanagement" ) @@ -325,33 +326,14 @@ func (es *elasticsearchComponent) Objects() ([]client.Object, []client.Object) { toCreate = append(toCreate, secret.ToRuntimeObjects(secret.CopyToNamespace(ECKOperatorNamespace, es.cfg.PullSecrets...)...)...) - toCreate = append(toCreate, - es.eckOperatorClusterRole(), - es.eckOperatorClusterRoleBinding(), - es.eckOperatorServiceAccount(), - ) + toCreate = append(toCreate, es.eckOperatorServiceAccount(), es.eckOperatorClusterRole(), es.eckOperatorClusterRoleBinding()) + // This is needed for the operator to be able to set privileged mode for pods. // https://docs.docker.com/ee/ucp/authorization/#secure-kubernetes-defaults - if es.cfg.Provider == operatorv1.ProviderDockerEE { + if es.cfg.Provider.IsDockerEE() { toCreate = append(toCreate, es.eckOperatorClusterAdminClusterRoleBinding()) } - if es.cfg.UsePSP { - toCreate = append(toCreate, - es.elasticsearchClusterRoleBinding(), - es.elasticsearchClusterRole(), - es.eckOperatorPodSecurityPolicy(), - es.elasticsearchPodSecurityPolicy(), - ) - if es.cfg.KibanaEnabled { - toCreate = append(toCreate, - es.kibanaClusterRoleBinding(), - es.kibanaClusterRole(), - es.kibanaPodSecurityPolicy(), - ) - } - } - if es.cfg.ApplyTrial { toCreate = append(toCreate, es.elasticEnterpriseTrial()) } @@ -376,6 +358,13 @@ func (es *elasticsearchComponent) Objects() ([]client.Object, []client.Object) { toCreate = append(toCreate, es.elasticsearchCluster()) + if es.cfg.UsePSP || es.cfg.Installation.KubernetesProvider.IsOpenShift() { + toCreate = append(toCreate, es.elasticsearchClusterRole(), es.elasticsearchClusterRoleBinding()) + if es.cfg.UsePSP { + toCreate = append(toCreate, es.eckOperatorPodSecurityPolicy(), es.elasticsearchPodSecurityPolicy()) + } + } + if es.cfg.KibanaEnabled { // Kibana CRs // In order to use restricted, we need to change elastic-internal-init-config: @@ -388,6 +377,13 @@ func (es *elasticsearchComponent) Objects() ([]client.Object, []client.Object) { toCreate = append(toCreate, networkpolicy.AllowTigeraDefaultDeny(KibanaNamespace)) toCreate = append(toCreate, es.kibanaServiceAccount()) + if es.cfg.UsePSP || es.cfg.Installation.KubernetesProvider.IsOpenShift() { + toCreate = append(toCreate, es.kibanaClusterRole(), es.kibanaClusterRoleBinding()) + if es.cfg.UsePSP { + toCreate = append(toCreate, es.kibanaPodSecurityPolicy()) + } + } + if len(es.cfg.PullSecrets) > 0 { toCreate = append(toCreate, secret.ToRuntimeObjects(secret.CopyToNamespace(KibanaNamespace, es.cfg.PullSecrets...)...)...) } @@ -1115,6 +1111,15 @@ func (es elasticsearchComponent) eckOperatorClusterRole() *rbacv1.ClusterRole { }) } + if es.cfg.Installation.KubernetesProvider.IsOpenShift() { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, + }) + } + return &rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{ @@ -1582,19 +1587,30 @@ func (es elasticsearchComponent) elasticEnterpriseTrial() *corev1.Secret { } func (es elasticsearchComponent) elasticsearchClusterRole() *rbacv1.ClusterRole { + var rules []rbacv1.PolicyRule + if es.cfg.UsePSP { + rules = append(rules, rbacv1.PolicyRule{ + // Allow access to the pod security policy in case this is enforced on the cluster + APIGroups: []string{"policy"}, + Resources: []string{"podsecuritypolicies"}, + Verbs: []string{"use"}, + ResourceNames: []string{ElasticsearchObjectName}, + }) + } + if es.cfg.Installation.KubernetesProvider.IsOpenShift() { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.Privileged}, + }) + } return &rbacv1.ClusterRole{ + TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{ Name: ElasticsearchObjectName, }, - Rules: []rbacv1.PolicyRule{ - { - // Allow access to the pod security policy in case this is enforced on the cluster - APIGroups: []string{"policy"}, - Resources: []string{"podsecuritypolicies"}, - Verbs: []string{"use"}, - ResourceNames: []string{ElasticsearchObjectName}, - }, - }, + Rules: rules, } } @@ -1633,19 +1649,30 @@ func (es elasticsearchComponent) elasticsearchPodSecurityPolicy() *policyv1beta1 } func (es elasticsearchComponent) kibanaClusterRole() *rbacv1.ClusterRole { + var rules []rbacv1.PolicyRule + if es.cfg.UsePSP { + rules = append(rules, rbacv1.PolicyRule{ + // Allow access to the pod security policy in case this is enforced on the cluster + APIGroups: []string{"policy"}, + Resources: []string{"podsecuritypolicies"}, + Verbs: []string{"use"}, + ResourceNames: []string{KibanaObjectName}, + }) + } + if es.cfg.Installation.KubernetesProvider.IsOpenShift() { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, + }) + } return &rbacv1.ClusterRole{ + TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{ Name: KibanaObjectName, }, - Rules: []rbacv1.PolicyRule{ - { - // Allow access to the pod security policy in case this is enforced on the cluster - APIGroups: []string{"policy"}, - Resources: []string{"podsecuritypolicies"}, - Verbs: []string{"use"}, - ResourceNames: []string{KibanaObjectName}, - }, - }, + Rules: rules, } } @@ -1755,7 +1782,7 @@ func (es *elasticsearchComponent) eckOperatorAllowTigeraPolicy() *v3.NetworkPoli // Allow access to Elasticsearch client nodes from Kibana, ECK Operator and ES Gateway. func (es *elasticsearchComponent) elasticsearchAllowTigeraPolicy() *v3.NetworkPolicy { egressRules := []v3.Rule{} - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, es.cfg.Provider == operatorv1.ProviderOpenShift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, es.cfg.Provider.IsOpenShift()) egressRules = append(egressRules, []v3.Rule{ { Action: v3.Allow, diff --git a/pkg/render/logstorage/esgateway/esgateway.go b/pkg/render/logstorage/esgateway/esgateway.go index 7c12ea0d85..72658b2a15 100644 --- a/pkg/render/logstorage/esgateway/esgateway.go +++ b/pkg/render/logstorage/esgateway/esgateway.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2021-2024 Tigera, Inc. 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. @@ -29,6 +29,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" + operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/components" "github.com/tigera/operator/pkg/render" @@ -39,6 +40,7 @@ import ( "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" "github.com/tigera/operator/pkg/render/logstorage/esmetrics" "github.com/tigera/operator/pkg/tls/certificatemanagement" ) @@ -158,6 +160,15 @@ func (e *esGateway) esGatewayRole() *rbacv1.Role { }) } + if e.cfg.Installation.KubernetesProvider.IsOpenShift() { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, + }) + } + return &rbacv1.Role{ TypeMeta: metav1.TypeMeta{Kind: "Role", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{ @@ -338,7 +349,7 @@ func (e *esGateway) esGatewayService() *corev1.Service { // Allow access to ES Gateway from components that need to talk to Elasticsearch or Kibana. func (e *esGateway) esGatewayAllowTigeraPolicy() *v3.NetworkPolicy { egressRules := []v3.Rule{} - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, e.cfg.Installation.KubernetesProvider == operatorv1.ProviderOpenShift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, e.cfg.Installation.KubernetesProvider.IsOpenShift()) egressRules = append(egressRules, []v3.Rule{ { Action: v3.Allow, diff --git a/pkg/render/logstorage/esgateway/esgateway_test.go b/pkg/render/logstorage/esgateway/esgateway_test.go index 89f060c62e..f2179ea361 100644 --- a/pkg/render/logstorage/esgateway/esgateway_test.go +++ b/pkg/render/logstorage/esgateway/esgateway_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2021-2024 Tigera, Inc. 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. @@ -153,6 +153,21 @@ var _ = Describe("ES Gateway rendering tests", func() { } }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift + component := EsGateway(cfg) + Expect(component.ResolveImages(nil)).To(BeNil()) + resources, _ := component.Objects() + + role := rtest.GetResource(resources, "tigera-secure-es-gateway", "tigera-elasticsearch", "rbac.authorization.k8s.io", "v1", "Role").(*rbacv1.Role) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"nonroot-v2"}, + })) + }) + It("should not render PodAffinity when ControlPlaneReplicas is 1", func() { var replicas int32 = 1 installation.ControlPlaneReplicas = &replicas @@ -218,7 +233,7 @@ var _ = Describe("ES Gateway rendering tests", func() { DescribeTable("should render allow-tigera policy", func(scenario testutils.AllowTigeraScenario) { - if scenario.Openshift { + if scenario.OpenShift { cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift } else { cfg.Installation.KubernetesProvider = operatorv1.ProviderNone @@ -232,8 +247,8 @@ var _ = Describe("ES Gateway rendering tests", func() { }, // ES Gateway only renders in the presence of an LogStorage CR and absence of a ManagementClusterConnection CR, therefore // does not have a config option for managed clusters. - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), ) }) It("should set the right env when FIPS mode is enabled", func() { diff --git a/pkg/render/logstorage/esmetrics/elasticsearch_metrics.go b/pkg/render/logstorage/esmetrics/elasticsearch_metrics.go index dfe8877a9d..89cf73f692 100644 --- a/pkg/render/logstorage/esmetrics/elasticsearch_metrics.go +++ b/pkg/render/logstorage/esmetrics/elasticsearch_metrics.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2021-2024 Tigera, Inc. 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. @@ -26,6 +26,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" + operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/components" "github.com/tigera/operator/pkg/ptr" @@ -36,6 +37,7 @@ import ( "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" "github.com/tigera/operator/pkg/tls/certificatemanagement" ) @@ -97,12 +99,11 @@ func (e *elasticsearchMetrics) Objects() (objsToCreate, objsToDelete []client.Ob toCreate = append(toCreate, secret.ToRuntimeObjects(secret.CopyToNamespace(render.ElasticsearchNamespace, e.cfg.ESMetricsCredsSecret)...)...) toCreate = append(toCreate, e.metricsService(), e.metricsDeployment(), e.serviceAccount()) - if e.cfg.UsePSP { - toCreate = append(toCreate, - e.metricsRole(), - e.metricsRoleBinding(), - e.metricsPodSecurityPolicy(), - ) + if e.cfg.UsePSP || e.cfg.Installation.KubernetesProvider.IsOpenShift() { + toCreate = append(toCreate, e.metricsRole(), e.metricsRoleBinding()) + if e.cfg.UsePSP { + toCreate = append(toCreate, e.metricsPodSecurityPolicy()) + } } return toCreate, objsToDelete } @@ -126,20 +127,30 @@ func (e *elasticsearchMetrics) SupportedOSType() rmeta.OSType { } func (e *elasticsearchMetrics) metricsRole() *rbacv1.Role { + var rules []rbacv1.PolicyRule + if e.cfg.UsePSP { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"policy"}, + Resources: []string{"podsecuritypolicies"}, + Verbs: []string{"use"}, + ResourceNames: []string{ElasticsearchMetricsPodSecurityPolicyName}, + }) + } + if e.cfg.Installation.KubernetesProvider.IsOpenShift() { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, + }) + } return &rbacv1.Role{ TypeMeta: metav1.TypeMeta{Kind: "Role", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{ Name: ElasticsearchMetricsRoleName, Namespace: render.ElasticsearchNamespace, }, - Rules: []rbacv1.PolicyRule{ - { - APIGroups: []string{"policy"}, - Resources: []string{"podsecuritypolicies"}, - Verbs: []string{"use"}, - ResourceNames: []string{ElasticsearchMetricsPodSecurityPolicyName}, - }, - }, + Rules: rules, } } @@ -271,7 +282,7 @@ func (e *elasticsearchMetrics) allowTigeraPolicy() *v3.NetworkPolicy { Destination: networkpolicy.DefaultHelper().ESGatewayEntityRule(), }, } - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, e.cfg.Installation.KubernetesProvider == operatorv1.ProviderOpenShift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, e.cfg.Installation.KubernetesProvider.IsOpenShift()) egressRules = append(egressRules, v3.Rule{ Action: v3.Allow, diff --git a/pkg/render/logstorage/esmetrics/elasticsearch_metrics_test.go b/pkg/render/logstorage/esmetrics/elasticsearch_metrics_test.go index 3d66162ea5..bf86388762 100644 --- a/pkg/render/logstorage/esmetrics/elasticsearch_metrics_test.go +++ b/pkg/render/logstorage/esmetrics/elasticsearch_metrics_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2021-2024 Tigera, Inc. 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. @@ -21,6 +21,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -263,6 +264,21 @@ var _ = Describe("Elasticsearch metrics", func() { } }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift + component := ElasticsearchMetrics(cfg) + Expect(component.ResolveImages(nil)).To(BeNil()) + resources, _ := component.Objects() + + role := rtest.GetResource(resources, "tigera-elasticsearch-metrics", "tigera-elasticsearch", "rbac.authorization.k8s.io", "v1", "Role").(*rbacv1.Role) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"nonroot-v2"}, + })) + }) + It("should apply controlPlaneNodeSelector correctly", func() { cfg.Installation.ControlPlaneNodeSelector = map[string]string{"foo": "bar"} @@ -303,7 +319,7 @@ var _ = Describe("Elasticsearch metrics", func() { DescribeTable("should render allow-tigera policy", func(scenario testutils.AllowTigeraScenario) { - if scenario.Openshift { + if scenario.OpenShift { cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift } else { cfg.Installation.KubernetesProvider = operatorv1.ProviderNone @@ -317,8 +333,8 @@ var _ = Describe("Elasticsearch metrics", func() { }, // ES Gateway only renders in the presence of an LogStorage CR and absence of a ManagementClusterConnection CR, therefore // does not have a config option for managed clusters. - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), ) }) }) diff --git a/pkg/render/logstorage/linseed/linseed.go b/pkg/render/logstorage/linseed/linseed.go index b1ed6c2035..49f63c2bcd 100644 --- a/pkg/render/logstorage/linseed/linseed.go +++ b/pkg/render/logstorage/linseed/linseed.go @@ -1,4 +1,4 @@ -// Copyright (c) 2022-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2022-2024 Tigera, Inc. 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. @@ -19,10 +19,6 @@ import ( "strconv" "strings" - "github.com/tigera/operator/pkg/ptr" - - relasticsearch "github.com/tigera/operator/pkg/render/common/elasticsearch" - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" policyv1beta1 "k8s.io/api/policy/v1beta1" @@ -33,17 +29,21 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" + operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/common" "github.com/tigera/operator/pkg/components" + "github.com/tigera/operator/pkg/ptr" "github.com/tigera/operator/pkg/render" rcomponents "github.com/tigera/operator/pkg/render/common/components" + relasticsearch "github.com/tigera/operator/pkg/render/common/elasticsearch" rmeta "github.com/tigera/operator/pkg/render/common/meta" "github.com/tigera/operator/pkg/render/common/networkpolicy" "github.com/tigera/operator/pkg/render/common/podaffinity" "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" "github.com/tigera/operator/pkg/render/logstorage/esmetrics" "github.com/tigera/operator/pkg/tls/certificatemanagement" ) @@ -229,6 +229,15 @@ func (l *linseed) linseedClusterRole() *rbacv1.ClusterRole { }) } + if l.cfg.Installation.KubernetesProvider.IsOpenShift() { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, + }) + } + return &rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{ @@ -491,7 +500,7 @@ func (l *linseed) linseedAllowTigeraPolicy() *v3.NetworkPolicy { // - Cluster DNS // - Elasticsearch egressRules := []v3.Rule{} - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, l.cfg.Installation.KubernetesProvider == operatorv1.ProviderOpenShift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, l.cfg.Installation.KubernetesProvider.IsOpenShift()) egressRules = append(egressRules, []v3.Rule{ { Action: v3.Allow, diff --git a/pkg/render/logstorage/linseed/linseed_test.go b/pkg/render/logstorage/linseed/linseed_test.go index 532976c3c9..9179d0125b 100644 --- a/pkg/render/logstorage/linseed/linseed_test.go +++ b/pkg/render/logstorage/linseed/linseed_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2022-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2022-2024 Tigera, Inc. 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. @@ -136,6 +136,21 @@ var _ = Describe("Linseed rendering tests", func() { } }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift + component := Linseed(cfg) + Expect(component.ResolveImages(nil)).To(BeNil()) + resources, _ := component.Objects() + + role := rtest.GetResource(resources, "tigera-linseed", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"nonroot-v2"}, + })) + }) + It("should render a Linseed deployment and all supporting resources when CertificateManagement is enabled", func() { secret, err := certificatemanagement.CreateSelfSignedSecret("", "", "", nil) Expect(err).NotTo(HaveOccurred()) @@ -230,7 +245,7 @@ var _ = Describe("Linseed rendering tests", func() { DescribeTable("should render allow-tigera policy", func(scenario testutils.AllowTigeraScenario) { - if scenario.Openshift { + if scenario.OpenShift { cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift } else { cfg.Installation.KubernetesProvider = operatorv1.ProviderNone @@ -245,10 +260,10 @@ var _ = Describe("Linseed rendering tests", func() { }, // Linseed only renders in the presence of an LogStorage CR and absence of a ManagementClusterConnection CR, therefore // does not have a config option for managed clusters. - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, kube-dns with dpi", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false, DPIEnabled: true}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), - Entry("for management/standalone, openshift-dns with dpi", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true, DPIEnabled: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, kube-dns with dpi", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false, DPIEnabled: true}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), + Entry("for management/standalone, openshift-dns with dpi", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true, DPIEnabled: true}), ) }) diff --git a/pkg/render/logstorage_test.go b/pkg/render/logstorage_test.go index 7a444bd688..8bfd87c70d 100644 --- a/pkg/render/logstorage_test.go +++ b/pkg/render/logstorage_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2020-2024 Tigera, Inc. 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. @@ -23,6 +23,9 @@ import ( . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" + esv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/elasticsearch/v1" + kbv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/kibana/v1" + appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" @@ -35,9 +38,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" - esv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/elasticsearch/v1" - kbv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/kibana/v1" - v3 "github.com/tigera/api/pkg/apis/projectcalico/v3" operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/apis" @@ -186,6 +186,37 @@ var _ = Describe("Elasticsearch rendering tests", func() { } }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift + component := render.LogStorage(cfg) + Expect(component.ResolveImages(nil)).To(BeNil()) + resources, _ := component.Objects() + + role := rtest.GetResource(resources, "elastic-operator", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"nonroot-v2"}, + })) + + role = rtest.GetResource(resources, "tigera-elasticsearch", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"privileged"}, + })) + + role = rtest.GetResource(resources, "tigera-kibana", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"nonroot-v2"}, + })) + }) + It("should render an elasticsearchComponent", func() { expectedCreateResources := []client.Object{ // ECK Resources @@ -221,6 +252,7 @@ var _ = Describe("Elasticsearch rendering tests", func() { &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: "tigera-elasticsearch", Namespace: render.ElasticsearchNamespace}}, &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: relasticsearch.ClusterConfigConfigMapName, Namespace: common.OperatorNamespace()}}, &esv1.Elasticsearch{ObjectMeta: metav1.ObjectMeta{Name: render.ElasticsearchName, Namespace: render.ElasticsearchNamespace}}, + &policyv1beta1.PodSecurityPolicy{ObjectMeta: metav1.ObjectMeta{Name: "tigera-elasticsearch"}}, &rbacv1.Role{ObjectMeta: metav1.ObjectMeta{Name: render.EsManagerRole, Namespace: render.ElasticsearchNamespace}}, &rbacv1.RoleBinding{ObjectMeta: metav1.ObjectMeta{Name: render.EsManagerRoleBinding, Namespace: render.ElasticsearchNamespace}}, } @@ -464,16 +496,9 @@ var _ = Describe("Elasticsearch rendering tests", func() { {render.ECKOperatorNamespace, "", &corev1.Namespace{}, nil}, {render.ECKOperatorPolicyName, render.ECKOperatorNamespace, &v3.NetworkPolicy{}, nil}, {"tigera-pull-secret", render.ECKOperatorNamespace, &corev1.Secret{}, nil}, + {"elastic-operator", render.ECKOperatorNamespace, &corev1.ServiceAccount{}, nil}, {"elastic-operator", "", &rbacv1.ClusterRole{}, nil}, {"elastic-operator", "", &rbacv1.ClusterRoleBinding{}, nil}, - {"elastic-operator", render.ECKOperatorNamespace, &corev1.ServiceAccount{}, nil}, - {"tigera-elasticsearch", "", &rbacv1.ClusterRoleBinding{}, nil}, - {"tigera-elasticsearch", "", &rbacv1.ClusterRole{}, nil}, - {render.ECKOperatorName, "", &policyv1beta1.PodSecurityPolicy{}, nil}, - {"tigera-elasticsearch", "", &policyv1beta1.PodSecurityPolicy{}, nil}, - {"tigera-kibana", "", &rbacv1.ClusterRoleBinding{}, nil}, - {"tigera-kibana", "", &rbacv1.ClusterRole{}, nil}, - {"tigera-kibana", "", &policyv1beta1.PodSecurityPolicy{}, nil}, {render.ECKOperatorName, render.ECKOperatorNamespace, &appsv1.StatefulSet{}, nil}, {render.ElasticsearchNamespace, "", &corev1.Namespace{}, nil}, {render.ElasticsearchPolicyName, render.ElasticsearchNamespace, &v3.NetworkPolicy{}, nil}, @@ -483,10 +508,17 @@ var _ = Describe("Elasticsearch rendering tests", func() { {"tigera-elasticsearch", render.ElasticsearchNamespace, &corev1.ServiceAccount{}, nil}, {relasticsearch.ClusterConfigConfigMapName, common.OperatorNamespace(), &corev1.ConfigMap{}, nil}, {render.ElasticsearchName, render.ElasticsearchNamespace, &esv1.Elasticsearch{}, nil}, + {"tigera-elasticsearch", "", &rbacv1.ClusterRole{}, nil}, + {"tigera-elasticsearch", "", &rbacv1.ClusterRoleBinding{}, nil}, + {render.ECKOperatorName, "", &policyv1beta1.PodSecurityPolicy{}, nil}, + {"tigera-elasticsearch", "", &policyv1beta1.PodSecurityPolicy{}, nil}, {render.KibanaNamespace, "", &corev1.Namespace{}, nil}, {render.KibanaPolicyName, render.KibanaNamespace, &v3.NetworkPolicy{}, nil}, {networkpolicy.TigeraComponentDefaultDenyPolicyName, render.KibanaNamespace, &v3.NetworkPolicy{}, nil}, {"tigera-kibana", render.KibanaNamespace, &corev1.ServiceAccount{}, nil}, + {"tigera-kibana", "", &rbacv1.ClusterRole{}, nil}, + {"tigera-kibana", "", &rbacv1.ClusterRoleBinding{}, nil}, + {"tigera-kibana", "", &policyv1beta1.PodSecurityPolicy{}, nil}, {"tigera-pull-secret", render.KibanaNamespace, &corev1.Secret{}, nil}, {render.KibanaName, render.KibanaNamespace, &kbv1.Kibana{}, nil}, {render.EsManagerRole, render.ElasticsearchNamespace, &rbacv1.Role{}, nil}, @@ -530,16 +562,9 @@ var _ = Describe("Elasticsearch rendering tests", func() { {render.ECKOperatorNamespace, "", &corev1.Namespace{}, nil}, {render.ECKOperatorPolicyName, render.ECKOperatorNamespace, &v3.NetworkPolicy{}, nil}, {"tigera-pull-secret", render.ECKOperatorNamespace, &corev1.Secret{}, nil}, + {"elastic-operator", render.ECKOperatorNamespace, &corev1.ServiceAccount{}, nil}, {"elastic-operator", "", &rbacv1.ClusterRole{}, nil}, {"elastic-operator", "", &rbacv1.ClusterRoleBinding{}, nil}, - {"elastic-operator", render.ECKOperatorNamespace, &corev1.ServiceAccount{}, nil}, - {"tigera-elasticsearch", "", &rbacv1.ClusterRoleBinding{}, nil}, - {"tigera-elasticsearch", "", &rbacv1.ClusterRole{}, nil}, - {render.ECKOperatorName, "", &policyv1beta1.PodSecurityPolicy{}, nil}, - {"tigera-elasticsearch", "", &policyv1beta1.PodSecurityPolicy{}, nil}, - {"tigera-kibana", "", &rbacv1.ClusterRoleBinding{}, nil}, - {"tigera-kibana", "", &rbacv1.ClusterRole{}, nil}, - {"tigera-kibana", "", &policyv1beta1.PodSecurityPolicy{}, nil}, {render.ECKOperatorName, render.ECKOperatorNamespace, &appsv1.StatefulSet{}, nil}, {render.ElasticsearchNamespace, "", &corev1.Namespace{}, nil}, {render.ElasticsearchPolicyName, render.ElasticsearchNamespace, &v3.NetworkPolicy{}, nil}, @@ -549,10 +574,17 @@ var _ = Describe("Elasticsearch rendering tests", func() { {"tigera-elasticsearch", render.ElasticsearchNamespace, &corev1.ServiceAccount{}, nil}, {relasticsearch.ClusterConfigConfigMapName, common.OperatorNamespace(), &corev1.ConfigMap{}, nil}, {render.ElasticsearchName, render.ElasticsearchNamespace, &esv1.Elasticsearch{}, nil}, + {"tigera-elasticsearch", "", &rbacv1.ClusterRole{}, nil}, + {"tigera-elasticsearch", "", &rbacv1.ClusterRoleBinding{}, nil}, + {render.ECKOperatorName, "", &policyv1beta1.PodSecurityPolicy{}, nil}, + {"tigera-elasticsearch", "", &policyv1beta1.PodSecurityPolicy{}, nil}, {render.KibanaNamespace, "", &corev1.Namespace{}, nil}, {render.KibanaPolicyName, render.KibanaNamespace, &v3.NetworkPolicy{}, nil}, {networkpolicy.TigeraComponentDefaultDenyPolicyName, render.KibanaNamespace, &v3.NetworkPolicy{}, nil}, {"tigera-kibana", render.KibanaNamespace, &corev1.ServiceAccount{}, nil}, + {"tigera-kibana", "", &rbacv1.ClusterRole{}, nil}, + {"tigera-kibana", "", &rbacv1.ClusterRoleBinding{}, nil}, + {"tigera-kibana", "", &policyv1beta1.PodSecurityPolicy{}, nil}, {"tigera-pull-secret", render.KibanaNamespace, &corev1.Secret{}, nil}, {render.KibanaName, render.KibanaNamespace, &kbv1.Kibana{}, nil}, {render.EsManagerRole, render.ElasticsearchNamespace, &rbacv1.Role{}, nil}, @@ -613,16 +645,9 @@ var _ = Describe("Elasticsearch rendering tests", func() { {render.ECKOperatorNamespace, "", &corev1.Namespace{}, nil}, {render.ECKOperatorPolicyName, render.ECKOperatorNamespace, &v3.NetworkPolicy{}, nil}, {"tigera-pull-secret", render.ECKOperatorNamespace, &corev1.Secret{}, nil}, + {"elastic-operator", render.ECKOperatorNamespace, &corev1.ServiceAccount{}, nil}, {"elastic-operator", "", &rbacv1.ClusterRole{}, nil}, {"elastic-operator", "", &rbacv1.ClusterRoleBinding{}, nil}, - {"elastic-operator", render.ECKOperatorNamespace, &corev1.ServiceAccount{}, nil}, - {"tigera-elasticsearch", "", &rbacv1.ClusterRoleBinding{}, nil}, - {"tigera-elasticsearch", "", &rbacv1.ClusterRole{}, nil}, - {render.ECKOperatorName, "", &policyv1beta1.PodSecurityPolicy{}, nil}, - {"tigera-elasticsearch", "", &policyv1beta1.PodSecurityPolicy{}, nil}, - {"tigera-kibana", "", &rbacv1.ClusterRoleBinding{}, nil}, - {"tigera-kibana", "", &rbacv1.ClusterRole{}, nil}, - {"tigera-kibana", "", &policyv1beta1.PodSecurityPolicy{}, nil}, {render.ECKOperatorName, render.ECKOperatorNamespace, &appsv1.StatefulSet{}, nil}, {render.ElasticsearchNamespace, "", &corev1.Namespace{}, nil}, {render.ElasticsearchPolicyName, render.ElasticsearchNamespace, &v3.NetworkPolicy{}, nil}, @@ -632,10 +657,17 @@ var _ = Describe("Elasticsearch rendering tests", func() { {"tigera-elasticsearch", render.ElasticsearchNamespace, &corev1.ServiceAccount{}, nil}, {relasticsearch.ClusterConfigConfigMapName, common.OperatorNamespace(), &corev1.ConfigMap{}, nil}, {render.ElasticsearchName, render.ElasticsearchNamespace, &esv1.Elasticsearch{}, nil}, + {"tigera-elasticsearch", "", &rbacv1.ClusterRole{}, nil}, + {"tigera-elasticsearch", "", &rbacv1.ClusterRoleBinding{}, nil}, + {render.ECKOperatorName, "", &policyv1beta1.PodSecurityPolicy{}, nil}, + {"tigera-elasticsearch", "", &policyv1beta1.PodSecurityPolicy{}, nil}, {render.KibanaNamespace, "", &corev1.Namespace{}, nil}, {render.KibanaPolicyName, render.KibanaNamespace, &v3.NetworkPolicy{}, nil}, {networkpolicy.TigeraComponentDefaultDenyPolicyName, render.KibanaNamespace, &v3.NetworkPolicy{}, nil}, {"tigera-kibana", render.KibanaNamespace, &corev1.ServiceAccount{}, nil}, + {"tigera-kibana", "", &rbacv1.ClusterRole{}, nil}, + {"tigera-kibana", "", &rbacv1.ClusterRoleBinding{}, nil}, + {"tigera-kibana", "", &policyv1beta1.PodSecurityPolicy{}, nil}, {"tigera-pull-secret", render.KibanaNamespace, &corev1.Secret{}, nil}, {render.KibanaName, render.KibanaNamespace, &kbv1.Kibana{}, nil}, {render.EsCuratorPolicyName, render.ElasticsearchNamespace, &v3.NetworkPolicy{}, nil}, @@ -699,7 +731,7 @@ var _ = Describe("Elasticsearch rendering tests", func() { DescribeTable("should render allow-tigera policy", func(scenario testutils.AllowTigeraScenario) { - if scenario.Openshift { + if scenario.OpenShift { cfg.Provider = operatorv1.ProviderOpenShift } else { cfg.Provider = operatorv1.ProviderNone @@ -714,8 +746,8 @@ var _ = Describe("Elasticsearch rendering tests", func() { Expect(policy).To(Equal(expectedPolicy)) } }, - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), ) }) }) @@ -868,13 +900,9 @@ var _ = Describe("Elasticsearch rendering tests", func() { {render.ECKOperatorNamespace, "", &corev1.Namespace{}, nil}, {render.ECKOperatorPolicyName, render.ECKOperatorNamespace, &v3.NetworkPolicy{}, nil}, {"tigera-pull-secret", render.ECKOperatorNamespace, &corev1.Secret{}, nil}, + {"elastic-operator", render.ECKOperatorNamespace, &corev1.ServiceAccount{}, nil}, {"elastic-operator", "", &rbacv1.ClusterRole{}, nil}, {"elastic-operator", "", &rbacv1.ClusterRoleBinding{}, nil}, - {"elastic-operator", render.ECKOperatorNamespace, &corev1.ServiceAccount{}, nil}, - {"tigera-elasticsearch", "", &rbacv1.ClusterRoleBinding{}, nil}, - {"tigera-elasticsearch", "", &rbacv1.ClusterRole{}, nil}, - {render.ECKOperatorName, "", &policyv1beta1.PodSecurityPolicy{}, nil}, - {"tigera-elasticsearch", "", &policyv1beta1.PodSecurityPolicy{}, nil}, {render.ECKEnterpriseTrial, render.ECKOperatorNamespace, &corev1.Secret{}, nil}, {render.ECKOperatorName, render.ECKOperatorNamespace, &appsv1.StatefulSet{}, nil}, {render.ElasticsearchNamespace, "", &corev1.Namespace{}, nil}, @@ -885,6 +913,10 @@ var _ = Describe("Elasticsearch rendering tests", func() { {"tigera-elasticsearch", render.ElasticsearchNamespace, &corev1.ServiceAccount{}, nil}, {relasticsearch.ClusterConfigConfigMapName, common.OperatorNamespace(), &corev1.ConfigMap{}, nil}, {render.ElasticsearchName, render.ElasticsearchNamespace, &esv1.Elasticsearch{}, nil}, + {"tigera-elasticsearch", "", &rbacv1.ClusterRole{}, nil}, + {"tigera-elasticsearch", "", &rbacv1.ClusterRoleBinding{}, nil}, + {render.ECKOperatorName, "", &policyv1beta1.PodSecurityPolicy{}, nil}, + {"tigera-elasticsearch", "", &policyv1beta1.PodSecurityPolicy{}, nil}, {render.ElasticsearchKeystoreSecret, common.OperatorNamespace(), &corev1.Secret{}, nil}, {render.ElasticsearchKeystoreSecret, render.ElasticsearchNamespace, &corev1.Secret{}, nil}, {render.EsManagerRole, render.ElasticsearchNamespace, &rbacv1.Role{}, nil}, @@ -1021,7 +1053,7 @@ var _ = Describe("Elasticsearch rendering tests", func() { DescribeTable("should render allow-tigera policy", func(scenario testutils.AllowTigeraScenario) { - if scenario.Openshift { + if scenario.OpenShift { cfg.Provider = operatorv1.ProviderOpenShift } else { cfg.Provider = operatorv1.ProviderNone @@ -1036,8 +1068,8 @@ var _ = Describe("Elasticsearch rendering tests", func() { Expect(policy).To(Equal(expectedPolicy)) } }, - Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: false}), - Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: true}), + Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: false}), + Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: true}), ) }) }) diff --git a/pkg/render/manager.go b/pkg/render/manager.go index 653eb39010..782043b768 100644 --- a/pkg/render/manager.go +++ b/pkg/render/manager.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2020-2024 Tigera, Inc. 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. @@ -20,8 +20,6 @@ import ( "strconv" "strings" - ocsv1 "github.com/openshift/api/security/v1" - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" policyv1beta1 "k8s.io/api/policy/v1beta1" @@ -47,6 +45,7 @@ import ( "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" "github.com/tigera/operator/pkg/tls/certificatemanagement" "github.com/tigera/operator/pkg/tls/certkeyusage" ) @@ -129,7 +128,7 @@ type ManagerConfiguration struct { ESSecrets []*corev1.Secret ClusterConfig *relasticsearch.ClusterConfig PullSecrets []*corev1.Secret - Openshift bool + OpenShift bool Installation *operatorv1.InstallationSpec ManagementCluster *operatorv1.ManagementCluster @@ -218,7 +217,7 @@ func (c *managerComponent) Objects() ([]client.Object, []client.Object) { objs = append(objs, managerClusterRoleBinding(c.cfg.BindingNamespaces), - managerClusterRole(c.cfg.ManagementCluster != nil, false, c.cfg.UsePSP, c.cfg.Installation.KubernetesProvider), + managerClusterRole(false, c.cfg.UsePSP, c.cfg.Installation.KubernetesProvider), managerClusterWideSettingsGroup(), managerUserSpecificSettingsGroup(), managerClusterWideTigeraLayer(), @@ -238,11 +237,6 @@ func (c *managerComponent) Objects() ([]client.Object, []client.Object) { objs = append(objs, c.getTLSObjects()...) objs = append(objs, c.managerService()) - // If we're running on openshift, we need to add in an SCC. - if c.cfg.Openshift { - objs = append(objs, c.securityContextConstraints()) - } - objs = append(objs, secret.ToRuntimeObjects(secret.CopyToNamespace(c.cfg.Namespace, c.cfg.ESSecrets...)...)...) objs = append(objs, c.managerDeployment()) if c.cfg.KeyValidatorConfig != nil { @@ -648,7 +642,7 @@ func managerPodSecurityPolicy() *policyv1beta1.PodSecurityPolicy { } // managerClusterRole returns a clusterrole that allows authn/authz review requests. -func managerClusterRole(managementCluster, managedCluster, usePSP bool, kubernetesProvider operatorv1.Provider) *rbacv1.ClusterRole { +func managerClusterRole(managedCluster, usePSP bool, kubernetesProvider operatorv1.Provider) *rbacv1.ClusterRole { cr := &rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{ @@ -803,13 +797,13 @@ func managerClusterRole(managementCluster, managedCluster, usePSP bool, kubernet ) } - if kubernetesProvider == operatorv1.ProviderOpenShift { + if kubernetesProvider.IsOpenShift() { cr.Rules = append(cr.Rules, rbacv1.PolicyRule{ APIGroups: []string{"security.openshift.io"}, Resources: []string{"securitycontextconstraints"}, Verbs: []string{"use"}, - ResourceNames: []string{PSSPrivileged}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, }, ) } @@ -817,29 +811,6 @@ func managerClusterRole(managementCluster, managedCluster, usePSP bool, kubernet return cr } -// TODO: Can we get rid of this and instead just bind to default ones? -func (c *managerComponent) securityContextConstraints() *ocsv1.SecurityContextConstraints { - privilegeEscalation := false - return &ocsv1.SecurityContextConstraints{ - TypeMeta: metav1.TypeMeta{Kind: "SecurityContextConstraints", APIVersion: "security.openshift.io/v1"}, - ObjectMeta: metav1.ObjectMeta{Name: c.cfg.Namespace}, - AllowHostDirVolumePlugin: true, - AllowHostIPC: false, - AllowHostNetwork: false, - AllowHostPID: true, - AllowHostPorts: false, - AllowPrivilegeEscalation: &privilegeEscalation, - AllowPrivilegedContainer: false, - FSGroup: ocsv1.FSGroupStrategyOptions{Type: ocsv1.FSGroupStrategyRunAsAny}, - RunAsUser: ocsv1.RunAsUserStrategyOptions{Type: ocsv1.RunAsUserStrategyRunAsAny}, - ReadOnlyRootFilesystem: false, - SELinuxContext: ocsv1.SELinuxContextStrategyOptions{Type: ocsv1.SELinuxStrategyMustRunAs}, - SupplementalGroups: ocsv1.SupplementalGroupsStrategyOptions{Type: ocsv1.SupplementalGroupsStrategyRunAsAny}, - Users: []string{fmt.Sprintf("system:serviceaccount:%s:tigera-manager", c.cfg.Namespace)}, - Volumes: []ocsv1.FSType{"*"}, - } -} - func (c *managerComponent) getTLSObjects() []client.Object { objs := []client.Object{} for _, s := range c.tlsSecrets { @@ -895,7 +866,7 @@ func (c *managerComponent) managerAllowTigeraNetworkPolicy() *v3.NetworkPolicy { Destination: networkpolicy.KubeAPIServerServiceSelectorEntityRule, }, } - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, c.cfg.Openshift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, c.cfg.OpenShift) egressRules = append(egressRules, v3.Rule{ Action: v3.Allow, Protocol: &networkpolicy.TCPProtocol, diff --git a/pkg/render/manager_test.go b/pkg/render/manager_test.go index 3b5c1c5ba4..1234f59faa 100644 --- a/pkg/render/manager_test.go +++ b/pkg/render/manager_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2019-2024 Tigera, Inc. 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. @@ -194,7 +194,7 @@ var _ = Describe("Tigera Secure Manager rendering tests", func() { Expect(ns.Labels["pod-security.kubernetes.io/enforce-version"]).To(Equal("latest")) }) - It("should render security context constrains properly when provider is openshift", func() { + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { resources := renderObjects(renderConfig{oidc: false, managementCluster: nil, installation: &operatorv1.InstallationSpec{ControlPlaneReplicas: &replicas, KubernetesProvider: operatorv1.ProviderOpenShift}, compliance: compliance, complianceFeatureActive: true}) // tigera-manager-role clusterRole should have openshift securitycontextconstraints PolicyRule @@ -203,7 +203,7 @@ var _ = Describe("Tigera Secure Manager rendering tests", func() { APIGroups: []string{"security.openshift.io"}, Resources: []string{"securitycontextconstraints"}, Verbs: []string{"use"}, - ResourceNames: []string{"privileged"}, + ResourceNames: []string{"nonroot-v2"}, })) }) @@ -854,7 +854,7 @@ var _ = Describe("Tigera Secure Manager rendering tests", func() { func(scenario testutils.AllowTigeraScenario) { // Default configuration. resources := renderObjects(renderConfig{ - openshift: scenario.Openshift, + openshift: scenario.OpenShift, oidc: false, managementCluster: nil, installation: installation, @@ -880,8 +880,8 @@ var _ = Describe("Tigera Secure Manager rendering tests", func() { Expect(policy).To(Equal(expectedPolicy)) }, // Manager only renders in the presence of a Manager CR, therefore does not have a config option for managed clusters. - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), ) }) @@ -1069,7 +1069,7 @@ func renderObjects(roc renderConfig) []client.Object { Replicas: roc.installation.ControlPlaneReplicas, Compliance: roc.compliance, ComplianceLicenseActive: roc.complianceFeatureActive, - Openshift: roc.openshift, + OpenShift: roc.openshift, UsePSP: true, Namespace: roc.ns, BindingNamespaces: roc.bindingNamespaces, diff --git a/pkg/render/monitor/monitor.go b/pkg/render/monitor/monitor.go index 8e8dbaa4df..b9f95899b9 100644 --- a/pkg/render/monitor/monitor.go +++ b/pkg/render/monitor/monitor.go @@ -45,6 +45,7 @@ import ( "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" "github.com/tigera/operator/pkg/render/logstorage/esmetrics" "github.com/tigera/operator/pkg/tls/certificatemanagement" "github.com/tigera/operator/pkg/tls/certkeyusage" @@ -127,7 +128,7 @@ type Config struct { ClientTLSSecret certificatemanagement.KeyPairInterface ClusterDomain string TrustedCertBundle certificatemanagement.TrustedBundle - Openshift bool + OpenShift bool KubeControllerPort int UsePSP bool } @@ -353,6 +354,15 @@ func (mc *monitorComponent) prometheusOperatorClusterRole() *rbacv1.ClusterRole }) } + if mc.cfg.OpenShift { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, + }) + } + return &rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{Name: CalicoPrometheusOperator}, @@ -618,6 +628,15 @@ func (mc *monitorComponent) prometheusClusterRole() *rbacv1.ClusterRole { }) } + if mc.cfg.OpenShift { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, + }) + } + return &rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{Name: PrometheusClusterRoleName}, @@ -658,6 +677,15 @@ func (mc *monitorComponent) prometheusServiceClusterRole() client.Object { }, } + if mc.cfg.OpenShift { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, + }) + } + return &rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{ @@ -953,7 +981,7 @@ func (mc *monitorComponent) operatorRoleBinding() *rbacv1.RoleBinding { // Creates a network policy to allow traffic to Alertmanager (TCP port 9093). func allowTigeraAlertManagerPolicy(cfg *Config) *v3.NetworkPolicy { egressRules := []v3.Rule{} - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.Openshift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.OpenShift) egressRules = append(egressRules, v3.Rule{ // Allows all egress traffic from AlertManager. Action: v3.Allow, @@ -1005,7 +1033,7 @@ func allowTigeraAlertManagerMeshPolicy(cfg *Config) *v3.NetworkPolicy { }, }, } - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.Openshift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.OpenShift) return &v3.NetworkPolicy{ TypeMeta: metav1.TypeMeta{Kind: "NetworkPolicy", APIVersion: "projectcalico.org/v3"}, @@ -1044,7 +1072,7 @@ func allowTigeraAlertManagerMeshPolicy(cfg *Config) *v3.NetworkPolicy { // Creates a network policy to allow traffic to access the Prometheus (TCP port 9095). func allowTigeraPrometheusPolicy(cfg *Config) *v3.NetworkPolicy { egressRules := []v3.Rule{} - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.Openshift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.OpenShift) egressRules = append(egressRules, []v3.Rule{ { Action: v3.Allow, @@ -1129,7 +1157,7 @@ func allowTigeraPrometheusPolicy(cfg *Config) *v3.NetworkPolicy { // Creates a network policy to allow traffic to access through tigera-prometheus-api func allowTigeraPrometheusAPIPolicy(cfg *Config) *v3.NetworkPolicy { egressRules := []v3.Rule{} - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.Openshift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.OpenShift) egressRules = append(egressRules, v3.Rule{ Action: v3.Allow, Protocol: &networkpolicy.TCPProtocol, @@ -1164,7 +1192,7 @@ func allowTigeraPrometheusAPIPolicy(cfg *Config) *v3.NetworkPolicy { // Creates a network policy to allow the prometheus-operatorto access the kube-apiserver func allowTigeraPrometheusOperatorPolicy(cfg *Config) *v3.NetworkPolicy { egressRules := []v3.Rule{} - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.Openshift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.OpenShift) egressRules = append(egressRules, v3.Rule{ Action: v3.Allow, Protocol: &networkpolicy.TCPProtocol, diff --git a/pkg/render/monitor/monitor_test.go b/pkg/render/monitor/monitor_test.go index dc6e5d7459..a8e8928bda 100644 --- a/pkg/render/monitor/monitor_test.go +++ b/pkg/render/monitor/monitor_test.go @@ -584,6 +584,38 @@ var _ = Describe("monitor rendering tests", func() { } }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift + cfg.OpenShift = true + component := monitor.Monitor(cfg) + Expect(component.ResolveImages(nil)).To(BeNil()) + resources, _ := component.Objects() + + role := rtest.GetResource(resources, "calico-prometheus-operator", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"nonroot-v2"}, + })) + + role = rtest.GetResource(resources, "prometheus", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"nonroot-v2"}, + })) + + role = rtest.GetResource(resources, "tigera-prometheus", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"nonroot-v2"}, + })) + }) + It("Should render Prometheus resources when Dex is enabled", func() { authentication := &operatorv1.Authentication{ Spec: operatorv1.AuthenticationSpec{ @@ -776,7 +808,7 @@ var _ = Describe("monitor rendering tests", func() { DescribeTable("should render allow-tigera policy", func(scenario testutils.AllowTigeraScenario) { - cfg.Openshift = scenario.Openshift + cfg.OpenShift = scenario.OpenShift cfg.KubeControllerPort = 9094 component := monitor.MonitorPolicy(cfg) @@ -788,10 +820,10 @@ var _ = Describe("monitor rendering tests", func() { Expect(policy).To(Equal(expectedPolicy)) } }, - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), - Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: false}), - Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), + Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: false}), + Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: true}), ) It("prometheus policy should omit kube-controller egress rule when kube-controller port is 0", func() { diff --git a/pkg/render/namespaces.go b/pkg/render/namespaces.go index d19236f94b..6508a64dca 100644 --- a/pkg/render/namespaces.go +++ b/pkg/render/namespaces.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2019-2024 Tigera, Inc. 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. @@ -15,14 +15,14 @@ package render import ( - rmeta "github.com/tigera/operator/pkg/render/common/meta" - "github.com/tigera/operator/pkg/render/common/secret" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/common" + rmeta "github.com/tigera/operator/pkg/render/common/meta" + "github.com/tigera/operator/pkg/render/common/secret" ) func Namespaces(cfg *NamespaceConfiguration) Component { @@ -108,8 +108,9 @@ func CreateNamespace(name string, provider operatorv1.Provider, pss PodSecurityS switch provider { case operatorv1.ProviderOpenShift: - ns.Labels["openshift.io/run-level"] = "0" ns.Annotations["openshift.io/node-selector"] = "" + ns.Annotations["security.openshift.io/scc.podSecurityLabelSync"] = "false" + ns.Labels["openshift.io/run-level"] = "0" case operatorv1.ProviderAKS: ns.Labels["control-plane"] = "true" } diff --git a/pkg/render/namespaces_test.go b/pkg/render/namespaces_test.go index 4df73dc4fb..d6c8293e61 100644 --- a/pkg/render/namespaces_test.go +++ b/pkg/render/namespaces_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2019-2024 Tigera, Inc. 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. @@ -17,9 +17,10 @@ package render_test import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - operatorv1 "github.com/tigera/operator/api/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + operatorv1 "github.com/tigera/operator/api/v1" "github.com/tigera/operator/pkg/render" rtest "github.com/tigera/operator/pkg/render/common/test" ) @@ -54,6 +55,7 @@ var _ = Describe("Namespace rendering tests", func() { Expect(meta.GetLabels()["openshift.io/run-level"]).To(Equal("0")) Expect(meta.GetLabels()).NotTo(ContainElement("control-plane")) Expect(meta.GetAnnotations()["openshift.io/node-selector"]).To(Equal("")) + Expect(meta.GetAnnotations()["security.openshift.io/scc.podSecurityLabelSync"]).To(Equal("false")) }) It("should render a namespace for aks", func() { diff --git a/pkg/render/node.go b/pkg/render/node.go index 34f0142ec8..49c9db15c9 100644 --- a/pkg/render/node.go +++ b/pkg/render/node.go @@ -41,6 +41,7 @@ import ( rmeta "github.com/tigera/operator/pkg/render/common/meta" "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" "github.com/tigera/operator/pkg/tls/certificatemanagement" ) @@ -217,7 +218,7 @@ func (c *nodeComponent) Objects() ([]client.Object, []client.Object) { objs = append(objs, btcm) } - if c.cfg.Installation.KubernetesProvider == operatorv1.ProviderDockerEE { + if c.cfg.Installation.KubernetesProvider.IsDockerEE() { objs = append(objs, c.clusterAdminClusterRoleBinding()) } @@ -545,12 +546,12 @@ func (c *nodeComponent) nodeRole() *rbacv1.ClusterRole { ResourceNames: []string{common.NodeDaemonSetName}, }) } - if c.cfg.Installation.KubernetesProvider == operatorv1.ProviderOpenShift { + if c.cfg.Installation.KubernetesProvider.IsOpenShift() { role.Rules = append(role.Rules, rbacv1.PolicyRule{ APIGroups: []string{"security.openshift.io"}, Resources: []string{"securitycontextconstraints"}, Verbs: []string{"use"}, - ResourceNames: []string{PSSPrivileged}, + ResourceNames: []string{securitycontextconstraints.Privileged}, }) } return role @@ -899,7 +900,7 @@ func (c *nodeComponent) nodeDaemonset(cniCfgMap *corev1.ConfigMap) *appsv1.Daemo } var affinity *corev1.Affinity - if c.cfg.Installation.KubernetesProvider == operatorv1.ProviderAKS { + if c.cfg.Installation.KubernetesProvider.IsAKS() { affinity = &corev1.Affinity{ NodeAffinity: &corev1.NodeAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ @@ -913,7 +914,7 @@ func (c *nodeComponent) nodeDaemonset(cniCfgMap *corev1.ConfigMap) *appsv1.Daemo }, }, } - } else if c.cfg.Installation.KubernetesProvider == operatorv1.ProviderEKS { + } else if c.cfg.Installation.KubernetesProvider.IsEKS() { affinity = &corev1.Affinity{ NodeAffinity: &corev1.NodeAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ @@ -1509,7 +1510,7 @@ func (c *nodeComponent) nodeEnvVars() []corev1.EnvVar { Name: "FELIX_XDPENABLED", Value: "false", }) - if c.cfg.Installation.KubernetesProvider == operatorv1.ProviderEKS { + if c.cfg.Installation.KubernetesProvider.IsEKS() { nodeEnv = append(nodeEnv, corev1.EnvVar{ Name: "FELIX_AWSSRCDSTCHECK", Value: "Disable", diff --git a/pkg/render/node_test.go b/pkg/render/node_test.go index ea65e1996d..80ec4234e5 100644 --- a/pkg/render/node_test.go +++ b/pkg/render/node_test.go @@ -48,8 +48,6 @@ import ( ) var ( - openshift = true - notOpenshift = false bgpEnabled = operatorv1.BGPEnabled bgpDisabled = operatorv1.BGPDisabled nonPrivilegedEnabled = operatorv1.NonPrivilegedEnabled @@ -157,6 +155,21 @@ var _ = Describe("Node rendering tests", func() { } }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift + component := render.Node(&cfg) + Expect(component.ResolveImages(nil)).To(BeNil()) + resources, _ := component.Objects() + + role := rtest.GetResource(resources, "calico-node", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"privileged"}, + })) + }) + It("should render all resources for a default configuration", func() { expectedResources := []struct { name string diff --git a/pkg/render/packet_capture_api.go b/pkg/render/packet_capture_api.go index 2854973550..2138c753f7 100644 --- a/pkg/render/packet_capture_api.go +++ b/pkg/render/packet_capture_api.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2021-2024 Tigera, Inc. 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. @@ -34,6 +34,7 @@ import ( "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" "github.com/tigera/operator/pkg/tls/certificatemanagement" ) @@ -61,7 +62,7 @@ var ( // PacketCaptureApiConfiguration contains all the config information needed to render the component. type PacketCaptureApiConfiguration struct { PullSecrets []*corev1.Secret - Openshift bool + OpenShift bool Installation *operatorv1.InstallationSpec KeyValidatorConfig authentication.KeyValidatorConfig ServerCertSecret certificatemanagement.KeyPairInterface @@ -201,6 +202,15 @@ func (pc *packetCaptureApiComponent) clusterRole() client.Object { }) } + if pc.cfg.OpenShift { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, + }) + } + return &rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{ @@ -347,7 +357,7 @@ func allowTigeraPolicy(cfg *PacketCaptureApiConfiguration) *v3.NetworkPolicy { Destination: networkpolicy.KubeAPIServerEntityRule, }, } - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.Openshift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, cfg.OpenShift) if !managedCluster { egressRules = append(egressRules, v3.Rule{ Action: v3.Allow, diff --git a/pkg/render/packet_capture_api_test.go b/pkg/render/packet_capture_api_test.go index 5e338c8e4e..e9942a8018 100644 --- a/pkg/render/packet_capture_api_test.go +++ b/pkg/render/packet_capture_api_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2021-2024 Tigera, Inc. 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. @@ -85,6 +85,7 @@ var _ = Describe("Rendering tests for PacketCapture API component", func() { Installation: &i, KeyValidatorConfig: config, ServerCertSecret: secret, + OpenShift: i.KubernetesProvider.IsOpenShift(), UsePSP: true, } pc := render.PacketCaptureAPI(cfg) @@ -375,6 +376,20 @@ var _ = Describe("Rendering tests for PacketCapture API component", func() { Expect(deployment.Spec.Template.Spec.Tolerations).Should(ContainElements(append(rmeta.TolerateCriticalAddonsAndControlPlane, t))) }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + resources := renderPacketCapture(operatorv1.InstallationSpec{ + KubernetesProvider: operatorv1.ProviderOpenShift, + }, nil) + + role := rtest.GetResource(resources, "tigera-packetcapture", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"nonroot-v2"}, + })) + }) + It("should render all resources for an installation with certificate management", func() { ca, _ := tls.MakeCA(rmeta.DefaultOperatorCASignerName()) cert, _, _ := ca.Config.GetPEMBytes() // create a valid pem block @@ -414,7 +429,7 @@ var _ = Describe("Rendering tests for PacketCapture API component", func() { Installation: &defaultInstallation, ServerCertSecret: secret, } - cfg.Openshift = scenario.Openshift + cfg.OpenShift = scenario.OpenShift if scenario.ManagedCluster { cfg.ManagementClusterConnection = &operatorv1.ManagementClusterConnection{} } else { @@ -434,10 +449,10 @@ var _ = Describe("Rendering tests for PacketCapture API component", func() { ) Expect(policy).To(Equal(expectedPolicy)) }, - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), - Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: false}), - Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), + Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: false}), + Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: true}), ) }) }) diff --git a/pkg/render/policyrecommendation.go b/pkg/render/policyrecommendation.go index 8c4ddea50b..cb84e17952 100644 --- a/pkg/render/policyrecommendation.go +++ b/pkg/render/policyrecommendation.go @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2023-2024 Tigera, Inc. 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. @@ -34,6 +34,7 @@ import ( "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/secret" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" "github.com/tigera/operator/pkg/tls/certificatemanagement" "github.com/tigera/operator/pkg/tls/certkeyusage" ) @@ -62,7 +63,7 @@ type PolicyRecommendationConfiguration struct { ClusterDomain string Installation *operatorv1.InstallationSpec ManagedCluster bool - Openshift bool + OpenShift bool PullSecrets []*corev1.Secret TrustedBundle certificatemanagement.TrustedBundle PolicyRecommendationCertSecret certificatemanagement.KeyPairInterface @@ -188,6 +189,15 @@ func (pr *policyRecommendationComponent) clusterRole() client.Object { }) } + if pr.cfg.OpenShift { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{securitycontextconstraints.HostNetworkV2}, + }) + } + return &rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{ @@ -349,7 +359,7 @@ func (pr *policyRecommendationComponent) allowTigeraPolicyForPolicyRecommendatio }) } - egressRules = networkpolicy.AppendDNSEgressRules(egressRules, pr.cfg.Openshift) + egressRules = networkpolicy.AppendDNSEgressRules(egressRules, pr.cfg.OpenShift) return &v3.NetworkPolicy{ TypeMeta: metav1.TypeMeta{Kind: "NetworkPolicy", APIVersion: "projectcalico.org/v3"}, diff --git a/pkg/render/policyrecommendation_test.go b/pkg/render/policyrecommendation_test.go index 86d0085677..ff83de04cf 100644 --- a/pkg/render/policyrecommendation_test.go +++ b/pkg/render/policyrecommendation_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2023-2024 Tigera, Inc. 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. @@ -85,7 +85,7 @@ var _ = Describe("Policy recommendation rendering tests", func() { }) It("should render all resources for a default configuration", func() { - cfg.Openshift = notOpenshift + cfg.OpenShift = false component := render.PolicyRecommendation(cfg) resources, _ := component.Objects() @@ -189,6 +189,22 @@ var _ = Describe("Policy recommendation rendering tests", func() { } }) + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { + cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift + cfg.OpenShift = true + component := render.PolicyRecommendation(cfg) + Expect(component.ResolveImages(nil)).To(BeNil()) + resources, _ := component.Objects() + + role := rtest.GetResource(resources, "tigera-policy-recommendation", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(role.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"use"}, + ResourceNames: []string{"hostnetwork-v2"}, + })) + }) + It("should apply controlPlaneNodeSelector correctly", func() { cfg.Installation = &operatorv1.InstallationSpec{ ControlPlaneNodeSelector: map[string]string{"foo": "bar"}, @@ -251,7 +267,7 @@ var _ = Describe("Policy recommendation rendering tests", func() { DescribeTable("should render allow-tigera policy", func(scenario testutils.AllowTigeraScenario) { cfg.ManagedCluster = scenario.ManagedCluster - cfg.Openshift = scenario.Openshift + cfg.OpenShift = scenario.OpenShift component := render.PolicyRecommendation(cfg) resources, _ := component.Objects() @@ -259,8 +275,8 @@ var _ = Describe("Policy recommendation rendering tests", func() { expectedPolicy := getExpectedPolicy(scenario) Expect(policy).To(Equal(expectedPolicy)) }, - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), ) }) diff --git a/pkg/render/testutils/policy.go b/pkg/render/testutils/policy.go index 500480764e..0ca49ed3bf 100644 --- a/pkg/render/testutils/policy.go +++ b/pkg/render/testutils/policy.go @@ -1,4 +1,4 @@ -// Copyright (c) 2022-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2022-2024 Tigera, Inc. 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. @@ -32,7 +32,7 @@ import ( // allow-tigera policies correctly adapt for each relevant potential case. Update if new scenarios arise. type AllowTigeraScenario struct { ManagedCluster bool - Openshift bool + OpenShift bool DPIEnabled bool } @@ -100,13 +100,13 @@ func SelectPolicyByClusterTypeAndProvider(scenario AllowTigeraScenario, managedOpenshiftPolicy *v3.NetworkPolicy, ) *v3.NetworkPolicy { switch scenario { - case AllowTigeraScenario{ManagedCluster: false, Openshift: false}: + case AllowTigeraScenario{ManagedCluster: false, OpenShift: false}: return unmanagedNoProviderPolicy - case AllowTigeraScenario{ManagedCluster: false, Openshift: true}: + case AllowTigeraScenario{ManagedCluster: false, OpenShift: true}: return unmanagedOpenshiftPolicy - case AllowTigeraScenario{ManagedCluster: true, Openshift: false}: + case AllowTigeraScenario{ManagedCluster: true, OpenShift: false}: return managedNoProviderPolicy - case AllowTigeraScenario{ManagedCluster: true, Openshift: true}: + case AllowTigeraScenario{ManagedCluster: true, OpenShift: true}: return managedOpenshiftPolicy default: return nil @@ -115,7 +115,7 @@ func SelectPolicyByClusterTypeAndProvider(scenario AllowTigeraScenario, // SelectPolicyByProvider simply selects a variant of a policy that varies depending on provider type only. func SelectPolicyByProvider(scenario AllowTigeraScenario, noProviderPolicy *v3.NetworkPolicy, openshiftProviderPolicy *v3.NetworkPolicy) *v3.NetworkPolicy { - if scenario.Openshift { + if scenario.OpenShift { return openshiftProviderPolicy } else { return noProviderPolicy diff --git a/pkg/render/tiers/tiers.go b/pkg/render/tiers/tiers.go index 0b5b7e6aa7..6836b5b69f 100644 --- a/pkg/render/tiers/tiers.go +++ b/pkg/render/tiers/tiers.go @@ -1,4 +1,4 @@ -// Copyright (c) 2022-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2022-2024 Tigera, Inc. 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. @@ -38,7 +38,7 @@ func Tiers(cfg *Config) render.Component { } type Config struct { - Openshift bool + OpenShift bool DNSEgressCIDRs DNSEgressCIDR // CalicoNamespaces contains a list of namespaces running Calico components. This must be @@ -106,7 +106,7 @@ func (t tiersComponent) allowTigeraTier() *v3.Tier { func (t tiersComponent) allowTigeraClusterDNSPolicy() *v3.NetworkPolicy { var dnsPolicySelector string var dnsPolicyNamespace string - if t.cfg.Openshift { + if t.cfg.OpenShift { dnsPolicySelector = "dns.operator.openshift.io/daemonset-dns == 'default'" dnsPolicyNamespace = "openshift-dns" } else { diff --git a/pkg/render/tiers/tiers_test.go b/pkg/render/tiers/tiers_test.go index da28456675..a30c86cd1d 100644 --- a/pkg/render/tiers/tiers_test.go +++ b/pkg/render/tiers/tiers_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2022-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2022-2024 Tigera, Inc. 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. @@ -65,7 +65,7 @@ var _ = Describe("Tiers rendering tests", func() { BeforeEach(func() { // Establish default config for test cases to override. cfg = &tiers.Config{ - Openshift: false, + OpenShift: false, DNSEgressCIDRs: getDNSEgressCIDRs(testutils.IPV4), CalicoNamespaces: []string{ common.CalicoNamespace, @@ -95,7 +95,7 @@ var _ = Describe("Tiers rendering tests", func() { getExpectedPolicy := func(name types.NamespacedName, scenario testutils.AllowTigeraScenario) *v3.NetworkPolicy { if name.Name == "allow-tigera.cluster-dns" && - ((scenario.Openshift && name.Namespace == "openshift-dns") || (!scenario.Openshift && name.Namespace == "kube-system")) { + ((scenario.OpenShift && name.Namespace == "openshift-dns") || (!scenario.OpenShift && name.Namespace == "kube-system")) { return testutils.SelectPolicyByProvider(scenario, clusterDNSPolicy, clusterDNSPolicyForOCP) } @@ -104,7 +104,7 @@ var _ = Describe("Tiers rendering tests", func() { DescribeTable("should render allow-tigera network policy", func(scenario testutils.AllowTigeraScenario) { - cfg.Openshift = scenario.Openshift + cfg.OpenShift = scenario.OpenShift component := tiers.Tiers(cfg) resourcesToCreate, _ := component.Objects() @@ -121,10 +121,10 @@ var _ = Describe("Tiers rendering tests", func() { } }, - Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: false}), - Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, Openshift: true}), - Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: false}), - Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, Openshift: true}), + Entry("for management/standalone, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: false}), + Entry("for management/standalone, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: false, OpenShift: true}), + Entry("for managed, kube-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: false}), + Entry("for managed, openshift-dns", testutils.AllowTigeraScenario{ManagedCluster: true, OpenShift: true}), ) }) diff --git a/pkg/render/typha.go b/pkg/render/typha.go index c68de2e732..78fa70b721 100644 --- a/pkg/render/typha.go +++ b/pkg/render/typha.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2019-2024 Tigera, Inc. 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. @@ -35,6 +35,7 @@ import ( rmeta "github.com/tigera/operator/pkg/render/common/meta" "github.com/tigera/operator/pkg/render/common/podsecuritypolicy" "github.com/tigera/operator/pkg/render/common/securitycontext" + "github.com/tigera/operator/pkg/render/common/securitycontextconstraints" ) const ( @@ -352,12 +353,12 @@ func (c *typhaComponent) typhaRole() *rbacv1.ClusterRole { ResourceNames: []string{common.TyphaDeploymentName}, }) } - if c.cfg.Installation.KubernetesProvider == operatorv1.ProviderOpenShift { + if c.cfg.Installation.KubernetesProvider.IsOpenShift() { role.Rules = append(role.Rules, rbacv1.PolicyRule{ APIGroups: []string{"security.openshift.io"}, Resources: []string{"securitycontextconstraints"}, Verbs: []string{"use"}, - ResourceNames: []string{PSSPrivileged}, + ResourceNames: []string{securitycontextconstraints.NonRootV2}, }) } return role diff --git a/pkg/render/typha_test.go b/pkg/render/typha_test.go index 1af62e378e..67043630e2 100644 --- a/pkg/render/typha_test.go +++ b/pkg/render/typha_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2023 Tigera, Inc. All rights reserved. +// Copyright (c) 2019-2024 Tigera, Inc. 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. @@ -90,7 +90,7 @@ var _ = Describe("Typha rendering tests", func() { } }) - It("should render security context constrains properly when provider is openshift", func() { + It("should render SecurityContextConstrains properly when provider is OpenShift", func() { cfg.Installation.KubernetesProvider = operatorv1.ProviderOpenShift component := render.Typha(&cfg) Expect(component.ResolveImages(nil)).To(BeNil()) @@ -102,7 +102,7 @@ var _ = Describe("Typha rendering tests", func() { APIGroups: []string{"security.openshift.io"}, Resources: []string{"securitycontextconstraints"}, Verbs: []string{"use"}, - ResourceNames: []string{"privileged"}, + ResourceNames: []string{"nonroot-v2"}, })) }) diff --git a/pkg/render/windows.go b/pkg/render/windows.go index f4c8f42758..2a9db112fc 100644 --- a/pkg/render/windows.go +++ b/pkg/render/windows.go @@ -826,7 +826,7 @@ func (c *windowsComponent) windowsDaemonset(cniCfgMap *corev1.ConfigMap) *appsv1 } var affinity *corev1.Affinity - if c.cfg.Installation.KubernetesProvider == operatorv1.ProviderAKS { + if c.cfg.Installation.KubernetesProvider.IsAKS() { affinity = &corev1.Affinity{ NodeAffinity: &corev1.NodeAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ @@ -840,7 +840,7 @@ func (c *windowsComponent) windowsDaemonset(cniCfgMap *corev1.ConfigMap) *appsv1 }, }, } - } else if c.cfg.Installation.KubernetesProvider == operatorv1.ProviderEKS { + } else if c.cfg.Installation.KubernetesProvider.IsEKS() { affinity = &corev1.Affinity{ NodeAffinity: &corev1.NodeAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{