From 0aba84c5db12f97ada02342f5345e017562b6cd4 Mon Sep 17 00:00:00 2001 From: Bo Andersson Date: Thu, 26 May 2016 17:43:10 +0200 Subject: [PATCH] Add template for performing custom sshd pam config Specific use case is to be able to supply options to pam_vas3 that are specifically needed (and should only be configured) for sshd to resolv intermittent ssh login failures with QAS. --- README.md | 28 +++++++++ manifests/init.pp | 24 +++++++ spec/classes/init_spec.rb | 129 ++++++++++++++++++++++++++++++++++++++ templates/sshd.custom.erb | 15 +++++ 4 files changed, 196 insertions(+) create mode 100644 templates/sshd.custom.erb diff --git a/README.md b/README.md index d7736906..66676f79 100644 --- a/README.md +++ b/README.md @@ -188,8 +188,36 @@ pam_d_sshd_template ------------------- Content template of $pam_d_sshd_path. If undef, parameter is set based on the OS version. +For cases where a full customization of the sshd PAM configuration is required, set pam_d_sshd_template to use pam/sshd.custom.erb that is provided with this module. +pam/sshd.custom.erb must be further configured with the parameters pam_sshd_auth_lines, pam_sshd_account_lines, pam_sshd_password_lines and pam_sshd_session_lines. +Note that the pam_d_sshd_template parameter is a no-op on Solaris. + - *Default*: undef, default is set based on OS version +pam_sshd_auth_lines +------------------- +An ordered array of strings that define the content for PAM sshd auth. This setting is required and only valid if pam_d_sshd_template is configured to use the pam/sshd.custom.erb template. + +- *Default*: undef + +pam_sshd_account_lines +---------------------- +An ordered array of strings that define the content for PAM sshd account. This setting is required and only valid if pam_d_sshd_template is configured to use the pam/sshd.custom.erb template. + +- *Default*: undef + +pam_sshd_password_lines +----------------------- +An ordered array of strings that define the content for PAM sshd password. This setting is required and only valid if pam_d_sshd_template is configured to use the pam/sshd.custom.erb template. + +- *Default*: undef + +pam_sshd_session_lines +---------------------- +An ordered array of strings that define the content for PAM sshd session. This setting is required and only valid if pam_d_sshd_template is configured to use the pam/sshd.custom.erb template. + +- *Default*: undef + pam_auth_lines -------------- An ordered array of strings that define the content for PAM auth. If undef, parameter is set based on the OS version. diff --git a/manifests/init.pp b/manifests/init.pp index 4abe947a..794489e4 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -23,6 +23,10 @@ $pam_d_sshd_group = 'root', $pam_d_sshd_mode = '0644', $pam_d_sshd_template = undef, + $pam_sshd_auth_lines = undef, + $pam_sshd_account_lines = undef, + $pam_sshd_password_lines = undef, + $pam_sshd_session_lines = undef, $pam_auth_lines = undef, $pam_account_lines = undef, $pam_password_lines = undef, @@ -1113,6 +1117,26 @@ $my_pam_d_login_template = $pam_d_login_template } + if $pam_d_sshd_template == 'pam/sshd.custom.erb' { + if $pam_sshd_auth_lines == undef or + $pam_sshd_account_lines == undef or + $pam_sshd_password_lines == undef or + $pam_sshd_session_lines == undef { + fail('pam_sshd_[auth|account|password|session]_lines required when using the pam/sshd.custom.erb template') + } + validate_array($pam_sshd_auth_lines) + validate_array($pam_sshd_account_lines) + validate_array($pam_sshd_password_lines) + validate_array($pam_sshd_session_lines) + } else { + if $pam_sshd_auth_lines != undef or + $pam_sshd_account_lines != undef or + $pam_sshd_password_lines != undef or + $pam_sshd_session_lines != undef { + fail('pam_sshd_[auth|account|password|session]_lines are only valid when pam_d_sshd_template is configured with the pam/sshd.custom.erb template') + } + } + if $pam_d_sshd_template == undef { $my_pam_d_sshd_template = $default_pam_d_sshd_template } else { diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb index dd724d9c..8de25617 100644 --- a/spec/classes/init_spec.rb +++ b/spec/classes/init_spec.rb @@ -273,6 +273,44 @@ describe 'config files' do platforms.sort.each do |k,v| + context "when configuring pam_d_sshd_template on #{v[:osfamily]} with #{v[:releasetype]} #{v[:release]}" do + let :facts do + { :osfamily => v[:osfamily], + :"#{v[:releasetype]}" => v[:release], + :lsbdistid => v[:lsbdistid], + } + end + + let (:params) do + { :pam_d_sshd_template => 'pam/sshd.custom.erb', + :pam_sshd_auth_lines => [ 'auth_line1', 'auth_line2' ], + :pam_sshd_account_lines => [ 'account_line1', 'account_line2' ], + :pam_sshd_session_lines => [ 'session_line1', 'session_line2' ], + :pam_sshd_password_lines => [ 'password_line1', 'password_line2' ], + } + end + + sshd_custom_content = <<-END.gsub(/^\s+\|/, '') + |# This file is being maintained by Puppet. + |# DO NOT EDIT + |# + |auth_line1 + |auth_line2 + |account_line1 + |account_line2 + |password_line1 + |password_line2 + |session_line1 + |session_line2 + END + + if v[:osfamily] == 'Solaris' + it { should_not contain_file('pam_d_sshd') } + else + it { should contain_file('pam_d_sshd').with('content' => sshd_custom_content) } + end + end + context "with specifying services param on #{v[:osfamily]} with #{v[:releasetype]} #{v[:release]}" do let :facts do { :osfamily => v[:osfamily], @@ -794,6 +832,97 @@ it { should contain_class('pam') } end end + + [:pam_sshd_auth_lines, :pam_sshd_account_lines, :pam_sshd_password_lines, :pam_sshd_session_lines].each do |param| + context "with pam_d_sshd_template set to pam/sshd.custom.erb when only #{param} is missing" do + let :full_params do + { + :pam_sshd_auth_lines => %w(auth_line1 auth_line2), + :pam_sshd_account_lines => %w(account_line1 account_line2), + :pam_sshd_session_lines => %w(session_line1 session_line2), + :pam_sshd_password_lines => %w(password_line1 password_line2), + :pam_d_sshd_template => 'pam/sshd.custom.erb', + } + end + let :facts do + { + :osfamily => v[:osfamily], + :"#{v[:releasetype]}" => v[:release], + :lsbdistid => v[:lsbdistid], + } + end + let(:params) { + # remove param from full_params hash before applying + full_params.delete(param) + full_params + } + + it 'should fail' do + expect { should contain_class(subject) }.to raise_error(Puppet::Error, %r{pam_sshd_\[auth\|account\|password\|session\]_lines required when using the pam/sshd.custom.erb template}) + end + end + end + + [ :pam_sshd_auth_lines, :pam_sshd_account_lines, :pam_sshd_password_lines, :pam_sshd_session_lines ].each do |param| + context "with #{param} specified and pam_d_sshd_template not specified on #{v[:osfamily]} with #{v[:releasetype]} #{v[:release]}" do + let :facts do + { :osfamily => v[:osfamily], + :"#{v[:releasetype]}" => v[:release], + :lsbdistid => v[:lsbdistid], + } + end + + let(:params) { { param => [ '#' ] } } + + it "should fail" do + expect { + should contain_class('pam') + }.to raise_error(Puppet::Error, %r{pam_sshd_\[auth\|account\|password\|session\]_lines are only valid when pam_d_sshd_template is configured with the pam/sshd.custom.erb template}) + end + end + end end end + + describe 'variable type and content validations' do + # set needed custom facts and variables + let(:facts) do + { + :operatingsystemmajrelease => '7', + :osfamily => 'RedHat', + } + end + let(:mandatory_params) { {} } + + validations = { + 'array for pam_sshd_(auth|account|password|session)_lines' => { + :name => %w(pam_sshd_auth_lines pam_sshd_account_lines pam_sshd_password_lines pam_sshd_session_lines), + :params => { :pam_d_sshd_template => 'pam/sshd.custom.erb', :pam_sshd_auth_lines => ['#'], :pam_sshd_account_lines => ['#'], :pam_sshd_password_lines => ['#'], :pam_sshd_session_lines => ['#']}, + :valid => [%w(array)], + :invalid => ['string', { 'ha' => 'sh' }, 3, 2.42, true, false], + :message => 'is not an Array', + }, + } + + validations.sort.each do |type, var| + var[:name].each do |var_name| + var[:params] = {} if var[:params].nil? + var[:valid].each do |valid| + context "when #{var_name} (#{type}) is set to valid #{valid} (as #{valid.class})" do + let(:params) { [mandatory_params, var[:params], { :"#{var_name}" => valid, }].reduce(:merge) } + it { should compile } + end + end + + var[:invalid].each do |invalid| + context "when #{var_name} (#{type}) is set to invalid #{invalid} (as #{invalid.class})" do + let(:params) { [mandatory_params, var[:params], { :"#{var_name}" => invalid, }].reduce(:merge) } + it 'should fail' do + expect { should contain_class(subject) }.to raise_error(Puppet::Error, /#{var[:message]}/) + end + end + end + end # var[:name].each + end # validations.sort.each + end # describe 'variable type and content validations' end diff --git a/templates/sshd.custom.erb b/templates/sshd.custom.erb new file mode 100644 index 00000000..47f25e80 --- /dev/null +++ b/templates/sshd.custom.erb @@ -0,0 +1,15 @@ +# This file is being maintained by Puppet. +# DO NOT EDIT +# +<% @pam_sshd_auth_lines.each do |auth_line| -%> +<%= auth_line %> +<% end -%> +<% @pam_sshd_account_lines.each do |account_line| -%> +<%= account_line %> +<% end -%> +<% @pam_sshd_password_lines.each do |password_line| -%> +<%= password_line %> +<% end -%> +<% @pam_sshd_session_lines.each do |session_line| -%> +<%= session_line %> +<% end -%>