feat: re introduce IpWhitelist middleware as deprecated

This commit is contained in:
Michael 2024-01-11 10:40:06 +01:00 committed by GitHub
parent 3bbc560283
commit ff7966f9cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 1314 additions and 200 deletions

View file

@ -0,0 +1,63 @@
package ipwhitelist
import (
"context"
"errors"
"fmt"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/traefik/traefik/v3/pkg/ip"
"github.com/traefik/traefik/v3/pkg/middlewares"
"github.com/traefik/traefik/v3/pkg/tcp"
)
const (
typeName = "IPWhiteListerTCP"
)
// ipWhiteLister is a middleware that provides Checks of the Requesting IP against a set of Whitelists.
type ipWhiteLister struct {
next tcp.Handler
whiteLister *ip.Checker
name string
}
// New builds a new TCP IPWhiteLister given a list of CIDR-Strings to whitelist.
func New(ctx context.Context, next tcp.Handler, config dynamic.TCPIPWhiteList, name string) (tcp.Handler, error) {
logger := middlewares.GetLogger(ctx, name, typeName)
logger.Debug().Msg("Creating middleware")
if len(config.SourceRange) == 0 {
return nil, errors.New("sourceRange is empty, IPWhiteLister not created")
}
checker, err := ip.NewChecker(config.SourceRange)
if err != nil {
return nil, fmt.Errorf("cannot parse CIDR whitelist %s: %w", config.SourceRange, err)
}
logger.Debug().Msgf("Setting up IPWhiteLister with sourceRange: %s", config.SourceRange)
return &ipWhiteLister{
whiteLister: checker,
next: next,
name: name,
}, nil
}
func (wl *ipWhiteLister) ServeTCP(conn tcp.WriteCloser) {
logger := middlewares.GetLogger(context.Background(), wl.name, typeName)
addr := conn.RemoteAddr().String()
err := wl.whiteLister.IsAuthorized(addr)
if err != nil {
logger.Error().Err(err).Msgf("Connection from %s rejected", addr)
conn.Close()
return
}
logger.Debug().Msgf("Connection from %s accepted", addr)
wl.next.ServeTCP(conn)
}

View file

@ -0,0 +1,139 @@
package ipwhitelist
import (
"context"
"io"
"net"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/traefik/traefik/v3/pkg/tcp"
)
func TestNewIPWhiteLister(t *testing.T) {
testCases := []struct {
desc string
whiteList dynamic.TCPIPWhiteList
expectedError bool
}{
{
desc: "Empty config",
whiteList: dynamic.TCPIPWhiteList{},
expectedError: true,
},
{
desc: "invalid IP",
whiteList: dynamic.TCPIPWhiteList{
SourceRange: []string{"foo"},
},
expectedError: true,
},
{
desc: "valid IP",
whiteList: dynamic.TCPIPWhiteList{
SourceRange: []string{"10.10.10.10"},
},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
next := tcp.HandlerFunc(func(conn tcp.WriteCloser) {})
whiteLister, err := New(context.Background(), next, test.whiteList, "traefikTest")
if test.expectedError {
assert.Error(t, err)
} else {
require.NoError(t, err)
assert.NotNil(t, whiteLister)
}
})
}
}
func TestIPWhiteLister_ServeHTTP(t *testing.T) {
testCases := []struct {
desc string
whiteList dynamic.TCPIPWhiteList
remoteAddr string
expected string
}{
{
desc: "authorized with remote address",
whiteList: dynamic.TCPIPWhiteList{
SourceRange: []string{"20.20.20.20"},
},
remoteAddr: "20.20.20.20:1234",
expected: "OK",
},
{
desc: "non authorized with remote address",
whiteList: dynamic.TCPIPWhiteList{
SourceRange: []string{"20.20.20.20"},
},
remoteAddr: "20.20.20.21:1234",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
next := tcp.HandlerFunc(func(conn tcp.WriteCloser) {
write, err := conn.Write([]byte("OK"))
require.NoError(t, err)
assert.Equal(t, 2, write)
err = conn.Close()
require.NoError(t, err)
})
whiteLister, err := New(context.Background(), next, test.whiteList, "traefikTest")
require.NoError(t, err)
server, client := net.Pipe()
go func() {
whiteLister.ServeTCP(&contextWriteCloser{client, addr{test.remoteAddr}})
}()
read, err := io.ReadAll(server)
require.NoError(t, err)
assert.Equal(t, test.expected, string(read))
})
}
}
type contextWriteCloser struct {
net.Conn
addr
}
type addr struct {
remoteAddr string
}
func (a addr) Network() string {
panic("implement me")
}
func (a addr) String() string {
return a.remoteAddr
}
func (c contextWriteCloser) CloseWrite() error {
panic("implement me")
}
func (c contextWriteCloser) RemoteAddr() net.Addr { return c.addr }
func (c contextWriteCloser) Context() context.Context {
return context.Background()
}