parent
a1818d9618
commit
6d28e7f4ab
|
@ -1165,6 +1165,7 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
|
"structopt",
|
||||||
"toml",
|
"toml",
|
||||||
"ureq",
|
"ureq",
|
||||||
"wgctrl",
|
"wgctrl",
|
||||||
|
|
|
@ -3,9 +3,9 @@ use dialoguer::{Confirm, Input};
|
||||||
use hostsfile::HostsBuilder;
|
use hostsfile::HostsBuilder;
|
||||||
use indoc::printdoc;
|
use indoc::printdoc;
|
||||||
use shared::{
|
use shared::{
|
||||||
interface_config::InterfaceConfig, prompts, Association, AssociationContents, Cidr, CidrTree,
|
interface_config::InterfaceConfig, prompts, AddCidrContents, AddPeerContents, Association,
|
||||||
EndpointContents, Interface, IoErrorContext, Peer, RedeemContents, State, CLIENT_CONFIG_PATH,
|
AssociationContents, Cidr, CidrTree, EndpointContents, Interface, IoErrorContext, Peer,
|
||||||
REDEEM_TRANSITION_WAIT,
|
RedeemContents, State, CLIENT_CONFIG_PATH, REDEEM_TRANSITION_WAIT,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
|
@ -103,10 +103,20 @@ enum Command {
|
||||||
Down { interface: Interface },
|
Down { interface: Interface },
|
||||||
|
|
||||||
/// Add a new peer.
|
/// Add a new peer.
|
||||||
AddPeer { interface: Interface },
|
AddPeer {
|
||||||
|
interface: Interface,
|
||||||
|
|
||||||
|
#[structopt(flatten)]
|
||||||
|
args: AddPeerContents,
|
||||||
|
},
|
||||||
|
|
||||||
/// Add a new CIDR.
|
/// Add a new CIDR.
|
||||||
AddCidr { interface: Interface },
|
AddCidr {
|
||||||
|
interface: Interface,
|
||||||
|
|
||||||
|
#[structopt(flatten)]
|
||||||
|
args: AddCidrContents,
|
||||||
|
},
|
||||||
|
|
||||||
/// Disable an enabled peer.
|
/// Disable an enabled peer.
|
||||||
DisablePeer { interface: Interface },
|
DisablePeer { interface: Interface },
|
||||||
|
@ -463,13 +473,13 @@ fn uninstall(interface: &InterfaceName) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_cidr(interface: &InterfaceName) -> Result<(), Error> {
|
fn add_cidr(interface: &InterfaceName, args: AddCidrContents) -> Result<(), Error> {
|
||||||
let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?;
|
let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?;
|
||||||
println!("Fetching CIDRs");
|
println!("Fetching CIDRs");
|
||||||
let api = Api::new(&server);
|
let api = Api::new(&server);
|
||||||
let cidrs: Vec<Cidr> = api.http("GET", "/admin/cidrs")?;
|
let cidrs: Vec<Cidr> = api.http("GET", "/admin/cidrs")?;
|
||||||
|
|
||||||
let cidr_request = prompts::add_cidr(&cidrs)?;
|
let cidr_request = prompts::add_cidr(&cidrs, &args)?;
|
||||||
|
|
||||||
println!("Creating CIDR...");
|
println!("Creating CIDR...");
|
||||||
let cidr: Cidr = api.http_form("POST", "/admin/cidrs", cidr_request)?;
|
let cidr: Cidr = api.http_form("POST", "/admin/cidrs", cidr_request)?;
|
||||||
|
@ -489,7 +499,7 @@ fn add_cidr(interface: &InterfaceName) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_peer(interface: &InterfaceName) -> Result<(), Error> {
|
fn add_peer(interface: &InterfaceName, args: AddPeerContents) -> Result<(), Error> {
|
||||||
let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?;
|
let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?;
|
||||||
let api = Api::new(&server);
|
let api = Api::new(&server);
|
||||||
|
|
||||||
|
@ -499,7 +509,7 @@ fn add_peer(interface: &InterfaceName) -> Result<(), Error> {
|
||||||
let peers: Vec<Peer> = api.http("GET", "/admin/peers")?;
|
let peers: Vec<Peer> = api.http("GET", "/admin/peers")?;
|
||||||
let cidr_tree = CidrTree::new(&cidrs[..]);
|
let cidr_tree = CidrTree::new(&cidrs[..]);
|
||||||
|
|
||||||
if let Some((peer_request, keypair)) = prompts::add_peer(&peers, &cidr_tree)? {
|
if let Some((peer_request, keypair)) = prompts::add_peer(&peers, &cidr_tree, &args)? {
|
||||||
println!("Creating peer...");
|
println!("Creating peer...");
|
||||||
let peer: Peer = api.http_form("POST", "/admin/peers", peer_request)?;
|
let peer: Peer = api.http_form("POST", "/admin/peers", peer_request)?;
|
||||||
let server_peer = peers.iter().find(|p| p.id == 1).unwrap();
|
let server_peer = peers.iter().find(|p| p.id == 1).unwrap();
|
||||||
|
@ -510,6 +520,7 @@ fn add_peer(interface: &InterfaceName) -> Result<(), Error> {
|
||||||
&cidr_tree,
|
&cidr_tree,
|
||||||
keypair,
|
keypair,
|
||||||
&server.internal_endpoint,
|
&server.internal_endpoint,
|
||||||
|
&args.save_config,
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
println!("exited without creating peer.");
|
println!("exited without creating peer.");
|
||||||
|
@ -838,8 +849,8 @@ fn run(opt: Opt) -> Result<(), Error> {
|
||||||
)?,
|
)?,
|
||||||
Command::Down { interface } => wg::down(&interface)?,
|
Command::Down { interface } => wg::down(&interface)?,
|
||||||
Command::Uninstall { interface } => uninstall(&interface)?,
|
Command::Uninstall { interface } => uninstall(&interface)?,
|
||||||
Command::AddPeer { interface } => add_peer(&interface)?,
|
Command::AddPeer { interface, args } => add_peer(&interface, args)?,
|
||||||
Command::AddCidr { interface } => add_cidr(&interface)?,
|
Command::AddCidr { interface, args } => add_cidr(&interface, args)?,
|
||||||
Command::DisablePeer { interface } => enable_or_disable_peer(&interface, false)?,
|
Command::DisablePeer { interface } => enable_or_disable_peer(&interface, false)?,
|
||||||
Command::EnablePeer { interface } => enable_or_disable_peer(&interface, true)?,
|
Command::EnablePeer { interface } => enable_or_disable_peer(&interface, true)?,
|
||||||
Command::AddAssociation { interface } => add_association(&interface)?,
|
Command::AddAssociation { interface } => add_association(&interface)?,
|
||||||
|
|
|
@ -2,8 +2,8 @@ use crate::{ClientError, Error};
|
||||||
use colored::*;
|
use colored::*;
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use shared::{interface_config::ServerInfo, INNERNET_PUBKEY_HEADER};
|
use shared::{interface_config::ServerInfo, INNERNET_PUBKEY_HEADER};
|
||||||
use ureq::{Agent, AgentBuilder};
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use ureq::{Agent, AgentBuilder};
|
||||||
|
|
||||||
pub fn human_duration(duration: Duration) -> String {
|
pub fn human_duration(duration: Duration) -> String {
|
||||||
match duration.as_secs() {
|
match duration.as_secs() {
|
||||||
|
@ -81,11 +81,13 @@ impl<'a> Api<'a> {
|
||||||
endpoint: &str,
|
endpoint: &str,
|
||||||
form: Option<S>,
|
form: Option<S>,
|
||||||
) -> Result<T, Error> {
|
) -> Result<T, Error> {
|
||||||
let request = self.agent.request(
|
let request = self
|
||||||
verb,
|
.agent
|
||||||
&format!("http://{}/v1{}", self.server.internal_endpoint, endpoint),
|
.request(
|
||||||
)
|
verb,
|
||||||
.set(INNERNET_PUBKEY_HEADER, &self.server.public_key);
|
&format!("http://{}/v1{}", self.server.internal_endpoint, endpoint),
|
||||||
|
)
|
||||||
|
.set(INNERNET_PUBKEY_HEADER, &self.server.public_key);
|
||||||
|
|
||||||
let response = if let Some(form) = form {
|
let response = if let Some(form) = form {
|
||||||
request.send_json(serde_json::to_value(form)?)?
|
request.send_json(serde_json::to_value(form)?)?
|
||||||
|
|
|
@ -7,7 +7,7 @@ use ipnetwork::IpNetwork;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
use shared::{IoErrorContext, INNERNET_PUBKEY_HEADER};
|
use shared::{AddCidrContents, AddPeerContents, IoErrorContext, INNERNET_PUBKEY_HEADER};
|
||||||
use std::{
|
use std::{
|
||||||
convert::Infallible,
|
convert::Infallible,
|
||||||
env,
|
env,
|
||||||
|
@ -60,10 +60,20 @@ enum Command {
|
||||||
Serve { interface: Interface },
|
Serve { interface: Interface },
|
||||||
|
|
||||||
/// Add a peer to an existing network.
|
/// Add a peer to an existing network.
|
||||||
AddPeer { interface: Interface },
|
AddPeer {
|
||||||
|
interface: Interface,
|
||||||
|
|
||||||
|
#[structopt(flatten)]
|
||||||
|
args: AddPeerContents,
|
||||||
|
},
|
||||||
|
|
||||||
/// Add a new CIDR to an existing network.
|
/// Add a new CIDR to an existing network.
|
||||||
AddCidr { interface: Interface },
|
AddCidr {
|
||||||
|
interface: Interface,
|
||||||
|
|
||||||
|
#[structopt(flatten)]
|
||||||
|
args: AddCidrContents,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Db = Arc<Mutex<Connection>>;
|
pub type Db = Arc<Mutex<Connection>>;
|
||||||
|
@ -201,8 +211,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
},
|
},
|
||||||
Command::Uninstall { interface } => uninstall(&interface, &conf)?,
|
Command::Uninstall { interface } => uninstall(&interface, &conf)?,
|
||||||
Command::Serve { interface } => serve(&interface, &conf).await?,
|
Command::Serve { interface } => serve(&interface, &conf).await?,
|
||||||
Command::AddPeer { interface } => add_peer(&interface, &conf)?,
|
Command::AddPeer { interface, args } => add_peer(&interface, &conf, args)?,
|
||||||
Command::AddCidr { interface } => add_cidr(&interface, &conf)?,
|
Command::AddCidr { interface, args } => add_cidr(&interface, &conf, args)?,
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -224,7 +234,11 @@ fn open_database_connection(
|
||||||
Ok(Connection::open(&database_path)?)
|
Ok(Connection::open(&database_path)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_peer(interface: &InterfaceName, conf: &ServerConfig) -> Result<(), Error> {
|
fn add_peer(
|
||||||
|
interface: &InterfaceName,
|
||||||
|
conf: &ServerConfig,
|
||||||
|
args: AddPeerContents,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let config = ConfigFile::from_file(conf.config_path(interface))?;
|
let config = ConfigFile::from_file(conf.config_path(interface))?;
|
||||||
let conn = open_database_connection(interface, conf)?;
|
let conn = open_database_connection(interface, conf)?;
|
||||||
let peers = DatabasePeer::list(&conn)?
|
let peers = DatabasePeer::list(&conn)?
|
||||||
|
@ -234,7 +248,7 @@ fn add_peer(interface: &InterfaceName, conf: &ServerConfig) -> Result<(), Error>
|
||||||
let cidrs = DatabaseCidr::list(&conn)?;
|
let cidrs = DatabaseCidr::list(&conn)?;
|
||||||
let cidr_tree = CidrTree::new(&cidrs[..]);
|
let cidr_tree = CidrTree::new(&cidrs[..]);
|
||||||
|
|
||||||
if let Some((peer_request, keypair)) = shared::prompts::add_peer(&peers, &cidr_tree)? {
|
if let Some((peer_request, keypair)) = shared::prompts::add_peer(&peers, &cidr_tree, &args)? {
|
||||||
let peer = DatabasePeer::create(&conn, peer_request)?;
|
let peer = DatabasePeer::create(&conn, peer_request)?;
|
||||||
if cfg!(not(test)) && DeviceInfo::get_by_name(interface).is_ok() {
|
if cfg!(not(test)) && DeviceInfo::get_by_name(interface).is_ok() {
|
||||||
// Update the current WireGuard interface with the new peers.
|
// Update the current WireGuard interface with the new peers.
|
||||||
|
@ -254,6 +268,7 @@ fn add_peer(interface: &InterfaceName, conf: &ServerConfig) -> Result<(), Error>
|
||||||
&cidr_tree,
|
&cidr_tree,
|
||||||
keypair,
|
keypair,
|
||||||
&SocketAddr::new(config.address, config.listen_port),
|
&SocketAddr::new(config.address, config.listen_port),
|
||||||
|
&args.save_config,
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
println!("exited without creating peer.");
|
println!("exited without creating peer.");
|
||||||
|
@ -262,10 +277,14 @@ fn add_peer(interface: &InterfaceName, conf: &ServerConfig) -> Result<(), Error>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_cidr(interface: &InterfaceName, conf: &ServerConfig) -> Result<(), Error> {
|
fn add_cidr(
|
||||||
|
interface: &InterfaceName,
|
||||||
|
conf: &ServerConfig,
|
||||||
|
args: AddCidrContents,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let conn = open_database_connection(interface, conf)?;
|
let conn = open_database_connection(interface, conf)?;
|
||||||
let cidrs = DatabaseCidr::list(&conn)?;
|
let cidrs = DatabaseCidr::list(&conn)?;
|
||||||
if let Some(cidr_request) = shared::prompts::add_cidr(&cidrs)? {
|
if let Some(cidr_request) = shared::prompts::add_cidr(&cidrs, &args)? {
|
||||||
let cidr = DatabaseCidr::create(&conn, cidr_request)?;
|
let cidr = DatabaseCidr::create(&conn, cidr_request)?;
|
||||||
printdoc!(
|
printdoc!(
|
||||||
"
|
"
|
||||||
|
|
|
@ -14,6 +14,7 @@ ipnetwork = { git = "https://github.com/mcginty/ipnetwork" } # pending https://g
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
structopt = "0.3"
|
||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
ureq = { version = "2", default-features = false }
|
ureq = { version = "2", default-features = false }
|
||||||
wgctrl = { path = "../wgctrl-rs" }
|
wgctrl = { path = "../wgctrl-rs" }
|
||||||
|
|
|
@ -40,7 +40,11 @@ pub fn ensure_dirs_exist(dirs: &[&Path]) -> Result<(), Error> {
|
||||||
_ => {
|
_ => {
|
||||||
let target_file = File::open(dir).with_path(dir)?;
|
let target_file = File::open(dir).with_path(dir)?;
|
||||||
if chmod(&target_file, 0o700)? {
|
if chmod(&target_file, 0o700)? {
|
||||||
println!("{} updated permissions for {} to 0700.", "[!]".yellow(), dir.display());
|
println!(
|
||||||
|
"{} updated permissions for {} to 0700.",
|
||||||
|
"[!]".yellow(),
|
||||||
|
dir.display()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -52,16 +56,16 @@ pub fn ensure_dirs_exist(dirs: &[&Path]) -> Result<(), Error> {
|
||||||
/// permissions had to be changed, `Ok(false)` if permissions were already
|
/// permissions had to be changed, `Ok(false)` if permissions were already
|
||||||
/// correct.
|
/// correct.
|
||||||
pub fn chmod(file: &File, new_mode: u32) -> Result<bool, Error> {
|
pub fn chmod(file: &File, new_mode: u32) -> Result<bool, Error> {
|
||||||
let metadata = file.metadata()?;
|
let metadata = file.metadata()?;
|
||||||
let mut permissions = metadata.permissions();
|
let mut permissions = metadata.permissions();
|
||||||
let mode = permissions.mode() & 0o777;
|
let mode = permissions.mode() & 0o777;
|
||||||
let updated = if mode != new_mode {
|
let updated = if mode != new_mode {
|
||||||
permissions.set_mode(new_mode);
|
permissions.set_mode(new_mode);
|
||||||
file.set_permissions(permissions)?;
|
file.set_permissions(permissions)?;
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(updated)
|
Ok(updated)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
interface_config::{InterfaceConfig, InterfaceInfo, ServerInfo},
|
interface_config::{InterfaceConfig, InterfaceInfo, ServerInfo},
|
||||||
Association, Cidr, CidrContents, CidrTree, Error, Peer, PeerContents,
|
AddCidrContents, AddPeerContents, Association, Cidr, CidrContents, CidrTree, Error, Peer,
|
||||||
PERSISTENT_KEEPALIVE_INTERVAL_SECS,
|
PeerContents, PERSISTENT_KEEPALIVE_INTERVAL_SECS,
|
||||||
};
|
};
|
||||||
use colored::*;
|
use colored::*;
|
||||||
use dialoguer::{theme::ColorfulTheme, Confirm, Input, Select};
|
use dialoguer::{theme::ColorfulTheme, Confirm, Input, Select};
|
||||||
|
@ -32,10 +32,27 @@ pub fn hostname_validator(name: &String) -> Result<(), &'static str> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bring up a prompt to create a new CIDR. Returns the peer request.
|
/// Bring up a prompt to create a new CIDR. Returns the peer request.
|
||||||
pub fn add_cidr(cidrs: &[Cidr]) -> Result<Option<CidrContents>, Error> {
|
pub fn add_cidr(cidrs: &[Cidr], request: &AddCidrContents) -> Result<Option<CidrContents>, Error> {
|
||||||
let parent_cidr = choose_cidr(cidrs, "Parent CIDR")?;
|
let parent_cidr = if let Some(ref parent_name) = request.parent {
|
||||||
let name: String = Input::with_theme(&*THEME).with_prompt("Name").interact()?;
|
cidrs
|
||||||
let cidr: IpNetwork = Input::with_theme(&*THEME).with_prompt("CIDR").interact()?;
|
.iter()
|
||||||
|
.find(|cidr| &cidr.name == parent_name)
|
||||||
|
.ok_or("No parent CIDR with that name exists.")?
|
||||||
|
} else {
|
||||||
|
choose_cidr(cidrs, "Parent CIDR")?
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = if let Some(ref name) = request.name {
|
||||||
|
name.clone()
|
||||||
|
} else {
|
||||||
|
Input::with_theme(&*THEME).with_prompt("Name").interact()?
|
||||||
|
};
|
||||||
|
|
||||||
|
let cidr = if let Some(cidr) = request.cidr {
|
||||||
|
cidr
|
||||||
|
} else {
|
||||||
|
Input::with_theme(&*THEME).with_prompt("CIDR").interact()?
|
||||||
|
};
|
||||||
|
|
||||||
let cidr_request = CidrContents {
|
let cidr_request = CidrContents {
|
||||||
name,
|
name,
|
||||||
|
@ -44,10 +61,11 @@ pub fn add_cidr(cidrs: &[Cidr]) -> Result<Option<CidrContents>, Error> {
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
if Confirm::with_theme(&*THEME)
|
if request.force
|
||||||
.with_prompt(&format!("Create CIDR \"{}\"?", cidr_request.name))
|
|| Confirm::with_theme(&*THEME)
|
||||||
.default(false)
|
.with_prompt(&format!("Create CIDR \"{}\"?", cidr_request.name))
|
||||||
.interact()?
|
.default(false)
|
||||||
|
.interact()?
|
||||||
{
|
{
|
||||||
Some(cidr_request)
|
Some(cidr_request)
|
||||||
} else {
|
} else {
|
||||||
|
@ -143,10 +161,18 @@ pub fn delete_association<'a>(
|
||||||
pub fn add_peer(
|
pub fn add_peer(
|
||||||
peers: &[Peer],
|
peers: &[Peer],
|
||||||
cidr_tree: &CidrTree,
|
cidr_tree: &CidrTree,
|
||||||
|
args: &AddPeerContents,
|
||||||
) -> Result<Option<(PeerContents, KeyPair)>, Error> {
|
) -> Result<Option<(PeerContents, KeyPair)>, Error> {
|
||||||
let leaves = cidr_tree.leaves();
|
let leaves = cidr_tree.leaves();
|
||||||
|
|
||||||
let cidr = choose_cidr(&leaves[..], "Eligible CIDRs for peer")?;
|
let cidr = if let Some(ref parent_name) = args.cidr {
|
||||||
|
leaves
|
||||||
|
.iter()
|
||||||
|
.find(|cidr| &cidr.name == parent_name)
|
||||||
|
.ok_or("No eligible CIDR with that name exists.")?
|
||||||
|
} else {
|
||||||
|
choose_cidr(&leaves[..], "Eligible CIDRs for peer")?
|
||||||
|
};
|
||||||
|
|
||||||
let mut available_ip = None;
|
let mut available_ip = None;
|
||||||
let candidate_ips = cidr.iter().filter(|ip| cidr.is_assignable(*ip));
|
let candidate_ips = cidr.iter().filter(|ip| cidr.is_assignable(*ip));
|
||||||
|
@ -159,20 +185,35 @@ pub fn add_peer(
|
||||||
|
|
||||||
let available_ip = available_ip.expect("No IPs in this CIDR are avavilable");
|
let available_ip = available_ip.expect("No IPs in this CIDR are avavilable");
|
||||||
|
|
||||||
let ip = Input::with_theme(&*THEME)
|
let ip = if let Some(ip) = args.ip {
|
||||||
.with_prompt("IP")
|
ip
|
||||||
.default(available_ip)
|
} else if args.auto_ip {
|
||||||
.interact()?;
|
available_ip
|
||||||
|
} else {
|
||||||
|
Input::with_theme(&*THEME)
|
||||||
|
.with_prompt("IP")
|
||||||
|
.default(available_ip)
|
||||||
|
.interact()?
|
||||||
|
};
|
||||||
|
|
||||||
let name: String = Input::with_theme(&*THEME)
|
let name = if let Some(ref name) = args.name {
|
||||||
.with_prompt("Name")
|
name.clone()
|
||||||
.validate_with(hostname_validator)
|
} else {
|
||||||
.interact()?;
|
Input::with_theme(&*THEME)
|
||||||
|
.with_prompt("Name")
|
||||||
|
.validate_with(hostname_validator)
|
||||||
|
.interact()?
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_admin = if let Some(is_admin) = args.admin {
|
||||||
|
is_admin
|
||||||
|
} else {
|
||||||
|
Confirm::with_theme(&*THEME)
|
||||||
|
.with_prompt(&format!("Make {} an admin?", name))
|
||||||
|
.default(false)
|
||||||
|
.interact()?
|
||||||
|
};
|
||||||
|
|
||||||
let is_admin = Confirm::with_theme(&*THEME)
|
|
||||||
.with_prompt(&format!("Make {} an admin?", name))
|
|
||||||
.default(false)
|
|
||||||
.interact()?;
|
|
||||||
let default_keypair = KeyPair::generate();
|
let default_keypair = KeyPair::generate();
|
||||||
let peer_request = PeerContents {
|
let peer_request = PeerContents {
|
||||||
name,
|
name,
|
||||||
|
@ -187,10 +228,11 @@ pub fn add_peer(
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
if Confirm::with_theme(&*THEME)
|
if args.force
|
||||||
.with_prompt(&format!("Create peer {}?", peer_request.name.yellow()))
|
|| Confirm::with_theme(&*THEME)
|
||||||
.default(false)
|
.with_prompt(&format!("Create peer {}?", peer_request.name.yellow()))
|
||||||
.interact()?
|
.default(false)
|
||||||
|
.interact()?
|
||||||
{
|
{
|
||||||
Some((peer_request, default_keypair))
|
Some((peer_request, default_keypair))
|
||||||
} else {
|
} else {
|
||||||
|
@ -245,6 +287,7 @@ pub fn save_peer_invitation(
|
||||||
root_cidr: &Cidr,
|
root_cidr: &Cidr,
|
||||||
keypair: KeyPair,
|
keypair: KeyPair,
|
||||||
server_api_addr: &SocketAddr,
|
server_api_addr: &SocketAddr,
|
||||||
|
config_location: &Option<String>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let peer_invitation = InterfaceConfig {
|
let peer_invitation = InterfaceConfig {
|
||||||
interface: InterfaceInfo {
|
interface: InterfaceInfo {
|
||||||
|
@ -262,10 +305,14 @@ pub fn save_peer_invitation(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let invitation_save_path = Input::with_theme(&*THEME)
|
let invitation_save_path = if let Some(location) = config_location {
|
||||||
.with_prompt("Save peer invitation file as")
|
location.clone()
|
||||||
.default(format!("{}.toml", peer.name))
|
} else {
|
||||||
.interact()?;
|
Input::with_theme(&*THEME)
|
||||||
|
.with_prompt("Save peer invitation file as")
|
||||||
|
.default(format!("{}.toml", peer.name))
|
||||||
|
.interact()?
|
||||||
|
};
|
||||||
|
|
||||||
peer_invitation.write_to_path(&invitation_save_path, true, None)?;
|
peer_invitation.write_to_path(&invitation_save_path, true, None)?;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use ipnetwork::IpNetwork;
|
|
||||||
use crate::prompts::hostname_validator;
|
use crate::prompts::hostname_validator;
|
||||||
|
use ipnetwork::IpNetwork;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{Display, Formatter},
|
fmt::{Display, Formatter},
|
||||||
|
@ -8,6 +8,7 @@ use std::{
|
||||||
path::Path,
|
path::Path,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
use structopt::StructOpt;
|
||||||
use wgctrl::{InterfaceName, InvalidInterfaceName, Key, PeerConfig, PeerConfigBuilder};
|
use wgctrl::{InterfaceName, InvalidInterfaceName, Key, PeerConfig, PeerConfigBuilder};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -165,6 +166,51 @@ pub struct RedeemContents {
|
||||||
pub public_key: String,
|
pub public_key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, StructOpt)]
|
||||||
|
pub struct AddPeerContents {
|
||||||
|
/// Name of new peer
|
||||||
|
#[structopt(long)]
|
||||||
|
pub name: Option<String>,
|
||||||
|
|
||||||
|
/// Specify desired IP of new peer (within parent CIDR)
|
||||||
|
#[structopt(long, conflicts_with = "auto-ip")]
|
||||||
|
pub ip: Option<IpAddr>,
|
||||||
|
|
||||||
|
/// Auto-assign the peer the first available IP within the CIDR
|
||||||
|
#[structopt(long = "auto-ip")]
|
||||||
|
pub auto_ip: bool,
|
||||||
|
|
||||||
|
/// Name of CIDR to add new peer under
|
||||||
|
#[structopt(long)]
|
||||||
|
pub cidr: Option<String>,
|
||||||
|
|
||||||
|
/// Make new peer an admin
|
||||||
|
#[structopt(long)]
|
||||||
|
pub admin: Option<bool>,
|
||||||
|
|
||||||
|
/// Force confirmation
|
||||||
|
#[structopt(short, long)]
|
||||||
|
pub force: bool,
|
||||||
|
|
||||||
|
#[structopt(long)]
|
||||||
|
pub save_config: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, StructOpt)]
|
||||||
|
pub struct AddCidrContents {
|
||||||
|
#[structopt(long)]
|
||||||
|
pub name: Option<String>,
|
||||||
|
|
||||||
|
#[structopt(long)]
|
||||||
|
pub cidr: Option<IpNetwork>,
|
||||||
|
|
||||||
|
#[structopt(long)]
|
||||||
|
pub parent: Option<String>,
|
||||||
|
|
||||||
|
#[structopt(short, long)]
|
||||||
|
pub force: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
||||||
pub struct PeerContents {
|
pub struct PeerContents {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -393,4 +439,4 @@ mod tests {
|
||||||
println!("{:?}", config);
|
println!("{:?}", config);
|
||||||
assert!(matches!(peer.diff(&config), Some(_)));
|
assert!(matches!(peer.diff(&config), Some(_)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -309,11 +309,14 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_interface_names() {
|
fn test_interface_names() {
|
||||||
assert_eq!("wg-01".parse::<InterfaceName>().unwrap().as_str_lossy(), "wg-01");
|
assert_eq!(
|
||||||
|
"wg-01".parse::<InterfaceName>().unwrap().as_str_lossy(),
|
||||||
|
"wg-01"
|
||||||
|
);
|
||||||
assert!("longer-nul\0".parse::<InterfaceName>().is_err());
|
assert!("longer-nul\0".parse::<InterfaceName>().is_err());
|
||||||
|
|
||||||
let invalid_names = &[
|
let invalid_names = &[
|
||||||
("", InvalidInterfaceName::Empty), // Empty Rust string
|
("", InvalidInterfaceName::Empty), // Empty Rust string
|
||||||
("\0", InvalidInterfaceName::InvalidChars), // Empty C string
|
("\0", InvalidInterfaceName::InvalidChars), // Empty C string
|
||||||
("ifname\0nul", InvalidInterfaceName::InvalidChars), // Contains interior NUL
|
("ifname\0nul", InvalidInterfaceName::InvalidChars), // Contains interior NUL
|
||||||
("if name", InvalidInterfaceName::InvalidChars), // Contains a space
|
("if name", InvalidInterfaceName::InvalidChars), // Contains a space
|
||||||
|
|
Loading…
Reference in New Issue