てんこ

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

AnsibleでFortigateのLB機能を使ってみた

この記事は、Ansible 3 Advent Calendar 2019の22日目の記事です。

昨日分は、よこちさんのNetBoxに関する記事でした。

Ansibleを構成管理ツールとして使う場合、特にTower/AWXを使わない場合、インベントリをAnsibleで管理するか…というのは悩ましい点ですよね。NetBoxも一つの解にできそうですね。

さて、私の分は「またFortigate」です。すみません。

Ansibleのネットワーク系の課題をやると、A10/BIG-IPさんの課題でよく例に出る「ロードバランシング機能」があります。

実は、Fortigateでもロードバランシング機能が存在します。それをAnsibleで利用するまでの流れを記載させていただきます。

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


目的

fortigateでロードバランシング機能を使うためのPlaybookの書き方や、<your_own_value>を明らかにするため

最初に結論

  • AnsibleでFortigateのLB機能を一通り設定することができた
    • ヘルスチェックなども問題なく動作することが分かった
  • 【残課題】手動での動作確認を、assertなどで実施できるよう学習を進める

Ansible/Fortigate環境

構成図

このような感じの構成で確認を行いました

ロードバランサー的な用語でいうと、「ワンアーム構成」です。

(自宅のネットワーク環境の構成上、これ以外つらいんです。直さねば)

前提条件

  • Webサーバはすでに構築されている前提
    • 各々の index.htmlにはホストを示す異なるコンテンツを配備
  • Fortigateは、シングル構成、DMZのIFにIPが付与されている程度
    • 前回作成したHA環境は今回は使っていません。
  • クライアント(Ansibleホスト)はVIPで提供される「192.168.10.113」にWebアクセスする
    • FortigateがLBとして動作し、ラウンドロビンで各Webサーバにバランシングする
    • ヘルスチェックでDownを検知したサーバには振り分けをしないようにする。
    • Webサーバへ接続する際には、Fortigateのアドレスで送信元NATする。

事前確認

■Webサーバ#1へのアクセス確認
$ curl http://192.168.10.111
Web01(192.168.10.111)

■Webサーバ#2へのアクセス確認
$ curl http://192.168.10.112
Web02(192.168.10.112)

→各Webサーバにアクセスした際、明示的に異なる結果になることを確認

LB機能が動作する環境を作成する

事前の補足

Fortigateのロードバランシング機能は、WebUIのメニュー上は[Virtual Servers]という名称です。

ただ、コマンドは[vip]だったり、[Load Balancing]だったり、いろいろ変わってくるのでちょっとわかりにくいです。

NAT機能部分に[Virtual IP]とか[IP Pool]とかもあるのでお間違え無きよう。

作業の流れ

全体的な作業の流れとしては、以下のようになります。

  1. Feature SetでLoad Balancing機能(表示するか否か)を有効化する。
  2. ヘルスモニターを作成する。
  3. Virtual Serverを作成する。
  4. 作成したVirtual Serverを用いたポリシーを設定する。

なお、最初のFeature Setは、無視しても設定は可能です。

作成したPlaybook

いろいろ使ったので、結構長くなりました。 with_item構成にしたほうが恰好がいい(ヘルスチェックのところとか)とも思うんですが、どういう単位でPlaybookを再利用するかによりますかね。個人的にはわかりやすさ重視なのでべた書きでも良い派です。

構成要素ごとに簡単に説明もします。

---
- hosts: fortigate  
  gather_facts: false

  vars:
    web01_address: "192.168.10.111"
    web02_address: "192.168.10.112"
    vip_address: "192.168.10.113"
    vip_name: "Ansible_TEST_VIP"
    tcp_port: "80"
    vip_if: "dmz"
    balance_method: "round-robin"
    policy_srcintf: "{{ vip_if }}"
    policy_dstintf: "{{ vip_if }}"
    policy_service: "HTTP"

  tasks:

#1. Feature SetでLoad Balancing機能(表示するか否か)を有効化する。
  - name: Enable Loadbalance Feature Visibillity
    fortios_system_settings:
      system_settings:
        gui_load_balance: "enable"

#2. ヘルスモニターを作成する。
  - name: Create Health Monitor(PING)
    fortios_firewall_ldb_monitor:
      firewall_ldb_monitor:
        name: "PING"
        state: "present"
        type: "ping"
        interval: "10"
        retry: "3"
        timeout: "2"
        port: "0"

  - name: Create Health Monitor(TCP_80)
    fortios_firewall_ldb_monitor:
      firewall_ldb_monitor:
        name: "TCP_80"
        state: "present"
        type: "tcp"
        interval: "10"
        retry: "3"
        timeout: "2"
        port: "{{ tcp_port }}"

  - name: Create Health Monitor(HTTP_GET_80)
    fortios_firewall_ldb_monitor:
      firewall_ldb_monitor:
        name: "HTTP_GET_80"
        state: "present"
        type: "http"
        interval: "10"
        retry: "3"
        timeout: "2"
        port: "{{ tcp_port }}"
        http_get: ""
        http_match: ""

#3. Virtual Serverを作成する。
  - name: Create VIP Setting
    fortios_firewall_vip:
      firewall_vip:
        state: "present"
        arp_reply: "enable"
        name: "{{ vip_name }}"
        comment: "{{ vip_name }}"
        id: "0"
        extip: "{{ vip_address }}"
        extintf: "{{ vip_if }}"
        protocol: "tcp"
        extport: "{{ tcp_port }}"
        server_type: "http"
        ldb_method: "{{ balance_method }}"
        type: "server-load-balance"
        monitor:
          - name: "PING"
          - name: "TCP_80"
          - name: "HTTP_GET_80"
        realservers:
          - 
            id: "1"
            client_ip: ""
            ip: "{{ web01_address }}"
            port: "{{ tcp_port }}"
            status: "active"
            healthcheck: "vip"
          - 
            id: "2"
            client_ip: ""
            ip: "{{ web02_address }}"
            port: "{{ tcp_port }}"
            status: "active"
            healthcheck: "vip"

#4. 作成したVirtual Serverを用いたポリシーを設定する。
  - name: Create Firewall Policy for VIP
    fortios_firewall_policy:
      state: "present"
      firewall_policy:
        policyid: "2"
        srcintf:
          - name: "{{ policy_srcintf }}"
        dstintf:
          - name: "{{ policy_dstintf }}"
        service:
          - name: "{{ policy_service }}"
        srcaddr: 
          - name: "all"
        dstaddr: 
          - name:  "{{ vip_name }}"
        schedule: "always"
        action: "accept"
        nat: "enable"
        status: "enable"

1. Feature SetでLoad Balancing機能(表示するか否か)を有効化する。

詳細はこちら

  - name: Enable Loadbalance Feature Visibillity
    fortios_system_settings:
      system_settings:
        gui_load_balance: "enable"

WebUIで見るとこの部分です。

これを有効化すると、左側のメニューの[Policy & Objects]に[Virtual Servers],[Health Check]というメニューが登場します。

2. ヘルスモニターを作成する。

詳細はこちら

  - name: Create Health Monitor(HTTP_GET_80)
    fortios_firewall_ldb_monitor:
      firewall_ldb_monitor:
        name: "HTTP_GET_80"
        state: "present"
        type: "http"
        interval: "10"
        retry: "3"
        timeout: "2"
        port: "{{ tcp_port }}"
        http_get: ""
        http_match: ""

HTTP監視の部分を持ってきました。 fortios_firewall_ldb_monitorというモジュールを使います。モジュールのリファレンスはこちら。

所定のパスの応答(http_get)や、レスポンスのチェック(http_match)も可能なようです。

3. Virtual Serverを作成する。

詳細はこちら

  - name: Create VIP Setting
    fortios_firewall_vip:
      firewall_vip:
        state: "present"
        arp_reply: "enable"
        name: "{{ vip_name }}"
        comment: "{{ vip_name }}"
        id: "0"
        extip: "{{ vip_address }}"
        extintf: "{{ vip_if }}"
        protocol: "tcp"
        extport: "{{ tcp_port }}"
        server_type: "http"
        ldb_method: "{{ balance_method }}"
        type: "server-load-balance"

前半部分では、対象のVIPの名前やアドレス、インターフェースの定義などを行います。

fortios_firewall_vipというモジュールを使います。モジュールのリファレンスはこちら。

ldb_method という部分がロードバランシング機能の肝です。

least-session(既存コネクション数の少ないもの)、Weighted(重みづけラウンドロビン)など、他のアルゴリズムもいくつか選択できるようです。

        monitor:
          - name: "PING"
          - name: "TCP_80"
          - name: "HTTP_GET_80"
        realservers:
          - 
            id: "1"
            client_ip: ""
            ip: "{{ web01_address }}"
            port: "{{ tcp_port }}"
            status: "active"
            healthcheck: "vip"

後半に2.で作成したヘルスモニターや、バランシング対象の実サーバの情報を登録します。対象サーバの指定で、ポリシー用のオブジェクトが使えればいいんですが、利用できないようです。残念。

4. 作成したVirtual Serverを用いたポリシーを設定する。

詳細はこちら

  - name: Create Firewall Policy for VIP
    fortios_firewall_policy:
      state: "present"
      firewall_policy:
        policyid: "2"
        srcintf:
          - name: "{{ policy_srcintf }}"
        dstintf:
          - name: "{{ policy_dstintf }}"
        service:
          - name: "{{ policy_service }}"
        srcaddr: 
          - name: "all"
        dstaddr: 
          - name:  "{{ vip_name }}"
        schedule: "always"
        action: "accept"
        nat: "enable"
        status: "enable"

別の記事でも利用した、fortios_firewall_policyを利用します。ワンアーム構成なので、実態としてはsrcintf/dstintfは同じものです。dstaddrの部分に、作成したVirtual Serverを宣言します。送信元NATはここで定義しています。

動作確認

バランシング確認

Ansibleホストからcurlで確認してみたところ、問題なくバランシングされていました。

$ curl http://192.168.10.113
Web01(192.168.10.111)

$ curl http://192.168.10.113
Web02(192.168.10.112)

$ curl http://192.168.10.113
Web01(192.168.10.111)

$ curl http://192.168.10.113
Web02(192.168.10.112)

NAT確認

192.168.10.61 - - [22/Dec/2019:15:27:39 +0900] "GET / HTTP/1.1" 200 23 "-" "curl/7.29.0"

curlでアクセスしている際の送信元IPがちゃんとFortigateのアドレス(192.168.10.61)になっています。

ヘルスチェック確認(定期監視)

サーバ側のアクセスログは以下のようになっていました。

ちゃんとインターバルで定義した10秒ごとにチェックが来ています。 User-AgentがFortiOS 4.0というのがなんとも…

192.168.10.61 - - [22/Dec/2019:12:13:49 +0900] "GET / HTTP/1.0" 200 23 "-" "FortiGate (FortiOS 4.0)"
192.168.10.61 - - [22/Dec/2019:12:13:59 +0900] "GET / HTTP/1.0" 200 23 "-" "FortiGate (FortiOS 4.0)"
192.168.10.61 - - [22/Dec/2019:12:14:09 +0900] "GET / HTTP/1.0" 200 23 "-" "FortiGate (FortiOS 4.0)"

ヘルスチェック確認(ダウン時)

試しにWeb01のhttpdをダウンさせ、動作確認してみます。

$ curl http://192.168.10.113
Web02(192.168.10.112)

$ curl http://192.168.10.113
Web02(192.168.10.112)

$ curl http://192.168.10.113
Web02(192.168.10.112)

$ curl http://192.168.10.113
Web02(192.168.10.112)

ちゃんとWeb02側だけにバランシングされました。

イベントログにもこんな形でログが記録されます。(VirtualServer側には何も出ません)

まとめ

以下のような結果になりました。

  • AnsibleでFortigateのLB機能を一通り設定することができた
    • ヘルスチェックなども問題なく動作することが分かった
  • 【残課題】手動での動作確認を、assertなどで実施できるよう学習を進める

商用でFortigateをLBとして使うケースはあまりないかも、とも思いますが、中小規模環境で、予算が削られて個別のLBが買えない、なんていう場合には利用できるかもしれません。(性能面は要検証です)

今回は試していませんが、SSLオフロード用機器としても利用できそうです。 また、今回はIPv4のみですが、IPv6や64/46も可能なようです。

なんかAnsibleというよりFortigate機能検証みたいな形になっていますが、どなたかのお役に立てれば幸いです。

バトンリレー

前回に引き続き、今回も無事にバトンをつなぐことができました。

お次は、@atsushi586さんです。

よろしくお願いいたします!