diff --git a/commands/cmdutil.go b/commands/cmdutil.go new file mode 100644 index 00000000..9a58019b --- /dev/null +++ b/commands/cmdutil.go @@ -0,0 +1,21 @@ +package commands + +import ( + "fmt" + + "github.com/howeyc/gopass" +) + +func getPasswd(prompt string) (string, error) { + for { + fmt.Print(prompt) + pass, err := gopass.GetPasswdMasked() + if err != nil { + return "", fmt.Errorf("Failed to read password") + } + if 0 < len(pass) { + return string(pass[:]), nil + } + } + +} diff --git a/commands/discover.go b/commands/discover.go index 6b68e526..bc84e211 100644 --- a/commands/discover.go +++ b/commands/discover.go @@ -111,9 +111,7 @@ subjectPrefix = "[vuls]" [default] #port = "22" #user = "username" -#password = "password" #keyPath = "/home/username/.ssh/id_rsa" -#keyPassword = "password" [servers] {{- $names:= .Names}} @@ -122,9 +120,7 @@ subjectPrefix = "[vuls]" host = "{{$ip}}" #port = "22" #user = "root" -#password = "password" #keyPath = "/home/username/.ssh/id_rsa" -#keyPassword = "password" #cpeNames = [ # "cpe:/a:rubyonrails:ruby_on_rails:4.2.1", #] diff --git a/commands/prepare.go b/commands/prepare.go index 5ca45324..9cfc3458 100644 --- a/commands/prepare.go +++ b/commands/prepare.go @@ -34,6 +34,9 @@ type PrepareCmd struct { debug bool configPath string + askSudoPassword bool + askKeyPassword bool + useUnattendedUpgrades bool } @@ -55,7 +58,10 @@ func (*PrepareCmd) Synopsis() string { // Usage return usage func (*PrepareCmd) Usage() string { return `prepare: - prepare [-config=/path/to/config.toml] [-debug] + prepare + [-config=/path/to/config.toml] [-debug] + [-ask-sudo-password] + [-ask-key-password] ` } @@ -68,6 +74,20 @@ func (p *PrepareCmd) SetFlags(f *flag.FlagSet) { defaultConfPath := os.Getenv("PWD") + "/config.toml" f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml") + f.BoolVar( + &p.askKeyPassword, + "ask-key-password", + false, + "Ask ssh privatekey password of target servers before scanning", + ) + + f.BoolVar( + &p.askSudoPassword, + "ask-sudo-password", + false, + "Ask sudo password of target servers before scanning", + ) + f.BoolVar( &p.useUnattendedUpgrades, "use-unattended-upgrades", @@ -78,14 +98,30 @@ func (p *PrepareCmd) SetFlags(f *flag.FlagSet) { // Execute execute func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { - logrus.Infof("Start Preparing (config: %s)", p.configPath) + var keyPass, sudoPass string + var err error + if p.askKeyPassword { + prompt := "SSH key password: " + if keyPass, err = getPasswd(prompt); err != nil { + logrus.Error(err) + return subcommands.ExitFailure + } + } + if p.askSudoPassword { + prompt := "sudo password: " + if sudoPass, err = getPasswd(prompt); err != nil { + logrus.Error(err) + return subcommands.ExitFailure + } + } - err := c.Load(p.configPath) + err = c.Load(p.configPath, keyPass, sudoPass) if err != nil { logrus.Errorf("Error loading %s, %s", p.configPath, err) return subcommands.ExitUsageError } + logrus.Infof("Start Preparing (config: %s)", p.configPath) target := make(map[string]c.ServerInfo) for _, arg := range f.Args() { found := false diff --git a/commands/scan.go b/commands/scan.go index 137b4150..e186ff0b 100644 --- a/commands/scan.go +++ b/commands/scan.go @@ -45,12 +45,15 @@ type ScanCmd struct { cvssScoreOver float64 httpProxy string - useYumPluginSecurity bool - useUnattendedUpgrades bool - // reporting reportSlack bool reportMail bool + + askSudoPassword bool + askKeyPassword bool + + useYumPluginSecurity bool + useUnattendedUpgrades bool } // Name return subcommand name @@ -71,6 +74,8 @@ func (*ScanCmd) Usage() string { [-report-slack] [-report-mail] [-http-proxy=http://192.168.0.1:8080] + [-ask-sudo-password] + [-ask-key-password] [-debug] [-debug-sql] ` @@ -111,6 +116,20 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) { f.BoolVar(&p.reportSlack, "report-slack", false, "Slack report") f.BoolVar(&p.reportMail, "report-mail", false, "Email report") + f.BoolVar( + &p.askSudoPassword, + "ask-sudo-password", + false, + "Ask sudo password of target servers before scanning", + ) + + f.BoolVar( + &p.askKeyPassword, + "ask-key-password", + false, + "Ask ssh privatekey password of target servers before scanning", + ) + f.BoolVar( &p.useYumPluginSecurity, "use-yum-plugin-security", @@ -129,14 +148,30 @@ 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 err error + if p.askKeyPassword { + prompt := "SSH key password: " + if keyPass, err = getPasswd(prompt); err != nil { + logrus.Error(err) + return subcommands.ExitFailure + } + } + if p.askSudoPassword { + prompt := "sudo password: " + if sudoPass, err = getPasswd(prompt); err != nil { + logrus.Error(err) + return subcommands.ExitFailure + } + } - logrus.Infof("Start scanning (config: %s)", p.configPath) - err := c.Load(p.configPath) + err = c.Load(p.configPath, keyPass, sudoPass) if err != nil { logrus.Errorf("Error loading %s, %s", p.configPath, err) return subcommands.ExitUsageError } + logrus.Infof("Start scanning (config: %s)", p.configPath) target := make(map[string]c.ServerInfo) for _, arg := range f.Args() { found := false diff --git a/config/jsonloader.go b/config/jsonloader.go index a5a1e406..a3bb5432 100644 --- a/config/jsonloader.go +++ b/config/jsonloader.go @@ -24,6 +24,6 @@ type JSONLoader struct { } // Load load the configuraiton JSON file specified by path arg. -func (c JSONLoader) Load(path string) (err error) { +func (c JSONLoader) Load(path, sudoPass, keyPass string) (err error) { return fmt.Errorf("Not implement yet") } diff --git a/config/loader.go b/config/loader.go index bb1151cb..d157f8bb 100644 --- a/config/loader.go +++ b/config/loader.go @@ -18,16 +18,14 @@ along with this program. If not, see . package config // Load loads configuration -func Load(path string) error { - - //TODO if path's suffix .toml +func Load(path, keyPass, sudoPass string) error { var loader Loader loader = TOMLLoader{} - return loader.Load(path) + return loader.Load(path, keyPass, sudoPass) } // Loader is interface of concrete loader type Loader interface { - Load(string) error + Load(string, string, string) error } diff --git a/config/tomlloader.go b/config/tomlloader.go index 3339ade6..3f9578a5 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 string) (err error) { +func (c TOMLLoader) Load(pathToToml, keyPass, sudoPass string) (err error) { var conf Config if _, err := toml.DecodeFile(pathToToml, &conf); err != nil { log.Error("Load config failed", err) @@ -53,10 +53,7 @@ func (c TOMLLoader) Load(pathToToml string) (err error) { s.User = d.User } - s.Password = v.Password - if s.Password == "" { - s.Password = d.Password - } + s.Password = sudoPass s.Host = v.Host @@ -76,10 +73,7 @@ func (c TOMLLoader) Load(pathToToml string) (err error) { } } - s.KeyPassword = v.KeyPassword - if s.KeyPassword == "" { - s.KeyPassword = d.KeyPassword - } + s.KeyPassword = keyPass s.CpeNames = v.CpeNames if len(s.CpeNames) == 0 { diff --git a/scan/redhat.go b/scan/redhat.go index 43507435..5111a083 100644 --- a/scan/redhat.go +++ b/scan/redhat.go @@ -458,7 +458,7 @@ func (o *redhat) parseYumCheckUpdateLine(line string) (models.PackageInfo, error } func (o *redhat) getChangelog(packageNames string) (stdout string, err error) { - command := "echo N | " + command := "" if 0 < len(config.Conf.HTTPProxy) { command += util.ProxyEnv() } diff --git a/scan/serverapi.go b/scan/serverapi.go index 8f116650..dccecd74 100644 --- a/scan/serverapi.go +++ b/scan/serverapi.go @@ -7,7 +7,6 @@ import ( "github.com/Sirupsen/logrus" "github.com/future-architect/vuls/config" "github.com/future-architect/vuls/models" - "github.com/k0kubun/pp" cve "github.com/kotakanbe/go-cve-dictionary/models" ) @@ -110,8 +109,6 @@ func InitServers(localLogger *logrus.Entry) (err error) { Log = localLogger if servers, err = detectServersOS(); err != nil { err = fmt.Errorf("Failed to detect the type of OS. err: %s", err) - } else { - Log.Debugf("%s", pp.Sprintf("%s", servers)) } return } diff --git a/scan/sshutil.go b/scan/sshutil.go index 6d0acd2e..00c2a97f 100644 --- a/scan/sshutil.go +++ b/scan/sshutil.go @@ -137,7 +137,8 @@ func sshExec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (re // set pipefail option. // http://unix.stackexchange.com/questions/14270/get-exit-status-of-process-thats-piped-to-another cmd = fmt.Sprintf("set -o pipefail; %s", cmd) - logger.Debugf("Command: %s", strings.Replace(cmd, "\n", "", -1)) + logger.Debugf("Command: %s", + strings.Replace(maskPassword(cmd, c.Password), "\n", "", -1)) var client *ssh.Client client, err = sshConnect(c) @@ -189,7 +190,7 @@ func sshExec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (re logger.Debugf( "SSH executed. cmd: %s, status: %#v\nstdout: \n%s\nstderr: \n%s", - cmd, err, result.Stdout, result.Stderr) + maskPassword(cmd, c.Password), err, result.Stdout, result.Stderr) return } @@ -240,7 +241,7 @@ func sshConnect(c conf.ServerInfo) (client *ssh.Client, err error) { // log.Debugf("config: %s", pp.Sprintf("%v", config)) notifyFunc := func(e error, t time.Duration) { - logrus.Warnf("Failed to ssh %s@%s:%s. err: %s, Retrying in %s...", + logrus.Warnf("Failed to ssh %s@%s:%s err: %s, Retrying in %s...", c.User, c.Host, c.Port, e, t) logrus.Debugf("sshConInfo: %s", pp.Sprintf("%v", c)) } @@ -309,3 +310,8 @@ func parsePemBlock(block *pem.Block) (interface{}, error) { return nil, fmt.Errorf("rtop: 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) +}