From 34ec813370f0b5fd1917ed9a5aece4829f2c4bef Mon Sep 17 00:00:00 2001 From: James Elliott Date: Sun, 30 Apr 2023 10:52:45 +1000 Subject: [PATCH] fix(middlewares): failure to detect remote ip (#5339) This fixes an edge case where the RemoteIP detection could safely fail with an error, and instead defaults to the TCP packet information. Signed-off-by: James Elliott --- internal/middlewares/authelia_context.go | 11 ++++--- internal/middlewares/authelia_context_test.go | 32 +++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/internal/middlewares/authelia_context.go b/internal/middlewares/authelia_context.go index c4e6d8b1d..7f83374e4 100644 --- a/internal/middlewares/authelia_context.go +++ b/internal/middlewares/authelia_context.go @@ -452,12 +452,13 @@ func (ctx *AutheliaCtx) SetJSONBody(value any) error { // RemoteIP return the remote IP taking X-Forwarded-For header into account if provided. func (ctx *AutheliaCtx) RemoteIP() net.IP { - XForwardedFor := ctx.Request.Header.PeekBytes(headerXForwardedFor) - if XForwardedFor != nil { - ips := strings.Split(string(XForwardedFor), ",") + if header := ctx.Request.Header.PeekBytes(headerXForwardedFor); len(header) != 0 { + ips := strings.SplitN(string(header), ",", 2) - if len(ips) > 0 { - return net.ParseIP(strings.Trim(ips[0], " ")) + if len(ips) != 0 { + if ip := net.ParseIP(strings.Trim(ips[0], " ")); ip != nil { + return ip + } } } diff --git a/internal/middlewares/authelia_context_test.go b/internal/middlewares/authelia_context_test.go index 6b263a46b..bd8d0a50e 100644 --- a/internal/middlewares/authelia_context_test.go +++ b/internal/middlewares/authelia_context_test.go @@ -1,6 +1,7 @@ package middlewares_test import ( + "net" "net/url" "testing" @@ -17,6 +18,37 @@ import ( "github.com/authelia/authelia/v4/internal/session" ) +func TestAutheliaCtx_RemoteIP(t *testing.T) { + testCases := []struct { + name string + have []byte + expected net.IP + }{ + {"ShouldDefaultToRemoteAddr", nil, net.ParseIP("127.0.0.127")}, + {"ShouldParseProperlyFormattedXFFWithIPv4", []byte("192.168.1.1, 127.0.0.1"), net.ParseIP("192.168.1.1")}, + {"ShouldParseProperlyFormattedXFFWithIPv6", []byte("2001:db8:85a3:8d3:1319:8a2e:370:7348, 127.0.0.1"), net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348")}, + {"ShouldFallbackToRemoteAddrOnImproperlyFormattedXFFWithIPv6", []byte("[2001:db8:85a3:8d3:1319:8a2e:370:7348], 127.0.0.1"), net.ParseIP("127.0.0.127")}, + {"ShouldFallbackToRemoteAddrOnBlankXFFHeader", []byte(""), net.ParseIP("127.0.0.127")}, + {"ShouldFallbackToRemoteAddrOnBlankXFFEntry", []byte(", 127.0.0.1"), net.ParseIP("127.0.0.127")}, + {"ShouldFallbackToRemoteAddrOnBadXFFEntry", []byte("abc, 127.0.0.1"), net.ParseIP("127.0.0.127")}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + mock := mocks.NewMockAutheliaCtx(t) + defer mock.Close() + + mock.Ctx.SetRemoteAddr(&net.TCPAddr{Port: 80, IP: net.ParseIP("127.0.0.127")}) + + if tc.have != nil { + mock.Ctx.RequestCtx.Request.Header.SetBytesV(fasthttp.HeaderXForwardedFor, tc.have) + } + + assert.Equal(t, tc.expected, mock.Ctx.RemoteIP()) + }) + } +} + func TestContentTypes(t *testing.T) { testCases := []struct { name string