@@ -459,6 +459,7 @@ func TestExtractIPDirect(t *testing.T) {
459459
460460func TestExtractIPFromRealIPHeader (t * testing.T ) {
461461 _ , ipForRemoteAddrExternalRange , _ := net .ParseCIDR ("203.0.113.199/24" )
462+ _ , ipv6ForRemoteAddrExternalRange , _ := net .ParseCIDR ("2001:db8::/64" )
462463
463464 var testCases = []struct {
464465 name string
@@ -493,6 +494,16 @@ func TestExtractIPFromRealIPHeader(t *testing.T) {
493494 },
494495 expectIP : "203.0.113.1" ,
495496 },
497+ {
498+ name : "request is from external IP has valid + UNTRUSTED external X-Real-Ip header, extract IP from remote addr" ,
499+ whenRequest : http.Request {
500+ Header : http.Header {
501+ HeaderXRealIP : []string {"[2001:db8::113:199]" }, // <-- this is untrusted
502+ },
503+ RemoteAddr : "[2001:db8::113:1]:8080" ,
504+ },
505+ expectIP : "2001:db8::113:1" ,
506+ },
496507 {
497508 name : "request is from external IP has valid + TRUSTED X-Real-Ip header, extract IP from X-Real-Ip header" ,
498509 givenTrustOptions : []TrustOption { // case for "trust direct-facing proxy"
@@ -506,6 +517,19 @@ func TestExtractIPFromRealIPHeader(t *testing.T) {
506517 },
507518 expectIP : "203.0.113.199" ,
508519 },
520+ {
521+ name : "request is from external IP has valid + TRUSTED X-Real-Ip header, extract IP from X-Real-Ip header" ,
522+ givenTrustOptions : []TrustOption { // case for "trust direct-facing proxy"
523+ TrustIPRange (ipv6ForRemoteAddrExternalRange ), // we trust external IP range "2001:db8::/64"
524+ },
525+ whenRequest : http.Request {
526+ Header : http.Header {
527+ HeaderXRealIP : []string {"[2001:db8::113:199]" },
528+ },
529+ RemoteAddr : "[2001:db8::113:1]:8080" ,
530+ },
531+ expectIP : "2001:db8::113:199" ,
532+ },
509533 {
510534 name : "request is from external IP has XFF and valid + TRUSTED X-Real-Ip header, extract IP from X-Real-Ip header" ,
511535 givenTrustOptions : []TrustOption { // case for "trust direct-facing proxy"
@@ -520,6 +544,20 @@ func TestExtractIPFromRealIPHeader(t *testing.T) {
520544 },
521545 expectIP : "203.0.113.199" ,
522546 },
547+ {
548+ name : "request is from external IP has XFF and valid + TRUSTED X-Real-Ip header, extract IP from X-Real-Ip header" ,
549+ givenTrustOptions : []TrustOption { // case for "trust direct-facing proxy"
550+ TrustIPRange (ipv6ForRemoteAddrExternalRange ), // we trust external IP range "2001:db8::/64"
551+ },
552+ whenRequest : http.Request {
553+ Header : http.Header {
554+ HeaderXRealIP : []string {"[2001:db8::113:199]" },
555+ HeaderXForwardedFor : []string {"[2001:db8::113:198], [2001:db8::113:197]" }, // <-- should not affect anything
556+ },
557+ RemoteAddr : "[2001:db8::113:1]:8080" ,
558+ },
559+ expectIP : "2001:db8::113:199" ,
560+ },
523561 }
524562
525563 for _ , tc := range testCases {
@@ -532,6 +570,7 @@ func TestExtractIPFromRealIPHeader(t *testing.T) {
532570
533571func TestExtractIPFromXFFHeader (t * testing.T ) {
534572 _ , ipForRemoteAddrExternalRange , _ := net .ParseCIDR ("203.0.113.199/24" )
573+ _ , ipv6ForRemoteAddrExternalRange , _ := net .ParseCIDR ("2001:db8::/64" )
535574
536575 var testCases = []struct {
537576 name string
@@ -566,6 +605,16 @@ func TestExtractIPFromXFFHeader(t *testing.T) {
566605 },
567606 expectIP : "127.0.0.3" ,
568607 },
608+ {
609+ name : "request trusts all IPs in XFF header, extract IP from furthest in XFF chain" ,
610+ whenRequest : http.Request {
611+ Header : http.Header {
612+ HeaderXForwardedFor : []string {"[fe80::3], [fe80::2], [fe80::1]" },
613+ },
614+ RemoteAddr : "[fe80::1]:8080" ,
615+ },
616+ expectIP : "fe80::3" ,
617+ },
569618 {
570619 name : "request is from external IP has valid + UNTRUSTED external XFF header, extract IP from remote addr" ,
571620 whenRequest : http.Request {
@@ -576,6 +625,16 @@ func TestExtractIPFromXFFHeader(t *testing.T) {
576625 },
577626 expectIP : "203.0.113.1" ,
578627 },
628+ {
629+ name : "request is from external IP has valid + UNTRUSTED external XFF header, extract IP from remote addr" ,
630+ whenRequest : http.Request {
631+ Header : http.Header {
632+ HeaderXForwardedFor : []string {"[2001:db8::1]" }, // <-- this is untrusted
633+ },
634+ RemoteAddr : "[2001:db8::2]:8080" ,
635+ },
636+ expectIP : "2001:db8::2" ,
637+ },
579638 {
580639 name : "request is from external IP is valid and has some IPs TRUSTED XFF header, extract IP from XFF header" ,
581640 givenTrustOptions : []TrustOption {
@@ -595,6 +654,25 @@ func TestExtractIPFromXFFHeader(t *testing.T) {
595654 },
596655 expectIP : "203.0.100.100" , // this is first trusted IP in XFF chain
597656 },
657+ {
658+ name : "request is from external IP is valid and has some IPs TRUSTED XFF header, extract IP from XFF header" ,
659+ givenTrustOptions : []TrustOption {
660+ TrustIPRange (ipv6ForRemoteAddrExternalRange ), // we trust external IP range "2001:db8::/64"
661+ },
662+ // from request its seems that request has been proxied through 6 servers.
663+ // 1) 2001:db8:1::1:100 (this is external IP set by 2001:db8:2::100:100 which we do not trust - could be spoofed)
664+ // 2) 2001:db8:2::100:100 (this is outside of our network but set by 2001:db8::113:199 which we trust to set correct IPs)
665+ // 3) 2001:db8::113:199 (we trust, for example maybe our proxy from some other office)
666+ // 4) fd12:3456:789a:1::1 (internal IP, some internal upstream loadbalancer ala SSL offloading with F5 products)
667+ // 5) fe80::1 (is proxy on localhost. maybe we have Nginx in front of our Echo instance doing some routing)
668+ whenRequest : http.Request {
669+ Header : http.Header {
670+ HeaderXForwardedFor : []string {"[2001:db8:1::1:100], [2001:db8:2::100:100], [2001:db8::113:199], [fd12:3456:789a:1::1]" },
671+ },
672+ RemoteAddr : "[fe80::1]:8080" , // IP of proxy upstream of our APP
673+ },
674+ expectIP : "2001:db8:2::100:100" , // this is first trusted IP in XFF chain
675+ },
598676 }
599677
600678 for _ , tc := range testCases {
0 commit comments