mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-30 22:26:42 +00:00
add api for access log events
This commit is contained in:
@@ -25,6 +25,8 @@ func NewLogger(client gRPCClient) *Logger {
|
||||
}
|
||||
|
||||
type logEntry struct {
|
||||
ID string
|
||||
AccountID string
|
||||
ServiceId string
|
||||
Host string
|
||||
Path string
|
||||
@@ -50,6 +52,8 @@ func (l *Logger) log(ctx context.Context, entry logEntry) {
|
||||
go func() {
|
||||
if _, err := l.client.SendAccessLog(context.Background(), &proto.SendAccessLogRequest{
|
||||
Log: &proto.AccessLog{
|
||||
LogId: entry.ID,
|
||||
AccountId: entry.AccountID,
|
||||
Timestamp: now,
|
||||
ServiceId: entry.ServiceId,
|
||||
Host: entry.Host,
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/rs/xid"
|
||||
|
||||
"github.com/netbirdio/netbird/proxy/internal/auth"
|
||||
"github.com/netbirdio/netbird/proxy/internal/proxy"
|
||||
)
|
||||
@@ -31,8 +33,10 @@ func (l *Logger) Middleware(next http.Handler) http.Handler {
|
||||
host = r.Host
|
||||
}
|
||||
|
||||
l.log(r.Context(), logEntry{
|
||||
entry := logEntry{
|
||||
ID: xid.New().String(),
|
||||
ServiceId: proxy.ServiceIdFromContext(r.Context()),
|
||||
AccountID: proxy.AccountIdFromContext(r.Context()),
|
||||
Host: host,
|
||||
Path: r.URL.Path,
|
||||
DurationMs: duration.Milliseconds(),
|
||||
@@ -42,6 +46,7 @@ func (l *Logger) Middleware(next http.Handler) http.Handler {
|
||||
AuthMechanism: auth.MethodFromContext(r.Context()).String(),
|
||||
UserId: auth.UserFromContext(r.Context()),
|
||||
AuthSuccess: sw.status != http.StatusUnauthorized && sw.status != http.StatusForbidden,
|
||||
})
|
||||
}
|
||||
l.log(r.Context(), entry)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,14 +2,54 @@ package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type requestContextKey string
|
||||
|
||||
const (
|
||||
serviceIdKey requestContextKey = "serviceId"
|
||||
serviceIdKey requestContextKey = "serviceId"
|
||||
accountIdKey requestContextKey = "accountId"
|
||||
capturedDataKey requestContextKey = "capturedData"
|
||||
)
|
||||
|
||||
// CapturedData is a mutable struct that allows downstream handlers
|
||||
// to pass data back up the middleware chain.
|
||||
type CapturedData struct {
|
||||
mu sync.RWMutex
|
||||
ServiceId string
|
||||
AccountId string
|
||||
}
|
||||
|
||||
// SetServiceId safely sets the service ID
|
||||
func (c *CapturedData) SetServiceId(serviceId string) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.ServiceId = serviceId
|
||||
}
|
||||
|
||||
// SetAccountId safely sets the account ID
|
||||
func (c *CapturedData) SetAccountId(accountId string) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.AccountId = accountId
|
||||
}
|
||||
|
||||
// WithCapturedData adds a CapturedData struct to the context
|
||||
func WithCapturedData(ctx context.Context, data *CapturedData) context.Context {
|
||||
return context.WithValue(ctx, capturedDataKey, data)
|
||||
}
|
||||
|
||||
// CapturedDataFromContext retrieves the CapturedData from context
|
||||
func CapturedDataFromContext(ctx context.Context) *CapturedData {
|
||||
v := ctx.Value(capturedDataKey)
|
||||
data, ok := v.(*CapturedData)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func withServiceId(ctx context.Context, serviceId string) context.Context {
|
||||
return context.WithValue(ctx, serviceIdKey, serviceId)
|
||||
}
|
||||
@@ -22,3 +62,15 @@ func ServiceIdFromContext(ctx context.Context) string {
|
||||
}
|
||||
return serviceId
|
||||
}
|
||||
func withAccountId(ctx context.Context, accountId string) context.Context {
|
||||
return context.WithValue(ctx, accountIdKey, accountId)
|
||||
}
|
||||
|
||||
func AccountIdFromContext(ctx context.Context) string {
|
||||
v := ctx.Value(accountIdKey)
|
||||
accountId, ok := v.(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return accountId
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func NewReverseProxy(transport http.RoundTripper) *ReverseProxy {
|
||||
}
|
||||
|
||||
func (p *ReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
target, serviceId, exists := p.findTargetForRequest(r)
|
||||
target, serviceId, accountID, exists := p.findTargetForRequest(r)
|
||||
if !exists {
|
||||
// No mapping found so return an error here.
|
||||
// TODO: prettier error page.
|
||||
@@ -36,6 +36,8 @@ func (p *ReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Set the serviceId in the context for later retrieval.
|
||||
ctx := withServiceId(r.Context(), serviceId)
|
||||
// Set the accountId in the context for later retrieval.
|
||||
ctx = withAccountId(ctx, accountID)
|
||||
|
||||
// Set up a reverse proxy using the transport and then use it to serve the request.
|
||||
proxy := httputil.NewSingleHostReverseProxy(target)
|
||||
|
||||
@@ -8,12 +8,13 @@ import (
|
||||
)
|
||||
|
||||
type Mapping struct {
|
||||
ID string
|
||||
Host string
|
||||
Paths map[string]*url.URL
|
||||
ID string
|
||||
AccountID string
|
||||
Host string
|
||||
Paths map[string]*url.URL
|
||||
}
|
||||
|
||||
func (p *ReverseProxy) findTargetForRequest(req *http.Request) (*url.URL, string, bool) {
|
||||
func (p *ReverseProxy) findTargetForRequest(req *http.Request) (*url.URL, string, string, bool) {
|
||||
p.mappingsMux.RLock()
|
||||
if p.mappings == nil {
|
||||
p.mappingsMux.RUnlock()
|
||||
@@ -21,12 +22,12 @@ func (p *ReverseProxy) findTargetForRequest(req *http.Request) (*url.URL, string
|
||||
defer p.mappingsMux.Unlock()
|
||||
p.mappings = make(map[string]Mapping)
|
||||
// There cannot be any loaded Mappings as we have only just initialized.
|
||||
return nil, "", false
|
||||
return nil, "", "", false
|
||||
}
|
||||
defer p.mappingsMux.RUnlock()
|
||||
m, exists := p.mappings[req.Host]
|
||||
if !exists {
|
||||
return nil, "", false
|
||||
return nil, "", "", false
|
||||
}
|
||||
|
||||
// Sort paths by length (longest first) in a naive attempt to match the most specific route first.
|
||||
@@ -40,10 +41,10 @@ func (p *ReverseProxy) findTargetForRequest(req *http.Request) (*url.URL, string
|
||||
|
||||
for _, path := range paths {
|
||||
if strings.HasPrefix(req.URL.Path, path) {
|
||||
return m.Paths[path], m.ID, true
|
||||
return m.Paths[path], m.ID, m.AccountID, true
|
||||
}
|
||||
}
|
||||
return nil, "", false
|
||||
return nil, "", "", false
|
||||
}
|
||||
|
||||
func (p *ReverseProxy) AddMapping(m Mapping) {
|
||||
|
||||
@@ -3,12 +3,14 @@ package roundtrip
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/netbirdio/netbird/client/embed"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/client/embed"
|
||||
)
|
||||
|
||||
const deviceNamePrefix = "ingress-"
|
||||
@@ -34,6 +36,7 @@ func (n *NetBird) AddPeer(ctx context.Context, domain, key string) error {
|
||||
DeviceName: deviceNamePrefix + domain,
|
||||
ManagementURL: n.mgmtAddr,
|
||||
SetupKey: key,
|
||||
LogOutput: io.Discard,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("create netbird client: %w", err)
|
||||
|
||||
@@ -334,6 +334,7 @@ func (s *Server) protoToMapping(mapping *proto.ProxyMapping) proxy.Mapping {
|
||||
// TODO: Should we warn management about this so it can be bubbled up to a user to reconfigure?
|
||||
log.WithFields(log.Fields{
|
||||
"service_id": mapping.GetId(),
|
||||
"account_id": mapping.GetAccountId(),
|
||||
"domain": mapping.GetDomain(),
|
||||
"path": pathMapping.GetPath(),
|
||||
"target": pathMapping.GetTarget(),
|
||||
@@ -343,8 +344,9 @@ func (s *Server) protoToMapping(mapping *proto.ProxyMapping) proxy.Mapping {
|
||||
paths[pathMapping.GetPath()] = targetURL
|
||||
}
|
||||
return proxy.Mapping{
|
||||
ID: mapping.GetId(),
|
||||
Host: mapping.GetDomain(),
|
||||
Paths: paths,
|
||||
ID: mapping.GetId(),
|
||||
AccountID: mapping.AccountId,
|
||||
Host: mapping.GetDomain(),
|
||||
Paths: paths,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user