Showing with 235 additions and 40 deletions.
  1. +32 −18 .travis.yml
  2. +13 −2 Gemfile
  3. +0 −12 Modulefile
  4. +29 −0 README.md
  5. +15 −0 manifests/init.pp
  6. +5 −8 metadata.json
  7. +125 −0 spec/classes/init_spec.rb
  8. +16 −0 templates/sshd_config.erb
50 changes: 32 additions & 18 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,25 +1,39 @@
---
env:
- PUPPET_VERSION=3.3.2
- PUPPET_VERSION=3.4.2
- PUPPET_VERSION=3.5.1
- PUPPET_VERSION=3.6.0
- PUPPET_VERSION=3.7.0
notifications:
email: false
language: ruby

rvm:
- 1.8.7
- 1.9.3
- 2.0.0
- 2.1.0
- 1.8.7
- 1.9.3
- 2.0.0
- 2.1.0

env:
matrix:
- PUPPET_GEM_VERSION="~> 3.1.0"
- PUPPET_GEM_VERSION="~> 3.2.0"
- PUPPET_GEM_VERSION="~> 3.3.0"
- PUPPET_GEM_VERSION="~> 3.4.0"
- PUPPET_GEM_VERSION="~> 3.5.1"
- PUPPET_GEM_VERSION="~> 3.6.0"
- PUPPET_GEM_VERSION="~> 3.7.0"

sudo: false

script: 'bundle exec metadata-json-lint metadata.json && bundle exec rake validate && bundle exec rake lint && SPEC_OPTS="--format documentation" bundle exec rake spec'

matrix:
fast_finish: true
exclude:
- rvm: 2.0.0
env: PUPPET_GEM_VERSION="~> 3.1.0"
- rvm: 2.1.0
env: PUPPET_VERSION=3.3.2
env: PUPPET_GEM_VERSION="~> 3.1.0"
- 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'
gemfile: Gemfile
env: PUPPET_GEM_VERSION="~> 3.2.0"
- rvm: 2.1.0
env: PUPPET_GEM_VERSION="~> 3.3.0"
- rvm: 2.1.0
env: PUPPET_GEM_VERSION="~> 3.4.0"

notifications:
email: false
15 changes: 13 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
source 'https://rubygems.org'

puppetversion = ENV.key?('PUPPET_VERSION') ? "= #{ENV['PUPPET_VERSION']}" : ['>= 3.3']
gem 'puppet', puppetversion
if puppetversion = ENV['PUPPET_GEM_VERSION']
gem 'puppet', puppetversion, :require => false
else
gem 'puppet', :require => false
end

gem 'metadata-json-lint'
gem 'puppetlabs_spec_helper', '>= 0.1.0'
gem 'puppet-lint', '>= 1.0.0'
gem 'facter', '>= 1.7.0'
gem 'rspec-puppet', '~>1.0'

# rspec must be v2 for ruby 1.8.7
if RUBY_VERSION >= '1.8.7' and RUBY_VERSION < '1.9'
gem 'rspec', '~> 2.0'
end
12 changes: 0 additions & 12 deletions Modulefile

This file was deleted.

29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,35 @@ Specifies the maximum number of open sessions permitted per network connection.

- *Default*: undef

sshd_config_chrootdirectory
---------------------------
String with absolute path for the ChrootDirectory directive for the SSH daemon.

- *Default*: undef

sshd_config_forcecommand
---------------------------
String with command for the ForceCommand directive for the SSH daemon.

- *Default*: undef

sshd_config_match
-----------------
Hash for matches with nested arrays for options for the Match directive for the SSH daemon.
Match directive is supported on SSH >= 5.x.

- *Default*: undef

- *Hiera example*:
<pre>
ssh::sshd_config_match:
'User JohnDoe':
- 'AllowTcpForwarding yes'
'Address 2.4.2.0':
- 'X11Forwarding yes'
- 'PasswordAuthentication no'
</pre>

