mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 18:54:06 +00:00
PodSecurity: api: runtime API
Label keys, values, and parsing helper functions Co-authored-by: Jordan Liggitt <liggitt@google.com>
This commit is contained in:
parent
51e1969d9c
commit
9ce17c8773
46
staging/src/k8s.io/pod-security-admission/api/constants.go
Normal file
46
staging/src/k8s.io/pod-security-admission/api/constants.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 api
|
||||||
|
|
||||||
|
type Level string
|
||||||
|
|
||||||
|
const (
|
||||||
|
LevelPrivileged Level = "privileged"
|
||||||
|
LevelBaseline Level = "baseline"
|
||||||
|
LevelRestricted Level = "restricted"
|
||||||
|
)
|
||||||
|
|
||||||
|
var validLevels = []string{
|
||||||
|
string(LevelPrivileged),
|
||||||
|
string(LevelBaseline),
|
||||||
|
string(LevelRestricted),
|
||||||
|
}
|
||||||
|
|
||||||
|
const VersionLatest = "latest"
|
||||||
|
|
||||||
|
const AuditAnnotationPrefix = labelPrefix
|
||||||
|
|
||||||
|
const (
|
||||||
|
labelPrefix = "pod-security.kubernetes.io/"
|
||||||
|
|
||||||
|
EnforceLevelLabel = labelPrefix + "enforce"
|
||||||
|
EnforceVersionLabel = labelPrefix + "enforce-version"
|
||||||
|
AuditLevelLabel = labelPrefix + "audit"
|
||||||
|
AuditVersionLabel = labelPrefix + "audit-version"
|
||||||
|
WarnLevelLabel = labelPrefix + "warn"
|
||||||
|
WarnVersionLabel = labelPrefix + "warn-version"
|
||||||
|
)
|
18
staging/src/k8s.io/pod-security-admission/api/doc.go
Normal file
18
staging/src/k8s.io/pod-security-admission/api/doc.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 api contains constants and helpers for PodSecurity admission label keys and values
|
||||||
|
package api // import "k8s.io/pod-security-admission/api"
|
202
staging/src/k8s.io/pod-security-admission/api/helpers.go
Normal file
202
staging/src/k8s.io/pod-security-admission/api/helpers.go
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/util/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Version struct {
|
||||||
|
major int
|
||||||
|
minor int
|
||||||
|
latest bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Version) String() string {
|
||||||
|
if v.latest {
|
||||||
|
return "latest"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("v%d.%d", v.major, v.minor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Older returns true if this version v is older than the other.
|
||||||
|
func (v *Version) Older(other Version) bool {
|
||||||
|
if v.latest { // Latest is always consider newer, even than future versions.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if other.latest {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if v.major != other.major {
|
||||||
|
return v.major < other.major
|
||||||
|
}
|
||||||
|
return v.minor < other.minor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Version) Major() int {
|
||||||
|
return v.major
|
||||||
|
}
|
||||||
|
func (v *Version) Minor() int {
|
||||||
|
return v.minor
|
||||||
|
}
|
||||||
|
func (v *Version) Latest() bool {
|
||||||
|
return v.latest
|
||||||
|
}
|
||||||
|
|
||||||
|
func MajorMinorVersion(major, minor int) Version {
|
||||||
|
return Version{major: major, minor: minor}
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatestVersion() Version {
|
||||||
|
return Version{latest: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseLevel returns the level that should be evaluated.
|
||||||
|
// level must be "privileged", "baseline", or "restricted".
|
||||||
|
// if level does not match one of those strings, "restricted" and an error is returned.
|
||||||
|
func ParseLevel(level string) (Level, error) {
|
||||||
|
switch Level(level) {
|
||||||
|
case LevelPrivileged, LevelBaseline, LevelRestricted:
|
||||||
|
return Level(level), nil
|
||||||
|
default:
|
||||||
|
return LevelRestricted, fmt.Errorf(`must be one of %s`, strings.Join(validLevels, ", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid checks whether the level l is a valid level.
|
||||||
|
func (l *Level) Valid() bool {
|
||||||
|
switch *l {
|
||||||
|
case LevelPrivileged, LevelBaseline, LevelRestricted:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var versionRegexp = regexp.MustCompile(`^v1\.([0-9]|[1-9][0-9]*)$`)
|
||||||
|
|
||||||
|
// ParseVersion returns the policy version that should be evaluated.
|
||||||
|
// version must be "latest" or "v1.x".
|
||||||
|
// If version does not match one of those patterns, the latest version and an error is returned.
|
||||||
|
func ParseVersion(version string) (Version, error) {
|
||||||
|
if version == "latest" {
|
||||||
|
return Version{latest: true}, nil
|
||||||
|
}
|
||||||
|
match := versionRegexp.FindStringSubmatch(version)
|
||||||
|
if len(match) != 2 {
|
||||||
|
return Version{latest: true}, fmt.Errorf(`must be "latest" or "v1.x"`)
|
||||||
|
}
|
||||||
|
versionNumber, err := strconv.Atoi(match[1])
|
||||||
|
if err != nil || versionNumber < 0 {
|
||||||
|
return Version{latest: true}, fmt.Errorf(`must be "latest" or "v1.x"`)
|
||||||
|
}
|
||||||
|
return Version{major: 1, minor: versionNumber}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type LevelVersion struct {
|
||||||
|
Level
|
||||||
|
Version
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lv LevelVersion) String() string {
|
||||||
|
return fmt.Sprintf("%s:%s", lv.Level, lv.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Policy struct {
|
||||||
|
Enforce LevelVersion
|
||||||
|
Audit LevelVersion
|
||||||
|
Warn LevelVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyToEvaluate resolves the PodSecurity namespace labels to the policy for that namespace,
|
||||||
|
// falling back to the provided defaults when a label is unspecified. A valid policy is always
|
||||||
|
// returned, even when an error is returned. If labels cannot be parsed correctly, the values of
|
||||||
|
// "restricted" and "latest" are used for level and version respectively.
|
||||||
|
func PolicyToEvaluate(labels map[string]string, defaults Policy) (Policy, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
errs []error
|
||||||
|
|
||||||
|
p = defaults
|
||||||
|
)
|
||||||
|
if level, ok := labels[EnforceLevelLabel]; ok {
|
||||||
|
p.Enforce.Level, err = ParseLevel(level)
|
||||||
|
errs = appendErr(errs, err, "Enforce.Level")
|
||||||
|
}
|
||||||
|
if version, ok := labels[EnforceVersionLabel]; ok {
|
||||||
|
p.Enforce.Version, err = ParseVersion(version)
|
||||||
|
errs = appendErr(errs, err, "Enforce.Version")
|
||||||
|
}
|
||||||
|
if level, ok := labels[AuditLevelLabel]; ok {
|
||||||
|
p.Audit.Level, err = ParseLevel(level)
|
||||||
|
errs = appendErr(errs, err, "Audit.Level")
|
||||||
|
if err != nil {
|
||||||
|
p.Audit.Level = LevelPrivileged // Fail open for audit.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if version, ok := labels[AuditVersionLabel]; ok {
|
||||||
|
p.Audit.Version, err = ParseVersion(version)
|
||||||
|
errs = appendErr(errs, err, "Audit.Version")
|
||||||
|
}
|
||||||
|
if level, ok := labels[WarnLevelLabel]; ok {
|
||||||
|
p.Warn.Level, err = ParseLevel(level)
|
||||||
|
errs = appendErr(errs, err, "Warn.Level")
|
||||||
|
if err != nil {
|
||||||
|
p.Warn.Level = LevelPrivileged // Fail open for warn.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if version, ok := labels[WarnVersionLabel]; ok {
|
||||||
|
p.Warn.Version, err = ParseVersion(version)
|
||||||
|
errs = appendErr(errs, err, "Warn.Version")
|
||||||
|
}
|
||||||
|
return p, errors.NewAggregate(errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareLevels returns an integer comparing two levels by strictness. The result will be 0 if
|
||||||
|
// a==b, -1 if a is less strict than b, and +1 if a is more strict than b.
|
||||||
|
func CompareLevels(a, b Level) int {
|
||||||
|
if a == b {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
switch a {
|
||||||
|
case LevelPrivileged:
|
||||||
|
return -1
|
||||||
|
case LevelRestricted:
|
||||||
|
return 1
|
||||||
|
default:
|
||||||
|
if b == LevelPrivileged {
|
||||||
|
return 1
|
||||||
|
} else if b == LevelRestricted {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This should only happen if both a & b are invalid levels.
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendErr is a helper function to collect field-specific errors.
|
||||||
|
func appendErr(errs []error, err error, field string) []error {
|
||||||
|
if err != nil {
|
||||||
|
return append(errs, fmt.Errorf("%s: %s", field, err.Error()))
|
||||||
|
}
|
||||||
|
return errs
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseVersion(t *testing.T) {
|
||||||
|
successes := map[string]Version{
|
||||||
|
"latest": LatestVersion(),
|
||||||
|
"v1.0": MajorMinorVersion(1, 0),
|
||||||
|
"v1.1": MajorMinorVersion(1, 1),
|
||||||
|
"v1.20": MajorMinorVersion(1, 20),
|
||||||
|
"v1.10000": MajorMinorVersion(1, 10000),
|
||||||
|
}
|
||||||
|
for v, expected := range successes {
|
||||||
|
t.Run(v, func(t *testing.T) {
|
||||||
|
actual, err := ParseVersion(v)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
failures := []string{
|
||||||
|
"foo",
|
||||||
|
"",
|
||||||
|
"v2.0",
|
||||||
|
"v1",
|
||||||
|
"1.1",
|
||||||
|
}
|
||||||
|
for _, v := range failures {
|
||||||
|
t.Run(v, func(t *testing.T) {
|
||||||
|
_, err := ParseVersion(v)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user