Initial work on PAM config. Has issues, deploy with care

This commit is contained in:
Ezri Brimhall 2024-09-05 09:02:15 -06:00
commit bd86a9e1f9
Signed by: ezri
GPG Key ID: 3DA1675C4E9B9216
27 changed files with 407 additions and 0 deletions

3
ansible.cfg Normal file
View File

@ -0,0 +1,3 @@
[defaults]
inventory = inventory
host_key_checking = False

8
group_vars/all.yml Normal file
View 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
View File

@ -0,0 +1,5 @@
---
user_source: "kanidm-native"
allowed_groups: "{{ [ 'desktop_users' ] }}"

15
inventory Normal file
View 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

View File

@ -0,0 +1,8 @@
---
- name: Configure desktop systems
hosts: desktop
roles:
- aur
- kanidm_native
- 2fa

View File

@ -0,0 +1,6 @@
---
- name: Kanidm native unixd clients
hosts: kanidm_native
roles:
- kanidm_native

View 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

View 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

View 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

View 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

View 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

View File

@ -0,0 +1,2 @@
+:ALL:LOCAL
-:ALL:ALL

View 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

View 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

View 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

View 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"

View 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

View 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

View 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

View 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

View 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

View File

@ -0,0 +1,6 @@
---
- name: Restart SSH
ansible.builtin.systemd:
unit: "sshd.service"
state: "restarted"

View 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"

View File

@ -0,0 +1,6 @@
PubkeyAuthentication yes
UsePAM yes
Match Group {{ allowed_groups | join(',') }}
AuthorizedKeysCommand /usr/sbin/kanidm_ssh_authorizedkeys %u
AuthorizedKeysCommandUser nobody

View File

@ -0,0 +1,8 @@
uri = "{{ kanidm_uri }}"
verify_ca = true
verify_hostnames = true
{% for name, uri in kanidm_supplemental %}
[{{ name }}]
uri = "{{ uri }}"
{% endfor %}

View 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
View File

@ -0,0 +1,6 @@
---
collections:
- name: >-
kewlfft.aur
- name: >-
community.general