You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-08-06 06:02:40 +03:00
New config options to set the database certificates
This commit is contained in:
@@ -228,6 +228,54 @@ fn database_connect_options_from_config(
|
|||||||
opts
|
opts
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let options = match (config.ssl_ca.as_deref(), config.ssl_ca_file.as_deref()) {
|
||||||
|
(None, None) => options,
|
||||||
|
(Some(pem), None) => options.ssl_root_cert_from_pem(pem.as_bytes().to_owned()),
|
||||||
|
(None, Some(path)) => options.ssl_root_cert(path),
|
||||||
|
(Some(_), Some(_)) => {
|
||||||
|
anyhow::bail!("invalid database configuration: both `ssl_ca` and `ssl_ca_file` are set")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let options = match (
|
||||||
|
config.ssl_certificate.as_deref(),
|
||||||
|
config.ssl_certificate_file.as_deref(),
|
||||||
|
) {
|
||||||
|
(None, None) => options,
|
||||||
|
(Some(pem), None) => options.ssl_client_cert_from_pem(pem.as_bytes()),
|
||||||
|
(None, Some(path)) => options.ssl_client_cert(path),
|
||||||
|
(Some(_), Some(_)) => {
|
||||||
|
anyhow::bail!("invalid database configuration: both `ssl_certificate` and `ssl_certificate_file` are set")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let options = match (config.ssl_key.as_deref(), config.ssl_key_file.as_deref()) {
|
||||||
|
(None, None) => options,
|
||||||
|
(Some(pem), None) => options.ssl_client_key_from_pem(pem.as_bytes()),
|
||||||
|
(None, Some(path)) => options.ssl_client_key(path),
|
||||||
|
(Some(_), Some(_)) => {
|
||||||
|
anyhow::bail!(
|
||||||
|
"invalid database configuration: both `ssl_key` and `ssl_key_file` are set"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let options = match &config.ssl_mode {
|
||||||
|
Some(ssl_mode) => {
|
||||||
|
let ssl_mode = match ssl_mode {
|
||||||
|
mas_config::PgSslMode::Disable => sqlx::postgres::PgSslMode::Disable,
|
||||||
|
mas_config::PgSslMode::Allow => sqlx::postgres::PgSslMode::Allow,
|
||||||
|
mas_config::PgSslMode::Prefer => sqlx::postgres::PgSslMode::Prefer,
|
||||||
|
mas_config::PgSslMode::Require => sqlx::postgres::PgSslMode::Require,
|
||||||
|
mas_config::PgSslMode::VerifyCa => sqlx::postgres::PgSslMode::VerifyCa,
|
||||||
|
mas_config::PgSslMode::VerifyFull => sqlx::postgres::PgSslMode::VerifyFull,
|
||||||
|
};
|
||||||
|
|
||||||
|
options.ssl_mode(ssl_mode)
|
||||||
|
}
|
||||||
|
None => options,
|
||||||
|
};
|
||||||
|
|
||||||
let options = options
|
let options = options
|
||||||
.log_statements(LevelFilter::Debug)
|
.log_statements(LevelFilter::Debug)
|
||||||
.log_slow_statements(LevelFilter::Warn, Duration::from_millis(100));
|
.log_slow_statements(LevelFilter::Warn, Duration::from_millis(100));
|
||||||
|
@@ -55,6 +55,13 @@ impl Default for DatabaseConfig {
|
|||||||
username: None,
|
username: None,
|
||||||
password: None,
|
password: None,
|
||||||
database: None,
|
database: None,
|
||||||
|
ssl_mode: None,
|
||||||
|
ssl_ca: None,
|
||||||
|
ssl_ca_file: None,
|
||||||
|
ssl_certificate: None,
|
||||||
|
ssl_certificate_file: None,
|
||||||
|
ssl_key: None,
|
||||||
|
ssl_key_file: None,
|
||||||
max_connections: default_max_connections(),
|
max_connections: default_max_connections(),
|
||||||
min_connections: Default::default(),
|
min_connections: Default::default(),
|
||||||
connect_timeout: default_connect_timeout(),
|
connect_timeout: default_connect_timeout(),
|
||||||
@@ -64,6 +71,34 @@ impl Default for DatabaseConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Options for controlling the level of protection provided for PostgreSQL SSL
|
||||||
|
/// connections.
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
pub enum PgSslMode {
|
||||||
|
/// Only try a non-SSL connection.
|
||||||
|
Disable,
|
||||||
|
|
||||||
|
/// First try a non-SSL connection; if that fails, try an SSL connection.
|
||||||
|
Allow,
|
||||||
|
|
||||||
|
/// First try an SSL connection; if that fails, try a non-SSL connection.
|
||||||
|
Prefer,
|
||||||
|
|
||||||
|
/// Only try an SSL connection. If a root CA file is present, verify the
|
||||||
|
/// connection in the same way as if `VerifyCa` was specified.
|
||||||
|
Require,
|
||||||
|
|
||||||
|
/// Only try an SSL connection, and verify that the server certificate is
|
||||||
|
/// issued by a trusted certificate authority (CA).
|
||||||
|
VerifyCa,
|
||||||
|
|
||||||
|
/// Only try an SSL connection; verify that the server certificate is issued
|
||||||
|
/// by a trusted CA and that the requested server host name matches that
|
||||||
|
/// in the certificate.
|
||||||
|
VerifyFull,
|
||||||
|
}
|
||||||
|
|
||||||
/// Database connection configuration
|
/// Database connection configuration
|
||||||
#[serde_as]
|
#[serde_as]
|
||||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
@@ -115,6 +150,50 @@ pub struct DatabaseConfig {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub database: Option<String>,
|
pub database: Option<String>,
|
||||||
|
|
||||||
|
/// How to handle SSL connections
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub ssl_mode: Option<PgSslMode>,
|
||||||
|
|
||||||
|
/// The PEM-encoded root certificate for SSL connections
|
||||||
|
///
|
||||||
|
/// This must not be specified if the `ssl_ca_file` option is specified.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub ssl_ca: Option<String>,
|
||||||
|
|
||||||
|
/// Path to the root certificate for SSL connections
|
||||||
|
///
|
||||||
|
/// This must not be specified if the `ssl_ca` option is specified.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[schemars(with = "Option<String>")]
|
||||||
|
pub ssl_ca_file: Option<Utf8PathBuf>,
|
||||||
|
|
||||||
|
/// The PEM-encoded client certificate for SSL connections
|
||||||
|
///
|
||||||
|
/// This must not be specified if the `ssl_certificate_file` option is
|
||||||
|
/// specified.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub ssl_certificate: Option<String>,
|
||||||
|
|
||||||
|
/// Path to the client certificate for SSL connections
|
||||||
|
///
|
||||||
|
/// This must not be specified if the `ssl_certificate` option is specified.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[schemars(with = "Option<String>")]
|
||||||
|
pub ssl_certificate_file: Option<Utf8PathBuf>,
|
||||||
|
|
||||||
|
/// The PEM-encoded client key for SSL connections
|
||||||
|
///
|
||||||
|
/// This must not be specified if the `ssl_key_file` option is specified.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub ssl_key: Option<String>,
|
||||||
|
|
||||||
|
/// Path to the client key for SSL connections
|
||||||
|
///
|
||||||
|
/// This must not be specified if the `ssl_key` option is specified.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[schemars(with = "Option<String>")]
|
||||||
|
pub ssl_key_file: Option<Utf8PathBuf>,
|
||||||
|
|
||||||
/// Set the maximum number of connections the pool should maintain
|
/// Set the maximum number of connections the pool should maintain
|
||||||
#[serde(default = "default_max_connections")]
|
#[serde(default = "default_max_connections")]
|
||||||
pub max_connections: NonZeroU32,
|
pub max_connections: NonZeroU32,
|
||||||
@@ -153,6 +232,12 @@ impl ConfigurationSection for DatabaseConfig {
|
|||||||
|
|
||||||
fn validate(&self, figment: &figment::Figment) -> Result<(), figment::error::Error> {
|
fn validate(&self, figment: &figment::Figment) -> Result<(), figment::error::Error> {
|
||||||
let metadata = figment.find_metadata(Self::PATH.unwrap());
|
let metadata = figment.find_metadata(Self::PATH.unwrap());
|
||||||
|
let annotate = |mut error: figment::Error| {
|
||||||
|
error.metadata = metadata.cloned();
|
||||||
|
error.profile = Some(figment::Profile::Default);
|
||||||
|
error.path = vec![Self::PATH.unwrap().to_owned()];
|
||||||
|
Err(error)
|
||||||
|
};
|
||||||
|
|
||||||
// Check that the user did not specify both `uri` and the split options at the
|
// Check that the user did not specify both `uri` and the split options at the
|
||||||
// same time
|
// same time
|
||||||
@@ -164,19 +249,42 @@ impl ConfigurationSection for DatabaseConfig {
|
|||||||
|| self.database.is_some();
|
|| self.database.is_some();
|
||||||
|
|
||||||
if self.uri.is_some() && has_split_options {
|
if self.uri.is_some() && has_split_options {
|
||||||
let mut error = figment::error::Error::from(
|
return annotate(figment::error::Error::from(
|
||||||
"uri must not be specified if host, port, socket, username, password, or database are specified".to_owned(),
|
"uri must not be specified if host, port, socket, username, password, or database are specified".to_owned(),
|
||||||
);
|
));
|
||||||
error.metadata = metadata.cloned();
|
}
|
||||||
error.profile = Some(figment::Profile::Default);
|
|
||||||
error.path = vec![Self::PATH.unwrap().to_owned(), "uri".to_owned()];
|
if self.ssl_ca.is_some() && self.ssl_ca_file.is_some() {
|
||||||
return Err(error);
|
return annotate(figment::error::Error::from(
|
||||||
|
"ssl_ca must not be specified if ssl_ca_file is specified".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.ssl_certificate.is_some() && self.ssl_certificate_file.is_some() {
|
||||||
|
return annotate(figment::error::Error::from(
|
||||||
|
"ssl_certificate must not be specified if ssl_certificate_file is specified"
|
||||||
|
.to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.ssl_key.is_some() && self.ssl_key_file.is_some() {
|
||||||
|
return annotate(figment::error::Error::from(
|
||||||
|
"ssl_key must not be specified if ssl_key_file is specified".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.ssl_key.is_some() || self.ssl_key_file.is_some())
|
||||||
|
^ (self.ssl_certificate.is_some() || self.ssl_certificate_file.is_some())
|
||||||
|
{
|
||||||
|
return annotate(figment::error::Error::from(
|
||||||
|
"both a ssl_certificate and a ssl_key must be set at the same time or none of them"
|
||||||
|
.to_owned(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use figment::{
|
use figment::{
|
||||||
|
@@ -35,7 +35,7 @@ pub use self::{
|
|||||||
branding::BrandingConfig,
|
branding::BrandingConfig,
|
||||||
captcha::{CaptchaConfig, CaptchaServiceKind},
|
captcha::{CaptchaConfig, CaptchaServiceKind},
|
||||||
clients::{ClientAuthMethodConfig, ClientConfig, ClientsConfig},
|
clients::{ClientAuthMethodConfig, ClientConfig, ClientsConfig},
|
||||||
database::DatabaseConfig,
|
database::{DatabaseConfig, PgSslMode},
|
||||||
email::{EmailConfig, EmailSmtpMode, EmailTransportKind},
|
email::{EmailConfig, EmailSmtpMode, EmailTransportKind},
|
||||||
experimental::ExperimentalConfig,
|
experimental::ExperimentalConfig,
|
||||||
http::{
|
http::{
|
||||||
|
@@ -1003,6 +1003,38 @@
|
|||||||
"description": "The database name\n\nThis must not be specified if `uri` is specified.",
|
"description": "The database name\n\nThis must not be specified if `uri` is specified.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ssl_mode": {
|
||||||
|
"description": "How to handle SSL connections",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/PgSslMode"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ssl_ca": {
|
||||||
|
"description": "The PEM-encoded root certificate for SSL connections\n\nThis must not be specified if the `ssl_ca_file` option is specified.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ssl_ca_file": {
|
||||||
|
"description": "Path to the root certificate for SSL connections\n\nThis must not be specified if the `ssl_ca` option is specified.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ssl_certificate": {
|
||||||
|
"description": "The PEM-encoded client certificate for SSL connections\n\nThis must not be specified if the `ssl_certificate_file` option is specified.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ssl_certificate_file": {
|
||||||
|
"description": "Path to the client certificate for SSL connections\n\nThis must not be specified if the `ssl_certificate` option is specified.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ssl_key": {
|
||||||
|
"description": "The PEM-encoded client key for SSL connections\n\nThis must not be specified if the `ssl_key_file` option is specified.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ssl_key_file": {
|
||||||
|
"description": "Path to the client key for SSL connections\n\nThis must not be specified if the `ssl_key` option is specified.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"max_connections": {
|
"max_connections": {
|
||||||
"description": "Set the maximum number of connections the pool should maintain",
|
"description": "Set the maximum number of connections the pool should maintain",
|
||||||
"default": 10,
|
"default": 10,
|
||||||
@@ -1044,6 +1076,53 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "hostname"
|
"format": "hostname"
|
||||||
},
|
},
|
||||||
|
"PgSslMode": {
|
||||||
|
"description": "Options for controlling the level of protection provided for PostgreSQL SSL connections.",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"description": "Only try a non-SSL connection.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"disable"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "First try a non-SSL connection; if that fails, try an SSL connection.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"allow"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "First try an SSL connection; if that fails, try a non-SSL connection.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"prefer"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Only try an SSL connection. If a root CA file is present, verify the connection in the same way as if `VerifyCa` was specified.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"require"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Only try an SSL connection, and verify that the server certificate is issued by a trusted certificate authority (CA).",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"verify-ca"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Only try an SSL connection; verify that the server certificate is issued by a trusted CA and that the requested server host name matches that in the certificate.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"verify-full"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"TelemetryConfig": {
|
"TelemetryConfig": {
|
||||||
"description": "Configuration related to sending monitoring data",
|
"description": "Configuration related to sending monitoring data",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
Reference in New Issue
Block a user