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

@@ -87,7 +87,7 @@ Hello Vulsチュートリアルでは手動でのセットアップ方法で説
1. go-cve-dictionaryをデプロイ
1. Vulsをデプロイ
1. 設定
1. Prepare
1. 設定ファイルと、スキャン対象サーバの設定のチェック
1. Scan
1. Reporting
1. TUI(Terminal-Based User Interface)で結果を参照する
@@ -216,15 +216,14 @@ port = "22"
user = "ec2-user"
keyPath = "/home/ec2-user/.ssh/id_rsa"
```
## Step7. Check config.toml and settings on the server before scanning
```
$ vuls configtest
```
## Step7. Setting up target servers for Vuls
```
$ vuls prepare
```
詳細は [Usage: Prepare](https://github.com/future-architect/vuls#usage-prepare) を参照
詳細は [Usage: configtest](#usage-configtest) を参照
## Step8. Start Scanning
@@ -327,7 +326,7 @@ $ vuls tui
# Architecture
## A. Scan via SSH Mode
## A. Scan via SSH Mode (Remote Scan Mode)
![Vuls-Architecture](img/vuls-architecture.png)
@@ -585,8 +584,6 @@ host = "172.31.4.82"
# Usage: Configtest
configtestサブコマンドは、config.tomlで定義されたサーバ/コンテナに対してSSH可能かどうかをチェックする。
```
$ vuls configtest --help
configtest:
@@ -595,6 +592,7 @@ configtest:
[-log-dir=/path/to/log]
[-ask-key-password]
[-ssh-external]
[-http-proxy=http://192.168.0.1:8080]
[-debug]
[SERVER]...
@@ -604,66 +602,70 @@ configtest:
/path/to/toml (default "/Users/kotakanbe/go/src/github.com/future-architect/vuls/config.toml")
-debug
debug mode
-http-proxy string
http://proxy-url:port (default: empty)
-log-dir string
/path/to/log (default "/var/log/vuls")
-ssh-external
Use external ssh command. Default: Use the Go native implementation
```
また、スキャン対象サーバに対してパスワードなしでSUDO可能な状態かもチェックする
configtestサブコマンドは以下をチェックする
- config.tomlで定義されたサーバ/コンテナに対してSSH可能かどうか
- スキャン対象のサーバ上に依存パッケーがインストールされているか
- /etc/sudoers
スキャン対象サーバ上の`/etc/sudoers`のサンプル
## Dependencies on Target Servers
- CentOS, RHEL, Amazon Linux
```
vuls ALL=(root) NOPASSWD: /usr/bin/yum
```
- Ubuntu, Debian, Raspbian
```
vuls ALL=(root) NOPASSWD: /usr/bin/apt-get
```
- Amazon Linux, FreeBSDはRoot権限なしでスキャン可能
----
# Usage: Prepare
Prepareサブコマンドは、Vuls内部で利用する以下のパッケージをスキャン対象サーバにインストールする。
スキャンするためには、下記のパッケージが必要なので、手動かまたはAnsibleなどのツールで事前にインストールする必要がある。
| Distribution| Release | Requirements |
|:------------|-------------------:|:-------------|
| Ubuntu | 12, 14, 16| - |
| Debian | 7, 8| aptitude |
| CentOS | 5| yum-changelog |
| CentOS | 6, 7| yum-plugin-changelog |
| Amazon | All | - |
| RHEL | 5, 6, 7 | - |
| Amazon | All | - |
| RHEL | 5 | yum-security |
| RHEL | 6, 7 | - |
| FreeBSD | 10 | - |
| Raspbian | Wheezy, Jessie | - |
## Check /etc/sudoers
```
$ vuls prepare -help
prepare:
prepare
[-config=/path/to/config.toml]
[-log-dir=/path/to/log]
[-ask-key-password]
[-debug]
[-ssh-external]
スキャン対象サーバに対してパスワードなしでSUDO可能な状態かもチェックする。
スキャン対象サーバ上の`/etc/sudoers`のサンプル
[SERVER]...
-ask-key-password
Ask ssh privatekey password before scanning
-config string
/path/to/toml (default "$PWD/config.toml")
-debug
debug mode
-log-dir string
/path/to/log (default "/var/log/vuls")
-ssh-external
Use external ssh command. Default: Use the Go native implementation
- CentOS
```
vuls ALL=(ALL) NOPASSWD:/usr/bin/yum --changelog --assumeno update *
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
- RHEL 5
```
vuls ALL=(ALL) NOPASSWD:/usr/bin/yum --color=never repolist, /usr/bin/yum --color=never list-security --security, /usr/bin/yum --color=never check-update, /usr/bin/yum --color=never info-security
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
- RHEL 6, 7
```
vuls ALL=(ALL) NOPASSWD:/usr/bin/yum --color=never repolist, /usr/bin/yum --color=never --security updateinfo list updates, /usr/bin/yum --color=never check-update, /usr/bin/yum --color=never --security updateinfo updates
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
- Debian
```
vuls ALL=(ALL) NOPASSWD: /usr/bin/apt-get update
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
- Ubuntu/Raspbian
```
vuls ALL=(ALL) NOPASSWD: /usr/bin/apt-get update
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
- Amazon Linux, FreeBSDは今のところRoot権限なしでスキャン可能
----

109
README.md
View File

@@ -93,7 +93,7 @@ This can be done in the following steps.
1. Deploy go-cve-dictionary
1. Deploy Vuls
1. Configuration
1. Prepare
1. Check config.toml and settings on the server before scanning
1. Scan
1. Reporting
1. TUI(Terminal-Based User Interface)
@@ -211,15 +211,15 @@ port = "22"
user = "ec2-user"
keyPath = "/home/ec2-user/.ssh/id_rsa"
```
## Step7. Check config.toml and settings on the server before scanning
```
$ vuls configtest
```
## Step7. Setting up target servers for Vuls
```
$ vuls prepare
```
see [Usage: Prepare](https://github.com/future-architect/vuls#usage-prepare)
see [Usage: configtest](#usage-configtest)
## Step8. Start Scanning
@@ -325,7 +325,7 @@ see https://github.com/future-architect/vuls/tree/master/setup/docker
# Architecture
## A. Scan via SSH Mode
## A. Scan via SSH Mode (Remote Scan Mode)
![Vuls-Architecture](img/vuls-architecture.png)
@@ -589,7 +589,6 @@ You can customize your configuration using this template.
# Usage: Configtest
Configtest subcommand check if vuls is able to connect via ssh to servers/containers defined in the config.toml.
```
$ vuls configtest --help
configtest:
@@ -607,72 +606,72 @@ configtest:
/path/to/toml (default "/Users/kotakanbe/go/src/github.com/future-architect/vuls/config.toml")
-debug
debug mode
-http-proxy string
http://proxy-url:port (default: empty)
-log-dir string
/path/to/log (default "/var/log/vuls")
-ssh-external
Use external ssh command. Default: Use the Go native implementation
```
And also, configtest subcommand checks sudo settings on target servers whether Vuls is able to SUDO with nopassword via SSH.
The configtest subcommand checks the following
- Whether vuls is able to connect via ssh to servers/containers defined in the config.toml
- Whether Dependent package is installed on the scan target server
- Check /etc/sudoers
Example of /etc/sudoers on target servers
- CentOS and RHEL
```
vuls ALL=(root) NOPASSWD: /usr/bin/yum
```
- Ubuntu, Debian and Raspbian
```
vuls ALL=(root) NOPASSWD: /usr/bin/apt-get
```
- It is possible to scan without root privilege for Amazon Linux, FreeBSD.
## Dependencies on Target Servers
----
# Usage: Prepare
Prepare subcommand installs required packages on each server.
In order to scan, the following dependencies are required, so you need to install them manually or with tools such as Ansible.
| Distribution| Release | Requirements |
|:------------|-------------------:|:-------------|
| Ubuntu | 12, 14, 16| - |
| Debian | 7, 8| aptitude |
| CentOS | 5| yum-changelog |
| CentOS | 6, 7| yum-plugin-changelog |
| Amazon | All | - |
| RHEL | 5, 6, 7 | - |
| Amazon | All | - |
| RHEL | 5 | yum-security |
| RHEL | 6, 7 | - |
| FreeBSD | 10 | - |
| Raspbian | Wheezy, Jessie | - |
## Check /etc/sudoers
```
$ vuls prepare -help
prepare:
prepare
[-config=/path/to/config.toml]
[-log-dir=/path/to/log]
[-ask-key-password]
[-assume-yes]
[-debug]
[-ssh-external]
The configtest subcommand checks sudo settings on target servers whether Vuls is able to SUDO with nopassword via SSH.
[SERVER]...
-ask-key-password
Ask ssh privatekey password before scanning
-ask-sudo-password
[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on target servers and use SSH key-based authentication
-assume-yes
Assume any dependencies should be installed
-config string
/path/to/toml (default "$PWD/config.toml")
-debug
debug mode
-log-dir string
/path/to/log (default "/var/log/vuls")
-ssh-external
Use external ssh command. Default: Use the Go native implementation
Example of /etc/sudoers on target servers
- CentOS
```
vuls ALL=(ALL) NOPASSWD:/usr/bin/yum --changelog --assumeno update *
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
- RHEL 5
```
vuls ALL=(ALL) NOPASSWD:/usr/bin/yum --color=never repolist, /usr/bin/yum --color=never list-security --security, /usr/bin/yum --color=never check-update, /usr/bin/yum --color=never info-security
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
- RHEL 6, 7
```
vuls ALL=(ALL) NOPASSWD:/usr/bin/yum --color=never repolist, /usr/bin/yum --color=never --security updateinfo list updates, /usr/bin/yum --color=never check-update, /usr/bin/yum --color=never --security updateinfo updates
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
- Debian
```
vuls ALL=(ALL) NOPASSWD: /usr/bin/apt-get update
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
- Ubuntu/Raspbian
```
vuls ALL=(ALL) NOPASSWD: /usr/bin/apt-get update
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
- On Amazon Linux, FreeBSD, it is possible to scan without root privilege for now.
----
# Usage: Scan

View File

@@ -36,6 +36,7 @@ type ConfigtestCmd struct {
logDir string
askKeyPassword bool
sshExternal bool
httpProxy string
debug bool
}
@@ -54,6 +55,7 @@ func (*ConfigtestCmd) Usage() string {
[-log-dir=/path/to/log]
[-ask-key-password]
[-ssh-external]
[-http-proxy=http://192.168.0.1:8080]
[-debug]
[SERVER]...
@@ -78,6 +80,13 @@ func (p *ConfigtestCmd) SetFlags(f *flag.FlagSet) {
"Ask ssh privatekey password before scanning",
)
f.StringVar(
&p.httpProxy,
"http-proxy",
"",
"http://proxy-url:port (default: empty)",
)
f.BoolVar(
&p.sshExternal,
"ssh-external",
@@ -108,6 +117,7 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
return subcommands.ExitUsageError
}
c.Conf.SSHExternal = p.sshExternal
c.Conf.HTTPProxy = p.httpProxy
var servernames []string
if 0 < len(f.Args()) {
@@ -144,7 +154,10 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
return subcommands.ExitFailure
}
util.Log.Info("Checking sudo configuration...")
util.Log.Info("Checking dependendies...")
scan.CheckDependencies()
util.Log.Info("Checking sudo settings...")
scan.CheckIfSudoNoPasswd()
scan.PrintSSHableServerNames()

View File

@@ -1,185 +0,0 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package commands
import (
"context"
"flag"
"os"
"path/filepath"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/scan"
"github.com/future-architect/vuls/util"
"github.com/google/subcommands"
)
// PrepareCmd is Subcommand of host discovery mode
type PrepareCmd struct {
debug bool
configPath string
logDir string
askSudoPassword bool
askKeyPassword bool
sshExternal bool
assumeYes bool
}
// Name return subcommand name
func (*PrepareCmd) Name() string { return "prepare" }
// Synopsis return synopsis
func (*PrepareCmd) Synopsis() string {
return `Install required packages to scan.
CentOS: yum-plugin-security, yum-plugin-changelog
Amazon: None
RHEL: None
Ubuntu: None
Debian: aptitude
`
}
// Usage return usage
func (*PrepareCmd) Usage() string {
return `prepare:
prepare
[-config=/path/to/config.toml]
[-log-dir=/path/to/log]
[-ask-key-password]
[-assume-yes]
[-debug]
[-ssh-external]
[SERVER]...
`
}
// SetFlags set flag
func (p *PrepareCmd) SetFlags(f *flag.FlagSet) {
f.BoolVar(&p.debug, "debug", false, "debug mode")
wd, _ := os.Getwd()
defaultConfPath := filepath.Join(wd, "config.toml")
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
defaultLogDir := util.GetDefaultLogDir()
f.StringVar(&p.logDir, "log-dir", defaultLogDir, "/path/to/log")
f.BoolVar(
&p.askKeyPassword,
"ask-key-password",
false,
"Ask ssh privatekey password before scanning",
)
f.BoolVar(
&p.askSudoPassword,
"ask-sudo-password",
false,
"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on target servers and use SSH key-based authentication",
)
f.BoolVar(
&p.sshExternal,
"ssh-external",
false,
"Use external ssh command. Default: Use the Go native implementation")
f.BoolVar(
&p.assumeYes,
"assume-yes",
false,
"Assume any dependencies should be installed")
}
// Execute execute
func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
c.Conf.LogDir = p.logDir
c.Conf.Debug = p.debug
util.Log = util.NewCustomLogger(c.ServerInfo{})
var keyPass string
var err error
if p.askKeyPassword {
prompt := "SSH key password: "
if keyPass, err = getPasswd(prompt); err != nil {
util.Log.Error(err)
return subcommands.ExitFailure
}
}
if p.askSudoPassword {
util.Log.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on target servers and use SSH key-based authentication")
return subcommands.ExitFailure
}
err = c.Load(p.configPath, keyPass)
if err != nil {
util.Log.Errorf("Error loading %s, %s", p.configPath, err)
return subcommands.ExitUsageError
}
util.Log.Infof("Start Preparing (config: %s)", p.configPath)
target := make(map[string]c.ServerInfo)
for _, arg := range f.Args() {
found := false
for servername, info := range c.Conf.Servers {
if servername == arg {
target[servername] = info
found = true
break
}
}
if !found {
util.Log.Errorf("%s is not in config", arg)
return subcommands.ExitUsageError
}
}
if 0 < len(f.Args()) {
c.Conf.Servers = target
}
c.Conf.SSHExternal = p.sshExternal
c.Conf.AssumeYes = p.assumeYes
util.Log.Info("Validating config...")
if !c.Conf.ValidateOnPrepare() {
return subcommands.ExitUsageError
}
util.Log.Info("Detecting OS... ")
if err := scan.InitServers(); err != nil {
util.Log.Errorf("Failed to init servers: %s", err)
return subcommands.ExitFailure
}
util.Log.Info("Checking sudo configuration... ")
scan.CheckIfSudoNoPasswd()
if err := scan.Prepare(); err != nil {
util.Log.Errorf("Failed to prepare: %s", err)
return subcommands.ExitFailure
}
return subcommands.ExitSuccess
}

View File

@@ -216,9 +216,6 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
return subcommands.ExitFailure
}
util.Log.Info("Checking sudo configuration... ")
scan.CheckIfSudoNoPasswd()
util.Log.Info("Detecting Platforms... ")
scan.DetectPlatforms()

View File

@@ -41,7 +41,6 @@ func main() {
subcommands.Register(&commands.DiscoverCmd{}, "discover")
subcommands.Register(&commands.TuiCmd{}, "tui")
subcommands.Register(&commands.ScanCmd{}, "scan")
subcommands.Register(&commands.PrepareCmd{}, "prepare")
subcommands.Register(&commands.HistoryCmd{}, "history")
subcommands.Register(&commands.ReportCmd{}, "report")
subcommands.Register(&commands.ConfigtestCmd{}, "configtest")

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
}

View File

@@ -95,7 +95,7 @@ func URLPathParamJoin(baseURL string, paths []string, params map[string]string)
// ProxyEnv returns shell environment variables to set proxy
func ProxyEnv() string {
httpProxyEnv := "env"
httpProxyEnv := ""
keys := []string{
"http_proxy",
"https_proxy",

View File

@@ -105,14 +105,14 @@ func TestPrependHTTPProxyEnv(t *testing.T) {
"http://proxy.co.jp:8080",
"yum check-update",
},
`env http_proxy="http://proxy.co.jp:8080" https_proxy="http://proxy.co.jp:8080" HTTP_PROXY="http://proxy.co.jp:8080" HTTPS_PROXY="http://proxy.co.jp:8080" yum check-update`,
` http_proxy="http://proxy.co.jp:8080" https_proxy="http://proxy.co.jp:8080" HTTP_PROXY="http://proxy.co.jp:8080" HTTPS_PROXY="http://proxy.co.jp:8080" yum check-update`,
},
{
[]string{
"http://proxy.co.jp:8080",
"",
},
`env http_proxy="http://proxy.co.jp:8080" https_proxy="http://proxy.co.jp:8080" HTTP_PROXY="http://proxy.co.jp:8080" HTTPS_PROXY="http://proxy.co.jp:8080" `,
` http_proxy="http://proxy.co.jp:8080" https_proxy="http://proxy.co.jp:8080" HTTP_PROXY="http://proxy.co.jp:8080" HTTPS_PROXY="http://proxy.co.jp:8080" `,
},
{
[]string{