--- - name: Install Koji Hub, Web, httpd, and crypto packages ansible.builtin.dnf: name: - koji-hub - koji-web - httpd - mod_ssl - mod_wsgi - python3-cryptography - nmap-ncat - postgresql - policycoreutils-python-utils - koji-utils - createrepo_c - tree - python3-koji-hub-plugins state: present - name: Create directories for certs and client configs ansible.builtin.file: path: "{{ item }}" state: directory owner: apache group: apache mode: "0700" loop: - "{{ koji_top_dir }}/certs" - "{{ koji_top_dir }}/clients" - "{{ koji_top_dir }}/packages" - "{{ koji_top_dir }}/repos" - "{{ koji_top_dir }}/work" - "{{ koji_top_dir }}/scratch" - "{{ koji_top_dir }}/repos-dist" - name: Create kojira group ansible.builtin.group: name: kojira state: present - name: Create kojira system user ansible.builtin.user: name: kojira group: kojira home: /var/lib/kojira shell: /sbin/nologin system: true state: present - name: Ensure admin user exists ansible.builtin.user: name: "{{ koji_admin_user }}" state: present create_home: true - name: Add kojiadmin to wheel group for sudo access ansible.builtin.user: name: "{{ koji_admin_user }}" groups: wheel append: true state: present # This is for demonstration purposes only. In a production environment, # it is recommended to configure sudo access with a more restrictive policy. - name: Ensure wheel group has NOPASSWD sudo access ansible.builtin.lineinfile: path: /etc/sudoers regexp: '^# %wheel\s+ALL=\(ALL\)\s+NOPASSWD:\s+ALL$' line: '%wheel ALL=(ALL) NOPASSWD: ALL' validate: 'visudo -cf %s' state: present - name: Ensure .koji directory exists for '{{ koji_admin_user }}' ansible.builtin.file: path: "{{ koji_hub_admin_user_koji_dir }}" state: directory owner: "{{ koji_admin_user }}" group: "{{ koji_admin_user }}" mode: "{{ koji_client_dir_mode }}" - name: Configure Koji client for admin user ansible.builtin.template: src: koji.conf.j2 dest: "{{ koji_hub_admin_user_koji_dir }}/config" owner: "{{ koji_admin_user }}" group: "{{ koji_admin_user }}" mode: "{{ koji_client_file_mode }}" - name: Copy client certificates for '{{ koji_admin_user }}' ansible.builtin.copy: src: "{{ item.src }}" dest: "{{ koji_hub_admin_user_koji_dir }}/{{ item.dest }}" owner: "{{ koji_admin_user }}" group: "{{ koji_admin_user }}" mode: "{{ koji_client_file_mode }}" loop: - src: "{{ koji_pki_dir }}/kojiadmin.pem" dest: client.pem - src: "{{ koji_pki_dir }}/kojiadmin.key" dest: client.key - src: "{{ koji_pki_dir }}/{{ koji_ca_name }}_cert.crt" dest: clientca.crt - src: "{{ koji_pki_dir }}/{{ koji_ca_name }}_cert.crt" dest: serverca.crt - name: Create Koji PKI directory on the hub ansible.builtin.file: path: "{{ koji_remote_pki_dir }}" state: directory owner: root group: root mode: "0755" - name: Copy Koji certificates to the hub ansible.builtin.copy: src: "{{ koji_pki_dir }}/{{ item.src }}" dest: "{{ item.dest }}" owner: root group: root mode: "{{ item.mode }}" loop: - { src: "{{ koji_ca_name }}_cert.crt", dest: "{{ koji_remote_ca_trust_path }}", mode: "0644" } - { src: "kojihub.crt", dest: "{{ koji_hub_cert_path }}", mode: "0644", group: "apache" } - { src: "kojihub.key", dest: "{{ koji_hub_key_path }}", mode: "0640", group: "apache" } - { src: "kojihub.pem", dest: "/etc/pki/koji/kojihub.pem", mode: "0644", group: "apache" } - { src: "kojiadmin.pem", dest: "{{ koji_admin_cert_path }}", mode: "0644" } - { src: "kojira.pem", dest: "/etc/pki/koji/kojira.pem", mode: "0640", group: "kojira" } - { src: "kojiweb.pem", dest: "/etc/pki/koji/kojiweb.pem", mode: "0640", group: "apache" } - name: Update CA trust store ansible.builtin.command: update-ca-trust changed_when: false - name: Flush handlers ansible.builtin.meta: flush_handlers - name: Create Koji Hub and Web config directories ansible.builtin.file: path: "{{ item }}" state: directory owner: root group: root mode: "0755" loop: - "{{ koji_hub_config_dir }}" - "{{ koji_hub_koji_web_config_dir }}" - name: Configure httpd for Koji ansible.builtin.lineinfile: path: "{{ item.path }}" regexp: "{{ item.regexp }}" line: "{{ item.line }}" backup: true loop: - { path: "{{ koji_hub_httpd_ssl_config_file }}", regexp: "^SSLCertificateFile", line: "SSLCertificateFile {{ koji_hub_cert_path }}" } - { path: "{{ koji_hub_httpd_ssl_config_file }}", regexp: "^SSLCertificateKeyFile", line: "SSLCertificateKeyFile {{ koji_hub_key_path }}" } - { path: "{{ koji_hub_httpd_ssl_config_file }}", regexp: "^#?SSLCACertificateFile", line: "SSLCACertificateFile {{ koji_remote_ca_trust_path }}" } notify: Restart httpd - name: Enable SSL for Koji Hub ansible.builtin.blockinfile: path: "{{ koji_hub_httpd_config_file }}" marker: "# {mark} ANSIBLE MANAGED BLOCK" block: | SSLVerifyClient require SSLVerifyDepth 10 SSLOptions +StdEnvVars notify: Restart httpd - name: Template Koji Hub and Web configs ansible.builtin.template: src: "{{ item.src }}" dest: "{{ item.dest }}" group: apache mode: "0640" loop: - { src: "00-main.conf.j2", dest: "{{ koji_hub_config_dir }}/00-main.conf" } - { src: "db.conf.j2", dest: "{{ koji_hub_config_dir }}/db.conf" } - { src: "admin.conf.j2", dest: "{{ koji_hub_config_dir }}/admin.conf" } - { src: "web.conf.j2", dest: "{{ koji_hub_koji_web_config_dir }}/web.conf" } - { src: "policies.conf.j2", dest: "{{ koji_hub_config_dir }}/policies.conf" } - { src: "99-local-policies.conf.j2", dest: "{{ koji_hub_config_dir }}/99-local-policies.conf" } notify: Restart httpd - name: Set SELinux booleans for Apache ansible.posix.seboolean: name: "{{ item }}" state: true persistent: true loop: - httpd_can_network_connect_db - httpd_can_network_connect - allow_httpd_anon_write - httpd_use_nfs notify: Restart httpd - name: Configure httpd MaxConnectionsPerChild to prevent memory leaks ansible.builtin.lineinfile: path: /etc/httpd/conf/httpd.conf regexp: '^#?\s*MaxConnectionsPerChild\s+\d+' line: 'MaxConnectionsPerChild 100' state: present backup: true notify: Restart httpd - name: Start and enable httpd ansible.builtin.service: name: httpd state: started enabled: true - name: Open firewall for http/https ansible.posix.firewalld: service: "{{ item }}" permanent: true state: enabled loop: ["http", "https"] notify: Reload firewalld - name: Set SELinux context for Koji topdir ansible.builtin.command: semanage fcontext -a -t httpd_sys_rw_content_t "/mnt/koji(/.*)?" changed_when: false - name: Apply SELinux context ansible.builtin.command: restorecon -r -v /mnt/koji changed_when: false - name: Add database host to /etc/hosts ansible.builtin.lineinfile: path: /etc/hosts line: "{{ hostvars[groups['kojidb'][0]].ansible_default_ipv4.address }} {{ koji_db_host }}" regexp: ".*{{ koji_db_host }}" state: present - name: Add hub host to /etc/hosts ansible.builtin.lineinfile: path: /etc/hosts line: "{{ ansible_default_ipv4.address }} {{ koji_hub_fqdn }}" regexp: ".*{{ koji_hub_fqdn }}" state: present - name: Wait for database to be available ansible.builtin.wait_for: host: "{{ koji_db_host }}" port: 5432 timeout: 120 - name: Check if Koji DB schema is loaded community.postgresql.postgresql_query: login_db: "{{ koji_db_name }}" query: "SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'users');" login_host: "{{ koji_db_host }}" login_user: "{{ koji_db_user }}" login_password: "{{ koji_db_password }}" register: koji_hub_schema_check changed_when: false - name: Load Koji DB Schema community.postgresql.postgresql_script: db: "{{ koji_db_name }}" path: /usr/share/koji/schema.sql login_host: "{{ koji_db_host }}" login_user: "{{ koji_db_user }}" login_password: "{{ koji_db_password }}" when: not koji_hub_schema_check.query_result[0].exists - name: Create kojiadmin user in database community.postgresql.postgresql_query: login_db: "{{ koji_db_name }}" query: "INSERT INTO users (name, status, usertype) VALUES ('{{ koji_admin_user }}', 0, 0) ON CONFLICT DO NOTHING" login_host: "{{ koji_db_host }}" login_user: "{{ koji_db_user }}" login_password: "{{ koji_db_password }}" - name: Configure Koji client via ansible-settings.conf ansible.builtin.template: src: koji.conf.j2 dest: "{{ koji_hub_koji_client_config_file }}" group: apache mode: "0644" - name: Grant admin privileges to kojiadmin user community.postgresql.postgresql_query: login_db: "{{ koji_db_name }}" query: > INSERT INTO user_perms (user_id, perm_id, creator_id) VALUES ( (SELECT id FROM users WHERE name = '{{ koji_admin_user }}'), (SELECT id FROM permissions WHERE name = 'admin'), (SELECT id FROM users WHERE name = '{{ koji_admin_user }}') ) ON CONFLICT DO NOTHING login_host: "{{ koji_db_host }}" login_user: "{{ koji_db_user }}" login_password: "{{ koji_db_password }}" - name: Configure Koji Hub Main Options (hub.conf) ansible.builtin.template: src: hub.conf.j2 dest: /etc/koji-hub/hub.conf owner: root group: root mode: "0644" notify: Restart httpd - name: Copy Sigul signing plugin to Koji Hub plugins directory ansible.builtin.copy: src: sigul_sign.py dest: /usr/lib/koji-hub-plugins/sigul_sign.py owner: root group: root mode: "0644" notify: Restart httpd - name: Create spool directories for Koji signing ansible.builtin.file: path: "{{ item }}" state: directory owner: apache group: apache mode: "0770" loop: - /var/spool/koji-sign - /var/spool/koji-sign/queue - /var/spool/koji-sign/failed - name: Copy Koji sign spooler daemon to libexec ansible.builtin.copy: src: koji-sign-spooler dest: /usr/libexec/koji-sign-spooler owner: root group: root mode: "0755" notify: Restart koji-sign-spooler - name: Copy Koji sign spooler systemd service ansible.builtin.copy: src: koji-sign-spooler.service dest: /usr/lib/systemd/system/koji-sign-spooler.service owner: root group: root mode: "0644" notify: Restart koji-sign-spooler - name: Enable and start koji-sign-spooler service ansible.builtin.systemd: name: koji-sign-spooler state: started enabled: true daemon_reload: true - name: Template Koji callback script ansible.builtin.template: src: koji_callbacks.py.j2 dest: "{{ koji_callbacks_script_path }}" mode: "0755" - name: Configure Koji callbacks ansible.builtin.template: src: callbacks.conf.j2 dest: "{{ koji_callbacks_config_path }}" mode: "0640" notify: Restart httpd - name: Template Koji cleanup script ansible.builtin.template: src: koji_cleanup.sh.j2 dest: "{{ koji_cleanup_script_path }}" mode: "0755" - name: Create systemd service for Koji cleanup ansible.builtin.copy: src: koji-cleanup.service dest: "{{ koji_cleanup_service_path }}" mode: "0644" - name: Create systemd timer for Koji cleanup ansible.builtin.copy: src: koji-cleanup.timer dest: "{{ koji_cleanup_timer_path }}" mode: "0644" - name: Enable and start Koji cleanup timer ansible.builtin.systemd: name: koji-cleanup.timer state: started enabled: true daemon_reload: true - name: Enable and start Koji DB sweeper timer ansible.builtin.service: name: koji-sweep-db.timer state: started enabled: true - name: "Generate: MxOS Theme Graphic Assets on Localhost" delegate_to: localhost run_once: true block: - name: Check if rsvg-convert is installed on localhost ansible.builtin.command: which rsvg-convert register: rsvg_check ignore_errors: true changed_when: false - name: Ensure librsvg2-tools is installed on localhost (Fedora) ansible.builtin.dnf: name: librsvg2-tools state: present become: true when: rsvg_check.rc != 0 - name: Convert main logo SVG to PNG ansible.builtin.command: cmd: "rsvg-convert -w 250 --keep-aspect-ratio -o /tmp/mxos-koji.png /home/renich/Projects/MxOS/mxos.svg" creates: /tmp/mxos-koji.png - name: Convert favicon logo SVG to PNG ansible.builtin.command: cmd: "rsvg-convert -w 32 --keep-aspect-ratio -o /tmp/mxos-favicon.png /home/renich/Projects/MxOS/mxos-reducido.svg" creates: /tmp/mxos-favicon.png - name: "Deploy: Koji Web MxOS Branding Theme" block: - name: Create MxOS theme directories on Hub ansible.builtin.file: path: "{{ item }}" state: directory owner: root group: root mode: "0755" loop: - /usr/share/koji-web/static/themes/mxos - /usr/share/koji-web/static/themes/mxos/images - name: Copy logo PNG to theme images directory ansible.builtin.copy: src: /tmp/mxos-koji.png dest: /usr/share/koji-web/static/themes/mxos/images/koji.png owner: root group: root mode: "0644" notify: Restart httpd - name: Copy favicon PNG to theme images directory as favicon.ico ansible.builtin.copy: src: /tmp/mxos-favicon.png dest: /usr/share/koji-web/static/themes/mxos/images/favicon.ico owner: root group: root mode: "0644" notify: Restart httpd - name: Deploy MxOS koji.css style overrides ansible.builtin.copy: dest: /usr/share/koji-web/static/themes/mxos/koji.css owner: root group: root mode: "0644" content: | @import url("../default/koji.css"); body { font-family: 'Inter', system-ui, -apple-system, sans-serif; } #header { background-color: #0f172a; /* Pizarra oscuro */ border-bottom: 3px solid #10b981; /* Verde esmeralda de MxOS */ } #header a { color: #10b981; } #header h1 { background-image: url("images/koji.png"); background-repeat: no-repeat; background-position: left center; height: 60px; padding-left: 270px; /* Suficiente espacio para la imagen de 250px */ } /* Cambiar colores de enlaces y botones */ a { color: #10b981; } a:hover { color: #059669; /* Verde oscuro */ } th { background-color: #1e293b !important; color: #f8fafc !important; } .button, input[type="submit"], input[type="button"] { background-color: #10b981; color: white; border: none; border-radius: 4px; padding: 4px 12px; cursor: pointer; } .button:hover, input[type="submit"]:hover, input[type="button"]:hover { background-color: #059669; } notify: Restart httpd