Add per-user IP addresses
The new upstream-user-ip directive allows bouncer operators to assign one IP address per user.
This commit is contained in:
parent
97152191ad
commit
55840312b4
@ -93,6 +93,7 @@ func loadConfig() (*config.Server, *soju.Config, error) {
|
|||||||
AcceptProxyIPs: raw.AcceptProxyIPs,
|
AcceptProxyIPs: raw.AcceptProxyIPs,
|
||||||
MaxUserNetworks: raw.MaxUserNetworks,
|
MaxUserNetworks: raw.MaxUserNetworks,
|
||||||
MultiUpstream: raw.MultiUpstream,
|
MultiUpstream: raw.MultiUpstream,
|
||||||
|
UpstreamUserIPs: raw.UpstreamUserIPs,
|
||||||
Debug: debug,
|
Debug: debug,
|
||||||
MOTD: motd,
|
MOTD: motd,
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ type Server struct {
|
|||||||
|
|
||||||
MaxUserNetworks int
|
MaxUserNetworks int
|
||||||
MultiUpstream bool
|
MultiUpstream bool
|
||||||
|
UpstreamUserIPs []*net.IPNet
|
||||||
}
|
}
|
||||||
|
|
||||||
func Defaults() *Server {
|
func Defaults() *Server {
|
||||||
@ -150,6 +151,29 @@ func parse(cfg scfg.Block) (*Server, error) {
|
|||||||
return nil, fmt.Errorf("directive %q: %v", d.Name, err)
|
return nil, fmt.Errorf("directive %q: %v", d.Name, err)
|
||||||
}
|
}
|
||||||
srv.MultiUpstream = v
|
srv.MultiUpstream = v
|
||||||
|
case "upstream-user-ip":
|
||||||
|
if len(srv.UpstreamUserIPs) > 0 {
|
||||||
|
return nil, fmt.Errorf("directive %q: can only be specified once", d.Name)
|
||||||
|
}
|
||||||
|
var hasIPv4, hasIPv6 bool
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
if n.IP.To4() == nil {
|
||||||
|
if hasIPv6 {
|
||||||
|
return nil, fmt.Errorf("directive %q: found two IPv6 CIDRs", d.Name)
|
||||||
|
}
|
||||||
|
hasIPv6 = true
|
||||||
|
} else {
|
||||||
|
if hasIPv4 {
|
||||||
|
return nil, fmt.Errorf("directive %q: found two IPv4 CIDRs", d.Name)
|
||||||
|
}
|
||||||
|
hasIPv4 = true
|
||||||
|
}
|
||||||
|
srv.UpstreamUserIPs = append(srv.UpstreamUserIPs, n)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown directive %q", d.Name)
|
return nil, fmt.Errorf("unknown directive %q", d.Name)
|
||||||
}
|
}
|
||||||
|
@ -156,6 +156,15 @@ The following directives are supported:
|
|||||||
Globally enable or disable multi-upstream mode. By default, multi-upstream
|
Globally enable or disable multi-upstream mode. By default, multi-upstream
|
||||||
mode is enabled.
|
mode is enabled.
|
||||||
|
|
||||||
|
*upstream-user-ip* <cidr...>
|
||||||
|
Enable per-user IP addresses. One IPv4 range and/or one IPv6 range can be
|
||||||
|
specified in CIDR notation. One IP address per range will be assigned to
|
||||||
|
each user and will be used as the source address when connecting to an
|
||||||
|
upstream network.
|
||||||
|
|
||||||
|
This can be useful to avoid having the whole bouncer banned from an upstream
|
||||||
|
network because of one malicious user.
|
||||||
|
|
||||||
# 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.
|
||||||
|
@ -64,6 +64,7 @@ type Config struct {
|
|||||||
MaxUserNetworks int
|
MaxUserNetworks int
|
||||||
MultiUpstream bool
|
MultiUpstream bool
|
||||||
MOTD string
|
MOTD string
|
||||||
|
UpstreamUserIPs []*net.IPNet
|
||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
|
16
upstream.go
16
upstream.go
@ -140,6 +140,11 @@ func connectToUpstream(network *network) (*upstreamConn, error) {
|
|||||||
addr = u.Host + ":6697"
|
addr = u.Host + ":6697"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dialer.LocalAddr, err = network.user.localTCPAddrForHost(host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to pick local IP for remote host %q: %v", host, err)
|
||||||
|
}
|
||||||
|
|
||||||
logger.Printf("connecting to TLS server at address %q", addr)
|
logger.Printf("connecting to TLS server at address %q", addr)
|
||||||
|
|
||||||
tlsConfig := &tls.Config{ServerName: host, NextProtos: []string{"irc"}}
|
tlsConfig := &tls.Config{ServerName: host, NextProtos: []string{"irc"}}
|
||||||
@ -174,8 +179,15 @@ func connectToUpstream(network *network) (*upstreamConn, error) {
|
|||||||
netConn = tls.Client(netConn, tlsConfig)
|
netConn = tls.Client(netConn, tlsConfig)
|
||||||
case "irc+insecure":
|
case "irc+insecure":
|
||||||
addr := u.Host
|
addr := u.Host
|
||||||
if _, _, err := net.SplitHostPort(addr); err != nil {
|
host, _, err := net.SplitHostPort(addr)
|
||||||
addr = addr + ":6667"
|
if err != nil {
|
||||||
|
host = u.Host
|
||||||
|
addr = u.Host + ":6667"
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer.LocalAddr, err = network.user.localTCPAddrForHost(host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to pick local IP for remote host %q: %v", host, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Printf("connecting to plain-text server at address %q", addr)
|
logger.Printf("connecting to plain-text server at address %q", addr)
|
||||||
|
45
user.go
45
user.go
@ -6,6 +6,8 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gopkg.in/irc.v3"
|
"gopkg.in/irc.v3"
|
||||||
@ -956,3 +958,46 @@ func (u *user) hasPersistentMsgStore() bool {
|
|||||||
_, isMem := u.msgStore.(*memoryMessageStore)
|
_, isMem := u.msgStore.(*memoryMessageStore)
|
||||||
return !isMem
|
return !isMem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// localAddrForHost returns the local address to use when connecting to host.
|
||||||
|
// A nil address is returned when the OS should automatically pick one.
|
||||||
|
func (u *user) localTCPAddrForHost(host string) (*net.TCPAddr, error) {
|
||||||
|
upstreamUserIPs := u.srv.Config().UpstreamUserIPs
|
||||||
|
if len(upstreamUserIPs) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ips, err := net.LookupIP(host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
wantIPv6 := false
|
||||||
|
for _, ip := range ips {
|
||||||
|
if ip.To4() == nil {
|
||||||
|
wantIPv6 = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ipNet *net.IPNet
|
||||||
|
for _, in := range upstreamUserIPs {
|
||||||
|
if wantIPv6 == (in.IP.To4() == nil) {
|
||||||
|
ipNet = in
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ipNet == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var ipInt big.Int
|
||||||
|
ipInt.SetBytes(ipNet.IP)
|
||||||
|
ipInt.Add(&ipInt, big.NewInt(u.ID+1))
|
||||||
|
ip := net.IP(ipInt.Bytes())
|
||||||
|
if !ipNet.Contains(ip) {
|
||||||
|
return nil, fmt.Errorf("IP network %v too small", ipNet)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &net.TCPAddr{IP: ip}, nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user