github.com/oam-dev/kubevela@v1.9.11/pkg/resourcekeeper/admission.go (about)

     1  /*
     2  Copyright 2021 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package resourcekeeper
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  
    24  	"github.com/pkg/errors"
    25  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    26  
    27  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    28  )
    29  
    30  var (
    31  	// AllowCrossNamespaceResource indicates whether application can apply resources into other namespaces
    32  	AllowCrossNamespaceResource = true
    33  	// AllowResourceTypes if not empty, application can only apply resources with specified types
    34  	AllowResourceTypes = ""
    35  )
    36  
    37  // AdmissionCheck check whether resources dispatch/deletion is admitted
    38  func (h *resourceKeeper) AdmissionCheck(ctx context.Context, manifests []*unstructured.Unstructured) error {
    39  	for _, handler := range []ResourceAdmissionHandler{
    40  		&NamespaceAdmissionHandler{app: h.app},
    41  		&ResourceTypeAdmissionHandler{},
    42  	} {
    43  		if err := handler.Validate(ctx, manifests); err != nil {
    44  			return err
    45  		}
    46  	}
    47  	return nil
    48  }
    49  
    50  // ResourceAdmissionHandler defines the handler to validate the admission of resource operation
    51  type ResourceAdmissionHandler interface {
    52  	Validate(ctx context.Context, manifests []*unstructured.Unstructured) error
    53  }
    54  
    55  // NamespaceAdmissionHandler defines the handler to validate if the resource namespace is valid to be dispatch/delete
    56  type NamespaceAdmissionHandler struct {
    57  	app *v1beta1.Application
    58  }
    59  
    60  // Validate check if cross namespace is available
    61  func (h *NamespaceAdmissionHandler) Validate(_ context.Context, manifests []*unstructured.Unstructured) error {
    62  	if !AllowCrossNamespaceResource {
    63  		for _, manifest := range manifests {
    64  			if manifest.GetNamespace() != h.app.GetNamespace() {
    65  				return errors.Errorf("forbidden resource: %s %s/%s is outside the namespace of application", manifest.GetKind(), manifest.GetNamespace(), manifest.GetName())
    66  			}
    67  		}
    68  	}
    69  	return nil
    70  }
    71  
    72  // ResourceTypeAdmissionHandler defines the handler to validate if the resource type is valid to be dispatch/delete
    73  type ResourceTypeAdmissionHandler struct {
    74  	initialized     bool
    75  	isWhiteList     bool
    76  	resourceTypeMap map[string]struct{}
    77  }
    78  
    79  // Validate check if resource type is valid
    80  func (h *ResourceTypeAdmissionHandler) Validate(_ context.Context, manifests []*unstructured.Unstructured) error {
    81  	if AllowResourceTypes != "" {
    82  		if !h.initialized {
    83  			h.initialized = true
    84  			h.resourceTypeMap = make(map[string]struct{})
    85  			if strings.HasPrefix(AllowResourceTypes, "whitelist:") {
    86  				for _, t := range strings.Split(strings.TrimPrefix(AllowResourceTypes, "whitelist:"), ",") {
    87  					h.isWhiteList = true
    88  					h.resourceTypeMap[t] = struct{}{}
    89  				}
    90  			}
    91  			if strings.HasPrefix(AllowResourceTypes, "blacklist:") {
    92  				for _, t := range strings.Split(strings.TrimPrefix(AllowResourceTypes, "blacklist:"), ",") {
    93  					h.isWhiteList = false
    94  					h.resourceTypeMap[t] = struct{}{}
    95  				}
    96  			}
    97  		}
    98  		for _, manifest := range manifests {
    99  			gvk := manifest.GetObjectKind().GroupVersionKind()
   100  			resourceType := fmt.Sprintf("%s.%s", manifest.GetKind(), gvk.Version)
   101  			if gvk.Group != "" {
   102  				resourceType += "." + gvk.Group
   103  			}
   104  			_, found := h.resourceTypeMap[resourceType]
   105  			if h.isWhiteList != found {
   106  				return errors.Errorf("forbidden resource: type (%s) of resource %s/%s is not allowed", manifest.GetKind(), manifest.GetNamespace(), manifest.GetName())
   107  			}
   108  		}
   109  	}
   110  	return nil
   111  }