@@ -26,6 +26,7 @@ import (
26
26
"strings"
27
27
"time"
28
28
29
+ "k8s.io/api/core/v1"
29
30
"k8s.io/apimachinery/pkg/api/errors"
30
31
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31
32
"k8s.io/apimachinery/pkg/fields"
@@ -74,6 +75,8 @@ func (podStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
74
75
}
75
76
76
77
podutil .DropDisabledPodFields (pod , nil )
78
+
79
+ applySeccompVersionSkew (pod )
77
80
}
78
81
79
82
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
@@ -569,3 +572,129 @@ func validateContainer(container string, pod *api.Pod) (string, error) {
569
572
570
573
return container , nil
571
574
}
575
+
576
+ // applySeccompVersionSkew implements the version skew behavior described in:
577
+ // https://github.com/kubernetes/enhancements/blob/master/keps/sig-node/20190717-seccomp-ga.md#version-skew-strategy
578
+ func applySeccompVersionSkew (pod * api.Pod ) {
579
+ // get possible annotation and field
580
+ annotation , hasAnnotation := pod .Annotations [v1 .SeccompPodAnnotationKey ]
581
+ field , hasField := (* api .SeccompProfile )(nil ), false
582
+
583
+ if pod .Spec .SecurityContext != nil && pod .Spec .SecurityContext .SeccompProfile != nil {
584
+ field = pod .Spec .SecurityContext .SeccompProfile
585
+ hasField = true
586
+ }
587
+
588
+ // sync field and annotation
589
+ if hasField && ! hasAnnotation {
590
+ newAnnotation := seccompAnnotationForField (field )
591
+
592
+ if newAnnotation != "" {
593
+ if pod .Annotations == nil {
594
+ pod .Annotations = map [string ]string {}
595
+ }
596
+ pod .Annotations [v1 .SeccompPodAnnotationKey ] = newAnnotation
597
+ }
598
+ } else if hasAnnotation && ! hasField {
599
+ newField := seccompFieldForAnnotation (annotation )
600
+
601
+ if newField != nil {
602
+ if pod .Spec .SecurityContext == nil {
603
+ pod .Spec .SecurityContext = & api.PodSecurityContext {}
604
+ }
605
+ pod .Spec .SecurityContext .SeccompProfile = newField
606
+ }
607
+ }
608
+
609
+ // Handle the containers of the pod
610
+ podutil .VisitContainers (& pod .Spec , podutil .AllFeatureEnabledContainers (),
611
+ func (ctr * api.Container , _ podutil.ContainerType ) bool {
612
+ // get possible annotation and field
613
+ key := api .SeccompContainerAnnotationKeyPrefix + ctr .Name
614
+ annotation , hasAnnotation := pod .Annotations [key ]
615
+
616
+ field , hasField := (* api .SeccompProfile )(nil ), false
617
+ if ctr .SecurityContext != nil && ctr .SecurityContext .SeccompProfile != nil {
618
+ field = ctr .SecurityContext .SeccompProfile
619
+ hasField = true
620
+ }
621
+
622
+ // sync field and annotation
623
+ if hasField && ! hasAnnotation {
624
+ newAnnotation := seccompAnnotationForField (field )
625
+
626
+ if newAnnotation != "" {
627
+ if pod .Annotations == nil {
628
+ pod .Annotations = map [string ]string {}
629
+ }
630
+ pod .Annotations [key ] = newAnnotation
631
+ }
632
+ } else if hasAnnotation && ! hasField {
633
+ newField := seccompFieldForAnnotation (annotation )
634
+
635
+ if newField != nil {
636
+ if ctr .SecurityContext == nil {
637
+ ctr .SecurityContext = & api.SecurityContext {}
638
+ }
639
+ ctr .SecurityContext .SeccompProfile = newField
640
+ }
641
+ }
642
+
643
+ return true
644
+ })
645
+ }
646
+
647
+ // seccompFieldForAnnotation takes a pod seccomp profile field and returns the
648
+ // converted annotation value
649
+ func seccompAnnotationForField (field * api.SeccompProfile ) string {
650
+ // If only seccomp fields are specified, add the corresponding annotations.
651
+ // This ensures that the fields are enforced even if the node version
652
+ // trails the API version
653
+ switch field .Type {
654
+ case api .SeccompProfileTypeUnconfined :
655
+ return v1 .SeccompProfileNameUnconfined
656
+
657
+ case api .SeccompProfileTypeRuntimeDefault :
658
+ return v1 .SeccompProfileRuntimeDefault
659
+
660
+ case api .SeccompProfileTypeLocalhost :
661
+ if field .LocalhostProfile != nil {
662
+ return v1 .SeccompLocalhostProfileNamePrefix + * field .LocalhostProfile
663
+ }
664
+ }
665
+
666
+ // we can only reach this code path if the LocalhostProfile is nil but the
667
+ // provided field type is SeccompProfileTypeLocalhost or if an unrecognized
668
+ // type is specified
669
+ return ""
670
+ }
671
+
672
+ // seccompFieldForAnnotation takes a pod annotation and returns the converted
673
+ // seccomp profile field.
674
+ func seccompFieldForAnnotation (annotation string ) * api.SeccompProfile {
675
+ // If only seccomp annotations are specified, copy the values into the
676
+ // corresponding fields. This ensures that existing applications continue
677
+ // to enforce seccomp, and prevents the kubelet from needing to resolve
678
+ // annotations & fields.
679
+ if annotation == v1 .SeccompProfileNameUnconfined {
680
+ return & api.SeccompProfile {Type : api .SeccompProfileTypeUnconfined }
681
+ }
682
+
683
+ if annotation == api .SeccompProfileRuntimeDefault || annotation == api .DeprecatedSeccompProfileDockerDefault {
684
+ return & api.SeccompProfile {Type : api .SeccompProfileTypeRuntimeDefault }
685
+ }
686
+
687
+ if strings .HasPrefix (annotation , v1 .SeccompLocalhostProfileNamePrefix ) {
688
+ localhostProfile := strings .TrimPrefix (annotation , v1 .SeccompLocalhostProfileNamePrefix )
689
+ if localhostProfile != "" {
690
+ return & api.SeccompProfile {
691
+ Type : api .SeccompProfileTypeLocalhost ,
692
+ LocalhostProfile : & localhostProfile ,
693
+ }
694
+ }
695
+ }
696
+
697
+ // we can only reach this code path if the localhostProfile name has a zero
698
+ // length or if the annotation has an unrecognized value
699
+ return nil
700
+ }
0 commit comments