てんこ

ブログ名は愛猫(てん)の愛称です。中身は個人のIT系学習記録です。

AnsibleでFortigateのオブジェクト情報をGETしてみた(汎用版)

別記事でFortigateのポリシーをGETしてみたというのを書きましたが、それの汎用版です。

エンドポイントの指定にはAPIの種類であるtypeと、pathnameというのが必要そうな感じなので、それで大体どんなのが取れるのというのもメモ書きでまとめておきます。

fortios のものはわかりやすいのですが、"_" が3つ、4つなどのものもあるため、試してみたものを徐々に更新します。

理想は、これをuriで書くことなく、標準モジュールのGETメソッドにしてくれると嬉しいんですが…

Ansible関連の実践記事一覧はこちら


汎用Playbook

group_varsで認証情報を指定し、fortiosモジュールの動作を参考にしてuriモジュールでREST APIを利用する形にしています。

---
- hosts: fortigate  
  gather_facts: false

  vars:
    api_type: "cmdb" # cmdb/monitorの2種類があるようです。
    api_path: "system" #必要に応じてここを書き換えたり-eしたりSurveyしたり
    api_name: "ha" #必要に応じてここを書き換えたり-eしたりSurveyしたり

  tasks:
  - name: Login
    uri:
      url: https://{{ ansible_host }}/logincheck
      validate_certs: no
      method : "POST"
      force_basic_auth: yes
      body_format: "form-urlencoded"
      url_username: "{{ ansible_user }}"
      url_password: "{{ ansible_password }}"
      body:
        - [ username, "{{ ansible_user }}" ]
        - [ secretkey, "{{ ansible_password }}" ]
        - [ ajax, "1" ]
    register: http_response
    
  - name: GET Object
    uri:
      url: https://{{ ansible_host }}/api/v2/{{ api_type }}/{{ api_path }}/{{ api_name }}
      validate_certs: no
      method: "GET"
      headers:
        Cookie: "{{ http_response.set_cookie }}"
      return_content: yes
    register: get_obj

  - name: Debug Object
    debug:
      var: get_obj.json.results
      

typeとpath、nameの組み合わせ表

type path name 概要 備考
cmdb firewall policy ファイアウォールのポリシー設定が取得可能 /firewall/policy/1などとするとポリシーIDを限定可能
monitor firewall policy ファイアウォールのポリシー毎のActiveセッションや転送バイト数が取得可能
cmdb firewall address ファイアウォールのアドレスオブジェクトが取得可能 -
cmdb firewall addrgrp ファイアウォールのアドレスグループオブジェクトが取得可能 -
cmdb firewall internet-service ファイアウォールのインターネットサービスオブジェクトが取得可能 -
cmdb system interface 物理IFの各種設定が取得可能。多すぎ… -
monitor system interface 物理IFのIPやTX/RXバイト、エラー数が取得可能 fortios_factsの”system_interface_select”と同じだが、あちらはfilter設定で要素を追加可能な模様。
cmdb dlp sensor DLPセンサー設定が取得可能 -
cmdb ips rule 現在のIPSのシグネチャ情報が取得可能 LTでは、このネタにtemplateを組み合わせてcsv出力した
cmdb firewall ssl-ssh-profile SSLSSHプロファイル情報が取得可能

実際のログ

cmdb/firewall/ssl-ssh-profile

詳細はここをクリック

        "results": [
            {
                "caname": "Fortinet_CA_SSL",
                "comment": "Read-only SSL handshake inspection profile.",
                "ftps": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "bypass",
                    "ports": [
                        990
                    ],
                    "status": "disable",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "https": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "bypass",
                    "ports": [
                        443
                    ],
                    "status": "certificate-inspection",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "imaps": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "inspect",
                    "ports": [
                        993
                    ],
                    "status": "disable",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "mapi-over-https": "disable",
                "name": "certificate-inspection",
                "pop3s": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "inspect",
                    "ports": [
                        995
                    ],
                    "status": "disable",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "q_origin_key": "certificate-inspection",
                "rpc-over-https": "disable",
                "server-cert": "Fortinet_SSL",
                "server-cert-mode": "re-sign",
                "smtps": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "inspect",
                    "ports": [
                        465
                    ],
                    "status": "disable",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "ssh": {
                    "inspect-all": "disable",
                    "ports": [
                        22
                    ],
                    "ssh-algorithm": "compatible",
                    "ssh-policy-check": "disable",
                    "ssh-tun-policy-check": "disable",
                    "status": "disable",
                    "unsupported-version": "bypass"
                },
                "ssl": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "bypass",
                    "inspect-all": "disable",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "ssl-anomalies-log": "enable",
                "ssl-exempt": [],
                "ssl-exemptions-log": "disable",
                "ssl-server": [],
                "untrusted-caname": "Fortinet_CA_Untrusted",
                "use-ssl-server": "disable",
                "whitelist": "disable"
            },
            {
                "caname": "Fortinet_CA_SSL",
                "comment": "Customizable deep inspection profile.",
                "ftps": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "bypass",
                    "ports": [
                        990
                    ],
                    "status": "deep-inspection",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "https": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "bypass",
                    "ports": [
                        443
                    ],
                    "status": "deep-inspection",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "imaps": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "inspect",
                    "ports": [
                        993
                    ],
                    "status": "deep-inspection",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "mapi-over-https": "disable",
                "name": "custom-deep-inspection",
                "pop3s": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "inspect",
                    "ports": [
                        995
                    ],
                    "status": "deep-inspection",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "q_origin_key": "custom-deep-inspection",
                "rpc-over-https": "disable",
                "server-cert": "",
                "server-cert-mode": "re-sign",
                "smtps": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "inspect",
                    "ports": [
                        465
                    ],
                    "status": "deep-inspection",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "ssh": {
                    "inspect-all": "disable",
                    "ports": [
                        22
                    ],
                    "ssh-algorithm": "compatible",
                    "ssh-policy-check": "disable",
                    "ssh-tun-policy-check": "disable",
                    "status": "deep-inspection",
                    "unsupported-version": "bypass"
                },
                "ssl": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "bypass",
                    "inspect-all": "disable",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "ssl-anomalies-log": "enable",
                "ssl-exempt": [
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 31,
                        "id": 1,
                        "q_origin_key": 1,
                        "regex": "",
                        "type": "fortiguard-category",
                        "wildcard-fqdn": ""
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 33,
                        "id": 2,
                        "q_origin_key": 2,
                        "regex": "",
                        "type": "fortiguard-category",
                        "wildcard-fqdn": ""
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 3,
                        "q_origin_key": 3,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "adobe"
                    }
                ],
                "ssl-exemptions-log": "disable",
                "ssl-server": [],
                "untrusted-caname": "Fortinet_CA_Untrusted",
                "use-ssl-server": "disable",
                "whitelist": "disable"
            },
            {
                "caname": "Fortinet_CA_SSL",
                "comment": "Read-only deep inspection profile.",
                "ftps": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "bypass",
                    "ports": [
                        990
                    ],
                    "status": "deep-inspection",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "https": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "bypass",
                    "ports": [
                        443
                    ],
                    "status": "deep-inspection",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "imaps": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "inspect",
                    "ports": [
                        993
                    ],
                    "status": "deep-inspection",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "mapi-over-https": "disable",
                "name": "deep-inspection",
                "pop3s": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "inspect",
                    "ports": [
                        995
                    ],
                    "status": "deep-inspection",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "q_origin_key": "deep-inspection",
                "rpc-over-https": "disable",
                "server-cert": "",
                "server-cert-mode": "re-sign",
                "smtps": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "inspect",
                    "ports": [
                        465
                    ],
                    "status": "deep-inspection",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "ssh": {
                    "inspect-all": "disable",
                    "ports": [
                        22
                    ],
                    "ssh-algorithm": "compatible",
                    "ssh-policy-check": "disable",
                    "ssh-tun-policy-check": "disable",
                    "status": "deep-inspection",
                    "unsupported-version": "bypass"
                },
                "ssl": {
                    "allow-invalid-server-cert": "disable",
                    "client-cert-request": "bypass",
                    "inspect-all": "disable",
                    "unsupported-ssl": "bypass",
                    "untrusted-cert": "allow"
                },
                "ssl-anomalies-log": "enable",
                "ssl-exempt": [
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 31,
                        "id": 1,
                        "q_origin_key": 1,
                        "regex": "",
                        "type": "fortiguard-category",
                        "wildcard-fqdn": ""
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 33,
                        "id": 2,
                        "q_origin_key": 2,
                        "regex": "",
                        "type": "fortiguard-category",
                        "wildcard-fqdn": ""
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 3,
                        "q_origin_key": 3,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "adobe"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 4,
                        "q_origin_key": 4,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "Adobe Login"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 5,
                        "q_origin_key": 5,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "android"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 6,
                        "q_origin_key": 6,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "apple"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 7,
                        "q_origin_key": 7,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "appstore"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 8,
                        "q_origin_key": 8,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "auth.gfx.ms"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 9,
                        "q_origin_key": 9,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "citrix"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 10,
                        "q_origin_key": 10,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "dropbox.com"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 11,
                        "q_origin_key": 11,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "eease"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 12,
                        "q_origin_key": 12,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "firefox update server"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 13,
                        "q_origin_key": 13,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "fortinet"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 14,
                        "q_origin_key": 14,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "googleapis.com"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 15,
                        "q_origin_key": 15,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "google-drive"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 16,
                        "q_origin_key": 16,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "google-play2"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 17,
                        "q_origin_key": 17,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "google-play3"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 18,
                        "q_origin_key": 18,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "Gotomeeting"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 19,
                        "q_origin_key": 19,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "icloud"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 20,
                        "q_origin_key": 20,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "itunes"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 21,
                        "q_origin_key": 21,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "microsoft"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 22,
                        "q_origin_key": 22,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "skype"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 23,
                        "q_origin_key": 23,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "softwareupdate.vmware.com"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 24,
                        "q_origin_key": 24,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "verisign"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 25,
                        "q_origin_key": 25,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "Windows update 2"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 26,
                        "q_origin_key": 26,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "live.com"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 27,
                        "q_origin_key": 27,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "google-play"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 28,
                        "q_origin_key": 28,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "update.microsoft.com"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 29,
                        "q_origin_key": 29,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "swscan.apple.com"
                    },
                    {
                        "address": "",
                        "address6": "",
                        "fortiguard-category": 0,
                        "id": 30,
                        "q_origin_key": 30,
                        "regex": "",
                        "type": "wildcard-fqdn",
                        "wildcard-fqdn": "autoupdate.opera.com"
                    }
                ],
                "ssl-exemptions-log": "disable",
                "ssl-server": [],
                "untrusted-caname": "Fortinet_CA_Untrusted",
                "use-ssl-server": "disable",
                "whitelist": "disable"
            }
        ],
        "revision": "156.0.2.661485498.1576996671",
        "serial": "FGT6xxxxxx",
        "status": "success",
        "vdom": "root",
        "version": "v6.0.8"
    }
}

Ansiblejpネットワーク部でLTをしてきました。

2019/11/20に、Ansibleユーザ会(Ansiblejp)のイベントで、「ネットワーク部」という分科会が開催され、その場でLTをさせていただきました。

人生初LTということもあり、とても貴重な経験をさせていただきましたので、その際に感じたことなどの記録を残しておきます。

イベントそのものについて

以下URLのイベントです。

ansible-users.connpass.com

当日発表した資料について

直前までSlideshareにしようとしてたんですが、11時くらいから、コンテンツのアップロードはできるものの変換処理が完了しなかったこともあり、急遽Speakerdeckへ切り替えました。

speakerdeck.com

Slideshareの件は、前日・当日発生していたOffice365障害などとも関連があるのでしょうか…(調べてません)

LTについて

やったこと(Y)

  • LT資料のつくり方などを、TLに流れてきた資料を参考にざっくり考える
  • LT資料のために、少し中途半端だったFortios系モジュールの検証を進める
  • LT資料の作成(1週間くらい前に仮組み完了。当日AMまで修正)
  • LT資料の中で誤解を招く表現などがないか、Ansible実践ガイドと照らし合わせて確認
  • LT当日に発表練習

わかったこと(W)

  • LTはすごい楽しい。LT未経験の方は習熟度などにあまり躊躇せず、ぜひやるべき。

    • LTの準備は前後タスクまで含めて非常にいい経験になる。
      • 自分が、どこまでわかってて、どこから分かっていないかが整理できる。
      • 想像力も膨らむ。あ、次これやりたいなと思えるようになる。
    • 自分という存在と、興味のあることを知ってもらえる非常にいい機会になる。
      • 自己紹介を済ませているのと同じなので、懇親会があった際なども話しかけられる材料になりやすい。
  • Ansibleユーザ会の中でも、fortiosモジュールに興味がある人がいてくれることがわかった。

    • fortiosモジュールの話、待ってましたというツイートを後から見て、泣きそうになった。
    • 試してみようとか言っていただける人もいた。他者の行動のきっかけにしてもらえるのは、本当にうれしかった。
  • 極度自動化戦隊の一体感は素晴らしい。

次やること(T)

  • もっと「発表を見終わった相手に何を伝えたいか」を意識する。
    • 全部詰め込むのは無理。伝えたいことにフォーカスしたほうが良い。
  • ちゃんと事前練習する。
  • 横道に逸れるなら、それもシナリオのうちに入れて時間内に終われるように計算しておく。

反省点

  • 発表練習は当日になってからやるもんじゃない(あたりまえ)。
  • 操作練習もしないと、マウスでスライド動かなくてアワアワすることになる。
  • もっと聞いていただいている方々の顔・反応を見て話すようにすればよかった。
  • ギリギリの時間のなか、調子に乗ってシナリオにない余計なことを言う悪い癖がまた出たので、やるならやるで構成をちゃんと考えるべきだった。

素の感想

超~~~~楽しかったです。

今年の2月くらいからコミュニティに参加させていただいていましたが、普段からもくもく会参加もリモートばかりなのもあり、数名の方以外は面識もない状態でした。 そのため、こういう機会を生かして改めてお会いできたり、特定個人として認識していただけたり、さらに前に進むための活力をいただけたりと、良いことづくめでした。

私の話は、どちらかというと「これを使うとこんなことができる」という話だったのですが、他の方の発表は「こんな課題を解決するために、Ansibleを現場で活用する(した)」という課題解決のためのAnsible、という位置づけの発表でした。

特にネットワーク系は文献も少ないこともあり、「What(なにができるか)」につながる材料も貴重なのですが、やはり聞いていても「Why(なぜ)/How(どのように)/Where(どのような環境で)」についての内容のほうが、聴講されてた方の反応も良かったように思いました。

「あるある」「あぁー」「わかる!」という反応を自分もしていましたし、やはり、参加される方の多くはすでにAnsibleを利用されており、利用する上で苦労もされていて、工夫の仕方などを参考にしたい、というような目的をお持ちなのではないかなと感じました。

ちなみに、自身のLT前後に実況ツイートの数が激減してるのですが、緊張のためと、発表後の放心状態のためです。特に後ろのplatinaさんのLTは前半放心状態で、ほとんど聞けてませんでした(大変申し訳ない。資料公開期待しております。)

今後について

Ansibleについては、現時点では業務では全く使っておらず、帰宅後や休日合間を縫って趣味のレベルで取り組んでいるのが現状で、それをちゃんとやり始められたのが今年の10月からです。まだ学習中ということもあり「こんなことができるのか」とWhatを知る毎日です。

Why/Howは実業務での利用経験がないので話できないなーと思ってもいたのですが、せっかくNUC環境も整えたので、(現場に似せる形で)「ボクの考えた運用がつらいシステム」なんかを作ってみれば、類似の経験もできそうです。(ドMかな?)

また、実践ガイド第三版で、よこちさんがネットワークモジュールの動作確認環境の作成方法を紹介してくださっているので、そちらも活用していろいろな機器の組み合わせを試してみます。

自分が関与している業務環境への導入はまだまだ先になると思いますが、業務環境への導入を一つの目標として、今後もAnsibleをはじめとしたIaCの勉強を続け、何かしらの形でコミュニティにも関わり続けていきたいなと改めて感じました。

まずは… Ansible Advent Calendar 2019か…?

AnsibleでFortigateのポリシー情報をGETしてみた

別記事でも書きましたが、現状ではfortios用のモジュールに、GETのモジュールは無いように思えます。

なので、パケットキャプチャで取得したHTTPの情報から、uriモジュールを利用してGETリクエストが発行できるPlaybookを書いてみました。

Ansible関連の実践記事一覧はこちら


目的

fortios系モジュールには無いと思われる、現在の情報取得をPlaybookで実現するため

環境

  • OS: CentOS Linux release 7.7.1908 (Core)
  • Ansible : 2.9.0
  • Python: 2.7.6 / 3.6.8
  • Fortios: 6.0.6

やってみたいと思ったこと

現在のPolicyの情報を、json形式で取得したい

作成したPlaybook

ユーザ名やらパスワードやらは、inventoryだったりgroup_varsだったりに書いています。

---
- hosts: fortigate  
  gather_facts: false
  connection: local # httpapiでも利用可能でした。
  vars:
    host : "{{ ansible_host }}"

  tasks:
  - name: Login
    uri:
      url: https://{{ host }}/logincheck
      validate_certs: no
      method : "POST"
      force_basic_auth: yes
      body_format: "form-urlencoded"
      url_username: "{{ ansible_user }}"
      url_password: "{{ ansible_password }}"
      body:
        - [ username, "{{ ansible_user }}" ]
        - [ secretkey, "{{ ansible_password }}" ]
        - [ ajax, "1" ]
    register: http_response
    
  - name: debug
    debug:
      var: http_response

  - name: GET Policy
    uri:
      url: https://{{ host }}/api/v2/cmdb/firewall/policy
      validate_certs: no
      method: "GET"
      headers:
        Cookie: "{{ http_response.set_cookie }}"
      return_content: yes
    register: get_policy

  - name: debug2
    debug:
      var: get_policy.json
      

結果(debug2の部分)

  fw01 ok: {
    "changed": false, 
    "get_policy.json": {
        "build": 272, 
        "http_method": "GET", 
        "http_status": 200, 
        "name": "policy", 
        "path": "firewall", 
        "results": [
            {
                "action": "accept", 
(略)
                "dstaddr": [
                    {
                        "name": "all", 
                        "q_origin_key": "all"
                    }
                ], 
                "dstaddr-negate": "disable", 
                "dstintf": [
                    {
                        "name": "wan1", 
                        "q_origin_key": "wan1"
                    }
                ], 
(略)
                "ippool": "disable", 
                "ips-sensor": "", 
                "label": "", 
                "learning-mode": "disable", 
                "logtraffic": "disable", 
                "logtraffic-start": "disable", 
                "match-vip": "disable", 
                "name": "", 
                "nat": "disable", 
(略)
                "policyid": 1, 
                "poolname": [], 
                "profile-group": "", 
                "profile-protocol-options": "default", 
                "profile-type": "single", 
                "q_origin_key": 1, 
                "radius-mac-auth-bypass": "disable", 
                "redirect-url": "", 
                "replacemsg-override-group": "", 
                "rsso": "disable", 
                "rtp-addr": [], 
                "rtp-nat": "disable", 
                "scan-botnet-connections": "disable", 
                "schedule": "always", 
                "schedule-timeout": "disable", 
                "send-deny-packet": "disable", 
                "service": [
                    {
                        "name": "SSH", 
                        "q_origin_key": "SSH"
                    }
                ], 
                "service-negate": "disable", 
                "session-ttl": 0, 
                "spamfilter-profile": "", 
                "srcaddr": [
                    {
                        "name": "all", 
                        "q_origin_key": "all"
                    }
                ], 
                "srcaddr-negate": "disable", 
                "srcintf": [
                    {
                        "name": "internal", 
                        "q_origin_key": "internal"
                    }
                ], 
(略)
                "status": "enable", 
(略)
            }, 
            {
(略)
                "policyid": 2, 
(略)            }
        ], 
        "revision": "80.0.0.661485498.1572955273", 
        "serial": "FGT6xxxxxxxxxxxx", 
        "status": "success", 
        "vdom": "root", 
        "version": "v6.0.6"
    }
}

書き忘れ

VDOM指定を書き忘れました…これでも取れるんですが、VDOM指定したい場合どうすればいいか、調べてまた修正します。

どうやら、2個目のurlの末尾に、?vdom=<vdom_name>とつけると対象のVDOM指定ができるようです。

感想

思いのほか、手軽に現在のポリシー情報のGETが実現できました。

やってることとしては、キャプチャを取った結果に従って、単純に実装するだけなのでそれほど難しいことはやっていないかと思いますが、これでAPIのエンドポイントさえわかれば、簡単に情報が取得できそうです。

ここでは結局、コネクションプラグインはlocalを使っているので、httpapiよりもfortiosapiのほうが一式で考えると相性がいいのかな…?

と、httpapi使えない頃の自分は考えていましたが、httpapiでも問題なく利用可能でした。 そうすると、やはりhttpapi側に統一していくべきですね…

AnsibleでFortigateのhttpapi利用がうまくいっていない話→Python3に上げたら解消!

AnsibleでFortigateをREST APIで操作する方法は、以下の2種類が存在します。

  • fortiosapi (Legacy Mode)
  • httpapi (今後の推奨)

私の環境では、前者のfortiosapiは今のところ問題なく動作しますが、後者のhttpapiはうまく動いていません。

[2019/11/10追記] Python3にあげることで解消しました!!

いったん棚上げしますが、調べている最中の備忘録です。

Ansible関連の実践記事一覧はこちら


目的

自身の環境で、httpapi経由でFortigateが操作できない原因を明らかにするため

環境

  • FortiOS : 6.0.6
  • Ansible実行環境のOS: Windows 10 WSL2上のUbuntu 18.04.3
  • 上記環境内のAnsible : 2.9.0
  • 上記環境内のPython: 2.7.15+
