Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package proxyproto
import (
"fmt"
"net"
"net/netip"
"strings"
)

Expand Down Expand Up @@ -340,12 +341,14 @@ func ipFromAddr(upstream net.Addr) (net.IP, error) {
return nil, err
}

upstreamIP := net.ParseIP(upstreamString)
if nil == upstreamIP {
upstreamIP, err := netip.ParseAddr(upstreamString)
if err != nil {
return nil, fmt.Errorf("proxyproto: invalid IP address")
}

return upstreamIP, nil
// Policies compare IPs, so the interface zone of a scoped IPv6 socket
// address must not prevent matching the configured IP or CIDR.
return net.IP(upstreamIP.AsSlice()), nil
}

// TrustProxyHeaderFrom returns a ConnPolicyFunc implementing the spec's
Expand Down
56 changes: 56 additions & 0 deletions policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,3 +521,59 @@ func TestPolicyFromRanges(t *testing.T) {
}
}
}

func TestPoliciesWithIPv6Zone(t *testing.T) {
trustedIP := net.ParseIP("fe80::1")
_, trustedRange, err := net.ParseCIDR("fe80::/64")
if err != nil {
t.Fatal(err)
}
trustRanges, err := TrustProxyHeaderFromRanges([]string{"fe80::1/128"})
if err != nil {
t.Fatal(err)
}
policies := []struct {
name string
policy ConnPolicyFunc
matched Policy
unmatched Policy
denyUnmatched bool
}{
{"trusted IP", TrustProxyHeaderFrom(trustedIP), REQUIRE, REJECT, true},
{"trusted range", trustRanges, REQUIRE, REJECT, true},
{"range policy", MustPolicyFromRanges([]string{"fe80::1/128"}, USE, IGNORE), USE, IGNORE, false},
{"skip CIDR", ConnSkipProxyHeaderForCIDR(trustedRange, REJECT), SKIP, REJECT, false},
{"local interface", IgnoreProxyHeaderNotOnInterface(trustedIP), USE, IGNORE, false},
}
for _, tc := range policies {
t.Run(tc.name, func(t *testing.T) {
for _, network := range []string{"stream", "datagram"} {
t.Run(network, func(t *testing.T) {
for _, matched := range []bool{true, false} {
ip := trustedIP
want := tc.matched
if !matched {
ip = net.ParseIP("fe81::1")
want = tc.unmatched
}
var addr net.Addr = &net.TCPAddr{IP: ip, Port: 1234, Zone: "eth0"}
if network == "datagram" {
addr = &net.UDPAddr{IP: ip, Port: 1234, Zone: "eth0"}
}
got, err := tc.policy(ConnPolicyOptions{Upstream: addr, Downstream: addr})
if !matched && tc.denyUnmatched {
if !errors.Is(err, ErrInvalidUpstream) {
t.Fatalf("expected untrusted peer error, got %v", err)
}
} else if err != nil {
t.Fatalf("unexpected policy error for %s: %v", addr, err)
}
if got != want {
t.Fatalf("policy for %s: got %v, want %v", addr, got, want)
}
}
})
}
})
}
}
Loading