てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible] Cisco ACI モジュールの REST API ログイン処理の流れ

はじめに

Ansible には Cisco ACI 対応のモジュールが多数用意されています。

ACI の REST API を利用しているのだろうな、という予想はすぐついたのですが、実際のログインの処理を覗いてみたくなりました。

ACI モジュール共通だと思いますが、ここでは代表して aci_epg モジュールのログイン処理を追った時の話をまとめます。

先にまとめますと、以下の流れのようでした。

なお、細かく精査したり、実際に実行しながら確認したわけでない点はご了承ください。


■ aci_epg モジュール の main()

aci_epg モジュールは lib/ansible/modules/network/aci/aci_epg.pymain() から追っていきます。

この中に 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 モジュールの usernamepassword などの認証情報のオプションを、ログインするリクエストの組み立てに利用しています。

リクエストの形式は、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 REST API にリクエストしているのは

    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回目以降のオーバーヘッドを低減するためにこのようにしているのだと思います。