Compare commits

..

9 Commits

Author SHA1 Message Date
Adam Chalmers f116ecfc29 Release 2021.5.0 2021-05-11 11:11:05 -05:00
Michael Borkenstein ea54ed62ba AUTH-3537: AUDs in JWTs are now always arrays 2021-05-10 20:37:24 +00:00
Adam Chalmers 4f362c97f6 TUN-4384: Silence log from automaxprocs 2021-05-10 13:33:47 -05:00
Adam Chalmers f5f9efa459 TUN-4359: Warn about unused keys in 'tunnel ingress validate' 2021-05-06 17:29:18 -05:00
Adam Chalmers 3dc095b4ce TUN-4357: Bump Go to 1.16 2021-05-06 20:53:32 +00:00
Adam Chalmers 76a1997b82 TUN-4356: Set AUTOMAXPROCS to the CPU limit when running in a Linux container 2021-05-06 15:08:50 -05:00
Adam Chalmers c010e10f7c TUN-4343: Fix broken build by setting debug field correctly 2021-05-03 16:46:43 -05:00
Daniel Hwang c605780c95 debug: log host / path
to help understand why the ingress rule logged is being selected.

in addition, combine "Request Headers..." and "Serving with ingress..." logs
into this updated log.

Co-authored-by: Silver <sssilver@users.noreply.github.com>
2021-05-03 15:47:42 +01:00
abelinkinbio 336dd7cd10 TUN-4000: Release notes for cloudflared replica model 2021-04-30 16:21:17 -05:00
26 changed files with 213 additions and 332 deletions
-20
View File
@@ -1,20 +0,0 @@
on: [push, pull_request]
name: Check
jobs:
check:
strategy:
matrix:
go-version: [1.15.x]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Install go-sumtype
run: go get github.com/BurntSushi/go-sumtype
- name: Checkout code
uses: actions/checkout@v2
- name: Test
run: make test
+1 -30
View File
@@ -1,35 +1,6 @@
**Experimental**: This is a new format for release notes. The format and availability is subject to change.
## 2021.6.0
### Bug Fixes
- Fixes a http2 transport (the new default for Named Tunnels) to work with unix socket origins.
## 2021.5.10
### Bug Fixes
- Fixes a memory leak in h2mux transport that connects cloudflared to Cloudflare edge.
## 2021.5.9
### New Features
- Uses new Worker based login helper service to facilitate token exchange in cloudflared flows.
### Bug Fixes
- Fixes Centos-7 builds.
## 2021.5.8
### New Features
- When creating a DNS record to point a hostname at a tunnel, you can now use --overwrite-dns to overwrite any existing
DNS records with that hostname. This works both when using the CLI to provision DNS, as well as when starting an adhoc
named tunnel, e.g.:
- `cloudflared tunnel route dns --overwrite-dns foo-tunnel foo.example.com`
- `cloudflared tunnel --overwrite-dns --name foo-tunnel --hostname foo.example.com`
## 2021.5.7
### New Features
- Named Tunnels will automatically select the protocol to connect to Cloudflare's edge network.
## 2021.5.0
## 2020.5.0
### New Features
- It is now possible to run the same tunnel using more than one `cloudflared` instance. This is a server-side change and
-36
View File
@@ -1,39 +1,3 @@
2021.6.0
- 2021-06-21 TUN-4571: Changelog for 2021.6.0
- 2021-06-18 TUN-4571: Fix proxying to unix sockets when using HTTP2 transport to Cloudflare Edge
- 2021-06-07 TUN-4502: Make `cloudflared tunnel route` subcommands described consistently
- 2021-06-08 TUN-4504: Fix component tests in windows
- 2021-05-27 TUN-4461: Log resulting DNS hostname if one is received from Cloudflare API
2021.5.10
- 2021-05-25 TUN-4456: Replaced instances of Tick() with Ticker() in h2mux paths
2021.5.9
- 2021-05-20 TUN-4426: Fix centos builds
- 2021-05-20 Update changelog
- 2021-04-30 AUTH-3426: Point to new transfer service URL and eliminate PUT /ok
2021.5.8
- 2021-05-14 TUN-4419: Improve error message when cloudflared cannot reach origin
- 2021-05-19 TUN-4425: --overwrite-dns flag for in adhoc and route dns cmds
2021.5.7
- 2021-05-17 Fix typo in Changes.md
- 2021-05-17 TUN-4421: Named Tunnels will automatically select the protocol to connect to Cloudflare's edge network
2021.5.6
- 2021-05-14 TUN-4418: Downgrade to Go 1.16.3
2021.5.5
2021.5.4
- Fix release pipeline
2021.5.1
- 2021-05-10 TUN-4342: Fix false positive warning about unused hostname property
- 2021-05-10 Release 2021.5.0
2021.5.0
- 2021-05-10 TUN-4384: Silence log from automaxprocs
- 2021-05-10 AUTH-3537: AUDs in JWTs are now always arrays
+5 -7
View File
@@ -1,4 +1,4 @@
pinned_go: &pinned_go go=1.16.3-1
pinned_go: &pinned_go go=1.16.4-3
pinned_go_fips: &pinned_go_fips go-fips=1.15.5-3
build_dir: &build_dir /cfsetup_build
@@ -279,9 +279,8 @@ centos-7:
- https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
pre-cache:
- yum install -y fakeroot
- yum upgrade -y binutils-2.27-44.base.el7.x86_64
- wget https://golang.org/dl/go1.16.3.linux-amd64.tar.gz -P /tmp/
- tar -C /usr/local -xzf /tmp/go1.16.3.linux-amd64.tar.gz
- wget https://golang.org/dl/go1.16.4.linux-amd64.tar.gz -P /tmp/
- tar -C /usr/local -xzf /tmp/go1.16.4.linux-amd64.tar.gz
post-cache:
- export PATH=$PATH:/usr/local/go/bin
- export GOOS=linux
@@ -292,9 +291,8 @@ centos-7:
builddeps: *el7_builddeps
pre-cache:
- yum install -y fakeroot
- yum upgrade -y binutils-2.27-44.base.el7.x86_64
- wget https://golang.org/dl/go1.16.3.linux-amd64.tar.gz -P /tmp/
- tar -C /usr/local -xzf /tmp/go1.16.3.linux-amd64.tar.gz
- wget https://golang.org/dl/go1.16.4.linux-amd64.tar.gz -P /tmp/
- tar -C /usr/local -xzf /tmp/go1.16.4.linux-amd64.tar.gz
post-cache:
- export PATH=$PATH:/usr/local/go/bin
- export GOOS=linux
+3 -8
View File
@@ -87,10 +87,6 @@ const (
var (
graceShutdownC chan struct{}
version string
routeFailMsg = fmt.Sprintf("failed to provision routing, please create it manually via Cloudflare dashboard or UI; "+
"most likely you already have a conflicting record there. You can also rerun this command with --%s to overwrite "+
"any existing DNS records for this hostname.", overwriteDNSFlag)
)
func Flags() []cli.Flag {
@@ -185,7 +181,7 @@ func runAdhocNamedTunnel(sc *subcommandContext, name, credentialsOutputPath stri
if r, ok := routeFromFlag(sc.c); ok {
if res, err := sc.route(tunnel.ID, r); err != nil {
sc.log.Err(err).Str("route", r.String()).Msg(routeFailMsg)
sc.log.Err(err).Msg("failed to create route, please create it manually")
} else {
sc.log.Info().Msg(res.SuccessSummary())
}
@@ -203,12 +199,12 @@ func runClassicTunnel(sc *subcommandContext) error {
return StartServer(sc.c, version, nil, sc.log, sc.isUIEnabled)
}
func routeFromFlag(c *cli.Context) (route tunnelstore.Route, ok bool) {
func routeFromFlag(c *cli.Context) (tunnelstore.Route, bool) {
if hostname := c.String("hostname"); hostname != "" {
if lbPool := c.String("lb-pool"); lbPool != "" {
return tunnelstore.NewLBRoute(hostname, lbPool), true
}
return tunnelstore.NewDNSRoute(hostname, c.Bool(overwriteDNSFlagName)), true
return tunnelstore.NewDNSRoute(hostname), true
}
return nil, false
}
@@ -617,7 +613,6 @@ func tunnelFlags(shouldHide bool) []cli.Flag {
Hidden: shouldHide,
}),
selectProtocolFlag,
overwriteDNSFlag,
}...)
return flags
+4
View File
@@ -160,6 +160,10 @@ func prepareTunnelConfig(
if err != nil {
log.Err(err).Str(LogFieldHostname, configHostname).Msg("Invalid hostname")
return nil, ingress.Ingress{}, errors.Wrap(err, "Invalid hostname")
} else if hostname != "" && isNamedTunnel {
log.Warn().Msg("The property `hostname` in your configuration is ignored because you configured a Named Tunnel " +
"in the property `tunnel`. Make sure to provision the routing (e.g. via `cloudflared tunnel route`) or else " +
"your origin will not be reachable. You should remove the `hostname` property to avoid this warning.")
}
isFreeTunnel := hostname == ""
clientID := c.String("id")
+1 -1
View File
@@ -20,7 +20,7 @@ import (
const (
baseLoginURL = "https://dash.cloudflare.com/argotunnel"
callbackStoreURL = "https://login.cloudflareaccess.org/"
callbackStoreURL = "https://login.argotunnel.com/"
)
func buildLoginSubcommand(hidden bool) *cli.Command {
-2
View File
@@ -1,5 +1,3 @@
// +build !windows
package tunnel
import (
+37 -62
View File
@@ -29,11 +29,10 @@ import (
)
const (
allSortByOptions = "name, id, createdAt, deletedAt, numConnections"
connsSortByOptions = "id, startedAt, numConnections, version"
CredFileFlagAlias = "cred-file"
CredFileFlag = "credentials-file"
overwriteDNSFlagName = "overwrite-dns"
allSortByOptions = "name, id, createdAt, deletedAt, numConnections"
connsSortByOptions = "id, startedAt, numConnections, version"
CredFileFlagAlias = "cred-file"
CredFileFlag = "credentials-file"
LogFieldTunnelID = "tunnelID"
)
@@ -111,7 +110,7 @@ var (
}
selectProtocolFlag = altsrc.NewStringFlag(&cli.StringFlag{
Name: "protocol",
Value: "auto",
Value: "h2mux",
Aliases: []string{"p"},
Usage: fmt.Sprintf("Protocol implementation to connect with Cloudflare's edge network. %s", connection.AvailableProtocolFlagMessage),
EnvVars: []string{"TUNNEL_TRANSPORT_PROTOCOL"},
@@ -134,12 +133,6 @@ var (
Usage: `Constraints the cleanup to stop the connections of a single Connector (by its ID). You can find the various Connectors (and their IDs) currently connected to your tunnel via 'cloudflared tunnel info <name>'.`,
EnvVars: []string{"TUNNEL_CLEANUP_CONNECTOR"},
}
overwriteDNSFlag = &cli.BoolFlag{
Name: overwriteDNSFlagName,
Aliases: []string{"f"},
Usage: `Overwrites existing DNS records with this hostname`,
EnvVars: []string{"TUNNEL_FORCE_PROVISIONING_DNS"},
}
)
func buildCreateCommand() *cli.Command {
@@ -602,12 +595,6 @@ func runCommand(c *cli.Context) error {
}
}
if c.String("hostname") != "" {
sc.log.Warn().Msg("The property `hostname` in your configuration is ignored because you configured a Named Tunnel " +
"in the property `tunnel` to run. Make sure to provision the routing (e.g. via `cloudflared tunnel route dns/lb`) or else " +
"your origin will not be reachable. You should remove the `hostname` property to avoid this warning.")
}
return runNamedTunnel(sc, tunnelRef)
}
@@ -655,6 +642,7 @@ func cleanupCommand(c *cli.Context) error {
func buildRouteCommand() *cli.Command {
return &cli.Command{
Name: "route",
Action: cliutil.ConfiguredAction(routeCommand),
Usage: "Define which traffic routed from Cloudflare edge to this tunnel: requests to a DNS hostname, to a Cloudflare Load Balancer, or traffic originating from Cloudflare WARP clients",
UsageText: "cloudflared tunnel [tunnel command options] route [subcommand options] [dns TUNNEL HOSTNAME]|[lb TUNNEL HOSTNAME LB-POOL]|[ip NETWORK TUNNEL]",
Description: `The route command defines how Cloudflare will proxy requests to this tunnel.
@@ -674,30 +662,15 @@ Further information about managing Cloudflare WARP traffic to your tunnel is ava
`,
CustomHelpTemplate: commandHelpTemplate(),
Subcommands: []*cli.Command{
{
Name: "dns",
Action: cliutil.ConfiguredAction(routeDnsCommand),
Usage: "Route a hostname by creating a DNS CNAME record to a tunnel",
UsageText: "cloudflared tunnel route dns [TUNNEL] [HOSTNAME]",
Description: `Creates a DNS CNAME record hostname that points to the tunnel.`,
Flags: []cli.Flag{overwriteDNSFlag},
},
{
Name: "lb",
Action: cliutil.ConfiguredAction(routeLbCommand),
Usage: "Use this tunnel as a load balancer origin, creating pool and load balancer if necessary",
UsageText: "cloudflared tunnel route dns [TUNNEL] [HOSTNAME] [LB-POOL]",
Description: `Creates Load Balancer with an origin pool that points to the tunnel.`,
},
buildRouteIPSubcommand(),
},
}
}
func dnsRouteFromArg(c *cli.Context, overwriteExisting bool) (tunnelstore.Route, error) {
func dnsRouteFromArg(c *cli.Context) (tunnelstore.Route, error) {
const (
userHostnameIndex = 1
expectedNArgs = 2
userHostnameIndex = 2
expectedNArgs = 3
)
if c.NArg() != expectedNArgs {
return nil, cliutil.UsageError("Expected %d arguments, got %d", expectedNArgs, c.NArg())
@@ -708,14 +681,14 @@ func dnsRouteFromArg(c *cli.Context, overwriteExisting bool) (tunnelstore.Route,
} else if !validateHostname(userHostname, true) {
return nil, errors.Errorf("%s is not a valid hostname", userHostname)
}
return tunnelstore.NewDNSRoute(userHostname, overwriteExisting), nil
return tunnelstore.NewDNSRoute(userHostname), nil
}
func lbRouteFromArg(c *cli.Context) (tunnelstore.Route, error) {
const (
lbNameIndex = 1
lbPoolIndex = 2
expectedNArgs = 3
lbNameIndex = 2
lbPoolIndex = 3
expectedNArgs = 4
)
if c.NArg() != expectedNArgs {
return nil, cliutil.UsageError("Expected %d arguments, got %d", expectedNArgs, c.NArg())
@@ -757,39 +730,41 @@ func validateHostname(s string, allowWildcardSubdomain bool) bool {
return err == nil && validateName(puny, allowWildcardSubdomain)
}
func routeDnsCommand(c *cli.Context) error {
if c.NArg() != 2 {
return cliutil.UsageError(`This command expects the format "cloudflared tunnel route dns <tunnel name/id> <hostname>"`)
func routeCommand(c *cli.Context) error {
if c.NArg() < 2 {
return cliutil.UsageError(`"cloudflared tunnel route" requires the first argument to be the route type(dns or lb), followed by the ID or name of the tunnel`)
}
return routeCommand(c, "dns")
}
func routeLbCommand(c *cli.Context) error {
if c.NArg() != 3 {
return cliutil.UsageError(`This command expects the format "cloudflared tunnel route lb <tunnel name/id> <hostname> <load balancer pool>"`)
}
return routeCommand(c, "lb")
}
func routeCommand(c *cli.Context, routeType string) error {
sc, err := newSubcommandContext(c)
if err != nil {
return err
}
tunnelID, err := sc.findID(c.Args().Get(0))
if err != nil {
return err
}
const tunnelIDIndex = 1
routeType := c.Args().First()
var route tunnelstore.Route
var tunnelID uuid.UUID
switch routeType {
case "dns":
route, err = dnsRouteFromArg(c, c.Bool(overwriteDNSFlagName))
tunnelID, err = sc.findID(c.Args().Get(tunnelIDIndex))
if err != nil {
return err
}
route, err = dnsRouteFromArg(c)
if err != nil {
return err
}
case "lb":
tunnelID, err = sc.findID(c.Args().Get(tunnelIDIndex))
if err != nil {
return err
}
route, err = lbRouteFromArg(c)
}
if err != nil {
return err
if err != nil {
return err
}
default:
return cliutil.UsageError("%s is not a recognized route type. Supported route types are dns and lb", routeType)
}
res, err := sc.route(tunnelID, route)
+2 -1
View File
@@ -1,6 +1,7 @@
package tunnel
import (
"fmt"
"path/filepath"
"testing"
@@ -94,7 +95,7 @@ func TestTunnelfilePath(t *testing.T) {
assert.NoError(t, err)
homeDir, err := homedir.Dir()
assert.NoError(t, err)
expected := filepath.Join(homeDir, ".cloudflared", tunnelID.String()+".json")
expected := fmt.Sprintf("%s/.cloudflared/%v.json", homeDir, tunnelID)
assert.Equal(t, expected, actual)
}
@@ -1,5 +1,3 @@
// +build !windows
package updater
import (
@@ -15,7 +13,6 @@ import (
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
@@ -24,8 +21,6 @@ import (
"github.com/stretchr/testify/require"
)
var testFilePath = filepath.Join(os.TempDir(), "test")
func respondWithJSON(w http.ResponseWriter, v interface{}, status int) {
data, _ := json.Marshal(v)
@@ -213,6 +208,7 @@ func TestUpdateService(t *testing.T) {
ts := createServer()
defer ts.Close()
testFilePath := "tmpfile"
createTestFile(t, testFilePath)
defer os.Remove(testFilePath)
log.Println("server url: ", ts.URL)
@@ -233,6 +229,7 @@ func TestBetaUpdateService(t *testing.T) {
ts := createServer()
defer ts.Close()
testFilePath := "tmpfile"
createTestFile(t, testFilePath)
defer os.Remove(testFilePath)
@@ -252,6 +249,7 @@ func TestFailUpdateService(t *testing.T) {
ts := createServer()
defer ts.Close()
testFilePath := "tmpfile"
createTestFile(t, testFilePath)
defer os.Remove(testFilePath)
@@ -265,6 +263,7 @@ func TestNoUpdateService(t *testing.T) {
ts := createServer()
defer ts.Close()
testFilePath := "tmpfile"
createTestFile(t, testFilePath)
defer os.Remove(testFilePath)
@@ -279,6 +278,7 @@ func TestForcedUpdateService(t *testing.T) {
ts := createServer()
defer ts.Close()
testFilePath := "tmpfile"
createTestFile(t, testFilePath)
defer os.Remove(testFilePath)
@@ -298,6 +298,7 @@ func TestUpdateSpecificVersionService(t *testing.T) {
ts := createServer()
defer ts.Close()
testFilePath := "tmpfile"
createTestFile(t, testFilePath)
defer os.Remove(testFilePath)
reqVersion := "2020.9.1"
@@ -318,6 +319,7 @@ func TestCompressedUpdateService(t *testing.T) {
ts := createServer()
defer ts.Close()
testFilePath := "tmpfile"
createTestFile(t, testFilePath)
defer os.Remove(testFilePath)
@@ -337,6 +339,7 @@ func TestUpdateWhenRunningKnownBuggyVersion(t *testing.T) {
ts := createServer()
defer ts.Close()
testFilePath := "tmpfile"
createTestFile(t, testFilePath)
defer os.Remove(testFilePath)
+1 -1
View File
@@ -7,7 +7,7 @@ from util import start_cloudflared, wait_tunnel_ready, send_requests
# Rolling logger rotate log files after 1 MB
rotate_after_size = 1000 * 1000
default_log_file = "cloudflared.log"
expect_message = "Starting Hello"
expect_message = "Starting Hello World server"
def assert_log_to_terminal(cloudflared):
+8 -9
View File
@@ -1,23 +1,20 @@
#!/usr/bin/env python
import copy
import platform
from time import sleep
import pytest
from flaky import flaky
from util import start_cloudflared, wait_tunnel_ready, check_tunnel_not_connected
@flaky(max_runs=3, min_passes=1)
class TestReconnect:
class TestReconnect():
default_ha_conns = 4
default_reconnect_secs = 15
default_reconnect_secs = 5
extra_config = {
"stdin-control": True,
}
@pytest.mark.skipif(platform.system() == "Windows", reason=f"Currently buggy on Windows TUN-4584")
def test_named_reconnect(self, tmp_path, component_tests_config):
config = component_tests_config(self.extra_config)
with start_cloudflared(tmp_path, config, new_process=True, allow_input=True, capture_output=False) as cloudflared:
@@ -39,7 +36,7 @@ class TestReconnect:
cloudflared.stdin.flush()
def assert_reconnect(self, config, cloudflared, repeat):
wait_tunnel_ready(tunnel_url=config.get_url(), require_min_connections=self.default_ha_conns)
wait_tunnel_ready(tunnel_url=config.get_url())
for _ in range(repeat):
for i in range(self.default_ha_conns):
self.send_reconnect(cloudflared, self.default_reconnect_secs)
@@ -47,9 +44,11 @@ class TestReconnect:
if expect_connections > 0:
# Don't check if tunnel returns 200 here because there is a race condition between wait_tunnel_ready
# retrying to get 200 response and reconnecting
wait_tunnel_ready(require_min_connections=expect_connections)
wait_tunnel_ready(
require_min_connections=expect_connections)
else:
check_tunnel_not_connected()
sleep(self.default_reconnect_secs * 2)
wait_tunnel_ready(tunnel_url=config.get_url(), require_min_connections=self.default_ha_conns)
sleep(self.default_reconnect_secs + 10)
wait_tunnel_ready(tunnel_url=config.get_url(),
require_min_connections=self.default_ha_conns)
+3 -2
View File
@@ -34,7 +34,7 @@ def cloudflared_cmd(config, config_path, cfd_args, cfd_pre_args, root):
cmd += ["sudo"]
cmd += [config.cloudflared_binary]
cmd += cfd_pre_args
cmd += ["--config", str(config_path)]
cmd += ["--config", config_path]
cmd += cfd_args
LOGGER.info(f"Run cmd {cmd} with config {config}")
return cmd
@@ -50,7 +50,8 @@ def run_cloudflared_background(cmd, allow_input, capture_output):
finally:
cfd.terminate()
if capture_output:
LOGGER.info(f"cloudflared log: {cfd.stderr.read()}")
LOGGER.info(
f"cloudflared log: {cfd.stderr.read()}")
@retry(stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000)
+2 -3
View File
@@ -167,8 +167,7 @@ func (h *h2muxConnection) serveMuxer(ctx context.Context) error {
}
func (h *h2muxConnection) controlLoop(ctx context.Context, connectedFuse ConnectedFuse, isNamedTunnel bool) {
updateMetricsTicker := time.NewTicker(h.muxerConfig.MetricsUpdateFreq)
defer updateMetricsTicker.Stop()
updateMetricsTickC := time.Tick(h.muxerConfig.MetricsUpdateFreq)
var shutdownCompleted <-chan struct{}
for {
select {
@@ -192,7 +191,7 @@ func (h *h2muxConnection) controlLoop(ctx context.Context, connectedFuse Connect
// don't wait for shutdown to finish when context is closed, this is the hard termination path
return
case <-updateMetricsTicker.C:
case <-updateMetricsTickC:
h.observer.metrics.updateMuxerMetrics(h.connIndexStr, h.muxer.Metrics())
}
}
-16
View File
@@ -98,8 +98,6 @@ func (c *http2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer c.activeRequestsWG.Done()
connType := determineHTTP2Type(r)
handleMissingRequestParts(connType, r)
respWriter, err := newHTTP2RespWriter(r, w, connType)
if err != nil {
c.observer.log.Error().Msg(err.Error())
@@ -257,20 +255,6 @@ func determineHTTP2Type(r *http.Request) Type {
}
}
func handleMissingRequestParts(connType Type, r *http.Request) {
if connType == TypeHTTP {
// http library has no guarantees that we receive a filled URL. If not, then we fill it, as we reuse the request
// for proxying. We use the same values as we used to in h2mux. For proxying they should not matter since we
// control the dialer on every egress proxied.
if len(r.URL.Scheme) == 0 {
r.URL.Scheme = "http"
}
if len(r.URL.Host) == 0 {
r.URL.Host = "localhost:8080"
}
}
}
func isControlStreamUpgrade(r *http.Request) bool {
return r.Header.Get(InternalUpgradeHeader) == ControlStreamUpgrade
}
+2 -3
View File
@@ -69,13 +69,12 @@ func (r *MuxReader) run(log *zerolog.Logger) error {
// routine to periodically update bytesRead
go func() {
ticker := time.NewTicker(updateFreq)
defer ticker.Stop()
tickC := time.Tick(updateFreq)
for {
select {
case <-r.abortChan:
return
case <-ticker.C:
case <-tickC:
r.metricsUpdater.updateInBoundBytes(r.bytesRead.Count())
}
}
+2 -3
View File
@@ -78,13 +78,12 @@ func (w *MuxWriter) run(log *zerolog.Logger) error {
// routine to periodically communicate bytesWrote
go func() {
ticker := time.NewTicker(updateFreq)
defer ticker.Stop()
tickC := time.Tick(updateFreq)
for {
select {
case <-w.abortChan:
return
case <-ticker.C:
case <-tickC:
w.metricsUpdater.updateOutBoundBytes(w.bytesWrote.Count())
}
}
+48
View File
@@ -0,0 +1,48 @@
package metrics
import (
"time"
"github.com/prometheus/client_golang/prometheus"
)
// Timer assumes the metrics is partitioned by one label
type Timer struct {
startTime map[string]time.Time
metrics *prometheus.HistogramVec
measureUnit time.Duration
labelKey string
}
func NewTimer(metrics *prometheus.HistogramVec, unit time.Duration, labelKey string) *Timer {
return &Timer{
startTime: make(map[string]time.Time),
measureUnit: unit,
metrics: metrics,
labelKey: labelKey,
}
}
func (i *Timer) Start(labelVal string) {
i.startTime[labelVal] = time.Now()
}
func (i *Timer) End(labelVal string) time.Duration {
if start, ok := i.startTime[labelVal]; ok {
return Latency(start, time.Now())
}
return 0
}
func (i *Timer) Observe(measurement time.Duration, labelVal string) {
metricsLabels := prometheus.Labels{i.labelKey: labelVal}
i.metrics.With(metricsLabels).Observe(float64(measurement / i.measureUnit))
}
func (i *Timer) EndAndObserve(labelVal string) {
i.Observe(i.End(labelVal), labelVal)
}
func Latency(startTime, endTime time.Time) time.Duration {
return endTime.Sub(startTime)
}
+24
View File
@@ -0,0 +1,24 @@
package metrics
import (
"testing"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
)
func TestEnd(t *testing.T) {
m := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: "TestCallLatencyWithoutMeasurement",
Name: "Latency",
Buckets: prometheus.LinearBuckets(0, 50, 100),
},
[]string{"key"},
)
timer := NewTimer(m, time.Millisecond, "key")
assert.Equal(t, time.Duration(0), timer.End("dne"))
timer.Start("test")
assert.NotEqual(t, time.Duration(0), timer.End("test"))
}
+11 -18
View File
@@ -18,10 +18,9 @@ import (
)
const (
TagHeaderNamePrefix = "Cf-Warp-Tag-"
LogFieldCFRay = "cfRay"
LogFieldRule = "ingressRule"
LogFieldOriginService = "originService"
TagHeaderNamePrefix = "Cf-Warp-Tag-"
LogFieldCFRay = "cfRay"
LogFieldRule = "ingressRule"
)
type proxy struct {
@@ -71,7 +70,7 @@ func (p *proxy) Proxy(w connection.ResponseWriter, req *http.Request, sourceConn
rule: ingress.ServiceWarpRouting,
}
if err := p.proxyStreamRequest(serveCtx, w, req, p.warpRouting.Proxy, logFields); err != nil {
p.logRequestError(err, cfRay, "", ingress.ServiceWarpRouting)
p.logRequestError(err, cfRay, ingress.ServiceWarpRouting)
return err
}
return nil
@@ -87,8 +86,7 @@ func (p *proxy) Proxy(w connection.ResponseWriter, req *http.Request, sourceConn
if sourceConnectionType == connection.TypeHTTP {
if err := p.proxyHTTPRequest(w, req, rule, logFields); err != nil {
rule, srv := ruleField(p.ingressRules, ruleNum)
p.logRequestError(err, cfRay, rule, srv)
p.logRequestError(err, cfRay, ruleField(p.ingressRules, ruleNum))
return err
}
return nil
@@ -101,19 +99,17 @@ func (p *proxy) Proxy(w connection.ResponseWriter, req *http.Request, sourceConn
}
if err := p.proxyStreamRequest(serveCtx, w, req, connectionProxy, logFields); err != nil {
rule, srv := ruleField(p.ingressRules, ruleNum)
p.logRequestError(err, cfRay, rule, srv)
p.logRequestError(err, cfRay, ruleField(p.ingressRules, ruleNum))
return err
}
return nil
}
func ruleField(ing ingress.Ingress, ruleNum int) (ruleID string, srv string) {
srv = ing.Rules[ruleNum].Service.String()
func ruleField(ing ingress.Ingress, ruleNum int) string {
if ing.IsSingleRule() {
return "", srv
return ""
}
return fmt.Sprintf("%d", ruleNum), srv
return fmt.Sprintf("%d", ruleNum)
}
func (p *proxy) proxyHTTPRequest(w connection.ResponseWriter, req *http.Request, rule *ingress.Rule, fields logFields) error {
@@ -137,7 +133,7 @@ func (p *proxy) proxyHTTPRequest(w connection.ResponseWriter, req *http.Request,
resp, err := httpService.RoundTrip(req)
if err != nil {
return errors.Wrap(err, "Unable to reach the origin service. The service may be down or it may not be responding to traffic from cloudflared")
return errors.Wrap(err, "Error proxying request to origin")
}
defer resp.Body.Close()
@@ -275,7 +271,7 @@ func (p *proxy) logOriginResponse(resp *http.Response, fields logFields) {
}
}
func (p *proxy) logRequestError(err error, cfRay string, rule, service string) {
func (p *proxy) logRequestError(err error, cfRay string, rule string) {
requestErrors.Inc()
log := p.log.Error().Err(err)
if cfRay != "" {
@@ -284,9 +280,6 @@ func (p *proxy) logRequestError(err error, cfRay string, rule, service string) {
if rule != "" {
log = log.Str(LogFieldRule, rule)
}
if service != "" {
log = log.Str(LogFieldOriginService, service)
}
log.Msg("")
}
+20 -67
View File
@@ -6,11 +6,9 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/http/httptest"
"os"
"sync"
"testing"
"time"
@@ -275,7 +273,26 @@ func TestProxyMultipleOrigins(t *testing.T) {
},
}
tests := []MultipleIngressTest{
ingress, err := ingress.ParseIngress(&config.Configuration{
TunnelID: t.Name(),
Ingress: unvalidatedIngress,
})
require.NoError(t, err)
log := zerolog.Nop()
ctx, cancel := context.WithCancel(context.Background())
errC := make(chan error)
var wg sync.WaitGroup
require.NoError(t, ingress.StartOrigins(&wg, &log, ctx.Done(), errC))
proxy := NewOriginProxy(ingress, unusedWarpRoutingService, testTags, &log)
tests := []struct {
url string
expectedStatus int
expectedBody []byte
}{
{
url: "http://api.example.com",
expectedStatus: http.StatusCreated,
@@ -300,31 +317,6 @@ func TestProxyMultipleOrigins(t *testing.T) {
},
}
runIngressTestScenarios(t, unvalidatedIngress, tests)
}
type MultipleIngressTest struct {
url string
expectedStatus int
expectedBody []byte
}
func runIngressTestScenarios(t *testing.T, unvalidatedIngress []config.UnvalidatedIngressRule, tests []MultipleIngressTest) {
ingress, err := ingress.ParseIngress(&config.Configuration{
TunnelID: t.Name(),
Ingress: unvalidatedIngress,
})
require.NoError(t, err)
log := zerolog.Nop()
ctx, cancel := context.WithCancel(context.Background())
errC := make(chan error)
var wg sync.WaitGroup
require.NoError(t, ingress.StartOrigins(&wg, &log, ctx.Done(), errC))
proxy := NewOriginProxy(ingress, unusedWarpRoutingService, testTags, &log)
for _, test := range tests {
responseWriter := newMockHTTPRespWriter()
req, err := http.NewRequest(http.MethodGet, test.url, nil)
@@ -641,45 +633,6 @@ func TestConnections(t *testing.T) {
}
}
func TestUnixSocketOrigin(t *testing.T) {
file, err := ioutil.TempFile("", "unix.sock")
require.NoError(t, err)
os.Remove(file.Name()) // remove the file since binding the socket expects to create it
l, err := net.Listen("unix", file.Name())
require.NoError(t, err)
defer l.Close()
defer os.Remove(file.Name())
api := &httptest.Server{
Listener: l,
Config: &http.Server{Handler: mockAPI{}},
}
api.Start()
defer api.Close()
unvalidatedIngress := []config.UnvalidatedIngressRule{
{
Hostname: "unix.example.com",
Service: "unix:" + file.Name(),
},
{
Hostname: "*",
Service: "http_status:404",
},
}
tests := []MultipleIngressTest{
{
url: "http://unix.example.com",
expectedStatus: http.StatusCreated,
expectedBody: []byte("Created"),
},
}
runIngressTestScenarios(t, unvalidatedIngress, tests)
}
type requestBody struct {
pw *io.PipeWriter
pr *io.PipeReader
-2
View File
@@ -1,5 +1,3 @@
// +build !windows
package sshgen
import (
+23 -3
View File
@@ -15,7 +15,7 @@ import (
)
const (
baseStoreURL = "https://login.cloudflareaccess.org/"
baseStoreURL = "https://login.argotunnel.com/"
clientTimeout = time.Second * 60
)
@@ -83,13 +83,11 @@ func buildRequestURL(baseURL *url.URL, key, value string, cli, useHostOnly bool)
if useHostOnly {
baseURL.Path = ""
}
// TODO: pass arg for tunnel login
if !cli {
return baseURL.String(), nil
}
q.Set("redirect_url", baseURL.String()) // we add the token as a query param on both the redirect_url and the main url
q.Set("send_org_token", "true") // indicates that the cli endpoint should return both the org and app token
q.Set("edge_token_transfer", "true") // use new LoginHelper service built on workers
baseURL.RawQuery = q.Encode() // and this actual baseURL.
baseURL.Path = "cdn-cgi/access/cli"
return baseURL.String(), nil
@@ -105,6 +103,9 @@ func transferRequest(requestURL string, log *zerolog.Logger) ([]byte, string, er
if err != nil {
return nil, "", err
} else if len(buf) > 0 {
if err := putSuccess(client, requestURL); err != nil {
log.Err(err).Msg("Failed to update resource success")
}
return buf, key, nil
}
}
@@ -135,3 +136,22 @@ func poll(client *http.Client, requestURL string, log *zerolog.Logger) ([]byte,
}
return buf.Bytes(), resp.Header.Get("service-public-key"), nil
}
// putSuccess tells the server we successfully downloaded the resource
func putSuccess(client *http.Client, requestURL string) error {
req, err := http.NewRequest("PUT", requestURL+"/ok", nil)
if err != nil {
return err
}
resp, err := client.Do(req)
if err != nil {
return err
}
resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("HTTP Response Status Code: %d", resp.StatusCode)
}
return nil
}
+8 -31
View File
@@ -72,7 +72,6 @@ type Route interface {
json.Marshaler
RecordType() string
UnmarshalResult(body io.Reader) (RouteResult, error)
String() string
}
type RouteResult interface {
@@ -81,32 +80,27 @@ type RouteResult interface {
}
type DNSRoute struct {
userHostname string
overwriteExisting bool
userHostname string
}
type DNSRouteResult struct {
route *DNSRoute
CName Change `json:"cname"`
Name string `json:"name"`
}
func NewDNSRoute(userHostname string, overwriteExisting bool) Route {
func NewDNSRoute(userHostname string) Route {
return &DNSRoute{
userHostname: userHostname,
overwriteExisting: overwriteExisting,
userHostname: userHostname,
}
}
func (dr *DNSRoute) MarshalJSON() ([]byte, error) {
s := struct {
Type string `json:"type"`
UserHostname string `json:"user_hostname"`
OverwriteExisting bool `json:"overwrite_existing"`
Type string `json:"type"`
UserHostname string `json:"user_hostname"`
}{
Type: dr.RecordType(),
UserHostname: dr.userHostname,
OverwriteExisting: dr.overwriteExisting,
Type: dr.RecordType(),
UserHostname: dr.userHostname,
}
return json.Marshal(&s)
}
@@ -122,10 +116,6 @@ func (dr *DNSRoute) RecordType() string {
return "dns"
}
func (dr *DNSRoute) String() string {
return fmt.Sprintf("%s %s", dr.RecordType(), dr.userHostname)
}
func (res *DNSRouteResult) SuccessSummary() string {
var msgFmt string
switch res.CName {
@@ -136,16 +126,7 @@ func (res *DNSRouteResult) SuccessSummary() string {
case ChangeUnchanged:
msgFmt = "%s is already configured to route to your tunnel"
}
return fmt.Sprintf(msgFmt, res.hostname())
}
// hostname yields the resulting name for the DNS route; if that is not available from Cloudflare API, then the
// requested name is returned instead (should not be the common path, it is just a fall-back).
func (res *DNSRouteResult) hostname() string {
if res.Name != "" {
return res.Name
}
return res.route.userHostname
return fmt.Sprintf(msgFmt, res.route.userHostname)
}
type LBRoute struct {
@@ -183,10 +164,6 @@ func (lr *LBRoute) RecordType() string {
return "lb"
}
func (lb *LBRoute) String() string {
return fmt.Sprintf("%s %s %s", lb.RecordType(), lb.lbName, lb.lbPool)
}
func (lr *LBRoute) UnmarshalResult(body io.Reader) (RouteResult, error) {
var result LBRouteResult
err := parseResponse(body, &result)
-2
View File
@@ -1,5 +1,3 @@
// +build !windows
package watcher
import (