踏み台サーバー(Bastion)とSession Manager|本番サーバーへの安全な入り方を設計する

kento_morota 13分で読めます
社内のAI・IT活用、技術がわかる相手に無料相談。 相談 →
目次

本番サーバーの/var/log/auth.logを開いて、見知らぬIPアドレスからのログイン試行が1日に数千件並んでいるのを見て驚いた経験はないでしょうか。SSHポートをインターネットに公開しているサーバーは、公開した瞬間から世界中の自動攻撃の対象になります。鍵認証にしていれば突破はされにくいものの、「そもそも入口を見せない」ほうがはるかに安全です。

この記事では、本番サーバーにSSHポートを直接開けない理由、踏み台サーバー(Bastion)を経由する構成とProxyJumpの設定、AWS Systems Manager Session Manager・GCP IAP・Cloudflare Accessを使ってポート開放をゼロにする方法、操作ログの記録、そして一時的なアクセス権の付与まで解説します。SSHの基本操作と鍵認証の設定はLinuxのSSH接続入門で扱っているので、本記事では「どの経路で入るか」という設計に絞ります。

本番サーバーにSSHポートを直接開けてはいけない理由

攻撃面を最小にするという原則

セキュリティ設計の基本原則に「攻撃面(attack surface)の最小化」があります。攻撃面とは、外部から到達できる入口の総量のことです。SSHの22番ポートをインターネットに公開すると、次のリスクを常に抱えることになります。

  • 総当たり攻撃と脆弱性攻撃の標的になる:鍵認証でパスワード攻撃は防げても、SSHサーバー自体の脆弱性が公表された場合、パッチを当てるまでの間は無防備になる。
  • 鍵の漏洩が即侵入につながる:退職者のPCや盗まれたノートPCに秘密鍵が残っていれば、それだけで本番に入れる。
  • 誰が・いつ・何をしたかが追えない:各サーバーに直接入る運用では、ログが分散し、共有アカウントを使っていると個人を特定できない。
  • 設定ミスがそのまま公開される:セキュリティグループで「22番を0.0.0.0/0に開放」というミスは非常に多く、監査で最初に指摘される項目。

これに対する答えが、「本番サーバーは外から一切到達できないプライベートネットワークに置き、管理者は決められた1つの入口からだけ入る」という設計です。入口を1か所に絞れば、そこを重点的に守り、ログを集約し、必要なときだけ開けることができます。

入口の設計には3つの世代がある

方式開けるポート認証ログ向いている環境
直接SSH(IP制限あり)各サーバーの22番(送信元IP限定)SSH鍵各サーバーに分散固定IPのオフィスから少人数で運用する場合の最低限
踏み台サーバー(Bastion)踏み台の22番のみSSH鍵(+MFA)踏み台に集約可能オンプレ・任意のクラウド、既存構成への追加が容易
Session Manager / IAP / Cloudflare Accessなし(ポート開放ゼロ)クラウドのIAM・IdPクラウド側に自動記録AWS/GCP/クラウド前提の環境、監査要件が厳しい場合

踏み台サーバーの構成とProxyJump

踏み台は「入口専用の小さなサーバー」

踏み台サーバー(Bastion host、Jump hostとも)は、パブリックサブネットに置かれた、SSHの中継だけを役割とする小さなサーバーです。本番のアプリサーバーやDBはプライベートサブネットに置き、セキュリティグループで「踏み台からの22番のみ許可」とします。踏み台自身は、送信元IPをオフィスやVPNに限定し、鍵認証のみ、パスワード認証は無効化します。サブネットとセキュリティグループの構成はAWS VPCのネットワーク設計入門を参照してください。

踏み台の運用で守るべき点は次のとおりです。

  1. 踏み台には中継以外の役割(アプリ、DBクライアント、cron)を載せない。載せるほど攻撃面が増える。
  2. 踏み台に秘密鍵を置かない。踏み台に鍵があれば、踏み台が侵害された時点で全サーバーが危険になる。
  3. OSとSSHサーバーの更新を最優先で当てる。踏み台は数が少ないので自動更新にしてもよい。
  4. 利用者ごとに個別のアカウントと鍵を発行し、共有アカウントを作らない。

ProxyJumpで「鍵を踏み台に置かずに」中継する

