Last active
February 23, 2025 19:28
-
-
Save a5r0n/68447f3a5610f1f985357a499147735a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"context" | |
"crypto/tls" | |
"encoding/json" | |
"flag" | |
"fmt" | |
"io" | |
"net/http" | |
"os" | |
"runtime" | |
"strings" | |
"time" | |
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" | |
"github.com/cloudflare/cloudflared/config" | |
"github.com/cloudflare/cloudflared/connection" | |
"github.com/cloudflare/cloudflared/edgediscovery" | |
"github.com/cloudflare/cloudflared/edgediscovery/allregions" | |
"github.com/cloudflare/cloudflared/features" | |
"github.com/cloudflare/cloudflared/ingress" | |
"github.com/cloudflare/cloudflared/logger" | |
"github.com/cloudflare/cloudflared/metrics" | |
"github.com/cloudflare/cloudflared/orchestration" | |
"github.com/cloudflare/cloudflared/signal" | |
"github.com/cloudflare/cloudflared/supervisor" | |
"github.com/cloudflare/cloudflared/tlsconfig" | |
"github.com/cloudflare/cloudflared/tunnelrpc/pogs" | |
"github.com/google/uuid" | |
"github.com/pkg/errors" | |
"github.com/urfave/cli/v2" | |
) | |
var graceShutdownC chan struct{} | |
var ( | |
Version = "DEV" | |
BuildTime = "unknown" | |
BuildType = "" | |
bInfo *cliutil.BuildInfo | |
) | |
const httpTimeout = 15 * time.Second | |
func createQuickTunnel(clientID uuid.UUID) (*connection.TunnelProperties, error) { | |
client := http.Client{ | |
Transport: &http.Transport{ | |
TLSHandshakeTimeout: httpTimeout, | |
ResponseHeaderTimeout: httpTimeout, | |
}, | |
Timeout: httpTimeout, | |
} | |
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/tunnel", "https://api.trycloudflare.com"), nil) | |
if err != nil { | |
return nil, errors.Wrap(err, "failed to build quick tunnel request") | |
} | |
req.Header.Add("Content-Type", "application/json") | |
req.Header.Add("User-Agent", bInfo.UserAgent()) | |
resp, err := client.Do(req) | |
if err != nil { | |
return nil, errors.Wrap(err, "failed to request quick Tunnel") | |
} | |
defer resp.Body.Close() | |
// This will read the entire response into memory so we can print it in case of error | |
rsp_body, err := io.ReadAll(resp.Body) | |
if err != nil { | |
return nil, errors.Wrap(err, "failed to read quick-tunnel response") | |
} | |
var data QuickTunnelResponse | |
if err := json.Unmarshal(rsp_body, &data); err != nil { | |
return nil, errors.Wrap(err, "failed to unmarshal quick Tunnel") | |
} | |
tunnelID, err := uuid.Parse(data.Result.ID) | |
if err != nil { | |
return nil, errors.Wrap(err, "failed to parse quick Tunnel ID") | |
} | |
credentials := connection.Credentials{ | |
AccountTag: data.Result.AccountTag, | |
TunnelSecret: data.Result.Secret, | |
TunnelID: tunnelID, | |
} | |
url := data.Result.Hostname | |
if !strings.HasPrefix(url, "https://") { | |
url = "https://" + url | |
} | |
fmt.Println(url) | |
pogslient := pogs.ClientInfo{ | |
ClientID: clientID[:], | |
Features: []string{}, | |
Version: bInfo.Version(), | |
Arch: bInfo.OSArch(), | |
} | |
return &connection.TunnelProperties{Credentials: credentials, QuickTunnelUrl: data.Result.Hostname, Client: pogslient}, nil | |
} | |
func main() { | |
os.Setenv("QUIC_GO_DISABLE_ECN", "1") | |
metrics.RegisterBuildInfo(BuildType, BuildTime, Version) | |
bInfo = cliutil.GetBuildInfo(BuildType, Version) | |
ctx := context.Background() | |
clientID, err := uuid.NewRandom() | |
if err != nil { | |
panic(errors.Wrap(err, "can't generate connector UUID")) | |
} | |
println("Generated Connector ID: " + clientID.String()) | |
logTransport := logger.Create(logger.CreateConfig( | |
"", | |
false, | |
"", | |
"", | |
)) | |
observer := connection.NewObserver(logTransport, logTransport) // log, logTransport | |
ing, err := ingress.ParseIngress(&config.Configuration{ | |
Ingress: []config.UnvalidatedIngressRule{ | |
{ | |
Service: "http://localhost:8080", | |
}, | |
}, | |
}) | |
if err != nil { | |
panic(err) | |
} | |
orchestrator, err := orchestration.NewOrchestrator( | |
ctx, | |
&orchestration.Config{ | |
Ingress: &ing, | |
WarpRouting: ingress.NewWarpRoutingConfig(&config.WarpRoutingConfig{}), // Defaults | |
ConfigurationFlags: map[string]string{}, | |
WriteTimeout: 0, // write-stream-timeout, default is 0 | |
}, | |
[]pogs.Tag{}, | |
[]ingress.Rule{}, | |
logTransport, | |
) | |
if err != nil { | |
panic(err) | |
} | |
connectedSignal := signal.New(make(chan struct{})) | |
reconnectCh := make(chan supervisor.ReconnectSignal, 4) // 4 is default | |
protocolSelector, err := connection.NewProtocolSelector( | |
connection.HTTP2.String(), | |
"random value", // TODO credentials account tag. But it's just used as a hash | |
false, | |
false, | |
edgediscovery.ProtocolPercentage, | |
connection.ResolveTTL, | |
logTransport, | |
) | |
if err != nil { | |
panic(errors.Wrap(err, "unable to create protocol selector")) | |
} | |
edgeTLSConfigs := make(map[connection.Protocol]*tls.Config, len(connection.ProtocolList)) | |
for _, p := range connection.ProtocolList { | |
tlsSettings := p.TLSSettings() | |
if tlsSettings == nil { | |
panic(fmt.Errorf("%s has unknown TLS settings", p)) | |
} | |
edgeTLSConfig, err := tlsconfig.CreateTunnelConfig(cli.NewContext(cli.NewApp(), &flag.FlagSet{}, nil), tlsSettings.ServerName) | |
if err != nil { | |
panic(errors.Wrap(err, "unable to create TLS config to connect with edge")) | |
} | |
if len(tlsSettings.NextProtos) > 0 { | |
edgeTLSConfig.NextProtos = tlsSettings.NextProtos | |
} | |
edgeTLSConfigs[p] = edgeTLSConfig | |
} | |
tunnel, err := createQuickTunnel(clientID) | |
if err != nil { | |
panic(err) | |
} | |
tunnelConfig := &supervisor.TunnelConfig{ | |
GracePeriod: 30, // grace-period, default is 30 | |
ReplaceExisting: false, // force | |
OSArch: runtime.GOOS + "_" + runtime.GOARCH, | |
ClientID: clientID.String(), | |
EdgeAddrs: []string{}, | |
Region: "", | |
EdgeIPVersion: allregions.Auto, // Default is ipv4 | |
EdgeBindAddr: nil, // default is to let cf handle it | |
HAConnections: 4, // 4 is default | |
IsAutoupdated: false, | |
LBPool: "", // looks interesting? | |
Tags: []pogs.Tag{}, | |
Log: logTransport, | |
LogTransport: logTransport, | |
Observer: observer, | |
ReportedVersion: "embedded-go-test", | |
Retries: 5, // retries, default is 5 | |
RunFromTerminal: true, // todo false | |
NamedTunnel: tunnel, | |
ProtocolSelector: protocolSelector, | |
EdgeTLSConfigs: edgeTLSConfigs, | |
FeatureSelector: &features.FeatureSelector{}, // I think this should be OK | |
MaxEdgeAddrRetries: 8, // max-edge-addr-retries, default is 8 | |
RPCTimeout: 5 * time.Second, // rpc-timeout, default is 5s | |
WriteStreamTimeout: time.Second * 0, | |
DisableQUICPathMTUDiscovery: false, | |
QUICConnectionLevelFlowControlLimit: 30 * (1 << 20), // default is 30MB | |
QUICStreamLevelFlowControlLimit: 6 * (1 << 20), // default is 6MB | |
ICMPRouterServer: nil, | |
} | |
supervisor.StartTunnelDaemon(ctx, tunnelConfig, orchestrator, connectedSignal, reconnectCh, graceShutdownC) | |
} | |
type QuickTunnelResponse struct { | |
Success bool | |
Result QuickTunnel | |
Errors []QuickTunnelError | |
} | |
type QuickTunnelError struct { | |
Code int | |
Message string | |
} | |
type QuickTunnel struct { | |
ID string `json:"id"` | |
Name string `json:"name"` | |
Hostname string `json:"hostname"` | |
AccountTag string `json:"account_tag"` | |
Secret []byte `json:"secret"` | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment