origin_service.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. package ingress
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "net"
  9. "net/http"
  10. "net/url"
  11. "strconv"
  12. "time"
  13. "github.com/pkg/errors"
  14. "github.com/rs/zerolog"
  15. "github.com/cloudflare/cloudflared/hello"
  16. "github.com/cloudflare/cloudflared/ipaccess"
  17. "github.com/cloudflare/cloudflared/management"
  18. "github.com/cloudflare/cloudflared/socks"
  19. "github.com/cloudflare/cloudflared/tlsconfig"
  20. )
  21. const (
  22. HelloWorldService = "hello_world"
  23. HelloWorldFlag = "hello-world"
  24. HttpStatusService = "http_status"
  25. )
  26. // OriginService is something a tunnel can proxy traffic to.
  27. type OriginService interface {
  28. String() string
  29. // Start the origin service if it's managed by cloudflared, e.g. proxy servers or Hello World.
  30. // If it's not managed by cloudflared, this is a no-op because the user is responsible for
  31. // starting the origin service.
  32. // Implementor of services managed by cloudflared should terminate the service if shutdownC is closed
  33. start(log *zerolog.Logger, shutdownC <-chan struct{}, cfg OriginRequestConfig) error
  34. MarshalJSON() ([]byte, error)
  35. }
  36. // unixSocketPath is an OriginService representing a unix socket (which accepts HTTP or HTTPS)
  37. type unixSocketPath struct {
  38. path string
  39. scheme string
  40. transport *http.Transport
  41. }
  42. func (o *unixSocketPath) String() string {
  43. scheme := ""
  44. if o.scheme == "https" {
  45. scheme = "+tls"
  46. }
  47. return fmt.Sprintf("unix%s:%s", scheme, o.path)
  48. }
  49. func (o *unixSocketPath) start(log *zerolog.Logger, _ <-chan struct{}, cfg OriginRequestConfig) error {
  50. transport, err := newHTTPTransport(o, cfg, log)
  51. if err != nil {
  52. return err
  53. }
  54. o.transport = transport
  55. return nil
  56. }
  57. func (o unixSocketPath) MarshalJSON() ([]byte, error) {
  58. return json.Marshal(o.String())
  59. }
  60. type httpService struct {
  61. url *url.URL
  62. hostHeader string
  63. transport *http.Transport
  64. matchSNIToHost bool
  65. }
  66. func (o *httpService) start(log *zerolog.Logger, _ <-chan struct{}, cfg OriginRequestConfig) error {
  67. transport, err := newHTTPTransport(o, cfg, log)
  68. if err != nil {
  69. return err
  70. }
  71. o.hostHeader = cfg.HTTPHostHeader
  72. o.transport = transport
  73. o.matchSNIToHost = cfg.MatchSNIToHost
  74. return nil
  75. }
  76. func (o *httpService) String() string {
  77. return o.url.String()
  78. }
  79. func (o httpService) MarshalJSON() ([]byte, error) {
  80. return json.Marshal(o.String())
  81. }
  82. // rawTCPService dials TCP to the destination specified by the client
  83. // It's used by warp routing
  84. type rawTCPService struct {
  85. name string
  86. dialer net.Dialer
  87. writeTimeout time.Duration
  88. logger *zerolog.Logger
  89. }
  90. func (o *rawTCPService) String() string {
  91. return o.name
  92. }
  93. func (o *rawTCPService) start(_ *zerolog.Logger, _ <-chan struct{}, _ OriginRequestConfig) error {
  94. return nil
  95. }
  96. func (o rawTCPService) MarshalJSON() ([]byte, error) {
  97. return json.Marshal(o.String())
  98. }
  99. // tcpOverWSService models TCP origins serving eyeballs connecting over websocket, such as
  100. // cloudflared access commands.
  101. type tcpOverWSService struct {
  102. scheme string
  103. dest string
  104. isBastion bool
  105. streamHandler streamHandlerFunc
  106. dialer net.Dialer
  107. }
  108. type socksProxyOverWSService struct {
  109. conn *socksProxyOverWSConnection
  110. }
  111. func newTCPOverWSService(url *url.URL) *tcpOverWSService {
  112. switch url.Scheme {
  113. case "ssh":
  114. addPortIfMissing(url, 22)
  115. case "rdp":
  116. addPortIfMissing(url, 3389)
  117. case "smb":
  118. addPortIfMissing(url, 445)
  119. case "tcp":
  120. addPortIfMissing(url, 7864) // just a random port since there isn't a default in this case
  121. }
  122. return &tcpOverWSService{
  123. scheme: url.Scheme,
  124. dest: url.Host,
  125. }
  126. }
  127. func newBastionService() *tcpOverWSService {
  128. return &tcpOverWSService{
  129. isBastion: true,
  130. }
  131. }
  132. func newSocksProxyOverWSService(accessPolicy *ipaccess.Policy) *socksProxyOverWSService {
  133. proxy := socksProxyOverWSService{
  134. conn: &socksProxyOverWSConnection{
  135. accessPolicy: accessPolicy,
  136. },
  137. }
  138. return &proxy
  139. }
  140. func addPortIfMissing(uri *url.URL, port int) {
  141. hostname := uri.Hostname()
  142. if uri.Port() == "" {
  143. uri.Host = net.JoinHostPort(hostname, strconv.FormatInt(int64(port), 10))
  144. }
  145. }
  146. func (o *tcpOverWSService) String() string {
  147. if o.isBastion {
  148. return ServiceBastion
  149. }
  150. if o.scheme != "" {
  151. return fmt.Sprintf("%s://%s", o.scheme, o.dest)
  152. } else {
  153. return o.dest
  154. }
  155. }
  156. func (o *tcpOverWSService) start(log *zerolog.Logger, _ <-chan struct{}, cfg OriginRequestConfig) error {
  157. if cfg.ProxyType == socksProxy {
  158. o.streamHandler = socks.StreamHandler
  159. } else {
  160. o.streamHandler = DefaultStreamHandler
  161. }
  162. o.dialer.Timeout = cfg.ConnectTimeout.Duration
  163. o.dialer.KeepAlive = cfg.TCPKeepAlive.Duration
  164. return nil
  165. }
  166. func (o tcpOverWSService) MarshalJSON() ([]byte, error) {
  167. return json.Marshal(o.String())
  168. }
  169. func (o *socksProxyOverWSService) start(log *zerolog.Logger, _ <-chan struct{}, cfg OriginRequestConfig) error {
  170. return nil
  171. }
  172. func (o *socksProxyOverWSService) String() string {
  173. return ServiceSocksProxy
  174. }
  175. func (o socksProxyOverWSService) MarshalJSON() ([]byte, error) {
  176. return json.Marshal(o.String())
  177. }
  178. // HelloWorld is an OriginService for the built-in Hello World server.
  179. // Users only use this for testing and experimenting with cloudflared.
  180. type helloWorld struct {
  181. httpService
  182. server net.Listener
  183. }
  184. func (o *helloWorld) String() string {
  185. return HelloWorldService
  186. }
  187. // Start starts a HelloWorld server and stores its address in the Service receiver.
  188. func (o *helloWorld) start(
  189. log *zerolog.Logger,
  190. shutdownC <-chan struct{},
  191. cfg OriginRequestConfig,
  192. ) error {
  193. if err := o.httpService.start(log, shutdownC, cfg); err != nil {
  194. return err
  195. }
  196. helloListener, err := hello.CreateTLSListener("127.0.0.1:")
  197. if err != nil {
  198. return errors.Wrap(err, "Cannot start Hello World Server")
  199. }
  200. go hello.StartHelloWorldServer(log, helloListener, shutdownC)
  201. o.server = helloListener
  202. o.httpService.url = &url.URL{
  203. Scheme: "https",
  204. Host: o.server.Addr().String(),
  205. }
  206. return nil
  207. }
  208. func (o helloWorld) MarshalJSON() ([]byte, error) {
  209. return json.Marshal(o.String())
  210. }
  211. // statusCode is an OriginService that just responds with a given HTTP status.
  212. // Typical use-case is "user wants the catch-all rule to just respond 404".
  213. type statusCode struct {
  214. code int
  215. // Set only when the user has not defined any ingress rules
  216. defaultResp bool
  217. log *zerolog.Logger
  218. }
  219. func newStatusCode(status int) statusCode {
  220. return statusCode{code: status}
  221. }
  222. // default status code (503) that is returned for requests to cloudflared that don't have any ingress rules setup
  223. func newDefaultStatusCode(log *zerolog.Logger) statusCode {
  224. return statusCode{code: 503, defaultResp: true, log: log}
  225. }
  226. func (o *statusCode) String() string {
  227. return fmt.Sprintf("http_status:%d", o.code)
  228. }
  229. func (o *statusCode) start(
  230. log *zerolog.Logger,
  231. _ <-chan struct{},
  232. cfg OriginRequestConfig,
  233. ) error {
  234. return nil
  235. }
  236. func (o statusCode) MarshalJSON() ([]byte, error) {
  237. return json.Marshal(o.String())
  238. }
  239. // WarpRoutingService starts a tcp stream between the origin and requests from
  240. // warp clients.
  241. type WarpRoutingService struct {
  242. Proxy StreamBasedOriginProxy
  243. }
  244. func NewWarpRoutingService(config WarpRoutingConfig, writeTimeout time.Duration) *WarpRoutingService {
  245. svc := &rawTCPService{
  246. name: ServiceWarpRouting,
  247. dialer: net.Dialer{
  248. Timeout: config.ConnectTimeout.Duration,
  249. KeepAlive: config.TCPKeepAlive.Duration,
  250. },
  251. writeTimeout: writeTimeout,
  252. }
  253. return &WarpRoutingService{Proxy: svc}
  254. }
  255. // ManagementService starts a local HTTP server to handle incoming management requests.
  256. type ManagementService struct {
  257. HTTPLocalProxy
  258. }
  259. func newManagementService(managementProxy HTTPLocalProxy) *ManagementService {
  260. return &ManagementService{
  261. HTTPLocalProxy: managementProxy,
  262. }
  263. }
  264. func (o *ManagementService) start(log *zerolog.Logger, _ <-chan struct{}, cfg OriginRequestConfig) error {
  265. return nil
  266. }
  267. func (o *ManagementService) String() string {
  268. return "management"
  269. }
  270. func (o ManagementService) MarshalJSON() ([]byte, error) {
  271. return json.Marshal(o.String())
  272. }
  273. func NewManagementRule(management *management.ManagementService) Rule {
  274. return Rule{
  275. Hostname: management.Hostname,
  276. Service: newManagementService(management),
  277. }
  278. }
  279. type NopReadCloser struct{}
  280. // Read always returns EOF to signal end of input
  281. func (nrc *NopReadCloser) Read(buf []byte) (int, error) {
  282. return 0, io.EOF
  283. }
  284. func (nrc *NopReadCloser) Close() error {
  285. return nil
  286. }
  287. func newHTTPTransport(service OriginService, cfg OriginRequestConfig, log *zerolog.Logger) (*http.Transport, error) {
  288. originCertPool, err := tlsconfig.LoadOriginCA(cfg.CAPool, log)
  289. if err != nil {
  290. return nil, errors.Wrap(err, "Error loading cert pool")
  291. }
  292. httpTransport := http.Transport{
  293. Proxy: http.ProxyFromEnvironment,
  294. MaxIdleConns: cfg.KeepAliveConnections,
  295. MaxIdleConnsPerHost: cfg.KeepAliveConnections,
  296. IdleConnTimeout: cfg.KeepAliveTimeout.Duration,
  297. TLSHandshakeTimeout: cfg.TLSTimeout.Duration,
  298. ExpectContinueTimeout: 1 * time.Second,
  299. TLSClientConfig: &tls.Config{RootCAs: originCertPool, InsecureSkipVerify: cfg.NoTLSVerify},
  300. ForceAttemptHTTP2: cfg.Http2Origin,
  301. }
  302. if _, isHelloWorld := service.(*helloWorld); !isHelloWorld && cfg.OriginServerName != "" {
  303. httpTransport.TLSClientConfig.ServerName = cfg.OriginServerName
  304. }
  305. dialer := &net.Dialer{
  306. Timeout: cfg.ConnectTimeout.Duration,
  307. KeepAlive: cfg.TCPKeepAlive.Duration,
  308. }
  309. if cfg.NoHappyEyeballs {
  310. dialer.FallbackDelay = -1 // As of Golang 1.12, a negative delay disables "happy eyeballs"
  311. }
  312. // DialContext depends on which kind of origin is being used.
  313. dialContext := dialer.DialContext
  314. switch service := service.(type) {
  315. // If this origin is a unix socket, enforce network type "unix".
  316. case *unixSocketPath:
  317. httpTransport.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) {
  318. return dialContext(ctx, "unix", service.path)
  319. }
  320. // Otherwise, use the regular network config.
  321. default:
  322. httpTransport.DialContext = dialContext
  323. }
  324. return &httpTransport, nil
  325. }
  326. // MockOriginHTTPService should only be used by other packages to mock OriginService. Set Transport to configure desired RoundTripper behavior.
  327. type MockOriginHTTPService struct {
  328. Transport http.RoundTripper
  329. }
  330. func (mos MockOriginHTTPService) RoundTrip(req *http.Request) (*http.Response, error) {
  331. return mos.Transport.RoundTrip(req)
  332. }
  333. func (mos MockOriginHTTPService) String() string {
  334. return "MockOriginService"
  335. }
  336. func (mos MockOriginHTTPService) start(log *zerolog.Logger, _ <-chan struct{}, cfg OriginRequestConfig) error {
  337. return nil
  338. }
  339. func (mos MockOriginHTTPService) MarshalJSON() ([]byte, error) {
  340. return json.Marshal(mos.String())
  341. }