mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 05:40:42 +00:00 
			
		
		
		
	With the graduation of seccomp to GA we automatically convert the deprecated seccomp profile annotation `docker/default` to `runtime/default`. This means that we now have to automatically allow `runtime/default` if a user specifies `docker/default` and vice versa in an allowed PSP seccomp profile. Signed-off-by: Sascha Grunert <sgrunert@suse.com>
		
			
				
	
	
		
			179 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			179 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2016 The Kubernetes Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package seccomp
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| 
 | |
| 	v1 "k8s.io/api/core/v1"
 | |
| 	"k8s.io/apimachinery/pkg/util/validation/field"
 | |
| 	podutil "k8s.io/kubernetes/pkg/api/pod"
 | |
| 	api "k8s.io/kubernetes/pkg/apis/core"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	// AllowAny is the wildcard used to allow any profile.
 | |
| 	AllowAny = "*"
 | |
| 	// DefaultProfileAnnotationKey specifies the default seccomp profile.
 | |
| 	DefaultProfileAnnotationKey = "seccomp.security.alpha.kubernetes.io/defaultProfileName"
 | |
| 	// AllowedProfilesAnnotationKey specifies the allowed seccomp profiles.
 | |
| 	AllowedProfilesAnnotationKey = "seccomp.security.alpha.kubernetes.io/allowedProfileNames"
 | |
| )
 | |
| 
 | |
| // Strategy defines the interface for all seccomp constraint strategies.
 | |
| type Strategy interface {
 | |
| 	// Generate returns a profile based on constraint rules.
 | |
| 	Generate(annotations map[string]string, pod *api.Pod) (string, error)
 | |
| 	// Validate ensures that the specified values fall within the range of the strategy.
 | |
| 	ValidatePod(pod *api.Pod) field.ErrorList
 | |
| 	// Validate ensures that the specified values fall within the range of the strategy.
 | |
| 	ValidateContainer(pod *api.Pod, container *api.Container) field.ErrorList
 | |
| }
 | |
| 
 | |
| type strategy struct {
 | |
| 	defaultProfile  string
 | |
| 	allowedProfiles map[string]bool
 | |
| 	// For printing error messages (preserves order).
 | |
| 	allowedProfilesString string
 | |
| 	// does the strategy allow any profile (wildcard)
 | |
| 	allowAnyProfile bool
 | |
| }
 | |
| 
 | |
| var _ Strategy = &strategy{}
 | |
| 
 | |
| // NewStrategy creates a new strategy that enforces seccomp profile constraints.
 | |