2番のルールを守るための仕組みが、OpenSSHのProxyJumpです。手元のPCから踏み台を経由して奥のサーバーへ接続しますが、認証は両方とも手元の鍵で行われ、踏み台には鍵も認証情報も残りません。~/.ssh/configに次のように書いておけば、ssh app1と打つだけで踏み台経由の接続になります。

# ~/.ssh/config
Host bastion
    HostName 203.0.113.10
    User ops-taro
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes

Host app1 app2 db1
    User ops-taro
    IdentityFile ~/.ssh/id_ed25519
    ProxyJump bastion

Host app1
    HostName 10.0.11.21
Host app2
    HostName 10.0.11.22
Host db1
    HostName 10.0.21.5

古い手順書にある「エージェントフォワーディング(ssh -A)」は、踏み台上で自分の鍵が一時的に使える状態を作るため、踏み台が侵害されると鍵が悪用されます。現在はProxyJumpに置き換えるのが標準です。

ポート開放ゼロにする|Session Manager・IAP・Cloudflare Access

AWS Systems Manager Session Manager

Session Managerは、EC2上のエージェント(SSM Agent)がAWS側へアウトバウンドで接続を張り、管理者はAWSのAPI経由でそのセッションに入る仕組みです。サーバー側でインバウンドのポートを一切開ける必要がなく、踏み台サーバーも不要になります。認証と認可はIAMで行うため、「誰がどのサーバーに入れるか」をIAMポリシーで統一的に管理できます。前提は、インスタンスにSSM用のIAMロール(AmazonSSMManagedInstanceCore)が付いていることと、SSMのエンドポイントに到達できること(NAT経由またはVPCエンドポイント)です。

# ブラウザを使わずCLIからシェルを開く(Session Managerプラグインが必要)
aws ssm start-session --target i-0123456789abcdef0

# ポートフォワードでプライベートなDBへローカルから接続する
aws ssm start-session --target i-0123456789abcdef0 \
  --document-name AWS-StartPortForwardingSessionToRemoteHost \
  --parameters '{"host":["prod-db.xxxx.ap-northeast-1.rds.amazonaws.com"],"portNumber":["5432"],"localPortNumber":["15432"]}'

# 既存のsshコマンドをSession Manager経由にする(~/.ssh/config)
Host i-* mi-*
    ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"

IAMポリシーの設計、特に「本番インスタンスにはタグ条件付きで特定のグループだけ」といった絞り込みはAWS IAMのベストプラクティスの考え方をそのまま適用できます。

GCP Identity-Aware Proxy(IAP)とCloudflare Access

GCPではIAPのTCP転送が同じ役割を果たします。gcloud compute ssh --tunnel-through-iapで、外部IPを持たないVMにGoogleアカウントの認証を経て接続でき、ファイアウォールはIAPの送信元範囲(35.235.240.0/20)からの22番だけを許可すれば済みます。

クラウドに依存しない選択肢がCloudflare Accessです。サーバー側でcloudflaredがCloudflareへアウトバウンド接続を張り、管理者はCloudflareのIdP連携(Google Workspace、Microsoft Entra IDなど)で認証してからSSHに到達します。オンプレや複数クラウドが混在する環境でも、入口をひとつのIdPに統一できるのが利点です。この考え方はいわゆるゼロトラストの実践例であり、全体像はゼロトラストセキュリティとはにまとめています。

トラブル事例:Session Managerで「Target is not connected」

症状:プライベートサブネットにEC2を移し、セキュリティグループから22番を削除したうえでSession Managerに切り替えたところ、一部のインスタンスだけ「TargetNotConnected」で接続できなくなった。

原因:接続できないインスタンスはNATゲートウェイのないサブネットにあり、SSM Agentがssm.ap-northeast-1.amazonaws.comなどのエンドポイントにアウトバウンドで到達できていなかった。エージェントはインスタンスにインストールされていたが、登録(マネージドインスタンス化)ができていなかった。

対処ssmssmmessagesec2messagesの3つのVPCエンドポイント(インターフェース型)をそのサブネットに作成し、エンドポイント用セキュリティグループで443番をVPC内から許可した。あわせて、aws ssm describe-instance-informationで全インスタンスが「Online」になっていることをCIで定期確認するようにした。Session Managerは「インバウンドを閉じる」代わりに「アウトバウンドの経路」が必須になる、という点が見落とされやすいところです。

操作ログの記録と一時的アクセス権の付与

「誰が何をしたか」を後から追える状態にする

入口を一本化する最大の利点は、操作ログを集約できることです。Session Managerでは、セッションのログをS3やCloudWatch Logsに保存する設定ができ、入力したコマンドと出力がすべて記録されます。踏み台方式でも、auditdや各利用者の個別アカウント+sudoログで、個人単位の追跡は可能です。

# Session Managerのログ保存設定(Session Manager preferences相当)を確認
aws ssm get-document --name SSM-SessionManagerRunShell \
  --query "Content" --output text | python3 -c "import json,sys; d=json.load(sys.stdin); print(json.dumps(d['inputs'], indent=2))"

# 出力例(s3BucketName / cloudWatchLogGroupName が設定されているか)
{
  "s3BucketName": "prod-session-logs",
  "s3EncryptionEnabled": true,
  "cloudWatchLogGroupName": "/ssm/sessions",
  "cloudWatchEncryptionEnabled": true,
  "runAsEnabled": true,
  "runAsDefaultUser": "ops"
}

ログは「本番で何かが壊れたときに、直前に誰が何を変更したか」を調べるための最短経路であり、監査対応でも必須になります。サーバー内での権限の分け方はsudoの設計とroot運用とあわせて設計してください。

常時アクセス権を持たせない

もう一歩進めるなら、「普段は誰も本番に入れず、必要なときだけ時間制限付きで権限を付与する」運用(Just-in-Timeアクセス)です。AWSであれば、IAM Identity Centerの一時的な権限昇格、あるいは有効期限付きのIAMポリシーを承認フロー経由で付与する仕組みで実現できます。Cloudflare Accessにはセッションの有効期間と承認者による一時許可の機能があります。

簡易的には、次のようなルールだけでも効果があります。

  1. 本番アクセス権を持つグループを最小人数にし、四半期ごとに棚卸しする。
  2. 本番に入る際は、チケット番号と目的をチャットに投稿してから入る(記録を残す)。
  3. 退職・異動時は、IdPのアカウント無効化で全経路のアクセスが同時に止まる構成にしておく(鍵ファイルの回収に依存しない)。

開発・ステージング・本番でアクセス権の強さを変える設計は開発・ステージング・本番環境の分離設計で扱っています。

実務での選び方

  • AWSのみで運用している:Session Managerを第一候補にする。踏み台の維持費とパッチ管理が不要になり、IAMで統合管理できる。
  • GCPのみ:IAPのTCP転送。設定は数行で済む。
  • オンプレやVPS、複数クラウドが混在:Cloudflare Accessのような外部のゼロトラスト製品か、踏み台+ProxyJump+IP制限。
  • 今すぐ改善したいが構成を大きく変えられない:まず全サーバーの22番を「踏み台またはオフィスIPからのみ」に絞り、パスワード認証を無効化する。それだけで攻撃面は大きく減る。

まとめ

本番サーバーへの入口は、「各サーバーに直接SSH」から「踏み台に集約」へ、さらに「ポートを一切開けずにクラウドのIAMやIdPで認証する」方式へと進化してきました。踏み台を使う場合はProxyJumpで鍵を踏み台に置かず、Session ManagerやIAP、Cloudflare Accessを使う場合はアウトバウンド経路とログ保存の設定を忘れない。そして、誰が何をしたかを追えるログと、必要なときだけ付与する一時的な権限で、入口そのものの運用を引き締めることが要点です。

まずは自社の本番サーバーのセキュリティグループやファイアウォールで、22番がどこから到達可能になっているかを確認してみてください。安全なアクセス経路の設計やSession Managerへの移行にお困りの際は、Harmonic Societyのシステム開発・インフラ支援にご相談ください。

#踏み台サーバー#Bastion#Session Manager#SSH

Harmonic Society

この記事の内容、自社の業務でも活かせそうですか?

ローカルLLM・AI・クラウドなどの技術導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK。まずは30分の無料相談から。売り込みはしません。

共有:
無料メルマガ

週1回、最新の技術記事をお届け

