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
This commit is contained in:
Luis Neto
2025-07-23 20:09:50 +01:00
parent ddf4e6d854
commit 1cedefa1c2
7 changed files with 60 additions and 19 deletions

View File

@@ -12,14 +12,7 @@ const (
accessClaimsCtxKey ctxKey = iota
)
const (
connectorIDQuery = "connector_id"
accessTokenQuery = "access_token"
)
var (
errMissingAccessToken = managementError{Code: 1001, Message: "missing access_token query parameter"}
)
var errMissingAccessToken = managementError{Code: 1001, Message: "missing access_token query parameter"}
// HTTP middleware setting the parsed access_token claims in the request context
func ValidateAccessTokenQueryMiddleware(next http.Handler) http.Handler {
@@ -30,7 +23,7 @@ func ValidateAccessTokenQueryMiddleware(next http.Handler) http.Handler {
writeHTTPErrorResponse(w, errMissingAccessToken)
return
}
token, err := parseToken(accessToken)
token, err := ParseToken(accessToken)
if err != nil {
writeHTTPErrorResponse(w, errMissingAccessToken)
return

View File

@@ -7,9 +7,12 @@ import (
"github.com/go-jose/go-jose/v4/jwt"
)
const tunnelstoreFEDIssuer = "fed-tunnelstore"
type managementTokenClaims struct {
Tunnel tunnel `json:"tun"`
Actor actor `json:"actor"`
jwt.Claims
}
// VerifyTunnel compares the tun claim isn't empty
@@ -37,7 +40,7 @@ func (t *actor) verify() bool {
return t.ID != ""
}
func parseToken(token string) (*managementTokenClaims, error) {
func ParseToken(token string) (*managementTokenClaims, error) {
jwt, err := jwt.ParseSigned(token, []jose.SignatureAlgorithm{jose.ES256})
if err != nil {
return nil, fmt.Errorf("malformed jwt: %v", err)
@@ -54,3 +57,7 @@ func parseToken(token string) (*managementTokenClaims, error) {
}
return &claims, nil
}
func (m *managementTokenClaims) IsFed() bool {
return m.Issuer == tunnelstoreFEDIssuer
}

View File

@@ -12,7 +12,7 @@ import (
)
const (
validToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.eyJ0dW4iOnsiaWQiOiI3YjA5ODE0OS01MWZlLTRlZTUtYTY4Ny0zZTM3NDQ2NmVmYzciLCJhY2NvdW50X3RhZyI6ImNkMzkxZTljMDYyNmE4Zjc2Y2IxZjY3MGY2NTkxYjA1In0sImFjdG9yIjp7ImlkIjoiZGNhcnJAY2xvdWRmbGFyZS5jb20iLCJzdXBwb3J0IjpmYWxzZX0sInJlcyI6WyJsb2dzIl0sImV4cCI6MTY3NzExNzY5NiwiaWF0IjoxNjc3MTE0MDk2LCJpc3MiOiJ0dW5uZWxzdG9yZSJ9.mKenOdOy3Xi4O-grldFnAAemdlE9WajEpTDC_FwezXQTstWiRTLwU65P5jt4vNsIiZA4OJRq7bH-QYID9wf9NA"
validToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjEifQ.eyJ0dW4iOnsiaWQiOiI3YjA5ODE0OS01MWZlLTRlZTUtYTY4Ny0zZTM3NDQ2NmVmYzciLCJhY2NvdW50X3RhZyI6ImNkMzkxZTljMDYyNmE4Zjc2Y2IxZjY3MGY2NTkxYjA1In0sImFjdG9yIjp7ImlkIjoiZGNhcnJAY2xvdWRmbGFyZS5jb20iLCJzdXBwb3J0IjpmYWxzZX0sInJlcyI6WyJsb2dzIl0sImV4cCI6MTY3NzExNzY5NiwiaWF0IjoxNjc3MTE0MDk2LCJpc3MiOiJ0dW5uZWxzdG9yZSJ9.mKenOdOy3Xi4O-grldFnAAemdlE9WajEpTDC_FwezXQTstWiRTLwU65P5jt4vNsIiZA4OJRq7bH-QYID9wf9NA" // nolint: gosec
accountTag = "cd391e9c0626a8f76cb1f670f6591b05"
tunnelID = "7b098149-51fe-4ee5-a687-3e374466efc7"
@@ -105,12 +105,12 @@ func TestParseToken(t *testing.T) {
} {
t.Run(test.name, func(t *testing.T) {
jwt := signToken(t, test.claims, key)
claims, err := parseToken(jwt)
claims, err := ParseToken(jwt)
if test.err != nil {
require.EqualError(t, err, test.err.Error())
return
}
require.Nil(t, err)
require.NoError(t, err)
require.Equal(t, test.claims, *claims)
})
}