--- # Two-factor authentication — port of Modules/Security/secharden-2fa.sh. # Configures TOTP 2FA for SSH, Cockpit, and Webmin (if present). # --------------------------------------------------------------------------- # Backups (backup_configs) # --------------------------------------------------------------------------- - name: Ensure 2FA backup directory exists ansible.builtin.file: path: /root/backup/2fa state: directory owner: root group: root mode: "0700" - name: Backup existing SSH config ansible.builtin.copy: src: /etc/ssh/sshd_config dest: /root/backup/2fa/sshd_config.bak remote_src: true mode: "0600" failed_when: false - name: Backup existing PAM directory ansible.builtin.copy: src: /etc/pam.d dest: /root/backup/2fa/pam.d.bak remote_src: true mode: "0700" failed_when: false - name: Backup existing Cockpit config (if present) ansible.builtin.copy: src: /etc/cockpit/cockpit.conf dest: /root/backup/2fa/cockpit.conf.bak remote_src: true mode: "0600" failed_when: false # --------------------------------------------------------------------------- # Packages (install_2fa_packages) # --------------------------------------------------------------------------- - name: Install 2FA packages ansible.builtin.apt: name: - libpam-google-authenticator - qrencode state: present update_cache: true # --------------------------------------------------------------------------- # SSH configuration (configure_ssh_2fa) # --------------------------------------------------------------------------- - name: Enable ChallengeResponseAuthentication ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: '^\s*ChallengeResponseAuthentication\s+' line: 'ChallengeResponseAuthentication yes' create: true mode: "0600" owner: root group: root notify: Validate and restart sshd (2fa) - name: Enable KbdInteractiveAuthentication ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: '^\s*KbdInteractiveAuthentication\s+' line: 'KbdInteractiveAuthentication yes' create: true mode: "0600" owner: root group: root notify: Validate and restart sshd (2fa) - name: Enable UsePAM ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: '^\s*UsePAM\s+' line: 'UsePAM yes' create: true mode: "0600" owner: root group: root notify: Validate and restart sshd (2fa) - name: Require publickey + 2FA token ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: '^\s*AuthenticationMethods\s+' line: 'AuthenticationMethods publickey,keyboard-interactive' create: true mode: "0600" owner: root group: root notify: Validate and restart sshd (2fa) # --------------------------------------------------------------------------- # PAM for SSH (configure_pam_2fa) # --------------------------------------------------------------------------- - name: Deploy PAM sshd config with Google Authenticator ansible.builtin.copy: src: pam_sshd dest: /etc/pam.d/sshd owner: root group: root mode: "0644" backup: true # --------------------------------------------------------------------------- # Cockpit 2FA (configure_cockpit_2fa) # --------------------------------------------------------------------------- - name: Ensure /etc/cockpit exists ansible.builtin.file: path: /etc/cockpit state: directory owner: root group: root mode: "0755" - name: Deploy Cockpit config for 2FA ansible.builtin.copy: src: cockpit.conf dest: /etc/cockpit/cockpit.conf owner: root group: root mode: "0644" backup: true - name: Deploy PAM cockpit config with Google Authenticator ansible.builtin.copy: src: pam_cockpit dest: /etc/pam.d/cockpit owner: root group: root mode: "0644" backup: true # --------------------------------------------------------------------------- # Webmin 2FA (configure_webmin_2fa) — only if webmin installed # --------------------------------------------------------------------------- - name: Check for Webmin installation # noqa var-naming[no-role-prefix] ansible.builtin.stat: path: /etc/webmin/miniserv.conf register: _webmin_conf - name: Configure Webmin 2FA (TOTP provider + enable) when: _webmin_conf.stat.exists block: - name: Set twofactor_provider=totp ansible.builtin.lineinfile: path: /etc/webmin/miniserv.conf regexp: '^twofactor_provider=' line: 'twofactor_provider=totp' create: true mode: "0644" owner: root group: root notify: Restart webmin (2fa) - name: Enable twofactor=1 ansible.builtin.lineinfile: path: /etc/webmin/miniserv.conf regexp: '^twofactor=' line: 'twofactor=1' create: true mode: "0644" owner: root group: root notify: Restart webmin (2fa) # --------------------------------------------------------------------------- # Per-user enrollment scaffolding (setup_user_2fa) # Only localuser and root are enrolled by default (matches legacy script). # --------------------------------------------------------------------------- - name: Users to enroll with 2FA # noqa var-naming[no-role-prefix] ansible.builtin.set_fact: _2fa_users: "{{ managed_users | selectattr('name', 'in', ['root', 'localuser']) | list }}" - name: Detect existing 2FA target users ansible.builtin.getent: database: passwd - name: Drop enrollment script for each 2FA user ansible.builtin.copy: src: setup-2fa.sh dest: "/tmp/setup-2fa-{{ item.name }}.sh" owner: root group: root mode: "0755" loop: "{{ _2fa_users }}" when: item.name in getent_passwd - name: Drop instructions in each 2FA user's home ansible.builtin.copy: src: 2fa-setup-instructions.txt dest: "{{ item.home }}/2fa-setup-instructions.txt" owner: "{{ item.name }}" group: "{{ item.name }}" mode: "0644" loop: "{{ _2fa_users }}" when: item.name in getent_passwd failed_when: false