camptocamp / puppet-accounts

11 stars 40 forks source link

non standard authorized_key files #29

Open martin-gerdes opened 8 years ago

martin-gerdes commented 8 years ago

I am just starting out with puppet. I am administrating debian wheezy servers. Right now I am trying to migrate the user management to puppet, which is where I found this module (puppet-accounts).

In my /etc/ssh/sshd_config I have the following config: AuthorizedKeysFile /etc/authorized_keys/%u ->the filles have the primary group of the user, and mode 640.

This has always worked out great for me - the users can't change their public keys -> correct, they are centrally managed. And all the public keys are collected in one place, making applying changes simple. Whether or not this is the ideal solution, I saw no reason to change this structure just because I am starting to use puppet.

However, this module has no easy way to point to the authorized key file (doing it for every key is not dry), and neither this module nor puppet offer an easy way to set the file permissions (again, declaring a resource for every key file repeats information).

So I took a look at the coding of this module, and modified it to make it flexible enough for my needs. I am posting these modifications here in case anyone else is interested, or in case something similar might be even incorporated into the module. That way, I could update the module later on (right now, I have effectively forked it). Obviously, since I am just starting out with puppet, this code will not be ideal. Anyway, here goes (with comments):

init.pp:

# See README.md for details.
class accounts(
  $groups                   = {},
  $groups_membership        = undef,
  $ssh_keys                 = {},
  $ssh_keys_group           = undef,
  $ssh_keys_mode            = undef,
  $ssh_keys_target          = undef,
  $ssh_keys_type            = undef,
  $ssh_keys_user            = '%{account}',
  $users                    = {},
  $usergroups               = {},
  $accounts                 = {},
  $start_uid                = undef,
  $start_gid                = undef,
  $purge_ssh_keys           = false,
  $ssh_authorized_key_title = '%{ssh_key}-on-%{account}',
  $shell                    = undef,
) {
  include ::accounts::config

  create_resources(group, $groups)

  create_resources(accounts::account, $accounts)

  # Remove users marked as absent
  $absent_users = keys(absents($users))
  user { $absent_users:
    ensure => absent,
  }
}

All I have done here, is adding new variables $ssh_keys_group, $ssh_keys_mode, $ssh_keys_target, $ssh_keys_type and $ssh_keys_user target, type and user will act as defaults, while mode and group can only be set here.

authorized_key.pp:

# See README.md for details.
define accounts::authorized_key(
  $ensure                   = present,
  $ssh_key                  = regsubst($name, '^(\S+)-on-\S+$', '\1'),
  $account                  = regsubst($name, '^\S+-on-(\S+)$', '\1'),
  $options                  = undef,
  $target                   = undef,
  $ssh_authorized_key_title = $::accounts::ssh_authorized_key_title,
) {
  validate_hash($::accounts::ssh_keys)

  # Retrieve $ssh_keys and $users in the current scope
  $ssh_keys = $::accounts::ssh_keys
  $users    = $::accounts::users

  if $ssh_key =~ /^@(\S+)$/ {
    if ! has_key($::accounts::usergroups, $1) {
      fail "Can't find usergroup : ${1}"
    }
    ensure_resource(
      accounts::authorized_key,
      suffix($::accounts::usergroups[$1], "-on-${account}"),
      {
        ensure                   => $ensure,
        options                  => $options,
        target                   => $target,
        ssh_authorized_key_title => $ssh_authorized_key_title,
      }
    )
  } else {
    if has_key($::accounts::ssh_keys, $ssh_key) {
      if $::accounts::ssh_keys[$ssh_key]['title'] == undef {
        $_ssh_authorized_key_title = strformat($ssh_authorized_key_title)
      } else {
        $_ssh_authorized_key_title = $::accounts::ssh_keys[$ssh_key]['title']
      }

      if $::accounts::ssh_keys[$ssh_key]['type'] == undef {
        $_type=$::accounts::ssh_keys_type
      } else {
        $_type=$::accounts::ssh_keys[$ssh_key]['type']
      }
      if $target == undef {
        $_user   = strformat($::accounts::ssh_keys_user)
        $_target = strformat($::accounts::ssh_keys_target)
      } else {
        $_user   = 'root'
        $_target = strformat($target)
      }
      ssh_authorized_key { $_ssh_authorized_key_title:
        ensure  => $ensure,
        key     => $::accounts::ssh_keys[$ssh_key]['public'],
        options => $options,
        target  => $_target,
        type    => $_type,
        user    => $_user,
      }
      if $::accounts::ssh_keys_group != undef or $::accounts::ssh_keys_mode != undef {
        ensure_resource(
          file,
          $_target,
          {
            require => Ssh_authorized_key[$_ssh_authorized_key_title],
            mode => $::accounts::ssh_keys_mode,
            group => strformat($::accounts::ssh_keys_group),
          }
        )
      }
    }
  }
}

changes: -if $::accounts::ssh_keys[$ssh_key]['type'] is unset, I replace it with the default $::accounts::ssh_keys_type -if $target is unset, I set user to $::accounts::ssh_keys_user (which is by default account, just as before), and target to $::accounts::ssh_keys_target (I have no idea why the user is set to root if a target is defined, so I have left it as is...) -if $::accounts::ssh_keys_group or $::accounts::ssh_keys_mode is set, I define a file resource which sets group and mode of the target file -added $::accounts::ssh_keys[$ssh_key]['title'], which overrides $ssh_authorized_key_title if set for a ssh-key. This way, you can keep old keys (because you match their title) instead of creating a duplicate.

All other files of this module have been left unchanged. Sorry this does not come in the form of a diff or anything. I am an administrator, not a programmer. :-) I mainly wanted to post this as a form of feedback, but feel free to ask me questions!

This is my example config using this code (tested, works):

class { 'accounts':
    ssh_keys_group => '%{account}',
    ssh_keys_mode => '640',
    ssh_keys_target => '/etc/authorized_keys/%{account}',
    ssh_keys_type => 'ssh-rsa',
    ssh_keys_user => 'root',
    ssh_keys => {
        'user1'=>{public=>'publicKeyGoesHere'},
    },
    users => {
        user1=>{groups=>['human']},
    }
}

EDIT: Got the code formatting working at last...