Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
475 changes: 237 additions & 238 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ http = "1.3.1"
http-body = "1.0.1"
http-body-util = "0.1.2"
humantime = "2.2.0"
ic-bn-lib = { version = "0.4", features = [
ic-bn-lib = { version = "0.4.1", features = [
"cert-providers",
"lb",
"clients-hyper",
Expand All @@ -48,7 +48,7 @@ ic-http-gateway-protocol = { package = "ic-http-gateway-protocol", git = "https:
isbot = "0.1"
itertools = "0.15.0"
lazy_static = "1.5.0"
maxminddb = "0.28.1"
maxminddb = "0.29"
moka = { version = "0.12.8", features = ["sync", "future"] }
prometheus = "0.14.0"
rand = { version = "0.8.5", features = ["small_rng"] }
Expand Down Expand Up @@ -97,7 +97,7 @@ hex = "0.4.3"
httptest = "0.16.1"
ic-certified-assets = { git = "https://github.com/dfinity/sdk.git", rev = "d65717bd6d0c172247c37dd23395c9fb13b2ba20" }
ic-http-certification = "3.1.0"
mockall = "0.14.0"
mockall = "0.15.0"
nix = { version = "0.31.0", features = ["signal"] }
pocket-ic = "=13.0.0"
rand_regex = "0.17.0"
Expand Down
25 changes: 17 additions & 8 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ pub struct Acme {
/// Currently supported:
/// - alpn: all served domains must resolve to the host where this service is running.
/// - dns: allows to request wildcard certificates, requires DNS backend to be configured.
/// - dns_persist: allows to request wildcard certificates and requires no DNS-interaction.
#[clap(env, long, requires = "acme_cache_path")]
pub acme_challenge: Option<Challenge>,

Expand All @@ -393,26 +394,34 @@ pub struct Acme {
#[clap(env, long)]
pub acme_cache_path: Option<PathBuf>,

/// DNS backend to use when using DNS challenge. Currently only "cloudflare" is supported.
/// ACME account credentials in JSON format.
/// If not provided - new account will be created.
#[clap(env, long)]
pub acme_account_creds: Option<String>,

/// DNS backend to use when using DNS challenge.
/// Currently only "cloudflare" is supported.
#[clap(env, long, default_value = "cloudflare")]
pub acme_dns_backend: DnsBackend,

/// Cloudflare API URL
/// Cloudflare API URL.
/// Makes sense only when the DNS backend is set to `cloudflare`.
#[clap(env, long, default_value = DEFAULT_CLOUDFLARE_URL)]
pub acme_dns_cloudflare_url: Url,

/// File from which to read API token if DNS backend is Cloudflare
/// Cloudflare token to use.
/// Makes sense only when the DNS backend is set to `cloudflare`.
#[clap(env, long)]
pub acme_dns_cloudflare_token: Option<PathBuf>,
pub acme_dns_cloudflare_token: Option<String>,

/// Asks ACME client to request a wildcard certificate for each of the domains configured.
/// So in addition to `foo.app` the certificate will be also valid for `*.foo.app`.
/// For obvious reasons this works only with DNS challenge, has no effect with ALPN.
/// For obvious reasons this works only with DNS/DNS-PERSIST challenge, has no effect with ALPN.
#[clap(env, long)]
pub acme_wildcard: bool,

/// Attempt to renew the certificates when less than this duration is left until expiration.
/// This works only with DNS challenge, ALPN currently starts to renew after half of certificate
/// This works only with DNS/DNS-PERSIST challenge, ALPN currently starts to renew after half of certificate
/// lifetime has passed (45d for LetsEncrypt)
#[clap(env, long, value_parser = parse_duration, default_value = "30d")]
pub acme_renew_before: Duration,
Expand All @@ -422,8 +431,8 @@ pub struct Acme {
#[clap(env, long, default_value = "le_stag")]
pub acme_url: AcmeUrl,

/// E-Mail to use when creating ACME accounts, must start with mailto:
#[clap(env, long, default_value = "mailto:boundary-nodes@dfinity.org")]
/// E-Mail to use when creating an ACME account
#[clap(env, long, default_value = "boundary-nodes@dfinity.org")]
pub acme_contact: String,
}

Expand Down
2 changes: 1 addition & 1 deletion src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ pub async fn main(
#[cfg(feature = "acme")]
domains.clone(),
#[cfg(feature = "acme")]
Arc::new(dns_resolver),
dns_resolver,
certificate_providers,
&registry,
)
Expand Down
6 changes: 4 additions & 2 deletions src/log/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::time::{SystemTime, UNIX_EPOCH};
use std::{
os::unix::net::UnixDatagram,
time::{SystemTime, UNIX_EPOCH},
};

use anyhow::{Context, Error};
use serde::ser::{SerializeMap, Serializer as _};
use serde_json::Serializer;
use std::os::unix::net::UnixDatagram;
use tracing::{Event, Level, Subscriber};
use tracing_serde::AsSerde;
use tracing_subscriber::{
Expand Down
14 changes: 6 additions & 8 deletions src/metrics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,12 @@ use tracing::info;

use crate::{
core::{ENV, HOSTNAME},
routing::RemoteAddr,
};

use crate::routing::{
CanisterId, RequestCtx,
error_cause::ErrorCause,
ic::{BNRequestMetadata, BNResponseMetadata, IcResponseStatus},
middleware::{geoip::CountryCode, request_id::RequestId},
routing::{
CanisterId, RemoteAddr, RequestCtx,
error_cause::ErrorCause,
ic::{BNRequestMetadata, BNResponseMetadata, IcResponseStatus},
middleware::{geoip::CountryCode, request_id::RequestId},
},
};

const KB: f64 = 1024.0;
Expand Down
2 changes: 1 addition & 1 deletion src/metrics/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use tikv_jemalloc_ctl::{epoch, stats};
use tokio_util::sync::CancellationToken;
use tracing::{debug, warn};

// https://prometheus.io/docs/instrumenting/exposition_formats/#basic-info
/// https://prometheus.io/docs/instrumenting/exposition_formats/#basic-info
const PROMETHEUS_CONTENT_TYPE: &str = "text/plain; version=0.0.4";

pub struct MetricsCache {
Expand Down
1 change: 1 addition & 0 deletions src/policy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ pub fn load_principal_list(path: &PathBuf) -> Result<AHashSet<Principal>, Error>
.filter(|x| !x.trim().is_empty())
.map(Principal::from_text)
.collect::<Result<AHashSet<Principal>, _>>()?;

Ok(set)
}
3 changes: 1 addition & 2 deletions src/routing/ic/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use ic_bn_lib::http::{
};
use ic_http_gateway_protocol::{CanisterRequest, HttpGatewayClient, HttpGatewayRequestArgs};

use super::{BNRequestMetadata, BNResponseMetadata};
use crate::routing::{
CanisterId, RequestCtx,
error_cause::{CanisterError, ErrorCause},
Expand All @@ -22,8 +23,6 @@ use crate::routing::{
middleware::request_id::RequestId,
};

use super::{BNRequestMetadata, BNResponseMetadata};

#[derive(derive_new::new)]
pub struct HandlerState {
client: HttpGatewayClient,
Expand Down
12 changes: 3 additions & 9 deletions src/routing/middleware/geoip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,16 @@ impl GeoIp {

warn!(
"GeoIP loaded with {} entries in {}s",
db.metadata.node_count,
db.metadata().node_count,
start.elapsed().as_secs_f64()
);

Ok(Self { db })
}

pub fn lookup(&self, ip: IpAddr) -> Option<CountryCode> {
let country: Option<geoip2::Country> = self.db.lookup(ip)
.and_then(|r| r.decode())
.ok()
.flatten();

country.and_then(|x| {
x.country.iso_code.map(|code| CountryCode(code.into()))
})
let country: Option<geoip2::Country> = self.db.lookup(ip).ok()?.decode().ok()?;
Some(CountryCode(country?.country.iso_code?.into()))
}
}

Expand Down
94 changes: 63 additions & 31 deletions src/tls/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use std::sync::Arc;

use anyhow::{Error, bail};
#[cfg(feature = "acme")]
use ic_bn_lib::{dns::resolvers::Resolves, tls::acme::Challenge};
use ic_bn_lib::{
health::HealthManager,
tasks::TaskManager,
Expand All @@ -14,7 +12,6 @@ use ic_bn_lib::{
};
use prometheus::Registry;
use rustls::server::ServerConfig;

#[cfg(feature = "acme")]
use {
anyhow::{Context, anyhow},
Expand All @@ -23,8 +20,13 @@ use {
self,
dns::{AcmeDns, TokenManagerDns},
},
ic_bn_lib::{
dns::resolvers::Resolver,
http::client::clients_reqwest,
tls::acme::{Challenge, TokenManager, TokenManagerNoop},
},
rustls::server::ResolvesServerCert as ResolvesServerCertRustls,
std::{fs, time::Duration},
std::time::Duration,
};

use crate::cli::Cli;
Expand All @@ -35,7 +37,7 @@ async fn setup_acme(
tasks: &mut TaskManager,
domains: Vec<FQDN>,
challenge: &Challenge,
dns_resolver: Arc<dyn Resolves>,
dns_resolver: Resolver,
) -> Result<Arc<dyn ResolvesServerCertRustls>, Error> {
let cache_path = cli.acme.acme_cache_path.clone().unwrap();

Expand All @@ -46,6 +48,10 @@ async fn setup_acme(
domains.iter().map(|x| x.to_string()).collect::<Vec<_>>(),
cli.acme.acme_contact.clone(),
Comment thread
blind-oracle marked this conversation as resolved.
cache_path,
cli.acme
.acme_account_creds
.as_ref()
.map(|x| x.as_bytes().to_vec()),
None,
);

Expand All @@ -55,45 +61,71 @@ async fn setup_acme(
acme_alpn
}

Challenge::Dns => {
Challenge::Dns | Challenge::DnsPersist => {
use ic_bn_lib::tls::acme::DnsBackend;

let dns_backend = match cli.acme.acme_dns_backend {
DnsBackend::Cloudflare => {
use ic_bn_lib::tls::acme::DnsManager;

let path = cli
.acme
.acme_dns_cloudflare_token
.clone()
.ok_or_else(|| anyhow!("Cloudflare token not defined"))?;

let token =
fs::read_to_string(path).context("unable to read Cloudflare token")?;

Arc::new(acme::dns::cloudflare::Cloudflare::new(
cli.acme.acme_dns_cloudflare_url.clone(),
token,
)?) as Arc<dyn DnsManager>
}

_ => bail!("unsupported DNS backend: {}", cli.acme.acme_dns_backend),
// Create a token manager for DNS challenge, or use a no-op one for DNS-PERSIST
let token_manager = if *challenge == Challenge::Dns {
let dns_backend = match cli.acme.acme_dns_backend {
DnsBackend::Cloudflare => {
use ic_bn_lib::tls::acme::dns::DnsManager;

let token = cli
.acme
.acme_dns_cloudflare_token
.clone()
.ok_or_else(|| anyhow!("Cloudflare token not defined"))?;

let http_client = clients_reqwest::new(
(&cli.http_client).into(),
Some(dns_resolver.clone()),
)
.context("unable to create HTTP client for Cloudflare")?;

Arc::new(acme::dns::cloudflare::Cloudflare::new_with_http_client(
cli.acme.acme_dns_cloudflare_url.clone(),
token,
http_client,
)) as Arc<dyn DnsManager>
}

_ => bail!("unsupported DNS backend: {}", cli.acme.acme_dns_backend),
};

Arc::new(TokenManagerDns::new(
Arc::new(dns_resolver.clone()),
dns_backend,
None,
)) as Arc<dyn TokenManager>
} else {
Arc::new(TokenManagerNoop)
};

let token_manager = Arc::new(TokenManagerDns::new(dns_resolver, dns_backend, None));
let account_credentials = if let Some(v) = &cli.acme.acme_account_creds {
Some(
serde_json::from_str(v)
.context("unable to parse ACME account credentials as JSON")?,
)
} else {
None
};

let opts = acme::dns::Opts {
acme_url: cli.acme.acme_url.clone(),
domains,
path: cache_path,
wildcard: cli.acme.acme_wildcard,
renew_before: cli.acme.acme_renew_before,
account_credentials: None,
account_credentials,
token_manager,
insecure_tls: false,
contact: cli.acme.acme_contact.clone(),
Comment thread
blind-oracle marked this conversation as resolved.
};

let acme_dns = Arc::new(AcmeDns::new(opts).await.context("unable to init AcmeDns")?);
let acme_dns = Arc::new(
AcmeDns::new_with_http_opts(opts, (&cli.http_client).into(), dns_resolver)
.await
.context("unable to init AcmeDns")?,
);
tasks.add_interval("acme_dns_runner", acme_dns.clone(), Duration::from_mins(10));

acme_dns
Expand All @@ -109,7 +141,7 @@ pub async fn setup(
tasks: &mut TaskManager,
health_manager: Arc<HealthManager>,
#[cfg(feature = "acme")] domains: Vec<FQDN>,
#[cfg(feature = "acme")] dns_resolver: Arc<dyn Resolves>,
#[cfg(feature = "acme")] dns_resolver: Resolver,
certificate_providers: Vec<Arc<dyn ProvidesCertificates>>,
registry: &Registry,
) -> Result<ServerConfig, Error> {
Expand Down
2 changes: 1 addition & 1 deletion tools/create_acme_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use ic_bn_lib::tls::acme::{AcmeUrl, client::ClientBuilder};

#[tokio::main]
async fn main() {
let (_, creds) = ClientBuilder::new(false)
let (_, creds) = ClientBuilder::default()
.with_acme_url(AcmeUrl::LetsEncryptStaging)
.create_account("mailto:boundary-nodes@dfinity.org")
.await
Expand Down
Loading