Skip to content

Add sslmode verify-ca and verify-full #774

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
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
127 changes: 78 additions & 49 deletions docker/sql_setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,76 @@
set -e

cat > "$PGDATA/server.key" <<-EOKEY
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAllItXwrj62MkxKVlz2FimJk42WWc3K82Rn2vAl6z38zQxSCj
t9uWwXWTx5YOdGiUcA+JUAruZxqN7vdfphJoYtTrcrpT4rC/FsCMImBxkj1cxdYT
q94SFn9bQBRZk7RUx4Kolt+/h0d3PpNIb4DbyQ8A0MVvNVxLpRRVwc6yQP+NkRMy
gHR+m3P8fxHEtkHCVy7HORbASvN8fRlREMHDL2hkadX0BNM72DDo+DWhPA8GF6WX
tIl1gU6GP6pSbEeMHD3f+uj7f9iSjvkrHrOt2nLUQ9Qnev2nhmU0/dOIweQ17/Fr
lL9jYDUUFNORyjRnlXXUoP5BO/LdEAAqT2A0pwIDAQABAoIBAQCIXu74XUneHuiZ
Wa+eTqwC4mZXmz6OWonzs0vU65NlgksXuv+r6ZO/2GoD1Bcy9jlL3Fxm+DPF56pB
07u7TtHSb3VWdMFrU4tYGcBH45TE5dRHSmo4LlPcgxeGb6/ANwX+pYNKtJvuHyCH
7Vf2iEFcCrdjrumv0BZ0IZmXJGxEV+7mK2Og0bZ/zbmJNaH25muuWj6BKlvLhL0N
S2LlBjKx3HqtppUgUqNFqjLs6IA1u79S5dAomOsxZtnuByaX5WFzpktU2pveZmyF
cl0dwHYZIaxR3ewYeQXGF8ANUmIx3nnxD2JOysPkitaGzeqt6dQZV14tPlDZDKat
Vf0b6BHhAoGBAMWV7rG+7nVXoQ30CIcPGklkST3mVOlrzeBbKP1SeAwoGRbfsdhp
rFMkh5UxTexnOzD4O8HPuJ6NGeWRQfqZT1nnjwHPeJWtiMHT6cnWxlzvxAZ61mio
0jRfb8flhgFKk+G9+Xa6WaYAAwGWdF062EMe2Ym92oKM9ilTPGFVRk1XAoGBAMLD
ETSQd2UqTF/y7wxMPqF3l6d1KBjwpuNuin2IjkXTOfGkDnAU3mSQlr7K1IPX8NPO
gdyMfJoysfRaBuRcNA/o/0l0wyxW4HWtTtPYI0+pRCFtRLsI1MB997QKeaGKb+me
3nBXkOksPSr9oa0Cs27z2cSoBOkpq2N/zzBseHExAoGAOyq3rKBZNehEwTHnb9I0
8+9FA3U6zh9LKjkCIEGW00Uapj/cOMsEIG2a8DEwfW84SWS8OEBkr43fSGBkGo/Y
NDrkFw2ytVee0TQNGTTod6IQ2EPmera7I5XEml5/71kOyZWi40vQVqZAQDR2qgha
BFdzmwywJ1Hg0OUs+pSXlccCgYEAgyOVki80NYolovWQwFcWVOKR2s+oECL6PGlS
FvS714hCm9I7ZnymwlAZMJ6iOaRNJFEIX9i4jZtU95Mm0NzEsXHRc0SLpm9Y8+Oe
EEaYgCsZFOjePpHTr0kiYLgs7fipIkU2wa40hMyk4y2kjzoiV7MaDrCTnevQ205T
0+c1sgECgYBAXKcwdkh9JVSrLXFamsxiOx3MZ0n6J1d28wpdA3y4Y4AAJm4TGgFt
eG/6qHRy6CHdFtJ7a84EMe1jaVLQJYW/VrOC2bWLftkU7qaOnkXHvr4CAHsXQHcx
JhLfvh4ab3KyoK/iimifvcoS5z9gp7IBFKMyh5IeJ9Y75TgcfJ5HMg==
-----END RSA PRIVATE KEY-----
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDX71FGKHUepupP
7c4VGoeX+Dkn5PZjDkwv+DYsTxpcZ0x/S4fHSz+aEX7cP8M0vxPmxOwJPoAm2Vt2
RTva+8TrGClzWPT8YHzyN7r8jdP9crBQDAidYuQdHxPpXji9TMyRAGk51Qy0U+K/
Wjx787LsWF3DFTgvJdw7YaD9EZzG7sqXvCaWAQ8sxUdj0moSm42ftVFx4Ztv5JEo
nAgKkuJ+0KklLJ08/KTp8OMm95PJzHPl+MDxqDv3jqvg5eVUnm+nE9kgZvTBtNNH
YJM3VZ0rjQDThNlLz9PdHWpfKlBTv1uVGg2SWfVpiaqxq4n686QZPHS0fu/ZQejx
h0bZ8qeBAgMBAAECggEBAKKZ3XOdJ4RrYGnLwrF1hsFS84ctDLPOomRE3lZDQrBu
QNZiQ944ta4ImqSzhwUDFbNiefMEE3AtoIfQ3p+pksENMrlfNSuOZMfoW2+uRQHH
CSldxmbtfqTHMDE8+DDj0e8mhhY8bhKkUEyTYJReEE+UwxYRtnsaYVp9y8KFLq9E
4f6NDMzzSpw/ujkcACtx0DxeWZfaP6Ms4ydh2uDEvzUwnmw4kpsgo4NtPLHDNx/y
kshfSpayYBKJ08qpzUAOXpi2UIRzvrYZE5cAcXtK6Jw02VnpNIr6+q/DAE58at7W
RwvswhNdpVVVwn04o68c4GUsGQFG/Qve8hDLdN+KygECgYEA//PE9FAeuSy2KWxS
HKSYZ422Sx/M4tuAQrX//yCFizxEhs9SF3ybZX4SCHGeQxeogIqOrFKKrXpzpCcH
3fB4LjRpdUKdv11sxFoo0Jw6wtY3N+24yM3jrpDsQqcCxUxm+qOgwGyKRJCkLxK3
RNkAAmoT2mONeaMyWLg5g4wVW5ECgYEA1/miyi6PmT3+y4DCdNYdJll3NRmR4DGk
HDYOd+Qb+DSoBhcxz/bqBDDdXr6FT3nZEkTxKAsaPjarzKjK2J88fvnF2JRnM5Oy
HKRNk3a9KxM//UwUgoLCdg/qZe4EXX9LJr06G6YGgg0uG6Cjsa+rZ33FiucBYrEL
aevQ+cReNPECgYEAsCDlRWHk4nQ8HiEmGAPDxG6mJOgLK4j0p/Np5/xPKVMdrM75
pKPgo2SvsBPPXkfnchzmtPpP57S94xXguf8CFHmIoGJo/wihEjUgpPz9CpoygVAa
ukPEC5o6mlsm8vHyY0M6GXAXbbtC4Am3B69z7DVm1/9tmWiN+rM7EKTTBaECgYBi
qOUWmyJ6DHoCmLU8DjuOszvjg+TBl6uyP3doiUnFnrhK3/mfWNoaRAA8MahQYAcr
c1b+xeOdG/hrK4hOYJ+QGaWphFGInCW3M89EV++eZ9LJcSHFZNpUeHzJR2uzEl1Q
Owz6aGN8sWyorj9ZAji4tBmzlEdrwBjIsDLshinK4QKBgEbmw1Dp1ZQEZcNiNKBl
EEzce+yHf8FSaC4KQSOnZIK30ZoHGLkQfr+C+8qKeDe4WYn3yf5zhjG7ssyxgWrB
S8GdV0OgrtvO5zhDH72KqddZe+api/34Zh2zY/2PKG2gBZ+ubsRpgptVK2ny5pmj
WN5CmfEv9kwQmSKzzSGUJ59l
-----END PRIVATE KEY-----
EOKEY
chmod 0600 "$PGDATA/server.key"

cat > "$PGDATA/server.crt" <<-EOCERT
-----BEGIN CERTIFICATE-----
MIID9DCCAtygAwIBAgIJAIYfg4EQ2pVAMA0GCSqGSIb3DQEBBQUAMFkxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xNjA2MjgyMjQw
NDFaFw0yNjA2MjYyMjQwNDFaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21l
LVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV
BAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJZS
LV8K4+tjJMSlZc9hYpiZONllnNyvNkZ9rwJes9/M0MUgo7fblsF1k8eWDnRolHAP
iVAK7mcaje73X6YSaGLU63K6U+KwvxbAjCJgcZI9XMXWE6veEhZ/W0AUWZO0VMeC
qJbfv4dHdz6TSG+A28kPANDFbzVcS6UUVcHOskD/jZETMoB0fptz/H8RxLZBwlcu
xzkWwErzfH0ZURDBwy9oZGnV9ATTO9gw6Pg1oTwPBhell7SJdYFOhj+qUmxHjBw9
3/ro+3/Yko75Kx6zrdpy1EPUJ3r9p4ZlNP3TiMHkNe/xa5S/Y2A1FBTTkco0Z5V1
1KD+QTvy3RAAKk9gNKcCAwEAAaOBvjCBuzAdBgNVHQ4EFgQUEcuoFxzUZ4VV9VPv
5frDyIuFA5cwgYsGA1UdIwSBgzCBgIAUEcuoFxzUZ4VV9VPv5frDyIuFA5ehXaRb
MFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJ
bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMTCWxvY2FsaG9zdIIJAIYf
g4EQ2pVAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHwMzmXdtz3R
83HIdRQic40bJQf9ucSwY5ArkttPhC8ewQGyiGexm1Tvx9YA/qT2rscKPHXCPYcP
IUE+nJTc8lQb8wPnFwGdHUsJfCvurxE4Yv4Oi74+q1enhHBGsvhFdFY5jTYD9unM
zBEn+ZHX3PlKhe3wMub4khBTbPLK+n/laQWuZNsa+kj7BynkAg8W/6RK0Z0cJzzw
aiVP0bSvatAAcSwkEfKEv5xExjWqoewjSlQLEZYIjJhXdtx/8AMnrcyxrFvKALUQ
9M15FXvlPOB7ez14xIXQBKvvLwXvteHF6kYbzg/Bl1Q2GE9usclPa4UvTpnLv6gq
NmFaAhoxnXA=
MIICojCCAYoCCQD51cTqxXxVZDANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdy
b290LWNhMB4XDTIxMDUxNzIxMDExM1oXDTIyMDUxNzIxMDExM1owFDESMBAGA1UE
AwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1+9R
Rih1HqbqT+3OFRqHl/g5J+T2Yw5ML/g2LE8aXGdMf0uHx0s/mhF+3D/DNL8T5sTs
CT6AJtlbdkU72vvE6xgpc1j0/GB88je6/I3T/XKwUAwInWLkHR8T6V44vUzMkQBp
OdUMtFPiv1o8e/Oy7FhdwxU4LyXcO2Gg/RGcxu7Kl7wmlgEPLMVHY9JqEpuNn7VR
ceGbb+SRKJwICpLiftCpJSydPPyk6fDjJveTycxz5fjA8ag7946r4OXlVJ5vpxPZ
IGb0wbTTR2CTN1WdK40A04TZS8/T3R1qXypQU79blRoNkln1aYmqsauJ+vOkGTx0
tH7v2UHo8YdG2fKngQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAqJLwuK1HIq2p0
I4N7HUjApiyxYWKqAeiC/65sLyU5TUfTgiSxTJRh625NwEYXzGTGbY674quIYK7I
uUIDrWTu2GBT1DIZJG78xbYfeWoHtKrTZ+MYy70FK448dI4lv0lZbmub0HircR2M
9MVqhWw8ik5FrpiR2DcwTkwNuNlVSu+hr/c/ljhvNP7dBfIxc9Og6xp1tyHW2hce
Vm/3HFjJqBfLw/lbZ6rx5wJA3E13r0LpnwuKQlgPYyaighfgetJdxorj37gCxLn3
77qfpOnFfk/mgY+bLFu9ncR2svab4CGRXPey9Kb6wP+OCwnh0vCBioocUFRANkLb
bEjAYyqo
-----END CERTIFICATE-----
EOCERT

