Skip to content

Commit 658bb17

Browse files
authored
Merge pull request #36021 from mtardy/blogpost/psp-historical-context
Add blog post on PodSecurityPolicy historical context
2 parents 72b254d + fce0c4b commit 658bb17

File tree

3 files changed

+2150
-0
lines changed

3 files changed

+2150
-0
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
---
2+
layout: blog
3+
title: "PodSecurityPolicy: The Historical Context"
4+
date: 2022-08-23T15:00:00-0800
5+
slug: podsecuritypolicy-the-historical-context
6+
---
7+
8+
**Author:** Mahé Tardy (Quarkslab)
9+
10+
The PodSecurityPolicy (PSP) admission controller has been removed, as of
11+
Kubernetes v1.25. Its deprecation was announced and detailed in the blog post
12+
[PodSecurityPolicy Deprecation: Past, Present, and Future](/blog/2021/04/06/podsecuritypolicy-deprecation-past-present-and-future/),
13+
published for the Kubernetes v1.21 release.
14+
15+
This article aims to provide historical context on the birth and evolution of
16+
PSP, explain why the feature never made it to stable, and show why it was
17+
removed and replaced by Pod Security admission control.
18+
19+
PodSecurityPolicy, like other specialized admission control plugins, provided
20+
fine-grained permissions on specific fields concerning the pod security settings
21+
as a built-in policy API. It acknowledged that cluster administrators and
22+
cluster users are usually not the same people, and that creating workloads in
23+
the form of a Pod or any resource that will create a Pod should not equal being
24+
"root on the cluster". It could also encourage best practices by configuring
25+
more secure defaults through mutation and decoupling low-level Linux security
26+
decisions from the deployment process.
27+
28+
## The birth of PodSecurityPolicy
29+
30+
PodSecurityPolicy originated from OpenShift's SecurityContextConstraints
31+
(SCC) that were in the very first release of the Red Hat OpenShift Container Platform,
32+
even before Kubernetes 1.0. PSP was a stripped-down version of the SCC.
33+
34+
The origin of the creation of PodSecurityPolicy is difficult to track, notably
35+
because it was mainly added before Kubernetes Enhancements Proposal (KEP)
36+
process, when design proposals were still a thing. Indeed, the archive of the final
37+
[design proposal](https://github.com/kubernetes/design-proposals-archive/blob/main/auth/pod-security-policy.md)
38+
is still available. Nevertheless, a [KEP issue number five](https://github.com/kubernetes/enhancements/issues/5)
39+
was created after the first pull requests were merged.
40+
41+
Before adding the first piece of code that created PSP, two main pull
42+
requests were merged into Kubernetes, a [`SecurityContext` subresource](https://github.com/kubernetes/kubernetes/pull/7343)
43+
that defined new fields on pods' containers, and the first iteration of the [ServiceAccount](https://github.com/kubernetes/kubernetes/pull/7101)
44+
API.
45+
46+
Kubernetes 1.0 was released on 10 July 2015 without any mechanism to restrict the
47+
security context and sensitive options of workloads, other than an alpha-quality
48+
SecurityContextDeny admission plugin (then known as `scdeny`).
49+
The [SecurityContextDeny plugin](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#securitycontextdeny)
50+
is still in Kubernetes today (as an alpha feature) and creates an admission controller that
51+
prevents the usage of some fields in the security context.
52+
53+
The roots of the PodSecurityPolicy were added with
54+
[the very first pull request on security policy](https://github.com/kubernetes/kubernetes/pull/7893),
55+
which added the design proposal with the new PSP object, based on the SCC (Security Context Constraints). It
56+
was a long discussion of nine months, with back and forth from OpenShift's SCC,
57+
many rebases, and the rename to PodSecurityPolicy that finally made it to
58+
upstream Kubernetes in February 2016. Now that the PSP object
59+
had been created, the next step was to add an admission controller that could enforce
60+
these policies. The first step was to add the admission
61+
[without taking into account the users or groups](https://github.com/kubernetes/kubernetes/pull/7893#issuecomment-180410539).
62+
A specific [issue to bring PodSecurityPolicy to a usable state](https://github.com/kubernetes/kubernetes/issues/23217)
63+
was added to keep track of the progress and a first version of the admission
64+
controller was merged in [pull request named PSP admission](https://github.com/kubernetes/kubernetes/pull/24600)
65+
in May 2016. Then around two months later, Kubernetes 1.3 was released.
66+
67+
Here is a timeline that recaps the main pull requests of the birth of the
68+
PodSecurityPolicy and its admission controller with 1.0 and 1.3 releases as
69+
reference points.
70+
71+
{{< figure src="./timeline.svg" alt="Timeline of the PodSecurityPolicy creation pull requests" >}}
72+
73+
After that, the PSP admission controller was enhanced by adding what was initially
74+
left aside. [The authorization mechanism](https://github.com/kubernetes/kubernetes/pull/33080),
75+
merged in early November 2016 allowed administrators to use multiple policies
76+
in a cluster to grant different levels of access for different types of users.
77+
Later, a [pull request](https://github.com/kubernetes/kubernetes/pull/52849)
78+
merged in October 2017 fixed [a design issue](https://github.com/kubernetes/kubernetes/issues/36184)
79+
on ordering PodSecurityPolicies between mutating and alphabetical order, and continued to
80+
build the PSP admission as we know it. After that, many improvements and fixes
81+
followed to build the PodSecurityPolicy feature of recent Kubernetes releases.
82+
83+
## The rise of Pod Security Admission
84+
85+
Despite the crucial issue it was trying to solve, PodSecurityPolicy presented
86+
some major flaws:
87+
88+
- **Flawed authorization model** - users can create a pod if they have the
89+
**use** verb on the PSP that allows that pod or the pod's service account has
90+
the **use** permission on the allowing PSP.
91+
- **Difficult to roll out** - PSP fail-closed. That is, in the absence of a policy,
92+
all pods are denied. It mostly means that it cannot be enabled by default and
93+
that users have to add PSPs for all workloads before enabling the feature,
94+
thus providing no audit mode to discover which pods would not be allowed by
95+
the new policy. The opt-in model also leads to insufficient test coverage and
96+
frequent breakage due to cross-feature incompatibility. And unlike RBAC,
97+
there was no strong culture of shipping PSP manifests with projects.
98+
- **Inconsistent unbounded API** - the API has grown with lots of
99+
inconsistencies notably because of many requests for niche use cases: e.g.
100+
labels, scheduling, fine-grained volume controls, etc. It has poor
101+
composability with a weak prioritization model, leading to unexpected
102+
mutation priority. It made it really difficult to combine PSP with other
103+
third-party admission controllers.
104+
- **Require security knowledge** - effective usage still requires an
105+
understanding of Linux security primitives. e.g. MustRunAsNonRoot +
106+
AllowPrivilegeEscalation.
107+
108+
The experience with PodSecurityPolicy concluded that most users care for two or three
109+
policies, which led to the creation of the [Pod Security Standards](/docs/concepts/security/pod-security-standards/),
110+
that define three policies:
111+
- **Privileged** - unrestricted policy.
112+
- **Baseline** - minimally restrictive policy, allowing the default pod
113+
configuration.
114+
- **Restricted** - security best practice policy.
115+
116+
The replacement for PSP, the new [Pod Security Admission](/docs/concepts/security/pod-security-admission/)
117+
is an in-tree, stable for Kubernetes v1.25, admission plugin to enforce these
118+
standards at the namespace level. It makes it easier to enforce basic pod
119+
security without deep security knowledge. For more sophisticated use cases, you
120+
might need a third-party solution that can be easily combined with Pod Security
121+
Admission.
122+
123+
## What's next
124+
125+
For further details on the SIG Auth processes, covering PodSecurityPolicy removal and
126+
creation of Pod Security admission, the
127+
[SIG auth update at KubeCon NA 2019](https://www.youtube.com/watch?v=SFtHRmPuhEw)
128+
and the [PodSecurityPolicy Replacement: Past, Present, and Future](https://www.youtube.com/watch?v=HsRRmlTJpls)
129+
presentation at KubeCon NA 2021 records are available.
130+
131+
Particularly on the PSP removal, the
132+
[PodSecurityPolicy Deprecation: Past, Present, and Future](/blog/2021/04/06/podsecuritypolicy-deprecation-past-present-and-future/)
133+
blog post is still accurate.
134+
135+
And for the new Pod Security admission,
136+
[documentation is available](/docs/concepts/security/pod-security-admission/).
137+
In addition, the blog post
138+
[Kubernetes 1.23: Pod Security Graduates to Beta](/blog/2021/12/09/pod-security-admission-beta/)
139+
along with the KubeCon EU 2022 presentation
140+
[The Hitchhiker's Guide to Pod Security](https://www.youtube.com/watch?v=gcz5VsvOYmI)
141+
give great hands-on tutorials to learn.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<mxfile host="app.diagrams.net" modified="2022-08-20T10:59:36.610Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36" etag="2BRdQVi9ypjLT9as_Jj5" version="17.4.0" type="device"><diagram name="k8s color" id="EOYQ7ahtYo4FzmBLCOt_">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</diagram></mxfile>

0 commit comments

Comments
 (0)