mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2026-05-08 12:34:43 +02:00
Merge f829426d2c into 7cf0c5d67e
This commit is contained in:
commit
e0d5829e44
4 changed files with 48 additions and 16 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
|
@ -2993,6 +2993,7 @@ dependencies = [
|
|||
"async-std",
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
"ed25519-dalek",
|
||||
"email-encoding",
|
||||
"email_address",
|
||||
"fastrand",
|
||||
|
|
@ -3005,9 +3006,11 @@ dependencies = [
|
|||
"nom 8.0.0",
|
||||
"percent-encoding",
|
||||
"quoted_printable",
|
||||
"rsa",
|
||||
"rustls 0.23.38",
|
||||
"rustls-native-certs",
|
||||
"serde",
|
||||
"sha2 0.10.9",
|
||||
"socket2 0.6.3",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.4",
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ webauthn-rs-core = "0.5.4"
|
|||
url = "2.5.8"
|
||||
|
||||
# Email libraries
|
||||
lettre = { version = "0.11.21", features = ["smtp-transport", "sendmail-transport", "builder", "serde", "hostname", "tracing", "tokio1-rustls", "ring", "rustls-native-certs"], default-features = false }
|
||||
lettre = { version = "0.11.21", features = ["smtp-transport", "sendmail-transport", "dkim", "builder", "serde", "hostname", "tracing", "tokio1-rustls", "ring", "rustls-native-certs"], default-features = false }
|
||||
percent-encoding = "2.3.2" # URL encoding library used for URL's in the emails
|
||||
email_address = "0.2.9"
|
||||
|
||||
|
|
|
|||
|
|
@ -13,11 +13,9 @@ use reqwest::Url;
|
|||
use serde::de::{self, Deserialize, Deserializer, MapAccess, Visitor};
|
||||
|
||||
use crate::{
|
||||
error::Error,
|
||||
util::{
|
||||
get_active_web_release, get_env, get_env_bool, is_valid_email, parse_experimental_client_feature_flags,
|
||||
FeatureFlagFilter,
|
||||
},
|
||||
error::Error, mail::check_dkim, util::{
|
||||
FeatureFlagFilter, get_active_web_release, get_env, get_env_bool, is_valid_email, parse_experimental_client_feature_flags
|
||||
}
|
||||
};
|
||||
|
||||
static CONFIG_FILE: LazyLock<String> = LazyLock::new(|| {
|
||||
|
|
@ -888,6 +886,12 @@ make_config! {
|
|||
smtp_username: String, true, option;
|
||||
/// Password
|
||||
smtp_password: Pass, true, option;
|
||||
/// Dkim private key (type:privatekey). Private must be base64-encoded ed key or PKCS#1 format RSA key.
|
||||
dkim_privatekey: String, true, option;
|
||||
/// Dkim algo (true if RSA else ed25519)
|
||||
dkim_use_rsa: bool, true, def, false;
|
||||
/// 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: String, true, option;
|
||||
/// SMTP connection timeout |> Number of seconds when to stop trying to connect to the SMTP server
|
||||
|
|
@ -1150,6 +1154,9 @@ fn validate_config(cfg: &ConfigItems, on_update: bool) -> Result<(), Error> {
|
|||
if cfg._enable_email_2fa && cfg.email_token_size < 6 {
|
||||
err!("`EMAIL_TOKEN_SIZE` has a minimum size of 6")
|
||||
}
|
||||
if let Err(e) = check_dkim() {
|
||||
err!(format!("DKIM config fails. {}",e))
|
||||
}
|
||||
}
|
||||
|
||||
if cfg._enable_email_2fa && !(cfg.smtp_host.is_some() || cfg.use_sendmail) {
|
||||
|
|
|
|||
42
src/mail.rs
42
src/mail.rs
|
|
@ -3,7 +3,10 @@ use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
|
|||
use std::{env::consts::EXE_SUFFIX, str::FromStr};
|
||||
|
||||
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::client::{Tls, TlsParameters},
|
||||
transport::smtp::extension::ClientId,
|
||||
|
|
@ -11,14 +14,10 @@ use lettre::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
api::EmptyResult,
|
||||
auth::{
|
||||
CONFIG, api::EmptyResult, auth::{
|
||||
encode_jwt, generate_delete_claims, generate_emergency_access_invite_claims, generate_invite_claims,
|
||||
generate_verify_email_claims,
|
||||
},
|
||||
db::models::{Device, DeviceType, EmergencyAccessId, MembershipId, OrganizationId, User, UserId},
|
||||
error::Error,
|
||||
CONFIG,
|
||||
}, db::models::{Device, DeviceType, EmergencyAccessId, MembershipId, OrganizationId, User, UserId}, error::Error, util::get_env
|
||||
};
|
||||
|
||||
fn sendmail_transport() -> AsyncSendmailTransport<Tokio1Executor> {
|
||||
|
|
@ -703,7 +702,28 @@ async fn send_with_selected_transport(email: Message) -> EmptyResult {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_dkim() -> Result<Option<DkimConfig>, String> {
|
||||
match (get_env::<String>("dkim_privatekey"), CONFIG.dkim_infos()) {
|
||||
(Some(pk), Some(infos)) => {
|
||||
let algo = if CONFIG.dkim_use_rsa() {DkimSigningAlgorithm::Rsa } else { DkimSigningAlgorithm::Ed25519 };
|
||||
let (selector, domain, privatekey) = match (DkimSigningKey::new(pk.as_str(), algo), infos.split(':').collect::<Vec<&str>>()) {
|
||||
(Ok(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());
|
||||
}
|
||||
};
|
||||
return Ok(Some(DkimConfig::default_config(selector, domain, privatekey)));
|
||||
},
|
||||
(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 {
|
||||
let smtp_from = Address::from_str(&CONFIG.smtp_from())?;
|
||||
|
||||
|
|
@ -726,12 +746,14 @@ async fn send_email(address: &str, subject: &str, body_html: String, body_text:
|
|||
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.domain())))
|
||||
.to(Mailbox::new(None, Address::from_str(address)?))
|
||||
.from(Mailbox::new(Some(CONFIG.smtp_from_name()), smtp_from))
|
||||
.subject(subject)
|
||||
.multipart(body)?;
|
||||
|
||||
if let Ok(Some(sig)) = check_dkim() {
|
||||
dkim_sign(&mut email, &sig);
|
||||
}
|
||||
send_with_selected_transport(email).await
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue