Skip to content

Commit

Permalink
PSP types
Browse files Browse the repository at this point in the history
  • Loading branch information
pweil- committed May 11, 2016
1 parent 04dc71f commit 56193b7
Show file tree
Hide file tree
Showing 37 changed files with 3,908 additions and 90 deletions.
74 changes: 70 additions & 4 deletions pkg/apis/extensions/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,8 @@ type ReplicaSetStatus struct {
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
}

// +genclient=true,nonNamespaced=true

// PodSecurityPolicy governs the ability to make requests that affect the SecurityContext
// that will be applied to a pod and container.
type PodSecurityPolicy struct {
Expand All @@ -638,8 +640,17 @@ type PodSecurityPolicy struct {
type PodSecurityPolicySpec struct {
// Privileged determines if a pod can request to be run as privileged.
Privileged bool `json:"privileged,omitempty"`
// Capabilities is a list of capabilities that can be added.
Capabilities []api.Capability `json:"capabilities,omitempty"`
// 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.
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
// may be used.
Volumes []FSType `json:"volumes,omitempty"`
Expand All @@ -652,9 +663,19 @@ type PodSecurityPolicySpec struct {
// HostIPC determines if the policy allows the use of HostIPC in the pod spec.
HostIPC bool `json:"hostIPC,omitempty"`
// 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 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
Expand All @@ -670,6 +691,9 @@ type HostPortRange struct {
type FSType string

var (
AzureFile FSType = "azureFile"
Flocker FSType = "flocker"
FlexVolume FSType = "flexVolume"
HostPath FSType = "hostPath"
EmptyDir FSType = "emptyDir"
GCEPersistentDisk FSType = "gcePersistentDisk"
Expand All @@ -685,6 +709,8 @@ var (
CephFS FSType = "cephFS"
DownwardAPI FSType = "downwardAPI"
FC FSType = "fc"
ConfigMap FSType = "configMap"
All FSType = "*"
)

// SELinuxStrategyOptions defines the strategy type and any options used to create the strategy.
Expand Down Expand Up @@ -736,6 +762,46 @@ const (
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.
type PodSecurityPolicyList struct {
unversioned.TypeMeta `json:",inline"`
Expand Down
84 changes: 75 additions & 9 deletions pkg/apis/extensions/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,8 @@ type ReplicaSetStatus struct {
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
// that will be applied to a pod and container.
type PodSecurityPolicy struct {
Expand All @@ -928,29 +930,51 @@ type PodSecurityPolicy struct {
type PodSecurityPolicySpec struct {
// privileged determines if a pod can request to be run as privileged.
Privileged bool `json:"privileged,omitempty" protobuf:"varint,1,opt,name=privileged"`
// capabilities is a list of capabilities that can be added.
Capabilities []v1.Capability `json:"capabilities,omitempty" protobuf:"bytes,2,rep,name=capabilities,casttype=k8s.io/kubernetes/pkg/api/v1.Capability"`
// 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.
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
// 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 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 []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 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 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 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 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.
type FSType string

var (
AzureFile FSType = "azureFile"
Flocker FSType = "flocker"
FlexVolume FSType = "flexVolume"
HostPath FSType = "hostPath"
EmptyDir FSType = "emptyDir"
GCEPersistentDisk FSType = "gcePersistentDisk"
Expand All @@ -966,6 +990,8 @@ var (
CephFS FSType = "cephFS"
DownwardAPI FSType = "downwardAPI"
FC FSType = "fc"
ConfigMap FSType = "configMap"
All FSType = "*"
)

// Host Port Range defines a range of host ports that will be enabled by a policy
Expand Down Expand Up @@ -1026,6 +1052,46 @@ const (
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.
type PodSecurityPolicyList struct {
unversioned.TypeMeta `json:",inline"`
Expand Down
85 changes: 70 additions & 15 deletions pkg/apis/extensions/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package validation

import (
"fmt"
"net"
"regexp"
"strconv"
Expand All @@ -28,6 +29,7 @@ import (
apivalidation "k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/labels"
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
"k8s.io/kubernetes/pkg/util/intstr"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/util/validation"
Expand Down Expand Up @@ -529,7 +531,11 @@ func ValidatePodSecurityPolicySpec(spec *extensions.PodSecurityPolicySpec, fldPa

allErrs = append(allErrs, validatePSPRunAsUser(fldPath.Child("runAsUser"), &spec.RunAsUser)...)
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, validatePSPCapsAgainstDrops(spec.RequiredDropCapabilities, spec.DefaultAddCapabilities, field.NewPath("defaultAddCapabilities"))...)
allErrs = append(allErrs, validatePSPCapsAgainstDrops(spec.RequiredDropCapabilities, spec.AllowedCapabilities, field.NewPath("allowedCapabilities"))...)

return allErrs
}
Expand Down Expand Up @@ -568,24 +574,48 @@ func validatePSPRunAsUser(fldPath *field.Path, runAsUser *extensions.RunAsUserSt
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.
func validatePodSecurityPolicyVolumes(fldPath *field.Path, volumes []extensions.FSType) field.ErrorList {
allErrs := field.ErrorList{}
allowed := sets.NewString(string(extensions.HostPath),
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))
allowed := psputil.GetAllFSTypesAsSet()
// add in the * value since that is a pseudo type that is not included by default
allowed.Insert(string(extensions.All))
for _, v := range volumes {
if !allowed.Has(string(v)) {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumes"), v, allowed.List()))
Expand Down Expand Up @@ -614,6 +644,31 @@ func validateIDRanges(fldPath *field.Path, rng extensions.IDRange) field.ErrorLi
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.
func ValidatePodSecurityPolicyUpdate(old *extensions.PodSecurityPolicy, new *extensions.PodSecurityPolicy) field.ErrorList {
allErrs := field.ErrorList{}
Expand Down
Loading

0 comments on commit 56193b7

Please sign in to comment.