From d2646f7fffb8308079442f2d0e504339d3220fd6 Mon Sep 17 00:00:00 2001 From: Derrick Mehaffy Date: Fri, 17 Jan 2025 09:25:40 -0800 Subject: [PATCH 1/2] dev: start work on dynamic database configuration sample --- .../docs/dev-docs/configurations/database.md | 142 +++++++++++++++--- 1 file changed, 117 insertions(+), 25 deletions(-) diff --git a/docusaurus/docs/dev-docs/configurations/database.md b/docusaurus/docs/dev-docs/configurations/database.md index 1fcb4b7818..253606b3b6 100644 --- a/docusaurus/docs/dev-docs/configurations/database.md +++ b/docusaurus/docs/dev-docs/configurations/database.md @@ -35,40 +35,40 @@ The `/config/database.js|ts` file accepts 2 main configuration objects: ### `connection` configuration object -| Parameter | Description | Type | Default | -|----------------------------------------------------------|-------------------------------------------------------------------------------------------------------|-----------|---------| +| Parameter | Description | Type | Default | +|----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|---------| | `client` | Database client to create the connection.
Accepts the following values: | `String` | - | -| `connection` | Database [connection information](#connection-parameters) | `Object` | - | -| `debug` | Show database exchanges and errors. | `Boolean` | `false` | -| `useNullAsDefault`

_Optional, only for SQLite_ | Use `NULL` as a default value | `Boolean` | `true` | -| `pool`

_Optional_ | [Database pooling options](#database-pooling-options) | `Object` | - | -| `acquireConnectionTimeout`

_Optional_ | How long knex will wait before throwing a timeout error when acquiring a connection (in milliseconds) | `Integer` | `60000` | +| `connection` | Database [connection information](#connection-parameters) | `Object` | - | +| `debug` | Show database exchanges and errors. | `Boolean` | `false` | +| `useNullAsDefault`

_Optional, only for SQLite_ | Use `NULL` as a default value | `Boolean` | `true` | +| `pool`

_Optional_ | [Database pooling options](#database-pooling-options) | `Object` | - | +| `acquireConnectionTimeout`

_Optional_ | How long knex will wait before throwing a timeout error when acquiring a connection (in milliseconds) | `Integer` | `60000` | :::note Strapi only supports the following client values, and will automatically rewrite the `client` value to the following options before passing the configuration to Knex: -| `client` value | Actual package used | -|----------------|-----------------------------------------------------------------| -| sqlite | [better-sqlite3](https://www.npmjs.com/package/better-sqlite3) | -| mysql | [mysql2](https://www.npmjs.com/package/mysql2) | -| postgres | [pg](https://www.npmjs.com/package/pg) | +| `client` value | Actual package used | +|----------------|----------------------------------------------------------------| +| sqlite | [better-sqlite3](https://www.npmjs.com/package/better-sqlite3) | +| mysql | [mysql2](https://www.npmjs.com/package/mysql2) | +| postgres | [pg](https://www.npmjs.com/package/pg) | ::: #### Connection parameters The `connection.connection` object found in `./config/database.js` (or `./config/database.ts` for TypeScript) is used to pass database connection information and accepts the following parameters: -| Parameter | Description | Type | -|------------|-------------------------------------------------------------------------------------------------------------------------------|-----------------------| -| `connectionString`| Database connection string. When set, it overrides the other `connection.connection` properties. To disable use an empty string: `''`.
**Available in `v4.6.2`+** | `String` | -| `host` | Database host name. Default value: `localhost`. | `String` | -| `port` | Database port | `Integer` | -| `database` | Database name. | `String` | -| `user` | Username used to establish the connection | `String` | -| `password` | Password used to establish the connection | `String` | -| `timezone` | Set the default behavior for local time. Default value: `utc` [Timezone options](https://www.php.net/manual/en/timezones.php) | `String` | -| `schema` | Set the default database schema. **Used only for Postgres DB.** | `String` | -| `ssl` | For SSL database connection.
Use an object to pass certificate files as strings. | `Boolean` or `Object` | +| Parameter | Description | Type | +|--------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------| +| `connectionString` | Database connection string. When set, it overrides the other `connection.connection` properties. To disable use an empty string: `''`.
**Available in `v4.6.2`+** | `String` | +| `host` | Database host name. Default value: `localhost`. | `String` | +| `port` | Database port | `Integer` | +| `database` | Database name. | `String` | +| `user` | Username used to establish the connection | `String` | +| `password` | Password used to establish the connection | `String` | +| `timezone` | Set the default behavior for local time. Default value: `utc` [Timezone options](https://www.php.net/manual/en/timezones.php) | `String` | +| `schema` | Set the default database schema. **Used only for Postgres DB.** | `String` | +| `ssl` | For SSL database connection.
Use an object to pass certificate files as strings. | `Boolean` or `Object` | :::note Depending on the database client used, more parameters can be set (e.g., `charset` and `collation` for [mysql](https://github.com/mysqljs/mysql#connection-options)). Check the database client documentation to know what parameters are available, for instance the [pg](https://node-postgres.com/apis/client#new-client), [mysql](https://github.com/mysqljs/mysql#connection-options), and [better-sqlite3](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#new-databasepath-options) documentations. @@ -99,7 +99,7 @@ When using Docker, change the pool `min` value to `0` as Docker will kill any id The `settings` object found in `./config/database.js` (or `./config/database.ts` for TypeScript) is used to configure Strapi-specific database settings and accepts the following parameters: | Parameter | Description | Type | Default | -| ---------------- | --------------------------------------------------------------- | --------- | ------- | +|------------------|-----------------------------------------------------------------|-----------|---------| | `forceMigration` | Enable or disable the forced database migration. | `Boolean` | `true` | | `runMigrations` | Enable or disable database migrations from running on start up. | `Boolean` | `true` | @@ -522,7 +522,7 @@ For deployed versions of your application the database environment variables sho |-------------------------------------------------------|---------------------------------| | Virtual private server/virtual machine (e.g. AWS EC2) | `ecosystem.config.js` or `.env` | | DigitalOcean App Platform | `Environment Variables` table | -| Heroku | `Config vars` table | +| Heroku | `Config vars` table | ## Databases installation @@ -636,3 +636,95 @@ $ \c my_strapi_db_name admin_user # Grant schema privileges to the user $ GRANT ALL ON SCHEMA public TO my_strapi_db_user; ``` + +### Dynamic Database Credentials + +When using a DaaS (database as a service) such as AWS RDS, Google Cloud SQL, or Azure Database for PostgreSQL, you may need to use dynamic credentials. Dynamic credentials are temporary credentials that are generated by the database service and are rotated periodically. + +To use dynamic credentials with Strapi, you will need to build a custom database config for your use-case (for fetching the rotating token). You will also need to download the certificate bundle from the database service and store it in your project. + +The following is an example of how to use dynamic credentials with [AWS RDS IAM authentication](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.Connecting.html): + + + + + +```js title="./config/env/production/database.js" +// You will probably need to install this package +const { Signer } = require('@aws-sdk/rds-signer'); + +module.exports = ({ env }) => ({ + connection: async () => { + const rdsIamSigner = new Signer({ + hostname: env('DATABASE_HOST'), + port: env.int('DATABASE_PORT'), + username: env('DATABASE_USERNAME'), + }); + let token = await rdsIamSigner.getAuthToken(); + let expiresAt = new Date(Date.now() + 15 * 60 * 1000); // 15 minutes + + return { + client: env('DATABASE_CLIENT', 'postgres'), + connection: { + host: env('DATABASE_HOST'), + port: env.int('DATABASE_PORT'), + database: env('DATABASE_NAME'), + user: env('DATABASE_USERNAME'), + password: token, + ssl: { + // You can import the certificate bundle from the database service also + rejectUnauthorized: env.bool('DATABASE_SSL_SELF', false), + }, + expirationChecker: () => { + let remainingTime = expiresAt - new Date(); + return remainingTime <= 5; + } + } + } + } +}); +``` + + + + + +```ts title="./config/env/production/database.ts" +// You will probably need to install this package +import { Signer } from '@aws-sdk/rds-signer'; + +export default ({ env }) => ({ + connection: async () => { + const rdsIamSigner = new Signer({ + hostname: env('DATABASE_HOST'), + port: env.int('DATABASE_PORT'), + username: env('DATABASE_USERNAME'), + }); + let token = await rdsIamSigner.getAuthToken(); + let expiresAt = new Date(Date.now() + 15 * 60 * 1000); // 15 minutes + + return { + client: env('DATABASE_CLIENT', 'postgres'), + connection: { + host: env('DATABASE_HOST'), + port: env.int('DATABASE_PORT'), + database: env('DATABASE_NAME'), + user: env('DATABASE_USERNAME'), + password: token, + ssl: { + // You can import the certificate bundle from the database service also + rejectUnauthorized: env.bool('DATABASE_SSL_SELF', false), + }, + expirationChecker: () => { + let remainingTime = expiresAt - new Date(); + return remainingTime <= 5; + } + } + } + } +}); +``` + + + + From 204e610b044a642c035d28f99daad89d757b8a55 Mon Sep 17 00:00:00 2001 From: Derrick Mehaffy Date: Fri, 17 Jan 2025 09:28:04 -0800 Subject: [PATCH 2/2] dev: add experimental warning --- docusaurus/docs/dev-docs/configurations/database.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docusaurus/docs/dev-docs/configurations/database.md b/docusaurus/docs/dev-docs/configurations/database.md index 253606b3b6..0a81dd4390 100644 --- a/docusaurus/docs/dev-docs/configurations/database.md +++ b/docusaurus/docs/dev-docs/configurations/database.md @@ -639,6 +639,10 @@ $ GRANT ALL ON SCHEMA public TO my_strapi_db_user; ### Dynamic Database Credentials +:::caution 🚧 This configuration is considered experimental. +This configuration is considered experimental and has not been tested thoroughly in production environments. Please use with caution and report any issues you encounter. +::: + When using a DaaS (database as a service) such as AWS RDS, Google Cloud SQL, or Azure Database for PostgreSQL, you may need to use dynamic credentials. Dynamic credentials are temporary credentials that are generated by the database service and are rotated periodically. To use dynamic credentials with Strapi, you will need to build a custom database config for your use-case (for fetching the rotating token). You will also need to download the certificate bundle from the database service and store it in your project.