Initial work on PAM config. Has issues, deploy with care
This commit is contained in:
commit
bd86a9e1f9
3
ansible.cfg
Normal file
3
ansible.cfg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[defaults]
|
||||||
|
inventory = inventory
|
||||||
|
host_key_checking = False
|
||||||
8
group_vars/all.yml
Normal file
8
group_vars/all.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
# Default user source. Overridden in other groups.
|
||||||
|
user_source: "local"
|
||||||
|
|
||||||
|
kanidm_uri: "https://idm.ezri.dev"
|
||||||
|
|
||||||
|
kanidm_supplemental: []
|
||||||
5
group_vars/desktop.yml
Normal file
5
group_vars/desktop.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
user_source: "kanidm-native"
|
||||||
|
|
||||||
|
allowed_groups: "{{ [ 'desktop_users' ] }}"
|
||||||
15
inventory
Normal file
15
inventory
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
; -*-conf-windows-*-
|
||||||
|
|
||||||
|
[container_runners]
|
||||||
|
horizon.servers.ezri.dev
|
||||||
|
|
||||||
|
[kanidm_native]
|
||||||
|
normandy.network.ezri.dev
|
||||||
|
tycho.vpn.ezri.dev
|
||||||
|
rocinante.vpn.ezri.dev
|
||||||
|
serenity.wlan.ezri.dev
|
||||||
|
|
||||||
|
[desktop]
|
||||||
|
;normandy ansible_connection=ssh ansible_become=true
|
||||||
|
serenity ansible_connection=ssh ansible_become=true
|
||||||
|
|
||||||
8
playbooks/deploy-desktop.yml
Normal file
8
playbooks/deploy-desktop.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: Configure desktop systems
|
||||||
|
hosts: desktop
|
||||||
|
roles:
|
||||||
|
- aur
|
||||||
|
- kanidm_native
|
||||||
|
- 2fa
|
||||||
6
playbooks/deploy-kanidm-native.yml
Normal file
6
playbooks/deploy-kanidm-native.yml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: Kanidm native unixd clients
|
||||||
|
hosts: kanidm_native
|
||||||
|
roles:
|
||||||
|
- kanidm_native
|
||||||
8
playbooks/roles/2fa/files/common-account
Normal file
8
playbooks/roles/2fa/files/common-account
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#%PAM-1.0 -*- mode: conf-space; tab-width: 10 -*-
|
||||||
|
|
||||||
|
-account [success=2 default=ignore] pam_systemd_home.so
|
||||||
|
account [success=1 default=ignore] pam_unix.so
|
||||||
|
# If any of the above account lines fail, they'll jump here, which kills the authorization attempt.
|
||||||
|
account [default=die] pam_deny.so
|
||||||
|
account optional pam_permit.so
|
||||||
|
account required pam_time.so
|
||||||
7
playbooks/roles/2fa/files/common-auth
Normal file
7
playbooks/roles/2fa/files/common-auth
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#%PAM-1.0 -*- mode: conf-space; tab-width: 8 -*-
|
||||||
|
|
||||||
|
auth include first-factor
|
||||||
|
auth include second-factor
|
||||||
|
auth optional pam_permit.so
|
||||||
|
auth required pam_env.so
|
||||||
|
auth required pam_faillock.so authsucc
|
||||||
6
playbooks/roles/2fa/files/common-password
Normal file
6
playbooks/roles/2fa/files/common-password
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#%PAM-1.0 -*- mode: conf-space; tab-width: 10 -*-
|
||||||
|
|
||||||
|
-password [success=2 default=ignore] pam_systemd_home.so
|
||||||
|
password [success=1 default=ignore] pam_unix.so try_first_pass nullok shadow sha512
|
||||||
|
password [default=die] pam_deny.so
|
||||||
|
password optional pam_permit.so
|
||||||
6
playbooks/roles/2fa/files/common-session
Normal file
6
playbooks/roles/2fa/files/common-session
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#%PAM-1.0 -*- mode: conf-space; tab-width: 10 -*-
|
||||||
|
|
||||||
|
session required pam_limits.so
|
||||||
|
session [success=1 default=ignore] pam_unix.so
|
||||||
|
session [default=die] pam_deny.so
|
||||||
|
session optional pam_permit.so
|
||||||
6
playbooks/roles/2fa/files/first-factor
Normal file
6
playbooks/roles/2fa/files/first-factor
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#%PAM-1.0 -*- mode: conf-space; tab-width: 8 -*-
|
||||||
|
|
||||||
|
auth requisite pam_faillock.so preauth
|
||||||
|
auth [success=2 default=ignore] pam_unix.so try_first_pass nullok
|
||||||
|
-auth [success=1 default=ignore] pam_systemd_home.so
|
||||||
|
auth [default=die] pam_faillock.so authfail
|
||||||
2
playbooks/roles/2fa/files/remote-switch.access.conf
Normal file
2
playbooks/roles/2fa/files/remote-switch.access.conf
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
+:ALL:LOCAL
|
||||||
|
-:ALL:ALL
|
||||||
6
playbooks/roles/2fa/files/system-auth
Normal file
6
playbooks/roles/2fa/files/system-auth
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#%PAM-1.0 -*- mode: conf-space; tab-width: 10 -*-
|
||||||
|
|
||||||
|
auth include common-auth
|
||||||
|
account include common-account
|
||||||
|
password include common-password
|
||||||
|
session include common-session
|
||||||
74
playbooks/roles/2fa/tasks/main.yml
Normal file
74
playbooks/roles/2fa/tasks/main.yml
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: 'Install TOTP authenticator and pam_u2f'
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- libpam-google-authenticator
|
||||||
|
- libpam-u2f
|
||||||
|
- pamu2fcfg
|
||||||
|
state: present
|
||||||
|
when: ansible_pkg_mgr == "apt"
|
||||||
|
|
||||||
|
- name: 'Install TOTP authenticator'
|
||||||
|
community.general.pacman:
|
||||||
|
name:
|
||||||
|
- libpam-google-authenticator
|
||||||
|
- pam-u2f
|
||||||
|
state: present
|
||||||
|
when: ansible_pkg_mgr == "pacman"
|
||||||
|
|
||||||
|
- name: 'Deploy PAM remote-user allowlist'
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: remote-switch.access.conf
|
||||||
|
dest: /etc/security/remote-switch.access.conf
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: 'Deploy local-users first auth factor'
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: first-factor
|
||||||
|
dest: /etc/pam.d/first-factor
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
# Only deploy when we're not using Kanidm for native or ldap
|
||||||
|
when: user_source == "local"
|
||||||
|
|
||||||
|
- name: 'Deploy local-access second auth factor'
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: second-factor.j2
|
||||||
|
dest: /etc/pam.d/second-factor
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: 'Deploy PAM common-auth file'
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: common-auth
|
||||||
|
dest: /etc/pam.d/
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: 'Deploy PAM system-auth file'
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: system-auth
|
||||||
|
dest: /etc/pam.d/
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
when: ansible_os_family == "Archlinux"
|
||||||
|
|
||||||
|
- name: 'Deploy local-users common PAM files'
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: 'common-{{ item }}'
|
||||||
|
dest: '/etc/pam.d/'
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
when: user_source == "local"
|
||||||
|
with_items:
|
||||||
|
- password
|
||||||
|
- session
|
||||||
|
- account
|
||||||
11
playbooks/roles/2fa/templates/second-factor.j2
Normal file
11
playbooks/roles/2fa/templates/second-factor.j2
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#%PAM-1.0 -*- mode: conf-space; tab-width: 8 -*-
|
||||||
|
|
||||||
|
# Only prompt for security key if this is a local session.
|
||||||
|
auth [success=ignore default=2] pam_access.so accessfile=/etc/security/remote-switch.access.conf
|
||||||
|
auth [success=2 default=ignore] pam_u2f.so cue origin=pam://{{ ansible_nodename }} appid=pam://{{ ansible_nodename }} userpresence=1
|
||||||
|
# This is a moderate security risk due to the nullok, but the alternative is locking ourselves out of remote machines.
|
||||||
|
# This turns 2FA into an opt-in system.
|
||||||
|
auth [success=1 default=ignore] pam_google_authenticator.so nullok
|
||||||
|
# We could change this to 'pam_faillock.so authfail', but idk that that's worth it.
|
||||||
|
auth [default=die] pam_deny.so
|
||||||
|
auth optional pam_permit.so
|
||||||
42
playbooks/roles/aur/tasks/main.yml
Normal file
42
playbooks/roles/aur/tasks/main.yml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: Install devel packages
|
||||||
|
community.general.pacman:
|
||||||
|
name:
|
||||||
|
- 'base-devel'
|
||||||
|
- 'git'
|
||||||
|
when: ansible_pkg_mgr == "pacman"
|
||||||
|
|
||||||
|
- name: Disable package compression
|
||||||
|
ansible.builtin.replace:
|
||||||
|
path: '/etc/makepkg.conf'
|
||||||
|
regexp: "^PKGEXT=.*$"
|
||||||
|
replace: "PKGEXT='.pkg.tar'"
|
||||||
|
|
||||||
|
- name: Create AUR build user
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: aur_builder
|
||||||
|
system: yes
|
||||||
|
home: /var/lib/pacman/aur/
|
||||||
|
password_lock: yes
|
||||||
|
shell: /usr/bin/false
|
||||||
|
when: ansible_pkg_mgr == "pacman"
|
||||||
|
|
||||||
|
- name: Allow AUR build user to run Pacman as root
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /etc/sudoers.d/aur_builder-allow-to-sudo-pacman
|
||||||
|
state: present
|
||||||
|
line: "aur_builder ALL=(ALL) NOPASSWD: /usr/bin/pacman"
|
||||||
|
validate: /usr/sbin/visudo -cf %s
|
||||||
|
create: yes
|
||||||
|
when: ansible_pkg_mgr == "pacman"
|
||||||
|
|
||||||
|
- name: Install AUR helper using makepkg
|
||||||
|
kewlfft.aur.aur:
|
||||||
|
name: paru-bin
|
||||||
|
use: makepkg
|
||||||
|
state: present
|
||||||
|
become: yes
|
||||||
|
become_user: aur_builder
|
||||||
|
when: ansible_pkg_mgr == "pacman"
|
||||||
|
|
||||||
12
playbooks/roles/kanidm_native/files/common-account
Normal file
12
playbooks/roles/kanidm_native/files/common-account
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#%PAM-1.0 -*- mode: conf-space; tab-width: 10 -*-
|
||||||
|
|
||||||
|
# Local users don't authenticate with Kanidm
|
||||||
|
account [success=1 default=ignore] pam_localuser.so
|
||||||
|
# When Kanidm fails, jump straight to the deny line. We already know we're not a local user, so this is fine.
|
||||||
|
account [success=3 default=2] pam_kanidm.so
|
||||||
|
-account [success=2 default=ignore] pam_systemd_home.so
|
||||||
|
account [success=1 default=ignore] pam_unix.so
|
||||||
|
# If any of the above account lines fail, they'll jump here, which kills the authorization attempt.
|
||||||
|
account [default=die] pam_deny.so
|
||||||
|
account optional pam_permit.so
|
||||||
|
account required pam_time.so
|
||||||
8
playbooks/roles/kanidm_native/files/common-password
Normal file
8
playbooks/roles/kanidm_native/files/common-password
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#%PAM-1.0 -*- mode: conf-space; tab-width: 10 -*-
|
||||||
|
|
||||||
|
password [success=1 default=ignore] pam_localuser.so
|
||||||
|
password [success=3 default=2] pam_kanidm.so
|
||||||
|
-password [success=2 default=ignore] pam_systemd_home.so
|
||||||
|
password [success=1 default=ignore] pam_unix.so try_first_pass nullok shadow sha512
|
||||||
|
password [default=die] pam_deny.so
|
||||||
|
password optional pam_permit.so
|
||||||
7
playbooks/roles/kanidm_native/files/common-session
Normal file
7
playbooks/roles/kanidm_native/files/common-session
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#%PAM-1.0 -*- mode: conf-space; tab-width: 10 -*-
|
||||||
|
|
||||||
|
session required pam_limits.so
|
||||||
|
session optional pam_unix.so
|
||||||
|
session optional pam_umask.so
|
||||||
|
session optional pam_kanidm.so
|
||||||
|
session optional pam_env.so
|
||||||
11
playbooks/roles/kanidm_native/files/first-factor
Normal file
11
playbooks/roles/kanidm_native/files/first-factor
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#%PAM-1.0 -*- mode: conf-space; tab-width: 8 -*-
|
||||||
|
|
||||||
|
# First authentication factor for Kanidm-native systems
|
||||||
|
|
||||||
|
auth requisite pam_faillock.so preauth
|
||||||
|
auth [success=1 default=ignore] pam_localuser.so
|
||||||
|
auth [success=3 default=2] pam_kanidm.so
|
||||||
|
auth [sucesss=2 default=ignore] pam_unix.so try_first_pass nullok
|
||||||
|
-auth [success=1 default=ignore] pam_systemd_home.so
|
||||||
|
auth [default=die] pam_faillock.so authfail
|
||||||
|
auth optional pam_permit.so
|
||||||
10
playbooks/roles/kanidm_native/files/kanidm-hack.sh
Normal file
10
playbooks/roles/kanidm_native/files/kanidm-hack.sh
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This script is called by systemd-sleep, and is used to prevent the
|
||||||
|
# daemon from getting stuck when resuming from sleep.
|
||||||
|
# Only noticed so far on resume from hibernate, but it's possible that
|
||||||
|
# it could happen on suspend as well.
|
||||||
|
|
||||||
|
if [[ $1 == "post" ]]; then
|
||||||
|
systemctl restart kanidm-unixd.service
|
||||||
|
fi
|
||||||
6
playbooks/roles/kanidm_native/handlers/main.yml
Normal file
6
playbooks/roles/kanidm_native/handlers/main.yml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: Restart SSH
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
unit: "sshd.service"
|
||||||
|
state: "restarted"
|
||||||
109
playbooks/roles/kanidm_native/tasks/main.yml
Normal file
109
playbooks/roles/kanidm_native/tasks/main.yml
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: 'Install kanidm clients with pacman'
|
||||||
|
kewlfft.aur.aur:
|
||||||
|
use: paru
|
||||||
|
aur_only: yes
|
||||||
|
name: kanidm-unixd-clients
|
||||||
|
become: yes
|
||||||
|
become_user: aur_builder
|
||||||
|
when: ansible_pkg_mgr == "pacman"
|
||||||
|
|
||||||
|
- name: 'Fetch kanidm PPA key'
|
||||||
|
ansible.builtin.apt_key:
|
||||||
|
url: >-
|
||||||
|
https://kanidm.github.io/kanidm_ppa/KEY.gpg
|
||||||
|
state: present
|
||||||
|
id: 'EA20E95D68A65191FE8CE79576CC814060B23E66'
|
||||||
|
when: ansible_pkg_mgr == "apt"
|
||||||
|
|
||||||
|
- name: 'Create kanidm PPA'
|
||||||
|
ansible.builtin.apt_repository:
|
||||||
|
repo: >-
|
||||||
|
deb https://kanidm.github.io/kanidm_ppa/{{ ansible_distribution | lower }} ./
|
||||||
|
state: present
|
||||||
|
when: ansible_pkg_mgr == "apt"
|
||||||
|
|
||||||
|
- name: 'Install kanidm with apt'
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: kanidm-unixd-clients
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
when: ansible_pkg_mgr == "apt"
|
||||||
|
|
||||||
|
- name: 'Ensure kanidm config directory exists'
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /etc/kanidm
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: 'Install kanidm config files'
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: '{{ item }}.j2'
|
||||||
|
dest: '/etc/kanidm/{{ item }}'
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
with_items:
|
||||||
|
- unixd
|
||||||
|
- config
|
||||||
|
|
||||||
|
- name: 'Enable kanidm daemons'
|
||||||
|
ansible.builtin.systemd_service:
|
||||||
|
state: started
|
||||||
|
enabled: yes
|
||||||
|
name: "{{ item }}"
|
||||||
|
daemon_reload: yes
|
||||||
|
with_items:
|
||||||
|
- kanidm-unixd
|
||||||
|
- kanidm-unixd-tasks
|
||||||
|
|
||||||
|
|
||||||
|
- name: 'Enable kanidm as a passwd db'
|
||||||
|
ansible.builtin.replace:
|
||||||
|
path: '/etc/nsswitch.conf'
|
||||||
|
regexp: "^{{ item }}:.*$"
|
||||||
|
replace: "{{ item }}: files {{ (item == 'group') | ternary('[SUCCESS=merge]', '') }} systemd compat kanidm"
|
||||||
|
# This is a critical system file that could brick the OS. Back it up!
|
||||||
|
backup: yes
|
||||||
|
with_items:
|
||||||
|
- passwd
|
||||||
|
- group
|
||||||
|
|
||||||
|
- name: 'Deploy first-factor PAM configuration'
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: first-factor
|
||||||
|
dest: /etc/pam.d/first-factor
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: 'Deploy common PAM modules for kanidm'
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: '{{ item }}'
|
||||||
|
dest: /etc/pam.d/
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
with_fileglob:
|
||||||
|
- "../files/common-*"
|
||||||
|
|
||||||
|
- name: 'Deploy SSH key handling'
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: 10-kanidm-keys.conf.j2
|
||||||
|
dest: /etc/ssh/sshd_config.d/
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
notify: Restart SSH
|
||||||
|
|
||||||
|
- name: 'Deploy sleep fix hack'
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: kanidm-hack.sh
|
||||||
|
dest: /usr/lib/systemd/system-sleep/
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
PubkeyAuthentication yes
|
||||||
|
UsePAM yes
|
||||||
|
|
||||||
|
Match Group {{ allowed_groups | join(',') }}
|
||||||
|
AuthorizedKeysCommand /usr/sbin/kanidm_ssh_authorizedkeys %u
|
||||||
|
AuthorizedKeysCommandUser nobody
|
||||||
8
playbooks/roles/kanidm_native/templates/config.j2
Normal file
8
playbooks/roles/kanidm_native/templates/config.j2
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
uri = "{{ kanidm_uri }}"
|
||||||
|
verify_ca = true
|
||||||
|
verify_hostnames = true
|
||||||
|
|
||||||
|
{% for name, uri in kanidm_supplemental %}
|
||||||
|
[{{ name }}]
|
||||||
|
uri = "{{ uri }}"
|
||||||
|
{% endfor %}
|
||||||
11
playbooks/roles/kanidm_native/templates/unixd.j2
Normal file
11
playbooks/roles/kanidm_native/templates/unixd.j2
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# -*-conf-unix-*-
|
||||||
|
|
||||||
|
pam_allowed_login_groups = {{ allowed_groups }}
|
||||||
|
default_shell = "/bin/bash"
|
||||||
|
home_attr = "uuid"
|
||||||
|
home_alias = "name"
|
||||||
|
use_etc_skel = true
|
||||||
|
# Just use name for usernames, it shouldn't be too hard to keep track of
|
||||||
|
uid_attr_map = "name"
|
||||||
|
# Use the SPN for groups, so we can easily tell which are local and which are remote
|
||||||
|
gid_attr_map = "spn"
|
||||||
6
requirements.yml
Normal file
6
requirements.yml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
collections:
|
||||||
|
- name: >-
|
||||||
|
kewlfft.aur
|
||||||
|
- name: >-
|
||||||
|
community.general
|
||||||
Loading…
x
Reference in New Issue
Block a user