Add Pod Security Admission

This commit is contained in:
Jérôme Petazzoni
2021-11-18 18:24:43 +01:00
parent 5d3ab6b61f
commit 5e50f2a3a4
9 changed files with 510 additions and 125 deletions

View File

@@ -0,0 +1,16 @@
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
configuration:
apiVersion: pod-security.admission.config.k8s.io/v1alpha1
kind: PodSecurityConfiguration
defaults:
enforce: baseline
audit: baseline
warn: baseline
exemptions:
usernames:
- cluster-admin
namespaces:
- kube-system

View File

@@ -0,0 +1,291 @@
# Pod Security Admission
- "New" policies
(available in alpha since Kubernetes 1.22)
- Easier to use
(doesn't require complex interaction bewteen policies and RBAC)
---
## PSA in theory
- Leans on PSS (Pod Security Standards)
- Defines three policies:
- `privileged` (can do everything; for system components)
- `restricted` (no root user; almost no capabilities)
- `baseline` (in-between with reasonable defaults)
- Label namespaces to indicate which policies are allowed there
- Also supports setting global defaults
- Supports `enforce`, `audit`, and `warn` modes
---
## Pod Security Standards
- `privileged`
- can do everything
- `baseline`
- disables hostNetwork, hostPID, hostIPC, hostPorts, hostPath volumes
- limits which SELinux/AppArmor profiles can be used
- containers can still run as root and use most capabilities
- `restricted`
- limits volumes to configMap, emptyDir, ephemeral, secret, PVC
- containers can't run as root, only capability is NET_BIND_SERVICE
- `baseline` (can't do privileged pods, hostPath, hostNetwork...)
---
class: extra-details
## Why `baseline` ≠ `restricted` ?
- `baseline` = should work for that vast majority of images
- `restricted` = better, but might break / require adaptation
- Many images run as root by default
- Some images use CAP_CHOWN (to `chown` files)
- Some programs use CAP_NET_RAW (e.g. `ping`)
---
## PSA in practice
- Step 1: enable the PodSecurity admission plugin
- Step 2: label some Namespaces
- Step 3: provide an AdmissionConfiguration (optional)
- Step 4: profit!
---
## Enabling PodSecurity
- This requires Kubernetes 1.22 or later
- This requires the ability to reconfigure the API server
- The following slides assume that we're using `kubeadm`
(and have write access to `/etc/kubernetes/manifests`)
---
## Reconfiguring the API server
- In Kubernetes 1.22, we need to enable the `PodSecurity` feature gate
- In later versions, this might be enabled automatically
.exercise[
- Edit `/etc/kubernetes/manifests/kube-apiserver.yaml`
- In the `command` list, add `--feature-gates=PodSecurity=true`
- Save, quit, wait for the API server to be back up again
]
Note: for bonus points, edit the `kubeadm-config` ConfigMap instead!
---
## Namespace labels
- Three optional labels can be added to namespaces:
`pod-security.kubernetes.io/enforce`
`pod-security.kubernetes.io/audit`
`pod-security.kubernetes.io/warn`
- The values can be: `baseline`, `restricted`, `privileged`
(setting it to `privileged` doesn't really do anything)
---
## `enforce`, `audit`, `warn`
- `enforce` = prevents creation of pods
- `warn` = allow creation but include a warning in the API response
(will be visible e.g. in `kubectl` output)
- `audit` = allow creation but generate an API audit event
(will be visible if API auditing has been enabled and configured)
---
## Blocking privileged pods
- Let's block `privileged` pods everywhere
- And issue warnings and audit for anything above the `restricted` level
.exercise[
- Set up the default policy for all namespaces:
```bash
kubectl label namespaces \
pod-security.kubernetes.io/enforce=baseline \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/warn=restricted \
--all
```
]
Note: warnings will be issued for infringing pods, but they won't be affected yet.
---
class: extra-details
## Check before you apply
- When adding an `enforce` policy, we see warnings
(for the pods that would infringe that policy)
- It's possible to do a `--dry-run=server` to see these warnings
(without applying the label)
- It will only show warnings for `enforce` policies
(not `warn` or `audit`)
---
## Relaxing `kube-system`
- We have many system components in `kube-system`
- These pods aren't affected yet, but if there is a rolling update or something like that, the new pods won't be able to come up
.exercise[
- Let's allow `privileged` pods in `kube-system`:
```bash
kubectl label namespaces \
pod-security.kubernetes.io/enforce=privileged \
pod-security.kubernetes.io/audit=privileged \
pod-security.kubernetes.io/warn=privileged \
kube-system --overwrite
```
]
---
## What about new namespaces?
- If new namespaces are created, they will get default permissions
- We can change that be using an *admission configuration*
- Step 1: write an "admission configuration file"
- Step 2: make sure that file is readable by the API server
- Step 3: add a flag to the API server to read that file
---
## Admission Configuration
Let's use @@LINK[k8s/admission-configuration.yaml]:
```yaml
@@INCLUDE[k8s/admission-configuration.yaml]
```
---
## Copy the file to the API server
- We need the file to be available from the API server pod
- For convenience, let's copy it do `/etc/kubernetes/pki`
(it's definitely where it *should* be, but that'll do!)
.exercise[
- Copy the file:
```bash
sudo cp ~/container.training/k8s/admission-configuration.yaml \
/etc/kubernetes/pki
```
]
---
## Reconfigure the API server
- We need to add a flag to the API server to use that file
.exercise[
- Edit `/etc/kubernetes/manifests/kube-apiserver.yaml`
- In the list of `command` parameters, add:
`--admission-control-config-file=/etc/kubernetes/pki/admission-configuration.yaml`
- Wait until the API server comes back online
]
---
## Test the new default policy
- Create a new Namespace
- Try to create the "hacktheplanet" DaemonSet in the new namespace
- We get a warning when creating the DaemonSet
- The DaemonSet is created
- But the Pods don't get created
---
## Clean up
- We probably want to remove the API server flags that we added
(the feature gate and the admission configuration)
???
:EN:- Preventing privilege escalation with Pod Security Admission
:FR:- Limiter les droits des conteneurs avec *Pod Security Admission*

View File

@@ -0,0 +1,184 @@
# Restricting Pod Permissions
- By default, our pods and containers can do *everything*
(including taking over the entire cluster)
- We are going to show an example of a malicious pod
(which will give us root access to the whole cluster)
- Then we will explain how to avoid this with admission control
(PodSecurityAdmission, PodSecurityPolicy, or external policy engine)
---
## Setting up a namespace
- For simplicity, let's work in a separate namespace
- Let's create a new namespace called "green"
.exercise[
- Create the "green" namespace:
```bash
kubectl create namespace green
```
- Change to that namespace:
```bash
kns green
```
]
---
## Creating a basic Deployment
- Just to check that everything works correctly, deploy NGINX
.exercise[
- Create a Deployment using the official NGINX image:
```bash
kubectl create deployment web --image=nginx
```
- Confirm that the Deployment, ReplicaSet, and Pod exist, and that the Pod is running:
```bash
kubectl get all
```
]
---
## One example of malicious pods
- We will now show an escalation technique in action
- We will deploy a DaemonSet that adds our SSH key to the root account
(on *each* node of the cluster)
- The Pods of the DaemonSet will do so by mounting `/root` from the host
.exercise[
- Check the file `k8s/hacktheplanet.yaml` with a text editor:
```bash
vim ~/container.training/k8s/hacktheplanet.yaml
```
- If you would like, change the SSH key (by changing the GitHub user name)
]
---
## Deploying the malicious pods
- Let's deploy our "exploit"!
.exercise[
- Create the DaemonSet:
```bash
kubectl create -f ~/container.training/k8s/hacktheplanet.yaml
```
- Check that the pods are running:
```bash
kubectl get pods
```
- Confirm that the SSH key was added to the node's root account:
```bash
sudo cat /root/.ssh/authorized_keys
```
]
---
## Mitigations
- This can be avoided with *admission control*
- Admission control = filter for (write) API requests
- Admission control can use:
- plugins (compiled in API server; enabled/disabled by reconfiguration)
- webhooks (registesred dynamically)
- Admission control has many other uses
(enforcing quotas, adding ServiceAccounts automatically, etc.)
---
## Admission plugins
- [PodSecurityPolicy](https://kubernetes.io/docs/concepts/policy/pod-security-policy/) (will be removed in Kubernetes 1.24)
- create PodSecurityPolicy resources
- create Role that can `use` a PodSecurityPolicy
- create RoleBinding that grants the Role to a user or ServiceAccount
- [PodSecurityAdmission](https://kubernetes.io/docs/concepts/security/pod-security-admission/) (alpha since Kubernetes 1.22)
- use pre-defined policies (privileged, baseline, restricted)
- label namespaces to indicate which policies they can use
- optionally, define default rules (in the absence of labels)
---
## Dynamic admission
- Leverage ValidatingWebhookConfigurations
(to register a validating webhook)
- Examples:
[Kubewarden](https://www.kubewarden.io/)
[Kyverno](https://kyverno.io/policies/pod-security/)
[OPA Gatekeeper](https://github.com/open-policy-agent/gatekeeper)
- Pros: available today; very flexible and customizable
- Cons: performance and reliability of external webhook
---
## Acronym salad
- PSP = Pod Security Policy
- an admission plugin called PodSecurityPolicy
- a resource named PodSecurityPolicy (`apiVersion: policy/v1beta1`)
- PSA = Pod Security Admission
- an admission plugin called PodSecurity, enforcing PSS
- PSS = Pod Security Standards
- a set of 3 policies (privileged, baseline, restricted)\
???
:EN:- Mechanisms to prevent pod privilege escalation
:FR:- Les mécanismes pour limiter les privilèges des pods

View File

@@ -1,128 +1,12 @@
# Pod Security Policies # Pod Security Policies
- By default, our pods and containers can do *everything* - "Legacy" policies
(including taking over the entire cluster) (deprecated since Kubernetes 1.21; will be removed in 1.25)
- We are going to show an example of a malicious pod - Superseded by Pod Security Standards + Pod Security Admission
- Then we will explain how to avoid this with PodSecurityPolicies (available in alpha since Kubernetes 1.22)
- We will enable PodSecurityPolicies on our cluster
- We will create a couple of policies (restricted and permissive)
- Finally we will see how to use them to improve security on our cluster
---
## Setting up a namespace
- For simplicity, let's work in a separate namespace
- Let's create a new namespace called "green"
.exercise[
- Create the "green" namespace:
```bash
kubectl create namespace green
```
- Change to that namespace:
```bash
kns green
```
]
---
## Creating a basic Deployment
- Just to check that everything works correctly, deploy NGINX
.exercise[
- Create a Deployment using the official NGINX image:
```bash
kubectl create deployment web --image=nginx
```
- Confirm that the Deployment, ReplicaSet, and Pod exist, and that the Pod is running:
```bash
kubectl get all
```
]
---
## One example of malicious pods
- We will now show an escalation technique in action
- We will deploy a DaemonSet that adds our SSH key to the root account
(on *each* node of the cluster)
- The Pods of the DaemonSet will do so by mounting `/root` from the host
.exercise[
- Check the file `k8s/hacktheplanet.yaml` with a text editor:
```bash
vim ~/container.training/k8s/hacktheplanet.yaml
```
- If you would like, change the SSH key (by changing the GitHub user name)
]
---
## Deploying the malicious pods
- Let's deploy our "exploit"!
.exercise[
- Create the DaemonSet:
```bash
kubectl create -f ~/container.training/k8s/hacktheplanet.yaml
```
- Check that the pods are running:
```bash
kubectl get pods
```
- Confirm that the SSH key was added to the node's root account:
```bash
sudo cat /root/.ssh/authorized_keys
```
]
---
## Cleaning up
- Before setting up our PodSecurityPolicies, clean up that namespace
.exercise[
- Remove the DaemonSet:
```bash
kubectl delete daemonset hacktheplanet
```
- Remove the Deployment:
```bash
kubectl delete deployment web
```
]
--- ---

View File

@@ -50,7 +50,9 @@ content:
#- k8s/cloud-controller-manager.md #- k8s/cloud-controller-manager.md
#- k8s/bootstrap.md #- k8s/bootstrap.md
- k8s/control-plane-auth.md - k8s/control-plane-auth.md
- k8s/podsecuritypolicy.md - k8s/pod-security-intro.md
- k8s/pod-security-policies.md
- k8s/pod-security-admission.md
- k8s/user-cert.md - k8s/user-cert.md
- k8s/csr-api.md - k8s/csr-api.md
- k8s/openid-connect.md - k8s/openid-connect.md

View File

@@ -58,7 +58,9 @@ content:
- k8s/control-plane-auth.md - k8s/control-plane-auth.md
###- k8s/bootstrap.md ###- k8s/bootstrap.md
- k8s/netpol.md - k8s/netpol.md
- k8s/podsecuritypolicy.md - k8s/pod-security-intro.md
- k8s/pod-security-policies.md
- k8s/pod-security-admission.md
- - k8s/resource-limits.md - - k8s/resource-limits.md
- k8s/metrics-server.md - k8s/metrics-server.md
- k8s/cluster-sizing.md - k8s/cluster-sizing.md

View File

@@ -100,7 +100,9 @@ content:
#- k8s/user-cert.md #- k8s/user-cert.md
#- k8s/csr-api.md #- k8s/csr-api.md
#- k8s/openid-connect.md #- k8s/openid-connect.md
#- k8s/podsecuritypolicy.md #- k8s/pod-security-intro.md
#- k8s/pod-security-policies.md
#- k8s/pod-security-admission.md
#- k8s/exercise-configmap.md #- k8s/exercise-configmap.md
#- k8s/build-with-docker.md #- k8s/build-with-docker.md
#- k8s/build-with-kaniko.md #- k8s/build-with-kaniko.md

View File

@@ -96,7 +96,9 @@ content:
- -
- k8s/netpol.md - k8s/netpol.md
- k8s/authn-authz.md - k8s/authn-authz.md
- k8s/podsecuritypolicy.md - k8s/pod-security-intro.md
- k8s/pod-security-policies.md
- k8s/pod-security-admission.md
- k8s/user-cert.md - k8s/user-cert.md
- k8s/csr-api.md - k8s/csr-api.md
- k8s/openid-connect.md - k8s/openid-connect.md

View File

@@ -95,7 +95,9 @@ content:
- k8s/authn-authz.md - k8s/authn-authz.md
#- k8s/csr-api.md #- k8s/csr-api.md
#- k8s/openid-connect.md #- k8s/openid-connect.md
#- k8s/podsecuritypolicy.md #- k8s/pod-security-intro.md
#- k8s/pod-security-policies.md
#- k8s/pod-security-admission.md
- -
- k8s/volumes.md - k8s/volumes.md
#- k8s/exercise-configmap.md #- k8s/exercise-configmap.md