AnsibleでCisco FTDを操作する
Ciscoが提供する次世代ファイアウォール Firepower Threat Defence(FTD)を操作するためのAnsibleモジュールftd_configuration
を使って、REST API経由でネットワークオブジェクトやアクセスルールの追加を行ってみました。
FTD API概要
FTDの設定変更方法としては、CLIやWeb GUIに加え、バージョン6.2.3からREST APIもサポートされています。GUIやREST APIの場合は、管理ツール経由で操作する形になります。
FTDの管理ツールは以下の3種類があります。
1. Firepower Management Center (FMC)
複数のデバイスを一括管理可能。大規模環境向き。(ただし2や3との共存は不可。)
2. Firepower Device Manager (FDM)
単一のデバイスを”On Box(同一筐体上)”で管理。中~小規模環境向き。
3. Cisco Defense Orchestrator (CDO)
クラウドベースで複数のデバイスを管理。
これらの内、ftd_xxx
モジュールは、2つめのFDM経由で操作するモジュールになります。
(1や3のAnsibleモジュールは、2019年11月時点でリリースされていません。)
API経由で操作できる項目は以下の通りです。
- Interface Configuration (incl. BVI, Sub-interfaces)
- Routing (static routing only)
- VPN (Site-to-site and Remote Access)
- System Settings (Interface, NTP, Hostname, etc.)
- Policy Objects, such as Networks, Ports, Security Zones, Applications, URLs, Geolocation
- Policies (SSL, Identity, Security Intel, Blacklists, NAT, Access, Intrusion)
- Talos updates
- Smart Licensing
- Backup Restore
- Troubleshooting
参考URL: DevNet Learning Labs - Firepower Threat Defense API
FTD Ansibleモジュール概要
FTD用のモジュールは、執筆時点で以下の4つがあります。
- ftd_configuration
REST API経由でFTDを操作するモジュール - ftd_file_download
HTTP(S)経由でFTDから各種ファイル(pending changes, disk files, certificates, troubleshoot reports, and backup)をダウンロードするモジュール - ftd_file_upload
HTTP(S)経由でFTDへ各種ファイルをアップロードするモジュール - ftd_install
ROMMONイメージやFTD pkgイメージをインストールするモジュール
一見少なく見えますが、1つ目のftd_configuration
モジュールで、利用可能なAPIをすべてAnsibleから実行できるようです。具体的には、operation
オプションで、各設定項目名の頭に以下を付けることで、CRUDに近い形で操作できます。
- get - fetches a object by its ID (e.g., getNetworkObject);
- getList - fetches a list of objects matching given criteria (e.g., getNetworkObjectList). For example, it can be used to find an object by name or other attribute when its ID is not known;
- add - creates a new object (e.g., addNetworkObject);
- edit - updates an existing object (e.g., editNetworkObject). ID of the existing object is a mandatory attribute for this operation type;
- delete - deletes an existing object by its ID (e.g., deleteNetworkObject);
- upsert - creates a new object if it does not exist, or updates it when the object already exists (e.g., upsertNetworkObject). By default, upsert operation looks for an object by its name, but the filtering criteria can be adjusted. See Upsert operation for more details;
この中でupsertは、存在しない場合新規作成(Create)したり、既に存在する場合に更新(Update)できるもので、メンテナンス時は使い勝手が良さそうです。 詳細は、Cisco DevNet - FTD AnsibleのUser GuideやOperationsを参照願います。
その他、Playbookを作る上で参考になりそうなサイトを記載しておきます。
- Ansible Network modules - Ftd
Ansibleの公式ドキュメント。まず初めに目を通す。 - GitHub - FTD Ansible / samples
Playbookサンプルが豊富。どのバージョンで動くかもまとめられていて便利。 - FDM - API Explorer
https://<FDMのIPアドレス>/#/api-explorer
REST APIの詳細を確認可能。
用意した環境
- FTD:FTDv 6.4.0 VMware install package
※ VMware Workstation Player 15.5.0上に構築
※ URLベースのアクセスルールを設定する場合は、URL Licenseの有効化が必要 - Ansible:2.9.0
※ Cisco DevNet Sandbox「FirePower Threat Defense REST API (6.5)」だと、ネットワークオブジェクトの作成は出来るものの、アクセスルールの作成がエラーで出来ませんでした。
Inventoryファイル
以下の通り作成しました。
- inventory_ftd_vm
[ftd] firepower ansible_host=192.168.100.45 [ftd:vars] ansible_network_os=ftd ansible_user=admin ansible_password=<FDMのパスワード> ansible_httpapi_use_ssl=yes ansible_httpapi_validate_certs=no
Playbook① ポリシー作成
- ホストオブジェクトの作成
1つ目のタスクで、operation
オプションupsertNetworkObject
を指定し、IPアドレス192.168.2.1
に紐づくネットワークオブジェクトAnsible-network-host
を作成しています。
2つ目のタスクで、operation
オプションgetNetworkObject
を指定し、objId
(オブジェクトID)をキーに情報取得を行い、3つ目のタスクで結果を表示しています。 - アクセスルールの作成
後半は、operation
オプションupsertAccessRule
で、Ansible-network-host
からの通信を許可するアクセスルールを設定し、同様に結果を表示しています。
playbook_ftd_acl_add2.yml
--- - hosts: ftd gather_facts: no connection: httpapi tasks: - name: Create a network object ftd_configuration: operation: upsertNetworkObject data: name: Ansible-network-host description: From Ansible with love subType: HOST value: 192.168.2.1 dnsResolution: IPV4_AND_IPV6 type: networkobject register_as: hostNetwork - name: Get network object ftd_configuration: operation: getNetworkObject path_params: objId: "{{ hostNetwork['id'] }}" register_as: network_obj - name: Display network object debug: msg: "{{ network_obj }}" - name: Create an access rule allowing traffic from Ansible-network-host ftd_configuration: operation: upsertAccessRule data: name: Allow_Ansible_Traffic type: accessrule sourceNetworks: - "{{ hostNetwork }}" ruleAction: PERMIT eventLogAction: LOG_BOTH path_params: parentId: default register_as: accessRule - name: Get access rule ftd_configuration: operation: getAccessRule path_params: parentId: default objId: "{{ accessRule['id'] }}" register_as: access_rule - name: Display access rule debug: msg: "{{ access_rule }}"
実行結果① ポリシー作成
changed=2
となり、問題なくオブジェクトとアクセスルールが設定されました。
ちなみに、同じPlaybookをもう一回実行すると、changed=0
になりました。また、operation
をaddNetworkObject
やaddAccessRule
とした場合も同様の結果だったため、冪等性は担保されているようです。
$ ansible-playbook -i inventory_ftd_vm playbook_ftd_acl_add2.yml PLAY [ftd] ********************************************************************************************************************************** TASK [Create a network object] ************************************************************************************************************** changed: [firepower] TASK [Get network object] ******************************************************************************************************************* ok: [firepower] TASK [Display network object] *************************************************************************************************************** ok: [firepower] => { "msg": { "description": "From Ansible with love", "dnsResolution": "IPV4_AND_IPV6", "id": "c9b4138b-087c-11ea-948b-9bbf3850c347", "isSystemDefined": false, "links": { "self": "https://192.168.100.45/api/fdm/v3/object/networks/c9b4138b-087c-11ea-948b-9bbf3850c347" }, "name": "Ansible-network-host", "subType": "HOST", "type": "networkobject", "value": "192.168.2.1", "version": "hs4yv7xe7yk34" } } TASK [Create an access rule allowing traffic from Ansible-network-host] ********************************************************************* changed: [firepower] TASK [Get access rule] ********************************************************************************************************************** ok: [firepower] TASK [Display access rule] ****************************************************************************************************************** ok: [firepower] => { "msg": { "destinationNetworks": [], "destinationPorts": [], "destinationZones": [], "embeddedAppFilter": null, "eventLogAction": "LOG_BOTH", "filePolicy": null, "id": "cc1ef90e-087c-11ea-948b-5f82967c1861", "identitySources": [], "intrusionPolicy": null, "links": { "self": "https://192.168.100.45/api/fdm/v3/policy/accesspolicies/default/accessrules/cc1ef90e-087c-11ea-948b-5f82967c1861" }, "logFiles": false, "name": "Allow_Ansible_Traffic", "ruleAction": "PERMIT", "ruleId": 268435462, "sourceNetworks": [ { "id": "c9b4138b-087c-11ea-948b-9bbf3850c347", "name": "Ansible-network-host", "type": "networkobject", "version": "hs4yv7xe7yk34" } ], "sourcePorts": [], "sourceZones": [], "syslogServer": null, "type": "accessrule", "urlFilter": null, "users": [], "version": "jk3l2ohh5ku2v" } } PLAY RECAP ********************************************************************************************************************************** firepower : ok=6 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
FDMのWeb GUI画面でも、問題なく設定されている事が分かります。
Playbook② デプロイ
この段階では、設定がFTDに反映されておらず、反映するにはデプロイが必要です。
以下のPlaybookでデプロイを行います。
- inventory_ftd_deploy.yml
--- - hosts: ftd gather_facts: no connection: httpapi tasks: - name: Fetch pending changes ftd_configuration: operation: getBaseEntityDiffList register_as: pending_changes - name: Complete playbook when nothing to deploy meta: end_play when: pending_changes | length == 0 - name: Start deployment ftd_configuration: operation: addDeployment register_as: deployment_job - name: Poll deployment status until the job is finished ftd_configuration: operation: getDeployment path_params: objId: '{{ deployment_job.id }}' register_as: deployment_status until: deployment_status.endTime != -1 retries: 100 delay: 3 - name: Stop the playbook if the deployment failed fail: msg: 'Deployment failed. Status: {{ deployment_status.statusMessages }}' when: deployment_status.state != 'DEPLOYED'
出力結果② デプロイ
$ ansible-playbook -i inventory_ftd_vm playbook_ftd_deploy.yml PLAY [ftd] ********************************************************************************************************************************** TASK [Fetch pending changes] **************************************************************************************************************** ok: [firepower] TASK [Start deployment] ********************************************************************************************************************* changed: [firepower] TASK [Poll deployment status until the job is finished] ************************************************************************************* FAILED - RETRYING: Poll deployment status until the job is finished (100 retries left). FAILED - RETRYING: Poll deployment status until the job is finished (99 retries left). FAILED - RETRYING: Poll deployment status until the job is finished (98 retries left). FAILED - RETRYING: Poll deployment status until the job is finished (97 retries left). FAILED - RETRYING: Poll deployment status until the job is finished (96 retries left). FAILED - RETRYING: Poll deployment status until the job is finished (95 retries left). FAILED - RETRYING: Poll deployment status until the job is finished (94 retries left). FAILED - RETRYING: Poll deployment status until the job is finished (93 retries left). ok: [firepower] TASK [Stop the playbook if the deployment failed] ******************************************************************************************* skipping: [firepower] PLAY RECAP ********************************************************************************************************************************** firepower : ok=3 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
FDM画面でも、デプロイに成功し、保留中の変更がない事が確認できます。
最後にFTDのCLI画面で、設定が反映されたことを確認します。
> show running-config : Saved : : Serial Number: XXXXXXXX : Hardware: NGFWv, 8192 MB RAM, CPU Lynnfield 1991 MHz, 1 CPU (4 cores) : NGFW Version 6.4.0 ! hostname firepower enable password ***** encrypted strong-encryption-disable names no mac-address auto ! interface GigabitEthernet0/0 nameif outside cts manual propagate sgt preserve-untag policy static sgt disabled trusted security-level 0 ip address 192.168.100.46 255.255.255.0 ! interface GigabitEthernet0/1 nameif inside cts manual propagate sgt preserve-untag policy static sgt disabled trusted security-level 0 ip address 10.1.1.2 255.255.255.0 ! (中略) object network any-ipv4 subnet 0.0.0.0 0.0.0.0 object network any-ipv6 subnet ::/0 object network OutsideIPv4Gateway host 192.168.100.1 object network OutsideIPv4DefaultRoute subnet 0.0.0.0 0.0.0.0 object network Ansible-network-host host 192.168.2.1 object-group service |acSvcg-268435457 service-object ip object-group service |acSvcg-268435462 service-object ip access-list NGFW_ONBOX_ACL remark rule-id 268435457: ACCESS POLICY: NGFW_Access_Policy access-list NGFW_ONBOX_ACL remark rule-id 268435457: L5 RULE: Inside_Outside_Rule access-list NGFW_ONBOX_ACL advanced trust object-group |acSvcg-268435457 ifc inside any ifc outside any rule-id 268435457 event-log both access-list NGFW_ONBOX_ACL remark rule-id 268435462: ACCESS POLICY: NGFW_Access_Policy access-list NGFW_ONBOX_ACL remark rule-id 268435462: L5 RULE: Allow_Ansible_Traffic access-list NGFW_ONBOX_ACL advanced permit object-group |acSvcg-268435462 object Ansible-network-host any rule-id 268435462 event-log both access-list NGFW_ONBOX_ACL remark rule-id 1: ACCESS POLICY: NGFW_Access_Policy access-list NGFW_ONBOX_ACL remark rule-id 1: L5 RULE: DefaultActionRule access-list NGFW_ONBOX_ACL advanced deny ip any any rule-id 1 (中略)
最後に
今回試した範囲では、FTD 6.4.0では正しく設定が行われ、冪等性も問題ありませんでした。
まだ触り始めたばかりで不明点が多いので、引き続き勉強していきたいと思います。