mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 12:15:52 +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