Add local scan mode.

If the scan target server is localhost, Don't use SSH. #210
This commit is contained in:
Kota Kanbe
2017-01-12 16:35:11 +09:00
parent 910385b084
commit 20275a1063
18 changed files with 1857 additions and 207 deletions

View File

@@ -169,6 +169,7 @@ NVDから脆弱性データベースを取得する。
環境によって異なるが、AWS上では10分程度かかる。
```bash
$ cd $HOME
$ for i in {2002..2016}; do go-cve-dictionary fetchnvd -years $i; done
... snip ...
$ ls -alh cve.sqlite3
@@ -320,8 +321,18 @@ $ vuls tui
# Architecture
## A. Scan via SSH Mode
![Vuls-Architecture](img/vuls-architecture.png)
## B. Scan without SSH (Local Scan Mode)
Vulsをスキャン対象サーバにデプロイする。Vulsはローカルホストにコマンドを発行する(SSH経由ではない。スキャン結果のJSONを別サーバに集約する。スキャン結果の詳細化のためにはCVEデータベースへのアクセスが必要なので、事前にgo-cve-dictionaryをserver modeで起動しておく。
その集約サーバ上で、あなたはWebUIやTUIを用いて各スキャン対象サーバのスキャン結果を参照することが可能。
![Vuls-Architecture Local Scan Mode](img/vuls-architecture-localscan.png)
[詳細](#example-scan-via-shell-instead-of-ssh)
## [go-cve-dictionary](https://github.com/kotakanbe/go-cve-dictionary)
- NVDとJVN(日本語)から脆弱性データベースを取得し、SQLite3に格納する。
@@ -532,11 +543,13 @@ host = "172.31.4.82"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#containers = ["${running}"]
#ignoreCves = ["CVE-2016-6314"]
#optional = [
# ["key", "value"],
#]
#containers = ["${running}"]
#[servers.172-31-4-82.container]
#type = "lxd"
```
serversセクションの値は、defaultセクションの値よりも優先される。
@@ -547,9 +560,9 @@ host = "172.31.4.82"
- user: SSH username
- keyPath: SSH private key path
- cpeNames: see [Usage: Scan vulnerability of non-OS package](https://github.com/future-architect/vuls/blob/master/README.ja.md#usage-scan-vulnerability-of-non-os-package)
- containers: see [Usage: Scan Docker containers](https://github.com/future-architect/vuls/blob/master/README.ja.md#usage-scan-docker-containers)
- ignoreCves: CVE IDs that will not be reported. But output to JSON file.
- optional: JSONレポートに含めたい追加情報
- containers: see [Usage: Scan Docker containers](https://github.com/future-architect/vuls/blob/master/README.ja.md#usage-scan-docker-containers)
Vulsは各サーバにSSHで接続するが、Goのネイティブ実装と、OSコマンドの種類のSSH接続方法をサポートしている。
@@ -714,11 +727,29 @@ $ vuls scan server1 server2
- ーパスワードでsudoが実行可能
- configで定義されているサーバの中の、server1, server2のみスキャン
## Example: Scan Docker containers
## Example: Scan via shell instead of SSH.
DockerコンテナはSSHデーモンを起動しないで運用するケースが一般的
ローカルホストのスキャンする場合、SSHではなく直接コマンドの発行が可能
config.tomlのhostに`localhost または 127.0.0.1`かつ、portに`local`を設定する必要がある。
For more details, see [Architecture section](https://github.com/future-architect/vuls#architecture)
- config.toml
```
[servers]
[servers.localhost]
host = "localhost" # or "127.0.0.1"
port = "local"
```
## Example: Scan containers (Docker/LXD)
コンテナはSSHデーモンを起動しないで運用するケースが一般的。
[Docker Blog:Why you don't need to run SSHd in your Docker containers](https://blog.docker.com/2014/06/why-you-dont-need-to-run-sshd-in-docker/)
### Docker
Vulsは、DockerホストにSSHで接続し、`docker exec`でDockerコンテナにコマンドを発行して脆弱性をスキャンする。
詳細は、[Architecture section](https://github.com/future-architect/vuls#architecture)を参照
@@ -747,9 +778,24 @@ Vulsは、DockerホストにSSHで接続し、`docker exec`でDockerコンテナ
keyPath = "/home/username/.ssh/id_rsa"
containers = ["container_name_a", "4aa37a8b63b9"]
```
- コンテナのみをスキャンする場合(ホストはスキャンしない)
--containers-onlyオプションを指定する
### LXDコンテナをスキャンする場合
Vulsは、ホストにSSHで接続し、`lxc exec`でLXDコンテナにコマンドを発行して脆弱性をスキャンする。
```
[servers]
[servers.172-31-4-82]
host = "172.31.4.82"
user = "ec2-user"
keyPath = "/home/username/.ssh/id_rsa"
containers = ["${running}"]
[servers.172-31-4-82.container]
type = "lxd"
```
# Usage: Report

View File

@@ -173,6 +173,7 @@ Fetch vulnerability data from NVD.
It takes about 10 minutes (on AWS).
```bash
$ cd $HOME
$ for i in {2002..2016}; do go-cve-dictionary fetchnvd -years $i; done
... snip ...
$ ls -alh cve.sqlite3
@@ -319,8 +320,18 @@ see https://github.com/future-architect/vuls/tree/master/setup/docker
# Architecture
## A. Scan via SSH Mode
![Vuls-Architecture](img/vuls-architecture.png)
## B. Scan without SSH (Local Scan Mode)
Deploy Vuls to the scan target server. Vuls issues a command to the local host (not via SSH). Aggregate the JSON of the scan result into another server. Since it is necessary to access the CVE database in order to refine the scan result, start go-cve-dictionary in server mode beforehand.
On the aggregation server, you can refer to the scanning result of each scan target server using WebUI or TUI.
![Vuls-Architecture Local Scan Mode](img/vuls-architecture-localscan.png)
[Details](#example-scan-via-shell-instead-of-ssh)
## [go-cve-dictinary](https://github.com/kotakanbe/go-cve-dictionary)
- Fetch vulnerability information from NVD and JVN(Japanese), then insert into SQLite3 or MySQL.
@@ -445,11 +456,13 @@ host = "172.31.4.82"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#containers = ["${running}"]
#ignoreCves = ["CVE-2016-6313"]
#optional = [
# ["key", "value"],
#]
#containers = ["${running}"]
#[servers.172-31-4-82.container]
#type = "lxd"
```
You can customize your configuration using this template.
@@ -538,11 +551,13 @@ You can customize your configuration using this template.
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#containers = ["${running}"]
#ignoreCves = ["CVE-2016-6314"]
#optional = [
# ["key", "value"],
#]
#containers = ["${running}"]
#[servers.172-31-4-82.container]
#type = "lxd"
```
You can overwrite the default value specified in default section.
@@ -552,9 +567,9 @@ You can customize your configuration using this template.
- user: SSH username
- keyPath: SSH private key path
- cpeNames: see [Usage: Scan vulnerability of non-OS package](https://github.com/future-architect/vuls#usage-scan-vulnerability-of-non-os-package)
- containers: see [Usage: Scan Docker containers](https://github.com/future-architect/vuls#usage-scan-docker-containers)
- ignoreCves: CVE IDs that will not be reported. But output to JSON file.
- optional: Add additional information to JSON report.
- containers: see [Example: Scan containers (Docker/LXD)(#example-scan-containers-dockerlxd)
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)
@@ -721,11 +736,27 @@ With this sample command, it will ..
- Use SSH Key-Based authentication with empty password (without -ask-key-password option)
- Scan only 2 servers (server1, server2)
## Example: Scan Docker containers
## Example: Scan via shell instead of SSH.
It is common that keep Docker containers running without SSHd daemon.
Vuls scans localhost instead of SSH if the host address is `localhst or 127.0.0.1` and the port is `local` in config.
For more details, see [Architecture section](https://github.com/future-architect/vuls#architecture)
- config.toml
```
[servers]
[servers.localhost]
host = "localhost" # or "127.0.0.1"
port = "local"
```
## Example: Scan containers (Docker/LXD)
It is common that keep containers running without SSHd daemon.
see [Docker Blog:Why you don't need to run SSHd in your Docker containers](https://blog.docker.com/2014/06/why-you-dont-need-to-run-sshd-in-docker/)
### Docker
Vuls scans Docker containers via `docker exec` instead of SSH.
For more details, see [Architecture section](https://github.com/future-architect/vuls#architecture)
@@ -758,6 +789,21 @@ For more details, see [Architecture section](https://github.com/future-architect
- To scan containers only
- --containers-only option is available.
### LXD
Vuls scans lxd via `lxc exec` instead of SSH.
```
[servers]
[servers.172-31-4-82]
host = "172.31.4.82"
user = "ec2-user"
keyPath = "/home/username/.ssh/id_rsa"
containers = ["${running}"]
[servers.172-31-4-82.container]
type = "lxd"
```
----
# Usage: Report

View File

@@ -116,11 +116,12 @@ subjectPrefix = "[vuls]"
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
#containers = ["${running}"]
#ignoreCves = ["CVE-2014-6271"]
#optional = [
# ["key", "value"],
#]
#containers = ["${running}"]
[servers]
{{- $names:= .Names}}
@@ -134,11 +135,15 @@ host = "{{$ip}}"
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
#containers = ["${running}"]
#ignoreCves = ["CVE-2014-0160"]
#optional = [
# ["key", "value"],
#]
#containers = ["${running}"]
#[servers.{{index $names $i}}.container]
#type = "docker" #or "lxd" defualt: docker
{{end}}
`

View File

@@ -62,15 +62,6 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
s := ServerInfo{ServerName: name}
switch {
case v.User != "":
s.User = v.User
case d.User != "":
s.User = d.User
default:
return fmt.Errorf("%s is invalid. User is empty", name)
}
s.Host = v.Host
if len(s.Host) == 0 {
return fmt.Errorf("%s is invalid. host is empty", name)
@@ -85,6 +76,17 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
s.Port = "22"
}
switch {
case v.User != "":
s.User = v.User
case d.User != "":
s.User = d.User
default:
if s.Port != "local" {
return fmt.Errorf("%s is invalid. User is empty", name)
}
}
s.KeyPath = v.KeyPath
if len(s.KeyPath) == 0 {
s.KeyPath = d.KeyPath

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

View File

@@ -471,7 +471,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="201.49428304036473" width="161.48764324188164" x="964.6223485469814" y="296.7320760091144"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="161.48764324188164" x="0.0" y="0.0">Docker Containers</y:NodeLabel>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="161.48764324188164" x="0.0" y="0.0">Docker/LXD</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
@@ -497,7 +497,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="50.0" width="85.0" x="1011.6223485469814" y="433.22635904947913"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="71.91015625" x="6.544921875" y="15.93359375">DockerHost<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="30.794921875" x="27.1025390625" y="15.93359375">Host<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -514,8 +514,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="50.0" width="85.0" x="1011.6223485469814" y="333.3980916341144"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="60.748046875" x="12.1259765625" y="8.8671875">Docker
Container<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="60.748046875" x="12.1259765625" y="15.93359375">Container<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -676,7 +675,6 @@ Container<y:LabelModel>
</graph>
</node>
<node id="n12">
<data key="d5"/>
<data key="d6">
<y:ImageNode>
<y:Geometry height="116.06321112515802" width="97.39570164348925" x="445.4298356510746" y="571.968394437421"/>
@@ -706,13 +704,12 @@ BLOB<y:LabelModel>
</data>
</node>
<node id="n14">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.card">
<y:Geometry height="40.0" width="39.02970922882429" x="390.97029077117577" y="490.0"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="29.828125" x="4.600792114412172" y="10.93359375">.xml<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="29.828125" x="4.6007921144121156" y="10.93359375">.xml<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -723,13 +720,12 @@ BLOB<y:LabelModel>
</data>
</node>
<node id="n15">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.card">
<y:Geometry height="40.0" width="39.02970922882429" x="389.4630622503162" y="587.4087217193426"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="24.1328125" x="7.4484483644121156" y="10.93359375">.txt<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="24.1328125" x="7.448448364412172" y="10.93359375">.txt<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -740,13 +736,12 @@ BLOB<y:LabelModel>
</data>
</node>
<node id="n16">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.card">
<y:Geometry height="40.0" width="39.02970922882429" x="389.9353177243998" y="538.8895352718077"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="32.3828125" x="3.3234483644121156" y="10.93359375">.json<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="32.3828125" x="3.3234483644121724" y="10.93359375">.json<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -757,7 +752,6 @@ BLOB<y:LabelModel>
</data>
</node>
<node id="n17">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.card">
<y:Geometry height="40.0" width="39.02970922882429" x="389.7450294816688" y="640.0"/>
@@ -888,11 +882,12 @@ Vulnerability data<y:LabelModel>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" visible="true" width="77.576171875" x="-68.78805184364364" y="-33.974731445312614">docker exec<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" visible="true" width="77.576171875" x="-86.46309207519198" y="-41.560991819769924">docker exec
lxc exec<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="left" ratio="0.5" segment="0"/>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="47.67504023154834" distanceToCenter="true" position="left" ratio="0.5687397467585064" segment="-1"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
@@ -985,7 +980,6 @@ Vulnerability data<y:LabelModel>
</data>
</edge>
<edge id="e10" source="n4::n1" target="n11">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -996,7 +990,6 @@ Vulnerability data<y:LabelModel>
</data>
</edge>
<edge id="e11" source="n4::n1" target="n5">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -1007,7 +1000,6 @@ Vulnerability data<y:LabelModel>
</data>
</edge>
<edge id="e12" source="n4::n1" target="n6">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -1018,7 +1010,6 @@ Vulnerability data<y:LabelModel>
</data>
</edge>
<edge id="e13" source="n4::n1" target="n8">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -1029,7 +1020,6 @@ Vulnerability data<y:LabelModel>
</data>
</edge>
<edge id="e14" source="n4::n2" target="n11">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -1040,7 +1030,6 @@ Vulnerability data<y:LabelModel>
</data>
</edge>
<edge id="e15" source="n4::n1" target="n12">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -1059,7 +1048,6 @@ Vulnerability data<y:LabelModel>
</data>
</edge>
<edge id="e16" source="n4::n1" target="n13">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -1070,7 +1058,6 @@ Vulnerability data<y:LabelModel>
</data>
</edge>
<edge id="e17" source="n4::n3" target="n11">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -1081,7 +1068,6 @@ Vulnerability data<y:LabelModel>
</data>
</edge>
<edge id="e18" source="n5" target="n4::n3">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 90 KiB

View File

@@ -162,47 +162,43 @@ func (r ScanResult) ReportKeyName() (name string) {
// ServerInfo returns server name one line
func (r ScanResult) ServerInfo() string {
hostinfo := ""
if len(r.Container.ContainerID) == 0 {
hostinfo = fmt.Sprintf(
"%s (%s%s)",
r.ServerName,
r.Family,
r.Release,
)
} else {
hostinfo = fmt.Sprintf(
"%s / %s (%s%s) on %s",
r.Container.Name,
r.Container.ContainerID,
r.Family,
r.Release,
r.ServerName,
return fmt.Sprintf("%s (%s%s)",
r.ServerName, r.Family, r.Release,
)
}
return hostinfo
return fmt.Sprintf(
"%s / %s (%s%s) on %s",
r.Container.Name,
r.Container.ContainerID,
r.Family,
r.Release,
r.ServerName,
)
}
// ServerInfoTui returns server infromation for TUI sidebar
func (r ScanResult) ServerInfoTui() string {
hostinfo := ""
if len(r.Container.ContainerID) == 0 {
hostinfo = fmt.Sprintf(
"%s (%s%s)",
r.ServerName,
r.Family,
r.Release,
)
} else {
hostinfo = fmt.Sprintf(
"|-- %s (%s%s)",
r.Container.Name,
r.Family,
r.Release,
// r.Container.ContainerID,
)
return fmt.Sprintf("%s (%s%s)",
r.ServerName, r.Family, r.Release)
}
return hostinfo
return fmt.Sprintf(
"|-- %s (%s%s)",
r.Container.Name,
r.Family,
r.Release,
// r.Container.ContainerID,
)
}
// FormatServerName returns server and contianer name
func (r ScanResult) FormatServerName() string {
if len(r.Container.ContainerID) == 0 {
return r.ServerName
}
return fmt.Sprintf("%s@%s",
r.Container.Name, r.ServerName)
}
// CveSummary summarize the number of CVEs group by CVSSv2 Severity

View File

@@ -30,8 +30,8 @@ type StdoutWriter struct{}
// WriteScanSummary prints Scan summary at the end of scan
func (w StdoutWriter) WriteScanSummary(rs ...models.ScanResult) {
fmt.Printf("\n\n")
fmt.Printf("Scan Summary\n")
fmt.Printf("============\n")
fmt.Println("One Line Summary")
fmt.Println("================")
fmt.Printf("%s\n", toScanSummary(rs...))
}

View File

@@ -35,7 +35,7 @@ func toScanSummary(rs ...models.ScanResult) string {
table.Wrap = true
for _, r := range rs {
cols := []interface{}{
r.ServerName,
r.FormatServerName(),
fmt.Sprintf("%s%s", r.Family, r.Release),
fmt.Sprintf("%d CVEs", len(r.ScannedCves)),
r.Packages.ToUpdatablePacksSummary(),
@@ -51,7 +51,7 @@ func toOneLineSummary(rs ...models.ScanResult) string {
table.Wrap = true
for _, r := range rs {
cols := []interface{}{
r.ServerName,
r.FormatServerName(),
r.CveSummary(),
r.Packages.ToUpdatablePacksSummary(),
}

View File

@@ -41,8 +41,8 @@ type base struct {
errs []error
}
func (l *base) ssh(cmd string, sudo bool) sshResult {
return sshExec(l.ServerInfo, cmd, sudo, l.log)
func (l *base) exec(cmd string, sudo bool) execResult {
return exec(l.ServerInfo, cmd, sudo, l.log)
}
func (l *base) setServerInfo(c config.ServerInfo) {
@@ -143,7 +143,7 @@ func (l *base) exitedContainers() (containers []config.Container, err error) {
func (l *base) dockerPs(option string) (string, error) {
cmd := fmt.Sprintf("docker ps %s", option)
r := l.ssh(cmd, noSudo)
r := l.exec(cmd, noSudo)
if !r.isSuccess() {
return "", fmt.Errorf("Failed to SSH: %s", r)
}
@@ -152,7 +152,7 @@ func (l *base) dockerPs(option string) (string, error) {
func (l *base) lxdPs(option string) (string, error) {
cmd := fmt.Sprintf("lxc list %s", option)
r := l.ssh(cmd, noSudo)
r := l.exec(cmd, noSudo)
if !r.isSuccess() {
return "", fmt.Errorf("failed to SSH: %s", r)
}
@@ -180,7 +180,7 @@ func (l *base) parseDockerPs(stdout string) (containers []config.Container, err
func (l *base) parseLxdPs(stdout string) (containers []config.Container, err error) {
lines := strings.Split(stdout, "\n")
for i, line := range lines[3:] {
if i % 2 == 1 {
if i%2 == 1 {
continue
}
fields := strings.Fields(strings.Replace(line, "|", " ", -1))
@@ -219,9 +219,9 @@ func (l *base) detectPlatform() error {
}
func (l base) detectRunningOnAws() (ok bool, instanceID string, err error) {
if r := l.ssh("type curl", noSudo); r.isSuccess() {
if r := l.exec("type curl", noSudo); r.isSuccess() {
cmd := "curl --max-time 1 --retry 3 --noproxy 169.254.169.254 http://169.254.169.254/latest/meta-data/instance-id"
r := l.ssh(cmd, noSudo)
r := l.exec(cmd, noSudo)
if r.isSuccess() {
id := strings.TrimSpace(r.Stdout)
if !l.isAwsInstanceID(id) {
@@ -239,9 +239,9 @@ func (l base) detectRunningOnAws() (ok bool, instanceID string, err error) {
}
}
if r := l.ssh("type wget", noSudo); r.isSuccess() {
if r := l.exec("type wget", noSudo); r.isSuccess() {
cmd := "wget --tries=3 --timeout=1 --no-proxy -q -O - http://169.254.169.254/latest/meta-data/instance-id"
r := l.ssh(cmd, noSudo)
r := l.exec(cmd, noSudo)
if r.isSuccess() {
id := strings.TrimSpace(r.Stdout)
if !l.isAwsInstanceID(id) {

View File

@@ -48,9 +48,9 @@ func newDebian(c config.ServerInfo) *debian {
func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err error) {
deb = newDebian(c)
if r := sshExec(c, "ls /etc/debian_version", noSudo); !r.isSuccess() {
if r := exec(c, "ls /etc/debian_version", noSudo); !r.isSuccess() {
if r.Error != nil {
return false, deb, r.Error
return false, deb, nil
}
if r.ExitStatus == 255 {
return false, deb, fmt.Errorf(
@@ -60,7 +60,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
return false, deb, nil
}
if r := sshExec(c, "lsb_release -ir", noSudo); r.isSuccess() {
if r := exec(c, "lsb_release -ir", noSudo); r.isSuccess() {
// e.g.
// root@fa3ec524be43:/# lsb_release -ir
// Distributor ID: Ubuntu
@@ -79,7 +79,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
return true, deb, nil
}
if r := sshExec(c, "cat /etc/lsb-release", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/lsb-release", noSudo); r.isSuccess() {
// e.g.
// DISTRIB_ID=Ubuntu
// DISTRIB_RELEASE=14.04
@@ -100,7 +100,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
// Debian
cmd := "cat /etc/debian_version"
if r := sshExec(c, cmd, noSudo); r.isSuccess() {
if r := exec(c, cmd, noSudo); r.isSuccess() {
deb.setDistro("debian", trim(r.Stdout))
return true, deb, nil
}
@@ -114,7 +114,7 @@ func trim(str string) string {
}
func (o *debian) checkIfSudoNoPasswd() error {
r := o.ssh("apt-get -v", sudo)
r := o.exec("apt-get -v", sudo)
if !r.isSuccess() {
o.log.Errorf("sudo error on %s", r)
return fmt.Errorf("Failed to sudo: %s", r)
@@ -133,7 +133,7 @@ func (o *debian) checkDependencies() error {
// Because unable to get changelogs via apt-get changelog on Debian.
name := "aptitude"
cmd := name + " -h"
if r := o.ssh(cmd, noSudo); !r.isSuccess() {
if r := o.exec(cmd, noSudo); !r.isSuccess() {
o.lackDependencies = []string{name}
}
return nil
@@ -151,7 +151,7 @@ func (o *debian) install() error {
// apt-get update
o.log.Infof("apt-get update...")
cmd := util.PrependProxyEnv("apt-get update")
if r := o.ssh(cmd, sudo); !r.isSuccess() {
if r := o.exec(cmd, sudo); !r.isSuccess() {
msg := fmt.Sprintf("Failed to SSH: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
@@ -159,7 +159,7 @@ func (o *debian) install() error {
for _, name := range o.lackDependencies {
cmd = util.PrependProxyEnv("apt-get install -y " + name)
if r := o.ssh(cmd, sudo); !r.isSuccess() {
if r := o.exec(cmd, sudo); !r.isSuccess() {
msg := fmt.Sprintf("Failed to SSH: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
@@ -188,7 +188,7 @@ func (o *debian) scanPackages() error {
}
func (o *debian) scanInstalledPackages() (packs []models.PackageInfo, err error) {
r := o.ssh("dpkg-query -W", noSudo)
r := o.exec("dpkg-query -W", noSudo)
if !r.isSuccess() {
return packs, fmt.Errorf("Failed to SSH: %s", r)
}
@@ -232,7 +232,7 @@ func (o *debian) parseScannedPackagesLine(line string) (name, version string, er
func (o *debian) checkRequiredPackagesInstalled() error {
if o.Distro.Family == "debian" {
if r := o.ssh("test -f /usr/bin/aptitude", noSudo); !r.isSuccess() {
if r := o.exec("test -f /usr/bin/aptitude", noSudo); !r.isSuccess() {
msg := fmt.Sprintf("aptitude is not installed: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
@@ -244,7 +244,7 @@ func (o *debian) checkRequiredPackagesInstalled() error {
func (o *debian) scanUnsecurePackages(installed []models.PackageInfo) ([]models.VulnInfo, error) {
o.log.Infof("apt-get update...")
cmd := util.PrependProxyEnv("apt-get update")
if r := o.ssh(cmd, sudo); !r.isSuccess() {
if r := o.exec(cmd, sudo); !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
@@ -328,7 +328,7 @@ func (o *debian) fillCandidateVersion(before models.PackageInfoList) (filled []m
names = append(names, p.Name)
}
cmd := fmt.Sprintf("LANGUAGE=en_US.UTF-8 apt-cache policy %s", strings.Join(names, " "))
r := o.ssh(cmd, sudo)
r := o.exec(cmd, sudo)
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
@@ -350,7 +350,7 @@ func (o *debian) fillCandidateVersion(before models.PackageInfoList) (filled []m
func (o *debian) GetUpgradablePackNames() (packNames []string, err error) {
cmd := util.PrependProxyEnv("LANGUAGE=en_US.UTF-8 apt-get upgrade --dry-run")
r := o.ssh(cmd, sudo)
r := o.exec(cmd, sudo)
if r.isSuccess(0, 1) {
return o.parseAptGetUpgrade(r.Stdout)
}
@@ -529,7 +529,7 @@ func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]string, error) {
}
cmd = util.PrependProxyEnv(cmd)
r := o.ssh(cmd, noSudo)
r := o.exec(cmd, noSudo)
if !r.isSuccess() {
o.log.Warnf("Failed to SSH: %s", r)
// Ignore this Error.
@@ -603,8 +603,7 @@ func (o *debian) parseChangelog(changelog string,
}
func (o *debian) splitAptCachePolicy(stdout string) map[string]string {
// re := regexp.MustCompile(`(?m:^[^ \t]+:$)`)
re := regexp.MustCompile(`(?m:^[^ \t]+:\r\n)`)
re := regexp.MustCompile(`(?m:^[^ \t]+:\r?\n)`)
ii := re.FindAllStringIndex(stdout, -1)
ri := []int{}
for i := len(ii) - 1; 0 <= i; i-- {

View File

@@ -46,9 +46,9 @@ func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
// Prevent from adding `set -o pipefail` option
c.Distro = config.Distro{Family: "FreeBSD"}
if r := sshExec(c, "uname", noSudo); r.isSuccess() {
if r := exec(c, "uname", noSudo); r.isSuccess() {
if strings.Contains(r.Stdout, "FreeBSD") == true {
if b := sshExec(c, "uname -r", noSudo); b.isSuccess() {
if b := exec(c, "uname -r", noSudo); b.isSuccess() {
rel := strings.TrimSpace(b.Stdout)
bsd.setDistro("FreeBSD", rel)
return true, bsd
@@ -97,7 +97,7 @@ func (o *bsd) scanPackages() error {
func (o *bsd) scanInstalledPackages() ([]models.PackageInfo, error) {
cmd := util.PrependProxyEnv("pkg version -v")
r := o.ssh(cmd, noSudo)
r := o.exec(cmd, noSudo)
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
@@ -107,13 +107,13 @@ func (o *bsd) scanInstalledPackages() ([]models.PackageInfo, error) {
func (o *bsd) scanUnsecurePackages() (vulnInfos []models.VulnInfo, err error) {
const vulndbPath = "/tmp/vuln.db"
cmd := "rm -f " + vulndbPath
r := o.ssh(cmd, noSudo)
r := o.exec(cmd, noSudo)
if !r.isSuccess(0) {
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
cmd = util.PrependProxyEnv("pkg audit -F -r -f " + vulndbPath)
r = o.ssh(cmd, noSudo)
r = o.exec(cmd, noSudo)
if !r.isSuccess(0, 1) {
return nil, fmt.Errorf("Failed to SSH: %s", r)
}

View File

@@ -48,18 +48,18 @@ func newRedhat(c config.ServerInfo) *redhat {
func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
red = newRedhat(c)
if r := sshExec(c, "ls /etc/fedora-release", noSudo); r.isSuccess() {
if r := exec(c, "ls /etc/fedora-release", noSudo); r.isSuccess() {
red.setDistro("fedora", "unknown")
Log.Warn("Fedora not tested yet: %s", r)
return true, red
}
if r := sshExec(c, "ls /etc/redhat-release", noSudo); r.isSuccess() {
if r := exec(c, "ls /etc/redhat-release", noSudo); r.isSuccess() {
// https://www.rackaid.com/blog/how-to-determine-centos-or-red-hat-version/
// e.g.
// $ cat /etc/redhat-release
// CentOS release 6.5 (Final)
if r := sshExec(c, "cat /etc/redhat-release", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/redhat-release", noSudo); r.isSuccess() {
re := regexp.MustCompile(`(.*) release (\d[\d.]*)`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
@@ -79,10 +79,10 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
return true, red
}
if r := sshExec(c, "ls /etc/system-release", noSudo); r.isSuccess() {
if r := exec(c, "ls /etc/system-release", noSudo); r.isSuccess() {
family := "amazon"
release := "unknown"
if r := sshExec(c, "cat /etc/system-release", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/system-release", noSudo); r.isSuccess() {
fields := strings.Fields(r.Stdout)
if len(fields) == 5 {
release = fields[4]
@@ -97,7 +97,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
}
func (o *redhat) checkIfSudoNoPasswd() error {
r := o.ssh("yum --version", o.sudo())
r := o.exec("yum --version", o.sudo())
if !r.isSuccess() {
o.log.Errorf("sudo error on %s", r)
return fmt.Errorf("Failed to sudo: %s", r)
@@ -128,7 +128,7 @@ func (o *redhat) checkDependencies() error {
}
cmd := "rpm -q " + name
if r := o.ssh(cmd, noSudo); r.isSuccess() {
if r := o.exec(cmd, noSudo); r.isSuccess() {
return nil
}
o.lackDependencies = []string{name}
@@ -142,7 +142,7 @@ func (o *redhat) checkDependencies() error {
func (o *redhat) install() error {
for _, name := range o.lackDependencies {
cmd := util.PrependProxyEnv("yum install -y " + name)
if r := o.ssh(cmd, sudo); !r.isSuccess() {
if r := o.exec(cmd, sudo); !r.isSuccess() {
return fmt.Errorf("Failed to SSH: %s", r)
}
o.log.Infof("Installed: %s", name)
@@ -165,7 +165,7 @@ func (o *redhat) checkRequiredPackagesInstalled() error {
}
cmd := "rpm -q " + packName
if r := o.ssh(cmd, noSudo); !r.isSuccess() {
if r := o.exec(cmd, noSudo); !r.isSuccess() {
msg := fmt.Sprintf("%s is not installed", packName)
o.log.Errorf(msg)
return fmt.Errorf(msg)
@@ -194,7 +194,7 @@ func (o *redhat) scanPackages() error {
func (o *redhat) scanInstalledPackages() (installedPackages models.PackageInfoList, err error) {
cmd := "rpm -qa --queryformat '%{NAME}\t%{VERSION}\t%{RELEASE}\n'"
r := o.ssh(cmd, noSudo)
r := o.exec(cmd, noSudo)
if r.isSuccess() {
// e.g.
// openssl 1.0.1e 30.el6.11
@@ -249,7 +249,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (models.VulnInfos, er
cmd = fmt.Sprintf(cmd, "")
}
r := o.ssh(util.PrependProxyEnv(cmd), sudo)
r := o.exec(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 SSH: %s", r)
@@ -543,7 +543,7 @@ func (o *redhat) getAllChangelog(packInfoList models.PackageInfoList) (stdout st
// yum update --changelog doesn't have --color option.
command += fmt.Sprintf(" LANGUAGE=en_US.UTF-8 yum %s --changelog update ", yumopts) + packageNames
r := o.ssh(command, sudo)
r := o.exec(command, sudo)
if !r.isSuccess(0, 1) {
return "", fmt.Errorf(
"Failed to get changelog. status: %d, stdout: %s, stderr: %s",
@@ -568,7 +568,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (models.VulnInfos,
}
cmd := "yum --color=never repolist"
r := o.ssh(util.PrependProxyEnv(cmd), o.sudo())
r := o.exec(util.PrependProxyEnv(cmd), o.sudo())
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
@@ -584,7 +584,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (models.VulnInfos,
} else {
cmd = "yum --color=never updateinfo list available --security"
}
r = o.ssh(util.PrependProxyEnv(cmd), o.sudo())
r = o.exec(util.PrependProxyEnv(cmd), o.sudo())
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
@@ -593,7 +593,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (models.VulnInfos,
// get package name, version, rel to be upgrade.
// cmd = "yum check-update --security"
cmd = "LANGUAGE=en_US.UTF-8 yum --color=never check-update"
r = o.ssh(util.PrependProxyEnv(cmd), o.sudo())
r = o.exec(util.PrependProxyEnv(cmd), o.sudo())
if !r.isSuccess(0, 100) {
//returns an exit code of 100 if there are available updates.
return nil, fmt.Errorf("Failed to SSH: %s", r)
@@ -628,7 +628,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (models.VulnInfos,
} else {
cmd = "yum --color=never updateinfo --security update"
}
r = o.ssh(util.PrependProxyEnv(cmd), o.sudo())
r = o.exec(util.PrependProxyEnv(cmd), o.sudo())
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
}

View File

@@ -91,47 +91,6 @@ func TestChangeSectionState(t *testing.T) {
}
}
func TestParseYumUpdateinfoHeader(t *testing.T) {
r := newRedhat(config.ServerInfo{})
var tests = []struct {
in string
out []models.PackageInfo
}{
{
" nodejs-0.10.36-3.el6,libuv-0.10.34-1.el6,v8-3.14.5.10-17.el6 ",
[]models.PackageInfo{
{
Name: "nodejs",
Version: "0.10.36",
Release: "3.el6",
},
{
Name: "libuv",
Version: "0.10.34",
Release: "1.el6",
},
{
Name: "v8",
Version: "3.14.5.10",
Release: "17.el6",
},
},
},
}
for _, tt := range tests {
if a, err := r.parseYumUpdateinfoHeaderCentOS(tt.in); err != nil {
t.Errorf("err: %s", err)
} else {
if !reflect.DeepEqual(a, tt.out) {
e := pp.Sprintf("%#v", tt.out)
a := pp.Sprintf("%#v", a)
t.Errorf("expected %s, actual %s", e, a)
}
}
}
}
func TestParseYumUpdateinfoLineToGetCveIDs(t *testing.T) {
r := newRedhat(config.ServerInfo{})
var tests = []struct {
@@ -804,31 +763,6 @@ if-not-architecture 100-200 amzn-main
}
}
func TestParseYumUpdateinfoAmazonLinuxHeader(t *testing.T) {
r := newRedhat(config.ServerInfo{})
var tests = []struct {
in string
out models.DistroAdvisory
}{
{
"Amazon Linux AMI 2014.03 - ALAS-2015-598: low priority package update for grep",
models.DistroAdvisory{
AdvisoryID: "ALAS-2015-598",
Severity: "low",
},
},
}
for _, tt := range tests {
a, _, _ := r.parseYumUpdateinfoHeaderAmazon(tt.in)
if !reflect.DeepEqual(a, tt.out) {
e := pp.Sprintf("%v", tt.out)
a := pp.Sprintf("%v", a)
t.Errorf("expected %s, actual %s", e, a)
}
}
}
func TestParseYumUpdateinfoListAvailable(t *testing.T) {
r := newRedhat(config.ServerInfo{})
rhelStdout := `RHSA-2015:2315 Moderate/Sec. NetworkManager-1:1.0.6-27.el7.x86_64

View File

@@ -105,6 +105,9 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) {
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

View File

@@ -25,7 +25,7 @@ import (
"io/ioutil"
"net"
"os"
"os/exec"
ex "os/exec"
"strings"
"syscall"
"time"
@@ -39,7 +39,7 @@ import (
"github.com/future-architect/vuls/util"
)
type sshResult struct {
type execResult struct {
Servername string
Host string
Port string
@@ -50,16 +50,13 @@ type sshResult struct {
Error error
}
func (s sshResult) String() string {
func (s execResult) String() string {
return fmt.Sprintf(
"SSHResult: servername: %s, cmd: %s, exitstatus: %d, stdout: %s, stderr: %s, err: %s",
"execResult: servername: %s\n cmd: %s\n exitstatus: %d\n stdout: %s\n stderr: %s\n 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
}
func (s execResult) isSuccess(expectedStatusCodes ...int) bool {
if len(expectedStatusCodes) == 0 {
return s.ExitStatus == 0
}
@@ -68,6 +65,9 @@ func (s sshResult) isSuccess(expectedStatusCodes ...int) bool {
return true
}
}
if s.Error != nil {
return false
}
return false
}
@@ -148,8 +148,11 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []
return
}
func sshExec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (result sshResult) {
if conf.Conf.SSHExternal {
func exec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (result execResult) {
if c.Port == "local" &&
(c.Host == "127.0.0.1" || c.Host == "localhost") {
result = localExec(c, cmd, sudo)
} else if conf.Conf.SSHExternal {
result = sshExecExternal(c, cmd, sudo)
} else {
result = sshExecNative(c, cmd, sudo)
@@ -160,7 +163,37 @@ func sshExec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (re
return
}
func sshExecNative(c conf.ServerInfo, cmd string, sudo bool) (result sshResult) {
func localExec(c conf.ServerInfo, cmdstr string, sudo bool) (result execResult) {
cmdstr = decolateCmd(c, cmdstr, sudo)
var cmd *ex.Cmd
if c.Distro.Family == "FreeBSD" {
cmd = ex.Command("/bin/sh", "-c", cmdstr)
} else {
cmd = ex.Command("/bin/bash", "-c", cmdstr)
}
var stdoutBuf, stderrBuf bytes.Buffer
cmd.Stdout = &stdoutBuf
cmd.Stderr = &stderrBuf
if err := cmd.Run(); err != nil {
result.Error = err
if exitError, ok := err.(*ex.ExitError); ok {
waitStatus := exitError.Sys().(syscall.WaitStatus)
result.ExitStatus = waitStatus.ExitStatus()
} else {
result.ExitStatus = 999
}
} else {
result.ExitStatus = 0
}
result.Stdout = stdoutBuf.String()
result.Stderr = stderrBuf.String()
result.Cmd = strings.Replace(cmdstr, "\n", "", -1)
return
}
func sshExecNative(c conf.ServerInfo, cmd string, sudo bool) (result execResult) {
result.Servername = c.ServerName
result.Host = c.Host
result.Port = c.Port
@@ -219,8 +252,8 @@ func sshExecNative(c conf.ServerInfo, cmd string, sudo bool) (result sshResult)
return
}
func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool) (result sshResult) {
sshBinaryPath, err := exec.LookPath("ssh")
func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool) (result execResult) {
sshBinaryPath, err := ex.LookPath("ssh")
if err != nil {
return sshExecNative(c, cmd, sudo)
}
@@ -256,13 +289,13 @@ func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool) (result sshResult
// cmd = fmt.Sprintf("stty cols 256; set -o pipefail; %s", cmd)
args = append(args, cmd)
execCmd := exec.Command(sshBinaryPath, args...)
execCmd := ex.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 e, ok := err.(*ex.ExitError); ok {
if s, ok := e.Sys().(syscall.WaitStatus); ok {
result.ExitStatus = s.ExitStatus()
} else {