Add accept-proxy-ip config directive
This allows to set the list of IPs allowed to act as a proxy. This is only used for WebSockets right now, but will be expanded to TCP as well once the PROXY protocol is supported.
This commit is contained in:
parent
b0bf012bbc
commit
ef2dd479bf
@ -4,23 +4,48 @@ import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/google/shlex"
|
||||
)
|
||||
|
||||
type IPSet []*net.IPNet
|
||||
|
||||
func (set IPSet) Contains(ip net.IP) bool {
|
||||
for _, n := range set {
|
||||
if n.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// loopbackIPs contains the loopback networks 127.0.0.0/8 and ::1/128.
|
||||
var loopbackIPs = IPSet{
|
||||
&net.IPNet{
|
||||
IP: net.IP{127, 0, 0, 0},
|
||||
Mask: net.CIDRMask(8, 32),
|
||||
},
|
||||
&net.IPNet{
|
||||
IP: net.IPv6loopback,
|
||||
Mask: net.CIDRMask(128, 128),
|
||||
},
|
||||
}
|
||||
|
||||
type TLS struct {
|
||||
CertPath, KeyPath string
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Listen []string
|
||||
Hostname string
|
||||
TLS *TLS
|
||||
SQLDriver string
|
||||
SQLSource string
|
||||
LogPath string
|
||||
HTTPOrigins []string
|
||||
Listen []string
|
||||
Hostname string
|
||||
TLS *TLS
|
||||
SQLDriver string
|
||||
SQLSource string
|
||||
LogPath string
|
||||
HTTPOrigins []string
|
||||
AcceptProxyIPs IPSet
|
||||
}
|
||||
|
||||
func Defaults() *Server {
|
||||
@ -29,9 +54,10 @@ func Defaults() *Server {
|
||||
hostname = "localhost"
|
||||
}
|
||||
return &Server{
|
||||
Hostname: hostname,
|
||||
SQLDriver: "sqlite3",
|
||||
SQLSource: "soju.db",
|
||||
Hostname: hostname,
|
||||
SQLDriver: "sqlite3",
|
||||
SQLSource: "soju.db",
|
||||
AcceptProxyIPs: loopbackIPs,
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,6 +119,15 @@ func Parse(r io.Reader) (*Server, error) {
|
||||
}
|
||||
case "http-origin":
|
||||
srv.HTTPOrigins = append(srv.HTTPOrigins, d.Params...)
|
||||
case "accept-proxy-ip":
|
||||
srv.AcceptProxyIPs = nil
|
||||
for _, s := range d.Params {
|
||||
_, n, err := net.ParseCIDR(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("directive %q: failed to parse CIDR: %v", d.Name, err)
|
||||
}
|
||||
srv.AcceptProxyIPs = append(srv.AcceptProxyIPs, n)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown directive %q", d.Name)
|
||||
}
|
||||
|
@ -109,6 +109,12 @@ The following directives are supported:
|
||||
List of allowed HTTP origins for WebSocket listeners. The parameters are
|
||||
interpreted as shell patterns, see *glob*(7).
|
||||
|
||||
*accept-proxy-ip* <cidr...>
|
||||
Allow the specified IPs to act as a proxy. Proxys have the ability to
|
||||
overwrite the remote and local connection addresses (via the X-Forwarded-*
|
||||
HTTP header fields). By default, the loopback addresses 127.0.0.0/8 and
|
||||
::1/128 are accepted.
|
||||
|
||||
# IRC SERVICE
|
||||
|
||||
soju exposes an IRC service called *BouncerServ* to manage the bouncer.
|
||||
|
25
server.go
25
server.go
@ -11,6 +11,8 @@ import (
|
||||
|
||||
"gopkg.in/irc.v3"
|
||||
"nhooyr.io/websocket"
|
||||
|
||||
"git.sr.ht/~emersion/soju/config"
|
||||
)
|
||||
|
||||
// TODO: make configurable
|
||||
@ -41,13 +43,14 @@ func (l *prefixLogger) Printf(format string, v ...interface{}) {
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Hostname string
|
||||
Logger Logger
|
||||
RingCap int
|
||||
HistoryLimit int
|
||||
LogPath string
|
||||
Debug bool
|
||||
HTTPOrigins []string
|
||||
Hostname string
|
||||
Logger Logger
|
||||
RingCap int
|
||||
HistoryLimit int
|
||||
LogPath string
|
||||
Debug bool
|
||||
HTTPOrigins []string
|
||||
AcceptProxyIPs config.IPSet
|
||||
|
||||
db *DB
|
||||
|
||||
@ -153,19 +156,19 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
isLoopback := false
|
||||
isProxy := false
|
||||
if host, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
isLoopback = ip.IsLoopback()
|
||||
isProxy = s.AcceptProxyIPs.Contains(ip)
|
||||
}
|
||||
}
|
||||
|
||||
// Only trust X-Forwarded-* header fields if this is a loopback connection,
|
||||
// Only trust X-Forwarded-* header fields if this is a trusted proxy IP
|
||||
// to prevent users from spoofing the remote address
|
||||
remoteAddr := req.RemoteAddr
|
||||
forwardedHost := req.Header.Get("X-Forwarded-For")
|
||||
forwardedPort := req.Header.Get("X-Forwarded-Port")
|
||||
if isLoopback && forwardedHost != "" && forwardedPort != "" {
|
||||
if isProxy && forwardedHost != "" && forwardedPort != "" {
|
||||
remoteAddr = net.JoinHostPort(forwardedHost, forwardedPort)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user