diff --git a/commands/prepare.go b/commands/prepare.go index dc71624b1b..8f34993389 100644 --- a/commands/prepare.go +++ b/commands/prepare.go @@ -149,10 +149,9 @@ func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{ return subcommands.ExitFailure } - logger.Info("Installing...") if errs := scan.Prepare(); 0 < len(errs) { for _, e := range errs { - logger.Errorf("Failed: %s", e) + logger.Errorf("Failed to prepare: %s", e) } return subcommands.ExitFailure } diff --git a/scan/base.go b/scan/base.go index 8353081a60..3a39d6c70a 100644 --- a/scan/base.go +++ b/scan/base.go @@ -33,8 +33,9 @@ import ( type base struct { ServerInfo config.ServerInfo Distro config.Distro + Platform models.Platform - Platform models.Platform + lackDependencies []string osPackages log *logrus.Entry @@ -77,6 +78,10 @@ func (l base) getPlatform() models.Platform { return l.Platform } +func (l base) getLackDependencies() []string { + return l.lackDependencies +} + func (l base) allContainers() (containers []config.Container, err error) { switch l.ServerInfo.Container.Type { case "", "docker": diff --git a/scan/debian.go b/scan/debian.go index 5e88982966..65a21d19f7 100644 --- a/scan/debian.go +++ b/scan/debian.go @@ -124,7 +124,31 @@ func (o *debian) checkIfSudoNoPasswd() error { return nil } +func (o *debian) checkDependencies() error { + switch o.Distro.Family { + case "ubuntu": + return nil + + case "debian": + // Debian needs aptitude to get changelogs. + // Because unable to get changelogs via apt-get changelog on Debian. + name := "aptitude" + cmd := name + " -h" + if r := o.ssh(cmd, noSudo); !r.isSuccess() { + o.lackDependencies = []string{name} + } + return nil + + default: + return fmt.Errorf("Not implemented yet: %s", o.Distro) + } +} + func (o *debian) install() error { + if len(o.lackDependencies) == 0 { + return nil + } + // apt-get update o.log.Infof("apt-get update...") cmd := util.PrependProxyEnv("apt-get update") @@ -134,15 +158,14 @@ func (o *debian) install() error { return fmt.Errorf(msg) } - if o.Distro.Family == "debian" { - // install aptitude - cmd = util.PrependProxyEnv("apt-get install --force-yes -y aptitude") + for _, name := range o.lackDependencies { + cmd = util.PrependProxyEnv("apt-get install " + name) if r := o.ssh(cmd, sudo); !r.isSuccess() { msg := fmt.Sprintf("Failed to SSH: %s", r) o.log.Errorf(msg) return fmt.Errorf(msg) } - o.log.Infof("Installed: aptitude") + o.log.Infof("Installed: " + name) } return nil } diff --git a/scan/freebsd.go b/scan/freebsd.go index 28215bf385..2d904f17ed 100644 --- a/scan/freebsd.go +++ b/scan/freebsd.go @@ -66,6 +66,10 @@ func (o *bsd) checkIfSudoNoPasswd() error { return nil } +func (o *bsd) checkDependencies() error { + return nil +} + func (o *bsd) install() error { return nil } diff --git a/scan/redhat.go b/scan/redhat.go index 8a777ffebd..6eef39cfce 100644 --- a/scan/redhat.go +++ b/scan/redhat.go @@ -112,45 +112,46 @@ func (o *redhat) checkIfSudoNoPasswd() error { // CentOS 6 ... yum-plugin-changelog // CentOS 7 ... yum-plugin-changelog // RHEL, Amazon ... no additinal packages needed -func (o *redhat) install() error { +func (o *redhat) checkDependencies() error { switch o.Distro.Family { case "rhel", "amazon": - o.log.Infof("Nothing to do") + // o.log.Infof("Nothing to do") return nil - } - // CentOS - return o.installYumChangelog() -} -func (o *redhat) installYumChangelog() error { - if o.Distro.Family == "centos" { + case "centos": var majorVersion int if 0 < len(o.Distro.Release) { majorVersion, _ = strconv.Atoi(strings.Split(o.Distro.Release, ".")[0]) } else { - return fmt.Errorf( - "Not implemented yet: %s", o.Distro) + return fmt.Errorf("Not implemented yet: %s", o.Distro) } - var packName = "" + var name = "" if majorVersion < 6 { - packName = "yum-changelog" + name = "yum-changelog" } else { - packName = "yum-plugin-changelog" + name = "yum-plugin-changelog" } - cmd := "rpm -q " + packName + cmd := "rpm -q " + name if r := o.ssh(cmd, noSudo); r.isSuccess() { - o.log.Infof("Ignored: %s already installed", packName) return nil } + o.lackDependencies = []string{name} + return nil - o.log.Infof("Installing %s...", packName) - cmd = util.PrependProxyEnv("yum install -y " + packName) + default: + return fmt.Errorf("Not implemented yet: %s", o.Distro) + } +} + +func (o *redhat) install() error { + for _, name := range o.lackDependencies { + cmd := util.PrependProxyEnv("yum install -y " + name) if r := o.ssh(cmd, sudo); !r.isSuccess() { return fmt.Errorf("Failed to SSH: %s", r) } - o.log.Infof("Installed: %s", packName) + o.log.Infof("Installed: %s", name) } return nil } diff --git a/scan/serverapi.go b/scan/serverapi.go index 2e7b87aefc..34db2506d5 100644 --- a/scan/serverapi.go +++ b/scan/serverapi.go @@ -18,7 +18,10 @@ along with this program. If not, see . package scan import ( + "bufio" "fmt" + "os" + "strings" "time" "github.com/Sirupsen/logrus" @@ -40,7 +43,10 @@ type osTypeInterface interface { setDistro(string, string) getDistro() config.Distro - // getFamily() string + + // checkDependencies checks if dependencies are installed on the target server. + checkDependencies() error + getLackDependencies() []string checkIfSudoNoPasswd() error detectPlatform() error @@ -60,7 +66,7 @@ type osTypeInterface interface { setErrs([]error) } -// osPackages included by linux struct +// osPackages is included by base struct type osPackages struct { // installed packages Packages models.PackageInfoList @@ -425,12 +431,65 @@ func detectPlatforms() []error { // Prepare installs requred packages to scan vulnerabilities. func Prepare() []error { - return parallelSSHExec(func(o osTypeInterface) error { + errs := parallelSSHExec(func(o osTypeInterface) error { + if err := o.checkDependencies(); err != nil { + return err + } + return nil + }) + if len(errs) != 0 { + return errs + } + + var targets []osTypeInterface + for _, s := range servers { + deps := s.getLackDependencies() + if len(deps) != 0 { + targets = append(targets, s) + } + } + if len(targets) == 0 { + Log.Info("No need to install dependencies") + return nil + } + + Log.Info("Below servers are needed to install dependencies") + for _, s := range targets { + for _, d := range s.getLackDependencies() { + Log.Infof(" - %s on %s", d, s.getServerInfo().GetServerName()) + } + } + Log.Info("Is this ok to install dependencies on the servers? [y/N]") + + reader := bufio.NewReader(os.Stdin) + for { + text, err := reader.ReadString('\n') + if err != nil { + return []error{err} + } + switch strings.TrimSpace(text) { + case "", "N", "n": + return nil + case "y", "Y": + goto yes + default: + Log.Info("Please enter y or N") + } + } + +yes: + servers = targets + errs = parallelSSHExec(func(o osTypeInterface) error { if err := o.install(); err != nil { return err } return nil }) + if len(errs) != 0 { + return errs + } + Log.Info("All dependencies were installed correctly") + return nil } // Scan scan