diff --git a/README.md b/README.md index 3bbb4c16..0233929c 100644 --- a/README.md +++ b/README.md @@ -242,6 +242,22 @@ This option applies to protocol version 2 only. - *Default*: '0' +sshd_client_alive_count_max +-------------------------- +ClientAliveCountMax in sshd_config. +Sets the number of client alive messages (see below) which may be sent without sshd(8) +receiving any messages back from the client. If this threshold is reached while client alive +messages are being sent, sshd will disconnect the client, terminating the session. It is +important to note that the use of client alive messages is very different from TCPKeepAlive +(below). The client alive messages are sent through the encrypted channel and therefore will +not be spoofable. The TCP keepalive option enabled by TCPKeepAlive is spoofable. The client +alive mechanism is valuable when the client or server depend on knowing when a connection has +become inactive. The default value is 3. If ClientAliveInterval (see below) is set to 15, +and ClientAliveCountMax is left at the default, unresponsive SSH clients will be disconnected +after approximately 45 seconds. This option applies to protocol version 2 only. + +- *Default*: '3' + keys ---- Hash of keys for user's ~/.ssh/authorized_keys diff --git a/manifests/init.pp b/manifests/init.pp index 8a5c3ed1..60ae22c2 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -42,6 +42,7 @@ $sshd_allow_tcp_forwarding = 'yes', $sshd_x11_forwarding = 'yes', $sshd_use_pam = 'USE_DEFAULTS', + $sshd_client_alive_count_max = '3', $sshd_client_alive_interval = '0', $sshd_gssapiauthentication = 'yes', $sshd_gssapikeyexchange = 'USE_DEFAULTS', @@ -323,6 +324,7 @@ validate_re($sshd_use_pam_real, '^(yes|no)$', "ssh::sshd_use_pam may be either 'yes' or 'no' and is set to <${sshd_use_pam_real}>.") } if is_integer($sshd_client_alive_interval) == false { fail("ssh::sshd_client_alive_interval must be an integer and is set to <${sshd_client_alive_interval}>.") } + if is_integer($sshd_client_alive_count_max) == false { fail("ssh::sshd_client_alive_count_max must be an integer and is set to <${sshd_client_alive_count_max}>.") } if $sshd_config_banner != 'none' { validate_absolute_path($sshd_config_banner) diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb index 514658cb..7cd5cd05 100644 --- a/spec/classes/init_spec.rb +++ b/spec/classes/init_spec.rb @@ -70,6 +70,7 @@ 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_file('sshd_config').with_content(/^ClientAliveCountMax 3$/) } it { should contain_file('sshd_config').with_content(/^GSSAPIAuthentication yes$/) } it { should contain_file('sshd_config').with_content(/^GSSAPICleanupCredentials yes$/) } it { should contain_file('sshd_config').with_content(/^HostKey \/etc\/ssh\/ssh_host_rsa_key$/) } @@ -448,6 +449,7 @@ 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_file('sshd_config').with_content(/^ClientAliveCountMax 3$/) } it { should contain_file('sshd_config').with_content(/^GSSAPIAuthentication yes$/) } it { should contain_file('sshd_config').with_content(/^GSSAPICleanupCredentials yes$/) } it { should contain_file('sshd_config').with_content(/^HostKey \/etc\/ssh\/ssh_host_rsa_key$/) } @@ -542,6 +544,7 @@ 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_file('sshd_config').with_content(/^ClientAliveCountMax 3$/) } it { should contain_file('sshd_config').with_content(/^GSSAPIAuthentication yes$/) } it { should contain_file('sshd_config').with_content(/^GSSAPICleanupCredentials yes$/) } it { should contain_file('sshd_config').with_content(/^HostKey \/etc\/ssh\/ssh_host_rsa_key$/) } @@ -636,6 +639,7 @@ 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_file('sshd_config').with_content(/^ClientAliveCountMax 3$/) } it { should contain_file('sshd_config').with_content(/^GSSAPIAuthentication yes$/) } it { should contain_file('sshd_config').with_content(/^GSSAPICleanupCredentials yes$/) } it { should contain_file('sshd_config').with_content(/^HostKey \/etc\/ssh\/ssh_host_rsa_key$/) } @@ -748,6 +752,7 @@ :sshd_x11_forwarding => 'no', :sshd_use_pam => 'no', :sshd_client_alive_interval => '242', + :sshd_client_alive_count_max => '0', } end @@ -779,6 +784,7 @@ 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$/) } + it { should contain_file('sshd_config').with_content(/^ClientAliveCountMax 0$/) } it { should contain_file('sshd_config').with_content(/^GSSAPIAuthentication yes$/) } it { should contain_file('sshd_config').with_content(/^GSSAPICleanupCredentials yes$/) } it { should contain_file('sshd_config').with_content(/^HostKey \/etc\/ssh\/ssh_host_rsa_key$/) } @@ -993,6 +999,25 @@ end end + context 'with sshd_client_alive_count_max 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_count_max => 'invalid' } + end + + it 'should fail' do + expect { + should contain_class('ssh') + }.to raise_error(Puppet::Error,/^ssh::sshd_client_alive_count_max must be an integer and is set to \./) + end + end + context 'with sshd_config_banner set to invalid value on valid osfamily' do let(:params) { { :sshd_config_banner => 'invalid/path' } } let(:facts) do diff --git a/templates/sshd_config.erb b/templates/sshd_config.erb index 68c13958..a5f79e4e 100644 --- a/templates/sshd_config.erb +++ b/templates/sshd_config.erb @@ -130,7 +130,7 @@ PrintMotd <%= @sshd_config_print_motd %> #Compression delayed #ClientAliveInterval 0 ClientAliveInterval <%= @sshd_client_alive_interval %> -#ClientAliveCountMax 3 +ClientAliveCountMax <%= @sshd_client_alive_count_max %> #ShowPatchLevel no <% if @sshd_config_use_dns_real != nil -%> #UseDNS yes