てんこ

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

AnsibleでFortigateのポリシーを設定してみた(その1)

前回に引き続き、AnsibleでFortigateの設定をしてみました。

なかなか素敵なハマりをしたので、ご紹介しつつ。

入れ替え(move)とか並び順とかはその2を別で書こうと思いますので、まずはスタンダードなところから。

■間違い探し

みなさんは、以下の2つのポリシーの違いは判りますか…?

f:id:tatematsu_san:20191104143305p:plain

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


目的

AnsibleでFortigateのFirewallポリシーを設定できるか試すため

環境

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

実践

Firewallポリシーの追加・削除に利用するモジュール

ansible2.8にリリースされた、fortios_firewall_policyモジュールを利用します。

モジュールの公式リファレンスはこちら

ansible2.9からstateなどが加わっているようです。むしろ、これまでは削除ってどうしてたんだろう…

ポリシーの追加

以下のようなPlaybookでポリシーの追加が可能です。

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

  tasks:
  - name: Configure Firewall Policy(add).
    fortios_firewall_policy:
      host: "{{ host }}"
      username: "{{ username }}"
      password: "{{ password }}"
      vdom: "{{ vdom }}"
      ssl_verify: "{{ ssl_verify }}"
      state: "present"
      firewall_policy:
        policyid: "1"
        srcintf:
          - name: "internal"
        dstintf:
          - name: "wan1"
        srcaddr: 
          - name: "all"
        dstaddr: 
          - name: "all"
        service:
          - name: "ALL"
        schedule: "always"
        logtraffic: "disable"
        fsso: "disable"
        action: "accept"
        status: "enable"

ポリシーの削除

stateをabsentにして、policyidだけ書けば削除が可能でした。

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

  tasks:
  - name: Configure Firewall Policy (Delete)
    fortios_firewall_policy:
      host: "{{ host }}"
      username: "{{ username }}"
      password: "{{ password }}"
      vdom: "{{ vdom }}"
      ssl_verify: "{{ ssl_verify }}"
      state: "absent"
      firewall_policy:
        policyid: "1"

ポリシーの編集

編集したいポリシーのpolicyidを指定することで、上書きすることが可能でした。

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

  tasks:
  - name: Configure Firewall Policy (modify).
    fortios_firewall_policy:
      host: "{{ host }}"
      username: "{{ username }}"
      password: "{{ password }}"
      vdom: "{{ vdom }}"
      ssl_verify: "{{ ssl_verify }}"
      state: "present"
      firewall_policy:
        policyid: "1"
        srcintf:
          - name: "internal"
        dstintf:
          - name: "wan1"
        srcaddr: 
          - name: "all"
        dstaddr: 
          - name: "all"
        service:
          - name: "SSH" # もともとは”ALL"
        schedule: "always"
        logtraffic: "disable"
        fsso: "disable"
        action: "accept"
        status: "enable"

↓変更後のポリシー f:id:tatematsu_san:20191104145402p:plain

冪等性があるか?

全く同じPlaybookを2回実行しても、changed=1になってしまいました…

とはいえ、結果的には同じ内容になるので、changedになるのを知ってさえいれば問題なさそうです。

f:id:tatematsu_san:20191104144323p:plain

わかったこと

想像通りな感じで、ポリシーについては、それほど支障なく追加・削除・編集はできそうです。

また、連続でPlaybook書いてると、やっぱりhttpapi側に切り替えたくなってきました…長い…

httpapiで操作する環境についてのブログをよこちさんが書いてくださったので、そちらも参考にしてください!

tekunabe.hatenablog.jp

ただ、公式なこのモジュールの説明見ても、httpapi使えるとは書いてないんですよね。修正漏れなのか、本当に使えないのか…httpapiの環境が動いたらやってみます。

気を付けていただきたいこと

ansibleによるFortigateの操作は、原則としてFortigateのREST APIを利用しており、これはCLIとは異なります。

なので、しっかりと動作試験を行ったうえで利用してください。やってはいけない例を1つご紹介します。

やってはいけないPlaybook

以下のようなPlaybookを実行したとします。

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

  tasks:
  - name: Configure Firewall Policy Ng Pattern (Removed at Reboot).
    fortios_firewall_policy:
      host: "{{ host }}"
      username: "{{ username }}"
      password: "{{ password }}"
      vdom: "{{ vdom }}"
      ssl_verify: "{{ ssl_verify }}"
      state: "present"
      firewall_policy:
        policyid: "1"
        srcintf:
          - name: "internal"
        dstintf:
          - name: "wan1"
        srcaddr: 
          - name: "all"
        dstaddr: 
          - name: "all"
        service:
          - name: "ALL"
        action: "accept"
        status: "enable"

↓実行結果はok,changedで成功した時と同じような感じ。 f:id:tatematsu_san:20191104151109p:plain

-vつけてもこんなかんじ。ステータスコードは200でSuccess。

f:id:tatematsu_san:20191104154203p:plain

このときのWebUIの画面(これが冒頭で出した、NGなポリシーのもの) f:id:tatematsu_san:20191104151250p:plain

この状態で再起動するとどうなるか?

以下のようなログが出ます。

f:id:tatematsu_san:20191104150741p:plain

再起動するとポリシーはどうなるか?

消えます…

Configからも消え去ります。

なぜこうなるのか?

REST APIを使うことで、CLIでカバーされているエラーチェックが動かないことが主な要因のようです。

この状態のConfigは以下のようになります。

config firewall policy
    edit 1
        set uuid ee645184-fec9-51e9-d467-0b6fb1a24d6d
        set srcintf "internal"
        set dstintf "wan1"
        set srcaddr "all"
        set dstaddr "all"
        set action accept
        set service "ALL"
    next
end

キーとなるのはなんなのか?

あまり細かくは試していませんが、NGなPlaybookの中に「schedule: "always"」だけ加えたら、再起動後も設定は維持されていました。

CLIだとどうなるか?

CLIで同じように、「schedule」の指定を抜いた状態でendしようとすると、怒られました。

f:id:tatematsu_san:20191104145927p:plain

この状態でポリシーは動作するのか?

pingレベルでは動いてしまいました…(1.1.1.254は暫定で付与したwan1のIP)

f:id:tatematsu_san:20191104152457p:plain

やってはいけないことをやってみて

アンチパターンは貴重な財産ですね。

今回のは、CLIで普段設定する人からすると当たり前かもしれませんが、Playbookを手抜きしていた結果、見つけることができました。

今後もこういうのを見つけたらどんどん載せていきたいと思います。