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 }