Amazon VPC から Private Space 内の Heroku Postgres へ PrivateLink で接続

[Connecting to a Private Heroku Postgres Database from an Amazon VPC](https://devcenter.heroku.com/articles/heroku-postgres-via-privatelink)の記事を書こうと思ったんですが、DevCenter の記事がやっぱりよくできているので、ざっくり翻訳してみました。そのほうが便利。なんか英語部分で違和感を感じる部分を、わいの感覚で修正していますが、まちがってはないはずです。

----
AWS PrivateLinkを使って、AWS VPCとPrivate Space内で動作する Heroku Postgres データベースを安全に接続する方法について紹介します。主に次の3つのステップでできちゃいます。

1. Private Space 内の Heroku Postgresに Endpointを作成
2. AWS VPC側に Endpoint Network Interfaceを作成
3. それぞれのEndpointの接続

接続の設定で、許可されているアカウントのリストを指定して、VPCからPostgresへのアクセスを制御できます。

> この機能を利用するには、接続先のHeroku Postgresと同じリージョンのサブネットで Amazon VPC Endpointを作成できなければなりません


# Heroku 前提条件

PrivateLinkのEndpointを作成するためには、次の前提条件を満たす必要があります

- Private Spaceが必要 - Heroku ダッシュボードかCLIで、Private Spaceを作れること
- Private Space 内で動作する Heroku Postgresを利用するHerokuアプリがあること - privateプランのHeroku Dynoが対象の Private Space内で動かせること

# Heroku側にEndpointを作成する

## 1. PrivateLink CLIプラグインを導入する

```bash
heroku plugins:install pg-privatelink
```

## 2. AWSのAccount ID情報を取得する

次のAWS CLIコマンドでAccount ID情報を取得できます。

```bash
aws sts get-caller-identity --output text --query 'Account'

123456789101
```

> '123456789101'というAccount IDはサンプルです

AWSのダッシュボードからでも、`Account Settings`からAccount IDを取得することができます。

<img width="1440" alt="5196-imported-1558542947-1546968690-Screen-Shot-2019-01-07-at-4.11.57-PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/6ff35828-9b1c-0ea1-c8da-17e9b86d8f1a.png">

## 3. PrivateLink endpointを作成する

PrivateLink endpointを作成するには、次のコマンドを実行します(置き換える値については気をつけること)。

```bash
heroku pg:privatelink:create POSTGRESQL_ADDON_NAME --aws-account-id ACCOUNT_ID --app APP_NAME
```

- `POSTGRESQL_ADDON_NAME` は、今回接続先となる Heroku Postgresのデータベース名です(例えば`postgresql-sushi-12345`みたいな)。
- `APP_NAME`は、対象のHerokuアプリ名です。
- `ACCOUNT_ID`は、先程確認したAWS Account IDです。このIDは次のいずれかのパターンにマッチしたものです。
 - account-id
 - account-id:user/username
 - account-id:role/rolename

> `--aws-account-id` フラグを複数指定することで、複数のアカウントの指定が可能です

出力結果は、次のようなものになります

```bash
heroku pg:privatelink:create postgresql-sushi-12345 --aws-account-id 123456789101:user/abc.xyz --app privatelink-vpc-endpoint-demo
Creating privatelink... done

Service Name: Provisioning
Status:       Provisioning

The privatelink is now being provisioned for postgresql-sushi-12345.
Run heroku pg:privatelink:wait -a APP to check the creation process.
```

> だいたい 5分から10分でPrivateLink endpointが利用可能になります。Provisionの状況は`heroku pg:privatelink:wait --app APP_NAME`でわかります。

## 4. endpoint のサービス名を取得する

PrivateLinkのendpointが利用可能になったら、次のコマンドで詳細を確認できます。

```bash
heroku pg:privatelink POSTGRESQL_ADDON_NAME --app APP_NAME
```

`POSTGRESQL_ADDON_NAME`は該当のPrivate Space内の Heroku Postgres名へ、`APP_NAME`は該当のHerokuアプリ名へ置き換えてください

実行すると、次のような結果が帰ってきます

```bash
heroku pg:privatelink postgresql-sushi-12345 --app privatelink-vpc-endpoint-demo
=== privatelinks for postgresql-sushi-12345
Service Name:         com.amazonaws.vpce.us-east-1.vpce-svc-0410a2e25933fe8ec
Status:               Operational

=== Whitelisted Accounts
ARN                                    Status
arn:aws:iam::123456789101:user/abc.xyz Active

Your privatelink is now operational.
You must now copy the Service Name and follow the rest of the steps listed in https://devcenter.heroku.com/articles/heroku-postgres-via-privatelink.
```

`Service Name`項目の内容(ここでは `com.amazonaws.vpce.us-east-1.vpce-svc-0410a2e25933fe8ec`となっている部分)をコピーしておいてください。Amazon VPC endpointを作成するときに必要になります。

# Amazon VPC endpointを作成する

次に、Amazon VPCダッシュボードで以下の手順を実行していきます。

## 1. セキュリティグループの設定

endpointには、適切なセキュリティルールを持つセキュリティグループが必要になりますので、Amazon VPCダッシュボードの[Security Groups]タブで[Create security group]をクリックしましょう。

<img width="1440" alt="5196-imported-1558542948-1547075343-Screen-Shot-2019-01-09-at-3.05.36-PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/d60f13e8-c91e-d31d-7e58-6c738a582ba2.png">

ここで、適切な(テキトーな)セキュリティグループ名と詳細を入力して、[Create]ボタンをクリックします

<img width="1440" alt="1550854536-1547159779-Screen-Shot-2019-01-10-at-2.34.22-PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/13c0e945-cfce-322d-7a28-7ee3f408576a.png">

次に、できあがったセキュリティグループを選択して、[Actions]から[Edit inbound rules]を実行します

<img width="1440" alt="1550854536-1547161352-Screen-Shot-2019-01-10-at-3.01.15-PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/d7e6d8d1-870c-721d-d15a-706a76580fff.png">

接続元のIPアドレス(CIDR)を入力して、`Port Range`内に`5432-5433`と指定したら[Save rules]をクリックします。

<img width="1440" alt="1550854536-1547156500-Screen-Shot-2019-01-10-at-1.39.03-PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/c6798843-1366-019f-0189-8840d22bb566.png">

## 2. endpointを作成する

VPCダッシュボードから[Endpoints]タブを選択して、[Create Endpoint]をクリックします。

<img width="1440" alt="5196-imported-1558542948-1547158556-Screen-Shot-2019-01-10-at-2.10.34-PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/4cd50fe5-c7cb-c146-2d66-b553af31bfb7.png">

`Create Endpoint`画面になったら、`Find service by name`を選択して、` Service Name`項目へ、先程確認(コピー)した `Service Name`を入力(ペースト)します。

次に、`Verify`ボタンをクリックして、利用可能なサブネットリストを確認します。

<img width="1440" alt="1558470081-Screen-Shot-2019-05-21-at-1.06.14-PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/e0c1da1d-8bad-19f2-ce60-4735fd377ebd.png">

[Security group]で、先ほど作成したセキュリティグループを指定して、[Create endpoint]ボタンをクリックします。

<img width="1440" alt="1558470122-Screen-Shot-2019-05-21-at-1.09.24-PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/8ea1084b-bf12-754b-7a20-5743dbd2a237.png">

endpointの作成して `Available`になるまで、だいたい5分から10分程度かかります。

<img width="1440" alt="5196-imported-1558542948-1547169381-Screen-Shot-2019-01-10-at-5.15.21-PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/7e39560f-82f9-720a-f112-c6504c9207b8.png">

# Amazon VPC endpointからHerokuへ接続する

Amazon VPC endpointが利用可能(Available)になったら、VPCがPrivate Space内のHeroku Postgresへ接続するためのURLを取得できるようになります。

まず、PrivateLink endpoint から、後半の17桁の`Endpoint ID`を取得します。また、このIDは大文字にして利用します。

もし`Endpoint ID`が`vpce-01c87ae3c05563935`だったときは、`01C87AE3C05563935`というようになります。

次のコマンドを実行して、必要なIDを取得します。

```bash
heroku config --app your_app_name | grep ENDPOINT_ID_HERE
```

このコマンドを実行することによって、接続先のHeroku Postgresへ接続するためのAWS VPC Endpoint URLが、次のフォーマット形式で表示されます。

> postgres://user:password@vpc-endpoint-dns-name:5432/database

このURLを使用して、AWS VPC内のアプリケーションからPrivate Heroku Postgresへ接続できます。コマンドの実行例は次の通り。

> 訳者補足: `privatelink-vpc-endpoint-demo`がHerokuアプリ名、`01C87AE3C05563935` が先程取得した Endpoint IDの後半17桁を大文字に変換したもの、です。

```bash
heroku config --app privatelink-vpc-endpoint-demo | grep 01C87AE3C05563935

DATABASE_ENDPOINT_01C87AE3C05563935_URL:           postgres://abcdefghijklmn:abcdefghijklmnopqrstuvwxyz123456789101112131415161718192021222324@ec2-3-83-63-168.compute-1.amazonaws.com:5432/dd0k757ojc5qt
```

> この機能を利用する場合に懸念事項があれば、サポートチケットを上げてください

# VPC endpointでEC2からHeroku Postgresへ接続する

VPC endpointが設定できたら、Heroku Postgresへ接続するためのEC2インスタンスを作成しましょう。

EC2ダッシュボード内の`Launch Instance`をクリックして、AMIとインスタンスタイプを選択します。ここでは、`Ubuntu t2.micro`インスタンスを指定しています。

<img width="1440" alt="5196-imported-1558542949-1547170100-Screen-Shot-2019-01-10-at-5.27.12-PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/eb7b61a4-40c0-0326-d24e-27c1e46e7011.png">

<img width="1440" alt="5196-imported-1558542949-1547170154-Screen-Shot-2019-01-10-at-5.28.55-PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/008f4222-fba3-0c33-9803-40bece0f9fb6.png">

インスタンスの詳細を設定するときに、VPCネットワークでは、先ほど作成したセキュリティグループを指定します。まちがいないことを確認できたら、`Review and Launch`をクリックしてEC2インスタンスを起動します。

<img width="1440" alt="5196-imported-1558542949-1547170256-Screen-Shot-2019-01-10-at-5.30.39-PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/800259eb-8fec-c591-e61d-3fe063842297.png">

インスタンスのステータスが`running`になったら、インスタンス作成時に指定したSSHキーペアを使ってインスタンスへssh接続してみましょう。

最初の段階で、postgresクライアントコマンドを次のようにインストールして設定します。

```bash
sudo apt-get -qq update && sudo apt-get install -y curl ca-certificates
curl -s https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
sudo apt-get -qq update
sudo apt-get install -y postgresql-client-10
```
EC2インスタンスから、Heroku Postgresへ接続するには、次のコマンドを実行します。

```bash
psql postgres://user:password@vpc-endpoint-dns-name:5432/database
```

正常に接続できると、次の画面のようになります。

![1550854537-1547172839-Screen-Shot-2019-01-10-at-6.08.25-PM.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37263/7528b5e4-0aa5-e8f9-a578-eea57dea22a4.jpeg)

# 制限事項

- 作成するAmazon VPC endpointは、Heroku Postgresデータベースと同一リージョンにあるsubnetへ作成しなければなりません
- 共通するAmazon VPCとHeroku Postgres VPCのAZ内に存在する Private Space Heroku Postgresへ接続できます
- Heroku Postgresへ安全にアクセスできるように、VPCのセキュリティを担保するのは、お客さまの役割となります

# `Private Space trusted IP ranges` との違い

Private Spaces では、`trusted IP ranges for data services`をβサービスとしてサポートしています。この機能は、Private Space外からHeroku Postgresへ接続する場合のオプションです。しかし、この機能を利用するにはHerokuチームへ依頼することが必要で、細かいアクセス制御を実施することができません。

AWS PrivateLinkで接続することによって、Heroku PostgresはVPCの一部として扱われ、ユーザー・ロールによるアクセス制御を実施できます。PrivateLinkにより接続できる場合には、この方式での接続を推奨します。

----

怪しい意訳があったら、とっととご連絡を。