Unverified Commit f0e337cd authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #53179 from wanghaoran1988/psp_flexvolume

Automatic merge from submit-queue (batch tested with PRs 55824, 53179). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Allow Pod Security Policy to manage access to the Flexvolumes **What this PR does / why we need it**: For proposal: https://github.com/kubernetes/community/blob/a1b9495e1b722699196ccec88d831fc850100827/contributors/design-proposals/auth/flex-volumes-drivers-psp.md (https://github.com/kubernetes/community/issues/723) **Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes # **Special notes for your reviewer**: **Release note**: ```release-note Pod Security Policy can now manage access to specific FlexVolume drivers ```
parents 6a1d3366 e297a819
...@@ -79217,6 +79217,18 @@ ...@@ -79217,6 +79217,18 @@
} }
} }
}, },
"io.k8s.api.extensions.v1beta1.AllowedFlexVolume": {
"description": "AllowedFlexVolume represents a single Flexvolume that is allowed to be used.",
"required": [
"driver"
],
"properties": {
"driver": {
"description": "Driver is the name of the Flexvolume driver.",
"type": "string"
}
}
},
"io.k8s.api.extensions.v1beta1.AllowedHostPath": { "io.k8s.api.extensions.v1beta1.AllowedHostPath": {
"description": "defines the host volume conditions that will be enabled by a policy for pods to use. It requires the path prefix to be defined.", "description": "defines the host volume conditions that will be enabled by a policy for pods to use. It requires the path prefix to be defined.",
"properties": { "properties": {
...@@ -80171,6 +80183,13 @@ ...@@ -80171,6 +80183,13 @@
"type": "string" "type": "string"
} }
}, },
"allowedFlexVolumes": {
"description": "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.",
"type": "array",
"items": {
"$ref": "#/definitions/io.k8s.api.extensions.v1beta1.AllowedFlexVolume"
}
},
"allowedHostPaths": { "allowedHostPaths": {
"description": "is a white list of allowed host paths. Empty indicates that all host paths may be used.", "description": "is a white list of allowed host paths. Empty indicates that all host paths may be used.",
"type": "array", "type": "array",
...@@ -10314,6 +10314,13 @@ ...@@ -10314,6 +10314,13 @@
"$ref": "v1beta1.AllowedHostPath" "$ref": "v1beta1.AllowedHostPath"
}, },
"description": "is a white list of allowed host paths. Empty indicates that all host paths may be used." "description": "is a white list of allowed host paths. Empty indicates that all host paths may be used."
},
"allowedFlexVolumes": {
"type": "array",
"items": {
"$ref": "v1beta1.AllowedFlexVolume"
},
"description": "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."
} }
} }
}, },
...@@ -10442,6 +10449,19 @@ ...@@ -10442,6 +10449,19 @@
} }
} }
}, },
"v1beta1.AllowedFlexVolume": {
"id": "v1beta1.AllowedFlexVolume",
"description": "AllowedFlexVolume represents a single Flexvolume that is allowed to be used.",
"required": [
"driver"
],
"properties": {
"driver": {
"type": "string",
"description": "Driver is the name of the Flexvolume driver."
}
}
},
"v1beta1.ReplicaSetList": { "v1beta1.ReplicaSetList": {
"id": "v1beta1.ReplicaSetList", "id": "v1beta1.ReplicaSetList",
"description": "ReplicaSetList is a collection of ReplicaSets.", "description": "ReplicaSetList is a collection of ReplicaSets.",
......
...@@ -6730,6 +6730,40 @@ Both these may change in the future. Incoming requests are matched against the h ...@@ -6730,6 +6730,40 @@ Both these may change in the future. Incoming requests are matched against the h
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_v1beta1_allowedflexvolume">v1beta1.AllowedFlexVolume</h3>
<div class="paragraph">
<p>AllowedFlexVolume represents a single Flexvolume that is allowed to be used.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">driver</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Driver is the name of the Flexvolume driver.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_apiresource">v1.APIResource</h3> <h3 id="_v1_apiresource">v1.APIResource</h3>
<div class="paragraph"> <div class="paragraph">
<p>APIResource specifies the name of a resource and whether it is namespaced.</p> <p>APIResource specifies the name of a resource and whether it is namespaced.</p>
...@@ -7972,6 +8006,13 @@ Both these may change in the future. Incoming requests are matched against the h ...@@ -7972,6 +8006,13 @@ Both these may change in the future. Incoming requests are matched against the h
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1beta1_allowedhostpath">v1beta1.AllowedHostPath</a> array</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1beta1_allowedhostpath">v1beta1.AllowedHostPath</a> array</p></td>
<td class="tableblock halign-left valign-top"></td> <td class="tableblock halign-left valign-top"></td>
</tr> </tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">allowedFlexVolumes</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">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.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1beta1_allowedflexvolume">v1beta1.AllowedFlexVolume</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody> </tbody>
</table> </table>
......
...@@ -860,6 +860,11 @@ type PodSecurityPolicySpec struct { ...@@ -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. // AllowedHostPaths is a white list of allowed host paths. Empty indicates that all host paths may be used.
// +optional // +optional
AllowedHostPaths []AllowedHostPath 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 // AllowedHostPath defines the host volume conditions that will be enabled by a policy
...@@ -923,6 +928,12 @@ var ( ...@@ -923,6 +928,12 @@ var (
All FSType = "*" 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. // SELinuxStrategyOptions defines the strategy type and any options used to create the strategy.
type SELinuxStrategyOptions struct { type SELinuxStrategyOptions struct {
// Rule is the strategy that will dictate the allowable labels that may be set. // Rule is the strategy that will dictate the allowable labels that may be set.
......
...@@ -41,6 +41,8 @@ func init() { ...@@ -41,6 +41,8 @@ func init() {
// Public to allow building arbitrary schemes. // Public to allow building arbitrary schemes.
func RegisterConversions(scheme *runtime.Scheme) error { func RegisterConversions(scheme *runtime.Scheme) error {
return scheme.AddGeneratedConversionFuncs( return scheme.AddGeneratedConversionFuncs(
Convert_v1beta1_AllowedFlexVolume_To_extensions_AllowedFlexVolume,
Convert_extensions_AllowedFlexVolume_To_v1beta1_AllowedFlexVolume,
Convert_v1beta1_AllowedHostPath_To_extensions_AllowedHostPath, Convert_v1beta1_AllowedHostPath_To_extensions_AllowedHostPath,
Convert_extensions_AllowedHostPath_To_v1beta1_AllowedHostPath, Convert_extensions_AllowedHostPath_To_v1beta1_AllowedHostPath,
Convert_v1beta1_CustomMetricCurrentStatus_To_extensions_CustomMetricCurrentStatus, Convert_v1beta1_CustomMetricCurrentStatus_To_extensions_CustomMetricCurrentStatus,
...@@ -140,6 +142,26 @@ func RegisterConversions(scheme *runtime.Scheme) error { ...@@ -140,6 +142,26 @@ func RegisterConversions(scheme *runtime.Scheme) error {
) )
} }
func autoConvert_v1beta1_AllowedFlexVolume_To_extensions_AllowedFlexVolume(in *v1beta1.AllowedFlexVolume, out *extensions.AllowedFlexVolume, s conversion.Scope) error {
out.Driver = in.Driver
return nil
}
// Convert_v1beta1_AllowedFlexVolume_To_extensions_AllowedFlexVolume is an autogenerated conversion function.
func Convert_v1beta1_AllowedFlexVolume_To_extensions_AllowedFlexVolume(in *v1beta1.AllowedFlexVolume, out *extensions.AllowedFlexVolume, s conversion.Scope) error {
return autoConvert_v1beta1_AllowedFlexVolume_To_extensions_AllowedFlexVolume(in, out, s)
}
func autoConvert_extensions_AllowedFlexVolume_To_v1beta1_AllowedFlexVolume(in *extensions.AllowedFlexVolume, out *v1beta1.AllowedFlexVolume, s conversion.Scope) error {
out.Driver = in.Driver
return nil
}
// Convert_extensions_AllowedFlexVolume_To_v1beta1_AllowedFlexVolume is an autogenerated conversion function.
func Convert_extensions_AllowedFlexVolume_To_v1beta1_AllowedFlexVolume(in *extensions.AllowedFlexVolume, out *v1beta1.AllowedFlexVolume, s conversion.Scope) error {
return autoConvert_extensions_AllowedFlexVolume_To_v1beta1_AllowedFlexVolume(in, out, s)
}
func autoConvert_v1beta1_AllowedHostPath_To_extensions_AllowedHostPath(in *v1beta1.AllowedHostPath, out *extensions.AllowedHostPath, s conversion.Scope) error { func autoConvert_v1beta1_AllowedHostPath_To_extensions_AllowedHostPath(in *v1beta1.AllowedHostPath, out *extensions.AllowedHostPath, s conversion.Scope) error {
out.PathPrefix = in.PathPrefix out.PathPrefix = in.PathPrefix
return nil return nil
...@@ -1072,6 +1094,7 @@ func autoConvert_v1beta1_PodSecurityPolicySpec_To_extensions_PodSecurityPolicySp ...@@ -1072,6 +1094,7 @@ func autoConvert_v1beta1_PodSecurityPolicySpec_To_extensions_PodSecurityPolicySp
return err return err
} }
out.AllowedHostPaths = *(*[]extensions.AllowedHostPath)(unsafe.Pointer(&in.AllowedHostPaths)) out.AllowedHostPaths = *(*[]extensions.AllowedHostPath)(unsafe.Pointer(&in.AllowedHostPaths))
out.AllowedFlexVolumes = *(*[]extensions.AllowedFlexVolume)(unsafe.Pointer(&in.AllowedFlexVolumes))
return nil return nil
} }
...@@ -1108,6 +1131,7 @@ func autoConvert_extensions_PodSecurityPolicySpec_To_v1beta1_PodSecurityPolicySp ...@@ -1108,6 +1131,7 @@ func autoConvert_extensions_PodSecurityPolicySpec_To_v1beta1_PodSecurityPolicySp
return err return err
} }
out.AllowedHostPaths = *(*[]v1beta1.AllowedHostPath)(unsafe.Pointer(&in.AllowedHostPaths)) out.AllowedHostPaths = *(*[]v1beta1.AllowedHostPath)(unsafe.Pointer(&in.AllowedHostPaths))
out.AllowedFlexVolumes = *(*[]v1beta1.AllowedFlexVolume)(unsafe.Pointer(&in.AllowedFlexVolumes))
return nil return nil
} }
......
...@@ -655,6 +655,7 @@ func ValidatePodSecurityPolicySpec(spec *extensions.PodSecurityPolicySpec, fldPa ...@@ -655,6 +655,7 @@ func ValidatePodSecurityPolicySpec(spec *extensions.PodSecurityPolicySpec, fldPa
allErrs = append(allErrs, validatePSPCapsAgainstDrops(spec.RequiredDropCapabilities, spec.AllowedCapabilities, field.NewPath("allowedCapabilities"))...) 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, validatePSPDefaultAllowPrivilegeEscalation(fldPath.Child("defaultAllowPrivilegeEscalation"), spec.DefaultAllowPrivilegeEscalation, spec.AllowPrivilegeEscalation)...)
allErrs = append(allErrs, validatePSPAllowedHostPaths(fldPath.Child("allowedHostPaths"), spec.AllowedHostPaths)...) allErrs = append(allErrs, validatePSPAllowedHostPaths(fldPath.Child("allowedHostPaths"), spec.AllowedHostPaths)...)
allErrs = append(allErrs, validatePSPAllowedFlexVolumes(fldPath.Child("allowedFlexVolumes"), spec.AllowedFlexVolumes)...)
return allErrs return allErrs
} }
...@@ -721,6 +722,20 @@ func validatePSPAllowedHostPaths(fldPath *field.Path, allowedHostPaths []extensi ...@@ -721,6 +722,20 @@ func validatePSPAllowedHostPaths(fldPath *field.Path, allowedHostPaths []extensi
return allErrs 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. // validatePSPSELinux validates the SELinux fields of PodSecurityPolicy.
func validatePSPSELinux(fldPath *field.Path, seLinux *extensions.SELinuxStrategyOptions) field.ErrorList { func validatePSPSELinux(fldPath *field.Path, seLinux *extensions.SELinuxStrategyOptions) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
...@@ -802,7 +817,6 @@ func validatePodSecurityPolicyVolumes(fldPath *field.Path, volumes []extensions. ...@@ -802,7 +817,6 @@ func validatePodSecurityPolicyVolumes(fldPath *field.Path, volumes []extensions.
allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumes"), v, allowed.List())) allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumes"), v, allowed.List()))
} }
} }
return allErrs return allErrs
} }
......
...@@ -2450,6 +2450,13 @@ func TestValidatePodSecurityPolicy(t *testing.T) { ...@@ -2450,6 +2450,13 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
pe := true pe := true
invalidDefaultAllowPrivilegeEscalation.Spec.DefaultAllowPrivilegeEscalation = &pe 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 { type testCase struct {
psp *extensions.PodSecurityPolicy psp *extensions.PodSecurityPolicy
errorType field.ErrorType errorType field.ErrorType
...@@ -2581,6 +2588,11 @@ func TestValidatePodSecurityPolicy(t *testing.T) { ...@@ -2581,6 +2588,11 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
errorType: field.ErrorTypeInvalid, errorType: field.ErrorTypeInvalid,
errorDetail: "must not contain '..'", errorDetail: "must not contain '..'",
}, },
"empty flex volume driver": {
psp: emptyFlexDriver,
errorType: field.ErrorTypeRequired,
errorDetail: "must specify a driver",
},
} }
for k, v := range errorCases { for k, v := range errorCases {
...@@ -2660,6 +2672,17 @@ func TestValidatePodSecurityPolicy(t *testing.T) { ...@@ -2660,6 +2672,17 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
validDefaultAllowPrivilegeEscalation.Spec.DefaultAllowPrivilegeEscalation = &pe validDefaultAllowPrivilegeEscalation.Spec.DefaultAllowPrivilegeEscalation = &pe
validDefaultAllowPrivilegeEscalation.Spec.AllowPrivilegeEscalation = true 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 { successCases := map[string]struct {
psp *extensions.PodSecurityPolicy psp *extensions.PodSecurityPolicy
}{ }{
...@@ -2690,6 +2713,12 @@ func TestValidatePodSecurityPolicy(t *testing.T) { ...@@ -2690,6 +2713,12 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
"valid defaultAllowPrivilegeEscalation as true": { "valid defaultAllowPrivilegeEscalation as true": {
psp: validDefaultAllowPrivilegeEscalation, 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 { for k, v := range successCases {
......
...@@ -27,6 +27,22 @@ import ( ...@@ -27,6 +27,22 @@ import (
) )
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllowedFlexVolume) DeepCopyInto(out *AllowedFlexVolume) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AllowedFlexVolume.
func (in *AllowedFlexVolume) DeepCopy() *AllowedFlexVolume {
if in == nil {
return nil
}
out := new(AllowedFlexVolume)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllowedHostPath) DeepCopyInto(out *AllowedHostPath) { func (in *AllowedHostPath) DeepCopyInto(out *AllowedHostPath) {
*out = *in *out = *in
return return
...@@ -919,6 +935,11 @@ func (in *PodSecurityPolicySpec) DeepCopyInto(out *PodSecurityPolicySpec) { ...@@ -919,6 +935,11 @@ func (in *PodSecurityPolicySpec) DeepCopyInto(out *PodSecurityPolicySpec) {
*out = make([]AllowedHostPath, len(*in)) *out = make([]AllowedHostPath, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.AllowedFlexVolumes != nil {
in, out := &in.AllowedFlexVolumes, &out.AllowedFlexVolumes
*out = make([]AllowedFlexVolume, len(*in))
copy(*out, *in)
}
return return
} }
......
...@@ -3386,6 +3386,9 @@ func describePodSecurityPolicy(psp *extensions.PodSecurityPolicy) (string, error ...@@ -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 Capabilities:\t%s\n", capsToString(psp.Spec.AllowedCapabilities))
w.Write(LEVEL_1, "Allowed Volume Types:\t%s\n", fsTypeToString(psp.Spec.Volumes)) 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 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 Ports:\t%s\n", hostPortRangeToString(psp.Spec.HostPorts))
w.Write(LEVEL_1, "Allow Host PID:\t%t\n", psp.Spec.HostPID) w.Write(LEVEL_1, "Allow Host PID:\t%t\n", psp.Spec.HostPID)
...@@ -3419,10 +3422,14 @@ func describePodSecurityPolicy(psp *extensions.PodSecurityPolicy) (string, error ...@@ -3419,10 +3422,14 @@ func describePodSecurityPolicy(psp *extensions.PodSecurityPolicy) (string, error
} }
func stringOrNone(s string) string { func stringOrNone(s string) string {
return stringOrDefaultValue(s, "<none>")
}
func stringOrDefaultValue(s, defaultValue string) string {
if len(s) > 0 { if len(s) > 0 {
return s return s
} }
return "<none>" return defaultValue
} }
func fsTypeToString(volumes []extensions.FSType) string { func fsTypeToString(volumes []extensions.FSType) string {
...@@ -3433,6 +3440,14 @@ func fsTypeToString(volumes []extensions.FSType) string { ...@@ -3433,6 +3440,14 @@ func fsTypeToString(volumes []extensions.FSType) string {
return stringOrNone(strings.Join(strVolumes, ",")) 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 { func hostPortRangeToString(ranges []extensions.HostPortRange) string {
formattedString := "" formattedString := ""
if ranges != nil { if ranges != nil {
......
...@@ -233,9 +233,24 @@ func (s *simpleProvider) ValidatePodSecurityContext(pod *api.Pod, fldPath *field ...@@ -233,9 +233,24 @@ func (s *simpleProvider) ValidatePodSecurityContext(pod *api.Pod, fldPath *field
fmt.Sprintf("is not allowed to be used"))) 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 return allErrs
} }
......
...@@ -256,6 +256,18 @@ func TestValidatePodSecurityContextFailures(t *testing.T) { ...@@ -256,6 +256,18 @@ func TestValidatePodSecurityContextFailures(t *testing.T) {
failSeccompProfilePod := defaultPod() failSeccompProfilePod := defaultPod()
failSeccompProfilePod.Annotations = map[string]string{api.SeccompPodAnnotationKey: "foo"} 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 { errorCases := map[string]struct {
pod *api.Pod pod *api.Pod
psp *extensions.PodSecurityPolicy psp *extensions.PodSecurityPolicy
...@@ -341,6 +353,16 @@ func TestValidatePodSecurityContextFailures(t *testing.T) { ...@@ -341,6 +353,16 @@ func TestValidatePodSecurityContextFailures(t *testing.T) {
psp: defaultPSP(), psp: defaultPSP(),
expectedError: "Forbidden: seccomp may not be set", 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 { for k, v := range errorCases {
provider, err := NewSimpleProvider(v.psp, "namespace", NewSimpleStrategyFactory()) provider, err := NewSimpleProvider(v.psp, "namespace", NewSimpleStrategyFactory())
...@@ -358,6 +380,28 @@ func TestValidatePodSecurityContextFailures(t *testing.T) { ...@@ -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) { func TestValidateContainerSecurityContextFailures(t *testing.T) {
// fail user strat // fail user strat
failUserPSP := defaultPSP() failUserPSP := defaultPSP()
...@@ -597,6 +641,18 @@ func TestValidatePodSecurityContextSuccess(t *testing.T) { ...@@ -597,6 +641,18 @@ func TestValidatePodSecurityContextSuccess(t *testing.T) {
api.SeccompPodAnnotationKey: "foo", 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 { successCases := map[string]struct {
pod *api.Pod pod *api.Pod
psp *extensions.PodSecurityPolicy psp *extensions.PodSecurityPolicy
...@@ -653,6 +709,22 @@ func TestValidatePodSecurityContextSuccess(t *testing.T) { ...@@ -653,6 +709,22 @@ func TestValidatePodSecurityContextSuccess(t *testing.T) {
pod: seccompPod, pod: seccompPod,
psp: seccompPSP, 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 { for k, v := range successCases {
......
...@@ -32,6 +32,12 @@ import "k8s.io/apimachinery/pkg/util/intstr/generated.proto"; ...@@ -32,6 +32,12 @@ import "k8s.io/apimachinery/pkg/util/intstr/generated.proto";
// Package-wide variables from generator "generated". // Package-wide variables from generator "generated".
option go_package = "v1beta1"; option go_package = "v1beta1";
// AllowedFlexVolume represents a single Flexvolume that is allowed to be used.
message AllowedFlexVolume {
// Driver is the name of the Flexvolume driver.
optional string driver = 1;
}
// defines the host volume conditions that will be enabled by a policy // defines the host volume conditions that will be enabled by a policy
// for pods to use. It requires the path prefix to be defined. // for pods to use. It requires the path prefix to be defined.
message AllowedHostPath { message AllowedHostPath {
...@@ -843,6 +849,12 @@ message PodSecurityPolicySpec { ...@@ -843,6 +849,12 @@ message PodSecurityPolicySpec {
// is a white list of allowed host paths. Empty indicates that all host paths may be used. // is a white list of allowed host paths. Empty indicates that all host paths may be used.
// +optional // +optional
repeated AllowedHostPath allowedHostPaths = 17; repeated AllowedHostPath allowedHostPaths = 17;
// 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
repeated AllowedFlexVolume allowedFlexVolumes = 18;
} }
// DEPRECATED - This group version of ReplicaSet is deprecated by apps/v1beta2/ReplicaSet. See the release notes for // DEPRECATED - This group version of ReplicaSet is deprecated by apps/v1beta2/ReplicaSet. See the release notes for
......
...@@ -938,6 +938,11 @@ type PodSecurityPolicySpec struct { ...@@ -938,6 +938,11 @@ type PodSecurityPolicySpec struct {
// is a white list of allowed host paths. Empty indicates that all host paths may be used. // is a white list of allowed host paths. Empty indicates that all host paths may be used.
// +optional // +optional
AllowedHostPaths []AllowedHostPath `json:"allowedHostPaths,omitempty" protobuf:"bytes,17,rep,name=allowedHostPaths"` 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 // defines the host volume conditions that will be enabled by a policy
...@@ -981,6 +986,12 @@ var ( ...@@ -981,6 +986,12 @@ var (
All FSType = "*" 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 // 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. // for pods to use. It requires both the start and end to be defined.
type HostPortRange struct { type HostPortRange struct {
......
...@@ -27,6 +27,15 @@ package v1beta1 ...@@ -27,6 +27,15 @@ package v1beta1
// Those methods can be generated by using hack/update-generated-swagger-docs.sh // Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE // AUTO-GENERATED FUNCTIONS START HERE
var map_AllowedFlexVolume = map[string]string{
"": "AllowedFlexVolume represents a single Flexvolume that is allowed to be used.",
"driver": "Driver is the name of the Flexvolume driver.",
}
func (AllowedFlexVolume) SwaggerDoc() map[string]string {
return map_AllowedFlexVolume
}
var map_AllowedHostPath = map[string]string{ var map_AllowedHostPath = map[string]string{
"": "defines the host volume conditions that will be enabled by a policy for pods to use. It requires the path prefix to be defined.", "": "defines the host volume conditions that will be enabled by a policy for pods to use. It requires the path prefix to be defined.",
"pathPrefix": "is the path prefix that the host volume must match. It does not support `*`. Trailing slashes are trimmed when validating the path prefix with a host path.\n\nExamples: `/foo` would allow `/foo`, `/foo/` and `/foo/bar` `/foo` would not allow `/food` or `/etc/foo`", "pathPrefix": "is the path prefix that the host volume must match. It does not support `*`. Trailing slashes are trimmed when validating the path prefix with a host path.\n\nExamples: `/foo` would allow `/foo`, `/foo/` and `/foo/bar` `/foo` would not allow `/food` or `/etc/foo`",
...@@ -465,6 +474,7 @@ var map_PodSecurityPolicySpec = map[string]string{ ...@@ -465,6 +474,7 @@ var map_PodSecurityPolicySpec = map[string]string{
"defaultAllowPrivilegeEscalation": "DefaultAllowPrivilegeEscalation controls the default setting for whether a process can gain more privileges than its parent process.", "defaultAllowPrivilegeEscalation": "DefaultAllowPrivilegeEscalation controls the default setting for whether a process can gain more privileges than its parent process.",
"allowPrivilegeEscalation": "AllowPrivilegeEscalation determines if a pod can request to allow privilege escalation. If unspecified, defaults to true.", "allowPrivilegeEscalation": "AllowPrivilegeEscalation determines if a pod can request to allow privilege escalation. If unspecified, defaults to true.",
"allowedHostPaths": "is a white list of allowed host paths. Empty indicates that all host paths may be used.", "allowedHostPaths": "is a white list of allowed host paths. Empty indicates that all host paths may be used.",
"allowedFlexVolumes": "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.",
} }
func (PodSecurityPolicySpec) SwaggerDoc() map[string]string { func (PodSecurityPolicySpec) SwaggerDoc() map[string]string {
......
...@@ -28,6 +28,22 @@ import ( ...@@ -28,6 +28,22 @@ import (
) )
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllowedFlexVolume) DeepCopyInto(out *AllowedFlexVolume) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AllowedFlexVolume.
func (in *AllowedFlexVolume) DeepCopy() *AllowedFlexVolume {
if in == nil {
return nil
}
out := new(AllowedFlexVolume)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllowedHostPath) DeepCopyInto(out *AllowedHostPath) { func (in *AllowedHostPath) DeepCopyInto(out *AllowedHostPath) {
*out = *in *out = *in
return return
...@@ -1194,6 +1210,11 @@ func (in *PodSecurityPolicySpec) DeepCopyInto(out *PodSecurityPolicySpec) { ...@@ -1194,6 +1210,11 @@ func (in *PodSecurityPolicySpec) DeepCopyInto(out *PodSecurityPolicySpec) {
*out = make([]AllowedHostPath, len(*in)) *out = make([]AllowedHostPath, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.AllowedFlexVolumes != nil {
in, out := &in.AllowedFlexVolumes, &out.AllowedFlexVolumes
*out = make([]AllowedFlexVolume, len(*in))
copy(*out, *in)
}
return return
} }
......
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