3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-14 16:09:32 +01:00

Allow WEBIRC from specified hosts

This commit is contained in:
Daniel Oaks 2017-10-15 16:18:14 +10:00
parent 0cc5e68e0f
commit 34987ba53a
7 changed files with 158 additions and 41 deletions

View File

@ -276,6 +276,11 @@ var Commands = map[string]Command{
handler: versionHandler, handler: versionHandler,
minParams: 0, minParams: 0,
}, },
"WEBIRC": {
handler: webircHandler,
usablePreReg: true,
minParams: 4,
},
"WHO": { "WHO": {
handler: whoHandler, handler: whoHandler,
minParams: 0, minParams: 0,

View File

@ -158,9 +158,10 @@ type Config struct {
STS STSConfig STS STSConfig
CheckIdent bool `yaml:"check-ident"` CheckIdent bool `yaml:"check-ident"`
MOTD string MOTD string
MOTDFormatting bool `yaml:"motd-formatting"` MOTDFormatting bool `yaml:"motd-formatting"`
ProxyAllowedFrom []string `yaml:"proxy-allowed-from"` ProxyAllowedFrom []string `yaml:"proxy-allowed-from"`
MaxSendQString string `yaml:"max-sendq"` WebIRC []webircConfig `yaml:"webirc"`
MaxSendQString string `yaml:"max-sendq"`
MaxSendQBytes uint64 MaxSendQBytes uint64
ConnectionLimiter connection_limits.LimiterConfig `yaml:"connection-limits"` ConnectionLimiter connection_limits.LimiterConfig `yaml:"connection-limits"`
ConnectionThrottler connection_limits.ThrottlerConfig `yaml:"connection-throttling"` ConnectionThrottler connection_limits.ThrottlerConfig `yaml:"connection-throttling"`
@ -393,6 +394,22 @@ func LoadConfig(filename string) (config *Config, err error) {
return nil, fmt.Errorf("Could not parse connection-throttle ban-duration: %s", err.Error()) return nil, fmt.Errorf("Could not parse connection-throttle ban-duration: %s", err.Error())
} }
} }
// process webirc blocks
var newWebIRC []webircConfig
for _, webirc := range config.Server.WebIRC {
// skip webirc blocks with no hosts (such as the example one)
if len(webirc.hosts) == 0 {
continue
}
err = webirc.ProcessPassword()
if err != nil {
return nil, fmt.Errorf("Could not parse WebIRC config: %s", err.Error())
}
newWebIRC = append(newWebIRC, webirc)
}
config.Server.WebIRC = newWebIRC
// process limits
if config.Limits.LineLen.Tags < 512 || config.Limits.LineLen.Rest < 512 { if config.Limits.LineLen.Tags < 512 || config.Limits.LineLen.Rest < 512 {
return nil, errors.New("Line lengths must be 512 or greater (check the linelen section under server->limits)") return nil, errors.New("Line lengths must be 512 or greater (check the linelen section under server->limits)")
} }

103
irc/gateways.go Normal file
View File

@ -0,0 +1,103 @@
// Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2014-2015 Edmund Huber
// Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license
package irc
import (
"fmt"
"net"
"github.com/oragono/oragono/irc/passwd"
"github.com/goshuirc/irc-go/ircmsg"
"github.com/oragono/oragono/irc/utils"
)
type webircConfig struct {
passwordString string `yaml:"password"`
password []byte `yaml:"password-bytes"`
hosts []string
}
// ProcessPassword populates our password.
func (wc *webircConfig) ProcessPassword() error {
password, error := passwd.DecodePasswordHash(wc.passwordString)
wc.password = password
return error
}
// WEBIRC password gateway hostname ip
func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
// only allow unregistered clients to use this command
if client.registered {
return false
}
clientAddress := utils.IPString(client.socket.conn.RemoteAddr())
clientHostname := client.hostname
server.configurableStateMutex.RLock()
defer server.configurableStateMutex.RUnlock()
for _, info := range server.webirc {
for _, address := range info.hosts {
if clientHostname == address || clientAddress == address {
// confirm password
givenPassword := msg.Params[0]
if passwd.ComparePasswordString(info.password, givenPassword) == nil {
proxiedIP := msg.Params[3]
return client.ApplyProxiedIP(proxiedIP)
}
}
}
}
client.Quit("WEBIRC command is not usable from your address or incorrect password given")
return true
}
// PROXY TCP4/6 SOURCEIP DESTIP SOURCEPORT DESTPORT
// http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
func proxyHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
// only allow unregistered clients to use this command
if client.registered {
return false
}
clientAddress := utils.IPString(client.socket.conn.RemoteAddr())
clientHostname := client.hostname
server.configurableStateMutex.RLock()
defer server.configurableStateMutex.RUnlock()
for _, address := range server.proxyAllowedFrom {
if clientHostname == address || clientAddress == address {
proxiedIP := msg.Params[1]
return client.ApplyProxiedIP(proxiedIP)
}
}
client.Quit("PROXY command is not usable from your address")
return true
}
// ApplyProxiedIP applies the given IP to the client.
func (client *Client) ApplyProxiedIP(proxiedIP string) (exiting bool) {
// ensure IP is sane
parsedProxiedIP := net.ParseIP(proxiedIP)
if parsedProxiedIP == nil {
client.Quit(fmt.Sprintf("Proxied IP address is not valid: [%s]", proxiedIP))
return true
}
isBanned, banMsg := client.server.checkBans(parsedProxiedIP)
if isBanned {
client.Quit(banMsg)
return true
}
// given IP is sane! override the client's current IP
client.proxiedIP = proxiedIP
client.rawHostname = utils.LookupHostname(proxiedIP)
client.hostname = client.rawHostname
return false
}

