Guillermo Alvarado
Guillermo Alvarado
Creador del blog
Sep 20, 2019 8 min read

Introducción + Instalación de SUSE Portus, un repositorio privado de imágenes para Docker

thumbnail for this post

Si tu o tu empresa están buscando un Docker Registry privado con características de seguridad avanzadas y tener la capacidad de instalarlo de manera local, te va a encantar Portus. Pues no solo obtienes una solución sólida para almacenar las imágenes, sino que también obtienes la capacidad (instalando en conjunto Clair) de escanear tus imágenes en busca de vulnerabilidades.

¿Qué es Docker Registry?

Cuando trabajamos con Docker, debemos decidir dónde almacenaremos las imágenes de los contenedores. Docker Registry es una aplicación que gestiona el almacenamiento y la entrega de imágenes de contenedores Docker.

Docker tiene un registro público gratuito, Docker Hub, que puede alojar nuestras imágenes personalizadas, pero hay situaciones en las que no deseamos que nuestras imagen estén disponibles públicamente. Las imágenes generalmente contienen todo el código necesario para ejecutar una aplicación, por lo que es preferible utilizar un registro privado cuando se utiliza software propietario.

Si necesitamos restringir el acceso a nuestras imágenes de Docker, hay 3 opciones:

1.Obtener una suscripción en Docker Hub que desbloquea la función para crear repositorios privados.

2.Ejecutar una instancia local del registro de Docker, con es posible evitar por completo el uso de Docker Hub. Esta opción tiene dos limitaciones principales:

  • Carece de cualquier forma de autenticación. Eso significa que todos los que tengan acceso al Registro de Docker pueden insertar y extraer imágenes. Eso también incluye la posibilidad de sobrescribir imágenes ya existentes.
  • No hay forma de ver qué imágenes se han enviado al Registro de Docker. Debemos tomar notas manualmente de lo que se almacena dentro de él. Tampoco hay funcionalidad de búsqueda, lo que dificulta la colaboración. Estas limitaciones se resuelven instalando un registro de terceros.

3.Ejecutar un registro de Docker de terceros, que contenga características de seguridad avanzadas. Esto último es Portus

¿Qué es Portus?

Portus es un servicio de código abierto que proporciona autenticación/ autorización y una interfaz de usuario para el Registro de Docker. Es una aplicación local que permite a los usuarios administrar y proteger los registros de Docker.

Ficha técnica

  • Creado por SuSE, adoptado por usuarios de todo el mundo
  • Liberado como código abierto (Licencia Apache 2.0)
  • Registro de contenedores
  • Enfoque: almacena, firma y escanea contenido
  • Sitio web: http://port.us.org/
  • Repositorio:https://github.com/SUSE/Portus

Características de Portus

  • Sincronización con nuestro registro privado para obtener qué imágenes y etiquetas que estén disponibles.
  • Autenticación de usuarios con LDAP.
  • Autenticación OAuth y OpenID-Connect
  • Monitoreo de todas las actividades realizadas en su registro privado y en el propio Portus.
  • Busqueda de repositorios y etiquetas dentro del registro privado.
  • Destaca repositorios favoritos.
  • Deshabilitar usuarios temporalmente.
  • Opcionalmente, usar tokens de aplicaciones para una mejor seguridad.

Además, se integra con Clair, que escanea las imágenes de contenedores en busca de vulnerabilidades de seguridad. Juntas, estas herramientas brindan una seguridad incomparable.

Clair

Clair es un proyecto de código abierto para el análisis estático de vulnerabilidades en contenedores de aplicaciones.

Los datos de vulnerabilidades se importan continuamente desde un conjunto conocido de bases de datos y se correlacionan con el contenido indexado de las imágenes del contenedor para producir listas de vulnerabilidades .

Cuando Portus se instala en un entorno sin conexión a Internet, Clair no puede obtener datos de las bases de datos de vulnerabilidades públicas. En estos escenarios, el administrador debe actualizar manualmente la base de datos de Clair.

Instalación de Portus

El siguiente procediemiento despliega Portus en un ambiente basado en contenedores, usaré un host CentOS 7.6 pero no hay dependencia del sistema operativo, el requisito es ejecutar Docker.

En mi caso desplegué un droplet en DigitalOcean con 2 vCPUs y 2 GB RAM.

Agregué en mi DNS un record A a la IP pública que me asignó DO para que resolviera hacia registry.galvarado.com.mx ya que usaremos un certificado SSL generado por Certbot y es necesario definir un FQDN para el host pues el certificado está ligado a el

Instalar Docker

Configurar el repositorio de docker-ce:

$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

Instalar docker-ce:

