domingo, 2 de septiembre de 2018

Despliegue de un "docker registry" sencillo en Debian

Sin duda alguna, los contenedores se ha convertido en una tecnología de uso sumamente extendido recientemente y Docker, como máximo competidor de este mercado ha despertado mucho interés. Una pieza central de Docker, es el "servidor" que nos permite tener almacenadas las imágenes a utilizar para crear los contenedores, a este servidor se le llama registry y podemos encontrar múltiples ofertas gratuitas de ellos en la web, como por ejemplo Docker Hub o el mismo GitLab, pero muchas veces es preferible tener un registry privado, a continuación una receta para crear uno muy sencillo en Debian:


Instalación de Docker

El primer paso consiste en realizar la instalación de docker en el equipo que será utilizado como registry, en este caso se asume que está instalado Debian Stretch:
sudo apt update
sudo apt install -y apt-transport-https ca-certificates curl gnupg2 software-properties-common
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
sudo apt update && sudo apt install -y docker-ce
sudo systemctl enable docker

Crear un contenedor para que sea el registry

El siguiente paso, es arrancar una imagen con la configuración necesaria para correr el registry, la gente de docker ya proporciona una que puede ser instalada fácilmente:
sudo docker run -d -p 127.0.0.1:5000:5000 --restart=always --name registry registry:2

De esta manera ya tenemos un registry corriendo de manera local, cosa que no es muy útil. Existen muchas maneras de poner el servicio disponible en la red, en este caso se mostrará como hacerlo con Apache en proxy reverso, añadiendo la opción de cifrar la conexión con certificados de LetsEncrypt y creando grupos autenticados para hacer pull y push de imágenes:

Instalación de LetsEncrypt y creación de certificado

En este ejemplo se asume que se va a prestar el servicio hacia Internet y adicionalmente que ya existe un registro en el DNS con el nombre registry.ejemplo.com que apunta hacia la IP pública de nuestro servidor.
La manera de emitir un certificado de LetsEncrypt es con la utilidad certbot:

# Recordar sustituir registry.ejemplo.com y correo@ejemplo.com con los datos reales de dominio y correo electrónico
sudo apt install certbot
sudo certbot certonly --standalone -d registry.ejemplo.com -m correo@ejemplo.com

Instalación de Apache y configuración en proxy reverso para el sitio registry.ejemplo.com

Con el contenedor corriendo, se procede a configurar Apache como frontend para que haga las funciones proxy reverso, autenticación y autorización.

sudo apt install -y apache2
# creando el usuario "foo" con clave "bar" que luego será utilizado por los clientes
sudo htpasswd -c /etc/apache2/registry-users.txt foo bar
# creando el grupo "dockerpush" al cual pertenecerá "foo" para poder hacer push de imágenes
echo "dockerpush:foo"  | sudo tee /etc/apache2/registry-groups.txt
chown www-data: /etc/apache2/{registry-users.txt,registry-gorups.txt}
# habilitando modulos adicionales para apache
sudo a2enmod ssl authz_user authz_groupfile headers proxy_http
# Creación de archivo de configuración, recordar sustituir los valores de ejemplo
cat <<EOF | sudo tee /etc/apache2/sites-available/registry.ejemplo.com 
<VirtualHost *:443>

  ServerName registry.ejemplo.com

  SSLEngine on
  SSLCertificateFile /etc/letsencrypt/live/registry.ejemplo.com/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/registry.ejemplo.com/privkey.pem

  Header always set "Docker-Distribution-Api-Version" "registry/2.0"
  Header onsuccess set "Docker-Distribution-Api-Version" "registry/2.0"
  RequestHeader set X-Forwarded-Proto "https"

  ProxyRequests     off
  ProxyPreserveHost on

  # no proxy for /error/ (Apache HTTPd errors messages)
  ProxyPass /error/ !

  ProxyPass        /v2 http://localhost:5000/v2
  ProxyPassReverse /v2 http://localhost:5000/v2

  <Location /v2>
    Order deny,allow
    Allow from all
    AuthName "Registry Authentication"
    AuthType basic
    AuthUserFile "/etc/apache2/registry-users.txt"
    AuthGroupFile "/etc/apache2/registry-groups.txt"

    # Read access to authentified users
    <Limit GET HEAD>
      Require valid-user
    </Limit>

    # Write access to docker-deployer only
    <Limit POST PUT DELETE PATCH>
      Require group dockerpush
    </Limit>

  </Location>

</VirtualHost>
EOF 
# Si todo está bien, debería de funciona la comprobación de la configuración y recarga de la misma
sudo a2ensite registry.ejemplo.com
sudo apache2ctl configtest && sudo systemctl reload apache2

Login en el nuevo registro

Si todo está bien configurado, el nuevo registro debería poder aceptar peticiones, además de poder autenticar las mismas, por ejemplo desde el cliente en la misma máquina o en una remota se debería hacer los siguiente con éxito:
# Proporcionar usuario "foo" con la clave "bar", que se creó anteriormente
docker login registry.ejemplo.com

Consideraciones adicionales

Esta receta tiene amplio margen de mejora y también hay cosas que hay que tener a consideración:
  • A la fecha de publicación de esta entrada el cliente de docker me dio algunos problemas con SNI, lo que quiere decir que podría dar algunos problemas en caso de estar hospedando múltiples sitios seguros en el mismo servidor apache.
  • Por razones de simplicidad, se omitió configuraciones adicionales de seguridad.
  • Las imágenes subidas al registry se están guardando dentro del mismo contenedor, al destruir el contenedor, se eliminan las imágenes.

Referencias

https://docs.docker.com/registry/deploying/
https://docs.docker.com/registry/recipes/apache/

1 comentario: