# export sshd's authorized_keys fragments # # This creates exported sshd `authorized_keys` snippets that different # hosts can then collect using `ssh::authorized_key_collect`. # # This is a replacement for the builtin ssh_authorized_keys, although # it requires exported resources to work. # # The builtin type had too many problems to overcome to be useful in # our environment. A short overview of known issues, some of which are # security-sensitive: # # * MODULES-7595: ssh_authorized_key should be able to take a ready-made OpenSSH public key # * MODULES-7596: puppet ssh_authorized_key not purged as expected # * MODULES-9726: allow read-only authorized_keys # * MODULES-7610: sshkey uses name instead of title for duplication check # # There are many more issues on the sshkeys module, which doesn't seem # to be very well maintained anyways: # # https://tickets.puppetlabs.com/browse/MODULES-9726?jql=project%20%3D%20MODULES%20AND%20component%20%3D%20sshkeys_core # # @param target_user the filename to save the key under # # @param collect_tag which tag to export this resource as # # @param options a list of options, defaults to ["restrict"] # # @param command the command to enforce for this keyfile # # @param from a list of IPv4 or IPv6 address to pass to the # key's `from=` parameter. # # @param key the actual public key, including ssh-*, the public key # material and the comment define ssh::authorized_key_add( String $target_user, Variant[Array[String], String] $collect_tag, Array[String] $options = ['restrict'], Optional[String] $command = '', Optional[Array[Stdlib::IP::Address]] $from = $base::public_addresses, Optional[String] $key = undef, ) { $ssh_from_string = $from.join(',') if $command { $options_command = ["command=\"${command}\""] } else { $options_command = [] } if $ssh_from_string { $options_from = ["from=\"${ssh_from_string}\""] } else { $options_from = [] } $real_options = $options_command + $options_from + $options $options_string = $real_options.join(',') if ($key and size(split($key, "\n")) > 1) { fail('More than one line in key for ssh::authorized_key') } if (size(split($command, '"')) > 1) { fail('command must not contain double quotes') } if (size(split($ssh_from_string, '"')) > 1) { fail('from must not contain double quotes') } if $collect_tag =~ String { $raw_tags = [ $collect_tag ] } else { $raw_tags = $collect_tag } $ssh_tags = $raw_tags.map |$t| { "ssh::authorized_key::fragment::${t}::${target_user}" } $ferm_tags = $raw_tags.map |$t| { "ssh::authorized_key::ferm::${t}::${target_user}" } $ferm_from_string = $from.join(' ') if $key { @@concat::fragment { "ssh::authorized_key::${name} ${target_user} from ${::hostname}": tag => $ssh_tags, target => "/etc/ssh/puppetkeys/${target_user}", content => @("EOF"), # from ${::fqdn} ${options_string} ${key} | EOF } } else { notify { "Warning, ssh key for ${name}, ${target_user} not defined (yet?).": loglevel => warning, } } @@ferm::rule { "ssh-${raw_tags[0]}_${target_user}-${name}_from_${::hostname}": tag => $ferm_tags, description => "allow ssh for ssh to ${target_user}", domain => '(ip ip6)', chain => 'ssh', rule => "saddr (${ferm_from_string}) ACCEPT", } }