Skip to content

Commit

Permalink
feat: allow cli option to disable ssl verification (#157)
Browse files Browse the repository at this point in the history
feat: add CLI option to disable SSL verification

Co-authored-by: Christopher Kolstad <chriswk@getunleash.ai>
  • Loading branch information
sighphyre and Christopher Kolstad authored Apr 17, 2023
1 parent 845aa17 commit b740273
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 16 deletions.
33 changes: 33 additions & 0 deletions examples/server.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFnzCCA4cCFCUGGh4r5q0rSDgWacZfimC2/HoMMA0GCSqGSIb3DQEBCwUAMIGL
MQswCQYDVQQGEwJaQTEMMAoGA1UECAwDS1pOMQ8wDQYDVQQHDAZQbGFjZXMxEDAO
BgNVBAoMB1VubGVhc2gxFDASBgNVBAsMC2RldmVsb3BtZW50MRIwEAYDVQQDDAls
b2NhbGhvc3QxITAfBgkqhkiG9w0BCQEWEnRlYW1AZ2V0dW5sZWFzaC5pbzAeFw0y
MzA0MTcxMDUyMThaFw00MzA0MTIxMDUyMThaMIGLMQswCQYDVQQGEwJaQTEMMAoG
A1UECAwDS1pOMQ8wDQYDVQQHDAZQbGFjZXMxEDAOBgNVBAoMB1VubGVhc2gxFDAS
BgNVBAsMC2RldmVsb3BtZW50MRIwEAYDVQQDDAlsb2NhbGhvc3QxITAfBgkqhkiG
9w0BCQEWEnRlYW1AZ2V0dW5sZWFzaC5pbzCCAiIwDQYJKoZIhvcNAQEBBQADggIP
ADCCAgoCggIBALujqEdzGN/M5RhUbkdnOhIo4oJ3lzDX2Pkn2hkWAitBsr4FIUA+
12O1H00J9QP9kNydF+xDu0aAiAjN2rm9pzNQ+0KkzjWR2E7CPTo8ZMqR4u31EWG5
2dPqEQdmvRvp/kPv71ldz8JlrFn9MqSWY6KrKhEiFiz208OlKg0VN/FWLx6fkqMX
D2dLy+6t5k7yhNU2hgZ2fImYYD9l7KaD8rM8TLzwqcSM2M2OwrRfDMI4eaMwD9jl
I/qIpa+sgZlSxnABwet02PugpZ1eVbJKrezSWx1qNI2KeWywVizhdTlckVoa14nW
hk9qdblY2gH+IENjeRKog7Nr7VcGSakwzuDqGS1QqsadL8n/czzUxRTEBX/wRAPZ
ND/obLxSHNMYKsnKn0xMa3cBbbyu2zj+cdwWaE3/7q4UZ8X2VCVStl+6IjmgaStV
xNmHvkNRD4SL0UliKtNPFUTSGZxMeGbqLjPbRL7rgCfcxpLxj+5d09yYLZesy2zG
/fMxxUVKFvPaBd8QDf6DoDlAReB6ebNa1AKozBQEz2l50lIB9BneZfY+S55ovfpI
EHwRGTQbWy/q24ZM/UXMLa6O9OBeAN82v03zkrtd2/DAGHvFZYacoCnZEGmX+RsH
sqf//iMKJ1tpN3Gi4krmtiE2nm+XREgmgCP+KmGlFeA2bDLtpdZFr5JtAgMBAAEw
DQYJKoZIhvcNAQELBQADggIBADEKJnMpbsUA4NuM9MtpCykmo2ZPCcYjHKo8Ymjj
WOkoHQ078CiK7QUQQgIBWGtqZ0Z323qPpGddr7xYX0D3PzON2qM6i6Y4CKhSWIo1
dRdmeLfkLsUwY6pEClF6h53d6G87k2raLAc77+W+rEiS7PS4pm7AdEjyfqeUhcnC
JbFwDC8kVjR/3+0/4MGYaYSKbUwe+20O927UIiShVaoruiJJ9JaH1YnIDCNOzekj
4/Gd/mnlqitJuyRSuVSAI6S+YkfaLeAXyrYIJsNrQpoShblXEYMc/C39rCHvNU2C
V/xBABkXOmOX48DeMaZ26I1q8xmIDewAs2iCtJtaN3nX5ioY7C09UDYH57wCdxF6
oL78p2IQxL4FJhn7bmtKUWWXDct1Bbiikhrmyckz2VwvoVWrh+gaekPRXGvAtC7c
YRBLbQGthAR/uZrsYdFTYkQc1Y0Ntxrs+jbqljce2yQprcne6JKjqIeAfxN8Cvt6
ZUkV6MECckgMVbt9Aw9fiM0CNWKb1mj7myHMOXf6zr2HzHKC4J9pY0Pv7al9p4Fq
aztOQBJAj9DkA6UqrCYFh4HnhV7QDzkzp4/h8VylrQ+cii0wpwz1kkCTGvYeKwdc
dZieoT+78V+PRtmb8Ch4Wh/7oHxm5uYNU3be/gjwNnSoHO3FYTO3P0e2H1mhhPGJ
eZFS
-----END CERTIFICATE-----
52 changes: 52 additions & 0 deletions examples/server.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
RfVjCJn+jnI3UV8MzyE+ii2n5dNTvOY3
-----END PRIVATE KEY-----
2 changes: 1 addition & 1 deletion server/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ async fn build_edge(args: &EdgeArgs) -> EdgeResult<EdgeInfo> {
let persistence = get_data_source(args).await;

let unleash_client = Url::parse(&args.upstream_url.clone())
.map(UnleashClient::from_url)
.map(|url| UnleashClient::from_url(url, args.skip_ssl_verification))
.map(|c| c.with_custom_client_headers(args.custom_client_headers.clone()))
.map(Arc::new)
.map_err(|_| EdgeError::InvalidServerUrl(args.upstream_url.clone()))?;
Expand Down
4 changes: 4 additions & 0 deletions server/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ pub struct EdgeArgs {
/// for instance `-H X-Api-Key: mysecretapikey`
#[clap(short = 'H', long, env, value_delimiter = ',', value_parser = string_to_header_tuple)]
pub custom_client_headers: Vec<(String, String)>,

/// If set to true, we will skip SSL verification when connecting to the upstream Unleash server
#[clap(short, long, env, default_value_t = false)]
pub skip_ssl_verification: bool,
}

pub fn string_to_header_tuple(s: &str) -> Result<(String, String), String> {
Expand Down
2 changes: 1 addition & 1 deletion server/src/client_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pub async fn get_features(
}
#[utoipa::path(
context_path = "/api",
params(("feature_name" = String, Path,)),
params(("feature_name" = String, Path,)),
responses(
(status = 200, description = "Return feature toggles for this token", body = ClientFeature),
(status = 403, description = "Was not allowed to access feature"),
Expand Down
21 changes: 14 additions & 7 deletions server/src/http/feature_refresher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,8 @@ mod tests {

#[tokio::test]
pub async fn registering_token_for_refresh_works() {
let unleash_client = UnleashClient::from_url(Url::parse("http://localhost:4242").unwrap());
let unleash_client =
UnleashClient::from_url(Url::parse("http://localhost:4242").unwrap(), false);
let features_cache = Arc::new(DashMap::default());
let engines_cache = Arc::new(DashMap::default());

Expand All @@ -349,7 +350,8 @@ mod tests {

#[tokio::test]
pub async fn registering_multiple_non_overlapping_tokens_will_keep_all() {
let unleash_client = UnleashClient::from_url(Url::parse("http://localhost:4242").unwrap());
let unleash_client =
UnleashClient::from_url(Url::parse("http://localhost:4242").unwrap(), false);
let features_cache = Arc::new(DashMap::default());
let engines_cache = Arc::new(DashMap::default());
let duration = Duration::seconds(5);
Expand Down Expand Up @@ -384,7 +386,8 @@ mod tests {

#[tokio::test]
pub async fn registering_wildcard_project_token_only_keeps_the_wildcard() {
let unleash_client = UnleashClient::from_url(Url::parse("http://localhost:4242").unwrap());
let unleash_client =
UnleashClient::from_url(Url::parse("http://localhost:4242").unwrap(), false);
let features_cache = Arc::new(DashMap::default());
let engines_cache = Arc::new(DashMap::default());
let duration = Duration::seconds(5);
Expand Down Expand Up @@ -428,7 +431,8 @@ mod tests {

#[tokio::test]
pub async fn registering_tokens_with_multiple_projects_overwrites_single_tokens() {
let unleash_client = UnleashClient::from_url(Url::parse("http://localhost:4242").unwrap());
let unleash_client =
UnleashClient::from_url(Url::parse("http://localhost:4242").unwrap(), false);
let features_cache = Arc::new(DashMap::default());
let engines_cache = Arc::new(DashMap::default());
let duration = Duration::seconds(5);
Expand Down Expand Up @@ -476,7 +480,8 @@ mod tests {

#[tokio::test]
pub async fn registering_a_token_that_is_already_subsumed_does_nothing() {
let unleash_client = UnleashClient::from_url(Url::parse("http://localhost:4242").unwrap());
let unleash_client =
UnleashClient::from_url(Url::parse("http://localhost:4242").unwrap(), false);
let features_cache = Arc::new(DashMap::default());
let engines_cache = Arc::new(DashMap::default());

Expand Down Expand Up @@ -509,7 +514,8 @@ mod tests {

#[tokio::test]
pub async fn simplification_only_happens_in_same_environment() {
let unleash_client = UnleashClient::from_url(Url::parse("http://localhost:4242").unwrap());
let unleash_client =
UnleashClient::from_url(Url::parse("http://localhost:4242").unwrap(), false);
let features_cache = Arc::new(DashMap::default());
let engines_cache = Arc::new(DashMap::default());

Expand Down Expand Up @@ -537,7 +543,8 @@ mod tests {

#[tokio::test]
pub async fn is_able_to_only_fetch_for_tokens_due_to_refresh() {
let unleash_client = UnleashClient::from_url(Url::parse("http://localhost:4242").unwrap());
let unleash_client =
UnleashClient::from_url(Url::parse("http://localhost:4242").unwrap(), false);
let features_cache = Arc::new(DashMap::default());
let engines_cache = Arc::new(DashMap::default());

Expand Down
89 changes: 82 additions & 7 deletions server/src/http/unleash_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::str::FromStr;
use std::time::Duration;
use ulid::Ulid;
use unleash_types::client_features::ClientFeatures;

use crate::metrics::client_metrics::MetricsBatch;
Expand Down Expand Up @@ -60,7 +59,7 @@ pub struct UnleashClient {
custom_headers: HashMap<String, String>,
}

fn new_reqwest_client(instance_id: String) -> Client {
fn new_reqwest_client(instance_id: String, skip_ssl_verification: bool) -> Client {
let mut header_map = HeaderMap::new();
header_map.insert(
UNLEASH_APPNAME_HEADER,
Expand All @@ -77,6 +76,7 @@ fn new_reqwest_client(instance_id: String) -> Client {
Client::builder()
.user_agent(format!("unleash-edge-{}", crate::types::build::PKG_VERSION))
.default_headers(header_map)
.danger_accept_invalid_certs(skip_ssl_verification)
.timeout(Duration::from_secs(5))
.build()
.unwrap()
Expand All @@ -87,19 +87,33 @@ pub struct EdgeTokens {
}

impl UnleashClient {
pub fn from_url(server_url: Url) -> Self {
pub fn from_url(server_url: Url, skip_ssl_verification: bool) -> Self {
Self {
urls: UnleashUrls::from_base_url(server_url),
backing_client: new_reqwest_client("unleash_edge".into()),
backing_client: new_reqwest_client("unleash_edge".into(), skip_ssl_verification),
custom_headers: Default::default(),
}
}

#[cfg(test)]
pub fn new(server_url: &str, instance_id_opt: Option<String>) -> Result<Self, EdgeError> {
use ulid::Ulid;

let instance_id = instance_id_opt.unwrap_or_else(|| Ulid::new().to_string());
Ok(Self {
urls: UnleashUrls::from_str(server_url)?,
backing_client: new_reqwest_client(instance_id),
backing_client: new_reqwest_client(instance_id, false),
custom_headers: Default::default(),
})
}

#[cfg(test)]
pub fn new_insecure(server_url: &str) -> Result<Self, EdgeError> {
use ulid::Ulid;

Ok(Self {
urls: UnleashUrls::from_str(server_url)?,
backing_client: new_reqwest_client(Ulid::new().to_string(), true),
custom_headers: Default::default(),
})
}
Expand Down Expand Up @@ -258,13 +272,15 @@ impl UnleashClient {
#[cfg(test)]
mod tests {
use crate::{
cli::TlsOptions,
middleware::as_async_middleware::as_async_middleware,
tls,
types::{
ClientFeaturesRequest, ClientFeaturesResponse, EdgeToken, TokenValidationStatus,
ValidateTokensRequest,
},
};
use actix_http::{body::MessageBody, HttpService};
use actix_http::{body::MessageBody, HttpService, TlsAcceptorConfig};
use actix_http_test::{test_server, TestServer};
use actix_middleware_etag::Etag;
use actix_service::map_config;
Expand All @@ -273,7 +289,7 @@ mod tests {
http::header::EntityTag,
web, App, HttpResponse,
};
use std::str::FromStr;
use std::{str::FromStr, time::Duration};
use unleash_types::client_features::{ClientFeature, ClientFeatures};

use super::{EdgeTokens, UnleashClient};
Expand Down Expand Up @@ -333,6 +349,35 @@ mod tests {
.await
}

async fn test_features_server_with_untrusted_ssl() -> TestServer {
test_server(move || {
let tls_options = TlsOptions {
tls_server_cert: Some("../examples/server.crt".into()),
tls_enable: true,
tls_server_key: Some("../examples/server.key".into()),
tls_server_port: 443,
};
let server_config = tls::config(tls_options).unwrap();
let tls_acceptor_config =
TlsAcceptorConfig::default().handshake_timeout(Duration::from_secs(5));
HttpService::new(map_config(
App::new()
.wrap(Etag::default())
.service(
web::resource("/api/client/features")
.route(web::get().to(return_client_features)),
)
.service(
web::resource("/edge/validate")
.route(web::post().to(return_validate_tokens)),
),
|_| AppConfig::default(),
))
.rustls_with_config(server_config, tls_acceptor_config)
})
.await
}

async fn validate_api_key_middleware(
req: ServiceRequest,
srv: crate::middleware::as_async_middleware::Next<impl MessageBody + 'static>,
Expand Down Expand Up @@ -478,4 +523,34 @@ mod tests {
.await;
assert!(authed_res.is_ok());
}

#[actix_web::test]
pub async fn disabling_ssl_verification_allows_communicating_with_upstream_unleash_with_self_signed_cert(
) {
let srv = test_features_server_with_untrusted_ssl().await;
let client = UnleashClient::new_insecure(srv.surl("/").as_str()).unwrap();

let validate_result = client
.validate_tokens(ValidateTokensRequest {
tokens: vec![TEST_TOKEN.to_string()],
})
.await;

assert!(validate_result.is_ok());
}

#[actix_web::test]
pub async fn not_disabling_ssl_verification_fails_communicating_with_upstream_unleash_with_self_signed_cert(
) {
let srv = test_features_server_with_untrusted_ssl().await;
let client = UnleashClient::new(srv.surl("/").as_str(), None).unwrap();

let validate_result = client
.validate_tokens(ValidateTokensRequest {
tokens: vec![TEST_TOKEN.to_string()],
})
.await;

assert!(validate_result.is_err());
}
}

0 comments on commit b740273

Please sign in to comment.