mirror of
https://gitlab.com/veilid/veilid.git
synced 2025-03-12 00:46:29 -04:00
Merge branch 'geolocation-3' into 'main'
Country code denylist for route creation See merge request veilid/veilid!337
This commit is contained in:
commit
95d61855a8
@ -175,9 +175,7 @@ impl RouteSpecStore {
|
||||
pub fn allocate_route(
|
||||
&self,
|
||||
crypto_kinds: &[CryptoKind],
|
||||
stability: Stability,
|
||||
sequencing: Sequencing,
|
||||
hop_count: usize,
|
||||
safety_spec: &SafetySpec,
|
||||
directions: DirectionSet,
|
||||
avoid_nodes: &[TypedKey],
|
||||
automatic: bool,
|
||||
@ -190,9 +188,7 @@ impl RouteSpecStore {
|
||||
inner,
|
||||
rti,
|
||||
crypto_kinds,
|
||||
stability,
|
||||
sequencing,
|
||||
hop_count,
|
||||
safety_spec,
|
||||
directions,
|
||||
avoid_nodes,
|
||||
automatic,
|
||||
@ -206,15 +202,17 @@ impl RouteSpecStore {
|
||||
inner: &mut RouteSpecStoreInner,
|
||||
rti: &mut RoutingTableInner,
|
||||
crypto_kinds: &[CryptoKind],
|
||||
stability: Stability,
|
||||
sequencing: Sequencing,
|
||||
hop_count: usize,
|
||||
safety_spec: &SafetySpec,
|
||||
directions: DirectionSet,
|
||||
avoid_nodes: &[TypedKey],
|
||||
automatic: bool,
|
||||
) -> VeilidAPIResult<RouteId> {
|
||||
use core::cmp::Ordering;
|
||||
|
||||
if safety_spec.preferred_route.is_some() {
|
||||
apibail_generic!("safety_spec.preferred_route must be empty when allocating new route");
|
||||
}
|
||||
|
||||
let ip6_prefix_size = rti
|
||||
.unlocked_inner
|
||||
.config
|
||||
@ -222,19 +220,19 @@ impl RouteSpecStore {
|
||||
.network
|
||||
.max_connections_per_ip6_prefix_size as usize;
|
||||
|
||||
if hop_count < 1 {
|
||||
if safety_spec.hop_count < 1 {
|
||||
apibail_invalid_argument!(
|
||||
"Not allocating route less than one hop in length",
|
||||
"hop_count",
|
||||
hop_count
|
||||
safety_spec.hop_count
|
||||
);
|
||||
}
|
||||
|
||||
if hop_count > self.unlocked_inner.max_route_hop_count {
|
||||
if safety_spec.hop_count > self.unlocked_inner.max_route_hop_count {
|
||||
apibail_invalid_argument!(
|
||||
"Not allocating route longer than max route hop count",
|
||||
"hop_count",
|
||||
hop_count
|
||||
safety_spec.hop_count
|
||||
);
|
||||
}
|
||||
|
||||
@ -291,6 +289,83 @@ impl RouteSpecStore {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Exclude nodes from blacklisted countries
|
||||
#[cfg(feature = "geolocation")]
|
||||
{
|
||||
let country_code_denylist = self
|
||||
.unlocked_inner
|
||||
.routing_table
|
||||
.config
|
||||
.get()
|
||||
.network
|
||||
.privacy
|
||||
.country_code_denylist
|
||||
.clone();
|
||||
|
||||
if !country_code_denylist.is_empty() {
|
||||
let geolocation_info =
|
||||
sni.get_geolocation_info(RoutingDomain::PublicInternet);
|
||||
|
||||
// Since denylist is used, consider nodes with unknown countries to be automatically
|
||||
// excluded as well
|
||||
if geolocation_info.country_code().is_none() {
|
||||
log_rtab!(
|
||||
debug "allocate_route_inner: skipping node {} from unknown country",
|
||||
e.best_node_id()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
// Same thing applies to relays used by the node
|
||||
if geolocation_info
|
||||
.relay_country_codes()
|
||||
.iter()
|
||||
.any(Option::is_none)
|
||||
{
|
||||
log_rtab!(
|
||||
debug "allocate_route_inner: skipping node {} using relay from unknown country",
|
||||
e.best_node_id()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure that node is not excluded
|
||||
// Safe to unwrap here, checked above
|
||||
if country_code_denylist.contains(&geolocation_info.country_code().unwrap())
|
||||
{
|
||||
log_rtab!(
|
||||
debug "allocate_route_inner: skipping node {} from excluded country {}",
|
||||
e.best_node_id(),
|
||||
geolocation_info.country_code().unwrap()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure that node relays are not excluded
|
||||
// Safe to unwrap here, checked above
|
||||
if geolocation_info
|
||||
.relay_country_codes()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Option::unwrap)
|
||||
.any(|cc| country_code_denylist.contains(&cc))
|
||||
{
|
||||
log_rtab!(
|
||||
debug "allocate_route_inner: skipping node {} using relay from excluded country {:?}",
|
||||
e.best_node_id(),
|
||||
geolocation_info
|
||||
.relay_country_codes()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Option::unwrap)
|
||||
.filter(|cc| country_code_denylist.contains(&cc))
|
||||
.next()
|
||||
.unwrap()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude nodes on our same ipblock, or their relay is on our same ipblock
|
||||
// or our relay is on their ipblock, or their relay is on our relays same ipblock
|
||||
|
||||
@ -350,7 +425,7 @@ impl RouteSpecStore {
|
||||
entry.with_inner(|e| {
|
||||
e.signed_node_info(RoutingDomain::PublicInternet)
|
||||
.map(|sni| {
|
||||
sni.has_sequencing_matched_dial_info(sequencing)
|
||||
sni.has_sequencing_matched_dial_info(safety_spec.sequencing)
|
||||
&& sni.node_info().has_capability(CAP_ROUTE)
|
||||
})
|
||||
.unwrap_or(false)
|
||||
@ -387,16 +462,16 @@ impl RouteSpecStore {
|
||||
// apply sequencing preference
|
||||
// ensureordered will be taken care of by filter
|
||||
// and nopreference doesn't care
|
||||
if matches!(sequencing, Sequencing::PreferOrdered) {
|
||||
if matches!(safety_spec.sequencing, Sequencing::PreferOrdered) {
|
||||
let cmp_seq = entry1.with_inner(|e1| {
|
||||
entry2.with_inner(|e2| {
|
||||
let e1_can_do_ordered = e1
|
||||
.signed_node_info(RoutingDomain::PublicInternet)
|
||||
.map(|sni| sni.has_sequencing_matched_dial_info(sequencing))
|
||||
.map(|sni| sni.has_sequencing_matched_dial_info(safety_spec.sequencing))
|
||||
.unwrap_or(false);
|
||||
let e2_can_do_ordered = e2
|
||||
.signed_node_info(RoutingDomain::PublicInternet)
|
||||
.map(|sni| sni.has_sequencing_matched_dial_info(sequencing))
|
||||
.map(|sni| sni.has_sequencing_matched_dial_info(safety_spec.sequencing))
|
||||
.unwrap_or(false);
|
||||
// Reverse this comparison because ordered is preferable (less)
|
||||
e2_can_do_ordered.cmp(&e1_can_do_ordered)
|
||||
@ -410,7 +485,7 @@ impl RouteSpecStore {
|
||||
// apply stability preference
|
||||
// always prioritize reliable nodes, but sort by oldest or fastest
|
||||
entry1.with_inner(|e1| {
|
||||
entry2.with_inner(|e2| match stability {
|
||||
entry2.with_inner(|e2| match safety_spec.stability {
|
||||
Stability::LowLatency => BucketEntryInner::cmp_fastest_reliable(cur_ts, e1, e2),
|
||||
Stability::Reliable => BucketEntryInner::cmp_oldest_reliable(cur_ts, e1, e2),
|
||||
})
|
||||
@ -427,7 +502,7 @@ impl RouteSpecStore {
|
||||
rti.find_peers_with_sort_and_filter(usize::MAX, cur_ts, filters, compare, transform);
|
||||
|
||||
// If we couldn't find enough nodes, wait until we have more nodes in the routing table
|
||||
if nodes.len() < hop_count {
|
||||
if nodes.len() < safety_spec.hop_count {
|
||||
apibail_try_again!("not enough nodes to construct route at this time");
|
||||
}
|
||||
|
||||
@ -498,7 +573,7 @@ impl RouteSpecStore {
|
||||
previous_node.clone(),
|
||||
current_node.clone(),
|
||||
DialInfoFilter::all(),
|
||||
sequencing,
|
||||
safety_spec.sequencing,
|
||||
None,
|
||||
);
|
||||
if matches!(cm, ContactMethod::Unreachable) {
|
||||
@ -537,7 +612,7 @@ impl RouteSpecStore {
|
||||
next_node.clone(),
|
||||
current_node.clone(),
|
||||
DialInfoFilter::all(),
|
||||
sequencing,
|
||||
safety_spec.sequencing,
|
||||
None,
|
||||
);
|
||||
if matches!(cm, ContactMethod::Unreachable) {
|
||||
@ -573,9 +648,11 @@ impl RouteSpecStore {
|
||||
let mut route_nodes: Vec<usize> = Vec::new();
|
||||
let mut can_do_sequenced: bool = true;
|
||||
|
||||
for start in 0..(nodes.len() - hop_count) {
|
||||
for start in 0..(nodes.len() - safety_spec.hop_count) {
|
||||
// Try the permutations available starting with 'start'
|
||||
if let Some((rn, cds)) = with_route_permutations(hop_count, start, &mut perm_func) {
|
||||
if let Some((rn, cds)) =
|
||||
with_route_permutations(safety_spec.hop_count, start, &mut perm_func)
|
||||
{
|
||||
route_nodes = rn;
|
||||
can_do_sequenced = cds;
|
||||
break;
|
||||
@ -625,7 +702,7 @@ impl RouteSpecStore {
|
||||
route_set,
|
||||
hop_node_refs,
|
||||
directions,
|
||||
stability,
|
||||
safety_spec.stability,
|
||||
can_do_sequenced,
|
||||
automatic,
|
||||
);
|
||||
@ -1333,9 +1410,7 @@ impl RouteSpecStore {
|
||||
inner,
|
||||
rti,
|
||||
&[crypto_kind],
|
||||
safety_spec.stability,
|
||||
safety_spec.sequencing,
|
||||
safety_spec.hop_count,
|
||||
safety_spec,
|
||||
direction,
|
||||
avoid_nodes,
|
||||
true,
|
||||
|
@ -207,11 +207,15 @@ impl RoutingTable {
|
||||
for _n in 0..routes_to_allocate {
|
||||
// Parameters here must be the most inclusive safety route spec
|
||||
// These will be used by test_remote_route as well
|
||||
let safety_spec = SafetySpec {
|
||||
preferred_route: None,
|
||||
hop_count: default_route_hop_count,
|
||||
stability: Stability::Reliable,
|
||||
sequencing: Sequencing::PreferOrdered,
|
||||
};
|
||||
match rss.allocate_route(
|
||||
&VALID_CRYPTO_KINDS,
|
||||
Stability::Reliable,
|
||||
Sequencing::PreferOrdered,
|
||||
default_route_hop_count,
|
||||
&safety_spec,
|
||||
DirectionSet::all(),
|
||||
&[],
|
||||
true,
|
||||
|
@ -172,6 +172,7 @@ impl RPCProcessor {
|
||||
// Ensure the route is validated, and construct a return safetyspec that matches the inbound preferences
|
||||
let rss = self.routing_table().route_spec_store();
|
||||
let preferred_route = rss.get_route_id_for_key(&pr_pubkey.value);
|
||||
|
||||
let Some((secret_key, safety_spec)) = rss.with_signature_validated_route(
|
||||
&pr_pubkey,
|
||||
routed_operation.signatures(),
|
||||
|
@ -282,6 +282,8 @@ pub fn config_callback(key: String) -> ConfigCallbackReturn {
|
||||
"network.protocol.wss.listen_address" => Ok(Box::new("".to_owned())),
|
||||
"network.protocol.wss.path" => Ok(Box::new(String::from("ws"))),
|
||||
"network.protocol.wss.url" => Ok(Box::new(Option::<String>::None)),
|
||||
#[cfg(feature = "geolocation")]
|
||||
"network.privacy.country_code_denylist" => Ok(Box::new(Vec::<CountryCode>::new())),
|
||||
_ => {
|
||||
let err = format!("config key '{}' doesn't exist", key);
|
||||
debug!("{}", err);
|
||||
@ -419,6 +421,9 @@ pub async fn test_config() {
|
||||
assert_eq!(inner.network.protocol.wss.listen_address, "");
|
||||
assert_eq!(inner.network.protocol.wss.path, "ws");
|
||||
assert_eq!(inner.network.protocol.wss.url, None);
|
||||
|
||||
#[cfg(feature = "geolocation")]
|
||||
assert_eq!(inner.network.privacy.country_code_denylist, Vec::new());
|
||||
}
|
||||
|
||||
pub async fn test_all() {
|
||||
|
@ -300,16 +300,16 @@ impl VeilidAPI {
|
||||
c.network.rpc.default_route_hop_count.into()
|
||||
};
|
||||
|
||||
let rss = self.routing_table()?.route_spec_store();
|
||||
let route_id = rss.allocate_route(
|
||||
crypto_kinds,
|
||||
let safety_spec = SafetySpec {
|
||||
preferred_route: None,
|
||||
hop_count: default_route_hop_count,
|
||||
stability,
|
||||
sequencing,
|
||||
default_route_hop_count,
|
||||
DirectionSet::all(),
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
};
|
||||
|
||||
let rss = self.routing_table()?.route_spec_store();
|
||||
let route_id =
|
||||
rss.allocate_route(crypto_kinds, &safety_spec, DirectionSet::all(), &[], false)?;
|
||||
match rss.test_route(route_id).await? {
|
||||
Some(true) => {
|
||||
// route tested okay
|
||||
|
@ -185,6 +185,7 @@ fn get_safety_selection(routing_table: RoutingTable) -> impl Fn(&str) -> Option<
|
||||
sequencing = s;
|
||||
}
|
||||
}
|
||||
|
||||
let ss = SafetySpec {
|
||||
preferred_route,
|
||||
hop_count,
|
||||
@ -1146,22 +1147,22 @@ impl VeilidAPI {
|
||||
ai += 1;
|
||||
}
|
||||
|
||||
// Allocate route
|
||||
let out = match rss.allocate_route(
|
||||
&VALID_CRYPTO_KINDS,
|
||||
let safety_spec = SafetySpec {
|
||||
preferred_route: None,
|
||||
hop_count,
|
||||
stability,
|
||||
sequencing,
|
||||
hop_count,
|
||||
directions,
|
||||
&[],
|
||||
false,
|
||||
) {
|
||||
Ok(v) => v.to_string(),
|
||||
Err(e) => {
|
||||
format!("Route allocation failed: {}", e)
|
||||
}
|
||||
};
|
||||
|
||||
// Allocate route
|
||||
let out =
|
||||
match rss.allocate_route(&VALID_CRYPTO_KINDS, &safety_spec, directions, &[], false) {
|
||||
Ok(v) => v.to_string(),
|
||||
Err(e) => {
|
||||
format!("Route allocation failed: {}", e)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
async fn debug_route_release(&self, args: Vec<String>) -> VeilidAPIResult<String> {
|
||||
|
@ -237,6 +237,10 @@ pub fn fix_veilidconfiginner() -> VeilidConfigInner {
|
||||
url: Some("https://veilid.com/wss".to_string()),
|
||||
},
|
||||
},
|
||||
#[cfg(feature = "geolocation")]
|
||||
privacy: VeilidConfigPrivacy {
|
||||
country_code_denylist: vec![CountryCode([b'N', b'Z'])],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -275,6 +275,28 @@ pub struct VeilidConfigProtocol {
|
||||
pub wss: VeilidConfigWSS,
|
||||
}
|
||||
|
||||
/// Privacy preferences for routes.
|
||||
///
|
||||
/// ```yaml
|
||||
/// privacy:
|
||||
/// country_code_denylist: []
|
||||
/// ```
|
||||
#[cfg(feature = "geolocation")]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
|
||||
pub struct VeilidConfigPrivacy {
|
||||
pub country_code_denylist: Vec<CountryCode>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "geolocation")]
|
||||
impl Default for VeilidConfigPrivacy {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
country_code_denylist: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure TLS.
|
||||
///
|
||||
/// ```yaml
|
||||
@ -503,6 +525,8 @@ pub struct VeilidConfigNetwork {
|
||||
pub tls: VeilidConfigTLS,
|
||||
pub application: VeilidConfigApplication,
|
||||
pub protocol: VeilidConfigProtocol,
|
||||
#[cfg(feature = "geolocation")]
|
||||
pub privacy: VeilidConfigPrivacy,
|
||||
}
|
||||
|
||||
impl Default for VeilidConfigNetwork {
|
||||
@ -527,6 +551,8 @@ impl Default for VeilidConfigNetwork {
|
||||
tls: VeilidConfigTLS::default(),
|
||||
application: VeilidConfigApplication::default(),
|
||||
protocol: VeilidConfigProtocol::default(),
|
||||
#[cfg(feature = "geolocation")]
|
||||
privacy: VeilidConfigPrivacy::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -970,6 +996,8 @@ impl VeilidConfig {
|
||||
get_config!(inner.network.protocol.wss.listen_address);
|
||||
get_config!(inner.network.protocol.wss.path);
|
||||
get_config!(inner.network.protocol.wss.url);
|
||||
#[cfg(feature = "geolocation")]
|
||||
get_config!(inner.network.privacy.country_code_denylist);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
@ -42,6 +42,8 @@ tracking = ["veilid-core/tracking"]
|
||||
debug-json-api = []
|
||||
debug-locks = ["veilid-core/debug-locks"]
|
||||
|
||||
geolocation = ["veilid-core/geolocation"]
|
||||
|
||||
[dependencies]
|
||||
veilid-core = { path = "../veilid-core", default-features = false }
|
||||
tracing = { version = "^0.1.40", features = ["log", "attributes"] }
|
||||
|
@ -32,6 +32,14 @@ lazy_static! {
|
||||
}
|
||||
|
||||
pub fn load_default_config() -> EyreResult<config::Config> {
|
||||
#[cfg(not(feature = "geolocation"))]
|
||||
let privacy_section = "";
|
||||
#[cfg(feature = "geolocation")]
|
||||
let privacy_section = r#"
|
||||
privacy:
|
||||
country_code_denylist: []
|
||||
"#;
|
||||
|
||||
let mut default_config = String::from(
|
||||
r#"---
|
||||
daemon:
|
||||
@ -188,6 +196,7 @@ core:
|
||||
listen_address: ':5150'
|
||||
path: 'ws'
|
||||
# url: ''
|
||||
%PRIVACY_SECTION%
|
||||
"#,
|
||||
)
|
||||
.replace(
|
||||
@ -217,7 +226,8 @@ core:
|
||||
.replace(
|
||||
"%REMOTE_MAX_SUBKEY_CACHE_MEMORY_MB%",
|
||||
&Settings::get_default_remote_max_subkey_cache_memory_mb().to_string(),
|
||||
);
|
||||
)
|
||||
.replace("%PRIVACY_SECTION%", privacy_section);
|
||||
|
||||
let dek_password = if let Some(dek_password) = std::env::var_os("DEK_PASSWORD") {
|
||||
dek_password
|
||||
@ -584,6 +594,12 @@ pub struct Protocol {
|
||||
pub wss: Wss,
|
||||
}
|
||||
|
||||
#[cfg(feature = "geolocation")]
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Privacy {
|
||||
pub country_code_denylist: Vec<CountryCode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Tls {
|
||||
pub certificate_path: String,
|
||||
@ -661,6 +677,8 @@ pub struct Network {
|
||||
pub tls: Tls,
|
||||
pub application: Application,
|
||||
pub protocol: Protocol,
|
||||
#[cfg(feature = "geolocation")]
|
||||
pub privacy: Privacy,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
@ -1164,6 +1182,8 @@ impl Settings {
|
||||
set_config_value!(inner.core.network.protocol.wss.listen_address, value);
|
||||
set_config_value!(inner.core.network.protocol.wss.path, value);
|
||||
set_config_value!(inner.core.network.protocol.wss.url, value);
|
||||
#[cfg(feature = "geolocation")]
|
||||
set_config_value!(inner.core.network.privacy.country_code_denylist, value);
|
||||
Err(eyre!("settings key '{key}' not found"))
|
||||
}
|
||||
|
||||
@ -1548,6 +1568,10 @@ impl Settings {
|
||||
.as_ref()
|
||||
.map(|a| a.urlstring.clone()),
|
||||
)),
|
||||
#[cfg(feature = "geolocation")]
|
||||
"network.privacy.country_code_denylist" => Ok(Box::new(
|
||||
inner.core.network.privacy.country_code_denylist.clone(),
|
||||
)),
|
||||
_ => Err(VeilidAPIError::generic(format!(
|
||||
"config key '{}' doesn't exist",
|
||||
key
|
||||
@ -1788,5 +1812,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(s.core.network.protocol.wss.url, None);
|
||||
//
|
||||
#[cfg(feature = "geolocation")]
|
||||
assert_eq!(s.core.network.privacy.country_code_denylist, &[]);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user