はじめに
前回の記事では、GitLabとOpenShift、Gatekeeperを組み合わせたDevSecOpsモデルケース環境の構築方法を紹介しました。
本記事では、それらを組み合わせて閉域環境におけるDevSecOpsモデルケースを構築し、デモアプリを使ってCI/CDの流れにセキュリティがどのように組み込まれるのかを確認します。
金融や公共分野など高いセキュリティ要件が求められるシステムでは、インターネット非接続の環境であっても、開発のスピードと安全性を両立することが求められます。
GitLabとGatekeeperを活用することで、この課題をどのように解決できるのかを具体的に示すのが今回の目的です。
DevSecOpsとは、DevOpsの迅速な開発・運用プロセスに「セキュリティ」を組み込み、スピードと安全性を両立させる手法です。従来は「開発が完了した後にセキュリティチェックを行う」ことが一般的でした。しかしこの方法では、最終段階で脆弱性が見つかった際に大きな修正が必要となり、時間やコストの増大、リリースの遅延を招いてしまいます。
DevSecOpsでは、開発ライフサイクルの各段階にセキュリティを統合することで、早い段階からリスクを検知し、小規模な修正で対処できます。その結果、開発のスピードを維持しながら、コスト削減と安全性の向上を同時に実現できます。
DevSecOpsは、下図のように開発(Dev)と運用(Ops)のライフサイクルにセキュリティ(Sec)を組み込む考え方です。

DevSecOpsの詳細については以下の記事で解説していますので、ご参照ください。
DevSecOpsとは?安全性とスピードを両立する開発手法 | SIOS Tech. Lab
本記事のデモは、以下の流れで進めます。

ゴール
本記事のゴールは次のとおりです。
- DevSecOpsのライフサイクルを実際のCI/CDパイプラインを通じて理解する
- GitLabとGatekeeperを組み合わせたセキュリティ統制の仕組みを把握する
前提条件
本記事のデモンストレーションは、以下の環境を前提としています。
- GitLab サーバー(ソースコード管理・CI/CD実行)
- OpenShift クラスター(閉域環境にて構築済み、アプリケーション実行環境)
- GitLab Runner(OpenShift 内にデプロイ済み)
- Gatekeeper(OpenShift 内にデプロイ済み、リソース作成許可ポリシーを定義済み)
- 踏み台サーバー(クラスターへのアクセス用)
これらは前回の記事「構築編」で構築したモデルケース環境に含まれています。
構築手順の詳細は「構築編」を参照してください。
デモアプリの仕様
- ベースイメージ:nginx
- サービス提供ポート:8080
- 到達性:内部ネットワークのみアクセス可能(外部公開なし)
- デプロイ方式:Helm(Chart・valuesでイメージタグを切り替え)
- デモで使用するタグ:v1.0(初回デプロイ)、v1.1(修正デプロイ)
パイプラインの実行フロー
今回利用するCI/CDパイプラインの実行フローは以下の通りです。
ビルド用パイプライン実行フロー
- 開発者がGitLabのビルド用プロジェクトにブランチをマージ
- マージをトリガーとして、GitLab CI/CDパイプラインが起動
- GitLab CI/CDからOpenShiftクラスター内のGitLab Runnerへビルドジョブ実行リクエストを送信
- RunnerがジョブPodを起動
- ジョブPodがビルド用プロジェクトからアプリのソースコードをClone
- ジョブPodがdocker CLIなどを利用してコンテナイメージをビルド
- ジョブPodがビルドしたイメージをGitLabのビルド用プロジェクト内のレジストリにpush
デプロイ用パイプライン実行フロー
- 開発者がGitLabのデプロイ用プロジェクトにブランチをマージ
- マージをトリガーとして、GitLab CI/CDパイプラインが起動
- GitLab CI/CDからOpenShiftクラスター内のGitLab Runnerへデプロイジョブ実行リクエストを送信
- RunnerがジョブPodを起動
- ジョブPodがOpenShiftのAPIサーバーに対し、リソースのデプロイリクエストを送信
- APIサーバーがGitLabのレジストリからアプリのイメージをpull
- APIサーバーがOpenShiftクラスター内にアプリをデプロイ
DevSecOpsのデモンストレーション
ここからは、構築済みのモデルケース環境を使って、実際にDevSecOpsの流れを確認します。
以下のように、アプリのデプロイを2回繰り返し、最後にGatekeeperによる制御を体験します。
- アプリの初回アップロード(v1.0 デプロイ)
- アプリを修正して再度デプロイ(v1.1 デプロイ)
- Gatekeeperによる不正リソース作成の拒否
アプリケーションの初回デプロイ
ここからは、DevSecOpsライフサイクルのCode → Operate(1週目)に対応します。

まず、アプリの初回デプロイを行います。
コードをビルド用プロジェクトのfeatureブランチにpushし、developブランチへマージします。

developブランチにマージすると、コンテナイメージをビルドするパイプラインが起動します。

この操作によって、開発用のイメージ(dev-v1.0)がビルドされます。

続いて、デプロイ用プロジェクトのfeatureブランチをdevelop ブランチへマージします。

これにより、先ほどビルドされたv1.0イメージを利用して、開発用Namespaceにアプリがデプロイされます。

