ConftestとCircleCIで継続的に構造データにPolicyを適用する

構造化されたデータにPolicyを適用したい、って気持ちはチームでコラボレーションしていたら自然と生まれてくる欲求だと思います。コードの場合は最近だと優秀なLinterがあるおかげである程度は強制することができるのですが、例えばYAMLだったりiniファイルだったりはどうでしょう?これらの構造化されたデータに対して、自分で作ったPolicyを適用できたら素敵だと思いませんか?そこで取り上げたいのがConftestです。以前このブログでも紹介したことがあるので、初めて聞いたという方はこちらの記事も合わせて読んでみてください。

kenfdev.hateblo.jp

今回はConftestをただ単体で使うのではなく、CircleCIと組み合わせて継続的にPolicyを適用する方法について見ていきます。これを実現したいがためにconftest-orbというCircleCIのOrbを作りました。

github.com

概要

CircleCIでconftest-orbを使った最小限のYAMLは以下のようになります。

version: 2.1
orbs:
  conftest: kenfdev/conftest-orb@x.y # バージョンは指定してください
workflows:
  build:
    jobs:
      - conftest/test:
          pre-steps:
            - checkout
          file: config_to_test.yaml

前提条件は以下の通りです:

これらを守った上でconfig_to_test.yamlに対してPolicyを適用します。

Serverless FrameworkのYAMLに適用してみる

では、もうちょっと具体例をあげてみます。Serverless FrameworkのYAMLを例にとって、Policyを実際に適用してみます。以下のリポジトリに実際にソースを置いています。

github.com

.circleci/config.ymlに設定しているCIの内容は以下のとおり:

version: 2.1
orbs:
  conftest: kenfdev/conftest-orb@0.0.8
workflows:
  build:
    jobs:
      - conftest/test:
          pre-steps:
            - checkout
          file: serverless.yaml

リポジトリの構成も、上で述べている前提条件を守っています。

kenfdev/conftest-serverless-circleci
├── policy
│   ├── base.rego
│   └── util.rego
└── serverless.yaml

そして、Policyを適用する対象となるserverless.yamlは以下のような内容になっています。

service: aws-python-scheduled-cron

frameworkVersion: '>=1.2.0 <2.0.0'

provider:
  name: aws
  runtime: python2.7
  tags:
    author: 'this field is required'

functions:
  cron:
    handler: handler.run
    runtime: python2.7
    events:
      - schedule: cron(0/2 * ? * MON-FRI *)

今回適用するPolicyの詳細(Regoファイル)についてはこの記事では見ません。興味がある方は実際に見てみてください。概要としては次のとおりです。

  • provider.tagsauthorを設定していること
  • provider.runtimeにPython2.7を設定していないこと
  • functionsruntimeにPython2.7を設定していないこと

上のYAMLと照らし合わせて、最初のPolicyは守られているけど、最後の2つに関しては守られていないことがわかります。この状態でCircleCIを走らせると以下のようにちゃんと叱られます。

f:id:kenev:20191218175806p:plain

Policyを一箇所で管理する

ここまでで、いい感じにCIでPolicyを適用できることがわかりました。ただ、結構大きな問題点があります。それは、適用したい構造データと同じリポジトリにPolicyも管理している点です。Policyは再利用するケースが多いと思うので、上で用意したようなPolicyをすべてのリポジトリで管理なんてしていられません。ということでここでConftestの強力な機能であるPolicyのpushとpull機能を紹介します。

v0.15.0時点でpush先、あるいはpull先として使えるのはOCIレジストリになります。Docker Registryが身近で使えるOCI互換のレジストリなので、これをどうにか使えないか試行錯誤してみました。

かなり斜め上な使い方をすることになってしまいますが、GitHub上にPolicyを保存して、更新するたびにこのPolicyを含んだDocker Registryのコンテナイメージを作るようにしてみました。流れのイメージとしては下図のとおりとなります。

f:id:kenev:20191218180955p:plain

この仕組みを使ってCircleCIのOrbに関するベストプラクティスが適用されているか、継続的にチェックしたいと思います。Policyは以下のリポジトリで管理しています。

github.com

Policyを含んだDocker Registryがあることで、CircleCI上でConftestを実行する際に一箇所で管理されているPolicyを再利用して適用することができます。

conftest-orbの開発フローで面白い点は、自分自身のCIにCircleCI Orbのベストプラクティスを継続的に適用していることです。ドッグフーディング状態だと言えます。

そんなconftest-orbのCIは以下のように定義しています。

jobs:
  general_usecase_test:
    executor: machine
    steps:
      - checkout
      - circleci-cli/install
      - run:
          name: Pack the orb.yml
          command: circleci config pack src > orb.yml
      - conftest/install
      # start the OCI registry(this command is declared in a different place)
      - start_oci_registry:
          image: kenfdev/circleci-orbs-policies
      # pull the policies from the OCI registry
      - conftest/pull:
          policy_path: policy
          repository: 127.0.0.1:5000/policies:latest
      # test with minimum options
      - conftest/test:
          policy_path: policy
          file: orb.yml

ちょっとグチャっとしている点もありますが、それはCI中にDocker Registryを起動する必要があるからです。外部にすでにOCI Registryがあれば、下のようにすっきり書くことができます。

version: 2.1
orbs:
  conftest: kenfdev/conftest-orb@0.0.8
workflows:
  build:
    jobs:
      - conftest/test:
          pre-steps:
            - checkout
          repository: <path-to-your-oci-registry>
          file: serverless.yaml

<path-to-your-oci-registry>からPolicyをpullしてConftestを実行することができます。

このように、Policyを一箇所で管理しつつ、複数のリポジトリに対してCI経由で継続的に適用するということが比較的観点に実現することができます。Conftestのexampleを見ると、下の一覧のようにかなり様々なユースケースに適用できるということがわかるので、興味がある方はぜひconftest-orbも使ってみてください!

  • awssam
  • compose
  • configfile
  • docker
  • hcl2
  • ini
  • kubernetes
  • serverless
  • tekton
  • terraform
  • ts
  • xml
  • etc.

まとめ

  • conftest-orbを使ってCircleCIで継続的に構造データにPolicyを適用できる
  • OCI Registryを使ってPolicyを一箇所で管理して共有することができる

以下のPRもマージされたため、v0.16.0以降ではOCI Registryではなく、HTTPでPolicyを外部から取得できるようになるので、さらに簡単にPolicyを適用できるようになりそうです!

github.com

関連

kenfdev.hateblo.jp