@@ -459,6 +459,7 @@ func TestExtractIPDirect(t *testing.T) {
459
459
460
460
func TestExtractIPFromRealIPHeader (t * testing.T ) {
461
461
_ , ipForRemoteAddrExternalRange , _ := net .ParseCIDR ("203.0.113.199/24" )
462
+ _ , ipv6ForRemoteAddrExternalRange , _ := net .ParseCIDR ("2001:db8::/64" )
462
463
463
464
var testCases = []struct {
464
465
name string
@@ -493,6 +494,16 @@ func TestExtractIPFromRealIPHeader(t *testing.T) {
493
494
},
494
495
expectIP : "203.0.113.1" ,
495
496
},
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
+ },
496
507
{
497
508
name : "request is from external IP has valid + TRUSTED X-Real-Ip header, extract IP from X-Real-Ip header" ,
498
509
givenTrustOptions : []TrustOption { // case for "trust direct-facing proxy"
@@ -506,6 +517,19 @@ func TestExtractIPFromRealIPHeader(t *testing.T) {
506
517
},
507
518
expectIP : "203.0.113.199" ,
508
519
},
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
+ },
509
533
{
510
534
name : "request is from external IP has XFF and valid + TRUSTED X-Real-Ip header, extract IP from X-Real-Ip header" ,
511
535
givenTrustOptions : []TrustOption { // case for "trust direct-facing proxy"
@@ -520,6 +544,20 @@ func TestExtractIPFromRealIPHeader(t *testing.T) {
520
544
},
521
545
expectIP : "203.0.113.199" ,
522
546
},
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
+ },
523
561
}
524
562
525
563
for _ , tc := range testCases {
@@ -532,6 +570,7 @@ func TestExtractIPFromRealIPHeader(t *testing.T) {
532
570
533
571
func TestExtractIPFromXFFHeader (t * testing.T ) {
534
572
_ , ipForRemoteAddrExternalRange , _ := net .ParseCIDR ("203.0.113.199/24" )
573
+ _ , ipv6ForRemoteAddrExternalRange , _ := net .ParseCIDR ("2001:db8::/64" )
535
574
536
575
var testCases = []struct {
537
576
name string
@@ -566,6 +605,16 @@ func TestExtractIPFromXFFHeader(t *testing.T) {
566
605
},
567
606
expectIP : "127.0.0.3" ,
568
607
},
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
+ },
569
618
{
570
619
name : "request is from external IP has valid + UNTRUSTED external XFF header, extract IP from remote addr" ,
571
620
whenRequest : http.Request {
@@ -576,6 +625,16 @@ func TestExtractIPFromXFFHeader(t *testing.T) {
576
625
},
577
626
expectIP : "203.0.113.1" ,
578
627
},
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
+ },
579
638
{
580
639
name : "request is from external IP is valid and has some IPs TRUSTED XFF header, extract IP from XFF header" ,
581
640
givenTrustOptions : []TrustOption {
@@ -595,6 +654,25 @@ func TestExtractIPFromXFFHeader(t *testing.T) {
595
654
},
596
655
expectIP : "203.0.100.100" , // this is first trusted IP in XFF chain
597
656
},
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
+ },
598
676
}
599
677
600
678
for _ , tc := range testCases {
0 commit comments