From e3e35990956d79e85001df14d6d80eaf52d3476c Mon Sep 17 00:00:00 2001 From: Anis Eleuch Date: Wed, 10 Jul 2024 16:04:50 +0100 Subject: [PATCH] ws: Calculate the client IP from the WS request headers as well (#3403) Currently, the websocket code adds an IP to X-Forwarded-For where the IP is calculated from the TCP connection established to Console, however the established TCP connection can be coming from the load balancer as well, hence the necesssity to evaluate the client IP based on X-Forwarded-For or X-Real-IP headers Look for client IPs in the websocket request connection. Co-authored-by: Anis Eleuch --- api/ws_handle.go | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/api/ws_handle.go b/api/ws_handle.go index b2c5af68c..ab400140a 100644 --- a/api/ws_handle.go +++ b/api/ws_handle.go @@ -177,9 +177,19 @@ func serveWS(w http.ResponseWriter, req *http.Request) { return } + clientIP := getSourceIPFromHeaders(req) + if clientIP == "" { + if ip, _, err := net.SplitHostPort(conn.RemoteAddr().String()); err == nil { + clientIP = ip + } else { + // In case there's an error, return an empty string + LogError("Invalid ws.RemoteAddr() = %v\n", err) + } + } + switch { case strings.HasPrefix(wsPath, `/trace`): - wsAdminClient, err := newWebSocketAdminClient(conn, session) + wsAdminClient, err := newWebSocketAdminClient(conn, session, clientIP) if err != nil { ErrorWithContext(ctx, err) closeWsConn(conn) @@ -216,7 +226,7 @@ func serveWS(w http.ResponseWriter, req *http.Request) { go wsAdminClient.trace(ctx, traceRequestItem) case strings.HasPrefix(wsPath, `/console`): - wsAdminClient, err := newWebSocketAdminClient(conn, session) + wsAdminClient, err := newWebSocketAdminClient(conn, session, clientIP) if err != nil { ErrorWithContext(ctx, err) closeWsConn(conn) @@ -237,7 +247,7 @@ func serveWS(w http.ResponseWriter, req *http.Request) { closeWsConn(conn) return } - wsAdminClient, err := newWebSocketAdminClient(conn, session) + wsAdminClient, err := newWebSocketAdminClient(conn, session, clientIP) if err != nil { ErrorWithContext(ctx, err) closeWsConn(conn) @@ -251,7 +261,7 @@ func serveWS(w http.ResponseWriter, req *http.Request) { closeWsConn(conn) return } - wsS3Client, err := newWebSocketS3Client(conn, session, wOptions.BucketName, "") + wsS3Client, err := newWebSocketS3Client(conn, session, wOptions.BucketName, "", clientIP) if err != nil { ErrorWithContext(ctx, err) closeWsConn(conn) @@ -265,7 +275,7 @@ func serveWS(w http.ResponseWriter, req *http.Request) { closeWsConn(conn) return } - wsAdminClient, err := newWebSocketAdminClient(conn, session) + wsAdminClient, err := newWebSocketAdminClient(conn, session, clientIP) if err != nil { ErrorWithContext(ctx, err) closeWsConn(conn) @@ -279,7 +289,7 @@ func serveWS(w http.ResponseWriter, req *http.Request) { closeWsConn(conn) return } - wsAdminClient, err := newWebSocketAdminClient(conn, session) + wsAdminClient, err := newWebSocketAdminClient(conn, session, clientIP) if err != nil { ErrorWithContext(ctx, err) closeWsConn(conn) @@ -288,7 +298,7 @@ func serveWS(w http.ResponseWriter, req *http.Request) { go wsAdminClient.profile(ctx, pOptions) case strings.HasPrefix(wsPath, `/objectManager`): - wsMinioClient, err := newWebSocketMinioClient(conn, session) + wsMinioClient, err := newWebSocketMinioClient(conn, session, clientIP) if err != nil { ErrorWithContext(ctx, err) closeWsConn(conn) @@ -303,12 +313,11 @@ func serveWS(w http.ResponseWriter, req *http.Request) { } // newWebSocketAdminClient returns a wsAdminClient authenticated as an admin user -func newWebSocketAdminClient(conn *websocket.Conn, autClaims *models.Principal) (*wsAdminClient, error) { +func newWebSocketAdminClient(conn *websocket.Conn, autClaims *models.Principal, clientIP string) (*wsAdminClient, error) { // create a websocket connection interface implementation // defining the connection to be used wsConnection := wsConn{conn: conn} - clientIP := wsConnection.remoteAddress() // Only start Websocket Interaction after user has been // authenticated with MinIO mAdmin, err := newAdminFromClaims(autClaims, clientIP) @@ -326,15 +335,9 @@ func newWebSocketAdminClient(conn *websocket.Conn, autClaims *models.Principal) } // newWebSocketS3Client returns a wsAdminClient authenticated as Console admin -func newWebSocketS3Client(conn *websocket.Conn, claims *models.Principal, bucketName, prefix string) (*wsS3Client, error) { +func newWebSocketS3Client(conn *websocket.Conn, claims *models.Principal, bucketName, prefix, clientIP string) (*wsS3Client, error) { // Only start Websocket Interaction after user has been // authenticated with MinIO - clientIP, _, err := net.SplitHostPort(conn.RemoteAddr().String()) - if err != nil { - // In case there's an error, return an empty string - log.Printf("Invalid ws.clientIP = %s\n", err) - } - s3Client, err := newS3BucketClient(claims, bucketName, prefix, clientIP) if err != nil { LogError("error creating S3Client:", err) @@ -351,14 +354,7 @@ func newWebSocketS3Client(conn *websocket.Conn, claims *models.Principal, bucket return wsS3Client, nil } -func newWebSocketMinioClient(conn *websocket.Conn, claims *models.Principal) (*wsMinioClient, error) { - // Only start Websocket Interaction after user has been - // authenticated with MinIO - clientIP, _, err := net.SplitHostPort(conn.RemoteAddr().String()) - if err != nil { - // In case there's an error, return an empty string - log.Printf("Invalid ws.clientIP = %s\n", err) - } +func newWebSocketMinioClient(conn *websocket.Conn, claims *models.Principal, clientIP string) (*wsMinioClient, error) { mClient, err := newMinioClient(claims, clientIP) if err != nil { LogError("error creating MinioClient:", err)