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]]
|
[[package]]
|
||||||
name = "ssh-key"
|
name = "ssh-key"
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4b0a17fec6ea344bfa1cda3aed2f0696fddc6295cfcc8c454a3bf58b8ffaabeb"
|
checksum = "728fdf5286c394f12d83eaad51190af629aade5494813ce6b57f7425f1ca51b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dsa",
|
"dsa",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
|
|
|
@ -42,5 +42,4 @@ lettre = "0.10.4"
|
||||||
maud = "0.25.0"
|
maud = "0.25.0"
|
||||||
|
|
||||||
# verifying ssh keys
|
# 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/password/auth").post(account_recover::password::auth);
|
||||||
app.at("/ldap/recover/username").post(account_recover::username::submit);
|
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/request").post(account_recover::ssh::request);
|
||||||
|
app.at("/ldap/recover/ssh/verify").post(account_recover::ssh::verify);
|
||||||
|
|
||||||
app.listen(host_port).await?;
|
app.listen(host_port).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -426,7 +426,9 @@ pub mod username {
|
||||||
pub mod ssh {
|
pub mod ssh {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::AccountsSSH;
|
use crate::AccountsSSH;
|
||||||
use ssh_key::AuthorizedKeys;
|
use ldap3::Mod;
|
||||||
|
use ssh_key::{AuthorizedKeys, SshSig};
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
// this is for a legacy member who has forgotten their account password to be able to set an email.
|
// 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
|
// check if there is ane listing entry, use that auth if exists
|
||||||
if let Ok(result) = sqlx::query_as::<_, AccountsSSH>(
|
if let Ok(result) = sqlx::query_as::<_, AccountsSSH>(
|
||||||
r#"
|
r#"
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM accounts_ssh
|
FROM accounts_ssh
|
||||||
WHERE user == ?
|
WHERE user == ?
|
||||||
|
@ -495,7 +497,7 @@ pub mod ssh {
|
||||||
// not in db, generate auth and save
|
// not in db, generate auth and save
|
||||||
let auth = random_string(50);
|
let auth = random_string(50);
|
||||||
if sqlx::query_as::<_, AccountsSSH>(
|
if sqlx::query_as::<_, AccountsSSH>(
|
||||||
"
|
"
|
||||||
INSERT OR REPLACE INTO accounts_ssh (user, auth_code, email)
|
INSERT OR REPLACE INTO accounts_ssh (user, auth_code, email)
|
||||||
VALUES (?1, ?2, ?3)
|
VALUES (?1, ?2, ?3)
|
||||||
",
|
",
|
||||||
|
@ -514,4 +516,96 @@ pub mod ssh {
|
||||||
// return the full thing
|
// return the full thing
|
||||||
Ok(json!({"result": "success","success": { "auth": auth,"keys": keys }}).into())
|
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