Add trusted whitelist proxy protocol
This commit is contained in:
parent
93a1db77c5
commit
6287a3dd53
9 changed files with 249 additions and 143 deletions
75
whitelist/ip.go
Normal file
75
whitelist/ip.go
Normal file
|
@ -0,0 +1,75 @@
|
|||
package whitelist
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// IP allows to check that addresses are in a white list
|
||||
type IP struct {
|
||||
whiteListsIPs []*net.IP
|
||||
whiteListsNet []*net.IPNet
|
||||
}
|
||||
|
||||
// NewIP builds a new IP given a list of CIDR-Strings to whitelist
|
||||
func NewIP(whitelistStrings []string) (*IP, error) {
|
||||
if len(whitelistStrings) == 0 {
|
||||
return nil, errors.New("no whiteListsNet provided")
|
||||
}
|
||||
|
||||
ip := IP{}
|
||||
|
||||
for _, whitelistString := range whitelistStrings {
|
||||
ipAddr := net.ParseIP(whitelistString)
|
||||
if ipAddr != nil {
|
||||
ip.whiteListsIPs = append(ip.whiteListsIPs, &ipAddr)
|
||||
} else {
|
||||
_, whitelist, err := net.ParseCIDR(whitelistString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing CIDR whitelist %s: %v", whitelist, err)
|
||||
}
|
||||
ip.whiteListsNet = append(ip.whiteListsNet, whitelist)
|
||||
}
|
||||
}
|
||||
|
||||
return &ip, nil
|
||||
}
|
||||
|
||||
// Contains checks if provided address is in the white list
|
||||
func (ip *IP) Contains(addr string) (bool, net.IP, error) {
|
||||
ipAddr, err := ipFromRemoteAddr(addr)
|
||||
if err != nil {
|
||||
return false, nil, fmt.Errorf("unable to parse address: %s: %s", addr, err)
|
||||
}
|
||||
|
||||
contains, err := ip.ContainsIP(ipAddr)
|
||||
return contains, ipAddr, err
|
||||
}
|
||||
|
||||
// ContainsIP checks if provided address is in the white list
|
||||
func (ip *IP) ContainsIP(addr net.IP) (bool, error) {
|
||||
for _, whiteListIP := range ip.whiteListsIPs {
|
||||
if whiteListIP.Equal(addr) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, whiteListNet := range ip.whiteListsNet {
|
||||
if whiteListNet.Contains(addr) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func ipFromRemoteAddr(addr string) (net.IP, error) {
|
||||
userIP := net.ParseIP(addr)
|
||||
if userIP == nil {
|
||||
return nil, fmt.Errorf("can't parse IP from address %s", addr)
|
||||
}
|
||||
|
||||
return userIP, nil
|
||||
}
|
318
whitelist/ip_test.go
Normal file
318
whitelist/ip_test.go
Normal file
|
@ -0,0 +1,318 @@
|
|||
package whitelist
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
whitelistStrings []string
|
||||
expectedWhitelists []*net.IPNet
|
||||
errMessage string
|
||||
}{
|
||||
{
|
||||
desc: "nil whitelist",
|
||||
whitelistStrings: nil,
|
||||
expectedWhitelists: nil,
|
||||
errMessage: "no whiteListsNet provided",
|
||||
}, {
|
||||
desc: "empty whitelist",
|
||||
whitelistStrings: []string{},
|
||||
expectedWhitelists: nil,
|
||||
errMessage: "no whiteListsNet provided",
|
||||
}, {
|
||||
desc: "whitelist containing empty string",
|
||||
whitelistStrings: []string{
|
||||
"1.2.3.4/24",
|
||||
"",
|
||||
"fe80::/16",
|
||||
},
|
||||
expectedWhitelists: nil,
|
||||
errMessage: "parsing CIDR whitelist <nil>: invalid CIDR address: ",
|
||||
}, {
|
||||
desc: "whitelist containing only an empty string",
|
||||
whitelistStrings: []string{
|
||||
"",
|
||||
},
|
||||
expectedWhitelists: nil,
|
||||
errMessage: "parsing CIDR whitelist <nil>: invalid CIDR address: ",
|
||||
}, {
|
||||
desc: "whitelist containing an invalid string",
|
||||
whitelistStrings: []string{
|
||||
"foo",
|
||||
},
|
||||
expectedWhitelists: nil,
|
||||
errMessage: "parsing CIDR whitelist <nil>: invalid CIDR address: foo",
|
||||
}, {
|
||||
desc: "IPv4 & IPv6 whitelist",
|
||||
whitelistStrings: []string{
|
||||
"1.2.3.4/24",
|
||||
"fe80::/16",
|
||||
},
|
||||
expectedWhitelists: []*net.IPNet{
|
||||
{IP: net.IPv4(1, 2, 3, 0).To4(), Mask: net.IPv4Mask(255, 255, 255, 0)},
|
||||
{IP: net.ParseIP("fe80::"), Mask: net.IPMask(net.ParseIP("ffff::"))},
|
||||
},
|
||||
errMessage: "",
|
||||
}, {
|
||||
desc: "IPv4 only",
|
||||
whitelistStrings: []string{
|
||||
"127.0.0.1/8",
|
||||
},
|
||||
expectedWhitelists: []*net.IPNet{
|
||||
{IP: net.IPv4(127, 0, 0, 0).To4(), Mask: net.IPv4Mask(255, 0, 0, 0)},
|
||||
},
|
||||
errMessage: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range cases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
whitelister, err := NewIP(test.whitelistStrings)
|
||||
if test.errMessage != "" {
|
||||
require.EqualError(t, err, test.errMessage)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
for index, actual := range whitelister.whiteListsNet {
|
||||
expected := test.expectedWhitelists[index]
|
||||
assert.Equal(t, expected.IP, actual.IP)
|
||||
assert.Equal(t, expected.Mask.String(), actual.Mask.String())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsAllowed(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
whitelistStrings []string
|
||||
passIPs []string
|
||||
rejectIPs []string
|
||||
}{
|
||||
{
|
||||
desc: "IPv4",
|
||||
whitelistStrings: []string{
|
||||
"1.2.3.4/24",
|
||||
},
|
||||
passIPs: []string{
|
||||
"1.2.3.1",
|
||||
"1.2.3.32",
|
||||
"1.2.3.156",
|
||||
"1.2.3.255",
|
||||
},
|
||||
rejectIPs: []string{
|
||||
"1.2.16.1",
|
||||
"1.2.32.1",
|
||||
"127.0.0.1",
|
||||
"8.8.8.8",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "IPv4 single IP",
|
||||
whitelistStrings: []string{
|
||||
"8.8.8.8",
|
||||
},
|
||||
passIPs: []string{
|
||||
"8.8.8.8",
|
||||
},
|
||||
rejectIPs: []string{
|
||||
"8.8.8.7",
|
||||
"8.8.8.9",
|
||||
"8.8.8.0",
|
||||
"8.8.8.255",
|
||||
"4.4.4.4",
|
||||
"127.0.0.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "IPv4 Net single IP",
|
||||
whitelistStrings: []string{
|
||||
"8.8.8.8/32",
|
||||
},
|
||||
passIPs: []string{
|
||||
"8.8.8.8",
|
||||
},
|
||||
rejectIPs: []string{
|
||||
"8.8.8.7",
|
||||
"8.8.8.9",
|
||||
"8.8.8.0",
|
||||
"8.8.8.255",
|
||||
"4.4.4.4",
|
||||
"127.0.0.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "multiple IPv4",
|
||||
whitelistStrings: []string{
|
||||
"1.2.3.4/24",
|
||||
"8.8.8.8/8",
|
||||
},
|
||||
passIPs: []string{
|
||||
"1.2.3.1",
|
||||
"1.2.3.32",
|
||||
"1.2.3.156",
|
||||
"1.2.3.255",
|
||||
"8.8.4.4",
|
||||
"8.0.0.1",
|
||||
"8.32.42.128",
|
||||
"8.255.255.255",
|
||||
},
|
||||
rejectIPs: []string{
|
||||
"1.2.16.1",
|
||||
"1.2.32.1",
|
||||
"127.0.0.1",
|
||||
"4.4.4.4",
|
||||
"4.8.8.8",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "IPv6",
|
||||
whitelistStrings: []string{
|
||||
"2a03:4000:6:d080::/64",
|
||||
},
|
||||
passIPs: []string{
|
||||
"2a03:4000:6:d080::",
|
||||
"2a03:4000:6:d080::1",
|
||||
"2a03:4000:6:d080:dead:beef:ffff:ffff",
|
||||
"2a03:4000:6:d080::42",
|
||||
},
|
||||
rejectIPs: []string{
|
||||
"2a03:4000:7:d080::",
|
||||
"2a03:4000:7:d080::1",
|
||||
"fe80::",
|
||||
"4242::1",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "IPv6 single IP",
|
||||
whitelistStrings: []string{
|
||||
"2a03:4000:6:d080::42/128",
|
||||
},
|
||||
passIPs: []string{
|
||||
"2a03:4000:6:d080::42",
|
||||
},
|
||||
rejectIPs: []string{
|
||||
"2a03:4000:6:d080::1",
|
||||
"2a03:4000:6:d080:dead:beef:ffff:ffff",
|
||||
"2a03:4000:6:d080::43",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "multiple IPv6",
|
||||
whitelistStrings: []string{
|
||||
"2a03:4000:6:d080::/64",
|
||||
"fe80::/16",
|
||||
},
|
||||
passIPs: []string{
|
||||
"2a03:4000:6:d080::",
|
||||
"2a03:4000:6:d080::1",
|
||||
"2a03:4000:6:d080:dead:beef:ffff:ffff",
|
||||
"2a03:4000:6:d080::42",
|
||||
"fe80::1",
|
||||
"fe80:aa00:00bb:4232:ff00:eeee:00ff:1111",
|
||||
"fe80::fe80",
|
||||
},
|
||||
rejectIPs: []string{
|
||||
"2a03:4000:7:d080::",
|
||||
"2a03:4000:7:d080::1",
|
||||
"4242::1",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "multiple IPv6 & IPv4",
|
||||
whitelistStrings: []string{
|
||||
"2a03:4000:6:d080::/64",
|
||||
"fe80::/16",
|
||||
"1.2.3.4/24",
|
||||
"8.8.8.8/8",
|
||||
},
|
||||
passIPs: []string{
|
||||
"2a03:4000:6:d080::",
|
||||
"2a03:4000:6:d080::1",
|
||||
"2a03:4000:6:d080:dead:beef:ffff:ffff",
|
||||
"2a03:4000:6:d080::42",
|
||||
"fe80::1",
|
||||
"fe80:aa00:00bb:4232:ff00:eeee:00ff:1111",
|
||||
"fe80::fe80",
|
||||
"1.2.3.1",
|
||||
"1.2.3.32",
|
||||
"1.2.3.156",
|
||||
"1.2.3.255",
|
||||
"8.8.4.4",
|
||||
"8.0.0.1",
|
||||
"8.32.42.128",
|
||||
"8.255.255.255",
|
||||
},
|
||||
rejectIPs: []string{
|
||||
"2a03:4000:7:d080::",
|
||||
"2a03:4000:7:d080::1",
|
||||
"4242::1",
|
||||
"1.2.16.1",
|
||||
"1.2.32.1",
|
||||
"127.0.0.1",
|
||||
"4.4.4.4",
|
||||
"4.8.8.8",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "broken IP-addresses",
|
||||
whitelistStrings: []string{
|
||||
"127.0.0.1/32",
|
||||
},
|
||||
passIPs: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range cases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
whiteLister, err := NewIP(test.whitelistStrings)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, whiteLister)
|
||||
|
||||
for _, testIP := range test.passIPs {
|
||||
allowed, ip, err := whiteLister.Contains(testIP)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, ip, err)
|
||||
assert.True(t, allowed, testIP+" should have passed "+test.desc)
|
||||
}
|
||||
|
||||
for _, testIP := range test.rejectIPs {
|
||||
allowed, ip, err := whiteLister.Contains(testIP)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, ip, err)
|
||||
assert.False(t, allowed, testIP+" should not have passed "+test.desc)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBrokenIPs(t *testing.T) {
|
||||
brokenIPs := []string{
|
||||
"foo",
|
||||
"10.0.0.350",
|
||||
"fe:::80",
|
||||
"",
|
||||
"\\&$§&/(",
|
||||
}
|
||||
|
||||
whiteLister, err := NewIP([]string{"1.2.3.4/24"})
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, testIP := range brokenIPs {
|
||||
_, ip, err := whiteLister.Contains(testIP)
|
||||
assert.Error(t, err)
|
||||
require.Nil(t, ip, err)
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue