dockerのポート設定は必要なものだけにする

## はじめに

https://qiita.com/isso_719/items/c22e617986c821b2f624

上の記事を読んでいて(実はしっかり読んでないけど)、疑問に思ったことがあります。ファイヤーウォール以前に、

**そもそも本番サーバーでデータベースをホストから使える設定にしてるのがおかしくない？**
ってことです。

## docker hubの公式イメージだと...
例えば[postgresの公式イメージ](https://hub.docker.com/_/postgres)では

```yaml:docker-compose.yml
version: '3.1'
services:
  db:
    image: postgres
    restart: always
    environment:
      POSTGRES_PASSWORD: example
```

こんな感じのdocker-compose.ymlが公開されています。ポートの公開してませんよね？

## 公式にあるyamlにサービスを追加する

dockerのデフォルトのネットワークは[bridgeという名のnat](https://docs.docker.com/network/drivers/bridge/)(IPマスカレードがnatです)なので、公開しない限りホストからもアクセスできませんが、bridgeなので他のサービスからは普通に開いています。例えば上のdocker-compose.ymlを[こちらの記事](https://zenn.dev/takeo/articles/8c06f2420c328c)を参考に編集して以下のように変えたとしましょう。

```yaml: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を追加して、初回起動時コケにくくした

## アプリを適当に用意する

先の記事を~~丸パクリ~~参考にして作成します。

```bash
$ docker compose run --rm app bash -c 'npm init -y;npm install express pg'
$ docker compose down
```

```JavaScript:index.js
'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に接続してバージョンだけ表示するアプリです。

## 確認してみる

まずは起動してみます。
```bash
$ docker compose up -d
```
(初回はdatabase作成などの都合によりnodeの方が先に起動してしまうと失敗します)

次にホスト(dockerの外)からアクセスしてみます。

```bash
$ 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をいじること](https://docs.docker.com/network/packet-filtering-firewalls/)を知らなくても初心者ならそこまで怒られないかもしれませんが、不要なポートを公開してればあなたの評価は確実に下がると思います。逆に言えば不要なポートの公開さえしていなければ、何も問題など起きなかったわけです。