今関わっているチームでサービスのインフラの構成管理でAnsibleを利用しています。
Ansible playbookを複数人でメンテナンスしていることもあり、Playbookの書き方や制約などを設けたいと考え始めました。
何か仕組みがないか探しているところで、ansible-policy というものを見つけたのでこれを動かしてみたいと思います。
プロトタイプ段階のプロジェクトのようです。
動かしてみる
READMEに従って動かしてみます。
当方の環境は、macOS Sonomaで進めます。
1. opa
コマンドのインストール
OPA(Open Policy Agent)を利用するためopa
コマンドのインストールをインストールします。
私が確認した時点では最新のバージョンのopa
コマンドでは正常に動作しないため、過去のバージョンのものをインストールします。
curl -LO https://github.com/open-policy-agent/opa/releases/download/v0.69.0/opa_darwin_arm64_static
chmod +x opa_darwin_arm64_static
mv opa_darwin_arm64_static opa
sudo mv opa /usr/local/bin/
2. ansible-policyの取得
git clone git@github.com:ansible/ansible-policy.git
cd ansible-policy
3. ansbile-policy
コマンドのインストール
python3 -m venv venv-ansible-policy
source ./venv-ansible-policy/bin/activate
pip install -e .
4. ポリシーブックの準備
いくつかのポリシーブックの例がexamples/check_project/policies/
に存在しています。
承認されたパッケージのみがインストールされているかどうかを確認
# package-example.yml
---
- name: Check for mysql package installation
hosts: localhost
vars:
allowed_packages:
- "mysql-server"
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
許可されたコレクションのみが使用されているかどうかを確認
# package-example.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
become: true が使用されているかどうかを確認し、信頼できるユーザーのみが使用されているかどうかを確認
# package-example.yml
---
- name: Check for privilage escalation
hosts: localhost
vars:
allowed_users:
- "trusted_user"
policies:
- name: Check for using become in task
target: task
condition:
any:
- input.become == true and input.become_user not in allowed_users
- input.become == true and input lacks key become_user
actions:
- deny:
msg: privilage escalation is detected. allowed users are one of {{ allowed_users }}
tags:
- compliance
- name: Check for using become in play
target: play
condition:
any:
- input.become == true and input.become_user not in allowed_users
- input.become == true and input lacks key become_user
actions:
- deny:
msg: privilage escalation is detected. allowed users are one of {{ allowed_users }}
tags:
- compliance
5. ポリシー評価を実行する
今回ポリシーを評価するPlaybookがこちらです。
---
- 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: Install Unauthorized App
become: true
ansible.builtin.package:
name: "{{ package_list }}"
state: present
- name: Set MySQL root password [using unauthorized collection]
community.mysql.mysql_user:
name: root
password: "{{ mysql_root_password }}"
host: "{{ item }}"
login_unix_socket: yes
with_items: ["localhost", "127.0.0.1", "::1"]
実行します、そうすると3つのポリシー違反が表示されました。
$ ansible-policy -p examples/check_project/playbook.yml --policy-dir examples/check_project/policies
PLAY [Provision EC2 instance and set up MySQL] examples/check_project/playbook.yml L2-45 **********************************************************************************************************************************************
... Check_for_using_become_in_play Not Validated
privilage escalation is detected. allowed users are one of ["trusted_user"]
TASK [Install Unauthorized App] examples/check_project/playbook.yml L32-37 ************************************************************************************************************************************************************
... Check_for_using_become_in_task Not Validated
privilage escalation is detected. allowed users are one of ["trusted_user"]
TASK [Set MySQL root password [using unauthorized collection]] examples/check_project/playbook.yml L38-45 *****************************************************************************************************************************
... Check_for_collection_name Not Validated
The collection community.mysql is not allowed, allowed collection are one of ["ansible.builtin", "amazon.aws"]
... Check_for_package_name Not Validated
The package unauthorized-app is not allowed, allowed packages are one of ["mysql-server"]
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SUMMARY
... Total files: 1, Validated: 0, Not Validated: 1
Violations are detected! in 1 play, 2 tasks
ポリシー違反を元に修正をします。
PlayとTaskにbecome_userを指定、許可されたパッケージ名を指定して修正。
cp examples/check_project/playbook.yml examples/check_project/playbook.yml.org
$ diff examples/check_project/playbook.yml.org examples/check_project/playbook.yml
5a6
> become_user: "trusted_user"
15c16
< - unauthorized-app
---
> - mysql-server
33a35
> become_user: "trusted_user"
再度実行をします、1つのポリシー違反が表示されました。
community.mysqlコレクションが使えるようにポリシーを修正します。
$ ansible-policy -p examples/check_project/playbook.yml --policy-dir examples/check_project/policies
TASK [Set MySQL root password [using unauthorized collection]] examples/check_project/playbook.yml L40-47 *****************************************************************************************************************************
... Check_for_collection_name Not Validated
The collection community.mysql 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
cp examples/check_project/policies/check_collection.yml examples/check_project/policies/check_collection.yml.org
$ diff examples/check_project/policies/check_collection.yml examples/check_project/policies/check_collection.yml.org
9d8
< - community.mysql
再度実行します、ポリシー違反がなくなりました。
$ ansible-policy -p examples/check_project/playbook.yml --policy-dir examples/check_project/policies
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SUMMARY
... Total files: 1, Validated: 1, Not Validated: 0
No violations are detected
感想
yamlでポリシーを定義できるのはいいなと思いました、Rego言語を習得するハードルが少し高いと感じたので親しみのあるyamlで記述できるのは良いと感じました。
今後チームで、セキュリティやアクセス制限などクリティカルな部分を操作するAnsible taskなどでこの仕組みを用いて制約をかけるトライをやってみる予定です。
今回はスタンダードな使い方を紹介しましたが、他の機能もあるため一通りプロトタイプを触ってみたいと思います。