Commit f9d722c6 authored by Craig Squire's avatar Craig Squire

Updates from MR feedback

parent fbbb8a44
Pipeline #128469623 failed with stages
in 27 minutes and 11 seconds
......@@ -247,18 +247,18 @@ type KubernetesEmptyDir struct {
}
type KubernetesBuildContainerCapabilities struct {
Add []string `toml:"add" long:"add" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_ADD" description:"List of capabilities to add to the container"`
Drop []string `toml:"drop" long:"drop" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_DROP" description:"List of capabilities to drop from the container"`
Add []api.Capability `toml:"add" long:"add" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_ADD" description:"List of capabilities to add to the container"`
Drop []api.Capability `toml:"drop" long:"drop" env:"KUBERNETES_BUILD_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_DROP" description:"List of capabilities to drop from the container"`
}
type KubernetesServiceContainerCapabilities struct {
Add []string `toml:"add" long:"add" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_ADD" description:"List of capabilities to add to the container"`
Drop []string `toml:"drop" long:"drop" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_DROP" description:"List of capabilities to drop from the container"`
Add []api.Capability `toml:"add" long:"add" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_ADD" description:"List of capabilities to add to the container"`
Drop []api.Capability `toml:"drop" long:"drop" env:"KUBERNETES_SERVICE_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_DROP" description:"List of capabilities to drop from the container"`
}
type KubernetesHelperContainerCapabilities struct {
Add []string `toml:"add" long:"add" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_ADD" description:"List of capabilities to add to the container"`
Drop []string `toml:"drop" long:"drop" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_DROP" description:"List of capabilities to drop from the container"`
Add []api.Capability `toml:"add" long:"add" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_ADD" description:"List of capabilities to add to the container"`
Drop []api.Capability `toml:"drop" long:"drop" env:"KUBERNETES_HELPER_CONTAINER_SECURITY_CONTEXT_CAPABILITIES_DROP" description:"List of capabilities to drop from the container"`
}
type KubernetesBuildContainerSecurityContext struct {
......@@ -666,60 +666,42 @@ func (c *KubernetesConfig) getPrivilegedEffective(containerPrivileged *bool) *bo
func (c *KubernetesConfig) getBuildContainerCapabilities() *api.Capabilities {
capabilities := c.BuildContainerSecurityContext.Capabilities
if capabilities == nil ||
(len(capabilities.Add) == 0 &&
len(capabilities.Drop) == 0) {
if capabilities == nil {
return nil
}
return &api.Capabilities{
Add: c.stringsToCapabilities(capabilities.Add),
Drop: c.stringsToCapabilities(capabilities.Drop),
Add: capabilities.Add,
Drop: capabilities.Drop,
}
}
func (c *KubernetesConfig) getServiceContainerCapabilities() *api.Capabilities {
capabilities := c.ServiceContainerSecurityContext.Capabilities
if capabilities == nil ||
(len(capabilities.Add) == 0 &&
len(capabilities.Drop) == 0) {
if capabilities == nil {
return nil
}
return &api.Capabilities{
Add: c.stringsToCapabilities(capabilities.Add),
Drop: c.stringsToCapabilities(capabilities.Drop),
Add: capabilities.Add,
Drop: capabilities.Drop,
}
}
func (c *KubernetesConfig) getHelperContainerCapabilities() *api.Capabilities {
capabilities := c.HelperContainerSecurityContext.Capabilities
if capabilities == nil ||
(len(capabilities.Add) == 0 &&
len(capabilities.Drop) == 0) {
if capabilities == nil {
return nil
}
return &api.Capabilities{
Add: c.stringsToCapabilities(capabilities.Add),
Drop: c.stringsToCapabilities(capabilities.Drop),
Add: capabilities.Add,
Drop: capabilities.Drop,
}
}
func (c *KubernetesConfig) stringsToCapabilities(strings []string) []api.Capability {
if len(strings) == 0 {
return nil
}
var capabilities []api.Capability
for _, s := range strings {
capabilities = append(capabilities, api.Capability(s))
}
return capabilities
}
func (c *DockerMachine) GetIdleCount() int {
if c.isOffPeak() {
return c.OffPeakIdleCount
......
......@@ -3,6 +3,8 @@ package common
import (
"testing"
v1 "k8s.io/api/core/v1"
"github.com/BurntSushi/toml"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
......@@ -267,32 +269,47 @@ func TestService_ToImageDefinition(t *testing.T) {
}
func TestEffectivePrivilege(t *testing.T) {
type privilegedTest struct {
tests := map[string]struct {
Pod bool
Container bool
Expected bool
}{
"pod and container privileged": {
Pod: true,
Container: true,
Expected: true,
},
"pod privileged": {
Pod: true,
Container: false,
Expected: false,
},
"container privileged": {
Pod: false,
Container: true,
Expected: true,
},
"all unprivileged": {
Pod: false,
Container: false,
Expected: false,
},
}
var privilegedTests = []privilegedTest{
{true, true, true},
{true, false, false},
{false, true, true},
{false, false, false},
}
for _, tt := range privilegedTests {
k8sConfig := KubernetesConfig{
Privileged: tt.Pod,
}
effectivePrivileged := k8sConfig.getPrivilegedEffective(&tt.Container)
assert.Equal(t, tt.Expected, *effectivePrivileged)
for tn, tt := range tests {
t.Run(tn, func(t *testing.T) {
k8sConfig := KubernetesConfig{
Privileged: tt.Pod,
}
effectivePrivileged := k8sConfig.getPrivilegedEffective(&tt.Container)
assert.Equal(t, tt.Expected, *effectivePrivileged)
})
}
}
func TestBuildSecurityContextNil(t *testing.T) {
k8sConfig := KubernetesConfig{
BuildContainerSecurityContext: KubernetesBuildContainerSecurityContext{},
}
k8sConfig := KubernetesConfig{}
sc := k8sConfig.GetBuildContainerSecurityContext()
assert.Nil(t, sc)
}
......@@ -305,6 +322,7 @@ func TestBuildSecurityContextFieldsNil(t *testing.T) {
},
}
sc := k8sConfig.GetBuildContainerSecurityContext()
assert.NotNil(t, sc)
assert.False(t, *sc.Privileged)
assert.Nil(t, sc.AllowPrivilegeEscalation)
assert.Nil(t, sc.Capabilities)
......@@ -314,18 +332,36 @@ func TestBuildSecurityContextFieldsNil(t *testing.T) {
assert.Nil(t, sc.SELinuxOptions)
}
func TestBuildSecurityContextCapabilities(t *testing.T) {
func TestBuildSecurityContextCapabilitiesAdd(t *testing.T) {
k8sConfig := KubernetesConfig{
BuildContainerSecurityContext: KubernetesBuildContainerSecurityContext{
Capabilities: &KubernetesBuildContainerCapabilities{
Add: []v1.Capability{"SYS_TIME"},
},
},
}
c := k8sConfig.getBuildContainerCapabilities()
assert.NotNil(t, c)
assert.Len(t, c.Add, 1)
assert.Equal(t, v1.Capability("SYS_TIME"), c.Add[0])
assert.Nil(t, c.Drop)
}
func TestBuildSecurityContextCapabilitiesDrop(t *testing.T) {
k8sConfig := KubernetesConfig{
BuildContainerSecurityContext: KubernetesBuildContainerSecurityContext{
Capabilities: &KubernetesBuildContainerCapabilities{
Add: []string{},
Drop: []string{},
Drop: []v1.Capability{"SYS_TIME"},
},
},
}
c := k8sConfig.getBuildContainerCapabilities()
assert.Nil(t, c)
assert.NotNil(t, c)
assert.Len(t, c.Drop, 1)
assert.Equal(t, v1.Capability("SYS_TIME"), c.Drop[0])
assert.Nil(t, c.Add)
}
func TestServiceSecurityContextNil(t *testing.T) {
......@@ -344,6 +380,7 @@ func TestServiceSecurityContextFieldsNil(t *testing.T) {
},
}
sc := k8sConfig.GetServiceContainerSecurityContext()
assert.NotNil(t, sc)
assert.False(t, *sc.Privileged)
assert.Nil(t, sc.AllowPrivilegeEscalation)
assert.Nil(t, sc.Capabilities)
......@@ -353,18 +390,36 @@ func TestServiceSecurityContextFieldsNil(t *testing.T) {
assert.Nil(t, sc.SELinuxOptions)
}
func TestServiceSecurityContextCapabilities(t *testing.T) {
func TestServiceSecurityContextCapabilitiesAdd(t *testing.T) {
k8sConfig := KubernetesConfig{
ServiceContainerSecurityContext: KubernetesServiceContainerSecurityContext{
Capabilities: &KubernetesServiceContainerCapabilities{
Add: []string{},
Drop: []string{},
Add: []v1.Capability{"SYS_TIME"},
},
},
}
c := k8sConfig.getServiceContainerCapabilities()
assert.Nil(t, c)
assert.NotNil(t, c)
assert.Len(t, c.Add, 1)
assert.Equal(t, v1.Capability("SYS_TIME"), c.Add[0])
assert.Nil(t, c.Drop)
}
func TestServiceSecurityContextCapabilitiesDrop(t *testing.T) {
k8sConfig := KubernetesConfig{
ServiceContainerSecurityContext: KubernetesServiceContainerSecurityContext{
Capabilities: &KubernetesServiceContainerCapabilities{
Drop: []v1.Capability{"SYS_TIME"},
},
},
}
c := k8sConfig.getServiceContainerCapabilities()
assert.NotNil(t, c)
assert.Len(t, c.Drop, 1)
assert.Equal(t, v1.Capability("SYS_TIME"), c.Drop[0])
assert.Nil(t, c.Add)
}
func TestHelperSecurityContextNil(t *testing.T) {
......@@ -392,16 +447,34 @@ func TestHelperSecurityContextFieldsNil(t *testing.T) {
assert.Nil(t, sc.SELinuxOptions)
}
func TestHelperSecurityContextCapabilities(t *testing.T) {
func TestHelperSecurityContextCapabilitiesAdd(t *testing.T) {
k8sConfig := KubernetesConfig{
HelperContainerSecurityContext: KubernetesHelperContainerSecurityContext{
Capabilities: &KubernetesHelperContainerCapabilities{
Add: []v1.Capability{"SYS_TIME"},
},
},
}
c := k8sConfig.getHelperContainerCapabilities()
assert.NotNil(t, c)
assert.Len(t, c.Add, 1)
assert.Equal(t, v1.Capability("SYS_TIME"), c.Add[0])
assert.Nil(t, c.Drop)
}
func TestHelperSecurityContextCapabilitiesDrop(t *testing.T) {
k8sConfig := KubernetesConfig{
HelperContainerSecurityContext: KubernetesHelperContainerSecurityContext{
Capabilities: &KubernetesHelperContainerCapabilities{
Add: []string{},
Drop: []string{},
Drop: []v1.Capability{"SYS_TIME"},
},
},
}
c := k8sConfig.getHelperContainerCapabilities()
assert.Nil(t, c)
assert.NotNil(t, c)
assert.Len(t, c.Drop, 1)
assert.Equal(t, v1.Capability("SYS_TIME"), c.Drop[0])
assert.Nil(t, c.Add)
}
......@@ -407,21 +407,23 @@ check_interval = 30
fs_group = 59417
```
[Container security context][k8s-container-security-docs] configuration instructs executor to set a container security policy on the build pod.
### Container security context
| Option | Type | Required | Description |
|---------------------|-------------|----------|-------------|
| run_as_group | int | no | The GID to run the entrypoint of the container process |
| run_as_non_root | boolean | no | Indicates that the container must run as a non-root user |
| run_as_user | int | no | The UID to run the entrypoint of the container process |
| capabilities.add | string list | no | The capabilities to add when running the container |
| capabilities.drop | string list | no | The capabilities to drop when running the container |
The [container security context](https://kubernetes.io/docs/concepts/policy/pod-security-policy/) configuration configures the executor to set a container security policy on the build pod.
| Option | Type | Required | Description |
|-----------------------|-------------|----------|-------------|
| `run_as_group` | int | no | The GID to run the entrypoint of the container process |
| `run_as_non_root` | boolean | no | Indicates that the container must run as a non-root user |
| `run_as_user` | int | no | The UID to run the entrypoint of the container process |
| `capabilities.add` | string list | no | The capabilities to add when running the container |
| `capabilities.drop` | string list | no | The capabilities to drop when running the container |
Assigining a security context to containers allows you to override the pod security context for an individual container in the pod.
The example below sets a pod security context, then overrides run_as_user and run_as_group for both the build and helper containers. In the example, any service containers would inherit run_as_user and run_as_group from the pod security context.
The example below sets a pod security context, then overrides `run_as_user` and `run_as_group` for both the build and helper containers. In the example, all service containers would inherit `run_as_user` and `run_as_group` from the pod security context.
Example of setting pod security context in your config.toml:
Example of setting pod security context in your `config.toml`:
```toml
concurrent = %(concurrent)s
......@@ -440,9 +442,14 @@ check_interval = 30
[runners.kubernetes.build_container_security_context]
run_as_user = 65534
run_as_group = 65534
[runners.kubernetes.build_container_security_context.capabilities]
add = ["NET_ADMIN"]
[runners.kubernetes.helper_container_security_context]
run_as_user = 1000
run_as_group = 1000
[runners.kubernetes.service_container_security_context]
run_as_user = 1000
run_as_group = 1000
```
## Using services
......
......@@ -450,7 +450,6 @@ func (s *executor) cleanupResources() {
}
func (s *executor) buildContainer(name, image string, imageDefinition common.Image, requests, limits api.ResourceList, securityContext *api.SecurityContext, containerCommand ...string) api.Container {
privileged := false
containerPorts := make([]api.ContainerPort, len(imageDefinition.Ports))
proxyPorts := make([]proxy.Port, len(imageDefinition.Ports))
......@@ -820,6 +819,15 @@ func (s *executor) setupBuildPod() error {
services[i] = s.buildContainer(fmt.Sprintf("svc-%d", i), resolvedImage, service, s.serviceRequests, s.serviceLimits, s.Config.Kubernetes.GetServiceContainerSecurityContext())
}
buildImage := s.Build.GetAllVariables().ExpandValue(s.options.Image.Name)
buildContainerSecurityContext := s.Config.Kubernetes.GetBuildContainerSecurityContext()
helperContainerSecurityContext := s.Config.Kubernetes.GetHelperContainerSecurityContext()
containers := append([]api.Container{
s.buildContainer(buildContainerName, buildImage, s.options.Image, s.buildRequests, s.buildLimits, buildContainerSecurityContext, s.BuildShell.DockerCommand...),
s.buildContainer(helperContainerName, s.getHelperImage(), common.Image{}, s.helperRequests, s.helperLimits, helperContainerSecurityContext, s.BuildShell.DockerCommand...),
}, services...)
// We set a default label to the pod. This label will be used later
// by the services, to link each service to the pod
labels := map[string]string{"pod": s.projectUniqueName()}
......@@ -846,7 +854,7 @@ func (s *executor) setupBuildPod() error {
return err
}
podConfig := s.preparePodConfig(labels, annotations, services, imagePullSecrets, hostAlias)
podConfig := s.preparePodConfig(labels, annotations, containers, imagePullSecrets, hostAlias)
s.Debugln("Creating build pod")
pod, err := s.kubeClient.CoreV1().Pods(s.configurationOverwrites.namespace).Create(&podConfig)
......@@ -863,8 +871,7 @@ func (s *executor) setupBuildPod() error {
return nil
}
func (s *executor) preparePodConfig(labels, annotations map[string]string, services []api.Container, imagePullSecrets []api.LocalObjectReference, hostAlias *api.HostAlias) api.Pod {
buildImage := s.Build.GetAllVariables().ExpandValue(s.options.Image.Name)
func (s *executor) preparePodConfig(labels, annotations map[string]string, containers []api.Container, imagePullSecrets []api.LocalObjectReference, hostAlias *api.HostAlias) api.Pod {
pod := api.Pod{
ObjectMeta: metav1.ObjectMeta{
......@@ -874,16 +881,12 @@ func (s *executor) preparePodConfig(labels, annotations map[string]string, servi
Annotations: annotations,
},
Spec: api.PodSpec{
Volumes: s.getVolumes(),
ServiceAccountName: s.configurationOverwrites.serviceAccount,
RestartPolicy: api.RestartPolicyNever,
NodeSelector: s.Config.Kubernetes.NodeSelector,
Tolerations: s.Config.Kubernetes.GetNodeTolerations(),
Containers: append([]api.Container{
// TODO use the build and helper template here
s.buildContainer(buildContainerName, buildImage, s.options.Image, s.buildRequests, s.buildLimits, s.Config.Kubernetes.GetBuildContainerSecurityContext(), s.BuildShell.DockerCommand...),
s.buildContainer(helperContainerName, s.getHelperImage(), common.Image{}, s.helperRequests, s.helperLimits, s.Config.Kubernetes.GetHelperContainerSecurityContext(), s.BuildShell.DockerCommand...),
}, services...),
Volumes: s.getVolumes(),
ServiceAccountName: s.configurationOverwrites.serviceAccount,
RestartPolicy: api.RestartPolicyNever,
NodeSelector: s.Config.Kubernetes.NodeSelector,
Tolerations: s.Config.Kubernetes.GetNodeTolerations(),
Containers: containers,
TerminationGracePeriodSeconds: &s.Config.Kubernetes.TerminationGracePeriodSeconds,
ImagePullSecrets: imagePullSecrets,
SecurityContext: s.Config.Kubernetes.GetPodSecurityContext(),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment