Просмотр исходного кода

Integrated MaxMind GeoIP lookup

Rod Hynes 10 лет назад
Родитель
Сommit
4aafc1354c
6 измененных файлов с 125 добавлено и 6 удалено
  1. 3 6
      psiphon/TCPConn.go
  2. 13 0
      psiphon/net.go
  3. 3 0
      psiphon/server/config.go
  4. 88 0
      psiphon/server/geoip.go
  5. 6 0
      psiphon/server/services.go
  6. 12 0
      psiphon/server/sshService.go

+ 3 - 6
psiphon/TCPConn.go

@@ -64,12 +64,9 @@ func makeTCPDialer(config *DialConfig) func(network, addr string) (net.Conn, err
 		// Note: when an upstream proxy is used, we don't know what IP address
 		// was resolved, by the proxy, for that destination.
 		if config.ResolvedIPCallback != nil && config.UpstreamProxyUrl == "" {
-			remoteAddr := conn.RemoteAddr()
-			if remoteAddr != nil {
-				host, _, err := net.SplitHostPort(conn.RemoteAddr().String())
-				if err == nil {
-					config.ResolvedIPCallback(host)
-				}
+			ipAddress := IPAddressFromAddr(conn.RemoteAddr())
+			if ipAddress != "" {
+				config.ResolvedIPCallback(ipAddress)
 			}
 		}
 		return conn, nil

+ 13 - 0
psiphon/net.go

@@ -377,3 +377,16 @@ func MakeTunneledHttpClient(
 		Timeout:   requestTimeout,
 	}, nil
 }
+
+// IPAddressFromAddr is a helper which extracts an IP address
+// from a net.Addr or returns "" if there is no IP address.
+func IPAddressFromAddr(addr net.Addr) string {
+	ipAddress := ""
+	if addr != nil {
+		host, _, err := net.SplitHostPort(addr.String())
+		if err == nil {
+			ipAddress = host
+		}
+	}
+	return ipAddress
+}

+ 3 - 0
psiphon/server/config.go

@@ -38,6 +38,7 @@ import (
 const (
 	SERVER_CONFIG_FILENAME                 = "psiphon-server.config"
 	SERVER_ENTRY_FILENAME                  = "serverEntry.dat"
+	DEFAULT_GEO_IP_DATABASE_FILENAME       = "GeoLite2-City.mmdb"
 	DEFAULT_SERVER_IP_ADDRESS              = "127.0.0.1"
 	WEB_SERVER_SECRET_BYTE_LENGTH          = 32
 	WEB_SERVER_CERTIFICATE_RSA_KEY_BITS    = 2048
@@ -55,6 +56,7 @@ const (
 )
 
 type Config struct {
+	GeoIPDatabaseFilename   string
 	ServerIPAddress         string
 	WebServerPort           int
 	WebServerSecret         string
@@ -174,6 +176,7 @@ func GenerateConfig(params *GenerateConfigParams) ([]byte, []byte, error) {
 	// Assemble config and server entry
 
 	config := &Config{
+		GeoIPDatabaseFilename:   DEFAULT_GEO_IP_DATABASE_FILENAME,
 		ServerIPAddress:         serverIPaddress,
 		WebServerPort:           webServerPort,
 		WebServerSecret:         webServerSecret,

+ 88 - 0
psiphon/server/geoip.go

@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2016, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package server
+
+import (
+	"net"
+
+	maxminddb "github.com/Psiphon-Inc/maxminddb-golang"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
+)
+
+const UNKNOWN_GEOIP_VALUE = "None"
+
+type GeoIPData struct {
+	Country string
+	City    string
+	ISP     string
+}
+
+func GeoIPLookup(ipAddress string) GeoIPData {
+
+	result := GeoIPData{
+		Country: UNKNOWN_GEOIP_VALUE,
+		City:    UNKNOWN_GEOIP_VALUE,
+		ISP:     UNKNOWN_GEOIP_VALUE,
+	}
+
+	ip := net.ParseIP(ipAddress)
+
+	if ip == nil || geoIPReader == nil {
+		return result
+	}
+
+	var geoIPFields struct {
+		Country struct {
+			ISOCode string `maxminddb:"iso_code"`
+		} `maxminddb:"country"`
+		City struct {
+			Names map[string]string `maxminddb:"names"`
+		} `maxminddb:"city"`
+		ISP string `maxminddb:"isp"`
+	}
+
+	err := geoIPReader.Lookup(ip, &geoIPFields)
+	if err != nil {
+		log.WithContextFields(LogFields{"error": err}).Warning("GeoIP lookup failed")
+	}
+
+	result.Country = geoIPFields.Country.ISOCode
+	name, ok := geoIPFields.City.Names["en"]
+	if ok {
+		result.City = name
+	}
+	result.ISP = geoIPFields.ISP
+
+	return result
+}
+
+var geoIPReader *maxminddb.Reader
+
+func InitGeoIP(config *Config) error {
+	if config.GeoIPDatabaseFilename != "" {
+		var err error
+		geoIPReader, err = maxminddb.Open(config.GeoIPDatabaseFilename)
+		if err != nil {
+			return psiphon.ContextError(err)
+		}
+		log.WithContext().Info("GeoIP initialized")
+	}
+	return nil
+}

+ 6 - 0
psiphon/server/services.go

@@ -37,6 +37,12 @@ func RunServices(encodedConfig []byte) error {
 
 	// TODO: init logging
 
+	err = InitGeoIP(config)
+	if err != nil {
+		log.WithContextFields(LogFields{"error": err}).Error("init GeoIP failed")
+		return psiphon.ContextError(err)
+	}
+
 	waitGroup := new(sync.WaitGroup)
 	shutdownBroadcast := make(chan struct{})
 	errors := make(chan error)

+ 12 - 0
psiphon/server/sshService.go

@@ -21,6 +21,7 @@ package server
 
 import (
 	"crypto/subtle"
+	"encoding/hex"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -180,6 +181,17 @@ func (sshServer *sshServer) passwordCallback(conn ssh.ConnMetadata, password []b
 		return nil, psiphon.ContextError(fmt.Errorf("invalid password for %q", conn.User()))
 	}
 
+	geoIPData := GeoIPLookup(psiphon.IPAddressFromAddr(conn.RemoteAddr()))
+
+	log.WithContextFields(
+		LogFields{
+			"sshSessionID":     hex.EncodeToString(conn.SessionID()),
+			"psiphonSessionID": sshPasswordPayload.SessionId,
+			"country":          geoIPData.Country,
+			"city":             geoIPData.City,
+			"ISP":              geoIPData.ISP,
+		}).Info("tunnel started")
+
 	return nil, nil
 }