feat: Mejorar la configuración y seguridad de Koji

Este commit introduce varias mejoras en los roles de Ansible para Koji:

- **Rol koji_hub:**
  - Se reemplazó `lineinfile` con una plantilla completa para `hub.conf` para mejorar la legibilidad y mantenibilidad.
  - Se aseguró la instalación del paquete `python3-koji-hub-plugins` para soportar el plugin `echo`.
  - Se eliminó una tarea `blockinfile` redundante que duplicaba la configuración de la base de datos en `hub.conf`.
  - Se rehabilitó el plugin `echo` en `hub.conf` después de asegurar su disponibilidad.

- **Rol postgresql:**
  - Se modificó la configuración de `pg_hba.conf` para iterar correctamente sobre todos los hosts en el grupo `kojihub`, asegurando que todas las instancias del hub puedan conectarse a la base de datos.

- **Rol dist_git:**
  - Se implementó el etiquetado persistente de contextos SELinux para los directorios de `dist-git` usando `semanage fcontext` y `restorecon`, asegurando que los contextos persistan tras reetiquetados del sistema de archivos.

- **Rol common:**
  - Se mejoró la seguridad en la generación de claves GPG usando un archivo temporal para el script por lotes, el cual se elimina de forma segura después de la generación de la clave.

- **Configuración Global:**
  - Se introdujo la variable `koji_source_script_path` en `vars/default.yaml` para centralizar la ruta del script `mxos-get-sources.bash`, eliminando rutas hardcodeadas en `kojid_allowed_scms` y las tareas de `kojid`.

- **Documentación:**
  - Se actualizó `docs/instalación.rst` para reflejar los cambios en la configuración de Dist-Git, plugins y variables.
This commit is contained in:
Rénich Bon Ćirić 2025-11-22 11:48:06 -06:00
commit f14c01da0b
Signed by: renich
GPG key ID: AB2A9F49384FE324
25 changed files with 731 additions and 123 deletions

66
bin/dist-git-test.bash Executable file
View file

