// LoadConfig loads and validates a JSON encoded server config. func LoadConfig(configJSON []byte) (*Config, error) { var config Config err := json.Unmarshal(configJSON, &config) if err != nil { return nil, psiphon.ContextError(err) } if config.Fail2BanFormat != "" && strings.Count(config.Fail2BanFormat, "%s") != 1 { return nil, errors.New("Fail2BanFormat must have one '%%s' placeholder") } if config.ServerIPAddress == "" { return nil, errors.New("ServerIPAddress is missing from config file") } if config.WebServerPort > 0 && (config.WebServerSecret == "" || config.WebServerCertificate == "" || config.WebServerPrivateKey == "") { return nil, errors.New( "Web server requires WebServerSecret, WebServerCertificate, WebServerPrivateKey") } for tunnelProtocol, _ := range config.TunnelProtocolPorts { if psiphon.TunnelProtocolUsesSSH(tunnelProtocol) || psiphon.TunnelProtocolUsesObfuscatedSSH(tunnelProtocol) { if config.SSHPrivateKey == "" || config.SSHServerVersion == "" || config.SSHUserName == "" || config.SSHPassword == "" { return nil, fmt.Errorf( "Tunnel protocol %s requires SSHPrivateKey, SSHServerVersion, SSHUserName, SSHPassword", tunnelProtocol) } } if psiphon.TunnelProtocolUsesObfuscatedSSH(tunnelProtocol) { if config.ObfuscatedSSHKey == "" { return nil, fmt.Errorf( "Tunnel protocol %s requires ObfuscatedSSHKey", tunnelProtocol) } } if psiphon.TunnelProtocolUsesMeekHTTP(tunnelProtocol) || psiphon.TunnelProtocolUsesMeekHTTPS(tunnelProtocol) { if config.MeekCookieEncryptionPrivateKey == "" || config.MeekObfuscatedKey == "" { return nil, fmt.Errorf( "Tunnel protocol %s requires MeekCookieEncryptionPrivateKey, MeekObfuscatedKey", tunnelProtocol) } } if psiphon.TunnelProtocolUsesMeekHTTPS(tunnelProtocol) { if config.MeekCertificateCommonName == "" { return nil, fmt.Errorf( "Tunnel protocol %s requires MeekCertificateCommonName", tunnelProtocol) } } } validateNetworkAddress := func(address string) error { host, port, err := net.SplitHostPort(address) if err == nil && net.ParseIP(host) == nil { err = errors.New("Host must be an IP address") } if err == nil { _, err = strconv.Atoi(port) } return err } if config.UDPForwardDNSServerAddress != "" { if err := validateNetworkAddress(config.UDPForwardDNSServerAddress); err != nil { return nil, fmt.Errorf("UDPForwardDNSServerAddress is invalid: %s", err) } } if config.UDPInterceptUdpgwServerAddress != "" { if err := validateNetworkAddress(config.UDPInterceptUdpgwServerAddress); err != nil { return nil, fmt.Errorf("UDPInterceptUdpgwServerAddress is invalid: %s", err) } } return &config, nil }
func (sshServer *sshServer) handleClient(tunnelProtocol string, clientConn net.Conn) { sshServer.registerAcceptedClient(tunnelProtocol) defer sshServer.unregisterAcceptedClient(tunnelProtocol) geoIPData := sshServer.support.GeoIPService.Lookup( psiphon.IPAddressFromAddr(clientConn.RemoteAddr())) // TODO: apply reload of TrafficRulesSet to existing clients sshClient := newSshClient( sshServer, tunnelProtocol, geoIPData, sshServer.support.TrafficRulesSet.GetTrafficRules(geoIPData.Country)) // Wrap the base client connection with an ActivityMonitoredConn which will // terminate the connection if no data is received before the deadline. This // timeout is in effect for the entire duration of the SSH connection. Clients // must actively use the connection or send SSH keep alive requests to keep // the connection active. activityConn := psiphon.NewActivityMonitoredConn( clientConn, SSH_CONNECTION_READ_DEADLINE, false, nil) clientConn = activityConn // Further wrap the connection in a rate limiting ThrottledConn. rateLimits := sshClient.trafficRules.GetRateLimits(tunnelProtocol) clientConn = psiphon.NewThrottledConn( clientConn, rateLimits.DownstreamUnlimitedBytes, int64(rateLimits.DownstreamBytesPerSecond), rateLimits.UpstreamUnlimitedBytes, int64(rateLimits.UpstreamBytesPerSecond)) // Run the initial [obfuscated] SSH handshake in a goroutine so we can both // respect shutdownBroadcast and implement a specific handshake timeout. // The timeout is to reclaim network resources in case the handshake takes // too long. type sshNewServerConnResult struct { conn net.Conn sshConn *ssh.ServerConn channels <-chan ssh.NewChannel requests <-chan *ssh.Request err error } resultChannel := make(chan *sshNewServerConnResult, 2) if SSH_HANDSHAKE_TIMEOUT > 0 { time.AfterFunc(time.Duration(SSH_HANDSHAKE_TIMEOUT), func() { resultChannel <- &sshNewServerConnResult{err: errors.New("ssh handshake timeout")} }) } go func(conn net.Conn) { sshServerConfig := &ssh.ServerConfig{ PasswordCallback: sshClient.passwordCallback, AuthLogCallback: sshClient.authLogCallback, ServerVersion: sshServer.support.Config.SSHServerVersion, } sshServerConfig.AddHostKey(sshServer.sshHostKey) result := &sshNewServerConnResult{} // Wrap the connection in an SSH deobfuscator when required. if psiphon.TunnelProtocolUsesObfuscatedSSH(tunnelProtocol) { // Note: NewObfuscatedSshConn blocks on network I/O // TODO: ensure this won't block shutdown conn, result.err = psiphon.NewObfuscatedSshConn( psiphon.OBFUSCATION_CONN_MODE_SERVER, clientConn, sshServer.support.Config.ObfuscatedSSHKey) if result.err != nil { result.err = psiphon.ContextError(result.err) } } if result.err == nil { result.sshConn, result.channels, result.requests, result.err = ssh.NewServerConn(conn, sshServerConfig) } resultChannel <- result }(clientConn) var result *sshNewServerConnResult select { case result = <-resultChannel: case <-sshServer.shutdownBroadcast: // Close() will interrupt an ongoing handshake // TODO: wait for goroutine to exit before returning? clientConn.Close() return } if result.err != nil { clientConn.Close() // This is a Debug log due to noise. The handshake often fails due to I/O // errors as clients frequently interrupt connections in progress when // client-side load balancing completes a connection to a different server. log.WithContextFields(LogFields{"error": result.err}).Debug("handshake failed") return } sshClient.Lock() sshClient.sshConn = result.sshConn sshClient.activityConn = activityConn sshClient.Unlock() clientID, ok := sshServer.registerEstablishedClient(sshClient) if !ok { clientConn.Close() log.WithContext().Warning("register failed") return } defer sshServer.unregisterEstablishedClient(clientID) sshClient.runClient(result.channels, result.requests) // Note: sshServer.unregisterClient calls sshClient.Close(), // which also closes underlying transport Conn. }