LoginSignup
33
33

More than 5 years have passed since last update.

Ansibleでfirewalldを設定(ポート開閉)

Last updated at Posted at 2014-10-16

CentOS7のfirewalldに対して、特定portを開閉する Ansible Playbook を書いた。
若干、素直な動作でなかったので、注意点をメモしておく。

★10/17更新:with_itemsを利用した別解を、Playbook2として掲載

お題

こんな感じのPlaybookを書く。

  • Firewalldが起動中のマシンだけ、設定する
  • portのON/OFFを即座に反映、マシンの再起動は不要
  • 永続的に設定、マシン再起動後も設定を維持
  • 既に設定済みの状況なら、余計なことをしない(changed=0)

利用イメージ

普通に実行

ansible-playbook -i hosts firewalld.yml

ポート状態を引数で指定: port 443をオープンする場合

ansible-playbook -i hosts firewalld.yml -e "port=443/tcp state=enabled"

Playbook1|notifyを利用

firewalld.yml
--- # file: firewalld.yml

- hosts: all
  remote_user: root
  gather_facts: False

  vars:
    port: 80/tcp
    state: enabled  # enabled: open, disabled: close

  tasks:
    - name: check if firewalld is running
      command: systemctl is-active firewalld
      register: firewalld_result
      changed_when: False
      ignore_errors: True  # rc is 3 when firewalld is stopped

    - name: set the port state
      firewalld: permanent=True port={{ port }} state={{ state }}
      notify: reload firewalld
      when: firewalld_result.stdout == "active"

  handlers:
    - name: reload firewalld
      service: name=firewalld state=restarted

注意点

上記Playbookでの注意点は3つ。

  1. permanent=True で書き換わるのは、設定ファイルのみ
  2. 設定ファイルが変化した時だけ、リロードするため、notifyを利用
  3. リロードでは restarted を使う: reloadedではダメだった

[補足] reloadedでは、portクローズについては即反映されるが、portオープンは反映されない様子。portクローズ&オープン両方とも即時反映するために、restarted を使っている。

また、少し脇道にそれるけれども。。。

  • 上記PlaybookをRole化して、Tag付けして利用する時は、tasks側とhandlers側の両方のファイルに、同じTagを付ける必要あり

というバグ?に引っかかってしまった。roleとtagの組み合わせは色々と動きがおかしいような。。。相性悪いのかな?

実行結果

firewalld が停止している時

$ ansible-playbook -i hosts firewalld.yml

PLAY [all] ********************************************************************

TASK: [check if firewalld is running] *****************************************
failed: [localhost] => {"changed": false, "cmd": ["systemctl", "is-active", "firewalld"], "delta": "0:00:00.003834", "end": "2014-10-16 17:10:22.305641", "rc": 3, "start": "2014-10-16 17:10:22.301807", "stdout_lines": ["inactive"]}
stdout: inactive
...ignoring

TASK: [set the port state] ****************************************************
skipping: [localhost]

PLAY RECAP ********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0

firewalld が起動中: disabled -> enabled

$ ansible-playbook -i hosts firewalld.yml

PLAY [all] ********************************************************************

TASK: [check if firewalld is running] *****************************************
ok: [localhost]

TASK: [set the port state] ****************************************************
changed: [localhost]

NOTIFIED: [reload firewalld] **************************************************
changed: [localhost]

PLAY RECAP ********************************************************************
localhost                  : ok=3    changed=2    unreachable=0    failed=0

firewalld が起動中: enabled -> enabled

$ ansible-playbook -i hosts firewalld.yml

PLAY [all] ********************************************************************

TASK: [check if firewalld is running] *****************************************
ok: [localhost]

TASK: [set the port state] ****************************************************
ok: [localhost]

PLAY RECAP ********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0

Playbook2|with_itemsを利用

Playbook1だと、より大きなPlaybookに組み込む場合には、望ましくない性質がある。

Handlers部分は、Playbook実行の最後に行われるので、Tasks部分でfirewalldを設定した後、何らかの後続の処理が失敗、実行が中断すると、Handlers部分のリロードが行われない。以降は、Playbookを再実行しても(既に設定は変更済みなわけだから)notifyもされず、設定ファイルは一生リロードされない。

場合によっては、Notifyを使うより、現在のサービス状態だけを見て、その場でサービス状態を変更するほうが良いかもしれない。

そのように書いたPlaybookが以下。

firewalld.yml
--- # file: firewalld.yml

- hosts: all
  remote_user: root
  gather_facts: False

  vars:
    port: 80/tcp
    state: enabled  # enabled: open, disabled: close

  tasks:
    - name: check if firewalld is running
      command: systemctl is-active firewalld
      register: firewalld_result
      changed_when: False
      ignore_errors: True  # rc is 3 when firewalld is stopped

    - name: set the port state
      firewalld: permanent={{ item }} port={{ port }} state={{ state }}
      with_items: [ True, False ]
      when: firewalld_result.stdout == "active"

with_itemsでループを回している。
ループの1回めは item==True になり、設定ファイルのみ変更。
ループの2回めは、item==False になり、サービス状態のみ変更。
設定ファイルのリロードは無し。

小さくなったし、こっちの方がベターかな。

ただこの場合は、設定ファイルを複数回変更する場合は、まとめて1度だけリロードする、といった芸当はできなくなる。
リロード回数が重要な時は、Playbook1も良いかも。

実行結果

firewalld が停止している時

$ ansible-playbook -i hosts firewalld.yml

PLAY [all] ********************************************************************

TASK: [check if firewalld is running] *****************************************
failed: [localhost] => {"changed": false, "cmd": ["systemctl", "is-active", "firewalld"], "delta": "0:00:00.006989", "end": "2014-10-17 07:51:41.657187", "rc": 3, "start": "2014-10-17 07:51:41.650198", "stdout_lines": ["inactive"]}
stdout: inactive
...ignoring

TASK: [set the port state] ****************************************************
skipping: [localhost] => (item=True)
skipping: [localhost]

PLAY RECAP ********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0

firewalld が起動中: disabled -> enabled

with_itemsを使っているので、2つ変更しても changed=1 ですね。

$ ansible-playbook -i hosts firewalld.yml

PLAY [all] ********************************************************************

TASK: [check if firewalld is running] *****************************************
ok: [localhost]

TASK: [set the port state] ****************************************************
changed: [localhost] => (item=True)
changed: [localhost]

PLAY RECAP ********************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0

firewalld が起動中: enabled -> enabled

$ ansible-playbook -i hosts firewalld.yml

PLAY [all] ********************************************************************

TASK: [check if firewalld is running] *****************************************
ok: [localhost]

TASK: [set the port state] ****************************************************
ok: [localhost] => (item=True)
ok: [localhost]

PLAY RECAP ********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0

参考:firewalldの状態確認

ポートの状態は、以下のコマンドで確認した。

# firewall-cmd --list-all
public (default)
  interfaces:
  sources:
  services: dhcpv6-client ssh
  ports: 80/tcp
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:
33
33
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
33