tunnel.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. package tunneldns
  2. import (
  3. "net"
  4. "os"
  5. "os/signal"
  6. "strconv"
  7. "sync"
  8. "syscall"
  9. "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
  10. "github.com/cloudflare/cloudflared/logger"
  11. "github.com/cloudflare/cloudflared/metrics"
  12. "github.com/coredns/coredns/core/dnsserver"
  13. "github.com/coredns/coredns/plugin"
  14. "github.com/coredns/coredns/plugin/cache"
  15. "github.com/pkg/errors"
  16. "github.com/rs/zerolog"
  17. "github.com/urfave/cli/v2"
  18. )
  19. const (
  20. LogFieldAddress = "address"
  21. LogFieldURL = "url"
  22. )
  23. // Listener is an adapter between CoreDNS server and Warp runnable
  24. type Listener struct {
  25. server *dnsserver.Server
  26. wg sync.WaitGroup
  27. log *zerolog.Logger
  28. }
  29. func Command(hidden bool) *cli.Command {
  30. return &cli.Command{
  31. Name: "proxy-dns",
  32. Action: cliutil.ErrorHandler(Run),
  33. Usage: "Run a DNS over HTTPS proxy server.",
  34. Flags: []cli.Flag{
  35. &cli.StringFlag{
  36. Name: "metrics",
  37. Value: "localhost:",
  38. Usage: "Listen address for metrics reporting.",
  39. EnvVars: []string{"TUNNEL_METRICS"},
  40. },
  41. &cli.StringFlag{
  42. Name: "address",
  43. Usage: "Listen address for the DNS over HTTPS proxy server.",
  44. Value: "localhost",
  45. EnvVars: []string{"TUNNEL_DNS_ADDRESS"},
  46. },
  47. // Note TUN-3758 , we use Int because UInt is not supported with altsrc
  48. &cli.IntFlag{
  49. Name: "port",
  50. Usage: "Listen on given port for the DNS over HTTPS proxy server.",
  51. Value: 53,
  52. EnvVars: []string{"TUNNEL_DNS_PORT"},
  53. },
  54. &cli.StringSliceFlag{
  55. Name: "upstream",
  56. Usage: "Upstream endpoint URL, you can specify multiple endpoints for redundancy.",
  57. Value: cli.NewStringSlice("https://1.1.1.1/dns-query", "https://1.0.0.1/dns-query"),
  58. EnvVars: []string{"TUNNEL_DNS_UPSTREAM"},
  59. },
  60. &cli.StringSliceFlag{
  61. Name: "bootstrap",
  62. Usage: "bootstrap endpoint URL, you can specify multiple endpoints for redundancy.",
  63. Value: cli.NewStringSlice("https://162.159.36.1/dns-query", "https://162.159.46.1/dns-query", "https://[2606:4700:4700::1111]/dns-query", "https://[2606:4700:4700::1001]/dns-query"),
  64. EnvVars: []string{"TUNNEL_DNS_BOOTSTRAP"},
  65. },
  66. },
  67. ArgsUsage: " ", // can't be the empty string or we get the default output
  68. Hidden: hidden,
  69. }
  70. }
  71. // Run implements a foreground runner
  72. func Run(c *cli.Context) error {
  73. log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
  74. metricsListener, err := net.Listen("tcp", c.String("metrics"))
  75. if err != nil {
  76. log.Fatal().Err(err).Msg("Failed to open the metrics listener")
  77. }
  78. go metrics.ServeMetrics(metricsListener, nil, nil, log)
  79. listener, err := CreateListener(
  80. c.String("address"),
  81. // Note TUN-3758 , we use Int because UInt is not supported with altsrc
  82. uint16(c.Int("port")),
  83. c.StringSlice("upstream"),
  84. c.StringSlice("bootstrap"),
  85. log,
  86. )
  87. if err != nil {
  88. log.Err(err).Msg("Failed to create the listeners")
  89. return err
  90. }
  91. // Try to start the server
  92. readySignal := make(chan struct{})
  93. err = listener.Start(readySignal)
  94. if err != nil {
  95. log.Err(err).Msg("Failed to start the listeners")
  96. return listener.Stop()
  97. }
  98. <-readySignal
  99. // Wait for signal
  100. signals := make(chan os.Signal, 10)
  101. signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT)
  102. defer signal.Stop(signals)
  103. <-signals
  104. // Shut down server
  105. err = listener.Stop()
  106. if err != nil {
  107. log.Err(err).Msg("failed to stop")
  108. }
  109. return err
  110. }
  111. // Create a CoreDNS server plugin from configuration
  112. func createConfig(address string, port uint16, p plugin.Handler) *dnsserver.Config {
  113. c := &dnsserver.Config{
  114. Zone: ".",
  115. Transport: "dns",
  116. ListenHosts: []string{address},
  117. Port: strconv.FormatUint(uint64(port), 10),
  118. }
  119. c.AddPlugin(func(next plugin.Handler) plugin.Handler { return p })
  120. return c
  121. }
  122. // Start blocks for serving requests
  123. func (l *Listener) Start(readySignal chan struct{}) error {
  124. defer close(readySignal)
  125. l.log.Info().Str(LogFieldAddress, l.server.Address()).Msg("Starting DNS over HTTPS proxy server")
  126. // Start UDP listener
  127. if udp, err := l.server.ListenPacket(); err == nil {
  128. l.wg.Add(1)
  129. go func() {
  130. _ = l.server.ServePacket(udp)
  131. l.wg.Done()
  132. }()
  133. } else {
  134. return errors.Wrap(err, "failed to create a UDP listener")
  135. }
  136. // Start TCP listener
  137. tcp, err := l.server.Listen()
  138. if err == nil {
  139. l.wg.Add(1)
  140. go func() {
  141. _ = l.server.Serve(tcp)
  142. l.wg.Done()
  143. }()
  144. }
  145. return errors.Wrap(err, "failed to create a TCP listener")
  146. }
  147. // Stop signals server shutdown and blocks until completed
  148. func (l *Listener) Stop() error {
  149. if err := l.server.Stop(); err != nil {
  150. return err
  151. }
  152. l.wg.Wait()
  153. return nil
  154. }
  155. // CreateListener configures the server and bound sockets
  156. func CreateListener(address string, port uint16, upstreams []string, bootstraps []string, log *zerolog.Logger) (*Listener, error) {
  157. // Build the list of upstreams
  158. upstreamList := make([]Upstream, 0)
  159. for _, url := range upstreams {
  160. log.Info().Str(LogFieldURL, url).Msg("Adding DNS upstream")
  161. upstream, err := NewUpstreamHTTPS(url, bootstraps, log)
  162. if err != nil {
  163. return nil, errors.Wrap(err, "failed to create HTTPS upstream")
  164. }
  165. upstreamList = append(upstreamList, upstream)
  166. }
  167. // Create a local cache with HTTPS proxy plugin
  168. chain := cache.New()
  169. chain.Next = ProxyPlugin{
  170. Upstreams: upstreamList,
  171. }
  172. // Format an endpoint
  173. endpoint := "dns://" + net.JoinHostPort(address, strconv.FormatUint(uint64(port), 10))
  174. // Create the actual middleware server
  175. server, err := dnsserver.NewServer(endpoint, []*dnsserver.Config{createConfig(address, port, NewMetricsPlugin(chain))})
  176. if err != nil {
  177. return nil, err
  178. }
  179. return &Listener{server: server, log: log}, nil
  180. }