In the following guide we will focus on integrating build.security's PDP as a validating admission controller. Using the PDP will allow you to enjoy the control plane's rich capabilities on top of OPA.
What is a Kubernetes admission controller?
In a nutshell, Kubernetes admission controllers are plugins that govern and enforce how the cluster is used. They can be thought of as a gatekeeper that intercept (authenticated) API requests and may change the request object or deny the request altogether. The admission control process has two phases: the mutating phase is executed first, followed by the validating phase. Consequently, admission controllers can act as mutating or validating controllers or as a combination of both.
Create a new policy in the control plane. Make sure you pick Kubernetes integration.
Notice that the default policy comes with a set of rules that are all set as Monitored. By default we set the policy to allow all requests, so at first we won't enforce any of the rules.
Now let's publish the new policy so it would be served to the PDP once it is deployed.
When PDP is deployed on top of Kubernetes, policies are automatically loaded whenever you publish a policy using the control plane.
kubectl create namespace buildsecurity
kubectl to use this namespace:
kubectl config set-context pdp-tutorial --user minikube --cluster minikube --namespace buildsecuritykubectl config use-context pdp-tutorial
Communication between Kubernetes and PDP must be secured using TLS. To configure TLS, use
openssl to create a certificate authority (CA) and certificate/key pair for PDP:
openssl genrsa -out ca.key 2048openssl req -x509 -new -nodes -key ca.key -days 100000 -out ca.crt -subj "/CN=admission_ca"
Generate the TLS key and certificate for PDP:
cat >server.conf <<EOF[req]req_extensions = v3_reqdistinguished_name = req_distinguished_nameprompt = no[req_distinguished_name]CN = pdp.buildsecurity.svc[ v3_req ]basicConstraints = CA:FALSEkeyUsage = nonRepudiation, digitalSignature, keyEnciphermentextendedKeyUsage = clientAuth, serverAuthsubjectAltName = @alt_names[alt_names]DNS.1 = pdp.buildsecurity.svcEOF
openssl genrsa -out server.key 2048openssl req -new -key server.key -out server.csr -config server.confopenssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 100000 -extensions v3_req -extfile server.conf
Note: the Common Name value and Subject Alternative Name you give to openssl MUST match the name of the PDP service created below.
Create a Secret to store the TLS credentials for PDP:
kubectl create secret tls pdp-server --cert=server.crt --key=server.key
Next, use the file below to deploy PDP as an admission controller.
kind: ServiceapiVersion: v1metadata:name: pdpnamespace: buildsecurityspec:selector:app: pdpports:- name: httpsprotocol: TCPport: 443targetPort: 8181---apiVersion: apps/v1kind: Deploymentmetadata:labels:app: pdpnamespace: buildsecurityname: pdpspec:replicas: 1selector:matchLabels:app: pdptemplate:metadata:labels:app: pdpname: pdpspec:containers:- name: pdpimage: buildsecurity/pdp:latestimagePullPolicy: Alwaysargs:- "--tls-cert-file=/certs/tls.crt"- "--tls-private-key-file=/certs/tls.key"env:- name: API_KEYvalue: "YOUR API KEY HERE"- name: API_SECRETvalue: "YOUR API SECRET HERE"- name: CONTROL_PLANE_ADDRvalue: "https://api.build.security/v1/api/pdp"volumeMounts:- readOnly: truemountPath: /certsname: pdp-serverreadinessProbe:httpGet:path: /health?plugins&bundlescheme: HTTPSport: 8181initialDelaySeconds: 10periodSeconds: 5livenessProbe:httpGet:path: /healthscheme: HTTPSport: 8181initialDelaySeconds: 10periodSeconds: 5volumes:- name: pdp-serversecret:secretName: pdp-server
And run the following:
kubectl apply -f admission-controller.yaml
You can now check that the PDP is online in the control plane.
Generate the manifest that will be used to register PDP as an admission controller. This webhook will ignore any namespace with the label
cat > webhook-configuration.yaml <<EOFkind: ValidatingWebhookConfigurationapiVersion: admissionregistration.k8s.io/v1metadata:name: pdp-validating-webhookwebhooks:- name: validating-webhook.build.securitynamespaceSelector:matchExpressions:- key: build.security/webhookoperator: NotInvalues:- ignorerules:- operations: ["CREATE", "UPDATE"]apiGroups: ["*"]apiVersions: ["*"]resources: ["*"]sideEffects: NonefailurePolicy: Ignore # Change to Fail - to strict on webhook failure and fail the actionclientConfig:caBundle: $(cat ca.crt | base64 | tr -d '\n')service:namespace: buildsecurityname: pdppath: /v0/data-ex/kubernetes/admission/responseadmissionReviewVersions: ["v1", "v1beta1"]timeoutSeconds: 5EOF
The generated configuration file includes a base64 encoded representation of the CA certificate so that TLS connections can be established between the Kubernetes API server and PDP.
kube-system and the
buildsecurity namespace so that PDP does not control the resources in those namespaces.
kubectl label ns kube-system build.security/webhook=ignorekubectl label ns buildsecurity build.security/webhook=ignore
Finally, register the PDP as an admission controller:
kubectl apply -f webhook-configuration.yaml
You can follow the PDP logs to see the webhook requests being issued by the Kubernetes API server:
# ctrl-c to exitkubectl logs -l app=pdp -c pdp -f
Go to Decision Logs to see the requests being made and being sent to the control plane properly:
apiVersion: apps/v1kind: Deploymentmetadata:name: nginx-deploymentspec:selector:matchLabels:app: nginxreplicas: 1template:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:latestresources:requests:memory: 64Micpu: 250mports:- containerPort: 80
Now lets create the nginx and see the results:
kubectl create -f nginx.yaml -n default
Use Decision Logs filters to find the relevant events to nginx:
Now let's change some of the rules that could have denied the request and publish our changes
Now, let’s try and recreate nginx. Remember that we expect this operation to fail since the relevant rule is now active
kubectl delete -f nginx.yaml -n defaultkubectl create -f nginx.yaml -n default# Error from server (nginx in the Deployment nginx-deployment is not using a read only root filesystem): error when creating "nginx.yaml": admission webhook "validating-webhook.build.security" denied the request: nginx in the Deployment nginx-deployment is not using a read only root filesystem
And in the Decision Logs you would see the following: