エラーの内容
RHEL または CentOS ホストに対して Ansible プレイブックを実行すると、最初のタスクで以下のエラーが発生します:
fatal: [web01]: FAILED! => {
"msg": "Aborting, target uses selinux but python bindings (libselinux-python) are not installed!"
}
何も実行されません。Ansible がリモートホスト上の SELinux を検出し、Python バインディングを読み込もうとしたところ、何も見つからずに終了したのです。ファイルコンテキストを破壊するリスクが高いため、Ansible は状況が不明なまま処理を続行しません。
原因
Ansible はリモート OS と通信するために Python を使用します。file、copy、template、パッケージ管理など、多くのモジュールは処理を行う前に SELinux コンテキストをひそかに確認します。この確認には、コントロールノードではなくターゲットマシン上に libselinux-python(Python 2)または python3-libselinux(Python 3)が必要です。
このエラーが発生する典型的な状況は4つあります:
- RHEL/CentOS の最小インストール直後 — SELinux 自体が有効になっていても、SELinux バインディングはデフォルトで含まれていない
- CentOS 7 から CentOS 8/RHEL 8 への移行 — パッケージ名が変更されたが、既存のプレイブックがまだそれを認識していない
- 不要なものをすべて削除した Docker やクラウドイメージ
- Python 2 バージョンのバインディングしかインストールされていないホストで明示的に
ansible_python_interpreter=/usr/bin/python3を設定している場合
修正方法1:ターゲットホストに正しいパッケージをインストールする
SSH でログインし、OS バージョンに応じてインストールします。パッケージ名は RHEL 7 と 8 で異なります。
RHEL/CentOS 7 — Python 2
sudo yum install -y libselinux-python
RHEL/CentOS 8 / Rocky Linux 8 / AlmaLinux 8 — Python 3
sudo dnf install -y python3-libselinux
RHEL/CentOS 9 / Rocky Linux 9 / AlmaLinux 9
sudo dnf install -y python3-libselinux
Ansible がどの Python バージョンを使用しているか分からない場合は、コントロールノードから以下を実行してください:
ansible all -i inventory.ini -m setup -a 'filter=ansible_python_version'
修正方法2:プレイブックに pre_task を追加する(チーム向け推奨)
20台のホストに手動で SSH するのは大変です。pre_task を使って Ansible 自身にインストールを任せましょう:
---
- name: Deploy web application
hosts: all
become: yes
pre_tasks:
- name: Ensure SELinux Python bindings are installed (RHEL/CentOS 7)
yum:
name: libselinux-python
state: present
when:
- ansible_os_family == "RedHat"
- ansible_distribution_major_version | int == 7
- name: Ensure SELinux Python bindings are installed (RHEL/CentOS 8+)
dnf:
name: python3-libselinux
state: present
when:
- ansible_os_family == "RedHat"
- ansible_distribution_major_version | int >= 8
tasks:
- name: Your actual tasks here
...
一点注意があります。これは典型的な鶏と卵の問題です。Ansible はバインディングがないと動作しませんが、そのバインディングを Ansible 経由でインストールしようとしています。yum/dnf モジュールはインストール完了前に SELinux チェックをトリガーしないことが多いため、多くの場合は問題なく動作します。それでも pre_task で失敗する場合は、修正方法3に進んでください。
修正方法3:raw モジュールでパッケージをブートストラップする
raw モジュールは Python を完全にバイパスし、SSH コマンドをそのまま実行します。Python も SELinux チェックも鶏と卵の問題も発生しません。
---
- name: Bootstrap SELinux bindings before anything else
hosts: all
become: yes
gather_facts: no # critical: fact gathering triggers the SELinux check
tasks:
- name: Install libselinux Python bindings (raw, no Python required)
raw: |
if command -v dnf &>/dev/null; then
dnf install -y python3-libselinux
else
yum install -y libselinux-python
fi
- name: Main playbook
hosts: all
become: yes
tasks:
- name: Your actual tasks
...
ここで gather_facts: no は必須です。ファクト収集こそが SELinux バインディングのチェックをトリガーする処理です。ブートストラッププレイが完了するまで、ファクト収集はスキップしてください。
修正方法4:SELinux チェックを無効にする(本番環境では非推奨)
今すぐ動かす必要があり、後できちんと修正する予定がある場合は、Ansible に SELinux チェックを完全にスキップさせることができます。
インベントリファイルに記述する場合:
# inventory.ini
[webservers]
web01 ansible_selinux_enabled=false
プレイブック変数として設定する場合:
vars:
ansible_selinux_enabled: false
この設定を行うと、Ansible は SELinux ファイルコンテキストを一切管理しなくなります。permissive モードのシステムであれば短期的には問題ないでしょう。しかし enforcing モードのシステムでは、ファイルに誤ったラベルが付いたりサービスが不可解な形で失敗するなど、気づきにくい設定ミスを招く恐れがあります。この設定をそのままにしておかないでください。
Python 2 と Python 3 の不一致への対応
CentOS 8 以降では、両方のパッケージをインストールしてもエラーが出続けることがあります。これは Ansible が Python 3 で動作しているにもかかわらず、Python 2 のパッケージ(libselinux-python)しかインストールされていない場合に起こります。両者は相互に利用できません。
Ansible が実際に使用しているインタープリターを確認するには:
ansible web01 -i inventory.ini -m debug -a 'var=ansible_python_interpreter'
/usr/bin/python の場合は libselinux-python を、/usr/bin/python3 の場合は python3-libselinux をインストールします。シンプルなルールです。
推測を避けるために、インベントリでインタープリターを明示的に固定しましょう:
# RHEL 8+
[webservers]
web01 ansible_python_interpreter=/usr/bin/python3
# RHEL 7
web01 ansible_python_interpreter=/usr/bin/python
修正の確認
プレイブックを再実行する前に、以下の3つのチェックで正しく動作していることを確認してください。
- ターゲットホストにパッケージがインストールされていることを確認:
# RHEL 7
rpm -q libselinux-python
# RHEL 8+
rpm -q python3-libselinux
- Ansible が接続できてファクト収集が正常に行えることを確認:
ansible web01 -i inventory.ini -m ping
- Ansible が SELinux のファクトを認識していることを確認:
ansible web01 -i inventory.ini -m setup -a 'filter=ansible_selinux'
正常な出力は以下のようになります:
"ansible_selinux": {
"config_mode": "enforcing",
"mode": "enforcing",
"policyvers": 33,
"status": "enabled",
"type": "targeted"
}
3つすべてのチェックが通過したら、元のプレイブックを再実行してください。エラーは再発しません。
予防策:ベースロールに組み込む
50台以上の RHEL ホストを管理している場合、新しいマシンをプロビジョニングするたびにこの問題が発生します。根本的な解決策は、SELinux バインディングをベースサーバーロールに追加し、他のすべてのロールが実行される前に確実に存在するようにすることです。
# roles/base/tasks/main.yml
- name: Install SELinux Python bindings
package:
name: "{{ 'python3-libselinux' if ansible_distribution_major_version | int >= 8 else 'libselinux-python' }}"
state: present
when: ansible_os_family == "RedHat"
package モジュール(yum や dnf ではなく)を使うことで将来の互換性が保たれます。適切なパッケージマネージャーが自動的に選択されます。三項演算子は OS バージョンに基づいて Python 2/3 のパッケージ名の違いを吸収します。タスク1つで、RHEL 環境全体に対応できます。

