(比較的)標準化をした Vuls/VulsRepo の導入

# 重要

2016/12の環境に合わせ標準化案を [標準化した、Vuls/VulsRepoの導入](http://qiita.com/hogehogehugahuga/items/239c0ad02ac409e34f12)に記載しました。
これから始めてこの記事を見る方は上記リンクの記事を参照ください。

- 記録の為に本記事は残しておきますが、現時点ではあまりお勧めできる内容ではないかもしれません。

# 概要

Vulsのインストールや構成についてある程度標準化できそうなので、まとめました。
本記事作成時点で、良いと思われる構成、で作成しています。

## 重要

本ページリリース後に行った、重要な修正について記載します。

- **後述のFAQに、事前検討すべきこと等を追加しました。**
- **-ask-sudo-passwordを使用しない形式にしました。**

# 全体構成
## ディレクトリ構成
```
/ (root)
   +- /opt/vuls
   |  +- go/
   |  |  +- bin/
   |  |  |  +- go-cve-dictionary
   |  |  |  +- vuls
   |  |  +- src/github.com/
   |  |     +- future-architect/vuls/
   |  |     +- kotakanbe/go-cve-dictionary/
   |  +- result/currnet
   |  |  +- <remote-servers1>.json
   |  |  +- ...
   |  |  +- <remote-serversN>.json
   |  |  +- all.json (deprecated)
   |  +- ssh_keys/  (as chmod 700)
   |  |  +- <remote-servers1_id_rsa>
   |  |  +- ...
   |  |  +- <remote-serversN_id_rsa>
   |  +- config
   |  |  +- config.toml (Default use)
   |  |  +- otherConfig.toml
   |  |  +- ...
   |  |  +- otherConfigN.toml
   |  +- cve.sqlite3
   |  +- vuls.sqlite3
   +- /home/vuls
   +- /var/log/vuls
      +- <remote-servers1>
      +- ...
      +- <remote-serversN>
      +- cve-dictionary.log
      +- report.log
```

## 環境について

- Vuls実行は `vuls` ユーザとする
    - 実行コマンド
        - `$ vuls prepare`
            - `-ask-sudo-password`
        - `$ vuls scan`
            - `-ask-sudo-password`
            -  `-config=/opt/vuls/config.toml`
            -  `-cve-dictionary-dbpath=/opt/vuls/cve.sqlite3`
            -  `-report-json`
    - sudo権限
        - Ubuntu等
            - `vuls  ALL=(root)  /usr/bin/apt-get,/usr/bin/apt-cache`
        - CentOS/RHEL等
            - `vuls  ALL=(root)  /usr/bin/yum`
    - Update
        - `$ /opt/vuls/go/src/github.com/.../[go-cve-dictionary|vuls]`
        - `$ git pull`
        - `$ glide install`
        - `$ go install`
    - 各サーバ用id_rsaを集約
        - `/opt/vuls/ssh_keys`
    - ~~sudo passwordは必須、鍵にはパスワード付けず~~
        - PCIDSS等に対応する際に管理者権限には制限が必要となる。
        - 以下のポリシーとする。
            - ssh鍵認証は、鍵を持っていることが前提であるため、パスフレーズは設定しない。
            - 実際の管理者権限を行使する sudo については、パスワード制限をかける。
        - ここら辺は、どのようなセキュリティポリシーの環境で運用するかという点にかかわるので、まずは方針を決め、それに合わせてvulsの設定をする。
- 各サーバのid_rsa（SSHで利用する鍵）は、集約する
    - opt/vuls/ssh_keys に配置し、他のユーザにはアクセスをさせないよう、保護する。
        - ディレクトリ事態を vuls実行ユーザが保有者になり chmod 600 とするのが良いと考えられる。
- IDS等は、本ページでは考慮しない
    - スキャン時に、vulsスキャンサーバからスキャン対象サーバへ多数のssh接続が行われる。
    - 通常であればSSH接続は成功しているので制限されないと思うが、設定やポリシーにより、sshが制限されてしまう場合がある。
        - その際は、IDSの調整を行うこと。
        - iptablesで実装している場合は、sshが成功した場合は遮断しないようにく見直す必要がある。


# 手順
## OSのインストール

今回は Ubuntu14を利用します。
インターネットに接続できる環境で構成します。

## vulsユーザの作成、sudo調整、鍵ペア作成

```
[root]# adduser vuls
[root]# passwd vuls
[root]# visudo
vuls  ALL=(root) /usr/bin/apt-get,/usr/bin/apt-cache,/usr/bin/tar   (/usr/bin/tarはVulsサーバのみ必須)
[root]# su - vuls
[vuls]$ cd
[vuls]~$ ssh-keygen -t rsa
...
パスフレーズなしで作成
...
[vuls]~$ cat .ssh/id_rsa.pub >> .ssh/authorized_keys
[vuls]~$ chmod 600 .ssh/authorized_keys
```

以降、rootとvulsのコンソールを開いて作業します。

- suで切替えてもよいですが、面倒です。

## go言語のインストール

```
[vuls@~]$ sudo apt-get install sqlite git gcc
[vuls@~]$ wget https://storage.googleapis.com/golang/go1.6.3.linux-amd64.tar.gz
[vuls@~]$ sudo tar -C /usr/local -xzf go1.6.3.linux-amd64.tar.gz
---
[root]# vi /etc/profile.d/goenv.sh  (一旦、rootにて作業。pathはお好みで。)
export GOROOT=/usr/local/go
export GOPATH=/opt/vuls/go
export PATH=/usr/local/bin:$PATH:$GOROOT/bin:$GOPATH/bin
[root]# mkdir /opt/vuls
[root]# chown vuls /opt/vuls
[root]# chmod 755 /opt/vuls
[root]# mkdir /opt/vuls/ssh_keys
[root]# chown vuls /opt/vuls/ssh_keys
[root]# chmod 600 /opt/vuls/ssh_keys
---
[vuls@~]$ source /etc/profile.d/goenv.sh
[vuls@~]$ go version
go version go1.6.2 linux/amd64
```

## go-cve-dictionaryを導入

cve等のデータベースとなる go-cve-dictionary `https://github.com/kotakanbe/go-cve-dictionary` を導入

```
[root]$ mkdir /var/log/vuls
[root]$ chown vuls /var/log/vuls
[root]$ chmod 700 /var/log/vuls
---
[vuls@~]$ cd /opt/vuls
[vuls@/opt/vuls]$ go get github.com/kotakanbe/go-cve-dictionary
[vuls@/opt/vuls]$ for i in {2002..20016}; do go-cve-dictionary fetchnvd -years $i; done
...しばらく待ち...
```

## vulsの導入

脆弱性スキャナ Vuls `https://github.com/future-architect/vuls` を導入

```
[vuls@/opt/vuls]$ go get github.com/future-architect/vuls
[vuls@/opt/vuls]$ vuls -v
vuls 0.1.4
```

## vulsのconfigを作成

```
[vuls@/opt/vuls]$ vi /opt/vuls/config.toml
[servers]

[servers.vuls-server]
host = "127.0.0.1"
port = "22"
user = "vuls"
keyPath = "/home/vuls/.ssh/id_rsa"
```

## 自分自身のスキャンを実施

```
[vuls@/opt/vuls]$ vuls prepare -ask-sudo-password
sudo password: *****
[vuls@/opt/vuls]$ vuls scan -ask-sudo-password -cve-dictionary-dbpath=/opt/vuls/cve.sqlite3
sudo password: *****
INFO [0011] Start scanning
...
[vuls@/opt/vuls]$ vuls tui
```

これで、コンソール上から対応可能な脆弱性情報を見ることが可能になる。

## VulsRepoを導入

vulsで得た情報を、ブラウザでインタラクティブにみるための VulsRepo `https://github.com/usiusi360/vulsrepo` を導入する。
VulsRepoで利用するためのjsonを用意する(先ほどのscanを -report-jsonを追加して実行

```
[vuls@/opt/vuls]$ vuls scan -ask-sudo-password -cve-dictionary-dbpath=/home/vuls/cve.sqlite3 -report-json
```

VulsRepoを導入

```
[root]# apt-get install apache2
...
/var/www/html が DocumentRootと仮定して進めます。
...
----
[root]# cd /tmp
[root]/tmp# wget https://github.com/usiusi360/vulsrepo/archive/master.zip
[root]/tmp# unzip master.zip
[root]/tmp# mv ./vulsrepo-master/src /var/www/html/vulsrepo
[root]/tmp# cd /var/www/html/vulsrepo
[root]/vulsrepo# ln -s /opt/vuls/result/current current
...
vulsrepoが/opt/vuls/result/currentに配置されているjsonを読む
...
```

# 他の監視対象の追加

基本的に、スキャンを行うユーザを作り、sudoで権限を与えるだけでよい

- 鍵認証は最低限必要でしょう
- sudoは https://github.com/future-architect/vuls/blob/master/README.ja.md の通り、OSごとに許可が必要
    - yum系
        - /usr/bin/yumを許可
            - yum-plugin-security, yum-plugin-changelog が必要。
    - apt系
        - /usr/bin/apt-get,/usr/bin/apt-cacheを許可
    - FreeBSD
        - pkgを許可
- 監視対象のセキュリティドメインごとにパスワードを変える運用をする場合、configを同じパスワード群ごとに分割する。
    - `--config=`でコンフィグを指定し実行することが可能。その場合のcurrentの出力については、現在プロジェクトとして検討中のようだ。

以下の前提で追加例を示します。

- 対象はCentOS511, 6.8
- vulsユーザにてチェックを行う。sudoパスワードも統一する。
- .ssh/id_rsa等は、個別に作成し、vulsサーバに回収する。
- 定期的に、手動でチェックを行う運用想定
    - 自動チェックは後程。
- ホスト名
    - vulsサーバ: VulsServer
    - CentOS5クライアント: Cent511
    - CentOS6クライアント: Cent608


## 監視対象サーバにユーザを作成する

監視対象サーバにログインし、ユーザを作成、id_rsaをvulsサーバに渡す。

```
[root@Cent511:~]# addusr vuls
[root@Cent511:~]# passwd vuls
[root@Cent511:~]# visudo
vuls  ALL=(root) /usr/bin/yum
[root@Cent511:~]# su - vuls
[vuls@Cent511:~]$ cd
[vuls@Cent511:~]$ ssh-keygen -t rsa
...
パスフレーズなしで作成
...
[vuls@Cent511:~]$ cat .ssh/id_rsa.pub >> .ssh/authorized_keys
[vuls@Cent511:~]$ chmod 700 .ssh/authorized_keys
[vuls@Cent511:~]$ scp .ssh/id_rsa.pub vuls@VulsServer:/opt/vuls/ssh_keys/Cent511-id_rsa
```

同様に、CentOS6も設定する。CentOS6.8はyumなので、/usr/bin/yumをsudo可能とする。

## Vulsのコンフィグを変える

```
[vuls@VulsServer:/opt/vuls]$ vi /opt/vuls/config.toml
[default]
port = "22"
user = "vuls"

[servers]

[servers.vuls-server]
host = "127.0.0.1"
keyPath = "/home/vuls/.ssh/id_rsa"

[servers.Cent511]
host = "xxx.xxx.xxx.xxx"
keyPath = "/opt/vuls/ssh_keys/Cent511-id_rsa"

[servers.Cent608]
host = "xxx.xxx.xxx.xxx"
keyPath = "/opt/vuls/ssh_keys/Cent608-id_rsa"
```

[default]に共通する設定を書くことが可能。今回は全てのサーバに対して、デフォルトのsshポート、vulsユーザでの接続、を行うのでまとめる。

## 再度 prepare, scan の実施

```
[vuls@VulsServer:/opt/vuls]$ vuls prepare -ask-sudo-password
sudo password: *****
[vuls@VulsServer:/opt/vuls]$ vuls scan -ask-sudo-password -cve-dictionary-dbpath=/home/vuls/cve.sqlite3
sudo password: *****
INFO [0011] Start scanning
...
[vuls@VulsServer:/opt/vuls]$ 
```

これで情報が更新されるので、先ほどの VulsRepoのWEB画面を見ると、追加サーバが増えている。

# その他の情報
## アップデート

Vulsの[README](https://github.com/future-architect/vuls/blob/master/README.ja.md) 参照

- 当該のディレクトリでgit pullし、vendor を移動(本ページでは削除)し、make installする。
- $GOPATH/bin にバイナリが配置されるので、make install前に別名にして保存することを推奨。


### go-cve-dictionary update

```
$ cd /opt/vuls/go/src/github.com/kotakanbe/go-cve-dictionary
$ git pull
$ rm vendor
$ make install
```

### vuls update

```
$ cd /opt/vuls/src/github.com/future-architect/vuls
$ git pull
$ rm vendor
$ make install
```

### go-cve-dictionaryデータ更新

- NVDの更新
    - fetchnvd の定期実施
- JVNの更新
    - fetchjvn の定期実施
- 状況によっては、SQLite3ではなく、mysqlにデータを入れる事を検討
    - `-dbtype mysql`オプション


## 運用

- 定期的なアップデートが必要なもの
    - go-cve-dictionaryの、CVE情報のアップデートが必要
        - CVE、JVN等の情報を定期的にアップデートする必要がある
- 定期的なスキャン
    - 必要なタイミングでのスキャンが必要
        - PCIDSSでの確認タイミング
        - 毎日アップデート/毎日チェック
        - 特定の脆弱性情報がでタイミング
        - etc..

# FAQ

## 初期の検討

セキュリティ要件を検討する必要があります。
- Vulsスキャン対象サーバの vulsユーザ秘密鍵は、全て同じでも良いのか/別々にするべきなのか
    - セキュリティ上はホスト毎に変えるのが最良ですが、運用形態によっては同じ鍵を利用することも可能です。
    - 同一のカギを利用した場合、一度鍵が流出すると、関連するすべてのサーバへのログインができてしまうリスクがあります。
- Vulsスキャン結果は、誰に見せてよいのか
    - 完全に公開とすると、対象サーバの脆弱性を自ら公開することになり、状況により攻撃を誘発することになります。
    - 通常は管理者のみ閲覧可能なように、httpd側で制限を書けます(クライアント証明書認証/BASIC認証)
- httpdは apache で稼働させるのか
- Vulsサーバへのログイン可能ユーザは、なるべく減らした方が良いのか

これらについて、例えばPCI/DSSに従うのであれば、指定があるので決定が容易く行えます。
そうでない場合は、「Vulsのスキャンデータの保護重要性」「運用上の負荷」「データ流出時の影響範囲」を検討して、決定する必要があります。
- PCI/DSSの場合、以下の点を検討する必要があります。
    - パスワードに関しては、定期的に更新する必要がある
        - BASIC認証でパスワードを使う場合は、定期更新の必要性がある = 鍵認証の方が運用が楽
        - Need to knowの原則により、そもそものログインユーザを減らすことで、パスワード管理の手間を減らせる
        - 等

## 定期自動スキャンがしたい

- 本当に自動スキャンが必要か、等の、ポリシーを決めましょう
    - 監査としての意味合いであれば、月に一回の実行で十分と思われます。
    - 脆弱性が出た場合に即時に知りたいのであれば、JVNやJPCERT/CC等のRSS/RDFを購読しましょう
    - 脆弱性情報が公開されるのは、日本時間でいうと夜間や早朝の場合が多い気がします。
        - それを日本語訳し、詳細確認したのちにJVNで公開されるので、一定のタイムラグがあります。午後一～夕方位に公開されることが多いように見受けられます（個人の感想です）。
        - ゆえに、夕方位でスキャンすると JVN登録早々に検知できる、可能性が高くなります。
            - が、バックアップウインドウというかスキャンウィンドウは夜間でしか取れないと思うので、状況に合わせてください。
- 次に、自動実行をするユーザが乗っ取られても大丈夫か、を検討してください。
    - 自動検査のメリットと、自動化による当該ユーザのセキュリティ強度低下が、割に合うようなら自動化しましょう。
        - (場合によりますが)PCIDSS等で、かたくなに管理者権限を制限してくる認定企業もある、かもしれません。
- 基本的には、sshパスフレーズなし、sudo時にパスワードなし/そもそも対象コマンドの実行県があればよい、ように調整すればできます。
    - 実行タイミングはcronで決めればよく、特に技術的にも難しくない。実行ユーザの権限やポリシーを決めるのが、一番骨を折るところ。
- Etsさんの記事も参照してください。
    - [改訂版]vulsを使って脆弱性の自動スキャンを実現する : http://qiita.com/Ets/items/4ea663ed2b272d6ab93a

## vuls -prepareは通るが、スキャンをするとsshが応答しなくなる

- IDS等によるブロックと思われます。
    - スキャン中は多数のsshアクセスが発生するため、状況によりブルートフォース攻撃として認識されます。
    - IPSなアプライアンスやアプリケーションを利用している場合は、ホワイトリスト的に許可する等をしましょう。
    - iptalbes等で実装している場合は、sshが失敗している場合にのみブロックする=成功していれば回数は気にしない、ように構成する必要があるとも割れます。
        - ブルートさんの力技攻撃なので、sshがfailすることを検知すればよい、と考えると幸せになるかもしれません。

以上。