diff --git a/content/en/blog/_posts/2021-12-15-pod-security-admission-beta.md b/content/en/blog/_posts/2021-12-15-pod-security-admission-beta.md new file mode 100644 index 0000000000000..1ae0a2351250b --- /dev/null +++ b/content/en/blog/_posts/2021-12-15-pod-security-admission-beta.md @@ -0,0 +1,612 @@ +--- +layout: blog +title: 'Pod Security Admission (PSA) Graduates to Beta' +date: 2021-12-15 +slug: pod-security-admission-beta +--- + +**Authors:** Jim Angel (Google), Lachlan Evenson (Microsoft) + +With the release of Kubernetes v1.23, [Pod Security Admission (PSA)](/docs/concepts/security/pod-security-admission/) has now entered beta. PSA is a [built-in](/docs/reference/access-authn-authz/admission-controllers/) admission controller that evaluates pod specifications against a predefined set of [Pod Security Standards](/docs/concepts/security/pod-security-standards/) and determines whether to `admit` or `deny` the pod from running. PSA is the successor to [PodSecurityPolicy](/docs/concepts/policy/pod-security-policy/) which was deprecated in v1.21 and is removed in v1.25. Let's explore how Pod Security Admission works in the hope that cluster administrators and developers alike will use this to enforce secure defaults for their workloads. In this blog we will cover the key concepts of Pod Security Admission along with how to use it. + +## Why Pod Security Admission + +Pod Security Admission overcomes key shortcomings of Kubernetes' existing, but deprecated, PodSecurityPolicy (PSP) mechanism: + + * Policy authorization model — challenging to deploy with controllers. + * Risks around switching — a lack of dry-run/audit capabilities made it hard to enable PodSecurityPolicy. + * Inconsistent and Unbounded API — security constraints were ever-changing which led to the API needing changes. + +The shortcomings of PSP made it very difficult to use which led the community to reevaluate whether or not a better implementation could achieve the same goals. One of those goals was to provide an out-of-the-box solution to apply security best practices. Pod Security Admission ships with predefined Pod Security levels that a cluster administrator can configure to meet the desired security posture. + +It's important to note that Pod Security Admission doesn't have complete feature parity with the deprecated PodSecurityPolicy. Specifically, it doesn't have the ability to `mutate` or change Kubernetes resources to auto-remediate a policy violation on behalf of the user. Additionally, it doesn't provide fine-grain control over each allowed field and value within a pod specification or any other Kubernetes resource that you may wish to evaluate. If you need more fine-grained policy control then take a look at the [Gatekeeper](https://github.com/open-policy-agent/gatekeeper) and [other](/docs/concepts/security/pod-security-standards/#faq) projects which support such use cases. + +Pod Security Admission also adheres to Kubernetes best practices of declarative object management by denying resources that violate the policy. This requires resources to be updated in source repositories, and tooling to be updated prior to being deployed to Kubernetes. + +## How Does Pod Security Admission Work? + +Pod Security Admission is a built-in [admission controller](/docs/reference/access-authn-authz/admission-controllers/). Admission controllers function by intercepting requests in the Kubernetes API server prior to persistence to storage. They can either `admit` or `deny` a request. In the case of Pod Security Admission, pod specifications will be evaluated against a configured policy in the form of a Pod Security Standard. This means that security sensitive fields in a pod specification will only be allowed to have [specific](h/docs/concepts/security/pod-security-standards/#profile-details) values. + +## Configuring Pod Security Admission + +### Pod Security Standards + +In order to use Pod Security Admission we first need to understand [Pod Security Standards](/docs/concepts/security/pod-security-standards/). These standards define three different policy levels that range from permissive to restrictive. These levels are as follows: + * Privileged — open and unrestricted + * Baseline — Covers most common known privilege escalations and provides an easier onboarding + * Restricted — Highly restricted, covering best practices. May cause compatibility issues + +Each of these policy levels define which fields are restricted within a pod specification and the allowed values. Some of the fields restricted by these policies include: + * `spec.containers[*].ports` + * `spec.volumes[*].hostPath` + * `spec.securityContext` + * `spec.containers[*].securityContext` + +These policy levels can be applied in two different ways. Either via namespace labels or an AdmissionConfiguration resource. Using namespace labels allows for granular per namespace policy selection whereas using AdmissionConfiguration allows cluster-level defaulting along with exemptions. + +### Policy modes + +Policies are applied using modes. Here is a list of modes: + * `enforce` — Any Pods that violate the policy will be rejected + * `audit` — Pods with violations will be allowed and an audit annotation will be added + * `warn` — Pods that violate the policy will be allowed and a warning message will be sent back to the user. + +In addition to modes you can also pin the policy to a specific version for example v1.22. Pinning to a specific version allows the behavior to remain consistent as policy changes happen over Kubernetes releases. + +## Hands on demo + +### Prerequisites + +- [kind CLI installed](https://kind.sigs.k8s.io/docs/user/quick-start/#installation) +- [Docker](https://docs.docker.com/get-docker/) or [Podman](https://podman.io/getting-started/installation) container runtime & CLI + +### Deploy a kind cluster + +```shell +kind create cluster --image kindest/node:v1.23.0 +``` + +It might take awhile to start and once it's started it might take a minute or so before the node becomes ready. + +```shell +kubectl cluster-info --context kind-kind +``` + +Wait for the node STATUS to become ready. + +```shell +kubectl get nodes +``` + +The output is similar to this: + +```shell +NAME STATUS ROLES AGE VERSION +kind-control-plane Ready control-plane,master 54m v1.23.0 +``` + +### Confirm PSA is enabled + +The best way to [confirm the API's default enabled plugins](/docs/reference/access-authn-authz/admission-controllers/#which-plugins-are-enabled-by-default) is to check the Kubernetes API container's help arguments. + +```shell +kubectl -n kube-system exec kube-apiserver-kind-control-plane -it -- kube-apiserver -h | grep "default enabled ones" +``` + +The output is similar to this: + +```shell +... + --enable-admission-plugins strings +admission plugins that should be enabled in addition +to default enabled ones (NamespaceLifecycle, LimitRanger, +ServiceAccount, TaintNodesByCondition, PodSecurity, Priority, +DefaultTolerationSeconds, DefaultStorageClass, +StorageObjectInUseProtection, PersistentVolumeClaimResize, +RuntimeClass, CertificateApproval, CertificateSigning, +CertificateSubjectRestriction, DefaultIngressClass, +MutatingAdmissionWebhook, ValidatingAdmissionWebhook, +ResourceQuota). +... +``` + +`PodSecurity` is listed in the group of default enabled admission plugins. + +### Configure Pod Security Admission + +Policies are applied to a namespace via labels. These labels are as follows: + * `pod-security.kubernetes.io/: ` (required) + * `pod-security.kubernetes.io/-version: ` (*optional*, defaults to latest) + +A specific version can be supplied for each enforcement mode. The version pins the policy to the version that was shipped as part of the Kubernetes release. Pinning to a specific Kubernetes version allows for deterministic policy behavior while allowing flexibility for future updates to Security Policy Standards + +From earlier, the modes are: + * `enforce` — Any Pods that violate the policy will be rejected + * `audit` — Pods with violations will be allowed and an audit annotation will be added + * `warn` — Pods that violate the policy will be allowed and a warning message will be sent back to the user. + +> Note: The default for enforce/warn/audit is `privileged` if not specified + +When to `warn`? + +Don't use `warn` for the exact same level+version of the policy as `enforce`. In the admission sequence, if `enforce` fails, the entire sequence fails before evaluating the `warn`. + +The typical uses for `warn` are: + +* `warn` at the same level but a different version (e.g. pin `enforce` to *restricted+v1.23* and `warn` at *restricted+latest*) +* `warn` at a stricter level (e.g. `enforce` baseline, `warn` restricted) + + +Let's create 3 namespaces and apply all 3 PSA security profiles. First, create the namespaces: + +```shell +kubectl create namespace kube-test-privileged +kubectl create namespace kube-test-baseline +kubectl create namespace kube-test-restricted +``` + +Add the respective labels. + +```shell +# kube-test-privileged enforces a "privileged" security policy and warns / audits on baseline +kubectl label --overwrite ns kube-test-privileged \ + pod-security.kubernetes.io/enforce=privileged \ + pod-security.kubernetes.io/warn=baseline \ + pod-security.kubernetes.io/audit=baseline + +# kube-test-baseline enforces a "baseline" security policy and warns / audits on restricted +kubectl label --overwrite ns kube-test-baseline \ + pod-security.kubernetes.io/enforce=baseline \ + pod-security.kubernetes.io/warn=restricted \ + pod-security.kubernetes.io/audit=restricted + +# kube-test-restricted enforces a "restricted" security policy and audits on restricted +kubectl label --overwrite ns kube-test-restricted \ + pod-security.kubernetes.io/enforce=restricted \ + pod-security.kubernetes.io/audit=restricted +``` + +### Deploy demo workloads + +Each workload represents a higher level of security that would not pass the profile that comes after it. + +For the following examples, use the `k8s.gcr.io/pause` container which is a small container that runs a never ending `sleep` command. Pod Security is not interested in the container, but rather the Kubernetes manifests that are being admitted. + +**Privileged level and workload** + +For the privileged pod, allow [privilege escalation](/docs/concepts/security/pod-security-standards/#privileged). This allows the process inside a container to gain new processes and can be dangerous if untrusted. + +First, let's try to deploy it in the restricted namespace: + +```shell +cat < +``` + +### Applying a cluster-wide policy + +In addition to applying labels to namespaces to configure policy you can also configure cluster-wide policies and exemptions using the AdmissionConfiguration resource. + +Using this resource, policy definitions are applied cluster-wide by default and any policy that is applied via namespace labels will take precedence. + +There is no runtime configurable API for this resource and a cluster administrator would need to specify a path to the file below via the `--admission-control-config-file` flag on the API server. + +In the following resource we are enforcing the baseline policy and warning and auditing the baseline policy. We are also making the kube-system namespace exempt from this policy. + +It's not recommended to alter control plane / clusters after install, so let's build a new cluster with a default policy on all namespaces. + +First, delete the current cluster. + +```shell +kind delete cluster +``` + +Create a Pod Security configuration. + + + +```shell +cat < pod-security.yaml +apiVersion: apiserver.config.k8s.io/v1 +kind: AdmissionConfiguration +plugins: +- name: PodSecurity + configuration: + apiVersion: pod-security.admission.config.k8s.io/v1beta1 + kind: PodSecurityConfiguration + defaults: + enforce: "baseline" + enforce-version: "latest" + audit: "baseline" + audit-version: "latest" + warn: "restricted" + warn-version: "latest" + exemptions: + # Array of authenticated usernames to exempt. + usernames: [] + # Array of runtime class names to exempt. + runtimeClasses: [] + # Array of namespaces to exempt. + namespaces: [kube-system] +EOF +``` + +We now have a default baseline policy, next pass it to the kind configuration to enable the `--admission-control-config-file` API server argument and pass the policy file. + +```shell +cat < kind-config.yaml +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: +- role: control-plane + kubeadmConfigPatches: + - | + kind: ClusterConfiguration + apiServer: + # enable admission-control-config flag on the API server + extraArgs: + admission-control-config-file: /etc/kubernetes/policies/pod-security.yaml + # mount new file / directories on the control plane + extraVolumes: + - name: policies + hostPath: /etc/kubernetes/policies + mountPath: /etc/kubernetes/policies + readOnly: true + pathType: "DirectoryOrCreate" + # mount the local file on the control plane + extraMounts: + - hostPath: ./pod-security.yaml + containerPath: /etc/kubernetes/policies/pod-security.yaml + readOnly: true +EOF +``` + +```shell +kind create cluster --image kindest/node:v1.23.0 --config kind-config.yaml +``` + +Let's look at the default namespace. + +```shell +kubectl describe namespace default +``` + +The output is similar to this: + +``` +Name: default +Labels: kubernetes.io/metadata.name=default +Annotations: +Status: Active + +No resource quota. + +No LimitRange resource. +``` + +Let's create a new namespace and see if the labels apply there. + +```shell +kubectl create namespace test-defaults +kubectl describe namespace test-defaults +``` + +Same. + +``` +Name: test-defaults +Labels: kubernetes.io/metadata.name=test-defaults +Annotations: +Status: Active + +No resource quota. + +No LimitRange resource. +``` + +Can a privileged workload be deployed? + +```shell +cat <