1.はじめに
2.検証環境の構成
3.前作業
4.minikubeのインストール
5.Gitlabのインストール
6.Helmのインストール
7.KubernetesとGitlabを連携
8.CICDのパイプラインにTrivyによるコンテナイメージスキャンを追加
#1. はじめに
この記事は、NTTコムウェア Advent Calendar 2021 23日目の記事です。
NTT コムウェアでセキュリティエンジニアの仕事をしている山原です。
この記事では、社内のProxy環境下でMinikubeを使用してKubernetes環境を構築し、
GitLabのCI/CDのパイプラインの中にコンテナのイメージスキャンを組み込んだ検証結果をまとめました。
KubernetesやCI/CDには興味があるけど、社内環境で構築するのがしんどいと思う人が参考にできる内容となっています。
また、コンテナイメージの脆弱性スキャンが必要な理由としては、DockerHubに公開されている400万イメージのうち51パーセントに重大な脆弱性を抱えています。
https://www.infoq.com/jp/news/2021/03/dockerhub-image-vulnerabilities/
DockerコンテナやKubernetesは、現在のエンジニアリングには欠かせない便利なツールとなっていますが、システムにコンテナの脆弱性を盛り込めば、外部からの攻撃対象となってしまいます。安全にコンテナイメージを使用するために、脆弱性スキャンを取り入れることを検討してください。
皆様良きコンテナライフを!!
##やりたいこと
オンプレ環境にGitlabを構築し、minikubeで作成したkubernetes環境と連携をします。
CICDのパイプラインの上でプライベートコンテナレジストリにおける脆弱性スキャンを実施し、
コンテナイメージの安全性を確かめます。
#2. 検証環境の構成
OS:CentOS Stream release 8
GitLab CE 14.5.2
minikube version: v1.24.0
Docker Version 19.03.9
#3. 前作業
- サーバ証明書の設定
- Proxyの設定
- kubectlをインストール
- Dockerのインストール
- Dockerのレジストリの証明書を設定
サーバ証明書の設定
社内で配布しているルート証明書を以下のディレクトリに配置する
cp ca.crt /etc/pki/ca-trust/source/anchors/
update-ca-trust extract
Proxyの設定
/etc/profileにproxyの設定を追記する
no_proxyの設定まで実施すること
vi /etc/profile
PROXY='XXXXXXXXX:8080' # proxyサーバのドメインとポートを指定
export http_proxy=$PROXY
export HTTP_PROXY=$PROXY
export https_proxy=$PROXY
export HTTPS_PROXY=$PROXY
export no_proxy=127.0.0.1,localhost,192.168.1.1/16
kubectlのインストール
1.yumにkubernetesのrepoを追加する
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
2.kubectlをインストールする
yum install -y kubectl
インストール済み:
kubectl-1.22.4-0.x86_64
Dockerのインストール
1.インストール用のtarファイルをダウンロードする
wget --no-check-certificate https://download.docker.com/linux/static/stable/x86_64/docker-19.03.9.tgz
2.tarファイルを展開する
tar xzvf docker-19.03.9.tgz
3.コマンドを使用可能な状態にする
chown root:root docker/*
cp docker/* /usr/bin/
4.dockerサービスの設定をする
[root@hogehoge]# cat << EOF > /usr/lib/systemd/system/docker.service
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
BindsTo=containerd.service
After=network-online.target firewalld.service containerd.service
Wants=network-online.target
Requires=docker.socket
[Service]
Type=notify
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP \$MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
StartLimitBurst=3
StartLimitInterval=60s
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
Delegate=yes
KillMode=process
[Install]
WantedBy=multi-user.target
EOF
5.Dockerソケットを設定する
#cat << EOF > /usr/lib/systemd/system/docker.socket
[Unit]
Description=Docker Socket for the API
PartOf=docker.service
[Socket]
ListenStream=/var/run/docker.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker
[Install]
WantedBy=sockets.target
EOF
# cat << EOF >/usr/lib/systemd/system/containerd.service
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target
[Service]
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/bin/containerd
KillMode=process
Delegate=yes
LimitNOFILE=1048576
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
[Install]
WantedBy=multi-user.target
EOF
##Dockerレジストリの証明書を設定
Proxy配下では使用するDockerレジストリに証明書を設定しないとコンテナイメージをpullすることができない。
1.証明書を格納するためのディレクトリを作成する
cd /etc/docker/certs.d
mkdir registry-1.docker.io
mkdir k8s.gcr.io
mkdir ghcr.io
2.ルート証明書を配置する
cp /tmp/ca.crt /etc/docker/certs.d/registry-1.docker.io
cp /tmp/ca.crt /etc/docker/certs.d/k8s.gcr.io
cp /tmp/ca.crt /etc/docker/certs.d/ghcr.io
3.Dockerに証明書を反映させる
sudo systemctl daemon-reload
sudo systemctl restart docker
#4. minikubeのインストール
1.swapをoffにする
swapoff -a
2.minikubeのインストール資材をダウンロードし、コマンドに実行権限を与える
wget https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
cp minikube-linux-amd64 /usr/local/bin/minikube
chmod 755 /usr/local/bin/minikube
3.minikubeのバージョンを確認する
minikube version
minikube version: v1.24.0
commit: 76b94fb3c4e8ac5062daf70d60cf03ddcc0a741b
4.minikubeをスタートする
今回はkvm2を指定する。Dockerを指定しても大丈夫
※minikubeはroot実行できない
minikube start --vm-driver=kvm2 --cache-images=false --docker-env HTTP_PROXY=$HTTP_PROXY --docker-env HTTPS_PROXY=$HTTPS_PROXY --listen-address=0.0.0.0
5.minikubeにも証明書をインストールする
一旦minikubeのシングルクラスタを削除して、所定のディレクトリにPEMファイルを配置する
minikube delete
openssl x509 -in ca.crt -out tmp.der -outform DER
openssl x509 -in tmp.der -inform DER -out ca.pem -outform pem
cp ca.pem /home/${USER}/.minikube/certs/
minikube start --vm-driver=kvm2 --cache-images=false --docker-env HTTP_PROXY=$HTTP_PROXY --docker-env HTTPS_PROXY=$HTTPS_PROXY --listen-address=0.0.0.0
6.kubectlコマンドを使用できるようにminikubeのユーザコンテキストに切り替える
kubectl config use-context minikube
#5. Gitlabをインストールする
minikube上でGitlabのpodを作成すると、メモリの制約の問題でおすすめできない
今回はサーバOS上にGitlabをインストールする
1.インストール資材をダウンロードして実行する
dnf install gitlab-ce
2./etc/gitlab/gitlab.rbを編集し、外部からGitlabとコンテナレジストリを公開する
vi /etc/gitlab/gitlab.rb
external_url 'http://XXXXXXXXXXXXX:1010' ← コメントを外して外部からアクセスできるURLを設定する。IPアドレスでも良い
registry_external_url 'http://XXXXXXXXXXXXX:2020' ← コメントを外して外部からアクセスできるURLを設定する。IPアドレスでも良い
3.gitlabのコンフィグを読み直す
/opt/gitlab/embedded/bin/runsvdir-start &
gitlab-ctl reconfigure
4.Gitlabの初期パスワードを確認する
rootでログインするパスワードとなる
cat /etc/gitlab/initial_root_password
5.ブラウザで外部に公開したURLにアクセスし、適当なグループとプロジェクトを作成する
#6. helmをインストールする
1.インストール資材をダウンロードする
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
2.資材を使ってインストールする
chmod 700 get_helm.sh
./get_helm.sh
#7. KubernetesとGitlabを連携
1.kubernetes上にgitlab-runner用のサービスアカウントを作成する
kubectl apply -f gitlab-admin-service-account.yaml
(gitlab-admin-service-account.yaml)
apiVersion: v1
kind: ServiceAccount
metadata:
name: gitlab
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: gitlab-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: gitlab
namespace: kube-system
2.Kubernetesと連携する
Gitlabにアクセスして、AdminエリアよりKubernetesを選択し、「Connect existing cluster」をクリックし、必要な情報を入力する
Kubernetes cluster name: minikube
API URL: /home/XXXXX/.kube/config のserverを参照する
CA Certificate : /home/XXXXX/.kube/config のclient-certificateを参照する
残りはデフォルトの設定
4.gitlabrunnerのRegistrationTokenを確認する
作成したグループのSettingsよりCI/CDを選択し、Runnersの詳細を見ると[RegistrationToken]が表示されているので、コピーする。
5.Helmを使用してgitlab runnnerをデプロイする
runnerRegistrationTokenに先ほどコピーしたTokenを入力する
helm install --namespace gitlab gitlab-runner gitlab/gitlab-runner --set gitlabUrl=http://XXXXXXXXXXX:1010/ --set runnerRegistrationToken=XXXXXXXXXXXXX--set privileged=true --set rbac.create=true --set runners.config="
[[runners]]
clone_url = \"http://xxxxxxxxxx:1010/\"
[runners.kubernetes]
image = \"ubuntu:16.04\"
"
6.gitlab runnerが正常に立ち上がっているかを確認する
正常にRunning状態になっていることを確認しておく
$ kubectl get po -n gitlab
NAME READY STATUS RESTARTS AGE
gitlab-runner-gitlab-runner-8546485df9-f58c6 1/1 Running 0 6d20h
#8. CICDのパイプラインにTrivyによるコンテナイメージスキャンを追加
1.Gitlabのパイプラインの中でビルドをする時にDocker in Docker(Dind)を使用するため、
コンテナが外部と疎通ができるように、Proxyと証明書の設定をしておく必要がある。
[settings]-[CI/CD]-[Variables]に証明書を変数として登録しておく。(管理は厳重に!)
2.Gitlabに作成したプロジェクトの上にDockerファイルと**.gitlab-ci.yml**を作成すれば、
コンテナのイメージスキャンは実施できる。
今回は、どなたでもパイプライン上でイメージスキャンを実行できるように[環境準備]、[テスト]、[デプロイ]をスキップする。
stages:
- prepare
- test
- build
- scan
- deploy
prepare-job:
stage: prepare
script:
- echo "Suceess"
test-job:
stage: test
script:
- echo "Suceess test"
build-job:
stage: build
image: docker:dind
variables:
DOCKER_DRIVER: overlay2
http_proxy: http://XXXXXXXXXXXX:8080
https_proxy: http://XXXXXXXXXXXXX:8080
NO_PROXY: 127.0.0.1,localhost,192.168.1.1/16
DOCKER_HOST: /var/run/docker.sock
services:
- docker:dind
script:
- touch /usr/local/share/ca-certificates/root.crt
- echo "${rootca}" >> /usr/local/share/ca-certificates/root.crt
- update-ca-certificates
- docker build ./-t (GitlabregistryのURI)/trivy-test2:1.0
- dockerd --insecure-registry=XXXXXXXXXXX &
- sleep 5
- docker login XXXXXXXX:2020 -u (Gitlabのユーザ) -p (Gitlabのパスワード)
- docker build ./-t GitlabregistryのURI)/trivy-test1.0
- docker push GitlabregistryのURI)t/trivy-test1.0
scan-job:
stage: scan
image: docker.io/aquasec/trivy
variables:
http_proxy: http://XXXXXXXXXXXX:8080
https_proxy: http://XXXXXXXXXXXXX:8080
NO_PROXY: "127.0.0.1,localhost,192.168.1.1/16"
TRIVY_USERNAME: "Gitlabユーザ"
TRIVY_PASSWORD: "Gitlabパスワード"
TRIVY_NON_SSL: "true"
script:
- touch /usr/local/share/ca-certificates/root.crt
- echo "${rootca}" >> /usr/local/share/ca-certificates/root.crt
- update-ca-certificates
- trivy GitlabregistryのURI)/trivy-test1.0
artifacts:
when: always
reports:
scan-job: gl-container-scanning-report.json
test-deploy:
stage: deploy
script:
- echo "Suceess deploy"
(Dockerfile)
FROM node:8.11-alpine
WORKDIR /usr/src/app
ENV http_proxy="XXXXXXXX:8080"
ENV https_proxy="XXXXXXXXXXX:8080"
ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
COPY package*.json /usr/src/app/
RUN npm install
COPY . /usr/src/app
ENV PORT 5000
EXPOSE $PORT
CMD [ "npm", "start" ]
3.Dockerファイルと**.gitlab-ci.yml**をGitlabの任意のプロジェクトにpushすれば、CICD+コンテナイメージスキャンのジョブが自動起動する
パイプラインの実行中の画面
4.パイプラインの実行が成功するとすべてグリーンのチェック状態となる。
5.イメージスキャンジョブの中身を見ると、脆弱性スキャンが実行されていることがわかる。
gl-container-scanning-report.jsonとしてジョブの結果はレポートとして保存される。