keys
----
Hash of keys for user's ~/.ssh/authorized_keys
Expand Down
15 changes: 15 additions & 0 deletions manifests/init.pp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
$sshd_config_allowgroups = undef,
$sshd_config_maxstartups = undef,
$sshd_config_maxsessions = undef,
$sshd_config_chrootdirectory = undef,
$sshd_config_forcecommand = undef,
$sshd_config_match = undef,
$sshd_banner_content = undef,
$sshd_banner_owner = 'root',
$sshd_banner_group = 'root',
Expand Down Expand Up @@ -452,6 +455,18 @@
}
}

if $sshd_config_chrootdirectory != undef {
validate_absolute_path($sshd_config_chrootdirectory)
}

if $sshd_config_forcecommand != undef {
validate_string($sshd_config_forcecommand)
}

if $sshd_config_match != undef {
validate_hash($sshd_config_match)
}

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}>.")
}
Expand Down
13 changes: 5 additions & 8 deletions metadata.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "ghoneycutt-ssh",
"version": "3.24.0",
"version": "3.25.0",
"author": "ghoneycutt",
"summary": "Manages SSH",
"license": "Apache License, Version 2.0",
"license": "Apache-2.0",
"source": "git://github.com/ghoneycutt/puppet-module-ssh.git",
"project_page": "https://github.com/ghoneycutt/puppet-module-ssh",
"issues_url": "https://github.com/ghoneycutt/puppet-module-ssh/issues",
Expand Down Expand Up @@ -78,12 +78,9 @@
}
],
"description": "Manage SSH",
"types": [

],
"dependencies": [
{"name":"puppetlabs/stdlib","version_requirement":">= 3.2.0"},
{"name":"ghoneycutt/common","version_requirement":">= 1.0.2"},
{"name":"puppetlabs/firewall","version_requirement":">= 0.2.1"}
{"name":"puppetlabs/stdlib","version_requirement":">= 3.2.0 < 5.0.0"},
{"name":"ghoneycutt/common","version_requirement":">= 1.0.2 < 2.0.0"},
{"name":"puppetlabs/firewall","version_requirement":">= 0.2.1 < 2.0.0"}
]
}
125 changes: 125 additions & 0 deletions spec/classes/init_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@
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(/^#ChrootDirectory none/) }
it { should contain_file('sshd_config').without_content(/^ForceCommand/) }
it { should contain_file('sshd_config').without_content(/^Match/) }
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/) }
Expand Down Expand Up @@ -230,6 +233,9 @@
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(/^#ChrootDirectory none/) }
it { should contain_file('sshd_config').without_content(/^ForceCommand/) }
it { should contain_file('sshd_config').without_content(/^Match/) }
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/) }
Expand Down Expand Up @@ -344,6 +350,9 @@
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(/^#ChrootDirectory none/) }
it { should contain_file('sshd_config').without_content(/^ForceCommand/) }
it { should contain_file('sshd_config').without_content(/^Match/) }
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/) }
Expand Down Expand Up @@ -457,6 +466,9 @@
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(/^#ChrootDirectory none/) }
it { should contain_file('sshd_config').without_content(/^ForceCommand/) }
it { should contain_file('sshd_config').without_content(/^Match/) }
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/) }
Expand Down Expand Up @@ -578,6 +590,9 @@
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(/^#ChrootDirectory none/) }
it { should contain_file('sshd_config').without_content(/^ForceCommand/) }
it { should contain_file('sshd_config').without_content(/^Match/) }
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/) }
Expand Down Expand Up @@ -698,6 +713,9 @@
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(/^#ChrootDirectory none/) }
it { should contain_file('sshd_config').without_content(/^ForceCommand/) }
it { should contain_file('sshd_config').without_content(/^Match/) }
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/) }
Expand Down Expand Up @@ -818,6 +836,9 @@
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(/^#ChrootDirectory none/) }
it { should contain_file('sshd_config').without_content(/^ForceCommand/) }
it { should contain_file('sshd_config').without_content(/^Match/) }
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/) }
Expand Down Expand Up @@ -934,6 +955,9 @@
:sshd_config_syslog_facility => 'DAEMON',
:sshd_config_login_grace_time => '60',
:permit_root_login => 'no',
:sshd_config_chrootdirectory => '/chrootdir',
:sshd_config_forcecommand => '/force/command --with-parameter 242',
:sshd_config_match => { 'User JohnDoe' => [ 'AllowTcpForwarding yes', ], },
:sshd_config_challenge_resp_auth => 'no',
:sshd_config_print_motd => 'no',
:sshd_config_use_dns => 'no',
Expand Down Expand Up @@ -1024,6 +1048,9 @@
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(/^ChrootDirectory \/chrootdir$/) }
it { should contain_file('sshd_config').with_content(/^ForceCommand \/force\/command --with-parameter 242$/) }
it { should contain_file('sshd_config').with_content(/^Match User JohnDoe\n AllowTcpForwarding yes\Z/) }
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$/) }
Expand All @@ -1045,6 +1072,104 @@
}
end

