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

     1  /*
     2  Copyright 2017 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 audit
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"fmt"
    23  	"net/http"
    24  	"time"
    25  
    26  	authnv1 "k8s.io/api/authentication/v1"
    27  	"k8s.io/apimachinery/pkg/api/meta"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/runtime"
    30  	"k8s.io/apimachinery/pkg/runtime/schema"
    31  	utilnet "k8s.io/apimachinery/pkg/util/net"
    32  	auditinternal "k8s.io/apiserver/pkg/apis/audit"
    33  	"k8s.io/apiserver/pkg/authentication/user"
    34  	"k8s.io/apiserver/pkg/authorization/authorizer"
    35  	"k8s.io/klog/v2"
    36  )
    37  
    38  const (
    39  	maxUserAgentLength      = 1024
    40  	userAgentTruncateSuffix = "...TRUNCATED"
    41  )
    42  
    43  func LogRequestMetadata(ctx context.Context, req *http.Request, requestReceivedTimestamp time.Time, level auditinternal.Level, attribs authorizer.Attributes) {
    44  	ac := AuditContextFrom(ctx)
    45  	if !ac.Enabled() {
    46  		return
    47  	}
    48  	ev := &ac.Event
    49  
    50  	ev.RequestReceivedTimestamp = metav1.NewMicroTime(requestReceivedTimestamp)
    51  	ev.Verb = attribs.GetVerb()
    52  	ev.RequestURI = req.URL.RequestURI()
    53  	ev.UserAgent = maybeTruncateUserAgent(req)
    54  	ev.Level = level
    55  
    56  	ips := utilnet.SourceIPs(req)
    57  	ev.SourceIPs = make([]string, len(ips))
    58  	for i := range ips {
    59  		ev.SourceIPs[i] = ips[i].String()
    60  	}
    61  
    62  	if user := attribs.GetUser(); user != nil {
    63  		ev.User.Username = user.GetName()
    64  		ev.User.Extra = map[string]authnv1.ExtraValue{}
    65  		for k, v := range user.GetExtra() {
    66  			ev.User.Extra[k] = authnv1.ExtraValue(v)
    67  		}
    68  		ev.User.Groups = user.GetGroups()
    69  		ev.User.UID = user.GetUID()
    70  	}
    71  
    72  	if attribs.IsResourceRequest() {
    73  		ev.ObjectRef = &auditinternal.ObjectReference{
    74  			Namespace:   attribs.GetNamespace(),
    75  			Name:        attribs.GetName(),
    76  			Resource:    attribs.GetResource(),
    77  			Subresource: attribs.GetSubresource(),
    78  			APIGroup:    attribs.GetAPIGroup(),
    79  			APIVersion:  attribs.GetAPIVersion(),
    80  		}
    81  	}
    82  }
    83  
    84  // LogImpersonatedUser fills in the impersonated user attributes into an audit event.
    85  func LogImpersonatedUser(ae *auditinternal.Event, user user.Info) {
    86  	if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
    87  		return
    88  	}
    89  	ae.ImpersonatedUser = &authnv1.UserInfo{
    90  		Username: user.GetName(),
    91  	}
    92  	ae.ImpersonatedUser.Groups = user.GetGroups()
    93  	ae.ImpersonatedUser.UID = user.GetUID()
    94  	ae.ImpersonatedUser.Extra = map[string]authnv1.ExtraValue{}
    95  	for k, v := range user.GetExtra() {
    96  		ae.ImpersonatedUser.Extra[k] = authnv1.ExtraValue(v)
    97  	}
    98  }
    99  
   100  // LogRequestObject fills in the request object into an audit event. The passed runtime.Object
   101  // will be converted to the given gv.
   102  func LogRequestObject(ctx context.Context, obj runtime.Object, objGV schema.GroupVersion, gvr schema.GroupVersionResource, subresource string, s runtime.NegotiatedSerializer) {
   103  	ae := AuditEventFrom(ctx)
   104  	if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
   105  		return
   106  	}
   107  
   108  	// complete ObjectRef
   109  	if ae.ObjectRef == nil {
   110  		ae.ObjectRef = &auditinternal.ObjectReference{}
   111  	}
   112  
   113  	// meta.Accessor is more general than ObjectMetaAccessor, but if it fails, we can just skip setting these bits
   114  	if meta, err := meta.Accessor(obj); err == nil {
   115  		if len(ae.ObjectRef.Namespace) == 0 {
   116  			ae.ObjectRef.Namespace = meta.GetNamespace()
   117  		}
   118  		if len(ae.ObjectRef.Name) == 0 {
   119  			ae.ObjectRef.Name = meta.GetName()
   120  		}
   121  		if len(ae.ObjectRef.UID) == 0 {
   122  			ae.ObjectRef.UID = meta.GetUID()
   123  		}
   124  		if len(ae.ObjectRef.ResourceVersion) == 0 {
   125  			ae.ObjectRef.ResourceVersion = meta.GetResourceVersion()
   126  		}
   127  	}
   128  	if len(ae.ObjectRef.APIVersion) == 0 {
   129  		ae.ObjectRef.APIGroup = gvr.Group
   130  		ae.ObjectRef.APIVersion = gvr.Version
   131  	}
   132  	if len(ae.ObjectRef.Resource) == 0 {
   133  		ae.ObjectRef.Resource = gvr.Resource
   134  	}
   135  	if len(ae.ObjectRef.Subresource) == 0 {
   136  		ae.ObjectRef.Subresource = subresource
   137  	}
   138  
   139  	if ae.Level.Less(auditinternal.LevelRequest) {
   140  		return
   141  	}
   142  
   143  	if shouldOmitManagedFields(ctx) {
   144  		copy, ok, err := copyWithoutManagedFields(obj)
   145  		if err != nil {
   146  			klog.ErrorS(err, "Error while dropping managed fields from the request", "auditID", ae.AuditID)
   147  		}
   148  		if ok {
   149  			obj = copy
   150  		}
   151  	}
   152  
   153  	// TODO(audit): hook into the serializer to avoid double conversion
   154  	var err error
   155  	ae.RequestObject, err = encodeObject(obj, objGV, s)
   156  	if err != nil {
   157  		// TODO(audit): add error slice to audit event struct
   158  		klog.ErrorS(err, "Encoding failed of request object", "auditID", ae.AuditID, "gvr", gvr.String(), "obj", obj)
   159  		return
   160  	}
   161  }
   162  
   163  // LogRequestPatch fills in the given patch as the request object into an audit event.
   164  func LogRequestPatch(ctx context.Context, patch []byte) {
   165  	ae := AuditEventFrom(ctx)
   166  	if ae == nil || ae.Level.Less(auditinternal.LevelRequest) {
   167  		return
   168  	}
   169  
   170  	ae.RequestObject = &runtime.Unknown{
   171  		Raw:         patch,
   172  		ContentType: runtime.ContentTypeJSON,
   173  	}
   174  }
   175  
   176  // LogResponseObject fills in the response object into an audit event. The passed runtime.Object
   177  // will be converted to the given gv.
   178  func LogResponseObject(ctx context.Context, obj runtime.Object, gv schema.GroupVersion, s runtime.NegotiatedSerializer) {
   179  	ae := AuditEventFrom(ctx)
   180  	if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
   181  		return
   182  	}
   183  	if status, ok := obj.(*metav1.Status); ok {
   184  		// selectively copy the bounded fields.
   185  		ae.ResponseStatus = &metav1.Status{
   186  			Status:  status.Status,
   187  			Message: status.Message,
   188  			Reason:  status.Reason,
   189  			Details: status.Details,
   190  			Code:    status.Code,
   191  		}
   192  	}
   193  
   194  	if ae.Level.Less(auditinternal.LevelRequestResponse) {
   195  		return
   196  	}
   197  
   198  	if shouldOmitManagedFields(ctx) {
   199  		copy, ok, err := copyWithoutManagedFields(obj)
   200  		if err != nil {
   201  			klog.ErrorS(err, "Error while dropping managed fields from the response", "auditID", ae.AuditID)
   202  		}
   203  		if ok {
   204  			obj = copy
   205  		}
   206  	}
   207  
   208  	// TODO(audit): hook into the serializer to avoid double conversion
   209  	var err error
   210  	ae.ResponseObject, err = encodeObject(obj, gv, s)
   211  	if err != nil {
   212  		klog.ErrorS(err, "Encoding failed of response object", "auditID", ae.AuditID, "obj", obj)
   213  	}
   214  }
   215  
   216  func encodeObject(obj runtime.Object, gv schema.GroupVersion, serializer runtime.NegotiatedSerializer) (*runtime.Unknown, error) {
   217  	const mediaType = runtime.ContentTypeJSON
   218  	info, ok := runtime.SerializerInfoForMediaType(serializer.SupportedMediaTypes(), mediaType)
   219  	if !ok {
   220  		return nil, fmt.Errorf("unable to locate encoder -- %q is not a supported media type", mediaType)
   221  	}
   222  
   223  	enc := serializer.EncoderForVersion(info.Serializer, gv)
   224  	var buf bytes.Buffer
   225  	if err := enc.Encode(obj, &buf); err != nil {
   226  		return nil, fmt.Errorf("encoding failed: %v", err)
   227  	}
   228  
   229  	return &runtime.Unknown{
   230  		Raw:         buf.Bytes(),
   231  		ContentType: mediaType,
   232  	}, nil
   233  }
   234  
   235  // truncate User-Agent if too long, otherwise return it directly.
   236  func maybeTruncateUserAgent(req *http.Request) string {
   237  	ua := req.UserAgent()
   238  	if len(ua) > maxUserAgentLength {
   239  		ua = ua[:maxUserAgentLength] + userAgentTruncateSuffix
   240  	}
   241  
   242  	return ua
   243  }
   244  
   245  // copyWithoutManagedFields will make a deep copy of the specified object and
   246  // will discard the managed fields from the copy.
   247  // The specified object is expected to be a meta.Object or a "list".
   248  // The specified object obj is treated as readonly and hence not mutated.
   249  // On return, an error is set if the function runs into any error while
   250  // removing the managed fields, the boolean value is true if the copy has
   251  // been made successfully, otherwise false.
   252  func copyWithoutManagedFields(obj runtime.Object) (runtime.Object, bool, error) {
   253  	isAccessor := true
   254  	if _, err := meta.Accessor(obj); err != nil {
   255  		isAccessor = false
   256  	}
   257  	isList := meta.IsListType(obj)
   258  	_, isTable := obj.(*metav1.Table)
   259  	if !isAccessor && !isList && !isTable {
   260  		return nil, false, nil
   261  	}
   262  
   263  	// TODO a deep copy isn't really needed here, figure out how we can reliably
   264  	//  use shallow copy here to omit the manageFields.
   265  	copy := obj.DeepCopyObject()
   266  
   267  	if isAccessor {
   268  		if err := removeManagedFields(copy); err != nil {
   269  			return nil, false, err
   270  		}
   271  	}
   272  
   273  	if isList {
   274  		if err := meta.EachListItem(copy, removeManagedFields); err != nil {
   275  			return nil, false, err
   276  		}
   277  	}
   278  
   279  	if isTable {
   280  		table := copy.(*metav1.Table)
   281  		for i := range table.Rows {
   282  			rowObj := table.Rows[i].Object
   283  			if err := removeManagedFields(rowObj.Object); err != nil {
   284  				return nil, false, err
   285  			}
   286  		}
   287  	}
   288  
   289  	return copy, true, nil
   290  }
   291  
   292  func removeManagedFields(obj runtime.Object) error {
   293  	if obj == nil {
   294  		return nil
   295  	}
   296  	accessor, err := meta.Accessor(obj)
   297  	if err != nil {
   298  		return err
   299  	}
   300  	accessor.SetManagedFields(nil)
   301  	return nil
   302  }
   303  
   304  func shouldOmitManagedFields(ctx context.Context) bool {
   305  	if auditContext := AuditContextFrom(ctx); auditContext != nil {
   306  		return auditContext.RequestAuditConfig.OmitManagedFields
   307  	}
   308  
   309  	// If we can't decide, return false to maintain current behavior which is
   310  	// to retain the manage fields in the audit.
   311  	return false
   312  }