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>
  • Loading branch information
Kieron Browne and danail-branekov committed Jun 29, 2022
1 parent 13f99cd commit 5301b48
Show file tree
Hide file tree
Showing 15 changed files with 450 additions and 31 deletions.
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
12 changes: 12 additions & 0 deletions kpack-image-builder/config/webhook/object_selector_patch.yaml
@@ -0,0 +1,12 @@
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
- key: korifi.cloudfoundry.org/build-workload-name
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 5301b48

Please sign in to comment.