手元でPowerDNSのAPIを叩くためのDockerイメージを作った(その2)
こちらの記事で、手元でPowerDNSのAPIを叩くためのDockerイメージを作りましたが、
加えて、
というのをやっていきます。
レコードの保存先をMySQLへ
docker-composeに以下の設定を追加しました。
mysql: image: mysql:5.7 healthcheck: test: "echo 'SELECT version();'| mysql -u pdns -h 127.0.0.1 --password=pdns pdns" timeout: 30s retries: 5 ports: - "13306:3306/tcp" volumes: - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro - mysql_data:/var/lib/mysql environment: - "MYSQL_DATABASE=pdns" - "MYSQL_USER=pdns" - "MYSQL_PASSWORD=pdns" - "MYSQL_RANDOM_ROOT_PASSWORD=yes" volumes: mysql_data:
powerdns: depends_on: mysql: condition: service_healthy
また、設定に以下を追加します。
launch=gmysql # launch=bind から変更 gmysql-host=mysql gmysql-user=pdns gmysql-password=pdns gmysql-dbname=pdns
具体的に何をやっているかというと・・・
healthcheck, depends_on
DBの準備ができる前にpowerdnsを起動してしまうと、DBのコネクションエラーになってしまうので、MySQLが立ち上がり、DBがセットアップできるまでpowerdnsを起動させないようにしています。
volumes
/docker-entrypoint-initdb.d/
以下にSQL, シェルスクリプトなどのファイルを置いておくと、MySQLコンテナ起動時に実行してくれるようになります。
それを利用して、PowerDNSのテーブルをCREATEするようにしています。
SQLはこちら https://github.com/PowerDNS/pdns/blob/rel/auth-4.3.x/modules/gmysqlbackend/schema.mysql.sql
environment
MySQLのDB情報を設定しています。
詳しくはこちら https://hub.docker.com/_/mysql
PowerDNS-Adminを使ってGUIで管理できるようにする
以下の設定を追加しました。
powerdns-admin: image: ngoduykhanh/powerdns-admin:latest ports: - "9191:80" logging: driver: json-file options: max-size: 50m environment: - SQLALCHEMY_DATABASE_URI=mysql://pdns:pdns@mysql/pdns - GUNICORN_TIMEOUT=60 - GUNICORN_WORKERS=2 - GUNICORN_LOGLEVEL=DEBUG - OFFLINE_MODE=False
これは公式の設定通り
https://github.com/ngoduykhanh/PowerDNS-Admin
動かしてみる
起動は通常通り docker-compose up
で起動、http://localhost:9191/ へアクセスし、GUIからのAレコードを追加してみます。
test.takapi-example.jp
からIPアドレス127.0.0.1が引けるようにしました。
dig +norec @localhost -p 1053 test.takapi-example.jp A ; <<>> DiG 9.10.6 <<>> +norec @localhost -p 1053 test.takapi-example.jp A ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57925 ;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ;; QUESTION SECTION: ;test.takapi-example.jp. IN A ;; ANSWER SECTION: test.takapi-example.jp. 60 IN A 127.0.0.1 ;; Query time: 7 msec ;; SERVER: ::1#1053(::1) ;; WHEN: Fri Jan 01 17:42:52 JST 2021 ;; MSG SIZE rcvd: 67
構築した環境はこちらで公開しております。 github.com
とりあえず、今日はここまで
手元でPowerDNSのAPIを叩くためのDockerイメージを作った
PowerDNSのAPIを手元で叩ける環境が必要だったので用意しました。
要件は以下です。
- ローカルで動作すること
- 最低限ビルトインのAPIが動くこと
- digで問い合わせができること
インストールする
こちらを参考にDockerfileを作成します。 https://repo.powerdns.com/
CentOS7, PowerDNS4.3を使いたかったので、PowerDNS Authoritative Server - version 4.3.X
に記載されているコマンドを使います。
yum install epel-release yum-plugin-priorities && curl -o /etc/yum.repos.d/powerdns-auth-43.repo https://repo.powerdns.com/repo-files/centos-auth-43.repo && yum install pdns
起動する
/usr/lib/systemd/system/pdns.service
の ExecStart
をみてみると、/usr/sbin/pdns_server
で起動するようなので、
CMD ["/usr/sbin/pdns_server"]
を追加します。
Dockerfileはこんな感じになりました。
FROM centos:7 RUN yum update -y \ && yum install -y epel-release yum-plugin-priorities \ && curl -o /etc/yum.repos.d/powerdns-auth-43.repo https://repo.powerdns.com/repo-files/centos-auth-43.repo \ && yum install -y pdns \ && rm -rf /var/cache/yum \ && yum clean all CMD ["/usr/sbin/pdns_server"]
設定する
以下を参考に設定 https://doc.powerdns.com/recursor/http-api/#enabling-the-api
開発環境なので、どこからでもAPIを叩けるようにします。
設定はこんな感じになりました。
api=yes api-key=changeme launch=bind setgid=pdns setuid=pdns webserver=yes webserver-address=0.0.0.0 webserver-allow-from=0.0.0.0/0 webserver-port=8081
設定をvolumesで読み込ませ、portをbindします。
version: '3' services: powerdns: build: ./ volumes: - ./pdns.conf:/etc/pdns/pdns.conf:ro ports: - "8081:8081" - "1053:53/tcp" - "1053:53/udp"
動作確認
上記で準備ができたので、動作確認します。
APIを叩く
なんか返ってきた
curl -H 'X-API-Key: changeme' http://127.0.0.1:8081/api/v1/servers/localhost | jq . { "config_url": "/api/v1/servers/localhost/config{/config_setting}", "daemon_type": "authoritative", "id": "localhost", "type": "Server", "url": "/api/v1/servers/localhost", "version": "4.3.1", "zones_url": "/api/v1/servers/localhost/zones{/zone}" }
digで問い合わせをする
こちらもなんか返ってきた
dig +norec @localhost -p 1053 test.example.com ANY ;; Truncated, retrying in TCP mode. ; <<>> DiG 9.10.6 <<>> +norec @localhost -p 1053 test.example.com ANY ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 28280 ;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ;; QUESTION SECTION: ;test.example.com. IN ANY ;; Query time: 4 msec ;; SERVER: ::1#1053(::1) ;; WHEN: Fri Jan 01 17:11:39 JST 2021 ;; MSG SIZE rcvd: 45
まとめ
とりあえず、APIを手元で叩けるようになりました。 実際にレコードを登録するなどは、後ほどやっていこうと思います。
参考
Smartyでのエスケープをまとめた
足りてないパターンがあれば追加予定。
参考
- 『体系的に学ぶ安全なWebアプリケーションの作り方 第2版』
- Smartyのコード https://github.com/smarty-php/smarty/blob/478f8a29b4da07b7d18164cc8554b967a2036678/libs/plugins/modifier.escape.php
要素内容
<p>ここに文字が入る場合</p>
XSS対策方法
<
と &
を文字参照にする
Smartyでは
<{$hoge|escape:'html'}>
を使います。
※ & " ' < >
がエスケープされます。
例:
<div> <p>名前:</p><span><{$name|escape:'html'}></span> </div>
属性値
<input type="text" name="name" value="ここに文字が入る場合">
XSS対策方法
- 属性値を
"
で囲う <
"
&
を文字参照にする
Smartyでは
<{$hoge|escape:'html'}>
を使います。
※ & " ' < >
がエスケープされます。
例:
<input type="text" name="name" value="<{$name|escape:'html'}>">
属性値(URL)
<a href="ここに文字が入る場合">hogehoge</a>
XSS対策方法
- URLの形式チェックをする
- 属性値としてエスケープ
Smartyでは
<{$hoge|escape:'html'}>
を使います。
※ & " ' < >
がエスケープされます。
また、PHP側で形式のチェックをします。
例:
<a href="<{$url|escape:'html'}>">hogehoge</a>
if (preg_match('/\Ahttps?:\/\//', $url) || preg_match('/\A\//', $url)) { return ture; } else { return false; }
イベントハンドラ
<body onload="init('ここに文字が入る場合')">
XSS対策方法
- JavaScriptとしてエスケープ
\ ' " 改行
=>\\ \' \" \n
- 属性値としてエスケープ
Smartyでは
<{$hoge|escape:'javascript'|escape:'html'}>
を使います。
※ \ " ' 改行 </
と & " ' < >
がエスケープされます。
例:
<body onload="init('<{$hoge|escape:'javascript'|escape:'html'}>')">
script要素内の文字列リテラル
※基本的には、JavaScriptに直接文字を埋め込まないのが推奨
<script type="text/javascript"> var x = "hoge"; document.write('ここに文字が入る場合'); </script>
XSS対策方法
- Javascriptとしてエスケープ
</
が出現しないよう考慮
Smartyでは
<{$hoge|escape:'javascript'}>
を使います。
※ \ " ' 改行 </
がエスケープされます。
例:
<script type="text/javascript"> var x = "hoge"; document.write('<{$hoge|escape:'javascript'}>'); </script>
オープンソースのSNSをDockerで動かしてみたら懐かしすぎた
こんにちは、たかぴーです。
このエントリは、GMOペパボエンジニア Advent Calendar 2020 - Adventarの8日目の記事です。7日目はしばっちさんの「AWSでDNSをRoute53を使わずに構築する」でした。
社内のPHPアプリケーションをDocker化するにあたりどういった流れで構築していくのか、自分はこうやっているよ!というのを書いていこうと思ったのですが、社内のアプリケーションの設定やソースコードは表に出す訳にはいかないので、今回は代わりにOpenPNEという、PHP製のオープンソースなSNSをDocker化し構築の流れを書いていきます。
今回は最低限動かすことが目的なので、ローカルで動くところまでをゴールとします。
1. まずは、ざっくり動かしてみる
まずは細かいことは気にせず、ざっくり動かしていきます。
情報を集める
最新版(12/8時点)のOpenPNEのセットアップ手順をの通りにやれば良さそうです。
推奨構成はこんな感じ
OS: Debian jessie Apache 2.4.10 以降 PHP: PHP 7 以降 DB MySQL 5.5.53以降
いわゆるLAMP環境ってやつですね。 Docker Hubの公式イメージを使って作れそうです。
docker-composeを使って大枠を作る
最終的には、docker-composeがなくてもイメージ単体で動作させる予定ですが、 イメージ構築時は、設定をyamlに追加しながら作業できるので、個人的にはdocker-composeを使いながら構築するのがやりやすいです。
こんな感じで大枠を作成
version: '3' services: app: build: ./ db: image: mysql:5.7.32
FROM php:7.4.10-apache-buster
php:7.4.10-apacheで、jessieのイメージはなかったので、busterを使いました。(多分動くはず)
基本的に野良イメージは使わず、Officialなイメージを使うようにしています。
ライブラリを追加
引き続き、セットアップ手順を参考に必要なライブラリを追加していきます。
Dockerfileは後ほどきれいにするので、まずは雑にライブラリをインストールしていきます。
Dockerfileは上から各命令ごと順番にキャッシュされます。 そのため、構築時に上の方に記述されている命令を変更すると、それ以下はキャッシュが効かなくなるため、一気にインストールせずキャッシュを効かせながらインストールしています。
FROM php:7.4.10-apache-buster RUN apt update && apt -y upgrade RUN apt install -y libonig-dev RUN docker-php-ext-install mbstring RUN apt install -y libxml2-dev RUN docker-php-ext-install xml RUN docker-php-ext-install pdo_mysql RUN docker-php-ext-install json RUN apt -y install zlib1g-dev RUN apt -y install libpng-dev RUN apt -y install exif RUN apt -y install mcrypt RUN pecl install apcu RUN docker-php-ext-enable apcu RUN pecl install xdebug RUN docker-php-ext-enable xdebug RUN a2enmod rewrite RUN docker-php-ext-install exif
ソースコード・設定ファイルをマウント
docker-compose側で、ソースコードとOpenPNEの設定ファイルをマウントするようにします。 また、ここには出していないですがphp.ini, apache2.confの内容もいい感じに設定しています。
volumes: - ./src:/var/www/html - ./config/OpenPNE.yml:/var/www/html/config/OpenPNE.yml - ./config/ProjectConfiguration.class.php:/var/www/html/config/ProjectConfiguration.class.php - ./config/apache2.conf:/etc/apache2/apache2.conf - ./config/php.ini:/usr/local/etc/php/php.ini
個人的に、設定ファイルはconf.dなどメインのファイルにインクルードするような場所に置くと使用するイメージの設定によって設定内容が変わってくるため、conf.dなどのメインに設定にインクルードするような箇所にはマウントせず、メインのファイルのみ書き換えインクルードさせないようにするのが好みです。(が、みなさんどうやっているのだろう??)
初期化する
初期設定では、コマンドを叩きテーブルを初期化する必要があるようなので、mysqlのDBにユーザ・パスワード等の設定を追加し、実行します。
DBの設定を追加
db: image: mysql:5.7.32 volumes: - data_volume:/var/lib/mysql environment: TZ: Asia/Tokyo LANG: ja_JP.UTF-8 MYSQL_DATABASE: openpne MYSQL_USER: openpne_user MYSQL_PASSWORD: openpne_password MYSQL_RANDOM_ROOT_PASSWORD: "yes" volumes: data_volume:
初期化コマンドを実行!
docker-compose run --rm app bash -c "./symfony openpne:fast-install --dbms=mysql --dbuser=openpne_user --dbpassword=openpne_password --dbhost=db --dbport=3306 --dbname=openpne"
>> installer installation is completed!
と出力されたので上手くいったっぽい。
動かす!
docker-compose up して動かします!
おお、なんかすごくm○xiっぽい。構築がおわったらゆっくり見ていきます。
とりあえず、それっぽいコミュニティを設定しときました。
動かない箇所の修正
ポチポチ動かしてみると画像のアップロードが上手く行きませんでした。 エラーログやXdebugなどでデバッグで確認していきます。
ちなみに、DockerへのXdebug導入はこんな感じ
今回は、拡張モジュールであるGDが漏れていたようなので追加しました。
RUN apt-get install -y libfreetype6-dev libjpeg-dev RUN docker-php-ext-configure gd --with-jpeg=/usr/include/ --with-freetype=/usr/include/ RUN docker-php-ext-install -j$(nproc) gd
これで一通り動きました!
2. 設定を見直す
Dockerfileを整理する
キャッシュが効くようにババっと書いていってしまいましたが、 見た目も悪いですし、イメージのサイズも大きくなってしまうので整理していきます。
FROM php:7.4.10-apache-buster RUN apt update && apt -y upgrade && apt install -y --no-install-recommends \ exif \ libfreetype6-dev \ libjpeg-dev \ libonig-dev \ libpng-dev \ libxml2-dev \ mcrypt \ zlib1g-dev \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* RUN pecl install apcu && docker-php-ext-enable apcu RUN docker-php-ext-install -j$(nproc) \ exif \ json \ mbstring \ pdo_mysql \ xml RUN docker-php-ext-configure gd --with-jpeg=/usr/include/ --with-freetype=/usr/include/ \ && docker-php-ext-install -j$(nproc) gd RUN a2enmod rewrite
こんな感じで整理しましたが、整理した以外にも上記の設定はこんなことを意識しています。
apt updateと一緒にapt installする
apt installをする際に古いパッケージ情報をキャッシュしたままだと、更新できないケースがありますので、それを避けるために、apt update apt installまとめています。 これにより、パッケージを追加したときにすべて新しいパッケージ情報で、更新されるようになります。
--no-install-recommendsを追加する
必須ではないパッケージが入ってくることを防いでいます。 https://manpages.debian.org/unstable/apt/apt-get.8.ja.html#%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3
キャッシュを削除する
&& apt-get clean \ && rm -rf /var/lib/apt/lists/*
aptキャッシュをクリーンにし、/var/lib/apt/lits を削除することで、イメージのサイズを減らしています。
Dockerfile のベストプラクティス — Docker-docs-ja 1.9.0b ドキュメント
docker-composeに設定した値をDockerfileに寄せる
今回はDockerイメージ単体でも動作するようにしたいので、ソースコード・設定ファイルをDockerfileに移動させます。
COPY ./src /var/www/html COPY ./config/OpenPNE.yml ./config/ProjectConfiguration.class.php /var/www/html/config/ COPY ./config/apache2.conf /etc/apache2/apache2.conf COPY ./config/php.ini /usr/local/etc/php/php.ini
コピーする命令はCOPYの他にADDがありますが、ADDはローカル上でのtar展開や、リモートURLのサポートなど多くの機能を持っていて混乱の元なので、明確にCOPY以外の機能を使いたいとき以外は使わないようにしています。
こちらでもそう言っていますね。
Dockerfile のベストプラクティス — Docker-docs-ja 1.9.0b ドキュメント
その他、細かな設定を追加
WORKDIR,、EXPOSE、タイムゾーン、言語設定を追加します。
ENV TZ=Asia/Tokyo ENV LANG=ja_JP.UTF-8 WORKDIR /var/www/html EXPOSE 80
また、先に、DBが起動して欲しいので、depends_on
を追加します。
depends_on: - db
3.ちょっとだけ、セキュアにする
今回はローカル環境でしか動かさないのですが、もし今後公開した環境で稼働させたくなったときにスムーズに移行できるよう、今回少しだけセキュリティ設定はしておきます。
アプリケーションのパーミッションに設定する
ソースコードを置いているディレクトリは最低限必要な権限のみを付与するようにします。
RUN chown -R www-data:www-data /var/www/html \ && chmod -R 400 /var/www/html \ && find /var/www/html -type d | xargs -I{} chmod 500 {} \ && chmod 700 /var/www/html/log /var/www/html/cache
ReadOnlyで起動する
docker-compose.ymlに read_only: true
を追加します。
書き込みが必要なディレクトリは次のように匿名ボリュームをマウントすることで書き込めるようにします。
read_only: true volumes: - /var/www/html/log - /var/run/apache2/ - /tmp
こちらに関しては先日記事を書きましたので良かったらご覧ください
dockerignoreを設定する
イメージに含める必要のないファイルは.dockerignoreで除外しました。
4. 最終チェック
--no-cache
オプションを利用して念の為キャッシュを使わず最初からビルドをし直して問題ないか確認します。
COMPOSE_DOCKER_CLI_BUILD=1 docker-compose build --no-cache
※COMPOSE_DOCKER_CLI_BUILD=1を設定することで、BuildKitを使って高速にビルドする様にしています。BuildKitについてはこちら
いろいろイジってみる
どうやら、インストール直後はプロフィールとコミュニティ機能くらいしか無いようなのですが、プラグインを追加することで、タイムラインや日記、あしあと機能などを追加ができるようです。
各プラグインはこのページの各プラグインをクリックしたあとに表示されるコマンドを入力することで追加できるようです。
プラグインを追加していった結果こんな見た目になりました!! なんかすごく大学生の頃を思い出すような懐かしい見た目になった!!!
本家(UIの参考になったであろうサービス)の方もたまにはログインしてみようと思います。
まとめ
今回は、OpenPNEを使ったローカル環境を構築してみましたが、業務で使用している開発環境も大体同じような流れで構築をしています。もしこうすると良いよ!などアドバイスなどがあればコメントいただけると嬉しいです。
Twitterやっているので、良かったらマイミク申請フォローお願いします。
明日9日目は、mochikoさんです〜
書き込みが必要なDockerコンテナをReadOnlyで動かせるようにする
コンテナを触っている人であれば当たり前っちゃ当たり前のテクニックな感じだと思いますが、最近k8s環境でDockerコンテナを読み取り専用で動かすことができたので、メモしておきます。
Dockerコンテナを安全に運用するための手段の一つとして、コンテナを読み取り専用(ReadOnly)にすることがあります。 しかし、ログやキャッシュディレクトリなどアプリケーションの仕組み上、どうしても書き込みが必要な場合があり、そのままでは読み取り専用で動作させることはできません。
例えば、nginxの公式イメージだと起動時にこうなります。
docker run --rm --read-only nginx:1.18.0 /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/ /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh 10-listen-on-ipv6-by-default.sh: error: can not modify /etc/nginx/conf.d/default.conf (read-only file system?) /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh /docker-entrypoint.sh: Configuration complete; ready for start up 2020/11/23 XX:XX:XX [emerg] 1#1: mkdir() "/var/cache/nginx/client_temp" failed (30: Read-only file system) nginx: [emerg] mkdir() "/var/cache/nginx/client_temp" failed (30: Read-only file system)
※ --read-only
をつけると読み取り専用で動作させることができます。
これを解決するために、書き込み可能なボリュームをマウントしてあげることで回避することができました。
以下、設定例です。
- dockerコマンドの場合
docker run --rm \ -v /var/cache/nginx \ -v /var/run \ -v /etc/nginx/conf.d \ --read-only \ -p 80:80 \ nginx:1.18.0
- docker-composeの場合
設定箇所のみピックアップして載せています
services: nginx: image: nginx:1.18.0 read_only: true volumes: - /var/cache/nginx - /var/run - /etc/nginx/conf.d
- Kubernetes(Deployment)の場合
設定箇所のみピックアップして載せています
template: spec: containers: - name: nginx image: nginx:1.18.0 securityContext: readOnlyRootFilesystem: true volumeMounts: - name: nginx-cache mountPath: /var/cache/nginx - name: pid-dir mountPath: /var/run - name: nginx-conf mountPath: /etc/nginx/conf.d volumes: - name: nginx-cache emptyDir: {} - name: pid-dir emptyDir: {} - name: nginx-conf emptyDir: {}
今回の例では匿名ボリューム、emptyDirを設定しましたが必要に応じて、適切なボリュームを設定してあげてください。
もっと良い方法があれば教えてください。
コンテナイメージの脆弱性スキャンツールTrivyを使ってみる 〜とりあえず試す編〜
Docker Bench for Securityに引き続き、コンテナイメージの脆弱性スキャンツールTrivyを使ってみます。
Docker Bench for Securityについてのエントリーはこちら takapi86.hatenablog.com
Trivyとは何か
Docker Bench for Securityがコンテナイメージの作成方法や動作環境が基準に沿ったセキュアな環境かどうか診断するのに対し、Trivyはイメージに含まれる具体的な脆弱性情報を検知してくれるツールです。
対象はyum/rpm, apt/apt-get/dpkg, apkやGemfile.lockやcomposer.lockなど、アプリケーションのライブラリの依存関係から脆弱性情報を検知できます。
https://github.com/aquasecurity/trivy#os-packages https://github.com/aquasecurity/trivy#application-dependencies
また、リポジトリのAboutにもある通り、
A Simple and Comprehensive Vulnerability Scanner for Containers, Suitable for CI
と、CIに組み込みやすい特徴があるようです。
公式リポジトリ
https://github.com/aquasecurity/trivy
今回はお試しということで、CIには組み込まず、コマンドで実行し脆弱性情報を確認するところまでやっていきます。
インストールする
様々なパッケージ管理システムを使ってインストールできるようです。 https://github.com/aquasecurity/trivy#installation
実行する
- 直接コマンドで実行する場合
trivy image [your_image]
- コンテナイメージを使う場合
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v [YOUR_CACHE_DIR]:/root/.cache/ aquasec/trivy [YOUR_IMAGE_NAME]
試す
2年ほど放置していて稼働していないPrivateリポジトリにあるRailsアプリケーションのイメージで試してみます。
trivy image rails_app
このようにたんまり脆弱性情報が出てしまいました 🥺
usr/src/app/Gemfile.lock ======================== Total: 22 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 9, CRITICAL: 2) +----------------------+------------------+----------+-------------------+--------------------------------+--------------------------------+ | LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE | +----------------------+------------------+----------+-------------------+--------------------------------+--------------------------------+ | XXXXXXXXXX | XXXXXXXXXX | HIGH | XXXXX | XXXXXXXXXX | XXXXXXXXXX | | | | | | | XXXXXXXXXX | + +------------------+----------+ + +--------------------------------+ | | XXXXXXXXXX | MEDIUM | | | XXXXXXXXXX | | | | | | | XXXXXXXXXX | | | | | | | XXXXXXXXXX | +----------------------+------------------+ + +--------------------------------+--------------------------------+ (この下にもばーっと脆弱性情報が出ています)
GitHub Actionsに組み込んで試す
続いて、CIで検知できるようGitHub Actionsに組み込んでみようと思います。 こちらはCI導入編ということで別エントリーであげようと思います。
今回はここまで
Docker Bench for Securityを使ってみる 〜とりあえず試す編〜
これは何?
CIS Docker BenchmarksというDockerのセキュリティ基準に沿って、自動診断してくれるツール
診断項目はこんな感じ
- Host Configuration
- Docker daemon configuration
- Docker daemon configuration files
- Container Images and Build File
- Container Runtime
- Docker Security Operations
- Docker Swarm Configuration
公式リポジトリはこちら https://github.com/docker/docker-bench-security
お試し環境の構築
実際使用する際には、本番環境もしくは、本番に近い環境で診断することが望ましいと思われるのですが、今回はVagrant環境を使ってお試し環境を作りました。
https://github.com/takapi86/docker-bench-security-test
実行までの流れ
VMを起動
vagrant up
VMの中に入る
vagrant ssh
dockerを起動
sudo systemctl start docker
docker-bench-securityをpull
docker pull docker/docker-bench-security
診断したいコンテナをあげる
docker run --rm -d nginx
今回は、nginxを例としていますが、診断したいコンテナを起動してください。
診断実行
docker run --rm --net host --pid host --userns host --cap-add audit_control \ -e DOCKER_CONTENT_TRUST=1 \ -v /etc:/etc:ro \ -v /usr/lib/systemd:/usr/lib/systemd:ro \ -v /var/lib:/var/lib:ro \ -v /var/run/docker.sock:/var/run/docker.sock:ro \ --label docker_bench_security \ docker/docker-bench-security
診断結果を見る
項目が結構あるので、今回は Container Images and Build File
を見てみる
[INFO] 4 - Container Images and Build File [WARN] 4.1 - Ensure a user for the container has been created [WARN] * Running as root: serene_yonath [NOTE] 4.2 - Ensure that containers use trusted base images [NOTE] 4.3 - Ensure unnecessary packages are not installed in the container [NOTE] 4.4 - Ensure images are scanned and rebuilt to include security patches [PASS] 4.5 - Ensure Content trust for Docker is Enabled [WARN] 4.6 - Ensure HEALTHCHECK instructions have been added to the container image [WARN] * No Healthcheck found: [docker.io/nginx:latest] [PASS] 4.7 - Ensure update instructions are not use alone in the Dockerfile [NOTE] 4.8 - Ensure setuid and setgid permissions are removed in the images [INFO] 4.9 - Ensure COPY is used instead of ADD in Dockerfile [INFO] * ADD in image history: [docker.io/nginx:latest] [INFO] * ADD in image history: [docker.io/docker/docker-bench-security:latest] [NOTE] 4.10 - Ensure secrets are not stored in Dockerfiles [NOTE] 4.11 - Ensure verified packages are only Installed
WARNINGとして、コンテナがrootで実行されていることと、HEALTHCHECKがコンテナイメージにないことが指摘されている。
ガイドラインと照らし合わせてチェックしていくと大変なので、便利そう。