From f939041606443a5beb528d9c42f2cab3ed1a99cd Mon Sep 17 00:00:00 2001 From: kota kanbe Date: Tue, 9 Aug 2016 10:23:57 +0900 Subject: [PATCH] Disable -ask-sudo-password for security reasons --- README.ja.md | 37 +++++++++++------------- README.md | 38 +++++++++++-------------- commands/configtest.go | 8 +++++- commands/prepare.go | 14 ++++----- commands/scan.go | 20 +++++++------ config/config.go | 12 -------- config/loader.go | 7 ++--- config/tomlloader.go | 16 ++--------- scan/debian.go | 14 ++++++--- scan/freebsd.go | 6 ++++ scan/redhat.go | 14 ++++++--- scan/serverapi.go | 32 +++++++++++++++------ scan/sshutil.go | 64 ++++++++++++++++++++++++------------------ 13 files changed, 149 insertions(+), 133 deletions(-) diff --git a/README.ja.md b/README.ja.md index a2d13f1a..44a766a1 100644 --- a/README.ja.md +++ b/README.ja.md @@ -112,6 +112,9 @@ $ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys $ chmod 600 ~/.ssh/authorized_keys ``` +VulsはSSHパスワード認証をサポートしていない。SSH公開鍵鍵認証を使う必要がある。 +また、パスワードありのSUDOもセキュリティ上の理由によりサポートしていないため、スキャン対象サーバに/etc/sudoersにNOPASSWDを設定して、パスワードなしでSUDO可能にする必要がある。 + ## Step3. Install requirements Vulsセットアップに必要な以下のソフトウェアをインストールする。 @@ -506,13 +509,13 @@ host = "172.31.4.82" また、以下のSSH認証をサポートしている。 - SSH agent - SSH public key authentication (with password, empty password) - - Password authentication + SSH Password認証はサポートしていない ---- # Usage: Configtest -configtestサブコマンドは、config.tomlで定義されたサーバ/コンテナに対してSSH可能かどうかをチェックする。 +configtestサブコマンドは、config.tomlで定義されたサーバ/コンテナに対してSSH可能かどうかをチェックする。 ``` $ vuls configtest --help @@ -534,6 +537,18 @@ configtest: Use external ssh command. Default: Use the Go native implementation ``` +また、スキャン対象サーバに対してパスワードなしでSUDO可能な状態かもチェックする。 +スキャン対象サーバ上の`/etc/sudoers`のサンプル +- CentOS, Amazon Linux, RedHat Enterprise Linux +``` +vuls ALL=(root) NOPASSWD: /usr/bin/yum +``` +- Ubuntu, Debian +``` +vuls ALL=(root) NOPASSWD: /usr/bin/apt-get, /usr/bin/apt-cache +``` + + ---- # Usage: Prepare @@ -555,14 +570,11 @@ Prepareサブコマンドは、Vuls内部で利用する以下のパッケージ $ vuls prepare -help prepare [-config=/path/to/config.toml] [-debug] - [-ask-sudo-password] [-ask-key-password] [SERVER]... -ask-key-password Ask ssh privatekey password before scanning - -ask-sudo-password - Ask sudo password of target servers before scanning -config string /path/to/toml (default "$PWD/config.toml") -debug @@ -595,7 +607,6 @@ scan: [-report-slack] [-report-text] [-http-proxy=http://192.168.0.1:8080] - [-ask-sudo-password] [-ask-key-password] [-debug] [-debug-sql] @@ -611,8 +622,6 @@ scan: -ask-key-password Ask ssh privatekey password before scanning - -ask-sudo-password - Ask sudo password of target servers before scanning -aws-profile string AWS Profile to use (default "default") -aws-region string @@ -685,14 +694,6 @@ Defaults:vuls !requiretty | empty password | - | | | with password | required | or use ssh-agent | -## -ask-sudo-password option - -| sudo password on target servers | -ask-sudo-password | | -|:-----------------|:-------|:------| -| NOPASSWORD | - | defined as NOPASSWORD in /etc/sudoers on target servers | -| with password | required | | - - ## -report-json , -report-text option 結果をファイルに出力したい場合に指定する。出力先は、`$PWD/result/current/` @@ -705,12 +706,10 @@ $ vuls scan \ -report-slack \ -report-mail \ -cvss-over=7 \ - -ask-sudo-password \ -ask-key-password \ -cve-dictionary-dbpath=$PWD/cve.sqlite3 ``` この例では、 -- スキャン対象サーバのsudoパスワードを指定 - SSH公開鍵認証(秘密鍵パスフレーズ)を指定 - configに定義された全サーバをスキャン - レポートをslack, emailに送信 @@ -745,7 +744,6 @@ $ vuls scan \ ``` この例では、 - SSH公開鍵認証(秘密鍵パスフレーズなし) -- ノーパスワードでsudoが実行可能 - configに定義された全サーバをスキャン - 結果をJSON形式でS3に格納する。 - バケット名 ... vuls @@ -767,7 +765,6 @@ $ vuls scan \ ``` この例では、 - SSH公開鍵認証(秘密鍵パスフレーズなし) -- ノーパスワードでsudoが実行可能 - configに定義された全サーバをスキャン - 結果をJSON形式でAzure Blobに格納する。 - コンテナ名 ... vuls diff --git a/README.md b/README.md index b3d43233..f327350b 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,9 @@ $ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys $ chmod 600 ~/.ssh/authorized_keys ``` +Vuls doesn't support SSH password authentication. So you have to use SSH key-based authentication. +And also, SUDO with password is not supported for security reasons. So you have to define NOPASSWORD in /etc/sudoers on target servers. + ## Step3. Install requirements Vuls requires the following packages. @@ -506,15 +509,14 @@ You can customize your configuration using this template. Multiple SSH authentication methods are supported. - SSH agent - - SSH public key authentication (with password, empty password) - - Password authentication + - SSH public key authentication (with password and empty password) + Password authentication is not supported. ---- # Usage: Configtest Configtest subcommand check if vuls is able to connect via ssh to servers/containers defined in the config.toml. - ``` $ vuls configtest --help configtest: @@ -535,6 +537,16 @@ configtest: Use external ssh command. Default: Use the Go native implementation ``` +And also, configtest subcommand checks sudo settings on target servers whether Vuls is able to SUDO with nopassword via SSH. +Example of /etc/sudoers on target servers +- CentOS, Amazon Linux, RedHat Enterprise Linux +``` +vuls ALL=(root) NOPASSWD: /usr/bin/yum +``` +- Ubuntu, Debian +``` +vuls ALL=(root) NOPASSWD: /usr/bin/apt-get, /usr/bin/apt-cache +``` ---- @@ -557,14 +569,11 @@ Prepare subcommand installs required packages on each server. $ vuls prepare -help prepare [-config=/path/to/config.toml] [-debug] - [-ask-sudo-password] [-ask-key-password] [SERVER]... -ask-key-password Ask ssh privatekey password before scanning - -ask-sudo-password - Ask sudo password of target servers before scanning -config string /path/to/toml (default "$PWD/config.toml") -debug @@ -597,7 +606,6 @@ scan: [-report-slack] [-report-text] [-http-proxy=http://192.168.0.1:8080] - [-ask-sudo-password] [-ask-key-password] [-debug] [-debug-sql] @@ -612,8 +620,6 @@ scan: -ask-key-password Ask ssh privatekey password before scanning - -ask-sudo-password - Ask sudo password of target servers before scanning -aws-profile string AWS Profile to use (default "default") -aws-region string @@ -687,14 +693,6 @@ Defaults:vuls !requiretty | empty password | - | | | with password | required | or use ssh-agent | -## -ask-sudo-password option - -| sudo password on target servers | -ask-sudo-password | | -|:-----------------|:-------|:------| -| NOPASSWORD | - | defined as NOPASSWORD in /etc/sudoers on target servers | -| with password | required | | - - ## -report-json , -report-text option At the end of the scan, scan results will be available in the `$PWD/result/current/` directory. @@ -706,12 +704,11 @@ $ vuls scan \ --report-slack \ --report-mail \ --cvss-over=7 \ - -ask-sudo-password \ -ask-key-password \ -cve-dictionary-dbpath=$PWD/cve.sqlite3 ``` With this sample command, it will .. -- Ask sudo password and ssh key passsword before scanning +- Ask SSH key passsword before scanning - Scan all servers defined in config file - Send scan results to slack and email - Only Report CVEs that CVSS score is over 7 @@ -725,7 +722,6 @@ $ vuls scan \ ``` With this sample command, it will .. - Use SSH Key-Based authentication with empty password (without -ask-key-password option) -- Sudo with no password (without -ask-sudo-password option) - Scan only 2 servers (server1, server2) - Print scan result to terminal @@ -745,7 +741,6 @@ $ vuls scan \ ``` With this sample command, it will .. - Use SSH Key-Based authentication with empty password (without -ask-key-password option) -- Sudo with no password (without -ask-sudo-password option) - Scan all servers defined in config file - Put scan result(JSON) in S3 bucket. The bucket name is "vuls" in ap-northeast-1 and profile is "default" @@ -764,7 +759,6 @@ $ vuls scan \ ``` With this sample command, it will .. - Use SSH Key-Based authentication with empty password (without -ask-key-password option) -- Sudo with no password (without -ask-sudo-password option) - Scan all servers defined in config file - Put scan result(JSON) in Azure Blob Storage. The container name is "vuls", storage account is "test" and accesskey is "access-key-string" diff --git a/commands/configtest.go b/commands/configtest.go index 3a188c35..cdab6142 100644 --- a/commands/configtest.go +++ b/commands/configtest.go @@ -98,7 +98,7 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa c.Conf.Debug = p.debug - err = c.Load(p.configPath, keyPass, "") + err = c.Load(p.configPath, keyPass) if err != nil { logrus.Errorf("Error loading %s, %s", p.configPath, err) return subcommands.ExitUsageError @@ -152,5 +152,11 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa Log.Info("Detecting Server/Contianer OS... ") scan.InitServers(Log) + Log.Info("Checking sudo configuration... ") + if err := scan.CheckIfSudoNoPasswd(Log); err != nil { + Log.Errorf("Failed to sudo with nopassword via SSH. Define NOPASSWD in /etc/sudoers on target servers. err: %s", err) + return subcommands.ExitFailure + } + scan.PrintSSHableServerNames() return subcommands.ExitSuccess } diff --git a/commands/prepare.go b/commands/prepare.go index e6d98df7..43b39203 100644 --- a/commands/prepare.go +++ b/commands/prepare.go @@ -61,7 +61,6 @@ func (*PrepareCmd) Usage() string { return `prepare: prepare [-config=/path/to/config.toml] - [-ask-sudo-password] [-ask-key-password] [-debug] @@ -90,7 +89,7 @@ func (p *PrepareCmd) SetFlags(f *flag.FlagSet) { &p.askSudoPassword, "ask-sudo-password", false, - "Ask sudo password of target servers before scanning", + "[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASON. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication", ) f.BoolVar( @@ -103,7 +102,7 @@ func (p *PrepareCmd) SetFlags(f *flag.FlagSet) { // Execute execute func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { - var keyPass, sudoPass string + var keyPass string var err error if p.askKeyPassword { prompt := "SSH key password: " @@ -113,14 +112,11 @@ func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{ } } if p.askSudoPassword { - prompt := "sudo password: " - if sudoPass, err = getPasswd(prompt); err != nil { - logrus.Error(err) - return subcommands.ExitFailure - } + logrus.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication") + return subcommands.ExitFailure } - err = c.Load(p.configPath, keyPass, sudoPass) + err = c.Load(p.configPath, keyPass) if err != nil { logrus.Errorf("Error loading %s, %s", p.configPath, err) return subcommands.ExitUsageError diff --git a/commands/scan.go b/commands/scan.go index 298f9586..a2036138 100644 --- a/commands/scan.go +++ b/commands/scan.go @@ -102,7 +102,6 @@ func (*ScanCmd) Usage() string { [-report-slack] [-report-text] [-http-proxy=http://192.168.0.1:8080] - [-ask-sudo-password] [-ask-key-password] [-debug] [-debug-sql] @@ -211,7 +210,7 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) { &p.askSudoPassword, "ask-sudo-password", false, - "Ask sudo password of target servers before scanning", + "[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication", ) f.BoolVar( @@ -232,7 +231,7 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) { // Execute execute func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { - var keyPass, sudoPass string + var keyPass string var err error if p.askKeyPassword { prompt := "SSH key password: " @@ -242,14 +241,11 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) } } if p.askSudoPassword { - prompt := "sudo password: " - if sudoPass, err = getPasswd(prompt); err != nil { - logrus.Error(err) - return subcommands.ExitFailure - } + logrus.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication") + return subcommands.ExitFailure } - err = c.Load(p.configPath, keyPass, sudoPass) + err = c.Load(p.configPath, keyPass) if err != nil { logrus.Errorf("Error loading %s, %s", p.configPath, err) return subcommands.ExitUsageError @@ -383,6 +379,12 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) Log.Info("Detecting Server/Contianer OS... ") scan.InitServers(Log) + Log.Info("Checking sudo configuration... ") + if err := scan.CheckIfSudoNoPasswd(Log); err != nil { + Log.Errorf("Failed to sudo with nopassword via SSH. Define NOPASSWD in /etc/sudoers on target servers") + return subcommands.ExitFailure + } + Log.Info("Detecting Platforms... ") scan.DetectPlatforms(Log) diff --git a/config/config.go b/config/config.go index d99718ac..39e690d0 100644 --- a/config/config.go +++ b/config/config.go @@ -216,7 +216,6 @@ func (c *SlackConf) Validate() (errs []error) { type ServerInfo struct { ServerName string User string - Password string Host string Port string KeyPath string @@ -232,7 +231,6 @@ type ServerInfo struct { // used internal LogMsgAnsiColor string // DebugLog Color - SudoOpt SudoOption Container Container Family string } @@ -253,13 +251,3 @@ type Container struct { Name string Type string } - -// SudoOption is flag of sudo option. -type SudoOption struct { - - // echo pass | sudo -S ls - ExecBySudo bool - - // echo pass | sudo sh -C 'ls' - ExecBySudoSh bool -} diff --git a/config/loader.go b/config/loader.go index d157f8bb..fe89f3aa 100644 --- a/config/loader.go +++ b/config/loader.go @@ -18,14 +18,13 @@ along with this program. If not, see . package config // Load loads configuration -func Load(path, keyPass, sudoPass string) error { +func Load(path, keyPass string) error { var loader Loader loader = TOMLLoader{} - - return loader.Load(path, keyPass, sudoPass) + return loader.Load(path, keyPass) } // Loader is interface of concrete loader type Loader interface { - Load(string, string, string) error + Load(string, string) error } diff --git a/config/tomlloader.go b/config/tomlloader.go index 7797e12c..06c88478 100644 --- a/config/tomlloader.go +++ b/config/tomlloader.go @@ -31,7 +31,7 @@ type TOMLLoader struct { } // Load load the configuraiton TOML file specified by path arg. -func (c TOMLLoader) Load(pathToToml, keyPass, sudoPass string) (err error) { +func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) { var conf Config if _, err := toml.DecodeFile(pathToToml, &conf); err != nil { log.Error("Load config failed", err) @@ -49,15 +49,11 @@ func (c TOMLLoader) Load(pathToToml, keyPass, sudoPass string) (err error) { d.KeyPassword = keyPass } - if sudoPass != "" { - d.Password = sudoPass - } - i := 0 for name, v := range conf.Servers { - if 0 < len(v.KeyPassword) || 0 < len(v.Password) { - log.Warn("[Deprecated] password and keypassword in config file are unsecure. Remove them immediately for a security reason. They will be removed in a future release.") + if 0 < len(v.KeyPassword) { + log.Warn("[Deprecated] KEYPASSWORD IN CONFIG FILE ARE UNSECURE. REMOVE THEM IMMEDIATELY FOR A SECURITY REASONS. THEY WILL BE REMOVED IN A FUTURE RELEASE.") } s := ServerInfo{ServerName: name} @@ -71,12 +67,6 @@ func (c TOMLLoader) Load(pathToToml, keyPass, sudoPass string) (err error) { return fmt.Errorf("%s is invalid. User is empty", name) } - // s.Password = sudoPass - s.Password = v.Password - if s.Password == "" { - s.Password = d.Password - } - s.Host = v.Host if s.Host == "" { return fmt.Errorf("%s is invalid. host is empty", name) diff --git a/scan/debian.go b/scan/debian.go index f6575ddf..f2bc27fd 100644 --- a/scan/debian.go +++ b/scan/debian.go @@ -45,11 +45,7 @@ func newDebian(c config.ServerInfo) *debian { // Ubuntu, Debian // https://github.com/serverspec/specinfra/blob/master/lib/specinfra/helper/detect_os/debian.rb func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err error) { - deb = newDebian(c) - - // set sudo option flag - c.SudoOpt = config.SudoOption{ExecBySudo: true} deb.setServerInfo(c) if r := sshExec(c, "ls /etc/debian_version", noSudo); !r.isSuccess() { @@ -119,6 +115,16 @@ func trim(str string) string { return strings.TrimSpace(str) } +func (o *debian) checkIfSudoNoPasswd() error { + r := o.ssh("apt-get -v", sudo) + if !r.isSuccess() { + o.log.Errorf("sudo error on %s", r) + return fmt.Errorf("Failed to sudo: %s", r) + } + o.log.Infof("sudo ... OK") + return nil +} + func (o *debian) install() error { // apt-get update diff --git a/scan/freebsd.go b/scan/freebsd.go index 9d52500f..0c6e5225 100644 --- a/scan/freebsd.go +++ b/scan/freebsd.go @@ -39,6 +39,12 @@ func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) { return false, bsd } +func (o *bsd) checkIfSudoNoPasswd() error { + // FreeBSD doesn't need root privilege + o.log.Infof("sudo ... OK") + return nil +} + func (o *bsd) install() error { return nil } diff --git a/scan/redhat.go b/scan/redhat.go index ac1ee9ac..d10d3272 100644 --- a/scan/redhat.go +++ b/scan/redhat.go @@ -47,11 +47,7 @@ func newRedhat(c config.ServerInfo) *redhat { // https://github.com/serverspec/specinfra/blob/master/lib/specinfra/helper/detect_os/redhat.rb func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) { - red = newRedhat(c) - - // set sudo option flag - c.SudoOpt = config.SudoOption{ExecBySudo: true} red.setServerInfo(c) if r := sshExec(c, "ls /etc/fedora-release", noSudo); r.isSuccess() { @@ -102,6 +98,16 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) { return false, red } +func (o *redhat) checkIfSudoNoPasswd() error { + r := o.ssh("yum --version", sudo) + if !r.isSuccess() { + o.log.Errorf("sudo error on %s", r) + return fmt.Errorf("Failed to sudo: %s", r) + } + o.log.Infof("sudo ... OK") + return nil +} + // CentOS 5 ... yum-plugin-security, yum-changelog // CentOS 6 ... yum-plugin-security, yum-plugin-changelog // CentOS 7 ... yum-plugin-security, yum-plugin-changelog diff --git a/scan/serverapi.go b/scan/serverapi.go index a6b1a122..d338370b 100644 --- a/scan/serverapi.go +++ b/scan/serverapi.go @@ -23,6 +23,7 @@ type osTypeInterface interface { setDistributionInfo(string, string) getDistributionInfo() string + checkIfSudoNoPasswd() error detectPlatform() error getPlatform() models.Platform @@ -133,14 +134,8 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) { return } -// InitServers detect the kind of OS distribution of target servers -func InitServers(localLogger *logrus.Entry) { - Log = localLogger - servers = detectServerOSes() - - containers := detectContainerOSes() - servers = append(servers, containers...) - +// PrintSSHableServerNames print SSH-able servernames +func PrintSSHableServerNames() { Log.Info("SSH-able servers are below...") for _, s := range servers { if s.getServerInfo().IsContainer() { @@ -155,6 +150,14 @@ func InitServers(localLogger *logrus.Entry) { fmt.Printf("\n") } +// InitServers detect the kind of OS distribution of target servers +func InitServers(localLogger *logrus.Entry) { + Log = localLogger + servers = detectServerOSes() + containers := detectContainerOSes() + servers = append(servers, containers...) +} + func detectServerOSes() (sshAbleOses []osTypeInterface) { Log.Info("Detecting OS of servers... ") osTypeChan := make(chan osTypeInterface, len(config.Conf.Servers)) @@ -345,6 +348,19 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn return oses } +// CheckIfSudoNoPasswd checks whether vuls can sudo with nopassword via SSH +func CheckIfSudoNoPasswd(localLogger *logrus.Entry) error { + timeoutSec := 1 * 15 + errs := parallelSSHExec(func(o osTypeInterface) error { + return o.checkIfSudoNoPasswd() + }, timeoutSec) + + if 0 < len(errs) { + return fmt.Errorf(fmt.Sprintf("%s", errs)) + } + return nil +} + // DetectPlatforms detects the platform of each servers. func DetectPlatforms(localLogger *logrus.Entry) { errs := detectPlatforms() diff --git a/scan/sshutil.go b/scan/sshutil.go index e4397d8a..1042a79a 100644 --- a/scan/sshutil.go +++ b/scan/sshutil.go @@ -79,8 +79,11 @@ const sudo = true const noSudo = false func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []error) { + resChan := make(chan string, len(servers)) errChan := make(chan error, len(servers)) defer close(errChan) + defer close(resChan) + for _, s := range servers { go func(s osTypeInterface) { defer func() { @@ -97,7 +100,7 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs [] err, ) } else { - errChan <- nil + resChan <- s.getServerInfo().ServerName } }(s) } @@ -109,19 +112,40 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs [] timeout = timeoutSec[0] } + var snames []string + isTimedout := false for i := 0; i < len(servers); i++ { select { + case s := <-resChan: + snames = append(snames, s) case err := <-errChan: - if err != nil { - errs = append(errs, err) - } else { - logrus.Debug("Parallel SSH Success") - } + errs = append(errs, err) case <-time.After(time.Duration(timeout) * time.Second): - logrus.Errorf("Parallel SSH Timeout") - errs = append(errs, fmt.Errorf("Timed out")) + isTimedout = true } } + + // collect timed out servernames + var timedoutSnames []string + if isTimedout { + for _, s := range servers { + name := s.getServerInfo().ServerName + found := false + for _, t := range snames { + if name == t { + found = true + break + } + } + if !found { + timedoutSnames = append(timedoutSnames, name) + } + } + } + if isTimedout { + errs = append(errs, fmt.Errorf( + "Timed out: %s", timedoutSnames)) + } return } @@ -196,7 +220,7 @@ func sshExecNative(c conf.ServerInfo, cmd string, sudo bool) (result sshResult) result.Stdout = stdoutBuf.String() result.Stderr = stderrBuf.String() - result.Cmd = strings.Replace(maskPassword(cmd, c.Password), "\n", "", -1) + result.Cmd = strings.Replace(cmd, "\n", "", -1) return } @@ -234,6 +258,8 @@ func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool) (result sshResult } cmd = decolateCmd(c, cmd, sudo) + // cmd = fmt.Sprintf("stty cols 256; set -o pipefail; %s", cmd) + args = append(args, cmd) execCmd := exec.Command(sshBinaryPath, args...) @@ -259,8 +285,7 @@ func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool) (result sshResult result.Servername = c.ServerName result.Host = c.Host result.Port = c.Port - result.Cmd = fmt.Sprintf("%s %s", - sshBinaryPath, maskPassword(strings.Join(args, " "), c.Password)) + result.Cmd = fmt.Sprintf("%s %s", sshBinaryPath, strings.Join(args, " ")) return } @@ -272,14 +297,8 @@ func getSSHLogger(log ...*logrus.Entry) *logrus.Entry { } func decolateCmd(c conf.ServerInfo, cmd string, sudo bool) string { - c.SudoOpt.ExecBySudo = true if sudo && c.User != "root" && !c.IsContainer() { - switch { - case c.SudoOpt.ExecBySudo: - cmd = fmt.Sprintf("echo %s | sudo -S %s", c.Password, cmd) - case c.SudoOpt.ExecBySudoSh: - cmd = fmt.Sprintf("echo %s | sudo sh -c '%s'", c.Password, cmd) - } + cmd = fmt.Sprintf("sudo -S %s", cmd) } if c.Family != "FreeBSD" { @@ -331,10 +350,6 @@ func sshConnect(c conf.ServerInfo) (client *ssh.Client, err error) { return nil, err } - if c.Password != "" { - auths = append(auths, ssh.Password(c.Password)) - } - // http://blog.ralch.com/tutorial/golang-ssh-connection/ config := &ssh.ClientConfig{ User: c.User, @@ -411,8 +426,3 @@ func parsePemBlock(block *pem.Block) (interface{}, error) { return nil, fmt.Errorf("Unsupported key type %q", block.Type) } } - -// ref golang.org/x/crypto/ssh/keys.go#ParseRawPrivateKey. -func maskPassword(cmd, sudoPass string) string { - return strings.Replace(cmd, fmt.Sprintf("echo %s", sudoPass), "echo *****", -1) -}