Session Manager をローカルから利用するときに、セキュアな環境にするときの考慮点

# はじめに

EC2 インスタンスや、Systems Manager で管理しているオンプレミスのマシンにリモートアクセスができる Session Manager という機能があります。従来は踏み台サーバーを使ってリモートアクセスを実現していたことが多かったと思いますが、踏み台サーバー自体に Public IP を持たせている場合はセキュリティ的にちょっときになります。こういった課題感をマネージドサービスで解決するのが、Session Manager という機能です。具体的な使い方は[こちらの記事で検証](https://qiita.com/sugimount-a/items/d9ac9427bdc01e05770f)をしました。

ただ、これでもセキュリティ的に気になる点はまだあります。ラップトップに AWS CLI を入れてリモートアクセスする構成を考えたときに、ラップトップに格納するアクセスキー・シークレットキーの取り扱いが気になります。仮に、大きな権限を持たせたアクセスキーが漏洩してしまったときに大変なことになるので、適切な管理が必要です。

この「適切な管理」で対処した方が良いポイントをいくつか整理してみましょう。



# AWS が出しているベストプラクティス

まず、AWS がベストプラクティスを Document で公開しています。量が多いですが、それぞれの観点が網羅的に紹介されていて、とても有用なリソースです。この記事で紹介しきれないプラクティスもあるので、ぜひこちらも確認してみてください。

Systems Manager のセキュリティのベストプラクティス
https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/security-best-practices.html

AWS アクセスキーを管理するためのベストプラクティス
https://docs.aws.amazon.com/ja_jp/accounts/latest/reference/credentials-access-keys-best-practices.html

IAM でのセキュリティのベストプラクティス
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html



# この記事で扱うポイント

上記のベストプラクティスの中で、**個人的**に重要そうと思ったポイントをいくつか抜粋していきます。紹介しきれないものもあるので、ぜひ上の AWS Document も合わせてご確認ください。



- アクセスキーの取り扱い
- 最小限の権限設定
- MFA の有効化
- ログの有効化
- IP アドレス制限
- コマンド制限
- sudo の制限
- Start-Session の通知





# アクセスキーの取り扱い

ラップトップに AWS CLI の実行権限を与えるためのアクセスキーの取り扱いについてです。ベストプラクティスに書いているのですが、そもそもアクセスキーを生成しない方法が紹介されています。アクセスキーを発行することで漏洩する恐れがあるなら、そもそも作らなければいいじゃん、ということですね。SAML で Okta や Azure AD みたいな Identity Provider を使うことで、一時的な権限を生成する方法です。

SAML 認証情報を使って、一時的なユーザー認証情報を取得する方法は次の URL で紹介されています。(ちょっと手間そうだけど・・・。)

https://aws.amazon.com/jp/premiumsupport/knowledge-center/aws-cli-call-store-saml-credentials/

　

また、Okta を使っている場合は、一時的なユーザー認証情報を生成する okta-awscli というツールがあるみたいですね。結構便利そうです。

https://dev.classmethod.jp/articles/okta-awscli-usage/

　

あと他のプラクティスで紹介されているのが、アクセスキーの定期的な更新や、使っていないアクセスキーの削除などがありました。

アクセスキーのベストプラクティス
https://docs.aws.amazon.com/ja_jp/accounts/latest/reference/credentials-access-keys-best-practices.html



# 最小限の権限設定

アクセスキーが万が一漏洩したときに、その影響を最小限にするために、最小限の権限設定をするのがお勧めです。いくつかのサンプルが AWS Document で紹介されているので、具体的に方法を見ていきましょう。

IAM Policy で、Session Manager でアクセスする先の Node (仮想マシンのこと) を制限することが出来ます。例えば、次のような IAM Policy を設定します。

- Action で `ssm:StartSession` を指定しており、Session Manager の接続開始のみできる
- Resource で `i-04c2e28d623aab574` を指定しており、特定の EC2 Instance に限定して接続ができる

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:StartSession"
            ],
            "Resource": [
                "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/i-04c2e28d623aab574"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:TerminateSession",
                "ssm:ResumeSession"
            ],
            "Resource": [
                "arn:aws:ssm:*:*:session/${aws:username}-*"
            ]
        }
    ]
}
```

　

こんな感じで、IAM Policy を作成します。

![image-20221228185555613.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/00b523fa-8bb7-e30f-2e8d-a407c9101dc0.png)

　

上記の IAM Policy を設定した結果、接続を許可しているインスタンスに限定して接続できます。

```
> aws ssm start-session --target i-04c2e28d623aab574 --profile ssm

