feat: add config generation
This commit is contained in:
@@ -16,3 +16,6 @@ tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-native-roots"]
|
||||
tracing = "0.1.44"
|
||||
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
|
||||
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_ext::{Box, Construct};
|
||||
use tokio_tungstenite::tungstenite;
|
||||
@@ -15,6 +17,24 @@ pub enum ErrorKind {
|
||||
WsRead(#[source] tungstenite::Error),
|
||||
#[error("error deserializing json")]
|
||||
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>;
|
||||
|
||||
@@ -1,33 +1,72 @@
|
||||
mod config;
|
||||
mod error;
|
||||
mod wireguard;
|
||||
|
||||
use console::style;
|
||||
use futures::StreamExt;
|
||||
use registry::PeerMessage;
|
||||
use registry::{Peer, PeerMessage};
|
||||
use thiserror_ext::AsReport;
|
||||
use tokio_tungstenite::tungstenite::Message;
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing_subscriber::{
|
||||
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<()> {
|
||||
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)
|
||||
.await
|
||||
.map_err(|e| Error::ws_connect(e, &url))?;
|
||||
let (_, mut read) = ws_stream.split();
|
||||
tracing::info!("connected, response: {:?}", response.status());
|
||||
|
||||
let mut peers: Vec<Peer> = Vec::new();
|
||||
|
||||
while let Some(msg) = read.next().await {
|
||||
match msg.map_err(|e| Error::ws_read(e))? {
|
||||
Message::Text(text) => {
|
||||
let server_msg: PeerMessage =
|
||||
serde_json::from_str(&text).map_err(|e| Error::deserialize_json(e))?;
|
||||
match server_msg {
|
||||
PeerMessage::HydratePeers { peers } => {}
|
||||
PeerMessage::PeerUpdate { peer } => {}
|
||||
PeerMessage::HydratePeers { peers: new_peers } => {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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