mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2025-09-28 20:41:17 +00:00
Merge 3f40fc49c0
into 1f73630136
This commit is contained in:
commit
4f3786c574
5 changed files with 60 additions and 6 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -2748,6 +2748,7 @@ dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"chumsky",
|
"chumsky",
|
||||||
|
"ed25519-dalek",
|
||||||
"email-encoding",
|
"email-encoding",
|
||||||
"email_address",
|
"email_address",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
|
@ -2760,9 +2761,11 @@ dependencies = [
|
||||||
"nom 8.0.0",
|
"nom 8.0.0",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"quoted_printable",
|
"quoted_printable",
|
||||||
|
"rsa",
|
||||||
"rustls 0.23.31",
|
"rustls 0.23.31",
|
||||||
"rustls-native-certs",
|
"rustls-native-certs",
|
||||||
"serde",
|
"serde",
|
||||||
|
"sha2",
|
||||||
"socket2 0.6.0",
|
"socket2 0.6.0",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls 0.26.2",
|
"tokio-rustls 0.26.2",
|
||||||
|
|
|
@ -135,7 +135,7 @@ webauthn-rs-core = "0.5.2"
|
||||||
url = "2.5.4"
|
url = "2.5.4"
|
||||||
|
|
||||||
# Email libraries
|
# Email libraries
|
||||||
lettre = { version = "0.11.18", features = ["smtp-transport", "sendmail-transport", "builder", "serde", "hostname", "tracing", "tokio1-rustls", "ring", "rustls-native-certs"], default-features = false }
|
lettre = { version = "0.11.18", features = ["smtp-transport", "sendmail-transport", "dkim", "builder", "serde", "hostname", "tracing", "tokio1-rustls", "ring", "rustls-native-certs"], default-features = false }
|
||||||
percent-encoding = "2.3.1" # URL encoding library used for URL's in the emails
|
percent-encoding = "2.3.1" # URL encoding library used for URL's in the emails
|
||||||
email_address = "0.2.9"
|
email_address = "0.2.9"
|
||||||
|
|
||||||
|
|
|
@ -772,6 +772,12 @@ make_config! {
|
||||||
smtp_username: String, true, option;
|
smtp_username: String, true, option;
|
||||||
/// Password
|
/// Password
|
||||||
smtp_password: Pass, true, option;
|
smtp_password: Pass, true, option;
|
||||||
|
/// Dkim signature (type:privatekey). Private must be base64-encoded ed key or PKCS#1 format RSA key.
|
||||||
|
dkim_signature: String, true, option;
|
||||||
|
/// Dkim algo (true if RSA else ed25519)
|
||||||
|
dkim_use_rsa: bool, true, option;
|
||||||
|
/// Dkim infos (selector:domain)
|
||||||
|
dkim_infos: String, true, option;
|
||||||
/// SMTP Auth mechanism |> Defaults for SSL is "Plain" and "Login" and nothing for Non-SSL connections. Possible values: ["Plain", "Login", "Xoauth2"]. Multiple options need to be separated by a comma ','.
|
/// SMTP Auth mechanism |> Defaults for SSL is "Plain" and "Login" and nothing for Non-SSL connections. Possible values: ["Plain", "Login", "Xoauth2"]. Multiple options need to be separated by a comma ','.
|
||||||
smtp_auth_mechanism: String, true, option;
|
smtp_auth_mechanism: String, true, option;
|
||||||
/// SMTP connection timeout |> Number of seconds when to stop trying to connect to the SMTP server
|
/// SMTP connection timeout |> Number of seconds when to stop trying to connect to the SMTP server
|
||||||
|
|
50
src/mail.rs
50
src/mail.rs
|
@ -3,7 +3,10 @@ use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
|
||||||
use std::{env::consts::EXE_SUFFIX, str::FromStr};
|
use std::{env::consts::EXE_SUFFIX, str::FromStr};
|
||||||
|
|
||||||
use lettre::{
|
use lettre::{
|
||||||
message::{Attachment, Body, Mailbox, Message, MultiPart, SinglePart},
|
message::{
|
||||||
|
dkim::{DkimConfig, DkimSigningAlgorithm, DkimSigningKey},
|
||||||
|
dkim_sign, Attachment, Body, Mailbox, Message, MultiPart, SinglePart,
|
||||||
|
},
|
||||||
transport::smtp::authentication::{Credentials, Mechanism as SmtpAuthMechanism},
|
transport::smtp::authentication::{Credentials, Mechanism as SmtpAuthMechanism},
|
||||||
transport::smtp::client::{Tls, TlsParameters},
|
transport::smtp::client::{Tls, TlsParameters},
|
||||||
transport::smtp::extension::ClientId,
|
transport::smtp::extension::ClientId,
|
||||||
|
@ -689,7 +692,44 @@ async fn send_with_selected_transport(email: Message) -> EmptyResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn check_dkim() -> Result<Option<DkimConfig>, String> {
|
||||||
|
match (CONFIG.dkim_signature(), CONFIG.dkim_infos()) {
|
||||||
|
(Some(sig), Some(infos)) => {
|
||||||
|
let config = {
|
||||||
|
let algo = match CONFIG.dkim_use_rsa() {
|
||||||
|
Some(true) => DkimSigningAlgorithm::Rsa,
|
||||||
|
_ => DkimSigningAlgorithm::Ed25519,
|
||||||
|
};
|
||||||
|
let sig = match std::fs::read_to_string(sig) {
|
||||||
|
Err(e) => {
|
||||||
|
return Err(format!("Cannot read DKIM file. Err is {:?}", e));
|
||||||
|
}
|
||||||
|
Ok(key) => match DkimSigningKey::new(&key, algo) {
|
||||||
|
Ok(d) => d,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(format!("Cannot read DKIM file. Err is {:?}", e));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
match (sig, infos.split(':').collect::<Vec<&str>>()) {
|
||||||
|
(sig, split2) if split2.len() == 2 => {
|
||||||
|
let (selector, domain, sig) =
|
||||||
|
(String::from(*split2.first().unwrap()), String::from(*split2.last().unwrap()), sig);
|
||||||
|
(selector, domain, sig)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err("DKIM issue, invalid domain, selector.".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Some(DkimConfig::default_config(config.0, config.1, config.2)))
|
||||||
|
}
|
||||||
|
(None, None) => Ok(None),
|
||||||
|
_ => {
|
||||||
|
Err("DKIM setting is badly implemented. One config is missing (DKIM signature or DKIM infos).".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
async fn send_email(address: &str, subject: &str, body_html: String, body_text: String) -> EmptyResult {
|
async fn send_email(address: &str, subject: &str, body_html: String, body_text: String) -> EmptyResult {
|
||||||
let smtp_from = &CONFIG.smtp_from();
|
let smtp_from = &CONFIG.smtp_from();
|
||||||
|
|
||||||
|
@ -712,12 +752,14 @@ async fn send_email(address: &str, subject: &str, body_html: String, body_text:
|
||||||
MultiPart::alternative_plain_html(body_text, body_html)
|
MultiPart::alternative_plain_html(body_text, body_html)
|
||||||
};
|
};
|
||||||
|
|
||||||
let email = Message::builder()
|
let mut email = Message::builder()
|
||||||
.message_id(Some(format!("<{}@{}>", crate::util::get_uuid(), smtp_from.split('@').collect::<Vec<&str>>()[1])))
|
.message_id(Some(format!("<{}@{}>", crate::util::get_uuid(), smtp_from.split('@').collect::<Vec<&str>>()[1])))
|
||||||
.to(Mailbox::new(None, Address::from_str(address)?))
|
.to(Mailbox::new(None, Address::from_str(address)?))
|
||||||
.from(Mailbox::new(Some(CONFIG.smtp_from_name()), Address::from_str(smtp_from)?))
|
.from(Mailbox::new(Some(CONFIG.smtp_from_name()), Address::from_str(smtp_from)?))
|
||||||
.subject(subject)
|
.subject(subject)
|
||||||
.multipart(body)?;
|
.multipart(body)?;
|
||||||
|
if let Ok(Some(sig)) = check_dkim() {
|
||||||
|
dkim_sign(&mut email, &sig);
|
||||||
|
}
|
||||||
send_with_selected_transport(email).await
|
send_with_selected_transport(email).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,10 @@ async fn main() -> Result<(), Error> {
|
||||||
schedule_jobs(pool.clone());
|
schedule_jobs(pool.clone());
|
||||||
db::models::TwoFactor::migrate_u2f_to_webauthn(&mut pool.get().await.unwrap()).await.unwrap();
|
db::models::TwoFactor::migrate_u2f_to_webauthn(&mut pool.get().await.unwrap()).await.unwrap();
|
||||||
db::models::TwoFactor::migrate_credential_to_passkey(&mut pool.get().await.unwrap()).await.unwrap();
|
db::models::TwoFactor::migrate_credential_to_passkey(&mut pool.get().await.unwrap()).await.unwrap();
|
||||||
|
if let Err(e) = mail::check_dkim() {
|
||||||
|
error!("{}", e);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
let extra_debug = matches!(level, log::LevelFilter::Trace | log::LevelFilter::Debug);
|
let extra_debug = matches!(level, log::LevelFilter::Trace | log::LevelFilter::Debug);
|
||||||
launch_rocket(pool, extra_debug).await // Blocks until program termination.
|
launch_rocket(pool, extra_debug).await // Blocks until program termination.
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue