IPStrategy for selecting IP in whitelist
This commit is contained in:
parent
1ec4e03738
commit
00728e711c
65 changed files with 2444 additions and 1837 deletions
|
@ -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)
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue