php 8.3 fireworks

Installer PHP 8.3 sur le serveur

Tous les sites et services hébergés sur Apollo tournent actuellement sous PHP 8.2 mais PHP 8.3 vient de pointer le bout de son nez donc nous allons l’installer sur le serveur.

Nouveautés et changements de PHP 8.3

PHP 8.3 marque une avancée notable dans l’univers de PHP, apportant avec elle une panoplie de nouveautés et d’améliorations captivantes. Au cœur de PHP 8.3, on découvre des innovations passionnantes.

Des constantes de classe typées : avec PHP 8.3, place à la précision ! Les constantes de classe se dotent d’un typage explicite, renforçant ainsi la rigueur dans l’application des types et affinant la détection des erreurs.

Récupération dynamique des constantes de classe : simplification à l’horizon ! Cette nouveauté rend la récupération des constantes de classe plus intuitive, rendant ainsi votre code plus clair et plus facile à entretenir.

Le flamboyant attribut #[\Override] : tel un gardien, cet attribut veille à ce qu’une méthode portant le même nom soit présente dans une classe parente ou une interface, boostant la fiabilité de votre code et facilitant son évolution.

Clonage en profondeur des propriétés en lecture seule : PHP 8.3 innove en permettant la modification unique des propriétés en lecture seule dans la méthode __clone, ouvrant la voie à un clonage plus profond.

La fonction innovante json_validate() : vérifier la validité d’un JSON devient un jeu d’enfant avec cette nouvelle fonction, plus efficace et rapide.

La méthode inédite Randomizer::getBytesFromString() : prolongeant l’extension Random de PHP 8.2, cette méthode est un véritable couteau suisse pour générer des chaînes aléatoires spécifiques, idéale pour créer des identifiants uniques comme des noms de domaine.

Un éventail de nouvelles méthodes et fonctions : PHP 8.3 n’est pas avare en nouveautés et offre un large éventail de méthodes et fonctions inédites, telles que DOMElement::getAttributeNames(), IntlCalendar::setDate(), et ldap_connect_wallet(), pour n’en nommer que quelques-unes.

Avec PHP 8.3, non seulement vous bénéficiez d’améliorations de performances et de corrections de bugs, mais aussi d’une expérience de développement web plus fluide et efficace.

PHP 8.3 est-il plus rapide?

Alors on peut se demander si cette nouvelle mouture est bien plus rapide que les versions précédentes. Rien de tel qu’un petit test avec WordPress et WooCommerce.

Sous WordPress 6.4, le site sert:

  • 153 requêtes/s avec PHP 8.2
  • 165 requêtes/s avec PHP 8.3

Sous WooCommerce 8.0, on obtient:

  • 49 requêtes/s avec PHP 8.2
  • 58 requêtes/s avec PHP 8.3

Donc oui, PHP 8.3 est plus rapide que PHP 8.2 et les versions précédentes.

Lire la suite

A robotic hand is typing on a laptop keyboard to extract content.

Comment empêcher les chatbots IA d’extraire le contenu de votre site

Vous êtes inquiet à l’idée que des chatbots basés sur l’intelligence artificielle puissent aspirer le contenu de votre site Web ? Heureusement, il existe des moyens de les en empêcher. Voici comment procéder.

Comment les chatbots IA accèdent-ils au contenu de votre site ?

Les chatbots alimentés par intelligence artificielle sont formés à l’aide de multiples jeux de données, dont certains sont en libre accès et disponibles au grand public. Selon un article de recherche publié par OpenAI, GPT-3 a été formé à partir de cinq jeux de données distincts :

  • Common Crawl (contribution de 60% à l’entraînement)
  • WebText2 (contribution de 22% à l’entraînement)
  • Books1 (contribution de 8% à l’entraînement)
  • Books2 (contribution de 8% à l’entraînement)
  • Wikipédia (contribution de 3% à l’entraînement)

Common Crawl englobe des pétaoctets de données issues de sites Web collectées depuis 2008, un peu à la manière dont l’algorithme de recherche de Google scrute les contenus en ligne. WebText2 est un jeu de données créé par OpenAI, qui contient environ 45 millions de pages Web liées depuis des publications Reddit ayant reçu au moins trois votes positifs.

Il est donc important de noter que, dans le cas de ChatGPT, le chatbot n’accède ni n’explore directement les pages de votre site Web — du moins, pas pour l’instant. Néanmoins, l’annonce récente d’un navigateur Web hébergé par ChatGPT a suscité des inquiétudes sur une éventuelle évolution de cette situation.

À l’heure actuelle, les propriétaires de sites Web devraient surveiller d’autres chatbots IA qui entrent sur le marché. Bard est un autre acteur majeur dans ce domaine, mais on en sait peu sur les jeux de données utilisés pour son entraînement. Bien sûr, nous savons que les robots d’exploration de Google parcourent constamment les pages Web, mais cela ne signifie pas nécessairement que Bard a accès aux mêmes données.

Pourquoi certains propriétaires de sites Web sont-ils inquiets ?

La principale inquiétude des propriétaires de sites Web est la dévalorisation de leur contenu par des bots IA tels que ChatGPT, Bard et Bing Chat. Ces bots IA utilisent le contenu existant pour générer des réponses, réduisant ainsi le besoin pour les utilisateurs d’accéder à la source originale. Au lieu de visiter des sites Web pour obtenir des informations, les utilisateurs peuvent simplement demander à Google ou à Bing de générer un résumé des informations dont ils ont besoin.

Dans le contexte des chatbots IA intégrés aux moteurs de recherche, la perte de trafic est la préoccupation majeure des propriétaires de sites Web. Par exemple, Bard, le chatbot IA, inclut rarement des citations dans ses réponses génératives, ce qui indique aux utilisateurs de quelles pages il tire ses informations.

Ainsi, en plus de remplacer les visites sur les sites Web par des réponses générées par IA, Bard supprime presque toute possibilité pour le site source de recevoir du trafic — même si l’utilisateur souhaite obtenir des informations supplémentaires. À l’opposé, Bing Chat lie plus fréquemment aux sources d’information.

