Cliquez sur un ou plusieurs tags pour appliquer un filtre sur la liste des notes de type "Journaux" :

Résultat de la recherche (1654 notes) :

Jeudi 20 février 2025

Workflow de gestion des secrets d'un projet basé sur Age et des clés ssh #Doctrine, #chiffrement, #password, #secret, #dev-kit

De 2018 à 2023, j'utilisais l'outil "pass" (https://www.passwordstore.org/) couplé avec GNU Privacy Guard pour chiffrer et déchiffrer les secrets dans mes projets professionnels.

Bien que cette méthode fonctionnait, elle s'avérait laborieuse.
La procédure documentant l'installation, la création des clés et la configuration de gnupg était l'étape posant le plus de difficultés d'onboarding des développeur sur ces projets.
Au feeling, je dirais que trois développeurs sur quatre bloquaient à cette étape.

En parallèle à cette méthode, j'ai essayé d'utiliser la cli de Bitwarden, mais l'expérience n'a pas été très concluante, principalement à cause de problèmes de latence lors de son exécution.

Aujourd'hui, j'ai exploré une nouvelle méthode basée sur Age et son support natif des clés ssh.

Voici le repository Git du résultat de cette exploration : age-secret-skeleton.

Le résultat est très positif et je pense avoir trouvé ma nouvelle méthode de chiffrement des secrets dans mes projets 🙂.

Pour en savoir plus, vous pouvez à la fois suivre le README.me du repository et continuer la lecture de cette note.

Détail des fichiers du repository

.
├── .envrc
├── .gitignore
├── .mise.toml
├── README.md
├── scripts
│   ├── decrypt_secrets.sh
│   └── encrypt_secrets.sh
├── .secret
├── .secret.age
├── .secret.skel
└── ssh-keys
    └── stephane-klein.pub
  • /.secret : fichier qui contient les secrets en clair. Ce fichier est ignoré par .gitignore afin qu'il ne soit pas présent dans le repository git
  • /.secret.age : fichier qui contient le contenu du fichier .secret chiffré avec Age. Ce fichier est ajouté au repository git.
  • /ssh-keys/ : ce dossier contient les clés publiques ssh des personnes qui ont accès au contenu de .secret.age
  • /scripts/decrypt_secrets.sh : permet de déchiffrer avec Age le contenu de .secret.age et écrit le résultat en clair dans .secret
  • /scripts/encrypt_secrets.sh : permet de chiffrer avec Age le contenu de .secret vers .secret.age. Ce script doit être exécuté quand le contenu de .secret est modifié ou quand une nouvelle clé est ajoutée dans /ssh-keys/
  • /.envrc : Dans ce skeleton, ce fichier charge uniquement les variables d'environnement présentes dans .secret mais dans un vrai projet, il permet de charger automatiquement des variables d'environnement qui ne sont pas des secrets.
  • /.secret.skel : ce fichier contient l'exemple de contenu du fichier .secret, sans les secrets. Par exemple :
# You can either request these secrets from Stéphane Klein (contact@stephane-klein.info)
# or, if your public ssh key is present in `./ssh-keys/` use the ./scripts/decrypt_secrets.sh command
# which will automatically decrypt the .secret.age file to .secret

export POSTGRES_PASSWORD="..."
export SCW_SECRET_KEY="..."

Ce fichier n'est pas nécessaire au bon fonctionnement du workflow mais je le trouve utile pour :

  • documenter le contenu de .secret aux personnes qui ont juste accès au dépôt Git
  • permettre de review les modifications de .secret.skel dans des Merge Request.

Utilisation des clés ssh

L'un des avantages majeurs de Age par rapport à pass + gnupg est son support natif des clés SSH pour chiffrer et déchiffrer les secrets.

Ainsi, je n'ai plus besoin de demander aux développeurs deux types de clés (SSH et gnupg) : une seule clé SSH suffit.

De plus, il me semble qu'un grand nombre de développeurs possèdent déjà une clé SSH, alors que je pense que gnupg reste une technologie bien moins répandue.

Détail d'implémentation des scripts

Vvoici le contenu de /scripts/encrypt_secrets.sh :

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

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

# Prepare recipient arguments for age
recipient_args=()
for pubkey in ./ssh-keys/*.pub; do
    if [ -f "$pubkey" ]; then
        recipient_args+=("-R" "$pubkey")
    fi
done

# Execute age with all public keys
age "${recipient_args[@]}" -o .secret.age .secret

La boucle permet de passer toutes les clés ssh du dossier /ssh-keys/* en paramètre d' Age.

Voici le contenu de /scripts/decrypt_secrets.sh :

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

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

# Prepare identity arguments for age
identity_args=()
for key in ~/.ssh/id_*; do
    if [ -f "$key" ] && ! [[ "$key" == *.pub ]]; then
        identity_args+=("-i" "$key")
    fi
done

# Execute age with all identity files
age -d "${identity_args[@]}" -o .secret .secret.age

cat << EOF
Secret decrypted in .secret
Don't forget to run the command:

$ source .envrc
EOF

La boucle permet de passer en paramètre toutes les clés privées ssh du dossier ~/.ssh/ en espérant en trouver une qui correspond à une clé publique du dossier /ssh-keys/.

Plusieurs niveaux de sécurisation

Si je veux cloisonner les secrets en limitant leur accès à des groupes d'utilisateurs distincts, je peux utiliser des secrets différents selon l'environnement.

Par exemple :

.
├── production
│   ├── .secret.age
│   └── ssh-keys
└── sandbox
    ├── .secret.age
    └── ssh-keys

Cette structure me permet de donner à certains utilisateurs accès aux secrets de l'environnement sandbox, sans leur donner accès à ceux de production.

Aller plus loin avec par exemple Vault ?

Je pense qu'il est possible d'aller plus loin en matière de sécurité avec des solutions comme Vault, mais trouve que la méthode basée sur Age reste plus simple à déployer dans une petite équipe.

Jeudi 13 février 2025

Journal du jeudi 13 février 2025 à 14:50 #backup, #postgresql, #L15, #L1

Suite à ce commentaire et celui-ci, je m'adresse dans cette note à Anarcat (francophone) et Martín Marqués pour expliquer ce que j'essaie de faire dans le POC https://github.com/stephane-klein/poc-barman, ce que j'ai réussi à faire et présenter aussi mes difficultés.

J'ai traduit cette note en anglais et je l'ai postée sur "GitHub Barman discussion" : https://github.com/EnterpriseDB/barman/discussions/1067.


Mon objectif dans le repository poc-barman est d'essayer d'utiliser barman dans un container Docker sidecar pour sauvegarder un container PostgreSQL.

Une de mes contraintes est d'effectuer un minimum de changements au niveau du container PostgreSQL que je souhaite sauvegarder. Je souhaite pouvoir utiliser une image Docker PostgreSQL mainstream https://hub.docker.com/_/postgres, sans changement.

Je souhaite utiliser le mode de sauvegarde de barman nommé streaming backups method: backup_method = postgres qui se base sur la commande pg_basebackup (commande officielle intégrée à PostgreSQL).

Je souhaite utiliser la nouvelle fonctionnalité pg_basebackup --incremental... de la version 17 de PostgreSQL.

Voici ma configuration de barman : https://github.com/stephane-klein/poc-barman/blob/4df58ecc5af6d2d1f7607c364400f8c5ba012496/docker-compose.yml#L15

Et voici ma configuration de PostgreSQL 17 :

J'ai implémenté un script nommé ./scripts/reset.sh qui effectue un test de bout automatiquement.

Voici son screencast :

Voici ce qu'il fait :

Questions que je me pose :

  • Pourquoi la restauration basée sur la sauvegarde incrémentielle échoue ?
  • Est-ce que mon scénario de test d'usage de barman est correct, est-ce qu'il me manque des étapes ou est-ce que je fais des opérations non nécessaires ?
  • Est-ce que j'ai fait des erreurs importantes ?

Voici ci-dessous la version anglaise posté ici.


Subject: Streaming Incremental Backup Configuration with PostgreSQL 17 using Docker Sidecar

Hello,

In the poc-barman repository, I'm trying to use barman in a Docker sidecar container to backup a PostgreSQL container.

One of my constraints is to make minimal changes to the PostgreSQL container that I want to backup. I want to be able to use a mainstream Docker PostgreSQL image https://hub.docker.com/_/postgres, without modifications.

I want to use the barman backup mode called streaming backups method: backup_method = postgres which is based on the pg_basebackup command (official command integrated into PostgreSQL).

I want to use the new pg_basebackup --incremental... feature from PostgreSQL version 17.

Here is my barman configuration: https://github.com/stephane-klein/poc-barman/blob/4df58ecc5af6d2d1f7607c364400f8c5ba012496/docker-compose.yml#L15

And here is my PostgreSQL 17 configuration:

I implemented a script called ./scripts/reset.sh that performs an end-to-end test automatically.

Here's its screencast:

Here's what it does:

Questions:

  • Why does the restoration based on the incremental backup fail?
  • Is my barman usage test scenario correct, am I missing steps or am I performing unnecessary operations?
  • Have I made any significant mistakes?

Best regards,
Stephane

Journal du jeudi 13 février 2025 à 14:09 #postgresql, #backup

Suite de mes notes 2025-02-09_1705, 2025-02-12_1044, 2025-02-12_1511, 2025-02-12_1534 et 2025-02-12_2305 au sujet de barman pour sauvegarder des bases de données PostgreSQL


Je ne sais pas pourquoi je dois lancer select pg_switch_wal();.

source

J'ai découvert dans ce commentaire qu'il existe une commande nommée : barman switch-wal.

Je pense avoir compris qu'avant d'exécuter barman backup… il est nécessaire d'exécuter :

$ barman switch-wal
$ barman cron
$ barman check postgres1
Server postgres1:
        PostgreSQL: OK
        superuser or standard user with backup privileges: OK
        PostgreSQL streaming: OK
        wal_level: OK
        replication slot: OK
        directories: OK
        retention policy settings: OK
        backup maximum age: OK (no last_backup_maximum_age provided)
        backup minimum size: OK (0 B)
        wal maximum age: OK (no last_wal_maximum_age provided)
        wal size: OK (0 B)
        compression settings: OK
        failed backups: OK (there are 0 failed backups)
        minimum redundancy requirements: OK (have 0 backups, expected at least 0)
        pg_basebackup: OK
        pg_basebackup compatible: OK
        pg_basebackup supports tablespaces mapping: OK
        systemid coherence: OK (no system Id stored on disk)
        pg_receivexlog: OK
        pg_receivexlog compatible: OK
        receive-wal running: OK
        archiver errors: OK

$ barman backup postgres1 --immediate-checkpoint
Starting backup using postgres method for server postgres1 in /var/lib/barman/postgres1/base/20250213T100353
Backup start at LSN: 0/4000000 (000000010000000000000004, 00000000)
Starting backup copy via pg_basebackup for 20250213T100353
Copy done (time: 1 second)
Finalising the backup.
This is the first backup for server postgres1
WAL segments preceding the current backup have been found:
        000000010000000000000002 from server postgres1 has been removed
Backup size: 22.3 MiB
Backup end at LSN: 0/6000000 (000000010000000000000006, 00000000)
Backup completed (start time: 2025-02-13 10:03:53.072228, elapsed time: 1 second)
Processing xlog segments from streaming for postgres1
        000000010000000000000003
        000000010000000000000004
WARNING: IMPORTANT: this backup is classified as WAITING_FOR_WALS, meaning that Barman has not received yet all the required WAL files for the backup consistency.
This is a common behaviour in concurrent backup scenarios, and Barman automatically set the backup as DONE once all the required WAL files have been archived.
Hint: execute the backup command with '--wait'
total 4.0K
$ ls /var/lib/barman/postgres1/base/ -lha
total 8.0K
drwxr-xr-x 1 barman barman 60 Feb 13 10:00 .
drwxr-xr-x 1 barman barman 88 Feb 13 09:59 ..
drwxr-xr-x 1 barman barman 30 Feb 13 09:59 20250213T095917

$ barman list-backups postgres1
postgres1 20250213T103723 - F - Thu Feb 13 10:37:24 2025 - Size: 22.3 MiB - WAL Size: 0 B - WAITING_FOR_WALS

J'ai réussi dans le POC https://github.com/stephane-klein/poc-barman à dérouler toutes les étapes du backup complet jusqu'à la restauration d'une base de données.

Toutefois, pour le moment, je n'ai toujours pas réussi à restaurer un backup incrémental 🙁.

À cet endroit, j'ai l'erreur suivante :

$ docker compose up postgres2
postgres2-1  | PostgreSQL Database directory appears to contain a database; Skipping initialization
postgres2-1  |
postgres2-1  | 2025-02-13 13:20:07.594 UTC [1] LOG:  starting PostgreSQL 17.2 (Debian 17.2-1.pgdg120+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
postgres2-1  | 2025-02-13 13:20:07.594 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
postgres2-1  | 2025-02-13 13:20:07.594 UTC [1] LOG:  listening on IPv6 address "::", port 5432
postgres2-1  | 2025-02-13 13:20:07.596 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
postgres2-1  | 2025-02-13 13:20:07.598 UTC [1] LOG:  could not open directory "pg_tblspc": No such file or directory
postgres2-1  | 2025-02-13 13:20:07.600 UTC [29] LOG:  database system was interrupted; last known up at 2025-02-13 13:20:03 UTC
postgres2-1  | 2025-02-13 13:20:07.643 UTC [29] LOG:  could not open directory "pg_tblspc": No such file or directory
postgres2-1  | 2025-02-13 13:20:07.643 UTC [29] LOG:  starting backup recovery with redo LSN 0/8000028, checkpoint LSN 0/8000080, on timeline ID 1
postgres2-1  | 2025-02-13 13:20:07.643 UTC [29] LOG:  could not open directory "pg_tblspc": No such file or directory
postgres2-1  | 2025-02-13 13:20:07.649 UTC [29] FATAL:  could not open directory "pg_tblspc": No such file or directory
postgres2-1  | 2025-02-13 13:20:07.651 UTC [1] LOG:  startup process (PID 29) exited with exit code 1
postgres2-1  | 2025-02-13 13:20:07.651 UTC [1] LOG:  aborting startup due to startup process failure
postgres2-1  | 2025-02-13 13:20:07.652 UTC [1] LOG:  database system is shut down

Mercredi 12 février 2025

Journal du mercredi 12 février 2025 à 23:05 #backup, #postgresql

Suite de ma note 2025-02-12_1511.

J'ai passé 5h40 sur un POC de barman, mais je n'ai pas eu beaucoup plus de succès qu'avec pgBackRest. Décidément, ces outils ne m'aiment pas 😔.

Repository du POC : https://github.com/stephane-klein/poc-barman

La commande barman check streaming-server retourne le message WAL archive: FAILED (please make sure WAL shipping is setup). Pour fixer cette erreur, je dois faire les manipulations suivantes que je trouve bizarre :

$ ./scripts/enter-in-pg1.sh
postgres=# select pg_switch_wal();
 pg_switch_wal
---------------
 0/206A330
(1 row)

et ensuite :

$ docker compose exec barman bash
root@5482aa5f8420:/# su barman
barman@5482aa5f8420:/$ barman cron
Starting WAL archiving for server streaming-server
barman@5482aa5f8420:/$ barman check streaming-server
Server streaming-server:
        PostgreSQL: OK
        superuser or standard user with backup privileges: OK
        PostgreSQL streaming: OK
        wal_level: OK
        replication slot: OK
        directories: OK
        retention policy settings: OK
        backup maximum age: OK (no last_backup_maximum_age provided)
        backup minimum size: OK (0 B)
        wal maximum age: OK (no last_wal_maximum_age provided)
        wal size: OK (0 B)
        compression settings: OK
        failed backups: OK (there are 0 failed backups)
        minimum redundancy requirements: OK (have 0 backups, expected at least 0)
        pg_basebackup: OK
        pg_basebackup compatible: OK
        pg_basebackup supports tablespaces mapping: OK
        systemid coherence: OK (no system Id stored on disk)
        pg_receivexlog: OK
        pg_receivexlog compatible: OK
        receive-wal running: OK
        archiver errors: OK

Je ne sais pas pourquoi je dois lancer select pg_switch_wal();.

J'ai pourtant configuré checkpoint_timeout='60s' :

    command: >
      postgres
      -c wal_level=replica
      -c summarize_wal=on
      -c checkpoint_timeout='60s'
      -c max_wal_size='100MB'

Je pensais que ce paramètre effectuait la même action que pg_switch_wal(); mais je constate que non.

Aussi, je constate que je dois aussi lancer pg_switch_wal(); pour que la commande suivante se termine :

barman@5482aa5f8420:/$ barman backup streaming-server --wait
Starting backup using postgres method for server streaming-server in /var/lib/barman/streaming-server/base/20250212T221703
Backup start at LSN: 0/5000B40 (000000010000000000000005, 00000B40)
Starting backup copy via pg_basebackup for 20250212T221703
Copy done (time: 1 second)
Finalising the backup.
Backup size: 22.3 MiB
Backup end at LSN: 0/7000000 (000000010000000000000007, 00000000)
Backup completed (start time: 2025-02-12 22:17:03.190492, elapsed time: 1 second)
Waiting for the WAL file 000000010000000000000007 from server 'streaming-server'
Processing xlog segments from streaming for streaming-server
        000000010000000000000005
Processing xlog segments from streaming for streaming-server
        000000010000000000000006

Je ne comprends pas non plus pourquoi.

Journal du mercredi 12 février 2025 à 15:53 #JaiDécouvert, #astuce, #docker

Jusqu'à aujourd'hui, j'utilise la commande sleep infinity pour garder en "vie" un container Docker qui n'exécute aucun service.

Exemple de docker-compose.yml :

services:
  ubuntu:
    image: ubuntu
    command: sleep infinity

Ce qui me permet d'entrer dans le container après son lancement en background :

$ docker compose up -d ubuntu
$ docker compose exec ubuntu bash
root@357466c4b52c:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

Aujourd'hui, j'ai découvert via Copilot une alternative préférable à sleep infinity :

services:
  ubuntu:
    image: ubuntu
    command: tail -f /dev/null

Cela fonctionne de la même manière, mais semble avoir l'avantage de ne pas dépendre de la dépendance sleep 🤔.

Journal du mercredi 12 février 2025 à 15:11 #backup, #docker, #postgresql, #JaiDécidé

Je pense comprendre que pgBackRest ne permet pas d'utiliser des INET sockets pour communiquer avec PostgreSQL.

Toutefois, je me dis que je pourrais partager le volume PGDATA avec le sidecar pgBackRest pour lui donner accès à l'Unix Socket du Streaming Replication Protocol 🤔.

source

Je viens de me rappeler que pgBackRest a une seconde contrainte qui semble l'empêcher de fonctionner en Docker sidecar :

Backing up a running PostgreSQL cluster requires WAL archiving to be enabled. Note that at least one WAL segment will be created during the backup process even if no explicit writes are made to the cluster.

pg-primary:/etc/postgresql/15/demo/postgresql.conf ⇒ Configure archive settings :

archive_command = 'pgbackrest --stanza=demo archive-push %p'
archive_mode = on
max_wal_senders = 3
wal_level = replica 

source

Cela signifie que l'exécutable pgbackrest doit être installé dans l'image Docker PostgreSQL.

Cela me pose un problème parce que mon objectif est de pouvoir utiliser un système de sauvegarde en Docker sidecar sans avoir à utiliser une image Docker PostgreSQL modifiée.

Cette contrainte ne semble pas présente avec barman qui propose 3 méthodes de backup :

La méthode postgres utilise pg_basebackup et je pense qu'elle peut fonctionner en Docker sidecar.

#JaiDécidé d'explorer cette piste.

Journal du mercredi 12 février 2025 à 11:41 #network, #unix, #linux, #JaiDécouvert

En rédigeant la note 2025-02-12_1044, je me suis demandé quelle est la différence de performance entre un Unix Socket et un INET socket.

Durant mes recherches, j'ai découvert qu'Unix Socket est plus rapide que INET sockets. Je n'ai pas été surpris, j'avais cette intuition.

Toutefois, j'ai été surpris d'apprendre que le gain est non négligeable, de 30% à 50% de gains !

Exemple :

On my machine, Ryzen 3900X, Ubuntu 22,

A basic C++ TCP server app that only sends 64K packets, and a basic c++ receiver that pulls 100GB of these packets and copies them to it's internal buffer only, single-threaded:

  • achieves ~30-33 GBit/sec for TCP connection (~4.0GB/sec) (not MBit)
  • and ~55-58GBit/sec for a socket connection, (~7.3 GB/sec)
  • and ~492Gbit/sec for in-process memcopy (~61GB /sec)

source

Conséquence : je vais essayer d'utiliser des Unix sockets autant que je peux.

Journal du mercredi 12 février 2025 à 10:44 #PostgREST, #backup, #JaiDécouvert

En travaillant sur le projet Projet 23 - "Ajouter le support pg_basebackup incremental à restic-pg_dump-docker", j'ai découvert que PostgreSQL propose deux protocoles de communication :

Sans vraiment comprendre, en 2020, j'avais activé le protocole streaming replication dans ce POC postgresql-streaming-replication-playground. Mais je n'avais jamais pris conscience de l'existence de ce second protocole.

Les développeurs de PostgreSQL semblent avoir décidé de créer un second protocole parce qu'ils n'ont pas du tout le même objectif.
Streaming Replication Protocol est optimisé dans la transmission des WAL et des snapshots (copie de l'intégralité du dossier PGDATA).

Le protocole Streaming Replication Protocol est entre autres utilisé par pg_basebackup, barman, pgBackRest, ou Patroni.

Comment activer Streaming Replication Protocol ?

Les images Docker Postgres setup par défaut le fichier pg_hba.conf suivant :

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     trust
# IPv4 local connections:
host    all             all             127.0.0.1/32            trust
# IPv6 local connections:
host    all             all             ::1/128                 trust
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     all                                     trust
host    replication     all             127.0.0.1/32            trust
host    replication     all             ::1/128                 trust
host all all all scram-sha-256

J'ai compris que par défaut, toutes ces lignes configurent l'accès au Frontend/Backend Protocol.
Seules les lignes qui contiennent replication configurent l'accès au Streaming Replication Protocol.

Pour permettre l'accès au Streaming Replication Protocol par réseau TCP/IP en dehors du container Docker il est nécessaire d'ajouter la ligne suivante :

host    replication     all             all                     scram-sha-256

Suite à cette découverte, j'ai repensé à :

Pour faire face à ce problème, j'ai exploré fin 2023 une solution basée sur pgBackRest : Implémenter un POC de pgBackRest.
Je suis plus ou moins arrivé au bout de ce POC mais je n'ai pas été satisfait du résultat.
Je n'ai pas réussi à configurer pgBackRest en "pure Docker sidecar".
De plus, j'ai trouvé la restauration du backup difficile à exécuter.

source

et je me suis demandé si mon échec de configuration de pgBackRest en Docker sidecar n'était pas seulement dû au fait que je n'avais pas activé Streaming Replication Protocol 🤔.

La réponse semble être "oui" et "non".

Je suis tombé sur l'issue suivante : pgbackrest with postgresql in docker.

The problem might be harder than you think unfortunately. If the pgBackRest process is running on the VM (docker host), it will try to connect to PG locally using the unix socket, not the tcp "localhost" connection.

source

Je pense comprendre que pgBackRest ne permet pas d'utiliser des INET sockets pour communiquer avec PostgreSQL.

Toutefois, je me dis que je pourrais partager le volume PGDATA avec le sidecar pgBackRest pour lui donner accès à l'Unix Socket du Streaming Replication Protocol 🤔.

Entre mon exploration de pg_basebackup, mes envies de tester barman et de continuer mon POC pgBackRest… je me dis que je ne suis pas encore au bout de ce Yak!.

Mardi 11 février 2025

Journal du mardi 11 février 2025 à 16:18 #TVA, #microservices, #freelance

Suite de mes notes 2025-02-03_1718 et 2025-02-04_1136 au sujet des régimes de TVA.

Cette après-midi, j'ai envoyé le message suivant à mon centre des impôts :

Bonjour,

J'ai bien noté de votre réponse suivante lors de notre dernier échange par message :

Je vous remercie d’envoyer une lettre d’option à la TVA datée et signée en spécifiant le régime demandé.

J'ai trouvé un modèle de lettre que je n'ai pas encore totalement rempli :

Avant de faire mon choix, j'ai quelques questions :

  • a. En régime réel simplifié de TVA, comment est calculé le montant de l'acompte ? J'ai lu qu'il est basé sur le chiffre d'affaires N-1, mais est-ce qu'il est possible de modifier le montant de cet acompte si je sais déjà que mon chiffre d'affaires sera en baisse ou en hausse ?
  • b. Pouvez-vous me confirmer que ma déclaration devrait se faire sur la page web indiquée dans le screenshot en pièce jointe ?
  • c. Est-ce que la déclaration doit s'effectuer en fonction de la date d'émission de la facture ou alors de sa date d'encaissement ?

Cordialement, Stéphane Klein

Fort de l'expérience tirée de mon précédent message, j'ai décidé d'appeler le centre des impôts pour obtenir des réponses à mes questions.

Ce fut une excellente décision : voici les réponses que j'ai acquises à l'oral.

  • a. En régime réel simplifié de TVA, comment est calculé le montant de l'acompte ? J'ai lu qu'il est basé sur le chiffre d'affaires N-1, mais est-ce qu'il est possible de modifier le montant de cet acompte si je sais déjà que mon chiffre d'affaires sera en baisse ou en hausse ?

Réponse :

Oui, il est possible de modifier manuellement le montant de l'acompte. Toutefois, en déclarant un acompte inférieur à celui basé sur le chiffre d'affaires de l'année N-1, l'administration fiscale tolère une marge d'erreur de 10 % par rapport au chiffre d'affaires de l'année N.

Prenons l'exemple d'une entreprise en régime réel simplifié de TVA.

  • En N-1, son chiffre d'affaires était de 100 000 €.
  • Le montant des acomptes de TVA est donc calculé sur cette base.

L'entreprise prévoit une baisse d'activité en N et estime son chiffre d'affaires à 85 000 €. Elle souhaite ajuster son acompte en conséquence.

Tolérance de 10 % sur le chiffre d'affaires N : Si, en fin d'année N, le chiffre d'affaires réel est finalement de 90 000 €, alors l'acompte déclaré (basé sur 85 000 €) reste dans la marge d'erreur de 10 % et ne posera pas de problème fiscal.

En revanche, si le chiffre d'affaires réel s'avère être de 95 000 € ou plus, alors l'acompte a été sous-estimé au-delà de la tolérance, ce qui pourrait entraîner un redressement ou des pénalités.

Pouvez-vous me confirmer que ma déclaration devrait se faire sur la page web indiquée dans le screenshot en pièce jointe ?

La réponse est non.

c. Est-ce que la déclaration doit s'effectuer en fonction de la date d'émission de la facture ou alors de sa date d'encaissement ?

Pour une activité de prestation de services, la TVA est déclarée sur la base de la date d’encaissement de la facture, et non de sa date d’émission.

Information supplémentaire que j'ai reçue.

Le seuil de franchise en base de TVA de 34 400 € en prestations de services est calculé sur la base d'une année civile. Concernant ma micro-entreprise, j'ai encaissé moins de 34 400 € au 31 décembre 2024. Par conséquent, je n'étais pas obligé de quitter le régime de franchise en base de TVA. Toutefois, j'ai facturé de la TVA dans ma 4ᵉ facture de 2024, par conséquent, je dois la déclarer, quoi qu'il arrive.

Suite à ces réponses, j'ai décidé d'opter pour l'option "Régime réel simplifié d'imposition à la TVA". Je vais envoyer un courrier (template) pour faire cette demande.

Dimanche 9 février 2025

Journal du dimanche 09 février 2025 à 17:05 #postgresql, #backup, #projet, #JaiDécidé

J'utilise depuis 2019 les containers Docker suivant en sidecar pour sauvegarder automatiquement et régulièrement directement un volume Docker et un volume PostgreSQL :

restic-pg_dump-docker est très pratique et facile d'usage, voici un exemple d'utilisation dans un docker-compose.yml :

    restic-pg-dump:
        image: stephaneklein/restic-pg_dump:latest
        environment:
            AWS_ACCESS_KEY_ID: "admin"
            AWS_SECRET_ACCESS_KEY: "password"
            RESTIC_REPOSITORY: "s3:http://minio:9000/bucket1"
            RESTIC_PASSWORD: secret
            POSTGRES_USER: postgres
            POSTGRES_PASSWORD: password
            POSTGRES_HOST: postgres
            POSTGRES_DB: postgres

    postgres:
        image: postgres:16.1
        environment:
            POSTGRES_USER: postgres
            POSTGRES_DB: postgres
            POSTGRES_PASSWORD: password
        ports:
            - "5432:5432"
        volumes:
            - ./volumes/postgres/:/var/lib/postgresql/data/
        healthcheck:
            test: ["CMD", "sh", "-c", "pg_isready -U $$POSTGRES_USER -h $$(hostname -i)"]
            interval: 10s
            start_period: 30s

Il suffit de configurer les paramètres d'accès à l'instance PostgreSQL à sauvegarder et ceux de l'Object Storage où uploader les backups. Rien de plus, 😉.
Pour plus de paramètres, voir la section Configuration du README.md.

Cependant, je ne suis pas totalement satisfait de restic-pg_dump-docker. Cet outil effectue seulement des sauvegardes complètes de la base de données.
Ceci ne pose généralement pas trop de problème quand la base de données est d'une taille modeste, mais c'est bien plus compliqué dès que celle-ci fait, par exemple, plusieurs centaines de mégas.

Pour faire face à ce problème, j'ai exploré fin 2023 une solution basée sur pgBackRest : Implémenter un POC de pgBackRest.
Je suis plus ou moins arrivé au bout de ce POC mais je n'ai pas été satisfait du résultat.
Je n'ai pas réussi à configurer pgBackRest en "pure Docker sidecar".
De plus, j'ai trouvé la restauration du backup difficile à exécuter.

Un élément a changé depuis septembre 2024. Comme je le disais dans cette note 2024-11-03_1151, la version 17 de PostgreSQL propose de nouvelles options de sauvegarde :

  • l'outil pg_basebackup qui permet de réaliser les sauvegardes incrémentales,
  • et un nouvel utilitaire, pg_combinebackup, qui permet de reconstituer une sauvegarde complète à partir de sauvegardes incrémentales.

Cette nouvelle méthode semble apporter certains avantages par rapport aux solutions basées sur WAL comme pgBackRest ou barman.

Une consommation d'espace réduite :

In this mailing list thread on the Postgres-hackers mailing list, Jakub from EDB ran a test. This is a pgbench test. The idea is that the data size doesn't really change much throughout this test. This is a 24 hour long test. At the start the database is 3.3GB. At the end, the database is 4.3GB. Then, as it's running, it's continuously running pgbench workloads. In those 24 hours, if you looked at the WAL archive, there were 77 GB of WAL produced.

That's a lot of WAL to replay if you wanted to restore to a particular point in time within that timeframe!

Jakub ran one full backup in the beginning and then incremental backups every two hours. The full backup in the beginning is 3.4 GB, but then all the 11 other backups are 3.5 in total, they're essentially one 10th of a full backup size.

source

Une vitesse de restauration grandement accélérée :

A 10x time safe

What Jakub tested then was the restore to a particular point in time. Previously, to restore to a particular point in time would take more than an hour to replay the WAL versus in this case because we have more frequent, incremental backups, it's going to be much, much faster to restore. In this particular test case 78 minutes compared to 4 minutes. This is a more than a 10 times improvement in recovery time. Of course you won't necessarily always see this amount of benefit, but I think this shows why you might want to do this. It is because you want to enable more frequent backups and incremental backups are the way to do that.

source

Nombre 2024 j'ai passé un peu de temps à étudier les solutions de backup qui utilisent la nouvelle fonctionnalité de PostgreSQL 17, mais je n'avais rien trouvé

Je viens à nouveau de chercher dans les archives de Postgre Weely, sur GitHub, sur le forum de Restic, etc., et je n'ai rien trouvé d'intéressant.

#JaiDécidé de prendre les choses en main et de faire évoluer le projet restic-pg_dump-docker pour y ajouter le support du backup incrémental de PostgreSQL 17.

Voir : Projet 23 - "Ajouter le support pg_basebackup incremental à restic-pg_dump-docker".

Vendredi 7 février 2025

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:

Jeudi 6 février 2025

Je découvre Fastlane #JaiDécouvert, #iOS, #Android, #MobileDev, #InfrastructureAsCode

Je viens de découvrir le projet Fastlane (https://fastlane.tools/).

fastlane is the easiest way to automate beta deployments and releases for your iOS and Android apps. 🚀 It handles all tedious tasks, like generating screenshots, dealing with code signing, and releasing your application.

source

Je pense que c'est l'outil qui me manquait pour suivre le paradigme configuration as code dans mon "Projet 17 - Créer un POC de création d'une app smartphone avec Capacitor".

Il supporte Android et iOS.

Ce projet a commencé en 2014 par Felix Krause et repris par Google en 2017 : « fastlane is joining Google - Felix Krause ».

Mais, je découvre que Google semble avoir arrêter de financer le développement du projet en 2023 : Google is no longer sponsoring Fastlane | Hacker News.

Depuis, le projet semble être toujours actif avec de nombreuses release : https://github.com/fastlane/fastlane/releases.

Fonctionnalités de Fastlane qui m'intéressent tout particulièrement :

Fastlane permet aussi d'automatiser de nombreuses autres tâches que je n'ai pas encore pris le temps d'explorer : https://docs.fastlane.tools/actions/.


Installation de Fastlane avec Mise que j'ai testée sous Fedora et MacOS :

[tools]
ruby = '3.1.6' # for fastlane
fastlane = "2.226.0"

[alias]
fastlane = "https://github.com/mollyIV/asdf-fastlane.git"

(Toujours aussi pénibles ces outils développés en Ruby 😔)

À noter que sous MacOS j'ai dû lancer :

$ mise install -f fastlane

Parce que lors du premier lancement de mise install, j'ai l'impression qu'il a essayé d'installer fastlane avec l'instance ruby native de l'OS.

J'ai cherché s'il existe une option pour préciser que fastlane doit utiliser ruby installé par Mise, mais je n'ai pas trouvé.


17:05 - Solution pour contourner le problème mentionné ci-dessus :

$ mise install ruby
$ mise install

Mercredi 5 février 2025

Journal du mercredi 05 février 2025 à 18:32 #OnMaPartagé, #JaiDécouvert, #python, #package, #mise

Un ami m'a fait découvrir uv (https://github.com/astral-sh/uv).

An extremely fast Python package and project manager, written in Rust.

source

Je trouve cela amusant de constater que Rust prend en charge de plus en plus d'outils pour différents langages 😉.

Le projet a commencé fin 2023.

Voici un thread Hacker News de 200 commentaires à ce sujet qui date de février 2024 : Uv: Python packaging in Rust .

L'article de ce thread contient beaucoup d'éléments intéressants : https://astral.sh/blog/uv

Son nom uv semble être une référence à uvloop.

J'en ai profité pour migrer le playground mise-python-flask-playground de pip vers uv : https://github.com/stephane-klein/mise-python-flask-playground/commit/2f1678798cfc6749dcfdb514a8fe4a3e54739844.

J'ai lancé une installation et effectivement, sa rapidité est très impressionnante :

$ uv pip install -r requirements.txt
Resolved 15 packages in 245ms
Prepared 15 packages in 176ms
Installed 15 packages in 37ms
 + alembic==1.14.1
 + blinker==1.9.0
 + click==8.1.8
 + flask==3.1.0
 + flask-migrate==4.1.0
 + flask-sqlalchemy==3.1.1
 + greenlet==3.1.1
 + itsdangerous==2.2.0
 + jinja2==3.1.5
 + mako==1.3.9
 + markupsafe==3.0.2
 + psycopg2-binary==2.9.10
 + sqlalchemy==2.0.37
 + typing-extensions==4.12.2
 + werkzeug==3.1.3

uv ne propose pas seulement une amélioration de l'installation de packages Python, mais propose beaucoup d'autres choses comme :

Pour cette partie, dans un but d'unification, je continuerai à utiliser Mise pour installer une version précise de Python. De plus, Mise intègre nativement UV : https://mise.jdx.dev/mise-cookbook/python.html#mise-uv

Exemple :

$ uv run example.py

Je pense avoir compris que cela lance ce script avec les dépendances du virtual environment du projet. Un peu comme fonctionne npm, yarn ou pnpm qui permet aux scripts d'utiliser les packages présents dans ./node_modules/.

Par exemple, le linter Python ruff, exemple :

$ uv tool run ruff

J'ai un peu parcouru la documentation de pyproject.toml : https://packaging.python.org/en/latest/guides/writing-pyproject-toml/.

J'ai lu aussi la section uv - Locking environments.

Suite à ces lectures, j'ai migré le playground mise-python-flask-playground vers pyproject.toml : https://github.com/stephane-klein/mise-python-flask-playground/commit/c17216464778df4bc00bf782d5a889cb3f198051.

Je ne suis pas certain que ces commandes soient une bonne pratique :

$ uv pip compile requirements.in -o requirements.txt
$ uv pip install -r requirements.txt

Confirmation du bon fonctionnement de mon environnement de développement sous Windows, avec WSL2 qui se comporte comme Ubuntu #windows, #dev-kit

Je précise que je n'ai pas eu l'occasion de tester l'installation sous Windows, hier j'ai essayé, mais je n'ai pas réussi à installer WSL2 sous Windows dans un Virtualbox lancé sous Fedora. Je suis à la recherche d'une personne pour tester si mes instructions d'installation sont valides ou non.

source

Merci à Alexandre 🤗 qui a pris le temps de tester l'installation sous WSL2 du playground que j'ai présenté dans "Playground qui présente comment je setup un projet Python Flask en 2025".

Le playground : https://github.com/stephane-klein/mise-python-flask-playground

Après quelques petites corrections https://github.com/stephane-klein/mise-python-flask-playground/commits/main/ Alexandre a réussi avec succès à installer et lancer tous les services sous Windows 11 avec WSL2.

C'est une très bonne nouvelle 🙂.

Cela ajoute une « corde à mon arc ». Jusqu'à présent, je précisais bien que mes development kit n'étaient pas compatible MS Windows. Je le mentionnais même dans mes annonces d'embauche, pour ne pas surprendre les candidats.

Maintenant, mes environnements de développement sont compatibles Linux, MacOS, et Linux 🙂.

Je n'ai pas réussi à installer WSL2 sous Windows dans un Virtualbox lancé sous Fedora #WSL, #windows, #VirtualBox

Après avoir réussi à lancer Windows 11 sous ma Fedora dans VirtualBox (voir 2025-02-04_1646), j'ai essayé d'installer et de lancer WSL2, mais je n'ai pas réussi 🙁.

Je suis resté bloqué à cette étape :

vagrant@WIN11 C:\Users\vagrant>wsl --install
Ubuntu est déjà installé.
Lancement de Ubuntu...
Installing, this may take a few minutes...
WslRegisterDistribution failed with error: 0x80370102
Please enable the Virtual Machine Platform Windows feature and ensure virtualization is enabled in the BIOS.
For information please visit https://aka.ms/enablevirtualization
Press any key to continue...
L’opération a réussi.

Même avec ces options :

    win11.vm.provider "virtualbox" do |vb|
      vb.customize ["modifyvm", :id, "--nested-hw-virt", "on"]
      vb.customize ["modifyvm", :id, "--paravirtprovider", "kvm"]
      vb.customize ["modifyvm", :id, "--cpu-profile", "host"]
      vb.cpus = 4
      vb.memory = 16000
    end
  end

Windows ne semble pas vouloir lancer Hyper-v dans un VirtualBox.

J'ai essayé différents paramètres pour --paravirtprovider, sans succès.

Si quelqu'un a la solution, je suis preneur ! (contact@stephane-klein.info).

Playground qui présente comment je setup un projet Python Flask en 2025 #dev-kit, #python, #mise, #docker, #WSL, #playground, #software-engineering

Je pense que cela doit faire depuis 2015 que je n'ai pas développé une application en Python Flask !

Entre 2008 et 2015, j'ai beaucoup itéré dans mes méthodes d'installation et de setup de mes environnements de développement Python.

D'après mes souvenirs, si je devais dresser la liste des différentes étapes, ça donnerai ceci :

  • 2006 : aucune méthode, j'installe Python 🙂
  • 2007 : je me bats avec setuptools et distutils (mais ça va, c'était plus mature que ce que je pouvais trouver dans le monde PHP qui n'avait pas encore imaginé composer)
  • 2008 : je trouve la paie avec virtualenv
  • 2010 : j'ai peur d'écrire des scripts en Bash alors à la place, j'écris un script bootstrap.py dans lequel j'essaie d'automatiser au maximum l'installation du projet
  • 2012 : je me bats avec buildout pour essayer d'automatiser des éléments d'installation. Avec le recul, je réalise que je n'ai jamais rien compris à buildout
  • 2012 : j'utilise Vagrant pour fixer les éléments d'installation, je suis plutôt satisfait
  • 2015 : je suis radicale, j'enferme tout l'environnement de dev Python dans un container de développement, je monte un path volume pour exposer le code source du projet dans le container. Je bricole en entrypoint avec la commande "sleep".

Des choses ont changé depuis 2015.

Mais, une chose que je n'ai pas changée, c'est que je continue à suivre le modèle The Twelve-Factors App et je continue à déployer tous mes projets packagé dans des images Docker. Généralement avec un simple docker-compose.yml sur le serveur, ou alors Kubernetes pour des projets de plus grande envergure… mais cela ne m'arrive jamais en pratique, je travaille toujours sur des petits projets.

Choses qui ont changé : depuis fin 2018, j'ai décidé de ne plus utiliser Docker dans mes environnements de développement pour les projets codés en NodeJS, Golang, Python

Au départ, cela a commencé par uniquement les projets en NodeJS pour des raisons de performance.

J'ai ensuite découvert Asdf et plus récemment Mise. À partir de cela, tout est devenu plus facilement pour moi.
Avec Asdf, je n'ai plus besoin "d'enfermer" mes projets dans des containers Docker pour fixer l'environnement de développement, les versions…

Cette introduction est un peu longue, je n'ai pas abordé le sujet principal de cette note 🙂.

Je viens de publier un playground d'un exemple de projet minimaliste Python Flask suivant mes pratiques de 2025.

Voici son repository : mise-python-flask-playground

Ce playground est "propulsé" par Docker et Mise.

J'ai documenté la méthode d'installation pour :

Je précise que je n'ai pas eu l'occasion de tester l'installation sous Windows, hier j'ai essayé, mais je n'ai pas réussi à installer WSL2 sous Windows dans un Virtualbox lancé sous Fedora. Je suis à la recherche d'une personne pour tester si mes instructions d'installation sont valides ou non.

Briques technologiques présentes dans le playground :

Voici quelques petites subtilités.

Dans le fichier alembic.ini j'ai modifié le paramètre file_template parce que j'aime que les fichiers de migration soient classés par ordre chronologique :

[alembic]
# template used to generate migration files
file_template = %%(year)d%%(month).2d%%(day).2d_%%(hour).2d%%(minute).2d%%(second).2d_%%(slug)s

Ce qui donne par exemple :

20250205_124639_users.py
20250205_125437_add_user_lastname.py

Ici le port de PostgreSQL est généré dynamiquement par docker compose :

  postgres:
    image: postgres:17
	...
	ports:
      - 5432 # <= ici

Avec cela, fini les conflits de port quand je lance plusieurs projets en même temps sur ma workstation.

L'URL vers le serveur PostgreSQL est générée dynamiquement par le script get_postgres_url.sh qui est appelé par le fichier .envrc. Tout cela se passe de manière transparente.

J'initialise ici les extensions PostgreSQL :

def init_db():
    db.drop_all()
    db.session.execute(db.text('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'))
    db.session.execute(db.text('CREATE EXTENSION IF NOT EXISTS "unaccent"'))
    db.session.commit()
    db.create_all()

et ici dans la première migration :

def upgrade():
    op.execute('CREATE EXTENSION IF NOT EXISTS "uuid-ossp";')
    op.execute('CREATE EXTENSION IF NOT EXISTS "unaccent";')
    op.create_table('users',
        sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
        sa.Column('firstname', sa.String(), nullable=False),
        sa.PrimaryKeyConstraint('id')
    )

Je découvre Colima, installation minimaliste de Docker sous MacOS #docker, #dev-kit, #MacOS, #JaiDécouvert

#JaiDécouvert le projet Colima : https://github.com/abiosoft/colima

Colima - container runtimes on macOS (and Linux) with minimal setup.

Support for Intel and Apple Silicon Macs, and Linux

  • Simple CLI interface with sensible defaults
  • Automatic Port Forwarding
  • Volume mounts
  • Multiple instances
  • Support for multiple container runtimes
    • Docker (with optional Kubernetes)
    • Containerd (with optional Kubernetes)
    • Incus (containers and virtual machines)

source

Colima est une solution minimaliste qui permet d'installer sous MacOS docker-engine sans Docker Desktop.

Thread Hacker News à ce sujet de 2023 : Colima: Container runtimes on macOS (and Linux) with minimal setup.

Méthode d'installation que je suis sous MacOS avec Brew :

$ brew install colima docker docker-compose
$ cat << EOF > ~/.docker/config.json
{
    "auths": {},
    "currentContext": "colima",
    "cliPluginsExtraDirs": [
        "/opt/homebrew/lib/docker/cli-plugins"
    ]
}
EOF
$ brew services start colima

Comme indiqué ici, la modification du fichier ~/.docker/config.json permet d'activer de plugin docker compose, ce qui permet d'utiliser, par exemple :

$ docker compose ps

Qui est, depuis 2020, la méthode recommandée d'utiliser docker compose sans -.

Vérification, que tout est bien installé et lancé :

$ colima status
INFO[0000] colima is running using macOS Virtualization.Framework
INFO[0000] arch: aarch64
INFO[0000] runtime: docker
INFO[0000] mountType: sshfs
INFO[0000] socket: unix:///Users/m1/.colima/default/docker.sock
$ docker info
Client: Docker Engine - Community
 Version:    27.5.1
 Context:    colima
 Debug Mode: false

Server:
 Containers: 0
  Running: 0
  Paused: 0
  Stopped: 0
 Images: 1
 Server Version: 27.4.0
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Using metacopy: false
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Cgroup Version: 2
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
 Swarm: inactive
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 88bf19b2105c8b17560993bee28a01ddc2f97182
 runc version: v1.2.2-0-g7cb3632
 init version: de40ad0
 Security Options:
  apparmor
  seccomp
   Profile: builtin
  cgroupns
 Kernel Version: 6.8.0-50-generic
 Operating System: Ubuntu 24.04.1 LTS
 OSType: linux
 Architecture: aarch64
 CPUs: 2
 Total Memory: 1.914GiB
 Name: colima
 ID: 7fd5e4bd-6430-4724-8238-e420b3f23609
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Live Restore Enabled: false

WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled

J'ai suivi de loin l'histoire de Docker Desktop qui est devenu "propriétaire". Je viens prendre le temps d'étudier un peu le sujet, et voici ce que j'ai trouvé :

August 2021: Docker Desktop for Windows and MacOS was no longer available free of charge for enterprise users. Docker ended free Docker Desktop use for larger business customers and replaced its Free Plan with a Personal Plan. Docker on Linux distributions remained unaffected.

source

Sur le site officiel, sur la page "docker.desktop", quand je clique sur « Choose plan » je tombe sur ceci :

Je n'ai pas tout compris, j'ai l'impression qu'il est tout de même possible d'installer et d'utiliser gratuitement Docker Desktop.

Au final, tout cela n'a pas beaucoup d'importance pour moi, je ne trouve aucune utilité à Docker Desktop, par conséquent, sous MacOS j'utilise Colima.

J'ai vu qu'il est possible d'installer Colima sous Linux, mais je ne l'utilise pas, car je n'y vois aucun intérêt pour le moment.

J'ai adhéré à la Fédération nationale des micro-entrepreneurs #micro-entreprise, #freelance, #fédération

La FNAE a fait cela matin un "live" au sujet de cet amendement visant à modifier l'article 293b du Code Général des Impôts en réduisant le seuil de TVA à 25 000 € !
Bien que la page de l'amendement indique "rejeté", celui-ci semble avoir été adopté par 49-3.

Au moment où j'écris cette note, ce live n'est pas encore uploadé sur le compte YouTube de la FNAE : https://www.youtube.com/@Federation-auto-entrepreneurFr/videos.

J'ai trouvé ce live de qualité, et il m’a permis d’apprendre de nouvelles choses.

Il y a quelque jours, j'ai ouvert le thread suivant sur Pragmatic Entrepreneurs Forum : « Avis sur l’adhésion à la FNAE – Fédération Nationale des Auto-Entrepreneurs ? ».

Suite au live de matin, j'ai décidé d'adhérer à la FNAE.

Pour le moment, j'ai choisi l'offre à 12 € par an, mais je vais peut-être évoluer vers l'offre à 139 € principalement pour les soutenir.

Mardi 4 février 2025

Journal du mardi 04 février 2025 à 16:46 #windows, #VirtualBox, #WSL, #JaiDécouvert

Je souhaite créer un playground d'un development kit pour Python + PostgreSQL (via Docker) + Flask + Flask-Migrate, basé sur Mise.

J'ai la contrainte suivante : le development kit doit fonctionner sous MS Windows !

Je me dis que c'est une bonne occasion pour moi de tester Windows Subsystem for Linux 🙂.

Problème : je ne possède pas d'instance MS Windows.

#JaiDécouvert que depuis 2015, Microsoft met à disposition des ISOs officiels de MS Windows :

J'ai testé dans ce playground le lancement d'une Virtual machine MS Windows avec Vagrant : https://github.com/stephane-klein/vagrant-windows-playground.

Cela a bien fonctionné 🙂.

J'ai aussi découvert le repository windows-vagrant qui semble permettre de construire différents types d'images MS Windows avec Packer. Je n'ai pas essayé d'en construire une.

Journal du mardi 04 février 2025 à 14:57 #mémento, #mémo, #aide-mémoire, #VirtualBox, #vagrant

Petite note de type #mémento.

Si je rencontre l'erreur suivante avec Vagrant - VirtualBox :

$ vagrant up

...

Command: ["startvm", "2fa1ff70-52d8-4875-b48d-7c2df56f1507", "--type", "gui"]

Stderr: VBoxManage: error: VirtualBox can't enable the AMD-V extension. Please disable the KVM kernel extension, recompile your kernel and reboot (VERR_SVM_IN_USE)
VBoxManage: error: Details: code NS_ERROR_FAILURE (0x80004005), component ConsoleWrap, interface IConsole

La solution est la suivante :

$ sudo modprobe -r kvm_amd
$ sudo modprobe -r kvm

Journal du mardi 04 février 2025 à 11:36 #micro-entreprise, #TVA, #freelance, #JeMeDemande

Suite de ma note 2025-02-03_1718.

Suite à la réponse de l'agent des impôts qui ne m'a pas beaucoup aidé :

Bonjour,

C’est juste une attestation sur l’honneur.

En vous remerciant de votre attention. Cette demande est terminée, si vous souhaitez y répondre ou apporter des remarques ou aborder d’autres sujets, vous devez déposer une autre demande.

J'ai effectué la recherche suivante : Demande d'option pour un régime de TVA modèle de lettre

J'ai parcouru les dix premières pages de résultats sans trouver le moindre modèle de lettre sur un site officiel de l'État. C'est désolant de devoir dépendre de sites privés qui exploitent les lacunes d'un service public pour en tirer profit.

Voici un modèle de lettre que j'ai trouvé :

Je pense que c'est ce type de lettre que me demande l'agence des impôts.

Le formulaire me propose 3 options :

  • "Le régime réel simplifié d'imposition TVA"
  • "Le régime normal d'imposition TVA"
  • "Le régime alternatif du mini-réel"

D'après mes recherches, je comprends qu'il y a en réalité 4 options :

(Lien vers une version Google Spreadshet du tableau)

Critères Régime en base de TVA Régime réel simplifié de TVA Régime mini-réel de TVA Régime réel normal de TVA
Facturation de la TVA ❌ Non ✅ Oui ✅ Oui ✅ Oui
Déclarations de TVA ❌ Aucune ✅ Annuelle (CA12) + acomptes ✅ Mensuelle/trimestrielle (CA3) ✅ Mensuelle/trimestrielle (CA3)
Paiement de la TVA ❌ Non ✅ 2 acomptes + régularisation annuelle ✅ Régulier, au fil des déclarations ✅ Régulier, au fil des déclarations
Récupération de la TVA ❌ Non ✅ Oui ✅ Oui ✅ Oui
Obligations comptables 📌 Ultra simplifiée 📌 Allégée 📌 Comptabilité complète 📌 Comptabilité complète
Charge administrative ✅ Très faible ⚠️ Moyenne ❌ Plus lourde ❌ Lourde
Seuil de chiffre d’affaires -91 900 € (commerce) / 36 800 € (services) < 840 000 € (commerce) / < 254 000 € (services) < 840 000 € (commerce) / < 254 000 € (services) > 840 000 € (commerce) / > 254 000 € (services)
Public concerné Micro-entreprises, indépendants Petites entreprises Entreprises voulant mensualiser la TVA Entreprises à forte activité
Flexibilité de trésorerie ✅ Maximum ⚠️ Moins flexible ✅ Bonne gestion ❌ Contraignant

J'ai réalisé ce modèle de lettre Google Docs :

#JeMeDemande si je préfère choisir l'option "Régime réel simplifié d'imposition à la TVA" ou "Régime mini-réel d'imposition à la TVA".

Le "Régime réel simplifié d'imposition à la TVA" fonctionne avec un système d'acomptes semestriels qui est calculé à partir de la TVA collectée par l'entreprise durant l'année précédente. #JeMeDemande comment le montant de cet acompte est calculé la première année.

À ce jour, j'ignore encore si je vais continuer ou non une activité de Freelance sur le long terme.
Avec le système d'acompte, j'ai peur de devoir continuer à payer des acomptes pendant un an sur un chiffre d'affaires qui sera nul et de voir faire des démarches pour me faire rembourser 🤔.

Pour le moment, je n'ai pas encore trouvé réponse à ces deux questions :

  • c. Pouvez-vous me confirmer que ma déclaration devrait se faire sur la page web indiquée dans le screenshot en pièce jointe ?
  • d. Est-ce que la déclaration doit s'effectuer en fonction de la date d'émission de la facture ou alors sa date d'encaissement ?

Lundi 3 février 2025

Journal du lundi 03 février 2025 à 17:54 #micro-entreprise, #freelance, #FNAE

Je viens de publier le thread suivant sur Pragmatic Entrepreneurs Forum : https://forum.pragmaticentrepreneurs.com/t/avis-sur-l-adhesion-a-la-fnae-federation-nationale-des-auto-entrepreneurs/23607


Titre : Avis sur l’adhésion à la FNAE – Fédération Nationale des Auto-Entrepreneurs ?

Bonjour,

Y a-t-il des membres de ce forum qui sont adhérents à la FNAE (https://fnae.fr - Fédération Nationale des Auto-Entrepreneurs) ?

Si c'est votre cas, quels sont vos retours ?

  • Trouvez-vous l’adhésion utile ?
  • Vous sentez-vous bien représenté ?
  • Êtes-vous en accord avec la ligne politique de cette fédération ?
  • Les dirigeants sont-ils accessibles ?
  • Connaissez-vous des alternatives ?

Si vous ne souhaitez pas répondre publiquement, vous pouvez me contacter à contact@stephane-klein.info.

Merci d’avance pour vos retours !

Journal du lundi 03 février 2025 à 17:18 #micro-entreprise, #TVA

Je viens de publier le thread suivant sur Pragmatic Entrepreneurs Forum : https://forum.pragmaticentrepreneurs.com/t/micro-entreprise-a-quel-moment-je-dois-declarer-la-tva-est-ce-que-la-declaration-doit-seffectuer-en-fonction-de-la-date-demission-de-la-facture-ou-alors-de-sa-date-dencaissement/23606

Mon premier message :

Micro-entreprise, à quel moment je dois déclarer la TVA ? Est-ce que la déclaration doit s'effectuer en fonction de la date d'émission de la facture ou alors de sa date d'encaissement ?

Bonjour,

Le 28 janvier, j'ai envoyé le message suivant à mon "Service impôts des entreprises".

Je partage ici, "afin de documenter" le processus au fur et à mesure des réponses que je reçois.

Bonjour,

J'ai commencé mon activité en micro-entreprise au 1ᵉʳ juillet 2024.

À ce jour, voici ce que j'ai facturé :

  • 4 juillet 2024, 10 000 € net sans TVA (encaissé)
  • 6 septembre 2024, 10 000 € net sans TVA (encaissé)
  • 13 novembre 2024, 10 000 € net sans TVA (encaissé le 29 novembre 2024)
  • 20 décembre 2024, 10 000 € TTC, dont 2 000 € de TVA (encaissé le 20 janvier 2025)

J'ai lu que la TVA est applicable si je dépasse 34 400 € en prestations de services.

Dans le doute, j'ai commencé à appliquer la TVA à partir de ma 4ᵉ facture.

Questions :

  • a.Est-ce que je suis dans les "règles" en ayant appliqué la TVA à ma 4ᵉ facture ?
  • b. À quel moment je vais devoir déclarer les 2000 € de TVA de ma 4ᵉ facture ?
  • c. Pouvez-vous me confirmer que ma déclaration devrait se faire sur la page web indiquée dans le screenshot en pièce jointe ?
  • d. Est-ce que la déclaration doit s'effectuer en fonction de la date d'émission de la facture ou alors sa date d'encaissement ?

Cordialement, Stéphane Klein Tel: xx xx xx xx xx


Mon second message :

Le 3 février, j'ai reçu la réponse suivante :

Bonjour,

Je vous remercie d'envoyer une lettre d'option à la TVA datée et signée en spécifiant le régime demandé.

En vous remerciant de votre attention.

Mes commentaires au sujet de cette réponse :

  • Je trouve cela dommage que l'agent n'ait répondu à aucune de mes 4 questions 😔
  • Je trouve dommage que l'agent ne m'ai pas donné de lien vers
    • une documentation au sujet de cette lettre d'option à la TVA
    • un modèle de lettre ;
  • Je trouve cela dommage que je doive rédiger une lettre plutôt que déclarer des champs dans un formulaire. Je pense que la méthode basée sur une lettre augmente la charge de l'administration, est source d'erreur, est difficilement automatisable…

Quelques remarques sur cette réponse :

  • L’agent n’a répondu à aucune de mes quatre questions 😔.
  • Aucune ressource n’est fournie pour mieux comprendre cette demande :
    • Pas de lien vers une documentation expliquant cette lettre d’option à la TVA.
  • Pas de modèle de lettre proposé.
  • Il est regrettable que cette démarche passe par une lettre plutôt qu’un formulaire dédié. Je pense qu'un formulaire éviterait des erreurs, réduirait la charge administrative et faciliterait l’automatisation.

Je me demande comment je pourrais faire remonter ces retours à l’administration. Ces améliorations sont peut-être déjà prévues dans leur feuille de route, ou bien l’administration manque tout simplement de moyens pour les mettre en place 🤔.

Prochaine étape : essayer de trouver un modèle de lettre d'option à la TVA.

Jeudi 30 janvier 2025

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

Mercredi 29 janvier 2025

Journal du mercredi 29 janvier 2025 à 22:22 #JaiDécouvert, #network, #dns

En étudiant Pi-hole, je découvre le terme "DNS sinkhole" :

A DNS sinkhole, also known as a sinkhole server, Internet sinkhole, or Blackhole DNS is a Domain Name System (DNS) server that has been configured to hand out non-routable addresses for a certain set of domain names.

...

Another use is to block ad serving sites, either using a host's file-based sinkhole or by locally running a DNS server (e.g., using a Pi-hole). Local DNS servers effectively block ads for all devices on the network.

source

Journal du mercredi 29 janvier 2025 à 16:29 #OnMaPartagé, #JaiDécouvert, #gitlab, #markdown

Alexandre m'a fait remarquer que GitLab a activé par défaut une extension Markdown de génération automatique de TOC :

A table of contents is an unordered list that links to subheadings in the document. You can add a table of contents to issues, merge requests, and epics, but you can’t add one to notes or comments.

Add one of these tags on their own line to the description field of any of the supported content types:

[[_TOC_]]
or
[TOC]
  • Markdown files.
  • Wiki pages.
  • Issues.
  • Merge requests.
  • Epics.

source

Je trouve cela excellent que cette extension Markdown soit supportée un peu partout, en particulier les issues, Merge Request… 👍️.

Cette fonctionnalité a été ajoutée en mars 2020 🫢 ! Comment j'ai pu passer à côté ?

GitHub permet d'afficher un TOC au niveau des README, mais je viens de vérifier, GitHub ne semble pas supporter cette extension TOC Markdown au niveau des issues… Pull Request

Journal du mercredi 29 janvier 2025 à 11:55 #serveur-http, #Nuxt, #javascript, #JaiDécouvert, #JeMeDemande

#JaiDécouvert Nitro (https://nitro.build/)

Next Generation Server Toolkit.

Create web servers with everything you need and deploy them wherever you prefer.

source

D'après ce que j'ai compris, Nitro est un serveur http en NodeJS qui a été spécialement conçu pour Nuxt.js.

Nitro a été introduit fin 2021 dans la version 3 de Nuxt.

Nitro fait partie de l'écosystème UnJS.

Je découvre l'existence de UnJS Ecosystem. #JeMeDemande si ce projet a un lien avec VoidZero 🤔.

Je viens de vérifier, SvelteKit ne semble pas utiliser Nitro. Nitro semble être utilisé uniquement par Nuxt.js.

Mardi 28 janvier 2025

Journal du mardi 28 janvier 2025 à 15:26 #neovim, #CodeAssistant

D'ici quelques jours, je prévois de rédiger un bilan d'utilisation de avante.nvim pour faire le point sur mon expérience avec cet outil.

source

Après 16 jours d'utilisation d'avante.nvim et comme je le disais dans cette note mon retour est positif, mis à part le bug suivant : bug: non-streaming output never shows.

Pour le moment, je n'arrive pas à comprendre dans quelle condition ce bug arrive 🤔.

Je n'ai pas encore pris le temps de creuser le sujet. J'espère que l'issue va rapidement être résolue. 🤞.

Journal du mardi 28 janvier 2025 à 13:49 #software-engineering, #Doctrine

Alexandre me dit : « Le contenu de Speed of Code Reviews (https://google.github.io/eng-practices/review/reviewer/speed.html) ressemble à ce dont tu faisais la promotion dans notre précédente équipe ».

En effet, après lecture, les recommandations de cette documentation font partie de ma doctrine d'artisan développeur.

Note: j'ai remplacé CL qui signifie Changelist par Merge Request.

When code reviews are slow, several things happen:

  • The velocity of the team as a whole is decreased. Yes, the individual who doesn’t respond quickly to the review gets other work done. However, new features and bug fixes for the rest of the team are delayed by days, weeks, or months as each Merge Request waits for review and re-review.
  • Developers start to protest the code review process. If a reviewer only responds every few days, but requests major changes to the Merge Request each time, that can be frustrating and difficult for developers. Often, this is expressed as complaints about how “strict” the reviewer is being. If the reviewer requests the same substantial changes (changes which really do improve code health), but responds quickly every time the developer makes an update, the complaints tend to disappear. Most complaints about the code review process are actually resolved by making the process faster.
  • Code health can be impacted. When reviews are slow, there is increased pressure to allow developers to submit Merge Request that are not as good as they could be. Slow reviews also discourage code cleanups, refactorings, and further improvements to existing Merge Request.

source

J'ai fait le même constat et je trouve que cette section explique très bien les conséquences 👍️.

How Fast Should Code Reviews Be?

If you are not in the middle of a focused task, you should do a code review shortly after it comes in.

One business day is the maximum time it should take to respond to a code review request (i.e., first thing the next morning).

Following these guidelines means that a typical Merge Request should get multiple rounds of review (if needed) within a single day.

source

Je partage et recommande cette pratique 👍️.

If you are too busy to do a full review on a Merge Request when it comes in, you can still send a quick response that lets the developer know when you will get to it, suggest other reviewers who might be able to respond more quickly.

source

👍️

Large Merge Request

If somebody sends you a code review that is so large you’re not sure when you will be able to have time to review it, your typical response should be to ask the developer to split the Merge Request into several smaller Merge Requests that build on each other, instead of one huge Merge Request that has to be reviewed all at once. This is usually possible and very helpful to reviewers, even if it takes additional work from the developer.

source

Je partage très fortement cette recommandation et je pense que c'est celle que j'avais le plus de difficulté à faire accepter par les nouveaux développeurs.

Quand je code, j'essaie de garder à l'esprit que mon objectif est de faciliter au maximum le travail du reviewer plutôt que de chercher à minimiser mes propres efforts.

J'ai sans doute acquis cet état d'esprit du monde open source. En effet, l'un des principaux défis lors d'une contribution à un projet open source est de faire accepter son patch par le mainteneur. On comprend rapidement qu'un patch doit être simple à comprendre et rapide à intégrer pour maximiser ses chances d'acceptation.

Un bon patch doit remplir un objectif unique et ne contenir que les modifications strictement nécessaires pour l'atteindre.

Je suis convaincu que si une équipe de développeurs applique ces principes issus de l'open source dans leur contexte professionnel, leur efficacité collective s'en trouvera grandement améliorée.

Par ailleurs, une Merge Request de taille réduite présente plusieurs avantages concrets :

  • elle est non seulement plus simple à rebase,
  • mais elle a aussi plus de chances d'être mergée rapidement.

Cela permet à l'équipe de bénéficier plus rapidement des améliorations apportées, qu'il s'agisse de corrections de bugs ou de nouvelles fonctionnalités.

Lundi 27 janvier 2025

Journal du lundi 27 janvier 2025 à 11:49 #JaiDécouvert, #neovim

En lisant la documentation de lazy.nvim, #JaiDécouvert que la bonne pratique n'est pas celle-ci :

{
  "folke/todo-comments.nvim",
  config = function()
    require("todo-comments").setup({})
  end,
},

Mais simplement celle-ci :

{ "folke/todo-comments.nvim", opts = {} },

Journal du lundi 27 janvier 2025 à 10:51 #neovim, #dotfiles, #JaiDécidé

Il y a plus de deux ans, j'avais écrit la note suivante :

Pour effectuer une rotation du layout de mes windows Neovim comme la commande ctrl-b space sous tmux, j'ai trouvé les combinaisons de raccourcis suivantes :

  • ctrl-w J
  • ctrl-w H

Attention, les majuscules sont importantes.

source

En plus de les oublier régulièrement, je trouve ces deux commandes pas pratiques.

Je dois utiliser ctrl-w H pour transformer des windows horizontaux en verticaux et ctrl-w J pour transformer des windows verticaux en horizontaux. Cela fait deux raccourcis alors que j'aimerais en utiliser un seul.

Ce matin, #JaiDécidé d'essayer d'utiliser winshift.nvim (https://github.com/sindrets/winshift.nvim).

Voici mon commit de changement de mon dotfiles : https://github.com/stephane-klein/dotfiles/commit/5d6f798538ac16ab8a308d4da26913306b0cff82

J'ai testé un peu winshift.nvim. C'est parfait 🙂, ce plugin correspond parfaitement à mes besoins et il est très facile à utiliser !

Jeudi 23 janvier 2025

Journal du jeudi 23 janvier 2025 à 14:37 #Inference, #artificial-intelligence, #JaiDécouvert

#JaiDécouvert Moshi (https://github.com/kyutai-labs/moshi).

Moshi is a speech-text foundation model and full-duplex spoken dialogue framework. It uses Mimi, a state-of-the-art streaming neural audio codec.

Moshi models two streams of audio: one corresponds to Moshi, and the other one to the user. At inference, the stream from the user is taken from the audio input, and the one for Moshi is sampled from the model's output. Along these two audio streams, Moshi predicts text tokens corresponding to its own speech, its inner monologue, which greatly improves the quality of its generation.

source

Mardi 21 janvier 2025

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) <<--

Lundi 20 janvier 2025

Journal du lundi 20 janvier 2025 à 22:28 #mastodon, #archive

J'ai récupéré l'archive de mes posts, je vais sans doute les publiers avec l'un des outils listés dans Awesome Mastodon - Archiving.

source

Voilà, je viens de publier mon archive de mon ancien compte Mastodon : http://archives.sklein.xyz/mamot.fr/.

Pour réaliser cette archive, j'ai utilisé Posty (https://posty.1sland.social/).

Journal du lundi 20 janvier 2025 à 10:28 #OnMaPartagé, #association, #coop, #JaiDécouvert

Un ami vient de me partager le site web de l'association L'Échappée Belle : https://lechappeebelle.team/.

En lisant les pages suivantes :

Je pense avoir compris que les membres de cette association l'utilisent pour faire une sorte de portage salarial.

Cela permet à des indépendants d'être salariés, de mutualiser les frais comptables, bancaires…

Je ne savais pas qu'une association loi 1901 pouvait être utilisée pour un fonctionnement proche du portage salarial. C’est une solution astucieuse pour mutualiser les frais tout en offrant une flexibilité structurelle.

J'aime beaucoup leur pratique, par exemple, la forme de leur "Journal de Décisions", la rédaction de la page "Menace juridique".

CAE, SCOP ou asso ?

CAE ça a l’air plus relou que SCOP alors qu’en SCOP à priori on peut avoir ce qu’on veut en ayant tou.te.es le statut de gérant non salarié mais rémunéré. Mais on est théoriquement en attente d’une ultime réponse de l’avocat sur la SCOP. Du coup, on (David et Sabine) n’a pas envie d’attendre cette réponse pour passer sous un statut salarié ou assimilé, donc on choisit de créer une association au moins pour commencer, parce que c’est théoriquement facile, rapide et non coûteux à créer. On verra si on la transforme plus tard en SCOP ou si on reste sous le statut d’association.

source

Après étude de CAE versus SCOP, il me semble qu’une CAE conviendrait mieux à leur projet.
Je pense qu'une SCOP est idéale pour des structures qui exploitent des outils de production, comme des boulangeries ou des usines.

Je trouve ce projet d’association inspirant, et je tiens à féliciter les fondateurs! 👏

Dimanche 19 janvier 2025

Appliquer une configuration nftables avec un rollback automatique de sécurité #astuce, #mémo, #mémento, #aide-mémoire, #network, #nftables, #firewall

Voici une astuce pour appliquer une configuration nftables en toute sécurité, pour éviter tout risque d'être "enfermé dehors".

Je commence par m'assurer que le fichier de configuration ne contient pas d'erreur de syntaxe ou autre :

# nft -c -f /etc/nftables.conf

Sauvegarder la configuration actuelle :

# nft list ruleset > /root/nftables-backup.conf

Application de la configuration avec un rollback automatique exécuté après 200 secondes :

# (sleep 200 && nft -f /root/nftables-backup.conf) & sudo nft -f /etc/nftables.conf

Après cette commande, j'ai 200 secondes pour tester si j'ai toujours bien accès au serveur.

Si tout fonctionne bien, alors je peux exécuter la commande suivante pour désactiver le rollback :

# pkill -f "sleep 200"

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

Vendredi 17 janvier 2025

Journal du vendredi 17 janvier 2025 à 19:02 #neovim, #CodeAssistant

D'ici quelques jours, je prévois de rédiger un bilan d'utilisation de avante.nvim pour faire le point sur mon expérience avec cet outil.

source

Après 5 jours d'utilisation, mon retour est positif. Je trouve avante.nvim très agréable à utiliser et GitHub Copilot avec Claude Sonnet 3.5 m'assiste efficacement 🙂.

Pour le moment, le seul reproche que je peux faire à avante.nvim, c'est que je ne peux pas utiliser Neovim (me balader dans le code, éditer un fichier) pendant qu'une réponse est en train d'être rédigée dans la sidebar.

J'ai trouvé cette issue qui semble correspondre à ce problème : feature: Cursor Movement Issue During Chat Response Generation.

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.

Mercredi 15 janvier 2025

Journal du mercredi 15 janvier 2025 à 14:55 #IndieHacker, #JaiDécouvert

#JaiDécouvert que le site Scaleway Feature Requests est propulsé par Fiber (https://fider.io/), développé principalement pour Guilherme Oenning, un irelandais, qui se qualifie de « Solopreneur building in public ». Il a aussi créé aptabase et SEO Gets.

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.

Pas de notes plus récentes | [ Notes plus anciennes (802) >> ]