Skip to content

Commit

Permalink
test/nanocld: ssl self signed (#826)
Browse files Browse the repository at this point in the history
* test/nanocld: ssl self signed
  • Loading branch information
leon3s authored Jan 31, 2024
1 parent e3bef49 commit 2e0dfec
Show file tree
Hide file tree
Showing 27 changed files with 470 additions and 230 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This section tell which versions are currently being supported with security upd

| Version | Supported |
| ------- | ------------------ |
| 0.10.x | :white_check_mark: |
| 0.13.x | :white_check_mark: |

## Reporting a Vulnerability

Expand Down
2 changes: 1 addition & 1 deletion bin/nanocl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ dialoguer = "0.11"
termios = "0.3"
liquid = { version = "0.26", features = ["stdlib"] }
regex = "1.10"
nanocld_client = { version = "0.13", features = ["tokio"] }
nanocld_client = { version = "0.13", features = ["tokio", "openssl"] }
nanocl_error = { version = "0.2", features = [
"io",
"tokio",
Expand Down
1 change: 1 addition & 0 deletions bin/nanocl/src/commands/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ pub async fn exec_install(args: &InstallOpts) -> IoResult<()> {
"Nanocl".into(),
ContextEndpoint {
host: format!("unix://{home_dir}/.nanocl/run/nanocl.sock"),
ssl: None,
},
);
map
Expand Down
7 changes: 3 additions & 4 deletions bin/nanocl/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,9 @@ fn create_cli_config(cli_args: &Cli) -> IoResult<CliConfig> {
}
}
}
let endpoint = context.endpoints.get("Nanocl").unwrap();
#[allow(unused)]
let mut host = cli_args
.host
.clone()
.unwrap_or(context.endpoints.get("Nanocl").unwrap().host.clone());
let mut host = cli_args.host.clone().unwrap_or(endpoint.host.clone());
#[cfg(any(feature = "dev", feature = "test"))]
{
if context.name == "default" {
Expand All @@ -44,6 +42,7 @@ fn create_cli_config(cli_args: &Cli) -> IoResult<CliConfig> {
}
let client = NanocldClient::connect_to(&ConnectOpts {
url: host.clone(),
ssl: endpoint.ssl.clone(),
..Default::default()
});
Ok(CliConfig {
Expand Down
5 changes: 5 additions & 0 deletions bin/nanocl/src/models/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use tabled::Tabled;
use clap::{Parser, Subcommand};
use serde::{Serialize, Deserialize};

use nanocld_client::stubs::system::SslConfig;

/// `nanocl context` available arguments
#[derive(Parser)]
pub struct ContextArg {
Expand Down Expand Up @@ -34,6 +36,8 @@ pub enum ContextCommand {
#[serde(rename_all = "PascalCase")]
pub struct ContextEndpoint {
pub host: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub ssl: Option<SslConfig>,
}

/// A context metadata definition
Expand Down Expand Up @@ -67,6 +71,7 @@ impl std::default::Default for Context {
ContextEndpoint {
host: std::env::var("NANOCL_HOST")
.unwrap_or("unix:///run/nanocl/nanocl.sock".into()),
ssl: None,
},
);
map
Expand Down
3 changes: 2 additions & 1 deletion bin/nanocld/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ release = []
clap = { version = "4.4", features = ["derive"] }
clap_mangen = { version = "0.2" }
nanocl_utils = { version = "0.4", features = ["build_tools"] }
nanocl_stubs = { version = "0.13", features = ["clap"] }

[dev-dependencies]
serde_yaml = "0.9"
Expand Down Expand Up @@ -79,7 +80,7 @@ chrono = { version = "0.4", default-features = false, features = [
jsonschema = { version = "0.17", default-features = false }
nanocld_client = { version = "0.13", features = ["tokio"] }
metrsd_client = "0.5"
nanocl_stubs = { version = "0.13", features = ["serde"] }
nanocl_stubs = { version = "0.13", features = ["serde", "clap"] }
nanocl_utils = { version = "0.4", features = ["unix", "ntex", "logger"] }
utoipa = { version = "4.2", features = ["yaml"], optional = true }
notify = "6.1"
Expand Down
14 changes: 3 additions & 11 deletions bin/nanocld/specs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2854,17 +2854,9 @@ components:
format: int32
description: Group id
minimum: 0
cert:
type: string
description: Certificate path
nullable: true
cert_key:
type: string
description: Certificate key path
nullable: true
cert_ca:
type: string
description: Ca certificate path
ssl:
allOf:
- $ref: '#/components/schemas/SslConfig'
nullable: true
DeviceMapping:
type: object
Expand Down
18 changes: 6 additions & 12 deletions bin/nanocld/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use clap::Parser;

use nanocl_stubs::system::SslConfig;

/// Nanocl Daemon - Self Sufficient Orchestrator
#[derive(Debug, Clone, Parser)]
#[command(name = "Nanocl")]
Expand Down Expand Up @@ -34,15 +36,9 @@ pub struct Cli {
/// Group id
#[clap(long, default_value = "0")]
pub gid: u32,
/// Optional certificate path
#[clap(long)]
pub cert: Option<String>,
/// Optional certificate key path
#[clap(long)]
pub cert_key: Option<String>,
/// Optional ca certificate path
#[clap(long)]
pub cert_ca: Option<String>,
/// Optional ssl options
#[clap(flatten)]
pub ssl: Option<SslConfig>,
}

impl Default for Cli {
Expand All @@ -57,9 +53,7 @@ impl Default for Cli {
nodes: vec![],
advertise_addr: None,
gid: 0,
cert: None,
cert_key: None,
cert_ca: None,
ssl: None,
}
}
}
Expand Down
4 changes: 1 addition & 3 deletions bin/nanocld/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ fn gen_daemon_conf(
advertise_addr,
nodes: args.nodes.clone(),
conf_dir: args.conf_dir.clone(),
cert: args.cert.clone(),
cert_key: args.cert_key.clone(),
cert_ca: args.cert_ca.clone(),
ssl: args.ssl.clone(),
})
}

Expand Down
78 changes: 74 additions & 4 deletions bin/nanocld/src/utils/server.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use ntex::web;
use ntex_cors::Cors;
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode};

use nanocl_utils::ntex::middlewares;

Expand Down Expand Up @@ -50,10 +50,11 @@ pub async fn gen(
};
} else if host.starts_with("tcp://") {
let addr = host.replace("tcp://", "");
if let Some(cert) = config.cert.clone() {
if let Some(ssl) = config.ssl.clone() {
log::debug!("server::gen: {addr}: with ssl");
let cert_key = config.cert_key.clone().unwrap();
let cert_ca = config.cert_ca.clone().unwrap();
let cert = ssl.cert.clone().unwrap();
let cert_key = ssl.cert_key.clone().unwrap();
let cert_ca = ssl.cert_ca.clone().unwrap();
server = match server.bind_openssl(&addr, {
let mut builder =
SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
Expand All @@ -62,6 +63,9 @@ 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.set_verify(
SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT,
);
builder
}) {
Err(err) => {
Expand Down Expand Up @@ -108,6 +112,9 @@ pub async fn gen(
#[cfg(test)]
mod tests {
use clap::Parser;
use nanocl_stubs::system::BinaryInfo;
use ntex::http::{client::Connector, StatusCode};
use openssl::ssl::SslConnector;

use super::*;

Expand Down Expand Up @@ -180,4 +187,67 @@ mod tests {
let args = init_test_config(vec!["nanocl", "-H", "not_valid"]);
assert_config_err(args).await;
}

#[ntex::test]
async fn ssl_valid_client() {
let args = init_test_config(vec![
"nanocl",
"-H",
"tcp://0.0.0.0:6443",
"--cert",
"../../tests/server.crt",
"--cert-key",
"../../tests/server.key",
"--cert-ca",
"../../tests/ca.crt",
]);
assert_config_ok(args).await;
// Configure SSL/TLS settings
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
builder.set_verify(SslVerifyMode::NONE);
builder
.set_certificate_file("../../tests/client.crt", SslFiletype::PEM)
.unwrap();
builder
.set_private_key_file("../../tests/client.key", SslFiletype::PEM)
.unwrap();
let client = ntex::http::client::Client::build()
.connector(Connector::default().openssl(builder.build()).finish())
.finish();
let mut res = client
.get("https://0.0.0.0:6443/v0.13/version")
.send()
.await
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let version = res.json::<BinaryInfo>().await.unwrap();
assert_eq!(version.version, vars::VERSION);
}

#[ntex::test]
async fn ssl_wrong_client() {
let args = init_test_config(vec![
"nanocl",
"-H",
"tcp://0.0.0.0:4443",
"--cert",
"../../tests/server.crt",
"--cert-key",
"../../tests/server.key",
"--cert-ca",
"../../tests/ca.crt",
]);
assert_config_ok(args).await;
// Configure SSL/TLS settings
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
builder.set_verify(SslVerifyMode::NONE);
let client = ntex::http::client::Client::build()
.connector(Connector::default().openssl(builder.build()).finish())
.finish();
let res = client
.get("https://0.0.0.0:4443/v0.13/version")
.send()
.await;
assert!(res.is_err());
}
}
3 changes: 2 additions & 1 deletion bin/ncproxy/src/subsystem/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::sync::Arc;

use nanocl_error::io::IoResult;

use nanocld_client::{ConnectOpts, NanocldClient};
use nanocld_client::NanocldClient;

use crate::{
cli::Cli,
Expand All @@ -16,6 +16,7 @@ pub async fn init(cli: &Cli) -> IoResult<SystemStateRef> {
let mut client = NanocldClient::connect_with_unix_default();
#[cfg(any(feature = "dev", feature = "test"))]
{
use nanocld_client::ConnectOpts;
client = NanocldClient::connect_to(&ConnectOpts {
url: "http://nanocl.internal:8585".into(),
..Default::default()
Expand Down
2 changes: 2 additions & 0 deletions crates/nanocl_stubs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ default = ["serde"]
serde = ["dep:serde", "uuid/serde", "chrono/serde"]
utoipa = ["dep:utoipa"]
schemars = ["dep:schemars", "bollard-next/schemars"]
clap = ["dep:clap"]
test = []

[dependencies]
Expand All @@ -30,3 +31,4 @@ chrono = { version = "0.4", default-features = false, features = [
serde = { version = "1.0", features = ["derive"], optional = true }
utoipa = { version = "4", features = ["uuid", "chrono"], optional = true }
schemars = { version = "0.8", features = ["uuid1", "chrono"], optional = true }
clap = { version = "4.4", features = ["derive", "cargo"], optional = true }
14 changes: 5 additions & 9 deletions crates/nanocl_stubs/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};

use super::system::SslConfig;

/// Configuration of the daemon
/// It is used to configure the daemon
#[derive(Debug, Clone)]
Expand All @@ -27,12 +29,8 @@ pub struct DaemonConfig {
pub conf_dir: String,
/// Group id
pub gid: u32,
/// Certificate path
pub cert: Option<String>,
/// Certificate key path
pub cert_key: Option<String>,
/// Ca certificate path
pub cert_ca: Option<String>,
/// Optional ssl configuration
pub ssl: Option<SslConfig>,
}

/// Configuration File of the daemon
Expand Down Expand Up @@ -64,9 +62,7 @@ impl Default for DaemonConfig {
gateway: String::default(),
nodes: Vec::default(),
advertise_addr: String::default(),
cert: None,
cert_key: None,
cert_ca: None,
ssl: None,
}
}
}
Expand Down
27 changes: 27 additions & 0 deletions crates/nanocl_stubs/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,33 @@ use serde::{Serialize, Deserialize};

use crate::config::DaemonConfig;

#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "PascalCase"))]
#[cfg_attr(feature = "clap", derive(clap::Parser))]
pub struct SslConfig {
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "Option::is_none")
)]
#[cfg_attr(feature = "clap", clap(long))]
pub cert: Option<String>,
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "Option::is_none")
)]
#[cfg_attr(feature = "clap", clap(long))]
pub cert_key: Option<String>,
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "Option::is_none")
)]
#[cfg_attr(feature = "clap", clap(long))]
pub cert_ca: Option<String>,
}

#[derive(Clone, Debug, Default, Eq, PartialEq)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
Expand Down
Loading

0 comments on commit 2e0dfec

Please sign in to comment.