mirror of
https://github.com/cloudflare/cloudflared
synced 2026-06-08 13:33:07 +00:00
Compare commits
15 Commits
2020.11.6
...
2020.11.10
| Author | SHA1 | Date | |
|---|---|---|---|
| 87203bbe25 | |||
| 11acb50cf7 | |||
| 23f2a04ed7 | |||
| 1805261263 | |||
| 53de779a0a | |||
| b7e91466f5 | |||
| 4c1b89576c | |||
| a1a554a29d | |||
| fdb1f961b3 | |||
| 293b9af4a7 | |||
| 029f7e0378 | |||
| 58c5e25b9a | |||
| 25e72f7760 | |||
| 7613410855 | |||
| c40cb7dc56 |
+1
-1
@@ -1,7 +1,7 @@
|
||||
# use a builder image for building cloudflare
|
||||
ARG TARGET_GOOS
|
||||
ARG TARGET_GOARCH
|
||||
FROM golang:1.15.3 as builder
|
||||
FROM golang:1.15.5 as builder
|
||||
ENV GO111MODULE=on \
|
||||
CGO_ENABLED=0 \
|
||||
TARGET_GOOS=${TARGET_GOOS} \
|
||||
|
||||
@@ -89,7 +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)'; \
|
||||
ssh cfsync@$$HOST 'chmod g+w /state/cf-pkg/staging/$(2)/$(TARGET_PUBLIC_REPO)/cloudflared/*.$(1)'; \
|
||||
done
|
||||
endef
|
||||
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
2020.11.10
|
||||
- 2020-11-20 TUN-3562: Fix panic when using bastion mode ingress rule
|
||||
- 2020-11-20 EDGEPLAT-2958 build cloudflared for Bullseye
|
||||
|
||||
2020.11.9
|
||||
- 2020-11-18 TUN-3557: Detect SSE if content-type starts with text/event-stream
|
||||
- 2020-11-18 TUN-3559: Share response meta header with other packages
|
||||
- 2020-11-18 DEVTOOLS-7936: Remove redundant chgrp from publish
|
||||
- 2020-11-18 TUN-3558: cloudflared allows empty config files
|
||||
- 2020-11-18 TUN-3544: Upgrade to Go 1.15.5
|
||||
|
||||
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
|
||||
|
||||
|
||||
+7
-9
@@ -1,6 +1,6 @@
|
||||
pinned_go: &pinned_go go=1.15.3-1
|
||||
pinned_go: &pinned_go go=1.15.5-1
|
||||
build_dir: &build_dir /cfsetup_build
|
||||
default-flavor: stretch
|
||||
default-flavor: buster
|
||||
stretch: &stretch
|
||||
build:
|
||||
build_dir: *build_dir
|
||||
@@ -239,10 +239,8 @@ stretch: &stretch
|
||||
- export GOARCH=amd64
|
||||
- make publish-cloudflared-junos
|
||||
|
||||
jessie: *stretch
|
||||
|
||||
buster: *stretch
|
||||
|
||||
bullseye: *stretch
|
||||
centos-7:
|
||||
publish-rpm:
|
||||
build_dir: *build_dir
|
||||
@@ -250,8 +248,8 @@ centos-7:
|
||||
- https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
|
||||
pre-cache:
|
||||
- yum install -y fakeroot
|
||||
- wget https://golang.org/dl/go1.15.3.linux-amd64.tar.gz -P /tmp/
|
||||
- tar -C /usr/local -xzf /tmp/go1.15.3.linux-amd64.tar.gz
|
||||
- wget https://golang.org/dl/go1.15.5.linux-amd64.tar.gz -P /tmp/
|
||||
- tar -C /usr/local -xzf /tmp/go1.15.5.linux-amd64.tar.gz
|
||||
post-cache:
|
||||
- export PATH=$PATH:/usr/local/go/bin
|
||||
- export GOOS=linux
|
||||
@@ -262,8 +260,8 @@ centos-7:
|
||||
builddeps: *el7_builddeps
|
||||
pre-cache:
|
||||
- yum install -y fakeroot
|
||||
- wget https://golang.org/dl/go1.15.3.linux-amd64.tar.gz -P /tmp/
|
||||
- tar -C /usr/local -xzf /tmp/go1.15.3.linux-amd64.tar.gz
|
||||
- wget https://golang.org/dl/go1.15.5.linux-amd64.tar.gz -P /tmp/
|
||||
- tar -C /usr/local -xzf /tmp/go1.15.5.linux-amd64.tar.gz
|
||||
post-cache:
|
||||
- export PATH=$PATH:/usr/local/go/bin
|
||||
- export GOOS=linux
|
||||
|
||||
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -392,6 +393,10 @@ func ReadConfigFile(c *cli.Context, log logger.Service) (*configFileSettings, er
|
||||
}
|
||||
defer file.Close()
|
||||
if err := yaml.NewDecoder(file).Decode(&configuration); err != nil {
|
||||
if err == io.EOF {
|
||||
log.Errorf("Configuration file %s was empty", configFile)
|
||||
return &configuration, nil
|
||||
}
|
||||
return nil, errors.Wrap(err, "error parsing YAML in config file at "+configFile)
|
||||
}
|
||||
configuration.sourceFile = configFile
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/cloudflare/cloudflared/logger"
|
||||
@@ -27,7 +28,7 @@ type FileManager struct {
|
||||
notifier Notifier
|
||||
configPath string
|
||||
logger logger.Service
|
||||
ReadConfig func(string) (Root, error)
|
||||
ReadConfig func(string, logger.Service) (Root, error)
|
||||
}
|
||||
|
||||
// NewFileManager creates a config manager
|
||||
@@ -59,7 +60,7 @@ func (m *FileManager) Start(notifier Notifier) error {
|
||||
|
||||
// GetConfig reads the yaml file from the disk
|
||||
func (m *FileManager) GetConfig() (Root, error) {
|
||||
return m.ReadConfig(m.configPath)
|
||||
return m.ReadConfig(m.configPath, m.logger)
|
||||
}
|
||||
|
||||
// Shutdown stops the watcher
|
||||
@@ -67,7 +68,7 @@ func (m *FileManager) Shutdown() {
|
||||
m.watcher.Shutdown()
|
||||
}
|
||||
|
||||
func readConfigFromPath(configPath string) (Root, error) {
|
||||
func readConfigFromPath(configPath string, log logger.Service) (Root, error) {
|
||||
if configPath == "" {
|
||||
return Root{}, errors.New("unable to find config file")
|
||||
}
|
||||
@@ -80,6 +81,10 @@ func readConfigFromPath(configPath string) (Root, error) {
|
||||
|
||||
var config Root
|
||||
if err := yaml.NewDecoder(file).Decode(&config); err != nil {
|
||||
if err == io.EOF {
|
||||
log.Errorf("Configuration file %s was empty", configPath)
|
||||
return Root{}, nil
|
||||
}
|
||||
return Root{}, errors.Wrap(err, "error parsing YAML in config file at "+configPath)
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ func TestConfigChanged(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
configRead := func(configPath string) (Root, error) {
|
||||
configRead := func(configPath string, log logger.Service) (Root, error) {
|
||||
return *c, nil
|
||||
}
|
||||
wait := make(chan struct{})
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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...")
|
||||
|
||||
@@ -53,10 +53,13 @@ type ConnectedFuse interface {
|
||||
IsConnected() bool
|
||||
}
|
||||
|
||||
func IsServerSentEvent(headers http.Header) bool {
|
||||
if contentType := headers.Get("content-type"); contentType != "" {
|
||||
return strings.HasPrefix(strings.ToLower(contentType), "text/event-stream")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func uint8ToString(input uint8) string {
|
||||
return strconv.FormatUint(uint64(input), 10)
|
||||
}
|
||||
|
||||
func isServerSentEvent(headers http.Header) bool {
|
||||
return strings.ToLower(headers.Get("content-type")) == "text/event-stream"
|
||||
}
|
||||
|
||||
@@ -5,11 +5,13 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cloudflare/cloudflared/cmd/cloudflared/ui"
|
||||
"github.com/cloudflare/cloudflared/logger"
|
||||
"github.com/gobwas/ws/wsutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -111,3 +113,40 @@ func (mcf mockConnectedFuse) Connected() {}
|
||||
func (mcf mockConnectedFuse) IsConnected() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func TestIsEventStream(t *testing.T) {
|
||||
tests := []struct {
|
||||
headers http.Header
|
||||
isEventStream bool
|
||||
}{
|
||||
{
|
||||
headers: newHeader("Content-Type", "text/event-stream"),
|
||||
isEventStream: true,
|
||||
},
|
||||
{
|
||||
headers: newHeader("content-type", "text/event-stream"),
|
||||
isEventStream: true,
|
||||
},
|
||||
{
|
||||
headers: newHeader("Content-Type", "text/event-stream; charset=utf-8"),
|
||||
isEventStream: true,
|
||||
},
|
||||
{
|
||||
headers: newHeader("Content-Type", "application/json"),
|
||||
isEventStream: false,
|
||||
},
|
||||
{
|
||||
headers: http.Header{},
|
||||
isEventStream: false,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
assert.Equal(t, test.isEventStream, IsServerSentEvent(test.headers))
|
||||
}
|
||||
}
|
||||
|
||||
func newHeader(key, value string) http.Header {
|
||||
header := http.Header{}
|
||||
header.Add(key, value)
|
||||
return header
|
||||
}
|
||||
|
||||
+2
-2
@@ -207,14 +207,14 @@ type h2muxRespWriter struct {
|
||||
|
||||
func (rp *h2muxRespWriter) WriteRespHeaders(resp *http.Response) error {
|
||||
headers := h2mux.H1ResponseToH2ResponseHeaders(resp)
|
||||
headers = append(headers, h2mux.Header{Name: responseMetaHeaderField, Value: responseMetaHeaderOrigin})
|
||||
headers = append(headers, h2mux.Header{Name: ResponseMetaHeaderField, Value: responseMetaHeaderOrigin})
|
||||
return rp.WriteHeaders(headers)
|
||||
}
|
||||
|
||||
func (rp *h2muxRespWriter) WriteErrorResponse() {
|
||||
rp.WriteHeaders([]h2mux.Header{
|
||||
{Name: ":status", Value: "502"},
|
||||
{Name: responseMetaHeaderField, Value: responseMetaHeaderCfd},
|
||||
{Name: ResponseMetaHeaderField, Value: responseMetaHeaderCfd},
|
||||
})
|
||||
rp.Write([]byte("502 Bad Gateway"))
|
||||
}
|
||||
|
||||
@@ -105,9 +105,9 @@ func TestServeStreamHTTP(t *testing.T) {
|
||||
require.True(t, hasHeader(stream, ":status", strconv.Itoa(test.expectedStatus)))
|
||||
|
||||
if test.isProxyError {
|
||||
assert.True(t, hasHeader(stream, responseMetaHeaderField, responseMetaHeaderCfd))
|
||||
assert.True(t, hasHeader(stream, ResponseMetaHeaderField, responseMetaHeaderCfd))
|
||||
} else {
|
||||
assert.True(t, hasHeader(stream, responseMetaHeaderField, responseMetaHeaderOrigin))
|
||||
assert.True(t, hasHeader(stream, ResponseMetaHeaderField, responseMetaHeaderOrigin))
|
||||
body := make([]byte, len(test.expectedBody))
|
||||
_, err = stream.Read(body)
|
||||
require.NoError(t, err)
|
||||
@@ -154,7 +154,7 @@ func TestServeStreamWS(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, hasHeader(stream, ":status", strconv.Itoa(http.StatusSwitchingProtocols)))
|
||||
assert.True(t, hasHeader(stream, responseMetaHeaderField, responseMetaHeaderOrigin))
|
||||
assert.True(t, hasHeader(stream, ResponseMetaHeaderField, responseMetaHeaderOrigin))
|
||||
|
||||
data := []byte("test websocket")
|
||||
err = wsutil.WriteClientText(writePipe, data)
|
||||
@@ -209,7 +209,7 @@ func benchmarkServeStreamHTTPSimple(b *testing.B, test testRequest) {
|
||||
b.StopTimer()
|
||||
|
||||
require.NoError(b, openstreamErr)
|
||||
assert.True(b, hasHeader(stream, responseMetaHeaderField, responseMetaHeaderOrigin))
|
||||
assert.True(b, hasHeader(stream, ResponseMetaHeaderField, responseMetaHeaderOrigin))
|
||||
require.True(b, hasHeader(stream, ":status", strconv.Itoa(http.StatusOK)))
|
||||
require.NoError(b, readBodyErr)
|
||||
require.Equal(b, test.expectedBody, body)
|
||||
|
||||
@@ -8,12 +8,12 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
responseMetaHeaderField = "cf-cloudflared-response-meta"
|
||||
ResponseMetaHeaderField = "cf-cloudflared-response-meta"
|
||||
)
|
||||
|
||||
var (
|
||||
canonicalResponseUserHeadersField = http.CanonicalHeaderKey(h2mux.ResponseUserHeadersField)
|
||||
canonicalResponseMetaHeaderField = http.CanonicalHeaderKey(responseMetaHeaderField)
|
||||
canonicalResponseMetaHeaderField = http.CanonicalHeaderKey(ResponseMetaHeaderField)
|
||||
responseMetaHeaderCfd = mustInitRespMetaHeader("cloudflared")
|
||||
responseMetaHeaderOrigin = mustInitRespMetaHeader("origin")
|
||||
)
|
||||
|
||||
+1
-1
@@ -167,7 +167,7 @@ func (rp *http2RespWriter) WriteRespHeaders(resp *http.Response) error {
|
||||
status = http.StatusOK
|
||||
}
|
||||
rp.w.WriteHeader(status)
|
||||
if isServerSentEvent(resp.Header) {
|
||||
if IsServerSentEvent(resp.Header) {
|
||||
rp.shouldFlush = true
|
||||
}
|
||||
if rp.shouldFlush {
|
||||
|
||||
@@ -100,9 +100,9 @@ func TestServeHTTP(t *testing.T) {
|
||||
require.Equal(t, test.expectedBody, respBody)
|
||||
}
|
||||
if test.isProxyError {
|
||||
require.Equal(t, responseMetaHeaderCfd, resp.Header.Get(responseMetaHeaderField))
|
||||
require.Equal(t, responseMetaHeaderCfd, resp.Header.Get(ResponseMetaHeaderField))
|
||||
} else {
|
||||
require.Equal(t, responseMetaHeaderOrigin, resp.Header.Get(responseMetaHeaderField))
|
||||
require.Equal(t, responseMetaHeaderOrigin, resp.Header.Get(ResponseMetaHeaderField))
|
||||
}
|
||||
}
|
||||
cancel()
|
||||
@@ -202,7 +202,7 @@ func TestServeWS(t *testing.T) {
|
||||
resp := respWriter.Result()
|
||||
// http2RespWriter should rewrite status 101 to 200
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
require.Equal(t, responseMetaHeaderOrigin, resp.Header.Get(responseMetaHeaderField))
|
||||
require.Equal(t, responseMetaHeaderOrigin, resp.Header.Get(ResponseMetaHeaderField))
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.15.3 as builder
|
||||
FROM golang:1.15.5 as builder
|
||||
ENV GO111MODULE=on \
|
||||
CGO_ENABLED=0
|
||||
WORKDIR /go/src/github.com/cloudflare/cloudflared/
|
||||
|
||||
+1
-1
@@ -189,7 +189,7 @@ func websocketHandler(logger logger.Service, upgrader websocket.Upgrader) http.H
|
||||
|
||||
func sseHandler(logger logger.Service) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
|
||||
flusher, ok := w.(http.Flusher)
|
||||
if !ok {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
||||
+11
-3
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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: `
|
||||
|
||||
@@ -84,10 +84,6 @@ func (o *localService) Dial(reqURL *url.URL, headers http.Header) (*gws.Conn, *h
|
||||
return d.Dial(reqURL.String(), headers)
|
||||
}
|
||||
|
||||
func (o *localService) address() string {
|
||||
return o.URL.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, log)
|
||||
if err != nil {
|
||||
@@ -96,8 +92,7 @@ func (o *localService) start(wg *sync.WaitGroup, log logger.Service, shutdownC <
|
||||
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
|
||||
}
|
||||
@@ -152,7 +147,14 @@ func (o *localService) startProxy(staticHost string, wg *sync.WaitGroup, log log
|
||||
}
|
||||
|
||||
func (o *localService) String() string {
|
||||
return o.address()
|
||||
if o.isBastion() {
|
||||
return "Bastion"
|
||||
}
|
||||
return o.URL.String()
|
||||
}
|
||||
|
||||
func (o *localService) isBastion() bool {
|
||||
return o.URL == nil
|
||||
}
|
||||
|
||||
func (o *localService) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
@@ -164,6 +166,10 @@ func (o *localService) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
|
||||
func (o *localService) staticHost() string {
|
||||
|
||||
if o.URL == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
addPortIfMissing := func(uri *url.URL, port int) string {
|
||||
if uri.Port() != "" {
|
||||
return uri.Host
|
||||
|
||||
+1
-12
@@ -96,7 +96,7 @@ func (c *client) proxyHTTP(w connection.ResponseWriter, req *http.Request, rule
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error writing response header")
|
||||
}
|
||||
if isEventStream(resp) {
|
||||
if connection.IsServerSentEvent(resp.Header) {
|
||||
c.logger.Debug("Detected Server-Side Events from Origin")
|
||||
c.writeEventStream(w, resp.Body)
|
||||
} else {
|
||||
@@ -222,14 +222,3 @@ func findCfRayHeader(req *http.Request) string {
|
||||
func isLBProbeRequest(req *http.Request) bool {
|
||||
return strings.HasPrefix(req.UserAgent(), lbProbeUserAgentPrefix)
|
||||
}
|
||||
|
||||
func uint8ToString(input uint8) string {
|
||||
return strconv.FormatUint(uint64(input), 10)
|
||||
}
|
||||
|
||||
func isEventStream(response *http.Response) bool {
|
||||
if response.Header.Get("content-type") == "text/event-stream" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
+55
-41
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user