Skip to content

issue-11 fix; #13

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

Merged
merged 3 commits into from
Jul 29, 2025
Merged
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
20 changes: 19 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ repository = "https://github.com/rustonbsd/iroh-ssh"
readme = "README.md"
keywords = ["networking"]
categories = ["network-programming"]
version = "0.2.3"
version = "0.2.4"
edition = "2024"

[dependencies]
Expand All @@ -26,6 +26,7 @@ tokio = { version = "1", features = [
] }
clap = { version = "4.5.41", features = ["derive"] }
homedir = "0.3.6"
whoami = "1.6.0"
z32 = "1.3"
runas = "1.2.0"
tempfile = "3.20.0"
Expand Down
25 changes: 12 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,23 @@ Download and setup the binary automatically for your operating system from [GitH
Linux
```bash
# Linux
wget https://github.com/rustonbsd/iroh-ssh/releases/download/0.2.3/iroh-ssh.linux
wget https://github.com/rustonbsd/iroh-ssh/releases/download/0.2.4/iroh-ssh.linux
chmod +x iroh-ssh.linux
sudo mv iroh-ssh.linux /usr/local/bin/iroh-ssh
```

macOS
```bash
# macOS arm
curl -LJO https://github.com/rustonbsd/iroh-ssh/releases/download/0.2.3/iroh-ssh.macos
curl -LJO https://github.com/rustonbsd/iroh-ssh/releases/download/0.2.4/iroh-ssh.macos
chmod +x iroh-ssh.macos
sudo mv iroh-ssh.macos /usr/local/bin/iroh-ssh
```

Windows
```bash
# Windows x86 64bit
curl -L -o iroh-ssh.exe https://github.com/rustonbsd/iroh-ssh/releases/download/0.2.3/iroh-ssh.exe
curl -L -o iroh-ssh.exe https://github.com/rustonbsd/iroh-ssh/releases/download/0.2.4/iroh-ssh.exe
mkdir %LOCALAPPDATA%\iroh-ssh
move iroh-ssh.exe %LOCALAPPDATA%\iroh-ssh\
setx PATH "%PATH%;%LOCALAPPDATA%\iroh-ssh"
Expand Down Expand Up @@ -137,15 +137,14 @@ Display its Node ID and share it to allow connection
> iroh-ssh info

Your iroh-ssh nodeid: 38b7dc10df96005255c3beaeaeef6cfebd88344aa8c85e1dbfc1ad5e50f372ac
iroh-ssh version 0.2.2
iroh-ssh version 0.2.4
https://github.com/rustonbsd/iroh-ssh

run 'iroh-ssh server --persist' to start the server with persistent keys
run 'iroh-ssh server' to start the server with ephemeral keys
run 'iroh-ssh service install' to start the server as a service (always uses persistent keys)

Your iroh-ssh nodeid:
Your server iroh-ssh nodeid:
iroh-ssh my-user@38b7dc10df96005255c3beaeaeef6cfebd88344aa8c85e1dbfc1ad5e50f372ac

Your service iroh-ssh nodeid:
iroh-ssh my-user@4fjeeiui4jdm96005255c3begj389xk3aeaeef6cfebd88344aa8c85e1dbfc1ad
```

---
Expand All @@ -155,15 +154,15 @@ Display its Node ID and share it to allow connection
## How It Works

```
┌─────────────┐ ┌──────────────┐ ┌─────────────────┐ ┌─────────────┐
┌─────────────┐ ┌──────────────┐ ┌─────────────────┐ ┌─────────────┐
│ iroh-ssh │───▶│ internal TCP │───▶│ QUIC Tunnel │───▶│ iroh-ssh │
│ (your machine) │ Listener │ │ (P2P Network) │ │ server │
└─────────────┘ | (your machine) └─────────────────┘ └─────────────┘
│ (your machine) │ Listener │ │ (P2P Network) │ │ server │
└─────────────┘ | (your machine) └─────────────────┘ └─────────────┘
└──────────────┘
│ ▲ │
▼ │ ▼
┌──────────────┐ ┌─────────────┐
⦜ -- ▶ │ run: ssh │ │ SSH Server │
⦜ -- ▶ │ run: ssh │ │ SSH Server │
│ user@localhost │ (port 22) │
└──────────────┘ └─────────────┘
```
Expand Down
53 changes: 36 additions & 17 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,48 @@ use iroh::{NodeId, SecretKey};
use crate::{dot_ssh, IrohSsh};

pub async fn info_mode() -> anyhow::Result<()> {
let key = dot_ssh(&SecretKey::generate(rand::rngs::OsRng), false);
if key.is_err() {
println!("No keys found, run 'iroh-ssh server --persist' or '-p' to create it");
let server_key = match dot_ssh(&SecretKey::generate(rand::rngs::OsRng), false,false) {
Ok(key) => Some(key),
Err(_) => None,
};
let service_key = match dot_ssh(&SecretKey::generate(rand::rngs::OsRng), false,true) {
Ok(key) => Some(key),
Err(_) => None,
};

if server_key.is_none() && service_key.is_none() {
println!("No keys found, run for server or service:\n 'iroh-ssh server --persist' or '-p' to create it");
println!();
println!("(if an iroh-ssh instance is currently running, it is using ephemeral keys)");
bail!("No keys found")
}

let key = key.unwrap();
let node_id = key.public();
println!("Your iroh-ssh nodeid: {}", node_id.to_string());

println!("iroh-ssh version {}", env!("CARGO_PKG_VERSION"));
println!("https://github.com/rustonbsd/iroh-ssh");
println!("");
println!("run 'iroh-ssh server --persist' to start the server with persistent keys");
println!("run 'iroh-ssh server' to start the server with ephemeral keys");
println!(
"run 'iroh-ssh service install' to copy the binary, install the service and start the server (always uses persistent keys)"
);
println!("");
println!("Your iroh-ssh nodeid:");
println!(" iroh-ssh my-user@{}\n\n", key.public().to_string());

if server_key.is_none() && service_key.is_none() {
println!("run 'iroh-ssh server --persist' to start the server with persistent keys");
println!("run 'iroh-ssh server' to start the server with ephemeral keys");
println!(
"run 'iroh-ssh service install' to copy the binary, install the service and start the server (always uses persistent keys)"
);
}

if let Some(key) = server_key {
println!("");
println!("Your server iroh-ssh nodeid:");
println!(" iroh-ssh {}@{}", whoami::username(), key.clone().public().to_string());
println!("");
}

if let Some(key) = service_key {
println!("");
println!("Your service iroh-ssh nodeid:");
println!(" iroh-ssh {}@{}", whoami::username(), key.clone().public().to_string());
println!("");
}

Ok(())
}

Expand All @@ -56,12 +75,12 @@ pub mod service {
pub async fn server_mode(ssh_port: u16, persist: bool) -> anyhow::Result<()> {
let mut iroh_ssh_builder = IrohSsh::new().accept_incoming(true).accept_port(ssh_port);
if persist {
iroh_ssh_builder = iroh_ssh_builder.dot_ssh_integration(true);
iroh_ssh_builder = iroh_ssh_builder.dot_ssh_integration(true,false);
}
let iroh_ssh = iroh_ssh_builder.build().await?;

println!("Connect to this this machine:");
println!("\n iroh-ssh my-user@{}\n", iroh_ssh.node_id());
println!("\n iroh-ssh {}@{}\n", whoami::username(), iroh_ssh.node_id());
if persist {

let distro_home = my_home()?.ok_or_else(|| anyhow::anyhow!("home directory not found"))?;
Expand Down
16 changes: 8 additions & 8 deletions src/ssh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ impl Builder {
self
}

pub fn dot_ssh_integration(mut self, persist: bool) -> Self {
if let Ok(secret_key) = dot_ssh(&SecretKey::from_bytes(&self.secret_key), persist) {
pub fn dot_ssh_integration(mut self, persist: bool, service: bool) -> Self {
if let Ok(secret_key) = dot_ssh(&SecretKey::from_bytes(&self.secret_key), persist, service) {
self.secret_key = secret_key.to_bytes();
}
self
Expand Down Expand Up @@ -213,25 +213,25 @@ impl ProtocolHandler for IrohSsh {
}
}

pub fn dot_ssh(default_secret_key: &SecretKey, persist: bool) -> anyhow::Result<SecretKey> {


pub fn dot_ssh(default_secret_key: &SecretKey, persist: bool, service: bool) -> anyhow::Result<SecretKey> {
let distro_home = my_home()?.ok_or_else(|| anyhow::anyhow!("home directory not found"))?;
#[allow(unused_mut)]
let mut ssh_dir = distro_home.join(".ssh");

// For now linux services are installed as "sudo'er" so
// we need to use the root .ssh directory
#[cfg(target_os = "linux")]
if !ssh_dir.join("irohssh_ed25519.pub").exists() {
if service {
ssh_dir = std::path::PathBuf::from("/root/.ssh");
println!("[INFO] using linux service ssh_dir: {}", ssh_dir.display());
}

// Weird windows System service profile location:
// "C:\WINDOWS\system32\config\systemprofile\.ssh"
#[cfg(target_os = "windows")]
if !ssh_dir.join("irohssh_ed25519.pub").exists() {
if service {
ssh_dir = std::path::PathBuf::from(r#"C:\WINDOWS\system32\config\systemprofile\.ssh"#);
println!("[INFO] using windows service ssh_dir: {}", ssh_dir.display());
}

let pub_key = ssh_dir.join("irohssh_ed25519.pub");
Expand All @@ -244,7 +244,7 @@ pub fn dot_ssh(default_secret_key: &SecretKey, persist: bool) -> anyhow::Result<
(false, true) => {
std::fs::create_dir_all(&ssh_dir)?;
println!("[INFO] created .ssh folder: {}", ssh_dir.display());
dot_ssh(default_secret_key, persist)
dot_ssh(default_secret_key, persist, service)
}
(true, true) => {
// check pub and priv key already exists
Expand Down