k8s.io/apiserver@v0.31.1/plugin/pkg/audit/truncate/truncate.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes 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 truncate
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"k8s.io/apimachinery/pkg/runtime"
    23  	"k8s.io/apimachinery/pkg/runtime/schema"
    24  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    25  	auditinternal "k8s.io/apiserver/pkg/apis/audit"
    26  	"k8s.io/apiserver/pkg/audit"
    27  )
    28  
    29  const (
    30  	// PluginName is the name reported in error metrics.
    31  	PluginName = "truncate"
    32  
    33  	// annotationKey defines the name of the annotation used to indicate truncation.
    34  	annotationKey = "audit.k8s.io/truncated"
    35  	// annotationValue defines the value of the annotation used to indicate truncation.
    36  	annotationValue = "true"
    37  )
    38  
    39  // Config represents truncating backend configuration.
    40  type Config struct {
    41  	// MaxEventSize defines max allowed size of the event. If the event is larger,
    42  	// truncating will be performed.
    43  	MaxEventSize int64
    44  
    45  	// MaxBatchSize defined max allowed size of the batch of events, passed to the backend.
    46  	// If the total size of the batch is larger than this number, batch will be split. Actual
    47  	// size of the serialized request might be slightly higher, on the order of hundreds of bytes.
    48  	MaxBatchSize int64
    49  }
    50  
    51  type backend struct {
    52  	// The delegate backend that actually exports events.
    53  	delegateBackend audit.Backend
    54  
    55  	// Configuration used for truncation.
    56  	c Config
    57  
    58  	// Encoder used to calculate audit event sizes.
    59  	e runtime.Encoder
    60  }
    61  
    62  var _ audit.Backend = &backend{}
    63  
    64  // NewBackend returns a new truncating backend, using configuration passed in the parameters.
    65  // Truncate backend automatically runs and shut downs the delegate backend.
    66  func NewBackend(delegateBackend audit.Backend, config Config, groupVersion schema.GroupVersion) audit.Backend {
    67  	return &backend{
    68  		delegateBackend: delegateBackend,
    69  		c:               config,
    70  		e:               audit.Codecs.LegacyCodec(groupVersion),
    71  	}
    72  }
    73  
    74  func (b *backend) ProcessEvents(events ...*auditinternal.Event) bool {
    75  	var errors []error
    76  	var impacted []*auditinternal.Event
    77  	var batch []*auditinternal.Event
    78  	var batchSize int64
    79  	success := true
    80  	for _, event := range events {
    81  		size, err := b.calcSize(event)
    82  		// If event was correctly serialized, but the size is more than allowed
    83  		// and it makes sense to do trimming, i.e. there's a request and/or
    84  		// response present, try to strip away request and response.
    85  		if err == nil && size > b.c.MaxEventSize && event.Level.GreaterOrEqual(auditinternal.LevelRequest) {
    86  			event = truncate(event)
    87  			size, err = b.calcSize(event)
    88  		}
    89  		if err != nil {
    90  			errors = append(errors, err)
    91  			impacted = append(impacted, event)
    92  			continue
    93  		}
    94  		if size > b.c.MaxEventSize {
    95  			errors = append(errors, fmt.Errorf("event is too large even after truncating"))
    96  			impacted = append(impacted, event)
    97  			continue
    98  		}
    99  
   100  		if len(batch) > 0 && batchSize+size > b.c.MaxBatchSize {
   101  			success = b.delegateBackend.ProcessEvents(batch...) && success
   102  			batch = []*auditinternal.Event{}
   103  			batchSize = 0
   104  		}
   105  
   106  		batchSize += size
   107  		batch = append(batch, event)
   108  	}
   109  
   110  	if len(batch) > 0 {
   111  		success = b.delegateBackend.ProcessEvents(batch...) && success
   112  	}
   113  
   114  	if len(impacted) > 0 {
   115  		audit.HandlePluginError(PluginName, utilerrors.NewAggregate(errors), impacted...)
   116  	}
   117  	return success
   118  }
   119  
   120  // truncate removed request and response objects from the audit events,
   121  // to try and keep at least metadata.
   122  func truncate(e *auditinternal.Event) *auditinternal.Event {
   123  	// Make a shallow copy to avoid copying response/request objects.
   124  	newEvent := &auditinternal.Event{}
   125  	*newEvent = *e
   126  
   127  	newEvent.RequestObject = nil
   128  	newEvent.ResponseObject = nil
   129  
   130  	if newEvent.Annotations == nil {
   131  		newEvent.Annotations = make(map[string]string)
   132  	}
   133  	newEvent.Annotations[annotationKey] = annotationValue
   134  
   135  	return newEvent
   136  }
   137  
   138  func (b *backend) Run(stopCh <-chan struct{}) error {
   139  	return b.delegateBackend.Run(stopCh)
   140  }
   141  
   142  func (b *backend) Shutdown() {
   143  	b.delegateBackend.Shutdown()
   144  }
   145  
   146  func (b *backend) calcSize(e *auditinternal.Event) (int64, error) {
   147  	s := &sizer{}
   148  	if err := b.e.Encode(e, s); err != nil {
   149  		return 0, err
   150  	}
   151  	return s.Size, nil
   152  }
   153  
   154  func (b *backend) String() string {
   155  	return fmt.Sprintf("%s<%s>", PluginName, b.delegateBackend)
   156  }
   157  
   158  type sizer struct {
   159  	Size int64
   160  }
   161  
   162  func (s *sizer) Write(p []byte) (n int, err error) {
   163  	s.Size += int64(len(p))
   164  	return len(p), nil
   165  }