1use anyhow::{anyhow, bail};
2use colored::*;
3use db::{DatabaseCidr, DatabasePeer};
4use dialoguer::Confirm;
5use hyper::{http, server::conn::AddrStream, Body, Request, Response};
6use indoc::printdoc;
7use innernet_shared::{
8 get_local_addrs,
9 interface_config::{InterfaceInfo, PeerInvitation, ServerInfo},
10 prompts, update_hosts_file, wg, AddCidrOpts, AddPeerOpts, CidrTree, DeleteCidrOpts,
11 EnableDisablePeerOpts, Endpoint, Error, HostsOpts, Interface, IoErrorContext, NetworkOpts,
12 PeerContents, RenameCidrOpts, RenamePeerOpts, INNERNET_PUBKEY_HEADER,
13};
14use ipnet::IpNet;
15use parking_lot::{Mutex, RwLock};
16use rusqlite::Connection;
17use serde::{Deserialize, Serialize};
18use std::{
19 collections::{HashMap, VecDeque},
20 convert::TryInto,
21 env,
22 fs::File,
23 io::prelude::*,
24 net::{IpAddr, SocketAddr, TcpListener},
25 ops::Deref,
26 path::{Path, PathBuf},
27 sync::Arc,
28 time::Duration,
29};
30use subtle::ConstantTimeEq;
31use wireguard_control::{
32 Backend, Device, DeviceUpdate, InterfaceName, Key, KeyPair, PeerConfigBuilder,
33};
34
35mod api;
36mod db;
37mod error;
38pub mod initialize;
39#[cfg(test)]
40mod test;
41mod util;
42
43pub use error::ServerError;
44
45const VERSION: &str = env!("CARGO_PKG_VERSION");
46
47type Db = Arc<Mutex<Connection>>;
48type Endpoints = Arc<RwLock<HashMap<String, SocketAddr>>>;
49
50#[derive(Clone)]
51pub struct Context {
52 pub db: Db,
53 pub endpoints: Endpoints,
54 pub interface: InterfaceName,
55 pub backend: Backend,
56 pub public_key: Key,
57}
58
59pub struct Session {
60 pub context: Context,
61 pub peer: DatabasePeer,
62}
63
64impl Session {
65 pub fn admin_capable(&self) -> bool {
66 self.peer.is_admin && self.user_capable()
67 }
68
69 pub fn user_capable(&self) -> bool {
70 !self.peer.is_disabled && self.peer.is_redeemed
71 }
72
73 pub fn redeemable(&self) -> bool {
74 !self.peer.is_disabled && !self.peer.is_redeemed
75 }
76}
77
78#[derive(Deserialize, Serialize, Debug)]
79#[serde(rename_all = "kebab-case")]
80pub struct ConfigFile {
81 pub private_key: String,
83
84 pub listen_port: u16,
86
87 pub address: IpAddr,
89
90 pub network_cidr_prefix: u8,
92}
93
94impl ConfigFile {
95 pub fn write_to_path<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
96 let mut invitation_file = File::create(&path).with_path(&path)?;
97 innernet_shared::chmod(&invitation_file, 0o600)?;
98 invitation_file
99 .write_all(toml::to_string(self).unwrap().as_bytes())
100 .with_path(path)?;
101 Ok(())
102 }
103
104 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
105 let path = path.as_ref();
106 let file = File::open(path).with_path(path)?;
107 if innernet_shared::chmod(&file, 0o600)? {
108 println!(
109 "{} updated permissions for {} to 0600.",
110 "[!]".yellow(),
111 path.display()
112 );
113 }
114 Ok(toml::from_str(
115 &std::fs::read_to_string(path).with_path(path)?,
116 )?)
117 }
118}
119
120#[derive(Clone, Debug)]
121pub struct ServerConfig {
122 pub config_dir: PathBuf,
123 pub data_dir: PathBuf,
124}
125
126impl ServerConfig {
127 pub fn new(config_dir: PathBuf, data_dir: PathBuf) -> Self {
128 Self {
129 config_dir,
130 data_dir,
131 }
132 }
133
134 pub fn database_dir(&self) -> &Path {
135 &self.data_dir
136 }
137
138 pub fn database_path(&self, interface: &InterfaceName) -> PathBuf {
139 PathBuf::new()
140 .join(self.database_dir())
141 .join(interface.to_string())
142 .with_extension("db")
143 }
144
145 pub fn config_dir(&self) -> &Path {
146 &self.config_dir
147 }
148
149 pub fn config_path(&self, interface: &InterfaceName) -> PathBuf {
150 PathBuf::new()
151 .join(self.config_dir())
152 .join(interface.to_string())
153 .with_extension("conf")
154 }
155}
156
157fn open_database_connection(
158 interface: &InterfaceName,
159 conf: &ServerConfig,
160) -> Result<rusqlite::Connection, Error> {
161 let database_path = conf.database_path(interface);
162 if !Path::new(&database_path).exists() {
163 bail!(
164 "no database file found at {}",
165 database_path.to_string_lossy()
166 );
167 }
168
169 let conn = Connection::open(&database_path)?;
171 conn.pragma_update(None, "foreign_keys", 1)?;
173 db::auto_migrate(&conn)?;
174 Ok(conn)
175}
176
177pub fn add_peer(
178 interface: &InterfaceName,
179 conf: &ServerConfig,
180 opts: AddPeerOpts,
181 network: NetworkOpts,
182) -> Result<(), Error> {
183 let config = ConfigFile::from_file(conf.config_path(interface))?;
184 let conn = open_database_connection(interface, conf)?;
185 let peers = DatabasePeer::list(&conn)?
186 .into_iter()
187 .map(|dp| dp.inner)
188 .collect::<Vec<_>>();
189 let cidrs = DatabaseCidr::list(&conn)?;
190 let cidr_tree = CidrTree::new(&cidrs[..]);
191
192 if let Some((new_peer_info, target_path)) =
193 innernet_shared::prompts::gather_new_peer_info(&peers, &cidr_tree, &opts)?
194 {
195 let keypair = KeyPair::generate();
196 let peer_contents = new_peer_info.into_peer_contents(&keypair);
197 let peer = DatabasePeer::create(&conn, peer_contents)?;
198 if cfg!(not(test)) && Device::get(interface, network.backend).is_ok() {
199 DeviceUpdate::new()
201 .add_peer(PeerConfigBuilder::from(&*peer))
202 .apply(interface, network.backend)
203 .map_err(|_| ServerError::WireGuard)?;
204
205 println!("adding to WireGuard interface: {}", &*peer);
206 }
207
208 let address = cidr_tree
209 .ip_net_for(peer.ip)
210 .expect("Peer's IpNet address to be valid because the peer was created successfully.");
211 let interface_info = InterfaceInfo::new(interface, &keypair, address);
212
213 let internal_endpoint = SocketAddr::new(config.address, config.listen_port);
214 let server_peer = DatabasePeer::get(&conn, 1)?;
215 let server_info = ServerInfo::new(&server_peer, internal_endpoint);
216
217 let invitation = PeerInvitation::new(interface_info, server_info);
218 invitation.save_new(target_path)?;
219 } else {
220 println!("exited without creating peer.");
221 }
222
223 Ok(())
224}
225
226pub fn rename_peer(
227 interface: &InterfaceName,
228 conf: &ServerConfig,
229 opts: RenamePeerOpts,
230) -> Result<(), Error> {
231 let conn = open_database_connection(interface, conf)?;
232 let peers = DatabasePeer::list(&conn)?
233 .into_iter()
234 .map(|dp| dp.inner)
235 .collect::<Vec<_>>();
236
237 if let Some((peer_request, old_name)) = innernet_shared::prompts::rename_peer(&peers, &opts)? {
238 let mut db_peer = DatabasePeer::list(&conn)?
239 .into_iter()
240 .find(|p| p.name == old_name)
241 .ok_or_else(|| anyhow!("Peer not found."))?;
242 db_peer.update(&conn, peer_request)?;
243 } else {
244 println!("exited without creating peer.");
245 }
246
247 Ok(())
248}
249
250pub fn enable_or_disable_peer(
251 interface: &InterfaceName,
252 conf: &ServerConfig,
253 enable: bool,
254 network: NetworkOpts,
255 opts: EnableDisablePeerOpts,
256) -> Result<(), Error> {
257 let conn = open_database_connection(interface, conf)?;
258 let peers = DatabasePeer::list(&conn)?
259 .into_iter()
260 .map(|dp| dp.inner)
261 .collect::<Vec<_>>();
262
263 if let Some(peer) = prompts::enable_or_disable_peer(&peers[..], &opts, enable)? {
264 let mut db_peer = DatabasePeer::get(&conn, peer.id)?;
265 db_peer.update(
266 &conn,
267 PeerContents {
268 is_disabled: !enable,
269 ..peer.contents.clone()
270 },
271 )?;
272
273 if enable {
274 DeviceUpdate::new()
275 .add_peer(db_peer.deref().into())
276 .apply(interface, network.backend)
277 .map_err(|_| ServerError::WireGuard)?;
278 } else {
279 let public_key =
280 Key::from_base64(&peer.public_key).map_err(|_| ServerError::WireGuard)?;
281
282 DeviceUpdate::new()
283 .remove_peer_by_key(&public_key)
284 .apply(interface, network.backend)
285 .map_err(|_| ServerError::WireGuard)?;
286 }
287 } else {
288 log::info!("exiting without enabling or disabling peer.");
289 }
290
291 Ok(())
292}
293
294pub fn add_cidr(
295 interface: &InterfaceName,
296 conf: &ServerConfig,
297 opts: AddCidrOpts,
298) -> Result<(), Error> {
299 let conn = open_database_connection(interface, conf)?;
300 let cidrs = DatabaseCidr::list(&conn)?;
301 if let Some(cidr_request) = innernet_shared::prompts::add_cidr(&cidrs, &opts)? {
302 let cidr = DatabaseCidr::create(&conn, cidr_request)?;
303 printdoc!(
304 "
305 CIDR \"{cidr_name}\" added.
306
307 Right now, peers within {cidr_name} can only see peers in the same CIDR, and in
308 the special \"innernet-server\" CIDR that includes the innernet server peer.
309
310 You'll need to add more associations for peers in diffent CIDRs to communicate.
311 ",
312 cidr_name = cidr.name.bold()
313 );
314 } else {
315 println!("exited without creating CIDR.");
316 }
317
318 Ok(())
319}
320
321pub fn rename_cidr(
322 interface: &InterfaceName,
323 conf: &ServerConfig,
324 opts: RenameCidrOpts,
325) -> Result<(), Error> {
326 let conn = open_database_connection(interface, conf)?;
327 let cidrs = DatabaseCidr::list(&conn)?;
328
329 if let Some((cidr_request, old_name)) = innernet_shared::prompts::rename_cidr(&cidrs, &opts)? {
330 let db_cidr = DatabaseCidr::list(&conn)?
331 .into_iter()
332 .find(|c| c.name == old_name)
333 .ok_or_else(|| anyhow!("CIDR not found."))?;
334 db::DatabaseCidr::from(db_cidr).update(&conn, cidr_request)?;
335 } else {
336 println!("exited without renaming CIDR.");
337 }
338
339 Ok(())
340}
341
342pub fn delete_cidr(
343 interface: &InterfaceName,
344 conf: &ServerConfig,
345 args: DeleteCidrOpts,
346) -> Result<(), Error> {
347 println!("Fetching eligible CIDRs");
348 let conn = open_database_connection(interface, conf)?;
349 let cidrs = DatabaseCidr::list(&conn)?;
350 let peers = DatabasePeer::list(&conn)?
351 .into_iter()
352 .map(|dp| dp.inner)
353 .collect::<Vec<_>>();
354
355 let cidr_id = prompts::delete_cidr(&cidrs, &peers, &args)?;
356
357 println!("Deleting CIDR...");
358 DatabaseCidr::delete(&conn, cidr_id)?;
359
360 println!("CIDR deleted.");
361
362 Ok(())
363}
364
365pub fn uninstall(
366 interface: &InterfaceName,
367 conf: &ServerConfig,
368 network: NetworkOpts,
369 yes: bool,
370) -> Result<(), Error> {
371 if yes
372 || Confirm::with_theme(&*prompts::THEME)
373 .with_prompt(format!(
374 "Permanently delete network \"{}\"?",
375 interface.as_str_lossy().yellow()
376 ))
377 .default(false)
378 .interact()?
379 {
380 println!("{} bringing down interface (if up).", "[*]".dimmed());
381 wg::down(interface, network.backend).ok();
382 let config = conf.config_path(interface);
383 let data = conf.database_path(interface);
384 std::fs::remove_file(&config)
385 .with_path(&config)
386 .map_err(|e| println!("[!] {}", e.to_string().yellow()))
387 .ok();
388 std::fs::remove_file(&data)
389 .with_path(&data)
390 .map_err(|e| println!("[!] {}", e.to_string().yellow()))
391 .ok();
392 println!(
393 "{} network {} is uninstalled.",
394 "[*]".dimmed(),
395 interface.as_str_lossy().yellow()
396 );
397 }
398 Ok(())
399}
400
401fn spawn_endpoint_refresher(interface: InterfaceName, network: NetworkOpts) -> Endpoints {
402 let endpoints = Arc::new(RwLock::new(HashMap::new()));
403 tokio::task::spawn({
404 let endpoints = endpoints.clone();
405 async move {
406 let mut interval = tokio::time::interval(Duration::from_secs(10));
407 loop {
408 interval.tick().await;
409 if let Ok(info) = Device::get(&interface, network.backend) {
410 for peer in info.peers {
411 if let Some(endpoint) = peer.config.endpoint {
412 endpoints
413 .write()
414 .insert(peer.config.public_key.to_base64(), endpoint);
415 }
416 }
417 }
418 }
419 }
420 });
421 endpoints
422}
423
424fn spawn_expired_invite_sweeper(db: Db) {
425 tokio::task::spawn(async move {
426 let mut interval = tokio::time::interval(Duration::from_secs(10));
427 loop {
428 interval.tick().await;
429 match DatabasePeer::delete_expired_invites(&db.lock()) {
430 Ok(deleted) if deleted > 0 => {
431 log::info!("Deleted {} expired peer invitations.", deleted)
432 },
433 Err(e) => log::error!("Failed to delete expired peer invitations: {}", e),
434 _ => {},
435 }
436 }
437 });
438}
439
440fn spawn_hostfile_writer(db: Db, interface: InterfaceName, hosts_opts: HostsOpts) {
441 tokio::task::spawn({
442 async move {
443 let mut interval = tokio::time::interval(Duration::from_secs(10));
444 loop {
445 interval.tick().await;
446
447 match DatabasePeer::list_enabled(&db.lock()) {
448 Ok(peers) => {
449 if let Err(e) = update_hosts_file(
450 &interface,
451 &hosts_opts,
452 peers.into_iter().map(|peer| peer.inner),
453 ) {
454 log::error!("Failed to write hostfile: {}", e);
455 }
456 },
457 Err(e) => {
458 log::error!("Failed to list enabled peers for hostfile writing: {}", e)
459 },
460 }
461 }
462 }
463 });
464}
465
466pub async fn serve(
467 interface: InterfaceName,
468 conf: &ServerConfig,
469 network: NetworkOpts,
470 hosts_opts: HostsOpts,
471) -> Result<(), Error> {
472 let config = ConfigFile::from_file(conf.config_path(&interface))?;
473 log::debug!("opening database connection...");
474 let conn = open_database_connection(&interface, conf)?;
475
476 let mut peers = DatabasePeer::list(&conn)?;
477 log::debug!("peers listed...");
478 let peer_configs = peers
479 .iter()
480 .map(|peer| peer.deref().into())
481 .collect::<Vec<PeerConfigBuilder>>();
482
483 log::info!("bringing up interface.");
484 wg::up(
485 &interface,
486 &config.private_key,
487 IpNet::new(config.address, config.network_cidr_prefix)?,
488 Some(config.listen_port),
489 None,
490 &network,
491 )?;
492
493 DeviceUpdate::new()
494 .add_peers(&peer_configs)
495 .apply(&interface, network.backend)?;
496
497 log::info!("{} peers added to wireguard interface.", peers.len());
498
499 let candidates: Vec<Endpoint> = get_local_addrs()?
500 .map(|addr| SocketAddr::from((addr, config.listen_port)).into())
501 .collect();
502 let num_candidates = candidates.len();
503 let myself = peers
504 .iter_mut()
505 .find(|peer| peer.ip == config.address)
506 .expect("Couldn't find server peer in peer list.");
507 myself.update(
508 &conn,
509 PeerContents {
510 candidates,
511 ..myself.contents.clone()
512 },
513 )?;
514
515 log::info!(
516 "{} local candidates added to server peer config.",
517 num_candidates
518 );
519
520 let public_key = wireguard_control::Key::from_base64(&config.private_key)?.get_public();
521 let db = Arc::new(Mutex::new(conn));
522 let endpoints = spawn_endpoint_refresher(interface, network);
523 spawn_expired_invite_sweeper(db.clone());
524
525 if !hosts_opts.no_write_hosts {
526 spawn_hostfile_writer(db.clone(), interface, hosts_opts);
527 }
528
529 let context = Context {
530 db,
531 endpoints,
532 interface,
533 public_key,
534 backend: network.backend,
535 };
536
537 log::info!("innernet-server {} starting.", VERSION);
538
539 let listener = get_listener((config.address, config.listen_port).into(), &interface)?;
540
541 let make_svc = hyper::service::make_service_fn(move |socket: &AddrStream| {
542 let remote_addr = socket.remote_addr();
543 let context = context.clone();
544 async move {
545 Ok::<_, http::Error>(hyper::service::service_fn(move |req: Request<Body>| {
546 log::debug!("{} - {} {}", &remote_addr, req.method(), req.uri());
547 hyper_service(req, context.clone(), remote_addr)
548 }))
549 }
550 });
551
552 let server = hyper::Server::from_tcp(listener)?.serve(make_svc);
553
554 server.await?;
555
556 Ok(())
557}
558
559#[cfg(target_os = "linux")]
566fn get_listener(addr: SocketAddr, interface: &InterfaceName) -> Result<TcpListener, Error> {
567 let listener = TcpListener::bind(addr)?;
568 listener.set_nonblocking(true)?;
569 let sock = socket2::Socket::from(listener);
570 sock.bind_device(Some(interface.as_str_lossy().as_bytes()))?;
571 Ok(sock.into())
572}
573
574#[cfg(not(target_os = "linux"))]
581fn get_listener(addr: SocketAddr, _interface: &InterfaceName) -> Result<TcpListener, Error> {
582 let listener = TcpListener::bind(addr)?;
583 listener.set_nonblocking(true)?;
584 Ok(listener)
585}
586
587pub(crate) async fn hyper_service(
588 req: Request<Body>,
589 context: Context,
590 remote_addr: SocketAddr,
591) -> Result<Response<Body>, http::Error> {
592 let components: VecDeque<_> = req
594 .uri()
595 .path()
596 .trim_start_matches('/')
597 .split('/')
598 .map(String::from)
599 .collect();
600
601 routes(req, context, remote_addr, components)
602 .await
603 .or_else(TryInto::try_into)
604}
605
606async fn routes(
607 req: Request<Body>,
608 context: Context,
609 remote_addr: SocketAddr,
610 mut components: VecDeque<String>,
611) -> Result<Response<Body>, ServerError> {
612 if components.pop_front().as_deref() != Some("v1") {
614 Err(ServerError::NotFound)
615 } else {
616 let session = get_session(&req, context, remote_addr.ip())?;
617 let component = components.pop_front();
618 match component.as_deref() {
619 Some("user") => api::user::routes(req, components, session).await,
620 Some("admin") => api::admin::routes(req, components, session).await,
621 _ => Err(ServerError::NotFound),
622 }
623 }
624}
625
626fn get_session(
627 req: &Request<Body>,
628 context: Context,
629 addr: IpAddr,
630) -> Result<Session, ServerError> {
631 let pubkey = req
632 .headers()
633 .get(INNERNET_PUBKEY_HEADER)
634 .ok_or(ServerError::Unauthorized)?;
635 let pubkey = pubkey.to_str().map_err(|_| ServerError::Unauthorized)?;
636 let pubkey = Key::from_base64(pubkey).map_err(|_| ServerError::Unauthorized)?;
637 if pubkey
638 .as_bytes()
639 .ct_eq(context.public_key.as_bytes())
640 .into()
641 {
642 let peer = DatabasePeer::get_from_ip(&context.db.lock(), addr).map_err(|e| match e {
643 rusqlite::Error::QueryReturnedNoRows => ServerError::Unauthorized,
644 e => ServerError::Database(e),
645 })?;
646
647 if !peer.is_disabled {
648 return Ok(Session { context, peer });
649 }
650 }
651
652 Err(ServerError::Unauthorized)
653}
654
655#[cfg(test)]
656mod tests {
657 use super::*;
658 use crate::test;
659 use anyhow::Result;
660 use hyper::StatusCode;
661 use std::path::Path;
662
663 #[test]
664 fn test_init_wizard() -> Result<(), Error> {
665 let server = test::Server::new()?;
667
668 assert!(Path::new(&server.wg_conf_path()).exists());
669
670 Ok(())
671 }
672
673 #[tokio::test]
674 async fn test_with_session_disguised_with_headers() -> Result<(), Error> {
675 let server = test::Server::new()?;
676
677 let path = if cfg!(feature = "v6-test") {
678 format!("http://[{}]/v1/admin/peers", test::WG_MANAGE_PEER_IP)
679 } else {
680 format!("http://{}/v1/admin/peers", test::WG_MANAGE_PEER_IP)
681 };
682 let req = Request::builder()
683 .uri(path)
684 .header("Forwarded", format!("for={}", test::ADMIN_PEER_IP))
685 .header("X-Forwarded-For", test::ADMIN_PEER_IP)
686 .header("X-Real-IP", test::ADMIN_PEER_IP)
687 .body(Body::empty())
688 .unwrap();
689
690 let res = if cfg!(feature = "v6-test") {
692 server.raw_request("fd00:1337::1337", req).await
693 } else {
694 server.raw_request("10.80.80.80", req).await
695 };
696
697 assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
701
702 Ok(())
703 }
704
705 #[tokio::test]
706 async fn test_incorrect_public_key() -> Result<(), Error> {
707 let server = test::Server::new()?;
708
709 let key = Key::generate_private().get_public();
710
711 let path = if cfg!(feature = "v6-test") {
712 format!("http://[{}]/v1/admin/peers", test::WG_MANAGE_PEER_IP)
713 } else {
714 format!("http://{}/v1/admin/peers", test::WG_MANAGE_PEER_IP)
715 };
716 let req = Request::builder()
718 .uri(path)
719 .header(innernet_shared::INNERNET_PUBKEY_HEADER, key.to_base64())
720 .body(Body::empty())
721 .unwrap();
722 let res = if cfg!(feature = "v6-test") {
723 server.raw_request("fd00:1337::1337", req).await
724 } else {
725 server.raw_request("10.80.80.80", req).await
726 };
727
728 assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
732
733 Ok(())
734 }
735
736 #[tokio::test]
737 async fn test_unparseable_public_key() -> Result<(), Error> {
738 let server = test::Server::new()?;
739
740 let path = if cfg!(feature = "v6-test") {
741 format!("http://[{}]/v1/admin/peers", test::WG_MANAGE_PEER_IP)
742 } else {
743 format!("http://{}/v1/admin/peers", test::WG_MANAGE_PEER_IP)
744 };
745 let req = Request::builder()
746 .uri(path)
747 .header(innernet_shared::INNERNET_PUBKEY_HEADER, "!!!")
748 .body(Body::empty())
749 .unwrap();
750 let res = if cfg!(feature = "v6-test") {
751 server.raw_request("fd00:1337::1337", req).await
752 } else {
753 server.raw_request("10.80.80.80", req).await
754 };
755
756 assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
760
761 Ok(())
762 }
763}