Multiple users, same key in puppet.
by
modules/user/manifests/auth.pp
# =========
# ssh::auth
# =========
#
# The latest official release and documentation for ssh::auth can always
# be found at http://reductivelabs.com/trac/puppet/wiki/Recipes/ModuleSSHAuth .
#
# Version: 0.3.2
# Release date: 2009-12-29
class ssh::auth {
$keymaster_storage = "/var/lib/keys"
Exec { path => "/usr/bin:/usr/sbin:/bin:/sbin" }
Notify { withpath => false }
##########################################################################
# ssh::auth::key
# Declare keys. The approach here is just to define a bunch of
# virtual resources, representing key files on the keymaster, client,
# and server. The virtual keys are then realized by
# ssh::auth::{keymaster,client,server}, respectively. The reason for
# doing things that way is that it makes ssh::auth::key into a "one
# stop shop" where users can declare their keys with all of their
# parameters, whether those parameters apply to the keymaster, server,
# or client. The real work of creating, installing, and removing keys
# is done in the private definitions called by the virtual resources:
# ssh_auth_key_{master,server,client}.
define key ($ensure = "present", $filename = "", $force = false, $group = "puppet", $home = "", $keytype = "rsa", $length = 2048, $maxdays = "", $mindate = "", $options = "", $user = "", $key = "") {
ssh_auth_key_namecheck { "${title}-title": parm => "title", value => $title }
# apply defaults
$_filename = $filename ? { "" => "id_${keytype}", default => $filename }
$_length = $keytype ? { "rsa" => $length, "dsa" => 1024 }
$_user = $user ? {
"" => regsubst($title, '^([^@]*)@?.*$', '\1'),
default => $user,
}
$_home = $home ? { "" => "/home/$_user", default => $home }
ssh_auth_key_namecheck { "${title}-filename": parm => "filename", value => $_filename }
@ssh_auth_key_master { $title:
ensure => $ensure,
force => $force,
keytype => $keytype,
length => $_length,
maxdays => $maxdays,
mindate => $mindate,
}
@ssh_auth_key_client { $title:
ensure => $ensure,
filename => $_filename,
group => $group,
home => $_home,
user => $_user,
}
@ssh_auth_key_server { $title:
ensure => $ensure,
group => $group,
home => $_home,
options => $options,
user => $_user,
key => $key,
}
}
##########################################################################
# ssh::auth::keymaster
#
# Keymaster host:
# Create key storage; create, regenerate, and remove key pairs
class keymaster {
# Set up key storage
file { $ssh::auth::keymaster_storage:
ensure => directory,
owner => puppet,
group => puppet,
mode => 644,
}
# Realize all virtual master keys
Ssh_auth_key_master
} # class keymaster
##########################################################################
# ssh::auth::client
#
# Install generated key pairs onto clients
define client ($ensure = "", $filename = "", $group = "", $home = "", $user = "") {
# Realize the virtual client keys.
# Override the defaults set in ssh::auth::key, as needed.
if $ensure { Ssh_auth_key_client { ensure => $ensure } }
if $filename { Ssh_auth_key_client { filename => $filename } }
if $group { Ssh_auth_key_client { group => $group } }
if $user { Ssh_auth_key_client { user => $user, home => "/home/$user" } }
if $home { Ssh_auth_key_client { home => $home } }
if $key { Ssh_auth_key_client { key => $key } }
realize Ssh_auth_key_client[$title]
} # define client
##########################################################################
# ssh::auth::server
#
# Install public keys onto clients
define server ($ensure = "", $group = "", $home = "", $options = "", $user = "", $key="") {
# Realize the virtual server keys.
# Override the defaults set in ssh::auth::key, as needed.
if $ensure { Ssh_auth_key_server { ensure => $ensure } }
if $group { Ssh_auth_key_server { group => $group } }
if $options { Ssh_auth_key_server { options => $options } }
if $user { Ssh_auth_key_server { user => $user, home => "/home/$user" } }
if $home { Ssh_auth_key_server { home => $home } }
if $key { Ssh_auth_key_server { key => $key } }
realize Ssh_auth_key_server[$title]
} # define server
} # class ssh::auth
##########################################################################
# ssh_auth_key_master
#
# Create/regenerate/remove a key pair on the keymaster.
# This definition is private, i.e. it is not intended to be called directly by users.
# ssh::auth::key calls it to create virtual keys, which are realized in ssh::auth::keymaster.
define ssh_auth_key_master ($ensure, $force, $keytype, $length, $maxdays, $mindate) {
Exec { path => "/usr/bin:/usr/sbin:/bin:/sbin" }
File {
owner => puppet,
group => puppet,
mode => 600,
}
$keydir = "${ssh::auth::keymaster_storage}/${title}"
$keyfile = "${keydir}/key"
file {
"$keydir":
ensure => directory,
mode => 644;
"$keyfile":
ensure => $ensure;
"${keyfile}.pub":
ensure => $ensure,
mode => 644;
}
if $ensure == "present" {
# Remove the existing key pair, if
# * $force is true, or
# * $maxdays or $mindate criteria aren't met, or
# * $keytype or $length have changed
$keycontent = file("${keyfile}.pub", "/dev/null")
if $keycontent {
if $force {
$reason = "force=true"
}
if !$reason and $mindate and generate("/usr/bin/find", $keyfile, "!", "-newermt", "${mindate}") {
$reason = "created before ${mindate}"
}
if !$reason and $maxdays and generate("/usr/bin/find", $keyfile, "-mtime", "+${maxdays}") {
$reason = "older than ${maxdays} days"
}
if !$reason and $keycontent =~ /^ssh-... [^ ]+ (...) (\d+)$/ {
if $keytype != $1 { $reason = "keytype changed: $1 -> $keytype" }
else { if $length != $2 { $reason = "length changed: $2 -> $length" } }
}
if $reason {
exec { "Revoke previous key ${title}: ${reason}":
command => "rm $keyfile ${keyfile}.pub",
before => Exec["Create key $title: $keytype, $length bits"],
}
}
}
# Create the key pair.
# We "repurpose" the comment field in public keys on the keymaster to
# store data about the key, i.e. $keytype and $length. This avoids
# having to rerun ssh-keygen -l on every key at every run to determine
# the key length.
exec { "Create key $title: $keytype, $length bits":
command => "ssh-keygen -t ${keytype} -b ${length} -f ${keyfile} -C \"${keytype} ${length}\" -N \"\"",
user => "puppet",
group => "puppet",
creates => $keyfile,
require => File[$keydir],
before => File[$keyfile, "${keyfile}.pub"],
}
} # if $ensure == "present"
} # define ssh_auth_key_master
##########################################################################
# ssh_auth_key_client
#
# Install a key pair into a user's account.
# This definition is private, i.e. it is not intended to be called directly by users.
define ssh_auth_key_client ($ensure, $filename, $group, $home, $user) {
File {
owner => $user,
group => $group,
mode => 600,
require => [ User[$user], File[$home]],
}
$key_src_file = "${ssh::auth::keymaster_storage}/${title}/key" # on the keymaster
$key_tgt_file = "${home}/.ssh/${filename}" # on the client
$key_src_content_pub = file("${key_src_file}.pub", "/dev/null")
if $ensure == "absent" or $key_src_content_pub =~ /^(ssh-...) ([^ ]+)/ {
$keytype = $1
$modulus = $2
file {
$key_tgt_file:
ensure => $ensure,
content => file($key_src_file, "/dev/null");
"${key_tgt_file}.pub":
ensure => $ensure,
content => "$keytype $modulus $title\n",
mode => 644;
}
} else {
notify { "Private key file $key_src_file for key $title not found on keymaster; skipping ensure => present": }
}
} # define ssh_auth_key_client
##########################################################################
# ssh_auth_key_server
#
# Install a public key into a server user's authorized_keys(5) file.
# This definition is private, i.e. it is not intended to be called directly by users.
define ssh_auth_key_server ($ensure, $group, $home, $options, $user, $key) {
# on the keymaster:
$key_src_dir = "${ssh::auth::keymaster_storage}/${key}"
$key_src_file = "${key_src_dir}/key.pub"
# on the server:
$key_tgt_file = "${home}/.ssh/authorized_keys"
File {
owner => $user,
group => $group,
require => User[$user],
mode => 600,
}
Ssh_authorized_key {
user => $user,
target => $key_tgt_file,
}
if $ensure == "absent" {
ssh_authorized_key { $title: ensure => "absent" }
}
else {
$key_src_content = file($key_src_file, "/dev/null")
if ! $key_src_content {
notify { "Public key file $key_src_file for key $_key not found on keymaster; skipping ensure => present": }
} else { if $ensure == "present" and $key_src_content !~ /^(ssh-...) ([^ ]*)/ {
err("Can't parse public key file $key_src_file")
notify { "Can't parse public key file $key_src_file for key $_key on the keymaster: skipping ensure => $ensure": }
} else {
$keytype = $1
$modulus = $2
ssh_authorized_key { $title:
ensure => "present",
type => $keytype,
key => $modulus,
options => $options ? { "" => undef, default => $options },
}
}} # if ... else ... else
} # if ... else
} # define ssh_auth_key_server
##########################################################################
# ssh_auth_key_namecheck
#
# Check a name (e.g. key title or filename) for the allowed form
define ssh_auth_key_namecheck ($parm, $value) {
if $value !~ /^[A-Za-z0-9]/ {
fail("ssh::auth::key: $parm '$value' not allowed: must begin with a letter or digit")
}
if $value !~ /^[A-Za-z0-9_.:@-]+$/ {
fail("ssh::auth::key: $parm '$value' not allowed: may only contain the characters A-Za-z0-9_.:@-")
}
} # define namecheck
Usage
Using the new calls is easy. Assuming you've cerated your user addding the following lines will allow the previously created andrewmccall key to login as root:manifests/nodes.pp
node build {
include sudo, user, sshd, ssh::auth, user::keystore, repos
user::create{"andrewmccall": groups => "sudo"}
user::client_key{"andrewmccall":}
user::server_key{"andrewmccall":}
ssh::auth::key{"andrewmccall-root": key=>"andrewmccall"}
ssh::auth::server{"andrewmccall-root": key=>"andrewmccall", user=>"root", home=>"/root"}
}
Subscribe via RSS