AWS Verified Access で VPN-less な世界を体験してみた

この記事は [**AWS Community Builders Advent Calendar 2022**](https://adventar.org/calendars/7633) および [**セゾン情報システムズ Advent Calendar 2022**](https://qiita.com/advent-calendar/2022/sisco) の 16 日目の記事です

## 2023/10/10
AWS Verified Access が東京リージョンでも利用可能になりました！

https://aws.amazon.com/jp/about-aws/whats-new/2023/10/aws-verified-access-two-regions/

## 2023/5/2 追記
2023/4/28 に AWS Verified Access が一般利用可能になりました。ただし、東京リージョンおよび大阪リージョンではまだ利用できません。

https://aws.amazon.com/jp/blogs/networking-and-content-delivery/introducing-aws-verified-access-general-availability/

GA に伴い、以下の機能が追加されています。

* [Verified Access instance に対する AWS WAF のサポート](https://docs.aws.amazon.com/verified-access/latest/ug/waf-integration.html)
* [署名済み ID コンテキストをアプリケーションエンドポイントに渡せるように (`x-amzn-ava-user-context` ヘッダー)](https://docs.aws.amazon.com/verified-access/latest/ug/user-claims-passing.html)
:::note warn
以降の内容は 2022/12/16 時点の情報で作成しています。
:::

## AWS Verified Access とは
AWS Verified Access (AVA) は AWS re:Invent 2022 で発表された新サービスです。

https://aws.amazon.com/jp/blogs/news/aws-verified-access-preview-vpn-less-secure-network-access-to-corporate-applications/

VPN を使用せずに企業内アプリケーションにセキュアなアクセスを提供するサービスで、[AWS Zero Trust security principles](https://aws.amazon.com/jp/security/zero-trust/) に基づいて設計されています。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671114433635/bpuMyB15X.png)
[Leaping ahead: The power of cloud network innovation (NET211-L)](https://d1.awsstatic.com/events/Summits/reinvent2022/NET211-L_Leaping-ahead-The-power-of-cloud-network-innovation.pdf) より引用

## ユーザーの課題
Zero Trust はデータへのアクセスがネットワークの場所だけに基づいて行われるべきではないという考えを中心としたセキュリティモデルです。一般的には Identity を中心としたコントロールに置き換えられ、ネットワークのの場所への依存度は低下しますが、ネットワーク制御が不要という意味ではありません。

企業ネットワークが信頼できることを前提に、企業ネットワークに接続していることを正義、神とするのが伝統的なアプリケーションへの接続方法でした。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671115658855/rXLeoNlaA.png)
[Introducing AWS Verified Access: Secure connections to your apps (NET214)](https://www.youtube.com/watch?v=Kkxn-bAIlnI) より引用

今日の Zero Trust モデルでは企業ネットワークは引き続き重要ではあるものの、VPN によるリモート接続、アプリケーションでの ID ベースの制御、クライアントデバイスの制御など多層防御の形で実装されています。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671115735418/33AY1oNqG.png)
[Introducing AWS Verified Access: Secure connections to your apps (NET214)](https://www.youtube.com/watch?v=Kkxn-bAIlnI) より引用

こういった実装におけるユーザーの課題は複数の接続ポリシーを管理する必要があり、さらには担当者も別々に存在し、管理が複雑になるこということです。これを解決するサービスが AWS Verified Access です。

## AWS Verified Access の概要
### 主要コンポーネント
AWS Verified Access がどのように動作するかを図示しました。
![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671116628789/Z8z1-cSlO.png)
* **Verified Access Instances:** Trust Provider と Group を関連付けるためのリソース
* **Verified Access Groups:** 同様のセキュリティ要件を持つアプリケーションをグループ化するためのリソース
* **Verified Access Endpoints:** クライアントから見たエンドポイントで、1アプリケーションに紐づく
* **Access Policies:** アプリケーションへのアクセス可否を決定するためのルールを Cedar で記述
* **Trust Providers:** ユーザー ID またはデバイスのセキュリティ状態取得する外部サービスを登録

ユーザーはアプリケーションアクセスするために Verified Access Endpoint にリクエストを送信します。Verified Access は Trust Provider からユーザー ID やデバイス情報などを取得し、Verified Access Groups および Endpoint に設定された Access Policy を評価します。アクセスが許可されている場合、リクエストはエンドポイントを介してアプリケーションに送信されます。

### サポートする Trust Provider
AWS Verified Access では User Identity Trust Provider と Device Management Trust Provider を使用できます。これにより所属組織などのユーザー ID に紐づく属性や OS パッチの適用バージョンなどのデバイス管理情報もとにアクセスを制御できます。

User Identity Trust Provider は AWS IAM Identity Center や Okta などのOpenID Connect (OIDC) 互換プロバイダーをサポートします。

Device Management Trust Provider は Preview 開始時点では Jamf (macOS) と CrowdStrike (Windows 10/11) がサポートされています。これらのパートナーからデータを取得するにはクライアント端末に AWS Verified Access 用のブラウザ拡張機能をインストールする必要があります。詳細はドキュメントをご確認ください。

https://docs.aws.amazon.com/verified-access/latest/ug/trust-data-third-party-trust.html#trust-data-browser

1 つのVerified Access Instance に対し、1 つの User Identity Trust Provider と複数の Device Management Trust Provider を紐づけることができます。
 
### Cedar
Access Policy は AWS が開発した Cedar (しーだー) と呼ばれるポリシー言語で記述する必要があります。

* effect
  * permit (許可) or forbid (拒否) を指定します
* scope
  * effect が適用される範囲を指定します
  * AWS Verified Access では使用されないため、常に未定義とする必要があります
* condition clause
  * effect が適用される条件句を指定します

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671120424467/bO9dKaTlD.png)

詳しいポリシーの定義方法やポリシーの例はドキュメントを参照ください。

https://docs.aws.amazon.com/verified-access/latest/ug/auth-policies.html


また Cedar は同じく re:Invent 2022 で発表された Amazon Verified Permissions でも採用されています。ブレイクアウトセッションでは Cedar が開発された背景などがより詳しく解説されていますので、興味のある方はご参照ください。

https://www.youtube.com/watch?v=k6pPcnLuOXY

## お試し構成
ほぼ先ほどの画像と変わりませんが、VPN-less な世界を体験するために以下のような構成で検証を行いました。
![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671118337172/A6QpOseFB.png)

* Trust Provider: Auth0 を使用
* Access Policy: 会社ドメインのメールアドレスのみを許可するシンプルなポリシー
* 企業アプリケーション: ALB から簡易的な HTML を返す Lambda 関数を起動

User Identity Trust Provider は、IAM Identity Center (旧称 AWS SSO) または OIDC を指定できます。恐らく簡単に設定できるのは IAM Identity Center なのですが、Verified Access を設定するリージョンと同じリージョンで有効になっている必要があります。

私の環境では IAM Identity Center を東京リージョンで設定していましたが、Preview 開始時点で AWS Verified Access は東京リージョンをサポートしていません。そのため今回は Auth0 を選択しました。

## 体験してみた
### アプリケーションの準備
#### Lambda 関数
以下のような Lambda 関数を準備します。

```lambda_function.py
def lambda_handler(event, context):
    response = {
       'statusCode': 200,
       'statusDescription': '200 OK',
       'isBase64Encoded': False,
       'headers': {
           'Content-Type': 'text/html; charset=utf-8'
        }
    }

    response['body'] = """<html>
    <head>
    <title>Hello World!</title>
    <style>
    html, body {
       margin: 0; padding: 0;
       font-family: arial; font-weight: 700; font-size: 3em;
       text-align: center;
    }
    </style>
    </head>
       <body>
          <p>Hello World!</p>
      </body>
    </html>"""
    return response
```

#### ALB
ターゲットタイプを Lambda 関数に設定したターゲットグループを作成します。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671123723083/Mw8SaJHAo.png)

作成した Lambda 関数をターゲットに登録します。

![](https://camo.qiitausercontent.com/fce62bd2c250a37f7e75930e806592c5d335e4a6/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3132353130352f31376537346631352d613534342d666566322d333933382d3561376465313265326264612e706e67)

次に Internal Facing な ALB を作成します。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671124029067/5lFSEYwin.png)

リスナーとルーティングの設定で、デフォルトアクションに作成したターゲットグループを指定して ALB を作成します。

![](https://camo.qiitausercontent.com/b638578ccb857d5ef55fa5364f4b35ba32bd3327/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3132353130352f61383138373664302d626239382d326162622d313333612d6333653763313534633565382e706e67)

### Trust Provider の登録
まず Auth0 で新規アプリケーションを作成します。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671124351482/TD1T0jwEK.png)

Allowed Callback URLs に `https://<Verified Endpoint に設定するドメイン>/oauth2/idpresponse` を設定し、変更を保存します。

AWS Verified Access の設定メニューは VPC コンソールにあります。
最初に Verified Access 信頼プロバイダーを新規作成します。

任意の Name タグとポリシー参照名を入力します。
ポリシー参照名はアクセスポリシー設定時に使用します。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671125523957/GXK6DZixh.png)

ユーザーの信頼プロバイダータイプで OIDC を選択し、発行者以降は以下のように入力して作成します。

|項目名|設定値|
|---|---|
|発行者|https://\<your_auth0_domain\>/|
|認証エンドポイント|https://\<your_auth0_domain\>/authorize|
|トークンエンドポイント|https://\<your_auth0_domain\>/oauth/token|
|ユーザーエンドポイント|https://\<your_auth0_domain\>/userinfo
|クライアント ID|Auth0 のアプリケーション設定から取得した値|
|クライアントシークレット|Auth0 のアプリケーション設定から取得した値|
|範囲|openid profile email|

### Verified Access Insntace の作成
Verified Access インスタンスは Trust Provider と Group を関連付けるためのリソースです。任意の Name タグと先ほど作成した信頼プロバイダーを指定し、インスタンスを作成します。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671126170754/Ik9EyLL8n.png)

アクセスログの記録設定は Verified Access インスタンスに対して設定します。S3/CloudWatch/Kinesis Firehose を出力先として設定できますが、デフォルトではすべて無効になっています。

インスタンス作成後、Verified Access インスタンスのログ記録設定タブから必要に応じてログ記録を有効化してください。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671126658902/K5nZgYXNa.png)


