Le conteneur Discourse utilise-t-il unattended-upgrades ?

Le conteneur Docker de Discourse utilise-t-il unattended-upgrades pour maintenir à jour les paquets du système d’exploitation de son conteneur Docker basé sur Debian ?

J’ai remarqué que le guide INSTALL-cloud.md recommande d’installer unattended-upgrades sur la machine hôte Docker, ce qui m’a fait me demander quel était l’état des paquets du système d’exploitation à l’intérieur du conteneur Docker. J’ai recherché à la fois dans les dépôts discourse et discourse_docker sur GitHub, mais la seule référence à unattended-upgrades que j’ai trouvée était le même document d’installation :

Mais qu’en est-il du conteneur Docker de Discourse ? Est-ce que lui utilise unattended-upgrades ?

Après des recherches plus approfondies, il semble que unattended-upgrades soit installé mais non exécuté sur le conteneur Docker Discourse.

Tout d’abord, il est clairement installé :

root@osestaging1-discourse-ose:/var/www/discourse# dpkg -l | grep -i unatt
ii  unattended-upgrades             1.11.2                       all          installation automatique des mises à jour de sécurité
root@osestaging1-discourse-ose:/var/www/discourse# 

Une inspection plus poussée de la configuration du paquet unattended-upgrades, conformément à l’article pertinent du wiki Debian, montre :

La configuration par défaut semble raisonnable :

root@osestaging1-discourse-ose:/var/www/discourse# grep -ir 'origin=' /etc/apt/apt.conf.d/50unattended-upgrades 
//      "origin=Debian,codename=${distro_codename}-updates";
//      "origin=Debian,codename=${distro_codename}-proposed-updates";
		"origin=Debian,codename=${distro_codename},label=Debian";
		"origin=Debian,codename=${distro_codename},label=Debian-Security";
root@osestaging1-discourse-ose:/var/www/discourse# cat /etc/apt/apt.conf.d/20auto-upgrades 
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
root@osestaging1-discourse-ose:/var/www/discourse# 

Cependant, la vérification des journaux montre que la dernière entrée remonte à un mois :

root@osestaging1-discourse-ose:/var/www/discourse# tail -f /var/log/unattended-upgrades/unattended-upgrades*.log
==> /var/log/unattended-upgrades/unattended-upgrades-dpkg.log <==
Log started: 2019-11-17  12:34:54
(Reading database ... 44559 files and directories currently installed.)
Removing freetype2-doc (2.9.1-3+deb10u1) ...
Log ended: 2019-11-17  12:34:54

Log started: 2019-11-17  12:34:56
(Reading database ... 44389 files and directories currently installed.)
Removing libjs-jquery (3.3.1~dfsg-3) ...
Log ended: 2019-11-17  12:34:57


==> /var/log/unattended-upgrades/unattended-upgrades.log <==
2019-11-26 16:37:47,549 INFO Initial blacklist : 
2019-11-26 16:37:47,550 INFO Initial whitelist: 
2019-11-26 16:37:47,551 INFO Starting unattended upgrades script
2019-11-26 16:37:47,552 INFO Allowed origins are: origin=Debian,codename=buster,label=Debian, origin=Debian,codename=buster,label=Debian-Security
2019-11-26 16:37:50,811 INFO Checking if system is running on battery is skipped. Please install powermgmt-base package to check power status and skip installing updates when the system is running on battery.
2019-11-26 16:37:50,814 INFO Initial blacklist : 
2019-11-26 16:37:50,815 INFO Initial whitelist: 
2019-11-26 16:37:50,815 INFO Starting unattended upgrades script
2019-11-26 16:37:50,815 INFO Allowed origins are: origin=Debian,codename=buster,label=Debian, origin=Debian,codename=buster,label=Debian-Security
2019-11-26 16:37:53,119 INFO No packages found that can be upgraded unattended and no pending auto-removals
^C
root@osestaging1-discourse-ose:/var/www/discourse# 

…Bien que les minuteries systemd par défaut définies pour unattended-upgrades soient configurées pour s’exécuter au moins une fois par jour :

root@osestaging1-discourse-ose:/var/www/discourse# cat /lib/systemd/system/apt-daily.timer
[Unit]
Description=Daily apt download activities

[Timer]
OnCalendar=*-*-* 6,18:00
RandomizedDelaySec=12h
Persistent=true

[Install]
WantedBy=timers.target
root@osestaging1-discourse-ose:/var/www/discourse# cat /etc/systemd/system/apt-daily.timer.d/override.conf
cat: /etc/systemd/system/apt-daily.timer.d/override.conf: No such file or directory
root@osestaging1-discourse-ose:/var/www/discourse# cat /lib/systemd/system/apt-daily-upgrade.timer
[Unit]
Description=Daily apt upgrade and clean activities
After=apt-daily.timer

[Timer]
OnCalendar=*-*-* 6:00
RandomizedDelaySec=60m
Persistent=true

[Install]
WantedBy=timers.target
root@osestaging1-discourse-ose:/var/www/discourse# cat /etc/systemd/system/apt-daily-upgrade.timer.d/override.conf
cat: /etc/systemd/system/apt-daily-upgrade.timer.d/override.conf: No such file or directory
root@osestaging1-discourse-ose:/var/www/discourse# 

Mais, en effet, ces minuteries sont désactivées.

root@osestaging1-discourse-ose:/var/www/discourse# sudo systemctl status apt-daily.timer
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down
root@osestaging1-discourse-ose:/var/www/discourse# sudo systemctl status apt-daily-upgrade.timer
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down
root@osestaging1-discourse-ose:/var/www/discourse# 

Ceci est confirmé par l’exécution manuelle de unattended-upgrades, qui a justement nécessité une mise à jour de deux paquets liés à git :

root@osestaging1-discourse-ose:/var/www/discourse# sudo unattended-upgrade -d
...
Checking: git ([<Origin component:'main' archive:'stable' origin:'Debian' label:'Debian-Security' site:'security.debian.org' isTrusted:True>])
Checking: git-man ([<Origin component:'main' archive:'stable' origin:'Debian' label:'Debian-Security' site:'security.debian.org' isTrusted:True>])
pkgs that look like they should be upgraded: git   
git-man
...
All upgrades installed
InstCount=0 DelCount=0 BrokenCount=0
Extracting content from /var/log/unattended-upgrades/unattended-upgrades-dpkg.log since 2019-12-24 17:32:55
root@osestaging1-discourse-ose:/var/www/discourse# 

La version de git mise à jour lors de cette exécution de unattended-upgrades était git (1:2.20.1-2+deb10u1). J’ai effectué ce test aujourd’hui (2019-12-24), mais la mise à jour de sécurité était disponible pour Debian Buster (le système d’exploitation sur lequel l’image Docker Discourse est construite) depuis deux semaines (depuis le 2019-12-10) !

Il s’agit en réalité d’une mise à jour assez sérieuse qui corrige plusieurs vulnérabilités, dont deux vecteurs d’exécution de code à distance. Plus d’informations sont disponibles dans l’avis de sécurité Debian 4581-1 :

Mais git n’est qu’un exemple sur lequel je suis tombé par hasard. Il est extrêmement préoccupant que le conteneur Docker Discourse n’applique pas (par défaut) réellement les correctifs de sécurité à son système d’exploitation.

S’agit-il d’un bug ? Ou était-ce une décision intentionnelle de l’équipe Discourse ? Ou s’agit-il simplement du comportement par défaut en attendant une demande de fonctionnalité pour activer unattended-upgrades sur le conteneur Docker Discourse ?

Mon avis est que c’est désactivé volontairement, car cela pourrait casser quelque chose, et l’image elle-même est mise à jour lorsqu’un problème de sécurité grave est détecté.

Vous pouvez l’activer si vous le souhaitez, il suffit d’entrer dans le conteneur.

En fait, je viens de réaliser que l’image Docker de Discourse est configurée pour utiliser runit. systemd est-il même installé dans le conteneur Docker de Discourse ?

Je suppose que l’absence de systemd est ce qui empêche unattended-upgrades de fonctionner.

