TCP-Forwarding über das Iroh-Netz (QUIC), adressiert per Public Key statt IP (Umsetzung von Option B aus recherche-vpn-vs-p2p-netzwerk.md). - server: nimmt Iroh-Verbindungen an, leitet QUIC-Streams an lokalen Port weiter - client: lauscht lokal auf TCP, öffnet pro Verbindung einen QUIC-Stream zum Peer - keygen: erzeugt Key-Datei und gibt EndpointId aus (Vorab-Verteilung) - Allowlist (--allow) erlaubter Client-EndpointIds - QUIC-Multiplexing, Reconnect mit Backoff, n0-Default-Relays + DNS-Discovery Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
64 lines
1.9 KiB
Rust
64 lines
1.9 KiB
Rust
//! Laden/Erzeugen und Persistieren des Iroh-SecretKeys (Node-Identität).
|
|
|
|
use std::path::Path;
|
|
|
|
use anyhow::{Context, Result};
|
|
use data_encoding::HEXLOWER;
|
|
use iroh::SecretKey;
|
|
|
|
/// Lädt den SecretKey aus `path` oder erzeugt einen neuen und speichert ihn.
|
|
///
|
|
/// Der Key bestimmt die EndpointId (= Public Key = Adresse im Iroh-Netz). Persistenz
|
|
/// ist wichtig, damit der Serving Host nach einem Neustart unter derselben Adresse
|
|
/// erreichbar bleibt.
|
|
pub fn load_or_create_secret_key(path: &Path) -> Result<SecretKey> {
|
|
if path.exists() {
|
|
let hex = std::fs::read_to_string(path)
|
|
.with_context(|| format!("Key-Datei lesen: {}", path.display()))?;
|
|
let bytes = HEXLOWER
|
|
.decode(hex.trim().as_bytes())
|
|
.context("Key-Datei ist kein gültiges Hex")?;
|
|
let arr: [u8; 32] = bytes
|
|
.as_slice()
|
|
.try_into()
|
|
.context("Key-Datei hat nicht 32 Bytes")?;
|
|
Ok(SecretKey::from_bytes(&arr))
|
|
} else {
|
|
let secret_key = SecretKey::generate();
|
|
let hex = HEXLOWER.encode(&secret_key.to_bytes());
|
|
write_secret(path, &hex)
|
|
.with_context(|| format!("Key-Datei schreiben: {}", path.display()))?;
|
|
Ok(secret_key)
|
|
}
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
fn write_secret(path: &Path, hex: &str) -> Result<()> {
|
|
use std::io::Write;
|
|
use std::os::unix::fs::OpenOptionsExt;
|
|
|
|
if let Some(parent) = path.parent() {
|
|
if !parent.as_os_str().is_empty() {
|
|
std::fs::create_dir_all(parent)?;
|
|
}
|
|
}
|
|
let mut f = std::fs::OpenOptions::new()
|
|
.write(true)
|
|
.create_new(true)
|
|
.mode(0o600)
|
|
.open(path)?;
|
|
f.write_all(hex.as_bytes())?;
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(not(unix))]
|
|
fn write_secret(path: &Path, hex: &str) -> Result<()> {
|
|
if let Some(parent) = path.parent() {
|
|
if !parent.as_os_str().is_empty() {
|
|
std::fs::create_dir_all(parent)?;
|
|
}
|
|
}
|
|
std::fs::write(path, hex)?;
|
|
Ok(())
|
|
}
|