こねくり回してようやく解決したので、備忘録。
目的
パブリックIPアドレスが固定されていないEC2インスタンスにVS Codeでgit pull
/push
するときに、
毎回.ssh/config
を書き換えたくない。
すなわち、
パブリックIPアドレスが固定されていないEC2インスタンスに、毎回.ssh/config
を書き換えずにssh
接続したい。
環境
Windowsで個人開発をしていて、EC2インスタンスをプライベートなGitサーバ兼デプロイサーバとして使っています。
インスタンスは常時起動ではなく、必要な時だけ起動するので、そのたびにパブリックIPアドレスが変わります。
この上でローカルからリモートへgit経由でアクセスしようとすると、毎回
git remote set-url origin https://[毎回違うパブリックIP]/[.gitの場所]
とリモートアドレスを書き換えるか、.ssh/config
に設定を書き込んだ上で、URLを毎回書き換える必要があります。面倒!
有料のElastic IPをアタッチすればIPは固定されるけど、少額とはいえもったいない。
そこで無料のEC2 Instance Connectorの出番です。
AWS CLI経由でsshができるので、これを使う設定を.ssh/config
に書き込んで、
VS Codeのコマンドからgit pull
/push
したりRemote SSHで直接アクセスできるようにします。
準備
EC2 Instance Connector経由で、AWS CLIを使ってインスタンスに接続できるのが前提です。
AWS: EC2 Instance Connect Endpoint を使用して Linux インスタンスに接続する をベースに、とても丁寧で救われたこちらの解説記事を参考にしました。
IAMに付与する許可ポリシーが一番のハマりポイントだと思います(私はハマった)。
AWS: EC2 Instance Connect Endpoint を使用するための IAM アクセス許可の付与 に記載されているJSONを適宜編集します。
既知で単一のエンドポイントにだけ接続したい場合は次の通り:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EC2InstanceConnect",
"Action": "ec2-instance-connect:OpenTunnel",
"Effect": "Allow",
"Resource": "arn:aws:ec2:ap-northeast-1:[アカウントID]:instance-connect-endpoint/[エンドポイントID]",
"Condition": {
"NumericEquals": {
"ec2-instance-connect:remotePort": "22"
},
"NumericLessThanEquals": {
"ec2-instance-connect:maxTunnelDuration": "3600"
}
}
},
{
"Sid": "SSHPublicKey",
"Effect": "Allow",
"Action": "ec2-instance-connect:SendSSHPublicKey",
"Resource": "*",
"Condition": {
"StringEquals": {
"ec2:osuser": "ami-user"
}
}
},
{
"Sid": "Describe",
"Action": [
"ec2:DescribeInstances",
"ec2:DescribeInstanceConnectEndpoints"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
ami-username
はAWS: IAM への EC2 Instance Connect のアクセス許可の付与 にある通り、
- インスタンスのOSがAL2023またはAmazon Linux 2の場合
ec2-user
- Ubuntuでは
ubuntu
です(ただしami-username
のままでも動いた)。
このページにもポリシーのJSONがありますが、これだけだとOpenTunnel許可がないよ、と怒られます。
ポリシーを付与した後、コマンドプロンプトないしPowerShellから
aws ec2-instance-connect ssh --instance-id [インスタンスID] \
--eice-options maxTunnelDuration=3600,endpointId=[エンドポイントID] \
--os-user [インスタンス上のユーザ名]
でインスタンスに接続できれば、準備完了です。
手順
Windows上のAWS CLIでssh接続できるようになったわけですが、ここから AWS: EC2 Instance Connect Endpoint を使用して Linux インスタンスに接続する に従って
ssh -i [ssh鍵] [インスタンス上のユーザ名]@[インスタンスID] \
-o ProxyCommand='aws ec2-instance-connect open-tunnel --instance-id [インスタンスID]'
を実行すると、
2024-03-06 14:51:13,755 - awscli.customizations.ec2instanceconnect.websocket - ERROR - {"ErrorCode":"AccessDeniedException","Message":"User: arn:aws:iam::[アカウントID]:user/[接続に使うIAMユーザ] is not authorized to perform: ec2-instance-connect:OpenTunnel on resource: arn:aws:ec2:ap-northeast-1:[アカウントID]:instance-connect-endpoint/[エンドポイントID] because no identity-based policy allows the ec2-instance-connect:OpenTunnel action"}
AWS_ERROR_HTTP_WEBSOCKET_UPGRADE_FAILURE: Failed to upgrade HTTP connection to Websocket.
kex_exchange_identification: Connection closed by remote host
Connection closed by UNKNOWN port 65535
と再び怒られてしまいます。もー!
1. IAMに許可ポリシーを追加する
OpenTunnelその他を許可するインラインポリシーを新しく作り、ユーザに付与します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SecureInstanceConnect",
"Effect": "Allow",
"Action": [
"ec2-instance-connect:OpenTunnel",
"ec2-instance-connect:SendSSHPublicKey",
"ec2-instance-connect:SendSerialConsoleSSHPublicKey"
],
"Resource": [
"arn:aws:ec2:ap-northeast-1:322811116193:instance/[インスタンスID]",
"arn:aws:ec2:ap-northeast-1:322811116193:instance-connect-endpoint/[エンドポイントID]"
]
}
]
}
これで再び
ssh -i [ssh鍵] [インスタンス上のユーザ名]@[インスタンスID] \
-o ProxyCommand='aws ec2-instance-connect open-tunnel --instance-id [インスタンスID]'
を実行して、インスタンスに接続できればOKです。
2. 上記を.ssh/config
に書き込む
.ssh/config
に
Host [お好きな名前]
HostName [インスタンスID]
User [EC2上のユーザ名]
Port 22
IdentitiesOnly yes
IdentityFile [ssh鍵]
StrictHostKeyChecking no
ProxyCommand "C:\Program Files\Amazon\AWSCLIV2\aws.exe" ec2-instance-connect open-tunnel --instance-id [インスタンスID]
を追記します。
aws
はPATH
を通していない場合はフルパスで書く必要があります。
WindowsだとデフォルトでC:\Program Files
にインストールされます。この空白で引っかかるので、パス全体をquoteで囲む必要があります。
StrictHostKeyChecking
を no
にすると、IPが変わった後の質問を回避できます。
ただしセキュリティ的には良くないとGitHub Copilot先生が教えてくれました。
最後に
ssh [お好きな名前]
でインスタンスに接続できるか確認します。無事接続できたら完了です。
これで、パブリックIPアドレスが固定されていないEC2インスタンスを、.ssh/config/
に保存された他の接続先と同じように扱えるようになりました。お疲れ様でした。
それでは楽しいsshライフを!