diff options
| author | Franck Cuny <fcuny@roblox.com> | 2025-09-06 13:05:00 -0700 |
|---|---|---|
| committer | Franck Cuny <fcuny@roblox.com> | 2025-09-06 13:05:00 -0700 |
| commit | 34ad93bfff38348318575ac044c1711f05eae78b (patch) | |
| tree | 2b7336e63e40fbb162efe88c6f56fd110f30d6e0 /cmd | |
| parent | add a tool to check validity of SSH certificates (diff) | |
| download | x-34ad93bfff38348318575ac044c1711f05eae78b.tar.gz | |
ssh-cert-info in go
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/ssh-cert-info/README.org | 1 | ||||
| -rw-r--r-- | cmd/ssh-cert-info/main.go | 162 |
2 files changed, 163 insertions, 0 deletions
diff --git a/cmd/ssh-cert-info/README.org b/cmd/ssh-cert-info/README.org new file mode 100644 index 0000000..9d10f5f --- /dev/null +++ b/cmd/ssh-cert-info/README.org @@ -0,0 +1 @@ +A quick way to check information about various SSH certificates on my machine. diff --git a/cmd/ssh-cert-info/main.go b/cmd/ssh-cert-info/main.go new file mode 100644 index 0000000..957dd93 --- /dev/null +++ b/cmd/ssh-cert-info/main.go @@ -0,0 +1,162 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "golang.org/x/crypto/ssh" +) + +func main() { + sshDir, err := getSSHDirectory() + if err != nil { + fmt.Fprintf(os.Stderr, "Error getting SSH directory: %v\n", err) + os.Exit(1) + } + + certFiles, err := findCertificateFiles(sshDir) + if err != nil { + fmt.Fprintf(os.Stderr, "Error reading SSH directory %s: %v\n", sshDir, err) + os.Exit(1) + } + + if len(certFiles) == 0 { + fmt.Printf("No SSH certificates found in %s\n", sshDir) + return + } + + fmt.Println("SSH Certificate Report") + fmt.Println("======================") + fmt.Println() + + for _, certPath := range certFiles { + checkCertificate(certPath) + fmt.Println() + } +} + +func getSSHDirectory() (string, error) { + home, err := os.UserHomeDir() + if err != nil { + return "", fmt.Errorf("failed to get home directory: %w", err) + } + return filepath.Join(home, ".ssh"), nil +} + +func findCertificateFiles(sshDir string) ([]string, error) { + entries, err := os.ReadDir(sshDir) + if err != nil { + return nil, err + } + + var certFiles []string + for _, entry := range entries { + if entry.IsDir() { + continue + } + + name := entry.Name() + if strings.HasSuffix(name, ".pub") && strings.Contains(name, "-cert.pub") { + certFiles = append(certFiles, filepath.Join(sshDir, name)) + } + } + + return certFiles, nil +} + +func checkCertificate(certPath string) { + fmt.Printf("Certificate: %s\n", certPath) + + certData, err := os.ReadFile(certPath) + if err != nil { + fmt.Printf(" Error: Failed to read certificate file: %v\n", err) + return + } + + pubKey, _, _, _, err := ssh.ParseAuthorizedKey(certData) + if err != nil { + fmt.Printf(" Error: Failed to parse certificate: %v\n", err) + return + } + + cert, ok := pubKey.(*ssh.Certificate) + if !ok { + fmt.Printf(" Error: Not an SSH certificate\n") + return + } + + displayCertificateInfo(cert) +} + +func displayCertificateInfo(cert *ssh.Certificate) { + certType := "Unknown" + switch cert.CertType { + case ssh.UserCert: + certType = "ssh-ed25519-cert-v01@openssh.com user certificate" + case ssh.HostCert: + certType = "ssh-ed25519-cert-v01@openssh.com host certificate" + } + fmt.Printf(" Type: %s\n", certType) + + fmt.Printf(" Public key: %s %s\n", cert.Key.Type(), cert.KeyId) + + fmt.Printf(" Signing CA: %s\n", cert.SignatureKey.Type()) + + fmt.Printf(" Key ID: %s\n", cert.KeyId) + + fmt.Printf(" Serial: %d\n", cert.Serial) + + validFrom := time.Unix(int64(cert.ValidAfter), 0) + validTo := time.Unix(int64(cert.ValidBefore), 0) + + fmt.Printf(" Valid from: %s\n", validFrom.Format("2006-01-02T15:04:05")) + + if cert.ValidBefore == ssh.CertTimeInfinity { + fmt.Printf(" Valid to: forever\n") + fmt.Println(" ✓ Certificate is currently valid") + } else { + fmt.Printf(" Valid to: %s\n", validTo.Format("2006-01-02T15:04:05")) + checkCertificateExpiration(validTo) + } + + if len(cert.ValidPrincipals) > 0 { + fmt.Println(" Principals:") + for _, principal := range cert.ValidPrincipals { + fmt.Printf(" %s\n", principal) + } + } + + if len(cert.CriticalOptions) > 0 { + fmt.Println(" Critical Options:") + for key, value := range cert.CriticalOptions { + if value == "" { + fmt.Printf(" %s\n", key) + } else { + fmt.Printf(" %s %s\n", key, value) + } + } + } + + if len(cert.Extensions) > 0 { + fmt.Println(" Extensions:") + for key, value := range cert.Extensions { + if value == "" { + fmt.Printf(" %s\n", key) + } else { + fmt.Printf(" %s %s\n", key, value) + } + } + } +} + +func checkCertificateExpiration(validTo time.Time) { + now := time.Now() + if validTo.Before(now) { + fmt.Println(" ⚠️ WARNING: Certificate has EXPIRED!") + } else { + fmt.Println(" ✓ Certificate is currently valid") + } +} |
