Deprecate prepare subcommand to minimize the root authority #375
This commit is contained in:
104
README.ja.md
104
README.ja.md
@@ -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)
|
||||
|
||||

|
||||
|
||||
@@ -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
109
README.md
@@ -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)
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
|
||||
1
main.go
1
main.go
@@ -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")
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
126
scan/redhat.go
126
scan/redhat.go
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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{
|
||||
|
||||
Reference in New Issue
Block a user