@ -0,0 +1,66 @@
#!/usr/bin/bash
set -e
distgit_host="cs10-koji-04.mx-os"
repo_name="test-repo.git"
repo_path="/var/lib/dist-git/git/RPMs/${repo_name}"
cache_file="test-file.txt"
cache_path="/var/lib/dist-git/cache/${cache_file}"
test_content="Hello Dist-Git"
echo "Testing Dist-Git functionality on ${distgit_host}..."
# Ensure we can connect via SSH first
if ! ssh -q -o BatchMode=yes -o StrictHostKeyChecking=no root@${distgit_host} exit; then
echo "Error: Cannot SSH to root@${distgit_host}. Please check connectivity and keys."
exit 1
fi
# 1. Setup Test Environment on Server
echo "Setting up test repository and cache file on server..."
ssh -o StrictHostKeyChecking=no root@${distgit_host} "
# Create git repo
if [ -d ${repo_path} ]; then rm -rf ${repo_path}; fi
mkdir -p ${repo_path}
git init --bare ${repo_path}
# Allow anonymous push for testing purposes if needed, though we only test clone here
git -C ${repo_path} config http.receivepack true
chown -R git:git ${repo_path}
# Create cache file
echo '${test_content}' > ${cache_path}
chown git:git ${cache_path}
chmod 644 ${cache_path}
"
# 2. Test Git HTTP Clone
echo "Testing Git HTTP Clone..."
tmp_dir=$(mktemp -d)
cleanup() {
rm -rf "${tmp_dir}"
echo "Cleaning up server..."
ssh -o StrictHostKeyChecking=no root@${distgit_host} "rm -rf ${repo_path} ${cache_path}"
}
trap cleanup EXIT
if git clone -q http://${distgit_host}/RPMs/${repo_name} "${tmp_dir}/clone"; then
echo "[PASS] Git clone successful."
else
echo "[FAIL] Git clone failed."
exit 1
fi
# 3. Test Lookaside Cache Download
echo "Testing Lookaside Cache Download..."
downloaded_content=$(curl -s -f http://${distgit_host}/sources/${cache_file})
if [[ "${downloaded_content}" == "${test_content}" ]]; then
echo "[PASS] Lookaside cache download successful."
else
echo "[FAIL] Lookaside cache download failed or content mismatch."
echo "Expected: ${test_content}"
echo "Got: ${downloaded_content}"
exit 1
fi
echo "All tests passed!"

118
bin/import-srpm.bash Normal file
View file

@ -0,0 +1,118 @@
#!/usr/bin/bash
set -e
if [ -z "$1" ]; then
echo "Usage: $0 <SRPM_URL_OR_FILE>"
exit 1
fi
srpm_input="$1"
temp_dir=$(mktemp -d)
dist_git_base="/var/lib/dist-git"
git_repos="${dist_git_base}/git/RPMs"
cache_dir="${dist_git_base}/cache"
# Ensure we are running as root or can sudo
if [ "$EUID" -ne 0 ]; then
echo "Please run as root"
exit 1
fi
cd "${temp_dir}"
# 1. Get SRPM
if [[ "$srpm_input" == http* ]] || [[ "$srpm_input" == ftp* ]]; then
echo "Downloading ${srpm_input}..."
wget -q "$srpm_input"
srpm_file=$(basename "$srpm_input")
else
cp "$srpm_input" .
srpm_file=$(basename "$srpm_input")
fi
# 2. Get Package Name
pkg_name=$(rpm -qp --queryformat "%{NAME}" "${srpm_file}")
echo "Package: ${pkg_name}"
# 3. Extract SRPM
echo "Extracting SRPM..."
mkdir -p extract
cd extract
rpm2cpio "../${srpm_file}" | cpio -idmv --quiet
# 4. Prepare Dist-Git Repo
repo_dir="${git_repos}/${pkg_name}.git"
echo "Initializing Git Repo at ${repo_dir}..."
if [ ! -d "${repo_dir}" ]; then
mkdir -p "${repo_dir}"
git init --bare "${repo_dir}"
chown -R git:git "${repo_dir}"
# Safe directory config should be global/system, but we can set it per repo to be sure? No, system config handles it.
fi
# 5. Process Files
# Create a temporary git worktree to commit changes
work_dir="${temp_dir}/git-work"
mkdir -p "${work_dir}"
git clone "${repo_dir}" "${work_dir}"
# Move files to Work Dir
mv *.spec "${work_dir}/"
# Identify sources (archives) vs others.
# Simple heuristic: .tar.gz, .tgz, .tar.bz2, .tbz, .zip, .xz are sources.
# Everything else (patches, configs) goes to git.
# BUT we need to be precise. Parsing the SPEC file is hard.
# The "sources" file format is: SHA512 (filename) = hash
# We will treat typical archive extensions as Lookaside Cache candidates.
touch "${work_dir}/sources"
touch "${work_dir}/.gitignore"
for f in *; do
if [[ "$f" == *.spec ]]; then continue; fi # Already moved
# Check if it looks like an archive
if [[ "$f" =~ \.(tar\.|tgz|tbz|zip|xz|bz2|gz) ]]; then
echo "Processing source: $f"
# Calculate SHA512
sum=$(sha512sum "$f" | awk '{print $1}')
# Add to sources file (Dist-Git format)
echo "SHA512 ($f) = $sum" >> "${work_dir}/sources"
# Add to .gitignore
echo "$f" >> "${work_dir}/.gitignore"
# Move to Lookaside Cache
# Structure: package/filename/checksum/filename
cache_path="${cache_dir}/${pkg_name}/$f/$sum"
mkdir -p "${cache_path}"
mv "$f" "${cache_path}/$f"
# Fix permissions
chown -R git:git "${cache_dir}/${pkg_name}"
chmod 644 "${cache_path}/$f"
# Ensure directories are executable
find "${cache_dir}/${pkg_name}" -type d -exec chmod 755 {} +
else
echo "Adding to git: $f"
mv "$f" "${work_dir}/"
fi
done
# 6. Commit and Push
cd "${work_dir}"
git add .
git config user.name "Dist-Git Importer"
git config user.email "importer@mx-os.mx"
git commit -m "Import ${srpm_file}"
git push origin master
# 7. Fix ownership of the bare repo
chown -R git:git "${repo_dir}"
# Cleanup
rm -rf "${temp_dir}"
echo "Import of ${pkg_name} complete."

View file

@ -10,10 +10,6 @@
- role: common
tags: common
- name: Gather facts from all hosts
hosts: all
gather_facts: true
- name: "Setup: Common on all hosts"
hosts: all
vars_files:
@ -43,6 +39,14 @@
hosts: koji_nodes
vars_files:
- vars/default.yaml
pre_tasks:
- name: Gather facts for kojifs host
ansible.builtin.setup:
delegate_to: "{{ item }}"
delegate_facts: true
run_once: true
when: groups['kojifs'] is defined and inventory_hostname in groups['koji_nodes'] # Only gather if kojifs exists and if we're running on a koji_node
loop: "{{ groups['kojifs'] }}"
roles:
- role: nfs_client
tags: nfs_client
@ -65,7 +69,23 @@
vars_files:
- vars/default.yaml
- vars/credentials.yaml
pre_tasks:
- name: Gather facts for kojihub host
ansible.builtin.setup:
delegate_to: "{{ item }}"
delegate_facts: true
run_once: true
when: groups['kojihub'] is defined
loop: "{{ groups['kojihub'] }}"
roles:
- role: kojid
tags: kojid
- name: "Deploy: Dist-Git Server"
hosts: distgit
vars_files:
- vars/default.yaml
roles:
- role: dist_git
tags: dist_git
...

View file

@ -14,10 +14,10 @@ Crea la Autoridad de Certificación (CA):
Crea un directorio dedicado para tu infraestructura PKI de Koji (p. ej., `/etc/pki/koji`).
Genera un certificado de CA Raíz autofirmado y su clave privada correspondiente. Usarás esta CA para firmar todos los demás certificados de componentes de Koji.
Genera Certificados de Componentes y Usuarios:
Para cada componente de Koji que requiere autenticación (koji_hub, koji-web, kojid, kojira y usuarios administradores), genera
Para cada componente de Koji que requiere autenticación (koji_hub, koji-web, kojid, kojira, dist-git y usuarios administradores), genera
una solicitud de firma de certificado (CSR) única y una clave privada.
Certificados del Lado del Servidor (koji_hub, koji-web):
Certificados del Lado del Servidor (koji_hub, koji-web, dist-git):
El Common Name (CN) en estos certificados *debe* coincidir con el
Nombre de Dominio Completo (FQDN) del servidor donde los desplegarás. Estos certificados deben incluir `serverAuth` en su
`extendedKeyUsage`.
@ -32,6 +32,8 @@ Genera Certificados de Componentes y Usuarios:
Para que los usuarios administradores accedan a través del navegador web, genera un paquete PKCS12 (`.p12`) a partir del
certificado y la clave del usuario, que incluya el certificado de la CA.
**Nota de Seguridad**: Para la generación de claves GPG, utiliza archivos temporales seguros que se eliminen inmediatamente después de su uso para evitar exponer la frase de paso.
Fase 2: Configuración de la Base de Datos PostgreSQL
====================================================
Koji usa una base de datos PostgreSQL para almacenar todos los metadatos relacionados con la compilación, la información del usuario y la configuración.
@ -56,7 +58,7 @@ Configura el Acceso a la Base de Datos (`postgresql.conf` y `pg_hba.conf`):
* Configurar `listen_addresses` para permitir conexiones desde Koji Hub (p. ej., `'*'`).
* Aumentar `max_connections` para satisfacer las necesidades de Koji (p. ej., `200`).
Edita `pg_hba.conf` para permitir conexiones desde Koji Hub a la base de datos de Koji con SSL y un método de autenticación seguro (p. ej., `scram-sha-256`). Además, configura el acceso local para el usuario `postgres`.
Edita `pg_hba.conf` para permitir conexiones desde todos los hosts de Koji Hub (si hay varios) a la base de datos de Koji con SSL y un método de autenticación seguro (p. ej., `scram-sha-256`). Además, configura el acceso local para el usuario `postgres`.
Fase 3: Configuración de Koji Hub
=================================
@ -64,14 +66,13 @@ El Koji Hub es el plano de control central para el sistema de compilación Koji.
Instala Koji Hub y Dependencias:
Instala `koji_hub`, `koji-web`, `httpd`, `mod_ssl`, `mod_wsgi`, `python3-cryptography`, `nmap-ncat`, `postgresql` (bibliotecas
de cliente) y `policycoreutils-python-utils`.
de cliente), `policycoreutils-python-utils` y `python3-koji-hub-plugins`.
Configura el Hub (`/etc/koji_hub/hub.conf` y el directorio `.d`):
Configura los parámetros de conexión de la base de datos (`DBName`, `DBUser`, `DBHost`, `DBPass`).
Establece `KojiDir` en la ruta donde Koji almacenará los datos de compilación (p. ej., `/mnt/koji`).
Configura los ajustes de autenticación, incluyendo `DNUsernameComponent` y `ProxyDNs` para la autenticación de certificados de
cliente SSL.
Coloca las configuraciones sensibles (como `DBPass`) in un archivo separado dentro de `/etc/koji_hub/hub.conf.d/` con permisos
restringidos.
Asegúrate de que la configuración de plugins apunte a una ruta válida (p. ej., `/usr/lib/koji-hub-plugins`) y habilita los plugins necesarios (como `echo`).
Configura Apache para el Hub:
Asegúrate de que `mod_ssl` y `mod_wsgi` estén habilitados.
Edita `/etc/httpd/conf.d/ssl.conf` para que apunte al certificado del servidor, la clave y el certificado de la CA de Koji del
@ -82,8 +83,6 @@ Crea la Estructura del Sistema de Archivos de Koji:
Crea los subdirectorios necesarios en `KojiDir` (p. ej., `packages`, `repos`, `work`, `scratch`, `repos-dist`, `certs`,
`clients`).
Asegúrate de que el usuario `apache` tenga la propiedad y los permisos de escritura adecuados en estos directorios.
Despliega Plugins de Koji Hub:
Copia cualquier complemento personalizado de Koji Hub (p. ej., `sigul_sign.py`) a `/usr/lib/koji_hub/plugins`.
Fase 4: Configuración de Koji Web
=================================
@ -96,7 +95,19 @@ Configura Koji Web (`/etc/kojiweb/web.conf` y el directorio `.d`):
Configura Apache para Koji Web:
Configura el bloque `<Location /koji/login>` en `/etc/httpd/conf.d/kojiweb.conf` para que requiera certificados de cliente SSL si los usuarios se autenticarán a través de navegadores web.
Fase 5: Configuración de Kojid (Constructor) y Kojira (Administrador de Repos)
Fase 5: Configuración de Dist-Git
=================================
El sistema Dist-Git gestiona los repositorios Git para las fuentes de los paquetes RPM.
Instalación y Configuración:
Instala `git`, `git-daemon`, `httpd` y `mod_ssl`.
Configura `httpd` para servir los repositorios Git a través de HTTPS, utilizando certificados SSL generados por la CA de Koji.
Asegúrate de que se fuerce el tráfico HTTPS mediante redirecciones.
Directorio y Permisos:
Crea la estructura de directorios para los repositorios Git y la caché de fuentes (lookaside cache).
Asegura que los contextos de SELinux (`httpd_sys_content_t`) sean persistentes mediante `semanage fcontext`.
Fase 6: Configuración de Kojid (Constructor) y Kojira (Administrador de Repos)
==============================================================================
Configura Kojid (en cada host constructor):
@ -106,6 +117,7 @@ Configura Kojid (en cada host constructor):
Establece el parámetro `user` en el FQDN del host constructor.
Establece `topdir` en el directorio de Koji montado (p. ej., `/mnt/koji`).
Establece `workdir` en un directorio local (p. ej., `/var/lib/kojid-work`).
Configura `allowed_scms` para permitir la descarga de fuentes desde tu Dist-Git, utilizando un script wrapper (ej. `mxos-get-sources.bash`) para gestionar la caché de fuentes (lookaside cache).
Asegúrate de que el servicio `kojid` se ejecute como `root` (predeterminado) y tenga los permisos adecuados en `workdir`.
Agrega los usuarios que necesitan compilar paquetes (p. ej., `kojiadmin`) al grupo `mock`.
Configura Kojira (en el hub o en un host dedicado):
@ -115,12 +127,7 @@ Configura Kojira (en el hub o en un host dedicado):
Estos componentes manejan los procesos de compilación reales y la gestión de repositorios.
Configura Kojira (en el hub o en un host dedicado):
Instala `koji-utils`.
Edita `/etc/kojira/kojira.conf` para que apunte a la URL del servidor XML-RPC de Koji Hub.
Especifica el certificado de cliente del componente `kojira` y el certificado de la CA de Koji.
Fase 6: Configuración de SELinux y Firewall
Fase 7: Configuración de SELinux y Firewall
===========================================
Un contexto de seguridad y un acceso a la red adecuados son cruciales para que Koji funcione.
@ -134,18 +141,18 @@ Configuración de SELinux:
Aplica los contextos de archivo de SELinux correctos:
* `semanage fcontext -a -t public_content_rw_t "/mnt/koji(/.*)?"` (para contexto persistente)
* `semanage fcontext -a -t httpd_sys_rw_content_t "/mnt/koji(/.*)?"` (para contexto persistente en directorios de escritura)
* `restorecon -r -v /mnt/koji` (para aplicar el contexto inmediatamente)
* Alternativamente, para recursos compartidos NFS donde `semanage fcontext` podría no aplicarse, usa `chcon -R -t
public_content_rw_t /mnt/koji/*` (aunque esto no es persistente entre reinicios).
Configuración del Firewall:
Abre los puertos de firewall necesarios en los hosts respectivos:
* Koji Hub: HTTP (80/tcp), HTTPS (443/tcp), Servidor Mock Sigul (8080/tcp, si se despliega en el hub).
* Koji Hub: HTTP (80/tcp), HTTPS (443/tcp).
* Servidor PostgreSQL: PostgreSQL (5432/tcp) desde el Koji Hub.
* Servidor NFS: NFS (servicios nfs, rpc-bind, mountd).
* Dist-Git: HTTP (80/tcp), HTTPS (443/tcp), Git (9418/tcp).
Fase 7: Arranque del Entorno de Compilación de Koji
Fase 8: Arranque del Entorno de Compilación de Koji
===================================================
Esta fase final prepara a Koji para que realice compilaciones reales.
@ -172,7 +179,7 @@ Importa Paquetes Iniciales:
Este procedimiento detallado debería servirte como una guía completa para que configures un entorno Koji.
Fase 8: Características Adicionales
Fase 9: Características Adicionales
===================================
Recolección de Basura:
@ -186,4 +193,4 @@ Permisos Granulares:
lo que mejora la seguridad al limitar el acceso a funciones administrativas.
Autoincremento de NVR:
La etiqueta de compilación `mxos0-build` está configurada para incrementar automáticamente el número
de release de los paquetes, simplificando el proceso de compilación.
de release de los paquetes, simplificando el proceso de release.

View file

@ -1,2 +0,0 @@
---
...

View file

@ -15,8 +15,12 @@ all:
cs10-koji-03.mx-os:
vars:
ansible_python_interpreter: /usr/bin/python3
dist-git:
hosts:
cs10-koji-04.mx-os:
koji_nodes:
children:
kojihub:
kojibuilders:
dist-git:
...

View file

@ -8,12 +8,14 @@ apply:
.PHONY: destroy
destroy:
virsh -c qemu:///system destroy cs10-koji-00.mx-os
virsh -c qemu:///system destroy cs10-koji-01.mx-os
virsh -c qemu:///system destroy cs10-koji-02.mx-os
virsh -c qemu:///system destroy cs10-koji-03.mx-os
virsh -c qemu:///system undefine --remove-all-storage --nvram --tpm cs10-koji-00.mx-os
virsh -c qemu:///system undefine --remove-all-storage --nvram --tpm cs10-koji-01.mx-os
virsh -c qemu:///system undefine --remove-all-storage --nvram --tpm cs10-koji-02.mx-os
virsh -c qemu:///system undefine --remove-all-storage --nvram --tpm cs10-koji-03.mx-os
-virsh -c qemu:///system destroy cs10-koji-00.mx-os
-virsh -c qemu:///system destroy cs10-koji-01.mx-os
-virsh -c qemu:///system destroy cs10-koji-02.mx-os
-virsh -c qemu:///system destroy cs10-koji-03.mx-os
-virsh -c qemu:///system destroy cs10-koji-04.mx-os
-virsh -c qemu:///system undefine --remove-all-storage --nvram --tpm cs10-koji-00.mx-os
-virsh -c qemu:///system undefine --remove-all-storage --nvram --tpm cs10-koji-01.mx-os
-virsh -c qemu:///system undefine --remove-all-storage --nvram --tpm cs10-koji-02.mx-os
-virsh -c qemu:///system undefine --remove-all-storage --nvram --tpm cs10-koji-03.mx-os
-virsh -c qemu:///system undefine --remove-all-storage --nvram --tpm cs10-koji-04.mx-os
tofu destroy -auto-approve

View file

@ -30,7 +30,7 @@ variable "base_image_path" {
variable "guest_count" {
description = "Número de máquinas virtuales a crear"
type = number
default = 4
default = 5
}
variable "guest_vcpu" {

View file

@ -59,6 +59,15 @@
- name: Create GPG key batch file
when: inventory_hostname == 'localhost'
tags: gpg
block:
- name: Create temporary GPG batch file
ansible.builtin.tempfile:
state: file
suffix: .gpg
register: gpg_batch_file
- name: Populate GPG batch file
ansible.builtin.copy:
content: |
%echo Generating a basic OpenPGP key
@ -72,25 +81,26 @@
Passphrase: {{ gpg_key_passphrase }}
%commit
%echo done
dest: "{{ koji_pki_dir }}/gpg_batch"
dest: "{{ gpg_batch_file.path }}"
mode: "0600"
delegate_to: localhost
run_once: true
- name: Generate GPG key
- name: Generate GPG key
ansible.builtin.command:
cmd: "gpg --batch --gen-key {{ koji_pki_dir }}/gpg_batch"
delegate_to: localhost
run_once: true
cmd: "gpg --batch --gen-key {{ gpg_batch_file.path }}"
args:
creates: "{{ koji_pki_dir }}/gpg_key.pub"
- name: Export GPG public key
- name: Export GPG public key
ansible.builtin.shell:
cmd: "gpg --armor --export 'Koji Signing Key' > {{ koji_pki_dir }}/gpg_key.pub"
changed_when: false
delegate_to: localhost
run_once: true
always:
- name: Remove temporary GPG batch file
ansible.builtin.file:
path: "{{ gpg_batch_file.path }}"
state: absent
when: gpg_batch_file.path is defined
- name: Set hostname
ansible.builtin.hostname:
@ -119,10 +129,21 @@
state: absent
loop: "{{ groups['all'] }}"
- name: Scan SSH keys
ansible.builtin.command: ssh-keyscan -t rsa {{ item }}
loop: "{{ groups['all'] }}"
register: scanned_keys
delegate_to: localhost
run_once: true
changed_when: false
until: scanned_keys.rc == 0
retries: 3
delay: 2
- name: "Add current host keys"
ansible.builtin.known_hosts:
path: "~/.ssh/known_hosts"
name: "{{ item }}"
key: "{{ lookup('pipe', 'ssh-keyscan -t rsa ' + item) }}"
name: "{{ item.item }}"
key: "{{ item.stdout }}"
state: present
loop: "{{ groups['all'] }}"
loop: "{{ scanned_keys.results }}"

View file

@ -0,0 +1,8 @@
---
dist_git_base_dir: /var/lib/dist-git
dist_git_repos_dir: "{{ dist_git_base_dir }}/git"
dist_git_cache_dir: "{{ dist_git_base_dir }}/cache"
dist_git_user: git
dist_git_group: git
dist_git_httpd_conf_path: /etc/httpd/conf.d/dist-git.conf
dist_git_fqdn: "{{ inventory_hostname }}"

View file

@ -0,0 +1,9 @@
---
- name: Restart httpd
ansible.builtin.service:
name: httpd
state: restarted
- name: Reload systemd daemon
ansible.builtin.systemd:
daemon_reload: true

View file

@ -0,0 +1,125 @@
---
- name: Install Git and HTTPD
ansible.builtin.dnf:
name:
- git
- git-daemon
- httpd
- mod_ssl
- policycoreutils-python-utils
- firewalld
- rpm-build
- rpmdevtools
- wget
state: present
- name: Create dist-git group
ansible.builtin.group:
name: "{{ dist_git_group }}"
state: present
- name: Create dist-git user
ansible.builtin.user:
name: "{{ dist_git_user }}"
group: "{{ dist_git_group }}"
home: "{{ dist_git_base_dir }}"
create_home: true
shell: /bin/bash
state: present
- name: Create dist-git directory structure
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ dist_git_user }}"
group: "{{ dist_git_group }}"
mode: "0755"
loop:
- "{{ dist_git_base_dir }}"
- "{{ dist_git_repos_dir }}"
- "{{ dist_git_repos_dir }}/RPMs"
- "{{ dist_git_cache_dir }}"
- name: Configure git safe.directory system-wide
ansible.builtin.command: git config --system --add safe.directory "*"
changed_when: false
- name: Set SELinux boolean for HTTPD CGI support
ansible.builtin.command: setsebool -P httpd_enable_cgi 1
changed_when: false
- name: Set SELinux context for dist-git directories (persistent)
ansible.builtin.command: semanage fcontext -a -t httpd_sys_content_t "{{ dist_git_base_dir }}(/.*)?"
changed_when: false
- name: Apply SELinux context for dist-git directories
ansible.builtin.command: restorecon -R -v {{ dist_git_base_dir }}
changed_when: false
- name: Create PKI directory for Dist-Git
ansible.builtin.file:
path: /etc/pki/dist-git
state: directory
owner: root
group: root
mode: "0755"
- name: Copy Dist-Git certificates
ansible.builtin.copy:
src: "{{ koji_pki_dir }}/{{ item.src }}"
dest: "/etc/pki/dist-git/{{ item.dest }}"
mode: "{{ item.mode }}"
loop:
- { src: "dist-git.crt", dest: "dist-git.crt", mode: "0644" }
- { src: "dist-git.key", dest: "dist-git.key", mode: "0600" }
notify: Restart httpd
- name: Configure HTTPD for dist-git
ansible.builtin.template:
src: dist-git.conf.j2
dest: "{{ dist_git_httpd_conf_path }}"
owner: root
group: root
mode: "0644"
notify: Restart httpd
- name: Ensure firewalld is running
ansible.builtin.service:
name: firewalld
state: started
enabled: true
- name: Allow HTTP/HTTPS service in firewall
ansible.builtin.firewalld:
service: "{{ item }}"
permanent: true
state: enabled
immediate: true
loop:
- http
- https
- name: Ensure HTTPD is enabled and started
ansible.builtin.service:
name: httpd
state: started
enabled: true
- name: Deploy custom git-daemon systemd service
ansible.builtin.template:
src: git-daemon.service.j2
dest: /etc/systemd/system/git-daemon.service
owner: root
group: root
mode: '0644'
notify: Reload systemd daemon
- name: Reload systemd daemon to recognize new git-daemon service file
ansible.builtin.systemd:
daemon_reload: true
- name: Ensure git-daemon.service is enabled and started
ansible.builtin.systemd_service:
name: git-daemon.service
state: started
enabled: true