AI・クラウド・開発の最新記事を毎週月曜にメールでお届けします。登録は無料、いつでも解除できます。

プライバシーポリシーに基づき管理します

関連記事

Related / 9 articles

  1. プログラミング

    DDoS攻撃の仕組みと対策入門|レイヤー別の防御とCDN・クラウドの活用

    DDoS攻撃をボリューム型・プロトコル型・アプリ層に分けて仕組みを解説し、自前サーバーで防げない理由、CloudflareやAWS Shieldの標準防御、オリジンIPの隠し方、レートリミットとBot対策、攻撃を受けたときの初動、費用が跳ね上がるDenial of Walletへの備えまでわかります。

  2. プログラミング

    WAFとは?仕組み・導入パターン・誤検知対策|Webアプリを攻撃から守る実践ガイド

    WAFがファイアウォールやIDSと何が違うのか、シグネチャとマネージドルールの仕組み、Cloudflare WAF・AWS WAF・ModSecurityの比較、フォーム送信がブロックされる誤検知の調査と例外設定、ログ監視、WAFが代替できないことまで実践的に解説します。

  3. プログラミング

    セキュリティヘッダー入門|CSP・HSTS・X-Frame-Optionsの設定と効果を実践解説

    CSP・HSTS・X-Frame-Options・X-Content-Type-Optionsなど主要セキュリティヘッダーが防ぐ攻撃と、CSPのReport-Onlyからの段階導入、nonce/hash、HSTS preloadの不可逆リスク、Nginx・Next.js・Astroでの設定例、確認方法を解説します。

  4. プログラミング

    クラウドの通信費(Egress)入門|データ転送量課金の仕組みと転送コストを抑える設計

    クラウドの「受信無料・送信有料」の原則、AZ間・リージョン間・インターネット向けの単価差、NATゲートウェイ処理料の罠、CDNで転送量を減らす方法、バックアップやログ転送の見落とし、請求書で転送料を特定する手順を解説。想定外の請求を防げます。

  5. プログラミング

    秘密情報をGitに入れない仕組み|.gitignore・git-secrets・履歴から漏れた鍵の削除

    APIキーや.envをGitにコミットしてしまう典型経路と、.gitignore・.env.exampleの運用、pre-commitでのgitleaks検知、GitHub secret scanningの活用、漏れた鍵の無効化と履歴書き換え(git filter-repo)の手順を解説。仕組みで再発を防げます。

  6. プログラミング

    開発・ステージング・本番環境の分離設計|環境差分をなくす構成とアクセス制御

    開発・ステージング・本番それぞれの目的と、構成をコードで揃える方法、環境別の設定注入、本番データを使わないテストデータ戦略、ステージングの保護(Basic認証・IP制限・noindex)、コストを抑える運用までを解説。環境差分による本番障害を防げます。

  7. プログラミング

    ngrok・Cloudflare Tunnelでローカルを公開|Webhook開発とデモ環境の作り方

    NAT内のローカル環境にStripeやLINEのWebhookを届けるトンネリングの仕組みを解説。ngrok・Cloudflare Tunnel・localtunnelの比較、固定ドメインと認証、リクエスト検査、公開時のセキュリティ、自宅サーバー公開への応用までわかります。

  8. プログラミング

    ローカル開発環境のHTTPS化|mkcert・hostsファイル・自己署名証明書の正しい使い方

    ローカル開発をHTTPS前提にすべき理由(Secure Cookie・Service Worker・OAuth)と、mkcertでローカルCAを作りhostsで独自ドメインを割り当ててVite・Next.js・Dockerで使う手順を解説。証明書警告を無視する癖の危険も理解できます。

  9. プログラミング

    localhost・0.0.0.0・127.0.0.1の違い|ポートとUnixソケットを理解して「つながらない」を解決

    localhost・127.0.0.1・0.0.0.0の意味の違い、Dockerで外から接続できない原因、host.docker.internal、ポート競合の調べ方、Unixソケットの利点と権限、1024未満ポートの制約を解説。「つながらない」を仕組みから解決できます。

Harmonic Society

「読んで終わり」にせず、自社の業務で試してみませんか?

AI・ローカルLLM・クラウドの導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK・売り込みなし。

無料・30分・オンラインOK|1営業日以内に返信します