diff --git a/docs/features/auth/.pages b/docs/features/auth/.pages new file mode 100644 index 00000000..018c1905 --- /dev/null +++ b/docs/features/auth/.pages @@ -0,0 +1,8 @@ +nav: + - 'index.md' + - 'configuration.md' + - 'password.md' + - 'rds-iam.md' + - 'mtls.md' + - '...' +title: Authentication diff --git a/docs/features/auth/azure-workload.md b/docs/features/auth/azure-workload.md new file mode 100644 index 00000000..d8c15a46 --- /dev/null +++ b/docs/features/auth/azure-workload.md @@ -0,0 +1,29 @@ +--- +icon: material/microsoft-azure +--- + +# Azure Workload Identity + +PgDog supports using temporary credentials provided by Azure Workload Identity to connect to PostgreSQL running on Azure. This uses the [Azure SDK](https://github.com/Azure/azure-sdk-for-rust) and supports fetching credentials from the environment. + +To use Workload Identity authentication, configure it on each user in [`users.toml`](../../configuration/users.toml/users.md): + +=== "users.toml" + + ```toml + [[users]] + name = "pgdog" + database = "prod" + password = "hunter2" + server_auth = "azure_workload_identity" + ``` + +=== "Helm chart" + + ```yaml + users: + - name: pgdog + database: prod + password: hunter2 + serverAuth: azure_workload_identity + ``` diff --git a/docs/features/auth/configuration.md b/docs/features/auth/configuration.md new file mode 100644 index 00000000..dd4b39ea --- /dev/null +++ b/docs/features/auth/configuration.md @@ -0,0 +1,154 @@ +--- +icon: material/account-cog +--- + +# User configuration + +By default, client connections will use password authentication encrypted with SCRAM-SHA-256. This method is secure and recommended for production usage. + +PgDog supports using other methods, e.g., `md5`, `plain` and `trust`, which you can change with configuration: + +=== "pgdog.toml" + + ```toml + [general] + auth_type = "scram" # or "md5", "plain", "trust" + ``` + +=== "Helm chart" + + ```yaml + authType: scram # or "md5", "plain", "trust" + ``` + +## Configuring users + +The [`users.toml`](../../configuration/users.toml/users.md) configuration file follows a TOML list structure. To allow a user to connect to PgDog, add a `[[users]]` section with the user name, password and a corresponding database name (located in [`pgdog.toml`](../../configuration/pgdog.toml/databases.md)) to `users.toml`, for example: + +=== "users.toml" + + ```toml + [[users]] + name = "pgdog" + database = "pgdog" + password = "hunter2" + ``` + +=== "Helm chart" + + ```yaml + users: + - name: pgdog + database: pgdog + password: hunter2 + ``` + +The `database` parameter must match the name of one of the databases configured in [`pgdog.toml`](../../configuration/pgdog.toml/databases.md). For example: + +=== "users.toml" + + ```toml + [[users]] + name = "alice" + database = "prod" + password = "hunter2" + ``` + +=== "pgdog.toml" + + ```toml + [[databases]] + name = "prod" + host = "10.0.0.1" + ``` + +In this example, the `database` parameter in the `[[users]]` **`prod`** entry matches the `[[databases]]` database **`prod`** entry. + +!!! warning "User/database mismatch" + + If you add a user with a `database` name not specified in `pgdog.toml`, that entry will be ignored by PgDog at runtime and that user will not be able to connect. + +The same username, database name and password will also be used by PgDog to connect to PostgreSQL. This makes configuration simpler since the Postgres connection options used by applications don't have to change when PgDog is deployed for the first time. + +Read more about configuring users with password authentication [here](password.md). + +## Configuring user options + +PgDog supports setting user-specific options in `users.toml`. Some settings are overrides of equivalent global defaults set in the `[general]` section of `pgdog.toml`, while others can be set on users exclusively, for example: + +=== "pgdog.toml" + + ```toml + [[users]] + name = "pgdog" + database = "prod" + password = "hunter2" + pool_size = 15 + min_pool_size = 5 + ``` + +=== "Helm chart" + + ```yaml + users: + - name: pgdog + database: prod + password: hunter2 + poolSize: 15 + minPoolSize: 5 + ``` + +!!! note "Helm chart" + + Convert the setting name to `camelCase` if using our [Helm chart](../../installation.md#kubernetes). + +The following settings are supported: + +| User setting | Global setting | Description | +| ------------------------ | ------------------------ | -------------------------------------------------------------------------------------- | +| `pooler_mode` | `pooler_mode` | Transaction or session pooling mode. | +| `pool_size` | `default_pool_size` | Size of the user's connection pool. | +| `min_pool_size` | `min_pool_size` | Minimum number of idle connections in the user's pool. | +| `two_phase_commit` | `two_phase_commit` | Enable/disable [two-phase commit](../sharding/2pc/index.md) for this user. | +| `two_phase_commit_auto` | `two_phase_commit_auto` | Enable/disable [automatic](../sharding/2pc/index.md) two-phase-commit for this user. | +| `server_lifetime` | `server_lifetime` | Maximum connection age for this user. | +| `server_lifetime_jitter` | `server_lifetime_jitter` | Jitter added to `server_lifetime` for this user. | +| `cross_shard_disabled` | `cross_shard_disabled` | Disable [cross-shard](../sharding/cross-shard-queries/index.md) queries for this user. | + +### User-only settings + +In addition to overriding `pgdog.toml` defaults, some settings can only be set on users: + +| User setting | Description | +| ------------------- | ---------------------------------------------------------------------------------------------- | +| `statement_timeout` | Equivalent of executing `SET statement_timeout TO ` on connection pool creation. | +| `lock_timeout` | Equivalent of executing `SET lock_timeout TO ` on connection pool creation. | +| `idle_timeout` | Clients connected for longer than this (in ms) without executing queries will be disconnected. | + +#### Example + +=== "users.toml" + + ```toml + [[users]] + name = "alice" + pool_size = 10 + statement_timeout = 15_000 + ``` + +=== "Helm chart" + + ```yaml + users: + - name: alice + poolSize: 10 + statementTimeout: 15000 + ``` + +## Read more + +{{ next_steps_links([ + ("Password authentication", "/features/auth/password/", "Configure password authentication with SCRAM and other supported algorithms."), + ("RDS IAM", "/features/auth/rds-iam/", "Passwordless authentication to RDS PostgreSQL and Aurora databases."), + ("mTLS", "/features/auth/mtls/", "Passwordless authentication for client and server connections."), +]) }} diff --git a/docs/features/auth/hashicorp-vault.md b/docs/features/auth/hashicorp-vault.md new file mode 100644 index 00000000..f8760e0b --- /dev/null +++ b/docs/features/auth/hashicorp-vault.md @@ -0,0 +1,7 @@ +--- +icon: material/safe +--- + +# HashiCorp Vault + +PgDog supports using HashiCorp Vault for authentication to PostgreSQL. Documentation for this hasn't been written yet! diff --git a/docs/features/auth/index.md b/docs/features/auth/index.md new file mode 100644 index 00000000..b575563b --- /dev/null +++ b/docs/features/auth/index.md @@ -0,0 +1,36 @@ +--- +icon: material/login +--- + +# Authentication + +PostgreSQL servers support many authentication mechanisms. PgDog supports a subset of those, with the aim to support all of them over time. + +Additionally, PgDog supports some non-standard algorithms commonly used in the industry, like RDS IAM, Azure Identity, and others. This makes it relatively easy to deploy to a cloud environment without affecting security. + +## Supported methods + +The following table summarizes the current level of support for client and server connection authentication methods: + +| Authentication method | Client connections | Server connections | +| -------------------------------------------- | ------------------------------- | ------------------------------- | +| [Password](password.md) | :material-check-circle-outline: | :material-check-circle-outline: | +| Trust (no password) | :material-check-circle-outline: | :material-check-circle-outline: | +| [AWS RDS IAM](rds-iam.md) | No | :material-check-circle-outline: | +| [Azure Workload Identity](azure-workload.md) | No | :material-check-circle-outline: | +| [HashiCorp Vault](hashicorp-vault.md) | No | :material-check-circle-outline: | +| [Mutual TLS (mTLS)](mtls.md) | :material-check-circle-outline: | :material-check-circle-outline: | + +!!! note "Contributions" + + PgDog is an open source project. If you'd like to add an authentication method we don't currently support, + please [open an issue](https://github.com/pgdogdev/pgdog/issues) to discuss. + +## Read more + +{{ next_steps_links([ + ("Configuring users", "/features/auth/configuration/", "Configure users with authentication options and other settings."), + ("Password authentication", "/features/auth/password/", "Configure password authentication with SCRAM and other supported algorithms."), + ("RDS IAM", "/features/auth/rds-iam/", "Passwordless authentication to RDS PostgreSQL and Aurora databases."), + ("mTLS", "/features/auth/mtls/", "Passwordless authentication for client and server connections."), +]) }} diff --git a/docs/features/auth/mtls.md b/docs/features/auth/mtls.md new file mode 100644 index 00000000..24b712c1 --- /dev/null +++ b/docs/features/auth/mtls.md @@ -0,0 +1,214 @@ +--- +icon: material/certificate +--- + +# Mutual TLS (mTLS) + +Mutual TLS is a feature that allows applications to authenticate to PgDog, and for PgDog to authenticate to PostgreSQL, without using a password. The underlying mechanism uses TLS (SSL) certificates and ensures the connection is encrypted and secure, by exchanging certificates in advance. + +## How it works + +mTLS is supported for both [client to PgDog](#client-mtls) and [PgDog to Postgres](#server-mtls) connections. Each is configured separately in `pgdog.toml`. + +### Client mTLS + +Client (application) to PgDog mutual TLS is configured by enabling [TLS](../tls.md) and specifying a client TLS certificate: + +=== "pgdog.toml" + + ```toml + [general] + tls_certificate = "/path/to/cert.pem" + tls_private_key = "/path/to/key.pem" + tls_client_ca_certificate = "/path/to/client/cert.pem" + tls_client_required = true + ``` + +=== "Helm chart" + + ```yaml + tlsCertificate: /path/to/cert.pem + tlsPrivateKey: /path/to/key.pem + tlsClientCaCertificate: /path/to/client/cert.pem + tlsClientRequired: true + ``` + +| Setting | Description | +| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| `tls_certificate` | Path to the certificate provided to clients when they connect to PgDog. | +| `tls_private_key` | Path to the certificate private key. Never share this with clients. | +| `tls_client_ca_certificate` | Path to the CA (certificate authority) certificate that signed the certificates the client provides to PgDog upon connecting. | +| `tls_client_required` | Rejects any application connections that don't use TLS. | + +The client CA certificate can be self-signed, i.e., the clients can pass it directly when connecting or it can be used to sign other certificates. It can also be an intermediate, which allows you to build chains of trust without exposing your root certificate. + +### Users + +PgDog supports issuing different certificates to users in `users.toml`. To validate each user's certificate, PgDog will look at the SAN (Subject Alternative Name) DNSName attribute in the certificate, and match it to the configured user identity: + +=== "pgdog.toml" + + ```toml + [[users]] + name = "pgdog" + identity = "pgdog" + database = "postgres" + ``` + +=== "Helm chart" + + ```yaml + users: + - name: pgdog + identity: pgdog + database: postgres + ``` + +=== "Certificate" + + ```text + Subject: CN=pgdog + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:pgdog + X509v3 Extended Key Usage: + TLS Web Client Authentication + ``` + +#### Configuring apps + +Applications that use mTLS to connect to PgDog can be configured to provide the certificate at connection creation. They can also be configured to validate the `tls_certificate` PgDog will provide in return. + +##### Examples + +Most PostgreSQL client drivers accept the following TLS connection [parameters](https://www.postgresql.org/docs/current/libpq-ssl.html): + +| Parameter | Description | +| ------------- | ---------------------------------------------------------------------------------------------------- | +| `sslcert` | The application's client certificate, signed by a CA trusted by PgDog's `tls_client_ca_certificate`. | +| `sslkey` | The private key matching the application's client certificate. | +| `sslrootcert` | The CA certificate used by the application to verify PgDog's server certificate (`tls_certificate`). | + +=== "Rails" + + ```yaml title="database.yml" + production: + adapter: postgresql + host: pgdog.example.com + port: 6432 + database: postgres + username: pgdog + sslmode: verify-full + sslcert: /path/to/client/cert.pem + sslkey: /path/to/client/key.pem + sslrootcert: /path/to/server/ca.pem + ``` + +=== "psycopg (Python)" + + ```python + import psycopg + + conn = psycopg.connect( + host="pgdog.example.com", + port=6432, + dbname="postgres", + user="user_one", + sslmode="verify-full", + sslcert="/path/to/client/cert.pem", + sslkey="/path/to/client/key.pem", + sslrootcert="/path/to/server/ca.pem", + ) + ``` + +=== "SQLAlchemy (asyncpg)" + + ```python + import ssl + + from sqlalchemy.ext.asyncio import create_async_engine + + ssl_context = ssl.create_default_context(cafile="/path/to/server/ca.pem") + ssl_context.load_cert_chain( + certfile="/path/to/client/cert.pem", + keyfile="/path/to/client/key.pem", + ) + + engine = create_async_engine( + "postgresql+asyncpg://pgdog@pgdog.example.com:6432/postgres", + connect_args={"ssl": ssl_context}, + ) + ``` + +=== "pgx (Go)" + + ```go + import ( + "context" + + "github.com/jackc/pgx/v5" + ) + + ctx := context.Background() + conn, err := pgx.Connect(ctx, + "host=pgdog.example.com port=6432 dbname=postgres user=pgdog "+ + "sslmode=verify-full "+ + "sslcert=/path/to/client/cert.pem "+ + "sslkey=/path/to/client/key.pem "+ + "sslrootcert=/path/to/server/ca.pem", + ) + ``` + +### Server mTLS + +Much like [client mTLS](#client-mtls), PgDog can authenticate itself when connecting to PostgreSQL using a TLS certificate. PgDog can also validate the certificate offered by Postgres, ensuring the connection is validated from both ends: + +=== "pgdog.toml" + + ```toml + [general] + tls_verify = "verify_full" + tls_server_ca_certificate = "/path/to/ca/cert.pem" + tls_server_certificate = "/path/to/cert.pem" + tls_server_private_key = "/path/to/key.pem" + ``` + +=== "Helm chart" + + ```yaml + tlsVerify: verify_full + tlsServerCaCertificate: /path/to/ca/cert.pem + tlsServerCertificate: /path/to/cert.pem + tlsServerPrivateKey: /path/to/key.pem + ``` + +| Setting | Description | +| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `tls_verify` | Level of verification PgDog performs to validate the certificate provided by the server. Available options: `verify_ca` (only check certificate signature), `verify_full` (check certificate name matches database host). | +| `tls_server_ca_certificate` | Path to the CA (certificate authority) certificate (or intermediate) that signed the PostgreSQL server certificate. | +| `tls_server_certificate` | Path to the certificate PgDog will offer to Postgres for authentication. | +| `tls_server_private_key` | Path to that certificate's private key. | + +#### Per-database certificates + +PgDog supports using different certificates to authenticate to different `[[databases]]` entries in `pgdog.toml`, for example: + +=== "pgdog.toml" + + ```toml + [[databases]] + name = "postgres" + host = "prod.rds.amazonaws.com" + tls_server_certificate = "/path/to/cert.pem" + tls_server_private_key = "/path/to/key.pem" + ``` + +=== "Helm chart" + + ```yaml + databases: + - name: postgres + host: prod.rds.amazonaws.com + tlsServerCertificate: /path/to/cert.pem + tlsServerPrivateKey: /path/to/key.pem + ``` diff --git a/docs/features/auth/password.md b/docs/features/auth/password.md new file mode 100644 index 00000000..dcac1d4b --- /dev/null +++ b/docs/features/auth/password.md @@ -0,0 +1,221 @@ +--- +icon: material/form-textbox-password +--- + +# Password authentication + +Since PostgreSQL 14, `scram-sha-256` is widely used to encrypt passwords. PgDog supports this algorithm for both client and server connections. When enabled, applications connecting to PgDog must provide a username and password, either configured in [`users.toml`](../../configuration/users.toml/users.md), or via [passthrough](#passthrough-authentication) authentication. + +## Configuration + +Password authentication is configured in [`users.toml`](../../configuration/users.toml/users.md), by specifying a list of users and passwords, for example: + +=== "pgdog.toml" + + ```toml + [[users]] + name = "user_one" + database = "postgres" + password = "super-secret" + + [[users]] + name = "user_two" + database = "postgres" + password = "definitely-secret" + ``` + +=== "Helm chart" + + ```yaml + users: + - name: user_one + database: postgres + password: super-secret + - name: user_two + database: postgres + password: definitely-secret + ``` + +Each user will have its own dedicated connection pool. The password will be used to authenticate users connecting to PgDog and for connecting to Postgres. This is the most common use case, requiring no changes to your users in order to deploy PgDog between your application and the database. + +## Supported algorithms + +PgDog supports multiple password encryption algorithms commonly used by modern PostgreSQL servers and applications: + +| Password encryption | Client connections | Server connections | +| ------------------- | ------------------------------- | ------------------------------- | +| SCRAM-SHA-256 | :material-check-circle-outline: | :material-check-circle-outline: | +| SCRAM-SHA-256-PLUS | :material-check-circle-outline: | No | +| MD5 | :material-check-circle-outline: | :material-check-circle-outline: | +| Plain | :material-check-circle-outline: | :material-check-circle-outline: | +| Trust (no password) | :material-check-circle-outline: | :material-check-circle-outline: | + +!!! note "SCRAM performance" + + The SCRAM-SHA-256 algorithm is computationally expensive and will use a considerable amount of CPU time. This is by design, since it makes passwords difficult to brute-force. However, if your application is frequently creating new connections to the database, like serverless apps running on Vercel, Cloudflare workers, etc., this could also have a latency impact. + + If your application is affected by this, consider enabling [TLS](../tls.md) and using the `plain` authentication method instead. Modern CPUs implement TLS algorithms in hardware which makes them efficient and fast. + +### Overriding database credentials + +PgDog can connect to Postgres using different users and/or passwords than the application. This is useful when configuring multiple connection pools which re-use the same Postgres user, for example: + +=== "pgdog.toml" + + ```toml + [[users]] + name = "user_one" + database = "postgres" + password = "super-secret" + server_user = "postgres" + server_password = "different-secret" + ``` + +=== "Helm chart" + + ```yaml + users: + - name: user_one + database: postgres + password: super-secret + serverUser: postgres + serverPassword: different-secret + ``` + +Applications connecting to PgDog will use the `user_one` user; meanwhile PgDog will connect to Postgres using the `postgres` user and a different password. Any combinations of these settings are supported, e.g., different passwords and same username, or same username and different passwords. + +### Securing passwords + +If you're using our [Helm chart](../../installation.md), `users.toml` will be automatically stored as a `Secret`. + +If you're using GitOps tools, e.g., ArgoCD, you can avoid exposing passwords in version control by using the `ExternalSecret` operator, which can store the contents of `users.toml` in a supported SecretStore, e.g., AWS Secrets Manager: + +```yaml title="values.yaml" +externalSecrets: + enabled: true + secretStoreRef: + name: aws-secrets-manager + kind: SecretStore + remoteRefs: + - secretKey: users.toml + remoteRef: + key: pgdog/users +``` + +## Passthrough authentication + +With passthrough authentication, instead of storing passwords in `users.toml`, PgDog connects to PostgreSQL using the credentials provided by the client. Passthrough authentication simplifies PgDog deployments by using a single source of truth for user credentials and doesn't require passwords to be stored outside the database or the application. + +Passthrough authentication is **disabled** by default and can be enabled with configuration: + +=== "pgdog.toml" + + ```toml + [general] + passthrough_auth = "enabled" + ``` + +=== "Helm chart" + + ```yaml + passthroughAuth: enabled + ``` + +### How it works + +Since PgDog doesn't store the server password anymore, using passthrough authentication will require clients to send passwords in plain text. Therefore, PgDog will automatically change the `auth_method` to `plain` and ignore the setting configured in `pgdog.toml`. + +When a client connects to PgDog for the first time, it will create a connection pool for the database/user pair and the provided password. The database specified by the client must still exist in [`pgdog.toml`](../../configuration/pgdog.toml/databases.md). + +#### Configuration updates + +When configuration is changed and reloaded, connection pools created with passthrough auth are temporarily removed and immediately re-created when a connected client executes a query. As long as `passthrough_auth` is enabled between configuration changes, clients will not be impacted. + +### Security + +Sending passwords in plain text over unencrypted connections is not great, even if PgDog and Postgres are on the same local network. For this reason, `passthrough_auth = "enabled"` will only work if PgDog is configured to use [TLS encryption](../tls.md). + +If you don't want to set up TLS (it has some impact on latency), you can override this behavior and send passwords via plain text and an unencrypted connection: + +=== "pgdog.toml" + + ```toml + [general] + passthrough_auth = "enabled_plain" + ``` + +=== "Helm chart" + + ```yaml + passthroughAuth: enabled_plain + ``` + +### Changing passwords + +Connection pools created dynamically with passthrough authentication will have a static password. If that password is changed inside the server (e.g., by running the `ALTER USER [...] PASSWORD` command), PgDog will no longer be able to connect to the database. To change the password in PgDog without restarting the proxy, you can configure the passthrough auth passwords to be changeable: + +=== "pgdog.toml" + + ```toml + [general] + passthrough_auth = "enabled_allow_change" + ``` + +=== "Helm chart" + + ```yaml + passthroughAuth: enabled_allow_change + ``` + +When a client connects with a different password to what's currently stored in PgDog's memory, it will re-create the connection pool with the new password and re-connect to the server. + +!!! warning "Trusted clients only" + + Allowing clients to change their passwords at runtime should never be used with PgDog deployments open to the Internet. This feature is built for convenience to allow almost zero-downtime password rotation in Postgres and, if used incorrectly, can open up the proxy to a denial-of-service attack. + +#### Plaintext connections + +If passthrough authentication is used without [TLS](../tls.md), set it to `"enabled_plain_allow_change"` instead: + +=== "pgdog.toml" + + ```toml + [general] + passthrough_auth = "enabled_plain_allow_change" + ``` + +=== "Helm chart" + + ```yaml + passthroughAuth: enabled_plain_allow_change + ``` + +## Password rotation + +When using passwords, it's common practice to change passwords occasionally to protect the database against unauthorized access. + +PostgreSQL password rotation has been a problem for a while, since it typically requires downtime in order to change the password across your entire stack. + +PgDog makes it somewhat easier by allowing _multiple_ passwords to be specified for any entry in `users.toml`, for example: + +=== "pgdog.toml" + + ```toml + [[users]] + name = "user_one" + passwords = ["old-secret", "new-secret"] + database = "postgres" + ``` + +=== "Helm chart" + + ```yaml + users: + - name: user_one + passwords: ["old-secret", "new-secret"] + database: postgres + ``` + +PgDog will accept connections from applications using all passwords specified in `passwords` and will attempt to connect to Postgres until a password is accepted. + +Once the password is successfully rotated everywhere, the old entry can be removed from the configuration, without downtime. This works for SCRAM, md5 and plaintext authentication algorithms, so no additional configuration is required for this feature to work. diff --git a/docs/features/auth/rds-iam.md b/docs/features/auth/rds-iam.md new file mode 100644 index 00000000..6ed19e0f --- /dev/null +++ b/docs/features/auth/rds-iam.md @@ -0,0 +1,89 @@ +--- +icon: material/aws +--- + +# RDS IAM + +PgDog supports obtaining temporary credentials from [AWS IAM](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html) and using those to connect to RDS PostgreSQL (and Aurora) instances. + +## Configuration + +To use RDS IAM authentication, configure it on each user in [`users.toml`](../../configuration/users.toml/users.md), for example: + +=== "users.toml" + + ```toml + [[users]] + name = "pgdog" + database = "prod" + password = "hunter2" + server_auth = "rds_iam" + ``` + +=== "Helm chart" + + ```yaml + users: + - name: pgdog + database: prod + password: hunter2 + serverAuth: rds_iam + ``` + +### How it works + +Under the hood, PgDog is using the [AWS RDS SDK](https://docs.rs/aws-sdk-rds/latest/aws_sdk_rds/) to fetch credentials at runtime. The SDK can retrieve temporary credentials from the environment or from the EC2 IAM API. It will use the role assigned to the EC2 instance or Kubernetes pod to connect to RDS. + +If you're deploying PgDog in [Kubernetes](../../installation.md#kubernetes) using our Helm chart, you can assign the PgDog container an IAM role with the correct permissions, for example: + +```yaml title="values.yaml" +serviceAccount: + create: true + annotations: + eks.amazonaws.com/role-arn: "arn:aws:iam::123456789012:role/pgdog-role" +``` + +### Client authentication + +RDS IAM authentication is currently only supported for connections between PgDog and PostgreSQL. Clients need to continue using one of the supported authentication mechanisms, e.g., [password](password.md) auth. + +To completely avoid using passwords for user authentication, take a look at [mTLS](mtls.md). + +### Multiple roles + +PgDog supports assuming different roles for each user in order to connect to RDS. This is common when deploying PgDog across different AWS accounts or regions. + +For each user in `users.toml`, you can specify its IAM role (and optionally IAM region) as follows: + +=== "pgdog.toml" + + ```toml + [[users]] + name = "pgdog" + database = "prod" + password = "hunter2" + server_auth = "rds_iam" + server_iam_region = "us-west-2" + server_iam_assume_role = "arn:aws:iam::123456789012:role/pgdog-role-us-west-2" + ``` + +=== "Helm chart" + + ```yaml + users: + - name: pgdog + database: prod + password: hunter2 + serverAuth: rds_iam + serverIamRegion: us-west-2 + serverIamAssumeRole: arn:aws:iam::123456789012:role/pgdog-role-us-west-2 + ``` + +In order for this to work correctly, make sure the IAM role used to deploy PgDog has the correct Trust Policy to assume all roles specified in the configuration. + +## Read more + +{{ next_steps_links([ + ("TLS", "/features/tls/", "Configure encrypted connections for applications connecting to PgDog and PgDog's connections to the database."), + ("mTLS", "/features/auth/mtls/", "Passwordless authentication for application connections to PgDog."), +]) }} diff --git a/docs/features/authentication.md b/docs/features/authentication.md deleted file mode 100644 index 349f1b86..00000000 --- a/docs/features/authentication.md +++ /dev/null @@ -1,284 +0,0 @@ ---- -icon: material/login ---- -# Authentication - -PostgreSQL servers support many authentication mechanisms. PgDog supports a subset of those, with the aim to support all of them over time. Since PostgreSQL 14, `scram-sha-256` is widely used to encrypt passwords and PgDog supports this algorithm for both client and server connections. - -Authentication is **enabled** by default. Applications connecting to PgDog must provide a username and password, configured in [`users.toml`](../configuration/users.toml/users.md). - - -## Supported methods - -PgDog implements a subset of authentication methods supported by Postgres and some others commonly used in the industry. The following table summarizes the current level of support for client and server connection authentication methods: - -| Authentication method | Client connections | Server connections | -|-|-|-| -| `scram-sha-256` | :material-check-circle-outline: | :material-check-circle-outline: | -| `scram-sha-256-plus` | No | No | -| `md5` | :material-check-circle-outline: | :material-check-circle-outline: | -| `plain` | :material-check-circle-outline: | :material-check-circle-outline: | -| `trust` | :material-check-circle-outline: | :material-check-circle-outline: | -| [AWS RDS IAM](#rds-iam-authentication) | No | :material-check-circle-outline: | -| [Azure Workload Identity](#azure-workload-identity-authentication) | No | :material-check-circle-outline: | - -!!! note "Contributions" - PgDog is an open source project. If you'd like to add an authentication method we don't currently support, - please [open an issue](https://github.com/pgdogdev/pgdog/issues) to discuss. - -## Client authentication - -By default, client connections will use password authentication encrypted with `scram-sha-256`. This method is secure and recommended for production usage. PgDog supports using other methods, e.g., `md5` and `plain`, which you can change with configuration: - -=== "pgdog.toml" - ```toml - [general] - auth_type = "scram" - ``` -=== "Helm chart" - ```yaml - authType: scram - ``` - -The following password authentication algorithms are available for client connections: - -| Configuration | Description | -|-|-| -| `scram` | The most secure password encryption, used by default in PostgreSQL. | -| `md5` | Deprecated and insecure, but an order of magnitude faster than SCRAM. Still commonly used with connection poolers (e.g., pgbouncer). | -| `plain` | No encryption is used and the password is sent as-is. Commonly used with TLS-encrypted connections. | -| `trust` | Disables password authentication and allows all users to login. | - - -### SCRAM performance - -The SCRAM-SHA-256 algorithm is computationally expensive and will use a considerable amount of CPU time. This is by design, since it makes passwords difficult to brute-force. However, if your application is frequently creating new connections to the database, like serverless apps running on Vercel, Cloudflare workers, etc., this could also have a latency impact. - -If your application is affected by this, consider enabling [TLS](tls.md) and using the `plain` authentication method instead. Modern CPUs implement TLS algorithms in hardware which makes them efficient and fast. - -## Server authentication - -The server authentication method is controlled by PostgreSQL. PgDog will use whatever method Postgres requests during connection creation, which is configurable in the [`pg_hba.conf`](https://www.postgresql.org/docs/current/auth-pg-hba-conf.html) file on the server. - -PgDog currently supports four authentication methods for server connections: - -| Method | Description | -|-|-| -| Password authentication | PgDog sends the password using one of the supported [password encryption](#client-authentication) algorithms. | -| [AWS RDS IAM](#rds-iam-authentication) | PgDog requests a temporary password from AWS IAM and uses that for password authentication instead. | -| [Azure Workload Identity](#azure-workload-identity-authentication) | Same method as AWS RDS IAM, except supported on Azure. | -| Hashicorp Vault | PgDog connects to a configured instance of Hashicorp Vault and uses the provided username and password. | - -### RDS IAM authentication - -PgDog supports using temporary credentials from [AWS IAM](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html) and using those to connect to RDS PostgreSQL (and Aurora) instances. - -Under the hood, PgDog is using the [AWS RDS SDK](https://docs.rs/aws-sdk-rds/latest/aws_sdk_rds/) to fetch credentials at runtime. The SDK can retrieve them from the environment and from the EC2 IAM API. For example, if you're deploying PgDog in [Kubernetes](../installation.md#kubernetes), you just need to assign its container an IAM role with the right permissions. - -To use RDS IAM authentication, configure it on each user in [`users.toml`](../configuration/users.toml/users.md): - -=== "users.toml" - ```toml - [[users]] - name = "pgdog" - database = "prod" - password = "hunter2" - server_auth = "rds_iam" - ``` -=== "Helm chart" - ```yaml - users: - - name: pgdog - database: prod - password: hunter2 - serverAuth: rds_iam - ``` - -### Azure Workload Identity authentication - -PgDog supports using temporary credentials provided by Azure Workload Identity to connect to PostgreSQL running on Azure. This uses the [Azure SDK](https://github.com/Azure/azure-sdk-for-rust) and supports fetching credentials from the environment. - -To use Workload Identity authentication, configure it on each user in [`users.toml`](../configuration/users.toml/users.md): - -=== "users.toml" - ```toml - [[users]] - name = "pgdog" - database = "prod" - password = "hunter2" - server_auth = "azure_workload_identity" - ``` -=== "Helm chart" - ```yaml - users: - - name: pgdog - database: prod - password: hunter2 - serverAuth: azure_workload_identity - ``` - - -### Hashicorp Vault - -!!! info "TODO: Documentation" - Support for Hashicorp Vault authentication has been added in [v0.1.46](https://github.com/pgdogdev/pgdog/releases/tag/v0.1.46). It has not been documented or thoroughly tested yet. - -## Configuring users - -The [`users.toml`](../configuration/users.toml/users.md) configuration file follows a TOML list structure. To allow a user to connect to PgDog, add a `[[users]]` section with the user name, password and a corresponding database name (located in [`pgdog.toml`](../configuration/pgdog.toml/databases.md)) to `users.toml`, for example: - -=== "users.toml" - ```toml - [[users]] - name = "pgdog" - database = "pgdog" - password = "hunter2" - ``` -=== "Helm chart" - ```yaml - users: - - name: pgdog - database: pgdog - password: hunter2 - ``` - -The `database` parameter must match the name of one of the databases configured in [`pgdog.toml`](../configuration/pgdog.toml/databases.md). For example: - -=== "users.toml" - ```toml - [[users]] - name = "alice" - database = "prod" - password = "hunter2" - ``` -=== "pgdog.toml" - ```toml - [[databases]] - name = "prod" - host = "10.0.0.1" - ``` - -The `database` parameter in the `[[users]]` entry ("prod") matches the `[[databases]]` entry with the same `name` ("prod"). If you add a user with a `database` name not specified in `pgdog.toml`, that user entry will be ignored by PgDog at runtime and that user will not be able to connect. - -The same username, database name and password will also be used by PgDog to connect to PostgreSQL. This makes configuration simpler since the Postgres connection options used by applications don't have to change when PgDog is deployed for the first time. - -### Overriding server credentials - -If you want to use a different username or password for PgDog to connect to Postgres, you can override them by specifying the `server_user` and `server_password` parameters in the same user configuration entry: - -=== "users.toml" - ```toml - [[users]] - name = "pgdog" - password = "hunter2" - database = "pgdog" - server_user = "bob" - server_password = "opensesame" - ``` -=== "Helm chart" - ```yaml - users: - - name: pgdog - password: hunter2 - database: pgdog - serverUser: bob - serverPassword: opensesame - ``` - -This allows you to separate client and server authentication credentials. In case your applications accidentally leak their credentials (e.g., by committing them to git), you only need to rotate them in the PgDog configuration, without having to take downtime to change passwords in PostgreSQL. - -## Passthrough authentication - -With passthrough authentication, instead of storing passwords in `users.toml`, PgDog connects to PostgreSQL using the credentials provided by the client. Passthrough authentication simplifies PgDog deployments by using a single source of truth for user credentials and doesn't require passwords to be stored outside the database or the application. - -Passthrough authentication is **disabled** by default and can be enabled with configuration: - -=== "pgdog.toml" - ```toml - [general] - passthrough_auth = "enabled" - ``` -=== "Helm chart" - ```yaml - passthroughAuth: enabled - ``` - -Since PgDog doesn't store the server password anymore, using passthrough authentication will require clients to send passwords in plain text. Therefore, PgDog will automatically change the `auth_method` to `plain` and ignore the setting configured in `pgdog.toml`. - -When a client connects to PgDog for the first time, it will create a connection pool for the database/user pair and the provided password. The database specified by the client must still exist in [`pgdog.toml`](../configuration/pgdog.toml/databases.md). - -When configuration is reloaded, connection pools created with passthrough auth are temporarily removed and immediately re-created when a connected client executes a query. As long as `passthrough_auth` is enabled between configuration changes, clients will not be impacted. - -### Passthrough authentication security - -Sending passwords in plain text over unencrypted connections is not great, even if PgDog and Postgres are on the same local network. For this reason, `passthrough_auth = "enabled"` will only work if PgDog is configured to use [TLS encryption](tls.md). - -If you don't want to set up TLS (it has some impact on latency), you can override this behavior and send passwords via plain text and an unencrypted connection: - -=== "pgdog.toml" - ```toml - [general] - passthrough_auth = "enabled_plain" - ``` -=== "Helm chart" - ```yaml - passthroughAuth: enabled_plain - ``` - -### Configuring user options - -The most typical deployments of PgDog with passthrough authentication do not configure a `users.toml` at all. However, some user-specific options can only be configured in that file, for example, [server authentication](#server-authentication). To configure users with options and passthrough authentication, add them to `users.toml` without specifying a password: - -=== "users.toml" - ```toml - [[users]] - name = "alice" - pool_size = 10 - server_auth = "rds_iam" - ``` -=== "Helm chart" - ```yaml - users: - - name: alice - poolSize: 10 - serverAuth: rds_iam - ``` - -Passthrough authentication must still be enabled in `pgdog.toml`. PgDog will use the password supplied by the client and create a connection pool dynamically when they connect for the first time. - -### Changing passwords - -Connection pools created dynamically with passthrough authentication will have a static password. If that password is changed inside the server (e.g., by running `ALTER USER [...] PASSWORD` command), PgDog will no longer be able to connect to the database. To change the password in PgDog without restarting the proxy, you can configure the passthrough auth passwords to be changeable: - -=== "pgdog.toml" - ```toml - [general] - passthrough_auth = "enabled_allow_change" - ``` -=== "Helm chart" - ```yaml - passthroughAuth: enabled_allow_change - ``` - -When a client connects with a different password to what's currently stored in PgDog's memory, it will re-create the connection pool with the new password and re-connect to the server. - -!!! warning "Trusted clients only" - Allowing clients to change their passwords at runtime should never be used with PgDog deployments open to the Internet. This feature is built for convenience to allow almost zero-downtime password rotation in Postgres and, if used incorrectly, can open up the proxy to a denial-of-service attack. - -#### Plain text connections - -If passthrough authentication is used without [TLS](tls.md), set it to `"enabled_plain_allow_change"` instead: - -=== "pgdog.toml" - ```toml - [general] - passthrough_auth = "enabled_plain_allow_change" - ``` -=== "Helm chart" - ```yaml - passthroughAuth: enabled_plain_allow_change - ``` - -## Password security - -Since PgDog stores passwords in a separate configuration file (i.e., `users.toml`), it's possible to encrypt them at rest without compromising the DevOps experience. For example, Kubernetes provides built-in [secrets management](https://kubernetes.io/docs/concepts/configuration/secret/) to manage this, and our [Helm chart](../installation.md#kubernetes) automatically takes advantage of it. diff --git a/docs/features/tls.md b/docs/features/tls.md index 1d6ec110..399c0b8f 100644 --- a/docs/features/tls.md +++ b/docs/features/tls.md @@ -1,6 +1,7 @@ --- icon: material/lock --- + # TLS encryption PgDog supports TLS for both client and server connections. TLS encryption protects your connections from eavesdropping, especially if used across the public Internet, and is often required to pass security audits. @@ -14,12 +15,15 @@ To enable encryption for client connections, you need to provide (or generate) a Add the following settings to your `pgdog.toml`: === "pgdog.toml" + ```toml [general] tls_certificate = "/path/to/certificate.pem" tls_private_key = "/path/to/private_key.pem" ``` + === "Helm chart" + ```yaml tlsCertificate: /path/to/certificate.pem tlsPrivateKey: /path/to/private_key.pem @@ -40,11 +44,14 @@ postgres://user:password@host:port/database?sslmode=prefer PgDog can reject connections from clients that choose not to use TLS encryption: === "pgdog.toml" + ```toml [general] tls_client_required = true ``` + === "Helm chart" + ``` tlsClientRequired: true ``` @@ -56,6 +63,7 @@ This is helpful to enforce a security protocol but, in some rare scenarios, coul If you're deploying PgDog using our [Helm chart](../installation.md#kubernetes), you can configure it to generate a self-signed TLS certificate at deploy time: === "Helm chart" + ```yaml tlsGenerateSelfSignedCert: true ``` @@ -66,12 +74,12 @@ This is useful for quickly deploying TLS in development or staging. For producti PostgreSQL supports 4 modes for establishing encrypted connections, documented below: -| Mode | Description | -|-|-| -| `disabled` | TLS connections are disabled. Client will connect using plain TCP. | -| `prefer` | If PgDog/PostgreSQL support encryption, it will be used. If not, connections will be made using plain TCP. Any certificate will be accepted. This is often used with self-signed certificates. | -| `verify-ca` | Encryption will be used and if not supported, the connection attempt will be aborted. Additionally, the client will verify the validity of the certificate against a trusted anchor, e.g., local certificate store. | -| `verify-full` | In addition to verifying the certificate, the client will ensure the hostname provided matches the hostname on the certificate. | +| Mode | Description | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `disabled` | TLS connections are disabled. Client will connect using plain TCP. | +| `prefer` | If PgDog/PostgreSQL support encryption, it will be used. If not, connections will be made using plain TCP. Any certificate will be accepted. This is often used with self-signed certificates. | +| `verify-ca` | Encryption will be used and if not supported, the connection attempt will be aborted. Additionally, the client will verify the validity of the certificate against a trusted anchor, e.g., local certificate store. | +| `verify-full` | In addition to verifying the certificate, the client will ensure the hostname provided matches the hostname on the certificate. | The default value for most PostgreSQL connection drivers is typically `prefer`, which means if you configure certificates in PgDog, your clients will start using encrypted connections immediately. @@ -80,32 +88,38 @@ The default value for most PostgreSQL connection drivers is typically `prefer`, By default, PgDog will attempt to use TLS when connecting to PostgreSQL. This is configurable via a setting: === "pgdog.toml" + ```toml [general] tls_verify = "prefer" ``` + === "Helm chart" + ```yaml tlsVerify: prefer ``` This setting accepts almost identical values to the `sslmode` parameter used by clients: -| Value | Description | -|-|-| -| `disable` | Don't use TLS. | -| `prefer` | Use TLS if available, accept any certificate. | -| `verify_ca` | Use TLS, validate the certificate provided by Postgres. | +| Value | Description | +| ------------- | ------------------------------------------------------------------------------- | +| `disable` | Don't use TLS. | +| `prefer` | Use TLS if available, accept any certificate. | +| `verify_ca` | Use TLS, validate the certificate provided by Postgres. | | `verify_full` | Use TLS and validate the hostname against the certificate provided by Postgres. | If you use `verify_ca` or `verify_full` and your certificate is not signed by a well known CA, you can configure PgDog to validate it using your own certificate chain: === "pgdog.toml" + ```toml [general] tls_server_ca_certificate = "/path/to/ca/certificate.pem" ``` + === "Helm chart" + ```yaml tlsServerCaCertificate: /path/to/ca/certificate.pem ``` @@ -115,51 +129,61 @@ If you use `verify_ca` or `verify_full` and your certificate is not signed by a PgDog is commonly deployed in front of AWS RDS or Aurora. To make it easier to setup secure TLS, we are bundling the [RDS certificate bundle](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html#UsingWithRDS.SSL.CertificatesDownload) into the Helm chart and making it available to PgDog at runtime: === "Helm chart" + ```yaml rdsCertificateBundle: enabled: true ``` + === "AWS GovCloud" + If deploying into the AWS GovCloud (US), you can change the bundle accordingly: ```yaml rdsCertificateBundle: type: govcloud ``` - Once the bundle is loaded, you can switch to `verify_ca` (or `verify_full`) for server connections which will ensure that connections from PgDog to RDS are always encrypted _and_ authenticated: === "pgdog.toml" + ```toml [general] tls_verify = "verify_full" ``` + === "Helm chart" + ```yaml tlsVerify: verify_full ``` - ## Mutual TLS !!! note "New" + This is a new feature. Please report any issues you may run into. Mutual TLS (also known as **mTLS**) allows PgDog to authenticate connections received from the client using a mutually agreed upon certificate. If the client doesn't provide the right certificate (or doesn't have one), PgDog will reject the connection. This can be enabled by setting the client CA certificate in [`pgdog.toml`](../configuration/pgdog.toml/general.md): === "pgdog.toml" + ```toml [general] tls_client_ca_certificate = "/path/to/client/ca.pem" ``` + === "Helm chart" + ```yaml tlsClientCaCertificate: /path/to/client/ca.pem ``` The certificate provided by the client doesn't have to be self-signed. In fact, any certificate signed by any of the certs in the chain loaded via `tls_client_ca_certificate` is an acceptable anchor. This allows an internal CA (Certificate Authority) to issue unique certificates to each application, while also making them short-lived (e.g., 30 days expiration) to satisfy security or compliance requirements. +You can read more about this [here](auth/mtls.md). + ## TLS in practice PgDog terminates TLS from clients and opens a separate connection to Postgres. Traffic can be encrypted on both network hops, but it is not end-to-end encryption in the cryptographic sense: PgDog decrypts client traffic so it can read PostgreSQL protocol messages, route queries, and manage connections. diff --git a/mkdocs.yml b/mkdocs.yml index 1c1e1963..c444437d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -90,6 +90,7 @@ plugins: - macros - redirects: redirect_maps: + "features/authentication.md": "features/auth/index.md" "features/healthchecks.md": "features/load-balancer/healthchecks.md" "features/sharding/migrations.md": "features/sharding/schema_management/migrations.md" "features/sharding/primary-keys.md": "features/sharding/sequences.md"