feat: add config generation
This commit is contained in:
105
Cargo.lock
generated
105
Cargo.lock
generated
@@ -367,6 +367,7 @@ name = "client"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"console",
|
"console",
|
||||||
|
"dirs",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"registry",
|
"registry",
|
||||||
@@ -376,8 +377,10 @@ dependencies = [
|
|||||||
"thiserror-ext",
|
"thiserror-ext",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite",
|
"tokio-tungstenite",
|
||||||
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -528,6 +531,27 @@ dependencies = [
|
|||||||
"crypto-common",
|
"crypto-common",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"option-ext",
|
||||||
|
"redox_users",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
@@ -982,6 +1006,16 @@ version = "0.2.181"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "459427e2af2b9c839b132acb702a1c654d95e10f8c326bfc2ad11310e458b1c5"
|
checksum = "459427e2af2b9c839b132acb702a1c654d95e10f8c326bfc2ad11310e458b1c5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libredox"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "litemap"
|
name = "litemap"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
@@ -1124,6 +1158,12 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
|
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "option-ext"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.5"
|
version = "0.12.5"
|
||||||
@@ -1304,6 +1344,17 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_users"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.17",
|
||||||
|
"libredox",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.12.3"
|
version = "1.12.3"
|
||||||
@@ -1532,6 +1583,15 @@ dependencies = [
|
|||||||
"zmij",
|
"zmij",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_urlencoded"
|
name = "serde_urlencoded"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -1817,6 +1877,45 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "1.0.1+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbe30f93627849fa362d4a602212d41bb237dc2bd0f8ba0b2ce785012e124220"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde_core",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_parser",
|
||||||
|
"toml_writer",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "1.0.0+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_parser"
|
||||||
|
version = "1.0.8+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0742ff5ff03ea7e67c8ae6c93cac239e0d9784833362da3f9a9c1da8dfefcbdc"
|
||||||
|
dependencies = [
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_writer"
|
||||||
|
version = "1.0.6+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.44"
|
version = "0.1.44"
|
||||||
@@ -2216,6 +2315,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wit-bindgen"
|
name = "wit-bindgen"
|
||||||
version = "0.51.0"
|
version = "0.51.0"
|
||||||
|
|||||||
@@ -16,3 +16,6 @@ tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-native-roots"]
|
|||||||
tracing = "0.1.44"
|
tracing = "0.1.44"
|
||||||
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
|
||||||
registry = { path = "../registry" }
|
registry = { path = "../registry" }
|
||||||
|
dirs = "6.0.0"
|
||||||
|
toml = "1.0.1"
|
||||||
|
url = "2.5.8"
|
||||||
|
|||||||
38
client/src/config.rs
Normal file
38
client/src/config.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::error::{Error, Result};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
pub interface: InterfaceConfig,
|
||||||
|
pub server: ServerConfig,
|
||||||
|
}
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct InterfaceConfig {
|
||||||
|
pub private_key: String,
|
||||||
|
pub listen_port: u16,
|
||||||
|
pub address: String,
|
||||||
|
}
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ServerConfig {
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn load() -> Result<Self> {
|
||||||
|
let path = base_path().join("config.toml");
|
||||||
|
let bytes = std::fs::read(&path).map_err(|e| Error::read_config(e, &path))?;
|
||||||
|
Ok(toml::from_slice(&bytes)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_path() -> PathBuf {
|
||||||
|
dirs::config_dir()
|
||||||
|
.unwrap_or_else(|| PathBuf::from("."))
|
||||||
|
.join("wg-mesh")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wg_config_path() -> PathBuf {
|
||||||
|
PathBuf::from("/etc/wireguard/mesh0.conf")
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use thiserror_ext::{Box, Construct};
|
use thiserror_ext::{Box, Construct};
|
||||||
use tokio_tungstenite::tungstenite;
|
use tokio_tungstenite::tungstenite;
|
||||||
@@ -15,6 +17,24 @@ pub enum ErrorKind {
|
|||||||
WsRead(#[source] tungstenite::Error),
|
WsRead(#[source] tungstenite::Error),
|
||||||
#[error("error deserializing json")]
|
#[error("error deserializing json")]
|
||||||
DeserializeJson(#[source] serde_json::Error),
|
DeserializeJson(#[source] serde_json::Error),
|
||||||
|
#[error("error reading configuration at {path}")]
|
||||||
|
ReadConfig {
|
||||||
|
path: PathBuf,
|
||||||
|
#[source]
|
||||||
|
source: std::io::Error,
|
||||||
|
},
|
||||||
|
#[error("error deserializing toml")]
|
||||||
|
DeserializeToml(#[from] toml::de::Error),
|
||||||
|
#[error("invalid url")]
|
||||||
|
Url(#[from] url::ParseError),
|
||||||
|
#[error("invalid url scheme: {url}")]
|
||||||
|
UrlScheme { url: String },
|
||||||
|
#[error("error writing wireguard config to {path}")]
|
||||||
|
WriteConfig {
|
||||||
|
path: PathBuf,
|
||||||
|
#[source]
|
||||||
|
source: std::io::Error,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = core::result::Result<T, Error>;
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
|||||||
@@ -1,33 +1,72 @@
|
|||||||
|
mod config;
|
||||||
|
mod error;
|
||||||
|
mod wireguard;
|
||||||
|
|
||||||
use console::style;
|
use console::style;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use registry::PeerMessage;
|
use registry::{Peer, PeerMessage};
|
||||||
use thiserror_ext::AsReport;
|
use thiserror_ext::AsReport;
|
||||||
use tokio_tungstenite::tungstenite::Message;
|
use tokio_tungstenite::tungstenite::Message;
|
||||||
use tracing::level_filters::LevelFilter;
|
use tracing::level_filters::LevelFilter;
|
||||||
use tracing_subscriber::{
|
use tracing_subscriber::{
|
||||||
EnvFilter, fmt::format::FmtSpan, layer::SubscriberExt, util::SubscriberInitExt,
|
EnvFilter, fmt::format::FmtSpan, layer::SubscriberExt, util::SubscriberInitExt,
|
||||||
};
|
};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::{
|
||||||
|
config::{Config, InterfaceConfig, wg_config_path},
|
||||||
|
error::{Error, Result},
|
||||||
|
};
|
||||||
|
|
||||||
mod error;
|
fn parse_url(input: &str) -> Result<String> {
|
||||||
|
let url = Url::parse(&input)?.join("/ws/peers")?;
|
||||||
|
if url.scheme() != "ws" && url.scheme() != "wss" {
|
||||||
|
return Err(Error::url_scheme(url.to_string()));
|
||||||
|
}
|
||||||
|
Ok(url.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_wg_config(interface: &InterfaceConfig, peers: &[Peer]) -> Result<()> {
|
||||||
|
let path = wg_config_path();
|
||||||
|
let config = wireguard::generate_config(interface, peers);
|
||||||
|
std::fs::write(&path, config).map_err(|e| Error::write_config(e, &path))?;
|
||||||
|
tracing::info!("wrote {} with {} peers", path.display(), peers.len());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn run() -> crate::error::Result<()> {
|
async fn run() -> crate::error::Result<()> {
|
||||||
let url = get_api_from_env();
|
let config = Config::load()?;
|
||||||
|
let url = parse_url(&config.server.url)?;
|
||||||
let (ws_stream, response) = tokio_tungstenite::connect_async(&url)
|
let (ws_stream, response) = tokio_tungstenite::connect_async(&url)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Error::ws_connect(e, &url))?;
|
.map_err(|e| Error::ws_connect(e, &url))?;
|
||||||
let (_, mut read) = ws_stream.split();
|
let (_, mut read) = ws_stream.split();
|
||||||
tracing::info!("connected, response: {:?}", response.status());
|
tracing::info!("connected, response: {:?}", response.status());
|
||||||
|
|
||||||
|
let mut peers: Vec<Peer> = Vec::new();
|
||||||
|
|
||||||
while let Some(msg) = read.next().await {
|
while let Some(msg) = read.next().await {
|
||||||
match msg.map_err(|e| Error::ws_read(e))? {
|
match msg.map_err(|e| Error::ws_read(e))? {
|
||||||
Message::Text(text) => {
|
Message::Text(text) => {
|
||||||
let server_msg: PeerMessage =
|
let server_msg: PeerMessage =
|
||||||
serde_json::from_str(&text).map_err(|e| Error::deserialize_json(e))?;
|
serde_json::from_str(&text).map_err(|e| Error::deserialize_json(e))?;
|
||||||
match server_msg {
|
match server_msg {
|
||||||
PeerMessage::HydratePeers { peers } => {}
|
PeerMessage::HydratePeers { peers: new_peers } => {
|
||||||
PeerMessage::PeerUpdate { peer } => {}
|
tracing::info!("received {} peers", new_peers.len());
|
||||||
|
peers = new_peers;
|
||||||
|
write_wg_config(&config.interface, &peers)?;
|
||||||
|
}
|
||||||
|
PeerMessage::PeerUpdate { peer } => {
|
||||||
|
tracing::info!("peer update: {}", peer.public_key);
|
||||||
|
if let Some(existing) =
|
||||||
|
peers.iter_mut().find(|p| p.public_key == peer.public_key)
|
||||||
|
{
|
||||||
|
*existing = peer;
|
||||||
|
} else {
|
||||||
|
peers.push(peer);
|
||||||
|
}
|
||||||
|
write_wg_config(&config.interface, &peers)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -57,7 +96,3 @@ async fn main() {
|
|||||||
std::process::exit(1)
|
std::process::exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_api_from_env() -> String {
|
|
||||||
return "ws://localhost:8080/ws/peers".to_string();
|
|
||||||
}
|
|
||||||
|
|||||||
24
client/src/wireguard.rs
Normal file
24
client/src/wireguard.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
use registry::Peer;
|
||||||
|
|
||||||
|
use crate::config::InterfaceConfig;
|
||||||
|
|
||||||
|
pub fn generate_config(interface: &InterfaceConfig, peers: &[Peer]) -> String {
|
||||||
|
let mut config = format!(
|
||||||
|
"[Interface]\nPrivateKey = {}\nListenPort = {}\nAddress = {}\n",
|
||||||
|
interface.private_key, interface.listen_port, interface.address,
|
||||||
|
);
|
||||||
|
for peer in peers {
|
||||||
|
config.push_str(&format!(
|
||||||
|
"\n[Peer]\nPublicKey = {}\nEndpoint = {}:{}\nAllowedIPs = {}\n",
|
||||||
|
peer.public_key,
|
||||||
|
peer.public_ip,
|
||||||
|
peer.port,
|
||||||
|
peer.allowed_ips
|
||||||
|
.iter()
|
||||||
|
.map(|ip| ip.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", "),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
config
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user