blob: 25d6c33ebade395e0b3c2dfe9f22075f2db4790d [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
2Copyright 2017 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package audit
18
19import (
20 "bytes"
21 "fmt"
22 "net/http"
23 "time"
24
25 "github.com/golang/glog"
26 "github.com/pborman/uuid"
27
28 "reflect"
29
30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31 "k8s.io/apimachinery/pkg/runtime"
32 "k8s.io/apimachinery/pkg/runtime/schema"
33 "k8s.io/apimachinery/pkg/types"
34 utilnet "k8s.io/apimachinery/pkg/util/net"
35 auditinternal "k8s.io/apiserver/pkg/apis/audit"
36 "k8s.io/apiserver/pkg/authentication/user"
37 "k8s.io/apiserver/pkg/authorization/authorizer"
38)
39
40func NewEventFromRequest(req *http.Request, level auditinternal.Level, attribs authorizer.Attributes) (*auditinternal.Event, error) {
41 ev := &auditinternal.Event{
42 RequestReceivedTimestamp: metav1.NewMicroTime(time.Now()),
43 Verb: attribs.GetVerb(),
44 RequestURI: req.URL.RequestURI(),
45 }
46
47 ev.Level = level
48
49 // prefer the id from the headers. If not available, create a new one.
50 // TODO(audit): do we want to forbid the header for non-front-proxy users?
51 ids := req.Header.Get(auditinternal.HeaderAuditID)
52 if ids != "" {
53 ev.AuditID = types.UID(ids)
54 } else {
55 ev.AuditID = types.UID(uuid.NewRandom().String())
56 }
57
58 ips := utilnet.SourceIPs(req)
59 ev.SourceIPs = make([]string, len(ips))
60 for i := range ips {
61 ev.SourceIPs[i] = ips[i].String()
62 }
63
64 if user := attribs.GetUser(); user != nil {
65 ev.User.Username = user.GetName()
66 ev.User.Extra = map[string]auditinternal.ExtraValue{}
67 for k, v := range user.GetExtra() {
68 ev.User.Extra[k] = auditinternal.ExtraValue(v)
69 }
70 ev.User.Groups = user.GetGroups()
71 ev.User.UID = user.GetUID()
72 }
73
74 if attribs.IsResourceRequest() {
75 ev.ObjectRef = &auditinternal.ObjectReference{
76 Namespace: attribs.GetNamespace(),
77 Name: attribs.GetName(),
78 Resource: attribs.GetResource(),
79 Subresource: attribs.GetSubresource(),
80 APIGroup: attribs.GetAPIGroup(),
81 APIVersion: attribs.GetAPIVersion(),
82 }
83 }
84
85 return ev, nil
86}
87
88// LogImpersonatedUser fills in the impersonated user attributes into an audit event.
89func LogImpersonatedUser(ae *auditinternal.Event, user user.Info) {
90 if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
91 return
92 }
93 ae.ImpersonatedUser = &auditinternal.UserInfo{
94 Username: user.GetName(),
95 }
96 ae.ImpersonatedUser.Groups = user.GetGroups()
97 ae.ImpersonatedUser.UID = user.GetUID()
98 ae.ImpersonatedUser.Extra = map[string]auditinternal.ExtraValue{}
99 for k, v := range user.GetExtra() {
100 ae.ImpersonatedUser.Extra[k] = auditinternal.ExtraValue(v)
101 }
102}
103
104// LogRequestObject fills in the request object into an audit event. The passed runtime.Object
105// will be converted to the given gv.
106func LogRequestObject(ae *auditinternal.Event, obj runtime.Object, gvr schema.GroupVersionResource, subresource string, s runtime.NegotiatedSerializer) {
107 if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
108 return
109 }
110
111 // complete ObjectRef
112 if ae.ObjectRef == nil {
113 ae.ObjectRef = &auditinternal.ObjectReference{}
114 }
115 if acc, ok := obj.(metav1.ObjectMetaAccessor); ok {
116 meta := acc.GetObjectMeta()
117 if len(ae.ObjectRef.Namespace) == 0 {
118 ae.ObjectRef.Namespace = meta.GetNamespace()
119 }
120 if len(ae.ObjectRef.Name) == 0 {
121 ae.ObjectRef.Name = meta.GetName()
122 }
123 if len(ae.ObjectRef.UID) == 0 {
124 ae.ObjectRef.UID = meta.GetUID()
125 }
126 if len(ae.ObjectRef.ResourceVersion) == 0 {
127 ae.ObjectRef.ResourceVersion = meta.GetResourceVersion()
128 }
129 }
130 if len(ae.ObjectRef.APIVersion) == 0 {
131 ae.ObjectRef.APIGroup = gvr.Group
132 ae.ObjectRef.APIVersion = gvr.Version
133 }
134 if len(ae.ObjectRef.Resource) == 0 {
135 ae.ObjectRef.Resource = gvr.Resource
136 }
137 if len(ae.ObjectRef.Subresource) == 0 {
138 ae.ObjectRef.Subresource = subresource
139 }
140
141 if ae.Level.Less(auditinternal.LevelRequest) {
142 return
143 }
144
145 // TODO(audit): hook into the serializer to avoid double conversion
146 var err error
147 ae.RequestObject, err = encodeObject(obj, gvr.GroupVersion(), s)
148 if err != nil {
149 // TODO(audit): add error slice to audit event struct
150 glog.Warningf("Auditing failed of %v request: %v", reflect.TypeOf(obj).Name(), err)
151 return
152 }
153}
154
155// LogRequestPatch fills in the given patch as the request object into an audit event.
156func LogRequestPatch(ae *auditinternal.Event, patch []byte) {
157 if ae == nil || ae.Level.Less(auditinternal.LevelRequest) {
158 return
159 }
160
161 ae.RequestObject = &runtime.Unknown{
162 Raw: patch,
163 ContentType: runtime.ContentTypeJSON,
164 }
165}
166
167// LogResponseObject fills in the response object into an audit event. The passed runtime.Object
168// will be converted to the given gv.
169func LogResponseObject(ae *auditinternal.Event, obj runtime.Object, gv schema.GroupVersion, s runtime.NegotiatedSerializer) {
170 if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
171 return
172 }
173 if status, ok := obj.(*metav1.Status); ok {
174 // selectively copy the bounded fields.
175 ae.ResponseStatus = &metav1.Status{
176 Status: status.Status,
177 Reason: status.Reason,
178 Code: status.Code,
179 }
180 }
181
182 if ae.Level.Less(auditinternal.LevelRequestResponse) {
183 return
184 }
185 // TODO(audit): hook into the serializer to avoid double conversion
186 var err error
187 ae.ResponseObject, err = encodeObject(obj, gv, s)
188 if err != nil {
189 glog.Warningf("Audit failed for %q response: %v", reflect.TypeOf(obj).Name(), err)
190 }
191}
192
193func encodeObject(obj runtime.Object, gv schema.GroupVersion, serializer runtime.NegotiatedSerializer) (*runtime.Unknown, error) {
194 supported := serializer.SupportedMediaTypes()
195 for i := range supported {
196 if supported[i].MediaType == "application/json" {
197 enc := serializer.EncoderForVersion(supported[i].Serializer, gv)
198 var buf bytes.Buffer
199 if err := enc.Encode(obj, &buf); err != nil {
200 return nil, fmt.Errorf("encoding failed: %v", err)
201 }
202
203 return &runtime.Unknown{
204 Raw: buf.Bytes(),
205 ContentType: runtime.ContentTypeJSON,
206 }, nil
207 }
208 }
209 return nil, fmt.Errorf("no json encoder found")
210}
211
212// LogAnnotation fills in the Annotations according to the key value pair.
213func LogAnnotation(ae *auditinternal.Event, key, value string) {
214 if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
215 return
216 }
217 if ae.Annotations == nil {
218 ae.Annotations = make(map[string]string)
219 }
220 if v, ok := ae.Annotations[key]; ok && v != value {
221 glog.Warningf("Failed to set annotations[%q] to %q for audit:%q, it has already been set to %q", key, value, ae.AuditID, ae.Annotations[key])
222 return
223 }
224 ae.Annotations[key] = value
225}
226
227// LogAnnotations fills in the Annotations according to the annotations map.
228func LogAnnotations(ae *auditinternal.Event, annotations map[string]string) {
229 if ae == nil || ae.Level.Less(auditinternal.LevelMetadata) {
230 return
231 }
232 for key, value := range annotations {
233 LogAnnotation(ae, key, value)
234 }
235}