Add configtest subcommand. skip un-ssh-able servers.

This commit is contained in:
kota kanbe
2016-07-14 20:39:14 +09:00
parent f2ddafc718
commit 34d6d6e709
13 changed files with 417 additions and 207 deletions

View File

@@ -115,9 +115,7 @@ func (l *base) dockerPs(option string) (string, error) {
cmd := fmt.Sprintf("docker ps %s", option)
r := l.ssh(cmd, noSudo)
if !r.isSuccess() {
return "", fmt.Errorf(
"Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
return "", fmt.Errorf("Failed to SSH: %s", r)
}
return r.Stdout, nil
}

View File

@@ -53,13 +53,14 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
deb.setServerInfo(c)
if r := sshExec(c, "ls /etc/debian_version", noSudo); !r.isSuccess() {
if r.Error != nil {
return false, deb, r.Error
}
if r.ExitStatus == 255 {
return false, deb, fmt.Errorf(
"Unable to connect via SSH. Check SSH settings. servername: %s, %s@%s:%s, status: %d, stdout: %s, stderr: %s",
c.ServerName, c.User, c.Host, c.Port, r.ExitStatus, r.Stdout, r.Stderr,
)
"Unable to connect via SSH. Check SSH settings. %s", r)
}
Log.Debugf("Not Debian like Linux. Host: %s:%s", c.Host, c.Port)
Log.Debugf("Not Debian like Linux. %s", r)
return false, deb, nil
}
@@ -75,8 +76,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
if len(result) == 0 {
deb.setDistributionInfo("debian/ubuntu", "unknown")
Log.Warnf(
"Unknown Debian/Ubuntu version. lsb_release -ir: %s, Host: %s:%s",
r.Stdout, c.Host, c.Port)
"Unknown Debian/Ubuntu version. lsb_release -ir: %s", r)
} else {
distro := strings.ToLower(trim(result[1]))
deb.setDistributionInfo(distro, trim(result[2]))
@@ -95,8 +95,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
result := re.FindStringSubmatch(trim(r.Stdout))
if len(result) == 0 {
Log.Warnf(
"Unknown Debian/Ubuntu. cat /etc/lsb-release: %s, Host: %s:%s",
r.Stdout, c.Host, c.Port)
"Unknown Debian/Ubuntu. cat /etc/lsb-release: %s", r)
deb.setDistributionInfo("debian/ubuntu", "unknown")
} else {
distro := strings.ToLower(trim(result[1]))
@@ -112,7 +111,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
return true, deb, nil
}
Log.Debugf("Not Debian like Linux. Host: %s:%s", c.Host, c.Port)
Log.Debugf("Not Debian like Linux: %s", c.ServerName)
return false, deb, nil
}
@@ -126,8 +125,7 @@ func (o *debian) install() error {
o.log.Infof("apt-get update...")
cmd := util.PrependProxyEnv("apt-get update")
if r := o.ssh(cmd, sudo); !r.isSuccess() {
msg := fmt.Sprintf("Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
msg := fmt.Sprintf("Failed to SSH: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
@@ -136,8 +134,7 @@ func (o *debian) install() error {
// install aptitude
cmd = util.PrependProxyEnv("apt-get install --force-yes -y aptitude")
if r := o.ssh(cmd, sudo); !r.isSuccess() {
msg := fmt.Sprintf("Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
msg := fmt.Sprintf("Failed to SSH: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
@@ -158,8 +155,7 @@ func (o *debian) install() error {
cmd = util.PrependProxyEnv(
"apt-get install --force-yes -y unattended-upgrades")
if r := o.ssh(cmd, sudo); !r.isSuccess() {
msg := fmt.Sprintf("Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
msg := fmt.Sprintf("Failed to SSH: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
@@ -189,9 +185,7 @@ func (o *debian) scanPackages() error {
func (o *debian) scanInstalledPackages() (packs []models.PackageInfo, err error) {
r := o.ssh("dpkg-query -W", noSudo)
if !r.isSuccess() {
return packs, fmt.Errorf(
"Failed to scan packages. status: %d, stdout:%s, stderr: %s",
r.ExitStatus, r.Stdout, r.Stderr)
return packs, fmt.Errorf("Failed to SSH: %s", r)
}
// e.g.
@@ -232,7 +226,7 @@ func (o *debian) checkRequiredPackagesInstalled() error {
if o.Family == "debian" {
if r := o.ssh("test -f /usr/bin/aptitude", noSudo); !r.isSuccess() {
msg := "aptitude is not installed"
msg := fmt.Sprintf("aptitude is not installed: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
@@ -243,7 +237,7 @@ func (o *debian) checkRequiredPackagesInstalled() error {
}
if r := o.ssh("type unattended-upgrade", noSudo); !r.isSuccess() {
msg := "unattended-upgrade is not installed"
msg := fmt.Sprintf("unattended-upgrade is not installed: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
@@ -255,10 +249,7 @@ func (o *debian) scanUnsecurePackages(packs []models.PackageInfo) ([]CvePacksInf
// cmd := prependProxyEnv(conf.HTTPProxy, "apt-get update | cat; echo 1")
cmd := util.PrependProxyEnv("apt-get update")
if r := o.ssh(cmd, sudo); !r.isSuccess() {
return nil, fmt.Errorf(
"Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr,
)
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
var upgradablePackNames []string
@@ -325,9 +316,7 @@ func (o *debian) fillCandidateVersion(packs []models.PackageInfo) ([]models.Pack
cmd := fmt.Sprintf("apt-cache policy %s", p.Name)
r := o.ssh(cmd, sudo)
if !r.isSuccess() {
errChan <- fmt.Errorf(
"Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
errChan <- fmt.Errorf("Failed to SSH: %s.", r)
return
}
ver, err := o.parseAptCachePolicy(r.Stdout, p.Name)
@@ -473,7 +462,6 @@ func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePac
}()
timeout := time.After(30 * 60 * time.Second)
concurrency := 10
tasks := util.GenWorkers(concurrency)
for range unsecurePacks {
@@ -554,9 +542,7 @@ func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]string, error) {
r := o.ssh(cmd, noSudo)
if !r.isSuccess() {
o.log.Warnf(
"Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
o.log.Warnf("Failed to SSH: %s", r)
// Ignore this Error.
return nil, nil

View File

@@ -35,7 +35,7 @@ func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
}
}
}
Log.Debugf("Not FreeBSD. Host: %s:%s", c.Host, c.Port)
Log.Debugf("Not FreeBSD. servernam: %s", c.ServerName)
return false, bsd
}
@@ -69,8 +69,7 @@ func (o *bsd) scanInstalledPackages() ([]models.PackageInfo, error) {
cmd := util.PrependProxyEnv("pkg version -v")
r := o.ssh(cmd, noSudo)
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to %s. status: %d, stdout:%s, Stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
return o.parsePkgVersion(r.Stdout), nil
}
@@ -80,15 +79,13 @@ func (o *bsd) scanUnsecurePackages() (cvePacksList []CvePacksInfo, err error) {
cmd := "rm -f " + vulndbPath
r := o.ssh(cmd, noSudo)
if !r.isSuccess(0) {
return nil, fmt.Errorf("Failed to %s. status: %d, stdout:%s, Stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
cmd = util.PrependProxyEnv("pkg audit -F -r -f " + vulndbPath)
r = o.ssh(cmd, noSudo)
if !r.isSuccess(0, 1) {
return nil, fmt.Errorf("Failed to %s. status: %d, stdout:%s, Stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
if r.ExitStatus == 0 {
// no vulnerabilities

View File

@@ -56,7 +56,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
if r := sshExec(c, "ls /etc/fedora-release", noSudo); r.isSuccess() {
red.setDistributionInfo("fedora", "unknown")
Log.Warn("Fedora not tested yet. Host: %s:%s", c.Host, c.Port)
Log.Warn("Fedora not tested yet: %s", r)
return true, red
}
@@ -69,9 +69,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
re, _ := regexp.Compile(`(.*) release (\d[\d.]*)`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
Log.Warn(
"Failed to parse RedHat/CentOS version. stdout: %s, Host: %s:%s",
r.Stdout, c.Host, c.Port)
Log.Warn("Failed to parse RedHat/CentOS version: %s", r)
return true, red
}
@@ -100,7 +98,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
return true, red
}
Log.Debugf("Not RedHat like Linux. Host: %s:%s", c.Host, c.Port)
Log.Debugf("Not RedHat like Linux. servername: %s", c.ServerName)
return false, red
}
@@ -132,9 +130,7 @@ func (o *redhat) installYumPluginSecurity() error {
o.log.Info("Installing yum-plugin-security...")
cmd := util.PrependProxyEnv("yum install -y yum-plugin-security")
if r := o.ssh(cmd, sudo); !r.isSuccess() {
return fmt.Errorf(
"Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
return fmt.Errorf("Failed to SSH: %s", r)
}
return nil
}
@@ -167,9 +163,7 @@ func (o *redhat) installYumChangelog() error {
o.log.Infof("Installing %s...", packName)
cmd = util.PrependProxyEnv("yum install -y " + packName)
if r := o.ssh(cmd, sudo); !r.isSuccess() {
return fmt.Errorf(
"Failed to install %s. status: %d, stdout: %s, stderr: %s",
packName, r.ExitStatus, r.Stdout, r.Stderr)
return fmt.Errorf("Failed to SSH: %s", r)
}
o.log.Infof("Installed: %s", packName)
}
@@ -291,9 +285,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (CvePacksList, error)
r := o.ssh(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess(0, 100) {
//returns an exit code of 100 if there are available updates.
return nil, fmt.Errorf(
"Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
// get Updateble package name, installed, candidate version.
@@ -472,9 +464,7 @@ func (o *redhat) getChangelog(packageNames string) (stdout string, err error) {
r := o.ssh(command, sudo)
if !r.isSuccess(0, 1) {
return "", fmt.Errorf(
"Failed to get changelog. status: %d, stdout: %s, stderr: %s",
r.ExitStatus, r.Stdout, r.Stderr)
return "", fmt.Errorf("Failed to SSH: %s", r)
}
return r.Stdout, nil
}
@@ -497,18 +487,14 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
cmd := "yum --color=never repolist"
r := o.ssh(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess() {
return nil, fmt.Errorf(
"Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
// get advisoryID(RHSA, ALAS) - package name,version
cmd = "yum --color=never updateinfo list available --security"
r = o.ssh(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess() {
return nil, fmt.Errorf(
"Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
advIDPackNamesList, err := o.parseYumUpdateinfoListAvailable(r.Stdout)
@@ -518,9 +504,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
r = o.ssh(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess(0, 100) {
//returns an exit code of 100 if there are available updates.
return nil, fmt.Errorf(
"Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
updatable, err := o.parseYumCheckUpdateLines(r.Stdout)
if err != nil {
@@ -547,9 +531,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
cmd = "yum --color=never updateinfo --security update"
r = o.ssh(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess() {
return nil, fmt.Errorf(
"Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
advisoryCveIDsList, err := o.parseYumUpdateinfo(r.Stdout)
if err != nil {

View File

@@ -109,8 +109,8 @@ func (s CvePacksList) Less(i, j int) bool {
func detectOS(c config.ServerInfo) (osType osTypeInterface) {
var itsMe bool
var fatalErr error
itsMe, osType, fatalErr = detectDebian(c)
itsMe, osType, fatalErr = detectDebian(c)
if fatalErr != nil {
osType.setServerInfo(c)
osType.setErrs([]error{fatalErr})
@@ -134,74 +134,61 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) {
}
// InitServers detect the kind of OS distribution of target servers
func InitServers(localLogger *logrus.Entry) error {
func InitServers(localLogger *logrus.Entry) {
Log = localLogger
servers = detectServerOSes()
hosts, err := detectServerOSes()
if err != nil {
return fmt.Errorf("Failed to detect server OSes. err: %s", err)
}
servers = hosts
Log.Info("Detecting Container OS...")
containers, err := detectContainerOSes()
if err != nil {
return fmt.Errorf("Failed to detect Container OSes. err: %s", err)
}
containers := detectContainerOSes()
servers = append(servers, containers...)
Log.Info("Detecting Platforms...")
errs := detectPlatforms()
if 0 < len(errs) {
// Only logging
Log.Errorf("Failed to detect platforms. err: %v", errs)
}
for i, s := range servers {
Log.Info("SSH-able servers are below...")
for _, s := range servers {
if s.getServerInfo().IsContainer() {
Log.Infof("(%d/%d) %s on %s is running on %s",
i+1, len(servers),
fmt.Printf("%s@%s ",
s.getServerInfo().Container.Name,
s.getServerInfo().ServerName,
s.getPlatform().Name,
)
} else {
Log.Infof("(%d/%d) %s is running on %s",
i+1, len(servers),
s.getServerInfo().ServerName,
s.getPlatform().Name,
)
fmt.Printf("%s ", s.getServerInfo().ServerName)
}
}
return nil
fmt.Printf("\n")
}
func detectServerOSes() (oses []osTypeInterface, err error) {
func detectServerOSes() (sshAbleOses []osTypeInterface) {
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) {
//TODO handling Unknown OS
defer func() {
if p := recover(); p != nil {
Log.Debugf("Panic: %s on %s", p, s.ServerName)
}
}()
osTypeChan <- detectOS(s)
}(s)
}
timeout := time.After(60 * time.Second)
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.Infof("(%d/%d) Failed %s",
Log.Errorf("(%d/%d) Failed: %s, err: %s",
i+1, len(config.Conf.Servers),
res.getServerInfo().ServerName)
res.getServerInfo().ServerName,
res.getErrs())
} else {
Log.Infof("(%d/%d) Detected %s: %s",
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"
msg := "Timed out while detecting servers"
Log.Error(msg)
for servername := range config.Conf.Servers {
found := false
@@ -212,52 +199,56 @@ func detectServerOSes() (oses []osTypeInterface, err error) {
}
}
if !found {
Log.Errorf("Failed to detect. servername: %s", servername)
Log.Errorf("(%d/%d) Timed out: %s",
i+1, len(config.Conf.Servers),
servername)
i++
}
}
return oses, fmt.Errorf(msg)
}
}
errs := []error{}
for _, osi := range oses {
if 0 < len(osi.getErrs()) {
errs = append(errs, fmt.Errorf(
"Error occurred on %s. errs: %s",
osi.getServerInfo().ServerName, osi.getErrs()))
for _, o := range oses {
if len(o.getErrs()) == 0 {
sshAbleOses = append(sshAbleOses, o)
}
}
if 0 < len(errs) {
return oses, fmt.Errorf("%s", errs)
}
return
}
func detectContainerOSes() (oses []osTypeInterface, err error) {
func detectContainerOSes() (actives []osTypeInterface) {
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",
p, s.getServerInfo().ServerName)
}
}()
osTypesChan <- detectContainerOSesOnServer(s)
}(s)
}
timeout := time.After(60 * time.Second)
for i := 0; i < len(config.Conf.Servers); i++ {
var oses []osTypeInterface
timeout := time.After(30 * time.Second)
for i := 0; i < len(servers); i++ {
select {
case res := <-osTypesChan:
oses = append(oses, res...)
for _, osi := range res {
sinfo := osi.getServerInfo()
if 0 < len(osi.getErrs()) {
Log.Errorf("Failed: %s err: %s", sinfo.ServerName, osi.getErrs())
continue
}
sinfo := osi.getServerInfo()
Log.Infof("Detected %s/%s on %s: %s",
sinfo.Container.ContainerID, sinfo.Container.Name,
sinfo.ServerName, osi.getDistributionInfo())
oses = append(oses, res...)
Log.Infof("Detected: %s@%s: %s",
sinfo.Container.Name, sinfo.ServerName, osi.getDistributionInfo())
}
case <-timeout:
msg := "Timeout occurred while detecting"
msg := "Timed out while detecting containers"
Log.Error(msg)
for servername := range config.Conf.Servers {
found := false
@@ -268,24 +259,17 @@ func detectContainerOSes() (oses []osTypeInterface, err error) {
}
}
if !found {
Log.Errorf("Failed to detect. servername: %s", servername)
Log.Errorf("Timed out: %s", servername)
}
}
return oses, fmt.Errorf(msg)
}
}
errs := []error{}
for _, osi := range oses {
if 0 < len(osi.getErrs()) {
errs = append(errs, fmt.Errorf(
"Error occurred on %s. errs: %s",
osi.getServerInfo().ServerName, osi.getErrs()))
for _, o := range oses {
if len(o.getErrs()) == 0 {
actives = append(actives, o)
}
}
if 0 < len(errs) {
return oses, fmt.Errorf("%s", errs)
}
return
}
@@ -361,6 +345,33 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn
return oses
}
// 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)
}
for i, s := range servers {
if s.getServerInfo().IsContainer() {
Log.Infof("(%d/%d) %s on %s is running on %s",
i+1, len(servers),
s.getServerInfo().Container.Name,
s.getServerInfo().ServerName,
s.getPlatform().Name,
)
} else {
Log.Infof("(%d/%d) %s is running on %s",
i+1, len(servers),
s.getServerInfo().ServerName,
s.getPlatform().Name,
)
}
}
return
}
func detectPlatforms() []error {
timeoutSec := 1 * 60
return parallelSSHExec(func(o osTypeInterface) error {
@@ -410,7 +421,7 @@ func checkRequiredPackagesInstalled() []error {
}
func scanPackages() []error {
timeoutSec := 30 * 60
timeoutSec := 120 * 60
return parallelSSHExec(func(o osTypeInterface) error {
return o.scanPackages()
}, timeoutSec)

View File

@@ -37,18 +37,30 @@ import (
"github.com/Sirupsen/logrus"
"github.com/cenkalti/backoff"
conf "github.com/future-architect/vuls/config"
"github.com/k0kubun/pp"
"github.com/future-architect/vuls/util"
)
type sshResult struct {
Servername string
Host string
Port string
Cmd string
Stdout string
Stderr string
ExitStatus int
Error error
}
func (s sshResult) String() string {
return fmt.Sprintf(
"SSHResult: servername: %s, cmd: %s, exitstatus: %d, stdout: %s, stderr: %s, err: %s",
s.Servername, s.Cmd, s.ExitStatus, s.Stdout, s.Stderr, s.Error)
}
func (s sshResult) isSuccess(expectedStatusCodes ...int) bool {
if s.Error != nil {
return false
}
if len(expectedStatusCodes) == 0 {
return s.ExitStatus == 0
}
@@ -71,6 +83,12 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []
defer close(errChan)
for _, s := range servers {
go func(s osTypeInterface) {
defer func() {
if p := recover(); p != nil {
logrus.Debugf("Panic: %s on %s",
p, s.getServerInfo().ServerName)
}
}()
if err := fn(s); err != nil {
errChan <- fmt.Errorf("%s@%s:%s: %s",
s.getServerInfo().User,
@@ -109,28 +127,35 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []
func sshExec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (result sshResult) {
if runtime.GOOS == "windows" || !conf.Conf.SSHExternal {
return sshExecNative(c, cmd, sudo, log...)
result = sshExecNative(c, cmd, sudo)
} else {
result = sshExecExternal(c, cmd, sudo)
}
return sshExecExternal(c, cmd, sudo, log...)
logger := getSSHLogger(log...)
logger.Debug(result)
return
}
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))
func sshExecNative(c conf.ServerInfo, cmd string, sudo bool) (result sshResult) {
result.Servername = c.ServerName
result.Host = c.Host
result.Port = c.Port
var client *ssh.Client
var err error
client, err = sshConnect(c)
if client, err = sshConnect(c); err != nil {
result.Error = err
result.ExitStatus = 999
return
}
defer client.Close()
var session *ssh.Session
if session, err = client.NewSession(); err != nil {
logger.Errorf("Failed to new session. err: %s, c: %s",
err,
pp.Sprintf("%v", c))
result.Error = fmt.Errorf(
"Failed to create a new session. servername: %s, err: %s",
c.ServerName, err)
result.ExitStatus = 999
return
}
@@ -143,10 +168,9 @@ func sshExecNative(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entr
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
if err = session.RequestPty("xterm", 400, 256, modes); err != nil {
logger.Errorf("Failed to request for pseudo terminal. err: %s, c: %s",
err,
pp.Sprintf("%v", c))
result.Error = fmt.Errorf(
"Failed to request for pseudo terminal. servername: %s, err: %s",
c.ServerName, err)
result.ExitStatus = 999
return
}
@@ -155,6 +179,7 @@ func sshExecNative(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entr
session.Stdout = &stdoutBuf
session.Stderr = &stderrBuf
cmd = decolateCmd(c, cmd, sudo)
if err := session.Run(cmd); err != nil {
if exitErr, ok := err.(*ssh.ExitError); ok {
result.ExitStatus = exitErr.ExitStatus()
@@ -167,23 +192,14 @@ func sshExecNative(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entr
result.Stdout = stdoutBuf.String()
result.Stderr = stderrBuf.String()
result.Host = c.Host
result.Port = c.Port
logger.Debugf(
"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)
result.Cmd = strings.Replace(maskPassword(cmd, c.Password), "\n", "", -1)
return
}
func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (result sshResult) {
logger := getSSHLogger(log...)
func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool) (result sshResult) {
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...)
return sshExecNative(c, cmd, sudo)
}
defaultSSHArgs := []string{
@@ -235,31 +251,17 @@ func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.En
result.Stdout = stdoutBuf.String()
result.Stderr = stderrBuf.String()
result.Servername = c.ServerName
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)
result.Cmd = fmt.Sprintf("%s %s",
sshBinaryPath, maskPassword(strings.Join(args, " "), c.Password))
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 util.NewCustomLogger(conf.ServerInfo{})
}
return log[0]
}
@@ -314,15 +316,13 @@ func tryAgentConnect(c conf.ServerInfo) *ssh.Client {
}
func sshConnect(c conf.ServerInfo) (client *ssh.Client, err error) {
if client = tryAgentConnect(c); client != nil {
return client, nil
}
var auths = []ssh.AuthMethod{}
if auths, err = addKeyAuth(auths, c.KeyPath, c.KeyPassword); err != nil {
logrus.Fatalf("Failed to add keyAuth. %s@%s:%s err: %s",
c.User, c.Host, c.Port, err)
return nil, err
}
if c.Password != "" {
@@ -336,8 +336,9 @@ func sshConnect(c conf.ServerInfo) (client *ssh.Client, err error) {
}
notifyFunc := func(e error, t time.Duration) {
logrus.Warnf("Failed to ssh %s@%s:%s err: %s, Retrying in %s...",
c.User, c.Host, c.Port, e, t)
logger := getSSHLogger()
logger.Debugf("Failed to Dial to %s, err: %s, Retrying in %s...",
c.ServerName, e, t)
}
err = backoff.RetryNotify(func() error {
if client, err = ssh.Dial("tcp", c.Host+":"+c.Port, config); err != nil {