Skip to content

Commit 1cedefa

Browse files
committed
TUN-9583: set proper url and hostname for cloudflared tail command
This commit adds support for FedRAMP environments. Cloudflared will now dynamically configure the management hostname and API URL, switching to FedRAMP-specific values like `management.fed.argotunnel.com` and `https://api.fed.cloudflare.com/client/v4` when a FedRAMP endpoint is detected. Key to this is an enhanced `ParseToken` function, which now includes an `IsFed()` method to determine if a management token's issuer is `fed-tunnelstore`. This allows cloudflared to correctly identify and operate within a FedRAMP context, ensuring proper connectivity. Closes TUN-9583
1 parent ddf4e6d commit 1cedefa

File tree

7 files changed

+60
-19
lines changed

7 files changed

+60
-19
lines changed

cmd/cloudflared/flags/flags.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,7 @@ const (
160160

161161
// Virtual DNS resolver service resolver addresses to use instead of dynamically fetching them from the OS.
162162
VirtualDNSServiceResolverAddresses = "dns-resolver-addrs"
163+
164+
// Management hostname to signify incoming management requests
165+
ManagementHostname = "management-hostname"
163166
)

cmd/cloudflared/tail/cmd.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func buildTailManagementTokenSubcommand() *cli.Command {
5151

5252
func managementTokenCommand(c *cli.Context) error {
5353
log := createLogger(c)
54+
5455
token, err := getManagementToken(c, log)
5556
if err != nil {
5657
return err
@@ -99,7 +100,7 @@ func buildTailCommand(subcommands []*cli.Command) *cli.Command {
99100
EnvVars: []string{"TUNNEL_MANAGEMENT_TOKEN"},
100101
},
101102
&cli.StringFlag{
102-
Name: "management-hostname",
103+
Name: cfdflags.ManagementHostname,
103104
Usage: "Management hostname to signify incoming management requests",
104105
EnvVars: []string{"TUNNEL_MANAGEMENT_HOSTNAME"},
105106
Hidden: true,
@@ -236,7 +237,14 @@ func getManagementToken(c *cli.Context, log *zerolog.Logger) (string, error) {
236237
return "", err
237238
}
238239

239-
client, err := userCreds.Client(c.String(cfdflags.ApiURL), buildInfo.UserAgent(), log)
240+
var apiURL string
241+
if userCreds.IsFEDEndpoint() {
242+
apiURL = credentials.FedRampBaseApiURL
243+
} else {
244+
apiURL = c.String(cfdflags.ApiURL)
245+
}
246+
247+
client, err := userCreds.Client(apiURL, buildInfo.UserAgent(), log)
240248
if err != nil {
241249
return "", err
242250
}
@@ -261,14 +269,27 @@ func getManagementToken(c *cli.Context, log *zerolog.Logger) (string, error) {
261269
// buildURL will build the management url to contain the required query parameters to authenticate the request.
262270
func buildURL(c *cli.Context, log *zerolog.Logger) (url.URL, error) {
263271
var err error
264-
managementHostname := c.String("management-hostname")
272+
265273
token := c.String("token")
266274
if token == "" {
267275
token, err = getManagementToken(c, log)
268276
if err != nil {
269277
return url.URL{}, fmt.Errorf("unable to acquire management token for requested tunnel id: %w", err)
270278
}
271279
}
280+
281+
claims, err := management.ParseToken(token)
282+
if err != nil {
283+
return url.URL{}, fmt.Errorf("failed to determine if token is FED: %w", err)
284+
}
285+
286+
var managementHostname string
287+
if claims.IsFed() {
288+
managementHostname = credentials.FedRampHostname
289+
} else {
290+
managementHostname = c.String(cfdflags.ManagementHostname)
291+
}
292+
272293
query := url.Values{}
273294
query.Add("access_token", token)
274295
connector := c.String("connector-id")

cmd/cloudflared/tunnel/cmd.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ var (
9797
"no-tls-verify",
9898
"no-chunked-encoding",
9999
"http2-origin",
100-
"management-hostname",
100+
cfdflags.ManagementHostname,
101101
"service-op-ip",
102102
"local-ssh-port",
103103
"ssh-idle-timeout",
@@ -459,8 +459,23 @@ func StartServer(
459459
}
460460
}
461461

462+
userCreds, err := credentials.Read(c.String(cfdflags.OriginCert), log)
463+
var isFEDEndpoint bool
464+
if err != nil {
465+
isFEDEndpoint = false
466+
} else {
467+
isFEDEndpoint = userCreds.IsFEDEndpoint()
468+
}
469+
470+
var managementHostname string
471+
if isFEDEndpoint {
472+
managementHostname = credentials.FedRampHostname
473+
} else {
474+
managementHostname = c.String(cfdflags.ManagementHostname)
475+
}
476+
462477
mgmt := management.New(
463-
c.String("management-hostname"),
478+
managementHostname,
464479
c.Bool("management-diagnostics"),
465480
serviceIP,
466481
connectorID,
@@ -1042,7 +1057,7 @@ func configureProxyFlags(shouldHide bool) []cli.Flag {
10421057
Value: false,
10431058
}),
10441059
altsrc.NewStringFlag(&cli.StringFlag{
1045-
Name: "management-hostname",
1060+
Name: cfdflags.ManagementHostname,
10461061
Usage: "Management hostname to signify incoming management requests",
10471062
EnvVars: []string{"TUNNEL_MANAGEMENT_HOSTNAME"},
10481063
Hidden: true,

credentials/credentials.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
const (
1111
logFieldOriginCertPath = "originCertPath"
1212
FedEndpoint = "fed"
13+
FedRampBaseApiURL = "https://api.fed.cloudflare.com/client/v4"
14+
FedRampHostname = "management.fed.argotunnel.com"
1315
)
1416

1517
type User struct {

management/middleware.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,7 @@ const (
1212
accessClaimsCtxKey ctxKey = iota
1313
)
1414

15-
const (
16-
connectorIDQuery = "connector_id"
17-
accessTokenQuery = "access_token"
18-
)
19-
20-
var (
21-
errMissingAccessToken = managementError{Code: 1001, Message: "missing access_token query parameter"}
22-
)
15+
var errMissingAccessToken = managementError{Code: 1001, Message: "missing access_token query parameter"}
2316

2417
// HTTP middleware setting the parsed access_token claims in the request context
2518
func ValidateAccessTokenQueryMiddleware(next http.Handler) http.Handler {
@@ -30,7 +23,7 @@ func ValidateAccessTokenQueryMiddleware(next http.Handler) http.Handler {
3023
writeHTTPErrorResponse(w, errMissingAccessToken)
3124
return
3225
}
33-
token, err := parseToken(accessToken)
26+
token, err := ParseToken(accessToken)
3427
if err != nil {
3528
writeHTTPErrorResponse(w, errMissingAccessToken)
3629
return

management/token.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ import (
77
"github.com/go-jose/go-jose/v4/jwt"
88
)
99

10+
const tunnelstoreFEDIssuer = "fed-tunnelstore"
11+
1012
type managementTokenClaims struct {
1113
Tunnel tunnel `json:"tun"`
1214
Actor actor `json:"actor"`
15+
jwt.Claims
1316
}
1417

1518
// VerifyTunnel compares the tun claim isn't empty
@@ -37,7 +40,7 @@ func (t *actor) verify() bool {
3740
return t.ID != ""
3841
}
3942

40-
func parseToken(token string) (*managementTokenClaims, error) {
43+
func ParseToken(token string) (*managementTokenClaims, error) {
4144
jwt, err := jwt.ParseSigned(token, []jose.SignatureAlgorithm{jose.ES256})
4245
if err != nil {
4346
return nil, fmt.Errorf("malformed jwt: %v", err)
@@ -54,3 +57,7 @@ func parseToken(token string) (*managementTokenClaims, error) {
5457
}
5558
return &claims, nil
5659
}
60+
61+
func (m *managementTokenClaims) IsFed() bool {
62+
return m.Issuer == tunnelstoreFEDIssuer
63+
}

management/token_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
)
1313

1414
const (
15-
validToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.eyJ0dW4iOnsiaWQiOiI3YjA5ODE0OS01MWZlLTRlZTUtYTY4Ny0zZTM3NDQ2NmVmYzciLCJhY2NvdW50X3RhZyI6ImNkMzkxZTljMDYyNmE4Zjc2Y2IxZjY3MGY2NTkxYjA1In0sImFjdG9yIjp7ImlkIjoiZGNhcnJAY2xvdWRmbGFyZS5jb20iLCJzdXBwb3J0IjpmYWxzZX0sInJlcyI6WyJsb2dzIl0sImV4cCI6MTY3NzExNzY5NiwiaWF0IjoxNjc3MTE0MDk2LCJpc3MiOiJ0dW5uZWxzdG9yZSJ9.mKenOdOy3Xi4O-grldFnAAemdlE9WajEpTDC_FwezXQTstWiRTLwU65P5jt4vNsIiZA4OJRq7bH-QYID9wf9NA"
15+
validToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.eyJ0dW4iOnsiaWQiOiI3YjA5ODE0OS01MWZlLTRlZTUtYTY4Ny0zZTM3NDQ2NmVmYzciLCJhY2NvdW50X3RhZyI6ImNkMzkxZTljMDYyNmE4Zjc2Y2IxZjY3MGY2NTkxYjA1In0sImFjdG9yIjp7ImlkIjoiZGNhcnJAY2xvdWRmbGFyZS5jb20iLCJzdXBwb3J0IjpmYWxzZX0sInJlcyI6WyJsb2dzIl0sImV4cCI6MTY3NzExNzY5NiwiaWF0IjoxNjc3MTE0MDk2LCJpc3MiOiJ0dW5uZWxzdG9yZSJ9.mKenOdOy3Xi4O-grldFnAAemdlE9WajEpTDC_FwezXQTstWiRTLwU65P5jt4vNsIiZA4OJRq7bH-QYID9wf9NA" // nolint: gosec
1616

1717
accountTag = "cd391e9c0626a8f76cb1f670f6591b05"
1818
tunnelID = "7b098149-51fe-4ee5-a687-3e374466efc7"
@@ -105,12 +105,12 @@ func TestParseToken(t *testing.T) {
105105
} {
106106
t.Run(test.name, func(t *testing.T) {
107107
jwt := signToken(t, test.claims, key)
108-
claims, err := parseToken(jwt)
108+
claims, err := ParseToken(jwt)
109109
if test.err != nil {
110110
require.EqualError(t, err, test.err.Error())
111111
return
112112
}
113-
require.Nil(t, err)
113+
require.NoError(t, err)
114114
require.Equal(t, test.claims, *claims)
115115
})
116116
}

0 commit comments

Comments
 (0)