mirror of https://github.com/goharbor/harbor.git
87 lines
3.0 KiB
Go
87 lines
3.0 KiB
Go
// Copyright Project Harbor Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package clientinfo
|
|
|
|
import (
|
|
"net/http"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/goharbor/harbor/src/lib/config"
|
|
|
|
"github.com/goharbor/harbor/src/lib"
|
|
"github.com/goharbor/harbor/src/server/middleware"
|
|
)
|
|
|
|
var (
|
|
// De-facto standard header keys.
|
|
xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
|
|
xRealIP = http.CanonicalHeaderKey("X-Real-IP")
|
|
|
|
// RFC7239 defines a new "Forwarded: " header designed to replace the
|
|
// existing use of X-Forwarded-* headers.
|
|
// e.g. Forwarded: for=192.0.2.60;proto=https;by=203.0.113.43
|
|
forwarded = http.CanonicalHeaderKey("Forwarded")
|
|
// Allows for a sub-match of the first value after 'for=' to the next
|
|
// comma, semi-colon or space. The match is case-insensitive.
|
|
forRegex = regexp.MustCompile(`(?i)(?:for=)([^(;|,| )]+)`)
|
|
)
|
|
|
|
func getIP(r *http.Request) string {
|
|
var addr string
|
|
|
|
if fwd := r.Header.Get(xForwardedFor); fwd != "" {
|
|
// Only grab the first (client) address. Note that '192.168.0.1,
|
|
// 10.1.1.1' is a valid key for X-Forwarded-For where addresses after
|
|
// the first may represent forwarding proxies earlier in the chain.
|
|
s := strings.Index(fwd, ", ")
|
|
if s == -1 {
|
|
s = len(fwd)
|
|
}
|
|
addr = fwd[:s]
|
|
} else if fwd := r.Header.Get(xRealIP); fwd != "" {
|
|
// X-Real-IP should only contain one IP address (the client making the
|
|
// request).
|
|
addr = fwd
|
|
} else if fwd := r.Header.Get(forwarded); fwd != "" {
|
|
// match should contain at least two elements if the protocol was
|
|
// specified in the Forwarded header. The first element will always be
|
|
// the 'for=' capture, which we ignore. In the case of multiple IP
|
|
// addresses (for=8.8.8.8, 8.8.4.4,172.16.1.20 is valid) we only
|
|
// extract the first, which should be the client IP.
|
|
if match := forRegex.FindStringSubmatch(fwd); len(match) > 1 {
|
|
// IPv6 addresses in Forwarded headers are quoted-strings. We strip
|
|
// these quotes.
|
|
addr = strings.Trim(match[1], `"`)
|
|
}
|
|
}
|
|
|
|
return addr
|
|
}
|
|
|
|
// Middleware sends the notification after transaction success
|
|
func Middleware(skippers ...middleware.Skipper) func(http.Handler) http.Handler {
|
|
return middleware.New(func(w http.ResponseWriter, r *http.Request, next http.Handler) {
|
|
ctx := r.Context()
|
|
if config.AuditLogTrackIPAddress(ctx) {
|
|
ctx = lib.WithClientIPAddress(ctx, getIP(r))
|
|
}
|
|
if config.AuditLogTrackUserAgent(ctx) {
|
|
ctx = lib.WithUserAgent(ctx, r.UserAgent())
|
|
}
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
}, skippers...)
|
|
}
|