Add configtest subcommand. skip un-ssh-able servers.
This commit is contained in:
@@ -115,9 +115,7 @@ func (l *base) dockerPs(option string) (string, error) {
|
||||
cmd := fmt.Sprintf("docker ps %s", option)
|
||||
r := l.ssh(cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return "", fmt.Errorf(
|
||||
"Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return "", fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
return r.Stdout, nil
|
||||
}
|
||||
|
||||
@@ -53,13 +53,14 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
|
||||
deb.setServerInfo(c)
|
||||
|
||||
if r := sshExec(c, "ls /etc/debian_version", noSudo); !r.isSuccess() {
|
||||
if r.Error != nil {
|
||||
return false, deb, r.Error
|
||||
}
|
||||
if r.ExitStatus == 255 {
|
||||
return false, deb, fmt.Errorf(
|
||||
"Unable to connect via SSH. Check SSH settings. servername: %s, %s@%s:%s, status: %d, stdout: %s, stderr: %s",
|
||||
c.ServerName, c.User, c.Host, c.Port, r.ExitStatus, r.Stdout, r.Stderr,
|
||||
)
|
||||
"Unable to connect via SSH. Check SSH settings. %s", r)
|
||||
}
|
||||
Log.Debugf("Not Debian like Linux. Host: %s:%s", c.Host, c.Port)
|
||||
Log.Debugf("Not Debian like Linux. %s", r)
|
||||
return false, deb, nil
|
||||
}
|
||||
|
||||
@@ -75,8 +76,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
|
||||
if len(result) == 0 {
|
||||
deb.setDistributionInfo("debian/ubuntu", "unknown")
|
||||
Log.Warnf(
|
||||
"Unknown Debian/Ubuntu version. lsb_release -ir: %s, Host: %s:%s",
|
||||
r.Stdout, c.Host, c.Port)
|
||||
"Unknown Debian/Ubuntu version. lsb_release -ir: %s", r)
|
||||
} else {
|
||||
distro := strings.ToLower(trim(result[1]))
|
||||
deb.setDistributionInfo(distro, trim(result[2]))
|
||||
@@ -95,8 +95,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
|
||||
result := re.FindStringSubmatch(trim(r.Stdout))
|
||||
if len(result) == 0 {
|
||||
Log.Warnf(
|
||||
"Unknown Debian/Ubuntu. cat /etc/lsb-release: %s, Host: %s:%s",
|
||||
r.Stdout, c.Host, c.Port)
|
||||
"Unknown Debian/Ubuntu. cat /etc/lsb-release: %s", r)
|
||||
deb.setDistributionInfo("debian/ubuntu", "unknown")
|
||||
} else {
|
||||
distro := strings.ToLower(trim(result[1]))
|
||||
@@ -112,7 +111,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
|
||||
return true, deb, nil
|
||||
}
|
||||
|
||||
Log.Debugf("Not Debian like Linux. Host: %s:%s", c.Host, c.Port)
|
||||
Log.Debugf("Not Debian like Linux: %s", c.ServerName)
|
||||
return false, deb, nil
|
||||
}
|
||||
|
||||
@@ -126,8 +125,7 @@ func (o *debian) install() error {
|
||||
o.log.Infof("apt-get update...")
|
||||
cmd := util.PrependProxyEnv("apt-get update")
|
||||
if r := o.ssh(cmd, sudo); !r.isSuccess() {
|
||||
msg := fmt.Sprintf("Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
msg := fmt.Sprintf("Failed to SSH: %s", r)
|
||||
o.log.Errorf(msg)
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
@@ -136,8 +134,7 @@ func (o *debian) install() error {
|
||||
// install aptitude
|
||||
cmd = util.PrependProxyEnv("apt-get install --force-yes -y aptitude")
|
||||
if r := o.ssh(cmd, sudo); !r.isSuccess() {
|
||||
msg := fmt.Sprintf("Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
msg := fmt.Sprintf("Failed to SSH: %s", r)
|
||||
o.log.Errorf(msg)
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
@@ -158,8 +155,7 @@ func (o *debian) install() error {
|
||||
cmd = util.PrependProxyEnv(
|
||||
"apt-get install --force-yes -y unattended-upgrades")
|
||||
if r := o.ssh(cmd, sudo); !r.isSuccess() {
|
||||
msg := fmt.Sprintf("Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
msg := fmt.Sprintf("Failed to SSH: %s", r)
|
||||
o.log.Errorf(msg)
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
@@ -189,9 +185,7 @@ func (o *debian) scanPackages() error {
|
||||
func (o *debian) scanInstalledPackages() (packs []models.PackageInfo, err error) {
|
||||
r := o.ssh("dpkg-query -W", noSudo)
|
||||
if !r.isSuccess() {
|
||||
return packs, fmt.Errorf(
|
||||
"Failed to scan packages. status: %d, stdout:%s, stderr: %s",
|
||||
r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return packs, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
|
||||
// e.g.
|
||||
@@ -232,7 +226,7 @@ func (o *debian) checkRequiredPackagesInstalled() error {
|
||||
|
||||
if o.Family == "debian" {
|
||||
if r := o.ssh("test -f /usr/bin/aptitude", noSudo); !r.isSuccess() {
|
||||
msg := "aptitude is not installed"
|
||||
msg := fmt.Sprintf("aptitude is not installed: %s", r)
|
||||
o.log.Errorf(msg)
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
@@ -243,7 +237,7 @@ func (o *debian) checkRequiredPackagesInstalled() error {
|
||||
}
|
||||
|
||||
if r := o.ssh("type unattended-upgrade", noSudo); !r.isSuccess() {
|
||||
msg := "unattended-upgrade is not installed"
|
||||
msg := fmt.Sprintf("unattended-upgrade is not installed: %s", r)
|
||||
o.log.Errorf(msg)
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
@@ -255,10 +249,7 @@ func (o *debian) scanUnsecurePackages(packs []models.PackageInfo) ([]CvePacksInf
|
||||
// cmd := prependProxyEnv(conf.HTTPProxy, "apt-get update | cat; echo 1")
|
||||
cmd := util.PrependProxyEnv("apt-get update")
|
||||
if r := o.ssh(cmd, sudo); !r.isSuccess() {
|
||||
return nil, fmt.Errorf(
|
||||
"Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr,
|
||||
)
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
|
||||
var upgradablePackNames []string
|
||||
@@ -325,9 +316,7 @@ func (o *debian) fillCandidateVersion(packs []models.PackageInfo) ([]models.Pack
|
||||
cmd := fmt.Sprintf("apt-cache policy %s", p.Name)
|
||||
r := o.ssh(cmd, sudo)
|
||||
if !r.isSuccess() {
|
||||
errChan <- fmt.Errorf(
|
||||
"Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
errChan <- fmt.Errorf("Failed to SSH: %s.", r)
|
||||
return
|
||||
}
|
||||
ver, err := o.parseAptCachePolicy(r.Stdout, p.Name)
|
||||
@@ -473,7 +462,6 @@ func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePac
|
||||
}()
|
||||
|
||||
timeout := time.After(30 * 60 * time.Second)
|
||||
|
||||
concurrency := 10
|
||||
tasks := util.GenWorkers(concurrency)
|
||||
for range unsecurePacks {
|
||||
@@ -554,9 +542,7 @@ func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]string, error) {
|
||||
|
||||
r := o.ssh(cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
o.log.Warnf(
|
||||
"Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
o.log.Warnf("Failed to SSH: %s", r)
|
||||
// Ignore this Error.
|
||||
return nil, nil
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.Debugf("Not FreeBSD. Host: %s:%s", c.Host, c.Port)
|
||||
Log.Debugf("Not FreeBSD. servernam: %s", c.ServerName)
|
||||
return false, bsd
|
||||
}
|
||||
|
||||
@@ -69,8 +69,7 @@ func (o *bsd) scanInstalledPackages() ([]models.PackageInfo, error) {
|
||||
cmd := util.PrependProxyEnv("pkg version -v")
|
||||
r := o.ssh(cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return nil, fmt.Errorf("Failed to %s. status: %d, stdout:%s, Stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
return o.parsePkgVersion(r.Stdout), nil
|
||||
}
|
||||
@@ -80,15 +79,13 @@ func (o *bsd) scanUnsecurePackages() (cvePacksList []CvePacksInfo, err error) {
|
||||
cmd := "rm -f " + vulndbPath
|
||||
r := o.ssh(cmd, noSudo)
|
||||
if !r.isSuccess(0) {
|
||||
return nil, fmt.Errorf("Failed to %s. status: %d, stdout:%s, Stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
|
||||
cmd = util.PrependProxyEnv("pkg audit -F -r -f " + vulndbPath)
|
||||
r = o.ssh(cmd, noSudo)
|
||||
if !r.isSuccess(0, 1) {
|
||||
return nil, fmt.Errorf("Failed to %s. status: %d, stdout:%s, Stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
if r.ExitStatus == 0 {
|
||||
// no vulnerabilities
|
||||
|
||||
@@ -56,7 +56,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
|
||||
if r := sshExec(c, "ls /etc/fedora-release", noSudo); r.isSuccess() {
|
||||
red.setDistributionInfo("fedora", "unknown")
|
||||
Log.Warn("Fedora not tested yet. Host: %s:%s", c.Host, c.Port)
|
||||
Log.Warn("Fedora not tested yet: %s", r)
|
||||
return true, red
|
||||
}
|
||||
|
||||
@@ -69,9 +69,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
re, _ := regexp.Compile(`(.*) release (\d[\d.]*)`)
|
||||
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
|
||||
if len(result) != 3 {
|
||||
Log.Warn(
|
||||
"Failed to parse RedHat/CentOS version. stdout: %s, Host: %s:%s",
|
||||
r.Stdout, c.Host, c.Port)
|
||||
Log.Warn("Failed to parse RedHat/CentOS version: %s", r)
|
||||
return true, red
|
||||
}
|
||||
|
||||
@@ -100,7 +98,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
return true, red
|
||||
}
|
||||
|
||||
Log.Debugf("Not RedHat like Linux. Host: %s:%s", c.Host, c.Port)
|
||||
Log.Debugf("Not RedHat like Linux. servername: %s", c.ServerName)
|
||||
return false, red
|
||||
}
|
||||
|
||||
@@ -132,9 +130,7 @@ func (o *redhat) installYumPluginSecurity() error {
|
||||
o.log.Info("Installing yum-plugin-security...")
|
||||
cmd := util.PrependProxyEnv("yum install -y yum-plugin-security")
|
||||
if r := o.ssh(cmd, sudo); !r.isSuccess() {
|
||||
return fmt.Errorf(
|
||||
"Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -167,9 +163,7 @@ func (o *redhat) installYumChangelog() error {
|
||||
o.log.Infof("Installing %s...", packName)
|
||||
cmd = util.PrependProxyEnv("yum install -y " + packName)
|
||||
if r := o.ssh(cmd, sudo); !r.isSuccess() {
|
||||
return fmt.Errorf(
|
||||
"Failed to install %s. status: %d, stdout: %s, stderr: %s",
|
||||
packName, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
o.log.Infof("Installed: %s", packName)
|
||||
}
|
||||
@@ -291,9 +285,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (CvePacksList, error)
|
||||
r := o.ssh(util.PrependProxyEnv(cmd), sudo)
|
||||
if !r.isSuccess(0, 100) {
|
||||
//returns an exit code of 100 if there are available updates.
|
||||
return nil, fmt.Errorf(
|
||||
"Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
|
||||
// get Updateble package name, installed, candidate version.
|
||||
@@ -472,9 +464,7 @@ func (o *redhat) getChangelog(packageNames string) (stdout string, err error) {
|
||||
|
||||
r := o.ssh(command, sudo)
|
||||
if !r.isSuccess(0, 1) {
|
||||
return "", fmt.Errorf(
|
||||
"Failed to get changelog. status: %d, stdout: %s, stderr: %s",
|
||||
r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return "", fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
return r.Stdout, nil
|
||||
}
|
||||
@@ -497,18 +487,14 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
|
||||
cmd := "yum --color=never repolist"
|
||||
r := o.ssh(util.PrependProxyEnv(cmd), sudo)
|
||||
if !r.isSuccess() {
|
||||
return nil, fmt.Errorf(
|
||||
"Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
|
||||
// get advisoryID(RHSA, ALAS) - package name,version
|
||||
cmd = "yum --color=never updateinfo list available --security"
|
||||
r = o.ssh(util.PrependProxyEnv(cmd), sudo)
|
||||
if !r.isSuccess() {
|
||||
return nil, fmt.Errorf(
|
||||
"Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
advIDPackNamesList, err := o.parseYumUpdateinfoListAvailable(r.Stdout)
|
||||
|
||||
@@ -518,9 +504,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
|
||||
r = o.ssh(util.PrependProxyEnv(cmd), sudo)
|
||||
if !r.isSuccess(0, 100) {
|
||||
//returns an exit code of 100 if there are available updates.
|
||||
return nil, fmt.Errorf(
|
||||
"Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
updatable, err := o.parseYumCheckUpdateLines(r.Stdout)
|
||||
if err != nil {
|
||||
@@ -547,9 +531,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
|
||||
cmd = "yum --color=never updateinfo --security update"
|
||||
r = o.ssh(util.PrependProxyEnv(cmd), sudo)
|
||||
if !r.isSuccess() {
|
||||
return nil, fmt.Errorf(
|
||||
"Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
advisoryCveIDsList, err := o.parseYumUpdateinfo(r.Stdout)
|
||||
if err != nil {
|
||||
|
||||
@@ -109,8 +109,8 @@ func (s CvePacksList) Less(i, j int) bool {
|
||||
func detectOS(c config.ServerInfo) (osType osTypeInterface) {
|
||||
var itsMe bool
|
||||
var fatalErr error
|
||||
itsMe, osType, fatalErr = detectDebian(c)
|
||||
|
||||
itsMe, osType, fatalErr = detectDebian(c)
|
||||
if fatalErr != nil {
|
||||
osType.setServerInfo(c)
|
||||
osType.setErrs([]error{fatalErr})
|
||||
@@ -134,74 +134,61 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) {
|
||||
}
|
||||
|
||||
// InitServers detect the kind of OS distribution of target servers
|
||||
func InitServers(localLogger *logrus.Entry) error {
|
||||
func InitServers(localLogger *logrus.Entry) {
|
||||
Log = localLogger
|
||||
servers = detectServerOSes()
|
||||
|
||||
hosts, err := detectServerOSes()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to detect server OSes. err: %s", err)
|
||||
}
|
||||
servers = hosts
|
||||
Log.Info("Detecting Container OS...")
|
||||
containers, err := detectContainerOSes()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to detect Container OSes. err: %s", err)
|
||||
}
|
||||
containers := detectContainerOSes()
|
||||
servers = append(servers, containers...)
|
||||
|
||||
Log.Info("Detecting Platforms...")
|
||||
errs := detectPlatforms()
|
||||
if 0 < len(errs) {
|
||||
// Only logging
|
||||
Log.Errorf("Failed to detect platforms. err: %v", errs)
|
||||
}
|
||||
for i, s := range servers {
|
||||
Log.Info("SSH-able servers are below...")
|
||||
for _, s := range servers {
|
||||
if s.getServerInfo().IsContainer() {
|
||||
Log.Infof("(%d/%d) %s on %s is running on %s",
|
||||
i+1, len(servers),
|
||||
fmt.Printf("%s@%s ",
|
||||
s.getServerInfo().Container.Name,
|
||||
s.getServerInfo().ServerName,
|
||||
s.getPlatform().Name,
|
||||
)
|
||||
|
||||
} else {
|
||||
Log.Infof("(%d/%d) %s is running on %s",
|
||||
i+1, len(servers),
|
||||
s.getServerInfo().ServerName,
|
||||
s.getPlatform().Name,
|
||||
)
|
||||
fmt.Printf("%s ", s.getServerInfo().ServerName)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
func detectServerOSes() (oses []osTypeInterface, err error) {
|
||||
func detectServerOSes() (sshAbleOses []osTypeInterface) {
|
||||
Log.Info("Detecting OS of servers... ")
|
||||
osTypeChan := make(chan osTypeInterface, len(config.Conf.Servers))
|
||||
defer close(osTypeChan)
|
||||
for _, s := range config.Conf.Servers {
|
||||
go func(s config.ServerInfo) {
|
||||
//TODO handling Unknown OS
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
Log.Debugf("Panic: %s on %s", p, s.ServerName)
|
||||
}
|
||||
}()
|
||||
osTypeChan <- detectOS(s)
|
||||
}(s)
|
||||
}
|
||||
|
||||
timeout := time.After(60 * time.Second)
|
||||
var oses []osTypeInterface
|
||||
timeout := time.After(30 * time.Second)
|
||||
for i := 0; i < len(config.Conf.Servers); i++ {
|
||||
select {
|
||||
case res := <-osTypeChan:
|
||||
oses = append(oses, res)
|
||||
if 0 < len(res.getErrs()) {
|
||||
Log.Infof("(%d/%d) Failed %s",
|
||||
Log.Errorf("(%d/%d) Failed: %s, err: %s",
|
||||
i+1, len(config.Conf.Servers),
|
||||
res.getServerInfo().ServerName)
|
||||
res.getServerInfo().ServerName,
|
||||
res.getErrs())
|
||||
} else {
|
||||
Log.Infof("(%d/%d) Detected %s: %s",
|
||||
Log.Infof("(%d/%d) Detected: %s: %s",
|
||||
i+1, len(config.Conf.Servers),
|
||||
res.getServerInfo().ServerName,
|
||||
res.getDistributionInfo())
|
||||
}
|
||||
case <-timeout:
|
||||
msg := "Timeout occurred while detecting"
|
||||
msg := "Timed out while detecting servers"
|
||||
Log.Error(msg)
|
||||
for servername := range config.Conf.Servers {
|
||||
found := false
|
||||
@@ -212,52 +199,56 @@ func detectServerOSes() (oses []osTypeInterface, err error) {
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
Log.Errorf("Failed to detect. servername: %s", servername)
|
||||
Log.Errorf("(%d/%d) Timed out: %s",
|
||||
i+1, len(config.Conf.Servers),
|
||||
servername)
|
||||
i++
|
||||
}
|
||||
}
|
||||
return oses, fmt.Errorf(msg)
|
||||
}
|
||||
}
|
||||
|
||||
errs := []error{}
|
||||
for _, osi := range oses {
|
||||
if 0 < len(osi.getErrs()) {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"Error occurred on %s. errs: %s",
|
||||
osi.getServerInfo().ServerName, osi.getErrs()))
|
||||
for _, o := range oses {
|
||||
if len(o.getErrs()) == 0 {
|
||||
sshAbleOses = append(sshAbleOses, o)
|
||||
}
|
||||
}
|
||||
if 0 < len(errs) {
|
||||
return oses, fmt.Errorf("%s", errs)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func detectContainerOSes() (oses []osTypeInterface, err error) {
|
||||
func detectContainerOSes() (actives []osTypeInterface) {
|
||||
Log.Info("Detecting OS of containers... ")
|
||||
osTypesChan := make(chan []osTypeInterface, len(servers))
|
||||
defer close(osTypesChan)
|
||||
for _, s := range servers {
|
||||
go func(s osTypeInterface) {
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
Log.Debugf("Panic: %s on %s",
|
||||
p, s.getServerInfo().ServerName)
|
||||
}
|
||||
}()
|
||||
osTypesChan <- detectContainerOSesOnServer(s)
|
||||
}(s)
|
||||
}
|
||||
|
||||
timeout := time.After(60 * time.Second)
|
||||
for i := 0; i < len(config.Conf.Servers); i++ {
|
||||
var oses []osTypeInterface
|
||||
timeout := time.After(30 * time.Second)
|
||||
for i := 0; i < len(servers); i++ {
|
||||
select {
|
||||
case res := <-osTypesChan:
|
||||
oses = append(oses, res...)
|
||||
for _, osi := range res {
|
||||
sinfo := osi.getServerInfo()
|
||||
if 0 < len(osi.getErrs()) {
|
||||
Log.Errorf("Failed: %s err: %s", sinfo.ServerName, osi.getErrs())
|
||||
continue
|
||||
}
|
||||
sinfo := osi.getServerInfo()
|
||||
Log.Infof("Detected %s/%s on %s: %s",
|
||||
sinfo.Container.ContainerID, sinfo.Container.Name,
|
||||
sinfo.ServerName, osi.getDistributionInfo())
|
||||
oses = append(oses, res...)
|
||||
Log.Infof("Detected: %s@%s: %s",
|
||||
sinfo.Container.Name, sinfo.ServerName, osi.getDistributionInfo())
|
||||
}
|
||||
case <-timeout:
|
||||
msg := "Timeout occurred while detecting"
|
||||
msg := "Timed out while detecting containers"
|
||||
Log.Error(msg)
|
||||
for servername := range config.Conf.Servers {
|
||||
found := false
|
||||
@@ -268,24 +259,17 @@ func detectContainerOSes() (oses []osTypeInterface, err error) {
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
Log.Errorf("Failed to detect. servername: %s", servername)
|
||||
Log.Errorf("Timed out: %s", servername)
|
||||
|
||||
}
|
||||
}
|
||||
return oses, fmt.Errorf(msg)
|
||||
}
|
||||
}
|
||||
|
||||
errs := []error{}
|
||||
for _, osi := range oses {
|
||||
if 0 < len(osi.getErrs()) {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"Error occurred on %s. errs: %s",
|
||||
osi.getServerInfo().ServerName, osi.getErrs()))
|
||||
for _, o := range oses {
|
||||
if len(o.getErrs()) == 0 {
|
||||
actives = append(actives, o)
|
||||
}
|
||||
}
|
||||
if 0 < len(errs) {
|
||||
return oses, fmt.Errorf("%s", errs)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -361,6 +345,33 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn
|
||||
return oses
|
||||
}
|
||||
|
||||
// DetectPlatforms detects the platform of each servers.
|
||||
func DetectPlatforms(localLogger *logrus.Entry) {
|
||||
errs := detectPlatforms()
|
||||
if 0 < len(errs) {
|
||||
// Only logging
|
||||
Log.Warnf("Failed to detect platforms. err: %v", errs)
|
||||
}
|
||||
for i, s := range servers {
|
||||
if s.getServerInfo().IsContainer() {
|
||||
Log.Infof("(%d/%d) %s on %s is running on %s",
|
||||
i+1, len(servers),
|
||||
s.getServerInfo().Container.Name,
|
||||
s.getServerInfo().ServerName,
|
||||
s.getPlatform().Name,
|
||||
)
|
||||
|
||||
} else {
|
||||
Log.Infof("(%d/%d) %s is running on %s",
|
||||
i+1, len(servers),
|
||||
s.getServerInfo().ServerName,
|
||||
s.getPlatform().Name,
|
||||
)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func detectPlatforms() []error {
|
||||
timeoutSec := 1 * 60
|
||||
return parallelSSHExec(func(o osTypeInterface) error {
|
||||
@@ -410,7 +421,7 @@ func checkRequiredPackagesInstalled() []error {
|
||||
}
|
||||
|
||||
func scanPackages() []error {
|
||||
timeoutSec := 30 * 60
|
||||
timeoutSec := 120 * 60
|
||||
return parallelSSHExec(func(o osTypeInterface) error {
|
||||
return o.scanPackages()
|
||||
}, timeoutSec)
|
||||
|
||||
105
scan/sshutil.go
105
scan/sshutil.go
@@ -37,18 +37,30 @@ import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/cenkalti/backoff"
|
||||
conf "github.com/future-architect/vuls/config"
|
||||
"github.com/k0kubun/pp"
|
||||
"github.com/future-architect/vuls/util"
|
||||
)
|
||||
|
||||
type sshResult struct {
|
||||
Servername string
|
||||
Host string
|
||||
Port string
|
||||
Cmd string
|
||||
Stdout string
|
||||
Stderr string
|
||||
ExitStatus int
|
||||
Error error
|
||||
}
|
||||
|
||||
func (s sshResult) String() string {
|
||||
return fmt.Sprintf(
|
||||
"SSHResult: servername: %s, cmd: %s, exitstatus: %d, stdout: %s, stderr: %s, err: %s",
|
||||
s.Servername, s.Cmd, s.ExitStatus, s.Stdout, s.Stderr, s.Error)
|
||||
}
|
||||
|
||||
func (s sshResult) isSuccess(expectedStatusCodes ...int) bool {
|
||||
if s.Error != nil {
|
||||
return false
|
||||
}
|
||||
if len(expectedStatusCodes) == 0 {
|
||||
return s.ExitStatus == 0
|
||||
}
|
||||
@@ -71,6 +83,12 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []
|
||||
defer close(errChan)
|
||||
for _, s := range servers {
|
||||
go func(s osTypeInterface) {
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
logrus.Debugf("Panic: %s on %s",
|
||||
p, s.getServerInfo().ServerName)
|
||||
}
|
||||
}()
|
||||
if err := fn(s); err != nil {
|
||||
errChan <- fmt.Errorf("%s@%s:%s: %s",
|
||||
s.getServerInfo().User,
|
||||
@@ -109,28 +127,35 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []
|
||||
|
||||
func sshExec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (result sshResult) {
|
||||
if runtime.GOOS == "windows" || !conf.Conf.SSHExternal {
|
||||
return sshExecNative(c, cmd, sudo, log...)
|
||||
result = sshExecNative(c, cmd, sudo)
|
||||
} else {
|
||||
result = sshExecExternal(c, cmd, sudo)
|
||||
}
|
||||
return sshExecExternal(c, cmd, sudo, log...)
|
||||
|
||||
logger := getSSHLogger(log...)
|
||||
logger.Debug(result)
|
||||
return
|
||||
}
|
||||
|
||||
func sshExecNative(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (result sshResult) {
|
||||
logger := getSSHLogger(log...)
|
||||
|
||||
cmd = decolateCmd(c, cmd, sudo)
|
||||
logger.Debugf("Command: %s",
|
||||
strings.Replace(maskPassword(cmd, c.Password), "\n", "", -1))
|
||||
func sshExecNative(c conf.ServerInfo, cmd string, sudo bool) (result sshResult) {
|
||||
result.Servername = c.ServerName
|
||||
result.Host = c.Host
|
||||
result.Port = c.Port
|
||||
|
||||
var client *ssh.Client
|
||||
var err error
|
||||
client, err = sshConnect(c)
|
||||
if client, err = sshConnect(c); err != nil {
|
||||
result.Error = err
|
||||
result.ExitStatus = 999
|
||||
return
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
var session *ssh.Session
|
||||
if session, err = client.NewSession(); err != nil {
|
||||
logger.Errorf("Failed to new session. err: %s, c: %s",
|
||||
err,
|
||||
pp.Sprintf("%v", c))
|
||||
result.Error = fmt.Errorf(
|
||||
"Failed to create a new session. servername: %s, err: %s",
|
||||
c.ServerName, err)
|
||||
result.ExitStatus = 999
|
||||
return
|
||||
}
|
||||
@@ -143,10 +168,9 @@ func sshExecNative(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entr
|
||||
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
|
||||
}
|
||||
if err = session.RequestPty("xterm", 400, 256, modes); err != nil {
|
||||
logger.Errorf("Failed to request for pseudo terminal. err: %s, c: %s",
|
||||
err,
|
||||
pp.Sprintf("%v", c))
|
||||
|
||||
result.Error = fmt.Errorf(
|
||||
"Failed to request for pseudo terminal. servername: %s, err: %s",
|
||||
c.ServerName, err)
|
||||
result.ExitStatus = 999
|
||||
return
|
||||
}
|
||||
@@ -155,6 +179,7 @@ func sshExecNative(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entr
|
||||
session.Stdout = &stdoutBuf
|
||||
session.Stderr = &stderrBuf
|
||||
|
||||
cmd = decolateCmd(c, cmd, sudo)
|
||||
if err := session.Run(cmd); err != nil {
|
||||
if exitErr, ok := err.(*ssh.ExitError); ok {
|
||||
result.ExitStatus = exitErr.ExitStatus()
|
||||
@@ -167,23 +192,14 @@ func sshExecNative(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entr
|
||||
|
||||
result.Stdout = stdoutBuf.String()
|
||||
result.Stderr = stderrBuf.String()
|
||||
result.Host = c.Host
|
||||
result.Port = c.Port
|
||||
|
||||
logger.Debugf(
|
||||
"SSH executed. cmd: %s, err: %#v, status: %d\nstdout: \n%s\nstderr: \n%s",
|
||||
maskPassword(cmd, c.Password), err, result.ExitStatus, result.Stdout, result.Stderr)
|
||||
|
||||
result.Cmd = strings.Replace(maskPassword(cmd, c.Password), "\n", "", -1)
|
||||
return
|
||||
}
|
||||
|
||||
func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (result sshResult) {
|
||||
logger := getSSHLogger(log...)
|
||||
|
||||
func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool) (result sshResult) {
|
||||
sshBinaryPath, err := exec.LookPath("ssh")
|
||||
if err != nil {
|
||||
logger.Debug("Failed to find SSH binary, using native Go implementation")
|
||||
return sshExecNative(c, cmd, sudo, log...)
|
||||
return sshExecNative(c, cmd, sudo)
|
||||
}
|
||||
|
||||
defaultSSHArgs := []string{
|
||||
@@ -235,31 +251,17 @@ func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.En
|
||||
|
||||
result.Stdout = stdoutBuf.String()
|
||||
result.Stderr = stderrBuf.String()
|
||||
result.Servername = c.ServerName
|
||||
result.Host = c.Host
|
||||
result.Port = c.Port
|
||||
|
||||
logger.Debugf(
|
||||
"SSH executed. cmd: %s %s, err: %#v, status: %d\nstdout: \n%s\nstderr: \n%s",
|
||||
sshBinaryPath,
|
||||
maskPassword(strings.Join(args, " "), c.Password),
|
||||
err, result.ExitStatus, result.Stdout, result.Stderr)
|
||||
|
||||
result.Cmd = fmt.Sprintf("%s %s",
|
||||
sshBinaryPath, maskPassword(strings.Join(args, " "), c.Password))
|
||||
return
|
||||
}
|
||||
|
||||
func getSSHLogger(log ...*logrus.Entry) *logrus.Entry {
|
||||
if len(log) == 0 {
|
||||
level := logrus.InfoLevel
|
||||
if conf.Conf.Debug == true {
|
||||
level = logrus.DebugLevel
|
||||
}
|
||||
l := &logrus.Logger{
|
||||
Out: os.Stderr,
|
||||
Formatter: new(logrus.TextFormatter),
|
||||
Hooks: make(logrus.LevelHooks),
|
||||
Level: level,
|
||||
}
|
||||
return logrus.NewEntry(l)
|
||||
return util.NewCustomLogger(conf.ServerInfo{})
|
||||
}
|
||||
return log[0]
|
||||
}
|
||||
@@ -314,15 +316,13 @@ func tryAgentConnect(c conf.ServerInfo) *ssh.Client {
|
||||
}
|
||||
|
||||
func sshConnect(c conf.ServerInfo) (client *ssh.Client, err error) {
|
||||
|
||||
if client = tryAgentConnect(c); client != nil {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
var auths = []ssh.AuthMethod{}
|
||||
if auths, err = addKeyAuth(auths, c.KeyPath, c.KeyPassword); err != nil {
|
||||
logrus.Fatalf("Failed to add keyAuth. %s@%s:%s err: %s",
|
||||
c.User, c.Host, c.Port, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.Password != "" {
|
||||
@@ -336,8 +336,9 @@ func sshConnect(c conf.ServerInfo) (client *ssh.Client, err error) {
|
||||
}
|
||||
|
||||
notifyFunc := func(e error, t time.Duration) {
|
||||
logrus.Warnf("Failed to ssh %s@%s:%s err: %s, Retrying in %s...",
|
||||
c.User, c.Host, c.Port, e, t)
|
||||
logger := getSSHLogger()
|
||||
logger.Debugf("Failed to Dial to %s, err: %s, Retrying in %s...",
|
||||
c.ServerName, e, t)
|
||||
}
|
||||
err = backoff.RetryNotify(func() error {
|
||||
if client, err = ssh.Dial("tcp", c.Host+":"+c.Port, config); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user