View file

@ -0,0 +1,40 @@
<VirtualHost *:80>
ServerName {{ dist_git_fqdn }}
DocumentRoot {{ dist_git_base_dir }}
# Redirect all HTTP traffic to HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</VirtualHost>
<VirtualHost *:443>
ServerName {{ dist_git_fqdn }}
DocumentRoot {{ dist_git_base_dir }}
SSLEngine on
SSLCertificateFile /etc/pki/dist-git/dist-git.crt
SSLCertificateKeyFile /etc/pki/dist-git/dist-git.key
# Git Smart HTTP for RPMs
# Maps /RPMs/pkg.git to {{ dist_git_repos_dir }}/RPMs/pkg.git
SetEnvIf Request_URI "^/RPMs/" GIT_PROJECT_ROOT={{ dist_git_repos_dir }}/RPMs
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAlias /RPMs/ /usr/libexec/git-core/git-http-backend/
<Directory "/usr/libexec/git-core">
Options +ExecCGI
Require all granted
</Directory>
<Directory "{{ dist_git_repos_dir }}">
Require all granted
</Directory>
# Lookaside Cache
Alias /sources {{ dist_git_cache_dir }}
<Directory "{{ dist_git_cache_dir }}">
Options +Indexes
Require all granted
</Directory>
</VirtualHost>

View file

@ -0,0 +1,13 @@
[Unit]
Description=Fast, unauthenticated GIT access
Documentation=man:git-daemon(1)
[Service]
Type=simple
ExecStart=/usr/libexec/git-core/git-daemon --base-path={{ dist_git_base_dir }}/git --export-all --listen=0.0.0.0 --port=9418
AmbientCapabilities=CAP_NET_BIND_SERVICE
User={{ dist_git_user }}
Group={{ dist_git_group }}
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,20 @@
---
- name: Add Koji builder host {{ item }}
block:
- name: Try to add Koji builder host
ansible.builtin.command: >
sudo -u {{ koji_admin_user }} koji
--config={{ koji_hub_admin_user_koji_dir }}/config
add-host {{ item }} x86_64
register: koji_add_host_result
changed_when: "'Host added' in koji_add_host_result.stdout"
failed_when: "'is already in the database' not in koji_add_host_result.stderr and koji_add_host_result.rc != 0"
rescue:
- name: Convert user to host in DB if it exists as user
ansible.builtin.command: >
sudo -u postgres psql {{ koji_db_name }} -c
"UPDATE users SET usertype = 1 WHERE name = '{{ item }}' AND usertype = 0;"
when:
- "'already exists and it is not a host' in koji_add_host_result.stderr"
changed_when: true
delegate_to: "{{ koji_db_host }}"

View file

@ -76,19 +76,43 @@
--config={{ koji_hub_admin_user_koji_dir }}/config
add-external-repo -t {{ koji_build_tag }}-build centos-stream-10-epel
https://mirror.facebook.net/fedora/epel/10/Everything/x86_64/
register: koji_bootstrap_add_appstream_repo_result
changed_when: "'External repository created' in koji_bootstrap_add_appstream_repo_result.stdout"
failed_when: "koji_bootstrap_add_appstream_repo_result.rc != 0 and 'already exists' not in koji_bootstrap_add_appstream_repo_result.stderr"
register: koji_bootstrap_add_epel_repo_result
changed_when: "'External repository created' in koji_bootstrap_add_epel_repo_result.stdout"
failed_when: "koji_bootstrap_add_epel_repo_result.rc != 0 and 'already exists' not in koji_bootstrap_add_epel_repo_result.stderr"
- name: Add Koji builder hosts
- name: Download repomd.xml from CentOS Stream 10 build repo
ansible.builtin.get_url:
url: https://kojihub.stream.centos.org/kojifiles/repos/c10s-build/latest/x86_64/repodata/repomd.xml
dest: /tmp/c10s-repomd.xml
- name: Extract comps.xml path
ansible.builtin.shell: grep 'comps.xml"' /tmp/c10s-repomd.xml | head -n 1 | sed -e 's/.*href="//' -e 's/".*//'
register: comps_path
changed_when: false
- name: Download comps.xml
ansible.builtin.get_url:
url: "https://kojihub.stream.centos.org/kojifiles/repos/c10s-build/latest/x86_64/{{ comps_path.stdout }}"
dest: /tmp/c10s-comps.xml
- name: Import comps groups
ansible.builtin.command: >
sudo -u {{ koji_admin_user }} koji
--config={{ koji_hub_admin_user_koji_dir }}/config
add-host {{ item }} x86_64
import-comps /tmp/c10s-comps.xml {{ koji_build_tag }}-build
register: koji_bootstrap_import_comps_result
changed_when: "'Groups imported' in koji_bootstrap_import_comps_result.stdout"
- name: Add Koji builder hosts
ansible.builtin.include_tasks: add_host.yaml
loop: "{{ groups['kojibuilders'] }}"
register: koji_bootstrap_add_host_result
changed_when: "'Host added' in koji_bootstrap_add_host_result.stdout"
failed_when: "koji_bootstrap_add_host_result.rc != 0 and 'is already in the database' not in koji_bootstrap_add_host_result.stderr"
- name: Restart httpd on kojihub to apply Koji changes
ansible.builtin.service:
name: httpd
state: restarted
delegate_to: "{{ groups['kojihub'][0] }}"
run_once: true
- name: Add Koji builder hosts to createrepo channel
ansible.builtin.command: >

View file

