From 182c62493d7fb60b7254accadb788ebfd1c5cdad Mon Sep 17 00:00:00 2001 From: pedram Date: Wed, 1 Nov 2023 16:57:52 +0330 Subject: [PATCH] Implemented check last public ip in Cerberus authenticator (#23) * implement last public ip logic * Change order of remoteAddr and x-forwarded-for --------- Co-authored-by: Pedram Sadeghian --- pkg/auth/authenticator.go | 44 ++++++++++++++++++++++++++++++---- pkg/auth/authenticator_test.go | 2 +- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/pkg/auth/authenticator.go b/pkg/auth/authenticator.go index 903cc8c..f2a36e1 100644 --- a/pkg/auth/authenticator.go +++ b/pkg/auth/authenticator.go @@ -85,6 +85,12 @@ const ( // CerberusReasonBadDomainList means that domain list items are not in valid patterns CerberusReasonBadDomainList CerberusReason = "bad-domain-list" + // CerberusReasonInvalidSourceIp means that source ip in remoteAddre is not valid + CerberusReasonInvalidSourceIp CerberusReason = "invalid-source-ip" + + // CerberusReasonBadIpList means that there is no valid public ip for validation + CerberusReasonNoValidIp CerberusReason = "no-valid-ip" + // CerberusReasonBadIpList means that ip list items are not in valid patterns which is CIDR notation of the networks CerberusReasonBadIpList CerberusReason = "bad-ip-list" @@ -255,10 +261,24 @@ func (a *Authenticator) TestAccess(request *Request, wsvc ServicesCacheEntry) (b defer a.cacheLock.RUnlock() defer cacheReaders.Dec() + ipList := make([]string, 0) + // Retrieve "x-forwarded-for" and "referrer" headers from the request xForwardedFor := request.Request.Header.Get("x-forwarded-for") + if xForwardedFor != "" { + ips := strings.Split(xForwardedFor, ", ") + ipList = append(ipList, ips...) + } referrer := request.Request.Header.Get("referrer") + // Retrieve "remoteAddr" from the requeset + remoteAddr := request.Request.RemoteAddr + host, _, err := net.SplitHostPort(remoteAddr) + if err != nil { + return false, CerberusReasonInvalidSourceIp, newExtraHeaders + } + ipList = append(ipList, host) + if token == "" { return false, CerberusReasonTokenEmpty, newExtraHeaders } @@ -270,8 +290,12 @@ func (a *Authenticator) TestAccess(request *Request, wsvc ServicesCacheEntry) (b } // Check x-forwarded-for header against IP allow list - if len(ac.Spec.IpAllowList) > 0 && xForwardedFor != "" { - ipAllowed, err := CheckIP(xForwardedFor, ac.Spec.IpAllowList) + if len(ac.Spec.IpAllowList) > 0 { + publicIp, err := lastPublicIp(ipList) + if err != nil { + return false, CerberusReasonNoValidIp, newExtraHeaders + } + ipAllowed, err := checkIP(publicIp, ac.Spec.IpAllowList) if err != nil { return false, CerberusReasonBadIpList, newExtraHeaders } @@ -379,9 +403,21 @@ func NewAuthenticator(logger logr.Logger) (*Authenticator, error) { return &a, nil } -// CheckIP checks if given ip is a member of given CIDR networks or not +// lastPublicIp will identify the last valid public IP address within the list of IPs +// will return an error if it cannot find any valid public IP addresses in the input list. +func lastPublicIp(ips []string) (string, error) { + for i := len(ips); i >= 0; i-- { + clientIP := net.ParseIP(ips[i]) + if clientIP != nil && !clientIP.IsPrivate() { + return ips[i], nil + } + } + return "", nil +} + +// checkIP checks if given ip is a member of given CIDR networks or not // ipAllowList should be CIDR notation of the networks or net.ParseError will be retuned -func CheckIP(ip string, ipAllowList []string) (bool, error) { +func checkIP(ip string, ipAllowList []string) (bool, error) { clientIP := net.ParseIP(ip) for _, AllowedRangeIP := range ipAllowList { diff --git a/pkg/auth/authenticator_test.go b/pkg/auth/authenticator_test.go index 7dc74a6..f004544 100644 --- a/pkg/auth/authenticator_test.go +++ b/pkg/auth/authenticator_test.go @@ -35,7 +35,7 @@ func BenchmarkCheckIPWithLargeInput(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, _ = CheckIP(testIP, ipAllowList) + _, _ = checkIP(testIP, ipAllowList) } }