【目次】
■ はじめに
Ansible は Cisco ACI にも対応して、多数のモジュールがあります。APIC の REST API を叩く仕様になっています。
Cisco ACI Guide という Ansible の公式ドキュメントにも記載されていますが、認証方式は 2通りあります。
- パスワードベース認証方式 (Password-based authentication)
- 署名ベース認証方式 (Signature-based authentication using certificates)
この記事では、署名ベース認証方式の仕組み、環境の準備、Playbook の書き方を説明します。 少々長くなるので先にまとめます。
- パスワードベース認証では、DoS対策機能にひっかかってしまうことがあるため、自動化においては署名ベース認証方式のほうが吉
- 準備として、予めAnsible 側で証明書(公開鍵)と秘密鍵のペアを生成し、証明書を APIC に登録しておく
- Ansible から APIC へのリクエスト時に URL などを元にした署名を Cookie にセットし、APIC で検証、認証する
- 各 ACI モジュールでは、署名ベース認証用の
certificate_name
、private_key
オプションを利用する certificate_name
のデフォルト値はprivate_key
の指定方法によって異なる
前提環境
- APIC: Cisco DevNet Sandbox (APIC 4.1)
- Ansible 2.8.6
- 署名ベース認証の対象ユーザー:
admin
- 手順の検証容易性のために
admin
を利用していますが、運用環境では他のユーザーを利用を推奨
- 手順の検証容易性のために
(参考)パスワードベース認証方式の制限
パスワードベース認証方式は、Web GUI と同じ認証方式なので手軽で便利ですが、Password-based authentication の Note や、Known issues には、「パスワード認証は、リクエストの頻度によっては(D)DoS対策の機能にひっかかり、HTTP 503 でログインエラーになる」旨が書かれています。署名ベース認証方式は、この問題の解決にもなります。
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.
Starting with ACI v3.1 the APIC will actively throttle password-based authenticated connection rates over a specific threshold. This is as part of an anti-DDOS measure but can act up when using Ansible with ACI using password-based authentication. Currently, one solution is to increase this threshold within the nginx configuration, but using signature-based authentication is recommended.
■ 仕組み
署名ベース認証方式は、SSH 接続時の公開鍵認証や SSL/TLS におけるクライアント認証とは異なる方式です。
ACI の公式ドキュメントの以下のページや、Ansible のソースコードをもとにして分かった仕組みについてまとめます。
- Cisco ACI ベーシック コンフィギュレーション ガイド - ユーザ アクセス、認証およびアカウンティング [Cisco Application Policy Infrastructure Controller(APIC)] - Cisco
- 署名ベースのトランザクションについて
- 秘密キーを使用した署名の計算
- Ansible のコード lib/ansible/module_utils/network/aci/aci.py の
cert_auth
メソッド
予めしておくこと
予め、以下の流れで準備しておきます。
より具体的な手順は、後述の「環境準備の手順」で説明します。
リクエストごとに行っていること
リクエストごとに以下の流れで署名データを作成して認証しています。
リクエストごとに以下の流れで署名データを作成、付加して認証しています。
- Ansible 側で、リクエストの HTTP メソッド(GET/POST/DELETEなど)と、URL、ペイロード(実データのjson/xml)を文字列連結する
- Ansible 側で、1 の文字列データのハッシュ値を sha256 で計算する
- Ansible 側で、2 のハッシュ値に対して、ユーザーの秘密鍵(A)を用いて署名を生成する
- Ansible 側で、Cookie に、署名や、証明書のDN(Distinguished Name: どのユーザーのどの証明書か)などを付加する。DN には、後述するモジュールの
username
、certificate_name
オプションの名前が利用される - Ansible から APIC に REST API のリクエストをする
- APIC 側で、送られてきたリクエストの HTTP メソッド(GET/POST/DELETEなど)と、URL、ペイロード(実データのjson/xml)を文字列連結する(Ansible側の 1 と同様)
- APIC 側で、5 の文字列データのハッシュ値を sha256 で計算する(Ansible側の 2 と同様)
- APIC 側で、リクエストの Cookie 内の証明書のDN(どのユーザーのどの証明書か)から、ユーザーの証明書(公開鍵)を特定
- APIC 側で、リクエストの Cookie 内の署名を、ユーザーの証明書(公開鍵)で復号し、元のハッシュ値を計算する
- APIC 側で、7 と 9 結果のハッシュ値が一致することの確認をもってユーザー認証する
※ APIC 側の処理はユーザー側(Ansible)で行っていることからの推測
仕組みは以上です。
■ 環境準備の手順
実際に、Ansible の ACI モジュールで署名ベースの認証を利用するために必要な手順です。
Ansible 側でユーザー側で証明書(公開鍵)と秘密鍵を生成する
Asible 公式ドキュメントの Generate certificate and private key に従い、証明書(公開鍵)と秘密鍵を生成します。有効期限などはセキュリティポリシーに応じて変えます。
$ openssl req -new -newkey rsa:1024 -days 36500 -nodes -x509 -keyout admin.key -out admin.crt -subj '/CN=Admin/O=Your Company/C=US'
なお、subject に指定する値は、認証そのものには影響しません。
APIC 側でユーザーの証明書を登録する
手動で登録する方法と、Ansible で登録する方法があります。
手動で登録する場合
今回利用している Cisco DevNet Sandbox の環境(APIC 4.1)では、Ansible 公式ドキュメントの Configure your local user のステップでは、証明書登録画面にたどり着けませんでした。
代わりに、以下の手順で証明書を登録します。
Admin
>AAA
>Users
でユーザー一覧を開き、対象のユーザーをクリックします。なお、署名ベース認証に対応しているの Local User のみです。対象ユーザ設定画面内の
User Certificates
の+
をクリックします。Create X509 Certificate
画面のName
、Data
を入力して、Submit
ボタンをクリックします。
フィールド名 | 説明 |
---|---|
Name |
証明書の名前を指定します。ユーザー名と異なる名前でも構いません。後述する Ansible モジュールで指定する certificate_name が指す名前です。 |
Data |
Ansible 側で生成した証明書のデータ(ここでは admin.crt の内容)を貼り付けます。 |
Ansible で登録する場合
aci_aaa_user_certificate モジュールによって、ユーザーの証明書を登録作業自身を Ansible で実行できます。
# 証明書を登録する Playbook。このタスクの認証自体はパスワードベース。 - name: Ensure we have a certificate installed aci_aaa_user_certificate: host: my-apic-1 username: admin password: my-password aaa_user: admin # 証明書を登録する対象のユーザー名 certificate_name: admin # 証明書の名前、Create X509 Certificate 画面の Name 相当 certificate: "{{ lookup('file', 'pki/admin.crt') }}" # 証明書データ、Create X509 Certificate 画面の Data 相当
引用元: 公式ドキュメント - Configure your local user、コメントは追記
環境の準備は以上です。
■ 署名ベース認証方式を利用するPlaybook の書き方
次は 署名ベース認証方式を利用するPlaybook の書き方についてです。
Playbook
署名ベース認証では、証明書の名前 certificate_name
と、秘密鍵のパスまたはデータ private_key
というオプションを利用します。
--- - hosts: apic gather_facts: no tasks: - name: tenant aci_tenant: host: "{{ ansible_host }}" username: admin # ユーザー名 certificate_name: admin # 証明書の名前 private_key: admin.key # 秘密鍵 validate_certs: no tenant: test_tenant01 state: present
各オプションと証明書設定画面のフィールドの関係
オプションと証明書設定画面のフィールドの関係は以下のとおりです。
username
オプション- ユーザ名を指定
- ユーザー名と証明書の名前が同じであれば
username
、certificate_name
のどちらかの指定のみでも可
certificate_name
オプション- 証明書の名前を指定
- 「APIC-側でユーザーの証明書を登録する」の手順で
name
フィールドに指定した名前。今回の場合はadmin
。 - ユーザー名と証明書の名前が同じであれば
username
、certificate_name
のどちらかの指定のみでも可
private_key
オプション- 秘密鍵を指定
- 「仕組み」で説明した図の【A】に相当
- 秘密鍵のデータそのもの、またはパスを指定する
- データそのもの指定する場合は、
{{ lookup('file', 'admin.key') }}"
のように file lookup plugin を利用 - Ansible 2.8 から Vault で暗号化した秘密鍵のデータも指定可能
certificate_name
オプションのデフォルト値は、private_key
オプションの指定の仕方によります。
private_key の指定方法 |
certificate_name のデフォルト |
---|---|
データそのものを指定した場合(file lookup plugin 含む) | username で指定したユーザー名 |
秘密鍵のパスを指定した場合 | 秘密鍵の basename から拡張子を除外した値(admin.key なら admin ) |
対応するコードは aci.py
のこのあたりです。(当初、各モジュールのページにちゃんと書いてあったことに気が付かずにコードを読んでいました・・)
いくつか具体例をあげます。
# 1. わかりやすさ重視ですべて明示する場合 username: admin certificate_name: admin private_key: "{{ lookup('file', 'crt/admin.key') }}" # 2. ユーザー名と証明書の名前が同じの場合 ## certificate_name 省略パターン username: admin private_key: "{{ lookup('file', 'crt/admin.key') }}" ## username 省略パターン certificate_name: admin private_key: "{{ lookup('file', 'crt/admin.key') }}" # 3. ユーザー名と証明書の名前が異なる場合 username: admin certificate_name: admincert private_key: "{{ lookup('file', 'crt/admin.key') }}" # 4. ユーザー名、証明書の名前、秘密鍵ファイルのファイル名が同じの場合 private_key: "crt/admin.key"
補足
- パスワード認証では
username
とpaswword
オプションを利用します。 validate_certs
オプションはユーザー認証には直接関係ありません。APIC への HTTPS 接続する際のサーバー証明書の確認に関するオプションです。
Playbook の実行
Playbook を実行した例を掲載します。
$ ansible-playbook -i inventory.ini tenant.yml PLAY [apic] ********************************************************************** TASK [tenant] ******************************************************************** changed: [apic01] PLAY RECAP *********************************************************************** apic01 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
無事に、署名ベース認証方式を利用して、Ansible の ACI モジュールを実行できました。
■ まとめ
APIC が備える認証方式のうち、署名ベース認証方式の仕組み、準備、Playbook の書き方についてまとめました。 以下、冒頭に掲載したまとめを再掲します。