From 3de9493b686fa6d43cd91a44e2884b5a22d8e9cc Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Fri, 15 Mar 2019 17:31:47 -0400 Subject: [PATCH] initial implementation of updated configuration syntax --- config.yml | 138 +++++++++++++++++++++++++++--------- pkg/config/config.go | 95 ++++++++++++++++--------- pkg/config/config_test.go | 144 ++++++++++++++++++++++++-------------- 3 files changed, 260 insertions(+), 117 deletions(-) diff --git a/config.yml b/config.yml index e7fd307c..03ab94d3 100644 --- a/config.yml +++ b/config.yml @@ -1,35 +1,107 @@ resources: - requests: - cpu: - min: 50m - max: 1 - memory: - min: 100M - max: 3G - limits: - cpu: - min: 150m - max: 2 - memory: - min: 150M - max: 4G -healthChecks: - readiness: - require: true - liveness: - require: true + cpuRequests: + onAbsence: warning + warning: + below: 50m + above: 1000m + error: + below: 500m + above: 2000m + cpuLimits: + onAbsence: warning + warning: + below: 50m + above: 1000m + error: + below: 500m + above: 2000m + memoryRequests: + onAbsence: warning + warning: + below: 50M + above: 2G + error: + below: 100M + above: 4G + memoryLimits: + onAbsence: warning + warning: + below: 50M + above: 2G + error: + below: 100M + above: 4G +resources: + cpuRequests: + error: + below: 100m + above: 1 + warning: + below: 200m + above: 800m + memoryRequests: + error: + below: 100M + above: 3G + warning: + below: 200M + above: 2G + cpuLimits: + error: + below: 100m + above: 2 + warning: + below: 300m + above: 1800m + memoryLimits: + error: + below: 200M + above: 6G + warning: + below: 300M + above: 4G + images: - tagRequired: true - whitelistRepos: - - gcr.io -hostNetwork: - hostAlias: - require: true - hostIPC: - require: true - hostNetwork: - require: true - hostPID: - require: true - hostPort: - require: true + tagNotSpecified: 'error' + pullPolicyNotAlways: 'warning' + error: + whitelist: + - gcr.io/* + warning: + blacklist: + - docker.io/* +healthChecks: + readinessProbeMissing: warning + livenessProbeMissing: warning +networking: + hostAliasSet: error + hostIPCSet: error + hostNetworkSet: error + hostPIDSet: error + hostPortSet: error +security: + runAsPriviliged: warning + notReadOnlyRootFileSystem: warning + runAsNonRoot: warning + capabilities: + warning: + blacklist: + - CHOWN + - SYS_CHROOT + - AUDIT_WRITE + error: + whitelist: + - CHOWN + - DAC_OVERRIDE + - FSETID + - FOWNER + - MKNOD + - NET_RAW + - SETGID + - SETUID + - SETFCAP + - SETPCAP + - NET_BIND_SERVICE + - SYS_CHROOT + - KILL + - AUDIT_WRITE diff --git a/pkg/config/config.go b/pkg/config/config.go index f0f65f7f..c295f70e 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -6,58 +6,89 @@ import ( "io" "io/ioutil" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/util/yaml" ) // Configuration contains all of the config for the validation checks. type Configuration struct { - Resources RequestsAndLimits `json:"resources"` - HealthChecks Probes `json:"healthChecks"` - Images Images `json:"images"` - HostNetworking HostNetworking `json:"hostNetworking"` + Resources Resources `json:"resources"` + HealthChecks HealthChecks `json:"healthChecks"` + Images Images `json:"images"` + Networking Networking `json:"networking"` + Security Security `json:"security"` } -// RequestsAndLimits contains config for resource requests and limits. -type RequestsAndLimits struct { - Requests ResourceList `json:"requests"` - Limits ResourceList `json:"limits"` +// Severity represents the severity of action to take (Ignore, Warning, Error). +type Severity string + +// Resources contains config for resource requests and limits. +type Resources struct { + CPURequests Resource `json:"cpuRequests"` + CPULimits Resource `json:"cpuLimits"` + MemoryRequests Resource `json:"memoryRequests"` + MemoryLimits Resource `json:"memoryLimits"` } -// ResourceList maps the resource name to a range on min and max values. -type ResourceList map[corev1.ResourceName]ResourceMinMax - -// ResourceMinMax sets a range for a min and max setting for a resource. -type ResourceMinMax struct { - Min *resource.Quantity `json:"min"` - Max *resource.Quantity `json:"max"` +// Resource contains config for requests or limits for a specific resource. +type Resource struct { + Absent Severity `json:"absent"` + Warning ResourceRange `json:"warning"` + Error ResourceRange `json:"error"` } -// Probes contains config for the readiness and liveness probes. -type Probes struct { - Readiness ResourceRequire `json:"readiness"` - Liveness ResourceRequire `json:"liveness"` +// ResourceRange can contain below and above conditions for validation. +type ResourceRange struct { + Below *resource.Quantity `json:"below"` + Above *resource.Quantity `json:"above"` } -// ResourceRequire indicates if this resource should be validated. -type ResourceRequire struct { - Require bool `json:"require"` +// HealthChecks contains config for readiness and liveness probes. +type HealthChecks struct { + ReadinessProbeMissing Severity `json:"readinessProbeMissing"` + LivenessProbeMissing Severity `json:"livenessProbeMissing"` } // Images contains the config for images. type Images struct { - TagRequired bool `json:"tagRequired"` - WhitelistRepos []string `json:"whitelistRepos"` + TagNotSpecified Severity `json:"tagNotSpecified"` + PullPolicyNotAlways Severity `json:"pullPolicyNotAlways"` + Repositories Repositories `json:"repositories"` } -// HostNetworking contains the config for host networking validations. -type HostNetworking struct { - HostAlias ResourceRequire `json:"hostAlias"` - HostIPC ResourceRequire `json:"hostIPC"` - HostNetwork ResourceRequire `json:"hostNetwork"` - HostPID ResourceRequire `json:"hostPID"` - HostPort ResourceRequire `json:"hostPort"` +// Repositories provides lists of patterns to match or avoid in image tags. +type Repositories struct { + Error WhitelistBlacklist `json:"error"` + Warning WhitelistBlacklist `json:"warning"` +} + +// WhitelistBlacklist can contain a whitelist or blacklist. +type WhitelistBlacklist struct { + Whitelist []string `json:"whitelist"` + Blacklist []string `json:"blacklist"` +} + +// Networking contains the config for networking validations. +type Networking struct { + HostAliasSet Severity `json:"hostAliasSet"` + HostIPCSet Severity `json:"hostIPCSet"` + HostNetworkSet Severity `json:"hostNetworkSet"` + HostPIDSet Severity `json:"hostPIDSet"` + HostPortSet Severity `json:"hostPortSet"` +} + +// Security contains the config for security validations. +type Security struct { + runAsNonRoot Severity `json:"runAsNonRoot"` + runAsPriviliged Severity `json:"runAsPriviliged"` + notReadOnlyRootFileSystem Severity `json:"notReadOnlyRootFileSystem"` + capabilities SecurityCapabilities `json:"capabilities"` +} + +// SecurityCapabilities contains the config for security capabilities validations. +type SecurityCapabilities struct { + Error WhitelistBlacklist `json:"error"` + Warning WhitelistBlacklist `json:"warning"` } // ParseFile parses config from a file. diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 6b4fa439..13f1ab25 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -25,42 +25,76 @@ var resourceConfInvalid1 = `test` var resourceConfYAML1 = `--- resources: - requests: - cpu: - min: 100m - max: 1 - memory: - min: 100M - max: 3G - limits: - cpu: - min: 150m - max: 2 - memory: - min: 150M - max: 4G + cpuRequests: + error: + below: 100m + above: 1 + warning: + below: 200m + above: 800m + memoryRequests: + error: + below: 100M + above: 3G + warning: + below: 200M + above: 2G + cpuLimits: + error: + below: 100m + above: 2 + warning: + below: 300m + above: 1800m + memoryLimits: + error: + below: 200M + above: 6G + warning: + below: 300M + above: 4G ` var resourceConfJSON1 = `{ "resources": { - "requests": { - "cpu": { - "min": "100m", - "max": "1" + "cpuRequests": { + "error": { + "below": "100m", + "above": 1 }, - "memory": { - "min": "100M", - "max": "3G" + "warning": { + "below": "200m", + "above": "800m" } }, - "limits": { - "cpu": { - "min": "150m", - "max": "2" + "memoryRequests": { + "error": { + "below": "100M", + "above": "3G" }, - "memory": { - "min": "150M", - "max": "4G" + "warning": { + "below": "200M", + "above": "2G" + } + }, + "cpuLimits": { + "error": { + "below": "100m", + "above": 2 + }, + "warning": { + "below": "300m", + "above": "1800m" + } + }, + "memoryLimits": { + "error": { + "below": "200M", + "above": "6G" + }, + "warning": { + "below": "300M", + "above": "4G" } } } @@ -74,34 +108,40 @@ func TestParseError(t *testing.T) { func TestParseYaml(t *testing.T) { parsedConf, err := Parse([]byte(resourceConfYAML1)) - assert.NoError(t, err, "Expected no error when parsing config") + assert.NoError(t, err, "Expected no error when parsing YAML config") - requests := parsedConf.Resources.Requests - assert.Equal(t, int64(100), requests["cpu"].Min.ScaledValue(resource.Milli)) - assert.Equal(t, int64(1000), requests["cpu"].Max.ScaledValue(resource.Milli)) - assert.Equal(t, int64(100), requests["memory"].Min.ScaledValue(resource.Mega)) - assert.Equal(t, int64(3000), requests["memory"].Max.ScaledValue(resource.Mega)) - - limits := parsedConf.Resources.Limits - assert.Equal(t, int64(150), limits["cpu"].Min.ScaledValue(resource.Milli)) - assert.Equal(t, int64(2000), limits["cpu"].Max.ScaledValue(resource.Milli)) - assert.Equal(t, int64(150), limits["memory"].Min.ScaledValue(resource.Mega)) - assert.Equal(t, int64(4000), limits["memory"].Max.ScaledValue(resource.Mega)) + testParsedConfig(t, &parsedConf) } func TestParseJson(t *testing.T) { parsedConf, err := Parse([]byte(resourceConfJSON1)) - assert.NoError(t, err, "Expected no error when parsing config") + assert.NoError(t, err, "Expected no error when parsing JSON config") - requests := parsedConf.Resources.Requests - assert.Equal(t, int64(100), requests["cpu"].Min.ScaledValue(resource.Milli)) - assert.Equal(t, int64(1000), requests["cpu"].Max.ScaledValue(resource.Milli)) - assert.Equal(t, int64(100), requests["memory"].Min.ScaledValue(resource.Mega)) - assert.Equal(t, int64(3000), requests["memory"].Max.ScaledValue(resource.Mega)) - - limits := parsedConf.Resources.Limits - assert.Equal(t, int64(150), limits["cpu"].Min.ScaledValue(resource.Milli)) - assert.Equal(t, int64(2000), limits["cpu"].Max.ScaledValue(resource.Milli)) - assert.Equal(t, int64(150), limits["memory"].Min.ScaledValue(resource.Mega)) - assert.Equal(t, int64(4000), limits["memory"].Max.ScaledValue(resource.Mega)) + testParsedConfig(t, &parsedConf) +} + +func testParsedConfig(t *testing.T, config *Configuration) { + cpuRequests := config.Resources.CPURequests + assert.Equal(t, int64(100), cpuRequests.Error.Below.ScaledValue(resource.Milli)) + assert.Equal(t, int64(1000), cpuRequests.Error.Above.ScaledValue(resource.Milli)) + assert.Equal(t, int64(200), cpuRequests.Warning.Below.ScaledValue(resource.Milli)) + assert.Equal(t, int64(800), cpuRequests.Warning.Above.ScaledValue(resource.Milli)) + + memRequests := config.Resources.MemoryRequests + assert.Equal(t, int64(100), memRequests.Error.Below.ScaledValue(resource.Mega)) + assert.Equal(t, int64(3000), memRequests.Error.Above.ScaledValue(resource.Mega)) + assert.Equal(t, int64(200), memRequests.Warning.Below.ScaledValue(resource.Mega)) + assert.Equal(t, int64(2000), memRequests.Warning.Above.ScaledValue(resource.Mega)) + + cpuLimits := config.Resources.CPULimits + assert.Equal(t, int64(100), cpuLimits.Error.Below.ScaledValue(resource.Milli)) + assert.Equal(t, int64(2000), cpuLimits.Error.Above.ScaledValue(resource.Milli)) + assert.Equal(t, int64(300), cpuLimits.Warning.Below.ScaledValue(resource.Milli)) + assert.Equal(t, int64(1800), cpuLimits.Warning.Above.ScaledValue(resource.Milli)) + + memLimits := config.Resources.MemoryLimits + assert.Equal(t, int64(200), memLimits.Error.Below.ScaledValue(resource.Mega)) + assert.Equal(t, int64(6000), memLimits.Error.Above.ScaledValue(resource.Mega)) + assert.Equal(t, int64(300), memLimits.Warning.Below.ScaledValue(resource.Mega)) + assert.Equal(t, int64(4000), memLimits.Warning.Above.ScaledValue(resource.Mega)) }