Compare commits

...

5 Commits

Author SHA1 Message Date
Dalton 12c615e2e6 Release 2020.6.4 2020-06-16 14:13:06 -05:00
Dalton 6e5ccd7c85 AUTH-2815 add the log file to support the config.yaml file
added small delay to handle the possiblity of the server not being started yet
2020-06-16 17:48:12 +00:00
Adam Chalmers 3ec500bdbb TUN-3084: Generate and store tunnel_secret value during tunnel creation 2020-06-16 11:45:27 -05:00
Igor Postelnik 8f75feac94 TUN-3085: Pass connection authentication information using TunnelAuth struct 2020-06-16 16:35:46 +00:00
Igor Postelnik 448a7798f7 TUN-3015: Add a new cap'n'proto RPC interface for connection registration as well as matching client and server implementations. The old interface extends the new one for backward compatibility. 2020-06-16 16:35:46 +00:00
14 changed files with 2007 additions and 150 deletions
+6
View File
@@ -1,3 +1,9 @@
2020.6.4
- 2020-06-11 TUN-3085: Pass connection authentication information using TunnelAuth struct
- 2020-06-15 TUN-3084: Generate and store tunnel_secret value during tunnel creation
- 2020-06-16 AUTH-2815 add the log file to support the config.yaml file
- 2020-06-02 TUN-3015: Add a new cap'n'proto RPC interface for connection registration as well as matching client and server implementations. The old interface extends the new one for backward compatibility.
2020.6.3
- 2020-06-15 DEVTOOLS-7321: Add openssh-client pkg for missing ssh-keyscan
- 2020-06-15 AUTH-2813 adds back a single file support a cloudflared log file
+14 -4
View File
@@ -215,7 +215,12 @@ func Init(v string, s, g chan struct{}) {
func createLogger(c *cli.Context, isTransport bool) (logger.Service, error) {
loggerOpts := []logger.Option{}
logPath := c.String(logDirectoryFlag)
logPath := c.String("logfile")
if logPath != "" {
logPath = c.String(logDirectoryFlag)
}
if logPath != "" {
loggerOpts = append(loggerOpts, logger.DefaultFile(logPath))
}
@@ -833,12 +838,17 @@ func tunnelFlags(shouldHide bool) []cli.Flag {
Hidden: shouldHide,
}),
altsrc.NewStringFlag(&cli.StringFlag{
Name: logDirectoryFlag,
Aliases: []string{"logfile"}, // This flag used to be called logfile when it was a single file
Usage: "Save application log to this directory for reporting issues.",
Name: "logfile",
Usage: "Save application log to this file for reporting issues.",
EnvVars: []string{"TUNNEL_LOGFILE"},
Hidden: shouldHide,
}),
altsrc.NewStringFlag(&cli.StringFlag{
Name: logDirectoryFlag,
Usage: "Save application log to this directory for reporting issues.",
EnvVars: []string{"TUNNEL_LOGDIRECTORY"},
Hidden: shouldHide,
}),
altsrc.NewIntFlag(&cli.IntFlag{
Name: "ha-connections",
Value: 4,
+83 -13
View File
@@ -1,9 +1,11 @@
package tunnel
import (
"crypto/rand"
"encoding/json"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"time"
@@ -15,6 +17,7 @@ import (
"github.com/cloudflare/cloudflared/certutil"
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
"github.com/cloudflare/cloudflared/logger"
"github.com/cloudflare/cloudflared/tunnelrpc/pogs"
"github.com/cloudflare/cloudflared/tunnelstore"
)
@@ -39,6 +42,13 @@ func buildCreateCommand() *cli.Command {
}
}
// generateTunnelSecret as an array of 32 bytes using secure random number generator
func generateTunnelSecret() ([]byte, error) {
randomBytes := make([]byte, 32)
_, err := rand.Read(randomBytes)
return randomBytes, err
}
func createTunnel(c *cli.Context) error {
if c.NArg() != 1 {
return cliutil.UsageError(`"cloudflared tunnel create" requires exactly 1 argument, the name of tunnel to create.`)
@@ -50,16 +60,40 @@ func createTunnel(c *cli.Context) error {
return errors.Wrap(err, "error setting up logger")
}
client, err := newTunnelstoreClient(c, logger)
tunnelSecret, err := generateTunnelSecret()
if err != nil {
return err
}
tunnel, err := client.CreateTunnel(name)
originCertPath, err := findOriginCert(c, logger)
if err != nil {
return errors.Wrap(err, "Error locating origin cert")
}
cert, err := getOriginCertFromContext(originCertPath, logger)
if err != nil {
return err
}
client := newTunnelstoreClient(c, cert, logger)
tunnel, err := client.CreateTunnel(name, tunnelSecret)
if err != nil {
return errors.Wrap(err, "Error creating a new tunnel")
}
if writeFileErr := writeTunnelCredentials(tunnel.ID, cert.AccountID, originCertPath, tunnelSecret, logger); err != nil {
var errorLines []string
errorLines = append(errorLines, fmt.Sprintf("Your tunnel '%v' was created with ID %v. However, cloudflared couldn't write to the tunnel credentials file at %v.json.", tunnel.Name, tunnel.ID, tunnel.ID))
errorLines = append(errorLines, fmt.Sprintf("The file-writing error is: %v", writeFileErr))
if deleteErr := client.DeleteTunnel(tunnel.ID); deleteErr != nil {
errorLines = append(errorLines, fmt.Sprintf("Cloudflared tried to delete the tunnel for you, but encountered an error. You should use `cloudflared tunnel delete %v` to delete the tunnel yourself, because the tunnel can't be run without the tunnelfile.", tunnel.ID))
errorLines = append(errorLines, fmt.Sprintf("The delete tunnel error is: %v", deleteErr))
} else {
errorLines = append(errorLines, fmt.Sprintf("The tunnel was deleted, because the tunnel can't be run without the tunnelfile"))
}
errorMsg := strings.Join(errorLines, "\n")
return errors.New(errorMsg)
}
if outputFormat := c.String(outputFormatFlag.Name); outputFormat != "" {
return renderOutput(outputFormat, &tunnel)
}
@@ -68,6 +102,34 @@ func createTunnel(c *cli.Context) error {
return nil
}
func tunnelFilePath(tunnelID, originCertPath string) (string, error) {
fileName := fmt.Sprintf("%v.json", tunnelID)
return filepath.Clean(fmt.Sprintf("%v/../%v", originCertPath, fileName)), nil
}
func writeTunnelCredentials(tunnelID, accountID, originCertPath string, tunnelSecret []byte, logger logger.Service) error {
filePath, err := tunnelFilePath(tunnelID, originCertPath)
if err != nil {
return err
}
logger.Infof("Writing tunnel credentials to %v. cloudflared chose this file based on where your origin certificate was found.", filePath)
logger.Infof("Keep this file secret. To revoke these credentials, delete the tunnel.")
file, err := os.Create(filePath)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("Unable to write to %s", filePath))
}
defer file.Close()
body, err := json.Marshal(pogs.TunnelAuth{
AccountTag: accountID,
TunnelSecret: tunnelSecret,
})
if err != nil {
return errors.Wrap(err, "Unable to marshal tunnel credentials to JSON")
}
fmt.Fprintf(file, "%d", body)
return nil
}
func buildListCommand() *cli.Command {
return &cli.Command{
Name: "list",
@@ -85,10 +147,15 @@ func listTunnels(c *cli.Context) error {
return errors.Wrap(err, "error setting up logger")
}
client, err := newTunnelstoreClient(c, logger)
originCertPath, err := findOriginCert(c, logger)
if err != nil {
return errors.Wrap(err, "Error locating origin cert")
}
cert, err := getOriginCertFromContext(originCertPath, logger)
if err != nil {
return err
}
client := newTunnelstoreClient(c, cert, logger)
tunnels, err := client.ListTunnels()
if err != nil {
@@ -155,10 +222,15 @@ func deleteTunnel(c *cli.Context) error {
return errors.Wrap(err, "error setting up logger")
}
client, err := newTunnelstoreClient(c, logger)
originCertPath, err := findOriginCert(c, logger)
if err != nil {
return errors.Wrap(err, "Error locating origin cert")
}
cert, err := getOriginCertFromContext(originCertPath, logger)
if err != nil {
return err
}
client := newTunnelstoreClient(c, cert, logger)
if err := client.DeleteTunnel(id); err != nil {
return errors.Wrapf(err, "Error deleting tunnel %s", id)
@@ -180,11 +252,12 @@ func renderOutput(format string, v interface{}) error {
}
}
func newTunnelstoreClient(c *cli.Context, logger logger.Service) (tunnelstore.Client, error) {
originCertPath, err := findOriginCert(c, logger)
if err != nil {
return nil, errors.Wrap(err, "Error locating origin cert")
}
func newTunnelstoreClient(c *cli.Context, cert *certutil.OriginCert, logger logger.Service) tunnelstore.Client {
client := tunnelstore.NewRESTClient(c.String("api-url"), cert.AccountID, cert.ServiceKey, logger)
return client
}
func getOriginCertFromContext(originCertPath string, logger logger.Service) (*certutil.OriginCert, error) {
blocks, err := readOriginCert(originCertPath, logger)
if err != nil {
@@ -199,8 +272,5 @@ func newTunnelstoreClient(c *cli.Context, logger logger.Service) (tunnelstore.Cl
if cert.AccountID == "" {
return nil, errors.Errorf(`Origin certificate needs to be refreshed before creating new tunnels.\nDelete %s and run "cloudflared login" to obtain a new cert.`, originCertPath)
}
client := tunnelstore.NewRESTClient(c.String("api-url"), cert.AccountID, cert.ServiceKey, logger)
return client, nil
return cert, nil
}
@@ -6,6 +6,7 @@ import (
"github.com/cloudflare/cloudflared/tunnelstore"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func Test_fmtConnections(t *testing.T) {
@@ -69,3 +70,10 @@ func Test_fmtConnections(t *testing.T) {
})
}
}
func TestTunnelfilePath(t *testing.T) {
actual, err := tunnelFilePath("tunnel", "~/.cloudflared/cert.pem")
assert.NoError(t, err)
expected := "~/.cloudflared/tunnel.json"
assert.Equal(t, expected, actual)
}
-1
View File
@@ -54,7 +54,6 @@ require (
github.com/prometheus/common v0.7.0 // indirect
github.com/prometheus/procfs v0.0.5 // indirect
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect
github.com/sirupsen/logrus v1.4.2 // indirect
github.com/stretchr/testify v1.3.0
github.com/tinylib/msgp v1.1.0 // indirect
github.com/xo/dburl v0.0.0-20191005012637-293c3298d6c0
+2 -1
View File
@@ -6,6 +6,7 @@ import (
"net"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"golang.org/x/net/proxy"
@@ -58,7 +59,6 @@ func startTestServer(t *testing.T, httpHandler func(w http.ResponseWriter, r *ht
// start the servers
go http.ListenAndServe(":8085", mux)
}
func respondWithJSON(w http.ResponseWriter, v interface{}, status int) {
@@ -78,6 +78,7 @@ func OkJSONResponseHandler(w http.ResponseWriter, r *http.Request) {
func TestSocksConnection(t *testing.T) {
startTestServer(t, OkJSONResponseHandler)
time.Sleep(100 * time.Millisecond)
b := sendSocksRequest(t)
assert.True(t, len(b) > 0, "no data returned!")
+233
View File
@@ -0,0 +1,233 @@
package pogs
import (
"context"
"errors"
"net"
"time"
"github.com/google/uuid"
"zombiezen.com/go/capnproto2/pogs"
"zombiezen.com/go/capnproto2/server"
"github.com/cloudflare/cloudflared/tunnelrpc"
)
type RegistrationServer interface {
RegisterConnection(ctx context.Context, auth TunnelAuth, tunnelID uuid.UUID, connIndex byte, options *ConnectionOptions) (*ConnectionDetails, error)
UnregisterConnection(ctx context.Context)
}
type ClientInfo struct {
ClientID []byte `capnp:"clientId"` // must be a slice for capnp compatibility
Features []string
Version string
Arch string
}
type ConnectionOptions struct {
Client ClientInfo
OriginLocalIP net.IP `capnp:"originLocalIp"`
ReplaceExisting bool
CompressionQuality uint8
}
type TunnelAuth struct {
AccountTag string
TunnelSecret []byte
}
func (p *ConnectionOptions) MarshalCapnproto(s tunnelrpc.ConnectionOptions) error {
return pogs.Insert(tunnelrpc.ConnectionOptions_TypeID, s.Struct, p)
}
func (p *ConnectionOptions) UnmarshalCapnproto(s tunnelrpc.ConnectionOptions) error {
return pogs.Extract(p, tunnelrpc.ConnectionOptions_TypeID, s.Struct)
}
func (a *TunnelAuth) MarshalCapnproto(s tunnelrpc.TunnelAuth) error {
return pogs.Insert(tunnelrpc.TunnelAuth_TypeID, s.Struct, a)
}
func (a *TunnelAuth) UnmarshalCapnproto(s tunnelrpc.TunnelAuth) error {
return pogs.Extract(a, tunnelrpc.TunnelAuth_TypeID, s.Struct)
}
type ConnectionDetails struct {
UUID uuid.UUID
Location string
}
func (details *ConnectionDetails) MarshalCapnproto(s tunnelrpc.ConnectionDetails) error {
if err := s.SetUuid(details.UUID[:]); err != nil {
return err
}
if err := s.SetLocationName(details.Location); err != nil {
return err
}
return nil
}
func (details *ConnectionDetails) UnmarshalCapnproto(s tunnelrpc.ConnectionDetails) error {
uuidBytes, err := s.Uuid()
if err != nil {
return err
}
details.UUID, err = uuid.FromBytes(uuidBytes)
if err != nil {
return err
}
details.Location, err = s.LocationName()
if err != nil {
return err
}
return err
}
func MarshalError(s tunnelrpc.ConnectionError, err error) error {
if err := s.SetCause(err.Error()); err != nil {
return err
}
if retryableErr, ok := err.(*RetryableError); ok {
s.SetShouldRetry(true)
s.SetRetryAfter(int64(retryableErr.Delay))
}
return nil
}
func (i TunnelServer_PogsImpl) RegisterConnection(p tunnelrpc.RegistrationServer_registerConnection) error {
server.Ack(p.Options)
auth, err := p.Params.Auth()
if err != nil {
return err
}
var pogsAuth TunnelAuth
err = pogsAuth.UnmarshalCapnproto(auth)
if err != nil {
return err
}
uuidBytes, err := p.Params.TunnelId()
if err != nil {
return err
}
tunnelID, err := uuid.FromBytes(uuidBytes)
if err != nil {
return err
}
connIndex := p.Params.ConnIndex()
options, err := p.Params.Options()
if err != nil {
return err
}
var pogsOptions ConnectionOptions
err = pogsOptions.UnmarshalCapnproto(options)
if err != nil {
return err
}
connDetails, callError := i.impl.RegisterConnection(p.Ctx, pogsAuth, tunnelID, connIndex, &pogsOptions)
resp, err := p.Results.NewResult()
if err != nil {
return err
}
if callError != nil {
if connError, err := resp.Result().NewError(); err != nil {
return err
} else {
return MarshalError(connError, callError)
}
}
if details, err := resp.Result().NewConnectionDetails(); err != nil {
return err
} else {
return connDetails.MarshalCapnproto(details)
}
}
func (i TunnelServer_PogsImpl) UnregisterConnection(p tunnelrpc.RegistrationServer_unregisterConnection) error {
server.Ack(p.Options)
i.impl.UnregisterConnection(p.Ctx)
return nil
}
func (c TunnelServer_PogsClient) RegisterConnection(ctx context.Context, auth TunnelAuth, tunnelID uuid.UUID, connIndex byte, options *ConnectionOptions) (*ConnectionDetails, error) {
client := tunnelrpc.TunnelServer{Client: c.Client}
promise := client.RegisterConnection(ctx, func(p tunnelrpc.RegistrationServer_registerConnection_Params) error {
tunnelAuth, err := p.NewAuth()
if err != nil {
return err
}
if err = auth.MarshalCapnproto(tunnelAuth); err != nil {
return err
}
err = p.SetAuth(tunnelAuth)
if err != nil {
return err
}
err = p.SetTunnelId(tunnelID[:])
if err != nil {
return err
}
p.SetConnIndex(connIndex)
connectionOptions, err := p.NewOptions()
if err != nil {
return err
}
err = options.MarshalCapnproto(connectionOptions)
if err != nil {
return err
}
return nil
})
response, err := promise.Result().Struct()
if err != nil {
return nil, wrapRPCError(err)
}
result := response.Result()
switch result.Which() {
case tunnelrpc.ConnectionResponse_result_Which_error:
resultError, err := result.Error()
if err != nil {
return nil, wrapRPCError(err)
}
cause, err := resultError.Cause()
if err != nil {
return nil, wrapRPCError(err)
}
err = errors.New(cause)
if resultError.ShouldRetry() {
err = RetryErrorAfter(err, time.Duration(resultError.RetryAfter()))
}
return nil, err
case tunnelrpc.ConnectionResponse_result_Which_connectionDetails:
connDetails, err := result.ConnectionDetails()
if err != nil {
return nil, wrapRPCError(err)
}
details := new(ConnectionDetails)
if err = details.UnmarshalCapnproto(connDetails); err != nil {
return nil, wrapRPCError(err)
}
return details, nil
}
return nil, newRPCError("unknown result which %d", result.Which())
}
func (c TunnelServer_PogsClient) Unregister(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)
}
+140
View File
@@ -0,0 +1,140 @@
package pogs
import (
"context"
"errors"
"net"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
capnp "zombiezen.com/go/capnproto2"
"zombiezen.com/go/capnproto2/rpc"
"github.com/cloudflare/cloudflared/tunnelrpc"
)
const testAccountTag = "abc123"
func TestMarshalConnectionOptions(t *testing.T) {
clientID := uuid.New()
orig := ConnectionOptions{
Client: ClientInfo{
ClientID: clientID[:],
Features: []string{"a", "b"},
Version: "1.2.3",
Arch: "macos",
},
OriginLocalIP: []byte{10, 2, 3, 4},
ReplaceExisting: false,
CompressionQuality: 1,
}
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
require.NoError(t, err)
capnpOpts, err := tunnelrpc.NewConnectionOptions(seg)
require.NoError(t, err)
err = orig.MarshalCapnproto(capnpOpts)
assert.NoError(t, err)
var pogsOpts ConnectionOptions
err = pogsOpts.UnmarshalCapnproto(capnpOpts)
assert.NoError(t, err)
assert.Equal(t, orig, pogsOpts)
}
func TestConnectionRegistrationRPC(t *testing.T) {
p1, p2 := net.Pipe()
t1, t2 := rpc.StreamTransport(p1), rpc.StreamTransport(p2)
// Server-side
testImpl := testConnectionRegistrationServer{}
srv := TunnelServer_ServerToClient(&testImpl)
serverConn := rpc.NewConn(t1, rpc.MainInterface(srv.Client))
defer serverConn.Wait()
ctx := context.Background()
clientConn := rpc.NewConn(t2)
defer clientConn.Close()
client := TunnelServer_PogsClient{
Client: clientConn.Bootstrap(ctx),
Conn: clientConn,
}
defer client.Close()
clientID := uuid.New()
options := &ConnectionOptions{
Client: ClientInfo{
ClientID: clientID[:],
Features: []string{"foo"},
Version: "1.2.3",
Arch: "macos",
},
OriginLocalIP: net.IP{10, 20, 30, 40},
ReplaceExisting: true,
CompressionQuality: 0,
}
expectedDetails := ConnectionDetails{
UUID: uuid.New(),
Location: "TEST",
}
testImpl.details = &expectedDetails
testImpl.err = nil
auth := TunnelAuth{
AccountTag: testAccountTag,
TunnelSecret: []byte{1, 2, 3, 4},
}
// success
tunnelID := uuid.New()
details, err := client.RegisterConnection(ctx, auth, tunnelID, 2, options)
assert.NoError(t, err)
assert.Equal(t, expectedDetails, *details)
// regular error
testImpl.details = nil
testImpl.err = errors.New("internal")
_, err = client.RegisterConnection(ctx, auth, tunnelID, 2, options)
assert.EqualError(t, err, "internal")
// retriable error
testImpl.details = nil
const delay = 27 * time.Second
testImpl.err = RetryErrorAfter(errors.New("retryable"), delay)
_, err = client.RegisterConnection(ctx, auth, tunnelID, 2, options)
assert.EqualError(t, err, "retryable")
re, ok := err.(*RetryableError)
assert.True(t, ok)
assert.Equal(t, delay, re.Delay)
}
type testConnectionRegistrationServer struct {
mockTunnelServerBase
details *ConnectionDetails
err error
}
func (t *testConnectionRegistrationServer) RegisterConnection(ctx context.Context, auth TunnelAuth, tunnelID uuid.UUID, connIndex byte, options *ConnectionOptions) (*ConnectionDetails, error) {
if auth.AccountTag != testAccountTag {
panic("bad account tag: " + auth.AccountTag)
}
if t.err != nil {
return nil, t.err
}
if t.details != nil {
return t.details, nil
}
panic("either details or err mush be set")
}
+53
View File
@@ -0,0 +1,53 @@
package pogs
import (
"fmt"
"time"
)
type RetryableError struct {
err error
Delay time.Duration
}
func (re *RetryableError) Error() string {
return re.err.Error()
}
// RetryErrorAfter wraps err to indicate that client should retry after delay
func RetryErrorAfter(err error, delay time.Duration) *RetryableError {
return &RetryableError{
err: err,
Delay: delay,
}
}
func (re *RetryableError) Unwrap() error {
return re.err
}
// RPCError is used to indicate errors returned by the RPC subsystem rather
// than failure of a remote operation
type RPCError struct {
err error
}
func (re *RPCError) Error() string {
return re.err.Error()
}
func wrapRPCError(err error) *RPCError {
return &RPCError{
err: err,
}
}
func newRPCError(format string, args ...interface{}) *RPCError {
return &RPCError{
fmt.Errorf(format, args...),
}
}
func (re *RPCError) Unwrap() error {
return re.err
}
+41
View File
@@ -0,0 +1,41 @@
package pogs
import (
"context"
"github.com/google/uuid"
)
// mockTunnelServerBase provides a placeholder implementation
// for TunnelServer interface that can be used to build
// mocks for specific unit tests without having to implement every method
type mockTunnelServerBase struct{}
func (mockTunnelServerBase) RegisterConnection(ctx context.Context, auth TunnelAuth, tunnelID uuid.UUID, connIndex byte, options *ConnectionOptions) (*ConnectionDetails, error) {
panic("unexpected call to RegisterConnection")
}
func (mockTunnelServerBase) UnregisterConnection(ctx context.Context) {
panic("unexpected call to UnregisterConnection")
}
func (mockTunnelServerBase) RegisterTunnel(ctx context.Context, originCert []byte, hostname string, options *RegistrationOptions) *TunnelRegistration {
panic("unexpected call to RegisterTunnel")
}
func (mockTunnelServerBase) GetServerInfo(ctx context.Context) (*ServerInfo, error) {
panic("unexpected call to GetServerInfo")
}
func (mockTunnelServerBase) UnregisterTunnel(ctx context.Context, gracePeriodNanoSec int64) error {
panic("unexpected call to UnregisterTunnel")
}
func (mockTunnelServerBase) Authenticate(ctx context.Context, originCert []byte, hostname string, options *RegistrationOptions) (*AuthenticateResponse, error) {
panic("unexpected call to Authenticate")
}
func (mockTunnelServerBase) ReconnectTunnel(ctx context.Context, jwt, eventDigest, connDigest []byte, hostname string, options *RegistrationOptions) (*TunnelRegistration, error) {
panic("unexpected call to ReconnectTunnel")
}
+1
View File
@@ -201,6 +201,7 @@ func UnmarshalServerInfo(s tunnelrpc.ServerInfo) (*ServerInfo, error) {
}
type TunnelServer interface {
RegistrationServer
RegisterTunnel(ctx context.Context, originCert []byte, hostname string, options *RegistrationOptions) *TunnelRegistration
GetServerInfo(ctx context.Context) (*ServerInfo, error)
UnregisterTunnel(ctx context.Context, gracePeriodNanoSec int64) error
+54 -1
View File
@@ -78,7 +78,60 @@ struct AuthenticateResponse {
hoursUntilRefresh @3 :UInt8;
}
interface TunnelServer {
struct ClientInfo {
# The tunnel client's unique identifier, used to verify a reconnection.
clientId @0 :Data;
# Set of features this cloudflared knows it supports
features @1 :List(Text);
# Information about the running binary.
version @2 :Text;
# Client OS and CPU info
arch @3 :Text;
}
struct ConnectionOptions {
# client details
client @0 :ClientInfo;
# origin LAN IP
originLocalIp @1 :Data;
# What to do if connection already exists
replaceExisting @2 :Bool;
# cross stream compression setting, 0 - off, 3 - high
compressionQuality @3 :UInt8;
}
struct ConnectionResponse {
result :union {
error @0 :ConnectionError;
connectionDetails @1 :ConnectionDetails;
}
}
struct ConnectionError {
cause @0 :Text;
# How long should this connection wait to retry in ns
retryAfter @1 :Int64;
shouldRetry @2 :Bool;
}
struct ConnectionDetails {
# identifier of this connection
uuid @0 :Data;
# airport code of the colo where this connection landed
locationName @1 :Text;
}
struct TunnelAuth {
accountTag @0 :Text;
tunnelSecret @1 :Data;
}
interface RegistrationServer {
registerConnection @0 (auth :TunnelAuth, tunnelId :Data, connIndex :UInt8, options :ConnectionOptions) -> (result :ConnectionResponse);
unregisterConnection @1 () -> ();
}
interface TunnelServer extends (RegistrationServer) {
registerTunnel @0 (originCert :Data, hostname :Text, options :RegistrationOptions) -> (result :TunnelRegistration);
getServerInfo @1 () -> (result :ServerInfo);
unregisterTunnel @2 (gracePeriodNanoSec :Int64) -> ();
+1366 -126
View File
File diff suppressed because it is too large Load Diff
+6 -4
View File
@@ -39,7 +39,7 @@ type Connection struct {
}
type Client interface {
CreateTunnel(name string) (*Tunnel, error)
CreateTunnel(name string, tunnelSecret []byte) (*Tunnel, error)
GetTunnel(id string) (*Tunnel, error)
DeleteTunnel(id string) error
ListTunnels() ([]Tunnel, error)
@@ -74,15 +74,17 @@ func NewRESTClient(baseURL string, accountTag string, authToken string, logger l
}
type newTunnel struct {
Name string `json:"name"`
Name string `json:"name"`
TunnelSecret []byte `json:"tunnel_secret"`
}
func (r *RESTClient) CreateTunnel(name string) (*Tunnel, error) {
func (r *RESTClient) CreateTunnel(name string, tunnelSecret []byte) (*Tunnel, error) {
if name == "" {
return nil, errors.New("tunnel name required")
}
body, err := json.Marshal(&newTunnel{
Name: name,
Name: name,
TunnelSecret: tunnelSecret,
})
if err != nil {
return nil, errors.Wrap(err, "Failed to serialize new tunnel request")