こんにちは、デベロッパーアドボケイトのよこなです。もう9月ですね。な〜つのお〜わ〜り〜
ソースコードだけじゃなくてアーティファクトを専用リポジトリで保存しましょう!
という話をよくするのですが、最近はもはやおまじないであっという間に「ソースコード→デプロイ」の過程を終えられる便利ツールが増えていますよね。「アーティファクトを取っておくと言っても、コマンド叩いたらサーバーまで行っちゃうんだけど…」と思われる方もいらっしゃるかもしれません。
この記事ではGCPとJFrog Platformを組み合わせた例を使って、アーティファクトの保管・セキュリティスキャンを行います。
☝やりたいこと
- Dockerで動くアプリケーションをビルドする
- アーティファクトをJFrog Artifactoryに保存する
- Artifactory上のアーティファクトにセキュリティスキャンをかけ、問題ない場合のみビルドを正常終了させる
この流れをCloud Build (CI/CDツール) 上で実現します。デプロイするところまで書こうかと思ったのですが、上記だけで超大作になったため割愛します(デプロイにJFrogは登場せず、純粋にCloud Buildの機能なので)。
記事が長いので身構えるかもしれませんが、動かしたいアプリもない、クラウド環境もない、JFrogサーバーもないというゼロからの状態で始めて丁寧に説明しているからなのでご安心を。実際は既存で使っているものをカスタマイズすることになると思うのでもうちょっと気楽にお試し頂けるかと思います。
例えば既にCloud Buildでコンテナイメージのデプロイをしている場合はちょっとステップを追加するだけで脆弱性のスキャンができるようになります!必要に応じ読み飛ばしながらご活用ください🐸
☝Frogに関する前提知識
- JFrog Artifactory: アーティファクト(今回はDockerイメージ)を保存するためのリポジトリ。
- JFrog Xray: いわゆるSCAツール。Artifactoryと連携し、アーティファクトが依存するOSSに脆弱性やライセンス違反がないかをスキャンする。
☝コンテナ化されたアプリケーションを用意する
今回デプロイするアプリケーションはこちらです。JavaとSpring bootというフレームワークで作りました。アプリの作り方は詳細割愛しますが、Springの公式ドキュメントを参考にしました。
github.com
実行・アクセスすると Hello Docker World!
と返せるだけのシンプル・サンプルアプリケーションです。
☝JFrog Artifactory上にDockerイメージ用のリポジトリを作成する
JFrog SaaS版の利用を開始する
ここから登録してJFrog Platformが使える状態にしてください。
Name Your Environment*
欄はお好きな値を指定していただけばOKです(既存の別のサーバーと重複はNG)。それがURLになり、後々Cloud Build側の設定にも使います。
リポジトリを作成する
アカウント作成して初回ログイン(これ以降、admin権限があるユーザーを使ってください)をしたら、そのままリポジトリも用意しちゃいましょう。
- リモートリポジトリ
- ローカルリポジトリ
- バーチャルリポジトリ
の3つを作成します。以前は別々に作成する必要があったのですが、最近まとめて3つ作れるようになったので結構楽ちんです。
今回は雑(サンプルとしては分かりやすいとも言う)ですが、spring
という名前を指定しました。spring-docker-local
, spring-docker-remote
, spring-docker
という3つのリポジトリが爆誕します。
リポジトリをXrayでスキャンできるよう設定する
スキャンには時間がかかるので、予めインデックスを追加しておくことでいざスキャンの命令が来たときに素早く結果を返せるようにしておきます。
リポジトリの設定画面からローカル・リモートリポジトリに対してEnable Indexing in Xray
を有効にする設定を入れてあげればOKです。ローカルとリモートを束ねる存在であるバーチャルリポジトリには設定不要です(そもそも設定項目がありません)。
☝Cloud Buildでビルドできるようにする
Google Cloud上にプロジェクトを作成(手順)済みの状態で始めましょう。今回の例では「My First Project」を作りました。
ビルドを定義する設定ファイルcloudbuild.yaml
を作成する
さて、ビルドの準備を進めていきますが、基本的には公式ドキュメントが詳しいし分かりやすいです。
cloud.google.com
サンプルアプリケーションのプロジェクトルートにcloudbuild.yaml
(Cloud Build上で自動で実行したいことを書き連ねていく設定ファイル)を追加します。完成形はGitHub上のファイルをご確認ください。
では、まずはDockerイメージをビルドしてタグをうつところまで。
steps: - name: 'gcr.io/cloud-builders/docker' entrypoint: 'bash' args: - '-c' - |- docker build --build-arg JAR_FILE==build/libs/*.jar -t gcr.io/$PROJECT_ID/spring-boot-docker . docker tag gcr.io/$PROJECT_ID/spring-boot-docker:latest ${_JFROG_PLATFORM_URL}/spring-docker/spring-boot-docker:latest
公式ドキュメントと異なるのはentrypoint
を指定してbash -c
でコマンドをまるごと渡せるようにしているところです。これで、ひとつのステップで2行コマンドを実行できます(参考: bash スクリプトの実行)。
コマンド内の$PROJECT_ID
はプロジェクトIDに置換される変数で、Cloud Buildが標準で提供しているものです。一方、${_JFROG_PLATFORM_URL}
は独自で設定する環境変数です。値の設定方法は後ほど見せますが、ここではArtifactoryのURLをCI実行時に埋め込むようにしています。
ここまで出来たらgcloud
コマンドが使えるようCloud SDKをインストール(手順)して、プロジェクトルートで次のコマンドを実行すればCloud Buildを動かすことができます。
gcloud builds submit
結果はそのままターミナルで確認できますが、Cloud Buildのコンソールにて「履歴」を見ることも可能です。
ビルドのトリガーを設定し、自動でビルドが実行されるようにする
先に設定ファイルを書ききってもいいですが、いったん何らかのイベントをきっかけにCloud Build上で自動ビルドが走るように設定しちゃいましょう。ローカルでコマンドを打たなくて良いので動作確認も楽になります。
今回はspring-boot-docker
リポジトリのmain
ブランチに変更が入ったらビルドするという設定にします。Cloud Buildのコンソールで「トリガー」ページを開きます。
先に最終形をお見せするとこんな感じです。
GitHubと初めて連携する場合、リポジトリを指定するところでGitHubとの行き来が発生します。認証を行い、使うリポジトリにGoogle Cloud Buildのツールを入れましょう。
また、変数をセットするのもポイントですね。今回は次の2つを設定しておいてください(参考: 変換値の置換)。
変数名 | 値 |
---|---|
_JFROG_PLATFORM_URL |
JFrog Platform登録時に指定したサーバー名を含むURL(xxx.jfrog.io ) |
_JFROG_PLATFORM_USER |
JFrog Platformのログインユーザー(メアド) |
☝Cloud BuildからArtifactoryへDockerイメージを保存する
Cloud Build上でさっきビルドしたイメージをArtifactoryへ保存するための設定をしていきます。
JFrog PlatformのAPI KEYをCloud Key Management Serviceで暗号化する
まずはJFrog Platformで認証するためのAPI KEYを発行し、クリップボードにコピーしてローカルで使えるようにします。
続いて、今取得したAPI KEYを暗号化します。そうすることで、cloudbuild.yml
に(暗号化した)値を直接書いて安全に使うことができるようになります。
公式ドキュメントを参考にしつつ、暗号化するための鍵を作る2ステップを実施します。
gcloud kms keyrings create [KEYRING-NAME] --location=global gcloud kms keys create [KEY-NAME] --location=global --keyring=[KEYRING-NAME] --purpose=encryption
[KEYRING-NAME]
(Keyring = 簡単に言うと鍵のグループ)と[KEY-NAME]
(Key = 鍵)は自分で決めます。サンプルではそれぞれspring-docker-keyring
, key-example
としました。これらの値はこの後cloudbuild.yaml
でも使います。
これで鍵が作成されたので、それを使って暗号化します。さっきクリップボードにコピーしたAPI_KEYをローカルの環境変数$RT_API_KEY
に設定した上で、次のコマンドを実行します。3つ目のコマンドを実行すると返ってくる値こそが暗号化したAPI KEYなので、控えておいてください(まぁ紛失してもまたコマンドを実行すればOKです)。
echo $RT_API_KEY | gcloud kms encrypt --plaintext-file=- --ciphertext-file=- --location=global --keyring=[KEYRING-NAME] --key=[KEY-NAME] | base64
暗号化した値を復号できるよう設定する
暗号化した鍵はビルド実行時に復号したいので、Cloud Buildに復号の許可を与えます。Cloud Buildのコンソールより「設定」画面で変更してください。
おそらくこのときに「APIを有効にしてね」という別画面が立ち上がると思うので、画面に従って設定しましょう。
設定ファイルで暗号化したAPI KEYを使えるよう追記する
ようやく準備が整ったので、cloudbuild.yaml
に追記します。まずはファイルの末尾に暗号化したAPI KEYを追記しましょう。[]
で覆われた値は上の手順にある鍵発行・暗号化時に得たものに差し替えてください。
secrets: - kmsKeyName: projects/dark-hall-324609/locations/global/keyRings/[KEYRING-NAME]/cryptoKeys/[KEY-NAME] secretEnv: APIKEY: '[暗号化されたAPI KEY]'
つまり私のサンプルバージョンだとこんな感じになります。
secrets: - kmsKeyName: projects/dark-hall-324609/locations/global/keyRings/spring-docker-keyring/cryptoKeys/key-example secretEnv: APIKEY: 'CiQAy/L6lVzsfbJqvTbOInV71GDrfUnHFPMbclD7a45xkGgsXlkScwAbTXpQ7gUCXwLhVYLC9BaGcGx7KmaSjuIaErsNQ7CacG/Jetll1xpDc/ITCAr+WrCZqYSktGWyvmE4zSBct9cMWPdVgPnoc1mmOst9iiD1S8G9QeCu8xY9XFYJq/T5K+Cu5lchGtAfWRNB8c1QA58/Qk4='
JFrog CLIを使ってDockerイメージをArtifactoryへ保存する
続いて、ArtifactoryへにDockerイメージを保存するためのステップを追加します。やっとこの記事の肝にたどり着きました…。
ググるとIntegrating Google Cloud Build with JFrog Artifactory | Google Cloud Blogっていう公式の記事もあるんですが、
- 最新のJFrog CLIのバージョンがv2になっていること
- かつてCLIのイメージを取得する先であったbintrayはサービス終了していること
をふまえると、次のような記述が良いでしょう。
- name: 'releases-docker.jfrog.io/jfrog/jfrog-cli-v2' entrypoint: 'bash' args: - '-c' - |- apk add docker-cli jfrog c add my-jfrog-server --url=https://${_JFROG_PLATFORM_URL}/ --user=${_JFROG_PLATFORM_USER} --password=$$APIKEY jfrog rt dp ${_JFROG_PLATFORM_URL}/spring-docker/spring-boot-docker:latest spring-docker --build-name=spring-boot-docker-build --build-number=${BUILD_ID} --server-id=my-jfrog-server jfrog rt bce spring-boot-docker-build ${BUILD_ID} jfrog rt bp spring-boot-docker-build ${BUILD_ID}
jfrog-cli-v2
イメージを使用することでCLIを有効にし、jfrog
コマンドが使えるようにします。
CLIの使い方はやりたいことに応じてご確認いただくのが1番ですが、今回使っているコマンドについては説明します。
www.jfrog.com
apk add docker-cli
(当然これはJFrog CLIじゃないですが、準備として必要なので補足します)
- Cloud Build上ではCLIもコンテナで動いているわけですが、そのコンテナからDockerイメージをプッシュするためにDockerクライアントが必要です。
- Alpine(CLIのDockerコンテナで動いている軽量のLinux)にapkというパッケージマネージャーでDockerクライアントをインストールしておきます。
jfrog c add
c
はconfig
のことです。この記事の上の方で作ったJFrogのサーバーを指定し、認証しておきます。- CLIにおいてJFrogのサーバーを一意に識別するため、
my-jfrog-server
という名前をここでつけています。
jfrog rt dp
rt
はArtifactoryを操作するコマンド、dp
はdocker-push
の略です。直前に設定したmy-jfrog-server
という名前でサーバーを指定し、そこに対してdocker push
しています。- この作業で
spring-boot-docker-build
という名の「ビルド」という塊を作っているのですが、この後セキュリティスキャンをする際もこのビルドに対して操作を行います。 spring-docker
は前半で作成したバーチャルリポジトリの名前で、プッシュ先として指定しています。バーチャルリポジトリに対してプッシュするとアーティファクトはローカルリポジトリに保存されます*1。- ビルドを識別するためにIDを振る必要があるので、今回はCloud Buildが標準で提供してくれている変数
$BUILD_ID
を使いました。シーケンシャルな番号を振るとかでも良いです。
jfrog rt bce
bce
はbuild-collect-env
の略で、次のステップ (jfrog rt bp
) を実行する前の準備としてbuild info (アーティファクトの詳細や依存関係、サーバーの環境変数などビルドの付加情報) を収集します。
jfrog rt bp
bp
はbuild-publish
を意味しており、これを実行するとビルド実行時のbuild infoがArtifactory上に一緒に保存されます。
上記の設定を反映してGitHubにプッシュすると、トリガーの設定がうまく行っていればCloud Buildが実行されるはずです。
ビルドをXrayでスキャンできるよう設定する
作成したビルドにもXrayのインデックスを追加しておきましょう。AdminメニューのXray SettingsでIndexed Resources
を開き、Build
タブで操作します。
Manage Index
をクリック後に開く画面でspring-boot-docker-build
を追加すれば完了です。
☝JFrog Xrayでスキャンする
無事ビルドを作成できたのでXrayを使ってスキャンします。無料版では脆弱性のスキャンのみが使えるので、やっていきましょう。
その前にXray用語をごく簡単に紹介します。
- ルール: チェックのレベルを定義します。「どの緊急度まで検知するか」「検知したら誰にどうやって通知するか」「検知した場合、そのアーティファクトの利用を許可するか」などを決めます。CVSSのスコアにも対応しています。
- ポリシー: ルールを束ねてスキャンの方針を示すようなものです。後述のウォッチと紐付けて使います。
- ウォッチ: ポリシーをどのビルド, リポジトリ*2に適用するかを定義します。
ということでルール, ポリシー, ウォッチを用意します。
ポリシー・ルールを追加する
ルールの作成画面はこのように細かい決まりを定められます。今回は例として
- 緊急度がHigh以上なら検知する
- 検知をしたらCIでのビルドを失敗させる(
Fail Build
にチェックを入れるとそうなります)
という設定を入れてみます。
ルールが追加できたらポリシーを保存しましょう。
ちなみに、なぜ1つのポリシーに複数のルールが設定できるのかについてはユースケースの例を挙げると分かりやすいかもしれません。
例えば「緊急度がHigh以上ならビルドを失敗させたいけど、Medium以下ならとりあえずメールで通知してほしい」場合は、今行った設定に加えて、Mediumならばメール通知するというルールを足してあげればOKです。
ウォッチを作成する
ポリシーを作った画面で隣のWatches
タブから操作できます。
ウォッチはポリシーとビルドを紐付けるものです。どのリポジトリのどのビルドに対しどのポリシーでXrayスキャンを行うかここで指定してください。
それぞれプラスボタンを押して、追加画面でポチポチ選択していきます。
JFrog CLIを使って保存したDockerイメージにセキュリティスキャンを行う
Xrayが準備万端になったので、cloudbuild.yaml
にスキャンするためのコマンドをbp
コマンドの後に追記しましょう。
jfrog rt bs spring-boot-docker-build ${BUILD_ID}
bs
はbinary-scan
のことで、これがIDで特定したビルドに対しスキャンを行うための記述です。
結果
ついに全ての設定が完了しました!!!GitHubへ変更を反映させるとまたCloud Buildが走ります。
Xrayをオンにしたらビルドがコケるようになりました(1番上の行)。サンプルで使ったアプリケーションはベースイメージで大量の脆弱性が見つかってしまったんです…。
Cloud Buildの実行結果を見たらJSON形式で詳細が確認できます。
JFrog Platformにアクセスすればよりリッチに詳細を見ることもできます。
☝終わりに
今回はGCP系のツールでやってみましたが、別のプラットフォーム、CI/CDツールでも同様のことが出来ます。オフィシャルにインテグレーションがサポートされている場合は公式ドキュメントが見つかるはずですが、英語しかなかったり最新バージョンを追従できていなかったりすることもあるので、JFrog関連で気になることや不明点があったらぜひご連絡ください〜!ブログのコメント欄でもOKです◎