feat: added verification for the ssh keys #42
5 changed files with 104 additions and 7 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -2690,9 +2690,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ssh-key"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b0a17fec6ea344bfa1cda3aed2f0696fddc6295cfcc8c454a3bf58b8ffaabeb"
|
||||
checksum = "728fdf5286c394f12d83eaad51190af629aade5494813ce6b57f7425f1ca51b7"
|
||||
dependencies = [
|
||||
"dsa",
|
||||
"ed25519-dalek",
|
||||
|
|
|
@ -42,5 +42,4 @@ lettre = "0.10.4"
|
|||
maud = "0.25.0"
|
||||
|
||||
# verifying ssh keys
|
||||
ssh-key = { version = "0.6.0", features = ["ecdsa", "rand_core", "std", "dsa", "ed25519", "rsa" ] }
|
||||
|
||||
ssh-key = { version = "0.6.1", features = ["crypto", "dsa"] }
|
||||
|
|
3
silver/.ssh/authorized_keys
Normal file
3
silver/.ssh/authorized_keys
Normal file
|
@ -0,0 +1,3 @@
|
|||
# --- BEGIN PVE ---
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBHbsOjxIcLasz+CHA8gUg1pvc8dPHwMKdWoIwNvPxLp Desktop_WSL
|
||||
# --- END PVE ---
|
|
@ -32,6 +32,7 @@ async fn main() -> tide::Result<()> {
|
|||
app.at("/ldap/recover/password/auth").post(account_recover::password::auth);
|
||||
app.at("/ldap/recover/username").post(account_recover::username::submit);
|
||||
app.at("/ldap/recover/ssh/request").post(account_recover::ssh::request);
|
||||
app.at("/ldap/recover/ssh/verify").post(account_recover::ssh::verify);
|
||||
|
||||
app.listen(host_port).await?;
|
||||
Ok(())
|
||||
|
|
|
@ -426,7 +426,9 @@ pub mod username {
|
|||
pub mod ssh {
|
||||
use super::*;
|
||||
use crate::AccountsSSH;
|
||||
use ssh_key::AuthorizedKeys;
|
||||
use ldap3::Mod;
|
||||
use ssh_key::{AuthorizedKeys, SshSig};
|
||||
use std::collections::HashSet;
|
||||
use std::fs;
|
||||
|
||||
// this is for a legacy member who has forgotten their account password to be able to set an email.
|
||||
|
@ -479,7 +481,7 @@ pub mod ssh {
|
|||
|
||||
// check if there is ane listing entry, use that auth if exists
|
||||
if let Ok(result) = sqlx::query_as::<_, AccountsSSH>(
|
||||
r#"
|
||||
r#"
|
||||
SELECT *
|
||||
FROM accounts_ssh
|
||||
WHERE user == ?
|
||||
|
@ -495,7 +497,7 @@ pub mod ssh {
|
|||
// not in db, generate auth and save
|
||||
let auth = random_string(50);
|
||||
if sqlx::query_as::<_, AccountsSSH>(
|
||||
"
|
||||
"
|
||||
INSERT OR REPLACE INTO accounts_ssh (user, auth_code, email)
|
||||
VALUES (?1, ?2, ?3)
|
||||
",
|
||||
|
@ -514,4 +516,96 @@ pub mod ssh {
|
|||
// return the full thing
|
||||
Ok(json!({"result": "success","success": { "auth": auth,"keys": keys }}).into())
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct RequestVerify {
|
||||
user: String,
|
||||
auth_signed: String,
|
||||
}
|
||||
|
||||
// echo "auth code" | ssh-keygen -Y sign -n file -f /path/to/key
|
||||
pub async fn verify(mut req: Request<State>) -> tide::Result {
|
||||
let RequestVerify {
|
||||
user,
|
||||
auth_signed,
|
||||
} = req.body_json().await?;
|
||||
|
||||
let db = &req.state().db;
|
||||
let config = &req.state().config;
|
||||
let details = if let Ok(result) = sqlx::query_as::<_, AccountsSSH>(
|
||||
r#"
|
||||
SELECT *
|
||||
FROM accounts_ssh
|
||||
WHERE user == ?
|
||||
"#,
|
||||
)
|
||||
.bind(&user)
|
||||
.fetch_one(db)
|
||||
.await
|
||||
{
|
||||
result
|
||||
} else {
|
||||
return Ok(json!({ "result": "error"}).into());
|
||||
};
|
||||
|
||||
// check if <root>/<user>/.ssh/authorized_keys exists
|
||||
//let root = "/skynet_old";
|
||||
let root = ".";
|
||||
let path = format!("{}/{}/.ssh/authorized_keys", root, user);
|
||||
|
||||
let sig = match SshSig::from_pem(auth_signed) {
|
||||
Ok(x) => x,
|
||||
Err(_) => {
|
||||
return Ok(json!({ "result": "error", "error": "Incorrect signed format"}).into());
|
||||
}
|
||||
};
|
||||
|
||||
// when ye echo it adds a newline to the end of the "file", need to duplicate it here.
|
||||
let msg_tmp = format!("{}\n", details.auth_code);
|
||||
let msg = msg_tmp.as_bytes();
|
||||
|
||||
let mut valid = false;
|
||||
if fs::read_to_string(&path).is_ok() {
|
||||
if let Ok(x) = AuthorizedKeys::read_file(path) {
|
||||
for entry in x {
|
||||
let key = entry.public_key();
|
||||
match key.verify("file", msg, &sig) {
|
||||
Ok(_) => {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return Ok(json!({"result": "error", "error": "no valid key"}).into());
|
||||
}
|
||||
|
||||
// add to ldap
|
||||
let mut ldap = LdapConn::new(&config.ldap_host)?;
|
||||
ldap.simple_bind(&config.ldap_admin, &config.ldap_admin_pw)?.success()?;
|
||||
|
||||
let mods = vec![Mod::Replace(String::from("mail"), HashSet::from([details.email]))];
|
||||
|
||||
let dn = format!("uid={},ou=users,dc=skynet,dc=ie", &user);
|
||||
ldap.modify(&dn, mods)?.success()?;
|
||||
ldap.unbind()?;
|
||||
|
||||
// delete from tmp
|
||||
sqlx::query_as::<_, AccountsSSH>(
|
||||
r#"
|
||||
DELETE FROM accounts_ssh
|
||||
WHERE user == ?
|
||||
"#,
|
||||
)
|
||||
.bind(&user)
|
||||
.fetch_optional(db)
|
||||
.await?;
|
||||
|
||||
// return the full thing
|
||||
Ok(json!({"result": "success", "success": "key valid"}).into())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue