mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: add certificate authentication for ssh
This commit is contained in:
parent
f10b8910ad
commit
288ca2ffa2
2 changed files with 63 additions and 0 deletions
|
|
@ -39,6 +39,10 @@ pub struct ServiceSftp {
|
||||||
pub key_file: PathBuf,
|
pub key_file: PathBuf,
|
||||||
pub key_passphrase: Option<String>,
|
pub key_passphrase: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub cert_file: PathBuf,
|
||||||
|
#[serde(default)]
|
||||||
|
pub skip_cert_validation: bool,
|
||||||
|
#[serde(default)]
|
||||||
pub identity_agent: PathBuf,
|
pub identity_agent: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,10 @@ impl Conn {
|
||||||
|
|
||||||
let session = if self.config.password.is_some() {
|
let session = if self.config.password.is_some() {
|
||||||
self.connect_by_password(pref).await
|
self.connect_by_password(pref).await
|
||||||
|
} else if !self.config.key_file.as_os_str().is_empty()
|
||||||
|
&& !self.config.cert_file.as_os_str().is_empty()
|
||||||
|
{
|
||||||
|
self.connect_by_key_and_cert(pref).await
|
||||||
} else if !self.config.key_file.as_os_str().is_empty() {
|
} else if !self.config.key_file.as_os_str().is_empty() {
|
||||||
self.connect_by_key(pref).await
|
self.connect_by_key(pref).await
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -146,6 +150,61 @@ impl Conn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn connect_by_key_and_cert(
|
||||||
|
self,
|
||||||
|
pref: Arc<russh::client::Config>,
|
||||||
|
) -> Result<russh::client::Handle<Self>, russh::Error> {
|
||||||
|
let key_file = &self.config.key_file;
|
||||||
|
if key_file.as_os_str().is_empty() {
|
||||||
|
return Err(russh::Error::InvalidConfig("Key file not provided".to_owned()));
|
||||||
|
};
|
||||||
|
let cert_file = &self.config.cert_file;
|
||||||
|
if cert_file.as_os_str().is_empty() {
|
||||||
|
return Err(russh::Error::InvalidConfig("Cert file not provided".to_owned()));
|
||||||
|
};
|
||||||
|
|
||||||
|
let cert = russh::keys::load_openssh_certificate(cert_file).map_err(
|
||||||
|
|e: russh::keys::ssh_key::Error| {
|
||||||
|
russh::Error::InvalidConfig(format!("Failed to read cert file: {e}"))
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if !self.config.skip_cert_validation {
|
||||||
|
// TODO: Also validate the certificate cryptographically: https://docs.rs/russh/latest/russh/keys/struct.Certificate.html#method.validate
|
||||||
|
// however, this would require fetching CAs, which could require additional config and complexity
|
||||||
|
|
||||||
|
cert.verify_signature().map_err(|e| {
|
||||||
|
russh::Error::InvalidConfig(format!("Certificate signature verification failed: {e}"))
|
||||||
|
})?;
|
||||||
|
let unix_timestamp = std::time::UNIX_EPOCH.elapsed().unwrap_or_default().as_secs();
|
||||||
|
if !(cert.valid_after() <= unix_timestamp && unix_timestamp <= cert.valid_before()) {
|
||||||
|
return Err(russh::Error::InvalidConfig(
|
||||||
|
"Certificate is not valid at this time".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = Local::regular(key_file)
|
||||||
|
.read_to_string()
|
||||||
|
.await
|
||||||
|
.map_err(|e| russh::Error::InvalidConfig(format!("Failed to read key file: {e}")))?;
|
||||||
|
|
||||||
|
let key = russh::keys::decode_secret_key(&key, self.config.key_passphrase.as_deref())?;
|
||||||
|
|
||||||
|
let mut session =
|
||||||
|
russh::client::connect(pref, (self.config.host.as_str(), self.config.port), self).await?;
|
||||||
|
|
||||||
|
let result = session.authenticate_openssh_cert(&self.config.user, Arc::new(key), cert).await?;
|
||||||
|
|
||||||
|
if result.success() {
|
||||||
|
Ok(session)
|
||||||
|
} else {
|
||||||
|
Err(russh::Error::InvalidConfig(
|
||||||
|
"Public key with certificate authentication failed".to_owned(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn connect_by_agent(
|
async fn connect_by_agent(
|
||||||
self,
|
self,
|
||||||
pref: Arc<russh::client::Config>,
|
pref: Arc<russh::client::Config>,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue