Deprecate prepare subcommand to minimize the root authority #375

This commit is contained in:
Kota Kanbe
2017-03-08 13:40:44 +09:00
parent ce6a4231ef
commit 688cfd6872
15 changed files with 218 additions and 548 deletions

View File

@@ -34,7 +34,6 @@ type base struct {
Distro config.Distro
Platform models.Platform
lackDependencies []string
osPackages
log *logrus.Entry
@@ -77,10 +76,6 @@ 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":

View File

@@ -128,12 +128,14 @@ func trim(str string) string {
}
func (o *debian) checkIfSudoNoPasswd() error {
r := o.exec("apt-get -v", noSudo)
cmd := util.PrependProxyEnv("apt-get update")
o.log.Infof("Checking... sudo %s", cmd)
r := o.exec(cmd, sudo)
if !r.isSuccess() {
o.log.Errorf("sudo error on %s", r)
return fmt.Errorf("Failed to sudo: %s", r)
}
o.log.Infof("sudo ... OK")
o.log.Infof("Sudo... Pass")
return nil
}
@@ -145,11 +147,12 @@ func (o *debian) checkDependencies() error {
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.exec(cmd, noSudo); !r.isSuccess() {
o.lackDependencies = []string{name}
if r := o.exec("test -f /usr/bin/aptitude", noSudo); !r.isSuccess() {
msg := fmt.Sprintf("aptitude is not installed: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
o.log.Infof("Dependencies... Pass")
return nil
default:
@@ -157,32 +160,6 @@ func (o *debian) checkDependencies() error {
}
}
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")
if r := o.exec(cmd, sudo); !r.isSuccess() {
msg := fmt.Sprintf("Failed to SSH: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
for _, name := range o.lackDependencies {
cmd = util.PrependProxyEnv("apt-get install -y " + name)
if r := o.exec(cmd, sudo); !r.isSuccess() {
msg := fmt.Sprintf("Failed to SSH: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
o.log.Infof("Installed: " + name)
}
return nil
}
func (o *debian) scanPackages() error {
var err error
var packs []models.PackageInfo
@@ -244,17 +221,6 @@ func (o *debian) parseScannedPackagesLine(line string) (name, version string, er
return "", "", fmt.Errorf("Unknown format: %s", line)
}
func (o *debian) checkRequiredPackagesInstalled() error {
if o.Distro.Family == "debian" {
if r := o.exec("test -f /usr/bin/aptitude", noSudo); !r.isSuccess() {
msg := fmt.Sprintf("aptitude is not installed: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
}
return nil
}
func (o *debian) scanUnsecurePackages(installed []models.PackageInfo) ([]models.VulnInfo, error) {
o.log.Infof("apt-get update...")
cmd := util.PrependProxyEnv("apt-get update")

View File

@@ -327,9 +327,6 @@ func decorateCmd(c conf.ServerInfo, cmd string, sudo bool) string {
if sudo && c.User != "root" && !c.IsContainer() {
cmd = fmt.Sprintf("sudo -S %s", cmd)
cmd = strings.Replace(cmd, "|", "| sudo ", -1)
// echo command does not need sudo (for CentOS)
cmd = strings.Replace(cmd, "sudo -S echo", "echo", -1)
}
// If you are using pipe and you want to detect preprocessing errors, remove comment out

View File

@@ -69,14 +69,6 @@ func (o *bsd) checkDependencies() error {
return nil
}
func (o *bsd) install() error {
return nil
}
func (o *bsd) checkRequiredPackagesInstalled() error {
return nil
}
func (o *bsd) scanPackages() error {
var err error
var packs []models.PackageInfo

View File

@@ -102,83 +102,97 @@ func (o *redhat) checkIfSudoNoPasswd() error {
return nil
}
cmd := "yum --version"
if o.Distro.Family == "centos" {
cmd = "echo N | " + cmd
type cmd struct {
cmd string
expectedStatusCodes []int
}
r := o.exec(cmd, o.sudo())
if !r.isSuccess() {
o.log.Errorf("sudo error on %s", r)
return fmt.Errorf("Failed to sudo: %s", r)
}
o.log.Infof("sudo ... OK")
return nil
}
var cmds []cmd
var zero = []int{0}
// CentOS 5 ... yum-changelog
// CentOS 6 ... yum-plugin-changelog
// CentOS 7 ... yum-plugin-changelog
// RHEL, Amazon ... no additinal packages needed
func (o *redhat) checkDependencies() error {
switch o.Distro.Family {
case "rhel", "amazon":
return nil
case "centos":
cmds = []cmd{
{"yum --changelog --assumeno update yum", []int{0, 1}},
}
case "rhel":
majorVersion, err := o.Distro.MajorVersion()
if err != nil {
return fmt.Errorf("Not implemented yet: %s, err: %s", o.Distro, err)
}
var name = "yum-plugin-changelog"
if majorVersion < 6 {
name = "yum-changelog"
cmds = []cmd{
{"yum --color=never repolist", zero},
{"yum --color=never check-update", []int{0, 100}},
{"yum --color=never list-security --security", zero},
{"yum --color=never info-security", zero},
}
} else {
cmds = []cmd{
{"yum --color=never repolist", zero},
{"yum --color=never check-update", []int{0, 100}},
{"yum --color=never --security updateinfo list updates", zero},
{"yum --color=never --security updateinfo updates", zero},
}
}
cmd := "rpm -q " + name
if r := o.exec(cmd, noSudo); r.isSuccess() {
return nil
}
o.lackDependencies = []string{name}
return nil
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.exec(cmd, sudo); !r.isSuccess() {
return fmt.Errorf("Failed to SSH: %s", r)
for _, c := range cmds {
cmd := util.PrependProxyEnv(c.cmd)
o.log.Infof("Checking... sudo %s", cmd)
r := o.exec(util.PrependProxyEnv(cmd), o.sudo())
if !r.isSuccess(c.expectedStatusCodes...) {
o.log.Errorf("Check sudo or proxy settings: %s", r)
return fmt.Errorf("Failed to sudo: %s", r)
}
o.log.Infof("Installed: %s", name)
}
o.log.Infof("Sudo... Pass")
return nil
}
func (o *redhat) checkRequiredPackagesInstalled() error {
if o.Distro.Family == "centos" {
majorVersion, err := o.Distro.MajorVersion()
if err != nil {
msg := fmt.Sprintf("Not implemented yet: %s, err: %s", o.Distro, err)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
// CentOS 5 ... yum-changelog
// CentOS 6, 7 ... yum-plugin-changelog
// RHEL 5 ... yum-security
// RHEL 6, 7 ... -
// Amazon ... -
func (o *redhat) checkDependencies() error {
var packName string
if o.Distro.Family == "amazon" {
return nil
}
var packName = "yum-plugin-changelog"
majorVersion, err := o.Distro.MajorVersion()
if err != nil {
msg := fmt.Sprintf("Not implemented yet: %s, err: %s", o.Distro, err)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
switch o.Distro.Family {
case "centos":
packName = "yum-plugin-changelog"
if majorVersion < 6 {
packName = "yum-changelog"
}
cmd := "rpm -q " + packName
if r := o.exec(cmd, noSudo); !r.isSuccess() {
msg := fmt.Sprintf("%s is not installed", packName)
o.log.Errorf(msg)
return fmt.Errorf(msg)
case "rhel":
if majorVersion < 6 {
packName = "yum-security"
} else {
// yum-plugin-security is installed by default on RHEL6, 7
return nil
}
default:
return fmt.Errorf("Not implemented yet: %s", o.Distro)
}
cmd := "rpm -q " + packName
if r := o.exec(cmd, noSudo); !r.isSuccess() {
msg := fmt.Sprintf("%s is not installed", packName)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
o.log.Infof("Dependencies... Pass")
return nil
}
@@ -551,7 +565,7 @@ func (o *redhat) getAllChangelog(packInfoList models.PackageInfoList) (stdout st
packageNames += fmt.Sprintf("%s ", packInfo.Name)
}
command := "echo N | "
command := ""
if 0 < len(config.Conf.HTTPProxy) {
command += util.ProxyEnv()
}
@@ -565,7 +579,7 @@ func (o *redhat) getAllChangelog(packInfoList models.PackageInfoList) (stdout st
}
// yum update --changelog doesn't have --color option.
command += fmt.Sprintf(" LANGUAGE=en_US.UTF-8 yum %s --changelog update ", yumopts) + packageNames
command += fmt.Sprintf(" LANGUAGE=en_US.UTF-8 yum --changelog --assumeno update %s ", yumopts) + packageNames
r := o.exec(command, sudo)
if !r.isSuccess(0, 1) {
@@ -647,7 +661,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (models.VulnInfos,
// get advisoryID(RHSA, ALAS) - CVE IDs
if o.Distro.Family == "rhel" && major == 5 {
cmd = "yum --color=never info-sec"
cmd = "yum --color=never info-security"
} else {
cmd = "yum --color=never --security updateinfo updates"
}

View File

@@ -18,11 +18,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package scan
import (
"bufio"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"github.com/future-architect/vuls/cache"
@@ -38,21 +36,16 @@ var servers, errServers []osTypeInterface
type osTypeInterface interface {
setServerInfo(config.ServerInfo)
getServerInfo() config.ServerInfo
setDistro(string, string)
getDistro() config.Distro
// checkDependencies checks if dependencies are installed on the target server.
checkDependencies() error
getLackDependencies() []string
checkIfSudoNoPasswd() error
detectPlatform()
getPlatform() models.Platform
checkRequiredPackagesInstalled() error
// checkDependencies checks if dependencies are installed on the target server.
checkDependencies() error
checkIfSudoNoPasswd() error
scanPackages() error
install() error
convertToModel() models.ScanResult
runningContainers() ([]config.Container, error)
@@ -113,7 +106,7 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) {
// PrintSSHableServerNames print SSH-able servernames
func PrintSSHableServerNames() {
util.Log.Info("SSH-able servers are below...")
util.Log.Info("Scannable servers are below...")
for _, s := range servers {
if s.getServerInfo().IsContainer() {
fmt.Printf("%s@%s ",
@@ -338,9 +331,18 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn
return oses
}
// CheckDependencies checks dependencies are installed on target servers.
func CheckDependencies() {
timeoutSec := 5 * 60
parallelExec(func(o osTypeInterface) error {
return o.checkDependencies()
}, timeoutSec)
return
}
// CheckIfSudoNoPasswd checks whether vuls can sudo with nopassword via SSH
func CheckIfSudoNoPasswd() {
timeoutSec := 15
timeoutSec := 5 * 60
parallelExec(func(o osTypeInterface) error {
return o.checkIfSudoNoPasswd()
}, timeoutSec)
@@ -380,117 +382,12 @@ func detectPlatforms() {
return
}
// Prepare installs requred packages to scan vulnerabilities.
func Prepare() error {
parallelExec(func(o osTypeInterface) error {
if err := o.checkDependencies(); err != nil {
return err
}
return nil
})
var targets, nonTargets []osTypeInterface
for _, s := range servers {
deps := s.getLackDependencies()
if len(deps) != 0 {
targets = append(targets, s)
} else {
nonTargets = append(nonTargets, s)
}
}
if len(targets) == 0 {
if 0 < len(nonTargets) {
util.Log.Info("The following servers were already installed dependencies")
for _, s := range nonTargets {
util.Log.Infof(" - %s", s.getServerInfo().GetServerName())
}
}
if 0 < len(errServers) {
util.Log.Error("Some errors occurred in the following servers")
for _, s := range errServers {
util.Log.Errorf(" - %s", s.getServerInfo().GetServerName())
}
} else {
util.Log.Info("Success")
}
return nil
}
util.Log.Info("The following servers need to install dependencies")
for _, s := range targets {
for _, d := range s.getLackDependencies() {
util.Log.Infof(" - %s on %s", d, s.getServerInfo().GetServerName())
}
}
if !config.Conf.AssumeYes {
util.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 err
}
switch strings.TrimSpace(text) {
case "", "N", "n":
return nil
case "y", "Y":
goto yes
default:
util.Log.Info("Please enter y or N")
}
}
}
yes:
servers = targets
parallelExec(func(o osTypeInterface) error {
if err := o.install(); err != nil {
return err
}
return nil
})
if 0 < len(servers) {
util.Log.Info("Successfully installed in the followring servers")
for _, s := range servers {
util.Log.Infof(" - %s", s.getServerInfo().GetServerName())
}
}
if 0 < len(nonTargets) {
util.Log.Info("The following servers were already installed dependencies")
for _, s := range nonTargets {
util.Log.Infof(" - %s", s.getServerInfo().GetServerName())
}
}
if 0 < len(errServers) {
util.Log.Error("Some errors occurred in the following servers")
for _, s := range errServers {
util.Log.Errorf(" - %s", s.getServerInfo().GetServerName())
}
}
if len(errServers) == 0 {
util.Log.Info("Success")
} else {
util.Log.Error("Failure")
}
return nil
}
// Scan scan
func Scan() error {
if len(servers) == 0 {
return fmt.Errorf("No server defined. Check the configuration")
}
util.Log.Info("Check required packages for scanning...")
checkRequiredPackagesInstalled()
if err := setupChangelogCache(); err != nil {
return err
}
@@ -530,14 +427,6 @@ func setupChangelogCache() error {
return nil
}
func checkRequiredPackagesInstalled() []error {
timeoutSec := 30 * 60
parallelExec(func(o osTypeInterface) error {
return o.checkRequiredPackagesInstalled()
}, timeoutSec)
return nil
}
func scanVulns(jsonDir string, scannedAt time.Time) error {
var results models.ScanResults
timeoutSec := 120 * 60

View File

@@ -30,14 +30,6 @@ func (o unknown) checkDependencies() error {
return nil
}
func (o *unknown) install() error {
return nil
}
func (o *unknown) checkRequiredPackagesInstalled() error {
return nil
}
func (o *unknown) scanPackages() error {
return nil
}