Servidor de paquetes binarios en FreeBSD mediante Poudriere


Todos conocemos las ventajas de utilizar binarios compilados a medida:

  • Menor consumo de RAM
  • Binarios mas pequeños
  • Versiones mas modernas de software
  • Menor superficie de ataque ya que se han eliminado todas las funcionalidades innecesarias del binario

El único inconveniente es el tiempo requerido para la compilación, pero y si dispusiésemos de un equipo con mayor potencia para realizar la compilación y utilizar estos binarios en nuestro servidor? Precisamente Poudriere se ocupa de esta tarea, compila binarios a medida desde los ports para que sean utilizados por otros equipos.

Todas la información ha sido recolectada de las siguientes fuentes:

https://github.com/freebsd/poudriere/wiki

https://www.freebsd.org/doc/handbook/ports-poudriere.html

https://www.digitalocean.com/community/tutorials/how-to-set-up-a-poudriere-build-system-to-create-packages-for-your-freebsd-servers

https://www.freebsd.org/cgi/man.cgi?query=poudriere&sektion=8&manpath=freebsd-release-ports

https://hackmd.io/@dch/HkwIhv6x7

Para poder generar binarios mediante Poudriere tendremos que instalar Portmaster, Nginx y ccache:

pkg install poudriere portmaster nginx ccache

Cuando construyamos paquetes con Poudriere los firmaremos con una private key, de este modo nos aseguraremos de que nadie pueda suplantar nuestro servidor de paquetes, creamos los directorios necesarios:

mkdir -p /usr/local/etc/ssl/{keys,certs}
chmod 0600 /usr/local/etc/ssl/keys

Generamos la private key:

openssl genrsa -out /usr/local/etc/ssl/keys/poudriere.key 4096

Generamos el certificado que será incluído en la firma de los paquetes:

openssl rsa -in /usr/local/etc/ssl/keys/poudriere.key -pubout -out /usr/local/etc/ssl/certs/poudriere.cert

Si nuestro servidor no soporta ZFS:

vi /usr/local/etc/poudriere.conf
NO_ZFS=yes

En caso contrario indicamos el pool y el rootfs:

ZPOOL=zroot
ZROOTFS=/poudriere

Poudriere monta una jail donde compilará los ports, con el parámetro FREEBSD_HOST le indicamos de donde debe bajarse la imagen de la jail:

FREEBSD_HOST=ftp://ftp.freebsd.org

Indicamos donde debe generar los ficheros necesarios para su funcionamiento:

POUDRIERE_DATA=${BASEFS}/data

CHECK_CHANGED_OPTIONS indica si se deben recompilar los paquetes si las opciones de compilación han cambiado. CHECK_CHANGED_DEPS indica si se deben recompilar los paquetes si las dependencias han cambiado desde la última compilación.

CHECK_CHANGED_OPTIONS=verbose
CHECK_CHANGED_DEPS=yes

Le indicamos a Poudriere donde está la key con la que debe firmar los paquetes:

PKG_REPO_SIGNING_KEY=/usr/local/etc/ssl/keys/poudriere.key

Finalmente indicamos la URL con la que los clientes tendrán acceso a los paquetes:

URL_BASE=http://poudriere.alfaexploit.com/

Indicamos el directorio donde guardar los ficheros objeto cacheados de compilaciones previas:

CCACHE_DIR=/var/cache/ccache
mkdir -p /var/cache/ccache

Mi configuración entera queda del siguiente modo:

egrep -v '^($|[[:space:]]*#|\;)'  /usr/local/etc/poudriere.conf

ZPOOL=zroot
NO_ZFS=no
ZROOTFS=/poudriere
FREEBSD_HOST=ftp://ftp.freebsd.org
RESOLV_CONF=/etc/resolv.conf
BASEFS=/usr/local/poudriere
POUDRIERE_DATA=${BASEFS}/data
USE_PORTLINT=no
USE_TMPFS=yes
DISTFILES_CACHE=/usr/ports/distfiles
CHECK_CHANGED_OPTIONS=verbose
CHECK_CHANGED_DEPS=yes
PKG_REPO_SIGNING_KEY=/usr/local/etc/ssl/keys/poudriere.key
CCACHE_DIR=/var/cache/ccache
URL_BASE=http://poudriere.alfaexploit.com/

