@@ -23,7 +23,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/google/subcommands"
|
||||
|
||||
c "github.com/future-architect/vuls/config"
|
||||
@@ -88,26 +87,27 @@ func (p *ConfigtestCmd) SetFlags(f *flag.FlagSet) {
|
||||
|
||||
// Execute execute
|
||||
func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
// Setup Logger
|
||||
c.Conf.Debug = p.debug
|
||||
c.Conf.LogDir = p.logDir
|
||||
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 {
|
||||
logrus.Error(err)
|
||||
util.Log.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
c.Conf.Debug = p.debug
|
||||
c.Conf.SSHExternal = p.sshExternal
|
||||
c.Conf.LogDir = p.logDir
|
||||
|
||||
err = c.Load(p.configPath, keyPass)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
util.Log.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
c.Conf.SSHExternal = p.sshExternal
|
||||
|
||||
var servernames []string
|
||||
if 0 < len(f.Args()) {
|
||||
@@ -125,7 +125,7 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
logrus.Errorf("%s is not in config", arg)
|
||||
util.Log.Errorf("%s is not in config", arg)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
}
|
||||
@@ -133,25 +133,20 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
|
||||
c.Conf.Servers = target
|
||||
}
|
||||
|
||||
// logger
|
||||
Log := util.NewCustomLogger(c.ServerInfo{})
|
||||
|
||||
Log.Info("Validating Config...")
|
||||
util.Log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnConfigtest() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
Log.Info("Detecting Server/Container OS... ")
|
||||
if err := scan.InitServers(Log); err != nil {
|
||||
Log.Errorf("Failed to init servers: %s", err)
|
||||
util.Log.Info("Detecting Server/Container OS... ")
|
||||
if err := scan.InitServers(); err != nil {
|
||||
util.Log.Errorf("Failed to init servers: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
Log.Info("Checking sudo configuration... ")
|
||||
if err := scan.CheckIfSudoNoPasswd(Log); err != nil {
|
||||
Log.Errorf("Failed to sudo with nopassword via SSH. Define NOPASSWD in /etc/sudoers on target servers. err: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
util.Log.Info("Checking sudo configuration...")
|
||||
scan.CheckIfSudoNoPasswd()
|
||||
|
||||
scan.PrintSSHableServerNames()
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/scan"
|
||||
"github.com/future-architect/vuls/util"
|
||||
@@ -116,27 +115,31 @@ func (p *PrepareCmd) SetFlags(f *flag.FlagSet) {
|
||||
|
||||
// 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 {
|
||||
logrus.Error(err)
|
||||
util.Log.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
if p.askSudoPassword {
|
||||
logrus.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on target servers and use SSH key-based authentication")
|
||||
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 {
|
||||
logrus.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
util.Log.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
logrus.Infof("Start Preparing (config: %s)", p.configPath)
|
||||
util.Log.Infof("Start Preparing (config: %s)", p.configPath)
|
||||
target := make(map[string]c.ServerInfo)
|
||||
for _, arg := range f.Args() {
|
||||
found := false
|
||||
@@ -148,7 +151,7 @@ func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
logrus.Errorf("%s is not in config", arg)
|
||||
util.Log.Errorf("%s is not in config", arg)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
}
|
||||
@@ -156,34 +159,25 @@ func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{
|
||||
c.Conf.Servers = target
|
||||
}
|
||||
|
||||
c.Conf.Debug = p.debug
|
||||
c.Conf.SSHExternal = p.sshExternal
|
||||
c.Conf.AssumeYes = p.assumeYes
|
||||
c.Conf.LogDir = p.logDir
|
||||
|
||||
logrus.Info("Validating Config...")
|
||||
util.Log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnPrepare() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
// Set up custom logger
|
||||
logger := util.NewCustomLogger(c.ServerInfo{})
|
||||
|
||||
logger.Info("Detecting OS... ")
|
||||
if err := scan.InitServers(logger); err != nil {
|
||||
logger.Errorf("Failed to init servers: %s", err)
|
||||
util.Log.Info("Detecting OS... ")
|
||||
if err := scan.InitServers(); err != nil {
|
||||
util.Log.Errorf("Failed to init servers: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
logger.Info("Checking sudo configuration... ")
|
||||
if err := scan.CheckIfSudoNoPasswd(logger); err != nil {
|
||||
logger.Errorf("Failed to sudo with nopassword via SSH. Define NOPASSWD in /etc/sudoers on target servers")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
util.Log.Info("Checking sudo configuration... ")
|
||||
scan.CheckIfSudoNoPasswd()
|
||||
|
||||
if errs := scan.Prepare(); 0 < len(errs) {
|
||||
for _, e := range errs {
|
||||
logger.Errorf("Failed to prepare: %s", e)
|
||||
}
|
||||
if err := scan.Prepare(); err != nil {
|
||||
util.Log.Errorf("Failed to prepare: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
|
||||
@@ -249,10 +249,10 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
|
||||
c.Conf.Debug = p.debug
|
||||
c.Conf.DebugSQL = p.debugSQL
|
||||
c.Conf.LogDir = p.logDir
|
||||
Log := util.NewCustomLogger(c.ServerInfo{})
|
||||
util.Log = util.NewCustomLogger(c.ServerInfo{})
|
||||
|
||||
if err := c.Load(p.configPath, ""); err != nil {
|
||||
Log.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
util.Log.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
|
||||
c.Conf.Pipe = p.pipe
|
||||
jsonDir, err := jsonDir(f.Args())
|
||||
if err != nil {
|
||||
Log.Errorf("Failed to read from JSON: %s", err)
|
||||
util.Log.Errorf("Failed to read from JSON: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
|
||||
c.Conf.AwsProfile = p.awsProfile
|
||||
c.Conf.S3Bucket = p.awsS3Bucket
|
||||
if err := report.CheckIfBucketExists(); err != nil {
|
||||
Log.Errorf("Check if there is a bucket beforehand: %s, err: %s", c.Conf.S3Bucket, err)
|
||||
util.Log.Errorf("Check if there is a bucket beforehand: %s, err: %s", c.Conf.S3Bucket, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
reports = append(reports, report.S3Writer{})
|
||||
@@ -323,11 +323,11 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
|
||||
|
||||
c.Conf.AzureContainer = p.azureContainer
|
||||
if len(c.Conf.AzureContainer) == 0 {
|
||||
Log.Error("Azure storage container name is requied with --azure-container option")
|
||||
util.Log.Error("Azure storage container name is requied with --azure-container option")
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
if err := report.CheckIfAzureContainerExists(); err != nil {
|
||||
Log.Errorf("Check if there is a container beforehand: %s, err: %s", c.Conf.AzureContainer, err)
|
||||
util.Log.Errorf("Check if there is a container beforehand: %s, err: %s", c.Conf.AzureContainer, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
reports = append(reports, report.AzureBlobWriter{})
|
||||
@@ -338,20 +338,20 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
|
||||
c.Conf.FormatShortText = true
|
||||
}
|
||||
|
||||
Log.Info("Validating Config...")
|
||||
util.Log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnReport() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
if ok, err := cveapi.CveClient.CheckHealth(); !ok {
|
||||
Log.Errorf("CVE HTTP server is not running. err: %s", err)
|
||||
Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with --cvedb-path option")
|
||||
util.Log.Errorf("CVE HTTP server is not running. err: %s", err)
|
||||
util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with --cvedb-path option")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
if c.Conf.CveDictionaryURL != "" {
|
||||
Log.Infof("cve-dictionary: %s", c.Conf.CveDictionaryURL)
|
||||
util.Log.Infof("cve-dictionary: %s", c.Conf.CveDictionaryURL)
|
||||
} else {
|
||||
if c.Conf.CveDBType == "sqlite3" {
|
||||
Log.Infof("cve-dictionary: %s", c.Conf.CveDBPath)
|
||||
util.Log.Infof("cve-dictionary: %s", c.Conf.CveDBPath)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,10 +360,10 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
|
||||
var results []models.ScanResult
|
||||
for _, r := range history.ScanResults {
|
||||
if p.refreshCve || needToRefreshCve(r) {
|
||||
Log.Debugf("need to refresh")
|
||||
util.Log.Debugf("need to refresh")
|
||||
if c.Conf.CveDBType == "sqlite3" && c.Conf.CveDictionaryURL == "" {
|
||||
if _, err := os.Stat(c.Conf.CveDBPath); os.IsNotExist(err) {
|
||||
Log.Errorf("SQLite3 DB(CVE-Dictionary) is not exist: %s",
|
||||
util.Log.Errorf("SQLite3 DB(CVE-Dictionary) is not exist: %s",
|
||||
c.Conf.CveDBPath)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
@@ -371,18 +371,18 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
|
||||
|
||||
filled, err := fillCveInfoFromCveDB(r)
|
||||
if err != nil {
|
||||
Log.Errorf("Failed to fill CVE information: %s", err)
|
||||
util.Log.Errorf("Failed to fill CVE information: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
filled.Lang = c.Conf.Lang
|
||||
|
||||
if err := overwriteJSONFile(jsonDir, filled); err != nil {
|
||||
Log.Errorf("Failed to write JSON: %s", err)
|
||||
util.Log.Errorf("Failed to write JSON: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
results = append(results, filled)
|
||||
} else {
|
||||
Log.Debugf("no need to refresh")
|
||||
util.Log.Debugf("no need to refresh")
|
||||
results = append(results, r)
|
||||
}
|
||||
}
|
||||
@@ -393,7 +393,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
|
||||
}
|
||||
for _, w := range reports {
|
||||
if err := w.Write(res...); err != nil {
|
||||
Log.Errorf("Failed to report: %s", err)
|
||||
util.Log.Errorf("Failed to report: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/scan"
|
||||
"github.com/future-architect/vuls/util"
|
||||
@@ -138,25 +137,30 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
|
||||
|
||||
// Execute execute
|
||||
func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
|
||||
// Setup Logger
|
||||
c.Conf.Debug = p.debug
|
||||
c.Conf.LogDir = p.logDir
|
||||
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 {
|
||||
logrus.Error(err)
|
||||
util.Log.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
c.Conf.Debug = p.debug
|
||||
err = c.Load(p.configPath, keyPass)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
util.Log.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
logrus.Info("Start scanning")
|
||||
logrus.Infof("config: %s", p.configPath)
|
||||
util.Log.Info("Start scanning")
|
||||
util.Log.Infof("config: %s", p.configPath)
|
||||
|
||||
c.Conf.Pipe = p.pipe
|
||||
var servernames []string
|
||||
@@ -165,7 +169,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
} else if c.Conf.Pipe {
|
||||
bytes, err := ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to read stdin: %s", err)
|
||||
util.Log.Errorf("Failed to read stdin: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
fields := strings.Fields(string(bytes))
|
||||
@@ -185,18 +189,14 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
logrus.Errorf("%s is not in config", arg)
|
||||
util.Log.Errorf("%s is not in config", arg)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
}
|
||||
if 0 < len(servernames) {
|
||||
c.Conf.Servers = target
|
||||
}
|
||||
logrus.Debugf("%s", pp.Sprintf("%v", target))
|
||||
|
||||
// logger
|
||||
c.Conf.LogDir = p.logDir
|
||||
Log := util.NewCustomLogger(c.ServerInfo{})
|
||||
util.Log.Debugf("%s", pp.Sprintf("%v", target))
|
||||
|
||||
c.Conf.ResultsDir = p.resultsDir
|
||||
c.Conf.CacheDBPath = p.cacheDBPath
|
||||
@@ -205,31 +205,26 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
c.Conf.ContainersOnly = p.containersOnly
|
||||
c.Conf.SkipBroken = p.skipBroken
|
||||
|
||||
Log.Info("Validating Config...")
|
||||
util.Log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnScan() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
Log.Info("Detecting Server/Container OS... ")
|
||||
if err := scan.InitServers(Log); err != nil {
|
||||
Log.Errorf("Failed to init servers: %s", err)
|
||||
util.Log.Info("Detecting Server/Container OS... ")
|
||||
if err := scan.InitServers(); err != nil {
|
||||
util.Log.Errorf("Failed to init servers: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
Log.Info("Checking sudo configuration... ")
|
||||
if err := scan.CheckIfSudoNoPasswd(Log); err != nil {
|
||||
Log.Errorf("Failed to sudo with nopassword via SSH. Define NOPASSWD in /etc/sudoers on target servers")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
util.Log.Info("Checking sudo configuration... ")
|
||||
scan.CheckIfSudoNoPasswd()
|
||||
|
||||
Log.Info("Detecting Platforms... ")
|
||||
scan.DetectPlatforms(Log)
|
||||
util.Log.Info("Detecting Platforms... ")
|
||||
scan.DetectPlatforms()
|
||||
|
||||
Log.Info("Scanning vulnerabilities... ")
|
||||
if errs := scan.Scan(); 0 < len(errs) {
|
||||
for _, e := range errs {
|
||||
Log.Errorf("Failed to scan. err: %s", e)
|
||||
}
|
||||
util.Log.Info("Scanning vulnerabilities... ")
|
||||
if err := scan.Scan(); err != nil {
|
||||
util.Log.Errorf("Failed to scan. err: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
fmt.Printf("\n\n\n")
|
||||
|
||||
@@ -115,7 +115,7 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
|
||||
c.Conf.CveDBPath = p.cvedbpath
|
||||
c.Conf.CveDictionaryURL = p.cveDictionaryURL
|
||||
|
||||
log.Info("Validating Config...")
|
||||
log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnTui() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
2
glide.lock
generated
2
glide.lock
generated
@@ -1,5 +1,5 @@
|
||||
hash: c3167d83e68562cd7ef73f138ce60cb9e60b72b50394e8615388d1f3ba9fbef2
|
||||
updated: 2017-01-02T09:37:09.437363123+09:00
|
||||
updated: 2017-02-08T11:59:59.893522816+09:00
|
||||
imports:
|
||||
- name: github.com/asaskevich/govalidator
|
||||
version: 7b3beb6df3c42abd3509abfc3bcacc0fbfb7c877
|
||||
|
||||
@@ -73,6 +73,7 @@ type ScanResult struct {
|
||||
|
||||
Packages PackageInfoList
|
||||
|
||||
Errors []string
|
||||
Optional [][]interface{}
|
||||
}
|
||||
|
||||
@@ -164,8 +165,7 @@ func (r ScanResult) ReportKeyName() (name string) {
|
||||
func (r ScanResult) ServerInfo() string {
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
return fmt.Sprintf("%s (%s%s)",
|
||||
r.ServerName, r.Family, r.Release,
|
||||
)
|
||||
r.ServerName, r.Family, r.Release)
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"%s / %s (%s%s) on %s",
|
||||
|
||||
@@ -42,11 +42,14 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
}
|
||||
|
||||
for _, r := range rs {
|
||||
subject := fmt.Sprintf("%s%s %s",
|
||||
conf.EMail.SubjectPrefix,
|
||||
r.ServerInfo(),
|
||||
r.CveSummary(),
|
||||
)
|
||||
var subject string
|
||||
if len(r.Errors) != 0 {
|
||||
subject = fmt.Sprintf("%s%s An error occurred while scanning",
|
||||
conf.EMail.SubjectPrefix, r.ServerInfo())
|
||||
} else {
|
||||
subject = fmt.Sprintf("%s%s %s",
|
||||
conf.EMail.SubjectPrefix, r.ServerInfo(), r.CveSummary())
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["From"] = conf.EMail.From
|
||||
|
||||
104
report/slack.go
104
report/slack.go
@@ -20,6 +20,7 @@ package report
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -59,48 +60,90 @@ type SlackWriter struct{}
|
||||
func (w SlackWriter) Write(rs ...models.ScanResult) error {
|
||||
conf := config.Conf.Slack
|
||||
channel := conf.Channel
|
||||
count := 0
|
||||
retryMax := 10
|
||||
|
||||
for _, r := range rs {
|
||||
if channel == "${servername}" {
|
||||
channel = fmt.Sprintf("#%s", r.ServerName)
|
||||
}
|
||||
|
||||
msg := message{
|
||||
Text: msgText(r),
|
||||
Username: conf.AuthUser,
|
||||
IconEmoji: conf.IconEmoji,
|
||||
Channel: channel,
|
||||
Attachments: toSlackAttachments(r),
|
||||
}
|
||||
|
||||
bytes, _ := json.Marshal(msg)
|
||||
jsonBody := string(bytes)
|
||||
|
||||
f := func() (err error) {
|
||||
resp, body, errs := gorequest.New().Proxy(config.Conf.HTTPProxy).Post(conf.HookURL).Send(string(jsonBody)).End()
|
||||
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
|
||||
count++
|
||||
if count == retryMax {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf(
|
||||
"HTTP POST error: %v, url: %s, resp: %v, body: %s",
|
||||
errs, conf.HookURL, resp, body)
|
||||
if 0 < len(r.Errors) {
|
||||
serverInfo := fmt.Sprintf("*%s*", r.ServerInfo())
|
||||
notifyUsers := getNotifyUsers(config.Conf.Slack.NotifyUsers)
|
||||
txt := fmt.Sprintf("%s\n%s\nError: %s", notifyUsers, serverInfo, r.Errors)
|
||||
msg := message{
|
||||
Text: txt,
|
||||
Username: conf.AuthUser,
|
||||
IconEmoji: conf.IconEmoji,
|
||||
Channel: channel,
|
||||
}
|
||||
return nil
|
||||
if err := send(msg); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
notify := func(err error, t time.Duration) {
|
||||
log.Warnf("Error %s", err)
|
||||
log.Warn("Retrying in ", t)
|
||||
|
||||
// A maximum of 100 attachments are allowed on a message.
|
||||
// Split into chunks with 100 elements
|
||||
// https://api.slack.com/methods/chat.postMessage
|
||||
maxAttachments := 100
|
||||
m := map[int][]*attachment{}
|
||||
for i, a := range toSlackAttachments(r) {
|
||||
m[i/maxAttachments] = append(m[i/maxAttachments], a)
|
||||
}
|
||||
boff := backoff.NewExponentialBackOff()
|
||||
if err := backoff.RetryNotify(f, boff, notify); err != nil {
|
||||
return fmt.Errorf("HTTP error: %s", err)
|
||||
chunkKeys := []int{}
|
||||
for k := range m {
|
||||
chunkKeys = append(chunkKeys, k)
|
||||
}
|
||||
sort.Ints(chunkKeys)
|
||||
|
||||
for i, k := range chunkKeys {
|
||||
txt := ""
|
||||
if i == 0 {
|
||||
txt = msgText(r)
|
||||
}
|
||||
msg := message{
|
||||
Text: txt,
|
||||
Username: conf.AuthUser,
|
||||
IconEmoji: conf.IconEmoji,
|
||||
Channel: channel,
|
||||
Attachments: m[k],
|
||||
}
|
||||
if err := send(msg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func send(msg message) error {
|
||||
conf := config.Conf.Slack
|
||||
count, retryMax := 0, 10
|
||||
|
||||
bytes, _ := json.Marshal(msg)
|
||||
jsonBody := string(bytes)
|
||||
|
||||
f := func() (err error) {
|
||||
resp, body, errs := gorequest.New().Proxy(config.Conf.HTTPProxy).Post(conf.HookURL).Send(string(jsonBody)).End()
|
||||
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
|
||||
count++
|
||||
if count == retryMax {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf(
|
||||
"HTTP POST error: %v, url: %s, resp: %v, body: %s",
|
||||
errs, conf.HookURL, resp, body)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
notify := func(err error, t time.Duration) {
|
||||
log.Warnf("Error %s", err)
|
||||
log.Warn("Retrying in ", t)
|
||||
}
|
||||
boff := backoff.NewExponentialBackOff()
|
||||
if err := backoff.RetryNotify(f, boff, notify); err != nil {
|
||||
return fmt.Errorf("HTTP error: %s", err)
|
||||
}
|
||||
if count == retryMax {
|
||||
return fmt.Errorf("Retry count exceeded")
|
||||
}
|
||||
@@ -112,7 +155,6 @@ func msgText(r models.ScanResult) string {
|
||||
if 0 < len(r.KnownCves) || 0 < len(r.UnknownCves) {
|
||||
notifyUsers = getNotifyUsers(config.Conf.Slack.NotifyUsers)
|
||||
}
|
||||
|
||||
serverInfo := fmt.Sprintf("*%s*", r.ServerInfo())
|
||||
return fmt.Sprintf("%s\n%s\n>%s", notifyUsers, serverInfo, r.CveSummary())
|
||||
}
|
||||
|
||||
@@ -545,6 +545,10 @@ func summaryLines() string {
|
||||
stable.MaxColWidth = 1000
|
||||
stable.Wrap = false
|
||||
|
||||
if len(currentScanResult.Errors) != 0 {
|
||||
return "Error: Scan with --debug to view the details"
|
||||
}
|
||||
|
||||
indexFormat := ""
|
||||
if len(currentScanResult.AllCves()) < 10 {
|
||||
indexFormat = "[%1d]"
|
||||
@@ -650,6 +654,10 @@ type dataForTmpl struct {
|
||||
}
|
||||
|
||||
func detailLines() (string, error) {
|
||||
if len(currentScanResult.Errors) != 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if len(currentScanResult.AllCves()) == 0 {
|
||||
return "No vulnerable packages", nil
|
||||
}
|
||||
@@ -730,8 +738,6 @@ func detailLines() (string, error) {
|
||||
return string(buf.Bytes()), nil
|
||||
}
|
||||
|
||||
// * {{.Name}}-{{.Version}}-{{.Release}}
|
||||
|
||||
func detailTemplate() string {
|
||||
return `
|
||||
{{.CveID}}
|
||||
|
||||
@@ -34,11 +34,21 @@ func toScanSummary(rs ...models.ScanResult) string {
|
||||
table.MaxColWidth = maxColWidth
|
||||
table.Wrap = true
|
||||
for _, r := range rs {
|
||||
cols := []interface{}{
|
||||
r.FormatServerName(),
|
||||
fmt.Sprintf("%s%s", r.Family, r.Release),
|
||||
fmt.Sprintf("%d CVEs", len(r.ScannedCves)),
|
||||
r.Packages.ToUpdatablePacksSummary(),
|
||||
var cols []interface{}
|
||||
if len(r.Errors) == 0 {
|
||||
cols = []interface{}{
|
||||
r.FormatServerName(),
|
||||
fmt.Sprintf("%s%s", r.Family, r.Release),
|
||||
fmt.Sprintf("%d CVEs", len(r.ScannedCves)),
|
||||
r.Packages.ToUpdatablePacksSummary(),
|
||||
}
|
||||
} else {
|
||||
cols = []interface{}{
|
||||
r.FormatServerName(),
|
||||
"Error",
|
||||
"",
|
||||
"Run with --debug to view the details",
|
||||
}
|
||||
}
|
||||
table.AddRow(cols...)
|
||||
}
|
||||
@@ -50,10 +60,19 @@ func toOneLineSummary(rs ...models.ScanResult) string {
|
||||
table.MaxColWidth = maxColWidth
|
||||
table.Wrap = true
|
||||
for _, r := range rs {
|
||||
cols := []interface{}{
|
||||
r.FormatServerName(),
|
||||
r.CveSummary(),
|
||||
r.Packages.ToUpdatablePacksSummary(),
|
||||
var cols []interface{}
|
||||
if len(r.Errors) == 0 {
|
||||
cols = []interface{}{
|
||||
r.FormatServerName(),
|
||||
r.CveSummary(),
|
||||
r.Packages.ToUpdatablePacksSummary(),
|
||||
}
|
||||
} else {
|
||||
cols = []interface{}{
|
||||
r.FormatServerName(),
|
||||
"Error: Scan with --debug to view the details",
|
||||
"",
|
||||
}
|
||||
}
|
||||
table.AddRow(cols...)
|
||||
}
|
||||
@@ -81,6 +100,12 @@ func toShortPlainText(r models.ScanResult) string {
|
||||
r.Packages.ToUpdatablePacksSummary(),
|
||||
)
|
||||
|
||||
if len(r.Errors) != 0 {
|
||||
return fmt.Sprintf(
|
||||
"%s\nError: Scan with --debug to view the details\n%s\n\n",
|
||||
header, r.Errors)
|
||||
}
|
||||
|
||||
if len(cves) == 0 {
|
||||
return fmt.Sprintf(`
|
||||
%s
|
||||
@@ -168,6 +193,12 @@ func toFullPlainText(r models.ScanResult) string {
|
||||
r.Packages.ToUpdatablePacksSummary(),
|
||||
)
|
||||
|
||||
if len(r.Errors) != 0 {
|
||||
return fmt.Sprintf(
|
||||
"%s\nError: Scan with --debug to view the details\n%s\n\n",
|
||||
header, r.Errors)
|
||||
}
|
||||
|
||||
if len(r.KnownCves) == 0 && len(r.UnknownCves) == 0 {
|
||||
return fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
23
scan/base.go
23
scan/base.go
@@ -199,24 +199,23 @@ func (l *base) parseLxdPs(stdout string) (containers []config.Container, err err
|
||||
return
|
||||
}
|
||||
|
||||
func (l *base) detectPlatform() error {
|
||||
func (l *base) detectPlatform() {
|
||||
ok, instanceID, err := l.detectRunningOnAws()
|
||||
if err != nil {
|
||||
return err
|
||||
l.setPlatform(models.Platform{Name: "other"})
|
||||
return
|
||||
}
|
||||
if ok {
|
||||
l.setPlatform(models.Platform{
|
||||
Name: "aws",
|
||||
InstanceID: instanceID,
|
||||
})
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
//TODO Azure, GCP...
|
||||
l.setPlatform(models.Platform{
|
||||
Name: "other",
|
||||
})
|
||||
return nil
|
||||
l.setPlatform(models.Platform{Name: "other"})
|
||||
return
|
||||
}
|
||||
|
||||
func (l base) detectRunningOnAws() (ok bool, instanceID string, err error) {
|
||||
@@ -271,7 +270,7 @@ func (l base) isAwsInstanceID(str string) bool {
|
||||
return awsInstanceIDPattern.MatchString(str)
|
||||
}
|
||||
|
||||
func (l *base) convertToModel() (models.ScanResult, error) {
|
||||
func (l *base) convertToModel() models.ScanResult {
|
||||
for _, p := range l.VulnInfos {
|
||||
sort.Sort(models.PackageInfosByName(p.Packages))
|
||||
}
|
||||
@@ -283,6 +282,11 @@ func (l *base) convertToModel() (models.ScanResult, error) {
|
||||
Image: l.ServerInfo.Container.Image,
|
||||
}
|
||||
|
||||
errs := []string{}
|
||||
for _, e := range l.errs {
|
||||
errs = append(errs, fmt.Sprintf("%s", e))
|
||||
}
|
||||
|
||||
return models.ScanResult{
|
||||
ServerName: l.ServerInfo.ServerName,
|
||||
ScannedAt: time.Now(),
|
||||
@@ -293,7 +297,8 @@ func (l *base) convertToModel() (models.ScanResult, error) {
|
||||
ScannedCves: l.VulnInfos,
|
||||
Packages: l.Packages,
|
||||
Optional: l.ServerInfo.Optional,
|
||||
}, nil
|
||||
Errors: errs,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *base) setErrs(errs []error) {
|
||||
|
||||
@@ -56,7 +56,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
|
||||
return false, deb, fmt.Errorf(
|
||||
"Unable to connect via SSH. Check SSH settings. %s", r)
|
||||
}
|
||||
Log.Debugf("Not Debian like Linux. %s", r)
|
||||
util.Log.Debugf("Not Debian like Linux. %s", r)
|
||||
return false, deb, nil
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
|
||||
|
||||
if len(result) == 0 {
|
||||
deb.setDistro("debian/ubuntu", "unknown")
|
||||
Log.Warnf(
|
||||
util.Log.Warnf(
|
||||
"Unknown Debian/Ubuntu version. lsb_release -ir: %s", r)
|
||||
} else {
|
||||
distro := strings.ToLower(trim(result[1]))
|
||||
@@ -88,7 +88,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
|
||||
re := regexp.MustCompile(`(?s)^DISTRIB_ID=(.+?)\n*DISTRIB_RELEASE=(.+?)\n.*$`)
|
||||
result := re.FindStringSubmatch(trim(r.Stdout))
|
||||
if len(result) == 0 {
|
||||
Log.Warnf(
|
||||
util.Log.Warnf(
|
||||
"Unknown Debian/Ubuntu. cat /etc/lsb-release: %s", r)
|
||||
deb.setDistro("debian/ubuntu", "unknown")
|
||||
} else {
|
||||
@@ -105,7 +105,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
|
||||
return true, deb, nil
|
||||
}
|
||||
|
||||
Log.Debugf("Not Debian like Linux: %s", c.ServerName)
|
||||
util.Log.Debugf("Not Debian like Linux: %s", c.ServerName)
|
||||
return false, deb, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -71,35 +71,30 @@ func (s execResult) isSuccess(expectedStatusCodes ...int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Sudo is Const value for sudo mode
|
||||
// sudo is Const value for sudo mode
|
||||
const sudo = true
|
||||
|
||||
// NoSudo is Const value for normal user mode
|
||||
// noSudo is Const value for normal user mode
|
||||
const noSudo = false
|
||||
|
||||
func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []error) {
|
||||
resChan := make(chan string, len(servers))
|
||||
errChan := make(chan error, len(servers))
|
||||
defer close(errChan)
|
||||
// Issue commands to the target servers in parallel via SSH or local execution. If execution fails, the server will be excluded from the target server list(servers) and added to the error server list(errServers).
|
||||
func parallelExec(fn func(osTypeInterface) error, timeoutSec ...int) {
|
||||
resChan := make(chan osTypeInterface, len(servers))
|
||||
defer close(resChan)
|
||||
|
||||
for _, s := range servers {
|
||||
go func(s osTypeInterface) {
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
logrus.Debugf("Panic: %s on %s",
|
||||
util.Log.Debugf("Panic: %s on %s",
|
||||
p, s.getServerInfo().GetServerName())
|
||||
}
|
||||
}()
|
||||
if err := fn(s); err != nil {
|
||||
errChan <- fmt.Errorf("%s@%s:%s: %s",
|
||||
s.getServerInfo().User,
|
||||
s.getServerInfo().Host,
|
||||
s.getServerInfo().Port,
|
||||
err,
|
||||
)
|
||||
s.setErrs([]error{err})
|
||||
resChan <- s
|
||||
} else {
|
||||
resChan <- s.getServerInfo().GetServerName()
|
||||
resChan <- s
|
||||
}
|
||||
}(s)
|
||||
}
|
||||
@@ -111,40 +106,44 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []
|
||||
timeout = timeoutSec[0]
|
||||
}
|
||||
|
||||
var snames []string
|
||||
var successes []osTypeInterface
|
||||
isTimedout := false
|
||||
for i := 0; i < len(servers); i++ {
|
||||
select {
|
||||
case s := <-resChan:
|
||||
snames = append(snames, s)
|
||||
case err := <-errChan:
|
||||
errs = append(errs, err)
|
||||
if len(s.getErrs()) == 0 {
|
||||
successes = append(successes, s)
|
||||
} else {
|
||||
util.Log.Errorf("Error: %s, err: %s",
|
||||
s.getServerInfo().GetServerName(), s.getErrs())
|
||||
errServers = append(errServers, s)
|
||||
}
|
||||
case <-time.After(time.Duration(timeout) * time.Second):
|
||||
isTimedout = true
|
||||
}
|
||||
}
|
||||
|
||||
// collect timed out servernames
|
||||
var timedoutSnames []string
|
||||
if isTimedout {
|
||||
// set timed out error and append to errServers
|
||||
for _, s := range servers {
|
||||
name := s.getServerInfo().GetServerName()
|
||||
found := false
|
||||
for _, t := range snames {
|
||||
if name == t {
|
||||
for _, ss := range successes {
|
||||
if name == ss.getServerInfo().GetServerName() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
timedoutSnames = append(timedoutSnames, name)
|
||||
msg := fmt.Sprintf("Timed out: %s",
|
||||
s.getServerInfo().GetServerName())
|
||||
util.Log.Errorf(msg)
|
||||
s.setErrs([]error{fmt.Errorf(msg)})
|
||||
errServers = append(errServers, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
if isTimedout {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"Timed out: %s", timedoutSnames))
|
||||
}
|
||||
servers = successes
|
||||
return
|
||||
}
|
||||
|
||||
@@ -55,13 +55,13 @@ func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.Debugf("Not FreeBSD. servernam: %s", c.ServerName)
|
||||
util.Log.Debugf("Not FreeBSD. servernam: %s", c.ServerName)
|
||||
return false, bsd
|
||||
}
|
||||
|
||||
func (o *bsd) checkIfSudoNoPasswd() error {
|
||||
// FreeBSD doesn't need root privilege
|
||||
o.log.Infof("sudo ... OK")
|
||||
o.log.Infof("sudo ... No need")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
|
||||
if r := exec(c, "ls /etc/fedora-release", noSudo); r.isSuccess() {
|
||||
red.setDistro("fedora", "unknown")
|
||||
Log.Warn("Fedora not tested yet: %s", r)
|
||||
util.Log.Warn("Fedora not tested yet: %s", r)
|
||||
return true, red
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
re := regexp.MustCompile(`(.*) release (\d[\d.]*)`)
|
||||
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
|
||||
if len(result) != 3 {
|
||||
Log.Warn("Failed to parse RedHat/CentOS version: %s", r)
|
||||
util.Log.Warn("Failed to parse RedHat/CentOS version: %s", r)
|
||||
return true, red
|
||||
}
|
||||
|
||||
@@ -92,11 +92,16 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
return true, red
|
||||
}
|
||||
|
||||
Log.Debugf("Not RedHat like Linux. servername: %s", c.ServerName)
|
||||
util.Log.Debugf("Not RedHat like Linux. servername: %s", c.ServerName)
|
||||
return false, red
|
||||
}
|
||||
|
||||
func (o *redhat) checkIfSudoNoPasswd() error {
|
||||
if !o.sudo() {
|
||||
o.log.Infof("sudo ... No need")
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := "yum --version"
|
||||
if o.Distro.Family == "centos" {
|
||||
cmd = "echo N | " + cmd
|
||||
@@ -117,7 +122,6 @@ func (o *redhat) checkIfSudoNoPasswd() error {
|
||||
func (o *redhat) checkDependencies() error {
|
||||
switch o.Distro.Family {
|
||||
case "rhel", "amazon":
|
||||
// o.log.Infof("Nothing to do")
|
||||
return nil
|
||||
|
||||
case "centos":
|
||||
|
||||
@@ -25,17 +25,14 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/future-architect/vuls/cache"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/report"
|
||||
"github.com/future-architect/vuls/util"
|
||||
)
|
||||
|
||||
// Log for localhsot
|
||||
var Log *logrus.Entry
|
||||
|
||||
var servers []osTypeInterface
|
||||
var servers, errServers []osTypeInterface
|
||||
|
||||
// Base Interface of redhat, debian, freebsd
|
||||
type osTypeInterface interface {
|
||||
@@ -50,13 +47,13 @@ type osTypeInterface interface {
|
||||
getLackDependencies() []string
|
||||
|
||||
checkIfSudoNoPasswd() error
|
||||
detectPlatform() error
|
||||
detectPlatform()
|
||||
getPlatform() models.Platform
|
||||
|
||||
checkRequiredPackagesInstalled() error
|
||||
scanPackages() error
|
||||
install() error
|
||||
convertToModel() (models.ScanResult, error)
|
||||
convertToModel() models.ScanResult
|
||||
|
||||
runningContainers() ([]config.Container, error)
|
||||
exitedContainers() ([]config.Container, error)
|
||||
@@ -89,33 +86,32 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) {
|
||||
|
||||
itsMe, osType, fatalErr = detectDebian(c)
|
||||
if fatalErr != nil {
|
||||
osType.setServerInfo(c)
|
||||
osType.setErrs([]error{fatalErr})
|
||||
return
|
||||
} else if itsMe {
|
||||
Log.Debugf("Debian like Linux. Host: %s:%s", c.Host, c.Port)
|
||||
}
|
||||
|
||||
if itsMe {
|
||||
util.Log.Debugf("Debian like Linux. Host: %s:%s", c.Host, c.Port)
|
||||
return
|
||||
}
|
||||
|
||||
if itsMe, osType = detectRedhat(c); itsMe {
|
||||
Log.Debugf("Redhat like Linux. Host: %s:%s", c.Host, c.Port)
|
||||
util.Log.Debugf("Redhat like Linux. Host: %s:%s", c.Host, c.Port)
|
||||
return
|
||||
}
|
||||
|
||||
if itsMe, osType = detectFreebsd(c); itsMe {
|
||||
Log.Debugf("FreeBSD. Host: %s:%s", c.Host, c.Port)
|
||||
util.Log.Debugf("FreeBSD. Host: %s:%s", c.Host, c.Port)
|
||||
return
|
||||
}
|
||||
|
||||
//TODO darwin https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/darwin.rb
|
||||
|
||||
osType.setServerInfo(c)
|
||||
osType.setErrs([]error{fmt.Errorf("Unknown OS Type")})
|
||||
return
|
||||
}
|
||||
|
||||
// PrintSSHableServerNames print SSH-able servernames
|
||||
func PrintSSHableServerNames() {
|
||||
Log.Info("SSH-able servers are below...")
|
||||
util.Log.Info("SSH-able servers are below...")
|
||||
for _, s := range servers {
|
||||
if s.getServerInfo().IsContainer() {
|
||||
fmt.Printf("%s@%s ",
|
||||
@@ -130,67 +126,74 @@ func PrintSSHableServerNames() {
|
||||
}
|
||||
|
||||
// InitServers detect the kind of OS distribution of target servers
|
||||
func InitServers(localLogger *logrus.Entry) error {
|
||||
Log = localLogger
|
||||
servers = detectServerOSes()
|
||||
func InitServers() error {
|
||||
servers, errServers = detectServerOSes()
|
||||
if len(servers) == 0 {
|
||||
return fmt.Errorf("No scannable servers")
|
||||
}
|
||||
|
||||
containers := detectContainerOSes()
|
||||
actives, inactives := detectContainerOSes()
|
||||
if config.Conf.ContainersOnly {
|
||||
servers = containers
|
||||
servers = actives
|
||||
errServers = inactives
|
||||
} else {
|
||||
servers = append(servers, containers...)
|
||||
servers = append(servers, actives...)
|
||||
errServers = append(errServers, inactives...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func detectServerOSes() (sshAbleOses []osTypeInterface) {
|
||||
Log.Info("Detecting OS of servers... ")
|
||||
func detectServerOSes() (servers, errServers []osTypeInterface) {
|
||||
util.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) {
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
Log.Debugf("Panic: %s on %s", p, s.ServerName)
|
||||
util.Log.Debugf("Panic: %s on %s", p, s.ServerName)
|
||||
}
|
||||
}()
|
||||
osTypeChan <- detectOS(s)
|
||||
}(s)
|
||||
}
|
||||
|
||||
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.Errorf("(%d/%d) Failed: %s, err: %s",
|
||||
errServers = append(errServers, res)
|
||||
util.Log.Errorf("(%d/%d) Failed: %s, err: %s",
|
||||
i+1, len(config.Conf.Servers),
|
||||
res.getServerInfo().ServerName,
|
||||
res.getErrs())
|
||||
} else {
|
||||
Log.Infof("(%d/%d) Detected: %s: %s",
|
||||
servers = append(servers, res)
|
||||
util.Log.Infof("(%d/%d) Detected: %s: %s",
|
||||
i+1, len(config.Conf.Servers),
|
||||
res.getServerInfo().ServerName,
|
||||
res.getDistro())
|
||||
}
|
||||
case <-timeout:
|
||||
msg := "Timed out while detecting servers"
|
||||
Log.Error(msg)
|
||||
for servername := range config.Conf.Servers {
|
||||
util.Log.Error(msg)
|
||||
for servername, sInfo := range config.Conf.Servers {
|
||||
found := false
|
||||
for _, o := range oses {
|
||||
for _, o := range append(servers, errServers...) {
|
||||
if servername == o.getServerInfo().ServerName {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
Log.Errorf("(%d/%d) Timed out: %s",
|
||||
u := &unknown{}
|
||||
u.setServerInfo(sInfo)
|
||||
u.setErrs([]error{
|
||||
fmt.Errorf("Timed out"),
|
||||
})
|
||||
errServers = append(errServers, u)
|
||||
util.Log.Errorf("(%d/%d) Timed out: %s",
|
||||
i+1, len(config.Conf.Servers),
|
||||
servername)
|
||||
i++
|
||||
@@ -198,24 +201,18 @@ func detectServerOSes() (sshAbleOses []osTypeInterface) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, o := range oses {
|
||||
if len(o.getErrs()) == 0 {
|
||||
sshAbleOses = append(sshAbleOses, o)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func detectContainerOSes() (actives []osTypeInterface) {
|
||||
Log.Info("Detecting OS of containers... ")
|
||||
func detectContainerOSes() (actives, inactives []osTypeInterface) {
|
||||
util.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",
|
||||
util.Log.Debugf("Panic: %s on %s",
|
||||
p, s.getServerInfo().GetServerName())
|
||||
}
|
||||
}()
|
||||
@@ -223,7 +220,6 @@ func detectContainerOSes() (actives []osTypeInterface) {
|
||||
}(s)
|
||||
}
|
||||
|
||||
var oses []osTypeInterface
|
||||
timeout := time.After(30 * time.Second)
|
||||
for i := 0; i < len(servers); i++ {
|
||||
select {
|
||||
@@ -231,36 +227,38 @@ func detectContainerOSes() (actives []osTypeInterface) {
|
||||
for _, osi := range res {
|
||||
sinfo := osi.getServerInfo()
|
||||
if 0 < len(osi.getErrs()) {
|
||||
Log.Errorf("Failed: %s err: %s", sinfo.ServerName, osi.getErrs())
|
||||
inactives = append(inactives, osi)
|
||||
util.Log.Errorf("Failed: %s err: %s", sinfo.ServerName, osi.getErrs())
|
||||
continue
|
||||
}
|
||||
oses = append(oses, osi)
|
||||
Log.Infof("Detected: %s@%s: %s",
|
||||
actives = append(actives, osi)
|
||||
util.Log.Infof("Detected: %s@%s: %s",
|
||||
sinfo.Container.Name, sinfo.ServerName, osi.getDistro())
|
||||
}
|
||||
case <-timeout:
|
||||
msg := "Timed out while detecting containers"
|
||||
Log.Error(msg)
|
||||
for servername := range config.Conf.Servers {
|
||||
util.Log.Error(msg)
|
||||
for servername, sInfo := range config.Conf.Servers {
|
||||
found := false
|
||||
for _, o := range oses {
|
||||
for _, o := range append(actives, inactives...) {
|
||||
if servername == o.getServerInfo().ServerName {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
Log.Errorf("Timed out: %s", servername)
|
||||
u := &unknown{}
|
||||
u.setServerInfo(sInfo)
|
||||
u.setErrs([]error{
|
||||
fmt.Errorf("Timed out"),
|
||||
})
|
||||
inactives = append(inactives)
|
||||
util.Log.Errorf("Timed out: %s", servername)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, o := range oses {
|
||||
if len(o.getErrs()) == 0 {
|
||||
actives = append(actives, o)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -339,28 +337,20 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn
|
||||
}
|
||||
|
||||
// CheckIfSudoNoPasswd checks whether vuls can sudo with nopassword via SSH
|
||||
func CheckIfSudoNoPasswd(localLogger *logrus.Entry) error {
|
||||
func CheckIfSudoNoPasswd() {
|
||||
timeoutSec := 15
|
||||
errs := parallelSSHExec(func(o osTypeInterface) error {
|
||||
parallelExec(func(o osTypeInterface) error {
|
||||
return o.checkIfSudoNoPasswd()
|
||||
}, timeoutSec)
|
||||
|
||||
if 0 < len(errs) {
|
||||
return fmt.Errorf(fmt.Sprintf("%s", errs))
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
func DetectPlatforms() {
|
||||
detectPlatforms()
|
||||
for i, s := range servers {
|
||||
if s.getServerInfo().IsContainer() {
|
||||
Log.Infof("(%d/%d) %s on %s is running on %s",
|
||||
util.Log.Infof("(%d/%d) %s on %s is running on %s",
|
||||
i+1, len(servers),
|
||||
s.getServerInfo().Container.Name,
|
||||
s.getServerInfo().ServerName,
|
||||
@@ -368,7 +358,7 @@ func DetectPlatforms(localLogger *logrus.Entry) {
|
||||
)
|
||||
|
||||
} else {
|
||||
Log.Infof("(%d/%d) %s is running on %s",
|
||||
util.Log.Infof("(%d/%d) %s is running on %s",
|
||||
i+1, len(servers),
|
||||
s.getServerInfo().ServerName,
|
||||
s.getPlatform().Name,
|
||||
@@ -378,52 +368,68 @@ func DetectPlatforms(localLogger *logrus.Entry) {
|
||||
return
|
||||
}
|
||||
|
||||
func detectPlatforms() []error {
|
||||
func detectPlatforms() {
|
||||
timeoutSec := 1 * 60
|
||||
return parallelSSHExec(func(o osTypeInterface) error {
|
||||
return o.detectPlatform()
|
||||
parallelExec(func(o osTypeInterface) error {
|
||||
o.detectPlatform()
|
||||
// Logging only if platform can not be specified
|
||||
return nil
|
||||
}, timeoutSec)
|
||||
return
|
||||
}
|
||||
|
||||
// Prepare installs requred packages to scan vulnerabilities.
|
||||
func Prepare() []error {
|
||||
errs := parallelSSHExec(func(o osTypeInterface) error {
|
||||
func Prepare() error {
|
||||
parallelExec(func(o osTypeInterface) error {
|
||||
if err := o.checkDependencies(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if len(errs) != 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
var targets []osTypeInterface
|
||||
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 {
|
||||
Log.Info("No need to install dependencies")
|
||||
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
|
||||
}
|
||||
|
||||
Log.Info("The following servers need dependencies installed")
|
||||
util.Log.Info("The following servers need to install dependencies")
|
||||
for _, s := range targets {
|
||||
for _, d := range s.getLackDependencies() {
|
||||
Log.Infof(" - %s on %s", d, s.getServerInfo().GetServerName())
|
||||
util.Log.Infof(" - %s on %s", d, s.getServerInfo().GetServerName())
|
||||
}
|
||||
}
|
||||
|
||||
if !config.Conf.AssumeYes {
|
||||
Log.Info("Is this ok to install dependencies on the servers? [y/N]")
|
||||
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 []error{err}
|
||||
return err
|
||||
}
|
||||
switch strings.TrimSpace(text) {
|
||||
case "", "N", "n":
|
||||
@@ -431,62 +437,81 @@ func Prepare() []error {
|
||||
case "y", "Y":
|
||||
goto yes
|
||||
default:
|
||||
Log.Info("Please enter y or N")
|
||||
util.Log.Info("Please enter y or N")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yes:
|
||||
servers = targets
|
||||
errs = parallelSSHExec(func(o osTypeInterface) error {
|
||||
parallelExec(func(o osTypeInterface) error {
|
||||
if err := o.install(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if len(errs) != 0 {
|
||||
return errs
|
||||
|
||||
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")
|
||||
}
|
||||
Log.Info("All dependencies were installed correctly")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Scan scan
|
||||
func Scan() []error {
|
||||
func Scan() error {
|
||||
if len(servers) == 0 {
|
||||
return []error{fmt.Errorf("No server defined. Check the configuration")}
|
||||
return fmt.Errorf("No server defined. Check the configuration")
|
||||
}
|
||||
|
||||
Log.Info("Check required packages for scanning...")
|
||||
if errs := checkRequiredPackagesInstalled(); errs != nil {
|
||||
Log.Error("Please execute with [prepare] subcommand to install required packages before scanning")
|
||||
return errs
|
||||
}
|
||||
util.Log.Info("Check required packages for scanning...")
|
||||
checkRequiredPackagesInstalled()
|
||||
|
||||
if err := setupCangelogCache(); err != nil {
|
||||
return []error{err}
|
||||
if err := setupChangelogCache(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if cache.DB != nil {
|
||||
cache.DB.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
Log.Info("Scanning vulnerable OS packages...")
|
||||
util.Log.Info("Scanning vulnerable OS packages...")
|
||||
scannedAt := time.Now()
|
||||
dir, err := ensureResultDir(scannedAt)
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
return err
|
||||
}
|
||||
if errs := scanVulns(dir, scannedAt); errs != nil {
|
||||
return errs
|
||||
if err := scanVulns(dir, scannedAt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupCangelogCache() error {
|
||||
func setupChangelogCache() error {
|
||||
needToSetupCache := false
|
||||
for _, s := range servers {
|
||||
switch s.getDistro().Family {
|
||||
@@ -496,7 +521,7 @@ func setupCangelogCache() error {
|
||||
}
|
||||
}
|
||||
if needToSetupCache {
|
||||
if err := cache.SetupBolt(config.Conf.CacheDBPath, Log); err != nil {
|
||||
if err := cache.SetupBolt(config.Conf.CacheDBPath, util.Log); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -505,28 +530,24 @@ func setupCangelogCache() error {
|
||||
|
||||
func checkRequiredPackagesInstalled() []error {
|
||||
timeoutSec := 30 * 60
|
||||
return parallelSSHExec(func(o osTypeInterface) error {
|
||||
parallelExec(func(o osTypeInterface) error {
|
||||
return o.checkRequiredPackagesInstalled()
|
||||
}, timeoutSec)
|
||||
return nil
|
||||
}
|
||||
|
||||
func scanVulns(jsonDir string, scannedAt time.Time) []error {
|
||||
func scanVulns(jsonDir string, scannedAt time.Time) error {
|
||||
var results models.ScanResults
|
||||
timeoutSec := 120 * 60
|
||||
errs := parallelSSHExec(func(o osTypeInterface) error {
|
||||
if err := o.scanPackages(); err != nil {
|
||||
return err
|
||||
}
|
||||
parallelExec(func(o osTypeInterface) error {
|
||||
return o.scanPackages()
|
||||
}, timeoutSec)
|
||||
|
||||
r, err := o.convertToModel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range append(servers, errServers...) {
|
||||
r := s.convertToModel()
|
||||
r.ScannedAt = scannedAt
|
||||
results = append(results, r)
|
||||
|
||||
return nil
|
||||
}, timeoutSec)
|
||||
}
|
||||
|
||||
config.Conf.FormatJSON = true
|
||||
ws := []report.ResultWriter{
|
||||
@@ -534,14 +555,9 @@ func scanVulns(jsonDir string, scannedAt time.Time) []error {
|
||||
}
|
||||
for _, w := range ws {
|
||||
if err := w.Write(results...); err != nil {
|
||||
return []error{
|
||||
fmt.Errorf("Failed to write summary report: %s", err),
|
||||
}
|
||||
return fmt.Errorf("Failed to write summary report: %s", err)
|
||||
}
|
||||
}
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
|
||||
report.StdoutWriter{}.WriteScanSummary(results...)
|
||||
return nil
|
||||
|
||||
43
scan/unknownDistro.go
Normal file
43
scan/unknownDistro.go
Normal file
@@ -0,0 +1,43 @@
|
||||
/* 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 scan
|
||||
|
||||
// inherit OsTypeInterface
|
||||
type unknown struct {
|
||||
base
|
||||
}
|
||||
|
||||
func (o *unknown) checkIfSudoNoPasswd() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -29,6 +29,9 @@ import (
|
||||
formatter "github.com/kotakanbe/logrus-prefixed-formatter"
|
||||
)
|
||||
|
||||
// Log for localhsot
|
||||
var Log *logrus.Entry
|
||||
|
||||
// NewCustomLogger creates logrus
|
||||
func NewCustomLogger(c config.ServerInfo) *logrus.Entry {
|
||||
log := logrus.New()
|
||||
@@ -47,7 +50,7 @@ func NewCustomLogger(c config.ServerInfo) *logrus.Entry {
|
||||
|
||||
if _, err := os.Stat(logDir); os.IsNotExist(err) {
|
||||
if err := os.Mkdir(logDir, 0700); err != nil {
|
||||
logrus.Errorf("Failed to create log directory: %s", err)
|
||||
log.Errorf("Failed to create log directory: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user