En résumé, la génération actuelle d’outils IA génératifs utilise le travail des créateurs de contenu pour systématiquement supplanter le besoin de ces créateurs. Cela soulève la question de l’incitatif pour les propriétaires de sites Web à continuer à publier du contenu. Et, par extension, que se passera-t-il pour les chatbots IA si les sites Web cessent de publier le contenu dont ils dépendent pour fonctionner ?

Lire la suite

A woman using a cell phone while sitting at a table with a cup of coffee and reading an eBook on her tablette.

ePub, comment lire un eBook (tablette et PC) ?

Découvrez la simplicité de la lecture numérique avec ePub

ePub est devenu le format de prédilection pour les adeptes du livre numérique, et pour cause : sa facilité d’utilisation est inégalée. Cet article vous guide à travers le processus en deux étapes faciles : télécharger un lecteur d’eBook et plonger dans votre lecture.

Qu’est-ce que le format ePub ?

ePub, l’acronyme de ‘electronic publication’, est largement reconnu comme le standard universel pour les livres numériques. Sa conception ouverte et adaptable garantit que le texte s’affiche parfaitement sur tous vos appareils, qu’il s’agisse d’ordinateurs, de tablettes ou de smartphones.

Vos premiers pas avec les lecteurs d’eBooks

La première étape consiste à choisir et installer un lecteur d’eBooks. Cette étape franchie, vous pourrez accéder à votre bibliothèque numérique à tout moment, sans aucune manipulation supplémentaire.

Comment lire un ePub sur Smartphone ou Tablette ?

Pour les utilisateurs d’iOS (iPad ou iPhone) :

Transférez vos fichiers ePub sur votre appareil via iTunes, et retrouvez-les facilement sur l’application iBooks, préinstallée et prête à l’emploi.

Pour les utilisateurs d’Android :

Aldiko est une excellente option, mais libre à vous d’explorer et de choisir l’application qui vous convient le mieux.

Lire un ePub sur un ordinateur

Sur Mac et Windows :

Adobe Digital Editions (ADE) est le choix privilégié pour les nouveaux venus dans le monde de la lecture numérique sur ordinateur. C’est une solution fiable qui promet une expérience de lecture fluide et intuitive. Voici comment démarrer avec ADE :

  1. Téléchargez et installez le logiciel sur votre ordinateur, qu’il soit sous Windows ou macOS.
  2. Une fois l’installation terminée, ouvrez ADE et ajoutez vos livres au format ePub à la bibliothèque.
  3. Double-cliquez sur un livre pour commencer à lire.

Sur Mac, Windows et Linux :

Calibre est bien plus qu’un simple lecteur d’eBooks : c’est une véritable bibliothèque numérique pour votre ordinateur, compatible avec Windows, macOS et Linux. C’est un outil complet qui permet non seulement de lire, mais aussi de gérer et d’organiser votre collection d’eBooks. Suivez ces instructions pour utiliser Calibre :

  1. Téléchargez et Installez Calibre et lancez-le.
  2. Importez vos eBooks au format ePub dans Calibre en cliquant sur “Ajouter des livres”.
  3. Sélectionnez un livre et cliquez sur “Lire” pour ouvrir le livre et profiter de votre lecture.

Lire la suite

An image of a computer screen with a green and purple background displaying the Message Of The Day (MOTD) on Ubuntu Server.

Changer le Message Of The Day (MOTD) sous Ubuntu Server

Le Message Of The Day (communément appelé MOTD) est un message d’accueil ou des notifications importantes que les administrateurs de serveurs peuvent configurer pour s’afficher lorsqu’un utilisateur se connecte au serveur via SSH.

Ce tutoriel vous montre comment mettre à jour et personnaliser le MOTD sur un serveur Ubuntu 22.04.

Prérequis

  • Serveur Ubuntu 22.04 avec accès root ou un utilisateur avec des privilèges sudo.
  • Accès SSH au serveur.

Étape 1 : connexion au Serveur

Connectez-vous à votre serveur Ubuntu via SSH:

ssh USER@SERVER_IP -pPORTCode language: CSS (css)

Étape 2 : vérifier le MOTD existant

Pour voir le MOTD actuel, utilisez la commande suivante :

cat /run/motd.dynamic

Étape 3 : modifier le MOTD dynamique

Ubuntu 22.04 utilise des scripts dans le répertoire /etc/update-motd.d/ pour générer une partie du MOTD dynamiquement. Vous pouvez ajouter, supprimer ou modifier les scripts dans ce répertoire pour personnaliser davantage le MOTD.

Entête MOTD

Nous allons créer un entête qui contient le nom de notre serveur en ASCII art, ce qui nous permettra de bien l’identifier lors de nos sessions SSH:

sudo nano /etc/update-motd.d/00-custom

Et on y ajoute notre script bash:

#!/bin/sh

# Reset
Color_Off='\033[0m'       # Text Reset

# Bold
BBlack='\033[1;30m'       # Black
BRed='\033[1;31m'         # Red
BGreen='\033[1;32m'       # Green
BYellow='\033[1;33m'      # Yellow
BBlue='\033[1;34m'        # Blue
BPurple='\033[1;35m'      # Purple / Magenta
BCyan='\033[1;36m'        # Cyan
BWhite='\033[1;37m'       # White

# Set the text color to green
printf "$BGreen"

# https://patorjk.com/software/taag/#p=display&f=Doom&t=APOLLO%20.%20SKYMINDS%20.%20NET
cat << "EOF"
  ___  ______ _____ _      _     _____       _____ _   ____   ____  ________ _   _______  _____       _   _  _____ _____
 / _ \ | ___ \  _  | |    | |   |  _  |     /  ___| | / /\ \ / /  \/  |_   _| \ | |  _  \/  ___|     | \ | ||  ___|_   _|
/ /_\ \| |_/ / | | | |    | |   | | | |     \ `--.| |/ /  \ V /| .  . | | | |  \| | | | |\ `--.      |  \| || |__   | |
|  _  ||  __/| | | | |    | |   | | | |      `--. \    \   \ / | |\/| | | | | . ` | | | | `--. \     | . ` ||  __|  | |
| | | || |   \ \_/ / |____| |___\ \_/ /  _  /\__/ / |\  \  | | | |  | |_| |_| |\  | |/ / /\__/ /  _  | |\  || |___  | |
\_| |_/\_|    \___/\_____/\_____/\___/  (_) \____/\_| \_/  \_/ \_|  |_/\___/\_| \_/___/  \____/  (_) \_| \_/\____/  \_/
EOF

# Set the text color to magenta
printf "$BPurple"

# Left image lines
left1="                                   88 88  "
left2="                                   88 88  "
left3="                                   88 88  "
left4=",adPPYYba, 8b,dPPYba,   ,adPPYba,  88 88  ,adPPYba,"
left5="\"\"     \`Y8 88P'    \"8a a8\"     \"8a 88 88 a8\"     \"8a"
left6=",adPPPPP88 88       d8 8b       d8 88 88 8b       d8"
left7="88,    ,88 88b,   ,a8\" \"8a,   ,a8\" 88 88 \"8a,   ,a8\""
left8="\"8bbdP\"Y8 88\`YbbdP\"'   \"\"YbbdP\"'  88 88  \"\"YbbdP\"'"
left9="           88                                          "
left10="           88                                         "

# Right image lines
right1=" .              +   .                .   . .     .  ."
right2="                   .                    .       .     *"
right3="  .       *                        . . . .  .   .  + ."
right4="            \"You Are Here\"            .   .  +  . . ."
right5=".                 |             .  .   .    .    . ."
right6="                  |           .     .     . +.    +  ."
right7="                 \\|/            .       .   . ."
right8="         . .        V          .    * . . .  .  +   ."
right9="               +      .           .   .      +"
right10="                               .       . +  .+. ."

# Combine and print
echo "$left1 $right1"
echo "$left2 $right2"
echo "$left3 $right3"
echo "$left4 $right4"
echo "$left5 $right5"
echo "$left6 $right6"
echo "$left7 $right7"
echo "$left8 $right8"
echo "$left9 $right9"
echo "$left10 $right10"

# STATS
upSeconds="$(/usr/bin/cut -d. -f1 /proc/uptime)"
secs=$((${upSeconds}%60))
mins=$((${upSeconds}/60%60))
hours=$((${upSeconds}/3600%24))
days=$((${upSeconds}/86400))
UPTIME=$(printf "%d days, %02dh%02dm%02ds" "$days" "$hours" "$mins" "$secs")

# get the load averages
read one five fifteen rest < /proc/loadavg

# text in yellow
printf "$BYellow"

echo "
   .~~.   .~~.    `date +"%A, %e %B %Y, %r"`
  '. \ ' ' / .'   `uname -srmo`
   .~ .~~~..~.
  : .~.'~'.~. :   Uptime.............: ${UPTIME}
 ~ (   ) (   ) ~  Memory.............: `cat /proc/meminfo | grep MemFree | awk {'print $2'}`kB (Free) / `cat /proc/meminfo | grep MemTotal | awk {'print $2'}`kB (Total)
( : '~'.~.'~' : ) Load Averages......: ${one}, ${five}, ${fifteen} (1, 5, 15 min)
 ~ .~ (   ) ~. ~  Running Processes..: `ps ax | wc -l | tr -d " "`
  (  : '~' :  )   IP Addresses.......: `ip a | grep glo | awk '{print $2}' | head -1 | cut -f1 -d/` and `wget -q -O - https://icanhazip.com/ | tail`
   '~ .~~~. ~'    Weather............: `curl -s "https://rss.accuweather.com/rss/liveweather_rss.asp?metric=1&locCode=EUR|FR|FR|NANTES|" | sed -n '/Currently:/ s/.*: \(.*\): \([0-9]*\)\([CF]\).*/\2°\3, \1/p'`
       '~'
"

# text default colour
printf "$Color_Off"Code language: PHP (php)

Cela affiche le nom du serveur (apollo.skyminds.net) puis une petite frise, et enfin les statistiques ainsi que la météo pour Nantes. Vous pouvez modifier la ville dans l’avant-dernière ligne.

Notez que j’ai compilé deux images pour qu’elles s’affichent sur les mêmes lignes: le mot “apollo” en ASCII art et une version de l’univers avec le texte “You are here!”. Il faut jouer avec l’indentation mais au bout de quelques essais, cela rend plutôt pas mal je trouve.

Rendez le script exécutable :

sudo chmod +x  /etc/update-motd.d/00-custom

Lancez la compilation de tous les modules dynamiques pour obtenir le MOTD final qui sera affiché à chaque connexion SSH:

run-parts /etc/update-motd.d/ > /run/motd.dynamicCode language: JavaScript (javascript)

Lire la suite

viewport overflow

Vanilla JS : identifier l’élément plus large que le viewport

Aujourd’hui, je régle un petit souci d’affichage sur un une boutique WooCommerce propulsée par Astra.

Le problème est le suivant: lorsqu’on visite le site depuis un mobile, il est possible de faire bouger le contenu de la page vers la gauche, ce qui dévoile une hideuse colonne grise/verte sur le côté droit. Pourtant, aucun élément ne semble dépasser la largeur du contenu.

L’identification de l’élément causant ce défaut d’affichage n’est pas vraiment une chose aisée, car visuellement, on ne distingue rien qui dépasse.

Vanilla JS à la rescousse

Voici un petit script en Vanilla JS qui permet de trouver l’élément qui dépasse la largeur du viewport de votre page:

/**
 * Identify elements that exceed the viewport width
 * by Matt Biscay
 * https://www.skyminds.net/?p=614036
 */
document.addEventListener("DOMContentLoaded", function() {
  const elements = document.querySelectorAll('body *');
  elements.forEach(element => {
    if (element.offsetWidth > document.documentElement.clientWidth) {
      console.log('Element causing overflow: ', element);
    }
  });
});Code language: JavaScript (javascript)

CSS: couvrez cet overflow que je ne saurais voir

Une fois votre ou vos éléments identifiés, il ne vous reste plus qu’à cacher son overflow-x :

#element {
  overflow-x: hidden;
}Code language: CSS (css)

Rafraîchissez la page. Hop, problème résolu.

Update MySQL client and server versions using apt config.

Mettre à jour MySQL client, server et apt-config

Un an après mon article sur l’erreur APT : the following packages have been kept back, voici que cela recommence : impossible d’installer les mises à jour de mysql-client et mysql-server parce que mysql-apt-config n’est plus à jour.

J’ai tendance à oublier la solution parce que je n’y suis confronté que depuis peu donc j’ai écris un petit script bash que je pourrai lancer la prochaine fois, histoire de gagner du temps.

Pré-requis: installation de pup

pup est un outil en ligne de commande pour traiter le HTML. Il lit à partir de stdin, imprime vers stdout, et permet à l’utilisateur de filtrer des parties de la page en utilisant des sélecteurs CSS.

Nous téléchargeons pup depuis son repo sur Github, on extraie le binaire et on le déplace dans /usr/local/bin/ :

wget https://github.com/ericchiang/pup/releases/download/v0.4.0/pup_v0.4.0_linux_amd64.zip

unzip pup_v0.4.0_linux_amd64.zip

sudo mv pup /usr/local/bin/Code language: JavaScript (javascript)

Script bash pour mettre à jour MySQL avec mysql-apt-config

Nous créons notre nouveau script, et nous le rendons exécutable:

cd /home/scripts

nano install-latest-mysql-apt.sh

chmod +x install-latest-mysql-apt.sh

Et voici le contenu du script:

#!/bin/bash
# ----------------------------------------- #
# Script Name: install-latest-mysql-apt.sh  #
# Author: Matt Biscay                       #
# URL: https://www.skyminds.net/?p=613967   #
# ----------------------------------------- #

# Color codes
RED=$(tput bold; tput setaf 1)
GREEN=$(tput bold; tput setaf 2)
YELLOW=$(tput bold; tput setaf 3)
MAGENTA=$(tput bold; tput setaf 5)
NC=$(tput sgr0) # No Color

# Function to print the header
function print_header() {
  echo -e "${YELLOW}# ----------------------------------------- #${NC}"
  echo -e "${YELLOW}# Script Name: install-latest-mysql-apt.sh  #${NC}"
  echo -e "${YELLOW}# Author: Matt Biscay                       #${NC}"
  echo -e "${YELLOW}# URL: https://www.skyminds.net/?p=613967   #${NC}"
  echo -e "${YELLOW}# ----------------------------------------- #${NC}"
}

# Print the header
print_header

# Function to check if the command exists
function check_command() {
  command -v "$1" &> /dev/null || {
    echo -e "${RED}$1 is not installed. Please install it and try again.${NC}"
    exit 1
  }
}

# Check if wget, unzip are installed
check_command wget
check_command unzip

# Check if pup is installed
if ! command -v pup &> /dev/null; then
  read -p $"${MAGENTA}The \"pup\" tool is not installed, would you like to install it now? (Y/N) ${NC}" INSTALL_PUP
  case ${INSTALL_PUP:0:1} in
    [Yy]* )
      TMP_DIR=$(mktemp -d)
      wget https://github.com/ericchiang/pup/releases/download/v0.4.0/pup_v0.4.0_linux_amd64.zip -O "$TMP_DIR/pup.zip"
      unzip "$TMP_DIR/pup.zip" -d "$TMP_DIR"
      mv "$TMP_DIR/pup" /usr/local/bin/
      rm -r "$TMP_DIR"
      echo -e "${GREEN}Package pup has been installed.${NC}"
      ;;
    [Nn]* )
      echo "Exiting since pup is not installed."
      exit 1
      ;;
    * )
      echo -e "${RED}Invalid input. Please enter Y or N.${NC}"
      exit 1
      ;;
  esac
fi

BASE_URL="https://dev.mysql.com"
INITIAL_URL="${BASE_URL}/downloads/repo/apt/"

# Create temporary files
TMP_INITIAL=$(mktemp)
TMP_SECONDARY=$(mktemp)

echo "Fetching initial page... $INITIAL_URL"
wget -qO- "$INITIAL_URL" > "$TMP_INITIAL"

# Extract the secondary download page URL from the main page
echo "Extracting secondary page URL..."
SECONDARY_URL=$(cat "$TMP_INITIAL" | pup 'div.button03 a attr{href}')
echo "Secondary URL: ${BASE_URL}${SECONDARY_URL}"

# Fetch the secondary page and store its content for debugging
echo "Fetching secondary page..."
wget -qO- "${BASE_URL}${SECONDARY_URL}" > "$TMP_SECONDARY"

# Extract the download link from the secondary page
echo "Extracting download link..."
DOWNLOAD_URL=$(cat "$TMP_SECONDARY" | pup 'a:contains("No thanks, just start my download.") attr{href}')
echo "Download URL: ${BASE_URL}${DOWNLOAD_URL}"

# Ask user if they want to install mysql-apt-config
read -p $"${MAGENTA}Do you want to download and install the package? (Y/N) ${NC}" DOWNLOAD_MYSQL_APT_CONFIG
case ${DOWNLOAD_MYSQL_APT_CONFIG:0:1} in
    [Yy]* )
        wget "${BASE_URL}${DOWNLOAD_URL}" -O mysql-apt-config.deb
        dpkg -i mysql-apt-config.deb

        echo -e "${GREEN}Package mysql-apt-config has been updated.${NC}"

        read -p $"${MAGENTA}Are you ready to install updates with \"apt update && apt upgrade\"? (Y/N) ${NC}" UPDATE_UPGRADE
        case ${UPDATE_UPGRADE:0:1} in
            [Yy]* )
                apt update && apt upgrade
                ;;
            [Nn]* )
                echo "Exiting without installing updates."
                ;;
            * )
                echo -e "${RED}Invalid input. Please enter Y or N.${NC}"
                ;;
        esac
        ;;
    [Nn]* )
        echo "Exiting without installing."
        ;;
    * )
        echo -e "${RED}Invalid input. Please enter Y or N.${NC}"
        ;;
esac

# Trap to clean up temporary files
trap 'rm -f "$TMP_INITIAL" "$TMP_SECONDARY" mysql-apt-config.deb' EXITCode language: PHP (php)

Fonctionnement du script

Le script commence par vérifier que pup est installé. S’il ne l’est pas, on prompte l’utilisateur pour l’installer:

The "pup" tool is not installed, would you like to install it now? (Y/N) y

Connecting to objects.githubusercontent.com (objects.githubusercontent.com)|185.199.109.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1718186 (1.6M) [application/octet-stream]
Saving to: ‘/tmp/tmp.4buPXeejfo/pup.zip’

Archive:  /tmp/tmp.4buPXeejfo/pup.zip
  inflating: /tmp/tmp.4buPXeejfo/pup
Package pup has been installed.Code language: JavaScript (javascript)

Le script effectue deux requêtes sur le site de MySQL: lors de la première requête, il identifie le lien du bouton “Download”.

Lire la suite

Resolve error 526 with Cloudflare and NginX.

Résoudre l’erreur 526 entre Cloudflare et NginX

Dernièrement, je me suis apercu qu’une requête curl sur le domaine skyminds.net (sans www donc), donnait une erreur 526 sous Cloudflare (avec le proxy activé, mais aussi sans):

curl -Ik https://skyminds.net


HTTP/2 526
date: Fri, 18 Aug 2023 23:50:35 GMT
content-type: text/html; charset=UTF-8
content-length: 7111
cf-ray: 7f8e0f5538300636-CDG
cf-cache-status: BYPASS
cache-control: private, max-age=0, no-store, no-cache, must-revalidate, post-check=0, pre-check=0
expires: Thu, 01 Jan 1970 00:00:01 GMT
set-cookie: cf_ob_info=526:7f8e0f55430d0636:CDG; path=/; expires=Fri, 18-Aug-23 23:51:05 GMT
vary: Accept-Encoding
cf-apo-via: origin,resnok
referrer-policy: same-origin
set-cookie: cf_use_ob=443; path=/; expires=Fri, 18-Aug-23 23:51:05 GMT
x-frame-options: SAMEORIGIN
report-to: {"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v3?s=KQ7z1CYzwZD3r%2FzoJ2QLzFRmcgQzql1Oz3KwXyVnHVWHH5mUgxKqwU8p2%2F0C9rn%2FJUOSQG%2BWZl9%2B%2FDdQSPXznmFKKeiqFdqXzpuGFK5xgZbHAohOVxY4Vk6%2F1bM73jNRhrjfYZ42r6FMJ6c%3D"}],"group":"cf-nel","max_age":604800}
nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
server: cloudflare
alt-svc: h3=":443"; ma=86400
Code language: JavaScript (javascript)

Sous tous les navigateurs par contre, une brève page d’erreur s’affiche (quelques millisecondes) avant que finalement la redirection vers le sous-domaine www ne s’effectue. Je me suis dis que c’était bizarre et que je réglerais le problème plus tard.

Quelques semaines mois passent et j’effectue une petite visite de routine sur mes sous-domaines, notamment celui de postfixadmin. Et là, patatras : erreur 526 avec le proxy Cloudflare actif, mais aussi une ribambelle d’erreurs selon les navigateurs utilisés:

Échec de la connexion sécurisée Une erreur est survenue pendant une connexion à demo.skyminds.net.

Le pair SSL a rejeté un message d’établissement de liaison à cause d’un contenu inacceptable. Code d’erreur : SSL_ERROR_ILLEGAL_PARAMETER_ALERT

La page que vous essayez de consulter ne peut pas être affichée car l’authenticité des données reçues ne peut être vérifiée. Veuillez contacter les propriétaires du site web pour les informer de ce problème.

Firefox

Ce site est inaccessibleIl se peut que la page Web à l’adresse https://demo.skyminds.net soit temporairement inaccessible ou qu’elle ait été déplacée de façon permanente à une autre adresse Web. ERR_QUIC_PROTOCOL_ERROR

Brave

Bon, il va falloir s’atteler à comprendre ce qui se passe vraiment ici!

Vérification du certificat TLS

On vérifie que le certificat est valide:

echo | openssl s_client -connect skyminds.net:443 -servername skyminds.net 2>/dev/null | openssl x509 -noout -dates

notBefore=Aug  6 12:15:26 2023 GMT
notAfter=Nov  4 12:15:25 2023 GMTCode language: JavaScript (javascript)

Tout est bon, le certificat est toujours valide.

On vérifie ensuite que les sous-domaines sont bien inclus (au cas où, mais je crée toujours des certificats wildcard par défaut):

echo | openssl s_client -connect skyminds.net:4echo | openssl s_client -connect skyminds.net:443 -servername skyminds.net 2>/dev/null | openssl x509 -noout -text


Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            03:cc:a2:48:39:97:ea:6c:d0:5d:86:5a:e3:f8:e3:d3:b2:1a
        Signature Algorithm: ecdsa-with-SHA384
        Issuer: C = US, O = Let's Encrypt, CN = E1
        Validity
            Not Before: Aug  6 12:15:26 2023 GMT
            Not After : Nov  4 12:15:25 2023 GMT
        Subject: CN = skyminds.net
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:69:84:99:0b:ea:1b:6f:38:ac:ee:fa:76:7d:4f:
                    59:01:02:bd:6e:d8:25:83:a7:8a:c5:d5:a4:b9:c5:
                    64:65:2d:49:20:3d:bc:4c:06:38:7d:73:d0:c0:55:
                    0b:90:cf:44:f7:5a:8c:37:8f:f9:da:51:eb:c3:f0:
                    5b:87:ce:00:ba
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier:
                E7:3A:09:C3:D5:75:02:65:A6:D7:7D:12:D4:F3:5D:42:72:11:75:B4
            X509v3 Authority Key Identifier:
                5A:F3:ED:2B:FC:36:C2:37:79:B9:52:30:EA:54:6F:CF:55:CB:2E:AC
            Authority Information Access:
                OCSP - URI:http://e1.o.lencr.org
                CA Issuers - URI:http://e1.i.lencr.org/
            X509v3 Subject Alternative Name:
                DNS:*.skyminds.net, DNS:skyminds.net
            X509v3 Certificate Policies:
                Policy: 2.23.140.1.2.1
            CT Precertificate SCTs:
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : 7A:32:8C:54:D8:B7:2D:B6:20:EA:38:E0:52:1E:E9:84:
                                16:70:32:13:85:4D:3B:D2:2B:C1:3A:57:A3:52:EB:52
                    Timestamp : Aug  6 13:15:26.501 2023 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : B7:3E:FB:24:DF:9C:4D:BA:75:F2:39:C5:BA:58:F4:6C:
                                5D:FC:42:CF:7A:9F:35:C4:9E:1D:09:81:25:ED:B4:99
                    Timestamp : Aug  6 13:15:26.502 2023 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
    Signature Algorithm: ecdsa-with-SHA384
    Code language: PHP (php)

