はじめに
上の記事を読んでいて(実はしっかり読んでないけど)、疑問に思ったことがあります。ファイヤーウォール以前に、
そもそも本番サーバーでデータベースをホストから使える設定にしてるのがおかしくない?
ってことです。
docker hubの公式イメージだと...
例えばpostgresの公式イメージでは
version: '3.1'
services:
db:
image: postgres
restart: always
environment:
POSTGRES_PASSWORD: example
こんな感じのdocker-compose.ymlが公開されています。ポートの公開してませんよね?
公式にあるyamlにサービスを追加する
dockerのデフォルトのネットワークはbridgeという名のnat(IPマスカレードがnatです)なので、公開しない限りホストからもアクセスできませんが、bridgeなので他のサービスからは普通に開いています。例えば上のdocker-compose.ymlをこちらの記事を参考に編集して以下のように変えたとしましょう。
version: '3.7'
services:
db:
image: postgres
restart: always
volumes:
- ./data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: example
app:
image: node:lts
working_dir: /home/app
tty: true
ports:
- '3000:3000'
volumes:
- ./:/home/app
depends_on:
- db
command: >
bash -c "npm install &&
npm install nodemon -g &&
nodemon index.js"
これもnode用のポート3000しか公開しておらず、postgresは公開していません。
細かいところを言うと少しだけいじっています。
- 不要ボリュームを消すのが面倒なのでpostgres側volume設定を追加
- app側にdepends_onを追加して、初回起動時コケにくくした
アプリを適当に用意する
先の記事を丸パクリ参考にして作成します。
$ docker compose run --rm app bash -c 'npm init -y;npm install express pg'
$ docker compose down
'use strict';
const express = require('express');
const PORT = 3000;
const HOST = '0.0.0.0';
const app = express();
const {Pool} = require('pg');
const pool = new Pool({
user: 'postgres',
host: 'db',
database: 'postgres',
password: 'example',
port: 5432,
});
app.get('/', async(req, res) => {
const {rows} = await pool.query('select version()');
res.send(rows);
});
app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);
postgresに接続してバージョンだけ表示するアプリです。
確認してみる
まずは起動してみます。
$ docker compose up -d
(初回はdatabase作成などの都合によりnodeの方が先に起動してしまうと失敗します)
次にホスト(dockerの外)からアクセスしてみます。
$ wget -O - 'http://localhost:3000' | cat
--2023-12-23 22:49:41-- http://localhost:3000/
localhost (localhost) をDNSに問いあわせています... ::1, 127.0.0.1
localhost (localhost)|::1|:3000 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 131 [application/json]
‘STDOUT’ に保存中
- 100%[====================================================================================================>] 131 --.-KB/s in 0s
[{"version":"PostgreSQL 16.1 (Debian 16.1-1.pgdg120+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit"}]
2023-12-23 22:49:41 (21.3 MB/s) - stdout へ出力完了 [131/131]
$
後ろにパイプを噛ませているのはapplication/json
というContent-Type
が指定されているからです。ないとwgetがバイナリと判断して端末上に出力してくれません。普通にブラウザで見ても大丈夫です。
見れば分かるとおり、ちゃんとpostgresのバージョン情報をnodeアプリ側で表示出来ています。
結論
postgresのポート設定は公開しなくていいのです。
ポートの公開はあくまで開発時ツールなどを使ってホストから直接見たいときに行うサポート的なものなんです。なので…
本番サーバーで不要なポートをdockerコンテナの外に公開しちゃいけません
dockerがiptablesをいじることを知らなくても初心者ならそこまで怒られないかもしれませんが、不要なポートを公開してればあなたの評価は確実に下がると思います。逆に言えば不要なポートの公開さえしていなければ、何も問題など起きなかったわけです。