Skip to content

Commit 4e38ef0

Browse files
authored
Merge pull request #157 from artem-sidorenko/pam
Switch UsePAM default to yes
2 parents 5878040 + 9f79f0f commit 4e38ef0

File tree

11 files changed

+216
-11
lines changed

11 files changed

+216
-11
lines changed

.kitchen.vagrant.yml

+23-2
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ platforms:
1111
- name: ubuntu-14.04
1212
- name: ubuntu-16.04
1313
- name: centos-6.8
14-
- name: centos-7.2
14+
- name: centos-7.3
1515
- name: debian-7.11
1616
- name: debian-8.6
17-
- name: fedora-23
1817
- name: fedora-24
18+
- name: fedora-25
1919
- name: opensuse-leap-42.1
2020
- name: opensuse-13.2
2121

@@ -29,3 +29,24 @@ suites:
2929
verifier:
3030
inspec_tests:
3131
- https://github.com/dev-sec/tests-ssh-hardening
32+
- name: rhel-with-disabled-pam
33+
includes:
34+
- centos-6.8
35+
- centos-7.3
36+
- fedora-24
37+
- fedora-25
38+
driver:
39+
provision: true
40+
vagrantfiles:
41+
- test/fixtures/vagrantfiles/enforce_selinux.rb
42+
run_list:
43+
- recipe[ssh-hardening]
44+
attributes:
45+
ssh-hardening:
46+
ssh:
47+
server:
48+
use_pam: false
49+
verifier:
50+
inspec_tests:
51+
- https://github.com/dev-sec/tests-ssh-hardening
52+
- test/integration/without-pam

.kitchen.yml

+4-5
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,15 @@ platforms:
6262
- RUN /usr/bin/apt-get update
6363
- RUN /usr/bin/apt-get install -y procps
6464
pid_one_command: /bin/systemd
65-
- name: fedora-23
65+
- name: fedora-24
6666
driver:
67-
image: fedora:23
67+
image: fedora:24
6868
pid_one_command: /usr/lib/systemd/systemd
69-
# Chef needs yum package for now: https://github.com/chef/chef/issues/3201
7069
intermediate_instructions:
7170
- RUN dnf install -y yum
72-
- name: fedora-24
71+
- name: fedora-25
7372
driver:
74-
image: fedora:24
73+
image: fedora:25
7574
pid_one_command: /usr/lib/systemd/systemd
7675
intermediate_instructions:
7776
- RUN dnf install -y yum

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ This cookbook provides secure ssh-client and ssh-server configurations. This coo
2121
- RHEL 6, 7
2222
- CentOS 6, 7
2323
- Oracle Linux 6, 7
24-
- Fedora 23, 24
24+
- Fedora 24, 25
2525
- OpenSuse Leap 42.1
2626
- OpenSuse 13.2
2727

@@ -51,7 +51,7 @@ override['ssh-hardening']['ssh']['server']['listen_to'] = node['ipaddress']
5151
* `['ssh-hardening']['ssh']['server']['allow_tcp_forwarding']` - `false`. Set to `true` to allow TCP Forwarding
5252
* `['ssh-hardening']['ssh']['server']['allow_agent_forwarding']` - `false`. Set to `true` to allow Agent Forwarding
5353
* `['ssh-hardening']['ssh']['server']['allow_x11_forwarding']` - `false`. Set to `true` to allow X11 Forwarding
54-
* `['ssh-hardening']['ssh']['server']['use_pam']` - `false`. Set to `true` to enable the pam authentication of sshd
54+
* `['ssh-hardening']['ssh']['server']['use_pam']` - `true`. Set to `false` to disable the pam authentication of sshd
5555
* `['ssh-hardening']['ssh']['server']['challenge_response_authentication']` - `false`. Set to `true` to enable challenge response authentication.
5656
* `['ssh-hardening']['ssh']['server']['deny_users']` - `[]` to configure `DenyUsers`, if specified login is disallowed for user names that match one of the patterns.
5757
* `['ssh-hardening']['ssh']['server']['allow_users']` - `[]` to configure `AllowUsers`, if specified, login is allowed only for user names that match one of the patterns.

attributes/default.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
default['ssh-hardening']['ssh']['server']['allow_tcp_forwarding'] = false
7878
default['ssh-hardening']['ssh']['server']['allow_agent_forwarding'] = false
7979
default['ssh-hardening']['ssh']['server']['allow_x11_forwarding'] = false
80-
default['ssh-hardening']['ssh']['server']['use_pam'] = false
80+
default['ssh-hardening']['ssh']['server']['use_pam'] = true
8181
default['ssh-hardening']['ssh']['server']['challenge_response_authentication'] = false
8282
default['ssh-hardening']['ssh']['server']['deny_users'] = []
8383
default['ssh-hardening']['ssh']['server']['allow_users'] = []

files/default/ssh_password.te

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module ssh_password 1.0;
2+
3+
require {
4+
type sshd_t;
5+
type shadow_t;
6+
class file { read open };
7+
}
8+
9+
#============= sshd_t ==============
10+
allow sshd_t shadow_t:file { read open };

recipes/server.rb

+38
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,44 @@
3535
package_name node['ssh-hardening']['sshserver']['package']
3636
end
3737

38+
# Handle addional SELinux policy on RHEL/Fedora for different UsePAM options
39+
if %w(fedora rhel).include?(node['platform_family'])
40+
policy_dir = ::File.join(Chef::Config[:file_cache_path], cookbook_name.to_s)
41+
policy_file = ::File.join(policy_dir, 'ssh_password.te')
42+
module_file = ::File.join(policy_dir, 'ssh_password.mod')
43+
package_file = ::File.join(policy_dir, 'ssh_password.pp')
44+
45+
package 'policycoreutils-python'
46+
# on fedora we need an addtional package for semodule_package
47+
package 'policycoreutils-python-utils' if node['platform_family'] == 'fedora'
48+
49+
if node['ssh-hardening']['ssh']['server']['use_pam']
50+
# UsePAM yes: disable and remove the additional SELinux policy
51+
52+
execute 'remove selinux policy' do
53+
command 'semodule -r ssh_password'
54+
only_if 'getenforce | grep -vq Disabled && semodule -l | grep -q ssh_password'
55+
end
56+
else
57+
# UsePAM no: enable and install the additional SELinux policy
58+
59+
directory policy_dir
60+
61+
cookbook_file policy_file do
62+
source 'ssh_password.te'
63+
end
64+
65+
bash 'build selinux package and install it' do
66+
code <<-EOC
67+
checkmodule -M -m -o #{module_file} #{policy_file}
68+
semodule_package -o #{package_file} -m #{module_file}
69+
semodule -i #{package_file}
70+
EOC
71+
not_if 'getenforce | grep -q Disabled || semodule -l | grep -q ssh_password'
72+
end
73+
end
74+
end
75+
3876
# defines the sshd service
3977
service 'sshd' do
4078
# use upstart for ubuntu, otherwise chef uses init

spec/recipes/server_spec.rb

+120
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,122 @@
211211
end
212212
end
213213

214+
it 'should set UsePAM to yes per default' do
215+
expect(chef_run).to render_file('/etc/ssh/sshd_config').with_content('UsePAM yes')
216+
end
217+
218+
describe 'UsePAM option' do
219+
let(:use_pam) { true }
220+
221+
let(:chef_run) do
222+
ChefSpec::ServerRunner.new(platform: platform, version: version) do |node|
223+
node.normal['ssh-hardening']['ssh']['server']['use_pam'] = use_pam
224+
end.converge(described_recipe)
225+
end
226+
227+
context 'when running on Ubuntu' do
228+
let(:platform) { 'ubuntu' }
229+
let(:version) { '16.04' }
230+
231+
it 'does not invoke any SELinux resources' do
232+
expect(chef_run).not_to create_directory('/tmp/ssh-hardening-file-cache/ssh-hardening')
233+
expect(chef_run).not_to render_file('/tmp/ssh-hardening-file-cache/ssh-hardening/ssh_password.te')
234+
expect(chef_run).not_to run_execute('remove selinux policy')
235+
expect(chef_run).not_to run_bash('build selinux package and install it')
236+
expect(chef_run).not_to install_package('policycoreutils-python')
237+
end
238+
239+
context 'when use_pam is set to true' do
240+
let(:use_pam) { true }
241+
242+
it 'should set UsePAM to yes' do
243+
expect(chef_run).to render_file('/etc/ssh/sshd_config').with_content('UsePAM yes')
244+
end
245+
end
246+
247+
context 'when use_pam is set to false' do
248+
let(:use_pam) { false }
249+
250+
it 'should set UsePAM to no' do
251+
expect(chef_run).to render_file('/etc/ssh/sshd_config').with_content('UsePAM no')
252+
end
253+
end
254+
end
255+
256+
context 'when running on CentOS' do
257+
let(:platform) { 'centos' }
258+
let(:version) { '7.2.1511' }
259+
260+
let(:selinux_disabled_or_policy_removed) { false }
261+
let(:selinux_enabled_and_policy_installed) { false }
262+
263+
before do
264+
stub_command('getenforce | grep -vq Disabled && semodule -l | grep -q ssh_password').and_return(selinux_enabled_and_policy_installed)
265+
stub_command('getenforce | grep -q Disabled || semodule -l | grep -q ssh_password').and_return(selinux_disabled_or_policy_removed)
266+
end
267+
268+
it 'should install selinux tools' do
269+
expect(chef_run).to install_package('policycoreutils-python')
270+
end
271+
272+
context 'when use_pam is set to true' do
273+
let(:use_pam) { true }
274+
275+
it 'should set UsePAM to yes' do
276+
expect(chef_run).to render_file('/etc/ssh/sshd_config').with_content('UsePAM yes')
277+
end
278+
279+
context 'when selinux is disabled or policy is removed' do
280+
let(:selinux_enabled_and_policy_installed) { false }
281+
282+
it 'should not invoke the policy removal' do
283+
expect(chef_run).not_to run_execute('remove selinux policy')
284+
end
285+
end
286+
287+
context 'when selinux is enabled and policy is present' do
288+
let(:selinux_enabled_and_policy_installed) { true }
289+
290+
it 'should invoke the policy removal' do
291+
expect(chef_run).to run_execute('remove selinux policy')
292+
end
293+
end
294+
end
295+
296+
context 'when use_pam is set to false' do
297+
let(:use_pam) { false }
298+
299+
it 'should set UsePAM to no' do
300+
expect(chef_run).to render_file('/etc/ssh/sshd_config').with_content('UsePAM no')
301+
end
302+
303+
it 'should create cache directory for policy files' do
304+
expect(chef_run).to create_directory('/tmp/ssh-hardening-file-cache/ssh-hardening')
305+
end
306+
307+
it 'should create selinux source policy file' do
308+
expect(chef_run).to render_file('/tmp/ssh-hardening-file-cache/ssh-hardening/ssh_password.te')
309+
end
310+
311+
context 'when selinux is disabled or policy is installed' do
312+
let(:selinux_disabled_or_policy_removed) { true }
313+
314+
it 'should not install the policy' do
315+
expect(chef_run).not_to run_bash('build selinux package and install it')
316+
end
317+
end
318+
319+
context 'when selinux is enabled and policy is not installed' do
320+
let(:selinux_disabled_or_policy_removed) { false }
321+
322+
it 'should install the policy' do
323+
expect(chef_run).to run_bash('build selinux package and install it')
324+
end
325+
end
326+
end
327+
end
328+
end
329+
214330
describe 'debian banner' do
215331
cached(:chef_run) do
216332
ChefSpec::ServerRunner.new(platform: 'ubuntu', version: '16.04').converge(described_recipe)
@@ -235,6 +351,10 @@
235351
end
236352

237353
context 'with centos as platform' do
354+
before do
355+
stub_command('getenforce | grep -vq Disabled && semodule -l | grep -q ssh_password').and_return(true)
356+
end
357+
238358
cached(:chef_run) do
239359
ChefSpec::ServerRunner.new(platform: 'centos', version: '7.2.1511') do |node|
240360
node.normal['ssh-hardening']['ssh']['server']['os_banner'] = true

spec/spec_helper.rb

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
# OS and version for mocking of ohai data, needed by chefspec
2929
config.platform = 'ubuntu'
3030
config.version = '16.04'
31+
config.file_cache_path = '/tmp/ssh-hardening-file-cache'
3132
end
3233

3334
require_relative '../libraries/devsec_ssh'

templates/default/opensshd.conf.erb

-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@ KerberosTicketCleanup yes
119119
GSSAPIAuthentication no
120120
GSSAPICleanupCredentials yes
121121

122-
# In case you don't use PAM (`UsePAM no`), you can alternatively restrict users and groups here. For key-based authentication this is not necessary, since all keys must be explicitely enabled.
123122
<% unless @node['ssh-hardening']['ssh']['server']['deny_users'].empty? %>
124123
DenyUsers <%= @node['ssh-hardening']['ssh']['server']['deny_users'].join(' ') %>
125124
<% else %>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Vagrant.configure(2) do |config|
2+
config.vm.provision 'shell', inline: <<-SHELL
3+
yum -y update selinux-* # there are sometimes issues because of old selinux policy in the box
4+
setenforce 1
5+
SHELL
6+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
describe command('getenforce') do
2+
its(:exit_status) { should eq 0 }
3+
its(:stdout) { should include 'Enforcing' }
4+
its(:stderr) { should eq '' }
5+
end
6+
7+
describe command('semodule -l | grep "ssh_password"') do
8+
its(:exit_status) { should eq 0 }
9+
its(:stdout) { should include 'ssh_password' }
10+
its(:stderr) { should eq '' }
11+
end

0 commit comments

Comments
 (0)