cloudflare/cloudflared

Public

mirrored from https://github.com/cloudflare/cloudflaredAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2023.7.3

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

cmd/cloudflared/access/carrier.go

146lines · modecode

1package access
2
3import (
4 "crypto/tls"
5 "fmt"
6 "io"
7 "net/http"
8 "strings"
9
10 "github.com/pkg/errors"
11 "github.com/rs/zerolog"
12 "github.com/urfave/cli/v2"
13
14 "github.com/cloudflare/cloudflared/carrier"
15 "github.com/cloudflare/cloudflared/config"
16 "github.com/cloudflare/cloudflared/logger"
17 "github.com/cloudflare/cloudflared/stream"
18 "github.com/cloudflare/cloudflared/validation"
19)
20
21const (
22 LogFieldHost = "host"
23 cfAccessClientIDHeader = "Cf-Access-Client-Id"
24 cfAccessClientSecretHeader = "Cf-Access-Client-Secret"
25)
26
27// StartForwarder starts a client side websocket forward
28func StartForwarder(forwarder config.Forwarder, shutdown <-chan struct{}, log *zerolog.Logger) error {
29 validURL, err := validation.ValidateUrl(forwarder.Listener)
30 if err != nil {
31 return errors.Wrap(err, "error validating origin URL")
32 }
33
34 // get the headers from the config file and add to the request
35 headers := make(http.Header)
36 if forwarder.TokenClientID != "" {
37 headers.Set(cfAccessClientIDHeader, forwarder.TokenClientID)
38 }
39
40 if forwarder.TokenSecret != "" {
41 headers.Set(cfAccessClientSecretHeader, forwarder.TokenSecret)
42 }
43 headers.Set("User-Agent", userAgent)
44
45 carrier.SetBastionDest(headers, forwarder.Destination)
46
47 options := &carrier.StartOptions{
48 OriginURL: forwarder.URL,
49 Headers: headers, //TODO: TUN-2688 support custom headers from config file
50 }
51
52 // we could add a cmd line variable for this bool if we want the SOCK5 server to be on the client side
53 wsConn := carrier.NewWSConnection(log)
54
55 log.Info().Str(LogFieldHost, validURL.Host).Msg("Start Websocket listener")
56 return carrier.StartForwarder(wsConn, validURL.Host, shutdown, options)
57}
58
59// ssh will start a WS proxy server for server mode
60// or copy from stdin/stdout for client mode
61// useful for proxying other protocols (like ssh) over websockets
62// (which you can put Access in front of)
63func ssh(c *cli.Context) error {
64 // If not running as a forwarder, disable terminal logs as it collides with the stdin/stdout of the parent process
65 outputTerminal := logger.DisableTerminalLog
66 if c.IsSet(sshURLFlag) {
67 outputTerminal = logger.EnableTerminalLog
68 }
69 log := logger.CreateSSHLoggerFromContext(c, outputTerminal)
70
71 // get the hostname from the cmdline and error out if its not provided
72 rawHostName := c.String(sshHostnameFlag)
73 url, err := parseURL(rawHostName)
74 if err != nil {
75 log.Err(err).Send()
76 return cli.ShowCommandHelp(c, "ssh")
77 }
78
79 // get the headers from the cmdline and add them
80 headers := parseRequestHeaders(c.StringSlice(sshHeaderFlag))
81 if c.IsSet(sshTokenIDFlag) {
82 headers.Set(cfAccessClientIDHeader, c.String(sshTokenIDFlag))
83 }
84 if c.IsSet(sshTokenSecretFlag) {
85 headers.Set(cfAccessClientSecretHeader, c.String(sshTokenSecretFlag))
86 }
87 headers.Set("User-Agent", userAgent)
88
89 carrier.SetBastionDest(headers, c.String(sshDestinationFlag))
90
91 options := &carrier.StartOptions{
92 OriginURL: url.String(),
93 Headers: headers,
94 Host: url.Host,
95 }
96
97 if connectTo := c.String(sshConnectTo); connectTo != "" {
98 parts := strings.Split(connectTo, ":")
99 switch len(parts) {
100 case 1:
101 options.OriginURL = fmt.Sprintf("https://%s", parts[0])
102 case 2:
103 options.OriginURL = fmt.Sprintf("https://%s:%s", parts[0], parts[1])
104 case 3:
105 options.OriginURL = fmt.Sprintf("https://%s:%s", parts[2], parts[1])
106 options.TLSClientConfig = &tls.Config{
107 InsecureSkipVerify: true,
108 ServerName: parts[0],
109 }
110 log.Warn().Msgf("Using insecure SSL connection because SNI overridden to %s", parts[0])
111 default:
112 return fmt.Errorf("invalid connection override: %s", connectTo)
113 }
114 }
115
116 // we could add a cmd line variable for this bool if we want the SOCK5 server to be on the client side
117 wsConn := carrier.NewWSConnection(log)
118
119 if c.NArg() > 0 || c.IsSet(sshURLFlag) {
120 forwarder, err := config.ValidateUrl(c, true)
121 if err != nil {
122 log.Err(err).Msg("Error validating origin URL")
123 return errors.Wrap(err, "error validating origin URL")
124 }
125 log.Info().Str(LogFieldHost, forwarder.Host).Msg("Start Websocket listener")
126 err = carrier.StartForwarder(wsConn, forwarder.Host, shutdownC, options)
127 if err != nil {
128 log.Err(err).Msg("Error on Websocket listener")
129 }
130 return err
131 }
132
133 var s io.ReadWriter
134 s = &carrier.StdinoutStream{}
135 if c.IsSet(sshDebugStream) {
136 maxMessages := c.Uint64(sshDebugStream)
137 if maxMessages == 0 {
138 // default to 10 if provided but unset
139 maxMessages = 10
140 }
141 logger := log.With().Str("host", url.Host).Logger()
142 s = stream.NewDebugStream(s, &logger, maxMessages)
143 }
144 carrier.StartClient(wsConn, s, options)
145 return nil
146}
147