Le certificat couvre bien skyminds.net et *.skyminds.net, donc tous les sous-domaines.

Lire la suite

php 8.2

Installation de PHP 8.2 sur le serveur

Aujourd’hui, on installe PHP 8.2 sur le serveur vu que SkyMinds est compatible avec selon notre testeur de compatibilité PHP.

La dernière version stable installée sur le serveur est PHP8 donc on récupère la liste des extensions PHP installées avec PHP 8:

dpkg -l | grep php8.0 | grep ii | awk '{print $2}'

php8.0
php8.0-apcu
php8.0-bcmath
php8.0-cli
php8.0-common
php8.0-curl
php8.0-fpm
php8.0-gd
php8.0-gmp
php8.0-igbinary
php8.0-imagick
php8.0-imap
php8.0-intl
php8.0-mbstring
php8.0-mysql
php8.0-opcache
php8.0-readline
php8.0-redis
php8.0-soap
php8.0-xml
php8.0-zipCode language: JavaScript (javascript)

C’est bien mais nous pouvons améliorer la commande pour replacer php8.0 par php8.2:

dpkg -l | grep php8.0 | grep ii | awk '{print $2}' | sed 's/php8.0/php8.2/g'

php8.2
php8.2-apcu
php8.2-bcmath
php8.2-cli
php8.2-common
php8.2-curl
php8.2-fpm
php8.2-gd
php8.2-gmp
php8.2-igbinary
php8.2-imagick
php8.2-imap
php8.2-intl
php8.2-mbstring
php8.2-mysql
php8.2-opcache
php8.2-readline
php8.2-redis
php8.2-soap
php8.2-xml
php8.2-zipCode language: JavaScript (javascript)

Transformons maintenant le résultat pour avoir une liste séparée par des espaces et non des sauts de lignes:

dpkg -l | grep php8.0 | grep ii | awk '{print $2}' | sed 's/php8.0/php8.2/g' | tr '\n' ' '

php8.2 php8.2-apcu php8.2-bcmath php8.2-cli php8.2-common php8.2-curl php8.2-fpm php8.2-gd php8.2-gmp php8.2-igbinary php8.2-imagick php8.2-imap php8.2-intl php8.2-mbstring php8.2-mysql php8.2-opcache php8.2-readline php8.2-redis php8.2-soap php8.2-xml php8.2-zipCode language: JavaScript (javascript)

Installation de PHP 8.2

Parfait, nous pouvons maintenant lancer apt install combinée avec notre dernière commande:

apt install $(dpkg -l | grep php8.0 | grep ii | awk '{print $2}' | sed 's/php8.0/php8.2/g' | tr '\n' ' ')

Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
php8.2-cli is already the newest version (8.2.7-1+ubuntu22.04.1+deb.sury.org+1).
php8.2-cli set to manually installed.
php8.2-common is already the newest version (8.2.7-1+ubuntu22.04.1+deb.sury.org+1).
php8.2-common set to manually installed.
php8.2-imagick is already the newest version (3.7.0-3+ubuntu22.04.1+deb.sury.org+1).
php8.2-imagick set to manually installed.
php8.2-opcache is already the newest version (8.2.7-1+ubuntu22.04.1+deb.sury.org+1).
php8.2-opcache set to manually installed.
php8.2-readline is already the newest version (8.2.7-1+ubuntu22.04.1+deb.sury.org+1).
php8.2-readline set to manually installed.
Suggested packages:
  php-pear
The following NEW packages will be installed:
  php8.2 php8.2-apcu php8.2-bcmath php8.2-curl php8.2-fpm
  php8.2-gd php8.2-gmp php8.2-igbinary php8.2-imap php8.2-intl
  php8.2-mbstring php8.2-mysql php8.2-redis php8.2-soap
  php8.2-xml php8.2-zip
0 upgraded, 16 newly installed, 0 to remove and 0 not upgraded.
Need to get 3440 kB of archives.
After this operation, 10.7 MB of additional disk space will be used.
Do you want to continue? [Y/n] yCode language: JavaScript (javascript)

Configuration de PHP 8.2

Pour la configuration du php.ini ainsi que du pool PHP, je vous conseille d’utiliser la configuration de notre WordPress RocketStack, qui donne d’excellents résultats en terme de performance et réactivité.

Mise à jour du server block NginX

C’est plutôt simple, il vous suffit d’éditer le server block NginX de votre domaine et de changer cette ligne:

# PHP 8
fastcgi_pass unix:/run/php/php8.0-fpm.sock;Code language: PHP (php)

par cette ligne:

# PHP 8.2
fastcgi_pass unix:/run/php/php8.2-fpm.sock;Code language: PHP (php)

Ensuite, il ne nous reste plus qu’à relancer les services:

service nginx restart
service php8.2-fpm restartCode language: CSS (css)

Et voilà PHP 8.2 est installé et fait tourner le site, en moins de 10 minutes.

wordpress rocketstack

WordPress Rocket Stack : envoyez WordPress sur orbite !