Ahora que ya tenemos Poudriere configurado debemos poner a punto nuestro entorno de compilación, como ya se ha indicado anteriormente Poudriere montará una jail para tal efecto, el único requisito es que la jail debe de ser una versión igual o inferior a la del equipo padre.

Creamos la jail indicando nombre y versión:

poudriere jail -c -j freebsd_12-1x64 -v 12.1-RELEASE

NOTA: No se recomienda el uso de . en el nombre de la jail ya que puede dar problemas con ciertas herramientas.

poudriere jail -l
JAILNAME        VERSION         ARCH  METHOD TIMESTAMP           PATH
freebsd_12-1x64 12.1-RELEASE-p5 amd64 ftp    2020-06-05 20:24:50 /usr/local/poudriere/jails/freebsd_12-1x64

Instalamos el ports tree, mas adelante le indicaremos a la jail que lo utilice:

poudriere ports -c -p HEAD

NOTA: Podemos tener varias jails con distintas versiones de SO y versiones de los ports.

poudriere ports -l
PORTSTREE METHOD   TIMESTAMP           PATH
HEAD      portsnap 2020-06-05 20:36:10 /usr/local/poudriere/ports/HEAD

Generamos la lista de ports a compilar, la mejor manera de hacer esto es sacando la lista de paquetes instalados actualmente en los servidores que vayan a utilizar nuestro repositorio, antes de generar la lista hacemos un poco de limpieza para que la lista sea lo mas pequeña posible.

pkg autoremove

Sacamos la lista de los paquetes que instalamos explícitamente sin dependencias:

portmaster --list-origins | sort -d | tee /root/port-list

Copiamos la lista al servidor Poudriere.

scp SERVER:/root/port-list /usr/local/etc/poudriere.d/port-list

Este proceso hay que hacerlo en todos los servidores y en el servidor de compilación unificar las lista.

Si fuese necesario añadimos ports adicionales a la lista:

vi /usr/local/etc/poudriere.d/port-list

NOTA: No hay que preocuparse por las dependencias ya que se compilarán de forma automática

Si queremos parametrizar el make.conf debemos indicarlo, en mi caso me interesa compilar todo lo relacionado con PHP con la versión7.4 y los binarios sin soporte para las X ni ipv6:

vi /usr/local/etc/poudriere.d/freebsd_12-1x64-make.conf
OPTIONS_UNSET+= X11 IPV6
DEFAULT_VERSIONS+= php=7.4

Le indicamos a Poudriere con que opciones debe compilar los ports, solo aparecerá el dialógo de configuración si no se ha configurada previamente en el fichero /usr/local/etc/poudriere.d/freebsd_12-1x64-HEAD-options/PORTNAME/options
una vez realizada la configuración ya no volverá a pedirla en posteriores compilaciones, es importante elegir el mínimo de opciones ya que así habrán menos dependencias y la compilación será más rápida.

La interfaz web de Poudriere nos muestra porque se está compilando cada port, podemos saber de quien es dependencia cada uno de ellos y así averiguar parámetros de configuración erróneos.

poudriere options -j freebsd_12-1x64 -p HEAD -f /usr/local/etc/poudriere.d/port-list

Si mas adelante queremos que se nos vuelva a preguntar por las opciones de compilación ejecutaremos el mismo comando pero con la opción -c:

poudriere options -c -j freebsd_12-1x64 -p HEAD -f /usr/local/etc/poudriere.d/port-list

Si solo queremos reconfigurar las opciones de uno de los ports lo mas sencillo es borrar la configuración asociada:

