Rewrite PodSecurityPolicy migration guide (#31782)
This commit is contained in:
@@ -329,6 +329,11 @@ main {
|
|||||||
.td-content>table td {
|
.td-content>table td {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.no-word-break td,
|
||||||
|
table.no-word-break code {
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,13 @@ weight: 30
|
|||||||
|
|
||||||
{{< feature-state for_k8s_version="v1.21" state="deprecated" >}}
|
{{< feature-state for_k8s_version="v1.21" state="deprecated" >}}
|
||||||
|
|
||||||
PodSecurityPolicy is deprecated as of Kubernetes v1.21, and will be removed in v1.25. It has been replaced by
|
{{< caution >}}
|
||||||
[Pod Security Admission](/docs/concepts/security/pod-security-admission/). For more information on the deprecation,
|
PodSecurityPolicy is deprecated as of Kubernetes v1.21, and **will be removed in v1.25**. We recommend migrating to
|
||||||
|
[Pod Security Admission](/docs/concepts/security/pod-security-admission/), or a 3rd party admission plugin.
|
||||||
|
For a migration guide, see [Migrate from PodSecurityPolicy to the Built-In PodSecurity Admission Controller](/docs/tasks/configure-pod-container/migrate-from-psp/).
|
||||||
|
For more information on the deprecation,
|
||||||
see [PodSecurityPolicy Deprecation: Past, Present, and Future](/blog/2021/04/06/podsecuritypolicy-deprecation-past-present-and-future/).
|
see [PodSecurityPolicy Deprecation: Past, Present, and Future](/blog/2021/04/06/podsecuritypolicy-deprecation-past-present-and-future/).
|
||||||
|
{{< /caution >}}
|
||||||
|
|
||||||
Pod Security Policies enable fine-grained authorization of pod creation and
|
Pod Security Policies enable fine-grained authorization of pod creation and
|
||||||
updates.
|
updates.
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ fail validation.
|
|||||||
<caption style="display:none">Baseline policy specification</caption>
|
<caption style="display:none">Baseline policy specification</caption>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong>Control</strong></td>
|
<th>Control</th>
|
||||||
<td><strong>Policy</strong></td>
|
<th>Policy</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="white-space: nowrap">HostProcess</td>
|
<td style="white-space: nowrap">HostProcess</td>
|
||||||
|
|||||||
@@ -0,0 +1,272 @@
|
|||||||
|
---
|
||||||
|
reviewers:
|
||||||
|
- tallclair
|
||||||
|
- liggitt
|
||||||
|
title: Mapping PodSecurityPolicies to Pod Security Standards
|
||||||
|
content_type: concept
|
||||||
|
weight: 95
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- overview -->
|
||||||
|
The tables below enumerate the configuration parameters on
|
||||||
|
[PodSecurityPolicy](/docs/concepts/policy/pod-security-policy/) objects, whether the field mutates
|
||||||
|
and/or validates pods, and how the configuration values map to the
|
||||||
|
[Pod Security Standards](/docs/concepts/security/pod-security-standards/).
|
||||||
|
|
||||||
|
For each applicable parameter, the allowed values for the
|
||||||
|
[Baseline](/docs/concepts/security/pod-security-standards/#baseline) and
|
||||||
|
[Restricted](/docs/concepts/security/pod-security-standards/#restricted) profiles are listed.
|
||||||
|
Anything outside the allowed values for those profiles would fall under the
|
||||||
|
[Privileged](/docs/concepts/security/pod-security-standards/#priveleged) profile. "No opinion"
|
||||||
|
means all values are allowed under all Pod Security Standards.
|
||||||
|
|
||||||
|
<!-- body -->
|
||||||
|
|
||||||
|
## PodSecurityPolicy Spec
|
||||||
|
|
||||||
|
The fields enumerated in this table are part of the `PodSecurityPolicySpec`, which is specified
|
||||||
|
under the `.spec` field path.
|
||||||
|
|
||||||
|
<table class="no-word-break">
|
||||||
|
<caption style="display:none">Mapping PodSecurityPolicySpec fields to Pod Security Standards</caption>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th><code>PodSecurityPolicySpec</code></th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Pod Security Standards Equivalent</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>privileged</code></td>
|
||||||
|
<td>Validating</td>
|
||||||
|
<td><b>Baseline & Restricted</b>: <code>false</code> / undefined / nil</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>defaultAddCapabilities</code></td>
|
||||||
|
<td>Mutating & Validating</td>
|
||||||
|
<td>Requirements match <code>allowedCapabilities</code> below.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>allowedCapabilities</code></td>
|
||||||
|
<td>Validating</td>
|
||||||
|
<td>
|
||||||
|
<p><b>Baseline</b>: subset of</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>AUDIT_WRITE</code></li>
|
||||||
|
<li><code>CHOWN</code></li>
|
||||||
|
<li><code>DAC_OVERRIDE</code></li>
|
||||||
|
<li><code>FOWNER</code></li>
|
||||||
|
<li><code>FSETID</code></li>
|
||||||
|
<li><code>KILL</code></li>
|
||||||
|
<li><code>MKNOD</code></li>
|
||||||
|
<li><code>NET_BIND_SERVICE</code></li>
|
||||||
|
<li><code>SETFCAP</code></li>
|
||||||
|
<li><code>SETGID</code></li>
|
||||||
|
<li><code>SETPCAP</code></li>
|
||||||
|
<li><code>SETUID</code></li>
|
||||||
|
<li><code>SYS_CHROOT</code></li>
|
||||||
|
</ul>
|
||||||
|
<p><b>Restricted</b>: empty / undefined / nil OR a list containing <i>only</i> <code>NET_BIND_SERVICE</code>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>requiredDropCapabilities</code></td>
|
||||||
|
<td>Mutating & Validating</td>
|
||||||
|
<td>
|
||||||
|
<p><b>Baseline</b>: no opinion</p>
|
||||||
|
<p><b>Restricted</b>: must include <code>ALL</code></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>volumes</code></td>
|
||||||
|
<td>Validating</td>
|
||||||
|
<td>
|
||||||
|
<p><b>Baseline</b>: anything except</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>hostPath</code></li>
|
||||||
|
<li><code>*</code></li>
|
||||||
|
</ul>
|
||||||
|
<p><b>Restricted</b>: subset of</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>configMap</code></li>
|
||||||
|
<li><code>csi</code></li>
|
||||||
|
<li><code>downwardAPI</code></li>
|
||||||
|
<li><code>emptyDir</code></li>
|
||||||
|
<li><code>ephemeral</code></li>
|
||||||
|
<li><code>persistentVolumeClaim</code></li>
|
||||||
|
<li><code>projected</code></li>
|
||||||
|
<li><code>secret</code></li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>hostNetwork</code></td>
|
||||||
|
<td>Validating</td>
|
||||||
|
<td><b>Baseline & Restricted</b>: <code>false</code> / undefined / nil</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>hostPorts</code></td>
|
||||||
|
<td>Validating</td>
|
||||||
|
<td><b>Baseline & Restricted</b>: undefined / nil / empty</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>hostPID</code></td>
|
||||||
|
<td>Validating</td>
|
||||||
|
<td><b>Baseline & Restricted</b>: <code>false</code> / undefined / nil</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>hostIPC</code></td>
|
||||||
|
<td>Validating</td>
|
||||||
|
<td><b>Baseline & Restricted</b>: <code>false</code> / undefined / nil</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>seLinux</code></td>
|
||||||
|
<td>Mutating & Validating</td>
|
||||||
|
<td>
|
||||||
|
<p><b>Baseline & Restricted</b>:
|
||||||
|
<code>seLinux.rule</code> is <code>MustRunAs</code>, with the following <code>options</code></p>
|
||||||
|
<ul>
|
||||||
|
<li><code>user</code> is unset (<code>""</code> / undefined / nil)</li>
|
||||||
|
<li><code>role</code> is unset (<code>""</code> / undefined / nil)</li>
|
||||||
|
<li><code>type</code> is unset or one of: <code>container_t, container_init_t, container_kvm_t</code></li>
|
||||||
|
<li><code>level</code> is anything</li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>runAsUser</code></td>
|
||||||
|
<td>Mutating & Validating</td>
|
||||||
|
<td>
|
||||||
|
<p><b>Baseline</b>: Anything</p>
|
||||||
|
<p><b>Restricted</b>: <code>rule</code> is <code>MustRunAsNonRoot</code></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>runAsGroup</code></td>
|
||||||
|
<td>Mutating (MustRunAs) & Validating</td>
|
||||||
|
<td>
|
||||||
|
<i>No opinion</i>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>supplementalGroups</code></td>
|
||||||
|
<td>Mutating & Validating</td>
|
||||||
|
<td>
|
||||||
|
<i>No opinion</i>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>fsGroup</code></td>
|
||||||
|
<td>Mutating & Validating</td>
|
||||||
|
<td>
|
||||||
|
<i>No opinion</i>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>readOnlyRootFilesystem</code></td>
|
||||||
|
<td>Mutating & Validating</td>
|
||||||
|
<td>
|
||||||
|
<i>No opinion</i>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>defaultAllowPrivilegeEscalation</code></td>
|
||||||
|
<td>Mutating</td>
|
||||||
|
<td>
|
||||||
|
<i>No opinion (non-validating)</i>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>allowPrivilegeEscalation</code></td>
|
||||||
|
<td>Mutating & Validating</td>
|
||||||
|
<td>
|
||||||
|
<p><i>Only mutating if set to <code>false</code></i></p>
|
||||||
|
<p><b>Baseline</b>: No opinion</p>
|
||||||
|
<p><b>Restricted</b>: <code>false</code></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>allowedHostPaths</code></td>
|
||||||
|
<td>Validating</td>
|
||||||
|
<td><i>No opinion (volumes takes precedence)</i></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>allowedFlexVolumes</code></td>
|
||||||
|
<td>Validating</td>
|
||||||
|
<td><i>No opinion (volumes takes precedence)</i></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>allowedCSIDrivers</code></td>
|
||||||
|
<td>Validating</td>
|
||||||
|
<td><i>No opinion (volumes takes precedence)</i></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>allowedUnsafeSysctls</code></td>
|
||||||
|
<td>Validating</td>
|
||||||
|
<td><b>Baseline & Restricted</b>: undefined / nil / empty</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>forbiddenSysctls</code></td>
|
||||||
|
<td>Validating</td>
|
||||||
|
<td><i>No opinion</i></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>allowedProcMountTypes</code><br><i>(alpha feature)</i></td>
|
||||||
|
<td>Validating</td>
|
||||||
|
<td><b>Baseline & Restricted</b>: <code>["Default"]</code> OR undefined / nil / empty</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>runtimeClass</code><br><code> .defaultRuntimeClassName</code></td>
|
||||||
|
<td>Mutating</td>
|
||||||
|
<td><i>No opinion</i></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>runtimeClass</code><br><code> .allowedRuntimeClassNames</code></td>
|
||||||
|
<td>Validating</td>
|
||||||
|
<td><i>No opinion</i></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
## PodSecurityPolicy annotations
|
||||||
|
|
||||||
|
The [annotations](/docs/concepts/overview/working-with-objects/annotations/) enumerated in this
|
||||||
|
table can be specified under `.metadata.annotations` on the PodSecurityPolicy object.
|
||||||
|
|
||||||
|
<table class="no-word-break">
|
||||||
|
<caption style="display:none">Mapping PodSecurityPolicy annotations to Pod Security Standards</caption>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th><code>PSP Annotation</code></th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Pod Security Standards Equivalent</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>seccomp.security.alpha.kubernetes.io</code><br><code>/defaultProfileName</code></td>
|
||||||
|
<td>Mutating</td>
|
||||||
|
<td><i>No opinion</i></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>seccomp.security.alpha.kubernetes.io</code><br><code>/allowedProfileNames</code></td>
|
||||||
|
<td>Validating</td>
|
||||||
|
<td>
|
||||||
|
<p><b>Baseline</b>: <code>"runtime/default,"</code> <i>(Trailing comma to allow unset)</i></p>
|
||||||
|
<p><b>Restricted</b>: <code>"runtime/default"</code> <i>(No trailing comma)</i></p>
|
||||||
|
<p><i><code>localhost/*</code> values are also permitted for both Baseline & Restricted.</i></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>apparmor.security.beta.kubernetes.io</code><br><code>/defaultProfileName</code></td>
|
||||||
|
<td>Mutating</td>
|
||||||
|
<td><i>No opinion</i></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>apparmor.security.beta.kubernetes.io</code><br><code>/allowedProfileNames</code></td>
|
||||||
|
<td>Validating</td>
|
||||||
|
<td>
|
||||||
|
<p><b>Baseline</b>: <code>"runtime/default,"</code> <i>(Trailing comma to allow unset)</i></p>
|
||||||
|
<p><b>Restricted</b>: <code>"runtime/default"</code> <i>(No trailing comma)</i></p>
|
||||||
|
<p><i><code>localhost/*</code> values are also permitted for both Baseline & Restricted.</i></p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
@@ -19,30 +19,323 @@ admission controller. This can be done effectively using a combination of dry-ru
|
|||||||
|
|
||||||
- Ensure the `PodSecurity` [feature gate](/docs/reference/command-line-tools-reference/feature-gates/#feature-gates-for-alpha-or-beta-features) is enabled.
|
- Ensure the `PodSecurity` [feature gate](/docs/reference/command-line-tools-reference/feature-gates/#feature-gates-for-alpha-or-beta-features) is enabled.
|
||||||
|
|
||||||
|
This page assumes you are already familiar with the basic [Pod Security Admission](/docs/concepts/security/pod-security-admission/)
|
||||||
|
concepts.
|
||||||
|
|
||||||
<!-- body -->
|
<!-- body -->
|
||||||
|
|
||||||
## Steps
|
## Overall approach
|
||||||
|
|
||||||
- **Eliminate mutating PodSecurityPolicies, if your cluster has any set up.**
|
There are multiple strategies you can take for migrating from PodSecurityPolicy to Pod Security
|
||||||
- Clone all mutating PSPs into a non-mutating version.
|
Admission. The following steps are one possible migration path, with a goal of minimizing both the
|
||||||
- Update all ClusterRoles authorizing use of those mutating PSPs to also authorize use of the
|
risks of a production outage and of a security gap.
|
||||||
non-mutating variant.
|
|
||||||
- Watch for Pods using the mutating PSPs and work with code owners to migrate to valid,
|
<!-- Keep section header numbering in sync with this list. -->
|
||||||
non-mutating resources.
|
0. Decide whether Pod Security Admission is the right fit for your use case.
|
||||||
- Delete mutating PSPs.
|
1. Review namespace permissions
|
||||||
- **Select a compatible policy level for each namespace.** Analyze existing resources in the
|
2. Simplify & standardize PodSecurityPolicies
|
||||||
namespace to drive this decision.
|
3. Update namespaces
|
||||||
- Review the requirements of the different [Pod Security Standards](/docs/concepts/security/pod-security-standards).
|
1. Identify an appropriate Pod Security level
|
||||||
- Evaluate the difference in privileges that would come from disabling the PSP controller.
|
2. Verify the Pod Security level
|
||||||
- In the event that a PodSecurityPolicy falls between two levels, consider:
|
3. Enforce the Pod Security level
|
||||||
- Selecting a _less_ permissive PodSecurity level prioritizes security, and may require adjusting
|
4. Bypass PodSecurityPolicy
|
||||||
workloads to fit within the stricter policy.
|
4. Review namespace creation processes
|
||||||
- Selecting a _more_ permissive PodSecurity level prioritizes avoiding disrupting or
|
5. Disable PodSecurityPolicy
|
||||||
changing workloads, but may allow workload authors in the namespace greater permissions
|
|
||||||
than desired.
|
## 0. Decide whether Pod Security Admission is right for you {#is-psa-right-for-you}
|
||||||
- **Apply the selected profiles in `warn` and `audit` mode.** This will give you an idea of how
|
|
||||||
your Pods will respond to the new policies, without breaking existing workloads. Iterate on your
|
Pod Security Admission was designed to meet the most common security needs out of the box, and to
|
||||||
[Pods' configuration](/docs/concepts/security/pod-security-admission#configuring-pods) until
|
provide a standard set of security levels across clusters. However, it is less flexible than
|
||||||
they are in compliance with the selected profiles.
|
PodSecurityPolicy. Notably, the following features are supported by PodSecurityPolicy but not Pod
|
||||||
- Apply the profiles in `enforce` mode.
|
Security Admission:
|
||||||
- Stop including `PodSecurityPolicy` in the `--enable-admission-plugins` flag.
|
|
||||||
|
- **Setting default security constraints** - Pod Security Admission is a non-mutating admission
|
||||||
|
controller, meaning it won't modify pods before validating them. If you were relying on this
|
||||||
|
aspect of PSP, you will need to either modify your workloads to meet the Pod Security constraints,
|
||||||
|
or use a [Mutating Admission Webhook](/docs/reference/access-authn-authz/extensible-admission-controllers/)
|
||||||
|
to make those changes. See [Simplify & Standardize PodSecurityPolicies](#simplify-psps) below for more detail.
|
||||||
|
- **Fine-grained control over policy definition** - Pod Security Admission only supports
|
||||||
|
[3 standard levels](/docs/concepts/security/pod-security-standards/).
|
||||||
|
If you require more control over specific constraints, then you will need to use a
|
||||||
|
[Validating Admission Webhook](/docs/reference/access-authn-authz/extensible-admission-controllers/)
|
||||||
|
to enforce those policies.
|
||||||
|
- **Sub-namespace policy granularity** - PodSecurityPolicy lets you bind different policies to
|
||||||
|
different Service Accounts or users, even within a single namespace. This approach has many
|
||||||
|
pitfalls and is not recommended, but if you require this feature anyway you will
|
||||||
|
need to use a 3rd party webhook instead. The exception to this is if you only need to completely exempt
|
||||||
|
specific users or [RuntimeClasses](/docs/concepts/containers/runtime-class/), in which case Pod
|
||||||
|
Security Admission does expose some
|
||||||
|
[static configuration for exemptions](/docs/concepts/security/pod-security-admission/#exemptions).
|
||||||
|
|
||||||
|
Even if Pod Security Admission does not meet all of your needs it was designed to be _complementary_
|
||||||
|
to other policy enforcement mechanisms, and can provide a useful fallback running alongside other
|
||||||
|
admission webhooks.
|
||||||
|
|
||||||
|
|
||||||
|
## 1. Review namespace permissions {#review-namespace-permissions}
|
||||||
|
|
||||||
|
Pod Security Admission is controlled by [labels on
|
||||||
|
namespaces](/docs/concepts/security/pod-security-admission/#pod-security-admission-labels-for-namespaces).
|
||||||
|
This means that anyone who can update (or patch or create) a namespace can also modify the Pod
|
||||||
|
Security level for that namespace, which could be used to bypass a more restrictive policy. Before
|
||||||
|
proceeding, ensure that only trusted, privileged users have these namespace permissions. It is not
|
||||||
|
recommended to grant these powerful permissions to users that shouldn't have elevated permissions,
|
||||||
|
but if you must you will need to use an
|
||||||
|
[admission webhook](/docs/reference/access-authn-authz/extensible-admission-controllers/)
|
||||||
|
to place additional restrictions on setting Pod Security labels on Namespace objects.
|
||||||
|
|
||||||
|
## 2. Simplify & standardize PodSecurityPolicies {#simplify-psps}
|
||||||
|
|
||||||
|
In this section, you will reduce mutating PodSecurityPolicies and remove options that are outside
|
||||||
|
the scope of the Pod Security Standards. You should make the changes recommended here to an offline
|
||||||
|
copy of the original PodSecurityPolicy being modified. The cloned PSP should have a different
|
||||||
|
name that is alphabetically before the original (for example, prepend a `0` to it). Do not create the
|
||||||
|
new policies in Kubernetes yet - that will be covered in the [Rollout the updated
|
||||||
|
policies](#psp-update-rollout) section below.
|
||||||
|
|
||||||
|
### 2.a. Eliminate purely mutating fields {#eliminate-mutating-fields}
|
||||||
|
|
||||||
|
If a PodSecurityPolicy is mutating pods, then you could end up with pods that don't meet the Pod
|
||||||
|
Security level requirements when you finally turn PodSecurityPolicy off. In order to avoid this, you
|
||||||
|
should eliminate all PSP mutation prior to switching over. Unfortunately PSP does not cleanly
|
||||||
|
separate mutating & validating fields, so this is not a straightforward migration.
|
||||||
|
|
||||||
|
You can start by eliminating the fields that are purely mutating, and don't have any bearing on the
|
||||||
|
validating policy. These fields (also listed in the
|
||||||
|
[Mapping PodSecurityPolicies to Pod Security Standards](/docs/reference/access-authn-authz/psp-to-pod-security-standards/)
|
||||||
|
reference) are:
|
||||||
|
|
||||||
|
- `.spec.defaultAllowPrivilegeEscalation`
|
||||||
|
- `.spec.runtimeClass.defaultRuntimeClassName`
|
||||||
|
- `.metadata.annotations['seccomp.security.alpha.kubernetes.io/defaultProfileName']`
|
||||||
|
- `.metadata.annotations['apparmor.security.beta.kubernetes.io/defaultProfileName']`
|
||||||
|
- `.spec.defaultAddCapabilities` - Although technically a mutating & validating field, these should
|
||||||
|
be merged into `.spec.allowedCapabilities` which performs the same validation without mutation.
|
||||||
|
|
||||||
|
{{< caution >}}
|
||||||
|
Removing these could result in workloads missing required configuration, and cause problems. See
|
||||||
|
[Rollout the updated policies](#psp-update-rollout) below for advice on how to roll these changes
|
||||||
|
out safely.
|
||||||
|
{{< /caution >}}
|
||||||
|
|
||||||
|
### 2.b. Eliminate options not covered by the Pod Security Standards {#eliminate-non-standard-options}
|
||||||
|
|
||||||
|
There are several fields in PodSecurityPolicy that are not covered by the Pod Security Standards. If
|
||||||
|
you must enforce these options, you will need to supplement Pod Security Admission with an
|
||||||
|
[admission webhook](/docs/reference/access-authn-authz/extensible-admission-controllers/),
|
||||||
|
which is outside the scope of this guide.
|
||||||
|
|
||||||
|
First, you can remove the purely validating fields that the Pod Security Standards do not cover.
|
||||||
|
These fields (also listed in the
|
||||||
|
[Mapping PodSecurityPolicies to Pod Security Standards](/docs/reference/access-authn-authz/psp-to-pod-security-standards/)
|
||||||
|
reference with "no opinion") are:
|
||||||
|
|
||||||
|
- `.spec.allowedHostPaths`
|
||||||
|
- `.spec.allowedFlexVolumes`
|
||||||
|
- `.spec.allowedCSIDrivers`
|
||||||
|
- `.spec.forbiddenSysctls`
|
||||||
|
- `.spec.runtimeClass`
|
||||||
|
|
||||||
|
You can also remove the following fields, that are related to POSIX / UNIX group controls.
|
||||||
|
|
||||||
|
{{< caution >}}
|
||||||
|
If any of these use the `MustRunAs` strategy they may be mutating! Removing these could result in
|
||||||
|
workloads not setting the required groups, and cause problems. See
|
||||||
|
[Rollout the updated policies](#psp-update-rollout) below for advice on how to roll these changes
|
||||||
|
out safely.
|
||||||
|
{{< /caution >}}
|
||||||
|
|
||||||
|
- `.spec.runAsGroup`
|
||||||
|
- `.spec.supplementalGroups`
|
||||||
|
- `.spec.fsGroup`
|
||||||
|
|
||||||
|
The remaining mutating fields are required to properly support the Pod Security Standards, and will
|
||||||
|
need to be handled on a case-by-case basis later:
|
||||||
|
|
||||||
|
- `.spec.requiredDropCapabilities` - Required to drop `ALL` for the Restricted profile.
|
||||||
|
- `.spec.seLinux` - (Only mutating with the `MustRunAs` rule) required to enforce the SELinux
|
||||||
|
requirements of the Baseline & Restricted profiles.
|
||||||
|
- `.spec.runAsUser` - (Non-mutating with the `RunAsAny` rule) required to enforce `RunAsNonRoot` for
|
||||||
|
the Restricted profile.
|
||||||
|
- `.spec.allowPrivilegeEscalation` - (Only mutating if set to `false`) required for the Restricted
|
||||||
|
profile.
|
||||||
|
|
||||||
|
### 2.c. Rollout the updated PSPs {#psp-update-rollout}
|
||||||
|
|
||||||
|
Next, you can rollout the updated policies to your cluster. You should proceed with caution, as
|
||||||
|
removing the mutating options may result in workloads missing required configuration.
|
||||||
|
|
||||||
|
For each updated PodSecurityPolicy:
|
||||||
|
|
||||||
|
1. Identify pods running under the original PSP. This can be done using the `kubernetes.io/psp`
|
||||||
|
annotation. For example, using kubectl:
|
||||||
|
```sh
|
||||||
|
PSP_NAME="original" # Set the name of the PSP you're checking for
|
||||||
|
kubectl get pods --all-namespaces -o jsonpath="{range .items[?(@.metadata.annotations.kubernetes\.io\/psp=='$PSP_NAME')]}{.metadata.namespace} {.metadata.name}{'\n'}{end}"
|
||||||
|
```
|
||||||
|
2. Compare these running pods against the original pod spec to determine whether PodSecurityPolicy
|
||||||
|
has modified the pod. For pods created by a [workload resource](/docs/concepts/workloads/controllers/)
|
||||||
|
you can compare the pod with the PodTemplate in the controller resource. If any changes are
|
||||||
|
identified, the original Pod or PodTemplate should be updated with the desired configuration.
|
||||||
|
The fields to review are:
|
||||||
|
- `.metadata.annotations['container.apparmor.security.beta.kubernetes.io/*']` (replace * with each container name)
|
||||||
|
- `.spec.runtimeClassName`
|
||||||
|
- `.spec.securityContext.fsGroup`
|
||||||
|
- `.spec.securityContext.seccompProfile`
|
||||||
|
- `.spec.securityContext.seLinuxOptions`
|
||||||
|
- `.spec.securityContext.supplementalGroups`
|
||||||
|
- On containers, under `.spec.containers[*]` and `.spec.initContainers[*]`:
|
||||||
|
- `.securityContext.allowPrivilegeEscalation`
|
||||||
|
- `.securityContext.capabilities.add`
|
||||||
|
- `.securityContext.capabilities.drop`
|
||||||
|
- `.securityContext.readOnlyRootFilesystem`
|
||||||
|
- `.securityContext.runAsGroup`
|
||||||
|
- `.securityContext.runAsNonRoot`
|
||||||
|
- `.securityContext.runAsUser`
|
||||||
|
- `.securityContext.seccompProfile`
|
||||||
|
- `.securityContext.seLinuxOptions`
|
||||||
|
3. Create the new PodSecurityPolicies. If any Roles or ClusterRoles are granting `use` on all PSPs
|
||||||
|
this could cause the new PSPs to be used instead of their mutating counter-parts.
|
||||||
|
4. Update your authorization to grant access to the new PSPs. In RBAC this means updating any Roles
|
||||||
|
or ClusterRoles that grant the `use` permision on the original PSP to also grant it to the
|
||||||
|
updated PSP.
|
||||||
|
5. Verify: after some soak time, rerun the command from step 1 to see if any pods are still using
|
||||||
|
the original PSPs. Note that pods need to be recreated after the new policies have been rolled
|
||||||
|
out before they can be fully verified.
|
||||||
|
6. (optional) Once you have verified that the original PSPs are no longer in use, you can delete
|
||||||
|
them.
|
||||||
|
|
||||||
|
## 3. Update Namespaces {#update-namespaces}
|
||||||
|
|
||||||
|
The following steps will need to be performed on every namespace in the cluster. Commands referenced
|
||||||
|
in these steps use the `$NAMESPACE` variable to refer to the namespace being updated.
|
||||||
|
|
||||||
|
### 3.a. Identify an appropriate Pod Security level {#identify-appropriate-level}
|
||||||
|
|
||||||
|
Start reviewing the [Pod Security Standards](/docs/concepts/security/pod-security-standards/) and
|
||||||
|
familiarizing yourself with the 3 different levels.
|
||||||
|
|
||||||
|
There are several ways to choose a Pod Security level for your namespace:
|
||||||
|
|
||||||
|
1. **By security requirements for the namespace** - If you are familiar with the expected access
|
||||||
|
level for the namespace, you can choose an appropriate level based on those requirements, similar
|
||||||
|
to how one might approach this on a new cluster.
|
||||||
|
2. **By existing PodSecurityPolicies** - Using the
|
||||||
|
[Mapping PodSecurityPolicies to Pod Security Standards](/docs/reference/access-authn-authz/psp-to-pod-security-standards/)
|
||||||
|
reference you can map each
|
||||||
|
PSP to a Pod Security Standard level. If your PSPs aren't based on the Pod Security Standards, you
|
||||||
|
may need to decide between choosing a level that is at least as permissive as the PSP, and a
|
||||||
|
level that is at least as restrictive. You can see which PSPs are in use for pods in a given
|
||||||
|
namespace with this command:
|
||||||
|
```sh
|
||||||
|
kubectl get pods -n $NAMESPACE -o jsonpath="{.items[*].metadata.annotations.kubernetes\.io\/psp}" | tr " " "\n" | sort -u
|
||||||
|
```
|
||||||
|
3. **By existing pods** - Using the strategies under [Verify the Pod Security level](#verify-pss-level),
|
||||||
|
you can test out both the Baseline and Restricted levels to see
|
||||||
|
whether they are sufficiently permissive for existing workloads, and chose the least-privileged
|
||||||
|
valid level.
|
||||||
|
|
||||||
|
{{< caution >}}
|
||||||
|
Options 2 & 3 above are based on _existing_ pods, and may miss workloads that aren't currently
|
||||||
|
running, such as CronJobs, scale-to-zero workloads, or other workloads that haven't rolled out.
|
||||||
|
{{< /caution >}}
|
||||||
|
|
||||||
|
### 3.b. Verify the Pod Security level {#verify-pss-level}
|
||||||
|
|
||||||
|
Once you have selected a Pod Security level for the namespace (or if you're trying several), it's a
|
||||||
|
good idea to test it out first (you can skip this step if using the Privileged level). Pod Security
|
||||||
|
includes several tools to help test and safely roll out profiles.
|
||||||
|
|
||||||
|
First, you can dry-run the policy, which will evaluate pods currently running in the namespace
|
||||||
|
against the applied policy, without making the new policy take effect:
|
||||||
|
```sh
|
||||||
|
# $LEVEL is the level to dry-run, either "baseline" or "restricted".
|
||||||
|
kubectl label --dry-run=server --overwrite ns $NAMESPACE pod-security.kubernetes.io/enforce=$LEVEL
|
||||||
|
```
|
||||||
|
This command will return a warning for any _existing_ pods that are not valid under the proposed
|
||||||
|
level.
|
||||||
|
|
||||||
|
The second option is better for catching workloads that are not currently running: audit mode. When
|
||||||
|
running under audit-mode (as opposed to enforcing), pods that violate the policy level are recorded
|
||||||
|
in the audit logs, which can be reviewed later after some soak time, but are not forbidden. Warning
|
||||||
|
mode works similarly, but returns the warning to the user immediately. You can set the audit level
|
||||||
|
on a namespace with this command:
|
||||||
|
```sh
|
||||||
|
kubectl label --overwrite ns $NAMESPACE pod-security.kubernetes.io/audit=$LEVEL
|
||||||
|
```
|
||||||
|
|
||||||
|
If either of these approaches yield unexpected violations, you will need to either update the
|
||||||
|
violating workloads to meet the policy requirements, or relax the namespace Pod Security level.
|
||||||
|
|
||||||
|
### 3.c. Enforce the Pod Security level {#enforce-pod-security-level}
|
||||||
|
|
||||||
|
When you are satisfied that the chosen level can safely be enforced on the namespace, you can update
|
||||||
|
the namespace to enforce the desired level:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
kubectl label --overwrite ns $NAMESPACE pod-security.kubernetes.io/enforce=$LEVEL
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.d. Bypass PodSecurityPolicy {#bypass-psp}
|
||||||
|
|
||||||
|
Finally, you can effectively bypass PodSecurityPolicy at the namespace level by binding the fully
|
||||||
|
{{< example file="policy/privileged-psp.yaml" >}}privileged PSP{{< /example >}} to all service
|
||||||
|
accounts in the namespace.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# The following cluster-scoped commands are only needed once.
|
||||||
|
kubectl apply -f privileged-psp.yaml
|
||||||
|
kubectl create clusterrole privileged-psp --verb use --resource podsecuritypolicies.policy --resource-name privileged
|
||||||
|
|
||||||
|
# Per-namespace disable
|
||||||
|
kubectl create -n $NAMESPACE rolebinding disable-psp --clusterrole privileged-psp --group system:serviceaccounts:$NAMESPACE
|
||||||
|
```
|
||||||
|
|
||||||
|
Since the privileged PSP is non-mutating, and the PSP admission controller always
|
||||||
|
prefers non-mutating PSPs, this will ensure that pods in this namespace are no longer being modified
|
||||||
|
or restricted by PodSecurityPolicy.
|
||||||
|
|
||||||
|
The advantage to disabling PodSecurityPolicy on a per-namespace basis like this is if a problem
|
||||||
|
arises you can easily roll the change back by deleting the RoleBinding. Just make sure the
|
||||||
|
pre-existing PodSecurityPolicies are still in place!
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Undo PodSecurityPolicy disablement.
|
||||||
|
kubectl delete -n $NAMESPACE rolebinding disable-psp
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Review namespace creation processes {#review-namespace-creation-process}
|
||||||
|
|
||||||
|
Now that existing namespaces have been updated to enforce Pod Security Admission, you should ensure
|
||||||
|
that your processes and/or policies for creating new namespaces are updated to ensure that an
|
||||||
|
appropriate Pod Security profile is applied to new namespaces.
|
||||||
|
|
||||||
|
You can also statically configure the Pod Security admission controller to set a default enforce,
|
||||||
|
audit, and/or warn level for unlabeled namespaces. See
|
||||||
|
[Configure the Admission Controller](docs/tasks/configure-pod-container/enforce-standards-admission-controller/#configure-the-admission-controller)
|
||||||
|
for more information.
|
||||||
|
|
||||||
|
## 5. Disable PodSecurityPolicy {#disable-psp}
|
||||||
|
|
||||||
|
Finally, you're ready to disable PodSecurityPolicy. To do so, you will need to modify the admission
|
||||||
|
configuration of the API server:
|
||||||
|
[How do I turn off an admission controller?](/docs/reference/access-authn-authz/admission-controllers/#how-do-i-turn-off-an-admission-controller).
|
||||||
|
|
||||||
|
To verify that the PodSecurityPolicy admission controller is no longer enabled, you can manually run
|
||||||
|
a test by impersonating a user without access to any PodSecurityPolicies (see the
|
||||||
|
[PodSecurityPolicy example](/docs/concepts/policy/pod-security-policy/#example)), or by verifying in
|
||||||
|
the API server logs. At startup, the API server outputs log lines listing the loaded admission
|
||||||
|
controller plugins:
|
||||||
|
|
||||||
|
```
|
||||||
|
I0218 00:59:44.903329 13 plugins.go:158] Loaded 16 mutating admission controller(s) successfully in the following order: NamespaceLifecycle,LimitRanger,ServiceAccount,NodeRestriction,TaintNodesByCondition,Priority,DefaultTolerationSeconds,ExtendedResourceToleration,PersistentVolumeLabel,DefaultStorageClass,StorageObjectInUseProtection,RuntimeClass,DefaultIngressClass,MutatingAdmissionWebhook.
|
||||||
|
I0218 00:59:44.903350 13 plugins.go:161] Loaded 14 validating admission controller(s) successfully in the following order: LimitRanger,ServiceAccount,PodSecurity,Priority,PersistentVolumeClaimResize,RuntimeClass,CertificateApproval,CertificateSigning,CertificateSubjectRestriction,DenyServiceExternalIPs,ValidatingAdmissionWebhook,ResourceQuota.
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see `PodSecurity` (in the validating admission controllers), and neither list should
|
||||||
|
contain `PodSecurityPolicy`.
|
||||||
|
|
||||||
|
Once you are certain the PSP admission controller is disabled (and after sufficient soak time to be
|
||||||
|
confident you won't need to roll back), you are free to delete your PodSecurityPolicies and any
|
||||||
|
associated Roles, ClusterRoles, RoleBindings and ClusterRoleBindings (just make sure they don't
|
||||||
|
grant any other unrelated permissions).
|
||||||
|
|||||||
Reference in New Issue
Block a user