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  }