fix(configtest,scan): support SSH config file (#1388)

* fix(configtest,scan): support SSH config file

* chore(subcmds): remove askKeyPassword flag
This commit is contained in:
MaineK00n
2022-02-12 21:50:56 +09:00
committed by GitHub
parent e5855922c1
commit 07335617d3
11 changed files with 99 additions and 101 deletions

View File

@@ -1,10 +1,9 @@
package config
// Load loads configuration
func Load(path, keyPass string) error {
var loader Loader
loader = TOMLLoader{}
return loader.Load(path, keyPass)
func Load(path string) error {
loader := TOMLLoader{}
return loader.Load(path)
}
// Loader is interface of concrete loader

View File

@@ -15,7 +15,7 @@ type TOMLLoader struct {
}
// Load load the configuration TOML file specified by path arg.
func (c TOMLLoader) Load(pathToToml, _ string) error {
func (c TOMLLoader) Load(pathToToml string) error {
// util.Log.Infof("Loading config: %s", pathToToml)
if _, err := toml.DecodeFile(pathToToml, &Conf); err != nil {
return err
@@ -149,18 +149,11 @@ func setDefaultIfEmpty(server *ServerInfo) error {
}
if server.Port == "" {
if Conf.Default.Port != "" {
server.Port = Conf.Default.Port
} else {
server.Port = "22"
}
server.Port = Conf.Default.Port
}
if server.User == "" {
server.User = Conf.Default.User
if server.User == "" && server.Port != "local" {
return xerrors.Errorf("server.user is empty")
}
}
if server.SSHConfigPath == "" {

View File

@@ -186,10 +186,10 @@ func sshExecExternal(c config.ServerInfo, cmd string, sudo bool) (result execRes
return execResult{Error: err}
}
defaultSSHArgs := []string{"-tt"}
args := []string{"-tt"}
if 0 < len(c.SSHConfigPath) {
defaultSSHArgs = append(defaultSSHArgs, "-F", c.SSHConfigPath)
if c.SSHConfigPath != "" {
args = append(args, "-F", c.SSHConfigPath)
} else {
home, err := homedir.Dir()
if err != nil {
@@ -200,7 +200,7 @@ func sshExecExternal(c config.ServerInfo, cmd string, sudo bool) (result execRes
}
controlPath := filepath.Join(home, ".vuls", `controlmaster-%r-`+c.ServerName+`.%p`)
defaultSSHArgs = append(defaultSSHArgs,
args = append(args,
"-o", "StrictHostKeyChecking=yes",
"-o", "LogLevel=quiet",
"-o", "ConnectionAttempts=3",
@@ -212,19 +212,22 @@ func sshExecExternal(c config.ServerInfo, cmd string, sudo bool) (result execRes
}
if config.Conf.Vvv {
defaultSSHArgs = append(defaultSSHArgs, "-vvv")
args = append(args, "-vvv")
}
if len(c.JumpServer) != 0 {
defaultSSHArgs = append(defaultSSHArgs, "-J", strings.Join(c.JumpServer, ","))
args = append(args, "-J", strings.Join(c.JumpServer, ","))
}
args := append(defaultSSHArgs, fmt.Sprintf("%s@%s", c.User, c.Host))
args = append(args, "-p", c.Port)
if 0 < len(c.KeyPath) {
if c.User != "" {
args = append(args, "-l", c.User)
}
if c.Port != "" {
args = append(args, "-p", c.Port)
}
if c.KeyPath != "" {
args = append(args, "-i", c.KeyPath)
args = append(args, "-o", "PasswordAuthentication=no")
}
args = append(args, c.Host)
cmd = decorateCmd(c, cmd, sudo)
cmd = fmt.Sprintf("stty cols 1000; %s", cmd)

View File

@@ -3,8 +3,10 @@ package scanner
import (
"fmt"
"math/rand"
"net"
"net/http"
"os"
ex "os/exec"
"strings"
"time"
@@ -287,7 +289,7 @@ func (s Scanner) detectServerOSes() (servers, errServers []osTypeInterface) {
logging.Log.Debugf("Panic: %s on %s", p, srv.ServerName)
}
}()
if err := checkHostinKnownHosts(srv); err != nil {
if err := validateSSHConfig(&srv); err != nil {
checkOS := unknown{base{ServerInfo: srv}}
checkOS.setErrs([]error{err})
osTypeChan <- &checkOS
@@ -332,16 +334,36 @@ func (s Scanner) detectServerOSes() (servers, errServers []osTypeInterface) {
return
}
func checkHostinKnownHosts(c config.ServerInfo) error {
func validateSSHConfig(c *config.ServerInfo) error {
if isLocalExec(c.Port, c.Host) {
return nil
}
args := []string{"-G"}
if len(c.SSHConfigPath) > 0 {
args = []string{"-G", "-F", c.SSHConfigPath}
logging.Log.Debugf("Validating SSH Settings for Server:%s ...", c.GetServerName())
sshBinaryPath, err := ex.LookPath("ssh")
if err != nil {
return xerrors.Errorf("Failed to lookup ssh binary path. err: %w", err)
}
r := localExec(c, fmt.Sprintf("ssh %s %s", strings.Join(args, " "), c.Host), noSudo)
sshKeygenBinaryPath, err := ex.LookPath("ssh-keygen")
if err != nil {
return xerrors.Errorf("Failed to lookup ssh-keygen binary path. err: %w", err)
}
sshConfigCmd := []string{sshBinaryPath, "-G"}
if c.SSHConfigPath != "" {
sshConfigCmd = append(sshConfigCmd, "-F", c.SSHConfigPath)
}
if c.Port != "" {
sshConfigCmd = append(sshConfigCmd, "-p", c.Port)
}
if c.User != "" {
sshConfigCmd = append(sshConfigCmd, "-l", c.User)
}
sshConfigCmd = append(sshConfigCmd, c.Host)
cmd := strings.Join(sshConfigCmd, " ")
logging.Log.Debugf("Executing... %s", strings.Replace(cmd, "\n", "", -1))
r := localExec(*c, cmd, noSudo)
if !r.isSuccess() {
return xerrors.Errorf("Failed to print SSH configuration. err: %w", r.Error)
}
@@ -352,14 +374,31 @@ func checkHostinKnownHosts(c config.ServerInfo) error {
userKnownHosts string
)
for _, line := range strings.Split(r.Stdout, "\n") {
if strings.HasPrefix(line, "hostname ") {
if strings.HasPrefix(line, "user ") {
user := strings.TrimPrefix(line, "user ")
logging.Log.Debugf("Setting SSH User:%s for Server:%s ...", user, c.GetServerName())
c.User = user
} else if strings.HasPrefix(line, "hostname ") {
hostname = strings.TrimPrefix(line, "hostname ")
logging.Log.Debugf("Validating SSH HostName:%s for Server:%s ...", hostname, c.GetServerName())
if _, err := net.LookupHost(hostname); err != nil {
return xerrors.New("Failed to name resolution. Please check the HostName settings for SSH")
}
} else if strings.HasPrefix(line, "port ") {
port := strings.TrimPrefix(line, "port ")
logging.Log.Debugf("Setting SSH Port:%s for Server:%s ...", port, c.GetServerName())
c.Port = port
} else if strings.HasPrefix(line, "globalknownhostsfile ") {
globalKnownHosts = strings.TrimPrefix(line, "globalknownhostsfile ")
} else if strings.HasPrefix(line, "userknownhostsfile ") {
userKnownHosts = strings.TrimPrefix(line, "userknownhostsfile ")
}
}
if c.User == "" || c.Port == "" {
return xerrors.New("Failed to find User or Port setting. Please check the User or Port settings for SSH")
}
logging.Log.Debugf("Checking if the host's public key is in known_hosts...")
knownHostsPaths := []string{}
for _, knownHosts := range []string{userKnownHosts, globalKnownHosts} {
@@ -370,29 +409,44 @@ func checkHostinKnownHosts(c config.ServerInfo) error {
}
}
if len(knownHostsPaths) == 0 {
return xerrors.New("Failed to find any known_hosts to use. Please check the UserKnownHostsFile and GlobalKnownHostsFile settings for SSH.")
return xerrors.New("Failed to find any known_hosts to use. Please check the UserKnownHostsFile and GlobalKnownHostsFile settings for SSH")
}
for _, knownHosts := range knownHostsPaths {
if c.Port != "" && c.Port != "22" {
cmd := fmt.Sprintf("ssh-keygen -F %s -f %s", fmt.Sprintf("\"[%s]:%s\"", c.Host, c.Port), knownHosts)
if r := localExec(c, cmd, noSudo); r.isSuccess() {
cmd := fmt.Sprintf("%s -F %s -f %s", sshKeygenBinaryPath, fmt.Sprintf("\"[%s]:%s\"", hostname, c.Port), knownHosts)
logging.Log.Debugf("Executing... %s", strings.Replace(cmd, "\n", "", -1))
if r := localExec(*c, cmd, noSudo); r.isSuccess() {
return nil
}
}
cmd := fmt.Sprintf("ssh-keygen -F %s -f %s", c.Host, knownHosts)
if r := localExec(c, cmd, noSudo); r.isSuccess() {
cmd := fmt.Sprintf("%s -F %s -f %s", sshKeygenBinaryPath, hostname, knownHosts)
logging.Log.Debugf("Executing... %s", strings.Replace(cmd, "\n", "", -1))
if r := localExec(*c, cmd, noSudo); r.isSuccess() {
return nil
}
}
sshCmd := fmt.Sprintf("ssh -i %s %s@%s", c.KeyPath, c.User, c.Host)
sshKeyScancmd := fmt.Sprintf("ssh-keyscan -H %s >> %s", hostname, knownHostsPaths[0])
if c.Port != "" && c.Port != "22" {
sshCmd = fmt.Sprintf("ssh -i %s -p %s %s@%s", c.KeyPath, c.Port, c.User, c.Host)
sshKeyScancmd = fmt.Sprintf("ssh-keyscan -H -p %s %s >> %s", c.Port, hostname, knownHostsPaths[0])
sshConnArgs := []string{}
sshKeyScanArgs := []string{"-H"}
if c.SSHConfigPath != "" {
sshConnArgs = append(sshConnArgs, "-F", c.SSHConfigPath)
}
return xerrors.Errorf("Failed to find the host in known_hosts. Plaese exec `$ %s` or `$ %s`", sshCmd, sshKeyScancmd)
if c.KeyPath != "" {
sshConnArgs = append(sshConnArgs, "-i", c.KeyPath)
}
if c.Port != "" {
sshConnArgs = append(sshConnArgs, "-p", c.Port)
sshKeyScanArgs = append(sshKeyScanArgs, "-p", c.Port)
}
if c.User != "" {
sshConnArgs = append(sshConnArgs, "-l", c.User)
}
sshConnArgs = append(sshConnArgs, c.Host)
sshKeyScanArgs = append(sshKeyScanArgs, fmt.Sprintf("%s >> %s", hostname, knownHostsPaths[0]))
sshConnCmd := fmt.Sprintf("ssh %s", strings.Join(sshConnArgs, " "))
sshKeyScancmd := fmt.Sprintf("ssh-keyscan %s", strings.Join(sshKeyScanArgs, " "))
return xerrors.Errorf("Failed to find the host in known_hosts. Plaese exec `$ %s` or `$ %s`", sshConnCmd, sshKeyScancmd)
}
func (s Scanner) detectContainerOSes(hosts []osTypeInterface) (actives, inactives []osTypeInterface) {

View File

@@ -17,9 +17,8 @@ import (
// ConfigtestCmd is Subcommand
type ConfigtestCmd struct {
configPath string
askKeyPassword bool
timeoutSec int
configPath string
timeoutSec int
}
// Name return subcommand name
@@ -35,7 +34,6 @@ func (*ConfigtestCmd) Usage() string {
[-config=/path/to/config.toml]
[-log-to-file]
[-log-dir=/path/to/log]
[-ask-key-password]
[-timeout=300]
[-containers-only]
[-http-proxy=http://192.168.0.1:8080]
@@ -59,10 +57,6 @@ func (p *ConfigtestCmd) SetFlags(f *flag.FlagSet) {
f.IntVar(&p.timeoutSec, "timeout", 5*60, "Timeout(Sec)")
f.BoolVar(&p.askKeyPassword, "ask-key-password", false,
"Ask ssh privatekey password before scanning",
)
f.StringVar(&config.Conf.HTTPProxy, "http-proxy", "",
"http://proxy-url:port (default: empty)")
@@ -79,18 +73,7 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
return subcommands.ExitUsageError
}
var keyPass string
var err error
if p.askKeyPassword {
prompt := "SSH key password: "
if keyPass, err = getPasswd(prompt); err != nil {
logging.Log.Error(err)
return subcommands.ExitFailure
}
}
err = config.Load(p.configPath, keyPass)
if err != nil {
if err := config.Load(p.configPath); err != nil {
msg := []string{
fmt.Sprintf("Error loading %s", p.configPath),
"If you update Vuls and get this error, there may be incompatible changes in config.toml",

View File

@@ -175,7 +175,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "")
logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision)
if err := config.Load(p.configPath, ""); err != nil {
if err := config.Load(p.configPath); err != nil {
logging.Log.Errorf("Error loading %s, %+v", p.configPath, err)
return subcommands.ExitUsageError
}

View File

@@ -65,7 +65,7 @@ func (p *SaaSCmd) SetFlags(f *flag.FlagSet) {
func (p *SaaSCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "")
logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision)
if err := config.Load(p.configPath, ""); err != nil {
if err := config.Load(p.configPath); err != nil {
logging.Log.Errorf("Error loading %s, %+v", p.configPath, err)
return subcommands.ExitUsageError
}

View File

@@ -20,7 +20,6 @@ import (
// ScanCmd is Subcommand of host discovery mode
type ScanCmd struct {
configPath string
askKeyPassword bool
timeoutSec int
scanTimeoutSec int
cacheDBPath string
@@ -43,7 +42,6 @@ func (*ScanCmd) Usage() string {
[-log-dir=/path/to/log]
[-cachedb-path=/path/to/cache.db]
[-http-proxy=http://192.168.0.1:8080]
[-ask-key-password]
[-timeout=300]
[-timeout-scan=7200]
[-debug]
@@ -80,10 +78,6 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
f.StringVar(&config.Conf.HTTPProxy, "http-proxy", "",
"http://proxy-url:port (default: empty)")
f.BoolVar(&p.askKeyPassword, "ask-key-password", false,
"Ask ssh privatekey password before scanning",
)
f.BoolVar(&config.Conf.Pipe, "pipe", false, "Use stdin via PIPE")
f.BoolVar(&p.detectIPS, "ips", false, "retrieve IPS information")
@@ -116,18 +110,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
}
}
var keyPass string
var err error
if p.askKeyPassword {
prompt := "SSH key password: "
if keyPass, err = getPasswd(prompt); err != nil {
logging.Log.Error(err)
return subcommands.ExitFailure
}
}
err = config.Load(p.configPath, keyPass)
if err != nil {
if err := config.Load(p.configPath); err != nil {
msg := []string{
fmt.Sprintf("Error loading %s", p.configPath),
"If you update Vuls and get this error, there may be incompatible changes in config.toml",

View File

@@ -93,7 +93,7 @@ func (p *ServerCmd) SetFlags(f *flag.FlagSet) {
func (p *ServerCmd) Execute(_ context.Context, _ *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "")
logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision)
if err := config.Load(p.configPath, ""); err != nil {
if err := config.Load(p.configPath); err != nil {
logging.Log.Errorf("Error loading %s. err: %+v", p.configPath, err)
return subcommands.ExitUsageError
}

View File

@@ -110,7 +110,7 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "")
logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision)
if err := config.Load(p.configPath, ""); err != nil {
if err := config.Load(p.configPath); err != nil {
logging.Log.Errorf("Error loading %s, err: %+v", p.configPath, err)
return subcommands.ExitUsageError
}

View File

@@ -1,29 +1,12 @@
package subcmds
import (
"fmt"
"os"
"path/filepath"
"github.com/howeyc/gopass"
homedir "github.com/mitchellh/go-homedir"
"golang.org/x/xerrors"
)
func getPasswd(prompt string) (string, error) {
for {
fmt.Print(prompt)
pass, err := gopass.GetPasswdMasked()
if err != nil {
return "", xerrors.New("Failed to read a password")
}
if 0 < len(pass) {
return string(pass), nil
}
}
}
func mkdirDotVuls() error {
home, err := homedir.Dir()
if err != nil {