Compare commits

..

13 Commits

Author SHA1 Message Date
Michael Borkenstein 772ccc9607 Release 2020.7.0 2020-07-01 10:52:35 -05:00
Michael Borkenstein 7724ff8176 AUTH-2860: Fix builds 2020-07-01 15:51:02 +00:00
Igor Postelnik 2a3d486126 TUN-3007: Implement named tunnel connection registration and unregistration.
Removed flag for using quick reconnect, this logic is now always enabled.
2020-07-01 04:19:30 +00:00
Rachel Williams 932e383051 TUN-3140: Add timestamps to terminal log entries 2020-06-30 22:28:22 +00:00
Adam Chalmers dbe3516204 Release 2020.6.6 2020-06-30 12:55:54 -05:00
Dalton 6fc9f1b405 AUTH-2849 all log output to stderr 2020-06-30 16:54:31 +00:00
Michael Borkenstein 370c17e48c AUTH-2718: Add target for publishing deb to pkg.cloudflare repo 2020-06-30 14:46:24 +00:00
Michael Borkenstein 92da73aa9d AUTH-2652: Update cloudflare repo 2020-06-26 14:24:20 +00:00
Dalton 0c65daaa7d AUTH-2712 mac package build script and better config file handling when started as a service 2020-06-25 16:44:57 -05:00
Michael Borkenstein b46acd7f63 AUTH-2685: Adds script to create release 2020-06-25 18:39:37 +00:00
Michael Borkenstein e3a9aa4296 AUTH-2652: Adds .docker-images to push images to docker hub 2020-06-23 11:35:48 -05:00
cthuang 3886021ba5 TUN-3107: UnregisterConnection shouldn't wrap nil error as RPC error 2020-06-18 21:17:43 +08:00
Adam Chalmers 4d3ebaf984 TUN-3106: Pass NamedTunnel config to StartServer 2020-06-17 23:20:37 +00:00
24 changed files with 693 additions and 175 deletions
+10
View File
@@ -0,0 +1,10 @@
images:
- name: cloudflared
dockerfile: Dockerfile
context: .
versions:
- latest
registries:
- name: docker.io/cloudflare
user: env:DOCKER_USER
password: env:DOCKER_PASSWORD
+1
View File
@@ -9,6 +9,7 @@ guide/public
\#*\#
cscope.*
cloudflared
cloudflared.pkg
cloudflared.exe
!cmd/cloudflared/
.DS_Store
+7
View File
@@ -0,0 +1,7 @@
#!/bin/bash
# uninstall first in case this is an upgrade
/usr/local/bin/cloudflared service uninstall
# install the new service using launchctl
/usr/local/bin/cloudflared service install
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
/usr/local/bin/cloudflared service uninstall
rm /usr/local/bin/cloudflared
pkgutil --forget com.cloudflare.cloudflared
+20 -1
View File
@@ -17,7 +17,9 @@ ifeq ($(EQUINOX_IS_DRAFT), true)
endif
LOCAL_ARCH ?= $(shell uname -m)
ifeq ($(LOCAL_ARCH),x86_64)
ifneq ($(GOARCH),)
TARGET_ARCH ?= $(GOARCH)
else ifeq ($(LOCAL_ARCH),x86_64)
TARGET_ARCH ?= amd64
else ifeq ($(shell echo $(LOCAL_ARCH) | head -c 5),armv8)
TARGET_ARCH ?= arm64
@@ -40,6 +42,12 @@ else
$(error This system's OS $(LOCAL_OS) isn't supported)
endif
ifeq ($(TARGET_OS), windows)
EXECUTABLE_PATH=./cloudflared.exe
else
EXECUTABLE_PATH=./cloudflared
endif
.PHONY: all
all: cloudflared test
@@ -63,6 +71,13 @@ test: vet
test-ssh-server:
docker-compose -f ssh_server_tests/docker-compose.yml up
.PHONY: publish-deb
publish-deb: cloudflared-deb
for HOST in $(CF_PKG_HOSTS); do \
ssh-keyscan -t rsa $$HOST >> ~/.ssh/known_hosts; \
scp -4 cloudflared_$(VERSION)_amd64.deb cfsync@$$HOST:/state/cf-pkg/staging/apt/$(FLAVOR)/cloudflared/; \
done
.PHONY: cloudflared-deb
cloudflared-deb: cloudflared
mkdir -p $(PACKAGE_DIR)
@@ -88,6 +103,10 @@ homebrew-release: homebrew-upload
release: bin/equinox
bin/equinox release $(EQUINOX_FLAGS) -- $(VERSION_FLAGS) $(IMPORT_PATH)/cmd/cloudflared
.PHONY: github-release
github-release: cloudflared
python3 github_release.py --path $(EXECUTABLE_PATH) --release-version $(VERSION)
bin/equinox:
mkdir -p bin
curl -s https://bin.equinox.io/c/75JtLRTsJ3n/release-tool-beta-$(EQUINOX_PLATFORM).tgz | tar xz -C bin/
+15
View File
@@ -1,3 +1,18 @@
2020.7.0
- 2020-06-30 TUN-3140: Add timestamps to terminal log entries
- 2020-06-30 AUTH-2860: Fix builds
- 2020-06-25 TUN-3007: Implement named tunnel connection registration and unregistration.
2020.6.6
- 2020-06-23 AUTH-2685: Adds script to create release
- 2020-06-25 AUTH-2652: Update cloudflare repo
- 2020-06-26 AUTH-2718: Add target for publishing deb to pkg.cloudflare repo
- 2020-06-26 AUTH-2849 all log output to stderr
- 2020-06-17 TUN-3106: Pass NamedTunnel config to StartServer
- 2020-06-18 TUN-3107: UnregisterConnection shouldn't wrap nil error as RPC error
- 2020-06-17 AUTH-2652: Adds .docker-images to push images to docker hub
- 2020-06-18 AUTH-2712 mac package build script and better config file handling when started as a service
2020.6.5
- 2020-06-16 DEVTOOLS-7321: Don't skip macOS builds based on tag
- 2020-06-16 fix for a flaky test
+76
View File
@@ -22,6 +22,18 @@ stretch: &stretch
- export GOOS=linux
- export GOARCH=amd64
- make cloudflared-deb
publish-deb:
build_dir: *build_dir
builddeps:
- *pinned_go
- build-essential
- fakeroot
- rubygem-fpm
- openssh-client
post-cache:
- export GOOS=linux
- export GOARCH=amd64
- make publish-deb
release-linux-amd64:
build_dir: *build_dir
builddeps:
@@ -31,6 +43,18 @@ stretch: &stretch
- export GOOS=linux
- export GOARCH=amd64
- make release
github-release-linux-amd64:
build_dir: *build_dir
builddeps:
- *pinned_go
- build-essential
- python3-setuptools
- python3-pip
post-cache:
- pip3 install pygithub
- export GOOS=linux
- export GOARCH=amd64
- make github-release
release-linux-armv6:
build_dir: *build_dir
builddeps:
@@ -42,6 +66,20 @@ stretch: &stretch
- export GOARCH=arm
- export CC=arm-linux-gnueabihf-gcc
- make release
github-release-linux-armv6:
build_dir: *build_dir
builddeps:
- *pinned_go
- crossbuild-essential-armhf
- gcc-arm-linux-gnueabihf
- python3-setuptools
- python3-pip
post-cache:
- pip3 install pygithub
- export GOOS=linux
- export GOARCH=arm
- export CC=arm-linux-gnueabihf-gcc
- make github-release
release-linux-386:
build_dir: *build_dir
builddeps:
@@ -51,6 +89,18 @@ stretch: &stretch
- export GOOS=linux
- export GOARCH=386
- make release
github-release-linux-386:
build_dir: *build_dir
builddeps:
- *pinned_go
- gcc-multilib
- python3-setuptools
- python3-pip
post-cache:
- pip3 install pygithub
- export GOOS=linux
- export GOARCH=386
- make github-release
release-windows-amd64:
build_dir: *build_dir
builddeps:
@@ -61,6 +111,19 @@ stretch: &stretch
- export GOARCH=amd64
- export CC=x86_64-w64-mingw32-gcc
- make release
github-release-windows-amd64:
build_dir: *build_dir
builddeps:
- *pinned_go
- gcc-mingw-w64
- python3-setuptools
- python3-pip
post-cache:
- pip3 install pygithub
- export GOOS=windows
- export GOARCH=amd64
- export CC=x86_64-w64-mingw32-gcc
- make github-release
release-windows-386:
build_dir: *build_dir
builddeps:
@@ -71,6 +134,19 @@ stretch: &stretch
- export GOARCH=386
- export CC=i686-w64-mingw32-gcc-win32
- make release
github-release-windows-386:
build_dir: *build_dir
builddeps:
- *pinned_go
- gcc-mingw-w64
- python3-setuptools
- python3-pip
post-cache:
- pip3 install pygithub
- export GOOS=windows
- export GOARCH=386
- export CC=i686-w64-mingw32-gcc-win32
- make github-release
test:
build_dir: *build_dir
builddeps:
+1 -1
View File
@@ -106,7 +106,7 @@ func ssh(c *cli.Context) error {
wsConn := carrier.NewWSConnection(logger, false)
if c.NArg() > 0 || c.IsSet(sshURLFlag) {
localForwarder, err := config.ValidateUrl(c)
localForwarder, err := config.ValidateUrl(c, true)
if err != nil {
logger.Errorf("Error validating origin URL: %s", err)
return errors.Wrap(err, "error validating origin URL")
+80 -7
View File
@@ -4,25 +4,65 @@ import (
"errors"
"os"
"path/filepath"
"runtime"
"github.com/cloudflare/cloudflared/validation"
homedir "github.com/mitchellh/go-homedir"
"gopkg.in/urfave/cli.v2"
"gopkg.in/urfave/cli.v2/altsrc"
"gopkg.in/yaml.v2"
"github.com/cloudflare/cloudflared/validation"
)
var (
// File names from which we attempt to read configuration.
// DefaultConfigFiles is the file names from which we attempt to read configuration.
DefaultConfigFiles = []string{"config.yml", "config.yaml"}
// DefaultUnixConfigLocation is the primary location to find a config file
DefaultUnixConfigLocation = "/usr/local/etc/cloudflared"
// DefaultUnixLogLocation is the primary location to find log files
DefaultUnixLogLocation = "/var/log/cloudflared"
// Launchd doesn't set root env variables, so there is default
// Windows default config dir was ~/cloudflare-warp in documentation; let's keep it compatible
DefaultConfigDirs = []string{"~/.cloudflared", "~/.cloudflare-warp", "~/cloudflare-warp", "/usr/local/etc/cloudflared", "/etc/cloudflared"}
DefaultConfigDirs = []string{"~/.cloudflared", "~/.cloudflare-warp", "~/cloudflare-warp", "/etc/cloudflared", DefaultUnixConfigLocation}
)
const DefaultCredentialFile = "cert.pem"
// DefaultConfigDirectory returns the default directory of the config file
func DefaultConfigDirectory() string {
if runtime.GOOS == "windows" {
path := os.Getenv("CFDPATH")
if path == "" {
path = filepath.Join(os.Getenv("ProgramFiles(x86)"), "cloudflared")
if _, err := os.Stat(path); os.IsNotExist(err) { //doesn't exist, so return an empty failure string
return ""
}
}
return path
}
return DefaultUnixConfigLocation
}
// DefaultLogDirectory returns the default directory for log files
func DefaultLogDirectory() string {
if runtime.GOOS == "windows" {
return DefaultConfigDirectory()
}
return DefaultUnixLogLocation
}
// DefaultConfigPath returns the default location of a config file
func DefaultConfigPath() string {
dir := DefaultConfigDirectory()
if dir == "" {
return DefaultConfigFiles[0]
}
return filepath.Join(dir, DefaultConfigFiles[0])
}
// FileExists checks to see if a file exist at the provided path.
func FileExists(path string) (bool, error) {
f, err := os.Open(path)
@@ -64,10 +104,43 @@ func FindDefaultConfigPath() string {
return ""
}
// FindOrCreateConfigPath returns the first path that contains a config file
// or creates one in the primary default path if it doesn't exist
func FindOrCreateConfigPath() string {
path := FindDefaultConfigPath()
if path == "" {
// create the default directory if it doesn't exist
path = DefaultConfigPath()
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
return ""
}
// write a new config file out
file, err := os.Create(path)
if err != nil {
return ""
}
defer file.Close()
logDir := DefaultLogDirectory()
os.MkdirAll(logDir, os.ModePerm) //try and create it. Doesn't matter if it succeed or not, only byproduct will be no logs
c := Root{
LogDirectory: logDir,
}
if err := yaml.NewEncoder(file).Encode(&c); err != nil {
return ""
}
}
return path
}
// FindLogSettings gets the log directory and level from the config file
func FindLogSettings() (string, string) {
configPath := FindDefaultConfigPath()
defaultDirectory := filepath.Dir(configPath)
configPath := FindOrCreateConfigPath()
defaultDirectory := DefaultLogDirectory()
defaultLevel := "info"
file, err := os.Open(configPath)
@@ -104,9 +177,9 @@ func ValidateUnixSocket(c *cli.Context) (string, error) {
// ValidateUrl will validate url flag correctness. It can be either from --url or argument
// Notice ValidateUnixSocket, it will enforce --unix-socket is not used with --url or argument
func ValidateUrl(c *cli.Context) (string, error) {
func ValidateUrl(c *cli.Context, allowFromArgs bool) (string, error) {
var url = c.String("url")
if c.NArg() > 0 {
if allowFromArgs && c.NArg() > 0 {
if c.IsSet("url") {
return "", errors.New("Specified origin urls using both --url and argument. Decide which one you want, I can only support one.")
}
-3
View File
@@ -49,9 +49,6 @@ func TestConfigChanged(t *testing.T) {
os.Remove(filePath)
}()
c := &Root{
OrgKey: "abcd",
ConfigType: "mytype",
CheckinInterval: 1,
Forwarders: []Forwarder{
{
URL: "test.daltoniam.com",
+5 -8
View File
@@ -34,14 +34,11 @@ type DNSResolver struct {
// Root is the base options to configure the service
type Root struct {
OrgKey string `json:"org_key" yaml:"orgKey"`
ConfigType string `json:"type"`
LogDirectory string `json:"log_directory" yaml:"logDirectory,omitempty"`
LogLevel string `json:"log_level" yaml:"logLevel"`
CheckinInterval int `json:"checkin_interval" yaml:"checkinInterval"`
Forwarders []Forwarder `json:"forwarders,omitempty"`
Tunnels []Tunnel `json:"tunnels,omitempty"`
Resolver DNSResolver `json:"resolver,omitempty"`
LogDirectory string `json:"log_directory" yaml:"logDirectory,omitempty"`
LogLevel string `json:"log_level" yaml:"logLevel,omitempty"`
Forwarders []Forwarder `json:"forwarders,omitempty" yaml:"forwarders,omitempty"`
Tunnels []Tunnel `json:"tunnels,omitempty" yaml:"tunnels,omitempty"`
Resolver DNSResolver `json:"resolver,omitempty" yaml:"resolver,omitempty"`
}
// Hash returns the computed values to see if the forwarder values change
+2 -2
View File
@@ -129,7 +129,7 @@ func action(version string, shutdownC, graceShutdownC chan struct{}) cli.ActionF
tags := make(map[string]string)
tags["hostname"] = c.String("hostname")
raven.SetTagsContext(tags)
raven.CapturePanic(func() { err = tunnel.StartServer(c, version, shutdownC, graceShutdownC) }, nil)
raven.CapturePanic(func() { err = tunnel.StartServer(c, version, shutdownC, graceShutdownC, nil) }, nil)
exitCode := 0
if err != nil {
handleError(err)
@@ -182,7 +182,7 @@ func handleServiceMode(shutdownC chan struct{}) error {
return err
}
configPath := config.FindDefaultConfigPath()
configPath := config.FindOrCreateConfigPath()
configManager, err := config.NewFileManager(f, configPath, logger)
if err != nil {
logger.Errorf("Cannot setup config file for monitoring: %s", err)
+15 -13
View File
@@ -207,7 +207,7 @@ func Commands() []*cli.Command {
}
func tunnel(c *cli.Context) error {
return StartServer(c, version, shutdownC, graceShutdownC)
return StartServer(c, version, shutdownC, graceShutdownC, nil)
}
func Init(v string, s, g chan struct{}) {
@@ -238,7 +238,7 @@ func createLogger(c *cli.Context, isTransport bool) (logger.Service, error) {
return logger.New(loggerOpts...)
}
func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan struct{}) error {
func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan struct{}, namedTunnel *origin.NamedTunnelConfig) error {
logger, err := createLogger(c, false)
if err != nil {
return cliutil.PrintLoggerSetupError("error setting up logger", err)
@@ -359,7 +359,7 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan
defer wg.Done()
hello.StartHelloWorldServer(logger, helloListener, shutdownC)
}()
c.Set("url", "https://"+helloListener.Addr().String())
forceSetFlag(c, "url", "https://"+helloListener.Addr().String())
}
if c.IsSet(sshServerFlag) {
@@ -409,7 +409,7 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan
close(shutdownC)
}
}()
c.Set("url", "ssh://"+localServerAddress)
forceSetFlag(c, "url", "ssh://"+localServerAddress)
}
url := c.String("url")
@@ -453,7 +453,7 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan
}
errC <- websocket.StartProxyServer(logger, listener, staticHost, shutdownC, streamHandler)
}()
c.Set("url", "http://"+listener.Addr().String())
forceSetFlag(c, "url", "http://"+listener.Addr().String())
}
transportLogger, err := createLogger(c, true)
@@ -461,7 +461,7 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan
return errors.Wrap(err, "error setting up transport logger")
}
tunnelConfig, err := prepareTunnelConfig(c, buildInfo, version, logger, transportLogger)
tunnelConfig, err := prepareTunnelConfig(c, buildInfo, version, logger, transportLogger, namedTunnel)
if err != nil {
return err
}
@@ -481,6 +481,15 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan
return waitToShutdown(&wg, errC, shutdownC, graceShutdownC, c.Duration("grace-period"), logger)
}
// forceSetFlag attempts to set the given flag value in the closest context that has it defined
func forceSetFlag(c *cli.Context, name, value string) {
for _, ctx := range c.Lineage() {
if err := ctx.Set(name, value); err == nil {
break
}
}
}
func Before(c *cli.Context) error {
logger, err := createLogger(c, false)
if err != nil {
@@ -969,13 +978,6 @@ func tunnelFlags(shouldHide bool) []cli.Flag {
EnvVars: []string{"TUNNEL_USE_RECONNECT_TOKEN"},
Hidden: true,
}),
altsrc.NewBoolFlag(&cli.BoolFlag{
Name: "use-quick-reconnects",
Usage: "Test reestablishing connections with the new 'connection digest' flow.",
Value: true,
EnvVars: []string{"TUNNEL_USE_QUICK_RECONNECTS"},
Hidden: true,
}),
altsrc.NewDurationFlag(&cli.DurationFlag{
Name: "dial-edge-timeout",
Usage: "Maximum wait time to set up a connection with the edge",
+49 -32
View File
@@ -158,7 +158,10 @@ func prepareTunnelConfig(
version string,
logger logger.Service,
transportLogger logger.Service,
namedTunnel *origin.NamedTunnelConfig,
) (*origin.TunnelConfig, error) {
compatibilityMode := namedTunnel == nil
hostname, err := validation.ValidateHostname(c.String("hostname"))
if err != nil {
logger.Errorf("Invalid hostname: %s", err)
@@ -181,7 +184,7 @@ func prepareTunnelConfig(
tags = append(tags, tunnelpogs.Tag{Name: "ID", Value: clientID})
originURL, err := config.ValidateUrl(c)
originURL, err := config.ValidateUrl(c, compatibilityMode)
if err != nil {
logger.Errorf("Error validating origin URL: %s", err)
return nil, errors.Wrap(err, "Error validating origin URL")
@@ -254,38 +257,52 @@ func prepareTunnelConfig(
return nil, errors.Wrap(err, "unable to create TLS config to connect with edge")
}
if namedTunnel != nil {
clientUUID, err := uuid.NewRandom()
if err != nil {
return nil, errors.Wrap(err, "can't generate clientUUID")
}
namedTunnel.Client = tunnelpogs.ClientInfo{
ClientID: clientUUID[:],
Features: []string{origin.FeatureSerializedHeaders},
Version: version,
Arch: fmt.Sprintf("%s_%s", buildInfo.GoOS, buildInfo.GoArch),
}
}
return &origin.TunnelConfig{
BuildInfo: buildInfo,
ClientID: clientID,
ClientTlsConfig: httpTransport.TLSClientConfig,
CompressionQuality: c.Uint64("compression-quality"),
EdgeAddrs: c.StringSlice("edge"),
GracePeriod: c.Duration("grace-period"),
HAConnections: c.Int("ha-connections"),
HTTPTransport: httpTransport,
HeartbeatInterval: c.Duration("heartbeat-interval"),
Hostname: hostname,
HTTPHostHeader: c.String("http-host-header"),
IncidentLookup: origin.NewIncidentLookup(),
IsAutoupdated: c.Bool("is-autoupdated"),
IsFreeTunnel: isFreeTunnel,
LBPool: c.String("lb-pool"),
Logger: logger,
TransportLogger: transportLogger,
MaxHeartbeats: c.Uint64("heartbeat-count"),
Metrics: tunnelMetrics,
MetricsUpdateFreq: c.Duration("metrics-update-freq"),
NoChunkedEncoding: c.Bool("no-chunked-encoding"),
OriginCert: originCert,
OriginUrl: originURL,
ReportedVersion: version,
Retries: c.Uint("retries"),
RunFromTerminal: isRunningFromTerminal(),
Tags: tags,
TlsConfig: toEdgeTLSConfig,
UseDeclarativeTunnel: c.Bool("use-declarative-tunnels"),
UseReconnectToken: c.Bool("use-reconnect-token"),
UseQuickReconnects: c.Bool("use-quick-reconnects"),
BuildInfo: buildInfo,
ClientID: clientID,
ClientTlsConfig: httpTransport.TLSClientConfig,
CompressionQuality: c.Uint64("compression-quality"),
EdgeAddrs: c.StringSlice("edge"),
GracePeriod: c.Duration("grace-period"),
HAConnections: c.Int("ha-connections"),
HTTPTransport: httpTransport,
HeartbeatInterval: c.Duration("heartbeat-interval"),
Hostname: hostname,
HTTPHostHeader: c.String("http-host-header"),
IncidentLookup: origin.NewIncidentLookup(),
IsAutoupdated: c.Bool("is-autoupdated"),
IsFreeTunnel: isFreeTunnel,
LBPool: c.String("lb-pool"),
Logger: logger,
TransportLogger: transportLogger,
MaxHeartbeats: c.Uint64("heartbeat-count"),
Metrics: tunnelMetrics,
MetricsUpdateFreq: c.Duration("metrics-update-freq"),
NoChunkedEncoding: c.Bool("no-chunked-encoding"),
OriginCert: originCert,
OriginUrl: originURL,
ReportedVersion: version,
Retries: c.Uint("retries"),
RunFromTerminal: isRunningFromTerminal(),
Tags: tags,
TlsConfig: toEdgeTLSConfig,
NamedTunnel: namedTunnel,
ReplaceExisting: c.Bool("force"),
// turn off use of reconnect token and auth refresh when using named tunnels
UseReconnectToken: compatibilityMode && c.Bool("use-reconnect-token"),
}, nil
}
+14 -5
View File
@@ -11,6 +11,7 @@ import (
"strings"
"time"
"github.com/google/uuid"
"github.com/pkg/errors"
"gopkg.in/urfave/cli.v2"
"gopkg.in/yaml.v2"
@@ -18,6 +19,7 @@ import (
"github.com/cloudflare/cloudflared/certutil"
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
"github.com/cloudflare/cloudflared/logger"
"github.com/cloudflare/cloudflared/origin"
"github.com/cloudflare/cloudflared/tunnelrpc/pogs"
"github.com/cloudflare/cloudflared/tunnelstore"
)
@@ -33,7 +35,7 @@ var (
Aliases: []string{"o"},
Usage: "Render output using given `FORMAT`. Valid options are 'json' or 'yaml'",
}
forceFlag = &cli.StringFlag{
forceFlag = &cli.BoolFlag{
Name: "force",
Aliases: []string{"f"},
Usage: "By default, if a tunnel is currently being run from a cloudflared, you can't " +
@@ -147,9 +149,12 @@ func readTunnelCredentials(tunnelID, originCertPath string) (*pogs.TunnelAuth, e
if err != nil {
return nil, errors.Wrapf(err, "couldn't read tunnel credentials from %v", filePath)
}
auth := pogs.TunnelAuth{}
err = json.Unmarshal(body, &auth)
return &auth, errors.Wrap(err, "couldn't parse tunnel credentials from JSON")
var auth pogs.TunnelAuth
if err = json.Unmarshal(body, &auth); err != nil {
return nil, err
}
return &auth, nil
}
func buildListCommand() *cli.Command {
@@ -324,6 +329,10 @@ func runTunnel(c *cli.Context) error {
return cliutil.UsageError(`"cloudflared tunnel run" requires exactly 1 argument, the ID of the tunnel to run.`)
}
id := c.Args().First()
tunnelID, err := uuid.Parse(id)
if err != nil {
return errors.Wrap(err, "error parsing tunnel ID")
}
logger, err := logger.New()
if err != nil {
@@ -339,5 +348,5 @@ func runTunnel(c *cli.Context) error {
return err
}
logger.Debugf("Read credentials for %v", credentials.AccountTag)
panic("TODO: start tunnel supervisor")
return StartServer(c, version, shutdownC, graceShutdownC, &origin.NamedTunnelConfig{Auth: *credentials, ID: tunnelID})
}
+128
View File
@@ -0,0 +1,128 @@
#!/usr/bin/python3
"""
Creates Github Releases and uploads assets
"""
import argparse
import logging
import os
from github import Github, GithubException, UnknownObjectException
FORMAT = "%(levelname)s - %(asctime)s: %(message)s"
logging.basicConfig(format=FORMAT)
CLOUDFLARED_REPO = os.environ.get("GITHUB_REPO", "cloudflare/cloudflared")
GITHUB_CONFLICT_CODE = "already_exists"
def assert_tag_exists(repo, version):
""" Raise exception if repo does not contain a tag matching version """
tags = repo.get_tags()
if not tags or tags[0].name != version:
raise Exception("Tag {} not found".format(version))
def get_or_create_release(repo, version, dry_run=False):
"""
Get a Github Release matching the version tag or create a new one.
If a conflict occurs on creation, attempt to fetch the Release on last time
"""
try:
release = repo.get_release(version)
logging.info("Release %s found", version)
return release
except UnknownObjectException:
logging.info("Release %s not found", version)
# We dont want to create a new release tag if one doesnt already exist
assert_tag_exists(repo, version)
if dry_run:
logging.info("Skipping Release creation because of dry-run")
return
try:
logging.info("Creating release %s", version)
return repo.create_git_release(version, version, "")
except GithubException as e:
errors = e.data.get("errors", [])
if e.status == 422 and any(
[err.get("code") == GITHUB_CONFLICT_CODE for err in errors]
):
logging.warning(
"Conflict: Release was likely just made by a different build: %s",
e.data,
)
return repo.get_release(version)
raise e
def parse_args():
""" Parse and validate args """
parser = argparse.ArgumentParser(
description="Creates Github Releases and uploads assets."
)
parser.add_argument(
"--api-key", default=os.environ.get("API_KEY"), help="Github API key"
)
parser.add_argument(
"--release-version",
metavar="version",
default=os.environ.get("VERSION"),
help="Release version",
)
parser.add_argument(
"--path", default=os.environ.get("ASSET_PATH"), help="Asset path"
)
parser.add_argument(
"--name", default=os.environ.get("ASSET_NAME"), help="Asset Name"
)
parser.add_argument(
"--dry-run", action="store_true", help="Do not create release or upload asset"
)
args = parser.parse_args()
is_valid = True
if not args.release_version:
logging.error("Missing release version")
is_valid = False
if not args.path:
logging.error("Missing asset path")
is_valid = False
if not args.name:
logging.error("Missing asset name")
is_valid = False
if not args.api_key:
logging.error("Missing API key")
is_valid = False
if is_valid:
return args
parser.print_usage()
exit(1)
def main():
""" Attempts to upload Asset to Github Release. Creates Release if it doesnt exist """
try:
args = parse_args()
client = Github(args.api_key)
repo = client.get_repo(CLOUDFLARED_REPO)
release = get_or_create_release(repo, args.release_version, args.dry_run)
if args.dry_run:
logging.info("Skipping asset upload because of dry-run")
return
release.upload_asset(args.path, name=args.name)
except Exception as e:
logging.exception(e)
exit(1)
main()
+4 -13
View File
@@ -114,21 +114,12 @@ func New(opts ...Option) (Service, error) {
}
if !config.terminalOutputDisabled {
terminalFormatter := NewTerminalFormatter(time.RFC3339)
if len(config.supportedTerminalLevels) == 0 {
l.Add(os.Stdout, NewTerminalFormatter(""), InfoLevel)
l.Add(os.Stderr, NewTerminalFormatter(""), ErrorLevel, FatalLevel)
l.Add(os.Stderr, terminalFormatter, InfoLevel, ErrorLevel, FatalLevel)
} else {
errLevels := []Level{}
outLevels := []Level{}
for _, level := range config.supportedTerminalLevels {
if level == ErrorLevel || level == FatalLevel {
errLevels = append(errLevels, level)
} else {
outLevels = append(outLevels, level)
}
}
l.Add(os.Stdout, NewTerminalFormatter(""), outLevels...)
l.Add(os.Stderr, NewTerminalFormatter(""), errLevels...)
l.Add(os.Stderr, terminalFormatter, config.supportedTerminalLevels...)
}
}
return l, nil
+6 -5
View File
@@ -76,17 +76,18 @@ func NewTerminalFormatter(format string) Formatter {
// Timestamp returns the log level with a matching color to the log type
func (f *TerminalFormatter) Timestamp(l Level, d time.Time) string {
t := ""
dateStr := "[" + d.Format(f.format) + "] "
switch l {
case InfoLevel:
t = f.output("[INFO] ", skittles.Cyan)
t = f.output("INFO", skittles.Cyan)
case ErrorLevel:
t = f.output("[ERROR] ", skittles.Red)
t = f.output("ERROR", skittles.Red)
case DebugLevel:
t = f.output("[DEBUG] ", skittles.Yellow)
t = f.output("DEBUG", skittles.Yellow)
case FatalLevel:
t = f.output("[FATAL] ", skittles.Red)
t = f.output("FATAL", skittles.Red)
}
return t
return t + dateStr
}
// Content just writes the log line straight to the sources
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
TARGET_DIRECTORY=".build"
BINARY_NAME="cloudflared"
VERSION=$(git describe --tags --always --dirty="-dev")
PRODUCT="cloudflared"
echo "building cloudflared"
make cloudflared
echo "creating build directory"
mkdir ${TARGET_DIRECTORY}
mkdir ${TARGET_DIRECTORY}/contents
cp -r .mac_resources/scripts ${TARGET_DIRECTORY}/scripts
echo "move cloudflared into the build directory"
mv $BINARY_NAME {$TARGET_DIRECTORY}/contents/${PRODUCT}
echo "build the installer package"
pkgbuild --identifier com.cloudflare.${PRODUCT} \
--version ${VERSION} \
--scripts ${TARGET_DIRECTORY}/scripts \
--root ${TARGET_DIRECTORY}/contents \
--install-location /usr/local/bin \
${PRODUCT}.pkg
# TODO: our iOS/Mac account doesn't have this installer certificate type.
# need to find how we can get it --sign "Developer ID Installer: Cloudflare" \
echo "cleaning up the build directory"
rm -rf $TARGET_DIRECTORY
+9 -8
View File
@@ -80,7 +80,7 @@ type tunnelError struct {
err error
}
func NewSupervisor(config *TunnelConfig, u uuid.UUID) (*Supervisor, error) {
func NewSupervisor(config *TunnelConfig, cloudflaredUUID uuid.UUID) (*Supervisor, error) {
var (
edgeIPs *edgediscovery.Edge
err error
@@ -93,8 +93,9 @@ func NewSupervisor(config *TunnelConfig, u uuid.UUID) (*Supervisor, error) {
if err != nil {
return nil, err
}
return &Supervisor{
cloudflaredUUID: u,
cloudflaredUUID: cloudflaredUUID,
config: config,
edgeIPs: edgeIPs,
tunnelErrors: make(chan tunnelError),
@@ -226,17 +227,17 @@ func (s *Supervisor) startFirstTunnel(ctx context.Context, connectedSignal *sign
addr *net.TCPAddr
err error
)
const thisConnID = 0
const firstConnIndex = 0
defer func() {
s.tunnelErrors <- tunnelError{index: thisConnID, addr: addr, err: err}
s.tunnelErrors <- tunnelError{index: firstConnIndex, addr: addr, err: err}
}()
addr, err = s.edgeIPs.GetAddr(thisConnID)
addr, err = s.edgeIPs.GetAddr(firstConnIndex)
if err != nil {
return
}
err = ServeTunnelLoop(ctx, s, s.config, addr, thisConnID, connectedSignal, s.cloudflaredUUID, s.bufferPool, reconnectCh)
err = ServeTunnelLoop(ctx, s, s.config, addr, firstConnIndex, connectedSignal, s.cloudflaredUUID, s.bufferPool, reconnectCh)
// If the first tunnel disconnects, keep restarting it.
edgeErrors := 0
for s.unusedIPs() {
@@ -254,12 +255,12 @@ func (s *Supervisor) startFirstTunnel(ctx context.Context, connectedSignal *sign
return
}
if edgeErrors >= 2 {
addr, err = s.edgeIPs.GetDifferentAddr(thisConnID)
addr, err = s.edgeIPs.GetDifferentAddr(firstConnIndex)
if err != nil {
return
}
}
err = ServeTunnelLoop(ctx, s, s.config, addr, thisConnID, connectedSignal, s.cloudflaredUUID, s.bufferPool, reconnectCh)
err = ServeTunnelLoop(ctx, s, s.config, addr, firstConnIndex, connectedSignal, s.cloudflaredUUID, s.bufferPool, reconnectCh)
}
}
+159 -72
View File
@@ -26,6 +26,7 @@ import (
"github.com/cloudflare/cloudflared/logger"
"github.com/cloudflare/cloudflared/signal"
"github.com/cloudflare/cloudflared/tunnelrpc"
"github.com/cloudflare/cloudflared/tunnelrpc/pogs"
tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
"github.com/cloudflare/cloudflared/validation"
"github.com/cloudflare/cloudflared/websocket"
@@ -47,47 +48,46 @@ type registerRPCName string
const (
register registerRPCName = "register"
reconnect registerRPCName = "reconnect"
unknown registerRPCName = "unknown"
)
type TunnelConfig struct {
BuildInfo *buildinfo.BuildInfo
ClientID string
ClientTlsConfig *tls.Config
CloseConnOnce *sync.Once // Used to close connectedSignal no more than once
CompressionQuality uint64
EdgeAddrs []string
GracePeriod time.Duration
HAConnections int
HTTPTransport http.RoundTripper
HeartbeatInterval time.Duration
Hostname string
HTTPHostHeader string
IncidentLookup IncidentLookup
IsAutoupdated bool
IsFreeTunnel bool
LBPool string
Logger logger.Service
TransportLogger logger.Service
MaxHeartbeats uint64
Metrics *TunnelMetrics
MetricsUpdateFreq time.Duration
NoChunkedEncoding bool
OriginCert []byte
ReportedVersion string
Retries uint
RunFromTerminal bool
Tags []tunnelpogs.Tag
TlsConfig *tls.Config
UseDeclarativeTunnel bool
WSGI bool
BuildInfo *buildinfo.BuildInfo
ClientID string
ClientTlsConfig *tls.Config
CloseConnOnce *sync.Once // Used to close connectedSignal no more than once
CompressionQuality uint64
EdgeAddrs []string
GracePeriod time.Duration
HAConnections int
HTTPTransport http.RoundTripper
HeartbeatInterval time.Duration
Hostname string
HTTPHostHeader string
IncidentLookup IncidentLookup
IsAutoupdated bool
IsFreeTunnel bool
LBPool string
Logger logger.Service
TransportLogger logger.Service
MaxHeartbeats uint64
Metrics *TunnelMetrics
MetricsUpdateFreq time.Duration
NoChunkedEncoding bool
OriginCert []byte
ReportedVersion string
Retries uint
RunFromTerminal bool
Tags []tunnelpogs.Tag
TlsConfig *tls.Config
WSGI bool
// OriginUrl may not be used if a user specifies a unix socket.
OriginUrl string
// feature-flag to use new edge reconnect tokens
UseReconnectToken bool
// feature-flag for using ConnectionDigest
UseQuickReconnects bool
NamedTunnel *NamedTunnelConfig
ReplaceExisting bool
}
// ReconnectTunnelCredentialManager is invoked by functions in this file to
@@ -102,6 +102,8 @@ type ReconnectTunnelCredentialManager interface {
type dupConnRegisterTunnelError struct{}
var errDuplicationConnection = &dupConnRegisterTunnelError{}
func (e dupConnRegisterTunnelError) Error() string {
return "already connected to this server"
}
@@ -170,12 +172,31 @@ func (c *TunnelConfig) RegistrationOptions(connectionID uint8, OriginLocalIP str
}
}
func (c *TunnelConfig) SupportedFeatures() []string {
basic := []string{FeatureSerializedHeaders}
if c.UseQuickReconnects {
basic = append(basic, FeatureQuickReconnects)
func (c *TunnelConfig) ConnectionOptions(originLocalAddr string) *tunnelpogs.ConnectionOptions {
// attempt to parse out origin IP, but don't fail since it's informational field
host, _, _ := net.SplitHostPort(originLocalAddr)
originIP := net.ParseIP(host)
return &tunnelpogs.ConnectionOptions{
Client: c.NamedTunnel.Client,
OriginLocalIP: originIP,
ReplaceExisting: c.ReplaceExisting,
CompressionQuality: uint8(c.CompressionQuality),
}
return basic
}
func (c *TunnelConfig) SupportedFeatures() []string {
features := []string{FeatureSerializedHeaders}
if c.NamedTunnel == nil {
features = append(features, FeatureQuickReconnects)
}
return features
}
type NamedTunnelConfig struct {
Auth pogs.TunnelAuth
ID uuid.UUID
Client pogs.ClientInfo
}
func StartTunnelDaemon(ctx context.Context, config *TunnelConfig, connectedSignal *signal.Signal, cloudflaredID uuid.UUID, reconnectCh chan ReconnectSignal) error {
@@ -190,9 +211,9 @@ func ServeTunnelLoop(ctx context.Context,
credentialManager ReconnectTunnelCredentialManager,
config *TunnelConfig,
addr *net.TCPAddr,
connectionID uint8,
connectionIndex uint8,
connectedSignal *signal.Signal,
u uuid.UUID,
cloudflaredUUID uuid.UUID,
bufferPool *buffer.Pool,
reconnectCh chan ReconnectSignal,
) error {
@@ -213,16 +234,16 @@ func ServeTunnelLoop(ctx context.Context,
credentialManager,
config,
config.Logger,
addr, connectionID,
addr, connectionIndex,
connectedFuse,
&backoff,
u,
cloudflaredUUID,
bufferPool,
reconnectCh,
)
if recoverable {
if duration, ok := backoff.GetBackoffDuration(ctx); ok {
config.Logger.Infof("Retrying in %s seconds: connectionID: %d", duration, connectionID)
config.Logger.Infof("Retrying connection %d in %s seconds", connectionIndex, duration)
backoff.Backoff(ctx)
continue
}
@@ -237,10 +258,10 @@ func ServeTunnel(
config *TunnelConfig,
logger logger.Service,
addr *net.TCPAddr,
connectionID uint8,
connectionIndex uint8,
connectedFuse *h2mux.BooleanFuse,
backoff *BackoffHandler,
u uuid.UUID,
cloudflaredUUID uuid.UUID,
bufferPool *buffer.Pool,
reconnectCh chan ReconnectSignal,
) (err error, recoverable bool) {
@@ -256,22 +277,18 @@ func ServeTunnel(
}
}()
connectionTag := uint8ToString(connectionID)
// additional tags to send other than hostname which is set in cloudflared main package
tags := make(map[string]string)
tags["ha"] = connectionTag
connectionTag := uint8ToString(connectionIndex)
// Returns error from parsing the origin URL or handshake errors
handler, originLocalIP, err := NewTunnelHandler(ctx, config, addr, connectionID, bufferPool)
handler, originLocalAddr, err := NewTunnelHandler(ctx, config, addr, connectionIndex, bufferPool)
if err != nil {
switch err.(type) {
case connection.DialError:
logger.Errorf("Unable to dial edge: %s connectionID: %d", err, connectionID)
logger.Errorf("Connection %d unable to dial edge: %s", connectionIndex, err)
case h2mux.MuxerHandshakeError:
logger.Errorf("Handshake failed with edge server: %s connectionID: %d", err, connectionID)
logger.Errorf("Connection %d handshake with edge server failed: %s", connectionIndex, err)
default:
logger.Errorf("Tunnel creation failure: %s connectionID: %d", err, connectionID)
logger.Errorf("Connection %d failed: %s", connectionIndex, err)
return err, false
}
return err, true
@@ -287,20 +304,21 @@ func ServeTunnel(
}
}()
if config.NamedTunnel != nil {
return RegisterConnection(ctx, handler.muxer, config, connectionIndex, originLocalAddr)
}
if config.UseReconnectToken && connectedFuse.Value() {
token, tokenErr := credentialManager.ReconnectToken()
eventDigest, eventDigestErr := credentialManager.EventDigest()
// if we have both credentials, we can reconnect
if tokenErr == nil && eventDigestErr == nil {
var connDigest []byte
// check if we can use Quick Reconnects
if config.UseQuickReconnects {
if digest, connDigestErr := credentialManager.ConnDigest(connectionID); connDigestErr == nil {
connDigest = digest
}
if digest, connDigestErr := credentialManager.ConnDigest(connectionIndex); connDigestErr == nil {
connDigest = digest
}
return ReconnectTunnel(serveCtx, token, eventDigest, connDigest, handler.muxer, config, logger, connectionID, originLocalIP, u, credentialManager)
return ReconnectTunnel(serveCtx, token, eventDigest, connDigest, handler.muxer, config, logger, connectionIndex, originLocalAddr, cloudflaredUUID, credentialManager)
}
// log errors and proceed to RegisterTunnel
if tokenErr != nil {
@@ -310,7 +328,7 @@ func ServeTunnel(
logger.Errorf("Couldn't get event digest: %s", eventDigestErr)
}
}
return RegisterTunnel(serveCtx, credentialManager, handler.muxer, config, logger, connectionID, originLocalIP, u)
return RegisterTunnel(serveCtx, credentialManager, handler.muxer, config, logger, connectionIndex, originLocalAddr, cloudflaredUUID)
})
errGroup.Go(func() error {
@@ -319,12 +337,15 @@ func ServeTunnel(
select {
case <-serveCtx.Done():
// UnregisterTunnel blocks until the RPC call returns
var err error
if connectedFuse.Value() {
err = UnregisterTunnel(handler.muxer, config.GracePeriod, config.TransportLogger)
if config.NamedTunnel != nil {
_ = UnregisterConnection(ctx, handler.muxer, config)
} else {
_ = UnregisterTunnel(handler.muxer, config.GracePeriod, config.TransportLogger)
}
}
handler.muxer.Shutdown()
return err
return nil
case <-updateMetricsTickC:
handler.UpdateMetrics(connectionTag)
}
@@ -355,8 +376,6 @@ func ServeTunnel(
err = errGroup.Wait()
if err != nil {
_ = newClientRegisterTunnelError(err, config.Metrics.regFail, unknown)
switch castedErr := err.(type) {
case dupConnRegisterTunnelError:
logger.Info("Already connected to this server, selecting a different one")
@@ -376,7 +395,7 @@ func ServeTunnel(
logger.Info("Muxer shutdown")
return err, true
case *ReconnectSignal:
logger.Infof("Restarting due to reconnect signal in %d seconds", castedErr.Delay)
logger.Infof("Restarting connection %d due to reconnect signal in %d seconds", connectionIndex, castedErr.Delay)
castedErr.DelayBeforeReconnect()
return err, true
default:
@@ -387,6 +406,74 @@ func ServeTunnel(
return nil, true
}
func RegisterConnection(
ctx context.Context,
muxer *h2mux.Muxer,
config *TunnelConfig,
connectionIndex uint8,
originLocalAddr string,
) error {
const registerConnection = "registerConnection"
config.TransportLogger.Debug("initiating RPC stream for RegisterConnection")
rpc, err := connection.NewRPCClient(ctx, muxer, config.TransportLogger, openStreamTimeout)
if err != nil {
// RPC stream open error
return newClientRegisterTunnelError(err, config.Metrics.rpcFail, registerConnection)
}
defer rpc.Close()
conn, err := rpc.RegisterConnection(
ctx,
config.NamedTunnel.Auth,
config.NamedTunnel.ID,
connectionIndex,
config.ConnectionOptions(originLocalAddr),
)
if err != nil {
if err.Error() == DuplicateConnectionError {
config.Metrics.regFail.WithLabelValues("dup_edge_conn", registerConnection).Inc()
return errDuplicationConnection
}
config.Metrics.regFail.WithLabelValues("server_error", registerConnection).Inc()
return serverRegistrationErrorFromRPC(err)
}
config.Metrics.regSuccess.WithLabelValues(registerConnection).Inc()
config.Logger.Infof("Connection %d registered with %s using ID %s", connectionIndex, conn.Location, conn.UUID)
return nil
}
func serverRegistrationErrorFromRPC(err error) *serverRegisterTunnelError {
if retryable, ok := err.(*tunnelpogs.RetryableError); ok {
return &serverRegisterTunnelError{
cause: retryable.Unwrap(),
permanent: false,
}
}
return &serverRegisterTunnelError{
cause: err,
permanent: true,
}
}
func UnregisterConnection(
ctx context.Context,
muxer *h2mux.Muxer,
config *TunnelConfig,
) error {
config.TransportLogger.Debug("initiating RPC stream for UnregisterConnection")
rpc, err := connection.NewRPCClient(ctx, muxer, config.TransportLogger, openStreamTimeout)
if err != nil {
// RPC stream open error
return newClientRegisterTunnelError(err, config.Metrics.rpcFail, register)
}
defer rpc.Close()
return rpc.UnregisterConnection(ctx)
}
func RegisterTunnel(
ctx context.Context,
credentialManager ReconnectTunnelCredentialManager,
@@ -431,7 +518,7 @@ func ReconnectTunnel(
config *TunnelConfig,
logger logger.Service,
connectionID uint8,
originLocalIP string,
originLocalAddr string,
uuid uuid.UUID,
credentialManager ReconnectTunnelCredentialManager,
) error {
@@ -453,7 +540,7 @@ func ReconnectTunnel(
eventDigest,
connDigest,
config.Hostname,
config.RegistrationOptions(connectionID, originLocalIP, uuid),
config.RegistrationOptions(connectionID, originLocalAddr, uuid),
)
if registrationErr := registration.DeserializeError(); registrationErr != nil {
// ReconnectTunnel RPC failure
@@ -502,11 +589,11 @@ func processRegistrationSuccess(
func processRegisterTunnelError(err tunnelpogs.TunnelRegistrationError, metrics *TunnelMetrics, name registerRPCName) error {
if err.Error() == DuplicateConnectionError {
metrics.regFail.WithLabelValues("dup_edge_conn", string(name)).Inc()
return dupConnRegisterTunnelError{}
return errDuplicationConnection
}
metrics.regFail.WithLabelValues("server_error", string(name)).Inc()
return serverRegisterTunnelError{
cause: fmt.Errorf("Server error: %s", err.Error()),
cause: err,
permanent: err.IsPermanent(),
}
}
+5 -2
View File
@@ -223,11 +223,14 @@ func (c TunnelServer_PogsClient) RegisterConnection(ctx context.Context, auth Tu
return nil, newRPCError("unknown result which %d", result.Which())
}
func (c TunnelServer_PogsClient) Unregister(ctx context.Context) error {
func (c TunnelServer_PogsClient) UnregisterConnection(ctx context.Context) error {
client := tunnelrpc.TunnelServer{Client: c.Client}
promise := client.UnregisterConnection(ctx, func(p tunnelrpc.RegistrationServer_unregisterConnection_Params) error {
return nil
})
_, err := promise.Struct()
return wrapRPCError(err)
if err != nil {
return wrapRPCError(err)
}
return nil
}
+6 -3
View File
@@ -37,9 +37,12 @@ func (re *RPCError) Error() string {
}
func wrapRPCError(err error) *RPCError {
return &RPCError{
err: err,
if err != nil {
return &RPCError{
err: err,
}
}
return nil
}
func newRPCError(format string, args ...interface{}) *RPCError {
@@ -50,4 +53,4 @@ func newRPCError(format string, args ...interface{}) *RPCError {
func (re *RPCError) Unwrap() error {
return re.err
}
}
+45
View File
@@ -0,0 +1,45 @@
{
"product": "cloudflared",
"company": "cloudflare",
"license": "LICENSE",
"upgrade-code": "23f90fdd-9328-47ea-ab52-5380855a4b12",
"files": {
"guid": "35e5e858-9372-4449-bf73-1cd6f7267128",
"items": [
"cloudflared.exe"
]
},
"env": {
"guid": "6bb74449-d10d-4f4a-933e-6fc9fa006eae",
"vars": [
{
"name": "CFDPATH",
"value": "[INSTALLDIR].",
"permanent": "no",
"system": "yes",
"action": "set",
"part": "all"
}
]
},
"shortcuts": {},
"choco": {
"description": "cloudflared connects your machine or user identity to Cloudflare's global network.",
"project-url": "https://github.com/cloudflare/cloudflared",
"license-url": "https://github.com/cloudflare/cloudflared/blob/master/LICENSE"
},
"hooks": [
{
"command": "sc.exe create Cloudflared binPath=\"[INSTALLDIR]cloudflared.exe\" type=share start=auto DisplayName=\"Cloudflared\"",
"when": "install"
},
{
"command": "sc.exe start Cloudflared",
"when": "install"
},
{
"command": "sc.exe delete Cloudflared",
"when": "uninstall"
}
]
}