From c49591f845655b760df3b8ec704b4c63dd18062c Mon Sep 17 00:00:00 2001 From: Yamzik Date: Sun, 18 Dec 2022 18:57:01 +0300 Subject: [PATCH] Refactor config --- Application.Deploy.Dockerfile | 3 +- data/wg0.conf | 19 ++++++ data/wg0.json | 20 ++++++ src/utils/os.rs | 50 ++++++++------- src/wg_rs/config.rs | 58 +++++++++-------- src/wg_rs/wireguard.rs | 113 ++++++++++++++++++++-------------- 6 files changed, 170 insertions(+), 93 deletions(-) create mode 100644 data/wg0.conf create mode 100644 data/wg0.json diff --git a/Application.Deploy.Dockerfile b/Application.Deploy.Dockerfile index db11ed1f..3d8dabe5 100644 --- a/Application.Deploy.Dockerfile +++ b/Application.Deploy.Dockerfile @@ -3,7 +3,8 @@ RUN apk add -U --no-cache wireguard-tools dumb-init FROM alpine_wg AS runtime COPY application/bin/rs-wg /usr/local/cargo/bin/rs-wg -COPY data/wg /etc/wireguard +# COPY data/wireguard /etc/wireguard +CMD [ "mkdir", "/etc/wireguard" ] COPY data/www /etc/www WORKDIR /etc/wireguard EXPOSE 8080 \ No newline at end of file diff --git a/data/wg0.conf b/data/wg0.conf new file mode 100644 index 00000000..2d546ed4 --- /dev/null +++ b/data/wg0.conf @@ -0,0 +1,19 @@ +# Note: Do not edit this file directly. +# Your changes will be overwritten! + +# Server +[Interface] +PrivateKey = 7djcxyZBiUQ6OrB36p1cKLGHl1zg5MqQShN9Udl41Hw= +Address = 10.8.0.1/24 +ListenPort = 51820 +PreUp = +PostUp = iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE; iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; +PreDown = +PostDown = + +# Client: 321 (184a9b70-03b3-4bfb-ae89-ce463634f394) +[Peer] +PublicKey = ej10pb+XPRVuotGrkz+423Lku6Jyf5OHqt0ccjx6KU8= +PresharedKey = Y/5LvXKegqRoeugphZZOLS1/Kl+wN+HdJoCdulxzrEs= +AllowedIPs = 10.8.0.2/32 + diff --git a/data/wg0.json b/data/wg0.json new file mode 100644 index 00000000..ca7bbf7f --- /dev/null +++ b/data/wg0.json @@ -0,0 +1,20 @@ +{ + "server": { + "privateKey": "7djcxyZBiUQ6OrB36p1cKLGHl1zg5MqQShN9Udl41Hw=", + "publicKey": "kAW7Jefvgqs5H6hyuF05a1vSZJJdAiedg3AnVdgjFVk=", + "address": "10.8.0.1" + }, + "clients": { + "184a9b70-03b3-4bfb-ae89-ce463634f394": { + "uuid": "184a9b70-03b3-4bfb-ae89-ce463634f394", + "name": "321", + "address": "10.8.0.2", + "privateKey": "+K2Bi7AYB1pUCWqIPdRIpqnGFOjG0PE7W8pOBLsatXo=", + "publicKey": "ej10pb+XPRVuotGrkz+423Lku6Jyf5OHqt0ccjx6KU8=", + "preSharedKey": "Y/5LvXKegqRoeugphZZOLS1/Kl+wN+HdJoCdulxzrEs=", + "createdAt": "2022-12-18T15:55:49Z", + "updatedAt": "2022-12-18T15:55:49Z", + "enabled": true + } + } +} diff --git a/src/utils/os.rs b/src/utils/os.rs index 0df42ff6..ac119057 100644 --- a/src/utils/os.rs +++ b/src/utils/os.rs @@ -1,4 +1,4 @@ -use std::string::FromUtf8Error; +use std::{error::Error, string::FromUtf8Error}; use futures_util::TryFutureExt; use log::{info, warn}; @@ -9,6 +9,20 @@ use tokio::{ process::Command, }; +// pub struct Error { +// error: T, +// } +// impl Error { +// pub fn new(error: T) -> Self { +// Self { error } +// } +// } +// impl ToString for Error { +// fn to_string(&self) -> String { +// self.error.to_string() +// } +// } + #[derive(Serialize)] pub struct StdResult { pub stdout: String, @@ -31,22 +45,20 @@ pub async fn write_file, D: Into>( data: D, ) -> Result<(), std::io::Error> { tokio::fs::OpenOptions::new() + .create(true) .write(true) .truncate(true) - .create(true) .open(path.into()) - .and_then(|mut file| async move { - let result = file.write(data.into().as_bytes()).await; - file.flush().await - }) + .and_then(|mut file| async move { file.write(data.into().as_bytes()).await.map(|_| file) }) + .and_then(|mut file| async move { file.flush().await }) .await } pub async fn read_file>(path: S) -> Result { tokio::fs::OpenOptions::new() - .read(true) - .write(true) .create(true) + .write(true) + .read(true) .open(path.into()) .and_then(|mut file| async move { let mut dst = String::new(); @@ -84,27 +96,23 @@ pub async fn load_and_read_file_unhandled(path: &str) -> (File, String) { } } -pub async fn exec_sh>(command: S) -> Result { - let command = command.into(); +pub async fn exec_sh(command: &S) -> Result> { + let command = command.to_string(); info!("$ {}", command.clone()); let res = match Command::new("sh").arg("-c").arg(command).output().await { Ok(val) => val, - Err(err) => return Err(err.to_string()), + Err(err) => return Err(Box::new(err)), }; let stdout_b = &res.stdout.to_vec(); let stderr_b = &res.stderr.to_vec(); - let stdout = match remove_escape(stdout_b) { - Ok(val) => val, - Err(err) => return Err(err.to_string()), - }; - let stderr = match remove_escape(stderr_b) { - Ok(val) => val, - Err(err) => return Err(err.to_string()), - }; - - Ok(StdResult { stdout, stderr }) + match remove_escape(stdout_b) + .and_then(|stdout| remove_escape(stderr_b).map(|stderr| (stdout, stderr))) + { + Ok((stdout, stderr)) => Ok(StdResult { stdout, stderr }), + Err(err) => return Err(Box::new(err)), + } } pub fn remove_escape(bytes: &Vec) -> Result { diff --git a/src/wg_rs/config.rs b/src/wg_rs/config.rs index b657e389..b57c74a2 100644 --- a/src/wg_rs/config.rs +++ b/src/wg_rs/config.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, sync::Arc}; +use std::{collections::HashMap, error::Error, sync::Arc}; use chrono::{SecondsFormat, SubsecRound, Utc}; @@ -6,7 +6,7 @@ use futures_util::TryFutureExt; use log::error; use serde::{Deserialize, Serialize}; -use tokio::{io::AsyncWriteExt, join}; +use tokio::join; use uuid::Uuid; use crate::utils::{misc, os}; @@ -128,37 +128,45 @@ pub struct Accessor { } impl Accessor { - pub async fn new(memento_path: String, config_path: String, settings: Arc) -> Self { + pub async fn build( + memento_path: String, + config_path: String, + settings: Arc, + ) -> Result> { let memento_str = os::read_file(memento_path.clone()) .await .expect("Could not read memento"); - let memento = if memento_str.is_empty() { + if memento_str.is_empty() { log::info!("Creating new WireGuard configuration..."); - let private_key = os::exec_sh("wg genkey").await.expect("error genkey").stdout; - let public_key = os::exec_sh(format!("echo {} | wg pubkey", private_key)) + os::exec_sh(&"wg genkey") + .and_then(|res_public| async move { + os::exec_sh(&format!("echo {} | wg pubkey", &res_public.stdout)) + .await + .map(|res_private| (res_public.stdout, res_private.stdout)) + }) .await - .expect("error pubkey") - .stdout; - - let address = settings.default_address.clone(); - let memento = Memento::new(Server { - private_key, - public_key, - address, - }); - log::info!("WireGuard configuration generated..."); - memento + .and_then(|(public_key, private_key)| { + let address = settings.default_address.clone(); + let memento = Memento::new(Server { + private_key, + public_key, + address, + }); + log::info!("WireGuard configuration generated..."); + Ok(memento) + }) } else { - serde_json::from_str(&memento_str).expect("Could not serialize memento") - }; - - Self { - config_path, - memento_path, - memento, - settings, + Ok(serde_json::from_str(&memento_str).expect("Could not serialize memento")) } + .and_then(|memento| { + Ok(Self { + config_path, + memento_path, + memento, + settings, + }) + }) } pub async fn set(&mut self, memento: Memento) { diff --git a/src/wg_rs/wireguard.rs b/src/wg_rs/wireguard.rs index f5a7643c..eac22f42 100644 --- a/src/wg_rs/wireguard.rs +++ b/src/wg_rs/wireguard.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use chrono::{SecondsFormat, SubsecRound, Utc}; +use log::error; use uuid::Uuid; use crate::utils::{misc, os}; @@ -27,32 +28,46 @@ impl WireGuard { Self { name, settings: counter.clone(), - memento_accessor: Accessor::new(memento_path, config_path, counter).await, + memento_accessor: Accessor::build(memento_path, config_path, counter) + .await + .unwrap_or_else(|err| { + error!("Error initializing Wireguard: {}", err); + panic!() + }), } } pub async fn quick_up(&self) { - let result = os::exec_sh(format!("wg-quick up {}", &self.name)) + os::exec_sh(&format!("wg-quick up {}", &self.name)) .await - .expect("Error"); - result.log(); + .unwrap_or_else(|err| { + error!("Error quick_up: {}", err); + panic!() + }) + .log(); } pub async fn quick_down(&self) { - let result = os::exec_sh(format!("wg-quick down {}", &self.name)) + os::exec_sh(&format!("wg-quick down {}", &self.name)) .await - .expect("Error"); - result.log(); + .unwrap_or_else(|err| { + error!("Error quick_down: {}", err); + panic!() + }) + .log(); } pub async fn sync_config(&self) { - let result = os::exec_sh(format!( + os::exec_sh(&format!( "wg syncconf {} <(wg-quick strip {})", self.name, self.name )) .await - .expect("Error syncing"); - result.log(); + .unwrap_or_else(|err| { + error!("Error syncing: {}", err); + panic!() + }) + .log(); } pub async fn start(&mut self) { @@ -64,12 +79,18 @@ impl WireGuard { } pub async fn create_client(&mut self, name: String) -> Client { - let private_key = os::exec_sh("wg genkey").await.expect("error genkey").stdout; + let private_key = os::exec_sh(&"wg genkey") + .await + .expect("error genkey") + .stdout; let pub_cmd = format!("echo {} | wg pubkey", private_key); - let public_key = os::exec_sh(pub_cmd).await.expect("error pubkey").stdout; + let public_key = os::exec_sh(&pub_cmd).await.expect("error pubkey").stdout; - let pre_shared_key = os::exec_sh("wg genpsk").await.expect("error genpsk").stdout; + let pre_shared_key = os::exec_sh(&"wg genpsk") + .await + .expect("error genpsk") + .stdout; let mut memento = self.memento_accessor.get(); @@ -122,29 +143,29 @@ impl WireGuard { } pub async fn get_clients(&self) -> Vec { - // let res = os::exec_sh("wg show wg0 dump").await.unwrap(); - - // let lines: Vec> = res - // .stdout - // .trim() - // .split('\n') - // .skip(1) - // .map(|line| line.split('\t').map(|x| x.to_string()).collect()) - // .collect(); - - // let lines: Vec = lines - // .iter() - // .map(|x| DumpClient { - // public_key: x[0].to_string(), - // pre_shared_key: x[1].to_string(), - // endpoint: x[2].to_string(), - // allowed_ips: x[3].to_string(), - // latest_handshake_at: x[4].to_string(), - // transfer_rx: x[5].parse::().unwrap(), - // transfer_tx: x[6].parse::().unwrap(), - // persistent_keepalive: x[7].to_string(), - // }) - // .collect(); + let res = os::exec_sh(&"wg show wg0 dump").await.unwrap(); + + let lines: Vec> = res + .stdout + .trim() + .split('\n') + .skip(1) + .map(|line| line.split('\t').map(|x| x.to_string()).collect()) + .collect(); + + let lines: Vec = lines + .iter() + .map(|x| DumpClient { + public_key: x[0].to_string(), + pre_shared_key: x[1].to_string(), + endpoint: x[2].to_string(), + allowed_ips: x[3].to_string(), + latest_handshake_at: x[4].to_string(), + transfer_rx: x[5].parse::().unwrap(), + transfer_tx: x[6].parse::().unwrap(), + persistent_keepalive: x[7].to_string(), + }) + .collect(); let memento = self.memento_accessor.get(); let clients: Vec = memento @@ -153,12 +174,12 @@ impl WireGuard { .map(|(uuid, value)| { let uuid = uuid.to_owned(); let value = value.to_owned(); - // let client_conf; + let client_conf; - // match lines.iter().find(|x| x.public_key.eq(&value.public_key)) { - // Some(conf) => client_conf = conf, - // None => return None, - // }; + match lines.iter().find(|x| x.public_key.eq(&value.public_key)) { + Some(conf) => client_conf = conf, + None => return None, + }; Some(WebClient { id: uuid, name: value.name, @@ -167,11 +188,11 @@ impl WireGuard { public_key: value.public_key, created_at: value.created_at, updated_at: value.updated_at, - allowed_ips: "123".to_string(), //client_conf.allowed_ips.to_string(), - persistent_keepalive: "123".to_string(), //client_conf.persistent_keepalive.to_string(), - latest_handshake_at: "123".to_string(), //client_conf.latest_handshake_at.to_string(), - transfer_rx: 1234, //client_conf.transfer_rx, - transfer_tx: 1234, //client_conf.transfer_tx, + allowed_ips: client_conf.allowed_ips.to_string(), + persistent_keepalive: client_conf.persistent_keepalive.to_string(), + latest_handshake_at: client_conf.latest_handshake_at.to_string(), + transfer_rx: client_conf.transfer_rx, + transfer_tx: client_conf.transfer_tx, }) }) .filter(|x| x.is_some())