Skip to main content

innernet_server/
lib.rs

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    /// The server's WireGuard key
82    pub private_key: String,
83
84    /// The listen port of the server
85    pub listen_port: u16,
86
87    /// The internal WireGuard IP address assigned to the server
88    pub address: IpAddr,
89
90    /// The CIDR prefix of the WireGuard network
91    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    // TODO(strohel): warn about too open permissions on the db file?
170    let conn = Connection::open(&database_path)?;
171    // Foreign key constraints aren't on in SQLite by default. Enable.
172    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            // Update the current WireGuard interface with the new peers.
200            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/// This function differs per OS, because different operating systems have
560/// opposing characteristics when binding to a specific IP address.
561/// On Linux, binding to a specific local IP address does *not* bind it to
562/// that IP's interface, allowing for spoofing attacks.
563///
564/// See https://github.com/tonarino/innernet/issues/26 for more details.
565#[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/// BSD-likes do seem to bind to an interface when binding to an IP,
575/// according to the internet, but we may want to explicitly use
576/// IP_BOUND_IF in the future regardless. This isn't currently in
577/// the socket2 crate however, so we aren't currently using it.
578///
579/// See https://github.com/tonarino/innernet/issues/26 for more details.
580#[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    // Break the path into components.
593    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    // Must be "/v1/[something]"
613    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        // This runs init_wizard().
666        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        // Request from an unknown IP, trying to disguise as an admin using HTTP headers.
691        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        // addr::remote() filter only look at remote_addr from TCP socket.
698        // HTTP headers are not considered. This also means that innernet
699        // server would not function behind an HTTP proxy.
700        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        // Request from an unknown IP, trying to disguise as an admin using HTTP headers.
717        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        // addr::remote() filter only look at remote_addr from TCP socket.
729        // HTTP headers are not considered. This also means that innernet
730        // server would not function behind an HTTP proxy.
731        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        // addr::remote() filter only look at remote_addr from TCP socket.
757        // HTTP headers are not considered. This also means that innernet
758        // server would not function behind an HTTP proxy.
759        assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
760
761        Ok(())
762    }
763}