main.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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/pluggable-transports/ptutil/safelog"
  13. "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event"
  14. "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/version"
  15. sf "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/proxy/lib"
  16. )
  17. const minPollInterval = 2 * time.Second
  18. func main() {
  19. pollInterval := flag.Duration("poll-interval", sf.DefaultPollInterval,
  20. 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\"."))
  21. capacity := flag.Uint("capacity", 0, "maximum concurrent clients (default is to accept an unlimited number of clients)")
  22. 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")
  23. logFilename := flag.String("log", "", "log `filename`. If not specified, logs will be output to stderr (console).")
  24. rawBrokerURL := flag.String("broker", sf.DefaultBrokerURL, "The `URL` of the broker server that the proxy will be using to find clients")
  25. unsafeLogging := flag.Bool("unsafe-logging", false, "keep IP addresses and other sensitive info in the logs")
  26. logLocalTime := flag.Bool("log-local-time", false, "Use local time for logging (default: UTC)")
  27. 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.")
  28. 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")
  29. 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")
  30. outboundAddress := flag.String("outbound-address", "", "prefer the given `address` as outbound address for client connections")
  31. 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$\"")
  32. 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.")
  33. 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.")
  34. NATTypeMeasurementInterval := flag.Duration("nat-retest-interval", time.Hour*24,
  35. "the time interval between NAT type is retests (see \"nat-probe-server\"). 0s disables retest. Valid time units are \"s\", \"m\", \"h\".")
  36. summaryInterval := flag.Duration("summary-interval", time.Hour,
  37. "the time interval between summary log outputs, 0s disables summaries. Valid time units are \"s\", \"m\", \"h\".")
  38. disableStatsLogger := flag.Bool("disable-stats-logger", false, "disable the exposing mechanism for stats using logs")
  39. enableMetrics := flag.Bool("metrics", false, "enable the exposing mechanism for stats using metrics")
  40. metricsAddress := flag.String("metrics-address", "localhost", "set listen `address` for metrics service")
  41. metricsPort := flag.Int("metrics-port", 9999, "set port for the metrics service")
  42. verboseLogging := flag.Bool("verbose", false, "increase log verbosity")
  43. ephemeralPortsRangeFlag := flag.String("ephemeral-ports-range", "", "Set the `range` of ports used for client connections (format:\"<min>:<max>\").\nIf omitted, the ports will be chosen automatically.")
  44. versionFlag := flag.Bool("version", false, "display version info to stderr and quit")
  45. var ephemeralPortsRange []uint16 = []uint16{0, 0}
  46. flag.Parse()
  47. if *versionFlag {
  48. fmt.Fprintf(os.Stderr, "snowflake-proxy %s", version.ConstructResult())
  49. os.Exit(0)
  50. }
  51. if *pollInterval < minPollInterval {
  52. log.Fatalf("poll-interval must be >= %v", minPollInterval)
  53. }
  54. if *outboundAddress != "" && *keepLocalAddresses {
  55. log.Fatal("Cannot keep local address candidates when outbound address is specified")
  56. }
  57. eventLogger := event.NewSnowflakeEventDispatcher()
  58. if *ephemeralPortsRangeFlag != "" {
  59. ephemeralPortsRangeParts := strings.Split(*ephemeralPortsRangeFlag, ":")
  60. if len(ephemeralPortsRangeParts) == 2 {
  61. ephemeralMinPort, err := strconv.ParseUint(ephemeralPortsRangeParts[0], 10, 16)
  62. if err != nil {
  63. log.Fatal(err)
  64. }
  65. ephemeralMaxPort, err := strconv.ParseUint(ephemeralPortsRangeParts[1], 10, 16)
  66. if err != nil {
  67. log.Fatal(err)
  68. }
  69. if ephemeralMinPort == 0 || ephemeralMaxPort == 0 {
  70. log.Fatal("Ephemeral port cannot be zero")
  71. }
  72. if ephemeralMinPort > ephemeralMaxPort {
  73. log.Fatal("Invalid port range: min > max")
  74. }
  75. ephemeralPortsRange = []uint16{uint16(ephemeralMinPort), uint16(ephemeralMaxPort)}
  76. } else {
  77. log.Fatalf("Bad range port format: %v", *ephemeralPortsRangeFlag)
  78. }
  79. }
  80. proxy := sf.SnowflakeProxy{
  81. PollInterval: *pollInterval,
  82. Capacity: uint(*capacity),
  83. STUNURL: *stunURL,
  84. BrokerURL: *rawBrokerURL,
  85. KeepLocalAddresses: *keepLocalAddresses,
  86. RelayURL: *defaultRelayURL,
  87. NATProbeURL: *probeURL,
  88. OutboundAddress: *outboundAddress,
  89. EphemeralMinPort: ephemeralPortsRange[0],
  90. EphemeralMaxPort: ephemeralPortsRange[1],
  91. NATTypeMeasurementInterval: *NATTypeMeasurementInterval,
  92. EventDispatcher: eventLogger,
  93. RelayDomainNamePattern: *allowedRelayHostNamePattern,
  94. AllowProxyingToPrivateAddresses: *allowProxyingToPrivateAddresses,
  95. AllowNonTLSRelay: *allowNonTLSRelay,
  96. SummaryInterval: *summaryInterval,
  97. }
  98. var logOutput = io.Discard
  99. var eventlogOutput io.Writer = os.Stderr
  100. loggerFlags := log.LstdFlags
  101. if !*logLocalTime {
  102. loggerFlags |= log.LUTC
  103. }
  104. log.SetFlags(loggerFlags)
  105. if *verboseLogging {
  106. logOutput = os.Stderr
  107. }
  108. if *logFilename != "" {
  109. f, err := os.OpenFile(*logFilename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
  110. if err != nil {
  111. log.Fatal(err)
  112. }
  113. defer f.Close()
  114. if *verboseLogging {
  115. logOutput = io.MultiWriter(logOutput, f)
  116. }
  117. eventlogOutput = io.MultiWriter(eventlogOutput, f)
  118. }
  119. if *unsafeLogging {
  120. log.SetOutput(logOutput)
  121. } else {
  122. log.SetOutput(&safelog.LogScrubber{Output: logOutput})
  123. }
  124. proxyEventLogger := sf.NewProxyEventLogger(eventlogOutput, *disableStatsLogger)
  125. eventLogger.AddSnowflakeEventListener(proxyEventLogger)
  126. if *enableMetrics {
  127. metrics := sf.NewMetrics()
  128. err := metrics.Start(net.JoinHostPort(*metricsAddress, strconv.Itoa(*metricsPort)))
  129. if err != nil {
  130. log.Fatalf("could not enable metrics: %v", err)
  131. }
  132. eventLogger.AddSnowflakeEventListener(sf.NewEventMetrics(metrics))
  133. }
  134. log.Printf("snowflake-proxy %s\n", version.GetVersion())
  135. err := proxy.Start()
  136. if err != nil {
  137. log.Fatal(err)
  138. }
  139. }