mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-10 22:19:31 +01:00
Add initial automated connection throttling
This commit is contained in:
parent
ddba5af265
commit
91d59575ce
@ -11,15 +11,17 @@ New release of Oragono!
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
* Added ARM build (for Raspberry PIs and similar).
|
* Added ARM build (for Raspberry PIs and similar).
|
||||||
* Added `KLINE` and `UNDLINE` commands. Complementing `KLINE`, this lets you ban masks from the server.
|
* Added automated connection throttling! To enable this, copy the `connection-throttling` section from the config.
|
||||||
|
* Added `KLINE` and `UNDLINE` commands. Complementing `DLINE`'s per-IP and per-network bans, this lets you ban masks from the server.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
* Connection limits can now be freely enabled or disabled. To enable automated limit handling, see the new `enabled` flag in the config, under `connection-limits`.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
* Fixed an issue where `UNDLINE` didn't save across server launches.
|
* Fixed an issue where `UNDLINE` didn't save across server launches.
|
||||||
* Removed several race conditions and made the server more resiliant to these bugs.
|
* Removed several race conditions which could result in server panics.
|
||||||
|
|
||||||
|
|
||||||
## [0.5.0] - 2016-12-10
|
## [0.5.0] - 2016-12-10
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
@ -95,12 +96,26 @@ type RestAPIConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ConnectionLimitsConfig struct {
|
type ConnectionLimitsConfig struct {
|
||||||
|
Enabled bool
|
||||||
CidrLenIPv4 int `yaml:"cidr-len-ipv4"`
|
CidrLenIPv4 int `yaml:"cidr-len-ipv4"`
|
||||||
CidrLenIPv6 int `yaml:"cidr-len-ipv6"`
|
CidrLenIPv6 int `yaml:"cidr-len-ipv6"`
|
||||||
IPsPerCidr int `yaml:"ips-per-subnet"`
|
IPsPerCidr int `yaml:"ips-per-subnet"`
|
||||||
Exempted []string
|
Exempted []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ConnectionThrottleConfig struct {
|
||||||
|
Enabled bool
|
||||||
|
CidrLenIPv4 int `yaml:"cidr-len-ipv4"`
|
||||||
|
CidrLenIPv6 int `yaml:"cidr-len-ipv6"`
|
||||||
|
ConnectionsPerCidr int `yaml:"max-connections"`
|
||||||
|
DurationString string `yaml:"duration"`
|
||||||
|
Duration time.Duration `yaml:"duration-time"`
|
||||||
|
BanDurationString string `yaml:"ban-duration"`
|
||||||
|
BanDuration time.Duration
|
||||||
|
BanMessage string `yaml:"ban-message"`
|
||||||
|
Exempted []string
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Network struct {
|
Network struct {
|
||||||
Name string
|
Name string
|
||||||
@ -118,6 +133,7 @@ type Config struct {
|
|||||||
Log string
|
Log string
|
||||||
MOTD string
|
MOTD string
|
||||||
ConnectionLimits ConnectionLimitsConfig `yaml:"connection-limits"`
|
ConnectionLimits ConnectionLimitsConfig `yaml:"connection-limits"`
|
||||||
|
ConnectionThrottle ConnectionThrottleConfig `yaml:"connection-throttling"`
|
||||||
}
|
}
|
||||||
|
|
||||||
Datastore struct {
|
Datastore struct {
|
||||||
@ -309,6 +325,16 @@ func LoadConfig(filename string) (config *Config, err error) {
|
|||||||
if config.Limits.NickLen < 1 || config.Limits.ChannelLen < 2 || config.Limits.AwayLen < 1 || config.Limits.KickLen < 1 || config.Limits.TopicLen < 1 {
|
if config.Limits.NickLen < 1 || config.Limits.ChannelLen < 2 || config.Limits.AwayLen < 1 || config.Limits.KickLen < 1 || config.Limits.TopicLen < 1 {
|
||||||
return nil, errors.New("Limits aren't setup properly, check them and make them sane")
|
return nil, errors.New("Limits aren't setup properly, check them and make them sane")
|
||||||
}
|
}
|
||||||
|
if config.Server.ConnectionThrottle.Enabled {
|
||||||
|
config.Server.ConnectionThrottle.Duration, err = time.ParseDuration(config.Server.ConnectionThrottle.DurationString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Could not parse connection-throttle duration: %s", err.Error())
|
||||||
|
}
|
||||||
|
config.Server.ConnectionThrottle.BanDuration, err = time.ParseDuration(config.Server.ConnectionThrottle.BanDurationString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Could not parse connection-throttle ban-duration: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ var (
|
|||||||
|
|
||||||
// ConnectionLimits manages the automated client connection limits.
|
// ConnectionLimits manages the automated client connection limits.
|
||||||
type ConnectionLimits struct {
|
type ConnectionLimits struct {
|
||||||
|
enabled bool
|
||||||
ipv4Mask net.IPMask
|
ipv4Mask net.IPMask
|
||||||
ipv6Mask net.IPMask
|
ipv6Mask net.IPMask
|
||||||
// subnetLimit is the maximum number of clients per subnet
|
// subnetLimit is the maximum number of clients per subnet
|
||||||
@ -44,6 +45,10 @@ func (cl *ConnectionLimits) maskAddr(addr net.IP) net.IP {
|
|||||||
// AddClient adds a client to our population if possible. If we can't, throws an error instead.
|
// AddClient adds a client to our population if possible. If we can't, throws an error instead.
|
||||||
// 'force' is used to add already-existing clients (i.e. ones that are already on the network).
|
// 'force' is used to add already-existing clients (i.e. ones that are already on the network).
|
||||||
func (cl *ConnectionLimits) AddClient(addr net.IP, force bool) error {
|
func (cl *ConnectionLimits) AddClient(addr net.IP, force bool) error {
|
||||||
|
if !cl.enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// check exempted lists
|
// check exempted lists
|
||||||
// we don't track populations for exempted addresses or nets - this is by design
|
// we don't track populations for exempted addresses or nets - this is by design
|
||||||
if cl.exemptedIPs[addr.String()] {
|
if cl.exemptedIPs[addr.String()] {
|
||||||
@ -70,6 +75,10 @@ func (cl *ConnectionLimits) AddClient(addr net.IP, force bool) error {
|
|||||||
|
|
||||||
// RemoveClient removes the given address from our population
|
// RemoveClient removes the given address from our population
|
||||||
func (cl *ConnectionLimits) RemoveClient(addr net.IP) {
|
func (cl *ConnectionLimits) RemoveClient(addr net.IP) {
|
||||||
|
if !cl.enabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
addrString := addr.String()
|
addrString := addr.String()
|
||||||
cl.population[addrString] = cl.population[addrString] - 1
|
cl.population[addrString] = cl.population[addrString] - 1
|
||||||
|
|
||||||
@ -82,6 +91,8 @@ func (cl *ConnectionLimits) RemoveClient(addr net.IP) {
|
|||||||
// NewConnectionLimits returns a new connection limit handler.
|
// NewConnectionLimits returns a new connection limit handler.
|
||||||
func NewConnectionLimits(config ConnectionLimitsConfig) (*ConnectionLimits, error) {
|
func NewConnectionLimits(config ConnectionLimitsConfig) (*ConnectionLimits, error) {
|
||||||
var cl ConnectionLimits
|
var cl ConnectionLimits
|
||||||
|
cl.enabled = config.Enabled
|
||||||
|
|
||||||
cl.population = make(map[string]int)
|
cl.population = make(map[string]int)
|
||||||
cl.exemptedIPs = make(map[string]bool)
|
cl.exemptedIPs = make(map[string]bool)
|
||||||
|
|
||||||
|
142
irc/connection_throttling.go
Normal file
142
irc/connection_throttling.go
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
// Copyright (c) 2016- Daniel Oaks <daniel@danieloaks.net>
|
||||||
|
// released under the MIT license
|
||||||
|
|
||||||
|
package irc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/DanielOaks/girc-go/ircmsg"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ThrottleDetails holds the connection-throttling details for a subnet/IP.
|
||||||
|
type ThrottleDetails struct {
|
||||||
|
Start time.Time
|
||||||
|
ClientCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectionThrottle manages automated client connection throttling.
|
||||||
|
type ConnectionThrottle struct {
|
||||||
|
enabled bool
|
||||||
|
ipv4Mask net.IPMask
|
||||||
|
ipv6Mask net.IPMask
|
||||||
|
subnetLimit int
|
||||||
|
duration time.Duration
|
||||||
|
population map[string]ThrottleDetails
|
||||||
|
|
||||||
|
// used by the server to ban clients that go over this limit
|
||||||
|
BanDuration time.Duration
|
||||||
|
BanMessage string
|
||||||
|
BanMessageBytes []byte
|
||||||
|
|
||||||
|
// exemptedIPs holds IPs that are exempt from limits
|
||||||
|
exemptedIPs map[string]bool
|
||||||
|
// exemptedNets holds networks that are exempt from limits
|
||||||
|
exemptedNets []net.IPNet
|
||||||
|
}
|
||||||
|
|
||||||
|
// maskAddr masks the given IPv4/6 address with our cidr limit masks.
|
||||||
|
func (ct *ConnectionThrottle) maskAddr(addr net.IP) net.IP {
|
||||||
|
if addr.To4() == nil {
|
||||||
|
// IPv6 addr
|
||||||
|
addr = addr.Mask(ct.ipv6Mask)
|
||||||
|
} else {
|
||||||
|
// IPv4 addr
|
||||||
|
addr = addr.Mask(ct.ipv4Mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetFor removes any existing count for the given address.
|
||||||
|
func (ct *ConnectionThrottle) ResetFor(addr net.IP) {
|
||||||
|
if !ct.enabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove
|
||||||
|
ct.maskAddr(addr)
|
||||||
|
addrString := addr.String()
|
||||||
|
delete(ct.population, addrString)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddClient introduces a new client connection if possible. If we can't, throws an error instead.
|
||||||
|
func (ct *ConnectionThrottle) AddClient(addr net.IP) error {
|
||||||
|
if !ct.enabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// check exempted lists
|
||||||
|
if ct.exemptedIPs[addr.String()] {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, ex := range ct.exemptedNets {
|
||||||
|
if ex.Contains(addr) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check throttle
|
||||||
|
ct.maskAddr(addr)
|
||||||
|
addrString := addr.String()
|
||||||
|
|
||||||
|
details, exists := ct.population[addrString]
|
||||||
|
if !exists || details.Start.Add(ct.duration).Before(time.Now()) {
|
||||||
|
details = ThrottleDetails{
|
||||||
|
Start: time.Now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if details.ClientCount+1 > ct.subnetLimit {
|
||||||
|
return errTooManyClients
|
||||||
|
}
|
||||||
|
|
||||||
|
details.ClientCount++
|
||||||
|
ct.population[addrString] = details
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConnectionThrottle returns a new client connection throttler.
|
||||||
|
func NewConnectionThrottle(config ConnectionThrottleConfig) (*ConnectionThrottle, error) {
|
||||||
|
var ct ConnectionThrottle
|
||||||
|
ct.enabled = config.Enabled
|
||||||
|
|
||||||
|
ct.population = make(map[string]ThrottleDetails)
|
||||||
|
ct.exemptedIPs = make(map[string]bool)
|
||||||
|
|
||||||
|
ct.ipv4Mask = net.CIDRMask(config.CidrLenIPv4, 32)
|
||||||
|
ct.ipv6Mask = net.CIDRMask(config.CidrLenIPv6, 128)
|
||||||
|
ct.subnetLimit = config.ConnectionsPerCidr
|
||||||
|
|
||||||
|
ct.duration = config.Duration
|
||||||
|
|
||||||
|
ct.BanDuration = config.BanDuration
|
||||||
|
ct.BanMessage = config.BanMessage
|
||||||
|
ircmsgOutput := ircmsg.MakeMessage(nil, "", "ERROR", ct.BanMessage)
|
||||||
|
msg, err := ircmsgOutput.Line()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Could not make error message: %s", err.Error())
|
||||||
|
}
|
||||||
|
ct.BanMessageBytes = []byte(msg)
|
||||||
|
|
||||||
|
// assemble exempted nets
|
||||||
|
for _, cidr := range config.Exempted {
|
||||||
|
ipaddr := net.ParseIP(cidr)
|
||||||
|
_, netaddr, err := net.ParseCIDR(cidr)
|
||||||
|
|
||||||
|
if ipaddr == nil && err != nil {
|
||||||
|
return nil, fmt.Errorf("Could not parse exempted IP/network [%s]", cidr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipaddr != nil {
|
||||||
|
ct.exemptedIPs[ipaddr.String()] = true
|
||||||
|
} else {
|
||||||
|
ct.exemptedNets = append(ct.exemptedNets, *netaddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ct, nil
|
||||||
|
}
|
@ -28,7 +28,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// cached because this may be used lots
|
// cached because this may be used lots
|
||||||
tooManyClientsMsg = ircmsg.MakeMessage(nil, "", "ERROR", "Too many clients from your IP or network")
|
tooManyClientsMsg = ircmsg.MakeMessage(nil, "", "ERROR", "Too many clients from your network")
|
||||||
tooManyClientsBytes, _ = tooManyClientsMsg.Line()
|
tooManyClientsBytes, _ = tooManyClientsMsg.Line()
|
||||||
|
|
||||||
bannedFromServerMsg = ircmsg.MakeMessage(nil, "", "ERROR", "You are banned from this server (%s)")
|
bannedFromServerMsg = ircmsg.MakeMessage(nil, "", "ERROR", "You are banned from this server (%s)")
|
||||||
@ -80,6 +80,8 @@ type Server struct {
|
|||||||
clients *ClientLookupSet
|
clients *ClientLookupSet
|
||||||
commands chan Command
|
commands chan Command
|
||||||
configFilename string
|
configFilename string
|
||||||
|
connectionThrottle *ConnectionThrottle
|
||||||
|
connectionThrottleMutex sync.Mutex // used when affecting the connection limiter, to make sure rehashing doesn't make things go out-of-whack
|
||||||
connectionLimits *ConnectionLimits
|
connectionLimits *ConnectionLimits
|
||||||
connectionLimitsMutex sync.Mutex // used when affecting the connection limiter, to make sure rehashing doesn't make things go out-of-whack
|
connectionLimitsMutex sync.Mutex // used when affecting the connection limiter, to make sure rehashing doesn't make things go out-of-whack
|
||||||
ctime time.Time
|
ctime time.Time
|
||||||
@ -157,6 +159,10 @@ func NewServer(configFilename string, config *Config) *Server {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error loading connection limits:", err.Error())
|
log.Fatal("Error loading connection limits:", err.Error())
|
||||||
}
|
}
|
||||||
|
connectionThrottle, err := NewConnectionThrottle(config.Server.ConnectionThrottle)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error loading connection throttler:", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
server := &Server{
|
server := &Server{
|
||||||
accounts: make(map[string]*ClientAccount),
|
accounts: make(map[string]*ClientAccount),
|
||||||
@ -166,6 +172,7 @@ func NewServer(configFilename string, config *Config) *Server {
|
|||||||
commands: make(chan Command),
|
commands: make(chan Command),
|
||||||
configFilename: configFilename,
|
configFilename: configFilename,
|
||||||
connectionLimits: connectionLimits,
|
connectionLimits: connectionLimits,
|
||||||
|
connectionThrottle: connectionThrottle,
|
||||||
ctime: time.Now(),
|
ctime: time.Now(),
|
||||||
currentOpers: make(map[*Client]bool),
|
currentOpers: make(map[*Client]bool),
|
||||||
idle: make(chan *Client),
|
idle: make(chan *Client),
|
||||||
@ -403,6 +410,27 @@ func (server *Server) Run() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check connection throttle
|
||||||
|
server.connectionThrottleMutex.Lock()
|
||||||
|
err = server.connectionThrottle.AddClient(ipaddr)
|
||||||
|
server.connectionThrottleMutex.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
// too many connections too quickly from client, tell them and close the connection
|
||||||
|
length := &IPRestrictTime{
|
||||||
|
Duration: server.connectionThrottle.BanDuration,
|
||||||
|
Expires: time.Now().Add(server.connectionThrottle.BanDuration),
|
||||||
|
}
|
||||||
|
server.dlines.AddIP(ipaddr, length, server.connectionThrottle.BanMessage, "Exceeded automated connection throttle")
|
||||||
|
|
||||||
|
// reset ban on connectionThrottle
|
||||||
|
server.connectionThrottle.ResetFor(ipaddr)
|
||||||
|
|
||||||
|
// this might not show up properly on some clients, but our objective here is just to close it out before it has a load impact on us
|
||||||
|
conn.Conn.Write([]byte(server.connectionThrottle.BanMessageBytes))
|
||||||
|
conn.Conn.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
go NewClient(server, conn.Conn, conn.IsTLS)
|
go NewClient(server, conn.Conn, conn.IsTLS)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -1066,23 +1094,29 @@ func (server *Server) rehash() error {
|
|||||||
config, err := LoadConfig(server.configFilename)
|
config, err := LoadConfig(server.configFilename)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error rehashing config file: %s", err.Error())
|
return fmt.Errorf("Error rehashing config file config: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// confirm connectionLimits are fine
|
// confirm connectionLimits are fine
|
||||||
connectionLimits, err := NewConnectionLimits(config.Server.ConnectionLimits)
|
connectionLimits, err := NewConnectionLimits(config.Server.ConnectionLimits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error rehashing config file: %s", err.Error())
|
return fmt.Errorf("Error rehashing config file connection-limits: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// confirm connectionThrottler is fine
|
||||||
|
connectionThrottle, err := NewConnectionThrottle(config.Server.ConnectionThrottle)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error rehashing config file connection-throttle: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// confirm operator stuff all exists and is fine
|
// confirm operator stuff all exists and is fine
|
||||||
operclasses, err := config.OperatorClasses()
|
operclasses, err := config.OperatorClasses()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error rehashing config file: %s", err.Error())
|
return fmt.Errorf("Error rehashing config file operclasses: %s", err.Error())
|
||||||
}
|
}
|
||||||
opers, err := config.Operators(operclasses)
|
opers, err := config.Operators(operclasses)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error rehashing config file: %s", err.Error())
|
return fmt.Errorf("Error rehashing config file opers: %s", err.Error())
|
||||||
}
|
}
|
||||||
for client := range server.currentOpers {
|
for client := range server.currentOpers {
|
||||||
_, exists := opers[client.operName]
|
_, exists := opers[client.operName]
|
||||||
@ -1094,6 +1128,8 @@ func (server *Server) rehash() error {
|
|||||||
// apply new connectionlimits
|
// apply new connectionlimits
|
||||||
server.connectionLimitsMutex.Lock()
|
server.connectionLimitsMutex.Lock()
|
||||||
server.connectionLimits = connectionLimits
|
server.connectionLimits = connectionLimits
|
||||||
|
server.connectionThrottleMutex.Lock()
|
||||||
|
server.connectionThrottle = connectionThrottle
|
||||||
|
|
||||||
server.clients.ByNickMutex.RLock()
|
server.clients.ByNickMutex.RLock()
|
||||||
for _, client := range server.clients.ByNick {
|
for _, client := range server.clients.ByNick {
|
||||||
|
31
oragono.yaml
31
oragono.yaml
@ -51,6 +51,9 @@ server:
|
|||||||
|
|
||||||
# maximum number of connections per subnet
|
# maximum number of connections per subnet
|
||||||
connection-limits:
|
connection-limits:
|
||||||
|
# whether to throttle limits or not
|
||||||
|
enabled: true
|
||||||
|
|
||||||
# how wide the cidr should be for IPv4
|
# how wide the cidr should be for IPv4
|
||||||
cidr-len-ipv4: 24
|
cidr-len-ipv4: 24
|
||||||
|
|
||||||
@ -66,6 +69,34 @@ server:
|
|||||||
- "127.0.0.1/8"
|
- "127.0.0.1/8"
|
||||||
- "::1/128"
|
- "::1/128"
|
||||||
|
|
||||||
|
# automated connection throttling
|
||||||
|
connection-throttling:
|
||||||
|
# whether to throttle connections or not
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# how wide the cidr should be for IPv4
|
||||||
|
cidr-len-ipv4: 32
|
||||||
|
|
||||||
|
# how wide the cidr should be for IPv6
|
||||||
|
cidr-len-ipv6: 128
|
||||||
|
|
||||||
|
# how long to keep track of connections for
|
||||||
|
duration: 10m
|
||||||
|
|
||||||
|
# maximum number of connections, per subnet, within the given duration
|
||||||
|
max-connections: 12
|
||||||
|
|
||||||
|
# how long to ban offenders for, and the message to use
|
||||||
|
# after banning them, the number of connections is reset (which lets you use UNDLINE to unban people)
|
||||||
|
ban-duration: 10m
|
||||||
|
ban-message: You have attempted to connect too many times within a short duration. Wait a while, and you will be able to connect.
|
||||||
|
|
||||||
|
# IPs/networks which are exempted from connection limits
|
||||||
|
exempted:
|
||||||
|
- "127.0.0.1"
|
||||||
|
- "127.0.0.1/8"
|
||||||
|
- "::1/128"
|
||||||
|
|
||||||
# account/channel registration
|
# account/channel registration
|
||||||
registration:
|
registration:
|
||||||
# account registration
|
# account registration
|
||||||
|
Loading…
Reference in New Issue
Block a user