Sur Orion, j’ai installé ma WordPress Rocket Stack qui est configurée avec la stack suivante:

  • Ubuntu Server 22.04 LTS
  • MySQL 8+
  • NginX 1.25+
  • PHP 8.3
  • Redis
  • Nginx FastCGI Cache
  • Fail2ban
  • LetsEncrypt avec acme.sh

Hébergement

L’hébergement est la base de votre site, c’est tout simplement la fondation sur laquelle va reposer votre code.

Vous avez tout intérêt à avoir un très bon hébergeur : il doit être rapide dès le départ et offrir de bonnes garanties en termes de performance et de sécurité.

Si vous avez un site WordPress ou WooCommerce, je ne peux que vous recommander Kinsta, WPEngine ou Nexcess. Tous trois sont de très bons hébergeurs, particulièrement orientés vers la performance avec des ressources garanties et un support technique réactif et efficace en cas de besoin.

Personnellement, j’utilise un serveur dédié chez OVH parce que j’héberge pas mal de sites et j’ai besoin d’avoir un contrôle fin sur la configuration de chacun des services.

Ubuntu Server

J’étais auparavant sous Debian mais j’ai finalement opté pour Ubuntu Server 22.04 LTS pour ce nouveau serveur.

L’avantage d’Ubuntu est de pouvoir disposer des mises à jour plus rapidement que sous Debian.

Lire la suite

gmail android

Effacer les mails depuis les notifications Android

Les notifications Android vous informent de l’arrivée des nouveaux e-mails et vous donnent un aperçu sur quelques lignes, ce qui vous permet de prendre connaissance de la teneur du message. J’utilise principalement Gmail et les notifications sont très pratiques.

Deux boutons sont proposés par défaut lorsqu’une notification affiche un e-mail: Archiver et Répondre.

Je n’archive jamais de messages depuis les notifications et je lance toujours l’application Gmail pour consulter l’intégralité du message, ouvrir les pièces jointes et ensuite décider ou non s’il faut supprimer le message, le labelliser et/ou l’archiver pour le retrouver plus tard.

L’action Archiver n’est donc pas la plus utile, je préfère très largement avoir un bouton Supprimer, ce qui évitera d’avoir une boite de réception avec des centaines ou milliers de messages. Je suis dans les 500 actuellement et c’est déjà une grosse charge mentale à chaque fois que j’ouvre Gmail…

Un bouton Supprimer dans les notifications

Pour remplacer le bouton Archiver par un bouton Supprimer, il suffit de se rendre dans l’application Gmail ⟶ Paramètres ⟶ Paramètres généraux :

Paramètres généraux de Gmail : action par défaut des notifications
Paramètres généraux de Gmail : action par défaut des notifications

Tapez sur Action par défaut pour les notifications. Une fenêtre s’ouvre et vous propose deux choix: Archiver ou Supprimer.

Lire la suite

ssh secure shell laptop

SSH : Host key verification failed

Voici la solution pour le problème “authenticity of host ‘example.com can’t be established” ou “Host key verification failed” lors d’une session SSH sous Linux, MacOS ou Windows.

J’ai récemment travaillé sur un site WordPress multisite hébergé sur WPEngine. Lorsque j’ai voulu me connecter au site en SSH, j’ai reçu une drôle d’erreur:

The authenticity of host 'example.ssh.wpengine.net (xxx.xxx.xxx.xxx)' can't be established.
RSA key fingerprint is SHA256:T8IoIg6/q7i3pVfZipYtxow4.
This host key is known by the following other names/addresses:
    ~/.ssh/known_hosts:38: 1.ssh.wpengine.net
    ~/.ssh/known_hosts:42: 2.ssh.wpengine.net
    ~/.ssh/known_hosts:47: 3.ssh.wpengine.net
    ~/.ssh/known_hosts:52: 4.ssh.wpengine.net
    ~/.ssh/known_hosts:54: 5.ssh.wpengine.net
    ~/.ssh/known_hosts:56: 6.ssh.wpengine.net
    ~/.ssh/known_hosts:76: 7.ssh.wpengine.net
    (2 additional names omitted)
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Host key verification failed.Code language: PHP (php)

Comme vous pouvez le voir, il y a déjà pas mal de domaines connus dans mon trousseau SSH, qui appartiennent à ssh.wpengine.net.

Pour une raison obscure toutefois, le nouveau sous-domaine n’est pas admis, contrairement aux sous-domaines précédents qui n’avaient posé aucun problème.

Solution: ajouter la clé au trousseau manuellement

Si la clé ne peut être ajoutée automatiquement, ajoutons-la manuellement et tentons de nous connecter.

Concrétement, il faut donc insérer la clé RSA du serveur dans le fichier /home/user/.ssh/known_hosts de la machine qui se connecte en SSH au serveur distant.

Nous pouvons faire cela en une seule commande:

ssh-keyscan -t rsa example.ssh.wpengine.net >>  ~/.ssh/known_hosts
# example.ssh.wpengine.net:22 SSH-2.0-GoCode language: PHP (php)

Voici ce que fait la commande:

  • ssh-keyscan -t rsa SSH.EXAMPLE.COM : cela récupère la clé RSA de l’hôte SSH.EXAMPLE.COM
  • >> ~/.ssh/known_hosts : cela copie la clé et l’insère dans le répertoire .ssh du dossier personnel de notre utilisateur, à la fin du fichier known_hosts qui contient tous les hôtes et clés RSA pour SSH.

Lire la suite

lastfm scrobbler for icecast

Soumettre à Last.fm les chansons d’un flux Icecast

Voici le script python que j’ai écrit pour soumettre automatiquement à Last.fm les chansons qui sont diffusées sur ma radio Icecast.

Au fil du temps, j’ai publié dans ces colonnes différents tutoriels pour gérer une radio avec Icecast, à l’aide de Winamp, VirtualDJ, et SAM Broadcaster dont on pouvait créer un fichier texte qui pouvait ensuite être filtré puis utilisé avec un script PHP pour soumettre les chansons à Last.fm.

