Skip to content

Commit

Permalink
Update securityContext to include required configurations other than …
Browse files Browse the repository at this point in the history
…runAsUser and runAsGroup

As part of this PR, when the flag el-security-context is enabled the securityContext have all configuration
except RunAsUser and RunAsGroup
Added new fields default-run-as-user and default-run-as-group to config-defaults-triggers configmap so that RunAsUser and RunAsGroup can be now configured through CM
This change handles cases in environments where user ID 65532 is not allowed, such as OpenShift.

Signed-off-by: Savita Ashture <sashture@redhat.com>
  • Loading branch information
savitaashture committed May 30, 2024
1 parent f76be74 commit 7c5c79f
Show file tree
Hide file tree
Showing 14 changed files with 153 additions and 34 deletions.
2 changes: 2 additions & 0 deletions config/config-defaults-triggers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@ data:
# default-service-account contains the default service account name
# to use for TaskRun and PipelineRun, if none is specified.
default-service-account: "default"
default-run-as-user: "65532"
default-run-as-group: "65532"
38 changes: 37 additions & 1 deletion pkg/apis/config/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,28 @@ limitations under the License.
package config

import (
"fmt"
"os"
"strconv"

corev1 "k8s.io/api/core/v1"
)

const (
defaultServiceAccountKey = "default-service-account"
defaultRunAsUserKey = "default-run-as-user"
defaultRunAsGroupKey = "default-run-as-group"
DefaultServiceAccountValue = "default"
defaultRunAsUserValue = 65532
defaultRunAsGroupValue = 65532
)

// Defaults holds the default configurations
// +k8s:deepcopy-gen=true
type Defaults struct {
DefaultServiceAccount string
DefaultRunAsUser int64
DefaultRunAsGroup int64
}

// GetDefaultsConfigName returns the name of the configmap containing all
Expand All @@ -52,19 +60,47 @@ func (cfg *Defaults) Equals(other *Defaults) bool {
return false
}

return other.DefaultServiceAccount == cfg.DefaultServiceAccount
return other.DefaultServiceAccount == cfg.DefaultServiceAccount &&
other.DefaultRunAsUser == cfg.DefaultRunAsUser &&
other.DefaultRunAsGroup == cfg.DefaultRunAsGroup
}

// NewDefaultsFromMap returns a Config given a map corresponding to a ConfigMap
func NewDefaultsFromMap(cfgMap map[string]string) (*Defaults, error) {
tc := Defaults{
DefaultServiceAccount: DefaultServiceAccountValue,
DefaultRunAsUser: defaultRunAsUserValue,
DefaultRunAsGroup: defaultRunAsGroupValue,
}

if defaultServiceAccount, ok := cfgMap[defaultServiceAccountKey]; ok {
tc.DefaultServiceAccount = defaultServiceAccount
}

if defaultRunAsUser, ok := cfgMap[defaultRunAsUserKey]; ok {
if defaultRunAsUser == "" {
tc.DefaultRunAsUser = 0
} else {
runAsUser, err := strconv.ParseInt(defaultRunAsUser, 10, 0)
if err != nil {
return nil, fmt.Errorf("failed parsing runAsUser config %q", defaultRunAsUser)
}
tc.DefaultRunAsUser = runAsUser
}
}

if defaultRunAsGroup, ok := cfgMap[defaultRunAsGroupKey]; ok {
if defaultRunAsGroup == "" {
tc.DefaultRunAsGroup = 0
} else {
runAsGroup, err := strconv.ParseInt(defaultRunAsGroup, 10, 0)
if err != nil {
return nil, fmt.Errorf("failed parsing runAsUser config %q", defaultRunAsGroup)
}
tc.DefaultRunAsGroup = runAsGroup
}
}

return &tc, nil
}

