support flexvlome in psp

pull/6/head
Haoran Wang 2017-09-27 17:00:27 +08:00
parent 84b3dcca08
commit 98faf6b39c
7 changed files with 170 additions and 3 deletions

View File

@ -860,6 +860,11 @@ type PodSecurityPolicySpec struct {
// AllowedHostPaths is a white list of allowed host paths. Empty indicates that all host paths may be used.
// +optional
AllowedHostPaths []AllowedHostPath
// AllowedFlexVolumes is a whitelist of allowed Flexvolumes. Empty or nil indicates that all
// Flexvolumes may be used. This parameter is effective only when the usage of the Flexvolumes
// is allowed in the "Volumes" field.
// +optional
AllowedFlexVolumes []AllowedFlexVolume
}
// AllowedHostPath defines the host volume conditions that will be enabled by a policy
@ -923,6 +928,12 @@ var (
All FSType = "*"
)
// AllowedFlexVolume represents a single Flexvolume that is allowed to be used.
type AllowedFlexVolume struct {
// Driver is the name of the Flexvolume driver.
Driver string
}
// SELinuxStrategyOptions defines the strategy type and any options used to create the strategy.
type SELinuxStrategyOptions struct {
// Rule is the strategy that will dictate the allowable labels that may be set.

View File

@ -655,6 +655,7 @@ func ValidatePodSecurityPolicySpec(spec *extensions.PodSecurityPolicySpec, fldPa
allErrs = append(allErrs, validatePSPCapsAgainstDrops(spec.RequiredDropCapabilities, spec.AllowedCapabilities, field.NewPath("allowedCapabilities"))...)
allErrs = append(allErrs, validatePSPDefaultAllowPrivilegeEscalation(fldPath.Child("defaultAllowPrivilegeEscalation"), spec.DefaultAllowPrivilegeEscalation, spec.AllowPrivilegeEscalation)...)
allErrs = append(allErrs, validatePSPAllowedHostPaths(fldPath.Child("allowedHostPaths"), spec.AllowedHostPaths)...)
allErrs = append(allErrs, validatePSPAllowedFlexVolumes(fldPath.Child("allowedFlexVolumes"), spec.AllowedFlexVolumes)...)
return allErrs
}
@ -721,6 +722,20 @@ func validatePSPAllowedHostPaths(fldPath *field.Path, allowedHostPaths []extensi
return allErrs
}
// validatePSPAllowedFlexVolumes
func validatePSPAllowedFlexVolumes(fldPath *field.Path, flexVolumes []extensions.AllowedFlexVolume) field.ErrorList {
allErrs := field.ErrorList{}
if len(flexVolumes) > 0 {
for idx, fv := range flexVolumes {
if len(fv.Driver) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("allowedFlexVolumes").Index(idx).Child("driver"),
"must specify a driver"))
}
}
}
return allErrs
}
// validatePSPSELinux validates the SELinux fields of PodSecurityPolicy.
func validatePSPSELinux(fldPath *field.Path, seLinux *extensions.SELinuxStrategyOptions) field.ErrorList {
allErrs := field.ErrorList{}
@ -802,7 +817,6 @@ func validatePodSecurityPolicyVolumes(fldPath *field.Path, volumes []extensions.
allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumes"), v, allowed.List()))
}
}
return allErrs
}

View File

