From 8f1d5f7010c21e4572eccd2de4a8131fd5ae3bbe Mon Sep 17 00:00:00 2001 From: Subhra264 Date: Wed, 28 Feb 2024 17:15:48 +0530 Subject: [PATCH] feat: introduce SMTP env configurations (#2810) Addresses #2601. `lettre` crate requires Rust `1.70+`. `ZO_SMTP_ENCRYPTION` env variable offers two important encryption method - `starttls` and `ssltls`. --- CONTRIBUTING.md | 2 +- Cargo.lock | 149 +++++++++++++++++++++++++++++++++++---- Cargo.toml | 9 +++ src/config/Cargo.toml | 1 + src/config/src/config.rs | 56 +++++++++++++++ 5 files changed, 204 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0dbddc1b1..0a06ae030 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ OpenObserve uses Rust & embdeds sled db (For server) & VueJS (For Web UI) You must have follwing installed: 1. Git -2. Rust & Cargo 1.61.0 + (We recommend 1.66+), +2. Rust & Cargo 1.70+ 3. nodejs v14+ and npm v6+ Alternatively you can use pre-configured devcontainer in VS code to get up and running quickly. diff --git a/Cargo.lock b/Cargo.lock index f88d80c9e..2f3c53357 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1181,7 +1181,7 @@ dependencies = [ "hyper-rustls", "lazy_static", "pin-project-lite", - "rustls", + "rustls 0.21.10", "tokio", "tower", "tracing", @@ -1823,6 +1823,16 @@ dependencies = [ "phf_codegen", ] +[[package]] +name = "chumsky" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9" +dependencies = [ + "hashbrown 0.14.3", + "stacker", +] + [[package]] name = "ciborium" version = "0.2.2" @@ -2072,6 +2082,7 @@ dependencies = [ "hex", "indexmap 2.1.0", "itertools 0.12.1", + "lettre", "log", "memchr", "murmur3", @@ -2783,6 +2794,22 @@ dependencies = [ "serde", ] +[[package]] +name = "email-encoding" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbfb21b9878cf7a348dcb8559109aabc0ec40d69924bd706fa5149846c4fef75" +dependencies = [ + "base64 0.21.7", + "memchr", +] + +[[package]] +name = "email_address" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2153bd83ebc09db15bcbdc3e2194d901804952e3dc96967e1cd3b0c5c32d112" + [[package]] name = "ena" version = "0.14.2" @@ -3532,10 +3559,10 @@ dependencies = [ "http 0.2.11", "hyper", "log", - "rustls", + "rustls 0.21.10", "rustls-native-certs", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", ] [[package]] @@ -3894,6 +3921,36 @@ dependencies = [ "spin 0.5.2", ] +[[package]] +name = "lettre" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357ff5edb6d8326473a64c82cf41ddf78ab116f89668c50c4fac1b321e5e80f4" +dependencies = [ + "async-trait", + "base64 0.21.7", + "chumsky", + "email-encoding", + "email_address", + "fastrand 2.0.1", + "futures-io", + "futures-util", + "hostname", + "httpdate", + "idna", + "mime", + "nom", + "percent-encoding", + "quoted_printable", + "rustls 0.22.2", + "rustls-pemfile 2.0.0", + "socket2", + "tokio", + "tokio-rustls 0.25.0", + "url", + "webpki-roots 0.26.1", +] + [[package]] name = "lexical-core" version = "0.8.5" @@ -4671,6 +4728,7 @@ dependencies = [ "ipnetwork 0.20.0", "itertools 0.12.1", "jsonwebtoken", + "lettre", "log", "maxminddb", "memory-stats", @@ -5483,6 +5541,15 @@ version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + [[package]] name = "ptr_meta" version = "0.1.4" @@ -5756,7 +5823,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls", + "rustls 0.21.10", "rustls-native-certs", "rustls-pemfile 1.0.4", "serde", @@ -5765,7 +5832,7 @@ dependencies = [ "sync_wrapper", "system-configuration", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tokio-util", "tower-service", "url", @@ -5773,7 +5840,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots", + "webpki-roots 0.25.4", "winreg", ] @@ -6013,10 +6080,24 @@ checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", "ring 0.17.7", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +dependencies = [ + "log", + "ring 0.17.7", + "rustls-pki-types", + "rustls-webpki 0.102.2", + "subtle", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.6.3" @@ -6064,6 +6145,17 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "rustls-webpki" +version = "0.102.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +dependencies = [ + "ring 0.17.7", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -6590,7 +6682,7 @@ dependencies = [ "once_cell", "paste", "percent-encoding", - "rustls", + "rustls 0.21.10", "rustls-pemfile 1.0.4", "serde", "serde_json", @@ -6602,7 +6694,7 @@ dependencies = [ "tokio-stream", "tracing", "url", - "webpki-roots", + "webpki-roots 0.25.4", ] [[package]] @@ -6758,6 +6850,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "stacker" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "libc", + "psm", + "winapi 0.3.9", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -7191,7 +7296,18 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.10", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.2", + "rustls-pki-types", "tokio", ] @@ -7296,10 +7412,10 @@ dependencies = [ "percent-encoding", "pin-project", "prost 0.12.3", - "rustls", + "rustls 0.21.10", "rustls-pemfile 1.0.4", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tokio-stream", "tower", "tower-layer", @@ -7999,6 +8115,15 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "webpki-roots" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "which" version = "4.4.2" diff --git a/Cargo.toml b/Cargo.toml index 356415c9e..6ee339a83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -152,6 +152,7 @@ zstd.workspace = true config.workspace = true infra.workspace = true ingester.workspace = true +lettre.workspace = true [build-dependencies] chrono = { version = "0.4", default-features = false, features = ["clock"] } @@ -217,6 +218,14 @@ hex = "0.4" indexmap = { version = "2.0", features = ["serde"] } ipnetwork = "0.20" itertools = "0.12" +lettre = { version = "0.11", default-features = false, features = [ + "builder", + "hostname", + "smtp-transport", + "pool", + "tokio1", + "tokio1-rustls-tls", +] } log = "0.4" memchr = "2.5" murmur3 = "0.5" diff --git a/src/config/Cargo.toml b/src/config/Cargo.toml index 043c5386a..13a3ba0a6 100644 --- a/src/config/Cargo.toml +++ b/src/config/Cargo.toml @@ -47,3 +47,4 @@ tracing-log.workspace = true tracing-subscriber.workspace = true utoipa.workspace = true walkdir.workspace = true +lettre.workspace = true \ No newline at end of file diff --git a/src/config/src/config.rs b/src/config/src/config.rs index ac28f2c97..6a02e0518 100644 --- a/src/config/src/config.rs +++ b/src/config/src/config.rs @@ -19,6 +19,13 @@ use dotenv_config::EnvConfig; use dotenvy::dotenv; use hashbrown::{HashMap, HashSet}; use itertools::chain; +use lettre::{ + transport::smtp::{ + authentication::Credentials, + client::{Tls, TlsParameters}, + }, + AsyncSmtpTransport, Tokio1Executor, +}; use once_cell::sync::Lazy; use reqwest::Client; use sysinfo::{DiskExt, SystemExt}; @@ -139,6 +146,34 @@ pub static TELEMETRY_CLIENT: Lazy = Lazy::new(|| { ) }); +pub static SMTP_CLIENT: Lazy>> = Lazy::new(|| { + if !CONFIG.smtp.smtp_enabled { + None + } else { + let tls_parameters = TlsParameters::new(CONFIG.smtp.smtp_host.clone()).unwrap(); + let mut transport_builder = + AsyncSmtpTransport::::builder_dangerous(&CONFIG.smtp.smtp_host) + .port(CONFIG.smtp.smtp_port); + + let option = &CONFIG.smtp.smtp_encryption; + transport_builder = if option == "starttls" { + transport_builder.tls(Tls::Required(tls_parameters)) + } else if option == "ssltls" { + transport_builder.tls(Tls::Wrapper(tls_parameters)) + } else { + transport_builder + }; + + if !CONFIG.smtp.smtp_username.is_empty() && !CONFIG.smtp.smtp_password.is_empty() { + transport_builder = transport_builder.credentials(Credentials::new( + CONFIG.smtp.smtp_username.clone(), + CONFIG.smtp.smtp_password.clone(), + )); + } + Some(transport_builder.build()) + } +}); + #[derive(EnvConfig)] pub struct Config { pub auth: Auth, @@ -158,6 +193,27 @@ pub struct Config { pub tcp: TCP, pub prom: Prometheus, pub profiling: Pyroscope, + pub smtp: Smtp, +} + +#[derive(EnvConfig)] +pub struct Smtp { + #[env_config(name = "ZO_SMTP_ENABLED", default = false)] + pub smtp_enabled: bool, + #[env_config(name = "ZO_SMTP_HOST", default = "localhost")] + pub smtp_host: String, + #[env_config(name = "ZO_SMTP_PORT", default = 25)] + pub smtp_port: u16, + #[env_config(name = "ZO_SMTP_USER_NAME", default = "")] + pub smtp_username: String, + #[env_config(name = "ZO_SMTP_PASSWORD", default = "")] + pub smtp_password: String, + #[env_config(name = "ZO_SMTP_REPLY_TO", default = "")] + pub smtp_reply_to: String, + #[env_config(name = "ZO_SMTP_FROM_EMAIL", default = "")] + pub smtp_from_email: String, + #[env_config(name = "ZO_SMTP_ENCRYPTION", default = "")] + pub smtp_encryption: String, } #[derive(EnvConfig)]