Fetch User Keys - simple tool for fetching SSH keys from various sources
1use ssh_key::PublicKey; 2use tokio::process::Command; 3use serde::Deserialize; 4use async_trait::async_trait; 5 6#[async_trait] 7pub trait Fetch: std::fmt::Debug { 8 async fn fetch(&self) -> Vec<PublicKey>; 9} 10 11#[derive(Debug, Deserialize)] 12#[serde(rename_all = "lowercase")] 13pub enum Source { 14 Raw(Raw), 15 Hosts(Hosts), 16 Forge(Forge), 17 Github(String), 18 Sourcehut(String), 19 Gitlab(String), 20 Codeberg(String), 21} 22 23#[async_trait] 24impl Fetch for Source { 25 async fn fetch(&self) -> Vec<PublicKey> { 26 match *self { 27 Source::Raw(ref raw) => raw.fetch().await, 28 Source::Hosts(ref raw) => raw.fetch().await, 29 Source::Forge(ref raw) => raw.fetch().await, 30 Source::Github(ref user) => Forge { user: user.clone(), host: "github.com".into() }.fetch().await, 31 Source::Sourcehut(ref user) => Forge { user: user.clone(), host: "meta.sr.ht".into() }.fetch().await, 32 Source::Gitlab(ref user) => Forge { user: user.clone(), host: "gitlab.com".into() }.fetch().await, 33 Source::Codeberg(ref user) => Forge { user: user.clone(), host: "codeberg.org".into() }.fetch().await, 34 } 35 } 36} 37 38#[derive(Debug, Deserialize)] 39pub struct Raw(Vec<PublicKey>); 40 41#[async_trait::async_trait] 42impl Fetch for Raw { 43 async fn fetch(&self) -> Vec<PublicKey> { 44 self.0.clone() 45 } 46} 47 48#[derive(Debug, Deserialize)] 49pub struct Hosts(pub Vec<String>); 50 51#[async_trait] 52impl Fetch for Hosts { 53 async fn fetch(&self) -> Vec<PublicKey> { 54 // TODO: Check if we can do it in-process instead of shelling out to `ssh-keyscan` 55 let result = Command::new("ssh-keyscan") 56 .args(&self.0) 57 .output() 58 .await 59 .unwrap(); 60 61 std::str::from_utf8(&result.stdout) 62 .unwrap() 63 .trim() 64 .split('\n') 65 .map(|line| { 66 // Ignore first column as it contain hostname which is not 67 // needed there 68 line.split(' ') 69 .skip(1) 70 .collect::<Vec<_>>() 71 .join(" ") 72 .to_owned() 73 }) 74 .map(|k| PublicKey::from_openssh(&k).unwrap()) 75 .collect() 76 } 77} 78 79#[derive(Debug, Deserialize)] 80pub struct Forge { 81 pub user: String, 82 pub host: String, 83} 84 85#[async_trait] 86impl Fetch for Forge { 87 async fn fetch(&self) -> Vec<PublicKey> { 88 let url = format!("https://{}/{}.keys", self.host, self.user); 89 90 reqwest::get(url) 91 .await 92 .unwrap() 93 .text() 94 .await 95 .unwrap() 96 .trim() 97 .split('\n') 98 .map(|s| PublicKey::from_openssh(s).unwrap()) 99 .collect() 100 } 101}