ワークフローのマニフェスト
ワークフローは YAML フォーマットで記述された マニフェスト(Manifest) により定義されます:
このマニフェストを記述し、Shisho Cloud 上のワークフローを作成することで、監査・検査やその後の対応をコードとして定義できます。
大構造
ワークフローのマニフェストは、以下の 5 つから構成されます:
version
: 定数 (0.1.0
)id
: ワークフローに対する一意な IDname
: ワークフローのわかりやすい表示名triggers
: ワークフローの実行タイミングの設定jobs
: ワークフロー内で行う監査・監査やその後の対応の設定
つまり、ワークフローのマニフェストは、以下のようなフォーマットに従う YAML ファイルだということです:
version: 0.1.0
id: "example-workflow"
name: "Example"
triggers:
# ...
jobs:
# ....
トリガー
ワークフローは複数の トリガー を triggers
ブロックとして定義します:
version: 0.1.0
id: "example-workflow"
# 複数のトリガーの定義
triggers:
# ...
1 つ 1 つのトリガーは、そのワークフローがいつ実行されるべきかを定義するものです。「一定時間おきにトリガーを実行する」といった時間に基づく定義や、「他サービスからの Webhook メッセージを受け取ったら実行する」というようなイベントに基づく定義が利用できます。
より具体的には、以下のようなトリガーを利用できます:
schedule
トリガーgithub
トリガー
schedule
トリガー
schedule
トリガーは、cron
フォーマットを用いてワークフローの実行タイミングを定義するためのトリガーです。
schedule
トリガーには cron
フォーマットでの実行タイミング設定を 1 つ以上含めることができ、少なくとも 1 つの cron 設定にマッチする時刻 にワークフローが実行されるようになります。
例えば、以下のようなトリガーを持つワークフローは 10 分おきに実行されます:
version: 0.1.0
id: "example-workflow"
triggers:
schedule:
# 10 分おきに実行
- cron: "*/10 * * * *"
# ...
以下は複数の cron
設定を含む schedule
トリガーの例です:
version: 0.1.0
id: "example-workflow"
triggers:
schedule:
- cron: "*/10 * * * *"
- cron: "*/30 * * * *"
# ...
複数の cron
設定が、同一時刻でのワークフローの実行を指示する場合であっても、ワークフローの実行は 少なくとも 1 回 しか保証されません。
github
トリガー
github
トリガーは、GitHub から Shisho Cloud に対して届けられた Webhook メッセージを用いてワークフローの実行タイミングを定義するためのトリガーです。
github
トリガーには、Webhook メッセージに基づくワークフローの実行条件を、1 つまたは複数個設定できます。
そのような設定のもと、GitHub から届いた Webhook メッセージが少なくとも 1 つの設定にマッチする場合 にワークフローが実行されるようになります。
例えば、以下のようなトリガーを持つワークフローは、各リポジトリのデフォルトブランチに対する push イベントが発生した際に実行されます:
version: 0.1.0
id: "example-workflow"
triggers:
github:
- push:
branches:
- :default_branch
以下は複数の条件を含む github
トリガーの例です:
version: 0.1.0
id: "example-workflow"
triggers:
github:
- push:
branches:
- :default_branch
- push:
branches:
- staging
GitHub が Webhook メッセージを通して配信するイベント情報のうち、Shisho Cloud は現在、以下のようなイベントを処理できます:
push
イベント (GitHub によるドキュメント)pull_request
イベント (GitHub によるドキュメント)
push
イベントの処理
push イベントに基づきワークフローの実行を制御する場合、以下のようなフィルタをご利用いただけます:
organization
: push 先のリポジトリが所属する GitHub Organization 名が、指定した文字列と一致するname
: push 先のリポジトリ名が、指定した文字列と一致するpush.branches
: 指定された パターン のうち、少なくとも 1 つに push 先のブランチ名が一致する- パターンと push 先ブランチ名を比較する際には、パターン中の以下の文字/文字列が特殊な挙動をとる
*
:/
を除く 1 文字以上の文字列にマッチ**
: 1 文字以上の文字列にマッチ- 3 つ以上
*
が続く場合:*
が奇数個: 最後が/
でない 1 文字以上の文字列*
が偶数個: 1 文字以上の文字列にマッチ
- パターンが以下のどれかに完全一致する場合、一致の検証は特別な挙動をとる
:default_branch
: パターンと push 先ブランチ名を比較する代わりに、当該リポジトリのデフォルトブランチ名と push 先ブランチ名を比較する
- パターンと push 先ブランチ名を比較する際には、パターン中の以下の文字/文字列が特殊な挙動をとる
push.branches_ignore
: 指定された パターン のどれとも push 先のブランチ名が一致しない- パターン中では
branches
同等の特殊文字・特殊な識別子が利用可能
- パターン中では
push.tags
: 指定された パターン のうち少なくとも 1 つに push されたタグ名が一致する- パターンと push されたタグ名を比較する際には、パターン中の以下の文字/文字列が特殊な挙動をとる
*
:/
を除く 1 文字以上の文字列にマッチ**
: 1 文字以上の文字列にマッチ- 3 つ以上
*
が続く場合:*
が奇数個: 最後が/
でない 1 文字以上の文字列*
が偶数個: 1 文字以上の文字列にマッチ
- パターンと push されたタグ名を比較する際には、パターン中の以下の文字/文字列が特殊な挙動をとる
push.tags_ignore
: 指定された パターン のどれとも push されたタグ名が一致しない- パターン中では
tags
同等の特殊文字が利用可能
- パターン中では
上述のフィルタが複数指定された場合は、それらは AND 条件として扱われます。つまり、全てのフィルタにマッチする場合のみ、ワークフローが実行されるようになります。
以下は上述のフィルタの利用例です:
triggers:
github:
# GitHub からの push イベントに基づきワークフローを実行する。ただし:
# - octcat/Hello-World に対する push イベントのみに反応する
# - staging ブランチ、または feat/ から始まるブランチにのみ反応する
- organization: "octcat"
name: "Hello-World"
push:
branches:
- staging
- feat/**
pull_request
イベントの処理
pull_request
イベントに基づきワークフローの実行を制御する場合、以下のようなフィルタをご利用いただけます:
organization
: push 先のリポジトリが所属する GitHub Organization 名が、指定した文字列と一致するname
: push 先のリポジトリ名が、指定した文字列と一致するpull_request.types
:pull_request
イベントの種別が、指定したリストの中に含まれるpull_request.branches
: 指定された パターン のうち、少なくとも 1 つにpull_request
のベースブランチ名が一致する- パターンと
pull_request
の ベースブランチ名を比較する際には、パターン中の以下の文字/文字列が特殊な挙動をとる*
:/
を除く 1 文字以上の文字列にマッチ**
: 1 文字以上の文字列にマッチ
- パターンが以下のどれかに完全一致する場合、一致の検証は特別な挙動をとる
:default_branch
: パターンとpull_request
のベースブランチ名を比較する代わりに、当該リポジトリのデフォルトブランチ名とpull_request
のベースブランチ名を比較する
- パターンと
PULL_REQUEST.branches_ignore
: 指定された パターン のどれともpull_request
のベースブランチ名が一致しない- パターン中では
branches
同等の特殊文字・特殊な識別子が利用可能
- パターン中では
上述のフィルタが複数指定された場合は、それらは AND 条件として扱われます。つまり、全てのフィルタにマッチする場合のみ、ワークフローが実行されるようになります。
以下は上述のフィルタの利用例です:
triggers:
github:
# GitHub からの pull_request イベントに基づきワークフローを実行する。ただし:
# - octcat/Hello-World に対する pull_request イベントのみに反応する
# - synchronize, opened, reopened イベントにのみ反応する
- organization: "octcat"
name: "Hello-World"
pull_request:
types:
- synchronize
- opened
- reopened
ジョブ
ワークフローは複数の ジョブ(Job) を定義します:
version: 0.1.0
id: "example-workflow"
# 複数のジョブの定義
jobs:
# ....
一つ一つのジョブは以下を含んでいます:
id
: ジョブのワークフロー内で一意な IDname
: ジョブのわかりやすい表示名decide
: どんなデータを・どのように検査/監査するかnotify
(optional): ジョブ実行の各フェーズにおいて、どのように通知するか
decide
ブロック
どんなデータを・どのように検査/監査するかを定義するために、ジョブは必ず一つの decide
ブロックを含みます:
version: 0.1.0
id: "example-workflow"
jobs:
- id: "..."
name: "..."
# decide ブロック
decide:
# ...
構造
「どんなデータを・どのように検査/監査するか」のうち、「どんなデータを」の部分を定義するものとして、decide
ブロックには必ず以下が含まれます:
input
ブロック
「どのように検査/監査するか」の部分を定義するものとしては、を必ず以下の少なくとも一方が含まれます:
rego
ブロックuses
ブロック
図示すると、検査/監査の対象(画像左部分)と、検査/監査の方法(画像中央部分)を規定するのがこれら 2 つの要素です:
input
ブロック
input
ブロックは、GraphQL クエリの形で、検査/監査対象のデータを定義するものです。
Shisho Cloud は、このようなクエリの定義に従って各ジョブごとにデータを取得し、その取得したデータをポリシーコードに入力します。
jobs:
- decide:
input:
# GraphQL クエリ
schema: |
query {
...
}
GraphQL クエリの仕様確認や記述のためには、簡易的な Playground が利用できます。
以下に「連携済みの GitHub Organization やリポジトリ群」を取得するための GraphQL クエリの例を示します:
query {
github {
organizations {
login
metadata {
id
}
repositories {
metadata {
id
}
name
}
}
}
}
rego
ブロック
rego
ブロックはポリシー言語 Rego を用いて、「どのように検査/監査するか」を規定するためのものです。
ポリシーは、ジョブ中の GraphQL クエリの定義通りに Shisho Cloud により取得されたデータを受け取ります。
そして、複数の Decision と呼ばれるデータを出力することで、Shisho Cloud に対して検査/監査の結果を伝達します。
Shisho Cloud は、この伝達されたデータを元に、セキュリティ状況の可視化や改善をサポートします。
より具体的には、Shisho Cloud と Rego で書かれたポリシーは、以下のような形で値のやり取りします:
- ポリシーへの入力: Rego 上の変数
input
経由で与えられる - ポリシーからの出力: Rego 上の変数
decisions
経由で行う
GraphQL クエリに従うデータのポリシーへの入力
Shisho Cloud は、ジョブ中の input
ブロックで記述された GraphQL クエリを用いてデータを取得し、それをポリシーに入力します。取得されたデータは、input
変数 からアクセスできるようになっています。
例えば GitHub 組織の一覧を取得し、それに対して検査/監査を実施するために、ジョブ中の input
ブロックで、以下のようなクエリを記述したとします:
query {
github {
organizations {
login
requiresTwoFactorAuthentication
}
}
}
このとき、Rego ポリシー中からアクセスできる input
という変数に、以下のようなオブジェクトが格納されます:
{
"github": {
"organizations": [
{
"login": "octcat",
"requiresTwoFactorAuthentication": true
},
{
"login": "your-org-name",
"requiresTwoFactorAuthentication": false
}
]
}
}
そのため、以下のような記述を Rego ポリシー中で行うことにより、GraphQL クエリを用いて取得した GitHub 組織の一覧が取得できます:
input.github.organizations
もし各 GitHub 組織に関する requiresTwoFactorAuthentication
の値(組織中のメンバーに二要素認証の使用を強制しているかを表す bool 値)を検証したい場合は、以下のように記述できます:
org := input.github.organizations[_]
allowed := org.requiresTwoFactorAuthentication == true
shishoctl
コマンドを利用すると、記述した GraphQL クエリを元にポリシーに対するテスト入力を作成できます。
同コマンドは、Rego ポリシーを Open Policy Agent (OPA) コマンドでテストする場合に利用できます。
以下に jobs[].decide.rego
ポリシーに対する入力として利用できる JSON 形式のテストデータを、GraphQL クエリから生成するコマンドの例を示します:
shishoctl testcase generate \
--type decision \
--org (Shisho Cloud 組織の ID) \
--query-path (GraphQL クエリが格納されたファイルへのパス)
ポリシー記述に Rego 以外を利用している場合や、ポリシーの利用箇所が jobs[].notify
以下である場合も、上記のコマンドのオプションを変更することで対応できます。
詳しくは shishoctl testcase generate --help
コマンドの出力を確認してください。
ポリシーからの結果の出力
Rego 中で decisions
変数に値をセットすることで、検査/監査の結果見つかった問題や、あるいは問題が見つからなかったという事実を記録しておくことができます。別の言い方をすると、Shisho Cloud は、Rego ポリシー中の decisions
変数の値を加工の上記録します。
decisions := [
...
]
decisions[n] {
n := ...
}
ここで、ポリシーから decisions
変数に値をセットする際、その値は Decision オブジェクト である必要があります。Decision オブジェクト とは、どのような対象に対する・どのような種類の・どのような結果か を表すデータオブジェクトで、通常 Rego SDK を用いて生成されるものです。以下に例を示します:
decisions[d] {
allowed := org.requiresTwoFactorAuthentication == true
d := shisho.decision.github.org_2fa_status({
# whether the target is allowed by this policy or not
"allowed": allowed,
# the target of the decision (e.g. a GitHub repository, etc.)
"subject": org.metadata.id,
# clues on this decision
"payload": shisho.decision.github.org_2fa_status_payload({"enabled": org.requiresTwoFactorAuthentication}),
})
}
decide
ブロックの例
以下のジョブ定義の例では、検査/監査対象のデータを定義する input
ブロックと、その検査/監査方法を定義する rego
ブロックを定義しています:
jobs:
- name: check-with-foobar
id: check-with-foobar
decide:
rego: |
import data.shisho
decisions[d] {
org := input.github.organizations[_]
allowed := org.requiresTwoFactorAuthentication == true
d := shisho.decision.github.org_2fa_status({
"allowed": allowed,
"subject": org.metadata.id,
"payload": shisho.decision.github.org_2fa_status_payload({"enabled": org.requiresTwoFactorAuthentication}),
})
}
input:
schema: |
query {
github {
organizations {
requiresTwoFactorAuthentication
metadata {
id
}
}
}
}
with
ブロック
with
ブロックは、ポリシーコードへ渡すパラメータなどを定義するためのものです。この機能を用いることで、より柔軟で効率的なポリシーコードを作成することができます。
定義されたパラメータは Rego / TypeScript 両方のポリシーコード内で使用することが可能です。
以下の with
ブロックの定義の例では、ポリシーコード内で使用するシンボル名、パラメータタイプ、説明および値を定義しています。
jobs:
- decide:
decide: ...
input: ...
with:
allowName:
type: string
description: skip specific instances
value: "test-sample"
以下に Rego / TypeScript のポリシーコード内でパラメータを使用する例を示します。
# example: Rego policy code
...
allow_specific_instance(instance) {
instance.machineType == "f1-micro"
} else = contains(instance.name, input.params.allowName)
...
// example: TypeScript policy code
...
if (instance.name.includes(params.allowName)) {
return {
...
}
}
...
with
ブロックが定義されている場合、Shisho Cloud のワークフローエディタではパラメータ設定 UI が表示されます。
パラメータタイプ
は以下の4種類があり、それぞれの種類によってパラメータ設定 UI の表示が変わります。
- string
- notification_target
- slack_channel
- resource_exception
ポリシーコードにパラメータを渡す場合は string
を選択してください。
他のタイプに関しては こちらのページ も参照してください。
notify
ブロック
ジョブ実行の各フェーズにおいて、どのように通知するかを定義するために、ジョブは notify
ブロックを含むことがあります:
version: 0.1.0
id: "example-workflow"
jobs:
- id: "..."
name: "..."
# notify ブロック
notify:
# 入力の定義
input: # ...
# 通知方法の定義
rego: # ....
uses: # ....
構造
notify
ブロックは 「通知すべきかの判定や、通知内容の構成のために必要なデータ」 を規定する、以下のブロックを必ず含みます:
input
ブロック
また、通知すべきかの判定方法や、通知内容の構成の方法 を規定する、以下のブロックを必ず含みます:
rego
ブロックuses
ブロック
図示すると、どのような通知をするかの判断に必要な補助データ(画像左部分)と、通知ロジック(画像中央部分)を規定するのがこれら 2 つの要素です:
input
ブロック
notify
ブロック中の input
ブロックは、decide
ブロック中の input
ブロックと同様に振る舞います。
詳細は decide
ブロック中の input
ブロックの説明をご確認ください。
shishoctl
コマンドを利用すると、記述した GraphQL クエリを元にポリシーに対するテスト入力を作成できます。
同コマンドは、Rego ポリシーを Open Policy Agent (OPA) コマンドでテストする場合に利用できます。
以下に jobs[].notify.rego
ポリシーに対する入力として利用できる JSON 形式のテストデータを、GraphQL クエリから生成するコマンドの例を示します:
shishoctl testcase generate \
--type notification \
--org (Shisho Cloud 組織の ID) \
--query-path (GraphQL クエリが格納されたファイルへのパス)
ポリシー記述に Rego 以外を利用している場合や、ポリシーの利用箇所が jobs[].decide
以下である場合も、上記のコマンドのオプションを変更することで対応できます。
詳しくは shishoctl testcase generate --help
コマンドの出力を確認してください。
rego
ブロック
rego
ブロックはポリシー言語 Rego を用いて、「通知すべきかの判定方法や通知内容の構成の方法」を規定するためのものです。
GraphQL クエリに従うデータのポリシーへの入力
Shisho Cloud は、ジョブ中の input
ブロックで記述された GraphQL クエリを用いてデータを取得し、それをポリシーに入力します。取得されたデータは、input.query
変数 からアクセスできるようになっています。
input
変数からは、ジョブの実行状態など、ワークフロー機能に関連する様々な値が利用できます。詳しくは Rego インラインポリシーが従う API についての資料 を参照してください。
ポリシーからの結果の出力
Rego ポリシー中で notifications
変数に値をセットすることで、Shisho Cloud に通知の送信を指示することが出来ます。
notifications := [
...
]
notifications[n] {
n := ...
}
ここで、ポリシーから notifications
変数に値をセットする際、その値は Notification オブジェクト の配列である必要があります。
ここで、Notification オブジェクト は、どのような対象に対する・どのような種類の・どのような結果か を表すデータオブジェクトで、通常 Rego SDK を用いて生成されるものです。以下に例を示します:
notifications[n] {
target := shisho.notification.to_notification_group("id-of-notification-group")
msg := "test"
n := shisho.notification.new(target, msg)
}
notifications[n] {
target := shisho.notification.to_github_issue(
#...
)
msg := "test"
n := shisho.notification.new(target, msg)
}
notify
ブロックの例
以下のジョブ定義の例では、検査/監査対象のデータを定義する input
ブロックと、その検査/監査方法を定義する rego
ブロックを定義しています:
jobs:
- name: check-with-foobar
id: check-with-foobar
decide:
# ...
notify:
input:
schema: |
query {
__typename
}
rego: |
package policy.github.config.notify
import data.shisho
notification_groups := []
notifications[n] {
input.running_state == shisho.job.running_state_in_queue
ng := notification_groups[_]
target := shisho.notification.to_notification_group(ng)
msg := concat("", ["Shisho Cloud has started to audit the GitHub configurations! See https://cloud.shisho.dev for further information."])
n := shisho.notification.new(target, msg)
}