141 lines
4.3 KiB
Go
141 lines
4.3 KiB
Go
package access
|
|
|
|
import (
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/cloudflare/cloudflared/carrier"
|
|
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
|
|
"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
|
|
"github.com/cloudflare/cloudflared/h2mux"
|
|
"github.com/cloudflare/cloudflared/logger"
|
|
"github.com/cloudflare/cloudflared/validation"
|
|
"github.com/pkg/errors"
|
|
cli "github.com/urfave/cli/v2"
|
|
)
|
|
|
|
// StartForwarder starts a client side websocket forward
|
|
func StartForwarder(forwarder config.Forwarder, shutdown <-chan struct{}, logger logger.Service) error {
|
|
validURLString, err := validation.ValidateUrl(forwarder.Listener)
|
|
if err != nil {
|
|
return errors.Wrap(err, "error validating origin URL")
|
|
}
|
|
|
|
validURL, err := url.Parse(validURLString)
|
|
if err != nil {
|
|
return errors.Wrap(err, "error parsing origin URL")
|
|
}
|
|
|
|
// get the headers from the config file and add to the request
|
|
headers := make(http.Header)
|
|
if forwarder.TokenClientID != "" {
|
|
headers.Set(h2mux.CFAccessClientIDHeader, forwarder.TokenClientID)
|
|
}
|
|
|
|
if forwarder.TokenSecret != "" {
|
|
headers.Set(h2mux.CFAccessClientSecretHeader, forwarder.TokenSecret)
|
|
}
|
|
|
|
if forwarder.Destination != "" {
|
|
headers.Add(h2mux.CFJumpDestinationHeader, 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(logger, false)
|
|
|
|
logger.Infof("Start Websocket listener on: %s", validURL.Host)
|
|
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 {
|
|
logDirectory, logLevel := config.FindLogSettings()
|
|
|
|
flagLogDirectory := c.String(sshLogDirectoryFlag)
|
|
if flagLogDirectory != "" {
|
|
logDirectory = flagLogDirectory
|
|
}
|
|
|
|
flagLogLevel := c.String(sshLogLevelFlag)
|
|
if flagLogLevel != "" {
|
|
logLevel = flagLogLevel
|
|
}
|
|
|
|
logger, err := logger.New(logger.DefaultFile(logDirectory), logger.LogLevelString(logLevel))
|
|
if err != nil {
|
|
return cliutil.PrintLoggerSetupError("error setting up logger", err)
|
|
}
|
|
|
|
// 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(h2mux.CFAccessClientIDHeader, c.String(sshTokenIDFlag))
|
|
}
|
|
if c.IsSet(sshTokenSecretFlag) {
|
|
headers.Set(h2mux.CFAccessClientSecretHeader, c.String(sshTokenSecretFlag))
|
|
}
|
|
|
|
destination := c.String(sshDestinationFlag)
|
|
if destination != "" {
|
|
headers.Add(h2mux.CFJumpDestinationHeader, destination)
|
|
}
|
|
|
|
options := &carrier.StartOptions{
|
|
OriginURL: originURL,
|
|
Headers: headers,
|
|
}
|
|
|
|
// 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(logger, false)
|
|
|
|
if c.NArg() > 0 || c.IsSet(sshURLFlag) {
|
|
localForwarder, err := config.ValidateUrl(c, true)
|
|
if err != nil {
|
|
logger.Errorf("Error validating origin URL: %s", err)
|
|
return errors.Wrap(err, "error validating origin URL")
|
|
}
|
|
forwarder, err := url.Parse(localForwarder)
|
|
if err != nil {
|
|
logger.Errorf("Error validating origin URL: %s", err)
|
|
return errors.Wrap(err, "error validating origin URL")
|
|
}
|
|
|
|
logger.Infof("Start Websocket listener on: %s", forwarder.Host)
|
|
err = carrier.StartForwarder(wsConn, forwarder.Host, shutdownC, options)
|
|
if err != nil {
|
|
logger.Errorf("Error on Websocket listener: %s", err)
|
|
}
|
|
return err
|
|
}
|
|
|
|
return carrier.StartClient(wsConn, &carrier.StdinoutStream{}, options)
|
|
}
|
|
|
|
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
|
|
}
|