diff --git a/src/server/middleware/security/basic_auth.go b/src/server/middleware/security/basic_auth.go index 390979bf6..10d2f7625 100644 --- a/src/server/middleware/security/basic_auth.go +++ b/src/server/middleware/security/basic_auth.go @@ -16,6 +16,7 @@ package security import ( "net/http" + "os" "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/security" @@ -26,6 +27,36 @@ import ( type basicAuth struct{} +func trueClientIPHeaderName() string { + // because the true client IP header varies based on the foreground proxy/lb settings, + // make it configurable by env + name := os.Getenv("TRUE_CLIENT_IP_HEADER") + if len(name) == 0 { + name = "x-forwarded-for" + } + return name +} + +// GetClientIP get client ip from request +func GetClientIP(r *http.Request) string { + if r == nil { + return "" + } + ip := r.Header.Get(trueClientIPHeaderName()) + if len(ip) > 0 { + return ip + } + return r.RemoteAddr +} + +// GetUserAgent get the user agent of current request +func GetUserAgent(r *http.Request) string { + if r == nil { + return "" + } + return r.Header.Get("user-agent") +} + func (b *basicAuth) Generate(req *http.Request) security.Context { log := log.G(req.Context()) username, password, ok := req.BasicAuth() @@ -36,8 +67,9 @@ func (b *basicAuth) Generate(req *http.Request) security.Context { Principal: username, Password: password, }) + if err != nil { - log.Errorf("failed to authenticate %s: %v", username, err) + log.WithField("client IP", GetClientIP(req)).WithField("user agent", GetUserAgent(req)).Errorf("failed to authenticate user:%s, error:%v", username, err) return nil } if user == nil { diff --git a/src/server/middleware/security/basic_auth_test.go b/src/server/middleware/security/basic_auth_test.go index eb4ab31cc..985dd9f35 100644 --- a/src/server/middleware/security/basic_auth_test.go +++ b/src/server/middleware/security/basic_auth_test.go @@ -33,3 +33,51 @@ func TestBasicAuth(t *testing.T) { ctx := basicAuth.Generate(req) assert.NotNil(t, ctx) } + +func TestGetClientIP(t *testing.T) { + h := http.Header{} + h.Set("X-Forwarded-For", "1.1.1.1") + type args struct { + r *http.Request + } + tests := []struct { + name string + args args + want string + }{ + {"nil request", args{nil}, ""}, + {"no header", args{&http.Request{RemoteAddr: "10.10.10.10"}}, "10.10.10.10"}, + {"set x forworded for", args{&http.Request{Header: h, RemoteAddr: "10.10.10.10"}}, "1.1.1.1"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GetClientIP(tt.args.r); got != tt.want { + t.Errorf("GetClientIP() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetUserAgent(t *testing.T) { + h := http.Header{} + h.Set("user-agent", "docker") + type args struct { + r *http.Request + } + tests := []struct { + name string + args args + want string + }{ + {"nil request", args{nil}, ""}, + {"no header", args{&http.Request{}}, ""}, + {"with user-agent", args{&http.Request{Header: h}}, "docker"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GetUserAgent(tt.args.r); got != tt.want { + t.Errorf("GetUserAgent() = %v, want %v", got, tt.want) + } + }) + } +}