rm /usr/local/etc/poudriere.d/freebsd_12-1x64-HEAD-options/www_nginx/options
poudriere options -j freebsd_12-1x64 -p HEAD -f /usr/local/etc/poudriere.d/port-list

NOTA: Si tenemos dudas sobre los parámetros correctos siempre podemo consultarlos en otro servidor donde esté instalado el binario desde los repositorios de FreeBSD, por ejemplo para php7.4: pkg info php74

Por fín podemos empezar a construir ports, pero antes nos aseguramos de tener la jail y el port tree actualizados.

poudriere jail -u -j freebsd_12-1x64
poudriere ports -u -p HEAD

Comenzamos la compilación en una sesión de screen:

screen -S PoudriereCompilation
poudriere bulk -j freebsd_12-1x64 -p HEAD -f /usr/local/etc/poudriere.d/port-list

Obtenemos información del proceso de compilación:

CTRL-t
load: 6.80  cmd: sh 96156 [nanslp] 669.81r 0.21u 0.84s 0% 3852k
[freebsd_12-1x64-HEAD] [2020-06-05_20h46m50s] [parallel_build:] Queued: 325 Built: 59  Failed: 0   Skipped: 0   Ignored: 0   Tobuild: 266  Time: 00:11:10
 [01]: lang/tcl86                | tcl86-8.6.10              fetch           (00:00:17 / 00:00:19)
 [02]: devel/nspr                | nspr-4.25                 package         (00:00:01 / 00:00:12)
 [03]: lang/perl5.30             | perl5-5.30.3              build           (00:02:45 / 00:03:26)
 [04]: databases/db5             | db5-5.3.28_7              build           (00:00:28 / 00:00:39)
 [05]: dns/libidn2               | libidn2-2.3.0_1           fetch           (00:00:02 / 00:00:04)
 [06]: devel/swig30              | swig30-3.0.12_1           configure       (00:00:02 / 00:00:26)
 [07]: graphics/giflib           | giflib-5.2.1              fetch           (00:00:51 / 00:00:52)
 [08]: devel/gettext-tools       | gettext-tools-0.20.2      build           (00:01:36 / 00:02:09)

Ahora que ya tenemos los binarios compilados tan solo queda servirlos mediante Nginx, este Nginx además servirá para consultar las estadísticas de compilación de Poudriere.

Habilitamos el servicio en el arranque:

sysrc nginx_enable="YES"

Mediante la configuración de Nginx tendremos acceso a la interfaz de Poudriere(/), a los logs(/data) y a los paquetes(/packages).

vi /usr/local/etc/nginx/nginx.conf
#http context
. . .
    server {
        listen 80 default;
        server_name poudriere.alfaexploit.com/;
        root /usr/local/share/poudriere/html;

        location /data {
            alias /usr/local/poudriere/data/logs/bulk;
            autoindex on;
        }

        location /packages {
            root /usr/local/poudriere/data;
            autoindex on;
        }
    }
}

Para poder visualizar los logs directamente desde la interfaz web y no obligarnos a bajar el archivo tendremos que modificar el fichero mime.types de Nginx.

vi /usr/local/etc/nginx/mime.types
text/plain                          txt log;

Chequeamos la configuración:

service nginx configtest
Performing sanity check on nginx configuration:
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful

Arrancamos Nginx:

service nginx start

Si tenemos algún fw configurado lo adaptamos y ya podremos acceder a la interfaz web de Poudriere.

http://poudriere.alfaexploit.com

Si queremos ver los paquetes debemos acceder a /packages:

http://poudriere.alfaexploit.com/packages

Tan solo queda la configuración del cliente, igual como no es nada recomendable mezclar paquetes binarios con ports compilados por las diferentes opciones de compilación que puedan tener, tampoco lo es mezclar paquetes de los repositorios oficiales de FreeBSD con repositorios Poudriere propios, para utilizar exclusivamente nuestro repositorio tan solo tendremos que omitir el parámetro de configuración priority y deshabilitar los oficiales.

