WebからNAPALMを叩くには(NetDevOpsSecの現実解)

# はじめに

この記事は、[NetOpsCoding Advent Calendar 2016](http://qiita.com/advent-calendar/2016/netopscoding) の 2016年12月14日 の記事です。

ネットワーク機器の操作にはCLIが用いられることが多いが、CLIだとプログラムから扱うのが難しい (-_-;)

プログラムから扱いやすくするため、Spotify を中心とした一部のエンジニアが、'napalm' というライブラリを作成している。(Paramikoベース)
https://github.com/napalm-automation/napalm

# 実際の例
今回はJunos + CGI + napalm の構成で、Webからコンフィグロードを実施してみる。
Web からコマンドを叩きたい場合、(例えば、Firewallのポリシー追加) 以下のようなHTML/CGIを用意する。

```html:firewall-policy.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<form action="cgi-bin/firewall-policy.py" method="post">
    srcaddress:<input type="text" name="srcaddress">
    srcnetmask:<input type="text" name="srcnetmask">
    destaddress:<input type="text" name="destaddress">
    destnetmask:<input type="text" name="destnetmask">
    applicationname:<input type="text" name="applicationname">
    policy_then:<input type="text" name="policy_then">
    <input type="submit" value="Go">
</form>
</body>
</html>
```


```python:firewall-policy.py
#!/usr/bin/python


##
(srxipaddr, srxuser, srxpasswd)=('192.168.xx.xx', 'xx', 'xx')
##

import cgi
from napalm import get_network_driver

fs = cgi.FieldStorage()

js={}
for key in fs.keys():
  js[key]=fs[key].value

##
# do import stuff
##
candidate_config_template="""
security policies from-zone untrust to-zone untrust policy automation-policy {{
   match {{
       source-address automation-policy-src;
       destination-address automation-policy-dest;
       application {applicationname};
       }}
       then {{
           {policy_then};
   }}
}}
security zones security-zone untrust address-book {{
   address automation-policy-src {srcaddress}/{srcnetmask};
   address automation-policy-dest {destaddress}/{destnetmask};
}}
"""
candidate_config=candidate_config_template.format(**js)

driver = get_network_driver('junos')
device = driver(srxipaddr, srxuser,  srxpasswd)
device.open()
print (candidate_config)
device.load_merge_candidate(config=candidate_config)
print (device.compare_config())
device.commit_config()
device.close()
```


ポイントとしては

- Webフォームから受け取った引数を、dict (=JSON=YAML) に変換した後、コンフィグテンプレートに適用する

というところだろうか、、

もっと詳しく知りたい方は、以下を参照。
http://qiita.com/taijijiji/items/a3f21c8b9e7a0d3afdc6
http://www.slideshare.net/JuniperJapan/interop-tokyo-2016junos-automation


先駆者諸兄の素晴らしいブログのおかげで、あっさり終わってしまった、、、(-o-;)

これだと、本当にさらっと終わってしまい少々寂しいため、、もう少し複雑な例で考えてみたいと思う (-_-;)

# 承認フローとは

既に書いてきた通り、定型作業をWebから実施する場合、ユーザー指示(要するに、引数) の流れは、以下であらわせる。

1. 申請者(=作業者) -> Webアプリ -> napalm

ただ、このフローには

- ブラウザを操作して、作業内容を記述する人(申請者)
- 実際に作業を行う人(作業者)

が同じでないと使えない、という制限があり、実際の運用だとここが問題になってくる。

どういうことかというと、ITオペレーションについて監査が発生する、Enterprise環境では、
'申請者(アプリケーション開発ベンダーなど)' が直接、本番環境を操作できない、という制限が発生する。
※ いわゆるDevとOps

(参考)
http://forza.cocolog-nifty.com/blog/2015/02/itdevops-691f.html
http://blogs.itmedia.co.jp/infra/2013/09/devops1devops-7abb.html


この場合、申請者が記述した内容を承認者が承認、その後、作業者が作業実施、という流れになるのだが、
ITIL対応のツール(大抵、監査ではここが集中的に見られる) では、ここが画面として固定され、作業内容は添付ファイルで、という状況が発生する。
※ 少なくとも2つのベンダーのツールで遭遇した、、(-_-;)

なので、この場合の現実的なフローは以下となる。

1. 申請者 -> 作業指示書(エクセルなど) -> ITILツール -> (ワークフロー) -> 作業者
1. 作業者 -> Webアプリ -> napalm


この場合、申請者は出来るだけ細かく、作業指示書を記述するのだが、
日本語で説明できる情報には限界がある、、、
Amazon の画面を見ながら、注文は全てメールで出す、という感じで、

- どこに、いつ届けるのか？
- 先にピック出来た場合、分割して発送するのか？
- 支払い方法は？

など、一発で正確に伝えきるのは正直難しい。
さらに、

- そもそも在庫がいくつあるのか？

等、システムで調べないと分からない内容を確認する、等も含めると、
申請者の方も何らかのWebアプリをベースにして申請書を作るのが望ましい、と思う。

なので、理想的なフローとしては、以下、と思う。

1. 申請者 -> Webアプリ -> 入力内容をJSONでエクスポート
1. 申請者-> ITILツール(JSON添付) -> (ワークフロー) -> 作業者
1. 作業者 -> Webアプリ(JSONをインポート) -> napalm

これが実現できると、以下のメリットがある。

- 申請者(Dev): 作業可能な内容かどうか、をWebアプリで、自分で調べながら依頼書を作成できる、作業者とのやりとりが減る
- 作業者(Net, Ops): 作業がJSONインポート、に限定される、実機を操作する必要がない、申請者とのやりとりが減る
- セキュリティ担当者(Sec(仮)): フロー外の承認が行われにくくなるので、エビデンスの残し方に悩む必要がない、エビデンス保持(3年保管、など)の方法に悩む必要がない

# ツールをインプリしてみた

上記の課題について、手前味噌ながら、以下のツールをインプリしてみた。
https://github.com/tnaganawa/execjson

ツールの画面イメージは以下となる。
![advent-calendar-2016.png](https://qiita-image-store.s3.amazonaws.com/0/147677/664ab4b3-bc6a-9c59-ca50-aa85f19cbe5e.png)

まず、申請者は実施作業ごと(例では、Firewallポリシー追加) に、入力内容をWebフォームに入力する。
入力し終わったら'exportjson' をクリックすると、JSONがエクスポートされるので、そちらを承認フローに通す。
※ 作成されるJSONは以下

```json:sample.json
{
    "jobapplcode": "", 
    "jobenvcode": "Prod", 
    "joblist": [
        {
            "args": [
                {
                    "applicationname": "junos-vnc", 
                    "destaddress": "192.168.12.11", 
                    "destnetmask": "32", 
                    "policy_then": "permit", 
                    "srcaddress": "192.168.11.11", 
                    "srcnetmask": "32"
                }
            ], 
            "id": "1", 
            "iffail": "stop", 
            "name": "addfirewallpolicy", 
            "time": ""
        }
    ]
}
```

その後、JSONを受け取った作業者は、'load'で、JSONを再度インポートし、'execjson' を押すと、今度は処理が流れ始める、はずである。
※ 実際にはもう少しいろいろあるので、wiki 参照 (-_-;)

# まとめ

上記ツールで、ユーザー依頼作業の自動化について、とっかかりが得られるとよい、と思う。(環境依存なので何ともいえないが)
実際、DevOpsの流れはだいたいセキュリティで止まるのだが、上記のようなツールを使って、

- DevOps -> NetDevOps -> **NetDevOpsSec** <- New!

の順で、徐々に改善範囲を広げていければよいのではなかろうか。