mirror of
https://github.com/jpetazzo/container.training.git
synced 2026-02-14 17:49:59 +00:00
➕ Add Pod Security Admission
This commit is contained in:
16
k8s/admission-configuration.yaml
Normal file
16
k8s/admission-configuration.yaml
Normal 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
|
||||
291
slides/k8s/pod-security-admission.md
Normal file
291
slides/k8s/pod-security-admission.md
Normal 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*
|
||||
184
slides/k8s/pod-security-intro.md
Normal file
184
slides/k8s/pod-security-intro.md
Normal 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
|
||||
@@ -1,128 +1,12 @@
|
||||
# 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
|
||||
|
||||
- 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
|
||||
```
|
||||
|
||||
]
|
||||
(available in alpha since Kubernetes 1.22)
|
||||
|
||||
---
|
||||
|
||||
@@ -50,7 +50,9 @@ content:
|
||||
#- k8s/cloud-controller-manager.md
|
||||
#- k8s/bootstrap.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/csr-api.md
|
||||
- k8s/openid-connect.md
|
||||
|
||||
@@ -58,7 +58,9 @@ content:
|
||||
- k8s/control-plane-auth.md
|
||||
###- k8s/bootstrap.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/metrics-server.md
|
||||
- k8s/cluster-sizing.md
|
||||
|
||||
@@ -100,7 +100,9 @@ content:
|
||||
#- k8s/user-cert.md
|
||||
#- k8s/csr-api.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/build-with-docker.md
|
||||
#- k8s/build-with-kaniko.md
|
||||
|
||||
@@ -96,7 +96,9 @@ content:
|
||||
-
|
||||
- k8s/netpol.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/csr-api.md
|
||||
- k8s/openid-connect.md
|
||||
|
||||
@@ -95,7 +95,9 @@ content:
|
||||
- k8s/authn-authz.md
|
||||
#- k8s/csr-api.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/exercise-configmap.md
|
||||
|
||||
Reference in New Issue
Block a user