$ sudo yum install docker-ce

Agregar nuestro usuarioal grupo de docker:

$ sudo usermod -aG docker $(whoami)

Habilitar docker para que inicie automaticamente en el arranque del sistema:

$ sudo systemctl enable docker.service

Finalmente iniciar el servicio de Docker:

$ sudo systemctl start docker.service

Para verificar:

$ docker version
Client: Docker Engine - Community
 Version:           19.03.2
 API version:       1.40
 Go version:        go1.12.8
 Git commit:        6a30dfc
 Built:             Thu Aug 29 05:28:55 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Instalar docker-compose

Descargamos el binario par ala ultima versión estable

sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/bin/docker-compose

Aplicamos permisos

$ sudo chmod +x /usr/local/bin/docker-compose

Para verificar la instalación

$ docker-compose version
docker-compose version 1.24.1, build 4667896
docker-py version: 3.7.3
CPython version: 2.7.5
OpenSSL version: OpenSSL 1.0.2k-fips  26 Jan 2017

Clonar el repositorio de Portus

Instalar git:

$ sudo yum install git

Clonar el repo:

git clone https://github.com/SUSE/Portus.git

Usaremos el template que incluye la configuración de SSL y Clair

/Portus/examples/compose/docker-compose.clair-ssl.yml

Editar el archivo .env en /Portus/examples/compose/.env

MACHINE_FQDN=zxc.zxc.net
SECRET_KEY_BASE=b494a25faa8d22e430e843e220e424e10ac84d2ce0e64231f5b636d21251eb6d267adb042ad5884cbff0f3891bcf911bdf8abb3ce719849ccda9a4889249e5c2
PORTUS_PASSWORD=12341234
DATABASE_PASSWORD=portus

Certificado SSL

Usaremos Certbot para generar el certificado y el webserver será nginx

Instalamos los paquetes requeridos

$ sudo yum install httpd mod_ssl python-certbot-nginx

Para verificar

$ certbot --version
certbot 0.37.2

Ahora para generar el certificado SSL:

sudo certbot

Respondemos a todas las perguntas. Aqui es donde es necesario contar con el FQDN definido. Si no lo has definido, edita /etc/hostname y colocalo. En mi caso es registry.galvarado.com.mx:

$ sudo certbot certonly --nginx
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx
No names were found in your configuration files. Please enter in your domain
name(s) (comma and/or space separated)  (Enter 'c' to cancel): registry.galvarado.com.mx
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for registry.galvarado.com.mx
nginx: [error] invalid PID number "" in "/run/nginx.pid"
Waiting for verification...
Cleaning up challenges
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/registry.galvarado.com.mx/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/registry.galvarado.com.mx/privkey.pem
   Your cert will expire on 2019-12-19. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:
   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Los archivos se deben genetar el la siguiente ruta:

cd /etc/letsencrypt/live/registry.galvarado.com.mx/
$  ls
cert.pem  chain.pem  fullchain.pem  privkey.pem  README

Después de generar el certificado, lo agregamos a Portus. Copiar el archivo .pem del certificado y de la llave y renombrarlo como **portus:**

$ cp /etc/letsencrypt/live/registry.galvarado.com.mx/fullchain.pem ~/Portus/examples/compose/secrets/portus.crt
$ cp /etc/letsencrypt/live/registry.galvarado.com.mx/privkey.pem ~/Portus/examples/compose/secrets/portus.ke

Iniciar Portus

Levantamos los contendedores con docker-compose

$ docker-compose -f docker-compose.clair-ssl.yml up -d
compose_db_1 is up-to-date
Starting compose_postgres_1 ...
compose_portus_1 is up-to-date
compose_registry_1 is up-to-date
Starting compose_postgres_1 ... done
Starting compose_nginx_1    ... done
compose_clair_1 is up-to-date
[root@registry compose]# docker-compose ps
        Name                      Command               State                       Ports
--------------------------------------------------------------------------------------------------------------

compose_background_1   /init                            Up      3000/tcp
compose_db_1           /docker-entrypoint.sh mysq ...   Up      3306/tcp
compose_nginx_1        nginx -g daemon off;             Up      0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
compose_portus_1       /init                            Up      0.0.0.0:3000->3000/tcp
compose_registry_1     /entrypoint.sh /bin/sh /et ...   Up      0.0.0.0:5000->5000/tcp, 0.0.0.0:5001->5001/tcp

Podemos ver los 7 contenedores que se inician con la plantilla de compose:

