main.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "io"
  6. "log"
  7. "net"
  8. "os"
  9. "strconv"
  10. "strings"
  11. "time"
  12. "gitlab.torproject.org/tpo/anti-censorship/geoip"
  13. "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/ptutil/safelog"
  14. "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event"
  15. "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/version"
  16. sf "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/proxy/lib"
  17. )
  18. const minPollInterval = 2 * time.Second
  19. func main() {
  20. pollInterval := flag.Duration("poll-interval", sf.DefaultPollInterval,
  21. fmt.Sprint("how often to ask the broker for a new client. Keep in mind that asking for a client will not always result in getting one. Minumum value is ", minPollInterval, ". Valid time units are \"ms\", \"s\", \"m\", \"h\"."))
  22. capacity := flag.Uint("capacity", 0, "maximum concurrent clients (default is to accept an unlimited number of clients)")
  23. stunURL := flag.String("stun", sf.DefaultSTUNURL, "Comma-separated STUN server `URL`s that this proxy will use will use to, among some other things, determine its public IP address")
  24. logFilename := flag.String("log", "", "log `filename`. If not specified, logs will be output to stderr (console).")
  25. rawBrokerURL := flag.String("broker", sf.DefaultBrokerURL, "The `URL` of the broker server that the proxy will be using to find clients")
  26. unsafeLogging := flag.Bool("unsafe-logging", false, "keep IP addresses and other sensitive info in the logs")
  27. logLocalTime := flag.Bool("log-local-time", false, "Use local time for logging (default: UTC)")
  28. keepLocalAddresses := flag.Bool("keep-local-addresses", false, "keep local LAN address ICE candidates.\nThis is usually pointless because Snowflake clients don't usually reside on the same local network as the proxy.")
  29. defaultRelayURL := flag.String("relay", sf.DefaultRelayURL, "The default `URL` of the server (relay) that this proxy will forward client connections to, in case the broker itself did not specify the said URL")
  30. probeURL := flag.String("nat-probe-server", sf.DefaultNATProbeURL, "The `URL` of the server that this proxy will use to check its network NAT type.\nDetermining NAT type helps to understand whether this proxy is compatible with certain clients' NAT")
  31. outboundAddress := flag.String("outbound-address", "", "prefer the given `address` as outbound address for client connections")
  32. allowedRelayHostNamePattern := flag.String("allowed-relay-hostname-pattern", "snowflake.torproject.net$", "this proxy will only be allowed to forward client connections to relays (servers) whose URL matches this pattern.\nNote that a pattern \"example.com$\" will match \"subdomain.example.com\" as well as \"other-domain-example.com\".\nIn order to only match \"example.com\", prefix the pattern with \"^\": \"^example.com$\"")
  33. allowProxyingToPrivateAddresses := flag.Bool("allow-proxying-to-private-addresses", false, "allow forwarding client connections to private IP addresses.\nUseful when a Snowflake server (relay) is hosted on the same private network as this proxy.")
  34. allowNonTLSRelay := flag.Bool("allow-non-tls-relay", false, "allow this proxy to pass client's data to the relay in an unencrypted form.\nThis is only useful if the relay doesn't support encryption, e.g. for testing / development purposes.")
  35. NATTypeMeasurementInterval := flag.Duration("nat-retest-interval", time.Hour*24,
  36. "the time interval between NAT type is retests (see \"nat-probe-server\"). 0s disables retest. Valid time units are \"s\", \"m\", \"h\".")
  37. summaryInterval := flag.Duration("summary-interval", time.Hour,
  38. "the time interval between summary log outputs, 0s disables summaries. Valid time units are \"s\", \"m\", \"h\".")
  39. disableStatsLogger := flag.Bool("disable-stats-logger", false, "disable the exposing mechanism for stats using logs")
  40. enableMetrics := flag.Bool("metrics", false, "enable the exposing mechanism for stats using metrics")
  41. metricsAddress := flag.String("metrics-address", "localhost", "set listen `address` for metrics service")
  42. metricsPort := flag.Int("metrics-port", 9999, "set port for the metrics service")
  43. verboseLogging := flag.Bool("verbose", false, "increase log verbosity")
  44. ephemeralPortsRangeFlag := flag.String("ephemeral-ports-range", "", "Set the `range` of ports used for client connections (format:\"<min>:<max>\").\nUseful in conjunction with port forwarding, in order to make the proxy NAT type \"unrestricted\".\nIf omitted, the ports will be chosen automatically from a wide range.\nWhen specifying the range, make sure it's at least 2x as wide as the amount of clients that you are hoping to serve concurrently (see the \"capacity\" flag).")
  45. geoipDatabase := flag.String("geoipdb", "/usr/share/tor/geoip", "path to correctly formatted geoip database mapping IPv4 address ranges to country codes")
  46. geoip6Database := flag.String("geoip6db", "/usr/share/tor/geoip6", "path to correctly formatted geoip database mapping IPv6 address ranges to country codes")
  47. versionFlag := flag.Bool("version", false, "display version info to stderr and quit")
  48. var ephemeralPortsRange []uint16 = []uint16{0, 0}
  49. flag.Parse()
  50. if *versionFlag {
  51. fmt.Fprintf(os.Stderr, "snowflake-proxy %s", version.ConstructResult())
  52. os.Exit(0)
  53. }
  54. if *pollInterval < minPollInterval {
  55. log.Fatalf("poll-interval must be >= %v", minPollInterval)
  56. }
  57. if *outboundAddress != "" && *keepLocalAddresses {
  58. log.Fatal("Cannot keep local address candidates when outbound address is specified")
  59. }
  60. eventLogger := event.NewSnowflakeEventDispatcher()
  61. if *ephemeralPortsRangeFlag != "" {
  62. ephemeralPortsRangeParts := strings.Split(*ephemeralPortsRangeFlag, ":")
  63. if len(ephemeralPortsRangeParts) == 2 {
  64. ephemeralMinPort, err := strconv.ParseUint(ephemeralPortsRangeParts[0], 10, 16)
  65. if err != nil {
  66. log.Fatal(err)
  67. }
  68. ephemeralMaxPort, err := strconv.ParseUint(ephemeralPortsRangeParts[1], 10, 16)
  69. if err != nil {
  70. log.Fatal(err)
  71. }
  72. if ephemeralMinPort == 0 || ephemeralMaxPort == 0 {
  73. log.Fatal("Ephemeral port cannot be zero")
  74. }
  75. if ephemeralMinPort > ephemeralMaxPort {
  76. log.Fatal("Invalid port range: min > max")
  77. }
  78. ephemeralPortsRange = []uint16{uint16(ephemeralMinPort), uint16(ephemeralMaxPort)}
  79. } else {
  80. log.Fatalf("Bad range port format: %v", *ephemeralPortsRangeFlag)
  81. }
  82. }
  83. gip, err := geoip.New(*geoipDatabase, *geoip6Database)
  84. if *enableMetrics && err != nil {
  85. // The geoip DB is only used for metrics, let's only report the error if enabled
  86. log.Println("Error loading geoip db for country based metrics:", err)
  87. }
  88. proxy := sf.SnowflakeProxy{
  89. PollInterval: *pollInterval,
  90. Capacity: uint(*capacity),
  91. STUNURL: *stunURL,
  92. BrokerURL: *rawBrokerURL,
  93. KeepLocalAddresses: *keepLocalAddresses,
  94. RelayURL: *defaultRelayURL,
  95. NATProbeURL: *probeURL,
  96. OutboundAddress: *outboundAddress,
  97. EphemeralMinPort: ephemeralPortsRange[0],
  98. EphemeralMaxPort: ephemeralPortsRange[1],
  99. NATTypeMeasurementInterval: *NATTypeMeasurementInterval,
  100. EventDispatcher: eventLogger,
  101. RelayDomainNamePattern: *allowedRelayHostNamePattern,
  102. AllowProxyingToPrivateAddresses: *allowProxyingToPrivateAddresses,
  103. AllowNonTLSRelay: *allowNonTLSRelay,
  104. SummaryInterval: *summaryInterval,
  105. GeoIP: gip,
  106. }
  107. var logOutput = io.Discard
  108. var eventlogOutput io.Writer = os.Stderr
  109. loggerFlags := log.LstdFlags
  110. if !*logLocalTime {
  111. loggerFlags |= log.LUTC
  112. }
  113. log.SetFlags(loggerFlags)
  114. if *verboseLogging {
  115. logOutput = os.Stderr
  116. }
  117. if *logFilename != "" {
  118. f, err := os.OpenFile(*logFilename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
  119. if err != nil {
  120. log.Fatal(err)
  121. }
  122. defer f.Close()
  123. if *verboseLogging {
  124. logOutput = io.MultiWriter(logOutput, f)
  125. }
  126. eventlogOutput = io.MultiWriter(eventlogOutput, f)
  127. }
  128. if *unsafeLogging {
  129. log.SetOutput(logOutput)
  130. } else {
  131. log.SetOutput(&safelog.LogScrubber{Output: logOutput})
  132. }
  133. proxyEventLogger := sf.NewProxyEventLogger(eventlogOutput, *disableStatsLogger)
  134. eventLogger.AddSnowflakeEventListener(proxyEventLogger)
  135. if *enableMetrics {
  136. metrics := sf.NewMetrics()
  137. err := metrics.Start(net.JoinHostPort(*metricsAddress, strconv.Itoa(*metricsPort)))
  138. if err != nil {
  139. log.Fatalf("could not enable metrics: %v", err)
  140. }
  141. eventLogger.AddSnowflakeEventListener(sf.NewEventMetrics(metrics))
  142. }
  143. log.Printf("snowflake-proxy %s\n", version.GetVersion())
  144. err = proxy.Start()
  145. if err != nil {
  146. log.Fatal(err)
  147. }
  148. }