TUN-4118: Don't overwrite existing file with tunnel credentials. For ad-hoc tunnels, this means tunnel won't start if there's a file in the way.
This commit is contained in:
parent
9018ee5d5e
commit
50435546c5
|
@ -12,7 +12,8 @@
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
|
|
||||||
- none
|
- Tunnel create command, as well as, running ad-hoc tunnels using `cloudflared tunnel -name NAME`, will not overwrite
|
||||||
|
existing files when writing tunnel credentials.
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
@ -147,7 +148,7 @@ func (sc *subcommandContext) readTunnelCredentials(credFinder CredFinder) (conne
|
||||||
return credentials, nil
|
return credentials, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *subcommandContext) create(name string, credentialsOutputPath string) (*tunnelstore.Tunnel, error) {
|
func (sc *subcommandContext) create(name string, credentialsFilePath string) (*tunnelstore.Tunnel, error) {
|
||||||
client, err := sc.client()
|
client, err := sc.client()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "couldn't create client to talk to Argo Tunnel backend")
|
return nil, errors.Wrap(err, "couldn't create client to talk to Argo Tunnel backend")
|
||||||
|
@ -173,27 +174,40 @@ func (sc *subcommandContext) create(name string, credentialsOutputPath string) (
|
||||||
TunnelID: tunnel.ID,
|
TunnelID: tunnel.ID,
|
||||||
TunnelName: name,
|
TunnelName: name,
|
||||||
}
|
}
|
||||||
filePath, writeFileErr := writeTunnelCredentials(credential.certPath, credentialsOutputPath, &tunnelCredentials)
|
usedCertPath := false
|
||||||
|
if credentialsFilePath == "" {
|
||||||
|
originCertDir := filepath.Dir(credential.certPath)
|
||||||
|
credentialsFilePath, err = tunnelFilePath(tunnelCredentials.TunnelID, originCertDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
usedCertPath = true
|
||||||
|
}
|
||||||
|
writeFileErr := writeTunnelCredentials(credentialsFilePath, &tunnelCredentials)
|
||||||
if writeFileErr != nil {
|
if writeFileErr != nil {
|
||||||
var errorLines []string
|
var errorLines []string
|
||||||
errorLines = append(errorLines, fmt.Sprintf("Your tunnel '%v' was created with ID %v. However, cloudflared couldn't write to the tunnel credentials file at %v.json.", tunnel.Name, tunnel.ID, tunnel.ID))
|
errorLines = append(errorLines, fmt.Sprintf("Your tunnel '%v' was created with ID %v. However, cloudflared couldn't write tunnel credentials to %s.", tunnel.Name, tunnel.ID, credentialsFilePath))
|
||||||
errorLines = append(errorLines, fmt.Sprintf("The file-writing error is: %v", writeFileErr))
|
errorLines = append(errorLines, fmt.Sprintf("The file-writing error is: %v", writeFileErr))
|
||||||
if deleteErr := client.DeleteTunnel(tunnel.ID); deleteErr != nil {
|
if deleteErr := client.DeleteTunnel(tunnel.ID); deleteErr != nil {
|
||||||
errorLines = append(errorLines, fmt.Sprintf("Cloudflared tried to delete the tunnel for you, but encountered an error. You should use `cloudflared tunnel delete %v` to delete the tunnel yourself, because the tunnel can't be run without the tunnelfile.", tunnel.ID))
|
errorLines = append(errorLines, fmt.Sprintf("Cloudflared tried to delete the tunnel for you, but encountered an error. You should use `cloudflared tunnel delete %v` to delete the tunnel yourself, because the tunnel can't be run without the tunnelfile.", tunnel.ID))
|
||||||
errorLines = append(errorLines, fmt.Sprintf("The delete tunnel error is: %v", deleteErr))
|
errorLines = append(errorLines, fmt.Sprintf("The delete tunnel error is: %v", deleteErr))
|
||||||
} else {
|
} else {
|
||||||
errorLines = append(errorLines, fmt.Sprintf("The tunnel was deleted, because the tunnel can't be run without the tunnelfile"))
|
errorLines = append(errorLines, fmt.Sprintf("The tunnel was deleted, because the tunnel can't be run without the credentials file"))
|
||||||
}
|
}
|
||||||
errorMsg := strings.Join(errorLines, "\n")
|
errorMsg := strings.Join(errorLines, "\n")
|
||||||
return nil, errors.New(errorMsg)
|
return nil, errors.New(errorMsg)
|
||||||
}
|
}
|
||||||
sc.log.Info().Msgf("Tunnel credentials written to %v. cloudflared chose this file based on where your origin certificate was found. Keep this file secret. To revoke these credentials, delete the tunnel.", filePath)
|
|
||||||
|
|
||||||
if outputFormat := sc.c.String(outputFormatFlag.Name); outputFormat != "" {
|
if outputFormat := sc.c.String(outputFormatFlag.Name); outputFormat != "" {
|
||||||
return nil, renderOutput(outputFormat, &tunnel)
|
return nil, renderOutput(outputFormat, &tunnel)
|
||||||
}
|
}
|
||||||
|
|
||||||
sc.log.Info().Msgf("Created tunnel %s with id %s", tunnel.Name, tunnel.ID)
|
fmt.Printf("Tunnel credentials written to %v.", credentialsFilePath)
|
||||||
|
if usedCertPath {
|
||||||
|
fmt.Print(" cloudflared chose this file based on where your origin certificate was found.")
|
||||||
|
}
|
||||||
|
fmt.Println(" Keep this file secret. To revoke these credentials, delete the tunnel.")
|
||||||
|
fmt.Printf("\nCreated tunnel %s with id %s\n", tunnel.Name, tunnel.ID)
|
||||||
return tunnel, nil
|
return tunnel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -183,27 +183,20 @@ func tunnelFilePath(tunnelID uuid.UUID, directory string) (string, error) {
|
||||||
return homedir.Expand(filePath)
|
return homedir.Expand(filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If an `outputFile` is given, write the credentials there.
|
// writeTunnelCredentials saves `credentials` as a JSON into `filePath`, only if
|
||||||
// Otherwise, write it to the same directory as the originCert,
|
// the file does not exist already
|
||||||
// with the filename `<tunnel id>.json`.
|
func writeTunnelCredentials(filePath string, credentials *connection.Credentials) error {
|
||||||
func writeTunnelCredentials(
|
if _, err := os.Stat(filePath); !os.IsNotExist(err) {
|
||||||
originCertPath, outputFile string,
|
if err == nil {
|
||||||
credentials *connection.Credentials,
|
return fmt.Errorf("%s already exists", filePath)
|
||||||
) (filePath string, err error) {
|
}
|
||||||
filePath = outputFile
|
return err
|
||||||
if outputFile == "" {
|
|
||||||
originCertDir := filepath.Dir(originCertPath)
|
|
||||||
filePath, err = tunnelFilePath(credentials.TunnelID, originCertDir)
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
// Write the name and ID to the file too
|
|
||||||
body, err := json.Marshal(credentials)
|
body, err := json.Marshal(credentials)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrap(err, "Unable to marshal tunnel credentials to JSON")
|
return errors.Wrap(err, "Unable to marshal tunnel credentials to JSON")
|
||||||
}
|
}
|
||||||
return filePath, ioutil.WriteFile(filePath, body, 400)
|
return ioutil.WriteFile(filePath, body, 400)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildListCommand() *cli.Command {
|
func buildListCommand() *cli.Command {
|
||||||
|
|
Loading…
Reference in New Issue