diff --git a/config/loader.go b/config/loader.go index c0d0e6f5..319e5165 100644 --- a/config/loader.go +++ b/config/loader.go @@ -1,10 +1,9 @@ package config // Load loads configuration -func Load(path, keyPass string) error { - var loader Loader - loader = TOMLLoader{} - return loader.Load(path, keyPass) +func Load(path string) error { + loader := TOMLLoader{} + return loader.Load(path) } // Loader is interface of concrete loader diff --git a/config/tomlloader.go b/config/tomlloader.go index 0c90218a..f6e1a95a 100644 --- a/config/tomlloader.go +++ b/config/tomlloader.go @@ -15,7 +15,7 @@ type TOMLLoader struct { } // Load load the configuration TOML file specified by path arg. -func (c TOMLLoader) Load(pathToToml, _ string) error { +func (c TOMLLoader) Load(pathToToml string) error { // util.Log.Infof("Loading config: %s", pathToToml) if _, err := toml.DecodeFile(pathToToml, &Conf); err != nil { return err @@ -149,18 +149,11 @@ func setDefaultIfEmpty(server *ServerInfo) error { } if server.Port == "" { - if Conf.Default.Port != "" { - server.Port = Conf.Default.Port - } else { - server.Port = "22" - } + server.Port = Conf.Default.Port } if server.User == "" { server.User = Conf.Default.User - if server.User == "" && server.Port != "local" { - return xerrors.Errorf("server.user is empty") - } } if server.SSHConfigPath == "" { diff --git a/scanner/executil.go b/scanner/executil.go index c54ff71a..6e89fb06 100644 --- a/scanner/executil.go +++ b/scanner/executil.go @@ -186,10 +186,10 @@ func sshExecExternal(c config.ServerInfo, cmd string, sudo bool) (result execRes return execResult{Error: err} } - defaultSSHArgs := []string{"-tt"} + args := []string{"-tt"} - if 0 < len(c.SSHConfigPath) { - defaultSSHArgs = append(defaultSSHArgs, "-F", c.SSHConfigPath) + if c.SSHConfigPath != "" { + args = append(args, "-F", c.SSHConfigPath) } else { home, err := homedir.Dir() if err != nil { @@ -200,7 +200,7 @@ func sshExecExternal(c config.ServerInfo, cmd string, sudo bool) (result execRes } controlPath := filepath.Join(home, ".vuls", `controlmaster-%r-`+c.ServerName+`.%p`) - defaultSSHArgs = append(defaultSSHArgs, + args = append(args, "-o", "StrictHostKeyChecking=yes", "-o", "LogLevel=quiet", "-o", "ConnectionAttempts=3", @@ -212,19 +212,22 @@ func sshExecExternal(c config.ServerInfo, cmd string, sudo bool) (result execRes } if config.Conf.Vvv { - defaultSSHArgs = append(defaultSSHArgs, "-vvv") + args = append(args, "-vvv") } - if len(c.JumpServer) != 0 { - defaultSSHArgs = append(defaultSSHArgs, "-J", strings.Join(c.JumpServer, ",")) + args = append(args, "-J", strings.Join(c.JumpServer, ",")) } - - args := append(defaultSSHArgs, fmt.Sprintf("%s@%s", c.User, c.Host)) - args = append(args, "-p", c.Port) - if 0 < len(c.KeyPath) { + if c.User != "" { + args = append(args, "-l", c.User) + } + if c.Port != "" { + args = append(args, "-p", c.Port) + } + if c.KeyPath != "" { args = append(args, "-i", c.KeyPath) args = append(args, "-o", "PasswordAuthentication=no") } + args = append(args, c.Host) cmd = decorateCmd(c, cmd, sudo) cmd = fmt.Sprintf("stty cols 1000; %s", cmd) diff --git a/scanner/serverapi.go b/scanner/serverapi.go index 9335808e..de2c4d5b 100644 --- a/scanner/serverapi.go +++ b/scanner/serverapi.go @@ -3,8 +3,10 @@ package scanner import ( "fmt" "math/rand" + "net" "net/http" "os" + ex "os/exec" "strings" "time" @@ -287,7 +289,7 @@ func (s Scanner) detectServerOSes() (servers, errServers []osTypeInterface) { logging.Log.Debugf("Panic: %s on %s", p, srv.ServerName) } }() - if err := checkHostinKnownHosts(srv); err != nil { + if err := validateSSHConfig(&srv); err != nil { checkOS := unknown{base{ServerInfo: srv}} checkOS.setErrs([]error{err}) osTypeChan <- &checkOS @@ -332,16 +334,36 @@ func (s Scanner) detectServerOSes() (servers, errServers []osTypeInterface) { return } -func checkHostinKnownHosts(c config.ServerInfo) error { +func validateSSHConfig(c *config.ServerInfo) error { if isLocalExec(c.Port, c.Host) { return nil } - args := []string{"-G"} - if len(c.SSHConfigPath) > 0 { - args = []string{"-G", "-F", c.SSHConfigPath} + logging.Log.Debugf("Validating SSH Settings for Server:%s ...", c.GetServerName()) + + sshBinaryPath, err := ex.LookPath("ssh") + if err != nil { + return xerrors.Errorf("Failed to lookup ssh binary path. err: %w", err) } - r := localExec(c, fmt.Sprintf("ssh %s %s", strings.Join(args, " "), c.Host), noSudo) + sshKeygenBinaryPath, err := ex.LookPath("ssh-keygen") + if err != nil { + return xerrors.Errorf("Failed to lookup ssh-keygen binary path. err: %w", err) + } + + sshConfigCmd := []string{sshBinaryPath, "-G"} + if c.SSHConfigPath != "" { + sshConfigCmd = append(sshConfigCmd, "-F", c.SSHConfigPath) + } + if c.Port != "" { + sshConfigCmd = append(sshConfigCmd, "-p", c.Port) + } + if c.User != "" { + sshConfigCmd = append(sshConfigCmd, "-l", c.User) + } + sshConfigCmd = append(sshConfigCmd, c.Host) + cmd := strings.Join(sshConfigCmd, " ") + logging.Log.Debugf("Executing... %s", strings.Replace(cmd, "\n", "", -1)) + r := localExec(*c, cmd, noSudo) if !r.isSuccess() { return xerrors.Errorf("Failed to print SSH configuration. err: %w", r.Error) } @@ -352,14 +374,31 @@ func checkHostinKnownHosts(c config.ServerInfo) error { userKnownHosts string ) for _, line := range strings.Split(r.Stdout, "\n") { - if strings.HasPrefix(line, "hostname ") { + if strings.HasPrefix(line, "user ") { + user := strings.TrimPrefix(line, "user ") + logging.Log.Debugf("Setting SSH User:%s for Server:%s ...", user, c.GetServerName()) + c.User = user + } else if strings.HasPrefix(line, "hostname ") { hostname = strings.TrimPrefix(line, "hostname ") + logging.Log.Debugf("Validating SSH HostName:%s for Server:%s ...", hostname, c.GetServerName()) + if _, err := net.LookupHost(hostname); err != nil { + return xerrors.New("Failed to name resolution. Please check the HostName settings for SSH") + } + } else if strings.HasPrefix(line, "port ") { + port := strings.TrimPrefix(line, "port ") + logging.Log.Debugf("Setting SSH Port:%s for Server:%s ...", port, c.GetServerName()) + c.Port = port } else if strings.HasPrefix(line, "globalknownhostsfile ") { globalKnownHosts = strings.TrimPrefix(line, "globalknownhostsfile ") } else if strings.HasPrefix(line, "userknownhostsfile ") { userKnownHosts = strings.TrimPrefix(line, "userknownhostsfile ") } } + if c.User == "" || c.Port == "" { + return xerrors.New("Failed to find User or Port setting. Please check the User or Port settings for SSH") + } + + logging.Log.Debugf("Checking if the host's public key is in known_hosts...") knownHostsPaths := []string{} for _, knownHosts := range []string{userKnownHosts, globalKnownHosts} { @@ -370,29 +409,44 @@ func checkHostinKnownHosts(c config.ServerInfo) error { } } if len(knownHostsPaths) == 0 { - return xerrors.New("Failed to find any known_hosts to use. Please check the UserKnownHostsFile and GlobalKnownHostsFile settings for SSH.") + return xerrors.New("Failed to find any known_hosts to use. Please check the UserKnownHostsFile and GlobalKnownHostsFile settings for SSH") } for _, knownHosts := range knownHostsPaths { if c.Port != "" && c.Port != "22" { - cmd := fmt.Sprintf("ssh-keygen -F %s -f %s", fmt.Sprintf("\"[%s]:%s\"", c.Host, c.Port), knownHosts) - if r := localExec(c, cmd, noSudo); r.isSuccess() { + cmd := fmt.Sprintf("%s -F %s -f %s", sshKeygenBinaryPath, fmt.Sprintf("\"[%s]:%s\"", hostname, c.Port), knownHosts) + logging.Log.Debugf("Executing... %s", strings.Replace(cmd, "\n", "", -1)) + if r := localExec(*c, cmd, noSudo); r.isSuccess() { return nil } } - cmd := fmt.Sprintf("ssh-keygen -F %s -f %s", c.Host, knownHosts) - if r := localExec(c, cmd, noSudo); r.isSuccess() { + cmd := fmt.Sprintf("%s -F %s -f %s", sshKeygenBinaryPath, hostname, knownHosts) + logging.Log.Debugf("Executing... %s", strings.Replace(cmd, "\n", "", -1)) + if r := localExec(*c, cmd, noSudo); r.isSuccess() { return nil } } - sshCmd := fmt.Sprintf("ssh -i %s %s@%s", c.KeyPath, c.User, c.Host) - sshKeyScancmd := fmt.Sprintf("ssh-keyscan -H %s >> %s", hostname, knownHostsPaths[0]) - if c.Port != "" && c.Port != "22" { - sshCmd = fmt.Sprintf("ssh -i %s -p %s %s@%s", c.KeyPath, c.Port, c.User, c.Host) - sshKeyScancmd = fmt.Sprintf("ssh-keyscan -H -p %s %s >> %s", c.Port, hostname, knownHostsPaths[0]) + sshConnArgs := []string{} + sshKeyScanArgs := []string{"-H"} + if c.SSHConfigPath != "" { + sshConnArgs = append(sshConnArgs, "-F", c.SSHConfigPath) } - return xerrors.Errorf("Failed to find the host in known_hosts. Plaese exec `$ %s` or `$ %s`", sshCmd, sshKeyScancmd) + if c.KeyPath != "" { + sshConnArgs = append(sshConnArgs, "-i", c.KeyPath) + } + if c.Port != "" { + sshConnArgs = append(sshConnArgs, "-p", c.Port) + sshKeyScanArgs = append(sshKeyScanArgs, "-p", c.Port) + } + if c.User != "" { + sshConnArgs = append(sshConnArgs, "-l", c.User) + } + sshConnArgs = append(sshConnArgs, c.Host) + sshKeyScanArgs = append(sshKeyScanArgs, fmt.Sprintf("%s >> %s", hostname, knownHostsPaths[0])) + sshConnCmd := fmt.Sprintf("ssh %s", strings.Join(sshConnArgs, " ")) + sshKeyScancmd := fmt.Sprintf("ssh-keyscan %s", strings.Join(sshKeyScanArgs, " ")) + return xerrors.Errorf("Failed to find the host in known_hosts. Plaese exec `$ %s` or `$ %s`", sshConnCmd, sshKeyScancmd) } func (s Scanner) detectContainerOSes(hosts []osTypeInterface) (actives, inactives []osTypeInterface) { diff --git a/subcmds/configtest.go b/subcmds/configtest.go index 9348600e..431bfdec 100644 --- a/subcmds/configtest.go +++ b/subcmds/configtest.go @@ -17,9 +17,8 @@ import ( // ConfigtestCmd is Subcommand type ConfigtestCmd struct { - configPath string - askKeyPassword bool - timeoutSec int + configPath string + timeoutSec int } // Name return subcommand name @@ -35,7 +34,6 @@ func (*ConfigtestCmd) Usage() string { [-config=/path/to/config.toml] [-log-to-file] [-log-dir=/path/to/log] - [-ask-key-password] [-timeout=300] [-containers-only] [-http-proxy=http://192.168.0.1:8080] @@ -59,10 +57,6 @@ func (p *ConfigtestCmd) SetFlags(f *flag.FlagSet) { f.IntVar(&p.timeoutSec, "timeout", 5*60, "Timeout(Sec)") - f.BoolVar(&p.askKeyPassword, "ask-key-password", false, - "Ask ssh privatekey password before scanning", - ) - f.StringVar(&config.Conf.HTTPProxy, "http-proxy", "", "http://proxy-url:port (default: empty)") @@ -79,18 +73,7 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa return subcommands.ExitUsageError } - var keyPass string - var err error - if p.askKeyPassword { - prompt := "SSH key password: " - if keyPass, err = getPasswd(prompt); err != nil { - logging.Log.Error(err) - return subcommands.ExitFailure - } - } - - err = config.Load(p.configPath, keyPass) - if err != nil { + if err := config.Load(p.configPath); err != nil { msg := []string{ fmt.Sprintf("Error loading %s", p.configPath), "If you update Vuls and get this error, there may be incompatible changes in config.toml", diff --git a/subcmds/report.go b/subcmds/report.go index bdd79736..58428ba1 100644 --- a/subcmds/report.go +++ b/subcmds/report.go @@ -175,7 +175,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{} logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "") logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision) - if err := config.Load(p.configPath, ""); err != nil { + if err := config.Load(p.configPath); err != nil { logging.Log.Errorf("Error loading %s, %+v", p.configPath, err) return subcommands.ExitUsageError } diff --git a/subcmds/saas.go b/subcmds/saas.go index 927013df..18509132 100644 --- a/subcmds/saas.go +++ b/subcmds/saas.go @@ -65,7 +65,7 @@ func (p *SaaSCmd) SetFlags(f *flag.FlagSet) { func (p *SaaSCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "") logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision) - if err := config.Load(p.configPath, ""); err != nil { + if err := config.Load(p.configPath); err != nil { logging.Log.Errorf("Error loading %s, %+v", p.configPath, err) return subcommands.ExitUsageError } diff --git a/subcmds/scan.go b/subcmds/scan.go index 0e34f485..75bed6fe 100644 --- a/subcmds/scan.go +++ b/subcmds/scan.go @@ -20,7 +20,6 @@ import ( // ScanCmd is Subcommand of host discovery mode type ScanCmd struct { configPath string - askKeyPassword bool timeoutSec int scanTimeoutSec int cacheDBPath string @@ -43,7 +42,6 @@ func (*ScanCmd) Usage() string { [-log-dir=/path/to/log] [-cachedb-path=/path/to/cache.db] [-http-proxy=http://192.168.0.1:8080] - [-ask-key-password] [-timeout=300] [-timeout-scan=7200] [-debug] @@ -80,10 +78,6 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) { f.StringVar(&config.Conf.HTTPProxy, "http-proxy", "", "http://proxy-url:port (default: empty)") - f.BoolVar(&p.askKeyPassword, "ask-key-password", false, - "Ask ssh privatekey password before scanning", - ) - f.BoolVar(&config.Conf.Pipe, "pipe", false, "Use stdin via PIPE") f.BoolVar(&p.detectIPS, "ips", false, "retrieve IPS information") @@ -116,18 +110,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) } } - var keyPass string - var err error - if p.askKeyPassword { - prompt := "SSH key password: " - if keyPass, err = getPasswd(prompt); err != nil { - logging.Log.Error(err) - return subcommands.ExitFailure - } - } - - err = config.Load(p.configPath, keyPass) - if err != nil { + if err := config.Load(p.configPath); err != nil { msg := []string{ fmt.Sprintf("Error loading %s", p.configPath), "If you update Vuls and get this error, there may be incompatible changes in config.toml", diff --git a/subcmds/server.go b/subcmds/server.go index f5048a32..9b1bb179 100644 --- a/subcmds/server.go +++ b/subcmds/server.go @@ -93,7 +93,7 @@ func (p *ServerCmd) SetFlags(f *flag.FlagSet) { func (p *ServerCmd) Execute(_ context.Context, _ *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "") logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision) - if err := config.Load(p.configPath, ""); err != nil { + if err := config.Load(p.configPath); err != nil { logging.Log.Errorf("Error loading %s. err: %+v", p.configPath, err) return subcommands.ExitUsageError } diff --git a/subcmds/tui.go b/subcmds/tui.go index 38fa654c..75099042 100644 --- a/subcmds/tui.go +++ b/subcmds/tui.go @@ -110,7 +110,7 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) { func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "") logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision) - if err := config.Load(p.configPath, ""); err != nil { + if err := config.Load(p.configPath); err != nil { logging.Log.Errorf("Error loading %s, err: %+v", p.configPath, err) return subcommands.ExitUsageError } diff --git a/subcmds/util.go b/subcmds/util.go index 908808bc..1f7f896c 100644 --- a/subcmds/util.go +++ b/subcmds/util.go @@ -1,29 +1,12 @@ package subcmds import ( - "fmt" "os" "path/filepath" - "github.com/howeyc/gopass" homedir "github.com/mitchellh/go-homedir" - "golang.org/x/xerrors" ) -func getPasswd(prompt string) (string, error) { - for { - fmt.Print(prompt) - pass, err := gopass.GetPasswdMasked() - if err != nil { - return "", xerrors.New("Failed to read a password") - } - if 0 < len(pass) { - return string(pass), nil - } - } - -} - func mkdirDotVuls() error { home, err := homedir.Dir() if err != nil {