LoginSignup
2
0

More than 1 year has passed since last update.

OpenShiftとサンプルアプリで始めるRed Hat Advanced Cluster Security for Kubernetes(RHACS)入門

Last updated at Posted at 2022-07-13

はじめに

Red Hatは、Kubernetes環境で動作しているコンテナのセキュリティ対策状況を管理する Red Hat Advanced Cluster Security for Kubernetes(RHACS) という製品を展開しています。
今回は2回の記事に分けて、RHACSのインストールから、RHACSを使ったサンプルアプリのセキュリティ対策方法についてまとめていこうと思います。

この記事では、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層アプリになっています。
アプリケーションの操作方法についてはこちらを参照ください。

sample-blog

まずは上記リポジトリをローカルに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に反映させています。

entrypoint.sh
#!/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が作成されます。

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}"

sample-blog

これで無事にサンプルアプリをデプロイできました。
アプリケーションの操作方法についてはこちらに記載していますので、一通り触ってみてください。

RHACSを使ったサンプルアプリのセキュリティ対策

さて、実際にサンプルアプリがデプロイできたので、このアプリのセキュリティ状況を確認していきましょう。
以下のコマンドでRHACSのコンソールのURLを取得してにブラウザでアクセスしてみます。

oc get route central -n stackrox -o jsonpath="{.status.ingress[0].host}"

RHACSダッシュボード

RHACSは脆弱性管理、DevSecOps、ネットワークセグメンテーション、リスク分析など、多角的な視点でKubernetes環境のセキュリティ対策状況を確認することができます。
ただ逆にできることが多いため、何から手をつけていいか分からないこともあるかと思います。
ここではあくまで入門編になりますが、RHACSを使ったセキュリティ対策を進め方の一例をご紹介します。

Violations

まずはViolationsを確認して、先ほどデプロイしたサンプルアプリのポリシー準拠状況を確認してみます。
画面左の"Violations"を選択して、フィルターでNamespace: sample-blogを入力してみましょう。Severityで並び替えすると見やすくなります。
Violations

見ていただくとわかる通り、Violationはポリシーに基づきDeploymentやStatefulsetsなどのWorkloadリソースをチェックし、違反している場合はポリシーに従い重要度をつけて表示する機能になっています。
今回はRHACSをインストールした状態のままなので、ここに表示されているのはRHACSのデフォルトのポリシーに違反したリソースということになります。

デフォルトのポリシーについてもコンソール上で確認可能です。
左メニューの"Platform Configuration"→"Policies"を選択すると、デフォルトを含むポリシー一覧が確認できます。
policies

Severityで重要度順に並べ替えてみましょう。
policies severity
みてみると「特権コンテナの実行」や、非常に危険度の高い脆弱性(Spring4Shell/Log4Shell/Apache Struts)についてはCriticalに設定されています。Highではマニフェストの環境変数にSECRETPASSWORDを含むものが平文で書かれていることなどをチェックしているようです。
ちなみに執筆時(2022/07)ではデフォルトポリシーは以下の項目数となっています。

Severity Policies
Critical 4
High 28
Medium 24
Low 25

もちろん、RHACSでは既存のポリシーを変更したりカスタムポリシーを作成することも可能ですが。いきなり自分達でポリシーを定義することも難しいかと思いますので、まずはデフォルトのポリシーをベースに対策を進めるとイメージがつきやすいかと思います。

では少し戻って、Namespace: sample-blogで違反しているポリシーを実際に確認していきます。
以下はポリシーの中でもSeverity:Highのものを抽出したものです。
severity high

みていただくと4つのサービス全てでFixable Severity at least Importantというポリシーに違反しています。
一番上のmysqlのリンクをクリックして、具体的にどんなポリシーに違反しているのかを確認してみます。

先にPolicyタブを選択すると、このポリシーの概要を確認できます。
深刻度ランクがImportant以上の修正可能な脆弱性が存在する場合に違反となるポリシーであることが分かります。
Policy

Violationタブに移動すると、具体的にどのCVEの脆弱性があるかを確認できます。この違反をクリアするには、ここに表示されている脆弱性への対策を行う必要があることが分かります。
Violation

Vulnerability Management

なお、RHACSではVulnerability Managementという、コンテナイメージの脆弱性に対してより詳しく確認できる機能があります。
左側メニューの"Vulnerability Management"→"Dashboard"を選択します。
Vulnerability Management Dashboard

このダッシュボードでクラスター上にデプロイされているリソース(が使っているコンテナイメージ)の脆弱性対策状況が確認できます。
が、情報量がかなり多いのでもう少し絞ってみます。
右上の"APPLICATION & INFRASTRUCTURE"から"Deployments"を選択します。その後Namespace: sample-blogでフィルターをかけると、サンプルアプリのDeployment(&Statefulsets)の脆弱性情報が確認できます。
Deployments

では先ほど確認したmysqlの情報をみてみます。該当のDeploymentをクリックすると、以下のようなウィンドウが表示されます。
mysql

中央円グラフの"POLICY VIOLATIONS BY SEVERITY"では、現在違反しているポリシー数が確認でき、右側円グラフではこのコンテナイメージが持つ脆弱性を確認できます。
Deployment Findingsでは違反しているポリシーや、FIXABLE(修正可能)な脆弱性情報を確認できます。試しにFIXABLEタブを選択すると、以下のようにViolationsで確認した脆弱性情報が記載されています。
このように、コンテナの脆弱性状況は基本にVulnerability Mangementで確認し、ポリシーによってアラートを発火させる、というのが一般的な使い方になります。
fixable

行ったり来たりになりますが、一個前の画面の"POLICY VIOLATIONS BY SEVERITY"の右上にある"VIEW ALL"を選択すると、このRHACSで設定されている脆弱性関連のポリシー一覧と、このDeploymentにおける各ポリシーへのステータスが確認できます。
みてもらうと、デフォルトでは脆弱性関連のポリシー(のうち有効化されているもの)は5つあり、その中の1つのポリシーに違反していることがわかります。
POLICIES

コンテナイメージの脆弱性への対策

コンテナイメージに対して修正可能な脆弱性がひそんでいることがわかったので、実際に対策していきましょう。
まずはこのコンテナイメージはどのベースイメージからつくられているのかを確認します。

先ほど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
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: docker.io/mysql:8.0.26
...omit...
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: 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
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
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を確認してみましょう。
Violations
元々あったFixable Severity at least Importantが消えていますね。

Vulnerability Managementも確認してみます。
Vulnerability Management
Policy StatusがPassになりました!

このようにRed Hatのイメージを使うだけで脆弱性対策が施されたイメージで安全にアプリケーションをデプロイできます。
ただし以下のように注意事項もあるため、チームのセキュリティポリシーに合わせて適切なベースイメージを選択してください。

  • Red Hatが提供していないイメージもある
  • 使いたいバージョンのイメージがまだ提供されていないケースもある
    • たとえば、Redisの最新メジャーバージョンは7系ですが、Red Hatの提供イメージの最新は6系です。
  • 全ての脆弱性が対策されている訳ではないため、必要に応じてビルド時に別途対策が必要

Configuration Management

RHACSでは、コンテナイメージの脆弱性だけでなくKubernetesレイヤのマニフェスト構成をチェックし、セキュリティ的にリスクのある設定があるかどうかのチェックを行うことができます。

先ほどのViolationsをもう一度みてみると、HighMediumの違反ポリシーはKubernetesの設定周りのものであることが分かります。

  • Environment Variable Contains Secret
  • Pod Service Account Token Automatically Mounted
  • No resource requests or limits specified
    Violations

それぞれの違反ポリシーについて少し深掘りしていきます。

Environment Variable Contains Secret

これはDeploymentの環境変数内にSECRETPASSWORDのような文字を含むkeyが存在すると違反となるポリシーです。
djangomysqlでこの違反が出ていますが、これはMySQLのパスワード情報を環境変数でそのまま渡していることが原因です。
django-deployment.yamlをみるとMYSQL_PASSWORDBLOG_ADMIN_PASSWORDといった環境変数が設定されています。

django-deployment.yaml
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を確認できます。
Rationale

ということで、今回は別のサービスアカウントを作成し、Deloymentにてサービスアカウントを指定する形を取ります。

No resource requests or limits specified

これはイメージしやすいものかと思います。それぞれのdeployment.yamlstatefulset.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

ではViolationsを確認します。
Violations

HighMediumが全て消えました!
今回はLowは残したままの状態になっていますので、もし手元に試せる環境があればぜひ対策を施してみてください。

ちなみにこれまで対応したKuberetesの設定周りの状況については、RHACSのConfiguration Managementで確認できます。
画面左メニューのConfiguration Managementを選択します。
Configuration Management

ここで全クラスターの設定状況を確認できますが、かなり俯瞰的なものなので、Vulnerability Managementと同様、対象を絞り込みます。
右上の"APPLICATION & INFRASTRUCTURE"から"Deployments"を選択します。その後Namespace: sample-blogでフィルターをかけると、サンプルアプリのDeployment(&Statefulsets)の設定情報が確認できます。
deployments

詳細を確認する場合は個別のDeploymentを選択します。
ポリシー違反状態ですが、先ほど残したLowのポリシーに引っ掛かっていることが分かります。
このように、Vulnerability Management同様、アラートはポリシーのViolationで拾い、状況をConfiguration Managementで確認する、という使い方になるかと思います。
Summary

Network Policy Management

一通りデフォルトのポリシー違反のうち優先度の高いものは対応できましたが、もうひとつ、RHACSの機能であるNetwork Policy関連の機能を試してみます。
RHACSではアプリケーションの通信フローをチェックし、適切なNetwork Policyを自動生成する機能を具備しています。

左メニューからNetwork Graphを選択し、NamespacesプルダウンでSample-blogをチェックすると、以下のように4つのサービスを確認できる画面が表示されます。
Network Graph

この画面にて右上のNetwork Policy Simulatorを選択すると、以下のような画面がでるので、Generate and simulate network policiesを選択します。
Network Policy Simulator

するとクラスター内の通信状況をベースに自動でNewtwork Policyが作成されます。
スクリーンショット 2022-07-13 11.26.55.png

作成されたNetwork Policyは以下の通りです。5つ出力されていますが、Smaple-blogNamespaceに関するものは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作成機能の大きな特徴かと思います。

network-policy.yaml
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-blogNamespaceに閉じた設定にしたいのと、k8sマニフェストでGit管理するために、画面右上の"Download YAML"で取得した後、少し修正します。

修正後のファイルが以下になります。

cat network-policy.yaml
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を確認できます。
network policy

番外編:コンテナの振る舞い検知

RHACSのポリシーチェックはコンテナイメージやK8sマニフェストだけでなく、コンテナ内のアプリケーションの挙動に対するものも存在します。
左側メニューの"Platform"→"Policies"を選択後、フィルターでLifecycle Stage: RUNTIME と入力すると、実行中のコンテナに対するポリシーが表示されます。
画面を見ていただくとわかる通り、「kucectl execの実行」、「useraddの実行」、「暗号通貨マイニングプロセス」などのイベントを検知してViolationとしてアラートを発出することができます。
RUNTIME

実際に実行中の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"に移動し、確認してみます。
Violations

ご覧の通り、exec実行とuseradd実行の2つの違反が確認できます。
この機能も活用しながら、振る舞いを検知し早急に対策を施すことでセキュリティを強化できます。

おわりに

今回はサンプルアプリを用いてRHACSの基本的な機能を確認しました。
RHACSは他にもCICDパイプラインとの連携やイメージ署名検証、3rdパーティツールとのインテグレーションなどさまざまな機能を具備しています。
Kuberentes環境のセキュリティレベルを向上してみたい方はこのツールの導入を検討してみてはいかがでしょうか。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0