describe 'sshd_config_chrootdirectory param' do
let :facts do
{
:fqdn => 'monkey.example.com',
:osfamily => 'RedHat',
:root_home => '/root',
:sshrsakey => 'AAAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ=='
}
end
['/chrootdir/subdir','/baby/one/more/test',].each do |value|
context "set to valid #{value} (as #{value.class})" do
let (:params) {{'sshd_config_chrootdirectory' => value }}

it { should contain_file('sshd_config').with_content(/^ChrootDirectory #{value}$/) }
end
end

[true,'invalid','invalid/path/',3,2.42,['array'],a = { 'ha' => 'sh' }].each do |value|
context "set to invalid #{value} (as #{value.class})" do
let (:params) {{'sshd_config_chrootdirectory' => value }}

it 'should fail' do
expect {
should
}.to raise_error(Puppet::Error, /is not an absolute path/)
end
end
end

end

describe 'sshd_config_forcecommand param' do
let :facts do
{
:fqdn => 'monkey.example.com',
:osfamily => 'RedHat',
:root_home => '/root',
:sshrsakey => 'AAAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ=='
}
end
['/bin/command','/bin/command -parameters','/bin/command --parameters','/bin/command /parameters'].each do |value|
context "set to valid #{value} (as #{value.class})" do
let (:params) {{'sshd_config_forcecommand' => value }}

it { should contain_file('sshd_config').with_content(/^ForceCommand #{value}$/) }
end
end

[true,['array'],a = { 'ha' => 'sh' }].each do |value|
context "set to invalid #{value} (as #{value.class})" do
let (:params) {{'sshd_config_forcecommand' => value }}

it 'should fail' do
expect {
should
}.to raise_error(Puppet::Error, /is not a string/)
end
end
end

end

describe 'sshd_config_match param' do
# match and rules get alphabetically sorted by template, matches should be the last options in sshd_config (regex verify with= \Z)
let :facts do
{
:fqdn => 'monkey.example.com',
:osfamily => 'RedHat',
:root_home => '/root',
:sshrsakey => 'AAAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ=='
}
end

context 'set to valid hash containing nested arrays' do
let(:params) do
{ :sshd_config_match => {
'User JohnDoe' => [ 'AllowTcpForwarding yes', ],
'Addresss 2.4.2.0' => [ 'X11Forwarding yes', 'PasswordAuthentication no', ],
},
}
end

it { should contain_file('sshd_config').with_content(/^Match Addresss 2.4.2.0\n PasswordAuthentication no\n X11Forwarding yes\nMatch User JohnDoe\n AllowTcpForwarding yes\Z/) }
end

[true,'string',3,2.42,['array']].each do |value|
context "set to invalid #{value} (as #{value.class})" do
let (:params) {{'sshd_config_match' => value }}
it 'should fail' do
expect {
should
}.to raise_error(Puppet::Error, /is not a Hash/)
end
end
end

end

describe 'sshd_listen_address param' do
context 'when set to an array' do
let :facts do
Expand Down
Loading