istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/kube/inject/openshift.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Functions below were copied from 16 // https://github.com/openshift/apiserver-library-go/blob/c22aa58bb57416b9f9f190957d07c9e7669c26df/pkg/securitycontextconstraints/sccmatching/matcher.go 17 // These functions are not exported, and, if they were, when imported bring k8s.io/kubernetes as dependency, which is problematic 18 // License is Apache 2.0: https://github.com/openshift/apiserver-library-go/blob/c22aa58bb57416b9f9f190957d07c9e7669c26df/LICENSE 19 20 package inject 21 22 import ( 23 "fmt" 24 "strings" 25 26 securityv1 "github.com/openshift/api/security/v1" 27 corev1 "k8s.io/api/core/v1" 28 29 "istio.io/istio/pkg/log" 30 ) 31 32 // getPreallocatedUIDRange retrieves the annotated value from the namespace, splits it to make 33 // the min/max and formats the data into the necessary types for the strategy options. 34 func getPreallocatedUIDRange(ns *corev1.Namespace) (*int64, *int64, error) { 35 annotationVal, ok := ns.Annotations[securityv1.UIDRangeAnnotation] 36 if !ok { 37 return nil, nil, fmt.Errorf("unable to find annotation %s", securityv1.UIDRangeAnnotation) 38 } 39 if len(annotationVal) == 0 { 40 return nil, nil, fmt.Errorf("found annotation %s but it was empty", securityv1.UIDRangeAnnotation) 41 } 42 uidBlock, err := ParseBlock(annotationVal) 43 if err != nil { 44 return nil, nil, err 45 } 46 47 min := int64(uidBlock.Start) 48 max := int64(uidBlock.End) 49 log.Debugf("got preallocated values for min: %d, max: %d for uid range in namespace %s", min, max, ns.Name) 50 return &min, &max, nil 51 } 52 53 // getPreallocatedSupplementalGroups gets the annotated value from the namespace. 54 func getPreallocatedSupplementalGroups(ns *corev1.Namespace) ([]securityv1.IDRange, error) { 55 groups, err := getSupplementalGroupsAnnotation(ns) 56 if err != nil { 57 return nil, err 58 } 59 log.Debugf("got preallocated value for groups: %s in namespace %s", groups, ns.Name) 60 61 blocks, err := parseSupplementalGroupAnnotation(groups) 62 if err != nil { 63 return nil, err 64 } 65 66 idRanges := []securityv1.IDRange{} 67 for _, block := range blocks { 68 rng := securityv1.IDRange{ 69 Min: int64(block.Start), 70 Max: int64(block.End), 71 } 72 idRanges = append(idRanges, rng) 73 } 74 return idRanges, nil 75 } 76 77 // getSupplementalGroupsAnnotation provides a backwards compatible way to get supplemental groups 78 // annotations from a namespace by looking for SupplementalGroupsAnnotation and falling back to 79 // UIDRangeAnnotation if it is not found. 80 func getSupplementalGroupsAnnotation(ns *corev1.Namespace) (string, error) { 81 groups, ok := ns.Annotations[securityv1.SupplementalGroupsAnnotation] 82 if !ok { 83 log.Debugf("unable to find supplemental group annotation %s falling back to %s", securityv1.SupplementalGroupsAnnotation, securityv1.UIDRangeAnnotation) 84 85 groups, ok = ns.Annotations[securityv1.UIDRangeAnnotation] 86 if !ok { 87 return "", fmt.Errorf("unable to find supplemental group or uid annotation for namespace %s", ns.Name) 88 } 89 } 90 91 if len(groups) == 0 { 92 return "", fmt.Errorf("unable to find groups using %s and %s annotations", securityv1.SupplementalGroupsAnnotation, securityv1.UIDRangeAnnotation) 93 } 94 return groups, nil 95 } 96 97 // parseSupplementalGroupAnnotation parses the group annotation into blocks. 98 func parseSupplementalGroupAnnotation(groups string) ([]Block, error) { 99 blocks := []Block{} 100 segments := strings.Split(groups, ",") 101 for _, segment := range segments { 102 block, err := ParseBlock(segment) 103 if err != nil { 104 return nil, err 105 } 106 blocks = append(blocks, block) 107 } 108 if len(blocks) == 0 { 109 return nil, fmt.Errorf("no blocks parsed from annotation %s", groups) 110 } 111 return blocks, nil 112 } 113 114 // Functions below were copied from 115 // https://github.com/openshift/library-go/blob/561433066966536ac17f3c9852d7d85f7b7e1e36/pkg/security/uid/uid.go 116 // Copied here to avoid bringing tons of dependencies 117 // License is Apache 2.0: https://github.com/openshift/library-go/blob/561433066966536ac17f3c9852d7d85f7b7e1e36/LICENSE 118 119 type Block struct { 120 Start uint32 121 End uint32 122 } 123 124 var ( 125 ErrBlockSlashBadFormat = fmt.Errorf("block not in the format \"<start>/<size>\"") 126 ErrBlockDashBadFormat = fmt.Errorf("block not in the format \"<start>-<end>\"") 127 ) 128 129 func ParseBlock(in string) (Block, error) { 130 if strings.Contains(in, "/") { 131 var start, size uint32 132 n, err := fmt.Sscanf(in, "%d/%d", &start, &size) 133 if err != nil { 134 return Block{}, err 135 } 136 if n != 2 { 137 return Block{}, ErrBlockSlashBadFormat 138 } 139 return Block{Start: start, End: start + size - 1}, nil 140 } 141 142 var start, end uint32 143 n, err := fmt.Sscanf(in, "%d-%d", &start, &end) 144 if err != nil { 145 return Block{}, err 146 } 147 if n != 2 { 148 return Block{}, ErrBlockDashBadFormat 149 } 150 return Block{Start: start, End: end}, nil 151 } 152 153 func (b Block) String() string { 154 return fmt.Sprintf("%d/%d", b.Start, b.Size()) 155 } 156 157 func (b Block) RangeString() string { 158 return fmt.Sprintf("%d-%d", b.Start, b.End) 159 } 160 161 func (b Block) Size() uint32 { 162 return b.End - b.Start + 1 163 }