Merge pull request #103099 from liggitt/podsecurity
PodSecurity admission
This commit is contained in:
commit
d92f6c424d
1
go.mod
1
go.mod
@ -129,6 +129,7 @@ require (
|
||||
k8s.io/legacy-cloud-providers v0.0.0
|
||||
k8s.io/metrics v0.0.0
|
||||
k8s.io/mount-utils v0.0.0
|
||||
k8s.io/pod-security-admission v0.0.0
|
||||
k8s.io/sample-apiserver v0.0.0
|
||||
k8s.io/system-validators v1.4.0
|
||||
k8s.io/utils v0.0.0-20210521133846-da695404a2bc
|
||||
|
@ -721,6 +721,12 @@ const (
|
||||
//
|
||||
// Enables the use of `RuntimeDefault` as the default seccomp profile for all workloads.
|
||||
SeccompDefault featuregate.Feature = "SeccompDefault"
|
||||
|
||||
// owner: @liggitt, @tallclair, sig-auth
|
||||
// alpha: v1.22
|
||||
//
|
||||
// Enables the PodSecurity admission plugin
|
||||
PodSecurity featuregate.Feature = "PodSecurity"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -829,6 +835,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
||||
StatefulSetMinReadySeconds: {Default: false, PreRelease: featuregate.Alpha},
|
||||
ExpandedDNSConfig: {Default: false, PreRelease: featuregate.Alpha},
|
||||
SeccompDefault: {Default: false, PreRelease: featuregate.Alpha},
|
||||
PodSecurity: {Default: false, PreRelease: featuregate.Alpha},
|
||||
|
||||
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
|
||||
// unintentionally on either side:
|
||||
|
@ -44,6 +44,7 @@ import (
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction"
|
||||
podpriority "k8s.io/kubernetes/plugin/pkg/admission/priority"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/runtimeclass"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/security/podsecurity"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/security/podsecuritypolicy"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/securitycontext/scdeny"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
|
||||
@ -75,6 +76,7 @@ var AllOrderedPlugins = []string{
|
||||
alwayspullimages.PluginName, // AlwaysPullImages
|
||||
imagepolicy.PluginName, // ImagePolicyWebhook
|
||||
podsecuritypolicy.PluginName, // PodSecurityPolicy
|
||||
podsecurity.PluginName, // PodSecurity
|
||||
podnodeselector.PluginName, // PodNodeSelector
|
||||
podpriority.PluginName, // Priority
|
||||
defaulttolerationseconds.PluginName, // DefaultTolerationSeconds
|
||||
@ -126,6 +128,7 @@ func RegisterAllAdmissionPlugins(plugins *admission.Plugins) {
|
||||
podtolerationrestriction.Register(plugins)
|
||||
runtimeclass.Register(plugins)
|
||||
resourcequota.Register(plugins)
|
||||
podsecurity.Register(plugins) // before PodSecurityPolicy so audit/warn get exercised even if PodSecurityPolicy denies
|
||||
podsecuritypolicy.Register(plugins)
|
||||
podpriority.Register(plugins)
|
||||
scdeny.Register(plugins)
|
||||
@ -158,6 +161,7 @@ func DefaultOffAdmissionPlugins() sets.String {
|
||||
certsigning.PluginName, // CertificateSigning
|
||||
certsubjectrestriction.PluginName, // CertificateSubjectRestriction
|
||||
defaultingressclass.PluginName, // DefaultIngressClass
|
||||
podsecurity.PluginName, // PodSecurity
|
||||
)
|
||||
|
||||
return sets.NewString(AllOrderedPlugins...).Difference(defaultOnPlugins)
|
||||
|
252
plugin/pkg/admission/security/podsecurity/admission.go
Normal file
252
plugin/pkg/admission/security/podsecurity/admission.go
Normal file
@ -0,0 +1,252 @@
|
||||
/*
|
||||
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 podsecurity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
// install conversions for types we need to convert
|
||||
_ "k8s.io/kubernetes/pkg/apis/apps/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/batch/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
"k8s.io/apiserver/pkg/warning"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/component-base/featuregate"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
podsecurityadmission "k8s.io/pod-security-admission/admission"
|
||||
podsecurityconfigloader "k8s.io/pod-security-admission/admission/api/load"
|
||||
podsecurityadmissionapi "k8s.io/pod-security-admission/api"
|
||||
"k8s.io/pod-security-admission/policy"
|
||||
)
|
||||
|
||||
// PluginName is a string with the name of the plugin
|
||||
const PluginName = "PodSecurity"
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register(PluginName, func(reader io.Reader) (admission.Interface, error) {
|
||||
return newPlugin(reader)
|
||||
})
|
||||
}
|
||||
|
||||
// Plugin holds state for and implements the admission plugin.
|
||||
type Plugin struct {
|
||||
*admission.Handler
|
||||
|
||||
enabled bool
|
||||
inspectedFeatureGates bool
|
||||
|
||||
client kubernetes.Interface
|
||||
namespaceLister corev1listers.NamespaceLister
|
||||
podLister corev1listers.PodLister
|
||||
|
||||
delegate *podsecurityadmission.Admission
|
||||
}
|
||||
|
||||
var _ admission.ValidationInterface = &Plugin{}
|
||||
var _ genericadmissioninit.WantsExternalKubeInformerFactory = &Plugin{}
|
||||
var _ genericadmissioninit.WantsExternalKubeClientSet = &Plugin{}
|
||||
|
||||
// newPlugin creates a new admission plugin.
|
||||
func newPlugin(reader io.Reader) (*Plugin, error) {
|
||||
config, err := podsecurityconfigloader.LoadFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
evaluator, err := policy.NewEvaluator(policy.DefaultChecks())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create PodSecurityRegistry: %w", err)
|
||||
}
|
||||
|
||||
return &Plugin{
|
||||
Handler: admission.NewHandler(admission.Create, admission.Update),
|
||||
delegate: &podsecurityadmission.Admission{
|
||||
Configuration: config,
|
||||
Evaluator: evaluator,
|
||||
Metrics: nil, // TODO: wire to default prometheus metrics
|
||||
PodSpecExtractor: podsecurityadmission.DefaultPodSpecExtractor{},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetExternalKubeInformerFactory registers an informer
|
||||
func (p *Plugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
|
||||
namespaceInformer := f.Core().V1().Namespaces()
|
||||
p.namespaceLister = namespaceInformer.Lister()
|
||||
p.podLister = f.Core().V1().Pods().Lister()
|
||||
p.SetReadyFunc(namespaceInformer.Informer().HasSynced)
|
||||
p.updateDelegate()
|
||||
}
|
||||
|
||||
// SetExternalKubeClientSet sets the plugin's client
|
||||
func (p *Plugin) SetExternalKubeClientSet(client kubernetes.Interface) {
|
||||
p.client = client
|
||||
p.updateDelegate()
|
||||
}
|
||||
|
||||
func (p *Plugin) updateDelegate() {
|
||||
// return early if we don't have what we need to set up the admission delegate
|
||||
if p.namespaceLister == nil {
|
||||
return
|
||||
}
|
||||
if p.podLister == nil {
|
||||
return
|
||||
}
|
||||
if p.client == nil {
|
||||
return
|
||||
}
|
||||
p.delegate.PodLister = podsecurityadmission.PodListerFromInformer(p.podLister)
|
||||
p.delegate.NamespaceGetter = podsecurityadmission.NamespaceGetterFromListerAndClient(p.namespaceLister, p.client)
|
||||
}
|
||||
|
||||
func (c *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) {
|
||||
c.enabled = featureGates.Enabled(features.PodSecurity)
|
||||
c.inspectedFeatureGates = true
|
||||
}
|
||||
|
||||
// ValidateInitialization ensures all required options are set
|
||||
func (p *Plugin) ValidateInitialization() error {
|
||||
if !p.inspectedFeatureGates {
|
||||
return fmt.Errorf("%s did not see feature gates", PluginName)
|
||||
}
|
||||
if err := p.delegate.CompleteConfiguration(); err != nil {
|
||||
return fmt.Errorf("%s configuration error: %w", PluginName, err)
|
||||
}
|
||||
if err := p.delegate.ValidateConfiguration(); err != nil {
|
||||
return fmt.Errorf("%s invalid: %w", PluginName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
applicableResources = map[schema.GroupResource]bool{
|
||||
corev1.Resource("pods"): true,
|
||||
corev1.Resource("namespaces"): true,
|
||||
}
|
||||
)
|
||||
|
||||
func (p *Plugin) Validate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
|
||||
if !p.enabled {
|
||||
return nil
|
||||
}
|
||||
gr := a.GetResource().GroupResource()
|
||||
if !applicableResources[gr] && !p.delegate.PodSpecExtractor.HasPodSpec(gr) {
|
||||
return nil
|
||||
}
|
||||
|
||||
a = &lazyConvertingAttributes{Attributes: a}
|
||||
|
||||
result := p.delegate.Validate(ctx, a)
|
||||
for _, w := range result.Warnings {
|
||||
warning.AddWarning(ctx, "", w)
|
||||
}
|
||||
for k, v := range result.AuditAnnotations {
|
||||
audit.AddAuditAnnotation(ctx, podsecurityadmissionapi.AuditAnnotationPrefix+k, v)
|
||||
}
|
||||
if !result.Allowed {
|
||||
if result.Result != nil && len(result.Result.Message) > 0 {
|
||||
// TODO: use code/reason/etc from status
|
||||
return admission.NewForbidden(a, errors.New(result.Result.Message))
|
||||
}
|
||||
return admission.NewForbidden(a, errors.New("Not allowed by PodSecurity"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type lazyConvertingAttributes struct {
|
||||
admission.Attributes
|
||||
|
||||
convertObjectOnce sync.Once
|
||||
convertedObject runtime.Object
|
||||
|
||||
convertOldObjectOnce sync.Once
|
||||
convertedOldObject runtime.Object
|
||||
}
|
||||
|
||||
func (l *lazyConvertingAttributes) GetObject() runtime.Object {
|
||||
l.convertObjectOnce.Do(func() {
|
||||
obj, err := convert(l.Attributes.GetObject())
|
||||
if err != nil {
|
||||
utilruntime.HandleError(err)
|
||||
}
|
||||
l.convertedObject = obj
|
||||
})
|
||||
return l.convertedObject
|
||||
}
|
||||
func (l *lazyConvertingAttributes) GetOldObject() runtime.Object {
|
||||
l.convertOldObjectOnce.Do(func() {
|
||||
obj, err := convert(l.Attributes.GetOldObject())
|
||||
if err != nil {
|
||||
utilruntime.HandleError(err)
|
||||
}
|
||||
l.convertedOldObject = obj
|
||||
})
|
||||
return l.convertedOldObject
|
||||
}
|
||||
|
||||
func convert(in runtime.Object) (runtime.Object, error) {
|
||||
var out runtime.Object
|
||||
switch in.(type) {
|
||||
case *core.Namespace:
|
||||
out = &corev1.Namespace{}
|
||||
case *core.Pod:
|
||||
out = &corev1.Pod{}
|
||||
case *core.ReplicationController:
|
||||
out = &corev1.ReplicationController{}
|
||||
case *core.PodTemplate:
|
||||
out = &corev1.PodTemplate{}
|
||||
case *apps.ReplicaSet:
|
||||
out = &appsv1.ReplicaSet{}
|
||||
case *apps.Deployment:
|
||||
out = &appsv1.Deployment{}
|
||||
case *apps.StatefulSet:
|
||||
out = &appsv1.StatefulSet{}
|
||||
case *apps.DaemonSet:
|
||||
out = &appsv1.DaemonSet{}
|
||||
case *batch.Job:
|
||||
out = &batchv1.Job{}
|
||||
case *batch.CronJob:
|
||||
out = &batchv1.CronJob{}
|
||||
default:
|
||||
return in, fmt.Errorf("unexpected type %T", in)
|
||||
}
|
||||
if err := legacyscheme.Scheme.Convert(in, out, nil); err != nil {
|
||||
return in, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
60
plugin/pkg/admission/security/podsecurity/admission_test.go
Normal file
60
plugin/pkg/admission/security/podsecurity/admission_test.go
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
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 podsecurity
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
podsecurityadmission "k8s.io/pod-security-admission/admission"
|
||||
)
|
||||
|
||||
func TestConvert(t *testing.T) {
|
||||
extractor := podsecurityadmission.DefaultPodSpecExtractor{}
|
||||
internalTypes := map[schema.GroupResource]runtime.Object{
|
||||
core.Resource("pods"): &core.Pod{},
|
||||
core.Resource("replicationcontrollers"): &core.ReplicationController{},
|
||||
core.Resource("podtemplates"): &core.PodTemplate{},
|
||||
apps.Resource("replicasets"): &apps.ReplicaSet{},
|
||||
apps.Resource("deployments"): &apps.Deployment{},
|
||||
apps.Resource("statefulsets"): &apps.StatefulSet{},
|
||||
apps.Resource("daemonsets"): &apps.DaemonSet{},
|
||||
batch.Resource("jobs"): &batch.Job{},
|
||||
batch.Resource("cronjobs"): &batch.CronJob{},
|
||||
}
|
||||
for _, r := range extractor.PodSpecResources() {
|
||||
internalType, ok := internalTypes[r]
|
||||
if !ok {
|
||||
t.Errorf("no internal type registered for %s", r.String())
|
||||
continue
|
||||
}
|
||||
externalType, err := convert(internalType)
|
||||
if err != nil {
|
||||
t.Errorf("error converting %T: %v", internalType, err)
|
||||
continue
|
||||
}
|
||||
_, _, err = extractor.ExtractPodSpec(externalType)
|
||||
if err != nil {
|
||||
t.Errorf("error extracting from %T: %v", externalType, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
@ -267,6 +267,7 @@
|
||||
allowedImports:
|
||||
- k8s.io/api
|
||||
- k8s.io/apimachinery
|
||||
- k8s.io/apiserver/pkg/admission
|
||||
- k8s.io/client-go
|
||||
- k8s.io/klog
|
||||
- k8s.io/pod-security-admission
|
||||
|
@ -1469,3 +1469,14 @@ rules:
|
||||
branch: master
|
||||
dir: staging/src/k8s.io/pod-security-admission
|
||||
name: master
|
||||
dependencies:
|
||||
- repository: api
|
||||
branch: master
|
||||
- repository: apimachinery
|
||||
branch: master
|
||||
- repository: apiserver
|
||||
branch: master
|
||||
- repository: client-go
|
||||
branch: master
|
||||
- repository: component-base
|
||||
branch: master
|
||||
|
549
staging/src/k8s.io/pod-security-admission/admission/admission.go
Normal file
549
staging/src/k8s.io/pod-security-admission/admission/admission.go
Normal file
@ -0,0 +1,549 @@
|
||||
/*
|
||||
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 admission
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
admissionv1 "k8s.io/api/admission/v1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
admissionapi "k8s.io/pod-security-admission/admission/api"
|
||||
"k8s.io/pod-security-admission/admission/api/validation"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
"k8s.io/pod-security-admission/metrics"
|
||||
"k8s.io/pod-security-admission/policy"
|
||||
)
|
||||
|
||||
const (
|
||||
namespaceMaxPodsToCheck = 3000
|
||||
namespacePodCheckTimeout = 1 * time.Second
|
||||
)
|
||||
|
||||
// Admission implements the core admission logic for the Pod Security Admission controller.
|
||||
// The admission logic can be
|
||||
type Admission struct {
|
||||
Configuration *admissionapi.PodSecurityConfiguration
|
||||
|
||||
// Getting policy checks per level/version
|
||||
Evaluator policy.Evaluator
|
||||
|
||||
// Metrics
|
||||
Metrics metrics.EvaluationRecorder
|
||||
|
||||
// Arbitrary object --> PodSpec
|
||||
PodSpecExtractor PodSpecExtractor
|
||||
|
||||
// API connections
|
||||
NamespaceGetter NamespaceGetter
|
||||
PodLister PodLister
|
||||
|
||||
defaultPolicy api.Policy
|
||||
}
|
||||
|
||||
type NamespaceGetter interface {
|
||||
GetNamespace(ctx context.Context, name string) (*corev1.Namespace, error)
|
||||
}
|
||||
|
||||
type PodLister interface {
|
||||
ListPods(ctx context.Context, namespace string) ([]*corev1.Pod, error)
|
||||
}
|
||||
|
||||
// PodSpecExtractor extracts a PodSpec from pod-controller resources that embed a PodSpec.
|
||||
// This interface can be extended to enforce policy on CRDs for custom pod-controllers.
|
||||
type PodSpecExtractor interface {
|
||||
// HasPodSpec returns true if the given resource type MAY contain an extractable PodSpec.
|
||||
HasPodSpec(schema.GroupResource) bool
|
||||
// ExtractPodSpec returns a pod spec and metadata to evaluate from the object.
|
||||
// An error returned here does not block admission of the pod-spec-containing object and is not returned to the user.
|
||||
// If the object has no pod spec, return `nil, nil, nil`.
|
||||
ExtractPodSpec(runtime.Object) (*metav1.ObjectMeta, *corev1.PodSpec, error)
|
||||
}
|
||||
|
||||
var defaultPodSpecResources = map[schema.GroupResource]bool{
|
||||
corev1.Resource("pods"): true,
|
||||
corev1.Resource("replicationcontrollers"): true,
|
||||
corev1.Resource("podtemplates"): true,
|
||||
appsv1.Resource("replicasets"): true,
|
||||
appsv1.Resource("deployments"): true,
|
||||
appsv1.Resource("statefulsets"): true,
|
||||
appsv1.Resource("daemonsets"): true,
|
||||
batchv1.Resource("jobs"): true,
|
||||
batchv1.Resource("cronjobs"): true,
|
||||
}
|
||||
|
||||
type DefaultPodSpecExtractor struct{}
|
||||
|
||||
func (DefaultPodSpecExtractor) HasPodSpec(gr schema.GroupResource) bool {
|
||||
return defaultPodSpecResources[gr]
|
||||
}
|
||||
|
||||
func (DefaultPodSpecExtractor) ExtractPodSpec(obj runtime.Object) (*metav1.ObjectMeta, *corev1.PodSpec, error) {
|
||||
switch o := obj.(type) {
|
||||
case *corev1.Pod:
|
||||
return &o.ObjectMeta, &o.Spec, nil
|
||||
case *corev1.PodTemplate:
|
||||
return extractPodSpecFromTemplate(&o.Template)
|
||||
case *corev1.ReplicationController:
|
||||
return extractPodSpecFromTemplate(o.Spec.Template)
|
||||
case *appsv1.ReplicaSet:
|
||||
return extractPodSpecFromTemplate(&o.Spec.Template)
|
||||
case *appsv1.Deployment:
|
||||
return extractPodSpecFromTemplate(&o.Spec.Template)
|
||||
case *appsv1.DaemonSet:
|
||||
return extractPodSpecFromTemplate(&o.Spec.Template)
|
||||
case *appsv1.StatefulSet:
|
||||
return extractPodSpecFromTemplate(&o.Spec.Template)
|
||||
case *batchv1.Job:
|
||||
return extractPodSpecFromTemplate(&o.Spec.Template)
|
||||
case *batchv1.CronJob:
|
||||
return extractPodSpecFromTemplate(&o.Spec.JobTemplate.Spec.Template)
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("unexpected object type: %s", obj.GetObjectKind().GroupVersionKind().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (DefaultPodSpecExtractor) PodSpecResources() []schema.GroupResource {
|
||||
retval := make([]schema.GroupResource, 0, len(defaultPodSpecResources))
|
||||
for r := range defaultPodSpecResources {
|
||||
retval = append(retval, r)
|
||||
}
|
||||
return retval
|
||||
}
|
||||
|
||||
func extractPodSpecFromTemplate(template *corev1.PodTemplateSpec) (*metav1.ObjectMeta, *corev1.PodSpec, error) {
|
||||
if template == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
return &template.ObjectMeta, &template.Spec, nil
|
||||
}
|
||||
|
||||
// CompleteConfiguration() sets up default or derived configuration.
|
||||
func (a *Admission) CompleteConfiguration() error {
|
||||
if a.Configuration != nil {
|
||||
if p, err := admissionapi.ToPolicy(a.Configuration.Defaults); err != nil {
|
||||
return err
|
||||
} else {
|
||||
a.defaultPolicy = p
|
||||
}
|
||||
}
|
||||
|
||||
if a.PodSpecExtractor == nil {
|
||||
a.PodSpecExtractor = &DefaultPodSpecExtractor{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateConfiguration() ensures all required fields are set with valid values.
|
||||
func (a *Admission) ValidateConfiguration() error {
|
||||
if a.Configuration == nil {
|
||||
return fmt.Errorf("configuration required")
|
||||
} else if errs := validation.ValidatePodSecurityConfiguration(a.Configuration); len(errs) > 0 {
|
||||
return errs.ToAggregate()
|
||||
} else {
|
||||
if p, err := admissionapi.ToPolicy(a.Configuration.Defaults); err != nil {
|
||||
return err
|
||||
} else if !reflect.DeepEqual(p, a.defaultPolicy) {
|
||||
return fmt.Errorf("default policy does not match; CompleteConfiguration() was not called before ValidateConfiguration()")
|
||||
}
|
||||
}
|
||||
// TODO: check metrics is non-nil?
|
||||
if a.PodSpecExtractor == nil {
|
||||
return fmt.Errorf("PodSpecExtractor required")
|
||||
}
|
||||
if a.Evaluator == nil {
|
||||
return fmt.Errorf("Evaluator required")
|
||||
}
|
||||
if a.NamespaceGetter == nil {
|
||||
return fmt.Errorf("NamespaceGetter required")
|
||||
}
|
||||
if a.PodLister == nil {
|
||||
return fmt.Errorf("PodLister required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate admits an API request.
|
||||
// The objects in admission attributes are expected to be external v1 objects that we care about.
|
||||
func (a *Admission) Validate(ctx context.Context, attrs admission.Attributes) admissionv1.AdmissionResponse {
|
||||
var response admissionv1.AdmissionResponse
|
||||
switch attrs.GetResource().GroupResource() {
|
||||
case corev1.Resource("namespaces"):
|
||||
response = a.ValidateNamespace(ctx, attrs)
|
||||
case corev1.Resource("pods"):
|
||||
response = a.ValidatePod(ctx, attrs)
|
||||
default:
|
||||
response = a.ValidatePodController(ctx, attrs)
|
||||
}
|
||||
|
||||
// TODO: record metrics.
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
func (a *Admission) ValidateNamespace(ctx context.Context, attrs admission.Attributes) admissionv1.AdmissionResponse {
|
||||
// short-circuit on subresources
|
||||
if attrs.GetSubresource() != "" {
|
||||
return allowedResponse()
|
||||
}
|
||||
namespace, ok := attrs.GetObject().(*corev1.Namespace)
|
||||
if !ok {
|
||||
klog.InfoS("failed to assert namespace type", "type", reflect.TypeOf(attrs.GetObject()))
|
||||
return internalErrorResponse("failed to decode namespace")
|
||||
}
|
||||
|
||||
newPolicy, newErr := a.PolicyToEvaluate(namespace.Labels)
|
||||
|
||||
switch attrs.GetOperation() {
|
||||
case admission.Create:
|
||||
// require valid labels on create
|
||||
if newErr != nil {
|
||||
return invalidResponse(newErr.Error())
|
||||
}
|
||||
return allowedResponse()
|
||||
|
||||
case admission.Update:
|
||||
// if update, check if policy labels changed
|
||||
oldNamespace, ok := attrs.GetOldObject().(*corev1.Namespace)
|
||||
if !ok {
|
||||
klog.InfoS("failed to assert old namespace type", "type", reflect.TypeOf(attrs.GetOldObject()))
|
||||
return internalErrorResponse("failed to decode old namespace")
|
||||
}
|
||||
oldPolicy, oldErr := a.PolicyToEvaluate(oldNamespace.Labels)
|
||||
|
||||
// require valid labels on update if they have changed
|
||||
if newErr != nil && (oldErr == nil || newErr.Error() != oldErr.Error()) {
|
||||
return invalidResponse(newErr.Error())
|
||||
}
|
||||
|
||||
// Skip dry-running pods:
|
||||
// * if the enforce policy is unchanged
|
||||
// * if the new enforce policy is privileged
|
||||
// * if the new enforce is the same version and level was relaxed
|
||||
// * for exempt namespaces
|
||||
if newPolicy.Enforce == oldPolicy.Enforce {
|
||||
return allowedResponse()
|
||||
}
|
||||
if newPolicy.Enforce.Level == api.LevelPrivileged {
|
||||
return allowedResponse()
|
||||
}
|
||||
if newPolicy.Enforce.Version == oldPolicy.Enforce.Version &&
|
||||
api.CompareLevels(newPolicy.Enforce.Level, oldPolicy.Enforce.Level) < 1 {
|
||||
return allowedResponse()
|
||||
}
|
||||
if a.exemptNamespace(attrs.GetNamespace()) {
|
||||
return allowedResponse()
|
||||
}
|
||||
response := allowedResponse()
|
||||
response.Warnings = a.EvaluatePodsInNamespace(ctx, namespace.Name, newPolicy.Enforce)
|
||||
return response
|
||||
|
||||
default:
|
||||
return allowedResponse()
|
||||
}
|
||||
}
|
||||
|
||||
// ignoredPodSubresources is a set of ignored Pod subresources.
|
||||
// Any other subresource is expected to be a *v1.Pod type and is evaluated.
|
||||
// This ensures a version skewed webhook fails safe and denies an unknown pod subresource that allows modifying the pod spec.
|
||||
var ignoredPodSubresources = map[string]bool{
|
||||
"exec": true,
|
||||
"attach": true,
|
||||
"binding": true,
|
||||
"eviction": true,
|
||||
"log": true,
|
||||
"portforward": true,
|
||||
"proxy": true,
|
||||
"status": true,
|
||||
}
|
||||
|
||||
func (a *Admission) ValidatePod(ctx context.Context, attrs admission.Attributes) admissionv1.AdmissionResponse {
|
||||
// short-circuit on ignored subresources
|
||||
if ignoredPodSubresources[attrs.GetSubresource()] {
|
||||
return allowedResponse()
|
||||
}
|
||||
// short-circuit on exempt namespaces and users
|
||||
if a.exemptNamespace(attrs.GetNamespace()) || a.exemptUser(attrs.GetUserInfo().GetName()) {
|
||||
return allowedResponse()
|
||||
}
|
||||
|
||||
pod, ok := attrs.GetObject().(*corev1.Pod)
|
||||
if !ok {
|
||||
klog.InfoS("failed to assert pod type", "type", reflect.TypeOf(attrs.GetObject()))
|
||||
return internalErrorResponse("failed to decode pod")
|
||||
}
|
||||
enforce := true
|
||||
if attrs.GetOperation() == admission.Update {
|
||||
oldPod, ok := attrs.GetOldObject().(*corev1.Pod)
|
||||
if !ok {
|
||||
klog.InfoS("failed to assert old pod type", "type", reflect.TypeOf(attrs.GetOldObject()))
|
||||
return internalErrorResponse("failed to decode old pod")
|
||||
}
|
||||
if !isSignificantPodUpdate(pod, oldPod) {
|
||||
// Nothing we care about changed, so always allow the update.
|
||||
return allowedResponse()
|
||||
}
|
||||
}
|
||||
return a.EvaluatePod(ctx, attrs.GetNamespace(), &pod.ObjectMeta, &pod.Spec, enforce)
|
||||
}
|
||||
|
||||
func (a *Admission) ValidatePodController(ctx context.Context, attrs admission.Attributes) admissionv1.AdmissionResponse {
|
||||
// short-circuit on subresources
|
||||
if attrs.GetSubresource() != "" {
|
||||
return allowedResponse()
|
||||
}
|
||||
// short-circuit on exempt namespaces and users
|
||||
if a.exemptNamespace(attrs.GetNamespace()) || a.exemptUser(attrs.GetUserInfo().GetName()) {
|
||||
return allowedResponse()
|
||||
}
|
||||
|
||||
podMetadata, podSpec, err := a.PodSpecExtractor.ExtractPodSpec(attrs.GetObject())
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "failed to extract pod spec")
|
||||
return internalErrorResponse("failed to extract pod template")
|
||||
}
|
||||
if podMetadata == nil && podSpec == nil {
|
||||
// if a controller with an optional pod spec does not contain a pod spec, skip validation
|
||||
return allowedResponse()
|
||||
}
|
||||
return a.EvaluatePod(ctx, attrs.GetNamespace(), podMetadata, podSpec, false)
|
||||
}
|
||||
|
||||
// EvaluatePod looks up the policy for the pods namespace, and checks it against the given pod(-like) object.
|
||||
// The enforce policy is only checked if enforce=true.
|
||||
func (a *Admission) EvaluatePod(ctx context.Context, namespaceName string, podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec, enforce bool) admissionv1.AdmissionResponse {
|
||||
// short-circuit on exempt runtimeclass
|
||||
if a.exemptRuntimeClass(podSpec.RuntimeClassName) {
|
||||
return allowedResponse()
|
||||
}
|
||||
|
||||
namespace, err := a.NamespaceGetter.GetNamespace(ctx, namespaceName)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "failed to fetch pod namespace", "namespace", namespaceName)
|
||||
return internalErrorResponse(fmt.Sprintf("failed to lookup namespace %s", namespaceName))
|
||||
}
|
||||
|
||||
auditAnnotations := map[string]string{}
|
||||
nsPolicy, err := a.PolicyToEvaluate(namespace.Labels)
|
||||
if err != nil {
|
||||
klog.V(2).InfoS("failed to parse PodSecurity namespace labels", "err", err)
|
||||
auditAnnotations["error"] = fmt.Sprintf("Failed to parse policy: %v", err)
|
||||
}
|
||||
|
||||
response := allowedResponse()
|
||||
if enforce {
|
||||
if result := policy.AggregateCheckResults(a.Evaluator.EvaluatePod(nsPolicy.Enforce, podMetadata, podSpec)); !result.Allowed {
|
||||
response = forbiddenResponse(result.ForbiddenDetail())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: reuse previous evaluation if audit level+version is the same as enforce level+version
|
||||
if result := policy.AggregateCheckResults(a.Evaluator.EvaluatePod(nsPolicy.Audit, podMetadata, podSpec)); !result.Allowed {
|
||||
auditAnnotations["audit"] = result.ForbiddenDetail()
|
||||
}
|
||||
|
||||
// TODO: reuse previous evaluation if warn level+version is the same as audit or enforce level+version
|
||||
if result := policy.AggregateCheckResults(a.Evaluator.EvaluatePod(nsPolicy.Warn, podMetadata, podSpec)); !result.Allowed {
|
||||
// TODO: Craft a better user-facing warning message
|
||||
response.Warnings = append(response.Warnings, fmt.Sprintf("Pod violates PodSecurity profile %s: %s", nsPolicy.Warn.String(), result.ForbiddenDetail()))
|
||||
}
|
||||
|
||||
response.AuditAnnotations = auditAnnotations
|
||||
return response
|
||||
}
|
||||
|
||||
func (a *Admission) EvaluatePodsInNamespace(ctx context.Context, namespace string, enforce api.LevelVersion) []string {
|
||||
timeout := namespacePodCheckTimeout
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
timeRemaining := time.Duration(0.9 * float64(time.Until(deadline))) // Leave a little time to respond.
|
||||
if timeout > timeRemaining {
|
||||
timeout = timeRemaining
|
||||
}
|
||||
}
|
||||
deadline := time.Now().Add(timeout)
|
||||
ctx, cancel := context.WithDeadline(ctx, deadline)
|
||||
defer cancel()
|
||||
|
||||
pods, err := a.PodLister.ListPods(ctx, namespace)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "Failed to list pods", "namespace", namespace)
|
||||
return []string{"Failed to list pods"}
|
||||
}
|
||||
|
||||
var warnings []string
|
||||
if len(pods) > namespaceMaxPodsToCheck {
|
||||
warnings = append(warnings, fmt.Sprintf("Large namespace: only checking the first %d of %d pods", namespaceMaxPodsToCheck, len(pods)))
|
||||
pods = pods[0:namespaceMaxPodsToCheck]
|
||||
}
|
||||
|
||||
for i, pod := range pods {
|
||||
// short-circuit on exempt runtimeclass
|
||||
if a.exemptRuntimeClass(pod.Spec.RuntimeClassName) {
|
||||
continue
|
||||
}
|
||||
r := policy.AggregateCheckResults(a.Evaluator.EvaluatePod(enforce, &pod.ObjectMeta, &pod.Spec))
|
||||
if !r.Allowed {
|
||||
// TODO: consider aggregating results (e.g. multiple pods failed for the same reasons)
|
||||
warnings = append(warnings, fmt.Sprintf("%s: %s", pod.Name, r.ForbiddenReason()))
|
||||
}
|
||||
if time.Now().After(deadline) {
|
||||
return append(warnings, fmt.Sprintf("Timeout reached after checking %d pods", i+1))
|
||||
}
|
||||
}
|
||||
|
||||
return warnings
|
||||
}
|
||||
|
||||
func (a *Admission) PolicyToEvaluate(labels map[string]string) (api.Policy, error) {
|
||||
return api.PolicyToEvaluate(labels, a.defaultPolicy)
|
||||
}
|
||||
|
||||
// allowResponse is the response used when the admission decision is allow.
|
||||
func allowedResponse() admissionv1.AdmissionResponse {
|
||||
return admissionv1.AdmissionResponse{Allowed: true}
|
||||
}
|
||||
|
||||
// forbiddenResponse is the response used when the admission decision is deny for policy violations.
|
||||
func forbiddenResponse(msg string) admissionv1.AdmissionResponse {
|
||||
return admissionv1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Status: metav1.StatusFailure,
|
||||
Reason: metav1.StatusReasonForbidden,
|
||||
Message: msg,
|
||||
Code: http.StatusForbidden,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// invalidResponse is the response used for namespace requests when namespace labels are invalid.
|
||||
func invalidResponse(msg string) admissionv1.AdmissionResponse {
|
||||
return admissionv1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Status: metav1.StatusFailure,
|
||||
Reason: metav1.StatusReasonInvalid,
|
||||
Message: msg,
|
||||
Code: 422,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// internalErrorResponse is the response used for unexpected errors
|
||||
func internalErrorResponse(msg string) admissionv1.AdmissionResponse {
|
||||
return admissionv1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Status: metav1.StatusFailure,
|
||||
Reason: metav1.StatusReasonInternalError,
|
||||
Message: msg,
|
||||
Code: http.StatusInternalServerError,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// isSignificantPodUpdate determines whether a pod update should trigger a policy evaluation.
|
||||
// Relevant mutable pod fields as of 1.21 are image and seccomp annotations:
|
||||
// * https://github.com/kubernetes/kubernetes/blob/release-1.21/pkg/apis/core/validation/validation.go#L3947-L3949
|
||||
func isSignificantPodUpdate(pod, oldPod *corev1.Pod) bool {
|
||||
if pod.Annotations[corev1.SeccompPodAnnotationKey] != oldPod.Annotations[corev1.SeccompPodAnnotationKey] {
|
||||
return true
|
||||
}
|
||||
if len(pod.Spec.Containers) != len(oldPod.Spec.Containers) {
|
||||
return true
|
||||
}
|
||||
if len(pod.Spec.InitContainers) != len(oldPod.Spec.InitContainers) {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < len(pod.Spec.Containers); i++ {
|
||||
if isSignificantContainerUpdate(&pod.Spec.Containers[i], &oldPod.Spec.Containers[i], pod.Annotations, oldPod.Annotations) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(pod.Spec.InitContainers); i++ {
|
||||
if isSignificantContainerUpdate(&pod.Spec.InitContainers[i], &oldPod.Spec.InitContainers[i], pod.Annotations, oldPod.Annotations) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, c := range pod.Spec.EphemeralContainers {
|
||||
var oldC *corev1.Container
|
||||
for i, oc := range oldPod.Spec.EphemeralContainers {
|
||||
if oc.Name == c.Name {
|
||||
oldC = (*corev1.Container)(&oldPod.Spec.EphemeralContainers[i].EphemeralContainerCommon)
|
||||
break
|
||||
}
|
||||
}
|
||||
if oldC == nil {
|
||||
return true // EphemeralContainer added
|
||||
}
|
||||
if isSignificantContainerUpdate((*corev1.Container)(&c.EphemeralContainerCommon), oldC, pod.Annotations, oldPod.Annotations) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isSignificantContainerUpdate determines whether a container update should trigger a policy evaluation.
|
||||
func isSignificantContainerUpdate(container, oldContainer *corev1.Container, annotations, oldAnnotations map[string]string) bool {
|
||||
if container.Image != oldContainer.Image {
|
||||
return true
|
||||
}
|
||||
seccompKey := corev1.SeccompContainerAnnotationKeyPrefix + container.Name
|
||||
return annotations[seccompKey] != oldAnnotations[seccompKey]
|
||||
}
|
||||
|
||||
func (a *Admission) exemptNamespace(namespace string) bool {
|
||||
if len(namespace) == 0 {
|
||||
return false
|
||||
}
|
||||
// TODO: consider optimizing to O(1) lookup
|
||||
return containsString(namespace, a.Configuration.Exemptions.Namespaces)
|
||||
}
|
||||
func (a *Admission) exemptUser(username string) bool {
|
||||
if len(username) == 0 {
|
||||
return false
|
||||
}
|
||||
// TODO: consider optimizing to O(1) lookup
|
||||
return containsString(username, a.Configuration.Exemptions.Usernames)
|
||||
}
|
||||
func (a *Admission) exemptRuntimeClass(runtimeClass *string) bool {
|
||||
if runtimeClass == nil || len(*runtimeClass) == 0 {
|
||||
return false
|
||||
}
|
||||
// TODO: consider optimizing to O(1) lookup
|
||||
return containsString(*runtimeClass, a.Configuration.Exemptions.RuntimeClasses)
|
||||
}
|
||||
func containsString(needle string, haystack []string) bool {
|
||||
for _, s := range haystack {
|
||||
if s == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
@ -0,0 +1,465 @@
|
||||
/*
|
||||
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 admission
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
admissionv1 "k8s.io/api/admission/v1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
admissionapi "k8s.io/pod-security-admission/admission/api"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
"k8s.io/pod-security-admission/policy"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
func TestDefaultExtractPodSpec(t *testing.T) {
|
||||
metadata := metav1.ObjectMeta{
|
||||
Name: "foo-pod",
|
||||
}
|
||||
spec := corev1.PodSpec{
|
||||
Containers: []corev1.Container{{
|
||||
Name: "foo-container",
|
||||
}},
|
||||
}
|
||||
objects := []runtime.Object{
|
||||
&corev1.Pod{
|
||||
ObjectMeta: metadata,
|
||||
Spec: spec,
|
||||
},
|
||||
&corev1.PodTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo-template"},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metadata,
|
||||
Spec: spec,
|
||||
},
|
||||
},
|
||||
&corev1.ReplicationController{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo-rc"},
|
||||
Spec: corev1.ReplicationControllerSpec{
|
||||
Template: &corev1.PodTemplateSpec{
|
||||
ObjectMeta: metadata,
|
||||
Spec: spec,
|
||||
},
|
||||
},
|
||||
},
|
||||
&appsv1.ReplicaSet{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo-rs"},
|
||||
Spec: appsv1.ReplicaSetSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metadata,
|
||||
Spec: spec,
|
||||
},
|
||||
},
|
||||
},
|
||||
&appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo-deployment"},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metadata,
|
||||
Spec: spec,
|
||||
},
|
||||
},
|
||||
},
|
||||
&appsv1.StatefulSet{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo-ss"},
|
||||
Spec: appsv1.StatefulSetSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metadata,
|
||||
Spec: spec,
|
||||
},
|
||||
},
|
||||
},
|
||||
&appsv1.DaemonSet{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo-ds"},
|
||||
Spec: appsv1.DaemonSetSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metadata,
|
||||
Spec: spec,
|
||||
},
|
||||
},
|
||||
},
|
||||
&batchv1.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo-job"},
|
||||
Spec: batchv1.JobSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metadata,
|
||||
Spec: spec,
|
||||
},
|
||||
},
|
||||
},
|
||||
&batchv1.CronJob{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo-cronjob"},
|
||||
Spec: batchv1.CronJobSpec{
|
||||
JobTemplate: batchv1.JobTemplateSpec{
|
||||
Spec: batchv1.JobSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metadata,
|
||||
Spec: spec,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
extractor := &DefaultPodSpecExtractor{}
|
||||
for _, obj := range objects {
|
||||
name := obj.(metav1.Object).GetName()
|
||||
actualMetadata, actualSpec, err := extractor.ExtractPodSpec(obj)
|
||||
assert.NoError(t, err, name)
|
||||
assert.Equal(t, &metadata, actualMetadata, "%s: Metadata mismatch", name)
|
||||
assert.Equal(t, &spec, actualSpec, "%s: PodSpec mismatch", name)
|
||||
}
|
||||
|
||||
service := &corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-svc",
|
||||
},
|
||||
}
|
||||
_, _, err := extractor.ExtractPodSpec(service)
|
||||
assert.Error(t, err, "service should not have an extractable pod spec")
|
||||
}
|
||||
|
||||
func TestDefaultHasPodSpec(t *testing.T) {
|
||||
podLikeResources := []schema.GroupResource{
|
||||
corev1.Resource("pods"),
|
||||
corev1.Resource("replicationcontrollers"),
|
||||
corev1.Resource("podtemplates"),
|
||||
appsv1.Resource("replicasets"),
|
||||
appsv1.Resource("deployments"),
|
||||
appsv1.Resource("statefulsets"),
|
||||
appsv1.Resource("daemonsets"),
|
||||
batchv1.Resource("jobs"),
|
||||
batchv1.Resource("cronjobs"),
|
||||
}
|
||||
extractor := &DefaultPodSpecExtractor{}
|
||||
for _, gr := range podLikeResources {
|
||||
assert.True(t, extractor.HasPodSpec(gr), gr.String())
|
||||
}
|
||||
|
||||
nonPodResources := []schema.GroupResource{
|
||||
corev1.Resource("services"),
|
||||
admissionv1.Resource("admissionreviews"),
|
||||
appsv1.Resource("foobars"),
|
||||
}
|
||||
for _, gr := range nonPodResources {
|
||||
assert.False(t, extractor.HasPodSpec(gr), gr.String())
|
||||
}
|
||||
}
|
||||
|
||||
type testEvaluator struct {
|
||||
lv api.LevelVersion
|
||||
}
|
||||
|
||||
func (t *testEvaluator) EvaluatePod(lv api.LevelVersion, meta *metav1.ObjectMeta, spec *corev1.PodSpec) []policy.CheckResult {
|
||||
t.lv = lv
|
||||
if meta.Annotations["error"] != "" {
|
||||
return []policy.CheckResult{{Allowed: false, ForbiddenReason: meta.Annotations["error"]}}
|
||||
} else {
|
||||
return []policy.CheckResult{{Allowed: true}}
|
||||
}
|
||||
}
|
||||
|
||||
type testPodLister struct {
|
||||
called bool
|
||||
pods []*corev1.Pod
|
||||
}
|
||||
|
||||
func (t *testPodLister) ListPods(ctx context.Context, namespace string) ([]*corev1.Pod, error) {
|
||||
t.called = true
|
||||
return t.pods, nil
|
||||
}
|
||||
|
||||
func TestValidateNamespace(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
exemptNamespaces []string
|
||||
exemptRuntimeClasses []string
|
||||
// override default policy
|
||||
defaultPolicy *api.Policy
|
||||
// request subresource
|
||||
subresource string
|
||||
// labels for the new namespace
|
||||
newLabels map[string]string
|
||||
// labels for the old namespace (only used if update=true)
|
||||
oldLabels map[string]string
|
||||
// list of pods to return
|
||||
pods []*corev1.Pod
|
||||
|
||||
expectAllowed bool
|
||||
expectError string
|
||||
expectListPods bool
|
||||
expectEvaluate api.LevelVersion
|
||||
expectWarnings []string
|
||||
}{
|
||||
// creation tests, just validate labels
|
||||
{
|
||||
name: "create privileged",
|
||||
newLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelPrivileged), api.EnforceVersionLabel: "v1.0"},
|
||||
expectAllowed: true,
|
||||
expectListPods: false,
|
||||
},
|
||||
{
|
||||
name: "create baseline",
|
||||
newLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelBaseline)},
|
||||
expectAllowed: true,
|
||||
expectListPods: false,
|
||||
},
|
||||
{
|
||||
name: "create restricted",
|
||||
newLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelBaseline)},
|
||||
expectAllowed: true,
|
||||
expectListPods: false,
|
||||
},
|
||||
{
|
||||
name: "create malformed level",
|
||||
newLabels: map[string]string{api.EnforceLevelLabel: "unknown"},
|
||||
expectAllowed: false,
|
||||
expectError: `must be one of privileged, baseline, restricted`,
|
||||
expectListPods: false,
|
||||
},
|
||||
{
|
||||
name: "create malformed version",
|
||||
newLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelPrivileged), api.EnforceVersionLabel: "unknown"},
|
||||
expectAllowed: false,
|
||||
expectError: `must be "latest" or "v1.x"`,
|
||||
expectListPods: false,
|
||||
},
|
||||
|
||||
// update tests that don't tighten effective policy, no pod list/evaluate
|
||||
{
|
||||
name: "update no-op",
|
||||
newLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelRestricted), api.EnforceVersionLabel: "v1.0"},
|
||||
oldLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelRestricted), api.EnforceVersionLabel: "v1.0"},
|
||||
expectAllowed: true,
|
||||
expectListPods: false,
|
||||
},
|
||||
{
|
||||
name: "update no-op malformed level",
|
||||
newLabels: map[string]string{api.EnforceLevelLabel: "unknown"},
|
||||
oldLabels: map[string]string{api.EnforceLevelLabel: "unknown"},
|
||||
expectAllowed: true,
|
||||
expectListPods: false,
|
||||
},
|
||||
{
|
||||
name: "update no-op malformed version",
|
||||
newLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelBaseline), api.EnforceVersionLabel: "unknown"},
|
||||
oldLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelBaseline), api.EnforceVersionLabel: "unknown"},
|
||||
expectAllowed: true,
|
||||
expectListPods: false,
|
||||
},
|
||||
{
|
||||
name: "update relax level identical version",
|
||||
newLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelBaseline), api.EnforceVersionLabel: "v1.0"},
|
||||
oldLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelRestricted), api.EnforceVersionLabel: "v1.0"},
|
||||
expectAllowed: true,
|
||||
expectListPods: false,
|
||||
},
|
||||
{
|
||||
name: "update relax level explicit latest",
|
||||
newLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelBaseline), api.EnforceVersionLabel: "latest"},
|
||||
oldLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelRestricted), api.EnforceVersionLabel: "latest"},
|
||||
expectAllowed: true,
|
||||
expectListPods: false,
|
||||
},
|
||||
{
|
||||
name: "update relax level implicit latest",
|
||||
newLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelBaseline)},
|
||||
oldLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelRestricted)},
|
||||
expectAllowed: true,
|
||||
expectListPods: false,
|
||||
},
|
||||
{
|
||||
name: "update to explicit privileged",
|
||||
newLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelPrivileged)},
|
||||
oldLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelRestricted), api.EnforceVersionLabel: "v1.0"},
|
||||
expectAllowed: true,
|
||||
expectListPods: false,
|
||||
},
|
||||
{
|
||||
name: "update to implicit privileged",
|
||||
newLabels: map[string]string{},
|
||||
oldLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelRestricted), api.EnforceVersionLabel: "v1.0"},
|
||||
expectAllowed: true,
|
||||
expectListPods: false,
|
||||
},
|
||||
{
|
||||
name: "update exempt to restricted",
|
||||
exemptNamespaces: []string{"test"},
|
||||
newLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelRestricted), api.EnforceVersionLabel: "v1.0"},
|
||||
oldLabels: map[string]string{},
|
||||
expectAllowed: true,
|
||||
expectListPods: false,
|
||||
},
|
||||
|
||||
// update tests that introduce labels errors
|
||||
{
|
||||
name: "update malformed level",
|
||||
newLabels: map[string]string{api.EnforceLevelLabel: "unknown"},
|
||||
oldLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelRestricted), api.EnforceVersionLabel: "v1.0"},
|
||||
expectAllowed: false,
|
||||
expectError: `must be one of privileged, baseline, restricted`,
|
||||
expectListPods: false,
|
||||
},
|
||||
{
|
||||
name: "update malformed version",
|
||||
newLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelPrivileged), api.EnforceVersionLabel: "unknown"},
|
||||
oldLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelRestricted), api.EnforceVersionLabel: "v1.0"},
|
||||
expectAllowed: false,
|
||||
expectError: `must be "latest" or "v1.x"`,
|
||||
expectListPods: false,
|
||||
},
|
||||
|
||||
// update tests that tighten effective policy
|
||||
{
|
||||
name: "update to implicit restricted",
|
||||
newLabels: map[string]string{},
|
||||
oldLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelBaseline), api.EnforceVersionLabel: "v1.0"},
|
||||
defaultPolicy: &api.Policy{Enforce: api.LevelVersion{Level: api.LevelRestricted, Version: api.LatestVersion()}},
|
||||
expectAllowed: true,
|
||||
expectListPods: true,
|
||||
expectEvaluate: api.LevelVersion{Level: api.LevelRestricted, Version: api.LatestVersion()},
|
||||
expectWarnings: []string{"noruntimeclasspod: message", "runtimeclass1pod: message", "runtimeclass2pod: message"},
|
||||
},
|
||||
{
|
||||
name: "update with runtimeclass exempt pods",
|
||||
exemptRuntimeClasses: []string{"runtimeclass1"},
|
||||
newLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelRestricted)},
|
||||
oldLabels: map[string]string{api.EnforceLevelLabel: string(api.LevelBaseline)},
|
||||
expectAllowed: true,
|
||||
expectListPods: true,
|
||||
expectEvaluate: api.LevelVersion{Level: api.LevelRestricted, Version: api.LatestVersion()},
|
||||
expectWarnings: []string{"noruntimeclasspod: message", "runtimeclass2pod: message"},
|
||||
},
|
||||
|
||||
// TODO: test for aggregating pods with identical warnings
|
||||
// TODO: test for bounding evalution time with a warning
|
||||
// TODO: test for bounding pod count with a warning
|
||||
// TODO: test for prioritizing evaluating pods from unique controllers
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
newObject := &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Labels: tc.newLabels,
|
||||
},
|
||||
}
|
||||
var operation = admission.Create
|
||||
var oldObject runtime.Object
|
||||
if tc.oldLabels != nil {
|
||||
operation = admission.Update
|
||||
oldObject = &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Labels: tc.oldLabels,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
attrs := admission.NewAttributesRecord(
|
||||
newObject,
|
||||
oldObject,
|
||||
schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"},
|
||||
newObject.Name,
|
||||
newObject.Name,
|
||||
schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"},
|
||||
tc.subresource,
|
||||
operation,
|
||||
nil,
|
||||
false,
|
||||
nil,
|
||||
)
|
||||
|
||||
defaultPolicy := api.Policy{
|
||||
Enforce: api.LevelVersion{
|
||||
Level: api.LevelPrivileged,
|
||||
Version: api.LatestVersion(),
|
||||
},
|
||||
}
|
||||
if tc.defaultPolicy != nil {
|
||||
defaultPolicy = *tc.defaultPolicy
|
||||
}
|
||||
|
||||
pods := tc.pods
|
||||
if pods == nil {
|
||||
pods = []*corev1.Pod{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "noruntimeclasspod", Annotations: map[string]string{"error": "message"}},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "runtimeclass1pod", Annotations: map[string]string{"error": "message"}},
|
||||
Spec: corev1.PodSpec{RuntimeClassName: pointer.String("runtimeclass1")},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "runtimeclass2pod", Annotations: map[string]string{"error": "message"}},
|
||||
Spec: corev1.PodSpec{RuntimeClassName: pointer.String("runtimeclass2")},
|
||||
},
|
||||
}
|
||||
}
|
||||
podLister := &testPodLister{pods: pods}
|
||||
evaluator := &testEvaluator{}
|
||||
a := &Admission{
|
||||
PodLister: podLister,
|
||||
Evaluator: evaluator,
|
||||
Configuration: &admissionapi.PodSecurityConfiguration{
|
||||
Exemptions: admissionapi.PodSecurityExemptions{
|
||||
Namespaces: tc.exemptNamespaces,
|
||||
RuntimeClasses: tc.exemptRuntimeClasses,
|
||||
},
|
||||
},
|
||||
defaultPolicy: defaultPolicy,
|
||||
}
|
||||
result := a.ValidateNamespace(context.TODO(), attrs)
|
||||
if result.Allowed != tc.expectAllowed {
|
||||
t.Errorf("expected allowed=%v, got %v", tc.expectAllowed, result.Allowed)
|
||||
}
|
||||
|
||||
resultError := ""
|
||||
if result.Result != nil {
|
||||
resultError = result.Result.Message
|
||||
}
|
||||
if (len(resultError) > 0) != (len(tc.expectError) > 0) {
|
||||
t.Errorf("expected error=%v, got %v", tc.expectError, resultError)
|
||||
}
|
||||
if len(tc.expectError) > 0 && !strings.Contains(resultError, tc.expectError) {
|
||||
t.Errorf("expected error containing '%s', got %s", tc.expectError, resultError)
|
||||
}
|
||||
if podLister.called != tc.expectListPods {
|
||||
t.Errorf("expected getPods=%v, got %v", tc.expectListPods, podLister.called)
|
||||
}
|
||||
if evaluator.lv != tc.expectEvaluate {
|
||||
t.Errorf("expected to evaluate %v, got %v", tc.expectEvaluate, evaluator.lv)
|
||||
}
|
||||
if !reflect.DeepEqual(result.Warnings, tc.expectWarnings) {
|
||||
t.Errorf("expected warnings:\n%v\ngot\n%v", tc.expectWarnings, result.Warnings)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
|
||||
// Package api contains PodSecurity admission configuration file types
|
||||
package api
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/errors"
|
||||
policyapi "k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
var requiredErr = fmt.Errorf("required")
|
||||
|
||||
// TODO: deduplicate against PolicyToEvaluate
|
||||
func ToPolicy(defaults PodSecurityDefaults) (policyapi.Policy, error) {
|
||||
var (
|
||||
err error
|
||||
errs []error
|
||||
p policyapi.Policy
|
||||
)
|
||||
|
||||
if len(defaults.Enforce) == 0 {
|
||||
errs = appendErr(errs, requiredErr, "Enforce.Level")
|
||||
} else {
|
||||
p.Enforce.Level, err = policyapi.ParseLevel(defaults.Enforce)
|
||||
errs = appendErr(errs, err, "Enforce.Level")
|
||||
}
|
||||
|
||||
if len(defaults.EnforceVersion) == 0 {
|
||||
errs = appendErr(errs, requiredErr, "Enforce.Version")
|
||||
} else {
|
||||
p.Enforce.Version, err = policyapi.ParseVersion(defaults.EnforceVersion)
|
||||
errs = appendErr(errs, err, "Enforce.Version")
|
||||
}
|
||||
|
||||
if len(defaults.Audit) == 0 {
|
||||
errs = appendErr(errs, requiredErr, "Audit.Level")
|
||||
} else {
|
||||
p.Audit.Level, err = policyapi.ParseLevel(defaults.Audit)
|
||||
errs = appendErr(errs, err, "Audit.Level")
|
||||
}
|
||||
|
||||
if len(defaults.AuditVersion) == 0 {
|
||||
errs = appendErr(errs, requiredErr, "Audit.Version")
|
||||
} else {
|
||||
p.Audit.Version, err = policyapi.ParseVersion(defaults.AuditVersion)
|
||||
errs = appendErr(errs, err, "Audit.Version")
|
||||
}
|
||||
|
||||
if len(defaults.Warn) == 0 {
|
||||
errs = appendErr(errs, requiredErr, "Warn.Level")
|
||||
} else {
|
||||
p.Warn.Level, err = policyapi.ParseLevel(defaults.Warn)
|
||||
errs = appendErr(errs, err, "Warn.Level")
|
||||
}
|
||||
|
||||
if len(defaults.WarnVersion) == 0 {
|
||||
errs = appendErr(errs, requiredErr, "Warn.Version")
|
||||
} else {
|
||||
p.Warn.Version, err = policyapi.ParseVersion(defaults.WarnVersion)
|
||||
errs = appendErr(errs, err, "Warn.Version")
|
||||
}
|
||||
|
||||
return p, errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
// 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,78 @@
|
||||
/*
|
||||
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 load
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/pod-security-admission/admission/api"
|
||||
"k8s.io/pod-security-admission/admission/api/scheme"
|
||||
apiv1alpha1 "k8s.io/pod-security-admission/admission/api/v1alpha1"
|
||||
)
|
||||
|
||||
func LoadFromFile(file string) (*api.PodSecurityConfiguration, error) {
|
||||
if len(file) == 0 {
|
||||
// no file specified, use default config
|
||||
return LoadFromData(nil)
|
||||
}
|
||||
|
||||
// read from file
|
||||
data, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return LoadFromData(data)
|
||||
}
|
||||
|
||||
func LoadFromReader(reader io.Reader) (*api.PodSecurityConfiguration, error) {
|
||||
if reader == nil {
|
||||
// no reader specified, use default config
|
||||
return LoadFromData(nil)
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return LoadFromData(data)
|
||||
}
|
||||
|
||||
func LoadFromData(data []byte) (*api.PodSecurityConfiguration, error) {
|
||||
if len(data) == 0 {
|
||||
// no config provided, return default
|
||||
externalConfig := &apiv1alpha1.PodSecurityConfiguration{}
|
||||
scheme.Scheme.Default(externalConfig)
|
||||
internalConfig := &api.PodSecurityConfiguration{}
|
||||
if err := scheme.Scheme.Convert(externalConfig, internalConfig, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return internalConfig, nil
|
||||
}
|
||||
|
||||
decodedObj, err := runtime.Decode(scheme.Codecs.UniversalDecoder(), data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
configuration, ok := decodedObj.(*api.PodSecurityConfiguration)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected PodSecurityConfiguration, got %T", decodedObj)
|
||||
}
|
||||
return configuration, nil
|
||||
}
|
@ -0,0 +1,305 @@
|
||||
/*
|
||||
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 load
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"k8s.io/pod-security-admission/admission/api"
|
||||
)
|
||||
|
||||
var defaultConfig = &api.PodSecurityConfiguration{
|
||||
Defaults: api.PodSecurityDefaults{
|
||||
Enforce: "privileged", EnforceVersion: "latest",
|
||||
Warn: "privileged", WarnVersion: "latest",
|
||||
Audit: "privileged", AuditVersion: "latest",
|
||||
},
|
||||
}
|
||||
|
||||
func writeTempFile(t *testing.T, content string) string {
|
||||
t.Helper()
|
||||
file, err := ioutil.TempFile("", "podsecurityconfig")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
os.Remove(file.Name())
|
||||
})
|
||||
if err := ioutil.WriteFile(file.Name(), []byte(content), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return file.Name()
|
||||
}
|
||||
|
||||
func TestLoadFromFile(t *testing.T) {
|
||||
// no file
|
||||
{
|
||||
config, err := LoadFromFile("")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(config, defaultConfig) {
|
||||
t.Fatalf("unexpected config:\n%s", cmp.Diff(defaultConfig, config))
|
||||
}
|
||||
}
|
||||
|
||||
// empty file
|
||||
{
|
||||
config, err := LoadFromFile(writeTempFile(t, ``))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(config, defaultConfig) {
|
||||
t.Fatalf("unexpected config:\n%s", cmp.Diff(defaultConfig, config))
|
||||
}
|
||||
}
|
||||
|
||||
// valid file
|
||||
{
|
||||
input := `{
|
||||
"apiVersion":"pod-security.admission.config.k8s.io/v1alpha1",
|
||||
"kind":"PodSecurityConfiguration",
|
||||
"defaults":{"enforce":"baseline"}}`
|
||||
expect := &api.PodSecurityConfiguration{
|
||||
Defaults: api.PodSecurityDefaults{
|
||||
Enforce: "baseline", EnforceVersion: "latest",
|
||||
Warn: "privileged", WarnVersion: "latest",
|
||||
Audit: "privileged", AuditVersion: "latest",
|
||||
},
|
||||
}
|
||||
|
||||
config, err := LoadFromFile(writeTempFile(t, input))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(config, expect) {
|
||||
t.Fatalf("unexpected config:\n%s", cmp.Diff(expect, config))
|
||||
}
|
||||
}
|
||||
|
||||
// missing file
|
||||
{
|
||||
_, err := LoadFromFile(`bogus-missing-pod-security-policy-config-file`)
|
||||
if err == nil {
|
||||
t.Fatalf("expected err, got none")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "bogus-missing-pod-security-policy-config-file") {
|
||||
t.Fatalf("expected missing file error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// invalid content file
|
||||
{
|
||||
input := `{
|
||||
"apiVersion":"pod-security.admission.config.k8s.io/v99",
|
||||
"kind":"PodSecurityConfiguration",
|
||||
"defaults":{"enforce":"baseline"}}`
|
||||
|
||||
_, err := LoadFromFile(writeTempFile(t, input))
|
||||
if err == nil {
|
||||
t.Fatalf("expected err, got none")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "pod-security.admission.config.k8s.io/v99") {
|
||||
t.Fatalf("expected apiVersion error, got %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFromReader(t *testing.T) {
|
||||
// no reader
|
||||
{
|
||||
config, err := LoadFromReader(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(config, defaultConfig) {
|
||||
t.Fatalf("unexpected config:\n%s", cmp.Diff(defaultConfig, config))
|
||||
}
|
||||
}
|
||||
|
||||
// empty reader
|
||||
{
|
||||
config, err := LoadFromReader(&bytes.Buffer{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(config, defaultConfig) {
|
||||
t.Fatalf("unexpected config:\n%s", cmp.Diff(defaultConfig, config))
|
||||
}
|
||||
}
|
||||
|
||||
// valid reader
|
||||
{
|
||||
input := `{
|
||||
"apiVersion":"pod-security.admission.config.k8s.io/v1alpha1",
|
||||
"kind":"PodSecurityConfiguration",
|
||||
"defaults":{"enforce":"baseline"}}`
|
||||
expect := &api.PodSecurityConfiguration{
|
||||
Defaults: api.PodSecurityDefaults{
|
||||
Enforce: "baseline", EnforceVersion: "latest",
|
||||
Warn: "privileged", WarnVersion: "latest",
|
||||
Audit: "privileged", AuditVersion: "latest",
|
||||
},
|
||||
}
|
||||
|
||||
config, err := LoadFromReader(bytes.NewBufferString(input))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(config, expect) {
|
||||
t.Fatalf("unexpected config:\n%s", cmp.Diff(expect, config))
|
||||
}
|
||||
}
|
||||
|
||||
// invalid reader
|
||||
{
|
||||
input := `{
|
||||
"apiVersion":"pod-security.admission.config.k8s.io/v99",
|
||||
"kind":"PodSecurityConfiguration",
|
||||
"defaults":{"enforce":"baseline"}}`
|
||||
|
||||
_, err := LoadFromReader(bytes.NewBufferString(input))
|
||||
if err == nil {
|
||||
t.Fatalf("expected err, got none")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "pod-security.admission.config.k8s.io/v99") {
|
||||
t.Fatalf("expected apiVersion error, got %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFromData(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
expectErr string
|
||||
expectConfig *api.PodSecurityConfiguration
|
||||
}{
|
||||
{
|
||||
name: "nil",
|
||||
data: nil,
|
||||
expectConfig: defaultConfig,
|
||||
},
|
||||
{
|
||||
name: "nil",
|
||||
data: []byte{},
|
||||
expectConfig: defaultConfig,
|
||||
},
|
||||
{
|
||||
name: "v1alpha1 - json",
|
||||
data: []byte(`{
|
||||
"apiVersion":"pod-security.admission.config.k8s.io/v1alpha1",
|
||||
"kind":"PodSecurityConfiguration",
|
||||
"defaults":{"enforce":"baseline"}}`),
|
||||
expectConfig: &api.PodSecurityConfiguration{
|
||||
Defaults: api.PodSecurityDefaults{
|
||||
Enforce: "baseline", EnforceVersion: "latest",
|
||||
Warn: "privileged", WarnVersion: "latest",
|
||||
Audit: "privileged", AuditVersion: "latest",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "v1alpha1 - yaml",
|
||||
data: []byte(`
|
||||
apiVersion: pod-security.admission.config.k8s.io/v1alpha1
|
||||
kind: PodSecurityConfiguration
|
||||
defaults:
|
||||
enforce: baseline
|
||||
enforce-version: v1.7
|
||||
exemptions:
|
||||
usernames: ["alice","bob"]
|
||||
namespaces: ["kube-system"]
|
||||
runtimeClasses: ["special"]
|
||||
`),
|
||||
expectConfig: &api.PodSecurityConfiguration{
|
||||
Defaults: api.PodSecurityDefaults{
|
||||
Enforce: "baseline", EnforceVersion: "v1.7",
|
||||
Warn: "privileged", WarnVersion: "latest",
|
||||
Audit: "privileged", AuditVersion: "latest",
|
||||
},
|
||||
Exemptions: api.PodSecurityExemptions{
|
||||
Usernames: []string{"alice", "bob"},
|
||||
Namespaces: []string{"kube-system"},
|
||||
RuntimeClasses: []string{"special"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing apiVersion",
|
||||
data: []byte(`{"kind":"PodSecurityConfiguration"}`),
|
||||
expectErr: `'apiVersion' is missing`,
|
||||
},
|
||||
{
|
||||
name: "missing kind",
|
||||
data: []byte(`{"apiVersion":"pod-security.admission.config.k8s.io/v1alpha1"}`),
|
||||
expectErr: `'Kind' is missing`,
|
||||
},
|
||||
{
|
||||
name: "unknown group",
|
||||
data: []byte(`{"apiVersion":"apps/v1alpha1","kind":"PodSecurityConfiguration"}`),
|
||||
expectErr: `apps/v1alpha1`,
|
||||
},
|
||||
{
|
||||
name: "unknown version",
|
||||
data: []byte(`{"apiVersion":"pod-security.admission.config.k8s.io/v99","kind":"PodSecurityConfiguration"}`),
|
||||
expectErr: `pod-security.admission.config.k8s.io/v99`,
|
||||
},
|
||||
{
|
||||
name: "unknown kind",
|
||||
data: []byte(`{"apiVersion":"pod-security.admission.config.k8s.io/v1alpha1","kind":"SomeConfiguration"}`),
|
||||
expectErr: `SomeConfiguration`,
|
||||
},
|
||||
{
|
||||
name: "unknown field",
|
||||
data: []byte(`{
|
||||
"apiVersion":"pod-security.admission.config.k8s.io/v1alpha1",
|
||||
"kind":"PodSecurityConfiguration",
|
||||
"deflaults":{"enforce":"baseline"}}`),
|
||||
expectErr: `unknown field: deflaults`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
config, err := LoadFromData(tc.data)
|
||||
if err != nil {
|
||||
if len(tc.expectErr) == 0 {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !strings.Contains(err.Error(), tc.expectErr) {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(tc.expectErr) > 0 {
|
||||
t.Fatalf("expected err, got none")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(config, tc.expectConfig) {
|
||||
t.Fatalf("unexpected config:\n%s", cmp.Diff(tc.expectConfig, config))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
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 (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// GroupName is the group name use in this package
|
||||
const GroupName = "pod-security.admission.config.k8s.io"
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
|
||||
|
||||
var (
|
||||
// SchemeBuilder is a pointer used to call AddToScheme
|
||||
SchemeBuilder runtime.SchemeBuilder
|
||||
localSchemeBuilder = &SchemeBuilder
|
||||
// AddToScheme is used to register the types to API encoding/decoding machinery
|
||||
AddToScheme = localSchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
func init() {
|
||||
// We only register manually written functions here. The registration of the
|
||||
// generated functions takes place in the generated files. The separation
|
||||
// makes the code compile even when the generated files are missing.
|
||||
localSchemeBuilder.Register(addKnownTypes)
|
||||
}
|
||||
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&PodSecurityConfiguration{},
|
||||
)
|
||||
return nil
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
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
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
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 scheme
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
podsecurityapi "k8s.io/pod-security-admission/admission/api"
|
||||
podsecurityv1alpha1 "k8s.io/pod-security-admission/admission/api/v1alpha1"
|
||||
)
|
||||
|
||||
var (
|
||||
// Scheme is the runtime.Scheme to which all podsecurity api types are registered.
|
||||
Scheme = runtime.NewScheme()
|
||||
|
||||
// Codecs provides access to encoding and decoding for the scheme.
|
||||
Codecs = serializer.NewCodecFactory(Scheme, serializer.EnableStrict)
|
||||
)
|
||||
|
||||
func init() {
|
||||
AddToScheme(Scheme)
|
||||
}
|
||||
|
||||
// AddToScheme builds the podsecurity scheme using all known versions of the podsecurity api.
|
||||
func AddToScheme(scheme *runtime.Scheme) {
|
||||
utilruntime.Must(podsecurityapi.AddToScheme(scheme))
|
||||
utilruntime.Must(podsecurityv1alpha1.AddToScheme(scheme))
|
||||
utilruntime.Must(scheme.SetVersionPriority(podsecurityv1alpha1.SchemeGroupVersion))
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
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 scheme
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
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 (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
type PodSecurityConfiguration struct {
|
||||
metav1.TypeMeta
|
||||
Defaults PodSecurityDefaults
|
||||
Exemptions PodSecurityExemptions
|
||||
}
|
||||
|
||||
type PodSecurityDefaults struct {
|
||||
Enforce string
|
||||
EnforceVersion string
|
||||
Audit string
|
||||
AuditVersion string
|
||||
Warn string
|
||||
WarnVersion string
|
||||
}
|
||||
|
||||
type PodSecurityExemptions struct {
|
||||
Usernames []string
|
||||
Namespaces []string
|
||||
RuntimeClasses []string
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
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 v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
||||
return RegisterDefaults(scheme)
|
||||
}
|
||||
|
||||
func SetDefaults_PodSecurityDefaults(obj *PodSecurityDefaults) {
|
||||
if len(obj.Enforce) == 0 {
|
||||
obj.Enforce = string(api.LevelPrivileged)
|
||||
}
|
||||
if len(obj.Warn) == 0 {
|
||||
obj.Warn = string(api.LevelPrivileged)
|
||||
}
|
||||
if len(obj.Audit) == 0 {
|
||||
obj.Audit = string(api.LevelPrivileged)
|
||||
}
|
||||
|
||||
if len(obj.EnforceVersion) == 0 {
|
||||
obj.EnforceVersion = string(api.VersionLatest)
|
||||
}
|
||||
if len(obj.WarnVersion) == 0 {
|
||||
obj.WarnVersion = string(api.VersionLatest)
|
||||
}
|
||||
if len(obj.AuditVersion) == 0 {
|
||||
obj.AuditVersion = string(api.VersionLatest)
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
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 v1alpha1
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
// +k8s:conversion-gen=k8s.io/pod-security-admission/admission/api
|
||||
// +k8s:defaulter-gen=TypeMeta
|
||||
// +groupName=pod-security.admission.config.k8s.io
|
||||
|
||||
// Package v1alpha1 contains PodSecurity admission configuration file types
|
||||
package v1alpha1
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
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 v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// GroupName is the group name use in this package
|
||||
const GroupName = "pod-security.admission.config.k8s.io"
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
|
||||
|
||||
var (
|
||||
// SchemeBuilder is a pointer used to call AddToScheme
|
||||
SchemeBuilder runtime.SchemeBuilder
|
||||
localSchemeBuilder = &SchemeBuilder
|
||||
// AddToScheme is used to register the types to API encoding/decoding machinery
|
||||
AddToScheme = localSchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
func init() {
|
||||
// We only register manually written functions here. The registration of the
|
||||
// generated functions takes place in the generated files. The separation
|
||||
// makes the code compile even when the generated files are missing.
|
||||
localSchemeBuilder.Register(addKnownTypes, addDefaultingFuncs)
|
||||
}
|
||||
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&PodSecurityConfiguration{},
|
||||
)
|
||||
return nil
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
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 v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
type PodSecurityConfiguration struct {
|
||||
metav1.TypeMeta
|
||||
Defaults PodSecurityDefaults `json:"defaults"`
|
||||
Exemptions PodSecurityExemptions `json:"exemptions"`
|
||||
}
|
||||
|
||||
type PodSecurityDefaults struct {
|
||||
Enforce string `json:"enforce,omitempty"`
|
||||
EnforceVersion string `json:"enforce-version,omitempty"`
|
||||
Audit string `json:"audit,omitempty"`
|
||||
AuditVersion string `json:"audit-version,omitempty"`
|
||||
Warn string `json:"warn,omitempty"`
|
||||
WarnVersion string `json:"warn-version,omitempty"`
|
||||
}
|
||||
|
||||
type PodSecurityExemptions struct {
|
||||
Usernames []string `json:"usernames,omitempty"`
|
||||
Namespaces []string `json:"namespaces,omitempty"`
|
||||
RuntimeClasses []string `json:"runtimeClasses,omitempty"`
|
||||
}
|
153
staging/src/k8s.io/pod-security-admission/admission/api/v1alpha1/zz_generated.conversion.go
generated
Normal file
153
staging/src/k8s.io/pod-security-admission/admission/api/v1alpha1/zz_generated.conversion.go
generated
Normal file
@ -0,0 +1,153 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
// Code generated by conversion-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
unsafe "unsafe"
|
||||
|
||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
api "k8s.io/pod-security-admission/admission/api"
|
||||
)
|
||||
|
||||
func init() {
|
||||
localSchemeBuilder.Register(RegisterConversions)
|
||||
}
|
||||
|
||||
// RegisterConversions adds conversion functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
func RegisterConversions(s *runtime.Scheme) error {
|
||||
if err := s.AddGeneratedConversionFunc((*PodSecurityConfiguration)(nil), (*api.PodSecurityConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_PodSecurityConfiguration_To_api_PodSecurityConfiguration(a.(*PodSecurityConfiguration), b.(*api.PodSecurityConfiguration), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*api.PodSecurityConfiguration)(nil), (*PodSecurityConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_api_PodSecurityConfiguration_To_v1alpha1_PodSecurityConfiguration(a.(*api.PodSecurityConfiguration), b.(*PodSecurityConfiguration), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*PodSecurityDefaults)(nil), (*api.PodSecurityDefaults)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_PodSecurityDefaults_To_api_PodSecurityDefaults(a.(*PodSecurityDefaults), b.(*api.PodSecurityDefaults), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*api.PodSecurityDefaults)(nil), (*PodSecurityDefaults)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_api_PodSecurityDefaults_To_v1alpha1_PodSecurityDefaults(a.(*api.PodSecurityDefaults), b.(*PodSecurityDefaults), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*PodSecurityExemptions)(nil), (*api.PodSecurityExemptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_PodSecurityExemptions_To_api_PodSecurityExemptions(a.(*PodSecurityExemptions), b.(*api.PodSecurityExemptions), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*api.PodSecurityExemptions)(nil), (*PodSecurityExemptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_api_PodSecurityExemptions_To_v1alpha1_PodSecurityExemptions(a.(*api.PodSecurityExemptions), b.(*PodSecurityExemptions), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_PodSecurityConfiguration_To_api_PodSecurityConfiguration(in *PodSecurityConfiguration, out *api.PodSecurityConfiguration, s conversion.Scope) error {
|
||||
if err := Convert_v1alpha1_PodSecurityDefaults_To_api_PodSecurityDefaults(&in.Defaults, &out.Defaults, s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := Convert_v1alpha1_PodSecurityExemptions_To_api_PodSecurityExemptions(&in.Exemptions, &out.Exemptions, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_PodSecurityConfiguration_To_api_PodSecurityConfiguration is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_PodSecurityConfiguration_To_api_PodSecurityConfiguration(in *PodSecurityConfiguration, out *api.PodSecurityConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_PodSecurityConfiguration_To_api_PodSecurityConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_api_PodSecurityConfiguration_To_v1alpha1_PodSecurityConfiguration(in *api.PodSecurityConfiguration, out *PodSecurityConfiguration, s conversion.Scope) error {
|
||||
if err := Convert_api_PodSecurityDefaults_To_v1alpha1_PodSecurityDefaults(&in.Defaults, &out.Defaults, s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := Convert_api_PodSecurityExemptions_To_v1alpha1_PodSecurityExemptions(&in.Exemptions, &out.Exemptions, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_api_PodSecurityConfiguration_To_v1alpha1_PodSecurityConfiguration is an autogenerated conversion function.
|
||||
func Convert_api_PodSecurityConfiguration_To_v1alpha1_PodSecurityConfiguration(in *api.PodSecurityConfiguration, out *PodSecurityConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_api_PodSecurityConfiguration_To_v1alpha1_PodSecurityConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_PodSecurityDefaults_To_api_PodSecurityDefaults(in *PodSecurityDefaults, out *api.PodSecurityDefaults, s conversion.Scope) error {
|
||||
out.Enforce = in.Enforce
|
||||
out.EnforceVersion = in.EnforceVersion
|
||||
out.Audit = in.Audit
|
||||
out.AuditVersion = in.AuditVersion
|
||||
out.Warn = in.Warn
|
||||
out.WarnVersion = in.WarnVersion
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_PodSecurityDefaults_To_api_PodSecurityDefaults is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_PodSecurityDefaults_To_api_PodSecurityDefaults(in *PodSecurityDefaults, out *api.PodSecurityDefaults, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_PodSecurityDefaults_To_api_PodSecurityDefaults(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_api_PodSecurityDefaults_To_v1alpha1_PodSecurityDefaults(in *api.PodSecurityDefaults, out *PodSecurityDefaults, s conversion.Scope) error {
|
||||
out.Enforce = in.Enforce
|
||||
out.EnforceVersion = in.EnforceVersion
|
||||
out.Audit = in.Audit
|
||||
out.AuditVersion = in.AuditVersion
|
||||
out.Warn = in.Warn
|
||||
out.WarnVersion = in.WarnVersion
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_api_PodSecurityDefaults_To_v1alpha1_PodSecurityDefaults is an autogenerated conversion function.
|
||||
func Convert_api_PodSecurityDefaults_To_v1alpha1_PodSecurityDefaults(in *api.PodSecurityDefaults, out *PodSecurityDefaults, s conversion.Scope) error {
|
||||
return autoConvert_api_PodSecurityDefaults_To_v1alpha1_PodSecurityDefaults(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_PodSecurityExemptions_To_api_PodSecurityExemptions(in *PodSecurityExemptions, out *api.PodSecurityExemptions, s conversion.Scope) error {
|
||||
out.Usernames = *(*[]string)(unsafe.Pointer(&in.Usernames))
|
||||
out.Namespaces = *(*[]string)(unsafe.Pointer(&in.Namespaces))
|
||||
out.RuntimeClasses = *(*[]string)(unsafe.Pointer(&in.RuntimeClasses))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_PodSecurityExemptions_To_api_PodSecurityExemptions is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_PodSecurityExemptions_To_api_PodSecurityExemptions(in *PodSecurityExemptions, out *api.PodSecurityExemptions, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_PodSecurityExemptions_To_api_PodSecurityExemptions(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_api_PodSecurityExemptions_To_v1alpha1_PodSecurityExemptions(in *api.PodSecurityExemptions, out *PodSecurityExemptions, s conversion.Scope) error {
|
||||
out.Usernames = *(*[]string)(unsafe.Pointer(&in.Usernames))
|
||||
out.Namespaces = *(*[]string)(unsafe.Pointer(&in.Namespaces))
|
||||
out.RuntimeClasses = *(*[]string)(unsafe.Pointer(&in.RuntimeClasses))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_api_PodSecurityExemptions_To_v1alpha1_PodSecurityExemptions is an autogenerated conversion function.
|
||||
func Convert_api_PodSecurityExemptions_To_v1alpha1_PodSecurityExemptions(in *api.PodSecurityExemptions, out *PodSecurityExemptions, s conversion.Scope) error {
|
||||
return autoConvert_api_PodSecurityExemptions_To_v1alpha1_PodSecurityExemptions(in, out, s)
|
||||
}
|
99
staging/src/k8s.io/pod-security-admission/admission/api/v1alpha1/zz_generated.deepcopy.go
generated
Normal file
99
staging/src/k8s.io/pod-security-admission/admission/api/v1alpha1/zz_generated.deepcopy.go
generated
Normal file
@ -0,0 +1,99 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PodSecurityConfiguration) DeepCopyInto(out *PodSecurityConfiguration) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
out.Defaults = in.Defaults
|
||||
in.Exemptions.DeepCopyInto(&out.Exemptions)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSecurityConfiguration.
|
||||
func (in *PodSecurityConfiguration) DeepCopy() *PodSecurityConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PodSecurityConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *PodSecurityConfiguration) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PodSecurityDefaults) DeepCopyInto(out *PodSecurityDefaults) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSecurityDefaults.
|
||||
func (in *PodSecurityDefaults) DeepCopy() *PodSecurityDefaults {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PodSecurityDefaults)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PodSecurityExemptions) DeepCopyInto(out *PodSecurityExemptions) {
|
||||
*out = *in
|
||||
if in.Usernames != nil {
|
||||
in, out := &in.Usernames, &out.Usernames
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Namespaces != nil {
|
||||
in, out := &in.Namespaces, &out.Namespaces
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.RuntimeClasses != nil {
|
||||
in, out := &in.RuntimeClasses, &out.RuntimeClasses
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSecurityExemptions.
|
||||
func (in *PodSecurityExemptions) DeepCopy() *PodSecurityExemptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PodSecurityExemptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
37
staging/src/k8s.io/pod-security-admission/admission/api/v1alpha1/zz_generated.defaults.go
generated
Normal file
37
staging/src/k8s.io/pod-security-admission/admission/api/v1alpha1/zz_generated.defaults.go
generated
Normal file
@ -0,0 +1,37 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
// Code generated by defaulter-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// RegisterDefaults adds defaulters functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
// All generated defaulters are covering - they call all nested defaulters.
|
||||
func RegisterDefaults(scheme *runtime.Scheme) error {
|
||||
scheme.AddTypeDefaultingFunc(&PodSecurityConfiguration{}, func(obj interface{}) { SetObjectDefaults_PodSecurityConfiguration(obj.(*PodSecurityConfiguration)) })
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetObjectDefaults_PodSecurityConfiguration(in *PodSecurityConfiguration) {
|
||||
SetDefaults_PodSecurityDefaults(&in.Defaults)
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
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 validation
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/pod-security-admission/admission/api"
|
||||
)
|
||||
|
||||
func ValidatePodSecurityConfiguration(configuration *api.PodSecurityConfiguration) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
// TODO: validate default levels and versions
|
||||
return allErrs
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
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 validation
|
99
staging/src/k8s.io/pod-security-admission/admission/api/zz_generated.deepcopy.go
generated
Normal file
99
staging/src/k8s.io/pod-security-admission/admission/api/zz_generated.deepcopy.go
generated
Normal file
@ -0,0 +1,99 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PodSecurityConfiguration) DeepCopyInto(out *PodSecurityConfiguration) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
out.Defaults = in.Defaults
|
||||
in.Exemptions.DeepCopyInto(&out.Exemptions)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSecurityConfiguration.
|
||||
func (in *PodSecurityConfiguration) DeepCopy() *PodSecurityConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PodSecurityConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *PodSecurityConfiguration) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PodSecurityDefaults) DeepCopyInto(out *PodSecurityDefaults) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSecurityDefaults.
|
||||
func (in *PodSecurityDefaults) DeepCopy() *PodSecurityDefaults {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PodSecurityDefaults)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PodSecurityExemptions) DeepCopyInto(out *PodSecurityExemptions) {
|
||||
*out = *in
|
||||
if in.Usernames != nil {
|
||||
in, out := &in.Usernames, &out.Usernames
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Namespaces != nil {
|
||||
in, out := &in.Namespaces, &out.Namespaces
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.RuntimeClasses != nil {
|
||||
in, out := &in.RuntimeClasses, &out.RuntimeClasses
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodSecurityExemptions.
|
||||
func (in *PodSecurityExemptions) DeepCopy() *PodSecurityExemptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PodSecurityExemptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
18
staging/src/k8s.io/pod-security-admission/admission/doc.go
Normal file
18
staging/src/k8s.io/pod-security-admission/admission/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 admission contains PodSecurity admission logic
|
||||
package admission
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
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 admission
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
)
|
||||
|
||||
func NamespaceGetterFromClient(client kubernetes.Interface) NamespaceGetter {
|
||||
return &namespaceGetter{client: client}
|
||||
}
|
||||
|
||||
func NamespaceGetterFromListerAndClient(lister corev1listers.NamespaceLister, client kubernetes.Interface) NamespaceGetter {
|
||||
return &namespaceGetter{lister: lister, client: client}
|
||||
}
|
||||
|
||||
type namespaceGetter struct {
|
||||
lister corev1listers.NamespaceLister
|
||||
client kubernetes.Interface
|
||||
}
|
||||
|
||||
func (n *namespaceGetter) GetNamespace(ctx context.Context, name string) (namespace *corev1.Namespace, err error) {
|
||||
if n.lister != nil {
|
||||
namespace, err := n.lister.Get(name)
|
||||
if err == nil || !apierrors.IsNotFound(err) {
|
||||
return namespace, err
|
||||
}
|
||||
}
|
||||
return n.client.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{})
|
||||
}
|
61
staging/src/k8s.io/pod-security-admission/admission/pods.go
Normal file
61
staging/src/k8s.io/pod-security-admission/admission/pods.go
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
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 admission
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
)
|
||||
|
||||
// PodListerFromClient returns a PodLister that does live lists using the provided client.
|
||||
func PodListerFromClient(client kubernetes.Interface) PodLister {
|
||||
return &clientPodLister{client}
|
||||
}
|
||||
|
||||
type clientPodLister struct {
|
||||
client kubernetes.Interface
|
||||
}
|
||||
|
||||
func (p *clientPodLister) ListPods(ctx context.Context, namespace string) ([]*corev1.Pod, error) {
|
||||
list, err := p.client.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pods := make([]*corev1.Pod, len(list.Items))
|
||||
for i := range list.Items {
|
||||
pods[i] = &list.Items[i]
|
||||
}
|
||||
return pods, nil
|
||||
}
|
||||
|
||||
// PodListerFromInformer returns a PodLister that does cached lists using the provided lister.
|
||||
func PodListerFromInformer(lister corev1listers.PodLister) PodLister {
|
||||
return &informerPodLister{lister}
|
||||
}
|
||||
|
||||
type informerPodLister struct {
|
||||
lister corev1listers.PodLister
|
||||
}
|
||||
|
||||
func (p *informerPodLister) ListPods(ctx context.Context, namespace string) ([]*corev1.Pod, error) {
|
||||
return p.lister.Pods(namespace).List(labels.Everything())
|
||||
}
|
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)
|
||||
})
|
||||
}
|
||||
}
|
@ -4,4 +4,23 @@ module k8s.io/pod-security-admission
|
||||
|
||||
go 1.16
|
||||
|
||||
replace k8s.io/pod-security-admission => ../pod-security-admission
|
||||
require (
|
||||
github.com/google/go-cmp v0.5.5
|
||||
github.com/stretchr/testify v1.7.0
|
||||
k8s.io/api v0.0.0
|
||||
k8s.io/apimachinery v0.0.0
|
||||
k8s.io/apiserver v0.0.0
|
||||
k8s.io/client-go v0.0.0
|
||||
k8s.io/klog/v2 v2.9.0
|
||||
k8s.io/utils v0.0.0-20210521133846-da695404a2bc
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
replace (
|
||||
k8s.io/api => ../api
|
||||
k8s.io/apimachinery => ../apimachinery
|
||||
k8s.io/apiserver => ../apiserver
|
||||
k8s.io/client-go => ../client-go
|
||||
k8s.io/component-base => ../component-base
|
||||
k8s.io/pod-security-admission => ../pod-security-admission
|
||||
)
|
||||
|
714
staging/src/k8s.io/pod-security-admission/go.sum
generated
714
staging/src/k8s.io/pod-security-admission/go.sum
generated
@ -0,0 +1,714 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
|
||||
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
|
||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
|
||||
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
|
||||
github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=
|
||||
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
|
||||
go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE=
|
||||
go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc=
|
||||
go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4=
|
||||
go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
|
||||
go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=
|
||||
go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
|
||||
go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
|
||||
go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
|
||||
go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE=
|
||||
go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE=
|
||||
go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
|
||||
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||
k8s.io/utils v0.0.0-20210521133846-da695404a2bc h1:dx6VGe+PnOW/kD/2UV4aUSsRfJGd7+lcqgJ6Xg0HwUs=
|
||||
k8s.io/utils v0.0.0-20210521133846-da695404a2bc/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.21/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.1 h1:nYqY2A6oy37sKLYuSBXuQhbj4JVclzJK13BOIvJG5XU=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
18
staging/src/k8s.io/pod-security-admission/metrics/doc.go
Normal file
18
staging/src/k8s.io/pod-security-admission/metrics/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 metrics contains metrics interfaces and implementations for PodSecurity admission
|
||||
package metrics // import "k8s.io/pod-security-admission/metrics"
|
24
staging/src/k8s.io/pod-security-admission/metrics/metrics.go
Normal file
24
staging/src/k8s.io/pod-security-admission/metrics/metrics.go
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
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 metrics
|
||||
|
||||
type EvaluationRecorder interface {
|
||||
// TODO: fill in args required to record https://github.com/kubernetes/enhancements/tree/master/keps/sig-auth/2579-psp-replacement#monitoring
|
||||
RecordEvaluation()
|
||||
}
|
||||
|
||||
// TODO: default prometheus-based implementation
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
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 metrics
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
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 policy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
func init() {
|
||||
addCheck(CheckAddCapabilities)
|
||||
}
|
||||
|
||||
// CheckAddCapabilities returns a baseline level check
|
||||
// that limits the capabilities that can be added in 1.0+
|
||||
func CheckAddCapabilities() Check {
|
||||
return Check{
|
||||
ID: "addCapabilities",
|
||||
Level: api.LevelBaseline,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: addCapabilities_1_0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
capabilities_allowed_1_0 = sets.NewString(
|
||||
"AUDIT_WRITE",
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FOWNER",
|
||||
"FSETID",
|
||||
"KILL",
|
||||
"MKNOD",
|
||||
"NET_BIND_SERVICE",
|
||||
"SETFCAP",
|
||||
"SETGID",
|
||||
"SETPCAP",
|
||||
"SETUID",
|
||||
"SYS_CHROOT",
|
||||
)
|
||||
)
|
||||
|
||||
func addCapabilities_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
forbiddenContainers := sets.NewString()
|
||||
forbiddenCapabilities := sets.NewString()
|
||||
visitContainersWithPath(podSpec, field.NewPath("spec"), func(container *corev1.Container, path *field.Path) {
|
||||
if container.SecurityContext != nil && container.SecurityContext.Capabilities != nil {
|
||||
for _, c := range container.SecurityContext.Capabilities.Add {
|
||||
if !capabilities_allowed_1_0.Has(string(c)) {
|
||||
forbiddenContainers.Insert(container.Name)
|
||||
forbiddenCapabilities.Insert(string(c))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if len(forbiddenCapabilities) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "forbidden capabilities",
|
||||
ForbiddenDetail: fmt.Sprintf(
|
||||
"containers %q added %q",
|
||||
forbiddenContainers.List(),
|
||||
forbiddenCapabilities.List(),
|
||||
),
|
||||
}
|
||||
}
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
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 policy
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
Privilege escalation (such as via set-user-ID or set-group-ID file mode) should not be allowed.
|
||||
|
||||
**Restricted Fields:**
|
||||
|
||||
spec.containers[*].securityContext.allowPrivilegeEscalation
|
||||
spec.initContainers[*].securityContext.allowPrivilegeEscalation
|
||||
|
||||
**Allowed Values:** false
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckAllowPrivilegeEscalation)
|
||||
}
|
||||
|
||||
// CheckAllowPrivilegeEscalation returns a restricted level check
|
||||
// that requires allowPrivilegeEscalation=false in 1.8+
|
||||
func CheckAllowPrivilegeEscalation() Check {
|
||||
return Check{
|
||||
ID: "allowPrivilegeEscalation",
|
||||
Level: api.LevelRestricted,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
// Field added in 1.8:
|
||||
// https://github.com/kubernetes/kubernetes/blob/v1.8.0/staging/src/k8s.io/api/core/v1/types.go#L4797-L4804
|
||||
MinimumVersion: api.MajorMinorVersion(1, 8),
|
||||
CheckPod: allowPrivilegeEscalation_1_8,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func allowPrivilegeEscalation_1_8(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var forbiddenPaths []string
|
||||
visitContainersWithPath(podSpec, field.NewPath("spec"), func(container *corev1.Container, path *field.Path) {
|
||||
if container.SecurityContext == nil || container.SecurityContext.AllowPrivilegeEscalation == nil || *container.SecurityContext.AllowPrivilegeEscalation {
|
||||
forbiddenPaths = append(forbiddenPaths, path.Child("securityContext", "allowPrivilegeEscalation").String())
|
||||
}
|
||||
})
|
||||
|
||||
if len(forbiddenPaths) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "allowPrivilegeEscalation != false",
|
||||
ForbiddenDetail: strings.Join(forbiddenPaths, ", "),
|
||||
}
|
||||
}
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
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 policy
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
Containers must be required to run as non-root users.
|
||||
|
||||
**Restricted Fields:**
|
||||
|
||||
spec.securityContext.runAsNonRoot
|
||||
spec.containers[*].securityContext.runAsNonRoot
|
||||
spec.initContainers[*].securityContext.runAsNonRoot
|
||||
|
||||
**Allowed Values:** true
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckRunAsNonRoot)
|
||||
}
|
||||
|
||||
// CheckRunAsNonRoot returns a restricted level check
|
||||
// that requires runAsNonRoot=true in 1.0+
|
||||
func CheckRunAsNonRoot() Check {
|
||||
return Check{
|
||||
ID: "runAsNonRoot",
|
||||
Level: api.LevelRestricted,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: runAsNonRoot_1_0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runAsNonRoot_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var forbiddenPaths []string
|
||||
|
||||
// TODO: how to check ephemeral containers
|
||||
|
||||
containerCount := 0
|
||||
containerRunAsNonRootCount := 0
|
||||
podRunAsNonRoot := false
|
||||
|
||||
visitContainersWithPath(podSpec, field.NewPath("spec"), func(container *corev1.Container, path *field.Path) {
|
||||
containerCount++
|
||||
if container.SecurityContext != nil && container.SecurityContext.RunAsNonRoot != nil {
|
||||
if !*container.SecurityContext.RunAsNonRoot {
|
||||
forbiddenPaths = append(forbiddenPaths, path.Child("securityContext", "runAsNonRoot").String())
|
||||
} else {
|
||||
containerRunAsNonRootCount++
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if podSpec.SecurityContext != nil && podSpec.SecurityContext.RunAsNonRoot != nil {
|
||||
if !*podSpec.SecurityContext.RunAsNonRoot {
|
||||
forbiddenPaths = append(forbiddenPaths, field.NewPath("spec").Child("securityContext", "runAsNonRoot").String())
|
||||
} else {
|
||||
podRunAsNonRoot = true
|
||||
}
|
||||
}
|
||||
|
||||
// pod or containers explicitly set runAsNonRoot=false
|
||||
if len(forbiddenPaths) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "runAsNonRoot != false",
|
||||
ForbiddenDetail: strings.Join(forbiddenPaths, ", "),
|
||||
}
|
||||
}
|
||||
|
||||
// pod didn't set runAsNonRoot and not all containers opted into runAsNonRoot
|
||||
if podRunAsNonRoot == false && containerCount > containerRunAsNonRootCount {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "runAsNonRoot != false",
|
||||
}
|
||||
}
|
||||
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
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 policy
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
Setting the SELinux type is restricted, and setting a custom SELinux user or role option is forbidden.
|
||||
|
||||
**Restricted Fields:**
|
||||
spec.securityContext.seLinuxOptions.type
|
||||
spec.containers[*].securityContext.seLinuxOptions.type
|
||||
spec.initContainers[*].securityContext.seLinuxOptions.type
|
||||
|
||||
**Allowed Values:**
|
||||
undefined/empty
|
||||
container_t
|
||||
container_init_t
|
||||
container_kvm_t
|
||||
|
||||
**Restricted Fields:**
|
||||
spec.securityContext.seLinuxOptions.user
|
||||
spec.containers[*].securityContext.seLinuxOptions.user
|
||||
spec.initContainers[*].securityContext.seLinuxOptions.user
|
||||
spec.securityContext.seLinuxOptions.role
|
||||
spec.containers[*].securityContext.seLinuxOptions.role
|
||||
spec.initContainers[*].securityContext.seLinuxOptions.role
|
||||
|
||||
**Allowed Values:** undefined/empty
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckSELinux)
|
||||
}
|
||||
|
||||
// CheckSELinux returns a baseline level check
|
||||
// that limits seLinuxOptions type, user, and role values in 1.0+
|
||||
func CheckSELinux() Check {
|
||||
return Check{
|
||||
ID: "selinux",
|
||||
Level: api.LevelBaseline,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: checkSelinux_1_0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
selinux_allowed_types_1_0 = sets.NewString("", "container_t", "container_init_t", "container_kvm_t")
|
||||
)
|
||||
|
||||
func checkSelinux_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var forbiddenDetails []string
|
||||
|
||||
checkSelinuxOptions := func(path *field.Path, opts *corev1.SELinuxOptions) {
|
||||
if !selinux_allowed_types_1_0.Has(opts.Type) {
|
||||
forbiddenDetails = append(forbiddenDetails, path.Child("securityContext", "seLinuxOptions", "type").String())
|
||||
}
|
||||
if len(opts.User) > 0 {
|
||||
forbiddenDetails = append(forbiddenDetails, path.Child("securityContext", "seLinuxOptions", "user").String())
|
||||
}
|
||||
if len(opts.Role) > 0 {
|
||||
forbiddenDetails = append(forbiddenDetails, path.Child("securityContext", "seLinuxOptions", "role").String())
|
||||
}
|
||||
}
|
||||
|
||||
if podSpec.SecurityContext != nil && podSpec.SecurityContext.SELinuxOptions != nil {
|
||||
checkSelinuxOptions(field.NewPath("spec"), podSpec.SecurityContext.SELinuxOptions)
|
||||
}
|
||||
|
||||
visitContainersWithPath(podSpec, field.NewPath("spec"), func(container *corev1.Container, path *field.Path) {
|
||||
if container.SecurityContext != nil && container.SecurityContext.SELinuxOptions != nil {
|
||||
checkSelinuxOptions(path, container.SecurityContext.SELinuxOptions)
|
||||
}
|
||||
})
|
||||
|
||||
if len(forbiddenDetails) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "forbidden seLinuxOptions",
|
||||
ForbiddenDetail: strings.Join(forbiddenDetails, ", "),
|
||||
}
|
||||
}
|
||||
return CheckResult{Allowed: true}
|
||||
}
|
178
staging/src/k8s.io/pod-security-admission/policy/checks.go
Normal file
178
staging/src/k8s.io/pod-security-admission/policy/checks.go
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
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 policy
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
type Check struct {
|
||||
// ID is the unique ID of the check.
|
||||
ID string
|
||||
// Level is the policy level this check belongs to.
|
||||
// Must be Baseline or Restricted.
|
||||
// Baseline checks are evaluated for baseline and restricted namespaces.
|
||||
// Restricted checks are only evaluated for restricted namespaces.
|
||||
Level api.Level
|
||||
// Versions contains one or more revisions of the check that apply to different versions.
|
||||
// If the check is not yet assigned to a version, this must be a single-item list with a MinimumVersion of "".
|
||||
// Otherwise, MinimumVersion of items must represent strictly increasing versions.
|
||||
Versions []VersionedCheck
|
||||
}
|
||||
|
||||
type VersionedCheck struct {
|
||||
// MinimumVersion is the first policy version this check applies to.
|
||||
// If unset, this check is not yet assigned to a policy version.
|
||||
// If set, must not be "latest".
|
||||
MinimumVersion api.Version
|
||||
// CheckPod determines if the pod is allowed.
|
||||
CheckPod CheckPodFn
|
||||
}
|
||||
|
||||
type CheckPodFn func(*metav1.ObjectMeta, *corev1.PodSpec) CheckResult
|
||||
|
||||
// CheckResult contains the result of checking a pod and indicates whether the pod is allowed,
|
||||
// and if not, why it was forbidden.
|
||||
//
|
||||
// Example output for (false, "host ports", "8080, 9090"):
|
||||
// When checking all pods in a namespace:
|
||||
// disallowed by policy "baseline": host ports, privileged containers, non-default capabilities
|
||||
// When checking an individual pod:
|
||||
// disallowed by policy "baseline": host ports (8080, 9090), privileged containers, non-default capabilities (CAP_NET_RAW)
|
||||
type CheckResult struct {
|
||||
// Allowed indicates if the check allowed the pod.
|
||||
Allowed bool
|
||||
// ForbiddenReason must be set if Allowed is false.
|
||||
// ForbiddenReason should be as succinct as possible and is always output.
|
||||
// Examples:
|
||||
// - "host ports"
|
||||
// - "privileged containers"
|
||||
// - "non-default capabilities"
|
||||
ForbiddenReason string
|
||||
// ForbiddenDetail should only be set if Allowed is false, and is optional.
|
||||
// ForbiddenDetail can include specific values that were disallowed and is used when checking an individual object.
|
||||
// Examples:
|
||||
// - list specific invalid host ports: "8080, 9090"
|
||||
// - list specific invalid containers: "container1, container2"
|
||||
// - list specific non-default capabilities: "CAP_NET_RAW"
|
||||
ForbiddenDetail string
|
||||
}
|
||||
|
||||
// AggergateCheckResult holds the aggregate result of running CheckPod across multiple checks.
|
||||
type AggregateCheckResult struct {
|
||||
// Allowed indicates if all checks allowed the pod.
|
||||
Allowed bool
|
||||
// ForbiddenReasons is a slice of the forbidden reasons from all the forbidden checks. It should not include empty strings.
|
||||
// ForbiddenReasons and ForbiddenDetails must have the same number of elements, and the indexes are for the same check.
|
||||
ForbiddenReasons []string
|
||||
// ForbiddenDetails is a slice of the forbidden details from all the forbidden checks. It may include empty strings.
|
||||
// ForbiddenReasons and ForbiddenDetails must have the same number of elements, and the indexes are for the same check.
|
||||
ForbiddenDetails []string
|
||||
}
|
||||
|
||||
// ForbiddenReason returns a comma-separated string of of the forbidden reasons.
|
||||
// Example: host ports, privileged containers, non-default capabilities
|
||||
func (a *AggregateCheckResult) ForbiddenReason() string {
|
||||
return strings.Join(a.ForbiddenReasons, ", ")
|
||||
}
|
||||
|
||||
// ForbiddenDetail returns a detailed forbidden message, with non-empty details formatted in
|
||||
// parentheses with the associated reason.
|
||||
// Example: host ports (8080, 9090), privileged containers, non-default capabilities (NET_RAW)
|
||||
func (a *AggregateCheckResult) ForbiddenDetail() string {
|
||||
var b strings.Builder
|
||||
for i := 0; i < len(a.ForbiddenReasons); i++ {
|
||||
b.WriteString(a.ForbiddenReasons[i])
|
||||
if a.ForbiddenDetails[i] != "" {
|
||||
b.WriteString(" (")
|
||||
b.WriteString(a.ForbiddenDetails[i])
|
||||
b.WriteString(")")
|
||||
}
|
||||
if i != len(a.ForbiddenReasons)-1 {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// UnknownForbiddenReason is used as the placeholder forbidden reason for checks that incorrectly disallow without providing a reason.
|
||||
const UnknownForbiddenReason = "unknown forbidden reason"
|
||||
|
||||
// AggregateCheckPod runs all the checks and aggregates the forbidden results into a single CheckResult.
|
||||
// The aggregated reason is a comma-separated
|
||||
func AggregateCheckResults(results []CheckResult) AggregateCheckResult {
|
||||
var (
|
||||
reasons []string
|
||||
details []string
|
||||
)
|
||||
for _, result := range results {
|
||||
if !result.Allowed {
|
||||
if len(result.ForbiddenReason) == 0 {
|
||||
reasons = append(reasons, UnknownForbiddenReason)
|
||||
} else {
|
||||
reasons = append(reasons, result.ForbiddenReason)
|
||||
}
|
||||
details = append(details, result.ForbiddenDetail)
|
||||
}
|
||||
}
|
||||
return AggregateCheckResult{
|
||||
Allowed: len(reasons) == 0,
|
||||
ForbiddenReasons: reasons,
|
||||
ForbiddenDetails: details,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
defaultChecks []func() Check
|
||||
experimentalChecks []func() Check
|
||||
)
|
||||
|
||||
func addCheck(f func() Check) {
|
||||
// add to experimental or versioned list
|
||||
c := f()
|
||||
if len(c.Versions) == 1 && c.Versions[0].MinimumVersion == (api.Version{}) {
|
||||
experimentalChecks = append(experimentalChecks, f)
|
||||
} else {
|
||||
defaultChecks = append(defaultChecks, f)
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultChecks returns checks that are expected to be enabled by default.
|
||||
// The results are mutually exclusive with ExperimentalChecks.
|
||||
// It returns a new copy of checks on each invocation and is expected to be called once at setup time.
|
||||
func DefaultChecks() []Check {
|
||||
retval := make([]Check, 0, len(defaultChecks))
|
||||
for _, f := range defaultChecks {
|
||||
retval = append(retval, f())
|
||||
}
|
||||
return retval
|
||||
}
|
||||
|
||||
// ExperimentalChecks returns checks that have not yet been assigned to policy versions.
|
||||
// The results are mutually exclusive with DefaultChecks.
|
||||
// It returns a new copy of checks on each invocation and is expected to be called once at setup time.
|
||||
func ExperimentalChecks() []Check {
|
||||
retval := make([]Check, 0, len(experimentalChecks))
|
||||
for _, f := range experimentalChecks {
|
||||
retval = append(retval, f())
|
||||
}
|
||||
return retval
|
||||
}
|
18
staging/src/k8s.io/pod-security-admission/policy/doc.go
Normal file
18
staging/src/k8s.io/pod-security-admission/policy/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 policy contains implementations of Pod Security Standards checks
|
||||
package policy // import "k8s.io/pod-security-admission/policy"
|
146
staging/src/k8s.io/pod-security-admission/policy/registry.go
Normal file
146
staging/src/k8s.io/pod-security-admission/policy/registry.go
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
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 policy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
// Evaluator holds the Checks that are used to validate a policy.
|
||||
type Evaluator interface {
|
||||
// EvaluatePod evaluates the pod against the policy for the given level & version.
|
||||
EvaluatePod(lv api.LevelVersion, podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) []CheckResult
|
||||
}
|
||||
|
||||
// checkRegistry provides a default implementation of an Evaluator.
|
||||
type checkRegistry struct {
|
||||
// The checks are a map of check_ID -> sorted slice of versioned checks, newest first
|
||||
baselineChecks, restrictedChecks map[api.Version][]CheckPodFn
|
||||
// maxVersion is the maximum version that is cached, guaranteed to be at least
|
||||
// the max MinimumVersion of all registered checks.
|
||||
maxVersion api.Version
|
||||
}
|
||||
|
||||
// NewEvaluator constructs a new Evaluator instance from the list of checks. If the provided checks are invalid,
|
||||
// an error is returned. A valid list of checks must meet the following requirements:
|
||||
// 1. Check.ID is unique in the list
|
||||
// 2. Check.Level must be either Baseline or Restricted
|
||||
// 3. Checks must have a non-empty set of versions, sorted in a strictly increasing order
|
||||
// 4. Check.Versions cannot include 'latest'
|
||||
func NewEvaluator(checks []Check) (Evaluator, error) {
|
||||
if err := validateChecks(checks); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := &checkRegistry{
|
||||
baselineChecks: map[api.Version][]CheckPodFn{},
|
||||
restrictedChecks: map[api.Version][]CheckPodFn{},
|
||||
}
|
||||
populate(r, checks)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (r *checkRegistry) EvaluatePod(lv api.LevelVersion, podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) []CheckResult {
|
||||
if lv.Level == api.LevelPrivileged {
|
||||
return nil
|
||||
}
|
||||
if r.maxVersion.Older(lv.Version) {
|
||||
lv.Version = r.maxVersion
|
||||
}
|
||||
results := []CheckResult{}
|
||||
for _, check := range r.baselineChecks[lv.Version] {
|
||||
results = append(results, check(podMetadata, podSpec))
|
||||
}
|
||||
if lv.Level == api.LevelBaseline {
|
||||
return results
|
||||
}
|
||||
for _, check := range r.restrictedChecks[lv.Version] {
|
||||
results = append(results, check(podMetadata, podSpec))
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func validateChecks(checks []Check) error {
|
||||
ids := map[string]bool{}
|
||||
for _, check := range checks {
|
||||
if ids[check.ID] {
|
||||
return fmt.Errorf("multiple checks registered for ID %s", check.ID)
|
||||
}
|
||||
ids[check.ID] = true
|
||||
if check.Level != api.LevelBaseline && check.Level != api.LevelRestricted {
|
||||
return fmt.Errorf("check %s: invalid level %s", check.ID, check.Level)
|
||||
}
|
||||
if len(check.Versions) == 0 {
|
||||
return fmt.Errorf("check %s: empty", check.ID)
|
||||
}
|
||||
maxVersion := api.Version{}
|
||||
for _, c := range check.Versions {
|
||||
if c.MinimumVersion == (api.Version{}) {
|
||||
return fmt.Errorf("check %s: undefined version found", check.ID)
|
||||
}
|
||||
if c.MinimumVersion.Latest() {
|
||||
return fmt.Errorf("check %s: version cannot be 'latest'", check.ID)
|
||||
}
|
||||
if maxVersion == c.MinimumVersion {
|
||||
return fmt.Errorf("check %s: duplicate version %s", check.ID, c.MinimumVersion)
|
||||
}
|
||||
if !maxVersion.Older(c.MinimumVersion) {
|
||||
return fmt.Errorf("check %s: versions must be strictly increasing", check.ID)
|
||||
}
|
||||
maxVersion = c.MinimumVersion
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func populate(r *checkRegistry, validChecks []Check) {
|
||||
// Find the max(MinimumVersion) across all checks.
|
||||
for _, c := range validChecks {
|
||||
lastVersion := c.Versions[len(c.Versions)-1].MinimumVersion
|
||||
if r.maxVersion.Older(lastVersion) {
|
||||
r.maxVersion = lastVersion
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range validChecks {
|
||||
if c.Level == api.LevelRestricted {
|
||||
inflateVersions(c, r.restrictedChecks, r.maxVersion)
|
||||
} else {
|
||||
inflateVersions(c, r.baselineChecks, r.maxVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func inflateVersions(check Check, versions map[api.Version][]CheckPodFn, maxVersion api.Version) {
|
||||
for i, c := range check.Versions {
|
||||
var nextVersion api.Version
|
||||
if i+1 < len(check.Versions) {
|
||||
nextVersion = check.Versions[i+1].MinimumVersion
|
||||
} else {
|
||||
// Assumes only 1 Major version.
|
||||
nextVersion = api.MajorMinorVersion(1, maxVersion.Minor()+1)
|
||||
}
|
||||
// Iterate over all versions from the minimum of the current check, to the minimum of the
|
||||
// next check, or the maxVersion++.
|
||||
for v := c.MinimumVersion; v.Older(nextVersion); v = api.MajorMinorVersion(1, v.Minor()+1) {
|
||||
versions[v] = append(versions[v], check.Versions[i].CheckPod)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
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 policy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
func TestCheckRegistry(t *testing.T) {
|
||||
checks := []Check{
|
||||
generateCheck("a", api.LevelBaseline, []string{"v1.0"}),
|
||||
generateCheck("b", api.LevelBaseline, []string{"v1.10"}),
|
||||
generateCheck("c", api.LevelBaseline, []string{"v1.0", "v1.5", "v1.10"}),
|
||||
generateCheck("d", api.LevelBaseline, []string{"v1.11", "v1.15", "v1.20"}),
|
||||
generateCheck("e", api.LevelRestricted, []string{"v1.0"}),
|
||||
generateCheck("f", api.LevelRestricted, []string{"v1.12", "v1.16", "v1.21"}),
|
||||
}
|
||||
|
||||
reg, err := NewEvaluator(checks)
|
||||
require.NoError(t, err)
|
||||
|
||||
levelCases := []struct {
|
||||
level api.Level
|
||||
version string
|
||||
expectedReasons []string
|
||||
}{
|
||||
{api.LevelPrivileged, "v1.0", nil},
|
||||
{api.LevelPrivileged, "latest", nil},
|
||||
{api.LevelBaseline, "v1.0", []string{"a:v1.0", "c:v1.0"}},
|
||||
{api.LevelBaseline, "v1.4", []string{"a:v1.0", "c:v1.0"}},
|
||||
{api.LevelBaseline, "v1.5", []string{"a:v1.0", "c:v1.5"}},
|
||||
{api.LevelBaseline, "v1.10", []string{"a:v1.0", "b:v1.10", "c:v1.10"}},
|
||||
{api.LevelBaseline, "v1.11", []string{"a:v1.0", "b:v1.10", "c:v1.10", "d:v1.11"}},
|
||||
{api.LevelBaseline, "latest", []string{"a:v1.0", "b:v1.10", "c:v1.10", "d:v1.20"}},
|
||||
{api.LevelRestricted, "v1.0", []string{"a:v1.0", "c:v1.0", "e:v1.0"}},
|
||||
{api.LevelRestricted, "v1.4", []string{"a:v1.0", "c:v1.0", "e:v1.0"}},
|
||||
{api.LevelRestricted, "v1.5", []string{"a:v1.0", "c:v1.5", "e:v1.0"}},
|
||||
{api.LevelRestricted, "v1.10", []string{"a:v1.0", "b:v1.10", "c:v1.10", "e:v1.0"}},
|
||||
{api.LevelRestricted, "v1.11", []string{"a:v1.0", "b:v1.10", "c:v1.10", "d:v1.11", "e:v1.0"}},
|
||||
{api.LevelRestricted, "latest", []string{"a:v1.0", "b:v1.10", "c:v1.10", "d:v1.20", "e:v1.0", "f:v1.21"}},
|
||||
{api.LevelRestricted, "v1.10000", []string{"a:v1.0", "b:v1.10", "c:v1.10", "d:v1.20", "e:v1.0", "f:v1.21"}},
|
||||
}
|
||||
for _, test := range levelCases {
|
||||
t.Run(fmt.Sprintf("%s:%s", test.level, test.version), func(t *testing.T) {
|
||||
results := reg.EvaluatePod(api.LevelVersion{test.level, versionOrPanic(test.version)}, nil, nil)
|
||||
|
||||
// Set extract the ForbiddenReasons from the results.
|
||||
var actualReasons []string
|
||||
for _, result := range results {
|
||||
actualReasons = append(actualReasons, result.ForbiddenReason)
|
||||
}
|
||||
assert.ElementsMatch(t, test.expectedReasons, actualReasons)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func generateCheck(id string, level api.Level, versions []string) Check {
|
||||
c := Check{
|
||||
ID: id,
|
||||
Level: level,
|
||||
}
|
||||
for _, ver := range versions {
|
||||
v := versionOrPanic(ver) // Copy ver so it can be used in the CheckPod closure.
|
||||
c.Versions = append(c.Versions, VersionedCheck{
|
||||
MinimumVersion: v,
|
||||
CheckPod: func(_ *metav1.ObjectMeta, _ *corev1.PodSpec) CheckResult {
|
||||
return CheckResult{
|
||||
ForbiddenReason: fmt.Sprintf("%s:%s", id, v),
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func versionOrPanic(v string) api.Version {
|
||||
ver, err := api.ParseVersion(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ver
|
||||
}
|
42
staging/src/k8s.io/pod-security-admission/policy/visitor.go
Normal file
42
staging/src/k8s.io/pod-security-admission/policy/visitor.go
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
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 policy
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// ContainerVisitorWithPath is called with each container and the field.Path to that container
|
||||
type ContainerVisitorWithPath func(container *corev1.Container, path *field.Path)
|
||||
|
||||
// visitContainersWithPath invokes the visitor function with a pointer to the spec
|
||||
// of every container in the given pod spec and the field.Path to that container.
|
||||
func visitContainersWithPath(podSpec *corev1.PodSpec, specPath *field.Path, visitor ContainerVisitorWithPath) {
|
||||
fldPath := specPath.Child("initContainers")
|
||||
for i := range podSpec.InitContainers {
|
||||
visitor(&podSpec.InitContainers[i], fldPath.Index(i))
|
||||
}
|
||||
fldPath = specPath.Child("containers")
|
||||
for i := range podSpec.Containers {
|
||||
visitor(&podSpec.Containers[i], fldPath.Index(i))
|
||||
}
|
||||
fldPath = specPath.Child("ephemeralContainers")
|
||||
for i := range podSpec.EphemeralContainers {
|
||||
visitor((*corev1.Container)(&podSpec.EphemeralContainers[i].EphemeralContainerCommon), fldPath.Index(i))
|
||||
}
|
||||
}
|
18
staging/src/k8s.io/pod-security-admission/test/doc.go
Normal file
18
staging/src/k8s.io/pod-security-admission/test/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 test contains tests for PodSecurity admission
|
||||
package test // import "k8s.io/pod-security-admission/test"
|
193
staging/src/k8s.io/pod-security-admission/test/fixtures.go
Normal file
193
staging/src/k8s.io/pod-security-admission/test/fixtures.go
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
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 test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
"k8s.io/pod-security-admission/policy"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
// minimalValidPods holds minimal valid pods per-level per-version.
|
||||
// To get a valid pod for a particular level/version, use getMinimalValidPod().
|
||||
var minimalValidPods = map[api.Level]map[api.Version]*corev1.Pod{}
|
||||
|
||||
func init() {
|
||||
minimalValidPods[api.LevelBaseline] = map[api.Version]*corev1.Pod{}
|
||||
minimalValidPods[api.LevelRestricted] = map[api.Version]*corev1.Pod{}
|
||||
|
||||
// Define minimal valid baseline pod.
|
||||
// This must remain valid for all versions.
|
||||
baseline_1_0 := &corev1.Pod{Spec: corev1.PodSpec{
|
||||
InitContainers: []corev1.Container{{Name: "initcontainer1", Image: "k8s.gcr.io/pause"}},
|
||||
Containers: []corev1.Container{{Name: "container1", Image: "k8s.gcr.io/pause"}}}}
|
||||
minimalValidPods[api.LevelBaseline][api.MajorMinorVersion(1, 0)] = baseline_1_0
|
||||
|
||||
//
|
||||
// Define minimal valid restricted pods.
|
||||
//
|
||||
|
||||
// 1.0+: baseline + runAsNonRoot=true
|
||||
restricted_1_0 := tweak(baseline_1_0, func(p *corev1.Pod) {
|
||||
p.Spec.SecurityContext = &corev1.PodSecurityContext{RunAsNonRoot: pointer.BoolPtr(true)}
|
||||
})
|
||||
minimalValidPods[api.LevelRestricted][api.MajorMinorVersion(1, 0)] = restricted_1_0
|
||||
|
||||
// 1.8+: runAsNonRoot=true
|
||||
restricted_1_8 := tweak(restricted_1_0, func(p *corev1.Pod) {
|
||||
p.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{AllowPrivilegeEscalation: pointer.BoolPtr(false)}
|
||||
p.Spec.InitContainers[0].SecurityContext = &corev1.SecurityContext{AllowPrivilegeEscalation: pointer.BoolPtr(false)}
|
||||
})
|
||||
minimalValidPods[api.LevelRestricted][api.MajorMinorVersion(1, 8)] = restricted_1_8
|
||||
}
|
||||
|
||||
// getValidPod returns a minimal valid pod for the specified level and version.
|
||||
func getMinimalValidPod(level api.Level, version api.Version) (*corev1.Pod, error) {
|
||||
originalVersion := version
|
||||
for {
|
||||
pod, exists := minimalValidPods[level][version]
|
||||
if exists {
|
||||
return pod.DeepCopy(), nil
|
||||
}
|
||||
if version.Minor() <= 0 {
|
||||
return nil, fmt.Errorf("no valid pod fixture found in specified or older versions for %s/%s", level, originalVersion.String())
|
||||
}
|
||||
version = api.MajorMinorVersion(version.Major(), version.Minor()-1)
|
||||
}
|
||||
}
|
||||
|
||||
// fixtureGenerators holds fixture generators per-level per-version.
|
||||
// To add generators, use registerFixtureGenerator().
|
||||
// To get fixtures for a particular level/version, use getFixtures().
|
||||
var fixtureGenerators = map[fixtureKey]fixtureGenerator{}
|
||||
|
||||
// fixtureKey is a tuple of version/level/check name
|
||||
type fixtureKey struct {
|
||||
version api.Version
|
||||
level api.Level
|
||||
check string
|
||||
}
|
||||
|
||||
// fixtureGenerator holds generators for valid and invalid fixtures.
|
||||
type fixtureGenerator struct {
|
||||
// expectErrorSubstring is a substring to expect in the error message for failed pods.
|
||||
// if empty, the check ID is used.
|
||||
expectErrorSubstring string
|
||||
// generatePass transforms a minimum valid pod into one or more valid pods.
|
||||
// pods do not need to populate metadata.name.
|
||||
generatePass func(*corev1.Pod) []*corev1.Pod
|
||||
// generateFail transforms a minimum valid pod into one or more invalid pods.
|
||||
// pods do not need to populate metadata.name.
|
||||
generateFail func(*corev1.Pod) []*corev1.Pod
|
||||
}
|
||||
|
||||
// fixtureData holds valid and invalid pod fixtures.
|
||||
type fixtureData struct {
|
||||
expectErrorSubstring string
|
||||
|
||||
pass []*corev1.Pod
|
||||
fail []*corev1.Pod
|
||||
}
|
||||
|
||||
// registerFixtureGenerator adds a generator for the given level/version/check.
|
||||
// A generator registered for v1.x is used for v1.x+ if no generator is registered for the higher version.
|
||||
func registerFixtureGenerator(key fixtureKey, generator fixtureGenerator) {
|
||||
if err := checkKey(key); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, exists := fixtureGenerators[key]; exists {
|
||||
panic(fmt.Errorf("fixture generator already registered for key %#v", key))
|
||||
}
|
||||
if generator.generatePass == nil || generator.generateFail == nil {
|
||||
panic(fmt.Errorf("adding %#v: must specify generatePass/generateFail", key))
|
||||
}
|
||||
fixtureGenerators[key] = generator
|
||||
|
||||
if key.level == api.LevelBaseline {
|
||||
// also register to restricted
|
||||
restrictedKey := key
|
||||
restrictedKey.level = api.LevelRestricted
|
||||
if _, exists := fixtureGenerators[restrictedKey]; exists {
|
||||
panic(fmt.Errorf("fixture generator already registered for restricted version of key %#v", key))
|
||||
}
|
||||
fixtureGenerators[restrictedKey] = generator
|
||||
}
|
||||
}
|
||||
|
||||
// getFixtures returns the fixture data for the specified level/version/check.
|
||||
// Fixtures are generated by applying the registered generator to the minimal valid pod for that level/version.
|
||||
// If no fixture generator exists for the given version, previous generators are checked back to 1.0.
|
||||
func getFixtures(key fixtureKey) (fixtureData, error) {
|
||||
if err := checkKey(key); err != nil {
|
||||
return fixtureData{}, err
|
||||
}
|
||||
|
||||
validPodForLevel, err := getMinimalValidPod(key.level, key.version)
|
||||
if err != nil {
|
||||
return fixtureData{}, err
|
||||
}
|
||||
|
||||
for {
|
||||
if generator, exists := fixtureGenerators[key]; exists {
|
||||
data := fixtureData{
|
||||
expectErrorSubstring: generator.expectErrorSubstring,
|
||||
|
||||
pass: generator.generatePass(validPodForLevel.DeepCopy()),
|
||||
fail: generator.generateFail(validPodForLevel.DeepCopy()),
|
||||
}
|
||||
if len(data.expectErrorSubstring) == 0 {
|
||||
data.expectErrorSubstring = key.check
|
||||
}
|
||||
if len(data.pass) == 0 || len(data.fail) == 0 {
|
||||
return fixtureData{}, fmt.Errorf("generatePass/generateFail for %#v must return at least one pod each", key)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
if key.version.Minor() == 0 {
|
||||
return fixtureData{}, fmt.Errorf("no fixture generator found in specified or older versions for %#v", key)
|
||||
}
|
||||
// check the next older version
|
||||
key.version = api.MajorMinorVersion(key.version.Major(), key.version.Minor()-1)
|
||||
}
|
||||
}
|
||||
|
||||
// checkKey ensures the fixture key has a valid level, version, and check.
|
||||
func checkKey(key fixtureKey) error {
|
||||
if key.level != api.LevelBaseline && key.level != api.LevelRestricted {
|
||||
return fmt.Errorf("invalid key, level must be baseline or restricted: %#v", key)
|
||||
}
|
||||
if key.version.Latest() || key.version.Major() != 1 || key.version.Minor() < 0 {
|
||||
return fmt.Errorf("invalid key, version must be 1.0+: %#v", key)
|
||||
}
|
||||
if key.check == "" {
|
||||
return fmt.Errorf("invalid key, check must not be empty")
|
||||
}
|
||||
found := false
|
||||
for _, check := range policy.DefaultChecks() {
|
||||
if check.ID == key.check {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("invalid key %#v, check does not exist at version", key)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/*
|
||||
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 test
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
TODO: include field paths in reflect-based unit test
|
||||
|
||||
containerFields: []string{
|
||||
`securityContext.capabilities.add`,
|
||||
},
|
||||
*/
|
||||
|
||||
// ensureCapabilities ensures the pod and all initContainers and containers have a non-nil capabilities.
|
||||
func ensureCapabilities(p *corev1.Pod) *corev1.Pod {
|
||||
p = ensureSecurityContext(p)
|
||||
for i := range p.Spec.Containers {
|
||||
if p.Spec.Containers[i].SecurityContext.Capabilities == nil {
|
||||
p.Spec.Containers[i].SecurityContext.Capabilities = &corev1.Capabilities{}
|
||||
}
|
||||
}
|
||||
for i := range p.Spec.InitContainers {
|
||||
if p.Spec.InitContainers[i].SecurityContext.Capabilities == nil {
|
||||
p.Spec.InitContainers[i].SecurityContext.Capabilities = &corev1.Capabilities{}
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func init() {
|
||||
fixtureData_1_0 := fixtureGenerator{
|
||||
expectErrorSubstring: "forbidden capabilities",
|
||||
generatePass: func(p *corev1.Pod) []*corev1.Pod {
|
||||
p = ensureCapabilities(p)
|
||||
return []*corev1.Pod{
|
||||
// all allowed capabilities
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.Containers[0].SecurityContext.Capabilities.Add = []corev1.Capability{
|
||||
"AUDIT_WRITE", "CHOWN", "DAC_OVERRIDE", "FOWNER", "FSETID", "KILL", "MKNOD", "NET_BIND_SERVICE", "SETFCAP", "SETGID", "SETPCAP", "SETUID", "SYS_CHROOT",
|
||||
}
|
||||
}),
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.InitContainers[0].SecurityContext.Capabilities.Add = []corev1.Capability{
|
||||
"AUDIT_WRITE", "CHOWN", "DAC_OVERRIDE", "FOWNER", "FSETID", "KILL", "MKNOD", "NET_BIND_SERVICE", "SETFCAP", "SETGID", "SETPCAP", "SETUID", "SYS_CHROOT",
|
||||
}
|
||||
}),
|
||||
}
|
||||
},
|
||||
generateFail: func(p *corev1.Pod) []*corev1.Pod {
|
||||
p = ensureCapabilities(p)
|
||||
return []*corev1.Pod{
|
||||
// NET_RAW
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.Containers[0].SecurityContext.Capabilities.Add = []corev1.Capability{"NET_RAW"}
|
||||
}),
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.InitContainers[0].SecurityContext.Capabilities.Add = []corev1.Capability{"NET_RAW"}
|
||||
}),
|
||||
// case-difference
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.Containers[0].SecurityContext.Capabilities.Add = []corev1.Capability{"chown"}
|
||||
}),
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.InitContainers[0].SecurityContext.Capabilities.Add = []corev1.Capability{"chown"}
|
||||
}),
|
||||
// unknown capability
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.Containers[0].SecurityContext.Capabilities.Add = []corev1.Capability{"bogus"}
|
||||
}),
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.InitContainers[0].SecurityContext.Capabilities.Add = []corev1.Capability{"bogus"}
|
||||
}),
|
||||
// CAP_ prefix
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.Containers[0].SecurityContext.Capabilities.Add = []corev1.Capability{"CAP_CHOWN"}
|
||||
}),
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.InitContainers[0].SecurityContext.Capabilities.Add = []corev1.Capability{"CAP_CHOWN"}
|
||||
}),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
registerFixtureGenerator(
|
||||
fixtureKey{level: api.LevelBaseline, version: api.MajorMinorVersion(1, 0), check: "addCapabilities"},
|
||||
fixtureData_1_0,
|
||||
)
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
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 test
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
/*
|
||||
TODO: include field paths in reflect-based unit test
|
||||
|
||||
containerFields: []string{
|
||||
`securityContext.allowPrivilegeEscalation`,
|
||||
},
|
||||
|
||||
*/
|
||||
|
||||
func init() {
|
||||
fixtureData_1_8 := fixtureGenerator{
|
||||
generatePass: func(p *corev1.Pod) []*corev1.Pod {
|
||||
return []*corev1.Pod{
|
||||
// only valid pod is to explicitly set allowPrivilegeEscalation to false in all containers
|
||||
p,
|
||||
}
|
||||
},
|
||||
generateFail: func(p *corev1.Pod) []*corev1.Pod {
|
||||
return []*corev1.Pod{
|
||||
// explicit true
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = pointer.BoolPtr(true)
|
||||
}),
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.InitContainers[0].SecurityContext.AllowPrivilegeEscalation = pointer.BoolPtr(true)
|
||||
}),
|
||||
// nil AllowPrivilegeEscalation
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = nil }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.AllowPrivilegeEscalation = nil }),
|
||||
// nil security context
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext = nil }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext = nil }),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
registerFixtureGenerator(
|
||||
fixtureKey{level: api.LevelRestricted, version: api.MajorMinorVersion(1, 8), check: "allowPrivilegeEscalation"},
|
||||
fixtureData_1_8,
|
||||
)
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
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 test
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
/*
|
||||
TODO: include field paths in reflect-based unit test
|
||||
|
||||
podFields: []string{
|
||||
`securityContext.runAsNonRoot`,
|
||||
},
|
||||
containerFields: []string{
|
||||
`securityContext.runAsNonRoot`,
|
||||
},
|
||||
|
||||
*/
|
||||
|
||||
func init() {
|
||||
|
||||
fixtureData_1_0 := fixtureGenerator{
|
||||
generatePass: func(p *corev1.Pod) []*corev1.Pod {
|
||||
p = ensureSecurityContext(p)
|
||||
return []*corev1.Pod{
|
||||
// set at pod level
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.SecurityContext.RunAsNonRoot = pointer.BoolPtr(true)
|
||||
p.Spec.Containers[0].SecurityContext.RunAsNonRoot = nil
|
||||
p.Spec.InitContainers[0].SecurityContext.RunAsNonRoot = nil
|
||||
}),
|
||||
// set on all containers
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.SecurityContext.RunAsNonRoot = nil
|
||||
p.Spec.Containers[0].SecurityContext.RunAsNonRoot = pointer.BoolPtr(true)
|
||||
p.Spec.InitContainers[0].SecurityContext.RunAsNonRoot = pointer.BoolPtr(true)
|
||||
}),
|
||||
// set at pod level and containers
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.SecurityContext.RunAsNonRoot = pointer.BoolPtr(true)
|
||||
p.Spec.Containers[0].SecurityContext.RunAsNonRoot = pointer.BoolPtr(true)
|
||||
p.Spec.InitContainers[0].SecurityContext.RunAsNonRoot = pointer.BoolPtr(true)
|
||||
}),
|
||||
}
|
||||
},
|
||||
generateFail: func(p *corev1.Pod) []*corev1.Pod {
|
||||
p = ensureSecurityContext(p)
|
||||
return []*corev1.Pod{
|
||||
// unset everywhere
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.SecurityContext.RunAsNonRoot = nil
|
||||
p.Spec.Containers[0].SecurityContext.RunAsNonRoot = nil
|
||||
p.Spec.InitContainers[0].SecurityContext.RunAsNonRoot = nil
|
||||
}),
|
||||
// explicit false on pod
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.RunAsNonRoot = pointer.BoolPtr(false) }),
|
||||
// explicit false on containers
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.RunAsNonRoot = pointer.BoolPtr(false) }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.RunAsNonRoot = pointer.BoolPtr(false) }),
|
||||
// nil security contexts
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
p.Spec.SecurityContext = nil
|
||||
p.Spec.Containers[0].SecurityContext = nil
|
||||
p.Spec.InitContainers[0].SecurityContext = nil
|
||||
}),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
registerFixtureGenerator(
|
||||
fixtureKey{level: api.LevelRestricted, version: api.MajorMinorVersion(1, 0), check: "runAsNonRoot"},
|
||||
fixtureData_1_0,
|
||||
)
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
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 test
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
TODO: include field paths in reflect-based unit test
|
||||
|
||||
podFields: []string{
|
||||
`spec.securityContext.seLinuxOptions.type`,
|
||||
`spec.securityContext.seLinuxOptions.user`,
|
||||
`spec.securityContext.seLinuxOptions.role`,
|
||||
},
|
||||
containerFields: []string{
|
||||
`securityContext.seLinuxOptions.type`,
|
||||
`securityContext.seLinuxOptions.user`,
|
||||
`securityContext.seLinuxOptions.role`,
|
||||
},
|
||||
*/
|
||||
|
||||
func init() {
|
||||
fixtureData_1_0 := fixtureGenerator{
|
||||
expectErrorSubstring: "seLinuxOptions",
|
||||
generatePass: func(p *corev1.Pod) []*corev1.Pod {
|
||||
p = ensureSELinuxOptions(p)
|
||||
return []*corev1.Pod{
|
||||
// security context with no seLinuxOptions
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions = nil }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions = nil }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions = nil }),
|
||||
// seLinuxOptions with type=""
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Type = "" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Type = "" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Type = "" }),
|
||||
// seLinuxOptions with type="container_t"
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Type = "container_t" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Type = "container_t" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Type = "container_t" }),
|
||||
// seLinuxOptions with type="container_init_t"
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Type = "container_init_t" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Type = "container_init_t" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Type = "container_init_t" }),
|
||||
// seLinuxOptions with type="container_kvm_t"
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Type = "container_kvm_t" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Type = "container_kvm_t" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Type = "container_kvm_t" }),
|
||||
// seLinuxOptions with level=""
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Level = "" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Level = "" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Level = "" }),
|
||||
// seLinuxOptions with arbitrary level=""
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Level = "somevalue" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Level = "somevalue" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Level = "somevalue" }),
|
||||
}
|
||||
},
|
||||
generateFail: func(p *corev1.Pod) []*corev1.Pod {
|
||||
p = ensureSELinuxOptions(p)
|
||||
return []*corev1.Pod{
|
||||
// seLinuxOptions with out of bounds type
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Type = "somevalue" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Type = "somevalue" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Type = "somevalue" }),
|
||||
// seLinuxOptions with out of bounds user
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.User = "somevalue" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.User = "somevalue" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.User = "somevalue" }),
|
||||
// seLinuxOptions with out of bounds role
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Role = "somevalue" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Role = "somevalue" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Role = "somevalue" }),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
registerFixtureGenerator(
|
||||
fixtureKey{level: api.LevelBaseline, version: api.MajorMinorVersion(1, 0), check: "selinux"},
|
||||
fixtureData_1_0,
|
||||
)
|
||||
}
|
155
staging/src/k8s.io/pod-security-admission/test/fixtures_test.go
Normal file
155
staging/src/k8s.io/pod-security-admission/test/fixtures_test.go
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
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 test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
"k8s.io/pod-security-admission/policy"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
const updateEnvVar = "UPDATE_POD_SECURITY_FIXTURE_DATA"
|
||||
|
||||
// TestFixtures ensures fixtures are registered for every check,
|
||||
// and that in-memory fixtures match serialized fixtures in testdata.
|
||||
// When adding new versions or checks, serialized fixtures can be updated by running:
|
||||
//
|
||||
// UPDATE_POD_SECURITY_FIXTURE_DATA=true go test k8s.io/pod-security-admission/test
|
||||
func TestFixtures(t *testing.T) {
|
||||
expectedFiles := sets.NewString("testdata/README.md")
|
||||
|
||||
defaultChecks := policy.DefaultChecks()
|
||||
|
||||
for _, level := range []api.Level{api.LevelBaseline, api.LevelRestricted} {
|
||||
// TODO: derive from registered levels
|
||||
for version := 0; version <= 22; version++ {
|
||||
passDir := filepath.Join("testdata", string(level), fmt.Sprintf("v1.%d", version), "pass")
|
||||
failDir := filepath.Join("testdata", string(level), fmt.Sprintf("v1.%d", version), "fail")
|
||||
|
||||
// render the minimal valid pod fixture
|
||||
validPod, err := getMinimalValidPod(level, api.MajorMinorVersion(1, version))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expectedFiles.Insert(testFixtureFile(t, passDir, "base", validPod))
|
||||
|
||||
// render check-specific fixtures
|
||||
checkIDs, err := checksForLevelAndVersion(defaultChecks, level, api.MajorMinorVersion(1, version))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(checkIDs) == 0 {
|
||||
t.Fatal(fmt.Errorf("no checks registered for %s/1.%d", level, version))
|
||||
}
|
||||
for _, checkID := range checkIDs {
|
||||
checkData, err := getFixtures(fixtureKey{level: level, version: api.MajorMinorVersion(1, version), check: checkID})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i, pod := range checkData.pass {
|
||||
expectedFiles.Insert(testFixtureFile(t, passDir, fmt.Sprintf("%s%d", strings.ToLower(checkID), i), pod))
|
||||
}
|
||||
for i, pod := range checkData.fail {
|
||||
expectedFiles.Insert(testFixtureFile(t, failDir, fmt.Sprintf("%s%d", strings.ToLower(checkID), i), pod))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actualFileList := []string{}
|
||||
err := filepath.Walk("testdata", func(path string, f os.FileInfo, err error) error {
|
||||
if !f.IsDir() {
|
||||
actualFileList = append(actualFileList, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
actualFiles := sets.NewString(actualFileList...)
|
||||
if missingFiles := expectedFiles.Difference(actualFiles); len(missingFiles) > 0 {
|
||||
t.Errorf("unexpected missing fixtures:\n%s", strings.Join(missingFiles.List(), "\n"))
|
||||
}
|
||||
if extraFiles := actualFiles.Difference(expectedFiles); len(extraFiles) > 0 {
|
||||
t.Errorf("unexpected extra fixtures:\n%s", strings.Join(extraFiles.List(), "\n"))
|
||||
if os.Getenv(updateEnvVar) == "true" {
|
||||
for extra := range extraFiles {
|
||||
os.Remove(extra)
|
||||
}
|
||||
t.Logf("Removed extra fixture files")
|
||||
t.Logf("Verify the diff, commit changes, and rerun the tests")
|
||||
} else {
|
||||
t.Logf("If the files are expected to be removed, re-run with %s=true to drop extra fixture files", updateEnvVar)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testFixtureFile(t *testing.T, dir, name string, pod *corev1.Pod) string {
|
||||
filename := filepath.Join(dir, name+".yaml")
|
||||
pod = pod.DeepCopy()
|
||||
pod.Name = name
|
||||
|
||||
expectedYAML, _ := ioutil.ReadFile(filename)
|
||||
|
||||
jsonData, err := runtime.Encode(scheme.Codecs.LegacyCodec(corev1.SchemeGroupVersion), pod)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
yamlData, err := yaml.JSONToYAML(jsonData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// clean up noise in fixtures
|
||||
yamlData = []byte(strings.ReplaceAll(string(yamlData), " creationTimestamp: null\n", ""))
|
||||
yamlData = []byte(strings.ReplaceAll(string(yamlData), " resources: {}\n", ""))
|
||||
yamlData = []byte(strings.ReplaceAll(string(yamlData), "status: {}\n", ""))
|
||||
|
||||
if string(yamlData) != string(expectedYAML) {
|
||||
t.Errorf("fixture data does not match the test fixture in %s", filename)
|
||||
|
||||
if os.Getenv(updateEnvVar) == "true" {
|
||||
if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(filename, []byte(yamlData), os.FileMode(0755)); err == nil {
|
||||
t.Logf("Updated data in %s", filename)
|
||||
t.Logf("Verify the diff, commit changes, and rerun the tests")
|
||||
} else {
|
||||
t.Logf("Could not update data in %s: %v", filename, err)
|
||||
}
|
||||
} else {
|
||||
t.Logf("Diff between generated data and fixture data in %s:\n-------------\n%s", filename, diff.StringDiff(string(yamlData), string(expectedYAML)))
|
||||
t.Logf("If the change is expected, re-run with %s=true to update the fixtures", updateEnvVar)
|
||||
}
|
||||
}
|
||||
return filename
|
||||
}
|
67
staging/src/k8s.io/pod-security-admission/test/helpers.go
Normal file
67
staging/src/k8s.io/pod-security-admission/test/helpers.go
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
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 test
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// tweak makes a copy of in, passes it to f(), and returns the result.
|
||||
// the input is not modified.
|
||||
func tweak(in *corev1.Pod, f func(copy *corev1.Pod)) *corev1.Pod {
|
||||
out := in.DeepCopy()
|
||||
f(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// ensureSecurityContext ensures the pod and all initContainers and containers have a non-nil security context.
|
||||
func ensureSecurityContext(p *corev1.Pod) *corev1.Pod {
|
||||
p = p.DeepCopy()
|
||||
if p.Spec.SecurityContext == nil {
|
||||
p.Spec.SecurityContext = &corev1.PodSecurityContext{}
|
||||
}
|
||||
for i := range p.Spec.Containers {
|
||||
if p.Spec.Containers[i].SecurityContext == nil {
|
||||
p.Spec.Containers[i].SecurityContext = &corev1.SecurityContext{}
|
||||
}
|
||||
}
|
||||
for i := range p.Spec.InitContainers {
|
||||
if p.Spec.InitContainers[i].SecurityContext == nil {
|
||||
p.Spec.InitContainers[i].SecurityContext = &corev1.SecurityContext{}
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// ensureSELinuxOptions ensures the pod and all initContainers and containers have a non-nil seLinuxOptions.
|
||||
func ensureSELinuxOptions(p *corev1.Pod) *corev1.Pod {
|
||||
p = ensureSecurityContext(p)
|
||||
if p.Spec.SecurityContext.SELinuxOptions == nil {
|
||||
p.Spec.SecurityContext.SELinuxOptions = &corev1.SELinuxOptions{}
|
||||
}
|
||||
for i := range p.Spec.Containers {
|
||||
if p.Spec.Containers[i].SecurityContext.SELinuxOptions == nil {
|
||||
p.Spec.Containers[i].SecurityContext.SELinuxOptions = &corev1.SELinuxOptions{}
|
||||
}
|
||||
}
|
||||
for i := range p.Spec.InitContainers {
|
||||
if p.Spec.InitContainers[i].SecurityContext.SELinuxOptions == nil {
|
||||
p.Spec.InitContainers[i].SecurityContext.SELinuxOptions = &corev1.SELinuxOptions{}
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
300
staging/src/k8s.io/pod-security-admission/test/run.go
Normal file
300
staging/src/k8s.io/pod-security-admission/test/run.go
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
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 test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
"k8s.io/pod-security-admission/policy"
|
||||
)
|
||||
|
||||
// Options hold configuration for running integration tests against an existing server.
|
||||
type Options struct {
|
||||
// ClientConfig is a client configuration with sufficient permission to create, update, and delete
|
||||
// namespaces, pods, and pod-template-containing objects.
|
||||
// Required.
|
||||
ClientConfig *rest.Config
|
||||
|
||||
// CreateNamespace is an optional stub for creating a namespace with the given name and labels.
|
||||
// Returning an error fails the test.
|
||||
// If nil, DefaultCreateNamespace is used.
|
||||
CreateNamespace func(client kubernetes.Interface, name string, labels map[string]string) (*corev1.Namespace, error)
|
||||
|
||||
// These are the check ids/starting versions to exercise.
|
||||
// If unset, policy.DefaultChecks() are used.
|
||||
Checks []policy.Check
|
||||
|
||||
// ExemptClient is an optional client interface to exercise behavior of an exempt client.
|
||||
ExemptClient kubernetes.Interface
|
||||
// ExemptNamespaces are optional namespaces not expected to have PodSecurity controls enforced.
|
||||
ExemptNamespaces []string
|
||||
// ExemptRuntimeClasses are optional runtimeclasses not expected to have PodSecurity controls enforced.
|
||||
ExemptRuntimeClasses []string
|
||||
}
|
||||
|
||||
func toJSON(pod *corev1.Pod) string {
|
||||
data, _ := json.Marshal(pod)
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// checksForLevelAndVersion returns the set of check IDs that apply when evaluating the given level and version.
|
||||
// checks are assumed to be well-formed and valid to pass to policy.NewEvaluator().
|
||||
// level must be api.LevelRestricted or api.LevelBaseline
|
||||
func checksForLevelAndVersion(checks []policy.Check, level api.Level, version api.Version) ([]string, error) {
|
||||
retval := []string{}
|
||||
for _, check := range checks {
|
||||
if !version.Older(check.Versions[0].MinimumVersion) && (level == check.Level || level == api.LevelRestricted) {
|
||||
retval = append(retval, check.ID)
|
||||
}
|
||||
}
|
||||
return retval, nil
|
||||
}
|
||||
|
||||
// maxMinorVersionToTest returns the maximum minor version to exercise for a given set of checks.
|
||||
// checks are assumed to be well-formed and valid to pass to policy.NewEvaluator().
|
||||
func maxMinorVersionToTest(checks []policy.Check) (int, error) {
|
||||
// start with the release under development (1.22 at time of writing).
|
||||
// this can be incremented to the current version whenever is convenient.
|
||||
maxTestMinor := 22
|
||||
for _, check := range checks {
|
||||
lastCheckVersion := check.Versions[len(check.Versions)-1].MinimumVersion
|
||||
if lastCheckVersion.Major() != 1 {
|
||||
return 0, fmt.Errorf("expected major version 1, got %d", lastCheckVersion.Major())
|
||||
}
|
||||
if lastCheckVersion.Minor() > maxTestMinor {
|
||||
maxTestMinor = lastCheckVersion.Minor()
|
||||
}
|
||||
}
|
||||
return maxTestMinor, nil
|
||||
}
|
||||
|
||||
type testWarningHandler struct {
|
||||
lock sync.Mutex
|
||||
warnings []string
|
||||
}
|
||||
|
||||
func (t *testWarningHandler) HandleWarningHeader(code int, agent string, warning string) {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
t.warnings = append(t.warnings, warning)
|
||||
}
|
||||
func (t *testWarningHandler) FlushWarnings() []string {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
warnings := t.warnings
|
||||
t.warnings = nil
|
||||
return warnings
|
||||
}
|
||||
|
||||
// and ensures pod fixtures expected to pass and fail against that level/version work as expected.
|
||||
func Run(t *testing.T, opts Options) {
|
||||
warningHandler := &testWarningHandler{}
|
||||
|
||||
configCopy := rest.CopyConfig(opts.ClientConfig)
|
||||
configCopy.WarningHandler = warningHandler
|
||||
client, err := kubernetes.NewForConfig(configCopy)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating client: %v", err)
|
||||
}
|
||||
|
||||
if opts.CreateNamespace == nil {
|
||||
opts.CreateNamespace = DefaultCreateNamespace
|
||||
}
|
||||
if len(opts.Checks) == 0 {
|
||||
opts.Checks = policy.DefaultChecks()
|
||||
}
|
||||
_, err = policy.NewEvaluator(opts.Checks)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid checks: %v", err)
|
||||
}
|
||||
maxMinor, err := maxMinorVersionToTest(opts.Checks)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid checks: %v", err)
|
||||
}
|
||||
|
||||
for _, level := range []api.Level{api.LevelBaseline, api.LevelRestricted} {
|
||||
for minor := 0; minor <= maxMinor; minor++ {
|
||||
version := api.MajorMinorVersion(1, minor)
|
||||
|
||||
// create test name
|
||||
ns := fmt.Sprintf("podsecurity-%s-1-%d", level, minor)
|
||||
|
||||
// create namespace
|
||||
_, err := opts.CreateNamespace(client, ns, map[string]string{
|
||||
api.EnforceLevelLabel: string(level),
|
||||
api.EnforceVersionLabel: fmt.Sprintf("v1.%d", minor),
|
||||
api.WarnLevelLabel: string(level),
|
||||
api.WarnVersionLabel: fmt.Sprintf("v1.%d", minor),
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("failed creating namespace %s: %v", ns, err)
|
||||
continue
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
client.CoreV1().Namespaces().Delete(context.Background(), ns, metav1.DeleteOptions{})
|
||||
})
|
||||
|
||||
// create service account (to allow pod to pass serviceaccount admission)
|
||||
sa, err := client.CoreV1().ServiceAccounts(ns).Create(
|
||||
context.Background(),
|
||||
&corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: "default"}},
|
||||
metav1.CreateOptions{},
|
||||
)
|
||||
if err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
t.Errorf("failed creating serviceaccount %s: %v", ns, err)
|
||||
continue
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
client.CoreV1().ServiceAccounts(ns).Delete(context.Background(), sa.Name, metav1.DeleteOptions{})
|
||||
})
|
||||
|
||||
// create pod
|
||||
createPod := func(t *testing.T, i int, pod *corev1.Pod, expectSuccess bool, expectErrorSubstring string) {
|
||||
t.Helper()
|
||||
// avoid mutating original pod fixture
|
||||
pod = pod.DeepCopy()
|
||||
// assign pod name and serviceaccount
|
||||
pod.Name = "test"
|
||||
pod.Spec.ServiceAccountName = "default"
|
||||
// dry-run create
|
||||
_, err := client.CoreV1().Pods(ns).Create(context.Background(), pod, metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}})
|
||||
if !expectSuccess {
|
||||
if err == nil {
|
||||
t.Errorf("%d: expected error creating %s, got none", i, toJSON(pod))
|
||||
return
|
||||
}
|
||||
if strings.Contains(err.Error(), policy.UnknownForbiddenReason) {
|
||||
t.Errorf("%d: unexpected unknown forbidden reason creating %s: %v", i, toJSON(pod), err)
|
||||
return
|
||||
}
|
||||
if !strings.Contains(err.Error(), expectErrorSubstring) {
|
||||
t.Errorf("%d: expected error with substring %q, got %v", i, expectErrorSubstring, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if expectSuccess && err != nil {
|
||||
t.Errorf("%d: unexpected error creating %s: %v", i, toJSON(pod), err)
|
||||
}
|
||||
}
|
||||
|
||||
// create controller
|
||||
createController := func(t *testing.T, i int, pod *corev1.Pod, expectSuccess bool, expectErrorSubstring string) {
|
||||
t.Helper()
|
||||
// avoid mutating original pod fixture
|
||||
pod = pod.DeepCopy()
|
||||
if pod.Labels == nil {
|
||||
pod.Labels = map[string]string{}
|
||||
}
|
||||
pod.Labels["test"] = "true"
|
||||
|
||||
warningHandler.FlushWarnings()
|
||||
// dry-run create
|
||||
deployment := &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"test": "true"}},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: pod.ObjectMeta,
|
||||
Spec: pod.Spec,
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := client.AppsV1().Deployments(ns).Create(context.Background(), deployment, metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}})
|
||||
if err != nil {
|
||||
t.Errorf("%d: unexpected error creating controller with %s: %v", i, toJSON(pod), err)
|
||||
return
|
||||
}
|
||||
warningText := strings.Join(warningHandler.FlushWarnings(), "; ")
|
||||
if !expectSuccess {
|
||||
if len(warningText) == 0 {
|
||||
t.Errorf("%d: expected warnings creating %s, got none", i, toJSON(pod))
|
||||
return
|
||||
}
|
||||
if strings.Contains(warningText, policy.UnknownForbiddenReason) {
|
||||
t.Errorf("%d: unexpected unknown forbidden reason creating %s: %v", i, toJSON(pod), warningText)
|
||||
return
|
||||
}
|
||||
if !strings.Contains(warningText, expectErrorSubstring) {
|
||||
t.Errorf("%d: expected warning with substring %q, got %v", i, expectErrorSubstring, warningText)
|
||||
return
|
||||
}
|
||||
}
|
||||
if expectSuccess && len(warningText) > 0 {
|
||||
t.Errorf("%d: unexpected warning creating %s: %v", i, toJSON(pod), warningText)
|
||||
}
|
||||
}
|
||||
|
||||
minimalValidPod, err := getMinimalValidPod(level, version)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Run(ns+"_pass_base", func(t *testing.T) {
|
||||
createPod(t, 0, minimalValidPod.DeepCopy(), true, "")
|
||||
createController(t, 0, minimalValidPod.DeepCopy(), true, "")
|
||||
})
|
||||
|
||||
checkIDs, err := checksForLevelAndVersion(opts.Checks, level, version)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(checkIDs) == 0 {
|
||||
t.Fatal(fmt.Errorf("no checks registered for %s/1.%d", level, minor))
|
||||
}
|
||||
for _, checkID := range checkIDs {
|
||||
checkData, err := getFixtures(fixtureKey{level: level, version: version, check: checkID})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Run(ns+"_pass_"+checkID, func(t *testing.T) {
|
||||
for i, pod := range checkData.pass {
|
||||
createPod(t, i, pod, true, "")
|
||||
createController(t, i, pod, true, "")
|
||||
}
|
||||
})
|
||||
t.Run(ns+"_fail_"+checkID, func(t *testing.T) {
|
||||
for i, pod := range checkData.fail {
|
||||
createPod(t, i, pod, false, checkData.expectErrorSubstring)
|
||||
createController(t, i, pod, false, checkData.expectErrorSubstring)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultCreateNamespace(client kubernetes.Interface, name string, labels map[string]string) (*corev1.Namespace, error) {
|
||||
return client.CoreV1().Namespaces().Create(
|
||||
context.Background(),
|
||||
&corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: name, Labels: labels},
|
||||
},
|
||||
metav1.CreateOptions{},
|
||||
)
|
||||
}
|
1
staging/src/k8s.io/pod-security-admission/test/testdata/README.md
vendored
Normal file
1
staging/src/k8s.io/pod-security-admission/test/testdata/README.md
vendored
Normal file
@ -0,0 +1 @@
|
||||
The fixtures in this folder are generated by TestFixtures.
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/addcapabilities0.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/addcapabilities0.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: addcapabilities0
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- NET_RAW
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
capabilities: {}
|
||||
securityContext: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/addcapabilities1.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/addcapabilities1.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: addcapabilities1
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
capabilities: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- NET_RAW
|
||||
securityContext: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/addcapabilities2.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/addcapabilities2.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: addcapabilities2
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- chown
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
capabilities: {}
|
||||
securityContext: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/addcapabilities3.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/addcapabilities3.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: addcapabilities3
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
capabilities: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- chown
|
||||
securityContext: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/addcapabilities4.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/addcapabilities4.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: addcapabilities4
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- bogus
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
capabilities: {}
|
||||
securityContext: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/addcapabilities5.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/addcapabilities5.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: addcapabilities5
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
capabilities: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- bogus
|
||||
securityContext: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/addcapabilities6.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/addcapabilities6.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: addcapabilities6
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- CAP_CHOWN
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
capabilities: {}
|
||||
securityContext: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/addcapabilities7.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/addcapabilities7.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: addcapabilities7
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
capabilities: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- CAP_CHOWN
|
||||
securityContext: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux0.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux0.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux0
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
type: somevalue
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux1.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux1.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux1
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
type: somevalue
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux2.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux2.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux2
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
type: somevalue
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux3.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux3.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux3
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
user: somevalue
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux4.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux4.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux4
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
user: somevalue
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux5.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux5.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux5
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
user: somevalue
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux6.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux6.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux6
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
role: somevalue
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux7.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux7.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux7
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
role: somevalue
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux8.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/fail/selinux8.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux8
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
role: somevalue
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
30
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/addcapabilities0.yaml
vendored
Executable file
30
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/addcapabilities0.yaml
vendored
Executable file
@ -0,0 +1,30 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: addcapabilities0
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- AUDIT_WRITE
|
||||
- CHOWN
|
||||
- DAC_OVERRIDE
|
||||
- FOWNER
|
||||
- FSETID
|
||||
- KILL
|
||||
- MKNOD
|
||||
- NET_BIND_SERVICE
|
||||
- SETFCAP
|
||||
- SETGID
|
||||
- SETPCAP
|
||||
- SETUID
|
||||
- SYS_CHROOT
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
capabilities: {}
|
||||
securityContext: {}
|
30
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/addcapabilities1.yaml
vendored
Executable file
30
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/addcapabilities1.yaml
vendored
Executable file
@ -0,0 +1,30 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: addcapabilities1
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
capabilities: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- AUDIT_WRITE
|
||||
- CHOWN
|
||||
- DAC_OVERRIDE
|
||||
- FOWNER
|
||||
- FSETID
|
||||
- KILL
|
||||
- MKNOD
|
||||
- NET_BIND_SERVICE
|
||||
- SETFCAP
|
||||
- SETGID
|
||||
- SETPCAP
|
||||
- SETUID
|
||||
- SYS_CHROOT
|
||||
securityContext: {}
|
11
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/base.yaml
vendored
Executable file
11
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/base.yaml
vendored
Executable file
@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: base
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
16
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux0.yaml
vendored
Executable file
16
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux0.yaml
vendored
Executable file
@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux0
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext: {}
|
16
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux1.yaml
vendored
Executable file
16
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux1.yaml
vendored
Executable file
@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux1
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux10.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux10.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux10
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
type: container_init_t
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux11.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux11.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux11
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
type: container_init_t
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux12.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux12.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux12
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
type: container_kvm_t
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux13.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux13.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux13
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
type: container_kvm_t
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux14.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux14.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux14
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
type: container_kvm_t
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
17
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux15.yaml
vendored
Executable file
17
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux15.yaml
vendored
Executable file
@ -0,0 +1,17 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux15
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
17
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux16.yaml
vendored
Executable file
17
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux16.yaml
vendored
Executable file
@ -0,0 +1,17 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux16
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
17
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux17.yaml
vendored
Executable file
17
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux17.yaml
vendored
Executable file
@ -0,0 +1,17 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux17
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux18.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux18.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux18
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
level: somevalue
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux19.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux19.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux19
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
level: somevalue
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
16
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux2.yaml
vendored
Executable file
16
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux2.yaml
vendored
Executable file
@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux2
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext: {}
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux20.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux20.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux20
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
level: somevalue
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
17
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux3.yaml
vendored
Executable file
17
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux3.yaml
vendored
Executable file
@ -0,0 +1,17 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux3
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
17
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux4.yaml
vendored
Executable file
17
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux4.yaml
vendored
Executable file
@ -0,0 +1,17 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux4
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
17
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux5.yaml
vendored
Executable file
17
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux5.yaml
vendored
Executable file
@ -0,0 +1,17 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux5
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux6.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux6.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux6
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
type: container_t
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux7.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux7.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux7
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
type: container_t
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux8.yaml
vendored
Executable file
18
staging/src/k8s.io/pod-security-admission/test/testdata/baseline/v1.0/pass/selinux8.yaml
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: selinux8
|
||||
spec:
|
||||
containers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: container1
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
||||
initContainers:
|
||||
- image: k8s.gcr.io/pause
|
||||
name: initcontainer1
|
||||
securityContext:
|
||||
seLinuxOptions:
|
||||
type: container_t
|
||||
securityContext:
|
||||
seLinuxOptions: {}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user