Starting session with SessionId: cliuser-sessionmanagertest-0fbe2fd72b3463556
sh-4.2$
sh-4.2$ whoami
ssm-user
sh-4.2$
```

　

以下のように、許可していないインスタンスではエラーになります。

```
> aws ssm start-session --target i-0f7fdbd2425d0cd4a --profile ssm

An error occurred (AccessDeniedException) when calling the StartSession operation: User: arn:aws:iam::xxxxxxxxxxxx:user/cliuser-sessionmanagertest is not authorized to perform: ssm:StartSession on resource: arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/i-0f7fdbd2425d0cd4a because no identity-based policy allows the ssm:StartSession action
```

　

また、ssm 以外のサービスにはもちろんアクセスできません

```
> aws s3 ls --profile ssm

An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied
```

　

Session Manager の追加サンプル IAM ポリシー
https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/getting-started-restrict-access-examples.html



# MFA の有効化

AWS CLI で MFA を有効化できます。仮にアクセスキーが流出しても MFA を持っていない人はコマンドが実行できないです。また、一時的なアクセスキーが流出した場合でも、デフォルトで 12 時間といった有効期限が設定されているので、一定時間後には使えなくなります。MFA を使ったアクセスキーの保護はかなり有効的な利用だと思います。

内容が多かったため、別の記事の検証内容を掲載します。

https://qiita.com/sugimount-a/items/5c522af2f5354ab7ab9f



# ログの有効化

Session Manager では、OS 上で実行したコマンドと、その結果を CloudWatch Logs 等に出力することができます。ログ出力の設定はデフォルトで ON になっているはずです。

いつ、どんなコマンドを実行して、どんな結果だったのかを後から追いやすくなります。このログを有効活用すると、後から操作履歴を追いやすくなります。

![image-20221229130804539.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/a6cd3d7e-aad4-16a4-afa3-b9d019be4d32.png)



https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/session-manager-logging.html



# IP アドレス制限

IAM Policy の設定の中で、IP アドレス制限を掛けられます。最小限の権限設定と合わせて、IP アドレスの設定を加えることで、よりセキュアな構成が可能です。

次の IAM Policy の例をでは、 `NotIpAddress` で書かれている IP アドレス以外は、アクセスを拒否します。

```json
{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Deny",
        "Action": "*",
        "Resource": "*",
        "Condition": {
            "NotIpAddress": {
                "aws:SourceIp": [
                    "xx.xx.xx.xx/32"
                ]
            },
            "Bool": {"aws:ViaAWSService": "false"}
        }
    }
}
```

　

例えば、次のように IAM Policy を作成したあとに、

![image-20221229131752379.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/c8f46655-ad69-d036-3070-d46660760024.png)

　

IAM User や Group などに、そのポリシーを紐づけます。

![image-20221229131840990.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/da9e1987-0a99-2344-9e71-3603b4a5de7a.png)

　

すると、該当の IP アドレス以外からは、Session Manager の接続が禁止されました。

```
> aws ssm start-session --target i-04c2e28d623aab574 --profile ssm

An error occurred (AccessDeniedException) when calling the StartSession operation: User: arn:aws:iam::xxxxxxxxxxx:user/cliuser-sessionmanagertest is not authorized to perform: ssm:StartSession on resource: arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/i-04c2e28d623aab574 with an explicit deny in an identity-based policy
```

　

https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_aws_deny-ip.html



# コマンド制限

Session Manager では、`start-session` でリモート接続する以外にも、事前にコマンドを定義する Document を用意することで、コマンドを実行することができます。この機能と一緒に IAM Policy の制限を工夫することで、特定の Document のみ実行を制限させることできます。リモート接続で好きなコマンドを実行することを禁止できます。厳密な環境では、こういった制限をしていくことを検討してもいいかもしれません。

ちょっとやってみましょう。

　

事前準備として、Systems Manager で Document を作成します。

![image-20221229135206565.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/717fecbf-8002-cf01-f816-ced1cae18aaa.png)

　

Document の名前と、Type を選びます。

![image-20221229135831409.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/953a8f7b-c5aa-a5c5-63bc-c1face8ac785.png)

　

次の YAML を入力します。これが、事前に定義するコマンドです。

- `commands: "tail -f {{ logpath }}"` と指定しており、tail -f コマンドを実行する
- `logpath` の部分は、パラメーターになっていて、実行時に tail 対象のファイルを指定可能
- `allowedPattern` で受け付けるパラメータを正規表現でチェックする

```yaml
---
schemaVersion: '1.0'
description: Document to view a log file on a Linux instance
sessionType: InteractiveCommands
parameters:
  logpath:
    type: String
    description: The log file path to read.
    default: "/var/log/amazon/ssm/amazon-ssm-agent.log"
    allowedPattern: "^[a-zA-Z0-9-_/]+(.log)$"
properties:
  linux:
    commands: "tail -f {{ logpath }}"
    runAsElevated: true
```

　

こんな感じで入力したら、Create Document  を押します。

![image-20221229135908746.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/86a03915-1129-2061-3d59-8f53a4647923.png)

　

IAM Policy を次のように変更します。

- `"arn:aws:ssm:ap-northeast-1:xxxx:document/LogTail"` を追加することで、この Document の実行を許可します

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:StartSession"
            ],
            "Resource": [
                "arn:aws:ec2:ap-northeast-1:xxxx:instance/i-04c2e28d623aab574",
                "arn:aws:ssm:ap-northeast-1:xxxx:document/LogTail"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:TerminateSession",
                "ssm:ResumeSession"
            ],
            "Resource": [
                "arn:aws:ssm:*:*:session/${aws:username}-*"
            ]
        }
    ]
}
```

　

実際に、Document を使って実行してみましょう。

- `--document-name LogTail` と Document の名前を指定

```
aws ssm start-session \
    --target i-04c2e28d623aab574 \
    --document-name LogTail \
    --profile ssm
```

　

実行例

- Document で指定しているとおり、`"tail -f {{ logpath }}"` のコマンドが実行されている

```
Starting session with SessionId: cliuser-sessionmanagertest-042c4337bcf51ef77
2022-12-29 05:19:19 INFO [ssm-session-worker] [cliuser-sessionmanagertest-042c4337bcf51ef77] [DataBackend] received plugin config message
2022-12-29 05:19:19 INFO [ssm-agent-worker] [MessageService] [EngineProcessor] [BasicExecuter] [cliuser-sessionmanagertest-042c4337bcf51ef77] master listener started on path: /var/lib/amazon/ssm/i-04c2e28d623aab574/channels/cliuser-sessionmanagertest-042c4337bcf51ef77
2022-12-29 05:19:19 INFO [ssm-session-worker] [cliuser-sessionmanagertest-042c4337bcf51ef77] [DataBackend] {"DocumentInformation":{"DocumentID":"cliuser-sessionmanagerte
```

　

別のログを見てみましょう

- `--parameters logpath="/var/log/boot.log" \` と指定しており、他のログファイルを指定

```
aws ssm start-session \
    --target i-04c2e28d623aab574 \
    --document-name LogTail \
    --parameters logpath="/var/log/boot.log" \
    --profile ssm
