mirror of
https://github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin.git
synced 2026-07-21 11:38:59 +02:00
16 read ip address from x forwarded for or x real ip instead of remoteaddr if it exists (#17)
* Handle X forwarded for in the plugin * Update docker-compose.cloudflare.yml Change to version v1.0.9 * 🍱 fix + isolate cache * 🍱 missing files * 🍱 lint Co-authored-by: MathieuHa <mathieu@hanotaux.fr> Co-authored-by: Max Lerebourg <maxlerebourg@gmail.com>
This commit is contained in:
co-authored by
MathieuHa
Max Lerebourg
parent
6dde683a0a
commit
552b30a9ef
@@ -0,0 +1,121 @@
|
||||
package ip
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//CHECKER
|
||||
|
||||
// Checker allows to check that addresses are in a trusted IPs.
|
||||
type Checker struct {
|
||||
authorizedIPs []*net.IP
|
||||
authorizedIPsNet []*net.IPNet
|
||||
}
|
||||
|
||||
// NewChecker builds a new Checker given a list of CIDR-Strings to trusted IPs.
|
||||
func NewChecker(trustedIPs []string) (*Checker, error) {
|
||||
if len(trustedIPs) == 0 {
|
||||
return nil, errors.New("no trusted IPs provided")
|
||||
}
|
||||
|
||||
checker := &Checker{}
|
||||
|
||||
for _, ipMask := range trustedIPs {
|
||||
if ipAddr := net.ParseIP(ipMask); ipAddr != nil {
|
||||
checker.authorizedIPs = append(checker.authorizedIPs, &ipAddr)
|
||||
continue
|
||||
}
|
||||
|
||||
_, ipAddr, err := net.ParseCIDR(ipMask)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing CIDR trusted IPs %s: %w", ipAddr, err)
|
||||
}
|
||||
checker.authorizedIPsNet = append(checker.authorizedIPsNet, ipAddr)
|
||||
}
|
||||
|
||||
return checker, nil
|
||||
}
|
||||
|
||||
// Contains checks if provided address is in the trusted IPs.
|
||||
func (ip *Checker) Contains(addr string) (bool, error) {
|
||||
if len(addr) == 0 {
|
||||
return false, errors.New("empty IP address")
|
||||
}
|
||||
|
||||
ipAddr, err := parseIP(addr)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("unable to parse address: %s: %w", addr, err)
|
||||
}
|
||||
|
||||
return ip.ContainsIP(ipAddr), nil
|
||||
}
|
||||
|
||||
// ContainsIP checks if provided address is in the trusted IPs.
|
||||
func (ip *Checker) ContainsIP(addr net.IP) bool {
|
||||
for _, authorizedIP := range ip.authorizedIPs {
|
||||
if authorizedIP.Equal(addr) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, authorizedNet := range ip.authorizedIPsNet {
|
||||
if authorizedNet.Contains(addr) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func parseIP(addr string) (net.IP, error) {
|
||||
userIP := net.ParseIP(addr)
|
||||
if userIP == nil {
|
||||
return nil, fmt.Errorf("can't parse IP from address %s", addr)
|
||||
}
|
||||
|
||||
return userIP, nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// STRATEGY
|
||||
|
||||
const (
|
||||
xForwardedFor = "X-Forwarded-For"
|
||||
)
|
||||
|
||||
// PoolStrategy is a strategy based on an IP Checker.
|
||||
// It allows to check whether addresses are in a given pool of IPs.
|
||||
type PoolStrategy struct {
|
||||
Checker *Checker
|
||||
}
|
||||
|
||||
// GetIP checks the list of Forwarded IPs (most recent first) against the
|
||||
// Checker pool of IPs. It returns the first IP that is not in the pool, or the
|
||||
// empty string otherwise.
|
||||
func (s *PoolStrategy) GetIP(req *http.Request) string {
|
||||
if s.Checker == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
xff := req.Header.Get(xForwardedFor)
|
||||
xffs := strings.Split(xff, ",")
|
||||
|
||||
for i := len(xffs) - 1; i >= 0; i-- {
|
||||
xffTrimmed := strings.TrimSpace(xffs[i])
|
||||
if len(xffTrimmed) == 0 {
|
||||
continue
|
||||
}
|
||||
if contain, _ := s.Checker.Contains(xffTrimmed); !contain {
|
||||
return xffTrimmed
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user