Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
087e847
intial draft
CommanderStorm Jul 11, 2025
b034041
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 11, 2025
2b61b87
light reformatting of the troubleshooting section
CommanderStorm Jul 11, 2025
8eeec92
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 11, 2025
40e76a8
remove the recap in the end
CommanderStorm Jul 11, 2025
6b1f911
Merge branch 'main' into pg-sql-docs
CommanderStorm Jul 11, 2025
59d0f59
remove some cloud providers which are not really relevant
CommanderStorm Jul 11, 2025
9898ae7
reformat the pg-connections
CommanderStorm Jul 11, 2025
a6800b6
refactor the table with personal oppinions and guidance from the post…
CommanderStorm Jul 11, 2025
37dfd8f
fixed a few bugs
CommanderStorm Jul 11, 2025
df77df2
refactor the ssl cerfificate guide to actually work more
CommanderStorm Jul 11, 2025
d431169
second round of improvements to the docs
CommanderStorm Jul 11, 2025
13ae902
fix a few md-lints
CommanderStorm Jul 12, 2025
f000857
reformat how we tell people about certificates
CommanderStorm Jul 21, 2025
914661e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 21, 2025
2f07ab4
Reword a few things
CommanderStorm Jul 21, 2025
dd70f9e
Reword how wer explain `prefer`
CommanderStorm Jul 21, 2025
c69daa4
reword even more things
CommanderStorm Jul 21, 2025
9fda38d
Merge branch 'main' into pg-sql-docs
CommanderStorm Jul 21, 2025
8458b1e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 21, 2025
b00c939
fix markdown lint
CommanderStorm Jul 21, 2025
268e367
reword the summary
CommanderStorm Jul 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
- [Tile source specific](using-with-data.md)
- [Using Hosted PostgreSQL](recipes.md)
- [Setting up a basemap and overlaying data](recipe-basemap-postgis.md)
- [Using PostgreSQL with SSL Certificates](pg-ssl-certificates.md)
- [Bulk Tile Generation](martin-cp.md)
- [Working with MBTiles archives](mbtiles.md)
- [MBTiles Schemas](mbtiles-schema.md)
Expand Down
25 changes: 20 additions & 5 deletions docs/src/pg-connections.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
## PostgreSQL Connection String
## PostgreSQL Connections

Martin supports many of the PostgreSQL connection string settings such as `host`, `port`, `user`, `password`, `dbname`, `sslmode`, `connect_timeout`, `keepalives`, `keepalives_idle`, etc. See the [PostgreSQL docs](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING) for more details.
Martin supports standard PostgreSQL connection string settings including `host`, `port`, `user`, `password`, `dbname`, `sslmode`, `connect_timeout`, `keepalives`, `keepalives_idle`, etc.
See the [PostgreSQL docs](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING) for more details.

### PostgreSQL SSL Connections
### SSL Connections

Martin supports PostgreSQL `sslmode` including `disable`, `prefer`, `require`, `verify-ca` and `verify-full` modes as described in the [PostgreSQL docs](https://www.postgresql.org/docs/current/libpq-ssl.html). Certificates can be provided in the configuration file, or can be set using the same env vars as used for `psql`. When set as env vars, they apply to all PostgreSQL connections. See [environment vars](env-vars.md) section for more details.
Martin supports PostgreSQL `sslmode` settings: `disable`, `prefer`, `require`, `verify-ca` and `verify-full`.
See the [PostgreSQL docs](https://www.postgresql.org/docs/current/libpq-ssl.html) for mode descriptions.
Certificates can be provided in the configuration file or via environment variables (same as `psql`).
Environment variables apply to all PostgreSQL connections.
See [environment vars](env-vars.md) for details.

By default, `sslmode` is set to `prefer` which means that SSL is used if the server supports it, but the connection is not aborted if the server does not support it. This is the default behavior of `psql` and is the most compatible option. Use the `sslmode` param to set a different `sslmode`, e.g. `postgresql://user:password@host/db?sslmode=require`.
By default, `sslmode` is `prefer` - encrypt (don't check certificates) if the server supports it, but the connection proceeds without SSL if not supported.
This matches `psql` default behavior.

If you require guarnatees regarding [eavesdropping](https://en.wikipedia.org/wiki/Eavesdropping) or [MITM protection](https://en.wikipedia.org/wiki/Man-in-the-middle_attack), you need a different option.
Use the `sslmode` parameter to specify a different mode:

```bash
martin postgresql://user:password@host/db?sslmode=verify-full
```

For a practical walkthrough of SSL certificate setup — including creation, configuration, and troubleshooting — see our [PostgreSQL SSL Certificates Recipe](pg-ssl-certificates.md).
246 changes: 246 additions & 0 deletions docs/src/pg-ssl-certificates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
# PostgreSQL SSL Certificates

Martin supports SSL certificate authentication for PostgreSQL connections. This guide covers certificate generation, PostgreSQL configuration, and Martin setup.

## When to Use SSL Certificates

Use SSL certificates for:

- Deployments where martin and Postgis are on separate machines
- Compliance requirements (PCI DSS, HIPAA, etc.)
- Cloud PostgreSQL deployments
- High-security environments requiring certificate-based authentication

## SSL Modes

| sslmode | Eaves-<br/>dropping<br/>protection | MITM <br/>protection | Statement |
|---------------|--------------------------|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
| `disable` | ⛔ | ⛔ | I don't care about security, and I don't want to pay the overhead of encryption. |
| `allow` | 🤷 | ⛔ | I don't care about security, but I will pay the overhead of encryption if the server insists on it. |
| `prefer` | 🤷 | ⛔ | I don't care about encryption, but I wish to pay the overhead of encryption if the server supports it. |
| `require` | ✅ | ⛔ | I want my data to be encrypted, and I accept the overhead. I trust that the network will make sure I always connect to the server I want. |
| `verify-ca` | ✅ | Depends <br/> on CA policy | I want my data encrypted, and I accept the overhead. I want to be sure that I connect to a server that I trust. |
| `verify-full` | ✅ | ✅ | I want my data encrypted, and I accept the overhead. I want to be sure that I connect to a server I trust, and that it's the one I specify. |

Our recommendation: **`verify-full` or `allow`**.
There are not many cases where anything in between makes sense.

In particular, the default mode (`prefer`) does not make much sense.
From the postgres documentation:

> As is shown in the table, this makes no sense from a security point of view, and it only promises performance overhead if possible.
> It is only provided as the default for backward compatibility, and is not recommended in secure deployments.

For a fuller explanation of the different tradeoffs, refer to the [PostgreSQL SSL Certificates documentation](https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-CONFIG).

## Generating Certificates

For basic SSL encryption, you need:

- `server-cert.pem` - PostgreSQL server certificate
- `server-key.pem` - PostgreSQL server private key
- `ca-cert.pem` - Certificate Authority certificate

```raw
┌─────────────────┐ SSL/TLS ┌─────────────────┐
│ Martin │◄─────────────►│ PostgreSQL │
└─────────────────┘ verify-full └─────────────────┘
│ │
┌─────────┐ ┌─────────────┐
│ CA Cert │ │ Server Cert │
│ │ │ Server Key │
└─────────┘ └─────────────┘
```

### Self-Signed Certificates

To generate certificates as a CA, you will need a private key.
To verify the certificate, you will need the CA certificate.

```bash
# Generate CA private key
openssl genrsa -out ca-key.pem 3072

# Generate CA certificate
openssl req -new -x509 -days 365 -key ca-key.pem -out ca-cert.pem \
-subj "/C=US/ST=State/L=City/O=Organization/CN=Test CA"
```

You can then generate a server certificates:

```bash
# Generate server private key
openssl genrsa -out server-key.pem 3072

# Generate server certificate signing request with SAN extension
openssl req -new -key server-key.pem -out server-csr.pem \
-subj "/C=US/ST=State/L=City/O=Organization/CN=localhost" \
-addext "subjectAltName = DNS:localhost"

# Generate server certificate signed by CA with SAN extension
openssl x509 -req -days 365 -in server-csr.pem -CA ca-cert.pem -CAkey ca-key.pem \
-CAcreateserial -out server-cert.pem -extensions v3_req \
-extfile <(printf "[v3_req]\nsubjectAltName = DNS:localhost")

# Set permissions
chmod 400 *-key.pem
chmod 444 *-cert.pem ca-cert.pem
```

### Production Certificates

For production, use certificates from:

- Regular Certificate Authorities (Let's Encrypt, DigiCert, GlobalSign)
- Cloud provider managed Certificate Authorities
- Organization-Internal Certificate Authority

## PostgreSQL Configuration

```yaml
services:
db:
image: postgis/postgis:17-3.5
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
ports:
- "5432:5432"
volumes:
- ./server-cert.pem:/var/lib/postgresql/server.crt:ro
- ./server-key.pem:/var/lib/postgresql/server.key:ro
command: -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key
```

```bash
docker compose up
```

> [!TIP]
> Postgres requires specific file permissions and ownership for SSL certificates.
> In docker this can be a bit tricky:
>
> alpine images have `70:70` as the default `user:group`
> debian images have `999:999` as the default `user:group`
>
> You can change this by running the following commands:
>
> ```bash
> chown 999:999 *.pem
> chmod 400 *.pem
> ```

## Testing with psql

Test SSL Connection via

```bash
PGSSLROOTCERT=ca-cert.pem psql "postgresql://postgres:password@localhost:5432/postgres?sslmode=verify-full"
```

> [!TIP]
> If you get file permission errors, make sure the current user can access the files.
> The previous step may set them to not readable by the current user.

Then, verify SSL Status by

```sql
-- Enable SSL info extension (required for ssl_is_used function)
CREATE EXTENSION IF NOT EXISTS sslinfo;

-- Check SSL status
SELECT ssl_is_used();

-- SSL connection details
SELECT * FROM pg_stat_ssl WHERE pid = pg_backend_pid();
```

## Martin Configuration

Martin can be configured using environment variables, the CLI, or the configuration file.
Which of them you choose is up to you.
You do not need to configure things twice.

- <details>
<summary>Environment Variables (click to expand)</summary>

```bash
export PGSSLROOTCERT=./ca-cert.pem
export DATABASE_URL="postgresql://postgres:password@localhost:5432/postgres?sslmode=verify-full"
martin
```

</details>
- <details>
<summary>Configuration File (click to expand)</summary>

```yaml
postgres:
ssl_root_cert: './ca-cert.pem'
connection_string: 'postgresql://postgres:password@localhost:5432/postgres?sslmode=verify-full'
```

</details>
- <details>
<summary>Command Line (click to expand)</summary>

```bash
martin --ca-root-file ./ca-cert.pem \
"postgresql://postgres:password@localhost:5432/postgres?sslmode=verify-full"
```

</details>

## Troubleshooting

You can get more context via the following commands:

```bash
# Verbose psql
PGSSLMODE=verify-full PGSSLROOTCERT=./ca-cert.pem psql -h localhost -U postgres -d postgres -v

# Debug Martin
RUST_LOG=debug martin postgresql://...
```

These are the errors that can occur:

- <details>
<summary>Certificate verification failed (click to expand)</summary>

- Check server certificate is signed by the CA
- Verify CA certificate path in `PGSSLROOTCERT`
- Ensure certificate files are readable

</details>
- <details>
<summary>Hostname verification failed (click to expand)</summary>

- Server certificate CN/SAN must match hostname
- Use `verify-ca` instead of `verify-full` if hostname doesn't match

</details>
- <details>
<summary>Permission denied (click to expand)</summary>

- Check certificate file permissions
- Private keys should be `chmod 400` and readable by the user running the application

</details>
- <details>
<summary>Connection refused (click to expand)</summary>

- Verify PostgreSQL accepts SSL connections
- Check `pg_hba.conf` allows SSL from your IP

</details>

## Security Best Practices if using postgres via SSL

- Use at least 3072-bit RSA keys
- Protect private keys with restricted permissions (`chmod 400`)
- Rotate certificates before expiration
- Use `verify-full` in production
- Monitor certificate expiration
- Store `ca-key.pem` securely (only needed for certificate management)
- Use secure secret management for production certificates
Loading