From 671be3f2f76bd2e8653f5f4194badca69c226620 Mon Sep 17 00:00:00 2001 From: MaineK00n Date: Fri, 11 Feb 2022 12:54:17 +0900 Subject: [PATCH] feat(configtest,scan): detect known_hosts error (#1386) --- scanner/debian.go | 3 +- scanner/serverapi.go | 76 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/scanner/debian.go b/scanner/debian.go index 39e7b290..cb8cbe17 100644 --- a/scanner/debian.go +++ b/scanner/debian.go @@ -48,8 +48,7 @@ func detectDebian(c config.ServerInfo) (bool, osTypeInterface, error) { return false, nil, nil } if r.ExitStatus == 255 { - deb := newDebian(c) // Panic occur when return value 2 is nil and 3 is non-nil - return false, deb, xerrors.Errorf("Unable to connect via SSH. Scan with -vvv option to print SSH debugging messages and check SSH settings. If you have never SSH to the host to be scanned, SSH to the host before scanning in order to add the HostKey. %s@%s port: %s\n%s", c.User, c.Host, c.Port, r) + return false, &unknown{base{ServerInfo: c}}, xerrors.Errorf("Unable to connect via SSH. Scan with -vvv option to print SSH debugging messages and check SSH settings.\n%s", r) } logging.Log.Debugf("Not Debian like Linux. %s", r) return false, nil, nil diff --git a/scanner/serverapi.go b/scanner/serverapi.go index e7e2b4cd..9335808e 100644 --- a/scanner/serverapi.go +++ b/scanner/serverapi.go @@ -5,6 +5,7 @@ import ( "math/rand" "net/http" "os" + "strings" "time" debver "github.com/knqyf263/go-deb-version" @@ -286,6 +287,12 @@ func (s Scanner) detectServerOSes() (servers, errServers []osTypeInterface) { logging.Log.Debugf("Panic: %s on %s", p, srv.ServerName) } }() + if err := checkHostinKnownHosts(srv); err != nil { + checkOS := unknown{base{ServerInfo: srv}} + checkOS.setErrs([]error{err}) + osTypeChan <- &checkOS + return + } osTypeChan <- s.detectOS(srv) }(target) } @@ -296,12 +303,10 @@ func (s Scanner) detectServerOSes() (servers, errServers []osTypeInterface) { case res := <-osTypeChan: if 0 < len(res.getErrs()) { errServers = append(errServers, res) - logging.Log.Errorf("(%d/%d) Failed: %s, err: %+v", - i+1, len(s.Targets), res.getServerInfo().ServerName, res.getErrs()) + logging.Log.Errorf("(%d/%d) Failed: %s, err: %+v", i+1, len(s.Targets), res.getServerInfo().ServerName, res.getErrs()) } else { servers = append(servers, res) - logging.Log.Infof("(%d/%d) Detected: %s: %s", - i+1, len(s.Targets), res.getServerInfo().ServerName, res.getDistro()) + logging.Log.Infof("(%d/%d) Detected: %s: %s", i+1, len(s.Targets), res.getServerInfo().ServerName, res.getDistro()) } case <-timeout: msg := "Timed out while detecting servers" @@ -327,6 +332,69 @@ func (s Scanner) detectServerOSes() (servers, errServers []osTypeInterface) { return } +func checkHostinKnownHosts(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} + } + r := localExec(c, fmt.Sprintf("ssh %s %s", strings.Join(args, " "), c.Host), noSudo) + if !r.isSuccess() { + return xerrors.Errorf("Failed to print SSH configuration. err: %w", r.Error) + } + + var ( + hostname string + globalKnownHosts string + userKnownHosts string + ) + for _, line := range strings.Split(r.Stdout, "\n") { + if strings.HasPrefix(line, "hostname ") { + hostname = strings.TrimPrefix(line, "hostname ") + } else if strings.HasPrefix(line, "globalknownhostsfile ") { + globalKnownHosts = strings.TrimPrefix(line, "globalknownhostsfile ") + } else if strings.HasPrefix(line, "userknownhostsfile ") { + userKnownHosts = strings.TrimPrefix(line, "userknownhostsfile ") + } + } + + knownHostsPaths := []string{} + for _, knownHosts := range []string{userKnownHosts, globalKnownHosts} { + for _, knownHost := range strings.Split(knownHosts, " ") { + if knownHost != "" { + knownHostsPaths = append(knownHostsPaths, knownHost) + } + } + } + if len(knownHostsPaths) == 0 { + 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() { + return nil + } + } + cmd := fmt.Sprintf("ssh-keygen -F %s -f %s", c.Host, knownHosts) + 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]) + } + return xerrors.Errorf("Failed to find the host in known_hosts. Plaese exec `$ %s` or `$ %s`", sshCmd, sshKeyScancmd) +} + func (s Scanner) detectContainerOSes(hosts []osTypeInterface) (actives, inactives []osTypeInterface) { logging.Log.Info("Detecting OS of containers... ") osTypesChan := make(chan []osTypeInterface, len(hosts))