3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-25 13:29:27 +01:00

config: Advertise STS draft, fix subsequent REHASHing

This commit is contained in:
Daniel Oaks 2017-03-09 19:07:35 +10:00
parent 0675b09c88
commit 5c38800a02
5 changed files with 122 additions and 9 deletions

View File

@ -15,17 +15,19 @@ New release of Oragono!
### Added ### Added
* Added `USERHOST` command (thanks @vegax87). * Added `USERHOST` command (thanks @vegax87).
* Added draft IRCv3 capability [draft/sts](http://ircv3.net/specs/core/sts-3.3.html).
### Changed ### Changed
* Logging is now much better and useful. * Logging is now much better and useful.
* Can now specify years, months and days (e.g. `1y12m30d`) with DLINE and KLINE. * Can now specify years, months and days (e.g. `1y12m30d`) with DLINE and KLINE.
### Removed ### Removed
### Fixed ### Fixed
* Fixed an account issue where clients could login to multiple accounts at once. * Fixed an account issue where clients could login to multiple accounts at once.
* Fixed issues that prevented rehashing after the first rehash had gone through successfully.
## [0.6.0] - 2017-01-19 ## [0.6.0] - 2017-01-19
We've added a ton of new features in this release! Automated connection throttling, the ability to `KLINE`, updated casemapping and line-length specifications. We've added a ton of new features in this release! Automated connection throttling, the ability to `KLINE`, updated casemapping and line-length specifications.

View File

@ -28,6 +28,7 @@ const (
MultiPrefix Capability = "multi-prefix" MultiPrefix Capability = "multi-prefix"
SASL Capability = "sasl" SASL Capability = "sasl"
ServerTime Capability = "server-time" ServerTime Capability = "server-time"
STS Capability = "draft/sts"
UserhostInNames Capability = "userhost-in-names" UserhostInNames Capability = "userhost-in-names"
) )
@ -46,7 +47,8 @@ var (
MessageTags: true, MessageTags: true,
MultiPrefix: true, MultiPrefix: true,
// SASL is set during server startup // SASL is set during server startup
ServerTime: true, ServerTime: true,
// STS is set during server startup
UserhostInNames: true, UserhostInNames: true,
} }
CapValues = map[Capability]string{ CapValues = map[Capability]string{

View File

@ -14,6 +14,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/DanielOaks/oragono/irc/custime"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
@ -132,6 +133,26 @@ type LineLenConfig struct {
Rest int Rest int
} }
type STSConfig struct {
Enabled bool
Duration time.Duration `yaml:"duration-real"`
DurationString string `yaml:"duration"`
Port int
Preload bool
}
// Value returns the STS value to advertise in CAP
func (sts *STSConfig) Value() string {
val := fmt.Sprintf("duration=%d,", int(sts.Duration.Seconds()))
if sts.Enabled && sts.Port > 0 {
val += fmt.Sprintf(",port=%d", sts.Port)
}
if sts.Enabled && sts.Preload {
val += ",preload"
}
return val
}
type Config struct { type Config struct {
Network struct { Network struct {
Name string Name string
@ -144,8 +165,9 @@ type Config struct {
Listen []string Listen []string
Wslisten string `yaml:"ws-listen"` Wslisten string `yaml:"ws-listen"`
TLSListeners map[string]*TLSListenConfig `yaml:"tls-listeners"` TLSListeners map[string]*TLSListenConfig `yaml:"tls-listeners"`
RestAPI RestAPIConfig `yaml:"rest-api"` STS STSConfig
CheckIdent bool `yaml:"check-ident"` RestAPI RestAPIConfig `yaml:"rest-api"`
CheckIdent bool `yaml:"check-ident"`
MOTD string MOTD string
ConnectionLimits ConnectionLimitsConfig `yaml:"connection-limits"` ConnectionLimits ConnectionLimitsConfig `yaml:"connection-limits"`
ConnectionThrottle ConnectionThrottleConfig `yaml:"connection-throttling"` ConnectionThrottle ConnectionThrottleConfig `yaml:"connection-throttling"`
@ -342,6 +364,15 @@ 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.STS.Enabled {
config.Server.STS.Duration, err = custime.ParseDuration(config.Server.STS.DurationString)
if err != nil {
return nil, fmt.Errorf("Could not parse STS duration: %s", err.Error())
}
if config.Server.STS.Port < 0 || config.Server.STS.Port > 65535 {
return nil, fmt.Errorf("STS port is incorrect, should be 0 if disabled: %d", config.Server.STS.Port)
}
}
if config.Server.ConnectionThrottle.Enabled { if config.Server.ConnectionThrottle.Enabled {
config.Server.ConnectionThrottle.Duration, err = time.ParseDuration(config.Server.ConnectionThrottle.DurationString) config.Server.ConnectionThrottle.Duration, err = time.ParseDuration(config.Server.ConnectionThrottle.DurationString)
if err != nil { if err != nil {

View File

@ -118,6 +118,7 @@ type Server struct {
restAPI *RestAPIConfig restAPI *RestAPIConfig
signals chan os.Signal signals chan os.Signal
store *buntdb.DB store *buntdb.DB
stsEnabled bool
whoWas *WhoWasList whoWas *WhoWasList
} }
@ -154,6 +155,11 @@ func NewServer(configFilename string, config *Config, logger *Logger) (*Server,
SupportedCapabilities[SASL] = true SupportedCapabilities[SASL] = true
} }
if config.Server.STS.Enabled {
SupportedCapabilities[STS] = true
CapValues[STS] = config.Server.STS.Value()
}
if config.Limits.LineLen.Tags > 512 || config.Limits.LineLen.Rest > 512 { if config.Limits.LineLen.Tags > 512 || config.Limits.LineLen.Rest > 512 {
SupportedCapabilities[MaxLine] = true SupportedCapabilities[MaxLine] = true
CapValues[MaxLine] = fmt.Sprintf("%d,%d", config.Limits.LineLen.Tags, config.Limits.LineLen.Rest) CapValues[MaxLine] = fmt.Sprintf("%d,%d", config.Limits.LineLen.Tags, config.Limits.LineLen.Rest)
@ -212,6 +218,7 @@ func NewServer(configFilename string, config *Config, logger *Logger) (*Server,
operclasses: *operClasses, operclasses: *operClasses,
operators: opers, operators: opers,
signals: make(chan os.Signal, len(ServerExitSignals)), signals: make(chan os.Signal, len(ServerExitSignals)),
stsEnabled: config.Server.STS.Enabled,
rehashSignal: make(chan os.Signal, 1), rehashSignal: make(chan os.Signal, 1),
restAPI: &config.Server.RestAPI, restAPI: &config.Server.RestAPI,
whoWas: NewWhoWasList(config.Limits.WhowasEntries), whoWas: NewWhoWasList(config.Limits.WhowasEntries),
@ -1234,8 +1241,13 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
// rehash reloads the config and applies the changes from the config file. // rehash reloads the config and applies the changes from the config file.
func (server *Server) rehash() error { func (server *Server) rehash() error {
server.logger.Log(LogDebug, "rehash", "Starting rehash")
// only let one REHASH go on at a time // only let one REHASH go on at a time
server.rehashMutex.Lock() server.rehashMutex.Lock()
defer server.rehashMutex.Unlock()
server.logger.Log(LogDebug, "rehash", "Got rehash lock")
config, err := LoadConfig(server.configFilename) config, err := LoadConfig(server.configFilename)
@ -1290,11 +1302,13 @@ func (server *Server) rehash() error {
} }
} }
server.clients.ByNickMutex.RUnlock() server.clients.ByNickMutex.RUnlock()
server.connectionThrottleMutex.Unlock()
server.connectionLimitsMutex.Unlock() server.connectionLimitsMutex.Unlock()
// setup new and removed caps // setup new and removed caps
addedCaps := make(CapabilitySet) addedCaps := make(CapabilitySet)
removedCaps := make(CapabilitySet) removedCaps := make(CapabilitySet)
updatedCaps := make(CapabilitySet)
// SASL // SASL
if config.Accounts.AuthenticationEnabled && !server.accountAuthenticationEnabled { if config.Accounts.AuthenticationEnabled && !server.accountAuthenticationEnabled {
@ -1309,11 +1323,42 @@ func (server *Server) rehash() error {
} }
server.accountAuthenticationEnabled = config.Accounts.AuthenticationEnabled server.accountAuthenticationEnabled = config.Accounts.AuthenticationEnabled
// STS
stsValue := config.Server.STS.Value()
var stsDisabled bool
server.logger.Log(LogDebug, "rehash", "STS Vals", CapValues[STS], stsValue, fmt.Sprintf("server[%v] config[%v]", server.stsEnabled, config.Server.STS.Enabled))
if config.Server.STS.Enabled && !server.stsEnabled {
// enabling STS
SupportedCapabilities[STS] = true
addedCaps[STS] = true
CapValues[STS] = stsValue
} else if !config.Server.STS.Enabled && server.stsEnabled {
// disabling STS
SupportedCapabilities[STS] = false
removedCaps[STS] = true
stsDisabled = true
} else if config.Server.STS.Enabled && server.stsEnabled && stsValue != CapValues[STS] {
// STS policy updated
CapValues[STS] = stsValue
updatedCaps[STS] = true
}
server.stsEnabled = config.Server.STS.Enabled
// burst new and removed caps // burst new and removed caps
var capBurstClients ClientSet var capBurstClients ClientSet
added := make(map[CapVersion]string) added := make(map[CapVersion]string)
var removed string var removed string
// updated caps get DEL'd and then NEW'd
// so, we can just add updated ones to both removed and added lists here and they'll be correctly handled
server.logger.Log(LogDebug, "rehash", "Updated Caps", updatedCaps.String(Cap301), strconv.Itoa(len(updatedCaps)))
if len(updatedCaps) > 0 {
for capab := range updatedCaps {
addedCaps[capab] = true
removedCaps[capab] = true
}
}
if len(addedCaps) > 0 || len(removedCaps) > 0 { if len(addedCaps) > 0 || len(removedCaps) > 0 {
capBurstClients = server.clients.AllWithCaps(CapNotify) capBurstClients = server.clients.AllWithCaps(CapNotify)
@ -1324,15 +1369,30 @@ func (server *Server) rehash() error {
} }
for sClient := range capBurstClients { for sClient := range capBurstClients {
if len(addedCaps) > 0 { if stsDisabled {
sClient.Send(nil, server.name, "CAP", sClient.nick, "NEW", added[sClient.capVersion]) // remove STS policy
//TODO(dan): this is an ugly hack. we can write this better.
stsPolicy := "sts=duration=0"
if len(addedCaps) > 0 {
added[Cap302] = added[Cap302] + " " + stsPolicy
} else {
addedCaps[STS] = true
added[Cap302] = stsPolicy
}
} }
if len(removedCaps) > 0 { if len(removedCaps) > 0 {
sClient.Send(nil, server.name, "CAP", sClient.nick, "DEL", removed) sClient.Send(nil, server.name, "CAP", sClient.nick, "DEL", removed)
} }
if len(addedCaps) > 0 {
sClient.Send(nil, server.name, "CAP", sClient.nick, "NEW", added[sClient.capVersion])
}
} }
// set server options // set server options
lineLenConfig := LineLenLimits{
Tags: config.Limits.LineLen.Tags,
Rest: config.Limits.LineLen.Rest,
}
server.limits = Limits{ server.limits = Limits{
AwayLen: int(config.Limits.AwayLen), AwayLen: int(config.Limits.AwayLen),
ChannelLen: int(config.Limits.ChannelLen), ChannelLen: int(config.Limits.ChannelLen),
@ -1341,6 +1401,7 @@ func (server *Server) rehash() error {
NickLen: int(config.Limits.NickLen), NickLen: int(config.Limits.NickLen),
TopicLen: int(config.Limits.TopicLen), TopicLen: int(config.Limits.TopicLen),
ChanListModes: int(config.Limits.ChanListModes), ChanListModes: int(config.Limits.ChanListModes),
LineLen: lineLenConfig,
} }
server.operclasses = *operclasses server.operclasses = *operclasses
server.operators = opers server.operators = opers
@ -1403,7 +1464,6 @@ func (server *Server) rehash() error {
} }
} }
server.rehashMutex.Unlock()
return nil return nil
} }

View File

@ -26,6 +26,24 @@ server:
":6697": ":6697":
key: tls.key key: tls.key
cert: tls.crt cert: tls.crt
# strict transport security, to get clients to automagically use TLS
sts:
# whether to advertise STS
#
# to stop advertising STS, leave this enabled and set 'duration' below to "0". this will
# advertise to connecting users that the STS policy they have saved is no longer valid
enabled: true
# how long clients should be forced to use TLS for.
# setting this to a too-long time will mean bad things if you later remove your TLS
duration: 0
# tls port - you should be listening on this port above
port: 6697
# should clients include this STS policy when they ship their inbuilt preload lists?
preload: false
# rest management API, for use with web interface # rest management API, for use with web interface
rest-api: rest-api: