Skip to content

Commit

Permalink
feat: geo-blocking
Browse files Browse the repository at this point in the history
  • Loading branch information
Xavier Basty committed Sep 30, 2023
1 parent c458b45 commit 9d081d7
Show file tree
Hide file tree
Showing 10 changed files with 1,440 additions and 578 deletions.
1,825 changes: 1,280 additions & 545 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ authors = [
build = "build.rs"

[dependencies]
wc = { git = "https://github.com/WalletConnect/utils-rs.git", tag = "v0.5.1", features = ["geoip", "geoblock"] }

aws-config = "0.56"
aws-sdk-s3 = "0.31"

axum = "0.6.1"
axum-macros = "0.3.0"
tokio = { version = "1.0", features = ["full"] }
Expand Down
26 changes: 13 additions & 13 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
edition = "2021"
fn_single_line = false
format_code_in_doc_comments = true
format_strings = true
imports_layout = "HorizontalVertical"
imports_granularity = "One"
normalize_comments = true
normalize_doc_attributes = true
#fn_single_line = false
#format_code_in_doc_comments = true
#format_strings = true
#imports_layout = "HorizontalVertical"
#imports_granularity = "One"
#normalize_comments = true
#normalize_doc_attributes = true
reorder_imports = true
reorder_impl_items = true
group_imports = "StdExternalCrate"
#reorder_impl_items = true
#group_imports = "StdExternalCrate"
use_try_shorthand = true
wrap_comments = true
overflow_delimited_expr = true
#wrap_comments = true
#overflow_delimited_expr = true
remove_nested_parens = true
reorder_modules = true
unstable_features = true
use_field_init_shorthand = true
#unstable_features = true
use_field_init_shorthand = true
12 changes: 11 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,20 @@ pub struct Configuration {
pub log_level: String,
pub database_url: String,

// TELEMETRY
// Telemetry
pub telemetry_enabled: Option<bool>,
pub telemetry_grpc_url: Option<String>,
pub telemetry_prometheus_port: Option<u16>,

// AWS
pub s3_endpoint: Option<String>,

// GeoIP
pub geoip_db_bucket: Option<String>,
pub geoip_db_key: Option<String>,

// GeoBlocking
pub blocked_countries: Vec<String>,
}

impl Configuration {
Expand Down
108 changes: 89 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use {
crate::{config::Configuration, state::AppState},
::log::{info, warn},
crate::{config::Configuration, log::prelude::*, state::AppState},
aws_config::meta::region::RegionProviderChain,
aws_sdk_s3::{config::Region, Client as S3Client},
axum::{
body::HttpBody,
routing::{get, post},
Router,
},
Expand All @@ -16,6 +18,10 @@ use {
trace::{DefaultMakeSpan, DefaultOnRequest, DefaultOnResponse, TraceLayer},
},
tracing::Level,
wc::geoip::{
block::{middleware::GeoBlockLayer, BlockingPolicy},
MaxMindResolver,
},
};

pub mod auth;
Expand All @@ -35,6 +41,9 @@ pub async fn bootstrap(
let keys_persistent_storage: Arc<MongoPersistentStorage> =
Arc::new(MongoPersistentStorage::new(&config).await?);

let s3_client = get_s3_client(&config).await;
let geoip_resolver = get_geoip_resolver(&config, &s3_client).await;

let mut state = AppState::new(config, keys_persistent_storage)?;

if state.config.telemetry_prometheus_port.is_some() {
Expand Down Expand Up @@ -70,23 +79,26 @@ pub async fn bootstrap(
.allow_origin("*".parse::<HeaderValue>().unwrap())
.allow_methods([Method::GET, Method::POST]);

let app = Router::new()
.route("/health", get(handlers::health::handler))
.route(
"/identity",
get(handlers::identity::resolve::handler)
.post(handlers::identity::register::handler)
.delete(handlers::identity::unregister::handler),
)
.route(
"/invite",
post(handlers::invite::register::handler)
.delete(handlers::invite::unregister::handler)
.get(handlers::invite::resolve::handler),
)
.layer(global_middleware)
.layer(cors_layer)
.with_state(state_arc.clone());
let app = new_geoblocking_router(
geoip_resolver.clone(),
state_arc.config.blocked_countries.clone(),
)
.route("/health", get(handlers::health::handler))
.route(
"/identity",
get(handlers::identity::resolve::handler)
.post(handlers::identity::register::handler)
.delete(handlers::identity::unregister::handler),
)
.route(
"/invite",
post(handlers::invite::register::handler)
.delete(handlers::invite::unregister::handler)
.get(handlers::invite::resolve::handler),
)
.layer(global_middleware)
.layer(cors_layer)
.with_state(state_arc.clone());

let private_app = Router::new()
.route("/metrics", get(handlers::metrics::handler))
Expand All @@ -103,3 +115,61 @@ pub async fn bootstrap(

Ok(())
}

fn new_geoblocking_router<S, B>(
geoip_resolver: Option<Arc<MaxMindResolver>>,
blocked_countries: Vec<String>,
) -> Router<S, B>
where
S: Clone + Send + Sync + 'static,
B: HttpBody + Send + 'static,
{
if let Some(resolver) = geoip_resolver {
Router::new().layer(GeoBlockLayer::new(
resolver.clone(),
blocked_countries.clone(),
BlockingPolicy::AllowAll,
))
} else {
Router::new()
}
}

async fn get_s3_client(config: &Configuration) -> S3Client {
let region_provider = RegionProviderChain::first_try(Region::new("eu-central-1"));
let shared_config = aws_config::from_env().region(region_provider).load().await;

let aws_config = match &config.s3_endpoint {
Some(s3_endpoint) => {
info!(%s3_endpoint, "initializing analytics with custom s3 endpoint");

aws_sdk_s3::config::Builder::from(&shared_config)
.endpoint_url(s3_endpoint)
.build()
}
_ => aws_sdk_s3::config::Builder::from(&shared_config).build(),
};

S3Client::from_conf(aws_config)
}

async fn get_geoip_resolver(
config: &Configuration,
s3_client: &S3Client,
) -> Option<Arc<MaxMindResolver>> {
match (&config.geoip_db_bucket, &config.geoip_db_key) {
(Some(bucket), Some(key)) => {
info!(%bucket, %key, "initializing geoip database from aws s3");

Some(Arc::new(
MaxMindResolver::from_aws_s3(s3_client, bucket, key)
.await
.expect("failed to load geoip resolver"),
))
}
_ => {
info!("analytics geoip lookup is disabled");
None
}
}
}
6 changes: 6 additions & 0 deletions terraform/ecs/cluster.tf
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,13 @@ resource "aws_ecs_task_definition" "app_task" {
environment = [
{ "name" = "DATABASE_URL", "value" = var.keystore_addr },
{ "name" = "LOG_LEVEL", "value" = var.log_level },

{ "name" = "TELEMETRY_PROMETHEUS_PORT", "value" = tostring(local.telemetry_port) },

{ "name" = "GEOIP_DB_BUCKET", "value" = var.geoip_db_bucket_name },
{ "name" = "GEOIP_DB_KEY", "value" = var.geoip_db_key },

{ "name" = "BLOCKED_COUNTRIES", "value" = "KP,IR,CU,SY" },
],

portMappings = [
Expand Down
13 changes: 13 additions & 0 deletions terraform/ecs/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,16 @@ variable "prometheus_endpoint" {
description = "The endpoint of the Prometheus server to use for monitoring"
type = string
}

#---------------------------------------
# GeoIP

variable "geoip_db_bucket_name" {
description = "The name of the S3 bucket where the GeoIP database is stored"
type = string
}

variable "geoip_db_key" {
description = "The key of the GeoIP database in the S3 bucket"
type = string
}
10 changes: 10 additions & 0 deletions terraform/inputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ data "terraform_remote_state" "org" {
}
}

data "terraform_remote_state" "datalake" {
backend = "remote"
config = {
organization = "wallet-connect"
workspaces = {
name = "data-lake-${module.this.stage}"
}
}
}

data "terraform_remote_state" "dns" {
backend = "remote"
config = {
Expand Down
4 changes: 4 additions & 0 deletions terraform/res_application.tf
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,8 @@ module "ecs" {

# Monitoring
prometheus_endpoint = aws_prometheus_workspace.prometheus.prometheus_endpoint

# GeoIP
geoip_db_bucket_name = data.terraform_remote_state.datalake.outputs.geoip_bucket_id
geoip_db_key = var.geoip_db_key
}
9 changes: 9 additions & 0 deletions terraform/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,12 @@ variable "betterstack_cloudwatch_webhook" {
type = string
sensitive = true
}

#---------------------------------------
# GeoIP

variable "geoip_db_key" {
description = "The name to the GeoIP database"
type = string
default = "GeoLite2-City.mmdb"
}

0 comments on commit 9d081d7

Please sign in to comment.