mirror of
https://github.com/fumiama/terasu-cloudflared.git
synced 2026-06-07 01:50:23 +08:00
Allows for debugging the payloads that are sent in client mode to the ssh server. Required to be run with --log-directory to capture logging output. Additionally has maximum limit that is provided with the flag that will only capture the first N number of reads plus writes through the WebSocket stream. These reads/writes are not directly captured at the packet boundary so some reconstruction from the log messages will be required. Added User-Agent for all out-going cloudflared access tcp requests in client mode. Added check to not run terminal logging in cloudflared access tcp client mode to not obstruct the stdin and stdout.
158 lines
4.8 KiB
Go
158 lines
4.8 KiB
Go
package access
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/rs/zerolog"
|
|
"github.com/urfave/cli/v2"
|
|
|
|
"github.com/cloudflare/cloudflared/carrier"
|
|
"github.com/cloudflare/cloudflared/config"
|
|
"github.com/cloudflare/cloudflared/logger"
|
|
"github.com/cloudflare/cloudflared/stream"
|
|
"github.com/cloudflare/cloudflared/validation"
|
|
)
|
|
|
|
const (
|
|
LogFieldHost = "host"
|
|
cfAccessClientIDHeader = "Cf-Access-Client-Id"
|
|
cfAccessClientSecretHeader = "Cf-Access-Client-Secret"
|
|
)
|
|
|
|
// StartForwarder starts a client side websocket forward
|
|
func StartForwarder(forwarder config.Forwarder, shutdown <-chan struct{}, log *zerolog.Logger) error {
|
|
validURL, err := validation.ValidateUrl(forwarder.Listener)
|
|
if err != nil {
|
|
return errors.Wrap(err, "error validating origin URL")
|
|
}
|
|
|
|
// get the headers from the config file and add to the request
|
|
headers := make(http.Header)
|
|
if forwarder.TokenClientID != "" {
|
|
headers.Set(cfAccessClientIDHeader, forwarder.TokenClientID)
|
|
}
|
|
|
|
if forwarder.TokenSecret != "" {
|
|
headers.Set(cfAccessClientSecretHeader, forwarder.TokenSecret)
|
|
}
|
|
headers.Set("User-Agent", userAgent)
|
|
|
|
carrier.SetBastionDest(headers, forwarder.Destination)
|
|
|
|
options := &carrier.StartOptions{
|
|
OriginURL: forwarder.URL,
|
|
Headers: headers, //TODO: TUN-2688 support custom headers from config file
|
|
}
|
|
|
|
// we could add a cmd line variable for this bool if we want the SOCK5 server to be on the client side
|
|
wsConn := carrier.NewWSConnection(log)
|
|
|
|
log.Info().Str(LogFieldHost, validURL.Host).Msg("Start Websocket listener")
|
|
return carrier.StartForwarder(wsConn, validURL.Host, shutdown, options)
|
|
}
|
|
|
|
// ssh will start a WS proxy server for server mode
|
|
// or copy from stdin/stdout for client mode
|
|
// useful for proxying other protocols (like ssh) over websockets
|
|
// (which you can put Access in front of)
|
|
func ssh(c *cli.Context) error {
|
|
// If not running as a forwarder, disable terminal logs as it collides with the stdin/stdout of the parent process
|
|
outputTerminal := logger.DisableTerminalLog
|
|
if c.IsSet(sshURLFlag) {
|
|
outputTerminal = logger.EnableTerminalLog
|
|
}
|
|
log := logger.CreateSSHLoggerFromContext(c, outputTerminal)
|
|
|
|
// get the hostname from the cmdline and error out if its not provided
|
|
rawHostName := c.String(sshHostnameFlag)
|
|
hostname, err := validation.ValidateHostname(rawHostName)
|
|
if err != nil || rawHostName == "" {
|
|
return cli.ShowCommandHelp(c, "ssh")
|
|
}
|
|
originURL := ensureURLScheme(hostname)
|
|
|
|
// get the headers from the cmdline and add them
|
|
headers := buildRequestHeaders(c.StringSlice(sshHeaderFlag))
|
|
if c.IsSet(sshTokenIDFlag) {
|
|
headers.Set(cfAccessClientIDHeader, c.String(sshTokenIDFlag))
|
|
}
|
|
if c.IsSet(sshTokenSecretFlag) {
|
|
headers.Set(cfAccessClientSecretHeader, c.String(sshTokenSecretFlag))
|
|
}
|
|
headers.Set("User-Agent", userAgent)
|
|
|
|
carrier.SetBastionDest(headers, c.String(sshDestinationFlag))
|
|
|
|
options := &carrier.StartOptions{
|
|
OriginURL: originURL,
|
|
Headers: headers,
|
|
Host: hostname,
|
|
}
|
|
|
|
if connectTo := c.String(sshConnectTo); connectTo != "" {
|
|
parts := strings.Split(connectTo, ":")
|
|
switch len(parts) {
|
|
case 1:
|
|
options.OriginURL = fmt.Sprintf("https://%s", parts[0])
|
|
case 2:
|
|
options.OriginURL = fmt.Sprintf("https://%s:%s", parts[0], parts[1])
|
|
case 3:
|
|
options.OriginURL = fmt.Sprintf("https://%s:%s", parts[2], parts[1])
|
|
options.TLSClientConfig = &tls.Config{
|
|
InsecureSkipVerify: true,
|
|
ServerName: parts[0],
|
|
}
|
|
log.Warn().Msgf("Using insecure SSL connection because SNI overridden to %s", parts[0])
|
|
default:
|
|
return fmt.Errorf("invalid connection override: %s", connectTo)
|
|
}
|
|
}
|
|
|
|
// we could add a cmd line variable for this bool if we want the SOCK5 server to be on the client side
|
|
wsConn := carrier.NewWSConnection(log)
|
|
|
|
if c.NArg() > 0 || c.IsSet(sshURLFlag) {
|
|
forwarder, err := config.ValidateUrl(c, true)
|
|
if err != nil {
|
|
log.Err(err).Msg("Error validating origin URL")
|
|
return errors.Wrap(err, "error validating origin URL")
|
|
}
|
|
log.Info().Str(LogFieldHost, forwarder.Host).Msg("Start Websocket listener")
|
|
err = carrier.StartForwarder(wsConn, forwarder.Host, shutdownC, options)
|
|
if err != nil {
|
|
log.Err(err).Msg("Error on Websocket listener")
|
|
}
|
|
return err
|
|
}
|
|
|
|
var s io.ReadWriter
|
|
s = &carrier.StdinoutStream{}
|
|
if c.IsSet(sshDebugStream) {
|
|
maxMessages := c.Uint64(sshDebugStream)
|
|
if maxMessages == 0 {
|
|
// default to 10 if provided but unset
|
|
maxMessages = 10
|
|
}
|
|
logger := log.With().Str("host", hostname).Logger()
|
|
s = stream.NewDebugStream(s, &logger, maxMessages)
|
|
}
|
|
carrier.StartClient(wsConn, s, options)
|
|
return nil
|
|
}
|
|
|
|
func buildRequestHeaders(values []string) http.Header {
|
|
headers := make(http.Header)
|
|
for _, valuePair := range values {
|
|
split := strings.Split(valuePair, ":")
|
|
if len(split) > 1 {
|
|
headers.Add(strings.TrimSpace(split[0]), strings.TrimSpace(split[1]))
|
|
}
|
|
}
|
|
return headers
|
|
}
|