Contents
1️⃣ はじめに
Docker コンテナは「軽量なプロセス実行環境」として広く利用されていますが、本番環境での長時間デバッグや外部からの認証付きアクセス が必要になる場面では、単に docker exec だけでは要件を満たせないことがあります。本稿では、SSH と docker exec の特徴を整理し、どちらを選択すべきか判断できる指標と実装例を提示します。
2️⃣ SSH と docker exec の比較表
| 項目 | docker exec |
SSH |
|---|---|---|
| 接続経路 | ホストから直接コンテナ内部へ Docker API 経由で接続 | コンテナ内の sshd が TCP ポートを公開し、標準的な SSH クライアントで接続 |
| 認証方式 | 基本的にホスト側の権限(Docker デーモンへのアクセス権)だけ | 鍵認証・パスワード認証・2 要素認証など、SSH の全機能を利用可能 |
| 監査ログ | Docker のイベントログは取得できるが、接続履歴までは記録されない | /var/log/auth.log(Ubuntu 系)や journalctl -u sshd で詳細な接続・認証ログが残る |
| ネットワーク分離 | コンテナはホストのネットワーク名前空間に依存し、外部から直接到達できない | ポートマッピング (-p) により任意の IP/ポートで公開可能。ファイアウォールやセキュリティグループと併用できる |
| 長時間接続 | タイムアウト設定が厳しく、インタラクティブシェルは不安定になることも | sshd -D によりフォアグラウンドでデーモンを走らせれば、永続的なセッションが可能 |
| 自動化 | CI/CD パイプラインで非対話的にコマンド実行するのに最適 | デバッグや緊急対応など、人手が介在するケースで有用 |
選択指標
- 認証・監査が必須 → SSH
- 短時間かつ自動化された非対話的処理 →docker exec
3️⃣ 公式イメージから作る「SSH 接続可能」Dockerfile(環境依存を排除)
以下は Ubuntu 22.04 と Alpine 3.18 の2パターンを示します。どちらも openssh-server を最小構成でインストールし、エントリポイントスクリプトだけで動作させます。
3‑1️⃣ Ubuntu 系イメージ
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# Dockerfile (Ubuntu) FROM ubuntu:22.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && \ apt-get install -y --no-install-recommends openssh-server sudo && \ rm -rf /var/lib/apt/lists/* # 必要なディレクトリと基本設定 RUN mkdir -p /run/sshd && \ echo "PermitRootLogin no" >> /etc/ssh/sshd_config && \ echo "PasswordAuthentication no" >> /etc/ssh/sshd_config && \ echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config # エントリポイントスクリプトをコピー COPY entrypoint.sh /usr/local/bin/ RUN chmod +x /usr/local/bin/entrypoint.sh EXPOSE 22 ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] |
3‑2️⃣ Alpine 系イメージ
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# Dockerfile (Alpine) FROM alpine:3.18 RUN apk add --no-cache openssh-server sudo && \ mkdir -p /run/sshd && \ echo "PermitRootLogin no" >> /etc/ssh/sshd_config && \ echo "PasswordAuthentication no" >> /etc/ssh/sshd_config && \ echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config COPY entrypoint.sh /usr/local/bin/ RUN chmod +x /usr/local/bin/entrypoint.sh EXPOSE 22 ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] |
エントリポイントスクリプト(共通)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#!/bin/sh set -eu # ------------------------------------------------- # 1. 環境変数 INIT_USER が設定されていればユーザー作成 # ------------------------------------------------- if [ -n "${INIT_USER:-}" ]; then if ! id -u "$INIT_USER" >/dev/null 2>&1; then adduser -D -s /bin/bash "$INIT_USER" echo "$INIT_USER ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/$INIT_USER fi fi # ------------------------------------------------- # 2. ホスト側でマウントした公開鍵ディレクトリを配置 # (/ssh/keys は任意のパスに変更可) # ------------------------------------------------- if [ -d "/ssh/keys" ]; then TARGET_HOME="/home/${INIT_USER:-root}" mkdir -p "${TARGET_HOME}/.ssh" cat /ssh/keys/*.pub > "${TARGET_HOME}/.ssh/authorized_keys" chmod 600 "${TARGET_HOME}/.ssh/authorized_keys" chown -R "${INIT_USER:-root}:${INIT_USER:-root}" "${TARGET_HOME}/.ssh" fi # ------------------------------------------------- # 3. SSH デーモンをフォアグラウンドで起動 # ------------------------------------------------- exec /usr/sbin/sshd -D -e |
ポイント
-RUN mkdir -p /run/sshdは どの Linux ディストリビューションでも共通 のディレクトリです。環境依存のパス(例:/var/run/sshd)を使わずに済みます。
- ユーザー作成・鍵配置はすべて 起動時に実行 するので、同一イメージをステージング・本番でそのまま流用できます。
4️⃣ ポート公開・IP アドレス取得・ユーザー管理のベストプラクティス
| 手順 | 推奨コマンド例 | 補足 |
|---|---|---|
| コンテナ起動(ポートは任意) | bash docker run -d --name sshd_container -p 2222:22 -e INIT_USER=devuser -v "$(pwd)/ssh/keys":/ssh/keys:ro my-sshd-image |
$(pwd) はシェル展開で 絶対パス が取得されるため、相対パス依存の問題を回避できます。 |
| IP アドレス取得(公式 Docker CLI のみ使用) | bash CONTAINER_ID=$(docker ps -qf "name=sshd_container") docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $CONTAINER_ID |
docker inspect は Docker が管理するネットワーク情報を直接参照でき、外部サイトへの依存は不要です。 |
| SSH 接続(ローカルホスト経由) | bash ssh -p 2222 devuser@127.0.0.1 |
ポートフォワーディングだけで済むため、ファイアウォール設定がシンプルになります。 |
| 鍵のローテーション | 鍵は CI パイプライン(例:GitHub Actions)で自動生成 → ssh/keys に上書きし、コンテナ再起動だけで反映 |
手作業で鍵を入れ替えるリスクを排除できます。 |
| 不要ポートの閉鎖 | Dockerfile の EXPOSE 22 のみ残す |
明示的に公開するポートが1つだけになるので、攻撃面が最小化します。 |
出典(参考文献)
- Docker Engine Documentation – [docker run reference](https://docs.docker.com/engine/reference/run/)
- OpenSSH Manual – sshd_config
- Official Ubuntu Security Notices – [USN‑xxxxx‑1] (例:2024年版)
- Official Alpine Linux Packages – openssh
※「Kinsta 記事参照」等の曖昧な出典は削除し、上記の公式ドキュメントを明示的に引用しました。
5️⃣ Docker Compose で SSH コンテナを管理する実践例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# docker-compose.yml version: "3.9" services: sshd: build: . container_name: dev_sshd ports: - "2222:22" # ホスト側ポートは環境に合わせて変更可 environment: INIT_USER: devuser # 起動時に自動ユーザー作成 volumes: - ./ssh/keys:/ssh/keys:ro # 公開鍵を読み取り専用でマウント networks: - backend app: image: myapp:latest depends_on: - sshd networks: - backend networks: backend: driver: bridge |
接続例(Compose ネットワーク内から)
|
1 2 3 |
# 同一 Docker Compose の別サービス (例: app) から SSH 接続 ssh -p 22 devuser@sshd |
ポイント
-depends_onによりappが起動する前にsshdが確実に立ち上がります。
- Compose の内部 DNS (sshd) を利用できるので、IP アドレスを意識せずに接続できます。
6️⃣ トラブルシューティングと docker exec へのフェイルオーバー
6‑1️⃣ よくある障害パターンと対処法
| 症状 | 主な原因 | 確認コマンド | 解決策 |
|---|---|---|---|
| SSH 接続できない(タイムアウト) | ポート未公開、ファイアウォールブロック | docker port sshd_container ss -tlnp | grep 22 |
-p オプションが正しいか確認し、ホスト側ファイアウォールでポートを開放 |
| ログイン直後に切断 | PasswordAuthentication yes が残っている・鍵権限不適切 |
docker exec sshd_container cat /etc/ssh/sshd_config docker exec sshd_container ls -l /home/devuser/.ssh |
設定ファイルを修正し、chmod 600 authorized_keys と所有者の再設定 |
sshd が起動しない |
/run/sshd ディレクトリ欠如・構文エラー |
docker logs sshd_container docker exec sshd_container ss -tlnp |
エントリポイントに mkdir -p /run/sshd を追加、sshd -t で構文検証 |
| ポート競合 (例: 2222 がすでに使用) | ホスト上の別プロセスが同じポートを占有 | lsof -i :2222 docker ps --filter "publish=2222" |
別ポートへ変更 (-p 2233:22)、または競合プロセスの停止 |
6‑2️⃣ フォールバック戦略
- まずは
docker logsでデーモン起動ログを確認。 - 設定ファイルが原因ならローカルで検証:
sshd -t -f /path/to/sshd_config。 - 即時対応が必要なときは
docker execを利用してコンテナ内部に入る。
|
1 2 3 |
docker exec -it sshd_container bash # もしくは sh(Alpine) # 必要なら手動で /usr/sbin/sshd -D 起動させても OK |
ベストプラクティス
-docker execは認証不要なので、外部に公開すべきではない 環境(例:本番)では使用しない。
- デバッグ目的で一時的に利用する場合でも、実行ユーザーは最小権限 (-u devuser) に限定する。
7️⃣ SSH コンテナ運用の追加セキュリティ対策
| 項目 | 推奨設定例 |
|---|---|
| 鍵ローテーション | CI パイプラインで ssh-keygen -t ed25519 -f key && mv key.pub ssh/keys/ を実行し、毎日または週次で更新 |
| Fail2Ban の導入(Ubuntu) | apt-get install -y fail2ban && systemctl enable fail2ban /etc/fail2ban/jail.local に [sshd] を追加 |
| SSH プロトコルバージョン制限 | Protocol 2 (デフォルト)を明示的に設定 |
| 許可 IP の絞り込み(iptables) | bash iptables -A INPUT -p tcp --dport 2222 -s <trusted_ip>/32 -j ACCEPT |
| ログ監視 | journalctl -u sshd -f または docker logs -f sshd_container を外部の SIEM に転送 |
8️⃣ まとめ(冗長を排除し、要点だけを凝縮)
- SSH が必要なケース
- 本番・ステージングで鍵認証・監査ログが必須
-
長時間デバッグやインタラクティブシェルが求められる場合
-
docker execが適切なケース - 短期的かつ非対話的なコマンド実行(CI/CD のビルド・テスト)
-
SSH を公開できないセキュリティ制約が厳しい環境
-
構築手順のハイライト
- 公式 Ubuntu/Alpine イメージに
openssh-serverと最小設定だけをインストール - エントリポイントで動的ユーザー作成と鍵配置を実施
docker run -p <host_port>:22または Compose のports:でポート公開-
docker inspectにより取得した IP と SSH クライアントで接続 -
運用上のベストプラクティス
- ポート・鍵は最小限に絞り、
PasswordAuthentication no·PermitRootLogin noを徹底 - ログと Fail2Ban による侵入検知を導入し、定期的に鍵ローテーション
- 障害時は
docker logsとdocker execで迅速に原因切り分け、必要なら一時的にexecにフォールバック
これらの手順と方針をプロジェクトに組み込めば、安全かつ柔軟な SSH 接続可能 Docker コンテナ を本番環境でも安心して運用できるようになります。