Cela semble logique. Certaines personnes n’aiment vraiment pas systemd. Vous pouvez donc soit faire confiance au fait que le conteneur de base est régulièrement mis à jour par une équipe professionnelle nombreuse qui en dépend, soit le faire vous-même d’une autre manière en espérant ne rien casser.

Non, il ne l’utilise pas et nous n’avons pas l’intention de le faire utiliser. La recommandation est de maintenir votre système d’exploitation hôte à jour avec les derniers correctifs de sécurité.

Ce qui, pour la record, est le cas.

FYI, j’ai réparé l’installation cassée de unattended-upgrades (qui ne fonctionne pas sans systemd dans le conteneur Docker de Discourse) en le déclenchant via une tâche cron.

J’ai créé le fichier modèle YAML suivant dans mon répertoire /var/discourse/templates/ pour créer la tâche cron nécessaire (notez qu’il contient également une commande pour corriger un bug avec cron présent dans l’image Docker de Discourse basée sur Debian) :

cat << EOF > /var/discourse/templates/unattended-upgrades.template.yml
run:
  - file:
     path: /etc/cron.d/unattended-upgrades
     contents: |+
        ################################################################################
        # Fichier:    /etc/cron.d/unattended-upgrades
        # Version: 0.2
        # Objectif: exécuter unattended-upgrades à la place de systemd. Pour plus d'infos voir
        #           * https://wiki.opensourceecology.org/wiki/Discourse
        #           * https://meta.discourse.org/t/does-discourse-container-use-unattended-upgrades/136296/3
        # Auteur:  Michael Altfield <michael@opensourceecology.org>
        # Créé: 2020-03-23
        # Mis à jour: 2020-04-23
        ################################################################################
        20 04 * * * root /usr/bin/nice /usr/bin/unattended-upgrades --debug
        

  - exec: /bin/echo -e "\n" >> /etc/cron.d/unattended-upgrades
  # corriger le bug cron de Docker https://stackoverflow.com/questions/43323754/cannot-make-remove-an-entry-for-the-specified-session-cron
  - exec: /bin/sed --in-place=.\`date "+%Y%m%d_%H%M%S"\` 's%^\([^#]*\)\(session\s\+required\s\+pam_loginuid\.so\)$%\1#\2%' /etc/pam.d/cron
EOF

Pour activer le fichier modèle ci-dessus, vous devez l’ajouter à votre liste de templates dans le fichier YAML de votre application. Par exemple,

[root@osestaging1 discourse]# head -n20 /var/discourse/containers/app.yml
## ceci est le modèle de conteneur Docker Discourse tout-en-un, autonome
##
## Après avoir apporté des modifications à ce fichier, vous DEVEZ reconstruire
## /var/discourse/launcher rebuild app
##
## SOYEZ *TRÈS* PRUDENT EN ÉDITANT !
## LES FICHIERS YAML SONT EXTRÊMEMENT SENSIBLES AUX ERREURS D'ESPACEMENT OU D'ALIGNEMENT !
## visitez http://www.yamllint.com/ pour valider ce fichier si nécessaire

templates:
  - "templates/unattended-upgrades.template.yml"
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
#  - "templates/web.socketed.template.yml"
  - "templates/web.modsecurity.template.yml"
  - "templates/web.ratelimited.template.yml"
## Décommentez ces deux lignes si vous souhaitez ajouter Lets Encrypt (https)
  #- "templates/web.ssl.template.yml"
  #- "templates/web.letsencrypt.ssl.template.yml"
[root@osestaging1 discourse]# 

Il semble que cela soit activé sur une image Docker Discourse fraîchement installée (exécutant Debian 10). Elle est lancée via runit → cron → /etc/cron.daily/apt-compat → /usr/lib/apt/apt.systemd.daily

Cela fonctionnait également sur une image Docker basée sur Ubuntu 16 vieille d’un an (basée sur discourse/base:2.0.20190321-0122), et je suis presque certain qu’aucune personnalisation n’avait été effectuée.

systemd n’étant pas installé, il ne s’exécute jamais réellement.