7

I have an environment where I use ssh certificates to authenticate ssh host keys. I'm talking about the kind of certificates created by running ssh-keygen -s /path/to/ca -h .... These certificates are also created with a validity interval, stating when they will expire. These certificates have now been in use now long enough that I need to start monitor them, to get a heads up when they start getting close to expire.

Any way I can do a remote connection, without logging in, and somehow either get the validity interval displayed alt. get the certificate downloaded? Running ssh -vvv don't appear to display the info I need. Neither does ssh-keyscan appear to be certificate aware. Perhaps some library I haven't looked closely enough at?

Worst case I can always write a monitoring plugin which runs locally and parses the output of ssh-keygen -L -f. Still, a remote scan really feels like the preferable approach.

3
  • Comparing the output of ssh -vvv with a certified host key to what it looks like with an ordinary host key could provide some clue as to where to start looking for an answer.
    – kasperd
    Aug 12, 2014 at 21:42
  • It does not, only the fingerprint gets printed. Aug 15, 2014 at 13:19
  • openssl s_client -showcerts would work for a web server. I don't have an ssh server with a cert based key to test it on. Aug 15, 2014 at 21:12

4 Answers 4

9
+200

(Edit: OpenSSH 7.2 introduced tool support in the form of the new -c flag for the ssh-keyscan command. The rest of this answer was written more than a year before that release, but I'm keeping it here because I'm proud of it.)

This is possible, but it lacks tool support. I found a library which speaks the SSH protocol well enough to let me write a tool to extract the host cert valid_before time without a full ssh login. Here it is, in the Go language. I hope it helps.

package main

import "golang.org/x/crypto/ssh"
import "fmt"
import "os"
import "time"

func ignoreCertChain(auth ssh.PublicKey, address string) bool {
    return true // Pretend all certificates are trusted.
}

var sawACert bool

func examineCert(cert *ssh.Certificate) bool {
  expires := cert.ValidBefore
  var humanReadable string
  if expires >= ssh.CertTimeInfinity {
    humanReadable = "infinity"
  } else if expires < (1 << 63) {
    humanReadable = time.Unix(int64(expires), 0).Format(time.RFC3339)
  } else {
    humanReadable = "the distant future"
  }
  fmt.Println("Cert expires at time", expires, "(" + humanReadable + ")")
  sawACert = true
  return true  // Reject the cert, to force early connection close.
}

func main() {
  serverHostPort := os.Args[1]
  checker := &ssh.CertChecker{
    IsHostAuthority: ignoreCertChain,
    IsRevoked: examineCert,
  }
  config := &ssh.ClientConfig{
    User: "test-sshcertscan-not-a-real-login-attempt",
    Auth: []ssh.AuthMethod{
      ssh.Password(""),
    },
    HostKeyCallback: checker.CheckHostKey,
  }
  sawACert = false
  client, err := ssh.Dial("tcp", serverHostPort, config);
  if err != nil && !sawACert {
    panic(fmt.Sprint("Cannot connect to ", serverHostPort, ", error: ",
                     err.Error()))
  } else if client != nil {
    defer client.Close()
  }
}

(Quick usage instructions: install Go, save the code seen above in sshcertscan.go, run go build sshcertscan.go, then point it at an ssh server on examplehost port 22 with ./sshcertscan examplehost:22.)

1
  • Wow, also writing that example code certainly went way over and beyond, thanks! Now I just need to learn a bit more go :)
    – andol
    Aug 17, 2014 at 17:52
1

Unfortunately I don't know of any open-source tool. It seems nmap would be able to retrieve it somehow with NSE scripts (but needs some tweaking -- check /usr/share/nmap/scripts).

SSH's Tectia SSH server includes a tool called ssh-fetchkey that will retrieve the certificate and then you can use ssh-certview to view the details.

1
  • While I might not use NSE scripts for this purpose, it definitely sounds like something I might find future uses for. Thanks!
    – andol
    Aug 17, 2014 at 17:53
1

The following command will get the certificate and display information about it:

ssh-keyscan -c <host> | ssh-keygen -L  -f -

example output:

        Type: [email protected] host certificate
        Public key: ED25519-CERT SHA256:XXX<redacted>
        Signing CA: ED25519 SHA256:XXX<redacted>
        Key ID: "my_server"
        Serial: 0
        Valid: from 2022-12-05T15:42:00 to 2023-12-04T15:43:35
        Principals: 
                my_server.example.com
                my_server
                www.example.com
        Critical Options: (none)
        Extensions: (none)
1
  • 1
    ... since 7.2, which was 2 years after the Q was asked. Dec 7, 2022 at 0:38
0

I'm afraid the answer is "that's not possible". At least not in any way I found, using either the openssh client or the paramiko SSH library for python. I would suggest a local check as you described, combined with a simpler remote check that verifies that the key used by SSHD is the key you just checked the certificate lifetime of.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .