はじめに
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.
とはいえ、ちょっと触ってみたい欲があったので触ってみました。あくまで、現時点でこうやったらこうなった、程度です。
ためしたのは、モジュールのオプションのポリシーチェックと、利用コレクションのポリシーチェックです。
- 検証環境
- ansible-policy 2024/06/20 の コミットハッシュ
f9a80faf2a55aed7d67f368303076b5a8f44b03b
時点- 本記事内の各種リンクもこのコミット時点です。どんどん古くなるのでご注意ください
- ansible-core 2.17.1
- Python 3.11.3
- macOS 13.6
- ansible-policy 2024/06/20 の コミットハッシュ
環境の準備
前述の通り、まだ 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 |
おわりに
まだまだ発展途上ですが、今後もチェックしていきたいと思います。
参考
- ansible-policy 本体: GitHub - ansible/ansible-policy: ansible-policy is a prototype implementation which allows us to define and set constraints to the Ansible project in OPA Rego language.
- Policybook サンプル集: GitHub - ansible/ansible-policybook-examples
- ウェビナー: Automating Policy as Code for consistency and compliance