mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-11 06:29:29 +01:00
Merge pull request #1187 from slingamn/defcon.2
fix #328 (implement DEFCON)
This commit is contained in:
commit
205e8c1b76
@ -553,6 +553,7 @@ oper-classes:
|
|||||||
- "vhosts"
|
- "vhosts"
|
||||||
- "chanreg"
|
- "chanreg"
|
||||||
- "history"
|
- "history"
|
||||||
|
- "defcon"
|
||||||
|
|
||||||
# ircd operators
|
# ircd operators
|
||||||
opers:
|
opers:
|
||||||
|
@ -579,6 +579,7 @@ oper-classes:
|
|||||||
- "vhosts"
|
- "vhosts"
|
||||||
- "chanreg"
|
- "chanreg"
|
||||||
- "history"
|
- "history"
|
||||||
|
- "defcon"
|
||||||
|
|
||||||
# ircd operators
|
# ircd operators
|
||||||
opers:
|
opers:
|
||||||
|
@ -380,8 +380,8 @@ func (am *AccountManager) Register(client *Client, account string, callbackNames
|
|||||||
|
|
||||||
config := am.server.Config()
|
config := am.server.Config()
|
||||||
|
|
||||||
// final "is registration allowed" check, probably redundant:
|
// final "is registration allowed" check:
|
||||||
if !(config.Accounts.Registration.Enabled || callbackNamespace == "admin") {
|
if !(config.Accounts.Registration.Enabled || callbackNamespace == "admin") || am.server.Defcon() <= 4 {
|
||||||
return errFeatureDisabled
|
return errFeatureDisabled
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1642,6 +1642,11 @@ func (am *AccountManager) performVHostChange(account string, munger vhostMunger)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if am.server.Defcon() <= 3 {
|
||||||
|
err = errFeatureDisabled
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
am.vHostUpdateMutex.Lock()
|
am.vHostUpdateMutex.Lock()
|
||||||
defer am.vHostUpdateMutex.Unlock()
|
defer am.vHostUpdateMutex.Unlock()
|
||||||
|
|
||||||
|
@ -706,7 +706,8 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
|
|||||||
return errBanned
|
return errBanned
|
||||||
}
|
}
|
||||||
|
|
||||||
if channel.flags.HasMode(modes.RegisteredOnly) && details.account == "" {
|
if details.account == "" &&
|
||||||
|
(channel.flags.HasMode(modes.RegisteredOnly) || channel.server.Defcon() <= 2) {
|
||||||
return errRegisteredOnly
|
return errRegisteredOnly
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,6 +187,10 @@ func (cm *ChannelManager) Cleanup(channel *Channel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cm *ChannelManager) SetRegistered(channelName string, account string) (err error) {
|
func (cm *ChannelManager) SetRegistered(channelName string, account string) (err error) {
|
||||||
|
if cm.server.Defcon() <= 4 {
|
||||||
|
return errFeatureDisabled
|
||||||
|
}
|
||||||
|
|
||||||
var channel *Channel
|
var channel *Channel
|
||||||
cfname, err := CasefoldChannel(channelName)
|
cfname, err := CasefoldChannel(channelName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -529,7 +529,7 @@ const (
|
|||||||
authFailSaslRequired
|
authFailSaslRequired
|
||||||
)
|
)
|
||||||
|
|
||||||
func (client *Client) isAuthorized(config *Config, session *Session) AuthOutcome {
|
func (client *Client) isAuthorized(server *Server, config *Config, session *Session) AuthOutcome {
|
||||||
saslSent := client.account != ""
|
saslSent := client.account != ""
|
||||||
// PASS requirement
|
// PASS requirement
|
||||||
if (config.Server.passwordBytes != nil) && session.passStatus != serverPassSuccessful && !(config.Accounts.SkipServerPassword && saslSent) {
|
if (config.Server.passwordBytes != nil) && session.passStatus != serverPassSuccessful && !(config.Accounts.SkipServerPassword && saslSent) {
|
||||||
@ -540,7 +540,8 @@ func (client *Client) isAuthorized(config *Config, session *Session) AuthOutcome
|
|||||||
return authFailTorSaslRequired
|
return authFailTorSaslRequired
|
||||||
}
|
}
|
||||||
// finally, enforce require-sasl
|
// finally, enforce require-sasl
|
||||||
if config.Accounts.RequireSasl.Enabled && !saslSent && !utils.IPInNets(session.IP(), config.Accounts.RequireSasl.exemptedNets) {
|
if !saslSent && (config.Accounts.RequireSasl.Enabled || server.Defcon() <= 2) &&
|
||||||
|
!utils.IPInNets(session.IP(), config.Accounts.RequireSasl.exemptedNets) {
|
||||||
return authFailSaslRequired
|
return authFailSaslRequired
|
||||||
}
|
}
|
||||||
return authSuccess
|
return authSuccess
|
||||||
|
@ -124,6 +124,10 @@ func init() {
|
|||||||
minParams: 1,
|
minParams: 1,
|
||||||
oper: true,
|
oper: true,
|
||||||
},
|
},
|
||||||
|
"DEFCON": {
|
||||||
|
handler: defconHandler,
|
||||||
|
capabs: []string{"defcon"},
|
||||||
|
},
|
||||||
"DEOPER": {
|
"DEOPER": {
|
||||||
handler: deoperHandler,
|
handler: deoperHandler,
|
||||||
minParams: 0,
|
minParams: 0,
|
||||||
|
@ -37,6 +37,14 @@ func (server *Server) Languages() (lm *languages.Manager) {
|
|||||||
return server.Config().languageManager
|
return server.Config().languageManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (server *Server) Defcon() uint32 {
|
||||||
|
return atomic.LoadUint32(&server.defcon)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (server *Server) SetDefcon(defcon uint32) {
|
||||||
|
atomic.StoreUint32(&server.defcon, defcon)
|
||||||
|
}
|
||||||
|
|
||||||
func (client *Client) Sessions() (sessions []*Session) {
|
func (client *Client) Sessions() (sessions []*Session) {
|
||||||
client.stateMutex.RLock()
|
client.stateMutex.RLock()
|
||||||
sessions = client.sessions
|
sessions = client.sessions
|
||||||
|
@ -759,6 +759,21 @@ func debugHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func defconHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
||||||
|
if len(msg.Params) > 0 {
|
||||||
|
level, err := strconv.Atoi(msg.Params[0])
|
||||||
|
if err == nil && 1 <= level && level <= 5 {
|
||||||
|
server.SetDefcon(uint32(level))
|
||||||
|
server.snomasks.Send(sno.LocalAnnouncements, fmt.Sprintf("%s [%s] set DEFCON level to %d", client.Nick(), client.Oper().Name, level))
|
||||||
|
} else {
|
||||||
|
rb.Add(nil, server.name, ERR_UNKNOWNERROR, client.Nick(), msg.Command, client.t("Invalid DEFCON parameter"))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rb.Notice(fmt.Sprintf(client.t("Current DEFCON level is %d"), server.Defcon()))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// helper for parsing the reason args to DLINE and KLINE
|
// helper for parsing the reason args to DLINE and KLINE
|
||||||
func getReasonsFromParams(params []string, currentArg int) (reason, operReason string) {
|
func getReasonsFromParams(params []string, currentArg int) (reason, operReason string) {
|
||||||
reason = "No reason given"
|
reason = "No reason given"
|
||||||
@ -2052,6 +2067,10 @@ func dispatchMessageToTarget(client *Client, tags map[string]string, histType hi
|
|||||||
tnick := tDetails.nick
|
tnick := tDetails.nick
|
||||||
|
|
||||||
details := client.Details()
|
details := client.Details()
|
||||||
|
if details.account == "" && server.Defcon() <= 3 {
|
||||||
|
rb.Add(nil, server.name, ERR_NEEDREGGEDNICK, client.Nick(), tnick, client.t("Direct messages from unregistered users are temporarily restricted"))
|
||||||
|
return
|
||||||
|
}
|
||||||
nickMaskString := details.nickMask
|
nickMaskString := details.nickMask
|
||||||
accountName := details.accountName
|
accountName := details.accountName
|
||||||
var deliverySessions []*Session
|
var deliverySessions []*Session
|
||||||
|
14
irc/help.go
14
irc/help.go
@ -164,6 +164,20 @@ Provides various debugging commands for the IRCd. <option> can be one of:
|
|||||||
* STOPCPUPROFILE: Stops the CPU profiler.
|
* STOPCPUPROFILE: Stops the CPU profiler.
|
||||||
* PROFILEHEAP: Writes a memory profile.
|
* PROFILEHEAP: Writes a memory profile.
|
||||||
* CRASHSERVER: Crashes the server (for use in failover testing)`,
|
* CRASHSERVER: Crashes the server (for use in failover testing)`,
|
||||||
|
},
|
||||||
|
"defcon": {
|
||||||
|
oper: true,
|
||||||
|
text: `DEFCON [level]
|
||||||
|
|
||||||
|
The DEFCON system can disable server features at runtime, to mitigate
|
||||||
|
spam or other hostile activity. It has five levels, which are cumulative
|
||||||
|
(i.e., level 3 includes all restrictions from level 4 and so on):
|
||||||
|
|
||||||
|
5: Normal operation
|
||||||
|
4: No new account or channel registrations
|
||||||
|
3: All users are +R; no changes to vhosts
|
||||||
|
2: No new unauthenticated connections; all channels are +R
|
||||||
|
1: No new connections except from localhost or other trusted IPs`,
|
||||||
},
|
},
|
||||||
"deoper": {
|
"deoper": {
|
||||||
oper: true,
|
oper: true,
|
||||||
|
@ -80,6 +80,7 @@ type Server struct {
|
|||||||
whoWas WhoWasList
|
whoWas WhoWasList
|
||||||
stats Stats
|
stats Stats
|
||||||
semaphores ServerSemaphores
|
semaphores ServerSemaphores
|
||||||
|
defcon uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer returns a new Oragono server.
|
// NewServer returns a new Oragono server.
|
||||||
@ -91,6 +92,7 @@ func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
|
|||||||
logger: logger,
|
logger: logger,
|
||||||
rehashSignal: make(chan os.Signal, 1),
|
rehashSignal: make(chan os.Signal, 1),
|
||||||
signals: make(chan os.Signal, len(ServerExitSignals)),
|
signals: make(chan os.Signal, len(ServerExitSignals)),
|
||||||
|
defcon: 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
server.clients.Initialize()
|
server.clients.Initialize()
|
||||||
@ -149,6 +151,12 @@ func (server *Server) Run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
|
func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
|
||||||
|
if server.Defcon() == 1 {
|
||||||
|
if !(ipaddr.IsLoopback() || utils.IPInNets(ipaddr, server.Config().Server.secureNets)) {
|
||||||
|
return true, "New connections to this server are temporarily restricted"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check DLINEs
|
// check DLINEs
|
||||||
isBanned, info := server.dlines.CheckIP(ipaddr)
|
isBanned, info := server.dlines.CheckIP(ipaddr)
|
||||||
if isBanned {
|
if isBanned {
|
||||||
@ -220,7 +228,8 @@ func (server *Server) tryRegister(c *Client, session *Session) (exiting bool) {
|
|||||||
|
|
||||||
// client MUST send PASS if necessary, or authenticate with SASL if necessary,
|
// client MUST send PASS if necessary, or authenticate with SASL if necessary,
|
||||||
// before completing the other registration commands
|
// before completing the other registration commands
|
||||||
authOutcome := c.isAuthorized(server.Config(), session)
|
config := server.Config()
|
||||||
|
authOutcome := c.isAuthorized(server, config, session)
|
||||||
var quitMessage string
|
var quitMessage string
|
||||||
switch authOutcome {
|
switch authOutcome {
|
||||||
case authFailPass:
|
case authFailPass:
|
||||||
@ -270,7 +279,7 @@ func (server *Server) tryRegister(c *Client, session *Session) (exiting bool) {
|
|||||||
// Apply default user modes (without updating the invisible counter)
|
// Apply default user modes (without updating the invisible counter)
|
||||||
// The number of invisible users will be updated by server.stats.Register
|
// The number of invisible users will be updated by server.stats.Register
|
||||||
// if we're using default user mode +i.
|
// if we're using default user mode +i.
|
||||||
for _, defaultMode := range server.Config().Accounts.defaultUserModes {
|
for _, defaultMode := range config.Accounts.defaultUserModes {
|
||||||
c.SetMode(defaultMode, true)
|
c.SetMode(defaultMode, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user