Fargate containerにSession Managerでログインする

tl;dr

こんばんは。ブログ書かなすぎてはてなブログ Pro を契約したえのかわです。
Fargate に対して SSH ログインしたい時ってありますよね。SSH プロセスを起動するのもアレなので ssm-agent を入れた運用用途の Task を起動してみました。

きっかけは Tori さんのツイートです。 実際に業務でも「EC2 レスで RDS へのログインとかどうすっかなー」と考えていたので、良いきっかけでした。

やりたいことはこんな感じです。EC2 起動したら負け。

f:id:enokawaa:20190905001025p:plain

SSM Activation の作成

この方法は ECS Task を外部サーバとみなすため、SSM で Hybrid Activation を作成します。

$ aws ssm create-activation --description fargate-ops \
--iam-role "service-role/AmazonEC2RunCommandRoleForManagedInstances" \
--registration-limit 100000 \
--default-instance-name fargate-ops
{
    "ActivationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "ActivationCode": "xxxxxxxxxxxxxxxxxxx"
}

docs.aws.amazon.com

出力された ActivationIdActivationCode は後で利用するので控えておきます。 --registration-limit を 100000 としているのは、ECS Task を起動する度に SSM の Managed Instances に外部サーバ(2回目)として登録されるためです。 運用時は頻繁に ECS Task の起動や停止が予想されるので Max 値(2019/09/05 現在)にしています。

SSM Parameter Store への Activation 情報登録

前の手順で出力された ActivationIdActivationCodeamazon-ssm-agent の Register 時に必要となるので、SSM Parameter Store に put しておきましょう。

$ aws ssm put-parameter --name "ops-activation-id" \
--value "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
--type "SecureString"
{
    "Version": 1
}
$
$ aws ssm put-parameter --name "ops-activation-code" \
--value "xxxxxxxxxxxxxxxxxxx" \
--type "SecureString"
{
    "Version": 1
}

そういえば SSM Parameter Store の Name を /Fargate/ops-activation-id とかにしたら Fargate Task の起動が上手くいきませんでした。( エラー内容忘れた。。

Docker imageの作成 / Push

Dockerfile の中身は GitHub を見てみてください。( Star くれると喜びます

github.com

hub.docker.com

189MB て。。PR もお待ちしています。

$ docker image ls enokawa/ssm-to-fargate
REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
enokawa/ssm-to-fargate   latest              e864b10da748        42 minutes ago      189MB

ちなみにリポジトリ直下で $ docker-compose up -d を実行するとローカルでも動作確認が可能です。その際は以下の 3 つの環境変数も設定してあげてください。僕は direnv環境変数を設定しています。

  • SSM_AGENT_CODE
  • SSM_AGENT_ID
  • AWS_DEFAULT_REGION

ECR に Docker image を push します。

$ docker build -t enokawa/ssm-to-fargate:latest .
$ $(aws ecr get-login --no-include-email --region <AWS_DEFAULT_REGION>)
$ docker tag enokawa/ssm-to-fargate:latest <AWS_ACCOUNT_ID>.dkr.ecr.<AWS_DEFAULT_REGION>.amazonaws.com/ssm-to-fargate:latest
$ docker push <AWS_ACCOUNT_ID>.dkr.ecr.<AWS_DEFAULT_REGION>.amazonaws.com/ssm-to-fargate:latest

ECS Task の起動

事前に Task Definition 内の Container Definitions で、SSM_AGENT_CODESSM_AGENT_ID を定義します。

f:id:enokawaa:20190905013847p:plain

AWS_DEFAULT_REGION は ECS Task 内の環境変数に含まれているため設定不要です。ECS での機密情報や環境変数の扱いについてはハマコーさんの記事が詳しいです。

dev.classmethod.jp

タスクを起動します。

$ aws ecs run-task \
--launch-type FARGATE \
--task-definition "<YOUR_TASKDEF_ARN>" \
--platform-version 1.3.0 \
--cluster <YOUR_CLUSTER_NAME> \
--count 1 \
--network-configuration "{\"awsvpcConfiguration\":{\"subnets\":[\"subnet-xxxxxxxxxxxx\"],\"securityGroups\":[\"sg-xxxxxxxxxxxxxx\"],\"assignPublicIp\":\"DISABLED\"}}"

Task が起動すると、SSM の Managed Instances 上で外部サーバとして起動した Container の情報が確認できます。InstanceId を控えておきます。

$ aws ssm describe-instance-information
{
    "InstanceInformationList": [
        {
            "InstanceId": "mi-xxxxxxxxxxxxxxxx",
            "PingStatus": "Online",
            "LastPingDateTime": 1567615737.931,
            "AgentVersion": "2.3.701.0",
            "IsLatestVersion": true,
            "PlatformType": "Linux",
            "PlatformName": "Ubuntu",
            "PlatformVersion": "18.04",
            "ActivationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
            "IamRole": "service-role/AmazonEC2RunCommandRoleForManagedInstances",
            "RegistrationDate": 1567615733.866,
            "ResourceType": "ManagedInstance",
            "Name": "fargate-ops",
            "IPAddress": "169.254.172.42",
            "ComputerName": "ip-10-0-2-100.ap-northeast-1.compute.internal"
        }
    ]
}

Fargate Container へのログイン

Session Manager 経由で Fargate Container へログインします。

$ aws ssm start-session --target mi-xxxxxxxxxxxxxxxx

f:id:enokawaa:20190905020651p:plain

いい感じですね。後はお好きにどうぞ。

後片付け

ECS Task の Stop と SSM Managed Instance の Deregister を行います。

$ aws ecs stop-task --cluster sample-cluster \
--task "<TASK_ID>"
$
$ deregister-managed-instance --instance-id mi-xxxxxxxxxxxxxxxx

おわりに

無事に Fargate container に Session Manager 経由でログインすることができました。踏み台や秘密鍵の用意も不要なのでセキュアかつ費用も安く抑えることが可能です。aws/containers-roadmap の Issue でも活発に議論されているので、注目したいと思います。

github.com

個人的には、AWS が docker exec 的な API を作成してくれるのではないかなと妄想しています。aws-cli で以下のように実行できると嬉しいですね。

$ aws ecs exec --task "<TASK_ID>"

FYI

以下の Qiita 記事が参考になりました。ありがとうございました。

qiita.com

2020/07/06 追記

SSM の Hybrid Activation 作成ですが、有効期限が予め決まっています。 --expiration-date オプション指定なしだとデフォルトの 24 時間、指定ありだと最大 30 日です。 @fujiwara さんの仰る通りで、Hybrid Activation を今日作ったとしても有効期限は最大で 30 日となるため、新しく Hybrid Activation を作成して Parameter Store へ put するなどの運用が必要となります。再作成用の Lambda を作る手もありますが面倒ですね。#187 の Issue を引き続き Watch することにします。