View File

@ -460,6 +460,14 @@ Shows information about the given users. Takes up to 10 nicknames.`,
text: `VERSION [server] text: `VERSION [server]
Views the version of software and the RPL_ISUPPORT tokens for the given server.`, Views the version of software and the RPL_ISUPPORT tokens for the given server.`,
},
"webirc": {
oper: true, // not really, but it's restricted anyways
text: `WEBIRC <password> <gateway> <hostname> <ip>
Used by web<->IRC gateways and bouncers, the WEBIRC command allows gateways to
pass-through the real IP addresses of clients:
ircv3.net/specs/extensions/webirc.html`,
}, },
"who": { "who": {
text: `WHO <name> [o] text: `WHO <name> [o]

View File

@ -43,3 +43,8 @@ func DecodePasswordHash(encoded string) (decoded []byte, err error) {
func ComparePassword(hash, password []byte) error { func ComparePassword(hash, password []byte) error {
return bcrypt.CompareHashAndPassword(hash, password) return bcrypt.CompareHashAndPassword(hash, password)
} }
// ComparePasswordString compares a given password string with the given hash.
func ComparePasswordString(hash []byte, password string) error {
return ComparePassword(hash, []byte(password))
}

View File

@ -118,6 +118,7 @@ type Server struct {
snomasks *SnoManager snomasks *SnoManager
store *buntdb.DB store *buntdb.DB
stsEnabled bool stsEnabled bool
webirc []webircConfig
whoWas *WhoWasList whoWas *WhoWasList
} }
@ -1260,6 +1261,9 @@ func (server *Server) applyConfig(config *Config, initial bool) error {
} }
server.configurableStateMutex.Unlock() server.configurableStateMutex.Unlock()
// apply new WebIRC command restrictions
server.webirc = config.Server.WebIRC
// apply new PROXY command restrictions // apply new PROXY command restrictions
server.proxyAllowedFrom = config.Server.ProxyAllowedFrom server.proxyAllowedFrom = config.Server.ProxyAllowedFrom
@ -2158,41 +2162,3 @@ func userhostHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool
return false return false
} }
// PROXY TCP4/6 SOURCEIP DESTIP SOURCEPORT DESTPORT
// http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
func proxyHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
// only allow unregistered clients to use this command
if client.registered {
return false
}
clientAddress := utils.IPString(client.socket.conn.RemoteAddr())
clientHostname := client.hostname
for _, address := range server.proxyAllowedFrom {
if clientHostname == address || clientAddress == address {
proxiedIP := msg.Params[1]
// ensure IP is sane
parsedProxiedIP := net.ParseIP(proxiedIP)
if parsedProxiedIP == nil {
client.Quit(fmt.Sprintf("Proxied IP address is not valid: [%s]", proxiedIP))
return true
}
isBanned, banMsg := server.checkBans(parsedProxiedIP)
if isBanned {
client.Quit(banMsg)
return true
}
// override the client's regular IP
client.proxiedIP = msg.Params[1]
client.rawHostname = utils.LookupHostname(msg.Params[1])
client.hostname = client.rawHostname
return false
}
}
client.Quit("PROXY command is not usable from your address")
return true
}

View File

@ -65,6 +65,19 @@ server:
# - localhost # - localhost
# - "127.0.0.1" # - "127.0.0.1"
# controls the use of the WEBIRC command (by IRC<->web interfaces, bouncers and similar)
webirc:
# one webirc block -- should correspond to one set of gateways
-
# password the gateway uses to connect, made with oragono genpasswd
password: JDJhJDA0JG9rTTVERlNRa0hpOEZpNkhjZE95SU9Da1BseFdlcWtOTEQxNEFERVlqbEZNTkdhOVlYUkMu
# hosts that can use this webirc command
hosts:
# - localhost
# - "127.0.0.1"
# - "0::1"
# maximum length of clients' sendQ in bytes # maximum length of clients' sendQ in bytes
# this should be big enough to hold /LIST and HELP replies # this should be big enough to hold /LIST and HELP replies
max-sendq: 16k max-sendq: 16k