Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions internals/config/structure/structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ type ACCESS struct {
ENDPOINTS []string `koanf:"endpoints"`
FIELD_POLICIES map[string]FieldPolicy `koanf:"fieldpolicies" childtransform:"default"`
RATE_LIMITING RateLimiting `koanf:"ratelimiting"`
IP_FILTER []string `koanf:"ipfilter"`
TRUSTED_IPS []string `koanf:"trustedips"`
TRUSTED_PROXIES []string `koanf:"trustedproxies"`
}

type FieldPolicy struct {
Expand Down
2 changes: 1 addition & 1 deletion internals/proxy/middlewares/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ func (chain *AuthChain) Eval(w http.ResponseWriter, req *http.Request, tokens []
token, err = method.Authenticate(w, req, tokens)

if err != nil {
logger.Warn("User failed ", method.Name, " auth: ", err.Error())
logger.Warn("Client failed ", method.Name, " auth: ", err.Error())
}

if token != "" {
Expand Down
40 changes: 40 additions & 0 deletions internals/proxy/middlewares/clientip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package middlewares

import (
"net"
"net/http"
)

var InternalClientIP Middleware = Middleware{
Name: "_Client_IP",
Use: clientIPHandler,
}

var trustedClientKey contextKey = "isClientTrusted"

func clientIPHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
logger := getLogger(req)

conf := getConfigByReq(req)

rawTrustedIPs := conf.SETTINGS.ACCESS.TRUSTED_IPS

if rawTrustedIPs == nil {
rawTrustedIPs = getConfig("").SETTINGS.ACCESS.TRUSTED_IPS
}

ip := getContext[net.IP](req, clientIPKey)

trustedIPs := parseIPsAndIPNets(rawTrustedIPs)
trusted := isIPInList(ip, trustedIPs)

if trusted {
logger.Dev("Connection from trusted Client: ", ip.String())
}

req = setContext(req, trustedClientKey, trusted)

next.ServeHTTP(w, req)
})
}
8 changes: 4 additions & 4 deletions internals/proxy/middlewares/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ func endpointsHandler(next http.Handler) http.Handler {

reqPath := req.URL.Path

if isBlocked(reqPath, endpoints) {
logger.Warn("User tried to access blocked endpoint: ", reqPath)
if isEndpointBlocked(reqPath, endpoints) {
logger.Warn("Client tried to access blocked endpoint: ", reqPath)
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
Expand Down Expand Up @@ -58,8 +58,8 @@ func matchesPattern(endpoint, pattern string) bool {
return ok
}

func isBlocked(endpoint string, endpoints []string) bool {
if len(endpoints) == 0 {
func isEndpointBlocked(endpoint string, endpoints []string) bool {
if len(endpoints) == 0 || endpoints == nil {
// default: allow all
return false
}
Expand Down
95 changes: 95 additions & 0 deletions internals/proxy/middlewares/ipfilter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package middlewares

import (
"net"
"net/http"
"slices"
"strings"
)

var IPFilter Middleware = Middleware{
Name: "IP Filter",
Use: ipFilterHandler,
}

func ipFilterHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
logger := getLogger(req)

conf := getConfigByReq(req)

ipFilter := conf.SETTINGS.ACCESS.IP_FILTER

if ipFilter == nil {
ipFilter = getConfig("").SETTINGS.ACCESS.ENDPOINTS
}

ip := getContext[net.IP](req, clientIPKey)

if isIPBlocked(ip, ipFilter) {
logger.Warn("Client IP is blocked by filter: ", ip.String())
http.Error(w, "Forbidden", http.StatusForbidden)
return
}

next.ServeHTTP(w, req)
})
}

func getIPNets(ipNets []string) ([]string, []string) {
blockedIPNets := []string{}
allowedIPNets := []string{}

for _, ipNet := range ipNets {
ip, block := strings.CutPrefix(ipNet, "!")

if block {
blockedIPNets = append(blockedIPNets, ip)
} else {
allowedIPNets = append(allowedIPNets, ip)
}
}

return allowedIPNets, blockedIPNets
}

func isIPBlocked(ip net.IP, ipfilter []string) (bool) {
if len(ipfilter) == 0 || ipfilter == nil {
// default: allow all
return false
}

rawAllowed, rawBlocked := getIPNets(ipfilter)

allowed := parseIPsAndIPNets(rawAllowed)
blocked := parseIPsAndIPNets(rawBlocked)

isExplicitlyAllowed := slices.ContainsFunc(allowed, func(try *net.IPNet) bool {
return try.Contains(ip)
})
isExplicitlyBlocked := slices.ContainsFunc(blocked, func(try *net.IPNet) bool {
return try.Contains(ip)
})

// explicit allow > block
if isExplicitlyAllowed {
return false
}

if isExplicitlyBlocked {
return true
}

// if any allow rules exist, default is deny
if len(allowed) > 0 {
return true
}

// only blocked ips -> allow anything not blocked
if len(blocked) > 0 {
return false
}

// default: allow all
return false
}
40 changes: 28 additions & 12 deletions internals/proxy/middlewares/log.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package middlewares

import (
"net"
"net/http"
"strings"

Expand All @@ -17,6 +18,33 @@ var RequestLogger Middleware = Middleware{
const loggerKey contextKey = "logger"

func loggingHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
logger := getLogger(req)

ip := getContext[net.IP](req, clientIPKey)

if !logger.IsDev() {
logger.Info(ip.String(), " ", req.Method, " ", req.URL.Path, " ", req.URL.RawQuery)
} else {
body, _ := request.GetReqBody(req)

if body.Data != nil && !body.Empty {
logger.Dev(ip.String(), " ", req.Method, " ", req.URL.Path, " ", req.URL.RawQuery, body.Data)
} else {
logger.Info(ip.String(), " ", req.Method, " ", req.URL.Path, " ", req.URL.RawQuery)
}
}

next.ServeHTTP(w, req)
})
}

var InternalMiddlewareLogger Middleware = Middleware{
Name: "_Middleware_Logger",
Use: middlewareLoggerHandler,
}

func middlewareLoggerHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
conf := getConfigByReq(req)

Expand Down Expand Up @@ -49,18 +77,6 @@ func loggingHandler(next http.Handler) http.Handler {

req = setContext(req, loggerKey, l)

if !l.IsDev() {
l.Info(req.Method, " ", req.URL.Path, " ", req.URL.RawQuery)
} else {
body, _ := request.GetReqBody(req)

if body.Data != nil && !body.Empty {
l.Dev(req.Method, " ", req.URL.Path, " ", req.URL.RawQuery, body.Data)
} else {
l.Info(req.Method, " ", req.URL.Path, " ", req.URL.RawQuery)
}
}

next.ServeHTTP(w, req)
})
}
2 changes: 1 addition & 1 deletion internals/proxy/middlewares/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func policyHandler(next http.Handler) http.Handler {
shouldBlock, field := doBlock(body.Data, headerData, policies)

if shouldBlock {
logger.Warn("User tried to use blocked field: ", field)
logger.Warn("Client tried to use blocked field: ", field)
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
Expand Down
2 changes: 1 addition & 1 deletion internals/proxy/middlewares/port.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func portHandler(next http.Handler) http.Handler {
}

if port != allowedPort {
logger.Warn("User tried using Token on wrong Port")
logger.Warn("Client tried using Token on wrong Port")
onUnauthorized(w)
return
}
Expand Down
Loading