

# PostgreSQL での Amazon RDS 委任拡張機能サポートの使用
<a name="RDS_delegated_ext"></a>

PostgreSQL に対して Amazon RDS 委任拡張機能のサポートを使用すると、拡張機能管理を `rds_superuser` である必要のないユーザーに委任できます。この委任拡張機能のサポートにより、`rds_extension` という新しいロールが作成され、他の拡張機能を管理するには、これをユーザーに割り当てる必要があります。このロールは、拡張機能を作成、更新、削除できます。

RDS DB インスタンスにインストール可能な拡張機能は、`rds.allowed_extensions` パラメータにそれらをリストアップして指定することができます。詳細については、「[Amazon RDS for PostgreSQL で PostgreSQL 拡張機能を使用する](https://docs.aws.amazon.com//AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.Extensions.html)」を参照してください。

`rds.allowed_delegated_extensions` パラメータを使用して、`rds_extension` ロールでユーザーが管理できる拡張機能のリストを制限できます。

委任拡張機能のサポートは、次のバージョンで利用できます。
+ すべての上位バージョン
+ 16.4 以降の 16 バージョン
+ 15.8 以降の 15 バージョン
+ 14.13 以降の 14 バージョン
+ 13.16 以降の 13 バージョン
+ 12.20 以降の 12 バージョン

**Topics**
+ [ユーザーに対する委任拡張機能のサポートの有効化](#RDSPostgreSQL.delegated_ext_mgmt)
+ [PostgreSQL での RDS 委任拡張機能サポートで使用される設定](#RDSPostgreSQL.delegated_ext_config)
+ [委任拡張機能のサポートの無効化](#RDSPostgreSQL.delegated_ext_disable)
+ [Amazon RDS 委任拡張機能サポートの使用のメリット](#RDSPostgreSQL.delegated_ext_benefits)
+ [PostgreSQL での Amazon RDS 委任拡張機能サポートの制限](#RDSPostgreSQL.delegated_ext_limit)
+ [特定の拡張機能に必要なアクセス許可](#RDSPostgreSQL.delegated_ext_perm)
+ [セキュリティに関する考慮事項](#RDSPostgreSQL.delegated_ext_sec)
+ [DROP EXTENSION CASCADE を無効化](#RDSPostgreSQL.delegated_ext_drop)
+ [委任拡張機能サポートを使用して追加できる拡張機能の例](#RDSPostgreSQL.delegated_ext_support)

## ユーザーに対する委任拡張機能のサポートの有効化
<a name="RDSPostgreSQL.delegated_ext_mgmt"></a>

ユーザーに対して委任拡張機能のサポートを有効にするには、以下を実行する必要があります。

1. **ユーザーに `rds_extension` ロールを付与する** – `rds_superuser` としてデータベースに接続し、次のコマンドを実行します。

   ```
   Postgres => grant rds_extension to user_name;
   ```

1. **委任されたユーザーが管理できる拡張機能のリストを設定する** – `rds.allowed_delegated_extensions` では、DB クラスターパラメータで `rds.allowed_extensions` を使用して、使用可能な拡張機能のサブセットを指定できます。これは、次のいずれかのレベルで実行できます。
   + クラスターまたはインスタンスパラメータグループで、AWS マネジメントコンソール または API を使用します。詳細については、「[Amazon RDS のパラメータグループ](USER_WorkingWithParamGroups.md)」を参照してください。
   + データベースレベルで次のコマンドを使用します。

     ```
     alter database database_name set rds.allowed_delegated_extensions = 'extension_name_1,
                         extension_name_2,...extension_name_n';
     ```
   + ユーザーレベルで次のコマンドを使用します。

     ```
     alter user user_name set rds.allowed_delegated_extensions = 'extension_name_1,
                         extension_name_2,...extension_name_n';
     ```
**注記**  
`rds.allowed_delegated_extensions` 動的パラメータを変更した後にデータベースを再起動する必要はありません。

1. **拡張機能の作成プロセス中に作成されたオブジェクトへのアクセスを委任されたユーザーに許可する** – 特定の拡張機能では、`rds_extension` ロールを持つユーザーがオブジェクトにアクセスする前に、追加のアクセス許可を付与する必要があるオブジェクトが作成されます。`rds_superuser` は、それらのオブジェクトへのアクセス権を委任されたユーザーに付与する必要があります。オプションの 1 つは、イベントトリガーを使用して、委任されたユーザーにアクセス許可を自動的に付与することです。

   **イベントトリガーの例**

   `rds_extension` を持つ委任ユーザーに、拡張機能の作成によって作成されたオブジェクトに対するアクセス許可の設定を必要とする拡張機能の使用を許可する場合は、次のイベントトリガーの例をカスタマイズし、委任されたユーザーに完全な機能へのアクセスを許可する拡張機能のみを追加できます。このイベントトリガーは template1 (デフォルトのテンプレート) で作成できるため、template1 から作成されたすべてのデータベースにそのイベントトリガーがあります。委任されたユーザーが拡張機能をインストールすると、このトリガーは拡張機能によって作成されたオブジェクトの所有権を自動的に付与します。

   ```
   CREATE OR REPLACE FUNCTION create_ext()
   
     RETURNS event_trigger AS $$
   
   DECLARE
   
     schemaname TEXT;
     databaseowner TEXT;
   
     r RECORD;
   
   BEGIN
   
     IF tg_tag = 'CREATE EXTENSION' and current_user != 'rds_superuser' THEN
       RAISE NOTICE 'SECURITY INVOKER';
       RAISE NOTICE 'user: %', current_user;
       FOR r IN SELECT * FROM pg_catalog.pg_event_trigger_ddl_commands()
       LOOP
           CONTINUE WHEN r.command_tag != 'CREATE EXTENSION' OR r.object_type != 'extension';
   
           schemaname = (
               SELECT n.nspname
               FROM pg_catalog.pg_extension AS e
               INNER JOIN pg_catalog.pg_namespace AS n
               ON e.extnamespace = n.oid
               WHERE e.oid = r.objid
           );
   
           databaseowner = (
               SELECT pg_catalog.pg_get_userbyid(d.datdba)
               FROM pg_catalog.pg_database d
               WHERE d.datname = current_database()
           );
           RAISE NOTICE 'Record for event trigger %, objid: %,tag: %, current_user: %, schema: %, database_owenr: %', r.object_identity, r.objid, tg_tag, current_user, schemaname, databaseowner;
           IF r.object_identity = 'address_standardizer_data_us' THEN
               EXECUTE pg_catalog.format('GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE %I.us_gaz TO %I WITH GRANT OPTION;', schemaname, databaseowner);
               EXECUTE pg_catalog.format('GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE %I.us_lex TO %I WITH GRANT OPTION;', schemaname, databaseowner);
               EXECUTE pg_catalog.format('GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE %I.us_rules TO %I WITH GRANT OPTION;', schemaname, databaseowner);
           ELSIF r.object_identity = 'dict_int' THEN
               EXECUTE pg_catalog.format('ALTER TEXT SEARCH DICTIONARY %I.intdict OWNER TO %I;', schemaname, databaseowner);
           ELSIF r.object_identity = 'pg_partman' THEN
               EXECUTE pg_catalog.format('GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE %I.part_config TO %I WITH GRANT OPTION;', schemaname, databaseowner);
               EXECUTE pg_catalog.format('GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE %I.part_config_sub TO %I WITH GRANT OPTION;', schemaname, databaseowner);
               EXECUTE pg_catalog.format('GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE %I.custom_time_partitions TO %I WITH GRANT OPTION;', schemaname, databaseowner);
           ELSIF r.object_identity = 'postgis_topology' THEN
               EXECUTE pg_catalog.format('GRANT SELECT, UPDATE, INSERT, DELETE ON ALL TABLES IN SCHEMA topology TO %I WITH GRANT OPTION;', databaseowner);
               EXECUTE pg_catalog.format('GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA topology TO %I WITH GRANT OPTION;', databaseowner);
               EXECUTE pg_catalog.format('GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA topology TO %I WITH GRANT OPTION;', databaseowner);
               EXECUTE pg_catalog.format('GRANT USAGE ON SCHEMA topology TO %I WITH GRANT OPTION;', databaseowner);
           END IF;
       END LOOP;
     END IF;
   END;
   $$ LANGUAGE plpgsql SECURITY DEFINER;
   
   CREATE EVENT TRIGGER log_create_ext ON ddl_command_end EXECUTE PROCEDURE create_ext();
   ```

## PostgreSQL での RDS 委任拡張機能サポートで使用される設定
<a name="RDSPostgreSQL.delegated_ext_config"></a>


| 設定名 | 説明 | デフォルト値 | 注意事項 | アクセス許可を変更または付与できるユーザー | 
| --- | --- | --- | --- | --- | 
| `rds.allowed_delegated_extensions` | このパラメータは、rds\$1extension ロールがデータベースで管理できる拡張機能を制限します。rds.allowed\$1extensions のサブセットである必要があります。 | 空の文字列 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/RDS_delegated_ext.html) このパラメータの設定の詳細については、「[ユーザーに対する委任拡張機能のサポートの有効化](#RDSPostgreSQL.delegated_ext_mgmt)」を参照してください。 | rds\$1superuser | 
| `rds.allowed_extensions` | このパラメータにより、カスタマーは RDS DB インスタンスにインストールできる拡張機能を制限できます。詳細については、「[PostgreSQL 拡張機能のインストールの制限](https://docs.aws.amazon.com//AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html#PostgreSQL.Concepts.General.FeatureSupport.Extensions.Restriction)」を参照してください。 | "\$1" | デフォルトでは、このパラメータは「\$1」に設定されています。つまり、RDS for PostgreSQL および Aurora PostgreSQL でサポートされているすべての拡張機能は、必要な権限を持つユーザーが作成できます。 空の場合、RDS DB インスタンスに拡張機能をインストールできないことを意味します。 | 管理者 | 
| `rds-delegated_extension_allow_drop_cascade` | このパラメータは、`rds_extension` を持つユーザーがカスケードオプションを使用して拡張機能を削除する機能を制御します。 | 化 | デフォルトで、`rds-delegated_extension_allow_drop_cascade` は `off` に設定されています。つまり、`rds_extension` を持つユーザーは、カスケードオプションを使用して拡張機能を削除することはできません。 その機能を許可するには、`rds.delegated_extension_allow_drop_cascade` パラメータを `on` に設定する必要があります。 | rds\$1superuser | 

## 委任拡張機能のサポートの無効化
<a name="RDSPostgreSQL.delegated_ext_disable"></a>

**部分的にオフにする**  
委任されたユーザーは、新しい拡張機能を作成することはできませんが、既存の拡張機能を更新することはできます。
+ DB クラスターパラメータグループでデフォルト値に `rds.allowed_delegated_extensions` をリセットします。
+ データベースレベルで次のコマンドを使用します。

  ```
  alter database database_name reset rds.allowed_delegated_extensions;
  ```
+ ユーザーレベルで次のコマンドを使用します。

  ```
  alter user user_name reset rds.allowed_delegated_extensions;
  ```

**すべてオフにする**  
ユーザーから `rds_extension` ロールを取り消すと、ユーザーは標準のアクセス許可に戻ります。ユーザーは拡張機能を作成、更新、削除できなくなります。

```
postgres => revoke rds_extension from user_name;
```

## Amazon RDS 委任拡張機能サポートの使用のメリット
<a name="RDSPostgreSQL.delegated_ext_benefits"></a>

PostgreSQL に対して Amazon RDS 委任拡張機能のサポートを使用すると、拡張機能管理を `rds_superuser` ロールを持たないユーザーに対してセキュアに委任できます。この機能には次の利点があります。
+ 選択したユーザーに拡張機能管理を簡単に委任できます。
+ これには `rds_superuser` ロールは不要です。
+ 同じ DB クラスター内の異なるデータベースに対して、異なる拡張機能セットをサポートする機能を提供します。

## PostgreSQL での Amazon RDS 委任拡張機能サポートの制限
<a name="RDSPostgreSQL.delegated_ext_limit"></a>
+ 拡張機能の作成プロセス中に作成されたオブジェクトは、拡張機能が正しく機能するために追加の権限が必要になる場合があります。
+ 一部の拡張機能は、デフォルトで `log_fdw`、`pg_cron`、`pg_tle`、`pgactive`、`pglogical`、`postgis_raster`、`postgis_tiger_geocoder`、`postgis_topology` などの委任された拡張機能ユーザーが管理することはできません。

## 特定の拡張機能に必要なアクセス許可
<a name="RDSPostgreSQL.delegated_ext_perm"></a>

次の拡張機能を作成、使用、または更新するには、委任されたユーザーに次の関数、テーブル、スキーマに対する必要な権限が必要です。


| 所有権またはアクセス許可が必要な拡張機能 | 関数 | テーブル | スキーマ | テキスト検索ディクショナリ | Comment | 
| --- | --- | --- | --- | --- | --- | 
| address\$1standardizer\$1data\$1us | なし | us\$1gaz、us\$1lex、us\$1lex、I.us\$1rules | なし | なし | なし | 
| amcheck | bt\$1index\$1check、bt\$1index\$1parent\$1check | なし | なし | なし | なし | 
| dict\$1int | なし | なし | なし | intdict | なし | 
| pg\$1partman | なし | custom\$1time\$1partitions、part\$1config、part\$1config\$1sub | なし | なし | なし | 
| pg\$1stat\$1statements | なし | なし | なし | なし | なし | 
| PostGIS | st\$1tileenvelope | spatial\$1ref\$1sys | なし | なし | なし | 
| postgis\$1raster | なし | なし | なし | なし | なし | 
| postgis\$1topology | なし | トポロジ、レイヤー | トポロジ | なし | 委任されたユーザーはデータベース所有者であること | 
| log\$1fdw | create\$1foreign\$1table\$1for\$1log\$1file | なし | なし | なし | なし | 
| rds\$1tools | role\$1password\$1encryption\$1type | なし | なし | なし | なし | 
| postgis\$1tiger\$1geocoder | なし | geocode\$1settings\$1default, geocode\$1settings | tiger | なし | なし | 
| pg\$1freespacemap | pg\$1freespace | なし | なし | なし | なし | 
| pg\$1visibility | pg\$1visibility | なし | なし | なし | なし | 

## セキュリティに関する考慮事項
<a name="RDSPostgreSQL.delegated_ext_sec"></a>

 `rds_extension` ロールを持つユーザーは、接続権限を持つすべてのデータベースの拡張機能を管理できることに注意してください。委任されたユーザーが 1 つのデータベースの拡張機能を管理する場合は、各データベースのパブリックからすべての権限を取り消し、その特定のデータベースの接続権限を委任されたユーザーに明示的に付与することをお勧めします。

 ユーザーが複数のデータベースから情報にアクセスできる拡張機能がいくつかあります。これらの拡張機能を `rds.allowed_delegated_extensions` に追加する前に、`rds_extension` を付与するユーザーにクロスデータベース機能があることを確認してください。例えば、`postgres_fdw` と `dblink` は、同じインスタンスまたはリモートインスタンス上のデータベース間でクエリを実行する機能を提供します。`log_fdw` は postgres エンジンのログファイルを読み取ります。これらは、インスタンス内のすべてのデータベース用であり、複数のデータベースからのスロークエリやエラーメッセージが含まれている可能性があります。`pg_cron` は、DB インスタンスでスケジュールされたバックグラウンドジョブの実行を有効にし、別のデータベースで実行するようにジョブを設定できます。

## DROP EXTENSION CASCADE を無効化
<a name="RDSPostgreSQL.delegated_ext_drop"></a>

 `rds_extension` ロールを持つユーザーがカスケードオプションを使用して拡張機能を削除する機能は、`rds.delegated_extension_allow_drop_cascade` パラメータによって制御されます。デフォルトで、`rds-delegated_extension_allow_drop_cascade` は `off` に設定されています。つまり、`rds_extension` ロールを持つユーザーは、以下のクエリに示すように、カスケードオプションを使用して拡張機能を削除することはできません。

```
DROP EXTENSION CASCADE;
```

これにより、拡張機能に依存するオブジェクト、およびそれらのオブジェクトに依存するすべてのオブジェクトが自動的に削除されます。カスケードオプションを使用しようとすると、エラーが発生します。

 その機能を許可するには、`rds.delegated_extension_allow_drop_cascade` パラメータを `on` に設定する必要があります。

 `rds.delegated_extension_allow_drop_cascade` 動的パラメータを変更しても、データベースを再起動する必要はありません。これは、次のいずれかのレベルで実行できます。
+ クラスターまたはインスタンスパラメータグループで、AWS マネジメントコンソール または API を使用します。
+ データベースレベルで次のコマンドを使用する。

  ```
  alter database database_name set rds.delegated_extension_allow_drop_cascade = 'on';
  ```
+ ユーザーレベルで次のコマンドを使用する。

  ```
  alter role tenant_user set rds.delegated_extension_allow_drop_cascade = 'on';
  ```

## 委任拡張機能サポートを使用して追加できる拡張機能の例
<a name="RDSPostgreSQL.delegated_ext_support"></a>
+ `rds_tools`

  ```
  extension_test_db=> create extension rds_tools;
  CREATE EXTENSION
  extension_test_db=> SELECT * from rds_tools.role_password_encryption_type() where rolname = 'pg_read_server_files';
  ERROR: permission denied for function role_password_encryption_type
  ```
+ `amcheck`

  ```
  extension_test_db=> CREATE TABLE amcheck_test (id int);
  CREATE TABLE
  extension_test_db=> INSERT INTO amcheck_test VALUES (generate_series(1,100000));
  INSERT 0 100000
  extension_test_db=> CREATE INDEX amcheck_test_btree_idx ON amcheck_test USING btree (id);
  CREATE INDEX
  extension_test_db=> create extension amcheck;
  CREATE EXTENSION
  extension_test_db=> SELECT bt_index_check('amcheck_test_btree_idx'::regclass);
  ERROR: permission denied for function bt_index_check
  extension_test_db=> SELECT bt_index_parent_check('amcheck_test_btree_idx'::regclass);
  ERROR: permission denied for function bt_index_parent_check
  ```
+ `pg_freespacemap`

  ```
  extension_test_db=> create extension pg_freespacemap;
  CREATE EXTENSION
  extension_test_db=> SELECT * FROM pg_freespace('pg_authid');
  ERROR: permission denied for function pg_freespace
  extension_test_db=> SELECT * FROM pg_freespace('pg_authid',0);
  ERROR: permission denied for function pg_freespace
  ```
+ `pg_visibility`

  ```
  extension_test_db=> create extension pg_visibility;
  CREATE EXTENSION
  extension_test_db=> select * from pg_visibility('pg_database'::regclass);
  ERROR: permission denied for function pg_visibility
  ```
+ `postgres_fdw`

  ```
  extension_test_db=> create extension postgres_fdw;
  CREATE EXTENSION
  extension_test_db=> create server myserver foreign data wrapper postgres_fdw options (host 'foo', dbname 'foodb', port '5432');
  ERROR: permission denied for foreign-data wrapper postgres_fdw
  ```