Skip to content

Commit

Permalink
Apply more restrictive SecurityContext to components
Browse files Browse the repository at this point in the history
This changeset add more restrictive SecurityContext to Calico opensource
and enterprise components. Least privilege principle is applied to this
change.

Data plane components are mostly running as root user and application
layer compoennts are running as non-root users. `Capabilities` and
`SeccompProfile` are also filled with sensible defaults in
`SecurityContext`s.
  • Loading branch information
hjiawei committed Feb 2, 2023
1 parent de2269e commit 340fae0
Show file tree
Hide file tree
Showing 52 changed files with 1,231 additions and 668 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ $(BINDIR)/operator-$(ARCH): $(SRC_FILES)
mkdir -p $(BINDIR)
$(CONTAINERIZED) -e CGO_ENABLED=$(CGO_ENABLED) $(CALICO_BUILD) \
sh -c '$(GIT_CONFIG_SSH) \
go build -v -i -o $(BINDIR)/operator-$(ARCH) -ldflags "-X $(PACKAGE_NAME)/version.VERSION=$(GIT_VERSION) -w" ./main.go'
go build -v -o $(BINDIR)/operator-$(ARCH) -ldflags "-X $(PACKAGE_NAME)/version.VERSION=$(GIT_VERSION) -w" ./main.go'

.PHONY: image
image: build $(BUILD_IMAGE)
Expand Down
1 change: 0 additions & 1 deletion build/Dockerfile

This file was deleted.

2 changes: 1 addition & 1 deletion build/Dockerfile.amd64
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ LABEL name="Tigera Operator" \
maintainer="[email protected]>"

ENV OPERATOR=/usr/local/bin/operator \
USER_UID=1001
USER_UID=10001

# Install operator binary
COPY build/_output/bin/operator-amd64 ${OPERATOR}
Expand Down
2 changes: 1 addition & 1 deletion build/Dockerfile.arm64
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ LABEL name="Calico Operator" \
maintainer="Laurence Man <[email protected]>"

ENV OPERATOR=/usr/local/bin/operator \
USER_UID=1001
USER_UID=10001

# install operator binary
COPY build/_output/bin/operator-arm64 ${OPERATOR}
Expand Down
2 changes: 1 addition & 1 deletion build/Dockerfile.ppc64le
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ LABEL name="Tigera Operator" \
maintainer="[email protected]>"

ENV OPERATOR=/usr/local/bin/operator \
USER_UID=1001
USER_UID=10001

# Install operator binary
COPY build/_output/bin/operator-ppc64le ${OPERATOR}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2020-2022 Tigera, Inc. All rights reserved.
// Copyright (c) 2020-2023 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.
Expand All @@ -23,21 +23,22 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"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"
"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/utils"
"github.com/tigera/operator/pkg/controller/utils/imageset"
"github.com/tigera/operator/pkg/render"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

const ResourceName = "amazon-cloud-integration"
Expand All @@ -57,10 +58,11 @@ func Add(mgr manager.Manager, opts options.AddOptions) error {
// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager, opts options.AddOptions) reconcile.Reconciler {
r := &ReconcileAmazonCloudIntegration{
client: mgr.GetClient(),
scheme: mgr.GetScheme(),
provider: opts.DetectedProvider,
status: status.New(mgr.GetClient(), "amazon-cloud-integration", opts.KubernetesVersion),
client: mgr.GetClient(),
scheme: mgr.GetScheme(),
provider: opts.DetectedProvider,
status: status.New(mgr.GetClient(), "amazon-cloud-integration", opts.KubernetesVersion),
clusterDomain: opts.ClusterDomain,
}
r.status.Run(opts.ShutdownContext)
return r
Expand All @@ -71,7 +73,7 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error {
// Create a new controller
c, err := controller.New("amazoncloudintegration-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
return fmt.Errorf("Failed to create amazoncloudintegration-controller: %v", err)
return fmt.Errorf("failed to create amazoncloudintegration-controller: %v", err)
}

// Watch for changes to primary resource AmazonCloudIntegration
Expand Down Expand Up @@ -112,10 +114,11 @@ var _ reconcile.Reconciler = &ReconcileAmazonCloudIntegration{}
type ReconcileAmazonCloudIntegration struct {
// This client, initialized using mgr.Client() above, is a split client
// that reads objects from the cache and writes to the apiserver
client client.Client
scheme *runtime.Scheme
provider operatorv1.Provider
status status.StatusManager
client client.Client
scheme *runtime.Scheme
provider operatorv1.Provider
status status.StatusManager
clusterDomain string
}

// Reconcile reads that state of the cluster for a AmazonCloudIntegration object and makes changes based on the state read
Expand Down Expand Up @@ -187,7 +190,7 @@ func (r *ReconcileAmazonCloudIntegration) Reconcile(ctx context.Context, request
return reconcile.Result{}, err
}
if variant != operatorv1.TigeraSecureEnterprise {
r.status.SetDegraded(operatorv1.ResourceNotReady, "", fmt.Errorf("Waiting for network to be %s", operatorv1.TigeraSecureEnterprise), reqLogger)
r.status.SetDegraded(operatorv1.ResourceNotReady, "", fmt.Errorf("waiting for network to be %s", operatorv1.TigeraSecureEnterprise), reqLogger)
return reconcile.Result{}, nil
}

Expand All @@ -203,6 +206,19 @@ func (r *ReconcileAmazonCloudIntegration) Reconcile(ctx context.Context, request
return reconcile.Result{}, err
}

certificateManager, err := certificatemanager.Create(r.client, network, r.clusterDomain)
if err != nil {
r.status.SetDegraded(operatorv1.ResourceCreateError, "Unable to create the Tigera CA", err, reqLogger)
return reconcile.Result{}, err
}

// cloud controllers need to trust a public CA, so we mount all the system certificates.
trustedBundle, err := certificateManager.CreateTrustedBundleWithSystemRootCertificates()
if err != nil {
r.status.SetDegraded(operatorv1.ResourceCreateError, "Unable to create tigera-ca-bundle configmap", err, reqLogger)
return reconcile.Result{}, err
}

// Create a component handler to manage the rendered component.
handler := utils.NewComponentHandler(log, r.client, r.scheme, instance)

Expand All @@ -213,7 +229,7 @@ func (r *ReconcileAmazonCloudIntegration) Reconcile(ctx context.Context, request
Installation: network,
Credentials: awsCredential,
PullSecrets: pullSecrets,
Openshift: r.provider == operatorv1.ProviderOpenShift,
TrustedBundle: trustedBundle,
}
component := render.AmazonCloudIntegration(amazonCloudIntegrationCfg)

Expand Down Expand Up @@ -252,7 +268,7 @@ func getAmazonCredential(client client.Client) (*render.AmazonCredential, error)
Namespace: common.OperatorNamespace(),
}
if err := client.Get(context.Background(), secretNamespacedName, secret); err != nil {
return nil, fmt.Errorf("Failed to read secret %q: %s", render.AmazonCloudIntegrationCredentialName, err)
return nil, fmt.Errorf("failed to read secret %q: %s", render.AmazonCloudIntegrationCredentialName, err)
}

return render.ConvertSecretToCredential(secret)
Expand Down
13 changes: 5 additions & 8 deletions pkg/controller/migration/convert/aws_cni_policy_only_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2022 Tigera, Inc. All rights reserved.
// Copyright (c) 2022-2023 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.
Expand All @@ -20,13 +20,13 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"

"github.com/tigera/operator/pkg/render/common/securitycontext"
)

