2026年10月7日

2026年10月7日

Ansibleでセキュリティ設定を自動化する方法|基準のロール化・定期パッチ・設定のずれの検知

Ansible でセキュリティ設定を自動化すると、SSH・カーネルパラメーター・fail2ban・自動更新といった対策を、何台のサーバーにも同じ内容で適用し、あとから誰かが手で変えた「ずれ」まで検知できるようになります。この記事では、セキュリティ基準を1つのロールにまとめる書き方、締め出し事故を防ぐ実行手順、定期的なパッチ適用と再起動、そして --check --diff を使った設定のずれの検知までを順に説明します。

手作業のハードニングが崩れていく理由

サーバーを立てた直後に SSH の設定を強化し、ファイアウォールを入れても、半年後には次のような状態になりがちです。

  • 障害対応のときに一時的に緩めた設定が、そのまま残っている
  • 後から追加したサーバーだけ、手順書の一部が抜けている
  • 誰がいつ何を変えたのか分からない

Ansible の利点は、「あるべき状態」をコードとして書いておき、何度実行しても同じ結果になる(冪等性がある)ことです。セキュリティ設定をコード化しておけば、新しいサーバーにも同じ基準を当てられ、既存のサーバーが基準から外れていないかも定期的に確かめられます。

Ansible の導入やインベントリの書き方、初期設定のプレイブックはここでは扱いません。この記事は、すでに Ansible で接続できる状態から、セキュリティ設定を「継続して守る」ことに焦点を当てます。

セキュリティ基準を1つのロールにまとめる

設定はプレイブックに直接並べるより、security_baseline のようなロールに分けておくと、Web サーバー用・DB サーバー用など複数のプレイブックから再利用できます。

roles/security_baseline/
├── defaults/main.yml      # 上書きしてよい既定値
├── tasks/main.yml
├── templates/
│   ├── sshd_hardening.conf.j2
│   └── jail.local.j2
└── handlers/main.yml

変えてよい値は defaults/main.yml に集めます。

# roles/security_baseline/defaults/main.yml
ssh_port: 22
ssh_allow_users: ["deploy"]
fail2ban_maxretry: 5
fail2ban_bantime: 3600

SSH の設定は検証してから置く

SSH の設定ミスは、そのまま接続できなくなる事故につながります。template モジュールの validate を使うと、新しい設定ファイルを sshd -t で検査し、通ったときだけ配置されます。

# roles/security_baseline/tasks/main.yml(抜粋)
- name: SSH の強化設定を置く
  ansible.builtin.template:
    src: sshd_hardening.conf.j2
    dest: /etc/ssh/sshd_config.d/00-hardening.conf
    owner: root
    group: root
    mode: "0644"
    validate: /usr/sbin/sshd -t -f %s
  notify: Reload ssh
# roles/security_baseline/templates/sshd_hardening.conf.j2
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
MaxAuthTries 3
AllowUsers {{ ssh_allow_users | join(' ') }}

validate で -f %s を付けると、検査されるのは置こうとしている断片ファイル単体です。断片だけでは検査できない書き方をする場合は、配置後に sshd -t を実行するタスクを追加し、失敗したらプレイブックを止めます。AllowUsers は便利ですが、ここに自分の作業用ユーザーを入れ忘れると締め出されるので、変数の値は特に注意して確認してください。

カーネルパラメーターと fail2ban

ansible.posix.sysctl モジュールで、ネットワーク関連の安全側の設定をまとめて管理できます。

- name: カーネルパラメーターを設定する
  ansible.posix.sysctl:
    name: "{{ item.name }}"
    value: "{{ item.value }}"
    sysctl_file: /etc/sysctl.d/90-security.conf
    reload: true
  loop:
    - { name: net.ipv4.conf.all.accept_redirects, value: "0" }
    - { name: net.ipv4.conf.all.send_redirects, value: "0" }
    - { name: net.ipv4.tcp_syncookies, value: "1" }
    - { name: kernel.kptr_restrict, value: "2" }

- name: fail2ban を入れる
  ansible.builtin.apt:
    name: fail2ban
    state: present

- name: fail2ban の設定を置く
  ansible.builtin.template:
    src: jail.local.j2
    dest: /etc/fail2ban/jail.local
    mode: "0644"
  notify: Restart fail2ban
# roles/security_baseline/templates/jail.local.j2
[DEFAULT]
bantime  = {{ fail2ban_bantime }}
maxretry = {{ fail2ban_maxretry }}
ignoreip = 127.0.0.1/8 ::1

[sshd]
enabled = true
port    = {{ ssh_port }}

ignoreip に管理用の固定 IP を加えておくと、自分が誤ってブロックされる事故を減らせます。カーネルパラメーターは、コンテナやルーターを兼ねるサーバーでは不要な値や逆効果の値もあるため、役割ごとに値を見直してください。

自動セキュリティ更新とハンドラー

- name: unattended-upgrades を入れる
  ansible.builtin.apt:
    name: unattended-upgrades
    state: present

- name: 自動更新を有効にする
  ansible.builtin.copy:
    dest: /etc/apt/apt.conf.d/20auto-upgrades
    mode: "0644"
    content: |
      APT::Periodic::Update-Package-Lists "1";
      APT::Periodic::Unattended-Upgrade "1";
# roles/security_baseline/handlers/main.yml
- name: Reload ssh
  ansible.builtin.service:
    name: ssh        # RHEL 系では sshd
    state: reloaded

- name: Restart fail2ban
  ansible.builtin.service:
    name: fail2ban
    state: restarted

締め出されないための実行手順

セキュリティ設定は、誤ると管理者自身が入れなくなるのが他の設定との大きな違いです。次の順番で進めると安全です。

  1. ステージングで先に流す。 本番と同じ OS・同じ役割のサーバーで試します。
  2. **--check --diff で差分を見る。** 実際には変更せず、何がどう変わるかを表示します。
  3. 1台ずつ適用する。 プレイに serial: 1 を付けると、1台目で失敗した時点で残りへの適用が止まります。
  4. 既存の SSH セッションを1つ開いたまま実行する。 新しいセッションで接続できることを確かめてから閉じます。
  5. コンソール接続の手段を確認しておく。 クラウドや VPS の管理画面から入れるシリアルコンソールなど、SSH 以外の経路があるかを事前に確かめます。
ansible-playbook -i inventory.ini security.yml --limit staging --check --diff
ansible-playbook -i inventory.ini security.yml --limit web

パスワードや鍵は Vault で暗号化する

プレイブックに DB のパスワードや通知用のトークンを平文で書くと、Git に入った時点で漏えいの原因になります。ansible-vault で値を暗号化しておきます。

ansible-vault encrypt_string 'your-secret-value' --name 'alert_webhook_token'
ansible-playbook -i inventory.ini security.yml --ask-vault-pass

出力された暗号文を変数ファイルに貼り付けて使います。値を扱うタスクには no_log: true を付け、実行ログに中身が出ないようにします。

定期的なパッチ適用と再起動

自動更新に任せられるのはセキュリティ更新の一部で、カーネルの更新を反映するには再起動が要ります。月に1回などの計画メンテナンスでは、次のように1台ずつ更新と再起動を進めます。

# patch.yml
- name: パッケージを更新し、必要なら再起動する
  hosts: web
  become: true
  serial: 1
  tasks:
    - name: パッケージを更新する
      ansible.builtin.apt:
        update_cache: true
        upgrade: dist

    - name: 再起動が必要か確認する
      ansible.builtin.stat:
        path: /var/run/reboot-required
      register: reboot_flag

    - name: 再起動する
      ansible.builtin.reboot:
        reboot_timeout: 600
      when: reboot_flag.stat.exists

/var/run/reboot-required は Debian/Ubuntu で再起動が必要なときに作られるファイルです。RHEL 系では needs-restarting -r の終了コードで判断するなど、方法が異なります。ロードバランサーの後ろに複数台ある構成なら、serial: 1 で1台ずつ再起動すればサービスを止めずに済みます。WordPress が1台構成の場合は、アクセスの少ない時間帯を選び、事前にバックアップを取ってから実行してください。

設定のずれを定期的に検知する

ロールを作ったあとは、「基準どおりになっているか」を定期的に確かめる運用が効果的です。--check モードで実行し、変更予定(changed)が出たサーバーは、誰かが手で設定を変えたことを意味します。

# 週1回などで実行し、結果を残す
ansible-playbook -i inventory.ini security.yml --check --diff > /var/log/ansible-drift-$(date +%F).log 2>&1
grep -E 'changed=[1-9]' /var/log/ansible-drift-$(date +%F).log

grep で changed= が1以上のサーバーが見つかったら、--diff の出力で何が違うのかを確認します。意図した変更ならロールの側を直し、意図しない変更なら基準に戻すか、原因を調べます。

check モードの注意点として、command や shell のタスクは既定では実行されず「スキップ」扱いになり、その結果に依存する後続タスクも正しく判定できないことがあります。確認だけのコマンドには check_mode: false と changed_when: false を付けると、check モードでも実行されて結果が使えます。

既存のハードニング用コレクションを使う選択肢

すべてを自作する代わりに、公開されているロールを土台にする方法もあります。たとえば dev-sec プロジェクトの devsec.hardening コレクション(OS や SSH の強化用ロールを含む)や、CIS Benchmark に沿ったロール群を公開している Ansible Lockdown などが知られています(2026年10月現在)。

ただし、これらは多くの項目を一度に変えるため、そのまま本番に流すと Web サーバーやバックアップの動作に影響することがあります。どの項目が変わるかを --check --diff で確認し、不要な項目は変数で無効にしてから使ってください。ロールの変数名や対応 OS は版によって変わるので、使う版のドキュメントを確認することが前提です。

まとめ

  • セキュリティ設定をロールにまとめると、複数台に同じ基準を当てられ、新しいサーバーにも漏れなく適用できます。
  • SSH 設定は validate で検査してから置き、AllowUsers や ignoreip で自分を締め出さないよう確認します。
  • 本番へはステージング、--check --diff、serial: 1、既存セッションの維持、の順で慎重に適用します。
  • 秘密の値は ansible-vault で暗号化し、no_log: true でログに出さないようにします。
  • 定期パッチは1台ずつ更新と再起動を行い、週1回などの check モード実行で設定のずれを検知します。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