cat > "$PGDATA/root.crt" <<-EOCERT
-----BEGIN CERTIFICATE-----
MIIDHTCCAgWgAwIBAgIJAPuMcWp8Si1PMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV
BAMMB3Jvb3QtY2EwHhcNMjEwNTE3MjEwMTEzWhcNMzEwNTE1MjEwMTEzWjASMRAw
DgYDVQQDDAdyb290LWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
vhPvG4K6R1GAe3/MwLWsb3uQX3Zs8Z+1R15+h1Sx1SwgXLuLDwxMxg0dkip/R0ic
XyJFeVntOQqZfZpwd3iD47AZx6c4/Hn+U6OQtfvY6FmYNfmhngAnk9nr5te4Fu+S
n7YwUJ0+pfC8b6idM5XB2YBnO1azqP5Sa230gSqxBlzjOqUC8rlvF1woDej49E3l
pzP7jD6yrZ3Z3SvF1+ZhW+6CvWi4cm8xfMaTCCvwoR3E7ia6OGUmNP/rkyxXSUHV
O1ELw0FY63+J46BzONR/MRuoXBm2SF07WY9+kS35SIOK1PjO0ndNRlXYv7xzCxDW
4EnfoTfDLwZ6vOBvcST9VwIDAQABo3YwdDAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
DgQWBBTbVbnrDPLMcVV0ExWd/EexzCMhzDBCBgNVHSMEOzA5gBTbVbnrDPLMcVV0
ExWd/EexzCMhzKEWpBQwEjEQMA4GA1UEAwwHcm9vdC1jYYIJAPuMcWp8Si1PMA0G
CSqGSIb3DQEBCwUAA4IBAQA83u6ILbpsQRwyb074exRo2vLC0pjtOBeRLyhi95zk
TtilDHNP5oYf4pmrTAagv+i5eOwwAvoaXil1+mAtckUkV0FRoxAX9U6ZTUFge9HE
G0VLfhqmzlExRl7O6Jr/O7fC6hOz5YDD0SdAaLGx35J9kbWyOLXAWCte3FImetdB
72lbGD8M9J9Sm12aN+e9a8xovFQQG8Sah4XVTubs3Yw8QOhs+kxIrw3LzRt3Nisa
ASCK93sHNpRUfePn/9x+2VAd6p1r4ypDJAH9Tr1E7duPBe+2YwBjMMDviA7eCiFA
Xi7zm5vUeHGuQOBUIz6HE7RGMhQNkORbQiopzVFOBkys
-----END CERTIFICATE-----
EOCERT

