146 lines
3.4 KiB
Rust
146 lines
3.4 KiB
Rust
use async_fs::File;
|
|
use async_process::{Command, Stdio};
|
|
use futures_lite::AsyncWriteExt;
|
|
use std::{net::Ipv4Addr, path::PathBuf};
|
|
|
|
#[derive(serde::Deserialize)]
|
|
pub(crate) struct NewInterface {
|
|
name: String,
|
|
address: Ipv4Addr,
|
|
port: u16,
|
|
}
|
|
|
|
#[derive(serde::Deserialize, serde::Serialize)]
|
|
pub(crate) struct Interface {
|
|
name: String,
|
|
address: Ipv4Addr,
|
|
port: u16,
|
|
private_key: String,
|
|
public_key: String,
|
|
}
|
|
|
|
#[derive(serde::Deserialize, serde::Serialize)]
|
|
pub(crate) struct Peer {
|
|
public_key: String,
|
|
address: Ipv4Addr,
|
|
}
|
|
|
|
impl Interface {
|
|
fn config(&self) -> String {
|
|
format!(
|
|
r#"[Interface]
|
|
PrivateKey = {private_key}
|
|
Address = {address}/24
|
|
ListenPort = {port}
|
|
SaveConfig = true
|
|
"#,
|
|
private_key = self.private_key,
|
|
address = self.address,
|
|
port = self.port,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl Peer {
|
|
fn config(&self) -> String {
|
|
format!(
|
|
r#"[Peer]
|
|
PublicKey = {public_key}
|
|
AllowedIPs = {allowed_ip}/32
|
|
"#,
|
|
public_key = self.public_key,
|
|
allowed_ip = self.address,
|
|
)
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn add_peer(interface: &Interface, peer: &Peer) -> Result<(), anyhow::Error> {
|
|
use rand::Rng;
|
|
|
|
let filename = rand::thread_rng()
|
|
.sample_iter(rand::distributions::Alphanumeric)
|
|
.take(8)
|
|
.map(char::from)
|
|
.collect::<String>();
|
|
|
|
let filename = format!("{}.conf", filename);
|
|
|
|
let mut tmp_file = PathBuf::new();
|
|
tmp_file.push("/tmp");
|
|
tmp_file.push(&filename);
|
|
|
|
let mut file = File::create(&tmp_file).await?;
|
|
file.write_all(peer.config().as_bytes()).await?;
|
|
file.flush().await?;
|
|
drop(file);
|
|
|
|
let status = Command::new("wg")
|
|
.args(&[
|
|
&"addconf".as_ref(),
|
|
&interface.name.as_ref(),
|
|
&tmp_file.as_os_str(),
|
|
])
|
|
.status()
|
|
.await?;
|
|
|
|
async_fs::remove_file(&tmp_file).await?;
|
|
|
|
if !status.success() {
|
|
return Err(anyhow::anyhow!("Failed to add a peer"));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) async fn generate_config(
|
|
new_interface: NewInterface,
|
|
) -> Result<Interface, anyhow::Error> {
|
|
let output = Command::new("wg").arg("genkey").output().await?;
|
|
|
|
if !output.status.success() {
|
|
return Err(anyhow::anyhow!("Error generating private key"));
|
|
}
|
|
|
|
let private_key = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
|
|
|
let child = Command::new("wg")
|
|
.arg("pubkey")
|
|
.stdin(Stdio::piped())
|
|
.spawn()?;
|
|
|
|
let output = child.output().await?;
|
|
|
|
if !output.status.success() {
|
|
return Err(anyhow::anyhow!("Error generating public key"));
|
|
}
|
|
|
|
let public_key = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
|
|
|
Ok(Interface {
|
|
name: new_interface.name,
|
|
address: new_interface.address,
|
|
port: new_interface.port,
|
|
private_key,
|
|
public_key,
|
|
})
|
|
}
|
|
|
|
pub(crate) async fn apply(interface: &Interface) -> Result<(), anyhow::Error> {
|
|
let config = interface.config();
|
|
|
|
let mut file = File::create(format!("{}.conf", interface.name)).await?;
|
|
file.write_all(config.as_bytes()).await?;
|
|
file.flush().await?;
|
|
|
|
let status = Command::new("systemctl")
|
|
.args(&["enable", "--now", &interface.name])
|
|
.status()
|
|
.await?;
|
|
|
|
if !status.success() {
|
|
return Err(anyhow::anyhow!("Failed to enable wireguard service"));
|
|
}
|
|
|
|
Ok(())
|
|
}
|