(ansible29) ansible@ENVY360:~/ansible-study$ ansible --version
ansible 2.9.0
  config file = /home/ansible/ansible-study/ansible.cfg
  configured module search path = [u'/home/ansible/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/ansible/ansible29/local/lib/python2.7/site-packages/ansible
  executable location = /home/ansible/ansible29/bin/ansible
  python version = 2.7.15+ (default, Oct  7 2019, 17:39:04) [GCC 7.4.0]

httpapiを利用すると発生するエラー

以下のようなエラーが発生してfailedになります。-vvvつけた結果も、見てもさっぱり…(T_T)

An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ansible.module_utils.connection.ConnectionError: addinfourl instance has no attribute 'getheaders' fw01 failed | msg: MODULE FAILURE See stdout/stderr for the exact error

fortios系モジュールのAPI利用手順

fortiosapi側

リファレンスがありませんので実測からですが、以下のような流れのようです。

No. 方向 内容
1. [Ansible->FGT] /logincheck に対してPOSTで認証情報を投げる
2. [Ansible<-FGT] 上記リクエストに対して、問題ない場合にはSet-Cookieccsrftokenの値が返される
3. [Ansible->FGT] 2.で返されたHTTPヘッダを付けた状態でAPIが利用可能かの確認のため、GET /api/v2/cmdb/system/status?global=1 というリクエストを投げる(これは一例。処理をしようとするエンドポイントに合わせてこの値は変化する)
4. [Ansible<-FGT] 上記リクエストに対して、問題ない場合には200 OKのレスポンスが返る
5. [Ansible->FGT] 3.と同様、2.で返されたヘッダを付与した状態で、目的のエンドポイントに対してGET/PUTメソッドでリクエストを投げる。(fortios_facts以外はおそらくPUT)
6. [Ansible<-FGT] 5.のリクエストに対し、処理を行った結果をレスポンスで返す
7. [Ansible->FGT] 処理が完了したら、FIN/ACKでコネクションを切断する

上記のような動作のため、同一のコネクションを使いまわしています。

httpapi側

fortiosapiとは異なり、常にコネクションを切断→再接続していました。終了の処理もより丁寧になっています。

No. 方向 内容
1. [Ansible->FGT] /logincheck に対してPOSTで認証情報を投げる
2. [Ansible<-FGT] 上記リクエストに対して、問題ない場合にはSet-Cookieccsrftokenの値が返される
3. [Ansible->FGT] FIN/ACKでコネクションを切断する。
4. [Ansible->FGT] 2.で返されたHTTPヘッダを付けた状態でAPIが利用可能かの確認のため、GET /api/v2/cmdb/system/status?global=1 というリクエストを投げる(これは一例。処理をしようとするエンドポイントに合わせてこの値は変化する)
5. [Ansible<-FGT] 上記リクエストに対して、問題ない場合には200 OKのレスポンスが返る
6. [Ansible->FGT] FIN/ACKでコネクションを切断する。
7. [Ansible->FGT] 4.と同様、2.で返されたヘッダを付与した状態で、目的のエンドポイントに対してGET/PUTメソッドでリクエストを投げる。(fortios_facts以外はおそらくPUT)
8. [Ansible<-FGT] 7.のリクエストに対し、処理を行った結果をレスポンスで返す
9. [Ansible->FGT] FIN/ACKでコネクションを切断する。
10. [Ansible->FGT] /logout に対してPOSTで認証情報を付与したリクエスト投げる
11. [Ansible<-FGT] 10.のリクエストに対し、処理を行った結果をレスポンスで返す
12. [Ansible->FGT] FIN/ACKでコネクションを切断する。

共通していること

Fortigateのモジュールを触っていると、成功する場合は必ずOKが1以上返ってくるのですが、3.の時点でOK=1がカウントされているのだと思います。(ソースを確認したわけではありませんが)。

おそらくは以下のようなカウント方法では無いかと思います。

【ポイント】 APIへのリクエストに対し、成功するか否か

【カウント先判定】 GETメソッドの場合はOK、PUT(POSTもあるのか?)メソッドの場合はChanged

個人的には上「現在の値が何で、リクエストは実行するべきか?」という、冪等性を担保するための仕組み(GETメソッドでの現状確認)が欲しいなあと思うところではありますが、現状は上記のような動作となるため、設定変更系のモジュールについては冪等性はなさそうです。

httpapiではエラーがどの箇所で発生していたか

上述のhttpapiのうち、1.は問題なく動作しているのですが、4.のリクエストが出ず、上述した10.のlogoutに対して切断を行う動作となっています。

そのため、4.で必要なCookie情報をヘッダに付与する、という部分でエラーが発生していると理解しています。そうなると、基本的にはAnsibleホスト側の問題です。

切り分け

OS環境を変えてとりあえずやってみる

もしかしたらPythonのバージョンとかが影響するのかなあとなんもかんがえずにCentOSを構築。

  • 切り分け環境のOS: CentOS Linux release 7.7.1908 (Core)
  • 上記環境内のAnsible : 2.9.0
  • 上記環境内のPython: 2.7.5

CentOS側の環境

(ansible29) [ansible@ansible ansible-playbook]$ ansible --version
ansible 2.9.0
  config file = /home/ansible/ansible-playbook/ansible.cfg
  configured module search path = [u'/home/ansible/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/ansible/ansible29/lib/python2.7/site-packages/ansible
  executable location = /home/ansible/ansible29/bin/ansible
  python version = 2.7.5 (default, Aug  7 2019, 00:51:29) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]

→失敗。

リクエストそのものの違いを見てみる

共通して成功している1.の/logincheckへのPOSTリクエストをキャプチャし、キャプチャデータから各々のリクエストの差を比較してみました。

違いとして見えるのは以下の通り。

カテゴリ fortiosapi httpapi 備考
User-Agent python-requests 2.22 Python-urllib2.7
Connection keepalive close httpapiも1.1でリクエストはしているが
Content-Type 定義なし application/x-www-form-urlencoded
Authenticationヘッダ なし Basic認証 dataとしてはfortiosapiも同じ値を渡している

Pythonのバージョンを上げてやってみる[2019/11/10追記]

よこちさんの環境はPython3という情報をゲットしました。

まあ、サポート終了考えれば、3を使っていて当然ですよね…

python3を以下の手順でインストールし、venvを作成から実施。

$ sudo yum install python3

$ python3 -V
Python 3.6.8

$ python3 -m venv p3-ansible29

$ source p3-ansible29/bin/activate

$ python -V
Python 3.6.8

python3の環境で実行してみる。

(p3-ansible29) [ansible@ansible kiriwake]$ ansible-playbook forti_getfacts_httpapi.yml 
Executing playbook forti_getfacts_httpapi.yml

- fortios on hosts: fortios -
Get Facts....
  fortios01 ok
Debug...
  fortios01 ok: {
    "changed": false,
    "getfact_result": {
        "ansible_facts": {
            "ansible_net_gather_network_resources": [],
            "ansible_net_gather_subset": [],
            "ansible_network_resources": {
                "system_status_select": {
                    "action": "select",
                    "build": 272,
                    "http_method": "GET",
                    "name": "status",
                    "path": "system",
                    "results": {},
                    "serial": "FGT60D4615093671",
                    "status": "success",
                    "vdom": "root",
                    "version": "v6.0.6"
                }
            },
            "discovered_interpreter_python": "/usr/bin/python"
        },
        "changed": false,
        "failed": false
    }
}

- Play recap -
  fortios01                  : ok=2    changed=0    unreachable=0    failed=0    rescued=0    ignored=0   
(p3-ansible29) [ansible@ansible kiriwake]$ 

キタ━━━━(゚∀゚)━━━━!!

結論

Python3にあげることで問題なく利用ができるようになりました。

よこちさん、大変ご迷惑をおかけいたしました…

以下に、棚上げ時点での弱音を残しておきますが、実際にどんな差が出てPython2系だとだめなのかはちょっと気になります。すごーーーく時間がある時に調べられたら調べてみたいと思います。

■↓ここから棚上げ時↓■

ここから先行くのはUser-Agentで定義されている各々のライブラリを確認する必要があるのかなあと思い、python不勉強なのでいったん棚上げします。

もしかすると、よこちさんの環境とはFortiOSの環境も異なるので、そのあたりの差分も影響しているのかもしれません。

ぐぐぐ…悔しいが、前に進みます…

どなたか、何か気が付いた点があれば、Twitterなりなんなりでコメントいただけると泣いて喜びます…

AnsibleでFortigateの情報取得モジュール(fortios_facts)を試してみた。

AnsibleでのFortigate操作について、fortios_factsモジュールの出力について書いておきます。

Ansible関連の実践記事一覧はこちら


目的

唯一の情報取得用のモジュールであるfortios_factsが出力できる内容を確認するため

環境

  • ansible: 2.9.0
  • Fortios: 6.0.6
  • ansibleのコネクションプラグイン: local(fortiosapiライブラリ利用)

サンプルPlaybook

別のエントリにも書いてありますが、以下のように記載します。

---
- hosts: localhost 
  gather_facts: false
  vars:
    host: "10.254.254.254"
    username: "admin"
    password: "password"
    vdom: "root"
    ssl_verify: "no"

  tasks:
  - name: Get Facts.
    fortios_facts:
      host: "{{ host }}"
      username: "{{ username }}"
      password: "{{ password }}"
      vdom: "{{ vdom }}"
      ssl_verify: "{{ ssl_verify }}"
      https: "yes"
      gather_subset:
        - fact: 'system_status_select' #ここに欲しい要素の情報を書く
    register: getfact_result

  - name: Debug
    debug: 
      var: getfact_result

fortios_factsで利用可能な要素

利用可能な要素とその概要は以下の通りです。

要素名 概要
system_current-admins_select 管理者ユーザがログインしたイベントのログ
system_firmware_select 利用可能なファームウェアの表示【未確認】
system_fortimanager_status fortimanagerとの接続ステータス【未確認】
system_ha-checksums_select HAのConfigチェックサム状態の表示【未確認】
system_interface_select インターフェースの状態やカウンタの表示
system_status_select 用途不明… 【未確認】
system_time_select FW本体の時刻情報の取得

なにかしら変な値を入れたりすると、以下のようなエラーメッセージが表示され、利用できるのが以下の要素だというのがわかります。(公式のモジュール群説明にも、ソースにも書いてあります)

TASK [Get Facts.] ***********************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Subset must be one of [system_current-admins_select, system_firmware_select, system_fortimanager_status, system_ha-checksums_select, system_interface_select, system_status_select, system_time_select], got system_ha_select"}

system_current-admins_select

以下のように、unixtimeでイベントの発生タイミングとログインユーザ、現在のログイン状態などが表示されます。

実際のログはクリックして展開

ok: [localhost] => {
    "getfact_result": {
        "ansible_facts": {
            "ansible_net_gather_network_resources": [], 
            "ansible_net_gather_subset": [], 
            "ansible_network_resources": {
                "system_current-admins_select": {
                    "action": "select", 
                    "build": 272, 
                    "http_method": "GET", 
                    "name": "current-admins", 
                    "path": "system", 
                    "results": [
                        {
                            "admin": "admin", 
                            "disconnect_enabled": true, 
                            "id": 31, 
                            "is_current": false, 
                            "method": "https", 
                            "profile": "super_admin", 
                            "srcaddr": "10.254.254.1", 
                            "time": 1572681512
                        }, 
                        {
                            "admin": "admin", 
                            "disconnect_enabled": true, 
                            "id": 43, 
                            "is_current": false, 
                            "method": "http", 
                            "profile": "super_admin", 
                            "srcaddr": "10.254.254.1", 
                            "time": 1572682842
                        }, 
(略)
                    ], 
                    "serial": "FGTxxxxxxxxxxxxxxx", 
                    "status": "success", 
                    "vdom": "root", 
                    "version": "v6.0.6"
                }
            }
        }, 
        "changed": false, 
        "failed": false
    }
}

system_firmware_select

インターネットから切り離して検証しているのですが、おそらくavailableというところに、オンラインでアップデート可能なファームの情報が出てくるのだと思います。

【12/22追記】別記事で実際にオンラインにしてファーム情報を取得してきましたので、そちらもご参照ください!

実際のログはクリックして展開

(冒頭は省略)
"system_firmware_select": {
    "action": "select", 
    "build": 272, 
    "http_method": "GET", 
    "name": "firmware", 
    "path": "system", 
    "results": {
        "available": [], 
        "current": {
            "branch-point": 272, 
            "build": 272, 
            "id": "current", 
            "major": 6, 
            "minor": 0, 
            "name": "FortiOS", 
            "notes": "http://docs.fortinet.com/d/fortios-6.0.6-release-notes/download", 
            "patch": 6, 
            "platform-id": "FGT60D", 
            "release-type": "GA", 
            "source": "current", 
            "version": "v6.0.6"
        }
    }, 
    "serial": "FGxxxxxxxxxxxxxxxx", 
    "status": "success", 
    "vdom": "root", 
    "version": "v6.0.6"
}

system_fortimanager_status

fortimanager使ってないのでどんな情報が出るのが正しいのかわかりません…

実際のログはクリックして展開

"system_fortimanager_status": {
    "action": "status", 
    "build": 272, 
    "http_method": "GET", 
    "name": "fortimanager", 
    "path": "system", 
    "results": {},  # ここに何か出てくるはず…
    "serial": "FGxxxxxxxxxxxxx", 
    "status": "success", 
    "vdom": "root", 
    "version": "v6.0.6"
}, 

system_ha-checksums_select

おそらく、HAを組んでいればHAを組んだ対抗同士の設定情報のリビジョン番号などが出るのかなと思いますが、1台しかないので確認できず…

【12/22追記】取得してみました。各パーティション毎の、自分自身と対抗側のチェックサムを表示してくれるようですね。

実際のログはクリックして展開

"system_ha-checksums_select": {
    "action": "select",
    "build": 303,
    "http_method": "GET",
    "name": "ha-checksums",
    "path": "system",
    "results": [
        {
            "checksum": {
                "all": "8d840039e887d0ae71395107a396adb7",
                "global": "2bdb9a96e1e3afe9870b15d4339c20b7",
                "root": "a0f21470df97822589f0a2801dd9c915"
            },
            "is_manage_master": 1,
            "is_root_master": 1,
            "serial_no": "FGT60*******"
        },
        {
            "checksum": {
                "all": "8d840039e887d0ae71395107a396adb7",
                "global": "2bdb9a96e1e3afe9870b15d4339c20b7",
                "root": "a0f21470df97822589f0a2801dd9c915"
            },
            "is_manage_master": 0,
            "is_root_master": 0,
            "serial_no": "FGT60*******"
        }
    ],
    "revision": "149586697367309",
    "serial": "FGT60D******", #アクセスした側のホストのSerial
    "status": "success",
    "vdom": "root",
    "version": "v6.0.8"
}

system_interface_select

インターフェースのカウンタなどが表示されます。

…あれ?今気が付きましたが、スイッチモードになってるinternalのは表示されてませんね。インターフェース分離すれば個別のインターフェースになって表示されるんでしょうか…

実際のログはクリックして展開

"system_interface_select": {
    "action": "select", 
    "build": 272, 
    "http_method": "GET", 
    "name": "interface", 
    "path": "system", 
    "results": {
        "dmz": {
            "alias": "", 
            "duplex": 1, 
            "id": "dmz", 
            "ip": "10.10.10.1", 
            "link": true, 
            "mac": "00:00:00:00:00:00", 
            "mask": 24, 
            "name": "dmz", 
            "rx_bytes": 3112840, 
            "rx_errors": 0, 
            "rx_packets": 5276, 
            "speed": 1000.0, 
            "tx_bytes": 240, 
            "tx_errors": 0, 
            "tx_packets": 4
        }, 
        "modem": {
(略)
        }, 
        "wan1": {
(略)
        }, 
        "wan2": {
(略)
        }
    }, 
    "revision": "1572684445.19228", 
    "serial": "FGTxxxxxxxxxxxxxxxxxxx", 
    "status": "success", 
    "vdom": "root", 
    "version": "v6.0.6"
}, 

system_status_select

何が出るんだろう…

【12/22追記】HA組んでも表示されるものは変わりませんでした。うーん?

実際のログはクリックして展開

"system_status_select": {
    "action": "select", 
    "build": 272, 
    "http_method": "GET", 
    "name": "status", 
    "path": "system", 
    "results": {},  #ここになにかでるはず…
    "serial": "FGxxxxx", 
    "status": "success", 
    "vdom": "root", 
    "version": "v6.0.6"
}, 

system_time_select

本体の時刻の情報がUnixtime形式で取得できるようです。

実際のログはクリックして展開

"system_time_select": {
    "action": "select", 
    "build": 272, 
    "http_method": "GET", 
    "name": "time", 
    "path": "system", 
    "results": {
        "time": 1572684445
    }, 
    "serial": "FGTxxxxxx", 
    "status": "success", 
    "vdom": "root", 
    "version": "v6.0.6"
}

他の使いかた

複数要素を同時に出力させ、フィルターをかけることで該当する要素(ポート指定だけとか)もできるようです。

2019/11/6時点では未確認です。

感想

これだけしか取れないか‥‥という感想です(T_T)

現在の設定情報(アドレスオブジェクトやユーザオブジェクト、ポリシーなど)が取れないかと期待もしたのですが…

「現状の設定を確認」→「想定通りでなければ設定」のような処理を実装しようと思うと、fortiosモジュール単体では実現できず、自分で処理をくみ上げる必要がある、ということだと理解しました。

むう、実用化への道のりは長い…か…?