Expand All @@ -64,6 +80,7 @@ port = 5433
ssl = on
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
ssl_ca_file = 'root.crt'
EOCONF

cat > "$PGDATA/pg_hba.conf" <<-EOCONF
Expand All @@ -80,6 +97,16 @@ hostssl all ssl_user ::0/0 trust
host all ssl_user 0.0.0.0/0 reject
host all ssl_user ::0/0 reject

hostssl all cert_user_ca 0.0.0.0/0 trust clientcert=verify-ca
hostssl all cert_user_ca ::0/0 trust clientcert=verify-ca
host all cert_user_ca 0.0.0.0/0 reject
host all cert_user_ca ::0/0 reject

hostssl all cert_user_full 0.0.0.0/0 trust clientcert=verify-full
hostssl all cert_user_full ::0/0 trust clientcert=verify-full
host all cert_user_full 0.0.0.0/0 reject
host all cert_user_full ::0/0 reject

# IPv4 local connections:
host all postgres 0.0.0.0/0 trust
# IPv6 local connections:
Expand All @@ -94,6 +121,8 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL
SET password_encryption TO 'scram-sha-256';
CREATE ROLE scram_user PASSWORD 'password' LOGIN;
CREATE ROLE ssl_user LOGIN;
CREATE ROLE cert_user_ca LOGIN;
CREATE ROLE cert_user_full LOGIN;
CREATE EXTENSION hstore;
CREATE EXTENSION citext;
EOSQL
74 changes: 69 additions & 5 deletions postgres-openssl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,30 +43,41 @@

