Compare commits

..

12 Commits

Author SHA1 Message Date
Adam Chalmers 293b9af4a7 Release 2020.11.8 2020-11-17 17:15:50 -06:00
Adam Chalmers 029f7e0378 TUN-3555: Single origin service should default to localhost:8080 2020-11-17 23:12:32 +00:00
Adam Chalmers 58c5e25b9a Release 2020.11.7 2020-11-16 14:13:04 -06:00
Adam Chalmers 25e72f7760 TUN-3549: Use a separate handler for each websocket proxy 2020-11-16 20:05:35 +00:00
Adam Chalmers 7613410855 TUN-3548, TUN-3547: Bastion mode can be specified as a service, doesn't
require URL.
2020-11-16 20:04:36 +00:00
cthuang c40cb7dc56 TUN-3514: Stop setting --is-autoupdated flag after autoupdate because it can break named tunnel running in k8s 2020-11-16 09:40:38 +00:00
Adam Chalmers 9ae5f306bf Release 2020.11.6 2020-11-14 20:13:18 -06:00
Adam Chalmers 6159cb536f TUN-3546: Fix panic in tlsconfig.LoadOriginCA 2020-11-14 20:03:29 -06:00
Adam Chalmers ae4f687754 Release 2020.11.5 2020-11-13 17:39:51 -06:00
Adam Chalmers ce7d0572fe TUN-3543: ProxyAddress not using default in single-origin mode 2020-11-13 17:27:55 -06:00
Troy Varney 030b768eeb DEVTOOLS-7936: Set permissions on public packages
This updates the public repository upload process to change the group on
the uploaded files to `cf` and adds the write permission for members of
the group. This should allow the `cf` user to properly overwrite the
file when signing it.
2020-11-13 19:02:40 +00:00
Adam Chalmers f36dc6cfd8 TUN-3540: Better copy in ingress rules error messages 2020-11-12 17:57:19 -06:00
10 changed files with 156 additions and 58 deletions
+2 -1
View File
@@ -89,6 +89,7 @@ define publish_package
for HOST in $(CF_PKG_HOSTS); do \
ssh-keyscan -t rsa $$HOST >> ~/.ssh/known_hosts; \
scp -4 cloudflared*.$(1) cfsync@$$HOST:/state/cf-pkg/staging/$(2)/$(TARGET_PUBLIC_REPO)/cloudflared/; \
ssh cfsync@$$HOST 'chgrp cf /state/cf-pkg/staging/$(2)/$(TARGET_PUBLIC_REPO)/cloudflared/*.$(1) && chmod g+w /state/cf-pkg/staging/$(2)/$(TARGET_PUBLIC_REPO)/cloudflared/*.$(1)'; \
done
endef
@@ -225,4 +226,4 @@ vet:
.PHONY: msi
msi: cloudflared
go-msi make --msi cloudflared.msi --version $(MSI_VERSION)
go-msi make --msi cloudflared.msi --version $(MSI_VERSION)
+16
View File
@@ -1,3 +1,19 @@
2020.11.8
- 2020-11-17 TUN-3555: Single origin service should default to localhost:8080
2020.11.7
- 2020-11-13 TUN-3514: Stop setting --is-autoupdated flag after autoupdate because it can break named tunnel running in k8s
- 2020-11-15 TUN-3548, TUN-3547: Bastion mode can be specified as a service, doesn't require URL.
- 2020-11-16 TUN-3549: Use a separate handler for each websocket proxy
2020.11.6
- 2020-11-14 TUN-3546: Fix panic in tlsconfig.LoadOriginCA
2020.11.5
- 2020-11-12 TUN-3540: Better copy in ingress rules error messages
- 2020-11-12 DEVTOOLS-7936: Set permissions on public packages
- 2020-11-13 TUN-3543: ProxyAddress not using default in single-origin mode
2020.11.4
- 2020-11-11 TUN-3534: Specific error message when credentials file is a .pem not .json
- 2020-11-02 TUN-3500: Integrate replace h2mux by http2 work with multiple origin support
+1
View File
@@ -369,6 +369,7 @@ func StartServer(
tunnelConfig, ingressRules, err := prepareTunnelConfig(c, buildInfo, version, generalLogger, transportLogger, namedTunnel, isUIEnabled)
if err != nil {
generalLogger.Errorf("Couldn't start tunnel: %v", err)
return err
}
-1
View File
@@ -202,7 +202,6 @@ func (a *AutoUpdater) Run(ctx context.Context) error {
if a.configurable.enabled {
updateOutcome := loggedUpdate(a.logger, updateOptions{})
if updateOutcome.Updated {
os.Args = append(os.Args, "--is-autoupdated=true")
if IsSysV() {
// SysV doesn't have a mechanism to keep service alive, we have to restart the process
a.logger.Info("Restarting service managed by SysV...")
+15 -7
View File
@@ -17,10 +17,10 @@ import (
)
var (
ErrNoIngressRules = errors.New("No ingress rules were specified in the config file")
errLastRuleNotCatchAll = errors.New("The last ingress rule must match all hostnames (i.e. it must be missing, or must be \"*\")")
ErrNoIngressRules = errors.New("The config file doesn't contain any ingress rules")
errLastRuleNotCatchAll = errors.New("The last ingress rule must match all URLs (i.e. it should not have a hostname or path filter)")
errBadWildcard = errors.New("Hostname patterns can have at most one wildcard character (\"*\") and it can only be used for subdomains, e.g. \"*.example.com\"")
errHostnameContainsPort = errors.New("Hostname cannot contain port")
errHostnameContainsPort = errors.New("Hostname cannot contain a port")
ErrURLIncompatibleWithIngress = errors.New("You can't set the --url flag (or $TUNNEL_URL) when using multiple-origin ingress rules")
)
@@ -89,7 +89,7 @@ func parseSingleOriginService(c *cli.Context, allowURLFromArgs bool) (OriginServ
if c.IsSet("hello-world") {
return new(helloWorld), nil
}
if c.IsSet("url") {
if c.IsSet("url") || c.IsSet(config.BastionFlag) {
originURL, err := config.ValidateUrl(c, allowURLFromArgs)
if err != nil {
return nil, errors.Wrap(err, "Error validating origin URL")
@@ -103,7 +103,8 @@ func parseSingleOriginService(c *cli.Context, allowURLFromArgs bool) (OriginServ
}
return &unixSocketPath{path: path}, nil
}
return nil, errors.New("You must either set ingress rules in your config file, or use --url or use --unix-socket")
u, err := url.Parse("http://localhost:8080")
return &localService{URL: u, RootURL: u}, err
}
// IsEmpty checks if there are any ingress rules.
@@ -128,6 +129,7 @@ func (ing Ingress) CatchAll() *Rule {
func validate(ingress []config.UnvalidatedIngressRule, defaults OriginRequestConfig) (Ingress, error) {
rules := make([]Rule, len(ingress))
for i, r := range ingress {
cfg := setConfig(defaults, r.OriginRequest)
var service OriginService
if prefix := "unix:"; strings.HasPrefix(r.Service, prefix) {
@@ -143,6 +145,12 @@ func validate(ingress []config.UnvalidatedIngressRule, defaults OriginRequestCon
service = &srv
} else if r.Service == "hello_world" || r.Service == "hello-world" || r.Service == "helloworld" {
service = new(helloWorld)
} else if r.Service == "bastion" || cfg.BastionMode {
// Bastion mode will always start a Websocket proxy server, which will
// overwrite the localService.URL field when `start` is called. So,
// leave the URL field empty for now.
cfg.BastionMode = true
service = new(localService)
} else {
// Validate URL services
u, err := url.Parse(r.Service)
@@ -151,7 +159,7 @@ func validate(ingress []config.UnvalidatedIngressRule, defaults OriginRequestCon
}
if u.Scheme == "" || u.Hostname() == "" {
return Ingress{}, fmt.Errorf("The service %s must have a scheme and a hostname", r.Service)
return Ingress{}, fmt.Errorf("%s is an invalid address, please make sure it has a scheme and a hostname", r.Service)
}
if u.Path != "" {
@@ -178,7 +186,7 @@ func validate(ingress []config.UnvalidatedIngressRule, defaults OriginRequestCon
Hostname: r.Hostname,
Service: service,
Path: pathRegex,
Config: setConfig(defaults, r.OriginRequest),
Config: cfg,
}
}
return Ingress{Rules: rules, defaults: defaults}, nil
+42
View File
@@ -35,6 +35,7 @@ func Test_parseIngress(t *testing.T) {
fourOhFour := newStatusCode(404)
defaultConfig := setConfig(originRequestFromYAML(config.OriginRequestConfig{}), config.OriginRequestConfig{})
require.Equal(t, defaultKeepAliveConnections, defaultConfig.KeepAliveConnections)
tr := true
type args struct {
rawYAML string
}
@@ -209,6 +210,47 @@ ingress:
},
},
},
{
name: "URL isn't necessary if using bastion",
args: args{rawYAML: `
ingress:
- hostname: bastion.foo.com
originRequest:
bastionMode: true
- service: http_status:404
`},
want: []Rule{
{
Hostname: "bastion.foo.com",
Service: &localService{},
Config: setConfig(originRequestFromYAML(config.OriginRequestConfig{}), config.OriginRequestConfig{BastionMode: &tr}),
},
{
Service: &fourOhFour,
Config: defaultConfig,
},
},
},
{
name: "Bastion service",
args: args{rawYAML: `
ingress:
- hostname: bastion.foo.com
service: bastion
- service: http_status:404
`},
want: []Rule{
{
Hostname: "bastion.foo.com",
Service: &localService{},
Config: setConfig(originRequestFromYAML(config.OriginRequestConfig{}), config.OriginRequestConfig{BastionMode: &tr}),
},
{
Service: &fourOhFour,
Config: defaultConfig,
},
},
},
{
name: "Hostname contains port",
args: args{rawYAML: `
+1 -1
View File
@@ -49,7 +49,7 @@ func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig {
var noTLSVerify bool
var disableChunkedEncoding bool
var bastionMode bool
var proxyAddress string
var proxyAddress = defaultProxyAddress
var proxyPort uint
var proxyType string
if flag := ProxyConnectTimeoutFlag; c.IsSet(flag) {
+18
View File
@@ -1,11 +1,13 @@
package ingress
import (
"flag"
"testing"
"time"
"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"gopkg.in/yaml.v2"
)
@@ -182,3 +184,19 @@ ingress:
}
require.Equal(t, expected1, actual1)
}
func TestDefaultConfigFromCLI(t *testing.T) {
set := flag.NewFlagSet("contrive", 0)
c := cli.NewContext(nil, set, nil)
expected := OriginRequestConfig{
ConnectTimeout: defaultConnectTimeout,
TLSTimeout: defaultTLSTimeout,
TCPKeepAlive: defaultTCPKeepAlive,
KeepAliveConnections: defaultKeepAliveConnections,
KeepAliveTimeout: defaultKeepAliveTimeout,
ProxyAddress: defaultProxyAddress,
}
actual := originRequestFromSingeRule(c)
require.Equal(t, expected, actual)
}
+6 -7
View File
@@ -43,7 +43,7 @@ func (o *unixSocketPath) String() string {
}
func (o *unixSocketPath) start(wg *sync.WaitGroup, log logger.Service, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error {
transport, err := newHTTPTransport(o, cfg)
transport, err := newHTTPTransport(o, cfg, log)
if err != nil {
return err
}
@@ -89,15 +89,14 @@ func (o *localService) address() string {
}
func (o *localService) start(wg *sync.WaitGroup, log logger.Service, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error {
transport, err := newHTTPTransport(o, cfg)
transport, err := newHTTPTransport(o, cfg, log)
if err != nil {
return err
}
o.transport = transport
// Start a proxy if one is needed
staticHost := o.staticHost()
if originRequiresProxy(staticHost, cfg) {
if staticHost := o.staticHost(); originRequiresProxy(staticHost, cfg) {
if err := o.startProxy(staticHost, wg, log, shutdownC, errC, cfg); err != nil {
return err
}
@@ -198,7 +197,7 @@ func (o *helloWorld) String() string {
// Start starts a HelloWorld server and stores its address in the Service receiver.
func (o *helloWorld) start(wg *sync.WaitGroup, log logger.Service, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error {
transport, err := newHTTPTransport(o, cfg)
transport, err := newHTTPTransport(o, cfg, log)
if err != nil {
return err
}
@@ -274,8 +273,8 @@ func (nrc *NopReadCloser) Close() error {
return nil
}
func newHTTPTransport(service OriginService, cfg OriginRequestConfig) (*http.Transport, error) {
originCertPool, err := tlsconfig.LoadOriginCA(cfg.CAPool, nil)
func newHTTPTransport(service OriginService, cfg OriginRequestConfig, log logger.Service) (*http.Transport, error) {
originCertPool, err := tlsconfig.LoadOriginCA(cfg.CAPool, log)
if err != nil {
return nil, errors.Wrap(err, "Error loading cert pool")
}
+55 -41
View File
@@ -147,56 +147,70 @@ func StartProxyServer(logger logger.Service, listener net.Listener, staticHost s
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
h := handler{
upgrader: upgrader,
logger: logger,
staticHost: staticHost,
streamHandler: streamHandler,
}
httpServer := &http.Server{Addr: listener.Addr().String(), Handler: nil}
httpServer := &http.Server{Addr: listener.Addr().String(), Handler: &h}
go func() {
<-shutdownC
httpServer.Close()
}()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// If remote is an empty string, get the destination from the client.
finalDestination := staticHost
if finalDestination == "" {
if jumpDestination := r.Header.Get(h2mux.CFJumpDestinationHeader); jumpDestination == "" {
logger.Error("Did not receive final destination from client. The --destination flag is likely not set")
return
} else {
finalDestination = jumpDestination
}
}
stream, err := net.Dial("tcp", finalDestination)
if err != nil {
logger.Errorf("Cannot connect to remote: %s", err)
return
}
defer stream.Close()
if !websocket.IsWebSocketUpgrade(r) {
w.Write(nonWebSocketRequestPage())
return
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
logger.Errorf("failed to upgrade: %s", err)
return
}
conn.SetReadDeadline(time.Now().Add(pongWait))
conn.SetPongHandler(func(string) error { conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
done := make(chan struct{})
go pinger(logger, conn, done)
defer func() {
done <- struct{}{}
conn.Close()
}()
streamHandler(&Conn{conn}, stream, r.Header)
})
return httpServer.Serve(listener)
}
// HTTP handler for the websocket proxy.
type handler struct {
logger logger.Service
staticHost string
upgrader websocket.Upgrader
streamHandler func(wsConn *Conn, remoteConn net.Conn, requestHeaders http.Header)
}
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// If remote is an empty string, get the destination from the client.
finalDestination := h.staticHost
if finalDestination == "" {
if jumpDestination := r.Header.Get(h2mux.CFJumpDestinationHeader); jumpDestination == "" {
h.logger.Error("Did not receive final destination from client. The --destination flag is likely not set")
return
} else {
finalDestination = jumpDestination
}
}
stream, err := net.Dial("tcp", finalDestination)
if err != nil {
h.logger.Errorf("Cannot connect to remote: %s", err)
return
}
defer stream.Close()
if !websocket.IsWebSocketUpgrade(r) {
w.Write(nonWebSocketRequestPage())
return
}
conn, err := h.upgrader.Upgrade(w, r, nil)
if err != nil {
h.logger.Errorf("failed to upgrade: %s", err)
return
}
conn.SetReadDeadline(time.Now().Add(pongWait))
conn.SetPongHandler(func(string) error { conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
done := make(chan struct{})
go pinger(h.logger, conn, done)
defer func() {
done <- struct{}{}
conn.Close()
}()
h.streamHandler(&Conn{conn}, stream, r.Header)
}
// SendSSHPreamble sends the final SSH destination address to the cloudflared SSH proxy
// The destination is preceded by its length
// Not part of sshserver module to fix compilation for incompatible operating systems