CircleCI の設定ポリシー管理という config.yml にルールを設定する機能を使ってみた

## はじめに
CircleCI でカスタマーサクセスをしている鈴木です。私の娘(4歳)の好きな人ランキング第5位に入賞しています。ありがとうございます。

本記事では CircleCI の「設定ポリシー管理（Config Policy Management）」という機能を紹介します。

CircleCI では config.yml という設定ファイルによってビルドからデリバリープロセスまでを集中管理しています。その config.yml に「ポリシー（config.yml の記述ルール）」を設けて、自社の管理下にあるプロジェクトの config.yml がそのポリシーから逸脱している場合にアラートを出したり、ビルドを中止する機能です。

現在は **Scale プランという年間契約を結んでいる場合に使える**ため、どなたでも使えるわけではありませんが、こんな機能もあるんだなーという感じで御覧ください。

## 設定ポリシー管理の利用目的

設定ポリシー管理の利用で何が実現できるのか、利用目的とその目的に応じて設定するポリシーの例を以下にまとめてみます。

- セキュリティ強化を目的として
  - 特定の Orb のみ使用を許可する
  - 必ず IP Ranges を使用する
  - メインブランチの時には必ず特定の job を使用する
  - 必ず特定のコンテクストを使用する

- CircleCIのコスト管理を目的として
  - 利用できるリソースクラスを制限する
  - Parallelism の並列数を制限する
  - その他、有料機能（IP Ranges、DLC）を制限する

- 開発の統制強化、開発の効率化を目的として
  - 必ず特定の Orb を使用する
  - 必ず特定の job を使用する

これらはあくまで一例です。設定ポリシー管理では柔軟な設定が可能なので、皆さんが会社やチームで決めているCircleCIのガイドラインなどがあれば、それを反映できるような記述がきっとできるのではと思います。


## 設定の流れと設定ファイルについて

### 設定の流れ

CircleCI の設定ポリシー管理は Open Policy Agent (OPA) というオープンソースの汎用ポリシー管理エンジンをベースにしています。設定の簡単な流れは以下になります。

1. CircleCI CLI のコマンドで 設定ポリシー管理の機能を有効化
2. ローカルでポリシーのファイルを作成
3. 作成したポリシーを CircleCI CLI のコマンドで CircleCI へ push

### 設定ファイルについて

ちなみに上記手順の2でローカルに作成する設定ファイルは OPA の Rego というポリシー言語で記述します。CircleCI の config.yml のポリシーを書くための説明は[こちらのマニュアル](https://circleci.com/docs/ja/config-policy-management-overview/#writing-rego-policies-using-circleci-domain-specific-language)を参照してください。
また、CircleCI の config.yml のポリシーとして多く使われるであろう記述を外部ライブラリ化したヘルパーもありますので[こちらのリファレンス](https://circleci.com/docs/ja/config-policy-reference/)をご参考ください。

### 複数の設定ファイルを利用可能

設定ファイルは複数併用できます。設定の目的別、プロジェクト別にポリシーを分けて管理しておくことが可能です。

## 試してみた

実際に設定を進めてみます。

### 1\. CircleCI CLI のコマンドで 設定ポリシー管理の機能を有効化
CircleCI の管理者権限で circleci コマンドを実行できるマシンから次のような `circleci policy` コマンドで設定ポリシー管理機能を有効にします。
```
circleci policy settings --enabled=true --owner-id {ORGANIZATION_ID}
```

`--owner-id` オプションには CircleCI の Organization Settings で確認できる Organization ID を渡します。下図をご参考ください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/11243/9ea70733-d054-acb9-9032-c3fc55db30ba.png)


### 2\. ローカルでポリシーのファイルを作成

次にポリシーの設定ファイルを作成します。
前述のように、設定ファイルは複数作成可能なので、config-policies のようなディレクトリを設けて管理するとよさそうです。このディレクトリ自体をVCSで管理することもおすすめです。

今回は　CircleCI の公式ドキュメントにある[サンプルコード](https://circleci.com/docs/ja/config-policy-management-overview/#example-policy)を転用します。
 ```rego:example.rego
package org

import future.keywords

policy_name["example"]

use_official_docker_image[image] = reason {
  some image in docker_images   # docker_images are parsed below
  not startswith(image, "circleci")
  not startswith(image, "cimg")
  reason := sprintf("%s is not an approved Docker image", [image])
}

# helper to parse docker images from the config
docker_images := {image | walk(input, [path, value])  # walk the entire config tree
                          path[_] == "docker"         # find any settings that match 'docker'
                          image := value[_].image}    # grab the images from that section

enable_hard["use_official_docker_image"]
```

簡単にサンプルコードの説明をします。
まず、「CircleCI のジョブを実行する docker executor で指定するイメージを `circleci` あるいは `cimg` で始まる CircleCI の公式イメージに限定する」というポリシーです。

一行目の
```rego:
package org
```
は必ずこの形で宣言します。

次の import 文、
```rego:
import future.keywords
```
では、OPAが提供している [future.keywords](https://www.openpolicyagent.org/docs/latest/policy-language/#future-keywords) をインポートしています。

その次に、`policy_name[]`でポリシー名を定義します。一行目の `package org` とこのポリシー名の定義は必須です。
```rego:
policy_name["example"]
```
その後、数行に渡り「CircleCI のジョブを実行する docker executor で指定するイメージを `circleci` あるいは `cimg` で始まる CircleCI の公式イメージに限定する」というポリシーを定義しています。

最後の一行、
```rego:
enable_hard["use_official_docker_image"]
```
で、ポリシー違反だった場合に hard fail することを定義しています。


### 3. 作成したポリシーを CircleCI CLI のコマンドで CircleCI へ push
ポリシーの設定ファイルを作成したら、この設定内容を `circleci policy push` コマンドで CircleCI に push します。

```
circleci policy push ./config-policies --owner-id {ORGANIZATION_ID}
```
push 後、CircleCI の WebUI の Organization Settings > Policies で確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/11243/47da3f90-04bd-67c4-974c-d9080f9d9503.png)


### 4. ポリシーに違反してみる
試しにポリシー違反をしてみます。
「CircleCI のジョブを実行する docker executor で指定するイメージを `circleci` あるいは `cimg` で始まる CircleCI の公式イメージに限定する」
というポリシーだったので、以下のような config.yml で node の公式 Docker イメージを使ってみます。

```yaml:config.yml
version: 2.1

jobs:
  build:
    docker:
      - image: node:latest
    steps:
      - run: echo "Hello, world!"
```
この変更を push すると以下のように「policy evaluation failed」というエラーメッセージがパイプライン一覧に表示され、実行されなかったことを確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/11243/9909ee8d-7c08-d74b-399b-f63beb84b943.png)

## おわりに
というわけで CircleCI の「設定ポリシー管理（Config Policy Management）」という機能を使ってみました。記事内でも触れましたが、CircleCI の config.yml の記述ルールやガイドラインをすでに定めている場合はそれを落とし込むことですぐに活用いただけると思います。

これまで、「自社で安全を確認した Orb のホワイトリストを作成して、リスト以外の Orb 利用を禁止したい」、「うっかり高価なリソースクラスを指定しないように制限したい」といったご要望を伺うことがありました。活用いただけると幸いです。