mirror of https://github.com/k3s-io/k3s
ProcMount: add api options and feature gate
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>pull/8/head
parent
6b7c39a4f8
commit
30dcca6233
|
@ -262,6 +262,8 @@ func DropDisabledAlphaFields(podSpec *api.PodSpec) {
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.RuntimeClass) && podSpec.RuntimeClassName != nil {
|
if !utilfeature.DefaultFeatureGate.Enabled(features.RuntimeClass) && podSpec.RuntimeClassName != nil {
|
||||||
podSpec.RuntimeClassName = nil
|
podSpec.RuntimeClassName = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DropDisabledProcMountField(podSpec)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DropDisabledRunAsGroupField removes disabled fields from PodSpec related
|
// DropDisabledRunAsGroupField removes disabled fields from PodSpec related
|
||||||
|
@ -284,6 +286,24 @@ func DropDisabledRunAsGroupField(podSpec *api.PodSpec) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DropDisabledProcMountField removes disabled fields from PodSpec related
|
||||||
|
// to ProcMount
|
||||||
|
func DropDisabledProcMountField(podSpec *api.PodSpec) {
|
||||||
|
if !utilfeature.DefaultFeatureGate.Enabled(features.ProcMountType) {
|
||||||
|
defProcMount := api.DefaultProcMount
|
||||||
|
for i := range podSpec.Containers {
|
||||||
|
if podSpec.Containers[i].SecurityContext != nil {
|
||||||
|
podSpec.Containers[i].SecurityContext.ProcMount = &defProcMount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range podSpec.InitContainers {
|
||||||
|
if podSpec.InitContainers[i].SecurityContext != nil {
|
||||||
|
podSpec.InitContainers[i].SecurityContext.ProcMount = &defProcMount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DropDisabledVolumeMountsAlphaFields removes disabled fields from []VolumeMount.
|
// DropDisabledVolumeMountsAlphaFields removes disabled fields from []VolumeMount.
|
||||||
// This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a VolumeMount
|
// This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a VolumeMount
|
||||||
func DropDisabledVolumeMountsAlphaFields(volumeMounts []api.VolumeMount) {
|
func DropDisabledVolumeMountsAlphaFields(volumeMounts []api.VolumeMount) {
|
||||||
|
|
|
@ -4616,8 +4616,27 @@ type SecurityContext struct {
|
||||||
// the no_new_privs flag will be set on the container process.
|
// the no_new_privs flag will be set on the container process.
|
||||||
// +optional
|
// +optional
|
||||||
AllowPrivilegeEscalation *bool
|
AllowPrivilegeEscalation *bool
|
||||||
|
// ProcMount denotes the type of proc mount to use for the containers.
|
||||||
|
// The default is DefaultProcMount which uses the container runtime defaults for
|
||||||
|
// readonly paths and masked paths.
|
||||||
|
// +optional
|
||||||
|
ProcMount *ProcMountType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ProcMountType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultProcMount uses the container runtime defaults for readonly and masked
|
||||||
|
// paths for /proc. Most container runtimes mask certain paths in /proc to avoid
|
||||||
|
// accidental security exposure of special devices or information.
|
||||||
|
DefaultProcMount ProcMountType = "Default"
|
||||||
|
|
||||||
|
// UnmaskedProcMount bypasses the default masking behavior of the container
|
||||||
|
// runtime and ensures the newly created /proc the container stays in tact with
|
||||||
|
// no modifications.
|
||||||
|
UnmaskedProcMount ProcMountType = "Unmasked"
|
||||||
|
)
|
||||||
|
|
||||||
// SELinuxOptions are the labels to be applied to the container.
|
// SELinuxOptions are the labels to be applied to the container.
|
||||||
type SELinuxOptions struct {
|
type SELinuxOptions struct {
|
||||||
// SELinux user label
|
// SELinux user label
|
||||||
|
|
|
@ -380,6 +380,11 @@ func Convert_core_SecurityContext_To_v1_SecurityContext(in *core.SecurityContext
|
||||||
out.RunAsNonRoot = in.RunAsNonRoot
|
out.RunAsNonRoot = in.RunAsNonRoot
|
||||||
out.ReadOnlyRootFilesystem = in.ReadOnlyRootFilesystem
|
out.ReadOnlyRootFilesystem = in.ReadOnlyRootFilesystem
|
||||||
out.AllowPrivilegeEscalation = in.AllowPrivilegeEscalation
|
out.AllowPrivilegeEscalation = in.AllowPrivilegeEscalation
|
||||||
|
if in.ProcMount != nil {
|
||||||
|
pm := string(*in.ProcMount)
|
||||||
|
pmt := v1.ProcMountType(pm)
|
||||||
|
out.ProcMount = &pmt
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,7 @@ func SetDefaults_Container(obj *v1.Container) {
|
||||||
obj.TerminationMessagePolicy = v1.TerminationMessageReadFile
|
obj.TerminationMessagePolicy = v1.TerminationMessageReadFile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetDefaults_Service(obj *v1.Service) {
|
func SetDefaults_Service(obj *v1.Service) {
|
||||||
if obj.Spec.SessionAffinity == "" {
|
if obj.Spec.SessionAffinity == "" {
|
||||||
obj.Spec.SessionAffinity = v1.ServiceAffinityNone
|
obj.Spec.SessionAffinity = v1.ServiceAffinityNone
|
||||||
|
|
|
@ -6724,6 +6724,7 @@ func autoConvert_v1_SecurityContext_To_core_SecurityContext(in *v1.SecurityConte
|
||||||
out.RunAsNonRoot = (*bool)(unsafe.Pointer(in.RunAsNonRoot))
|
out.RunAsNonRoot = (*bool)(unsafe.Pointer(in.RunAsNonRoot))
|
||||||
out.ReadOnlyRootFilesystem = (*bool)(unsafe.Pointer(in.ReadOnlyRootFilesystem))
|
out.ReadOnlyRootFilesystem = (*bool)(unsafe.Pointer(in.ReadOnlyRootFilesystem))
|
||||||
out.AllowPrivilegeEscalation = (*bool)(unsafe.Pointer(in.AllowPrivilegeEscalation))
|
out.AllowPrivilegeEscalation = (*bool)(unsafe.Pointer(in.AllowPrivilegeEscalation))
|
||||||
|
out.ProcMount = (*core.ProcMountType)(unsafe.Pointer(in.ProcMount))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6741,6 +6742,7 @@ func autoConvert_core_SecurityContext_To_v1_SecurityContext(in *core.SecurityCon
|
||||||
out.RunAsNonRoot = (*bool)(unsafe.Pointer(in.RunAsNonRoot))
|
out.RunAsNonRoot = (*bool)(unsafe.Pointer(in.RunAsNonRoot))
|
||||||
out.ReadOnlyRootFilesystem = (*bool)(unsafe.Pointer(in.ReadOnlyRootFilesystem))
|
out.ReadOnlyRootFilesystem = (*bool)(unsafe.Pointer(in.ReadOnlyRootFilesystem))
|
||||||
out.AllowPrivilegeEscalation = (*bool)(unsafe.Pointer(in.AllowPrivilegeEscalation))
|
out.AllowPrivilegeEscalation = (*bool)(unsafe.Pointer(in.AllowPrivilegeEscalation))
|
||||||
|
out.ProcMount = (*v1.ProcMountType)(unsafe.Pointer(in.ProcMount))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -263,6 +263,9 @@ func SetObjectDefaults_Pod(in *v1.Pod) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if a.SecurityContext != nil {
|
||||||
|
SetDefaults_SecurityContext(a.SecurityContext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for i := range in.Spec.Containers {
|
for i := range in.Spec.Containers {
|
||||||
a := &in.Spec.Containers[i]
|
a := &in.Spec.Containers[i]
|
||||||
|
@ -305,6 +308,9 @@ func SetObjectDefaults_Pod(in *v1.Pod) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if a.SecurityContext != nil {
|
||||||
|
SetDefaults_SecurityContext(a.SecurityContext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,6 +415,9 @@ func SetObjectDefaults_PodTemplate(in *v1.PodTemplate) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if a.SecurityContext != nil {
|
||||||
|
SetDefaults_SecurityContext(a.SecurityContext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for i := range in.Template.Spec.Containers {
|
for i := range in.Template.Spec.Containers {
|
||||||
a := &in.Template.Spec.Containers[i]
|
a := &in.Template.Spec.Containers[i]
|
||||||
|
@ -451,6 +460,9 @@ func SetObjectDefaults_PodTemplate(in *v1.PodTemplate) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if a.SecurityContext != nil {
|
||||||
|
SetDefaults_SecurityContext(a.SecurityContext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,6 +569,9 @@ func SetObjectDefaults_ReplicationController(in *v1.ReplicationController) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if a.SecurityContext != nil {
|
||||||
|
SetDefaults_SecurityContext(a.SecurityContext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for i := range in.Spec.Template.Spec.Containers {
|
for i := range in.Spec.Template.Spec.Containers {
|
||||||
a := &in.Spec.Template.Spec.Containers[i]
|
a := &in.Spec.Template.Spec.Containers[i]
|
||||||
|
@ -599,6 +614,9 @@ func SetObjectDefaults_ReplicationController(in *v1.ReplicationController) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if a.SecurityContext != nil {
|
||||||
|
SetDefaults_SecurityContext(a.SecurityContext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,6 +228,10 @@ type PodSecurityPolicySpec struct {
|
||||||
// e.g. "foo.*" forbids "foo.bar", "foo.baz", etc.
|
// e.g. "foo.*" forbids "foo.bar", "foo.baz", etc.
|
||||||
// +optional
|
// +optional
|
||||||
ForbiddenSysctls []string
|
ForbiddenSysctls []string
|
||||||
|
// AllowedProcMountTypes is a whitelist of allowed ProcMountTypes.
|
||||||
|
// Empty or nil indicates that only the DefaultProcMountType may be used.
|
||||||
|
// +optional
|
||||||
|
AllowedProcMountTypes []api.ProcMountType
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllowedHostPath defines the host volume conditions that will be enabled by a policy
|
// AllowedHostPath defines the host volume conditions that will be enabled by a policy
|
||||||
|
|
|
@ -363,6 +363,12 @@ const (
|
||||||
//
|
//
|
||||||
// Enable volume snapshot data source support.
|
// Enable volume snapshot data source support.
|
||||||
VolumeSnapshotDataSource utilfeature.Feature = "VolumeSnapshotDataSource"
|
VolumeSnapshotDataSource utilfeature.Feature = "VolumeSnapshotDataSource"
|
||||||
|
|
||||||
|
// owner: @jessfraz
|
||||||
|
// alpha: v1.12
|
||||||
|
//
|
||||||
|
// Enables control over ProcMountType for containers.
|
||||||
|
ProcMountType utilfeature.Feature = "ProcMountType"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -424,6 +430,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
|
||||||
NodeLease: {Default: false, PreRelease: utilfeature.Alpha},
|
NodeLease: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
SCTPSupport: {Default: false, PreRelease: utilfeature.Alpha},
|
SCTPSupport: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
VolumeSnapshotDataSource: {Default: false, PreRelease: utilfeature.Alpha},
|
VolumeSnapshotDataSource: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
|
ProcMountType: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
|
|
||||||
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
|
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
|
||||||
// unintentionally on either side:
|
// unintentionally on either side:
|
||||||
|
|
|
@ -849,6 +849,8 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
|
||||||
klet.nodeLeaseController = nodelease.NewController(klet.clock, klet.heartbeatClient, string(klet.nodeName), kubeCfg.NodeLeaseDurationSeconds, klet.onRepeatedHeartbeatFailure)
|
klet.nodeLeaseController = nodelease.NewController(klet.clock, klet.heartbeatClient, string(klet.nodeName), kubeCfg.NodeLeaseDurationSeconds, klet.onRepeatedHeartbeatFailure)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
klet.softAdmitHandlers.AddPodAdmitHandler(lifecycle.NewProcMountAdmitHandler(klet.containerRuntime))
|
||||||
|
|
||||||
// Finally, put the most recent version of the config on the Kubelet, so
|
// Finally, put the most recent version of the config on the Kubelet, so
|
||||||
// people can see how it was configured.
|
// people can see how it was configured.
|
||||||
klet.kubeletConfiguration = *kubeCfg
|
klet.kubeletConfiguration = *kubeCfg
|
||||||
|
|
|
@ -30,7 +30,10 @@ func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *v1.Po
|
||||||
effectiveSc := securitycontext.DetermineEffectiveSecurityContext(pod, container)
|
effectiveSc := securitycontext.DetermineEffectiveSecurityContext(pod, container)
|
||||||
synthesized := convertToRuntimeSecurityContext(effectiveSc)
|
synthesized := convertToRuntimeSecurityContext(effectiveSc)
|
||||||
if synthesized == nil {
|
if synthesized == nil {
|
||||||
synthesized = &runtimeapi.LinuxContainerSecurityContext{}
|
synthesized = &runtimeapi.LinuxContainerSecurityContext{
|
||||||
|
MaskedPaths: securitycontext.ConvertToRuntimeMaskedPaths(effectiveSc.ProcMount),
|
||||||
|
ReadonlyPaths: securitycontext.ConvertToRuntimeReadonlyPaths(effectiveSc.ProcMount),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set SeccompProfilePath.
|
// set SeccompProfilePath.
|
||||||
|
@ -67,6 +70,9 @@ func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *v1.Po
|
||||||
|
|
||||||
synthesized.NoNewPrivs = securitycontext.AddNoNewPrivileges(effectiveSc)
|
synthesized.NoNewPrivs = securitycontext.AddNoNewPrivileges(effectiveSc)
|
||||||
|
|
||||||
|
synthesized.MaskedPaths = securitycontext.ConvertToRuntimeMaskedPaths(effectiveSc.ProcMount)
|
||||||
|
synthesized.ReadonlyPaths = securitycontext.ConvertToRuntimeReadonlyPaths(effectiveSc.ProcMount)
|
||||||
|
|
||||||
return synthesized
|
return synthesized
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -230,3 +230,73 @@ func noNewPrivsRequired(pod *v1.Pod) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewProcMountAdmitHandler(runtime kubecontainer.Runtime) PodAdmitHandler {
|
||||||
|
return &procMountAdmitHandler{
|
||||||
|
Runtime: runtime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type procMountAdmitHandler struct {
|
||||||
|
kubecontainer.Runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *procMountAdmitHandler) Admit(attrs *PodAdmitAttributes) PodAdmitResult {
|
||||||
|
// If the pod is already running or terminated, no need to recheck NoNewPrivs.
|
||||||
|
if attrs.Pod.Status.Phase != v1.PodPending {
|
||||||
|
return PodAdmitResult{Admit: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the containers in a pod only need the default ProcMountType, admit it.
|
||||||
|
if procMountIsDefault(attrs.Pod) {
|
||||||
|
return PodAdmitResult{Admit: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always admit runtimes except docker.
|
||||||
|
if a.Runtime.Type() != kubetypes.DockerContainerRuntime {
|
||||||
|
return PodAdmitResult{Admit: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure docker api version is valid.
|
||||||
|
// Merged in https://github.com/moby/moby/pull/36644
|
||||||
|
rversion, err := a.Runtime.APIVersion()
|
||||||
|
if err != nil {
|
||||||
|
return PodAdmitResult{
|
||||||
|
Admit: false,
|
||||||
|
Reason: "ProcMount",
|
||||||
|
Message: fmt.Sprintf("Cannot enforce ProcMount: %v", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v, err := rversion.Compare("1.38.0")
|
||||||
|
if err != nil {
|
||||||
|
return PodAdmitResult{
|
||||||
|
Admit: false,
|
||||||
|
Reason: "ProcMount",
|
||||||
|
Message: fmt.Sprintf("Cannot enforce ProcMount: %v", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the version is less than 1.38 it will return -1 above.
|
||||||
|
if v == -1 {
|
||||||
|
return PodAdmitResult{
|
||||||
|
Admit: false,
|
||||||
|
Reason: "ProcMount",
|
||||||
|
Message: fmt.Sprintf("Cannot enforce ProcMount: docker runtime API version %q must be greater than or equal to 1.38", rversion.String()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PodAdmitResult{Admit: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
func procMountIsDefault(pod *v1.Pod) bool {
|
||||||
|
// Iterate over pod containers and check if we are using the DefaultProcMountType
|
||||||
|
// for all containers.
|
||||||
|
for _, c := range pod.Spec.Containers {
|
||||||
|
if c.SecurityContext != nil {
|
||||||
|
if c.SecurityContext.ProcMount != nil && *c.SecurityContext.ProcMount != v1.DefaultProcMount {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
@ -289,6 +289,21 @@ func (s *simpleProvider) ValidateContainer(pod *api.Pod, container *api.Containe
|
||||||
allErrs = append(allErrs, field.Invalid(scPath.Child("privileged"), *privileged, "Privileged containers are not allowed"))
|
allErrs = append(allErrs, field.Invalid(scPath.Child("privileged"), *privileged, "Privileged containers are not allowed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
procMount := sc.ProcMount()
|
||||||
|
allowedProcMounts := s.psp.Spec.AllowedProcMountTypes
|
||||||
|
if len(allowedProcMounts) == 0 {
|
||||||
|
allowedProcMounts = []api.ProcMountType{api.DefaultProcMount}
|
||||||
|
}
|
||||||
|
foundProcMountType := false
|
||||||
|
for _, pm := range allowedProcMounts {
|
||||||
|
if pm == procMount {
|
||||||
|
foundProcMountType = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundProcMountType {
|
||||||
|
allErrs = append(allErrs, field.Invalid(scPath.Child("procMount"), procMount, "ProcMountType is not allowed"))
|
||||||
|
}
|
||||||
|
|
||||||
allErrs = append(allErrs, s.strategies.CapabilitiesStrategy.Validate(scPath.Child("capabilities"), pod, container, sc.Capabilities())...)
|
allErrs = append(allErrs, s.strategies.CapabilitiesStrategy.Validate(scPath.Child("capabilities"), pod, container, sc.Capabilities())...)
|
||||||
|
|
||||||
allErrs = append(allErrs, s.hasInvalidHostPort(container, containerPath)...)
|
allErrs = append(allErrs, s.hasInvalidHostPort(container, containerPath)...)
|
||||||
|
|
|
@ -485,6 +485,10 @@ func TestValidateContainerFailures(t *testing.T) {
|
||||||
var priv bool = true
|
var priv bool = true
|
||||||
failPrivPod.Spec.Containers[0].SecurityContext.Privileged = &priv
|
failPrivPod.Spec.Containers[0].SecurityContext.Privileged = &priv
|
||||||
|
|
||||||
|
failProcMountPod := defaultPod()
|
||||||
|
failProcMountPod.Spec.Containers[0].SecurityContext.ProcMount = new(api.ProcMountType)
|
||||||
|
*failProcMountPod.Spec.Containers[0].SecurityContext.ProcMount = api.UnmaskedProcMount
|
||||||
|
|
||||||
failCapsPod := defaultPod()
|
failCapsPod := defaultPod()
|
||||||
failCapsPod.Spec.Containers[0].SecurityContext.Capabilities = &api.Capabilities{
|
failCapsPod.Spec.Containers[0].SecurityContext.Capabilities = &api.Capabilities{
|
||||||
Add: []api.Capability{"foo"},
|
Add: []api.Capability{"foo"},
|
||||||
|
@ -540,6 +544,11 @@ func TestValidateContainerFailures(t *testing.T) {
|
||||||
psp: defaultPSP(),
|
psp: defaultPSP(),
|
||||||
expectedError: "Privileged containers are not allowed",
|
expectedError: "Privileged containers are not allowed",
|
||||||
},
|
},
|
||||||
|
"failProcMountPSP": {
|
||||||
|
pod: failProcMountPod,
|
||||||
|
psp: defaultPSP(),
|
||||||
|
expectedError: "ProcMountType is not allowed",
|
||||||
|
},
|
||||||
"failCapsPSP": {
|
"failCapsPSP": {
|
||||||
pod: failCapsPod,
|
pod: failCapsPod,
|
||||||
psp: defaultPSP(),
|
psp: defaultPSP(),
|
||||||
|
|
|
@ -188,6 +188,7 @@ func (w *podSecurityContextWrapper) SetFSGroup(v *int64) {
|
||||||
type ContainerSecurityContextAccessor interface {
|
type ContainerSecurityContextAccessor interface {
|
||||||
Capabilities() *api.Capabilities
|
Capabilities() *api.Capabilities
|
||||||
Privileged() *bool
|
Privileged() *bool
|
||||||
|
ProcMount() api.ProcMountType
|
||||||
SELinuxOptions() *api.SELinuxOptions
|
SELinuxOptions() *api.SELinuxOptions
|
||||||
RunAsUser() *int64
|
RunAsUser() *int64
|
||||||
RunAsNonRoot() *bool
|
RunAsNonRoot() *bool
|
||||||
|
@ -257,6 +258,15 @@ func (w *containerSecurityContextWrapper) SetPrivileged(v *bool) {
|
||||||
w.ensureContainerSC()
|
w.ensureContainerSC()
|
||||||
w.containerSC.Privileged = v
|
w.containerSC.Privileged = v
|
||||||
}
|
}
|
||||||
|
func (w *containerSecurityContextWrapper) ProcMount() api.ProcMountType {
|
||||||
|
if w.containerSC == nil {
|
||||||
|
return api.DefaultProcMount
|
||||||
|
}
|
||||||
|
if w.containerSC.ProcMount == nil {
|
||||||
|
return api.DefaultProcMount
|
||||||
|
}
|
||||||
|
return *w.containerSC.ProcMount
|
||||||
|
}
|
||||||
func (w *containerSecurityContextWrapper) SELinuxOptions() *api.SELinuxOptions {
|
func (w *containerSecurityContextWrapper) SELinuxOptions() *api.SELinuxOptions {
|
||||||
if w.containerSC == nil {
|
if w.containerSC == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -356,6 +366,9 @@ func (w *effectiveContainerSecurityContextWrapper) SetPrivileged(v *bool) {
|
||||||
w.containerSC.SetPrivileged(v)
|
w.containerSC.SetPrivileged(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func (w *effectiveContainerSecurityContextWrapper) ProcMount() api.ProcMountType {
|
||||||
|
return w.containerSC.ProcMount()
|
||||||
|
}
|
||||||
func (w *effectiveContainerSecurityContextWrapper) SELinuxOptions() *api.SELinuxOptions {
|
func (w *effectiveContainerSecurityContextWrapper) SELinuxOptions() *api.SELinuxOptions {
|
||||||
if v := w.containerSC.SELinuxOptions(); v != nil {
|
if v := w.containerSC.SELinuxOptions(); v != nil {
|
||||||
return v
|
return v
|
||||||
|
|
|
@ -35,8 +35,10 @@ func ValidSecurityContextWithContainerDefaults() *v1.SecurityContext {
|
||||||
// empty container defaults. Used for testing.
|
// empty container defaults. Used for testing.
|
||||||
func ValidInternalSecurityContextWithContainerDefaults() *api.SecurityContext {
|
func ValidInternalSecurityContextWithContainerDefaults() *api.SecurityContext {
|
||||||
priv := false
|
priv := false
|
||||||
|
dpm := api.DefaultProcMount
|
||||||
return &api.SecurityContext{
|
return &api.SecurityContext{
|
||||||
Capabilities: &api.Capabilities{},
|
Capabilities: &api.Capabilities{},
|
||||||
Privileged: &priv,
|
Privileged: &priv,
|
||||||
|
ProcMount: &dpm,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ func DetermineEffectiveSecurityContext(pod *v1.Pod, container *v1.Container) *v1
|
||||||
containerSc := container.SecurityContext
|
containerSc := container.SecurityContext
|
||||||
|
|
||||||
if effectiveSc == nil && containerSc == nil {
|
if effectiveSc == nil && containerSc == nil {
|
||||||
return nil
|
return &v1.SecurityContext{}
|
||||||
}
|
}
|
||||||
if effectiveSc != nil && containerSc == nil {
|
if effectiveSc != nil && containerSc == nil {
|
||||||
return effectiveSc
|
return effectiveSc
|
||||||
|
@ -121,6 +121,11 @@ func DetermineEffectiveSecurityContext(pod *v1.Pod, container *v1.Container) *v1
|
||||||
*effectiveSc.AllowPrivilegeEscalation = *containerSc.AllowPrivilegeEscalation
|
*effectiveSc.AllowPrivilegeEscalation = *containerSc.AllowPrivilegeEscalation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if containerSc.ProcMount != nil {
|
||||||
|
effectiveSc.ProcMount = new(v1.ProcMountType)
|
||||||
|
*effectiveSc.ProcMount = *containerSc.ProcMount
|
||||||
|
}
|
||||||
|
|
||||||
return effectiveSc
|
return effectiveSc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,3 +172,52 @@ func AddNoNewPrivileges(sc *v1.SecurityContext) bool {
|
||||||
// handle the case where defaultAllowPrivilegeEscalation is false or the user explicitly set allowPrivilegeEscalation to true/false
|
// handle the case where defaultAllowPrivilegeEscalation is false or the user explicitly set allowPrivilegeEscalation to true/false
|
||||||
return !*sc.AllowPrivilegeEscalation
|
return !*sc.AllowPrivilegeEscalation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// These *must* be kept in sync with moby/moby.
|
||||||
|
// https://github.com/moby/moby/blob/master/oci/defaults.go#L116-L134
|
||||||
|
// @jessfraz will watch changes to those files upstream.
|
||||||
|
defaultMaskedPaths = []string{
|
||||||
|
"/proc/acpi",
|
||||||
|
"/proc/kcore",
|
||||||
|
"/proc/keys",
|
||||||
|
"/proc/latency_stats",
|
||||||
|
"/proc/timer_list",
|
||||||
|
"/proc/timer_stats",
|
||||||
|
"/proc/sched_debug",
|
||||||
|
"/proc/scsi",
|
||||||
|
"/sys/firmware",
|
||||||
|
}
|
||||||
|
defaultReadonlyPaths = []string{
|
||||||
|
"/proc/asound",
|
||||||
|
"/proc/bus",
|
||||||
|
"/proc/fs",
|
||||||
|
"/proc/irq",
|
||||||
|
"/proc/sys",
|
||||||
|
"/proc/sysrq-trigger",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConvertToRuntimeMaskedPaths converts the ProcMountType to the specified or default
|
||||||
|
// masked paths.
|
||||||
|
func ConvertToRuntimeMaskedPaths(opt *v1.ProcMountType) []string {
|
||||||
|
if opt != nil && *opt == v1.UnmaskedProcMount {
|
||||||
|
// Unmasked proc mount should have no paths set as masked.
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, add the default masked paths to the runtime security context.
|
||||||
|
return defaultMaskedPaths
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertToRuntimeReadonlyPaths converts the ProcMountType to the specified or default
|
||||||
|
// readonly paths.
|
||||||
|
func ConvertToRuntimeReadonlyPaths(opt *v1.ProcMountType) []string {
|
||||||
|
if opt != nil && *opt == v1.UnmaskedProcMount {
|
||||||
|
// Unmasked proc mount should have no paths set as readonly.
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, add the default readonly paths to the runtime security context.
|
||||||
|
return defaultReadonlyPaths
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
package securitycontext
|
package securitycontext
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
|
@ -123,3 +124,61 @@ func TestAddNoNewPrivileges(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConvertToRuntimeMaskedPaths(t *testing.T) {
|
||||||
|
dPM := v1.DefaultProcMount
|
||||||
|
uPM := v1.UnmaskedProcMount
|
||||||
|
tests := map[string]struct {
|
||||||
|
pm *v1.ProcMountType
|
||||||
|
expect []string
|
||||||
|
}{
|
||||||
|
"procMount nil": {
|
||||||
|
pm: nil,
|
||||||
|
expect: defaultMaskedPaths,
|
||||||
|
},
|
||||||
|
"procMount default": {
|
||||||
|
pm: &dPM,
|
||||||
|
expect: defaultMaskedPaths,
|
||||||
|
},
|
||||||
|
"procMount unmasked": {
|
||||||
|
pm: &uPM,
|
||||||
|
expect: []string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range tests {
|
||||||
|
actual := ConvertToRuntimeMaskedPaths(v.pm)
|
||||||
|
if !reflect.DeepEqual(actual, v.expect) {
|
||||||
|
t.Errorf("%s failed, expected %#v but received %#v", k, v.expect, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertToRuntimeReadonlyPaths(t *testing.T) {
|
||||||
|
dPM := v1.DefaultProcMount
|
||||||
|
uPM := v1.UnmaskedProcMount
|
||||||
|
tests := map[string]struct {
|
||||||
|
pm *v1.ProcMountType
|
||||||
|
expect []string
|
||||||
|
}{
|
||||||
|
"procMount nil": {
|
||||||
|
pm: nil,
|
||||||
|
expect: defaultReadonlyPaths,
|
||||||
|
},
|
||||||
|
"procMount default": {
|
||||||
|
pm: &dPM,
|
||||||
|
expect: defaultReadonlyPaths,
|
||||||
|
},
|
||||||
|
"procMount unmasked": {
|
||||||
|
pm: &uPM,
|
||||||
|
expect: []string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range tests {
|
||||||
|
actual := ConvertToRuntimeReadonlyPaths(v.pm)
|
||||||
|
if !reflect.DeepEqual(actual, v.expect) {
|
||||||
|
t.Errorf("%s failed, expected %#v but received %#v", k, v.expect, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue