From 6287a3dd539866e29e47b1f626a2f24caa642f18 Mon Sep 17 00:00:00 2001 From: Emile Vauge Date: Tue, 10 Oct 2017 14:50:03 +0200 Subject: [PATCH] Add trusted whitelist proxy protocol --- .../anonymize/anonymize_config_test.go | 8 +- configuration/configuration.go | 20 ++- configuration/configuration_test.go | 20 +-- docs/configuration/entrypoints.md | 8 +- middlewares/ip_whitelister.go | 76 ++++----- server/server.go | 20 ++- server/server_test.go | 4 +- whitelist/ip.go | 75 ++++++++ .../ip_test.go | 161 ++++++++++-------- 9 files changed, 249 insertions(+), 143 deletions(-) create mode 100644 whitelist/ip.go rename middlewares/ip_whitelister_test.go => whitelist/ip_test.go (65%) diff --git a/cmd/traefik/anonymize/anonymize_config_test.go b/cmd/traefik/anonymize/anonymize_config_test.go index 43b0844d2..3ec49cddd 100644 --- a/cmd/traefik/anonymize/anonymize_config_test.go +++ b/cmd/traefik/anonymize/anonymize_config_test.go @@ -84,7 +84,9 @@ func TestDo_globalConfiguration(t *testing.T) { }, WhitelistSourceRange: []string{"foo WhitelistSourceRange 1", "foo WhitelistSourceRange 2", "foo WhitelistSourceRange 3"}, Compress: true, - ProxyProtocol: true, + ProxyProtocol: &configuration.ProxyProtocol{ + TrustedIPs: []string{"127.0.0.1/32", "192.168.0.1"}, + }, }, "fii": { Network: "fii Network", @@ -125,7 +127,9 @@ func TestDo_globalConfiguration(t *testing.T) { }, WhitelistSourceRange: []string{"fii WhitelistSourceRange 1", "fii WhitelistSourceRange 2", "fii WhitelistSourceRange 3"}, Compress: true, - ProxyProtocol: true, + ProxyProtocol: &configuration.ProxyProtocol{ + TrustedIPs: []string{"127.0.0.1/32", "192.168.0.1"}, + }, }, } config.Cluster = &types.Cluster{ diff --git a/configuration/configuration.go b/configuration/configuration.go index f3e91fbd0..b112af7e6 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -231,7 +231,14 @@ func (ep *EntryPoints) Set(value string) error { } compress := toBool(result, "Compress") - proxyProtocol := toBool(result, "ProxyProtocol") + + var proxyProtocol *ProxyProtocol + if len(result["ProxyProtocol"]) > 0 { + trustedIPs := strings.Split(result["ProxyProtocol"], ",") + proxyProtocol = &ProxyProtocol{ + TrustedIPs: trustedIPs, + } + } (*ep)[result["Name"]] = &EntryPoint{ Address: result["Address"], @@ -246,7 +253,7 @@ func (ep *EntryPoints) Set(value string) error { } func parseEntryPointsConfiguration(value string) (map[string]string, error) { - regex := regexp.MustCompile(`(?:Name:(?P\S*))\s*(?:Address:(?P
\S*))?\s*(?:TLS:(?P\S*))?\s*(?PTLS)?\s*(?:CA:(?P\S*))?\s*(?:Redirect\.EntryPoint:(?P\S*))?\s*(?:Redirect\.Regex:(?P\S*))?\s*(?:Redirect\.Replacement:(?P\S*))?\s*(?:Compress:(?P\S*))?\s*(?:WhiteListSourceRange:(?P\S*))?\s*(?:ProxyProtocol:(?P\S*))?`) + regex := regexp.MustCompile(`(?:Name:(?P\S*))\s*(?:Address:(?P
\S*))?\s*(?:TLS:(?P\S*))?\s*(?PTLS)?\s*(?:CA:(?P\S*))?\s*(?:Redirect\.EntryPoint:(?P\S*))?\s*(?:Redirect\.Regex:(?P\S*))?\s*(?:Redirect\.Replacement:(?P\S*))?\s*(?:Compress:(?P\S*))?\s*(?:WhiteListSourceRange:(?P\S*))?\s*(?:ProxyProtocol\.TrustedIPs:(?P\S*))?`) match := regex.FindAllStringSubmatch(value, -1) if match == nil { return nil, fmt.Errorf("bad EntryPoints format: %s", value) @@ -293,8 +300,8 @@ type EntryPoint struct { Redirect *Redirect `export:"true"` Auth *types.Auth `export:"true"` WhitelistSourceRange []string - Compress bool `export:"true"` - ProxyProtocol bool `export:"true"` + Compress bool `export:"true"` + ProxyProtocol *ProxyProtocol `export:"true"` } // Redirect configures a redirection of an entry point to another, or to an URL @@ -443,3 +450,8 @@ type ForwardingTimeouts struct { DialTimeout flaeg.Duration `description:"The amount of time to wait until a connection to a backend server can be established. Defaults to 30 seconds. If zero, no timeout exists" export:"true"` ResponseHeaderTimeout flaeg.Duration `description:"The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists" export:"true"` } + +// ProxyProtocol contains Proxy-Protocol configuration +type ProxyProtocol struct { + TrustedIPs []string +} diff --git a/configuration/configuration_test.go b/configuration/configuration_test.go index 5d476e4df..591e32d91 100644 --- a/configuration/configuration_test.go +++ b/configuration/configuration_test.go @@ -16,7 +16,7 @@ func Test_parseEntryPointsConfiguration(t *testing.T) { }{ { name: "all parameters", - value: "Name:foo Address:bar TLS:goo TLS CA:car Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:WhiteListSourceRange ProxyProtocol:true", + value: "Name:foo Address:bar TLS:goo TLS CA:car Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:WhiteListSourceRange ProxyProtocol.TrustedIPs:192.168.0.1", expectedResult: map[string]string{ "Name": "foo", "Address": "bar", @@ -27,18 +27,10 @@ func Test_parseEntryPointsConfiguration(t *testing.T) { "RedirectRegex": "RedirectRegex", "RedirectReplacement": "RedirectReplacement", "WhiteListSourceRange": "WhiteListSourceRange", - "ProxyProtocol": "true", + "ProxyProtocol": "192.168.0.1", "Compress": "true", }, }, - { - name: "proxy protocol on", - value: "Name:foo ProxyProtocol:on", - expectedResult: map[string]string{ - "Name": "foo", - "ProxyProtocol": "on", - }, - }, { name: "compress on", value: "Name:foo Compress:on", @@ -142,7 +134,7 @@ func TestEntryPoints_Set(t *testing.T) { }{ { name: "all parameters", - expression: "Name:foo Address:bar TLS:goo,gii TLS CA:car Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:Range ProxyProtocol:true", + expression: "Name:foo Address:bar TLS:goo,gii TLS CA:car Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:Range ProxyProtocol.TrustedIPs:192.168.0.1", expectedEntryPointName: "foo", expectedEntryPoint: &EntryPoint{ Address: "bar", @@ -151,8 +143,10 @@ func TestEntryPoints_Set(t *testing.T) { Regex: "RedirectRegex", Replacement: "RedirectReplacement", }, - Compress: true, - ProxyProtocol: true, + Compress: true, + ProxyProtocol: &ProxyProtocol{ + TrustedIPs: []string{"192.168.0.1"}, + }, WhitelistSourceRange: []string{"Range"}, TLS: &TLS{ ClientCAFiles: []string{"car"}, diff --git a/docs/configuration/entrypoints.md b/docs/configuration/entrypoints.md index 9fe9799ff..3defd7fd5 100644 --- a/docs/configuration/entrypoints.md +++ b/docs/configuration/entrypoints.md @@ -185,16 +185,20 @@ To enable IP whitelisting at the entrypoint level. [entryPoints] [entryPoints.http] address = ":80" - whiteListSourceRange = ["127.0.0.1/32"] + whiteListSourceRange = ["127.0.0.1/32", "192.168.1.7"] ``` ## ProxyProtocol Support To enable [ProxyProtocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) support. +Only IPs in `trustedIPs` will lead to remote client address replacement: you should declare your load-balancer IP or CIDR range here. + ```toml [entryPoints] [entryPoints.http] address = ":80" - proxyprotocol = true + [entryPoints.http.proxyProtocol] + trustedIPs = ["127.0.0.1/32", "192.168.1.7"] ``` +² \ No newline at end of file diff --git a/middlewares/ip_whitelister.go b/middlewares/ip_whitelister.go index 847762236..490986c29 100644 --- a/middlewares/ip_whitelister.go +++ b/middlewares/ip_whitelister.go @@ -6,80 +6,70 @@ import ( "net/http" "github.com/containous/traefik/log" + "github.com/containous/traefik/whitelist" "github.com/pkg/errors" "github.com/urfave/negroni" ) -// IPWhitelister is a middleware that provides Checks of the Requesting IP against a set of Whitelists -type IPWhitelister struct { - handler negroni.Handler - whitelists []*net.IPNet +// IPWhiteLister is a middleware that provides Checks of the Requesting IP against a set of Whitelists +type IPWhiteLister struct { + handler negroni.Handler + whiteLister *whitelist.IP } -// NewIPWhitelister builds a new IPWhitelister given a list of CIDR-Strings to whitelist -func NewIPWhitelister(whitelistStrings []string) (*IPWhitelister, error) { +// NewIPWhitelister builds a new IPWhiteLister given a list of CIDR-Strings to whitelist +func NewIPWhitelister(whitelistStrings []string) (*IPWhiteLister, error) { if len(whitelistStrings) == 0 { return nil, errors.New("no whitelists provided") } - whitelister := IPWhitelister{} + whiteLister := IPWhiteLister{} - for _, whitelistString := range whitelistStrings { - _, whitelist, err := net.ParseCIDR(whitelistString) - if err != nil { - return nil, fmt.Errorf("parsing CIDR whitelist %s: %v", whitelist, err) - } - whitelister.whitelists = append(whitelister.whitelists, whitelist) + ip, err := whitelist.NewIP(whitelistStrings) + if err != nil { + return nil, fmt.Errorf("parsing CIDR whitelist %s: %v", whitelistStrings, err) } + whiteLister.whiteLister = ip - whitelister.handler = negroni.HandlerFunc(whitelister.handle) - log.Debugf("configured %u IP whitelists: %s", len(whitelister.whitelists), whitelister.whitelists) + whiteLister.handler = negroni.HandlerFunc(whiteLister.handle) + log.Debugf("configured %u IP whitelists: %s", len(whitelistStrings), whitelistStrings) - return &whitelister, nil + return &whiteLister, nil } -func (whitelister *IPWhitelister) handle(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { - remoteIP, err := ipFromRemoteAddr(r.RemoteAddr) +func (wl *IPWhiteLister) handle(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + ipAddress, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { log.Warnf("unable to parse remote-address from header: %s - rejecting", r.RemoteAddr) reject(w) return } - for _, whitelist := range whitelister.whitelists { - if whitelist.Contains(*remoteIP) { - log.Debugf("source-IP %s matched whitelist %s - passing", remoteIP, whitelist) - next.ServeHTTP(w, r) - return - } + allowed, ip, err := wl.whiteLister.Contains(ipAddress) + if err != nil { + log.Debugf("source-IP %s matched none of the whitelists - rejecting", ipAddress) + reject(w) + return } - log.Debugf("source-IP %s matched none of the whitelists - rejecting", remoteIP) + if allowed { + log.Debugf("source-IP %s matched whitelist %s - passing", ipAddress, wl.whiteLister) + next.ServeHTTP(w, r) + return + } + + log.Debugf("source-IP %s matched none of the whitelists - rejecting", ip) reject(w) } +func (wl *IPWhiteLister) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + wl.handler.ServeHTTP(rw, r, next) +} + func reject(w http.ResponseWriter) { statusCode := http.StatusForbidden w.WriteHeader(statusCode) w.Write([]byte(http.StatusText(statusCode))) } - -func ipFromRemoteAddr(addr string) (*net.IP, error) { - ip, _, err := net.SplitHostPort(addr) - if err != nil { - return nil, fmt.Errorf("can't extract IP/Port from address %s: %s", addr, err) - } - - userIP := net.ParseIP(ip) - if userIP == nil { - return nil, fmt.Errorf("can't parse IP from address %s", ip) - } - - return &userIP, nil -} - -func (whitelister *IPWhitelister) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { - whitelister.handler.ServeHTTP(rw, r, next) -} diff --git a/server/server.go b/server/server.go index 25b990a52..24360d967 100644 --- a/server/server.go +++ b/server/server.go @@ -6,6 +6,7 @@ import ( "crypto/x509" "encoding/json" "errors" + "fmt" "io/ioutil" "net" "net/http" @@ -32,6 +33,7 @@ import ( "github.com/containous/traefik/safe" "github.com/containous/traefik/server/cookie" "github.com/containous/traefik/types" + "github.com/containous/traefik/whitelist" "github.com/streamrail/concurrent-map" thoas_stats "github.com/thoas/stats" "github.com/urfave/negroni" @@ -652,8 +654,22 @@ func (server *Server) prepareServer(entryPointName string, entryPoint *configura return nil, nil, err } - if entryPoint.ProxyProtocol { - listener = &proxyproto.Listener{Listener: listener} + if entryPoint.ProxyProtocol != nil { + IPs, err := whitelist.NewIP(entryPoint.ProxyProtocol.TrustedIPs) + if err != nil { + return nil, nil, fmt.Errorf("Error creating whitelist: %s", err) + } + log.Infof("Enabling ProxyProtocol for trusted IPs %v", entryPoint.ProxyProtocol.TrustedIPs) + listener = &proxyproto.Listener{ + Listener: listener, + SourceCheck: func(addr net.Addr) (bool, error) { + ip, ok := addr.(*net.TCPAddr) + if !ok { + return false, fmt.Errorf("Type error %v", addr) + } + return IPs.ContainsIP(ip.IP) + }, + } } return &http.Server{ diff --git a/server/server_test.go b/server/server_test.go index 9fc83bae2..a5ec9b091 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -355,7 +355,7 @@ func TestNewServerWithWhitelistSourceRange(t *testing.T) { "foo", }, middlewareConfigured: false, - errMessage: "parsing CIDR whitelist : invalid CIDR address: foo", + errMessage: "parsing CIDR whitelist [foo]: parsing CIDR whitelist : invalid CIDR address: foo", }, } @@ -536,7 +536,7 @@ func TestServerEntrypointWhitelistConfig(t *testing.T) { handler := srvEntryPoint.httpServer.Handler.(*negroni.Negroni) found := false for _, handler := range handler.Handlers() { - if reflect.TypeOf(handler) == reflect.TypeOf((*middlewares.IPWhitelister)(nil)) { + if reflect.TypeOf(handler) == reflect.TypeOf((*middlewares.IPWhiteLister)(nil)) { found = true } } diff --git a/whitelist/ip.go b/whitelist/ip.go new file mode 100644 index 000000000..af3b691e7 --- /dev/null +++ b/whitelist/ip.go @@ -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 +} diff --git a/middlewares/ip_whitelister_test.go b/whitelist/ip_test.go similarity index 65% rename from middlewares/ip_whitelister_test.go rename to whitelist/ip_test.go index 939af88bd..46ab7142f 100644 --- a/middlewares/ip_whitelister_test.go +++ b/whitelist/ip_test.go @@ -1,19 +1,14 @@ -package middlewares +package whitelist import ( - "fmt" "net" - "net/http" - "net/http/httptest" "testing" - "github.com/containous/traefik/testhelpers" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/urfave/negroni" ) -func TestNewIPWhitelister(t *testing.T) { +func TestNew(t *testing.T) { cases := []struct { desc string whitelistStrings []string @@ -24,12 +19,12 @@ func TestNewIPWhitelister(t *testing.T) { desc: "nil whitelist", whitelistStrings: nil, expectedWhitelists: nil, - errMessage: "no whitelists provided", + errMessage: "no whiteListsNet provided", }, { desc: "empty whitelist", whitelistStrings: []string{}, expectedWhitelists: nil, - errMessage: "no whitelists provided", + errMessage: "no whiteListsNet provided", }, { desc: "whitelist containing empty string", whitelistStrings: []string{ @@ -80,12 +75,12 @@ func TestNewIPWhitelister(t *testing.T) { test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() - whitelister, err := NewIPWhitelister(test.whitelistStrings) + whitelister, err := NewIP(test.whitelistStrings) if test.errMessage != "" { require.EqualError(t, err, test.errMessage) } else { require.NoError(t, err) - for index, actual := range whitelister.whitelists { + 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()) @@ -95,7 +90,7 @@ func TestNewIPWhitelister(t *testing.T) { } } -func TestIPWhitelisterHandle(t *testing.T) { +func TestIsAllowed(t *testing.T) { cases := []struct { desc string whitelistStrings []string @@ -122,6 +117,23 @@ func TestIPWhitelisterHandle(t *testing.T) { }, { 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", }, @@ -167,16 +179,16 @@ func TestIPWhitelisterHandle(t *testing.T) { "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]", + "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]", + "2a03:4000:7:d080::", + "2a03:4000:7:d080::1", + "fe80::", + "4242::1", }, }, { @@ -185,12 +197,12 @@ func TestIPWhitelisterHandle(t *testing.T) { "2a03:4000:6:d080::42/128", }, passIPs: []string{ - "[2a03:4000:6:d080::42]", + "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]", + "2a03:4000:6:d080::1", + "2a03:4000:6:d080:dead:beef:ffff:ffff", + "2a03:4000:6:d080::43", }, }, { @@ -200,18 +212,18 @@ func TestIPWhitelisterHandle(t *testing.T) { "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]", + "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]", + "2a03:4000:7:d080::", + "2a03:4000:7:d080::1", + "4242::1", }, }, { @@ -223,13 +235,13 @@ func TestIPWhitelisterHandle(t *testing.T) { "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]", + "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", @@ -240,9 +252,9 @@ func TestIPWhitelisterHandle(t *testing.T) { "8.255.255.255", }, rejectIPs: []string{ - "[2a03:4000:7:d080::]", - "[2a03:4000:7:d080::1]", - "[4242::1]", + "2a03:4000:7:d080::", + "2a03:4000:7:d080::1", + "4242::1", "1.2.16.1", "1.2.32.1", "127.0.0.1", @@ -256,13 +268,6 @@ func TestIPWhitelisterHandle(t *testing.T) { "127.0.0.1/32", }, passIPs: nil, - rejectIPs: []string{ - "foo", - "10.0.0.350", - "fe:::80", - "", - "\\&$§&/(", - }, }, } @@ -270,38 +275,44 @@ func TestIPWhitelisterHandle(t *testing.T) { test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() - whitelister, err := NewIPWhitelister(test.whitelistStrings) + whiteLister, err := NewIP(test.whitelistStrings) require.NoError(t, err) - require.NotNil(t, whitelister) - - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, "traefik") - }) - n := negroni.New(whitelister) - n.UseHandler(handler) + require.NotNil(t, whiteLister) for _, testIP := range test.passIPs { - req := testhelpers.MustNewRequest(http.MethodGet, "/", nil) - - req.RemoteAddr = testIP + ":2342" - recorder := httptest.NewRecorder() - n.ServeHTTP(recorder, req) - - assert.Equal(t, http.StatusOK, recorder.Code, testIP+" should have passed "+test.desc) - assert.Contains(t, recorder.Body.String(), "traefik") + 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 { - req := testhelpers.MustNewRequest(http.MethodGet, "/", nil) - - req.RemoteAddr = testIP + ":2342" - recorder := httptest.NewRecorder() - n.ServeHTTP(recorder, req) - - assert.Equal(t, http.StatusForbidden, recorder.Code, testIP+" should not have passed "+test.desc) - assert.NotContains(t, recorder.Body.String(), "traefik") + 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) + } + +}