Commit 4e57c800 authored by k8s-merge-robot's avatar k8s-merge-robot

Merge pull request #24600 from pweil-/psp

Automatic merge from submit-queue PSP admission ```release-note Update PodSecurityPolicy types and add admission controller that could enforce them ``` Still working on removing the non-relevant parts of the tests but I wanted to get this open to start soliciting feedback. - [x] bring PSP up to date with any new features we've added to SCC for discussion - [x] create admission controller that is a pared down version of SCC (no ns based strategies, no user/groups/service account permissioning) - [x] fix tests @liggitt @pmorie - this is the simple implementation requested that assumes all PSPs should be checked for each requests. It is a slimmed down version of our SCC admission controller @erictune @smarterclayton
parents b1560f36 65c8a1f6
apiVersion: extensions/v1beta1
kind: PodSecurityPolicy
metadata:
annotations:
kubernetes.io/description: 'privileged allows access to all privileged and host
features and the ability to run as any user, any group, any fsGroup, and with
any SELinux context.'
creationTimestamp: 2016-05-06T19:28:58Z
name: privileged
spec:
privileged: true
defaultAddCapabilities: null
requiredDropCapabilities: null
allowedCapabilities: null
volumes:
- '*'
hostNetwork: true
hostPorts:
-
min: 0
max: 65535
hostIPC: true
hostPID: true
runAsUser:
rule: 'RunAsAny'
seLinux:
rule: 'RunAsAny'
supplementalGroups:
rule: 'RunAsAny'
fsGroup:
rule: 'RunAsAny'
readOnlyRootFilesystem: false
...@@ -36,6 +36,7 @@ import ( ...@@ -36,6 +36,7 @@ import (
_ "k8s.io/kubernetes/plugin/pkg/admission/namespace/lifecycle" _ "k8s.io/kubernetes/plugin/pkg/admission/namespace/lifecycle"
_ "k8s.io/kubernetes/plugin/pkg/admission/persistentvolume/label" _ "k8s.io/kubernetes/plugin/pkg/admission/persistentvolume/label"
_ "k8s.io/kubernetes/plugin/pkg/admission/resourcequota" _ "k8s.io/kubernetes/plugin/pkg/admission/resourcequota"
_ "k8s.io/kubernetes/plugin/pkg/admission/security/podsecuritypolicy"
_ "k8s.io/kubernetes/plugin/pkg/admission/securitycontext/scdeny" _ "k8s.io/kubernetes/plugin/pkg/admission/securitycontext/scdeny"
_ "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount" _ "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
) )
...@@ -51,7 +51,7 @@ kube-apiserver ...@@ -51,7 +51,7 @@ kube-apiserver
### Options ### Options
``` ```
--admission-control="AlwaysAdmit": Ordered list of plug-ins to do admission control of resources into cluster. Comma-delimited list of: AlwaysAdmit, AlwaysDeny, AlwaysPullImages, DenyEscalatingExec, DenyExecOnPrivileged, InitialResources, LimitPodHardAntiAffinityTopology, LimitRanger, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, PersistentVolumeLabel, ResourceQuota, SecurityContextDeny, ServiceAccount --admission-control="AlwaysAdmit": Ordered list of plug-ins to do admission control of resources into cluster. Comma-delimited list of: AlwaysAdmit, AlwaysDeny, AlwaysPullImages, DenyEscalatingExec, DenyExecOnPrivileged, InitialResources, LimitPodHardAntiAffinityTopology, LimitRanger, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, PersistentVolumeLabel, PodSecurityPolicy, ResourceQuota, SecurityContextDeny, ServiceAccount
--admission-control-config-file="": File with admission control configuration. --admission-control-config-file="": File with admission control configuration.
--advertise-address=<nil>: The IP address on which to advertise the apiserver to members of the cluster. This address must be reachable by the rest of the cluster. If blank, the --bind-address will be used. If --bind-address is unspecified, the host's default interface will be used. --advertise-address=<nil>: The IP address on which to advertise the apiserver to members of the cluster. This address must be reachable by the rest of the cluster. If blank, the --bind-address will be used. If --bind-address is unspecified, the host's default interface will be used.
--allow-privileged[=false]: If true, allow privileged containers. --allow-privileged[=false]: If true, allow privileged containers.
...@@ -119,7 +119,7 @@ kube-apiserver ...@@ -119,7 +119,7 @@ kube-apiserver
--watch-cache-sizes=[]: List of watch cache sizes for every resource (pods, nodes, etc.), comma separated. The individual override format: resource#size, where size is a number. It takes effect when watch-cache is enabled. --watch-cache-sizes=[]: List of watch cache sizes for every resource (pods, nodes, etc.), comma separated. The individual override format: resource#size, where size is a number. It takes effect when watch-cache is enabled.
``` ```
###### Auto generated by spf13/cobra on 9-May-2016 ###### Auto generated by spf13/cobra on 10-May-2016
<!-- BEGIN MUNGE: GENERATED_ANALYTICS --> <!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
......
...@@ -45,6 +45,7 @@ func init() { ...@@ -45,6 +45,7 @@ func init() {
DeepCopy_extensions_DeploymentSpec, DeepCopy_extensions_DeploymentSpec,
DeepCopy_extensions_DeploymentStatus, DeepCopy_extensions_DeploymentStatus,
DeepCopy_extensions_DeploymentStrategy, DeepCopy_extensions_DeploymentStrategy,
DeepCopy_extensions_FSGroupStrategyOptions,
DeepCopy_extensions_HTTPIngressPath, DeepCopy_extensions_HTTPIngressPath,
DeepCopy_extensions_HTTPIngressRuleValue, DeepCopy_extensions_HTTPIngressRuleValue,
DeepCopy_extensions_HostPortRange, DeepCopy_extensions_HostPortRange,
...@@ -72,6 +73,7 @@ func init() { ...@@ -72,6 +73,7 @@ func init() {
DeepCopy_extensions_Scale, DeepCopy_extensions_Scale,
DeepCopy_extensions_ScaleSpec, DeepCopy_extensions_ScaleSpec,
DeepCopy_extensions_ScaleStatus, DeepCopy_extensions_ScaleStatus,
DeepCopy_extensions_SupplementalGroupsStrategyOptions,
DeepCopy_extensions_ThirdPartyResource, DeepCopy_extensions_ThirdPartyResource,
DeepCopy_extensions_ThirdPartyResourceData, DeepCopy_extensions_ThirdPartyResourceData,
DeepCopy_extensions_ThirdPartyResourceDataList, DeepCopy_extensions_ThirdPartyResourceDataList,
...@@ -311,6 +313,22 @@ func DeepCopy_extensions_DeploymentStrategy(in DeploymentStrategy, out *Deployme ...@@ -311,6 +313,22 @@ func DeepCopy_extensions_DeploymentStrategy(in DeploymentStrategy, out *Deployme
return nil return nil
} }
func DeepCopy_extensions_FSGroupStrategyOptions(in FSGroupStrategyOptions, out *FSGroupStrategyOptions, c *conversion.Cloner) error {
out.Rule = in.Rule
if in.Ranges != nil {
in, out := in.Ranges, &out.Ranges
*out = make([]IDRange, len(in))
for i := range in {
if err := DeepCopy_extensions_IDRange(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Ranges = nil
}
return nil
}
func DeepCopy_extensions_HTTPIngressPath(in HTTPIngressPath, out *HTTPIngressPath, c *conversion.Cloner) error { func DeepCopy_extensions_HTTPIngressPath(in HTTPIngressPath, out *HTTPIngressPath, c *conversion.Cloner) error {
out.Path = in.Path out.Path = in.Path
if err := DeepCopy_extensions_IngressBackend(in.Backend, &out.Backend, c); err != nil { if err := DeepCopy_extensions_IngressBackend(in.Backend, &out.Backend, c); err != nil {
...@@ -502,14 +520,32 @@ func DeepCopy_extensions_PodSecurityPolicyList(in PodSecurityPolicyList, out *Po ...@@ -502,14 +520,32 @@ func DeepCopy_extensions_PodSecurityPolicyList(in PodSecurityPolicyList, out *Po
func DeepCopy_extensions_PodSecurityPolicySpec(in PodSecurityPolicySpec, out *PodSecurityPolicySpec, c *conversion.Cloner) error { func DeepCopy_extensions_PodSecurityPolicySpec(in PodSecurityPolicySpec, out *PodSecurityPolicySpec, c *conversion.Cloner) error {
out.Privileged = in.Privileged out.Privileged = in.Privileged
if in.Capabilities != nil { if in.DefaultAddCapabilities != nil {
in, out := in.Capabilities, &out.Capabilities in, out := in.DefaultAddCapabilities, &out.DefaultAddCapabilities
*out = make([]api.Capability, len(in))
for i := range in {
(*out)[i] = in[i]
}
} else {
out.DefaultAddCapabilities = nil
}
if in.RequiredDropCapabilities != nil {
in, out := in.RequiredDropCapabilities, &out.RequiredDropCapabilities
*out = make([]api.Capability, len(in))
for i := range in {
(*out)[i] = in[i]
}
} else {
out.RequiredDropCapabilities = nil
}
if in.AllowedCapabilities != nil {
in, out := in.AllowedCapabilities, &out.AllowedCapabilities
*out = make([]api.Capability, len(in)) *out = make([]api.Capability, len(in))
for i := range in { for i := range in {
(*out)[i] = in[i] (*out)[i] = in[i]
} }
} else { } else {
out.Capabilities = nil out.AllowedCapabilities = nil
} }
if in.Volumes != nil { if in.Volumes != nil {
in, out := in.Volumes, &out.Volumes in, out := in.Volumes, &out.Volumes
...@@ -540,6 +576,13 @@ func DeepCopy_extensions_PodSecurityPolicySpec(in PodSecurityPolicySpec, out *Po ...@@ -540,6 +576,13 @@ func DeepCopy_extensions_PodSecurityPolicySpec(in PodSecurityPolicySpec, out *Po
if err := DeepCopy_extensions_RunAsUserStrategyOptions(in.RunAsUser, &out.RunAsUser, c); err != nil { if err := DeepCopy_extensions_RunAsUserStrategyOptions(in.RunAsUser, &out.RunAsUser, c); err != nil {
return err return err
} }
if err := DeepCopy_extensions_SupplementalGroupsStrategyOptions(in.SupplementalGroups, &out.SupplementalGroups, c); err != nil {
return err
}
if err := DeepCopy_extensions_FSGroupStrategyOptions(in.FSGroup, &out.FSGroup, c); err != nil {
return err
}
out.ReadOnlyRootFilesystem = in.ReadOnlyRootFilesystem
return nil return nil
} }
...@@ -691,6 +734,22 @@ func DeepCopy_extensions_ScaleStatus(in ScaleStatus, out *ScaleStatus, c *conver ...@@ -691,6 +734,22 @@ func DeepCopy_extensions_ScaleStatus(in ScaleStatus, out *ScaleStatus, c *conver
return nil return nil
} }
func DeepCopy_extensions_SupplementalGroupsStrategyOptions(in SupplementalGroupsStrategyOptions, out *SupplementalGroupsStrategyOptions, c *conversion.Cloner) error {
out.Rule = in.Rule
if in.Ranges != nil {
in, out := in.Ranges, &out.Ranges
*out = make([]IDRange, len(in))
for i := range in {
if err := DeepCopy_extensions_IDRange(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Ranges = nil
}
return nil
}
func DeepCopy_extensions_ThirdPartyResource(in ThirdPartyResource, out *ThirdPartyResource, c *conversion.Cloner) error { func DeepCopy_extensions_ThirdPartyResource(in ThirdPartyResource, out *ThirdPartyResource, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err return err
......
...@@ -624,6 +624,8 @@ type ReplicaSetStatus struct { ...@@ -624,6 +624,8 @@ type ReplicaSetStatus struct {
ObservedGeneration int64 `json:"observedGeneration,omitempty"` ObservedGeneration int64 `json:"observedGeneration,omitempty"`
} }
// +genclient=true,nonNamespaced=true
// PodSecurityPolicy governs the ability to make requests that affect the SecurityContext // PodSecurityPolicy governs the ability to make requests that affect the SecurityContext
// that will be applied to a pod and container. // that will be applied to a pod and container.
type PodSecurityPolicy struct { type PodSecurityPolicy struct {
...@@ -638,8 +640,17 @@ type PodSecurityPolicy struct { ...@@ -638,8 +640,17 @@ type PodSecurityPolicy struct {
type PodSecurityPolicySpec struct { type PodSecurityPolicySpec struct {
// Privileged determines if a pod can request to be run as privileged. // Privileged determines if a pod can request to be run as privileged.
Privileged bool `json:"privileged,omitempty"` Privileged bool `json:"privileged,omitempty"`
// Capabilities is a list of capabilities that can be added. // DefaultAddCapabilities is the default set of capabilities that will be added to the container
Capabilities []api.Capability `json:"capabilities,omitempty"` // unless the pod spec specifically drops the capability. You may not list a capabiility in both
// DefaultAddCapabilities and RequiredDropCapabilities.
DefaultAddCapabilities []api.Capability `json:"defaultAddCapabilities,omitempty"`
// RequiredDropCapabilities are the capabilities that will be dropped from the container. These
// are required to be dropped and cannot be added.
RequiredDropCapabilities []api.Capability `json:"requiredDropCapabilities,omitempty"`
// AllowedCapabilities is a list of capabilities that can be requested to add to the container.
// Capabilities in this field may be added at the pod author's discretion.
// You must not list a capability in both AllowedCapabilities and RequiredDropCapabilities.
AllowedCapabilities []api.Capability `json:"allowedCapabilities,omitempty"`
// Volumes is a white list of allowed volume plugins. Empty indicates that all plugins // Volumes is a white list of allowed volume plugins. Empty indicates that all plugins
// may be used. // may be used.
Volumes []FSType `json:"volumes,omitempty"` Volumes []FSType `json:"volumes,omitempty"`
...@@ -652,9 +663,19 @@ type PodSecurityPolicySpec struct { ...@@ -652,9 +663,19 @@ type PodSecurityPolicySpec struct {
// HostIPC determines if the policy allows the use of HostIPC in the pod spec. // HostIPC determines if the policy allows the use of HostIPC in the pod spec.
HostIPC bool `json:"hostIPC,omitempty"` HostIPC bool `json:"hostIPC,omitempty"`
// SELinux is the strategy that will dictate the allowable labels that may be set. // SELinux is the strategy that will dictate the allowable labels that may be set.
SELinux SELinuxStrategyOptions `json:"seLinux,omitempty"` SELinux SELinuxStrategyOptions `json:"seLinux"`
// RunAsUser is the strategy that will dictate the allowable RunAsUser values that may be set. // RunAsUser is the strategy that will dictate the allowable RunAsUser values that may be set.
RunAsUser RunAsUserStrategyOptions `json:"runAsUser,omitempty"` RunAsUser RunAsUserStrategyOptions `json:"runAsUser"`
// SupplementalGroups is the strategy that will dictate what supplemental groups are used by the SecurityContext.
SupplementalGroups SupplementalGroupsStrategyOptions `json:"supplementalGroups"`
// FSGroup is the strategy that will dictate what fs group is used by the SecurityContext.
FSGroup FSGroupStrategyOptions `json:"fsGroup"`
// ReadOnlyRootFilesystem when set to true will force containers to run with a read only root file
// system. If the container specifically requests to run with a non-read only root file system
// the PSP should deny the pod.
// If set to false the container may run with a read only root file system if it wishes but it
// will not be forced to.
ReadOnlyRootFilesystem bool `json:"readOnlyRootFilesystem,omitempty"`
} }
// HostPortRange defines a range of host ports that will be enabled by a policy // HostPortRange defines a range of host ports that will be enabled by a policy
...@@ -670,6 +691,9 @@ type HostPortRange struct { ...@@ -670,6 +691,9 @@ type HostPortRange struct {
type FSType string type FSType string
var ( var (
AzureFile FSType = "azureFile"
Flocker FSType = "flocker"
FlexVolume FSType = "flexVolume"
HostPath FSType = "hostPath" HostPath FSType = "hostPath"
EmptyDir FSType = "emptyDir" EmptyDir FSType = "emptyDir"
GCEPersistentDisk FSType = "gcePersistentDisk" GCEPersistentDisk FSType = "gcePersistentDisk"
...@@ -685,6 +709,8 @@ var ( ...@@ -685,6 +709,8 @@ var (
CephFS FSType = "cephFS" CephFS FSType = "cephFS"
DownwardAPI FSType = "downwardAPI" DownwardAPI FSType = "downwardAPI"
FC FSType = "fc" FC FSType = "fc"
ConfigMap FSType = "configMap"
All FSType = "*"
) )
// SELinuxStrategyOptions defines the strategy type and any options used to create the strategy. // SELinuxStrategyOptions defines the strategy type and any options used to create the strategy.
...@@ -736,6 +762,46 @@ const ( ...@@ -736,6 +762,46 @@ const (
RunAsUserStrategyRunAsAny RunAsUserStrategy = "RunAsAny" RunAsUserStrategyRunAsAny RunAsUserStrategy = "RunAsAny"
) )
// FSGroupStrategyOptions defines the strategy type and options used to create the strategy.
type FSGroupStrategyOptions struct {
// Rule is the strategy that will dictate what FSGroup is used in the SecurityContext.
Rule FSGroupStrategyType `json:"rule,omitempty"`
// Ranges are the allowed ranges of fs groups. If you would like to force a single
// fs group then supply a single range with the same start and end.
Ranges []IDRange `json:"ranges,omitempty"`
}
// FSGroupStrategyType denotes strategy types for generating FSGroup values for a
// SecurityContext
type FSGroupStrategyType string
const (
// container must have FSGroup of X applied.
FSGroupStrategyMustRunAs FSGroupStrategyType = "MustRunAs"
// container may make requests for any FSGroup labels.
FSGroupStrategyRunAsAny FSGroupStrategyType = "RunAsAny"
)
// SupplementalGroupsStrategyOptions defines the strategy type and options used to create the strategy.
type SupplementalGroupsStrategyOptions struct {
// Rule is the strategy that will dictate what supplemental groups is used in the SecurityContext.
Rule SupplementalGroupsStrategyType `json:"rule,omitempty"`
// Ranges are the allowed ranges of supplemental groups. If you would like to force a single
// supplemental group then supply a single range with the same start and end.
Ranges []IDRange `json:"ranges,omitempty"`
}
// SupplementalGroupsStrategyType denotes strategy types for determining valid supplemental
// groups for a SecurityContext.
type SupplementalGroupsStrategyType string
const (
// container must run as a particular gid.
SupplementalGroupsStrategyMustRunAs SupplementalGroupsStrategyType = "MustRunAs"
// container may make requests for any gid.
SupplementalGroupsStrategyRunAsAny SupplementalGroupsStrategyType = "RunAsAny"
)
// PodSecurityPolicyList is a list of PodSecurityPolicy objects. // PodSecurityPolicyList is a list of PodSecurityPolicy objects.
type PodSecurityPolicyList struct { type PodSecurityPolicyList struct {
unversioned.TypeMeta `json:",inline"` unversioned.TypeMeta `json:",inline"`
......
...@@ -62,6 +62,8 @@ func init() { ...@@ -62,6 +62,8 @@ func init() {
Convert_extensions_DeploymentStatus_To_v1beta1_DeploymentStatus, Convert_extensions_DeploymentStatus_To_v1beta1_DeploymentStatus,
Convert_v1beta1_DeploymentStrategy_To_extensions_DeploymentStrategy, Convert_v1beta1_DeploymentStrategy_To_extensions_DeploymentStrategy,
Convert_extensions_DeploymentStrategy_To_v1beta1_DeploymentStrategy, Convert_extensions_DeploymentStrategy_To_v1beta1_DeploymentStrategy,
Convert_v1beta1_FSGroupStrategyOptions_To_extensions_FSGroupStrategyOptions,
Convert_extensions_FSGroupStrategyOptions_To_v1beta1_FSGroupStrategyOptions,
Convert_v1beta1_HTTPIngressPath_To_extensions_HTTPIngressPath, Convert_v1beta1_HTTPIngressPath_To_extensions_HTTPIngressPath,
Convert_extensions_HTTPIngressPath_To_v1beta1_HTTPIngressPath, Convert_extensions_HTTPIngressPath_To_v1beta1_HTTPIngressPath,
Convert_v1beta1_HTTPIngressRuleValue_To_extensions_HTTPIngressRuleValue, Convert_v1beta1_HTTPIngressRuleValue_To_extensions_HTTPIngressRuleValue,
...@@ -138,6 +140,8 @@ func init() { ...@@ -138,6 +140,8 @@ func init() {
Convert_extensions_ScaleSpec_To_v1beta1_ScaleSpec, Convert_extensions_ScaleSpec_To_v1beta1_ScaleSpec,
Convert_v1beta1_ScaleStatus_To_extensions_ScaleStatus, Convert_v1beta1_ScaleStatus_To_extensions_ScaleStatus,
Convert_extensions_ScaleStatus_To_v1beta1_ScaleStatus, Convert_extensions_ScaleStatus_To_v1beta1_ScaleStatus,
Convert_v1beta1_SupplementalGroupsStrategyOptions_To_extensions_SupplementalGroupsStrategyOptions,
Convert_extensions_SupplementalGroupsStrategyOptions_To_v1beta1_SupplementalGroupsStrategyOptions,
Convert_v1beta1_ThirdPartyResource_To_extensions_ThirdPartyResource, Convert_v1beta1_ThirdPartyResource_To_extensions_ThirdPartyResource,
Convert_extensions_ThirdPartyResource_To_v1beta1_ThirdPartyResource, Convert_extensions_ThirdPartyResource_To_v1beta1_ThirdPartyResource,
Convert_v1beta1_ThirdPartyResourceData_To_extensions_ThirdPartyResourceData, Convert_v1beta1_ThirdPartyResourceData_To_extensions_ThirdPartyResourceData,
...@@ -646,6 +650,46 @@ func autoConvert_extensions_DeploymentStrategy_To_v1beta1_DeploymentStrategy(in ...@@ -646,6 +650,46 @@ func autoConvert_extensions_DeploymentStrategy_To_v1beta1_DeploymentStrategy(in
return nil return nil
} }
func autoConvert_v1beta1_FSGroupStrategyOptions_To_extensions_FSGroupStrategyOptions(in *FSGroupStrategyOptions, out *extensions.FSGroupStrategyOptions, s conversion.Scope) error {
out.Rule = extensions.FSGroupStrategyType(in.Rule)
if in.Ranges != nil {
in, out := &in.Ranges, &out.Ranges
*out = make([]extensions.IDRange, len(*in))
for i := range *in {
if err := Convert_v1beta1_IDRange_To_extensions_IDRange(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.Ranges = nil
}
return nil
}
func Convert_v1beta1_FSGroupStrategyOptions_To_extensions_FSGroupStrategyOptions(in *FSGroupStrategyOptions, out *extensions.FSGroupStrategyOptions, s conversion.Scope) error {
return autoConvert_v1beta1_FSGroupStrategyOptions_To_extensions_FSGroupStrategyOptions(in, out, s)
}
func autoConvert_extensions_FSGroupStrategyOptions_To_v1beta1_FSGroupStrategyOptions(in *extensions.FSGroupStrategyOptions, out *FSGroupStrategyOptions, s conversion.Scope) error {
out.Rule = FSGroupStrategyType(in.Rule)
if in.Ranges != nil {
in, out := &in.Ranges, &out.Ranges
*out = make([]IDRange, len(*in))
for i := range *in {
if err := Convert_extensions_IDRange_To_v1beta1_IDRange(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.Ranges = nil
}
return nil
}
func Convert_extensions_FSGroupStrategyOptions_To_v1beta1_FSGroupStrategyOptions(in *extensions.FSGroupStrategyOptions, out *FSGroupStrategyOptions, s conversion.Scope) error {
return autoConvert_extensions_FSGroupStrategyOptions_To_v1beta1_FSGroupStrategyOptions(in, out, s)
}
func autoConvert_v1beta1_HTTPIngressPath_To_extensions_HTTPIngressPath(in *HTTPIngressPath, out *extensions.HTTPIngressPath, s conversion.Scope) error { func autoConvert_v1beta1_HTTPIngressPath_To_extensions_HTTPIngressPath(in *HTTPIngressPath, out *extensions.HTTPIngressPath, s conversion.Scope) error {
out.Path = in.Path out.Path = in.Path
if err := Convert_v1beta1_IngressBackend_To_extensions_IngressBackend(&in.Backend, &out.Backend, s); err != nil { if err := Convert_v1beta1_IngressBackend_To_extensions_IngressBackend(&in.Backend, &out.Backend, s); err != nil {
...@@ -1602,14 +1646,32 @@ func Convert_extensions_PodSecurityPolicyList_To_v1beta1_PodSecurityPolicyList(i ...@@ -1602,14 +1646,32 @@ func Convert_extensions_PodSecurityPolicyList_To_v1beta1_PodSecurityPolicyList(i
func autoConvert_v1beta1_PodSecurityPolicySpec_To_extensions_PodSecurityPolicySpec(in *PodSecurityPolicySpec, out *extensions.PodSecurityPolicySpec, s conversion.Scope) error { func autoConvert_v1beta1_PodSecurityPolicySpec_To_extensions_PodSecurityPolicySpec(in *PodSecurityPolicySpec, out *extensions.PodSecurityPolicySpec, s conversion.Scope) error {
out.Privileged = in.Privileged out.Privileged = in.Privileged
if in.Capabilities != nil { if in.DefaultAddCapabilities != nil {
in, out := &in.Capabilities, &out.Capabilities in, out := &in.DefaultAddCapabilities, &out.DefaultAddCapabilities
*out = make([]api.Capability, len(*in))
for i := range *in {
(*out)[i] = api.Capability((*in)[i])
}
} else {
out.DefaultAddCapabilities = nil
}
if in.RequiredDropCapabilities != nil {
in, out := &in.RequiredDropCapabilities, &out.RequiredDropCapabilities
*out = make([]api.Capability, len(*in))
for i := range *in {
(*out)[i] = api.Capability((*in)[i])
}
} else {
out.RequiredDropCapabilities = nil
}
if in.AllowedCapabilities != nil {
in, out := &in.AllowedCapabilities, &out.AllowedCapabilities
*out = make([]api.Capability, len(*in)) *out = make([]api.Capability, len(*in))
for i := range *in { for i := range *in {
(*out)[i] = api.Capability((*in)[i]) (*out)[i] = api.Capability((*in)[i])
} }
} else { } else {
out.Capabilities = nil out.AllowedCapabilities = nil
} }
if in.Volumes != nil { if in.Volumes != nil {
in, out := &in.Volumes, &out.Volumes in, out := &in.Volumes, &out.Volumes
...@@ -1640,6 +1702,13 @@ func autoConvert_v1beta1_PodSecurityPolicySpec_To_extensions_PodSecurityPolicySp ...@@ -1640,6 +1702,13 @@ func autoConvert_v1beta1_PodSecurityPolicySpec_To_extensions_PodSecurityPolicySp
if err := Convert_v1beta1_RunAsUserStrategyOptions_To_extensions_RunAsUserStrategyOptions(&in.RunAsUser, &out.RunAsUser, s); err != nil { if err := Convert_v1beta1_RunAsUserStrategyOptions_To_extensions_RunAsUserStrategyOptions(&in.RunAsUser, &out.RunAsUser, s); err != nil {
return err return err
} }
if err := Convert_v1beta1_SupplementalGroupsStrategyOptions_To_extensions_SupplementalGroupsStrategyOptions(&in.SupplementalGroups, &out.SupplementalGroups, s); err != nil {
return err
}
if err := Convert_v1beta1_FSGroupStrategyOptions_To_extensions_FSGroupStrategyOptions(&in.FSGroup, &out.FSGroup, s); err != nil {
return err
}
out.ReadOnlyRootFilesystem = in.ReadOnlyRootFilesystem
return nil return nil
} }
...@@ -1649,14 +1718,32 @@ func Convert_v1beta1_PodSecurityPolicySpec_To_extensions_PodSecurityPolicySpec(i ...@@ -1649,14 +1718,32 @@ func Convert_v1beta1_PodSecurityPolicySpec_To_extensions_PodSecurityPolicySpec(i
func autoConvert_extensions_PodSecurityPolicySpec_To_v1beta1_PodSecurityPolicySpec(in *extensions.PodSecurityPolicySpec, out *PodSecurityPolicySpec, s conversion.Scope) error { func autoConvert_extensions_PodSecurityPolicySpec_To_v1beta1_PodSecurityPolicySpec(in *extensions.PodSecurityPolicySpec, out *PodSecurityPolicySpec, s conversion.Scope) error {
out.Privileged = in.Privileged out.Privileged = in.Privileged
if in.Capabilities != nil { if in.DefaultAddCapabilities != nil {
in, out := &in.Capabilities, &out.Capabilities in, out := &in.DefaultAddCapabilities, &out.DefaultAddCapabilities
*out = make([]v1.Capability, len(*in)) *out = make([]v1.Capability, len(*in))
for i := range *in { for i := range *in {
(*out)[i] = v1.Capability((*in)[i]) (*out)[i] = v1.Capability((*in)[i])
} }
} else { } else {
out.Capabilities = nil out.DefaultAddCapabilities = nil
}
if in.RequiredDropCapabilities != nil {
in, out := &in.RequiredDropCapabilities, &out.RequiredDropCapabilities
*out = make([]v1.Capability, len(*in))
for i := range *in {
(*out)[i] = v1.Capability((*in)[i])
}
} else {
out.RequiredDropCapabilities = nil
}
if in.AllowedCapabilities != nil {
in, out := &in.AllowedCapabilities, &out.AllowedCapabilities
*out = make([]v1.Capability, len(*in))
for i := range *in {
(*out)[i] = v1.Capability((*in)[i])
}
} else {
out.AllowedCapabilities = nil
} }
if in.Volumes != nil { if in.Volumes != nil {
in, out := &in.Volumes, &out.Volumes in, out := &in.Volumes, &out.Volumes
...@@ -1687,6 +1774,13 @@ func autoConvert_extensions_PodSecurityPolicySpec_To_v1beta1_PodSecurityPolicySp ...@@ -1687,6 +1774,13 @@ func autoConvert_extensions_PodSecurityPolicySpec_To_v1beta1_PodSecurityPolicySp
if err := Convert_extensions_RunAsUserStrategyOptions_To_v1beta1_RunAsUserStrategyOptions(&in.RunAsUser, &out.RunAsUser, s); err != nil { if err := Convert_extensions_RunAsUserStrategyOptions_To_v1beta1_RunAsUserStrategyOptions(&in.RunAsUser, &out.RunAsUser, s); err != nil {
return err return err
} }
if err := Convert_extensions_SupplementalGroupsStrategyOptions_To_v1beta1_SupplementalGroupsStrategyOptions(&in.SupplementalGroups, &out.SupplementalGroups, s); err != nil {
return err
}
if err := Convert_extensions_FSGroupStrategyOptions_To_v1beta1_FSGroupStrategyOptions(&in.FSGroup, &out.FSGroup, s); err != nil {
return err
}
out.ReadOnlyRootFilesystem = in.ReadOnlyRootFilesystem
return nil return nil
} }
...@@ -1987,6 +2081,46 @@ func Convert_extensions_ScaleSpec_To_v1beta1_ScaleSpec(in *extensions.ScaleSpec, ...@@ -1987,6 +2081,46 @@ func Convert_extensions_ScaleSpec_To_v1beta1_ScaleSpec(in *extensions.ScaleSpec,
return autoConvert_extensions_ScaleSpec_To_v1beta1_ScaleSpec(in, out, s) return autoConvert_extensions_ScaleSpec_To_v1beta1_ScaleSpec(in, out, s)
} }
func autoConvert_v1beta1_SupplementalGroupsStrategyOptions_To_extensions_SupplementalGroupsStrategyOptions(in *SupplementalGroupsStrategyOptions, out *extensions.SupplementalGroupsStrategyOptions, s conversion.Scope) error {
out.Rule = extensions.SupplementalGroupsStrategyType(in.Rule)
if in.Ranges != nil {
in, out := &in.Ranges, &out.Ranges
*out = make([]extensions.IDRange, len(*in))
for i := range *in {
if err := Convert_v1beta1_IDRange_To_extensions_IDRange(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.Ranges = nil
}
return nil
}
func Convert_v1beta1_SupplementalGroupsStrategyOptions_To_extensions_SupplementalGroupsStrategyOptions(in *SupplementalGroupsStrategyOptions, out *extensions.SupplementalGroupsStrategyOptions, s conversion.Scope) error {
return autoConvert_v1beta1_SupplementalGroupsStrategyOptions_To_extensions_SupplementalGroupsStrategyOptions(in, out, s)
}
func autoConvert_extensions_SupplementalGroupsStrategyOptions_To_v1beta1_SupplementalGroupsStrategyOptions(in *extensions.SupplementalGroupsStrategyOptions, out *SupplementalGroupsStrategyOptions, s conversion.Scope) error {
out.Rule = SupplementalGroupsStrategyType(in.Rule)
if in.Ranges != nil {
in, out := &in.Ranges, &out.Ranges
*out = make([]IDRange, len(*in))
for i := range *in {
if err := Convert_extensions_IDRange_To_v1beta1_IDRange(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.Ranges = nil
}
return nil
}
func Convert_extensions_SupplementalGroupsStrategyOptions_To_v1beta1_SupplementalGroupsStrategyOptions(in *extensions.SupplementalGroupsStrategyOptions, out *SupplementalGroupsStrategyOptions, s conversion.Scope) error {
return autoConvert_extensions_SupplementalGroupsStrategyOptions_To_v1beta1_SupplementalGroupsStrategyOptions(in, out, s)
}
func autoConvert_v1beta1_ThirdPartyResource_To_extensions_ThirdPartyResource(in *ThirdPartyResource, out *extensions.ThirdPartyResource, s conversion.Scope) error { func autoConvert_v1beta1_ThirdPartyResource_To_extensions_ThirdPartyResource(in *ThirdPartyResource, out *extensions.ThirdPartyResource, s conversion.Scope) error {
if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
return err return err
......
...@@ -48,6 +48,7 @@ func init() { ...@@ -48,6 +48,7 @@ func init() {
DeepCopy_v1beta1_DeploymentStatus, DeepCopy_v1beta1_DeploymentStatus,
DeepCopy_v1beta1_DeploymentStrategy, DeepCopy_v1beta1_DeploymentStrategy,
DeepCopy_v1beta1_ExportOptions, DeepCopy_v1beta1_ExportOptions,
DeepCopy_v1beta1_FSGroupStrategyOptions,
DeepCopy_v1beta1_HTTPIngressPath, DeepCopy_v1beta1_HTTPIngressPath,
DeepCopy_v1beta1_HTTPIngressRuleValue, DeepCopy_v1beta1_HTTPIngressRuleValue,
DeepCopy_v1beta1_HorizontalPodAutoscaler, DeepCopy_v1beta1_HorizontalPodAutoscaler,
...@@ -88,6 +89,7 @@ func init() { ...@@ -88,6 +89,7 @@ func init() {
DeepCopy_v1beta1_ScaleSpec, DeepCopy_v1beta1_ScaleSpec,
DeepCopy_v1beta1_ScaleStatus, DeepCopy_v1beta1_ScaleStatus,
DeepCopy_v1beta1_SubresourceReference, DeepCopy_v1beta1_SubresourceReference,
DeepCopy_v1beta1_SupplementalGroupsStrategyOptions,
DeepCopy_v1beta1_ThirdPartyResource, DeepCopy_v1beta1_ThirdPartyResource,
DeepCopy_v1beta1_ThirdPartyResourceData, DeepCopy_v1beta1_ThirdPartyResourceData,
DeepCopy_v1beta1_ThirdPartyResourceDataList, DeepCopy_v1beta1_ThirdPartyResourceDataList,
...@@ -347,6 +349,22 @@ func DeepCopy_v1beta1_ExportOptions(in ExportOptions, out *ExportOptions, c *con ...@@ -347,6 +349,22 @@ func DeepCopy_v1beta1_ExportOptions(in ExportOptions, out *ExportOptions, c *con
return nil return nil
} }
func DeepCopy_v1beta1_FSGroupStrategyOptions(in FSGroupStrategyOptions, out *FSGroupStrategyOptions, c *conversion.Cloner) error {
out.Rule = in.Rule
if in.Ranges != nil {
in, out := in.Ranges, &out.Ranges
*out = make([]IDRange, len(in))
for i := range in {
if err := DeepCopy_v1beta1_IDRange(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Ranges = nil
}
return nil
}
func DeepCopy_v1beta1_HTTPIngressPath(in HTTPIngressPath, out *HTTPIngressPath, c *conversion.Cloner) error { func DeepCopy_v1beta1_HTTPIngressPath(in HTTPIngressPath, out *HTTPIngressPath, c *conversion.Cloner) error {
out.Path = in.Path out.Path = in.Path
if err := DeepCopy_v1beta1_IngressBackend(in.Backend, &out.Backend, c); err != nil { if err := DeepCopy_v1beta1_IngressBackend(in.Backend, &out.Backend, c); err != nil {
...@@ -814,14 +832,32 @@ func DeepCopy_v1beta1_PodSecurityPolicyList(in PodSecurityPolicyList, out *PodSe ...@@ -814,14 +832,32 @@ func DeepCopy_v1beta1_PodSecurityPolicyList(in PodSecurityPolicyList, out *PodSe
func DeepCopy_v1beta1_PodSecurityPolicySpec(in PodSecurityPolicySpec, out *PodSecurityPolicySpec, c *conversion.Cloner) error { func DeepCopy_v1beta1_PodSecurityPolicySpec(in PodSecurityPolicySpec, out *PodSecurityPolicySpec, c *conversion.Cloner) error {
out.Privileged = in.Privileged out.Privileged = in.Privileged
if in.Capabilities != nil { if in.DefaultAddCapabilities != nil {
in, out := in.Capabilities, &out.Capabilities in, out := in.DefaultAddCapabilities, &out.DefaultAddCapabilities
*out = make([]v1.Capability, len(in))
for i := range in {
(*out)[i] = in[i]
}
} else {
out.DefaultAddCapabilities = nil
}
if in.RequiredDropCapabilities != nil {
in, out := in.RequiredDropCapabilities, &out.RequiredDropCapabilities
*out = make([]v1.Capability, len(in))
for i := range in {
(*out)[i] = in[i]
}
} else {
out.RequiredDropCapabilities = nil
}
if in.AllowedCapabilities != nil {
in, out := in.AllowedCapabilities, &out.AllowedCapabilities
*out = make([]v1.Capability, len(in)) *out = make([]v1.Capability, len(in))
for i := range in { for i := range in {
(*out)[i] = in[i] (*out)[i] = in[i]
} }
} else { } else {
out.Capabilities = nil out.AllowedCapabilities = nil
} }
if in.Volumes != nil { if in.Volumes != nil {
in, out := in.Volumes, &out.Volumes in, out := in.Volumes, &out.Volumes
...@@ -852,6 +888,13 @@ func DeepCopy_v1beta1_PodSecurityPolicySpec(in PodSecurityPolicySpec, out *PodSe ...@@ -852,6 +888,13 @@ func DeepCopy_v1beta1_PodSecurityPolicySpec(in PodSecurityPolicySpec, out *PodSe
if err := DeepCopy_v1beta1_RunAsUserStrategyOptions(in.RunAsUser, &out.RunAsUser, c); err != nil { if err := DeepCopy_v1beta1_RunAsUserStrategyOptions(in.RunAsUser, &out.RunAsUser, c); err != nil {
return err return err
} }
if err := DeepCopy_v1beta1_SupplementalGroupsStrategyOptions(in.SupplementalGroups, &out.SupplementalGroups, c); err != nil {
return err
}
if err := DeepCopy_v1beta1_FSGroupStrategyOptions(in.FSGroup, &out.FSGroup, c); err != nil {
return err
}
out.ReadOnlyRootFilesystem = in.ReadOnlyRootFilesystem
return nil return nil
} }
...@@ -1030,6 +1073,22 @@ func DeepCopy_v1beta1_SubresourceReference(in SubresourceReference, out *Subreso ...@@ -1030,6 +1073,22 @@ func DeepCopy_v1beta1_SubresourceReference(in SubresourceReference, out *Subreso
return nil return nil
} }
func DeepCopy_v1beta1_SupplementalGroupsStrategyOptions(in SupplementalGroupsStrategyOptions, out *SupplementalGroupsStrategyOptions, c *conversion.Cloner) error {
out.Rule = in.Rule
if in.Ranges != nil {
in, out := in.Ranges, &out.Ranges
*out = make([]IDRange, len(in))
for i := range in {
if err := DeepCopy_v1beta1_IDRange(in[i], &(*out)[i], c); err != nil {
return err
}
}
} else {
out.Ranges = nil
}
return nil
}
func DeepCopy_v1beta1_ThirdPartyResource(in ThirdPartyResource, out *ThirdPartyResource, c *conversion.Cloner) error { func DeepCopy_v1beta1_ThirdPartyResource(in ThirdPartyResource, out *ThirdPartyResource, c *conversion.Cloner) error {
if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { if err := unversioned.DeepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err return err
......
...@@ -235,6 +235,16 @@ message ExportOptions { ...@@ -235,6 +235,16 @@ message ExportOptions {
optional bool exact = 2; optional bool exact = 2;
} }
// FSGroupStrategyOptions defines the strategy type and options used to create the strategy.
message FSGroupStrategyOptions {
// Rule is the strategy that will dictate what FSGroup is used in the SecurityContext.
optional string rule = 1;
// Ranges are the allowed ranges of fs groups. If you would like to force a single
// fs group then supply a single range with the same start and end.
repeated IDRange ranges = 2;
}
// HTTPIngressPath associates a path regex with a backend. Incoming urls matching // HTTPIngressPath associates a path regex with a backend. Incoming urls matching
// the path are forwarded to the backend. // the path are forwarded to the backend.
message HTTPIngressPath { message HTTPIngressPath {
...@@ -641,30 +651,54 @@ message PodSecurityPolicySpec { ...@@ -641,30 +651,54 @@ message PodSecurityPolicySpec {
// privileged determines if a pod can request to be run as privileged. // privileged determines if a pod can request to be run as privileged.
optional bool privileged = 1; optional bool privileged = 1;
// capabilities is a list of capabilities that can be added. // DefaultAddCapabilities is the default set of capabilities that will be added to the container
repeated string capabilities = 2; // unless the pod spec specifically drops the capability. You may not list a capabiility in both
// DefaultAddCapabilities and RequiredDropCapabilities.
repeated string defaultAddCapabilities = 2;
// RequiredDropCapabilities are the capabilities that will be dropped from the container. These
// are required to be dropped and cannot be added.
repeated string requiredDropCapabilities = 3;
// AllowedCapabilities is a list of capabilities that can be requested to add to the container.
// Capabilities in this field may be added at the pod author's discretion.
// You must not list a capability in both AllowedCapabilities and RequiredDropCapabilities.
repeated string allowedCapabilities = 4;
// volumes is a white list of allowed volume plugins. Empty indicates that all plugins // volumes is a white list of allowed volume plugins. Empty indicates that all plugins
// may be used. // may be used.
repeated string volumes = 3; repeated string volumes = 5;
// hostNetwork determines if the policy allows the use of HostNetwork in the pod spec. // hostNetwork determines if the policy allows the use of HostNetwork in the pod spec.
optional bool hostNetwork = 4; optional bool hostNetwork = 6;
// hostPorts determines which host port ranges are allowed to be exposed. // hostPorts determines which host port ranges are allowed to be exposed.
repeated HostPortRange hostPorts = 5; repeated HostPortRange hostPorts = 7;
// hostPID determines if the policy allows the use of HostPID in the pod spec. // hostPID determines if the policy allows the use of HostPID in the pod spec.
optional bool hostPID = 6; optional bool hostPID = 8;
// hostIPC determines if the policy allows the use of HostIPC in the pod spec. // hostIPC determines if the policy allows the use of HostIPC in the pod spec.
optional bool hostIPC = 7; optional bool hostIPC = 9;
// seLinux is the strategy that will dictate the allowable labels that may be set. // seLinux is the strategy that will dictate the allowable labels that may be set.
optional SELinuxStrategyOptions seLinux = 8; optional SELinuxStrategyOptions seLinux = 10;
// runAsUser is the strategy that will dictate the allowable RunAsUser values that may be set. // runAsUser is the strategy that will dictate the allowable RunAsUser values that may be set.
optional RunAsUserStrategyOptions runAsUser = 9; optional RunAsUserStrategyOptions runAsUser = 11;
// SupplementalGroups is the strategy that will dictate what supplemental groups are used by the SecurityContext.
optional SupplementalGroupsStrategyOptions supplementalGroups = 12;
// FSGroup is the strategy that will dictate what fs group is used by the SecurityContext.
optional FSGroupStrategyOptions fsGroup = 13;
// ReadOnlyRootFilesystem when set to true will force containers to run with a read only root file
// system. If the container specifically requests to run with a non-read only root file system
// the PSP should deny the pod.
// If set to false the container may run with a read only root file system if it wishes but it
// will not be forced to.
optional bool readOnlyRootFilesystem = 14;
} }
// ReplicaSet represents the configuration of a ReplicaSet. // ReplicaSet represents the configuration of a ReplicaSet.
...@@ -836,6 +870,16 @@ message SubresourceReference { ...@@ -836,6 +870,16 @@ message SubresourceReference {
optional string subresource = 4; optional string subresource = 4;
} }
// SupplementalGroupsStrategyOptions defines the strategy type and options used to create the strategy.
message SupplementalGroupsStrategyOptions {
// Rule is the strategy that will dictate what supplemental groups is used in the SecurityContext.
optional string rule = 1;
// Ranges are the allowed ranges of supplemental groups. If you would like to force a single
// supplemental group then supply a single range with the same start and end.
repeated IDRange ranges = 2;
}
// A ThirdPartyResource is a generic representation of a resource, it is used by add-ons and plugins to add new resource // A ThirdPartyResource is a generic representation of a resource, it is used by add-ons and plugins to add new resource
// types to the API. It consists of one or more Versions of the api. // types to the API. It consists of one or more Versions of the api.
message ThirdPartyResource { message ThirdPartyResource {
......
...@@ -912,6 +912,8 @@ type ReplicaSetStatus struct { ...@@ -912,6 +912,8 @@ type ReplicaSetStatus struct {
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,3,opt,name=observedGeneration"` ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,3,opt,name=observedGeneration"`
} }
// +genclient=true,nonNamespaced=true
// Pod Security Policy governs the ability to make requests that affect the Security Context // Pod Security Policy governs the ability to make requests that affect the Security Context
// that will be applied to a pod and container. // that will be applied to a pod and container.
type PodSecurityPolicy struct { type PodSecurityPolicy struct {
...@@ -928,29 +930,51 @@ type PodSecurityPolicy struct { ...@@ -928,29 +930,51 @@ type PodSecurityPolicy struct {
type PodSecurityPolicySpec struct { type PodSecurityPolicySpec struct {
// privileged determines if a pod can request to be run as privileged. // privileged determines if a pod can request to be run as privileged.
Privileged bool `json:"privileged,omitempty" protobuf:"varint,1,opt,name=privileged"` Privileged bool `json:"privileged,omitempty" protobuf:"varint,1,opt,name=privileged"`
// capabilities is a list of capabilities that can be added. // DefaultAddCapabilities is the default set of capabilities that will be added to the container
Capabilities []v1.Capability `json:"capabilities,omitempty" protobuf:"bytes,2,rep,name=capabilities,casttype=k8s.io/kubernetes/pkg/api/v1.Capability"` // unless the pod spec specifically drops the capability. You may not list a capabiility in both
// DefaultAddCapabilities and RequiredDropCapabilities.
DefaultAddCapabilities []v1.Capability `json:"defaultAddCapabilities,omitempty" protobuf:"bytes,2,rep,name=defaultAddCapabilities,casttype=k8s.io/kubernetes/pkg/api/v1.Capability"`
// RequiredDropCapabilities are the capabilities that will be dropped from the container. These
// are required to be dropped and cannot be added.
RequiredDropCapabilities []v1.Capability `json:"requiredDropCapabilities,omitempty" protobuf:"bytes,3,rep,name=requiredDropCapabilities,casttype=k8s.io/kubernetes/pkg/api/v1.Capability"`
// AllowedCapabilities is a list of capabilities that can be requested to add to the container.
// Capabilities in this field may be added at the pod author's discretion.
// You must not list a capability in both AllowedCapabilities and RequiredDropCapabilities.
AllowedCapabilities []v1.Capability `json:"allowedCapabilities,omitempty" protobuf:"bytes,4,rep,name=allowedCapabilities,casttype=k8s.io/kubernetes/pkg/api/v1.Capability"`
// volumes is a white list of allowed volume plugins. Empty indicates that all plugins // volumes is a white list of allowed volume plugins. Empty indicates that all plugins
// may be used. // may be used.
Volumes []FSType `json:"volumes,omitempty" protobuf:"bytes,3,rep,name=volumes,casttype=FSType"` Volumes []FSType `json:"volumes,omitempty" protobuf:"bytes,5,rep,name=volumes,casttype=FSType"`
// hostNetwork determines if the policy allows the use of HostNetwork in the pod spec. // hostNetwork determines if the policy allows the use of HostNetwork in the pod spec.
HostNetwork bool `json:"hostNetwork,omitempty" protobuf:"varint,4,opt,name=hostNetwork"` HostNetwork bool `json:"hostNetwork,omitempty" protobuf:"varint,6,opt,name=hostNetwork"`
// hostPorts determines which host port ranges are allowed to be exposed. // hostPorts determines which host port ranges are allowed to be exposed.
HostPorts []HostPortRange `json:"hostPorts,omitempty" protobuf:"bytes,5,rep,name=hostPorts"` HostPorts []HostPortRange `json:"hostPorts,omitempty" protobuf:"bytes,7,rep,name=hostPorts"`
// hostPID determines if the policy allows the use of HostPID in the pod spec. // hostPID determines if the policy allows the use of HostPID in the pod spec.
HostPID bool `json:"hostPID,omitempty" protobuf:"varint,6,opt,name=hostPID"` HostPID bool `json:"hostPID,omitempty" protobuf:"varint,8,opt,name=hostPID"`
// hostIPC determines if the policy allows the use of HostIPC in the pod spec. // hostIPC determines if the policy allows the use of HostIPC in the pod spec.
HostIPC bool `json:"hostIPC,omitempty" protobuf:"varint,7,opt,name=hostIPC"` HostIPC bool `json:"hostIPC,omitempty" protobuf:"varint,9,opt,name=hostIPC"`
// seLinux is the strategy that will dictate the allowable labels that may be set. // seLinux is the strategy that will dictate the allowable labels that may be set.
SELinux SELinuxStrategyOptions `json:"seLinux,omitempty" protobuf:"bytes,8,opt,name=seLinux"` SELinux SELinuxStrategyOptions `json:"seLinux" protobuf:"bytes,10,opt,name=seLinux"`
// runAsUser is the strategy that will dictate the allowable RunAsUser values that may be set. // runAsUser is the strategy that will dictate the allowable RunAsUser values that may be set.
RunAsUser RunAsUserStrategyOptions `json:"runAsUser,omitempty" protobuf:"bytes,9,opt,name=runAsUser"` RunAsUser RunAsUserStrategyOptions `json:"runAsUser" protobuf:"bytes,11,opt,name=runAsUser"`
// SupplementalGroups is the strategy that will dictate what supplemental groups are used by the SecurityContext.
SupplementalGroups SupplementalGroupsStrategyOptions `json:"supplementalGroups" protobuf:"bytes,12,opt,name=supplementalGroups"`
// FSGroup is the strategy that will dictate what fs group is used by the SecurityContext.
FSGroup FSGroupStrategyOptions `json:"fsGroup" protobuf:"bytes,13,opt,name=fsGroup"`
// ReadOnlyRootFilesystem when set to true will force containers to run with a read only root file
// system. If the container specifically requests to run with a non-read only root file system
// the PSP should deny the pod.
// If set to false the container may run with a read only root file system if it wishes but it
// will not be forced to.
ReadOnlyRootFilesystem bool `json:"readOnlyRootFilesystem,omitempty" protobuf:"varint,14,opt,name=readOnlyRootFilesystem"`
} }
// FS Type gives strong typing to different file systems that are used by volumes. // FS Type gives strong typing to different file systems that are used by volumes.
type FSType string type FSType string
var ( var (
AzureFile FSType = "azureFile"
Flocker FSType = "flocker"
FlexVolume FSType = "flexVolume"
HostPath FSType = "hostPath" HostPath FSType = "hostPath"
EmptyDir FSType = "emptyDir" EmptyDir FSType = "emptyDir"
GCEPersistentDisk FSType = "gcePersistentDisk" GCEPersistentDisk FSType = "gcePersistentDisk"
...@@ -966,6 +990,8 @@ var ( ...@@ -966,6 +990,8 @@ var (
CephFS FSType = "cephFS" CephFS FSType = "cephFS"
DownwardAPI FSType = "downwardAPI" DownwardAPI FSType = "downwardAPI"
FC FSType = "fc" FC FSType = "fc"
ConfigMap FSType = "configMap"
All FSType = "*"
) )
// Host Port Range defines a range of host ports that will be enabled by a policy // Host Port Range defines a range of host ports that will be enabled by a policy
...@@ -1026,6 +1052,46 @@ const ( ...@@ -1026,6 +1052,46 @@ const (
RunAsUserStrategyRunAsAny RunAsUserStrategy = "RunAsAny" RunAsUserStrategyRunAsAny RunAsUserStrategy = "RunAsAny"
) )
// FSGroupStrategyOptions defines the strategy type and options used to create the strategy.
type FSGroupStrategyOptions struct {
// Rule is the strategy that will dictate what FSGroup is used in the SecurityContext.
Rule FSGroupStrategyType `json:"rule,omitempty" protobuf:"bytes,1,opt,name=rule,casttype=FSGroupStrategyType"`
// Ranges are the allowed ranges of fs groups. If you would like to force a single
// fs group then supply a single range with the same start and end.
Ranges []IDRange `json:"ranges,omitempty" protobuf:"bytes,2,rep,name=ranges"`
}
// FSGroupStrategyType denotes strategy types for generating FSGroup values for a
// SecurityContext
type FSGroupStrategyType string
const (
// container must have FSGroup of X applied.
FSGroupStrategyMustRunAs FSGroupStrategyType = "MustRunAs"
// container may make requests for any FSGroup labels.
FSGroupStrategyRunAsAny FSGroupStrategyType = "RunAsAny"
)
// SupplementalGroupsStrategyOptions defines the strategy type and options used to create the strategy.
type SupplementalGroupsStrategyOptions struct {
// Rule is the strategy that will dictate what supplemental groups is used in the SecurityContext.
Rule SupplementalGroupsStrategyType `json:"rule,omitempty" protobuf:"bytes,1,opt,name=rule,casttype=SupplementalGroupsStrategyType"`
// Ranges are the allowed ranges of supplemental groups. If you would like to force a single
// supplemental group then supply a single range with the same start and end.
Ranges []IDRange `json:"ranges,omitempty" protobuf:"bytes,2,rep,name=ranges"`
}
// SupplementalGroupsStrategyType denotes strategy types for determining valid supplemental
// groups for a SecurityContext.
type SupplementalGroupsStrategyType string
const (
// container must run as a particular gid.
SupplementalGroupsStrategyMustRunAs SupplementalGroupsStrategyType = "MustRunAs"
// container may make requests for any gid.
SupplementalGroupsStrategyRunAsAny SupplementalGroupsStrategyType = "RunAsAny"
)
// Pod Security Policy List is a list of PodSecurityPolicy objects. // Pod Security Policy List is a list of PodSecurityPolicy objects.
type PodSecurityPolicyList struct { type PodSecurityPolicyList struct {
unversioned.TypeMeta `json:",inline"` unversioned.TypeMeta `json:",inline"`
......
...@@ -186,6 +186,16 @@ func (ExportOptions) SwaggerDoc() map[string]string { ...@@ -186,6 +186,16 @@ func (ExportOptions) SwaggerDoc() map[string]string {
return map_ExportOptions return map_ExportOptions
} }
var map_FSGroupStrategyOptions = map[string]string{
"": "FSGroupStrategyOptions defines the strategy type and options used to create the strategy.",
"rule": "Rule is the strategy that will dictate what FSGroup is used in the SecurityContext.",
"ranges": "Ranges are the allowed ranges of fs groups. If you would like to force a single fs group then supply a single range with the same start and end.",
}
func (FSGroupStrategyOptions) SwaggerDoc() map[string]string {
return map_FSGroupStrategyOptions
}
var map_HTTPIngressPath = map[string]string{ var map_HTTPIngressPath = map[string]string{
"": "HTTPIngressPath associates a path regex with a backend. Incoming urls matching the path are forwarded to the backend.", "": "HTTPIngressPath associates a path regex with a backend. Incoming urls matching the path are forwarded to the backend.",
"path": "Path is a extended POSIX regex as defined by IEEE Std 1003.1, (i.e this follows the egrep/unix syntax, not the perl syntax) matched against the path of an incoming request. Currently it can contain characters disallowed from the conventional \"path\" part of a URL as defined by RFC 3986. Paths must begin with a '/'. If unspecified, the path defaults to a catch all sending traffic to the backend.", "path": "Path is a extended POSIX regex as defined by IEEE Std 1003.1, (i.e this follows the egrep/unix syntax, not the perl syntax) matched against the path of an incoming request. Currently it can contain characters disallowed from the conventional \"path\" part of a URL as defined by RFC 3986. Paths must begin with a '/'. If unspecified, the path defaults to a catch all sending traffic to the backend.",
...@@ -469,7 +479,9 @@ func (PodSecurityPolicyList) SwaggerDoc() map[string]string { ...@@ -469,7 +479,9 @@ func (PodSecurityPolicyList) SwaggerDoc() map[string]string {
var map_PodSecurityPolicySpec = map[string]string{ var map_PodSecurityPolicySpec = map[string]string{
"": "Pod Security Policy Spec defines the policy enforced.", "": "Pod Security Policy Spec defines the policy enforced.",
"privileged": "privileged determines if a pod can request to be run as privileged.", "privileged": "privileged determines if a pod can request to be run as privileged.",
"capabilities": "capabilities is a list of capabilities that can be added.", "defaultAddCapabilities": "DefaultAddCapabilities is the default set of capabilities that will be added to the container unless the pod spec specifically drops the capability. You may not list a capabiility in both DefaultAddCapabilities and RequiredDropCapabilities.",
"requiredDropCapabilities": "RequiredDropCapabilities are the capabilities that will be dropped from the container. These are required to be dropped and cannot be added.",
"allowedCapabilities": "AllowedCapabilities is a list of capabilities that can be requested to add to the container. Capabilities in this field may be added at the pod author's discretion. You must not list a capability in both AllowedCapabilities and RequiredDropCapabilities.",
"volumes": "volumes is a white list of allowed volume plugins. Empty indicates that all plugins may be used.", "volumes": "volumes is a white list of allowed volume plugins. Empty indicates that all plugins may be used.",
"hostNetwork": "hostNetwork determines if the policy allows the use of HostNetwork in the pod spec.", "hostNetwork": "hostNetwork determines if the policy allows the use of HostNetwork in the pod spec.",
"hostPorts": "hostPorts determines which host port ranges are allowed to be exposed.", "hostPorts": "hostPorts determines which host port ranges are allowed to be exposed.",
...@@ -477,6 +489,9 @@ var map_PodSecurityPolicySpec = map[string]string{ ...@@ -477,6 +489,9 @@ var map_PodSecurityPolicySpec = map[string]string{
"hostIPC": "hostIPC determines if the policy allows the use of HostIPC in the pod spec.", "hostIPC": "hostIPC determines if the policy allows the use of HostIPC in the pod spec.",
"seLinux": "seLinux is the strategy that will dictate the allowable labels that may be set.", "seLinux": "seLinux is the strategy that will dictate the allowable labels that may be set.",
"runAsUser": "runAsUser is the strategy that will dictate the allowable RunAsUser values that may be set.", "runAsUser": "runAsUser is the strategy that will dictate the allowable RunAsUser values that may be set.",
"supplementalGroups": "SupplementalGroups is the strategy that will dictate what supplemental groups are used by the SecurityContext.",
"fsGroup": "FSGroup is the strategy that will dictate what fs group is used by the SecurityContext.",
"readOnlyRootFilesystem": "ReadOnlyRootFilesystem when set to true will force containers to run with a read only root file system. If the container specifically requests to run with a non-read only root file system the PSP should deny the pod. If set to false the container may run with a read only root file system if it wishes but it will not be forced to.",
} }
func (PodSecurityPolicySpec) SwaggerDoc() map[string]string { func (PodSecurityPolicySpec) SwaggerDoc() map[string]string {
...@@ -615,6 +630,16 @@ func (SubresourceReference) SwaggerDoc() map[string]string { ...@@ -615,6 +630,16 @@ func (SubresourceReference) SwaggerDoc() map[string]string {
return map_SubresourceReference return map_SubresourceReference
} }
var map_SupplementalGroupsStrategyOptions = map[string]string{
"": "SupplementalGroupsStrategyOptions defines the strategy type and options used to create the strategy.",
"rule": "Rule is the strategy that will dictate what supplemental groups is used in the SecurityContext.",
"ranges": "Ranges are the allowed ranges of supplemental groups. If you would like to force a single supplemental group then supply a single range with the same start and end.",
}
func (SupplementalGroupsStrategyOptions) SwaggerDoc() map[string]string {
return map_SupplementalGroupsStrategyOptions
}
var map_ThirdPartyResource = map[string]string{ var map_ThirdPartyResource = map[string]string{
"": "A ThirdPartyResource is a generic representation of a resource, it is used by add-ons and plugins to add new resource types to the API. It consists of one or more Versions of the api.", "": "A ThirdPartyResource is a generic representation of a resource, it is used by add-ons and plugins to add new resource types to the API. It consists of one or more Versions of the api.",
"metadata": "Standard object metadata", "metadata": "Standard object metadata",
......
...@@ -17,6 +17,7 @@ limitations under the License. ...@@ -17,6 +17,7 @@ limitations under the License.
package validation package validation
import ( import (
"fmt"
"net" "net"
"regexp" "regexp"
"strconv" "strconv"
...@@ -28,6 +29,7 @@ import ( ...@@ -28,6 +29,7 @@ import (
apivalidation "k8s.io/kubernetes/pkg/api/validation" apivalidation "k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/labels"
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
"k8s.io/kubernetes/pkg/util/intstr" "k8s.io/kubernetes/pkg/util/intstr"
"k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/util/validation" "k8s.io/kubernetes/pkg/util/validation"
...@@ -529,7 +531,11 @@ func ValidatePodSecurityPolicySpec(spec *extensions.PodSecurityPolicySpec, fldPa ...@@ -529,7 +531,11 @@ func ValidatePodSecurityPolicySpec(spec *extensions.PodSecurityPolicySpec, fldPa
allErrs = append(allErrs, validatePSPRunAsUser(fldPath.Child("runAsUser"), &spec.RunAsUser)...) allErrs = append(allErrs, validatePSPRunAsUser(fldPath.Child("runAsUser"), &spec.RunAsUser)...)
allErrs = append(allErrs, validatePSPSELinux(fldPath.Child("seLinux"), &spec.SELinux)...) allErrs = append(allErrs, validatePSPSELinux(fldPath.Child("seLinux"), &spec.SELinux)...)
allErrs = append(allErrs, validatePSPSupplementalGroup(fldPath.Child("supplementalGroups"), &spec.SupplementalGroups)...)
allErrs = append(allErrs, validatePSPFSGroup(fldPath.Child("fsGroup"), &spec.FSGroup)...)
allErrs = append(allErrs, validatePodSecurityPolicyVolumes(fldPath, spec.Volumes)...) allErrs = append(allErrs, validatePodSecurityPolicyVolumes(fldPath, spec.Volumes)...)
allErrs = append(allErrs, validatePSPCapsAgainstDrops(spec.RequiredDropCapabilities, spec.DefaultAddCapabilities, field.NewPath("defaultAddCapabilities"))...)
allErrs = append(allErrs, validatePSPCapsAgainstDrops(spec.RequiredDropCapabilities, spec.AllowedCapabilities, field.NewPath("allowedCapabilities"))...)
return allErrs return allErrs
} }
...@@ -568,24 +574,48 @@ func validatePSPRunAsUser(fldPath *field.Path, runAsUser *extensions.RunAsUserSt ...@@ -568,24 +574,48 @@ func validatePSPRunAsUser(fldPath *field.Path, runAsUser *extensions.RunAsUserSt
return allErrs return allErrs
} }
// validatePSPFSGroup validates the FSGroupStrategyOptions fields of the PodSecurityPolicy.
func validatePSPFSGroup(fldPath *field.Path, groupOptions *extensions.FSGroupStrategyOptions) field.ErrorList {
allErrs := field.ErrorList{}
supportedRules := sets.NewString(
string(extensions.FSGroupStrategyMustRunAs),
string(extensions.FSGroupStrategyRunAsAny),
)
if !supportedRules.Has(string(groupOptions.Rule)) {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), groupOptions.Rule, supportedRules.List()))
}
for idx, rng := range groupOptions.Ranges {
allErrs = append(allErrs, validateIDRanges(fldPath.Child("ranges").Index(idx), rng)...)
}
return allErrs
}
// validatePSPSupplementalGroup validates the SupplementalGroupsStrategyOptions fields of the PodSecurityPolicy.
func validatePSPSupplementalGroup(fldPath *field.Path, groupOptions *extensions.SupplementalGroupsStrategyOptions) field.ErrorList {
allErrs := field.ErrorList{}
supportedRules := sets.NewString(
string(extensions.SupplementalGroupsStrategyRunAsAny),
string(extensions.SupplementalGroupsStrategyMustRunAs),
)
if !supportedRules.Has(string(groupOptions.Rule)) {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), groupOptions.Rule, supportedRules.List()))
}
for idx, rng := range groupOptions.Ranges {
allErrs = append(allErrs, validateIDRanges(fldPath.Child("ranges").Index(idx), rng)...)
}
return allErrs
}
// validatePodSecurityPolicyVolumes validates the volume fields of PodSecurityPolicy. // validatePodSecurityPolicyVolumes validates the volume fields of PodSecurityPolicy.
func validatePodSecurityPolicyVolumes(fldPath *field.Path, volumes []extensions.FSType) field.ErrorList { func validatePodSecurityPolicyVolumes(fldPath *field.Path, volumes []extensions.FSType) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
allowed := sets.NewString(string(extensions.HostPath), allowed := psputil.GetAllFSTypesAsSet()
string(extensions.EmptyDir), // add in the * value since that is a pseudo type that is not included by default
string(extensions.GCEPersistentDisk), allowed.Insert(string(extensions.All))
string(extensions.AWSElasticBlockStore),
string(extensions.GitRepo),
string(extensions.Secret),
string(extensions.NFS),
string(extensions.ISCSI),
string(extensions.Glusterfs),
string(extensions.PersistentVolumeClaim),
string(extensions.RBD),
string(extensions.Cinder),
string(extensions.CephFS),
string(extensions.DownwardAPI),
string(extensions.FC))
for _, v := range volumes { for _, v := range volumes {
if !allowed.Has(string(v)) { if !allowed.Has(string(v)) {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumes"), v, allowed.List())) allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumes"), v, allowed.List()))
...@@ -614,6 +644,31 @@ func validateIDRanges(fldPath *field.Path, rng extensions.IDRange) field.ErrorLi ...@@ -614,6 +644,31 @@ func validateIDRanges(fldPath *field.Path, rng extensions.IDRange) field.ErrorLi
return allErrs return allErrs
} }
// validatePSPCapsAgainstDrops ensures an allowed cap is not listed in the required drops.
func validatePSPCapsAgainstDrops(requiredDrops []api.Capability, capsToCheck []api.Capability, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if requiredDrops == nil {
return allErrs
}
for _, cap := range capsToCheck {
if hasCap(cap, requiredDrops) {
allErrs = append(allErrs, field.Invalid(fldPath, cap,
fmt.Sprintf("capability is listed in %s and requiredDropCapabilities", fldPath.String())))
}
}
return allErrs
}
// hasCap checks for needle in haystack.
func hasCap(needle api.Capability, haystack []api.Capability) bool {
for _, c := range haystack {
if needle == c {
return true
}
}
return false
}
// ValidatePodSecurityPolicyUpdate validates a PSP for updates. // ValidatePodSecurityPolicyUpdate validates a PSP for updates.
func ValidatePodSecurityPolicyUpdate(old *extensions.PodSecurityPolicy, new *extensions.PodSecurityPolicy) field.ErrorList { func ValidatePodSecurityPolicyUpdate(old *extensions.PodSecurityPolicy, new *extensions.PodSecurityPolicy) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
......
...@@ -27,6 +27,7 @@ type ExtensionsInterface interface { ...@@ -27,6 +27,7 @@ type ExtensionsInterface interface {
DaemonSetsGetter DaemonSetsGetter
DeploymentsGetter DeploymentsGetter
IngressesGetter IngressesGetter
PodSecurityPoliciesGetter
ReplicaSetsGetter ReplicaSetsGetter
ScalesGetter ScalesGetter
ThirdPartyResourcesGetter ThirdPartyResourcesGetter
...@@ -49,6 +50,10 @@ func (c *ExtensionsClient) Ingresses(namespace string) IngressInterface { ...@@ -49,6 +50,10 @@ func (c *ExtensionsClient) Ingresses(namespace string) IngressInterface {
return newIngresses(c, namespace) return newIngresses(c, namespace)
} }
func (c *ExtensionsClient) PodSecurityPolicies() PodSecurityPolicyInterface {
return newPodSecurityPolicies(c)
}
func (c *ExtensionsClient) ReplicaSets(namespace string) ReplicaSetInterface { func (c *ExtensionsClient) ReplicaSets(namespace string) ReplicaSetInterface {
return newReplicaSets(c, namespace) return newReplicaSets(c, namespace)
} }
......
...@@ -38,6 +38,10 @@ func (c *FakeExtensions) Ingresses(namespace string) unversioned.IngressInterfac ...@@ -38,6 +38,10 @@ func (c *FakeExtensions) Ingresses(namespace string) unversioned.IngressInterfac
return &FakeIngresses{c, namespace} return &FakeIngresses{c, namespace}
} }
func (c *FakeExtensions) PodSecurityPolicies() unversioned.PodSecurityPolicyInterface {
return &FakePodSecurityPolicies{c}
}
func (c *FakeExtensions) ReplicaSets(namespace string) unversioned.ReplicaSetInterface { func (c *FakeExtensions) ReplicaSets(namespace string) unversioned.ReplicaSetInterface {
return &FakeReplicaSets{c, namespace} return &FakeReplicaSets{c, namespace}
} }
......
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fake
import (
api "k8s.io/kubernetes/pkg/api"
unversioned "k8s.io/kubernetes/pkg/api/unversioned"
extensions "k8s.io/kubernetes/pkg/apis/extensions"
core "k8s.io/kubernetes/pkg/client/testing/core"
labels "k8s.io/kubernetes/pkg/labels"
watch "k8s.io/kubernetes/pkg/watch"
)
// FakePodSecurityPolicies implements PodSecurityPolicyInterface
type FakePodSecurityPolicies struct {
Fake *FakeExtensions
}
var podsecuritypoliciesResource = unversioned.GroupVersionResource{Group: "extensions", Version: "", Resource: "podsecuritypolicies"}
func (c *FakePodSecurityPolicies) Create(podSecurityPolicy *extensions.PodSecurityPolicy) (result *extensions.PodSecurityPolicy, err error) {
obj, err := c.Fake.
Invokes(core.NewRootCreateAction(podsecuritypoliciesResource, podSecurityPolicy), &extensions.PodSecurityPolicy{})
if obj == nil {
return nil, err
}
return obj.(*extensions.PodSecurityPolicy), err
}
func (c *FakePodSecurityPolicies) Update(podSecurityPolicy *extensions.PodSecurityPolicy) (result *extensions.PodSecurityPolicy, err error) {
obj, err := c.Fake.
Invokes(core.NewRootUpdateAction(podsecuritypoliciesResource, podSecurityPolicy), &extensions.PodSecurityPolicy{})
if obj == nil {
return nil, err
}
return obj.(*extensions.PodSecurityPolicy), err
}
func (c *FakePodSecurityPolicies) Delete(name string, options *api.DeleteOptions) error {
_, err := c.Fake.
Invokes(core.NewRootDeleteAction(podsecuritypoliciesResource, name), &extensions.PodSecurityPolicy{})
return err
}
func (c *FakePodSecurityPolicies) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
action := core.NewRootDeleteCollectionAction(podsecuritypoliciesResource, listOptions)
_, err := c.Fake.Invokes(action, &extensions.PodSecurityPolicyList{})
return err
}
func (c *FakePodSecurityPolicies) Get(name string) (result *extensions.PodSecurityPolicy, err error) {
obj, err := c.Fake.
Invokes(core.NewRootGetAction(podsecuritypoliciesResource, name), &extensions.PodSecurityPolicy{})
if obj == nil {
return nil, err
}
return obj.(*extensions.PodSecurityPolicy), err
}
func (c *FakePodSecurityPolicies) List(opts api.ListOptions) (result *extensions.PodSecurityPolicyList, err error) {
obj, err := c.Fake.
Invokes(core.NewRootListAction(podsecuritypoliciesResource, opts), &extensions.PodSecurityPolicyList{})
if obj == nil {
return nil, err
}
label := opts.LabelSelector
if label == nil {
label = labels.Everything()
}
list := &extensions.PodSecurityPolicyList{}
for _, item := range obj.(*extensions.PodSecurityPolicyList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested podSecurityPolicies.
func (c *FakePodSecurityPolicies) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(core.NewRootWatchAction(podsecuritypoliciesResource, opts))
}
...@@ -24,6 +24,8 @@ type IngressExpansion interface{} ...@@ -24,6 +24,8 @@ type IngressExpansion interface{}
type JobExpansion interface{} type JobExpansion interface{}
type PodSecurityPolicyExpansion interface{}
type ThirdPartyResourceExpansion interface{} type ThirdPartyResourceExpansion interface{}
type ReplicaSetExpansion interface{} type ReplicaSetExpansion interface{}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package unversioned
import (
api "k8s.io/kubernetes/pkg/api"
extensions "k8s.io/kubernetes/pkg/apis/extensions"
watch "k8s.io/kubernetes/pkg/watch"
)
// PodSecurityPoliciesGetter has a method to return a PodSecurityPolicyInterface.
// A group's client should implement this interface.
type PodSecurityPoliciesGetter interface {
PodSecurityPolicies() PodSecurityPolicyInterface
}
// PodSecurityPolicyInterface has methods to work with PodSecurityPolicy resources.
type PodSecurityPolicyInterface interface {
Create(*extensions.PodSecurityPolicy) (*extensions.PodSecurityPolicy, error)
Update(*extensions.PodSecurityPolicy) (*extensions.PodSecurityPolicy, error)
Delete(name string, options *api.DeleteOptions) error
DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error
Get(name string) (*extensions.PodSecurityPolicy, error)
List(opts api.ListOptions) (*extensions.PodSecurityPolicyList, error)
Watch(opts api.ListOptions) (watch.Interface, error)
PodSecurityPolicyExpansion
}
// podSecurityPolicies implements PodSecurityPolicyInterface
type podSecurityPolicies struct {
client *ExtensionsClient
}
// newPodSecurityPolicies returns a PodSecurityPolicies
func newPodSecurityPolicies(c *ExtensionsClient) *podSecurityPolicies {
return &podSecurityPolicies{
client: c,
}
}
// Create takes the representation of a podSecurityPolicy and creates it. Returns the server's representation of the podSecurityPolicy, and an error, if there is any.
func (c *podSecurityPolicies) Create(podSecurityPolicy *extensions.PodSecurityPolicy) (result *extensions.PodSecurityPolicy, err error) {
result = &extensions.PodSecurityPolicy{}
err = c.client.Post().
Resource("podsecuritypolicies").
Body(podSecurityPolicy).
Do().
Into(result)
return
}
// Update takes the representation of a podSecurityPolicy and updates it. Returns the server's representation of the podSecurityPolicy, and an error, if there is any.
func (c *podSecurityPolicies) Update(podSecurityPolicy *extensions.PodSecurityPolicy) (result *extensions.PodSecurityPolicy, err error) {
result = &extensions.PodSecurityPolicy{}
err = c.client.Put().
Resource("podsecuritypolicies").
Name(podSecurityPolicy.Name).
Body(podSecurityPolicy).
Do().
Into(result)
return
}
// Delete takes name of the podSecurityPolicy and deletes it. Returns an error if one occurs.
func (c *podSecurityPolicies) Delete(name string, options *api.DeleteOptions) error {
return c.client.Delete().
Resource("podsecuritypolicies").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *podSecurityPolicies) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
return c.client.Delete().
Resource("podsecuritypolicies").
VersionedParams(&listOptions, api.ParameterCodec).
Body(options).
Do().
Error()
}
// Get takes name of the podSecurityPolicy, and returns the corresponding podSecurityPolicy object, and an error if there is any.
func (c *podSecurityPolicies) Get(name string) (result *extensions.PodSecurityPolicy, err error) {
result = &extensions.PodSecurityPolicy{}
err = c.client.Get().
Resource("podsecuritypolicies").
Name(name).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of PodSecurityPolicies that match those selectors.
func (c *podSecurityPolicies) List(opts api.ListOptions) (result *extensions.PodSecurityPolicyList, err error) {
result = &extensions.PodSecurityPolicyList{}
err = c.client.Get().
Resource("podsecuritypolicies").
VersionedParams(&opts, api.ParameterCodec).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested podSecurityPolicies.
func (c *podSecurityPolicies) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.client.Get().
Prefix("watch").
Resource("podsecuritypolicies").
VersionedParams(&opts, api.ParameterCodec).
Watch()
}
...@@ -27,3 +27,5 @@ type JobExpansion interface{} ...@@ -27,3 +27,5 @@ type JobExpansion interface{}
type ThirdPartyResourceExpansion interface{} type ThirdPartyResourceExpansion interface{}
type ReplicaSetExpansion interface{} type ReplicaSetExpansion interface{}
type PodSecurityPolicyExpansion interface{}
...@@ -28,7 +28,7 @@ type PodSecurityPoliciesInterface interface { ...@@ -28,7 +28,7 @@ type PodSecurityPoliciesInterface interface {
type PodSecurityPolicyInterface interface { type PodSecurityPolicyInterface interface {
Get(name string) (result *extensions.PodSecurityPolicy, err error) Get(name string) (result *extensions.PodSecurityPolicy, err error)
Create(scc *extensions.PodSecurityPolicy) (*extensions.PodSecurityPolicy, error) Create(psp *extensions.PodSecurityPolicy) (*extensions.PodSecurityPolicy, error)
List(opts api.ListOptions) (*extensions.PodSecurityPolicyList, error) List(opts api.ListOptions) (*extensions.PodSecurityPolicyList, error)
Delete(name string) error Delete(name string) error
Update(*extensions.PodSecurityPolicy) (*extensions.PodSecurityPolicy, error) Update(*extensions.PodSecurityPolicy) (*extensions.PodSecurityPolicy, error)
...@@ -45,11 +45,11 @@ func newPodSecurityPolicy(c *ExtensionsClient) *podSecurityPolicy { ...@@ -45,11 +45,11 @@ func newPodSecurityPolicy(c *ExtensionsClient) *podSecurityPolicy {
return &podSecurityPolicy{c} return &podSecurityPolicy{c}
} }
func (s *podSecurityPolicy) Create(scc *extensions.PodSecurityPolicy) (*extensions.PodSecurityPolicy, error) { func (s *podSecurityPolicy) Create(psp *extensions.PodSecurityPolicy) (*extensions.PodSecurityPolicy, error) {
result := &extensions.PodSecurityPolicy{} result := &extensions.PodSecurityPolicy{}
err := s.client.Post(). err := s.client.Post().
Resource("podsecuritypolicies"). Resource("podsecuritypolicies").
Body(scc). Body(psp).
Do(). Do().
Into(result) Into(result)
......
...@@ -29,7 +29,7 @@ import ( ...@@ -29,7 +29,7 @@ import (
func TestPodSecurityPolicyCreate(t *testing.T) { func TestPodSecurityPolicyCreate(t *testing.T) {
ns := api.NamespaceNone ns := api.NamespaceNone
scc := &extensions.PodSecurityPolicy{ psp := &extensions.PodSecurityPolicy{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "abc", Name: "abc",
}, },
...@@ -40,18 +40,18 @@ func TestPodSecurityPolicyCreate(t *testing.T) { ...@@ -40,18 +40,18 @@ func TestPodSecurityPolicyCreate(t *testing.T) {
Method: "POST", Method: "POST",
Path: testapi.Extensions.ResourcePath(getPSPResourcename(), ns, ""), Path: testapi.Extensions.ResourcePath(getPSPResourcename(), ns, ""),
Query: simple.BuildQueryValues(nil), Query: simple.BuildQueryValues(nil),
Body: scc, Body: psp,
}, },
Response: simple.Response{StatusCode: 200, Body: scc}, Response: simple.Response{StatusCode: 200, Body: psp},
} }
response, err := c.Setup(t).PodSecurityPolicies().Create(scc) response, err := c.Setup(t).PodSecurityPolicies().Create(psp)
c.Validate(t, response, err) c.Validate(t, response, err)
} }
func TestPodSecurityPolicyGet(t *testing.T) { func TestPodSecurityPolicyGet(t *testing.T) {
ns := api.NamespaceNone ns := api.NamespaceNone
scc := &extensions.PodSecurityPolicy{ psp := &extensions.PodSecurityPolicy{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "abc", Name: "abc",
}, },
...@@ -63,7 +63,7 @@ func TestPodSecurityPolicyGet(t *testing.T) { ...@@ -63,7 +63,7 @@ func TestPodSecurityPolicyGet(t *testing.T) {
Query: simple.BuildQueryValues(nil), Query: simple.BuildQueryValues(nil),
Body: nil, Body: nil,
}, },
Response: simple.Response{StatusCode: 200, Body: scc}, Response: simple.Response{StatusCode: 200, Body: psp},
} }
response, err := c.Setup(t).PodSecurityPolicies().Get("abc") response, err := c.Setup(t).PodSecurityPolicies().Get("abc")
...@@ -72,7 +72,7 @@ func TestPodSecurityPolicyGet(t *testing.T) { ...@@ -72,7 +72,7 @@ func TestPodSecurityPolicyGet(t *testing.T) {
func TestPodSecurityPolicyList(t *testing.T) { func TestPodSecurityPolicyList(t *testing.T) {
ns := api.NamespaceNone ns := api.NamespaceNone
sccList := &extensions.PodSecurityPolicyList{ pspList := &extensions.PodSecurityPolicyList{
Items: []extensions.PodSecurityPolicy{ Items: []extensions.PodSecurityPolicy{
{ {
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
...@@ -88,7 +88,7 @@ func TestPodSecurityPolicyList(t *testing.T) { ...@@ -88,7 +88,7 @@ func TestPodSecurityPolicyList(t *testing.T) {
Query: simple.BuildQueryValues(nil), Query: simple.BuildQueryValues(nil),
Body: nil, Body: nil,
}, },
Response: simple.Response{StatusCode: 200, Body: sccList}, Response: simple.Response{StatusCode: 200, Body: pspList},
} }
response, err := c.Setup(t).PodSecurityPolicies().List(api.ListOptions{}) response, err := c.Setup(t).PodSecurityPolicies().List(api.ListOptions{})
c.Validate(t, response, err) c.Validate(t, response, err)
...@@ -96,7 +96,7 @@ func TestPodSecurityPolicyList(t *testing.T) { ...@@ -96,7 +96,7 @@ func TestPodSecurityPolicyList(t *testing.T) {
func TestPodSecurityPolicyUpdate(t *testing.T) { func TestPodSecurityPolicyUpdate(t *testing.T) {
ns := api.NamespaceNone ns := api.NamespaceNone
scc := &extensions.PodSecurityPolicy{ psp := &extensions.PodSecurityPolicy{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "abc", Name: "abc",
ResourceVersion: "1", ResourceVersion: "1",
...@@ -104,9 +104,9 @@ func TestPodSecurityPolicyUpdate(t *testing.T) { ...@@ -104,9 +104,9 @@ func TestPodSecurityPolicyUpdate(t *testing.T) {
} }
c := &simple.Client{ c := &simple.Client{
Request: simple.Request{Method: "PUT", Path: testapi.Extensions.ResourcePath(getPSPResourcename(), ns, "abc"), Query: simple.BuildQueryValues(nil)}, Request: simple.Request{Method: "PUT", Path: testapi.Extensions.ResourcePath(getPSPResourcename(), ns, "abc"), Query: simple.BuildQueryValues(nil)},
Response: simple.Response{StatusCode: 200, Body: scc}, Response: simple.Response{StatusCode: 200, Body: psp},
} }
response, err := c.Setup(t).PodSecurityPolicies().Update(scc) response, err := c.Setup(t).PodSecurityPolicies().Update(psp)
c.Validate(t, response, err) c.Validate(t, response, err)
} }
......
...@@ -47,16 +47,16 @@ func (c *FakePodSecurityPolicy) Get(name string) (*extensions.PodSecurityPolicy, ...@@ -47,16 +47,16 @@ func (c *FakePodSecurityPolicy) Get(name string) (*extensions.PodSecurityPolicy,
return obj.(*extensions.PodSecurityPolicy), err return obj.(*extensions.PodSecurityPolicy), err
} }
func (c *FakePodSecurityPolicy) Create(scc *extensions.PodSecurityPolicy) (*extensions.PodSecurityPolicy, error) { func (c *FakePodSecurityPolicy) Create(psp *extensions.PodSecurityPolicy) (*extensions.PodSecurityPolicy, error) {
obj, err := c.Fake.Invokes(NewCreateAction("podsecuritypolicies", c.Namespace, scc), &extensions.PodSecurityPolicy{}) obj, err := c.Fake.Invokes(NewCreateAction("podsecuritypolicies", c.Namespace, psp), &extensions.PodSecurityPolicy{})
if obj == nil { if obj == nil {
return nil, err return nil, err
} }
return obj.(*extensions.PodSecurityPolicy), err return obj.(*extensions.PodSecurityPolicy), err
} }
func (c *FakePodSecurityPolicy) Update(scc *extensions.PodSecurityPolicy) (*extensions.PodSecurityPolicy, error) { func (c *FakePodSecurityPolicy) Update(psp *extensions.PodSecurityPolicy) (*extensions.PodSecurityPolicy, error) {
obj, err := c.Fake.Invokes(NewUpdateAction("podsecuritypolicies", c.Namespace, scc), &extensions.PodSecurityPolicy{}) obj, err := c.Fake.Invokes(NewUpdateAction("podsecuritypolicies", c.Namespace, psp), &extensions.PodSecurityPolicy{})
if obj == nil { if obj == nil {
return nil, err return nil, err
} }
......
...@@ -1689,9 +1689,9 @@ func printConfigMapList(list *api.ConfigMapList, w io.Writer, options PrintOptio ...@@ -1689,9 +1689,9 @@ func printConfigMapList(list *api.ConfigMapList, w io.Writer, options PrintOptio
} }
func printPodSecurityPolicy(item *extensions.PodSecurityPolicy, w io.Writer, options PrintOptions) error { func printPodSecurityPolicy(item *extensions.PodSecurityPolicy, w io.Writer, options PrintOptions) error {
_, err := fmt.Fprintf(w, "%s\t%t\t%v\t%v\t%s\t%s\n", item.Name, item.Spec.Privileged, _, err := fmt.Fprintf(w, "%s\t%t\t%v\t%s\t%s\t%s\t%s\t%t\t%v\n", item.Name, item.Spec.Privileged,
item.Spec.Capabilities, item.Spec.Volumes, item.Spec.SELinux.Rule, item.Spec.AllowedCapabilities, item.Spec.SELinux.Rule,
item.Spec.RunAsUser.Rule) item.Spec.RunAsUser.Rule, item.Spec.FSGroup.Rule, item.Spec.SupplementalGroups.Rule, item.Spec.ReadOnlyRootFilesystem, item.Spec.Volumes)
return err return err
} }
......
...@@ -49,6 +49,12 @@ func validNewPodSecurityPolicy() *extensions.PodSecurityPolicy { ...@@ -49,6 +49,12 @@ func validNewPodSecurityPolicy() *extensions.PodSecurityPolicy {
RunAsUser: extensions.RunAsUserStrategyOptions{ RunAsUser: extensions.RunAsUserStrategyOptions{
Rule: extensions.RunAsUserStrategyRunAsAny, Rule: extensions.RunAsUserStrategyRunAsAny,
}, },
FSGroup: extensions.FSGroupStrategyOptions{
Rule: extensions.FSGroupStrategyRunAsAny,
},
SupplementalGroups: extensions.SupplementalGroupsStrategyOptions{
Rule: extensions.SupplementalGroupsStrategyRunAsAny,
},
}, },
} }
} }
...@@ -57,11 +63,11 @@ func TestCreate(t *testing.T) { ...@@ -57,11 +63,11 @@ func TestCreate(t *testing.T) {
storage, server := newStorage(t) storage, server := newStorage(t)
defer server.Terminate(t) defer server.Terminate(t)
test := registrytest.New(t, storage.Store).ClusterScope() test := registrytest.New(t, storage.Store).ClusterScope()
scc := validNewPodSecurityPolicy() psp := validNewPodSecurityPolicy()
scc.ObjectMeta = api.ObjectMeta{GenerateName: "foo-"} psp.ObjectMeta = api.ObjectMeta{GenerateName: "foo-"}
test.TestCreate( test.TestCreate(
// valid // valid
scc, psp,
// invalid // invalid
&extensions.PodSecurityPolicy{ &extensions.PodSecurityPolicy{
ObjectMeta: api.ObjectMeta{Name: "name with spaces"}, ObjectMeta: api.ObjectMeta{Name: "name with spaces"},
......
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package security contains security apis and implementations.
package security
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package capabilities
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// defaultCapabilities implements the CapabilitiesStrategy interface
type defaultCapabilities struct {
defaultAddCapabilities []api.Capability
requiredDropCapabilities []api.Capability
allowedCaps []api.Capability
}
var _ CapabilitiesStrategy = &defaultCapabilities{}
// NewDefaultCapabilities creates a new defaultCapabilities strategy that will provide defaults and validation
// based on the configured initial caps and allowed caps.
func NewDefaultCapabilities(defaultAddCapabilities, requiredDropCapabilities, allowedCaps []api.Capability) (CapabilitiesStrategy, error) {
return &defaultCapabilities{
defaultAddCapabilities: defaultAddCapabilities,
requiredDropCapabilities: requiredDropCapabilities,
allowedCaps: allowedCaps,
}, nil
}
// Generate creates the capabilities based on policy rules. Generate will produce the following:
// 1. a capabilities.Add set containing all the required adds (unless the
// container specifically is dropping the cap) and container requested adds
// 2. a capabilities.Drop set containing all the required drops and container requested drops
func (s *defaultCapabilities) Generate(pod *api.Pod, container *api.Container) (*api.Capabilities, error) {
defaultAdd := makeCapSet(s.defaultAddCapabilities)
requiredDrop := makeCapSet(s.requiredDropCapabilities)
containerAdd := sets.NewString()
containerDrop := sets.NewString()
if container.SecurityContext != nil && container.SecurityContext.Capabilities != nil {
containerAdd = makeCapSet(container.SecurityContext.Capabilities.Add)
containerDrop = makeCapSet(container.SecurityContext.Capabilities.Drop)
}
// remove any default adds that the container is specifically dropping
defaultAdd = defaultAdd.Difference(containerDrop)
combinedAdd := defaultAdd.Union(containerAdd).List()
combinedDrop := requiredDrop.Union(containerDrop).List()
// nothing generated? return nil
if len(combinedAdd) == 0 && len(combinedDrop) == 0 {
return nil, nil
}
return &api.Capabilities{
Add: capabilityFromStringSlice(combinedAdd),
Drop: capabilityFromStringSlice(combinedDrop),
}, nil
}
// Validate ensures that the specified values fall within the range of the strategy.
func (s *defaultCapabilities) Validate(pod *api.Pod, container *api.Container) field.ErrorList {
allErrs := field.ErrorList{}
// if the security context isn't set then we haven't generated correctly. Shouldn't get here
// if using the provider correctly
if container.SecurityContext == nil {
allErrs = append(allErrs, field.Invalid(field.NewPath("securityContext"), container.SecurityContext, "no security context is set"))
return allErrs
}
if container.SecurityContext.Capabilities == nil {
// if container.SC.Caps is nil then nothing was defaulted by the strat or requested by the pod author
// if there are no required caps on the strategy and nothing is requested on the pod
// then we can safely return here without further validation.
if len(s.defaultAddCapabilities) == 0 && len(s.requiredDropCapabilities) == 0 {
return allErrs
}
// container has no requested caps but we have required caps. We should have something in
// at least the drops on the container.
allErrs = append(allErrs, field.Invalid(field.NewPath("capabilities"), container.SecurityContext.Capabilities,
"required capabilities are not set on the securityContext"))
return allErrs
}
// validate that anything being added is in the default or allowed sets
defaultAdd := makeCapSet(s.defaultAddCapabilities)
allowedAdd := makeCapSet(s.allowedCaps)
for _, cap := range container.SecurityContext.Capabilities.Add {
sCap := string(cap)
if !defaultAdd.Has(sCap) && !allowedAdd.Has(sCap) {
allErrs = append(allErrs, field.Invalid(field.NewPath("capabilities", "add"), sCap, "capability may not be added"))
}
}
// validate that anything that is required to be dropped is in the drop set
containerDrops := makeCapSet(container.SecurityContext.Capabilities.Drop)
for _, requiredDrop := range s.requiredDropCapabilities {
sDrop := string(requiredDrop)
if !containerDrops.Has(sDrop) {
allErrs = append(allErrs, field.Invalid(field.NewPath("capabilities", "drop"), container.SecurityContext.Capabilities.Drop,
fmt.Sprintf("%s is required to be dropped but was not found", sDrop)))
}
}
return allErrs
}
// capabilityFromStringSlice creates a capability slice from a string slice.
func capabilityFromStringSlice(slice []string) []api.Capability {
if len(slice) == 0 {
return nil
}
caps := []api.Capability{}
for _, c := range slice {
caps = append(caps, api.Capability(c))
}
return caps
}
// makeCapSet makes a string set from capabilities and normalizes them to be all lower case to help
// with comparisons.
func makeCapSet(caps []api.Capability) sets.String {
s := sets.NewString()
for _, c := range caps {
s.Insert(string(c))
}
return s
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package capabilities
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// CapabilitiesStrategy defines the interface for all cap constraint strategies.
type CapabilitiesStrategy interface {
// Generate creates the capabilities based on policy rules.
Generate(pod *api.Pod, container *api.Container) (*api.Capabilities, error)
// Validate ensures that the specified values fall within the range of the strategy.
Validate(pod *api.Pod, container *api.Container) field.ErrorList
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package podsecuritypolicy
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/group"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/user"
"k8s.io/kubernetes/pkg/util/errors"
)
type simpleStrategyFactory struct{}
var _ StrategyFactory = &simpleStrategyFactory{}
func NewSimpleStrategyFactory() StrategyFactory {
return &simpleStrategyFactory{}
}
func (f *simpleStrategyFactory) CreateStrategies(psp *extensions.PodSecurityPolicy, namespace string) (*ProviderStrategies, error) {
errs := []error{}
userStrat, err := createUserStrategy(&psp.Spec.RunAsUser)
if err != nil {
errs = append(errs, err)
}
seLinuxStrat, err := createSELinuxStrategy(&psp.Spec.SELinux)
if err != nil {
errs = append(errs, err)
}
fsGroupStrat, err := createFSGroupStrategy(&psp.Spec.FSGroup)
if err != nil {
errs = append(errs, err)
}
supGroupStrat, err := createSupplementalGroupStrategy(&psp.Spec.SupplementalGroups)
if err != nil {
errs = append(errs, err)
}
capStrat, err := createCapabilitiesStrategy(psp.Spec.DefaultAddCapabilities, psp.Spec.RequiredDropCapabilities, psp.Spec.AllowedCapabilities)
if err != nil {
errs = append(errs, err)
}
if len(errs) > 0 {
return nil, errors.NewAggregate(errs)
}
strategies := &ProviderStrategies{
RunAsUserStrategy: userStrat,
SELinuxStrategy: seLinuxStrat,
FSGroupStrategy: fsGroupStrat,
SupplementalGroupStrategy: supGroupStrat,
CapabilitiesStrategy: capStrat,
}
return strategies, nil
}
// createUserStrategy creates a new user strategy.
func createUserStrategy(opts *extensions.RunAsUserStrategyOptions) (user.RunAsUserStrategy, error) {
switch opts.Rule {
case extensions.RunAsUserStrategyMustRunAs:
return user.NewMustRunAs(opts)
case extensions.RunAsUserStrategyMustRunAsNonRoot:
return user.NewRunAsNonRoot(opts)
case extensions.RunAsUserStrategyRunAsAny:
return user.NewRunAsAny(opts)
default:
return nil, fmt.Errorf("Unrecognized RunAsUser strategy type %s", opts.Rule)
}
}
// createSELinuxStrategy creates a new selinux strategy.
func createSELinuxStrategy(opts *extensions.SELinuxStrategyOptions) (selinux.SELinuxStrategy, error) {
switch opts.Rule {
case extensions.SELinuxStrategyMustRunAs:
return selinux.NewMustRunAs(opts)
case extensions.SELinuxStrategyRunAsAny:
return selinux.NewRunAsAny(opts)
default:
return nil, fmt.Errorf("Unrecognized SELinuxContext strategy type %s", opts.Rule)
}
}
// createFSGroupStrategy creates a new fsgroup strategy
func createFSGroupStrategy(opts *extensions.FSGroupStrategyOptions) (group.GroupStrategy, error) {
switch opts.Rule {
case extensions.FSGroupStrategyRunAsAny:
return group.NewRunAsAny()
case extensions.FSGroupStrategyMustRunAs:
return group.NewMustRunAs(opts.Ranges, fsGroupField)
default:
return nil, fmt.Errorf("Unrecognized FSGroup strategy type %s", opts.Rule)
}
}
// createSupplementalGroupStrategy creates a new supplemental group strategy
func createSupplementalGroupStrategy(opts *extensions.SupplementalGroupsStrategyOptions) (group.GroupStrategy, error) {
switch opts.Rule {
case extensions.SupplementalGroupsStrategyRunAsAny:
return group.NewRunAsAny()
case extensions.SupplementalGroupsStrategyMustRunAs:
return group.NewMustRunAs(opts.Ranges, supplementalGroupsField)
default:
return nil, fmt.Errorf("Unrecognized SupplementalGroups strategy type %s", opts.Rule)
}
}
// createCapabilitiesStrategy creates a new capabilities strategy.
func createCapabilitiesStrategy(defaultAddCaps, requiredDropCaps, allowedCaps []api.Capability) (capabilities.CapabilitiesStrategy, error) {
return capabilities.NewDefaultCapabilities(defaultAddCaps, requiredDropCaps, allowedCaps)
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package group
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// mustRunAs implements the GroupStrategy interface
type mustRunAs struct {
ranges []extensions.IDRange
field string
}
var _ GroupStrategy = &mustRunAs{}
// NewMustRunAs provides a new MustRunAs strategy based on ranges.
func NewMustRunAs(ranges []extensions.IDRange, field string) (GroupStrategy, error) {
if len(ranges) == 0 {
return nil, fmt.Errorf("ranges must be supplied for MustRunAs")
}
return &mustRunAs{
ranges: ranges,
field: field,
}, nil
}
// Generate creates the group based on policy rules. By default this returns the first group of the
// first range (min val).
func (s *mustRunAs) Generate(pod *api.Pod) ([]int64, error) {
return []int64{s.ranges[0].Min}, nil
}
// Generate a single value to be applied. This is used for FSGroup. This strategy will return
// the first group of the first range (min val).
func (s *mustRunAs) GenerateSingle(pod *api.Pod) (*int64, error) {
single := new(int64)
*single = s.ranges[0].Min
return single, nil
}
// Validate ensures that the specified values fall within the range of the strategy.
// Groups are passed in here to allow this strategy to support multiple group fields (fsgroup and
// supplemental groups).
func (s *mustRunAs) Validate(pod *api.Pod, groups []int64) field.ErrorList {
allErrs := field.ErrorList{}
if pod.Spec.SecurityContext == nil {
allErrs = append(allErrs, field.Invalid(field.NewPath("securityContext"), pod.Spec.SecurityContext, "unable to validate nil security context"))
return allErrs
}
if len(groups) == 0 && len(s.ranges) > 0 {
allErrs = append(allErrs, field.Invalid(field.NewPath(s.field), groups, "unable to validate empty groups against required ranges"))
}
for _, group := range groups {
if !s.isGroupValid(group) {
detail := fmt.Sprintf("%d is not an allowed group", group)
allErrs = append(allErrs, field.Invalid(field.NewPath(s.field), groups, detail))
}
}
return allErrs
}
func (s *mustRunAs) isGroupValid(group int64) bool {
for _, rng := range s.ranges {
if psputil.FallsInRange(group, rng) {
return true
}
}
return false
}
/*
Copyright 2014 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package group
import (
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestMustRunAsOptions(t *testing.T) {
tests := map[string]struct {
ranges []extensions.IDRange
pass bool
}{
"empty": {
ranges: []extensions.IDRange{},
},
"ranges": {
ranges: []extensions.IDRange{
{Min: 1, Max: 1},
},
pass: true,
},
}
for k, v := range tests {
_, err := NewMustRunAs(v.ranges, "")
if v.pass && err != nil {
t.Errorf("error creating strategy for %s: %v", k, err)
}
if !v.pass && err == nil {
t.Errorf("expected error for %s but got none", k)
}
}
}
func TestGenerate(t *testing.T) {
tests := map[string]struct {
ranges []extensions.IDRange
expected []int64
}{
"multi value": {
ranges: []extensions.IDRange{
{Min: 1, Max: 2},
},
expected: []int64{1},
},
"single value": {
ranges: []extensions.IDRange{
{Min: 1, Max: 1},
},
expected: []int64{1},
},
"multi range": {
ranges: []extensions.IDRange{
{Min: 1, Max: 1},
{Min: 2, Max: 500},
},
expected: []int64{1},
},
}
for k, v := range tests {
s, err := NewMustRunAs(v.ranges, "")
if err != nil {
t.Errorf("error creating strategy for %s: %v", k, err)
}
actual, err := s.Generate(nil)
if err != nil {
t.Errorf("unexpected error for %s: %v", k, err)
}
if len(actual) != len(v.expected) {
t.Errorf("unexpected generated values. Expected %v, got %v", v.expected, actual)
continue
}
if len(actual) > 0 && len(v.expected) > 0 {
if actual[0] != v.expected[0] {
t.Errorf("unexpected generated values. Expected %v, got %v", v.expected, actual)
}
}
single, err := s.GenerateSingle(nil)
if err != nil {
t.Errorf("unexpected error for %s: %v", k, err)
}
if single == nil {
t.Errorf("unexpected nil generated value for %s: %v", k, single)
}
if *single != v.expected[0] {
t.Errorf("unexpected generated single value. Expected %v, got %v", v.expected, actual)
}
}
}
func TestValidate(t *testing.T) {
validPod := func() *api.Pod {
return &api.Pod{
Spec: api.PodSpec{
SecurityContext: &api.PodSecurityContext{},
},
}
}
tests := map[string]struct {
ranges []extensions.IDRange
pod *api.Pod
groups []int64
pass bool
}{
"nil security context": {
pod: &api.Pod{},
ranges: []extensions.IDRange{
{Min: 1, Max: 3},
},
},
"empty groups": {
pod: validPod(),
ranges: []extensions.IDRange{
{Min: 1, Max: 3},
},
},
"not in range": {
pod: validPod(),
groups: []int64{5},
ranges: []extensions.IDRange{
{Min: 1, Max: 3},
{Min: 4, Max: 4},
},
},
"in range 1": {
pod: validPod(),
groups: []int64{2},
ranges: []extensions.IDRange{
{Min: 1, Max: 3},
},
pass: true,
},
"in range boundry min": {
pod: validPod(),
groups: []int64{1},
ranges: []extensions.IDRange{
{Min: 1, Max: 3},
},
pass: true,
},
"in range boundry max": {
pod: validPod(),
groups: []int64{3},
ranges: []extensions.IDRange{
{Min: 1, Max: 3},
},
pass: true,
},
"singular range": {
pod: validPod(),
groups: []int64{4},
ranges: []extensions.IDRange{
{Min: 4, Max: 4},
},
pass: true,
},
}
for k, v := range tests {
s, err := NewMustRunAs(v.ranges, "")
if err != nil {
t.Errorf("error creating strategy for %s: %v", k, err)
}
errs := s.Validate(v.pod, v.groups)
if v.pass && len(errs) > 0 {
t.Errorf("unexpected errors for %s: %v", k, errs)
}
if !v.pass && len(errs) == 0 {
t.Errorf("expected no errors for %s but got: %v", k, errs)
}
}
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package group
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// mustRunAs implements the GroupStrategy interface
type runAsAny struct {
}
var _ GroupStrategy = &runAsAny{}
// NewRunAsAny provides a new RunAsAny strategy.
func NewRunAsAny() (GroupStrategy, error) {
return &runAsAny{}, nil
}
// Generate creates the group based on policy rules. This strategy returns an empty slice.
func (s *runAsAny) Generate(pod *api.Pod) ([]int64, error) {
return []int64{}, nil
}
// Generate a single value to be applied. This is used for FSGroup. This strategy returns nil.
func (s *runAsAny) GenerateSingle(pod *api.Pod) (*int64, error) {
return nil, nil
}
// Validate ensures that the specified values fall within the range of the strategy.
func (s *runAsAny) Validate(pod *api.Pod, groups []int64) field.ErrorList {
return field.ErrorList{}
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package group
import (
"testing"
)
func TestRunAsAnyGenerate(t *testing.T) {
s, err := NewRunAsAny()
if err != nil {
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
}
groups, err := s.Generate(nil)
if len(groups) > 0 {
t.Errorf("expected empty but got %v", groups)
}
if err != nil {
t.Errorf("unexpected error generating groups: %v", err)
}
}
func TestRunAsAnyGenerateSingle(t *testing.T) {
s, err := NewRunAsAny()
if err != nil {
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
}
group, err := s.GenerateSingle(nil)
if group != nil {
t.Errorf("expected empty but got %v", group)
}
if err != nil {
t.Errorf("unexpected error generating groups: %v", err)
}
}
func TestRunAsAnyValidte(t *testing.T) {
s, err := NewRunAsAny()
if err != nil {
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
}
errs := s.Validate(nil, nil)
if len(errs) != 0 {
t.Errorf("unexpected errors: %v", errs)
}
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package group
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// GroupStrategy defines the interface for all group constraint strategies.
type GroupStrategy interface {
// Generate creates the group based on policy rules. The underlying implementation can
// decide whether it will return a full range of values or a subset of values from the
// configured ranges.
Generate(pod *api.Pod) ([]int64, error)
// Generate a single value to be applied. The underlying implementation decides which
// value to return if configured with multiple ranges. This is used for FSGroup.
GenerateSingle(pod *api.Pod) (*int64, error)
// Validate ensures that the specified values fall within the range of the strategy.
Validate(pod *api.Pod, groups []int64) field.ErrorList
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package selinux
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/util/validation/field"
)
type mustRunAs struct {
opts *extensions.SELinuxStrategyOptions
}
var _ SELinuxStrategy = &mustRunAs{}
func NewMustRunAs(options *extensions.SELinuxStrategyOptions) (SELinuxStrategy, error) {
if options == nil {
return nil, fmt.Errorf("MustRunAs requires SELinuxContextStrategyOptions")
}
if options.SELinuxOptions == nil {
return nil, fmt.Errorf("MustRunAs requires SELinuxOptions")
}
return &mustRunAs{
opts: options,
}, nil
}
// Generate creates the SELinuxOptions based on constraint rules.
func (s *mustRunAs) Generate(pod *api.Pod, container *api.Container) (*api.SELinuxOptions, error) {
return s.opts.SELinuxOptions, nil
}
// Validate ensures that the specified values fall within the range of the strategy.
func (s *mustRunAs) Validate(pod *api.Pod, container *api.Container) field.ErrorList {
allErrs := field.ErrorList{}
if container.SecurityContext == nil {
detail := fmt.Sprintf("unable to validate nil security context for %s", container.Name)
allErrs = append(allErrs, field.Invalid(field.NewPath("securityContext"), container.SecurityContext, detail))
return allErrs
}
if container.SecurityContext.SELinuxOptions == nil {
detail := fmt.Sprintf("unable to validate nil seLinuxOptions for %s", container.Name)
allErrs = append(allErrs, field.Invalid(field.NewPath("seLinuxOptions"), container.SecurityContext.SELinuxOptions, detail))
return allErrs
}
seLinuxOptionsPath := field.NewPath("seLinuxOptions")
seLinux := container.SecurityContext.SELinuxOptions
if seLinux.Level != s.opts.SELinuxOptions.Level {
detail := fmt.Sprintf("seLinuxOptions.level on %s does not match required level. Found %s, wanted %s", container.Name, seLinux.Level, s.opts.SELinuxOptions.Level)
allErrs = append(allErrs, field.Invalid(seLinuxOptionsPath.Child("level"), seLinux.Level, detail))
}
if seLinux.Role != s.opts.SELinuxOptions.Role {
detail := fmt.Sprintf("seLinuxOptions.role on %s does not match required role. Found %s, wanted %s", container.Name, seLinux.Role, s.opts.SELinuxOptions.Role)
allErrs = append(allErrs, field.Invalid(seLinuxOptionsPath.Child("role"), seLinux.Role, detail))
}
if seLinux.Type != s.opts.SELinuxOptions.Type {
detail := fmt.Sprintf("seLinuxOptions.type on %s does not match required type. Found %s, wanted %s", container.Name, seLinux.Type, s.opts.SELinuxOptions.Type)
allErrs = append(allErrs, field.Invalid(seLinuxOptionsPath.Child("type"), seLinux.Type, detail))
}
if seLinux.User != s.opts.SELinuxOptions.User {
detail := fmt.Sprintf("seLinuxOptions.user on %s does not match required user. Found %s, wanted %s", container.Name, seLinux.User, s.opts.SELinuxOptions.User)
allErrs = append(allErrs, field.Invalid(seLinuxOptionsPath.Child("user"), seLinux.User, detail))
}
return allErrs
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package selinux
import (
"reflect"
"strings"
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestMustRunAsOptions(t *testing.T) {
tests := map[string]struct {
opts *extensions.SELinuxStrategyOptions
pass bool
}{
"invalid opts": {
opts: &extensions.SELinuxStrategyOptions{},
pass: false,
},
"valid opts": {
opts: &extensions.SELinuxStrategyOptions{SELinuxOptions: &api.SELinuxOptions{}},
pass: true,
},
}
for name, tc := range tests {
_, err := NewMustRunAs(tc.opts)
if err != nil && tc.pass {
t.Errorf("%s expected to pass but received error %#v", name, err)
}
if err == nil && !tc.pass {
t.Errorf("%s expected to fail but did not receive an error", name)
}
}
}
func TestMustRunAsGenerate(t *testing.T) {
opts := &extensions.SELinuxStrategyOptions{
SELinuxOptions: &api.SELinuxOptions{
User: "user",
Role: "role",
Type: "type",
Level: "level",
},
}
mustRunAs, err := NewMustRunAs(opts)
if err != nil {
t.Fatalf("unexpected error initializing NewMustRunAs %v", err)
}
generated, err := mustRunAs.Generate(nil, nil)
if err != nil {
t.Fatalf("unexpected error generating selinux %v", err)
}
if !reflect.DeepEqual(generated, opts.SELinuxOptions) {
t.Errorf("generated selinux does not equal configured selinux")
}
}
func TestMustRunAsValidate(t *testing.T) {
newValidOpts := func() *api.SELinuxOptions {
return &api.SELinuxOptions{
User: "user",
Role: "role",
Level: "level",
Type: "type",
}
}
role := newValidOpts()
role.Role = "invalid"
user := newValidOpts()
user.User = "invalid"
level := newValidOpts()
level.Level = "invalid"
seType := newValidOpts()
seType.Type = "invalid"
tests := map[string]struct {
seLinux *api.SELinuxOptions
expectedMsg string
}{
"invalid role": {
seLinux: role,
expectedMsg: "does not match required role",
},
"invalid user": {
seLinux: user,
expectedMsg: "does not match required user",
},
"invalid level": {
seLinux: level,
expectedMsg: "does not match required level",
},
"invalid type": {
seLinux: seType,
expectedMsg: "does not match required type",
},
"valid": {
seLinux: newValidOpts(),
expectedMsg: "",
},
}
opts := &extensions.SELinuxStrategyOptions{
SELinuxOptions: newValidOpts(),
}
for name, tc := range tests {
mustRunAs, err := NewMustRunAs(opts)
if err != nil {
t.Errorf("unexpected error initializing NewMustRunAs for testcase %s: %#v", name, err)
continue
}
container := &api.Container{
SecurityContext: &api.SecurityContext{
SELinuxOptions: tc.seLinux,
},
}
errs := mustRunAs.Validate(nil, container)
//should've passed but didn't
if len(tc.expectedMsg) == 0 && len(errs) > 0 {
t.Errorf("%s expected no errors but received %v", name, errs)
}
//should've failed but didn't
if len(tc.expectedMsg) != 0 && len(errs) == 0 {
t.Errorf("%s expected error %s but received no errors", name, tc.expectedMsg)
}
//failed with additional messages
if len(tc.expectedMsg) != 0 && len(errs) > 1 {
t.Errorf("%s expected error %s but received multiple errors: %v", name, tc.expectedMsg, errs)
}
//check that we got the right message
if len(tc.expectedMsg) != 0 && len(errs) == 1 {
if !strings.Contains(errs[0].Error(), tc.expectedMsg) {
t.Errorf("%s expected error to contain %s but it did not: %v", name, tc.expectedMsg, errs)
}
}
}
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package selinux
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// runAsAny implements the SELinuxStrategy interface.
type runAsAny struct{}
var _ SELinuxStrategy = &runAsAny{}
// NewRunAsAny provides a strategy that will return the configured se linux context or nil.
func NewRunAsAny(options *extensions.SELinuxStrategyOptions) (SELinuxStrategy, error) {
return &runAsAny{}, nil
}
// Generate creates the SELinuxOptions based on constraint rules.
func (s *runAsAny) Generate(pod *api.Pod, container *api.Container) (*api.SELinuxOptions, error) {
return nil, nil
}
// Validate ensures that the specified values fall within the range of the strategy.
func (s *runAsAny) Validate(pod *api.Pod, container *api.Container) field.ErrorList {
return field.ErrorList{}
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package selinux
import (
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestRunAsAnyOptions(t *testing.T) {
_, err := NewRunAsAny(nil)
if err != nil {
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
}
_, err = NewRunAsAny(&extensions.SELinuxStrategyOptions{})
if err != nil {
t.Errorf("unexpected error initializing NewRunAsAny %v", err)
}
}
func TestRunAsAnyGenerate(t *testing.T) {
s, err := NewRunAsAny(&extensions.SELinuxStrategyOptions{})
if err != nil {
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
}
uid, err := s.Generate(nil, nil)
if uid != nil {
t.Errorf("expected nil uid but got %v", *uid)
}
if err != nil {
t.Errorf("unexpected error generating uid %v", err)
}
}
func TestRunAsAnyValidate(t *testing.T) {
s, err := NewRunAsAny(&extensions.SELinuxStrategyOptions{
SELinuxOptions: &api.SELinuxOptions{
Level: "foo",
},
},
)
if err != nil {
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
}
errs := s.Validate(nil, nil)
if len(errs) != 0 {
t.Errorf("unexpected errors validating with ")
}
s, err = NewRunAsAny(&extensions.SELinuxStrategyOptions{})
if err != nil {
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
}
errs = s.Validate(nil, nil)
if len(errs) != 0 {
t.Errorf("unexpected errors validating %v", errs)
}
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package selinux
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// SELinuxStrategy defines the interface for all SELinux constraint strategies.
type SELinuxStrategy interface {
// Generate creates the SELinuxOptions based on constraint rules.
Generate(pod *api.Pod, container *api.Container) (*api.SELinuxOptions, error)
// Validate ensures that the specified values fall within the range of the strategy.
Validate(pod *api.Pod, container *api.Container) field.ErrorList
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package podsecuritypolicy
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/group"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/user"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// Provider provides the implementation to generate a new security
// context based on constraints or validate an existing security context against constraints.
type Provider interface {
// Create a PodSecurityContext based on the given constraints.
CreatePodSecurityContext(pod *api.Pod) (*api.PodSecurityContext, error)
// Create a container SecurityContext based on the given constraints
CreateContainerSecurityContext(pod *api.Pod, container *api.Container) (*api.SecurityContext, error)
// Ensure a pod's SecurityContext is in compliance with the given constraints.
ValidatePodSecurityContext(pod *api.Pod, fldPath *field.Path) field.ErrorList
// Ensure a container's SecurityContext is in compliance with the given constraints
ValidateContainerSecurityContext(pod *api.Pod, container *api.Container, fldPath *field.Path) field.ErrorList
// Get the name of the PSP that this provider was initialized with.
GetPSPName() string
}
// StrategyFactory abstracts how the strategies are created from the provider so that you may
// implement your own custom strategies that may pull information from other resources as necessary.
// For example, if you would like to populate the strategies with values from namespace annotations
// you may create a factory with a client that can pull the namespace and populate the appropriate
// values.
type StrategyFactory interface {
// CreateStrategies creates the strategies that a provider will use. The namespace argument
// should be the namespace of the object being checked (the pod's namespace).
CreateStrategies(psp *extensions.PodSecurityPolicy, namespace string) (*ProviderStrategies, error)
}
// ProviderStrategies is a holder for all strategies that the provider requires to be populated.
type ProviderStrategies struct {
RunAsUserStrategy user.RunAsUserStrategy
SELinuxStrategy selinux.SELinuxStrategy
FSGroupStrategy group.GroupStrategy
SupplementalGroupStrategy group.GroupStrategy
CapabilitiesStrategy capabilities.CapabilitiesStrategy
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package user
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// mustRunAs implements the RunAsUserStrategy interface
type mustRunAs struct {
opts *extensions.RunAsUserStrategyOptions
}
// NewMustRunAs provides a strategy that requires the container to run as a specific UID in a range.
func NewMustRunAs(options *extensions.RunAsUserStrategyOptions) (RunAsUserStrategy, error) {
if options == nil {
return nil, fmt.Errorf("MustRunAsRange requires run as user options")
}
if len(options.Ranges) == 0 {
return nil, fmt.Errorf("MustRunAsRange requires at least one range")
}
return &mustRunAs{
opts: options,
}, nil
}
// Generate creates the uid based on policy rules. MustRunAs returns the first range's Min.
func (s *mustRunAs) Generate(pod *api.Pod, container *api.Container) (*int64, error) {
return &s.opts.Ranges[0].Min, nil
}
// Validate ensures that the specified values fall within the range of the strategy.
func (s *mustRunAs) Validate(pod *api.Pod, container *api.Container) field.ErrorList {
allErrs := field.ErrorList{}
securityContextPath := field.NewPath("securityContext")
if container.SecurityContext == nil {
detail := fmt.Sprintf("unable to validate nil security context for container %s", container.Name)
allErrs = append(allErrs, field.Invalid(securityContextPath, container.SecurityContext, detail))
return allErrs
}
if container.SecurityContext.RunAsUser == nil {
detail := fmt.Sprintf("unable to validate nil RunAsUser for container %s", container.Name)
allErrs = append(allErrs, field.Invalid(securityContextPath.Child("runAsUser"), container.SecurityContext.RunAsUser, detail))
return allErrs
}
if !s.isValidUID(*container.SecurityContext.RunAsUser) {
detail := fmt.Sprintf("UID on container %s does not match required range. Found %d, allowed: %v",
container.Name,
*container.SecurityContext.RunAsUser,
s.opts.Ranges)
allErrs = append(allErrs, field.Invalid(securityContextPath.Child("runAsUser"), *container.SecurityContext.RunAsUser, detail))
}
return allErrs
}
func (s *mustRunAs) isValidUID(id int64) bool {
for _, rng := range s.opts.Ranges {
if psputil.FallsInRange(id, rng) {
return true
}
}
return false
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package user
import (
"strings"
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestNewMustRunAs(t *testing.T) {
tests := map[string]struct {
opts *extensions.RunAsUserStrategyOptions
pass bool
}{
"nil opts": {
opts: nil,
pass: false,
},
"invalid opts": {
opts: &extensions.RunAsUserStrategyOptions{},
pass: false,
},
"valid opts": {
opts: &extensions.RunAsUserStrategyOptions{
Ranges: []extensions.IDRange{
{Min: 1, Max: 1},
},
},
pass: true,
},
}
for name, tc := range tests {
_, err := NewMustRunAs(tc.opts)
if err != nil && tc.pass {
t.Errorf("%s expected to pass but received error %#v", name, err)
}
if err == nil && !tc.pass {
t.Errorf("%s expected to fail but did not receive an error", name)
}
}
}
func TestGenerate(t *testing.T) {
opts := &extensions.RunAsUserStrategyOptions{
Ranges: []extensions.IDRange{
{Min: 1, Max: 1},
},
}
mustRunAs, err := NewMustRunAs(opts)
if err != nil {
t.Fatalf("unexpected error initializing NewMustRunAs %v", err)
}
generated, err := mustRunAs.Generate(nil, nil)
if err != nil {
t.Fatalf("unexpected error generating runAsUser %v", err)
}
if *generated != opts.Ranges[0].Min {
t.Errorf("generated runAsUser does not equal configured runAsUser")
}
}
func TestValidate(t *testing.T) {
opts := &extensions.RunAsUserStrategyOptions{
Ranges: []extensions.IDRange{
{Min: 1, Max: 1},
{Min: 10, Max: 20},
},
}
tests := map[string]struct {
container *api.Container
expectedMsg string
}{
"good container": {
container: &api.Container{
SecurityContext: &api.SecurityContext{
RunAsUser: int64Ptr(15),
},
},
},
"nil security context": {
container: &api.Container{
SecurityContext: nil,
},
expectedMsg: "unable to validate nil security context for container",
},
"nil run as user": {
container: &api.Container{
SecurityContext: &api.SecurityContext{
RunAsUser: nil,
},
},
expectedMsg: "unable to validate nil RunAsUser for container",
},
"invalid id": {
container: &api.Container{
SecurityContext: &api.SecurityContext{
RunAsUser: int64Ptr(21),
},
},
expectedMsg: "does not match required range",
},
}
for name, tc := range tests {
mustRunAs, err := NewMustRunAs(opts)
if err != nil {
t.Errorf("unexpected error initializing NewMustRunAs for testcase %s: %#v", name, err)
continue
}
errs := mustRunAs.Validate(nil, tc.container)
//should've passed but didn't
if len(tc.expectedMsg) == 0 && len(errs) > 0 {
t.Errorf("%s expected no errors but received %v", name, errs)
}
//should've failed but didn't
if len(tc.expectedMsg) != 0 && len(errs) == 0 {
t.Errorf("%s expected error %s but received no errors", name, tc.expectedMsg)
}
//failed with additional messages
if len(tc.expectedMsg) != 0 && len(errs) > 1 {
t.Errorf("%s expected error %s but received multiple errors: %v", name, tc.expectedMsg, errs)
}
//check that we got the right message
if len(tc.expectedMsg) != 0 && len(errs) == 1 {
if !strings.Contains(errs[0].Error(), tc.expectedMsg) {
t.Errorf("%s expected error to contain %s but it did not: %v", name, tc.expectedMsg, errs)
}
}
}
}
func int64Ptr(i int64) *int64 {
return &i
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package user
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/util/validation/field"
)
type nonRoot struct{}
var _ RunAsUserStrategy = &nonRoot{}
func NewRunAsNonRoot(options *extensions.RunAsUserStrategyOptions) (RunAsUserStrategy, error) {
return &nonRoot{}, nil
}
// Generate creates the uid based on policy rules. This strategy does return a UID. It assumes
// that the user will specify a UID or the container image specifies a UID.
func (s *nonRoot) Generate(pod *api.Pod, container *api.Container) (*int64, error) {
return nil, nil
}
// Validate ensures that the specified values fall within the range of the strategy. Validation
// of this will pass if either the UID is not set, assuming that the image will provided the UID
// or if the UID is set it is not root. In order to work properly this assumes that the kubelet
// performs a final check on runAsUser or the image UID when runAsUser is nil.
func (s *nonRoot) Validate(pod *api.Pod, container *api.Container) field.ErrorList {
allErrs := field.ErrorList{}
securityContextPath := field.NewPath("securityContext")
if container.SecurityContext == nil {
detail := fmt.Sprintf("unable to validate nil security context for container %s", container.Name)
allErrs = append(allErrs, field.Invalid(securityContextPath, container.SecurityContext, detail))
return allErrs
}
if container.SecurityContext.RunAsUser != nil && *container.SecurityContext.RunAsUser == 0 {
detail := fmt.Sprintf("running with the root UID is forbidden by the pod security policy %s", container.Name)
allErrs = append(allErrs, field.Invalid(securityContextPath.Child("runAsUser"), *container.SecurityContext.RunAsUser, detail))
return allErrs
}
return allErrs
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package user
import (
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestNonRootOptions(t *testing.T) {
_, err := NewRunAsNonRoot(nil)
if err != nil {
t.Fatalf("unexpected error initializing NewRunAsNonRoot %v", err)
}
_, err = NewRunAsNonRoot(&extensions.RunAsUserStrategyOptions{})
if err != nil {
t.Errorf("unexpected error initializing NewRunAsNonRoot %v", err)
}
}
func TestNonRootGenerate(t *testing.T) {
s, err := NewRunAsNonRoot(&extensions.RunAsUserStrategyOptions{})
if err != nil {
t.Fatalf("unexpected error initializing NewRunAsNonRoot %v", err)
}
uid, err := s.Generate(nil, nil)
if uid != nil {
t.Errorf("expected nil uid but got %d", *uid)
}
if err != nil {
t.Errorf("unexpected error generating uid %v", err)
}
}
func TestNonRootValidate(t *testing.T) {
var uid int64 = 1
var badUID int64 = 0
s, err := NewRunAsNonRoot(&extensions.RunAsUserStrategyOptions{})
if err != nil {
t.Fatalf("unexpected error initializing NewMustRunAs %v", err)
}
container := &api.Container{
SecurityContext: &api.SecurityContext{
RunAsUser: &badUID,
},
}
errs := s.Validate(nil, container)
if len(errs) == 0 {
t.Errorf("expected errors from root uid but got none")
}
container.SecurityContext.RunAsUser = &uid
errs = s.Validate(nil, container)
if len(errs) != 0 {
t.Errorf("expected no errors from non-root uid but got %v", errs)
}
container.SecurityContext.RunAsUser = nil
errs = s.Validate(nil, container)
if len(errs) != 0 {
t.Errorf("expected no errors from nil uid but got %v", errs)
}
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package user
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// runAsAny implements the interface RunAsUserStrategy.
type runAsAny struct{}
var _ RunAsUserStrategy = &runAsAny{}
// NewRunAsAny provides a strategy that will return nil.
func NewRunAsAny(options *extensions.RunAsUserStrategyOptions) (RunAsUserStrategy, error) {
return &runAsAny{}, nil
}
// Generate creates the uid based on policy rules.
func (s *runAsAny) Generate(pod *api.Pod, container *api.Container) (*int64, error) {
return nil, nil
}
// Validate ensures that the specified values fall within the range of the strategy.
func (s *runAsAny) Validate(pod *api.Pod, container *api.Container) field.ErrorList {
return field.ErrorList{}
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package user
import (
"testing"
"k8s.io/kubernetes/pkg/apis/extensions"
)
func TestRunAsAnyOptions(t *testing.T) {
_, err := NewRunAsAny(nil)
if err != nil {
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
}
_, err = NewRunAsAny(&extensions.RunAsUserStrategyOptions{})
if err != nil {
t.Errorf("unexpected error initializing NewRunAsAny %v", err)
}
}
func TestRunAsAnyGenerate(t *testing.T) {
s, err := NewRunAsAny(&extensions.RunAsUserStrategyOptions{})
if err != nil {
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
}
uid, err := s.Generate(nil, nil)
if uid != nil {
t.Errorf("expected nil uid but got %d", *uid)
}
if err != nil {
t.Errorf("unexpected error generating uid %v", err)
}
}
func TestRunAsAnyValidate(t *testing.T) {
s, err := NewRunAsAny(&extensions.RunAsUserStrategyOptions{})
if err != nil {
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
}
errs := s.Validate(nil, nil)
if len(errs) != 0 {
t.Errorf("unexpected errors validating with ")
}
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package user
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// RunAsUserStrategy defines the interface for all uid constraint strategies.
type RunAsUserStrategy interface {
// Generate creates the uid based on policy rules.
Generate(pod *api.Pod, container *api.Container) (*int64, error)
// Validate ensures that the specified values fall within the range of the strategy.
Validate(pod *api.Pod, container *api.Container) field.ErrorList
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/util/sets"
)
const (
ValidatedPSPAnnotation = "kubernetes.io/psp"
)
func GetAllFSTypesExcept(exceptions ...string) sets.String {
fstypes := GetAllFSTypesAsSet()
for _, e := range exceptions {
fstypes.Delete(e)
}
return fstypes
}
func GetAllFSTypesAsSet() sets.String {
fstypes := sets.NewString()
fstypes.Insert(
string(extensions.HostPath),
string(extensions.AzureFile),
string(extensions.Flocker),
string(extensions.FlexVolume),
string(extensions.EmptyDir),
string(extensions.GCEPersistentDisk),
string(extensions.AWSElasticBlockStore),
string(extensions.GitRepo),
string(extensions.Secret),
string(extensions.NFS),
string(extensions.ISCSI),
string(extensions.Glusterfs),
string(extensions.PersistentVolumeClaim),
string(extensions.RBD),
string(extensions.Cinder),
string(extensions.CephFS),
string(extensions.DownwardAPI),
string(extensions.FC),
string(extensions.ConfigMap))
return fstypes
}
// getVolumeFSType gets the FSType for a volume.
func GetVolumeFSType(v api.Volume) (extensions.FSType, error) {
switch {
case v.HostPath != nil:
return extensions.HostPath, nil
case v.EmptyDir != nil:
return extensions.EmptyDir, nil
case v.GCEPersistentDisk != nil:
return extensions.GCEPersistentDisk, nil
case v.AWSElasticBlockStore != nil:
return extensions.AWSElasticBlockStore, nil
case v.GitRepo != nil:
return extensions.GitRepo, nil
case v.Secret != nil:
return extensions.Secret, nil
case v.NFS != nil:
return extensions.NFS, nil
case v.ISCSI != nil:
return extensions.ISCSI, nil
case v.Glusterfs != nil:
return extensions.Glusterfs, nil
case v.PersistentVolumeClaim != nil:
return extensions.PersistentVolumeClaim, nil
case v.RBD != nil:
return extensions.RBD, nil
case v.FlexVolume != nil:
return extensions.FlexVolume, nil
case v.Cinder != nil:
return extensions.Cinder, nil
case v.CephFS != nil:
return extensions.CephFS, nil
case v.Flocker != nil:
return extensions.Flocker, nil
case v.DownwardAPI != nil:
return extensions.DownwardAPI, nil
case v.FC != nil:
return extensions.FC, nil
case v.AzureFile != nil:
return extensions.AzureFile, nil
case v.ConfigMap != nil:
return extensions.ConfigMap, nil
}
return "", fmt.Errorf("unknown volume type for volume: %#v", v)
}
// fsTypeToStringSet converts an FSType slice to a string set.
func FSTypeToStringSet(fsTypes []extensions.FSType) sets.String {
set := sets.NewString()
for _, v := range fsTypes {
set.Insert(string(v))
}
return set
}
// PSPAllowsAllVolumes checks for FSTypeAll in the psp's allowed volumes.
func PSPAllowsAllVolumes(psp *extensions.PodSecurityPolicy) bool {
return PSPAllowsFSType(psp, extensions.All)
}
// PSPAllowsFSType is a utility for checking if a PSP allows a particular FSType.
// If all volumes are allowed then this will return true for any FSType passed.
func PSPAllowsFSType(psp *extensions.PodSecurityPolicy, fsType extensions.FSType) bool {
if psp == nil {
return false
}
for _, v := range psp.Spec.Volumes {
if v == fsType || v == extensions.All {
return true
}
}
return false
}
// FallsInRange is a utility to determine it the id falls in the valid range.
func FallsInRange(id int64, rng extensions.IDRange) bool {
return id >= rng.Min && id <= rng.Max
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"reflect"
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
)
// TestVolumeSourceFSTypeDrift ensures that for every known type of volume source (by the fields on
// a VolumeSource object that GetVolumeFSType is returning a good value. This ensures both that we're
// returning an FSType for the VolumeSource field (protect the GetVolumeFSType method) and that we
// haven't drifted (ensure new fields in VolumeSource are covered).
func TestVolumeSourceFSTypeDrift(t *testing.T) {
allFSTypes := GetAllFSTypesAsSet()
val := reflect.ValueOf(api.VolumeSource{})
for i := 0; i < val.NumField(); i++ {
fieldVal := val.Type().Field(i)
volumeSource := api.VolumeSource{}
volumeSourceVolume := reflect.New(fieldVal.Type.Elem())
reflect.ValueOf(&volumeSource).Elem().FieldByName(fieldVal.Name).Set(volumeSourceVolume)
fsType, err := GetVolumeFSType(api.Volume{VolumeSource: volumeSource})
if err != nil {
t.Errorf("error getting fstype for field %s. This likely means that drift has occured between FSType and VolumeSource. Please update the api and getVolumeFSType", fieldVal.Name)
}
if !allFSTypes.Has(string(fsType)) {
t.Errorf("%s was missing from GetFSTypesAsSet", fsType)
}
}
}
func TestPSPAllowsFSType(t *testing.T) {
tests := map[string]struct {
psp *extensions.PodSecurityPolicy
fsType extensions.FSType
allows bool
}{
"nil psp": {
psp: nil,
fsType: extensions.HostPath,
allows: false,
},
"empty volumes": {
psp: &extensions.PodSecurityPolicy{},
fsType: extensions.HostPath,
allows: false,
},
"non-matching": {
psp: &extensions.PodSecurityPolicy{
Spec: extensions.PodSecurityPolicySpec{
Volumes: []extensions.FSType{extensions.AWSElasticBlockStore},
},
},
fsType: extensions.HostPath,
allows: false,
},
"match on FSTypeAll": {
psp: &extensions.PodSecurityPolicy{
Spec: extensions.PodSecurityPolicySpec{
Volumes: []extensions.FSType{extensions.All},
},
},
fsType: extensions.HostPath,
allows: true,
},
"match on direct match": {
psp: &extensions.PodSecurityPolicy{
Spec: extensions.PodSecurityPolicySpec{
Volumes: []extensions.FSType{extensions.HostPath},
},
},
fsType: extensions.HostPath,
allows: true,
},
}
for k, v := range tests {
allows := PSPAllowsFSType(v.psp, v.fsType)
if v.allows != allows {
t.Errorf("%s expected PSPAllowsFSType to return %t but got %t", k, v.allows, allows)
}
}
}
...@@ -159,6 +159,11 @@ func DetermineEffectiveSecurityContext(pod *api.Pod, container *api.Container) * ...@@ -159,6 +159,11 @@ func DetermineEffectiveSecurityContext(pod *api.Pod, container *api.Container) *
*effectiveSc.RunAsNonRoot = *containerSc.RunAsNonRoot *effectiveSc.RunAsNonRoot = *containerSc.RunAsNonRoot
} }
if containerSc.ReadOnlyRootFilesystem != nil {
effectiveSc.ReadOnlyRootFilesystem = new(bool)
*effectiveSc.ReadOnlyRootFilesystem = *containerSc.ReadOnlyRootFilesystem
}
return effectiveSc return effectiveSc
} }
......
/*
Copyright 2014 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// security contains admission plugins specific to cluster security.
package security
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment