mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-08 11:38:15 +00:00
Merge pull request #118760 from saschagrunert/user-namespaces-pss
KEP-127: Update PSS based on feature gate
This commit is contained in:
commit
1c8f88d4b6
@ -886,6 +886,18 @@ const (
|
||||
// ImageMaximumGCAge enables the Kubelet configuration field of the same name, allowing an admin
|
||||
// to specify the age after which an image will be garbage collected.
|
||||
ImageMaximumGCAge featuregate.Feature = "ImageMaximumGCAge"
|
||||
|
||||
// owner: @saschagrunert
|
||||
// alpha: v1.28
|
||||
//
|
||||
// Enables user namespace support for Pod Security Standards. Enabling this
|
||||
// feature will modify all Pod Security Standard rules to allow setting:
|
||||
// spec[.*].securityContext.[runAsNonRoot,runAsUser]
|
||||
// This feature gate should only be enabled if all nodes in the cluster
|
||||
// support the user namespace feature and have it enabled. The feature gate
|
||||
// will not graduate or be enabled by default in future Kubernetes
|
||||
// releases.
|
||||
UserNamespacesPodSecurityStandards featuregate.Feature = "UserNamespacesPodSecurityStandards"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -1125,6 +1137,8 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
||||
|
||||
ImageMaximumGCAge: {Default: false, PreRelease: featuregate.Alpha},
|
||||
|
||||
UserNamespacesPodSecurityStandards: {Default: false, PreRelease: featuregate.Alpha},
|
||||
|
||||
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
|
||||
// unintentionally on either side:
|
||||
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
_ "k8s.io/kubernetes/pkg/apis/apps/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/batch/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
|
||||
admissionv1 "k8s.io/api/admission/v1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
@ -151,6 +152,7 @@ func (p *Plugin) updateDelegate() {
|
||||
|
||||
func (c *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) {
|
||||
c.inspectedFeatureGates = true
|
||||
policy.RelaxPolicyForUserNamespacePods(featureGates.Enabled(features.UserNamespacesPodSecurityStandards))
|
||||
}
|
||||
|
||||
// ValidateInitialization ensures all required options are set
|
||||
|
@ -59,6 +59,11 @@ func CheckRunAsNonRoot() Check {
|
||||
}
|
||||
|
||||
func runAsNonRoot_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
// See KEP-127: https://github.com/kubernetes/enhancements/blob/308ba8d/keps/sig-node/127-user-namespaces/README.md?plain=1#L411-L447
|
||||
if relaxPolicyForUserNamespacePod(podSpec) {
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
||||
|
||||
// things that explicitly set runAsNonRoot=false
|
||||
var badSetters []string
|
||||
|
||||
|
@ -25,10 +25,12 @@ import (
|
||||
|
||||
func TestRunAsNonRoot(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pod *corev1.Pod
|
||||
expectReason string
|
||||
expectDetail string
|
||||
name string
|
||||
pod *corev1.Pod
|
||||
expectReason string
|
||||
expectDetail string
|
||||
allowed bool
|
||||
enableUserNamespacesPodSecurityStandards bool
|
||||
}{
|
||||
{
|
||||
name: "no explicit runAsNonRoot",
|
||||
@ -80,12 +82,36 @@ func TestRunAsNonRoot(t *testing.T) {
|
||||
expectReason: `runAsNonRoot != true`,
|
||||
expectDetail: `pod or containers "a", "b" must set securityContext.runAsNonRoot=true`,
|
||||
},
|
||||
{
|
||||
name: "UserNamespacesPodSecurityStandards enabled without HostUsers",
|
||||
pod: &corev1.Pod{Spec: corev1.PodSpec{
|
||||
HostUsers: utilpointer.Bool(false),
|
||||
}},
|
||||
allowed: true,
|
||||
enableUserNamespacesPodSecurityStandards: true,
|
||||
},
|
||||
{
|
||||
name: "UserNamespacesPodSecurityStandards enabled with HostUsers",
|
||||
pod: &corev1.Pod{Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{Name: "a"},
|
||||
},
|
||||
HostUsers: utilpointer.Bool(true),
|
||||
}},
|
||||
expectReason: `runAsNonRoot != true`,
|
||||
expectDetail: `pod or container "a" must set securityContext.runAsNonRoot=true`,
|
||||
allowed: false,
|
||||
enableUserNamespacesPodSecurityStandards: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if tc.enableUserNamespacesPodSecurityStandards {
|
||||
RelaxPolicyForUserNamespacePods(true)
|
||||
}
|
||||
result := runAsNonRoot_1_0(&tc.pod.ObjectMeta, &tc.pod.Spec)
|
||||
if result.Allowed {
|
||||
if result.Allowed && !tc.allowed {
|
||||
t.Fatal("expected disallowed")
|
||||
}
|
||||
if e, a := tc.expectReason, result.ForbiddenReason; e != a {
|
||||
|
@ -60,6 +60,11 @@ func CheckRunAsUser() Check {
|
||||
}
|
||||
|
||||
func runAsUser_1_23(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
// See KEP-127: https://github.com/kubernetes/enhancements/blob/308ba8d/keps/sig-node/127-user-namespaces/README.md?plain=1#L411-L447
|
||||
if relaxPolicyForUserNamespacePod(podSpec) {
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
||||
|
||||
// things that explicitly set runAsUser=0
|
||||
var badSetters []string
|
||||
|
||||
|
@ -25,11 +25,12 @@ import (
|
||||
|
||||
func TestRunAsUser(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pod *corev1.Pod
|
||||
expectAllow bool
|
||||
expectReason string
|
||||
expectDetail string
|
||||
name string
|
||||
pod *corev1.Pod
|
||||
expectAllow bool
|
||||
expectReason string
|
||||
expectDetail string
|
||||
enableUserNamespacesPodSecurityStandards bool
|
||||
}{
|
||||
{
|
||||
name: "pod runAsUser=0",
|
||||
@ -90,10 +91,35 @@ func TestRunAsUser(t *testing.T) {
|
||||
}},
|
||||
expectAllow: true,
|
||||
},
|
||||
{
|
||||
name: "UserNamespacesPodSecurityStandards enabled without HostUsers",
|
||||
pod: &corev1.Pod{Spec: corev1.PodSpec{
|
||||
HostUsers: utilpointer.Bool(false),
|
||||
}},
|
||||
expectAllow: true,
|
||||
enableUserNamespacesPodSecurityStandards: true,
|
||||
},
|
||||
{
|
||||
name: "UserNamespacesPodSecurityStandards enabled with HostUsers",
|
||||
pod: &corev1.Pod{Spec: corev1.PodSpec{
|
||||
SecurityContext: &corev1.PodSecurityContext{RunAsUser: utilpointer.Int64(0)},
|
||||
Containers: []corev1.Container{
|
||||
{Name: "a", SecurityContext: nil},
|
||||
},
|
||||
HostUsers: utilpointer.Bool(true),
|
||||
}},
|
||||
expectAllow: false,
|
||||
expectReason: `runAsUser=0`,
|
||||
expectDetail: `pod must not set runAsUser=0`,
|
||||
enableUserNamespacesPodSecurityStandards: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if tc.enableUserNamespacesPodSecurityStandards {
|
||||
RelaxPolicyForUserNamespacePods(true)
|
||||
}
|
||||
result := runAsUser_1_23(&tc.pod.ObjectMeta, &tc.pod.Spec)
|
||||
if tc.expectAllow {
|
||||
if !result.Allowed {
|
||||
|
@ -16,7 +16,12 @@ limitations under the License.
|
||||
|
||||
package policy
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func joinQuote(items []string) string {
|
||||
if len(items) == 0 {
|
||||
@ -31,3 +36,21 @@ func pluralize(singular, plural string, count int) string {
|
||||
}
|
||||
return plural
|
||||
}
|
||||
|
||||
var relaxPolicyForUserNamespacePods = &atomic.Bool{}
|
||||
|
||||
// RelaxPolicyForUserNamespacePods allows opting into relaxing runAsUser /
|
||||
// runAsNonRoot restricted policies for user namespace pods, before the
|
||||
// usernamespace feature has reached GA and propagated to the oldest supported
|
||||
// nodes.
|
||||
// This should only be opted into in clusters where the administrator ensures
|
||||
// all nodes in the cluster enable the user namespace feature.
|
||||
func RelaxPolicyForUserNamespacePods(relax bool) {
|
||||
relaxPolicyForUserNamespacePods.Store(relax)
|
||||
}
|
||||
|
||||
// relaxPolicyForUserNamespacePod returns true if a policy should be relaxed
|
||||
// because of enabled user namespaces in the provided pod spec.
|
||||
func relaxPolicyForUserNamespacePod(podSpec *corev1.PodSpec) bool {
|
||||
return relaxPolicyForUserNamespacePods.Load() && podSpec != nil && podSpec.HostUsers != nil && !*podSpec.HostUsers
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user