Il y a quelques semaines, Stéphan m’a contacté pour m’informer que le script PHP que j’utilisais jusqu’alors pour scrobbler les titres était maintenant incompatible avec PHP8, et qu’il lui fallait installer PHP7.4 spécifiquement pour le faire tourner.

Le script n’utilisait pas les dernières API de Last.fm non plus, donc c’était le bon moment de moderniser tout cela. J’en ai d’ailleurs profité pour servir le serveur Icecast en HTTPS.

Le choix de Python

Après de multiples essais, je me suis vite rendu compte que PHP n’était pas du tout adapté au scrobbling : la plupart des librairies PHP sont maintenant obsolètes (PHP8) ou ne sont plus maintenues car Last.fm est en perte de vitesse (par rapport à ce que c’était il y a quelques années).

Python, au contraire, est très simple à utiliser et il existe une librairie spécifique dédiée à l’API Last.fm qui nous permet de soumettre les titres simplement: pylast.

Enfin, l’avantage de python est qu’il est installé et mis à jour sur toutes les distributions Linux et MacOS nativement et donc cela rend le code portable et facile à transporter.

Outre Python 3, vous avez besoin du gestionnaire de paquets pip :

apt install python3-pip

Vous avez également besoin de quelques modules supplémentaires, que l’on installe avec pip :

python3 -m pip install --upgrade pip
pip3 install pylast
pip3 install requests

C’est tout ce qu’il vous faut au niveau des dépendances. Plus besoin de PHP ni de ses librairies, plus besoin de stocker le nom des titres dans un fichier texte, tout va se faire en direct depuis le serveur Icecast.

Last.FM Scrobbler for Icecast

Le script Last.FM Scrobbler for Icecast vérifie le statut du server Icecast toutes les 30 secondes. Si le serveur est démarré et que la radio joue, il récupère les informations de la chanson et les soumet à Last.fm, tout en vérifiant bien que la chanson n’est pas la même que la précédente. Cela évite tout doublon, ce qui fausserait les soumissions du compte.

Si le serveur Icecast n’est pas démarré, le script vous avertit et attend 5 minutes.

Si le serveur Icecast est démarré mais que le fichier status-json.xsl ne contient pas les informations nécessaires, le script vous avertit et vous recommande de redémarrer votre webradio car la connexion avec Icecast est perdue.

On crée notre script:

nano /home/scripts/lastfm-scrobbler-icecast.py

Et on y ajoute:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#-----------------------------------------------------------------------
# Script name : Last.FM Scrobbler for Icecast
# Created By  : Matt Biscay
# Created Date: 2023/01/24
# Author URI  : https://www.skyminds.net
# Version     : 1.0.0
# Copyright   : 2023 SkyMinds.Net
# ---------------------------------------------------------------------- 
"""
This script will check the status of the icecast server every 30 sec and if the server is running, it will check for the currently playing track and scrobble it to LastFM if it's different from the last scrobbled track.
If the server is not running, the script will wait for 5 minutes before retrying.
It uses the pylast library to interact with the LastFM API.

@param API_KEY: the LastFM API key
@param API_SECRET: the LastFM API secret
@param username: the LastFM username
@param password: the LastFM password
@param icecast_url: the URL of the icecast server status page
"""

import pylast
import time
import requests
import json
# --------------------------------------------------
# --------- EDIT CONFIGURATION HERE ----------------
# --------------------------------------------------
# Your LastFM API key and secret
API_KEY = "LASTFM_API_KEY"
API_SECRET = "LASTFM_API_SECRET"

# The username and password of the user you want to authenticate 
# and scrobble the track for
username = "LASTFM_USER"
password = "LASTFM_PWD"

# The URL of the Icecast server's status-json.xsl page
icecast_url = "http://icecast.example.com:8000/status-json.xsl"

# ------------------------------------------------
# ---------- END OF CONFIGURATION  ---------------
# ------------------------------------------------

# Create a new LastFM network object
network = pylast.LastFMNetwork(api_key=API_KEY, api_secret=API_SECRET,
                               username=username, password_hash=pylast.md5(password))

# The previously scrobbled track name
previous_track = ""

print("# ------------------------------------------ #")
print("#  Script: Last.FM Scrobbler for Icecast     #")
print("#  Author: Matt Biscay                       #")
print("#  URL   : https://www.skyminds.net          #")
print("# ------------------------------------------ #")
print("")

while True:

    try:
        # Make a GET request to the status-json.xsl page
        response = requests.get(icecast_url)
    except requests.exceptions.ConnectionError as e:
        print("Error: Icecast server is down.")
        # Wait for 5 minutes before retrying
        time.sleep(5*60)
        continue

    if response.status_code != 200:
        print("Error: Icecast server is not running or there is some other error")
        # Wait for 5 minutes before retrying
        time.sleep(5*60)
        continue

    # Parse the JSON response
    data = json.loads(response.text)

    # Get the currently playing track information
    if "icestats" in data and "source" in data["icestats"] and "artist" in data["icestats"]["source"] and "title" in data["icestats"]["source"]:
        artist = data["icestats"]["source"]["artist"]
        track = data["icestats"]["source"]["title"]
        current_track = f'{artist} - {track}'
        if current_track != previous_track:
            # Scrobble the track
            try:
                timestamp = int(time.time())
                network.scrobble(artist=artist, title=track, timestamp=timestamp)
                print(f"Track scrobbled successfully: {current_track}")
                previous_track = current_track
            except pylast.WSError as e:
                print("Error:", e)
        # Wait for 30 seconds before checking for new tracks
        time.sleep(30)
    else:
        print("Error: Icecast XML does not contain the required information. Restart your webradio.")
        # Wait for 1 minutes before retrying
        time.sleep(60)
        continueCode language: PHP (php)

Editez les variables dans la partie EDIT YOUR CONFIGURATION: vous avez besoin de votre clé API last.fm ainsi que la clé API secret, de votre nom d’utilisateur et mot de passe last.fm, et enfin l’adresse complète du fichier XML d’Icecast, status-json.xsl.

Lire la suite