チームにAWS セッションマネージャを導入した話

## はじめに

こんにちは。
この記事は、FOLIO Advent calendar 2019 7日目の記事です。
私はFOLIOのグロース部というデータ分析業務やダッシュボードや分析基盤の開発・運用をするチームに所属しており、そのなかで私は主に分析基盤の開発・運用を担当しています。
なお、FOLIOには今年の10月からジョインしたばかりです。
~~思えば去年の今頃は前職のアドベントカレンダーの[記事](https://qiita.com/paulxll/items/1962e806cdc8a96068c1)を書いていましたね。。Databricksは大好きなツールなので、普段遣いできなくなってしまったのは転職の心残りの一つです。~~

さて本題。
この記事では、直近AWSのセッションマネージャをチームに導入して、EC2へのSSHログインを廃止した話について書いていきます。

## AWSセッションマネージャとは

AWSセッションマネージャとは、AWS System Managerの一機能で、平たく言うとEC2サーバに公開鍵認証を経ずにログインし、コマンドを実行できる機能です。
ちなみにSystem managerはその名の通りAWSのリソース管理のための多様な機能を備えていますが、本記事ではそれらには触れません。
興味ある方は[こちら](https://dev.classmethod.jp/cloud/aws/system-management-with-aws-systems-manager/)の記事なんかがコンパクトに纏まっていて良いと思います。


## 前提となる業務状況と導入の目的

* 複数のサーバを動かしており、分析用のjupyterが動いているサーバ、データパイプラインを動かすための利用しているairflow用のサーバ, EC2にホスティングしているredashサーバなど複数のサーバが動いている
* ちなみにFOLIOでは部署ごとにAWSアカウントが別れているので、アプリケーションに関するリソースは分析メンバーは直接触れません。今回の話はデータ分析周りのAWSリソースのことを扱っているとご理解ください。
* 上記のような状況で、複数サーバにログインするための鍵やアカウントの管理が煩雑になっていた。

こうした状況のため、セッションマネージャの機能を利用してサーバにログインさせることで、シンプルに管理することが導入のモチベーションです。
また、セッションマネージャの良いところはAWSのマネジメントコンソール上からサーバにログインできることにもありますが、セッションマネージャ導入をメンバーに説明した段階で、セッションマネージャを(ブラウザではなく)ターミナル上で使いたいというニーズがあったため、チームメンバーにIAMユーザを払い出してAWS CLIでセッションマネージャを利用する方法を選択しました。



## 具体的に実施したこと

* セッションマネージャを利用するためのIAMポリシーの作成し、それをIAMグループにアタッチし、チームメンバーにそのグループに所属するIAMユーザを払い出した
* EC2サーバ側の準備として、
    * セッションマネージャを利用するための、IAMポリシーを作成し、セッションマネージャを利用したいインスタンスのロールにアタッチ
    * 各EC2サーバ上で必要に応じてamazon-ssm-agentをインストール(AWSが用意したAMIを使ってる場合、最初からインストールされていますが、一部コミュニティAMIを利用しているサーバには個別にインストールしました)
* ユーザ向けの導入準備として、普段SSHを使ってサーバにログインしているメンバー向けにセッションマネージャのハンズオンを実施。

それぞれの手順について書いていきます。

### IAMポリシーの準備

準備したポリシーは、
1. SSMを利用するユーザに付与するポリシー
2. EC2サーバ側のロールにアタッチするポリシー
の2つ。

Session manegerは便利なサービスである反面強力なサービスでもあるので、セキュリティホールにもなりえます。[^1]
必要十分なIAMポリシー設計ができると良いですね。
実際に利用しているものとは異なりますが、ポリシーの一例を提示します。


* ユーザに付与するポリシー

ごくシンプルなポリシーですが、工夫した点としては、セッションの終了は「自分が立ち上げたセッションのみ終了を許可している」点です。

```json:ssm-users-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ssm:StartSession",
            "Resource": [
                "<your-ec2-resource-list>"
            ]
        },
        {
            "Effect": "Allow",
            "Action": "ssm:TerminateSession",
            "Resource": "arn:aws:ssm:*:*:session/${aws:username}-*"
        }
    ]
}
```

* EC2サーバに付与するポリシー

AWS管理ポリシーの `AmazonEC2RoleforSSM` から先ほどの記事を参考にガッツリ削ってます。
ええ、セッションマネージャからEC2にアクセスするだけならこれだけでも十分なんです。

```json:policy-for-ec2-server.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:DescribeAssociation",
                "ssmmessages:*",
                "ssm:UpdateInstanceInformation",
                "ec2messages:*"
            ],
            "Resource": "*"
        }
    ]
}
```

### EC2サーバ上でSSMエージェントを動作させる

セッションマネージャを用いてEC2にアクセスするには、そのサーバ上でSSMエージェントが動作している必要があります。
前述の通り、そこそこ新しいAWS純正のAMIを使っているインスタンスには最初からインストールおよび起動されているので、インストールは不要です。
もしインストールされていなくても、Amazon Linuxであればyum, Ubuntuであればaptからインストールできます。
ただし、インストールしたのち、EC2にアタッチされているIAMロールに前述のIAMポリシーをアタッチしたあとに追加したポリシーの情報を反映するために**SSMエージェントを再起動する必要があります**。ここ、しょうもないポイントながら設定時にハマったので注意です。

さて、ここまで終わればEC2側の準備は完了なので、続いてユーザ側の準備を進めます。

### ユーザ側の準備

AWS CLIは当然必要なのでインストールした上で、セッションマネージャ用のプラグインをインストールします。
やり方は非常にシンプルです。

```.sh
# インストール用のファイルを落っことしてきて解凍する
curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/mac/sessionmanager-bundle.zip" -o "sessionmanager-bundle.zip"
unzip sessionmanager-bundle.zip
 
# 公式ドキュメント上のインストールコマンドを叩くだけ
sudo ./sessionmanager-bundle/install -i /usr/local/sessionmanagerplugin -b /usr/local/bin/session-manager-plugin
```

続いて、AWS CLIでIAMユーザを利用するための準備も進めましょう。
ユーザに配布しているIAMユーザには作業用ファイルを置くS3を操作するためのものなども存在するのですが、前述の理由からセッションマネージャ専用のIAMユーザを払い出すことにしました。
なので、専用のプロファイルを切って、そちらに必要な情報を追加していきましょう。
下の例ではssmというprofile名で必要な情報を格納しています。

```~/.aws/config
[profile ssm]
region = <your-region>
```

```~/.aws/credentials
[ssm]
aws_access_key_id = <your_aws_access_key_id>
aws_secret_access_key = <your_aws_secret_access_key>
```

接続する際はこんな感じ。接続したいインスタンスのIDを管理コンソールからコピペしてきましょう。

```.sh
aws ssm start-session --target <your-ec2-instance-id> --profile ssm 
```
このコマンドを実行すると、セッションIDが払い出され、サーバ上でコマンドが実行できます。

```.sh
Starting session with SessionId: xxxxxxxxxxxxxxxxxxxx(※実際にはここに実際のSessionIdが入ります)
$ pwd
/var/snap/amazon-ssm-agent/1480
$ whoami
ssm-user
$ echo "日本語も使える"
日本語も使える
```

ssm-userというユーザでログインされていることがわかりますね。
sudoが使えるので、ここからユーザを切り替えることもできるので、任意の別のユーザとしてコマンドを実行することもできます。
本来ならばユーザも指定したいところではありますが、現在はssm-userでログインしています(そのへんの経緯は後述しているので興味があれば読んでみてください)。

## まとめ

AWSセッションマネージャを用いて、鍵管理を行うことなくEC2にアクセスできるようになりました。
ターミナル上から操作できるので、さながらSSHを利用しているような使い心地でEC2リソースにアクセスできますね。
また、本記事では直接触れなかったものの、セッションマネージャの実行ログはCloudWatchで監査ログが残せるのもメリットの一つです。

## あとがき兼FAQ

* 最初にユーザ変えるコマンド打つのだるくない?SSHみたいにユーザ指定できないの?
    * ユーザ指定する方法はあります。ただし、今回の業務状況にはマッチしなかったので採用していません。
    * セッションマネージャを経由してログインするサーバが単一、もしくは共通のユーザ名を保つ場合には、こちらのドキュメント[^2]のようにセッションマネージャの設定からAWSアカウント単位、IAM role単位で設定することもできますが、前提条件に書いた、複数のサーバにログインする都合上採用できませんでした。
* scpはどうするの?
    * セッションマネージャのちょっと残念な点でもあるのですが、 AWS CLIを用いてscpコマンドを実行することもできるのですが、その際に **EC2サーバを作成する際に発行された秘密鍵が必要になります。** ~~せっかく鍵管理から解放されたと思ったのに...!~~ ということで、sshを廃止したついでにscpも廃止し、サーバとローカル環境間でファイルをやり取りする際には、作業用のファイルを置く(本番実行に関わるような設定ファイルは置かない)ためのS3バケットを作成し、そこを介してファイルをやり取りすることにしています。
* インスタンスID暗記するの辛い
    * よくログインするサーバにはエイリアスを張りましょう


[^1]: [AWS SSM is a trojan horse: fix it now!](https://cloudonaut.io/aws-ssm-is-a-trojan-horse-fix-it-now/)
[^2]: [Enable Run As Support for Linux Instances](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-preferences-run-as.html)