func awsCNIPolicyOnlyConfig() []runtime.Object {
fileOrCreate := corev1.HostPathFileOrCreate
isPrivileged := true
_true := true
_false := false
var terminationGracePeriod int64 = 0
maxUnav := intstr.FromInt(1)
updateStrat := appsv1.RollingUpdateDaemonSet{MaxUnavailable: &maxUnav}
Expand Down Expand Up @@ -87,7 +87,7 @@ func awsCNIPolicyOnlyConfig() []runtime.Object {
{Name: "IP", Value: ""},
{Name: "FELIX_HEALTHENABLED", Value: "true"},
},
SecurityContext: &corev1.SecurityContext{Privileged: &isPrivileged},
SecurityContext: securitycontext.NewRootContext(isPrivileged),
LivenessProbe: &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{Exec: &corev1.ExecAction{Command: []string{"/bin/calico-node", "-felix-live"}}},
PeriodSeconds: 10,
Expand Down Expand Up @@ -179,10 +179,7 @@ func awsCNIPolicyOnlyConfig() []runtime.Object {
PeriodSeconds: 30,
InitialDelaySeconds: 30,
},
SecurityContext: &corev1.SecurityContext{
AllowPrivilegeEscalation: &_false,
RunAsNonRoot: &_true,
},
SecurityContext: securitycontext.NewNonRootContext(),
ReadinessProbe: &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
HTTPGet: &corev1.HTTPGetAction{
Expand Down
12 changes: 7 additions & 5 deletions pkg/controller/migration/convert/calico_default_config_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2020-2022 Tigera, Inc. All rights reserved.
// Copyright (c) 2020-2023 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.
Expand All @@ -21,6 +21,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"

"github.com/tigera/operator/pkg/render/common/securitycontext"
)

func calicoDefaultConfig() []runtime.Object {
Expand Down Expand Up @@ -128,7 +130,7 @@ func calicoDefaultConfig() []runtime.Object {
{MountPath: "/var/lib/cni/networks", Name: "host-local-net-dir"},
{MountPath: "/host/opt/cni/bin", Name: "cni-bin-dir"},
},
SecurityContext: &corev1.SecurityContext{Privileged: &isPrivileged},
SecurityContext: securitycontext.NewRootContext(isPrivileged),
}, {
Name: "install-cni",
Image: "calico/cni:v3.15.1",
Expand Down Expand Up @@ -162,14 +164,14 @@ func calicoDefaultConfig() []runtime.Object {
{MountPath: "/host/opt/cni/bin", Name: "cni-bin-dir"},
{MountPath: "/host/etc/cni/net.d", Name: "cni-net-dir"},
},
SecurityContext: &corev1.SecurityContext{Privileged: &isPrivileged},
SecurityContext: securitycontext.NewRootContext(isPrivileged),
}, {
Name: "flexvol-driver",
Image: "calico/pod2daemon-flexvol:v3.15.1",
VolumeMounts: []corev1.VolumeMount{
{MountPath: "/host/driver", Name: "flexvol-driver-host"},
},
SecurityContext: &corev1.SecurityContext{Privileged: &isPrivileged},
SecurityContext: securitycontext.NewRootContext(isPrivileged),
}},
Containers: []corev1.Container{{
Name: "calico-node",
Expand Down Expand Up @@ -241,7 +243,7 @@ func calicoDefaultConfig() []runtime.Object {
{Name: "FELIX_LOGSEVERITYSCREEN", Value: "info"},
{Name: "FELIX_HEALTHENABLED", Value: "true"},
},
SecurityContext: &corev1.SecurityContext{Privileged: &isPrivileged},
SecurityContext: securitycontext.NewRootContext(isPrivileged),
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("250m"),
Expand Down
32 changes: 18 additions & 14 deletions pkg/render/amazoncloudintegration.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2019-2022 Tigera, Inc. All rights reserved.
// Copyright (c) 2019-2023 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.
Expand All @@ -18,8 +18,6 @@ import (
"fmt"
"strings"

"github.com/tigera/operator/pkg/render/common/secret"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
Expand All @@ -28,8 +26,10 @@ import (

operatorv1 "github.com/tigera/operator/api/v1"
"github.com/tigera/operator/pkg/components"
"github.com/tigera/operator/pkg/ptr"
rmeta "github.com/tigera/operator/pkg/render/common/meta"
"github.com/tigera/operator/pkg/render/common/secret"
"github.com/tigera/operator/pkg/render/common/securitycontext"
"github.com/tigera/operator/pkg/tls/certificatemanagement"
)

const (
Expand All @@ -51,7 +51,7 @@ type AmazonCloudIntegrationConfiguration struct {
Installation *operatorv1.InstallationSpec
Credentials *AmazonCredential
PullSecrets []*corev1.Secret
Openshift bool
TrustedBundle certificatemanagement.TrustedBundle
}

type amazonCloudIntegrationComponent struct {
Expand Down Expand Up @@ -79,7 +79,7 @@ type AmazonCredential struct {

func ConvertSecretToCredential(s *corev1.Secret) (*AmazonCredential, error) {
if s == nil {
return nil, fmt.Errorf("No secret specified")
return nil, fmt.Errorf("no secret specified")
}

missingKey := []string{}
Expand Down Expand Up @@ -114,6 +114,7 @@ func (c *amazonCloudIntegrationComponent) Objects() ([]client.Object, []client.O
c.clusterRole(),
c.clusterRoleBinding(),
c.credentialSecret(),
c.cfg.TrustedBundle.ConfigMap(AmazonCloudIntegrationNamespace),
c.deployment(),
)

Expand Down Expand Up @@ -224,6 +225,9 @@ func (c *amazonCloudIntegrationComponent) deployment() *appsv1.Deployment {

annotations := make(map[string]string)
annotations[credentialSecretHashAnnotation] = rmeta.AnnotationHash(c.cfg.Credentials)
for k, v := range c.cfg.TrustedBundle.HashAnnotations() {
annotations[k] = v
}

d := &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{Kind: "Deployment", APIVersion: "apps/v1"},
Expand All @@ -250,6 +254,9 @@ func (c *amazonCloudIntegrationComponent) deployment() *appsv1.Deployment {
Containers: []corev1.Container{
c.container(),
},
Volumes: []corev1.Volume{
c.cfg.TrustedBundle.Volume(),
},
},
},
},
Expand Down Expand Up @@ -293,14 +300,10 @@ func (c *amazonCloudIntegrationComponent) container() corev1.Container {
}

return corev1.Container{
Name: AmazonCloudIntegrationComponentName,
Image: c.image,
Env: env,
// Needed for permissions to write to the audit log
SecurityContext: &corev1.SecurityContext{
RunAsNonRoot: ptr.BoolToPtr(true),
AllowPrivilegeEscalation: ptr.BoolToPtr(false),
},
Name: AmazonCloudIntegrationComponentName,
Image: c.image,
Env: env,
SecurityContext: securitycontext.NewNonRootContext(),
ReadinessProbe: &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
Exec: &corev1.ExecAction{
Expand All @@ -314,6 +317,7 @@ func (c *amazonCloudIntegrationComponent) container() corev1.Container {
PeriodSeconds: 10,
FailureThreshold: 3,
},
VolumeMounts: []corev1.VolumeMount{c.cfg.TrustedBundle.VolumeMount(c.SupportedOSType())},
}
}

Expand Down
Loading

0 comments on commit 340fae0

Please sign in to comment.