$ oc get pod -n devsecops-develop -l app="nginx"
NAME READY STATUS RESTARTS AGE
nginx-deployment-5d75b659c5-cqbqp 1/1 Running 0 118s
動作確認が完了したら、ビルド用プロジェクトのdevelopブランチからmainブランチへのマージリクエストを作成し、承認後にマージします。
mainブランチにマージすると、パイプラインが起動し、prod-v1.0イメージがレジストリに格納されます。



次にデプロイ用プロジェクトでdevelopブランチをmainブランチにマージすると、パイプラインが起動し、本番環境用のネームスペースにアプリをデプロイします。

その結果、本番環境にアプリがデプロイされ、表示内容を確認できれば、Code → Operate の1週目(v1.0)が完了です。
$ oc get pod -n devsecops-production -l app="nginx"
NAME READY STATUS RESTARTS AGE
nginx-deployment-6c49676b47-m96pf 1/1 Running 0 29s
nginx-deployment-6c49676b47-pzv96 1/1 Running 0 29s

アプリケーションを修正して再度デプロイ
ここからは、DevSecOpsライフサイクルのCode → Operate(2週目)に対応します。

アプリを修正します。ここではindex.htmlの内容を更新し、あわせて.gitlab-ci.ymlで定義しているコンテナイメージのタグをv1.1に変更します。

次に、ビルド用プロジェクトのfeatureブランチにpushし、developブランチへマージします。
この操作によって、新しい開発用イメージ(v1.1)がビルドされます。



続いて、デプロイ用プロジェクトのfeatureブランチでConfig/develop-values.yamlと Config/product-values.yamlのtagを修正し、反映します。その後、同様にdevelopブランチ へマージして変更を適用します。



これにより、先ほどビルドされたv1.1イメージを利用して、開発用Namespaceにアプリがデプロイされます。
$ oc describe pod -n devsecops-develop nginx-deployment-54d94596b6-8j8w6 | grep "Image:"
Image: registry.gitlab.local.example.com/devsecops-user/devsecops-build-project/nginx:dev-v1.1
動作確認が完了したら、developブランチからmainブランチへのマージリクエストを作成し、承認後にマージします。
その結果、本番環境に修正版アプリがデプロイされ、表示内容が更新されていることを確認できます。
$ oc describe pod -n devsecops-production nginx-deployment-59ddc6dbb6-lhd6b | grep "Image:"
Image: registry.gitlab.local.example.com/devsecops-user/devsecops-build-project/nginx:prod-v1.1
この時点で、Code → Operate の2週目(v1.1)が完了です。
Gatekeeperによる不正リソースの検知・拒否
ここからは、DevSecOpsライフサイクルのSecに対応します。

最後に、あえて不正な設定を持つリソースをNamespaceにデプロイしてみます。
Gatekeeperのポリシー設定で、envラベルの値がデプロイ先のnamespace名と一致していないとPodの作成を許可しない制約を設定しています。
以下のように、デプロイ用プロジェクトでdeploymentのlabelsでenvラベルを不正な値に書き換える修正を行い、developブランチにマージします。


このときGatekeeperのポリシーが働き、不正なリソースの作成は拒否されます。
実際のログやイベントを確認すると、Constraint によって違反が検出され、Podのデプロイが失敗していることが分かります。
一番下のReplicaSetはDesired 1に対して、Currentが0であり、Podがデプロイされていないことがわかります。
$ oc get replicasets.apps -n devsecops-develop
NAME DESIRED CURRENT READY AGE
nginx-deployment-54d94596b6 1 1 1 20m
nginx-deployment-5d75b659c5 0 0 0 14h
nginx-deployment-6bfd599796 1 0 0 3m48s
PodがデプロイされていないReplicaSetのイベントを確認すると、Gatekeeperの制約テンプレートで設定したエラーメッセージが表示されていることがわかります。
$ oc describe replicasets.apps -n devsecops-develop nginx-deployment-6bfd599796
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedCreate 4m13s replicaset-controller Error creating: admission webhook "validation.gatekeeper.sh" denied the request: [require-env-label-match-namespace] Pod 'nginx-deployment-6bfd599796-z7hgf' の 'env' ラベルの値は、Namespace名 'devsecops-develop' と一致する必要があります。現在の値は 'dummy-env' です。
このように、開発者が意識しなくてもポリシー違反が自動的にブロックされることで、セキュリティを犠牲にせずに CI/CD のスピードを維持できることを体験できます。
まとめ
本記事では、GitLab、OpenShift、Gatekeeperを組み合わせて閉域環境にDevSecOpsモデルケースを構築し、デモアプリを用いて以下の流れを確認しました。
- GitLab CI/CDによるアプリケーションデプロイの自動化
- コード変更からの継続的デリバリー
- Gatekeeperポリシーによるセキュリティ担保
このデモを通じて、次のポイントを押さえることができました。
- DevOpsだけでは不十分
- 開発サイクルが迅速化しても、不正なリソースや設定が混入すれば安全性は損なわれる。
- ポリシー制御の仕組みが重要
- Gatekeeperを用いることで、開発の初期段階から不正なリソースを自動的に排除できる。
- GitLabとOpenShiftの統合は実用的
- インターネット非接続の閉域環境でも再現可能であり、金融・公共分野を含む実案件にも応用できる。
さらに実環境での利用を考えるなら、コンテナイメージのセキュリティスキャンや、より複雑なポリシー制御を組み合わせることで、より強固なDevSecOpsを実現できます。
まずは小規模なモデルケースから試し、徐々に自社の環境や要件に合わせて拡張していくことをおすすめします。