Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
ed36baed
Commit
ed36baed
authored
Aug 19, 2016
by
Dr. Stefan Schimanski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add sysctl PodSecurityPolicy support
parent
bea189e9
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
620 additions
and
7 deletions
+620
-7
helpers.go
pkg/apis/extensions/helpers.go
+37
-0
helpers_test.go
pkg/apis/extensions/helpers_test.go
+62
-0
types.go
pkg/apis/extensions/types.go
+7
-0
validation.go
pkg/apis/extensions/validation/validation.go
+41
-0
validation_test.go
pkg/apis/extensions/validation/validation_test.go
+68
-1
factory.go
pkg/security/podsecuritypolicy/factory.go
+20
-0
provider.go
pkg/security/podsecuritypolicy/provider.go
+2
-0
mustmatchpatterns.go
pkg/security/podsecuritypolicy/sysctl/mustmatchpatterns.go
+92
-0
mustmatchpatterns_test.go
...curity/podsecuritypolicy/sysctl/mustmatchpatterns_test.go
+106
-0
types.go
pkg/security/podsecuritypolicy/sysctl/types.go
+28
-0
types.go
pkg/security/podsecuritypolicy/types.go
+2
-0
admission_test.go
...kg/admission/security/podsecuritypolicy/admission_test.go
+155
-6
No files found.
pkg/apis/extensions/helpers.go
0 → 100644
View file @
ed36baed
/*
Copyright 2016 The Kubernetes Authors.
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
extensions
import
(
"strings"
)
// SysctlsFromPodSecurityPolicyAnnotation parses an annotation value of the key
// SysctlsSecurityPolocyAnnotationKey into a slice of sysctls. An empty slice
// is returned if annotation is the empty string.
func
SysctlsFromPodSecurityPolicyAnnotation
(
annotation
string
)
([]
string
,
error
)
{
if
len
(
annotation
)
==
0
{
return
[]
string
{},
nil
}
return
strings
.
Split
(
annotation
,
","
),
nil
}
// PodAnnotationsFromSysctls creates an annotation value for a slice of Sysctls.
func
PodAnnotationsFromSysctls
(
sysctls
[]
string
)
string
{
return
strings
.
Join
(
sysctls
,
","
)
}
pkg/apis/extensions/helpers_test.go
0 → 100644
View file @
ed36baed
/*
Copyright 2016 The Kubernetes Authors.
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
extensions
import
(
"reflect"
"testing"
)
func
TestPodAnnotationsFromSysctls
(
t
*
testing
.
T
)
{
type
Test
struct
{
sysctls
[]
string
expectedValue
string
}
for
_
,
test
:=
range
[]
Test
{
{
sysctls
:
[]
string
{
"a.b"
},
expectedValue
:
"a.b"
},
{
sysctls
:
[]
string
{
"a.b"
,
"c.d"
},
expectedValue
:
"a.b,c.d"
},
{
sysctls
:
[]
string
{
"a.b"
,
"a.b"
},
expectedValue
:
"a.b,a.b"
},
{
sysctls
:
[]
string
{},
expectedValue
:
""
},
{
sysctls
:
nil
,
expectedValue
:
""
},
}
{
a
:=
PodAnnotationsFromSysctls
(
test
.
sysctls
)
if
a
!=
test
.
expectedValue
{
t
.
Errorf
(
"wrong value for %v: got=%q wanted=%q"
,
test
.
sysctls
,
a
,
test
.
expectedValue
)
}
}
}
func
TestSysctlsFromPodSecurityPolicyAnnotation
(
t
*
testing
.
T
)
{
type
Test
struct
{
expectedValue
[]
string
annotation
string
}
for
_
,
test
:=
range
[]
Test
{
{
annotation
:
"a.b"
,
expectedValue
:
[]
string
{
"a.b"
}},
{
annotation
:
"a.b,c.d"
,
expectedValue
:
[]
string
{
"a.b"
,
"c.d"
}},
{
annotation
:
"a.b,a.b"
,
expectedValue
:
[]
string
{
"a.b"
,
"a.b"
}},
{
annotation
:
""
,
expectedValue
:
[]
string
{}},
}
{
sysctls
,
err
:=
SysctlsFromPodSecurityPolicyAnnotation
(
test
.
annotation
)
if
err
!=
nil
{
t
.
Errorf
(
"error for %q: %v"
,
test
.
annotation
,
err
)
}
if
!
reflect
.
DeepEqual
(
sysctls
,
test
.
expectedValue
)
{
t
.
Errorf
(
"wrong value for %q: got=%v wanted=%v"
,
test
.
annotation
,
sysctls
,
test
.
expectedValue
)
}
}
}
pkg/apis/extensions/types.go
View file @
ed36baed
...
@@ -35,6 +35,13 @@ import (
...
@@ -35,6 +35,13 @@ import (
"k8s.io/kubernetes/pkg/util/intstr"
"k8s.io/kubernetes/pkg/util/intstr"
)
)
const
(
// SysctlsPodSecurityPolicyAnnotationKey represents the key of a whitelist of
// allowed safe and unsafe sysctls in a pod spec. It's a comma-separated list of plain sysctl
// names or sysctl patterns (which end in *). The string "*" matches all sysctls.
SysctlsPodSecurityPolicyAnnotationKey
string
=
"security.alpha.kubernetes.io/sysctls"
)
// describes the attributes of a scale subresource
// describes the attributes of a scale subresource
type
ScaleSpec
struct
{
type
ScaleSpec
struct
{
// desired number of instances for the scaled object.
// desired number of instances for the scaled object.
...
...
pkg/apis/extensions/validation/validation.go
View file @
ed36baed
...
@@ -574,6 +574,7 @@ func ValidatePodSecurityPolicySpec(spec *extensions.PodSecurityPolicySpec, fldPa
...
@@ -574,6 +574,7 @@ func ValidatePodSecurityPolicySpec(spec *extensions.PodSecurityPolicySpec, fldPa
func
ValidatePodSecurityPolicySpecificAnnotations
(
annotations
map
[
string
]
string
,
fldPath
*
field
.
Path
)
field
.
ErrorList
{
func
ValidatePodSecurityPolicySpecificAnnotations
(
annotations
map
[
string
]
string
,
fldPath
*
field
.
Path
)
field
.
ErrorList
{
allErrs
:=
field
.
ErrorList
{}
allErrs
:=
field
.
ErrorList
{}
if
p
:=
annotations
[
apparmor
.
DefaultProfileAnnotationKey
];
p
!=
""
{
if
p
:=
annotations
[
apparmor
.
DefaultProfileAnnotationKey
];
p
!=
""
{
if
err
:=
apparmor
.
ValidateProfileFormat
(
p
);
err
!=
nil
{
if
err
:=
apparmor
.
ValidateProfileFormat
(
p
);
err
!=
nil
{
allErrs
=
append
(
allErrs
,
field
.
Invalid
(
fldPath
.
Key
(
apparmor
.
DefaultProfileAnnotationKey
),
p
,
err
.
Error
()))
allErrs
=
append
(
allErrs
,
field
.
Invalid
(
fldPath
.
Key
(
apparmor
.
DefaultProfileAnnotationKey
),
p
,
err
.
Error
()))
...
@@ -586,6 +587,16 @@ func ValidatePodSecurityPolicySpecificAnnotations(annotations map[string]string,
...
@@ -586,6 +587,16 @@ func ValidatePodSecurityPolicySpecificAnnotations(annotations map[string]string,
}
}
}
}
}
}
sysctlAnnotation
:=
annotations
[
extensions
.
SysctlsPodSecurityPolicyAnnotationKey
]
sysctlFldPath
:=
fldPath
.
Key
(
extensions
.
SysctlsPodSecurityPolicyAnnotationKey
)
sysctls
,
err
:=
extensions
.
SysctlsFromPodSecurityPolicyAnnotation
(
sysctlAnnotation
)
if
err
!=
nil
{
allErrs
=
append
(
allErrs
,
field
.
Invalid
(
sysctlFldPath
,
sysctlAnnotation
,
err
.
Error
()))
}
else
{
allErrs
=
append
(
allErrs
,
validatePodSecurityPolicySysctls
(
sysctlFldPath
,
sysctls
)
...
)
}
return
allErrs
return
allErrs
}
}
...
@@ -674,6 +685,36 @@ func validatePodSecurityPolicyVolumes(fldPath *field.Path, volumes []extensions.
...
@@ -674,6 +685,36 @@ func validatePodSecurityPolicyVolumes(fldPath *field.Path, volumes []extensions.
return
allErrs
return
allErrs
}
}
const
sysctlPatternSegmentFmt
string
=
"([a-z0-9][-_a-z0-9]*)?[a-z0-9*]"
const
SysctlPatternFmt
string
=
"("
+
apivalidation
.
SysctlSegmentFmt
+
"
\\
.)*"
+
sysctlPatternSegmentFmt
var
sysctlPatternRegexp
=
regexp
.
MustCompile
(
"^"
+
SysctlPatternFmt
+
"$"
)
func
IsValidSysctlPattern
(
name
string
)
bool
{
if
len
(
name
)
>
apivalidation
.
SysctlMaxLength
{
return
false
}
return
sysctlPatternRegexp
.
MatchString
(
name
)
}
// validatePodSecurityPolicySysctls validates the sysctls fields of PodSecurityPolicy.
func
validatePodSecurityPolicySysctls
(
fldPath
*
field
.
Path
,
sysctls
[]
string
)
field
.
ErrorList
{
allErrs
:=
field
.
ErrorList
{}
for
i
,
s
:=
range
sysctls
{
if
!
IsValidSysctlPattern
(
string
(
s
))
{
allErrs
=
append
(
allErrs
,
field
.
Invalid
(
fldPath
.
Index
(
i
),
sysctls
[
i
],
fmt
.
Sprintf
(
"must have at most %d characters and match regex %s"
,
apivalidation
.
SysctlMaxLength
,
SysctlPatternFmt
,
)),
)
}
}
return
allErrs
}
// validateIDRanges ensures the range is valid.
// validateIDRanges ensures the range is valid.
func
validateIDRanges
(
fldPath
*
field
.
Path
,
rng
extensions
.
IDRange
)
field
.
ErrorList
{
func
validateIDRanges
(
fldPath
*
field
.
Path
,
rng
extensions
.
IDRange
)
field
.
ErrorList
{
allErrs
:=
field
.
ErrorList
{}
allErrs
:=
field
.
ErrorList
{}
...
...
pkg/apis/extensions/validation/validation_test.go
View file @
ed36baed
...
@@ -1512,7 +1512,8 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
...
@@ -1512,7 +1512,8 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
validPSP
:=
func
()
*
extensions
.
PodSecurityPolicy
{
validPSP
:=
func
()
*
extensions
.
PodSecurityPolicy
{
return
&
extensions
.
PodSecurityPolicy
{
return
&
extensions
.
PodSecurityPolicy
{
ObjectMeta
:
api
.
ObjectMeta
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"foo"
,
Name
:
"foo"
,
Annotations
:
map
[
string
]
string
{},
},
},
Spec
:
extensions
.
PodSecurityPolicySpec
{
Spec
:
extensions
.
PodSecurityPolicySpec
{
SELinux
:
extensions
.
SELinuxStrategyOptions
{
SELinux
:
extensions
.
SELinuxStrategyOptions
{
...
@@ -1596,6 +1597,9 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
...
@@ -1596,6 +1597,9 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
apparmor
.
AllowedProfilesAnnotationKey
:
apparmor
.
ProfileRuntimeDefault
+
",not-good"
,
apparmor
.
AllowedProfilesAnnotationKey
:
apparmor
.
ProfileRuntimeDefault
+
",not-good"
,
}
}
invalidSysctlPattern
:=
validPSP
()
invalidSysctlPattern
.
Annotations
[
extensions
.
SysctlsPodSecurityPolicyAnnotationKey
]
=
"a.*.b"
errorCases
:=
map
[
string
]
struct
{
errorCases
:=
map
[
string
]
struct
{
psp
*
extensions
.
PodSecurityPolicy
psp
*
extensions
.
PodSecurityPolicy
errorType
field
.
ErrorType
errorType
field
.
ErrorType
...
@@ -1686,6 +1690,11 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
...
@@ -1686,6 +1690,11 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
errorType
:
field
.
ErrorTypeInvalid
,
errorType
:
field
.
ErrorTypeInvalid
,
errorDetail
:
"invalid AppArmor profile name:
\"
not-good
\"
"
,
errorDetail
:
"invalid AppArmor profile name:
\"
not-good
\"
"
,
},
},
"invalid sysctl pattern"
:
{
psp
:
invalidSysctlPattern
,
errorType
:
field
.
ErrorTypeInvalid
,
errorDetail
:
fmt
.
Sprintf
(
"must have at most 253 characters and match regex %s"
,
SysctlPatternFmt
),
},
}
}
for
k
,
v
:=
range
errorCases
{
for
k
,
v
:=
range
errorCases
{
...
@@ -1728,6 +1737,9 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
...
@@ -1728,6 +1737,9 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
apparmor
.
AllowedProfilesAnnotationKey
:
apparmor
.
ProfileRuntimeDefault
+
","
+
apparmor
.
ProfileNamePrefix
+
"foo"
,
apparmor
.
AllowedProfilesAnnotationKey
:
apparmor
.
ProfileRuntimeDefault
+
","
+
apparmor
.
ProfileNamePrefix
+
"foo"
,
}
}
withSysctl
:=
validPSP
()
withSysctl
.
Annotations
[
extensions
.
SysctlsPodSecurityPolicyAnnotationKey
]
=
"net.*"
successCases
:=
map
[
string
]
struct
{
successCases
:=
map
[
string
]
struct
{
psp
*
extensions
.
PodSecurityPolicy
psp
*
extensions
.
PodSecurityPolicy
}{
}{
...
@@ -1749,6 +1761,9 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
...
@@ -1749,6 +1761,9 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
"valid AppArmor annotations"
:
{
"valid AppArmor annotations"
:
{
psp
:
validAppArmor
,
psp
:
validAppArmor
,
},
},
"with network sysctls"
:
{
psp
:
withSysctl
,
},
}
}
for
k
,
v
:=
range
successCases
{
for
k
,
v
:=
range
successCases
{
...
@@ -2031,6 +2046,58 @@ func TestValidateNetworkPolicyUpdate(t *testing.T) {
...
@@ -2031,6 +2046,58 @@ func TestValidateNetworkPolicyUpdate(t *testing.T) {
}
}
}
}
func
TestIsValidSysctlPattern
(
t
*
testing
.
T
)
{
valid
:=
[]
string
{
"a.b.c.d"
,
"a"
,
"a_b"
,
"a-b"
,
"abc"
,
"abc.def"
,
"*"
,
"a.*"
,
"*"
,
"abc*"
,
"a.abc*"
,
"a.b.*"
,
}
invalid
:=
[]
string
{
""
,
"ä"
,
"a_"
,
"_"
,
"_a"
,
"_a._b"
,
"__"
,
"-"
,
"."
,
"a."
,
".a"
,
"a.b."
,
"a*.b"
,
"a*b"
,
"*a"
,
"Abc"
,
func
(
n
int
)
string
{
x
:=
make
([]
byte
,
n
)
for
i
:=
range
x
{
x
[
i
]
=
byte
(
'a'
)
}
return
string
(
x
)
}(
256
),
}
for
_
,
s
:=
range
valid
{
if
!
IsValidSysctlPattern
(
s
)
{
t
.
Errorf
(
"%q expected to be a valid sysctl pattern"
,
s
)
}
}
for
_
,
s
:=
range
invalid
{
if
IsValidSysctlPattern
(
s
)
{
t
.
Errorf
(
"%q expected to be an invalid sysctl pattern"
,
s
)
}
}
}
func
newBool
(
val
bool
)
*
bool
{
func
newBool
(
val
bool
)
*
bool
{
p
:=
new
(
bool
)
p
:=
new
(
bool
)
*
p
=
val
*
p
=
val
...
...
pkg/security/podsecuritypolicy/factory.go
View file @
ed36baed
...
@@ -25,6 +25,7 @@ import (
...
@@ -25,6 +25,7 @@ import (
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/group"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/group"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/sysctl"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/user"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/user"
"k8s.io/kubernetes/pkg/util/errors"
"k8s.io/kubernetes/pkg/util/errors"
)
)
...
@@ -70,6 +71,19 @@ func (f *simpleStrategyFactory) CreateStrategies(psp *extensions.PodSecurityPoli
...
@@ -70,6 +71,19 @@ func (f *simpleStrategyFactory) CreateStrategies(psp *extensions.PodSecurityPoli
errs
=
append
(
errs
,
err
)
errs
=
append
(
errs
,
err
)
}
}
var
unsafeSysctls
[]
string
if
ann
,
found
:=
psp
.
Annotations
[
extensions
.
SysctlsPodSecurityPolicyAnnotationKey
];
found
{
var
err
error
unsafeSysctls
,
err
=
extensions
.
SysctlsFromPodSecurityPolicyAnnotation
(
ann
)
if
err
!=
nil
{
errs
=
append
(
errs
,
err
)
}
}
sysctlsStrat
,
err
:=
createSysctlsStrategy
(
unsafeSysctls
)
if
err
!=
nil
{
errs
=
append
(
errs
,
err
)
}
if
len
(
errs
)
>
0
{
if
len
(
errs
)
>
0
{
return
nil
,
errors
.
NewAggregate
(
errs
)
return
nil
,
errors
.
NewAggregate
(
errs
)
}
}
...
@@ -81,6 +95,7 @@ func (f *simpleStrategyFactory) CreateStrategies(psp *extensions.PodSecurityPoli
...
@@ -81,6 +95,7 @@ func (f *simpleStrategyFactory) CreateStrategies(psp *extensions.PodSecurityPoli
FSGroupStrategy
:
fsGroupStrat
,
FSGroupStrategy
:
fsGroupStrat
,
SupplementalGroupStrategy
:
supGroupStrat
,
SupplementalGroupStrategy
:
supGroupStrat
,
CapabilitiesStrategy
:
capStrat
,
CapabilitiesStrategy
:
capStrat
,
SysctlsStrategy
:
sysctlsStrat
,
}
}
return
strategies
,
nil
return
strategies
,
nil
...
@@ -145,3 +160,8 @@ func createSupplementalGroupStrategy(opts *extensions.SupplementalGroupsStrategy
...
@@ -145,3 +160,8 @@ func createSupplementalGroupStrategy(opts *extensions.SupplementalGroupsStrategy
func
createCapabilitiesStrategy
(
defaultAddCaps
,
requiredDropCaps
,
allowedCaps
[]
api
.
Capability
)
(
capabilities
.
Strategy
,
error
)
{
func
createCapabilitiesStrategy
(
defaultAddCaps
,
requiredDropCaps
,
allowedCaps
[]
api
.
Capability
)
(
capabilities
.
Strategy
,
error
)
{
return
capabilities
.
NewDefaultCapabilities
(
defaultAddCaps
,
requiredDropCaps
,
allowedCaps
)
return
capabilities
.
NewDefaultCapabilities
(
defaultAddCaps
,
requiredDropCaps
,
allowedCaps
)
}
}
// createSysctlsStrategy creates a new unsafe sysctls strategy.
func
createSysctlsStrategy
(
sysctlsPatterns
[]
string
)
(
sysctl
.
SysctlsStrategy
,
error
)
{
return
sysctl
.
NewMustMatchPatterns
(
sysctlsPatterns
)
}
pkg/security/podsecuritypolicy/provider.go
View file @
ed36baed
...
@@ -210,6 +210,8 @@ func (s *simpleProvider) ValidatePodSecurityContext(pod *api.Pod, fldPath *field
...
@@ -210,6 +210,8 @@ func (s *simpleProvider) ValidatePodSecurityContext(pod *api.Pod, fldPath *field
allErrs
=
append
(
allErrs
,
field
.
Invalid
(
fldPath
.
Child
(
"hostIPC"
),
pod
.
Spec
.
SecurityContext
.
HostIPC
,
"Host IPC is not allowed to be used"
))
allErrs
=
append
(
allErrs
,
field
.
Invalid
(
fldPath
.
Child
(
"hostIPC"
),
pod
.
Spec
.
SecurityContext
.
HostIPC
,
"Host IPC is not allowed to be used"
))
}
}
allErrs
=
append
(
allErrs
,
s
.
strategies
.
SysctlsStrategy
.
Validate
(
pod
)
...
)
return
allErrs
return
allErrs
}
}
...
...
pkg/security/podsecuritypolicy/sysctl/mustmatchpatterns.go
0 → 100644
View file @
ed36baed
/*
Copyright 2016 The Kubernetes Authors.
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
sysctl
import
(
"fmt"
"strings"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// mustMatchPatterns implements the CapabilitiesStrategy interface
type
mustMatchPatterns
struct
{
patterns
[]
string
}
var
(
_
SysctlsStrategy
=
&
mustMatchPatterns
{}
defaultSysctlsPatterns
=
[]
string
{
"*"
}
)
// NewMustMatchPatterns creates a new mustMatchPattern strategy that will provide validation.
// Passing nil means the default pattern, passing an empty list means to disallow all sysctls.
func
NewMustMatchPatterns
(
patterns
[]
string
)
(
SysctlsStrategy
,
error
)
{
if
patterns
==
nil
{
patterns
=
defaultSysctlsPatterns
}
return
&
mustMatchPatterns
{
patterns
:
patterns
,
},
nil
}
// Validate ensures that the specified values fall within the range of the strategy.
func
(
s
*
mustMatchPatterns
)
Validate
(
pod
*
api
.
Pod
)
field
.
ErrorList
{
allErrs
:=
field
.
ErrorList
{}
allErrs
=
append
(
allErrs
,
s
.
validateAnnotation
(
pod
,
api
.
SysctlsPodAnnotationKey
)
...
)
allErrs
=
append
(
allErrs
,
s
.
validateAnnotation
(
pod
,
api
.
UnsafeSysctlsPodAnnotationKey
)
...
)
return
allErrs
}
func
(
s
*
mustMatchPatterns
)
validateAnnotation
(
pod
*
api
.
Pod
,
key
string
)
field
.
ErrorList
{
allErrs
:=
field
.
ErrorList
{}
fieldPath
:=
field
.
NewPath
(
"pod"
,
"metadata"
,
"annotations"
)
.
Key
(
key
)
sysctls
,
err
:=
api
.
SysctlsFromPodAnnotation
(
pod
.
Annotations
[
key
])
if
err
!=
nil
{
allErrs
=
append
(
allErrs
,
field
.
Invalid
(
fieldPath
,
pod
.
Annotations
[
key
],
err
.
Error
()))
}
if
len
(
sysctls
)
>
0
{
if
len
(
s
.
patterns
)
==
0
{
allErrs
=
append
(
allErrs
,
field
.
Invalid
(
fieldPath
,
pod
.
Annotations
[
key
],
"sysctls are not allowed"
))
}
else
{
for
i
,
sysctl
:=
range
sysctls
{
allErrs
=
append
(
allErrs
,
s
.
ValidateSysctl
(
sysctl
.
Name
,
fieldPath
.
Index
(
i
))
...
)
}
}
}
return
allErrs
}
func
(
s
*
mustMatchPatterns
)
ValidateSysctl
(
sysctlName
string
,
fldPath
*
field
.
Path
)
field
.
ErrorList
{
for
_
,
s
:=
range
s
.
patterns
{
if
s
[
len
(
s
)
-
1
]
==
'*'
{
prefix
:=
s
[
:
len
(
s
)
-
1
]
if
strings
.
HasPrefix
(
sysctlName
,
string
(
prefix
))
{
return
nil
}
}
else
if
sysctlName
==
s
{
return
nil
}
}
return
field
.
ErrorList
{
field
.
Forbidden
(
fldPath
,
fmt
.
Sprintf
(
"sysctl %q is not allowed"
,
sysctlName
))}
}
pkg/security/podsecuritypolicy/sysctl/mustmatchpatterns_test.go
0 → 100644
View file @
ed36baed
/*
Copyright 2016 The Kubernetes Authors.
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
sysctl
import
(
"testing"
"k8s.io/kubernetes/pkg/api"
)
func
TestValidate
(
t
*
testing
.
T
)
{
tests
:=
map
[
string
]
struct
{
patterns
[]
string
allowed
[]
string
disallowed
[]
string
}{
// no container requests
"nil"
:
{
patterns
:
nil
,
allowed
:
[]
string
{
"foo"
},
},
"empty"
:
{
patterns
:
[]
string
{},
disallowed
:
[]
string
{
"foo"
},
},
"without wildcard"
:
{
patterns
:
[]
string
{
"a"
,
"a.b"
},
allowed
:
[]
string
{
"a"
,
"a.b"
},
disallowed
:
[]
string
{
"b"
},
},
"with catch-all wildcard"
:
{
patterns
:
[]
string
{
"*"
},
allowed
:
[]
string
{
"a"
,
"a.b"
},
},
"with catch-all wildcard and non-wildcard"
:
{
patterns
:
[]
string
{
"a.b.c"
,
"*"
},
allowed
:
[]
string
{
"a"
,
"a.b"
,
"a.b.c"
,
"b"
},
},
"without catch-all wildcard"
:
{
patterns
:
[]
string
{
"a.*"
,
"b.*"
,
"c.d.e"
,
"d.e.f.*"
},
allowed
:
[]
string
{
"a.b"
,
"b.c"
,
"c.d.e"
,
"d.e.f.g.h"
},
disallowed
:
[]
string
{
"a"
,
"b"
,
"c"
,
"c.d"
,
"d.e"
,
"d.e.f"
},
},
}
for
k
,
v
:=
range
tests
{
strategy
,
err
:=
NewMustMatchPatterns
(
v
.
patterns
)
if
err
!=
nil
{
t
.
Errorf
(
"%s failed: %v"
,
k
,
err
)
continue
}
pod
:=
&
api
.
Pod
{}
errs
:=
strategy
.
Validate
(
pod
)
if
len
(
errs
)
!=
0
{
t
.
Errorf
(
"%s: unexpected validaton errors for empty sysctls: %v"
,
k
,
errs
)
}
sysctls
:=
[]
api
.
Sysctl
{}
for
_
,
s
:=
range
v
.
allowed
{
sysctls
=
append
(
sysctls
,
api
.
Sysctl
{
Name
:
s
,
Value
:
"dummy"
,
})
}
testAllowed
:=
func
(
key
string
,
category
string
)
{
pod
.
Annotations
=
map
[
string
]
string
{
key
:
api
.
PodAnnotationsFromSysctls
(
sysctls
),
}
errs
=
strategy
.
Validate
(
pod
)
if
len
(
errs
)
!=
0
{
t
.
Errorf
(
"%s: unexpected validaton errors for %s sysctls: %v"
,
k
,
category
,
errs
)
}
}
testDisallowed
:=
func
(
key
string
,
category
string
)
{
for
_
,
s
:=
range
v
.
disallowed
{
pod
.
Annotations
=
map
[
string
]
string
{
key
:
api
.
PodAnnotationsFromSysctls
([]
api
.
Sysctl
{{
s
,
"dummy"
}}),
}
errs
=
strategy
.
Validate
(
pod
)
if
len
(
errs
)
==
0
{
t
.
Errorf
(
"%s: expected error for %s sysctl %q"
,
k
,
category
,
s
)
}
}
}
testAllowed
(
api
.
SysctlsPodAnnotationKey
,
"safe"
)
testAllowed
(
api
.
UnsafeSysctlsPodAnnotationKey
,
"unsafe"
)
testDisallowed
(
api
.
SysctlsPodAnnotationKey
,
"safe"
)
testDisallowed
(
api
.
UnsafeSysctlsPodAnnotationKey
,
"unsafe"
)
}
}
pkg/security/podsecuritypolicy/sysctl/types.go
0 → 100644
View file @
ed36baed
/*
Copyright 2016 The Kubernetes Authors.
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
sysctl
import
(
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// SysctlsStrategy defines the interface for all sysctl strategies.
type
SysctlsStrategy
interface
{
// Validate ensures that the specified values fall within the range of the strategy.
Validate
(
pod
*
api
.
Pod
)
field
.
ErrorList
}
pkg/security/podsecuritypolicy/types.go
View file @
ed36baed
...
@@ -23,6 +23,7 @@ import (
...
@@ -23,6 +23,7 @@ import (
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/group"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/group"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/sysctl"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/user"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/user"
"k8s.io/kubernetes/pkg/util/validation/field"
"k8s.io/kubernetes/pkg/util/validation/field"
)
)
...
@@ -63,4 +64,5 @@ type ProviderStrategies struct {
...
@@ -63,4 +64,5 @@ type ProviderStrategies struct {
FSGroupStrategy
group
.
GroupStrategy
FSGroupStrategy
group
.
GroupStrategy
SupplementalGroupStrategy
group
.
GroupStrategy
SupplementalGroupStrategy
group
.
GroupStrategy
CapabilitiesStrategy
capabilities
.
Strategy
CapabilitiesStrategy
capabilities
.
Strategy
SysctlsStrategy
sysctl
.
SysctlsStrategy
}
}
plugin/pkg/admission/security/podsecuritypolicy/admission_test.go
View file @
ed36baed
...
@@ -26,7 +26,7 @@ import (
...
@@ -26,7 +26,7 @@ import (
kadmission
"k8s.io/kubernetes/pkg/admission"
kadmission
"k8s.io/kubernetes/pkg/admission"
kapi
"k8s.io/kubernetes/pkg/api"
kapi
"k8s.io/kubernetes/pkg/api"
extensions
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/auth/user"
"k8s.io/kubernetes/pkg/auth/user"
"k8s.io/kubernetes/pkg/client/cache"
"k8s.io/kubernetes/pkg/client/cache"
clientset
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
clientset
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
...
@@ -34,7 +34,7 @@ import (
...
@@ -34,7 +34,7 @@ import (
"k8s.io/kubernetes/pkg/security/apparmor"
"k8s.io/kubernetes/pkg/security/apparmor"
kpsp
"k8s.io/kubernetes/pkg/security/podsecuritypolicy"
kpsp
"k8s.io/kubernetes/pkg/security/podsecuritypolicy"
psputil
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
psputil
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
diff
"k8s.io/kubernetes/pkg/util/diff"
"k8s.io/kubernetes/pkg/util/diff"
)
)
const
defaultContainerName
=
"test-c"
const
defaultContainerName
=
"test-c"
...
@@ -1028,6 +1028,151 @@ func TestAdmitReadOnlyRootFilesystem(t *testing.T) {
...
@@ -1028,6 +1028,151 @@ func TestAdmitReadOnlyRootFilesystem(t *testing.T) {
}
}
}
}
func
TestAdmitSysctls
(
t
*
testing
.
T
)
{
podWithSysctls
:=
func
(
safeSysctls
[]
string
,
unsafeSysctls
[]
string
)
*
kapi
.
Pod
{
pod
:=
goodPod
()
dummySysctls
:=
func
(
names
[]
string
)
[]
kapi
.
Sysctl
{
sysctls
:=
make
([]
kapi
.
Sysctl
,
len
(
names
))
for
i
,
n
:=
range
names
{
sysctls
[
i
]
.
Name
=
n
sysctls
[
i
]
.
Value
=
"dummy"
}
return
sysctls
}
pod
.
Annotations
[
kapi
.
SysctlsPodAnnotationKey
]
=
kapi
.
PodAnnotationsFromSysctls
(
dummySysctls
(
safeSysctls
))
pod
.
Annotations
[
kapi
.
UnsafeSysctlsPodAnnotationKey
]
=
kapi
.
PodAnnotationsFromSysctls
(
dummySysctls
(
unsafeSysctls
))
return
pod
}
noSysctls
:=
restrictivePSP
()
noSysctls
.
Name
=
"no sysctls"
emptySysctls
:=
restrictivePSP
()
emptySysctls
.
Name
=
"empty sysctls"
emptySysctls
.
Annotations
[
extensions
.
SysctlsPodSecurityPolicyAnnotationKey
]
=
""
mixedSysctls
:=
restrictivePSP
()
mixedSysctls
.
Name
=
"wildcard sysctls"
mixedSysctls
.
Annotations
[
extensions
.
SysctlsPodSecurityPolicyAnnotationKey
]
=
"a.*,b.*,c,d.e.f"
aSysctl
:=
restrictivePSP
()
aSysctl
.
Name
=
"a sysctl"
aSysctl
.
Annotations
[
extensions
.
SysctlsPodSecurityPolicyAnnotationKey
]
=
"a"
bSysctl
:=
restrictivePSP
()
bSysctl
.
Name
=
"b sysctl"
bSysctl
.
Annotations
[
extensions
.
SysctlsPodSecurityPolicyAnnotationKey
]
=
"b"
cSysctl
:=
restrictivePSP
()
cSysctl
.
Name
=
"c sysctl"
cSysctl
.
Annotations
[
extensions
.
SysctlsPodSecurityPolicyAnnotationKey
]
=
"c"
catchallSysctls
:=
restrictivePSP
()
catchallSysctls
.
Name
=
"catchall sysctl"
catchallSysctls
.
Annotations
[
extensions
.
SysctlsPodSecurityPolicyAnnotationKey
]
=
"*"
tests
:=
map
[
string
]
struct
{
pod
*
kapi
.
Pod
psps
[]
*
extensions
.
PodSecurityPolicy
shouldPass
bool
expectedPSP
string
}{
"pod without unsafe sysctls request allowed under noSysctls PSP"
:
{
pod
:
goodPod
(),
psps
:
[]
*
extensions
.
PodSecurityPolicy
{
noSysctls
},
shouldPass
:
true
,
expectedPSP
:
noSysctls
.
Name
,
},
"pod without any sysctls request allowed under emptySysctls PSP"
:
{
pod
:
goodPod
(),
psps
:
[]
*
extensions
.
PodSecurityPolicy
{
emptySysctls
},
shouldPass
:
true
,
expectedPSP
:
emptySysctls
.
Name
,
},
"pod with safe sysctls request allowed under noSysctls PSP"
:
{
pod
:
podWithSysctls
([]
string
{
"a"
,
"b"
},
[]
string
{}),
psps
:
[]
*
extensions
.
PodSecurityPolicy
{
noSysctls
},
shouldPass
:
true
,
expectedPSP
:
noSysctls
.
Name
,
},
"pod with unsafe sysctls request allowed under noSysctls PSP"
:
{
pod
:
podWithSysctls
([]
string
{},
[]
string
{
"a"
,
"b"
}),
psps
:
[]
*
extensions
.
PodSecurityPolicy
{
noSysctls
},
shouldPass
:
true
,
expectedPSP
:
noSysctls
.
Name
,
},
"pod with safe sysctls request disallowed under emptySysctls PSP"
:
{
pod
:
podWithSysctls
([]
string
{
"a"
,
"b"
},
[]
string
{}),
psps
:
[]
*
extensions
.
PodSecurityPolicy
{
emptySysctls
},
shouldPass
:
false
,
},
"pod with unsafe sysctls request disallowed under emptySysctls PSP"
:
{
pod
:
podWithSysctls
([]
string
{},
[]
string
{
"a"
,
"b"
}),
psps
:
[]
*
extensions
.
PodSecurityPolicy
{
emptySysctls
},
shouldPass
:
false
,
},
"pod with matching sysctls request allowed under mixedSysctls PSP"
:
{
pod
:
podWithSysctls
([]
string
{
"a.b"
,
"b.c"
},
[]
string
{
"c"
,
"d.e.f"
}),
psps
:
[]
*
extensions
.
PodSecurityPolicy
{
mixedSysctls
},
shouldPass
:
true
,
expectedPSP
:
mixedSysctls
.
Name
,
},
"pod with not-matching unsafe sysctls request allowed under mixedSysctls PSP"
:
{
pod
:
podWithSysctls
([]
string
{
"a.b"
,
"b.c"
,
"c"
,
"d.e.f"
},
[]
string
{
"e"
}),
psps
:
[]
*
extensions
.
PodSecurityPolicy
{
mixedSysctls
},
shouldPass
:
false
,
},
"pod with not-matching safe sysctls request allowed under mixedSysctls PSP"
:
{
pod
:
podWithSysctls
([]
string
{
"a.b"
,
"b.c"
,
"c"
,
"d.e.f"
,
"e"
},
[]
string
{}),
psps
:
[]
*
extensions
.
PodSecurityPolicy
{
mixedSysctls
},
shouldPass
:
false
,
},
"pod with sysctls request allowed under catchallSysctls PSP"
:
{
pod
:
podWithSysctls
([]
string
{
"e"
},
[]
string
{
"f"
}),
psps
:
[]
*
extensions
.
PodSecurityPolicy
{
catchallSysctls
},
shouldPass
:
true
,
expectedPSP
:
catchallSysctls
.
Name
,
},
"pod with sysctls request allowed under catchallSysctls PSP, not under mixedSysctls or emptySysctls PSP"
:
{
pod
:
podWithSysctls
([]
string
{
"e"
},
[]
string
{
"f"
}),
psps
:
[]
*
extensions
.
PodSecurityPolicy
{
mixedSysctls
,
catchallSysctls
,
emptySysctls
},
shouldPass
:
true
,
expectedPSP
:
catchallSysctls
.
Name
,
},
"pod with safe c sysctl request allowed under cSysctl PSP, not under aSysctl or bSysctl PSP"
:
{
pod
:
podWithSysctls
([]
string
{},
[]
string
{
"c"
}),
psps
:
[]
*
extensions
.
PodSecurityPolicy
{
aSysctl
,
bSysctl
,
cSysctl
},
shouldPass
:
true
,
expectedPSP
:
cSysctl
.
Name
,
},
"pod with unsafe c sysctl request allowed under cSysctl PSP, not under aSysctl or bSysctl PSP"
:
{
pod
:
podWithSysctls
([]
string
{
"c"
},
[]
string
{}),
psps
:
[]
*
extensions
.
PodSecurityPolicy
{
aSysctl
,
bSysctl
,
cSysctl
},
shouldPass
:
true
,
expectedPSP
:
cSysctl
.
Name
,
},
}
for
k
,
v
:=
range
tests
{
origSafeSysctls
,
origUnsafeSysctls
,
err
:=
kapi
.
SysctlsFromPodAnnotations
(
v
.
pod
.
Annotations
)
if
err
!=
nil
{
t
.
Fatalf
(
"invalid sysctl annotation: %v"
,
err
)
}
testPSPAdmit
(
k
,
v
.
psps
,
v
.
pod
,
v
.
shouldPass
,
v
.
expectedPSP
,
t
)
if
v
.
shouldPass
{
safeSysctls
,
unsafeSysctls
,
_
:=
kapi
.
SysctlsFromPodAnnotations
(
v
.
pod
.
Annotations
)
if
!
reflect
.
DeepEqual
(
safeSysctls
,
origSafeSysctls
)
{
t
.
Errorf
(
"%s: wrong safe sysctls: expected=%v, got=%v"
,
k
,
origSafeSysctls
,
safeSysctls
)
}
if
!
reflect
.
DeepEqual
(
unsafeSysctls
,
origUnsafeSysctls
)
{
t
.
Errorf
(
"%s: wrong unsafe sysctls: expected=%v, got=%v"
,
k
,
origSafeSysctls
,
safeSysctls
)
}
}
}
}
func
testPSPAdmit
(
testCaseName
string
,
psps
[]
*
extensions
.
PodSecurityPolicy
,
pod
*
kapi
.
Pod
,
shouldPass
bool
,
expectedPSP
string
,
t
*
testing
.
T
)
{
func
testPSPAdmit
(
testCaseName
string
,
psps
[]
*
extensions
.
PodSecurityPolicy
,
pod
*
kapi
.
Pod
,
shouldPass
bool
,
expectedPSP
string
,
t
*
testing
.
T
)
{
namespace
:=
createNamespaceForTest
()
namespace
:=
createNamespaceForTest
()
serviceAccount
:=
createSAForTest
()
serviceAccount
:=
createSAForTest
()
...
@@ -1044,17 +1189,17 @@ func testPSPAdmit(testCaseName string, psps []*extensions.PodSecurityPolicy, pod
...
@@ -1044,17 +1189,17 @@ func testPSPAdmit(testCaseName string, psps []*extensions.PodSecurityPolicy, pod
err
:=
plugin
.
Admit
(
attrs
)
err
:=
plugin
.
Admit
(
attrs
)
if
shouldPass
&&
err
!=
nil
{
if
shouldPass
&&
err
!=
nil
{
t
.
Errorf
(
"%s expected no errors but received %v"
,
testCaseName
,
err
)
t
.
Errorf
(
"%s
:
expected no errors but received %v"
,
testCaseName
,
err
)
}
}
if
shouldPass
&&
err
==
nil
{
if
shouldPass
&&
err
==
nil
{
if
pod
.
Annotations
[
psputil
.
ValidatedPSPAnnotation
]
!=
expectedPSP
{
if
pod
.
Annotations
[
psputil
.
ValidatedPSPAnnotation
]
!=
expectedPSP
{
t
.
Errorf
(
"%s expected to validate under %s but found %s"
,
testCaseName
,
expectedPSP
,
pod
.
Annotations
[
psputil
.
ValidatedPSPAnnotation
])
t
.
Errorf
(
"%s
:
expected to validate under %s but found %s"
,
testCaseName
,
expectedPSP
,
pod
.
Annotations
[
psputil
.
ValidatedPSPAnnotation
])
}
}
}
}
if
!
shouldPass
&&
err
==
nil
{
if
!
shouldPass
&&
err
==
nil
{
t
.
Errorf
(
"%s expected errors but received none"
,
testCaseName
)
t
.
Errorf
(
"%s
:
expected errors but received none"
,
testCaseName
)
}
}
}
}
...
@@ -1238,7 +1383,8 @@ func TestCreateProvidersFromConstraints(t *testing.T) {
...
@@ -1238,7 +1383,8 @@ func TestCreateProvidersFromConstraints(t *testing.T) {
func
restrictivePSP
()
*
extensions
.
PodSecurityPolicy
{
func
restrictivePSP
()
*
extensions
.
PodSecurityPolicy
{
return
&
extensions
.
PodSecurityPolicy
{
return
&
extensions
.
PodSecurityPolicy
{
ObjectMeta
:
kapi
.
ObjectMeta
{
ObjectMeta
:
kapi
.
ObjectMeta
{
Name
:
"restrictive"
,
Name
:
"restrictive"
,
Annotations
:
map
[
string
]
string
{},
},
},
Spec
:
extensions
.
PodSecurityPolicySpec
{
Spec
:
extensions
.
PodSecurityPolicySpec
{
RunAsUser
:
extensions
.
RunAsUserStrategyOptions
{
RunAsUser
:
extensions
.
RunAsUserStrategyOptions
{
...
@@ -1291,6 +1437,9 @@ func createSAForTest() *kapi.ServiceAccount {
...
@@ -1291,6 +1437,9 @@ func createSAForTest() *kapi.ServiceAccount {
// psp when defaults are filled in.
// psp when defaults are filled in.
func
goodPod
()
*
kapi
.
Pod
{
func
goodPod
()
*
kapi
.
Pod
{
return
&
kapi
.
Pod
{
return
&
kapi
.
Pod
{
ObjectMeta
:
kapi
.
ObjectMeta
{
Annotations
:
map
[
string
]
string
{},
},
Spec
:
kapi
.
PodSpec
{
Spec
:
kapi
.
PodSpec
{
ServiceAccountName
:
"default"
,
ServiceAccountName
:
"default"
,
SecurityContext
:
&
kapi
.
PodSecurityContext
{},
SecurityContext
:
&
kapi
.
PodSecurityContext
{},
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment