2020-12-09 21:46:53 +00:00
|
|
|
package ingress
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2021-04-02 06:10:43 +00:00
|
|
|
"io"
|
2020-12-09 21:46:53 +00:00
|
|
|
"net"
|
|
|
|
"net/http"
|
2021-04-02 06:10:43 +00:00
|
|
|
"strings"
|
2020-12-09 21:46:53 +00:00
|
|
|
|
2021-03-23 14:30:43 +00:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
|
2021-03-26 04:04:56 +00:00
|
|
|
"github.com/cloudflare/cloudflared/carrier"
|
2021-02-02 18:27:50 +00:00
|
|
|
"github.com/cloudflare/cloudflared/websocket"
|
2020-12-09 21:46:53 +00:00
|
|
|
)
|
|
|
|
|
2021-02-05 13:01:53 +00:00
|
|
|
var (
|
2021-04-02 06:10:43 +00:00
|
|
|
switchingProtocolText = fmt.Sprintf("%d %s", http.StatusSwitchingProtocols, http.StatusText(http.StatusSwitchingProtocols))
|
|
|
|
errUnsupportedConnectionType = errors.New("internal error: unsupported connection type")
|
2021-02-05 13:01:53 +00:00
|
|
|
)
|
|
|
|
|
2020-12-09 21:46:53 +00:00
|
|
|
// HTTPOriginProxy can be implemented by origin services that want to proxy http requests.
|
|
|
|
type HTTPOriginProxy interface {
|
|
|
|
// RoundTrip is how cloudflared proxies eyeball requests to the actual origin services
|
|
|
|
http.RoundTripper
|
|
|
|
}
|
|
|
|
|
2021-02-05 13:01:53 +00:00
|
|
|
// StreamBasedOriginProxy can be implemented by origin services that want to proxy ws/TCP.
|
2020-12-09 21:46:53 +00:00
|
|
|
type StreamBasedOriginProxy interface {
|
2021-02-04 18:03:34 +00:00
|
|
|
EstablishConnection(r *http.Request) (OriginConnection, *http.Response, error)
|
2020-12-09 21:46:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (o *unixSocketPath) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
|
return o.transport.RoundTrip(req)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *httpService) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
|
// Rewrite the request URL so that it goes to the origin service.
|
|
|
|
req.URL.Host = o.url.Host
|
|
|
|
req.URL.Scheme = o.url.Scheme
|
2021-02-08 19:25:08 +00:00
|
|
|
if o.hostHeader != "" {
|
|
|
|
// For incoming requests, the Host header is promoted to the Request.Host field and removed from the Header map.
|
|
|
|
req.Host = o.hostHeader
|
|
|
|
}
|
2020-12-09 21:46:53 +00:00
|
|
|
return o.transport.RoundTrip(req)
|
|
|
|
}
|
|
|
|
|
2021-02-04 18:03:34 +00:00
|
|
|
func (o *httpService) EstablishConnection(req *http.Request) (OriginConnection, *http.Response, error) {
|
2021-04-02 06:10:43 +00:00
|
|
|
req = req.Clone(req.Context())
|
|
|
|
|
2021-02-02 18:27:50 +00:00
|
|
|
req.URL.Host = o.url.Host
|
2021-04-02 06:10:43 +00:00
|
|
|
req.URL.Scheme = o.url.Scheme
|
|
|
|
// allow ws(s) scheme for websocket-only origins, normal http(s) requests will fail
|
|
|
|
switch req.URL.Scheme {
|
|
|
|
case "ws":
|
|
|
|
req.URL.Scheme = "http"
|
|
|
|
case "wss":
|
|
|
|
req.URL.Scheme = "https"
|
|
|
|
}
|
|
|
|
|
2021-02-08 19:25:08 +00:00
|
|
|
if o.hostHeader != "" {
|
|
|
|
// For incoming requests, the Host header is promoted to the Request.Host field and removed from the Header map.
|
|
|
|
req.Host = o.hostHeader
|
|
|
|
}
|
2021-02-02 18:27:50 +00:00
|
|
|
|
2021-04-02 06:10:43 +00:00
|
|
|
return o.newWebsocketProxyConnection(req)
|
2020-12-09 21:46:53 +00:00
|
|
|
}
|
|
|
|
|
2021-04-02 06:10:43 +00:00
|
|
|
func (o *httpService) newWebsocketProxyConnection(req *http.Request) (OriginConnection, *http.Response, error) {
|
|
|
|
req.Header.Set("Connection", "Upgrade")
|
|
|
|
req.Header.Set("Upgrade", "websocket")
|
|
|
|
req.Header.Set("Sec-WebSocket-Version", "13")
|
|
|
|
|
|
|
|
req.ContentLength = 0
|
|
|
|
req.Body = nil
|
|
|
|
|
|
|
|
resp, err := o.transport.RoundTrip(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
toClose := resp.Body
|
|
|
|
defer func() {
|
|
|
|
if toClose != nil {
|
|
|
|
_ = toClose.Close()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
if resp.StatusCode != http.StatusSwitchingProtocols {
|
|
|
|
return nil, nil, fmt.Errorf("unexpected origin response: %s", resp.Status)
|
|
|
|
}
|
|
|
|
if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" {
|
|
|
|
return nil, nil, fmt.Errorf("unexpected upgrade: %q", resp.Header.Get("Upgrade"))
|
|
|
|
}
|
|
|
|
|
|
|
|
rwc, ok := resp.Body.(io.ReadWriteCloser)
|
|
|
|
if !ok {
|
|
|
|
return nil, nil, errUnsupportedConnectionType
|
|
|
|
}
|
|
|
|
conn := wsProxyConnection{
|
|
|
|
rwc: rwc,
|
|
|
|
}
|
|
|
|
// clear to prevent defer from closing
|
|
|
|
toClose = nil
|
|
|
|
|
|
|
|
return &conn, resp, nil
|
2020-12-09 21:46:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (o *statusCode) RoundTrip(_ *http.Request) (*http.Response, error) {
|
|
|
|
return o.resp, nil
|
|
|
|
}
|
|
|
|
|
2021-02-05 13:01:53 +00:00
|
|
|
func (o *rawTCPService) EstablishConnection(r *http.Request) (OriginConnection, *http.Response, error) {
|
|
|
|
dest, err := getRequestHost(r)
|
2020-12-09 21:46:53 +00:00
|
|
|
if err != nil {
|
2021-02-04 18:03:34 +00:00
|
|
|
return nil, nil, err
|
2020-12-09 21:46:53 +00:00
|
|
|
}
|
2021-02-05 13:01:53 +00:00
|
|
|
conn, err := net.Dial("tcp", dest)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
originConn := &tcpConnection{
|
|
|
|
conn: conn,
|
|
|
|
}
|
|
|
|
resp := &http.Response{
|
|
|
|
Status: switchingProtocolText,
|
|
|
|
StatusCode: http.StatusSwitchingProtocols,
|
|
|
|
ContentLength: -1,
|
|
|
|
}
|
|
|
|
return originConn, resp, nil
|
2020-12-09 21:46:53 +00:00
|
|
|
}
|
|
|
|
|
2021-01-11 19:59:45 +00:00
|
|
|
// getRequestHost returns the host of the http.Request.
|
|
|
|
func getRequestHost(r *http.Request) (string, error) {
|
|
|
|
if r.Host != "" {
|
|
|
|
return r.Host, nil
|
|
|
|
}
|
|
|
|
if r.URL != nil {
|
|
|
|
return r.URL.Host, nil
|
|
|
|
}
|
|
|
|
return "", errors.New("host not found")
|
|
|
|
}
|
|
|
|
|
2021-02-05 13:01:53 +00:00
|
|
|
func (o *tcpOverWSService) EstablishConnection(r *http.Request) (OriginConnection, *http.Response, error) {
|
|
|
|
var err error
|
|
|
|
dest := o.dest
|
|
|
|
if o.isBastion {
|
2021-03-26 04:04:56 +00:00
|
|
|
dest, err = carrier.ResolveBastionDest(r)
|
2021-02-05 13:01:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
conn, err := net.Dial("tcp", dest)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
originConn := &tcpOverWSConnection{
|
|
|
|
conn: conn,
|
|
|
|
streamHandler: o.streamHandler,
|
|
|
|
}
|
|
|
|
resp := &http.Response{
|
|
|
|
Status: switchingProtocolText,
|
|
|
|
StatusCode: http.StatusSwitchingProtocols,
|
|
|
|
Header: websocket.NewResponseHeader(r),
|
|
|
|
ContentLength: -1,
|
2021-01-11 19:59:45 +00:00
|
|
|
}
|
2021-02-05 13:01:53 +00:00
|
|
|
return originConn, resp, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-03-01 22:26:37 +00:00
|
|
|
func (o *socksProxyOverWSService) EstablishConnection(r *http.Request) (OriginConnection, *http.Response, error) {
|
|
|
|
originConn := o.conn
|
|
|
|
resp := &http.Response{
|
|
|
|
Status: switchingProtocolText,
|
|
|
|
StatusCode: http.StatusSwitchingProtocols,
|
|
|
|
Header: websocket.NewResponseHeader(r),
|
|
|
|
ContentLength: -1,
|
|
|
|
}
|
|
|
|
return originConn, resp, nil
|
|
|
|
}
|