AUTH-3221: Saves org token to disk and uses it to refresh the app token
This commit is contained in:
		
							parent
							
								
									cad58b9b57
								
							
						
					
					
						commit
						fcc393e2f0
					
				|  | @ -222,7 +222,7 @@ func login(c *cli.Context) error { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cfdToken, err := token.GetTokenIfExists(appURL) | 	cfdToken, err := token.GetAppTokenIfExists(appURL) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		fmt.Fprintln(os.Stderr, "Unable to find token for provided application.") | 		fmt.Fprintln(os.Stderr, "Unable to find token for provided application.") | ||||||
| 		return err | 		return err | ||||||
|  | @ -267,7 +267,7 @@ func curl(c *cli.Context) error { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	tok, err := token.GetTokenIfExists(appURL) | 	tok, err := token.GetAppTokenIfExists(appURL) | ||||||
| 	if err != nil || tok == "" { | 	if err != nil || tok == "" { | ||||||
| 		if allowRequest { | 		if allowRequest { | ||||||
| 			logger.Info("You don't have an Access token set. Please run access token <access application> to fetch one.") | 			logger.Info("You don't have an Access token set. Please run access token <access application> to fetch one.") | ||||||
|  | @ -295,7 +295,7 @@ func generateToken(c *cli.Context) error { | ||||||
| 		fmt.Fprintln(os.Stderr, "Please provide a url.") | 		fmt.Fprintln(os.Stderr, "Please provide a url.") | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	tok, err := token.GetTokenIfExists(appURL) | 	tok, err := token.GetAppTokenIfExists(appURL) | ||||||
| 	if err != nil || tok == "" { | 	if err != nil || tok == "" { | ||||||
| 		fmt.Fprintln(os.Stderr, "Unable to find token for provided application. Please run token command to generate token.") | 		fmt.Fprintln(os.Stderr, "Unable to find token for provided application. Please run token command to generate token.") | ||||||
| 		return err | 		return err | ||||||
|  |  | ||||||
|  | @ -11,8 +11,27 @@ import ( | ||||||
| 	"github.com/mitchellh/go-homedir" | 	"github.com/mitchellh/go-homedir" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // GenerateFilePathFromURL will return a filepath for given access application url
 | // GenerateAppTokenFilePathFromURL will return a filepath for given Access org token
 | ||||||
| func GenerateFilePathFromURL(url *url.URL, suffix string) (string, error) { | func GenerateAppTokenFilePathFromURL(url *url.URL, suffix string) (string, error) { | ||||||
|  | 	configPath, err := getConfigPath() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	name := strings.Replace(fmt.Sprintf("%s%s-%s", url.Hostname(), url.EscapedPath(), suffix), "/", "-", -1) | ||||||
|  | 	return filepath.Join(configPath, name), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GenerateOrgTokenFilePathFromURL will return a filepath for given Access application token
 | ||||||
|  | func GenerateOrgTokenFilePathFromURL(authDomain string) (string, error) { | ||||||
|  | 	configPath, err := getConfigPath() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	name := strings.Replace(fmt.Sprintf("%s-org-token", authDomain), "/", "-", -1) | ||||||
|  | 	return filepath.Join(configPath, name), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func getConfigPath() (string, error) { | ||||||
| 	configPath, err := homedir.Expand(config.DefaultConfigSearchDirectories()[0]) | 	configPath, err := homedir.Expand(config.DefaultConfigSearchDirectories()[0]) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
|  | @ -22,9 +41,5 @@ func GenerateFilePathFromURL(url *url.URL, suffix string) (string, error) { | ||||||
| 		// create config directory if doesn't already exist
 | 		// create config directory if doesn't already exist
 | ||||||
| 		err = os.Mkdir(configPath, 0700) | 		err = os.Mkdir(configPath, 0700) | ||||||
| 	} | 	} | ||||||
| 	if err != nil { | 	return configPath, err | ||||||
| 		return "", err |  | ||||||
| 	} |  | ||||||
| 	name := strings.Replace(fmt.Sprintf("%s%s-%s", url.Hostname(), url.EscapedPath(), suffix), "/", "-", -1) |  | ||||||
| 	return filepath.Join(configPath, name), nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -5,9 +5,11 @@ import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
|  | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/signal" | 	"os/signal" | ||||||
|  | 	"strings" | ||||||
| 	"syscall" | 	"syscall" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | @ -17,10 +19,12 @@ import ( | ||||||
| 	"github.com/cloudflare/cloudflared/logger" | 	"github.com/cloudflare/cloudflared/logger" | ||||||
| 	"github.com/cloudflare/cloudflared/origin" | 	"github.com/cloudflare/cloudflared/origin" | ||||||
| 	"github.com/coreos/go-oidc/jose" | 	"github.com/coreos/go-oidc/jose" | ||||||
|  | 	"github.com/pkg/errors" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	keyName     = "token" | 	keyName     = "token" | ||||||
|  | 	tokenHeader = "CF_Authorization" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type lock struct { | type lock struct { | ||||||
|  | @ -34,7 +38,7 @@ type signalHandler struct { | ||||||
| 	signals    []os.Signal | 	signals    []os.Signal | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type jwtPayload struct { | type appJWTPayload struct { | ||||||
| 	Aud   []string `json:"aud"` | 	Aud   []string `json:"aud"` | ||||||
| 	Email string   `json:"email"` | 	Email string   `json:"email"` | ||||||
| 	Exp   int      `json:"exp"` | 	Exp   int      `json:"exp"` | ||||||
|  | @ -45,7 +49,17 @@ type jwtPayload struct { | ||||||
| 	Subt  string   `json:"sub"` | 	Subt  string   `json:"sub"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p jwtPayload) isExpired() bool { | type orgJWTPayload struct { | ||||||
|  | 	appJWTPayload | ||||||
|  | 	Aud string `json:"aud"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type transferServiceResponse struct { | ||||||
|  | 	AppToken string `json:"app_token"` | ||||||
|  | 	OrgToken string `json:"org_token"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p appJWTPayload) isExpired() bool { | ||||||
| 	return int(time.Now().Unix()) > p.Exp | 	return int(time.Now().Unix()) > p.Exp | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -141,55 +155,173 @@ func FetchToken(appURL *url.URL, logger logger.Service) (string, error) { | ||||||
| 
 | 
 | ||||||
| // 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, useHostOnly bool, logger logger.Service) (string, error) { | func getToken(appURL *url.URL, useHostOnly bool, logger logger.Service) (string, error) { | ||||||
| 	if token, err := GetTokenIfExists(appURL); token != "" && err == nil { | 	if token, err := GetAppTokenIfExists(appURL); token != "" && err == nil { | ||||||
| 		return token, nil | 		return token, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	path, err := path.GenerateFilePathFromURL(appURL, keyName) | 	appTokenPath, err := path.GenerateAppTokenFilePathFromURL(appURL, keyName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", errors.Wrap(err, "failed to generate app token file path") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fileLock := newLock(path) | 	fileLockAppToken := newLock(appTokenPath) | ||||||
| 
 | 	if err = fileLockAppToken.Acquire(); err != nil { | ||||||
| 	err = fileLock.Acquire() | 		return "", errors.Wrap(err, "failed to acquire app token lock") | ||||||
| 	if err != nil { |  | ||||||
| 		return "", err |  | ||||||
| 	} | 	} | ||||||
| 	defer fileLock.Release() | 	defer fileLockAppToken.Release() | ||||||
| 
 | 
 | ||||||
| 	// check to see if another process has gotten a token while we waited for the lock
 | 	// check to see if another process has gotten a token while we waited for the lock
 | ||||||
| 	if token, err := GetTokenIfExists(appURL); token != "" && err == nil { | 	if token, err := GetAppTokenIfExists(appURL); token != "" && err == nil { | ||||||
| 		return token, nil | 		return token, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// If an app token couldnt be found on disk, check for an org token and attempt to exchange it for an app token.
 | ||||||
|  | 	var orgTokenPath string | ||||||
|  | 	// Get auth domain to format into org token file path
 | ||||||
|  | 	if authDomain, err := getAuthDomain(appURL); err != nil { | ||||||
|  | 		logger.Errorf("failed to get auth domain: %s", err) | ||||||
|  | 	} else { | ||||||
|  | 		orgToken, err := GetOrgTokenIfExists(authDomain) | ||||||
|  | 		if err != nil { | ||||||
|  | 			orgTokenPath, err = path.GenerateOrgTokenFilePathFromURL(authDomain) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return "", errors.Wrap(err, "failed to generate org token file path") | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			fileLockOrgToken := newLock(orgTokenPath) | ||||||
|  | 			if err = fileLockOrgToken.Acquire(); err != nil { | ||||||
|  | 				return "", errors.Wrap(err, "failed to acquire org token lock") | ||||||
|  | 			} | ||||||
|  | 			defer fileLockOrgToken.Release() | ||||||
|  | 			// check if an org token has been created since the lock was acquired
 | ||||||
|  | 			orgToken, err = GetOrgTokenIfExists(authDomain) | ||||||
|  | 		} | ||||||
|  | 		if err == nil { | ||||||
|  | 			if appToken, err := exchangeOrgToken(appURL, orgToken); err != nil { | ||||||
|  | 				logger.Debugf("failed to exchange org token for app token: %s", err) | ||||||
|  | 			} else { | ||||||
|  | 				if err := ioutil.WriteFile(appTokenPath, []byte(appToken), 0600); err != nil { | ||||||
|  | 					return "", errors.Wrap(err, "failed to write app token to disk") | ||||||
|  | 				} | ||||||
|  | 				return appToken, nil | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return getTokensFromEdge(appURL, appTokenPath, orgTokenPath, useHostOnly, logger) | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 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, appTokenPath, orgTokenPath string, useHostOnly bool, logger logger.Service) (string, error) { | ||||||
|  | 	// If no org token exists or if it couldnt 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)
 | ||||||
| 	token, err := transfer.Run(appURL, keyName, keyName, "", path, true, useHostOnly, logger) | 	resourceData, err := transfer.Run(appURL, keyName, keyName, "", true, useHostOnly, logger) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", errors.Wrap(err, "failed to run transfer service") | ||||||
|  | 	} | ||||||
|  | 	var resp transferServiceResponse | ||||||
|  | 	if err = json.Unmarshal(resourceData, &resp); err != nil { | ||||||
|  | 		return "", errors.Wrap(err, "failed to marshal transfer service response") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return string(token), nil | 	// If we were able to get the auth domain and generate an org token path, lets write it to disk.
 | ||||||
|  | 	if orgTokenPath != "" { | ||||||
|  | 		if err := ioutil.WriteFile(orgTokenPath, []byte(resp.OrgToken), 0600); err != nil { | ||||||
|  | 			return "", errors.Wrap(err, "failed to write org token to disk") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := ioutil.WriteFile(appTokenPath, []byte(resp.AppToken), 0600); err != nil { | ||||||
|  | 		return "", errors.Wrap(err, "failed to write app token to disk") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return resp.AppToken, nil | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetTokenIfExists will return the token from local storage if it exists and not expired
 | // getAuthDomain makes a request to the appURL and stops at the first redirect. The 302 location header will contain the
 | ||||||
| func GetTokenIfExists(url *url.URL) (string, error) { | // auth domain
 | ||||||
| 	path, err := path.GenerateFilePathFromURL(url, keyName) | func getAuthDomain(appURL *url.URL) (string, error) { | ||||||
| 	if err != nil { | 	client := &http.Client{ | ||||||
| 		return "", err | 		// do not follow redirects
 | ||||||
| 	} | 		CheckRedirect: func(req *http.Request, via []*http.Request) error { | ||||||
| 	content, err := ioutil.ReadFile(path) | 			return http.ErrUseLastResponse | ||||||
| 	if err != nil { | 		}, | ||||||
| 		return "", err | 		Timeout: time.Second * 7, | ||||||
| 	} |  | ||||||
| 	token, err := jose.ParseJWT(string(content)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "", err |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var payload jwtPayload | 	authDomainReq, err := http.NewRequest("HEAD", appURL.String(), nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", errors.Wrap(err, "failed to create auth domain request") | ||||||
|  | 	} | ||||||
|  | 	resp, err := client.Do(authDomainReq) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", errors.Wrap(err, "failed to get auth domain") | ||||||
|  | 	} | ||||||
|  | 	resp.Body.Close() | ||||||
|  | 	location, err := resp.Location() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", fmt.Errorf("failed to get auth domain. Received status code %d from %s", resp.StatusCode, appURL.String()) | ||||||
|  | 	} | ||||||
|  | 	return location.Hostname(), nil | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // exchangeOrgToken attaches an org token to a request to the appURL and returns an app token. This uses the Access SSO
 | ||||||
|  | // flow to automatically generate and return an app token without the login page.
 | ||||||
|  | func exchangeOrgToken(appURL *url.URL, orgToken string) (string, error) { | ||||||
|  | 	client := &http.Client{ | ||||||
|  | 		CheckRedirect: func(req *http.Request, via []*http.Request) error { | ||||||
|  | 			// attach org token to login request
 | ||||||
|  | 			if strings.Contains(req.URL.Path, "cdn-cgi/access/login") { | ||||||
|  | 				req.AddCookie(&http.Cookie{Name: tokenHeader, Value: orgToken}) | ||||||
|  | 			} | ||||||
|  | 			// stop after hitting authorized endpoint since it will contain the app token
 | ||||||
|  | 			if strings.Contains(via[len(via)-1].URL.Path, "cdn-cgi/access/authorized") { | ||||||
|  | 				return http.ErrUseLastResponse | ||||||
|  | 			} | ||||||
|  | 			return nil | ||||||
|  | 		}, | ||||||
|  | 		Timeout: time.Second * 7, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	appTokenRequest, err := http.NewRequest("HEAD", appURL.String(), nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", errors.Wrap(err, "failed to create app token request") | ||||||
|  | 	} | ||||||
|  | 	resp, err := client.Do(appTokenRequest) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", errors.Wrap(err, "failed to get app token") | ||||||
|  | 	} | ||||||
|  | 	resp.Body.Close() | ||||||
|  | 	var appToken string | ||||||
|  | 	for _, c := range resp.Cookies() { | ||||||
|  | 		if c.Name == tokenHeader { | ||||||
|  | 			appToken = c.Value | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(appToken) > 0 { | ||||||
|  | 		return appToken, nil | ||||||
|  | 	} | ||||||
|  | 	return "", fmt.Errorf("response from %s did not contain app token", resp.Request.URL.String()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func GetOrgTokenIfExists(authDomain string) (string, error) { | ||||||
|  | 	path, err := path.GenerateOrgTokenFilePathFromURL(authDomain) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	token, err := getTokenIfExists(path) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	var payload orgJWTPayload | ||||||
| 	err = json.Unmarshal(token.Payload, &payload) | 	err = json.Unmarshal(token.Payload, &payload) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
|  | @ -199,13 +331,49 @@ func GetTokenIfExists(url *url.URL) (string, error) { | ||||||
| 		err := os.Remove(path) | 		err := os.Remove(path) | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	return token.Encode(), nil | 	return token.Encode(), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func GetAppTokenIfExists(url *url.URL) (string, error) { | ||||||
|  | 	path, err := path.GenerateAppTokenFilePathFromURL(url, keyName) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	token, err := getTokenIfExists(path) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	var payload appJWTPayload | ||||||
|  | 	err = json.Unmarshal(token.Payload, &payload) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if payload.isExpired() { | ||||||
|  | 		err := os.Remove(path) | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	return token.Encode(), nil | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GetTokenIfExists will return the token from local storage if it exists and not expired
 | ||||||
|  | func getTokenIfExists(path string) (*jose.JWT, error) { | ||||||
|  | 	content, err := ioutil.ReadFile(path) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	token, err := jose.ParseJWT(string(content)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return &token, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // RemoveTokenIfExists removes the a token from local storage if it exists
 | // RemoveTokenIfExists removes the a token from local storage if it exists
 | ||||||
| func RemoveTokenIfExists(url *url.URL) error { | func RemoveTokenIfExists(url *url.URL) error { | ||||||
| 	path, err := path.GenerateFilePathFromURL(url, keyName) | 	path, err := path.GenerateAppTokenFilePathFromURL(url, keyName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -3,10 +3,8 @@ package transfer | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"encoding/base64" | 	"encoding/base64" | ||||||
| 	"errors" |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"io/ioutil" |  | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"os" | 	"os" | ||||||
|  | @ -15,6 +13,7 @@ import ( | ||||||
| 	"github.com/cloudflare/cloudflared/cmd/cloudflared/encrypter" | 	"github.com/cloudflare/cloudflared/cmd/cloudflared/encrypter" | ||||||
| 	"github.com/cloudflare/cloudflared/cmd/cloudflared/shell" | 	"github.com/cloudflare/cloudflared/cmd/cloudflared/shell" | ||||||
| 	"github.com/cloudflare/cloudflared/logger" | 	"github.com/cloudflare/cloudflared/logger" | ||||||
|  | 	"github.com/pkg/errors" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
|  | @ -28,7 +27,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 Run(transferURL *url.URL, resourceName, key, value, path string, shouldEncrypt bool, useHostOnly bool, logger logger.Service) ([]byte, error) { | func Run(transferURL *url.URL, resourceName, key, value string, shouldEncrypt bool, useHostOnly bool, logger logger.Service) ([]byte, error) { | ||||||
| 	encrypterClient, err := encrypter.New("cloudflared_priv.pem", "cloudflared_pub.pem") | 	encrypterClient, err := encrypter.New("cloudflared_priv.pem", "cloudflared_pub.pem") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
|  | @ -72,11 +71,8 @@ func Run(transferURL *url.URL, resourceName, key, value, path string, shouldEncr | ||||||
| 		resourceData = buf | 		resourceData = buf | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := ioutil.WriteFile(path, resourceData, 0600); err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return resourceData, nil | 	return resourceData, nil | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // BuildRequestURL creates a request suitable for a resource transfer.
 | // BuildRequestURL creates a request suitable for a resource transfer.
 | ||||||
|  | @ -93,6 +89,7 @@ func buildRequestURL(baseURL *url.URL, key, value string, cli, useHostOnly bool) | ||||||
| 		return baseURL.String(), nil | 		return baseURL.String(), nil | ||||||
| 	} | 	} | ||||||
| 	q.Set("redirect_url", baseURL.String()) // we add the token as a query param on both the redirect_url and the main url
 | 	q.Set("redirect_url", baseURL.String()) // we add the token as a query param on both the redirect_url and the main url
 | ||||||
|  | 	q.Set("send_org_token", "true")         // indicates that the cli endpoint should return both the org and app token
 | ||||||
| 	baseURL.RawQuery = q.Encode()           // and this actual baseURL.
 | 	baseURL.RawQuery = q.Encode()           // and this actual baseURL.
 | ||||||
| 	baseURL.Path = "cdn-cgi/access/cli" | 	baseURL.Path = "cdn-cgi/access/cli" | ||||||
| 	return baseURL.String(), nil | 	return baseURL.String(), nil | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ package tunnel | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
|  | @ -58,12 +59,16 @@ func login(c *cli.Context) error { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	_, err = transfer.Run(loginURL, "cert", "callback", callbackStoreURL, path, false, false, logger) | 	resourceData, err := transfer.Run(loginURL, "cert", "callback", callbackStoreURL, false, false, logger) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		fmt.Fprintf(os.Stderr, "Failed to write the certificate due to the following error:\n%v\n\nYour browser will download the certificate instead. You will have to manually\ncopy it to the following path:\n\n%s\n", err, path) | 		fmt.Fprintf(os.Stderr, "Failed to write the certificate due to the following error:\n%v\n\nYour browser will download the certificate instead. You will have to manually\ncopy it to the following path:\n\n%s\n", err, path) | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if err := ioutil.WriteFile(path, resourceData, 0600); err != nil { | ||||||
|  | 		return errors.Wrap(err, fmt.Sprintf("error writing cert to %s", path)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	fmt.Fprintf(os.Stdout, "You have successfully logged in.\nIf you wish to copy your credentials to a server, they have been saved to:\n%s\n", path) | 	fmt.Fprintf(os.Stdout, "You have successfully logged in.\nIf you wish to copy your credentials to a server, they have been saved to:\n%s\n", path) | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -52,7 +52,7 @@ var mockRequest func(url, contentType string, body io.Reader) (*http.Response, e | ||||||
| 
 | 
 | ||||||
| // GenerateShortLivedCertificate generates and stores a keypair for short lived certs
 | // GenerateShortLivedCertificate generates and stores a keypair for short lived certs
 | ||||||
| func GenerateShortLivedCertificate(appURL *url.URL, token string) error { | func GenerateShortLivedCertificate(appURL *url.URL, token string) error { | ||||||
| 	fullName, err := cfpath.GenerateFilePathFromURL(appURL, keyName) | 	fullName, err := cfpath.GenerateAppTokenFilePathFromURL(appURL, keyName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -35,7 +35,7 @@ func TestCertGenSuccess(t *testing.T) { | ||||||
| 	url, _ := url.Parse("https://cf-test-access.com/testpath") | 	url, _ := url.Parse("https://cf-test-access.com/testpath") | ||||||
| 	token := tokenGenerator() | 	token := tokenGenerator() | ||||||
| 
 | 
 | ||||||
| 	fullName, err := cfpath.GenerateFilePathFromURL(url, keyName) | 	fullName, err := cfpath.GenerateAppTokenFilePathFromURL(url, keyName) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	pubKeyName := fullName + ".pub" | 	pubKeyName := fullName + ".pub" | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue