UFW がサーバーを守っていると思っていた。実際は違った。
ネットワーク設定やファイアウォールにおける、ほんの小さな思い込みが、どのように内部サービスを意図せずインターネットへ公開してしまうのか――Docker、UFW、そして本番環境に潜む* ***false sense of security(「守られているはず」という思い込み)*** *について、実体験をもとにまとめた記事です。
すべて正常に見えていた
私はすでに UFW を有効化していました。
ufw status でも対象ポートは DENY になっていました。
ログにも特に異常はありません。
デプロイも正常。
監視画面もすべて正常状態。
すべて問題なく動いているように見えていました。
しかしある日、別の外部ネットワークから確認してみたところ、本番環境上の 内部サービス がインターネットからアクセス可能な状態になっていることに気付きました。
一番恐ろしかったのは、
- その問題が発生していることを示す兆候が何もなかったことです。
- アラートもない。
- エラーもない。
- デプロイ失敗もない。
ただ単に、
- 「システムは守られている」と思い込んでいた
- しかし実際には守られていなかった
それだけでした。
その瞬間、私はある重要なことに気付きました。
インフラ運用において、本当に危険なのは bug そのものではなく、
時に false sense of security なのだと。
内部サービスはそもそも外部公開を前提としていない
現代のシステムでは、多くの内部サービスが利用されています。
例えば:
- PostgreSQL
- Redis
- Elasticsearch
- Kafka
- RabbitMQ
- 管理用 API
- 監視サービス
これらのサービスは通常、
- バックエンドアプリケーション
- バッチ処理
- 社内ネットワーク
- VPN や SSH トンネル経由の開発者
のみが利用する前提で設計されています。
そもそも、インターネットへ公開することを想定していません。
しかし実際には、多くの本番環境で意図せず外部公開されてしまっています。
例えば:
ports:
- "9200:9200"
この設定は一見すると問題なさそうに見えます。
しかし多くの場合、これはサービスが次のアドレスで待ち受けていることを意味します。
0.0.0.0
つまり:
- すべてのネットワークインターフェース
- サーバーの公開インターフェースを含む
に対して接続を受け付ける状態です。
もしサーバーに グローバル IP アドレス が割り当てられていれば、そのサービスはインターネットから到達可能になっている可能性があります。
そして最も危険なのは、
多くの開発者がその事実に気付いていないことです。
「でも UFW で DENY していたのに?」
これは、まさに私自身が思っていたことでした。
私は、
ufw deny 9200
これだけで Elasticsearch は保護されていると思っていました。
見た目上は問題ありません。
- UFW は有効
- ルールも存在
ufw statusも DENY 表示
しかし外部ネットワークから確認すると:
curl http://public-ip:9200
サービスは普通に応答を返していました。
そこで初めて理解したのです。
Docker は単純に「ポートを開く」だけではありません。
コンテナのポートを公開する際、Docker は自動的に iptables を操作し、通信をコンテナへ転送します。
その結果、Docker が公開したポート は場合によって、開発者が UFW に期待している挙動を迂回してしまうことがあります。
この経験から、私は大きな教訓を得ました。
ファイアウォールのルールがあるからといって、安全性が保証されるわけではありません。
本当に重要なのは:
外部から実際にアクセス可能かどうか。
ということです。

インターネットは決して「静か」ではない
多くの開発者が過小評価しがちなのが、
インターネットでは常に誰かがシステムをスキャンしているという事実です。
あなたが有名だからではありません。
誰かが特別に狙っているわけでもありません。
現在のインターネットスキャンは、ほぼ完全に自動化されているからです。
無数の:
- スキャナー
- クローラー
- 自動攻撃ボット
- 大規模スキャンツール
が、常に以下をスキャンしています。
- Redis
- Elasticsearch
- PostgreSQL
- MongoDB
- 管理画面
- 内部 API
もしサービスが適切な保護なしにインターネットへ公開されていれば、発見されるのは時間の問題です。

本当に危険なのは「公開されていること」だけではない
本当に怖いのは、サービスが公開されていることそのものではありません。
むしろ:
- 誰も公開されていることに気付いていない
- ファイアウォールが守っていると思い込んでいる
ことです。
だからこそ、多くの障害やインシデントは:
- データが消える
- サーバーが悪用される
- インフラコストが急増する
まで発覚しません。
実際に:
- Elasticsearch の index が削除された
- Redis のデータが flush された
- データベースに ransom note が残された
- 内部サービスが暗号資産マイニングに悪用された
といった事例は数多く存在します。
そして、それらの多くは:
- zero-day exploit
- 高度な malware
- sophisticated attacker
から始まったわけではありません。
始まりは:
- デフォルト設定
- 開発時の利便性
- 間違った思い込み
- 検証不足
でした。
“Configuration Looks Safe” は “Actually Safe” を意味しない
これは今回の経験で得た最大の教訓です。
本番環境では:
「設定上は安全に見えること」
は、
「実際に安全であること」
を意味しません。
たとえ:
- ファイアウォールがある
- 拒否ルールがある
- 監視がある
- 監視画面が正常状態
- インフラ構成図が綺麗
だったとしても、
外部から通信できる状態であれば、それらの思い込みはすべて意味を失います。
セキュリティは:
- 監視画面
- 設定内容
- 思い込み
だけで判断するべきではありません。
必要なのは:
- 外部ネットワークからの検証
- 実際の接続確認
- 多層防御
です。
私が変えたこと
この経験以降、私はインフラ設計の考え方を見直しました。
1. 内部サービスを 127.0.0.1 に bind する
以前は:
ports:
- "9200:9200"
としていましたが、
現在は:
ports:
- "127.0.0.1:9200:9200"
を使っています。
これにより、サービスは localhost からのみアクセス可能になります。
2. 必要がない限り Internal Port を公開しない
多くの内部サービスはインターネットアクセスを必要としません。
コンテナ間通信だけであれば:
- Docker internal network
- private subnet
- service discovery
だけで十分なケースがほとんどです。
3. Host Firewall だけに依存しない
UFW は便利です。
しかし、それだけに依存すべきではありません。
現在は:
- クラウド側のセキュリティ設定
- private networking
- VPN
- zero-trust access
- network segmentation
を優先しています。
多層防御 は、単一レイヤーの防御よりも常に強力です。
4. 必ず外部ネットワークから確認する
これが最も重要な習慣になりました。
確認するべきなのは:
curl localhost:9200
ではなく、
- モバイル回線
- 外部 VPS
- 実際のインターネット接続
です。
外部からアクセスできないことを確認して、初めて安心できます。

最大の教訓
今回学んだ最大のことは、
「Docker のネットワーク設定は複雑である」
ことではありません。
本当の教訓は:
インフラ運用における「思い込み」は非常に危険である。
ということでした。
多くの本番障害やセキュリティ事故は catastrophic mistake から始まりません。
小さな思い込みを誰も検証しなかったことから始まります。
そして時に、本当に危険なのは:
- サービスが公開されていること
ではなく、
- 「公開されていない」と信じ込んでいること
なのです。
最後に
この経験以降、私はインフラの見方が変わりました。
以前は:
「設定は正しくできているだろうか?」
と考えていました。
しかし今は:
「もし実際にインターネット側からアクセスされたら、どうなるだろうか?」
と考えるようになりました。
これは:
- 「設定上は安全に見えること」
- 「実際に安全であること」
の大きな違いです。
そして時に、
ports:
- "9200:9200"
のような、たった一つの小さな思い込みが、
内部サービスを誰にも気付かれないまま外部公開状態にしてしまうことがあるのです。
