Recherche effectué dans :

Filtre actif, cliquez pour en enlever un tag :

Cliquez sur un tag pour affiner votre recherche :

Résultat de la recherche (92 notes) :

Object Storage append-only backup playground #DevOps, #admin-sys, #scaleway, #backup, #archive, #playground

Pour un projet, je dois mettre en place un système de sauvegarde sécurisé (WORM).
Ici, "sécurisé" signifie :

  • qui empêche la suppression accidentelle ou intentionnelle des données ;
  • une protection contre les ransomwares.

Pour cela, j'ai décidé de sauvegarder ces données chez deux fournisseurs d'Object Storage :

Je viens de publier le playground append-only-backup-playground qui m'a permis de tester la configuration de bucket Backblaze et Scaleway.

Object Storage append-only backup playground

In this "playground" repository, I explore different methods to configure object storage services in append-only or write-once-read-many (WORM) mode.

source

J'ai testé deux méthodes qui interdisent la suppression des données :

  • 1. La première, basée sur une clé d'accès avec des droits limités : l'interdiction de supprimer des fichiers. Si un attaquant parvient à s'infiltrer sur un serveur qui effectue des sauvegardes, la clé ne lui permettra pas d'effacer les anciennes sauvegardes.
  • 2. La seconde méthode plus stricte utilise la fonctionnalité object lock en mode GOVERNANCE ou COMPLIANCE.

Je vous recommande d'être vigilant avec le mode COMPLIANCE, car il vous sera impossible de supprimer les fichiers avant leur date de rétention, sauf si vous décidez de supprimer entièrement votre compte client !

Personnellement, je recommande d'utiliser la méthode 1 pour tous les environnements de développement.
En général, je pense que la méthode 1 est suffisante, même pour les environnements de production. Mais si les données sont vraiment critiques, alors je conseille le mode GOVERNANCE ou COMPLIANCE.

Le repository append-only-backup-playground contient 4 playgrounds :

  • Pour tester la méthode 1 :
    • /scaleway/ : configuration d'un bucket Scaleway Object Storage et une clé qui ne peut pas supprimer de fichier. L'option de versionning est activée.
    • /backblaze/ : configuration d'un bucket Backblaze et une clé qui ne peut pas supprimer de fichier. L'option de versionning est activée.
  • Pour tester la méthode 2 :
    • /scaleway-object-lock/ : la même chose que /scaleway/ avec en plus la configuration de object lock en mode GOVERNANCE avec une durée de rétention définie à 1 jour.
    • /backblaze-object-lock/ : la même chose que /backblaze/ avec en plus la configuration de object lock en mode GOVERNANCE avec une durée de rétention définie à 1 jour.

Journal du jeudi 27 février 2025 à 11:02 #monitoring, #selfhosting, #DevOps, #JaiDécouvert

Au mois d'août 2024, je disais :

Je cherche depuis plusieurs années une solution pour surveiller la date d'expiration des noms de domaine en analysant le contenu de Whois.

#JaiDécouvert cet exporter Prometheus qui correspond exactement à mon besoin : https://github.com/shift/domain_exporter

2024-08-28_1131

Ce matin, en travaillant sur la note "Je découvre Beszel", #JaiDécouvert que Gatus permet de monitorer l'expiration d'un domaine :

You can monitor the expiration of a domain with all endpoint types except for DNS by using the [DOMAIN_EXPIRATION] placeholder.

The aforementioned placeholder resolves to a duration (e.g. 734h22m5s), as such, the value you should compare it to should also be a duration (e.g. 720h, 1h30m). Here's an example of what a condition may look like:

[DOMAIN_EXPIRATION] > 720h

The condition above will fail if the domain expires in less than 720 hours (30 days).

source

Journal du jeudi 27 février 2025 à 10:41 #golang, #DevOps, #selfhosting, #JaiDécouvert, #JaimeraisUnJour

En travaillant sur la note "Je découvre Beszel", #JaiDécouvert ici un autre projet de monitoring intéressant : Dozzle (https://dozzle.dev/).

Dozzle is a small lightweight application with a web based interface to monitor Docker logs. It doesn’t store any log files. It is for live monitoring of your container logs only.

source

Là aussi, Dozzle est un projet en Golang, commencé fin 2018.

Dozzle est une alternative à Loki + Grafana.

#JaimeraisUnJour déployer Dozzle pour le tester et si ce test est concluant, je l'intégrerai peut-être à ma stack minimaliste de monitoring en complément de Beszel et Gatus.

Je découvre Beszel #JaiDécouvert, #OnMaPartagé, #monitoring, #DevOps, #Doctrine

Un ami m'a partagé le projet Beszel (https://beszel.dev/).

Beszel is a lightweight server monitoring platform that includes Docker statistics, historical data, and alert functions.

It has a friendly web interface, simple configuration, and is ready to use out of the box. It supports automatic backup, multi-user, OAuth authentication, and API access.

source

Beszel est codé en Golang, il est très récent, il a commencé en été 2024, c'est sans doute pour cela que je ne l'avais jamais croisé.

De prime abord, j'ai pensé que Beszel était un outil de Status / Uptime pages comme Uptime Kuma ou Gatus, mais ce n'est pas le cas.

Je qualifierai plutôt Beszel d'alternative "plug and play" de Prometheus + Grafana + node_exporter + cAdvisor.

Alors que l'annonce de Beszel a fait "choux blanc" sur Hacker News « Beszel: Lightweight server resource monitoring with history, Docker stats,alerts », le projet a suscité plus de réaction — 270 commentaires — sur Subreddit SelfHosted : « I just released Beszel, a server monitoring hub with historical data, docker stats, and alerts. It's a lighter and simpler alternative to Grafana + Prometheus or Checkmk. Any feedback is appreciated! ».

Les retours sont très positifs 🙂 :

« There is beauty in simplicity. Very nice little application! »

« Kiss »

« Just installed on all of my servers, gorgeous project, simple but also not simple. »

« Awesome work. I think you identified a good use case for the self hosting community, a simple server monitor running as a simple service. I will give it a go soon! »

« I never installed Grafana and Prometheus because it’s overkill for my little server.. but this looks really good! I’ll give it a go »

Prometheus propose bien plus d'exporter que Beszel, mais je pense que Beszel est un bon point de départ pour une stack de monitoring minimaliste.

À l'avenir, mon choix par défaut en matière monitoring sera probablement un couple Beszel + Gatus. Si des besoins plus avancés émergent, comme du monitoring poussé de PostgreSQL, Redis ou d'autres services, j'envisagerai alors de commencer la mise en place du couple Prometheus + Grafana.

Journal du vendredi 07 février 2025 à 16:12 #DevOps

Exemples d'outils qui suivent de modèle Orchestration Push Mode :

Exemples d'outils qui suivent le modèle Orchestration Pull Mode :

Journal du vendredi 07 février 2025 à 14:03 #DevOps, #admin-sys, #software-engineering, #paradigme, #Doctrine

Pendant l'année 2014, Athoune m'a fait découvrir les concepts DevOps "Baking" et "Frying".

Je le remercie, car ce sont des concepts que je considère très importants pour comprendre les différents paradigmes de déploiement.

Je n'ai aucune idée dans quelles conditions il avait découvert ces concepts. J'ai essayé de faire des recherches limitées à l'année 2014 et je suis tombé sur cette photo :

J'en déduis que cela devait être un sujet à la méthode dans l'écosystème DevOps de 2014.

Cet ami me l'avait très bien expliqué avec une analogie du type :

« Le baking en DevOps, c’est comme dans un restaurant où les plats sont préparés en cuisine et ensuite apportés tout prêt salle à la table du client. Le frying, c’est comme si le plat était préparé directement en salle sur la table du client. »

Bien que cette analogie ne soit pas totalement rigoureuse, elle m'a bien permis de saisir, en 2014, le paradigme Docker qui consiste à préparer des images de container en amont. Ce paradigme permet d'installer, de configurer ces images "en cuisine", donc pas sur les serveurs de production, "de goûter les plats" et de les envoyer ensuite de manière prédictible sur le serveur de production.

Ces images peuvent être construites soit sur la workstation du développeur ou mieux, sur des serveurs dédiés à cette fonction, comme Gitlab-Runner

Définitions proposées par LLaMa :

Baking (ou "Image Baking") : Il s'agit de créer une image de serveur prête à l'emploi, avec tous les logiciels et les configurations nécessaires déjà installés et configurés. Cette image est ensuite utilisée pour déployer de nouveaux serveurs, qui seront ainsi identiques et prêts à fonctionner immédiatement. L'avantage de cette approche est qu'elle permet de réduire le temps de déploiement et d'assurer la cohérence des environnements.

Frying (ou "Server Frying") : Il s'agit de déployer un serveur "nu" et de le configurer et de l'installer à la volée, en utilisant des outils d'automatisation tels que Ansible, Puppet ou Chef. Cette approche permet de personnaliser la configuration de chaque serveur en fonction des besoins spécifiques de l'application ou du service.

Exemple :

Cas d'usage Baking Frying
Docker Construire une image complète (docker build) et la stocker dans un registre Lancer un conteneur minimal et installer les dépendances au démarrage.
Machines virtuelles (VMs) Créer une image VM avec Packer et la déployer telle quelle Démarrer une VM de base et appliquer un script d’installation à la volée
CI/CD Compiler et packager une application en image prête à être déployée Construire l’application à chaque déploiement sur la machine cible

En 2014, lorsque le concept de baking m’a été présenté, j’ai immédiatement été enthousiasmé, car il répondait à trois problèmes que je cherchais à résoudre :

  • Réduire les risques d’échec d’une installation sur le serveur de production
  • Limiter la durée de l’indisponibilité (pendant la phase d’installation)
  • Éviter d'augmenter la charge du serveur durant les opérations de build lors de l’installation

Depuis, j'évite au maximum le frying et j'ai intégré le baking dans ma doctrine d'artisan développeur.

Comment tu déploies tes containers Docker en production sans Kubernetes ? #Kubernetes, #Doctrine, #deployment, #DevOps, #admin-sys, #software-engineering

Début novembre un ami me posait la question :

Quand tu déploies des conteneurs en prod, sans k8s, tu fais comment ?

Après 3 mois d'attente, voici ma réponse 🙂.

Mon contexte

Tout d'abord, un peu de contexte. Cela fait 25 ans que je travaille sur des projets web, et tous les projets sur lesquels j'ai travaillé pouvaient être hébergés sur un seul et unique serveur baremetal ou une Virtual machine, sans jamais nécessiter de scalabilité horizontale.

Je n'ai jamais eu besoin de serveurs avec plus de 96Go de RAM pour faire tourner un service en production. Il convient de noter que, dans 80% des cas, 8 Go ou 16 Go étaient largement suffisants.

Cela dit, j'ai également eu à gérer des infrastructures comportant plusieurs serveurs : 10, 20, 30 serveurs. Ces serveurs étaient généralement utilisés pour héberger une infrastructure de soutien (Platform infrastructure) à destination des développeurs. Par exemple :

  • Environnements de recettage
  • Serveurs pour faire tourner Gitlab-Runner
  • Sauvegarde des données
  • Etc.

Ce contexte montre que je n'ai jamais eu à gérer le déploiement de services à très forte charge, comme ceux que l'on trouve sur des plateformes telles que Deezer, le site des impôts, Radio France, Meetic, la Fnac, Cdiscount, France Travail, Blablacar, ou encore Doctolib. La méthode que je décris dans cette note ne concerne pas ce type d'infrastructure.

Ma méthode depuis 2015

Dans cette note, je ne vais pas retracer l'évolution complète de mes méthodes de déploiement, mais plutôt me concentrer sur deux d'entre elles : l'une que j'utilise depuis 2015, et une déclinaison adoptée en 2020.

Voici les principes que j'essaie de suivre et qui constituent le socle de ma doctrine en matière de déploiement de services :

En pratique, j'utilise Ansible pour déployer un fichier docker-compose.yml sur le serveur de production et ensuite lancer les services.

Je précise que cette note ne traite pas de la préparation préalable du serveur, de l'installation de Docker, ni d'autres aspects similaires. Afin de ne pas alourdir davantage cette note, je n'aborde pas non plus les questions de Continuous Integration ou de Continuous Delivery.

Imaginons que je souhaite déployer le lecteur RSS Miniflux connecté à un serveur PostgreSQL.
Voici les opérations effectuées par le rôle Ansible à distance sur le serveur de production :

    1. Création d'un dossier /srv/miniflux/
    1. Upload de /srv/miniflux/docker-compose.yml avec le contenu suivant :
services:
  postgres:
    image: postgres:17
    restart: unless-stopped
    environment:
      POSTGRES_DB: miniflux
      POSTGRES_USER: miniflux
      POSTGRES_PASSWORD: password
    volumes:
      - postgres:/var/lib/postgresql/data/
    healthcheck:
      test: ['CMD', 'pg_isready']
      interval: 10s
      start_period: 30s

  miniflux:
    image: miniflux/miniflux:2.2.5
    ports:
    - 8080:8080
    environment:
      DATABASE_URL: postgres://miniflux:password@postgres/miniflux?sslmode=disable
      RUN_MIGRATIONS: 1
      CREATE_ADMIN: 1
      ADMIN_USERNAME: johndoe
      ADMIN_PASSWORD: secret
    healthcheck:
      test: ["CMD", "/usr/bin/miniflux", "-healthcheck", "auto"]
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  postgres:
     name: miniflux_postgres
    1. Depuis le dossier /srv/miniflux/ lancement de la commande docker compose up -d --remove-orphans --wait --pull always

Voilà, c'est tout 🙂.

En 2020, j'enlève "une couche"

J'aime enlever des couches et en 2020, je me suis demandé si je pouvais pratiquer avec élégance la méthode Remote Execution sans Ansible.
Mon objectif était d'utiliser seulement ssh et un soupçon de Bash.

Voici le résultat de mes expérimentations.

J'ai besoin de deux fichiers.

  • _payload_deploy_miniflux.sh
  • deploy_miniflux.sh

Voici le contenu de _payload_deploy_miniflux.sh :

#!/usr/bin/env bash
set -e

PROJECT_FOLDER="/srv/miniflux/"

mkdir -p ${PROJECT_FOLDER}

cat <<EOF > ${PROJECT_FOLDER}docker-compose.yaml
services:
  postgres:
    image: postgres:17
    restart: unless-stopped
    environment:
      POSTGRES_DB: miniflux
      POSTGRES_USER: miniflux
      POSTGRES_PASSWORD: {{ .Env.MINIFLUX_POSTGRES_PASSWORD }}
    volumes:
      - postgres:/var/lib/postgresql/data/
    healthcheck:
      test: ['CMD', 'pg_isready']
      interval: 10s
      start_period: 30s

  miniflux:
    image: miniflux/miniflux:2.2.5
    ports:
    - 8080:8080
    environment:
      DATABASE_URL: postgres://miniflux:{{ .Env.MINIFLUX_POSTGRES_PASSWORD }}@postgres/miniflux?sslmode=disable
      RUN_MIGRATIONS: 1
      CREATE_ADMIN: 1
      ADMIN_USERNAME: johndoe
      ADMIN_PASSWORD: {{ .Env.MINIFLUX_ADMIN_PASSWORD }}
    healthcheck:
      test: ["CMD", "/usr/bin/miniflux", "-healthcheck", "auto"]
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  postgres:
     name: miniflux_postgres

EOF

cd ${PROJECT_FOLDER}

docker compose pull
docker compose up -d --remove-orphans --wait

Voici le contenu de deploy_miniflux.sh :

#!/usr/bin/env bash
set -e

cd "$(dirname "$0")/../"

gomplate -f _payload_deploy_miniflux.sh | ssh root@$SERVER1_IP 'bash -s'

J'utilise gomplate pour remplacer dynamiquement les secrets dans le script _payload_deploy_miniflux.sh.

En conclusion, pour déployer une nouvelle version, j'ai juste à exécuter :

$ ./deploy_miniflux.sh

Je trouve cela minimaliste et de plus, l'exécution est bien plus rapide que la solution Ansible.

Ce type de script peut ensuite être exécuté aussi bien manuellement par un développeur depuis sa workstation, que via GitLab-CI ou même Rundeck.

Pour un exemple plus détaillé, consultez ce repository : https://github.com/stephane-klein/poc-bash-ssh-docker-deployement-example


Bien entendu, si vous souhaitez déployer votre propre application que vous développez, vous devez ajouter à cela la partie baking, c'est-à-dire, le docker build qui prépare votre image, l'uploader sur un Docker registry… Généralement je réalise cela avec GitLab-CI/CD ou GitHub Actions.


Objections

Certains DevOps me disent :

  • « Mais on ne fait pas ça pour de la production ! Il faut utiliser Kubernetes ! »
  • « Comment ! Tu n'utilises pas Kubernetes ? »

Et d'autres :

  • « Il ne faut au grand jamais utiliser docker-compose en production ! »

Ne jamais utiliser docker compose en production ?

J'ai reçu cette objection en 2018. J'ai essayé de comprendre les raisons qui justifiaient que ce développeur considère l'usage de docker compose en production comme un Antipattern.

