Skip to content

Commit

Permalink
Introduce kpack build pod mutating webhook
Browse files Browse the repository at this point in the history
The mutating webhook sets a security policy in the kpack build pods'
containers that matches the `restricted` pod security enforced by the
workload namespace

The webhook is a workaround to kpack issue
buildpacks-community/kpack#972. Once the issue is resolved
and a fix is adopted, we should probably revert the commit and adopt the
kpack fix.

Issue: #1220
Co-authored-by: Danail Branekov <danailster@gmail.com>

nearly working

Co-authored-by: Danail Branekov <danailster@gmail.com>

It works

Co-authored-by: Danail Branekov <danailster@gmail.com>
  • Loading branch information
Kieron Browne and danail-branekov committed Jun 28, 2022
1 parent 45d916c commit f6e1c5c
Show file tree
Hide file tree
Showing 17 changed files with 449 additions and 33 deletions.
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -34,6 +34,7 @@ require (
k8s.io/apimachinery v0.24.2
k8s.io/client-go v0.24.2
k8s.io/metrics v0.24.2
k8s.io/pod-security-admission v0.24.2
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
sigs.k8s.io/controller-runtime v0.12.1
sigs.k8s.io/controller-tools v0.9.0
Expand All @@ -50,7 +51,6 @@ require (
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
google.golang.org/genproto v0.0.0-20220422154200-b37d22cd5731 // indirect
google.golang.org/grpc v1.46.0 // indirect
k8s.io/pod-security-admission v0.24.2 // indirect
)

require (
Expand Down
1 change: 0 additions & 1 deletion go.sum
Expand Up @@ -3299,7 +3299,6 @@ k8s.io/component-base v0.21.2/go.mod h1:9lvmIThzdlrJj5Hp8Z/TOgIkdfsNARQ1pT+3PByu
k8s.io/component-base v0.21.3/go.mod h1:kkuhtfEHeZM6LkX0saqSK8PbdO7A0HigUngmhhrwfGQ=
k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI=
k8s.io/component-base v0.23.0/go.mod h1:DHH5uiFvLC1edCpvcTDV++NKULdYYU6pR9Tt3HIKMKI=
k8s.io/component-base v0.24.1 h1:APv6W/YmfOWZfo+XJ1mZwep/f7g7Tpwvdbo9CQLDuts=
k8s.io/component-base v0.24.1/go.mod h1:DW5vQGYVCog8WYpNob3PMmmsY8A3L9QZNg4j/dV3s38=
k8s.io/component-base v0.24.2 h1:kwpQdoSfbcH+8MPN4tALtajLDfSfYxBDYlXobNWI6OU=
k8s.io/component-base v0.24.2/go.mod h1:ucHwW76dajvQ9B7+zecZAP3BVqvrHoOxm8olHEg0nmM=
Expand Down
25 changes: 25 additions & 0 deletions kpack-image-builder/config/certmanager/certificate.yaml
@@ -0,0 +1,25 @@
# The following manifests contain a self-signed issuer CR and a certificate CR.
# More document can be found at https://docs.cert-manager.io
# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes.
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: selfsigned-issuer
namespace: system
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml
namespace: system
spec:
# $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize
dnsNames:
- $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc
- $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local
issuerRef:
kind: Issuer
name: selfsigned-issuer
secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize
5 changes: 5 additions & 0 deletions kpack-image-builder/config/certmanager/kustomization.yaml
@@ -0,0 +1,5 @@
resources:
- certificate.yaml

configurations:
- kustomizeconfig.yaml
16 changes: 16 additions & 0 deletions kpack-image-builder/config/certmanager/kustomizeconfig.yaml
@@ -0,0 +1,16 @@
# This configuration is for teaching kustomize how to update name ref and var substitution
nameReference:
- kind: Issuer
group: cert-manager.io
fieldSpecs:
- kind: Certificate
group: cert-manager.io
path: spec/issuerRef/name

varReference:
- kind: Certificate
group: cert-manager.io
path: spec/commonName
- kind: Certificate
group: cert-manager.io
path: spec/dnsNames
Expand Up @@ -19,9 +19,9 @@ resources:
- ../../base
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
#- ../webhook
- ../../webhook
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
#- ../certmanager
- ../../certmanager
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
#- ../prometheus

Expand All @@ -37,39 +37,39 @@ patchesStrategicMerge:

# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
#- manager_webhook_patch.yaml
- manager_webhook_patch.yaml

# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.
# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.
# 'CERTMANAGER' needs to be enabled to use ca injection
#- webhookcainjection_patch.yaml
- webhookcainjection_patch.yaml

# the following config is for teaching kustomize how to do var substitution
vars:
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
# objref:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert # this name should match the one in certificate.yaml
# fieldref:
# fieldpath: metadata.namespace
#- name: CERTIFICATE_NAME
# objref:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert # this name should match the one in certificate.yaml
#- name: SERVICE_NAMESPACE # namespace of the service
# objref:
# kind: Service
# version: v1
# name: webhook-service
# fieldref:
# fieldpath: metadata.namespace
#- name: SERVICE_NAME
# objref:
# kind: Service
# version: v1
# name: webhook-service
- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
objref:
kind: Certificate
group: cert-manager.io
version: v1
name: serving-cert # this name should match the one in certificate.yaml
fieldref:
fieldpath: metadata.namespace
- name: CERTIFICATE_NAME
objref:
kind: Certificate
group: cert-manager.io
version: v1
name: serving-cert # this name should match the one in certificate.yaml
- name: SERVICE_NAMESPACE # namespace of the service
objref:
kind: Service
version: v1
name: webhook-service
fieldref:
fieldpath: metadata.namespace
- name: SERVICE_NAME
objref:
kind: Service
version: v1
name: webhook-service
@@ -0,0 +1,23 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: system
spec:
template:
spec:
containers:
- name: manager
ports:
- containerPort: 9443
name: webhook-server
protocol: TCP
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
volumes:
- name: cert
secret:
defaultMode: 420
secretName: webhook-server-cert
@@ -0,0 +1,15 @@
# This patch add annotation to admission webhook config and
# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize.
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: mutating-webhook-configuration
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
# ---
# apiVersion: admissionregistration.k8s.io/v1
# kind: ValidatingWebhookConfiguration
# metadata:
# name: validating-webhook-configuration
# annotations:
# cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
9 changes: 9 additions & 0 deletions kpack-image-builder/config/webhook/kustomization.yaml
@@ -0,0 +1,9 @@
resources:
- manifests.yaml
- service.yaml

configurations:
- kustomizeconfig.yaml

patchesStrategicMerge:
- object_selector_patch.yaml
25 changes: 25 additions & 0 deletions kpack-image-builder/config/webhook/kustomizeconfig.yaml
@@ -0,0 +1,25 @@
# the following config is for teaching kustomize where to look at when substituting vars.
# It requires kustomize v2.1.0 or newer to work properly.
nameReference:
- kind: Service
version: v1
fieldSpecs:
- kind: MutatingWebhookConfiguration
group: admissionregistration.k8s.io
path: webhooks/clientConfig/service/name
- kind: ValidatingWebhookConfiguration
group: admissionregistration.k8s.io
path: webhooks/clientConfig/service/name

namespace:
- kind: MutatingWebhookConfiguration
group: admissionregistration.k8s.io
path: webhooks/clientConfig/service/namespace
create: true
- kind: ValidatingWebhookConfiguration
group: admissionregistration.k8s.io
path: webhooks/clientConfig/service/namespace
create: true

varReference:
- path: metadata/annotations
27 changes: 26 additions & 1 deletion kpack-image-builder/config/webhook/manifests.yaml
@@ -1,2 +1,27 @@
---

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
creationTimestamp: null
name: mutating-webhook-configuration
webhooks:
- admissionReviewVersions:
- v1
- v1beta1
clientConfig:
service:
name: webhook-service
namespace: system
path: /mutate-kpack-build-pod
failurePolicy: Ignore
name: mkpackbuildpod.korifi.cloudfoundry.org
rules:
- apiGroups:
- ""
apiVersions:
- v1
operations:
- CREATE
resources:
- pods
sideEffects: None
10 changes: 10 additions & 0 deletions kpack-image-builder/config/webhook/object_selector_patch.yaml
@@ -0,0 +1,10 @@
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: mutating-webhook-configuration
webhooks:
- name: mkpackbuildpod.korifi.cloudfoundry.org
objectSelector:
matchExpressions:
- key: kpack.io/build
operator: Exists
13 changes: 13 additions & 0 deletions kpack-image-builder/config/webhook/service.yaml
@@ -0,0 +1,13 @@

apiVersion: v1
kind: Service
metadata:
name: webhook-service
namespace: system
spec:
ports:
- port: 443
protocol: TCP
targetPort: 9443
selector:
control-plane: controller-manager
@@ -0,0 +1,72 @@
package podsecurity

import (
"context"
"encoding/json"
"net/http"

corev1 "k8s.io/api/core/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

// +kubebuilder:webhook:path=/mutate-kpack-build-pod,mutating=true,failurePolicy=ignore,groups="",resources=pods,verbs=create,versions=v1,name=mkpackbuildpod.korifi.cloudfoundry.org,admissionReviewVersions={v1,v1beta1},sideEffects=none

type PodSecurityAdder struct {
decoder *admission.Decoder
}

func NewPodSecurityAdder() *PodSecurityAdder {
return &PodSecurityAdder{}
}

func pointerTo(b bool) *bool {
return &b
}

func (a *PodSecurityAdder) Handle(ctx context.Context, req admission.Request) admission.Response {
logger := ctrl.Log.WithName("kpack-build-pod-security")

pod := &corev1.Pod{}
err := a.decoder.Decode(req, pod)
if err != nil {
logger.Error(err, "decode-error")
return admission.Errored(http.StatusBadRequest, err)
}

patchContainerSecurity(pod.Spec.Containers)
patchContainerSecurity(pod.Spec.InitContainers)

marshaledPod, err := json.Marshal(pod)
if err != nil {
return admission.Errored(http.StatusInternalServerError, err)
}

logger.Info("patching pod security context", "namespace", req.Namespace, "name", req.Name)
return admission.PatchResponseFromRaw(req.Object.Raw, marshaledPod)
}

// InjectDecoder injects the decoder.
func (a *PodSecurityAdder) InjectDecoder(d *admission.Decoder) error {
a.decoder = d
return nil
}

func patchContainerSecurity(containers []corev1.Container) {
for i := range containers {
container := containers[i]
if container.SecurityContext == nil {
container.SecurityContext = new(corev1.SecurityContext)
}

container.SecurityContext.AllowPrivilegeEscalation = pointerTo(false)
container.SecurityContext.RunAsNonRoot = pointerTo(true)
container.SecurityContext.Capabilities = &corev1.Capabilities{
Drop: []corev1.Capability{"ALL"},
}
container.SecurityContext.SeccompProfile = &corev1.SeccompProfile{
Type: corev1.SeccompProfileTypeRuntimeDefault,
}
containers[i] = container
}
}

0 comments on commit f6e1c5c

Please sign in to comment.