aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ssh-cert-info/Cargo.toml6
-rw-r--r--src/ssh-cert-info/README.org1
-rw-r--r--src/ssh-cert-info/src/main.rs225
3 files changed, 0 insertions, 232 deletions
diff --git a/src/ssh-cert-info/Cargo.toml b/src/ssh-cert-info/Cargo.toml
deleted file mode 100644
index f00f2c5..0000000
--- a/src/ssh-cert-info/Cargo.toml
+++ /dev/null
@@ -1,6 +0,0 @@
-[package]
-name = "ssh-cert-info"
-version = "0.1.0"
-edition = "2024"
-
-[dependencies]
diff --git a/src/ssh-cert-info/README.org b/src/ssh-cert-info/README.org
deleted file mode 100644
index 9d10f5f..0000000
--- a/src/ssh-cert-info/README.org
+++ /dev/null
@@ -1 +0,0 @@
-A quick way to check information about various SSH certificates on my machine.
diff --git a/src/ssh-cert-info/src/main.rs b/src/ssh-cert-info/src/main.rs
deleted file mode 100644
index 4c0c1ff..0000000
--- a/src/ssh-cert-info/src/main.rs
+++ /dev/null
@@ -1,225 +0,0 @@
-use std::env;
-use std::fs;
-use std::path::PathBuf;
-use std::process::Command;
-
-fn main() {
- let ssh_dir = get_ssh_directory();
-
- match fs::read_dir(&ssh_dir) {
- Ok(entries) => {
- let mut cert_files = Vec::new();
-
- for entry in entries {
- if let Ok(entry) = entry {
- let path = entry.path();
- if let Some(extension) = path.extension() {
- if extension == "pub" {
- // I use the following convention for my certificates: <identificator>-cert.pub
- if let Some(filename) = path.file_name() {
- if filename.to_string_lossy().contains("-cert.pub") {
- cert_files.push(path);
- }
- }
- }
- }
- }
- }
-
- if cert_files.is_empty() {
- println!("No SSH certificates found in {}", ssh_dir.display());
- return;
- }
-
- println!("SSH Certificate Report");
- println!("======================");
- println!();
-
- for cert_path in cert_files {
- check_certificate(&cert_path);
- println!();
- }
- }
- Err(e) => {
- eprintln!("Error reading SSH directory {}: {}", ssh_dir.display(), e);
- std::process::exit(1);
- }
- }
-}
-
-fn get_ssh_directory() -> PathBuf {
- let home = env::var("HOME").unwrap_or_else(|_| {
- eprintln!("HOME environment variable not set");
- std::process::exit(1);
- });
-
- PathBuf::from(home).join(".ssh")
-}
-
-fn check_certificate(cert_path: &PathBuf) {
- println!("Certificate: {}", cert_path.display());
-
- // Use ssh-keygen to get certificate information
- // TODO: maybe consider https://docs.rs/ssh-key/latest/ssh_key/certificate/struct.Certificate.html ?
- let output = Command::new("ssh-keygen")
- .arg("-L")
- .arg("-f")
- .arg(cert_path)
- .output();
-
- match output {
- Ok(output) => {
- if output.status.success() {
- let cert_info = String::from_utf8_lossy(&output.stdout);
- parse_and_display_cert_info(&cert_info);
- } else {
- let error = String::from_utf8_lossy(&output.stderr);
- println!(" Error: Failed to read certificate: {}", error.trim());
- }
- }
- Err(e) => {
- println!(" Error: Failed to execute ssh-keygen: {}", e);
- }
- }
-}
-
-fn parse_and_display_cert_info(cert_info: &str) {
- let mut type_found = false;
- let mut public_key = String::new();
- let mut signing_ca = String::new();
- let mut key_id = String::new();
- let mut serial = String::new();
- let mut valid_from = String::new();
- let mut valid_to = String::new();
- let mut cert_type = String::new();
- let mut principals = Vec::new();
- let mut critical_options = Vec::new();
- let mut extensions = Vec::new();
-
- for line in cert_info.lines() {
- let line = line.trim();
-
- if line.starts_with("Type:") {
- cert_type = line.replace("Type:", "").trim().to_string();
- type_found = true;
- } else if line.starts_with("Public key:") {
- public_key = line.replace("Public key:", "").trim().to_string();
- } else if line.starts_with("Signing CA:") {
- signing_ca = line.replace("Signing CA:", "").trim().to_string();
- } else if line.starts_with("Key ID:") {
- key_id = line.replace("Key ID:", "").trim().to_string();
- } else if line.starts_with("Serial:") {
- serial = line.replace("Serial:", "").trim().to_string();
- } else if line.starts_with("Valid: from") {
- // Format: "Valid: from 2023-01-01T00:00:00 to 2024-01-01T00:00:00"
- let valid_line = line.replace("Valid: from", "").trim().to_string();
- if let Some(to_pos) = valid_line.find(" to ") {
- valid_from = valid_line[..to_pos].trim().to_string();
- valid_to = valid_line[to_pos + 4..].trim().to_string();
- }
- } else if line.starts_with("Principals:") {
- // Principals might be on the same line or following lines
- let principal_text = line.replace("Principals:", "").trim().to_string();
- if !principal_text.is_empty() {
- principals.push(principal_text);
- }
- } else if line.starts_with("Critical Options:") {
- let options_text = line.replace("Critical Options:", "").trim().to_string();
- if !options_text.is_empty() {
- critical_options.push(options_text);
- }
- } else if line.starts_with("Extensions:") {
- let extensions_text = line.replace("Extensions:", "").trim().to_string();
- if !extensions_text.is_empty() {
- extensions.push(extensions_text);
- }
- } else if !type_found {
- // Skip lines before we find the certificate type
- continue;
- } else if line.len() > 0 && (line.starts_with(" ") || line.starts_with("\t\t")) {
- // This might be a continuation of principals, critical options, or extensions
- let trimmed = line.trim();
- if !trimmed.is_empty() {
- if !principals.is_empty() {
- principals.push(trimmed.to_string());
- } else if !critical_options.is_empty() {
- critical_options.push(trimmed.to_string());
- } else if !extensions.is_empty() {
- extensions.push(trimmed.to_string());
- }
- }
- }
- }
-
- if !cert_type.is_empty() {
- println!(" Type: {}", cert_type);
- }
-
- if !public_key.is_empty() {
- println!(" Public key: {}", public_key);
- }
-
- if !signing_ca.is_empty() {
- println!(" Signing CA: {}", signing_ca);
- }
-
- if !key_id.is_empty() {
- println!(" Key ID: {}", key_id);
- }
-
- if !serial.is_empty() {
- println!(" Serial: {}", serial);
- }
-
- if !valid_from.is_empty() && !valid_to.is_empty() {
- println!(" Valid from: {}", valid_from);
- println!(" Valid to: {}", valid_to);
-
- check_expiration(&valid_to);
- }
-
- if !principals.is_empty() {
- println!(" Principals:");
- for principal in &principals {
- if !principal.is_empty() {
- println!(" {}", principal);
- }
- }
- }
-
- if !critical_options.is_empty() {
- println!(" Critical Options:");
- for option in &critical_options {
- if !option.is_empty() {
- println!(" {}", option);
- }
- }
- }
-
- if !extensions.is_empty() {
- println!(" Extensions:");
- for extension in &extensions {
- if !extension.is_empty() {
- println!(" {}", extension);
- }
- }
- }
-}
-
-fn check_expiration(valid_to: &str) {
- // TODO: maybe use chrono for more robust date handling ?
- let current_date = std::process::Command::new("date")
- .arg("+%Y-%m-%dT%H:%M:%S")
- .output();
-
- if let Ok(output) = current_date {
- if let Ok(current) = String::from_utf8(output.stdout) {
- let current = current.trim();
- if valid_to < current {
- println!(" ⚠️ WARNING: Certificate has EXPIRED!");
- } else {
- println!(" ✓ Certificate is currently valid");
- }
- }
- }
-}