Tor support
- Add support for using Tor via a socks5 proxy. - Tor usage of the milter and the updater process are configured separately. - Restructure the config file and add some documentation.
This commit is contained in:
Generated
+1
@@ -1292,6 +1292,7 @@ dependencies = [
|
||||
"log",
|
||||
"log4rs",
|
||||
"rand 0.10.2",
|
||||
"reqwest",
|
||||
"sequoia-cert-store",
|
||||
"sequoia-directories",
|
||||
"sequoia-keystore",
|
||||
|
||||
@@ -22,6 +22,8 @@ lettre = "0.11.22"
|
||||
log = "0.4.30"
|
||||
log4rs = "1.3.0"
|
||||
rand = "0.10.2"
|
||||
# the reqwest version has to be the same sequoia-net reexports!
|
||||
reqwest = { version = "0.12", features = ["socks"] }
|
||||
sequoia-cert-store = "0.7.3"
|
||||
sequoia-directories = "0.1.0"
|
||||
sequoia-keystore = "0.7.2"
|
||||
|
||||
+45
-2
@@ -1,17 +1,60 @@
|
||||
#
|
||||
# Husk config file
|
||||
#
|
||||
connection = "localhost:3000"
|
||||
|
||||
# The directory for anything Sequoia PGP related (keystore, certstore).
|
||||
#
|
||||
# default: /var/lib/husk
|
||||
sequoia_home = "/tmp/sq_home"
|
||||
|
||||
#keyservers = [ "hkps://keys.openpgp.org/", "hkps://mail-api.proton.me", "hkps://keys.mailvelope.com", "hkps://keyserver.ubuntu.com", "hkps://sks.pod01.fleetstreetops.com" ]
|
||||
[milter]
|
||||
# IP and port the milter will listen on.
|
||||
#
|
||||
# no default, must be specified.
|
||||
connection = "localhost:3000"
|
||||
|
||||
# Keyservers to use for key lookup.
|
||||
#
|
||||
# Example:
|
||||
# keyservers = [ "hkps://keys.openpgp.org/", "hkps://mail-api.proton.me", "hkps://keys.mailvelope.com", "hkps://keyserver.ubuntu.com", "hkps://sks.pod01.fleetstreetops.com" ]
|
||||
#
|
||||
# The default is the empty list, which prevents keyserver lookups.
|
||||
keyservers = [ "hkp://example.com" ]
|
||||
|
||||
# Location of the pid file of the milter.
|
||||
#
|
||||
# default: /var/lib/husk/husk.pid
|
||||
|
||||
# Subject header lines are protected (exchanged).
|
||||
#
|
||||
# Specify your replacement string:
|
||||
# subject_replacement = "Encrypted message"
|
||||
#
|
||||
# default: "..."
|
||||
|
||||
# Tor usage: If configured, the milter will use Tor via the specified
|
||||
# sock5 proxy.
|
||||
#
|
||||
# use_tor_via = "socks5://localhost:9050/"
|
||||
#
|
||||
# default: unconfigured, meaning: no usage of Tor.
|
||||
|
||||
[updater]
|
||||
|
||||
# The interval between two update requests for a certificate are
|
||||
# random.
|
||||
#
|
||||
# wait_min and wait_max specify the range for that randomness.
|
||||
# defaults:
|
||||
# min_wait = 5
|
||||
# max_wait = 3600
|
||||
wait_min = 1
|
||||
wait_max = 2
|
||||
|
||||
# Tor usage of the updater process. Semantics (and default) are the same
|
||||
# as with the milter.
|
||||
use_tor_via = "socks5://localhost:9050/"
|
||||
|
||||
[logging.appenders.console]
|
||||
kind = "console"
|
||||
[logging.appenders.console.encoder]
|
||||
|
||||
+30
-6
@@ -30,6 +30,7 @@ use sequoia_wot::{self as wot, CertSynopsis};
|
||||
use wot::store::Store as _;
|
||||
use wot::{Depth, Path};
|
||||
|
||||
use crate::config::TorConnectorType;
|
||||
use crate::types::errors::HuskError;
|
||||
use crate::types::husk_context::HuskContext;
|
||||
use crate::types::introducer::Introducer;
|
||||
@@ -125,11 +126,23 @@ async fn query_online_sources<'hc>(context: &HuskContext<'hc>, query: OnlineQuer
|
||||
log::debug!("remote lookup for {:?}", query);
|
||||
|
||||
let mut jobs = JoinSet::new();
|
||||
let http_client = sequoia_net::reqwest::Client::builder()
|
||||
let mut http_client_builder = sequoia_net::reqwest::Client::builder()
|
||||
.user_agent(USER_AGENT)
|
||||
.connect_timeout(CONNECT_TIMEOUT)
|
||||
.timeout(REQUEST_TIMEOUT)
|
||||
.build()?;
|
||||
.timeout(REQUEST_TIMEOUT);
|
||||
|
||||
if let TorConnectorType::SocksProxy(socks_url) = &context.use_tor_via {
|
||||
// Configure the use of a tor proxy.
|
||||
let mut nonce: [u8; 4] = [0; 4];
|
||||
sequoia_openpgp::crypto::random(&mut nonce[..])?;
|
||||
let nonce: String = sequoia_openpgp::fmt::hex::encode(&nonce);
|
||||
let url: String = format!("socks5h://anonymous:{}@{}", nonce, socks_url);
|
||||
|
||||
http_client_builder = http_client_builder
|
||||
.proxy(sequoia_net::reqwest::Proxy::all(url)?);
|
||||
}
|
||||
|
||||
let http_client = http_client_builder.build()?;
|
||||
|
||||
// local function to check certificates for usability
|
||||
let collect_certs = |certs: Vec<Result<Cert, anyhow::Error>>, query: OnlineQuery, policy: &StandardPolicy| {
|
||||
@@ -152,8 +165,8 @@ async fn query_online_sources<'hc>(context: &HuskContext<'hc>, query: OnlineQuer
|
||||
}
|
||||
}
|
||||
},
|
||||
OnlineQuery::Fingerprint(_) => {
|
||||
found = true;
|
||||
OnlineQuery::Fingerprint(f) => {
|
||||
found = cert.fingerprint() == f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,9 +206,19 @@ async fn query_online_sources<'hc>(context: &HuskContext<'hc>, query: OnlineQuer
|
||||
let q = query.clone();
|
||||
match query {
|
||||
OnlineQuery::Email(e) => {
|
||||
match ks.search(e.as_str()).await {
|
||||
Ok(certs) => {
|
||||
partial_result = collect_certs(certs, q, &p);
|
||||
}
|
||||
Err(e) => {
|
||||
log::debug!("Error: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
/*
|
||||
if let Ok(certs) = ks.search(e.as_str()).await {
|
||||
partial_result = collect_certs(certs, q, &p);
|
||||
}
|
||||
*/
|
||||
partial_result
|
||||
},
|
||||
OnlineQuery::Fingerprint(f) => {
|
||||
@@ -934,7 +957,8 @@ jk6lh01Ej9QTGQ==
|
||||
introducers: Vec::new(),
|
||||
keyservers: Vec::new(),
|
||||
subject_replacement: "...".into(),
|
||||
mail: MailContext::new()
|
||||
mail: MailContext::new(),
|
||||
use_tor_via: crate::config::TorConnectorType::NotUsed,
|
||||
};
|
||||
|
||||
// store the local trust root
|
||||
|
||||
+79
-20
@@ -9,6 +9,7 @@ use toml;
|
||||
use log4rs;
|
||||
|
||||
use crate::types::errors::HuskError;
|
||||
use crate::types::defaults::CONNECTION;
|
||||
use crate::types::defaults::CONSOLE_LOGGING;
|
||||
use crate::types::defaults::UPDATER_PID_FILE_LOCATION;
|
||||
use crate::types::defaults::UPDATER_STATE_FILE_LOCATION;
|
||||
@@ -17,21 +18,55 @@ use crate::types::defaults::UPDATER_WAIT_MAX;
|
||||
use crate::types::defaults::PID_FILE_LOCATION;
|
||||
use crate::types::defaults::SUBJECT_REPLACEMENT;
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub enum TorConnectorType {
|
||||
NotUsed,
|
||||
Errornous,
|
||||
SocksProxy(String),
|
||||
Arti,
|
||||
}
|
||||
|
||||
impl From<Option<String>> for TorConnectorType {
|
||||
fn from(value: Option<String>) -> Self {
|
||||
match value {
|
||||
Some(v) => {
|
||||
if v.to_ascii_lowercase() == "arti" {
|
||||
TorConnectorType::Arti
|
||||
} else {
|
||||
if v.starts_with("socks5://") {
|
||||
TorConnectorType::SocksProxy(v[9..].to_string())
|
||||
} else {
|
||||
TorConnectorType::Errornous
|
||||
}
|
||||
}
|
||||
},
|
||||
None => TorConnectorType::NotUsed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct MilterConfig {
|
||||
pub connection: String,
|
||||
pub pid_file: Option<String>,
|
||||
pub keyservers: Option<Vec<String>>,
|
||||
pub subject_replacement: Option<String>,
|
||||
use_tor_via: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct UpdaterConfig {
|
||||
pub wait_min: Option<u32>,
|
||||
pub wait_max: Option<u32>,
|
||||
pub pid_file: Option<String>,
|
||||
pub state_file: Option<String>,
|
||||
use_tor_via: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct HuskConfig {
|
||||
pub connection: String,
|
||||
pub sequoia_home: String,
|
||||
pub keyservers: Option<Vec<String>>,
|
||||
pub subject_replacement: Option<String>,
|
||||
pub pid_file: Option<String>,
|
||||
milter: Option<MilterConfig>,
|
||||
updater: Option<UpdaterConfig>,
|
||||
logging: Option<toml::Value>,
|
||||
}
|
||||
@@ -77,28 +112,47 @@ impl HuskConfig {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_keyservers(&self) -> Vec<String> {
|
||||
if let Some(kss) = &self.keyservers {
|
||||
kss.clone()
|
||||
pub fn get_connection(&self) -> String {
|
||||
if let Some(milter_config) = &self.milter {
|
||||
milter_config.connection.clone()
|
||||
} else {
|
||||
Vec::new()
|
||||
CONNECTION.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_milter_tor_usage(&self) -> TorConnectorType {
|
||||
if let Some(milter_config) = &self.milter {
|
||||
milter_config.use_tor_via.clone().into()
|
||||
} else {
|
||||
TorConnectorType::NotUsed
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_keyservers(&self) -> Vec<String> {
|
||||
if let Some(milter_config) = &self.milter {
|
||||
if let Some(kss) = &milter_config.keyservers {
|
||||
return kss.clone();
|
||||
}
|
||||
}
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
pub fn get_subject_replacement(&self) -> String {
|
||||
if let Some(replacement) = &self.subject_replacement {
|
||||
replacement.clone()
|
||||
} else {
|
||||
SUBJECT_REPLACEMENT.to_string()
|
||||
if let Some(milter_config) = &self.milter {
|
||||
if let Some(replacement) = &milter_config.subject_replacement {
|
||||
return replacement.clone();
|
||||
}
|
||||
}
|
||||
SUBJECT_REPLACEMENT.to_string()
|
||||
}
|
||||
|
||||
pub fn get_pid_file(&self) -> String {
|
||||
if let Some(pid_file) = &self.pid_file {
|
||||
pid_file.clone()
|
||||
} else {
|
||||
PID_FILE_LOCATION.to_string()
|
||||
if let Some(milter_config) = &self.milter {
|
||||
if let Some(pid_file) = &milter_config.pid_file {
|
||||
return pid_file.clone();
|
||||
}
|
||||
}
|
||||
PID_FILE_LOCATION.to_string()
|
||||
}
|
||||
|
||||
pub fn get_updater_pid_file(&self) -> String {
|
||||
@@ -147,6 +201,14 @@ impl HuskConfig {
|
||||
UPDATER_WAIT_MAX
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_updater_tor_usage(&self) -> TorConnectorType {
|
||||
if let Some(updater_config) = &self.updater {
|
||||
updater_config.use_tor_via.clone().into()
|
||||
} else {
|
||||
TorConnectorType::NotUsed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type HuskConfigContainer = Arc<Mutex<HuskConfig>>;
|
||||
@@ -168,12 +230,9 @@ mod tests {
|
||||
#[test]
|
||||
fn subject_line() {
|
||||
let config = HuskConfig {
|
||||
connection: "".into(),
|
||||
sequoia_home: "".into(),
|
||||
keyservers: None,
|
||||
subject_replacement: None,
|
||||
logging: None,
|
||||
pid_file: None,
|
||||
milter: None,
|
||||
updater: None,
|
||||
};
|
||||
|
||||
|
||||
+2
-1
@@ -33,7 +33,7 @@ impl Daemon {
|
||||
|
||||
let config: HuskConfig = config_container.clone().into();
|
||||
|
||||
let listener = TcpListener::bind(&config.connection)
|
||||
let listener = TcpListener::bind(&config.get_connection())
|
||||
.await
|
||||
.expect(" opening milter socket");
|
||||
|
||||
@@ -116,6 +116,7 @@ impl Daemon {
|
||||
// setup Context
|
||||
match HuskContext::new(&config) {
|
||||
Ok(mut context) => {
|
||||
context.for_milter(&config);
|
||||
context.set_sender(sender.unwrap());
|
||||
cx.data = Some(context);
|
||||
Status::Continue
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
//! Defaults
|
||||
|
||||
/// Default host and port of the milter.
|
||||
pub const CONNECTION: &str = "localhost:3000";
|
||||
|
||||
pub const CONFIG_FILE_LOCATION: &str = "/etc/husk/config.toml";
|
||||
|
||||
/// If encrypting a mail, the subject is moved into the encrypted body
|
||||
|
||||
@@ -17,7 +17,7 @@ use sequoia_cert_store::CertStore;
|
||||
use sequoia_directories::Home;
|
||||
use sequoia_keystore::Key;
|
||||
|
||||
use crate::config::HuskConfig;
|
||||
use crate::config::{HuskConfig, TorConnectorType};
|
||||
use crate::common::crypto;
|
||||
use crate::types::mail_context::MailContext;
|
||||
|
||||
@@ -32,6 +32,7 @@ pub struct HuskContext<'hc> {
|
||||
pub keyservers: Vec<String>,
|
||||
pub subject_replacement: String,
|
||||
pub mail: MailContext<'hc>,
|
||||
pub use_tor_via: TorConnectorType,
|
||||
}
|
||||
|
||||
impl<'hc> HuskContext<'hc> {
|
||||
@@ -82,6 +83,7 @@ impl<'hc> HuskContext<'hc> {
|
||||
keyservers: config.get_keyservers(),
|
||||
subject_replacement: config.get_subject_replacement(),
|
||||
mail: MailContext::new(),
|
||||
use_tor_via: TorConnectorType::NotUsed,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -109,4 +111,12 @@ impl<'hc> HuskContext<'hc> {
|
||||
self.mail.add_recipient(rcpt, certs)
|
||||
}
|
||||
|
||||
pub fn for_milter(&mut self, config: &HuskConfig) {
|
||||
self.use_tor_via = config.get_milter_tor_usage();
|
||||
}
|
||||
|
||||
pub fn for_updater(&mut self, config: &HuskConfig) {
|
||||
self.use_tor_via = config.get_updater_tor_usage();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-3
@@ -41,15 +41,16 @@ impl Updater {
|
||||
let mut updater_state = UpdaterState::thaw(state_path)
|
||||
.await
|
||||
.map_err(|e| { stay = false; e })?;
|
||||
let context = &HuskContext::without_signkey(&c)?;
|
||||
updater_state.refresh(context);
|
||||
let mut context = HuskContext::without_signkey(&c)?;
|
||||
context.for_updater(&c);
|
||||
updater_state.refresh(&context);
|
||||
|
||||
if let Some(fpr) = updater_state.next() {
|
||||
log::debug!("try to update {}", fpr.to_hex());
|
||||
|
||||
let stats = MergePublicCollectStats::new();
|
||||
|
||||
let certs = crypto::lookup_fingerprint(context, fpr.to_hex().as_str()).await?;
|
||||
let certs = crypto::lookup_fingerprint(&context, fpr.to_hex().as_str()).await?;
|
||||
if !certs.is_empty() {
|
||||
for cert in certs {
|
||||
let cert = Arc::new(LazyCert::from_cert(cert));
|
||||
|
||||
Reference in New Issue
Block a user