Skip to content

Commit c53e8bc

Browse files
authored
Merge pull request #835 from dev-sec/ssh_locked
Modify PAM to allow SSH key based logins with locked passwords
2 parents ab93a75 + b7f0bdd commit c53e8bc

File tree

7 files changed

+130
-1
lines changed

7 files changed

+130
-1
lines changed

molecule/os_hardening/verify.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
- verify_tasks/pw_ageing.yml
2121
- verify_tasks/netrc.yml
2222
- verify_tasks/ignore_home_folders.yml
23+
- verify_tasks/ssh_auth_locked.yml
2324

2425
# temp. disabled - https://github.com/dev-sec/ansible-collection-hardening/issues/690
2526
# - name: Include PAM tests
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
---
2+
- name: Install tools
3+
package:
4+
name: "{{ item }}"
5+
state: present
6+
ignore_errors: true
7+
loop:
8+
- sshpass
9+
- openssh
10+
- openssh-clients
11+
- openssh-server
12+
13+
- name: Allow password Login for sshd
14+
ansible.builtin.lineinfile:
15+
path: /etc/ssh/sshd_config
16+
search_string: PasswordAuthentication no
17+
line: PasswordAuthentication yes
18+
when:
19+
- ansible_facts.distribution == "Amazon"
20+
21+
- name: Start sshd service
22+
ansible.builtin.service:
23+
name: "{{ item }}"
24+
state: started
25+
ignore_errors: true
26+
loop:
27+
- sshd
28+
- ssh
29+
30+
- name: Set password for test
31+
ansible.builtin.set_fact:
32+
test_pw: myTest!pw
33+
34+
- name: Create locked_user
35+
user:
36+
name: locked_user
37+
password: "{{ test_pw | password_hash('sha512') }}"
38+
39+
- name: Create ssh-client-keypair
40+
community.crypto.openssh_keypair:
41+
path: /root/locked_user_id
42+
type: ed25519
43+
state: present
44+
register: generated_key
45+
46+
- name: Add ssh-public-key to locked_user
47+
ansible.posix.authorized_key:
48+
user: locked_user
49+
key: "{{ generated_key.public_key }}"
50+
state: present
51+
52+
- name: Check successful login with password
53+
ansible.builtin.shell:
54+
cmd: sshpass -p {{ test_pw }} ssh -o StrictHostKeyChecking=no locked_user@localhost echo "success"
55+
56+
- name: Check successful login with ssh key
57+
ansible.builtin.shell:
58+
cmd: ssh -i /root/locked_user_id -o StrictHostKeyChecking=no locked_user@localhost echo "success"
59+
60+
- name: Set password change date for locked_user
61+
ansible.builtin.shell:
62+
cmd: chage -d 2020-01-01 locked_user
63+
64+
- name: Check unsuccessful login with password
65+
ansible.builtin.shell:
66+
cmd: sshpass -p {{ test_pw }} ssh -o StrictHostKeyChecking=no locked_user@localhost echo "success"
67+
register: output
68+
ignore_errors: true
69+
70+
- name: Assert check unsuccessful login
71+
ansible.builtin.assert:
72+
that:
73+
- output.rc | int == 1
74+
- "'WARNING: Your password has expired.' in output.stderr"
75+
- "'success' not in output.stdout"
76+
when:
77+
- ansible_facts.os_family != "Suse"
78+
79+
- name: Assert check unsuccessful login
80+
ansible.builtin.assert:
81+
that:
82+
- output.rc | int == 5
83+
- output.stderr | length == 0
84+
- output.stdout | length == 0
85+
when:
86+
- ansible_facts.os_family == "Suse"
87+
88+
- name: Check successful login with ssh key
89+
ansible.builtin.shell:
90+
cmd: ssh -i /root/locked_user_id -o StrictHostKeyChecking=no locked_user@localhost echo "success"

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ ansible-core==2.18.1
44
docker==7.1.0
55
jmespath==1.0.1
66
aar-doc==2.0.1
7+
passlib==1.7.4

roles/os_hardening/README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,18 @@ We are setting this sysctl to a default of `32`, some systems only support small
9494
vm.mmap_rnd_bits: 16
9595
```
9696

97+
### password expiry and SSH key based logins
98+
99+
With default PAM configuration setting a password expiry for users will also block SSH key logins after the password has expired.
100+
We have added a flag for PAM to ignore the expiry if SSH keys or other login mechanisms are used.
101+
If you choose to use your own PAM configuration please adjust it accordingly to contain `no_pass_expiry` in the `account` stage for the `pam_unix.so` module.
102+
103+
A valid example would look like this:
104+
105+
```text
106+
account required pam_unix.so no_pass_expiry
107+
```
108+
97109
## Testing with inspec
98110

99111
If you're using inspec to test your machines after applying this role, please make sure to add the connecting user to the `os_ignore_users`-variable.

roles/os_hardening/tasks/pam.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,24 @@
2727
when:
2828
- ansible_facts.os_family == 'RedHat'
2929

30+
- name: Allow Login with SSH Keys, when user password is expired
31+
ansible.builtin.lineinfile:
32+
path: /etc/pam.d/system-auth
33+
backrefs: true
34+
regexp: "^(account.*pam_unix.so(?!.*no_pass_expiry).*)$"
35+
line: '\1 no_pass_expiry'
36+
when:
37+
- ansible_facts.os_family == 'Archlinux'
38+
39+
- name: Allow Login with SSH Keys, when user password is expired
40+
ansible.builtin.lineinfile:
41+
path: /etc/pam.d/common-account
42+
backrefs: true
43+
regexp: "^(account.*pam_unix.so(?!.*no_pass_expiry).*)$"
44+
line: '\1 no_pass_expiry'
45+
when:
46+
- ansible_facts.os_family == 'Suse'
47+
3048
- name: NSA 2.3.3.5 Upgrade Password Hashing Algorithm to SHA-512
3149
ansible.builtin.template:
3250
src: etc/libuser.conf.j2

roles/os_hardening/tasks/pam_debian.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,10 @@
118118
state: absent
119119
when:
120120
- not os_auth_pam_passwdqc_enable
121+
122+
- name: Allow Login with SSH Keys, when user password is expired
123+
ansible.builtin.lineinfile:
124+
path: /etc/pam.d/common-account
125+
backrefs: true
126+
regexp: "^(account.*pam_unix.so(?!.*no_pass_expiry).*)$"
127+
line: '\1 no_pass_expiry'

roles/os_hardening/templates/etc/pam.d/rhel_auth.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ auth required pam_deny.so
2424
{% if os_auth_retries|int > 0 %}
2525
account required pam_faillock.so
2626
{% endif %}
27-
account required pam_unix.so
27+
account required pam_unix.so no_pass_expiry
2828
account sufficient pam_localuser.so
2929
account sufficient pam_succeed_if.so uid < 1000 quiet
3030
{% if (os_auth_pam_sssd_enable | bool) %}

0 commit comments

Comments
 (0)