AWS Client VPN 接続承認で接続元IPアドレスを制限する

# 初めに

クライアント VPN では、不特定多数からアクセスされる危険がある。この記事ではクライアント VPN の接続承認という機能を使って IP アドレスでクライアントに制限を課す。

# 接続承認

クライアント接続ハンドラーで Lambda 関数を指定することで、デバイス、ユーザー、および接続属性に基づいて、新しい接続を許可するカスタムロジックを組み込むという機能。

## 要件と考慮事項

- Lambda 関数の名前は、 `AWSClientVPN-` という名前で始めなければならない
- Lambda 関数は、クライアント VPN エンドポイントと同じ AWS リージョンおよび同じ AWS アカウントに存在しなければならない
- Lambda 関数は 30 秒後にタイムアウトし、この値は変更できない
- Lambda 関数は同期的に呼び出される。デバイスとユーザーの認証後、および承認ルールが評価される前に呼び出される
- 新しい接続に対して Lambda 関数が呼び出され、クライアント VPN が関数から期待されるレスポンスを取得しない場合、クライアント VPN は接続要求を拒否する（例えば関数のエラー、タイムアウトなどが起こると、接続は失敗する）
- Lambda 関数にプロビジョニングされた同時実行数を設定して、レイテンシーの変動なしに関数をスケーリングできるようにすることが推奨される

## Lambda 関数が受け取るイベント

```
{
    "connection-id": <connection ID>,
    "endpoint-id": <client VPN endpoint ID>,
    "common-name": <cert-common-name>,
    "username": <user identifier>,
    "platform": <OS platform>,
    "platform-version": <OS version>,
    "public-ip": <public IP address>,
    "client-openvpn-version": <client OpenVPN version>,
    "schema-version": "v1"
}
```

- `connection-id` — クライアント VPN エンドポイントへのクライアント接続の ID

- `endpoint-id` — クライアント VPN エンドポイントの ID

- `common-name` — デバイス識別子。デバイス用に作成するクライアント証明書では、共通名によってデバイスが一意に識別される

- `username` — ユーザー ID (該当する場合)。Active Directory 認証の場合、これはユーザー名、SAML ベースのフェデレーション認証の場合は NameID （相互認証の場合、このフィールドは空）

- `platform` — クライアントのOS

- `platform-version` — OSのバージョン。クライアント VPN サービスは、クライアントがクライアント VPN エンドポイントに接続するとき、およびクライアントが Windows プラットフォームを実行しているときに --push-peer-info ディレクティブが OpenVPN クライアント設定に存在する場合に値を提供する

- `public-ip` — 接続デバイスのパブリック IP アドレス

- `client-openvpn-version` — クライアントが使用している OpenVPN バージョン

- `schema-version` — スキーマバージョン。デフォルトは v1

## Lambda 関数の戻り値

Lambda関数は次の値を返さなければならない。

```
{
    "allow": boolean,
    "error-msg-on-failed-posture-compliance": "",
    "posture-compliance-statuses": [],
    "schema-version": "v1"
}
```

- `allow` — 新しい接続を許可または拒否するかどうかを示すブール値 

- `error-msg-on-failed-posture-compliance` — Lambda 関数によって接続が拒否された場合に、クライアントにステップとガイダンスを提供するために使用できる最大 255 文字の文字列。

- `posture-compliance-statuses` ― 接続デバイスのステータスのリスト。デバイスの体制評価カテゴリ (compliant、quarantined、unknown など) に従って、ステータス名を定義。各名前の最大長は 255 文字。最大 10 個のステータスを指定可能

- `schema-version` — スキーマバージョン。デフォルトは v1

# 設定手順

## Lambda 関数の作成

`123.456.789.0/24` の範囲であれば、接続を許可する。

```python
import json

def lambda_handler(event, context):
    public_ip = event['public-ip']
    ip = "123.456.789."
    ips = [ip + str(i) for i in range(256)]

    if public_ip in ips:
        flag = 1
    else:
        flag = 0
    
    return {
                "allow": bool(flag),
                "error-msg-on-failed-posture-compliance": "Your IP is not allowed to access. Please Contact mail@example.co.jp",
                "posture-compliance-statuses": ["compliant"],
                "schema-version": "v1"
            }

```

## クライアント接続ハンドラの設定

クライアント VPN エンドポイント作成時でも、作成後でもどちらでも設定できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/f2ee6af5-dbbc-d1d6-c696-f0bc406256cf.png)

**失敗時**
OpneVPNに設定したメッセージが出力されている。

```
Sat May 29 08:28:31 2021 AUTH: Received control message: AUTH_FAILED,Your IP is not allowed to access. Please Contact mail@example.co.jp
```

**成功時**
問題なくOpenVPNの成功時のメッセージが出力されている。

```
Sat May 29 08:30:06 2021 MANAGEMENT: >STATE:1622244606,ASSIGN_IP,,10.1.0.130,,,,
```

# 参考記事

https://docs.aws.amazon.com/ja_jp/vpn/latest/clientvpn-admin/connection-authorization.html

https://dev.classmethod.jp/articles/aws-client-vpn-supports-client-connect-handler/