Home > Puppet, RedHat > Managing ssh host keys with Puppet

Managing ssh host keys with Puppet

Death of mother Earth
Never a rebirth
Evolution’s end
Never will it mend

(Metallica – Blackened)

Do you get annoyed when after every clean installation of an existing node you have to clean up your known_hosts file, accept new host key? Especially if you manage development machines that are reinstalled often. As we are starting to use Puppet extensively, I’ve thought about solving this problem with it. And I came up with pretty neat solution that I hope will benefit the masses. I’ve created ssh module, which has ssh::server class that looks like this:

class ssh::server {
...
  if generate('/etc/puppet/modules/ssh/scripts/generate_host_keys.sh',
$keys_dir) {
    include ssh::server::keys
  }
...
}

Generate function calls shell script, and if it’s exit status is 0, ssh::server class includes ssh::server::key class. The latter class defines the ssh host keys (as file resources), sets the correct permissions and reloads ssh service if any of the files/keys changes. This is an example of one file resource in that class:

file { '/etc/ssh/ssh_host_rsa_key.pub':
  ensure  => file,
  owner   => root,
  group   => root,
  mode    => '0644',
  source  => [
    'puppet:///private/ssh/ssh_host_rsa_key.pub',
    'puppet:///modules/ssh/ssh_host_rsa_key.pub',
  ],
  require => Package['openssh-server'],
  notify  => Service[$service_name],
}

Private section of puppet server is set up in /etc/puppet/fileserver.conf. Beauty of this approach is that each agent knows only for the existence of directories under his own $fqdn, so one host can’t access other hosts sensitive data (like ssh host keys). Second line in “source” property array value is just as backend if, for some reason, first one is not found. Class ssh:server:keys is not designed to be included directly in node manifest, but to be used as ssh::server class’s helper, so thas should not happen. This is a snippet from fileserver.conf:

[private]
 path /etc/puppet/private/%H
 allow *

and this is the directory structure:

# find /etc/puppet/private/
/etc/puppet/private/
/etc/puppet/private/host1.example.lan
/etc/puppet/private/host2.example.lan
/etc/puppet/private/host2.example.lan/ssh
/etc/puppet/private/host2.example.lan/ssh/ssh_host_key.pub
/etc/puppet/private/host2.example.lan/ssh/ssh_host_dsa_key
/etc/puppet/private/host2.example.lan/ssh/ssh_host_dsa_key.pub
/etc/puppet/private/host2.example.lan/ssh/ssh_host_key
/etc/puppet/private/host2.example.lan/ssh/ssh_host_rsa_key
/etc/puppet/private/host2.example.lan/ssh/ssh_host_rsa_key.pu

As you can see, host2 has keys already generated and host1 doesn’t. And, now the magic glue behind all of this, generate_host_keys.sh script:

#!/bin/bash

# check arg0: dir for keys
[ -z "$1" ] && echo "Please specify directory for key generation" && exit 1
KEYSDIR="$1"

# set umask
umask 0022

# create directory tree if it does not exist
[ ! -d "$KEYSDIR" ] && mkdir -p $KEYSDIR

#
# functions stolen from CentOS 6 sshd init script
#

# Some functions to make the below more readable
KEYGEN=/usr/bin/ssh-keygen
RSA1_KEY=$1/ssh_host_key
RSA_KEY=$1/ssh_host_rsa_key
DSA_KEY=$1/ssh_host_dsa_key

# source function library
. /etc/rc.d/init.d/functions

fips_enabled() {
  if [ -r /proc/sys/crypto/fips_enabled ]; then
    cat /proc/sys/crypto/fips_enabled
  else  
    echo 0
  fi
}

do_rsa1_keygen() {
  if [ ! -s $RSA1_KEY -a `fips_enabled` -eq 0 ]; then
    echo -n $"Generating SSH1 RSA host key: "
    rm -f $RSA1_KEY
    if test ! -f $RSA1_KEY && $KEYGEN -q -t rsa1 -f $RSA1_KEY -C '' -N '' >&/dev/null; then
      chmod 600 $RSA1_KEY
      chmod 644 $RSA1_KEY.pub
      success $"RSA1 key generation"
      echo
    else  
      failure $"RSA1 key generation"
      echo
      exit 1
    fi
  fi
}

do_rsa_keygen() {
  if [ ! -s $RSA_KEY ]; then
    echo -n $"Generating SSH2 RSA host key: "
    rm -f $RSA_KEY
    if test ! -f $RSA_KEY && $KEYGEN -q -t rsa -f $RSA_KEY -C '' -N '' >&/dev/null; then
      chmod 600 $RSA_KEY
      chmod 644 $RSA_KEY.pub
      success $"RSA key generation"
      echo
    else  
      failure $"RSA key generation"
      echo
      exit 1
    fi
  fi
}

do_dsa_keygen() {
  if [ ! -s $DSA_KEY ]; then
    echo -n $"Generating SSH2 DSA host key: "
    rm -f $DSA_KEY
    if test ! -f $DSA_KEY && $KEYGEN -q -t dsa -f $DSA_KEY -C '' -N '' >&/dev/null; then
      chmod 600 $DSA_KEY
      chmod 644 $DSA_KEY.pub
      success $"DSA key generation"
      echo
    else
      failure $"DSA key generation"
      echo
      exit 1
    fi
  fi
}

# main
do_rsa1_keygen
do_rsa_keygen
do_dsa_keygen
chmod -R 644 $KEYSDIR/*
exit 0

Hope you like the solution, and I promise to publish the whole module on the forge when it’s polished enough!

Advertisements
Categories: Puppet, RedHat Tags: , , ,
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: