AUTH-7480 update fed callback url for login helper

* AUTH-7480 update fed callback url for login helper
This commit is contained in:
Kyle Hiller 2025-08-19 18:54:31 +00:00
parent 50104548cf
commit 8825ceecb5
7 changed files with 56 additions and 35 deletions

View File

@ -32,6 +32,7 @@ type StartOptions struct {
Host string Host string
TLSClientConfig *tls.Config TLSClientConfig *tls.Config
AutoCloseInterstitial bool AutoCloseInterstitial bool
IsFedramp bool
} }
// Connection wraps up all the needed functions to forward over the tunnel // 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 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 { if err != nil {
return nil, err return nil, err
} }

View File

@ -47,6 +47,7 @@ func StartForwarder(forwarder config.Forwarder, shutdown <-chan struct{}, log *z
options := &carrier.StartOptions{ options := &carrier.StartOptions{
OriginURL: forwarder.URL, OriginURL: forwarder.URL,
Headers: headers, //TODO: TUN-2688 support custom headers from config file 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 // 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(), OriginURL: url.String(),
Headers: headers, Headers: headers,
Host: url.Host, Host: url.Host,
IsFedramp: c.Bool(fedrampFlag),
} }
if connectTo := c.String(sshConnectTo); connectTo != "" { if connectTo := c.String(sshConnectTo); connectTo != "" {

View File

@ -51,6 +51,7 @@ Host {{.Hostname}}
ProxyCommand {{.Cloudflared}} access ssh --hostname %h ProxyCommand {{.Cloudflared}} access ssh --hostname %h
{{end}} {{end}}
` `
fedrampFlag = "fedramp"
) )
const sentryDSN = "https://56a9c9fa5c364ab28f34b14f35ea0f1b@sentry.io/189878" const sentryDSN = "https://56a9c9fa5c364ab28f34b14f35ea0f1b@sentry.io/189878"
@ -79,6 +80,10 @@ func Commands() []*cli.Command {
Aliases: []string{"forward"}, Aliases: []string{"forward"},
Category: "Access", Category: "Access",
Usage: "access <subcommand>", Usage: "access <subcommand>",
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 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 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 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 <access application> to fetch one.") log.Info().Msg("You don't have an Access token set. Please run access token <access application> to fetch one.")
return run("curl", cmdArgs...) 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 { if err != nil {
log.Err(err).Msg("Failed to refresh token") log.Err(err).Msg("Failed to refresh token")
return err return err
@ -446,7 +451,7 @@ func sshGen(c *cli.Context) error {
if err != nil { if err != nil {
return err 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 { if err != nil {
return err return err
} }
@ -546,7 +551,7 @@ func verifyTokenAtEdge(appUrl *url.URL, appInfo *token.AppInfo, c *cli.Context,
if c.IsSet(sshTokenSecretFlag) { if c.IsSet(sshTokenSecretFlag) {
headers.Add(cfAccessClientSecretHeader, c.String(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 { if valid, err := isTokenValid(options, log); err != nil {
return err return err

View File

@ -22,9 +22,8 @@ import (
const ( const (
baseLoginURL = "https://dash.cloudflare.com/argotunnel" baseLoginURL = "https://dash.cloudflare.com/argotunnel"
callbackURL = "https://login.cloudflareaccess.org/" 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.fed.cloudflare.com/argotunnel"
fedBaseLoginURL = "https://dash.cloudflare.com/argotunnel" fedCallbackStoreURL = "https://login.fed.cloudflareaccess.org/"
fedCallbackStoreURL = "https://login.cloudflareaccess.org/"
fedRAMPParamName = "fedramp" fedRAMPParamName = "fedramp"
loginURLParamName = "loginURL" loginURLParamName = "loginURL"
callbackURLParamName = "callbackURL" callbackURLParamName = "callbackURL"
@ -99,6 +98,7 @@ func login(c *cli.Context) error {
false, false,
false, false,
c.Bool(cfdflags.AutoCloseInterstitial), c.Bool(cfdflags.AutoCloseInterstitial),
isFEDRamp,
log, log,
) )
if err != nil { if err != nil {

View File

@ -1,7 +1,7 @@
package config package config
import ( import (
"crypto/md5" "crypto/sha256"
"fmt" "fmt"
"io" "io"
"strings" "strings"
@ -16,6 +16,7 @@ type Forwarder struct {
TokenClientID string `json:"service_token_id" yaml:"serviceTokenID"` TokenClientID string `json:"service_token_id" yaml:"serviceTokenID"`
TokenSecret string `json:"secret_token_id" yaml:"serviceTokenSecret"` TokenSecret string `json:"secret_token_id" yaml:"serviceTokenSecret"`
Destination string `json:"destination"` Destination string `json:"destination"`
IsFedramp bool `json:"is_fedramp" yaml:"isFedramp"`
} }
// Tunnel represents a tunnel that should be started // 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 // Hash returns the computed values to see if the forwarder values change
func (f *Forwarder) Hash() string { func (f *Forwarder) Hash() string {
h := md5.New() h := sha256.New()
io.WriteString(h, f.URL) _, _ = io.WriteString(h, f.URL)
io.WriteString(h, f.Listener) _, _ = io.WriteString(h, f.Listener)
io.WriteString(h, f.TokenClientID) _, _ = io.WriteString(h, f.TokenClientID)
io.WriteString(h, f.TokenSecret) _, _ = io.WriteString(h, f.TokenSecret)
io.WriteString(h, f.Destination) _, _ = io.WriteString(h, f.Destination)
return fmt.Sprintf("%x", h.Sum(nil)) return fmt.Sprintf("%x", h.Sum(nil))
} }
// Hash returns the computed values to see if the forwarder values change // Hash returns the computed values to see if the forwarder values change
func (r *DNSResolver) Hash() string { func (r *DNSResolver) Hash() string {
h := md5.New() h := sha256.New()
io.WriteString(h, r.Address) _, _ = io.WriteString(h, r.Address)
io.WriteString(h, strings.Join(r.Bootstraps, ",")) _, _ = io.WriteString(h, strings.Join(r.Bootstraps, ","))
io.WriteString(h, strings.Join(r.Upstreams, ",")) _, _ = io.WriteString(h, strings.Join(r.Upstreams, ","))
io.WriteString(h, fmt.Sprintf("%d", r.Port)) _, _ = io.WriteString(h, fmt.Sprintf("%d", r.Port))
io.WriteString(h, fmt.Sprintf("%d", r.MaxUpstreamConnections)) _, _ = io.WriteString(h, fmt.Sprintf("%d", r.MaxUpstreamConnections))
io.WriteString(h, fmt.Sprintf("%v", r.Enabled)) _, _ = io.WriteString(h, fmt.Sprintf("%v", r.Enabled))
return fmt.Sprintf("%x", h.Sum(nil)) return fmt.Sprintf("%x", h.Sum(nil))
} }

View File

@ -185,18 +185,18 @@ func Init(version string) {
// FetchTokenWithRedirect will either load a stored token or generate a new one // 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 // 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) { func FetchTokenWithRedirect(appURL *url.URL, appInfo *AppInfo, autoClose bool, isFedramp bool, log *zerolog.Logger) (string, error) {
return getToken(appURL, appInfo, false, autoClose, log) return getToken(appURL, appInfo, false, autoClose, isFedramp, log)
} }
// FetchToken will either load a stored token or generate a new one // 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 // 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) { func FetchToken(appURL *url.URL, appInfo *AppInfo, autoClose bool, isFedramp bool, log *zerolog.Logger) (string, error) {
return getToken(appURL, appInfo, true, autoClose, log) return getToken(appURL, appInfo, true, autoClose, isFedramp, log)
} }
// getToken will either load a stored token or generate a new one // 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 { if token, err := GetAppTokenIfExists(appInfo); token != "" && err == nil {
return token, nil return token, nil
} }
@ -249,19 +249,19 @@ func getToken(appURL *url.URL, appInfo *AppInfo, useHostOnly bool, autoClose boo
return appToken, nil 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, // getTokensFromEdge will attempt to use the transfer service to retrieve an app and org token, save them to disk,
// and return the app token. // 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) 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. // 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 // 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 // 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) // 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 { if err != nil {
return "", errors.Wrap(err, "failed to run transfer service") return "", errors.Wrap(err, "failed to run transfer service")
} }

View File

@ -16,6 +16,7 @@ import (
const ( const (
baseStoreURL = "https://login.cloudflareaccess.org/" baseStoreURL = "https://login.cloudflareaccess.org/"
fedStoreURL = "https://login.fed.cloudflareaccess.org/"
clientTimeout = time.Second * 60 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 "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 // the user to complete an action, while it long polls in the background waiting for an
// action to be completed to download the resource. // 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") encrypterClient, err := NewEncrypter("cloudflared_priv.pem", "cloudflared_pub.pem")
if err != nil { if err != nil {
return nil, err return nil, err
@ -45,8 +46,14 @@ func RunTransfer(transferURL *url.URL, appAUD, resourceName, key, value string,
var resourceData []byte var resourceData []byte
storeURL := baseStoreURL
if fedramp {
storeURL = fedStoreURL
}
if shouldEncrypt { if shouldEncrypt {
buf, key, err := transferRequest(baseStoreURL+"transfer/"+encrypterClient.PublicKey(), log) buf, key, err := transferRequest(storeURL+"transfer/"+encrypterClient.PublicKey(), log)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -62,7 +69,7 @@ func RunTransfer(transferURL *url.URL, appAUD, resourceName, key, value string,
resourceData = decrypted resourceData = decrypted
} else { } else {
buf, _, err := transferRequest(baseStoreURL+encrypterClient.PublicKey(), log) buf, _, err := transferRequest(storeURL+encrypterClient.PublicKey(), log)
if err != nil { if err != nil {
return nil, err 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 // ignore everything other than server errors as the resource
// may not exist until the user does the interaction // may not exist until the user does the interaction
if resp.StatusCode >= 500 { 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 { if resp.StatusCode != 200 {
log.Info().Msg("Waiting for login...") log.Info().Msg("Waiting for login...")