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",
|
"log",
|
||||||
"log4rs",
|
"log4rs",
|
||||||
"rand 0.10.2",
|
"rand 0.10.2",
|
||||||
|
"reqwest",
|
||||||
"sequoia-cert-store",
|
"sequoia-cert-store",
|
||||||
"sequoia-directories",
|
"sequoia-directories",
|
||||||
"sequoia-keystore",
|
"sequoia-keystore",
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ lettre = "0.11.22"
|
|||||||
log = "0.4.30"
|
log = "0.4.30"
|
||||||
log4rs = "1.3.0"
|
log4rs = "1.3.0"
|
||||||
rand = "0.10.2"
|
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-cert-store = "0.7.3"
|
||||||
sequoia-directories = "0.1.0"
|
sequoia-directories = "0.1.0"
|
||||||
sequoia-keystore = "0.7.2"
|
sequoia-keystore = "0.7.2"
|
||||||
|
|||||||
+45
-2
@@ -1,17 +1,60 @@
|
|||||||
#
|
#
|
||||||
# Husk config file
|
# Husk config file
|
||||||
#
|
#
|
||||||
connection = "localhost:3000"
|
|
||||||
|
|
||||||
|
# The directory for anything Sequoia PGP related (keystore, certstore).
|
||||||
|
#
|
||||||
|
# default: /var/lib/husk
|
||||||
sequoia_home = "/tmp/sq_home"
|
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" ]
|
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]
|
[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_min = 1
|
||||||
wait_max = 2
|
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]
|
[logging.appenders.console]
|
||||||
kind = "console"
|
kind = "console"
|
||||||
[logging.appenders.console.encoder]
|
[logging.appenders.console.encoder]
|
||||||
|
|||||||
+30
-6
@@ -30,6 +30,7 @@ use sequoia_wot::{self as wot, CertSynopsis};
|
|||||||
use wot::store::Store as _;
|
use wot::store::Store as _;
|
||||||
use wot::{Depth, Path};
|
use wot::{Depth, Path};
|
||||||
|
|
||||||
|
use crate::config::TorConnectorType;
|
||||||
use crate::types::errors::HuskError;
|
use crate::types::errors::HuskError;
|
||||||
use crate::types::husk_context::HuskContext;
|
use crate::types::husk_context::HuskContext;
|
||||||
use crate::types::introducer::Introducer;
|
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);
|
log::debug!("remote lookup for {:?}", query);
|
||||||
|
|
||||||
let mut jobs = JoinSet::new();
|
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)
|
.user_agent(USER_AGENT)
|
||||||
.connect_timeout(CONNECT_TIMEOUT)
|
.connect_timeout(CONNECT_TIMEOUT)
|
||||||
.timeout(REQUEST_TIMEOUT)
|
.timeout(REQUEST_TIMEOUT);
|
||||||
.build()?;
|
|
||||||
|
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
|
// local function to check certificates for usability
|
||||||
let collect_certs = |certs: Vec<Result<Cert, anyhow::Error>>, query: OnlineQuery, policy: &StandardPolicy| {
|
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(_) => {
|
OnlineQuery::Fingerprint(f) => {
|
||||||
found = true;
|
found = cert.fingerprint() == f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,9 +206,19 @@ async fn query_online_sources<'hc>(context: &HuskContext<'hc>, query: OnlineQuer
|
|||||||
let q = query.clone();
|
let q = query.clone();
|
||||||
match query {
|
match query {
|
||||||
OnlineQuery::Email(e) => {
|
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 {
|
if let Ok(certs) = ks.search(e.as_str()).await {
|
||||||
partial_result = collect_certs(certs, q, &p);
|
partial_result = collect_certs(certs, q, &p);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
partial_result
|
partial_result
|
||||||
},
|
},
|
||||||
OnlineQuery::Fingerprint(f) => {
|
OnlineQuery::Fingerprint(f) => {
|
||||||
@@ -934,7 +957,8 @@ jk6lh01Ej9QTGQ==
|
|||||||
introducers: Vec::new(),
|
introducers: Vec::new(),
|
||||||
keyservers: Vec::new(),
|
keyservers: Vec::new(),
|
||||||
subject_replacement: "...".into(),
|
subject_replacement: "...".into(),
|
||||||
mail: MailContext::new()
|
mail: MailContext::new(),
|
||||||
|
use_tor_via: crate::config::TorConnectorType::NotUsed,
|
||||||
};
|
};
|
||||||
|
|
||||||
// store the local trust root
|
// store the local trust root
|
||||||
|
|||||||
+79
-20
@@ -9,6 +9,7 @@ use toml;
|
|||||||
use log4rs;
|
use log4rs;
|
||||||
|
|
||||||
use crate::types::errors::HuskError;
|
use crate::types::errors::HuskError;
|
||||||
|
use crate::types::defaults::CONNECTION;
|
||||||
use crate::types::defaults::CONSOLE_LOGGING;
|
use crate::types::defaults::CONSOLE_LOGGING;
|
||||||
use crate::types::defaults::UPDATER_PID_FILE_LOCATION;
|
use crate::types::defaults::UPDATER_PID_FILE_LOCATION;
|
||||||
use crate::types::defaults::UPDATER_STATE_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::PID_FILE_LOCATION;
|
||||||
use crate::types::defaults::SUBJECT_REPLACEMENT;
|
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)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct UpdaterConfig {
|
pub struct UpdaterConfig {
|
||||||
pub wait_min: Option<u32>,
|
pub wait_min: Option<u32>,
|
||||||
pub wait_max: Option<u32>,
|
pub wait_max: Option<u32>,
|
||||||
pub pid_file: Option<String>,
|
pub pid_file: Option<String>,
|
||||||
pub state_file: Option<String>,
|
pub state_file: Option<String>,
|
||||||
|
use_tor_via: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct HuskConfig {
|
pub struct HuskConfig {
|
||||||
pub connection: String,
|
|
||||||
pub sequoia_home: String,
|
pub sequoia_home: String,
|
||||||
pub keyservers: Option<Vec<String>>,
|
milter: Option<MilterConfig>,
|
||||||
pub subject_replacement: Option<String>,
|
|
||||||
pub pid_file: Option<String>,
|
|
||||||
updater: Option<UpdaterConfig>,
|
updater: Option<UpdaterConfig>,
|
||||||
logging: Option<toml::Value>,
|
logging: Option<toml::Value>,
|
||||||
}
|
}
|
||||||
@@ -77,28 +112,47 @@ impl HuskConfig {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_keyservers(&self) -> Vec<String> {
|
pub fn get_connection(&self) -> String {
|
||||||
if let Some(kss) = &self.keyservers {
|
if let Some(milter_config) = &self.milter {
|
||||||
kss.clone()
|
milter_config.connection.clone()
|
||||||
} else {
|
} 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 {
|
pub fn get_subject_replacement(&self) -> String {
|
||||||
if let Some(replacement) = &self.subject_replacement {
|
if let Some(milter_config) = &self.milter {
|
||||||
replacement.clone()
|
if let Some(replacement) = &milter_config.subject_replacement {
|
||||||
} else {
|
return replacement.clone();
|
||||||
SUBJECT_REPLACEMENT.to_string()
|
}
|
||||||
}
|
}
|
||||||
|
SUBJECT_REPLACEMENT.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pid_file(&self) -> String {
|
pub fn get_pid_file(&self) -> String {
|
||||||
if let Some(pid_file) = &self.pid_file {
|
if let Some(milter_config) = &self.milter {
|
||||||
pid_file.clone()
|
if let Some(pid_file) = &milter_config.pid_file {
|
||||||
} else {
|
return pid_file.clone();
|
||||||
PID_FILE_LOCATION.to_string()
|
}
|
||||||
}
|
}
|
||||||
|
PID_FILE_LOCATION.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_updater_pid_file(&self) -> String {
|
pub fn get_updater_pid_file(&self) -> String {
|
||||||
@@ -147,6 +201,14 @@ impl HuskConfig {
|
|||||||
UPDATER_WAIT_MAX
|
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>>;
|
pub type HuskConfigContainer = Arc<Mutex<HuskConfig>>;
|
||||||
@@ -168,12 +230,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn subject_line() {
|
fn subject_line() {
|
||||||
let config = HuskConfig {
|
let config = HuskConfig {
|
||||||
connection: "".into(),
|
|
||||||
sequoia_home: "".into(),
|
sequoia_home: "".into(),
|
||||||
keyservers: None,
|
|
||||||
subject_replacement: None,
|
|
||||||
logging: None,
|
logging: None,
|
||||||
pid_file: None,
|
milter: None,
|
||||||
updater: None,
|
updater: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -33,7 +33,7 @@ impl Daemon {
|
|||||||
|
|
||||||
let config: HuskConfig = config_container.clone().into();
|
let config: HuskConfig = config_container.clone().into();
|
||||||
|
|
||||||
let listener = TcpListener::bind(&config.connection)
|
let listener = TcpListener::bind(&config.get_connection())
|
||||||
.await
|
.await
|
||||||
.expect(" opening milter socket");
|
.expect(" opening milter socket");
|
||||||
|
|
||||||
@@ -116,6 +116,7 @@ impl Daemon {
|
|||||||
// setup Context
|
// setup Context
|
||||||
match HuskContext::new(&config) {
|
match HuskContext::new(&config) {
|
||||||
Ok(mut context) => {
|
Ok(mut context) => {
|
||||||
|
context.for_milter(&config);
|
||||||
context.set_sender(sender.unwrap());
|
context.set_sender(sender.unwrap());
|
||||||
cx.data = Some(context);
|
cx.data = Some(context);
|
||||||
Status::Continue
|
Status::Continue
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
//! Defaults
|
//! Defaults
|
||||||
|
|
||||||
|
/// Default host and port of the milter.
|
||||||
|
pub const CONNECTION: &str = "localhost:3000";
|
||||||
|
|
||||||
pub const CONFIG_FILE_LOCATION: &str = "/etc/husk/config.toml";
|
pub const CONFIG_FILE_LOCATION: &str = "/etc/husk/config.toml";
|
||||||
|
|
||||||
/// If encrypting a mail, the subject is moved into the encrypted body
|
/// 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_directories::Home;
|
||||||
use sequoia_keystore::Key;
|
use sequoia_keystore::Key;
|
||||||
|
|
||||||
use crate::config::HuskConfig;
|
use crate::config::{HuskConfig, TorConnectorType};
|
||||||
use crate::common::crypto;
|
use crate::common::crypto;
|
||||||
use crate::types::mail_context::MailContext;
|
use crate::types::mail_context::MailContext;
|
||||||
|
|
||||||
@@ -32,6 +32,7 @@ pub struct HuskContext<'hc> {
|
|||||||
pub keyservers: Vec<String>,
|
pub keyservers: Vec<String>,
|
||||||
pub subject_replacement: String,
|
pub subject_replacement: String,
|
||||||
pub mail: MailContext<'hc>,
|
pub mail: MailContext<'hc>,
|
||||||
|
pub use_tor_via: TorConnectorType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'hc> HuskContext<'hc> {
|
impl<'hc> HuskContext<'hc> {
|
||||||
@@ -82,6 +83,7 @@ impl<'hc> HuskContext<'hc> {
|
|||||||
keyservers: config.get_keyservers(),
|
keyservers: config.get_keyservers(),
|
||||||
subject_replacement: config.get_subject_replacement(),
|
subject_replacement: config.get_subject_replacement(),
|
||||||
mail: MailContext::new(),
|
mail: MailContext::new(),
|
||||||
|
use_tor_via: TorConnectorType::NotUsed,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,4 +111,12 @@ impl<'hc> HuskContext<'hc> {
|
|||||||
self.mail.add_recipient(rcpt, certs)
|
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)
|
let mut updater_state = UpdaterState::thaw(state_path)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| { stay = false; e })?;
|
.map_err(|e| { stay = false; e })?;
|
||||||
let context = &HuskContext::without_signkey(&c)?;
|
let mut context = HuskContext::without_signkey(&c)?;
|
||||||
updater_state.refresh(context);
|
context.for_updater(&c);
|
||||||
|
updater_state.refresh(&context);
|
||||||
|
|
||||||
if let Some(fpr) = updater_state.next() {
|
if let Some(fpr) = updater_state.next() {
|
||||||
log::debug!("try to update {}", fpr.to_hex());
|
log::debug!("try to update {}", fpr.to_hex());
|
||||||
|
|
||||||
let stats = MergePublicCollectStats::new();
|
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() {
|
if !certs.is_empty() {
|
||||||
for cert in certs {
|
for cert in certs {
|
||||||
let cert = Arc::new(LazyCert::from_cert(cert));
|
let cert = Arc::new(LazyCert::from_cert(cert));
|
||||||
|
|||||||
Reference in New Issue
Block a user