Skip to content

Commit

Permalink
feature/nanocld: store connection with SSL/TLS (#628)
Browse files Browse the repository at this point in the history
* feature/nanocld: store connection with SSL/TLS

* refactor/workflows/tests: remove --init usage for nanocld
  • Loading branch information
leon3s authored Nov 6, 2023
1 parent 8bf2dea commit 24b0790
Show file tree
Hide file tree
Showing 12 changed files with 166 additions and 154 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ jobs:
cargo build --no-default-features --features test --bin ncproxy
cargo build --no-default-features --features test --bin nanocld
docker compose -f ./tests/docker-compose.yaml up -d
cargo run --no-default-features --features test --bin nanocld -- --init
cargo run --no-default-features --features test --bin nanocld -- --hosts tcp://0.0.0.0:8585 &
sleep 4
sudo chown $USER:$USER -R ~/.nanocl_dev
cargo run --no-default-features --features test --bin nanocld -- --hosts tcp://0.0.0.0:8585 --state-dir ~/.nanocl_dev/state &
sleep 8
sudo mkdir -p /etc/nginx
sudo chmod 777 -R /run/nanocl
Expand Down
7 changes: 6 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,19 @@ Now you can run the CLI:
## 🧪 Testing

To run tests, make sure all `Nanocl` services are running with `docker compose up`.<br/>
Then be sure to have correct permission set on `/run/nanocl`<br />
You also need theses entries in your `/etc/hosts`

```
127.0.0.1 nstore.nanocl.internal
127.0.0.1 ndaemon.nanocl.internal
```
Then you need to chown the state directory to be able to read SSL/TLS certificate to connect to the database
```sh
sudo chown $USER:$USER -R ~/.nanocl_dev
```

- Run all tests

```sh
Expand Down
86 changes: 83 additions & 3 deletions bin/nanocld/src/boot.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use std::path::Path;
use std::os::unix::prelude::PermissionsExt;

use ntex::rt;
use tokio::fs;
use nanocl_error::io::{FromIo, IoResult};

Expand All @@ -8,6 +12,77 @@ use crate::models::DaemonState;

use crate::version::VERSION;

use notify::{Config, Watcher, RecursiveMode, RecommendedWatcher};

/// ## Set unix permission
///
/// Watch for change in the run directory and set the permission of the unix socket
///
fn set_unix_sock_perm() {
rt::Arbiter::new().exec_fn(|| {
rt::spawn(async {
log::debug!("set_unix_permission");
let path = Path::new("/run/nanocl");
if !path.exists() {
log::debug!(
"{} doesn't exists cannot change unix socket permission",
path.display()
);
return;
}
let (tx, rx) = std::sync::mpsc::channel();
// Automatically select the best implementation for your platform.
// You can also access each implementation directly e.g. INotifyWatcher.
let mut watcher = match RecommendedWatcher::new(tx, Config::default()) {
Ok(watcher) => watcher,
Err(e) => {
log::warn!("watcher error: {:?}", e);
return;
}
};
// Add a path to be watched. All files and directories at that path and
// below will be monitored for changes.
watcher.watch(path, RecursiveMode::Recursive).unwrap();
log::debug!("watching change of: {}", path.display());
for res in rx {
match res {
Ok(event) => {
log::debug!("event: {:?}", event);
if event.kind.is_modify()
|| event.kind.is_create()
|| event.kind.is_access()
|| event.kind.is_other()
{
log::debug!(
"change detected, change permission of /run/nanocl/nanocl.sock",
);
let mut perms =
match fs::metadata("/run/nanocl/nanocl.sock").await {
Err(_) => {
continue;
}
Ok(perms) => perms.permissions(),
};
perms.set_mode(0o770);
if let Err(err) =
fs::set_permissions("/run/nanocl/nanocl.sock", perms).await
{
log::warn!("set_unix_permission error: {err:?}");
}
break;
}
}
Err(err) => {
log::warn!("watch error: {err:?}");
break;
}
}
}
log::debug!("set_unix_permission done");
});
});
}

/// ## Ensure state dir
///
/// Ensure that the state dir exists and is ready to use
Expand Down Expand Up @@ -44,6 +119,7 @@ async fn ensure_state_dir(state_dir: &str) -> IoResult<()> {
/// * [Err](IoError) - The daemon state has not been initialized
///
pub async fn init(daemon_conf: &DaemonConfig) -> IoResult<DaemonState> {
set_unix_sock_perm();
let docker = bollard_next::Docker::connect_with_unix(
&daemon_conf.docker_host,
120,
Expand All @@ -53,7 +129,7 @@ pub async fn init(daemon_conf: &DaemonConfig) -> IoResult<DaemonState> {
err.map_err_context(|| "Unable to connect to docker daemon")
})?;
ensure_state_dir(&daemon_conf.state_dir).await?;
let pool = utils::store::init().await?;
let pool = utils::store::init(daemon_conf).await?;
let daemon_state = DaemonState {
pool: pool.clone(),
docker_api: docker.clone(),
Expand All @@ -73,26 +149,30 @@ pub async fn init(daemon_conf: &DaemonConfig) -> IoResult<DaemonState> {
mod tests {
use super::*;

use crate::utils::tests::*;
use crate::config;
use crate::cli::Cli;

/// Test init
#[ntex::test]
async fn basic_init() {
// Init cli args
before();
let home = std::env::var("HOME").expect("Failed to get home dir");
let args = Cli {
gid: 0,
init: false,
hosts: None,
docker_host: None,
state_dir: Some(String::from("/tmp/nanocl")),
state_dir: Some(format!("{home}/.nanocl_dev/state")),
conf_dir: String::from("/etc/nanocl"),
gateway: None,
nodes: Vec::default(),
hostname: None,
advertise_addr: None,
};
log::debug!("args: {args:?}");
let config = config::init(&args).expect("Expect to init config");
log::debug!("config: {config:?}");
// test function init
let _ = init(&config).await.unwrap();
}
Expand Down
5 changes: 0 additions & 5 deletions bin/nanocld/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ use clap::Parser;
#[command(author = "nexthat team <[email protected]>")]
#[command(version)]
pub struct Cli {
/// Ensure state is inited
#[clap(long)]
pub(crate) init: bool,
/// Hosts to listen to use tcp:// and unix:// [default: unix:///run/nanocl.sock]
#[clap(short = 'H', long = "hosts")]
pub(crate) hosts: Option<Vec<String>>,
Expand Down Expand Up @@ -49,7 +46,6 @@ mod tests {
fn cli_with_default() {
let args = Cli::parse_from(["nanocl"]);
assert_eq!(args.hosts, None);
assert!(!args.init);
assert_eq!(args.docker_host, None);
assert_eq!(args.state_dir, None);
assert_eq!(args.conf_dir, String::from("/etc/nanocl"));
Expand All @@ -73,7 +69,6 @@ mod tests {
args.hosts,
Some(vec![String::from("unix:///run/nanocl.sock")])
);
assert!(!args.init);
assert_eq!(args.docker_host, Some(String::from("/var/run/docker.sock")));
assert_eq!(args.state_dir, Some(String::from("/var/lib/nanocl")));
assert_eq!(args.conf_dir, String::from("/etc/nanocl"));
Expand Down
2 changes: 0 additions & 2 deletions bin/nanocld/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ mod tests {
state_dir: Some(String::from("/var/lib/nanocl")),
docker_host: Some(String::from("/var/run/docker.sock")),
conf_dir: String::from("/etc/nanocl"),
init: false,
gateway: None,
hostname: None,
advertise_addr: None,
Expand Down Expand Up @@ -223,7 +222,6 @@ mod tests {
state_dir: Some(String::from("/var/lib/nanocl")),
docker_host: Some(String::from("/var/run/docker.sock")),
conf_dir: String::from("/etc/nanocl"),
init: false,
gateway: None,
advertise_addr: None,
hostname: None,
Expand Down
94 changes: 2 additions & 92 deletions bin/nanocld/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,106 +1,21 @@
#[macro_use]
extern crate diesel;

use std::fs;
use std::path::Path;
use std::os::unix::prelude::PermissionsExt;

use ntex::rt;
use clap::Parser;

mod cli;
mod schema;
mod models;
mod version;
mod node;
mod boot;
mod utils;
mod event;
mod schema;
mod models;
mod config;
mod server;
mod services;
mod repositories;

use notify::{Config, Watcher, RecursiveMode, RecommendedWatcher};

/// ## Set unix permission
///
/// Watch for change in the run directory and set the permission of the unix socket
///
async fn set_unix_permission() {
rt::Arbiter::new().exec_fn(|| {
log::debug!("set_unix_permission");
let path = Path::new("/run/nanocl");
if !path.exists() {
log::debug!(
"{} doesn't exists cannot change unix socket permission",
path.display()
);
return;
}
let (tx, rx) = std::sync::mpsc::channel();
// Automatically select the best implementation for your platform.
// You can also access each implementation directly e.g. INotifyWatcher.
let mut watcher = match RecommendedWatcher::new(tx, Config::default()) {
Ok(watcher) => watcher,
Err(e) => {
log::warn!("watcher error: {:?}", e);
return;
}
};
// Add a path to be watched. All files and directories at that path and
// below will be monitored for changes.
watcher.watch(path, RecursiveMode::Recursive).unwrap();
log::debug!("watching change of: {}", path.display());
for res in rx {
match res {
Ok(event) => {
log::debug!("event: {:?}", event);
if event.kind.is_modify()
|| event.kind.is_create()
|| event.kind.is_access()
|| event.kind.is_other()
{
log::debug!(
"change detected, change permission of /run/nanocl/nanocl.sock",
);
let mut perms = match fs::metadata("/run/nanocl/nanocl.sock") {
Err(_) => {
continue;
}
Ok(perms) => perms.permissions(),
};
#[cfg(feature = "dev")]
{
perms.set_mode(0o777);
if let Err(err) =
fs::set_permissions("/run/nanocl/nanocl.sock", perms)
{
log::warn!("set_unix_permission error: {err:?}");
}
}
#[cfg(not(feature = "dev"))]
{
perms.set_mode(0o770);
if let Err(err) =
fs::set_permissions("/run/nanocl/nanocl.sock", perms)
{
log::warn!("set_unix_permission error: {err:?}");
}
}
break;
}
}
Err(err) => {
log::warn!("watch error: {err:?}");
break;
}
}
}
log::debug!("set_unix_permission done");
});
}

/// ## The Nanocl daemon
///
/// Provides an api to manage containers and virtual machines accross physical hosts
Expand Down Expand Up @@ -137,15 +52,10 @@ async fn main() -> std::io::Result<()> {
};
// Boot and init internal dependencies
let daemon_state = boot::init(&config).await?;
// If init is true we don't start the server
if args.init {
return Ok(());
}
if let Err(err) = node::join_cluster(&daemon_state).await {
log::error!("{err}");
std::process::exit(1);
}
set_unix_permission().await;
node::register(&daemon_state).await?;
utils::proxy::spawn_logger(&daemon_state);
utils::metric::spawn_logger(&daemon_state);
Expand Down
Loading

0 comments on commit 24b0790

Please sign in to comment.