SSMで特定のInstanceへのRDPだけを許可する

SSMネタの第3弾です



ことの発端

社長：「〇〇のAWSアカウントの請求額、高いねんけど」

社長：「ClientVPNが一番しめてるわ」

社長：「なんとかならん？」



このClientVPNはプライベートにあるWindowsサーバへ接続するためで、利用するのはWindows上で動いてるソフトのメンテ用になってます

何かあるとき(トラブル時)しか使わないので常時ClientVPNを置いておくのはもったいないってことで、ClientVPNをやめてSSMに置き換えてみます（インフラ民はSSMでつないでるのでClientVPNは使ってません）



やりたいこと

- RDPだけに制限したい
- 接続先を特定のWindowsサーバだけにしたい



## RDPだけに制限する

### RDPのポートだけに接続するSSMドキュメントを作成する

 `AWS-StartPortForwardingSession` はどのポートでも接続することができるため、このドキュメントをコピーして新たにRDPのポートだけのドキュメントを作成します

以下はドキュメントのコンテンツ内容です

```json:rdp_forward_document.json
{
  "schemaVersion": "1.0",
  "description": "Document to start port forwarding session over Session Manager",
  "sessionType": "Port",
  "parameters": {
    "localPortNumber": {
      "type": "String",
      "description": "(Optional) Port number on local machine to forward traffic to. An open port is chosen at run-time if not provided",
      "allowedPattern": "^([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$",
      "default": "0"
    }
  },
  "properties": {
    "portNumber": "3389",
    "type": "LocalPortForwarding",
    "localPortNumber": "{{ localPortNumber }}"
  }
}
```

変更箇所は `parameters` から `portNumber` を削除して、`properties` の `portNumber` を **3389** の固定にするだけです

上記のファイルで新たにドキュメントを作成

```
aws ssm create-document --content file://rdp_forward_document.json --name RdpForwardingSession --document-type Session --document-format JSON
```



### IAMユーザを作成

IAMユーザはSSMだけ使えるようにしたいので「**プログラムによるアクセス**」で作成します

IAMユーザに作成したドキュメントを許可するポリシーを設定します

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ssm:StartSession",
            "Resource": [
                "arn:aws:ssm:ap-northeast-1:xxxxxxxxxxxx:document/RdpForwardingSession"
            ]
        },
        {
            "Effect": "Allow",
            "Action": "ssm:TerminateSession",
            "Resource": "arn:aws:ssm:ap-northeast-1:xxxxxxxxxxxx:session/${aws:username}-*"
        }
    ]
}
```

`ssm:TerminateSession` では自分自身のsessionだけを対象とするため、`${aws:username}-*` とします

試しにテストしてみます

```
$ aws ssm start-session --target i-XXXXXXXXXXXXXXXXX --document-name RdpForwardingSession

Starting session with SessionId: hoge-xxxxxxxxxxxxxxxxx
Port 13389 opened for sessionId hoge-xxxxxxxxxxxxxxxxx.
```

```
$ aws ssm start-session --target i-XXXXXXXXXXXXXXXXX --document-name AWS-StartPortForwardingSession --parameters 'portNumber=3389'

An error occurred (AccessDeniedException) when calling the StartSession operation: User: arn:aws:iam::xxxxxxxxxxxx:user/hoge is not authorized to perform: ssm:StartSession on resource: arn:aws:ssm:ap-northeast-1::document/AWS-StartPortForwardingSession
```

`AWS-StartPortForwardingSession` は AccessDenied となったので意図通りのようです

Document指定なしでも試してみます

```
$ aws ssm start-session --target i-XXXXXXXXXXXXXXXXX

Starting session with SessionId: hoge-xxxxxxxxxxxxxxxxx
Windows PowerShell
Copyright (C) 2016 Microsoft Corporation. All rights reserved.

PS C:\Windows\system32>
```

あれ・・・？ 入れる・・・


AWSのドキュメントを確認してみます
[AWS CLI でセッションドキュメントのアクセス許可チェックを適用する](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/getting-started-sessiondocumentaccesscheck.html)
> デフォルトでは、IAM ユーザーポリシーでセッションを開始するアクセス許可がアカウントのユーザーに付与されている場合、そのユーザーはこの `SSM-SessionManagerRunShell` SSM ドキュメントにアクセスできます。つまり、AWS CLI を使用して `start-session` コマンドを実行し、`--document-name` オプションでドキュメントを指定しない場合、システムは `SSM-SessionManagerRunShell` を使用してセッションを開始します。セッションは、ユーザーの IAM ポリシーで `SSM-SessionManagerRunShell` ドキュメントへのアクセスが明示的に許可されていない場合でも開始されます。

なるほど・・・

> デフォルトまたは任意のセッションドキュメントへのアクセスを制限するには、ユーザーにセッションドキュメントへのアクセスが明示的に許可されているかどうかを検証する条件要素を、ユーザーの IAM ポリシーに追加できます。この条件が適用されると、ユーザーは `start-session` AWS CLI コマンドの `--document-name` オプションの値を指定する必要があります。

ってことなのでポリシーに明示的に許可されているかどうの条件を追加します

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ssm:StartSession",
            "Resource": [
                "arn:aws:ssm:ap-northeast-1:XXXXXXXXXXXX:document/RdpForwardingSession"
            ],
            "Condition": {
                "BoolIfExists": {
                    "ssm:SessionDocumentAccessCheck": "true"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": "ssm:TerminateSession",
            "Resource": "arn:aws:ssm:ap-northeast-1:XXXXXXXXXXXX:session/${aws:username}-*"
        }
    ]
}
```

もう再度テスト

```
$ aws ssm start-session --target i-XXXXXXXXXXXXXXXXX

An error occurred (AccessDeniedException) when calling the StartSession operation: User: arn:aws:iam::xxxxxxxxxxxx:user/hoge is not authorized to perform: ssm:StartSession on resource: arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/i-XXXXXXXXXXXXXXXXX
```

AccessDenied となりました

これで1つ目の課題はOKになりました



## 接続先を特定のInstanceだけにする

2つ目の課題、接続先も制限したいのでResourceにInstanceのARNを追加します

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ssm:StartSession",
            "Resource": [
                "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/i-XXXXXXXXXXXXXXXXX",
                "arn:aws:ssm:ap-northeast-1:XXXXXXXXXXXX:document/RdpForwardingSession"
            ],
            "Condition": {
                "BoolIfExists": {
                    "ssm:SessionDocumentAccessCheck": "true"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": "ssm:TerminateSession",
            "Resource": "arn:aws:ssm:ap-northeast-1:XXXXXXXXXXXX:session/${aws:username}-*"
        }
    ]
}
```

試しに別のInstanceへ接続

```
$ aws ssm start-session --target i-YYYYYYYYYYYYYYYYY

An error occurred (AccessDeniedException) when calling the StartSession operation: User: arn:aws:iam::xxxxxxxxxxxx:user/hoge is not authorized to perform: ssm:StartSession on resource: arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/i-YYYYYYYYYYYYYYYYY
```



あとは、AccessKey/SecretAccessKey と aws cliコマンドをべた書きしたバッチファイルをメンテする人に渡せばOK

これでなんとかClientVPNを撤廃できそう



SSMネタが続いたので次回は違うものにしよう・・・