From 8825ceecb533ddb84de04549b0640c76c6c52514 Mon Sep 17 00:00:00 2001 From: Kyle Hiller Date: Tue, 19 Aug 2025 18:54:31 +0000 Subject: [PATCH] AUTH-7480 update fed callback url for login helper * AUTH-7480 update fed callback url for login helper --- carrier/carrier.go | 3 ++- cmd/cloudflared/access/carrier.go | 2 ++ cmd/cloudflared/access/cmd.go | 11 ++++++++--- cmd/cloudflared/tunnel/login.go | 10 +++++----- config/model.go | 29 +++++++++++++++-------------- token/token.go | 16 ++++++++-------- token/transfer.go | 20 ++++++++++++++++---- 7 files changed, 56 insertions(+), 35 deletions(-) diff --git a/carrier/carrier.go b/carrier/carrier.go index ed493953..5f4fc834 100644 --- a/carrier/carrier.go +++ b/carrier/carrier.go @@ -32,6 +32,7 @@ type StartOptions struct { Host string TLSClientConfig *tls.Config AutoCloseInterstitial bool + IsFedramp bool } // Connection wraps up all the needed functions to forward over the tunnel @@ -139,7 +140,7 @@ func BuildAccessRequest(options *StartOptions, log *zerolog.Logger) (*http.Reque return nil, err } - token, err := token.FetchTokenWithRedirect(req.URL, options.AppInfo, options.AutoCloseInterstitial, log) + token, err := token.FetchTokenWithRedirect(req.URL, options.AppInfo, options.AutoCloseInterstitial, options.IsFedramp, log) if err != nil { return nil, err } diff --git a/cmd/cloudflared/access/carrier.go b/cmd/cloudflared/access/carrier.go index 11190de7..52aacc56 100644 --- a/cmd/cloudflared/access/carrier.go +++ b/cmd/cloudflared/access/carrier.go @@ -47,6 +47,7 @@ func StartForwarder(forwarder config.Forwarder, shutdown <-chan struct{}, log *z options := &carrier.StartOptions{ OriginURL: forwarder.URL, Headers: headers, //TODO: TUN-2688 support custom headers from config file + IsFedramp: forwarder.IsFedramp, } // we could add a cmd line variable for this bool if we want the SOCK5 server to be on the client side @@ -92,6 +93,7 @@ func ssh(c *cli.Context) error { OriginURL: url.String(), Headers: headers, Host: url.Host, + IsFedramp: c.Bool(fedrampFlag), } if connectTo := c.String(sshConnectTo); connectTo != "" { diff --git a/cmd/cloudflared/access/cmd.go b/cmd/cloudflared/access/cmd.go index d491cf55..636b9288 100644 --- a/cmd/cloudflared/access/cmd.go +++ b/cmd/cloudflared/access/cmd.go @@ -51,6 +51,7 @@ Host {{.Hostname}} ProxyCommand {{.Cloudflared}} access ssh --hostname %h {{end}} ` + fedrampFlag = "fedramp" ) const sentryDSN = "https://56a9c9fa5c364ab28f34b14f35ea0f1b@sentry.io/189878" @@ -79,6 +80,10 @@ func Commands() []*cli.Command { Aliases: []string{"forward"}, Category: "Access", Usage: "access ", + Flags: []cli.Flag{&cli.BoolFlag{ + Name: fedrampFlag, + Usage: "use when performing operations in fedramp account", + }}, Description: `Cloudflare Access protects internal resources by securing, authenticating and monitoring access per-user and by application. With Cloudflare Access, only authenticated users with the required permissions are able to reach sensitive resources. The commands provided here allow you to interact with Access protected @@ -326,7 +331,7 @@ func curl(c *cli.Context) error { log.Info().Msg("You don't have an Access token set. Please run access token to fetch one.") return run("curl", cmdArgs...) } - tok, err = token.FetchToken(appURL, appInfo, c.Bool(cfdflags.AutoCloseInterstitial), log) + tok, err = token.FetchToken(appURL, appInfo, c.Bool(cfdflags.AutoCloseInterstitial), c.Bool(fedrampFlag), log) if err != nil { log.Err(err).Msg("Failed to refresh token") return err @@ -446,7 +451,7 @@ func sshGen(c *cli.Context) error { if err != nil { return err } - cfdToken, err := token.FetchTokenWithRedirect(fetchTokenURL, appInfo, c.Bool(cfdflags.AutoCloseInterstitial), log) + cfdToken, err := token.FetchTokenWithRedirect(fetchTokenURL, appInfo, c.Bool(cfdflags.AutoCloseInterstitial), c.Bool(fedrampFlag), log) if err != nil { return err } @@ -546,7 +551,7 @@ func verifyTokenAtEdge(appUrl *url.URL, appInfo *token.AppInfo, c *cli.Context, if c.IsSet(sshTokenSecretFlag) { headers.Add(cfAccessClientSecretHeader, c.String(sshTokenSecretFlag)) } - options := &carrier.StartOptions{AppInfo: appInfo, OriginURL: appUrl.String(), Headers: headers, AutoCloseInterstitial: c.Bool(cfdflags.AutoCloseInterstitial)} + options := &carrier.StartOptions{AppInfo: appInfo, OriginURL: appUrl.String(), Headers: headers, AutoCloseInterstitial: c.Bool(cfdflags.AutoCloseInterstitial), IsFedramp: c.Bool(fedrampFlag)} if valid, err := isTokenValid(options, log); err != nil { return err diff --git a/cmd/cloudflared/tunnel/login.go b/cmd/cloudflared/tunnel/login.go index d231e666..067a8c5a 100644 --- a/cmd/cloudflared/tunnel/login.go +++ b/cmd/cloudflared/tunnel/login.go @@ -20,11 +20,10 @@ import ( ) const ( - baseLoginURL = "https://dash.cloudflare.com/argotunnel" - callbackURL = "https://login.cloudflareaccess.org/" - // For now these are the same but will change in the future once we know which URLs to use (TUN-8872) - fedBaseLoginURL = "https://dash.cloudflare.com/argotunnel" - fedCallbackStoreURL = "https://login.cloudflareaccess.org/" + baseLoginURL = "https://dash.cloudflare.com/argotunnel" + callbackURL = "https://login.cloudflareaccess.org/" + fedBaseLoginURL = "https://dash.fed.cloudflare.com/argotunnel" + fedCallbackStoreURL = "https://login.fed.cloudflareaccess.org/" fedRAMPParamName = "fedramp" loginURLParamName = "loginURL" callbackURLParamName = "callbackURL" @@ -99,6 +98,7 @@ func login(c *cli.Context) error { false, false, c.Bool(cfdflags.AutoCloseInterstitial), + isFEDRamp, log, ) if err != nil { diff --git a/config/model.go b/config/model.go index f90fd1ec..3f503a55 100644 --- a/config/model.go +++ b/config/model.go @@ -1,7 +1,7 @@ package config import ( - "crypto/md5" + "crypto/sha256" "fmt" "io" "strings" @@ -16,6 +16,7 @@ type Forwarder struct { TokenClientID string `json:"service_token_id" yaml:"serviceTokenID"` TokenSecret string `json:"secret_token_id" yaml:"serviceTokenSecret"` Destination string `json:"destination"` + IsFedramp bool `json:"is_fedramp" yaml:"isFedramp"` } // Tunnel represents a tunnel that should be started @@ -46,24 +47,24 @@ type Root struct { // Hash returns the computed values to see if the forwarder values change func (f *Forwarder) Hash() string { - h := md5.New() - io.WriteString(h, f.URL) - io.WriteString(h, f.Listener) - io.WriteString(h, f.TokenClientID) - io.WriteString(h, f.TokenSecret) - io.WriteString(h, f.Destination) + h := sha256.New() + _, _ = io.WriteString(h, f.URL) + _, _ = io.WriteString(h, f.Listener) + _, _ = io.WriteString(h, f.TokenClientID) + _, _ = io.WriteString(h, f.TokenSecret) + _, _ = io.WriteString(h, f.Destination) return fmt.Sprintf("%x", h.Sum(nil)) } // Hash returns the computed values to see if the forwarder values change func (r *DNSResolver) Hash() string { - h := md5.New() - io.WriteString(h, r.Address) - io.WriteString(h, strings.Join(r.Bootstraps, ",")) - io.WriteString(h, strings.Join(r.Upstreams, ",")) - io.WriteString(h, fmt.Sprintf("%d", r.Port)) - io.WriteString(h, fmt.Sprintf("%d", r.MaxUpstreamConnections)) - io.WriteString(h, fmt.Sprintf("%v", r.Enabled)) + h := sha256.New() + _, _ = io.WriteString(h, r.Address) + _, _ = io.WriteString(h, strings.Join(r.Bootstraps, ",")) + _, _ = io.WriteString(h, strings.Join(r.Upstreams, ",")) + _, _ = io.WriteString(h, fmt.Sprintf("%d", r.Port)) + _, _ = io.WriteString(h, fmt.Sprintf("%d", r.MaxUpstreamConnections)) + _, _ = io.WriteString(h, fmt.Sprintf("%v", r.Enabled)) return fmt.Sprintf("%x", h.Sum(nil)) } diff --git a/token/token.go b/token/token.go index 3966fe20..555a7f69 100644 --- a/token/token.go +++ b/token/token.go @@ -185,18 +185,18 @@ func Init(version string) { // FetchTokenWithRedirect will either load a stored token or generate a new one // it appends the full url as the redirect URL to the access cli request if opening the browser -func FetchTokenWithRedirect(appURL *url.URL, appInfo *AppInfo, autoClose bool, log *zerolog.Logger) (string, error) { - return getToken(appURL, appInfo, false, autoClose, log) +func FetchTokenWithRedirect(appURL *url.URL, appInfo *AppInfo, autoClose bool, isFedramp bool, log *zerolog.Logger) (string, error) { + return getToken(appURL, appInfo, false, autoClose, isFedramp, log) } // FetchToken will either load a stored token or generate a new one // it appends the host of the appURL as the redirect URL to the access cli request if opening the browser -func FetchToken(appURL *url.URL, appInfo *AppInfo, autoClose bool, log *zerolog.Logger) (string, error) { - return getToken(appURL, appInfo, true, autoClose, log) +func FetchToken(appURL *url.URL, appInfo *AppInfo, autoClose bool, isFedramp bool, log *zerolog.Logger) (string, error) { + return getToken(appURL, appInfo, true, autoClose, isFedramp, log) } // getToken will either load a stored token or generate a new one -func getToken(appURL *url.URL, appInfo *AppInfo, useHostOnly bool, autoClose bool, log *zerolog.Logger) (string, error) { +func getToken(appURL *url.URL, appInfo *AppInfo, useHostOnly bool, autoClose bool, isFedramp bool, log *zerolog.Logger) (string, error) { if token, err := GetAppTokenIfExists(appInfo); token != "" && err == nil { return token, nil } @@ -249,19 +249,19 @@ func getToken(appURL *url.URL, appInfo *AppInfo, useHostOnly bool, autoClose boo return appToken, nil } } - return getTokensFromEdge(appURL, appInfo.AppAUD, appTokenPath, orgTokenPath, useHostOnly, autoClose, log) + return getTokensFromEdge(appURL, appInfo.AppAUD, appTokenPath, orgTokenPath, useHostOnly, autoClose, isFedramp, log) } // getTokensFromEdge will attempt to use the transfer service to retrieve an app and org token, save them to disk, // and return the app token. -func getTokensFromEdge(appURL *url.URL, appAUD, appTokenPath, orgTokenPath string, useHostOnly bool, autoClose bool, log *zerolog.Logger) (string, error) { +func getTokensFromEdge(appURL *url.URL, appAUD, appTokenPath, orgTokenPath string, useHostOnly bool, autoClose bool, isFedramp bool, log *zerolog.Logger) (string, error) { fmt.Println("Get tokens from edge ", autoClose) // If no org token exists or if it couldn't be exchanged for an app token, then run the transfer service flow. // this weird parameter is the resource name (token) and the key/value // we want to send to the transfer service. the key is token and the value // is blank (basically just the id generated in the transfer service) - resourceData, err := RunTransfer(appURL, appAUD, keyName, keyName, "", true, useHostOnly, autoClose, log) + resourceData, err := RunTransfer(appURL, appAUD, keyName, keyName, "", true, useHostOnly, autoClose, isFedramp, log) if err != nil { return "", errors.Wrap(err, "failed to run transfer service") } diff --git a/token/transfer.go b/token/transfer.go index d5d0e1df..85cbb9a5 100644 --- a/token/transfer.go +++ b/token/transfer.go @@ -16,6 +16,7 @@ import ( const ( baseStoreURL = "https://login.cloudflareaccess.org/" + fedStoreURL = "https://login.fed.cloudflareaccess.org/" clientTimeout = time.Second * 60 ) @@ -25,7 +26,7 @@ const ( // The "dance" we refer to is building a HTTP request, opening that in a browser waiting for // the user to complete an action, while it long polls in the background waiting for an // action to be completed to download the resource. -func RunTransfer(transferURL *url.URL, appAUD, resourceName, key, value string, shouldEncrypt bool, useHostOnly bool, autoClose bool, log *zerolog.Logger) ([]byte, error) { +func RunTransfer(transferURL *url.URL, appAUD, resourceName, key, value string, shouldEncrypt bool, useHostOnly bool, autoClose bool, fedramp bool, log *zerolog.Logger) ([]byte, error) { encrypterClient, err := NewEncrypter("cloudflared_priv.pem", "cloudflared_pub.pem") if err != nil { return nil, err @@ -45,8 +46,14 @@ func RunTransfer(transferURL *url.URL, appAUD, resourceName, key, value string, var resourceData []byte + storeURL := baseStoreURL + + if fedramp { + storeURL = fedStoreURL + } + if shouldEncrypt { - buf, key, err := transferRequest(baseStoreURL+"transfer/"+encrypterClient.PublicKey(), log) + buf, key, err := transferRequest(storeURL+"transfer/"+encrypterClient.PublicKey(), log) if err != nil { return nil, err } @@ -62,7 +69,7 @@ func RunTransfer(transferURL *url.URL, appAUD, resourceName, key, value string, resourceData = decrypted } else { - buf, _, err := transferRequest(baseStoreURL+encrypterClient.PublicKey(), log) + buf, _, err := transferRequest(storeURL+encrypterClient.PublicKey(), log) if err != nil { return nil, err } @@ -131,7 +138,12 @@ func poll(client *http.Client, requestURL string, log *zerolog.Logger) ([]byte, // ignore everything other than server errors as the resource // may not exist until the user does the interaction if resp.StatusCode >= 500 { - return nil, "", fmt.Errorf("error on request %d", resp.StatusCode) + buf := new(bytes.Buffer) + if _, err := io.Copy(buf, resp.Body); err != nil { + return nil, "", err + } + + return nil, "", fmt.Errorf("error on request %d: %s", resp.StatusCode, buf.String()) } if resp.StatusCode != 200 { log.Info().Msg("Waiting for login...")