diff --git a/bin/nanocl/src/main.rs b/bin/nanocl/src/main.rs index 5e329123..1c16d48e 100644 --- a/bin/nanocl/src/main.rs +++ b/bin/nanocl/src/main.rs @@ -41,8 +41,11 @@ fn create_cli_config(cli_args: &Cli) -> IoResult { } let mut ssl = match &endpoint.ssl { Some(ssl) => { - let cert = std::fs::read_to_string(ssl.cert.clone().unwrap())?; - let cert_key = std::fs::read_to_string(ssl.cert_key.clone().unwrap())?; + let cert = + std::fs::read_to_string(ssl.cert.clone().expect("cert file unset"))?; + let cert_key = std::fs::read_to_string( + ssl.cert_key.clone().expect("cert key file unset"), + )?; Some(SslConfig { cert: Some(cert), cert_key: Some(cert_key), @@ -51,14 +54,16 @@ fn create_cli_config(cli_args: &Cli) -> IoResult { } None => None, }; - if let Ok(c) = std::env::var("CERT") { - if let Ok(ck) = std::env::var("CERT_KEY") { - ssl = Some(SslConfig { - cert: Some(c), - cert_key: Some(ck), - ..Default::default() - }); - } + if let Ok(cert) = std::env::var("CERT") { + let cert_key = std::env::var("CERT_KEY").ok(); + let cert_ca = std::env::var("CERT_CA").ok(); + ssl = Some(SslConfig { + cert: Some(cert), + cert_key, + cert_ca: cert_ca.clone(), + verify: cert_ca.is_some(), + ..Default::default() + }); } if let Ok(h) = std::env::var("HOST") { host = h; diff --git a/bin/nanocld/specs/swagger.yaml b/bin/nanocld/specs/swagger.yaml index 7ed8b040..05da08d2 100644 --- a/bin/nanocld/specs/swagger.yaml +++ b/bin/nanocld/specs/swagger.yaml @@ -6936,19 +6936,32 @@ components: additionalProperties: false SslConfig: type: object + required: + - Verify properties: Cert: type: - string - 'null' + description: The certificate content CertKey: type: - string - 'null' + description: The certificate key content CertCa: type: - string - 'null' + description: The certificate authority content + Verify: + type: boolean + description: Verify certificate authority + Password: + type: + - string + - 'null' + description: The certificate password if any StartExecOptions: type: object description: Exec configuration used in the [Create Exec API](Docker::create_exec()) diff --git a/bin/nanocld/src/utils/server.rs b/bin/nanocld/src/utils/server.rs index fb1c46de..0952f852 100644 --- a/bin/nanocld/src/utils/server.rs +++ b/bin/nanocld/src/utils/server.rs @@ -63,6 +63,7 @@ pub async fn gen( .unwrap(); builder.set_certificate_chain_file(cert).unwrap(); builder.set_ca_file(cert_ca).expect("Failed to set ca file"); + builder.check_private_key().unwrap(); builder.set_verify( SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT, ); diff --git a/crates/nanocl_stubs/src/system.rs b/crates/nanocl_stubs/src/system.rs index c1098222..b0ad4311 100644 --- a/crates/nanocl_stubs/src/system.rs +++ b/crates/nanocl_stubs/src/system.rs @@ -17,24 +17,37 @@ use crate::config::DaemonConfig; #[cfg_attr(feature = "serde", serde(rename_all = "PascalCase"))] #[cfg_attr(feature = "clap", derive(clap::Parser))] pub struct SslConfig { + /// The certificate content #[cfg_attr( feature = "serde", serde(skip_serializing_if = "Option::is_none") )] #[cfg_attr(feature = "clap", clap(long))] pub cert: Option, + /// The certificate key content #[cfg_attr( feature = "serde", serde(skip_serializing_if = "Option::is_none") )] #[cfg_attr(feature = "clap", clap(long))] pub cert_key: Option, + /// The certificate authority content #[cfg_attr( feature = "serde", serde(skip_serializing_if = "Option::is_none") )] #[cfg_attr(feature = "clap", clap(long))] pub cert_ca: Option, + /// Verify certificate authority + #[cfg_attr(feature = "clap", clap(long))] + pub verify: bool, + /// The certificate password if any + #[cfg_attr( + feature = "serde", + serde(skip_serializing_if = "Option::is_none") + )] + #[cfg_attr(feature = "clap", clap(long))] + pub password: Option, } #[derive(Clone, Debug, Default, Eq, PartialEq)] diff --git a/crates/nanocld_client/src/http_client.rs b/crates/nanocld_client/src/http_client.rs index c283e03a..88da5c83 100644 --- a/crates/nanocld_client/src/http_client.rs +++ b/crates/nanocld_client/src/http_client.rs @@ -123,7 +123,25 @@ impl NanocldClient { use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; if let Some(ssl) = &self.ssl { let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); - builder.set_verify(SslVerifyMode::PEER); + if ssl.verify { + builder.set_verify( + SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT, + ); + let cert_ca = openssl::x509::X509::from_pem( + ssl.cert_ca.clone().expect("Ssl.ca to be fill").as_bytes(), + ) + .expect("Invalid ssl cert ca"); + // Create an X509Store and add the certificate + let mut store_builder = openssl::x509::store::X509StoreBuilder::new() + .expect("Failed to create X509 store builder"); + store_builder + .add_cert(cert_ca) + .expect("Failed to add CA certificate to store"); + let store = store_builder.build(); + builder.set_cert_store(store); + } else { + builder.set_verify(SslVerifyMode::NONE); + } let cert = openssl::x509::X509::from_pem( ssl.cert.clone().expect("Ssl.cert to be fill").as_bytes(), ) diff --git a/docker-compose.yaml b/docker-compose.yaml index 3e428dfe..454390a7 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -177,6 +177,7 @@ services: # network_mode: host ports: - 8585:8585 + - 9443:9443 networks: - nanoclbr0 labels: