---
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.