3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-14 07:59:31 +01:00

Merge pull request #997 from slingamn/issue996_proto.1

fix #996
This commit is contained in:
Shivaram Lingamneni 2020-05-10 23:05:55 -07:00 committed by GitHub
commit 0f03b323d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 44 additions and 30 deletions

View File

@ -282,7 +282,7 @@ func (server *Server) RunClient(conn IRCConn) {
return return
} }
server.logger.Info("connect-ip", fmt.Sprintf("Client connecting from %v", realIP)) server.logger.Info("connect-ip", fmt.Sprintf("Client connecting: real IP %v, proxied IP %v", realIP, proxiedIP))
now := time.Now().UTC() now := time.Now().UTC()
config := server.Config() config := server.Config()
@ -327,22 +327,19 @@ func (server *Server) RunClient(conn IRCConn) {
session.resetFakelag() session.resetFakelag()
ApplyUserModeChanges(client, config.Accounts.defaultUserModes, false, nil) ApplyUserModeChanges(client, config.Accounts.defaultUserModes, false, nil)
if proxiedConn.Secure {
client.SetMode(modes.TLS, true)
}
if proxiedConn.Config.TLSConfig != nil { if proxiedConn.Config.TLSConfig != nil {
client.SetMode(modes.TLS, true)
// error is not useful to us here anyways so we can ignore it // error is not useful to us here anyways so we can ignore it
session.certfp, _ = utils.GetCertFP(proxiedConn.Conn, RegisterTimeout) session.certfp, _ = utils.GetCertFP(proxiedConn.Conn, RegisterTimeout)
} }
if session.isTor { if session.isTor {
client.SetMode(modes.TLS, true)
session.rawHostname = config.Server.TorListeners.Vhost session.rawHostname = config.Server.TorListeners.Vhost
client.rawHostname = session.rawHostname client.rawHostname = session.rawHostname
} else { } else {
if realIP.IsLoopback() || utils.IPInNets(realIP, config.Server.secureNets) {
// treat local connections as secure (may be overridden later by WEBIRC)
client.SetMode(modes.TLS, true)
}
if config.Server.CheckIdent { if config.Server.CheckIdent {
client.doIdentLookup(proxiedConn.Conn) client.doIdentLookup(proxiedConn.Conn)
} }

View File

@ -57,8 +57,9 @@ func (wc *webircConfig) Populate() (err error) {
// ApplyProxiedIP applies the given IP to the client. // ApplyProxiedIP applies the given IP to the client.
func (client *Client) ApplyProxiedIP(session *Session, proxiedIP net.IP, tls bool) (err error, quitMsg string) { func (client *Client) ApplyProxiedIP(session *Session, proxiedIP net.IP, tls bool) (err error, quitMsg string) {
// PROXY and WEBIRC are never accepted from a Tor listener, even if the address itself // PROXY and WEBIRC are never accepted from a Tor listener, even if the address itself
// is whitelisted: // is whitelisted. Furthermore, don't accept PROXY or WEBIRC if we already accepted
if session.isTor { // a proxied IP from any source (PROXY, WEBIRC, or X-Forwarded-For):
if session.isTor || session.proxiedIP != nil {
return errBadProxyLine, "" return errBadProxyLine, ""
} }

View File

@ -88,13 +88,6 @@ func (nl *NetListener) Stop() error {
return nl.listener.Close() return nl.listener.Close()
} }
// ensure that any IP we got from the PROXY line is trustworthy (otherwise, clear it)
func validateProxiedIP(conn *utils.WrappedConn, config *Config) {
if !utils.IPInNets(utils.AddrToIP(conn.RemoteAddr()), config.Server.proxyAllowedFromNets) {
conn.ProxiedIP = nil
}
}
func (nl *NetListener) serve() { func (nl *NetListener) serve() {
for { for {
conn, err := nl.listener.Accept() conn, err := nl.listener.Accept()
@ -103,9 +96,7 @@ func (nl *NetListener) serve() {
// hand off the connection // hand off the connection
wConn, ok := conn.(*utils.WrappedConn) wConn, ok := conn.(*utils.WrappedConn)
if ok { if ok {
if wConn.ProxiedIP != nil { confirmProxyData(wConn, "", "", "", nl.server.Config())
validateProxiedIP(wConn, nl.server.Config())
}
go nl.server.RunClient(NewIRCStreamConn(wConn)) go nl.server.RunClient(NewIRCStreamConn(wConn))
} else { } else {
nl.server.logger.Error("internal", "invalid connection type", nl.addr) nl.server.logger.Error("internal", "invalid connection type", nl.addr)
@ -159,8 +150,9 @@ func (wl *WSListener) Stop() error {
func (wl *WSListener) handle(w http.ResponseWriter, r *http.Request) { func (wl *WSListener) handle(w http.ResponseWriter, r *http.Request) {
config := wl.server.Config() config := wl.server.Config()
proxyAllowedFrom := config.Server.proxyAllowedFromNets remoteAddr := r.RemoteAddr
proxiedIP := utils.HandleXForwardedFor(r.RemoteAddr, r.Header.Get("X-Forwarded-For"), proxyAllowedFrom) xff := r.Header.Get("X-Forwarded-For")
xfp := r.Header.Get("X-Forwarded-Proto")
wsUpgrader := websocket.Upgrader{ wsUpgrader := websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { CheckOrigin: func(r *http.Request) bool {
@ -192,18 +184,39 @@ func (wl *WSListener) handle(w http.ResponseWriter, r *http.Request) {
conn.Close() conn.Close()
return return
} }
if wConn.ProxiedIP != nil {
validateProxiedIP(wConn, config) confirmProxyData(wConn, remoteAddr, xff, xfp, config)
} else {
// if there was no PROXY protocol IP, use the validated X-Forwarded-For IP instead,
// unless it is redundant
if proxiedIP != nil && !proxiedIP.Equal(utils.AddrToIP(wConn.RemoteAddr())) {
wConn.ProxiedIP = proxiedIP
}
}
// avoid a DoS attack from buffering excessively large messages: // avoid a DoS attack from buffering excessively large messages:
conn.SetReadLimit(maxReadQBytes) conn.SetReadLimit(maxReadQBytes)
go wl.server.RunClient(NewIRCWSConn(conn)) go wl.server.RunClient(NewIRCWSConn(conn))
} }
// validate conn.ProxiedIP and conn.Secure against config, HTTP headers, etc.
func confirmProxyData(conn *utils.WrappedConn, remoteAddr, xForwardedFor, xForwardedProto string, config *Config) {
if conn.ProxiedIP != nil {
if !utils.IPInNets(utils.AddrToIP(conn.RemoteAddr()), config.Server.proxyAllowedFromNets) {
conn.ProxiedIP = nil
}
} else if xForwardedFor != "" {
proxiedIP := utils.HandleXForwardedFor(remoteAddr, xForwardedFor, config.Server.proxyAllowedFromNets)
// don't set proxied IP if it is redundant with the actual IP
if proxiedIP != nil && !proxiedIP.Equal(utils.AddrToIP(conn.RemoteAddr())) {
conn.ProxiedIP = proxiedIP
}
}
if conn.Config.TLSConfig != nil || conn.Config.Tor {
// we terminated our own encryption:
conn.Secure = true
} else if !conn.Config.WebSocket {
// plaintext normal connection: loopback and secureNets are secure
realIP := utils.AddrToIP(conn.RemoteAddr())
conn.Secure = realIP.IsLoopback() || utils.IPInNets(realIP, config.Server.secureNets)
} else {
// plaintext websocket: trust X-Forwarded-Proto from a trusted source
conn.Secure = utils.IPInNets(utils.AddrToIP(conn.RemoteAddr()), config.Server.proxyAllowedFromNets) &&
xForwardedProto == "https"
}
}

View File

@ -93,6 +93,9 @@ type WrappedConn struct {
net.Conn net.Conn
ProxiedIP net.IP ProxiedIP net.IP
Config ListenerConfig Config ListenerConfig
// Secure indicates whether we believe the connection between us and the client
// was secure against interception and modification (including all proxies):
Secure bool
} }
// ReloadableListener is a wrapper for net.Listener that allows reloading // ReloadableListener is a wrapper for net.Listener that allows reloading