Expand Down
14 changes: 14 additions & 0 deletions pkg/apis/config/default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ func TestNewDefaultsFromConfigMap(t *testing.T) {
{
expectedConfig: &config.Defaults{
DefaultServiceAccount: "default",
DefaultRunAsUser: 65532,
DefaultRunAsGroup: 65532,
},
fileName: config.GetDefaultsConfigName(),
},
Expand All @@ -54,10 +56,22 @@ func TestNewDefaultsFromEmptyConfigMap(t *testing.T) {
DefaultsConfigEmptyName := "config-defaults-empty"
expectedConfig := &config.Defaults{
DefaultServiceAccount: "default",
DefaultRunAsUser: 65532,
DefaultRunAsGroup: 65532,
}
verifyConfigFileWithExpectedConfig(t, DefaultsConfigEmptyName, expectedConfig)
}

func TestNewDefaultsFromConfigMapWithEmptyVal(t *testing.T) {
DefaultsConfigEmptyVal := "config-defaults-triggers-empty-val"
expectedConfig := &config.Defaults{
DefaultServiceAccount: "default",
DefaultRunAsUser: 0,
DefaultRunAsGroup: 0,
}
verifyConfigFileWithExpectedConfig(t, DefaultsConfigEmptyVal, expectedConfig)
}

func TestEquals(t *testing.T) {
testCases := []struct {
name string
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/config/testdata/config-defaults-empty.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ data:
# default-timeout-minutes contains the default number of
# minutes to use for TaskRun, if none is specified.
default-service-accounts: "default"
default-run-as-user: "65532"
default-run-as-group: "65532"
24 changes: 24 additions & 0 deletions pkg/apis/config/testdata/config-defaults-triggers-empty-val.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2021 The Tekton Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://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.

apiVersion: v1
kind: ConfigMap
metadata:
name: config-defaults-triggers
namespace: tekton-pipelines
data:
default-service-account: "default"
default-run-as-user: ""
default-run-as-group: ""

3 changes: 3 additions & 0 deletions pkg/apis/config/testdata/config-defaults-triggers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ metadata:
namespace: tekton-pipelines
data:
default-service-account: "default"
default-run-as-user: "65532"
default-run-as-group: "65532"

15 changes: 9 additions & 6 deletions pkg/reconciler/eventlistener/eventlistener.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"strings"
"sync"

"github.com/tektoncd/triggers/pkg/apis/config"
"github.com/tektoncd/triggers/pkg/apis/triggers/contexts"
"github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1"
triggersclientset "github.com/tektoncd/triggers/pkg/client/clientset/versioned"
Expand Down Expand Up @@ -108,10 +109,12 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, el *v1beta1.EventListene
// and may not have had all of the assumed default specified.
el.SetDefaults(contexts.WithUpgradeViaDefaulting(ctx))

cfg := config.FromContextOrDefaults(ctx)

if el.Spec.Resources.CustomResource != nil {
return r.reconcileCustomObject(ctx, el)
return r.reconcileCustomObject(ctx, el, cfg)
}
deploymentReconcileError := r.reconcileDeployment(ctx, el)
deploymentReconcileError := r.reconcileDeployment(ctx, el, cfg)
serviceReconcileError := r.reconcileService(ctx, el)
if el.Spec.Resources.CustomResource == nil {
el.Status.SetReadyCondition()
Expand Down Expand Up @@ -184,8 +187,8 @@ func (r *Reconciler) reconcileService(ctx context.Context, el *v1beta1.EventList
return nil
}

func (r *Reconciler) reconcileDeployment(ctx context.Context, el *v1beta1.EventListener) error {
deployment, err := resources.MakeDeployment(ctx, el, r.configAcc, r.config)
func (r *Reconciler) reconcileDeployment(ctx context.Context, el *v1beta1.EventListener, cfg *config.Config) error {
deployment, err := resources.MakeDeployment(ctx, el, r.configAcc, r.config, cfg)
if err != nil {
logging.FromContext(ctx).Error(err)
return err
Expand Down Expand Up @@ -247,8 +250,8 @@ func (r *Reconciler) reconcileDeployment(ctx context.Context, el *v1beta1.EventL
return nil
}

func (r *Reconciler) reconcileCustomObject(ctx context.Context, el *v1beta1.EventListener) error {
data, err := resources.MakeCustomObject(ctx, el, r.configAcc, r.config)
func (r *Reconciler) reconcileCustomObject(ctx context.Context, el *v1beta1.EventListener, cfg *config.Config) error {
data, err := resources.MakeCustomObject(ctx, el, r.configAcc, r.config, cfg)
if err != nil {
logging.FromContext(ctx).Errorf("unable to construct custom object", err)
return err
Expand Down
34 changes: 30 additions & 4 deletions pkg/reconciler/eventlistener/eventlistener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,10 @@ func TestReconcile(t *testing.T) {
c.SetSecurityContext = ptr.Bool(false)
})

configWithSetSecurityContext := resources.MakeConfig(func(c *resources.Config) {
c.SetSecurityContext = ptr.Bool(true)
})

configWithSetEventListenerEventEnable := resources.MakeConfig(func(c *resources.Config) {
c.SetEventListenerEvent = ptr.String("enable")
})
Expand Down Expand Up @@ -889,7 +893,28 @@ func TestReconcile(t *testing.T) {

deploymentMissingSecurityContext := makeDeployment(func(d *appsv1.Deployment) {
d.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{}
d.Spec.Template.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{}
d.Spec.Template.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{
RunAsUser: ptr.Int64(65532),
RunAsGroup: ptr.Int64(65532),
}
})

deploymentWithSecurityContext := makeDeployment(func(d *appsv1.Deployment) {
d.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{
RunAsNonRoot: ptr.Bool(true),
}
d.Spec.Template.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{
AllowPrivilegeEscalation: ptr.Bool(false),
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{"ALL"},
},
RunAsNonRoot: ptr.Bool(true),
RunAsUser: ptr.Int64(65532),
RunAsGroup: ptr.Int64(65532),
SeccompProfile: &corev1.SeccompProfile{
Type: corev1.SeccompProfileTypeRuntimeDefault,
},
}
})

deploymentEventListenerEvent := makeDeployment(func(d *appsv1.Deployment) {
Expand Down Expand Up @@ -1332,12 +1357,13 @@ func TestReconcile(t *testing.T) {
Services: []*corev1.Service{elServiceWithTLSConnection},
},
}, {
name: "eventlistener with security context",
key: reconcileKey,
name: "eventlistener with security context",
key: reconcileKey,
config: configWithSetSecurityContext,
startResources: test.Resources{
Namespaces: []*corev1.Namespace{namespaceResource},
EventListeners: []*v1beta1.EventListener{elWithStatus},
Deployments: []*appsv1.Deployment{deploymentMissingSecurityContext},
Deployments: []*appsv1.Deployment{deploymentWithSecurityContext},
},
endResources: test.Resources{
Namespaces: []*corev1.Namespace{namespaceResource},
Expand Down
9 changes: 5 additions & 4 deletions pkg/reconciler/eventlistener/resources/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package resources
import (
"strconv"

"github.com/tektoncd/triggers/pkg/apis/config"
"github.com/tektoncd/triggers/pkg/apis/triggers"
"github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1"
corev1 "k8s.io/api/core/v1"
Expand All @@ -28,7 +29,7 @@ import (

type ContainerOption func(*corev1.Container)

func MakeContainer(el *v1beta1.EventListener, configAcc reconcilersource.ConfigAccessor, c Config, opts ...ContainerOption) corev1.Container {
func MakeContainer(el *v1beta1.EventListener, configAcc reconcilersource.ConfigAccessor, c Config, cfg *config.Config, opts ...ContainerOption) corev1.Container {
isMultiNS := false
if len(el.Spec.NamespaceSelector.MatchNames) != 0 {
isMultiNS = true
Expand Down Expand Up @@ -56,16 +57,16 @@ func MakeContainer(el *v1beta1.EventListener, configAcc reconcilersource.ConfigA
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{"ALL"},
},
// 65532 is the distroless nonroot user ID
RunAsUser: ptr.Int64(65532),
RunAsGroup: ptr.Int64(65532),
RunAsNonRoot: ptr.Bool(true),
SeccompProfile: &corev1.SeccompProfile{
Type: corev1.SeccompProfileTypeRuntimeDefault,
},
}
}

containerSecurityContext.RunAsUser = ptr.Int64(cfg.Defaults.DefaultRunAsUser)
containerSecurityContext.RunAsGroup = ptr.Int64(cfg.Defaults.DefaultRunAsGroup)

container := corev1.Container{
Name: "event-listener",
Image: *c.Image,
Expand Down
5 changes: 4 additions & 1 deletion pkg/reconciler/eventlistener/resources/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ limitations under the License.
package resources

import (
"context"
"strconv"
"testing"

"github.com/google/go-cmp/cmp"
cfg "github.com/tektoncd/triggers/pkg/apis/config"
"github.com/tektoncd/triggers/pkg/apis/triggers"
"github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1"
corev1 "k8s.io/api/core/v1"
Expand All @@ -35,6 +37,7 @@ func TestContainer(t *testing.T) {
tests := []struct {
name string
el *v1beta1.EventListener
cm cfg.Config
want corev1.Container
opts []ContainerOption
}{{
Expand Down Expand Up @@ -480,7 +483,7 @@ func TestContainer(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := MakeContainer(tt.el, &reconcilersource.EmptyVarsGenerator{}, config, tt.opts...)
got := MakeContainer(tt.el, &reconcilersource.EmptyVarsGenerator{}, config, cfg.FromContextOrDefaults(context.Background()), tt.opts...)
if diff := cmp.Diff(tt.want, got); diff != "" {
t.Errorf("MakeContainer() did not return expected. -want, +got: %s", diff)
}
Expand Down
5 changes: 3 additions & 2 deletions pkg/reconciler/eventlistener/resources/custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"os"
"reflect"

"github.com/tektoncd/triggers/pkg/apis/config"
"github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -32,7 +33,7 @@ import (
"knative.dev/pkg/kmeta"
)

func MakeCustomObject(ctx context.Context, el *v1beta1.EventListener, configAcc reconcilersource.ConfigAccessor, c Config) (*unstructured.Unstructured, error) {
func MakeCustomObject(ctx context.Context, el *v1beta1.EventListener, configAcc reconcilersource.ConfigAccessor, c Config, cfg *config.Config) (*unstructured.Unstructured, error) {
original := &duckv1.WithPod{}
decoder := json.NewDecoder(bytes.NewBuffer(el.Spec.Resources.CustomResource.Raw))
if err := decoder.Decode(&original); err != nil {
Expand All @@ -47,7 +48,7 @@ func MakeCustomObject(ctx context.Context, el *v1beta1.EventListener, configAcc
namespace = el.GetNamespace()
}

container := MakeContainer(el, configAcc, c, func(c *corev1.Container) {
container := MakeContainer(el, configAcc, c, cfg, func(c *corev1.Container) {
// handle env and resources for custom object
if len(original.Spec.Template.Spec.Containers) == 1 {
c.Env = append(c.Env, original.Spec.Template.Spec.Containers[0].Env...)
Expand Down
6 changes: 4 additions & 2 deletions pkg/reconciler/eventlistener/resources/custom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
cfg "github.com/tektoncd/triggers/pkg/apis/config"
"github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
Expand Down Expand Up @@ -448,7 +449,8 @@ func TestCustomObject(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := MakeCustomObject(context.Background(), tt.el, &reconcilersource.EmptyVarsGenerator{}, config)
got, err := MakeCustomObject(context.Background(), tt.el, &reconcilersource.EmptyVarsGenerator{}, config,
cfg.FromContextOrDefaults(context.Background()))
if err != nil {
t.Fatalf("MakeCustomObject() = %v", err)
}
Expand All @@ -471,7 +473,7 @@ func TestCustomObjectError(t *testing.T) {
Raw: []byte(`garbage`),
},
}
}), &reconcilersource.EmptyVarsGenerator{}, config)
}), &reconcilersource.EmptyVarsGenerator{}, config, cfg.FromContextOrDefaults(context.Background()))
if err == nil {
t.Fatalf("MakeCustomObject() = %v, wanted error", got)
}
Expand Down
Loading

0 comments on commit 7c5c79f

Please sign in to comment.