はじめに
2024年5月の AnsibleFest 2024 のキーノートでたびたび「Policy as Code」という言葉が出てきました。Policy as Code 自体はほかでも耳にしますが、Ansible の場合は例えば、Playbook 中で指定するAWS インスタンスタイプを制限する、のようなことができるという話でした。コンセプトはふんわり分かったものの、当時はどういう実装の仕方になるのかまではわかりませんでした。
最近、ansible/ansible-policy という、Policy as Code を実現するための実装のリポジトリが公開されたため、実装面の情報がで始めてきました。
Playbook に対するポリシーを YAML で定義するファイルのことを「Policybook」と呼びます。Policybook のサンプルリポジトリも公開されました。
ansible-policy はリポジトリの説明にもあるとおり、現状はプロトタイプとしての実装で、破壊的な変更が起きる可能性も十分あります。バージョン管理もされておらず、PyPi にもありません。そのため、まだ導入する段階ではありません。
README.md
にも冒頭に以下の説明があります。
Note: This repository is in prototype phase and under active development with subject to breaking changes.
とはいえ、ちょっと触ってみたい欲があったので触ってみました。あくまで、現時点でこうやったらこうなった、程度です。
ためしたのは、モジュールのオプションのポリシーチェックと、利用コレクションのポリシーチェックです。
環境の準備
前述の通り、まだ PyPi に公開されていないため、リポジトリを clone してからインストールします。
% git clone https://github.com/ansible/ansible-policy.git
% cd ansible-policy
% pip install -e .
また、ansible-poilcy は内部的に OPA (Open Policy Agent) と使っているらしいので、インストールしておきます。
% brew install opa
おためし1: モジュールのオプションのポリシーチェック
このモジュールのこのオプションは、この値しか指定しちゃだめ、のようなポリシーの実現です。
Policybook の作成
Playbook に対するポリシーの定義を Policybook として作成します。
あとででてきますが、ポリシーチェックの実行時は、複数(もちろん1つでも)の Policybook を含んだディレクトリを指定します。そのため、適当(今回は policies
ディレクトリの中に Policybook を作成します。
あまり凝ったことを試しても後で変わるかもしれないので、シンプルなポリシーで雰囲気だけつかむようにします。
今回は、examples ディレクトリにあるcheck_pkg.ymlを参考にし、以下のポリシーとします。
ansible.builtin.packges
モジュールでインストールするパッケージは httpd
のみ許可
これを実現する Policybook は以下のとおりです。
pollicies/check_packages.yml
:
---
- name: Check for httpd package installation
hosts: localhost
vars:
allowed_packages:
- httpd
policies:
- name: Check for package name
target: task
condition: input["ansible.builtin.package"].name not in allowed_packages
actions:
- deny:
msg: The package {{ input["ansible.builtin.package"].name }} is not allowed, allowed packages are one of {{ allowed_packages }}
tags:
- compliance
policies
配下にポリシー(複数可)を指定し、各ポリシーで target
(task
、play
、role
)や、条件、そのときのアクションを指定します。
ansible_policy/policybook/README.md
に説明があります。ただ、hosts
が一番ピンときていません・・。
condition
が一番肝ですかね。 input
が唐突に感じましたが、おそらく target
が task
の場合はタスクを指すという感じだと思います。
Playbook の作成とポリシーチェック(違反あり)
チェック対象の Playbook は以下のとおりです。あえて許可されていないパッケージ git
を指定しています。
playbook.yml
:
---
- name: Test Play
hosts: sv
gather_facts: false
vars:
package_list:
- git
- httpd
tasks:
- name: Install Packages
ansible.builtin.package:
name: "{{ package_list }}"
state: present
それでは、ansible-policy
コマンドでポリシーチェックを実行します。-p
で Playbook、--policy-dir
で Policybook を格納したディレクトリを指定します。
% ansible-policy -p playbook.yml --policy-dir policies
TASK [Install Packages] playbook.yml L12-17 ************************************************************************
... Check_for_package_name Not Validated
The package ["git", "httpd"] is not allowed, allowed packages are one of ["httpd"]
------------------------------------------------------------------------------------------------------------------------------
SUMMARY
... Total files: 1, Validated: 0, Not Validated: 1
Violations are detected! in 1 task
違反(Not Validated)が検出されて、actions
配下で指定したメッセージが表示されました。
Playbook の変数も評価してるのは良いですね。ただし、現時点で試した限り ansible.builtin.import_tasks
や `ansible.builtin.include_tasks
で読み込んだ先の違反は検出できませんでした。
なお、ansible-policy
コマンドに --format json
をつけると、結果が JSON で表示されます。機械処理させたいときに便利ですね。
% ansible-policy -p playbook.yml --policy-dir policies --format json | jq .
{
"summary": {
"policies": {
"total": 1,
"violation_detected": 1,
"list": [
"Check_for_package_name"
]
},
"files": {
"total": 1,
"validated": 0,
"not_validated": 1,
"list": [
"playbook.yml"
]
}
},
"files": [
{
"path": "playbook.yml",
"violation": true,
"policies": [
{
"policy_name": "Check_for_package_name",
"target_type": "task",
"violation": true,
"targets": [
{
"name": "Install Packages",
"lines": {
"begin": 12,
"end": 17
},
"validated": false,
"action_type": "deny",
"message": "The package [\"git\", \"httpd\"] is not allowed, allowed packages are one of [\"httpd\"]\n"
}
]
}
],
"metadata": {}
}
]
}
Playbook の作成とポリシーチェック(違反なし)
つづいて、違反なしの Playbook で試します。
---
- name: Test Play
hosts: sv
gather_facts: false
vars:
package_list:
- httpd
tasks:
- name: Install Packages
ansible.builtin.package:
name: "{{ package_list }}"
state: present
ポリシーチェックを実行します。
% ansible-policy -p playbook.yml --policy-dir policies
------------------------------------------------------------------------------------------------------------------------------
SUMMARY
... Total files: 1, Validated: 1, Not Validated: 0
No violations are detected
違反がなくなりました。
おためし2: モジュールのオプションのポリシーチェック
今度は、使っていいコレクションのポリシーの実現です。
Policybook の作成
今回は、examples ディレクトリにあるcheck_collection.ymlをベースにします。
ansible.builtin
と amazon.aws
のみの利用を許可することにします。
policies/check_collection.yml
:
---
- name: Check for using collection
hosts: localhost
vars:
allowed_collections:
- ansible.builtin
- amazon.aws
policies:
- name: Check for collection name
target: task
condition: input._agk.task.module_info.collection not in allowed_collections
actions:
- deny:
msg: The collection {{ input._agk.task.module_info.collection }} is not allowed, allowed collection are one of {{ allowed_collections }}
tags:
- compliance
condition
の条件の書き方が先ほどとは異なりますね。
Playbook の作成とポリシーチェック(違反あり)
examples/check_project/playbook.yml をベースにして、唐突に azure.azcollection
コレクションの利用を混ぜます。
---
- name: Provision EC2 instance and set up MySQL
hosts: localhost
gather_facts: false
become: True
vars:
region: "your_aws_region"
instance_type: "t2.micro"
ami_id: "your_ami_id"
key_name: "your_key_name"
security_group: "your_security_group_id"
subnet_id: "your_subnet_id"
mysql_root_password: "your_mysql_root_password"
package_list:
- unauthorized-app
tasks:
- name: Create EC2 instance
amazon.aws.ec2_instance:
region: "{{ region }}"
key_name: "{{ key_name }}"
instance_type: "{{ instance_type }}"
image_id: "{{ ami_id }}"
security_group: "{{ security_group }}"
subnet_id: "{{ subnet_id }}"
assign_public_ip: true
wait: yes
count: 1
instance_tags:
Name: "MySQLInstance"
register: ec2
- name: Create a resource group
azure.azcollection.azure_rm_resourcegroup:
name: myrg
state: present
ポリシーチェックを実行します。
% ansible-policy -p playbook2.yml --policy-dir policies
TASK [Create a resource group] playbook2.yml L32-36 ****************************************************************
... Check_for_collection_name Not Validated
The collection azure.azcollection is not allowed, allowed collection are one of ["ansible.builtin", "amazon.aws"]
------------------------------------------------------------------------------------------------------------------------------
SUMMARY
... Total files: 1, Validated: 0, Not Validated: 1
Violations are detected! in 1 task
違反が検出されました。
他にできそうなこと
サンプルを見る限り、become
を禁止(タスクレベル、プレイレベル) などのポリシーチェックもできるようです。
サンプル
つまりポリシーチェックってどういうこと?
現時点での私の浅い理解ではありますが、「Playbook の実装ポリシーをチェックできる。ポリシーはYAMLで定義でき、Policybookと呼ぶ」ということなのかなと思います。
似たようなものだと ansible-lint
を連想しますが、私なりに比較すると以下の通りです。
私が誤解しているかもしれないですし、今後仕様が変わるかもしれない点はご了承ください。
|
ansible-policy |
ansible-lint |
チェックレベル |
実装 |
文法 |
ルールの定義 |
YAML (Policybook) |
Python |
おわりに
まだまだ発展途上ですが、今後もチェックしていきたいと思います。
参考