```

　

実行例

```
Starting session with SessionId: cliuser-sessionmanagertest-0db17cc2b75264163
Dec 29 03:39:06 ip-192-168-1-149 NET: dhclient: Locked /run/dhclient/resolv.lock
Dec 29 04:07:40 ip-192-168-1-149 NET: dhclient: Locked /run/dhclient/resolv.lock
Dec 29 04:30:20 ip-192-168-1-149 NET: dhclient: Locked /run/dhclient/resolv.lock
Dec 29 04:55:16 ip-192-168-1-149 NET: dhclient: Locked /run/dhclient/resolv.lock
Dec 29 05:20:13 ip-192-168-1-149 NET: dhclient: Locked /run/dhclient/resolv.lock
```

　

**しかし、この IAM Policy では、Shell を使ったリモート接続が可能です。**

````
> aws ssm start-session \
          --target i-04c2e28d623aab574 \
          --profile ssm

Starting session with SessionId: cliuser-sessionmanagertest-0d320f50f4b162dfa
sh-4.2$
````

　

このリモート接続を禁止するため、 IAM Policy の変更が必要です。

- `"ssm:SessionDocumentAccessCheck":"true"` を指定することで、`Resource` で指定したすべての条件が存在しないとエラーになる。つまり、Document の `LogTail` を指定して `start-session` コマンドを実行しないとダメということになり、**特定の Document のみが実行できる環境が出来上がる。**

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:StartSession"
            ],
            "Resource": [
                "arn:aws:ec2:ap-northeast-1:xxxx:instance/i-04c2e28d623aab574",
                "arn:aws:ssm:ap-northeast-1:xxxx:document/LogTail"
            ],
            "Condition":{
               "BoolIfExists":{
                   "ssm:SessionDocumentAccessCheck":"true"
               }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:TerminateSession",
                "ssm:ResumeSession"
            ],
            "Resource": [
                "arn:aws:ssm:*:*:session/${aws:username}-*"
            ]
        }
    ]
}
```

　

IAM Policy を変更したあと、想定通りエラーになりました。

```
> aws ssm start-session \
          --target i-04c2e28d623aab574 \
          --profile ssm

An error occurred (AccessDeniedException) when calling the StartSession operation: User: arn:aws:iam::xxxxxxxx:user/cliuser-sessionmanagertest is not authorized to perform: ssm:StartSession on resource: arn:aws:ec2:ap-northeast-1:xxxx:instance/i-04c2e28d623aab574 because no identity-based policy allows the ssm:StartSession action
```

　

Document を指定した接続は、引き続き可能です。

```
> aws ssm start-session \
          --target i-04c2e28d623aab574 \
          --document-name LogTail \
          --parameters logpath="/var/log/boot.log" \
          --profile ssm

Starting session with SessionId: cliuser-sessionmanagertest-068857acd8ebe0559
Dec 29 03:39:06 ip-192-168-1-149 NET: dhclient: Locked /run/dhclient/resolv.lock
Dec 29 04:07:40 ip-192-168-1-149 NET: dhclient: Locked /run/dhclient/resolv.lock
Dec 29 04:30:20 ip-192-168-1-149 NET: dhclient: Locked /run/dhclient/resolv.lock
Dec 29 04:55:16 ip-192-168-1-149 NET: dhclient: Locked /run/dhclient/resolv.lock
Dec 29 05:20:13 ip-192-168-1-149 NET: dhclient: Locked /run/dhclient/resolv.lock
```

　

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/session-manager-restrict-command-access.html#restrict-command-access-parameters-cli

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/getting-started-sessiondocumentaccesscheck.html



# sudo の制限

Session Manager のリモート接続では、Linux では `ssm-user` が利用されます。これは、デフォルトで sudo コマンドが利用できるので、root 権限が付与されています。sudoers を編集することで、sudo を禁止できます。詳細は次の Document を参照してみてください。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/session-manager-getting-started-ssm-user-permissions.html



# Start-Session の通知

最後に、Session Manager で `start-session` のコマンドを実行したときに、メールや Slack などへ通知ができます。これによって、予期していないタイミングやユーザーによるリモート接続を早期に検知できます。検知できたら、管理者によるセッションの削除や、IAM User の削除などが考えられます。

EventBridge を使ってイベントを拾えるので、そこから SNS だったり Lambda だったりで通知の仕組みが作れます。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/session-manager-auditing.html



# 参考 URL

Systems Manager のセキュリティのベストプラクティス
https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/security-best-practices.html

AWS アクセスキーを管理するためのベストプラクティス
https://docs.aws.amazon.com/ja_jp/accounts/latest/reference/credentials-access-keys-best-practices.html

IAM でのセキュリティのベストプラクティス
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html