diff --git a/Modulefile b/Modulefile index b13e06770..9268adbf7 100644 --- a/Modulefile +++ b/Modulefile @@ -1,5 +1,5 @@ name 'saz-ssh' -version '1.4.0' +version '2.0.0' source 'git://github.com/saz/puppet-ssh.git' author 'saz' license 'Apache License, Version 2.0' diff --git a/README.markdown b/README.markdown index 73ee86acd..d9ebd7db5 100644 --- a/README.markdown +++ b/README.markdown @@ -2,27 +2,156 @@ Manage SSH client and server via Puppet -## Client only +### Gittip +[![Support via Gittip](https://rawgithub.com/twolfson/gittip-badge/0.2.0/dist/gittip.png)](https://www.gittip.com/saz/) + +## Requirements +* Exported resources for host keys management +* puppetlabs/stdlib + +## Usage + +Since version 2.0.0 only non-default values are written to both, +client and server, configuration files. + +Multiple occurances of one config key (e.g. sshd should be listening on +port 22 and 2222) should be passed as an array. + +``` + options => { + Port => [22, 2222], + } +``` + +This is working for both, client and server + +### Both client and server +Host keys will be collected and distributed + +``` + include ssh +``` + +or + +``` + class { 'ssh': + server_options => { + 'Match User www-data' => { + 'ChrootDirectory' => '%h', + 'ForceCommand' => 'internal-sftp', + 'PasswordAuthentication' => 'yes', + 'AllowTcpForwarding' => 'no', + 'X11Forwarding' => 'no', + }, + Port => [22, 2222, 2288], + }, + client_options => { + 'Host *.amazonaws.com' => { + 'User' => 'ec2-user', + }, + }, + } +``` + +### Client only Collected host keys from servers will be written to known_hosts ``` include ssh::client ``` -## Server only +or + +``` + class { 'ssh::client': + options => { + 'Host short' => { + 'User' => 'my-user', + 'HostName' => 'extreme.long.and.complicated.hostname.domain.tld', + }, + 'Host *' => { + 'User' => 'andromeda', + 'UserKnownHostsFile' => '/dev/null', + }, + }, + } +``` + +### Server only Host keys will be collected for client distribution ``` include ssh::server ``` -## Both client and server -Host keys will be collected and distributed +or ``` - include ssh + class { 'ssh::server': + options => { + 'Match User www-data' => { + 'ChrootDirectory' => '%h', + 'ForceCommand' => 'internal-sftp', + 'PasswordAuthentication' => 'yes', + 'AllowTcpForwarding' => 'no', + 'X11Forwarding' => 'no', + }, + 'PasswordAuthentication' => 'no', + 'PermitRootLogin' => 'no', + 'Port' => [22, 2222], + }, + } ``` -# Requirements -Requires Exported resources and augeas in order to work +## Default options + +### Client +``` + 'Host *' => { + 'SendEnv' => 'LANG LC_*', + 'HashKnownHosts' => 'yes', + 'GSSAPIAuthentication' => 'yes', + } +``` + +### Server + +``` + 'ChallengeResponseAuthentication' => 'no', + 'X11Forwarding' => 'yes', + 'PrintMotd' => 'no', + 'AcceptEnv' => 'LANG LC_*', + 'Subsystem' => 'sftp /usr/lib/openssh/sftp-server', + 'UsePAM' => 'yes', +``` + +## Overwriting default options +Default options will be merged with options passed in. +If an option is set both as default and via options parameter, the latter will +will win. + +The following example will disable X11Forwarding, which is enabled by default: + +``` + class { 'ssh::server': + options => { + 'X11Forwarding' => 'no', + }, + } +``` + +Which will lead to the following sshd_config file: + +``` +# File is managed by Puppet + +ChallengeResponseAuthentication no +X11Forwarding no +PrintMotd no +AcceptEnv LANG LC_* +Subsystem sftp /usr/lib/openssh/sftp-server +UsePAM yes +PasswordAuthentication no +``` diff --git a/manifests/client.pp b/manifests/client.pp index 8cd21e268..1a18d1bd2 100644 --- a/manifests/client.pp +++ b/manifests/client.pp @@ -1,6 +1,18 @@ -class ssh::client { - include ssh::params +class ssh::client( + $options = {} +) inherits ssh::params { + $merged_options = merge($ssh::params::ssh_default_options, $options) + include ssh::client::install include ssh::client::config include ssh::knownhosts + + anchor { 'ssh::client::start': } + anchor { 'ssh::client::end': } + + Anchor['ssh::client::start'] -> + Class['ssh::client::install'] -> + Class['ssh::client::config'] -> + Class['ssh::knownhosts'] -> + Anchor['ssh::client::end'] } diff --git a/manifests/client/config.pp b/manifests/client/config.pp index 5c4f497a3..608fd8cbd 100644 --- a/manifests/client/config.pp +++ b/manifests/client/config.pp @@ -1,8 +1,8 @@ -class ssh::client::config inherits ssh { +class ssh::client::config { file { $ssh::params::ssh_config: ensure => present, - owner => 'root', - group => 'root', + owner => 0, + group => 0, content => template("${module_name}/ssh_config.erb"), require => Class['ssh::client::install'], } diff --git a/manifests/init.pp b/manifests/init.pp index fe0ea452a..6d2cedbde 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,6 +1,12 @@ class ssh ( - $disable_user_known_hosts = true -) { - include ssh::server - include ssh::client + $server_options = {}, + $client_options = {} +) inherits ssh::params { + class { 'ssh::server': + options => $server_options, + } + + class { 'ssh::client': + options => $client_options, + } } diff --git a/manifests/params.pp b/manifests/params.pp index fb7fa2d5d..90a1dae4f 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -32,4 +32,21 @@ } } } + + $sshd_default_options = { + 'ChallengeResponseAuthentication' => 'no', + 'X11Forwarding' => 'yes', + 'PrintMotd' => 'no', + 'AcceptEnv' => 'LANG LC_*', + 'Subsystem' => 'sftp /usr/lib/openssh/sftp-server', + 'UsePAM' => 'yes', + } + + $ssh_default_options = { + 'Host *' => { + 'SendEnv' => 'LANG LC_*', + 'HashKnownHosts' => 'yes', + 'GSSAPIAuthentication' => 'yes', + }, + } } diff --git a/manifests/server.pp b/manifests/server.pp index f09a839f3..65ca278a7 100644 --- a/manifests/server.pp +++ b/manifests/server.pp @@ -1,8 +1,22 @@ -class ssh::server { - include ssh::params +class ssh::server( + $options = {} +) inherits ssh::params { + $merged_options = merge($ssh::params::sshd_default_options, $options) + include ssh::server::install include ssh::server::config include ssh::server::service include ssh::hostkeys include ssh::knownhosts + + anchor { 'ssh::server::start': } + anchor { 'ssh::server::end': } + + Anchor['ssh::server::start'] -> + Class['ssh::server::install'] -> + Class['ssh::server::config'] ~> + Class['ssh::server::service'] -> + Class['ssh::hostkeys'] -> + Class['ssh::knownhosts'] -> + Anchor['ssh::server::end'] } diff --git a/manifests/server/config.pp b/manifests/server/config.pp index 48be6f317..dc7615d56 100644 --- a/manifests/server/config.pp +++ b/manifests/server/config.pp @@ -1,11 +1,10 @@ class ssh::server::config { file { $ssh::params::sshd_config: ensure => present, - owner => 'root', - group => 'root', + owner => 0, + group => 0, mode => '0600', - replace => false, - source => "puppet:///modules/${module_name}/sshd_config", + content => template("${module_name}/sshd_config.erb"), require => Class['ssh::server::install'], notify => Class['ssh::server::service'], } diff --git a/manifests/server/configline.pp b/manifests/server/configline.pp deleted file mode 100644 index 8f8e225d6..000000000 --- a/manifests/server/configline.pp +++ /dev/null @@ -1,39 +0,0 @@ -define ssh::server::configline ( - $ensure = present, - $value = false -) { - include ssh::server - - Augeas { - context => "/files${ssh::params::sshd_config}", - notify => Class['ssh::server::service'], - require => Class['ssh::server::config'], - } - - case $ensure { - present: { - augeas { "sshd_config_${name}": - changes => "set ${name} ${value}", - onlyif => "get ${name} != ${value}", - } - } - add: { - augeas { "sshd_config_${name}": - onlyif => "get ${name}[. = '${value}'] != ${value}", - changes => [ - "ins ${name} after ${name}[last()]", - "set ${name}[last()] ${value}" - ], - } - } - absent: { - augeas { "sshd_config_${name}": - changes => "rm ${name}", - onlyif => "get ${name}", - } - } - default: { - fail("ensure value must be present, add or absent, not ${ensure}") - } - } -} diff --git a/templates/ssh_config.erb b/templates/ssh_config.erb index 4262d03be..cf4703bc0 100644 --- a/templates/ssh_config.erb +++ b/templates/ssh_config.erb @@ -1,56 +1,24 @@ +# File managed by Puppet -# This is the ssh client system-wide configuration file. See -# ssh_config(5) for more information. This file provides defaults for -# users, and the values can be changed in per-user configuration files -# or on the command line. - -# Configuration data is parsed as follows: -# 1. command line options -# 2. user-specific file -# 3. system-wide file -# Any configuration value is only changed the first time it is set. -# Thus, host-specific definitions should be at the beginning of the -# configuration file, and defaults at the end. - -# Site-wide defaults for some commonly used options. For a comprehensive -# list of available options, their meanings and defaults, please see the -# ssh_config(5) man page. - -Host * -# ForwardAgent no -# ForwardX11 no -# ForwardX11Trusted yes -# RhostsRSAAuthentication no -# RSAAuthentication yes -# PasswordAuthentication yes -# HostbasedAuthentication no -# GSSAPIAuthentication no -# GSSAPIDelegateCredentials no -# GSSAPIKeyExchange no -# GSSAPITrustDNS no -# BatchMode no -# CheckHostIP yes -# AddressFamily any -# ConnectTimeout 0 -# StrictHostKeyChecking ask -# IdentityFile ~/.ssh/identity -# IdentityFile ~/.ssh/id_rsa -# IdentityFile ~/.ssh/id_dsa -# Port 22 -# Protocol 2,1 -# Cipher 3des -# Ciphers aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc -# MACs hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160 -# EscapeChar ~ -# Tunnel no -# TunnelDevice any:any -# PermitLocalCommand no -# VisualHostKey no -# ProxyCommand ssh -q -W %h:%p gateway.example.com - SendEnv LANG LC_* - HashKnownHosts yes - GSSAPIAuthentication yes - GSSAPIDelegateCredentials no - <% if disable_user_known_hosts %> - UserKnownHostsFile /dev/null - <% end %> +<%- scope.lookupvar('ssh::client::merged_options').each do |k, v| -%> +<%- if v.is_a?(Hash) -%> +<%= k %> +<%- v.each do |key, value| -%> + <%- if value.is_a?(Array) -%> + <%- value.each do |a| -%> + <%= key %> <%= a %> + <%- end -%> + <%- else -%> + <%= key %> <%= value %> + <%- end -%> +<%- end -%> +<%- else -%> +<%- if v.is_a?(Array) -%> +<%- v.each do |a| -%> +<%= k %> <%= a %> +<%- end -%> +<%- else -%> +<%= k %> <%= v %> +<%- end -%> +<%- end -%> +<%- end -%> diff --git a/templates/sshd_config.erb b/templates/sshd_config.erb new file mode 100644 index 000000000..1fcd20944 --- /dev/null +++ b/templates/sshd_config.erb @@ -0,0 +1,24 @@ +# File is managed by Puppet + +<%- scope.lookupvar('ssh::server::merged_options').each do |k, v| -%> +<%- if v.is_a?(Hash) -%> +<%= k %> +<%- v.each do |key, value| -%> + <%- if value.is_a?(Array) -%> + <%- value.each do |a| -%> + <%= key %> <%= a %> + <%- end -%> + <%- else -%> + <%= key %> <%= value %> + <%- end -%> +<%- end -%> +<%- else -%> +<%- if v.is_a?(Array) -%> +<%- v.each do |a| -%> +<%= k %> <%= a %> +<%- end -%> +<%- else -%> +<%= k %> <%= v %> +<%- end -%> +<%- end -%> +<%- end -%>