diff --git a/README.md b/README.md index 98bbc5add57e70685568893b3a313076f4c0ca7a..a3ddc3aaf513196f332e69de6a477e4ad879cf65 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Features * One-time-password (OTP) support for client installation * Repair mode for clients * Backup and restore, also to and from controller +* Smartcard setup for servers and clients * Modules for automembership rule management * Modules for automount key management * Modules for automount location management @@ -425,6 +426,8 @@ Roles * [Replica](roles/ipareplica/README.md) * [Client](roles/ipaclient/README.md) * [Backup](roles/ipabackup/README.md) +* [SmartCard server](roles/ipasmartcard_server/README.md) +* [SmartCard client](roles/ipasmartcard_client/README.md) Modules in plugin/modules ========================= diff --git a/playbooks/install-smartcard-clients.yml b/playbooks/install-smartcard-clients.yml new file mode 100644 index 0000000000000000000000000000000000000000..d78e7834adcf084842d43539ed02acddc6d4cf54 --- /dev/null +++ b/playbooks/install-smartcard-clients.yml @@ -0,0 +1,8 @@ +--- +- name: Playbook to setup smartcard for IPA clients + hosts: ipaclients + become: true + + roles: + - role: ipasmartcard_client + state: present diff --git a/playbooks/install-smartcard-replicas.yml b/playbooks/install-smartcard-replicas.yml new file mode 100644 index 0000000000000000000000000000000000000000..1482d196692b52811165534da6a58f4f5ada6a56 --- /dev/null +++ b/playbooks/install-smartcard-replicas.yml @@ -0,0 +1,8 @@ +--- +- name: Playbook to setup smartcard for IPA replicas + hosts: ipareplicas + become: true + + roles: + - role: ipasmartcard_server + state: present diff --git a/playbooks/install-smartcard-server.yml b/playbooks/install-smartcard-server.yml new file mode 100644 index 0000000000000000000000000000000000000000..ebca03cb64903687c462748ccf398fcc82e68c27 --- /dev/null +++ b/playbooks/install-smartcard-server.yml @@ -0,0 +1,8 @@ +--- +- name: Playbook to setup smartcard for IPA server + hosts: ipaserver + become: true + + roles: + - role: ipasmartcard_server + state: present diff --git a/playbooks/install-smartcard-servers.yml b/playbooks/install-smartcard-servers.yml new file mode 100644 index 0000000000000000000000000000000000000000..4986fb4b7a8cc21a30426130ff1dc26229635a90 --- /dev/null +++ b/playbooks/install-smartcard-servers.yml @@ -0,0 +1,8 @@ +--- +- name: Playbook to setup smartcard for IPA server and replicas + hosts: ipaserver, ipareplicas + become: true + + roles: + - role: ipasmartcard_server + state: present diff --git a/roles/ipasmartcard_client/README.md b/roles/ipasmartcard_client/README.md new file mode 100644 index 0000000000000000000000000000000000000000..afe0ae1ce9396a3b35536c6a56fc6050f5ffb348 --- /dev/null +++ b/roles/ipasmartcard_client/README.md @@ -0,0 +1,111 @@ +ipasmartcard_client role +======================== + +Description +----------- + +This role allows to configure IPA clients for Smart Card authentication. + +**Note**: The ansible-freeipa smartcard client role requires an enrolled IPA client. + + +Features +-------- +* Client setup for Smart Card authentication + + +Supported FreeIPA Versions +-------------------------- + +FreeIPA versions 4.5 and up are supported by this role. + + +Supported Distributions +----------------------- + +* RHEL/CentOS 7.6+ +* Fedora 26+ + + +Requirements +------------ + +**Controller** +* Ansible version: 2.8+ + +**Node** +* Supported FreeIPA version (see above) +* Supported distribution (needed for package installation only, see above) +* Enrolled IPA client + + +Limitations +----------- + +Only the enablement of smartcards is supported by the role, there is no disablement. + + +Usage +===== + +Example inventory file with IPA clients: + +```ini +[ipaclients] +ipaclient1.example.com +ipaclient2.example.com + +[ipaclients:vars] +ipaadmin_password=SomeADMINpassword +ipasmartcard_client_ca_certs=/etc/ipa/ca.crt +``` + +Example playbook to setup smartcard for the IPA clients using admin password and ipasmartcard_client_ca_certs from inventory file: + +```yaml +--- +- name: Playbook to setup smartcard for IPA clients + hosts: ipaclients + become: true + + roles: + - role: ipasmartcard_client + state: present +``` + +Playbooks +========= + +The playbooks needed to setup smartcard for the IPA clients is part of the repository in the playbooks folder. + +``` +install-smartcard-clients.yml +``` + +Please remember to link or copy the playbooks to the base directory of ansible-freeipa if you want to use the roles within the source archive. + + +How to setup smartcard for clients +---------------------------------- + +```bash +ansible-playbook -v -i inventory/hosts install-smartcard-clients.yml +``` +This will setup the clients for smartcard use. + + +Variables +========= + +Variable | Description | Required +-------- | ----------- | -------- +`ipaadmin_principal` | The kerberos principal used for admin. Will be set to `admin` if not set. (string) | no +`ipaadmin_password` | The password for the IPA admin user. As an alternative an admin user keytab can be used instead with `ipaadmin_keytab`. (string) | yes +`ipaadmin_keytab` | The admin keytab as an alternative to `ipaadmin_password`. (string) | no +`ipasmartcard_client_ca_certs` | The CA certificates for smartcard use. If `ipasmartcard_client_ca_certs` is not set, but `ipasmartcard_server_ca_certs`, then `ipasmartcard_server_ca_certs` will be used. | yes + + +Authors +======= + +Thomas Woerner diff --git a/roles/ipasmartcard_client/defaults/main.yml b/roles/ipasmartcard_client/defaults/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..15602768ef9551010698c249a9585560672c3f0e --- /dev/null +++ b/roles/ipasmartcard_client/defaults/main.yml @@ -0,0 +1,4 @@ +--- +# defaults file for ipasmartcard_client role + +ipaclient_install_packages: yes diff --git a/roles/ipasmartcard_client/files/ipasmartcard_client_add_ca_to_sssd_auth_ca_db.sh b/roles/ipasmartcard_client/files/ipasmartcard_client_add_ca_to_sssd_auth_ca_db.sh new file mode 100644 index 0000000000000000000000000000000000000000..cdb891271d3deadb957a036c240df2480caaa825 --- /dev/null +++ b/roles/ipasmartcard_client/files/ipasmartcard_client_add_ca_to_sssd_auth_ca_db.sh @@ -0,0 +1,30 @@ +#!/bin/bash -eu + +# Authors: +# Thomas Woerner <twoerner@redhat.com> +# +# Copyright (C) 2022 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +cert_file=$1 +db=$2 + +if [ -z "${cert_file}" ] || [ -z "${db}" ]; then + echo "Usage: $0 <ca cert> <db file>" + exit 1 +fi + +cat "${cert_file}" >> "${db}" diff --git a/roles/ipasmartcard_client/files/ipasmartcard_client_add_ca_to_systemwide_db.sh b/roles/ipasmartcard_client/files/ipasmartcard_client_add_ca_to_systemwide_db.sh new file mode 100644 index 0000000000000000000000000000000000000000..6da158019492e184162bfabd4baffade6a5400e5 --- /dev/null +++ b/roles/ipasmartcard_client/files/ipasmartcard_client_add_ca_to_systemwide_db.sh @@ -0,0 +1,31 @@ +#!/bin/bash -eu + +# Authors: +# Thomas Woerner <twoerner@redhat.com> +# +# Copyright (C) 2022 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +cert_file=$1 +db=$2 + +if [ -z "${cert_file}" ] || [ -z "${db}" ]; then + echo "Usage: $0 <ca cert> <db file>" + exit 1 +fi + +uuid=$(uuidgen) +certutil -d "${db}" -A -i "${cert_file}" -n "Smart Card CA ${uuid}" -t CT,C,C diff --git a/roles/ipasmartcard_client/files/ipasmartcard_client_add_pkcs11_module_to_systemwide_db.sh b/roles/ipasmartcard_client/files/ipasmartcard_client_add_pkcs11_module_to_systemwide_db.sh new file mode 100644 index 0000000000000000000000000000000000000000..12f3e750826db11c4ac603e36db2387a86a24d14 --- /dev/null +++ b/roles/ipasmartcard_client/files/ipasmartcard_client_add_pkcs11_module_to_systemwide_db.sh @@ -0,0 +1,36 @@ +#!/bin/bash -eu + +# Authors: +# Thomas Woerner <twoerner@redhat.com> +# +# Copyright (C) 2022 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +nssdb=$1 +module_name="OpenSC" +pkcs11_shared_lib="/usr/lib64/opensc-pkcs11.so" + +if [ -z "${nssdb}" ]; then + echo "Usage: $0 <nssdb>" + exit 1 +fi + +if modutil -dbdir "${nssdb}" -list | grep -q "${module_name}" || p11-kit list-modules | grep -i "${module_name}" -q +then + echo "${module_name} PKCS#11 module already configured" +else + echo "" | modutil -dbdir "${nssdb}" -add "${module_name}" -libfile "${pkcs11_shared_lib}" +fi diff --git a/roles/ipasmartcard_client/library/ipasmartcard_client_get_vars.py b/roles/ipasmartcard_client/library/ipasmartcard_client_get_vars.py new file mode 100644 index 0000000000000000000000000000000000000000..66c65912af308eae59abdf56dddbcbd64b2adf5a --- /dev/null +++ b/roles/ipasmartcard_client/library/ipasmartcard_client_get_vars.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- + +# Authors: +# Thomas Woerner <twoerner@redhat.com> +# +# Copyright (C) 2022 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.0', + 'supported_by': 'community', + 'status': ['preview'], +} + +DOCUMENTATION = ''' +--- +module: ipasmartcard_client_get_vars +short description: + Get variables from ipaplatform and python interpreter used for the module. +description: + Get variables from ipaplatform and python interpreter used for the module. +options: +author: + - Thomas Woerner +''' + +EXAMPLES = ''' +- name: Get VARS from IPA + ipasmartcard_client_get_vars: + register: ipasmartcard_client_vars +''' + +RETURN = ''' +NSS_DB_DIR: + description: paths.NSS_DB_DIR from ipaplatform + returned: always + type: str +USE_AUTHSELECT: + description: True if "AUTHSELECT" is defined in paths + returned: always + type: bool +python_interpreter: + description: Python interpreter from sys.executable + returned: always + type: str +''' + +import sys +from ansible.module_utils.basic import AnsibleModule +from ipaplatform.paths import paths + + +def main(): + ansible_module = AnsibleModule( + argument_spec={}, + supports_check_mode=False, + ) + + ansible_module.exit_json(changed=False, + NSS_DB_DIR=paths.NSS_DB_DIR, + USE_AUTHSELECT=hasattr(paths, "AUTHSELECT"), + python_interpreter=sys.executable) + + +if __name__ == '__main__': + main() diff --git a/roles/ipasmartcard_client/library/ipasmartcard_client_validate_ca_certs.py b/roles/ipasmartcard_client/library/ipasmartcard_client_validate_ca_certs.py new file mode 100644 index 0000000000000000000000000000000000000000..c89427b13eff34d68132dea1f77093bc98c5dc4c --- /dev/null +++ b/roles/ipasmartcard_client/library/ipasmartcard_client_validate_ca_certs.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- + +# Authors: +# Thomas Woerner <twoerner@redhat.com> +# +# Based on ipa-replica-install code +# +# Copyright (C) 2022 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.0', + 'supported_by': 'community', + 'status': ['preview'], +} + +DOCUMENTATION = ''' +--- +module: ipasmartcard_server_validate_ca_certs +short description: Validate CA certs +description: Validate CA certs +options: + ca_cert_files: + description: + List of files containing CA certificates for the service certificate + files + required: yes +author: + - Thomas Woerner +''' + +EXAMPLES = ''' +''' + +RETURN = ''' +''' + +import os.path +from ansible.module_utils.basic import AnsibleModule +try: + from ipalib import x509 +except ImportError: + x509 = None + + +def main(): + ansible_module = AnsibleModule( + argument_spec=dict( + ca_cert_files=dict(required=False, type='list', default=[]), + ), + supports_check_mode=False, + ) + + # get parameters # + + ca_cert_files = ansible_module.params.get('ca_cert_files') + + # import check # + + if x509 is None: + ansible_module.fail_json(msg="Failed to import x509 from ipalib") + + # validate ca certs # + + if ca_cert_files is not None: + if not isinstance(ca_cert_files, list): + ansible_module.fail_json( + msg="Expected list, got %s" % repr(ca_cert_files)) + # remove duplicates + ca_cert_files = list(dict.fromkeys(ca_cert_files)) + # validate + for cert in ca_cert_files: + if not os.path.exists(cert): + ansible_module.fail_json(msg="'%s' does not exist" % cert) + if not os.path.isfile(cert): + ansible_module.fail_json(msg="'%s' is not a file" % cert) + if not os.path.isabs(cert): + ansible_module.fail_json( + msg="'%s' is not an absolute file path" % cert) + try: + x509.load_certificate_from_file(cert) + except Exception: + ansible_module.fail_json( + msg="'%s' is not a valid certificate file" % cert) + + # exit # + + ansible_module.exit_json(changed=False, + ca_cert_files=ca_cert_files) + + +if __name__ == '__main__': + main() diff --git a/roles/ipasmartcard_client/meta/main.yml b/roles/ipasmartcard_client/meta/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..26ef55df94f3c962e7aab8399895a141698cc019 --- /dev/null +++ b/roles/ipasmartcard_client/meta/main.yml @@ -0,0 +1,22 @@ +--- +dependencies: [] + +galaxy_info: + author: Thomas Woerner + description: A role to setup IPA server(s) for Smart Card authentication + company: Red Hat, Inc + license: GPLv3 + min_ansible_version: 2.8 + platforms: + - name: Fedora + versions: + - all + - name: EL + versions: + - 7 + - 8 + galaxy_tags: + - identity + - ipa + - freeipa + - smartcard diff --git a/roles/ipasmartcard_client/tasks/main.yml b/roles/ipasmartcard_client/tasks/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..e4a560bcb3d6dbdb57c7768bdbbf8643e2cb02ab --- /dev/null +++ b/roles/ipasmartcard_client/tasks/main.yml @@ -0,0 +1,173 @@ +--- +# tasks file for ipasmartcard_client role + +- name: Uninstall smartcard client + ansible.builtin.fail: msg="Uninstalling smartcard for IPA is not supported" + when: state|default('present') == 'absent' + +- name: Import variables specific to distribution + ansible.builtin.include_vars: "{{ item }}" + with_first_found: + - "vars/{{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_version'] }}.yml" + - "vars/{{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_major_version'] }}.yml" + - "vars/{{ ansible_facts['distribution'] }}.yml" + # os_family is used as a fallback for distros which are not currently + # supported, but are based on a supported distro family. For example, + # Oracle, Rocky, Alma and Alibaba linux, which are all "RedHat" based. + - "vars/{{ ansible_facts['os_family'] }}-{{ ansible_facts['distribution_version'] }}.yml" + - "vars/{{ ansible_facts['os_family'] }}-{{ ansible_facts['distribution_major_version'] }}.yml" + - "vars/{{ ansible_facts['os_family'] }}.yml" + # If neither distro nor family is supported, try a default configuration. + - "vars/default.yml" + +- block: + + # CA CERTS + + # Use "ipasmartcard_server_ca_certs" + + - name: Use "ipasmartcard_server_ca_certs" + ansible.builtin.set_fact: + ipasmartcard_client_ca_certs: "{{ ipasmartcard_server_ca_certs }}" + when: ipasmartcard_client_ca_certs is not defined and + ipasmartcard_server_ca_certs is defined + + # Fail on empty "ipasmartcard_client_ca_certs" + + - name: Fail on empty "ipasmartcard_client_ca_certs" + ansible.builtin.fail: msg="No CA certs given in 'ipasmartcard_client_ca_certs'" + when: ipasmartcard_client_ca_certs is not defined or + ipasmartcard_client_ca_certs | length < 1 + + # Validate ipasmartcard_client_ca_certs + + - name: Validate CA certs "{{ ipasmartcard_client_ca_certs }}" + ipasmartcard_client_validate_ca_certs: + ca_cert_files: "{{ ipasmartcard_client_ca_certs }}" + register: result_validate_ca_certs + + # INSTALL needed packages: opensc, dconf and krb5-pkinit-openssl + + - name: Ensure needed packages are installed + ansible.builtin.package: + name: "{{ ipasmartcard_client_packages }}" + state: present + + # REMOVE pam_pkcs11 + + - name: Ensure pam_pkcs11 is missing + ansible.builtin.package: + name: "{{ ipasmartcard_client_remove_pam_pkcs11_packages }}" + state: absent + + # KINIT + + - name: Set default principal if not given + ansible.builtin.set_fact: + ipaadmin_principal: admin + when: ipaadmin_principal is undefined + + - name: kinit using "{{ ipaadmin_principal }}" password + ansible.builtin.command: kinit "{{ ipaadmin_principal }}" + args: + stdin: "{{ ipaadmin_password }}" + when: ipaadmin_password is defined + + - name: kinit using "{{ ipaadmin_principal }}" keytab + ansible.builtin.command: kinit -kt "{{ ipaadmin_keytab }}" "{{ ipaadmin_principal }}" + when: ipaadmin_keytab is defined + + # Enable and start smartcard daemon + + - name: Enable and start smartcard daemon + ansible.builtin.service: + name: pcscd + enabled: true + state: started + + # GET VARS FROM IPA + + - name: Get VARS from IPA + ipasmartcard_client_get_vars: + register: ipasmartcard_client_vars + + # Add pkcs11 module to systemwide db + + - name: Add pkcs11 module to systemwide db + ansible.builtin.script: ipasmartcard_client_add_pkcs11_module_to_systemwide_db.sh + "{{ ipasmartcard_client_vars.NSS_DB_DIR }}" + + # Ensure /etc/sssd/pki exists + + - block: + - name: Ensure /etc/sssd/pki exists + ansible.builtin.file: + path: /etc/sssd/pki + state: directory + mode: 0711 + + - name: Ensure /etc/sssd/pki/sssd_auth_ca_db.pem is absent + ansible.builtin.file: + path: /etc/sssd/pki/sssd_auth_ca_db.pem + state: absent + + when: ipasmartcard_client_vars.USE_AUTHSELECT + + # Upload smartcard CA certificates to systemwide db + + - name: Upload smartcard CA certificates to systemwide db + ansible.builtin.script: ipasmartcard_client_add_ca_to_systemwide_db.sh + "{{ item }}" + "{{ ipasmartcard_client_vars.NSS_DB_DIR }}" + with_items: "{{ result_validate_ca_certs.ca_cert_files }}" + + # Newer version of sssd use OpenSSL and read the CA certs + # from /etc/sssd/pki/sssd_auth_ca_db.pem + + - name: Add CA certs to /etc/sssd/pki/sssd_auth_ca_db.pem + ansible.builtin.script: ipasmartcard_client_add_ca_to_sssd_auth_ca_db.sh + "{{ item }}" + /etc/sssd/pki/sssd_auth_ca_db.pem + with_items: "{{ result_validate_ca_certs.ca_cert_files }}" + when: ipasmartcard_client_vars.USE_AUTHSELECT + + # Update ipa CA certificate store + + - name: Update ipa CA certificate store + ansible.builtin.command: ipa-certupdate + + # Run authselect or authconfig to configure smartcard auth + + - name: Use authselect to enable Smart Card authentication + ansible.builtin.command: authselect enable-feature with-smartcard + when: ipasmartcard_client_vars.USE_AUTHSELECT + + - name: Use authconfig to enable Smart Card authentication + ansible.builtin.command: authconfig --enablesssd --enablesssdauth --enablesmartcard --smartcardmodule=sssd --smartcardaction=1 --updateall + when: not ipasmartcard_client_vars.USE_AUTHSELECT + + # Set pam_cert_auth=True in /etc/sssd/sssd.conf + + - name: Store NSS OCSP upgrade state + ansible.builtin.command: "{{ ipasmartcard_client_vars.python_interpreter }}" + args: + stdin: | + from SSSDConfig import SSSDConfig + c = SSSDConfig() + c.import_config() + c.set("pam", "pam_cert_auth", "True") + c.write() + when: ipasmartcard_client_vars.USE_AUTHSELECT + + # Restart sssd + + - name: Restart sssd + ansible.builtin.service: + name: sssd + state: restarted + + ### ALWAYS ### + + always: + - name: kdestroy + ansible.builtin.command: kdestroy -A diff --git a/roles/ipasmartcard_client/vars/default.yml b/roles/ipasmartcard_client/vars/default.yml new file mode 100644 index 0000000000000000000000000000000000000000..15e5ae2ca999f605f93a75e2bd79ea07c675711a --- /dev/null +++ b/roles/ipasmartcard_client/vars/default.yml @@ -0,0 +1,3 @@ +--- +ipasmartcard_client_remove_pam_pkcs11_packages: [ "pam_pkcs11" ] +ipasmartcard_client_packages: [ "opensc", "dconf", "krb5-pkinit-openssl" ] diff --git a/roles/ipasmartcard_server/README.md b/roles/ipasmartcard_server/README.md new file mode 100644 index 0000000000000000000000000000000000000000..299b49fcd274be5aa985ce5c5b96e8e9396b3f68 --- /dev/null +++ b/roles/ipasmartcard_server/README.md @@ -0,0 +1,169 @@ +ipasmartcard_server role +======================== + +Description +----------- + +This role allows to configure an IPA server (master or replica) for Smart Card authentication. + +**Note**: The ansible-freeipa smartcard server role requires a configured IPA server with ipa-ca.DOMAIN resolvable by the DNS server. + +With external DNS ipa-ca.DOMAIN needs to be set. + + +Features +-------- +* Server setup for Smart Card authentication + + +Supported FreeIPA Versions +-------------------------- + +FreeIPA versions 4.5 and up are supported by this role. + + +Supported Distributions +----------------------- + +* RHEL/CentOS 7.6+ +* Fedora 26+ + + +Requirements +------------ + +**Controller** +* Ansible version: 2.8+ + +**Node** +* Supported FreeIPA version (see above) +* Supported distribution (needed for package installation only, see above) +* Deployed IPA server + + +Limitations +----------- + +Only the enablement of smartcards is supported by the role, there is no disablement. The disablement of features in IPA in not supported. + + +Usage +===== + +Example inventory file with ipa server and replicas: + +```ini +[ipaserver] +ipaserver.example.com + +[ipareplicas] +ipareplica1.example.com +ipareplica2.example.com + +[ipacluster:children] +ipaserver +ipareplicas + +[ipacluster:vars] +ipaadmin_password=SomeADMINpassword +ipasmartcard_server_ca_certs=/etc/ipa/ca.crt +``` + +Example playbook to setup smartcard for the IPA server using admin password and ipasmartcard_server_ca_certs from inventory file: + +```yaml +--- +- name: Playbook to setup smartcard for IPA server + hosts: ipaserver + become: true + + roles: + - role: ipasmartcard_server + state: present +``` + +Example playbook to setup smartcard for the IPA servers in ipareplicas group using admin password and ipasmartcard_server_ca_certs from inventory file: + +```yaml +--- +- name: Playbook to setup smartcard for IPA replicas + hosts: ipareplicas + become: true + + roles: + - role: ipasmartcard_server + state: present +``` + +Example playbook to setup smartcard for the IPA servers in ipaserver and ipareplicas group using admin password and ipasmartcard_server_ca_certs from inventory file: + +```yaml +--- +- name: Playbook to setup smartcard for IPA server and replicas + hosts: ipaserver, ipareplicas + become: true + + roles: + - role: ipasmartcard_server + state: present +``` + + +Playbooks +========= + +The playbooks needed to setup smartcard for the IPA server and the replicas are part of the repository in the playbooks folder. + +``` +install-smartcard-server.yml +install-smartcard-servers.yml +install-smartcard-replicas.yml +``` + +Please remember to link or copy the playbooks to the base directory of ansible-freeipa if you want to use the roles within the source archive. + + +How to setup smartcard for server +--------------------------------- + +```bash +ansible-playbook -v -i inventory/hosts install-smartcard-server.yml +``` +This will setup the server for smartcard use. + + +How to setup smartcard for replicas +----------------------------------- + +```bash +ansible-playbook -v -i inventory/hosts install-smartcard-replicas.yml +``` +This will setup the replicas for smartcard use. + + +How to setup smartcard for server and replicas +---------------------------------------------- + +```bash +ansible-playbook -v -i inventory/hosts install-smartcard-servers.yml +``` +This will setup the replicas for smartcard use. + + +Variables +========= + +Variable | Description | Required +-------- | ----------- | -------- +`ipaadmin_principal` | The kerberos principal used for admin. Will be set to `admin` if not set. (string) | no +`ipaadmin_password` | The password for the IPA admin user. As an alternative an admin user keytab can be used instead with `ipaadmin_keytab`. (string) | yes +`ipaadmin_keytab` | The admin keytab as an alternative to `ipaadmin_password`. (string) | no +`ipaserver_hostname` | Fully qualified name of the server. By default `ansible_facts['fqdn']` will be used. (string) | no +`ipaserver_domain` | The primary DNS domain of an existing IPA deployment. By default the domain will be used from ipa server-find result. (string) | no +`ipasmartcard_server_ca_certs` | The CA certificates for smartcard use. (list of string) | yes + + +Authors +======= + +Thomas Woerner diff --git a/roles/ipasmartcard_server/defaults/main.yml b/roles/ipasmartcard_server/defaults/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..d852e46ec449832d96bf97d77cf1e9327e5d7950 --- /dev/null +++ b/roles/ipasmartcard_server/defaults/main.yml @@ -0,0 +1,4 @@ +--- +# defaults file for ipasmartcard_server role + +ipaserver_install_packages: yes diff --git a/roles/ipasmartcard_server/files/ipasmartcard_server_add_ca_to_sssd_auth_ca_db.sh b/roles/ipasmartcard_server/files/ipasmartcard_server_add_ca_to_sssd_auth_ca_db.sh new file mode 100644 index 0000000000000000000000000000000000000000..cdb891271d3deadb957a036c240df2480caaa825 --- /dev/null +++ b/roles/ipasmartcard_server/files/ipasmartcard_server_add_ca_to_sssd_auth_ca_db.sh @@ -0,0 +1,30 @@ +#!/bin/bash -eu + +# Authors: +# Thomas Woerner <twoerner@redhat.com> +# +# Copyright (C) 2022 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +cert_file=$1 +db=$2 + +if [ -z "${cert_file}" ] || [ -z "${db}" ]; then + echo "Usage: $0 <ca cert> <db file>" + exit 1 +fi + +cat "${cert_file}" >> "${db}" diff --git a/roles/ipasmartcard_server/files/ipasmartcard_server_add_ca_to_systemwide_db.sh b/roles/ipasmartcard_server/files/ipasmartcard_server_add_ca_to_systemwide_db.sh new file mode 100644 index 0000000000000000000000000000000000000000..6da158019492e184162bfabd4baffade6a5400e5 --- /dev/null +++ b/roles/ipasmartcard_server/files/ipasmartcard_server_add_ca_to_systemwide_db.sh @@ -0,0 +1,31 @@ +#!/bin/bash -eu + +# Authors: +# Thomas Woerner <twoerner@redhat.com> +# +# Copyright (C) 2022 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +cert_file=$1 +db=$2 + +if [ -z "${cert_file}" ] || [ -z "${db}" ]; then + echo "Usage: $0 <ca cert> <db file>" + exit 1 +fi + +uuid=$(uuidgen) +certutil -d "${db}" -A -i "${cert_file}" -n "Smart Card CA ${uuid}" -t CT,C,C diff --git a/roles/ipasmartcard_server/files/ipasmartcard_server_enable_ocsp_directive.sh b/roles/ipasmartcard_server/files/ipasmartcard_server_enable_ocsp_directive.sh new file mode 100644 index 0000000000000000000000000000000000000000..fb4bb32d09b888bea911bbb44de05c23defc8247 --- /dev/null +++ b/roles/ipasmartcard_server/files/ipasmartcard_server_enable_ocsp_directive.sh @@ -0,0 +1,35 @@ +#!/bin/bash -eu + +# Authors: +# Thomas Woerner <twoerner@redhat.com> +# +# Copyright (C) 2022 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +directive=$1 +conf_file=$2 + +if [ -z "${directive}" ] || [ -z "${conf_file}" ]; then + echo "Usage: $0 <directive> <config file>" + exit 1 +fi + +if grep -q "${directive} " "${conf_file}" +then + sed -i.ipabkp -r "s/^#*[[:space:]]*${directive}[[:space:]]+(on|off)$/${directive} on/" "${conf_file}" +else + sed -i.ipabkp "/<\/VirtualHost>/i ${directive} on" "${conf_file}" +fi diff --git a/roles/ipasmartcard_server/files/ipasmartcard_server_mark_httpd_cert_as_trusted.sh b/roles/ipasmartcard_server/files/ipasmartcard_server_mark_httpd_cert_as_trusted.sh new file mode 100644 index 0000000000000000000000000000000000000000..16b529d9b8c58abd3f1a96eb70a414578d294cd0 --- /dev/null +++ b/roles/ipasmartcard_server/files/ipasmartcard_server_mark_httpd_cert_as_trusted.sh @@ -0,0 +1,35 @@ +#!/bin/bash -eu + +# Authors: +# Thomas Woerner <twoerner@redhat.com> +# +# Copyright (C) 2022 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +directive=$1 +nss_conf=$2 +nickname=$3 +alias_dir=$4 + +if [ -z "${directive}" ] || [ -z "${nss_conf}" ] || [ -z "${nickname}" ] || + [ -z "${alias_dir}" ] +then + echo "Usage: $0 <directive> <nss conf> <nickname directive> <alias directory>" + exit 1 +fi + +http_cert_nick=$(grep "${nickname}" "${nss_conf}" | cut -f 2 -d ' ') +certutil -M -n "$http_cert_nick" -d "${alias_dir}" -f "${alias_dir}/pwdfile.txt" -t "Pu,u,u" diff --git a/roles/ipasmartcard_server/library/ipasmartcard_server_get_vars.py b/roles/ipasmartcard_server/library/ipasmartcard_server_get_vars.py new file mode 100644 index 0000000000000000000000000000000000000000..97b1d8c9caa0f35d491f3d6c4d6b35b43052b299 --- /dev/null +++ b/roles/ipasmartcard_server/library/ipasmartcard_server_get_vars.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- + +# Authors: +# Thomas Woerner <twoerner@redhat.com> +# +# Copyright (C) 2022 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.0', + 'supported_by': 'community', + 'status': ['preview'], +} + +DOCUMENTATION = ''' +--- +module: ipasmartcard_server_get_vars +short description: + Get variables from ipaplatform and ipaserver and python interpreter. +description: + Get variables from ipaplatform and ipaserver and python interpreter. +options: +author: + - Thomas Woerner +''' + +EXAMPLES = ''' +- name: Get VARS from IPA + ipasmartcard_server_get_vars: + register: ipasmartcard_server_vars +''' + +RETURN = ''' +NSS_OCSP_ENABLED: + description: + Empty string for newer systems using ssl.conf and not nss.conf for + HTTP instance where OCSP_ENABLED and OCSP_DIRECTIVE are defined in + ipaserver.install.httpinstance, else NSS_OCSP_ENABLED imported from + ipaserver.install.httpinstance. + returned: always + type: str +NSS_OCSP_DIRECTIVE: + description: + Empty string for newer systems using ssl.conf and not nss.conf for + HTTP instance where OCSP_ENABLED and OCSP_DIRECTIVE are defined in + ipaserver.install.httpinstance, else NSSOCSP. + returned: always + type: str +NSS_NICKNAME_DIRECTIVE: + description: + Empty string for newer systems using ssl.conf and not nss.conf for + HTTP instance where OCSP_ENABLED and OCSP_DIRECTIVE are defined in + ipaserver.install.httpinstance, else NSSNickname + returned: always + type: str +OCSP_ENABLED: + description: + OCSP_ENABLED imported from ipaserver.install.httpinstance, if import + succeeds, else "" + returned: always + type: str +OCSP_DIRECTIVE: + description: + OCSP_DIRECTIVE imported from ipaserver.install.httpinstance, if import + succeeds, else "" + returned: always + type: str +HTTPD_SSL_CONF: + description: paths.HTTPD_SSL_CONF from ipaplatform + returned: always + type: str +HTTPD_NSS_CONF: + description: paths.HTTPD_NSS_CONF from ipaplatform + returned: always + type: str +HTTPD_ALIAS_DIR: + description: paths.HTTPD_ALIAS_DIR from ipaplatform + returned: always + type: str +allow_httpd_ifp: + description: + True if sssd_enable_ifp can be imported from ipaclient.install.client, + else false. + returned: always + type: bool +NSS_DB_DIR: + description: paths.NSS_DB_DIR from ipaplatform + returned: always + type: str +USE_AUTHSELECT: + description: True if "AUTHSELECT" is defined in paths + returned: always + type: bool +python_interpreter: + description: Python interpreter from sys.executable + returned: always + type: str +''' + +import sys +from ansible.module_utils.basic import AnsibleModule +from ipaplatform.paths import paths +try: + from ipaserver.install.httpinstance import OCSP_ENABLED, OCSP_DIRECTIVE + NSS_OCSP_ENABLED = "" + NSS_OCSP_DIRECTIVE = "" + NSS_NICKNAME_DIRECTIVE = "" +except ImportError: + from ipaserver.install.httpinstance import NSS_OCSP_ENABLED + NSS_OCSP_DIRECTIVE = "NSSOCSP" + NSS_NICKNAME_DIRECTIVE = "NSSNickname" + OCSP_ENABLED = "" + OCSP_DIRECTIVE = "" +try: + from ipaclient.install.client import sssd_enable_ifp +except ImportError: + sssd_enable_ifp = None + + +def main(): + ansible_module = AnsibleModule( + argument_spec={}, + supports_check_mode=False, + ) + + ansible_module.exit_json(changed=False, + NSS_OCSP_ENABLED=NSS_OCSP_ENABLED, + NSS_OCSP_DIRECTIVE=NSS_OCSP_DIRECTIVE, + NSS_NICKNAME_DIRECTIVE=NSS_NICKNAME_DIRECTIVE, + OCSP_ENABLED=OCSP_ENABLED, + OCSP_DIRECTIVE=OCSP_DIRECTIVE, + HTTPD_SSL_CONF=paths.HTTPD_SSL_CONF, + HTTPD_NSS_CONF=paths.HTTPD_NSS_CONF, + HTTPD_ALIAS_DIR=paths.HTTPD_ALIAS_DIR, + allow_httpd_ifp=sssd_enable_ifp is not None, + NSS_DB_DIR=paths.NSS_DB_DIR, + USE_AUTHSELECT=hasattr(paths, "AUTHSELECT"), + python_interpreter=sys.executable) + + +if __name__ == '__main__': + main() diff --git a/roles/ipasmartcard_server/library/ipasmartcard_server_validate_ca_certs.py b/roles/ipasmartcard_server/library/ipasmartcard_server_validate_ca_certs.py new file mode 100644 index 0000000000000000000000000000000000000000..c89427b13eff34d68132dea1f77093bc98c5dc4c --- /dev/null +++ b/roles/ipasmartcard_server/library/ipasmartcard_server_validate_ca_certs.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- + +# Authors: +# Thomas Woerner <twoerner@redhat.com> +# +# Based on ipa-replica-install code +# +# Copyright (C) 2022 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.0', + 'supported_by': 'community', + 'status': ['preview'], +} + +DOCUMENTATION = ''' +--- +module: ipasmartcard_server_validate_ca_certs +short description: Validate CA certs +description: Validate CA certs +options: + ca_cert_files: + description: + List of files containing CA certificates for the service certificate + files + required: yes +author: + - Thomas Woerner +''' + +EXAMPLES = ''' +''' + +RETURN = ''' +''' + +import os.path +from ansible.module_utils.basic import AnsibleModule +try: + from ipalib import x509 +except ImportError: + x509 = None + + +def main(): + ansible_module = AnsibleModule( + argument_spec=dict( + ca_cert_files=dict(required=False, type='list', default=[]), + ), + supports_check_mode=False, + ) + + # get parameters # + + ca_cert_files = ansible_module.params.get('ca_cert_files') + + # import check # + + if x509 is None: + ansible_module.fail_json(msg="Failed to import x509 from ipalib") + + # validate ca certs # + + if ca_cert_files is not None: + if not isinstance(ca_cert_files, list): + ansible_module.fail_json( + msg="Expected list, got %s" % repr(ca_cert_files)) + # remove duplicates + ca_cert_files = list(dict.fromkeys(ca_cert_files)) + # validate + for cert in ca_cert_files: + if not os.path.exists(cert): + ansible_module.fail_json(msg="'%s' does not exist" % cert) + if not os.path.isfile(cert): + ansible_module.fail_json(msg="'%s' is not a file" % cert) + if not os.path.isabs(cert): + ansible_module.fail_json( + msg="'%s' is not an absolute file path" % cert) + try: + x509.load_certificate_from_file(cert) + except Exception: + ansible_module.fail_json( + msg="'%s' is not a valid certificate file" % cert) + + # exit # + + ansible_module.exit_json(changed=False, + ca_cert_files=ca_cert_files) + + +if __name__ == '__main__': + main() diff --git a/roles/ipasmartcard_server/meta/main.yml b/roles/ipasmartcard_server/meta/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..26ef55df94f3c962e7aab8399895a141698cc019 --- /dev/null +++ b/roles/ipasmartcard_server/meta/main.yml @@ -0,0 +1,22 @@ +--- +dependencies: [] + +galaxy_info: + author: Thomas Woerner + description: A role to setup IPA server(s) for Smart Card authentication + company: Red Hat, Inc + license: GPLv3 + min_ansible_version: 2.8 + platforms: + - name: Fedora + versions: + - all + - name: EL + versions: + - 7 + - 8 + galaxy_tags: + - identity + - ipa + - freeipa + - smartcard diff --git a/roles/ipasmartcard_server/tasks/main.yml b/roles/ipasmartcard_server/tasks/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..c3a89a4e3ba266330328835993dd565e62748edb --- /dev/null +++ b/roles/ipasmartcard_server/tasks/main.yml @@ -0,0 +1,247 @@ +--- +# tasks file for ipasmartcard_server role + +- name: Uninstall smartcard server + ansible.builtin.fail: msg="Uninstalling smartcard for IPA is not supported" + when: state|default('present') == 'absent' + +- name: Import variables specific to distribution + ansible.builtin.include_vars: "{{ item }}" + with_first_found: + - "vars/{{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_version'] }}.yml" + - "vars/{{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_major_version'] }}.yml" + - "vars/{{ ansible_facts['distribution'] }}.yml" + # os_family is used as a fallback for distros which are not currently + # supported, but are based on a supported distro family. For example, + # Oracle, Rocky, Alma and Alibaba linux, which are all "RedHat" based. + - "vars/{{ ansible_facts['os_family'] }}-{{ ansible_facts['distribution_version'] }}.yml" + - "vars/{{ ansible_facts['os_family'] }}-{{ ansible_facts['distribution_major_version'] }}.yml" + - "vars/{{ ansible_facts['os_family'] }}.yml" + # If neither distro nor family is supported, try a default configuration. + - "vars/default.yml" + +- block: + + # CA CERTS + + # Fail on empty "ipasmartcard_server_ca_certs" + - name: Fail on empty "ipasmartcard_server_ca_certs" + ansible.builtin.fail: msg="No CA certs given in 'ipasmartcard_server_ca_certs'" + when: ipasmartcard_server_ca_certs is not defined or + ipasmartcard_server_ca_certs | length < 1 + + # Validate ipasmartcard_server_ca_certs + + - name: Validate CA certs "{{ ipasmartcard_server_ca_certs }}" + ipasmartcard_server_validate_ca_certs: + ca_cert_files: "{{ ipasmartcard_server_ca_certs }}" + register: result_validate_ca_certs + + # INSTALL bind-utils + + - name: Ensure {{ ipasmartcard_server_bindutils_packages }} are installed + ansible.builtin.package: + name: "{{ ipasmartcard_server_bindutils_packages }}" + state: present + when: ipaserver_install_packages | bool + + # KINIT + + - name: Set default principal if not given + ansible.builtin.set_fact: + ipaadmin_principal: admin + when: ipaadmin_principal is undefined + + - name: kinit using "{{ ipaadmin_principal }}" password + ansible.builtin.command: kinit "{{ ipaadmin_principal }}" + args: + stdin: "{{ ipaadmin_password }}" + when: ipaadmin_password is defined + + - name: kinit using "{{ ipaadmin_principal }}" keytab + ansible.builtin.command: kinit -kt "{{ ipaadmin_keytab }}" "{{ ipaadmin_principal }}" + when: ipaadmin_keytab is defined + + # IS MASTER + + - name: Check that this is an IPA master + ansible.builtin.command: ipa server-show --raw "{{ ipaserver_hostname | default(ansible_facts['fqdn']) }}" + register: result_ipa_server_show + + - name: Fail if not an IPA server + ansible.builtin.fail: msg="Not an IPA server" + when: result_ipa_server_show.failed + + - name: Get Domain from server-find server name + ansible.builtin.set_fact: + ipaserver_domain: "{{ (result_ipa_server_show.stdout | regex_search('cn: (.+)', '\\1'))[0].split('.')[1:] | join ('.') }}" + when: ipaserver_domain is not defined + + - name: Get ipa-ca records + ansible.builtin.command: "dig +short ipa-ca.{{ ipaserver_domain }}" + register: result_get_ipaca_records + + - name: Fail if ipa-ca records are not resolvable + ansible.builtin.fail: msg="ipa-ca records are not resolvable" + when: result_get_ipaca_records.failed or + result_get_ipaca_records.stdout | length == 0 + + # GET VARS FROM IPA + + - name: Get VARS from IPA + ipasmartcard_server_get_vars: + register: ipasmartcard_server_vars + + # ENABLE NSS OCSP + + - name: Enable the OCSP directive in nss.conf + ansible.builtin.script: ipasmartcard_server_enable_ocsp_directive.sh + "{{ ipasmartcard_server_vars.NSS_OCSP_DIRECTIVE }}" + "{{ ipasmartcard_server_vars.HTTPD_NSS_CONF }}" + when: ipasmartcard_server_vars.NSS_OCSP_ENABLED | length > 0 + + # MARK NSS HTTPD CERT AS TRUSTED + + - name: Mark HTTPD CERT as trusted + ansible.builtin.script: ipasmartcard_server_mark_httpd_cert_as_trusted.sh + "{{ ipasmartcard_server_vars.NSS_OCSP_DIRECTIVE }}" + "{{ ipasmartcard_server_vars.HTTPD_NSS_CONF }}" + "{{ ipasmartcard_server_vars.NSS_NICKNAME_DIRECTIVE }}" + "{{ ipasmartcard_server_vars.HTTPD_ALIAS_DIR }}" + when: ipasmartcard_server_vars.NSS_OCSP_ENABLED | length > 0 + + # ENABLE SSL OCSP + + - name: Enable the OCSP directive in ssl.conf + ansible.builtin.script: ipasmartcard_server_enable_ocsp_directive.sh + "{{ ipasmartcard_server_vars.OCSP_DIRECTIVE }}" + "{{ ipasmartcard_server_vars.HTTPD_SSL_CONF }}" + when: ipasmartcard_server_vars.OCSP_ENABLED | length > 0 + + # Restart apache + + - name: Restart apache + ansible.builtin.service: + name: httpd + state: restarted + + # RECORD HTTPD OCSP STATUS + + # Store the NSS OCSP upgrade state + + - name: Store NSS OCSP upgrade state + ansible.builtin.command: "{{ ipasmartcard_server_vars.python_interpreter }}" + args: + stdin: | + from ipaserver.install import sysupgrade + sysupgrade.set_upgrade_state("httpd", "{{ ipasmartcard_server_vars.NSS_OCSP_DIRECTIVE }}", True) + when: ipasmartcard_server_vars.NSS_OCSP_ENABLED | length > 0 + + # Store the SSL OCSP upgrade state + + - name: Store SSL OCSP upgrade state + ansible.builtin.command: "{{ ipasmartcard_server_vars.python_interpreter }}" + args: + stdin: | + from ipaserver.install import sysupgrade + sysupgrade.set_upgrade_state("httpd", "{{ ipasmartcard_server_vars.OCSP_DIRECTIVE }}", True) + when: ipasmartcard_server_vars.OCSP_ENABLED | length > 0 + + # check whether PKINIT is configured on the master + + - name: Enable PKINIT + ansible.builtin.command: ipa-pkinit-manage enable + + # Enable OK-AS-DELEGATE flag on the HTTP principal + # This enables smart card login to WebUI + + - name: Enable OK-AS-DELEGATE flag on the HTTP principal + ipaservice: + name: "HTTP/{{ ipaserver_hostname | default(ansible_facts['fqdn']) }}" + ok_to_auth_as_delegate: yes + + # HTTPD IFP + + - block: + + # Allow Apache to access SSSD IFP + + - name: Allow Apache to access SSSD IFP + ansible.builtin.command: "{{ ipasmartcard_server_vars.python_interpreter }}" + args: + stdin: | + import SSSDConfig + from ipaclient.install.client import sssd_enable_ifp + from ipaplatform.paths import paths + c = SSSDConfig.SSSDConfig() + c.import_config() + sssd_enable_ifp(c, allow_httpd=True) + c.write(paths.SSSD_CONF) + when: ipasmartcard_server_vars.OCSP_ENABLED | length > 0 + + # Restart sssd + + - name: Restart sssd + ansible.builtin.service: + name: sssd + state: restarted + + when: ipasmartcard_server_vars.allow_httpd_ifp + + # Ensure /etc/sssd/pki exists + + - block: + - name: Ensure /etc/sssd/pki exists + ansible.builtin.file: + path: /etc/sssd/pki + state: directory + mode: 0711 + + - name: Ensure /etc/sssd/pki/sssd_auth_ca_db.pem is absent + ansible.builtin.file: + path: /etc/sssd/pki/sssd_auth_ca_db.pem + state: absent + + when: ipasmartcard_server_vars.USE_AUTHSELECT + + # Upload smartcard CA certificates to systemwide db + + - name: Upload smartcard CA certificates to systemwide db + ansible.builtin.script: ipasmartcard_server_add_ca_to_systemwide_db.sh + "{{ item }}" + "{{ ipasmartcard_server_vars.NSS_DB_DIR }}" + with_items: "{{ result_validate_ca_certs.ca_cert_files }}" + + # Newer version of sssd use OpenSSL and read the CA certs + # from /etc/sssd/pki/sssd_auth_ca_db.pem + + - name: Add CA certs to /etc/sssd/pki/sssd_auth_ca_db.pem + ansible.builtin.script: ipasmartcard_server_add_ca_to_sssd_auth_ca_db.sh + "{{ item }}" + /etc/sssd/pki/sssd_auth_ca_db.pem + with_items: "{{ result_validate_ca_certs.ca_cert_files }}" + when: ipasmartcard_server_vars.USE_AUTHSELECT + + # Install smartcard signing CA certs + + - name: Install smartcard signing CA certs + ansible.builtin.command: ipa-cacert-manage install "{{ item }}" -t CT,C,C + with_items: "{{ result_validate_ca_certs.ca_cert_files }}" + + # Update ipa CA certificate store + + - name: Update ipa CA certificate store + ansible.builtin.command: ipa-certupdate + + # Restart krb5kdc + + - name: Restart krb5kdc + ansible.builtin.service: + name: krb5kdc + state: restarted + + ### ALWAYS ### + + always: + - name: kdestroy + ansible.builtin.command: kdestroy -A diff --git a/roles/ipasmartcard_server/vars/default.yml b/roles/ipasmartcard_server/vars/default.yml new file mode 100644 index 0000000000000000000000000000000000000000..ff88a90d44992cf5666ab83ecf5416225dd9000f --- /dev/null +++ b/roles/ipasmartcard_server/vars/default.yml @@ -0,0 +1,2 @@ +--- +ipasmartcard_server_bindutils_packages: [ "bind-utils" ]