How do I validate the ECDSA key fingerprint of my newly-created Droplet?

October 25, 2017 393 views
Security Ubuntu 16.04

The first time I connect to a newly-created Droplet, I expect to be able to validate that the ECDSA key fingerprint is valid before adding it to my known_hosts file. I have not been able to find this information on the forum, how-to's, or in the web console.

I've read through How To Connect To Your Droplet with SSH, and the DO moderator surprisingly recommends disregarding the possibility of a man-in-the-middle attack and blindly accepting the key. The actual statement is as follows: "The first time you attempt to connect to your server, you will likely see a warning that looks like this... The authenticity of host '123.123.123.123 (123.123.123.123)' can't be established... Go ahead and type yes to continue to connect. Here, your computer is telling you that the remote server is not recognized. Since this is your first time connecting, this is completely expected." I agree that it's expected. However, I don't agree that that means it's safe to assume that the connection is not compromised.

Back to my question then: Is there any way to validate the host's fingerprint before connecting? Ideally it would be part of the response body when a new droplet is created or have its own API call.

1 comment
1 Answer
asb MOD February 22, 2018
Accepted Answer

There are a number of methods you can use to verify the host keys on a Droplet. Each has some pros and cons.

The first way is to launch the web console while it is in the process of booting and view the output. This includes the key's fingerprint. It looks like:

Generating public/private ecdsa key pair.
Your identification has been saved in /etc/ssh/ssh_host_ecdsa_key.
Your public key has been saved in /etc/ssh/ssh_host_ecdsa_key.pub.
The key fingerprint is:
SHA256:Xsq8YwExqeayL+OFLq1hRL4o6I0SK7gzL9+in2bjPlc root@ubuntu-s-1vcpu-1gb-nyc3-01
The key's randomart image is:
+---[ECDSA 256]---+
|       .         |
|      +          |
| .   . o         |
|o   o .          |
| o o   .S .      |
|= o.. E+.o       |
|**.o..  =.       |
|&o&+.   o.       |
|*^XXo  ...       |
+----[SHA256]-----+

Unfortunately, that can be very hard to catch as it scrolls by.

The second option would be to log into the Droplet via the web console. This requires that you have a user with password authentication enabled. While you can disable this latter, I still consider this a major drawback. Once on the Droplet, you can check the fingerprint by running:

  • ssh-keygen -lf /etc/ssh/ssh_host_ecdsa_key.pub

DigitalOcean has support for cloud-init which enables another set of options for verify the host keys.

The first option utilizing cloud-init is generating a key locally and providing it via user data. One draw back to this approach is that the user-data is also served via the DigitalOcean metadata service. So any user on that specific Droplet could retrieve the key by running curl http://169.254.169.254/metadata/v1/user-data Here's an example:

#cloud-config
ssh_keys:
  rsa_private: |
    -----BEGIN RSA PRIVATE KEY-----
    K95I4FVq5Y6lbHGxjN8v7owd2CwjIoHbmgzciJUshtLQXGXiPOLo2v1PI14IfIQf
    GUrHNw6DHIsjYMdSdfwh7LPF5yJP2xEflWljy/D4WC2CZ/XbvnBw3/T731PLbnXM
    al1O7zeNRmTUlnZjdlgQKt6bl1NbXhhNDXjETR6D03tHTwDmWhSIHBoDM1kzIyq2
    2ZqIxQECgYEA7yiKedA2ZI4iThqGH8ozvoxyTHPqpFHg5kCFEHUE9XlBnkGMMoq4
    Aq7yY8tDAem0n/lRK/sQEIVUV4c/4CDnR2ZLZtfVq4lDKTHmYJxJAYM0qTkFy/oQ
    dQv6n34PQp///g8b8k81uguA7IsaLNhADQn+/ByDyA2xGFJH9yTfJCiMJ579tldp
    FnGYFGwk5HZ9ksNPqoqb3H93SVzH0gjZqvqGqNaNAZupjHx8UNW4PX1io0agtaWW
    47Iw92J29glG5frLTGaQyzJpQwiKyxTAMgNF8ioWe3GRZrdXvhQNPDkqZqVwiSmX
    JBEH2jrDRE+rurtNQLd7GGESO2/GFRDUDcPAz0W1D9+M38VU8c4=
    -----END RSA PRIVATE KEY-----

Next, you can use the "phone home" module provided by cloud-init. This is probably the cleanest solution, but it requires that you have a service listening for callbacks. It allows you to provide a URL which will receive a POST containing the Droplet's public keys. You can limit it to only the ones you want, like so:

#cloud-config
phone_home:
    url: http://example.com/
    post:
        - pub_key_ecdsa
        - instance_id

Another draw back to the above approach is that you can not modify the format of the content. So if the service requires a specific format (e.g. Slack's incoming webhooks), you'll need a different approach. This is what I've used to post the fingerprint to Slack:

#cloud-config
runcmd:
    - DROPLET=`curl http://169.254.169.254/metadata/v1/id`
    - SSH_PUB_KEY=`ssh-keygen -lf /etc/ssh/ssh_host_ecdsa_key.pub`
    - |
        curl -X POST --data-urlencode "payload={\"text\": \"SSH Public key for Droplet $DROPLET\n$SSH_PUB_KEY\"}" https://hooks.slack.com/services/MYUNIQUE/URL

This fetches both the fingerprint and the Droplet's ID and POSTs them to a Slack URL in the required JSON format.

The DigitalOcean metadata service includes a field called "user-data", which can be used to specify a script that will be run as your server is brought online. The CloudInit program, which runs these scripts, can process a special script type called "cloud-config". In this guide, we'll explore how to create cloud-config files and the best ways to leverage their power.
Have another answer? Share your knowledge.