てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible] Playbook の実装ポリシーをチェックできる ansible-policy(開発進行中)をためした

はじめに

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

環境の準備

前述の通り、まだ 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 配下にポリシー(複数可)を指定し、各ポリシーで targettaskplayrole)や、条件、そのときのアクションを指定します。

ansible_policy/policybook/README.md に説明があります。ただ、hosts が一番ピンときていません・・。

condition が一番肝ですかね。 input が唐突に感じましたが、おそらく targettask の場合はタスクを指すという感じだと思います。

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.builtinamazon.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

おわりに

まだまだ発展途上ですが、今後もチェックしていきたいと思います。

参考