mkdir -p /usr/local/etc/pkg/repos
vi /usr/local/etc/pkg/repos/poudriere.conf
poudriere: {
    url: "http://poudriere.alfaexploit.com/packages/freebsd_12-1x64-HEAD/",
    mirror_type: "http",
    signature_type: "pubkey",
    pubkey: "/usr/local/etc/ssl/certs/poudriere.cert",
    enabled: yes,
}
vi /usr/local/etc/pkg/repos/freebsd.conf
FreeBSD: {
    enabled: no
}

Volcamos el contenido de la pubkey poudriere.cert en la configuración del cliente.

ssh SERVIDOR_POUDRIERE
cat /usr/local/etc/ssl/certs/poudriere.cert

En el cliente:

mkdir -p /usr/local/etc/ssl/{keys,certs}
vi /usr/local/etc/ssl/certs/poudriere.cert

Comprobamos manualmente que le cliente sea capaz de acceder a los paquetes:

curl http://poudriere.alfaexploit.com/packages/
<html>
<head><title>Index of /packages/</title></head>
<body>
<h1>Index of /packages/</h1><hr><pre><a href="../">../</a>
<a href="freebsd_12-1x64-HEAD/">freebsd_12-1x64-HEAD/</a>                              05-Jun-2020 18:46                   -
</pre><hr></body>
</html>

Actualizamos los paquetes desde nuestro repositorio:

pkg upgrade -f

Ya podemos instalar paquetes desde nuestro servidor de paquetes binarios 😆


Cada vez que queramos actualizar nuestros servidores primero habrá que compilar los útlimos binarios en Poudriere, los pasos a seguir son los siguientes.

Actualizamos la jail de compilación:

poudriere jail -u -j freebsd_12-1x64

Actualizamos el port tree:

poudriere ports -u -p HEAD

Compilamos(elegimos si queremos reconfigurar los parámetros de compilación):

screen -S PoudriereCompilation
poudriere options -j freebsd_12-1x64 -p HEAD -f /usr/local/etc/poudriere.d/port-list
poudriere options -c -j freebsd_12-1x64 -p HEAD -f /usr/local/etc/poudriere.d/port-list
poudriere bulk -j freebsd_12-1x64 -p HEAD -f /usr/local/etc/poudriere.d/port-list

Podemos ver el progreso de la compilación en la interfaz web:

http://poudriere.alfaexploit.com

Actualizamos en los servidores:

pkg upgrade

Para los mas perezosos dejo un script rápido:

vi updatePoudriere.sh
#!/usr/local/bin/bash
poudriere jail -u -j freebsd_12-1x64
poudriere ports -u -p HEAD
poudriere options -j freebsd_12-1x64 -p HEAD -f /usr/local/etc/poudriere.d/port-list
poudriere bulk -j freebsd_12-1x64 -p HEAD -f /usr/local/etc/poudriere.d/port-list

chmod 700 updatePoudriere.sh


TROUBLESHOOTING:

Si tenemos problemas compilando podemos probar:

Crear de nuevo la jail y un nuevo ports tree.

poudriere jail -d -j freebsd_12-1x64
poudriere ports -d -p HEAD
poudriere jail -c -j freebsd_12-1x64 -v 12.1-RELEASE
poudriere ports -c -p HEAD

Borrar la caché:

rm -rf /var/cache/ccache/*

Añadir los paquetes a la lista manualmente

cd /usr/ports:
make fetchindex
make search name=NAME
vi /usr/local/etc/poudriere.d/port-list
CATEGORY/NAME

También podemos eliminar ports compilados anteriormente y recompilarlos, con el tiempo que ello implica:

poudriere bulk -c -j freebsd_12-1x64 -p HEAD -f /usr/local/etc/poudriere.d/port-list
Si te ha gustado el artículo puedes invitarme a un redbull aquí.
Autor: kr0m -- 08/06/2020 01:54:51