| func NewStrategy(pspAnnotations map[string]string) Strategy {
 | |
| 	var allowedProfiles map[string]bool
 | |
| 	allowAnyProfile := false
 | |
| 	if allowed, ok := pspAnnotations[AllowedProfilesAnnotationKey]; ok {
 | |
| 		profiles := strings.Split(allowed, ",")
 | |
| 		allowedProfiles = make(map[string]bool, len(profiles))
 | |
| 		for _, p := range profiles {
 | |
| 			if p == AllowAny {
 | |
| 				allowAnyProfile = true
 | |
| 				continue
 | |
| 			}
 | |
| 			// With the graduation of seccomp to GA we automatically convert
 | |
| 			// the deprecated seccomp profile annotation `docker/default` to
 | |
| 			// `runtime/default`. This means that we now have to automatically
 | |
| 			// allow `runtime/default` if a user specifies `docker/default` and
 | |
| 			// vice versa in a PSP.
 | |
| 			if p == v1.DeprecatedSeccompProfileDockerDefault || p == v1.SeccompProfileRuntimeDefault {
 | |
| 				allowedProfiles[v1.SeccompProfileRuntimeDefault] = true
 | |
| 				allowedProfiles[v1.DeprecatedSeccompProfileDockerDefault] = true
 | |
| 			}
 | |
| 			allowedProfiles[p] = true
 | |
| 		}
 | |
| 	}
 | |
| 	return &strategy{
 | |
| 		defaultProfile:        pspAnnotations[DefaultProfileAnnotationKey],
 | |
| 		allowedProfiles:       allowedProfiles,
 | |
| 		allowedProfilesString: pspAnnotations[AllowedProfilesAnnotationKey],
 | |
| 		allowAnyProfile:       allowAnyProfile,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Generate returns a profile based on constraint rules.
 | |
| func (s *strategy) Generate(annotations map[string]string, pod *api.Pod) (string, error) {
 | |
| 	if annotations[api.SeccompPodAnnotationKey] != "" {
 | |
| 		// Profile already set, nothing to do.
 | |
| 		return annotations[api.SeccompPodAnnotationKey], nil
 | |
| 	}
 | |
| 	if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.SeccompProfile != nil {
 | |
| 		// Profile field already set, translate to annotation
 | |
| 		return podutil.SeccompAnnotationForField(pod.Spec.SecurityContext.SeccompProfile), nil
 | |
| 	}
 | |
| 	return s.defaultProfile, nil
 | |
| }
 | |
| 
 | |
| // ValidatePod ensures that the specified values on the pod fall within the range
 | |
| // of the strategy.
 | |
| func (s *strategy) ValidatePod(pod *api.Pod) field.ErrorList {
 | |
| 	allErrs := field.ErrorList{}
 | |
| 	podSpecFieldPath := field.NewPath("pod", "metadata", "annotations").Key(api.SeccompPodAnnotationKey)
 | |
| 	podProfile := pod.Annotations[api.SeccompPodAnnotationKey]
 | |
| 	// if the annotation is not set, see if the field is set and derive the corresponding annotation value
 | |
| 	if len(podProfile) == 0 && pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.SeccompProfile != nil {
 | |
| 		podProfile = podutil.SeccompAnnotationForField(pod.Spec.SecurityContext.SeccompProfile)
 | |
| 	}
 | |
| 
 | |
| 	if !s.allowAnyProfile && len(s.allowedProfiles) == 0 && podProfile != "" {
 | |
| 		allErrs = append(allErrs, field.Forbidden(podSpecFieldPath, "seccomp may not be set"))
 | |
| 		return allErrs
 | |
| 	}
 | |
| 
 | |
| 	if !s.profileAllowed(podProfile) {
 | |
| 		msg := fmt.Sprintf("%s is not an allowed seccomp profile. Valid values are %v", podProfile, s.allowedProfilesString)
 | |
| 		allErrs = append(allErrs, field.Forbidden(podSpecFieldPath, msg))
 | |
| 	}
 | |
| 
 | |
| 	return allErrs
 | |
| }
 | |
| 
 | |
| // ValidateContainer ensures that the specified values on the container fall within
 | |
| // the range of the strategy.
 | |
| func (s *strategy) ValidateContainer(pod *api.Pod, container *api.Container) field.ErrorList {
 | |
| 	allErrs := field.ErrorList{}
 | |
| 	fieldPath := field.NewPath("pod", "metadata", "annotations").Key(api.SeccompContainerAnnotationKeyPrefix + container.Name)
 | |
| 	containerProfile := profileForContainer(pod, container)
 | |
| 
 | |
| 	if !s.allowAnyProfile && len(s.allowedProfiles) == 0 && containerProfile != "" {
 | |
| 		allErrs = append(allErrs, field.Forbidden(fieldPath, "seccomp may not be set"))
 | |
| 		return allErrs
 | |
| 	}
 | |
| 
 | |
| 	if !s.profileAllowed(containerProfile) {
 | |
| 		msg := fmt.Sprintf("%s is not an allowed seccomp profile. Valid values are %v", containerProfile, s.allowedProfilesString)
 | |
| 		allErrs = append(allErrs, field.Forbidden(fieldPath, msg))
 | |
| 	}
 | |
| 
 | |
| 	return allErrs
 | |
| }
 | |
| 
 | |
| // profileAllowed checks if profile is in allowedProfiles or if allowedProfiles
 | |
| // contains the wildcard.
 | |
| func (s *strategy) profileAllowed(profile string) bool {
 | |
| 	// for backwards compatibility and PSPs without a defined list of allowed profiles.
 | |
| 	// If a PSP does not have allowedProfiles set then we should allow an empty profile.
 | |
| 	// This will mean that the runtime default is used.
 | |
| 	if len(s.allowedProfiles) == 0 && profile == "" {
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	return s.allowAnyProfile || s.allowedProfiles[profile]
 | |
| }
 | |
| 
 | |
| // profileForContainer returns the container profile if set, otherwise the pod profile.
 | |
| func profileForContainer(pod *api.Pod, container *api.Container) string {
 | |
| 	if container.SecurityContext != nil && container.SecurityContext.SeccompProfile != nil {
 | |
| 		// derive the annotation value from the container field
 | |
| 		return podutil.SeccompAnnotationForField(container.SecurityContext.SeccompProfile)
 | |
| 	}
 | |
| 	containerProfile, ok := pod.Annotations[api.SeccompContainerAnnotationKeyPrefix+container.Name]
 | |
| 	if ok {
 | |
| 		// return the existing container annotation
 | |
| 		return containerProfile
 | |
| 	}
 | |
| 	if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.SeccompProfile != nil {
 | |
| 		// derive the annotation value from the pod field
 | |
| 		return podutil.SeccompAnnotationForField(pod.Spec.SecurityContext.SeccompProfile)
 | |
| 	}
 | |
| 	// return the existing pod annotation
 | |
| 	return pod.Annotations[api.SeccompPodAnnotationKey]
 | |
| }
 |