@ -2450,6 +2450,13 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
pe := true
invalidDefaultAllowPrivilegeEscalation.Spec.DefaultAllowPrivilegeEscalation = &pe
emptyFlexDriver := validPSP()
emptyFlexDriver.Spec.Volumes = []extensions.FSType{extensions.FlexVolume}
emptyFlexDriver.Spec.AllowedFlexVolumes = []extensions.AllowedFlexVolume{{}}
nonEmptyFlexVolumes := validPSP()
nonEmptyFlexVolumes.Spec.AllowedFlexVolumes = []extensions.AllowedFlexVolume{{Driver: "example/driver"}}
type testCase struct {
psp *extensions.PodSecurityPolicy
errorType field.ErrorType
@ -2581,6 +2588,11 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
errorType: field.ErrorTypeInvalid,
errorDetail: "must not contain '..'",
},
"empty flex volume driver": {
psp: emptyFlexDriver,
errorType: field.ErrorTypeRequired,
errorDetail: "must specify a driver",
},
}
for k, v := range errorCases {
@ -2660,6 +2672,17 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
validDefaultAllowPrivilegeEscalation.Spec.DefaultAllowPrivilegeEscalation = &pe
validDefaultAllowPrivilegeEscalation.Spec.AllowPrivilegeEscalation = true
flexvolumeWhenFlexVolumesAllowed := validPSP()
flexvolumeWhenFlexVolumesAllowed.Spec.Volumes = []extensions.FSType{extensions.FlexVolume}
flexvolumeWhenFlexVolumesAllowed.Spec.AllowedFlexVolumes = []extensions.AllowedFlexVolume{
{Driver: "example/driver1"},
}
flexvolumeWhenAllVolumesAllowed := validPSP()
flexvolumeWhenAllVolumesAllowed.Spec.Volumes = []extensions.FSType{extensions.All}
flexvolumeWhenAllVolumesAllowed.Spec.AllowedFlexVolumes = []extensions.AllowedFlexVolume{
{Driver: "example/driver2"},
}
successCases := map[string]struct {
psp *extensions.PodSecurityPolicy
}{
@ -2690,6 +2713,12 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
"valid defaultAllowPrivilegeEscalation as true": {
psp: validDefaultAllowPrivilegeEscalation,
},
"allow white-listed flexVolume when flex volumes are allowed": {
psp: flexvolumeWhenFlexVolumesAllowed,
},
"allow white-listed flexVolume when all volumes are allowed": {
psp: flexvolumeWhenAllVolumesAllowed,
},
}
for k, v := range successCases {

View File

@ -3386,6 +3386,9 @@ func describePodSecurityPolicy(psp *extensions.PodSecurityPolicy) (string, error
w.Write(LEVEL_1, "Allowed Capabilities:\t%s\n", capsToString(psp.Spec.AllowedCapabilities))
w.Write(LEVEL_1, "Allowed Volume Types:\t%s\n", fsTypeToString(psp.Spec.Volumes))
if len(psp.Spec.AllowedFlexVolumes) > 0 {
w.Write(LEVEL_1, "Allowed FlexVolume Types:\t%s\n", flexVolumesToString(psp.Spec.AllowedFlexVolumes))
}
w.Write(LEVEL_1, "Allow Host Network:\t%t\n", psp.Spec.HostNetwork)
w.Write(LEVEL_1, "Allow Host Ports:\t%s\n", hostPortRangeToString(psp.Spec.HostPorts))
w.Write(LEVEL_1, "Allow Host PID:\t%t\n", psp.Spec.HostPID)
@ -3419,10 +3422,14 @@ func describePodSecurityPolicy(psp *extensions.PodSecurityPolicy) (string, error
}
func stringOrNone(s string) string {
return stringOrDefaultValue(s, "<none>")
}
func stringOrDefaultValue(s, defaultValue string) string {
if len(s) > 0 {
return s
}
return "<none>"
return defaultValue
}
func fsTypeToString(volumes []extensions.FSType) string {
@ -3433,6 +3440,14 @@ func fsTypeToString(volumes []extensions.FSType) string {
return stringOrNone(strings.Join(strVolumes, ","))
}
func flexVolumesToString(flexVolumes []extensions.AllowedFlexVolume) string {
volumes := []string{}
for _, flexVolume := range flexVolumes {
volumes = append(volumes, "driver="+flexVolume.Driver)
}
return stringOrDefaultValue(strings.Join(volumes, ","), "<all>")
}
func hostPortRangeToString(ranges []extensions.HostPortRange) string {
formattedString := ""
if ranges != nil {

View File

@ -233,9 +233,24 @@ func (s *simpleProvider) ValidatePodSecurityContext(pod *api.Pod, fldPath *field
fmt.Sprintf("is not allowed to be used")))
}
}
if fsType == extensions.FlexVolume && len(s.psp.Spec.AllowedFlexVolumes) > 0 {
found := false
driver := v.FlexVolume.Driver
for _, allowedFlexVolume := range s.psp.Spec.AllowedFlexVolumes {
if driver == allowedFlexVolume.Driver {
found = true
break
}
}
if !found {
allErrs = append(allErrs,
field.Invalid(fldPath.Child("volumes").Index(i).Child("driver"), driver,
"Flexvolume driver is not allowed to be used"))
}
}
}
}
return allErrs
}

View File

