Skip to content

Commit

Permalink
feat: remove managed fields and sensitive env vars from objects (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
saumas authored Aug 3, 2021
1 parent 0e316c1 commit 2eb562e
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 1 deletion.
1 change: 0 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1 h1:ocYkMQY5RrXTYgXl7ICpV0IXwlEQGwKIsery4gyXa1U=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
Expand Down
72 changes: 72 additions & 0 deletions internal/services/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package controller
import (
"context"
"reflect"
"regexp"
"sync"
"time"

Expand All @@ -11,6 +12,7 @@ import (
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
storagev1 "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/informers"
Expand All @@ -24,6 +26,12 @@ import (
"castai-agent/pkg/labels"
)

var (
// sensitiveValuePattern matches strings which are usually used to name variables holding sensitive values, like
// passwords. This is a case-insensitive match of the listed words.
sensitiveValuePattern = regexp.MustCompile(`(?i)passwd|pass|password|pwd|secret|token|key`)
)

type Controller struct {
log logrus.FieldLogger
clusterID string
Expand Down Expand Up @@ -172,12 +180,76 @@ func genericHandler(
return
}

cleanObj(obj)

queue.Add(&item{
obj: obj.(runtime.Object),
event: event,
})
}

// cleanObj removes unnecessary or sensitive values from K8s objects.
func cleanObj(obj interface{}) {
removeManagedFields(obj)
removeSensitiveEnvVars(obj)
}

func removeManagedFields(obj interface{}) {
if metaobj, ok := obj.(metav1.Object); ok {
metaobj.SetManagedFields(nil)
}
}

func removeSensitiveEnvVars(obj interface{}) {
var containers []*corev1.Container

switch o := obj.(type) {
case *corev1.Pod:
for i := range o.Spec.Containers {
containers = append(containers, &o.Spec.Containers[i])
}
case *appsv1.Deployment:
for i := range o.Spec.Template.Spec.Containers {
containers = append(containers, &o.Spec.Template.Spec.Containers[i])
}
case *appsv1.StatefulSet:
for i := range o.Spec.Template.Spec.Containers {
containers = append(containers, &o.Spec.Template.Spec.Containers[i])
}
case *appsv1.ReplicaSet:
for i := range o.Spec.Template.Spec.Containers {
containers = append(containers, &o.Spec.Template.Spec.Containers[i])
}
case *appsv1.DaemonSet:
for i := range o.Spec.Template.Spec.Containers {
containers = append(containers, &o.Spec.Template.Spec.Containers[i])
}
}

if len(containers) == 0 {
return
}

isSensitiveEnvVar := func(envVar corev1.EnvVar) bool {
if envVar.Value == "" {
return false
}
return sensitiveValuePattern.MatchString(envVar.Name)
}

for _, c := range containers {
validIdx := 0
for _, envVar := range c.Env {
if isSensitiveEnvVar(envVar) {
continue
}
c.Env[validIdx] = envVar
validIdx++
}
c.Env = c.Env[:validIdx]
}
}

func (c *Controller) Run(ctx context.Context) {
defer c.queue.ShutDown()

Expand Down
148 changes: 148 additions & 0 deletions internal/services/controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/google/uuid"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
Expand Down Expand Up @@ -94,3 +95,150 @@ func Test(t *testing.T) {
}
}, 10*time.Millisecond, ctx.Done())
}

func TestCleanObj(t *testing.T) {
tests := []struct {
name string
obj interface{}
matcher func(t *testing.T, obj interface{})
}{
{
name: "should clean managed fields",
obj: &v1.Pod{ObjectMeta: metav1.ObjectMeta{
ManagedFields: []metav1.ManagedFieldsEntry{
{
Manager: "mngr",
Operation: "op",
APIVersion: "v",
FieldsType: "t",
},
},
}},
matcher: func(t *testing.T, obj interface{}) {
pod := obj.(*v1.Pod)
require.Nil(t, pod.ManagedFields)
},
},
{
name: "should remove sensitive env vars from pod",
obj: &v1.Pod{Spec: v1.PodSpec{Containers: []v1.Container{
{
Env: []v1.EnvVar{
{
Name: "LOG_LEVEL",
Value: "5",
},
{
Name: "PASSWORD",
Value: "secret",
},
{
Name: "TOKEN",
ValueFrom: &v1.EnvVarSource{
SecretKeyRef: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
},
},
},
},
},
{
Env: []v1.EnvVar{
{
Name: "PWD",
Value: "super_secret",
},
},
},
}}},
matcher: func(t *testing.T, obj interface{}) {
pod := obj.(*v1.Pod)

require.Equal(t, []v1.EnvVar{
{
Name: "LOG_LEVEL",
Value: "5",
},
{
Name: "TOKEN",
ValueFrom: &v1.EnvVarSource{
SecretKeyRef: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
},
},
},
}, pod.Spec.Containers[0].Env)

require.Empty(t, pod.Spec.Containers[1].Env)
},
},
{
name: "should remove sensitive env vars from statefulset",
obj: &appsv1.StatefulSet{Spec: appsv1.StatefulSetSpec{Template: v1.PodTemplateSpec{Spec: v1.PodSpec{Containers: []v1.Container{
{
Env: []v1.EnvVar{
{
Name: "LOG_LEVEL",
Value: "5",
},
{
Name: "PASSWORD",
Value: "secret",
},
{
Name: "TOKEN",
ValueFrom: &v1.EnvVarSource{
SecretKeyRef: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
},
},
},
},
},
{
Env: []v1.EnvVar{
{
Name: "PWD",
Value: "super_secret",
},
},
},
}}}}},
matcher: func(t *testing.T, obj interface{}) {
sts := obj.(*appsv1.StatefulSet)

require.Equal(t, []v1.EnvVar{
{
Name: "LOG_LEVEL",
Value: "5",
},
{
Name: "TOKEN",
ValueFrom: &v1.EnvVarSource{
SecretKeyRef: &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "secret",
},
},
},
},
}, sts.Spec.Template.Spec.Containers[0].Env)

require.Empty(t, sts.Spec.Template.Spec.Containers[1].Env)
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
cleanObj(test.obj)
test.matcher(t, test.obj)
})
}
}

0 comments on commit 2eb562e

Please sign in to comment.