Réinstallation serveur ubuntu, statistiques web, favicon
Author: Unknown
Date: 20/10/2025
Marre des rapspberry qui en rebootent plus à cause d'une carte SD qui a pris un coup de calcaire. J'ai récupéré un optiplex 9020 / Core i3 / 4Go de RAM / 250Go de disque dur, et j'ai commence depuis zéro l'installation d'un serveur qui réponds le mieux possible à mes besoins.
Je prends des notes pour gagner du temps la prochaine fois.
Ce fichier reprends la dernière version à jour, complète. Les versions intermédiaires sont ici:
- 7 juin: [Première tentative, loupée](20250607-Réinstallation loupée d'un serveur ubuntu.md)
- 8 juin: [Deuxième tentative](20250608-Réinstallation d'un serveur ubuntu, première étape.md)
- 15 juillet:
- 19 juillet: [Sauvegarde régulière des scripts et configuration docker](20250719-Sauvegarde régulière de la config docker et de mes scripts.md)
- 28 juillet: [Création des différents sites webs, contenu statique de base, génération automatique de leur contenu dynamique](20250828-Réinstallation-serveur-ubuntu, suite.md)
- 27 septembre: [Résolution de problèmes divers](20250927-Réinstallation-serveur-ubuntu, résolution de problèmes divers.md)
- ce jour: [Statistiques web, favicon](20251020-Réinstallation-serveur-ubuntu, statistiques web, favicon.md)
Nouveauté du jour: statistiques web
Récupérer les logs du reverse proxy
Actuellement, le nginx-proxy crache ces logs sur la sortie standard. Il faut que le modifier cela pour qu'il les crache sur disque, dans un volume partagé avec le système hôte, et en effectuant une rotation de logs. Mon fichier compose.yaml est actuellement:
networks:
default:
external:
name: nginx-proxy
services:
nginx-proxy:
image: jwilder/nginx-proxy
container_name: nginx-proxy
restart: "unless-stopped"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./certs:/etc/nginx/certs
- ./htpasswd:/etc/nginx/htpasswd
- ./vhost.d:/etc/nginx/vhost.d
- ./html:/usr/share/nginx/html
environment:
- TRUST_DOWNSTREAM_PROXY=false
letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion
container_name: letsencrypt-companion
restart: "unless-stopped"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./certs:/etc/nginx/certs
- ./html:/usr/share/nginx/html
environment:
- NGINX_PROXY_CONTAINER=nginx-proxy
- DEFAULT_EMAIL=anothermail@gmail.com
depends_on:
- nginx-proxy
test-nginx:
image: nginx:alpine
container_name: test-nginx
restart: "unless-stopped"
volumes:
- ./test-nginx/html:/usr/share/nginx/html:ro
environment:
- VIRTUAL_HOST=test.9h56.fr
- LETSENCRYPT_HOST=test.9h56.fr
- LETSENCRYPT_EMAIL=anothermail@gmail.com
expose:
- "80"
Je rajoute un - ./logs:/var/log/nginx dans les volumes
Je crée le répertoire logs, et un répertoire de conf sur le système hôte. mkdir -p logs conf && chmod 0777 logs
Dans le répertoire de conf, je rajoute un fichier de conf sur la partie log ci-dessous , et je le rajoute dans la config docker (./conf/logging.conf:/etc/nginx/conf.d/logging.conf:ro)
- Le fichier
./conf/logging.conf:
log_format vhosts '$host $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log vhosts;
error_log /var/log/nginx/error.log warn;
Je redémarre le proxy (docker restart nginx-proxy), mais rien ne crache dans les logs. Peut-être faut-il que docker fasse de la magie pour prendre en compte ma modification de configuration. Du coup, je fais un docker stop nginx-proxy suivi d'un docker compose up -d et cela redémarre bien, j'ai bien des écritures dans le répertoire de logs avec deux fichiers access.log et error.log. Mais, le site web ne réponds plus, erreur TLS...
Échec de la connexion sécurisée
Une erreur est survenue pendant une connexion à ia.9h56.fr. Le pair SSL n’a pas de certificat pour le nom DNS demandé.
Code d’erreur : SSL_ERROR_UNRECOGNIZED_NAME_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.
Je redémarre les docker derrière le proxy, des fois que le problème vienne d'un soucis de connexion réseau entre (docker restart test-nginx) mais pas mieux. Dans le doute, problème TLS, je redémarre letsencrypt-companion (docker restart letsencrypt-companion). Et là, boum, cela fonctionne !
N'empêche que pendant tout ce temps, plus rien ne fonctionnait. C'est pas super propre. Il faudrait que je mette en place une déclinaison en environnement de qualification pour faire tous ces tests avant de déployer ensuite sur les vrais sites. On verra cela plus tard, dans des temps futurs, je n'en suis pas là. Maintenant que je ne perds plus les infos de log, l'essentiel est déjà fait. Je vais pouvoir retourner travailler à la pose du frein vapeur dans mon grenier.
Monter le serveur matomo et en protéger l'accès par une ACL
TODO
Importer les logs du reverse proxy dans matomo
TODO
Génération des favicon
Pour ia.9h56.fr
mkdir -p ~/noteblogger/conf/ia.9h56.fr/html/assets ~/noteblogger/conf/ia.9h56.fr/html/css- le fichier
~/noteblogger/conf/ia.9h56.fr/html/assets/favicon.ia.9h56.fr.svg:
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256"> <defs> <linearGradient id="grad" x1="0" y1="0" x2="1" y2="1"> <stop stop-color="#FFFFFF"/> <stop offset="1" stop-color="#F1F1EA"/> </linearGradient> </defs> <rect width="256" height="256" rx="40" ry="40" fill="url(#grad)"/> <text x="128" y="170" text-anchor="middle" fill="#333333" font-family="Segoe UI, Roboto, Arial" font-size="110" font-weight="bold" letter-spacing="6">IA</text> </svg>
- la génération des différentes icônes:
convert -background transparent -define 'icon:auto-resize=16,24,32,64' assets/*.svg favicon.ico - sauf qu'imagemagick doire complètement la transformation depuis un svg...
- je génère donc à la min un png depuis le wvg via un service en ligne
- la génération des différentes icônes:
convert -background transparent -define 'icon:auto-resize=16,24,32,64' assets/favicon*.png favicon.ico
Généralisation
- Je fais un script qui génère automatiquement les icônes pour l'ensemble des sites web, depuis une image png:
TODO
- Je modifie le script de génération automatique des sites web pour prendre en compte les ressources statiques, quand elles existent:
TODO
Install de base
J'installe une ubuntu serveur classique 24.04 LTS.
Update system
sudo apt update && sudo apt upgrade
Mise à jour automatiques de sécurité
sudo apt install unattended-upgrades -y
Remove Snap
J'aime pas Snap. Enfin, surtout, je connais pas assez. Alors, je vais le dégager dans un premier temps.
Déjà, y a quoi d'installé avec snap ? snap list. J'ai rien d'installé, donc je vire le principe même de Snap.Ah, bah, sur ubunto, cela a l'air pas immédiat/recommandé de tout virer snap. Je vais gagner du temps et le laisser, du coup.
Packages à installer
J'ai des vieilles habitudes, donc, vieux packages à remettre: sudo apt install net-tools plocate
Firewall
The UFW (Uncomplicated Firewall) is a user-friendly way to manage your firewall rules on Ubuntu Server. It provides a simplified interface for configuring and managing iptables. Here’s how you can set up UFW:
- Check the status of UFW: sudo ufw status verbose
- Enable UFW:
sudo ufw enable - Allow SSH access:
sudo ufw enable - Allow necessary services, such as HTTP (port 80) or HTTPS (port 443):
sudo ufw allow httpetsudo ufw allow https
sudo ufw status verbose
sudo ufw enable
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw status verbose
[Ajout du 15 juillet 2025] Bizarrement, après reboot, les règles ne sont plus en place
user@lnxsrv:~$ sudo ufw status
Status: inactive
La configuration n'est pas perdue pour autant:
user@lnxsrv:~$ sudo ufw show added
Added user rules (see 'ufw status' for running firewall):
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443
ufw allow 9090
ufw allow 9100
ufw allow 3000
ufw allow 3000/tcp
Une réactivation manuelle fonctionne:
user@lnxsrv:~$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
user@lnxsrv:~$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
80/tcp ALLOW IN Anywhere
443 ALLOW IN Anywhere
9090 ALLOW IN Anywhere
9100 ALLOW IN Anywhere
3000 ALLOW IN Anywhere
3000/tcp ALLOW IN Anywhere
22/tcp (v6) ALLOW IN Anywhere (v6)
80/tcp (v6) ALLOW IN Anywhere (v6)
443 (v6) ALLOW IN Anywhere (v6)
9090 (v6) ALLOW IN Anywhere (v6)
9100 (v6) ALLOW IN Anywhere (v6)
3000 (v6) ALLOW IN Anywhere (v6)
3000/tcp (v6) ALLOW IN Anywhere (v6)
Bizarrement, après reboot, cette fois-ci, les règles sont bien là. Ah zut non, finalement, plus tard, les règles ne sont plus là. Bizarre.
Fail2ban
sudo apt install fail2ban
Durcir accès ssh
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.orig
sudo vi /etc/ssh/sshd_config
Uncomment the line under the PermitRootLogin directive to explicitly forbid SSH login attempts as root::
PermitRootLogin no
MaxAuthTries 3
LoginGraceTime 15
sudo sshd -t
sudo systemctl reload ssh
Packages divers
sudo apt-get install git
Docker
sudo apt install apt-transport-https curl- Add Docker’s Official GPG Key
https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg - Set Up Docker’s Stable Repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt updatesudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin- Vérification:
sudo systemctl is-active dockersudo docker run hello-world
- Enabling Non-root Users to Run Docker Commands:
sudo usermod -aG docker ${USER}suivi d'unsudo reboot
Synchro du NAS
Montage partage du NAS
sudo mkdir -p /mnt/nas_pctv_rosudo cp /etc/fstab /etc/fstab.origsudo vi /etc/fstabet ajouter la ligne://192.168.0.13/DD3TO_ /mnt/nas_pctv_ro cifs username=****,password=****,ro,iocharset=utf8,file_mode=0777,dirmode=0777,vers=2.1 0 0sudo systemctl daemon-reloadsudo mount /mnt/nas_pctv_rols /mnt/nas_pctv_ro/
Montage du disque dur externe
Le disque dur interne de l'optiplex n'est pas assez grand. J'utilise un disque dur externe de 1To pour mes données. Dans un premier temps, pas chiffré, et c'est pas terrible.
lsblkpour repérer comment est identifié le disque dur externe. Cela me donne:
user@lnxsrv:~/scripts$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 298,1G 0 disk
├─sda1 8:1 0 1G 0 part /boot/efi
├─sda2 8:2 0 2G 0 part /boot
└─sda3 8:3 0 295G 0 part
├─ubuntu--vg-ubuntu--lv 252:0 0 245G 0 lvm /
└─ubuntu--vg-home 252:1 0 50G 0 lvm /home
sdb 8:16 0 931,5G 0 disk
├─sdb1 8:17 0 100G 0 part
├─sdb2 8:18 0 64G 0 part
└─sdb3 8:19 0 767,5G 0 part
sr0 11:0 1 1024M 0 rom
sudo mkdir -p /mnt/linux/sudo vi /etc/fstab- rajouter la ligne
/dev/sdb3 /mnt/linux ext4 defaults 0 2 sudo systemctl daemon-reloadsudo mount /mnt/linuxls /mnt/linux/touch /mnt/linux/PERSONNEL/test_prout
Script de synchro du NAS
- Tout d'abord:
cd
mkdir -p scripts
cd scripts
vi synchro_pctv_personnel_to_local.bash
- Le fichier synchro_pctv_personnel_to_local.bash à recopier dans vi
- Puis:
chmod +x synchro_pctv_personnel_to_local.bashsudo mkdir -p /var/log/sync_nassudo chmod a+rwx /var/log/sync_nas
Synchro en crontab
crontab -e33 3,7,11,17 * * * /home/user/scripts/synchro_pctv_personnel_to_local.bash
Utilisation de docker pour les sites statiques et les redirections
- Je ne veux pas avoir un seul fichier compose.yaml à rallonge.
- Si je veux que le proxy et que letsencrypt-companion arrive à communiquer avec les sites web montés par d'aures fichiers compose.yaml, alors il faut expliciter un réseau web:
docker network lsdevrait faire apparaitre uniquement bridge, host et nonedocker network create nginx-proxypour créer le réseau "externe" utilisable par différentscompose.yaml- L'arbo disque:
user@lnxsrv:~/docker$ find . -type d
.
./noteblogger-sites
./front-nginx-proxy
./front-nginx-proxy/html
./front-nginx-proxy/certs
./front-nginx-proxy/conf
./front-nginx-proxy/logs
./front-nginx-proxy/vhost.d
./front-nginx-proxy/test-nginx
./front-nginx-proxy/test-nginx/html
- Le fichier
./conf/logging.conf:
log_format vhosts '$host $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log vhosts;
error_log /var/log/nginx/error.log warn;
- Le fichier
./front-nginx-proxy/compose.yaml:
networks:
default:
external:
name: nginx-proxy
services:
nginx-proxy:
image: jwilder/nginx-proxy
container_name: nginx-proxy
restart: "unless-stopped"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./certs:/etc/nginx/certs
- ./htpasswd:/etc/nginx/htpasswd
- ./vhost.d:/etc/nginx/vhost.d
- ./html:/usr/share/nginx/html
- ./logs:/var/log/nginx
- ./conf/logging.conf:/etc/nginx/conf.d/logging.conf:ro
environment:
- TRUST_DOWNSTREAM_PROXY=false
letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion
container_name: letsencrypt-companion
restart: "unless-stopped"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./certs:/etc/nginx/certs
- ./html:/usr/share/nginx/html
environment:
- NGINX_PROXY_CONTAINER=nginx-proxy
- DEFAULT_EMAIL=anothermail@gmail.com
depends_on:
- nginx-proxy
test-nginx:
image: nginx:alpine
container_name: test-nginx
restart: "unless-stopped"
volumes:
- ./test-nginx/html:/usr/share/nginx/html:ro
environment:
- VIRTUAL_HOST=test.9h56.fr
- LETSENCRYPT_HOST=test.9h56.fr
- LETSENCRYPT_EMAIL=anothermail@gmail.com
expose:
- "80"
- Le fichier
./noteblogger-sites/compose.yaml:
networks:
default:
external:
name: nginx-proxy
services:
articles.9h56.fr-nginx:
image: nginx:alpine
container_name: articles.9h56.fr-nginx
restart: "unless-stopped"
volumes:
- /var/www/articles.9h56.fr/html:/usr/share/nginx/html:ro
environment:
- VIRTUAL_HOST=articles.9h56.fr
- LETSENCRYPT_HOST=articles.9h56.fr
expose:
- "81"
ia.9h56.fr-nginx:
image: nginx:alpine
container_name: ia.9h56.fr-nginx
restart: "unless-stopped"
volumes:
- /var/www/ia.9h56.fr/html:/usr/share/nginx/html:ro
environment:
- VIRTUAL_HOST=ia.9h56.fr
- LETSENCRYPT_HOST=ia.9h56.fr
expose:
- "83"
misc.9h56.fr-nginx:
image: nginx:alpine
container_name: misc.9h56.fr-nginx
restart: "unless-stopped"
volumes:
- /var/www/misc.9h56.fr/html:/usr/share/nginx/html:ro
environment:
- VIRTUAL_HOST=misc.9h56.fr
- LETSENCRYPT_HOST=misc.9h56.fr
expose:
- "85"
www.9h56.fr-nginx:
image: nginx:alpine
container_name: www.9h56.fr-nginx
restart: "unless-stopped"
volumes:
- /var/www/www.9h56.fr/html:/usr/share/nginx/html:ro
environment:
- VIRTUAL_HOST=www.9h56.fr
- LETSENCRYPT_HOST=www.9h56.fr
expose:
- "86"
bullespositives.9h56.fr-nginx:
image: nginx:alpine
container_name: bullespositives.9h56.fr-nginx
restart: "unless-stopped"
volumes:
- /var/www/bullespositives.9h56.fr/html:/usr/share/nginx/html:ro
environment:
- VIRTUAL_HOST=bullespositives.9h56.fr
- LETSENCRYPT_HOST=bullespositives.9h56.fr
expose:
- "87"
www.home.9h56.fr-nginx:
image: nginx:alpine
container_name: www.home.9h56.fr-nginx
restart: "unless-stopped"
volumes:
- /var/www/www.home.9h56.fr/html:/usr/share/nginx/html:ro
environment:
- VIRTUAL_HOST=www.home.9h56.fr
- LETSENCRYPT_HOST=www.home.9h56.fr
expose:
- "88"
calmsante-dev.9h56.fr-nginx:
image: nginx:alpine
container_name: calmsante-dev.9h56.fr-nginx
restart: "unless-stopped"
volumes:
- /var/www/calmsante-dev.9h56.fr/html:/usr/share/nginx/html:ro
environment:
- VIRTUAL_HOST=calmsante-dev.9h56.fr
- LETSENCRYPT_HOST=calmsante-dev.9h56.fr
expose:
- "89"
Création des arborescences disques
cd /var/www
sudo mkdir articles.9h56.fr bullespositives.9h56.fr calmsante-dev.9h56.fr ia.9h56.fr misc.9h56.fr www.9h56.fr www.home.9h56.fr
sudo chown a+rwx articles.9h56.fr bullespositives.9h56.fr calmsante-dev.9h56.fr ia.9h56.fr misc.9h56.fr www.9h56.fr www.home.9h56.fr
for i in bullespositives.9h56.fr calmsante-dev.9h56.fr ia.9h56.fr misc.9h56.fr www.9h56.fr www.home.9h56.fr
> do
> mkdir $i/html
> echo "$i" > $i/html/index.html
> done
mkdir www.home.9h56.fr/html/ia www.home.9h56.fr/html/articles www.home.9h56.fr/html/misc www.home.9h56.fr/html/bullespostives
Mise en place de l'ACL sur www.home.9h56.fr
Je mets en place le contrôle au niveau du reverse proxy. Celui-ci est basé sur le container jwilder/nginx-proxy qui crée automagiquement sa configuration. Il faut donc que je trouve comment y injecter la configuration spécifique pour le service www.home.9h56.fr. Pour générer le fichier de mot de passe, il faut l'utilitaire apache2-utils.
sudo apt-get install apache2-utils
cd ~/docker/front-nginx-proxy/
mkdir htpasswd
sudo htpasswd -c ./htpasswd/htpasswd.www.home.9h56.fr userACréér
Modification du fichier ~/docker/front-nginx-proxy/compose.yaml pour monter dans le container le répertoire htpasswd:
nginx-proxy:
image: jwilder/nginx-proxy
container_name: nginx-proxy
restart: "unless-stopped"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./certs:/etc/nginx/certs
- ./vhost.d:/etc/nginx/vhost.d
- ./htpasswd:/etc/nginx/htpasswd
- ./html:/usr/share/nginx/html
environment:
- TRUST_DOWNSTREAM_PROXY=false
Création du fichier ~/docker/front-nginx-proxy/vhost.d/www.home.9h56.fr :
auth_basic "Family’s Area";
auth_basic_user_file /etc/nginx/htpasswd/htpasswd.www.home.9h56.fr;
Il ne faut pas oublier de reconstruire l'image après modification du fichier compose.yaml:
docker stop nginx-proxy
docker rm nginx-proxy
docker compose -f compose.yaml up -d
Sauvegarde régulière de la config docker et de mes scripts
Script de synchro
- Tout d'abord:
cd
mkdir -p scripts
cd scripts
vi backup.bash
- Le backup.bash à recopier dans vi
- Puis:
chmod +x backup.bashsudo mkdir -p /var/log/backupsudo chmod a+rwx /var/log/backup
Synchro en crontab
crontab -e56 20 * * * /home/user/scripts/backup.bash
La suite:
J'aimerais également:
- une génération régulière des mes blogs
- remonter le disque dur externe si démonté-je-ne-sais-pas-pourquoi
- remonter le partage du nas si démonté-je-ne-sais-pas-pourquoi
- un redémarrage automatique des containers en cas de reboot du serveur
- un suivi / admin web docker (https://github.com/henrygd/beszel?tab=readme-ov-file?)
- une centralisation des logs apaches docker et les interroger via Matomo
- installer une gallerie photo++ style photoprism
- une centralisation des logs docker et les interroger via un ELK
- une centralisation des logs locaux et les interroger via un ELK
- vérifier que la sauvegarde mensuelle fonctionne
- une centralisation de certains logs distants et les interroger via un ELK
- sortir les éventuels password des fichiers de configuration
Tags: computing ubuntu serveur-bayart back2code informatique article mon-serveur-à-moi-qui-me-va-bien