#### 参考情報
信頼プロバイダーは必須項目ではなく、オプションになっています。信頼プロバイダーの設定有無に限らず、デフォルトで HTTP リクエストに関する情報を取得でき、アクセスポリシーに利用できます。

https://docs.aws.amazon.com/verified-access/latest/ug/trust-data-default-context.html

### Verified Access Group の作成
Verified Access グループは複数のアプリケーションをグループ化するためのリソースです。グループを作成することで同様のセキュリティ要件を持つアプリケーションごとにポリシーを定義する必要がなくなります。

任意の Name タグとグループにアタッチするインスタンスを選択、ポリシーの詳細は以下を入力して、グループを作成します。context キーの `Auth0` は信頼プロバイダー作成時に指定したポリシー参照名であることに注意してください。

```js
permit(principal, action, resource) when {
   context.Auth0.email like "*@example.com"
}
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671127463288/lUUM_CY_S.png)

### Verified Access Endpoint の作成
最後にクライアントがアクセスするためのエンドポイントを作成します。

* 任意の Name タグを設定し、関連付ける Verified Access グループを選択します
* アプリケーションドメインにエンドポイントに設定する DNS 名を入力します
* ドメイン証明書の ARN で入力した DNS 名に対応する ACM 証明書を選択します

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671128437165/mMj1pLA4r.png)

:::note warn
検証時点では対応するドメインのワイルドカードを証明書を選択するとエンドポイントの作成でエラーが発生しました。仕様か不具合かはわかりませんが、今後動作が変わる可能性があります。
:::

画面下部に進み、エンドポイントの詳細を入力します。

* アタッチメントタイプで VPC を選択します
* セキュリティグループでエンドポイントのに関連付けるセキュリティグループを選択します
  * (HTTPS のインバウント通信を許可してください)
* エンドポイントドメインプレフィックスは任意の値を入力します。
* エンドポイントタイプで ロードバランサー を選択します
* プロトコルは `HTTP`、ポート `80` を入力します
* サブネットでロードバランサーを作成したプライベートサブネットを選択します

ポリシーの詳細は何も入力せずにエンドポイントの作成を完了します。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671128958830/ELqf5i49Q.png)

アクセスポリシーはグループ、エンドポイントの両方に設定が可能です。グループレベルポリシーはエンドポイントポリシーに継承されるため、同一のセキュリティ要件はグループポリシーに設定し、個別の要件はエンドポイントポリシーに追加するといった運用が可能です。

エンドポイントの作成後、ステータスが Active になるまで気長に待ちましょう。
 (そこそこ時間がかかります) 

## DNS レコードの登録
エンドポイントを作成すると、`<endpoint_domain_prefix>.edge-xxxxxxxxxxxxxxxxx.vai-xxxxxxxxxxxxxxxxx.prod.verified-access.<region>.amazonaws.com` といった形式のエンドポイントドメインが払い出されます。この値をアプリケーションドメインに対する CNAME レコードとして登録します。

以下は Route 53 でのレコード作成例です。
![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671129914863/uCIRaiqR1.png)

### 動作確認
エンドポイントに設定したアプリケーションドメインにアクセスすると、Auth0 でホストされるログイン画面に遷移します。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671129835691/YDD5DMml1.png)

最初にアクセスポリシーに設定したドメインのメールアドレスでログインしてみると、、、Hello World の応答が返ってきました！

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671130155043/3qEJjuRbv.png)

アクセスポリシーに設定したドメイン以外のメールアドレスでログインすると、想定どおり 403 の応答が返ることを確認できました。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671130301738/cAo0nb3bC.png)

アクセスログには以下のようなレコードが記録されます。(許可の例)

<details>
  <summary><b>クリックで展開します</b></summary>

  ```json
{
    "activity": "Access Granted",
    "activity_id": "1",
    "category_name": "Application Activity",
    "category_uid": "8",
    "class_name": "Access Logs",
    "class_uid": "208001",
    "device": {
        "ip": "xxx.xxx.xxx.xxx",
        "type": "Unknown",
        "type_id": 0
    },
    "duration": "0.311",
    "end_time": "1671130082108",
    "time": "1671130082108",
    "http_request": {
        "http_method": "GET",
        "url": {
            "hostname": "ava.example.com",
            "path": "/",
            "port": 443,
            "scheme": "https",
            "text": "https://ava.example.com:443/"
        },
        "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
        "version": "HTTP/1.1"
    },
    "http_response": {
        "code": 200
    },
    "identity": {
        "authorizations": [
            {
                "decision": "Allow",
                "policy": {
                    "name": "inline"
                }
            },
            {
                "decision": "Allow",
                "policy": {
                    "name": "inline"
                }
            }
        ],
        "idp": {
            "name": "Auth0",
            "uid": "vatp-xxxxxxxxxxxxxxxxx"
        },
        "user": {
            "email_addr": "hayao_k@example.com",
            "name": "hayao_k@example.com",
            "uuid": "auth0|xxxxxxxxxxxxxxxxxxxxxxx"
        }
    },
    "message": "",
    "metadata": {
        "uid": "Root=x-xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx",
        "logged_time": 1671130383629,
        "version": "",
        "product": {
            "name": "Verified Access",
            "version": "0.1",
            "vendor_name": "AWS"
        }
    },
    "ref_time": "2022-12-15T18:48:02.108004Z",
    "proxy": {
        "ip": "xxx.xxx.xxx.xxx",
        "port": 443,
        "svc_name": "Verified Access",
        "uid": "vai-xxxxxxxxxxxxxxxxx"
    },
    "severity": "Informational",
    "severity_id": "1",
    "src_endpoint": {
        "ip": "xxx.xxx.xxx.xxx",
        "port": 20042
    },
    "start_time": "1671130081797",
    "status_code": "100",
    "status_details": "Access Granted",
    "status_id": "1",
    "status": "Success",
    "type_uid": "20800101",
    "type_name": "AccessLogs: Access Granted",
    "unmapped": null
}
```
</details>

## 価格
アプリケーションが AWS Verified Access に関連付けられた時間 (アプリケーション時間) とデータ処理量に対して請求されます。アプリケーション時間は Verified Access endpoint の稼働時間と言い換えることもできます。2022/12/16 時点でバージニア北部では以下の料金になっています。

* Per-app hours: $0.27/hr
* Per-GB data processing: $0.02/GB

1 つのアプリケーションが 100 GB 処理した場合、月額は約 $200 です。
730 * 0.27 + 100 * 0.02 = $199.1/月

https://aws.amazon.com/jp/verified-access/pricing/

## 参考
https://aws.amazon.com/jp/verified-access/

https://www.youtube.com/watch?v=Kkxn-bAIlnI

https://www.youtube.com/watch?v=aHClvCQfuPs

以上です。
参考になれば幸いです。