websocket.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. package websocket
  2. import (
  3. "bufio"
  4. "crypto/sha1"
  5. "crypto/tls"
  6. "encoding/base64"
  7. "encoding/binary"
  8. "encoding/json"
  9. "errors"
  10. "io"
  11. "net"
  12. "net/http"
  13. "time"
  14. "github.com/cloudflare/cloudflared/h2mux"
  15. "github.com/cloudflare/cloudflared/logger"
  16. "github.com/cloudflare/cloudflared/sshserver"
  17. "github.com/gorilla/websocket"
  18. )
  19. const (
  20. // Time allowed to write a message to the peer.
  21. writeWait = 10 * time.Second
  22. // Time allowed to read the next pong message from the peer.
  23. pongWait = 60 * time.Second
  24. // Send pings to peer with this period. Must be less than pongWait.
  25. pingPeriod = (pongWait * 9) / 10
  26. )
  27. var stripWebsocketHeaders = []string{
  28. "Upgrade",
  29. "Connection",
  30. "Sec-Websocket-Key",
  31. "Sec-Websocket-Version",
  32. "Sec-Websocket-Extensions",
  33. }
  34. // Conn is a wrapper around the standard gorilla websocket
  35. // but implements a ReadWriter
  36. type Conn struct {
  37. *websocket.Conn
  38. }
  39. // Read will read messages from the websocket connection
  40. func (c *Conn) Read(p []byte) (int, error) {
  41. _, message, err := c.Conn.ReadMessage()
  42. if err != nil {
  43. return 0, err
  44. }
  45. return copy(p, message), nil
  46. }
  47. // Write will write messages to the websocket connection
  48. func (c *Conn) Write(p []byte) (int, error) {
  49. if err := c.Conn.WriteMessage(websocket.BinaryMessage, p); err != nil {
  50. return 0, err
  51. }
  52. return len(p), nil
  53. }
  54. // IsWebSocketUpgrade checks to see if the request is a WebSocket connection.
  55. func IsWebSocketUpgrade(req *http.Request) bool {
  56. return websocket.IsWebSocketUpgrade(req)
  57. }
  58. // ClientConnect creates a WebSocket client connection for provided request. Caller is responsible for closing
  59. // the connection. The response body may not contain the entire response and does
  60. // not need to be closed by the application.
  61. func ClientConnect(req *http.Request, tlsClientConfig *tls.Config) (*websocket.Conn, *http.Response, error) {
  62. req.URL.Scheme = changeRequestScheme(req)
  63. wsHeaders := websocketHeaders(req)
  64. d := &websocket.Dialer{TLSClientConfig: tlsClientConfig}
  65. conn, response, err := d.Dial(req.URL.String(), wsHeaders)
  66. if err != nil {
  67. return nil, response, err
  68. }
  69. response.Header.Set("Sec-WebSocket-Accept", generateAcceptKey(req))
  70. return conn, response, err
  71. }
  72. // HijackConnection takes over an HTTP connection. Caller is responsible for closing connection.
  73. func HijackConnection(w http.ResponseWriter) (net.Conn, *bufio.ReadWriter, error) {
  74. hj, ok := w.(http.Hijacker)
  75. if !ok {
  76. return nil, nil, errors.New("hijack error")
  77. }
  78. conn, brw, err := hj.Hijack()
  79. if err != nil {
  80. return nil, nil, err
  81. }
  82. return conn, brw, nil
  83. }
  84. // Stream copies copy data to & from provided io.ReadWriters.
  85. func Stream(conn, backendConn io.ReadWriter) {
  86. proxyDone := make(chan struct{}, 2)
  87. go func() {
  88. io.Copy(conn, backendConn)
  89. proxyDone <- struct{}{}
  90. }()
  91. go func() {
  92. io.Copy(backendConn, conn)
  93. proxyDone <- struct{}{}
  94. }()
  95. // If one side is done, we are done.
  96. <-proxyDone
  97. }
  98. // DefaultStreamHandler is provided to the the standard websocket to origin stream
  99. // This exist to allow SOCKS to deframe data before it gets to the origin
  100. func DefaultStreamHandler(wsConn *Conn, remoteConn net.Conn, _ http.Header) {
  101. Stream(wsConn, remoteConn)
  102. }
  103. // StartProxyServer will start a websocket server that will decode
  104. // the websocket data and write the resulting data to the provided
  105. func StartProxyServer(logger logger.Service, listener net.Listener, staticHost string, shutdownC <-chan struct{}, streamHandler func(wsConn *Conn, remoteConn net.Conn, requestHeaders http.Header)) error {
  106. upgrader := websocket.Upgrader{
  107. ReadBufferSize: 1024,
  108. WriteBufferSize: 1024,
  109. }
  110. httpServer := &http.Server{Addr: listener.Addr().String(), Handler: nil}
  111. go func() {
  112. <-shutdownC
  113. httpServer.Close()
  114. }()
  115. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  116. // If remote is an empty string, get the destination from the client.
  117. finalDestination := staticHost
  118. if finalDestination == "" {
  119. if jumpDestination := r.Header.Get(h2mux.CFJumpDestinationHeader); jumpDestination == "" {
  120. logger.Error("Did not receive final destination from client. The --destination flag is likely not set")
  121. return
  122. } else {
  123. finalDestination = jumpDestination
  124. }
  125. }
  126. stream, err := net.Dial("tcp", finalDestination)
  127. if err != nil {
  128. logger.Errorf("Cannot connect to remote: %s", err)
  129. return
  130. }
  131. defer stream.Close()
  132. if !websocket.IsWebSocketUpgrade(r) {
  133. w.Write(nonWebSocketRequestPage())
  134. return
  135. }
  136. conn, err := upgrader.Upgrade(w, r, nil)
  137. if err != nil {
  138. logger.Errorf("failed to upgrade: %s", err)
  139. return
  140. }
  141. conn.SetReadDeadline(time.Now().Add(pongWait))
  142. conn.SetPongHandler(func(string) error { conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
  143. done := make(chan struct{})
  144. go pinger(logger, conn, done)
  145. defer func() {
  146. done <- struct{}{}
  147. conn.Close()
  148. }()
  149. streamHandler(&Conn{conn}, stream, r.Header)
  150. })
  151. return httpServer.Serve(listener)
  152. }
  153. // SendSSHPreamble sends the final SSH destination address to the cloudflared SSH proxy
  154. // The destination is preceded by its length
  155. // Not part of sshserver module to fix compilation for incompatible operating systems
  156. func SendSSHPreamble(stream net.Conn, destination, token string) error {
  157. preamble := sshserver.SSHPreamble{Destination: destination, JWT: token}
  158. payload, err := json.Marshal(preamble)
  159. if err != nil {
  160. return err
  161. }
  162. if uint16(len(payload)) > ^uint16(0) {
  163. return errors.New("ssh preamble payload too large")
  164. }
  165. sizeBytes := make([]byte, sshserver.SSHPreambleLength)
  166. binary.BigEndian.PutUint16(sizeBytes, uint16(len(payload)))
  167. if _, err := stream.Write(sizeBytes); err != nil {
  168. return err
  169. }
  170. if _, err := stream.Write(payload); err != nil {
  171. return err
  172. }
  173. return nil
  174. }
  175. // the gorilla websocket library sets its own Upgrade, Connection, Sec-WebSocket-Key,
  176. // Sec-WebSocket-Version and Sec-Websocket-Extensions headers.
  177. // https://github.com/gorilla/websocket/blob/master/client.go#L189-L194.
  178. func websocketHeaders(req *http.Request) http.Header {
  179. wsHeaders := make(http.Header)
  180. for key, val := range req.Header {
  181. wsHeaders[key] = val
  182. }
  183. // Assume the header keys are in canonical format.
  184. for _, header := range stripWebsocketHeaders {
  185. wsHeaders.Del(header)
  186. }
  187. wsHeaders.Set("Host", req.Host) // See TUN-1097
  188. return wsHeaders
  189. }
  190. // sha1Base64 sha1 and then base64 encodes str.
  191. func sha1Base64(str string) string {
  192. hasher := sha1.New()
  193. io.WriteString(hasher, str)
  194. hash := hasher.Sum(nil)
  195. return base64.StdEncoding.EncodeToString(hash)
  196. }
  197. // generateAcceptKey returns the string needed for the Sec-WebSocket-Accept header.
  198. // https://tools.ietf.org/html/rfc6455#section-1.3 describes this process in more detail.
  199. func generateAcceptKey(req *http.Request) string {
  200. return sha1Base64(req.Header.Get("Sec-WebSocket-Key") + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
  201. }
  202. // changeRequestScheme is needed as the gorilla websocket library requires the ws scheme.
  203. // (even though it changes it back to http/https, but ¯\_(ツ)_/¯.)
  204. func changeRequestScheme(req *http.Request) string {
  205. switch req.URL.Scheme {
  206. case "https":
  207. return "wss"
  208. case "http":
  209. return "ws"
  210. default:
  211. return req.URL.Scheme
  212. }
  213. }
  214. // pinger simulates the websocket connection to keep it alive
  215. func pinger(logger logger.Service, ws *websocket.Conn, done chan struct{}) {
  216. ticker := time.NewTicker(pingPeriod)
  217. defer ticker.Stop()
  218. for {
  219. select {
  220. case <-ticker.C:
  221. if err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil {
  222. logger.Debugf("failed to send ping message: %s", err)
  223. }
  224. case <-done:
  225. return
  226. }
  227. }
  228. }