Si mes souvenirs sont bons, je me souviens que pour lui, la bonne méthode conscistait à déclarer les états des containers à déployer avec le module Ansible docker_container (le lien est vers la version de 2018, depuis ce module s'est grandement amélioré).

Je n'ai pas eu plus d'explications 🙁.

J'ai essayé d'imaginer ses motivations.

J'en ai trouvé une que je ne trouve pas très pertinente :

  • Uplodaer un fichier docker-compose.yml en production pour ensuite lancer des fonctions distantes sur celui-ci est moins performant que manipuler docker-engine à distance.

J'en ai imaginé une valable :

  • En déclarant la configuration de services Docker uniquement dans le rôle Ansible cela garantit qu'aucun développeur n'ira modifier et manipuler directement le fichier docker-compose.yml sur le serveur de production.

Je trouve que c'est un très bon argument 👍️.

Cependant, cette méthode a à mes yeux les inconvénients suivants :

  • Je maitrise bien mieux la syntaxe de docker compose que la syntaxe du module Ansible community.docker.docker_container
  • J'utilise docker compose au quotidien sur ma workstation et je n'ai pas envie d'apprendre une syntaxe supplémentaire uniquement pour le déploiement.
  • Je pense que le nombre de développeurs qui maîtrisent docker compose est suppérieur au nombre de ceux qui maîtrisent le module Ansible community.docker.docker_container.
  • Je ne suis pas utilisateur maximaliste de la méthode Remote Execution. Dans certaines circonstances, je trouve très pratique de pouvoir manipuler docker compose dans une session ssh directement sur un serveur. En période de stress ou de debug compliqué, je trouve cela pratique. J'essaie d'être assez rigoureux pour ne pas oublier de reporter mes changements effectués directement le serveur dans les scripts de déploiements (configuration as code).

Tu dois utiliser Kubernetes !

Alors oui, il y a une multitude de raisons valables d'utiliser Kubernetes. C'est une technologie très puissante, je n'ai pas le moindre doute à ce sujet.
J'ai une expérience dans ce domaine, ayant utilisé Kubernetes presque quotidiennement dans un cadre professionnel de janvier 2016 à septembre 2017. J'ai administré un petit cluster auto-managé composé de quelques nœuds et y ai déployé diverses applications.

Ceci étant dit, je rappelle mon contexte :

Cela fait 25 ans que je travaille sur des projets web, et tous les projets sur lesquels j'ai travaillé pouvaient être hébergés sur un seul et unique serveur baremetal ou une Virtual machine, sans jamais nécessiter de scalabilité horizontale.

Je n'ai jamais eu besoin de serveurs avec plus de 96Go de RAM pour faire tourner un service en production. Il convient de noter que, dans 80% des cas, 8 Go ou 16 Go étaient largement suffisants.

Je pense que faire appel à Kubernetes dans ce contexte est de l'overengineering.

Je dois avouer que j'envisage d'expérimenter un usage minimaliste de K3s (attention au "3", je n'ai pas écrit k8s) pour mes déploiements. Mais je sais que Kubernetes est un rabbit hole : Helm, Kustomize, Istio, Helmfile, Grafana Tanka… J'ai peur de tomber dans un Yak!.

D'autre part, il existe déjà un pourcentage non négligeable de développeur qui ne maitrise ni Docker, ni docker compose et dans ces conditions, faire le choix de Kubernetes augmenterait encore plus la barrière à l'entrée permettant à des collègues de pouvoir comprendre et intervenir sur les serveurs d'hébergement.

C'est pour cela que ma doctrine d'artisan développeur consiste à utiliser Kubernetes seulement à partir du moment où je rencontre des besoins de forte charge, de scalabilité.

Journal du vendredi 07 février 2025 à 10:48 #DevOps, #nginx, #admin-sys, #mémento, #aide-mémoire

Note de type #mémento pour ajouter le support healthcheck à nginx-proxy et nginx-proxy acme-companion.

J'ai effectué des recherches dans les dépôts GitHub des projets :

J'ai aussi trouvé « Adding health check support to nginx-proxy nginx.tmpl file · nginx-proxy/nginx-proxy » qui est un sujet qui m'intéresse, mais pas en lien avec le sujet de cette note.

N'ayant rien trouvé de clés en main, voici ci-dessous mon implémentation.

Je ne suis pas certain qu'elle soit très robuste 🤔.

Elle a tout de même l'avantage de me permettre d'utiliser l'option --wait dans docker-compose up -d --remove-orphans --wait.

J'ai partagé aussi posté un commentaire à l'issue pour partager mon implémentation.

# docker-compose.yml
services:
  nginx-proxy:
    image: nginxproxy/nginx-proxy:1.6.4
    container_name: nginx-proxy
    restart: unless-stopped
    network_mode: "host"
    volumes:
      - ./vhost.d/:/etc/nginx/vhost.d:rw
      - ./htpasswd:/etc/nginx/htpasswd:ro
      - html:/usr/share/nginx/html
      - certs:/etc/nginx/certs:ro
      - /var/run/docker.sock:/tmp/docker.sock:ro
    healthcheck:
      test: ["CMD-SHELL", "nginx -t && kill -0 $$(cat /var/run/nginx.pid)"]
      interval: 10s
      timeout: 2s
      retries: 3

  acme-companion:
    image: nginxproxy/acme-companion:2.5.1
    restart: unless-stopped
    volumes_from:
      - nginx-proxy
    depends_on:
      - "nginx-proxy"
    environment:
      DEFAULT_EMAIL: contact@stephane-klein.info
    volumes:
      - certs:/etc/nginx/certs:rw
      - acme:/etc/acme.sh
      - /var/run/docker.sock:/var/run/docker.sock:ro
    healthcheck:
      test: ["CMD-SHELL", "ps aux | grep -v grep | grep -q '/bin/bash /app/start.sh'"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 10s

volumes:
  html:
  certs:
  acme:

Journal du jeudi 30 janvier 2025 à 12:02 ##JaiDécouvert, #DevOps, #linux, #aide-mémoire, #aide

Note de type #aide-mémoire : contrairement à ~/.zprofile, .zshenv est chargé même lors de l'exécution d'une session ssh en mode non interactif, par exemple :

$ ssh user@host 'echo "Hello, world!"'

Je me suis intéressé à ce sujet parce que mes scripts exécutés par ssh dans le cadre du projet /poc-capacitor/ n'avaient pas accès aux outils mis à disposition par Homebrew et Mise.

J'ai creusé le sujet et j'ai découvert que .zprofile était chargé seulement dans les cas suivants :

  • « login shell »
  • « interactive shell »

Un login shell est un shell qui est lancé lors d'une connexion utilisateur. C'est le type de shell qui exécute des fichiers de configuration spécifiques pour préparer l'environnement utilisateur. Un login shell se comporte comme si tu te connectais physiquement à une machine ou à un serveur.

Un shell interactif est un shell dans lequel tu peux entrer des commandes de manière active, et il attend des entrées de ta part. Un shell interactif est conçu pour interagir avec l'utilisateur et permet de saisir des commandes, d'exécuter des programmes, de lancer des scripts, etc.

ChatGPT

Suite à cela, dans ce commit "Move zsh config from .zprofile to .zshenv", j'ai déplacé la configuration de Homebrew et Mise de ~/.zprofile vers .zshenv.

Cela donne ceci une fois configuré :

$ cat .zshenv
eval "$(/opt/homebrew/bin/brew shellenv)"
eval "$(mise activate zsh)"

Mais, attention, « As /etc/zshenv is run for all instances of zsh ». Je pense que ce n'est pas forcément une bonne idée d'appliquer cette configuration sur une workstation, parce que cela peut "ralentir" légèrement le système en lançant inutilement ces commandes.

ChatGPT me conseille cette configuration pour éviter cela :

# Ne charge Brew et Mise que si on est dans un shell interactif ou SSH
if [[ -t 1 || -n "$SSH_CONNECTION" ]]; then
  eval "$(/opt/homebrew/bin/brew shellenv)"
  eval "$(mise activate zsh)"
fi

Journal du mardi 21 janvier 2025 à 10:45 #ssl, #certificat, #DevOps, #selfhosting, #JaiDécouvert, #OnMaPartagé

Alexandre m'a fait découvrir testssl.sh (https://github.com/testssl/testssl.sh) :

testssl.sh is a free command line tool which checks a server's service on any port for the support of TLS/SSL ciphers, protocols as well as some cryptographic flaws.

source

Voici ci-dessous le résultat pour mon domaine sklein.xyz.

Je lis : Overall Grade: A+

Quelques précisions concernant la configuration derrière sklein.xyz :

$ docker run --rm -ti  drwetter/testssl.sh sklein.xyz

#####################################################################
  testssl.sh version 3.2rc3 from https://testssl.sh/dev/

  This program is free software. Distribution and modification under
  GPLv2 permitted. USAGE w/o ANY WARRANTY. USE IT AT YOUR OWN RISK!

  Please file bugs @ https://testssl.sh/bugs/

#####################################################################

  Using OpenSSL 1.0.2-bad   [~183 ciphers]
  on 43cf528ca9c5:/home/testssl/bin/openssl.Linux.x86_64

 Start 2025-01-21 09:45:05                -->> 51.159.34.231:443 (sklein.xyz) <<--

 rDNS (51.159.34.231):   51-159-34-231.rev.poneytelecom.eu.
 Service detected:       HTTP

 Testing protocols via sockets except NPN+ALPN

 SSLv2      not offered (OK)
 SSLv3      not offered (OK)
 TLS 1      not offered
 TLS 1.1    not offered
 TLS 1.2    offered (OK)
 TLS 1.3    offered (OK): final
 NPN/SPDY   not offered
 ALPN/HTTP2 h2, http/1.1 (offered)

 Testing cipher categories

 NULL ciphers (no encryption)                      not offered (OK)
 Anonymous NULL Ciphers (no authentication)        not offered (OK)
 Export ciphers (w/o ADH+NULL)                     not offered (OK)
 LOW: 64 Bit + DES, RC[2,4], MD5 (w/o export)      not offered (OK)
 Triple DES Ciphers / IDEA                         not offered
 Obsoleted CBC ciphers (AES, ARIA etc.)            not offered
 Strong encryption (AEAD ciphers) with no FS       not offered
 Forward Secrecy strong encryption (AEAD ciphers)  offered (OK)


 Testing server's cipher preferences

Hexcode  Cipher Suite Name (OpenSSL)       KeyExch.   Encryption  Bits     Cipher Suite Name (IANA/RFC)
-----------------------------------------------------------------------------------------------------------------------------
SSLv2
 -
SSLv3
 -
TLSv1
 -
TLSv1.1
 -
TLSv1.2 (no server order, thus listed by strength)
 xc030   ECDHE-RSA-AES256-GCM-SHA384       ECDH 521   AESGCM      256      TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
 x9f     DHE-RSA-AES256-GCM-SHA384         DH 2048    AESGCM      256      TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
 xcca8   ECDHE-RSA-CHACHA20-POLY1305       ECDH 521   ChaCha20    256      TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
 xccaa   DHE-RSA-CHACHA20-POLY1305         DH 2048    ChaCha20    256      TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
 xc02f   ECDHE-RSA-AES128-GCM-SHA256       ECDH 521   AESGCM      128      TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
 x9e     DHE-RSA-AES128-GCM-SHA256         DH 2048    AESGCM      128      TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
TLSv1.3 (no server order, thus listed by strength)
 x1302   TLS_AES_256_GCM_SHA384            ECDH 253   AESGCM      256      TLS_AES_256_GCM_SHA384
 x1303   TLS_CHACHA20_POLY1305_SHA256      ECDH 253   ChaCha20    256      TLS_CHACHA20_POLY1305_SHA256
 x1301   TLS_AES_128_GCM_SHA256            ECDH 253   AESGCM      128      TLS_AES_128_GCM_SHA256

 Has server cipher order?     no
 (limited sense as client will pick)

 Testing robust forward secrecy (FS) -- omitting Null Authentication/Encryption, 3DES, RC4

 FS is offered (OK)           TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-GCM-SHA384
                              ECDHE-RSA-CHACHA20-POLY1305 DHE-RSA-CHACHA20-POLY1305 TLS_AES_128_GCM_SHA256 ECDHE-RSA-AES128-GCM-SHA256
                              DHE-RSA-AES128-GCM-SHA256
 Elliptic curves offered:     prime256v1 secp384r1 secp521r1 X25519 X448
 Finite field group:          ffdhe2048 ffdhe3072 ffdhe4096 ffdhe6144 ffdhe8192
 TLS 1.2 sig_algs offered:    RSA+SHA224 RSA+SHA256 RSA+SHA384 RSA+SHA512 RSA-PSS-RSAE+SHA256 RSA-PSS-RSAE+SHA384 RSA-PSS-RSAE+SHA512
 TLS 1.3 sig_algs offered:    RSA-PSS-RSAE+SHA256 RSA-PSS-RSAE+SHA384 RSA-PSS-RSAE+SHA512

 Testing server defaults (Server Hello)

 TLS extensions (standard)    "renegotiation info/#65281" "server name/#0" "EC point formats/#11" "status request/#5" "supported versions/#43"
                              "key share/#51" "supported_groups/#10" "max fragment length/#1" "application layer protocol negotiation/#16"
                              "extended master secret/#23"
 Session Ticket RFC 5077 hint no -- no lifetime advertised
 SSL Session ID support       yes
 Session Resumption           Tickets no, ID: yes
 TLS clock skew               Random values, no fingerprinting possible
 Certificate Compression      none
 Client Authentication        none
 Signature Algorithm          SHA256 with RSA
 Server key size              RSA 4096 bits (exponent is 65537)
 Server key usage             Digital Signature, Key Encipherment
 Server extended key usage    TLS Web Server Authentication, TLS Web Client Authentication
 Serial                       048539E72F864A52E28F6CBEFF15527F75C5 (OK: length 18)
 Fingerprints                 SHA1 5B966867DF42BC654DA90FADFDB93B6C77DD7053
                              SHA256 E79D3ACF988370EF01620C00F003E92B137FFB4EE992A5B1CE3755931561629D
 Common Name (CN)             sklein.xyz  (CN in response to request w/o SNI: letsencrypt-nginx-proxy-companion )
 subjectAltName (SAN)         cv.stephane-klein.info garden.stephane-klein.info sklein.xyz stephane-klein.info
 Trust (hostname)             Ok via SAN and CN (SNI mandatory)
 Chain of trust               Ok
 EV cert (experimental)       no
 Certificate Validity (UTC)   63 >= 30 days (2024-12-26 08:40 --> 2025-03-26 08:40)
 ETS/"eTLS", visibility info  not present
 Certificate Revocation List  --
 OCSP URI                     http://r11.o.lencr.org
 OCSP stapling                offered, not revoked
 OCSP must staple extension   --
 DNS CAA RR (experimental)    not offered
 Certificate Transparency     yes (certificate extension)
 Certificates provided        2
 Issuer                       R11 (Let's Encrypt from US)
 Intermediate cert validity   #1: ok > 40 days (2027-03-12 23:59). R11 <-- ISRG Root X1
 Intermediate Bad OCSP (exp.) Ok


 Testing HTTP header response @ "/"

 HTTP Status Code             302 Moved Temporarily, redirecting to "https://sklein.xyz/fr/"
 HTTP clock skew              0 sec from localtime
 Strict Transport Security    365 days=31536000 s, just this domain
 Public Key Pinning           --
 Server banner                nginx/1.27.1
 Application banner           --
 Cookie(s)                    (none issued at "/") -- maybe better try target URL of 30x
 Security headers             --
 Reverse Proxy banner         --


 Testing vulnerabilities

 Heartbleed (CVE-2014-0160)                not vulnerable (OK), no heartbeat extension
 CCS (CVE-2014-0224)                       not vulnerable (OK)
 Ticketbleed (CVE-2016-9244), experiment.  not vulnerable (OK), no session ticket extension
 ROBOT                                     Server does not support any cipher suites that use RSA key transport
 Secure Renegotiation (RFC 5746)           supported (OK)
 Secure Client-Initiated Renegotiation     not vulnerable (OK)
 CRIME, TLS (CVE-2012-4929)                not vulnerable (OK)
 BREACH (CVE-2013-3587)                    no gzip/deflate/compress/br HTTP compression (OK)  - only supplied "/" tested
 POODLE, SSL (CVE-2014-3566)               not vulnerable (OK), no SSLv3 support
 TLS_FALLBACK_SCSV (RFC 7507)              No fallback possible (OK), no protocol below TLS 1.2 offered
 SWEET32 (CVE-2016-2183, CVE-2016-6329)    not vulnerable (OK)
 FREAK (CVE-2015-0204)                     not vulnerable (OK)
 DROWN (CVE-2016-0800, CVE-2016-0703)      not vulnerable on this host and port (OK)
                                           make sure you don't use this certificate elsewhere with SSLv2 enabled services, see
                                           https://search.censys.io/search?resource=hosts&virtual_hosts=INCLUDE&q=E79D3ACF988370EF01620C00F003E92B137FFB4EE992A5B1CE3755931561629D
 LOGJAM (CVE-2015-4000), experimental      not vulnerable (OK): no DH EXPORT ciphers, no common prime detected
 BEAST (CVE-2011-3389)                     not vulnerable (OK), no SSL3 or TLS1
 LUCKY13 (CVE-2013-0169), experimental     not vulnerable (OK)
 Winshock (CVE-2014-6321), experimental    not vulnerable (OK)
 RC4 (CVE-2013-2566, CVE-2015-2808)        no RC4 ciphers detected (OK)


 Running client simulations (HTTP) via sockets

 Browser                      Protocol  Cipher Suite Name (OpenSSL)       Forward Secrecy
------------------------------------------------------------------------------------------------
 Android 6.0                  TLSv1.2   ECDHE-RSA-AES128-GCM-SHA256       256 bit ECDH (P-256)
 Android 7.0 (native)         TLSv1.2   ECDHE-RSA-AES128-GCM-SHA256       256 bit ECDH (P-256)
 Android 8.1 (native)         TLSv1.2   ECDHE-RSA-AES128-GCM-SHA256       253 bit ECDH (X25519)
 Android 9.0 (native)         TLSv1.3   TLS_AES_128_GCM_SHA256            253 bit ECDH (X25519)
 Android 10.0 (native)        TLSv1.3   TLS_AES_128_GCM_SHA256            253 bit ECDH (X25519)
 Android 11 (native)          TLSv1.3   TLS_AES_128_GCM_SHA256            253 bit ECDH (X25519)
 Android 12 (native)          TLSv1.3   TLS_AES_128_GCM_SHA256            253 bit ECDH (X25519)
 Chrome 79 (Win 10)           TLSv1.3   TLS_AES_128_GCM_SHA256            253 bit ECDH (X25519)
 Chrome 101 (Win 10)          TLSv1.3   TLS_AES_128_GCM_SHA256            253 bit ECDH (X25519)
 Firefox 66 (Win 8.1/10)      TLSv1.3   TLS_AES_128_GCM_SHA256            253 bit ECDH (X25519)
 Firefox 100 (Win 10)         TLSv1.3   TLS_AES_128_GCM_SHA256            253 bit ECDH (X25519)
 IE 6 XP                      No connection
 IE 8 Win 7                   No connection
 IE 8 XP                      No connection
 IE 11 Win 7                  TLSv1.2   DHE-RSA-AES256-GCM-SHA384         2048 bit DH
 IE 11 Win 8.1                TLSv1.2   DHE-RSA-AES256-GCM-SHA384         2048 bit DH
 IE 11 Win Phone 8.1          No connection
 IE 11 Win 10                 TLSv1.2   ECDHE-RSA-AES256-GCM-SHA384       256 bit ECDH (P-256)
 Edge 15 Win 10               TLSv1.2   ECDHE-RSA-AES256-GCM-SHA384       253 bit ECDH (X25519)
 Edge 101 Win 10 21H2         TLSv1.3   TLS_AES_128_GCM_SHA256            253 bit ECDH (X25519)
 Safari 12.1 (iOS 12.2)       TLSv1.3   TLS_CHACHA20_POLY1305_SHA256      253 bit ECDH (X25519)
 Safari 13.0 (macOS 10.14.6)  TLSv1.3   TLS_CHACHA20_POLY1305_SHA256      253 bit ECDH (X25519)
 Safari 15.4 (macOS 12.3.1)   TLSv1.3   TLS_AES_128_GCM_SHA256            253 bit ECDH (X25519)
 Java 7u25                    No connection
 Java 8u161                   TLSv1.2   ECDHE-RSA-AES256-GCM-SHA384       256 bit ECDH (P-256)
 Java 11.0.2 (OpenJDK)        TLSv1.3   TLS_AES_128_GCM_SHA256            256 bit ECDH (P-256)
 Java 17.0.3 (OpenJDK)        TLSv1.3   TLS_AES_256_GCM_SHA384            253 bit ECDH (X25519)
 go 1.17.8                    TLSv1.3   TLS_AES_128_GCM_SHA256            253 bit ECDH (X25519)
 LibreSSL 2.8.3 (Apple)       TLSv1.2   ECDHE-RSA-CHACHA20-POLY1305       253 bit ECDH (X25519)
 OpenSSL 1.0.2e               TLSv1.2   ECDHE-RSA-AES256-GCM-SHA384       256 bit ECDH (P-256)
 OpenSSL 1.1.0l (Debian)      TLSv1.2   ECDHE-RSA-AES256-GCM-SHA384       253 bit ECDH (X25519)
 OpenSSL 1.1.1d (Debian)      TLSv1.3   TLS_AES_256_GCM_SHA384            253 bit ECDH (X25519)
 OpenSSL 3.0.3 (git)          TLSv1.3   TLS_AES_256_GCM_SHA384            253 bit ECDH (X25519)
 Apple Mail (16.0)            TLSv1.2   ECDHE-RSA-AES256-GCM-SHA384       256 bit ECDH (P-256)
 Thunderbird (91.9)           TLSv1.3   TLS_AES_128_GCM_SHA256            253 bit ECDH (X25519)


 Rating (experimental)

 Rating specs (not complete)  SSL Labs's 'SSL Server Rating Guide' (version 2009q from 2020-01-30)
 Specification documentation  https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide
 Protocol Support (weighted)  100 (30)
 Key Exchange     (weighted)  90 (27)
 Cipher Strength  (weighted)  90 (36)
 Final Score                  93
 Overall Grade                A+

 Done 2025-01-21 09:46:08 [  66s] -->> 51.159.34.231:443 (sklein.xyz) <<--

Journal du dimanche 19 janvier 2025 à 11:24 #proxmox, #homelab, #network, #nftables, #iteration, #DevOps, #Server

#iteration Projet GH-271 - Installer Proxmox sur mon serveur NUC Intel i3-5010U, 8Go de Ram :

Être capable d'exposer sur Internet un port d'une VM.

source

Voici comment j'ai atteint cet objectif.

Pour faire ce test, j'ai installé un serveur http nginx sur une VM qui a l'IP 192.168.1.236.

Cette IP est attribuée par le DHCP installé sur mon routeur OpenWrt. Le serveur hôte Proxmox est configuré en mode bridge.

Ma Box Internet Bouygues sur 192.168.1.254 peut accéder directement à cette VM 192.168.1.236.

Pour exposer le serveur Proxmox sur Internet, j'ai configuré mon serveur Serveur NUC i3 en tant que DMZ host.

J'ai suivi la recommandation pour éviter une attaque du type : DNS amplification attacks

DNS amplification attacks involves an attacker sending a DNS name lookup request to one or more public DNS servers, spoofing the source IP address of the targeted victim.

source

Avec cette configuration, je peux accéder en ssh au Serveur NUC i3 depuis Internet.

J'ai tout de suite décidé d'augmenter la sécurité du serveur ssh :

# cat <<'EOF' > /etc/ssh/sshd_config.d/sklein.conf
Protocol 2
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
KbdInteractiveAuthentication no
X11Forwarding no
# systemctl restart ssh

J'ai ensuite configuré le firewall basé sur nftables pour mettre en place quelques règles de sécurité et mettre en place de redirection de port du serveur hôte Proxmox vers le port 80 de la VM 192.168.1.236.

nftables est installé par défaut sur Proxmox mais n'est pas activé. Je commence par activer nftables :

root@nuci3:~# systemctl enable nftables
root@nuci3:~# systemctl start nftables

Voici ma configuration /etc/nftables.conf, je me suis fortement inspiré des exemples présents dans ArchWiki : https://wiki.archlinux.org/title/Nftables#Server

# cat <<'EOF' > /etc/nftables.conf
flush ruleset;

table inet filter {
    # Configuration from https://wiki.archlinux.org/title/Nftables#Server
    set LANv4 {
        type ipv4_addr
        flags interval

        elements = { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 }
    }
    set LANv6 {
        type ipv6_addr
        flags interval

        elements = { fd00::/8, fe80::/10 }
    }

    chain input {
        type filter hook input priority filter; policy drop;
        iif lo accept comment "Accept any localhost traffic"

        ct state invalid drop comment "Drop invalid connections"
        ct state established,related accept comment "Accept traffic originated from us"

        meta l4proto ipv6-icmp accept comment "Accept ICMPv6"
        meta l4proto icmp accept comment "Accept ICMP"
        ip protocol igmp accept comment "Accept IGMP"

        udp dport mdns ip6 daddr ff02::fb accept comment "Accept mDNS"
        udp dport mdns ip daddr 224.0.0.251 accept comment "Accept mDNS"

        ip saddr @LANv4 accept comment "Connections from private IP address ranges"
        ip6 saddr @LANv6 accept comment "Connections from private IP address ranges"

        tcp dport ssh accept comment "Accept SSH on port 22"
        tcp dport 8006 accept comment "Accept Proxmox web console"

        udp sport bootpc udp dport bootps ip saddr 0.0.0.0 ip daddr 255.255.255.255 accept comment "Accept DHCPDISCOVER (for DHCP-Proxy)"
    }

    chain forward {
        type filter hook forward priority filter; policy accept;
    }

    chain output {
        type filter hook output priority filter; policy accept;
    }
}

table nat {
    chain prerouting {
        type nat hook prerouting priority dstnat;
        tcp dport 80 dnat to 192.168.1.236;
    }
    chain postrouting {
        type nat hook postrouting priority srcnat;
        masquerade
    }
}
EOF

Pour appliquer en toute sécurité cette configuration, j'ai suivi la méthode indiquée dans : "Appliquer une configuration nftables avec un rollback automatique de sécurité".

Après cela, voici les tests que j'ai effectués :

  • Depuis mon réseau local :
    • Test d'accès au serveur Proxmox via ssh : ssh root@192.168.1.43
    • Test d'accès au serveur Proxmox via la console web : https://192.168.1.43:8006
    • Test d'accès au service http dans la VM : curl -I http://192.168.1.236
  • Depuis Internet :
    • Test d'accès au serveur Proxmox via ssh : ssh root@176.142.86.141
    • Test que je n'ai pas accès au serveur Proxmox via la console web : curl http://176.142.86.141:8006
    • Test d'accès au service http dans la VM : curl -I http://176.142.86.141

Voilà, tout fonctionne correctement 🙂.

Prochaines étapes :

  • Être capable d'accéder depuis Internet via IPv6 à une VM
  • Je souhaite arrive à effectuer un déploiement d'une Virtual instance via Terraform

Journal du vendredi 17 janvier 2025 à 12:03 #scaleway, #dns, #DevOps

Suite de ma note 2025-01-15_1350.

Je vais créer un ticket de support Scaleway pour savoir si c'est un bug ou si j'ai mal compris comment cela fonctionne.

source

Voici la réponse que j'ai reçu :

Notre équipe produit est revenue vers nous pour nous indiquer qu’en effet il y a un défaut de documentation.

Ce process alternatif ne fonctionne que sur la racine des domaines pas sur un sous domaine.

C’était pour les tlds qui ne donnent pas de DNS par défaut aux clients.

Journal du vendredi 17 janvier 2025 à 11:58 #scaleway, #packer, #DevOps, #JeMeDemande

#JeMeDemande s'il est possible d'installer des serveurs Scaleway Elastic Metal avec des images d'OS préalablement construites avec Packer 🤔.

Je viens de poser la question suivante : Is it possible to create Elastic Metal OS images with Packer and use it to create a Elastic Metal serveurs?

En français :

Bonjour,

Je sais qu'il est possible de créer des images d'OS avec Packer utilisables lors de la création d'instance Scaleway (voir https://www.scaleway.com/en/docs/tutorials/deploy-instances-packer-terraform/).

De la même manière, je me demande s'il est possible de créer des images d'OS avec Packer pour installer des serveurs Elastic Metal .

Question : est-il possible de créer des images Elastic Metal avec Packer et d'utiliser celle-ci pour créer des serveurs Elastic Metal ?

Si c'est impossible actuellement, pensez-vous qu'il soit possible de l'implémenter ? Ou alors, est-ce que des limitations techniques de Elastic Metal rendent impossible cette fonctionnalité ?

Bonne journée, Stéphane

Je viens d'envoyer cette demande au support de Scaleway.

Journal du mercredi 15 janvier 2025 à 13:50 #dns, #DevOps, #admin-sys, #scaleway

Suite de la note 2025-01-14_2152 au sujet de Scaleway Domains and DNS.

Dans l'e-mail « External domain name validation » que j'ai reçu, je lis :

Alternative validation process (if your current registrar doesn't offer basic DNS service):

Please set your nameservers at your registrar to:

  • 9ca08f37-e2c8-478d-bb0e-8a525db976b9.ns0.dom.scw.cloud
  • 9ca08f37-e2c8-478d-bb0e-8a525db976b9.ns1.dom.scw.cloud

J'ai essayé cette méthode alternative pour le sous-domaine scw.stephane-klein.info :

Voici les DNS Records correspondants :

scw.stephane-klein.info.	1	IN	NS	9ca08f37-e2c8-478d-bb0e-8a525db976b9.ns1.dom.scw.cloud.
scw.stephane-klein.info.	1	IN	NS	9ca08f37-e2c8-478d-bb0e-8a525db976b9.ns0.dom.scw.cloud.

J'ai vérifié ma configuration :

$ dig NS scw.stephane-klein.info @ali.ns.cloudflare.com
scw.stephane-klein.info. 300    IN      NS      9ca08f37-e2c8-478d-bb0e-8a525db976b9.ns0.dom.scw.cloud.
scw.stephane-klein.info. 300    IN      NS      9ca08f37-e2c8-478d-bb0e-8a525db976b9.ns1.dom.scw.cloud.

J'ai attendu plus d'une heure et cette méthode de validation n'a toujours pas fonctionné.

Je pense que cela ne fonctionne pas 🤔.

Je vais créer un ticket de support Scaleway pour savoir si c'est un bug ou si j'ai mal compris comment cela fonctionne.


2025-01-17 : réponse que j'ai reçu :

Notre équipe produit est revenue vers nous pour nous indiquer qu’en effet il y a un défaut de documentation.

Ce process alternatif ne fonctionne que sur la racine des domaines pas sur un sous domaine.

C’était pour les tlds qui ne donnent pas de DNS par défaut aux clients.

Conclusion : la documentation était imprécise, ce que j'ai essayé de réaliser ne peut pas fonctionner.

Journal du mardi 14 janvier 2025 à 21:52 #dns, #DevOps, #admin-sys, #scaleway

J'ai réalisé un test pour vérifier que la délégation DNS par le managed service de Scaleway nommé Domains and DNS (lien direct) fonctionne correctement pour un sous-domaine tel que testscaleway.stephane-klein.info.

Je commence par suivre les instructions du how to : How to add an external domain to Domains and DNS.

Sur la page https://console.scaleway.com/domains/internal/create :

Je sélectionne la dernière entrée « Manage as external ».

Ensuite :

J'ai ajouté le DNS Record suivant à mon serveur DNS géré par cloudflare :

_scaleway-challenge.testscaleway.stephane-klein.info.	1	IN	TXT	"dd7e7931-56d6-4e04-bcd7-e358821e63dd"

Vérification :

$ dig TXT _scaleway-challenge.testscaleway.stephane-klein.info +short
"dd7e7931-56d6-4e04-bcd7-e358821e63dd"

Je n'ai aucune idée de la fréquencede passage du job Scaleway qui effectue la vérification de l'entrée TXT :

J'ai ajouté mon domaine à 22h22 et il a été validé à 22h54 :

Ensuite, j'ai ajouté les DNS Records suivants :

testscaleway.stephane-klein.info.	1	IN	NS	ns1.dom.scw.cloud.
testscaleway.stephane-klein.info.	1	IN	NS	ns0.dom.scw.cloud.

Vérification :

$ dig NS testscaleway.stephane-klein.info +short
ns0.dom.scw.cloud.
ns1.dom.scw.cloud.

Ensuite, j'ai configuré dans la console Scaleway, le DNS Record :

test1   3600 IN A  8.8.8.8

Vérification :

$ dig test1.testscaleway.stephane-klein.info +short
8.8.8.8

Conclusion : la réponse est oui, le managed service Scaleway Domains and DNS permet la délégation de sous-domaines 🙂.

Quelle est la fonction de "/deployment-playground/" dans mes projets ? #WebDev, #software-engineering, #DevOps, #deployment

L'objectif de cette note est d'expliquer ce que sont les dossiers /deployment-playground/ et ma pratique consistant à les intégrer dans la plupart de mes projets de développement web.

Je pense me souvenir que j'ai commencé à intégrer ce type de dossier vers 2018.

Au départ, je nommais ce dossier /demo/ dont l'objectif était le suivant : permettre à un développeur de lancer localement le plus rapidement possible une instance de démo complète de l'application.

Pour cela, ce dossier contenait généralement :

  • Un docker-compose.yml qui permettait le lancer tous les services à partir des dernières images Docker déjà buildé. Soit les images de la version de l'environnement de développement, staging ou production.
  • Un script d'injection de données de fixtures ou de données spécifiques de démonstration.
  • Mise à disposition des logins / password d'un ou plusieurs comptes utilisateurs.

Cet environnement était configuré au plus proche de la configuration de production.

Avec le temps, j'ai constaté que mon utilisation de ce dossier a évolué. Petit à petit, je m'en servais pour :

  • Aider les développeurs à comprendre le processus de déploiement de l'application en production, en fournissant une représentation concrète des résultats des scripts de déploiement.
  • Permettre aux développeurs de tester localement la bonne installation de l'application, afin de reproduire les conditions de production en cas de problème.
  • Faciliter l'itération locale sur l'amélioration ou le refactoring de la méthode de déploiement.
  • Et bien d'autres usages.

J'ai finalement décidé de renommer ce dossier deployment-playground, un bac à sable pour "jouer" localement avec la configuration de production de l'application.

Je ne peux pas vous partager mes exemples beaucoup plus complets qui sont hébergés sur des dépôts privés, mais voici quelques exemples minimalistes dans mes dépôts publics :

Je découvre la compression Zstandard #OnMaPartagé, #JaiDécouvert, #WebDev, #DevOps, #compression, #brotli, #zstandard, #JeMeDemande, #JaimeraisUnJour

Un ami m'a partagé Zstandard (zstd), un algorithme de compression.

Il y a 2 ans, j'ai étudié et activé Brotli dans mes containers nginx, voir la note : Mise en œuvre du module Nginx Brotli.

Je viens de trouver un module zstd pour nginx : https://github.com/tokers/zstd-nginx-module

Mon ami m'a partagé cet excellent article : Choosing Between gzip, Brotli and zStandard Compression. Très complet, il explique tout, contient des benchmarks…

Voici ce que je retiens.

Brotli a été créé par Google, Zstandard par Facebook :

Zstandard is a newer compression method developed by Facebook.

source

Je lis sur canIuse, le support Zstandard a été ajouté à Chrome en mars 2024 et à Firefox en mai 2024, c'est donc une technologie très jeune coté browser.

Benchmark sur le dépôt officiel de Zstandard :

J'ai trouvé ces threads Hacker News :

Zstandard semble être fortement adopté au niveau de l'écosystème des OS Linux :

The Linux kernel has included Zstandard since November 2017 (version 4.14) as a compression method for the btrfs and squashfs filesystems.

source

Packages Ubuntu et Debian :

In March 2018, Canonical tested the use of zstd as a deb package compression method by default for the Ubuntu Linux distribution. Compared with xz compression of deb packages, zstd at level 19 decompresses significantly faster, but at the cost of 6% larger package files. Support was added to Debian in April 2018

source

Packages Fedora :

Fedora added ZStandard support to rpm in May 2018 (Fedora release 28) and used it for packaging the release in October 2019 (Fedora 31). In Fedora 33, the filesystem is compressed by default with zstd.

source

#JeMeDemande si dans mes projets de doit utiliser Zstandard plutôt que Brotli 🤔.

Je pense avoir trouver une réponse ici :

The research I’ve shared in this article also shows that for many sites Brotli will provide better compression for static content. Zstandard could potentially provide some benefits for dynamic content due to its faster compression speeds. Additionally:

  • ...
  • For dynamic content
    • Brotli level 5 usually result in smaller payloads, at similar or slightly slower compression times.
    • zStandard level 12 often produces similar payloads to Brotli level 5, with compression times faster than gzip and Brotli.
  • For static content
    • Brotli level 11 produces the smallest payloads
    • zStandard is able to apply their highest compression levels much faster than Brotli, but the payloads are still smaller with Brotli.

source

#JaimeraisUnJour prendre le temps d'installer zstd-nginx-module à mon image Docker nginx-brotli-docker (ou alors d'en trouver une déjà existante).

Alternatives managées à ngrok Developer Preview #DevOps, #network, #comparaison

Dans la note 2024-12-31_1853, j'ai présenté sish-playground qui permet de self host sish.

Je souhaite maintenant lister des alternatives à ngrok qui proposent des services gérés.

Quand je parle d'alternative à ngrok, il est question uniquement de la fonctionnalité d'origine de ngrok en 2013 : exposer des serveurs web locaux (localhost) sur Internet via une URL publique. ngrok nomme désormais ce service "Developer Preview".

Offre managée de sish

Les développeurs de sish proposent un service managé à 2 € par mois.

Ce service permet l'utilisation de noms de domaine personnalisés : https://pico.sh/custom-domains#tunssh.

Test de la fonctionnalité "Developer Preview" de ngrok

J'ai commencé par créer un compte sur https://ngrok.com.

Ensuite, une fois connecté, la console web de ngrok m'invite à installer le client ngrok. Voici la méthode que j'ai suivie sur ma Fedora :

$ wget https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-amd64.tgz -O ~/Downloads/ngrok-v3-stable-linux-amd64.tgz
$ sudo tar -xvzf ~/Downloads/ngrok-v3-stable-linux-amd64.tgz -C /usr/local/bin
$ ngrok --help
ngrok version 3.19.0
$ ngrok config add-authtoken 2r....RN
$ ngrok http http://localhost:8080
ngrok                                                                                                                                                         (Ctrl+C to quit)

👋 Goodbye tunnels, hello Agent Endpoints: https://ngrok.com/r/aep

Session Status                online
Account                       Stéphane Klein (Plan: Free)
Version                       3.19.0
Region                        Europe (eu)
Web Interface                 http://127.0.0.1:4040
Forwarding                    https://2990-2a04-cec0-107a-ea02-74d7-2487-cc11-f4d2.ngrok-free.app -> http://localhost:8080

Connections                   ttl     opn     rt1     rt5     p50     p90
                              0       0       0.00    0.00    0.00    0.00

Cette fonctionnalité de ngrok est gratuite.

Toutefois, pour pouvoir utiliser un nom de domaine personnalisé, il est nécessaire de souscrire à l'offre "personal" à $8 par mois.

Test de la fonctionnalité Tunnel de Cloudflare

Pour exposer un service local sur Internet via une URL publique avec cloudflared, je pense qu'il faut suivre la documentation suivante : Create a locally-managed tunnel (CLI).

J'ai trouvé sur cette page, le package rpm pour installer cloudflared sous Fedora :

$ wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm -O /tmp/cloudflared-linux-x86_64.rpm
$ sudo rpm -i /tmp/cloudflared-linux-x86_64.rpm
$ cloudflared --version
cloudflared version 2024.12.2 (built 2024-12-19-1724 UTC)

Voici comment exposer un service local sur Internet via une URL publique avec cloudflared sans même créer un compte :

$ cloudflared tunnel --url http://localhost:8080
...
Requesting new quick Tunnel on trycloudflare.com...
Your quick Tunnel has been created! Visit it at (it may take some time to be reachable):
https://manufacturer-addressing-surgeon-tried.trycloudflare.com

Après cela, le service est exposé sur Internet sur l'URL suivante : https://manufacturer-addressing-surgeon-tried.trycloudflare.com.

Voici maintenant la méthode pour exposer un service sur un domaine spécifique.

Pour cela, il faut que le nom de domaine soit géré pour les serveurs DNS de cloudflare.
Au moment où j'écris cette note, c'est le cas pour mon domaine stephane-klein.info :

$ dig NS stephane-klein.info +short
ali.ns.cloudflare.com.
sri.ns.cloudflare.com.

Ensuite, il faut lancer :

$ cloudflared tunnel login

Cette commande ouvre un navigateur, ensuite il faut se connecter à cloudflare et sélectionner le nom de domaine à utiliser, dans mon cas j'ai sélectionné stephane-klein.info.

Ensuite, il faut créer un tunnel :

$ cloudflared tunnel create mytunnel
Tunnel credentials written to /home/stephane/.cloudflared/61b0e52f-13e3-4d57-b8da-6c28ff4e810b.json. …

La commande suivante, connecte le tunnel mytunnel au hostname mytunnel.stephane-klein.info :

$ cloudflared tunnel route dns mytunnel mytunnel.stephane-klein.info
2025-01-06T17:52:10Z INF Added CNAME mytunnel.stephane-klein.info which will route to this tunnel tunnelID=67db5943-1f16-4b4a-a307-e8ceeb01296c

Et, voici la commande pour exposer un service sur ce tunnel :

$ cloudflared tunnel --url http://localhost:8080 run mytunnel

Après cela, le service est exposé sur Internet sur l'URL suivante : https://mytunnel.stephane-klein.info.

Pour finir, voici comment détruire ce tunnel :

$ cloudflared tunnel delete mytunnel

J'ai essayé de trouver le prix de ce service, mais je n'ai pas trouvé. Je pense que ce service est gratuit, tout du moins jusqu'à un certain volume de transfert de données.

Journal du samedi 28 décembre 2024 à 17:10 #contribution, #DevOps, #admin-sys, #dns, #power-dns

Comme mentionné dans la note 2024-12-28_1621, j'ai implémenté un playground nommé powerdns-playground.
J'ai fait le triste constat de découvrir encore un projet (PowerDNS-Admin) qui ne supporte pas une "automated and unattended installation" 🫤.

PowerDNS-Admin ne permet pas de créer automatiquement un utilisateur admin.

Pour contourner cette limitation, j'ai implémenté un script configure_powerdns_admin.py qui permet de créer un utilisateur basé sur les variables d'environnement POWERDNS_ADMIN_USERNAME, POWERDNS_ADMIN_PASSWORD, POWERDNS_ADMIN_EMAIL.

Le script ./scripts/setup-powerdns-admin.sh se charge de copier le script Python dans le container powerdns-admin et de l'exécuter.

J'ai partagé ce script sur :

Journal du samedi 28 décembre 2024 à 16:21 #WebDev, #DevOps, #admin-sys, #yak, #dns, #power-dns

Je viens de tomber dans un Yak! 🙂.

Je cherchais des alternatives Open source à ngrok et j'ai trouvé sish (https://docs.ssi.sh/).

Côté client, sish utilise exclusivement ssh pour exposer des services (lien la documentation).

Voici comment exposer sur l'URL http://hereiam.tuns.sh le service HTTP exposé localement sur le port 8080 :

$ ssh -R hereiam:80:localhost:8080 tuns.sh

Je trouve cela très astucieux 👍️.

Après cela, j'ai commencé à étudier comment déployer sish et j'ai lu cette partie :

This includes taking care of SSL via Let's Encrypt for you. This uses the adferrand/dnsrobocert container to handle issuing wildcard certifications over DNS.

source

Après cela, j'ai étudié dnsrobocert qui permet de générer des certificats SSL Let's Encrypt avec la méthode DNS challenges, mais pour cela, il a besoin d'insérer et de modifier des DNS Record sur un serveur DNS.

Je n'ai pas envie de donner accès à l'intégralité de mes zones DNS à un script.
Pour éviter cela, j'ai dans un premier temps envisagé d'utiliser un serveur DNS managé de Scaleway, mais j'ai constaté que le provider Scaleway n'est pas supporté par Lexicon (qui est utilisé par dnsrobocert).

Après cela, j'ai décidé d'utiliser PowerDNS et je viens de publier ce playground : powerdns-playground.

Journal du lundi 23 décembre 2024 à 19:39 #selfhosting, #proxmox, #DevOps, #bug, #iteration

J'ai commencé le Projet GH-271 - Installer Proxmox sur mon serveur NUC Intel i3-5010U, 8Go de Ram le 9 octobre.
Le 27 octobre, j'ai publié la note 2024-10-27_2109 qui contient une erreur qui m'a fait perdre 14h !

La ligne en question :

# virt-customize -a noble-server-cloudimg-amd64.img --install qemu-guest-agent --run-command 'systemctl enable qemu-guest-agent.service'
[   0.0] Examining the guest ...
[   4.5] Setting a random seed
virt-customize: warning: random seed could not be set for this type of
guest
[   4.5] Setting the machine ID in /etc/machine-id
[   4.5] Installing packages: qemu-guest-agent

[  32.1] Running: systemctl enable qemu-guest-agent.service
[  32.6] Finishing off

Je n'avais pas fait attention au message Setting the machine ID in /etc/machine-id 🙊.

Conséquence : le template Proxmox Ubuntu contenait un fichier /etc/machine-id avec un id.
Conséquence : toutes les Virtual machine que je créais sous Proxmox avaient la même valeur machine-id.

J'ai découvert que l'option 61 "Client identifier" du protocole DHCP permet de passer un client id au serveur DHCP qui sera utilisé à la place de l'adresse MAC.

Conséquence : le serveur DHCP assignait la même IP à ces Virtual machine.

J'ai pensé que le serveur DHCP de mon router BBox avait un problème. J'ai donc décidé d'installer Projet 15 - Installation et configuration de OpenWrt sur Xiaomi Mi Router 4A Gigabit pour avoir une meilleure maitrise du serveur DHCP.

Problème : j'ai fait face au même problème avec le serveur DHCP de OpenWrt.

Après quelques recherches, j'ai découvert que contrairement à virt-customize la commande virt-sysprep permet d'agir sur des images qui ont vocation à être clonées.

"Sysprep" stands for "system preparation" tool. The name comes from the Microsoft program sysprep.exe which is used to unconfigure Windows machines in preparation for cloning them.

source

Pour corriger le problème, j'ai remplacé cette ligne :

# virt-customize -a noble-server-cloudimg-amd64.img --install qemu-guest-agent --run-command 'systemctl enable qemu-guest-agent.service'

Par ces deux lignes :

# virt-sysprep -a noble-server-cloudimg-amd64.img --network --install qemu-guest-agent --run-command 'systemctl enable qemu-guest-agent.service'
# virt-sysprep --operation machine-id -a noble-server-cloudimg-amd64.img

La seconde commande permet de supprimer le fichier /etc/machine-id, ce qui corrige le problème d'attribution d'IP par le serveur DHCP.

À noter que je ne comprends pas pourquoi il est nécessaire de lancer explicitement cette seconde commande, étant donné que la commande virt-sysprep est destinée aux images de type "template". Le fichier /etc/machine-id ne devrait jamais être créé, ou tout du moins, automatiquement supprimé à la fin de chaque utilisation de virt-sysprep.

Maintenant, l'instanciation de Virtual machine fonctionne bien, elles ont des IP différentes 🙂.

Prochaine étape du Projet GH-271 :

Je souhaite arrive à effectuer un déploiement d'une Virtual instance via cli de Terraform.

Journal du dimanche 22 décembre 2024 à 18:11 #mémo, #mémento, #proxmox, #DevOps

Alexandre m'a partagé l'article "Proxmox - Activer les mises à jour sans abonnement (no-subscription)".

Élément que je retiens :

Le processus de validation des paquets chez Proxmox est le suivant : Test -> No-Subscription -> Enterprise. Le dépôt No-Suscription est donc suffisamment stable. L'avertissement est à mon avis pour mettre hors cause l'équipe de Proxmox en cas de trou dans la raquette lors des tests de validation.

source

$ ssh root@192.168.1.43
# echo EOF
# cat <<EOF > /etc/apt/sources.list.d/proxmox.list
> deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription
> EOF
# apt update -y
# apt upgrade -y
...
# reboot

Pour supprimer le message de warning au login, sur la version 8.3.2, j'ai appliqué le patch suivant :

# patch /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js <<EOF
--- /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.old      2024-12-22 18:23:48.951557867 +0100
+++ /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js  2024-12-22 18:18:50.509376748 +0100
@@ -563,6 +563,7 @@
                    let res = response.result;
                    if (res === null || res === undefined || !res || res
                        .data.status.toLowerCase() !== 'active') {
+                       /*
                        Ext.Msg.show({
                            title: gettext('No valid subscription'),
                            icon: Ext.Msg.WARNING,
@@ -575,6 +576,7 @@
                                orig_cmd();
                            },
                        });
+                       */
                    } else {
                        orig_cmd();
                    }
EOF
# systemctl restart pveproxy

Je pense pouvoir maintenant remplacer Direnv par Mise 🤞 #dev-kit, #DevOps, #direnv, #mise, #diff

Le 6 novembre 2024, j'ai publié la note "Le support des variables d'environments de Mise est limité, je continue à utiliser direnv".

L'issue indiquée dans cette note a été cloturée il y a 3 jours : Use mise tools in env template · Issue #1982.

Voici le contenu de changement de la documentation : https://github.com/jdx/mise/pull/3598/files#diff-e8cfa8083d0343d5a04e010a9348083f7b64035c407faa971074c6f0e8d0d940

Ce qui signifie que je vais pouvoir maintenant utiliser terraform installé via Mise dans le fichier .envrc :

[env]
_.source = { value = "./.envrc.sh", tools = true }

[tools]
terraform="1.9.8"

Après avoir installé la dernière version de Mise, j'ai testé cette configuration dans repository suivant : install-and-configure-mise-skeleton.

Le fichier .envrc suivant a fonctionné :

export HELLO_WORLD=foo3
export NOW=$(date)
export TERRAFORM_VERSION=$(terraform --version | head -n1)

Exemple :

$ mise trust
$ echo $TERRAFORM_VERSION
Terraform v1.9.8

Je n'ai pas trouvé de commande Mise pour recharger les variables d'environnement du dossier courant, sans quitter le dossier. Avec direnv, pour effectuer un rechargement je lançais : direnv allow.

À la place, j'ai trouvé la méthode suivante :

$ source .envrc

Je pense pouvoir maintenant remplacer Direnv par Mise 🤞.

Journal du jeudi 19 décembre 2024 à 16:40 #DevOps, #docker, #JaiDécouvert

#JaiDécouvert le format de fichier Bake de Docker : https://docs.docker.com/build/bake/introduction/.

Je ne comprends pas bien son utilité 🤔.

La commande suivante me convient parfaitement :

$ docker build \
  -f Dockerfile \
  -t myapp:latest \
  --build-arg foo=bar \
  --no-cache \
  --platform linux/amd64,linux/arm64 \
  .

J'ai essayé de trouver l'issue d'origine du projet Bake, pour connaitre ses motivations, mais je n'ai pas trouvé.

Comment lancer une image Docker de l'architecture "arm64" sous Intel ? #docker, #DevOps, #JeMeDemande

#JeMeDemande comment lancer une image Docker pour l'architecture arm64 sur une architecture Intel sous Fedora ?

Par défaut, l'exécution de cette image Docker sous Intel avec l'option --platform linux/arm64 ne fonctionne pas :

$ docker run --rm -it --platform linux/arm64 hasura/graphql-engine:v2.43.0 bash
exec /usr/bin/bash: exec format error

J'ai consulté et suivi la documentation Docker officielle suivante : Install QEMU manually.

$ docker run --privileged --rm tonistiigi/binfmt --install all
installing: arm64 OK
installing: arm OK
installing: ppc64le OK
installing: riscv64 OK
installing: mips64le OK
installing: s390x OK
installing: mips64 OK
{
  "supported": [
    "linux/amd64",
    "linux/arm64",
    "linux/riscv64",
    "linux/ppc64le",
    "linux/s390x",
    "linux/386",
    "linux/mips64le",
    "linux/mips64",
    "linux/arm/v7",
    "linux/arm/v6"
  ],
  "emulators": [
    "qemu-aarch64",
    "qemu-arm",
    "qemu-mips64",
    "qemu-mips64el",
    "qemu-ppc64le",
    "qemu-riscv64",
    "qemu-s390x"
  ]
}

Après cela, je constate que j'arrive à lancer avec succès une image arm64 sous processeur Intel :

$ docker run --rm -it --platform linux/arm64 hasura/graphql-engine:v2.43.0 bash

root@bf74bfb8bc35:/# graphql-engine version
Hasura GraphQL Engine (Pro Edition): v2.43.0

J'ai pris un peu de temps pour explorer le repository tonistiigi/binfmt.
Je n'ai pas compris quelle est l'interaction entre les éléments installés par cette image et docker-engine.
Je constate que cette image a été créée en 2019 par deux développeurs de Docker : CrazyMax (un Français) et Tõnis Tiigi.

Journal du dimanche 08 décembre 2024 à 22:19 #dev-kit, #DevOps, #histoire

Je viens de rencontrer l'outil envdir (à ne pas confondre avec direnv) et son modèle de stockage de variables d'environnement, que je trouve très surprenant !

direnv ou dotenv utilisent de simples fichiers texte pour stocker les variables d'environnements, par exemple :

export POSTGRES_URL="postgres://postgres:password@localhost:5432/postgres"
export SMTP_HOST="127.0.0.1"
export SMTP_PORT="1025"
export MAIL_FROM="noreply@example.com"

Contrairement à ces deux outils, pour définir ces quatre variables, le modèle de stockage de envdir nécessite la création de 4 fichiers :

$ tree .
.
├── MAIL_FROM
├── POSTGRES_URL
├── SMTP_HOST
└── SMTP_PORT

1 directory, 4 files

$ cat POSTGRES_URL
postgres://postgres:password@localhost:5432/postgres
$ cat SMTP_HOST
127.0.0.1
$ cat SMTP_PORT
1025
$ cat MAIL_FROM
127.0.0.1

Je trouve ce modèle fort peu pratique. Contrairement à un simple fichier unique, le modèle de envdir présente certaines limitations :

  • Organisation : il ne permet pas de structurer librement l'ordre ou de regrouper visuellement les variables.
  • Lisibilité : l'ensemble de la configuration est plus difficile à visualiser d'un seul coup d'œil.
  • Manipulation : copier ou coller son contenu n'est pas aussi direct qu'avec un fichier texte.
  • Documentation : les commentaires ne peuvent pas être inclus pour expliquer les variables.
  • Rapidité : la saisie ou la modification de plusieurs variables demande plus de temps, chaque variable étant dans un fichier distinct.

J'ai été particulièrement intrigué par le choix fait par l'auteur de envdir. Sa décision semble vraiment surprenante.

envdir fait partie du projet daemontools, créé en 1990. Ainsi, il est bien plus ancien que dotenv, qui a été lancé autour de 2010, et direnv, qui date de 2014.

Si je me remets dans le contexte des années 1990, je pense que le modèle de envdir a été avant tout motivé par une simplicité d'implémentation plutôt que d'utilisation. En effet, il est plus facile de lister les fichiers d'un répertoire et de charger leur contenu que de développer un parser de fichiers.

Je pense qu'en 2024, envdir n'a plus sa place dans un environnement informatique moderne. Je recommande vivement de le remplacer par des solutions plus récentes, comme devenv ou direnv.

Personnellement, j'utilise direnv dans tous mes projets.

Journal du dimanche 03 novembre 2024 à 18:35 #qemu, #kvm, #proxmox, #admin-sys, #DevOps, #linux, #JaiDécouvert

Dans le tutoriel "Proxmox Template with Cloud Image and Cloud Init", #JaiDécouvert un usage de la commande virt-customize :

# wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
# virt-customize -a noble-server-cloudimg-amd64.img --install qemu-guest-agent --run-command 'systemctl enable qemu-guest-agent.service'

Je trouve cela extrêmement pratique, cela évite de devoir utiliser Packer pour personnaliser une image disque.

J'ai fait quelques recherches et j'ai appris que la fonctionnalité d'installation de package est ancienne, elle a été implémentée dans libguestfs en 2014 par Richard Jones, employé de chez Red Hat (auteur de libguestfs).

Journal du mercredi 30 octobre 2024 à 10:21 #DevOps, #dev-kit

Je garde trace dans cette notes de deux plugins Asdf / Mise que j'ai utilisé avec succès ces deux derniers jours.

Le premier, pour installer la cli de Hasura : asdf-hasura

$ mise plugin add hasura-cli https://github.com/gurukulkarni/asdf-hasura.git

Contenu de .mise.toml :

[tools]
hasura-cli = "2.43.0"
$ mise install
$ hasura version
INFO hasura cli                                    version=v2.43.0

Le second, pour installer Gitleaks : asdf-gitleaks

$ mise plugin add gitleaks https://github.com/jmcvetta/asdf-gitleaks.git

Contenu de .mise.toml :

[tools]
gitleaks = "8.21.2"
$ mise install
$ gitleaks --version
gitleaks version 8.21.2

Journal du dimanche 27 octobre 2024 à 21:09 #proxmox, #selfhosting, #DevOps, #iteration

Nouvelle #iteration du Projet GH-271 - Installer Proxmox sur mon serveur NUC Intel i3-5010U, 8Go de Ram.

J'ai eu des difficultés à trouver comment déployer avec Proxmox des Virtual instance basées sur Ubuntu Cloud Image.

J'ai trouvé réponse à mes questions dans cet article : "Perfect Proxmox Template with Cloud Image and Cloud Init".
Mais depuis, j'ai trouvé un meilleur tutoriel : "Linux VM Templates in Proxmox on EASY MODE using Prebuilt Cloud Init Images!".

Création d'un template Ubuntu LTS

J'ai exécuté les commandes suivantes en SSH sur mon serveur NUC i3 pour créer un template de VM Proxmox.

root@nuci3:~# apt update -y && apt install libguestfs-tools jq -y
root@nuci3:~# wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img

Ancienne commande qui contient une erreur à ne pas utilisé, plus d'information dans la note 2024-12-23_1939 :

root@nuci3:~# virt-customize -a noble-server-cloudimg-amd64.img --install qemu-guest-agent --run-command 'systemctl enable qemu-guest-agent.service'
root@nuci3:~# virt-sysprep -a noble-server-cloudimg-amd64.img --network --install qemu-guest-agent --run-command 'systemctl enable qemu-guest-agent.service'
root@nuci3:~# virt-sysprep --operation machine-id -a noble-server-cloudimg-amd64.img
root@nuci3:~# qm create 8000 --memory 2048 --core 2 --name ubuntu-cloud-template --net0 virtio,bridge=vmbr0
root@nuci3:~# qm disk import 8000 noble-server-cloudimg-amd64.img local-lvm
transferred 3.5 GiB of 3.5 GiB (100.00%)
transferred 3.5 GiB of 3.5 GiB (100.00%)
Successfully imported disk as 'unused0:local-lvm:vm-8000-disk-0'
root@nuci3:~# rm noble-server-cloudimg-amd64.img
root@nuci3:~# qm set 8000 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-8000-disk-0
update VM 8000: -scsi0 local-lvm:vm-8000-disk-0 -scsihw virtio-scsi-pci
root@nuci3:~# qm set 8000 --ide2 local-lvm:cloudinit
update VM 8000: -ide2 local:cloudinit
Formatting '/var/lib/vz/images/8000/vm-8000-cloudinit.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off preallocation=metadata compression_type=zlib size=4194304 lazy_refcounts=off refcount_bits=16
ide2: successfully created disk 'local:8000/vm-8000-cloudinit.qcow2,media=cdrom'
generating cloud-init ISO

(liste des paramètres cloud-init)

root@nuci3:~# qm set 8000 --ipconfig0 "ip6=auto,ip=dhcp"
root@nuci3:~# qm set 8000 --sshkeys ~/.ssh/authorized_keys
root@nuci3:~# qm set 8000 --ciuser stephane
root@nuci3:~# qm set 8000 --cipassword password # optionnel, seulement en phase de debug
root@nuci3:~# qm set 8000 --boot c --bootdisk scsi0
update VM 8000: -boot c -bootdisk scsi0
root@nuci3:~# qm set 8000 --serial0 socket --vga serial0
update VM 8000: -serial0 socket -vga serial0
root@nuci3:~# qm set 8000 --agent enabled=1
root@nuci3:~# qm set 8000 --ciupgrade 0
root@nuci3:~# qm template 8000
  Renamed "vm-8000-disk-0" to "base-8000-disk-0" in volume group "pve"
  Logical volume pve/base-8000-disk-0 changed.
  WARNING: Combining activation change with other commands is not advised.

Création d'une Virtual Instance

root@nuci3:~# qm clone 8000 100 --name server1
root@nuci3:~# qm start 100
root@nuci3:~# qm guest cmd 100 network-get-interfaces | jq -r '.[] | select(.name == "eth0") | .["ip-addresses"][0] | .["ip-address"]'
192.168.1.64
$ ssh stephane@192.168.1.64
The authenticity of host '192.168.1.64 (192.168.1.64)' can't be established.
ED25519 key fingerprint is SHA256:OJHcY3GHOsm3I4qcsYFc6V4qePNxVS4iAOBsDjeLM7o.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.1.64' (ED25519) to the list of known hosts.
Welcome to Ubuntu 24.04.1 LTS (GNU/Linux 6.8.0-45-generic x86_64)
...

AWS RDS playground et fixe du problème pg_dumpall #DevOps, #admin-sys, #AWS, #playground

En 2019, j'ai rencontré un problème lors de l'exécution de pg_dumpall sur une base de données PostgreSQL hébergée sur AWS RDS. À l'époque, ce problème était "la goutte d'eau" qui m'avait empressé de migrer de RDS vers une instance PostgreSQL self hosted avec une simple image Docker dans un docker-compose.yml, mais je digresse, ce n'est pas le sujet de cette note.

Aujourd'hui, j'ai fait face à nouveau à ce problème, mais cette fois, j'ai décidé de prendre le temps pour bien comprendre le problème et d'essayer de le traiter.

Pour cela, j'ai implémenté et publié un playground nommé rds-playground.

Je peux le dire maintenant, j'ai trouvé une solution à mon problème 🙂.

Ce playground contient :

  • Un exemple de déploiement d'une base de données AWS RDS avec Terraform.
  • Un script qui permet d'importer avec succès la base de données AWS RDS vers une instance locale de PostgreSQL, en incluant les rôles.

Au départ, je pensais que le problème venait d'un problème de configuration des rôles du côté de AWS RDS ou alors que je n'utilisais pas le bon user. J'ai ensuite compris que c'était une fausse piste.

J'ai ensuite découvert ce billet : "Using pg_dumpall with AWS RDS Postgres".

For those interested, RDS Postgres (by design) doesn't allow you to read pg_authid, which was earlier necessary for pg_dumpall to work.

J'ai compris que pour exécuter un pg_dumpall sur une instance RDS, il est impératif d'utiliser l'option --no-role-passwords.

Autre subtilité : sur une instance RDS, le rôle SUPERUSER est attribué au rôle rlsadmin, tandis que cette option est supprimé du rôle postgres.

ALTER ROLE postgres WITH NOSUPERUSER INHERIT CREATEROLE
CREATEDB LOGIN NOREPLICATION NOBYPASSRLS VALID UNTIL 'infinity';

Par conséquent, j'ai décidé d'utiliser le même nom d'utilisateur superuser pour l'instance locale PostgreSQL :

services:
  postgres:
    image: postgres:13.15
    environment:
      POSTGRES_USER: rdsadmin
      POSTGRES_DB: postgres
      POSTGRES_PASSWORD: password
      ...

Pour aller plus loin, je vous invite à suivre le README.md de rds-playground.

Journal du lundi 21 octobre 2024 à 22:36 #DevOps, #shell, #JaiLu

J'étudie Oils (shell) afin de me faire une idée si il pourrait être un bon compromis entre l'utilisation de Bash et Ansible pour l'implémentation de script de déploiement pour de toute petite infrastructure.

#JaiLu A Tour of YSH

Journal du vendredi 18 octobre 2024 à 22:51 #grafana, #DevOps, #InfrastructureAsCode, #JaiDécouvert

En cherchant un outil d'Infrastructure as code pour Grafana, #JaiDécouvert Grizzly.

Un projet qui a débuté en mars 2020, développé en Go par l'équipe de Grafana.

A utility for managing Jsonnet dashboards against the Grafana API

J'ai parcouru l'intégralité de la documentation et je suis ravi, ce projet correspond parfaitement à ce que je cherchais depuis des années !

Avant de découvrir cet outil, j'écrivais des scripts Python ou Bash d'exportation et d'importation de dashboards via l'API de Grafana.

Je souhaite l'utiliser dans le Projet 14 - Script de base d'installation d'un serveur Ubuntu LTS. Dans le repository basic_ubuntu_server_install_playground.

Journal du vendredi 18 octobre 2024 à 19:15 #DevOps, #admin-sys, #vagrant, #dns, #difficulté, #iteration, #file

Nouvelle #iteration de Projet 14.

Pour traiter ce problème, je souhaite essayer de remplacer Vagrant Host Manager par vagrant-dns.

-- from

Résultat : j'ai migré de Vagrant Host Manager vers vagrant-dns avec succès 🙂.

Voici le commit : lien vers le commit.

Voici quelques explications de la configuration Vagrantfile.

Les lignes suivantes permettent d'utiliser la seconde IP des machines virtuelles pour identifier les identifier (renseignés par le serveur DNS).

  config.dns.ip = -> (vm, opts) do
    ip = nil
    vm.communicate.execute("hostname -I | cut -d ' ' -f 2") do |type, data|
      ip = data.strip if type == :stdout
    end
    ip
  end

La commande hostname retourne les deux IP de la machine virtuelle :

vagrant@server1:~$ hostname -I
10.0.2.15 192.168.56.22

La commande hostname -I | cut -d ' ' -f 2 capture la seconde IP, ici 192.168.56.22.

La configuration DNS qui retourne cette IP est consultable via :

$ vagrant dns -l
/server1.vagrant.test/ => 192.168.56.22
/server2.vagrant.test/ => 192.168.56.23
/grafana.vagrant.test/ => 192.168.56.23
/loki.vagrant.test/ => 192.168.56.23

Autre subtilité :

vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]

Cette ligne configure la machine virtuelle pour qu'elle utilise le serveur DNS de vagrant-dns.
Cela permet de résoudre les noms des autres machines virtuelles. Exemple :

vagrant@server1:~$ resolvectl query server2.vagrant.test
server2.vagrant.test: 192.168.56.23            -- link: eth0

-- Information acquired via protocol DNS in 12.3ms.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network

En mettant en place vagrant-dns sur ma workstation qui tourne sous Fedora, j'ai rencontré la difficulté suivante.

J'avais la configuration suivante installée :

$ cat /etc/systemd/resolved.conf.d/csd.conf
[Resolve]
DNS=10.57.40.1
Domains=~csd

Elle me permet de résoudre les hostnames des machines qui appartiennent à un réseau privé exposé via OpenVPN (voir cette note).

Voici ma configuration complète de systemd-resolved :

$ systemd-analyze cat-config systemd/resolved.conf
# /etc/systemd/resolved.conf

...

[Resolve]
...

# /etc/systemd/resolved.conf.d/1-vagrant-dns.conf
# This file is generated by vagrant-dns
[Resolve]
DNS=127.0.0.1:5300
Domains=~test

# /etc/systemd/resolved.conf.d/csd.conf
[Resolve]
DNS=10.57.40.1
Domains=~csd

Quand je lançais resolvectl query server2.vagrant.test pour la première fois après redémarrage de sudo systemctl restart systemd-resolved, tout fonctionnait correctement :

$ resolvectl query server2.vagrant.test
server2.vagrant.test: 192.168.56.23

-- Information acquired via protocol DNS in 7.5073s.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network

Mais, la seconde fois, j'avais l'erreur suivante :

$ resolvectl query server2.vagrant.test
server2.vagrant.test: Name 'server2.vagrant.test' not found

Ce problème disparait si je supprime /etc/systemd/resolved.conf.d/csd.conf.

Je n'ai pas compris pourquoi. D'après la section "Protocols and routing" de systemd-resolved, le serveur 10.57.40.1 est utilisé seulement pour les hostnames qui se terminent par .csd.

J'ai activé les logs de systemd-resolved au niveau debug avec

$ sudo resolvectl log-level debug
$ journalctl -u systemd-resolved -f

Voici le contenu des logs lors de la première exécution de resolvectl query server2.vagrant.test : https://gist.github.com/stephane-klein/506a9fc7d740dc4892e88bfc590bee98.

Voici le contenu des logs lors de la seconde exécution de resolvectl query server2.vagrant.test : https://gist.github.com/stephane-klein/956befc280ef9738bfe48cdf7f5ef930.

J'ai l'impression que la ligne 13 indique que le cache de systemd-resolved a été utilisé et qu'il n'a pas trouvé de réponse pour server2.vagrant.test. Pourquoi ? Je ne sais pas.

Ligne 13 : NXDOMAIN cache hit for server2.vagrant.test IN A

Ensuite, je supprime /etc/systemd/resolved.conf.d/csd.conf :

$ sudo rm /etc/systemd/resolved.conf.d/csd.conf

Je relance systemd-resolved et voici le contenu des logs lors de la seconde exécution de resolvectl query server2.vagrant.test : https://gist.github.com/stephane-klein/9f87050524048ecf9766f9c97b789123#file-systemd-resolved-log-L11

Je constate que cette fois, la ligne 11 contient : Cache miss for server2.vagrant.test IN A.

Pourquoi avec .csd le cache retourne NXDOMAIN et sans .csd, le cache retourne Cache miss et systemd-resolved continue son algorithme de résosultion du hostname ?

Je soupçonne systemd-resolved de stocker en cache la résolution de server2.vagrant.test par le serveur DNS 10.57.40.1. Si c'est le cas, je me demande pourquoi il fait cela alors qu'il est configuré pour les hostnames qui se terminent par .csd 🤔.


Autre problème rencontré, la latence de réponse :

$ resolvectl query server2.vagrant.test
server2.vagrant.test: 192.168.56.23

-- Information acquired via protocol DNS in 7.5073s.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network

La réponse est retournée en 7 secondes, ce qui ne me semble pas normal.

J'ai découvert que je n'ai plus aucune latence si je passe le paramètre DNSStubListener à no :

$ sudo cat <<EOF > /etc/systemd/resolved.conf.d/0-vagrant-dns.conf
[Resolve]
DNSStubListener=no
EOF
$ sudo systemctl restart systemd-resolved
$ resolvectl query server2.vagrant.test
server2.vagrant.test: 192.168.56.23

-- Information acquired via protocol DNS in 2.9ms.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network

Le temps de réponse passe de 7.5s à 2.9ms. Je n'ai pas compris la signification de ma modification.


Je récapitule, pour faire fonctionner correctement vagrant-dns sur ma workstation Fedora j'ai dû :

  • supprimer /etc/systemd/resolved.conf.d/csd.conf
  • et paramétrer DNSStubListener à no

Migration de vagrant-hostmanager vers vagrant-dns #DevOps, #admin-sys, #vagrant, #dns

Dans le cadre du Projet 14 - Script de base d'installation d'un serveur Ubuntu LTS, j'utilise Vagrant avec le plugin Vagrant Host Manager pour gérer les hostnames des machines virtuelles.

Vagrant Host Manager met correctement à jour les fichiers /etc/hosts sur ma machine hôte, c'est-à-dire, ma workstation, et dans les machines virtuels.
Cependant, ces noms d'hôtes ne sont pas accessibles à l'intérieur des containers Docker.
Par exemple, ici, http://grafana.example.com:3100/ n'est pas accessible à l'intérieur du container Promtail.

Pour traiter ce problème, je souhaite essayer de remplacer Vagrant Host Manager par vagrant-dns.

vagrant-dns semble exposer un serveur DNS qui sera accessible et utilisé par les containers Docker.

D'autre part, vagrant-dns (page contributors) semble un peu plus actif que Vagrant Host Manager (page contributors).

Journal du mercredi 09 octobre 2024 à 23:52 #proxmox, #selfhosting, #DevOps, #iteration

#iteration Projet GH-271 - Installer Proxmox sur mon serveur NUC Intel i3-5010U, 8Go de Ram.

J'ai installé avec succès la version 8.2.2 de Proxmox sur mon Serveur NUC i3.

J'ai copié l'image ISO de Proxmox sur une clé USB.

J'ai branché un clavier et un moniteur sur mon Serveur NUC i3 et j'ai suivi la procédure d'installation sans rencontrer de difficulté.

Je peux me connecter au serveur via l'interface web de Proxmox ou directement via ssh.

Prochaine étape : déployer une Virtual instance Ubuntu LTS.

J'ai découvert Linux Audit #OnMaPartagé, #JaiDécouvert, #linux, #security, #admin-sys, #DevOps

Alexandre m'a partagé l'article "Linux : Enregistrer toutes les commandes saisies avec auditd" qui présente Linux Audit.

The Linux audit framework provides a CAPP-compliant (Controlled Access Protection Profile) auditing system that reliably collects information about any security-relevant (or non-security-relevant) event on a system. It can help you track actions performed on a system.

-- from

La norme de sécurité de l'industrie des cartes de paiement (Payment Card Industry Data Security Standard ou PCI DSS) est un standard destiné à poser les normes de la sécurité des systèmes d'information amenés à traiter et stocker des process ou des informations relatives aux systèmes de paiement.

Dans ce cadre, de nombreuses conditions sont à respecter afin d'être compatible avec cette norme. Parmi celles-ci, l'enregistrement des commandes et instructions saisies par les utilisateurs à privilèges sur un système.

-- from

D'après ce que j'ai compris, la fonctionnalité Linux Audit est implémentée au niveau du kernel.

Linux Audit permet de surveiller les actions effectuées sur les fichiers (lecture, écriture…) et les appels syscalls.

D'après ce que je comprends, Linux Audit est conçu à des fins de sécurité. Il semble peu adapté pour documenter les opérations réalisées sur un serveur dans le cadre d'un travail collaboratif.

Journal du mercredi 02 octobre 2024 à 09:55 #http-proxy, #selfhosting, #DevOps, #JaiDécouvert

#JaiDécouvert Kamal Proxy (from) « A minimal HTTP proxy for zero-downtime deployments » codé en Golang. Un projet Basecamp qui fonctionne avec kamal.

Cela attire ma curiosité, parce que la semaine dernière, je réfléchissais comment implémenter la fonctionnalité Skew Protection en self hosted, voir aussi 2023-07-04_1735.

Stratégie de promotion de mon activité freelance sur LinkedIn #freelance, #Freelance, #PayItForward, #WebDev, #MVP, #DevOps, #BugFix, #OpenToWork

J'ai lancé mon activité de freelance en juillet 2024.

Depuis, j'ai déjà travaillé pour deux clients en régie : l'un pour du développement web, et l'autre pour une mission DevOps. Actuellement, je suis en discussion pour deux nouvelles missions.

Jusqu'à présent, je n'ai fait aucune promotion de mon activité #freelance, même parmi mes amis. Très peu de personnes sont au courant de ma nouvelle activé.

Mon premier projet en tant que freelance a été la continuation d'un projet sur lequel je travaillais depuis près d'un an.
Quant au second projet, il m'est venu grâce à un ami développeur qui m'a mis en relation avec le client.

Place de maché de freelances

La dernière semaine d'août, j'ai créé et activé un compte sur Malt, mais pour le moment, personne ne m'a contacté.

Plus d'informations au sujet de mon aventure Malt dans la note suivante : Première itération de mon aventure Malt.

Je me suis aussi inscrit sur Collective, j'ai reçu cette semaine deux opportunités de missions auxquelles je dois répondre :

Je suis aussi inscrit sur Jean-Paul.io, pour le moment, je n'ai reçu aucun message.

J'ai prévu de m'inscrire sur Comet et LeHibou.

Ma stratégie de publication LinkedIn

Maintenant que les sections CV, Mes services et Mes disponibilités et tarifs sont a jours sur mon site personnelle et que j'ai enfin édité un nouveau CV au format A4 — ce fut difficile d'être concis — je me suis dit que je suis prêt à poster un message sur LinkedIn pour informer mon réseau de ma nouvelle activité.

Voici les messages que je souhaite poster :

Bonjour, petit message "publicitaire" 😉 pour vous informer que je suis #Freelance depuis juillet.

Depuis mon lancement, j'ai déjà travaillé pour deux clients en régie, pour du développement web et une mission DevOps. Actuellement, je suis en discussion pour deux autres missions.

Il me reste entre 30 et 40 jours de disponibilité d'ici fin décembre, et je suis donc à la recherche de nouvelles missions pour compléter mon planning.

Si vous avez des projets ou connaissez des personnes qui pourraient avoir besoin de mes services, n'hésitez pas à me contacter ou à partager mon profil. 🙏

#PayItForward : Un grand merci par avance pour tout partage (repost) ou recommandation en commentaire !

Pour plus d'informations :

À bientôt ! 🙂

Et 3 jours plus tard :

Vous travaillez au sein d'une organisation et vous cherchez quelqu'un pour lancer une application web de type MVP (Produit Minimum Viable) soignée, développée et déployée à partir de zéro avec un minimum de bug ?

Si la réponse est oui, n'hésitez pas à me contacter, je peux peut-être vous aider à réaliser ce projet.

Pour plus d'informations :

À bientôt ! 🙂

#Freelance #WebDev #MVP

3 jours plus tard :

En tant que #Freelance, je propose mes services en software engineering pour des prestations que je qualifie de "pompiers", par exemple :

  • Correction de bugs sous forme de "quick win" ;
  • Réduction des lenteurs de votre application sous forme de "quick win" ;
  • Traitement en urgence de problèmes d'hébergement (hosting).

Si vous rencontrez de type de problème, n'hésitez pas à me contacter, je peux peut-être vous aider sur ces sujets.

#Freelance #DevOps #BugFix #OpenToWork

Je souhaite également publier ces messages sur Twitter et Mastodon, et éventuellement le premier sur Facebook (même si je pense que ce n'est pas l'endroit idéal pour ce type de contenu).

Journal du vendredi 13 septembre 2024 à 10:23 #pnpm, #workspace, #docker, #WebDev, #DevOps

Après une mise en pratique plus approfondie, la technique présentée dans pnpm workspace et Docker build n'a pas fonctionné comme je l'attendais.

À cause de cette ligne du /demosite/pnpm-lock.yaml :

    dependencies:
      gibbon-replay-js:
        specifier: 0.2.0
        version: link:../packages/gibbon-replay-js

Pour générer un fichier pnpm-lock.yaml qui contient :

      gibbon-replay-js:
        specifier: 0.2.0
        version: 0.2.0

j'ai créé un script nommé demosite/scripts/generate-local-pnpm-lock-file.sh qui contient :

#!/usr/bin/env bash
# This script generates a pnpm-lock.yaml file in the local directory,
# without including dependencies from workspace projects.
# For example, the version of gibbon-replay-js installed is the version
# published on npmjs.
# This point is crucial to ensure the correct operation of the docker build
# command when creating the Docker image of the demo-site project.
set -e

cd "$(dirname "$0")/../"

export npm_config_link_workspace_packages=false
export npm_config_prefer_workspace_packages=false
export npm_config_shared_workspace_lockfile=false

pnpm install --lockfile-only

Cela me permet de générer un fichier pnpm-lock.yaml qui sera présent dans le dossier /demosite/ et utilisé lors de l'exécution de Docker build.

En complément de cela, j'utilise les paramètres suivants dans /demosite/.npmrc :

link-workspace-packages=true
prefer-workspace-packages=true
shared-workspace-lockfile=true

cela permet en phase de "développement", c'est-à-dire en dehors du build Docker, d'utiliser le fichier pnpm-lock.yaml à la racine et la version locale du package packages/gibbon-replay-js.

Tout cela me paraît un peu complexe, mais pour l'instant, je n'ai pas trouvé de méthode alternative permettant de configurer un environnement de développement répondant à ces deux exigences :

  • En mode développement, utiliser directement le code du packages/gibbon-replay-js sans qu'aucune action ne soit requise de la part du développeur.
  • Pouvoir générer l'image Docker en une seule commande.

Je suis ouvert à toute suggestion 🙂 (contact@stephane-klein.info).

pnpm workspace et Docker build #pnpm, #docker, #WebDev, #workspace, #DevOps

J'écris cette note pour me souvenir pourquoi j'ai paramétré .npmrc avec les options suivantes :

link-workspace-packages=true
prefer-workspace-packages=true
shared-workspace-lockfile=false

Sans l'option link-workspace-packages=true, je devais configurer package.json comme ceci

"gibbon-replay-js": "workspace:*"

pour que /demosite utilise le package local /packages/gibbon-replay-js.

Cette contrainte me posait un problème, parce que /demosite/Dockerfile ne pouvait pas être buildé.

L'option link-workspace-packages=true permet de configurer la dépendance suivante

"gibbon-replay-js": "0.2.0"

qui pourra être installé correctement lors du build de l'image Docker.

Attention, cette version de gibbon-replay-js doit avoir préalablement été publiée sur npm registry.

Seconde option qui m'a été utile : shared-workspace-lockfile=false.

Avec cette option, pnpm install génère les fichiers /demosite/pnpm-lock.yaml et /app/pnpm-lock.yaml, fichiers indispensables pour build les images Docker.

Journal du lundi 09 septembre 2024 à 16:03 #OnMaPartagé, #DevOps, #Kubernetes, #JaiLu

Alexandre m'a partagé le projet Grafana Tanka.

Flexible, reusable and concise configuration for Kubernetes.

Je découvre ce thread Hacker News que je n'ai pas pris le temps de lire : Tanka: Our way of deploying to Kubernetes.

#JaiLu Why not Helm?.

Journal du lundi 09 septembre 2024 à 15:59 #admin-sys, #DevOps, #Doctrine, #selfhosting

Dans cette note, je souhaite présenter ma doctrine de mise à jour d'OS de serveurs.

Je ne traiterai pas ici de la stratégie d'upgrade pour un Cluster Kubernetes.

La mise à jour d'un serveur, par exemple, sous un OS Ubuntu LTS, peut être effectuée avec les commandes suivantes :

  • sudo apt upgrade -y
  • ou sudo apt dist-upgrade -y (plus risqué)
  • ou sudo do-release-upgrade (encore plus risqué)

L'exécution d'un sudo apt upgrade -y peut :

  • Installer une mise à jour de docker, entraînant une interruption des services sur ce serveur de quelques secondes à quelques minutes.
  • Installer une mise à jour de sécurité du kernel, nécessitant alors un redémarrage du serveur, ce qui entraînera une coupure de quelques minutes.

Une montée de version de l'OS via sudo do-release-upgrade peut prendre encore plus de temps et impliquer des ajustements supplémentaires.

Bien que ces opérations se déroulent généralement sans encombre, il n'y a jamais de certitude totale, comme l'illustre l'exemple de la Panne informatique mondiale de juillet 2024.

Sachant cela, avant d'effectuer la mise à jour d'un serveur, j'essaie de déterminer quelles seraient les conséquences d'une coupure d'une journée de ce serveur.

Si je considère que ce risque de coupure est inacceptable ou ne serait pas accepté, j'applique alors la méthode suivante pour réaliser mon upgrade.

Je n'effectue pas la mise à jour le serveur existant. À la place, je déploie un nouveau serveur en utilisant mes scripts automatisés d'Infrastructure as code / GitOps.

C'est pourquoi je préfère éviter de nommer les serveurs d'après le service spécifique qu'ils hébergent (voir aussi Pets vs Cattle). Par exemple, au lieu de nommer un serveur gitlab.servers.example.com, je vais le nommer server1.servers.example.com et configurer gitlab.servers.example.com pour pointer vers server1.servers.example.com.

Ainsi, en cas de mise à jour de server1.servers.example.com, je crée un nouveau serveur nommé server(n+1).servers.example.com.

Ensuite, je lance les scripts de déploiement des services qui étaient présents sur server1.servers.example.com.

Idéalement, j'utilise mes scripts de restauration des données depuis les sauvegardes des services de server1.servers.example.com, ce qui me permet de vérifier leur bon fonctionnement. Ensuite, je prépare des scripts rsync pour synchroniser rapidement les volumes entre server1.servers.example.com et server(n+1).servers.example.com.

Je teste que tout fonctionne bien sur server(n+1).servers.example.com.

Si tout fonctionne correctement, alors :

  • J'arrête les services sur server(n+1).servers.example.com ;
  • J'exécute le script de synchronisation rsync de server1.servers.example.com vers server(n+1).servers.example.com ;
  • Je relance les services sur server(n+1).servers.example.com
  • Je modifie la configuration DNS pour faire pointer les services de server1.servers.example.com vers server(n+1).servers.example.com
  • Quelques jours après cette intervention, je décommissionne server1.servers.example.com.

Cette méthode est plus longue et plus complexe qu'une mise à jour directe de l'OS sur le server1.servers.example.com, mais elle présente plusieurs avantages :

  • Une grande sécurité ;
  • L'opération peut être faite tranquillement, sans stress, avec de la qualité ;
  • Une durée de coupure limitée et maîtrisée ;
  • La possibilité de confier la tâche en toute sécurité à un nouveau DevOps ;
  • La garantie du bon fonctionnement des scripts de déploiement automatisé ;
  • La vérification de l'efficacité des scripts de restauration des sauvegardes ;
  • Un test concret des scripts et de la documentation du Plan de reprise d'activité.

Si le serveur à mettre à jour fonctionne sur une Virtual instance, il est également possible de cloner la VM et de tester la mise à niveau. Cependant, je préfère éviter cette méthode, car elle ne permet pas de valider l'efficacité des scripts de déploiement.

Journal du samedi 07 septembre 2024 à 19:32 #DevOps, #admin-sys, #JeMeDemande, #JaiPublié

#JeMeDemande souvent comment nommer la méthode permettant d'installer des applications de manière scriptée.

Lorsque je recherche cette fonctionnalité, j'utilise généralement les mots-clés : "headless", "script", "cli".

Je constate que la roadmap de Proxmox utilise le terme « automated and unattended installation » :

Support for automated and unattended installation of Proxmox VE.

-- from

#JaiPublié cette question dans la section discussion du projet Plausible : Plausible automated and unattended installation support (create user and web site by script) · plausible/analytics.

Journal du mercredi 28 août 2024 à 11:31 #prometheus, #DevOps, #monitoring, #grafana, #DomainName, #JaiDécouvert

Je cherche depuis plusieurs années une solution pour surveiller la date d'expiration des noms de domaine en analysant le contenu de Whois.

#JaiDécouvert cet exporter Prometheus qui correspond exactement à mon besoin : https://github.com/shift/domain_exporter

En examinant l'historique du projet, je constate qu'il a été lancé en 2017. Il me semble pourtant avoir recherché ce type d'exporter en 2019 sans le trouver. Peut-être n'était-il pas encore assez mature à ce moment-là 🤔.

J'ai identifié aussi les projets suivants :

Pour le moment, je n'ai pas encore trouvé de dashboards Grafana pour cet exporter.

Journal du mercredi 21 août 2024 à 10:37 #coding, #DevOps, #snippets, #docker, #docker-compose

Note de type snippets concernant docker compose et l'utilisation de la fonctionnalité healthcheck et depends_on.

Cette méthode évite que le service webapp démarre avant que les services postgres et redis soient prêts.

# Fichier docker-composexyml
services:
    postgres:
        image: postgres:16
        ...
        healthcheck:
            test: ["CMD", "sh", "-c", "pg_isready -U $$POSTGRES_USER -h $$(hostname -i)"]
            interval: 10s
            start_period: 30s

	redis:
	    image: redis:7
	    ...
	    healthcheck:
	      test: ["CMD", "redis-cli", "ping"]
	      timeout: 10s
	      retries: 3
	      start_period: 10s

    webapp:
	    image: ...
        depends_on:
            postgres:
                condition: service_healthy
            redis:
                condition: service_healthy

Ici la commande :

$ docker compose up -d webapp

s'assure du lancement de ses dépendances, les services postgres et redis.

De plus, si le Dockerfile du service webapp contient par exemple :

# Fichier Dockerfile
...
RUN apt update -y; apt install -y curl
...
HEALTHCHECK --interval=30s --timeout=10s --retries=3 CMD curl --fail http://localhost:3000 || exit 1

Alors, je peux lancer webapp avec :

$ docker compose up -d webapp --wait

Avec l'option --wait docker compose "rend la main" lorsque le service webapp est prêt à recevoir des requêtes.

Ressources :

Journal du dimanche 28 juillet 2024 à 09:43 #DevOps, #admin-sys, #JaiDécouvert

Je viens d'apprendre que l'option -N de ssh permet de ne pas ouvrir une session shell interactive sur le serveur distant.

Option très utile lors de l'ouverture de tunnels ssh.

Exemple :

ssh -L 8080:localhost:80 user@host -N

Vous êtes sur la première page | [ Page suivante (42) >> ]