Julio B: 1 Accept proxy protocol on unix sockets 4 files changed, 34 insertions(+), 8 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~emersion/soju-dev/patches/36207/mbox | git am -3Learn more about email & git
--- Now soju accepts PROXY only on specified unix sockets. Below are some testing commands that i used. Example soju configuration ... listen unix:///tmp/sojuaccept.socket listen unix:///tmp/soju.socket accept-proxy-from unix:///tmp/sojuaccept.socket ... Quick testing soju rejects PROXY $ echo -ne "PROXY TCP4 27.13.13.12 10.10.10.10 44554 6697\r\nQUIT\r\n" | socat unix-client:/tmp/soju.socket - soju must accept $ echo -ne "PROXY TCP4 27.13.13.12 10.10.10.10 44554 6697\r\nQUIT\r\n" | socat unix-client:/tmp/sojuaccept.socket - Example usage behind reverse proxy nginx.conf ... stream { server { listen 55555; proxy_pass unix:/tmp/sojuaccept.socket; proxy_protocol on; } } ... soju accepts PROXY from nginx. Attackers PROXY is parsed as a common irc command $ echo -ne "PROXY TCP4 27.13.13.12 10.10.10.10 43215 6697\r\nQUIT\r\n" | socat tcp-connect:soju.irc:55555 - cmd/soju/main.go | 8 +++++--- config/config.go | 23 ++++++++++++++++++++--- doc/soju.1.scd | 10 ++++++++-- server.go | 1 + 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/cmd/soju/main.go b/cmd/soju/main.go index 0094381..7e57582 100644 --- a/cmd/soju/main.go +++ b/cmd/soju/main.go @@ -89,6 +89,7 @@ func loadConfig() (*config.Server, *soju.Config, error) { LogPath: raw.MsgStore.Source, HTTPOrigins: raw.HTTPOrigins, AcceptProxyIPs: raw.AcceptProxyIPs, + AcceptProxyUnix: raw.AcceptProxyUnix, MaxUserNetworks: raw.MaxUserNetworks, UpstreamUserIPs: raw.UpstreamUserIPs, MOTD: motd, @@ -345,10 +346,11 @@ func proxyProtoListener(ln net.Listener, srv *soju.Server) net.Listener { Listener: ln, Policy: func(upstream net.Addr) (proxyproto.Policy, error) { tcpAddr, ok := upstream.(*net.TCPAddr) - if !ok { - return proxyproto.IGNORE, nil + if ok && srv.Config().AcceptProxyIPs.Contains(tcpAddr.IP) { + return proxyproto.USE, nil } - if srv.Config().AcceptProxyIPs.Contains(tcpAddr.IP) { + unixAddr, ok := ln.Addr().(*net.UnixAddr) + if ok && srv.Config().AcceptProxyUnix.Contains(unixAddr.String()) { return proxyproto.USE, nil } return proxyproto.IGNORE, nil diff --git a/config/config.go b/config/config.go index 25233dd..7b3fe13 100644 --- a/config/config.go +++ b/config/config.go @@ -5,6 +5,7 @@ import ( "net" "os" "strconv" + "strings" "git.sr.ht/~emersion/go-scfg" ) @@ -32,6 +33,17 @@ var loopbackIPs = IPSet{ }, } +type UnixSet []string + +func (set UnixSet) Contains(socket string) bool { + for _, s := range set { + if s == "unix" || strings.TrimPrefix(s, "unix://") == socket { + return true + } + } + return false +} + type TLS struct { CertPath, KeyPath string } @@ -54,8 +66,9 @@ type Server struct { DB DB MsgStore MsgStore - HTTPOrigins []string - AcceptProxyIPs IPSet + HTTPOrigins []string + AcceptProxyIPs IPSet + AcceptProxyUnix UnixSet MaxUserNetworks int UpstreamUserIPs []*net.IPNet @@ -135,13 +148,17 @@ func parse(cfg scfg.Block) (*Server, error) { } case "http-origin": srv.HTTPOrigins = d.Params - case "accept-proxy-ip": + case "accept-proxy-from", "accept-proxy-ip": srv.AcceptProxyIPs = nil for _, s := range d.Params { if s == "localhost" { srv.AcceptProxyIPs = append(srv.AcceptProxyIPs, loopbackIPs...) continue } + if strings.HasPrefix(s, "unix") { + srv.AcceptProxyUnix = append(srv.AcceptProxyUnix, s) + continue + } _, n, err := net.ParseCIDR(s) if err != nil { return nil, fmt.Errorf("directive %q: failed to parse CIDR: %v", d.Name, err) diff --git a/doc/soju.1.scd b/doc/soju.1.scd index b5e398e..080c446 100644 --- a/doc/soju.1.scd +++ b/doc/soju.1.scd @@ -145,14 +145,20 @@ The following directives are supported: By default, only the request host is authorized. Use this directive to enable cross-origin WebSockets. -*accept-proxy-ip* <cidr...> +*accept-proxy-from* <cidr...> Allow the specified IPs to act as a proxy. Proxys have the ability to overwrite the remote and local connection addresses (via the PROXY protocol, the Forwarded HTTP header field defined in RFC 7239 or the X-Forwarded-\* HTTP header fields). The special name "localhost" accepts the loopback addresses 127.0.0.0/8 and ::1/128. - By default, all IPs are rejected. + The special name "unix" accepts PROXY protocol on all unix sockets that + soju is listening on. Use the more specific format "unix:///path/to/socket" + to accept just on that socket. + + By default, all IPs and sockets are rejected. + + (_accept-proxy-ip_ is a deprecated alias for this directive.) *max-user-networks* <limit> Maximum number of networks per user. By default, there is no limit. diff --git a/server.go b/server.go index dc209a3..1cad9f1 100644 --- a/server.go +++ b/server.go @@ -138,6 +138,7 @@ type Config struct { LogPath string HTTPOrigins []string AcceptProxyIPs config.IPSet + AcceptProxyUnix config.UnixSet MaxUserNetworks int MOTD string UpstreamUserIPs []*net.IPNet -- 2.38.0
Hm, I'd prefer not to add the filtering based on Unix socket names. The socket name is not a source address like an IP address is, it's a destination address. If we were to add it, I think accepting a block in listen directives would be better: listen unix://… { accept-proxy-from unix } But I'd rather not add it before someone comes with a use-case for it.