mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
vendor files
This commit is contained in:
29
vendor/k8s.io/kubernetes/pkg/security/BUILD
generated
vendored
Normal file
29
vendor/k8s.io/kubernetes/pkg/security/BUILD
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["doc.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/security",
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/security/apparmor:all-srcs",
|
||||
"//pkg/security/podsecuritypolicy:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
52
vendor/k8s.io/kubernetes/pkg/security/apparmor/BUILD
generated
vendored
Normal file
52
vendor/k8s.io/kubernetes/pkg/security/apparmor/BUILD
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"helpers.go",
|
||||
"validate.go",
|
||||
"validate_disabled.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/apparmor",
|
||||
deps = [
|
||||
"//pkg/features:go_default_library",
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
"//pkg/util/file:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["validate_test.go"],
|
||||
data = [
|
||||
"testdata/profiles",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/apparmor",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
80
vendor/k8s.io/kubernetes/pkg/security/apparmor/helpers.go
generated
vendored
Normal file
80
vendor/k8s.io/kubernetes/pkg/security/apparmor/helpers.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
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 apparmor
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// TODO: Move these values into the API package.
|
||||
const (
|
||||
// The prefix to an annotation key specifying a container profile.
|
||||
ContainerAnnotationKeyPrefix = "container.apparmor.security.beta.kubernetes.io/"
|
||||
// The annotation key specifying the default AppArmor profile.
|
||||
DefaultProfileAnnotationKey = "apparmor.security.beta.kubernetes.io/defaultProfileName"
|
||||
// The annotation key specifying the allowed AppArmor profiles.
|
||||
AllowedProfilesAnnotationKey = "apparmor.security.beta.kubernetes.io/allowedProfileNames"
|
||||
|
||||
// The profile specifying the runtime default.
|
||||
ProfileRuntimeDefault = "runtime/default"
|
||||
// The prefix for specifying profiles loaded on the node.
|
||||
ProfileNamePrefix = "localhost/"
|
||||
|
||||
// Unconfined profile
|
||||
ProfileNameUnconfined = "unconfined"
|
||||
)
|
||||
|
||||
// Checks whether app armor is required for pod to be run.
|
||||
func isRequired(pod *v1.Pod) bool {
|
||||
for key, value := range pod.Annotations {
|
||||
if strings.HasPrefix(key, ContainerAnnotationKeyPrefix) {
|
||||
return value != ProfileNameUnconfined
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns the name of the profile to use with the container.
|
||||
func GetProfileName(pod *v1.Pod, containerName string) string {
|
||||
return GetProfileNameFromPodAnnotations(pod.Annotations, containerName)
|
||||
}
|
||||
|
||||
// GetProfileNameFromPodAnnotations gets the name of the profile to use with container from
|
||||
// pod annotations
|
||||
func GetProfileNameFromPodAnnotations(annotations map[string]string, containerName string) string {
|
||||
return annotations[ContainerAnnotationKeyPrefix+containerName]
|
||||
}
|
||||
|
||||
// Sets the name of the profile to use with the container.
|
||||
func SetProfileName(pod *v1.Pod, containerName, profileName string) error {
|
||||
if pod.Annotations == nil {
|
||||
pod.Annotations = map[string]string{}
|
||||
}
|
||||
pod.Annotations[ContainerAnnotationKeyPrefix+containerName] = profileName
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sets the name of the profile to use with the container.
|
||||
func SetProfileNameFromPodAnnotations(annotations map[string]string, containerName, profileName string) error {
|
||||
if annotations == nil {
|
||||
return nil
|
||||
}
|
||||
annotations[ContainerAnnotationKeyPrefix+containerName] = profileName
|
||||
return nil
|
||||
}
|
24
vendor/k8s.io/kubernetes/pkg/security/apparmor/testdata/profiles
generated
vendored
Normal file
24
vendor/k8s.io/kubernetes/pkg/security/apparmor/testdata/profiles
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
/usr/bin/evince-thumbnailer (enforce)
|
||||
/usr/bin/evince-thumbnailer//sanitized_helper (enforce)
|
||||
/usr/bin/evince-previewer (enforce)
|
||||
/usr/bin/evince-previewer//sanitized_helper (enforce)
|
||||
/usr/bin/evince (enforce)
|
||||
/usr/bin/evince//sanitized_helper (enforce)
|
||||
/usr/lib/telepathy/telepathy-ofono (enforce)
|
||||
/usr/lib/telepathy/telepathy-* (enforce)
|
||||
/usr/lib/telepathy/telepathy-*//sanitized_helper (enforce)
|
||||
/usr/lib/telepathy/telepathy-*//pxgsettings (enforce)
|
||||
/usr/lib/telepathy/mission-control-5 (enforce)
|
||||
/usr/lib/lightdm/lightdm-guest-session (enforce)
|
||||
/usr/lib/lightdm/lightdm-guest-session//chromium (enforce)
|
||||
/usr/sbin/tcpdump (enforce)
|
||||
docker-default (enforce)
|
||||
/usr/sbin/cups-browsed (enforce)
|
||||
/usr/sbin/cupsd (enforce)
|
||||
/usr/lib/cups/backend/cups-pdf (enforce)
|
||||
/usr/sbin/ntpd (enforce)
|
||||
/usr/lib/connman/scripts/dhclient-script (enforce)
|
||||
/usr/lib/NetworkManager/nm-dhcp-client.action (enforce)
|
||||
/sbin/dhclient (enforce)
|
||||
foo-container (complain)
|
||||
foo://namespaced-profile (enforce)
|
229
vendor/k8s.io/kubernetes/pkg/security/apparmor/validate.go
generated
vendored
Normal file
229
vendor/k8s.io/kubernetes/pkg/security/apparmor/validate.go
generated
vendored
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
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 apparmor
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
utilfile "k8s.io/kubernetes/pkg/util/file"
|
||||
)
|
||||
|
||||
// Whether AppArmor should be disabled by default.
|
||||
// Set to true if the wrong build tags are set (see validate_disabled.go).
|
||||
var isDisabledBuild bool
|
||||
|
||||
// Interface for validating that a pod with with an AppArmor profile can be run by a Node.
|
||||
type Validator interface {
|
||||
Validate(pod *v1.Pod) error
|
||||
ValidateHost() error
|
||||
}
|
||||
|
||||
func NewValidator(runtime string) Validator {
|
||||
if err := validateHost(runtime); err != nil {
|
||||
return &validator{validateHostErr: err}
|
||||
}
|
||||
appArmorFS, err := getAppArmorFS()
|
||||
if err != nil {
|
||||
return &validator{
|
||||
validateHostErr: fmt.Errorf("error finding AppArmor FS: %v", err),
|
||||
}
|
||||
}
|
||||
return &validator{
|
||||
appArmorFS: appArmorFS,
|
||||
}
|
||||
}
|
||||
|
||||
type validator struct {
|
||||
validateHostErr error
|
||||
appArmorFS string
|
||||
}
|
||||
|
||||
func (v *validator) Validate(pod *v1.Pod) error {
|
||||
if !isRequired(pod) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if v.ValidateHost() != nil {
|
||||
return v.validateHostErr
|
||||
}
|
||||
|
||||
loadedProfiles, err := v.getLoadedProfiles()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read loaded profiles: %v", err)
|
||||
}
|
||||
|
||||
for _, container := range pod.Spec.InitContainers {
|
||||
if err := validateProfile(GetProfileName(pod, container.Name), loadedProfiles); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, container := range pod.Spec.Containers {
|
||||
if err := validateProfile(GetProfileName(pod, container.Name), loadedProfiles); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *validator) ValidateHost() error {
|
||||
return v.validateHostErr
|
||||
}
|
||||
|
||||
// Verify that the host and runtime is capable of enforcing AppArmor profiles.
|
||||
func validateHost(runtime string) error {
|
||||
// Check feature-gates
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.AppArmor) {
|
||||
return errors.New("AppArmor disabled by feature-gate")
|
||||
}
|
||||
|
||||
// Check build support.
|
||||
if isDisabledBuild {
|
||||
return errors.New("Binary not compiled for linux")
|
||||
}
|
||||
|
||||
// Check kernel support.
|
||||
if !IsAppArmorEnabled() {
|
||||
return errors.New("AppArmor is not enabled on the host")
|
||||
}
|
||||
|
||||
// Check runtime support. Currently only Docker is supported.
|
||||
if runtime != kubetypes.DockerContainerRuntime && runtime != kubetypes.RemoteContainerRuntime {
|
||||
return fmt.Errorf("AppArmor is only enabled for 'docker' and 'remote' runtimes. Found: %q.", runtime)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify that the profile is valid and loaded.
|
||||
func validateProfile(profile string, loadedProfiles map[string]bool) error {
|
||||
if err := ValidateProfileFormat(profile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.HasPrefix(profile, ProfileNamePrefix) {
|
||||
profileName := strings.TrimPrefix(profile, ProfileNamePrefix)
|
||||
if !loadedProfiles[profileName] {
|
||||
return fmt.Errorf("profile %q is not loaded", profileName)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateProfileFormat(profile string) error {
|
||||
if profile == "" || profile == ProfileRuntimeDefault || profile == ProfileNameUnconfined {
|
||||
return nil
|
||||
}
|
||||
if !strings.HasPrefix(profile, ProfileNamePrefix) {
|
||||
return fmt.Errorf("invalid AppArmor profile name: %q", profile)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *validator) getLoadedProfiles() (map[string]bool, error) {
|
||||
profilesPath := path.Join(v.appArmorFS, "profiles")
|
||||
profilesFile, err := os.Open(profilesPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open %s: %v", profilesPath, err)
|
||||
}
|
||||
defer profilesFile.Close()
|
||||
|
||||
profiles := map[string]bool{}
|
||||
scanner := bufio.NewScanner(profilesFile)
|
||||
for scanner.Scan() {
|
||||
profileName := parseProfileName(scanner.Text())
|
||||
if profileName == "" {
|
||||
// Unknown line format; skip it.
|
||||
continue
|
||||
}
|
||||
profiles[profileName] = true
|
||||
}
|
||||
return profiles, nil
|
||||
}
|
||||
|
||||
// The profiles file is formatted with one profile per line, matching a form:
|
||||
// namespace://profile-name (mode)
|
||||
// profile-name (mode)
|
||||
// Where mode is {enforce, complain, kill}. The "namespace://" is only included for namespaced
|
||||
// profiles. For the purposes of Kubernetes, we consider the namespace part of the profile name.
|
||||
func parseProfileName(profileLine string) string {
|
||||
modeIndex := strings.IndexRune(profileLine, '(')
|
||||
if modeIndex < 0 {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(profileLine[:modeIndex])
|
||||
}
|
||||
|
||||
func getAppArmorFS() (string, error) {
|
||||
mountsFile, err := os.Open("/proc/mounts")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not open /proc/mounts: %v", err)
|
||||
}
|
||||
defer mountsFile.Close()
|
||||
|
||||
scanner := bufio.NewScanner(mountsFile)
|
||||
for scanner.Scan() {
|
||||
fields := strings.Fields(scanner.Text())
|
||||
if len(fields) < 3 {
|
||||
// Unknown line format; skip it.
|
||||
continue
|
||||
}
|
||||
if fields[2] == "securityfs" {
|
||||
appArmorFS := path.Join(fields[1], "apparmor")
|
||||
if ok, err := utilfile.FileExists(appArmorFS); !ok {
|
||||
msg := fmt.Sprintf("path %s does not exist", appArmorFS)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%s: %v", msg, err)
|
||||
} else {
|
||||
return "", errors.New(msg)
|
||||
}
|
||||
} else {
|
||||
return appArmorFS, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return "", fmt.Errorf("error scanning mounts: %v", err)
|
||||
}
|
||||
|
||||
return "", errors.New("securityfs not found")
|
||||
}
|
||||
|
||||
// IsAppArmorEnabled returns true if apparmor is enabled for the host.
|
||||
// This function is forked from
|
||||
// https://github.com/opencontainers/runc/blob/1a81e9ab1f138c091fe5c86d0883f87716088527/libcontainer/apparmor/apparmor.go
|
||||
// to avoid the libapparmor dependency.
|
||||
func IsAppArmorEnabled() bool {
|
||||
if _, err := os.Stat("/sys/kernel/security/apparmor"); err == nil && os.Getenv("container") == "" {
|
||||
if _, err = os.Stat("/sbin/apparmor_parser"); err == nil {
|
||||
buf, err := ioutil.ReadFile("/sys/module/apparmor/parameters/enabled")
|
||||
return err == nil && len(buf) > 1 && buf[0] == 'Y'
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
24
vendor/k8s.io/kubernetes/pkg/security/apparmor/validate_disabled.go
generated
vendored
Normal file
24
vendor/k8s.io/kubernetes/pkg/security/apparmor/validate_disabled.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// +build !linux
|
||||
|
||||
/*
|
||||
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 apparmor
|
||||
|
||||
func init() {
|
||||
// If Kubernetes was not built for linux, apparmor is always disabled.
|
||||
isDisabledBuild = true
|
||||
}
|
198
vendor/k8s.io/kubernetes/pkg/security/apparmor/validate_test.go
generated
vendored
Normal file
198
vendor/k8s.io/kubernetes/pkg/security/apparmor/validate_test.go
generated
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
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 apparmor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetAppArmorFS(t *testing.T) {
|
||||
// This test only passes on systems running AppArmor with the default configuration.
|
||||
// The test should be manually run if modifying the getAppArmorFS function.
|
||||
t.Skip()
|
||||
|
||||
const expectedPath = "/sys/kernel/security/apparmor"
|
||||
actualPath, err := getAppArmorFS()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedPath, actualPath)
|
||||
}
|
||||
|
||||
func TestValidateHost(t *testing.T) {
|
||||
// This test only passes on systems running AppArmor with the default configuration.
|
||||
// The test should be manually run if modifying the getAppArmorFS function.
|
||||
t.Skip()
|
||||
|
||||
assert.NoError(t, validateHost("docker"))
|
||||
assert.Error(t, validateHost("rkt"))
|
||||
}
|
||||
|
||||
func TestValidateProfile(t *testing.T) {
|
||||
loadedProfiles := map[string]bool{
|
||||
"docker-default": true,
|
||||
"foo-bar": true,
|
||||
"baz": true,
|
||||
"/usr/sbin/ntpd": true,
|
||||
"/usr/lib/connman/scripts/dhclient-script": true,
|
||||
"/usr/lib/NetworkManager/nm-dhcp-client.action": true,
|
||||
"/usr/bin/evince-previewer//sanitized_helper": true,
|
||||
}
|
||||
tests := []struct {
|
||||
profile string
|
||||
expectValid bool
|
||||
}{
|
||||
{"", true},
|
||||
{ProfileRuntimeDefault, true},
|
||||
{ProfileNameUnconfined, true},
|
||||
{"baz", false}, // Missing local prefix.
|
||||
{ProfileNamePrefix + "/usr/sbin/ntpd", true},
|
||||
{ProfileNamePrefix + "foo-bar", true},
|
||||
{ProfileNamePrefix + "unloaded", false}, // Not loaded.
|
||||
{ProfileNamePrefix + "", false},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := validateProfile(test.profile, loadedProfiles)
|
||||
if test.expectValid {
|
||||
assert.NoError(t, err, "Profile %s should be valid", test.profile)
|
||||
} else {
|
||||
assert.Error(t, err, fmt.Sprintf("Profile %s should not be valid", test.profile))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateBadHost(t *testing.T) {
|
||||
hostErr := errors.New("expected host error")
|
||||
v := &validator{
|
||||
validateHostErr: hostErr,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
profile string
|
||||
expectValid bool
|
||||
}{
|
||||
{"", true},
|
||||
{ProfileRuntimeDefault, false},
|
||||
{ProfileNamePrefix + "docker-default", false},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := v.Validate(getPodWithProfile(test.profile))
|
||||
if test.expectValid {
|
||||
assert.NoError(t, err, "Pod with profile %q should be valid", test.profile)
|
||||
} else {
|
||||
assert.Equal(t, hostErr, err, "Pod with profile %q should trigger a host validation error", test.profile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateValidHost(t *testing.T) {
|
||||
v := &validator{
|
||||
appArmorFS: "./testdata/",
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
profile string
|
||||
expectValid bool
|
||||
}{
|
||||
{"", true},
|
||||
{ProfileRuntimeDefault, true},
|
||||
{ProfileNamePrefix + "docker-default", true},
|
||||
{ProfileNamePrefix + "foo-container", true},
|
||||
{ProfileNamePrefix + "/usr/sbin/ntpd", true},
|
||||
{"docker-default", false},
|
||||
{ProfileNamePrefix + "foo", false},
|
||||
{ProfileNamePrefix + "", false},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := v.Validate(getPodWithProfile(test.profile))
|
||||
if test.expectValid {
|
||||
assert.NoError(t, err, "Pod with profile %q should be valid", test.profile)
|
||||
} else {
|
||||
assert.Error(t, err, fmt.Sprintf("Pod with profile %q should trigger a validation error", test.profile))
|
||||
}
|
||||
}
|
||||
|
||||
// Test multi-container pod.
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
ContainerAnnotationKeyPrefix + "init": ProfileNamePrefix + "foo-container",
|
||||
ContainerAnnotationKeyPrefix + "test1": ProfileRuntimeDefault,
|
||||
ContainerAnnotationKeyPrefix + "test2": ProfileNamePrefix + "docker-default",
|
||||
},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
InitContainers: []v1.Container{
|
||||
{Name: "init"},
|
||||
},
|
||||
Containers: []v1.Container{
|
||||
{Name: "test1"},
|
||||
{Name: "test2"},
|
||||
{Name: "no-profile"},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.NoError(t, v.Validate(pod), "Multi-container pod should validate")
|
||||
for k, val := range pod.Annotations {
|
||||
pod.Annotations[k] = val + "-bad"
|
||||
assert.Error(t, v.Validate(pod), fmt.Sprintf("Multi-container pod with invalid profile %s:%s", k, pod.Annotations[k]))
|
||||
pod.Annotations[k] = val // Restore.
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseProfileName(t *testing.T) {
|
||||
tests := []struct{ line, expected string }{
|
||||
{"foo://bar/baz (kill)", "foo://bar/baz"},
|
||||
{"foo-bar (enforce)", "foo-bar"},
|
||||
{"/usr/foo/bar/baz (complain)", "/usr/foo/bar/baz"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
name := parseProfileName(test.line)
|
||||
assert.Equal(t, test.expected, name, "Parsing %s", test.line)
|
||||
}
|
||||
}
|
||||
|
||||
func getPodWithProfile(profile string) *v1.Pod {
|
||||
annotations := map[string]string{
|
||||
ContainerAnnotationKeyPrefix + "test": profile,
|
||||
}
|
||||
if profile == "" {
|
||||
annotations = map[string]string{
|
||||
"foo": "bar",
|
||||
}
|
||||
}
|
||||
return &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: annotations,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
18
vendor/k8s.io/kubernetes/pkg/security/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/kubernetes/pkg/security/doc.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
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 security contains security apis and implementations.
|
||||
package security // import "k8s.io/kubernetes/pkg/security"
|
77
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/BUILD
generated
vendored
Normal file
77
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/BUILD
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"factory.go",
|
||||
"provider.go",
|
||||
"types.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/security/podsecuritypolicy/apparmor:go_default_library",
|
||||
"//pkg/security/podsecuritypolicy/capabilities:go_default_library",
|
||||
"//pkg/security/podsecuritypolicy/group:go_default_library",
|
||||
"//pkg/security/podsecuritypolicy/seccomp:go_default_library",
|
||||
"//pkg/security/podsecuritypolicy/selinux:go_default_library",
|
||||
"//pkg/security/podsecuritypolicy/sysctl:go_default_library",
|
||||
"//pkg/security/podsecuritypolicy/user:go_default_library",
|
||||
"//pkg/security/podsecuritypolicy/util:go_default_library",
|
||||
"//pkg/securitycontext:go_default_library",
|
||||
"//pkg/util/maps:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["provider_test.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/core/v1:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/security/apparmor:go_default_library",
|
||||
"//pkg/security/podsecuritypolicy/seccomp:go_default_library",
|
||||
"//pkg/security/podsecuritypolicy/util:go_default_library",
|
||||
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/security/podsecuritypolicy/apparmor:all-srcs",
|
||||
"//pkg/security/podsecuritypolicy/capabilities:all-srcs",
|
||||
"//pkg/security/podsecuritypolicy/group:all-srcs",
|
||||
"//pkg/security/podsecuritypolicy/seccomp:all-srcs",
|
||||
"//pkg/security/podsecuritypolicy/selinux:all-srcs",
|
||||
"//pkg/security/podsecuritypolicy/sysctl:all-srcs",
|
||||
"//pkg/security/podsecuritypolicy/user:all-srcs",
|
||||
"//pkg/security/podsecuritypolicy/util:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
13
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/OWNERS
generated
vendored
Normal file
13
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/OWNERS
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
approvers:
|
||||
- deads2k
|
||||
- ericchiang
|
||||
- liggitt
|
||||
- pweil-
|
||||
- tallclair
|
||||
reviewers:
|
||||
- deads2k
|
||||
- ericchiang
|
||||
- liggitt
|
||||
- php-coder
|
||||
- pweil-
|
||||
- tallclair
|
47
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/apparmor/BUILD
generated
vendored
Normal file
47
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/apparmor/BUILD
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["strategy.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy/apparmor",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/security/apparmor:go_default_library",
|
||||
"//pkg/util/maps:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["strategy_test.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy/apparmor",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/security/apparmor:go_default_library",
|
||||
"//pkg/util/maps:go_default_library",
|
||||
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
110
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/apparmor/strategy.go
generated
vendored
Normal file
110
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/apparmor/strategy.go
generated
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
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 apparmor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/security/apparmor"
|
||||
"k8s.io/kubernetes/pkg/util/maps"
|
||||
)
|
||||
|
||||
// Strategy defines the interface for all AppArmor constraint strategies.
|
||||
type Strategy interface {
|
||||
// Generate updates the annotations based on constraint rules. The updates are applied to a copy
|
||||
// of the annotations, and returned.
|
||||
Generate(annotations map[string]string, container *api.Container) (map[string]string, error)
|
||||
// Validate ensures that the specified values fall within the range of the strategy.
|
||||
Validate(pod *api.Pod, container *api.Container) field.ErrorList
|
||||
}
|
||||
|
||||
type strategy struct {
|
||||
defaultProfile string
|
||||
allowedProfiles map[string]bool
|
||||
// For printing error messages (preserves order).
|
||||
allowedProfilesString string
|
||||
}
|
||||
|
||||
var _ Strategy = &strategy{}
|
||||
|
||||
// NewStrategy creates a new strategy that enforces AppArmor profile constraints.
|
||||
func NewStrategy(pspAnnotations map[string]string) Strategy {
|
||||
var allowedProfiles map[string]bool
|
||||
if allowed, ok := pspAnnotations[apparmor.AllowedProfilesAnnotationKey]; ok {
|
||||
profiles := strings.Split(allowed, ",")
|
||||
allowedProfiles = make(map[string]bool, len(profiles))
|
||||
for _, p := range profiles {
|
||||
allowedProfiles[p] = true
|
||||
}
|
||||
}
|
||||
return &strategy{
|
||||
defaultProfile: pspAnnotations[apparmor.DefaultProfileAnnotationKey],
|
||||
allowedProfiles: allowedProfiles,
|
||||
allowedProfilesString: pspAnnotations[apparmor.AllowedProfilesAnnotationKey],
|
||||
}
|
||||
}
|
||||
|
||||
func (s *strategy) Generate(annotations map[string]string, container *api.Container) (map[string]string, error) {
|
||||
copy := maps.CopySS(annotations)
|
||||
|
||||
if annotations[apparmor.ContainerAnnotationKeyPrefix+container.Name] != "" {
|
||||
// Profile already set, nothing to do.
|
||||
return copy, nil
|
||||
}
|
||||
|
||||
if s.defaultProfile == "" {
|
||||
// No default set.
|
||||
return copy, nil
|
||||
}
|
||||
|
||||
if copy == nil {
|
||||
copy = map[string]string{}
|
||||
}
|
||||
// Add the default profile.
|
||||
copy[apparmor.ContainerAnnotationKeyPrefix+container.Name] = s.defaultProfile
|
||||
|
||||
return copy, nil
|
||||
}
|
||||
|
||||
func (s *strategy) Validate(pod *api.Pod, container *api.Container) field.ErrorList {
|
||||
if s.allowedProfiles == nil {
|
||||
// Unrestricted: allow all.
|
||||
return nil
|
||||
}
|
||||
|
||||
allErrs := field.ErrorList{}
|
||||
fieldPath := field.NewPath("pod", "metadata", "annotations").Key(apparmor.ContainerAnnotationKeyPrefix + container.Name)
|
||||
|
||||
profile := apparmor.GetProfileNameFromPodAnnotations(pod.Annotations, container.Name)
|
||||
if profile == "" {
|
||||
if len(s.allowedProfiles) > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(fieldPath, "AppArmor profile must be set"))
|
||||
return allErrs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if !s.allowedProfiles[profile] {
|
||||
msg := fmt.Sprintf("%s is not an allowed profile. Allowed values: %q", profile, s.allowedProfilesString)
|
||||
allErrs = append(allErrs, field.Forbidden(fieldPath, msg))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
174
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/apparmor/strategy_test.go
generated
vendored
Normal file
174
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/apparmor/strategy_test.go
generated
vendored
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
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 apparmor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/security/apparmor"
|
||||
"k8s.io/kubernetes/pkg/util/maps"
|
||||
)
|
||||
|
||||
const (
|
||||
containerName = "test-c"
|
||||
)
|
||||
|
||||
var (
|
||||
withoutAppArmor = map[string]string{"foo": "bar"}
|
||||
withDefault = map[string]string{
|
||||
"foo": "bar",
|
||||
apparmor.ContainerAnnotationKeyPrefix + containerName: apparmor.ProfileRuntimeDefault,
|
||||
}
|
||||
withLocal = map[string]string{
|
||||
"foo": "bar",
|
||||
apparmor.ContainerAnnotationKeyPrefix + containerName: apparmor.ProfileNamePrefix + "foo",
|
||||
}
|
||||
withDisallowed = map[string]string{
|
||||
"foo": "bar",
|
||||
apparmor.ContainerAnnotationKeyPrefix + containerName: apparmor.ProfileNamePrefix + "bad",
|
||||
}
|
||||
|
||||
noAppArmor = map[string]string{"foo": "bar"}
|
||||
unconstrainedWithDefault = map[string]string{
|
||||
apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault,
|
||||
}
|
||||
constrained = map[string]string{
|
||||
apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault + "," +
|
||||
apparmor.ProfileNamePrefix + "foo",
|
||||
}
|
||||
constrainedWithDefault = map[string]string{
|
||||
apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault,
|
||||
apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault + "," +
|
||||
apparmor.ProfileNamePrefix + "foo",
|
||||
}
|
||||
|
||||
container = api.Container{
|
||||
Name: containerName,
|
||||
Image: "busybox",
|
||||
}
|
||||
)
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
type testcase struct {
|
||||
pspAnnotations map[string]string
|
||||
podAnnotations map[string]string
|
||||
expected map[string]string
|
||||
}
|
||||
tests := []testcase{{
|
||||
pspAnnotations: noAppArmor,
|
||||
podAnnotations: withoutAppArmor,
|
||||
expected: withoutAppArmor,
|
||||
}, {
|
||||
pspAnnotations: unconstrainedWithDefault,
|
||||
podAnnotations: withoutAppArmor,
|
||||
expected: withDefault,
|
||||
}, {
|
||||
pspAnnotations: constrained,
|
||||
podAnnotations: withoutAppArmor,
|
||||
expected: withoutAppArmor,
|
||||
}, {
|
||||
pspAnnotations: constrainedWithDefault,
|
||||
podAnnotations: withoutAppArmor,
|
||||
expected: withDefault,
|
||||
}}
|
||||
|
||||
// Add unchanging permutations.
|
||||
for _, podAnnotations := range []map[string]string{withDefault, withLocal} {
|
||||
for _, pspAnnotations := range []map[string]string{noAppArmor, unconstrainedWithDefault, constrained, constrainedWithDefault} {
|
||||
tests = append(tests, testcase{
|
||||
pspAnnotations: pspAnnotations,
|
||||
podAnnotations: podAnnotations,
|
||||
expected: podAnnotations,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
s := NewStrategy(test.pspAnnotations)
|
||||
msgAndArgs := []interface{}{"testcase[%d]: %s", i, spew.Sdump(test)}
|
||||
actual, err := s.Generate(test.podAnnotations, &container)
|
||||
assert.NoError(t, err, msgAndArgs...)
|
||||
assert.Equal(t, test.expected, actual, msgAndArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
type testcase struct {
|
||||
pspAnnotations map[string]string
|
||||
podAnnotations map[string]string
|
||||
expectErr bool
|
||||
}
|
||||
tests := []testcase{}
|
||||
// Valid combinations
|
||||
for _, podAnnotations := range []map[string]string{withDefault, withLocal} {
|
||||
for _, pspAnnotations := range []map[string]string{noAppArmor, unconstrainedWithDefault, constrained, constrainedWithDefault} {
|
||||
tests = append(tests, testcase{
|
||||
pspAnnotations: pspAnnotations,
|
||||
podAnnotations: podAnnotations,
|
||||
expectErr: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, podAnnotations := range []map[string]string{withoutAppArmor, withDisallowed} {
|
||||
for _, pspAnnotations := range []map[string]string{noAppArmor, unconstrainedWithDefault} {
|
||||
tests = append(tests, testcase{
|
||||
pspAnnotations: pspAnnotations,
|
||||
podAnnotations: podAnnotations,
|
||||
expectErr: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
// Invalid combinations
|
||||
for _, podAnnotations := range []map[string]string{withoutAppArmor, withDisallowed} {
|
||||
for _, pspAnnotations := range []map[string]string{constrained, constrainedWithDefault} {
|
||||
tests = append(tests, testcase{
|
||||
pspAnnotations: pspAnnotations,
|
||||
podAnnotations: podAnnotations,
|
||||
expectErr: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
s := NewStrategy(test.pspAnnotations)
|
||||
pod, container := makeTestPod(test.podAnnotations)
|
||||
msgAndArgs := []interface{}{"testcase[%d]: %s", i, spew.Sdump(test)}
|
||||
errs := s.Validate(pod, container)
|
||||
if test.expectErr {
|
||||
assert.Len(t, errs, 1, msgAndArgs...)
|
||||
} else {
|
||||
assert.Len(t, errs, 0, msgAndArgs...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeTestPod(annotations map[string]string) (*api.Pod, *api.Container) {
|
||||
return &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-pod",
|
||||
Annotations: maps.CopySS(annotations),
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{container},
|
||||
},
|
||||
}, &container
|
||||
}
|
47
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities/BUILD
generated
vendored
Normal file
47
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities/BUILD
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"mustrunas.go",
|
||||
"types.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["mustrunas_test.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
19
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
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 capabilities contains code for validating and defaulting a pod's
|
||||
// kernel capabilities according to a security policy.
|
||||
package capabilities
|
152
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities/mustrunas.go
generated
vendored
Normal file
152
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities/mustrunas.go
generated
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
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 capabilities
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
// defaultCapabilities implements the Strategy interface
|
||||
type defaultCapabilities struct {
|
||||
defaultAddCapabilities []api.Capability
|
||||
requiredDropCapabilities []api.Capability
|
||||
allowedCaps []api.Capability
|
||||
}
|
||||
|
||||
var _ Strategy = &defaultCapabilities{}
|
||||
|
||||
// NewDefaultCapabilities creates a new defaultCapabilities strategy that will provide defaults and validation
|
||||
// based on the configured initial caps and allowed caps.
|
||||
func NewDefaultCapabilities(defaultAddCapabilities, requiredDropCapabilities, allowedCaps []api.Capability) (Strategy, error) {
|
||||
return &defaultCapabilities{
|
||||
defaultAddCapabilities: defaultAddCapabilities,
|
||||
requiredDropCapabilities: requiredDropCapabilities,
|
||||
allowedCaps: allowedCaps,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Generate creates the capabilities based on policy rules. Generate will produce the following:
|
||||
// 1. a capabilities.Add set containing all the required adds (unless the
|
||||
// container specifically is dropping the cap) and container requested adds
|
||||
// 2. a capabilities.Drop set containing all the required drops and container requested drops
|
||||
//
|
||||
// Returns the original container capabilities if no changes are required.
|
||||
func (s *defaultCapabilities) Generate(pod *api.Pod, container *api.Container) (*api.Capabilities, error) {
|
||||
defaultAdd := makeCapSet(s.defaultAddCapabilities)
|
||||
requiredDrop := makeCapSet(s.requiredDropCapabilities)
|
||||
containerAdd := sets.NewString()
|
||||
containerDrop := sets.NewString()
|
||||
|
||||
var containerCapabilities *api.Capabilities
|
||||
if container.SecurityContext != nil && container.SecurityContext.Capabilities != nil {
|
||||
containerCapabilities = container.SecurityContext.Capabilities
|
||||
containerAdd = makeCapSet(container.SecurityContext.Capabilities.Add)
|
||||
containerDrop = makeCapSet(container.SecurityContext.Capabilities.Drop)
|
||||
}
|
||||
|
||||
// remove any default adds that the container is specifically dropping
|
||||
defaultAdd = defaultAdd.Difference(containerDrop)
|
||||
|
||||
combinedAdd := defaultAdd.Union(containerAdd)
|
||||
combinedDrop := requiredDrop.Union(containerDrop)
|
||||
|
||||
// no changes? return the original capabilities
|
||||
if (len(combinedAdd) == len(containerAdd)) && (len(combinedDrop) == len(containerDrop)) {
|
||||
return containerCapabilities, nil
|
||||
}
|
||||
|
||||
return &api.Capabilities{
|
||||
Add: capabilityFromStringSlice(combinedAdd.List()),
|
||||
Drop: capabilityFromStringSlice(combinedDrop.List()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Validate ensures that the specified values fall within the range of the strategy.
|
||||
func (s *defaultCapabilities) Validate(pod *api.Pod, container *api.Container, capabilities *api.Capabilities) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if capabilities == nil {
|
||||
// if container.SC.Caps is nil then nothing was defaulted by the strategy or requested by the pod author
|
||||
// if there are no required caps on the strategy and nothing is requested on the pod
|
||||
// then we can safely return here without further validation.
|
||||
if len(s.defaultAddCapabilities) == 0 && len(s.requiredDropCapabilities) == 0 {
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// container has no requested caps but we have required caps. We should have something in
|
||||
// at least the drops on the container.
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("capabilities"), capabilities,
|
||||
"required capabilities are not set on the securityContext"))
|
||||
return allErrs
|
||||
}
|
||||
|
||||
allowedAdd := makeCapSet(s.allowedCaps)
|
||||
allowAllCaps := allowedAdd.Has(string(extensions.AllowAllCapabilities))
|
||||
if allowAllCaps {
|
||||
// skip validation against allowed/defaultAdd/requiredDrop because all capabilities are allowed by a wildcard
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validate that anything being added is in the default or allowed sets
|
||||
defaultAdd := makeCapSet(s.defaultAddCapabilities)
|
||||
|
||||
for _, cap := range capabilities.Add {
|
||||
sCap := string(cap)
|
||||
if !defaultAdd.Has(sCap) && !allowedAdd.Has(sCap) {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("capabilities", "add"), sCap, "capability may not be added"))
|
||||
}
|
||||
}
|
||||
|
||||
// validate that anything that is required to be dropped is in the drop set
|
||||
containerDrops := makeCapSet(capabilities.Drop)
|
||||
|
||||
for _, requiredDrop := range s.requiredDropCapabilities {
|
||||
sDrop := string(requiredDrop)
|
||||
if !containerDrops.Has(sDrop) {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("capabilities", "drop"), capabilities.Drop,
|
||||
fmt.Sprintf("%s is required to be dropped but was not found", sDrop)))
|
||||
}
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// capabilityFromStringSlice creates a capability slice from a string slice.
|
||||
func capabilityFromStringSlice(slice []string) []api.Capability {
|
||||
if len(slice) == 0 {
|
||||
return nil
|
||||
}
|
||||
caps := []api.Capability{}
|
||||
for _, c := range slice {
|
||||
caps = append(caps, api.Capability(c))
|
||||
}
|
||||
return caps
|
||||
}
|
||||
|
||||
// makeCapSet makes a string set from capabilities.
|
||||
func makeCapSet(caps []api.Capability) sets.String {
|
||||
s := sets.NewString()
|
||||
for _, c := range caps {
|
||||
s.Insert(string(c))
|
||||
}
|
||||
return s
|
||||
}
|
410
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities/mustrunas_test.go
generated
vendored
Normal file
410
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities/mustrunas_test.go
generated
vendored
Normal file
@ -0,0 +1,410 @@
|
||||
/*
|
||||
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 capabilities
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
func TestGenerateAdds(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
defaultAddCaps []api.Capability
|
||||
containerCaps *api.Capabilities
|
||||
expectedCaps *api.Capabilities
|
||||
}{
|
||||
"no required, no container requests": {},
|
||||
"no required, no container requests, non-nil": {
|
||||
containerCaps: &api.Capabilities{},
|
||||
expectedCaps: &api.Capabilities{},
|
||||
},
|
||||
"required, no container requests": {
|
||||
defaultAddCaps: []api.Capability{"foo"},
|
||||
expectedCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"foo"},
|
||||
},
|
||||
},
|
||||
"required, container requests add required": {
|
||||
defaultAddCaps: []api.Capability{"foo"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"foo"},
|
||||
},
|
||||
expectedCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"foo"},
|
||||
},
|
||||
},
|
||||
"multiple required, container requests add required": {
|
||||
defaultAddCaps: []api.Capability{"foo", "bar", "baz"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"foo"},
|
||||
},
|
||||
expectedCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"bar", "baz", "foo"},
|
||||
},
|
||||
},
|
||||
"required, container requests add non-required": {
|
||||
defaultAddCaps: []api.Capability{"foo"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"bar"},
|
||||
},
|
||||
expectedCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"bar", "foo"},
|
||||
},
|
||||
},
|
||||
"generation does not mutate unnecessarily": {
|
||||
defaultAddCaps: []api.Capability{"foo", "bar"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"foo", "foo", "bar", "baz"},
|
||||
},
|
||||
expectedCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"foo", "foo", "bar", "baz"},
|
||||
},
|
||||
},
|
||||
"generation dedupes": {
|
||||
defaultAddCaps: []api.Capability{"foo", "bar"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"foo", "baz"},
|
||||
},
|
||||
expectedCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"bar", "baz", "foo"},
|
||||
},
|
||||
},
|
||||
"generation is case sensitive - will not dedupe": {
|
||||
defaultAddCaps: []api.Capability{"foo"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"FOO"},
|
||||
},
|
||||
expectedCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"FOO", "foo"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range tests {
|
||||
container := &api.Container{
|
||||
SecurityContext: &api.SecurityContext{
|
||||
Capabilities: v.containerCaps,
|
||||
},
|
||||
}
|
||||
|
||||
strategy, err := NewDefaultCapabilities(v.defaultAddCaps, nil, nil)
|
||||
if err != nil {
|
||||
t.Errorf("%s failed: %v", k, err)
|
||||
continue
|
||||
}
|
||||
generatedCaps, err := strategy.Generate(nil, container)
|
||||
if err != nil {
|
||||
t.Errorf("%s failed generating: %v", k, err)
|
||||
continue
|
||||
}
|
||||
if v.expectedCaps == nil && generatedCaps != nil {
|
||||
t.Errorf("%s expected nil caps to be generated but got %v", k, generatedCaps)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(v.expectedCaps, generatedCaps) {
|
||||
t.Errorf("%s did not generate correctly. Expected: %#v, Actual: %#v", k, v.expectedCaps, generatedCaps)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateDrops(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
defaultAddCaps []api.Capability
|
||||
requiredDropCaps []api.Capability
|
||||
containerCaps *api.Capabilities
|
||||
expectedCaps *api.Capabilities
|
||||
}{
|
||||
"no required, no container requests": {
|
||||
expectedCaps: nil,
|
||||
},
|
||||
"no required, no container requests, non-nil": {
|
||||
containerCaps: &api.Capabilities{},
|
||||
expectedCaps: &api.Capabilities{},
|
||||
},
|
||||
"required drops are defaulted": {
|
||||
requiredDropCaps: []api.Capability{"foo"},
|
||||
expectedCaps: &api.Capabilities{
|
||||
Drop: []api.Capability{"foo"},
|
||||
},
|
||||
},
|
||||
"required drops are defaulted when making container requests": {
|
||||
requiredDropCaps: []api.Capability{"baz"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Drop: []api.Capability{"foo", "bar"},
|
||||
},
|
||||
expectedCaps: &api.Capabilities{
|
||||
Drop: []api.Capability{"bar", "baz", "foo"},
|
||||
},
|
||||
},
|
||||
"required drops do not mutate unnecessarily": {
|
||||
requiredDropCaps: []api.Capability{"baz"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Drop: []api.Capability{"foo", "bar", "baz"},
|
||||
},
|
||||
expectedCaps: &api.Capabilities{
|
||||
Drop: []api.Capability{"foo", "bar", "baz"},
|
||||
},
|
||||
},
|
||||
"can drop a required add": {
|
||||
defaultAddCaps: []api.Capability{"foo"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Drop: []api.Capability{"foo"},
|
||||
},
|
||||
expectedCaps: &api.Capabilities{
|
||||
Drop: []api.Capability{"foo"},
|
||||
},
|
||||
},
|
||||
"can drop non-required add": {
|
||||
defaultAddCaps: []api.Capability{"foo"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Drop: []api.Capability{"bar"},
|
||||
},
|
||||
expectedCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"foo"},
|
||||
Drop: []api.Capability{"bar"},
|
||||
},
|
||||
},
|
||||
"defaulting adds and drops, dropping a required add": {
|
||||
defaultAddCaps: []api.Capability{"foo", "bar", "baz"},
|
||||
requiredDropCaps: []api.Capability{"abc"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Drop: []api.Capability{"foo"},
|
||||
},
|
||||
expectedCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"bar", "baz"},
|
||||
Drop: []api.Capability{"abc", "foo"},
|
||||
},
|
||||
},
|
||||
"generation dedupes": {
|
||||
requiredDropCaps: []api.Capability{"baz", "foo"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Drop: []api.Capability{"bar", "foo"},
|
||||
},
|
||||
expectedCaps: &api.Capabilities{
|
||||
Drop: []api.Capability{"bar", "baz", "foo"},
|
||||
},
|
||||
},
|
||||
"generation is case sensitive - will not dedupe": {
|
||||
requiredDropCaps: []api.Capability{"bar"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Drop: []api.Capability{"BAR"},
|
||||
},
|
||||
expectedCaps: &api.Capabilities{
|
||||
Drop: []api.Capability{"BAR", "bar"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for k, v := range tests {
|
||||
container := &api.Container{
|
||||
SecurityContext: &api.SecurityContext{
|
||||
Capabilities: v.containerCaps,
|
||||
},
|
||||
}
|
||||
|
||||
strategy, err := NewDefaultCapabilities(v.defaultAddCaps, v.requiredDropCaps, nil)
|
||||
if err != nil {
|
||||
t.Errorf("%s failed: %v", k, err)
|
||||
continue
|
||||
}
|
||||
generatedCaps, err := strategy.Generate(nil, container)
|
||||
if err != nil {
|
||||
t.Errorf("%s failed generating: %v", k, err)
|
||||
continue
|
||||
}
|
||||
if v.expectedCaps == nil && generatedCaps != nil {
|
||||
t.Errorf("%s expected nil caps to be generated but got %#v", k, generatedCaps)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(v.expectedCaps, generatedCaps) {
|
||||
t.Errorf("%s did not generate correctly. Expected: %#v, Actual: %#v", k, v.expectedCaps, generatedCaps)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAdds(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
defaultAddCaps []api.Capability
|
||||
allowedCaps []api.Capability
|
||||
containerCaps *api.Capabilities
|
||||
expectedError string
|
||||
}{
|
||||
// no container requests
|
||||
"no required, no allowed, no container requests": {},
|
||||
"no required, allowed, no container requests": {
|
||||
allowedCaps: []api.Capability{"foo"},
|
||||
},
|
||||
"required, no allowed, no container requests": {
|
||||
defaultAddCaps: []api.Capability{"foo"},
|
||||
expectedError: `capabilities: Invalid value: "null": required capabilities are not set on the securityContext`,
|
||||
},
|
||||
|
||||
// container requests match required
|
||||
"required, no allowed, container requests valid": {
|
||||
defaultAddCaps: []api.Capability{"foo"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"foo"},
|
||||
},
|
||||
},
|
||||
"required, no allowed, container requests invalid": {
|
||||
defaultAddCaps: []api.Capability{"foo"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"bar"},
|
||||
},
|
||||
expectedError: `capabilities.add: Invalid value: "bar": capability may not be added`,
|
||||
},
|
||||
|
||||
// container requests match allowed
|
||||
"no required, allowed, container requests valid": {
|
||||
allowedCaps: []api.Capability{"foo"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"foo"},
|
||||
},
|
||||
},
|
||||
"no required, all allowed, container requests valid": {
|
||||
allowedCaps: []api.Capability{extensions.AllowAllCapabilities},
|
||||
containerCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"foo"},
|
||||
},
|
||||
},
|
||||
"no required, allowed, container requests invalid": {
|
||||
allowedCaps: []api.Capability{"foo"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"bar"},
|
||||
},
|
||||
expectedError: `capabilities.add: Invalid value: "bar": capability may not be added`,
|
||||
},
|
||||
|
||||
// required and allowed
|
||||
"required, allowed, container requests valid required": {
|
||||
defaultAddCaps: []api.Capability{"foo"},
|
||||
allowedCaps: []api.Capability{"bar"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"foo"},
|
||||
},
|
||||
},
|
||||
"required, allowed, container requests valid allowed": {
|
||||
defaultAddCaps: []api.Capability{"foo"},
|
||||
allowedCaps: []api.Capability{"bar"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"bar"},
|
||||
},
|
||||
},
|
||||
"required, allowed, container requests invalid": {
|
||||
defaultAddCaps: []api.Capability{"foo"},
|
||||
allowedCaps: []api.Capability{"bar"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"baz"},
|
||||
},
|
||||
expectedError: `capabilities.add: Invalid value: "baz": capability may not be added`,
|
||||
},
|
||||
"validation is case sensitive": {
|
||||
defaultAddCaps: []api.Capability{"foo"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Add: []api.Capability{"FOO"},
|
||||
},
|
||||
expectedError: `capabilities.add: Invalid value: "FOO": capability may not be added`,
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range tests {
|
||||
strategy, err := NewDefaultCapabilities(v.defaultAddCaps, nil, v.allowedCaps)
|
||||
if err != nil {
|
||||
t.Errorf("%s failed: %v", k, err)
|
||||
continue
|
||||
}
|
||||
errs := strategy.Validate(nil, nil, v.containerCaps)
|
||||
if v.expectedError == "" && len(errs) > 0 {
|
||||
t.Errorf("%s should have passed but had errors %v", k, errs)
|
||||
continue
|
||||
}
|
||||
if v.expectedError != "" && len(errs) == 0 {
|
||||
t.Errorf("%s should have failed but received no errors", k)
|
||||
continue
|
||||
}
|
||||
if len(errs) == 1 && errs[0].Error() != v.expectedError {
|
||||
t.Errorf("%s should have failed with %v but received %v", k, v.expectedError, errs[0])
|
||||
continue
|
||||
}
|
||||
if len(errs) > 1 {
|
||||
t.Errorf("%s should have failed with at most one error, but received %v: %v", k, len(errs), errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDrops(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
requiredDropCaps []api.Capability
|
||||
containerCaps *api.Capabilities
|
||||
expectedError string
|
||||
}{
|
||||
// no container requests
|
||||
"no required, no container requests": {},
|
||||
"required, no container requests": {
|
||||
requiredDropCaps: []api.Capability{"foo"},
|
||||
expectedError: `capabilities: Invalid value: "null": required capabilities are not set on the securityContext`,
|
||||
},
|
||||
|
||||
// container requests match required
|
||||
"required, container requests valid": {
|
||||
requiredDropCaps: []api.Capability{"foo"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Drop: []api.Capability{"foo"},
|
||||
},
|
||||
},
|
||||
"required, container requests invalid": {
|
||||
requiredDropCaps: []api.Capability{"foo"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Drop: []api.Capability{"bar"},
|
||||
},
|
||||
expectedError: `capabilities.drop: Invalid value: []core.Capability{"bar"}: foo is required to be dropped but was not found`,
|
||||
},
|
||||
"validation is case sensitive": {
|
||||
requiredDropCaps: []api.Capability{"foo"},
|
||||
containerCaps: &api.Capabilities{
|
||||
Drop: []api.Capability{"FOO"},
|
||||
},
|
||||
expectedError: `capabilities.drop: Invalid value: []core.Capability{"FOO"}: foo is required to be dropped but was not found`,
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range tests {
|
||||
strategy, err := NewDefaultCapabilities(nil, v.requiredDropCaps, nil)
|
||||
if err != nil {
|
||||
t.Errorf("%s failed: %v", k, err)
|
||||
continue
|
||||
}
|
||||
errs := strategy.Validate(nil, nil, v.containerCaps)
|
||||
if v.expectedError == "" && len(errs) > 0 {
|
||||
t.Errorf("%s should have passed but had errors %v", k, errs)
|
||||
continue
|
||||
}
|
||||
if v.expectedError != "" && len(errs) == 0 {
|
||||
t.Errorf("%s should have failed but received no errors", k)
|
||||
continue
|
||||
}
|
||||
if len(errs) == 1 && errs[0].Error() != v.expectedError {
|
||||
t.Errorf("%s should have failed with %v but received %v", k, v.expectedError, errs[0])
|
||||
continue
|
||||
}
|
||||
if len(errs) > 1 {
|
||||
t.Errorf("%s should have failed with at most one error, but received %v: %v", k, len(errs), errs)
|
||||
}
|
||||
}
|
||||
}
|
30
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities/types.go
generated
vendored
Normal file
30
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities/types.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
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 capabilities
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
)
|
||||
|
||||
// Strategy defines the interface for all cap constraint strategies.
|
||||
type Strategy interface {
|
||||
// Generate creates the capabilities based on policy rules.
|
||||
Generate(pod *api.Pod, container *api.Container) (*api.Capabilities, error)
|
||||
// Validate ensures that the specified values fall within the range of the strategy.
|
||||
Validate(pod *api.Pod, container *api.Container, capabilities *api.Capabilities) field.ErrorList
|
||||
}
|
20
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/doc.go
generated
vendored
Normal file
20
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/doc.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
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 podsecuritypolicy contains code for validating and defaulting the
|
||||
// security context of a pod and its containers according to a security
|
||||
// policy.
|
||||
package podsecuritypolicy
|
176
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/factory.go
generated
vendored
Normal file
176
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/factory.go
generated
vendored
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
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 podsecuritypolicy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/errors"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/apparmor"
|
||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities"
|
||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/group"
|
||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp"
|
||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux"
|
||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/sysctl"
|
||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/user"
|
||||
)
|
||||
|
||||
type simpleStrategyFactory struct{}
|
||||
|
||||
var _ StrategyFactory = &simpleStrategyFactory{}
|
||||
|
||||
func NewSimpleStrategyFactory() StrategyFactory {
|
||||
return &simpleStrategyFactory{}
|
||||
}
|
||||
|
||||
func (f *simpleStrategyFactory) CreateStrategies(psp *extensions.PodSecurityPolicy, namespace string) (*ProviderStrategies, error) {
|
||||
errs := []error{}
|
||||
|
||||
userStrat, err := createUserStrategy(&psp.Spec.RunAsUser)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
seLinuxStrat, err := createSELinuxStrategy(&psp.Spec.SELinux)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
appArmorStrat, err := createAppArmorStrategy(psp)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
seccompStrat, err := createSeccompStrategy(psp)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
fsGroupStrat, err := createFSGroupStrategy(&psp.Spec.FSGroup)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
supGroupStrat, err := createSupplementalGroupStrategy(&psp.Spec.SupplementalGroups)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
capStrat, err := createCapabilitiesStrategy(psp.Spec.DefaultAddCapabilities, psp.Spec.RequiredDropCapabilities, psp.Spec.AllowedCapabilities)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
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 := createSysctlsStrategy(unsafeSysctls)
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
strategies := &ProviderStrategies{
|
||||
RunAsUserStrategy: userStrat,
|
||||
SELinuxStrategy: seLinuxStrat,
|
||||
AppArmorStrategy: appArmorStrat,
|
||||
FSGroupStrategy: fsGroupStrat,
|
||||
SupplementalGroupStrategy: supGroupStrat,
|
||||
CapabilitiesStrategy: capStrat,
|
||||
SeccompStrategy: seccompStrat,
|
||||
SysctlsStrategy: sysctlsStrat,
|
||||
}
|
||||
|
||||
return strategies, nil
|
||||
}
|
||||
|
||||
// createUserStrategy creates a new user strategy.
|
||||
func createUserStrategy(opts *extensions.RunAsUserStrategyOptions) (user.RunAsUserStrategy, error) {
|
||||
switch opts.Rule {
|
||||
case extensions.RunAsUserStrategyMustRunAs:
|
||||
return user.NewMustRunAs(opts)
|
||||
case extensions.RunAsUserStrategyMustRunAsNonRoot:
|
||||
return user.NewRunAsNonRoot(opts)
|
||||
case extensions.RunAsUserStrategyRunAsAny:
|
||||
return user.NewRunAsAny(opts)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unrecognized RunAsUser strategy type %s", opts.Rule)
|
||||
}
|
||||
}
|
||||
|
||||
// createSELinuxStrategy creates a new selinux strategy.
|
||||
func createSELinuxStrategy(opts *extensions.SELinuxStrategyOptions) (selinux.SELinuxStrategy, error) {
|
||||
switch opts.Rule {
|
||||
case extensions.SELinuxStrategyMustRunAs:
|
||||
return selinux.NewMustRunAs(opts)
|
||||
case extensions.SELinuxStrategyRunAsAny:
|
||||
return selinux.NewRunAsAny(opts)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unrecognized SELinuxContext strategy type %s", opts.Rule)
|
||||
}
|
||||
}
|
||||
|
||||
// createAppArmorStrategy creates a new AppArmor strategy.
|
||||
func createAppArmorStrategy(psp *extensions.PodSecurityPolicy) (apparmor.Strategy, error) {
|
||||
return apparmor.NewStrategy(psp.Annotations), nil
|
||||
}
|
||||
|
||||
// createSeccompStrategy creates a new seccomp strategy.
|
||||
func createSeccompStrategy(psp *extensions.PodSecurityPolicy) (seccomp.Strategy, error) {
|
||||
return seccomp.NewStrategy(psp.Annotations), nil
|
||||
}
|
||||
|
||||
// createFSGroupStrategy creates a new fsgroup strategy
|
||||
func createFSGroupStrategy(opts *extensions.FSGroupStrategyOptions) (group.GroupStrategy, error) {
|
||||
switch opts.Rule {
|
||||
case extensions.FSGroupStrategyRunAsAny:
|
||||
return group.NewRunAsAny()
|
||||
case extensions.FSGroupStrategyMustRunAs:
|
||||
return group.NewMustRunAs(opts.Ranges, fsGroupField)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unrecognized FSGroup strategy type %s", opts.Rule)
|
||||
}
|
||||
}
|
||||
|
||||
// createSupplementalGroupStrategy creates a new supplemental group strategy
|
||||
func createSupplementalGroupStrategy(opts *extensions.SupplementalGroupsStrategyOptions) (group.GroupStrategy, error) {
|
||||
switch opts.Rule {
|
||||
case extensions.SupplementalGroupsStrategyRunAsAny:
|
||||
return group.NewRunAsAny()
|
||||
case extensions.SupplementalGroupsStrategyMustRunAs:
|
||||
return group.NewMustRunAs(opts.Ranges, supplementalGroupsField)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unrecognized SupplementalGroups strategy type %s", opts.Rule)
|
||||
}
|
||||
}
|
||||
|
||||
// createCapabilitiesStrategy creates a new capabilities strategy.
|
||||
func createCapabilitiesStrategy(defaultAddCaps, requiredDropCaps, allowedCaps []api.Capability) (capabilities.Strategy, error) {
|
||||
return capabilities.NewDefaultCapabilities(defaultAddCaps, requiredDropCaps, allowedCaps)
|
||||
}
|
||||
|
||||
// createSysctlsStrategy creates a new unsafe sysctls strategy.
|
||||
func createSysctlsStrategy(sysctlsPatterns []string) sysctl.SysctlsStrategy {
|
||||
return sysctl.NewMustMatchPatterns(sysctlsPatterns)
|
||||
}
|
51
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/group/BUILD
generated
vendored
Normal file
51
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/group/BUILD
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"mustrunas.go",
|
||||
"runasany.go",
|
||||
"types.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy/group",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/security/podsecuritypolicy/util:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"mustrunas_test.go",
|
||||
"runasany_test.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy/group",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
19
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/group/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/group/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
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 group contains code for validating and defaulting the FSGroup and
|
||||
// supplemental groups of a pod according to a security policy.
|
||||
package group
|
88
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/group/mustrunas.go
generated
vendored
Normal file
88
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/group/mustrunas.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
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 group
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
|
||||
)
|
||||
|
||||
// mustRunAs implements the GroupStrategy interface
|
||||
type mustRunAs struct {
|
||||
ranges []extensions.GroupIDRange
|
||||
field string
|
||||
}
|
||||
|
||||
var _ GroupStrategy = &mustRunAs{}
|
||||
|
||||
// NewMustRunAs provides a new MustRunAs strategy based on ranges.
|
||||
func NewMustRunAs(ranges []extensions.GroupIDRange, field string) (GroupStrategy, error) {
|
||||
if len(ranges) == 0 {
|
||||
return nil, fmt.Errorf("ranges must be supplied for MustRunAs")
|
||||
}
|
||||
return &mustRunAs{
|
||||
ranges: ranges,
|
||||
field: field,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Generate creates the group based on policy rules. By default this returns the first group of the
|
||||
// first range (min val).
|
||||
func (s *mustRunAs) Generate(_ *api.Pod) ([]int64, error) {
|
||||
return []int64{s.ranges[0].Min}, nil
|
||||
}
|
||||
|
||||
// Generate a single value to be applied. This is used for FSGroup. This strategy will return
|
||||
// the first group of the first range (min val).
|
||||
func (s *mustRunAs) GenerateSingle(_ *api.Pod) (*int64, error) {
|
||||
single := new(int64)
|
||||
*single = s.ranges[0].Min
|
||||
return single, nil
|
||||
}
|
||||
|
||||
// Validate ensures that the specified values fall within the range of the strategy.
|
||||
// Groups are passed in here to allow this strategy to support multiple group fields (fsgroup and
|
||||
// supplemental groups).
|
||||
func (s *mustRunAs) Validate(_ *api.Pod, groups []int64) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if len(groups) == 0 && len(s.ranges) > 0 {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath(s.field), groups, "unable to validate empty groups against required ranges"))
|
||||
}
|
||||
|
||||
for _, group := range groups {
|
||||
if !s.isGroupValid(group) {
|
||||
detail := fmt.Sprintf("%d is not an allowed group", group)
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath(s.field), groups, detail))
|
||||
}
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func (s *mustRunAs) isGroupValid(group int64) bool {
|
||||
for _, rng := range s.ranges {
|
||||
if psputil.GroupFallsInRange(group, rng) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
177
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/group/mustrunas_test.go
generated
vendored
Normal file
177
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/group/mustrunas_test.go
generated
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
Copyright 2014 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 group
|
||||
|
||||
import (
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMustRunAsOptions(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
ranges []extensions.GroupIDRange
|
||||
pass bool
|
||||
}{
|
||||
"empty": {
|
||||
ranges: []extensions.GroupIDRange{},
|
||||
},
|
||||
"ranges": {
|
||||
ranges: []extensions.GroupIDRange{
|
||||
{Min: 1, Max: 1},
|
||||
},
|
||||
pass: true,
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range tests {
|
||||
_, err := NewMustRunAs(v.ranges, "")
|
||||
if v.pass && err != nil {
|
||||
t.Errorf("error creating strategy for %s: %v", k, err)
|
||||
}
|
||||
if !v.pass && err == nil {
|
||||
t.Errorf("expected error for %s but got none", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
ranges []extensions.GroupIDRange
|
||||
expected []int64
|
||||
}{
|
||||
"multi value": {
|
||||
ranges: []extensions.GroupIDRange{
|
||||
{Min: 1, Max: 2},
|
||||
},
|
||||
expected: []int64{1},
|
||||
},
|
||||
"single value": {
|
||||
ranges: []extensions.GroupIDRange{
|
||||
{Min: 1, Max: 1},
|
||||
},
|
||||
expected: []int64{1},
|
||||
},
|
||||
"multi range": {
|
||||
ranges: []extensions.GroupIDRange{
|
||||
{Min: 1, Max: 1},
|
||||
{Min: 2, Max: 500},
|
||||
},
|
||||
expected: []int64{1},
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range tests {
|
||||
s, err := NewMustRunAs(v.ranges, "")
|
||||
if err != nil {
|
||||
t.Errorf("error creating strategy for %s: %v", k, err)
|
||||
}
|
||||
actual, err := s.Generate(nil)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error for %s: %v", k, err)
|
||||
}
|
||||
if len(actual) != len(v.expected) {
|
||||
t.Errorf("unexpected generated values. Expected %v, got %v", v.expected, actual)
|
||||
continue
|
||||
}
|
||||
if len(actual) > 0 && len(v.expected) > 0 {
|
||||
if actual[0] != v.expected[0] {
|
||||
t.Errorf("unexpected generated values. Expected %v, got %v", v.expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
single, err := s.GenerateSingle(nil)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error for %s: %v", k, err)
|
||||
}
|
||||
if single == nil {
|
||||
t.Errorf("unexpected nil generated value for %s: %v", k, single)
|
||||
}
|
||||
if *single != v.expected[0] {
|
||||
t.Errorf("unexpected generated single value. Expected %v, got %v", v.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
ranges []extensions.GroupIDRange
|
||||
pod *api.Pod
|
||||
groups []int64
|
||||
pass bool
|
||||
}{
|
||||
"nil security context": {
|
||||
ranges: []extensions.GroupIDRange{
|
||||
{Min: 1, Max: 3},
|
||||
},
|
||||
},
|
||||
"empty groups": {
|
||||
ranges: []extensions.GroupIDRange{
|
||||
{Min: 1, Max: 3},
|
||||
},
|
||||
},
|
||||
"not in range": {
|
||||
groups: []int64{5},
|
||||
ranges: []extensions.GroupIDRange{
|
||||
{Min: 1, Max: 3},
|
||||
{Min: 4, Max: 4},
|
||||
},
|
||||
},
|
||||
"in range 1": {
|
||||
groups: []int64{2},
|
||||
ranges: []extensions.GroupIDRange{
|
||||
{Min: 1, Max: 3},
|
||||
},
|
||||
pass: true,
|
||||
},
|
||||
"in range boundry min": {
|
||||
groups: []int64{1},
|
||||
ranges: []extensions.GroupIDRange{
|
||||
{Min: 1, Max: 3},
|
||||
},
|
||||
pass: true,
|
||||
},
|
||||
"in range boundry max": {
|
||||
groups: []int64{3},
|
||||
ranges: []extensions.GroupIDRange{
|
||||
{Min: 1, Max: 3},
|
||||
},
|
||||
pass: true,
|
||||
},
|
||||
"singular range": {
|
||||
groups: []int64{4},
|
||||
ranges: []extensions.GroupIDRange{
|
||||
{Min: 4, Max: 4},
|
||||
},
|
||||
pass: true,
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range tests {
|
||||
s, err := NewMustRunAs(v.ranges, "")
|
||||
if err != nil {
|
||||
t.Errorf("error creating strategy for %s: %v", k, err)
|
||||
}
|
||||
errs := s.Validate(nil, v.groups)
|
||||
if v.pass && len(errs) > 0 {
|
||||
t.Errorf("unexpected errors for %s: %v", k, errs)
|
||||
}
|
||||
if !v.pass && len(errs) == 0 {
|
||||
t.Errorf("expected no errors for %s but got: %v", k, errs)
|
||||
}
|
||||
}
|
||||
}
|
49
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/group/runasany.go
generated
vendored
Normal file
49
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/group/runasany.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
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 group
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
)
|
||||
|
||||
// runAsAny implements the GroupStrategy interface.
|
||||
type runAsAny struct {
|
||||
}
|
||||
|
||||
var _ GroupStrategy = &runAsAny{}
|
||||
|
||||
// NewRunAsAny provides a new RunAsAny strategy.
|
||||
func NewRunAsAny() (GroupStrategy, error) {
|
||||
return &runAsAny{}, nil
|
||||
}
|
||||
|
||||
// Generate creates the group based on policy rules. This strategy returns an empty slice.
|
||||
func (s *runAsAny) Generate(_ *api.Pod) ([]int64, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Generate a single value to be applied. This is used for FSGroup. This strategy returns nil.
|
||||
func (s *runAsAny) GenerateSingle(_ *api.Pod) (*int64, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Validate ensures that the specified values fall within the range of the strategy.
|
||||
func (s *runAsAny) Validate(_ *api.Pod, groups []int64) field.ErrorList {
|
||||
return field.ErrorList{}
|
||||
|
||||
}
|
60
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/group/runasany_test.go
generated
vendored
Normal file
60
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/group/runasany_test.go
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
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 group
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRunAsAnyGenerate(t *testing.T) {
|
||||
s, err := NewRunAsAny()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
|
||||
}
|
||||
groups, err := s.Generate(nil)
|
||||
if len(groups) > 0 {
|
||||
t.Errorf("expected empty but got %v", groups)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error generating groups: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunAsAnyGenerateSingle(t *testing.T) {
|
||||
s, err := NewRunAsAny()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
|
||||
}
|
||||
group, err := s.GenerateSingle(nil)
|
||||
if group != nil {
|
||||
t.Errorf("expected empty but got %v", group)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error generating groups: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunAsAnyValidte(t *testing.T) {
|
||||
s, err := NewRunAsAny()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
|
||||
}
|
||||
errs := s.Validate(nil, nil)
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("unexpected errors: %v", errs)
|
||||
}
|
||||
}
|
35
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/group/types.go
generated
vendored
Normal file
35
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/group/types.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
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 group
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
)
|
||||
|
||||
// GroupStrategy defines the interface for all group constraint strategies.
|
||||
type GroupStrategy interface {
|
||||
// Generate creates the group based on policy rules. The underlying implementation can
|
||||
// decide whether it will return a full range of values or a subset of values from the
|
||||
// configured ranges.
|
||||
Generate(pod *api.Pod) ([]int64, error)
|
||||
// Generate a single value to be applied. The underlying implementation decides which
|
||||
// value to return if configured with multiple ranges. This is used for FSGroup.
|
||||
GenerateSingle(pod *api.Pod) (*int64, error)
|
||||
// Validate ensures that the specified values fall within the range of the strategy.
|
||||
Validate(pod *api.Pod, groups []int64) field.ErrorList
|
||||
}
|
362
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/provider.go
generated
vendored
Normal file
362
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/provider.go
generated
vendored
Normal file
@ -0,0 +1,362 @@
|
||||
/*
|
||||
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 podsecuritypolicy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
|
||||
"k8s.io/kubernetes/pkg/securitycontext"
|
||||
"k8s.io/kubernetes/pkg/util/maps"
|
||||
)
|
||||
|
||||
// used to pass in the field being validated for reusable group strategies so they
|
||||
// can create informative error messages.
|
||||
const (
|
||||
fsGroupField = "fsGroup"
|
||||
supplementalGroupsField = "supplementalGroups"
|
||||
)
|
||||
|
||||
// simpleProvider is the default implementation of Provider.
|
||||
type simpleProvider struct {
|
||||
psp *extensions.PodSecurityPolicy
|
||||
strategies *ProviderStrategies
|
||||
}
|
||||
|
||||
// ensure we implement the interface correctly.
|
||||
var _ Provider = &simpleProvider{}
|
||||
|
||||
// NewSimpleProvider creates a new Provider instance.
|
||||
func NewSimpleProvider(psp *extensions.PodSecurityPolicy, namespace string, strategyFactory StrategyFactory) (Provider, error) {
|
||||
if psp == nil {
|
||||
return nil, fmt.Errorf("NewSimpleProvider requires a PodSecurityPolicy")
|
||||
}
|
||||
if strategyFactory == nil {
|
||||
return nil, fmt.Errorf("NewSimpleProvider requires a StrategyFactory")
|
||||
}
|
||||
|
||||
strategies, err := strategyFactory.CreateStrategies(psp, namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &simpleProvider{
|
||||
psp: psp,
|
||||
strategies: strategies,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create a PodSecurityContext based on the given constraints. If a setting is already set
|
||||
// on the PodSecurityContext it will not be changed. Validate should be used after the context
|
||||
// is created to ensure it complies with the required restrictions.
|
||||
func (s *simpleProvider) CreatePodSecurityContext(pod *api.Pod) (*api.PodSecurityContext, map[string]string, error) {
|
||||
sc := securitycontext.NewPodSecurityContextMutator(pod.Spec.SecurityContext)
|
||||
annotations := maps.CopySS(pod.Annotations)
|
||||
|
||||
if sc.SupplementalGroups() == nil {
|
||||
supGroups, err := s.strategies.SupplementalGroupStrategy.Generate(pod)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sc.SetSupplementalGroups(supGroups)
|
||||
}
|
||||
|
||||
if sc.FSGroup() == nil {
|
||||
fsGroup, err := s.strategies.FSGroupStrategy.GenerateSingle(pod)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sc.SetFSGroup(fsGroup)
|
||||
}
|
||||
|
||||
if sc.SELinuxOptions() == nil {
|
||||
seLinux, err := s.strategies.SELinuxStrategy.Generate(pod, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sc.SetSELinuxOptions(seLinux)
|
||||
}
|
||||
|
||||
// This is only generated on the pod level. Containers inherit the pod's profile. If the
|
||||
// container has a specific profile set then it will be caught in the validation step.
|
||||
seccompProfile, err := s.strategies.SeccompStrategy.Generate(annotations, pod)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if seccompProfile != "" {
|
||||
if annotations == nil {
|
||||
annotations = map[string]string{}
|
||||
}
|
||||
annotations[api.SeccompPodAnnotationKey] = seccompProfile
|
||||
}
|
||||
return sc.PodSecurityContext(), annotations, nil
|
||||
}
|
||||
|
||||
// Create a SecurityContext based on the given constraints. If a setting is already set on the
|
||||
// container's security context then it will not be changed. Validation should be used after
|
||||
// the context is created to ensure it complies with the required restrictions.
|
||||
func (s *simpleProvider) CreateContainerSecurityContext(pod *api.Pod, container *api.Container) (*api.SecurityContext, map[string]string, error) {
|
||||
sc := securitycontext.NewEffectiveContainerSecurityContextMutator(
|
||||
securitycontext.NewPodSecurityContextAccessor(pod.Spec.SecurityContext),
|
||||
securitycontext.NewContainerSecurityContextMutator(container.SecurityContext),
|
||||
)
|
||||
|
||||
annotations := maps.CopySS(pod.Annotations)
|
||||
|
||||
if sc.RunAsUser() == nil {
|
||||
uid, err := s.strategies.RunAsUserStrategy.Generate(pod, container)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sc.SetRunAsUser(uid)
|
||||
}
|
||||
|
||||
if sc.SELinuxOptions() == nil {
|
||||
seLinux, err := s.strategies.SELinuxStrategy.Generate(pod, container)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sc.SetSELinuxOptions(seLinux)
|
||||
}
|
||||
|
||||
annotations, err := s.strategies.AppArmorStrategy.Generate(annotations, container)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// if we're using the non-root strategy set the marker that this container should not be
|
||||
// run as root which will signal to the kubelet to do a final check either on the runAsUser
|
||||
// or, if runAsUser is not set, the image UID will be checked.
|
||||
if sc.RunAsNonRoot() == nil && sc.RunAsUser() == nil && s.psp.Spec.RunAsUser.Rule == extensions.RunAsUserStrategyMustRunAsNonRoot {
|
||||
nonRoot := true
|
||||
sc.SetRunAsNonRoot(&nonRoot)
|
||||
}
|
||||
|
||||
caps, err := s.strategies.CapabilitiesStrategy.Generate(pod, container)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sc.SetCapabilities(caps)
|
||||
|
||||
// if the PSP requires a read only root filesystem and the container has not made a specific
|
||||
// request then default ReadOnlyRootFilesystem to true.
|
||||
if s.psp.Spec.ReadOnlyRootFilesystem && sc.ReadOnlyRootFilesystem() == nil {
|
||||
readOnlyRootFS := true
|
||||
sc.SetReadOnlyRootFilesystem(&readOnlyRootFS)
|
||||
}
|
||||
|
||||
// if the PSP sets DefaultAllowPrivilegeEscalation and the container security context
|
||||
// allowPrivilegeEscalation is not set, then default to that set by the PSP.
|
||||
if s.psp.Spec.DefaultAllowPrivilegeEscalation != nil && sc.AllowPrivilegeEscalation() == nil {
|
||||
sc.SetAllowPrivilegeEscalation(s.psp.Spec.DefaultAllowPrivilegeEscalation)
|
||||
}
|
||||
|
||||
// if the PSP sets psp.AllowPrivilegeEscalation to false set that as the default
|
||||
if !s.psp.Spec.AllowPrivilegeEscalation && sc.AllowPrivilegeEscalation() == nil {
|
||||
sc.SetAllowPrivilegeEscalation(&s.psp.Spec.AllowPrivilegeEscalation)
|
||||
}
|
||||
|
||||
return sc.ContainerSecurityContext(), annotations, nil
|
||||
}
|
||||
|
||||
// Ensure a pod's SecurityContext is in compliance with the given constraints.
|
||||
func (s *simpleProvider) ValidatePodSecurityContext(pod *api.Pod, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
sc := securitycontext.NewPodSecurityContextAccessor(pod.Spec.SecurityContext)
|
||||
|
||||
fsGroups := []int64{}
|
||||
if fsGroup := sc.FSGroup(); fsGroup != nil {
|
||||
fsGroups = append(fsGroups, *fsGroup)
|
||||
}
|
||||
allErrs = append(allErrs, s.strategies.FSGroupStrategy.Validate(pod, fsGroups)...)
|
||||
allErrs = append(allErrs, s.strategies.SupplementalGroupStrategy.Validate(pod, sc.SupplementalGroups())...)
|
||||
allErrs = append(allErrs, s.strategies.SeccompStrategy.ValidatePod(pod)...)
|
||||
|
||||
allErrs = append(allErrs, s.strategies.SELinuxStrategy.Validate(fldPath.Child("seLinuxOptions"), pod, nil, sc.SELinuxOptions())...)
|
||||
|
||||
if !s.psp.Spec.HostNetwork && sc.HostNetwork() {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("hostNetwork"), sc.HostNetwork(), "Host network is not allowed to be used"))
|
||||
}
|
||||
|
||||
if !s.psp.Spec.HostPID && sc.HostPID() {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("hostPID"), sc.HostPID(), "Host PID is not allowed to be used"))
|
||||
}
|
||||
|
||||
if !s.psp.Spec.HostIPC && sc.HostIPC() {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("hostIPC"), sc.HostIPC(), "Host IPC is not allowed to be used"))
|
||||
}
|
||||
|
||||
allErrs = append(allErrs, s.strategies.SysctlsStrategy.Validate(pod)...)
|
||||
|
||||
// TODO(tallclair): ValidatePodSecurityContext should be renamed to ValidatePod since its scope
|
||||
// is not limited to the PodSecurityContext.
|
||||
if len(pod.Spec.Volumes) > 0 {
|
||||
allowsAllVolumeTypes := psputil.PSPAllowsAllVolumes(s.psp)
|
||||
allowedVolumes := psputil.FSTypeToStringSet(s.psp.Spec.Volumes)
|
||||
for i, v := range pod.Spec.Volumes {
|
||||
fsType, err := psputil.GetVolumeFSType(v)
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "volumes").Index(i), string(fsType), err.Error()))
|
||||
continue
|
||||
}
|
||||
|
||||
if !allowsAllVolumeTypes && !allowedVolumes.Has(string(fsType)) {
|
||||
allErrs = append(allErrs, field.Invalid(
|
||||
field.NewPath("spec", "volumes").Index(i), string(fsType),
|
||||
fmt.Sprintf("%s volumes are not allowed to be used", string(fsType))))
|
||||
continue
|
||||
}
|
||||
|
||||
if fsType == extensions.HostPath {
|
||||
if !psputil.AllowsHostVolumePath(s.psp, v.HostPath.Path) {
|
||||
allErrs = append(allErrs, field.Invalid(
|
||||
field.NewPath("spec", "volumes").Index(i).Child("hostPath", "pathPrefix"), v.HostPath.Path,
|
||||
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
|
||||
}
|
||||
|
||||
// Ensure a container's SecurityContext is in compliance with the given constraints
|
||||
func (s *simpleProvider) ValidateContainerSecurityContext(pod *api.Pod, container *api.Container, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
podSC := securitycontext.NewPodSecurityContextAccessor(pod.Spec.SecurityContext)
|
||||
sc := securitycontext.NewEffectiveContainerSecurityContextAccessor(podSC, securitycontext.NewContainerSecurityContextMutator(container.SecurityContext))
|
||||
|
||||
allErrs = append(allErrs, s.strategies.RunAsUserStrategy.Validate(fldPath.Child("securityContext"), pod, container, sc.RunAsNonRoot(), sc.RunAsUser())...)
|
||||
allErrs = append(allErrs, s.strategies.SELinuxStrategy.Validate(fldPath.Child("seLinuxOptions"), pod, container, sc.SELinuxOptions())...)
|
||||
allErrs = append(allErrs, s.strategies.AppArmorStrategy.Validate(pod, container)...)
|
||||
allErrs = append(allErrs, s.strategies.SeccompStrategy.ValidateContainer(pod, container)...)
|
||||
|
||||
privileged := sc.Privileged()
|
||||
if !s.psp.Spec.Privileged && privileged != nil && *privileged {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("privileged"), *privileged, "Privileged containers are not allowed"))
|
||||
}
|
||||
|
||||
allErrs = append(allErrs, s.strategies.CapabilitiesStrategy.Validate(pod, container, sc.Capabilities())...)
|
||||
|
||||
if !s.psp.Spec.HostNetwork && podSC.HostNetwork() {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("hostNetwork"), podSC.HostNetwork(), "Host network is not allowed to be used"))
|
||||
}
|
||||
|
||||
containersPath := fldPath.Child("containers")
|
||||
for idx, c := range pod.Spec.Containers {
|
||||
idxPath := containersPath.Index(idx)
|
||||
allErrs = append(allErrs, s.hasInvalidHostPort(&c, idxPath)...)
|
||||
}
|
||||
|
||||
containersPath = fldPath.Child("initContainers")
|
||||
for idx, c := range pod.Spec.InitContainers {
|
||||
idxPath := containersPath.Index(idx)
|
||||
allErrs = append(allErrs, s.hasInvalidHostPort(&c, idxPath)...)
|
||||
}
|
||||
|
||||
if !s.psp.Spec.HostPID && podSC.HostPID() {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("hostPID"), podSC.HostPID(), "Host PID is not allowed to be used"))
|
||||
}
|
||||
|
||||
if !s.psp.Spec.HostIPC && podSC.HostIPC() {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("hostIPC"), podSC.HostIPC(), "Host IPC is not allowed to be used"))
|
||||
}
|
||||
|
||||
if s.psp.Spec.ReadOnlyRootFilesystem {
|
||||
readOnly := sc.ReadOnlyRootFilesystem()
|
||||
if readOnly == nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("readOnlyRootFilesystem"), readOnly, "ReadOnlyRootFilesystem may not be nil and must be set to true"))
|
||||
} else if !*readOnly {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("readOnlyRootFilesystem"), *readOnly, "ReadOnlyRootFilesystem must be set to true"))
|
||||
}
|
||||
}
|
||||
|
||||
allowEscalation := sc.AllowPrivilegeEscalation()
|
||||
if !s.psp.Spec.AllowPrivilegeEscalation && allowEscalation == nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("allowPrivilegeEscalation"), allowEscalation, "Allowing privilege escalation for containers is not allowed"))
|
||||
}
|
||||
|
||||
if !s.psp.Spec.AllowPrivilegeEscalation && allowEscalation != nil && *allowEscalation {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("allowPrivilegeEscalation"), *allowEscalation, "Allowing privilege escalation for containers is not allowed"))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// hasInvalidHostPort checks whether the port definitions on the container fall outside of the ranges allowed by the PSP.
|
||||
func (s *simpleProvider) hasInvalidHostPort(container *api.Container, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
for _, cp := range container.Ports {
|
||||
if cp.HostPort > 0 && !s.isValidHostPort(cp.HostPort) {
|
||||
detail := fmt.Sprintf("Host port %d is not allowed to be used. Allowed ports: [%s]", cp.HostPort, hostPortRangesToString(s.psp.Spec.HostPorts))
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("hostPort"), cp.HostPort, detail))
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// isValidHostPort returns true if the port falls in any range allowed by the PSP.
|
||||
func (s *simpleProvider) isValidHostPort(port int32) bool {
|
||||
for _, hostPortRange := range s.psp.Spec.HostPorts {
|
||||
if port >= hostPortRange.Min && port <= hostPortRange.Max {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Get the name of the PSP that this provider was initialized with.
|
||||
func (s *simpleProvider) GetPSPName() string {
|
||||
return s.psp.Name
|
||||
}
|
||||
|
||||
func hostPortRangesToString(ranges []extensions.HostPortRange) string {
|
||||
formattedString := ""
|
||||
if ranges != nil {
|
||||
strRanges := []string{}
|
||||
for _, r := range ranges {
|
||||
if r.Min == r.Max {
|
||||
strRanges = append(strRanges, fmt.Sprintf("%d", r.Min))
|
||||
} else {
|
||||
strRanges = append(strRanges, fmt.Sprintf("%d-%d", r.Min, r.Max))
|
||||
}
|
||||
}
|
||||
formattedString = strings.Join(strRanges, ",")
|
||||
}
|
||||
return formattedString
|
||||
}
|
1227
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/provider_test.go
generated
vendored
Normal file
1227
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/provider_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
41
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp/BUILD
generated
vendored
Normal file
41
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp/BUILD
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["strategy.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["strategy_test.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
149
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp/strategy.go
generated
vendored
Normal file
149
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp/strategy.go
generated
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
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 seccomp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
)
|
||||
|
||||
const (
|
||||
// AllowAny is the wildcard used to allow any profile.
|
||||
AllowAny = "*"
|
||||
// The annotation key specifying the default seccomp profile.
|
||||
DefaultProfileAnnotationKey = "seccomp.security.alpha.kubernetes.io/defaultProfileName"
|
||||
// The annotation key specifying the allowed seccomp profiles.
|
||||
AllowedProfilesAnnotationKey = "seccomp.security.alpha.kubernetes.io/allowedProfileNames"
|
||||
)
|
||||
|
||||
// Strategy defines the interface for all seccomp constraint strategies.
|
||||
type Strategy interface {
|
||||
// Generate returns a profile based on constraint rules.
|
||||
Generate(annotations map[string]string, pod *api.Pod) (string, error)
|
||||
// Validate ensures that the specified values fall within the range of the strategy.
|
||||
ValidatePod(pod *api.Pod) field.ErrorList
|
||||
// Validate ensures that the specified values fall within the range of the strategy.
|
||||
ValidateContainer(pod *api.Pod, container *api.Container) field.ErrorList
|
||||
}
|
||||
|
||||
type strategy struct {
|
||||
defaultProfile string
|
||||
allowedProfiles map[string]bool
|
||||
// For printing error messages (preserves order).
|
||||
allowedProfilesString string
|
||||
// does the strategy allow any profile (wildcard)
|
||||
allowAnyProfile bool
|
||||
}
|
||||
|
||||
var _ Strategy = &strategy{}
|
||||
|
||||
// NewStrategy creates a new strategy that enforces seccomp profile constraints.
|
||||
func NewStrategy(pspAnnotations map[string]string) Strategy {
|
||||
var allowedProfiles map[string]bool
|
||||
allowAnyProfile := false
|
||||
if allowed, ok := pspAnnotations[AllowedProfilesAnnotationKey]; ok {
|
||||
profiles := strings.Split(allowed, ",")
|
||||
allowedProfiles = make(map[string]bool, len(profiles))
|
||||
for _, p := range profiles {
|
||||
if p == AllowAny {
|
||||
allowAnyProfile = true
|
||||
continue
|
||||
}
|
||||
allowedProfiles[p] = true
|
||||
}
|
||||
}
|
||||
return &strategy{
|
||||
defaultProfile: pspAnnotations[DefaultProfileAnnotationKey],
|
||||
allowedProfiles: allowedProfiles,
|
||||
allowedProfilesString: pspAnnotations[AllowedProfilesAnnotationKey],
|
||||
allowAnyProfile: allowAnyProfile,
|
||||
}
|
||||
}
|
||||
|
||||
// Generate returns a profile based on constraint rules.
|
||||
func (s *strategy) Generate(annotations map[string]string, pod *api.Pod) (string, error) {
|
||||
if annotations[api.SeccompPodAnnotationKey] != "" {
|
||||
// Profile already set, nothing to do.
|
||||
return annotations[api.SeccompPodAnnotationKey], nil
|
||||
}
|
||||
return s.defaultProfile, nil
|
||||
}
|
||||
|
||||
// ValidatePod ensures that the specified values on the pod fall within the range
|
||||
// of the strategy.
|
||||
func (s *strategy) ValidatePod(pod *api.Pod) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
podSpecFieldPath := field.NewPath("pod", "metadata", "annotations").Key(api.SeccompPodAnnotationKey)
|
||||
podProfile := pod.Annotations[api.SeccompPodAnnotationKey]
|
||||
|
||||
if !s.allowAnyProfile && len(s.allowedProfiles) == 0 && podProfile != "" {
|
||||
allErrs = append(allErrs, field.Forbidden(podSpecFieldPath, "seccomp may not be set"))
|
||||
return allErrs
|
||||
}
|
||||
|
||||
if !s.profileAllowed(podProfile) {
|
||||
msg := fmt.Sprintf("%s is not an allowed seccomp profile. Valid values are %v", podProfile, s.allowedProfilesString)
|
||||
allErrs = append(allErrs, field.Forbidden(podSpecFieldPath, msg))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateContainer ensures that the specified values on the container fall within
|
||||
// the range of the strategy.
|
||||
func (s *strategy) ValidateContainer(pod *api.Pod, container *api.Container) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
fieldPath := field.NewPath("pod", "metadata", "annotations").Key(api.SeccompContainerAnnotationKeyPrefix + container.Name)
|
||||
containerProfile := profileForContainer(pod, container)
|
||||
|
||||
if !s.allowAnyProfile && len(s.allowedProfiles) == 0 && containerProfile != "" {
|
||||
allErrs = append(allErrs, field.Forbidden(fieldPath, "seccomp may not be set"))
|
||||
return allErrs
|
||||
}
|
||||
|
||||
if !s.profileAllowed(containerProfile) {
|
||||
msg := fmt.Sprintf("%s is not an allowed seccomp profile. Valid values are %v", containerProfile, s.allowedProfilesString)
|
||||
allErrs = append(allErrs, field.Forbidden(fieldPath, msg))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// profileAllowed checks if profile is in allowedProfiles or if allowedProfiles
|
||||
// contains the wildcard.
|
||||
func (s *strategy) profileAllowed(profile string) bool {
|
||||
// for backwards compatibility and PSPs without a defined list of allowed profiles.
|
||||
// If a PSP does not have allowedProfiles set then we should allow an empty profile.
|
||||
// This will mean that the runtime default is used.
|
||||
if len(s.allowedProfiles) == 0 && profile == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
return s.allowAnyProfile || s.allowedProfiles[profile]
|
||||
}
|
||||
|
||||
// profileForContainer returns the container profile if set, otherwise the pod profile.
|
||||
func profileForContainer(pod *api.Pod, container *api.Container) string {
|
||||
containerProfile, ok := pod.Annotations[api.SeccompContainerAnnotationKeyPrefix+container.Name]
|
||||
if ok {
|
||||
return containerProfile
|
||||
}
|
||||
return pod.Annotations[api.SeccompPodAnnotationKey]
|
||||
}
|
322
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp/strategy_test.go
generated
vendored
Normal file
322
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp/strategy_test.go
generated
vendored
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
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 seccomp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
)
|
||||
|
||||
var (
|
||||
withoutSeccomp = map[string]string{"foo": "bar"}
|
||||
allowAnyNoDefault = map[string]string{
|
||||
AllowedProfilesAnnotationKey: "*",
|
||||
}
|
||||
allowAnyDefault = map[string]string{
|
||||
AllowedProfilesAnnotationKey: "*",
|
||||
DefaultProfileAnnotationKey: "foo",
|
||||
}
|
||||
allowAnyAndSpecificDefault = map[string]string{
|
||||
AllowedProfilesAnnotationKey: "*,bar",
|
||||
DefaultProfileAnnotationKey: "foo",
|
||||
}
|
||||
allowSpecific = map[string]string{
|
||||
AllowedProfilesAnnotationKey: "foo",
|
||||
}
|
||||
)
|
||||
|
||||
func TestNewStrategy(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
annotations map[string]string
|
||||
expectedAllowedProfilesString string
|
||||
expectedAllowAny bool
|
||||
expectedAllowedProfiles map[string]bool
|
||||
expectedDefaultProfile string
|
||||
}{
|
||||
"no seccomp": {
|
||||
annotations: withoutSeccomp,
|
||||
expectedAllowAny: false,
|
||||
expectedAllowedProfilesString: "",
|
||||
expectedAllowedProfiles: nil,
|
||||
expectedDefaultProfile: "",
|
||||
},
|
||||
"allow any, no default": {
|
||||
annotations: allowAnyNoDefault,
|
||||
expectedAllowAny: true,
|
||||
expectedAllowedProfilesString: "*",
|
||||
expectedAllowedProfiles: map[string]bool{},
|
||||
expectedDefaultProfile: "",
|
||||
},
|
||||
"allow any, default": {
|
||||
annotations: allowAnyDefault,
|
||||
expectedAllowAny: true,
|
||||
expectedAllowedProfilesString: "*",
|
||||
expectedAllowedProfiles: map[string]bool{},
|
||||
expectedDefaultProfile: "foo",
|
||||
},
|
||||
"allow any and specific, default": {
|
||||
annotations: allowAnyAndSpecificDefault,
|
||||
expectedAllowAny: true,
|
||||
expectedAllowedProfilesString: "*,bar",
|
||||
expectedAllowedProfiles: map[string]bool{
|
||||
"bar": true,
|
||||
},
|
||||
expectedDefaultProfile: "foo",
|
||||
},
|
||||
}
|
||||
for k, v := range tests {
|
||||
strat := NewStrategy(v.annotations)
|
||||
internalStrat, _ := strat.(*strategy)
|
||||
|
||||
if internalStrat.allowAnyProfile != v.expectedAllowAny {
|
||||
t.Errorf("%s expected allowAnyProfile to be %t but found %t", k, v.expectedAllowAny, internalStrat.allowAnyProfile)
|
||||
}
|
||||
if internalStrat.allowedProfilesString != v.expectedAllowedProfilesString {
|
||||
t.Errorf("%s expected allowedProfilesString to be %s but found %s", k, v.expectedAllowedProfilesString, internalStrat.allowedProfilesString)
|
||||
}
|
||||
if internalStrat.defaultProfile != v.expectedDefaultProfile {
|
||||
t.Errorf("%s expected defaultProfile to be %s but found %s", k, v.expectedDefaultProfile, internalStrat.defaultProfile)
|
||||
}
|
||||
if !reflect.DeepEqual(v.expectedAllowedProfiles, internalStrat.allowedProfiles) {
|
||||
t.Errorf("%s expected expectedAllowedProfiles to be %#v but found %#v", k, v.expectedAllowedProfiles, internalStrat.allowedProfiles)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
pspAnnotations map[string]string
|
||||
podAnnotations map[string]string
|
||||
expectedProfile string
|
||||
}{
|
||||
"no seccomp, no pod annotations": {
|
||||
pspAnnotations: withoutSeccomp,
|
||||
podAnnotations: nil,
|
||||
expectedProfile: "",
|
||||
},
|
||||
"no seccomp, pod annotations": {
|
||||
pspAnnotations: withoutSeccomp,
|
||||
podAnnotations: map[string]string{
|
||||
api.SeccompPodAnnotationKey: "foo",
|
||||
},
|
||||
expectedProfile: "foo",
|
||||
},
|
||||
"seccomp with no default, no pod annotations": {
|
||||
pspAnnotations: allowAnyNoDefault,
|
||||
podAnnotations: nil,
|
||||
expectedProfile: "",
|
||||
},
|
||||
"seccomp with no default, pod annotations": {
|
||||
pspAnnotations: allowAnyNoDefault,
|
||||
podAnnotations: map[string]string{
|
||||
api.SeccompPodAnnotationKey: "foo",
|
||||
},
|
||||
expectedProfile: "foo",
|
||||
},
|
||||
"seccomp with default, no pod annotations": {
|
||||
pspAnnotations: allowAnyDefault,
|
||||
podAnnotations: nil,
|
||||
expectedProfile: "foo",
|
||||
},
|
||||
"seccomp with default, pod annotations": {
|
||||
pspAnnotations: allowAnyDefault,
|
||||
podAnnotations: map[string]string{
|
||||
api.SeccompPodAnnotationKey: "bar",
|
||||
},
|
||||
expectedProfile: "bar",
|
||||
},
|
||||
}
|
||||
for k, v := range tests {
|
||||
strat := NewStrategy(v.pspAnnotations)
|
||||
actual, err := strat.Generate(v.podAnnotations, nil)
|
||||
if err != nil {
|
||||
t.Errorf("%s received error during generation %#v", k, err)
|
||||
continue
|
||||
}
|
||||
if actual != v.expectedProfile {
|
||||
t.Errorf("%s expected profile %s but received %s", k, v.expectedProfile, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePod(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
pspAnnotations map[string]string
|
||||
podAnnotations map[string]string
|
||||
expectedError string
|
||||
}{
|
||||
"no pod annotations, required profiles": {
|
||||
pspAnnotations: allowSpecific,
|
||||
podAnnotations: nil,
|
||||
expectedError: "Forbidden: is not an allowed seccomp profile. Valid values are foo",
|
||||
},
|
||||
"no pod annotations, no required profiles": {
|
||||
pspAnnotations: withoutSeccomp,
|
||||
podAnnotations: nil,
|
||||
expectedError: "",
|
||||
},
|
||||
"valid pod annotations, required profiles": {
|
||||
pspAnnotations: allowSpecific,
|
||||
podAnnotations: map[string]string{
|
||||
api.SeccompPodAnnotationKey: "foo",
|
||||
},
|
||||
expectedError: "",
|
||||
},
|
||||
"invalid pod annotations, required profiles": {
|
||||
pspAnnotations: allowSpecific,
|
||||
podAnnotations: map[string]string{
|
||||
api.SeccompPodAnnotationKey: "bar",
|
||||
},
|
||||
expectedError: "Forbidden: bar is not an allowed seccomp profile. Valid values are foo",
|
||||
},
|
||||
"pod annotations, no required profiles": {
|
||||
pspAnnotations: withoutSeccomp,
|
||||
podAnnotations: map[string]string{
|
||||
api.SeccompPodAnnotationKey: "foo",
|
||||
},
|
||||
expectedError: "Forbidden: seccomp may not be set",
|
||||
},
|
||||
"pod annotations, allow any": {
|
||||
pspAnnotations: allowAnyNoDefault,
|
||||
podAnnotations: map[string]string{
|
||||
api.SeccompPodAnnotationKey: "foo",
|
||||
},
|
||||
expectedError: "",
|
||||
},
|
||||
"no pod annotations, allow any": {
|
||||
pspAnnotations: allowAnyNoDefault,
|
||||
podAnnotations: nil,
|
||||
expectedError: "",
|
||||
},
|
||||
}
|
||||
for k, v := range tests {
|
||||
pod := &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: v.podAnnotations,
|
||||
},
|
||||
}
|
||||
strat := NewStrategy(v.pspAnnotations)
|
||||
errs := strat.ValidatePod(pod)
|
||||
if v.expectedError == "" && len(errs) != 0 {
|
||||
t.Errorf("%s expected no errors but received %#v", k, errs.ToAggregate().Error())
|
||||
}
|
||||
if v.expectedError != "" && len(errs) == 0 {
|
||||
t.Errorf("%s expected error %s but received none", k, v.expectedError)
|
||||
}
|
||||
if v.expectedError != "" && len(errs) > 1 {
|
||||
t.Errorf("%s received multiple errors: %s", k, errs.ToAggregate().Error())
|
||||
}
|
||||
if v.expectedError != "" && len(errs) == 1 && !strings.Contains(errs.ToAggregate().Error(), v.expectedError) {
|
||||
t.Errorf("%s expected error %s but received %s", k, v.expectedError, errs.ToAggregate().Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateContainer(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
pspAnnotations map[string]string
|
||||
podAnnotations map[string]string
|
||||
expectedError string
|
||||
}{
|
||||
"no pod annotations, required profiles": {
|
||||
pspAnnotations: allowSpecific,
|
||||
podAnnotations: nil,
|
||||
expectedError: "Forbidden: is not an allowed seccomp profile. Valid values are foo",
|
||||
},
|
||||
"no pod annotations, no required profiles": {
|
||||
pspAnnotations: withoutSeccomp,
|
||||
podAnnotations: nil,
|
||||
expectedError: "",
|
||||
},
|
||||
"valid pod annotations, required profiles": {
|
||||
pspAnnotations: allowSpecific,
|
||||
podAnnotations: map[string]string{
|
||||
api.SeccompContainerAnnotationKeyPrefix + "container": "foo",
|
||||
},
|
||||
expectedError: "",
|
||||
},
|
||||
"invalid pod annotations, required profiles": {
|
||||
pspAnnotations: allowSpecific,
|
||||
podAnnotations: map[string]string{
|
||||
api.SeccompContainerAnnotationKeyPrefix + "container": "bar",
|
||||
},
|
||||
expectedError: "Forbidden: bar is not an allowed seccomp profile. Valid values are foo",
|
||||
},
|
||||
"pod annotations, no required profiles": {
|
||||
pspAnnotations: withoutSeccomp,
|
||||
podAnnotations: map[string]string{
|
||||
api.SeccompContainerAnnotationKeyPrefix + "container": "foo",
|
||||
},
|
||||
expectedError: "Forbidden: seccomp may not be set",
|
||||
},
|
||||
"pod annotations, allow any": {
|
||||
pspAnnotations: allowAnyNoDefault,
|
||||
podAnnotations: map[string]string{
|
||||
api.SeccompContainerAnnotationKeyPrefix + "container": "foo",
|
||||
},
|
||||
expectedError: "",
|
||||
},
|
||||
"no pod annotations, allow any": {
|
||||
pspAnnotations: allowAnyNoDefault,
|
||||
podAnnotations: nil,
|
||||
expectedError: "",
|
||||
},
|
||||
"container inherits valid pod annotation": {
|
||||
pspAnnotations: allowSpecific,
|
||||
podAnnotations: map[string]string{
|
||||
api.SeccompPodAnnotationKey: "foo",
|
||||
},
|
||||
expectedError: "",
|
||||
},
|
||||
"container inherits invalid pod annotation": {
|
||||
pspAnnotations: allowSpecific,
|
||||
podAnnotations: map[string]string{
|
||||
api.SeccompPodAnnotationKey: "bar",
|
||||
},
|
||||
expectedError: "Forbidden: bar is not an allowed seccomp profile. Valid values are foo",
|
||||
},
|
||||
}
|
||||
for k, v := range tests {
|
||||
pod := &api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: v.podAnnotations,
|
||||
},
|
||||
}
|
||||
container := &api.Container{
|
||||
Name: "container",
|
||||
}
|
||||
|
||||
strat := NewStrategy(v.pspAnnotations)
|
||||
errs := strat.ValidateContainer(pod, container)
|
||||
if v.expectedError == "" && len(errs) != 0 {
|
||||
t.Errorf("%s expected no errors but received %#v", k, errs.ToAggregate().Error())
|
||||
}
|
||||
if v.expectedError != "" && len(errs) == 0 {
|
||||
t.Errorf("%s expected error %s but received none", k, v.expectedError)
|
||||
}
|
||||
if v.expectedError != "" && len(errs) > 1 {
|
||||
t.Errorf("%s received multiple errors: %s", k, errs.ToAggregate().Error())
|
||||
}
|
||||
if v.expectedError != "" && len(errs) == 1 && !strings.Contains(errs.ToAggregate().Error(), v.expectedError) {
|
||||
t.Errorf("%s expected error %s but received %s", k, v.expectedError, errs.ToAggregate().Error())
|
||||
}
|
||||
}
|
||||
}
|
50
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux/BUILD
generated
vendored
Normal file
50
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux/BUILD
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"mustrunas.go",
|
||||
"runasany.go",
|
||||
"types.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"mustrunas_test.go",
|
||||
"runasany_test.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
19
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
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 selinux contains code for validating and defaulting the SELinux
|
||||
// context of a pod according to a security policy.
|
||||
package selinux
|
76
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux/mustrunas.go
generated
vendored
Normal file
76
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux/mustrunas.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
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 selinux
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
type mustRunAs struct {
|
||||
opts *extensions.SELinuxStrategyOptions
|
||||
}
|
||||
|
||||
var _ SELinuxStrategy = &mustRunAs{}
|
||||
|
||||
func NewMustRunAs(options *extensions.SELinuxStrategyOptions) (SELinuxStrategy, error) {
|
||||
if options == nil {
|
||||
return nil, fmt.Errorf("MustRunAs requires SELinuxContextStrategyOptions")
|
||||
}
|
||||
if options.SELinuxOptions == nil {
|
||||
return nil, fmt.Errorf("MustRunAs requires SELinuxOptions")
|
||||
}
|
||||
return &mustRunAs{
|
||||
opts: options,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Generate creates the SELinuxOptions based on constraint rules.
|
||||
func (s *mustRunAs) Generate(_ *api.Pod, _ *api.Container) (*api.SELinuxOptions, error) {
|
||||
return s.opts.SELinuxOptions, nil
|
||||
}
|
||||
|
||||
// Validate ensures that the specified values fall within the range of the strategy.
|
||||
func (s *mustRunAs) Validate(fldPath *field.Path, _ *api.Pod, _ *api.Container, seLinux *api.SELinuxOptions) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if seLinux == nil {
|
||||
allErrs = append(allErrs, field.Required(fldPath, ""))
|
||||
return allErrs
|
||||
}
|
||||
if seLinux.Level != s.opts.SELinuxOptions.Level {
|
||||
detail := fmt.Sprintf("must be %s", s.opts.SELinuxOptions.Level)
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("level"), seLinux.Level, detail))
|
||||
}
|
||||
if seLinux.Role != s.opts.SELinuxOptions.Role {
|
||||
detail := fmt.Sprintf("must be %s", s.opts.SELinuxOptions.Role)
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("role"), seLinux.Role, detail))
|
||||
}
|
||||
if seLinux.Type != s.opts.SELinuxOptions.Type {
|
||||
detail := fmt.Sprintf("must be %s", s.opts.SELinuxOptions.Type)
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("type"), seLinux.Type, detail))
|
||||
}
|
||||
if seLinux.User != s.opts.SELinuxOptions.User {
|
||||
detail := fmt.Sprintf("must be %s", s.opts.SELinuxOptions.User)
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("user"), seLinux.User, detail))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
153
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux/mustrunas_test.go
generated
vendored
Normal file
153
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux/mustrunas_test.go
generated
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
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 selinux
|
||||
|
||||
import (
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMustRunAsOptions(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
opts *extensions.SELinuxStrategyOptions
|
||||
pass bool
|
||||
}{
|
||||
"invalid opts": {
|
||||
opts: &extensions.SELinuxStrategyOptions{},
|
||||
pass: false,
|
||||
},
|
||||
"valid opts": {
|
||||
opts: &extensions.SELinuxStrategyOptions{SELinuxOptions: &api.SELinuxOptions{}},
|
||||
pass: true,
|
||||
},
|
||||
}
|
||||
for name, tc := range tests {
|
||||
_, err := NewMustRunAs(tc.opts)
|
||||
if err != nil && tc.pass {
|
||||
t.Errorf("%s expected to pass but received error %#v", name, err)
|
||||
}
|
||||
if err == nil && !tc.pass {
|
||||
t.Errorf("%s expected to fail but did not receive an error", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustRunAsGenerate(t *testing.T) {
|
||||
opts := &extensions.SELinuxStrategyOptions{
|
||||
SELinuxOptions: &api.SELinuxOptions{
|
||||
User: "user",
|
||||
Role: "role",
|
||||
Type: "type",
|
||||
Level: "level",
|
||||
},
|
||||
}
|
||||
mustRunAs, err := NewMustRunAs(opts)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error initializing NewMustRunAs %v", err)
|
||||
}
|
||||
generated, err := mustRunAs.Generate(nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error generating selinux %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(generated, opts.SELinuxOptions) {
|
||||
t.Errorf("generated selinux does not equal configured selinux")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustRunAsValidate(t *testing.T) {
|
||||
newValidOpts := func() *api.SELinuxOptions {
|
||||
return &api.SELinuxOptions{
|
||||
User: "user",
|
||||
Role: "role",
|
||||
Level: "level",
|
||||
Type: "type",
|
||||
}
|
||||
}
|
||||
|
||||
role := newValidOpts()
|
||||
role.Role = "invalid"
|
||||
|
||||
user := newValidOpts()
|
||||
user.User = "invalid"
|
||||
|
||||
level := newValidOpts()
|
||||
level.Level = "invalid"
|
||||
|
||||
seType := newValidOpts()
|
||||
seType.Type = "invalid"
|
||||
|
||||
tests := map[string]struct {
|
||||
seLinux *api.SELinuxOptions
|
||||
expectedMsg string
|
||||
}{
|
||||
"invalid role": {
|
||||
seLinux: role,
|
||||
expectedMsg: "role: Invalid value",
|
||||
},
|
||||
"invalid user": {
|
||||
seLinux: user,
|
||||
expectedMsg: "user: Invalid value",
|
||||
},
|
||||
"invalid level": {
|
||||
seLinux: level,
|
||||
expectedMsg: "level: Invalid value",
|
||||
},
|
||||
"invalid type": {
|
||||
seLinux: seType,
|
||||
expectedMsg: "type: Invalid value",
|
||||
},
|
||||
"valid": {
|
||||
seLinux: newValidOpts(),
|
||||
expectedMsg: "",
|
||||
},
|
||||
}
|
||||
|
||||
opts := &extensions.SELinuxStrategyOptions{
|
||||
SELinuxOptions: newValidOpts(),
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
mustRunAs, err := NewMustRunAs(opts)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error initializing NewMustRunAs for testcase %s: %#v", name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
errs := mustRunAs.Validate(nil, nil, nil, tc.seLinux)
|
||||
//should've passed but didn't
|
||||
if len(tc.expectedMsg) == 0 && len(errs) > 0 {
|
||||
t.Errorf("%s expected no errors but received %v", name, errs)
|
||||
}
|
||||
//should've failed but didn't
|
||||
if len(tc.expectedMsg) != 0 && len(errs) == 0 {
|
||||
t.Errorf("%s expected error %s but received no errors", name, tc.expectedMsg)
|
||||
}
|
||||
//failed with additional messages
|
||||
if len(tc.expectedMsg) != 0 && len(errs) > 1 {
|
||||
t.Errorf("%s expected error %s but received multiple errors: %v", name, tc.expectedMsg, errs)
|
||||
}
|
||||
//check that we got the right message
|
||||
if len(tc.expectedMsg) != 0 && len(errs) == 1 {
|
||||
if !strings.Contains(errs[0].Error(), tc.expectedMsg) {
|
||||
t.Errorf("%s expected error to contain %s but it did not: %v", name, tc.expectedMsg, errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
43
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux/runasany.go
generated
vendored
Normal file
43
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux/runasany.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
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 selinux
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
// runAsAny implements the SELinuxStrategy interface.
|
||||
type runAsAny struct{}
|
||||
|
||||
var _ SELinuxStrategy = &runAsAny{}
|
||||
|
||||
// NewRunAsAny provides a strategy that will return the configured se linux context or nil.
|
||||
func NewRunAsAny(options *extensions.SELinuxStrategyOptions) (SELinuxStrategy, error) {
|
||||
return &runAsAny{}, nil
|
||||
}
|
||||
|
||||
// Generate creates the SELinuxOptions based on constraint rules.
|
||||
func (s *runAsAny) Generate(pod *api.Pod, container *api.Container) (*api.SELinuxOptions, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Validate ensures that the specified values fall within the range of the strategy.
|
||||
func (s *runAsAny) Validate(fldPath *field.Path, _ *api.Pod, _ *api.Container, options *api.SELinuxOptions) field.ErrorList {
|
||||
return field.ErrorList{}
|
||||
}
|
72
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux/runasany_test.go
generated
vendored
Normal file
72
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux/runasany_test.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
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 selinux
|
||||
|
||||
import (
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRunAsAnyOptions(t *testing.T) {
|
||||
_, err := NewRunAsAny(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
|
||||
}
|
||||
_, err = NewRunAsAny(&extensions.SELinuxStrategyOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error initializing NewRunAsAny %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunAsAnyGenerate(t *testing.T) {
|
||||
s, err := NewRunAsAny(&extensions.SELinuxStrategyOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
|
||||
}
|
||||
uid, err := s.Generate(nil, nil)
|
||||
if uid != nil {
|
||||
t.Errorf("expected nil uid but got %v", *uid)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error generating uid %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunAsAnyValidate(t *testing.T) {
|
||||
s, err := NewRunAsAny(&extensions.SELinuxStrategyOptions{
|
||||
SELinuxOptions: &api.SELinuxOptions{
|
||||
Level: "foo",
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
|
||||
}
|
||||
errs := s.Validate(nil, nil, nil, nil)
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("unexpected errors validating with ")
|
||||
}
|
||||
s, err = NewRunAsAny(&extensions.SELinuxStrategyOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
|
||||
}
|
||||
errs = s.Validate(nil, nil, nil, nil)
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("unexpected errors validating %v", errs)
|
||||
}
|
||||
}
|
30
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux/types.go
generated
vendored
Normal file
30
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux/types.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
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 selinux
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
)
|
||||
|
||||
// SELinuxStrategy defines the interface for all SELinux constraint strategies.
|
||||
type SELinuxStrategy interface {
|
||||
// Generate creates the SELinuxOptions based on constraint rules.
|
||||
Generate(pod *api.Pod, container *api.Container) (*api.SELinuxOptions, error)
|
||||
// Validate ensures that the specified values fall within the range of the strategy.
|
||||
Validate(fldPath *field.Path, pod *api.Pod, container *api.Container, options *api.SELinuxOptions) field.ErrorList
|
||||
}
|
45
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/sysctl/BUILD
generated
vendored
Normal file
45
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/sysctl/BUILD
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"mustmatchpatterns.go",
|
||||
"types.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy/sysctl",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/core/helper:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["mustmatchpatterns_test.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy/sysctl",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/core/helper:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
93
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/sysctl/mustmatchpatterns.go
generated
vendored
Normal file
93
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/sysctl/mustmatchpatterns.go
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
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/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/helper"
|
||||
)
|
||||
|
||||
// mustMatchPatterns implements the SysctlsStrategy interface
|
||||
type mustMatchPatterns struct {
|
||||
patterns []string
|
||||
}
|
||||
|
||||
var (
|
||||
_ SysctlsStrategy = &mustMatchPatterns{}
|
||||
|
||||
defaultSysctlsPatterns = []string{"*"}
|
||||
)
|
||||
|
||||
// NewMustMatchPatterns creates a new mustMatchPatterns 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 {
|
||||
if patterns == nil {
|
||||
patterns = defaultSysctlsPatterns
|
||||
}
|
||||
return &mustMatchPatterns{
|
||||
patterns: patterns,
|
||||
}
|
||||
}
|
||||
|
||||
// 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 := helper.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))}
|
||||
}
|
102
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/sysctl/mustmatchpatterns_test.go
generated
vendored
Normal file
102
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/sysctl/mustmatchpatterns_test.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
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 (
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/helper"
|
||||
"testing"
|
||||
)
|
||||
|
||||
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 := NewMustMatchPatterns(v.patterns)
|
||||
|
||||
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: helper.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: helper.PodAnnotationsFromSysctls([]api.Sysctl{{Name: s, Value: "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")
|
||||
}
|
||||
}
|
28
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/sysctl/types.go
generated
vendored
Normal file
28
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/sysctl/types.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
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/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
70
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/types.go
generated
vendored
Normal file
70
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/types.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
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 podsecuritypolicy
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/apparmor"
|
||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/capabilities"
|
||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/group"
|
||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp"
|
||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/selinux"
|
||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/sysctl"
|
||||
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/user"
|
||||
)
|
||||
|
||||
// Provider provides the implementation to generate a new security
|
||||
// context based on constraints or validate an existing security context against constraints.
|
||||
type Provider interface {
|
||||
// Create a PodSecurityContext based on the given constraints. Also returns an updated set
|
||||
// of Pod annotations for alpha feature support.
|
||||
CreatePodSecurityContext(pod *api.Pod) (*api.PodSecurityContext, map[string]string, error)
|
||||
// Create a container SecurityContext based on the given constraints. Also returns an updated set
|
||||
// of Pod annotations for alpha feature support.
|
||||
CreateContainerSecurityContext(pod *api.Pod, container *api.Container) (*api.SecurityContext, map[string]string, error)
|
||||
// Ensure a pod's SecurityContext is in compliance with the given constraints.
|
||||
ValidatePodSecurityContext(pod *api.Pod, fldPath *field.Path) field.ErrorList
|
||||
// Ensure a container's SecurityContext is in compliance with the given constraints
|
||||
ValidateContainerSecurityContext(pod *api.Pod, container *api.Container, fldPath *field.Path) field.ErrorList
|
||||
// Get the name of the PSP that this provider was initialized with.
|
||||
GetPSPName() string
|
||||
}
|
||||
|
||||
// StrategyFactory abstracts how the strategies are created from the provider so that you may
|
||||
// implement your own custom strategies that may pull information from other resources as necessary.
|
||||
// For example, if you would like to populate the strategies with values from namespace annotations
|
||||
// you may create a factory with a client that can pull the namespace and populate the appropriate
|
||||
// values.
|
||||
type StrategyFactory interface {
|
||||
// CreateStrategies creates the strategies that a provider will use. The namespace argument
|
||||
// should be the namespace of the object being checked (the pod's namespace).
|
||||
CreateStrategies(psp *extensions.PodSecurityPolicy, namespace string) (*ProviderStrategies, error)
|
||||
}
|
||||
|
||||
// ProviderStrategies is a holder for all strategies that the provider requires to be populated.
|
||||
type ProviderStrategies struct {
|
||||
RunAsUserStrategy user.RunAsUserStrategy
|
||||
SELinuxStrategy selinux.SELinuxStrategy
|
||||
AppArmorStrategy apparmor.Strategy
|
||||
FSGroupStrategy group.GroupStrategy
|
||||
SupplementalGroupStrategy group.GroupStrategy
|
||||
CapabilitiesStrategy capabilities.Strategy
|
||||
SysctlsStrategy sysctl.SysctlsStrategy
|
||||
SeccompStrategy seccomp.Strategy
|
||||
}
|
53
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/BUILD
generated
vendored
Normal file
53
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/BUILD
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"mustrunas.go",
|
||||
"nonroot.go",
|
||||
"runasany.go",
|
||||
"types.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy/user",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/security/podsecuritypolicy/util:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"mustrunas_test.go",
|
||||
"nonroot_test.go",
|
||||
"runasany_test.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy/user",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
19
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
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 user contains code for validating and defaulting the UID of a pod
|
||||
// or container according to a security policy.
|
||||
package user
|
74
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/mustrunas.go
generated
vendored
Normal file
74
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/mustrunas.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
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 user
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
|
||||
)
|
||||
|
||||
// mustRunAs implements the RunAsUserStrategy interface
|
||||
type mustRunAs struct {
|
||||
opts *extensions.RunAsUserStrategyOptions
|
||||
}
|
||||
|
||||
// NewMustRunAs provides a strategy that requires the container to run as a specific UID in a range.
|
||||
func NewMustRunAs(options *extensions.RunAsUserStrategyOptions) (RunAsUserStrategy, error) {
|
||||
if options == nil {
|
||||
return nil, fmt.Errorf("MustRunAsRange requires run as user options")
|
||||
}
|
||||
if len(options.Ranges) == 0 {
|
||||
return nil, fmt.Errorf("MustRunAsRange requires at least one range")
|
||||
}
|
||||
return &mustRunAs{
|
||||
opts: options,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Generate creates the uid based on policy rules. MustRunAs returns the first range's Min.
|
||||
func (s *mustRunAs) Generate(pod *api.Pod, container *api.Container) (*int64, error) {
|
||||
return &s.opts.Ranges[0].Min, nil
|
||||
}
|
||||
|
||||
// Validate ensures that the specified values fall within the range of the strategy.
|
||||
func (s *mustRunAs) Validate(fldPath *field.Path, _ *api.Pod, _ *api.Container, runAsNonRoot *bool, runAsUser *int64) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if runAsUser == nil {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("runAsUser"), ""))
|
||||
return allErrs
|
||||
}
|
||||
|
||||
if !s.isValidUID(*runAsUser) {
|
||||
detail := fmt.Sprintf("must be in the ranges: %v", s.opts.Ranges)
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsUser"), *runAsUser, detail))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func (s *mustRunAs) isValidUID(id int64) bool {
|
||||
for _, rng := range s.opts.Ranges {
|
||||
if psputil.UserFallsInRange(id, rng) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
144
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/mustrunas_test.go
generated
vendored
Normal file
144
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/mustrunas_test.go
generated
vendored
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
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 user
|
||||
|
||||
import (
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewMustRunAs(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
opts *extensions.RunAsUserStrategyOptions
|
||||
pass bool
|
||||
}{
|
||||
"nil opts": {
|
||||
opts: nil,
|
||||
pass: false,
|
||||
},
|
||||
"invalid opts": {
|
||||
opts: &extensions.RunAsUserStrategyOptions{},
|
||||
pass: false,
|
||||
},
|
||||
"valid opts": {
|
||||
opts: &extensions.RunAsUserStrategyOptions{
|
||||
Ranges: []extensions.UserIDRange{
|
||||
{Min: 1, Max: 1},
|
||||
},
|
||||
},
|
||||
pass: true,
|
||||
},
|
||||
}
|
||||
for name, tc := range tests {
|
||||
_, err := NewMustRunAs(tc.opts)
|
||||
if err != nil && tc.pass {
|
||||
t.Errorf("%s expected to pass but received error %#v", name, err)
|
||||
}
|
||||
if err == nil && !tc.pass {
|
||||
t.Errorf("%s expected to fail but did not receive an error", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
opts := &extensions.RunAsUserStrategyOptions{
|
||||
Ranges: []extensions.UserIDRange{
|
||||
{Min: 1, Max: 1},
|
||||
},
|
||||
}
|
||||
mustRunAs, err := NewMustRunAs(opts)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error initializing NewMustRunAs %v", err)
|
||||
}
|
||||
generated, err := mustRunAs.Generate(nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error generating runAsUser %v", err)
|
||||
}
|
||||
if *generated != opts.Ranges[0].Min {
|
||||
t.Errorf("generated runAsUser does not equal configured runAsUser")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
opts := &extensions.RunAsUserStrategyOptions{
|
||||
Ranges: []extensions.UserIDRange{
|
||||
{Min: 1, Max: 1},
|
||||
{Min: 10, Max: 20},
|
||||
},
|
||||
}
|
||||
|
||||
validID := int64(15)
|
||||
invalidID := int64(21)
|
||||
|
||||
tests := map[string]struct {
|
||||
container *api.Container
|
||||
expectedMsg string
|
||||
}{
|
||||
"good container": {
|
||||
container: &api.Container{
|
||||
SecurityContext: &api.SecurityContext{
|
||||
RunAsUser: &validID,
|
||||
},
|
||||
},
|
||||
},
|
||||
"nil run as user": {
|
||||
container: &api.Container{
|
||||
SecurityContext: &api.SecurityContext{
|
||||
RunAsUser: nil,
|
||||
},
|
||||
},
|
||||
expectedMsg: "runAsUser: Required",
|
||||
},
|
||||
"invalid id": {
|
||||
container: &api.Container{
|
||||
SecurityContext: &api.SecurityContext{
|
||||
RunAsUser: &invalidID,
|
||||
},
|
||||
},
|
||||
expectedMsg: "runAsUser: Invalid",
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
mustRunAs, err := NewMustRunAs(opts)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error initializing NewMustRunAs for testcase %s: %#v", name, err)
|
||||
continue
|
||||
}
|
||||
errs := mustRunAs.Validate(nil, nil, nil, tc.container.SecurityContext.RunAsNonRoot, tc.container.SecurityContext.RunAsUser)
|
||||
//should've passed but didn't
|
||||
if len(tc.expectedMsg) == 0 && len(errs) > 0 {
|
||||
t.Errorf("%s expected no errors but received %v", name, errs)
|
||||
}
|
||||
//should've failed but didn't
|
||||
if len(tc.expectedMsg) != 0 && len(errs) == 0 {
|
||||
t.Errorf("%s expected error %s but received no errors", name, tc.expectedMsg)
|
||||
}
|
||||
//failed with additional messages
|
||||
if len(tc.expectedMsg) != 0 && len(errs) > 1 {
|
||||
t.Errorf("%s expected error %s but received multiple errors: %v", name, tc.expectedMsg, errs)
|
||||
}
|
||||
//check that we got the right message
|
||||
if len(tc.expectedMsg) != 0 && len(errs) == 1 {
|
||||
if !strings.Contains(errs[0].Error(), tc.expectedMsg) {
|
||||
t.Errorf("%s expected error to contain %s but it did not: %v", name, tc.expectedMsg, errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
59
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/nonroot.go
generated
vendored
Normal file
59
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/nonroot.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
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 user
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
type nonRoot struct{}
|
||||
|
||||
var _ RunAsUserStrategy = &nonRoot{}
|
||||
|
||||
func NewRunAsNonRoot(options *extensions.RunAsUserStrategyOptions) (RunAsUserStrategy, error) {
|
||||
return &nonRoot{}, nil
|
||||
}
|
||||
|
||||
// Generate creates the uid based on policy rules. This strategy does return a UID. It assumes
|
||||
// that the user will specify a UID or the container image specifies a UID.
|
||||
func (s *nonRoot) Generate(pod *api.Pod, container *api.Container) (*int64, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Validate ensures that the specified values fall within the range of the strategy. Validation
|
||||
// of this will pass if either the UID is not set, assuming that the image will provided the UID
|
||||
// or if the UID is set it is not root. Validation will fail if RunAsNonRoot is set to false.
|
||||
// In order to work properly this assumes that the kubelet performs a final check on runAsUser
|
||||
// or the image UID when runAsUser is nil.
|
||||
func (s *nonRoot) Validate(fldPath *field.Path, _ *api.Pod, _ *api.Container, runAsNonRoot *bool, runAsUser *int64) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if runAsNonRoot == nil && runAsUser == nil {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("runAsNonRoot"), "must be true"))
|
||||
return allErrs
|
||||
}
|
||||
if runAsNonRoot != nil && *runAsNonRoot == false {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsNonRoot"), *runAsNonRoot, "must be true"))
|
||||
return allErrs
|
||||
}
|
||||
if runAsUser != nil && *runAsUser == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsUser"), *runAsUser, "running with the root UID is forbidden"))
|
||||
return allErrs
|
||||
}
|
||||
return allErrs
|
||||
}
|
119
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/nonroot_test.go
generated
vendored
Normal file
119
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/nonroot_test.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
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 user
|
||||
|
||||
import (
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNonRootOptions(t *testing.T) {
|
||||
_, err := NewRunAsNonRoot(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error initializing NewRunAsNonRoot %v", err)
|
||||
}
|
||||
_, err = NewRunAsNonRoot(&extensions.RunAsUserStrategyOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error initializing NewRunAsNonRoot %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonRootGenerate(t *testing.T) {
|
||||
s, err := NewRunAsNonRoot(&extensions.RunAsUserStrategyOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error initializing NewRunAsNonRoot %v", err)
|
||||
}
|
||||
uid, err := s.Generate(nil, nil)
|
||||
if uid != nil {
|
||||
t.Errorf("expected nil uid but got %d", *uid)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error generating uid %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonRootValidate(t *testing.T) {
|
||||
goodUID := int64(1)
|
||||
badUID := int64(0)
|
||||
untrue := false
|
||||
unfalse := true
|
||||
s, err := NewRunAsNonRoot(&extensions.RunAsUserStrategyOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error initializing NewMustRunAs %v", err)
|
||||
}
|
||||
tests := []struct {
|
||||
container *api.Container
|
||||
expectedErr bool
|
||||
msg string
|
||||
}{
|
||||
{
|
||||
container: &api.Container{
|
||||
SecurityContext: &api.SecurityContext{
|
||||
RunAsUser: &badUID,
|
||||
},
|
||||
},
|
||||
expectedErr: true,
|
||||
msg: "in test case %d, expected errors from root uid but got none: %v",
|
||||
},
|
||||
{
|
||||
container: &api.Container{
|
||||
SecurityContext: &api.SecurityContext{
|
||||
RunAsUser: &goodUID,
|
||||
},
|
||||
},
|
||||
expectedErr: false,
|
||||
msg: "in test case %d, expected no errors from non-root uid but got %v",
|
||||
},
|
||||
{
|
||||
container: &api.Container{
|
||||
SecurityContext: &api.SecurityContext{
|
||||
RunAsNonRoot: &untrue,
|
||||
},
|
||||
},
|
||||
expectedErr: true,
|
||||
msg: "in test case %d, expected errors from RunAsNonRoot but got none: %v",
|
||||
},
|
||||
{
|
||||
container: &api.Container{
|
||||
SecurityContext: &api.SecurityContext{
|
||||
RunAsNonRoot: &unfalse,
|
||||
RunAsUser: &goodUID,
|
||||
},
|
||||
},
|
||||
expectedErr: false,
|
||||
msg: "in test case %d, expected no errors from non-root uid but got %v",
|
||||
},
|
||||
{
|
||||
container: &api.Container{
|
||||
SecurityContext: &api.SecurityContext{
|
||||
RunAsNonRoot: nil,
|
||||
RunAsUser: nil,
|
||||
},
|
||||
},
|
||||
expectedErr: true,
|
||||
msg: "in test case %d, expected errors from nil runAsNonRoot and nil runAsUser but got %v",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range tests {
|
||||
errs := s.Validate(nil, nil, nil, tc.container.SecurityContext.RunAsNonRoot, tc.container.SecurityContext.RunAsUser)
|
||||
if (len(errs) == 0) == tc.expectedErr {
|
||||
t.Errorf(tc.msg, i, errs)
|
||||
}
|
||||
}
|
||||
}
|
43
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/runasany.go
generated
vendored
Normal file
43
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/runasany.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
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 user
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
// runAsAny implements the interface RunAsUserStrategy.
|
||||
type runAsAny struct{}
|
||||
|
||||
var _ RunAsUserStrategy = &runAsAny{}
|
||||
|
||||
// NewRunAsAny provides a strategy that will return nil.
|
||||
func NewRunAsAny(options *extensions.RunAsUserStrategyOptions) (RunAsUserStrategy, error) {
|
||||
return &runAsAny{}, nil
|
||||
}
|
||||
|
||||
// Generate creates the uid based on policy rules.
|
||||
func (s *runAsAny) Generate(pod *api.Pod, container *api.Container) (*int64, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Validate ensures that the specified values fall within the range of the strategy.
|
||||
func (s *runAsAny) Validate(fldPath *field.Path, _ *api.Pod, _ *api.Container, runAsNonRoot *bool, runAsUser *int64) field.ErrorList {
|
||||
return field.ErrorList{}
|
||||
}
|
59
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/runasany_test.go
generated
vendored
Normal file
59
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/runasany_test.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
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 user
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
func TestRunAsAnyOptions(t *testing.T) {
|
||||
_, err := NewRunAsAny(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
|
||||
}
|
||||
_, err = NewRunAsAny(&extensions.RunAsUserStrategyOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error initializing NewRunAsAny %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunAsAnyGenerate(t *testing.T) {
|
||||
s, err := NewRunAsAny(&extensions.RunAsUserStrategyOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
|
||||
}
|
||||
uid, err := s.Generate(nil, nil)
|
||||
if uid != nil {
|
||||
t.Errorf("expected nil uid but got %d", *uid)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error generating uid %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunAsAnyValidate(t *testing.T) {
|
||||
s, err := NewRunAsAny(&extensions.RunAsUserStrategyOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
|
||||
}
|
||||
errs := s.Validate(nil, nil, nil, nil, nil)
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("unexpected errors validating with ")
|
||||
}
|
||||
}
|
30
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/types.go
generated
vendored
Normal file
30
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/user/types.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
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 user
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
)
|
||||
|
||||
// RunAsUserStrategy defines the interface for all uid constraint strategies.
|
||||
type RunAsUserStrategy interface {
|
||||
// Generate creates the uid based on policy rules.
|
||||
Generate(pod *api.Pod, container *api.Container) (*int64, error)
|
||||
// Validate ensures that the specified values fall within the range of the strategy.
|
||||
Validate(fldPath *field.Path, pod *api.Pod, container *api.Container, runAsNonRoot *bool, runAsUser *int64) field.ErrorList
|
||||
}
|
45
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/util/BUILD
generated
vendored
Normal file
45
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/util/BUILD
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"util.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["util_test.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
19
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/util/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/util/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
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 util contains utility code shared amongst different parts of the
|
||||
// pod security policy apparatus.
|
||||
package util
|
224
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/util/util.go
generated
vendored
Normal file
224
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/util/util.go
generated
vendored
Normal file
@ -0,0 +1,224 @@
|
||||
/*
|
||||
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 util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
const (
|
||||
ValidatedPSPAnnotation = "kubernetes.io/psp"
|
||||
)
|
||||
|
||||
func GetAllFSTypesExcept(exceptions ...string) sets.String {
|
||||
fstypes := GetAllFSTypesAsSet()
|
||||
for _, e := range exceptions {
|
||||
fstypes.Delete(e)
|
||||
}
|
||||
return fstypes
|
||||
}
|
||||
|
||||
func GetAllFSTypesAsSet() sets.String {
|
||||
fstypes := sets.NewString()
|
||||
fstypes.Insert(
|
||||
string(extensions.HostPath),
|
||||
string(extensions.AzureFile),
|
||||
string(extensions.Flocker),
|
||||
string(extensions.FlexVolume),
|
||||
string(extensions.EmptyDir),
|
||||
string(extensions.GCEPersistentDisk),
|
||||
string(extensions.AWSElasticBlockStore),
|
||||
string(extensions.GitRepo),
|
||||
string(extensions.Secret),
|
||||
string(extensions.NFS),
|
||||
string(extensions.ISCSI),
|
||||
string(extensions.Glusterfs),
|
||||
string(extensions.PersistentVolumeClaim),
|
||||
string(extensions.RBD),
|
||||
string(extensions.Cinder),
|
||||
string(extensions.CephFS),
|
||||
string(extensions.DownwardAPI),
|
||||
string(extensions.FC),
|
||||
string(extensions.ConfigMap),
|
||||
string(extensions.VsphereVolume),
|
||||
string(extensions.Quobyte),
|
||||
string(extensions.AzureDisk),
|
||||
string(extensions.PhotonPersistentDisk),
|
||||
string(extensions.StorageOS),
|
||||
string(extensions.Projected),
|
||||
string(extensions.PortworxVolume),
|
||||
string(extensions.ScaleIO),
|
||||
string(extensions.CSI),
|
||||
)
|
||||
return fstypes
|
||||
}
|
||||
|
||||
// getVolumeFSType gets the FSType for a volume.
|
||||
func GetVolumeFSType(v api.Volume) (extensions.FSType, error) {
|
||||
switch {
|
||||
case v.HostPath != nil:
|
||||
return extensions.HostPath, nil
|
||||
case v.EmptyDir != nil:
|
||||
return extensions.EmptyDir, nil
|
||||
case v.GCEPersistentDisk != nil:
|
||||
return extensions.GCEPersistentDisk, nil
|
||||
case v.AWSElasticBlockStore != nil:
|
||||
return extensions.AWSElasticBlockStore, nil
|
||||
case v.GitRepo != nil:
|
||||
return extensions.GitRepo, nil
|
||||
case v.Secret != nil:
|
||||
return extensions.Secret, nil
|
||||
case v.NFS != nil:
|
||||
return extensions.NFS, nil
|
||||
case v.ISCSI != nil:
|
||||
return extensions.ISCSI, nil
|
||||
case v.Glusterfs != nil:
|
||||
return extensions.Glusterfs, nil
|
||||
case v.PersistentVolumeClaim != nil:
|
||||
return extensions.PersistentVolumeClaim, nil
|
||||
case v.RBD != nil:
|
||||
return extensions.RBD, nil
|
||||
case v.FlexVolume != nil:
|
||||
return extensions.FlexVolume, nil
|
||||
case v.Cinder != nil:
|
||||
return extensions.Cinder, nil
|
||||
case v.CephFS != nil:
|
||||
return extensions.CephFS, nil
|
||||
case v.Flocker != nil:
|
||||
return extensions.Flocker, nil
|
||||
case v.DownwardAPI != nil:
|
||||
return extensions.DownwardAPI, nil
|
||||
case v.FC != nil:
|
||||
return extensions.FC, nil
|
||||
case v.AzureFile != nil:
|
||||
return extensions.AzureFile, nil
|
||||
case v.ConfigMap != nil:
|
||||
return extensions.ConfigMap, nil
|
||||
case v.VsphereVolume != nil:
|
||||
return extensions.VsphereVolume, nil
|
||||
case v.Quobyte != nil:
|
||||
return extensions.Quobyte, nil
|
||||
case v.AzureDisk != nil:
|
||||
return extensions.AzureDisk, nil
|
||||
case v.PhotonPersistentDisk != nil:
|
||||
return extensions.PhotonPersistentDisk, nil
|
||||
case v.StorageOS != nil:
|
||||
return extensions.StorageOS, nil
|
||||
case v.Projected != nil:
|
||||
return extensions.Projected, nil
|
||||
case v.PortworxVolume != nil:
|
||||
return extensions.PortworxVolume, nil
|
||||
case v.ScaleIO != nil:
|
||||
return extensions.ScaleIO, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("unknown volume type for volume: %#v", v)
|
||||
}
|
||||
|
||||
// FSTypeToStringSet converts an FSType slice to a string set.
|
||||
func FSTypeToStringSet(fsTypes []extensions.FSType) sets.String {
|
||||
set := sets.NewString()
|
||||
for _, v := range fsTypes {
|
||||
set.Insert(string(v))
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// PSPAllowsAllVolumes checks for FSTypeAll in the psp's allowed volumes.
|
||||
func PSPAllowsAllVolumes(psp *extensions.PodSecurityPolicy) bool {
|
||||
return PSPAllowsFSType(psp, extensions.All)
|
||||
}
|
||||
|
||||
// PSPAllowsFSType is a utility for checking if a PSP allows a particular FSType.
|
||||
// If all volumes are allowed then this will return true for any FSType passed.
|
||||
func PSPAllowsFSType(psp *extensions.PodSecurityPolicy, fsType extensions.FSType) bool {
|
||||
if psp == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, v := range psp.Spec.Volumes {
|
||||
if v == fsType || v == extensions.All {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// UserFallsInRange is a utility to determine it the id falls in the valid range.
|
||||
func UserFallsInRange(id int64, rng extensions.UserIDRange) bool {
|
||||
return id >= rng.Min && id <= rng.Max
|
||||
}
|
||||
|
||||
// GroupFallsInRange is a utility to determine it the id falls in the valid range.
|
||||
func GroupFallsInRange(id int64, rng extensions.GroupIDRange) bool {
|
||||
return id >= rng.Min && id <= rng.Max
|
||||
}
|
||||
|
||||
// AllowsHostVolumePath is a utility for checking if a PSP allows the host volume path.
|
||||
// This only checks the path. You should still check to make sure the host volume fs type is allowed.
|
||||
func AllowsHostVolumePath(psp *extensions.PodSecurityPolicy, hostPath string) bool {
|
||||
if psp == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// If no allowed paths are specified then allow any path
|
||||
if len(psp.Spec.AllowedHostPaths) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, allowedPath := range psp.Spec.AllowedHostPaths {
|
||||
if hasPathPrefix(hostPath, allowedPath.PathPrefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// hasPathPrefix returns true if the string matches pathPrefix exactly, or if is prefixed with pathPrefix at a path segment boundary
|
||||
// the string and pathPrefix are both normalized to remove trailing slashes prior to checking.
|
||||
func hasPathPrefix(s, pathPrefix string) bool {
|
||||
|
||||
s = strings.TrimSuffix(s, "/")
|
||||
pathPrefix = strings.TrimSuffix(pathPrefix, "/")
|
||||
|
||||
// Short circuit if s doesn't contain the prefix at all
|
||||
if !strings.HasPrefix(s, pathPrefix) {
|
||||
return false
|
||||
}
|
||||
|
||||
pathPrefixLength := len(pathPrefix)
|
||||
|
||||
if len(s) == pathPrefixLength {
|
||||
// Exact match
|
||||
return true
|
||||
}
|
||||
|
||||
if s[pathPrefixLength:pathPrefixLength+1] == "/" {
|
||||
// The next character in s is a path segment boundary
|
||||
// Check this instead of normalizing pathPrefix to avoid allocating on every call
|
||||
// Example where this check applies: s=/foo/bar and pathPrefix=/foo
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
196
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/util/util_test.go
generated
vendored
Normal file
196
vendor/k8s.io/kubernetes/pkg/security/podsecuritypolicy/util/util_test.go
generated
vendored
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
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 util
|
||||
|
||||
import (
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestVolumeSourceFSTypeDrift ensures that for every known type of volume source (by the fields on
|
||||
// a VolumeSource object that GetVolumeFSType is returning a good value. This ensures both that we're
|
||||
// returning an FSType for the VolumeSource field (protect the GetVolumeFSType method) and that we
|
||||
// haven't drifted (ensure new fields in VolumeSource are covered).
|
||||
func TestVolumeSourceFSTypeDrift(t *testing.T) {
|
||||
allFSTypes := GetAllFSTypesAsSet()
|
||||
val := reflect.ValueOf(api.VolumeSource{})
|
||||
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
fieldVal := val.Type().Field(i)
|
||||
|
||||
volumeSource := api.VolumeSource{}
|
||||
volumeSourceVolume := reflect.New(fieldVal.Type.Elem())
|
||||
|
||||
reflect.ValueOf(&volumeSource).Elem().FieldByName(fieldVal.Name).Set(volumeSourceVolume)
|
||||
|
||||
fsType, err := GetVolumeFSType(api.Volume{VolumeSource: volumeSource})
|
||||
if err != nil {
|
||||
t.Errorf("error getting fstype for field %s. This likely means that drift has occured between FSType and VolumeSource. Please update the api and getVolumeFSType", fieldVal.Name)
|
||||
}
|
||||
|
||||
if !allFSTypes.Has(string(fsType)) {
|
||||
t.Errorf("%s was missing from GetFSTypesAsSet", fsType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPSPAllowsFSType(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
psp *extensions.PodSecurityPolicy
|
||||
fsType extensions.FSType
|
||||
allows bool
|
||||
}{
|
||||
"nil psp": {
|
||||
psp: nil,
|
||||
fsType: extensions.HostPath,
|
||||
allows: false,
|
||||
},
|
||||
"empty volumes": {
|
||||
psp: &extensions.PodSecurityPolicy{},
|
||||
fsType: extensions.HostPath,
|
||||
allows: false,
|
||||
},
|
||||
"non-matching": {
|
||||
psp: &extensions.PodSecurityPolicy{
|
||||
Spec: extensions.PodSecurityPolicySpec{
|
||||
Volumes: []extensions.FSType{extensions.AWSElasticBlockStore},
|
||||
},
|
||||
},
|
||||
fsType: extensions.HostPath,
|
||||
allows: false,
|
||||
},
|
||||
"match on FSTypeAll": {
|
||||
psp: &extensions.PodSecurityPolicy{
|
||||
Spec: extensions.PodSecurityPolicySpec{
|
||||
Volumes: []extensions.FSType{extensions.All},
|
||||
},
|
||||
},
|
||||
fsType: extensions.HostPath,
|
||||
allows: true,
|
||||
},
|
||||
"match on direct match": {
|
||||
psp: &extensions.PodSecurityPolicy{
|
||||
Spec: extensions.PodSecurityPolicySpec{
|
||||
Volumes: []extensions.FSType{extensions.HostPath},
|
||||
},
|
||||
},
|
||||
fsType: extensions.HostPath,
|
||||
allows: true,
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range tests {
|
||||
allows := PSPAllowsFSType(v.psp, v.fsType)
|
||||
if v.allows != allows {
|
||||
t.Errorf("%s expected PSPAllowsFSType to return %t but got %t", k, v.allows, allows)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllowsHostVolumePath(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
psp *extensions.PodSecurityPolicy
|
||||
path string
|
||||
allows bool
|
||||
}{
|
||||
"nil psp": {
|
||||
psp: nil,
|
||||
path: "/test",
|
||||
allows: false,
|
||||
},
|
||||
"empty allowed paths": {
|
||||
psp: &extensions.PodSecurityPolicy{},
|
||||
path: "/test",
|
||||
allows: true,
|
||||
},
|
||||
"non-matching": {
|
||||
psp: &extensions.PodSecurityPolicy{
|
||||
Spec: extensions.PodSecurityPolicySpec{
|
||||
AllowedHostPaths: []extensions.AllowedHostPath{
|
||||
{PathPrefix: "/foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
path: "/foobar",
|
||||
allows: false,
|
||||
},
|
||||
"match on direct match": {
|
||||
psp: &extensions.PodSecurityPolicy{
|
||||
Spec: extensions.PodSecurityPolicySpec{
|
||||
AllowedHostPaths: []extensions.AllowedHostPath{
|
||||
{PathPrefix: "/foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
path: "/foo",
|
||||
allows: true,
|
||||
},
|
||||
"match with trailing slash on host path": {
|
||||
psp: &extensions.PodSecurityPolicy{
|
||||
Spec: extensions.PodSecurityPolicySpec{
|
||||
AllowedHostPaths: []extensions.AllowedHostPath{
|
||||
{PathPrefix: "/foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
path: "/foo/",
|
||||
allows: true,
|
||||
},
|
||||
"match with trailing slash on allowed path": {
|
||||
psp: &extensions.PodSecurityPolicy{
|
||||
Spec: extensions.PodSecurityPolicySpec{
|
||||
AllowedHostPaths: []extensions.AllowedHostPath{
|
||||
{PathPrefix: "/foo/"},
|
||||
},
|
||||
},
|
||||
},
|
||||
path: "/foo",
|
||||
allows: true,
|
||||
},
|
||||
"match child directory": {
|
||||
psp: &extensions.PodSecurityPolicy{
|
||||
Spec: extensions.PodSecurityPolicySpec{
|
||||
AllowedHostPaths: []extensions.AllowedHostPath{
|
||||
{PathPrefix: "/foo/"},
|
||||
},
|
||||
},
|
||||
},
|
||||
path: "/foo/bar",
|
||||
allows: true,
|
||||
},
|
||||
"non-matching parent directory": {
|
||||
psp: &extensions.PodSecurityPolicy{
|
||||
Spec: extensions.PodSecurityPolicySpec{
|
||||
AllowedHostPaths: []extensions.AllowedHostPath{
|
||||
{PathPrefix: "/foo/bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
path: "/foo",
|
||||
allows: false,
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range tests {
|
||||
allows := AllowsHostVolumePath(v.psp, v.path)
|
||||
if v.allows != allows {
|
||||
t.Errorf("%s expected %t but got %t", k, v.allows, allows)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user