docker ps
CONTAINER ID        IMAGE                         COMMAND                  CREATED             STATUS              PORTS                                      NAMES
b6d9001bdd95        nginx:alpine                  "nginx -g 'daemon of…"   20 minutes ago      Up 7 minutes        0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   compose_nginx_1
5acb65949a91        opensuse/portus:head          "/init"                  20 minutes ago      Up 12 minutes       3000/tcp                                   compose_background_1
5b0b81bf509b        registry:2.6                  "/entrypoint.sh /bin…"   20 minutes ago      Up 12 minutes       0.0.0.0:5000-5001->5000-5001/tcp           compose_registry_1
cb7ae5bf37a4        quay.io/coreos/clair:v2.0.1   "/clair -config /cla…"   20 minutes ago      Up 7 minutes        0.0.0.0:6060-6061->6060-6061/tcp           compose_clair_1
13c97afc2290        opensuse/portus:head          "/init"                  20 minutes ago      Up 12 minutes       0.0.0.0:3000->3000/tcp                     compose_portus_1
36de8d5fdc72        mariadb:10.0.23               "/docker-entrypoint.…"   20 minutes ago      Up 12 minutes       3306/tcp                                   compose_db_1
b4c55545626a        postgres:10-alpine            "docker-entrypoint.s…"   20 minutes ago      Up 7 minutes        5432/tcp                                   compose_postgres_1
  • 1 contenedor de nginx,
  • 1 contenedor Portus
  • 1 contenedor Portus background
  • 1 contenedor para el docker-registry,
  • 1 contenedor para BD en postgreSQL
  • 1 contenedor para BD en MariaDB,
  • 1 contenedor para el servicio de vulnerabilidades de Clair

Acceder a Portus

Vamos ala dirección del servidor donde iniciamos los servicios, en mi caso cree un record DNS como mencionaba para utilizar el FQDN. Si no creas un record en el DNS puedes modificar el archivo /etc/hosts de tu computadra para que la resolución sea local, pero el navegador solo podrá acceder por el FDQN debido al certificado SSL.

En mi caso:

https://registry.galvarado.com.mx/

Después de crear un usuario admin, configuramos el registro, en este caso es el mismo servicio inicado por docker-compose en el puerto 5000 del mismo host.

Subir una imagen a portus

Para fines prácticos voy a descargar una imagen existente, pero podríamos contrsuir una imagen desde un Dockerfile también.

Nos logueamos al nuevo registro desde nuestra consola:

$ docker login registry.galvarado.com.mx

Descargamos la imagen oficial de MySQL de DockerHub

$ docker pull mysql
Using default tag: latest
latest: Pulling from library/mysql
8f91359f1fff: Pull complete
6bbb1c853362: Pull complete
e6e554c0af6f: Pull complete
f391c1a77330: Pull complete
414a8a88eabc: Pull complete
fee78658f4dd: Pull complete
9568f6bff01b: Pull complete
5a026d8bbe50: Pull complete
07f193b54ae1: Pull complete
1e404375a275: Pull complete
b81b2ef0e430: Pull complete
2f499f36bd40: Pull complete
Digest: sha256:6d95fa56e008425121e24d2c01b76ebbf51ca1df0bafb1edbe1a46937f4a149d
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest

Tageamos la imagen que descargamos y la subimos a nuestro flamante repositorio privado y seguro. Para Taggear la imagen usamos el comando docker tag como se muestra a continuación:

$ docker tag [IMAGE] [REGISTRY]/[NAMESPACE]/[IMAGE:TAG]

En mi caso:

$ docker tag mysql:latest  registry.galvarado.com.mx/galvarado/mysql:latest
$ docker push registry.galvarado.com.mx/galvarado/mysql:latest
1cfb4d403fde: Pushed
e47b5971b1f1: Pushed
9ac6573d19b0: Pushed
3cd5c95dfa08: Pushed
05f26d9a462a: Pushed
9e88946b01ba: Pushed
7acae26d323c: Pushed
9a341d74c9b2: Pushed
5547ac6d39e8: Pushed
683d7a4130fe: Pushed
7288a4c980c6: Pushed
e9dc98463cd6: Pushed
latest: digest: sha256:2e4114bdc9dd797549f6df0cffb5f6cb6354bef9d96223a5935b6b17aea03840 size: 2828

Así se ve en portus:

Podemos observar la parte del análisis de vulnerabilidades de Clair claramente, detecta varias vulnerabilidades, al ver el detalle observamos:

Lo que resta es subir más imagenes y crear más usuarios para ser usado por el resto de la organización.

¿Que te parece? Tu propio Registro de imagenes, sencillo de instalar, protegido con SSL y autenticación e integrado Clair para escanear las imagenes en búsqueda de vulnerabilidaddes.

Si te es útil por favor comparte :)

comments powered by Disqus