[FACT-1377] $ecdsakey fact does not represent specific key type Created: 2016/03/29  Updated: 2018/09/10  Resolved: 2018/03/15

Status: Closed
Project: Facter
Component/s: None
Affects Version/s: None
Fix Version/s: FACT 3.11.0, FACT 3.12.0

Type: New Feature Priority: Normal
Reporter: Matthias Baur Assignee: Branan Riley
Resolution: Fixed Votes: 1
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Probaly all


Template:
Team: Platform OS
Release Notes: New Feature
Release Notes Summary: Key type will now be included as part of the facts for each SSH key.

 Description   

This ticket already exists in the old ticket system (https://projects.puppetlabs.com/issues/18253). Therefor i will just copy the description:

There are 5 types of SSH keys: rsa, dsa, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, and ecdsa-sha2-nistp521

So puppet already accounts for the three different ecdsa keytypes, but puts them into one fact $ecdsakey

So when doing something like this:

@@sshkey { “${::fqdn}_ecdsa”:
            host_aliases => $host_aliases,
            type => ecdsa-sha2-nistp256,
            key => $::sshecdsakey,
    }

It puts the wrong keytype in known_hosts if the key is 384 or 512 bits.

Suggest adding a fact for $ecdsatype or similar when the key is detected.

(RSA and DSA keys can be various sizes and they will always be RSA and DSA… but if you change the size of the ecdsa key size it changes they keytype. Don’t ask me why.)

The ticket was originally open by Michael Henry, who i can't find within the JIRA user directory.



 Comments   
Comment by Matthias Baur [ 2017/06/20 ]

Ugly hack ahead:

#
# This function returns the key length of a ecdsa key. This is needed for the sshkey type.
#
# See: https://docs.puppet.com/puppet/latest/types/sshkey.html#sshkey-attribute-type
# Bug: https://tickets.puppetlabs.com/browse/FACT-1377
#
Puppet::Functions.create_function(:ecdsa_key_length) do
 
  dispatch :ecdsa_key_length do
    param 'String', :key
  end
 
  def ecdsa_key_length(key)
    length_options = ['256', '384', '521']
    begin
      try_length = length_options.pop()
      cmd = [
        '/bin/bash',
        '-c',
        "/usr/bin/ssh-keygen -l -f /dev/stdin <<< 'ecdsa-sha2-nistp#{try_length} #{key}'",
      ]
      output = Puppet::Util::Execution.execute(cmd, {:failonfail => true})
    rescue Puppet::ExecutionFailure
      raise("#{key} seems not to be a ecdsa key. Tried key lengths: 256, 384 and 521") if length_options.length == 0
      retry
    end
    length = output.split(' ')[0]
    if length.to_i.is_a?(Integer)
      length
    else
      raise("Expected to get a Integer value. Got ##{length}#.")
    end
  end
end

Comment by Daniel Parks [ 2017/07/19 ]

This effectively prevents using the built-in fact to populate known_hosts, which is kind of the obvious use for it.

A simple fact that gets you the necessary information to populate a known_hosts file:

lib/facter/ssh_host_keys.rb

Facter.add(:ssh_host_keys) do
  setcode do
    types = {}
    Dir.glob("/etc/ssh/ssh_host_*.pub").each do |path|
      type, key, comment = File.read(path).chomp.split(" ", 3)
 
      # Skip the SSH v1 key in /etc/ssh/ssh_host_key.pub
      if type =~ /^[a-z]/
        types[type] = {
          "key" => key,
          "comment" => comment
        }
      end
    end
 
    types
  end
end

Tested trivially on the hosts I had laying around:

  • CentOS 6.5
  • CentOS 7.3.1611
  • OS X Sierra 10.12.5
  • Debian 8.8/jessie
  • Debian 7.8/wheezy
  • Solaris 11.3

I'm kind of amazed this works on all of those platforms. This has some obvious flaws:

  • It doesn't query the running server or its configuration; it just uses the default key locations
  • It doesn't handle multiple keys of the same type. I don't know if sshd handles that either.
  • It's quite possible that the SSH host key format allows for multiple keys, or other metadata.
  • If one file fails to read the entire fact fails.
  • It skips the version 1 key.
  • It doesn't do any validation of key type.
Comment by Daniel Parks [ 2017/07/19 ]

Here's how that fact can be used:

❯ curl -sSXGET 'http://localhost:8080/pdb/query/v4' -d limit=1 \
  -d 'query=inventory[facts] { nodes { deactivated is null and expired is null } }' \
  | jq -r '.[].facts
    | select(.ssh_host_keys and .networking and .networking.ip and .fqdn)
    | . as $facts | .ssh_host_keys | to_entries | .[]
    | "\($facts.fqdn),\($facts.networking.ip) \(.key) \(.value.key) \(.value.comment)"'
example.puppet.com,10.1.2.3 ssh-dss AAA... root@debian
example.puppet.com,10.1.2.3 ssh-rsa AAA... root@debian
example.puppet.com,10.1.2.3 ssh-ed25519 AAA... root@debian
example.puppet.com,10.1.2.3 ecdsa-sha2-nistp256 AAA... root@debian

Comment by Daniel Parks [ 2017/07/19 ]

Actually, it looks like it would be quite easy to add this to the built-in fact.

Proposal: add type and comment attributes to each of the entries in the ssh fact. The type will be the real key type, and the comment will be the comment, if it exists, or null if it doesn't.

Existing, ellipsized

Show all

❯ facter ssh
{
  ecdsa => {
    fingerprints => { ... },
    key => "..."
  },
  ed25519 => {
    fingerprints => { ... },
    key => "..."
  },
  rsa => {
    fingerprints => { ... },
    key => "..."
  }
}

Proposed, ellipsized

❯ facter ssh
{
  ecdsa => {
    fingerprints => { ... },
    key => "...",
    type => "ecdsa-sha2-nistp256",
    comment => nil
  },
  ed25519 => {
    fingerprints => { ... },
    key => "...",
    type => "ssh-ed25519",
    comment => "root@debian"
  },
  rsa => {
    fingerprints => { ... },
    key => "...",
    type => "ssh-rsa",
    comment => "root@debian"
  }
}

And, yeah, I'm volunteering. Might take me a while to get back around to this, though.

Ping Michael Smith and Josh Cooper since you seem to be the Puppet employees who have most recently touched facts. Sorry if your process already catches updates to tickets like this; I know ours does not (shame shame shame).

Comment by Daniel Parks [ 2017/12/08 ]

Finally got around to submitting this as PR #1678. I didn't include a comment attribute; it's not really used for host keys.

Comment by Matthias Baur [ 2018/03/01 ]

Thank you! Looking forward to the release including this!

Comment by Geoff Nichols [ 2018/03/14 ]

Daniel Parks, when you have a chance, could you please add release notes (or indicate they are not needed)? Thanks!

Comment by Daniel Parks [ 2018/03/15 ]

Thanks Michael Smith! Now I know how to add release notes in the future.

Generated at Fri Aug 07 09:41:19 PDT 2020 using Jira 8.5.2#805002-sha1:a66f9354b9e12ac788984e5d84669c903a370049.