diff --git a/.travis.yml b/.travis.yml index 00d4941..08e6e8e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,14 @@ rvm: - 1.8.7 - 1.9.3 - 2.0.0 +- 2.1.0 +matrix: + fast_finish: true + exclude: + - rvm: 2.1.0 + env: PUPPET_VERSION=3.3.2 + - rvm: 2.1.0 + env: PUPPET_VERSION=3.4.2 language: ruby before_script: 'gem install --no-ri --no-rdoc bundler' script: 'bundle exec rake validate && bundle exec rake lint && SPEC_OPTS="--format documentation" bundle exec rake spec' diff --git a/README.md b/README.md index 0edb4e9..e79ed7b 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ The module uses exported resources to manage ssh keys and removes ssh keys that # Compatability -This module has been tested to work on the following systems with Puppet v3 and Ruby versions 1.8.7, 1.9.3 and 2.0.0. +This module has been tested to work on the following systems with Puppet v3 and Ruby versions 1.8.7, 1.9.3, 2.0.0 and 2.1.0. * Debian 7 * EL 5 @@ -365,6 +365,18 @@ Array of users for the AllowUsers setting in sshd_config. - *Default*: undef +sshd_config_maxstartups +----------------------- +Specifies the maximum number of concurrent unauthenticated connections to the SSH daemon. + +- *Default*: undef + +sshd_config_maxsessions +----------------------- +Specifies the maximum number of open sessions permitted per network connection. + +- *Default*: undef + keys ---- Hash of keys for user's ~/.ssh/authorized_keys diff --git a/manifests/init.pp b/manifests/init.pp index da08605..92a0e84 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -47,6 +47,8 @@ class ssh ( $sshd_config_denygroups = undef, $sshd_config_allowusers = undef, $sshd_config_allowgroups = undef, + $sshd_config_maxstartups = undef, + $sshd_config_maxsessions = undef, $sshd_banner_content = undef, $sshd_banner_owner = 'root', $sshd_banner_group = 'root', @@ -434,6 +436,18 @@ class ssh ( validate_string($sshd_config_authkey_location) } + if $sshd_config_maxstartups != undef { + validate_re($sshd_config_maxstartups,'^(\d+)+(\d+?:\d+?:\d+)?$', + "ssh::sshd_config_maxstartups may be either an integer or three integers separated with colons, such as 10:30:100. Detected value is <${sshd_config_maxstartups}>.") + } + + if $sshd_config_maxsessions != undef { + $is_int_sshd_config_maxsessions = is_integer($sshd_config_maxsessions) + if $is_int_sshd_config_maxsessions == false { + fail("sshd_config_maxsessions must be an integer. Detected value is ${sshd_config_maxsessions}.") + } + } + if $sshd_config_strictmodes != undef { validate_re($sshd_config_strictmodes, '^(yes|no)$', "ssh::sshd_config_strictmodes may be either 'yes' or 'no' and is set to <${sshd_config_strictmodes}>.") } diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb index eebd65d..0a5e14a 100644 --- a/spec/classes/init_spec.rb +++ b/spec/classes/init_spec.rb @@ -86,6 +86,8 @@ describe 'ssh' do it { should_not contain_file('sshd_config').with_content(/^\s*GSSAPIKeyExchange no$/) } it { should_not contain_file('sshd_config').with_content(/^AuthorizedKeysFile/) } it { should_not contain_file('sshd_config').with_content(/^StrictModes/) } + it { should_not contain_file('sshd_config').with_content(/^MaxStartups/) } + it { should_not contain_file('sshd_config').with_content(/^MaxSessions/) } it { should contain_file('sshd_config').with_content(/^AcceptEnv L.*$/) } it { should contain_file('sshd_config').without_content(/^\s*Ciphers/) } it { should contain_file('sshd_config').without_content(/^\s*MACs/) } @@ -205,6 +207,8 @@ describe 'ssh' do it { should_not contain_file('sshd_config').with_content(/^\s*AcceptEnv L.*$/) } it { should_not contain_file('sshd_config').with_content(/^AuthorizedKeysFile/) } it { should_not contain_file('sshd_config').with_content(/^StrictModes/) } + it { should_not contain_file('sshd_config').with_content(/^MaxStartups/) } + it { should_not contain_file('sshd_config').with_content(/^MaxSessions/) } it { should contain_file('sshd_config').with_content(/^ServerKeyBits 768$/) } it { should contain_file('sshd_config').without_content(/^\s*Ciphers/) } it { should contain_file('sshd_config').without_content(/^\s*MACs/) } @@ -307,6 +311,8 @@ describe 'ssh' do it { should_not contain_file('sshd_config').with_content(/^\s*AcceptEnv L.*$/) } it { should_not contain_file('sshd_config').with_content(/^AuthorizedKeysFile/) } it { should_not contain_file('sshd_config').with_content(/^StrictModes/) } + it { should_not contain_file('sshd_config').with_content(/^MaxStartups/) } + it { should_not contain_file('sshd_config').with_content(/^MaxSessions/) } it { should contain_file('sshd_config').with_content(/^ServerKeyBits 768$/) } it { should contain_file('sshd_config').without_content(/^\s*Ciphers/) } it { should contain_file('sshd_config').without_content(/^\s*MACs/) } @@ -408,6 +414,8 @@ describe 'ssh' do it { should_not contain_file('sshd_config').with_content(/^\s*AcceptEnv L.*$/) } it { should_not contain_file('sshd_config').with_content(/^AuthorizedKeysFile/) } it { should_not contain_file('sshd_config').with_content(/^StrictModes/) } + it { should_not contain_file('sshd_config').with_content(/^MaxStartups/) } + it { should_not contain_file('sshd_config').with_content(/^MaxSessions/) } it { should contain_file('sshd_config').with_content(/^ServerKeyBits 768$/) } it { should contain_file('sshd_config').without_content(/^\s*Ciphers/) } it { should contain_file('sshd_config').without_content(/^\s*MACs/) } @@ -517,6 +525,8 @@ describe 'ssh' do it { should contain_file('sshd_config').with_content(/^AcceptEnv L.*$/) } it { should_not contain_file('sshd_config').with_content(/^AuthorizedKeysFile/) } it { should_not contain_file('sshd_config').with_content(/^StrictModes/) } + it { should_not contain_file('sshd_config').with_content(/^MaxStartups/) } + it { should_not contain_file('sshd_config').with_content(/^MaxSessions/) } it { should contain_file('ssh_config').without_content(/^\s*Ciphers/) } it { should contain_file('ssh_config').without_content(/^\s*MACs/) } it { should contain_file('ssh_config').without_content(/^\s*DenyUsers/) } @@ -625,6 +635,8 @@ describe 'ssh' do it { should contain_file('sshd_config').with_content(/^AcceptEnv L.*$/) } it { should_not contain_file('sshd_config').with_content(/^AuthorizedKeysFile/) } it { should_not contain_file('sshd_config').with_content(/^StrictModes/) } + it { should_not contain_file('sshd_config').with_content(/^MaxStartups/) } + it { should_not contain_file('sshd_config').with_content(/^MaxSessions/) } it { should contain_file('sshd_config').without_content(/^\s*Ciphers/) } it { should contain_file('sshd_config').without_content(/^\s*MACs/) } it { should contain_file('sshd_config').without_content(/^\s*DenyUsers/) } @@ -733,6 +745,8 @@ describe 'ssh' do it { should contain_file('sshd_config').with_content(/^AcceptEnv L.*$/) } it { should_not contain_file('sshd_config').with_content(/^AuthorizedKeysFile/) } it { should_not contain_file('sshd_config').with_content(/^StrictModes/) } + it { should_not contain_file('sshd_config').with_content(/^MaxStartups/) } + it { should_not contain_file('sshd_config').with_content(/^MaxSessions/) } it { should contain_file('sshd_config').without_content(/^\s*Ciphers/) } it { should contain_file('sshd_config').without_content(/^\s*MACs/) } it { should contain_file('sshd_config').without_content(/^\s*DenyUsers/) } @@ -935,6 +949,8 @@ describe 'ssh' do it { should contain_file('sshd_config').with_content(/^HostKey \/etc\/ssh\/ssh_host_rsa_key/) } it { should contain_file('sshd_config').with_content(/^HostKey \/etc\/ssh\/ssh_host_dsa_key/) } it { should contain_file('sshd_config').with_content(/^StrictModes yes$/) } + it { should_not contain_file('sshd_config').with_content(/^MaxStartups/) } + it { should_not contain_file('sshd_config').with_content(/^MaxSessions/) } it { should contain_file('sshd_config').with_content(/^\s*Ciphers aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,arcfour,aes192-cbc,aes256-cbc$/) } it { should contain_file('sshd_config').with_content(/^\s*MACs hmac-md5-etm@openssh.com,hmac-sha1-etm@openssh.com$/) } it { should contain_file('sshd_config').with_content(/^\s*DenyUsers root lusers$/) } @@ -2219,6 +2235,79 @@ describe 'ssh' do end end + describe 'with parameter sshd_config_maxstartups specified' do + ['10','10:30:100'].each do |value| + context "as a valid string - #{value}" do + let(:params) { { :sshd_config_maxstartups => value } } + let(:facts) do + { :fqdn => 'monkey.example.com', + :osfamily => 'RedHat', + :sshrsakey => 'AAAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ==' + } + end + + it { should contain_file('sshd_config').with_content(/^MaxStartups #{value}$/) } + end + end + + ['10a',true,'10:30:1a'].each do |value| + context "as an invalid string - #{value}" do + let(:params) { { :sshd_config_maxstartups => value } } + let(:facts) do + { :fqdn => 'monkey.example.com', + :osfamily => 'RedHat', + :sshrsakey => 'AAAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ==' + } + end + + it 'should fail' do + expect { + should contain_class('ssh') + }.to raise_error(Puppet::Error,/^ssh::sshd_config_maxstartups may be either an integer or three integers separated with colons, such as 10:30:100. Detected value is <#{value}>./) + end + end + end + + context 'as an invalid type' do + let(:params) { { :sshd_config_maxstartups => true } } + let(:facts) do + { :fqdn => 'monkey.example.com', + :osfamily => 'RedHat', + :sshrsakey => 'AAAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ==' + } + end + it 'should fail' do + expect { should contain_class('ssh') }.to raise_error(Puppet::Error) + end + end + end + + describe 'with parameter sshd_config_maxsessions specified' do + context 'as a valid integer' do + let(:params) { { :sshd_config_maxsessions => 10 } } + let(:facts) do + { :fqdn => 'monkey.example.com', + :osfamily => 'RedHat', + :sshrsakey => 'AAAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ==' + } + end + it { should contain_file('sshd_config').with_content(/^MaxSessions 10$/) } + end + + context 'as an invalid type' do + let(:params) { { :sshd_config_maxsessions => 'BOGUS' } } + let(:facts) do + { :fqdn => 'monkey.example.com', + :osfamily => 'RedHat', + :sshrsakey => 'AAAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ==' + } + end + it 'should fail' do + expect { should contain_class('ssh') }.to raise_error(Puppet::Error) + end + end + end + describe 'with parameter sshd_acceptenv specified' do ['true',true].each do |value| context "as #{value}" do diff --git a/templates/sshd_config.erb b/templates/sshd_config.erb index 7e04039..43f0231 100644 --- a/templates/sshd_config.erb +++ b/templates/sshd_config.erb @@ -154,7 +154,17 @@ ClientAliveCountMax <%= @sshd_client_alive_count_max %> UseDNS <%= @sshd_config_use_dns_real %> <% end -%> #PidFile /var/run/sshd.pid -#MaxStartups 10 +<% if @sshd_config_maxstartups -%> +MaxStartups <%= sshd_config_maxstartups %> +<% else -%> +#MaxStartups 10:30:100 +<% end -%> +<% if @sshd_config_maxsessions -%> +MaxSessions <%= sshd_config_maxsessions %> +<% else -%> +#MaxSessions 10 +<% end -%> + #PermitTunnel no #ChrootDirectory none