@ -14,6 +14,7 @@
- koji-utils
- createrepo_c
- tree
- python3-koji-hub-plugins
state: present
- name: Create directories for certs and client configs
@ -32,6 +33,20 @@
- "{{ 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 }}"
@ -109,6 +124,7 @@
- { 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" }
- name: Update CA trust store
ansible.builtin.command: update-ca-trust
@ -167,15 +183,6 @@
- { src: "99-local-policies.conf.j2", dest: "{{ koji_hub_config_dir }}/99-local-policies.conf" }
notify: Restart httpd
- name: Configure Koji Hub database connection
ansible.builtin.blockinfile:
path: /etc/koji-hub/hub.conf
block: |
DBHost = {{ koji_db_host }}
DBPass = {{ koji_db_password }}
marker: "# {mark} ANSIBLE MANAGED BLOCK FOR DB CONNECTION"
notify: Restart httpd
- name: Set SELinux booleans for Apache
ansible.posix.seboolean:
name: "{{ item }}"
@ -278,6 +285,15 @@
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: Template Koji callback script
ansible.builtin.template:
src: koji_callbacks.py.j2

View file

@ -1,25 +1,63 @@
# Koji Hub Configuration
[hub]
# Main directory for Koji data (packages, repos, etc.)
## ConfigParser style config file, similar to ini files
## https://docs.python.org/library/configparser.html
## Basic options ##
DBName = {{ koji_db_name }}
DBUser = {{ koji_db_user }}
DBHost = {{ koji_db_host }}
DBPass = {{ koji_db_password }}
KojiDir = {{ koji_top_dir }}
# Authentication methods. 'ssl' is primary for clients, 'password' for web UI.
AuthMethods = ssl, password
## Auth-related options ##
# CheckClientIP = True
# Automatically create a user account on first successful SSL login.
## Kerberos authentication options ##
# ProxyPrincipals = koji/kojiweb@EXAMPLE.COM
# HostPrincipalFormat = compile/%s@EXAMPLE.COM
# AllowedKrbRealms = *
# DisableURLSessions = False
## SSL client certificate auth configuration ##
# The client username is the common name of the subject of their client certificate
DNUsernameComponent = CN
# ProxyDNs is used to allow a specific user (usually the web interface) to act as others
# This should match the DN of the 'kojiweb' certificate
ProxyDNs = /C={{ koji_cert_country }}/ST={{ koji_cert_state }}/L={{ koji_cert_locality }}/O={{ koji_cert_organization }}/CN={{ koji_hub_fqdn }}
## Other options ##
LoginCreatesUser = On
KojiWebURL = {{ koji_url }}
EmailDomain = {{ koji_email_domain | default('mx-os.mx') }}
NotifyOnSuccess = False
NotifyOnFailure = False
DisableNotifications = False
# Header used by Apache/mod_proxy to forward the original client IP.
ProxyUserHeader = X-Forwarded-For
## Resource limits ##
# RLIMIT_AS =
# RLIMIT_CORE =
# RLIMIT_CPU =
# RLIMIT_DATA =
# RLIMIT_FSIZE =
# RLIMIT_MEMLOCK =
# RLIMIT_NOFILE =
# RLIMIT_NPROC =
# RLIMIT_OFILE =
# RLIMIT_RSS =
# RLIMIT_STACK =
# Message to display in the client when the hub is offline.
ServerOfflineMessage = The Koji build system is currently offline for maintenance.
## MemoryWarnThreshold = 5000
## MaxRequestLength = 4194304
# How long builder should wait for host-init to finish (seconds)
HostInitTimeout = 120
## Extended features
# EnableMaven = False
# EnableWin = False
Plugins = sigul_sign
## Koji hub plugins
PluginPath = /usr/lib/koji-hub-plugins
Plugins = echo
[sigul]
# Sigul server URL (replace with actual Sigul server address)
SigulServer = {{ sigul_server }}
## Debugging
KojiDebug = On
LogLevel = INFO

View file

@ -1,5 +1,10 @@
---
kojid_work_dir: /var/lib/kojid
kojid_allowed_scms: git.mx-os.mx:/RPMs/*
kojid_use_host_resolv: true
kojid_allowed_scms_use_config: true
kojid_allowed_scms_use_policy: false
kojid_log_level: INFO
kojid_max_jobs: 50
kojid_mock_user: kojibuilder
kojid_use_createrepo_c: true
kojid_use_host_resolv: true
kojid_work_dir: /var/lib/kojid

View file

@ -0,0 +1,41 @@
#!/usr/bin/bash
set -ex
SURL="https://cs10-koji-04.mx-os/sources"
PKGNAME=$(basename "$PWD")
if [ ! -f "sources" ]; then
echo "No sources file found."
exit 0
fi
while read -r alg filename_part eq hash; do
# Format: SHA512 (filename) = hash
# filename_part is "(filename)"
filename="${filename_part:1:-1}"
if [ -f "$filename" ]; then
echo "File $filename already exists, skipping."
continue
fi
# Construct URL based on MxOS layout: SURL/PKGNAME/FILENAME/HASH/FILENAME
# Note: PKGNAME might need to be passed or detected.
# Usually in Koji execution, PWD is the SCM root, so basename PWD should work if directory matches package name.
# However, Koji often clones into 'ed' or similar.
# Using the layout found: sources/ed/ed-1.20.tar.lz/HASH/
# Assuming the file is INSIDE that hash directory.
# Check if there's a trailing slash in SURL, remove it if so.
SURL=${SURL%/}
url="$SURL/$PKGNAME/$filename/$hash/$filename"
echo "Downloading $filename from $url"
curl -L --fail --retry 5 -o "$filename" "$url"
# Verify hash (optional but good)
echo "$hash $filename" | sha512sum -c -
done < sources

View file

@ -44,12 +44,7 @@
group: mock
mode: "2775"
- name: Copy mock configuration file
ansible.builtin.copy:
src: mxos0-build-x86_64.cfg
dest: /etc/mock/mxos0-build-x86_64.cfg
mode: "0644"
notify: restart kojid
- name: Configure kojid
ansible.builtin.template:
@ -104,6 +99,22 @@
regexp: ".*{{ koji_hub_fqdn }}"
state: present
- name: Copy source fetching wrapper script
ansible.builtin.copy:
src: mxos-get-sources.bash
dest: "{{ koji_source_script_path }}"
mode: "0755"
owner: root
group: root
- name: Configure MxOS Mock config
ansible.builtin.template:
src: mxos0-build-x86_64.cfg.j2
dest: /etc/mock/mxos0-build-x86_64.cfg
mode: "0644"
owner: root
group: mock
- name: Update CA trust
ansible.builtin.command: update-ca-trust
changed_when: false

View file

@ -33,3 +33,9 @@ from_addr = {{ kojid_from_addr }}
; certs
cert = {{ kojid_pem_path }}
serverca = {{ koji_remote_ca_trust_path }}
; repos
use_createrepo_c = {{ kojid_use_createrepo_c }}
; logs
log_level = {{ kojid_log_level }}

View file

@ -4,7 +4,7 @@ config_opts['rpmbuild_networking'] = True
config_opts['use_bootstrap'] = False
config_opts['target_arch'] = 'x86_64'
config_opts['legal_host_arches'] = ('x86_64',)
config_opts['chroot_setup_cmd'] = 'install rpm-build rpmdevtools rpmlint'
config_opts['chroot_setup_cmd'] = 'install rpm-build rpmdevtools rpmlint make'
config_opts['yum.conf'] = """
[main]
gpgcheck=1
@ -37,3 +37,13 @@ baseurl=https://dl.fedoraproject.org/pub/epel/10/Everything/x86_64/
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-epel-10
enabled=1
"""
# Bind mount the MxOS source fetching wrapper
config_opts['plugin_conf']['bind_mount_enable'] = True
if 'bind_mount_opts' in config_opts['plugin_conf']:
if 'dirs' in config_opts['plugin_conf']['bind_mount_opts']:
config_opts['plugin_conf']['bind_mount_opts']['dirs'].append(('/usr/local/bin/mxos-get-sources.bash', '/usr/local/bin/mxos-get-sources.bash'))
else:
config_opts['plugin_conf']['bind_mount_opts']['dirs'] = [('/usr/local/bin/mxos-get-sources.bash', '/usr/local/bin/mxos-get-sources.bash')]
else:
config_opts['plugin_conf']['bind_mount_opts'] = {'dirs': [('/usr/local/bin/mxos-get-sources.bash', '/usr/local/bin/mxos-get-sources.bash')]}

View file

@ -67,9 +67,10 @@
contype: hostssl
databases: "{{ koji_db_name }}"
users: "{{ koji_db_user }}"
address: "{{ hostvars[groups['kojihub'][0]].ansible_default_ipv4.address }}/32"
address: "{{ hostvars[item].ansible_default_ipv4.address }}/32"
method: scram-sha-256
backup: true
loop: "{{ groups['kojihub'] }}"
notify: Restart postgresql
- name: Configure pg_hba.conf to allow IPv6 connection from the Koji hub
@ -78,10 +79,11 @@
contype: hostssl
databases: "{{ koji_db_name }}"
users: "{{ koji_db_user }}"
address: "{{ hostvars[groups['kojihub'][0]].ansible_default_ipv6.address }}/128"
address: "{{ hostvars[item].ansible_default_ipv6.address }}/128"
method: scram-sha-256
backup: true
when: hostvars[groups['kojihub'][0]].ansible_default_ipv6.address is defined
loop: "{{ groups['kojihub'] }}"
when: hostvars[item].ansible_default_ipv6.address is defined
notify: Restart postgresql
- name: Configure pg_hba.conf for local postgres user trust authentication

View file

@ -10,6 +10,7 @@ koji_profile: koji
kojid_fqdn: cs10-koji-03.mx-os
koji_db_host: cs10-koji-01.mx-os
koji_nfs_host: cs10-koji-02.mx-os
koji_dist_git_fqdn: cs10-koji-04.mx-os
sigul_server: http://localhost:8080/sigul
# Paths & Directories
@ -17,7 +18,7 @@ koji_top_dir: /mnt/koji
koji_pki_dir: "{{ playbook_dir }}/certs"
kojid_work_dir: /var/lib/kojid
koji_remote_pki_dir: /etc/pki/koji
koji_hub_config_dir: /etc/koji_hub/hub.conf.d
koji_hub_config_dir: /etc/koji-hub/hub.conf.d
koji_hub_koji_web_config_dir: /etc/kojiweb
koji_hub_koji_client_config_file: /etc/koji.conf.d/ansible-settings.conf
koji_remote_kojira_conf_path: /etc/kojira/kojira.conf
@ -25,6 +26,7 @@ koji_remote_kojihub_httpd_conf_path: /etc/httpd/conf.d/kojihub.conf
koji_remote_kojiweb_httpd_conf_path: /etc/httpd/conf.d/kojiweb.conf
koji_hub_admin_user_home: "/home/{{ koji_admin_user }}"
koji_hub_admin_user_koji_dir: "{{ koji_hub_admin_user_home }}/.koji"
koji_source_script_path: /usr/local/bin/mxos-get-sources.bash
# Certificate Paths (Remote)
koji_hub_cert_path: /etc/pki/koji/kojihub.crt
@ -70,6 +72,10 @@ common_koji_certs_to_generate:
cn: "{{ koji_db_host }}"
is_user: false
is_server: true
- name: dist-git
cn: "{{ koji_dist_git_fqdn }}"
is_user: false
is_server: true
# File Permissions
koji_pki_dir_mode: "0755"
@ -79,14 +85,12 @@ koji_client_dir_mode: "0700"
koji_client_file_mode: "0600"
# Kojid Specific Settings
kojid_max_jobs: 2
kojid_mock_user: kojid
kojid_max_jobs: 10
kojid_mock_user: kojibuilder
kojid_vendor: Fundación MxOS
kojid_packager: Koji
kojid_distribution: MxOS
kojid_allowed_scms: git.mx-os.mx:/RPMs/*
kojid_allowed_scms_use_config: true
kojid_allowed_scms_use_policy: false
kojid_allowed_scms: "{{ koji_dist_git_fqdn }}:/RPMs/*:false git.mx-os.mx:*:true:{{ koji_source_script_path }} github.com:/*:false"
kojid_smtp_host: mail.mx-os.mx
kojid_smtp_user: kojid@cs10-koji-03.mx-os
kojid_from_addr: Koji Build System <kojid@cs10-koji-03.mx-os>