-
Notifications
You must be signed in to change notification settings - Fork 39.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Basic audit log #27087
Basic audit log #27087
Conversation
@@ -475,6 +479,10 @@ func (s *GenericAPIServer) init(c *Config) { | |||
|
|||
attributeGetter := apiserver.NewRequestAttributeGetter(s.RequestContextMapper, s.NewRequestInfoResolver()) | |||
handler = apiserver.WithAuthorizationCheck(handler, attributeGetter, s.authorizer) | |||
if c.EnableAudit { | |||
// audit handler must comes before the impersonationFilter to read the original user | |||
handler = audit.WithAudit(handler, s.RequestContextMapper) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this come before authorization? I imagine we'd want to capture authentication requests that were denied for some reason or another.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Woops, sorry it already does. (Got my middleware logic backwards)
This is great. Few comments:
[0] #2203 (comment) |
} | ||
|
||
func (a *auditResponseWriter) WriteHeader(code int) { | ||
glog.Infof("AUDIT: id=%q response=\"%d\"", a.id, code) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is writing to stdout really the best way to produce audit logs? I guess I'd expect a separate output file, though I've never made audit logs before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A separate log file is not uncommon (depends on whether you want your logs
in one place or two places) - putting this behind an interface certainly
can't hurt though, and fleshing out the implementation.
On Wed, Jun 8, 2016 at 6:56 PM, Daniel Smith notifications@github.com
wrote:
In pkg/apiserver/audit/audit.go
#27087 (comment)
:
- "github.com/golang/glog"
- "github.com/pborman/uuid"
- "k8s.io/kubernetes/pkg/api"
- utilnet "k8s.io/kubernetes/pkg/util/net"
+)
+// auditResponseWriter implements http.ResponseWriter interface.
+type auditResponseWriter struct {
- http.ResponseWriter
- id string
+}
+func (a *auditResponseWriter) WriteHeader(code int) {
- glog.Infof("AUDIT: id=%q response="%d"", a.id, code)
Is writing to stdout really the best way to produce audit logs? I guess
I'd expect a separate output file, though I've never made audit logs before.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/kubernetes/kubernetes/pull/27087/files/752c551c39be25f16e03d37bec8a3c2567467370#r66355213,
or mute the thread
https://github.com/notifications/unsubscribe/ABG_p4gePInwAQlDTfd0K-tGyId2SyZeks5qJ0iqgaJpZM4Ixcs-
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The log is already hard enough to read-- I think I'd prefer a separate file.
Also I think it might be good to put the audit ID in the context? I guess
we can do that when we have a use case.
On Wed, Jun 8, 2016 at 4:07 PM, Clayton Coleman notifications@github.com
wrote:
In pkg/apiserver/audit/audit.go
#27087 (comment)
:
- "github.com/golang/glog"
- "github.com/pborman/uuid"
- "k8s.io/kubernetes/pkg/api"
- utilnet "k8s.io/kubernetes/pkg/util/net"
+)
+// auditResponseWriter implements http.ResponseWriter interface.
+type auditResponseWriter struct {
- http.ResponseWriter
- id string
+}
+func (a *auditResponseWriter) WriteHeader(code int) {
- glog.Infof("AUDIT: id=%q response="%d"", a.id, code)
A separate log file is not uncommon (depends on whether you want your logs
in one place or two places) - putting this behind an interface certainly
can't hurt though, and fleshing out the implementation.
… <#m_-3854526224780300919_>
On Wed, Jun 8, 2016 at 6:56 PM, Daniel Smith _@_.***> wrote: In
pkg/apiserver/audit/audit.go <#27087 (comment)
https://github.com/kubernetes/kubernetes/pull/27087#discussion_r66355213>
: > + > + "github.com/golang/glog" > + "github.com/pborman/uuid" > + > + "
k8s.io/kubernetes/pkg/api" > + utilnet "k8s.io/kubernetes/pkg/util/net" >
+) > + > +// auditResponseWriter implements http.ResponseWriter interface.+type auditResponseWriter struct { > + http.ResponseWriter > + id string
+} > + > +func (a *auditResponseWriter) WriteHeader(code int) { > +
glog.Infof("AUDIT: id=%q response="%d"", a.id, code) Is writing to
stdout really the best way to produce audit logs? I guess I'd expect a
separate output file, though I've never made audit logs before. — You are
receiving this because you were mentioned. Reply to this email directly,
view it on GitHub <
https://github.com/kubernetes/kubernetes/pull/27087/files/752c551c39be25f16e03d37bec8a3c2567467370#r66355213>,
or mute the thread <
https://github.com/notifications/unsubscribe/ABG_p4gePInwAQlDTfd0K-tGyId2SyZeks5qJ0iqgaJpZM4Ixcs->
.—
You are receiving this because you were assigned.
Reply to this email directly, view it on GitHub
https://github.com/kubernetes/kubernetes/pull/27087/files/752c551c39be25f16e03d37bec8a3c2567467370#r66356421,
or mute the thread
https://github.com/notifications/unsubscribe/AAngljnZIzH5MvZkcNtqBs9oe67OxgN5ks5qJ0sjgaJpZM4Ixcs-
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The log is already hard enough to read-- I think I'd prefer a separate file.
I don't mind that, can you please tell me how to do it with glog though? I haven't see any possibility there :( But I agree with Clayton about placing it behind an interface.
Also I think it might be good to put the audit ID in the context? I guess we can do that when we have a use case.
Makes sense as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just don't use glog may be the easiest :)
On Thu, Jun 9, 2016 at 7:36 AM, Maciej Szulik notifications@github.com
wrote:
In pkg/apiserver/audit/audit.go
#27087 (comment)
:
- "github.com/golang/glog"
- "github.com/pborman/uuid"
- "k8s.io/kubernetes/pkg/api"
- utilnet "k8s.io/kubernetes/pkg/util/net"
+)
+// auditResponseWriter implements http.ResponseWriter interface.
+type auditResponseWriter struct {
- http.ResponseWriter
- id string
+}
+func (a *auditResponseWriter) WriteHeader(code int) {
- glog.Infof("AUDIT: id=%q response="%d"", a.id, code)
The log is already hard enough to read-- I think I'd prefer a separate
file.I don't mind that, can you please tell me how to do it with glog though? I
haven't see any possibility there :( But I agree with Clayton about placing
it behind an interface.Also I think it might be good to put the audit ID in the context? I guess
we can do that when we have a use case.Makes sense as well.
—
You are receiving this because you were assigned.
Reply to this email directly, view it on GitHub
https://github.com/kubernetes/kubernetes/pull/27087/files/752c551c39be25f16e03d37bec8a3c2567467370#r66452677,
or mute the thread
https://github.com/notifications/unsubscribe/AAnglhR5eD-2XOT8-YcGg8_0JQ1dW5Myks5qKCTVgaJpZM4Ixcs-
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll go with Clayton's solution of passing a writer, how that will be resolved further we can leave for other time :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separate file seems like a good default.
I'm going to want to add an interface that can call out to a webhook to post a batch of these. But that can wait for a later PR. Going with passing Writer in this PR seems fine.
So I think the most important thing about audit logging is probably certainty that it always gets called for all api accesses. I think you need a testing strategy for this. I also think this probably gives incorrect or unhelpful output for watches, port forwarding / attach / exec, proxy requests. |
I like this. Thanks for writing it. |
/cc @cjcullen |
@maisem you have some expertise in this area: would you take a look at the list of things that are being audit logged? |
// - impersonated user for the operation | ||
// - namespace of the request or <none> | ||
// - uri is the full URI as requested | ||
// 2. the response line containing the unique id from 1 and response code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maisem take a look at above comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you split this line into the same format as 1.
As it is right now, I thought it meant that we log the response body.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess my question is should we log the response/request body as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you split this line into the same format as 1.
As it is right now, I thought it meant that we log the response body.
Not sure what you need here. Basically it's split into two lines because of the response code. I want to have two separate logs for the matter of knowing which and when went in. I don't see any value in repeating all data in the 2nd line.
I guess my question is should we log the response/request body as well?
If you want to have full request/response body just increase the loglevel. This is meant rather as just a general audit information, hopefully at some point in time landing in a separate file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I meant the comment line.
2. the response line containing:
- the unique id from 1
- response code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😊 sure thing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logging the whole body for a secret create request seems like a bad idea.
On Thu, Jun 30, 2016 at 7:34 AM, Maciej Szulik notifications@github.com
wrote:
In pkg/apiserver/audit/audit.go
#27087 (comment)
:
+var _ http.CloseNotifier = &fancyResponseWriterDelegator{}
+var _ http.Flusher = &fancyResponseWriterDelegator{}
+var _ http.Hijacker = &fancyResponseWriterDelegator{}
+
+// WithAudit is responsible for logging audit information for all the
+// request coming to server. Each audit log contains two entries:
+// 1. the request line containing:
+// - unique id allowing to match the response line (see 2)
+// - source ip of the request
+// - HTTP method being invoked
+// - original user invoking the operation
+// - impersonated user for the operation
+// - namespace of the request or
+// - uri is the full URI as requested
+// 2. the response line containing the unique id from 1 and response code😊 sure thing
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/kubernetes/kubernetes/pull/27087/files/752c551c39be25f16e03d37bec8a3c2567467370#r69143282,
or mute the thread
https://github.com/notifications/unsubscribe/AHuudsn2QQ9HN0VVhLSrMxG9Krok0TzZks5qQ9P5gaJpZM4Ixcs-
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, but that's solvable by redacting some of the fields.
It would be good to know what the caller was generating secrets for.
It would be nice if the id generated here was available for log correlation at the api level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yet again, that falls into the category of debugging server, which can be easily achieved with loglevel=8. Audit is different, imho.
I'll try to update this PR tomorrow, now that 1.4 queue is open. |
Updated, ptal. |
} | ||
|
||
func (a *auditResponseWriter) WriteHeader(code int) { | ||
fmt.Fprintf(a.out, "AUDIT: id=%q response=\"%d\"", a.id, code) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to add a newline here? At the moment all the logs appear in a single line which is a bit of a pain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, I totally forgot about that one when switching from glog to Fprintf.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 for newline
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Should the audit log messages include a timestamp? |
} | ||
id := uuid.NewRandom().String() | ||
|
||
fmt.Fprintf(out, "AUDIT: id=%q ip=%q method=%q user=%q as=%q namespace=%q uri=%q", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about making this more structured, e.g. json or yaml?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by that? Printing yaml/json here, eg:
audit:
id: 123.456
ip: 192.168.1.1
method: POST
...
Wouldn't that make the log too long? With above you have just one line for each call, easily greppable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think key-value as it's written is a good first option since it's most amenable to systems like splunk and other conventional security logging utilities. If any other formats are added (e.g. JSON blobs on one one line each) they should be a configuration option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, ok. If this is a common format then that's totally fine with me.
On Tue, Jul 12, 2016 at 5:01 PM, Sam Ghods notifications@github.com wrote:
In pkg/apiserver/audit/audit.go
#27087 (comment)
:+// - response code
+func WithAudit(handler http.Handler, requestContextMapper api.RequestContextMapper, out io.Writer) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
ctx, _ := requestContextMapper.Get(req)
user, _ := api.UserFrom(ctx)
asuser := req.Header.Get("Impersonate-User")
if len(asuser) == 0 {
asuser = "<self>"
}
namespace := api.NamespaceValue(ctx)
if len(namespace) == 0 {
namespace = "<none>"
}
id := uuid.NewRandom().String()
fmt.Fprintf(out, "AUDIT: id=%q ip=%q method=%q user=%q as=%q namespace=%q uri=%q",
I think key-value as it's written is a good first option since it's most
amenable to systems like splunk and other conventional security logging
utilities. If any other formats are added (e.g. JSON blobs on one one line)
they should be a configuration option.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/kubernetes/kubernetes/pull/27087/files/39a81bd9c13e88bfa0da8c2bc6f91cf4ae450a28#r70544596,
or mute the thread
https://github.com/notifications/unsubscribe/AAnglp0vFSO0I3GTyjzzU862gyP4AwbJks5qVCrpgaJpZM4Ixcs-
.
+1 for timestapm. Looks close. It would be super awesome if you made the ordinary log messages include the audit log ID-- that would make it easier to trace a crash back to a request. I really want this because right now we've got no way to see request sources for requests that are still ongoing. |
@lavalamp yeah, I'll try to address the suggestions tomorrow.
Can you elaborate a bit more what do you want here? Not sure I follow. |
@soltysh @lavalamp I think I understand, but I would suggest it is out of scope for this. The ID here is necessary to correlate the request and the response. @lavalamp is suggesting that all k8s logs that can be traced back to a request include this ID. If this was done, an error in any part of k8s could be traced back to the API call that triggered it. I'm not sure this is feasible, but it's certainly a nice idea. |
@sttts added the 3 options (maxbackup, maxage and maxsize) to configure the logging as discussed on IRC and fixed the break, as well. ptal |
lgtm Review status: 1 of 4 files reviewed at latest revision, 2 unresolved discussions. Comments from Reviewable |
Fixed boilerplate in the newly added files, which was causing jenkins failure. Applying label based on previous approval. |
Review status: 0 of 4 files reviewed at latest revision, 2 unresolved discussions. pkg/apiserver/audit/audit_test.go, line 47 [r2] (raw file):
|
pkg/genericapiserver/options/server_run_options.go, line 299 [r2] (raw file):
|
Reviewed 4 of 4 files at r3. Comments from Reviewable |
Rebased and fixed the golint error. |
GCE e2e build/test passed for commit 24f1e1e. |
Reviewed 4 of 4 files at r3, 2 of 2 files at r4. Comments from Reviewable |
@k8s-bot test this [submit-queue is verifying that this PR is safe to merge] |
GCE e2e build/test passed for commit 24f1e1e. |
Automatic merge from submit-queue |
Fixes #2203 by introducing simple audit logging, including the information about impersonation. We currently have something identical in openshift, but I'm open to any suggestions. Sample logs look like that:
as
<self>
:as user:
@ericchiang @smarterclayton @roberthbailey @erictune @ghodss
This change is