Mi propio repositorio Debian …

debian-logo… para no tener que descargar, construir e instalar desde el principio cada pieza de software que me hace falta y que no encuentro en otros sitios.

Actualización: he retocado algunos detalles en la configuración y el uso del repositorio basándome en experiencias reales con el mismo.

Y es que no es únicamente por el software que yo puedo crear (que no es tanto), también están aquellos módulos Perl que empaqueto para declararlos como dependencias de otros y que no sé dónde colocar. Claro, en mi lugar de trabajo tengo un repositorio y puedo apañarme perfectamente con él; es más, se ha vuelto imprescindible dado que todos los equipos funcionan con Debian y las personalizaciones de ajustes son legión.

Pero emplear dicho repositorio desde fuera de la empresa significa abrirlo al exterior y ahí es cuando me topo con la primera dificultad: a los jefes no les hace ninguna gracia que los programas creados para la empresa puedan ser inspeccionados desde fuera. Y a mi, como responsable del departamento de un sólo hombre de informática, tampoco. No es sólo cuestión de seguridad, es que toda la lógica del negocio (y muchos datos) están allí. Descartado pues.

Así que empleando las notas que tomé en su momento -y ajustando la configuración a mis necesidades- me decido a empaquetarlo todo y a describir aquí el proceso.

Aviso, eso sí, de que éste sistema es para aquél que construye algún paquete Debian y lo envía empleando los mecanismos habituales y en los que están implicados un archivo .changes, un .tar.gz, un .dsc y otro .deb; no sirve -o no está pensado- para importar un grupo de paquetes .deb directamente. Sobre ese tema hablaré en otra entrada.

Comenzando

Todo el montaje está basado en el programa mini-dinstall que está enfocado a los repositorios personales y, como avisan en la documentación, hay varios controles que se omiten y que son imprescindibles en los oficiales. Entre ellos mencionan que se puede subir una versión anterior de un paquete y que es posible borrarlos a mano (vía rm) sin que el sistema se inmute. Se limitaría a reconstruir la lista de paquetes omitiendo los faltantes. A mí me vale, pero supongo que para otros no.

Básicamente lo que hace falta para ponerlo en marcha es:

  1. Instalar el paquete
  2. Crear un usuario y un grupo para trabajar con él (yo suelo poner debpkg pero vale cualquiera).
  3. Crear o elegir un directorio como raíz del repositorio y darle la propiedad y permisos del usuario y grupo del punto 2.
  4. Crear un archivo de configuración.
  5. Añadir un script de control del programa como demonio.
  6. Configurar un servidor web para tener acceso HTTP al repositorio

El archivo de configuración de mini-dinstall que yo empleo es el siguiente:

#
#   Aplicable a todas las distribuciones
#

[DEFAULT]

#   Raíz del repositorio 
archivedir = /var/lib/debian

# Envío de avisos de error vía email 
mail_log_level = ERROR
mail_to = victor@taquiones.net
mail_log_flush_level = ERROR
mail_log_flush_count = 10

#   Activar el reindexado de los archivos Packages/Sources tras cada
#   instalación de paquete (recomendable desactivarlo si se instalan
#   *muchos* paquetes cada vez)
trigger_reindex = 1

# Desactivar la verificación de firmas GPG en los archivos .changes (lo normal
# cuando se construyen paquete deb y no se firman en local)
verify_sigs = 0

#   Permisos para el directorio de entrada de paquetes. 
#   El valor cero (0) desactiva esta funcionalidad y es necesario poner a mano 
#   los permisos 
incoming_permissions = 0777

### A partir de aquí las opciones pueden especificarse por cada rama

# Nombre alternativo de la rama
alias = unstable

# Arquitecturas posibles en el repositorio
architectures = all, i386, amd64

# Estilo de archivo para paquetes:
#   - El valor "flat" es el predeterminado y consiste en poner todos los .deb
#   en un único directorio. 
#   - El valor "simple-subdir" crea subdirectorios para cada arquitectura
archive_style = simple-subdir

