IPStrategy for selecting IP in whitelist

This commit is contained in:
SALLEYRON Julien 2018-08-24 16:20:03 +02:00 committed by Traefiker Bot
parent 1ec4e03738
commit 00728e711c
65 changed files with 2444 additions and 1837 deletions

View file

@ -1,53 +0,0 @@
package server
import (
"net/http"
"os"
"github.com/containous/traefik/log"
"github.com/containous/traefik/whitelist"
"github.com/vulcand/oxy/forward"
)
// NewHeaderRewriter Create a header rewriter
func NewHeaderRewriter(trustedIPs []string, insecure bool) (forward.ReqRewriter, error) {
ips, err := whitelist.NewIP(trustedIPs, insecure, true)
if err != nil {
return nil, err
}
hostname, err := os.Hostname()
if err != nil {
hostname = "localhost"
}
return &headerRewriter{
secureRewriter: &forward.HeaderRewriter{TrustForwardHeader: false, Hostname: hostname},
insecureRewriter: &forward.HeaderRewriter{TrustForwardHeader: true, Hostname: hostname},
ips: ips,
insecure: insecure,
}, nil
}
type headerRewriter struct {
secureRewriter forward.ReqRewriter
insecureRewriter forward.ReqRewriter
insecure bool
ips *whitelist.IP
}
func (h *headerRewriter) Rewrite(req *http.Request) {
if h.insecure {
h.insecureRewriter.Rewrite(req)
return
}
err := h.ips.IsAuthorized(req)
if err != nil {
log.Debug(err)
h.secureRewriter.Rewrite(req)
return
}
h.insecureRewriter.Rewrite(req)
}

View file

@ -1,104 +0,0 @@
package server
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestHeaderRewriter_Rewrite(t *testing.T) {
testCases := []struct {
desc string
remoteAddr string
trustedIPs []string
insecure bool
expected map[string]string
}{
{
desc: "Secure & authorized",
remoteAddr: "10.10.10.10:80",
trustedIPs: []string{"10.10.10.10"},
insecure: false,
expected: map[string]string{
"X-Foo": "bar",
"X-Forwarded-For": "30.30.30.30",
},
},
{
desc: "Secure & unauthorized",
remoteAddr: "50.50.50.50:80",
trustedIPs: []string{"10.10.10.10"},
insecure: false,
expected: map[string]string{
"X-Foo": "bar",
"X-Forwarded-For": "",
},
},
{
desc: "Secure & authorized error",
remoteAddr: "10.10.10.10",
trustedIPs: []string{"10.10.10.10"},
insecure: false,
expected: map[string]string{
"X-Foo": "bar",
"X-Forwarded-For": "",
},
},
{
desc: "insecure & authorized",
remoteAddr: "10.10.10.10:80",
trustedIPs: []string{"10.10.10.10"},
insecure: true,
expected: map[string]string{
"X-Foo": "bar",
"X-Forwarded-For": "30.30.30.30",
},
},
{
desc: "insecure & unauthorized",
remoteAddr: "50.50.50.50:80",
trustedIPs: []string{"10.10.10.10"},
insecure: true,
expected: map[string]string{
"X-Foo": "bar",
"X-Forwarded-For": "30.30.30.30",
},
},
{
desc: "insecure & authorized error",
remoteAddr: "10.10.10.10",
trustedIPs: []string{"10.10.10.10"},
insecure: true,
expected: map[string]string{
"X-Foo": "bar",
"X-Forwarded-For": "30.30.30.30",
},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
rewriter, err := NewHeaderRewriter(test.trustedIPs, test.insecure)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodGet, "http://20.20.20.20/foo", nil)
require.NoError(t, err)
req.RemoteAddr = test.remoteAddr
req.Header.Set("X-Foo", "bar")
req.Header.Set("X-Forwarded-For", "30.30.30.30")
rewriter.Rewrite(req)
for key, value := range test.expected {
assert.Equal(t, value, req.Header.Get(key))
}
})
}
}

View file

