--- # Configuration for IPA - name: On rhel8 hosts enable the correct idm module ansible.builtin.copy: src: "{{ item }}" dest: /etc/dnf/modules.d/{{ item }} mode: "0644" with_items: - 389-ds.module - idm.module - pki-core.module - pki-deps.module when: ansible_distribution_major_version|int >= 8 and ansible_distribution == 'RedHat' tags: - ipa/server - config - name: Install needed packages ansible.builtin.package: name: "{{ item }}" state: present with_items: - haveged - ipa-server - ipa-server-dns - ipa-fas tags: - ipa/server - packages when: ansible_distribution_major_version|int >= 9 and ansible_distribution == 'RedHat' # TODO: need pynag for monitoring, not yet in rhel9. - name: Enable haveged ansible.builtin.service: name: haveged state: started enabled: yes tags: - ipa/server - config - name: Copy LDIF file for working around annoying IPA bug in initial sync ansible.builtin.copy: src: fix_sasl.ldif dest: /usr/share/ipa/fix_sasl.ldif mode: "0644" tags: - ipa/server - config - name: Install IPA ansible.builtin.command: argv: - ipa-server-install - --realm={{ ipa_realm }} - --domain={{ ipa_realm }} - --ds-password={{ ipa_dm_password }} - --admin-password={{ ipa_admin_password }} - --mkhomedir - --no-ntp - --unattended - --no-ssh - --no-sshd - --log-file=/var/log/ipainstall.log creates: /etc/ipa/default.conf tags: - ipa/server - config when: > ipa_initial and ansible_distribution_major_version|int > 8 and ansible_distribution == 'RedHat' - name: Create LDIF directory ansible.builtin.file: path: /root/ldif state: directory owner: root group: root mode: "0750" tags: - ipa/server - config - name: Copy LDIF files ansible.builtin.copy: src: "{{ item }}" dest: /root/ldif/{{ item }} mode: "0644" with_items: - grant_anonymous_replication_view.ldif - grant_fas_sync.ldif - use_id_fp_o.ldif - replica-install.ldif tags: - ipa/server - config # During the ipa-replica-install /var/log/ipainstall.log is created # Let's check if the file is available and prevent breaking replica # by running ipa-replica-install more than once - name: Check if /var/log/ipainstall.log is available ansible.builtin.stat: path: /var/log/ipainstall.log register: check_replica tags: - ipa/server - config # If you want to re-install replica again, just remove # /var/log/ipainstall.log on the machine you want to re-install # and this block will do the rest # In case you want to re-install master node you need to follow # IPA documentation as that is not straighforward - name: Configure replication when: - not ipa_initial - not check_replica.stat.exists tags: - ipa/server - config block: # The ipa-client-install makes the ipa-replica-install fail # on RHEL 9 with: "Your system is partly configured." # This will clean the previous installation and allows # the replica to be deployed. - name: Clean client installation ansible.builtin.command: argv: - ipa-server-install - --uninstall - --unattended changed_when: true when: ansible_distribution_major_version|int >= 9 - name: Get admin ticket on ipa master ansible.builtin.shell: set -o pipefail && echo "{{ ipa_admin_password }}" | kinit admin delegate_to: "{{ ipa_server }}" changed_when: false no_log: true # Replication agreement needs to be removed from ipa cluster # before installing the replica - name: Remove the replication agreement for the replica ansible.builtin.command: "ipa server-del --force {{ inventory_hostname }}" delegate_to: "{{ ipa_server }}" changed_when: true - name: Deploy replica ansible.builtin.command: argv: - ipa-replica-install - --setup-ca - --admin-password={{ ipa_admin_password }} - --no-host-dns - --mkhomedir - --no-ntp - --unattended - --no-ssh - --no-sshd - --log-file=/var/log/ipainstall.log - --domain={{ ipa_realm }} - --server={{ ipa_server }} - --dirsrv-config-file=/root/ldif/replica-install.ldif when: ansible_distribution_major_version|int >= 8 changed_when: true - name: Disable rewrites ansible.builtin.template: src: ipa-rewrite.conf dest: /etc/httpd/conf.d/ipa-rewrite.conf mode: "0644" notify: - Reload httpd tags: - ipa/server - config - ipa/rewrites - name: Disable the compat tree ansible.builtin.shell: | set -o pipefail echo "{{ ipa_dm_password }}" | ipa-compat-manage disable tags: - ipa/server - config register: output changed_when: "'Disabling plugin' in output.stdout" failed_when: "'Plugin is already disabled' not in output.stdout and output.rc != 0" notify: - Restart ipa - name: Disable the nis tree ansible.builtin.shell: | set -o pipefail echo "{{ ipa_dm_password }}" | ipa-nis-manage disable tags: - ipa/server - config register: output changed_when: "'Disabling plugin' in output.stdout" failed_when: "'Plugin is already disabled' not in output.stdout and output.rc != 0" notify: - Restart ipa - name: Set the expiration date for the admin user community.general.ipa_user: name: admin password: "{{ ipa_admin_password }}" # Password expiration date will be a Friday 13th in 30 years. I'm sure we'll remember that. krbpasswordexpiration: "20500513000000" update_password: on_create ipa_pass: "{{ ipa_admin_password }}" ipa_host: "{{ inventory_hostname }}" tags: - ipa/server - config - name: Get admin ticket ansible.builtin.shell: | set -o pipefail echo "{{ ipa_admin_password }}" | kinit admin changed_when: false no_log: true tags: - ipa/server - config - krb5 - toddlers # Reason for removing the next task: we don't store so much private information # now, and we can't disallow people from seeing other people's email address on # a case-by-case basis, it's either everyone or hand-picked services, but users # can't choose to let other users see their info or not. # # - name: Disable default permissions so we don't break our privacy policy # ansible.builtin.command: # argv: # - ipa # - permission-mod # - System: Read User Addressbook Attributes # - --bindtype=permission # tags: # - ipa/server # - config # register: output # changed_when: "'Modified permission' in output.stdout" # failed_when: "'no modifications to be performed' not in output.stderr and output.rc != 0" # # # Because of the previous task, we must explicitely allow users to read their own data # - name: Allow users to read their own data # ansible.builtin.command: # argv: # - ipa # - selfservice-add # - "Users can read their own addressbook attributes" # - --permissions=read # - --attrs=mail # - --attrs=userCertificate # - --attrs=ipaCertmapData # tags: # - ipa/server # - config # register: output # changed_when: "'Added selfservice' in output.stdout" # failed_when: "'already exists' not in output.stderr and output.rc != 0" # Set the default value back - name: Restore the default permission on user addressbook attributes ansible.builtin.command: argv: - ipa - permission-mod - "System: Read User Addressbook Attributes" - --bindtype=all tags: - ipa/server - config register: output changed_when: "'Modified permission' in output.stdout" failed_when: "'no modifications to be performed' not in output.stderr and output.rc != 0" - name: Configure password policy community.general.ipa_pwpolicy: minpwdlife: 0 maxpwdlife: 0 historylength: 0 minclasses: 0 minlength: 0 maxfailcount: 0 ipa_pass: "{{ ipa_admin_password }}" ipa_host: "{{ inventory_hostname }}" tags: - ipa/server - config - name: Create fas_sync user community.general.ipa_user: name: fas_sync givenname: FAS sn: Sync ipa_pass: "{{ ipa_admin_password }}" ipa_host: "{{ inventory_hostname }}" tags: - ipa/server - config # Certificate generation - name: Make a directory to store certificate profiles ansible.builtin.file: path: /etc/ipa/certprofiles state: directory mode: "0755" tags: - ipa/server - config - name: Warn admins that this is not the canonical source ansible.builtin.copy: dest: /etc/ipa/certprofiles/README content: > "This is just a dump of the server values, which are accessible with ipa certprofile-find" mode: "0644" tags: - ipa/server - config - name: Copy the certificate profile for users ansible.builtin.template: src: userCerts.conf dest: /etc/ipa/certprofiles/userCerts.conf mode: "0644" tags: - ipa/server - config - name: Create the certificate profile ansible.builtin.command: argv: - ipa - certprofile-import - userCerts - --desc=Profile for user certificates - --store=true - --file=/etc/ipa/certprofiles/userCerts.conf tags: - ipa/server - config register: create_output changed_when: "'already exists' not in create_output.stderr" failed_when: "'already exists' not in create_output.stderr and create_output.rc != 0" - name: Update the certificate profile ansible.builtin.command: argv: - ipa - certprofile-mod - userCerts - --desc=Profile for user certificates - --store=true - --file=/etc/ipa/certprofiles/userCerts.conf tags: - ipa/server - config changed_when: true when: "ipa_initial and 'already exists' in create_output.stderr" # Create a new ACL linking the new profile and ipausers group (that all users are members of) - name: Create the CA ACL for the new certificate profile ansible.builtin.command: ipa caacl-add userCerts tags: - ipa/server - config register: output changed_when: "'already exists' not in output.stderr" failed_when: "'already exists' not in output.stderr and output.rc != 0" - name: Add the ipausers group to the CA ACL ansible.builtin.command: ipa caacl-add-user userCerts --group ipausers tags: - ipa/server - config register: output changed_when: "'is already a member' not in output.stdout" failed_when: "'is already a member' not in output.stdout and output.rc != 0" - name: Add the ipausers group to the CA ACL ansible.builtin.command: ipa caacl-add-profile userCerts --certprofile userCerts tags: - ipa/server - config register: output changed_when: "'is already a member' not in output.stdout" failed_when: "'is already a member' not in output.stdout and output.rc != 0" # HBAC - name: Don't allow all users to log into all hosts ansible.builtin.command: ipa hbacrule-disable allow_all changed_when: true tags: - ipa/server - config register: output # Noggin user setup - name: Register the proper noggin admin password ansible.builtin.set_fact: noggin_password: > "{{ (env == 'production') | ternary(noggin_admin_password, noggin_stg_admin_password) }}" tags: - ipa/server - config - name: Create noggin user community.general.ipa_user: name: noggin givenname: Noggin sn: User password: > "{{ (env == 'production') | ternary(noggin_admin_password, noggin_stg_admin_password) }}" # Password expiration date will be a Friday 13th in 30 years. I'm sure we'll remember that. # (if unset, IPA will assume the password is expired because it hasn't been set by the user # themselves) krbpasswordexpiration: "20500513000000" update_password: on_create ipa_pass: "{{ ipa_admin_password }}" ipa_host: "{{ inventory_hostname }}" tags: - ipa/server - config - name: Create the noggin privilege ansible.builtin.command: argv: - ipa - privilege-add - Self-service Portal Administrators - --desc=Noggin admin users tags: - ipa/server - config register: output changed_when: "'already exists' not in output.stderr" failed_when: "'already exists' not in output.stderr and output.rc != 0" - name: Setup the noggin privilege ansible.builtin.command: argv: - ipa - privilege-add-permission - Self-service Portal Administrators - "--permissions=System: Modify Users" - "--permissions=System: Change User password" - "--permissions=System: Add Stage User" - "--permissions=System: Read Stage Users" - "--permissions=System: Modify Stage User" - "--permissions=System: Modify User RDN" - "--permissions=System: Remove Stage User" - "--permissions=System: Add Users" - "--permissions=System: Add User to default group" tags: - ipa/server - config register: output changed_when: "'Number of permissions added 0' not in output.stdout" failed_when: "'Number of permissions added 0' not in output.stdout and output.rc != 0" - name: Create the noggin role community.general.ipa_role: name: "Self-service Portal Administrator" description: "Noggin admin user" privilege: - "Self-service Portal Administrators" user: - noggin ipa_host: "{{ inventory_hostname }}" ipa_user: admin ipa_pass: "{{ ipa_admin_password }}" validate_certs: no tags: - ipa/server - config # User selfservice permissions - name: Setup the selfservice permission for passwords # When ansible-freeipa is upgraded, we'll get ipaselfservice # ipaselfservice: # ipaadmin_password: "{{ipa_admin_password}}" # name: "Users can modify their own password" # permission: write # attribute: # - userPassword # - krbPrincipalKey # - sambaLMPassword # - sambaNTPassword ansible.builtin.command: argv: - ipa - selfservice-add - "Users can modify their own password" - --permissions=write - --attrs=userPassword - --attrs=krbPrincipalKey - --attrs=sambaLMPassword - --attrs=sambaNTPassword register: output changed_when: "'Added selfservice' in output.stdout" failed_when: "'already exists' not in output.stderr and output.rc != 0" tags: - ipa/server - config - name: Setup the selfservice permission for addressbook attributes # When ansible-freeipa is upgraded, we'll get ipaselfservice # ipaselfservice: # ipaadmin_password: "{{ipa_admin_password}}" # name: "User Self service" # permission: write # attribute: # - givenname # - sn # - cn # - displayname # - gecos ansible.builtin.command: argv: - ipa - selfservice-add - "User Self service" - --permissions=write - --attrs=givenName - --attrs=sn - --attrs=cn - --attrs=displayName - --attrs=gecos register: output changed_when: "'Added selfservice' in output.stdout" failed_when: "'already exists' not in output.stderr and output.rc != 0" tags: - ipa/server - config # Let people in the sysadmin-main group manage registering users (Stage Users) # through Noggin: - name: Create the stage users managers privilege ansible.builtin.command: argv: - ipa - privilege-add - Stage User Managers - --desc=Manage registering users in Noggin tags: - ipa/server - config register: output changed_when: "'already exists' not in output.stderr" failed_when: "'already exists' not in output.stderr and output.rc != 0" - name: Setup the stage users managers privilege ansible.builtin.command: argv: - ipa - privilege-add-permission - Stage User Managers - "--permissions=System: Read Stage Users" - "--permissions=System: Modify Stage User" - "--permissions=System: Remove Stage User" tags: - ipa/server - config register: output changed_when: "'Number of permissions added 0' not in output.stdout" failed_when: "'Number of permissions added 0' not in output.stdout and output.rc != 0" - name: Create the stage users managers role community.general.ipa_role: name: "Stage User Managers" description: "Manage registering users in Noggin" privilege: - "Stage User Managers" group: - sysadmin-main ipa_host: "{{ inventory_hostname }}" ipa_user: admin ipa_pass: "{{ ipa_admin_password }}" validate_certs: no tags: - ipa/server - config # Add the missing topology segments that are missed during replication # Ignore any failure as that means that segment is already in place - name: Add the missing segments for ca suffix ansible.builtin.command: argv: - ipa - topologysegment-add - "--leftnode={{ inventory_hostname }}" - "--rightnode={{ item }}" - "ca" - "{{ inventory_hostname + '-' + item }}" with_items: "{{ (env == 'production') | ternary(groups['ipa'], groups['ipa_stg']) }}" ignore_errors: true # noqa ignore-errors register: output changed_when: "'Segment already exists' not in output.stdout" tags: - ipa/server - config # Add the missing topology segments that are missed during replication # Ignore any failure as that means that segment is already in place - name: Add the missing segments for domain suffix ansible.builtin.command: argv: - ipa - topologysegment-add - "--leftnode={{ inventory_hostname }}" - "--rightnode={{ item }}" - "domain" - "{{ inventory_hostname + '-' + item }}" with_items: "{{ (env == 'production') | ternary(groups['ipa'], groups['ipa_stg']) }}" ignore_errors: true # noqa ignore-errors register: output changed_when: "'Segment already exists' not in output.stdout" tags: - ipa/server - config - name: Include toddlers setup ansible.builtin.import_tasks: toddlers.yml tags: - ipa/server - config - toddlers - name: Destroy admin ticket ansible.builtin.command: kdestroy -A changed_when: true tags: - ipa/server - config - krb5 - name: Copy the new krb5 logrotate config ansible.builtin.copy: src: logrotate_krb5kdc dest: /etc/logrotate.d/krb5kdc mode: '0644' backup: yes tags: - ipa/server - config - krb5 - name: Include script.yml ansible.builtin.import_tasks: scripts.yml - name: Include journal-to-fedora-messaging setup ansible.builtin.import_tasks: journal2fedmsg.yml tags: - ipa/server - config # User groups - name: Set the members of the admin group community.general.ipa_group: name: admins user: - admin - fas_sync - arrfab ipa_host: "{{ inventory_hostname }}" ipa_user: admin ipa_pass: "{{ ipa_admin_password }}" validate_certs: no tags: - ipa/server - config - name: Create the sysadmin-main group community.general.ipa_group: ipa_pass: "{{ ipa_admin_password }}" name: sysadmin-main description: Fedora Main Sysadmin Group ipa_host: "{{ inventory_hostname }}" tags: - ipa/server - config # This is a special one, in that it needs to apply on each master since it's non-replicated. - name: Grant access to replication status ansible.builtin.command: argv: - /usr/bin/ldapmodify - -Y - EXTERNAL - -H - "{{ ipa_ldap_socket }}" - -f - "/root/ldif/{{ item }}" with_items: - grant_anonymous_replication_view.ldif - grant_fas_sync.ldif - use_id_fp_o.ldif tags: - ipa/server - config register: grant_repl_status_output changed_when: "'Type or value exists' not in grant_repl_status_output.stderr" failed_when: - "'Type or value exists' not in grant_repl_status_output.stderr" - "'modifying entry' not in grant_repl_status_output.stdout" # Make some httpd changes - name: Configure referer override ansible.builtin.template: src: referer-override.conf dest: /etc/httpd/conf.d/referer-override.conf mode: "0644" notify: - Reload apache tags: - ipa/server - config - name: Update xmlrpc_uri ansible.builtin.lineinfile: dest: /etc/ipa/default.conf regexp: 'xmlrpc_uri =' line: 'xmlrpc_uri = https://{{ inventory_hostname }}/ipa/xml' tags: - ipa/server - config # The httpd service should not be started with systemd, the ipa service will # start it. If systemd starts it, it will run before IPA is available and # KdcProxy will be disabled because it can't reach LDAP. - name: Disable the httpd service ansible.builtin.service: name: httpd enabled: no tags: - ipa/server - config - name: Set cron for daily data only backups ansible.builtin.copy: src: data-only-backup.sh dest: "/etc/cron.daily/data-only-backup.sh" mode: "0755" tags: - ipa/server - config # This has group=zabbix so that the monitoring can check the backups ran - name: Create IPA backup directory ansible.builtin.file: path: /var/lib/ipa/backup state: directory owner: root group: zabbix mode: "0750" tags: - ipa/server - config - name: Copy sweeper script to /usr/local/bin/ ansible.builtin.copy: src: sweeper.py dest: /usr/local/bin/sweeper mode: "0755" tags: - ipa/server - config - name: Set sweeper script on a cron schedule ansible.builtin.cron: name: "clean up mod_auth_gssapi tokens" hour: "3" minute: "0" user: root job: "/usr/local/bin/sweeper -g /run/ipa/ccaches/ > /dev/null" tags: - ipa/server - config # Zabbix monitoring of the internal IPA server - name: Install Zabbix agent config drop-in ansible.builtin.copy: src: zabbix/agent-ipa-backup.conf dest: /etc/zabbix/zabbix_agentd.d/ipa-backup.conf mode: '0644' notify: - Restart zabbix agent tags: - ipa/server - zabbix_agent - name: Zabbix API Block vars: ansible_zabbix_auth_key: "{{ zabbix_auth_key }}" ansible_network_os: "{{ zabbix_network_os }}" ansible_connection: "{{ zabbix_connection }}" ansible_httpapi_port: "{{ zabbix_httpapi_port }}" ansible_httpapi_use_ssl: "{{ zabbix_httpapi_use_ssl }}" ansible_httpapi_validate_certs: "{{ zabbix_httpapi_validate_certs }}" ansible_host: "{{ zabbix_server }}" ansible_zabbix_url_path: "{{ zabbix_url_path }}" tags: - ipa/server - zabbix_api block: - name: Import IPA template file community.zabbix.zabbix_template: template_yaml: "{{ lookup('file', 'zabbix/template-ipa.yml') }}" state: present - name: Add self to IPA template in Zabbix community.zabbix.zabbix_host: host_name: "{{ inventory_hostname }}" link_templates: IPA Monitoring force: false