#[cfg(feature = "runtime")]
use openssl::error::ErrorStack;
use openssl::hash::MessageDigest;
use openssl::nid::Nid;
#[cfg(feature = "runtime")]
use openssl::ssl::SslConnector;
use openssl::ssl::{self, ConnectConfiguration, SslRef};
use openssl::ssl::{self, ConnectConfiguration, SslFiletype, SslRef};
use openssl::x509::X509VerifyResult;
use std::error::Error;
use openssl::{hash::MessageDigest, ssl::SslMethod};
use openssl::{nid::Nid, ssl::SslVerifyMode};
use std::fmt::{self, Debug};
use std::future::Future;
use std::io;
use std::pin::Pin;
#[cfg(feature = "runtime")]
use std::sync::Arc;
use std::task::{Context, Poll};
use std::{error::Error, path::PathBuf};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio_openssl::SslStream;
use tokio_postgres::tls;
#[cfg(feature = "runtime")]
use tokio_postgres::tls::MakeTlsConnect;
use tokio_postgres::tls::{ChannelBinding, TlsConnect};
use tokio_postgres::{config::SslMode, tls};

#[cfg(test)]
mod test;

/// TLS configuration.
#[cfg(feature = "runtime")]
pub struct TlsConfig {
/// SSL mode (`sslmode`).
pub mode: SslMode,
/// Location of the client cert and key (`sslcert`, `sslkey`).
pub client_cert: Option<(PathBuf, PathBuf)>,
/// Location of the root certificate (`sslrootcert`).
pub root_cert: Option<PathBuf>,
}

/// A `MakeTlsConnect` implementation using the `openssl` crate.
///
/// Requires the `runtime` Cargo feature (enabled by default).
Expand All @@ -87,6 +98,59 @@ impl MakeTlsConnector {
}
}

/// Creates a new connector from the provided [`TlsConfig`].
///
/// The returned [`MakeTlsConnector`] will be configured to mimick libpq-ssl behavior.
pub fn from_tls_config(tls_config: TlsConfig) -> Result<MakeTlsConnector, ErrorStack> {
let mut builder = SslConnector::builder(SslMethod::tls_client())?;
// The mode dictates whether we verify peer certs and hostnames. By default, Postgres is
// pretty relaxed and recommends SslMode::VerifyCa or SslMode::VerifyFull for security.
//
// For more details, check out Table 33.1. SSL Mode Descriptions in
// https://postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-PROTECTION.
let (verify_mode, verify_hostname) = match tls_config.mode {
SslMode::Disable | SslMode::Prefer => (SslVerifyMode::NONE, false),
SslMode::Require => match tls_config.root_cert {
// If a root CA file exists, the behavior of sslmode=require will be the same as
// that of verify-ca, meaning the server certificate is validated against the CA.
//
// For more details, check out the note about backwards compatibility in
// https://postgresql.org/docs/current/libpq-ssl.html#LIBQ-SSL-CERTIFICATES.
Some(_) => (SslVerifyMode::PEER, false),
None => (SslVerifyMode::NONE, false),
},
SslMode::VerifyCa => (SslVerifyMode::PEER, false),
SslMode::VerifyFull => (SslVerifyMode::PEER, true),
_ => panic!("unexpected sslmode {:?}", tls_config.mode),
};

// Configure peer verification
builder.set_verify(verify_mode);

// Configure certificates
if tls_config.client_cert.is_some() {
let (cert, key) = tls_config.client_cert.unwrap();
builder.set_certificate_file(cert, SslFiletype::PEM)?;
builder.set_private_key_file(key, SslFiletype::PEM)?;
}
if tls_config.root_cert.is_some() {
builder.set_ca_file(tls_config.root_cert.unwrap())?;
}

let mut tls_connector = MakeTlsConnector::new(builder.build());

// Configure hostname verification
match (verify_mode, verify_hostname) {
(SslVerifyMode::PEER, false) => tls_connector.set_callback(|connect, _| {
connect.set_verify_hostname(false);
Ok(())
}),
_ => {}
}

Ok(tls_connector)
}

/// Sets a callback used to apply per-connection configuration.
///
/// The the callback is provided the domain name along with the `ConnectConfiguration`.
Expand Down
Loading