Add local scan mode.
If the scan target server is localhost, Don't use SSH. #210
This commit is contained in:
54
README.ja.md
54
README.ja.md
@@ -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
|
||||
|
||||

|
||||
|
||||
## B. Scan without SSH (Local Scan Mode)
|
||||
|
||||
Vulsをスキャン対象サーバにデプロイする。Vulsはローカルホストにコマンドを発行する(SSH経由ではない)。スキャン結果のJSONを別サーバに集約する。スキャン結果の詳細化のためにはCVEデータベースへのアクセスが必要なので、事前にgo-cve-dictionaryをserver modeで起動しておく。
|
||||
その集約サーバ上で、あなたはWebUIやTUIを用いて各スキャン対象サーバのスキャン結果を参照することが可能。
|
||||
|
||||

|
||||
[詳細](#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コマンドの2種類の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
|
||||
|
||||
|
||||
56
README.md
56
README.md
@@ -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
|
||||
|
||||

|
||||
|
||||
## 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.
|
||||
|
||||

|
||||
[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
|
||||
|
||||
@@ -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}}
|
||||
|
||||
`
|
||||
|
||||
@@ -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
|
||||
|
||||
1600
img/vuls-architecture-localscan.graphml
Normal file
1600
img/vuls-architecture-localscan.graphml
Normal file
File diff suppressed because it is too large
Load Diff
BIN
img/vuls-architecture-localscan.png
Normal file
BIN
img/vuls-architecture-localscan.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 104 KiB |
@@ -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 |
@@ -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
|
||||
|
||||
@@ -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...))
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
18
scan/base.go
18
scan/base.go
@@ -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) {
|
||||
|
||||
@@ -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-- {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user