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"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/google/shlex"
|
"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 {
|
type TLS struct {
|
||||||
CertPath, KeyPath string
|
CertPath, KeyPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Listen []string
|
Listen []string
|
||||||
Hostname string
|
Hostname string
|
||||||
TLS *TLS
|
TLS *TLS
|
||||||
SQLDriver string
|
SQLDriver string
|
||||||
SQLSource string
|
SQLSource string
|
||||||
LogPath string
|
LogPath string
|
||||||
HTTPOrigins []string
|
HTTPOrigins []string
|
||||||
|
AcceptProxyIPs IPSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func Defaults() *Server {
|
func Defaults() *Server {
|
||||||
@ -29,9 +54,10 @@ func Defaults() *Server {
|
|||||||
hostname = "localhost"
|
hostname = "localhost"
|
||||||
}
|
}
|
||||||
return &Server{
|
return &Server{
|
||||||
Hostname: hostname,
|
Hostname: hostname,
|
||||||
SQLDriver: "sqlite3",
|
SQLDriver: "sqlite3",
|
||||||
SQLSource: "soju.db",
|
SQLSource: "soju.db",
|
||||||
|
AcceptProxyIPs: loopbackIPs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,6 +119,15 @@ func Parse(r io.Reader) (*Server, error) {
|
|||||||
}
|
}
|
||||||
case "http-origin":
|
case "http-origin":
|
||||||
srv.HTTPOrigins = append(srv.HTTPOrigins, d.Params...)
|
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:
|
default:
|
||||||
return nil, fmt.Errorf("unknown directive %q", d.Name)
|
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
|
List of allowed HTTP origins for WebSocket listeners. The parameters are
|
||||||
interpreted as shell patterns, see *glob*(7).
|
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
|
# IRC SERVICE
|
||||||
|
|
||||||
soju exposes an IRC service called *BouncerServ* to manage the bouncer.
|
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"
|
"gopkg.in/irc.v3"
|
||||||
"nhooyr.io/websocket"
|
"nhooyr.io/websocket"
|
||||||
|
|
||||||
|
"git.sr.ht/~emersion/soju/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: make configurable
|
// TODO: make configurable
|
||||||
@ -41,13 +43,14 @@ func (l *prefixLogger) Printf(format string, v ...interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Hostname string
|
Hostname string
|
||||||
Logger Logger
|
Logger Logger
|
||||||
RingCap int
|
RingCap int
|
||||||
HistoryLimit int
|
HistoryLimit int
|
||||||
LogPath string
|
LogPath string
|
||||||
Debug bool
|
Debug bool
|
||||||
HTTPOrigins []string
|
HTTPOrigins []string
|
||||||
|
AcceptProxyIPs config.IPSet
|
||||||
|
|
||||||
db *DB
|
db *DB
|
||||||
|
|
||||||
@ -153,19 +156,19 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
isLoopback := false
|
isProxy := false
|
||||||
if host, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
|
if host, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
|
||||||
if ip := net.ParseIP(host); ip != 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
|
// to prevent users from spoofing the remote address
|
||||||
remoteAddr := req.RemoteAddr
|
remoteAddr := req.RemoteAddr
|
||||||
forwardedHost := req.Header.Get("X-Forwarded-For")
|
forwardedHost := req.Header.Get("X-Forwarded-For")
|
||||||
forwardedPort := req.Header.Get("X-Forwarded-Port")
|
forwardedPort := req.Header.Get("X-Forwarded-Port")
|
||||||
if isLoopback && forwardedHost != "" && forwardedPort != "" {
|
if isProxy && forwardedHost != "" && forwardedPort != "" {
|
||||||
remoteAddr = net.JoinHostPort(forwardedHost, forwardedPort)
|
remoteAddr = net.JoinHostPort(forwardedHost, forwardedPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user