1# Kubernetes Admission Control 2 3In Kubernetes, [Admission Controllers](https://kubernetes.io/docs/admin/admission-controllers/) enforce semantic validation of objects during create, update, and delete operations. With OPA you can enforce custom policies on Kubernetes objects without recompiling or reconfiguring the Kubernetes API server. 4 5## Goals 6 7This tutorial shows how to enforce custom policies on Kubernetes objects using OPA. In this tutorial, you will define admission control rules that prevent users from creating Kubernetes Ingress objects that violate the following organization policy: 8 9- Ingress hostnames must be whitelisted on the Namespace containing the Ingress. 10- Two ingresses in different namespaces must not have the same hostname. 11 12## Prerequisites 13 14This tutorial requires Kubernetes 1.9 or later. To run the tutorial locally, we recommend using [minikube](https://kubernetes.io/docs/getting-started-guides/minikube). 15 16## Steps 17 18### 1. Start Kubernetes recommended Admisson Controllers enabled 19 20To implement admission control rules that validate Kubernetes resources during create, update, and delete operations, you must enable the `ValidatingAdmissionWebhook` when the Kubernetes API server is started. The [recommended set of admission controllers to enable](https://kubernetes.io/docs/admin/admission-controllers/#is-there-a-recommended-set-of-admission-controllers-to-use) is defined below. 21 22```bash 23ADMISSION_CONTROLLERS=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota 24``` 25 26Start minikube with these admission controllers enabled: 27 28```bash 29minikube start --kubernetes-version v1.9.0 \ 30 --extra-config=apiserver.Admission.PluginNames=$ADMISSION_CONTROLLERS 31``` 32 33Make sure that the minikube ingress addon is enabled: 34 35```bash 36minikube addons enable ingress 37``` 38 39### 2. Create a new Namespace to deploy OPA into 40 41When OPA is deployed on top of Kubernetes, policies are automatically loaded out of ConfigMaps in the `opa` namespace. 42 43```bash 44kubectl create namespace opa 45``` 46 47Configure `kubectl` to use this namespace: 48 49```bash 50kubectl config set-context opa-tutorial --user minikube --cluster minikube --namespace opa 51kubectl config use-context opa-tutorial 52``` 53 54### 3. Deploy OPA on top of Kubernetes 55 56Communication between Kubernetes and OPA must be secured using TLS. To configure TLS, use `openssl` to create a certificate authority (CA) and certificate/key pair for OPA: 57 58```bash 59openssl genrsa -out ca.key 2048 60openssl req -x509 -new -nodes -key ca.key -days 100000 -out ca.crt -subj "/CN=admission_ca" 61``` 62 63Generate the TLS key and certificate for OPA: 64 65```bash 66cat >server.conf <<EOF 67[req] 68req_extensions = v3_req 69distinguished_name = req_distinguished_name 70[req_distinguished_name] 71[ v3_req ] 72basicConstraints = CA:FALSE 73keyUsage = nonRepudiation, digitalSignature, keyEncipherment 74extendedKeyUsage = clientAuth, serverAuth 75EOF 76``` 77 78```bash 79openssl genrsa -out server.key 2048 80openssl req -new -key server.key -out server.csr -subj "/CN=opa.opa.svc" -config server.conf 81openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 100000 -extensions v3_req -extfile server.conf 82``` 83 84> Note: the Common Name value you give to openssl MUST match the name of the OPA service created below. 85 86Create a Secret to store the TLS credentials for OPA: 87 88```bash 89kubectl create secret tls opa-server --cert=server.crt --key=server.key 90``` 91 92Next, use the file below to deploy OPA as an admission controller. 93 94**[admission-controller.yaml](https://github.com/open-policy-agent/opa/docs/book/tutorials/kubernetes-admission-control-validation/admission-controller.yaml)**: 95<pre><code class="lang-yaml">{% include "./tutorials/kubernetes-admission-control-validation/admission-controller.yaml" %}</code></pre> 96 97```bash 98kubectl apply -f admission-controller.yaml 99``` 100 101When OPA starts, the `kube-mgmt` container will load Kubernetes Namespace and Ingress objects into OPA. You can configure the sidecar to load any kind of Kubernetes object into OPA. The sidecar establishes watches on the Kubernetes API server so that OPA has access to an eventually consistent cache of Kubernetes objects. 102 103Next, generate the manifest that will be used to register OPA as an admission controller: 104 105```bash 106cat > webhook-configuration.yaml <<EOF 107kind: ValidatingWebhookConfiguration 108apiVersion: admissionregistration.k8s.io/v1beta1 109metadata: 110 name: opa-validating-webhook 111webhooks: 112 - name: validating-webhook.openpolicyagent.org 113 rules: 114 - operations: ["CREATE", "UPDATE"] 115 apiGroups: ["*"] 116 apiVersions: ["*"] 117 resources: ["*"] 118 clientConfig: 119 caBundle: $(cat ca.crt | base64 | tr -d '\n') 120 service: 121 namespace: opa 122 name: opa 123EOF 124``` 125 126The generated configuration file inclues a base64 encoded representation of the CA certificate so that TLS connections can be established between the Kubernetes API server and OPA. 127 128Finally, register OPA as and admission controller: 129 130```bash 131kubectl apply -f webhook-configuration.yaml 132``` 133 134You can follow the OPA logs to see the webhook requests being issued by the Kubernetes API server: 135 136``` 137kubectl logs -l app=opa -c opa 138``` 139 140### 4. Define a policy and load it into OPA via Kubernetes 141 142To test admission control, create a policy that restricts the hostnames that an ingress can use ([ingress-whitelist.rego](https://github.com/open-policy-agent/opa/docs/book/tutorials/kubernetes-admission-control-validation/ingress-whitelist.rego)): 143 144<pre><code class="lang-ruby">{% include "./tutorials/kubernetes-admission-control-validation/ingress-whitelist.rego" %}</code></pre> 145 146Store the policy in Kubernetes as a ConfigMap. 147 148```bash 149kubectl create configmap ingress-whitelist --from-file=ingress-whitelist.rego 150``` 151 152The OPA sidecar will notice the ConfigMap and automatically load the policy into OPA. 153 154### 5. Exercise the policy 155 156Create two new namespaces to test the Ingress policy. 157 158**qa-namespace.yaml**: 159 160```yaml 161apiVersion: v1 162kind: Namespace 163metadata: 164 annotations: 165 ingress-whitelist: "*.qa.acmecorp.com,*.internal.acmecorp.com" 166 name: qa 167``` 168 169**production-namespace.yaml**: 170 171```yaml 172apiVersion: v1 173kind: Namespace 174metadata: 175 annotations: 176 ingress-whitelist: "*.acmecorp.com" 177 name: production 178``` 179 180```bash 181kubectl create -f qa-namespace.yaml 182kubectl create -f production-namespace.yaml 183``` 184 185Next, define two Ingress objects. One of the Ingress objects will be permitted 186and the other will be rejected. 187 188**ingress-ok.yaml**: 189 190```yaml 191apiVersion: extensions/v1beta1 192kind: Ingress 193metadata: 194 name: ingress-ok 195spec: 196 rules: 197 - host: signin.acmecorp.com 198 http: 199 paths: 200 - backend: 201 serviceName: nginx 202 servicePort: 80 203``` 204 205**ingress-bad.yaml**: 206 207```yaml 208apiVersion: extensions/v1beta1 209kind: Ingress 210metadata: 211 name: ingress-bad 212spec: 213 rules: 214 - host: acmecorp.com 215 http: 216 paths: 217 - backend: 218 serviceName: nginx 219 servicePort: 80 220``` 221 222Finally, try to create both Ingress objects: 223 224```bash 225kubectl create -f ingress-ok.yaml -n production 226kubectl create -f ingress-bad.yaml -n qa 227``` 228 229The second Ingress is rejected because it's hostname does not match the whitelist in the `qa` namespace. 230 231### 6. Modify the policy and exercise the changes 232 233OPA allows you to modify policies on-the-fly without recompiling any of the services that offload policy decisions to it. 234 235To enforce the second half of the policy from the start of this tutorial you can load another policy into OPA that rejects Ingress objects in different namespaces from sharing the same hostname. 236 237[ingress-conflicts.rego](https://github.com/open-policy-agent/opa/docs/book/tutorials/kubernetes-admission-control-validation/ingress-conflicts.rego): 238 239<pre><code class="lang-ruby">{% include "./tutorials/kubernetes-admission-control-validation/ingress-conflicts.rego" %}</code></pre> 240 241```bash 242kubectl create configmap ingress-conflicts --from-file=ingress-conflicts.rego 243``` 244 245The OPA sidecar annotates ConfigMaps containing policies to indicate if they 246were installed successfully. Verify the ConfigMap was installed successfully. 247 248``` 249kubectl get configmap ingress-conflicts -o yaml 250``` 251 252Test that you cannot create an Ingress in another namespace with the same hostname as the one created earlier. 253 254**staging-namespace.yaml**: 255 256``` 257apiVersion: v1 258kind: Namespace 259metadata: 260 annotations: 261 ingress-whitelist: "*.acmecorp.com" 262 name: staging 263 ``` 264 265```bash 266kubectl create -f staging-namespace.yaml 267``` 268 269```bash 270kubectl create -f ingress-ok.yaml -n staging 271``` 272 273## Wrap Up 274 275Congratulations for finishing the tutorial! 276 277This tutorial showed how you can leverage OPA to enforce admission control 278decisions in Kubernetes clusters without modifying or recompiling any 279Kubernetes components. Furthermore, once Kubernetes is configured to use OPA as 280an External Admission Controller, policies can be modified on-the-fly to 281satisfy changing operational requirements. 282 283For more information about deploying OPA on top of Kubernetes, see 284[Deployments - Kubernetes](/deployments.md#kubernetes). 285