Unverified Commit 1f4f0123 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #64812 from hzxuzhonghu/audit-useragent

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Add user-agent to audit-logging **What this PR does / why we need it**: Add User-Agent to audit event. **Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*: Fixes #64791 **Special notes for your reviewer**: **Release note**: ```release-note Add user-agent to audit-logging. ```
parents 93055c77 f0b1f1c2
......@@ -99,6 +99,10 @@ type Event struct {
// Source IPs, from where the request originated and intermediate proxies.
// +optional
SourceIPs []string
// UserAgent records the user agent string reported by the client.
// Note that the UserAgent is provided by the client, and must not be trusted.
// +optional
UserAgent string
// Object reference this request is targeted at.
// Does not apply for List-type requests, or non-resource requests.
// +optional
......
......@@ -66,6 +66,11 @@ message Event {
// +optional
repeated string sourceIPs = 10;
// UserAgent records the user agent string reported by the client.
// Note that the UserAgent is provided by the client, and must not be trusted.
// +optional
optional string userAgent = 18;
// Object reference this request is targeted at.
// Does not apply for List-type requests, or non-resource requests.
// +optional
......
......@@ -105,6 +105,10 @@ type Event struct {
// Source IPs, from where the request originated and intermediate proxies.
// +optional
SourceIPs []string `json:"sourceIPs,omitempty" protobuf:"bytes,10,rep,name=sourceIPs"`
// UserAgent records the user agent string reported by the client.
// Note that the UserAgent is provided by the client, and must not be trusted.
// +optional
UserAgent string `json:"userAgent,omitempty" protobuf:"bytes,18,opt,name=userAgent"`
// Object reference this request is targeted at.
// Does not apply for List-type requests, or non-resource requests.
// +optional
......
......@@ -70,6 +70,7 @@ func autoConvert_v1alpha1_Event_To_audit_Event(in *Event, out *audit.Event, s co
}
out.ImpersonatedUser = (*audit.UserInfo)(unsafe.Pointer(in.ImpersonatedUser))
out.SourceIPs = *(*[]string)(unsafe.Pointer(&in.SourceIPs))
out.UserAgent = in.UserAgent
if in.ObjectRef != nil {
in, out := &in.ObjectRef, &out.ObjectRef
*out = new(audit.ObjectReference)
......@@ -100,6 +101,7 @@ func autoConvert_audit_Event_To_v1alpha1_Event(in *audit.Event, out *Event, s co
}
out.ImpersonatedUser = (*authentication_v1.UserInfo)(unsafe.Pointer(in.ImpersonatedUser))
out.SourceIPs = *(*[]string)(unsafe.Pointer(&in.SourceIPs))
out.UserAgent = in.UserAgent
if in.ObjectRef != nil {
in, out := &in.ObjectRef, &out.ObjectRef
*out = new(ObjectReference)
......
......@@ -70,6 +70,11 @@ message Event {
// +optional
repeated string sourceIPs = 10;
// UserAgent records the user agent string reported by the client.
// Note that the UserAgent is provided by the client, and must not be trusted.
// +optional
optional string userAgent = 18;
// Object reference this request is targeted at.
// Does not apply for List-type requests, or non-resource requests.
// +optional
......
......@@ -101,6 +101,10 @@ type Event struct {
// Source IPs, from where the request originated and intermediate proxies.
// +optional
SourceIPs []string `json:"sourceIPs,omitempty" protobuf:"bytes,10,rep,name=sourceIPs"`
// UserAgent records the user agent string reported by the client.
// Note that the UserAgent is provided by the client, and must not be trusted.
// +optional
UserAgent string `json:"userAgent,omitempty" protobuf:"bytes,18,opt,name=userAgent"`
// Object reference this request is targeted at.
// Does not apply for List-type requests, or non-resource requests.
// +optional
......
......@@ -70,6 +70,7 @@ func autoConvert_v1beta1_Event_To_audit_Event(in *Event, out *audit.Event, s con
}
out.ImpersonatedUser = (*audit.UserInfo)(unsafe.Pointer(in.ImpersonatedUser))
out.SourceIPs = *(*[]string)(unsafe.Pointer(&in.SourceIPs))
out.UserAgent = in.UserAgent
out.ObjectRef = (*audit.ObjectReference)(unsafe.Pointer(in.ObjectRef))
out.ResponseStatus = (*v1.Status)(unsafe.Pointer(in.ResponseStatus))
out.RequestObject = (*runtime.Unknown)(unsafe.Pointer(in.RequestObject))
......@@ -92,6 +93,7 @@ func autoConvert_audit_Event_To_v1beta1_Event(in *audit.Event, out *Event, s con
}
out.ImpersonatedUser = (*authentication_v1.UserInfo)(unsafe.Pointer(in.ImpersonatedUser))
out.SourceIPs = *(*[]string)(unsafe.Pointer(&in.SourceIPs))
out.UserAgent = in.UserAgent
out.ObjectRef = (*ObjectReference)(unsafe.Pointer(in.ObjectRef))
out.ResponseStatus = (*v1.Status)(unsafe.Pointer(in.ResponseStatus))
out.RequestObject = (*runtime.Unknown)(unsafe.Pointer(in.RequestObject))
......
......@@ -37,15 +37,20 @@ import (
"k8s.io/apiserver/pkg/authorization/authorizer"
)
const (
maxUserAgentLength = 1024
userAgentTruncateSuffix = "...TRUNCATED"
)
func NewEventFromRequest(req *http.Request, level auditinternal.Level, attribs authorizer.Attributes) (*auditinternal.Event, error) {
ev := &auditinternal.Event{
RequestReceivedTimestamp: metav1.NewMicroTime(time.Now()),
Verb: attribs.GetVerb(),
RequestURI: req.URL.RequestURI(),
UserAgent: maybeTruncateUserAgent(req),
Level: level,
}
ev.Level = level
// prefer the id from the headers. If not available, create a new one.
// TODO(audit): do we want to forbid the header for non-front-proxy users?
ids := req.Header.Get(auditinternal.HeaderAuditID)
......@@ -233,3 +238,13 @@ func LogAnnotations(ae *auditinternal.Event, annotations map[string]string) {
LogAnnotation(ae, key, value)
}
}
// truncate User-Agent if too long, otherwise return it directly.
func maybeTruncateUserAgent(req *http.Request) string {
ua := req.UserAgent()
if len(ua) > maxUserAgentLength {
ua = ua[:maxUserAgentLength] + userAgentTruncateSuffix
}
return ua
}
......@@ -17,9 +17,11 @@ limitations under the License.
package audit
import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
auditinternal "k8s.io/apiserver/pkg/apis/audit"
)
......@@ -36,3 +38,19 @@ func TestLogAnnotation(t *testing.T) {
LogAnnotation(ev, "qux", "baz")
assert.Equal(t, "", ev.Annotations["qux"], "audit annotation should not be overwritten.")
}
func TestMaybeTruncateUserAgent(t *testing.T) {
req := &http.Request{}
req.Header = http.Header{}
ua := "short-agent"
req.Header.Set("User-Agent", ua)
assert.Equal(t, ua, maybeTruncateUserAgent(req))
ua = ""
for i := 0; i < maxUserAgentLength*2; i++ {
ua = ua + "a"
}
req.Header.Set("User-Agent", ua)
assert.NotEqual(t, ua, maybeTruncateUserAgent(req))
}
......@@ -63,6 +63,7 @@ func TestAudit(t *testing.T) {
Other: "bla",
}
simpleCPrimeJSON, _ := runtime.Encode(testCodec, simpleCPrime)
userAgent := "audit-test"
// event checks
noRequestBody := func(i int) eventCheck {
......@@ -111,6 +112,16 @@ func TestAudit(t *testing.T) {
return nil
}
}
requestUserAgentMatches := func(userAgent string) eventCheck {
return func(events []*auditinternal.Event) error {
for i := range events {
if events[i].UserAgent != userAgent {
return fmt.Errorf("expected request user agent to match %q, but got: %q", userAgent, events[i].UserAgent)
}
}
return nil
}
}
for _, test := range []struct {
desc string
......@@ -295,6 +306,8 @@ func TestAudit(t *testing.T) {
t.Errorf("[%s] error creating the request: %v", test.desc, err)
}
req.Header.Set("User-Agent", userAgent)
response, err := client.Do(req)
if err != nil {
t.Errorf("[%s] error: %v", test.desc, err)
......@@ -326,6 +339,10 @@ func TestAudit(t *testing.T) {
t.Errorf("[%s,%d] %v", test.desc, i, err)
}
}
if err := requestUserAgentMatches(userAgent)(events); err != nil {
t.Errorf("[%s] %v", test.desc, err)
}
}
if len(events) > 0 {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment