Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,22 +166,28 @@ In Postgres, conventions used, including in connection URI are as follow:

### `pog` SSL usage

In `pog`, setting up an SSL connection simply ask you to indicate the proper flag
in `pog.Config`. The different options are `SslDisabled`, `SslUnverified` &
`SslVerified`. Because of the nature of the 3 modes of SSL, and because talking
to your database should be highly secured to protect you against man-in-the-middle
attacks, you should always try to use the most secured setting.
In `pog`, SSL configuration is designed to be both secure and flexible. The library provides three SSL modes through the `Ssl` type:

- `SslVerified`: The most secure option that verifies CA certificates (recommended)
- `SslUnverified`: Enables SSL without certificate verification (use with caution)
- `SslDisabled`: No SSL encryption (not recommended for production)

Both `SslVerified` and `SslUnverified` modes support Server Name Indication (SNI), which is essential for proper certificate verification when connecting to databases using virtual hosting or multi-domain certificates.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a note saying it's recommended to set this to true please


```gleam
import pog

pub fn connect() {
pog.default_config()
|> pog.ssl(pog.SslVerified)
|> pog.ssl(pog.SslVerified(sni_enabled: True))
|> pog.connect
}
```

The `sni_enabled` parameter (defaults to `True`) helps ensure proper SSL certificate verification by sending the server name during the SSL handshake. This is particularly important for:
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it default to true? How is that?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that was my original plan but realised default are not that simple in Gleam, I'll remove it, good catch

- Databases using virtual hosting
- Certificates covering multiple domain names

### Need some help?

You tried to setup a secured connection, but it does not work? Your container
Expand Down
30 changes: 21 additions & 9 deletions src/pog.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,14 @@ pub type Ssl {
/// option to use SSL and should be always used by default.
/// Never ignore CA certificate checking _unless you know exactly what you are
/// doing_.
SslVerified
SslVerified(sni_enabled: Bool)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document the sni_enabled property and recommend to set this to true please 🙏

/// Enable SSL connection, but don't check CA certificate.
/// `SslVerified` should always be prioritized upon `SslUnverified`.
/// As it implies, that option enables SSL, but as it is unverified, the
/// connection can be unsafe. _Use this option only if you know what you're
/// doing._ In case `pog` can not find the proper CA certificate, take a look
/// at the README to get some help to inject the CA certificate in your OS.
SslUnverified
SslUnverified(sni_enabled: Bool)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document the sni_enabled property and recommend to set this to true please 🙏

/// Disable SSL connection completely. Using this option will let the
/// connection unsecured, and should be avoided in production environment.
SslDisabled
Expand Down Expand Up @@ -113,7 +113,19 @@ pub fn password(config: Config, password: Option(String)) -> Config {

/// Whether to use SSL or not.
///
/// (default: False)
/// The SSL configuration provides three modes:
/// - `SslVerified`: Most secure option that verifies CA certificates (recommended)
/// - `SslUnverified`: Enables SSL without certificate verification (use with caution)
/// - `SslDisabled`: No SSL encryption (not recommended for production)
///
/// Each SSL mode can be configured with SNI (Server Name Indication) support,
/// which is particularly useful for virtual hosting and multi-domain certificates.
///
/// Example:
/// ```gleam
/// pog.default_config()
/// |> pog.ssl(pog.SslVerified(sni_enabled: True))
/// ```
pub fn ssl(config: Config, ssl: Ssl) -> Config {
Config(..config, ssl:)
}
Expand All @@ -125,10 +137,10 @@ pub fn connection_parameter(
name name: String,
value value: String,
) -> Config {
Config(
..config,
connection_parameters: [#(name, value), ..config.connection_parameters],
)
Config(..config, connection_parameters: [
#(name, value),
..config.connection_parameters
])
}

/// Number of connections to keep open with the database
Expand Down Expand Up @@ -290,8 +302,8 @@ fn extract_ssl_mode(query: option.Option(String)) -> Result(Ssl, Nil) {
use query <- result.then(uri.parse_query(query))
use sslmode <- result.then(list.key_find(query, "sslmode"))
case sslmode {
"require" -> Ok(SslUnverified)
"verify-ca" | "verify-full" -> Ok(SslVerified)
"require" -> Ok(SslUnverified(sni_enabled: True))
"verify-ca" | "verify-full" -> Ok(SslVerified(sni_enabled: True))
"disable" -> Ok(SslDisabled)
_ -> Error(Nil)
}
Expand Down
39 changes: 29 additions & 10 deletions src/pog_ffi.erl
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,37 @@ coerce(Value) ->
%% will not be handled correctly.
default_ssl_options(Host, Ssl) ->
case Ssl of
ssl_disabled -> {false, []};
ssl_unverified -> {true, [{verify, verify_none}]};
ssl_verified -> {true, [
{verify, verify_peer},
{cacerts, public_key:cacerts_get()},
{server_name_indication, binary_to_list(Host)},
{customize_hostname_check, [
{match_fun, public_key:pkix_verify_hostname_match_fun(https)}
]}
]}
{ssl_disabled} -> {false, []};
{ssl_unverified, SniEnabled} -> {true, get_unverified_options(Host, SniEnabled)};
{ssl_verified, SniEnabled} -> {true, get_verified_options(Host, SniEnabled)}
end.

%% Options for unverified SSL connections
get_unverified_options(Host, SniEnabled) ->
[
{verify, verify_none}
] ++ get_sni_options(Host, SniEnabled).

%% Options for verified SSL connections
get_verified_options(Host, SniEnabled) ->
[
{verify, verify_peer},
{cacerts, public_key:cacerts_get()}
] ++ get_hostname_check_options() ++ get_sni_options(Host, SniEnabled).

%% Server Name Indication (SNI) options
get_sni_options(Host, true) ->
[{server_name_indication, binary_to_list(Host)}];
get_sni_options(_Host, false) -> [].

%% Hostname verification options for wildcard certificates
get_hostname_check_options() ->
[
{customize_hostname_check, [
{match_fun, public_key:pkix_verify_hostname_match_fun(https)}
]}
].

connect(Config) ->
Id = integer_to_list(erlang:unique_integer([positive])),
PoolName = list_to_atom("pog_pool_" ++ Id),
Expand Down