{client, server}: make config/data directories configurable (#172)
* client: allow config/data dirs to be changed * server: allow config/data dirs to be changed * meta: cargo clippy & cargo fmt * shared: use const for Duration instead of lazy_staticpull/178/head
parent
bce7af20ce
commit
ae2c554b23
|
@ -1,9 +1,7 @@
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use shared::{
|
use shared::{chmod, ensure_dirs_exist, Cidr, IoErrorContext, Peer, WrappedIoError};
|
||||||
chmod, ensure_dirs_exist, Cidr, IoErrorContext, Peer, WrappedIoError, CLIENT_DATA_DIR,
|
|
||||||
};
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::{File, OpenOptions},
|
fs::{File, OpenOptions},
|
||||||
io::{self, Read, Seek, SeekFrom, Write},
|
io::{self, Read, Seek, SeekFrom, Write},
|
||||||
|
@ -55,23 +53,28 @@ impl DataStore {
|
||||||
Ok(Self { file, contents })
|
Ok(Self { file, contents })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_path(interface: &InterfaceName) -> PathBuf {
|
pub fn get_path(data_dir: &Path, interface: &InterfaceName) -> PathBuf {
|
||||||
CLIENT_DATA_DIR
|
data_dir.join(interface.to_string()).with_extension("json")
|
||||||
.join(interface.to_string())
|
|
||||||
.with_extension("json")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _open(interface: &InterfaceName, create: bool) -> Result<Self, WrappedIoError> {
|
fn _open(
|
||||||
ensure_dirs_exist(&[*CLIENT_DATA_DIR])?;
|
data_dir: &Path,
|
||||||
Self::open_with_path(Self::get_path(interface), create)
|
interface: &InterfaceName,
|
||||||
|
create: bool,
|
||||||
|
) -> Result<Self, WrappedIoError> {
|
||||||
|
ensure_dirs_exist(&[data_dir])?;
|
||||||
|
Self::open_with_path(Self::get_path(data_dir, interface), create)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open(interface: &InterfaceName) -> Result<Self, WrappedIoError> {
|
pub fn open(data_dir: &Path, interface: &InterfaceName) -> Result<Self, WrappedIoError> {
|
||||||
Self::_open(interface, false)
|
Self::_open(data_dir, interface, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_or_create(interface: &InterfaceName) -> Result<Self, WrappedIoError> {
|
pub fn open_or_create(
|
||||||
Self::_open(interface, true)
|
data_dir: &Path,
|
||||||
|
interface: &InterfaceName,
|
||||||
|
) -> Result<Self, WrappedIoError> {
|
||||||
|
Self::_open(data_dir, interface, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peers(&self) -> &[Peer] {
|
pub fn peers(&self) -> &[Peer] {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use shared::{
|
||||||
wg::{DeviceExt, PeerInfoExt},
|
wg::{DeviceExt, PeerInfoExt},
|
||||||
AddAssociationOpts, AddCidrOpts, AddPeerOpts, Association, AssociationContents, Cidr, CidrTree,
|
AddAssociationOpts, AddCidrOpts, AddPeerOpts, Association, AssociationContents, Cidr, CidrTree,
|
||||||
DeleteCidrOpts, Endpoint, EndpointContents, InstallOpts, Interface, IoErrorContext, NatOpts,
|
DeleteCidrOpts, Endpoint, EndpointContents, InstallOpts, Interface, IoErrorContext, NatOpts,
|
||||||
NetworkOpts, Peer, RedeemContents, RenamePeerOpts, State, WrappedIoError, CLIENT_CONFIG_DIR,
|
NetworkOpts, Peer, RedeemContents, RenamePeerOpts, State, WrappedIoError,
|
||||||
REDEEM_TRANSITION_WAIT,
|
REDEEM_TRANSITION_WAIT,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -44,7 +44,7 @@ macro_rules! println_pad {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Clone, Debug, StructOpt)]
|
||||||
#[structopt(name = "innernet", about, global_settings(&[AppSettings::ColoredHelp, AppSettings::DeriveDisplayOrder, AppSettings::VersionlessSubcommands, AppSettings::UnifiedHelpMessage]))]
|
#[structopt(name = "innernet", about, global_settings(&[AppSettings::ColoredHelp, AppSettings::DeriveDisplayOrder, AppSettings::VersionlessSubcommands, AppSettings::UnifiedHelpMessage]))]
|
||||||
struct Opts {
|
struct Opts {
|
||||||
#[structopt(subcommand)]
|
#[structopt(subcommand)]
|
||||||
|
@ -54,11 +54,17 @@ struct Opts {
|
||||||
#[structopt(short, long, parse(from_occurrences))]
|
#[structopt(short, long, parse(from_occurrences))]
|
||||||
verbose: u64,
|
verbose: u64,
|
||||||
|
|
||||||
|
#[structopt(short, long, default_value = "/etc/innernet")]
|
||||||
|
config_dir: PathBuf,
|
||||||
|
|
||||||
|
#[structopt(short, long, default_value = "/var/lib/innernet")]
|
||||||
|
data_dir: PathBuf,
|
||||||
|
|
||||||
#[structopt(flatten)]
|
#[structopt(flatten)]
|
||||||
network: NetworkOpts,
|
network: NetworkOpts,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Clone, Debug, StructOpt)]
|
||||||
struct HostsOpt {
|
struct HostsOpt {
|
||||||
/// The path to write hosts to.
|
/// The path to write hosts to.
|
||||||
#[structopt(long = "hosts-path", default_value = "/etc/hosts")]
|
#[structopt(long = "hosts-path", default_value = "/etc/hosts")]
|
||||||
|
@ -75,7 +81,7 @@ impl From<HostsOpt> for Option<PathBuf> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Clone, Debug, StructOpt)]
|
||||||
enum Command {
|
enum Command {
|
||||||
/// Install a new innernet config.
|
/// Install a new innernet config.
|
||||||
#[structopt(alias = "redeem")]
|
#[structopt(alias = "redeem")]
|
||||||
|
@ -87,7 +93,7 @@ enum Command {
|
||||||
hosts: HostsOpt,
|
hosts: HostsOpt,
|
||||||
|
|
||||||
#[structopt(flatten)]
|
#[structopt(flatten)]
|
||||||
opts: InstallOpts,
|
install_opts: InstallOpts,
|
||||||
|
|
||||||
#[structopt(flatten)]
|
#[structopt(flatten)]
|
||||||
nat: NatOpts,
|
nat: NatOpts,
|
||||||
|
@ -155,7 +161,7 @@ enum Command {
|
||||||
interface: Interface,
|
interface: Interface,
|
||||||
|
|
||||||
#[structopt(flatten)]
|
#[structopt(flatten)]
|
||||||
opts: AddPeerOpts,
|
sub_opts: AddPeerOpts,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Rename a peer.
|
/// Rename a peer.
|
||||||
|
@ -168,7 +174,7 @@ enum Command {
|
||||||
interface: Interface,
|
interface: Interface,
|
||||||
|
|
||||||
#[structopt(flatten)]
|
#[structopt(flatten)]
|
||||||
opts: RenamePeerOpts,
|
sub_opts: RenamePeerOpts,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Add a new CIDR.
|
/// Add a new CIDR.
|
||||||
|
@ -176,7 +182,7 @@ enum Command {
|
||||||
interface: Interface,
|
interface: Interface,
|
||||||
|
|
||||||
#[structopt(flatten)]
|
#[structopt(flatten)]
|
||||||
opts: AddCidrOpts,
|
sub_opts: AddCidrOpts,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Delete a CIDR.
|
/// Delete a CIDR.
|
||||||
|
@ -184,7 +190,7 @@ enum Command {
|
||||||
interface: Interface,
|
interface: Interface,
|
||||||
|
|
||||||
#[structopt(flatten)]
|
#[structopt(flatten)]
|
||||||
opts: DeleteCidrOpts,
|
sub_opts: DeleteCidrOpts,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// List CIDRs.
|
/// List CIDRs.
|
||||||
|
@ -207,7 +213,7 @@ enum Command {
|
||||||
interface: Interface,
|
interface: Interface,
|
||||||
|
|
||||||
#[structopt(flatten)]
|
#[structopt(flatten)]
|
||||||
opts: AddAssociationOpts,
|
sub_opts: AddAssociationOpts,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Delete an association between CIDRs.
|
/// Delete an association between CIDRs.
|
||||||
|
@ -279,18 +285,18 @@ fn update_hosts_file(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn install(
|
fn install(
|
||||||
|
opts: &Opts,
|
||||||
invite: &Path,
|
invite: &Path,
|
||||||
hosts_file: Option<PathBuf>,
|
hosts_file: Option<PathBuf>,
|
||||||
opts: InstallOpts,
|
install_opts: InstallOpts,
|
||||||
network: NetworkOpts,
|
|
||||||
nat: &NatOpts,
|
nat: &NatOpts,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
shared::ensure_dirs_exist(&[*CLIENT_CONFIG_DIR])?;
|
shared::ensure_dirs_exist(&[&opts.config_dir])?;
|
||||||
let config = InterfaceConfig::from_file(invite)?;
|
let config = InterfaceConfig::from_file(invite)?;
|
||||||
|
|
||||||
let iface = if opts.default_name {
|
let iface = if install_opts.default_name {
|
||||||
config.interface.network_name.clone()
|
config.interface.network_name.clone()
|
||||||
} else if let Some(ref iface) = opts.name {
|
} else if let Some(ref iface) = install_opts.name {
|
||||||
iface.clone()
|
iface.clone()
|
||||||
} else {
|
} else {
|
||||||
Input::with_theme(&*prompts::THEME)
|
Input::with_theme(&*prompts::THEME)
|
||||||
|
@ -299,7 +305,7 @@ fn install(
|
||||||
.interact()?
|
.interact()?
|
||||||
};
|
};
|
||||||
|
|
||||||
let target_conf = CLIENT_CONFIG_DIR.join(&iface).with_extension("conf");
|
let target_conf = opts.config_dir.join(&iface).with_extension("conf");
|
||||||
if target_conf.exists() {
|
if target_conf.exists() {
|
||||||
bail!(
|
bail!(
|
||||||
"An existing innernet network with the name \"{}\" already exists.",
|
"An existing innernet network with the name \"{}\" already exists.",
|
||||||
|
@ -307,7 +313,7 @@ fn install(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let iface = iface.parse()?;
|
let iface = iface.parse()?;
|
||||||
if Device::list(network.backend)
|
if Device::list(opts.network.backend)
|
||||||
.iter()
|
.iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.any(|name| name == &iface)
|
.any(|name| name == &iface)
|
||||||
|
@ -318,10 +324,10 @@ fn install(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
redeem_invite(&iface, config, target_conf, network).map_err(|e| {
|
redeem_invite(&iface, config, target_conf, opts.network).map_err(|e| {
|
||||||
log::error!("failed to start the interface: {}.", e);
|
log::error!("failed to start the interface: {}.", e);
|
||||||
log::info!("bringing down the interface.");
|
log::info!("bringing down the interface.");
|
||||||
if let Err(e) = wg::down(&iface, network.backend) {
|
if let Err(e) = wg::down(&iface, opts.network.backend) {
|
||||||
log::warn!("failed to bring down interface: {}.", e.to_string());
|
log::warn!("failed to bring down interface: {}.", e.to_string());
|
||||||
};
|
};
|
||||||
log::error!("Failed to redeem invite. Now's a good time to make sure the server is started and accessible!");
|
log::error!("Failed to redeem invite. Now's a good time to make sure the server is started and accessible!");
|
||||||
|
@ -330,15 +336,7 @@ fn install(
|
||||||
|
|
||||||
let mut fetch_success = false;
|
let mut fetch_success = false;
|
||||||
for _ in 0..3 {
|
for _ in 0..3 {
|
||||||
if fetch(
|
if fetch(&iface, opts, true, hosts_file.clone(), nat).is_ok() {
|
||||||
&iface,
|
|
||||||
true,
|
|
||||||
hosts_file.clone(),
|
|
||||||
network,
|
|
||||||
nat,
|
|
||||||
)
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
fetch_success = true;
|
fetch_success = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -350,7 +348,7 @@ fn install(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.delete_invite
|
if install_opts.delete_invite
|
||||||
|| Confirm::with_theme(&*prompts::THEME)
|
|| Confirm::with_theme(&*prompts::THEME)
|
||||||
.wait_for_newline(true)
|
.wait_for_newline(true)
|
||||||
.with_prompt(&format!(
|
.with_prompt(&format!(
|
||||||
|
@ -472,20 +470,20 @@ fn redeem_invite(
|
||||||
.set_private_key(keypair.private)
|
.set_private_key(keypair.private)
|
||||||
.apply(iface, network.backend)
|
.apply(iface, network.backend)
|
||||||
.with_str(iface.to_string())?;
|
.with_str(iface.to_string())?;
|
||||||
thread::sleep(*REDEEM_TRANSITION_WAIT);
|
thread::sleep(REDEEM_TRANSITION_WAIT);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn up(
|
fn up(
|
||||||
interface: &InterfaceName,
|
interface: &InterfaceName,
|
||||||
|
opts: &Opts,
|
||||||
loop_interval: Option<Duration>,
|
loop_interval: Option<Duration>,
|
||||||
hosts_path: Option<PathBuf>,
|
hosts_path: Option<PathBuf>,
|
||||||
routing: NetworkOpts,
|
|
||||||
nat: &NatOpts,
|
nat: &NatOpts,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
loop {
|
loop {
|
||||||
fetch(interface, true, hosts_path.clone(), routing, nat)?;
|
fetch(interface, opts, true, hosts_path.clone(), nat)?;
|
||||||
match loop_interval {
|
match loop_interval {
|
||||||
Some(interval) => thread::sleep(interval),
|
Some(interval) => thread::sleep(interval),
|
||||||
None => break,
|
None => break,
|
||||||
|
@ -497,13 +495,13 @@ fn up(
|
||||||
|
|
||||||
fn fetch(
|
fn fetch(
|
||||||
interface: &InterfaceName,
|
interface: &InterfaceName,
|
||||||
|
opts: &Opts,
|
||||||
bring_up_interface: bool,
|
bring_up_interface: bool,
|
||||||
hosts_path: Option<PathBuf>,
|
hosts_path: Option<PathBuf>,
|
||||||
network: NetworkOpts,
|
|
||||||
nat: &NatOpts,
|
nat: &NatOpts,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let config = InterfaceConfig::from_interface(interface)?;
|
let config = InterfaceConfig::from_interface(&opts.config_dir, interface)?;
|
||||||
let interface_up = match Device::list(network.backend) {
|
let interface_up = match Device::list(opts.network.backend) {
|
||||||
Ok(interfaces) => interfaces.iter().any(|name| name == interface),
|
Ok(interfaces) => interfaces.iter().any(|name| name == interface),
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
@ -532,17 +530,17 @@ fn fetch(
|
||||||
config.server.internal_endpoint.ip(),
|
config.server.internal_endpoint.ip(),
|
||||||
resolved_endpoint,
|
resolved_endpoint,
|
||||||
)),
|
)),
|
||||||
network,
|
opts.network,
|
||||||
)
|
)
|
||||||
.with_str(interface.to_string())?;
|
.with_str(interface.to_string())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!("fetching state from server.");
|
log::info!("fetching state from server.");
|
||||||
let mut store = DataStore::open_or_create(interface)?;
|
let mut store = DataStore::open_or_create(&opts.data_dir, interface)?;
|
||||||
let api = Api::new(&config.server);
|
let api = Api::new(&config.server);
|
||||||
let State { peers, cidrs } = api.http("GET", "/user/state")?;
|
let State { peers, cidrs } = api.http("GET", "/user/state")?;
|
||||||
|
|
||||||
let device = Device::get(interface, network.backend)?;
|
let device = Device::get(interface, opts.network.backend)?;
|
||||||
let modifications = device.diff(&peers);
|
let modifications = device.diff(&peers);
|
||||||
|
|
||||||
let updates = modifications
|
let updates = modifications
|
||||||
|
@ -555,7 +553,7 @@ fn fetch(
|
||||||
if !updates.is_empty() || !interface_up {
|
if !updates.is_empty() || !interface_up {
|
||||||
DeviceUpdate::new()
|
DeviceUpdate::new()
|
||||||
.add_peers(&updates)
|
.add_peers(&updates)
|
||||||
.apply(interface, network.backend)
|
.apply(interface, opts.network.backend)
|
||||||
.with_str(interface.to_string())?;
|
.with_str(interface.to_string())?;
|
||||||
|
|
||||||
if let Some(path) = hosts_path {
|
if let Some(path) = hosts_path {
|
||||||
|
@ -594,7 +592,7 @@ fn fetch(
|
||||||
if nat.no_nat_traversal {
|
if nat.no_nat_traversal {
|
||||||
log::debug!("NAT traversal explicitly disabled, not attempting.");
|
log::debug!("NAT traversal explicitly disabled, not attempting.");
|
||||||
} else {
|
} else {
|
||||||
let mut nat_traverse = NatTraverse::new(interface, network.backend, &modifications)?;
|
let mut nat_traverse = NatTraverse::new(interface, opts.network.backend, &modifications)?;
|
||||||
|
|
||||||
// Give time for handshakes with recently changed endpoints to complete before attempting traversal.
|
// Give time for handshakes with recently changed endpoints to complete before attempting traversal.
|
||||||
if !nat_traverse.is_finished() {
|
if !nat_traverse.is_finished() {
|
||||||
|
@ -615,7 +613,7 @@ fn fetch(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uninstall(interface: &InterfaceName, network: NetworkOpts) -> Result<(), Error> {
|
fn uninstall(interface: &InterfaceName, opts: &Opts) -> Result<(), Error> {
|
||||||
if Confirm::with_theme(&*prompts::THEME)
|
if Confirm::with_theme(&*prompts::THEME)
|
||||||
.with_prompt(&format!(
|
.with_prompt(&format!(
|
||||||
"Permanently delete network \"{}\"?",
|
"Permanently delete network \"{}\"?",
|
||||||
|
@ -626,9 +624,9 @@ fn uninstall(interface: &InterfaceName, network: NetworkOpts) -> Result<(), Erro
|
||||||
.interact()?
|
.interact()?
|
||||||
{
|
{
|
||||||
log::info!("bringing down interface (if up).");
|
log::info!("bringing down interface (if up).");
|
||||||
wg::down(interface, network.backend).ok();
|
wg::down(interface, opts.network.backend).ok();
|
||||||
let config = InterfaceConfig::get_path(interface);
|
let config = InterfaceConfig::get_path(&opts.config_dir, interface);
|
||||||
let data = DataStore::get_path(interface);
|
let data = DataStore::get_path(&opts.data_dir, interface);
|
||||||
std::fs::remove_file(&config)
|
std::fs::remove_file(&config)
|
||||||
.with_path(&config)
|
.with_path(&config)
|
||||||
.map_err(|e| log::warn!("{}", e.to_string().yellow()))
|
.map_err(|e| log::warn!("{}", e.to_string().yellow()))
|
||||||
|
@ -645,13 +643,14 @@ fn uninstall(interface: &InterfaceName, network: NetworkOpts) -> Result<(), Erro
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_cidr(interface: &InterfaceName, opts: AddCidrOpts) -> Result<(), Error> {
|
fn add_cidr(interface: &InterfaceName, opts: &Opts, sub_opts: AddCidrOpts) -> Result<(), Error> {
|
||||||
let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?;
|
let InterfaceConfig { server, .. } =
|
||||||
|
InterfaceConfig::from_interface(&opts.config_dir, interface)?;
|
||||||
log::info!("Fetching CIDRs");
|
log::info!("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")?;
|
||||||
|
|
||||||
if let Some(cidr_request) = prompts::add_cidr(&cidrs, &opts)? {
|
if let Some(cidr_request) = prompts::add_cidr(&cidrs, &sub_opts)? {
|
||||||
log::info!("Creating CIDR...");
|
log::info!("Creating CIDR...");
|
||||||
let cidr: Cidr = api.http_form("POST", "/admin/cidrs", cidr_request)?;
|
let cidr: Cidr = api.http_form("POST", "/admin/cidrs", cidr_request)?;
|
||||||
|
|
||||||
|
@ -673,14 +672,19 @@ fn add_cidr(interface: &InterfaceName, opts: AddCidrOpts) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_cidr(interface: &InterfaceName, opts: DeleteCidrOpts) -> Result<(), Error> {
|
fn delete_cidr(
|
||||||
let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?;
|
interface: &InterfaceName,
|
||||||
|
opts: &Opts,
|
||||||
|
sub_opts: DeleteCidrOpts,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let InterfaceConfig { server, .. } =
|
||||||
|
InterfaceConfig::from_interface(&opts.config_dir, interface)?;
|
||||||
println!("Fetching eligible CIDRs");
|
println!("Fetching eligible 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 peers: Vec<Peer> = api.http("GET", "/admin/peers")?;
|
let peers: Vec<Peer> = api.http("GET", "/admin/peers")?;
|
||||||
|
|
||||||
let cidr_id = prompts::delete_cidr(&cidrs, &peers, &opts)?;
|
let cidr_id = prompts::delete_cidr(&cidrs, &peers, &sub_opts)?;
|
||||||
|
|
||||||
println!("Deleting CIDR...");
|
println!("Deleting CIDR...");
|
||||||
let _ = api.http("DELETE", &*format!("/admin/cidrs/{}", cidr_id))?;
|
let _ = api.http("DELETE", &*format!("/admin/cidrs/{}", cidr_id))?;
|
||||||
|
@ -690,8 +694,8 @@ fn delete_cidr(interface: &InterfaceName, opts: DeleteCidrOpts) -> Result<(), Er
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_cidrs(interface: &InterfaceName, tree: bool) -> Result<(), Error> {
|
fn list_cidrs(interface: &InterfaceName, opts: &Opts, tree: bool) -> Result<(), Error> {
|
||||||
let data_store = DataStore::open(interface)?;
|
let data_store = DataStore::open(&opts.data_dir, interface)?;
|
||||||
if tree {
|
if tree {
|
||||||
let cidr_tree = CidrTree::new(data_store.cidrs());
|
let cidr_tree = CidrTree::new(data_store.cidrs());
|
||||||
colored::control::set_override(false);
|
colored::control::set_override(false);
|
||||||
|
@ -705,8 +709,9 @@ fn list_cidrs(interface: &InterfaceName, tree: bool) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_peer(interface: &InterfaceName, opts: AddPeerOpts) -> Result<(), Error> {
|
fn add_peer(interface: &InterfaceName, opts: &Opts, sub_opts: AddPeerOpts) -> Result<(), Error> {
|
||||||
let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?;
|
let InterfaceConfig { server, .. } =
|
||||||
|
InterfaceConfig::from_interface(&opts.config_dir, interface)?;
|
||||||
let api = Api::new(&server);
|
let api = Api::new(&server);
|
||||||
|
|
||||||
log::info!("Fetching CIDRs");
|
log::info!("Fetching CIDRs");
|
||||||
|
@ -715,7 +720,7 @@ fn add_peer(interface: &InterfaceName, opts: AddPeerOpts) -> 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(result) = prompts::add_peer(&peers, &cidr_tree, &opts)? {
|
if let Some(result) = prompts::add_peer(&peers, &cidr_tree, &sub_opts)? {
|
||||||
let (peer_request, keypair, target_path, mut target_file) = result;
|
let (peer_request, keypair, target_path, mut target_file) = result;
|
||||||
log::info!("Creating peer...");
|
log::info!("Creating peer...");
|
||||||
let peer: Peer = api.http_form("POST", "/admin/peers", peer_request)?;
|
let peer: Peer = api.http_form("POST", "/admin/peers", peer_request)?;
|
||||||
|
@ -736,14 +741,19 @@ fn add_peer(interface: &InterfaceName, opts: AddPeerOpts) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rename_peer(interface: &InterfaceName, opts: RenamePeerOpts) -> Result<(), Error> {
|
fn rename_peer(
|
||||||
let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?;
|
interface: &InterfaceName,
|
||||||
|
opts: &Opts,
|
||||||
|
sub_opts: RenamePeerOpts,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let InterfaceConfig { server, .. } =
|
||||||
|
InterfaceConfig::from_interface(&opts.config_dir, interface)?;
|
||||||
let api = Api::new(&server);
|
let api = Api::new(&server);
|
||||||
|
|
||||||
log::info!("Fetching peers");
|
log::info!("Fetching peers");
|
||||||
let peers: Vec<Peer> = api.http("GET", "/admin/peers")?;
|
let peers: Vec<Peer> = api.http("GET", "/admin/peers")?;
|
||||||
|
|
||||||
if let Some((peer_request, old_name)) = prompts::rename_peer(&peers, &opts)? {
|
if let Some((peer_request, old_name)) = prompts::rename_peer(&peers, &sub_opts)? {
|
||||||
log::info!("Renaming peer...");
|
log::info!("Renaming peer...");
|
||||||
|
|
||||||
let id = peers
|
let id = peers
|
||||||
|
@ -762,8 +772,13 @@ fn rename_peer(interface: &InterfaceName, opts: RenamePeerOpts) -> Result<(), Er
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enable_or_disable_peer(interface: &InterfaceName, enable: bool) -> Result<(), Error> {
|
fn enable_or_disable_peer(
|
||||||
let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?;
|
interface: &InterfaceName,
|
||||||
|
opts: &Opts,
|
||||||
|
enable: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let InterfaceConfig { server, .. } =
|
||||||
|
InterfaceConfig::from_interface(&opts.config_dir, interface)?;
|
||||||
let api = Api::new(&server);
|
let api = Api::new(&server);
|
||||||
|
|
||||||
log::info!("Fetching peers.");
|
log::info!("Fetching peers.");
|
||||||
|
@ -780,14 +795,19 @@ fn enable_or_disable_peer(interface: &InterfaceName, enable: bool) -> Result<(),
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_association(interface: &InterfaceName, opts: AddAssociationOpts) -> Result<(), Error> {
|
fn add_association(
|
||||||
let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?;
|
interface: &InterfaceName,
|
||||||
|
opts: &Opts,
|
||||||
|
sub_opts: AddAssociationOpts,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let InterfaceConfig { server, .. } =
|
||||||
|
InterfaceConfig::from_interface(&opts.config_dir, interface)?;
|
||||||
let api = Api::new(&server);
|
let api = Api::new(&server);
|
||||||
|
|
||||||
log::info!("Fetching CIDRs");
|
log::info!("Fetching CIDRs");
|
||||||
let cidrs: Vec<Cidr> = api.http("GET", "/admin/cidrs")?;
|
let cidrs: Vec<Cidr> = api.http("GET", "/admin/cidrs")?;
|
||||||
|
|
||||||
let association = if let (Some(ref cidr1), Some(ref cidr2)) = (opts.cidr1, opts.cidr2) {
|
let association = if let (Some(ref cidr1), Some(ref cidr2)) = (sub_opts.cidr1, sub_opts.cidr2) {
|
||||||
let cidr1 = cidrs
|
let cidr1 = cidrs
|
||||||
.iter()
|
.iter()
|
||||||
.find(|c| &c.name == cidr1)
|
.find(|c| &c.name == cidr1)
|
||||||
|
@ -816,8 +836,9 @@ fn add_association(interface: &InterfaceName, opts: AddAssociationOpts) -> Resul
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_association(interface: &InterfaceName) -> Result<(), Error> {
|
fn delete_association(interface: &InterfaceName, opts: &Opts) -> Result<(), Error> {
|
||||||
let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?;
|
let InterfaceConfig { server, .. } =
|
||||||
|
InterfaceConfig::from_interface(&opts.config_dir, interface)?;
|
||||||
let api = Api::new(&server);
|
let api = Api::new(&server);
|
||||||
|
|
||||||
log::info!("Fetching CIDRs");
|
log::info!("Fetching CIDRs");
|
||||||
|
@ -834,8 +855,9 @@ fn delete_association(interface: &InterfaceName) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_associations(interface: &InterfaceName) -> Result<(), Error> {
|
fn list_associations(interface: &InterfaceName, opts: &Opts) -> Result<(), Error> {
|
||||||
let InterfaceConfig { server, .. } = InterfaceConfig::from_interface(interface)?;
|
let InterfaceConfig { server, .. } =
|
||||||
|
InterfaceConfig::from_interface(&opts.config_dir, interface)?;
|
||||||
let api = Api::new(&server);
|
let api = Api::new(&server);
|
||||||
|
|
||||||
log::info!("Fetching CIDRs");
|
log::info!("Fetching CIDRs");
|
||||||
|
@ -867,18 +889,18 @@ fn list_associations(interface: &InterfaceName) -> Result<(), Error> {
|
||||||
|
|
||||||
fn set_listen_port(
|
fn set_listen_port(
|
||||||
interface: &InterfaceName,
|
interface: &InterfaceName,
|
||||||
|
opts: &Opts,
|
||||||
unset: bool,
|
unset: bool,
|
||||||
network: NetworkOpts,
|
|
||||||
) -> Result<Option<u16>, Error> {
|
) -> Result<Option<u16>, Error> {
|
||||||
let mut config = InterfaceConfig::from_interface(interface)?;
|
let mut config = InterfaceConfig::from_interface(&opts.config_dir, interface)?;
|
||||||
|
|
||||||
let listen_port = prompts::set_listen_port(&config.interface, unset)?;
|
let listen_port = prompts::set_listen_port(&config.interface, unset)?;
|
||||||
if let Some(listen_port) = listen_port {
|
if let Some(listen_port) = listen_port {
|
||||||
wg::set_listen_port(interface, listen_port, network.backend)?;
|
wg::set_listen_port(interface, listen_port, opts.network.backend)?;
|
||||||
log::info!("the interface is updated");
|
log::info!("the interface is updated");
|
||||||
|
|
||||||
config.interface.listen_port = listen_port;
|
config.interface.listen_port = listen_port;
|
||||||
config.write_to_interface(interface)?;
|
config.write_to_interface(&opts.config_dir, interface)?;
|
||||||
log::info!("the config file is updated");
|
log::info!("the config file is updated");
|
||||||
} else {
|
} else {
|
||||||
log::info!("exiting without updating the listen port.");
|
log::info!("exiting without updating the listen port.");
|
||||||
|
@ -887,12 +909,8 @@ fn set_listen_port(
|
||||||
Ok(listen_port.flatten())
|
Ok(listen_port.flatten())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn override_endpoint(
|
fn override_endpoint(interface: &InterfaceName, opts: &Opts, unset: bool) -> Result<(), Error> {
|
||||||
interface: &InterfaceName,
|
let config = InterfaceConfig::from_interface(&opts.config_dir, interface)?;
|
||||||
unset: bool,
|
|
||||||
network: NetworkOpts,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let config = InterfaceConfig::from_interface(interface)?;
|
|
||||||
let endpoint_contents = if unset {
|
let endpoint_contents = if unset {
|
||||||
prompts::unset_override_endpoint()?.then(|| EndpointContents::Unset)
|
prompts::unset_override_endpoint()?.then(|| EndpointContents::Unset)
|
||||||
} else {
|
} else {
|
||||||
|
@ -903,7 +921,7 @@ fn override_endpoint(
|
||||||
"{}: you need to set a listen port for your interface first.",
|
"{}: you need to set a listen port for your interface first.",
|
||||||
"note".bold().yellow()
|
"note".bold().yellow()
|
||||||
);
|
);
|
||||||
set_listen_port(interface, unset, network)?
|
set_listen_port(interface, opts, unset)?
|
||||||
};
|
};
|
||||||
let endpoint = if let Some(port) = listen_port {
|
let endpoint = if let Some(port) = listen_port {
|
||||||
prompts::override_endpoint(port)?
|
prompts::override_endpoint(port)?
|
||||||
|
@ -923,23 +941,19 @@ fn override_endpoint(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(
|
fn show(opts: &Opts, short: bool, tree: bool, interface: Option<Interface>) -> Result<(), Error> {
|
||||||
short: bool,
|
|
||||||
tree: bool,
|
|
||||||
interface: Option<Interface>,
|
|
||||||
network: NetworkOpts,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let interfaces = interface.map_or_else(
|
let interfaces = interface.map_or_else(
|
||||||
|| Device::list(network.backend),
|
|| Device::list(opts.network.backend),
|
||||||
|interface| Ok(vec![*interface]),
|
|interface| Ok(vec![*interface]),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let devices = interfaces
|
let devices = interfaces
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|name| {
|
.filter_map(|name| {
|
||||||
match DataStore::open(&name) {
|
match DataStore::open(&opts.data_dir, &name) {
|
||||||
Ok(store) => {
|
Ok(store) => {
|
||||||
let device = Device::get(&name, network.backend).with_str(name.as_str_lossy());
|
let device =
|
||||||
|
Device::get(&name, opts.network.backend).with_str(name.as_str_lossy());
|
||||||
Some(device.map(|device| (device, store)))
|
Some(device.map(|device| (device, store)))
|
||||||
},
|
},
|
||||||
// Skip WireGuard interfaces that aren't managed by innernet.
|
// Skip WireGuard interfaces that aren't managed by innernet.
|
||||||
|
@ -1099,24 +1113,24 @@ fn print_peer(peer: &PeerState, short: bool, level: usize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let opt = Opts::from_args();
|
let opts = Opts::from_args();
|
||||||
util::init_logger(opt.verbose);
|
util::init_logger(opts.verbose);
|
||||||
|
|
||||||
if let Err(e) = run(opt) {
|
if let Err(e) = run(&opts) {
|
||||||
println!();
|
println!();
|
||||||
log::error!("{}\n", e);
|
log::error!("{}\n", e);
|
||||||
if let Some(e) = e.downcast_ref::<WrappedIoError>() {
|
if let Some(e) = e.downcast_ref::<WrappedIoError>() {
|
||||||
util::permissions_helptext(e);
|
util::permissions_helptext(&opts.config_dir, &opts.data_dir, e);
|
||||||
}
|
}
|
||||||
if let Some(e) = e.downcast_ref::<io::Error>() {
|
if let Some(e) = e.downcast_ref::<io::Error>() {
|
||||||
util::permissions_helptext(e);
|
util::permissions_helptext(&opts.config_dir, &opts.data_dir, e);
|
||||||
}
|
}
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(opt: Opts) -> Result<(), Error> {
|
fn run(opts: &Opts) -> Result<(), Error> {
|
||||||
let command = opt.command.unwrap_or(Command::Show {
|
let command = opts.command.clone().unwrap_or(Command::Show {
|
||||||
short: false,
|
short: false,
|
||||||
tree: false,
|
tree: false,
|
||||||
interface: None,
|
interface: None,
|
||||||
|
@ -1126,19 +1140,19 @@ fn run(opt: Opts) -> Result<(), Error> {
|
||||||
Command::Install {
|
Command::Install {
|
||||||
invite,
|
invite,
|
||||||
hosts,
|
hosts,
|
||||||
opts,
|
install_opts,
|
||||||
nat,
|
nat,
|
||||||
} => install(&invite, hosts.into(), opts, opt.network, &nat)?,
|
} => install(opts, &invite, hosts.into(), install_opts, &nat)?,
|
||||||
Command::Show {
|
Command::Show {
|
||||||
short,
|
short,
|
||||||
tree,
|
tree,
|
||||||
interface,
|
interface,
|
||||||
} => show(short, tree, interface, opt.network)?,
|
} => show(opts, short, tree, interface)?,
|
||||||
Command::Fetch {
|
Command::Fetch {
|
||||||
interface,
|
interface,
|
||||||
hosts,
|
hosts,
|
||||||
nat,
|
nat,
|
||||||
} => fetch(&interface, false, hosts.into(), opt.network, &nat)?,
|
} => fetch(&interface, opts, false, hosts.into(), &nat)?,
|
||||||
Command::Up {
|
Command::Up {
|
||||||
interface,
|
interface,
|
||||||
daemon,
|
daemon,
|
||||||
|
@ -1147,28 +1161,43 @@ fn run(opt: Opts) -> Result<(), Error> {
|
||||||
interval,
|
interval,
|
||||||
} => up(
|
} => up(
|
||||||
&interface,
|
&interface,
|
||||||
|
opts,
|
||||||
daemon.then(|| Duration::from_secs(interval)),
|
daemon.then(|| Duration::from_secs(interval)),
|
||||||
hosts.into(),
|
hosts.into(),
|
||||||
opt.network,
|
|
||||||
&nat,
|
&nat,
|
||||||
)?,
|
)?,
|
||||||
Command::Down { interface } => wg::down(&interface, opt.network.backend)?,
|
Command::Down { interface } => wg::down(&interface, opts.network.backend)?,
|
||||||
Command::Uninstall { interface } => uninstall(&interface, opt.network)?,
|
Command::Uninstall { interface } => uninstall(&interface, opts)?,
|
||||||
Command::AddPeer { interface, opts } => add_peer(&interface, opts)?,
|
Command::AddPeer {
|
||||||
Command::RenamePeer { interface, opts } => rename_peer(&interface, opts)?,
|
interface,
|
||||||
Command::AddCidr { interface, opts } => add_cidr(&interface, opts)?,
|
sub_opts,
|
||||||
Command::DeleteCidr { interface, opts } => delete_cidr(&interface, opts)?,
|
} => add_peer(&interface, opts, sub_opts)?,
|
||||||
Command::ListCidrs { interface, tree } => list_cidrs(&interface, tree)?,
|
Command::RenamePeer {
|
||||||
Command::DisablePeer { interface } => enable_or_disable_peer(&interface, false)?,
|
interface,
|
||||||
Command::EnablePeer { interface } => enable_or_disable_peer(&interface, true)?,
|
sub_opts,
|
||||||
Command::AddAssociation { interface, opts } => add_association(&interface, opts)?,
|
} => rename_peer(&interface, opts, sub_opts)?,
|
||||||
Command::DeleteAssociation { interface } => delete_association(&interface)?,
|
Command::AddCidr {
|
||||||
Command::ListAssociations { interface } => list_associations(&interface)?,
|
interface,
|
||||||
|
sub_opts,
|
||||||
|
} => add_cidr(&interface, opts, sub_opts)?,
|
||||||
|
Command::DeleteCidr {
|
||||||
|
interface,
|
||||||
|
sub_opts,
|
||||||
|
} => delete_cidr(&interface, opts, sub_opts)?,
|
||||||
|
Command::ListCidrs { interface, tree } => list_cidrs(&interface, opts, tree)?,
|
||||||
|
Command::DisablePeer { interface } => enable_or_disable_peer(&interface, opts, false)?,
|
||||||
|
Command::EnablePeer { interface } => enable_or_disable_peer(&interface, opts, true)?,
|
||||||
|
Command::AddAssociation {
|
||||||
|
interface,
|
||||||
|
sub_opts,
|
||||||
|
} => add_association(&interface, opts, sub_opts)?,
|
||||||
|
Command::DeleteAssociation { interface } => delete_association(&interface, opts)?,
|
||||||
|
Command::ListAssociations { interface } => list_associations(&interface, opts)?,
|
||||||
Command::SetListenPort { interface, unset } => {
|
Command::SetListenPort { interface, unset } => {
|
||||||
set_listen_port(&interface, unset, opt.network)?;
|
set_listen_port(&interface, opts, unset)?;
|
||||||
},
|
},
|
||||||
Command::OverrideEndpoint { interface, unset } => {
|
Command::OverrideEndpoint { interface, unset } => {
|
||||||
override_endpoint(&interface, unset, opt.network)?;
|
override_endpoint(&interface, opts, unset)?;
|
||||||
},
|
},
|
||||||
Command::Completions { shell } => {
|
Command::Completions { shell } => {
|
||||||
Opts::clap().gen_completions_to("innernet", shell, &mut std::io::stdout());
|
Opts::clap().gen_completions_to("innernet", shell, &mut std::io::stdout());
|
||||||
|
|
|
@ -4,7 +4,7 @@ use indoc::eprintdoc;
|
||||||
use log::{Level, LevelFilter};
|
use log::{Level, LevelFilter};
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use shared::{interface_config::ServerInfo, PeerDiff, INNERNET_PUBKEY_HEADER};
|
use shared::{interface_config::ServerInfo, PeerDiff, INNERNET_PUBKEY_HEADER};
|
||||||
use std::{io, time::Duration};
|
use std::{io, path::Path, time::Duration};
|
||||||
use ureq::{Agent, AgentBuilder};
|
use ureq::{Agent, AgentBuilder};
|
||||||
|
|
||||||
static LOGGER: Logger = Logger;
|
static LOGGER: Logger = Logger;
|
||||||
|
@ -102,7 +102,7 @@ pub fn human_size(bytes: u64) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn permissions_helptext(e: &io::Error) {
|
pub fn permissions_helptext(config_dir: &Path, data_dir: &Path, e: &io::Error) {
|
||||||
if e.raw_os_error() == Some(1) {
|
if e.raw_os_error() == Some(1) {
|
||||||
let current_exe = std::env::current_exe()
|
let current_exe = std::env::current_exe()
|
||||||
.ok()
|
.ok()
|
||||||
|
@ -131,8 +131,8 @@ pub fn permissions_helptext(e: &io::Error) {
|
||||||
sudo chmod -R g+rwX {config} {data}
|
sudo chmod -R g+rwX {config} {data}
|
||||||
",
|
",
|
||||||
"ERROR".bold().red(),
|
"ERROR".bold().red(),
|
||||||
config = shared::CLIENT_CONFIG_DIR.to_string_lossy(),
|
config = config_dir.to_string_lossy(),
|
||||||
data = shared::CLIENT_DATA_DIR.to_string_lossy(),
|
data = data_dir.to_string_lossy(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,7 @@ mod handlers {
|
||||||
//
|
//
|
||||||
// Related: https://github.com/hyperium/hyper/issues/2181
|
// Related: https://github.com/hyperium/hyper/issues/2181
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async move {
|
||||||
tokio::time::sleep(*REDEEM_TRANSITION_WAIT).await;
|
tokio::time::sleep(REDEEM_TRANSITION_WAIT).await;
|
||||||
log::info!(
|
log::info!(
|
||||||
"WireGuard: adding new peer {}, removing old pubkey {}",
|
"WireGuard: adding new peer {}, removing old pubkey {}",
|
||||||
&*selected_peer,
|
&*selected_peer,
|
||||||
|
|
|
@ -39,17 +39,23 @@ mod initialize;
|
||||||
use db::{DatabaseCidr, DatabasePeer};
|
use db::{DatabaseCidr, DatabasePeer};
|
||||||
pub use error::ServerError;
|
pub use error::ServerError;
|
||||||
use initialize::InitializeOpts;
|
use initialize::InitializeOpts;
|
||||||
use shared::{prompts, wg, CidrTree, Error, Interface, SERVER_CONFIG_DIR, SERVER_DATABASE_DIR};
|
use shared::{prompts, wg, CidrTree, Error, Interface};
|
||||||
pub use shared::{Association, AssociationContents};
|
pub use shared::{Association, AssociationContents};
|
||||||
|
|
||||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
#[structopt(name = "innernet-server", about, global_settings(&[AppSettings::ColoredHelp, AppSettings::DeriveDisplayOrder, AppSettings::VersionlessSubcommands, AppSettings::UnifiedHelpMessage]))]
|
#[structopt(name = "innernet-server", about, global_settings(&[AppSettings::ColoredHelp, AppSettings::DeriveDisplayOrder, AppSettings::VersionlessSubcommands, AppSettings::UnifiedHelpMessage]))]
|
||||||
struct Opt {
|
struct Opts {
|
||||||
#[structopt(subcommand)]
|
#[structopt(subcommand)]
|
||||||
command: Command,
|
command: Command,
|
||||||
|
|
||||||
|
#[structopt(short, long, default_value = "/etc/innernet-server")]
|
||||||
|
config_dir: PathBuf,
|
||||||
|
|
||||||
|
#[structopt(short, long, default_value = "/var/lib/innernet-server")]
|
||||||
|
data_dir: PathBuf,
|
||||||
|
|
||||||
#[structopt(flatten)]
|
#[structopt(flatten)]
|
||||||
network: NetworkOpts,
|
network: NetworkOpts,
|
||||||
}
|
}
|
||||||
|
@ -184,17 +190,22 @@ impl ConfigFile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ServerConfig {
|
pub struct ServerConfig {
|
||||||
wg_manage_dir_override: Option<PathBuf>,
|
pub config_dir: PathBuf,
|
||||||
wg_dir_override: Option<PathBuf>,
|
pub data_dir: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerConfig {
|
impl ServerConfig {
|
||||||
|
pub fn new(config_dir: PathBuf, data_dir: PathBuf) -> Self {
|
||||||
|
Self {
|
||||||
|
config_dir,
|
||||||
|
data_dir,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn database_dir(&self) -> &Path {
|
fn database_dir(&self) -> &Path {
|
||||||
self.wg_manage_dir_override
|
&self.data_dir
|
||||||
.as_deref()
|
|
||||||
.unwrap_or(*SERVER_DATABASE_DIR)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn database_path(&self, interface: &InterfaceName) -> PathBuf {
|
fn database_path(&self, interface: &InterfaceName) -> PathBuf {
|
||||||
|
@ -205,9 +216,7 @@ impl ServerConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn config_dir(&self) -> &Path {
|
fn config_dir(&self) -> &Path {
|
||||||
self.wg_dir_override
|
&self.config_dir
|
||||||
.as_deref()
|
|
||||||
.unwrap_or(*SERVER_CONFIG_DIR)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn config_path(&self, interface: &InterfaceName) -> PathBuf {
|
fn config_path(&self, interface: &InterfaceName) -> PathBuf {
|
||||||
|
@ -226,32 +235,32 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
let opt = Opt::from_args();
|
let opts = Opts::from_args();
|
||||||
|
|
||||||
if unsafe { libc::getuid() } != 0 && !matches!(opt.command, Command::Completions { .. }) {
|
if unsafe { libc::getuid() } != 0 && !matches!(opts.command, Command::Completions { .. }) {
|
||||||
return Err("innernet-server must run as root.".into());
|
return Err("innernet-server must run as root.".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let conf = ServerConfig::default();
|
let conf = ServerConfig::new(opts.config_dir, opts.data_dir);
|
||||||
|
|
||||||
match opt.command {
|
match opts.command {
|
||||||
Command::New { opts } => {
|
Command::New { opts } => {
|
||||||
if let Err(e) = initialize::init_wizard(&conf, opts) {
|
if let Err(e) = initialize::init_wizard(&conf, opts) {
|
||||||
eprintln!("{}: {}.", "creation failed".red(), e);
|
eprintln!("{}: {}.", "creation failed".red(), e);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Command::Uninstall { interface } => uninstall(&interface, &conf, opt.network)?,
|
Command::Uninstall { interface } => uninstall(&interface, &conf, opts.network)?,
|
||||||
Command::Serve {
|
Command::Serve {
|
||||||
interface,
|
interface,
|
||||||
network: routing,
|
network: routing,
|
||||||
} => serve(*interface, &conf, routing).await?,
|
} => serve(*interface, &conf, routing).await?,
|
||||||
Command::AddPeer { interface, args } => add_peer(&interface, &conf, args, opt.network)?,
|
Command::AddPeer { interface, args } => add_peer(&interface, &conf, args, opts.network)?,
|
||||||
Command::RenamePeer { interface, args } => rename_peer(&interface, &conf, args)?,
|
Command::RenamePeer { interface, args } => rename_peer(&interface, &conf, args)?,
|
||||||
Command::AddCidr { interface, args } => add_cidr(&interface, &conf, args)?,
|
Command::AddCidr { interface, args } => add_cidr(&interface, &conf, args)?,
|
||||||
Command::DeleteCidr { interface, args } => delete_cidr(&interface, &conf, args)?,
|
Command::DeleteCidr { interface, args } => delete_cidr(&interface, &conf, args)?,
|
||||||
Command::Completions { shell } => {
|
Command::Completions { shell } => {
|
||||||
Opt::clap().gen_completions_to("innernet-server", shell, &mut std::io::stdout());
|
Opts::clap().gen_completions_to("innernet-server", shell, &mut std::io::stdout());
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,8 +88,8 @@ impl Server {
|
||||||
// cidrs and peers.
|
// cidrs and peers.
|
||||||
let interface = "test".to_string();
|
let interface = "test".to_string();
|
||||||
let conf = ServerConfig {
|
let conf = ServerConfig {
|
||||||
wg_manage_dir_override: Some(test_dir_path.to_path_buf()),
|
config_dir: test_dir_path.to_path_buf(),
|
||||||
wg_dir_override: Some(test_dir_path.to_path_buf()),
|
data_dir: test_dir_path.to_path_buf(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let opts = InitializeOpts {
|
let opts = InitializeOpts {
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
use crate::{
|
use crate::{chmod, ensure_dirs_exist, Endpoint, Error, IoErrorContext, WrappedIoError};
|
||||||
chmod, ensure_dirs_exist, Endpoint, Error, IoErrorContext, WrappedIoError, CLIENT_CONFIG_DIR,
|
|
||||||
};
|
|
||||||
use indoc::writedoc;
|
use indoc::writedoc;
|
||||||
use ipnetwork::IpNetwork;
|
use ipnetwork::IpNetwork;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -101,8 +99,12 @@ impl InterfaceConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Overwrites the config file if it already exists.
|
/// Overwrites the config file if it already exists.
|
||||||
pub fn write_to_interface(&self, interface: &InterfaceName) -> Result<PathBuf, Error> {
|
pub fn write_to_interface(
|
||||||
let path = Self::build_config_file_path(interface)?;
|
&self,
|
||||||
|
config_dir: &Path,
|
||||||
|
interface: &InterfaceName,
|
||||||
|
) -> Result<PathBuf, Error> {
|
||||||
|
let path = Self::build_config_file_path(config_dir, interface)?;
|
||||||
File::create(&path)
|
File::create(&path)
|
||||||
.with_path(&path)?
|
.with_path(&path)?
|
||||||
.write_all(toml::to_string(self).unwrap().as_bytes())?;
|
.write_all(toml::to_string(self).unwrap().as_bytes())?;
|
||||||
|
@ -113,21 +115,24 @@ impl InterfaceConfig {
|
||||||
Ok(toml::from_slice(&std::fs::read(&path).with_path(path)?)?)
|
Ok(toml::from_slice(&std::fs::read(&path).with_path(path)?)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_interface(interface: &InterfaceName) -> Result<Self, Error> {
|
pub fn from_interface(config_dir: &Path, interface: &InterfaceName) -> Result<Self, Error> {
|
||||||
let path = Self::build_config_file_path(interface)?;
|
let path = Self::build_config_file_path(config_dir, interface)?;
|
||||||
crate::warn_on_dangerous_mode(&path).with_path(&path)?;
|
crate::warn_on_dangerous_mode(&path).with_path(&path)?;
|
||||||
Self::from_file(path)
|
Self::from_file(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_path(interface: &InterfaceName) -> PathBuf {
|
pub fn get_path(config_dir: &Path, interface: &InterfaceName) -> PathBuf {
|
||||||
CLIENT_CONFIG_DIR
|
config_dir
|
||||||
.join(interface.to_string())
|
.join(interface.to_string())
|
||||||
.with_extension("conf")
|
.with_extension("conf")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_config_file_path(interface: &InterfaceName) -> Result<PathBuf, WrappedIoError> {
|
fn build_config_file_path(
|
||||||
ensure_dirs_exist(&[*CLIENT_CONFIG_DIR])?;
|
config_dir: &Path,
|
||||||
Ok(Self::get_path(interface))
|
interface: &InterfaceName,
|
||||||
|
) -> Result<PathBuf, WrappedIoError> {
|
||||||
|
ensure_dirs_exist(&[config_dir])?;
|
||||||
|
Ok(Self::get_path(config_dir, interface))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
pub use anyhow::Error;
|
pub use anyhow::Error;
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::{self, File, Permissions},
|
fs::{self, File, Permissions},
|
||||||
io,
|
io,
|
||||||
|
@ -17,14 +16,7 @@ pub mod wg;
|
||||||
|
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
|
|
||||||
lazy_static! {
|
pub const REDEEM_TRANSITION_WAIT: Duration = Duration::from_secs(5);
|
||||||
pub static ref CLIENT_CONFIG_DIR: &'static Path = Path::new("/etc/innernet");
|
|
||||||
pub static ref CLIENT_DATA_DIR: &'static Path = Path::new("/var/lib/innernet");
|
|
||||||
pub static ref SERVER_CONFIG_DIR: &'static Path = Path::new("/etc/innernet-server");
|
|
||||||
pub static ref SERVER_DATABASE_DIR: &'static Path = Path::new("/var/lib/innernet-server");
|
|
||||||
pub static ref REDEEM_TRANSITION_WAIT: Duration = Duration::from_secs(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const PERSISTENT_KEEPALIVE_INTERVAL_SECS: u16 = 25;
|
pub const PERSISTENT_KEEPALIVE_INTERVAL_SECS: u16 = 25;
|
||||||
pub const INNERNET_PUBKEY_HEADER: &str = "X-Innernet-Server-Key";
|
pub const INNERNET_PUBKEY_HEADER: &str = "X-Innernet-Server-Key";
|
||||||
|
|
||||||
|
|
|
@ -413,6 +413,7 @@ impl NatOpts {
|
||||||
no_nat_candidates: true,
|
no_nat_candidates: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if an IP is allowed to be reported as a candidate.
|
/// Check if an IP is allowed to be reported as a candidate.
|
||||||
pub fn is_excluded(&self, ip: IpAddr) -> bool {
|
pub fn is_excluded(&self, ip: IpAddr) -> bool {
|
||||||
self.no_nat_candidates
|
self.no_nat_candidates
|
||||||
|
|
Loading…
Reference in New Issue