---
# Configuration for IPA
- name: on rhel8 hosts enable the correct idm module
  copy:
    src: "{{item}}"
    dest: /etc/dnf/modules.d/{{item}}
  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

# TODO: consider switching to https://github.com/freeipa/ansible-freeipa
- name: install needed packages
  package: name={{ item }} state=present
  with_items:
  - haveged
  - ipa-server
  - ipa-server-dns
  - ipa-fas
  - pynag # needed for nagios checks
  tags:
  - ipa/server
  - packages
  when: ansible_distribution_major_version|int <= 8 and ansible_distribution == 'RedHat'

- name: install needed packages
  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
  service: name=haveged state=started enabled=yes
  tags:
  - ipa/server
  - config

- name: Copy LDIF file for working around annoying IPA bug in initial sync
  copy: src=fix_sasl.ldif dest=/usr/share/ipa/fix_sasl.ldif
  tags:
  - ipa/server
  - config

- name: install IPA
  command: 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
           --setup-dns
           --forwarder=10.3.163.33
           --forwarder=10.3.163.34
           --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: install IPA
  command: 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: install IPA vault
  command: ipa-kra-install
           --password={{ipa_dm_password}}
           --unattended
           --log-file=/var/log/ipakrainstall.log
           creates=/var/log/ipakrainstall.log
  tags:
  - ipa/server
  - config
  when: ipa_initial

- name: Create LDIF directory
  file:
    path: /root/ldif
    state: directory
    owner: root
    group: root
    mode: 0750
  tags:
  - ipa/server
  - config

- name: Copy LDIF files
  copy:
    src: "{{item}}"
    dest: /root/ldif/{{item}}
  with_items:
  - grant_anonymous_replication_view.ldif
  - grant_fas_sync.ldif
  - use_id_fp_o.ldif
  - replica-install.ldif
  tags:
  - ipa/server
  - config

  # ipactl status has return code > 0 when the IPA is not set up yet
  # even if the client is already installed
  # Previously we checked in /etc/ipa/default.conf exists, but this
  # is already created in ipa/client role so the replica never
  # started
- name: determine whether we need to set up replication
  shell: ipactl status
  register: replication_status
  ignore_errors: true
  tags:
  - ipa/server
  - config
  when: not ipa_initial

- name: configure replication
  block:
  - name: create replica file
    delegate_to: "{{ipa_server}}"
    command: ipa-replica-prepare
             --password={{ipa_dm_password}}
             {{inventory_hostname}}
             creates=/var/lib/ipa/replica-info-{{inventory_hostname}}.gpg
    when: ansible_distribution_major_version|int < 8

  - name: retrieve replica file
    delegate_to: "{{ipa_server}}"
    fetch: src=/var/lib/ipa/replica-info-{{inventory_hostname}}.gpg
           dest=/tmp/ipa_replica_{{inventory_hostname}}.gpg
           flat=yes
    when: ansible_distribution_major_version|int < 8

  - name: deploy replica file
    copy: src=/tmp/ipa_replica_{{inventory_hostname}}.gpg
          dest=/root/ipa_replica_{{inventory_hostname}}.gpg
          mode=0600 owner=root group=root
    when: ansible_distribution_major_version|int < 8

  - name: destroy replica file on ansible host
    delegate_to: localhost
    file: path=/tmp/ipa_replica_{{inventory_hostname}}.gpg state=absent
    when: ansible_distribution_major_version|int < 8

  - name: deploy replica
    command: ipa-replica-install
             --setup-ca
             --setup-kra
             --password={{ipa_dm_password}}
             --admin-password={{ipa_admin_password}}
             --mkhomedir
             --no-ntp
             --unattended
             --no-ssh
             --no-sshd
             --setup-dns
             --forwarder=10.3.163.33
             --forwarder=10.3.163.34
             --skip-conncheck
             --log-file=/var/log/ipainstall.log
             --dirsrv-config-file=/root/ldif/replica-install.ldif
             /root/ipa_replica_{{inventory_hostname}}.gpg
    when: ansible_distribution_major_version|int < 8

    # 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
    command: ipa-server-install
             --uninstall
             --unattended
    when: ansible_distribution_major_version|int >= 9

  - name: deploy replica
    command: ipa-replica-install
             --setup-ca
             --admin-password={{ipa_admin_password}}
             --no-host-dns
             --mkhomedir
             --no-ntp
             --unattended
             --no-ssh
             --no-sshd
             --skip-conncheck
             --force-join
             --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
  when: not ipa_initial and replication_status.rc > 0
  tags:
  - ipa/server
  - config

- name: Disable rewrites
  template: src=ipa-rewrite.conf dest=/etc/httpd/conf.d/ipa-rewrite.conf
  notify:
  - reload httpd
  tags:
  - ipa/server
  - config

- name: Disable the compat tree
  shell: 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
  shell: 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
  ipauser:
    name: admin
    password: "{{ ipa_admin_password }}"
    # Password expiration date will be a Friday 13th in 30 years. I'm sure we'll remember that.
    passwordexpiration: "2050-05-13 00:00:00"
    update_password: on_create
    ipaadmin_password: "{{ ipa_admin_password }}"
  tags:
  - ipa/server
  - config

- name: Get admin ticket
  shell: echo "{{ipa_admin_password}}" | kinit admin
  tags:
  - ipa/server
  - config
  - krb5

# 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
#   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
#   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
  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
  ipapwpolicy:
    minlife: 0
    maxlife: 0
    history: 0
    minclasses: 0
    minlength: 0
    maxfail: 0
    ipaadmin_password: "{{ ipa_admin_password }}"
  tags:
  - ipa/server
  - config

- name: Create fas_sync user
  ipauser:
    name: fas_sync
    givenname: FAS
    sn: Sync
    userclass: system
    ipaadmin_password: "{{ ipa_admin_password }}"
  tags:
  - ipa/server
  - config

# Certificate generation
- name: Make a directory to store certificate profiles
  file:
    path: /etc/ipa/certprofiles
    state: directory
  tags:
  - ipa/server
  - config

- name: Warn admins that this is not the canonical source
  copy:
    dest: /etc/ipa/certprofiles/README
    content: "This is just a dump of the server values, which are accessible with ipa certprofile-find"
  tags:
  - ipa/server
  - config

- name: Copy the certificate profile for users
  template:
    src: userCerts.conf
    dest: /etc/ipa/certprofiles/userCerts.conf
  tags:
  - ipa/server
  - config

- name: Create the certificate profile
  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
  command:
    argv:
      - ipa
      - certprofile-mod
      - userCerts
      - --desc=Profile for user certificates
      - --store=true
      - --file=/etc/ipa/certprofiles/userCerts.conf
  tags:
  - ipa/server
  - config
  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
  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
  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
  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
  command: ipa hbacrule-disable allow_all
  tags:
  - ipa/server
  - config
  register: output

# Noggin user setup

- name: Register the proper noggin admin password
  set_fact:
    noggin_password: "{{ (env == 'production')|ternary(noggin_admin_password, noggin_stg_admin_password) }}"
  tags:
  - ipa/server
  - config

- name: Create noggin user
  ipauser:
    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)
    passwordexpiration: "2050-05-13 00:00:00"
    update_password: on_create
    userclass: system
    ipaadmin_password: "{{ ipa_admin_password }}"
  tags:
  - ipa/server
  - config

- name: Create the noggin privilege
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  tags:
  - ipa/server
  - config

- name: Destroy admin ticket
  command: kdestroy -A
  tags:
  - ipa/server
  - config
  - krb5


- import_tasks: scripts.yml


# User groups

- name: Set the members of the admin group
  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
  ipagroup:
    ipaadmin_password: "{{ ipa_admin_password }}"
    name: sysadmin-main
    description: Fedora Main Sysadmin Group
  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
  command: 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 and 'modifying entry' not in grant_repl_status_output.stdout"

# Make some httpd changes
- name: Configure referer override
  template: src=referer-override.conf
            dest=/etc/httpd/conf.d/referer-override.conf
  notify:
  - reload apache
  tags:
  - ipa/server
  - config

- name: Update xmlrpc_uri
  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
  service:
    name: httpd
    enabled: no
  tags:
  - ipa/server
  - config

- name: Set cron for daily data only backups
  copy:
    src: data-only-backup.sh
    dest: "/etc/cron.daily/data-only-backup.sh"
    mode: 0755
  tags:
  - ipa/server
  - config

- name: Copy sweeper script to /usr/local/bin/
  copy:
    src: sweeper.py
    dest: /usr/local/bin/sweeper
    mode: 0755
  tags:
    - ipa/server
    - config

- name: Set sweeper script on a cron schedule
  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
