El manteniment de les imatges base acceleradores proporcionades pel CTTI finalitza el 15 de desembre de 2026.
A partir d'aquesta data aquestes imatges no s'actualitzaran i serà obligatori utilitzar les versions més recents dels repositoris oficials dels fabricants. Es recomana planificar amb antelació la migració de les aplicacions seguint els criteris per escollir la imatge base.
Excepció: les imatges Apache i Nginx d'integració amb GICAR (Apache GICAR Shibboleth i GICAR Nginx, incloses les seves variants per a Kubernetes/Openshift) no queden afectades per aquesta data: l'equip de GICAR continuarà mantenint-les. Podeu trobar més informació a Configuració de Gicar en Contenidors.
Introducció
A l’hora de crear les imatges dels diferents contenidors docker per les aplicacions gencat cal establir un conjunt de criteris que garanteixin la seva estabilitat i seguretat.
Imatges acceleradores del CTTI
L’equip de Suport Cloud del CTTI manté un conjunt d’imatges docker de les tecnologies més utilitzades a la Generalitat de Catalunya. S’ofereixen com a acceleradors per facilitar la tasca als desenvolupadors, i són compatibles amb totes les plataformes basades en contenidors disponibles a la Generalitat (Openshift i Kubernetes). El manteniment d’aquestes imatges finalitza el 15 de desembre de 2026: a partir d’aquesta data no s’actualitzaran, amb l’excepció de les imatges Apache i Nginx d’integració amb GICAR, que continuarà mantenint l’equip de GICAR (vegeu Configuració de Gicar en Contenidors).
Aquestes imatges docker es poden trobar al registre docker privat, al projecte gencatcloud. La llista d’imatges disponibles es pot consultar a Elements del catàleg de contenidors.
Podeu trobar més informació respecte al registre privat a Registre d’imatges.
Criteris per escollir la imatge base
La recomanació general és partir sempre de les imatges oficials dels fabricants, en la darrera versió LTS suportada i amb la versió fixada. Les imatges acceleradores del CTTI són una facilitat per al desenvolupador, no un substitut d’aquest criteri. Les recomanacions detallades (fonts oficials, versions LTS, fixació per versió i digest, actualització, imatges mínimes i traçabilitat) es descriuen a Elements del catàleg de contenidors.
- Les imatges de docker es crearan sempre a partir del fitxer Dockerfile, en cap cas es crearan imatges a partir de contenidors ni s’utilitzaran imatges preconstruïdes.
- S’utilitzaran les imatges oficials dels fabricants com a base (directiva FROM), normalment disponibles al docker hub o als registres del propi fabricant.
- S’escollirà l’última versió estable suportada i, preferentment, una versió LTS (Long Term Support), que rep pegats de seguretat i suport durant períodes llargs. No s’utilitzaran versions sense suport del fabricant ni versions preview, beta o RC en producció.
- Mai s’escollirà el tag latest, ni tags flotants sense versió (com
node,ltsostable). Són versions que van canviant en el temps i generen inestabilitat a les aplicacions. Escollir sempre la versió més tancada possible (major.minor.patch) i, per a la màxima reproducibilitat, fixar també el digest de la imatge (només en imatges de repositoris oficials: les imatges del Registre d’imatges del CTTI es referencien per etiqueta). - Entre les imatges oficials disponibles, s’aplicarà aquest ordre de preferència pel sistema operatiu base:
- En cas que existeixi una imatge oficial basada en Alpine, s’escollirà aquesta.
- En cas que no existeixi una imatge oficial basada en Alpine, s’escollirà la imatge basada en Centos, substituint Centos per la versió d’Oracle Linux Slim corresponent. Aquesta substitució és necessària pel support de pegats de seguretat que ofereix Oracle Linux i no ofereix Centos.
- En cas que no existeixi una imatge oficial basada en Alpine ni Centos, s’escollirà la que recomani el fabricant, indicant sempre una versió concreta.
- Les imatges acceleradores proporcionades pel CTTI es poden utilitzar com a base fins al 15 de desembre de 2026, sempre que la seva versió coincideixi amb la darrera versió LTS suportada del producte. A partir d’aquesta data deixaran de rebre manteniment i serà obligatori utilitzar les versions més recents dels repositoris oficials.
- Excepció: imatges d’integració amb GICAR. Les imatges Apache i Nginx d’integració amb GICAR (Apache GICAR Shibboleth i GICAR Nginx, incloses les seves variants per a Kubernetes/Openshift) continuaran sent mantingudes per l’equip de GICAR. En els components que integrin GICAR, el Dockerfile continuarà tenint com a base la imatge de GICAR corresponent, en la darrera versió publicada, tal com s’indica a Configuració de Gicar en Contenidors.
Criteris generals per la creació de les imatges
Alguns d’aquests criteris ja estan resolts a les imatges acceleradores del CTTI. Tanmateix, en construir la imatge directament sobre una imatge oficial dels fabricants (i obligatòriament a partir del 15 de desembre de 2026) cal aplicar-los tots.
- Deixar instal·lat el nombre mínim de paquets al docker, si és necessari instal·lar paquets per realitzar compilacions, desinstal·lar-los un cop realitzada aquesta.
- Actualitzar els packets del sistema operatiu a l’última versió disponible amb tots els pegats de seguretat aplicats.
- No deixar el codi font al docker, un cop compilat, eliminar-lo.
- Incloure a la imatge el script wait-for-it.sh, adjuntat a baix, per poder testejar les comunicacions. Notar que requereix tenir el bash instal·lat.
- No executar mai el procés final de les aplicacions amb l’usuari root. La majoria de tecnologies inclouen scripts que utilitzen usuaris específics per arrencar-les.
- Fixar els uid i gid dels usuaris i grups utilitzats per executar els processos, amb l’objectiu d’evitar escalat de privilegis.
- Assegurar la seguretat dels directoris definint el propietari i els permisos explícitament. S’adjunta a baix l’script docker-setup.sh d’exemple.
- Executar el procés principal des d’un script, mai directament al Dockerfile.
- Utilitzar el volum /data per desar la informació que cal persistir.
- No executar múltiples processos a dins de la imatge docker. Docker està pensat per executar un únic procés.
- Fer servir imatges confiables de repositoris oficials i verificats
- Exposar només els ports necessaris, i sempre que sigui possible, configurar l’aplicació per treballar amb ports no-estandar
- No afegir mai credencials a les instruccions del Dockerfile ni afegir dades sensibles
- La imatge generada ha de ser independent de recursos externs i autosificient per arrencar a qualsevol entorn
- Signar les imatges creades identificant l’equip que les ha generat
- Prioritzar l’us de COPY sobre ADD
- Utilitzar construccions multi-stage: una etapa amb les eines de compilació i una etapa final, basada en una imatge mínima, que només conté el resultat de la compilació i el runtime necessari (per exemple, JRE en lloc de JDK)
- Reduir el nº de layers tot el possible, concatenant comandes que ho admetin (per exemple, RUN)
- Ubicar les comandes que menys canvi presentin a l’inici del Dockerfile
- No escriure els logs de l’aplicació a fitxers. Enviar els logs sempre a la sortida estàndard. D’aquesta manera la plataforma capturarà els logs i no caldrà entrar als contenidors per veure’ls.
Per garantir el compliment de les bones pràctiques en la generació de les imatges, utilitzar l’eina Haskell Dockerfile Linter (hadolint).
Criteris de seguretat
- Utilitzar sempre l’última versió LTS suportada del producte, sol ser la que té menys vulnerabilitats de seguretat. No utilitzar versions sense suport del fabricant.
- Fixar la versió de la imatge base no implica no actualitzar-la: reconstruir periòdicament les imatges, encara que el codi no hagi canviat, per incorporar els pegats de seguretat de la imatge base, i actuar sense esperar al cicle periòdic davant d’una vulnerabilitat crítica.
- Revisar les vulnerabilitats de les imatges generades en el repositori de JFrog (https://ctti.jfrog.io/artifactory/docker-private/xxxx.yy/) on xxxx.yy és el codi de diàleg de l’aplicació i el codi de component. En cas que es detectin vulnerabilitats, intentar eliminar-les instal·lant els patches necessaris.
- Aplicar totes les configuracions de seguretat recomanades pel fabricant o la comunitat per cada producte en particular.
- Es recomanable seguir les directrius de seguretat definides per CIS. CIS Docker Benchmark.
Actualment els log dels pipelines de SIC 3.0 o els dels workflows de SIC+ ofereixen resumenes de les vulnerabilitats detectades per a facilitar la seva detecció i correcció prèvia.git
Criteris específics de Openshift
Openshift, tot i que suporta desplegar imatges de docker, presenta uns criteris més restrictius de seguretat que cal tenir present a l’hora de construir el Dockerfile.
- No utilitzar l’usuari root ni per executar l’script principal del docker (normalment s’utilitza l’usuari root). Utilitzar la directiva USER uid per definir amb quin usuari s’executarà el procés.
- Cal tenir present que openshift executa el procés amb un id d’usuari aleatori que pertany al grup root.
- Aquest usuari es crea a l’executar el docker. No existeix, ni es coneix, en etapa de construcció de la imatge de docker.
- A causa de la característica anterior, és possible que algunes accions que habitualment es fan en etapa de construcció calgui fer-les en etapa d’execució amb alguna shell.
- Cal que les carpetes on el procés escriu dades, pertanyin al grup root. Es pot veure a l’script docker-setup.sh inclòs abaix.
- Algunes aplicacions necessiten que l’usuari estigui definit al fitxer /etc/passwd. En aquest cas existeixen dues opcions:
-
Utilitzar el paquet nss_wrapper per emular els canvis en aquest fitxer. Es necessari heretar de la imatge alpine-nss. Important llegir el README.md amb les instruccions de com utilitzar-la.
Es pot veure un exemple d’ús a la imatge docker postgres. Fixeu-vos sobretot en l’ús del ENTRYPOINT.
-
Utilitar la funcionalitat que ofereix cri.o, motor de contenidors d’Openshift 4, que afegeix automàticament l’usuari amb que arrenca el contenidor al fitxer de /etc/password
Veure la plana web Openshift. Creating Images. Guidelines. OpenShift Origin-Specific Guidelines. Support Arbitrary User IDs per més detalls.
-
- Utilitzar variables d’entorn per la configuració.
- Més informació de com construir les imatges de docker està disponible a :
Exemples
Podeu trobar exemples de diferents imatges de docker seguint aquests criteris al registre docker privat projecte gencatcloud. Podeu trobar els Dockerfiles de les imatges a git imatges docker. Aquests exemples són a títol de referència: les imatges deixaran de rebre manteniment el 15 de desembre de 2026, excepte les d’integració amb GICAR, que continuarà mantenint l’equip de GICAR.
Quan trobeu que per una tecnologia existeix la versió normal i la versió amb amb sufix -openshift, les imatges amb versió normal son compatibles amb docker(local) i les imatges amb amb sufix -openshift són compatibles amb Kubernetes i Openshift.
Annexos
wait-for-it.sh
#!/usr/bin/env bash
# Use this script to test if a given TCP host/port are available
cmdname=$(basename $0)
echoerr() { if [[ $QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
usage()
{
cat << USAGE >&2
Usage:
$cmdname host:port [-s] [-t timeout] [-- command args]
-h HOST | --host=HOST Host or IP under test
-p PORT | --port=PORT TCP port under test
Alternatively, you specify the host and port as host:port
-s | --strict Only execute subcommand if the test succeeds
-q | --quiet Don't output any status messages
-t TIMEOUT | --timeout=TIMEOUT
Timeout in seconds, zero for no timeout
-- COMMAND ARGS Execute command with args after the test finishes
USAGE
exit 1
}
wait_for()
{
if [[ $TIMEOUT -gt 0 ]]; then
echoerr "$cmdname: waiting $TIMEOUT seconds for $HOST:$PORT"
else
echoerr "$cmdname: waiting for $HOST:$PORT without a timeout"
fi
start_ts=$(date +%s)
while :
do
(echo > /dev/tcp/$HOST/$PORT) >/dev/null 2>&1
result=$?
if [[ $result -eq 0 ]]; then
end_ts=$(date +%s)
echoerr "$cmdname: $HOST:$PORT is available after $((end_ts - start_ts)) seconds"
break
fi
sleep 1
done
return $result
}
wait_for_wrapper()
{
# In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
if [[ $QUIET -eq 1 ]]; then
timeout -t $TIMEOUT $0 --quiet --child --host=$HOST --port=$PORT --timeout=$TIMEOUT &
else
timeout -t $TIMEOUT $0 --child --host=$HOST --port=$PORT --timeout=$TIMEOUT &
fi
PID=$!
trap "kill -INT -$PID" INT
wait $PID
RESULT=$?
if [[ $RESULT -ne 0 ]]; then
echoerr "$cmdname: timeout occurred after waiting $TIMEOUT seconds for $HOST:$PORT"
fi
return $RESULT
}
# process arguments
while [[ $# -gt 0 ]]
do
case "$1" in
*:* )
hostport=(${1//:/ })
HOST=${hostport[0]}
PORT=${hostport[1]}
shift 1
;;
--child)
CHILD=1
shift 1
;;
-q | --quiet)
QUIET=1
shift 1
;;
-s | --strict)
STRICT=1
shift 1
;;
-h)
HOST="$2"
if [[ $HOST == "" ]]; then break; fi
shift 2
;;
--host=*)
HOST="${1#*=}"
shift 1
;;
-p)
PORT="$2"
if [[ $PORT == "" ]]; then break; fi
shift 2
;;
--port=*)
PORT="${1#*=}"
shift 1
;;
-t)
TIMEOUT="$2"
if [[ $TIMEOUT == "" ]]; then break; fi
shift 2
;;
--timeout=*)
TIMEOUT="${1#*=}"
shift 1
;;
--)
shift
CLI="$@"
break
;;
--help)
usage
;;
*)
echoerr "Unknown argument: $1"
usage
;;
esac
done
if [[ "$HOST" == "" || "$PORT" == "" ]]; then
echoerr "Error: you need to provide a host and port to test."
usage
fi
TIMEOUT=${TIMEOUT:-15}
STRICT=${STRICT:-0}
CHILD=${CHILD:-0}
QUIET=${QUIET:-0}
if [[ $CHILD -gt 0 ]]; then
wait_for
RESULT=$?
exit $RESULT
else
if [[ $TIMEOUT -gt 0 ]]; then
wait_for_wrapper
RESULT=$?
else
wait_for
RESULT=$?
fi
fi
if [[ $CLI != "" ]]; then
if [[ $RESULT -ne 0 && $STRICT -eq 1 ]]; then
echoerr "$cmdname: strict mode, refusing to execute subprocess"
exit $RESULT
fi
exec $CLI
else
exit $RESULT
fi
docker-setup.sh
#!/bin/sh
# setup directory for data
mkdir -p /data
chown -R nginx:0 /data
chmod g+w -R /data
chown -R nginx:0 /usr
chown -R nginx:0 /var
chgrp -R 0 /usr
chmod -R g+rw /usr
find /usr -type d -exec chmod g+x {} +
chgrp -R 0 /var
chmod -R g+rw /var
find /var -type d -exec chmod g+x {} +