iroh_ssh/
api.rs

1use std::{path::PathBuf, str::FromStr as _};
2
3use anyhow::bail;
4use homedir::my_home;
5use iroh::{NodeId, SecretKey};
6
7use crate::{dot_ssh,  IrohSsh};
8
9pub async fn info_mode() -> anyhow::Result<()> {
10    let server_key = match dot_ssh(&SecretKey::generate(rand::rngs::OsRng), false,false) {
11        Ok(key) => Some(key),
12        Err(_) => None,
13    };
14    let service_key = match dot_ssh(&SecretKey::generate(rand::rngs::OsRng), false,true) {
15        Ok(key) => Some(key),
16        Err(_) => None,
17    };
18    
19    if server_key.is_none()  && service_key.is_none() {
20        println!("No keys found, run for server or service:\n  'iroh-ssh server --persist' or '-p' to create it");
21        println!();
22        println!("(if an iroh-ssh instance is currently running, it is using ephemeral keys)");
23        bail!("No keys found")
24    }
25
26    println!("iroh-ssh version {}", env!("CARGO_PKG_VERSION"));
27    println!("https://github.com/rustonbsd/iroh-ssh");
28    println!("");
29
30    if server_key.is_none() && service_key.is_none() {
31        println!("run 'iroh-ssh server --persist' to start the server with persistent keys");
32        println!("run 'iroh-ssh server' to start the server with ephemeral keys");
33        println!(
34            "run 'iroh-ssh service install' to copy the binary, install the service and start the server (always uses persistent keys)"
35        );
36    }
37    
38    if let Some(key) = server_key {
39        println!("");
40        println!("Your server iroh-ssh nodeid:");
41        println!("  iroh-ssh {}@{}", whoami::username(), key.clone().public().to_string());
42        println!("");
43    }
44    
45    if let Some(key) = service_key {
46        println!("");
47        println!("Your service iroh-ssh nodeid:");
48        println!("  iroh-ssh {}@{}", whoami::username(), key.clone().public().to_string());
49        println!("");
50    }
51    
52    Ok(())
53}
54
55pub mod service {
56    use crate::{install_service, uninstall_service, ServiceParams};
57
58    pub async fn install(ssh_port: u16) -> anyhow::Result<()> {
59        if install_service(ServiceParams { ssh_port }).await.is_err() {
60            println!("service install is only supported on linux and windows");
61            anyhow::bail!("service install is only supported on linux and windows");
62        }
63        Ok(())
64    }
65
66    pub async fn uninstall() -> anyhow::Result<()> {
67        if uninstall_service().await.is_err() {
68            println!("service uninstall is only supported on linux or windows");
69            anyhow::bail!("service uninstall is only supported on linux or windows");
70        }
71        Ok(())
72    }
73}
74
75pub async fn server_mode(ssh_port: u16, persist: bool) -> anyhow::Result<()> {
76    let mut iroh_ssh_builder = IrohSsh::new().accept_incoming(true).accept_port(ssh_port);
77    if persist {
78        iroh_ssh_builder = iroh_ssh_builder.dot_ssh_integration(true,false);
79    }
80    let iroh_ssh = iroh_ssh_builder.build().await?;
81
82    println!("Connect to this this machine:");
83    println!("\n  iroh-ssh {}@{}\n", whoami::username(), iroh_ssh.node_id());
84    if persist {
85        
86        let distro_home = my_home()?.ok_or_else(|| anyhow::anyhow!("home directory not found"))?;
87        let ssh_dir = distro_home.join(".ssh");
88        println!("  (using persistent keys in {})", ssh_dir.display());
89    } else {
90        println!("  warning: (using ephemeral keys, run 'iroh-ssh server --persist' to create persistent keys)");
91    }
92    println!("");
93    println!(
94        "client -> iroh-ssh -> direct connect -> iroh-ssh -> local ssh :{}",
95        ssh_port
96    );
97
98    println!("Waiting for incoming connections...");
99    println!("Press Ctrl+C to exit");
100    tokio::signal::ctrl_c().await?;
101    Ok(())
102}
103
104pub async fn client_mode(target: String, identity_file: Option<PathBuf>) -> anyhow::Result<()> {
105    let (ssh_user, iroh_node_id) = parse_iroh_target(&target)?;
106    let iroh_ssh = IrohSsh::new().accept_incoming(false).build().await?;
107    let mut ssh_process = iroh_ssh.connect(&ssh_user, iroh_node_id, identity_file).await?;
108
109    ssh_process.wait().await?;
110
111    Ok(())
112}
113
114fn parse_iroh_target(target: &str) -> anyhow::Result<(String, NodeId)> {
115    let (user, node_id_str) = target
116        .split_once('@')
117        .ok_or_else(|| anyhow::anyhow!("Invalid format, use user@node_id"))?;
118    let node_id = NodeId::from_str(node_id_str)?;
119    Ok((user.to_string(), node_id))
120}