Session Managerを使ってEC2の先にあるRDSに接続してみた

2021.02.26

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは!DA(データアナリティクス)事業本部 インテグレーション部の大高です。

最近、AWS System ManagerのSession Managerを利用するとポートフォワーディングができるということをはじめて知りました。

ただ、このままだと接続先のEC2上のポートをローカルにフォワーディングできるのですが、更にEC2を踏み台にして、その先のRDSに接続するというようなことは出来ません。

この場合はSession Manager経由でSSHをすれば良いのですが、この場合にはどうしてもキーが必要になります。

個人的にこのキーの管理が煩雑だったため、どうにかできないか試してみました。

2022/05/30(月) 追記

このエントリでやりたかったことは、AWS System Managerで正式に対応されました!(irbbbさんの下記エントリ参照)

ですので、このエントリは過去の情報として参考程度にご参照いただければと思います。

前提

今回の環境は以下のような環境です。

  • 接続先のEC2はAmazon Linux 2
  • EC2はSession Managerで接続できるように設定済み
  • 接続元のローカルPCも接続先EC2にSession Manager経由で接続できるように設定済み
  • EC2からしかアクセスできないRDSがあって、ここにローカルPCからアクセスしたい

対応方針を検討する

EC2上のポートをローカルにフォワーディングするのは簡単にできるので、あとはEC2とRDS間をどうするかです。

ここは、今回はsocatを利用したいと思います。標準ではAmazon Linux 2にインストールされていないのでインストールする必要が出てきてしまうのですが、目をつむります。

socatコマンドを利用したTCPリレーについては、以前に書いた通りなのでこれをそのままSSMのRumCommandで実行し、その後にEC2上のポートをローカルにフォワーディングする方向でやってみます。

EC2 - RDS間のポートリレーをさせる

今回はすべてローカルPC上から操作を完結させたいので、SSMドキュメントを作成して実行させるようにします。

まずは以下のようなドキュメントを用意します。

command-document.yml

---
schemaVersion: "2.2"
description: Port Forwarding
parameters:
  localPort:
    type: String
    default: "5432"
    description: "フォワーディング元のポート番号を指定"
  targetHost:
    type: String
    default: ""
    description: "フォワーディング対象ホストを指定"
  targetPort:
    type: String
    default: "5432"
    description: "フォワーディング先のポート番号を指定"
mainSteps:
  - action: aws:runShellScript
    name: install
    inputs:
      runCommand:
        - yum install socat -y
  - action: aws:runShellScript
    name: run
    inputs:
      runCommand:
        - socat tcp4-listen:{{ localPort }},reuseaddr,fork TCP:{{ targetHost }}:{{ targetPort }}

ドキュメントが準備できたらCLIから登録します。以下のようなシェルを作成して登録してみました。念の為に、事前に同名ドキュメントを削除するコマンドも入れています。

create-document.sh

#!/bin/bash
DOCUMENT_NAME=port-relay

aws ssm delete-document \
    --name ${DOCUMENT_NAME}

aws ssm create-document \
    --content file://./command-document.yml \
    --name ${DOCUMENT_NAME} \
    --document-type "Command" \
    --document-format "YAML"

ドキュメントが登録できたら、実際に実行します。こちらも以下のようなシェルを作成して実行してみました。

send-command.sh

#!/bin/bash
INSTANCE_ID=i-xxxxxxxxxxxxxxxxx
DOCUMENT_NAME=port-relay
TARGET_HOST="foobar.ap-northeast-1.rds.amazonaws.com"

aws ssm send-command \
  --instance-ids ${INSTANCE_ID} \
  --document-name ${DOCUMENT_NAME} \
  --parameters targetHost=${TARGET_HOST}

これで、EC2でsocatコマンドが実行されている状態になります。

ローカルPCにポートフォワーディング

あとはローカルPCにポートフォワーディングするだけです。以下のシェルを実行してポートフォワーディングします。

start-port-forwarding.sh

#!/bin/bash
INSTANCE_ID=i-xxxxxxxxxxxxxxxxx

aws ssm start-session \
    --target ${INSTANCE_ID} \
    --document-name AWS-StartPortForwardingSession \
    --parameters portNumber=5432,localPortNumber=5432

これで、localhost:5432にアクセスすれば、EC2上の先にあるRDSにアクセスできるようになりました!

課題

一見よさそうですが、ちょっと問題があります。

下記の記事にもある通り、RunCommandで実行されているコマンドには「実行タイムアウト」が7200秒で設定されています。このため、2時間経過するとsocatのプロセスが死んでしまうという問題があります

socatコマンドの実行をシェルスクリプト化したり、RunCommandで実行するコマンドをnohup xxx &のようにしたりもしてみたのですが、RunCommandで実行されるコマンドには効果がなく、現状では解決策が見つかりませんでした。

良い方法が見つかったら後で追記したいと思います。

まとめ

以上、Session Managerを使ってポートフォワーディングをしてみました。Session Managerを利用すると、これまでのようにEC2インスタンスにパブリックIP経由でSSH接続せずとも色々と作業ができるのを知ったので、これから活用していきたいと思います。

どなたかのお役に立てば幸いです。それでは!

参考