12

I'm using puppet to distribute SSH keys, like so:

ssh_authorized_key { "alice@foo.com":
   ensure => present,
   key => 'xxxx',
   type => 'ssh-rsa',
   user => 'deploy',
}

The ~/.ssh/authorized_keys file ends up containing a mix of keys from multiple classes, which is the desired result. However, if a key is manually added to $HOME/.ssh/authorized_keys, Puppet will leave it in place. Is there a way to always remove any key that has not been explicitly defined in a manifest?

I have puppet version 2.7.1.

Dylan Tack
  • 223
  • 2
  • 7
  • I was thinking about using exported resources to get a copy of all my authorized_keys files into a central location. Then I could run scripts to find unusual keys, and either add them to puppet or add a resource to remove them. – Zoredache Sep 27 '11 at 19:58

4 Answers4

13

Instead of using ssh_authorized_key resources, I decided to define an authorized_keys resource, which takes a list of all SSH keys for a single user. The define looks like this:

define authorized_keys ($sshkeys, $ensure = "present", $home = '') {
    # This line allows default homedir based on $title variable.
    # If $home is empty, the default is used.
    $homedir = $home ? {'' => "/home/${title}", default => $home}
    file {
        "${homedir}/.ssh":
            ensure  => "directory",
            owner   => $title,
            group   => $title,
            mode    => 700,
            require => User[$title];
        "${homedir}/.ssh/authorized_keys":
            ensure  => $ensure,
            owner   => $ensure ? {'present' => $title, default => undef },
            group   => $ensure ? {'present' => $title, default => undef },
            mode    => 600,
            require => File["${homedir}/.ssh"],
            content => template("authorized_keys.erb");
    }
}

$ssh_keys parameter takes all necessary keys as a list. The authorized_keys.erb template looks like this:

# NOTICE: This file is autogenerated by Puppet and should not be modified
<% sshkeys.each do |key| -%>
<%= key %>
<% end -%>

Usage

user {'mikko':
    ...
}
authorized_keys {'mikko':
    sshkeys => [
        'ssh-rsa XXXXXXYYYYYYYYYZZZZZZZZZ mikko@domain.tld',
        'ssh-rsa XXXXXXZZZZZZZZZHHHHHHHHH mikko@other-host.tld',
    ],
}

Adding SSH keys conditionally (for example in different classes) is also easy, thanks to Puppet's +> operator:

Authorized_keys <| title == 'mikko' |> {
    sshkeys +> 'ssh-rsa ASDFASDFASDFASDF mikko@somewhere-else.tld'
}

With this method, the user will never have keys that are not explicitly specified in the Puppet configuration. The key string is used in authorized_keys just as it is, so adding options and restrictions is trivial.

I would be happy to hear if others have used this method successfully!

Mikko
  • 955
  • 8
  • 14
4

Starting with Puppet 3.6 it is now possible to purge unmanaged SSH authorized keys via the user type. For example,

user { 'nick':
  ensure         => present,
  purge_ssh_keys => true,
}
sciurus
  • 12,678
  • 2
  • 31
  • 49
3

You should be able to do this using the resources metatype. E.G.

resources { 'ssh_authorized_key': noop => true, purge => true, }

Setting noop => true, prevents the removal from taking place. Instead, puppet will report what would be removed. If it is what you want, remove the noop statement.

The ideal syntax for performing operations on unmanaged resources is under discussion.

EDIT: As mentioned in the comments, this answer does not work.

sciurus
  • 12,678
  • 2
  • 31
  • 49
  • This sound like just what I'm looking for. When I add this line, puppet produces this error: `puppet-agent[9895]: (/Stage[main]//Resources[ssh_authorized_key]) Failed to generate additional resources using 'generate': Attribute 'user' or 'target' is mandatory.` I tried adding a user, and then it says `Could not retrieve catalog from remote server: Error 400 on SERVER: Invalid parameter user `. Any ideas? – Dylan Tack Sep 28 '11 at 16:00
  • I'm not sure. It's probably worth asking on http://groups.google.com/group/puppet-users – sciurus Sep 28 '11 at 18:28
  • 1
    I'm really unclear why this is the accepted answer: as per Puppet Labs, this doesn't work and hasn't: http://projects.puppetlabs.com/issues/1917 . OP, did it work for you? – Bill Weiss Aug 29 '12 at 20:27
  • 1
    No, I didn't try it, I just expected it to behave like it did for other resources (e.g. hosts, nagios types). Looks like you're right and it does not work for ssh keys yet- http://projects.puppetlabs.com/issues/1581 – sciurus Aug 30 '12 at 21:09
1

On Puppet Forge a module has been published under the Apache License, Version 2.0 that offers this ability.

It relies on Puppet concat instead of templates though.

https://github.com/nightfly19/puppet-ssh_keys/tree/master/manifests

Instead of passing an array of keys as a parameter you define separate entries for each key.

Different approach from Mikko's, but same net result.

Rodney
  • 11
  • 1