v0.5.5: SFTP-over-SSH remote shares (russh, pure-Rust, ring backend)
Adds SFTP as a third remote ISO-library protocol alongside SMB and NFS. Pure-Rust russh + russh-sftp on the ring crypto backend — no kernel mount, no subprocess, no OpenSSL, no new C deps. Like NFS (and unlike SMB), SFTP-sourced ISOs support HTTP Range requests because SFTP opens a seekable file handle. - iso-store: SftpShareManager (connect/auth/READDIR/seekable stream), IsoSource::Sftp, password OR SSH-key auth, trust-on-first-use host-key pinning, 0600 credential sidecar with a restart-safe derived path. - http-api: /api/sftp-shares routes, Range-aware ISO dispatch arm, status/metrics counts, /api/docs entry, `sftp` terminal commands. - webui: "SFTP (SSH)" protocol option with a password/key auth toggle, host-key fingerprint display, dashboard tile, updated copy. SCP was deliberately rejected: sequential-only (no Range) and its crates wrap libssh2 (C + OpenSSL), which would break the static-musl build. russh is pinned to =0.55.0: russh 0.61 needs the stable RustCrypto generation (pkcs8 0.11), which is API-incompatible with the release- candidate crates bergshamra-crypto pins (pkcs8 =0.11.0-rc.11). 0.55 is the newest russh on the prior generation (pkcs8 0.7) that coexists. Do not bump past 0.55 until bergshamra adopts stable RustCrypto. 252 tests pass, clippy clean, static musl x86_64 binary (ring already present via rustls + bergshamra, so no new crypto/C deps). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
674a69f93b
commit
44a2212abe
@@ -20,6 +20,7 @@ pub mod entry;
|
||||
pub mod introspect;
|
||||
pub mod nfs_share;
|
||||
pub mod pxe_logo;
|
||||
pub mod sftp_share;
|
||||
pub mod smb;
|
||||
pub mod smb_share;
|
||||
pub mod store;
|
||||
@@ -40,6 +41,13 @@ pub use smb_share::{SmbAddRequest, SmbShare, SmbShareError, SmbShareManager, Smb
|
||||
// "works in any container" property as SMB, plus support for HTTP
|
||||
// Range requests because NFSv3 READ3 takes an explicit offset.
|
||||
pub use nfs_share::{NfsAddRequest, NfsShare, NfsShareError, NfsShareManager, NfsStream};
|
||||
// v0.5.5: SFTP-over-SSH remote shares via the pure-Rust `russh` +
|
||||
// `russh-sftp` crates (ring backend — no OpenSSL, no new C deps). Like
|
||||
// NFS, supports HTTP Range requests because SFTP opens a seekable file
|
||||
// handle. See crates/iso-store/src/sftp_share.rs.
|
||||
pub use sftp_share::{
|
||||
SftpAddRequest, SftpAuthKind, SftpShare, SftpShareError, SftpShareManager, SftpStream,
|
||||
};
|
||||
pub use store::{
|
||||
generate_boot_entries_for, slugify_str, IsoCategory, IsoMeta, IsoSource, IsoStore, UploadHandle,
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,10 @@ use tokio::io::AsyncWriteExt;
|
||||
/// `nfs3_client` crate (in-process, no subprocess). Same "works in
|
||||
/// any container" property as SMB, plus Range requests work because
|
||||
/// NFSv3 READ3 takes an explicit offset.
|
||||
/// `Sftp` (v0.5.5) — remote SFTP-over-SSH share, streamed via the
|
||||
/// pure-Rust `russh` + `russh-sftp` crates (in-process). Like NFS it
|
||||
/// supports HTTP Range requests because SFTP opens a seekable file
|
||||
/// handle (`SSH_FXP_READ` at offset).
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(tag = "kind", rename_all = "snake_case")]
|
||||
pub enum IsoSource {
|
||||
@@ -42,6 +46,13 @@ pub enum IsoSource {
|
||||
/// Filename at the export root.
|
||||
relative_path: String,
|
||||
},
|
||||
/// v0.5.5: SFTP-over-SSH via the in-process `russh` + `russh-sftp`
|
||||
/// crates.
|
||||
Sftp {
|
||||
share_id: String,
|
||||
/// Filename at the export root.
|
||||
relative_path: String,
|
||||
},
|
||||
}
|
||||
|
||||
/// Where the ISO lands in the PXE menu hierarchy.
|
||||
@@ -299,11 +310,11 @@ impl IsoStore {
|
||||
None
|
||||
}
|
||||
}
|
||||
// SMB and NFS sources have no local path — they're
|
||||
// SMB, NFS, and SFTP sources have no local path — they're
|
||||
// streamed in-process. Callers must inspect the source
|
||||
// kind first and dispatch to the appropriate share
|
||||
// manager.
|
||||
IsoSource::Smb { .. } | IsoSource::Nfs { .. } => None,
|
||||
IsoSource::Smb { .. } | IsoSource::Nfs { .. } | IsoSource::Sftp { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,9 +374,9 @@ impl IsoStore {
|
||||
pub fn drop_external_source(&self, share_id: &str) {
|
||||
let mut g = self.inner.write();
|
||||
g.isos.retain(|_, m| match &m.source {
|
||||
IsoSource::Smb { share_id: sid, .. } | IsoSource::Nfs { share_id: sid, .. } => {
|
||||
sid != share_id
|
||||
}
|
||||
IsoSource::Smb { share_id: sid, .. }
|
||||
| IsoSource::Nfs { share_id: sid, .. }
|
||||
| IsoSource::Sftp { share_id: sid, .. } => sid != share_id,
|
||||
IsoSource::Local => true,
|
||||
});
|
||||
}
|
||||
@@ -659,7 +670,10 @@ mod tests {
|
||||
// good.
|
||||
let s = linux_cmdline(DistroFamily::DebianUbuntu, "ubuntu-24-04");
|
||||
assert!(s.contains("boot=casper"), "{s}");
|
||||
assert!(s.contains("iso-url=${base-url}/iso/ubuntu-24-04.iso"), "{s}");
|
||||
assert!(
|
||||
s.contains("iso-url=${base-url}/iso/ubuntu-24-04.iso"),
|
||||
"{s}"
|
||||
);
|
||||
assert!(s.contains("ds=nocloud"), "{s}");
|
||||
assert!(s.contains("ip=dhcp"), "{s}");
|
||||
assert!(!s.contains("netboot=url"), "legacy option leaked: {s}");
|
||||
|
||||
Reference in New Issue
Block a user