はじめに
Red Hatは、Kubernetes環境で動作しているコンテナのセキュリティ対策状況を管理する Red Hat Advanced Cluster Security for Kubernetes(RHACS) という製品を展開しています。
今回は2回の記事に分けて、RHACSのインストールから、RHACSを使ったサンプルアプリのセキュリティ対策方法についてまとめていこうと思います。
この記事では、RHACSを使ったサンプルアプリのセキュリティ対策手順についてまとめていきます。
- 前編:OpenShiftへのRed Hat Advanced Cluster Security for Kubernetes(RHACS)のインストール手順
- 後編:OpenShiftとサンプルアプリで始めるRed Hat Advanced Cluster Security for Kubernetes(RHACS)入門(当記事)
RHACSの概要については、赤帽エンジニアブログの以下のページにて詳しく書かれていますので、一読いただくと作業のイメージがしやすいかと思います。
RHACS (Red Hat Advanced Cluster Security for Kubernetes) のご紹介
前提条件
今回は前編の記事にて作成したOpenShift&RHACS環境を使います。
その他、ローカル端末に以下のコマンドをインストールしておいてください。
- oc
- kubectl
- kustomize
目次
サンプルアプリのデプロイ
まずはセキュリティ対策を進めるためのサンプルアプリをデプロイします。
今回は以下のGitHubにアップしたものを利用します。
https://github.com/skitamura7446/sample-blog-for-rhacs-tutorial
※サンプルアプリはDjango Girlsのチュートリアルをベースに多少編集を施したものになります。
Web+APP+DBの一般的なWeb3層アプリになっています。
アプリケーションの操作方法についてはこちらを参照ください。
まずは上記リポジトリをローカルにCloneします。
git clone https://github.com/skitamura7446/sample-blog-for-rhacs-tutorial.git
つぎにOpenShiftにログインし、Project(Namespace)を作成します。
oc new-project sample-blog
Now using project "sample-blog" on server "https://api.aws-cluster01.xxx.com:6443".
You can add applications to this project with the 'new-app' command. For example, try:
oc new-app rails-postgresql-example
to build a new example application in Ruby. Or use kubectl to deploy a simple Kubernetes application:
kubectl create deployment hello-node --image=k8s.gcr.io/serve_hostname
では実際にサンプルアプリをデプロイしていきます。
今回はローカルにCloneしたソースコードとDockerfileを使ってOpenShift上でBuildを行います。
まずはNginx(Webコンテナ)のBuildConfigを作成します。
oc new-build --name=nginx --strategy=docker --binary
* A Docker build using binary input will be created
* The resulting image will be pushed to image stream tag "nginx:latest"
* A binary build was created, use 'oc start-build --from-dir' to trigger a new build
--> Creating resources with label build=nginx ...
imagestream.image.openshift.io "nginx" created
buildconfig.build.openshift.io "nginx" created
--> Success
BuildConfigとImageStreamが作成されたので、実際にBuildしていきます。
cd sample-blog/default/docker/nginx/
oc start-build nginx --from-dir=. --follow
これでコンテナのビルドが完了し、OpenShift上のコンテナレジストリにイメージが格納されました。
oc get istag
NAME IMAGE REFERENCE UPDATED
nginx:latest image-registry.openshift-image-registry.svc:5000/sample-blog/nginx@sha256:e952a1077decdc779f645962b5ffb3aa0e5819efd1d16d304e981ec68dad7bdf 15 seconds ago
同様にDjango(Appコンテナ)もデプロイしていきます。
oc new-build --name=django --strategy=docker --binary
cd ../django/
oc start-build django --from-dir=. --follow
これでサンプルアプリのコンテナイメージが作成できました。
oc get istag
NAME IMAGE REFERENCE UPDATED
django:latest image-registry.openshift-image-registry.svc:5000/sample-blog/django@sha256:2ad792fccaf721b5ac894bfcce0e12485f9f668b02bf24c58e30a531e1694ab8 6 seconds ago
nginx:latest image-registry.openshift-image-registry.svc:5000/sample-blog/nginx@sha256:e952a1077decdc779f645962b5ffb3aa0e5819efd1d16d304e981ec68dad7bdf 4 minutes ago
ではk8sマニフェストを使ってデプロイしていきます。
マニフェストのあるディレクトリに移動します。
cd ../../k8s/base/
ディレクトリ構成は以下の通りです。
tree
.
├── django-deployment.yaml
├── django-service.yaml
├── entrypoint.sh
├── kustomization.yaml
├── mysql-service.yaml
├── mysql-statefulset.yaml
├── nginx-deployment.yaml
├── nginx-route-template.yaml
├── nginx-service.yaml
├── redis-service.yaml
└── redis-statefulset.yaml
デプロイの前に、アプリをインターネット上に公開するためのRouteのk8sマニフェストを作成します。
entrypoint.sh
を実行して、nginx-route-template.yaml
からnginx-route.yaml
を作成します。
※実行前にOpenShiftにログインしておいてください。
./entrypoint.sh
entrypoint.shでは、OpenShiftのベースドメインを取得してrouteのspec.host
に反映させています。
#!/bin/bash
export BASE_DOMAIN=$(oc get ingresses.config/cluster -o jsonpath={.spec.domain})
envsubst < nginx-route-template.yaml > nginx-route.yaml
実行するとnginx-route.yamlが作成されます。
apiVersion: route.openshift.io/v1
kind: Route
metadata:
labels:
app: sample-blog
name: nginx
namespace: sample-blog
spec:
host: sample-blog.apps.aws-cluster01.xxxxx.com
port:
targetPort: 8080
to:
kind: Service
name: nginx
weight: 100
wildcardPolicy: None
マニフェストの準備もできたので、kustomizeを使ってデプロイします。
applyの際に-k
オプションを使うことでkustomization.yamlをベースにデプロイできます。
oc apply -k .
service/django created
service/mysql created
service/nginx created
service/redis created
deployment.apps/django created
deployment.apps/nginx created
statefulset.apps/mysql created
statefulset.apps/redis created
route.route.openshift.io/nginx created
しばらく待ってからrouteのアドレスを取得してブラウザでアクセスしてみます。
oc get route nginx -o jsonpath="{.status.ingress[0].host}"
これで無事にサンプルアプリをデプロイできました。
アプリケーションの操作方法についてはこちらに記載していますので、一通り触ってみてください。
RHACSを使ったサンプルアプリのセキュリティ対策
さて、実際にサンプルアプリがデプロイできたので、このアプリのセキュリティ状況を確認していきましょう。
以下のコマンドでRHACSのコンソールのURLを取得してにブラウザでアクセスしてみます。
oc get route central -n stackrox -o jsonpath="{.status.ingress[0].host}"
RHACSは脆弱性管理、DevSecOps、ネットワークセグメンテーション、リスク分析など、多角的な視点でKubernetes環境のセキュリティ対策状況を確認することができます。
ただ逆にできることが多いため、何から手をつけていいか分からないこともあるかと思います。
ここではあくまで入門編になりますが、RHACSを使ったセキュリティ対策を進め方の一例をご紹介します。
Violations
まずはViolationsを確認して、先ほどデプロイしたサンプルアプリのポリシー準拠状況を確認してみます。
画面左の"Violations"を選択して、フィルターでNamespace:
sample-blog
を入力してみましょう。Severity
で並び替えすると見やすくなります。
見ていただくとわかる通り、Violationはポリシーに基づきDeploymentやStatefulsetsなどのWorkloadリソースをチェックし、違反している場合はポリシーに従い重要度をつけて表示する機能になっています。
今回はRHACSをインストールした状態のままなので、ここに表示されているのはRHACSのデフォルトのポリシーに違反したリソースということになります。
デフォルトのポリシーについてもコンソール上で確認可能です。
左メニューの"Platform Configuration"→"Policies"を選択すると、デフォルトを含むポリシー一覧が確認できます。
Severityで重要度順に並べ替えてみましょう。
みてみると「特権コンテナの実行」や、非常に危険度の高い脆弱性(Spring4Shell/Log4Shell/Apache Struts)についてはCritical
に設定されています。High
ではマニフェストの環境変数にSECRET
やPASSWORD
を含むものが平文で書かれていることなどをチェックしているようです。
ちなみに執筆時(2022/07)ではデフォルトポリシーは以下の項目数となっています。
Severity | Policies |
---|---|
Critical | 4 |
High | 28 |
Medium | 24 |
Low | 25 |
もちろん、RHACSでは既存のポリシーを変更したりカスタムポリシーを作成することも可能ですが。いきなり自分達でポリシーを定義することも難しいかと思いますので、まずはデフォルトのポリシーをベースに対策を進めるとイメージがつきやすいかと思います。
では少し戻って、Namespace:
sample-blog
で違反しているポリシーを実際に確認していきます。
以下はポリシーの中でもSeverity:High
のものを抽出したものです。
みていただくと4つのサービス全てでFixable Severity at least Important
というポリシーに違反しています。
一番上のmysqlのリンクをクリックして、具体的にどんなポリシーに違反しているのかを確認してみます。
先にPolicy
タブを選択すると、このポリシーの概要を確認できます。
深刻度ランクがImportant
以上の修正可能な脆弱性が存在する場合に違反となるポリシーであることが分かります。
Violation
タブに移動すると、具体的にどのCVEの脆弱性があるかを確認できます。この違反をクリアするには、ここに表示されている脆弱性への対策を行う必要があることが分かります。
Vulnerability Management
なお、RHACSではVulnerability Managementという、コンテナイメージの脆弱性に対してより詳しく確認できる機能があります。
左側メニューの"Vulnerability Management"→"Dashboard"を選択します。
このダッシュボードでクラスター上にデプロイされているリソース(が使っているコンテナイメージ)の脆弱性対策状況が確認できます。
が、情報量がかなり多いのでもう少し絞ってみます。
右上の"APPLICATION & INFRASTRUCTURE"から"Deployments"を選択します。その後Namespace:
sample-blog
でフィルターをかけると、サンプルアプリのDeployment(&Statefulsets)の脆弱性情報が確認できます。
では先ほど確認したmysqlの情報をみてみます。該当のDeploymentをクリックすると、以下のようなウィンドウが表示されます。
中央円グラフの"POLICY VIOLATIONS BY SEVERITY"では、現在違反しているポリシー数が確認でき、右側円グラフではこのコンテナイメージが持つ脆弱性を確認できます。
Deployment Findingsでは違反しているポリシーや、FIXABLE(修正可能)な脆弱性情報を確認できます。試しにFIXABLEタブを選択すると、以下のようにViolationsで確認した脆弱性情報が記載されています。
このように、コンテナの脆弱性状況は基本にVulnerability Mangementで確認し、ポリシーによってアラートを発火させる、というのが一般的な使い方になります。
行ったり来たりになりますが、一個前の画面の"POLICY VIOLATIONS BY SEVERITY"の右上にある"VIEW ALL"を選択すると、このRHACSで設定されている脆弱性関連のポリシー一覧と、このDeploymentにおける各ポリシーへのステータスが確認できます。
みてもらうと、デフォルトでは脆弱性関連のポリシー(のうち有効化されているもの)は5つあり、その中の1つのポリシーに違反していることがわかります。
コンテナイメージの脆弱性への対策
コンテナイメージに対して修正可能な脆弱性がひそんでいることがわかったので、実際に対策していきましょう。
まずはこのコンテナイメージはどのベースイメージからつくられているのかを確認します。
先ほどgit clone したリポジトリからDockerfileやk8sマニフェストを見ていきます。
Nginx
Nginxは以下のファイルでベースイメージを確認できます。
cat default/docker/nginx/Dockerfile
FROM docker.io/nginx:1.20
COPY nginx.conf /etc/nginx/nginx.conf
COPY default.conf /etc/nginx/conf.d/default.conf
USER 1001
EXPOSE 8080
CMD nginx -g "daemon off;"
どうやらdokcer.ioから取得しているようです。
Django
DjangoもNginx同様、DockerビルドしているのでDockerfileを確認します。
cat default/docker/django/Dockerfile
FROM docker.io/python:3.9
ADD ./app .
RUN pip install -r requirements.txt
ENTRYPOINT /bin/bash docker-entrypoint.sh
同じくdocker.ioです。
Mysql / Redis
MysqlとRedisはビルドせず、外部のレジストリから取得したイメージをそのままデプロイしているため、statefulsets.yaml
を確認します。
cat default/k8s/base/mysql-statefulset.yaml
cat default/k8s/base/redis-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
labels:
app: sample-blog
app.kubernetes.io/part-of: sample-blog
spec:
...omit...
spec:
containers:
- name: mysql
image: docker.io/mysql:8.0.26
...omit...
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
labels:
app: sample-blog
app.kubernetes.io/part-of: sample-blog
spec:
...omit...
spec:
containers:
- name: redis
image: docker.io/redis:6.0.8
...omit...
もうわかっていたかと思いますが、これらもdocker.ioからのイメージになります。
これらは全てdocker.ioにある公式のイメージになりますが、このように採用するバージョンによっては修正可能な脆弱性が残っている場合もあるため、以下のような対策が必要になります。
- 脆弱性対策が施されたイメージを採用する
- 定期的に最新のイメージに更新する
- Dockerfileにて該当のPackageを更新するステップを含める
今回は取り急ぎ、ひとつ目の"脆弱性対策が施されたイメージを採用する"にて対策してみます。
Red Hatが提供するイメージ
Red Hatでは独自に脆弱性対策を施したベースイメージを提供しております。
これらはEcosystem Catalogで確認でき、イメージの検索やイメージごとのセキュリティ対策状況を確認できます。
またこのイメージをOpenShiftなどのRed Hat基盤上で利用した場合、イメージに含まれるパッケージに関するサポートを受けることができます。
そのためOpenShiftを利用する際は、まずはこのイメージの利用を検討するといいかと思います。
では実際にRed Hatが提供するイメージを使ってみましょう。
それぞれのイメージをRed Hatのイメージに書き換えて再デプロイしてみます。
Nginx
Dockerfileを書き換えます。
vi default/docker/nginx/Dockerfile
FROM registry.access.redhat.com/ubi8/nginx-120
COPY nginx.conf /etc/nginx/nginx.conf
COPY default.conf /etc/nginx/conf.d/default.conf
USER 1001
EXPOSE 8080
CMD nginx -g "daemon off;"
再ビルドします。今回はタグをv0.1としています。
cd defalut/docker/nginx
oc new-build --name=nginx-2 --strategy=docker --binary --to=nginx:v0.1
oc start-build nginx-2 --from-dir=. --follow
ImageStreamを見るとv0.1タグのイメージがビルドできました。
oc get is
NAME IMAGE REPOSITORY TAGS UPDATED
django image-registry.openshift-image-registry.svc:5000/sample-blog/django latest 26 hours ago
nginx image-registry.openshift-image-registry.svc:5000/sample-blog/nginx v0.1,latest 22 seconds ago
Django
Nginxと同様、Dockerfileを書き換えます。
cd ../django/
vi Dockerfile
FROM registry.access.redhat.com/ubi8/python-39
ADD ./app .
RUN pip install -r requirements.txt
ENTRYPOINT /bin/bash docker-entrypoint.sh
再ビルドします。
oc new-build --name=django-2 --strategy=docker --binary --to=django:v0.1
oc start-build django-2 --from-dir=. --follow
ImageStreamを確認します。
oc get is
NAME IMAGE REPOSITORY TAGS UPDATED
django image-registry.openshift-image-registry.svc:5000/sample-blog/django v0.1,latest 5 seconds ago
nginx image-registry.openshift-image-registry.svc:5000/sample-blog/nginx v0.1,latest 43 minutes ago
MySQL / Redis
MySQLはmysql-statefulset.yaml
を修正します。
cd ../../k8s/base/
vi mysql-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
labels:
app: sample-blog
app.kubernetes.io/part-of: sample-blog
spec:
...omit...
spec:
containers:
- name: mysql
image: registry.redhat.io/rhel8/mysql-80
...omit...
同じくRedisもredis-statefulset.yaml
を修正します。
vi redis-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
labels:
app: sample-blog
app.kubernetes.io/part-of: sample-blog
spec:
...omit...
spec:
containers:
- name: redis
image: registry.redhat.io/rhel8/redis-6
...omit...
イメージの書き換えが完了したので、実際にデプロイしてみます。
kustomization.yamlのimages.newTag
を書き換えることでマニフェスト側の修正なしでイメージのタグを変更できます。
vi kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: sample-blog
images:
- name: image-registry.openshift-image-registry.svc:5000/sample-blog/django
newName: image-registry.openshift-image-registry.svc:5000/sample-blog/django
newTag: v0.1
- name: image-registry.openshift-image-registry.svc:5000/sample-blog/nginx
newName: image-registry.openshift-image-registry.svc:5000/sample-blog/nginx
newTag: v0.1
...omit...
oc apply -k .
service/django unchanged
service/mysql unchanged
service/nginx unchanged
service/redis unchanged
deployment.apps/django configured
deployment.apps/nginx configured
statefulset.apps/mysql configured
statefulset.apps/redis configured
route.route.openshift.io/nginx unchanged
しばらくして、Podが正常に起動していることを確認します。
oc get pods
NAME READY STATUS RESTARTS AGE
django-5cd45cd58c-wk4gm 1/1 Running 0 101s
mysql-0 1/1 Running 0 96s
nginx-d64cf9495-d5dn5 1/1 Running 0 101s
redis-0 1/1 Running 0 96s
この状態でViolationsを確認してみましょう。
元々あったFixable Severity at least Important
が消えていますね。
Vulnerability Managementも確認してみます。
Policy Status
がPassになりました!
このようにRed Hatのイメージを使うだけで脆弱性対策が施されたイメージで安全にアプリケーションをデプロイできます。
ただし以下のように注意事項もあるため、チームのセキュリティポリシーに合わせて適切なベースイメージを選択してください。
- Red Hatが提供していないイメージもある
- 使いたいバージョンのイメージがまだ提供されていないケースもある
- たとえば、Redisの最新メジャーバージョンは7系ですが、Red Hatの提供イメージの最新は6系です。
- 全ての脆弱性が対策されている訳ではないため、必要に応じてビルド時に別途対策が必要
Configuration Management
RHACSでは、コンテナイメージの脆弱性だけでなくKubernetesレイヤのマニフェスト構成をチェックし、セキュリティ的にリスクのある設定があるかどうかのチェックを行うことができます。
先ほどのViolationsをもう一度みてみると、High
とMedium
の違反ポリシーはKubernetesの設定周りのものであることが分かります。
Environment Variable Contains Secret
Pod Service Account Token Automatically Mounted
-
No resource requests or limits specified
それぞれの違反ポリシーについて少し深掘りしていきます。
Environment Variable Contains Secret
これはDeploymentの環境変数内にSECRET
やPASSWORD
のような文字を含むkeyが存在すると違反となるポリシーです。
django
とmysql
でこの違反が出ていますが、これはMySQLのパスワード情報を環境変数でそのまま渡していることが原因です。
django-deployment.yaml
をみるとMYSQL_PASSWORD
やBLOG_ADMIN_PASSWORD
といった環境変数が設定されています。
apiVersion: apps/v1
kind: Deployment
metadata:
name: django
annotations:
app.openshift.io/connects-to: '["mysql", "redis"]'
labels:
app: sample-blog
app.kubernetes.io/part-of: sample-blog
spec:
replicas: 1
selector:
matchLabels:
app: django
template:
metadata:
labels:
app: django
spec:
initContainers:
- name: init-connection
...omit...
env:
- name: MYSQL_HOST
value: mysql
- name: MYSQL_USER
value: sampleblog
- name: MYSQL_PASSWORD
value: sampleblog
containers:
- name: django
...omit...
env:
- name: MYSQL_USER
value: sampleblog
- name: MYSQL_PASSWORD
value: sampleblog
- name: MYSQL_DATABASE
value: sampleblog
- name: MYSQL_HOST
value: mysql
- name: MYSQL_PORT
value: "3306"
- name: REDIS_HOST
value: redis
- name: BLOG_ADMIN_USER
value: admin
- name: BLOG_ADMIN_EMAIL
value: admin@sample.com
- name: BLOG_ADMIN_PASSWORD
value: admin
imagePullPolicy: Always
そのため、機密情報となるものはKubernetesのSecret
で渡す方式にしましょう。
Pod Service Account Token Automatically Mounted
このポリシーは、Podに対して自動的にKubernetesクラスターのdefault
サービスアカウントを付与している場合に違反となります。
Kubernetesのセキュリティのベストプラクティスとして、PodからKubernetes APIを操作する必要がない際はdefault
サービスアカウントは極力使わず、別のサービスアカウントを使うかautomountServiceAccountToken: false
設定でトークンをPodにマウントしない対策が推奨されています。
こういった根拠については、RHACSの"Policy overview"のRationaleを確認できます。
ということで、今回は別のサービスアカウントを作成し、Deloyment
にてサービスアカウントを指定する形を取ります。
No resource requests or limits specified
これはイメージしやすいものかと思います。それぞれのdeployment.yaml
やstatefulset.yaml
をみてもらうと分かりますが、resources
の設定をしていないのでこの違反が出ています。適切なQuota設定を実施することが必要です。
ポリシーに準拠したk8sマニフェストの適用
上記3つのポリシーに対応したマニフェストは、cloneしたリポジトリのimproved
に格納しています。
先述のベースイメージも書き換えも含めて反映されています。
※network-policy.yaml
は後ほど説明します。
cd ../../../improved/k8s/base
tree
.
├── django-configmap.yaml
├── django-deployment.yaml
├── django-secret.yaml
├── django-service.yaml
├── entrypoint.sh
├── kustomization.yaml
├── mysql-configmap.yaml
├── mysql-secret.yaml
├── mysql-service.yaml
├── mysql-statefulset.yaml
├── network-policy.yaml
├── nginx-deployment.yaml
├── nginx-route-template.yaml
├── nginx-service.yaml
├── redis-configmap.yaml
├── redis-service.yaml
├── redis-statefulset.yaml
└── sample-blog-sa.yaml
デプロイ前に、今デプロイされているアプリを削除します。
oc delete -k ../../../default/k8s/base/
次にnginx-route.yaml
を作成します。
./entrypoint.sh
ではデプロイします。
oc apply -k .
serviceaccount/sample-blog created
configmap/django-mysql-conf created
configmap/mysql-conf created
configmap/redis-conf created
secret/django-mysql-pass created
secret/mysql-pass created
service/django created
service/mysql created
service/nginx created
service/redis created
deployment.apps/django created
deployment.apps/nginx created
statefulset.apps/mysql created
statefulset.apps/redis created
route.route.openshift.io/nginx created
しばらくして、Podが正常に起動していることを確認します。
oc get pods
NAME READY STATUS RESTARTS AGE
django-74745f9ff6-jj84v 1/1 Running 0 71s
mysql-0 1/1 Running 0 71s
nginx-5b7cff7576-7b9vk 1/1 Running 0 71s
redis-0 1/1 Running 0 71s
High
とMedium
が全て消えました!
今回はLow
は残したままの状態になっていますので、もし手元に試せる環境があればぜひ対策を施してみてください。
ちなみにこれまで対応したKuberetesの設定周りの状況については、RHACSのConfiguration Managementで確認できます。
画面左メニューのConfiguration Managementを選択します。
ここで全クラスターの設定状況を確認できますが、かなり俯瞰的なものなので、Vulnerability Managementと同様、対象を絞り込みます。
右上の"APPLICATION & INFRASTRUCTURE"から"Deployments"を選択します。その後Namespace:
sample-blog
でフィルターをかけると、サンプルアプリのDeployment(&Statefulsets)の設定情報が確認できます。
詳細を確認する場合は個別のDeploymentを選択します。
ポリシー違反状態ですが、先ほど残したLowのポリシーに引っ掛かっていることが分かります。
このように、Vulnerability Management同様、アラートはポリシーのViolationで拾い、状況をConfiguration Managementで確認する、という使い方になるかと思います。
Network Policy Management
一通りデフォルトのポリシー違反のうち優先度の高いものは対応できましたが、もうひとつ、RHACSの機能であるNetwork Policy関連の機能を試してみます。
RHACSではアプリケーションの通信フローをチェックし、適切なNetwork Policyを自動生成する機能を具備しています。
左メニューからNetwork Graphを選択し、Namespaces
プルダウンでSample-blog
をチェックすると、以下のように4つのサービスを確認できる画面が表示されます。
この画面にて右上のNetwork Policy Simulatorを選択すると、以下のような画面がでるので、Generate and simulate network policiesを選択します。
するとクラスター内の通信状況をベースに自動でNewtwork Policyが作成されます。
作成されたNetwork Policyは以下の通りです。5つ出力されていますが、Smaple-blog
Namespaceに関するものは4つになります。
中身を読むと、以下のようなNW制御を実現するNetwork Policyが作成されるようです。
- stackrox-generated-mysql
- djangoからmysqlへの3306ポート通信
- stackrox-generated-redis
- djangoからredisへの6379ポート通信
- stackrox-generated-nginx
- ingress pod(Route)からnginxへの8080ポート通信
- stackrox-generated-django
- nginxからdjangoへの8000ポート通信
ターゲットやポート番号についてはServiceから取得できそうですが、どのPodからの通信があるか、といった点は実際の通信フローからでないと取得できないので、この点はRHACSのNetwork Policy作成機能の大きな特徴かと思います。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
creationTimestamp: "2022-07-13T02:30:51Z"
labels:
network-policy-generator.stackrox.io/generated: "true"
name: stackrox-generated-mysql
namespace: sample-blog
spec:
ingress:
- from:
- podSelector:
matchLabels:
app: django
ports:
- port: 3306
protocol: TCP
podSelector:
matchLabels:
app: mysql
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
creationTimestamp: "2022-07-13T02:30:51Z"
labels:
network-policy-generator.stackrox.io/generated: "true"
name: stackrox-generated-rhacs-operator-controller-manager
namespace: rhacs-operator
spec:
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: openshift-kube-apiserver-operator
podSelector:
matchLabels:
app: kube-apiserver-operator
ports:
- port: 9443
protocol: TCP
- ports:
- port: 8081
protocol: TCP
podSelector:
matchLabels:
app: rhacs-operator
control-plane: controller-manager
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
creationTimestamp: "2022-07-13T02:30:51Z"
labels:
network-policy-generator.stackrox.io/generated: "true"
name: stackrox-generated-redis
namespace: sample-blog
spec:
ingress:
- from:
- podSelector:
matchLabels:
app: django
ports:
- port: 6379
protocol: TCP
podSelector:
matchLabels:
app: redis
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
creationTimestamp: "2022-07-13T02:30:51Z"
labels:
network-policy-generator.stackrox.io/generated: "true"
name: stackrox-generated-nginx
namespace: sample-blog
spec:
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: openshift-ingress
podSelector:
matchLabels:
ingresscontroller.operator.openshift.io/deployment-ingresscontroller: default
ports:
- port: 8080
protocol: TCP
podSelector:
matchLabels:
app: nginx
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
creationTimestamp: "2022-07-13T02:30:51Z"
labels:
network-policy-generator.stackrox.io/generated: "true"
name: stackrox-generated-django
namespace: sample-blog
spec:
ingress:
- from:
- podSelector:
matchLabels:
app: nginx
ports:
- port: 8000
protocol: TCP
podSelector:
matchLabels:
app: django
policyTypes:
- Ingress
先ほどの画面でApply Network Policies
を選択すると、上記のNetwork Policyがデプロイされます。
ここではSample-blog
Namespaceに閉じた設定にしたいのと、k8sマニフェストでGit管理するために、画面右上の"Download YAML"で取得した後、少し修正します。
修正後のファイルが以下になります。
cat network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: nginx-network-policy
namespace: sample-blog
spec:
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: openshift-ingress
podSelector:
matchLabels:
ingresscontroller.operator.openshift.io/deployment-ingresscontroller: default
ports:
- port: 8080
protocol: TCP
podSelector:
matchLabels:
app: nginx
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: django-network-policy
namespace: sample-blog
spec:
ingress:
- from:
- podSelector:
matchLabels:
app: nginx
ports:
- port: 8000
protocol: TCP
podSelector:
matchLabels:
app: django
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mysql-network-poliy
namespace: sample-blog
spec:
ingress:
- from:
- podSelector:
matchLabels:
app: django
ports:
- port: 3306
protocol: TCP
podSelector:
matchLabels:
app: mysql
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: redis-network-policy
namespace: sample-blog
spec:
ingress:
- from:
- podSelector:
matchLabels:
app: django
ports:
- port: 6379
protocol: TCP
podSelector:
matchLabels:
app: redis
policyTypes:
- Ingress
kustomization.yamlを修正してデプロイします。
vi kustomization.yaml
---
network-policy.yamlのコメントアウトを消す
oc apply -k .
serviceaccount/sample-blog unchanged
configmap/django-mysql-conf unchanged
configmap/mysql-conf unchanged
configmap/redis-conf unchanged
secret/django-mysql-pass unchanged
secret/mysql-pass unchanged
service/django unchanged
service/mysql unchanged
service/nginx unchanged
service/redis unchanged
deployment.apps/django unchanged
deployment.apps/nginx unchanged
statefulset.apps/mysql unchanged
statefulset.apps/redis unchanged
networkpolicy.networking.k8s.io/django-network-policy created
networkpolicy.networking.k8s.io/mysql-network-poliy created
networkpolicy.networking.k8s.io/nginx-network-policy created
networkpolicy.networking.k8s.io/redis-network-policy created
route.route.openshift.io/nginx unchanged
実際にデプロイされ、Network Graphを確認すると適用したNetwork Policyを確認できます。
番外編:コンテナの振る舞い検知
RHACSのポリシーチェックはコンテナイメージやK8sマニフェストだけでなく、コンテナ内のアプリケーションの挙動に対するものも存在します。
左側メニューの"Platform"→"Policies"を選択後、フィルターでLifecycle Stage:
RUNTIME
と入力すると、実行中のコンテナに対するポリシーが表示されます。
画面を見ていただくとわかる通り、「kucectl execの実行」、「useraddの実行」、「暗号通貨マイニングプロセス」などのイベントを検知してViolationとしてアラートを発出することができます。
実際に実行中のPodにexecコマンドを実行して、useraddを試みてみます。
oc exec -it mysql-0 /bin/sh
oc exec -it mysql-0 /bin/sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
sh-4.4$ useradd test
useradd: Permission denied.
useradd: cannot lock /etc/passwd; try again later.
コンテナのユーザーIDがrootでないため、useraddは失敗しましたが、振る舞い検知としてアラートは発出されます。
実際に"Violations"に移動し、確認してみます。
ご覧の通り、exec実行とuseradd実行の2つの違反が確認できます。
この機能も活用しながら、振る舞いを検知し早急に対策を施すことでセキュリティを強化できます。
おわりに
今回はサンプルアプリを用いてRHACSの基本的な機能を確認しました。
RHACSは他にもCICDパイプラインとの連携やイメージ署名検証、3rdパーティツールとのインテグレーションなどさまざまな機能を具備しています。
Kuberentes環境のセキュリティレベルを向上してみたい方はこのツールの導入を検討してみてはいかがでしょうか。