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}