@ -256,6 +256,18 @@ func TestValidatePodSecurityContextFailures(t *testing.T) {
failSeccompProfilePod := defaultPod()
failSeccompProfilePod.Annotations = map[string]string{api.SeccompPodAnnotationKey: "foo"}
podWithInvalidFlexVolumeDriver := defaultPod()
podWithInvalidFlexVolumeDriver.Spec.Volumes = []api.Volume{
{
Name: "flex-volume",
VolumeSource: api.VolumeSource{
FlexVolume: &api.FlexVolumeSource{
Driver: "example/unknown",
},
},
},
}
errorCases := map[string]struct {
pod *api.Pod
psp *extensions.PodSecurityPolicy
@ -341,6 +353,16 @@ func TestValidatePodSecurityContextFailures(t *testing.T) {
psp: defaultPSP(),
expectedError: "Forbidden: seccomp may not be set",
},
"fail pod with disallowed flexVolume when flex volumes are allowed": {
pod: podWithInvalidFlexVolumeDriver,
psp: allowFlexVolumesPSP(false, false),
expectedError: "Flexvolume driver is not allowed to be used",
},
"fail pod with disallowed flexVolume when all volumes are allowed": {
pod: podWithInvalidFlexVolumeDriver,
psp: allowFlexVolumesPSP(false, true),
expectedError: "Flexvolume driver is not allowed to be used",
},
}
for k, v := range errorCases {
provider, err := NewSimpleProvider(v.psp, "namespace", NewSimpleStrategyFactory())
@ -358,6 +380,28 @@ func TestValidatePodSecurityContextFailures(t *testing.T) {
}
}
func allowFlexVolumesPSP(allowAllFlexVolumes, allowAllVolumes bool) *extensions.PodSecurityPolicy {
psp := defaultPSP()
allowedVolumes := []extensions.AllowedFlexVolume{
{Driver: "example/foo"},
{Driver: "example/bar"},
}
if allowAllFlexVolumes {
allowedVolumes = []extensions.AllowedFlexVolume{}
}
allowedVolumeType := extensions.FlexVolume
if allowAllVolumes {
allowedVolumeType = extensions.All
}
psp.Spec.AllowedFlexVolumes = allowedVolumes
psp.Spec.Volumes = []extensions.FSType{allowedVolumeType}
return psp
}
func TestValidateContainerSecurityContextFailures(t *testing.T) {
// fail user strat
failUserPSP := defaultPSP()
@ -597,6 +641,18 @@ func TestValidatePodSecurityContextSuccess(t *testing.T) {
api.SeccompPodAnnotationKey: "foo",
}
flexVolumePod := defaultPod()
flexVolumePod.Spec.Volumes = []api.Volume{
{
Name: "flex-volume",
VolumeSource: api.VolumeSource{
FlexVolume: &api.FlexVolumeSource{
Driver: "example/bar",
},
},
},
}
successCases := map[string]struct {
pod *api.Pod
psp *extensions.PodSecurityPolicy
@ -653,6 +709,22 @@ func TestValidatePodSecurityContextSuccess(t *testing.T) {
pod: seccompPod,
psp: seccompPSP,
},
"flex volume driver in a whitelist (all volumes are allowed)": {
pod: flexVolumePod,
psp: allowFlexVolumesPSP(false, true),
},
"flex volume driver with empty whitelist (all volumes are allowed)": {
pod: flexVolumePod,
psp: allowFlexVolumesPSP(true, true),
},
"flex volume driver in a whitelist (only flex volumes are allowed)": {
pod: flexVolumePod,
psp: allowFlexVolumesPSP(false, false),
},
"flex volume driver with empty whitelist (only flex volumes volumes are allowed)": {
pod: flexVolumePod,
psp: allowFlexVolumesPSP(true, false),
},
}
for k, v := range successCases {

View File

@ -938,6 +938,11 @@ type PodSecurityPolicySpec struct {
// is a white list of allowed host paths. Empty indicates that all host paths may be used.
// +optional
AllowedHostPaths []AllowedHostPath `json:"allowedHostPaths,omitempty" protobuf:"bytes,17,rep,name=allowedHostPaths"`
// AllowedFlexVolumes is a whitelist of allowed Flexvolumes. Empty or nil indicates that all
// Flexvolumes may be used. This parameter is effective only when the usage of the Flexvolumes
// is allowed in the "Volumes" field.
// +optional
AllowedFlexVolumes []AllowedFlexVolume `json:"allowedFlexVolumes,omitempty" protobuf:"bytes,18,rep,name=allowedFlexVolumes"`
}
// defines the host volume conditions that will be enabled by a policy
@ -981,6 +986,12 @@ var (
All FSType = "*"
)
// AllowedFlexVolume represents a single Flexvolume that is allowed to be used.
type AllowedFlexVolume struct {
// Driver is the name of the Flexvolume driver.
Driver string `json:"driver" protobuf:"bytes,1,opt,name=driver"`
}
// Host Port Range defines a range of host ports that will be enabled by a policy
// for pods to use. It requires both the start and end to be defined.
type HostPortRange struct {