cloudflare/cloudflared

Public

mirrored from https://github.com/cloudflare/cloudflaredAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2024.7.2

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

connection/control.go

144lines · modecode

1package connection
2
3import (
4 "context"
5 "io"
6 "net"
7 "time"
8
9 "github.com/cloudflare/cloudflared/management"
10 "github.com/cloudflare/cloudflared/tunnelrpc"
11 tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
12)
13
14// registerClient derives a named tunnel rpc client that can then be used to register and unregister connections.
15type registerClientFunc func(context.Context, io.ReadWriteCloser, time.Duration) tunnelrpc.RegistrationClient
16
17type controlStream struct {
18 observer *Observer
19
20 connectedFuse ConnectedFuse
21 tunnelProperties *TunnelProperties
22 connIndex uint8
23 edgeAddress net.IP
24 protocol Protocol
25
26 registerClientFunc registerClientFunc
27 registerTimeout time.Duration
28
29 gracefulShutdownC <-chan struct{}
30 gracePeriod time.Duration
31 stoppedGracefully bool
32}
33
34// ControlStreamHandler registers connections with origintunneld and initiates graceful shutdown.
35type ControlStreamHandler interface {
36 // ServeControlStream handles the control plane of the transport in the current goroutine calling this
37 ServeControlStream(ctx context.Context, rw io.ReadWriteCloser, connOptions *tunnelpogs.ConnectionOptions, tunnelConfigGetter TunnelConfigJSONGetter) error
38 // IsStopped tells whether the method above has finished
39 IsStopped() bool
40}
41
42type TunnelConfigJSONGetter interface {
43 GetConfigJSON() ([]byte, error)
44}
45
46// NewControlStream returns a new instance of ControlStreamHandler
47func NewControlStream(
48 observer *Observer,
49 connectedFuse ConnectedFuse,
50 tunnelProperties *TunnelProperties,
51 connIndex uint8,
52 edgeAddress net.IP,
53 registerClientFunc registerClientFunc,
54 registerTimeout time.Duration,
55 gracefulShutdownC <-chan struct{},
56 gracePeriod time.Duration,
57 protocol Protocol,
58) ControlStreamHandler {
59 if registerClientFunc == nil {
60 registerClientFunc = tunnelrpc.NewRegistrationClient
61 }
62 return &controlStream{
63 observer: observer,
64 connectedFuse: connectedFuse,
65 tunnelProperties: tunnelProperties,
66 registerClientFunc: registerClientFunc,
67 registerTimeout: registerTimeout,
68 connIndex: connIndex,
69 edgeAddress: edgeAddress,
70 gracefulShutdownC: gracefulShutdownC,
71 gracePeriod: gracePeriod,
72 protocol: protocol,
73 }
74}
75
76func (c *controlStream) ServeControlStream(
77 ctx context.Context,
78 rw io.ReadWriteCloser,
79 connOptions *tunnelpogs.ConnectionOptions,
80 tunnelConfigGetter TunnelConfigJSONGetter,
81) error {
82 registrationClient := c.registerClientFunc(ctx, rw, c.registerTimeout)
83
84 registrationDetails, err := registrationClient.RegisterConnection(
85 ctx,
86 c.tunnelProperties.Credentials.Auth(),
87 c.tunnelProperties.Credentials.TunnelID,
88 connOptions,
89 c.connIndex,
90 c.edgeAddress)
91 if err != nil {
92 defer registrationClient.Close()
93 if err.Error() == DuplicateConnectionError {
94 c.observer.metrics.regFail.WithLabelValues("dup_edge_conn", "registerConnection").Inc()
95 return errDuplicationConnection
96 }
97 c.observer.metrics.regFail.WithLabelValues("server_error", "registerConnection").Inc()
98 return serverRegistrationErrorFromRPC(err)
99 }
100 c.observer.metrics.regSuccess.WithLabelValues("registerConnection").Inc()
101
102 c.observer.logConnected(registrationDetails.UUID, c.connIndex, registrationDetails.Location, c.edgeAddress, c.protocol)
103 c.observer.sendConnectedEvent(c.connIndex, c.protocol, registrationDetails.Location)
104 c.connectedFuse.Connected()
105
106 // if conn index is 0 and tunnel is not remotely managed, then send local ingress rules configuration
107 if c.connIndex == 0 && !registrationDetails.TunnelIsRemotelyManaged {
108 if tunnelConfig, err := tunnelConfigGetter.GetConfigJSON(); err == nil {
109 if err := registrationClient.SendLocalConfiguration(ctx, tunnelConfig); err != nil {
110 c.observer.metrics.localConfigMetrics.pushesErrors.Inc()
111 c.observer.log.Err(err).Msg("unable to send local configuration")
112 }
113 c.observer.metrics.localConfigMetrics.pushes.Inc()
114 } else {
115 c.observer.log.Err(err).Msg("failed to obtain current configuration")
116 }
117 }
118
119 c.waitForUnregister(ctx, registrationClient)
120 return nil
121}
122
123func (c *controlStream) waitForUnregister(ctx context.Context, registrationClient tunnelrpc.RegistrationClient) {
124 // wait for connection termination or start of graceful shutdown
125 defer registrationClient.Close()
126 select {
127 case <-ctx.Done():
128 break
129 case <-c.gracefulShutdownC:
130 c.stoppedGracefully = true
131 }
132
133 c.observer.sendUnregisteringEvent(c.connIndex)
134 registrationClient.GracefulShutdown(ctx, c.gracePeriod)
135 c.observer.log.Info().
136 Int(management.EventTypeKey, int(management.Cloudflared)).
137 Uint8(LogFieldConnIndex, c.connIndex).
138 IPAddr(LogFieldIPAddress, c.edgeAddress).
139 Msg("Unregistered tunnel connection")
140}
141
142func (c *controlStream) IsStopped() bool {
143 return c.stoppedGracefully
144}
145