はじめに
Ansible には Cisco ACI 対応のモジュールが多数用意されています。
ACI の REST API を利用しているのだろうな、という予想はすぐついたのですが、実際のログインの処理を覗いてみたくなりました。
ACI モジュール共通だと思いますが、ここでは代表して aci_epg モジュールのログイン処理を追った時の話をまとめます。
- 対象バージョン Ansible 2.8.5
先にまとめますと、以下の流れのようでした。
aci_epg.py
で、ACIModule
をインスタンス化- その再、
aci.py 内の def login(felf)
で、ユーザー名パスワードなどを POST して Cookie を取得 - 実際に処理をするリクエストなど、以降はこの Cookie を利用
なお、細かく精査したり、実際に実行しながら確認したわけでない点はご了承ください。
■ aci_epg モジュール の main()
aci_epg モジュールは lib/ansible/modules/network/aci/aci_epg.py
の main()
から追っていきます。
この中に ACIModule
をインスタンス化しているコードがあります。
aci = ACIModule(module)
このクラスは、lib/ansible/module_utils/network/aci/aci.py
内で定義されています。
__init__
内を見てていくと、self.login()
を呼び出しています。
この先にログイン処理がありそうです。 いきなり処理を投げのではなく、事前にログイン処理をするということのようです。
■ ログイン
実際のログイン処理はここにありました。
https://github.com/ansible/ansible/blob/v2.8.5/lib/ansible/module_utils/network/aci/aci.py#L184
resp, auth = fetch_url(self.module, url, data=json.dumps(payload), method='POST', timeout=self.params['timeout'], use_proxy=self.params['use_proxy'])
Playbook 内の ACI モジュールの username
や password
などの認証情報のオプションを、ログインするリクエストの組み立てに利用しています。
リクエストの形式は、ACI REST API ドキュメントの「API セッションの認証と維持」にあるのと同じです。
(例の引用)
POST https://192.0.20.123/api/aaaLogin.json { "aaaUser" : { "attributes" : { "name" : "georgewa", "pwd" : "paSSword1" } } }
そして認証が正常であれば、
self.headers['Cookie'] = resp.headers['Set-Cookie']
で Cookie を保存しています。 https://github.com/ansible/ansible/blob/v2.8.5/lib/ansible/module_utils/network/aci/aci.py#L212
実際の処理を投げる際は、この認証済みの Cookie を利用するのでしょう。次のリクエストも見てみます。
■ 次のリクエスト
ACIModule
クラスのインスタス化の過程でログインを済ませ、Cookie も保存したあとは、また lib/ansible/modules/network/aci/aci_epg.py
に戻ります。
aci.get_existing()
のようです。 https://github.com/ansible/ansible/blob/v2.8.5/lib/ansible/modules/network/aci/aci_epg.py#L352
get_existing
の定義はこちら
https://github.com/ansible/ansible/blob/v2.8.5/lib/ansible/module_utils/network/aci/aci.py#L786
こちらでも ACI REST API へのリクエストが行われています。処理する前の状態を調べるためのようです。
resp, info = fetch_url(self.module, uri, headers=self.headers, method='GET', timeout=self.params['timeout'], use_proxy=self.params['use_proxy'])
ここで headers=self.headers
とあるので、先程保存した Cookie self.headers['Cookie']
も渡るはずです。
このようにして、事前にログインした情報を引き継いで、次のリクエストに渡しているようです。
更に次のリクエスト処理である、aci.post_config()
でも同様でした。
■ 注意点: パスワード認証は HTTP 503 になる可能性がある
Ansible 公式ドキュメントの Cisco ACI Guideには、以下の記載があります。
Password-based authentication also may trigger anti-DoS measures in ACI v3.1+ that causes session throttling and results in HTTP 503 errors and login failures.
ACI v3.1 以上では、DOS対策として、パスワードベースの認証にたいして HTTP 503 のエラーを返すことがあるそうです。
代わりに、署名方式の認証方法の仕方が紹介されています。
Signature-based authentication using certificates
■ まとめ
ACI モジュールの APIログイン処理の流れを追った時の話をまとめました。
当初は、処理のリクエストとログイン処理は同じリクエストだと予想していましたが、実際はことなりました。 一つのタスク内で、何度か ACI REST API へアクセスする都合上、2回目以降のオーバーヘッドを低減するためにこのようにしているのだと思います。