# Enviar un correo tras la instalación de un paquete con éxito
mail_on_success = 1

# Conservar los paquetes antiguos o no. Si se conservan sería conveniente
# efectuar algún tipo de limpieza para no cargar con decenas de versiones de
# un mismo software.
keep_old = 1

# Programa a ejecutar antes de instalar un archivo .changes que recibe como
# parámetro. Si termina con error provoca que se ignore dicho paquete.
#pre_install_script = ~/bin/pre-inst.sh

# Programa a ejecutar cuando un archivo .changes se instala
# satisfactoriamente.
#post_install_script = ~/bin/post-inst.sh

# Generar o no un archivo "Release"
generate_release = 1

# Valor predeterminado para el campo "Origin:" en el archivo "Release"
#release_origin = username

# Valor predeterminado para el campo "Label:" del archivo "Release"
#release_label = username

# Valor predeterminado para el campo "Suite:" del archivo "Release"
release_suite = Taquiones

# Valor predeterminado para el campo "Description" en el archivo "Release"
release_description = Repositorio local de taquiones.net

# Marcar o no la rama como experimental
experimental_release = 0

# Programa llamado para firmar los archivos "Release"
#release_signscript = /root/bin/sign-release.sh

# Activar el reindexado automático de los archivos "Packages" y "Sources"
# monitorizando el directorio de entrada. Sólo empleado cuando el programa
# mini-dinstall funciona como demonio.
dynamic_reindex = 1

# Desactivar el cambio de permisos de acceso para los archivos .changes. 
chown_changes_files = 0

# No emplear el programa /usr/bin/dnotiy (no funciona en todos los sistmeas)
use_dnotify = 0

# Tiempo en segundos de muestreo del directorio de entrada de paquetes
poll_time = 30

# Tiempo límite de espera para recibir un paquete antes de rechazarlo
max_retry_time = 172800

Esquema de directorios

El primer gráfico muestra cómo quedan y para qué se utilizan los diferentes subdirectorios desde el punto de vista del repositorio. El segundo está enfocado en el directorio de trabajo de mini-dinstall.

mini-dinstall-schema.pngmini-dinstall-work.png

Como puede verse no tiene mucho más misterio; basta con copiar los componentes de un paquete deb al directorio incoming/ para que el programa los instale y actualice el archivo Packages y Sources y estén listos para ser empleados como cualquier otro repositorio. Sí que hay varios detalles que afinar todavía y de los que hablo más abajo.

Script de inicio del demonio

No he encontrado uno en el propio paquete ya que no siempre se emplea como demonio pero es muy sencillo crearlo. Si se respetan las normas actuales en Wheezy basta con copiar el ejemplo habitual /etc/init.d/skeleton y alterar los siguientes campos:

### BEGIN INIT INFO
# Provides:          taquiones-debian
# Required-Start:    $network $local_fs
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Repositorio local de paquetes Debian
# Description:       Demonio de mantenimiento del repositorio local
#                    de paquetes Debian
### END INIT INFO

# Author: Victor Moral <victor@taquiones.net>

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Repositorio local de paquetes Debian"
NAME=mini-dinstall
DAEMON=/usr/bin/mini-dinstall
DAEMON_ARGS="--config /etc/mini-dinstall.conf"             
PIDFILE=/var/lib/debian/mini-dinstall/mini-dinstall.lock
SCRIPTNAME=/etc/init.d/$NAME

Aviso: cuidado con el archivo que guarda el PID que está situado y nombrado de forma insual. No utiliza el directorio habitual /var/run y como no puede cambiarse vía configuración es necesario ajustarlo aquí.

Cómo enviar paquetes

Enviar paquetes al repositorio precisa de cierta configuración en el sistema en el que estemos construyéndolos. Yo empleo el programa debrelease que a su vez utiliza a dupload (en mi caso) o a dput. Son éstos los que efectúan la subida real de los datos por lo que añado un archivo de configuración en $HOME/.dupload.conf como el que sigue:

package config;

$default_host = "taquiones";

$preupload{'changes'} = '';

$cfg{taquiones} = {
	fqdn			=>	q(taquiones.net),
	login			=>	q(victor),
	incoming		=>	q(/var/lib/debian/incoming),
	mailto			=>	q(victor@taquiones.net),
	dinstall_runs	=>	0,
	fullname		=>	q(Victor Moral),
	visibleuser		=>	q(victor),
	method			=>	'scpb'
};

1;

En él le estamos diciendo lo siguiente:

  • Transfiere los archivos al servidor taquiones.net empleando scp y usando el usuario victor como credencial.
  • Guárdalos en el directorio /var/lib/debian/incoming.
  • Envíame un correo cuando esté.
  • No ejecutes nada tras la copia ya que mini-dinstall se debe encargar de ello.

Cómo añadir seguridad

Existen dos formas de asegurar el repositorio que se complementan: la primera es firmar digitalmente los paquetes cuando se construyen y decirle a mini-dinstall que verifique las firmas y la segunda es, además, firmar digitalmente también las listas de paquetes del repositorio tras cada instalación.

En mi caso estoy empleando únicamente la segunda y creo haberla descrito lo suficiente en mi wiki  (con la valiosa ayuda de Stefano Zachirolli ) como para repetirme mucho más aquí. Pero como quedan flecos pendientes, especialmente la adaptación del script de firmado y la seguridad de la clave privada, voy a dejarlo para otra entrada. No está listo aún en mi paquete y prefiero no dejarlo incompleto.

Cómo usar el repositorio

Emplear el repositorio depende sobre todo de cómo esté configurado el servidor web para determinar el URL base del mismo. La distribución y la rama varían muy poco y en mi caso quedan de la siguiente forma. Es aconsejable que se guarde en un único archivo y se almacene bajo /etc/apt/sources.d/astillas.list para controlar mejor su eliminación.

La variable que se menciona $(ARCH) es sustituída por apt por la arquitectura correspondiente. En mi caso rara vez creo un fichero fuera de all porque trabajo en Perl, pero siempre puede existir algún caso con software compilado que lo haga necesario.

deb http://debian.astillas.net/ local/$(ARCH)/
deb http://debian.astillas.net/ local/all/
deb-src http://debian.astillas.net/ local/source/

Si queremos podemos utilizar conexiones seguras con el repositorio, bien utilizando el protocolo HTTPS, bien empleando ssh o rsh como transporte de paquetes. Para el primer caso debemos instalar en el sistema cliente el paquete apt-transport-https y cambiar las URL acorde a él. Para el segundo es casi obligado configurar conexiones ssh vía claves públicas o perderemos la posibilidad de descargar paquetes de forma desatendida.

El servidor web para el repositorio

Con respecto al servidor web lo he hecho lo más sencillo posible: he creado un servidor virtual (aprovechando que los certificados digitales de cacert.org admiten comodines) y lo he añadido a mi servidor nginx con la siguiente configuración básica:

#
#   Servidor virtual para paquetes Debian
#

server {
        listen          80;
        server_name     debian.astillas.net;

        root            /var/lib/debian;
        access_log      /var/log/virtual/debian.astillas.net/access.log; 
        error_log       /var/log/virtual/debian.astillas.net/error.log;
        rewrite_log     on;

        location "/mini-dinstall" {
            return 404;
        }

        location "/incoming" {
            return 404;
        }

        location / {
            autoindex on;
        }
}

Para otros servidores web el mecanismo es similar. Basta con algún módulo de listado de directorios (como mod_index de Apache) y excluir algunas carpetas de su alcance para asegurar un poco más el servicio. Entre éstas estarían la carpeta de las claves de firma de root y el directorio de trabajo de mini-dinstall.