Support scanning with external ssh command
This commit is contained in:
		
							
								
								
									
										22
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								README.md
									
									
									
									
									
								
							@@ -427,12 +427,18 @@ You can customize your configuration using this template.
 | 
			
		||||
    #]
 | 
			
		||||
    #containers = ["${running}"]
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    You can overwrite the default value specified in default section.  
 | 
			
		||||
    Vuls supports multiple SSH authentication methods.  
 | 
			
		||||
 | 
			
		||||
    Vuls supports two types of SSH. One is native go implementation. The other is external SSH command. For details, see [-ssh-external option](https://github.com/future-architect/vuls#-ssh-external-option)
 | 
			
		||||
    
 | 
			
		||||
    Multiple SSH authentication methods are supported.  
 | 
			
		||||
    - SSH agent
 | 
			
		||||
    - SSH public key authentication (with password, empty password)
 | 
			
		||||
    - Password authentication
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
# Usage: Prepare
 | 
			
		||||
@@ -484,6 +490,7 @@ scan:
 | 
			
		||||
                [-cve-dictionary-url=http://127.0.0.1:1323]
 | 
			
		||||
                [-cvss-over=7]
 | 
			
		||||
                [-ignore-unscored-cves]
 | 
			
		||||
                [-ssh-external]
 | 
			
		||||
                [-report-json]
 | 
			
		||||
                [-report-mail]
 | 
			
		||||
                [-report-s3]
 | 
			
		||||
@@ -538,6 +545,8 @@ scan:
 | 
			
		||||
        Send report via Slack
 | 
			
		||||
  -report-text
 | 
			
		||||
        Write report to text files ($PWD/results/current)
 | 
			
		||||
  -ssh-external
 | 
			
		||||
        Use external ssh command. Default: Use the Go native implementation
 | 
			
		||||
  -use-unattended-upgrades
 | 
			
		||||
        [Deprecated] For Ubuntu. Scan by unattended-upgrades or not (use apt-get upgrade --dry-run by default)
 | 
			
		||||
  -use-yum-plugin-security
 | 
			
		||||
@@ -545,6 +554,16 @@ scan:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## -ssh-external option
 | 
			
		||||
 | 
			
		||||
Vuls supports different types of SSH.  
 | 
			
		||||
 | 
			
		||||
By Defaut, using a native Go implementation from crypto/ssh.   
 | 
			
		||||
This is useful in situations where you may not have access to traditional UNIX tools.
 | 
			
		||||
 | 
			
		||||
To use external ssh command, specify this option.   
 | 
			
		||||
This is useful If you want to use ProxyCommand or chiper algorithm of SSH that is not supported by native go implementation.  
 | 
			
		||||
 | 
			
		||||
## -ask-key-password option 
 | 
			
		||||
 | 
			
		||||
| SSH key password |  -ask-key-password | |
 | 
			
		||||
@@ -559,6 +578,7 @@ scan:
 | 
			
		||||
| NOPASSWORD       | - | defined as NOPASSWORD in /etc/sudoers on target servers |
 | 
			
		||||
| with password    | required | . |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## -report-json , -report-text option
 | 
			
		||||
 | 
			
		||||
At the end of the scan, scan results will be available in the $PWD/result/current/ directory.  
 | 
			
		||||
 
 | 
			
		||||
@@ -67,6 +67,8 @@ type ScanCmd struct {
 | 
			
		||||
	awsProfile  string
 | 
			
		||||
	awsS3Bucket string
 | 
			
		||||
	awsRegion   string
 | 
			
		||||
 | 
			
		||||
	sshExternal bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name return subcommand name
 | 
			
		||||
@@ -86,6 +88,7 @@ func (*ScanCmd) Usage() string {
 | 
			
		||||
		[-cve-dictionary-url=http://127.0.0.1:1323]
 | 
			
		||||
		[-cvss-over=7]
 | 
			
		||||
		[-ignore-unscored-cves]
 | 
			
		||||
		[-ssh-external]
 | 
			
		||||
		[-report-json]
 | 
			
		||||
		[-report-mail]
 | 
			
		||||
		[-report-s3]
 | 
			
		||||
@@ -141,6 +144,12 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
		false,
 | 
			
		||||
		"Don't report the unscored CVEs")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(
 | 
			
		||||
		&p.sshExternal,
 | 
			
		||||
		"ssh-external",
 | 
			
		||||
		false,
 | 
			
		||||
		"Use external ssh command. Default: Use the Go native implementation")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&p.httpProxy,
 | 
			
		||||
		"http-proxy",
 | 
			
		||||
@@ -292,6 +301,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
 | 
			
		||||
	c.Conf.CveDictionaryURL = p.cveDictionaryURL
 | 
			
		||||
	c.Conf.CvssScoreOver = p.cvssScoreOver
 | 
			
		||||
	c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
 | 
			
		||||
	c.Conf.SSHExternal = p.sshExternal
 | 
			
		||||
	c.Conf.HTTPProxy = p.httpProxy
 | 
			
		||||
	c.Conf.UseYumPluginSecurity = p.useYumPluginSecurity
 | 
			
		||||
	c.Conf.UseUnattendedUpgrades = p.useUnattendedUpgrades
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,8 @@ type Config struct {
 | 
			
		||||
	CvssScoreOver      float64
 | 
			
		||||
	IgnoreUnscoredCves bool
 | 
			
		||||
 | 
			
		||||
	SSHExternal bool
 | 
			
		||||
 | 
			
		||||
	HTTPProxy string `valid:"url"`
 | 
			
		||||
	DBPath    string
 | 
			
		||||
	CveDBPath string
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,7 @@ func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	Log.Debugf("Not FreeBSD. Host: %s:%s", c.Host, c.Port)
 | 
			
		||||
	return false, bsd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -108,19 +108,19 @@ func (s CvePacksList) Less(i, j int) bool {
 | 
			
		||||
 | 
			
		||||
func detectOS(c config.ServerInfo) (osType osTypeInterface) {
 | 
			
		||||
	var itsMe bool
 | 
			
		||||
	itsMe, osType = detectDebian(c)
 | 
			
		||||
	if itsMe {
 | 
			
		||||
	if itsMe, osType = detectDebian(c); itsMe {
 | 
			
		||||
		Log.Debugf("Debian like Linux. Host: %s:%s", c.Host, c.Port)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	itsMe, osType = detectRedhat(c)
 | 
			
		||||
	if itsMe {
 | 
			
		||||
	if itsMe, osType = detectRedhat(c); itsMe {
 | 
			
		||||
		Log.Debugf("Redhat like Linux. Host: %s:%s", c.Host, c.Port)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	itsMe, osType = detectFreebsd(c)
 | 
			
		||||
	if itsMe {
 | 
			
		||||
	if itsMe, osType = detectFreebsd(c); itsMe {
 | 
			
		||||
		Log.Debugf("FreeBSD. Host: %s:%s", c.Host, c.Port)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	osType.setServerInfo(c)
 | 
			
		||||
	osType.setErrs([]error{fmt.Errorf("Unknown OS Type")})
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -177,18 +177,21 @@ func detectServerOSes() (oses []osTypeInterface, err error) {
 | 
			
		||||
		}(s)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(300 * time.Second)
 | 
			
		||||
	timeout := time.After(60 * time.Second)
 | 
			
		||||
	for i := 0; i < len(config.Conf.Servers); i++ {
 | 
			
		||||
		select {
 | 
			
		||||
		case res := <-osTypeChan:
 | 
			
		||||
			if 0 < len(res.getErrs()) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			Log.Infof("(%d/%d) Detected %s: %s",
 | 
			
		||||
				i+1, len(config.Conf.Servers),
 | 
			
		||||
				res.getServerInfo().ServerName,
 | 
			
		||||
				res.getDistributionInfo())
 | 
			
		||||
			oses = append(oses, res)
 | 
			
		||||
			if 0 < len(res.getErrs()) {
 | 
			
		||||
				Log.Infof("(%d/%d) Failed %s",
 | 
			
		||||
					i+1, len(config.Conf.Servers),
 | 
			
		||||
					res.getServerInfo().ServerName)
 | 
			
		||||
			} else {
 | 
			
		||||
				Log.Infof("(%d/%d) Detected %s: %s",
 | 
			
		||||
					i+1, len(config.Conf.Servers),
 | 
			
		||||
					res.getServerInfo().ServerName,
 | 
			
		||||
					res.getDistributionInfo())
 | 
			
		||||
			}
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			msg := "Timeout occurred while detecting"
 | 
			
		||||
			Log.Error(msg)
 | 
			
		||||
@@ -208,19 +211,16 @@ func detectServerOSes() (oses []osTypeInterface, err error) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	errorOccurred := false
 | 
			
		||||
	errs := []error{}
 | 
			
		||||
	for _, osi := range oses {
 | 
			
		||||
		if errs := osi.getErrs(); 0 < len(errs) {
 | 
			
		||||
			errorOccurred = true
 | 
			
		||||
			Log.Errorf("Some errors occurred on %s",
 | 
			
		||||
				osi.getServerInfo().ServerName)
 | 
			
		||||
			for _, err := range errs {
 | 
			
		||||
				Log.Error(err)
 | 
			
		||||
			}
 | 
			
		||||
		if 0 < len(osi.getErrs()) {
 | 
			
		||||
			errs = append(errs, fmt.Errorf(
 | 
			
		||||
				"Error occurred on %s. errs: %s",
 | 
			
		||||
				osi.getServerInfo().ServerName, osi.getErrs()))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if errorOccurred {
 | 
			
		||||
		return oses, fmt.Errorf("Some errors occurred")
 | 
			
		||||
	if 0 < len(errs) {
 | 
			
		||||
		return oses, fmt.Errorf("%s", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -234,10 +234,11 @@ func detectContainerOSes() (oses []osTypeInterface, err error) {
 | 
			
		||||
		}(s)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(300 * time.Second)
 | 
			
		||||
	timeout := time.After(60 * time.Second)
 | 
			
		||||
	for i := 0; i < len(config.Conf.Servers); i++ {
 | 
			
		||||
		select {
 | 
			
		||||
		case res := <-osTypesChan:
 | 
			
		||||
			oses = append(oses, res...)
 | 
			
		||||
			for _, osi := range res {
 | 
			
		||||
				if 0 < len(osi.getErrs()) {
 | 
			
		||||
					continue
 | 
			
		||||
@@ -247,7 +248,6 @@ func detectContainerOSes() (oses []osTypeInterface, err error) {
 | 
			
		||||
					sinfo.Container.ContainerID, sinfo.Container.Name,
 | 
			
		||||
					sinfo.ServerName, osi.getDistributionInfo())
 | 
			
		||||
			}
 | 
			
		||||
			oses = append(oses, res...)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			msg := "Timeout occurred while detecting"
 | 
			
		||||
			Log.Error(msg)
 | 
			
		||||
@@ -267,19 +267,16 @@ func detectContainerOSes() (oses []osTypeInterface, err error) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	errorOccurred := false
 | 
			
		||||
	errs := []error{}
 | 
			
		||||
	for _, osi := range oses {
 | 
			
		||||
		if errs := osi.getErrs(); 0 < len(errs) {
 | 
			
		||||
			errorOccurred = true
 | 
			
		||||
			Log.Errorf("Some errors occurred on %s",
 | 
			
		||||
				osi.getServerInfo().ServerName)
 | 
			
		||||
			for _, err := range errs {
 | 
			
		||||
				Log.Error(err)
 | 
			
		||||
			}
 | 
			
		||||
		if 0 < len(osi.getErrs()) {
 | 
			
		||||
			errs = append(errs, fmt.Errorf(
 | 
			
		||||
				"Error occurred on %s. errs: %s",
 | 
			
		||||
				osi.getServerInfo().ServerName, osi.getErrs()))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if errorOccurred {
 | 
			
		||||
		return oses, fmt.Errorf("Some errors occurred")
 | 
			
		||||
	if 0 < len(errs) {
 | 
			
		||||
		return oses, fmt.Errorf("%s", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										170
									
								
								scan/sshutil.go
									
									
									
									
									
								
							
							
						
						
									
										170
									
								
								scan/sshutil.go
									
									
									
									
									
								
							@@ -25,7 +25,10 @@ import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/ssh"
 | 
			
		||||
@@ -105,53 +108,21 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sshExec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (result sshResult) {
 | 
			
		||||
	// Setup Logger
 | 
			
		||||
	var logger *logrus.Entry
 | 
			
		||||
	if len(log) == 0 {
 | 
			
		||||
		level := logrus.InfoLevel
 | 
			
		||||
		if conf.Conf.Debug == true {
 | 
			
		||||
			level = logrus.DebugLevel
 | 
			
		||||
		}
 | 
			
		||||
		l := &logrus.Logger{
 | 
			
		||||
			Out:       os.Stderr,
 | 
			
		||||
			Formatter: new(logrus.TextFormatter),
 | 
			
		||||
			Hooks:     make(logrus.LevelHooks),
 | 
			
		||||
			Level:     level,
 | 
			
		||||
		}
 | 
			
		||||
		logger = logrus.NewEntry(l)
 | 
			
		||||
	} else {
 | 
			
		||||
		logger = log[0]
 | 
			
		||||
	}
 | 
			
		||||
	c.SudoOpt.ExecBySudo = true
 | 
			
		||||
	var err error
 | 
			
		||||
	if sudo && c.User != "root" && !c.IsContainer() {
 | 
			
		||||
		switch {
 | 
			
		||||
		case c.SudoOpt.ExecBySudo:
 | 
			
		||||
			cmd = fmt.Sprintf("echo %s | sudo -S %s", c.Password, cmd)
 | 
			
		||||
		case c.SudoOpt.ExecBySudoSh:
 | 
			
		||||
			cmd = fmt.Sprintf("echo %s | sudo sh -c '%s'", c.Password, cmd)
 | 
			
		||||
		default:
 | 
			
		||||
			logger.Panicf("sudoOpt is invalid. SudoOpt: %v", c.SudoOpt)
 | 
			
		||||
		}
 | 
			
		||||
	if runtime.GOOS == "windows" || !conf.Conf.SSHExternal {
 | 
			
		||||
		return sshExecNative(c, cmd, sudo, log...)
 | 
			
		||||
	}
 | 
			
		||||
	return sshExecExternal(c, cmd, sudo, log...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	if c.Family != "FreeBSD" {
 | 
			
		||||
		// set pipefail option. Bash only
 | 
			
		||||
		// http://unix.stackexchange.com/questions/14270/get-exit-status-of-process-thats-piped-to-another
 | 
			
		||||
		cmd = fmt.Sprintf("set -o pipefail; %s", cmd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.IsContainer() {
 | 
			
		||||
		switch c.Container.Type {
 | 
			
		||||
		case "", "docker":
 | 
			
		||||
			cmd = fmt.Sprintf(`docker exec %s /bin/bash -c "%s"`, c.Container.ContainerID, cmd)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
func sshExecNative(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (result sshResult) {
 | 
			
		||||
	logger := getSSHLogger(log...)
 | 
			
		||||
 | 
			
		||||
	cmd = decolateCmd(c, cmd, sudo)
 | 
			
		||||
	logger.Debugf("Command: %s",
 | 
			
		||||
		strings.Replace(maskPassword(cmd, c.Password), "\n", "", -1))
 | 
			
		||||
 | 
			
		||||
	var client *ssh.Client
 | 
			
		||||
	var err error
 | 
			
		||||
	client, err = sshConnect(c)
 | 
			
		||||
	defer client.Close()
 | 
			
		||||
 | 
			
		||||
@@ -200,12 +171,125 @@ func sshExec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (re
 | 
			
		||||
	result.Port = c.Port
 | 
			
		||||
 | 
			
		||||
	logger.Debugf(
 | 
			
		||||
		"SSH executed. cmd: %s, status: %#v\nstdout: \n%s\nstderr: \n%s",
 | 
			
		||||
		maskPassword(cmd, c.Password), err, result.Stdout, result.Stderr)
 | 
			
		||||
		"SSH executed. cmd: %s, err: %#v, status: %d\nstdout: \n%s\nstderr: \n%s",
 | 
			
		||||
		maskPassword(cmd, c.Password), err, result.ExitStatus, result.Stdout, result.Stderr)
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (result sshResult) {
 | 
			
		||||
	logger := getSSHLogger(log...)
 | 
			
		||||
 | 
			
		||||
	sshBinaryPath, err := exec.LookPath("ssh")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Debug("Failed to find SSH binary, using native Go implementation")
 | 
			
		||||
		return sshExecNative(c, cmd, sudo, log...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defaultSSHArgs := []string{
 | 
			
		||||
		"-o", "StrictHostKeyChecking=no",
 | 
			
		||||
		"-o", "UserKnownHostsFile=/dev/null",
 | 
			
		||||
		"-o", "LogLevel=quiet",
 | 
			
		||||
		"-o", "ConnectionAttempts=3",
 | 
			
		||||
		"-o", "ConnectTimeout=10",
 | 
			
		||||
		"-o", "ControlMaster=no",
 | 
			
		||||
		"-o", "ControlPath=none",
 | 
			
		||||
 | 
			
		||||
		// TODO ssh session multiplexing
 | 
			
		||||
		//  "-o", "ControlMaster=auto",
 | 
			
		||||
		//  "-o", `ControlPath=~/.ssh/controlmaster-%r-%h.%p`,
 | 
			
		||||
		//  "-o", "Controlpersist=30m",
 | 
			
		||||
	}
 | 
			
		||||
	args := append(defaultSSHArgs, fmt.Sprintf("%s@%s", c.User, c.Host))
 | 
			
		||||
	args = append(args, "-p", c.Port)
 | 
			
		||||
 | 
			
		||||
	//  if conf.Conf.Debug {
 | 
			
		||||
	//      args = append(args, "-v")
 | 
			
		||||
	//  }
 | 
			
		||||
 | 
			
		||||
	if 0 < len(c.KeyPath) {
 | 
			
		||||
		args = append(args, "-i", c.KeyPath)
 | 
			
		||||
		args = append(args, "-o", "PasswordAuthentication=no")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd = decolateCmd(c, cmd, sudo)
 | 
			
		||||
	args = append(args, cmd)
 | 
			
		||||
	execCmd := exec.Command(sshBinaryPath, args...)
 | 
			
		||||
 | 
			
		||||
	var stdoutBuf, stderrBuf bytes.Buffer
 | 
			
		||||
	execCmd.Stdout = &stdoutBuf
 | 
			
		||||
	execCmd.Stderr = &stderrBuf
 | 
			
		||||
	if err := execCmd.Run(); err != nil {
 | 
			
		||||
		if e, ok := err.(*exec.ExitError); ok {
 | 
			
		||||
			if s, ok := e.Sys().(syscall.WaitStatus); ok {
 | 
			
		||||
				result.ExitStatus = s.ExitStatus()
 | 
			
		||||
			} else {
 | 
			
		||||
				result.ExitStatus = 998
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			result.ExitStatus = 999
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		result.ExitStatus = 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result.Stdout = stdoutBuf.String()
 | 
			
		||||
	result.Stderr = stderrBuf.String()
 | 
			
		||||
	result.Host = c.Host
 | 
			
		||||
	result.Port = c.Port
 | 
			
		||||
 | 
			
		||||
	logger.Debugf(
 | 
			
		||||
		"SSH executed. cmd: %s %s, err: %#v, status: %d\nstdout: \n%s\nstderr: \n%s",
 | 
			
		||||
		sshBinaryPath,
 | 
			
		||||
		maskPassword(strings.Join(args, " "), c.Password),
 | 
			
		||||
		err, result.ExitStatus, result.Stdout, result.Stderr)
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getSSHLogger(log ...*logrus.Entry) *logrus.Entry {
 | 
			
		||||
	if len(log) == 0 {
 | 
			
		||||
		level := logrus.InfoLevel
 | 
			
		||||
		if conf.Conf.Debug == true {
 | 
			
		||||
			level = logrus.DebugLevel
 | 
			
		||||
		}
 | 
			
		||||
		l := &logrus.Logger{
 | 
			
		||||
			Out:       os.Stderr,
 | 
			
		||||
			Formatter: new(logrus.TextFormatter),
 | 
			
		||||
			Hooks:     make(logrus.LevelHooks),
 | 
			
		||||
			Level:     level,
 | 
			
		||||
		}
 | 
			
		||||
		return logrus.NewEntry(l)
 | 
			
		||||
	}
 | 
			
		||||
	return log[0]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func decolateCmd(c conf.ServerInfo, cmd string, sudo bool) string {
 | 
			
		||||
	c.SudoOpt.ExecBySudo = true
 | 
			
		||||
	if sudo && c.User != "root" && !c.IsContainer() {
 | 
			
		||||
		switch {
 | 
			
		||||
		case c.SudoOpt.ExecBySudo:
 | 
			
		||||
			cmd = fmt.Sprintf("echo %s | sudo -S %s", c.Password, cmd)
 | 
			
		||||
		case c.SudoOpt.ExecBySudoSh:
 | 
			
		||||
			cmd = fmt.Sprintf("echo %s | sudo sh -c '%s'", c.Password, cmd)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.Family != "FreeBSD" {
 | 
			
		||||
		// set pipefail option. Bash only
 | 
			
		||||
		// http://unix.stackexchange.com/questions/14270/get-exit-status-of-process-thats-piped-to-another
 | 
			
		||||
		cmd = fmt.Sprintf("set -o pipefail; %s", cmd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.IsContainer() {
 | 
			
		||||
		switch c.Container.Type {
 | 
			
		||||
		case "", "docker":
 | 
			
		||||
			cmd = fmt.Sprintf(`docker exec %s /bin/bash -c "%s"`, c.Container.ContainerID, cmd)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getAgentAuth() (auth ssh.AuthMethod, ok bool) {
 | 
			
		||||
	if sock := os.Getenv("SSH_AUTH_SOCK"); len(sock) > 0 {
 | 
			
		||||
		if agconn, err := net.Dial("unix", sock); err == nil {
 | 
			
		||||
@@ -317,7 +401,7 @@ func parsePemBlock(block *pem.Block) (interface{}, error) {
 | 
			
		||||
	case "DSA PRIVATE KEY":
 | 
			
		||||
		return ssh.ParseDSAPrivateKey(block.Bytes)
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("rtop: unsupported key type %q", block.Type)
 | 
			
		||||
		return nil, fmt.Errorf("Unsupported key type %q", block.Type)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user