@ -22,6 +22,7 @@ import (
"github.com/containous/traefik/cluster"
"github.com/containous/traefik/configuration"
"github.com/containous/traefik/h2c"
"github.com/containous/traefik/ip"
"github.com/containous/traefik/log"
"github.com/containous/traefik/metrics"
"github.com/containous/traefik/middlewares"
@ -31,7 +32,6 @@ import (
"github.com/containous/traefik/safe"
traefiktls "github.com/containous/traefik/tls"
"github.com/containous/traefik/types"
"github.com/containous/traefik/whitelist"
"github.com/sirupsen/logrus"
"github.com/urfave/negroni"
"github.com/xenolf/lego/acme"
@ -552,7 +552,7 @@ func (s *Server) prepareServer(entryPointName string, entryPoint *configuration.
if entryPoint.ProxyProtocol != nil {
listener, err = buildProxyProtocolListener(entryPoint, listener)
if err != nil {
return nil, nil, err
return nil, nil, fmt.Errorf("error creating proxy protocol listener: %v", err)
}
}
@ -572,23 +572,32 @@ func (s *Server) prepareServer(entryPointName string, entryPoint *configuration.
}
func buildProxyProtocolListener(entryPoint *configuration.EntryPoint, listener net.Listener) (net.Listener, error) {
IPs, err := whitelist.NewIP(entryPoint.ProxyProtocol.TrustedIPs, entryPoint.ProxyProtocol.Insecure, false)
if err != nil {
return nil, fmt.Errorf("error creating whitelist: %s", err)
var sourceCheck func(addr net.Addr) (bool, error)
if entryPoint.ProxyProtocol.Insecure {
sourceCheck = func(_ net.Addr) (bool, error) {
return true, nil
}
} else {
checker, err := ip.NewChecker(entryPoint.ProxyProtocol.TrustedIPs)
if err != nil {
return nil, err
}
sourceCheck = func(addr net.Addr) (bool, error) {
ipAddr, ok := addr.(*net.TCPAddr)
if !ok {
return false, fmt.Errorf("type error %v", addr)
}
return checker.ContainsIP(ipAddr.IP), nil
}
}
log.Infof("Enabling ProxyProtocol for trusted IPs %v", entryPoint.ProxyProtocol.TrustedIPs)
return &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), nil
},
Listener: listener,
SourceCheck: sourceCheck,
}, nil
}

View file

@ -233,17 +233,11 @@ func (s *Server) buildForwarder(entryPointName string, entryPoint *configuration
return nil, fmt.Errorf("failed to create RoundTripper for frontend %s: %v", frontendName, err)
}
rewriter, err := NewHeaderRewriter(entryPoint.ForwardedHeaders.TrustedIPs, entryPoint.ForwardedHeaders.Insecure)
if err != nil {
return nil, fmt.Errorf("error creating rewriter for frontend %s: %v", frontendName, err)
}
var fwd http.Handler
fwd, err = forward.New(
forward.Stream(true),
forward.PassHostHeader(frontend.PassHostHeader),
forward.RoundTripper(roundTripper),
forward.Rewriter(rewriter),
forward.ResponseModifier(responseModifier),
forward.BufferPool(s.bufferPool),
forward.WebsocketConnectionClosedHook(func(req *http.Request, conn net.Conn) {

View file

@ -9,6 +9,7 @@ import (
"github.com/containous/traefik/middlewares/accesslog"
mauth "github.com/containous/traefik/middlewares/auth"
"github.com/containous/traefik/middlewares/errorpages"
"github.com/containous/traefik/middlewares/forwardedheaders"
"github.com/containous/traefik/middlewares/redirect"
"github.com/containous/traefik/types"
thoas_stats "github.com/thoas/stats"
@ -47,7 +48,7 @@ func (s *Server) buildMiddlewares(frontendName string, frontend *types.Frontend,
}
// Whitelist
ipWhitelistMiddleware, err := buildIPWhiteLister(frontend.WhiteList, frontend.WhitelistSourceRange)
ipWhitelistMiddleware, err := buildIPWhiteLister(frontend.WhiteList, s.entryPoints[entryPointName].Configuration.ClientIPStrategy)
if err != nil {
return nil, nil, nil, fmt.Errorf("error creating IP Whitelister: %s", err)
}
@ -146,9 +147,18 @@ func (s *Server) buildServerEntryPointMiddlewares(serverEntryPointName string) (
serverMiddlewares = append(serverMiddlewares, &middlewares.Compress{})
}
ipWhitelistMiddleware, err := buildIPWhiteLister(
s.entryPoints[serverEntryPointName].Configuration.WhiteList,
s.entryPoints[serverEntryPointName].Configuration.WhitelistSourceRange)
if s.entryPoints[serverEntryPointName].Configuration.ForwardedHeaders != nil {
xForwardedMiddleware, err := forwardedheaders.NewXforwarded(
s.entryPoints[serverEntryPointName].Configuration.ForwardedHeaders.Insecure,
s.entryPoints[serverEntryPointName].Configuration.ForwardedHeaders.TrustedIPs,
)
if err != nil {
return nil, fmt.Errorf("failed to create xforwarded headers middleware: %v", err)
}
serverMiddlewares = append(serverMiddlewares, xForwardedMiddleware)
}
ipWhitelistMiddleware, err := buildIPWhiteLister(s.entryPoints[serverEntryPointName].Configuration.WhiteList, s.entryPoints[serverEntryPointName].Configuration.ClientIPStrategy)
if err != nil {
return nil, fmt.Errorf("failed to create ip whitelist middleware: %v", err)
}
@ -268,14 +278,21 @@ func (s *Server) buildRedirectHandler(srcEntryPointName string, opt *types.Redir
return redirection, nil
}
func buildIPWhiteLister(whiteList *types.WhiteList, wlRange []string) (*middlewares.IPWhiteLister, error) {
if whiteList != nil &&
len(whiteList.SourceRange) > 0 {
return middlewares.NewIPWhiteLister(whiteList.SourceRange, whiteList.UseXForwardedFor)
} else if len(wlRange) > 0 {
return middlewares.NewIPWhiteLister(wlRange, false)
func buildIPWhiteLister(whiteList *types.WhiteList, ipStrategy *types.IPStrategy) (*middlewares.IPWhiteLister, error) {
if whiteList == nil {
return nil, nil
}
return nil, nil
if whiteList.IPStrategy != nil {
ipStrategy = whiteList.IPStrategy
}
strategy, err := ipStrategy.Get()
if err != nil {
return nil, err
}
return middlewares.NewIPWhiteLister(whiteList.SourceRange, strategy)
}
func (s *Server) wrapNegroniHandlerWithAccessLog(handler negroni.Handler, frontendName string) negroni.Handler {

View file

@ -36,8 +36,10 @@ func TestServerEntryPointWhitelistConfig(t *testing.T) {
desc: "whitelist middleware should be added if configured on entrypoint",
entrypoint: &configuration.EntryPoint{
Address: ":0",
WhitelistSourceRange: []string{
"127.0.0.1/32",
WhiteList: &types.WhiteList{
SourceRange: []string{
"127.0.0.1/32",
},
},
ForwardedHeaders: &configuration.ForwardedHeaders{Insecure: true},
},
@ -96,23 +98,6 @@ func TestBuildIPWhiteLister(t *testing.T) {
middlewareConfigured: false,
errMessage: "",
},
{
desc: "whitelists configured (deprecated)",
whitelistSourceRange: []string{
"1.2.3.4/24",
"fe80::/16",
},
middlewareConfigured: true,
errMessage: "",
},
{
desc: "invalid whitelists configured (deprecated)",
whitelistSourceRange: []string{
"foo",
},
middlewareConfigured: false,
errMessage: "parsing CIDR whitelist [foo]: parsing CIDR white list <nil>: invalid CIDR address: foo",
},
{
desc: "whitelists configured",
whiteList: &types.WhiteList{
@ -120,22 +105,10 @@ func TestBuildIPWhiteLister(t *testing.T) {
"1.2.3.4/24",
"fe80::/16",
},
UseXForwardedFor: false,
},
middlewareConfigured: true,
errMessage: "",
},
{
desc: "invalid whitelists configured (deprecated)",
whiteList: &types.WhiteList{
SourceRange: []string{
"foo",
},
UseXForwardedFor: false,
},
middlewareConfigured: false,
errMessage: "parsing CIDR whitelist [foo]: parsing CIDR white list <nil>: invalid CIDR address: foo",
},
}
for _, test := range testCases {
@ -143,7 +116,7 @@ func TestBuildIPWhiteLister(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
middleware, err := buildIPWhiteLister(test.whiteList, test.whitelistSourceRange)
middleware, err := buildIPWhiteLister(test.whiteList, nil)
if test.errMessage != "" {
require.EqualError(t, err, test.errMessage)
@ -258,6 +231,11 @@ func TestServerGenericFrontendAuthFail(t *testing.T) {
"http": &configuration.EntryPoint{ForwardedHeaders: &configuration.ForwardedHeaders{Insecure: true}},
},
}
entryPoints := map[string]EntryPoint{
"http": {
Configuration: globalConfig.EntryPoints["http"],
},
}
dynamicConfigs := types.Configurations{
"config": &types.Configuration{
@ -286,7 +264,7 @@ func TestServerGenericFrontendAuthFail(t *testing.T) {
},
}
srv := NewServer(globalConfig, nil, nil)
srv := NewServer(globalConfig, nil, entryPoints)
_, err := srv.loadConfig(dynamicConfigs, globalConfig)
require.NoError(t, err)