mirror of
https://github.com/cloudflare/cloudflared.git
synced 2026-08-07 23:31:56 +00:00
Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9584adc38a | |||
| 0096f2613c | |||
| ac82c8b08b | |||
| af3a66d60e | |||
| 42e0540395 | |||
| 2ee90483bf | |||
| 2084a123c2 | |||
| b500e556bf | |||
| 1b0b6bf7a8 | |||
| 85eee4849f | |||
| 9b8a533435 | |||
| 5abb90b539 | |||
| 0c8bc56930 | |||
| fdab68aa08 | |||
| 5aaab967a3 | |||
| ccad59dfab | |||
| 8a3eade6d3 | |||
| 39847a70f2 | |||
| d1e338ee48 | |||
| b243602d1c | |||
| 960c5a7baf | |||
| aca3575b6d | |||
| 2b4815a9f5 | |||
| 729890d847 | |||
| 31f424d589 | |||
| cb4bd8d065 | |||
| 1abd22ef0a | |||
| a3bcf25fae | |||
| 20e36c5bf3 | |||
| 5693ba524b | |||
| 9c6fbfca18 | |||
| 925ec100d6 | |||
| 58b27a1ccf | |||
| 867360c8dd | |||
| cb97257815 | |||
| c43e07d6b7 | |||
| 9426b60308 | |||
| ff9621bbd5 | |||
| 7a0a618c0d | |||
| 0be1ed5284 | |||
| 50a0c44cee | |||
| 76391434c2 | |||
| e8841c0fb3 | |||
| aec1d8f653 | |||
| c7f343a3b4 | |||
| 7ecb6d3e88 | |||
| 88c25d2c67 | |||
| 38cd455e4d | |||
| ee5e447d44 | |||
| 4d30a71434 | |||
| 39b7aed24e | |||
| 4de1bc4bba | |||
| e426693330 | |||
| 0b5b9b8297 | |||
| 7a014b06ec | |||
| 171d4ac77c | |||
| 5e212a6bf3 |
@@ -1,3 +1,7 @@
|
||||
## 2023.7.0
|
||||
### New Features
|
||||
- You can now enable additional diagnostics over the management.argotunnel.com service for your active cloudflared connectors via a new runtime flag `--management-diagnostics` (or env `TUNNEL_MANAGEMENT_DIAGNOSTICS`). This feature is provided as opt-in and requires the flag to enable. Endpoints such as /metrics provides your prometheus metrics endpoint another mechanism to be reached. Additionally /debug/pprof/(goroutine|heap) are also introduced to allow for remotely retrieving active pprof information from a running cloudflared connector.
|
||||
|
||||
## 2023.4.1
|
||||
### New Features
|
||||
- You can now stream your logs from your remote cloudflared to your local terminal with `cloudflared tail <TUNNEL-ID>`. This new feature requires the remote cloudflared to be version 2023.4.1 or higher.
|
||||
|
||||
@@ -140,6 +140,7 @@ container:
|
||||
generate-docker-version:
|
||||
echo latest $(VERSION) > versions
|
||||
|
||||
|
||||
.PHONY: test
|
||||
test: vet
|
||||
ifndef CI
|
||||
@@ -147,9 +148,18 @@ ifndef CI
|
||||
else
|
||||
@mkdir -p .cover
|
||||
go test -v -mod=vendor -race $(LDFLAGS) -coverprofile=".cover/c.out" ./...
|
||||
go tool cover -html ".cover/c.out" -o .cover/all.html
|
||||
endif
|
||||
|
||||
.PHONY: cover
|
||||
cover:
|
||||
@echo ""
|
||||
@echo "=====> Total test coverage: <====="
|
||||
@echo ""
|
||||
# Print the overall coverage here for quick access.
|
||||
$Q go tool cover -func ".cover/c.out" | grep "total:" | awk '{print $$3}'
|
||||
# Generate the HTML report that can be viewed from the browser in CI.
|
||||
$Q go tool cover -html ".cover/c.out" -o .cover/all.html
|
||||
|
||||
.PHONY: test-ssh-server
|
||||
test-ssh-server:
|
||||
docker-compose -f ssh_server_tests/docker-compose.yml up
|
||||
@@ -199,58 +209,6 @@ cloudflared-darwin-amd64.tgz: cloudflared
|
||||
tar czf cloudflared-darwin-amd64.tgz cloudflared
|
||||
rm cloudflared
|
||||
|
||||
.PHONY: cloudflared-junos
|
||||
cloudflared-junos: cloudflared jetez-certificate.pem jetez-key.pem
|
||||
jetez --source . \
|
||||
-j jet.yaml \
|
||||
--key jetez-key.pem \
|
||||
--cert jetez-certificate.pem \
|
||||
--version $(VERSION)
|
||||
rm jetez-*.pem
|
||||
|
||||
jetez-certificate.pem:
|
||||
ifndef JETEZ_CERT
|
||||
$(error JETEZ_CERT not defined)
|
||||
endif
|
||||
@echo "Writing JetEZ certificate"
|
||||
@echo "$$JETEZ_CERT" > jetez-certificate.pem
|
||||
|
||||
jetez-key.pem:
|
||||
ifndef JETEZ_KEY
|
||||
$(error JETEZ_KEY not defined)
|
||||
endif
|
||||
@echo "Writing JetEZ key"
|
||||
@echo "$$JETEZ_KEY" > jetez-key.pem
|
||||
|
||||
.PHONY: publish-cloudflared-junos
|
||||
publish-cloudflared-junos: cloudflared-junos cloudflared-x86-64.latest.s3
|
||||
ifndef S3_ENDPOINT
|
||||
$(error S3_HOST not defined)
|
||||
endif
|
||||
ifndef S3_URI
|
||||
$(error S3_URI not defined)
|
||||
endif
|
||||
ifndef S3_ACCESS_KEY
|
||||
$(error S3_ACCESS_KEY not defined)
|
||||
endif
|
||||
ifndef S3_SECRET_KEY
|
||||
$(error S3_SECRET_KEY not defined)
|
||||
endif
|
||||
sha256sum cloudflared-x86-64-$(VERSION).tgz | awk '{printf $$1}' > cloudflared-x86-64-$(VERSION).tgz.shasum
|
||||
s4cmd --endpoint-url $(S3_ENDPOINT) --force --API-GrantRead=uri=http://acs.amazonaws.com/groups/global/AllUsers \
|
||||
put cloudflared-x86-64-$(VERSION).tgz $(S3_URI)/cloudflared-x86-64-$(VERSION).tgz
|
||||
s4cmd --endpoint-url $(S3_ENDPOINT) --force --API-GrantRead=uri=http://acs.amazonaws.com/groups/global/AllUsers \
|
||||
put cloudflared-x86-64-$(VERSION).tgz.shasum $(S3_URI)/cloudflared-x86-64-$(VERSION).tgz.shasum
|
||||
dpkg --compare-versions "$(VERSION)" gt "$(shell cat cloudflared-x86-64.latest.s3)" && \
|
||||
echo -n "$(VERSION)" > cloudflared-x86-64.latest && \
|
||||
s4cmd --endpoint-url $(S3_ENDPOINT) --force --API-GrantRead=uri=http://acs.amazonaws.com/groups/global/AllUsers \
|
||||
put cloudflared-x86-64.latest $(S3_URI)/cloudflared-x86-64.latest || \
|
||||
echo "Latest version not updated"
|
||||
|
||||
cloudflared-x86-64.latest.s3:
|
||||
s4cmd --endpoint-url $(S3_ENDPOINT) --force \
|
||||
get $(S3_URI)/cloudflared-x86-64.latest cloudflared-x86-64.latest.s3
|
||||
|
||||
.PHONY: homebrew-upload
|
||||
homebrew-upload: cloudflared-darwin-amd64.tgz
|
||||
aws s3 --endpoint-url $(S3_ENDPOINT) cp --acl public-read $$^ $(S3_URI)/cloudflared-$$(VERSION)-$1.tgz
|
||||
|
||||
@@ -1,3 +1,68 @@
|
||||
2023.7.2
|
||||
- 2023-07-19 TUN-7599: Onboard cloudflared to Software Dashboard
|
||||
- 2023-07-19 TUN-7587: Remove junos builds
|
||||
- 2023-07-18 TUN-7597: Add flag to disable auto-update services to be installed
|
||||
- 2023-07-17 TUN-7594: Add nightly arm64 cloudflared internal deb publishes
|
||||
- 2023-07-14 TUN-7586: Upgrade go-jose/go-jose/v3 and core-os/go-oidc/v3
|
||||
- 2023-07-14 TUN-7589: Remove legacy golang.org/x/crypto/ssh/terminal package usage
|
||||
- 2023-07-14 TUN-7590: Remove usages of ioutil
|
||||
- 2023-07-14 TUN-7585: Remove h2mux compression
|
||||
- 2023-07-14 TUN-7588: Update package coreos/go-systemd
|
||||
|
||||
2023.7.1
|
||||
- 2023-07-13 TUN-7582: Correct changelog wording for --management-diagnostics
|
||||
- 2023-07-12 TUN-7575: Add option to disable PTMU discovery over QUIC
|
||||
|
||||
2023.7.0
|
||||
- 2023-07-06 TUN-7558: Flush on Writes for StreamBasedOriginProxy
|
||||
- 2023-07-05 TUN-7553: Add flag to enable management diagnostic services
|
||||
- 2023-07-05 TUN-7564: Support cf-trace-id for cloudflared access
|
||||
- 2023-07-05 TUN-7477: Decrement UDP sessions on shutdown
|
||||
- 2023-07-03 TUN-7545: Add support for full bidirectionally streaming with close signal propagation
|
||||
- 2023-06-30 TUN-7549: Add metrics route to management service
|
||||
- 2023-06-30 TUN-7551: Complete removal of raven-go to sentry-go
|
||||
- 2023-06-30 TUN-7550: Add pprof endpoint to management service
|
||||
- 2023-06-29 TUN-7543: Add --debug-stream flag to cloudflared access ssh
|
||||
- 2023-06-26 TUN-6011: Remove docker networks from ICMP Proxy test
|
||||
- 2023-06-20 AUTH-5328 Pass cloudflared_token_check param when running cloudflared access login
|
||||
|
||||
2023.6.1
|
||||
- 2023-06-19 TUN-7480: Added a timeout for unregisterUDP.
|
||||
- 2023-06-16 TUN-7477: Add UDP/TCP session metrics
|
||||
- 2023-06-14 TUN-7468: Increase the limit of incoming streams
|
||||
|
||||
2023.6.0
|
||||
- 2023-06-15 TUN-7471: Fixes cloudflared not closing the quic stream on unregister UDP session
|
||||
- 2023-06-09 TUN-7463: Add default ingress rule if no ingress rules are provided when updating the configuration
|
||||
- 2023-05-31 TUN-7447: Add a cover build to report code coverage
|
||||
|
||||
2023.5.1
|
||||
- 2023-05-16 TUN-7424: Add CORS headers to host_details responses
|
||||
- 2023-05-11 TUN-7421: Add *.cloudflare.com to permitted Origins for management WebSocket requests
|
||||
- 2023-05-05 TUN-7404: Default configuration version set to -1
|
||||
- 2023-05-05 TUN-7227: Migrate to devincarr/quic-go
|
||||
|
||||
2023.5.0
|
||||
- 2023-04-27 TUN-7398: Add support for quic safe stream to set deadline
|
||||
- 2023-04-26 TUN-7394: Retry StartFirstTunnel on quic.ApplicationErrors
|
||||
- 2023-04-26 TUN-7392: Ignore release checksum upload if asset already uploaded
|
||||
- 2023-04-25 TUN-7392: Ignore duplicate artifact uploads for github release
|
||||
- 2023-04-25 TUN-7393: Add json output for cloudflared tail
|
||||
- 2023-04-24 TUN-7390: Remove Debian stretch builds
|
||||
|
||||
2023.4.2
|
||||
- 2023-04-24 TUN-7133: Add sampling support for streaming logs
|
||||
- 2023-04-21 TUN-7141: Add component tests for streaming logs
|
||||
- 2023-04-21 TUN-7373: Streaming logs override for same actor
|
||||
- 2023-04-20 TUN-7383: Bump requirements.txt
|
||||
- 2023-04-19 TUN-7361: Add a label to override hostname
|
||||
- 2023-04-19 TUN-7378: Remove RPC debug logs
|
||||
- 2023-04-18 TUN-7360: Add Get Host Details handler in management service
|
||||
- 2023-04-17 AUTH-3122 Verify that Access tokens are still valid in curl command
|
||||
- 2023-04-17 TUN-7129: Categorize TCP logs for streaming logs
|
||||
- 2023-04-17 TUN-7130: Categorize UDP logs for streaming logs
|
||||
- 2023-04-10 AUTH-4887 Add aud parameter to token transfer url
|
||||
|
||||
2023.4.1
|
||||
- 2023-04-13 TUN-7368: Report destination address for TCP requests in logs
|
||||
- 2023-04-12 TUN-7134: Acquire token for cloudflared tail
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: cloudflared
|
||||
description: Client for Cloudflare Tunnels
|
||||
annotations:
|
||||
backstage.io/source-location: url:https://bitbucket.cfdata.org/projects/TUN/repos/cloudflared/browse
|
||||
cloudflare.com/software-excellence-opt-in: "true"
|
||||
cloudflare.com/jira-project-key: "TUN"
|
||||
cloudflare.com/jira-project-component: "Cloudflare Tunnel"
|
||||
tags:
|
||||
- internal
|
||||
spec:
|
||||
type: "service"
|
||||
lifecycle: "Active"
|
||||
owner: "teams/tunnel-teams-routing"
|
||||
@@ -3,7 +3,7 @@ package cfapi
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -49,7 +49,7 @@ func Test_parseListTunnels(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
body := ioutil.NopCloser(bytes.NewReader([]byte(tt.args.body)))
|
||||
body := io.NopCloser(bytes.NewReader([]byte(tt.args.body)))
|
||||
got, err := parseListTunnels(body)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("parseListTunnels() error = %v, wantErr %v", err, tt.wantErr)
|
||||
|
||||
+20
-34
@@ -3,7 +3,7 @@ pinned_go_fips: &pinned_go_fips go-boring=1.19.6-1
|
||||
|
||||
build_dir: &build_dir /cfsetup_build
|
||||
default-flavor: bullseye
|
||||
stretch: &stretch
|
||||
buster: &buster
|
||||
build:
|
||||
build_dir: *build_dir
|
||||
builddeps: &build_deps
|
||||
@@ -29,6 +29,12 @@ stretch: &stretch
|
||||
- export GOARCH=amd64
|
||||
- export FIPS=true
|
||||
- make cloudflared
|
||||
cover:
|
||||
build_dir: *build_dir
|
||||
builddeps: *build_deps
|
||||
pre-cache: *build_pre_cache
|
||||
post-cache:
|
||||
- make cover
|
||||
# except FIPS (handled in github-fips-release-pkgs) and macos (handled in github-release-macos-amd64)
|
||||
github-release-pkgs:
|
||||
build_dir: *build_dir
|
||||
@@ -115,7 +121,7 @@ stretch: &stretch
|
||||
- export FIPS=true
|
||||
- export ORIGINAL_NAME=true
|
||||
- make cloudflared-deb
|
||||
build-fips-internal-deb-nightly:
|
||||
build-internal-deb-nightly-amd64:
|
||||
build_dir: *build_dir
|
||||
builddeps: *build_fips_deb_deps
|
||||
post-cache:
|
||||
@@ -125,6 +131,16 @@ stretch: &stretch
|
||||
- export FIPS=true
|
||||
- export ORIGINAL_NAME=true
|
||||
- make cloudflared-deb
|
||||
build-internal-deb-nightly-arm64:
|
||||
build_dir: *build_dir
|
||||
builddeps: *build_fips_deb_deps
|
||||
post-cache:
|
||||
- export GOOS=linux
|
||||
- export GOARCH=arm64
|
||||
- export NIGHTLY=true
|
||||
#- export FIPS=true # TUN-7595
|
||||
- export ORIGINAL_NAME=true
|
||||
- make cloudflared-deb
|
||||
build-deb-arm64:
|
||||
build_dir: *build_dir
|
||||
builddeps: *build_deb_deps
|
||||
@@ -238,36 +254,6 @@ stretch: &stretch
|
||||
pre-cache: *install_pygithub
|
||||
post-cache:
|
||||
- make github-message
|
||||
build-junos:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
- *pinned_go
|
||||
- build-essential
|
||||
- python3
|
||||
- genisoimage
|
||||
pre-cache:
|
||||
- ln -s /usr/bin/genisoimage /usr/bin/mkisofs
|
||||
post-cache:
|
||||
- export CGO_ENABLED=0
|
||||
- export GOOS=freebsd
|
||||
- export GOARCH=amd64
|
||||
- make cloudflared
|
||||
publish-junos:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
- *pinned_go
|
||||
- build-essential
|
||||
- python3
|
||||
- genisoimage
|
||||
- jetez
|
||||
- s4cmd
|
||||
pre-cache:
|
||||
- ln -s /usr/bin/genisoimage /usr/bin/mkisofs
|
||||
post-cache:
|
||||
- export GOOS=freebsd
|
||||
- export GOARCH=amd64
|
||||
- make publish-cloudflared-junos
|
||||
|
||||
buster: *stretch
|
||||
bullseye: *stretch
|
||||
bookworm: *stretch
|
||||
bullseye: *buster
|
||||
bookworm: *buster
|
||||
|
||||
@@ -3,6 +3,7 @@ package access
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -38,6 +40,7 @@ func StartForwarder(forwarder config.Forwarder, shutdown <-chan struct{}, log *z
|
||||
if forwarder.TokenSecret != "" {
|
||||
headers.Set(cfAccessClientSecretHeader, forwarder.TokenSecret)
|
||||
}
|
||||
headers.Set("User-Agent", userAgent)
|
||||
|
||||
carrier.SetBastionDest(headers, forwarder.Destination)
|
||||
|
||||
@@ -58,7 +61,12 @@ func StartForwarder(forwarder config.Forwarder, shutdown <-chan struct{}, log *z
|
||||
// useful for proxying other protocols (like ssh) over websockets
|
||||
// (which you can put Access in front of)
|
||||
func ssh(c *cli.Context) error {
|
||||
log := logger.CreateSSHLoggerFromContext(c, logger.EnableTerminalLog)
|
||||
// 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)
|
||||
@@ -76,6 +84,7 @@ func ssh(c *cli.Context) error {
|
||||
if c.IsSet(sshTokenSecretFlag) {
|
||||
headers.Set(cfAccessClientSecretHeader, c.String(sshTokenSecretFlag))
|
||||
}
|
||||
headers.Set("User-Agent", userAgent)
|
||||
|
||||
carrier.SetBastionDest(headers, c.String(sshDestinationFlag))
|
||||
|
||||
@@ -121,15 +130,27 @@ func ssh(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return carrier.StartClient(wsConn, &carrier.StdinoutStream{}, options)
|
||||
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]))
|
||||
header, value, found := strings.Cut(valuePair, ":")
|
||||
if found {
|
||||
headers.Add(strings.TrimSpace(header), strings.TrimSpace(value))
|
||||
}
|
||||
}
|
||||
return headers
|
||||
|
||||
@@ -12,7 +12,8 @@ func TestBuildRequestHeaders(t *testing.T) {
|
||||
headers.Add("client", "value")
|
||||
headers.Add("secret", "safe-value")
|
||||
|
||||
values := buildRequestHeaders([]string{"client: value", "secret: safe-value", "trash"})
|
||||
values := buildRequestHeaders([]string{"client: value", "secret: safe-value", "trash", "cf-trace-id: 000:000:0:1:asd"})
|
||||
assert.Equal(t, headers.Get("client"), values.Get("client"))
|
||||
assert.Equal(t, headers.Get("secret"), values.Get("secret"))
|
||||
assert.Equal(t, headers.Get("cf-trace-id"), values.Get("000:000:0:1:asd"))
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ const (
|
||||
sshTokenSecretFlag = "service-token-secret"
|
||||
sshGenCertFlag = "short-lived-cert"
|
||||
sshConnectTo = "connect-to"
|
||||
sshDebugStream = "debug-stream"
|
||||
sshConfigTemplate = `
|
||||
Add to your {{.Home}}/.ssh/config:
|
||||
|
||||
@@ -151,9 +152,12 @@ func Commands() []*cli.Command {
|
||||
EnvVars: []string{"TUNNEL_SERVICE_TOKEN_SECRET"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: logger.LogSSHDirectoryFlag,
|
||||
Aliases: []string{"logfile"}, //added to match the tunnel side
|
||||
Usage: "Save application log to this directory for reporting issues.",
|
||||
Name: logger.LogFileFlag,
|
||||
Usage: "Save application log to this file for reporting issues.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: logger.LogSSHDirectoryFlag,
|
||||
Usage: "Save application log to this directory for reporting issues.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: logger.LogSSHLevelFlag,
|
||||
@@ -165,6 +169,11 @@ func Commands() []*cli.Command {
|
||||
Hidden: true,
|
||||
Usage: "Connect to alternate location for testing, value is host, host:port, or sni:port:host",
|
||||
},
|
||||
&cli.Uint64Flag{
|
||||
Name: sshDebugStream,
|
||||
Hidden: true,
|
||||
Usage: "Writes up-to the max provided stream payloads to the logger as debug statements.",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -280,6 +289,13 @@ func curl(c *cli.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify that the existing token is still good; if not fetch a new one
|
||||
if err := verifyTokenAtEdge(appURL, appInfo, c, log); err != nil {
|
||||
log.Err(err).Msg("Could not verify token")
|
||||
return err
|
||||
}
|
||||
|
||||
tok, err := token.GetAppTokenIfExists(appInfo)
|
||||
if err != nil || tok == "" {
|
||||
if allowRequest {
|
||||
@@ -518,6 +534,11 @@ func isTokenValid(options *carrier.StartOptions, log *zerolog.Logger) (bool, err
|
||||
return false, errors.Wrap(err, "Could not create access request")
|
||||
}
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
|
||||
query := req.URL.Query()
|
||||
query.Set("cloudflared_token_check", "true")
|
||||
req.URL.RawQuery = query.Encode()
|
||||
|
||||
// Do not follow redirects
|
||||
client := &http.Client{
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
|
||||
@@ -25,6 +25,9 @@ func runApp(app *cli.App, graceShutdownC chan struct{}) {
|
||||
Name: "install",
|
||||
Usage: "Install cloudflared as a system service",
|
||||
Action: cliutil.ConfiguredAction(installLinuxService),
|
||||
Flags: []cli.Flag{
|
||||
noUpdateServiceFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "uninstall",
|
||||
@@ -39,15 +42,17 @@ func runApp(app *cli.App, graceShutdownC chan struct{}) {
|
||||
// The directory and files that are used by the service.
|
||||
// These are hard-coded in the templates below.
|
||||
const (
|
||||
serviceConfigDir = "/etc/cloudflared"
|
||||
serviceConfigFile = "config.yml"
|
||||
serviceCredentialFile = "cert.pem"
|
||||
serviceConfigPath = serviceConfigDir + "/" + serviceConfigFile
|
||||
cloudflaredService = "cloudflared.service"
|
||||
serviceConfigDir = "/etc/cloudflared"
|
||||
serviceConfigFile = "config.yml"
|
||||
serviceCredentialFile = "cert.pem"
|
||||
serviceConfigPath = serviceConfigDir + "/" + serviceConfigFile
|
||||
cloudflaredService = "cloudflared.service"
|
||||
cloudflaredUpdateService = "cloudflared-update.service"
|
||||
cloudflaredUpdateTimer = "cloudflared-update.timer"
|
||||
)
|
||||
|
||||
var systemdTemplates = []ServiceTemplate{
|
||||
{
|
||||
var systemdAllTemplates = map[string]ServiceTemplate{
|
||||
cloudflaredService: {
|
||||
Path: fmt.Sprintf("/etc/systemd/system/%s", cloudflaredService),
|
||||
Content: `[Unit]
|
||||
Description=cloudflared
|
||||
@@ -64,8 +69,8 @@ RestartSec=5s
|
||||
WantedBy=multi-user.target
|
||||
`,
|
||||
},
|
||||
{
|
||||
Path: "/etc/systemd/system/cloudflared-update.service",
|
||||
cloudflaredUpdateService: {
|
||||
Path: fmt.Sprintf("/etc/systemd/system/%s", cloudflaredUpdateService),
|
||||
Content: `[Unit]
|
||||
Description=Update cloudflared
|
||||
After=network.target
|
||||
@@ -74,8 +79,8 @@ After=network.target
|
||||
ExecStart=/bin/bash -c '{{ .Path }} update; code=$?; if [ $code -eq 11 ]; then systemctl restart cloudflared; exit 0; fi; exit $code'
|
||||
`,
|
||||
},
|
||||
{
|
||||
Path: "/etc/systemd/system/cloudflared-update.timer",
|
||||
cloudflaredUpdateTimer: {
|
||||
Path: fmt.Sprintf("/etc/systemd/system/%s", cloudflaredUpdateTimer),
|
||||
Content: `[Unit]
|
||||
Description=Update cloudflared
|
||||
|
||||
@@ -106,7 +111,7 @@ var sysvTemplate = ServiceTemplate{
|
||||
# Description: cloudflared agent
|
||||
### END INIT INFO
|
||||
name=$(basename $(readlink -f $0))
|
||||
cmd="{{.Path}} --pidfile /var/run/$name.pid --autoupdate-freq 24h0m0s{{ range .ExtraArgs }} {{ . }}{{ end }}"
|
||||
cmd="{{.Path}} --pidfile /var/run/$name.pid {{ range .ExtraArgs }} {{ . }}{{ end }}"
|
||||
pid_file="/var/run/$name.pid"
|
||||
stdout_log="/var/log/$name.log"
|
||||
stderr_log="/var/log/$name.err"
|
||||
@@ -178,6 +183,14 @@ exit 0
|
||||
`,
|
||||
}
|
||||
|
||||
var (
|
||||
noUpdateServiceFlag = &cli.BoolFlag{
|
||||
Name: "no-update-service",
|
||||
Usage: "Disable auto-update of the cloudflared linux service, which restarts the server to upgrade for new versions.",
|
||||
Value: false,
|
||||
}
|
||||
)
|
||||
|
||||
func isSystemd() bool {
|
||||
if _, err := os.Stat("/run/systemd/system"); err == nil {
|
||||
return true
|
||||
@@ -196,6 +209,9 @@ func installLinuxService(c *cli.Context) error {
|
||||
Path: etPath,
|
||||
}
|
||||
|
||||
// Check if the "no update flag" is set
|
||||
autoUpdate := !c.IsSet(noUpdateServiceFlag.Name)
|
||||
|
||||
var extraArgsFunc func(c *cli.Context, log *zerolog.Logger) ([]string, error)
|
||||
if c.NArg() == 0 {
|
||||
extraArgsFunc = buildArgsForConfig
|
||||
@@ -213,10 +229,10 @@ func installLinuxService(c *cli.Context) error {
|
||||
switch {
|
||||
case isSystemd():
|
||||
log.Info().Msgf("Using Systemd")
|
||||
err = installSystemd(&templateArgs, log)
|
||||
err = installSystemd(&templateArgs, autoUpdate, log)
|
||||
default:
|
||||
log.Info().Msgf("Using SysV")
|
||||
err = installSysv(&templateArgs, log)
|
||||
err = installSysv(&templateArgs, autoUpdate, log)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
@@ -261,7 +277,20 @@ credentials-file: CREDENTIALS-FILE
|
||||
}, nil
|
||||
}
|
||||
|
||||
func installSystemd(templateArgs *ServiceTemplateArgs, log *zerolog.Logger) error {
|
||||
func installSystemd(templateArgs *ServiceTemplateArgs, autoUpdate bool, log *zerolog.Logger) error {
|
||||
var systemdTemplates []ServiceTemplate
|
||||
if autoUpdate {
|
||||
systemdTemplates = []ServiceTemplate{
|
||||
systemdAllTemplates[cloudflaredService],
|
||||
systemdAllTemplates[cloudflaredUpdateService],
|
||||
systemdAllTemplates[cloudflaredUpdateTimer],
|
||||
}
|
||||
} else {
|
||||
systemdTemplates = []ServiceTemplate{
|
||||
systemdAllTemplates[cloudflaredService],
|
||||
}
|
||||
}
|
||||
|
||||
for _, serviceTemplate := range systemdTemplates {
|
||||
err := serviceTemplate.Generate(templateArgs)
|
||||
if err != nil {
|
||||
@@ -273,10 +302,14 @@ func installSystemd(templateArgs *ServiceTemplateArgs, log *zerolog.Logger) erro
|
||||
log.Err(err).Msgf("systemctl enable %s error", cloudflaredService)
|
||||
return err
|
||||
}
|
||||
if err := runCommand("systemctl", "start", "cloudflared-update.timer"); err != nil {
|
||||
log.Err(err).Msg("systemctl start cloudflared-update.timer error")
|
||||
return err
|
||||
|
||||
if autoUpdate {
|
||||
if err := runCommand("systemctl", "start", cloudflaredUpdateTimer); err != nil {
|
||||
log.Err(err).Msgf("systemctl start %s error", cloudflaredUpdateTimer)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := runCommand("systemctl", "daemon-reload"); err != nil {
|
||||
log.Err(err).Msg("systemctl daemon-reload error")
|
||||
return err
|
||||
@@ -284,12 +317,19 @@ func installSystemd(templateArgs *ServiceTemplateArgs, log *zerolog.Logger) erro
|
||||
return runCommand("systemctl", "start", cloudflaredService)
|
||||
}
|
||||
|
||||
func installSysv(templateArgs *ServiceTemplateArgs, log *zerolog.Logger) error {
|
||||
func installSysv(templateArgs *ServiceTemplateArgs, autoUpdate bool, log *zerolog.Logger) error {
|
||||
confPath, err := sysvTemplate.ResolvePath()
|
||||
if err != nil {
|
||||
log.Err(err).Msg("error resolving system path")
|
||||
return err
|
||||
}
|
||||
|
||||
if autoUpdate {
|
||||
templateArgs.ExtraArgs = append([]string{"--autoupdate-freq 24h0m0s"}, templateArgs.ExtraArgs...)
|
||||
} else {
|
||||
templateArgs.ExtraArgs = append([]string{"--no-autoupdate"}, templateArgs.ExtraArgs...)
|
||||
}
|
||||
|
||||
if err := sysvTemplate.Generate(templateArgs); err != nil {
|
||||
log.Err(err).Msg("error generating system template")
|
||||
return err
|
||||
@@ -327,19 +367,35 @@ func uninstallLinuxService(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func uninstallSystemd(log *zerolog.Logger) error {
|
||||
if err := runCommand("systemctl", "disable", cloudflaredService); err != nil {
|
||||
log.Err(err).Msgf("systemctl disable %s error", cloudflaredService)
|
||||
return err
|
||||
// Get only the installed services
|
||||
installedServices := make(map[string]ServiceTemplate)
|
||||
for serviceName, serviceTemplate := range systemdAllTemplates {
|
||||
if err := runCommand("systemctl", "status", serviceName); err == nil {
|
||||
installedServices[serviceName] = serviceTemplate
|
||||
} else {
|
||||
log.Info().Msgf("Service '%s' not installed, skipping its uninstall", serviceName)
|
||||
}
|
||||
}
|
||||
if err := runCommand("systemctl", "stop", cloudflaredService); err != nil {
|
||||
log.Err(err).Msgf("systemctl stop %s error", cloudflaredService)
|
||||
return err
|
||||
|
||||
if _, exists := installedServices[cloudflaredService]; exists {
|
||||
if err := runCommand("systemctl", "disable", cloudflaredService); err != nil {
|
||||
log.Err(err).Msgf("systemctl disable %s error", cloudflaredService)
|
||||
return err
|
||||
}
|
||||
if err := runCommand("systemctl", "stop", cloudflaredService); err != nil {
|
||||
log.Err(err).Msgf("systemctl stop %s error", cloudflaredService)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := runCommand("systemctl", "stop", "cloudflared-update.timer"); err != nil {
|
||||
log.Err(err).Msg("systemctl stop cloudflared-update.timer error")
|
||||
return err
|
||||
|
||||
if _, exists := installedServices[cloudflaredUpdateTimer]; exists {
|
||||
if err := runCommand("systemctl", "stop", cloudflaredUpdateTimer); err != nil {
|
||||
log.Err(err).Msgf("systemctl stop %s error", cloudflaredUpdateTimer)
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, serviceTemplate := range systemdTemplates {
|
||||
|
||||
for _, serviceTemplate := range installedServices {
|
||||
if err := serviceTemplate.Remove(); err != nil {
|
||||
log.Err(err).Msg("error removing service template")
|
||||
return err
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
@@ -64,7 +63,7 @@ func (st *ServiceTemplate) Generate(args *ServiceTemplateArgs) error {
|
||||
return fmt.Errorf("error creating %s: %v", plistFolder, err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(resolvedPath, buffer.Bytes(), fileMode)
|
||||
err = os.WriteFile(resolvedPath, buffer.Bytes(), fileMode)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing %s: %v", resolvedPath, err)
|
||||
}
|
||||
@@ -103,7 +102,7 @@ func runCommand(command string, args ...string) error {
|
||||
return fmt.Errorf("error starting %s: %v", command, err)
|
||||
}
|
||||
|
||||
output, _ := ioutil.ReadAll(stderr)
|
||||
output, _ := io.ReadAll(stderr)
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s %v returned with error code %v due to: %v", command, args, err, string(output))
|
||||
|
||||
@@ -32,6 +32,38 @@ func Init(bi *cliutil.BuildInfo) {
|
||||
}
|
||||
|
||||
func Command() *cli.Command {
|
||||
subcommands := []*cli.Command{
|
||||
buildTailManagementTokenSubcommand(),
|
||||
}
|
||||
|
||||
return buildTailCommand(subcommands)
|
||||
}
|
||||
|
||||
func buildTailManagementTokenSubcommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "token",
|
||||
Action: cliutil.ConfiguredAction(managementTokenCommand),
|
||||
Usage: "Get management access jwt",
|
||||
UsageText: "cloudflared tail token TUNNEL_ID",
|
||||
Description: `Get management access jwt for a tunnel`,
|
||||
Hidden: true,
|
||||
}
|
||||
}
|
||||
|
||||
func managementTokenCommand(c *cli.Context) error {
|
||||
log := createLogger(c)
|
||||
token, err := getManagementToken(c, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var tokenResponse = struct {
|
||||
Token string `json:"token"`
|
||||
}{Token: token}
|
||||
|
||||
return json.NewEncoder(os.Stdout).Encode(tokenResponse)
|
||||
}
|
||||
|
||||
func buildTailCommand(subcommands []*cli.Command) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "tail",
|
||||
Action: Run,
|
||||
@@ -51,16 +83,28 @@ func Command() *cli.Command {
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "level",
|
||||
Usage: "Filter by specific log levels (debug, info, warn, error)",
|
||||
Usage: "Filter by specific log levels (debug, info, warn, error). Filters by debug log level by default.",
|
||||
EnvVars: []string{"TUNNEL_MANAGEMENT_FILTER_LEVEL"},
|
||||
Value: "debug",
|
||||
},
|
||||
&cli.Float64Flag{
|
||||
Name: "sample",
|
||||
Usage: "Sample log events by percentage (0.0 .. 1.0). No sampling by default.",
|
||||
EnvVars: []string{"TUNNEL_MANAGEMENT_FILTER_SAMPLE"},
|
||||
Value: 1.0,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "token",
|
||||
Usage: "Access token for a specific tunnel",
|
||||
Value: "",
|
||||
EnvVars: []string{"TUNNEL_MANAGEMENT_TOKEN"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "output",
|
||||
Usage: "Output format for the logs (default, json)",
|
||||
Value: "default",
|
||||
EnvVars: []string{"TUNNEL_MANAGEMENT_OUTPUT"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "management-hostname",
|
||||
Usage: "Management hostname to signify incoming management requests",
|
||||
@@ -87,6 +131,7 @@ func Command() *cli.Command {
|
||||
Value: credentials.FindDefaultOriginCertPath(),
|
||||
},
|
||||
},
|
||||
Subcommands: subcommands,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,9 +184,11 @@ func createLogger(c *cli.Context) *zerolog.Logger {
|
||||
func parseFilters(c *cli.Context) (*management.StreamingFilters, error) {
|
||||
var level *management.LogLevel
|
||||
var events []management.LogEventType
|
||||
var sample float64
|
||||
|
||||
argLevel := c.String("level")
|
||||
argEvents := c.StringSlice("event")
|
||||
argSample := c.Float64("sample")
|
||||
|
||||
if argLevel != "" {
|
||||
l, ok := management.ParseLogLevel(argLevel)
|
||||
@@ -159,14 +206,20 @@ func parseFilters(c *cli.Context) (*management.StreamingFilters, error) {
|
||||
events = append(events, t)
|
||||
}
|
||||
|
||||
if level == nil && len(events) == 0 {
|
||||
if argSample <= 0.0 || argSample > 1.0 {
|
||||
return nil, fmt.Errorf("invalid --sample value provided, please make sure it is in the range (0.0 .. 1.0)")
|
||||
}
|
||||
sample = argSample
|
||||
|
||||
if level == nil && len(events) == 0 && argSample != 1.0 {
|
||||
// When no filters are provided, do not return a StreamingFilters struct
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &management.StreamingFilters{
|
||||
Level: level,
|
||||
Events: events,
|
||||
Level: level,
|
||||
Events: events,
|
||||
Sampling: sample,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -223,6 +276,24 @@ func buildURL(c *cli.Context, log *zerolog.Logger) (url.URL, error) {
|
||||
return url.URL{Scheme: "wss", Host: managementHostname, Path: "/logs", RawQuery: query.Encode()}, nil
|
||||
}
|
||||
|
||||
func printLine(log *management.Log, logger *zerolog.Logger) {
|
||||
fields, err := json.Marshal(log.Fields)
|
||||
if err != nil {
|
||||
fields = []byte("unable to parse fields")
|
||||
logger.Debug().Msgf("unable to parse fields from event %+v", log)
|
||||
}
|
||||
fmt.Printf("%s %s %s %s %s\n", log.Time, log.Level, log.Event, log.Message, fields)
|
||||
}
|
||||
|
||||
func printJSON(log *management.Log, logger *zerolog.Logger) {
|
||||
output, err := json.Marshal(log)
|
||||
if err != nil {
|
||||
logger.Debug().Msgf("unable to parse event to json %+v", log)
|
||||
} else {
|
||||
fmt.Println(string(output))
|
||||
}
|
||||
}
|
||||
|
||||
// Run implements a foreground runner
|
||||
func Run(c *cli.Context) error {
|
||||
log := createLogger(c)
|
||||
@@ -231,6 +302,16 @@ func Run(c *cli.Context) error {
|
||||
signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT)
|
||||
defer signal.Stop(signals)
|
||||
|
||||
output := "default"
|
||||
switch c.String("output") {
|
||||
case "default", "":
|
||||
output = "default"
|
||||
case "json":
|
||||
output = "json"
|
||||
default:
|
||||
log.Err(errors.New("invalid --output value provided, please make sure it is one of: default, json")).Send()
|
||||
}
|
||||
|
||||
filters, err := parseFilters(c)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("invalid filters provided")
|
||||
@@ -311,12 +392,11 @@ func Run(c *cli.Context) error {
|
||||
}
|
||||
// Output all the logs received to stdout
|
||||
for _, l := range logs.Logs {
|
||||
fields, err := json.Marshal(l.Fields)
|
||||
if err != nil {
|
||||
fields = []byte("unable to parse fields")
|
||||
log.Debug().Msgf("unable to parse fields from event %+v", l)
|
||||
if output == "json" {
|
||||
printJSON(l, log)
|
||||
} else {
|
||||
printLine(l, log)
|
||||
}
|
||||
fmt.Printf("%s %s %s %s %s\n", l.Time, l.Level, l.Event, l.Message, fields)
|
||||
}
|
||||
case management.UnknownServerEventType:
|
||||
fallthrough
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime/trace"
|
||||
@@ -12,7 +11,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-systemd/daemon"
|
||||
"github.com/coreos/go-systemd/v22/daemon"
|
||||
"github.com/facebookgo/grace/gracenet"
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/google/uuid"
|
||||
@@ -29,6 +28,7 @@ import (
|
||||
"github.com/cloudflare/cloudflared/config"
|
||||
"github.com/cloudflare/cloudflared/connection"
|
||||
"github.com/cloudflare/cloudflared/credentials"
|
||||
"github.com/cloudflare/cloudflared/edgediscovery"
|
||||
"github.com/cloudflare/cloudflared/features"
|
||||
"github.com/cloudflare/cloudflared/ingress"
|
||||
"github.com/cloudflare/cloudflared/logger"
|
||||
@@ -78,6 +78,14 @@ const (
|
||||
// hostKeyPath is the path of the dir to save SSH host keys too
|
||||
hostKeyPath = "host-key-path"
|
||||
|
||||
// udpUnregisterSessionTimeout is how long we wait before we stop trying to unregister a UDP session from the edge
|
||||
udpUnregisterSessionTimeoutFlag = "udp-unregister-session-timeout"
|
||||
|
||||
// quicDisablePathMTUDiscovery sets if QUIC should not perform PTMU discovery and use a smaller (safe) packet size.
|
||||
// Packets will then be at most 1252 (IPv4) / 1232 (IPv6) bytes in size.
|
||||
// Note that this may result in packet drops for UDP proxying, since we expect being able to send at least 1280 bytes of inner packets.
|
||||
quicDisablePathMTUDiscovery = "quic-disable-pmtu-discovery"
|
||||
|
||||
// uiFlag is to enable launching cloudflared in interactive UI mode
|
||||
uiFlag = "ui"
|
||||
|
||||
@@ -95,6 +103,7 @@ Eg. cloudflared tunnel --url localhost:8080/.
|
||||
Please note that Quick Tunnels are meant to be ephemeral and should only be used for testing purposes.
|
||||
For production usage, we recommend creating Named Tunnels. (https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-guide/)
|
||||
`
|
||||
connectorLabelFlag = "label"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -295,7 +304,7 @@ func StartServer(
|
||||
}
|
||||
|
||||
if c.IsSet("trace-output") {
|
||||
tmpTraceFile, err := ioutil.TempFile("", "trace")
|
||||
tmpTraceFile, err := os.CreateTemp("", "trace")
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Failed to create new temporary file to save trace output")
|
||||
}
|
||||
@@ -397,12 +406,27 @@ func StartServer(
|
||||
}
|
||||
}
|
||||
|
||||
localRules := []ingress.Rule{}
|
||||
internalRules := []ingress.Rule{}
|
||||
if features.Contains(features.FeatureManagementLogs) {
|
||||
mgmt := management.New(c.String("management-hostname"), logger.ManagementLogger.Log, logger.ManagementLogger)
|
||||
localRules = []ingress.Rule{ingress.NewManagementRule(mgmt)}
|
||||
serviceIP := c.String("service-op-ip")
|
||||
if edgeAddrs, err := edgediscovery.ResolveEdge(log, tunnelConfig.Region, tunnelConfig.EdgeIPVersion); err == nil {
|
||||
if serviceAddr, err := edgeAddrs.GetAddrForRPC(); err == nil {
|
||||
serviceIP = serviceAddr.TCP.String()
|
||||
}
|
||||
}
|
||||
|
||||
mgmt := management.New(
|
||||
c.String("management-hostname"),
|
||||
c.Bool("management-diagnostics"),
|
||||
serviceIP,
|
||||
clientID,
|
||||
c.String(connectorLabelFlag),
|
||||
logger.ManagementLogger.Log,
|
||||
logger.ManagementLogger,
|
||||
)
|
||||
internalRules = []ingress.Rule{ingress.NewManagementRule(mgmt)}
|
||||
}
|
||||
orchestrator, err := orchestration.NewOrchestrator(ctx, orchestratorConfig, tunnelConfig.Tags, localRules, tunnelConfig.Log)
|
||||
orchestrator, err := orchestration.NewOrchestrator(ctx, orchestratorConfig, tunnelConfig.Tags, internalRules, tunnelConfig.Log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -667,6 +691,23 @@ func tunnelFlags(shouldHide bool) []cli.Flag {
|
||||
Value: 4,
|
||||
Hidden: true,
|
||||
}),
|
||||
altsrc.NewDurationFlag(&cli.DurationFlag{
|
||||
Name: udpUnregisterSessionTimeoutFlag,
|
||||
Value: 5 * time.Second,
|
||||
Hidden: true,
|
||||
}),
|
||||
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||
Name: quicDisablePathMTUDiscovery,
|
||||
EnvVars: []string{"TUNNEL_DISABLE_QUIC_PMTU"},
|
||||
Usage: "Use this option to disable PTMU discovery for QUIC connections. This will result in lower packet sizes. Not however, that this may cause instability for UDP proxying.",
|
||||
Value: false,
|
||||
Hidden: true,
|
||||
}),
|
||||
altsrc.NewStringFlag(&cli.StringFlag{
|
||||
Name: connectorLabelFlag,
|
||||
Usage: "Use this option to give a meaningful label to a specific connector. When a tunnel starts up, a connector id unique to the tunnel is generated. This is a uuid. To make it easier to identify a connector, we will use the hostname of the machine the tunnel is running on along with the connector ID. This option exists if one wants to have more control over what their individual connectors are called.",
|
||||
Value: "",
|
||||
}),
|
||||
altsrc.NewDurationFlag(&cli.DurationFlag{
|
||||
Name: "grace-period",
|
||||
Usage: "When cloudflared receives SIGINT/SIGTERM it will stop accepting new requests, wait for in-progress requests to terminate, then shutdown. Waiting for in-progress requests will timeout after this grace period, or when a second SIGTERM/SIGINT is received.",
|
||||
@@ -735,6 +776,12 @@ func tunnelFlags(shouldHide bool) []cli.Flag {
|
||||
EnvVars: []string{"TUNNEL_POST_QUANTUM"},
|
||||
Hidden: FipsEnabled,
|
||||
}),
|
||||
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||
Name: "management-diagnostics",
|
||||
Usage: "Enables the in-depth diagnostic routes to be made available over the management service (/debug/pprof, /metrics, etc.)",
|
||||
EnvVars: []string{"TUNNEL_MANAGEMENT_DIAGNOSTICS"},
|
||||
Value: false,
|
||||
}),
|
||||
selectProtocolFlag,
|
||||
overwriteDNSFlag,
|
||||
}...)
|
||||
@@ -907,6 +954,13 @@ func configureProxyFlags(shouldHide bool) []cli.Flag {
|
||||
Hidden: true,
|
||||
Value: "management.argotunnel.com",
|
||||
}),
|
||||
altsrc.NewStringFlag(&cli.StringFlag{
|
||||
Name: "service-op-ip",
|
||||
Usage: "Fallback IP for service operations run by the management service.",
|
||||
EnvVars: []string{"TUNNEL_SERVICE_OP_IP"},
|
||||
Hidden: true,
|
||||
Value: "198.41.200.113:80",
|
||||
}),
|
||||
}
|
||||
return append(flags, sshFlags(shouldHide)...)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v2/altsrc"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"golang.org/x/term"
|
||||
|
||||
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
|
||||
"github.com/cloudflare/cloudflared/config"
|
||||
@@ -231,14 +231,16 @@ func prepareTunnelConfig(
|
||||
Observer: observer,
|
||||
ReportedVersion: info.Version(),
|
||||
// Note TUN-3758 , we use Int because UInt is not supported with altsrc
|
||||
Retries: uint(c.Int("retries")),
|
||||
RunFromTerminal: isRunningFromTerminal(),
|
||||
NamedTunnel: namedTunnel,
|
||||
ProtocolSelector: protocolSelector,
|
||||
EdgeTLSConfigs: edgeTLSConfigs,
|
||||
NeedPQ: needPQ,
|
||||
PQKexIdx: pqKexIdx,
|
||||
MaxEdgeAddrRetries: uint8(c.Int("max-edge-addr-retries")),
|
||||
Retries: uint(c.Int("retries")),
|
||||
RunFromTerminal: isRunningFromTerminal(),
|
||||
NamedTunnel: namedTunnel,
|
||||
ProtocolSelector: protocolSelector,
|
||||
EdgeTLSConfigs: edgeTLSConfigs,
|
||||
NeedPQ: needPQ,
|
||||
PQKexIdx: pqKexIdx,
|
||||
MaxEdgeAddrRetries: uint8(c.Int("max-edge-addr-retries")),
|
||||
UDPUnregisterSessionTimeout: c.Duration(udpUnregisterSessionTimeoutFlag),
|
||||
DisableQUICPathMTUDiscovery: c.Bool(quicDisablePathMTUDiscovery),
|
||||
}
|
||||
packetConfig, err := newPacketConfig(c, log)
|
||||
if err != nil {
|
||||
@@ -275,7 +277,7 @@ func gracePeriod(c *cli.Context) (time.Duration, error) {
|
||||
}
|
||||
|
||||
func isRunningFromTerminal() bool {
|
||||
return terminal.IsTerminal(int(os.Stdout.Fd()))
|
||||
return term.IsTerminal(int(os.Stdout.Fd()))
|
||||
}
|
||||
|
||||
// Remove any duplicates from the slice
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -23,5 +22,5 @@ func (fs realFileSystem) validFilePath(path string) bool {
|
||||
}
|
||||
|
||||
func (fs realFileSystem) readFile(filePath string) ([]byte, error) {
|
||||
return ioutil.ReadFile(filePath)
|
||||
return os.ReadFile(filePath)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package tunnel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -53,6 +52,7 @@ func login(c *cli.Context) error {
|
||||
|
||||
resourceData, err := token.RunTransfer(
|
||||
loginURL,
|
||||
"",
|
||||
"cert",
|
||||
"callback",
|
||||
callbackStoreURL,
|
||||
@@ -65,7 +65,7 @@ func login(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path, resourceData, 0600); err != nil {
|
||||
if err := os.WriteFile(path, resourceData, 0600); err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("error writing cert to %s", path))
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -241,7 +240,7 @@ func writeTunnelCredentials(filePath string, credentials *connection.Credentials
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Unable to marshal tunnel credentials to JSON")
|
||||
}
|
||||
return ioutil.WriteFile(filePath, body, 400)
|
||||
return os.WriteFile(filePath, body, 0400)
|
||||
}
|
||||
|
||||
func buildListCommand() *cli.Command {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/facebookgo/grace/gracenet"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"golang.org/x/term"
|
||||
|
||||
"github.com/cloudflare/cloudflared/config"
|
||||
"github.com/cloudflare/cloudflared/logger"
|
||||
@@ -304,7 +304,7 @@ func wasInstalledFromPackageManager() bool {
|
||||
}
|
||||
|
||||
func isRunningFromTerminal() bool {
|
||||
return terminal.IsTerminal(int(os.Stdout.Fd()))
|
||||
return term.IsTerminal(int(os.Stdout.Fd()))
|
||||
}
|
||||
|
||||
func IsSysV() bool {
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -224,7 +223,7 @@ func TestUpdateService(t *testing.T) {
|
||||
require.Equal(t, v.Version(), mostRecentVersion)
|
||||
|
||||
require.NoError(t, v.Apply())
|
||||
dat, err := ioutil.ReadFile(testFilePath)
|
||||
dat, err := os.ReadFile(testFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, string(dat), mostRecentVersion)
|
||||
@@ -243,7 +242,7 @@ func TestBetaUpdateService(t *testing.T) {
|
||||
require.Equal(t, v.Version(), mostRecentBetaVersion)
|
||||
|
||||
require.NoError(t, v.Apply())
|
||||
dat, err := ioutil.ReadFile(testFilePath)
|
||||
dat, err := os.ReadFile(testFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, string(dat), mostRecentBetaVersion)
|
||||
@@ -289,7 +288,7 @@ func TestForcedUpdateService(t *testing.T) {
|
||||
require.Equal(t, v.Version(), mostRecentVersion)
|
||||
|
||||
require.NoError(t, v.Apply())
|
||||
dat, err := ioutil.ReadFile(testFilePath)
|
||||
dat, err := os.ReadFile(testFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, string(dat), mostRecentVersion)
|
||||
@@ -309,7 +308,7 @@ func TestUpdateSpecificVersionService(t *testing.T) {
|
||||
require.Equal(t, reqVersion, v.Version())
|
||||
|
||||
require.NoError(t, v.Apply())
|
||||
dat, err := ioutil.ReadFile(testFilePath)
|
||||
dat, err := os.ReadFile(testFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, reqVersion, string(dat))
|
||||
@@ -328,7 +327,7 @@ func TestCompressedUpdateService(t *testing.T) {
|
||||
require.Equal(t, "2020.09.02", v.Version())
|
||||
|
||||
require.NoError(t, v.Apply())
|
||||
dat, err := ioutil.ReadFile(testFilePath)
|
||||
dat, err := os.ReadFile(testFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, "2020.09.02", string(dat))
|
||||
|
||||
@@ -2,7 +2,9 @@ import json
|
||||
import subprocess
|
||||
from time import sleep
|
||||
|
||||
from constants import MANAGEMENT_HOST_NAME
|
||||
from setup import get_config_from_file
|
||||
from util import get_tunnel_connector_id
|
||||
|
||||
SINGLE_CASE_TIMEOUT = 600
|
||||
|
||||
@@ -28,6 +30,36 @@ class CloudflaredCli:
|
||||
listed = self._run_command(cmd_args, "list")
|
||||
return json.loads(listed.stdout)
|
||||
|
||||
def get_management_token(self, config, config_path):
|
||||
basecmd = [config.cloudflared_binary]
|
||||
if config_path is not None:
|
||||
basecmd += ["--config", str(config_path)]
|
||||
origincert = get_config_from_file()["origincert"]
|
||||
if origincert:
|
||||
basecmd += ["--origincert", origincert]
|
||||
|
||||
cmd_args = ["tail", "token", config.get_tunnel_id()]
|
||||
cmd = basecmd + cmd_args
|
||||
result = run_subprocess(cmd, "token", self.logger, check=True, capture_output=True, timeout=15)
|
||||
return json.loads(result.stdout.decode("utf-8").strip())["token"]
|
||||
|
||||
def get_management_url(self, path, config, config_path):
|
||||
access_jwt = self.get_management_token(config, config_path)
|
||||
connector_id = get_tunnel_connector_id()
|
||||
return f"https://{MANAGEMENT_HOST_NAME}/{path}?connector_id={connector_id}&access_token={access_jwt}"
|
||||
|
||||
def get_management_wsurl(self, path, config, config_path):
|
||||
access_jwt = self.get_management_token(config, config_path)
|
||||
connector_id = get_tunnel_connector_id()
|
||||
return f"wss://{MANAGEMENT_HOST_NAME}/{path}?connector_id={connector_id}&access_token={access_jwt}"
|
||||
|
||||
def get_connector_id(self, config):
|
||||
op = self.get_tunnel_info(config.get_tunnel_id())
|
||||
connectors = []
|
||||
for conn in op["conns"]:
|
||||
connectors.append(conn["id"])
|
||||
return connectors
|
||||
|
||||
def get_tunnel_info(self, tunnel_id):
|
||||
info = self._run_command(["info", "--output", "json", tunnel_id], "info")
|
||||
return json.loads(info.stdout)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
cloudflared_binary: "cloudflared"
|
||||
tunnel: "ae21a96c-24d1-4ce8-a6ba-962cba5976d3"
|
||||
credentials_file: "/Users/sudarsan/.cloudflared/ae21a96c-24d1-4ce8-a6ba-962cba5976d3.json"
|
||||
origincert: "/Users/sudarsan/.cloudflared/cert.pem"
|
||||
ingress:
|
||||
- hostname: named-tunnel-component-tests.example.com
|
||||
service: hello_world
|
||||
- service: http_status:404
|
||||
@@ -4,6 +4,7 @@ BACKOFF_SECS = 7
|
||||
MAX_LOG_LINES = 50
|
||||
|
||||
PROXY_DNS_PORT = 9053
|
||||
MANAGEMENT_HOST_NAME = "management.argotunnel.com"
|
||||
|
||||
|
||||
def protocols():
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
cloudflare==2.8.15
|
||||
flaky==3.7.0
|
||||
pytest==6.2.2
|
||||
pytest==7.3.1
|
||||
pytest-asyncio==0.21.0
|
||||
pyyaml==5.4.1
|
||||
requests==2.25.1
|
||||
retrying==1.3.3
|
||||
requests==2.28.2
|
||||
retrying==1.3.4
|
||||
websockets==11.0.1
|
||||
|
||||
@@ -74,7 +74,7 @@ def assert_log_to_dir(config, log_dir):
|
||||
class TestLogging:
|
||||
def test_logging_to_terminal(self, tmp_path, component_tests_config):
|
||||
config = component_tests_config()
|
||||
with start_cloudflared(tmp_path, config, new_process=True) as cloudflared:
|
||||
with start_cloudflared(tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1"], new_process=True) as cloudflared:
|
||||
wait_tunnel_ready(tunnel_url=config.get_url())
|
||||
assert_log_to_terminal(cloudflared)
|
||||
|
||||
@@ -85,7 +85,7 @@ class TestLogging:
|
||||
"logfile": str(log_file),
|
||||
}
|
||||
config = component_tests_config(extra_config)
|
||||
with start_cloudflared(tmp_path, config, new_process=True, capture_output=False):
|
||||
with start_cloudflared(tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1"], new_process=True, capture_output=False):
|
||||
wait_tunnel_ready(tunnel_url=config.get_url(), cfd_logs=str(log_file))
|
||||
assert_log_in_file(log_file)
|
||||
assert_json_log(log_file)
|
||||
@@ -98,6 +98,6 @@ class TestLogging:
|
||||
"log-directory": str(log_dir),
|
||||
}
|
||||
config = component_tests_config(extra_config)
|
||||
with start_cloudflared(tmp_path, config, new_process=True, capture_output=False):
|
||||
with start_cloudflared(tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1"], new_process=True, capture_output=False):
|
||||
wait_tunnel_ready(tunnel_url=config.get_url(), cfd_logs=str(log_dir))
|
||||
assert_log_to_dir(config, log_dir)
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python
|
||||
import requests
|
||||
from conftest import CfdModes
|
||||
from constants import METRICS_PORT, MAX_RETRIES, BACKOFF_SECS
|
||||
from retrying import retry
|
||||
from cli import CloudflaredCli
|
||||
from util import LOGGER, write_config, start_cloudflared, wait_tunnel_ready, send_requests
|
||||
import platform
|
||||
|
||||
"""
|
||||
Each test in TestManagement will:
|
||||
1. Acquire a management token from Cloudflare public API
|
||||
2. Make a request against the management service for the running tunnel
|
||||
"""
|
||||
class TestManagement:
|
||||
"""
|
||||
test_get_host_details does the following:
|
||||
1. It gets a management token from Tunnelstore using cloudflared tail token <tunnel_id>
|
||||
2. It gets the connector_id after starting a cloudflare tunnel
|
||||
3. It sends a request to the management host with the connector_id and management token
|
||||
4. Asserts that the response has a hostname and ip.
|
||||
"""
|
||||
def test_get_host_details(self, tmp_path, component_tests_config):
|
||||
# TUN-7377 : wait_tunnel_ready does not work properly in windows.
|
||||
# Skipping this test for windows for now and will address it as part of tun-7377
|
||||
if platform.system() == "Windows":
|
||||
return
|
||||
config = component_tests_config(cfd_mode=CfdModes.NAMED, run_proxy_dns=False, provide_ingress=False)
|
||||
LOGGER.debug(config)
|
||||
headers = {}
|
||||
headers["Content-Type"] = "application/json"
|
||||
config_path = write_config(tmp_path, config.full_config)
|
||||
with start_cloudflared(tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1", "--label" , "test"], cfd_args=["run", "--hello-world"], new_process=True):
|
||||
wait_tunnel_ready(tunnel_url=config.get_url(),
|
||||
require_min_connections=1)
|
||||
cfd_cli = CloudflaredCli(config, config_path, LOGGER)
|
||||
connector_id = cfd_cli.get_connector_id(config)[0]
|
||||
url = cfd_cli.get_management_url("host_details", config, config_path)
|
||||
resp = send_request(url, headers=headers)
|
||||
|
||||
# Assert response json.
|
||||
assert resp.status_code == 200, "Expected cloudflared to return 200 for host details"
|
||||
assert resp.json()["hostname"] == "custom:test", "Expected cloudflared to return hostname"
|
||||
assert resp.json()["ip"] != "", "Expected cloudflared to return ip"
|
||||
assert resp.json()["connector_id"] == connector_id, "Expected cloudflared to return connector_id"
|
||||
|
||||
"""
|
||||
test_get_metrics will verify that the /metrics endpoint returns the prometheus metrics dump
|
||||
"""
|
||||
def test_get_metrics(self, tmp_path, component_tests_config):
|
||||
# TUN-7377 : wait_tunnel_ready does not work properly in windows.
|
||||
# Skipping this test for windows for now and will address it as part of tun-7377
|
||||
if platform.system() == "Windows":
|
||||
return
|
||||
config = component_tests_config(cfd_mode=CfdModes.NAMED, run_proxy_dns=False, provide_ingress=False)
|
||||
LOGGER.debug(config)
|
||||
config_path = write_config(tmp_path, config.full_config)
|
||||
with start_cloudflared(tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1", "--management-diagnostics"], new_process=True):
|
||||
wait_tunnel_ready(require_min_connections=1)
|
||||
cfd_cli = CloudflaredCli(config, config_path, LOGGER)
|
||||
url = cfd_cli.get_management_url("metrics", config, config_path)
|
||||
resp = send_request(url)
|
||||
|
||||
# Assert response.
|
||||
assert resp.status_code == 200, "Expected cloudflared to return 200 for /metrics"
|
||||
assert "# HELP build_info Build and version information" in resp.text, "Expected /metrics to have with the build_info details"
|
||||
|
||||
"""
|
||||
test_get_pprof_heap will verify that the /debug/pprof/heap endpoint returns a pprof/heap dump response
|
||||
"""
|
||||
def test_get_pprof_heap(self, tmp_path, component_tests_config):
|
||||
# TUN-7377 : wait_tunnel_ready does not work properly in windows.
|
||||
# Skipping this test for windows for now and will address it as part of tun-7377
|
||||
if platform.system() == "Windows":
|
||||
return
|
||||
config = component_tests_config(cfd_mode=CfdModes.NAMED, run_proxy_dns=False, provide_ingress=False)
|
||||
LOGGER.debug(config)
|
||||
config_path = write_config(tmp_path, config.full_config)
|
||||
with start_cloudflared(tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1", "--management-diagnostics"], new_process=True):
|
||||
wait_tunnel_ready(require_min_connections=1)
|
||||
cfd_cli = CloudflaredCli(config, config_path, LOGGER)
|
||||
url = cfd_cli.get_management_url("debug/pprof/heap", config, config_path)
|
||||
resp = send_request(url)
|
||||
|
||||
# Assert response.
|
||||
assert resp.status_code == 200, "Expected cloudflared to return 200 for /debug/pprof/heap"
|
||||
assert resp.headers["Content-Type"] == "application/octet-stream", "Expected /debug/pprof/heap to have return a binary response"
|
||||
|
||||
"""
|
||||
test_get_metrics_when_disabled will verify that diagnostic endpoints (such as /metrics) return 404 and are unmounted.
|
||||
"""
|
||||
def test_get_metrics_when_disabled(self, tmp_path, component_tests_config):
|
||||
# TUN-7377 : wait_tunnel_ready does not work properly in windows.
|
||||
# Skipping this test for windows for now and will address it as part of tun-7377
|
||||
if platform.system() == "Windows":
|
||||
return
|
||||
config = component_tests_config(cfd_mode=CfdModes.NAMED, run_proxy_dns=False, provide_ingress=False)
|
||||
LOGGER.debug(config)
|
||||
config_path = write_config(tmp_path, config.full_config)
|
||||
with start_cloudflared(tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1"], new_process=True):
|
||||
wait_tunnel_ready(require_min_connections=1)
|
||||
cfd_cli = CloudflaredCli(config, config_path, LOGGER)
|
||||
url = cfd_cli.get_management_url("metrics", config, config_path)
|
||||
resp = send_request(url)
|
||||
|
||||
# Assert response.
|
||||
assert resp.status_code == 404, "Expected cloudflared to return 404 for /metrics"
|
||||
|
||||
|
||||
@retry(stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000)
|
||||
def send_request(url, headers={}):
|
||||
with requests.Session() as s:
|
||||
return s.get(url, timeout=BACKOFF_SECS, headers=headers)
|
||||
@@ -12,6 +12,6 @@ class TestPostQuantum:
|
||||
def test_post_quantum(self, tmp_path, component_tests_config):
|
||||
config = component_tests_config(self._extra_config())
|
||||
LOGGER.debug(config)
|
||||
with start_cloudflared(tmp_path, config, cfd_args=["run", "--post-quantum"], new_process=True):
|
||||
with start_cloudflared(tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1"], cfd_args=["run", "--post-quantum"], new_process=True):
|
||||
wait_tunnel_ready(tunnel_url=config.get_url(),
|
||||
require_min_connections=4)
|
||||
require_min_connections=1)
|
||||
|
||||
@@ -25,7 +25,7 @@ def run_test_scenario(tmp_path, component_tests_config, cfd_mode, run_proxy_dns)
|
||||
|
||||
if cfd_mode == CfdModes.NAMED:
|
||||
expect_tunnel = True
|
||||
pre_args = ["tunnel"]
|
||||
pre_args = ["tunnel", "--ha-connections", "1"]
|
||||
args = ["run"]
|
||||
elif cfd_mode == CfdModes.PROXY_DNS:
|
||||
expect_proxy_dns = True
|
||||
|
||||
@@ -7,7 +7,7 @@ class TestQuickTunnels:
|
||||
def test_quick_tunnel(self, tmp_path, component_tests_config):
|
||||
config = component_tests_config(cfd_mode=CfdModes.QUICK, run_proxy_dns=False)
|
||||
LOGGER.debug(config)
|
||||
with start_cloudflared(tmp_path, config, cfd_args=["--hello-world"], new_process=True):
|
||||
with start_cloudflared(tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1"], cfd_args=["--hello-world"], new_process=True):
|
||||
wait_tunnel_ready(require_min_connections=1)
|
||||
url = get_quicktunnel_url()
|
||||
send_requests(url, 3, True)
|
||||
@@ -15,8 +15,8 @@ class TestQuickTunnels:
|
||||
def test_quick_tunnel_url(self, tmp_path, component_tests_config):
|
||||
config = component_tests_config(cfd_mode=CfdModes.QUICK, run_proxy_dns=False)
|
||||
LOGGER.debug(config)
|
||||
with start_cloudflared(tmp_path, config, cfd_args=["--url", f"http://localhost:{METRICS_PORT}/"], new_process=True):
|
||||
wait_tunnel_ready()
|
||||
with start_cloudflared(tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1"], cfd_args=["--url", f"http://localhost:{METRICS_PORT}/"], new_process=True):
|
||||
wait_tunnel_ready(require_min_connections=1)
|
||||
url = get_quicktunnel_url()
|
||||
send_requests(url+"/ready", 3, True)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ from util import start_cloudflared, wait_tunnel_ready, check_tunnel_not_connecte
|
||||
|
||||
@flaky(max_runs=3, min_passes=1)
|
||||
class TestReconnect:
|
||||
default_ha_conns = 4
|
||||
default_ha_conns = 1
|
||||
default_reconnect_secs = 15
|
||||
extra_config = {
|
||||
"stdin-control": True,
|
||||
@@ -29,7 +29,7 @@ class TestReconnect:
|
||||
@pytest.mark.parametrize("protocol", protocols())
|
||||
def test_named_reconnect(self, tmp_path, component_tests_config, protocol):
|
||||
config = component_tests_config(self._extra_config(protocol))
|
||||
with start_cloudflared(tmp_path, config, new_process=True, allow_input=True, capture_output=False) as cloudflared:
|
||||
with start_cloudflared(tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1"], new_process=True, allow_input=True, capture_output=False) as cloudflared:
|
||||
# Repeat the test multiple times because some issues only occur after multiple reconnects
|
||||
self.assert_reconnect(config, cloudflared, 5)
|
||||
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python
|
||||
import asyncio
|
||||
import json
|
||||
import pytest
|
||||
import requests
|
||||
import websockets
|
||||
from websockets.client import connect, WebSocketClientProtocol
|
||||
from conftest import CfdModes
|
||||
from constants import MAX_RETRIES, BACKOFF_SECS
|
||||
from retrying import retry
|
||||
from cli import CloudflaredCli
|
||||
from util import LOGGER, start_cloudflared, write_config, wait_tunnel_ready
|
||||
|
||||
class TestTail:
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_stop_streaming(self, tmp_path, component_tests_config):
|
||||
"""
|
||||
Validates that a websocket connection to management.argotunnel.com/logs can be opened
|
||||
with the access token and start and stop streaming on-demand.
|
||||
"""
|
||||
print("test_start_stop_streaming")
|
||||
config = component_tests_config(cfd_mode=CfdModes.NAMED, run_proxy_dns=False, provide_ingress=False)
|
||||
LOGGER.debug(config)
|
||||
config_path = write_config(tmp_path, config.full_config)
|
||||
with start_cloudflared(tmp_path, config, cfd_args=["run", "--hello-world"], new_process=True):
|
||||
wait_tunnel_ready(tunnel_url=config.get_url(), require_min_connections=1)
|
||||
cfd_cli = CloudflaredCli(config, config_path, LOGGER)
|
||||
url = cfd_cli.get_management_wsurl("logs", config, config_path)
|
||||
async with connect(url, open_timeout=5, close_timeout=3) as websocket:
|
||||
await websocket.send('{"type": "start_streaming"}')
|
||||
await websocket.send('{"type": "stop_streaming"}')
|
||||
await websocket.send('{"type": "start_streaming"}')
|
||||
await websocket.send('{"type": "stop_streaming"}')
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_streaming_logs(self, tmp_path, component_tests_config):
|
||||
"""
|
||||
Validates that a streaming logs connection will stream logs
|
||||
"""
|
||||
print("test_streaming_logs")
|
||||
config = component_tests_config(cfd_mode=CfdModes.NAMED, run_proxy_dns=False, provide_ingress=False)
|
||||
LOGGER.debug(config)
|
||||
config_path = write_config(tmp_path, config.full_config)
|
||||
with start_cloudflared(tmp_path, config, cfd_args=["run", "--hello-world"], new_process=True):
|
||||
wait_tunnel_ready(tunnel_url=config.get_url(), require_min_connections=1)
|
||||
cfd_cli = CloudflaredCli(config, config_path, LOGGER)
|
||||
url = cfd_cli.get_management_wsurl("logs", config, config_path)
|
||||
async with connect(url, open_timeout=5, close_timeout=5) as websocket:
|
||||
# send start_streaming
|
||||
await websocket.send('{"type": "start_streaming"}')
|
||||
# send some http requests to the tunnel to trigger some logs
|
||||
await generate_and_validate_http_events(websocket, config.get_url(), 10)
|
||||
# send stop_streaming
|
||||
await websocket.send('{"type": "stop_streaming"}')
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_streaming_logs_filters(self, tmp_path, component_tests_config):
|
||||
"""
|
||||
Validates that a streaming logs connection will stream logs
|
||||
but not http when filters applied.
|
||||
"""
|
||||
print("test_streaming_logs_filters")
|
||||
config = component_tests_config(cfd_mode=CfdModes.NAMED, run_proxy_dns=False, provide_ingress=False)
|
||||
LOGGER.debug(config)
|
||||
config_path = write_config(tmp_path, config.full_config)
|
||||
with start_cloudflared(tmp_path, config, cfd_args=["run", "--hello-world"], new_process=True):
|
||||
wait_tunnel_ready(tunnel_url=config.get_url(), require_min_connections=1)
|
||||
cfd_cli = CloudflaredCli(config, config_path, LOGGER)
|
||||
url = cfd_cli.get_management_wsurl("logs", config, config_path)
|
||||
async with connect(url, open_timeout=5, close_timeout=5) as websocket:
|
||||
# send start_streaming with tcp logs only
|
||||
await websocket.send(json.dumps({
|
||||
"type": "start_streaming",
|
||||
"filters": {
|
||||
"events": ["tcp"],
|
||||
"level": "debug"
|
||||
}
|
||||
}))
|
||||
# don't expect any http logs
|
||||
await generate_and_validate_no_log_event(websocket, config.get_url())
|
||||
# send stop_streaming
|
||||
await websocket.send('{"type": "stop_streaming"}')
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_streaming_logs_sampling(self, tmp_path, component_tests_config):
|
||||
"""
|
||||
Validates that a streaming logs connection will stream logs with sampling.
|
||||
"""
|
||||
print("test_streaming_logs_sampling")
|
||||
config = component_tests_config(cfd_mode=CfdModes.NAMED, run_proxy_dns=False, provide_ingress=False)
|
||||
LOGGER.debug(config)
|
||||
config_path = write_config(tmp_path, config.full_config)
|
||||
with start_cloudflared(tmp_path, config, cfd_args=["run", "--hello-world"], new_process=True):
|
||||
wait_tunnel_ready(tunnel_url=config.get_url(), require_min_connections=1)
|
||||
cfd_cli = CloudflaredCli(config, config_path, LOGGER)
|
||||
url = cfd_cli.get_management_wsurl("logs", config, config_path)
|
||||
async with connect(url, open_timeout=5, close_timeout=5) as websocket:
|
||||
# send start_streaming with info logs only
|
||||
await websocket.send(json.dumps({
|
||||
"type": "start_streaming",
|
||||
"filters": {
|
||||
"sampling": 0.5
|
||||
}
|
||||
}))
|
||||
# don't expect any http logs
|
||||
count = await generate_and_validate_http_events(websocket, config.get_url(), 10)
|
||||
assert count < (10 * 2) # There are typically always two log lines for http requests (request and response)
|
||||
# send stop_streaming
|
||||
await websocket.send('{"type": "stop_streaming"}')
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_streaming_logs_actor_override(self, tmp_path, component_tests_config):
|
||||
"""
|
||||
Validates that a streaming logs session can be overriden by the same actor
|
||||
"""
|
||||
print("test_streaming_logs_actor_override")
|
||||
config = component_tests_config(cfd_mode=CfdModes.NAMED, run_proxy_dns=False, provide_ingress=False)
|
||||
LOGGER.debug(config)
|
||||
config_path = write_config(tmp_path, config.full_config)
|
||||
with start_cloudflared(tmp_path, config, cfd_args=["run", "--hello-world"], new_process=True):
|
||||
wait_tunnel_ready(tunnel_url=config.get_url(), require_min_connections=1)
|
||||
cfd_cli = CloudflaredCli(config, config_path, LOGGER)
|
||||
url = cfd_cli.get_management_wsurl("logs", config, config_path)
|
||||
task = asyncio.ensure_future(start_streaming_to_be_remotely_closed(url))
|
||||
override_task = asyncio.ensure_future(start_streaming_override(url))
|
||||
await asyncio.wait([task, override_task])
|
||||
assert task.exception() == None, task.exception()
|
||||
assert override_task.exception() == None, override_task.exception()
|
||||
|
||||
async def start_streaming_to_be_remotely_closed(url):
|
||||
async with connect(url, open_timeout=5, close_timeout=5) as websocket:
|
||||
try:
|
||||
await websocket.send(json.dumps({"type": "start_streaming"}))
|
||||
await asyncio.sleep(10)
|
||||
assert websocket.closed, "expected this request to be forcibly closed by the override"
|
||||
except websockets.ConnectionClosed:
|
||||
# we expect the request to be closed
|
||||
pass
|
||||
|
||||
async def start_streaming_override(url):
|
||||
# wait for the first connection to be established
|
||||
await asyncio.sleep(1)
|
||||
async with connect(url, open_timeout=5, close_timeout=5) as websocket:
|
||||
await websocket.send(json.dumps({"type": "start_streaming"}))
|
||||
await asyncio.sleep(1)
|
||||
await websocket.send(json.dumps({"type": "stop_streaming"}))
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# Every http request has two log lines sent
|
||||
async def generate_and_validate_http_events(websocket: WebSocketClientProtocol, url: str, count_send: int):
|
||||
for i in range(count_send):
|
||||
send_request(url)
|
||||
# There are typically always two log lines for http requests (request and response)
|
||||
count = 0
|
||||
while True:
|
||||
try:
|
||||
req_line = await asyncio.wait_for(websocket.recv(), 2)
|
||||
log_line = json.loads(req_line)
|
||||
assert log_line["type"] == "logs"
|
||||
assert log_line["logs"][0]["event"] == "http"
|
||||
count += 1
|
||||
except asyncio.TimeoutError:
|
||||
# ignore timeout from waiting for recv
|
||||
break
|
||||
return count
|
||||
|
||||
# Every http request has two log lines sent
|
||||
async def generate_and_validate_no_log_event(websocket: WebSocketClientProtocol, url: str):
|
||||
send_request(url)
|
||||
try:
|
||||
# wait for 5 seconds and make sure we hit the timeout and not recv any events
|
||||
req_line = await asyncio.wait_for(websocket.recv(), 5)
|
||||
assert req_line == None, "expected no logs for the specified filters"
|
||||
except asyncio.TimeoutError:
|
||||
pass
|
||||
|
||||
@retry(stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000)
|
||||
def send_request(url, headers={}):
|
||||
with requests.Session() as s:
|
||||
resp = s.get(url, timeout=BACKOFF_SECS, headers=headers)
|
||||
assert resp.status_code == 200, f"{url} returned {resp}"
|
||||
return resp.status_code == 200
|
||||
@@ -34,7 +34,7 @@ class TestTermination:
|
||||
def test_graceful_shutdown(self, tmp_path, component_tests_config, signal, protocol):
|
||||
config = component_tests_config(self._extra_config(protocol))
|
||||
with start_cloudflared(
|
||||
tmp_path, config, new_process=True, capture_output=False) as cloudflared:
|
||||
tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1"], new_process=True, capture_output=False) as cloudflared:
|
||||
wait_tunnel_ready(tunnel_url=config.get_url())
|
||||
|
||||
connected = threading.Condition()
|
||||
@@ -56,7 +56,7 @@ class TestTermination:
|
||||
def test_shutdown_once_no_connection(self, tmp_path, component_tests_config, signal, protocol):
|
||||
config = component_tests_config(self._extra_config(protocol))
|
||||
with start_cloudflared(
|
||||
tmp_path, config, new_process=True, capture_output=False) as cloudflared:
|
||||
tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1"], new_process=True, capture_output=False) as cloudflared:
|
||||
wait_tunnel_ready(tunnel_url=config.get_url())
|
||||
|
||||
connected = threading.Condition()
|
||||
@@ -76,7 +76,7 @@ class TestTermination:
|
||||
def test_no_connection_shutdown(self, tmp_path, component_tests_config, signal, protocol):
|
||||
config = component_tests_config(self._extra_config(protocol))
|
||||
with start_cloudflared(
|
||||
tmp_path, config, new_process=True, capture_output=False) as cloudflared:
|
||||
tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1"], new_process=True, capture_output=False) as cloudflared:
|
||||
wait_tunnel_ready(tunnel_url=config.get_url())
|
||||
with self.within_grace_period():
|
||||
self.terminate_by_signal(cloudflared, signal)
|
||||
|
||||
@@ -3,7 +3,9 @@ import requests
|
||||
from conftest import CfdModes
|
||||
from constants import METRICS_PORT, MAX_RETRIES, BACKOFF_SECS
|
||||
from retrying import retry
|
||||
from util import LOGGER, start_cloudflared, wait_tunnel_ready, send_requests
|
||||
from cli import CloudflaredCli
|
||||
from util import LOGGER, write_config, start_cloudflared, wait_tunnel_ready, send_requests
|
||||
import platform
|
||||
|
||||
class TestTunnel:
|
||||
'''Test tunnels with no ingress rules from config.yaml but ingress rules from CLI only'''
|
||||
@@ -11,15 +13,15 @@ class TestTunnel:
|
||||
def test_tunnel_hello_world(self, tmp_path, component_tests_config):
|
||||
config = component_tests_config(cfd_mode=CfdModes.NAMED, run_proxy_dns=False, provide_ingress=False)
|
||||
LOGGER.debug(config)
|
||||
with start_cloudflared(tmp_path, config, cfd_args=["run", "--hello-world"], new_process=True):
|
||||
with start_cloudflared(tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1"], cfd_args=["run", "--hello-world"], new_process=True):
|
||||
wait_tunnel_ready(tunnel_url=config.get_url(),
|
||||
require_min_connections=4)
|
||||
require_min_connections=1)
|
||||
|
||||
def test_tunnel_url(self, tmp_path, component_tests_config):
|
||||
config = component_tests_config(cfd_mode=CfdModes.NAMED, run_proxy_dns=False, provide_ingress=False)
|
||||
LOGGER.debug(config)
|
||||
with start_cloudflared(tmp_path, config, cfd_args=["run", "--url", f"http://localhost:{METRICS_PORT}/"], new_process=True):
|
||||
wait_tunnel_ready(require_min_connections=4)
|
||||
with start_cloudflared(tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1"], cfd_args=["run", "--url", f"http://localhost:{METRICS_PORT}/"], new_process=True):
|
||||
wait_tunnel_ready(require_min_connections=1)
|
||||
send_requests(config.get_url()+"/ready", 3, True)
|
||||
|
||||
def test_tunnel_no_ingress(self, tmp_path, component_tests_config):
|
||||
@@ -29,8 +31,8 @@ class TestTunnel:
|
||||
'''
|
||||
config = component_tests_config(cfd_mode=CfdModes.NAMED, run_proxy_dns=False, provide_ingress=False)
|
||||
LOGGER.debug(config)
|
||||
with start_cloudflared(tmp_path, config, cfd_args=["run"], new_process=True):
|
||||
wait_tunnel_ready(require_min_connections=4)
|
||||
with start_cloudflared(tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1"], cfd_args=["run"], new_process=True):
|
||||
wait_tunnel_ready(require_min_connections=1)
|
||||
resp = send_request(config.get_url()+"/")
|
||||
assert resp.status_code == 503, "Expected cloudflared to return 503 for all requests with no ingress defined"
|
||||
resp = send_request(config.get_url()+"/test")
|
||||
@@ -38,6 +40,6 @@ class TestTunnel:
|
||||
|
||||
|
||||
@retry(stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000)
|
||||
def send_request(url):
|
||||
def send_request(url, headers={}):
|
||||
with requests.Session() as s:
|
||||
return s.get(url, timeout=BACKOFF_SECS)
|
||||
return s.get(url, timeout=BACKOFF_SECS, headers=headers)
|
||||
|
||||
@@ -9,6 +9,7 @@ import pytest
|
||||
|
||||
import requests
|
||||
import yaml
|
||||
import json
|
||||
from retrying import retry
|
||||
|
||||
from constants import METRICS_PORT, MAX_RETRIES, BACKOFF_SECS
|
||||
@@ -48,7 +49,6 @@ def start_cloudflared(directory, config, cfd_args=["run"], cfd_pre_args=["tunnel
|
||||
# By setting check=True, it will raise an exception if the process exits with non-zero exit code
|
||||
return subprocess.run(cmd, check=expect_success, capture_output=capture_output)
|
||||
|
||||
|
||||
def cloudflared_cmd(config, config_path, cfd_args, cfd_pre_args, root):
|
||||
cmd = []
|
||||
if root:
|
||||
@@ -106,13 +106,14 @@ def inner_wait_tunnel_ready(tunnel_url=None, require_min_connections=1):
|
||||
with requests.Session() as s:
|
||||
resp = send_request(s, metrics_url, True)
|
||||
|
||||
assert resp.json()["readyConnections"] >= require_min_connections, \
|
||||
ready_connections = resp.json()["readyConnections"]
|
||||
|
||||
assert ready_connections >= require_min_connections, \
|
||||
f"Ready endpoint returned {resp.json()} but we expect at least {require_min_connections} connections"
|
||||
|
||||
if tunnel_url is not None:
|
||||
send_request(s, tunnel_url, True)
|
||||
|
||||
|
||||
def _log_cloudflared_logs(cfd_logs):
|
||||
log_file = cfd_logs
|
||||
if os.path.isdir(cfd_logs):
|
||||
|
||||
@@ -157,14 +157,16 @@ type ReadWriteAcker interface {
|
||||
type HTTPResponseReadWriteAcker struct {
|
||||
r io.Reader
|
||||
w ResponseWriter
|
||||
f http.Flusher
|
||||
req *http.Request
|
||||
}
|
||||
|
||||
// NewHTTPResponseReadWriterAcker returns a new instance of HTTPResponseReadWriteAcker.
|
||||
func NewHTTPResponseReadWriterAcker(w ResponseWriter, req *http.Request) *HTTPResponseReadWriteAcker {
|
||||
func NewHTTPResponseReadWriterAcker(w ResponseWriter, flusher http.Flusher, req *http.Request) *HTTPResponseReadWriteAcker {
|
||||
return &HTTPResponseReadWriteAcker{
|
||||
r: req.Body,
|
||||
w: w,
|
||||
f: flusher,
|
||||
req: req,
|
||||
}
|
||||
}
|
||||
@@ -174,7 +176,11 @@ func (h *HTTPResponseReadWriteAcker) Read(p []byte) (int, error) {
|
||||
}
|
||||
|
||||
func (h *HTTPResponseReadWriteAcker) Write(p []byte) (int, error) {
|
||||
return h.w.Write(p)
|
||||
n, err := h.w.Write(p)
|
||||
if n > 0 {
|
||||
h.f.Flush()
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// AckConnection acks an HTTP connection by sending a switch protocols status code that enables the caller to
|
||||
|
||||
@@ -130,7 +130,8 @@ func wsEchoEndpoint(w ResponseWriter, r *http.Request) error {
|
||||
}
|
||||
wsCtx, cancel := context.WithCancel(r.Context())
|
||||
readPipe, writePipe := io.Pipe()
|
||||
wsConn := websocket.NewConn(wsCtx, NewHTTPResponseReadWriterAcker(w, r), &log)
|
||||
|
||||
wsConn := websocket.NewConn(wsCtx, NewHTTPResponseReadWriterAcker(w, w.(http.Flusher), r), &log)
|
||||
go func() {
|
||||
select {
|
||||
case <-wsCtx.Done():
|
||||
@@ -175,7 +176,7 @@ func wsFlakyEndpoint(w ResponseWriter, r *http.Request) error {
|
||||
}
|
||||
wsCtx, cancel := context.WithCancel(r.Context())
|
||||
|
||||
wsConn := websocket.NewConn(wsCtx, NewHTTPResponseReadWriterAcker(w, r), &log)
|
||||
wsConn := websocket.NewConn(wsCtx, NewHTTPResponseReadWriterAcker(w, w.(http.Flusher), r), &log)
|
||||
|
||||
closedAfter := time.Millisecond * time.Duration(rand.Intn(50))
|
||||
originConn := &flakyConn{closeAt: time.Now().Add(closedAfter)}
|
||||
|
||||
+5
-1
@@ -142,7 +142,7 @@ func (c *HTTP2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
break
|
||||
}
|
||||
|
||||
rws := NewHTTPResponseReadWriterAcker(respWriter, r)
|
||||
rws := NewHTTPResponseReadWriterAcker(respWriter, respWriter, r)
|
||||
requestErr = originProxy.ProxyTCP(r.Context(), rws, &TCPRequest{
|
||||
Dest: host,
|
||||
CFRay: FindCfRayHeader(r),
|
||||
@@ -289,6 +289,10 @@ func (rp *http2RespWriter) Header() http.Header {
|
||||
return rp.respHeaders
|
||||
}
|
||||
|
||||
func (rp *http2RespWriter) Flush() {
|
||||
rp.flusher.Flush()
|
||||
}
|
||||
|
||||
func (rp *http2RespWriter) WriteHeader(status int) {
|
||||
if rp.hijacked() {
|
||||
rp.log.Warn().Msg("WriteHeader after hijack")
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -84,7 +83,7 @@ func TestHTTP2ConfigurationSet(t *testing.T) {
|
||||
resp, err := edgeHTTP2Conn.RoundTrip(req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
bdy, err := ioutil.ReadAll(resp.Body)
|
||||
bdy, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, `{"lastAppliedVersion":2,"err":null}`, string(bdy))
|
||||
cancel()
|
||||
@@ -149,7 +148,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.expectedStatus, resp.StatusCode)
|
||||
if test.expectedBody != nil {
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.expectedBody, respBody)
|
||||
}
|
||||
@@ -546,7 +545,7 @@ func benchmarkServeHTTP(b *testing.B, test testRequest) {
|
||||
require.NoError(b, err)
|
||||
require.Equal(b, test.expectedStatus, resp.StatusCode)
|
||||
if test.expectedBody != nil {
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
require.NoError(b, err)
|
||||
require.Equal(b, test.expectedBody, respBody)
|
||||
}
|
||||
|
||||
+41
-9
@@ -16,8 +16,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/rs/zerolog"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
|
||||
"github.com/cloudflare/cloudflared/datagramsession"
|
||||
"github.com/cloudflare/cloudflared/ingress"
|
||||
"github.com/cloudflare/cloudflared/management"
|
||||
"github.com/cloudflare/cloudflared/packet"
|
||||
quicpogs "github.com/cloudflare/cloudflared/quic"
|
||||
"github.com/cloudflare/cloudflared/tracing"
|
||||
@@ -62,10 +63,13 @@ type QUICConnection struct {
|
||||
controlStreamHandler ControlStreamHandler
|
||||
connOptions *tunnelpogs.ConnectionOptions
|
||||
connIndex uint8
|
||||
|
||||
udpUnregisterTimeout time.Duration
|
||||
}
|
||||
|
||||
// NewQUICConnection returns a new instance of QUICConnection.
|
||||
func NewQUICConnection(
|
||||
ctx context.Context,
|
||||
quicConfig *quic.Config,
|
||||
edgeAddr net.Addr,
|
||||
localAddr net.IP,
|
||||
@@ -76,13 +80,14 @@ func NewQUICConnection(
|
||||
controlStreamHandler ControlStreamHandler,
|
||||
logger *zerolog.Logger,
|
||||
packetRouterConfig *ingress.GlobalRouterConfig,
|
||||
udpUnregisterTimeout time.Duration,
|
||||
) (*QUICConnection, error) {
|
||||
udpConn, err := createUDPConnForConnIndex(connIndex, localAddr, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
session, err := quic.Dial(udpConn, edgeAddr, edgeAddr.String(), tlsConfig, quicConfig)
|
||||
session, err := quic.Dial(ctx, udpConn, edgeAddr, tlsConfig, quicConfig)
|
||||
if err != nil {
|
||||
// close the udp server socket in case of error connecting to the edge
|
||||
udpConn.Close()
|
||||
@@ -110,6 +115,7 @@ func NewQUICConnection(
|
||||
controlStreamHandler: controlStreamHandler,
|
||||
connOptions: connOptions,
|
||||
connIndex: connIndex,
|
||||
udpUnregisterTimeout: udpUnregisterTimeout,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -298,11 +304,12 @@ func (q *QUICConnection) RegisterUdpSession(ctx context.Context, sessionID uuid.
|
||||
attribute.String("session-id", sessionID.String()),
|
||||
attribute.String("dst", fmt.Sprintf("%s:%d", dstIP, dstPort)),
|
||||
))
|
||||
log := q.logger.With().Int(management.EventTypeKey, int(management.UDP)).Logger()
|
||||
// Each session is a series of datagram from an eyeball to a dstIP:dstPort.
|
||||
// (src port, dst IP, dst port) uniquely identifies a session, so it needs a dedicated connected socket.
|
||||
originProxy, err := ingress.DialUDP(dstIP, dstPort)
|
||||
if err != nil {
|
||||
q.logger.Err(err).Msgf("Failed to create udp proxy to %s:%d", dstIP, dstPort)
|
||||
log.Err(err).Msgf("Failed to create udp proxy to %s:%d", dstIP, dstPort)
|
||||
tracing.EndWithErrorStatus(registerSpan, err)
|
||||
return nil, err
|
||||
}
|
||||
@@ -313,14 +320,18 @@ func (q *QUICConnection) RegisterUdpSession(ctx context.Context, sessionID uuid.
|
||||
|
||||
session, err := q.sessionManager.RegisterSession(ctx, sessionID, originProxy)
|
||||
if err != nil {
|
||||
q.logger.Err(err).Str("sessionID", sessionID.String()).Msgf("Failed to register udp session")
|
||||
log.Err(err).Str("sessionID", sessionID.String()).Msgf("Failed to register udp session")
|
||||
tracing.EndWithErrorStatus(registerSpan, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go q.serveUDPSession(session, closeAfterIdleHint)
|
||||
|
||||
q.logger.Debug().Str("sessionID", sessionID.String()).Str("src", originProxy.LocalAddr().String()).Str("dst", fmt.Sprintf("%s:%d", dstIP, dstPort)).Msgf("Registered session")
|
||||
log.Debug().
|
||||
Str("sessionID", sessionID.String()).
|
||||
Str("src", originProxy.LocalAddr().String()).
|
||||
Str("dst", fmt.Sprintf("%s:%d", dstIP, dstPort)).
|
||||
Msgf("Registered session")
|
||||
tracing.End(registerSpan)
|
||||
|
||||
resp := tunnelpogs.RegisterUdpSessionResponse{
|
||||
@@ -341,21 +352,29 @@ func (q *QUICConnection) serveUDPSession(session *datagramsession.Session, close
|
||||
q.closeUDPSession(ctx, session.ID, "terminated without error")
|
||||
}
|
||||
}
|
||||
q.logger.Debug().Err(err).Str("sessionID", session.ID.String()).Msg("Session terminated")
|
||||
q.logger.Debug().Err(err).
|
||||
Int(management.EventTypeKey, int(management.UDP)).
|
||||
Str("sessionID", session.ID.String()).
|
||||
Msg("Session terminated")
|
||||
}
|
||||
|
||||
// closeUDPSession first unregisters the session from session manager, then it tries to unregister from edge
|
||||
func (q *QUICConnection) closeUDPSession(ctx context.Context, sessionID uuid.UUID, message string) {
|
||||
q.sessionManager.UnregisterSession(ctx, sessionID, message, false)
|
||||
stream, err := q.session.OpenStream()
|
||||
quicStream, err := q.session.OpenStream()
|
||||
if err != nil {
|
||||
// Log this at debug because this is not an error if session was closed due to lost connection
|
||||
// with edge
|
||||
q.logger.Debug().Err(err).Str("sessionID", sessionID.String()).
|
||||
q.logger.Debug().Err(err).
|
||||
Int(management.EventTypeKey, int(management.UDP)).
|
||||
Str("sessionID", sessionID.String()).
|
||||
Msgf("Failed to open quic stream to unregister udp session with edge")
|
||||
return
|
||||
}
|
||||
rpcClientStream, err := quicpogs.NewRPCClientStream(ctx, stream, q.logger)
|
||||
|
||||
stream := quicpogs.NewSafeStreamCloser(quicStream)
|
||||
defer stream.Close()
|
||||
rpcClientStream, err := quicpogs.NewRPCClientStream(ctx, stream, q.udpUnregisterTimeout, q.logger)
|
||||
if err != nil {
|
||||
// Log this at debug because this is not an error if session was closed due to lost connection
|
||||
// with edge
|
||||
@@ -363,6 +382,8 @@ func (q *QUICConnection) closeUDPSession(ctx context.Context, sessionID uuid.UUI
|
||||
Msgf("Failed to open rpc stream to unregister udp session with edge")
|
||||
return
|
||||
}
|
||||
defer rpcClientStream.Close()
|
||||
|
||||
if err := rpcClientStream.UnregisterUdpSession(ctx, sessionID, message); err != nil {
|
||||
q.logger.Err(err).Str("sessionID", sessionID.String()).
|
||||
Msgf("Failed to unregister udp session with edge")
|
||||
@@ -428,10 +449,21 @@ func (hrw *httpResponseAdapter) WriteRespHeaders(status int, header http.Header)
|
||||
return hrw.WriteConnectResponseData(nil, metadata...)
|
||||
}
|
||||
|
||||
func (hrw *httpResponseAdapter) Write(p []byte) (int, error) {
|
||||
// Make sure to send WriteHeader response if not called yet
|
||||
if !hrw.connectResponseSent {
|
||||
hrw.WriteRespHeaders(http.StatusOK, hrw.headers)
|
||||
}
|
||||
return hrw.RequestServerStream.Write(p)
|
||||
}
|
||||
|
||||
func (hrw *httpResponseAdapter) Header() http.Header {
|
||||
return hrw.headers
|
||||
}
|
||||
|
||||
// This is a no-op Flush because this adapter is over a quic.Stream and we don't need Flush here.
|
||||
func (hrw *httpResponseAdapter) Flush() {}
|
||||
|
||||
func (hrw *httpResponseAdapter) WriteHeader(status int) {
|
||||
hrw.WriteRespHeaders(status, hrw.headers)
|
||||
}
|
||||
|
||||
+16
-14
@@ -16,8 +16,8 @@ import (
|
||||
|
||||
"github.com/gobwas/ws/wsutil"
|
||||
"github.com/google/uuid"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -32,9 +32,8 @@ import (
|
||||
var (
|
||||
testTLSServerConfig = quicpogs.GenerateTLSConfig()
|
||||
testQUICConfig = &quic.Config{
|
||||
ConnectionIDLength: 16,
|
||||
KeepAlivePeriod: 5 * time.Second,
|
||||
EnableDatagrams: true,
|
||||
KeepAlivePeriod: 5 * time.Second,
|
||||
EnableDatagrams: true,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -43,13 +42,6 @@ var _ ReadWriteAcker = (*streamReadWriteAcker)(nil)
|
||||
// TestQUICServer tests if a quic server accepts and responds to a quic client with the acceptance protocol.
|
||||
// It also serves as a demonstration for communication with the QUIC connection started by a cloudflared.
|
||||
func TestQUICServer(t *testing.T) {
|
||||
// Start a UDP Listener for QUIC.
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
udpListener, err := net.ListenUDP(udpAddr.Network(), udpAddr)
|
||||
require.NoError(t, err)
|
||||
defer udpListener.Close()
|
||||
|
||||
// This is simply a sample websocket frame message.
|
||||
wsBuf := &bytes.Buffer{}
|
||||
wsutil.WriteClientBinary(wsBuf, []byte("Hello"))
|
||||
@@ -145,8 +137,14 @@ func TestQUICServer(t *testing.T) {
|
||||
test := test // capture range variable
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
quicListener, err := quic.Listen(udpListener, testTLSServerConfig, testQUICConfig)
|
||||
// Start a UDP Listener for QUIC.
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
udpListener, err := net.ListenUDP(udpAddr.Network(), udpAddr)
|
||||
require.NoError(t, err)
|
||||
defer udpListener.Close()
|
||||
quicTransport := &quic.Transport{Conn: udpListener, ConnectionIDLength: 16}
|
||||
quicListener, err := quicTransport.Listen(testTLSServerConfig, testQUICConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
serverDone := make(chan struct{})
|
||||
@@ -187,7 +185,7 @@ func (fakeControlStream) IsStopped() bool {
|
||||
func quicServer(
|
||||
ctx context.Context,
|
||||
t *testing.T,
|
||||
listener quic.Listener,
|
||||
listener *quic.Listener,
|
||||
dest string,
|
||||
connectionType quicpogs.ConnectionType,
|
||||
metadata []quicpogs.Metadata,
|
||||
@@ -713,7 +711,10 @@ func testQUICConnection(udpListenerAddr net.Addr, t *testing.T, index uint8) *QU
|
||||
}
|
||||
// Start a mock httpProxy
|
||||
log := zerolog.New(os.Stdout)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
qc, err := NewQUICConnection(
|
||||
ctx,
|
||||
testQUICConfig,
|
||||
udpListenerAddr,
|
||||
nil,
|
||||
@@ -724,6 +725,7 @@ func testQUICConnection(udpListenerAddr net.Addr, t *testing.T, index uint8) *QU
|
||||
fakeControlStream{},
|
||||
&log,
|
||||
nil,
|
||||
5*time.Second,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
return qc
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"github.com/cloudflare/cloudflared/management"
|
||||
"github.com/cloudflare/cloudflared/packet"
|
||||
)
|
||||
|
||||
@@ -91,7 +92,10 @@ func (m *manager) shutdownSessions(err error) {
|
||||
byRemote: true,
|
||||
}
|
||||
for _, s := range m.sessions {
|
||||
s.close(closeSessionErr)
|
||||
m.unregisterSession(&unregisterSessionEvent{
|
||||
sessionID: s.ID,
|
||||
err: closeSessionErr,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,10 +121,13 @@ func (m *manager) registerSession(ctx context.Context, registration *registerSes
|
||||
session := m.newSession(registration.sessionID, registration.originProxy)
|
||||
m.sessions[registration.sessionID] = session
|
||||
registration.resultChan <- session
|
||||
incrementUDPSessions()
|
||||
}
|
||||
|
||||
func (m *manager) newSession(id uuid.UUID, dstConn io.ReadWriteCloser) *Session {
|
||||
logger := m.log.With().Str("sessionID", id.String()).Logger()
|
||||
logger := m.log.With().
|
||||
Int(management.EventTypeKey, int(management.UDP)).
|
||||
Str("sessionID", id.String()).Logger()
|
||||
return &Session{
|
||||
ID: id,
|
||||
sendFunc: m.sendFunc,
|
||||
@@ -160,6 +167,7 @@ func (m *manager) unregisterSession(unregistration *unregisterSessionEvent) {
|
||||
if ok {
|
||||
delete(m.sessions, unregistration.sessionID)
|
||||
session.close(unregistration.err)
|
||||
decrementUDPActiveSessions()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package datagramsession
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
namespace = "cloudflared"
|
||||
)
|
||||
|
||||
var (
|
||||
activeUDPSessions = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: "udp",
|
||||
Name: "active_sessions",
|
||||
Help: "Concurrent count of UDP sessions that are being proxied to any origin",
|
||||
})
|
||||
totalUDPSessions = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: "udp",
|
||||
Name: "total_sessions",
|
||||
Help: "Total count of UDP sessions that have been proxied to any origin",
|
||||
})
|
||||
)
|
||||
|
||||
func init() {
|
||||
prometheus.MustRegister(
|
||||
activeUDPSessions,
|
||||
totalUDPSessions,
|
||||
)
|
||||
}
|
||||
|
||||
func incrementUDPSessions() {
|
||||
totalUDPSessions.Inc()
|
||||
activeUDPSessions.Inc()
|
||||
}
|
||||
|
||||
func decrementUDPActiveSessions() {
|
||||
activeUDPSessions.Dec()
|
||||
}
|
||||
@@ -51,7 +51,7 @@ type Session struct {
|
||||
|
||||
func (s *Session) Serve(ctx context.Context, closeAfterIdle time.Duration) (closedByRemote bool, err error) {
|
||||
go func() {
|
||||
// QUIC implementation copies data to another buffer before returning https://github.com/lucas-clemente/quic-go/blob/v0.24.0/session.go#L1967-L1975
|
||||
// QUIC implementation copies data to another buffer before returning https://github.com/quic-go/quic-go/blob/v0.24.0/session.go#L1967-L1975
|
||||
// This makes it safe to share readBuffer between iterations
|
||||
const maxPacketSize = 1500
|
||||
readBuffer := make([]byte, maxPacketSize)
|
||||
|
||||
@@ -166,6 +166,17 @@ def parse_args():
|
||||
|
||||
def upload_asset(release, filepath, filename, release_version, kv_account_id, namespace_id, kv_api_token):
|
||||
logging.info("Uploading asset: %s", filename)
|
||||
assets = release.get_assets()
|
||||
uploaded = False
|
||||
for asset in assets:
|
||||
if asset.name == filename:
|
||||
uploaded = True
|
||||
break
|
||||
|
||||
if uploaded:
|
||||
logging.info("asset already uploaded, skipping upload")
|
||||
return
|
||||
|
||||
release.upload_asset(filepath, name=filename)
|
||||
|
||||
# check and extract if the file is a tar and gzipped file (as is the case with the macos builds)
|
||||
@@ -182,9 +193,11 @@ def upload_asset(release, filepath, filename, release_version, kv_account_id, na
|
||||
binary_path = os.path.join(os.getcwd(), 'cfd', 'cloudflared')
|
||||
|
||||
# send the sha256 (the checksum) to workers kv
|
||||
logging.info("Uploading sha256 checksum for: %s", filename)
|
||||
pkg_hash = get_sha256(binary_path)
|
||||
send_hash(pkg_hash, filename, release_version, kv_account_id, namespace_id, kv_api_token)
|
||||
|
||||
def move_asset(filepath, filename):
|
||||
# create the artifacts directory if it doesn't exist
|
||||
artifact_path = os.path.join(os.getcwd(), 'artifacts')
|
||||
if not os.path.isdir(artifact_path):
|
||||
@@ -215,6 +228,7 @@ def main():
|
||||
binary_path = os.path.join(args.path, filename)
|
||||
upload_asset(release, binary_path, filename, args.release_version, args.kv_account_id, args.namespace_id,
|
||||
args.kv_api_token)
|
||||
move_asset(binary_path, filename)
|
||||
else:
|
||||
upload_asset(release, args.path, args.name, args.release_version, args.kv_account_id, args.namespace_id,
|
||||
args.kv_api_token)
|
||||
|
||||
@@ -3,29 +3,29 @@ module github.com/cloudflare/cloudflared
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93
|
||||
github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc
|
||||
github.com/coredns/coredns v1.10.0
|
||||
github.com/coreos/go-oidc/v3 v3.4.0
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
||||
github.com/coreos/go-oidc/v3 v3.6.0
|
||||
github.com/coreos/go-systemd/v22 v22.5.0
|
||||
github.com/facebookgo/grace v0.0.0-20180706040059-75cf19382434
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/getsentry/raven-go v0.2.0
|
||||
github.com/getsentry/sentry-go v0.16.0
|
||||
github.com/go-chi/chi/v5 v5.0.8
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-jose/go-jose/v3 v3.0.0
|
||||
github.com/gobwas/ws v1.0.4
|
||||
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
|
||||
github.com/google/gopacket v1.1.19
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/lucas-clemente/quic-go v0.28.1
|
||||
github.com/mattn/go-colorable v0.1.13
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.13.0
|
||||
github.com/prometheus/client_model v0.2.0
|
||||
github.com/quic-go/quic-go v0.0.0-00010101000000-000000000000
|
||||
github.com/rs/zerolog v1.20.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/urfave/cli/v2 v2.3.0
|
||||
@@ -36,15 +36,13 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.6.3
|
||||
go.opentelemetry.io/proto/otlp v0.15.0
|
||||
go.uber.org/automaxprocs v1.4.0
|
||||
golang.org/x/crypto v0.5.0
|
||||
golang.org/x/net v0.7.0
|
||||
golang.org/x/crypto v0.8.0
|
||||
golang.org/x/net v0.9.0
|
||||
golang.org/x/sync v0.1.0
|
||||
golang.org/x/sys v0.7.0
|
||||
golang.org/x/term v0.7.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
gopkg.in/coreos/go-oidc.v2 v2.2.1
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
nhooyr.io/websocket v1.8.7
|
||||
zombiezen.com/go/capnproto2 v2.18.0+incompatible
|
||||
@@ -54,9 +52,7 @@ require (
|
||||
github.com/BurntSushi/toml v1.2.0 // indirect
|
||||
github.com/apparentlymart/go-cidr v1.1.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cheekybits/genny v1.0.0 // indirect
|
||||
github.com/cloudflare/circl v1.2.1-0.20220809205628-0a9554f37a47 // indirect
|
||||
github.com/coredns/caddy v1.1.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
@@ -71,57 +67,51 @@ require (
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/gobwas/httphead v0.0.0-20200921212729-da3d93bc3c58 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
||||
github.com/klauspost/compress v1.15.11 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.4.0 // indirect
|
||||
github.com/onsi/gomega v1.23.0 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/oauth2 v0.4.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
golang.org/x/oauth2 v0.6.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd // indirect
|
||||
google.golang.org/grpc v1.51.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/urfave/cli/v2 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d
|
||||
|
||||
replace github.com/lucas-clemente/quic-go => github.com/chungthuang/quic-go v0.27.1-0.20220809135021-ca330f1dec9f
|
||||
|
||||
// Avoid 'CVE-2022-21698'
|
||||
replace github.com/prometheus/golang_client => github.com/prometheus/golang_client v1.12.1
|
||||
|
||||
replace gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1
|
||||
|
||||
replace github.com/quic-go/quic-go => github.com/devincarr/quic-go v0.0.0-20230502200822-d1f4edacbee7
|
||||
|
||||
// Post-quantum tunnel RTG-1339
|
||||
replace (
|
||||
// Branches go1.18 go1.19 go1.20 on github.com/cloudflare/qtls-pq
|
||||
github.com/marten-seemann/qtls-go1-18 => github.com/cloudflare/qtls-pq v0.0.0-20230103171413-e7a2fb559a0e
|
||||
github.com/marten-seemann/qtls-go1-19 => github.com/cloudflare/qtls-pq v0.0.0-20230103171656-05e84f90909e
|
||||
github.com/marten-seemann/qtls-go1-20 => github.com/cloudflare/qtls-pq v0.0.0-20230215110727-8b4e1699c2a8
|
||||
github.com/quic-go/qtls-go1-18 => github.com/cloudflare/qtls-pq v0.0.0-20230103171413-e7a2fb559a0e
|
||||
github.com/quic-go/qtls-go1-19 => github.com/cloudflare/qtls-pq v0.0.0-20230103171656-05e84f90909e
|
||||
github.com/quic-go/qtls-go1-20 => github.com/cloudflare/qtls-pq v0.0.0-20230215110727-8b4e1699c2a8
|
||||
// Branches go1.19 go1.20 on github.com/cloudflare/qtls-pq
|
||||
github.com/quic-go/qtls-go1-19 => github.com/cloudflare/qtls-pq v0.0.0-20230320123031-3faac1a945b2
|
||||
github.com/quic-go/qtls-go1-20 => github.com/cloudflare/qtls-pq v0.0.0-20230320122459-4ed280d0d633
|
||||
)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
@@ -15,36 +13,14 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
||||
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
||||
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
||||
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
|
||||
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
|
||||
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
|
||||
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
|
||||
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
|
||||
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
|
||||
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
|
||||
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
|
||||
cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
|
||||
cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
|
||||
cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
|
||||
cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
|
||||
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
|
||||
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
@@ -54,13 +30,7 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
|
||||
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
|
||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
|
||||
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
@@ -71,7 +41,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU=
|
||||
github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
|
||||
@@ -79,54 +48,41 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/bwesterb/go-ristretto v1.2.2/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV47EEXL8mWmRdEfGscSJ+7EgePNgt0s=
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/chungthuang/quic-go v0.27.1-0.20220809135021-ca330f1dec9f h1:UWC3XjwZzocdNCzzXxq9j/1SdHMZXhcTOsh/+gNRBUQ=
|
||||
github.com/chungthuang/quic-go v0.27.1-0.20220809135021-ca330f1dec9f/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93 h1:QrGfkZDnMxcWHaYDdB7CmqS9i26OAnUj/xcus/abYkY=
|
||||
github.com/cloudflare/brotli-go v0.0.0-20191101163834-d34379f7ff93/go.mod h1:QiTe66jFdP7cUKMCCf/WrvDyYdtdmdZfVcdoLbzaKVY=
|
||||
github.com/cloudflare/circl v1.2.1-0.20220809205628-0a9554f37a47 h1:YzpECHxZ9TzO7LpnKmPxItSd79lLgrR5heIlnqU4dTU=
|
||||
github.com/cloudflare/circl v1.2.1-0.20220809205628-0a9554f37a47/go.mod h1:qhx8gBILsYlbam7h09SvHDSkjpe3TfLA7b/z4rxJvkE=
|
||||
github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc h1:Dvk3ySBsOm5EviLx6VCyILnafPcQinXGP5jbTdHUJgE=
|
||||
github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc/go.mod h1:HlgKKR8V5a1wroIDDIz3/A+T+9Janfq+7n1P5sEFdi0=
|
||||
github.com/cloudflare/qtls-pq v0.0.0-20230103171413-e7a2fb559a0e h1:frfo+L0qloEb6Vj+qjS4pbAYSJQZAlUnKZu0uJoErac=
|
||||
github.com/cloudflare/qtls-pq v0.0.0-20230103171413-e7a2fb559a0e/go.mod h1:mW0BgKFFDAiSmOdUwoORtjo0V2vqw5QzVYRtKQqw/Jg=
|
||||
github.com/cloudflare/qtls-pq v0.0.0-20230103171656-05e84f90909e h1:RtQDXvDi0PK3EonP0v7zkE5/rApK4MsgRATCdD+ughg=
|
||||
github.com/cloudflare/qtls-pq v0.0.0-20230103171656-05e84f90909e/go.mod h1:aIsWqC0WXyUiUxBl/RfxAjDyWE9CCLqvSMnCMTd/+bc=
|
||||
github.com/cloudflare/qtls-pq v0.0.0-20230320122459-4ed280d0d633 h1:ZTub2XMOBpxyBiJf6Q+UKqAi07yt1rZmFitriHvFd8M=
|
||||
github.com/cloudflare/qtls-pq v0.0.0-20230320122459-4ed280d0d633/go.mod h1:j/igSUc4PgBMayIsBGjAFu2i7g663rm6kZrKy4htb7E=
|
||||
github.com/cloudflare/qtls-pq v0.0.0-20230320123031-3faac1a945b2 h1:0/KuLjh9lBMiXlooAdwoo+FbLVD5DABtquB0ImEFOK0=
|
||||
github.com/cloudflare/qtls-pq v0.0.0-20230320123031-3faac1a945b2/go.mod h1:XzuZIjv4mF5cM205RHHW1d60PQtWGwMR6jx38YKuYHs=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0=
|
||||
github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4=
|
||||
github.com/coredns/coredns v1.10.0 h1:jCfuWsBjTs0dapkkhISfPCzn5LqvSRtrFtaf/Tjj4DI=
|
||||
github.com/coredns/coredns v1.10.0/go.mod h1:CIfRU5TgpuoIiJBJ4XrofQzfFQpPFh32ERpUevrSlaw=
|
||||
github.com/coreos/go-oidc/v3 v3.4.0 h1:xz7elHb/LDwm/ERpwHd+5nb7wFHL32rsr6bBOgaeu6g=
|
||||
github.com/coreos/go-oidc/v3 v3.4.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye6h3zkD2Pw=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-oidc/v3 v3.6.0 h1:AKVxfYw1Gmkn/w96z0DbT/B/xFnzTd3MkZvWLjF4n/o=
|
||||
github.com/coreos/go-oidc/v3 v3.6.0/go.mod h1:ZpHUsHBucTUj6WOkrP4E20UPynbLZzhTQ1XKCXkxyPc=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
@@ -134,16 +90,14 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/devincarr/quic-go v0.0.0-20230502200822-d1f4edacbee7 h1:qxyoKKPXmPsbvT7SZTcvhEgUaZhEttk4f6u8rIawKj0=
|
||||
github.com/devincarr/quic-go v0.0.0-20230502200822-d1f4edacbee7/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
|
||||
@@ -157,12 +111,8 @@ github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQD
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
|
||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||
github.com/getsentry/sentry-go v0.16.0 h1:owk+S+5XcgJLlGR/3+3s6N4d+uKwqYvh/eS0AIMjPWo=
|
||||
github.com/getsentry/sentry-go v0.16.0/go.mod h1:ZXCloQLj0pG7mja5NK6NPf2V4A88YJ4pNlc2mOHwh6Y=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
@@ -170,14 +120,16 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
||||
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
@@ -211,6 +163,7 @@ github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/E
|
||||
github.com/gobwas/ws v1.0.4 h1:5eXU1CZhpQdq5kXbKb+sECH5Ia5KiO6CYzIzdlVx6Bs=
|
||||
github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4=
|
||||
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU=
|
||||
@@ -220,7 +173,6 @@ github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0L
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
@@ -228,7 +180,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -246,10 +197,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
@@ -259,23 +208,16 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
@@ -283,35 +225,18 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
|
||||
github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
|
||||
github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
|
||||
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
|
||||
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
|
||||
@@ -319,12 +244,10 @@ github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d h1:PRDnysJ9dF1vUMmEzBu6aHQeUluSQy4eWH3RsSSy/vI=
|
||||
github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@@ -347,7 +270,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
@@ -355,13 +277,6 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.2 h1:JADBlm0LYiVbuSySCHeY863dNkcpMmDR7s0bLKJeYlQ=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
@@ -369,7 +284,6 @@ github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peK
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
@@ -383,26 +297,12 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
|
||||
github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs=
|
||||
github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
|
||||
github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys=
|
||||
github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
|
||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
|
||||
github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ=
|
||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
@@ -412,9 +312,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
@@ -427,14 +324,12 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
|
||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
@@ -447,39 +342,13 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
|
||||
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
||||
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
|
||||
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
|
||||
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
|
||||
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
|
||||
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
|
||||
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
|
||||
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
|
||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
|
||||
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
|
||||
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
|
||||
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
|
||||
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
|
||||
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
|
||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -495,27 +364,20 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/contrib/propagators v0.22.0 h1:KGdv58M2//veiYLIhb31mofaI2LgkIPXXAZVeYVyfd8=
|
||||
go.opentelemetry.io/contrib/propagators v0.22.0/go.mod h1:xGOuXr6lLIF9BXipA4pm6UuOSI0M98U6tsI3khbOiwU=
|
||||
go.opentelemetry.io/otel v1.0.0-RC2/go.mod h1:w1thVQ7qbAy8MHb0IFj8a5Q2QU0l2ksf8u/CN8m3NOM=
|
||||
@@ -534,20 +396,16 @@ go.opentelemetry.io/proto/otlp v0.15.0 h1:h0bKrvdrT/9sBwEJ6iWUqT/N/xPcS66bL4u3is
|
||||
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||
go.uber.org/automaxprocs v1.4.0 h1:CpDZl6aOlLhReez+8S3eEotD7Jx0Os++lemPlMULQP0=
|
||||
go.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -558,9 +416,10 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -571,8 +430,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
@@ -581,21 +438,15 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@@ -614,60 +465,28 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
|
||||
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M=
|
||||
golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||
golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
|
||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -676,20 +495,15 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -697,10 +511,8 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -716,50 +528,21 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
@@ -773,19 +556,15 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
@@ -826,31 +605,14 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@@ -867,32 +629,7 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
||||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
|
||||
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
|
||||
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
|
||||
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
|
||||
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
|
||||
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
|
||||
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
|
||||
google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
|
||||
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
|
||||
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
|
||||
google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
|
||||
google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
|
||||
google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
|
||||
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
|
||||
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
|
||||
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
@@ -901,10 +638,6 @@ google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
@@ -934,59 +667,9 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
||||
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
||||
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
|
||||
google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd h1:OjndDrsik+Gt+e6fs45z9AxiewiKyLKYpA45W5Kpkks=
|
||||
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@@ -999,29 +682,13 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
||||
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
|
||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -1044,17 +711,9 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/coreos/go-oidc.v2 v2.2.1 h1:MY5SZClJ7vhjKfr64a4nHAOV/c3WH2gB9BMrR64J1Mc=
|
||||
gopkg.in/coreos/go-oidc.v2 v2.2.1/go.mod h1:fYaTe2FS96wZZwR17YTDHwG+Mw6fmyqJNxN2eNCGPCI=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@@ -1066,8 +725,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@@ -1080,7 +737,5 @@ nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
zombiezen.com/go/capnproto2 v2.18.0+incompatible h1:mwfXZniffG5mXokQGHUJWGnqIBggoPfT/CEwon9Yess=
|
||||
zombiezen.com/go/capnproto2 v2.18.0+incompatible/go.mod h1:XO5Pr2SbXgqZwn0m0Ru54QBqpOf4K5AYBO+8LAOBQEQ=
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
//go:build !cgo
|
||||
// +build !cgo
|
||||
|
||||
package h2mux
|
||||
|
||||
import (
|
||||
@@ -1,22 +0,0 @@
|
||||
//go:build cgo
|
||||
// +build cgo
|
||||
|
||||
package h2mux
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/cloudflare/brotli-go"
|
||||
)
|
||||
|
||||
func CompressionIsSupported() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func newDecompressor(src io.Reader) *brotli.Reader {
|
||||
return brotli.NewReader(src)
|
||||
}
|
||||
|
||||
func newCompressor(dst io.Writer, quality, lgwin int) *brotli.Writer {
|
||||
return brotli.NewWriter(dst, brotli.WriterOptions{Quality: quality, LGWin: lgwin})
|
||||
}
|
||||
+1
-8
@@ -135,14 +135,7 @@ func Handshake(
|
||||
m.f.ReadMetaHeaders = hpack.NewDecoder(4096, func(hpack.HeaderField) {})
|
||||
// Initialise the settings to identify this connection and confirm the other end is sane.
|
||||
handshakeSetting := http2.Setting{ID: SettingMuxerMagic, Val: MuxerMagicEdge}
|
||||
compressionSetting := http2.Setting{ID: SettingCompression, Val: config.CompressionQuality.toH2Setting()}
|
||||
if CompressionIsSupported() {
|
||||
config.Log.Debug().Msg("muxer: Compression is supported")
|
||||
m.compressionQuality = config.CompressionQuality.getPreset()
|
||||
} else {
|
||||
config.Log.Debug().Msg("muxer: Compression is not supported")
|
||||
compressionSetting = http2.Setting{ID: SettingCompression, Val: 0}
|
||||
}
|
||||
compressionSetting := http2.Setting{ID: SettingCompression, Val: 0}
|
||||
|
||||
expectedMagic := MuxerMagicOrigin
|
||||
if config.IsClient {
|
||||
|
||||
+2
-230
@@ -5,7 +5,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
@@ -652,7 +651,7 @@ func TestHPACK(t *testing.T) {
|
||||
if stream.Headers[0].Value != "200" {
|
||||
t.Fatalf("expected status 200, got %s", stream.Headers[0].Value)
|
||||
}
|
||||
_, _ = ioutil.ReadAll(stream)
|
||||
_, _ = io.ReadAll(stream)
|
||||
_ = stream.Close()
|
||||
}
|
||||
}
|
||||
@@ -678,157 +677,6 @@ func AssertIfPipeReadable(t *testing.T, pipe io.ReadCloser) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleStreamsWithDictionaries(t *testing.T) {
|
||||
l := zerolog.Nop()
|
||||
|
||||
for q := CompressionNone; q <= CompressionMax; q++ {
|
||||
htmlBody := `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"` +
|
||||
`"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">` +
|
||||
`<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">` +
|
||||
`<head>` +
|
||||
` <title>Your page title here</title>` +
|
||||
`</head>` +
|
||||
`<body>` +
|
||||
`<h1>Your major heading here</h1>` +
|
||||
`<p>` +
|
||||
`This is a regular text paragraph.` +
|
||||
`</p>` +
|
||||
`<ul>` +
|
||||
` <li>` +
|
||||
` First bullet of a bullet list.` +
|
||||
` </li>` +
|
||||
` <li>` +
|
||||
` This is the <em>second</em> bullet.` +
|
||||
` </li>` +
|
||||
`</ul>` +
|
||||
`</body>` +
|
||||
`</html>`
|
||||
|
||||
f := MuxedStreamFunc(func(stream *MuxedStream) error {
|
||||
var contentType string
|
||||
var pathHeader Header
|
||||
|
||||
for _, h := range stream.Headers {
|
||||
if h.Name == ":path" {
|
||||
pathHeader = h
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if pathHeader.Name != ":path" {
|
||||
panic("Couldn't find :path header in test")
|
||||
}
|
||||
|
||||
if strings.Contains(pathHeader.Value, "html") {
|
||||
contentType = "text/html; charset=utf-8"
|
||||
} else if strings.Contains(pathHeader.Value, "js") {
|
||||
contentType = "application/javascript"
|
||||
} else if strings.Contains(pathHeader.Value, "css") {
|
||||
contentType = "text/css"
|
||||
} else {
|
||||
contentType = "img/gif"
|
||||
}
|
||||
|
||||
_ = stream.WriteHeaders([]Header{
|
||||
{Name: "content-type", Value: contentType},
|
||||
})
|
||||
_, _ = stream.Write([]byte(strings.Replace(htmlBody, "paragraph", pathHeader.Value, 1) + stream.Headers[5].Value))
|
||||
|
||||
return nil
|
||||
})
|
||||
muxPair := NewCompressedMuxerPair(t, fmt.Sprintf("%s_%d", t.Name(), q), q, f)
|
||||
muxPair.Serve(t)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
paths := []string{
|
||||
"/html1",
|
||||
"/html2?sa:ds",
|
||||
"/html3",
|
||||
"/css1",
|
||||
"/html1",
|
||||
"/html2?sa:ds",
|
||||
"/html3",
|
||||
"/css1",
|
||||
"/css2",
|
||||
"/css3",
|
||||
"/js",
|
||||
"/js",
|
||||
"/js",
|
||||
"/js2",
|
||||
"/img2",
|
||||
"/html1",
|
||||
"/html2?sa:ds",
|
||||
"/html3",
|
||||
"/css1",
|
||||
"/css2",
|
||||
"/css3",
|
||||
"/js",
|
||||
"/js",
|
||||
"/js",
|
||||
"/js2",
|
||||
"/img1",
|
||||
}
|
||||
|
||||
wg.Add(len(paths))
|
||||
errorsC := make(chan error, len(paths))
|
||||
|
||||
for i, s := range paths {
|
||||
go func(index int, path string) {
|
||||
defer wg.Done()
|
||||
stream, err := muxPair.OpenEdgeMuxStream(
|
||||
[]Header{
|
||||
{Name: ":method", Value: "GET"},
|
||||
{Name: ":scheme", Value: "https"},
|
||||
{Name: ":authority", Value: "tunnel.otterlyadorable.co.uk"},
|
||||
{Name: ":path", Value: path},
|
||||
{Name: "cf-ray", Value: "378948953f044408-SFO-DOG"},
|
||||
{Name: "idx", Value: strconv.Itoa(index)},
|
||||
{Name: "accept-encoding", Value: "gzip, br"},
|
||||
},
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
errorsC <- fmt.Errorf("error in OpenStream: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
expectBody := strings.Replace(htmlBody, "paragraph", path, 1) + strconv.Itoa(index)
|
||||
responseBody := make([]byte, len(expectBody)*2)
|
||||
n, err := stream.Read(responseBody)
|
||||
if err != nil {
|
||||
errorsC <- fmt.Errorf("stream %d error from (*MuxedStream).Read: %s", stream.streamID, err)
|
||||
return
|
||||
}
|
||||
if n != len(expectBody) {
|
||||
errorsC <- fmt.Errorf("stream %d expected response body to have %d bytes, got %d", stream.streamID, len(expectBody), n)
|
||||
return
|
||||
}
|
||||
if string(responseBody[:n]) != expectBody {
|
||||
errorsC <- fmt.Errorf("stream %d expected response body %s, got %s", stream.streamID, expectBody, responseBody[:n])
|
||||
return
|
||||
}
|
||||
}(i, s)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
close(errorsC)
|
||||
testFail := false
|
||||
for err := range errorsC {
|
||||
testFail = true
|
||||
l.Error().Msgf("%s", err)
|
||||
}
|
||||
if testFail {
|
||||
t.Fatalf("TestMultipleStreams failed")
|
||||
}
|
||||
|
||||
originMuxMetrics := muxPair.OriginMux.Metrics()
|
||||
if q > CompressionNone && originMuxMetrics.CompBytesBefore.Value() <= 10*originMuxMetrics.CompBytesAfter.Value() {
|
||||
t.Fatalf("Cross-stream compression is expected to give a better compression ratio")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sampleSiteHandler(files map[string][]byte) MuxedStreamFunc {
|
||||
return func(stream *MuxedStream) error {
|
||||
var contentType string
|
||||
@@ -905,7 +753,7 @@ func loadSampleFiles(paths []string) (map[string][]byte, error) {
|
||||
files := make(map[string][]byte)
|
||||
for _, path := range paths {
|
||||
if _, ok := files[path]; !ok {
|
||||
expectBody, err := ioutil.ReadFile(path)
|
||||
expectBody, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -915,82 +763,6 @@ func loadSampleFiles(paths []string) (map[string][]byte, error) {
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func TestSampleSiteWithDictionaries(t *testing.T) {
|
||||
paths := []string{
|
||||
"./sample/index.html",
|
||||
"./sample/index2.html",
|
||||
"./sample/index1.html",
|
||||
"./sample/ghost-url.min.js",
|
||||
"./sample/jquery.fitvids.js",
|
||||
"./sample/index1.html",
|
||||
"./sample/index2.html",
|
||||
"./sample/index.html",
|
||||
}
|
||||
files, err := loadSampleFiles(paths)
|
||||
assert.NoError(t, err)
|
||||
|
||||
for q := CompressionNone; q <= CompressionMax; q++ {
|
||||
muxPair := NewCompressedMuxerPair(t, fmt.Sprintf("%s_%d", t.Name(), q), q, sampleSiteHandler(files))
|
||||
muxPair.Serve(t)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
errC := make(chan error, len(paths))
|
||||
|
||||
wg.Add(len(paths))
|
||||
for _, s := range paths {
|
||||
go func(path string) {
|
||||
defer wg.Done()
|
||||
errC <- sampleSiteTest(muxPair, path, files)
|
||||
}(s)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
close(errC)
|
||||
|
||||
for err := range errC {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
originMuxMetrics := muxPair.OriginMux.Metrics()
|
||||
if q > CompressionNone && originMuxMetrics.CompBytesBefore.Value() <= 10*originMuxMetrics.CompBytesAfter.Value() {
|
||||
t.Fatalf("Cross-stream compression is expected to give a better compression ratio")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLongSiteWithDictionaries(t *testing.T) {
|
||||
paths := []string{
|
||||
"./sample/index.html",
|
||||
"./sample/index1.html",
|
||||
"./sample/index2.html",
|
||||
"./sample/ghost-url.min.js",
|
||||
"./sample/jquery.fitvids.js",
|
||||
}
|
||||
files, err := loadSampleFiles(paths)
|
||||
assert.NoError(t, err)
|
||||
for q := CompressionNone; q <= CompressionMedium; q++ {
|
||||
muxPair := NewCompressedMuxerPair(t, fmt.Sprintf("%s_%d", t.Name(), q), q, sampleSiteHandler(files))
|
||||
muxPair.Serve(t)
|
||||
|
||||
rand.Seed(time.Now().Unix())
|
||||
|
||||
tstLen := 500
|
||||
errGroup, _ := errgroup.WithContext(context.Background())
|
||||
for i := 0; i < tstLen; i++ {
|
||||
errGroup.Go(func() error {
|
||||
path := paths[rand.Int()%len(paths)]
|
||||
return sampleSiteTest(muxPair, path, files)
|
||||
})
|
||||
}
|
||||
assert.NoError(t, errGroup.Wait())
|
||||
|
||||
originMuxMetrics := muxPair.OriginMux.Metrics()
|
||||
if q > CompressionNone && originMuxMetrics.CompBytesBefore.Value() <= 10*originMuxMetrics.CompBytesAfter.Value() {
|
||||
t.Fatalf("Cross-stream compression is expected to give a better compression ratio")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkOpenStream(b *testing.B) {
|
||||
const streams = 5000
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -234,7 +234,7 @@ func rootHandler(serverName string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var buffer bytes.Buffer
|
||||
var body string
|
||||
rawBody, err := ioutil.ReadAll(r.Body)
|
||||
rawBody, err := io.ReadAll(r.Body)
|
||||
if err == nil {
|
||||
body = string(rawBody)
|
||||
} else {
|
||||
|
||||
+19
-10
@@ -44,7 +44,7 @@ func (ing Ingress) FindMatchingRule(hostname, path string) (*Rule, int) {
|
||||
if err == nil {
|
||||
hostname = host
|
||||
}
|
||||
for i, rule := range ing.LocalRules {
|
||||
for i, rule := range ing.InternalRules {
|
||||
if rule.Matches(hostname, path) {
|
||||
// Local rule matches return a negative rule index to distiguish local rules from user-defined rules in logs
|
||||
// Full range would be [-1 .. )
|
||||
@@ -77,7 +77,7 @@ func matchHost(ruleHost, reqHost string) bool {
|
||||
// Ingress maps eyeball requests to origins.
|
||||
type Ingress struct {
|
||||
// Set of ingress rules that are not added to remote config, e.g. management
|
||||
LocalRules []Rule
|
||||
InternalRules []Rule
|
||||
// Rules that are provided by the user from remote or local configuration
|
||||
Rules []Rule `json:"ingress"`
|
||||
Defaults OriginRequestConfig `json:"originRequest"`
|
||||
@@ -110,16 +110,18 @@ func ParseIngressFromConfigAndCLI(conf *config.Configuration, c *cli.Context, lo
|
||||
// --bastion for ssh bastion service
|
||||
ingressRules, err = parseCLIIngress(c, false)
|
||||
if errors.Is(err, ErrNoIngressRulesCLI) {
|
||||
// Only log a warning if the tunnel is not a remotely managed tunnel and the config
|
||||
// will be loaded after connecting.
|
||||
// If no token is provided, the probability of NOT being a remotely managed tunnel is higher.
|
||||
// So, we should warn the user that no ingress rules were found, because remote configuration will most likely not exist.
|
||||
if !c.IsSet("token") {
|
||||
log.Warn().Msgf(ErrNoIngressRulesCLI.Error())
|
||||
}
|
||||
return newDefaultOrigin(c, log), nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return Ingress{}, err
|
||||
}
|
||||
|
||||
return ingressRules, nil
|
||||
}
|
||||
|
||||
@@ -148,14 +150,10 @@ func parseCLIIngress(c *cli.Context, allowURLFromArgs bool) (Ingress, error) {
|
||||
// newDefaultOrigin always returns a 503 response code to help indicate that there are no ingress
|
||||
// rules setup, but the tunnel is reachable.
|
||||
func newDefaultOrigin(c *cli.Context, log *zerolog.Logger) Ingress {
|
||||
noRulesService := newDefaultStatusCode(log)
|
||||
defaultRule := GetDefaultIngressRules(log)
|
||||
defaults := originRequestFromSingleRule(c)
|
||||
ingress := Ingress{
|
||||
Rules: []Rule{
|
||||
{
|
||||
Service: &noRulesService,
|
||||
},
|
||||
},
|
||||
Rules: defaultRule,
|
||||
Defaults: defaults,
|
||||
}
|
||||
return ingress
|
||||
@@ -219,6 +217,17 @@ func (ing Ingress) CatchAll() *Rule {
|
||||
return &ing.Rules[len(ing.Rules)-1]
|
||||
}
|
||||
|
||||
// Gets the default ingress rule that will be return 503 status
|
||||
// code for all incoming requests.
|
||||
func GetDefaultIngressRules(log *zerolog.Logger) []Rule {
|
||||
noRulesService := newDefaultStatusCode(log)
|
||||
return []Rule{
|
||||
{
|
||||
Service: &noRulesService,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func validateAccessConfiguration(cfg *config.AccessConfig) error {
|
||||
if !cfg.Required {
|
||||
return nil
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -118,7 +117,7 @@ func TestSocksStreamWSOverTCPConnection(t *testing.T) {
|
||||
}
|
||||
for _, status := range statusCodes {
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []byte(sendMessage), body)
|
||||
|
||||
@@ -180,7 +179,7 @@ func TestSocksStreamWSOverTCPConnection(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, status, resp.StatusCode)
|
||||
require.Equal(t, echoHeaderReturnValue, resp.Header.Get(echoHeaderName))
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []byte(echoMessage), body)
|
||||
|
||||
|
||||
@@ -390,15 +390,16 @@ func getLocalIPs(t *testing.T, ipv4 bool) []netip.Addr {
|
||||
require.NoError(t, err)
|
||||
localIPs := []netip.Addr{}
|
||||
for _, i := range interfaces {
|
||||
// Skip TUN devices
|
||||
if strings.Contains(i.Name, "tun") {
|
||||
// Skip TUN devices, and Docker Networks
|
||||
if strings.Contains(i.Name, "tun") || strings.Contains(i.Name, "docker") || strings.HasPrefix(i.Name, "br-") {
|
||||
continue
|
||||
}
|
||||
addrs, err := i.Addrs()
|
||||
require.NoError(t, err)
|
||||
for _, addr := range addrs {
|
||||
if ipnet, ok := addr.(*net.IPNet); ok && (ipnet.IP.IsPrivate() || ipnet.IP.IsLoopback()) {
|
||||
if (ipv4 && ipnet.IP.To4() != nil) || (!ipv4 && ipnet.IP.To4() == nil) {
|
||||
// TODO DEVTOOLS-12514: We only run the IPv6 against the loopback interface due to issues on the CI runners.
|
||||
if (ipv4 && ipnet.IP.To4() != nil) || (!ipv4 && ipnet.IP.To4() == nil && ipnet.IP.IsLoopback()) {
|
||||
localIPs = append(localIPs, netip.MustParseAddr(ipnet.IP.String()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package ingress
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -141,7 +141,7 @@ func TestHTTPServiceHostHeaderOverride(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, respBody, []byte(originURL.Host))
|
||||
}
|
||||
@@ -180,7 +180,7 @@ func TestHTTPServiceUsesIngressRuleScheme(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, respBody, []byte(p))
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
basename: "cloudflared"
|
||||
comment: "Cloudflare Tunnel"
|
||||
copyright: "Cloudflare, Inc"
|
||||
arch: "x86"
|
||||
abi: "64"
|
||||
files:
|
||||
- source: cloudflared
|
||||
destination: /var/db/scripts/jet/cloudflared
|
||||
+1
-1
@@ -175,7 +175,7 @@ func createFromContext(
|
||||
|
||||
log := newZerolog(loggerConfig)
|
||||
if incompatibleFlagsSet := logFile != "" && logDirectory != ""; incompatibleFlagsSet {
|
||||
log.Error().Msgf("Your config includes values for both %s and %s, but they are incompatible. %s takes precedence.", LogFileFlag, logDirectoryFlagName, LogFileFlag)
|
||||
log.Error().Msgf("Your config includes values for both %s (%s) and %s (%s), but they are incompatible. %s takes precedence.", LogFileFlag, logFile, logDirectoryFlagName, logDirectory, LogFileFlag)
|
||||
}
|
||||
return log
|
||||
}
|
||||
|
||||
@@ -52,8 +52,9 @@ type EventStartStreaming struct {
|
||||
}
|
||||
|
||||
type StreamingFilters struct {
|
||||
Events []LogEventType `json:"events,omitempty"`
|
||||
Level *LogLevel `json:"level,omitempty"`
|
||||
Events []LogEventType `json:"events,omitempty"`
|
||||
Level *LogLevel `json:"level,omitempty"`
|
||||
Sampling float64 `json:"sampling,omitempty"`
|
||||
}
|
||||
|
||||
// EventStopStreaming signifies that the client wishes to halt receiving log events.
|
||||
|
||||
@@ -57,13 +57,23 @@ func TestIntoClientEvent_StartStreaming(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sampling filter",
|
||||
expected: EventStartStreaming{
|
||||
ClientEvent: ClientEvent{Type: StartStreaming},
|
||||
Filters: &StreamingFilters{
|
||||
Sampling: 0.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "level and events filters",
|
||||
expected: EventStartStreaming{
|
||||
ClientEvent: ClientEvent{Type: StartStreaming},
|
||||
Filters: &StreamingFilters{
|
||||
Level: infoLevel,
|
||||
Events: []LogEventType{Cloudflared},
|
||||
Level: infoLevel,
|
||||
Events: []LogEventType{Cloudflared},
|
||||
Sampling: 0.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+28
-57
@@ -11,16 +11,9 @@ import (
|
||||
|
||||
var json = jsoniter.ConfigFastest
|
||||
|
||||
const (
|
||||
// Indicates how many log messages the listener will hold before dropping.
|
||||
// Provides a throttling mechanism to drop latest messages if the sender
|
||||
// can't keep up with the influx of log messages.
|
||||
logWindow = 30
|
||||
)
|
||||
|
||||
// Logger manages the number of management streaming log sessions
|
||||
type Logger struct {
|
||||
sessions []*Session
|
||||
sessions []*session
|
||||
mu sync.RWMutex
|
||||
|
||||
// Unique logger that isn't a io.Writer of the list of zerolog writers. This helps prevent management log
|
||||
@@ -40,69 +33,47 @@ func NewLogger() *Logger {
|
||||
}
|
||||
|
||||
type LoggerListener interface {
|
||||
Listen(*StreamingFilters) *Session
|
||||
Close(*Session)
|
||||
// ActiveSession returns the first active session for the requested actor.
|
||||
ActiveSession(actor) *session
|
||||
// ActiveSession returns the count of active sessions.
|
||||
ActiveSessions() int
|
||||
// Listen appends the session to the list of sessions that receive log events.
|
||||
Listen(*session)
|
||||
// Remove a session from the available sessions that were receiving log events.
|
||||
Remove(*session)
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
// Buffered channel that holds the recent log events
|
||||
listener chan *Log
|
||||
// Types of log events that this session will provide through the listener
|
||||
filters *StreamingFilters
|
||||
}
|
||||
|
||||
func newSession(size int, filters *StreamingFilters) *Session {
|
||||
s := &Session{
|
||||
listener: make(chan *Log, size),
|
||||
}
|
||||
if filters != nil {
|
||||
s.filters = filters
|
||||
} else {
|
||||
s.filters = &StreamingFilters{}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Insert attempts to insert the log to the session. If the log event matches the provided session filters, it
|
||||
// will be applied to the listener.
|
||||
func (s *Session) Insert(log *Log) {
|
||||
// Level filters are optional
|
||||
if s.filters.Level != nil {
|
||||
if *s.filters.Level > log.Level {
|
||||
return
|
||||
func (l *Logger) ActiveSession(actor actor) *session {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
for _, session := range l.sessions {
|
||||
if session.actor.ID == actor.ID && session.active.Load() {
|
||||
return session
|
||||
}
|
||||
}
|
||||
// Event filters are optional
|
||||
if len(s.filters.Events) != 0 && !contains(s.filters.Events, log.Event) {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case s.listener <- log:
|
||||
default:
|
||||
// buffer is full, discard
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func contains(array []LogEventType, t LogEventType) bool {
|
||||
for _, v := range array {
|
||||
if v == t {
|
||||
return true
|
||||
func (l *Logger) ActiveSessions() int {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
count := 0
|
||||
for _, session := range l.sessions {
|
||||
if session.active.Load() {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
return false
|
||||
return count
|
||||
}
|
||||
|
||||
// Listen creates a new Session that will append filtered log events as they are created.
|
||||
func (l *Logger) Listen(filters *StreamingFilters) *Session {
|
||||
func (l *Logger) Listen(session *session) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
listener := newSession(logWindow, filters)
|
||||
l.sessions = append(l.sessions, listener)
|
||||
return listener
|
||||
session.active.Store(true)
|
||||
l.sessions = append(l.sessions, session)
|
||||
}
|
||||
|
||||
// Close will remove a Session from the available sessions that were receiving log events.
|
||||
func (l *Logger) Close(session *Session) {
|
||||
func (l *Logger) Remove(session *session) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
index := -1
|
||||
|
||||
+21
-106
@@ -1,8 +1,8 @@
|
||||
package management
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -21,9 +21,14 @@ func TestLoggerWrite_NoSessions(t *testing.T) {
|
||||
func TestLoggerWrite_OneSession(t *testing.T) {
|
||||
logger := NewLogger()
|
||||
zlog := zerolog.New(logger).With().Timestamp().Logger().Level(zerolog.InfoLevel)
|
||||
_, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
session := logger.Listen(nil)
|
||||
defer logger.Close(session)
|
||||
session := newSession(logWindow, actor{ID: actorID}, cancel)
|
||||
logger.Listen(session)
|
||||
defer logger.Remove(session)
|
||||
assert.Equal(t, 1, logger.ActiveSessions())
|
||||
assert.Equal(t, session, logger.ActiveSession(actor{ID: actorID}))
|
||||
zlog.Info().Int(EventTypeKey, int(HTTP)).Msg("hello")
|
||||
select {
|
||||
case event := <-session.listener:
|
||||
@@ -40,12 +45,20 @@ func TestLoggerWrite_OneSession(t *testing.T) {
|
||||
func TestLoggerWrite_MultipleSessions(t *testing.T) {
|
||||
logger := NewLogger()
|
||||
zlog := zerolog.New(logger).With().Timestamp().Logger().Level(zerolog.InfoLevel)
|
||||
_, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
session1 := newSession(logWindow, actor{}, cancel)
|
||||
logger.Listen(session1)
|
||||
defer logger.Remove(session1)
|
||||
assert.Equal(t, 1, logger.ActiveSessions())
|
||||
|
||||
session2 := newSession(logWindow, actor{}, cancel)
|
||||
logger.Listen(session2)
|
||||
assert.Equal(t, 2, logger.ActiveSessions())
|
||||
|
||||
session1 := logger.Listen(nil)
|
||||
defer logger.Close(session1)
|
||||
session2 := logger.Listen(nil)
|
||||
zlog.Info().Int(EventTypeKey, int(HTTP)).Msg("hello")
|
||||
for _, session := range []*Session{session1, session2} {
|
||||
for _, session := range []*session{session1, session2} {
|
||||
select {
|
||||
case event := <-session.listener:
|
||||
assert.NotEmpty(t, event.Time)
|
||||
@@ -58,7 +71,7 @@ func TestLoggerWrite_MultipleSessions(t *testing.T) {
|
||||
}
|
||||
|
||||
// Close session2 and make sure session1 still receives events
|
||||
logger.Close(session2)
|
||||
logger.Remove(session2)
|
||||
zlog.Info().Int(EventTypeKey, int(HTTP)).Msg("hello2")
|
||||
select {
|
||||
case event := <-session1.listener:
|
||||
@@ -79,104 +92,6 @@ func TestLoggerWrite_MultipleSessions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that the session filters events
|
||||
func TestSession_Insert(t *testing.T) {
|
||||
infoLevel := new(LogLevel)
|
||||
*infoLevel = Info
|
||||
warnLevel := new(LogLevel)
|
||||
*warnLevel = Warn
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
filters StreamingFilters
|
||||
expectLog bool
|
||||
}{
|
||||
{
|
||||
name: "none",
|
||||
expectLog: true,
|
||||
},
|
||||
{
|
||||
name: "level",
|
||||
filters: StreamingFilters{
|
||||
Level: infoLevel,
|
||||
},
|
||||
expectLog: true,
|
||||
},
|
||||
{
|
||||
name: "filtered out level",
|
||||
filters: StreamingFilters{
|
||||
Level: warnLevel,
|
||||
},
|
||||
expectLog: false,
|
||||
},
|
||||
{
|
||||
name: "events",
|
||||
filters: StreamingFilters{
|
||||
Events: []LogEventType{HTTP},
|
||||
},
|
||||
expectLog: true,
|
||||
},
|
||||
{
|
||||
name: "filtered out event",
|
||||
filters: StreamingFilters{
|
||||
Events: []LogEventType{Cloudflared},
|
||||
},
|
||||
expectLog: false,
|
||||
},
|
||||
{
|
||||
name: "filter and event",
|
||||
filters: StreamingFilters{
|
||||
Level: infoLevel,
|
||||
Events: []LogEventType{HTTP},
|
||||
},
|
||||
expectLog: true,
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
session := newSession(4, &test.filters)
|
||||
log := Log{
|
||||
Time: time.Now().UTC().Format(time.RFC3339),
|
||||
Event: HTTP,
|
||||
Level: Info,
|
||||
Message: "test",
|
||||
}
|
||||
session.Insert(&log)
|
||||
select {
|
||||
case <-session.listener:
|
||||
require.True(t, test.expectLog)
|
||||
default:
|
||||
require.False(t, test.expectLog)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that the session has a max amount of events to hold
|
||||
func TestSession_InsertOverflow(t *testing.T) {
|
||||
session := newSession(1, nil)
|
||||
log := Log{
|
||||
Time: time.Now().UTC().Format(time.RFC3339),
|
||||
Event: HTTP,
|
||||
Level: Info,
|
||||
Message: "test",
|
||||
}
|
||||
// Insert 2 but only max channel size for 1
|
||||
session.Insert(&log)
|
||||
session.Insert(&log)
|
||||
select {
|
||||
case <-session.listener:
|
||||
// pass
|
||||
default:
|
||||
require.Fail(t, "expected one log event")
|
||||
}
|
||||
// Second dequeue should fail
|
||||
select {
|
||||
case <-session.listener:
|
||||
require.Fail(t, "expected no more remaining log events")
|
||||
default:
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
type mockWriter struct {
|
||||
event *Log
|
||||
err error
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package management
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type ctxKey int
|
||||
|
||||
const (
|
||||
accessClaimsCtxKey ctxKey = iota
|
||||
)
|
||||
|
||||
const (
|
||||
connectorIDQuery = "connector_id"
|
||||
accessTokenQuery = "access_token"
|
||||
)
|
||||
|
||||
var (
|
||||
errMissingAccessToken = managementError{Code: 1001, Message: "missing access_token query parameter"}
|
||||
)
|
||||
|
||||
// HTTP middleware setting the parsed access_token claims in the request context
|
||||
func ValidateAccessTokenQueryMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Validate access token
|
||||
accessToken := r.URL.Query().Get("access_token")
|
||||
if accessToken == "" {
|
||||
writeHTTPErrorResponse(w, errMissingAccessToken)
|
||||
return
|
||||
}
|
||||
token, err := parseToken(accessToken)
|
||||
if err != nil {
|
||||
writeHTTPErrorResponse(w, errMissingAccessToken)
|
||||
return
|
||||
}
|
||||
r = r.WithContext(context.WithValue(r.Context(), accessClaimsCtxKey, token))
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// Middleware validation error struct for returning to the eyeball
|
||||
type managementError struct {
|
||||
Code int `json:"code,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
func (m *managementError) Error() string {
|
||||
return m.Message
|
||||
}
|
||||
|
||||
// Middleware validation error HTTP response JSON for returning to the eyeball
|
||||
type managementErrorResponse struct {
|
||||
Success bool `json:"success,omitempty"`
|
||||
Errors []managementError `json:"errors,omitempty"`
|
||||
}
|
||||
|
||||
// writeErrorResponse will respond to the eyeball with basic HTTP JSON payloads with validation failure information
|
||||
func writeHTTPErrorResponse(w http.ResponseWriter, errResp managementError) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
err := json.NewEncoder(w).Encode(managementErrorResponse{
|
||||
Success: false,
|
||||
Errors: []managementError{errResp},
|
||||
})
|
||||
// we have already written the header, so write a basic error response if unable to encode the error
|
||||
if err != nil {
|
||||
// fallback to text message
|
||||
http.Error(w, fmt.Sprintf(
|
||||
"%d %s",
|
||||
http.StatusBadRequest,
|
||||
http.StatusText(http.StatusBadRequest),
|
||||
), http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package management
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestValidateAccessTokenQueryMiddleware(t *testing.T) {
|
||||
r := chi.NewRouter()
|
||||
r.Use(ValidateAccessTokenQueryMiddleware)
|
||||
r.Get("/valid", func(w http.ResponseWriter, r *http.Request) {
|
||||
claims, ok := r.Context().Value(accessClaimsCtxKey).(*managementTokenClaims)
|
||||
assert.True(t, ok)
|
||||
assert.True(t, claims.verify())
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
r.Get("/invalid", func(w http.ResponseWriter, r *http.Request) {
|
||||
_, ok := r.Context().Value(accessClaimsCtxKey).(*managementTokenClaims)
|
||||
assert.False(t, ok)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
ts := httptest.NewServer(r)
|
||||
defer ts.Close()
|
||||
|
||||
// valid: with access_token query param
|
||||
path := "/valid?access_token=" + validToken
|
||||
resp, _ := testRequest(t, ts, "GET", path, nil)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
// invalid: unset token
|
||||
path = "/invalid"
|
||||
resp, err := testRequest(t, ts, "GET", path, nil)
|
||||
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, errMissingAccessToken, err.Errors[0])
|
||||
|
||||
// invalid: invalid token
|
||||
path = "/invalid?access_token=eyJ"
|
||||
resp, err = testRequest(t, ts, "GET", path, nil)
|
||||
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, errMissingAccessToken, err.Errors[0])
|
||||
}
|
||||
|
||||
func testRequest(t *testing.T, ts *httptest.Server, method, path string, body io.Reader) (*http.Response, *managementErrorResponse) {
|
||||
req, err := http.NewRequest(method, ts.URL+path, body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
resp, err := ts.Client().Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return nil, nil
|
||||
}
|
||||
var claims managementErrorResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&claims)
|
||||
if err != nil {
|
||||
return resp, nil
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return resp, &claims
|
||||
}
|
||||
+170
-36
@@ -2,12 +2,18 @@ package management
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/cors"
|
||||
"github.com/google/uuid"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/rs/zerolog"
|
||||
"nhooyr.io/websocket"
|
||||
)
|
||||
@@ -20,21 +26,37 @@ const (
|
||||
// value will return this error to incoming requests.
|
||||
StatusSessionLimitExceeded websocket.StatusCode = 4002
|
||||
reasonSessionLimitExceeded = "limit exceeded for streaming sessions"
|
||||
|
||||
// There is a limited idle time while not actively serving a session for a request before dropping the connection.
|
||||
StatusIdleLimitExceeded websocket.StatusCode = 4003
|
||||
reasonIdleLimitExceeded = "session was idle for too long"
|
||||
)
|
||||
|
||||
var (
|
||||
// CORS middleware required to allow dash to access management.argotunnel.com requests
|
||||
corsHandler = cors.Handler(cors.Options{
|
||||
// Allows for any subdomain of cloudflare.com
|
||||
AllowedOrigins: []string{"https://*.cloudflare.com"},
|
||||
// Required to present cookies or other authentication across origin boundries
|
||||
AllowCredentials: true,
|
||||
MaxAge: 300, // Maximum value not ignored by any of major browsers
|
||||
})
|
||||
)
|
||||
|
||||
type ManagementService struct {
|
||||
// The management tunnel hostname
|
||||
Hostname string
|
||||
|
||||
// Host details related configurations
|
||||
serviceIP string
|
||||
clientID uuid.UUID
|
||||
label string
|
||||
|
||||
// Additional Handlers
|
||||
metricsHandler http.Handler
|
||||
|
||||
log *zerolog.Logger
|
||||
router chi.Router
|
||||
|
||||
// streaming signifies if the service is already streaming logs. Helps limit the number of active users streaming logs
|
||||
// from this cloudflared instance.
|
||||
streaming atomic.Bool
|
||||
// streamingMut is a lock to prevent concurrent requests to start streaming. Utilizing the atomic.Bool is not
|
||||
// sufficient to complete this operation since many other checks during an incoming new request are needed
|
||||
// to validate this before setting streaming to true.
|
||||
@@ -42,16 +64,40 @@ type ManagementService struct {
|
||||
logger LoggerListener
|
||||
}
|
||||
|
||||
func New(managementHostname string, log *zerolog.Logger, logger LoggerListener) *ManagementService {
|
||||
func New(managementHostname string,
|
||||
enableDiagServices bool,
|
||||
serviceIP string,
|
||||
clientID uuid.UUID,
|
||||
label string,
|
||||
log *zerolog.Logger,
|
||||
logger LoggerListener,
|
||||
) *ManagementService {
|
||||
s := &ManagementService{
|
||||
Hostname: managementHostname,
|
||||
log: log,
|
||||
logger: logger,
|
||||
Hostname: managementHostname,
|
||||
log: log,
|
||||
logger: logger,
|
||||
serviceIP: serviceIP,
|
||||
clientID: clientID,
|
||||
label: label,
|
||||
metricsHandler: promhttp.Handler(),
|
||||
}
|
||||
r := chi.NewRouter()
|
||||
r.Get("/ping", ping)
|
||||
r.Head("/ping", ping)
|
||||
r.Use(ValidateAccessTokenQueryMiddleware)
|
||||
|
||||
// Default management services
|
||||
r.With(corsHandler).Get("/ping", ping)
|
||||
r.With(corsHandler).Head("/ping", ping)
|
||||
r.Get("/logs", s.logs)
|
||||
r.With(corsHandler).Get("/host_details", s.getHostDetails)
|
||||
|
||||
// Diagnostic management services
|
||||
if enableDiagServices {
|
||||
// Prometheus endpoint
|
||||
r.With(corsHandler).Get("/metrics", s.metricsHandler.ServeHTTP)
|
||||
// Supports only heap and goroutine
|
||||
r.With(corsHandler).Get("/debug/pprof/{profile:heap|goroutine}", pprof.Index)
|
||||
}
|
||||
|
||||
s.router = r
|
||||
return s
|
||||
}
|
||||
@@ -65,6 +111,53 @@ func ping(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
}
|
||||
|
||||
// The response provided by the /host_details endpoint
|
||||
type getHostDetailsResponse struct {
|
||||
ClientID string `json:"connector_id"`
|
||||
IP string `json:"ip,omitempty"`
|
||||
HostName string `json:"hostname,omitempty"`
|
||||
}
|
||||
|
||||
func (m *ManagementService) getHostDetails(w http.ResponseWriter, r *http.Request) {
|
||||
var getHostDetailsResponse = getHostDetailsResponse{
|
||||
ClientID: m.clientID.String(),
|
||||
}
|
||||
if ip, err := getPrivateIP(m.serviceIP); err == nil {
|
||||
getHostDetailsResponse.IP = ip
|
||||
}
|
||||
getHostDetailsResponse.HostName = m.getLabel()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(200)
|
||||
json.NewEncoder(w).Encode(getHostDetailsResponse)
|
||||
}
|
||||
|
||||
func (m *ManagementService) getLabel() string {
|
||||
if m.label != "" {
|
||||
return fmt.Sprintf("custom:%s", m.label)
|
||||
}
|
||||
|
||||
// If no label is provided we return the system hostname. This is not
|
||||
// a fqdn hostname.
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return "unknown"
|
||||
}
|
||||
return hostname
|
||||
}
|
||||
|
||||
// Get preferred private ip of this machine
|
||||
func getPrivateIP(addr string) (string, error) {
|
||||
conn, err := net.DialTimeout("tcp", addr, 1*time.Second)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer conn.Close()
|
||||
localAddr := conn.LocalAddr().String()
|
||||
host, _, err := net.SplitHostPort(localAddr)
|
||||
return host, err
|
||||
}
|
||||
|
||||
// readEvents will loop through all incoming websocket messages from a client and marshal them into the
|
||||
// proper Event structure and pass through to the events channel. Any invalid messages sent will automatically
|
||||
// terminate the connection.
|
||||
@@ -90,12 +183,11 @@ func (m *ManagementService) readEvents(c *websocket.Conn, ctx context.Context, e
|
||||
}
|
||||
|
||||
// streamLogs will begin the process of reading from the Session listener and write the log events to the client.
|
||||
func (m *ManagementService) streamLogs(c *websocket.Conn, ctx context.Context, session *Session) {
|
||||
defer m.logger.Close(session)
|
||||
for m.streaming.Load() {
|
||||
func (m *ManagementService) streamLogs(c *websocket.Conn, ctx context.Context, session *session) {
|
||||
for session.Active() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
m.streaming.Store(false)
|
||||
session.Stop()
|
||||
return
|
||||
case event := <-session.listener:
|
||||
err := WriteEvent(c, ctx, &EventLog{
|
||||
@@ -109,7 +201,7 @@ func (m *ManagementService) streamLogs(c *websocket.Conn, ctx context.Context, s
|
||||
m.log.Err(c.Close(websocket.StatusInternalError, err.Error())).Send()
|
||||
}
|
||||
// Any errors when writing the messages to the client will stop streaming and close the connection
|
||||
m.streaming.Store(false)
|
||||
session.Stop()
|
||||
return
|
||||
}
|
||||
default:
|
||||
@@ -118,33 +210,47 @@ func (m *ManagementService) streamLogs(c *websocket.Conn, ctx context.Context, s
|
||||
}
|
||||
}
|
||||
|
||||
// startStreaming will check the conditions of the request and begin streaming or close the connection for invalid
|
||||
// requests.
|
||||
func (m *ManagementService) startStreaming(c *websocket.Conn, ctx context.Context, event *ClientEvent) {
|
||||
// canStartStream will check the conditions of the request and return if the session can begin streaming.
|
||||
func (m *ManagementService) canStartStream(session *session) bool {
|
||||
m.streamingMut.Lock()
|
||||
defer m.streamingMut.Unlock()
|
||||
// Limits to one user for streaming logs
|
||||
if m.streaming.Load() {
|
||||
m.log.Warn().
|
||||
Msgf("Another management session request was attempted but one session already being served; there is a limit of streaming log sessions to reduce overall performance impact.")
|
||||
m.log.Err(c.Close(StatusSessionLimitExceeded, reasonSessionLimitExceeded)).Send()
|
||||
return
|
||||
// Limits to one actor for streaming logs
|
||||
if m.logger.ActiveSessions() > 0 {
|
||||
// Allow the same user to preempt their existing session to disconnect their old session and start streaming
|
||||
// with this new session instead.
|
||||
if existingSession := m.logger.ActiveSession(session.actor); existingSession != nil {
|
||||
m.log.Info().
|
||||
Msgf("Another management session request for the same actor was requested; the other session will be disconnected to handle the new request.")
|
||||
existingSession.Stop()
|
||||
m.logger.Remove(existingSession)
|
||||
existingSession.cancel()
|
||||
} else {
|
||||
m.log.Warn().
|
||||
Msgf("Another management session request was attempted but one session already being served; there is a limit of streaming log sessions to reduce overall performance impact.")
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// parseFilters will check the ClientEvent for start_streaming and assign filters if provided to the session
|
||||
func (m *ManagementService) parseFilters(c *websocket.Conn, event *ClientEvent, session *session) bool {
|
||||
// Expect the first incoming request
|
||||
startEvent, ok := IntoClientEvent[EventStartStreaming](event, StartStreaming)
|
||||
if !ok {
|
||||
m.log.Warn().Err(c.Close(StatusInvalidCommand, reasonInvalidCommand)).Msgf("expected start_streaming as first recieved event")
|
||||
return
|
||||
return false
|
||||
}
|
||||
m.streaming.Store(true)
|
||||
listener := m.logger.Listen(startEvent.Filters)
|
||||
m.log.Debug().Msgf("Streaming logs")
|
||||
go m.streamLogs(c, ctx, listener)
|
||||
session.Filters(startEvent.Filters)
|
||||
return true
|
||||
}
|
||||
|
||||
// Management Streaming Logs accept handler
|
||||
func (m *ManagementService) logs(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := websocket.Accept(w, r, nil)
|
||||
c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
|
||||
OriginPatterns: []string{
|
||||
"*.cloudflare.com",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
m.log.Debug().Msgf("management handshake: %s", err.Error())
|
||||
return
|
||||
@@ -160,11 +266,23 @@ func (m *ManagementService) logs(w http.ResponseWriter, r *http.Request) {
|
||||
ping := time.NewTicker(15 * time.Second)
|
||||
defer ping.Stop()
|
||||
|
||||
// Close the connection if no operation has occurred after the idle timeout.
|
||||
// Close the connection if no operation has occurred after the idle timeout. The timeout is halted
|
||||
// when streaming logs is active.
|
||||
idleTimeout := 5 * time.Minute
|
||||
idle := time.NewTimer(idleTimeout)
|
||||
defer idle.Stop()
|
||||
|
||||
// Fetch the claims from the request context to acquire the actor
|
||||
claims, ok := ctx.Value(accessClaimsCtxKey).(*managementTokenClaims)
|
||||
if !ok || claims == nil {
|
||||
// Typically should never happen as it is provided in the context from the middleware
|
||||
m.log.Err(c.Close(websocket.StatusInternalError, "missing access_token")).Send()
|
||||
return
|
||||
}
|
||||
|
||||
session := newSession(logWindow, claims.Actor, cancel)
|
||||
defer m.logger.Remove(session)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -175,12 +293,28 @@ func (m *ManagementService) logs(w http.ResponseWriter, r *http.Request) {
|
||||
switch event.Type {
|
||||
case StartStreaming:
|
||||
idle.Stop()
|
||||
m.startStreaming(c, ctx, event)
|
||||
// Expect the first incoming request
|
||||
startEvent, ok := IntoClientEvent[EventStartStreaming](event, StartStreaming)
|
||||
if !ok {
|
||||
m.log.Warn().Msgf("expected start_streaming as first recieved event")
|
||||
m.log.Err(c.Close(StatusInvalidCommand, reasonInvalidCommand)).Send()
|
||||
return
|
||||
}
|
||||
// Make sure the session can start
|
||||
if !m.canStartStream(session) {
|
||||
m.log.Err(c.Close(StatusSessionLimitExceeded, reasonSessionLimitExceeded)).Send()
|
||||
return
|
||||
}
|
||||
session.Filters(startEvent.Filters)
|
||||
m.logger.Listen(session)
|
||||
m.log.Debug().Msgf("Streaming logs")
|
||||
go m.streamLogs(c, ctx, session)
|
||||
continue
|
||||
case StopStreaming:
|
||||
idle.Reset(idleTimeout)
|
||||
// TODO: limit StopStreaming to only halt streaming for clients that are already streaming
|
||||
m.streaming.Store(false)
|
||||
// Stop the current session for the current actor who requested it
|
||||
session.Stop()
|
||||
m.logger.Remove(session)
|
||||
case UnknownClientEventType:
|
||||
fallthrough
|
||||
default:
|
||||
|
||||
@@ -3,10 +3,15 @@ package management
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"nhooyr.io/websocket"
|
||||
|
||||
@@ -14,9 +19,23 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
noopLogger = zerolog.New(io.Discard)
|
||||
noopLogger = zerolog.New(io.Discard)
|
||||
managementHostname = "https://management.argotunnel.com"
|
||||
)
|
||||
|
||||
func TestDisableDiagnosticRoutes(t *testing.T) {
|
||||
mgmt := New("management.argotunnel.com", false, "1.1.1.1:80", uuid.Nil, "", &noopLogger, nil)
|
||||
for _, path := range []string{"/metrics", "/debug/pprof/goroutine", "/debug/pprof/heap"} {
|
||||
t.Run(strings.Replace(path, "/", "_", -1), func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", managementHostname+path+"?access_token="+validToken, nil)
|
||||
recorder := httptest.NewRecorder()
|
||||
mgmt.ServeHTTP(recorder, req)
|
||||
resp := recorder.Result()
|
||||
require.Equal(t, http.StatusNotFound, resp.StatusCode)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadEventsLoop(t *testing.T) {
|
||||
sentEvent := EventStartStreaming{
|
||||
ClientEvent: ClientEvent{Type: StartStreaming},
|
||||
@@ -59,3 +78,67 @@ func TestReadEventsLoop_ContextCancelled(t *testing.T) {
|
||||
m.readEvents(server, ctx, events)
|
||||
server.Close(websocket.StatusInternalError, "")
|
||||
}
|
||||
|
||||
func TestCanStartStream_NoSessions(t *testing.T) {
|
||||
m := ManagementService{
|
||||
log: &noopLogger,
|
||||
logger: &Logger{
|
||||
Log: &noopLogger,
|
||||
},
|
||||
}
|
||||
_, cancel := context.WithCancel(context.Background())
|
||||
session := newSession(0, actor{}, cancel)
|
||||
assert.True(t, m.canStartStream(session))
|
||||
}
|
||||
|
||||
func TestCanStartStream_ExistingSessionDifferentActor(t *testing.T) {
|
||||
m := ManagementService{
|
||||
log: &noopLogger,
|
||||
logger: &Logger{
|
||||
Log: &noopLogger,
|
||||
},
|
||||
}
|
||||
_, cancel := context.WithCancel(context.Background())
|
||||
session1 := newSession(0, actor{ID: "test"}, cancel)
|
||||
assert.True(t, m.canStartStream(session1))
|
||||
m.logger.Listen(session1)
|
||||
assert.True(t, session1.Active())
|
||||
|
||||
// Try another session
|
||||
session2 := newSession(0, actor{ID: "test2"}, cancel)
|
||||
assert.Equal(t, 1, m.logger.ActiveSessions())
|
||||
assert.False(t, m.canStartStream(session2))
|
||||
|
||||
// Close session1
|
||||
m.logger.Remove(session1)
|
||||
assert.True(t, session1.Active()) // Remove doesn't stop a session
|
||||
session1.Stop()
|
||||
assert.False(t, session1.Active())
|
||||
assert.Equal(t, 0, m.logger.ActiveSessions())
|
||||
|
||||
// Try session2 again
|
||||
assert.True(t, m.canStartStream(session2))
|
||||
}
|
||||
|
||||
func TestCanStartStream_ExistingSessionSameActor(t *testing.T) {
|
||||
m := ManagementService{
|
||||
log: &noopLogger,
|
||||
logger: &Logger{
|
||||
Log: &noopLogger,
|
||||
},
|
||||
}
|
||||
actor := actor{ID: "test"}
|
||||
_, cancel := context.WithCancel(context.Background())
|
||||
session1 := newSession(0, actor, cancel)
|
||||
assert.True(t, m.canStartStream(session1))
|
||||
m.logger.Listen(session1)
|
||||
assert.True(t, session1.Active())
|
||||
|
||||
// Try another session
|
||||
session2 := newSession(0, actor, cancel)
|
||||
assert.Equal(t, 1, m.logger.ActiveSessions())
|
||||
assert.True(t, m.canStartStream(session2))
|
||||
// session1 is removed and stopped
|
||||
assert.Equal(t, 0, m.logger.ActiveSessions())
|
||||
assert.False(t, session1.Active())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
package management
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
// Indicates how many log messages the listener will hold before dropping.
|
||||
// Provides a throttling mechanism to drop latest messages if the sender
|
||||
// can't keep up with the influx of log messages.
|
||||
logWindow = 30
|
||||
)
|
||||
|
||||
// session captures a streaming logs session for a connection of an actor.
|
||||
type session struct {
|
||||
// Indicates if the session is streaming or not. Modifying this will affect the active session.
|
||||
active atomic.Bool
|
||||
// Allows the session to control the context of the underlying connection to close it out when done. Mostly
|
||||
// used by the LoggerListener to close out and cleanup a session.
|
||||
cancel context.CancelFunc
|
||||
// Actor who started the session
|
||||
actor actor
|
||||
// Buffered channel that holds the recent log events
|
||||
listener chan *Log
|
||||
// Types of log events that this session will provide through the listener
|
||||
filters *StreamingFilters
|
||||
// Sampling of the log events this session will send (runs after all other filters if available)
|
||||
sampler *sampler
|
||||
}
|
||||
|
||||
// NewSession creates a new session.
|
||||
func newSession(size int, actor actor, cancel context.CancelFunc) *session {
|
||||
s := &session{
|
||||
active: atomic.Bool{},
|
||||
cancel: cancel,
|
||||
actor: actor,
|
||||
listener: make(chan *Log, size),
|
||||
filters: &StreamingFilters{},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Filters assigns the StreamingFilters to the session
|
||||
func (s *session) Filters(filters *StreamingFilters) {
|
||||
if filters != nil {
|
||||
s.filters = filters
|
||||
sampling := filters.Sampling
|
||||
// clamp the sampling values between 0 and 1
|
||||
if sampling < 0 {
|
||||
sampling = 0
|
||||
}
|
||||
if sampling > 1 {
|
||||
sampling = 1
|
||||
}
|
||||
s.filters.Sampling = sampling
|
||||
if sampling > 0 && sampling < 1 {
|
||||
s.sampler = &sampler{
|
||||
p: int(sampling * 100),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s.filters = &StreamingFilters{}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert attempts to insert the log to the session. If the log event matches the provided session filters, it
|
||||
// will be applied to the listener.
|
||||
func (s *session) Insert(log *Log) {
|
||||
// Level filters are optional
|
||||
if s.filters.Level != nil {
|
||||
if *s.filters.Level > log.Level {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Event filters are optional
|
||||
if len(s.filters.Events) != 0 && !contains(s.filters.Events, log.Event) {
|
||||
return
|
||||
}
|
||||
// Sampling is also optional
|
||||
if s.sampler != nil && !s.sampler.Sample() {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case s.listener <- log:
|
||||
default:
|
||||
// buffer is full, discard
|
||||
}
|
||||
}
|
||||
|
||||
// Active returns if the session is active
|
||||
func (s *session) Active() bool {
|
||||
return s.active.Load()
|
||||
}
|
||||
|
||||
// Stop will halt the session
|
||||
func (s *session) Stop() {
|
||||
s.active.Store(false)
|
||||
}
|
||||
|
||||
func contains(array []LogEventType, t LogEventType) bool {
|
||||
for _, v := range array {
|
||||
if v == t {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// sampler will send approximately every p percentage log events out of 100.
|
||||
type sampler struct {
|
||||
p int
|
||||
}
|
||||
|
||||
// Sample returns true if the event should be part of the sample, false if the event should be dropped.
|
||||
func (s *sampler) Sample() bool {
|
||||
return rand.Intn(100) <= s.p
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package management
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Validate the active states of the session
|
||||
func TestSession_ActiveControl(t *testing.T) {
|
||||
_, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
session := newSession(4, actor{}, cancel)
|
||||
// session starts out not active
|
||||
assert.False(t, session.Active())
|
||||
session.active.Store(true)
|
||||
assert.True(t, session.Active())
|
||||
session.Stop()
|
||||
assert.False(t, session.Active())
|
||||
}
|
||||
|
||||
// Validate that the session filters events
|
||||
func TestSession_Insert(t *testing.T) {
|
||||
_, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
infoLevel := new(LogLevel)
|
||||
*infoLevel = Info
|
||||
warnLevel := new(LogLevel)
|
||||
*warnLevel = Warn
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
filters StreamingFilters
|
||||
expectLog bool
|
||||
}{
|
||||
{
|
||||
name: "none",
|
||||
expectLog: true,
|
||||
},
|
||||
{
|
||||
name: "level",
|
||||
filters: StreamingFilters{
|
||||
Level: infoLevel,
|
||||
},
|
||||
expectLog: true,
|
||||
},
|
||||
{
|
||||
name: "filtered out level",
|
||||
filters: StreamingFilters{
|
||||
Level: warnLevel,
|
||||
},
|
||||
expectLog: false,
|
||||
},
|
||||
{
|
||||
name: "events",
|
||||
filters: StreamingFilters{
|
||||
Events: []LogEventType{HTTP},
|
||||
},
|
||||
expectLog: true,
|
||||
},
|
||||
{
|
||||
name: "filtered out event",
|
||||
filters: StreamingFilters{
|
||||
Events: []LogEventType{Cloudflared},
|
||||
},
|
||||
expectLog: false,
|
||||
},
|
||||
{
|
||||
name: "sampling",
|
||||
filters: StreamingFilters{
|
||||
Sampling: 0.9999999,
|
||||
},
|
||||
expectLog: true,
|
||||
},
|
||||
{
|
||||
name: "sampling (invalid negative)",
|
||||
filters: StreamingFilters{
|
||||
Sampling: -1.0,
|
||||
},
|
||||
expectLog: true,
|
||||
},
|
||||
{
|
||||
name: "sampling (invalid too large)",
|
||||
filters: StreamingFilters{
|
||||
Sampling: 2.0,
|
||||
},
|
||||
expectLog: true,
|
||||
},
|
||||
{
|
||||
name: "filter and event",
|
||||
filters: StreamingFilters{
|
||||
Level: infoLevel,
|
||||
Events: []LogEventType{HTTP},
|
||||
},
|
||||
expectLog: true,
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
session := newSession(4, actor{}, cancel)
|
||||
session.Filters(&test.filters)
|
||||
log := Log{
|
||||
Time: time.Now().UTC().Format(time.RFC3339),
|
||||
Event: HTTP,
|
||||
Level: Info,
|
||||
Message: "test",
|
||||
}
|
||||
session.Insert(&log)
|
||||
select {
|
||||
case <-session.listener:
|
||||
require.True(t, test.expectLog)
|
||||
default:
|
||||
require.False(t, test.expectLog)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that the session has a max amount of events to hold
|
||||
func TestSession_InsertOverflow(t *testing.T) {
|
||||
_, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
session := newSession(1, actor{}, cancel)
|
||||
log := Log{
|
||||
Time: time.Now().UTC().Format(time.RFC3339),
|
||||
Event: HTTP,
|
||||
Level: Info,
|
||||
Message: "test",
|
||||
}
|
||||
// Insert 2 but only max channel size for 1
|
||||
session.Insert(&log)
|
||||
session.Insert(&log)
|
||||
select {
|
||||
case <-session.listener:
|
||||
// pass
|
||||
default:
|
||||
require.Fail(t, "expected one log event")
|
||||
}
|
||||
// Second dequeue should fail
|
||||
select {
|
||||
case <-session.listener:
|
||||
require.Fail(t, "expected no more remaining log events")
|
||||
default:
|
||||
// pass
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package management
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-jose/go-jose/v3/jwt"
|
||||
)
|
||||
|
||||
type managementTokenClaims struct {
|
||||
Tunnel tunnel `json:"tun"`
|
||||
Actor actor `json:"actor"`
|
||||
}
|
||||
|
||||
// VerifyTunnel compares the tun claim isn't empty
|
||||
func (c *managementTokenClaims) verify() bool {
|
||||
return c.Tunnel.verify() && c.Actor.verify()
|
||||
}
|
||||
|
||||
type tunnel struct {
|
||||
ID string `json:"id"`
|
||||
AccountTag string `json:"account_tag"`
|
||||
}
|
||||
|
||||
// verify compares the tun claim isn't empty
|
||||
func (t *tunnel) verify() bool {
|
||||
return t.AccountTag != "" && t.ID != ""
|
||||
}
|
||||
|
||||
type actor struct {
|
||||
ID string `json:"id"`
|
||||
Support bool `json:"support"`
|
||||
}
|
||||
|
||||
// verify checks the ID claim isn't empty
|
||||
func (t *actor) verify() bool {
|
||||
return t.ID != ""
|
||||
}
|
||||
|
||||
func parseToken(token string) (*managementTokenClaims, error) {
|
||||
jwt, err := jwt.ParseSigned(token)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("malformed jwt: %v", err)
|
||||
}
|
||||
|
||||
var claims managementTokenClaims
|
||||
// This is actually safe because we verify the token in the edge before it reaches cloudflared
|
||||
err = jwt.UnsafeClaimsWithoutVerification(&claims)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("malformed jwt: %v", err)
|
||||
}
|
||||
if !claims.verify() {
|
||||
return nil, fmt.Errorf("invalid management token format provided")
|
||||
}
|
||||
return &claims, nil
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package management
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/go-jose/go-jose/v3"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
validToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.eyJ0dW4iOnsiaWQiOiI3YjA5ODE0OS01MWZlLTRlZTUtYTY4Ny0zZTM3NDQ2NmVmYzciLCJhY2NvdW50X3RhZyI6ImNkMzkxZTljMDYyNmE4Zjc2Y2IxZjY3MGY2NTkxYjA1In0sImFjdG9yIjp7ImlkIjoiZGNhcnJAY2xvdWRmbGFyZS5jb20iLCJzdXBwb3J0IjpmYWxzZX0sInJlcyI6WyJsb2dzIl0sImV4cCI6MTY3NzExNzY5NiwiaWF0IjoxNjc3MTE0MDk2LCJpc3MiOiJ0dW5uZWxzdG9yZSJ9.mKenOdOy3Xi4O-grldFnAAemdlE9WajEpTDC_FwezXQTstWiRTLwU65P5jt4vNsIiZA4OJRq7bH-QYID9wf9NA"
|
||||
|
||||
accountTag = "cd391e9c0626a8f76cb1f670f6591b05"
|
||||
tunnelID = "7b098149-51fe-4ee5-a687-3e374466efc7"
|
||||
actorID = "45d2751e-6b59-45a9-814d-f630786bd0cd"
|
||||
)
|
||||
|
||||
type invalidManagementTokenClaims struct {
|
||||
Invalid string `json:"invalid"`
|
||||
}
|
||||
|
||||
func TestParseToken(t *testing.T) {
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
claims any
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Valid",
|
||||
claims: managementTokenClaims{
|
||||
Tunnel: tunnel{
|
||||
ID: tunnelID,
|
||||
AccountTag: accountTag,
|
||||
},
|
||||
Actor: actor{
|
||||
ID: actorID,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Invalid claims",
|
||||
claims: invalidManagementTokenClaims{Invalid: "invalid"},
|
||||
err: errors.New("invalid management token format provided"),
|
||||
},
|
||||
{
|
||||
name: "Missing Tunnel",
|
||||
claims: managementTokenClaims{
|
||||
Actor: actor{
|
||||
ID: actorID,
|
||||
},
|
||||
},
|
||||
err: errors.New("invalid management token format provided"),
|
||||
},
|
||||
{
|
||||
name: "Missing Tunnel ID",
|
||||
claims: managementTokenClaims{
|
||||
Tunnel: tunnel{
|
||||
AccountTag: accountTag,
|
||||
},
|
||||
Actor: actor{
|
||||
ID: actorID,
|
||||
},
|
||||
},
|
||||
err: errors.New("invalid management token format provided"),
|
||||
},
|
||||
{
|
||||
name: "Missing Account Tag",
|
||||
claims: managementTokenClaims{
|
||||
Tunnel: tunnel{
|
||||
ID: tunnelID,
|
||||
},
|
||||
Actor: actor{
|
||||
ID: actorID,
|
||||
},
|
||||
},
|
||||
err: errors.New("invalid management token format provided"),
|
||||
},
|
||||
{
|
||||
name: "Missing Actor",
|
||||
claims: managementTokenClaims{
|
||||
Tunnel: tunnel{
|
||||
ID: tunnelID,
|
||||
AccountTag: accountTag,
|
||||
},
|
||||
},
|
||||
err: errors.New("invalid management token format provided"),
|
||||
},
|
||||
{
|
||||
name: "Missing Actor ID",
|
||||
claims: managementTokenClaims{
|
||||
Tunnel: tunnel{
|
||||
ID: tunnelID,
|
||||
},
|
||||
Actor: actor{},
|
||||
},
|
||||
err: errors.New("invalid management token format provided"),
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
jwt := signToken(t, test.claims, key)
|
||||
claims, err := parseToken(jwt)
|
||||
if test.err != nil {
|
||||
require.EqualError(t, err, test.err.Error())
|
||||
return
|
||||
}
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, test.claims, *claims)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func signToken(t *testing.T, token any, key *ecdsa.PrivateKey) string {
|
||||
opts := (&jose.SignerOptions{}).WithType("JWT").WithHeader("kid", "1")
|
||||
signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.ES256, Key: key}, opts)
|
||||
require.NoError(t, err)
|
||||
payload, err := json.Marshal(token)
|
||||
require.NoError(t, err)
|
||||
jws, err := signer.Sign(payload)
|
||||
require.NoError(t, err)
|
||||
jwt, err := jws.CompactSerialize()
|
||||
require.NoError(t, err)
|
||||
return jwt
|
||||
}
|
||||
@@ -28,8 +28,8 @@ type Orchestrator struct {
|
||||
lock sync.RWMutex
|
||||
// Underlying value is proxy.Proxy, can be read without the lock, but still needs the lock to update
|
||||
proxy atomic.Value
|
||||
// Set of local ingress rules defined at cloudflared startup (separate from user-defined ingress rules)
|
||||
localRules []ingress.Rule
|
||||
// Set of internal ingress rules defined at cloudflared startup (separate from user-defined ingress rules)
|
||||
internalRules []ingress.Rule
|
||||
warpRoutingEnabled atomic.Bool
|
||||
config *Config
|
||||
tags []tunnelpogs.Tag
|
||||
@@ -41,11 +41,13 @@ type Orchestrator struct {
|
||||
proxyShutdownC chan<- struct{}
|
||||
}
|
||||
|
||||
func NewOrchestrator(ctx context.Context, config *Config, tags []tunnelpogs.Tag, localRules []ingress.Rule, log *zerolog.Logger) (*Orchestrator, error) {
|
||||
func NewOrchestrator(ctx context.Context, config *Config, tags []tunnelpogs.Tag, internalRules []ingress.Rule, log *zerolog.Logger) (*Orchestrator, error) {
|
||||
o := &Orchestrator{
|
||||
// Lowest possible version, any remote configuration will have version higher than this
|
||||
currentVersion: 0,
|
||||
localRules: localRules,
|
||||
// Starting at -1 allows a configuration migration (local to remote) to override the current configuration as it
|
||||
// will start at version 0.
|
||||
currentVersion: -1,
|
||||
internalRules: internalRules,
|
||||
config: config,
|
||||
tags: tags,
|
||||
log: log,
|
||||
@@ -114,8 +116,13 @@ func (o *Orchestrator) updateIngress(ingressRules ingress.Ingress, warpRouting i
|
||||
default:
|
||||
}
|
||||
|
||||
// Assign the local ingress rules to the parsed ingress
|
||||
ingressRules.LocalRules = o.localRules
|
||||
// Assign the internal ingress rules to the parsed ingress
|
||||
ingressRules.InternalRules = o.internalRules
|
||||
|
||||
// Check if ingress rules are empty, and add the default route if so.
|
||||
if ingressRules.IsEmpty() {
|
||||
ingressRules.Rules = ingress.GetDefaultIngressRules(o.log)
|
||||
}
|
||||
|
||||
// Start new proxy before closing the ones from last version.
|
||||
// The upside is we don't need to restart proxy from last version, which can fail
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -14,6 +13,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gobwas/ws/wsutil"
|
||||
"github.com/google/uuid"
|
||||
gows "github.com/gorilla/websocket"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -50,7 +50,7 @@ func TestUpdateConfiguration(t *testing.T) {
|
||||
initConfig := &Config{
|
||||
Ingress: &ingress.Ingress{},
|
||||
}
|
||||
orchestrator, err := NewOrchestrator(context.Background(), initConfig, testTags, []ingress.Rule{ingress.NewManagementRule(management.New("management.argotunnel.com", &testLogger, nil))}, &testLogger)
|
||||
orchestrator, err := NewOrchestrator(context.Background(), initConfig, testTags, []ingress.Rule{ingress.NewManagementRule(management.New("management.argotunnel.com", false, "1.1.1.1:80", uuid.Nil, "", &testLogger, nil))}, &testLogger)
|
||||
require.NoError(t, err)
|
||||
initOriginProxy, err := orchestrator.GetOriginProxy()
|
||||
require.NoError(t, err)
|
||||
@@ -90,15 +90,15 @@ func TestUpdateConfiguration(t *testing.T) {
|
||||
"enabled": true,
|
||||
"connectTimeout": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
updateWithValidation(t, orchestrator, 2, configJSONV2)
|
||||
configV2 := orchestrator.config
|
||||
// Validate local ingress rules
|
||||
require.Equal(t, "management.argotunnel.com", configV2.Ingress.LocalRules[0].Hostname)
|
||||
require.True(t, configV2.Ingress.LocalRules[0].Matches("management.argotunnel.com", "/ping"))
|
||||
require.Equal(t, "management", configV2.Ingress.LocalRules[0].Service.String())
|
||||
// Validate internal ingress rules
|
||||
require.Equal(t, "management.argotunnel.com", configV2.Ingress.InternalRules[0].Hostname)
|
||||
require.True(t, configV2.Ingress.InternalRules[0].Matches("management.argotunnel.com", "/ping"))
|
||||
require.Equal(t, "management", configV2.Ingress.InternalRules[0].Service.String())
|
||||
// Validate ingress rule 0
|
||||
require.Equal(t, "jira.tunnel.org", configV2.Ingress.Rules[0].Hostname)
|
||||
require.True(t, configV2.Ingress.Rules[0].Matches("jira.tunnel.org", "/login"))
|
||||
@@ -144,7 +144,7 @@ func TestUpdateConfiguration(t *testing.T) {
|
||||
{
|
||||
"originRequest":
|
||||
}
|
||||
|
||||
|
||||
`)
|
||||
|
||||
resp = orchestrator.UpdateConfig(3, invalidJSON)
|
||||
@@ -164,7 +164,7 @@ func TestUpdateConfiguration(t *testing.T) {
|
||||
"warp-routing": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
updateWithValidation(t, orchestrator, 10, configJSONV10)
|
||||
configV10 := orchestrator.config
|
||||
@@ -180,6 +180,57 @@ func TestUpdateConfiguration(t *testing.T) {
|
||||
require.NotEqual(t, originProxyV10, originProxyV2)
|
||||
}
|
||||
|
||||
// Validates that a new version 0 will be applied if the configuration is loaded locally.
|
||||
// This will happen when a locally managed tunnel is migrated to remote configuration and receives its first configuration.
|
||||
func TestUpdateConfiguration_FromMigration(t *testing.T) {
|
||||
initConfig := &Config{
|
||||
Ingress: &ingress.Ingress{},
|
||||
}
|
||||
orchestrator, err := NewOrchestrator(context.Background(), initConfig, testTags, []ingress.Rule{}, &testLogger)
|
||||
require.NoError(t, err)
|
||||
initOriginProxy, err := orchestrator.GetOriginProxy()
|
||||
require.NoError(t, err)
|
||||
require.Implements(t, (*connection.OriginProxy)(nil), initOriginProxy)
|
||||
require.False(t, orchestrator.WarpRoutingEnabled())
|
||||
|
||||
configJSONV2 := []byte(`
|
||||
{
|
||||
"ingress": [
|
||||
{
|
||||
"service": "http_status:404"
|
||||
}
|
||||
],
|
||||
"warp-routing": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
`)
|
||||
updateWithValidation(t, orchestrator, 0, configJSONV2)
|
||||
require.Len(t, orchestrator.config.Ingress.Rules, 1)
|
||||
}
|
||||
|
||||
// Validates that the default ingress rule will be set if there is no rule provided from the remote.
|
||||
func TestUpdateConfiguration_WithoutIngressRule(t *testing.T) {
|
||||
initConfig := &Config{
|
||||
Ingress: &ingress.Ingress{},
|
||||
}
|
||||
orchestrator, err := NewOrchestrator(context.Background(), initConfig, testTags, []ingress.Rule{}, &testLogger)
|
||||
require.NoError(t, err)
|
||||
initOriginProxy, err := orchestrator.GetOriginProxy()
|
||||
require.NoError(t, err)
|
||||
require.Implements(t, (*connection.OriginProxy)(nil), initOriginProxy)
|
||||
|
||||
// We need to create an empty RemoteConfigJSON because that will get unmarshalled to a RemoteConfig
|
||||
emptyConfig := &ingress.RemoteConfigJSON{}
|
||||
configBytes, err := json.Marshal(emptyConfig)
|
||||
if err != nil {
|
||||
require.FailNow(t, "The RemoteConfigJSON shouldn't fail while being marshalled")
|
||||
}
|
||||
|
||||
updateWithValidation(t, orchestrator, 0, configBytes)
|
||||
require.Len(t, orchestrator.config.Ingress.Rules, 1)
|
||||
}
|
||||
|
||||
// TestConcurrentUpdateAndRead makes sure orchestrator can receive updates and return origin proxy concurrently
|
||||
func TestConcurrentUpdateAndRead(t *testing.T) {
|
||||
const (
|
||||
@@ -286,7 +337,7 @@ func TestConcurrentUpdateAndRead(t *testing.T) {
|
||||
switch resp.StatusCode {
|
||||
// v1 proxy, warp enabled
|
||||
case 200:
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, t.Name(), string(body))
|
||||
warpRoutingDisabled = false
|
||||
@@ -398,7 +449,7 @@ func proxyTCP(ctx context.Context, originProxy connection.OriginProxy, originAdd
|
||||
CFRay: "123",
|
||||
LBProbe: false,
|
||||
}
|
||||
rws := connection.NewHTTPResponseReadWriterAcker(respWriter, req)
|
||||
rws := connection.NewHTTPResponseReadWriterAcker(respWriter, w.(http.Flusher), req)
|
||||
|
||||
return originProxy.ProxyTCP(ctx, rws, tcpReq)
|
||||
}
|
||||
|
||||
@@ -43,6 +43,22 @@ var (
|
||||
Help: "Count of error proxying to origin",
|
||||
},
|
||||
)
|
||||
activeTCPSessions = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: connection.MetricsNamespace,
|
||||
Subsystem: "tcp",
|
||||
Name: "active_sessions",
|
||||
Help: "Concurrent count of TCP sessions that are being proxied to any origin",
|
||||
},
|
||||
)
|
||||
totalTCPSessions = prometheus.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: connection.MetricsNamespace,
|
||||
Subsystem: "tcp",
|
||||
Name: "total_sessions",
|
||||
Help: "Total count of TCP sessions that have been proxied to any origin",
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -51,6 +67,8 @@ func init() {
|
||||
concurrentRequests,
|
||||
responseByCode,
|
||||
requestErrors,
|
||||
activeTCPSessions,
|
||||
totalTCPSessions,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -62,3 +80,14 @@ func incrementRequests() {
|
||||
func decrementConcurrentRequests() {
|
||||
concurrentRequests.Dec()
|
||||
}
|
||||
|
||||
func incrementTCPRequests() {
|
||||
incrementRequests()
|
||||
totalTCPSessions.Inc()
|
||||
activeTCPSessions.Inc()
|
||||
}
|
||||
|
||||
func decrementTCPConcurrentRequests() {
|
||||
decrementConcurrentRequests()
|
||||
activeTCPSessions.Dec()
|
||||
}
|
||||
|
||||
+9
-4
@@ -136,8 +136,11 @@ func (p *Proxy) ProxyHTTP(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rws := connection.NewHTTPResponseReadWriterAcker(w, req)
|
||||
flusher, ok := w.(http.Flusher)
|
||||
if !ok {
|
||||
return fmt.Errorf("response writer is not a flusher")
|
||||
}
|
||||
rws := connection.NewHTTPResponseReadWriterAcker(w, flusher, req)
|
||||
if err := p.proxyStream(tr.ToTracedContext(), rws, dest, originProxy); err != nil {
|
||||
rule, srv := ruleField(p.ingressRules, ruleNum)
|
||||
p.logRequestError(err, cfRay, "", rule, srv)
|
||||
@@ -158,8 +161,8 @@ func (p *Proxy) ProxyTCP(
|
||||
rwa connection.ReadWriteAcker,
|
||||
req *connection.TCPRequest,
|
||||
) error {
|
||||
incrementRequests()
|
||||
defer decrementConcurrentRequests()
|
||||
incrementTCPRequests()
|
||||
defer decrementTCPConcurrentRequests()
|
||||
|
||||
if p.warpRouting == nil {
|
||||
err := errors.New(`cloudflared received a request from WARP client, but your configuration has disabled ingress from WARP clients. To enable this, set "warp-routing:\n\t enabled: true" in your config.yaml`)
|
||||
@@ -173,6 +176,7 @@ func (p *Proxy) ProxyTCP(
|
||||
tracedCtx := tracing.NewTracedContext(serveCtx, req.CfTraceID, p.log)
|
||||
|
||||
p.log.Debug().
|
||||
Int(management.EventTypeKey, int(management.TCP)).
|
||||
Str(LogFieldFlowID, req.FlowID).
|
||||
Str(LogFieldDestAddr, req.Dest).
|
||||
Uint8(LogFieldConnIndex, req.ConnIndex).
|
||||
@@ -184,6 +188,7 @@ func (p *Proxy) ProxyTCP(
|
||||
}
|
||||
|
||||
p.log.Debug().
|
||||
Int(management.EventTypeKey, int(management.TCP)).
|
||||
Str(LogFieldFlowID, req.FlowID).
|
||||
Str(LogFieldDestAddr, req.Dest).
|
||||
Uint8(LogFieldConnIndex, req.ConnIndex).
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -17,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
func TestUnixSocketOrigin(t *testing.T) {
|
||||
file, err := ioutil.TempFile("", "unix.sock")
|
||||
file, err := os.CreateTemp("", "unix.sock")
|
||||
require.NoError(t, err)
|
||||
os.Remove(file.Name()) // remove the file since binding the socket expects to create it
|
||||
|
||||
|
||||
+5
-1
@@ -698,7 +698,7 @@ func TestConnections(t *testing.T) {
|
||||
}()
|
||||
}
|
||||
if test.args.connectionType == connection.TypeTCP {
|
||||
rwa := connection.NewHTTPResponseReadWriterAcker(respWriter, req)
|
||||
rwa := connection.NewHTTPResponseReadWriterAcker(respWriter, respWriter.(http.Flusher), req)
|
||||
err = proxy.ProxyTCP(ctx, rwa, &connection.TCPRequest{Dest: dest})
|
||||
} else {
|
||||
log := zerolog.Nop()
|
||||
@@ -834,6 +834,8 @@ func (w *wsRespWriter) WriteRespHeaders(status int, header http.Header) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *wsRespWriter) Flush() {}
|
||||
|
||||
func (w *wsRespWriter) AddTrailer(trailerName, trailerValue string) {
|
||||
// do nothing
|
||||
}
|
||||
@@ -873,6 +875,8 @@ func (m *mockTCPRespWriter) Write(p []byte) (n int, err error) {
|
||||
return m.w.Write(p)
|
||||
}
|
||||
|
||||
func (m *mockTCPRespWriter) Flush() {}
|
||||
|
||||
func (m *mockTCPRespWriter) AddTrailer(trailerName, trailerValue string) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
+4
-4
@@ -4,7 +4,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/logging"
|
||||
"github.com/quic-go/quic-go/logging"
|
||||
)
|
||||
|
||||
func perspectiveString(p logging.Perspective) string {
|
||||
@@ -28,7 +28,7 @@ func durationToPromGauge(duration time.Duration) float64 {
|
||||
return float64(duration.Milliseconds())
|
||||
}
|
||||
|
||||
// Helper to convert https://pkg.go.dev/github.com/lucas-clemente/quic-go@v0.23.0/logging#PacketType into string
|
||||
// Helper to convert https://pkg.go.dev/github.com/quic-go/quic-go@v0.23.0/logging#PacketType into string
|
||||
func packetTypeString(pt logging.PacketType) string {
|
||||
switch pt {
|
||||
case logging.PacketTypeInitial:
|
||||
@@ -52,7 +52,7 @@ func packetTypeString(pt logging.PacketType) string {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to convert https://pkg.go.dev/github.com/lucas-clemente/quic-go@v0.23.0/logging#PacketDropReason into string
|
||||
// Helper to convert https://pkg.go.dev/github.com/quic-go/quic-go@v0.23.0/logging#PacketDropReason into string
|
||||
func packetDropReasonString(reason logging.PacketDropReason) string {
|
||||
switch reason {
|
||||
case logging.PacketDropKeyUnavailable:
|
||||
@@ -82,7 +82,7 @@ func packetDropReasonString(reason logging.PacketDropReason) string {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to convert https://pkg.go.dev/github.com/lucas-clemente/quic-go@v0.23.0/logging#PacketLossReason into string
|
||||
// Helper to convert https://pkg.go.dev/github.com/quic-go/quic-go@v0.23.0/logging#PacketLossReason into string
|
||||
func packetLossReasonString(reason logging.PacketLossReason) string {
|
||||
switch reason {
|
||||
case logging.PacketLossReorderingThreshold:
|
||||
|
||||
+1
-1
@@ -5,8 +5,8 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"github.com/cloudflare/cloudflared/packet"
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/google/uuid"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/icmp"
|
||||
@@ -180,8 +180,10 @@ func testDatagram(t *testing.T, version uint8, sessionToPayloads []*packet.Sessi
|
||||
InsecureSkipVerify: true,
|
||||
NextProtos: []string{"argotunnel"},
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Establish quic connection
|
||||
quicSession, err := quic.DialAddrEarly(quicListener.Addr().String(), tlsClientConfig, quicConfig)
|
||||
quicSession, err := quic.DialAddrEarly(ctx, quicListener.Addr().String(), tlsClientConfig, quicConfig)
|
||||
require.NoError(t, err)
|
||||
defer quicSession.CloseWithError(0, "")
|
||||
|
||||
@@ -264,7 +266,7 @@ func validateTracingSpans(t *testing.T, receivedPacket Packet, expectedSpan *Tra
|
||||
require.Equal(t, tracingSpans, expectedSpan)
|
||||
}
|
||||
|
||||
func newQUICListener(t *testing.T, config *quic.Config) quic.Listener {
|
||||
func newQUICListener(t *testing.T, config *quic.Config) *quic.Listener {
|
||||
// Create a simple tls config.
|
||||
tlsConfig := generateTLSConfig()
|
||||
|
||||
|
||||
+1
-1
@@ -4,8 +4,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"github.com/cloudflare/cloudflared/packet"
|
||||
|
||||
+1
-1
@@ -3,8 +3,8 @@ package quic
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/logging"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/quic-go/quic-go/logging"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
package quic
|
||||
|
||||
const (
|
||||
// Due to https://github.com/lucas-clemente/quic-go/issues/3273, MTU discovery is disabled on Windows
|
||||
// 1220 is the default value https://github.com/lucas-clemente/quic-go/blob/84e03e59760ceee37359688871bb0688fcc4e98f/internal/protocol/params.go#L138
|
||||
// Due to https://github.com/quic-go/quic-go/issues/3273, MTU discovery is disabled on Windows
|
||||
// 1220 is the default value https://github.com/quic-go/quic-go/blob/84e03e59760ceee37359688871bb0688fcc4e98f/internal/protocol/params.go#L138
|
||||
MaxDatagramFrameSize = 1220
|
||||
// 3 more bytes are reserved at https://github.com/lucas-clemente/quic-go/blob/v0.24.0/internal/wire/datagram_frame.go#L61
|
||||
// 3 more bytes are reserved at https://github.com/quic-go/quic-go/blob/v0.24.0/internal/wire/datagram_frame.go#L61
|
||||
maxDatagramPayloadSize = MaxDatagramFrameSize - 3 - sessionIDLen - typeIDLen
|
||||
)
|
||||
|
||||
+12
-3
@@ -39,6 +39,9 @@ const (
|
||||
HandshakeIdleTimeout = 5 * time.Second
|
||||
MaxIdleTimeout = 5 * time.Second
|
||||
MaxIdlePingPeriod = 1 * time.Second
|
||||
|
||||
// MaxIncomingStreams is 2^60, which is the maximum supported value by Quic-Go
|
||||
MaxIncomingStreams = 1 << 60
|
||||
)
|
||||
|
||||
// RequestServerStream is a stream to serve requests
|
||||
@@ -226,9 +229,12 @@ func writeSignature(stream io.Writer, signature ProtocolSignature) error {
|
||||
type RPCClientStream struct {
|
||||
client tunnelpogs.CloudflaredServer_PogsClient
|
||||
transport rpc.Transport
|
||||
|
||||
// Time we wait for the server to respond to a request before we close the connection.
|
||||
rpcUnregisterUDPSessionDeadline time.Duration
|
||||
}
|
||||
|
||||
func NewRPCClientStream(ctx context.Context, stream io.ReadWriteCloser, logger *zerolog.Logger) (*RPCClientStream, error) {
|
||||
func NewRPCClientStream(ctx context.Context, stream io.ReadWriteCloser, rpcUnregisterUDPSessionDeadline time.Duration, logger *zerolog.Logger) (*RPCClientStream, error) {
|
||||
n, err := stream.Write(RPCStreamProtocolSignature[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -242,8 +248,9 @@ func NewRPCClientStream(ctx context.Context, stream io.ReadWriteCloser, logger *
|
||||
tunnelrpc.ConnLog(logger),
|
||||
)
|
||||
return &RPCClientStream{
|
||||
client: tunnelpogs.NewCloudflaredServer_PogsClient(conn.Bootstrap(ctx), conn),
|
||||
transport: transport,
|
||||
client: tunnelpogs.NewCloudflaredServer_PogsClient(conn.Bootstrap(ctx), conn),
|
||||
transport: transport,
|
||||
rpcUnregisterUDPSessionDeadline: rpcUnregisterUDPSessionDeadline,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -252,6 +259,8 @@ func (rcs *RPCClientStream) RegisterUdpSession(ctx context.Context, sessionID uu
|
||||
}
|
||||
|
||||
func (rcs *RPCClientStream) UnregisterUdpSession(ctx context.Context, sessionID uuid.UUID, message string) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, rcs.rpcUnregisterUDPSessionDeadline)
|
||||
defer cancel()
|
||||
return rcs.client.UnregisterUdpSession(ctx, sessionID, message)
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,63 @@ func TestConnectResponseMeta(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnregisterUdpSession(t *testing.T) {
|
||||
unregisterMessage := "closed by eyeball"
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
sessionRPCServer mockSessionRPCServer
|
||||
timeout time.Duration
|
||||
}{
|
||||
|
||||
{
|
||||
name: "UnregisterUdpSessionTimesout if the RPC server does not respond",
|
||||
sessionRPCServer: mockSessionRPCServer{
|
||||
sessionID: uuid.New(),
|
||||
dstIP: net.IP{172, 16, 0, 1},
|
||||
dstPort: 8000,
|
||||
closeIdleAfter: testCloseIdleAfterHint,
|
||||
unregisterMessage: unregisterMessage,
|
||||
traceContext: "1241ce3ecdefc68854e8514e69ba42ca:b38f1bf5eae406f3:0:1",
|
||||
},
|
||||
// very very low value so we trigger the timeout every time.
|
||||
timeout: time.Nanosecond * 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
logger := zerolog.Nop()
|
||||
clientStream, serverStream := newMockRPCStreams()
|
||||
sessionRegisteredChan := make(chan struct{})
|
||||
go func() {
|
||||
protocol, err := DetermineProtocol(serverStream)
|
||||
assert.NoError(t, err)
|
||||
rpcServerStream, err := NewRPCServerStream(serverStream, protocol)
|
||||
assert.NoError(t, err)
|
||||
err = rpcServerStream.Serve(test.sessionRPCServer, nil, &logger)
|
||||
assert.NoError(t, err)
|
||||
|
||||
serverStream.Close()
|
||||
close(sessionRegisteredChan)
|
||||
}()
|
||||
|
||||
rpcClientStream, err := NewRPCClientStream(context.Background(), clientStream, test.timeout, &logger)
|
||||
assert.NoError(t, err)
|
||||
|
||||
reg, err := rpcClientStream.RegisterUdpSession(context.Background(), test.sessionRPCServer.sessionID, test.sessionRPCServer.dstIP, test.sessionRPCServer.dstPort, testCloseIdleAfterHint, test.sessionRPCServer.traceContext)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, reg.Err)
|
||||
|
||||
assert.Error(t, rpcClientStream.UnregisterUdpSession(context.Background(), test.sessionRPCServer.sessionID, unregisterMessage))
|
||||
|
||||
rpcClientStream.Close()
|
||||
<-sessionRegisteredChan
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRegisterUdpSession(t *testing.T) {
|
||||
unregisterMessage := "closed by eyeball"
|
||||
|
||||
@@ -157,7 +214,7 @@ func TestRegisterUdpSession(t *testing.T) {
|
||||
close(sessionRegisteredChan)
|
||||
}()
|
||||
|
||||
rpcClientStream, err := NewRPCClientStream(context.Background(), clientStream, &logger)
|
||||
rpcClientStream, err := NewRPCClientStream(context.Background(), clientStream, 5*time.Second, &logger)
|
||||
assert.NoError(t, err)
|
||||
|
||||
reg, err := rpcClientStream.RegisterUdpSession(context.Background(), test.sessionRPCServer.sessionID, test.sessionRPCServer.dstIP, test.sessionRPCServer.dstPort, testCloseIdleAfterHint, test.sessionRPCServer.traceContext)
|
||||
@@ -208,7 +265,7 @@ func TestManageConfiguration(t *testing.T) {
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
rpcClientStream, err := NewRPCClientStream(ctx, clientStream, &logger)
|
||||
rpcClientStream, err := NewRPCClientStream(ctx, clientStream, 5*time.Second, &logger)
|
||||
assert.NoError(t, err)
|
||||
|
||||
result, err := rpcClientStream.UpdateConfiguration(ctx, version, config)
|
||||
|
||||
+5
-1
@@ -4,7 +4,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/quic-go/quic-go"
|
||||
)
|
||||
|
||||
type SafeStreamCloser struct {
|
||||
@@ -52,3 +52,7 @@ func (s *SafeStreamCloser) CloseWrite() error {
|
||||
// We can still read from this stream.
|
||||
return s.stream.Close()
|
||||
}
|
||||
|
||||
func (s *SafeStreamCloser) SetDeadline(deadline time.Time) error {
|
||||
return s.stream.SetDeadline(deadline)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -56,7 +56,9 @@ func quicClient(t *testing.T, addr net.Addr) {
|
||||
InsecureSkipVerify: true,
|
||||
NextProtos: []string{"argotunnel"},
|
||||
}
|
||||
session, err := quic.DialAddr(addr.String(), tlsConf, testQUICConfig)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
session, err := quic.DialAddr(ctx, addr.String(), tlsConf, testQUICConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
+22
-6
@@ -5,7 +5,7 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/logging"
|
||||
"github.com/quic-go/quic-go/logging"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
@@ -21,14 +21,15 @@ type tracerConfig struct {
|
||||
index uint8
|
||||
}
|
||||
|
||||
func NewClientTracer(logger *zerolog.Logger, index uint8) logging.Tracer {
|
||||
return &tracer{
|
||||
func NewClientTracer(logger *zerolog.Logger, index uint8) func(context.Context, logging.Perspective, logging.ConnectionID) logging.ConnectionTracer {
|
||||
t := &tracer{
|
||||
logger: logger,
|
||||
config: &tracerConfig{
|
||||
isClient: true,
|
||||
index: index,
|
||||
},
|
||||
}
|
||||
return t.TracerForConnection
|
||||
}
|
||||
|
||||
func NewServerTracer(logger *zerolog.Logger) logging.Tracer {
|
||||
@@ -47,7 +48,10 @@ func (t *tracer) TracerForConnection(_ctx context.Context, _p logging.Perspectiv
|
||||
return newConnTracer(newServiceCollector())
|
||||
}
|
||||
|
||||
func (*tracer) SentPacket(net.Addr, *logging.Header, logging.ByteCount, []logging.Frame) {}
|
||||
func (*tracer) SentPacket(net.Addr, *logging.Header, logging.ByteCount, []logging.Frame) {
|
||||
}
|
||||
func (*tracer) SentVersionNegotiationPacket(_ net.Addr, dest, src logging.ArbitraryLenConnectionID, _ []logging.VersionNumber) {
|
||||
}
|
||||
func (*tracer) DroppedPacket(net.Addr, logging.PacketType, logging.ByteCount, logging.PacketDropReason) {
|
||||
}
|
||||
|
||||
@@ -82,7 +86,7 @@ func (ct *connTracer) ReceivedPacket(hdr *logging.ExtendedHeader, size logging.B
|
||||
ct.metricsCollector.receivedPackets(size)
|
||||
}
|
||||
|
||||
func (ct *connTracer) BufferedPacket(pt logging.PacketType) {
|
||||
func (ct *connTracer) BufferedPacket(pt logging.PacketType, size logging.ByteCount) {
|
||||
ct.metricsCollector.bufferedPackets(pt)
|
||||
}
|
||||
|
||||
@@ -110,12 +114,24 @@ func (ct *connTracer) ReceivedTransportParameters(parameters *logging.TransportP
|
||||
func (ct *connTracer) RestoredTransportParameters(parameters *logging.TransportParameters) {
|
||||
}
|
||||
|
||||
func (ct *connTracer) ReceivedVersionNegotiationPacket(header *logging.Header, numbers []logging.VersionNumber) {
|
||||
func (ct *connTracer) SentLongHeaderPacket(hdr *logging.ExtendedHeader, size logging.ByteCount, ack *logging.AckFrame, frames []logging.Frame) {
|
||||
}
|
||||
|
||||
func (ct *connTracer) SentShortHeaderPacket(hdr *logging.ShortHeader, size logging.ByteCount, ack *logging.AckFrame, frames []logging.Frame) {
|
||||
}
|
||||
|
||||
func (ct *connTracer) ReceivedVersionNegotiationPacket(dest, src logging.ArbitraryLenConnectionID, _ []logging.VersionNumber) {
|
||||
}
|
||||
|
||||
func (ct *connTracer) ReceivedRetry(header *logging.Header) {
|
||||
}
|
||||
|
||||
func (ct *connTracer) ReceivedLongHeaderPacket(hdr *logging.ExtendedHeader, size logging.ByteCount, frames []logging.Frame) {
|
||||
}
|
||||
|
||||
func (ct *connTracer) ReceivedShortHeaderPacket(hdr *logging.ShortHeader, size logging.ByteCount, frames []logging.Frame) {
|
||||
}
|
||||
|
||||
func (ct *connTracer) AcknowledgedPacket(level logging.EncryptionLevel, number logging.PacketNumber) {
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package socks
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
@@ -32,7 +32,7 @@ func sendSocksRequest(t *testing.T) []byte {
|
||||
assert.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
|
||||
return b
|
||||
|
||||
+4
-4
@@ -10,15 +10,15 @@ import (
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/go-jose/go-jose/v3/jwt"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"github.com/pkg/errors"
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
"gopkg.in/square/go-jose.v2/jwt"
|
||||
|
||||
"github.com/cloudflare/cloudflared/config"
|
||||
cfpath "github.com/cloudflare/cloudflared/token"
|
||||
@@ -148,7 +148,7 @@ func generateKeyPair(fullName string) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
if exist {
|
||||
return ioutil.ReadFile(pubKeyName)
|
||||
return os.ReadFile(pubKeyName)
|
||||
}
|
||||
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
@@ -187,5 +187,5 @@ func writeKey(filename string, data []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filepath, data, 0600)
|
||||
return os.WriteFile(filepath, data, 0600)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
@@ -16,9 +15,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-jose/go-jose/v3"
|
||||
"github.com/go-jose/go-jose/v3/jwt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
"gopkg.in/square/go-jose.v2/jwt"
|
||||
|
||||
"github.com/cloudflare/cloudflared/config"
|
||||
cfpath "github.com/cloudflare/cloudflared/token"
|
||||
@@ -64,7 +63,7 @@ func TestCertGenSuccess(t *testing.T) {
|
||||
mockRequest = func(url, contentType string, body io.Reader) (*http.Response, error) {
|
||||
assert.Contains(t, "/cdn-cgi/access/cert_sign", url)
|
||||
assert.Equal(t, "application/json", contentType)
|
||||
buf, err := ioutil.ReadAll(body)
|
||||
buf, err := io.ReadAll(body)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, buf)
|
||||
return w.Result(), nil
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
// DebugStream will tee each read and write to the output logger as a debug message
|
||||
type DebugStream struct {
|
||||
reader io.Reader
|
||||
writer io.Writer
|
||||
log *zerolog.Logger
|
||||
max uint64
|
||||
count atomic.Uint64
|
||||
}
|
||||
|
||||
func NewDebugStream(stream io.ReadWriter, logger *zerolog.Logger, max uint64) *DebugStream {
|
||||
return &DebugStream{
|
||||
reader: stream,
|
||||
writer: stream,
|
||||
log: logger,
|
||||
max: max,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DebugStream) Read(p []byte) (n int, err error) {
|
||||
n, err = d.reader.Read(p)
|
||||
if n > 0 && d.max > d.count.Load() {
|
||||
d.count.Add(1)
|
||||
if err != nil {
|
||||
d.log.Err(err).
|
||||
Str("dir", "r").
|
||||
Int("count", n).
|
||||
Msgf("%+q", p[:n])
|
||||
} else {
|
||||
d.log.Debug().
|
||||
Str("dir", "r").
|
||||
Int("count", n).
|
||||
Msgf("%+q", p[:n])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *DebugStream) Write(p []byte) (n int, err error) {
|
||||
n, err = d.writer.Write(p)
|
||||
if n > 0 && d.max > d.count.Load() {
|
||||
d.count.Add(1)
|
||||
if err != nil {
|
||||
d.log.Err(err).
|
||||
Str("dir", "w").
|
||||
Int("count", n).
|
||||
Msgf("%+q", p[:n])
|
||||
} else {
|
||||
d.log.Debug().
|
||||
Str("dir", "w").
|
||||
Int("count", n).
|
||||
Msgf("%+q", p[:n])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
+75
-24
@@ -8,13 +8,43 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/raven-go"
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"github.com/cloudflare/cloudflared/cfio"
|
||||
)
|
||||
|
||||
type Stream interface {
|
||||
Reader
|
||||
WriterCloser
|
||||
}
|
||||
|
||||
type Reader interface {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
type WriterCloser interface {
|
||||
io.Writer
|
||||
WriteCloser
|
||||
}
|
||||
|
||||
type WriteCloser interface {
|
||||
CloseWrite() error
|
||||
}
|
||||
|
||||
type nopCloseWriterAdapter struct {
|
||||
io.ReadWriter
|
||||
}
|
||||
|
||||
func NopCloseWriterAdapter(stream io.ReadWriter) *nopCloseWriterAdapter {
|
||||
return &nopCloseWriterAdapter{stream}
|
||||
}
|
||||
|
||||
func (n *nopCloseWriterAdapter) CloseWrite() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type bidirectionalStreamStatus struct {
|
||||
doneChan chan struct{}
|
||||
anyDone uint32
|
||||
@@ -32,8 +62,24 @@ func (s *bidirectionalStreamStatus) markUniStreamDone() {
|
||||
s.doneChan <- struct{}{}
|
||||
}
|
||||
|
||||
func (s *bidirectionalStreamStatus) waitAnyDone() {
|
||||
func (s *bidirectionalStreamStatus) wait(maxWaitForSecondStream time.Duration) error {
|
||||
<-s.doneChan
|
||||
|
||||
// Only wait for second stream to finish if maxWait is greater than zero
|
||||
if maxWaitForSecondStream > 0 {
|
||||
|
||||
timer := time.NewTimer(maxWaitForSecondStream)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case <-timer.C:
|
||||
return fmt.Errorf("timeout waiting for second stream to finish")
|
||||
case <-s.doneChan:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func (s *bidirectionalStreamStatus) isAnyDone() bool {
|
||||
return atomic.LoadUint32(&s.anyDone) > 0
|
||||
@@ -41,16 +87,28 @@ func (s *bidirectionalStreamStatus) isAnyDone() bool {
|
||||
|
||||
// Pipe copies copy data to & from provided io.ReadWriters.
|
||||
func Pipe(tunnelConn, originConn io.ReadWriter, log *zerolog.Logger) {
|
||||
status := newBiStreamStatus()
|
||||
|
||||
go unidirectionalStream(tunnelConn, originConn, "origin->tunnel", status, log)
|
||||
go unidirectionalStream(originConn, tunnelConn, "tunnel->origin", status, log)
|
||||
|
||||
// If one side is done, we are done.
|
||||
status.waitAnyDone()
|
||||
PipeBidirectional(NopCloseWriterAdapter(tunnelConn), NopCloseWriterAdapter(originConn), 0, log)
|
||||
}
|
||||
|
||||
func unidirectionalStream(dst io.Writer, src io.Reader, dir string, status *bidirectionalStreamStatus, log *zerolog.Logger) {
|
||||
// PipeBidirectional copies data two BidirectionStreams. It is a special case of Pipe where it receives a concept that allows for Read and Write side to be closed independently.
|
||||
// The main difference is that when piping data from a reader to a writer, if EOF is read, then this implementation propagates the EOF signal to the destination/writer by closing the write side of the
|
||||
// Bidirectional Stream.
|
||||
// Finally, depending on once EOF is ready from one of the provided streams, the other direction of streaming data will have a configured time period to also finish, otherwise,
|
||||
// the method will return immediately with a timeout error. It is however, the responsability of the caller to close the associated streams in both ends in order to free all the resources/go-routines.
|
||||
func PipeBidirectional(downstream, upstream Stream, maxWaitForSecondStream time.Duration, log *zerolog.Logger) error {
|
||||
status := newBiStreamStatus()
|
||||
|
||||
go unidirectionalStream(downstream, upstream, "upstream->downstream", status, log)
|
||||
go unidirectionalStream(upstream, downstream, "downstream->upstream", status, log)
|
||||
|
||||
if err := status.wait(maxWaitForSecondStream); err != nil {
|
||||
return errors.Wrap(err, "unable to wait for both streams while proxying")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func unidirectionalStream(dst WriterCloser, src Reader, dir string, status *bidirectionalStreamStatus, log *zerolog.Logger) {
|
||||
defer func() {
|
||||
// The bidirectional streaming spawns 2 goroutines to stream each direction.
|
||||
// If any ends, the callstack returns, meaning the Tunnel request/stream (depending on http2 vs quic) will
|
||||
@@ -58,28 +116,21 @@ func unidirectionalStream(dst io.Writer, src io.Reader, dir string, status *bidi
|
||||
// server/origin listens forever until closure), it may read/write from the underlying ReadWriter (backed by
|
||||
// the Edge<->cloudflared transport) in an unexpected state.
|
||||
// Because of this, we set this recover() logic.
|
||||
if r := recover(); r != nil {
|
||||
if err := recover(); err != nil {
|
||||
if status.isAnyDone() {
|
||||
// We handle such unexpected errors only when we detect that one side of the streaming is done.
|
||||
log.Debug().Msgf("Gracefully handled error %v in Streaming for %s, error %s", r, dir, debug.Stack())
|
||||
log.Debug().Msgf("recovered from panic in stream.Pipe for %s, error %s, %s", dir, err, debug.Stack())
|
||||
} else {
|
||||
// Otherwise, this is unexpected, but we prevent the program from crashing anyway.
|
||||
log.Warn().Msgf("Gracefully handled unexpected error %v in Streaming for %s, error %s", r, dir, debug.Stack())
|
||||
|
||||
tags := make(map[string]string)
|
||||
tags["root"] = "websocket.stream"
|
||||
tags["dir"] = dir
|
||||
switch rval := r.(type) {
|
||||
case error:
|
||||
raven.CaptureError(rval, tags)
|
||||
default:
|
||||
rvalStr := fmt.Sprint(rval)
|
||||
raven.CaptureMessage(rvalStr, tags)
|
||||
}
|
||||
log.Warn().Msgf("recovered from panic in stream.Pipe for %s, error %s, %s", dir, err, debug.Stack())
|
||||
sentry.CurrentHub().Recover(err)
|
||||
sentry.Flush(time.Second * 5)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
defer dst.CloseWrite()
|
||||
|
||||
_, err := copyData(dst, src, dir)
|
||||
if err != nil {
|
||||
log.Debug().Msgf("%s copy: %v", dir, err)
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPipeBidirectionalFinishBothSides(t *testing.T) {
|
||||
fun := func(upstream, downstream *mockedStream) {
|
||||
downstream.closeReader()
|
||||
upstream.closeReader()
|
||||
}
|
||||
|
||||
testPipeBidirectionalUnblocking(t, fun, time.Millisecond*200, false)
|
||||
}
|
||||
|
||||
func TestPipeBidirectionalFinishOneSideTimeout(t *testing.T) {
|
||||
fun := func(upstream, downstream *mockedStream) {
|
||||
downstream.closeReader()
|
||||
}
|
||||
|
||||
testPipeBidirectionalUnblocking(t, fun, time.Millisecond*200, true)
|
||||
}
|
||||
|
||||
func TestPipeBidirectionalClosingWriteBothSidesAlsoExists(t *testing.T) {
|
||||
fun := func(upstream, downstream *mockedStream) {
|
||||
downstream.CloseWrite()
|
||||
upstream.CloseWrite()
|
||||
|
||||
downstream.writeToReader("abc")
|
||||
upstream.writeToReader("abc")
|
||||
}
|
||||
|
||||
testPipeBidirectionalUnblocking(t, fun, time.Millisecond*200, false)
|
||||
}
|
||||
|
||||
func TestPipeBidirectionalClosingWriteSingleSideAlsoExists(t *testing.T) {
|
||||
fun := func(upstream, downstream *mockedStream) {
|
||||
downstream.CloseWrite()
|
||||
|
||||
downstream.writeToReader("abc")
|
||||
upstream.writeToReader("abc")
|
||||
}
|
||||
|
||||
testPipeBidirectionalUnblocking(t, fun, time.Millisecond*200, true)
|
||||
}
|
||||
|
||||
func testPipeBidirectionalUnblocking(t *testing.T, afterFun func(*mockedStream, *mockedStream), timeout time.Duration, expectTimeout bool) {
|
||||
logger := zerolog.Nop()
|
||||
|
||||
downstream := newMockedStream()
|
||||
upstream := newMockedStream()
|
||||
|
||||
resultCh := make(chan error)
|
||||
go func() {
|
||||
resultCh <- PipeBidirectional(downstream, upstream, timeout, &logger)
|
||||
}()
|
||||
|
||||
afterFun(upstream, downstream)
|
||||
|
||||
select {
|
||||
case err := <-resultCh:
|
||||
if expectTimeout {
|
||||
require.NotNil(t, err)
|
||||
} else {
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
case <-time.After(timeout * 2):
|
||||
require.Fail(t, "test timeout")
|
||||
}
|
||||
}
|
||||
|
||||
func newMockedStream() *mockedStream {
|
||||
return &mockedStream{
|
||||
readCh: make(chan *string),
|
||||
writeCh: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
type mockedStream struct {
|
||||
readCh chan *string
|
||||
writeCh chan struct{}
|
||||
|
||||
writeCloseOnce sync.Once
|
||||
}
|
||||
|
||||
func (m *mockedStream) Read(p []byte) (n int, err error) {
|
||||
result := <-m.readCh
|
||||
if result == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
return len(*result), nil
|
||||
}
|
||||
|
||||
func (m *mockedStream) Write(p []byte) (n int, err error) {
|
||||
<-m.writeCh
|
||||
|
||||
return 0, fmt.Errorf("closed")
|
||||
}
|
||||
|
||||
func (m *mockedStream) CloseWrite() error {
|
||||
m.writeCloseOnce.Do(func() {
|
||||
close(m.writeCh)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockedStream) closeReader() {
|
||||
close(m.readCh)
|
||||
}
|
||||
func (m *mockedStream) writeToReader(content string) {
|
||||
m.readCh <- &content
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package supervisor
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -72,7 +72,7 @@ func fetchActiveIncidents() (incidents []Incident) {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"github.com/cloudflare/cloudflared/connection"
|
||||
@@ -286,6 +286,7 @@ func (s *Supervisor) startFirstTunnel(
|
||||
}
|
||||
case connection.DupConnRegisterTunnelError,
|
||||
*quic.IdleTimeoutError,
|
||||
*quic.ApplicationError,
|
||||
edgediscovery.DialError,
|
||||
*connection.EdgeQuicDialError:
|
||||
// Try again for these types of errors
|
||||
|
||||
+18
-10
@@ -11,8 +11,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/rs/zerolog"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
@@ -68,6 +68,10 @@ type TunnelConfig struct {
|
||||
ProtocolSelector connection.ProtocolSelector
|
||||
EdgeTLSConfigs map[connection.Protocol]*tls.Config
|
||||
PacketConfig *ingress.GlobalRouterConfig
|
||||
|
||||
UDPUnregisterSessionTimeout time.Duration
|
||||
|
||||
DisableQUICPathMTUDiscovery bool
|
||||
}
|
||||
|
||||
func (c *TunnelConfig) registrationOptions(connectionID uint8, OriginLocalIP string, uuid uuid.UUID) *tunnelpogs.RegistrationOptions {
|
||||
@@ -594,17 +598,19 @@ func (e *EdgeTunnelServer) serveQUIC(
|
||||
}
|
||||
|
||||
quicConfig := &quic.Config{
|
||||
HandshakeIdleTimeout: quicpogs.HandshakeIdleTimeout,
|
||||
MaxIdleTimeout: quicpogs.MaxIdleTimeout,
|
||||
KeepAlivePeriod: quicpogs.MaxIdlePingPeriod,
|
||||
MaxIncomingStreams: connection.MaxConcurrentStreams,
|
||||
MaxIncomingUniStreams: connection.MaxConcurrentStreams,
|
||||
EnableDatagrams: true,
|
||||
MaxDatagramFrameSize: quicpogs.MaxDatagramFrameSize,
|
||||
Tracer: quicpogs.NewClientTracer(connLogger.Logger(), connIndex),
|
||||
HandshakeIdleTimeout: quicpogs.HandshakeIdleTimeout,
|
||||
MaxIdleTimeout: quicpogs.MaxIdleTimeout,
|
||||
KeepAlivePeriod: quicpogs.MaxIdlePingPeriod,
|
||||
MaxIncomingStreams: quicpogs.MaxIncomingStreams,
|
||||
MaxIncomingUniStreams: quicpogs.MaxIncomingStreams,
|
||||
EnableDatagrams: true,
|
||||
MaxDatagramFrameSize: quicpogs.MaxDatagramFrameSize,
|
||||
Tracer: quicpogs.NewClientTracer(connLogger.Logger(), connIndex),
|
||||
DisablePathMTUDiscovery: e.config.DisableQUICPathMTUDiscovery,
|
||||
}
|
||||
|
||||
quicConn, err := connection.NewQUICConnection(
|
||||
ctx,
|
||||
quicConfig,
|
||||
edgeAddr,
|
||||
e.edgeBindAddr,
|
||||
@@ -614,7 +620,9 @@ func (e *EdgeTunnelServer) serveQUIC(
|
||||
connOptions,
|
||||
controlStreamHandler,
|
||||
connLogger.Logger(),
|
||||
e.config.PacketConfig)
|
||||
e.config.PacketConfig,
|
||||
e.config.UDPUnregisterSessionTimeout,
|
||||
)
|
||||
if err != nil {
|
||||
if e.config.NeedPQ {
|
||||
handlePQTunnelError(err, e.config)
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
@@ -77,7 +77,7 @@ func LoadOriginCA(originCAPoolFilename string, log *zerolog.Logger) (*x509.CertP
|
||||
|
||||
if originCAPoolFilename != "" {
|
||||
var err error
|
||||
originCustomCAPool, err = ioutil.ReadFile(originCAPoolFilename)
|
||||
originCustomCAPool, err = os.ReadFile(originCAPoolFilename)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("unable to read the file %s for --%s", originCAPoolFilename, OriginCAPoolFlag))
|
||||
}
|
||||
@@ -116,7 +116,7 @@ func LoadCustomOriginCA(originCAFilename string) (*x509.CertPool, error) {
|
||||
return certPool, nil
|
||||
}
|
||||
|
||||
customOriginCA, err := ioutil.ReadFile(originCAFilename)
|
||||
customOriginCA, err := os.ReadFile(originCAFilename)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("unable to read the file %s", originCAFilename))
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ package tlsconfig
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -90,7 +90,7 @@ func GetConfig(p *TLSParameters) (*tls.Config, error) {
|
||||
func LoadCert(certPaths []string) (*x509.CertPool, error) {
|
||||
ca := x509.NewCertPool()
|
||||
for _, certPath := range certPaths {
|
||||
caCert, err := ioutil.ReadFile(certPath)
|
||||
caCert, err := os.ReadFile(certPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Error reading certificate %s", certPath)
|
||||
}
|
||||
|
||||
+9
-10
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -13,9 +12,9 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/go-jose/go-jose/v3"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/cloudflare/cloudflared/config"
|
||||
"github.com/cloudflare/cloudflared/retry"
|
||||
@@ -124,7 +123,7 @@ func (l *lock) Acquire() error {
|
||||
|
||||
// Create a lock file so other processes won't also try to get the token at
|
||||
// the same time
|
||||
if err := ioutil.WriteFile(l.lockFilePath, []byte{}, 0600); err != nil {
|
||||
if err := os.WriteFile(l.lockFilePath, []byte{}, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -208,25 +207,25 @@ func getToken(appURL *url.URL, appInfo *AppInfo, useHostOnly bool, log *zerolog.
|
||||
log.Debug().Msgf("failed to exchange org token for app token: %s", err)
|
||||
} else {
|
||||
// generate app path
|
||||
if err := ioutil.WriteFile(appTokenPath, []byte(appToken), 0600); err != nil {
|
||||
if err := os.WriteFile(appTokenPath, []byte(appToken), 0600); err != nil {
|
||||
return "", errors.Wrap(err, "failed to write app token to disk")
|
||||
}
|
||||
return appToken, nil
|
||||
}
|
||||
}
|
||||
return getTokensFromEdge(appURL, appTokenPath, orgTokenPath, useHostOnly, log)
|
||||
return getTokensFromEdge(appURL, appInfo.AppAUD, appTokenPath, orgTokenPath, useHostOnly, log)
|
||||
|
||||
}
|
||||
|
||||
// getTokensFromEdge will attempt to use the transfer service to retrieve an app and org token, save them to disk,
|
||||
// and return the app token.
|
||||
func getTokensFromEdge(appURL *url.URL, appTokenPath, orgTokenPath string, useHostOnly bool, log *zerolog.Logger) (string, error) {
|
||||
func getTokensFromEdge(appURL *url.URL, appAUD, appTokenPath, orgTokenPath string, useHostOnly bool, log *zerolog.Logger) (string, error) {
|
||||
// If no org token exists or if it couldn't be exchanged for an app token, then run the transfer service flow.
|
||||
|
||||
// this weird parameter is the resource name (token) and the key/value
|
||||
// we want to send to the transfer service. the key is token and the value
|
||||
// is blank (basically just the id generated in the transfer service)
|
||||
resourceData, err := RunTransfer(appURL, keyName, keyName, "", true, useHostOnly, log)
|
||||
resourceData, err := RunTransfer(appURL, appAUD, keyName, keyName, "", true, useHostOnly, log)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to run transfer service")
|
||||
}
|
||||
@@ -237,12 +236,12 @@ func getTokensFromEdge(appURL *url.URL, appTokenPath, orgTokenPath string, useHo
|
||||
|
||||
// If we were able to get the auth domain and generate an org token path, lets write it to disk.
|
||||
if orgTokenPath != "" {
|
||||
if err := ioutil.WriteFile(orgTokenPath, []byte(resp.OrgToken), 0600); err != nil {
|
||||
if err := os.WriteFile(orgTokenPath, []byte(resp.OrgToken), 0600); err != nil {
|
||||
return "", errors.Wrap(err, "failed to write org token to disk")
|
||||
}
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(appTokenPath, []byte(resp.AppToken), 0600); err != nil {
|
||||
if err := os.WriteFile(appTokenPath, []byte(resp.AppToken), 0600); err != nil {
|
||||
return "", errors.Wrap(err, "failed to write app token to disk")
|
||||
}
|
||||
|
||||
@@ -389,7 +388,7 @@ func GetAppTokenIfExists(appInfo *AppInfo) (string, error) {
|
||||
|
||||
// GetTokenIfExists will return the token from local storage if it exists and not expired
|
||||
func getTokenIfExists(path string) (*jose.JSONWebSignature, error) {
|
||||
content, err := ioutil.ReadFile(path)
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+4
-3
@@ -25,12 +25,12 @@ const (
|
||||
// The "dance" we refer to is building a HTTP request, opening that in a browser waiting for
|
||||
// the user to complete an action, while it long polls in the background waiting for an
|
||||
// action to be completed to download the resource.
|
||||
func RunTransfer(transferURL *url.URL, resourceName, key, value string, shouldEncrypt bool, useHostOnly bool, log *zerolog.Logger) ([]byte, error) {
|
||||
func RunTransfer(transferURL *url.URL, appAUD, resourceName, key, value string, shouldEncrypt bool, useHostOnly bool, log *zerolog.Logger) ([]byte, error) {
|
||||
encrypterClient, err := NewEncrypter("cloudflared_priv.pem", "cloudflared_pub.pem")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
requestURL, err := buildRequestURL(transferURL, key, value+encrypterClient.PublicKey(), shouldEncrypt, useHostOnly)
|
||||
requestURL, err := buildRequestURL(transferURL, appAUD, key, value+encrypterClient.PublicKey(), shouldEncrypt, useHostOnly)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -76,9 +76,10 @@ func RunTransfer(transferURL *url.URL, resourceName, key, value string, shouldEn
|
||||
// BuildRequestURL creates a request suitable for a resource transfer.
|
||||
// it will return a constructed url based off the base url and query key/value provided.
|
||||
// cli will build a url for cli transfer request.
|
||||
func buildRequestURL(baseURL *url.URL, key, value string, cli, useHostOnly bool) (string, error) {
|
||||
func buildRequestURL(baseURL *url.URL, appAUD string, key, value string, cli, useHostOnly bool) (string, error) {
|
||||
q := baseURL.Query()
|
||||
q.Set(key, value)
|
||||
q.Set("aud", appAUD)
|
||||
baseURL.RawQuery = q.Encode()
|
||||
if useHostOnly {
|
||||
baseURL.Path = ""
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -105,7 +105,7 @@ func exchangeWireformat(msg []byte, endpoint *url.URL, client *http.Client) ([]b
|
||||
}
|
||||
|
||||
// Read wireformat response from the body
|
||||
buf, err := ioutil.ReadAll(resp.Body)
|
||||
buf, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to read the response body")
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user