From 28cc1c65af48fc55a7d76807809654d9707f7b19 Mon Sep 17 00:00:00 2001 From: Michael Borkenstein Date: Thu, 17 Oct 2019 16:23:06 -0500 Subject: [PATCH] AUTH-2167: Adds CLI option for host key directory --- cmd/cloudflared/tunnel/cmd.go | 14 ++++++- sshserver/host_keys.go | 67 ++++++++++++++++++---------------- sshserver/sshserver_unix.go | 4 +- sshserver/sshserver_windows.go | 2 +- 4 files changed, 50 insertions(+), 37 deletions(-) diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 6617456b..3f109503 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -74,6 +74,9 @@ const ( // s3URLFlag is the S3 URL of SSH log uploader (e.g. don't use AWS s3 and use google storage bucket instead) s3URLFlag = "s3-url-host" + // hostKeyPath is the path of the dir to save SSH host keys too + hostKeyPath = "host-key-path" + noIntentMsg = "The --intent argument is required. Cloudflared looks up an Intent to determine what configuration to use (i.e. which tunnels to start). If you don't have any Intents yet, you can use a placeholder Intent Label for now. Then, when you make an Intent with that label, cloudflared will get notified and open the tunnels you specified in that Intent." ) @@ -396,7 +399,7 @@ func StartServer(c *cli.Context, version string, shutdownC, graceShutdownC chan } localServerAddress := "127.0.0.1:" + c.String(sshPortFlag) - server, err := sshserver.New(logManager, logger, version, localServerAddress, c.String("hostname"), shutdownC, c.Duration(sshIdleTimeoutFlag), c.Duration(sshMaxTimeoutFlag)) + server, err := sshserver.New(logManager, logger, version, localServerAddress, c.String("hostname"), c.Path(hostKeyPath), shutdownC, c.Duration(sshIdleTimeoutFlag), c.Duration(sshMaxTimeoutFlag)) if err != nil { msg := "Cannot create new SSH Server" logger.WithError(err).Error(msg) @@ -1019,5 +1022,12 @@ func tunnelFlags(shouldHide bool) []cli.Flag { EnvVars: []string{"S3_URL"}, Hidden: true, }), + altsrc.NewPathFlag(&cli.PathFlag{ + Name: hostKeyPath, + Usage: "Absolute path of directory to save SSH host keys in", + EnvVars: []string{"HOST_KEY_PATH"}, + Hidden: true, + + }), } -} +} \ No newline at end of file diff --git a/sshserver/host_keys.go b/sshserver/host_keys.go index db0354c4..fcdf3582 100644 --- a/sshserver/host_keys.go +++ b/sshserver/host_keys.go @@ -19,47 +19,44 @@ import ( ) const ( - systemConfigPath = "/usr/local/etc/cloudflared/" - rsaFilename = "ssh_host_rsa_key" - ecdsaFilename = "ssh_host_ecdsa_key" + rsaFilename = "ssh_host_rsa_key" + ecdsaFilename = "ssh_host_ecdsa_key" ) -func (s *SSHProxy) configureHostKeys() error { - if _, err := os.Stat(systemConfigPath); os.IsNotExist(err) { - if err := os.MkdirAll(systemConfigPath, 0755); err != nil { - return errors.Wrap(err, fmt.Sprintf("Error creating %s directory", systemConfigPath)) +var defaultHostKeyDir = filepath.Join(".cloudflared", "host_keys") + +func (s *SSHProxy) configureHostKeys(hostKeyDir string) error { + if hostKeyDir == "" { + homeDir, err := os.UserHomeDir() + if err != nil { + return err + } + hostKeyDir = filepath.Join(homeDir, defaultHostKeyDir) + } + + if _, err := os.Stat(hostKeyDir); os.IsNotExist(err) { + if err := os.MkdirAll(hostKeyDir, 0755); err != nil { + return errors.Wrap(err, fmt.Sprintf("Error creating %s directory", hostKeyDir)) } } - if err := s.configureHostKey(s.ensureECDSAKeyExists); err != nil { + if err := s.configureECDSAKey(hostKeyDir); err != nil { return err } - if err := s.configureHostKey(s.ensureRSAKeyExists); err != nil { + if err := s.configureRSAKey(hostKeyDir); err != nil { return err } return nil } -func (s *SSHProxy) configureHostKey(keyFunc func() (string, error)) error { - path, err := keyFunc() - if err != nil { - return err - } - - if err := s.SetOption(ssh.HostKeyFile(path)); err != nil { - return errors.Wrap(err, "Could not set SSH host key") - } - return nil -} - -func (s *SSHProxy) ensureRSAKeyExists() (string, error) { - keyPath := filepath.Join(systemConfigPath, rsaFilename) +func (s *SSHProxy) configureRSAKey(basePath string) error { + keyPath := filepath.Join(basePath, rsaFilename) if _, err := os.Stat(keyPath); os.IsNotExist(err) { key, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { - return "", errors.Wrap(err, "Error generating RSA host key") + return errors.Wrap(err, "Error generating RSA host key") } privateKey := &pem.Block{ @@ -68,25 +65,28 @@ func (s *SSHProxy) ensureRSAKeyExists() (string, error) { } if err = writePrivateKey(keyPath, privateKey); err != nil { - return "", err + return err } s.logger.Debug("Created new RSA SSH host key: ", keyPath) } - return keyPath, nil + if err := s.SetOption(ssh.HostKeyFile(keyPath)); err != nil { + return errors.Wrap(err, "Could not set SSH RSA host key") + } + return nil } -func (s *SSHProxy) ensureECDSAKeyExists() (string, error) { - keyPath := filepath.Join(systemConfigPath, ecdsaFilename) +func (s *SSHProxy) configureECDSAKey(basePath string) error { + keyPath := filepath.Join(basePath, ecdsaFilename) if _, err := os.Stat(keyPath); os.IsNotExist(err) { key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { - return "", errors.Wrap(err, "Error generating ECDSA host key") + return errors.Wrap(err, "Error generating ECDSA host key") } keyBytes, err := x509.MarshalECPrivateKey(key) if err != nil { - return "", errors.Wrap(err, "Error marshalling ECDSA key") + return errors.Wrap(err, "Error marshalling ECDSA key") } privateKey := &pem.Block{ @@ -95,12 +95,15 @@ func (s *SSHProxy) ensureECDSAKeyExists() (string, error) { } if err = writePrivateKey(keyPath, privateKey); err != nil { - return "", err + return err } s.logger.Debug("Created new ECDSA SSH host key: ", keyPath) } - return keyPath, nil + if err := s.SetOption(ssh.HostKeyFile(keyPath)); err != nil { + return errors.Wrap(err, "Could not set SSH ECDSA host key") + } + return nil } func writePrivateKey(keyPath string, privateKey *pem.Block) error { diff --git a/sshserver/sshserver_unix.go b/sshserver/sshserver_unix.go index 1ea26f8a..101fa987 100644 --- a/sshserver/sshserver_unix.go +++ b/sshserver/sshserver_unix.go @@ -78,7 +78,7 @@ type SSHPreamble struct { } // New creates a new SSHProxy and configures its host keys and authentication by the data provided -func New(logManager sshlog.Manager, logger *logrus.Logger, version, localAddress, hostname string, shutdownC chan struct{}, idleTimeout, maxTimeout time.Duration) (*SSHProxy, error) { +func New(logManager sshlog.Manager, logger *logrus.Logger, version, localAddress, hostname, hostKeyDir string, shutdownC chan struct{}, idleTimeout, maxTimeout time.Duration) (*SSHProxy, error) { sshProxy := SSHProxy{ hostname: hostname, logger: logger, @@ -98,7 +98,7 @@ func New(logManager sshlog.Manager, logger *logrus.Logger, version, localAddress }, } - if err := sshProxy.configureHostKeys(); err != nil { + if err := sshProxy.configureHostKeys(hostKeyDir); err != nil { return nil, err } diff --git a/sshserver/sshserver_windows.go b/sshserver/sshserver_windows.go index d2cb617a..f87c582b 100644 --- a/sshserver/sshserver_windows.go +++ b/sshserver/sshserver_windows.go @@ -20,7 +20,7 @@ type SSHPreamble struct { JWT string } -func New(_ sshlog.Manager, _ *logrus.Logger, _, _, _ string, _ chan struct{}, _, _ time.Duration) (*SSHServer, error) { +func New(_ sshlog.Manager, _ *logrus.Logger, _, _, _, _ string, _ chan struct{}, _, _ time.Duration) (*SSHServer, error) { return nil, errors.New("cloudflared ssh server is not supported on windows") }