mirror of
https://github.com/lightninglabs/aperture.git
synced 2025-12-18 17:44:20 +01:00
proxy: implement blocklist
This commit is contained in:
@@ -862,7 +862,9 @@ func createProxy(cfg *Config, challenger challenger.Challenger,
|
||||
},
|
||||
))
|
||||
|
||||
prxy, err := proxy.New(authenticator, cfg.Services, localServices...)
|
||||
prxy, err := proxy.New(
|
||||
authenticator, cfg.Services, cfg.Blocklist, localServices...,
|
||||
)
|
||||
return prxy, proxyCleanup, err
|
||||
}
|
||||
|
||||
|
||||
@@ -226,6 +226,9 @@ type Config struct {
|
||||
|
||||
// Logging controls various aspects of aperture logging.
|
||||
Logging *build.LogConfig `group:"logging" namespace:"logging"`
|
||||
|
||||
// Blocklist is a list of IPs to deny access to.
|
||||
Blocklist []string `long:"blocklist" description:"List of IP addresses to block from accessing the proxy."`
|
||||
}
|
||||
|
||||
func (c *Config) validate() error {
|
||||
@@ -270,5 +273,6 @@ func NewConfig() *Config {
|
||||
WriteTimeout: defaultWriteTimeout,
|
||||
InvoiceBatchSize: defaultInvoiceBatchSize,
|
||||
Logging: build.DefaultLogConfig(),
|
||||
Blocklist: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"os"
|
||||
@@ -73,18 +74,30 @@ type Proxy struct {
|
||||
localServices []LocalService
|
||||
authenticator auth.Authenticator
|
||||
services []*Service
|
||||
blocklist map[string]struct{}
|
||||
}
|
||||
|
||||
// New returns a new Proxy instance that proxies between the services specified,
|
||||
// using the auth to validate each request's headers and get new challenge
|
||||
// headers if necessary.
|
||||
func New(auth auth.Authenticator, services []*Service,
|
||||
localServices ...LocalService) (*Proxy, error) {
|
||||
blocklist []string, localServices ...LocalService) (*Proxy, error) {
|
||||
|
||||
blMap := make(map[string]struct{})
|
||||
for _, ip := range blocklist {
|
||||
parsed := net.ParseIP(ip)
|
||||
if parsed == nil {
|
||||
log.Warnf("Could not parse IP %q in blocklist; skipping", ip)
|
||||
continue
|
||||
}
|
||||
blMap[parsed.String()] = struct{}{}
|
||||
}
|
||||
|
||||
proxy := &Proxy{
|
||||
localServices: localServices,
|
||||
authenticator: auth,
|
||||
services: services,
|
||||
blocklist: blMap,
|
||||
}
|
||||
err := proxy.UpdateServices(services)
|
||||
if err != nil {
|
||||
@@ -106,6 +119,14 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
defer logRequest()
|
||||
|
||||
// Blocklist check
|
||||
if _, blocked := p.blocklist[remoteIP.String()]; blocked {
|
||||
log.Debugf("Blocked request from IP: %s", remoteIP)
|
||||
addCorsHeaders(w.Header())
|
||||
sendDirectResponse(w, r, http.StatusForbidden, "access denied")
|
||||
return
|
||||
}
|
||||
|
||||
// For OPTIONS requests we only need to set the CORS headers, not serve
|
||||
// any content;
|
||||
if r.Method == "OPTIONS" {
|
||||
|
||||
@@ -111,6 +111,72 @@ func TestProxyHTTP(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestProxyHTTPBlocklist tests that the proxy can block HTTP requests from
|
||||
// a blocked IP.
|
||||
func TestProxyHTTPBlocklist(t *testing.T) {
|
||||
services := []*proxy.Service{{
|
||||
Address: testTargetServiceAddress,
|
||||
HostRegexp: testHostRegexp,
|
||||
PathRegexp: testPathRegexpHTTP,
|
||||
Protocol: "http",
|
||||
Auth: "off",
|
||||
}}
|
||||
|
||||
mockAuth := auth.NewMockAuthenticator()
|
||||
|
||||
// Block the IP that will be used in the request.
|
||||
blockedIP := "127.0.0.1"
|
||||
p, err := proxy.New(mockAuth, services, []string{blockedIP})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Start the proxy server.
|
||||
server := &http.Server{
|
||||
Addr: testProxyAddr,
|
||||
Handler: http.HandlerFunc(p.ServeHTTP),
|
||||
}
|
||||
go func() {
|
||||
if err := server.ListenAndServe(); err != http.ErrServerClosed {
|
||||
t.Errorf("proxy serve error: %v", err)
|
||||
}
|
||||
}()
|
||||
defer closeOrFail(t, server)
|
||||
|
||||
// Start the backend server.
|
||||
backendService := &http.Server{Addr: testTargetServiceAddress}
|
||||
go func() { _ = startBackendHTTP(backendService) }()
|
||||
defer closeOrFail(t, backendService)
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Make a request with a spoofed RemoteAddr that matches the blocklist.
|
||||
req, err := http.NewRequest(
|
||||
"GET",
|
||||
fmt.Sprintf("http://%s/http/test", testProxyAddr),
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a custom transport to override the local IP — simulate blocked IP.
|
||||
customTransport := &http.Transport{
|
||||
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||
lAddr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:0")
|
||||
d := net.Dialer{LocalAddr: lAddr}
|
||||
return d.DialContext(context.Background(), "tcp", testProxyAddr)
|
||||
},
|
||||
}
|
||||
client := &http.Client{Transport: customTransport}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
require.Equal(t, http.StatusForbidden, resp.StatusCode)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "access denied\n", string(body))
|
||||
}
|
||||
|
||||
// runHTTPTest tests that the proxy can forward HTTP requests to a backend
|
||||
// service and handle L402 authentication correctly.
|
||||
func runHTTPTest(t *testing.T, tc *testCase, method string) {
|
||||
@@ -125,7 +191,7 @@ func runHTTPTest(t *testing.T, tc *testCase, method string) {
|
||||
}}
|
||||
|
||||
mockAuth := auth.NewMockAuthenticator()
|
||||
p, err := proxy.New(mockAuth, services)
|
||||
p, err := proxy.New(mockAuth, services, []string{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Start server that gives requests to the proxy.
|
||||
@@ -288,7 +354,7 @@ func runGRPCTest(t *testing.T, tc *testCase) {
|
||||
|
||||
// Create the proxy server and start serving on TLS.
|
||||
mockAuth := auth.NewMockAuthenticator()
|
||||
p, err := proxy.New(mockAuth, services)
|
||||
p, err := proxy.New(mockAuth, services, []string{})
|
||||
require.NoError(t, err)
|
||||
server := &http.Server{
|
||||
Addr: testProxyAddr,
|
||||
|
||||
@@ -61,6 +61,10 @@ authenticator:
|
||||
# Set to true to skip verification of the mailbox server's tls cert.
|
||||
devserver: false
|
||||
|
||||
# List of IPs to block from accessing the proxy.
|
||||
blocklist:
|
||||
- "1.1.1.1"
|
||||
- "1.0.0.1"
|
||||
|
||||
# The selected database backend. The current default backend is "sqlite".
|
||||
# Aperture also has support for postgres and etcd.
|
||||
|
||||
Reference in New Issue
Block a user