diff --git a/README.md b/README.md index 6876f9e..42c3d96 100644 --- a/README.md +++ b/README.md @@ -217,8 +217,47 @@ Content of root's ~/.ssh/config. - *Default*: "# This file is being maintained by Puppet.\n# DO NOT EDIT\n" -=== +$sshd_password_authentication +----------------------------- +PasswordAuthentication in sshd_config. +Specifies whether password authentication is allowed. +- *Default*: 'yes' + +sshd_allow_tcp_forwarding +------------------------- +AllowTcpForwarding in sshd_config. +Specifies whether TCP forwarding is permitted. + +- *Default*: 'yes' + +sshd_x11_forwarding +------------------- +X11Forwarding in sshd_config. +Specifies whether X11 forwarding is permitted. + +- *Default*: 'no' + +sshd_use_pam +------------ +UsePam in sshd_config. +Enables the Pluggable Authentication Module interface. If set to 'yes' this will enable PAM +authentication using ChallengeResponseAuthentication and PasswordAuthentication in addition +to PAM account and session module processing for all authentication types. + +- *Default*: 'no' + +sshd_client_alive_interval +-------------------------- +ClientAliveInterval in sshd_config. +Sets a timeout interval in seconds after which if no data has been received from the client, +sshd(8) will send a message through the encrypted channel to request a response from the +client. The default is 0, indicating that these messages will not be sent to the client. +This option applies to protocol version 2 only. + +- *Default*: '0' + +=== # Manage user's ssh_authorized_keys This works by passing the ssh::keys hash to the ssh_authorized_keys type with create_resources(). Because of this, you may specify any valid parameter for ssh_authorized_key. See the [Type Reference](http://docs.puppetlabs.com/references/stable/type.html#ssh_authorized_key) for a complete list. diff --git a/manifests/init.pp b/manifests/init.pp index b7d3fbb..378993e 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -36,8 +36,21 @@ class ssh ( $keys = undef, $manage_root_ssh_config = 'false', $root_ssh_config_content = "# This file is being maintained by Puppet.\n# DO NOT EDIT\n", + $sshd_password_authentication = 'yes', + $sshd_allow_tcp_forwarding = 'yes', + $sshd_x11_forwarding = 'yes', + $sshd_use_pam = 'yes', + $sshd_client_alive_interval = '0', ) { + # + validate_re($sshd_password_authentication, '^(yes|no)$', "sshd_password_authentication may be either 'yes' or 'no' and is set to '${sshd_password_authentication}'.") + validate_re($sshd_allow_tcp_forwarding, '^(yes|no)$', "sshd_allow_tcp_forwarding may be either 'yes' or 'no' and is set to '${sshd_allow_tcp_forwarding}'.") + validate_re($sshd_x11_forwarding, '^(yes|no)$', "sshd_x11_forwarding may be either 'yes' or 'no' and is set to '${sshd_x11_forwarding}'.") + validate_re($sshd_use_pam, '^(yes|no)$', "sshd_use_pam may be either 'yes' or 'no' and is set to '${sshd_use_pam}'.") + if is_integer($sshd_client_alive_interval) == false { fail("sshd_client_alive_interval must be an integer and is set to '${sshd_client_alive_interval}'.") } + # + case $permit_root_login { 'no', 'yes', 'without-password', 'forced-commands-only': { # noop diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb index d1e6c4e..81dbcf0 100644 --- a/spec/classes/init_spec.rb +++ b/spec/classes/init_spec.rb @@ -57,6 +57,11 @@ describe 'ssh' do it { should contain_file('sshd_config').with_content(/^Banner none$/) } it { should contain_file('sshd_config').with_content(/^XAuthLocation \/usr\/bin\/xauth$/) } it { should contain_file('sshd_config').with_content(/^Subsystem sftp \/usr\/libexec\/openssh\/sftp-server$/) } + it { should contain_file('sshd_config').with_content(/^PasswordAuthentication yes$/) } + it { should contain_file('sshd_config').with_content(/^AllowTcpForwarding yes$/) } + it { should contain_file('sshd_config').with_content(/^X11Forwarding yes$/) } + it { should contain_file('sshd_config').with_content(/^UsePAM yes$/) } + it { should contain_file('sshd_config').with_content(/^ClientAliveInterval 0$/) } it { should contain_service('sshd_service').with({ @@ -150,6 +155,11 @@ describe 'ssh' do it { should contain_file('sshd_config').with_content(/^Banner none$/) } it { should contain_file('sshd_config').with_content(/^XAuthLocation \/usr\/bin\/xauth$/) } it { should contain_file('sshd_config').with_content(/^Subsystem sftp \/usr\/lib\/openssh\/sftp-server$/) } + it { should contain_file('sshd_config').with_content(/^PasswordAuthentication yes$/) } + it { should contain_file('sshd_config').with_content(/^AllowTcpForwarding yes$/) } + it { should contain_file('sshd_config').with_content(/^X11Forwarding yes$/) } + it { should contain_file('sshd_config').with_content(/^UsePAM yes$/) } + it { should contain_file('sshd_config').with_content(/^ClientAliveInterval 0$/) } it { should contain_service('sshd_service').with({ @@ -169,6 +179,168 @@ describe 'ssh' do } end + context 'with default params on osfamily Suse architecture x86_64' do + let :facts do + { + :fqdn => 'monkey.example.com', + :osfamily => 'Suse', + :architecture => 'x86_64', + :sshrsakey => 'AAAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ==' + } + end + it { should include_class('ssh')} + + it { should_not include_class('common')} + + it { + should contain_package('ssh_packages').with({ + 'ensure' => 'installed', + 'name' => 'openssh', + }) + } + + it { + should contain_file('ssh_config').with({ + 'ensure' => 'file', + 'path' => '/etc/ssh/ssh_config', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + 'require' => 'Package[ssh_packages]', + }) + } + + it { should contain_file('ssh_config').with_content(/^# This file is being maintained by Puppet.\n# DO NOT EDIT\n\n# \$OpenBSD: ssh_config,v 1.21 2005\/12\/06 22:38:27 reyk Exp \$/) } + + it { should_not contain_file('ssh_config').with_content(/^\s*ForwardAgent$/) } + it { should_not contain_file('ssh_config').with_content(/^\s*ForwardX11$/) } + it { should_not contain_file('ssh_config').with_content(/^\s*ServerAliveInterval$/) } + + it { + should contain_file('sshd_config').with({ + 'ensure' => 'file', + 'path' => '/etc/ssh/sshd_config', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0600', + 'require' => 'Package[ssh_packages]', + }) + } + + it { should contain_file('sshd_config').with_content(/^SyslogFacility AUTH$/) } + it { should contain_file('sshd_config').with_content(/^LoginGraceTime 120$/) } + it { should contain_file('sshd_config').with_content(/^PermitRootLogin no$/) } + it { should contain_file('sshd_config').with_content(/^ChallengeResponseAuthentication no$/) } + it { should contain_file('sshd_config').with_content(/^PrintMotd yes$/) } + it { should contain_file('sshd_config').with_content(/^UseDNS yes$/) } + it { should contain_file('sshd_config').with_content(/^Banner none$/) } + it { should contain_file('sshd_config').with_content(/^XAuthLocation \/usr\/bin\/xauth$/) } + it { should contain_file('sshd_config').with_content(/^Subsystem sftp \/usr\/lib64\/ssh\/sftp-server$/) } + it { should contain_file('sshd_config').with_content(/^PasswordAuthentication yes$/) } + it { should contain_file('sshd_config').with_content(/^AllowTcpForwarding yes$/) } + it { should contain_file('sshd_config').with_content(/^X11Forwarding yes$/) } + it { should contain_file('sshd_config').with_content(/^UsePAM yes$/) } + it { should contain_file('sshd_config').with_content(/^ClientAliveInterval 0$/) } + + it { + should contain_service('sshd_service').with({ + 'ensure' => 'running', + 'name' => 'sshd', + 'enable' => 'true', + 'hasrestart' => 'true', + 'hasstatus' => 'true', + 'subscribe' => 'File[sshd_config]', + }) + } + + it { + should contain_resources('sshkey').with({ + 'purge' => 'true', + }) + } + end + + context 'with default params on osfamily Suse architecture i386' do + let :facts do + { + :fqdn => 'monkey.example.com', + :osfamily => 'Suse', + :architecture => 'i386', + :sshrsakey => 'AAAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ==' + } + end + it { should include_class('ssh')} + + it { should_not include_class('common')} + + it { + should contain_package('ssh_packages').with({ + 'ensure' => 'installed', + 'name' => 'openssh', + }) + } + + it { + should contain_file('ssh_config').with({ + 'ensure' => 'file', + 'path' => '/etc/ssh/ssh_config', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + 'require' => 'Package[ssh_packages]', + }) + } + + it { should contain_file('ssh_config').with_content(/^# This file is being maintained by Puppet.\n# DO NOT EDIT\n\n# \$OpenBSD: ssh_config,v 1.21 2005\/12\/06 22:38:27 reyk Exp \$/) } + + it { should_not contain_file('ssh_config').with_content(/^\s*ForwardAgent$/) } + it { should_not contain_file('ssh_config').with_content(/^\s*ForwardX11$/) } + it { should_not contain_file('ssh_config').with_content(/^\s*ServerAliveInterval$/) } + + it { + should contain_file('sshd_config').with({ + 'ensure' => 'file', + 'path' => '/etc/ssh/sshd_config', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0600', + 'require' => 'Package[ssh_packages]', + }) + } + + it { should contain_file('sshd_config').with_content(/^SyslogFacility AUTH$/) } + it { should contain_file('sshd_config').with_content(/^LoginGraceTime 120$/) } + it { should contain_file('sshd_config').with_content(/^PermitRootLogin no$/) } + it { should contain_file('sshd_config').with_content(/^ChallengeResponseAuthentication no$/) } + it { should contain_file('sshd_config').with_content(/^PrintMotd yes$/) } + it { should contain_file('sshd_config').with_content(/^UseDNS yes$/) } + it { should contain_file('sshd_config').with_content(/^Banner none$/) } + it { should contain_file('sshd_config').with_content(/^XAuthLocation \/usr\/bin\/xauth$/) } + it { should contain_file('sshd_config').with_content(/^Subsystem sftp \/usr\/lib\/ssh\/sftp-server$/) } + it { should contain_file('sshd_config').with_content(/^PasswordAuthentication yes$/) } + it { should contain_file('sshd_config').with_content(/^AllowTcpForwarding yes$/) } + it { should contain_file('sshd_config').with_content(/^X11Forwarding yes$/) } + it { should contain_file('sshd_config').with_content(/^UsePAM yes$/) } + it { should contain_file('sshd_config').with_content(/^ClientAliveInterval 0$/) } + + it { + should contain_service('sshd_service').with({ + 'ensure' => 'running', + 'name' => 'sshd', + 'enable' => 'true', + 'hasrestart' => 'true', + 'hasstatus' => 'true', + 'subscribe' => 'File[sshd_config]', + }) + } + + it { + should contain_resources('sshkey').with({ + 'purge' => 'true', + }) + } + end + context 'with optional params used in ssh_config set on osfamily RedHat' do let :facts do { @@ -221,6 +393,11 @@ describe 'ssh' do :sshd_config_banner => '/etc/sshd_banner', :sshd_config_xauth_location => '/opt/ssh/bin/xauth', :sshd_config_subsystem_sftp => '/opt/ssh/bin/sftp', + :sshd_password_authentication => 'no', + :sshd_allow_tcp_forwarding => 'no', + :sshd_x11_forwarding => 'no', + :sshd_use_pam => 'no', + :sshd_client_alive_interval => '242', } end @@ -244,6 +421,11 @@ describe 'ssh' do it { should contain_file('sshd_config').with_content(/^Banner \/etc\/sshd_banner$/) } it { should contain_file('sshd_config').with_content(/^XAuthLocation \/opt\/ssh\/bin\/xauth$/) } it { should contain_file('sshd_config').with_content(/^Subsystem sftp \/opt\/ssh\/bin\/sftp$/) } + it { should contain_file('sshd_config').with_content(/^PasswordAuthentication no$/) } + it { should contain_file('sshd_config').with_content(/^AllowTcpForwarding no$/) } + it { should contain_file('sshd_config').with_content(/^X11Forwarding no$/) } + it { should contain_file('sshd_config').with_content(/^UsePAM no$/) } + it { should contain_file('sshd_config').with_content(/^ClientAliveInterval 242$/) } end context 'with manage_root_ssh_config set to \'true\' on valid osfamily' do @@ -305,6 +487,101 @@ describe 'ssh' do end end + context 'with sshd_password_authentication set to invalid value on valid osfamily' do + let :facts do + { + :fqdn => 'monkey.example.com', + :osfamily => 'RedHat', + :sshrsakey => 'AAAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ==' + } + end + let :params do + { :sshd_password_authentication => 'invalid' } + end + + it 'should fail' do + expect { + should include_class('ssh') + }.to raise_error(Puppet::Error,/sshd_password_authentication may be either \'yes\' or \'no\' and is set to \'invalid\'./) + end + end + + context 'with sshd_allow_tcp_forwarding set to invalid value on valid osfamily' do + let :facts do + { + :fqdn => 'monkey.example.com', + :osfamily => 'RedHat', + :sshrsakey => 'AAAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ==' + } + end + let :params do + { :sshd_allow_tcp_forwarding => 'invalid' } + end + + it 'should fail' do + expect { + should include_class('ssh') + }.to raise_error(Puppet::Error,/sshd_allow_tcp_forwarding may be either \'yes\' or \'no\' and is set to \'invalid\'./) + end + end + + context 'with sshd_x11_forwarding set to invalid value on valid osfamily' do + let :facts do + { + :fqdn => 'monkey.example.com', + :osfamily => 'RedHat', + :sshrsakey => 'AAAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ==' + } + end + let :params do + { :sshd_x11_forwarding => 'invalid' } + end + + it 'should fail' do + expect { + should include_class('ssh') + }.to raise_error(Puppet::Error,/sshd_x11_forwarding may be either \'yes\' or \'no\' and is set to \'invalid\'./) + end + end + + context 'with sshd_use_pam set to invalid value on valid osfamily' do + let :facts do + { + :fqdn => 'monkey.example.com', + :osfamily => 'RedHat', + :sshrsakey => 'AAAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ==' + } + end + let :params do + { :sshd_use_pam => 'invalid' } + end + + it 'should fail' do + expect { + should include_class('ssh') + }.to raise_error(Puppet::Error,/sshd_use_pam may be either \'yes\' or \'no\' and is set to \'invalid\'./) + end + end + + context 'with sshd_client_alive_interval set to invalid value on valid osfamily' do + let :facts do + { + :fqdn => 'monkey.example.com', + :osfamily => 'RedHat', + :sshrsakey => 'AAAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ==' + } + end + let :params do + { :sshd_client_alive_interval => 'invalid' } + end + + it 'should fail' do + expect { + should include_class('ssh') + }.to raise_error(Puppet::Error,/sshd_client_alive_interval must be an integer and is set to \'invalid\'./) + end + end + context 'with manage_firewall set to true on valid osfamily' do let :facts do { diff --git a/templates/sshd_config.erb b/templates/sshd_config.erb index fc4ab00..3595f50 100644 --- a/templates/sshd_config.erb +++ b/templates/sshd_config.erb @@ -28,7 +28,7 @@ Protocol 2 # Lifetime and size of ephemeral version 1 server key #KeyRegenerationInterval 1h -#ServerKeyBits 768 +#ServerKeyBits 1024 # Logging # obsoletes QuietMode and FascistLogging @@ -40,6 +40,7 @@ SyslogFacility <%= @sshd_config_syslog_facility %> #LoginGraceTime 120 LoginGraceTime <%= @sshd_config_login_grace_time %> +#PermitRootLogin yes PermitRootLogin <%= @permit_root_login %> #StrictModes yes #MaxAuthTries 6 @@ -60,8 +61,8 @@ PermitRootLogin <%= @permit_root_login %> # To disable tunneled clear text passwords, change to no here! #PasswordAuthentication yes +PasswordAuthentication <%= @sshd_password_authentication %> #PermitEmptyPasswords no -PasswordAuthentication yes # Change to no to disable s/key passwords #ChallengeResponseAuthentication yes @@ -88,16 +89,17 @@ GSSAPICleanupCredentials yes # session checks to run without PAM authentication, then enable this but set # ChallengeResponseAuthentication=no #UsePAM no -UsePAM yes +UsePAM <%= @sshd_use_pam %> # Accept locale-related environment variables AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT AcceptEnv LC_IDENTIFICATION LC_ALL #AllowTcpForwarding yes +AllowTcpForwarding <%= @sshd_allow_tcp_forwarding %> #GatewayPorts no #X11Forwarding no -X11Forwarding yes +X11Forwarding <%= @sshd_x11_forwarding %> #X11DisplayOffset 10 #X11UseLocalhost yes #PrintMotd yes @@ -109,6 +111,7 @@ PrintMotd <%= @sshd_config_print_motd %> #PermitUserEnvironment no #Compression delayed #ClientAliveInterval 0 +ClientAliveInterval <%= @sshd_client_alive_interval %> #ClientAliveCountMax 3 #ShowPatchLevel no #UseDNS yes