├── .ansible-lint ├── .github └── workflows │ └── tests.yml ├── .stickler.yml ├── KERBEROS.rst ├── MOLECULE.rst ├── README.rst ├── ansible.cfg ├── clone-koji-ansible ├── molecule └── centos8 │ ├── Dockerfile.j2 │ ├── converge.yml │ ├── molecule.yml │ ├── prepare.yml │ ├── roles │ └── verify.yml ├── requirements.yml ├── roles ├── activemq │ ├── files │ │ ├── activemq.service │ │ ├── activemq.sysconfig │ │ └── activemq.xml │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── vars │ │ └── main.yml ├── kdc │ ├── files │ │ ├── kdc.conf │ │ ├── kdcproxy.conf │ │ └── krb5.conf │ ├── handlers │ │ └── main.yml │ ├── library │ │ └── krb_principal.py │ ├── tasks │ │ ├── kdcproxy.yml │ │ └── main.yml │ ├── templates │ │ └── kdcproxy-apache.conf.j2 │ └── vars │ │ └── main.yml ├── koji-builder │ ├── files │ │ └── kojid.conf │ ├── handlers │ │ └── main.yml │ └── tasks │ │ └── main.yml ├── koji-client │ ├── files │ │ └── kojidev.conf │ └── tasks │ │ └── main.yml ├── koji-gc │ ├── files │ │ └── koji-gc.conf │ ├── handlers │ │ └── main.yml │ └── tasks │ │ └── main.yml ├── koji-hub │ ├── files │ │ ├── hub.conf │ │ ├── kojihub.conf │ │ └── ssl.conf │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── vars │ │ ├── CentOS-7.yml │ │ ├── CentOS-8.yml │ │ ├── RedHat-7.yml │ │ ├── RedHat-8.yml │ │ └── main.yml ├── koji-ra │ ├── files │ │ └── kojira.conf │ ├── handlers │ │ └── main.yml │ └── tasks │ │ └── main.yml ├── koji-ssl-admin │ ├── tasks │ │ └── main.yml │ └── vars │ │ ├── CentOS-7.yml │ │ ├── CentOS-8.yml │ │ ├── RedHat-7.yml │ │ └── RedHat-8.yml ├── koji-web │ ├── files │ │ ├── kojiweb.conf │ │ └── web.conf │ ├── handlers │ │ └── main.yml │ └── tasks │ │ └── main.yml ├── postgresql │ ├── handlers │ │ └── main.yml │ └── tasks │ │ └── main.yml └── rabbitmq │ ├── files │ └── rabbitmq.conf │ ├── handlers │ └── main.yml │ └── tasks │ └── main.yml ├── setup-koji.yml └── vagrant ├── README.rst ├── Vagrantfile ├── reset-database.sh ├── roles └── vagrant.yml /.ansible-lint: -------------------------------------------------------------------------------- 1 | --- 2 | skip_list: 3 | - fqcn[action-core] 4 | - fqcn[action] 5 | - jinja 6 | - name[casing] 7 | - risky-file-permissions 8 | - role-name 9 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | syntax-check: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - name: Clone koji-ansible 11 | run: ./clone-koji-ansible 12 | - name: Install ansible 13 | run: | 14 | sudo apt-get update 15 | sudo apt-get purge ansible 16 | sudo apt-get install python3-setuptools 17 | pip3 install ansible --user 18 | - name: ansible-playbook syntax check 19 | run: | 20 | export PATH=$PATH:$HOME/.local/bin 21 | ansible-playbook -i localhost, setup-koji.yml --syntax-check 22 | ansible-lint: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v3 26 | - name: Clone koji-ansible 27 | run: ./clone-koji-ansible 28 | - name: Install ansible-lint 29 | run: | 30 | sudo apt-get update 31 | sudo apt-get purge ansible 32 | sudo apt-get install python3-setuptools 33 | pip3 install ansible-lint[core] --user 34 | - name: Run ansible-lint 35 | run: | 36 | export PATH=$PATH:$HOME/.local/bin 37 | ansible-galaxy collection install -r requirements.yml 38 | ANSIBLE_LIBRARY=library ansible-lint -v roles/* 39 | molecule: 40 | runs-on: ubuntu-22.04 41 | steps: 42 | - uses: actions/checkout@v3 43 | - name: Clone koji-ansible 44 | run: ./clone-koji-ansible 45 | - name: Install podman and molecule 46 | run: | 47 | . /etc/os-release 48 | curl -sS https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/Release.key | gpg --dearmor | sudo dd of=/etc/apt/trusted.gpg.d/podman.gpg 49 | echo "deb [signed-by=/etc/apt/trusted.gpg.d/podman.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/ /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list 50 | sudo apt-get update 51 | sudo apt-get purge ansible 52 | sudo apt-get install podman python3-setuptools 53 | sudo mkdir -p /etc/containers 54 | echo 'cgroup_manager="cgroupfs"' | sudo tee /etc/containers/libpod.conf 55 | pip3 install molecule molecule-plugins[podman] --user 56 | - name: Run molecule 57 | run: | 58 | export PATH=$PATH:$HOME/.local/bin 59 | export ANSIBLE_MODULE_UTILS=$(pwd)/module_utils 60 | export ANSIBLE_LIBRARY=$(pwd)/library 61 | molecule test -s centos8 62 | -------------------------------------------------------------------------------- /.stickler.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | ansible: 3 | ignore: 'ANSIBLE0106,ANSIBLE0501' 4 | -------------------------------------------------------------------------------- /KERBEROS.rst: -------------------------------------------------------------------------------- 1 | Troubleshooting Kerberos 2 | ======================== 3 | 4 | On the client:: 5 | 6 | export KRB5_TRACE=/dev/stdout 7 | 8 | On the hub: 9 | 10 | Check ``/var/log/httpd/ssl_error_log``. You should see mod_auth_gssapi information here. 11 | 12 | On the KDC: 13 | 14 | Check ``/var/log/krb5kdc.log``. You should see an AS-REQ for the TGT and a TGS-REQ for HTTP/kojidev.example.com. Both should be under your user account (eg. "kdreyer"). 15 | 16 | In molecule container (as root):: 17 | 18 | tail -F /var/log/httpd/ssl_error_log /var/log/httpd/ssl_access_log /var/log/krb5kdc.log 19 | -------------------------------------------------------------------------------- /MOLECULE.rst: -------------------------------------------------------------------------------- 1 | molecule cheat sheet 2 | -------------------- 3 | 4 | Simple one-shot create/run example:: 5 | 6 | molecule test -s centos8 7 | 8 | To debug molecule, set the ``MOLECULE_NO_LOG=false`` environment variable and 9 | add the ``--debug`` command-line option. For example:: 10 | 11 | MOLECULE_NO_LOG=false molecule --debug test -s centos8 12 | 13 | Instead of the all-in-one "test" sub-command, you can break down the steps 14 | into individual commands. 15 | 16 | - Create and start a container:: 17 | 18 | molecule create -s centos8 19 | 20 | - Run the "converge" step, to apply the roles specified in 21 | ``converge.yml``:: 22 | 23 | molecule converge -s centos8 24 | 25 | - Destroy the running container:: 26 | 27 | molecule destroy -s centos8 28 | 29 | Verify that the container is running:: 30 | 31 | podman ps -a 32 | 33 | (and look for a container named "instance".) 34 | 35 | Shell into the container instance:: 36 | 37 | molecule login -s centos8 38 | 39 | (If that does not work, try ``podman exec -it instance /bin/sh``) 40 | 41 | Review the container's systemd log output:: 42 | 43 | podman logs instance 44 | 45 | Clean up the local container image that molecule created:: 46 | 47 | podman rmi localhost/molecule_local/centos:stream8 48 | 49 | Hints for parameterizing this and running it in CI: 50 | 51 | https://www.jeffgeerling.com/blog/2018/testing-your-ansible-roles-molecule 52 | 53 | https://www.jeffgeerling.com/blog/2020/travis-cis-new-pricing-plan-threw-wrench-my-open-source-works 54 | 55 | Replicating GitHub Actions locally 56 | ----------------------------- 57 | 58 | GitHub Actions runs the tests in an Ubuntu Jammy VM. It can be tedious to push 59 | changes to GitHub, wait, and review the output. You may want to set up your 60 | own local Ubuntu Jammy VM when making large changes that impact the tests. 61 | 62 | Follow these instructions to set up Podman on your own Ubuntu Jammy VM in a 63 | similar way to GitHub Actions:: 64 | 65 | sudo apt-get update 66 | # follow the steps in .github/workflows/... 67 | 68 | sudo apt-get -y install python3-pip 69 | pip3 install molecule[ansible,podman] --user 70 | 71 | 72 | Versioning 73 | ---------- 74 | 75 | There are many bugs in podman, Ansible, and Molecule. Please run the latest 76 | versions of each. 77 | 78 | Version combinations that are known to work: 79 | - python3-molecule-3.0.5-2.fc32 80 | - ansible-2.9.14-1.fc32 81 | - podman-2.1.1-7.fc32 82 | 83 | Note that since we run systemd in the container, that means that the systemd 84 | version in CentOS 8 and the kernel version on the host (eg Fedora) sometimes 85 | interact poorly. See https://bugzilla.redhat.com/1853736 for an example. 86 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://github.com/ktdreyer/koji-playbooks/workflows/tests/badge.svg 2 | :target: https://github.com/ktdreyer/koji-playbooks/actions 3 | 4 | Ansible playbook(s) for automating the `Koji server install process 5 | `_. 6 | 7 | This sets up the Kerberos (GSSAPI) method of authentication. I use this to 8 | quickly set up Koji in VMs in OpenStack. 9 | 10 | Playbooks 11 | --------- 12 | 13 | * ``setup-koji.yml`` - Installs and configures a Kerberos KDC, koji-hub, and 14 | koji-builder. 15 | 16 | Run this playbook on a RHEL or CentOS 7 or 8 host with EPEL enabled. /mnt/koji 17 | should be a disk with plenty of space. 18 | 19 | SSL configuration 20 | ----------------- 21 | 22 | * This playbook generates an SSL CA and keypair using the `koji-ssl-admin tool 23 | `_ . 24 | 25 | The Certificate Authority keypair: 26 | * ``/etc/pki/koji/koji-ca.crt`` 27 | * ``/etc/pki/koji/koji-ca.key`` 28 | 29 | The Apache web server HTTPS keypair (signed by koji-ca above): 30 | * ``/etc/pki/koji/kojidev.example.com.chain.crt`` 31 | * ``/etc/pki/koji/kojidev.example.com.key`` 32 | 33 | For GSSAPI (Kerberos) authentication, these are the only SSL certs you will 34 | need. 35 | 36 | The koji-hub role publishes the Koji CA at the following URL: 37 | https://kojidev.example.com/kojifiles/koji-ca.crt . External Koji clients 38 | can download this file to verify the HTTPS connections. 39 | 40 | Hard-coded things 41 | ----------------- 42 | 43 | This is a santized code drop from a set of internal playbooks, so several 44 | things are currently hard-coded: 45 | 46 | * The hostname is hardcoded in several places as "kojidev.example.com". 47 | 48 | * The main username is hardcoded in several places as "kdreyer". 49 | 50 | 51 | Roles 52 | ----- 53 | 54 | * ``roles/kdc`` - installs and configures a Kerberos KDC, and bootstraps all 55 | the keytabs we need. 56 | 57 | This will create a "kdreyer" Kerberos account. The ``koji-hub`` role will 58 | bootstrap this account into Koji's database. If you need more Kerberos 59 | users, add them here. 60 | 61 | * ``roles/koji-ssl-admin`` - Creates the SSL CA and HTTPS keypair for the Koji 62 | server. 63 | 64 | * ``roles/koji-client`` - Configures a ``kojidev`` script and `profile 65 | `_. 66 | 67 | * ``roles/postgresql`` - installs and configures PostgreSQL for Koji Hub 68 | 69 | * ``roles/koji-hub`` - installs and configures Koji Hub 70 | 71 | This role requires the `koji_host 72 | `_ 73 | module from the `koji-ansible project 74 | `_. 75 | 76 | This role will bootstrap "kdreyer" as the first Koji administrator in the 77 | database. 78 | 79 | If you need more users, add them with the `koji_user 80 | `_ 81 | module. 82 | 83 | * ``roles/koji-web`` - installs and configures the web interface for Koji. 84 | 85 | * ``roles/koji-builder`` - installs and configures a Koji builder. 86 | 87 | * ``roles/koji-ra`` - installs and configures the Koji "ra" (repository admin) 88 | service. 89 | 90 | * ``roles/koji-gc`` - installs and configures the Koji garbage collector 91 | service. 92 | 93 | * ``roles/activemq`` - installs and configures an ActiveMQ 5 broker for testing 94 | the Koji Hub protonmsg plugin. 95 | 96 | * ``roles/rabbitmq`` - installs and configures a RabbitMQ broker for testing 97 | the Koji Hub protonmsg plugin. 98 | 99 | See Also 100 | -------- 101 | 102 | For managing resources within your Koji hub, please see the 103 | https://github.com/ktdreyer/koji-ansible project. 104 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | retry_files_enabled = False 3 | stdout_callback = yaml 4 | 5 | [ssh_connection] 6 | pipelining=True 7 | -------------------------------------------------------------------------------- /clone-koji-ansible: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eux 4 | 5 | git clone --depth 1 https://github.com/ktdreyer/koji-ansible 6 | pushd koji-ansible && git log HEAD -1 --no-decorate && popd 7 | ln -s koji-ansible/library/ library 8 | ln -s ../koji-ansible/library/ vagrant/library 9 | ln -s koji-ansible/module_utils/ module_utils 10 | ln -s ../koji-ansible/module_utils/ vagrant/module_utils 11 | -------------------------------------------------------------------------------- /molecule/centos8/Dockerfile.j2: -------------------------------------------------------------------------------- 1 | {% if item.registry is defined %} 2 | FROM {{ item.registry.url }}/{{ item.image }} 3 | {% else %} 4 | FROM {{ item.image }} 5 | {% endif %} 6 | 7 | # Ensure package prerequisites 8 | # (This is simplified from the Molecule project upstream, plus 9 | # glibc-langpack-en for postgres) 10 | 11 | RUN dnf makecache \ 12 | && dnf --assumeyes install \ 13 | /usr/bin/python3 \ 14 | /usr/bin/python3-config \ 15 | /usr/bin/dnf-3 \ 16 | sudo \ 17 | bash \ 18 | iproute \ 19 | glibc-langpack-en 20 | 21 | # Create "ansible" unprivileged user with sudo permissions: 22 | 23 | ENV ANSIBLE_USER=ansible SUDO_GROUP=wheel 24 | RUN set -xe \ 25 | && groupadd -r ${ANSIBLE_USER} \ 26 | && useradd -m -g ${ANSIBLE_USER} ${ANSIBLE_USER} \ 27 | && usermod -aG ${SUDO_GROUP} ${ANSIBLE_USER} \ 28 | && sed -i "/^%${SUDO_GROUP}/s/ALL\$/NOPASSWD:ALL/g" /etc/sudoers 29 | -------------------------------------------------------------------------------- /molecule/centos8/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | # Note, "become: no" does not work in Ansible 2.9's podman connection plugin. 5 | # See https://github.com/ansible/ansible/pull/70541 6 | become: yes 7 | become_method: sudo 8 | tasks: 9 | - name: Add EPEL yum repository 10 | yum_repository: 11 | name: epel 12 | description: epel 13 | metalink: https://mirrors.fedoraproject.org/metalink?repo=epel-8&arch=$basearch 14 | gpgcheck: true 15 | gpgkey: https://archive.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-8 16 | roles: 17 | - kdc 18 | - koji-ssl-admin 19 | - koji-client 20 | - postgresql 21 | - koji-hub 22 | - koji-web 23 | - koji-builder 24 | - koji-ra 25 | - koji-gc 26 | -------------------------------------------------------------------------------- /molecule/centos8/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | options: 5 | requirements-file: requirements.yml 6 | driver: 7 | name: podman 8 | platforms: 9 | - name: instance 10 | registry: 11 | url: quay.io/centos 12 | image: centos:stream8 13 | privileged: true 14 | command: "/usr/sbin/init" 15 | tty: True 16 | etc_hosts: 17 | kojidev.example.com: 127.0.0.1 18 | hostname: kojidev.example.com 19 | provisioner: 20 | name: ansible 21 | inventory: 22 | host_vars: 23 | instance: 24 | ansible_user: ansible 25 | # Silence warnings in Ansible 2.9: 26 | ansible_python_interpreter: /usr/bin/python3 27 | verifier: 28 | name: ansible 29 | -------------------------------------------------------------------------------- /molecule/centos8/prepare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Prepare 3 | hosts: all 4 | become: yes 5 | become_method: sudo 6 | tasks: 7 | - name: Add EPEL yum repository 8 | yum_repository: 9 | name: epel 10 | description: epel 11 | metalink: https://mirrors.fedoraproject.org/metalink?repo=epel-8&arch=$basearch 12 | gpgcheck: true 13 | gpgkey: https://archive.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-8 14 | -------------------------------------------------------------------------------- /molecule/centos8/roles: -------------------------------------------------------------------------------- 1 | ../../roles/ -------------------------------------------------------------------------------- /molecule/centos8/verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This is an example playbook to execute Ansible tests. 3 | 4 | - name: Verify 5 | hosts: all 6 | tasks: 7 | - name: Example assertion 8 | assert: 9 | that: true 10 | -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | collections: 3 | - ansible.posix 4 | - community.crypto 5 | - community.general 6 | - community.postgresql 7 | - community.rabbitmq 8 | # Note: CI tests (GitHub Actions) use a Git clone of ktdreyer.koji_ansible 9 | # intead of requiring it here. 10 | -------------------------------------------------------------------------------- /roles/activemq/files/activemq.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Apache ActiveMQ 3 | After=network-online.target 4 | 5 | [Service] 6 | Type=forking 7 | WorkingDirectory=/opt/activemq/bin 8 | EnvironmentFile=/etc/sysconfig/activemq 9 | ExecStart=/opt/activemq/bin/activemq start 10 | ExecStop=/opt/activemq/bin/activemq stop 11 | Restart=on-abort 12 | User=activemq 13 | Group=activemq 14 | PIDFile=/opt/activemq/data/activemq.pid 15 | ProtectSystem=strict 16 | ProtectHome=yes 17 | ReadWritePaths=/opt 18 | 19 | [Install] 20 | WantedBy=multi-user.target 21 | -------------------------------------------------------------------------------- /roles/activemq/files/activemq.sysconfig: -------------------------------------------------------------------------------- 1 | ACTIVEMQ_SSL_OPTS="-Djavax.net.debug=ssl,keymanager -Djavax.net.ssl.keyStoreType=pkcs12 -Djavax.net.ssl.keyStore=/opt/activemq/kojidev.example.com.pkcs12 -Djavax.net.ssl.keyStorePassword=kojipass -Djavax.net.ssl.trustStore=/opt/activemq/koji-ca.ks -Djavax.net.ssl.trustStorePassword=kojipass" 2 | -------------------------------------------------------------------------------- /roles/activemq/files/activemq.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | 24 | 25 | 26 | 27 | file:${activemq.conf}/credentials.properties 28 | 29 | 30 | 31 | 32 | 35 | 36 | 37 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 75 | 76 | 77 | 78 | 79 | 86 | 87 | 88 | 89 | 90 | 91 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /roles/activemq/handlers/main.yml: -------------------------------------------------------------------------------- 1 | # Pick up our custom sytemd unit file: 2 | - name: reload systemd 3 | systemd: 4 | daemon_reload: true 5 | 6 | - name: restart activemq 7 | service: 8 | name: activemq 9 | state: restarted 10 | -------------------------------------------------------------------------------- /roles/activemq/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: install prerequisites 2 | package: 3 | name: 4 | - java-1.8.0-openjdk-headless 5 | - python3-pyOpenSSL 6 | state: present 7 | 8 | - name: create activemq user 9 | user: 10 | name: activemq 11 | shell: /sbin/nologin 12 | 13 | - name: download activemq tarball 14 | get_url: 15 | url: "https://archive.apache.org/dist/activemq/{{ activemq_version }}/apache-activemq-{{ activemq_version }}-bin.tar.gz" 16 | dest: "/tmp/apache-activemq-{{ activemq_version }}-bin.tar.gz" 17 | checksum: "{{ activemq_checksum }}" 18 | register: activemq_download 19 | 20 | - name: unpack activemq tarball # noqa no-handler 21 | unarchive: 22 | src: "/tmp/apache-activemq-{{ activemq_version }}-bin.tar.gz" 23 | dest: /opt 24 | remote_src: true 25 | owner: activemq 26 | group: activemq 27 | when: activemq_download.changed 28 | 29 | - name: symlink /opt/activemq 30 | file: 31 | src: "/opt/apache-activemq-{{ activemq_version }}" 32 | dest: /opt/activemq 33 | state: link 34 | 35 | - name: copy /opt/activemq/conf/activemq.xml 36 | copy: 37 | src: files/activemq.xml 38 | dest: /opt/activemq/conf/activemq.xml 39 | owner: root 40 | group: root 41 | mode: "0644" 42 | notify: 43 | - restart activemq 44 | 45 | - name: install activemq systemd config file 46 | copy: 47 | src: files/activemq.sysconfig 48 | dest: /etc/sysconfig/activemq 49 | owner: root 50 | group: root 51 | mode: "0644" 52 | notify: 53 | - restart activemq 54 | 55 | - name: install activemq systemd unit file 56 | copy: 57 | src: files/activemq.service 58 | dest: /etc/systemd/system/activemq.service 59 | owner: root 60 | group: root 61 | mode: "0644" 62 | notify: 63 | - reload systemd 64 | 65 | - name: combine activemq server cert into a pkcs12 file 66 | openssl_pkcs12: 67 | path: /opt/activemq/kojidev.example.com.pkcs12 68 | friendly_name: koji 69 | privatekey_path: /etc/pki/koji/kojidev.example.com.key 70 | certificate_path: /etc/pki/koji/kojidev.example.com.crt 71 | other_certificates: /etc/pki/koji/koji-ca.crt 72 | passphrase: kojipass 73 | owner: activemq 74 | group: activemq 75 | mode: "0400" 76 | state: present 77 | 78 | # TODO: use https://docs.ansible.com/ansible/2.9/modules/java_cert_module.html 79 | # Verify with the -list command: 80 | # keytool -list -v -keystore /opt/activemq/koji-ca.ks 81 | - name: transform koji-ca.crt file to java keystore file 82 | command: 83 | argv: 84 | - keytool 85 | - -import 86 | - -trustcacerts 87 | - -alias 88 | - kojica 89 | - -file 90 | - /etc/pki/koji/koji-ca.crt 91 | - -keystore 92 | - /opt/activemq/koji-ca.ks 93 | - -storepass 94 | - kojipass 95 | - -noprompt 96 | creates: /opt/activemq/koji-ca.ks 97 | 98 | 99 | - name: start activemq 100 | service: 101 | name: activemq 102 | enabled: true 103 | state: started 104 | -------------------------------------------------------------------------------- /roles/activemq/vars/main.yml: -------------------------------------------------------------------------------- 1 | activemq_version: "5.16.3" 2 | activemq_checksum: sha256:1846da2985ec64253ecc41a54f1477731eb4750fe840a9dd9fdfee88e5c94252 3 | -------------------------------------------------------------------------------- /roles/kdc/files/kdc.conf: -------------------------------------------------------------------------------- 1 | [kdcdefaults] 2 | kdc_ports = 88 3 | kdc_tcp_ports = 88 4 | 5 | [realms] 6 | KOJIDEV.EXAMPLE.COM = { 7 | #master_key_type = aes256-cts 8 | acl_file = /var/kerberos/krb5kdc/kadm5.acl 9 | dict_file = /usr/share/dict/words 10 | admin_keytab = /var/kerberos/krb5kdc/kadm5.keytab 11 | supported_enctypes = aes256-cts:normal camellia256-cts:normal 12 | } 13 | -------------------------------------------------------------------------------- /roles/kdc/files/kdcproxy.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | use_dns = false 3 | configs = mit 4 | -------------------------------------------------------------------------------- /roles/kdc/files/krb5.conf: -------------------------------------------------------------------------------- 1 | # Configuration snippets may be placed in this directory as well 2 | includedir /etc/krb5.conf.d/ 3 | 4 | [logging] 5 | default = FILE:/var/log/krb5libs.log 6 | kdc = FILE:/var/log/krb5kdc.log 7 | admin_server = FILE:/var/log/kadmind.log 8 | 9 | [libdefaults] 10 | dns_lookup_realm = false 11 | ticket_lifetime = 24h 12 | renew_lifetime = 7d 13 | forwardable = true 14 | rdns = false 15 | pkinit_anchors = /etc/pki/tls/certs/ca-bundle.crt 16 | default_realm = KOJIDEV.EXAMPLE.COM 17 | 18 | [realms] 19 | KOJIDEV.EXAMPLE.COM = { 20 | kdc = kojidev.example.com 21 | admin_server = kojidev.example.com 22 | } 23 | 24 | [domain_realm] 25 | # .example.com = EXAMPLE.COM 26 | # example.com = EXAMPLE.COM 27 | -------------------------------------------------------------------------------- /roles/kdc/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: restart krb5kdc 2 | service: 3 | name: krb5kdc 4 | state: restarted 5 | 6 | - name: restart httpd 7 | service: 8 | name: httpd 9 | state: restarted 10 | -------------------------------------------------------------------------------- /roles/kdc/library/krb_principal.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import errno 3 | import re 4 | import os 5 | import subprocess 6 | from ansible.module_utils.basic import AnsibleModule 7 | from ansible.module_utils.six import PY2 8 | 9 | 10 | ANSIBLE_METADATA = { 11 | 'metadata_version': '1.0', 12 | 'status': ['preview'], 13 | 'supported_by': 'community' 14 | } 15 | 16 | 17 | DOCUMENTATION = ''' 18 | --- 19 | module: krb_principal 20 | 21 | short_description: Create Kerberos principals and export keytabs 22 | description: 23 | - This module uses /usr/sbin/kadmin.local to create users and export 24 | keytab files. 25 | 26 | options: 27 | name: 28 | description: 29 | - The name of the kerberos principal, for example "kdreyer@EXAMPLE.COM" 30 | required: true 31 | state: 32 | description: 33 | - The only allowed value is "present". 34 | required: false 35 | choices: [present] 36 | keytab: 37 | description: 38 | - If specified, Ansible will extract a .keytab file to this path. 39 | - If any file already exists at this path, then Ansible will delete the 40 | file and create a new keytab if Ansible also created a new principal. 41 | If Ansible did not create a new principal, then it will not delete or 42 | edit this file if it exists. 43 | - Ansible does not validate that the pre-existing file is a keytab. 44 | required: false 45 | requirements: 46 | - "python >= 2.7" 47 | ''' 48 | 49 | EXAMPLES = ''' 50 | - name: Create a user and a keytab. 51 | hosts: localhost 52 | tasks: 53 | - name: Create a kdreyer keytab 54 | krb_principal: 55 | name: kdreyer@EXAMPLE.COM 56 | keytab: /var/local/kdreyer.keytab 57 | ''' 58 | 59 | 60 | def kadmin(query): 61 | """ Call "kadmin.local -q" with this query. """ 62 | cmd = ('/usr/sbin/kadmin.local', '-q', query) 63 | output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) 64 | if PY2: 65 | return output 66 | return output.decode('utf-8') 67 | 68 | 69 | def get_principal(name): 70 | """ Return the full principal name, or None if it does not exist. """ 71 | output = kadmin('getprinc %s' % name) 72 | for line in output.splitlines(): 73 | if line.startswith('Principal:'): 74 | _, principal = line.split(':') 75 | return principal 76 | 77 | 78 | def create_principal(name): 79 | """ Create an account, and return the full principal name. """ 80 | output = kadmin('addprinc -randkey %s' % name) 81 | for line in output.splitlines(): 82 | m = re.match('Principal "[^"]+" created.', line) 83 | if m: 84 | return m.group(0) 85 | raise RuntimeError('could not scrape addprinc output: %s' % output) 86 | 87 | 88 | def extract_keytab(principal, keytab): 89 | """ Extract a keytab file for a principal. """ 90 | kadmin('ktadd -k %s -norandkey %s' % (keytab, principal)) 91 | 92 | 93 | def run_module(): 94 | module_args = dict( 95 | name=dict(required=True), 96 | keytab=dict(type='path'), 97 | state=dict(choices=['present'], default='present'), 98 | ) 99 | module = AnsibleModule( 100 | argument_spec=module_args, 101 | supports_check_mode=True 102 | ) 103 | 104 | check_mode = module.check_mode 105 | params = module.params 106 | name = params['name'] 107 | keytab = params['keytab'] 108 | 109 | changes = [] 110 | principal = get_principal(name) 111 | if not principal: 112 | changes.append('create principal %s' % name) 113 | if not check_mode: 114 | principal = create_principal(name) 115 | 116 | if keytab: 117 | if changes and not check_mode: 118 | # Delete the keytab 119 | try: 120 | os.remove(keytab) 121 | except OSError as e: 122 | if e.errno != errno.ENOENT: 123 | raise 124 | if not os.path.exists(keytab): 125 | changes.append('extract keytab %s' % keytab) 126 | if not check_mode: 127 | extract_keytab(principal, keytab) 128 | 129 | if not changes: 130 | module.exit_json(changed=False) 131 | 132 | module.exit_json(changed=True, stdout_lines=changes) 133 | 134 | 135 | def main(): 136 | run_module() 137 | 138 | 139 | if __name__ == '__main__': 140 | main() 141 | -------------------------------------------------------------------------------- /roles/kdc/tasks/kdcproxy.yml: -------------------------------------------------------------------------------- 1 | - name: set python values for el7 2 | set_fact: 3 | # No "python3" package name prefix. 4 | python_kdcproxy: python-kdcproxy 5 | # See /usr/lib/rpm/macros.d/macros.python2 for %python2_sitelib 6 | python_sitelib: /usr/lib/python2.7/site-packages 7 | mod_wsgi: mod_wsgi 8 | when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int == 7 9 | 10 | - name: Add yum repository to work around rhbz1827758 for el8 11 | yum_repository: 12 | name: bz1827758 13 | description: work around rhbz1827758 14 | baseurl: https://fedorapeople.org/~ktdreyer/bz1827758/ 15 | gpgcheck: false 16 | when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int == 8 17 | 18 | - name: Install kdcproxy packages 19 | package: 20 | name: 21 | - httpd 22 | - mod_ssl 23 | - "{{ mod_wsgi }}" 24 | - "{{ python_kdcproxy }}" 25 | state: present 26 | retries: 100 27 | delay: 3 28 | 29 | - name: copy /etc/httpd/conf.d/kdcproxy.conf 30 | template: 31 | src: kdcproxy-apache.conf.j2 32 | dest: /etc/httpd/conf.d/kdcproxy.conf 33 | owner: root 34 | group: root 35 | mode: "0644" 36 | notify: 37 | - restart httpd 38 | 39 | - name: copy /etc/kdcproxy.conf 40 | copy: 41 | src: files/kdcproxy.conf 42 | dest: /etc/kdcproxy.conf 43 | owner: root 44 | group: root 45 | mode: "0644" 46 | notify: 47 | - restart httpd 48 | -------------------------------------------------------------------------------- /roles/kdc/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: configure FQDN in /etc/hosts 2 | copy: 3 | dest: '/etc/hosts' 4 | content: | 5 | 127.0.0.1 localhost 6 | ::1 localhost 7 | 127.0.0.1 kojidev.example.com 8 | mode: "0644" 9 | when: 10 | - ansible_virtualization_type | default('') not in ['docker', 'podman', 'container', 'containerd'] 11 | # Docker, Podman, and containerd will mount /etc/hosts as RO tmpfs. 12 | - ansible_mounts | selectattr('mount', 'equalto', '/etc/hosts') | list | length == 0 13 | 14 | - name: Install Kerberos packages 15 | package: 16 | name: 17 | - krb5-server 18 | - krb5-workstation 19 | state: present 20 | 21 | - name: copy /etc/krb5.conf 22 | copy: 23 | src: files/krb5.conf 24 | dest: /etc/krb5.conf 25 | owner: root 26 | group: root 27 | mode: "0644" 28 | notify: 29 | - restart krb5kdc 30 | 31 | - name: copy /var/kerberos/krb5kdc/kdc.conf 32 | copy: 33 | src: files/kdc.conf 34 | dest: /var/kerberos/krb5kdc/kdc.conf 35 | owner: root 36 | group: root 37 | mode: "0600" 38 | notify: 39 | - restart krb5kdc 40 | 41 | - name: configure /var/kerberos/krb5kdc/kadm5.acl 42 | copy: 43 | dest: /var/kerberos/krb5kdc/kadm5.acl 44 | owner: root 45 | group: root 46 | mode: "0600" 47 | content: | 48 | */admin@KOJIDEV.EXAMPLE.COM * 49 | notify: 50 | - restart krb5kdc 51 | 52 | - name: initialize KDC DB 53 | command: /usr/sbin/kdb5_util create -s -P test 54 | args: 55 | creates: /var/kerberos/krb5kdc/principal.ok 56 | notify: 57 | - restart krb5kdc 58 | 59 | - name: start krb5kdc 60 | service: 61 | name: krb5kdc 62 | enabled: true 63 | state: started 64 | 65 | - name: start kadmin 66 | service: 67 | name: kadmin 68 | enabled: true 69 | state: started 70 | 71 | - name: Create Kerberos principals 72 | krb_principal: 73 | name: "{{ item }}" 74 | keytab: "/var/local/{{ item | replace ('/', '.') }}.keytab" 75 | with_items: 76 | - kdreyer 77 | - rcm/debbuild 78 | - HTTP/kojidev.example.com 79 | - compile/kojidev.example.com 80 | - koji/kojiweb 81 | - koji/kojira 82 | - koji/garbagecollector 83 | tags: 84 | - krb_account 85 | 86 | - name: set up kdcproxy 87 | include_tasks: kdcproxy.yml 88 | -------------------------------------------------------------------------------- /roles/kdc/templates/kdcproxy-apache.conf.j2: -------------------------------------------------------------------------------- 1 | WSGIDaemonProcess kdcproxy processes=2 threads=15 maximum-requests=1000 \ 2 | display-name=%{GROUP} 3 | WSGIImportScript {{ python_sitelib }}/kdcproxy/__init__.py \ 4 | process-group=kdcproxy application-group=kdcproxy 5 | WSGIScriptAlias /KdcProxy {{ python_sitelib }}/kdcproxy/__init__.py 6 | WSGIScriptReloading Off 7 | 8 | 9 | Satisfy Any 10 | Order Deny,Allow 11 | Allow from all 12 | WSGIProcessGroup kdcproxy 13 | WSGIApplicationGroup kdcproxy 14 | SSLRequireSSL 15 | 16 | -------------------------------------------------------------------------------- /roles/kdc/vars/main.yml: -------------------------------------------------------------------------------- 1 | python_kdcproxy: python3-kdcproxy 2 | python_sitelib: /usr/lib/python3.6/site-packages 3 | mod_wsgi: python3-mod_wsgi 4 | -------------------------------------------------------------------------------- /roles/koji-builder/files/kojid.conf: -------------------------------------------------------------------------------- 1 | [kojid] 2 | ; The number of seconds to sleep between tasks 3 | ; sleeptime=15 4 | 5 | ; The maximum number of jobs that kojid will handle at a time 6 | ; maxjobs=10 7 | 8 | ; The minimum amount of free space (in MBs) required for each build root 9 | ; minspace=8192 10 | 11 | ; The directory root where work data can be found from the koji hub 12 | ; topdir=/mnt/koji 13 | 14 | ; The directory root for temporary storage 15 | ; workdir=/tmp/koji 16 | 17 | ; The temporary directory in buildroot 18 | ; chroot_tmpdir = /chroot_tmpdir 19 | 20 | ; The directory root for mock 21 | ; mockdir=/var/lib/mock 22 | 23 | ; The user to run as when doing builds 24 | ; mockuser=kojibuilder 25 | 26 | ; The vendor to use in rpm headers 27 | ; vendor=Koji 28 | 29 | ; The packager to use in rpm headers 30 | ; packager=Koji 31 | 32 | ; The distribution to use in rpm headers 33 | ; distribution=Koji 34 | 35 | ; The _host string to use in mock 36 | ; mockhost=koji-linux-gnu 37 | 38 | ; Timeout for build duration (24 hours) 39 | ; rpmbuild_timeout=86400 40 | 41 | ; Install timeout(seconds) for image build 42 | ; Default value is 0, which means using the number in /etc/oz/oz.cfg, 43 | ; supported since oz-0.16.0 44 | ; oz_install_timeout=7200 45 | 46 | ; The URL for the xmlrpc server 47 | server=https://kojidev.example.com/kojihub 48 | 49 | ; The URL for the file access 50 | ; Cannot use https with our custom CA here yet, see 51 | ; https://github.com/rpm-software-management/mock/issues/588 52 | topurl=http://kojidev.example.com/kojifiles 53 | 54 | ; use createrepo_c rather than createrepo 55 | ; use_createrepo_c=False 56 | 57 | ; A space-separated list of tuples from which kojid is allowed to checkout. 58 | ; The format of those tuples is: 59 | ; 60 | ; host:repository[:use_common[:source_cmd]] 61 | ; 62 | ; Incorrectly-formatted tuples will be ignored. 63 | ; 64 | ; If use_common is not present, kojid will attempt to checkout a common/ 65 | ; directory from the repository. If use_common is set to no, off, false, or 0, 66 | ; it will not attempt to checkout a common/ directory. 67 | ; 68 | ; source_cmd is a shell command (args separated with commas instead of spaces) 69 | ; to run before building the srpm. It is generally used to retrieve source 70 | ; files from a remote location. If no source_cmd is specified, "make sources" 71 | ; is run by default. 72 | allowed_scms= 73 | !src.fedoraproject.org:/pagure/fork/* 74 | !src.fedoraproject.org:/pagure/forks/* 75 | src.fedoraproject.org:/*:false:fedpkg,sources 76 | 77 | ; The mail host to use for sending email notifications 78 | ;smtphost=example.com 79 | 80 | ; The From address used when sending email notifications 81 | ;from_addr=Koji Build System 82 | 83 | ;configuration for Kerberos authentication 84 | 85 | ;the format of the principal used by the build hosts 86 | ;%s will be replaced by the FQDN of the host 87 | ;host_principal_format = compile/%s@KOJIDEV.EXAMPLE.COM 88 | ; kojid uses socket.fqdn() to find the FQDN for a host. That code looks at the 89 | ; public IP address for a host and does a DNS lookup for the PTR record of that 90 | ; IP address. If the host is running in a cloud environment, that is almost 91 | ; certainly not what you want, and the PTR record will be something out of your 92 | ; control. 93 | ; You can override the (imho broken) auto-discovery by setting krb_principal 94 | ; directly here: 95 | krb_principal = compile/kojidev.example.com@KOJIDEV.EXAMPLE.COM 96 | 97 | ;location of the keytab 98 | ;keytab = /etc/kojid/kojid.keytab 99 | keytab = /var/local/compile.kojidev.example.com.keytab 100 | 101 | ;the service name of the principal being used by the hub 102 | ;krbservice = host 103 | 104 | ;configuration for SSL authentication 105 | 106 | ;client certificate 107 | ;cert = /etc/kojid/client.crt 108 | 109 | ;certificate of the CA that issued the HTTP server certificate 110 | ;serverca = /etc/kojid/serverca.crt 111 | 112 | ;if set to True, failing subtask will not automatically cancel other siblings 113 | ;build_arch_can_fail = False 114 | 115 | ;if set to True additional logs with timestamps will get created and uploaded 116 | ;to hub. It could be useful for debugging purposes, but creates twice as many 117 | ;log files 118 | ;log_timestamps = False 119 | -------------------------------------------------------------------------------- /roles/koji-builder/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: restart kojid 2 | service: 3 | name: kojid 4 | state: restarted 5 | -------------------------------------------------------------------------------- /roles/koji-builder/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Add yum repository to work around rhbz1661580 2 | yum_repository: 3 | name: bz1661580 4 | description: work around rhbz1661580 5 | baseurl: https://fedorapeople.org/~ktdreyer/bz1661580/ 6 | gpgcheck: false 7 | when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int == 7 8 | 9 | - name: Install the very latest requests-kerberos (rhbz1661580) 10 | package: 11 | name: 12 | - python-requests-kerberos 13 | - python2-kerberos 14 | state: latest # noqa package-latest 15 | when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int == 7 16 | retries: 100 17 | delay: 3 18 | 19 | - name: Install Koji builder packages 20 | package: 21 | name: koji-builder 22 | state: present 23 | 24 | - name: copy /etc/kojid/kojid.conf 25 | copy: 26 | src: files/kojid.conf 27 | dest: /etc/kojid/kojid.conf 28 | owner: root 29 | group: root 30 | mode: "0644" 31 | notify: 32 | - restart kojid 33 | 34 | # Because we have "topurl=https://..." in kojid.conf, we must trust this CA 35 | # system-wide because we query this URL with urllib2. 36 | 37 | - name: link our Koji CA to the system-wide location 38 | file: 39 | src: /etc/pki/koji/koji-ca.crt 40 | dest: /etc/pki/ca-trust/source/anchors/koji-ca.crt 41 | state: link 42 | register: koji_ca_anchor 43 | 44 | - name: trust our new Koji CA system-wide # noqa no-handler 45 | command: update-ca-trust extract 46 | when: koji_ca_anchor.changed 47 | changed_when: true 48 | notify: 49 | - restart kojid 50 | 51 | - name: start kojid 52 | service: 53 | name: kojid 54 | enabled: true 55 | state: started 56 | -------------------------------------------------------------------------------- /roles/koji-client/files/kojidev.conf: -------------------------------------------------------------------------------- 1 | [kojidev] 2 | server = https://kojidev.example.com/kojihub 3 | authtype = kerberos 4 | topdir = /mnt/koji 5 | weburl = https://kojidev.example.com/koji 6 | topurl = https://kojidev.example.com/kojifiles 7 | 8 | # soon to be optional? see https://pagure.io/koji/pull-request/1194 9 | serverca = /etc/pki/koji/koji-ca.crt 10 | -------------------------------------------------------------------------------- /roles/koji-client/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # Set up the client for authentication 2 | 3 | - name: Install Koji client packages 4 | package: 5 | name: 6 | - koji 7 | - krb5-workstation 8 | state: present 9 | 10 | - name: copy koji client profile configuration 11 | copy: 12 | src: files/kojidev.conf 13 | dest: /etc/koji.conf.d/kojidev.conf 14 | owner: root 15 | group: root 16 | mode: "0644" 17 | 18 | - name: symlink "kojidev" alias for this profile 19 | file: 20 | src: /usr/bin/koji 21 | dest: /usr/bin/kojidev 22 | state: link 23 | 24 | - name: determine unprivileged user 25 | set_fact: 26 | unprivileged_user: "{{ ansible_user }}" 27 | become: false 28 | 29 | - name: determine unprivileged passwd info 30 | getent: 31 | database: passwd 32 | key: "{{ unprivileged_user }}" 33 | 34 | - name: determine unprivileged uid number 35 | set_fact: 36 | unprivileged_uid: "{{ getent_passwd[unprivileged_user][1] }}" 37 | 38 | - name: "create directory /var/kerberos/krb5/user/{{ unprivileged_uid }}" 39 | file: 40 | path: "/var/kerberos/krb5/user/{{ unprivileged_uid }}" 41 | state: directory 42 | mode: "0700" 43 | owner: "{{ unprivileged_user }}" 44 | 45 | - name: symlink kdreyer.keytab for unprivileged user 46 | file: 47 | src: /var/local/kdreyer.keytab 48 | dest: "/var/kerberos/krb5/user/{{ unprivileged_uid }}/client.keytab" 49 | state: link 50 | 51 | - name: make keytab readable by unprivileged user 52 | file: 53 | path: /var/local/kdreyer.keytab 54 | owner: "{{ unprivileged_user }}" 55 | -------------------------------------------------------------------------------- /roles/koji-gc/files/koji-gc.conf: -------------------------------------------------------------------------------- 1 | #test policy file 2 | #earlier = higher precedence! 3 | 4 | [main] 5 | key_aliases = 6 | 30C9ECF8 fedora-test 7 | 4F2A6FD2 fedora-gold 8 | 897DA07A redhat-beta 9 | 1AC70CE6 fedora-extras 10 | 11 | unprotected_keys = 12 | fedora-test 13 | fedora-extras 14 | redhat-beta 15 | 16 | server = https://kojidev.example.com/kojihub 17 | weburl = https://kojidev.example.com/koji 18 | 19 | keytab = /var/local/koji.garbagecollector.keytab 20 | 21 | # The service name of the principal being used by the hub 22 | #krbservice = host 23 | 24 | ## The realm of server principal. Using client's realm if not set 25 | # krb_server_realm = EXAMPLE.COM 26 | 27 | # The domain name that will be appended to Koji usernames 28 | # when creating email notifications 29 | #email_domain = fedoraproject.org 30 | 31 | # SMTP user and pass (uncomment and fill in if your smtp server requires authentication) 32 | #smtp_user=user@example.com 33 | #smtp_pass=CHANGEME 34 | 35 | [prune] 36 | policy = 37 | #stuff to protect 38 | #note that tags with master lock engaged are already protected 39 | tag *-updates :: keep 40 | age < 1 day :: skip 41 | sig fedora-gold :: skip 42 | sig fedora-test && age < 12 weeks :: keep 43 | 44 | #stuff to chuck semi-rapidly 45 | tag *-testing *-candidate :: { # nested rules 46 | order >= 2 :: untag 47 | order > 0 && age > 6 weeks :: untag 48 | } #closing braces must be on a line by themselves (modulo comments/whitespace) 49 | tag *-candidate && age > 60 weeks :: untag 50 | 51 | #default: keep the last 3 52 | order > 2 :: untag 53 | -------------------------------------------------------------------------------- /roles/koji-gc/handlers/main.yml: -------------------------------------------------------------------------------- 1 | # Pick up our custom sytemd unit files: 2 | - name: reload systemd 3 | systemd: 4 | daemon_reload: true 5 | -------------------------------------------------------------------------------- /roles/koji-gc/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Install Koji utils package 2 | package: 3 | name: koji-utils 4 | state: present 5 | 6 | - name: copy /etc/koji-gc/koji-gc.conf 7 | copy: 8 | src: files/koji-gc.conf 9 | dest: /etc/koji-gc/koji-gc.conf 10 | owner: root 11 | group: root 12 | mode: "0644" 13 | 14 | - name: enable koji-gc.timer 15 | service: 16 | name: koji-gc.timer 17 | enabled: true 18 | -------------------------------------------------------------------------------- /roles/koji-hub/files/hub.conf: -------------------------------------------------------------------------------- 1 | [hub] 2 | 3 | ## ConfigParser style config file, similar to ini files 4 | ## https://docs.python.org/library/configparser.html 5 | ## 6 | ## Note that multiline values can be set by indenting subsequent lines 7 | ## (which means you should not indent regular lines) 8 | 9 | ## Basic options ## 10 | DBName = koji 11 | DBUser = koji 12 | KojiDir = /mnt/koji 13 | 14 | ## Auth-related options ## 15 | # Use user IP in session management 16 | # CheckClientIP = True 17 | 18 | ## Kerberos authentication options ## 19 | 20 | AuthPrincipal = HTTP/kojidev.example.com@KOJIDEV.EXAMPLE.COM 21 | AuthKeytab = /var/local/HTTP.kojidev.example.com.keytab 22 | ProxyPrincipals = koji/kojiweb@KOJIDEV.EXAMPLE.COM 23 | ## format string for host principals (%s = hostname) 24 | HostPrincipalFormat = compile/%s@KOJIDEV.EXAMPLE.COM 25 | 26 | ## end Kerberos auth configuration 27 | 28 | 29 | 30 | ## SSL client certificate auth configuration ## 31 | #note: ssl auth may also require editing the httpd config (conf.d/kojihub.conf) 32 | 33 | ## the client username is the common name of the subject of their client certificate 34 | # DNUsernameComponent = CN 35 | ## separate multiple DNs with | 36 | # ProxyDNs = /C=US/ST=Massachusetts/O=Example Org/OU=Example User/CN=example/emailAddress=example@example.com 37 | 38 | ## end SSL client certificate auth configuration 39 | 40 | 41 | 42 | ## Other options ## 43 | LoginCreatesUser = On 44 | KojiWebURL = https://kojidev.example.com/koji 45 | # The domain name that will be appended to Koji usernames 46 | # when creating email notifications 47 | #EmailDomain = example.com 48 | # whether to send the task owner and package owner email or not on success. this still goes to watchers 49 | NotifyOnSuccess = False 50 | ## Disables all notifications 51 | DisableNotifications = True 52 | 53 | ## Resource limits ## 54 | ## All standard RLIMIT_* can be set here 55 | # RLIMIT_AS = 56 | # RLIMIT_CORE = 57 | # RLIMIT_CPU = 58 | # RLIMIT_DATA = 59 | # RLIMIT_FSIZE = 60 | # RLIMIT_MEMLOCK = 61 | # RLIMIT_NOFILE = 62 | # RLIMIT_NPROC = 63 | # RLIMIT_OFILE = 64 | # RLIMIT_RSS = 65 | # RLIMIT_STACK = 66 | 67 | ## If memory consumption raises during handling request for more 68 | ## than MemoryWarnThreshold kilobytes, warning is emitted to log 69 | # MemoryWarnThreshold = 5000 70 | 71 | ## Maximum request length can be limited on python-side 72 | # MaxRequestLength = 4194304 73 | 74 | 75 | ## Extended features 76 | ## Support Maven builds 77 | # EnableMaven = False 78 | ## Support Windows builds 79 | # EnableWin = False 80 | 81 | ## Koji hub plugins 82 | ## The path where plugins are found 83 | # PluginPath = /usr/lib/koji-hub-plugins 84 | ## A space-separated list of plugins to load 85 | # Plugins = echo 86 | 87 | ## If KojiDebug is on, the hub will be /very/ verbose and will report exception 88 | ## details to clients for anticipated errors (i.e. koji's own exceptions -- 89 | ## subclasses of koji.GenericError). 90 | # KojiDebug = On 91 | # 92 | ## Log level/format for python logging module at hub 93 | # LogLevel = WARNING 94 | # LogFormat = %(asctime)s [%(levelname)s] m=%(method)s u=%(user_name)s p=%(process)s r=%(remoteaddr)s %(name)s: %(message)s' 95 | # 96 | # 97 | ## If VerbosePolicy (or KojiDebug) is on, 'policy violation' 98 | ## messages will contain also policy rule which caused this denial 99 | ## VerbosePolicy = False 100 | # 101 | ## If MissingPolicyOk is on, and given policy is not set up, 102 | ## policy test will pass as ok. If 'deny' result is desired, set it 103 | ## to off 104 | # MissingPolicyOk = True 105 | # 106 | ## Determines how much detail about exceptions is reported to the client (via faults) 107 | ## Meaningful values: 108 | ## normal - a basic traceback (format_exception) 109 | ## extended - an extended traceback (format_exc_plus) 110 | ## anything else - no traceback, just the error message 111 | ## The extended traceback is intended for debugging only and should NOT be 112 | ## used in production, since it may contain sensitive information. 113 | # KojiTraceback = normal 114 | 115 | ## These options are intended for planned outages 116 | # ServerOffline = False 117 | # OfflineMessage = temporary outage 118 | # LockOut = False 119 | ## If ServerOffline is True, the server will always report a ServerOffline fault (with 120 | ## OfflineMessage as the fault string). 121 | ## If LockOut is True, the server will report a ServerOffline fault for all non-admin 122 | ## requests. 123 | -------------------------------------------------------------------------------- /roles/koji-hub/files/kojihub.conf: -------------------------------------------------------------------------------- 1 | # 2 | # koji-hub is an xmlrpc interface to the Koji database 3 | # 4 | 5 | Alias /kojihub /usr/share/koji-hub/kojiapp.py 6 | # Local Git clone: 7 | #Alias /kojihub /usr/local/koji/kojihub/app/kojiapp.py 8 | 9 | 10 | Options ExecCGI 11 | SetHandler wsgi-script 12 | WSGIApplicationGroup %{GLOBAL} 13 | # ^ works around a hub issue with OpenSSL 14 | # see: https://cryptography.io/en/latest/faq/#starting-cryptography-using-mod-wsgi-produces-an-internalerror-during-a-call-in-register-osrandom-engine 15 | WSGIScriptReloading Off 16 | # ^ reloading breaks hub "firstcall" check 17 | # see: https://pagure.io/koji/issue/875 18 | 19 | Order allow,deny 20 | Allow from all 21 | 22 | = 2.4> 23 | Require all granted 24 | 25 | 26 | 27 | # Local Git clone: 28 | # 29 | # Options ExecCGI 30 | # SetHandler wsgi-script 31 | # WSGIApplicationGroup %{GLOBAL} 32 | # Require all granted 33 | # 34 | 35 | # Also serve /mnt/koji 36 | Alias /kojifiles "/mnt/koji/" 37 | 38 | 39 | Options Indexes SymLinksIfOwnerMatch 40 | #If your top /mnt/koji directory is not owned by the httpd user, then 41 | #you will need to follow all symlinks instead, e.g. 42 | #Options Indexes FollowSymLinks 43 | AllowOverride None 44 | IndexOptions +NameWidth=* 45 | 46 | Order allow,deny 47 | Allow from all 48 | 49 | = 2.4> 50 | Require all granted 51 | 52 | 53 | 54 | # uncomment this to enable authentication via SSL client certificates 55 | # 56 | # SSLVerifyClient require 57 | # SSLVerifyDepth 10 58 | # SSLOptions +StdEnvVars 59 | # 60 | 61 | # GSSAPI authentication: 62 | 63 | AuthType GSSAPI 64 | AuthName "GSSAPI Single Sign On Login" 65 | GssapiCredStore keytab:/var/local/HTTP.kojidev.example.com.keytab 66 | Require valid-user 67 | 68 | 69 | -------------------------------------------------------------------------------- /roles/koji-hub/files/ssl.conf: -------------------------------------------------------------------------------- 1 | # 2 | # When we also provide SSL we have to listen to the 3 | # the HTTPS port in addition. 4 | # 5 | Listen 443 https 6 | 7 | ## 8 | ## SSL Global Context 9 | ## 10 | ## All SSL configuration in this context applies both to 11 | ## the main server and all SSL-enabled virtual hosts. 12 | ## 13 | 14 | # Pass Phrase Dialog: 15 | # Configure the pass phrase gathering process. 16 | # The filtering dialog program (`builtin' is a internal 17 | # terminal dialog) has to provide the pass phrase on stdout. 18 | SSLPassPhraseDialog exec:/usr/libexec/httpd-ssl-pass-dialog 19 | 20 | # Inter-Process Session Cache: 21 | # Configure the SSL Session Cache: First the mechanism 22 | # to use and second the expiring timeout (in seconds). 23 | SSLSessionCache shmcb:/run/httpd/sslcache(512000) 24 | SSLSessionCacheTimeout 300 25 | 26 | # Pseudo Random Number Generator (PRNG): 27 | # Configure one or more sources to seed the PRNG of the 28 | # SSL library. The seed data should be of good random quality. 29 | # WARNING! On some platforms /dev/random blocks if not enough entropy 30 | # is available. This means you then cannot use the /dev/random device 31 | # because it would lead to very long connection times (as long as 32 | # it requires to make more entropy available). But usually those 33 | # platforms additionally provide a /dev/urandom device which doesn't 34 | # block. So, if available, use this one instead. Read the mod_ssl User 35 | # Manual for more details. 36 | SSLRandomSeed startup file:/dev/urandom 256 37 | SSLRandomSeed connect builtin 38 | #SSLRandomSeed startup file:/dev/random 512 39 | #SSLRandomSeed connect file:/dev/random 512 40 | #SSLRandomSeed connect file:/dev/urandom 512 41 | 42 | # 43 | # Use "SSLCryptoDevice" to enable any supported hardware 44 | # accelerators. Use "openssl engine -v" to list supported 45 | # engine names. NOTE: If you enable an accelerator and the 46 | # server does not start, consult the error logs and ensure 47 | # your accelerator is functioning properly. 48 | # 49 | SSLCryptoDevice builtin 50 | #SSLCryptoDevice ubsec 51 | 52 | ## 53 | ## SSL Virtual Host Context 54 | ## 55 | 56 | 57 | 58 | # General setup for the virtual host, inherited from global configuration 59 | #DocumentRoot "/var/www/html" 60 | #ServerName www.example.com:443 61 | 62 | # Use separate log files for the SSL virtual host; note that LogLevel 63 | # is not inherited from httpd.conf. 64 | ErrorLog logs/ssl_error_log 65 | TransferLog logs/ssl_access_log 66 | LogLevel warn 67 | 68 | # SSL Engine Switch: 69 | # Enable/Disable SSL for this virtual host. 70 | SSLEngine on 71 | 72 | # SSL Protocol support: 73 | # List the enable protocol levels with which clients will be able to 74 | # connect. Disable SSLv2 access by default: 75 | SSLProtocol all -SSLv2 -SSLv3 76 | 77 | # SSL Cipher Suite: 78 | # List the ciphers that the client is permitted to negotiate. 79 | # See the mod_ssl documentation for a complete list. 80 | SSLCipherSuite HIGH:3DES:!aNULL:!MD5:!SEED:!IDEA 81 | 82 | # Speed-optimized SSL Cipher configuration: 83 | # If speed is your main concern (on busy HTTPS servers e.g.), 84 | # you might want to force clients to specific, performance 85 | # optimized ciphers. In this case, prepend those ciphers 86 | # to the SSLCipherSuite list, and enable SSLHonorCipherOrder. 87 | # Caveat: by giving precedence to RC4-SHA and AES128-SHA 88 | # (as in the example below), most connections will no longer 89 | # have perfect forward secrecy - if the server's key is 90 | # compromised, captures of past or future traffic must be 91 | # considered compromised, too. 92 | #SSLCipherSuite RC4-SHA:AES128-SHA:HIGH:MEDIUM:!aNULL:!MD5 93 | #SSLHonorCipherOrder on 94 | 95 | # Server Certificate: 96 | # Point SSLCertificateFile at a PEM encoded certificate. If 97 | # the certificate is encrypted, then you will be prompted for a 98 | # pass phrase. Note that a kill -HUP will prompt again. A new 99 | # certificate can be generated using the genkey(1) command. 100 | SSLCertificateFile /etc/pki/koji/kojidev.example.com.chain.crt 101 | 102 | # Server Private Key: 103 | # If the key is not combined with the certificate, use this 104 | # directive to point at the key file. Keep in mind that if 105 | # you've both a RSA and a DSA private key you can configure 106 | # both in parallel (to also allow the use of DSA ciphers, etc.) 107 | SSLCertificateKeyFile /etc/pki/koji/kojidev.example.com.key 108 | 109 | # Server Certificate Chain: 110 | # Point SSLCertificateChainFile at a file containing the 111 | # concatenation of PEM encoded CA certificates which form the 112 | # certificate chain for the server certificate. Alternatively 113 | # the referenced file can be the same as SSLCertificateFile 114 | # when the CA certificates are directly appended to the server 115 | # certificate for convinience. 116 | SSLCertificateChainFile /etc/pki/koji/kojidev.example.com.chain.crt 117 | 118 | # Certificate Authority (CA): 119 | # Set the CA certificate verification path where to find CA 120 | # certificates for client authentication or alternatively one 121 | # huge file containing all of them (file must be PEM encoded) 122 | SSLCACertificateFile /etc/pki/koji/koji-ca.crt 123 | 124 | # Client Authentication (Type): 125 | # Client certificate verification type and depth. Types are 126 | # none, optional, require and optional_no_ca. Depth is a 127 | # number which specifies how deeply to verify the certificate 128 | # issuer chain before deciding the certificate is not valid. 129 | #SSLVerifyClient require 130 | #SSLVerifyDepth 10 131 | 132 | # Access Control: 133 | # With SSLRequire you can do per-directory access control based 134 | # on arbitrary complex boolean expressions containing server 135 | # variable checks and other lookup directives. The syntax is a 136 | # mixture between C and Perl. See the mod_ssl documentation 137 | # for more details. 138 | # 139 | #SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)/ \ 140 | # and %{SSL_CLIENT_S_DN_O} eq "Snake Oil, Ltd." \ 141 | # and %{SSL_CLIENT_S_DN_OU} in {"Staff", "CA", "Dev"} \ 142 | # and %{TIME_WDAY} >= 1 and %{TIME_WDAY} <= 5 \ 143 | # and %{TIME_HOUR} >= 8 and %{TIME_HOUR} <= 20 ) \ 144 | # or %{REMOTE_ADDR} =~ m/^192\.76\.162\.[0-9]+$/ 145 | # 146 | 147 | # SSL Engine Options: 148 | # Set various options for the SSL engine. 149 | # o FakeBasicAuth: 150 | # Translate the client X.509 into a Basic Authorisation. This means that 151 | # the standard Auth/DBMAuth methods can be used for access control. The 152 | # user name is the `one line' version of the client's X.509 certificate. 153 | # Note that no password is obtained from the user. Every entry in the user 154 | # file needs this password: `xxj31ZMTZzkVA'. 155 | # o ExportCertData: 156 | # This exports two additional environment variables: SSL_CLIENT_CERT and 157 | # SSL_SERVER_CERT. These contain the PEM-encoded certificates of the 158 | # server (always existing) and the client (only existing when client 159 | # authentication is used). This can be used to import the certificates 160 | # into CGI scripts. 161 | # o StdEnvVars: 162 | # This exports the standard SSL/TLS related `SSL_*' environment variables. 163 | # Per default this exportation is switched off for performance reasons, 164 | # because the extraction step is an expensive operation and is usually 165 | # useless for serving static content. So one usually enables the 166 | # exportation for CGI and SSI requests only. 167 | # o StrictRequire: 168 | # This denies access when "SSLRequireSSL" or "SSLRequire" applied even 169 | # under a "Satisfy any" situation, i.e. when it applies access is denied 170 | # and no other module can change it. 171 | # o OptRenegotiate: 172 | # This enables optimized SSL connection renegotiation handling when SSL 173 | # directives are used in per-directory context. 174 | #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire 175 | 176 | SSLOptions +StdEnvVars 177 | 178 | 179 | SSLOptions +StdEnvVars 180 | 181 | 182 | # SSL Protocol Adjustments: 183 | # The safe and default but still SSL/TLS standard compliant shutdown 184 | # approach is that mod_ssl sends the close notify alert but doesn't wait for 185 | # the close notify alert from client. When you need a different shutdown 186 | # approach you can use one of the following variables: 187 | # o ssl-unclean-shutdown: 188 | # This forces an unclean shutdown when the connection is closed, i.e. no 189 | # SSL close notify alert is send or allowed to received. This violates 190 | # the SSL/TLS standard but is needed for some brain-dead browsers. Use 191 | # this when you receive I/O errors because of the standard approach where 192 | # mod_ssl sends the close notify alert. 193 | # o ssl-accurate-shutdown: 194 | # This forces an accurate shutdown when the connection is closed, i.e. a 195 | # SSL close notify alert is send and mod_ssl waits for the close notify 196 | # alert of the client. This is 100% SSL/TLS standard compliant, but in 197 | # practice often causes hanging connections with brain-dead browsers. Use 198 | # this only for browsers where you know that their SSL implementation 199 | # works correctly. 200 | # Notice: Most problems of broken clients are also related to the HTTP 201 | # keep-alive facility, so you usually additionally want to disable 202 | # keep-alive for those clients, too. Use variable "nokeepalive" for this. 203 | # Similarly, one has to force some clients to use HTTP/1.0 to workaround 204 | # their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and 205 | # "force-response-1.0" for this. 206 | BrowserMatch "MSIE [2-5]" \ 207 | nokeepalive ssl-unclean-shutdown \ 208 | downgrade-1.0 force-response-1.0 209 | 210 | # Per-Server Logging: 211 | # The home of a custom SSL log file. Use this when you want a 212 | # compact non-error SSL logfile on a virtual host basis. 213 | CustomLog logs/ssl_request_log \ 214 | "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /roles/koji-hub/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: restart httpd 2 | service: 3 | name: httpd 4 | state: restarted 5 | -------------------------------------------------------------------------------- /roles/koji-hub/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Gather OS-specific variables 2 | include_vars: "{{ item }}" 3 | with_first_found: 4 | - "{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml" 5 | - "{{ ansible_distribution }}.yml" 6 | 7 | - name: Install SELinux packages 8 | package: 9 | name: "{{ selinux_packages }}" 10 | state: present 11 | register: selinux_packages_installed 12 | 13 | - name: re-gather facts after installing SELinux packages 14 | setup: 15 | when: 16 | - not ansible_selinux_python_present 17 | - selinux_packages_installed.changed 18 | 19 | - name: Install Koji packages 20 | package: 21 | name: 22 | - koji-hub 23 | - mod_ssl 24 | - mod_auth_gssapi 25 | state: present 26 | 27 | - name: allow httpd selinux access to write public_content_rw_t files 28 | seboolean: 29 | name: httpd_anon_write 30 | state: true 31 | persistent: true 32 | when: 33 | - ansible_selinux.status != "disabled" 34 | notify: 35 | - restart httpd 36 | 37 | - name: create koji user 38 | user: 39 | name: koji 40 | shell: /bin/bash 41 | 42 | - name: create postgres user koji 43 | postgresql_user: 44 | name: koji 45 | password: test 46 | # priv: "CONNECT/koji:ALL" 47 | # login_unix_socket: /var/run/postgresql 48 | become_user: postgres 49 | 50 | - name: create postgres koji database 51 | postgresql_db: 52 | name: koji 53 | owner: koji 54 | # login_unix_socket: /var/run/postgresql 55 | become_user: postgres 56 | 57 | - name: check if database for app needs populating 58 | postgresql_query: 59 | db: koji 60 | login_user: koji 61 | query: SELECT COUNT(*) FROM permissions; 62 | become_user: koji 63 | ignore_errors: true 64 | register: database_is_populated 65 | 66 | - name: populate the koji database 67 | shell: psql koji koji < /usr/share/doc/koji*/docs/schema.sql 68 | args: 69 | executable: /bin/bash 70 | when: database_is_populated.failed 71 | changed_when: true 72 | become_user: koji 73 | 74 | - name: insert kdreyer as a user 75 | postgresql_query: 76 | db: koji 77 | login_user: koji 78 | query: "INSERT INTO users (name, status, usertype) VALUES ('kdreyer', 0, 0);" 79 | when: database_is_populated.failed 80 | become_user: koji 81 | 82 | - name: grant kdreyer admin rights 83 | postgresql_query: 84 | db: koji 85 | login_user: koji 86 | query: "INSERT INTO user_perms (user_id, perm_id, creator_id) VALUES (1, 1, 1);" 87 | when: database_is_populated.failed 88 | become_user: koji 89 | 90 | - name: grant apache selinux access to HTTP keytab 91 | sefcontext: 92 | target: /var/local/HTTP.kojidev.example.com.keytab 93 | setype: httpd_config_t 94 | state: present 95 | when: 96 | - ansible_selinux.status != "disabled" 97 | notify: 98 | - restart httpd 99 | 100 | - name: grant apache read access to HTTP keytab 101 | file: 102 | path: /var/local/HTTP.kojidev.example.com.keytab 103 | owner: root 104 | group: apache 105 | mode: "0640" 106 | setype: httpd_config_t 107 | notify: 108 | - restart httpd 109 | 110 | - name: copy mod_ssl config 111 | copy: 112 | src: files/ssl.conf 113 | dest: /etc/httpd/conf.d/ssl.conf 114 | owner: root 115 | group: root 116 | mode: "0644" 117 | notify: 118 | - restart httpd 119 | 120 | - name: copy /etc/httpd/conf.d/kojihub.conf 121 | copy: 122 | src: files/kojihub.conf 123 | dest: /etc/httpd/conf.d/kojihub.conf 124 | owner: root 125 | group: root 126 | mode: "0644" 127 | notify: 128 | - restart httpd 129 | 130 | - name: copy /etc/koji-hub/hub.conf 131 | copy: 132 | src: files/hub.conf 133 | dest: /etc/koji-hub/hub.conf 134 | owner: root 135 | group: root 136 | mode: "0644" 137 | notify: 138 | - restart httpd 139 | 140 | - name: start httpd 141 | service: 142 | name: httpd 143 | enabled: true 144 | state: started 145 | 146 | - name: grant apache selinux access to /mnt/koji 147 | sefcontext: 148 | target: "/mnt/koji(/.*)?" 149 | setype: public_content_rw_t 150 | state: present 151 | when: 152 | - ansible_selinux.status != "disabled" 153 | 154 | - name: /mnt/koji directories 155 | file: 156 | path: /mnt/koji/{{ item }} 157 | state: directory 158 | mode: "0755" 159 | owner: apache 160 | group: apache 161 | setype: public_content_rw_t 162 | with_items: 163 | - 164 | - packages 165 | - repos 166 | - work 167 | - scratch 168 | - repos-dist 169 | 170 | - name: link our Koji CA to a public web-accessible location 171 | file: 172 | src: /etc/pki/koji/koji-ca.crt 173 | dest: /mnt/koji/koji-ca.crt 174 | state: link 175 | 176 | - name: Configure the new builder on the hub 177 | koji_host: 178 | koji: kojidev 179 | name: kojidev.example.com 180 | arches: [x86_64] 181 | state: enabled 182 | channels: 183 | - default 184 | - createrepo 185 | become: false 186 | 187 | - name: Configure the kojira user account 188 | koji_user: 189 | koji: kojidev 190 | name: kojira 191 | state: enabled 192 | permissions: 193 | - repo 194 | krb_principals: 195 | - koji/kojira@KOJIDEV.EXAMPLE.COM 196 | become: false 197 | 198 | - name: Configure the garbagecollector user account 199 | koji_user: 200 | koji: kojidev 201 | name: garbagecollector 202 | state: enabled 203 | permissions: 204 | - admin 205 | krb_principals: 206 | - koji/garbagecollector@KOJIDEV.EXAMPLE.COM 207 | become: false 208 | 209 | - name: trashcan tag 210 | koji_tag: 211 | koji: kojidev 212 | name: trashcan 213 | become: false 214 | -------------------------------------------------------------------------------- /roles/koji-hub/vars/CentOS-7.yml: -------------------------------------------------------------------------------- 1 | RedHat-7.yml -------------------------------------------------------------------------------- /roles/koji-hub/vars/CentOS-8.yml: -------------------------------------------------------------------------------- 1 | RedHat-8.yml -------------------------------------------------------------------------------- /roles/koji-hub/vars/RedHat-7.yml: -------------------------------------------------------------------------------- 1 | selinux_packages: 2 | - libsemanage-python 3 | - policycoreutils-python 4 | -------------------------------------------------------------------------------- /roles/koji-hub/vars/RedHat-8.yml: -------------------------------------------------------------------------------- 1 | selinux_packages: 2 | - python3-libsemanage 3 | - python3-policycoreutils 4 | -------------------------------------------------------------------------------- /roles/koji-hub/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /roles/koji-ra/files/kojira.conf: -------------------------------------------------------------------------------- 1 | [kojira] 2 | ; The URL for the koji hub server 3 | server=https://kojidev.example.com/kojihub 4 | 5 | ; The directory containing the repos/ directory 6 | topdir=/mnt/koji 7 | 8 | ; Logfile 9 | logfile=/var/log/kojira.log 10 | 11 | ;configuration for Kerberos authentication 12 | 13 | ;the kerberos principal to use 14 | principal=koji/kojira@KOJIDEV.EXAMPLE.COM 15 | 16 | ;location of the keytab 17 | keytab = /var/local/koji.kojira.keytab 18 | 19 | ;configuration for SSL authentication 20 | 21 | ;client certificate 22 | ;cert = /etc/kojira/client.crt 23 | 24 | ;certificate of the CA that issued the HTTP server certificate 25 | ;serverca = /etc/kojira/serverca.crt 26 | 27 | ;how soon (in seconds) to clean up expired repositories. 1 week default 28 | ;deleted_repo_lifetime = 604800 29 | 30 | ;how soon (in seconds) to clean up dist repositories. 1 week default here too 31 | ;dist_repo_lifetime = 604800 32 | 33 | ;turn on debugging statements in the log 34 | ;debug = false 35 | 36 | ; ignored repositories according to glob. Multiple masks separated by space. 37 | ; ignore_tags = 38 | 39 | 40 | ; Monitor external repos and trigger the appropriate Koji repo regenerations 41 | ; when they change. Note that you need to have your database set to use UTC, 42 | ; as otherwise you can end with weird behaviour. For details see 43 | ; https://pagure.io/koji/issue/2159 44 | ; check_external_repos = false 45 | -------------------------------------------------------------------------------- /roles/koji-ra/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: restart kojira 2 | service: 3 | name: kojira 4 | state: restarted 5 | -------------------------------------------------------------------------------- /roles/koji-ra/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Add yum repository to work around rhbz1661580 2 | yum_repository: 3 | name: bz1661580 4 | description: work around rhbz1661580 5 | baseurl: https://fedorapeople.org/~ktdreyer/bz1661580/ 6 | gpgcheck: false 7 | when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int == 7 8 | 9 | - name: Install the very latest requests-kerberos (rhbz1661580) 10 | package: 11 | name: 12 | - python-requests-kerberos 13 | - python2-kerberos 14 | state: latest # noqa package-latest 15 | when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int == 7 16 | retries: 100 17 | delay: 3 18 | 19 | - name: Install Koji utils package 20 | package: 21 | name: koji-utils 22 | state: present 23 | 24 | - name: copy /etc/kojira/kojira.conf 25 | copy: 26 | src: files/kojira.conf 27 | dest: /etc/kojira/kojira.conf 28 | owner: root 29 | group: root 30 | mode: "0644" 31 | notify: 32 | - restart kojira 33 | 34 | - name: start kojira 35 | service: 36 | name: kojira 37 | enabled: true 38 | state: started 39 | -------------------------------------------------------------------------------- /roles/koji-ssl-admin/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Gather OS-specific variables 2 | include_vars: "{{ item }}" 3 | with_first_found: 4 | - "{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml" 5 | - "{{ ansible_distribution }}.yml" 6 | 7 | - name: install prerequisites 8 | package: 9 | name: "{{ ssl_admin_prerequisites }}" 10 | state: present 11 | 12 | - name: clone koji-tools.git (rhel 7 compatible) # noqa latest[git] 13 | git: 14 | repo: https://pagure.io/koji-tools.git 15 | dest: /usr/local/koji-tools 16 | # RHEL 7 has Git v1.8.3.1, so it cannot do shallow clones with "depth: 1" 17 | when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int == 7 18 | 19 | - name: clone koji-tools.git # noqa latest[git] 20 | git: 21 | repo: https://pagure.io/koji-tools.git 22 | dest: /usr/local/koji-tools 23 | depth: 1 24 | when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int > 7 25 | 26 | - name: /etc/pki/koji directory 27 | file: 28 | path: /etc/pki/koji 29 | state: directory 30 | mode: "0755" 31 | 32 | - name: Create a Koji SSL CA 33 | command: 34 | argv: 35 | - /usr/local/koji-tools/src/bin/koji-ssl-admin 36 | - new-ca 37 | - --common-name 38 | - Test Koji CA 39 | creates: /etc/pki/koji/koji-ca.crt 40 | chdir: /etc/pki/koji/ 41 | 42 | - name: Create a Server CSR 43 | command: 44 | argv: 45 | - /usr/local/koji-tools/src/bin/koji-ssl-admin 46 | - server-csr 47 | - kojidev.example.com 48 | creates: /etc/pki/koji/kojidev.example.com.csr 49 | chdir: /etc/pki/koji/ 50 | 51 | - name: Sign Server CSR 52 | command: 53 | argv: 54 | - /usr/local/koji-tools/src/bin/koji-ssl-admin 55 | - sign 56 | - kojidev.example.com.csr 57 | creates: /etc/pki/koji/kojidev.example.com.chain.crt 58 | chdir: /etc/pki/koji/ 59 | -------------------------------------------------------------------------------- /roles/koji-ssl-admin/vars/CentOS-7.yml: -------------------------------------------------------------------------------- 1 | RedHat-7.yml -------------------------------------------------------------------------------- /roles/koji-ssl-admin/vars/CentOS-8.yml: -------------------------------------------------------------------------------- 1 | RedHat-8.yml -------------------------------------------------------------------------------- /roles/koji-ssl-admin/vars/RedHat-7.yml: -------------------------------------------------------------------------------- 1 | ssl_admin_prerequisites: 2 | - git-core 3 | - python3 4 | - python36-cryptography 5 | - python36-dateutil 6 | -------------------------------------------------------------------------------- /roles/koji-ssl-admin/vars/RedHat-8.yml: -------------------------------------------------------------------------------- 1 | ssl_admin_prerequisites: 2 | - git-core 3 | - python3 4 | - python3-cryptography 5 | - python3-dateutil 6 | -------------------------------------------------------------------------------- /roles/koji-web/files/kojiweb.conf: -------------------------------------------------------------------------------- 1 | #We use wsgi by default 2 | Alias /koji "/usr/share/koji-web/scripts/wsgi_publisher.py" 3 | #(configuration goes in /etc/kojiweb/web.conf) 4 | 5 | 6 | Options ExecCGI 7 | SetHandler wsgi-script 8 | WSGIApplicationGroup %{GLOBAL} 9 | # ^ works around an OpenSSL issue 10 | # see: https://cryptography.io/en/latest/faq/#starting-cryptography-using-mod-wsgi-produces-an-internalerror-during-a-call-in-register-osrandom-engine 11 | 12 | Order allow,deny 13 | Allow from all 14 | 15 | = 2.4> 16 | Require all granted 17 | 18 | 19 | 20 | # uncomment this to enable authentication via Kerberos 21 | 22 | AuthType GSSAPI 23 | AuthName "Koji Web UI" 24 | GssapiCredStore keytab:/var/local/HTTP.kojidev.example.com.keytab 25 | Require valid-user 26 | ErrorDocument 401 /koji-static/errors/unauthorized.html 27 | 28 | 29 | # uncomment this to enable authentication via SSL client certificates 30 | # 31 | # SSLVerifyClient require 32 | # SSLVerifyDepth 10 33 | # SSLOptions +StdEnvVars 34 | # 35 | 36 | Alias /koji-static/ "/usr/share/koji-web/static/" 37 | 38 | 39 | Options None 40 | AllowOverride None 41 | 42 | Order allow,deny 43 | Allow from all 44 | 45 | = 2.4> 46 | Require all granted 47 | 48 | 49 | -------------------------------------------------------------------------------- /roles/koji-web/files/web.conf: -------------------------------------------------------------------------------- 1 | [web] 2 | SiteName = koji 3 | # KojiTheme = mytheme 4 | 5 | # Key urls 6 | KojiHubURL = https://kojidev.example.com/kojihub 7 | KojiFilesURL = https://kojidev.example.com/kojifiles 8 | 9 | # Kerberos authentication options 10 | WebPrincipal = koji/kojiweb@KOJIDEV.EXAMPLE.COM 11 | WebKeytab = /var/local/koji.kojiweb.keytab 12 | WebCCache = /var/tmp/kojiweb.ccache 13 | # The service name of the principal being used by the hub 14 | # KrbService = host 15 | 16 | # SSL authentication options 17 | #WebCert = /etc/kojiweb/kojiweb.cert 18 | KojiHubCA = /etc/pki/koji/koji-ca.crt 19 | 20 | LoginTimeout = 72 21 | 22 | # This must be changed and uncommented before deployment 23 | # Secret = CHANGE_ME 24 | Secret = f447905c-9314-43dc-acdb-f1e5ddfc63fa 25 | 26 | LibPath = /usr/share/koji-web/lib 27 | 28 | # If set to True, then the footer will be included literally. 29 | # If False, then the footer will be included as another Kid Template. 30 | # Defaults to True 31 | LiteralFooter = True 32 | 33 | # This can be a space-delimited list of the numeric IDs of users that you want 34 | # to hide from tasks listed on the front page. You might want to, for instance, 35 | # hide the activity of an account used for continuous integration. 36 | # HiddenUsers = 5372 1234 37 | 38 | # Task types visible in pulldown menu on tasks page. 39 | # Tasks = 40 | # runroot plugin provided via main package could be listed as: 41 | # Tasks = runroot 42 | # Tasks that can exist without a parent 43 | # ToplevelTasks = 44 | # Tasks that can have children 45 | # ParentTasks = 46 | 47 | # Uncommenting this will show python tracebacks in the webUI, but they are the 48 | # same as what you will see in apache's error_log. 49 | # Not for production use 50 | # PythonDebug = True 51 | -------------------------------------------------------------------------------- /roles/koji-web/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: restart httpd 2 | service: 3 | name: httpd 4 | state: restarted 5 | -------------------------------------------------------------------------------- /roles/koji-web/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Install Koji web packages 2 | package: 3 | name: koji-web 4 | state: present 5 | 6 | - name: copy /etc/httpd/conf.d/kojiweb.conf 7 | copy: 8 | src: files/kojiweb.conf 9 | dest: /etc/httpd/conf.d/kojiweb.conf 10 | owner: root 11 | group: root 12 | mode: "0644" 13 | notify: 14 | - restart httpd 15 | 16 | - name: copy /etc/kojiweb/web.conf 17 | copy: 18 | src: files/web.conf 19 | dest: /etc/kojiweb/web.conf 20 | owner: root 21 | group: root 22 | mode: "0644" 23 | notify: 24 | - restart httpd 25 | 26 | - name: grant apache selinux access to kojiweb keytab 27 | sefcontext: 28 | target: /var/local/koji.kojiweb.keytab 29 | setype: httpd_config_t 30 | state: present 31 | when: 32 | - ansible_selinux.status != "disabled" 33 | notify: 34 | - restart httpd 35 | 36 | - name: grant apache read access to kojiweb keytab 37 | file: 38 | path: /var/local/koji.kojiweb.keytab 39 | owner: root 40 | group: apache 41 | mode: "0640" 42 | setype: httpd_config_t 43 | notify: 44 | - restart httpd 45 | 46 | - name: allow httpd selinux access to contact kerberos and koji-hub 47 | seboolean: 48 | name: httpd_can_network_connect 49 | state: true 50 | persistent: true 51 | when: 52 | - ansible_selinux.status != "disabled" 53 | notify: 54 | - restart httpd 55 | -------------------------------------------------------------------------------- /roles/postgresql/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: reload postgresql 2 | service: 3 | name: postgresql 4 | state: reloaded 5 | -------------------------------------------------------------------------------- /roles/postgresql/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Install postgresql-server package 2 | package: 3 | name: postgresql-server 4 | state: present 5 | 6 | - name: initialize postgresql 7 | command: postgresql-setup initdb 8 | args: 9 | creates: /var/lib/pgsql/data/pg_hba.conf 10 | notify: 11 | - reload postgresql 12 | 13 | - name: configure pg_hba.conf 14 | copy: 15 | dest: /var/lib/pgsql/data/pg_hba.conf 16 | content: | 17 | local koji koji trust 18 | local all postgres peer 19 | mode: preserve 20 | notify: 21 | - reload postgresql 22 | 23 | - name: disable TCP/IP for postgres 24 | lineinfile: 25 | dest: /var/lib/pgsql/data/postgresql.conf 26 | regexp: '^#listen_addresses' 27 | line: "listen_addresses = ''" 28 | notify: 29 | - reload postgresql 30 | 31 | # Note: this is not in the upstream documentation. It's still under discussion 32 | # upstream, see 33 | # https://lists.fedorahosted.org/archives/list/koji-devel@lists.fedorahosted.org/thread/NMDIDYS7CZWB3SMPT6UO2P5WGZXKIZVW/ 34 | - name: increase number of max connections 35 | lineinfile: 36 | dest: /var/lib/pgsql/data/postgresql.conf 37 | regexp: '^max_connections' 38 | line: "max_connections = 500" 39 | notify: 40 | - reload postgresql 41 | when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int > 7 42 | 43 | - name: start postgresql 44 | service: 45 | name: postgresql 46 | state: started 47 | enabled: true 48 | -------------------------------------------------------------------------------- /roles/rabbitmq/files/rabbitmq.conf: -------------------------------------------------------------------------------- 1 | # Basic SSL configuration: 2 | listeners.ssl.default = 5671 3 | ssl_options.certfile = /etc/pki/koji/kojidev.example.com.chain.crt 4 | ssl_options.keyfile = /etc/pki/koji/kojidev.example.com.key 5 | ssl_options.cacertfile = /etc/pki/koji/koji-ca.crt 6 | ssl_options.verify = verify_peer 7 | ssl_options.fail_if_no_peer_cert = true 8 | 9 | # Enable mTLS auth: 10 | auth_backends.1 = rabbit_auth_backend_internal 11 | auth_mechanisms.1 = PLAIN 12 | auth_mechanisms.2 = EXTERNAL 13 | # TODO: change this to "UID"? 14 | ssl_cert_login_from = common_name 15 | 16 | # Secure the management interface on TCP 15671: 17 | management.ssl.port = 15671 18 | management.ssl.certfile = /etc/pki/koji/kojidev.example.com.chain.crt 19 | management.ssl.keyfile = /etc/pki/koji/kojidev.example.com.key 20 | management.ssl.cacertfile = /etc/pki/koji/koji-ca.crt 21 | 22 | # Disable non-TLS connections: 23 | listeners.tcp = none 24 | # management.tcp = none is an invalid config. maybe it doesn't listen to HTTP 25 | # by default? 26 | # management.tcp = none 27 | -------------------------------------------------------------------------------- /roles/rabbitmq/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: restart rabbitmq-server 2 | service: 3 | name: rabbitmq-server 4 | state: restarted 5 | -------------------------------------------------------------------------------- /roles/rabbitmq/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # https://github.com/fedora-selinux/selinux-policy/pull/1053 2 | - name: Allow RabbitMQ to listen on tcp port 15671 3 | seport: 4 | ports: 15671 5 | proto: tcp 6 | setype: rabbitmq_port_t 7 | state: present 8 | 9 | - name: install centos repo for rabbitmq 10 | package: 11 | name: centos-release-rabbitmq-38 12 | state: present 13 | 14 | - name: enable powertools 15 | ini_file: 16 | path: /etc/yum.repos.d/CentOS-Stream-PowerTools.repo 17 | section: powertools 18 | option: enabled 19 | value: "1" 20 | owner: root 21 | group: root 22 | mode: "0644" 23 | 24 | - name: install rabbitmq-server 25 | package: 26 | name: rabbitmq-server 27 | state: present 28 | 29 | - name: copy /etc/rabbitmq/rabbitmq.conf 30 | copy: 31 | src: files/rabbitmq.conf 32 | dest: /etc/rabbitmq/rabbitmq.conf 33 | owner: root 34 | group: root 35 | mode: "0644" 36 | notify: 37 | - restart rabbitmq-server 38 | 39 | - name: enable rabbitmq plugins 40 | rabbitmq_plugin: 41 | names: rabbitmq_management,rabbitmq_auth_mechanism_ssl,rabbitmq_amqp1_0 42 | state: enabled 43 | notify: 44 | - restart rabbitmq-server 45 | 46 | - name: start rabbitmq-server 47 | service: 48 | name: rabbitmq-server 49 | enabled: true 50 | state: started 51 | 52 | - name: remove rabbitmq guest user 53 | rabbitmq_user: 54 | user: guest 55 | state: absent 56 | 57 | - name: add rabbitmq koji user 58 | rabbitmq_user: 59 | user: msg-producer-koji 60 | vhost: / 61 | configure_priv: .* 62 | read_priv: .* 63 | write_priv: .* 64 | state: present 65 | 66 | - name: add read-only rabbitmq kdreyer user 67 | rabbitmq_user: 68 | user: kdreyer 69 | vhost: / 70 | configure_priv: ^$ 71 | read_priv: .* 72 | write_priv: ^$ 73 | state: present 74 | -------------------------------------------------------------------------------- /setup-koji.yml: -------------------------------------------------------------------------------- 1 | - name: manage Koji VM 2 | hosts: all 3 | become: yes 4 | become_method: sudo 5 | roles: 6 | - kdc 7 | - koji-ssl-admin 8 | - koji-client 9 | - postgresql 10 | - koji-hub 11 | - koji-web 12 | - koji-builder 13 | - koji-ra 14 | - koji-gc 15 | -------------------------------------------------------------------------------- /vagrant/README.rst: -------------------------------------------------------------------------------- 1 | Bring up a new server:: 2 | 3 | vagrant up --no-destroy-on-error 4 | 5 | Re-run the Ansible playbooks (if they failed, or if you changed something):: 6 | 7 | vagrant provision 8 | 9 | Log in with SSH:: 10 | 11 | vagrant ssh 12 | 13 | Destroy the VM and disk:: 14 | 15 | vagrant destroy 16 | 17 | Pull down an updated CentOS image from Vagrant Cloud:: 18 | 19 | vagrant box update 20 | 21 | Or use a centos.org snapshot:: 22 | 23 | vagrant box add --name centos/stream8 -f https://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-Vagrant-8-20230925.0.x86_64.vagrant-libvirt.box 24 | 25 | Note that you must destroy and rebuild your Vagrant VMs to make use of this newer image:: 26 | 27 | vagrant destroy 28 | vagrant up 29 | 30 | Helpful Documentation: 31 | 32 | * https://docs.ansible.com/ansible/latest/scenario_guides/guide_vagrant.html 33 | * https://www.vagrantup.com/docs/provisioning/ansible.html 34 | * https://www.vagrantup.com/docs/provisioning/ansible_common.html 35 | -------------------------------------------------------------------------------- /vagrant/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | 3 | ENV['VAGRANT_DEFAULT_PROVIDER'] = 'libvirt' 4 | 5 | Vagrant.configure("2") do |config| 6 | # Updates sort of lag here, see https://bugs.centos.org/view.php?id=18028 7 | config.vm.box = "centos/stream8" 8 | config.vm.box_check_update = false 9 | 10 | config.vm.provision "ansible" do |ansible| 11 | ansible.playbook = "vagrant.yml" 12 | end 13 | 14 | config.vm.provider :libvirt do |virt| 15 | virt.storage :file, :size => '20G', :device => 'vdb' 16 | virt.memory = 4096 17 | virt.cpus = 2 18 | virt.qemu_use_session = false 19 | end 20 | 21 | config.ssh.forward_agent = true 22 | 23 | config.vm.define :kojidev do |node| 24 | node.vm.hostname = 'kojidev' 25 | end 26 | 27 | # This creates a second NIC, which I do not want: 28 | # config.vm.network :private_network, :ip => '192.168.121.100' 29 | # Also there seems to be a bug with wrapping the network settings in a 30 | # "provider" block: 31 | # https://github.com/vagrant-libvirt/vagrant-libvirt/issues/1165 32 | 33 | end 34 | -------------------------------------------------------------------------------- /vagrant/reset-database.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eux 3 | 4 | # Reset the Koji database to an empty state. 5 | # Useful for starting over when applying large configurations. 6 | 7 | systemctl stop httpd 8 | 9 | sudo -u postgres psql -c 'DROP DATABASE IF EXISTS koji;' 10 | sudo -u postgres psql -c 'DROP USER IF EXISTS koji;' 11 | sudo -u postgres psql -c 'CREATE DATABASE koji;' 12 | sudo -u postgres psql -c "CREATE USER koji WITH ENCRYPTED PASSWORD 'koji';" 13 | sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE koji TO koji;" 14 | 15 | psql -q koji koji < /usr/share/doc/koji/docs/schema.sql 16 | 17 | psql -U koji -c "INSERT INTO users (name, status, usertype) VALUES ('kdreyer', 0, 0);" 18 | psql -U koji -c "INSERT INTO user_perms (user_id, perm_id, creator_id) VALUES (1, 1, 1);" 19 | 20 | systemctl start httpd 21 | -------------------------------------------------------------------------------- /vagrant/roles: -------------------------------------------------------------------------------- 1 | ../roles/ -------------------------------------------------------------------------------- /vagrant/vagrant.yml: -------------------------------------------------------------------------------- 1 | - name: manage Koji Vagrant VM 2 | hosts: all 3 | become: yes 4 | become_method: sudo 5 | pre_tasks: 6 | - name: Install EPEL repo 7 | yum: 8 | name: epel-release 9 | state: present 10 | - name: create filesystem on /dev/vdb 11 | filesystem: 12 | fstype: ext4 13 | dev: /dev/vdb 14 | - name: mount /dev/vdb 15 | mount: 16 | fstype: ext4 17 | src: /dev/vdb 18 | path: /mnt 19 | state: mounted 20 | - name: create /mnt/mock directory 21 | file: 22 | path: /mnt/mock 23 | state: directory 24 | mode: 0755 25 | - name: symlink /var/lib/mock to /mnt/mock 26 | file: 27 | src: /mnt/mock 28 | dest: /var/lib/mock 29 | state: link 30 | roles: 31 | - kdc 32 | - koji-ssl-admin 33 | - koji-client 34 | - postgresql 35 | - koji-hub 36 | - koji-web 37 | - koji-builder 38 | - koji-ra 39 | - koji-gc 40 | --------------------------------------------------------------------------------