Dockerのコンテナのポートの公開には注意する
リバースプロキシをホスト側、バックエンドをdockerで運用している場合、外部からdockerが公開しているポートに直接アクセスできていないかを確認しました。
サーバーの構成は以下になります。
サーバーはubuntu18.04、リバースプロキシにはnginxを利用しバックエンドはdockerでWordPressを稼働させ8000番ポートに公開します。
ファイアーウォールの設定にはufwを利用し80番ポートは許可し、8000番ポートは許可しないようにします。
検証用の環境構築
それでは検証用の環境を構築していきます。
ファイアーウォールの設定
まずはufwでファイアーウォールの設定を行います。
sudo ufw enable
sudo ufw default deny
sudo ufw limit "OpenSSH"
sudo ufw allow "Nginx Full"
sudo ufw reload
上記の設定はデフォルトのポリシーを拒否に設定、HTTPサーバーが稼働する80番ポートと443番ポート、およびSSHサーバーが稼働する22番ポートのみを許可しています。
バックエンドの構築
続いてバックエンドの構築のためdockerでWordPressを稼働させます。Quickstart: Compose and WordPressにあるdocker-compose.ymlを利用してコンテナを作成して起動します。
~/my_wordpress/
にdocker-compose.yml
を作成してQuickstartの通り以下のコマンドを実行します。
cd ~/my_wordpress
docker-compose up -d
リバースプロキシの設定
最後にnginxの設定を行います。まず/etc/nginx/nginx.conf
に記載してあるinclude /etc/nginx/sites-enabled/*;
をコメントアウトしました。
sudo sed -i -r 's/^\s+include\s+\/etc\/nginx\/sites-enabled\/\*;/#&/g' /etc/nginx/nginx.conf
続けてWordPressへのリバースプロキシの設定を追加します。以下の内容で/etc/nginx/conf.d/wordpress.conf
を作成しました。(今回はリバースプロキシ用のヘッダーの設定は省略しています)
server {
listen 80;
location / { proxy_pass http://127.0.0.1:8000/; }
}
念のため設定ファイルの構文チェックを行います。
sudo nginx -t
構文チェックで問題がなければ、設定ファイルの再読み込みを行います。
sudo service nginx reload
ここまでの内容をVagrantfile
にしてみました。
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
config.vm.box = "bento/ubuntu-18.04"
config.vm.network "public_network", ip: "192.168.0.111"
config.vm.provision :shell, inline: <<-'SHELL'
# install nginx, ufw
apt-get update
apt-get install -y nginx ufw
# install docker
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
# install docker-compose
curl -L \
"https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" \
-o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
gpasswd -a vagrant docker
# ufw setting
ufw default deny
ufw limit "OpenSSH"
ufw allow "Nginx Full"
yes | ufw enable
# nginx setting (omitted proxy headers)
sed -i -r 's/^\s+include\s+\/etc\/nginx\/sites-enabled\/\*;/#&/g' /etc/nginx/nginx.conf
cat << 'EOF' > /etc/nginx/conf.d/wordpress.conf
server {
listen 80;
location / { proxy_pass http://127.0.0.1:8000/; }
}
EOF
service nginx reload
# build docker WordPress container
mkdir ~/my_wordpress
cd ~/my_wordpress && cat << 'EOF' > docker-compose.yml && docker-compose up -d
version: "3.9"
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
volumes:
- wordpress_data:/var/www/html
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
db_data: {}
wordpress_data: {}
EOF
SHELL
end
もし利用される場合は以下の仮想マシンのネットワークの設定を自分の環境のネットワークアドレスに合わせて書き換えてください。
config.vm.network "public_network", ip: "192.168.0.111"
外部から疎通の確認
検証用の環境のIPアドレスは192.168.0.111
にしました。自分でも検証用の環境を構築された場合は自分の環境のIPアドレスに読み替えてください。
まずは80番ポートへの疎通を確認します。こちらへのアクセスはリバースプロキシによってWordPressにプロキシされます。初回アクセスのためWordPressの初期設定画面に遷移する想定です。
curl -X GET -I -L http://192.168.0.111
結果は想定通りWordPressの初期設定画面に遷移していることが確認できました。次は8000番ポートへ疎通できるかの確認を行います。8000番ポートはファイアーウォールで許可していないはずなので外部から直接アクセスできない想定です。
curl -X GET -I -L http://192.168.0.111:8000
結果は想定と異なり80番ポートと同じくWordPressの初期設定画面に遷移しました。外部から直接アクセスできてしまう状態になっていることが確認できました。
コンテナのポートの公開を変更して確認
dockerはデフォルトでコンテナのポートをIPアドレス0.0.0.0
に公開します。docker-compose.yml
のポートの指定を8000:80
からループバックアドレスに対して公開するよう127.0.0.1:8000:80
に変更しました。
cd ~/my_wordpress
sed -i -r 's/"8000:80"/"127.0.0.1:8000:80"/' docker-compose.yml
docker-compose down && docker-compose up -d
変更後に80番と8000番のポートに対して再度疎通の確認を行い、8000番のポートに外部から直接アクセスできないことが確認できました。
まとめ
dockerはデフォルトでコンテナのポートを0.0.0.0
に対して公開します。これはホストマシン上のすべてのインターフェースを意味していることに注意が必要です。
あまりないケースかとは思いますが、例えばデータベースをdockerで構築しポートを公開している場合で外部からの直接アクセスを想定していないにも関わらず直接アクセスできてしまっている場合はかなり危険です。