Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1inn 160 #3

Open
wants to merge 16 commits into
base: triton
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
788 changes: 549 additions & 239 deletions Cargo.lock

Large diffs are not rendered by default.

21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# innernet

[![Actively Maintained](https://img.shields.io/badge/Maintenance%20Level-Actively%20Maintained-green.svg)](https://gist.github.com/cheerfulstoic/d107229326a01ff0f333a1d3476e068d)
[![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/tonarino/innernet/blob/master/LICENSE)

A private network system that uses [WireGuard](https://wireguard.com) under the hood. See the [announcement blog post](https://blog.tonari.no/introducing-innernet) for a longer-winded explanation.

<img src="https://user-images.githubusercontent.com/373823/118917068-09ae7700-b96b-11eb-80f4-6860072d504d.gif" width="600" height="370">
Expand Down Expand Up @@ -195,9 +198,13 @@ It's assumed that WireGuard is installed on your system, either via the kernel m
pacman -S innernet
```

### Debian and Ubuntu

[**@tommie**](https://github.com/tommie) is kindly providing Debian/Ubuntu innernet builds in the https://github.com/tommie/innernet-debian repository.

### Other Linux Distributions

Starting with the 1.5.4 release, Debian and RPM packages are no longer built in this repository. We're looking for volunteers who are able to set up external builds for popular distributions. Please see issue [#203](https://github.com/tonarino/innernet/issues/203).
We're looking for volunteers who are able to set up external builds for popular distributions. Please see issue [#203](https://github.com/tonarino/innernet/issues/203).

### macOS

Expand All @@ -209,10 +216,10 @@ brew install tonarino/innernet/innernet

```sh
# to install innernet:
cargo install --git https://github.com/tonarino/innernet --tag v1.5.5 client
cargo install --git https://github.com/tonarino/innernet --tag v1.6.0 client

# to install innernet-server:
cargo install --git https://github.com/tonarino/innernet --tag v1.5.5 server
cargo install --git https://github.com/tonarino/innernet --tag v1.6.0 server
```

Note that you'll be responsible for updating manually.
Expand Down Expand Up @@ -248,6 +255,8 @@ The resulting binary will be located at `./target/release/innernet`

### Releases

1. Run `cargo release [--dry-run] [minor|major|patch|...]` to automatically bump the crates appropriately.
2. Create a new git tag (ex. `v0.6.0`).
3. Push (with tags) to the repo.
Please run the release script from a Linux machine: generated shell completions depend on available wireguard backends and Mac doesn't support the `kernel` backend.

1. Fetch and check-out the `main` branch.
2. Run `./release.sh [patch|major|minor|rc]`
3. Push the `main` branch and the created tag to the repo.
1 change: 0 additions & 1 deletion client/.rpm/innernet.spec
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ Requires: libgcc
%setup -q

%build
ln -s %{name} .%{_bindir}/inn

%install
rm -rf %{buildroot}
Expand Down
23 changes: 14 additions & 9 deletions client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
[package]
authors = ["Jake McGinty <[email protected]>"]
authors = [
"Jake McGinty <[email protected]>",
"Brian Schwind <[email protected]>",
"Ryo Kawaguchi <[email protected]>",
"Matěj Laitl <[email protected]>",
]
description = "A client to manage innernet network interfaces."
edition = "2021"
homepage = "https://github.com/tonarino/innernet"
license = "MIT"
name = "client"
publish = false
readme = "README.md"
repository = "https://github.com/tonarino/innernet"
version = "1.5.5"
version = "1.6.0"

[[bin]]
name = "innernet"
Expand All @@ -16,13 +22,12 @@ path = "src/main.rs"
[dependencies]
anyhow = "1"
colored = "2"
clap = { version = "3", features = ["derive"] }
clap_complete = "3"
clap = { version = "4.3", features = ["derive", "wrap_help"] }
clap_complete = "4.3"
dialoguer = { version = "0.10", default-features = false }
hostsfile = { path = "../hostsfile" }
indoc = "1"
indoc = "2.0.1"
ipnet = { version = "2.4", features = ["serde"] }
lazy_static = "1"
log = "0.4"
regex = { version = "1", default-features = false, features = ["std"] }
serde = { version = "1.0", features = ["derive"] }
Expand All @@ -32,12 +37,12 @@ ureq = { version = "2", default-features = false, features = ["json"] }
wireguard-control = { path = "../wireguard-control" }

[dev-dependencies]
once_cell = "1.17.1"
tempfile = "3"

[package.metadata.deb]
assets = [
["target/release/innernet", "usr/bin/", "755"],
["target/release/innernet", "usr/bin/inn", "755"],
["[email protected]", "usr/lib/systemd/system/", "644"],
["../doc/innernet.8.gz", "usr/share/man/man8/", "644"],
["../doc/innernet.completions.bash", "etc/bash_completion.d/innernet", "644"],
Expand All @@ -62,8 +67,8 @@ buildflags = ["--release"]
"../../doc/innernet.8.gz" = { path = "/usr/share/man/man8/innernet.8.gz" }
"../[email protected]" = { path = "/usr/lib/systemd/system/[email protected]" }
"../../doc/innernet.completions.bash" = { path = "/etc/bash_completion.d/innernet" }
"../../doc/innernet.completions.fish" = { path = "/usr/share/fish/vendor_completions.d/innernet.fish" }
"../../doc/innernet.completions.zsh" = { path = "/usr/share/zsh/site-functions/_innernet" }
"../../doc/innernet.completions.fish" = { path = "/usr/share/fish/vendor_completions.d/innernet.fish" }
"../../doc/innernet.completions.zsh" = { path = "/usr/share/zsh/site-functions/_innernet" }

[package.metadata.rpm.targets]
innernet = { path = "/usr/bin/innernet" }
22 changes: 12 additions & 10 deletions client/src/data_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,10 @@ impl DataStore {
#[cfg(test)]
mod tests {
use super::*;
use lazy_static::lazy_static;
use once_cell::sync::Lazy;
use shared::{Cidr, CidrContents, Peer, PeerContents};
lazy_static! {
static ref BASE_PEERS: Vec<Peer> = vec![Peer {
static BASE_PEERS: Lazy<Vec<Peer>> = Lazy::new(|| {
vec![Peer {
id: 0,
contents: PeerContents {
name: "blah".parse().unwrap(),
Expand All @@ -161,17 +161,19 @@ mod tests {
persistent_keepalive_interval: None,
invite_expires: None,
candidates: vec![],
}
}];
static ref BASE_CIDRS: Vec<Cidr> = vec![Cidr {
},
}]
});
static BASE_CIDRS: Lazy<Vec<Cidr>> = Lazy::new(|| {
vec![Cidr {
id: 1,
contents: CidrContents {
name: "cidr".to_string(),
cidr: "10.0.0.0/24".parse().unwrap(),
parent: None
}
}];
}
parent: None,
},
}]
});

fn setup_basic_store(dir: &Path) {
let mut store = DataStore::open_with_path(dir.join("peer_store.json"), true).unwrap();
Expand Down
39 changes: 17 additions & 22 deletions client/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anyhow::{anyhow, bail};
use clap::{AppSettings, Args, IntoApp, Parser, Subcommand};
use clap::{ArgAction, Args, Parser, Subcommand};
use colored::*;
use dialoguer::{Confirm, Input};
use hostsfile::HostsBuilder;
Expand Down Expand Up @@ -47,15 +47,14 @@ macro_rules! println_pad {
}

#[derive(Clone, Debug, Parser)]
#[clap(name = "innernet", author, version, about)]
#[clap(global_setting(AppSettings::DeriveDisplayOrder))]
#[command(name = "innernet", author, version, about)]
struct Opts {
#[clap(subcommand)]
command: Option<Command>,

/// Verbose output, use -vv for even higher verbositude
#[clap(short, long, parse(from_occurrences))]
verbose: u64,
#[clap(short, long, action = ArgAction::Count)]
verbose: u8,

#[clap(short, long, default_value = "/etc/innernet")]
config_dir: PathBuf,
Expand All @@ -74,7 +73,7 @@ struct HostsOpt {
hosts_path: PathBuf,

/// Don't write to any hosts files
#[clap(long = "no-write-hosts", conflicts_with = "hosts-path")]
#[clap(long = "no-write-hosts", conflicts_with = "hosts_path")]
no_write_hosts: bool,
}

Expand Down Expand Up @@ -254,7 +253,7 @@ enum Command {

/// Generate shell completion scripts
Completions {
#[clap(arg_enum)]
#[clap(value_enum)]
shell: clap_complete::Shell,
},
}
Expand All @@ -280,18 +279,23 @@ fn update_hosts_file(
hosts_path: PathBuf,
peers: &[Peer],
) -> Result<(), WrappedIoError> {
log::info!("updating {} with the latest peers.", "/etc/hosts".yellow());

let mut hosts_builder = HostsBuilder::new(format!("innernet {interface}"));
for peer in peers {
hosts_builder.add_hostname(
peer.contents.ip,
&format!("{}.{}.wg", peer.contents.name, interface),
);
}
if let Err(e) = hosts_builder.write_to(&hosts_path).with_path(hosts_path) {
log::warn!("failed to update hosts ({})", e);
}
match hosts_builder.write_to(&hosts_path).with_path(&hosts_path) {
Ok(has_written) if has_written => {
log::info!(
"updated {} with the latest peers.",
hosts_path.to_string_lossy().yellow()
)
},
Ok(_) => {},
Err(e) => log::warn!("failed to update hosts ({})", e),
};

Ok(())
}
Expand Down Expand Up @@ -1172,16 +1176,6 @@ fn main() {
let opts = Opts::parse();
util::init_logger(opts.verbose);

let argv0 = std::env::args().next().unwrap();
let executable = Path::new(&argv0).file_name().unwrap().to_str().unwrap();
if executable == "inn" {
log::warn!("");
log::warn!(" {}: the {} shortcut will be removed from OS packages soon in favor of users creating a shell alias.", "WARNING".bold(), "inn".yellow());
log::warn!("");
log::warn!(" See https://github.com/tonarino/innernet/issues/176 for instructions to continue using it.");
log::warn!("");
}

if let Err(e) = run(&opts) {
println!();
log::error!("{}\n", e);
Expand Down Expand Up @@ -1275,6 +1269,7 @@ fn run(opts: &Opts) -> Result<(), Error> {
override_endpoint(&interface, opts, sub_opts)?;
},
Command::Completions { shell } => {
use clap::CommandFactory;
let mut app = Opts::command();
let app_name = app.get_name().to_string();
clap_complete::generate(shell, &mut app, app_name, &mut std::io::stdout());
Expand Down
41 changes: 23 additions & 18 deletions client/src/nat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,28 @@ impl<'a> NatTraverse<'a> {
// Limit reported alternative candidates to 10.
peer.candidates.truncate(10);

// remove server-reported endpoint from elsewhere in the list if it existed.
// Remove server-reported endpoint from elsewhere in the list if it existed.
let endpoint = peer.endpoint.clone();
peer.candidates
.retain(|addr| Some(addr) != endpoint.as_ref());

// Add the server-reported endpoint to the beginning of the list. In the event
// no other endpoints worked, the remaining endpoint in the list will be the one
// assigned to the peer so it should default to the server-reported endpoint.
// This is inserted at the beginning of the Vec as candidates are popped from
// the end as the algorithm progresses.
if let Some(endpoint) = endpoint {
peer.candidates.insert(0, endpoint);
}
}
let mut nat_traverse = Self {
interface,
backend,
remaining,
};

nat_traverse.refresh_remaining()?;

Ok(nat_traverse)
}

Expand All @@ -55,10 +66,9 @@ impl<'a> NatTraverse<'a> {
self.remaining.len()
}

/// Refreshes the current state of candidate traversal attempts, returning
/// the peers that have been exhausted of all options (not included are
/// peers that have successfully connected, or peers removed from the interface).
fn refresh_remaining(&mut self) -> Result<Vec<Peer>, Error> {
/// Refreshes the current state of candidate traversal attempts, filtering out
/// the peers that have been exhausted of all endpoint options.
fn refresh_remaining(&mut self) -> Result<(), Error> {
let device = Device::get(self.interface, self.backend)?;
// Remove connected and missing peers
self.remaining.retain(|peer| {
Expand All @@ -79,21 +89,14 @@ impl<'a> NatTraverse<'a> {
false
}
});
let (exhausted, remaining): (Vec<_>, Vec<_>) = self
.remaining
.drain(..)
.partition(|peer| peer.candidates.is_empty());
self.remaining = remaining;
Ok(exhausted)

self.remaining.retain(|peer| !peer.candidates.is_empty());

Ok(())
}

pub fn step(&mut self) -> Result<(), Error> {
let exhausted = self.refresh_remaining()?;

// Reset peer endpoints that had no viable candidates back to the server-reported one, if it exists.
let reset_updates = exhausted
.into_iter()
.filter_map(|peer| set_endpoint(&peer.public_key, peer.endpoint.as_ref()));
self.refresh_remaining()?;

// Set all peers' endpoints to their next available candidate.
let candidate_updates = self.remaining.iter_mut().filter_map(|peer| {
Expand All @@ -104,7 +107,7 @@ impl<'a> NatTraverse<'a> {
set_endpoint(&peer.public_key, endpoint.as_ref())
});

let updates: Vec<_> = reset_updates.chain(candidate_updates).collect();
let updates: Vec<_> = candidate_updates.collect();

DeviceUpdate::new()
.add_peers(&updates)
Expand All @@ -113,12 +116,14 @@ impl<'a> NatTraverse<'a> {
let start = Instant::now();
while start.elapsed() < STEP_INTERVAL {
self.refresh_remaining()?;

if self.is_finished() {
log::debug!("NAT traverser is finished!");
break;
}
std::thread::sleep(Duration::from_millis(100));
}

Ok(())
}
}
Expand Down
Loading