はじめに
注文データベースは現在、アプリケーションの VPC 全体から接続を受け付け、アプリケーションはデータベース管理者のユーザーを使っています。ネットワークアクセスを絞り、注文の読み取りと追加はできても削除はできない専用ユーザーを作成します。
先にデータベース接続と SQL の実習を完了してください。この独立した環境には PostgreSQL プライマリ、3 件のサンプル注文、アプリケーションがあります。2 種類のアクセス境界をテストし、最後に練習用データベースを削除します。
認定試験との関連
この実習では、次の試験トピックに関する入門的な操作を練習します。
- Cloud Practitioner (CLF-C02) · タスク 3.5:ネットワークのアクセス境界としてセキュリティグループを理解します。
- Solutions Architect – Associate (SAA-C03) · タスク 1.3:最小権限を適用し、アプリケーションデータへのアクセスを制限します。
PostgreSQL のネットワーク接続元を制限する
このステップでは、VPC 全体からの広いインバウンドアクセスを、用意されたアプリケーションのセキュリティグループからのアクセスに置き換えます。
セキュリティグループは、どのネットワーク接続がデータベースに到達できるかを制御します。認証されたデータベースユーザーが実行できる SQL 操作は決定しません。その別の境界は次のステップで扱います。
用意された設定を読み込みます。
cd /home/labex/project
source database.env
データベースのグループと提供された 2 つのルール文書を確認します。
aws ec2 \
describe-security-groups \
--group-ids "$DB_SECURITY_GROUP_ID" \
--query 'SecurityGroups[].{Group:GroupName,Ingress:IpPermissions}'
cat broad-rule.json
cat application-rule.json
既存のルールは 10.70.0.0/16 から TCP ポート 5432 へのアクセスを許可するため、同じ VPC のほかのクライアントも接続を試せます。置換ルールは UserIdGroupPairs でアプリケーショングループを参照します。グループ参照は、その接続元グループに関連付けられたリソースからの通信を許可し、グループのルールをコピーするものではありません。
広いルールを削除し、アプリケーションのルールを追加します。
aws ec2 \
revoke-security-group-ingress \
--group-id "$DB_SECURITY_GROUP_ID" \
--ip-permissions file://broad-rule.json
aws ec2 \
authorize-security-group-ingress \
--group-id "$DB_SECURITY_GROUP_ID" \
--ip-permissions file://application-rule.json
アプリケーションの接続元から新しい接続をテストします。
psql \
"service=orders-db" \
--command 'SELECT count(*) FROM orders;'
3 が表示されます。続いてアプリケーショングループの外にある提供済みのクライアントをテストします。
psql \
"service=orders-db-external" \
--command 'SELECT count(*) FROM orders;'
外部クライアントの接続は失敗します。データベースのパスワードは有効ですが、接続元のネットワークが許可されていません。用意された接続名はこの 2 つの接続元を選びます。実習のアクセスルールは Terminal や AWS View への管理接続を変更しません。
AWS View では、広い VPC CIDR の代わりにアプリケーショングループが PostgreSQL の接続元として表示されます。
アプリケーション用のデータベースユーザーを作成する
このステップでは、管理権限や削除権限を与えずに、注文の読み取りと挿入を許可します。
PostgreSQL の ロールは権限を持つことができます。LOGIN のあるロールはデータベースユーザーとして認証できます。データベースロールは AWS IAM の ID とは別です。IAM は RDS リソースの管理を認可し、これらの SQL 権限は PostgreSQL 内部の操作を制御します。
提供された非公開のアプリケーションパスワードファイルで orders_app を作成します。--set は psql 変数を定義し、:'app_password' は値を SQL 文字列として安全に引用します。

概念図:ネットワークアクセス、ログイン、SQL 権限は別々の境界。
psql \
"service=orders-db" \
--set=ON_ERROR_STOP=1 \
--set=app_password="$(cat app-password.txt)" <<'SQL'
CREATE ROLE orders_app LOGIN PASSWORD :'app_password';
GRANT CONNECT ON DATABASE orders TO orders_app;
GRANT USAGE ON SCHEMA public TO orders_app;
GRANT SELECT, INSERT ON orders TO orders_app;
SQL
CONNECT はデータベース接続を許可します。USAGE はテーブルを含む名前空間であるスキーマを通じたアクセスを許可します。SELECT と INSERT は必要な注文操作だけを許可します。DELETE、UPDATE、CREATEDB、CREATEROLE、スーパーユーザー権限は与えていません。
非公開のパスワード値を psql に渡し、アプリケーションロールをテストします。
PGPASSWORD="$(cat app-password.txt)" psql \
"service=orders-db-app" \
--command 'SELECT current_user, count(*) FROM orders;'
ユーザー orders_app と 3 件の注文が表示されます。
存在しない注文 ID の削除を試します。誤って権限が広すぎた場合でも、この文は実際の注文を削除できません。
PGPASSWORD="$(cat app-password.txt)" psql \
"service=orders-db-app" \
--command 'DELETE FROM orders WHERE order_id = -1;'
permission denied for table orders が表示されます。これはネットワーク接続とデータベースへのログインが成功した後の失敗なので、先ほどの外部クライアントの接続失敗とは異なる境界を示します。
アプリケーションで制限されたユーザーを使用する
このステップでは、アプリケーションにロールを設定し、通常の注文操作が機能することを確認します。
2 つのエンドポイントフィールドを保持し、データベースユーザーとパスワードファイルの参照を変更します。
jq \
'.user = "orders_app" | .password_file = "app-password.txt"' \
app-config.json > app-config.new
mv app-config.new app-config.json
現在の注文一覧を読み取ります。
curl \
--silent \
--show-error \
--fail \
http://127.0.0.1:8080/application/orders | jq
元の 3 件の注文と user: orders_app が表示されます。
アプリケーションの HTTP エンドポイントから 1 件の注文を追加します。Content-Type はリクエスト本文が JSON であることを伝えます。
curl \
--silent \
--show-error \
--fail \
--request POST \
--header 'Content-Type: application/json' \
--data '{"order_id":104,"customer":"Kai","total":"5.00"}' \
http://127.0.0.1:8080/application/orders
saved: true と注文 ID 104 が表示されます。もう一度一覧を読み取ります。
curl \
--silent \
--show-error \
--fail \
http://127.0.0.1:8080/application/orders | jq
Kai の 5.00 の注文を含む 4 件が表示されます。AWS View には同じ保存データと制限されたアプリケーションユーザーが表示されます。権限を絞っても必要な動作は維持されています。

練習用データベースを削除する
このステップでは、サンプル注文と SQL ユーザーを含む実習用データベースを削除します。
aws rds \
delete-db-instance \
--db-instance-identifier orders-db \
--skip-final-snapshot \
--query 'DBInstance.DBInstanceIdentifier'
aws rds \
wait db-instance-deleted \
--db-instance-identifier orders-db
aws rds \
describe-db-instances \
--query 'DBInstances[].DBInstanceIdentifier'
[] が表示されます。エンジンとロールは削除され、アプリの古いエンドポイントは接続できません。提供されたネットワークと制限済みのデータベースグループを保持してください。後片付けのためにアクセスを再び開放する必要はありません。
まとめ
PostgreSQL 接続をアプリケーションの接続元に限定し、アプリに専用の制限されたロールを割り当てました。外部クライアントは接続できず、アプリのロールは注文を削除できませんでしたが、通常の読み取りと挿入は機能しました。その後、ネットワークの制限を保持して練習用データベースを削除しました。
次の実習では、手動の RDS スナップショットを新しいデータベースに復元して、失われた注文を取り戻します。


