FortiClient SSLVPN : se connecter avec un token TOTP (Google Authenticator)

Si comme moi vous êtes sous Linux et vous devez utiliser un VPN FortiGate avec une authentification forte (un token TOTP Google Authenticator), vous devez avoir remarqué un problème avec le client :

Il n’y a pas de champs pour rentrer son token.

La solution est simple, il suffit de rentrer son token dans le champs Password, directement à la suite de votre mot de passe.

Zabbix : acknowledge impossible

Sur un Zabbix 3.4, il n’était pas possible d’acknowledger les alertes. L’erreur était :

Fatal error, please report to the Zabbix team

- Controller: acknowledge.edit
- action: acknowledge.edit
- backurl: zabbix.php%3Faction%3Ddashboard.view
- eventids: Array

Le problème est tout con, et ne provient pas de Zabbix. Dans la configuration Apache j’ai retrouvé les lignes suivantes :

RewriteEngine on
RewriteRule ^/zabbix / [R,L]

Je suppose qu’a une époque l’interface web se trouvait derrière le répertoire /zabbix mais qu’à un moment quelqu’un a voulu simplifier et tout mettre à la racine. Ensuite il a rajouté la redirection pour ne pas casser tous les webservices existants (plusieurs services externes utilisent l’API de ce Zabbix). Le problème c’est que ce faisant il a redirigé tout ce qui commençait par /zabbix, y compris les appels à /zabbix.php. Et, vous l’aurez compris, l’acknowledge fait justement un appel à /zabbix.php.

En regardant les logs Apache j’ai vu que aucun webservice n’appelait /zabbix. J’ai donc tout simplement supprimé la redirection.

Si vous devez conserver la redirection, il devrait être possible de s’en sortir avec une règle du type (attention, je n’ai pas testé) :

RewriteEngine on
RewriteRule ^/zabbix(?:/.*)?$ /$1 [R,L]

Zabbix : un trigger actif seulement à certains moments de la journée

Dans Zabbix (jusqu’à la version 3.4), les maintenances ne peuvent s’appliquer qu’à des équipements entiers. Si on veut que certains trigger, mais pas tous, soient en maintenance à certains moments de la journée, il faut ruser. Il y a deux techniques principales.

Pour un seul trigger

Les fonctions date et dayofweek sont faites pour vous. dayofmonth existe aussi mais est moins utile.

{Template Windows:agent.ping.nodata(5m)}=1 and {Template Windows:agent.ping.time(0)}>090000 and {Template Windows:agent.ping.time(0)}<180000 and {Template Windows:agent.ping.dayofweek(0)}<6

Le trigger ci-dessus contient un test d’agent ping standard. J’ai rajouté deux conditions sur l’heure (.time(0)}>090000 et .time(0)}<180000, pour alerter seulement entre 9h et 18h). J’ai également ajouté une condition sur le jour de la semaine (.dayofweek(0)}<6),pour n’alerte que du lundi au vendredi.

Nous pouvons remarquer que les triggers de type date doivent obligatoirement s’appliquer sur un item, mais que sa valeur ne sert à rien dans l’évaluation de la condition.

Pour un gros groupes de triggers

La technique ci-dessus est assez contraignante si on a beaucoup de triggers. Dans ce cas il est possible de faire plus simple. On créé d’abord un trigger :

{Template Windows:agent.ping.time(0)}<090000 or {Template Windows:agent.ping.time(0)}>180000 or {Template Windows:agent.ping.dayofweek(0)}>4

Sur le même principe que la première technique, ce trigger sera systématiquement en alerte entre 18h et 9h, le samedi et le dimanche. Il faut le mettre en sévérité Information pour qu’il ne remonte pas en MCO et ne dérange pas l’astreinte. Ensuite il n’y a plus qu’à faire une dépendance entre ce trigger et ceux qui devront être en maintenance :

De cette manière, le vrai trigger ne sonnera pas quand le trigger « maintenance » sera en alerte, c’est à dire la nuit et le week-end.

Cette technique est plus efficace pour gérer un grand nombre de triggers sur la même plage de maintenance.

Zabbix : plusieurs seuils sur un seul trigger

Ce type de trigger est particulièrement utile pour superviser l’utilisation de ressources qui peuvent beaucoup varier d’un serveur à l’autre, comme le disque ou la RAM. Le concept est d’avoir un seuil qui s’adapte à la taille de la ressource.

Un exemple concret avec l’utilisation disque. Le but est d’avoir une alerte :

  • à 5% d’utilisation pour une partition de moins de 700 Go
  • à 35 Go pour une partition entre 700 Go et 1300 Go
  • à 50 Go pour une partition de plus de 50 Go

Le trigger sera le suivant :

({Template OS:vfs.fs.size[{#FSNAME},total].last(0)}<700000000000 and {Template OS:vfs.fs.size[{#FSNAME},pfree].max(#3)}<5) or ({Template OS:vfs.fs.size[{#FSNAME},total].last(0)}>=700000000000 and {Template OS:vfs.fs.size[{#FSNAME},total].last(0)}<=1300000000000 and {Template OS:vfs.fs.size[{#FSNAME},free].max(#3)}<35000000000) or ({Template OS:vfs.fs.size[{#FSNAME},total].last(0)}>1300000000000 and {Template OS:vfs.fs.size[{#FSNAME},free].max(#3)}<50000000000)

Ce trigger est un peu complexe et il est difficilement interprétable pour quelqu’un qui n’est pas familier avec Zabbix. Il faudra donc bien le documenter.

Ce trigger se découpe en trois parties principales (les trois seuils différents), elles mêmes découpées en plusieurs sous-parties.

  1. ({Template OS:vfs.fs.size[{#FSNAME},total].last(0)}<700000000000 and {Template OS:vfs.fs.size[{#FSNAME},pfree].last(0)}<5) : Si la taille de la partition est inférieure à 700 Go (la valeur dans le trigger est en octet), alors on sonne si il reste moins de 5% d’espace libre
  2. ({Template OS:vfs.fs.size[{#FSNAME},total].last(0)}>=700000000000 and {Template OS:vfs.fs.size[{#FSNAME},total].last(0)}<=1300000000000 and {Template OS:vfs.fs.size[{#FSNAME},free].last(0)}<35000000000) : Si la taille de la partition est comprise entre 700 Go et 1300 Go, on sonne en dessous de 35 Go d’espace libre
  3. ({Template OS:vfs.fs.size[{#FSNAME},total].last(0)}>1300000000000 and {Template OS:vfs.fs.size[{#FSNAME},free].max(#3)}<50000000000) : Si la taille de la partition est supérieure à 1300 Go, alors on sonne si il reste moins de 50 Go de libre

Il y a deux points sur lesquels il faut faire attention. D’abord il faut s’assurer que toutes les valeurs de taille de partition sont couvertes. Si par exemple je fais disque < X or disque > X, et bien un disque qui fait exactement X ne sera pas pris en compte. Et comme on a toujours tendance à arrondir les tailles de disque et les seuils, ça peut arriver plus vite qu’on ne le pense.

Ensuite, il est possible que ces seuils possèdent des effets de bord qui peuvent être contre intuitifs. Si je prends l’exemple ci-dessus et que je trace le seuil en % en fonction de la taille du disque :

On peut voir un saut à 1300 Go, qui correspond à la limite entre le deuxième et le troisième seuil. A utilisation disque égal, il est donc possible de faire sonner le trigger en augmentant la taille de la partition.

Linux : tester un port distant sans telnet/netcat/nmap

Une astuce vue sur blog.seboss666.info (licence CC BY-NC v3.0), et qui m’a déjà dépanné pas mal de fois.

Pour tester un port si aucun utilitaire n’est disponible pour le faire :

timeout 2 bash -c '</dev/tcp/1.2.3.4/443 && echo Port is open || echo Port is closed' || echo Connection timeout

Je ne suis moi-même pas sûr à 100% du fonctionnement de cette commande, donc n’hésitez pas à me le signaler si je dis n’importe quoi dans la suite de cet article.

Le dossier /dev/tcp/ permets d’envoyer des données directement sur la carte réseau. Aiinsi, si vous faites echo Pouet >> /dev/tcp/1.2.3.4/443, vous allez envoyer Les caractères Pouet à l’IP 1.2.3.4 sur le port 443.
Le caractère < juste avant sert à indiquer des données vides. La commande </dev/tcp/1.2.3.4/443 va donc avoir pour effet d’ouvrir une connexion sans rien envoyer, puis refermer la connexion.
&& est une condition. Ce qui suit sera exécuté uniquement si la commande précédente s’est terminée avec succès. Le && echo Port is open sert donc à afficher le message disant quele port ets ouvert si la connexion TCP a réussi.
|| est l’inverse de &&. C’est une condition qui permets d’exécuter une commande si la commande précédente est en erreur. Le || echo Port is closed permets donc d’afficher l’erreur si la requête TCP est rejetée.
À ce stade nous avons donc la commande suivante :

</dev/tcp/1.2.3.4/443 && echo Port is open || echo Port is closed

Mais il reste un dernier cas à gérer : c’est si la requête n’est ni acceptée, ni rejetée. Autrement dit, c’est le timeout.
La commande timeout sert à arrêter salement un programme au bout d’un certain temps. L’astuce consiste à utiliser la commande timeout 2 pour lancer la requête TCP. Si elle mets plus de deux secondes à s’exécuter, timeout arrête la requête avec une erreur, permettant à || echo Connection timeout de se lancer et d’afficher l’erreur.

Linux : savoir quand a été installé un serveur

Il n’y a pas de façon simple et fiable de savoir quand un ordinateur sous Linux a été installé. La technique la plus fiable que j’ai trouvé consiste à regarder la date de création du système de fichiers :

df / | awk '{print $1}' | grep /dev | xargs tune2fs -l | grep create

Il s’agit d’un enchaînement de plusieurs commandes. En voici une analyse :

  • df / : Affiche des infos sur la partition /
  • awk ‘{print $1}’ : Ne garde que la première colonne, qui contient l’emplacement de la partition / sur le disque dur (/dev/…)
  • grep /dev/ : Sert a supprimer la ligne d’en-tête
  • xargs : Permets de passer ce qui est affiché à l’écran en paramètre du script suivant (tune2fs dans notre cas). C’est indispensable, mais ça ne modifie aucune valeur.
  • tune2fs -l : Affiche des informations détaillées sur la partition.
  • grep create : Ne conserve que la ligne indiquant la date de création de la partition.

Chez moi, ça donne :

# df / | awk '{print $1}' | grep /dev | xargs tune2fs -l | grep create
Filesystem created: Tue Sep 18 09:56:24 2018

Ce qui correspond bien à la date d’installation de Linux sur mon ordinateur.

Il y a deux précautions à prendre avec cette commande :

  • Elle doit être lancée en root
  • S’il s’agit d’un serveur virtuel, cette commande retournera la date de création du modèle utilisé pour déployer le serveur. Ça peut potentiellement être beaucoup plus ancien que le serveur lui-même

Zabbix : superviser la fin de validité d’un certificat SSL

Le script

Pour superviser les certificats SSL, je vais utiliser l’external script suivant :

#!/bin/bash

# Affiche le timestamp correspondant à la fin de date de validité d'un certificat SSL

# Initialise les paramètres par défaut
DNS=$1         # DNS correspondant au vhost testé. Ce paramètre est obligatoire
IP=${2:-$DNS}  # IP du serveur testé. Si ce paramètre est manquant il prends la même valeur que $DNS
PORT=${3:-443} # Port du service testé. Si ce paramètre est manquant il prends la valeur par défaut 443

# Récupère la date de fin de validité du certificat, avec un résultat sous une forme du type : Feb 14 08:21:55 2019 GMT
ENDDATE=`echo | openssl s_client -servername $DNS -connect $IP:$PORT 2>/dev/null | openssl x509 -noout -enddate 2>&1 | cut -d '=' -f 2`

# Convertit la date en timestamp. Si ça échoute, retourne le timestamp actuel de manière à générer une alerte.
date -d "$ENDDATE" +%s 2>/dev/null || date +%s

exit 0
sslenddate.sh

Se script est à déployer sur le proxy Zabbix ou sur le master, dans le dossier des external scripts (valeur ExternalScripts dans le fichier zabbix_server.conf).

Il faut aussi rendre le script exécutable (chmod +x sslenddate.sh) et s’assurer que Zabbix ait les droits dessus (chown zabbix: sslenddate.sh)

Fonctionnement du script :
DNS=$1 : Le premier paramètre passé au script est le DNS à tester. C’est le seul paramètre obligatoire
IP=${2:-$DNS} : Le deuxième paramètre est l’IP sur laquelle se trouve le domaine à tester. Si ce paramètre est manquant, il prends la même valeur que $DNS
PORT=${3:-443} : Le troisième paramètre est le port sur lequel se trouve le domaine à tester. Si ce paramètre est manquant, il prends la valeur par défaut 443
On pourra remarquer ici l’utilisation de conditions ternaires en Bash. C’est bien plus court que de faire une série de if pour initialiser les variables.

openssl sépare l’IP de serveur de son DNS. Il est donc indispensable de séparer ces deux paramètres.

ENDDATE=`echo | openssl s_client -servername $DNS -connect $IP:$PORT 2>/dev/null | openssl x509 -noout -enddate 2>&1 | cut -d '=' -f 2` # Récupère la date de fin de validité du certificat, avec un résultat sous une forme du type "Feb 14 08:21:55 2019 GMT"

La ligne précédente est un enchaînement de commandes via des pipes. En voici une analyse détaillée :

  • echo | : Sert à envoyer une commande vide à openssl. Sinon, il va attendre indéfiniment que l’utilisateur n’entre une commande
  • openssl s_client -servername $DNS -connect $IP:$PORT : Affiche tous les détails du certificat SSL
  • 2>/dev/null : n’affiche pas les erreurs, s’il y en a. La gestion des erreurs se fera plus tard
  • openssl x509 -noout -enddate : n’affiche que la date de fin de validité du certificat, sous un format du type « notAfter=Feb 14 08:21:55 2019 GMT »
  • 2>&1 : Redirige les erreurs sur la sortie courante
  • cut -d '=' -f 2 : Supprime tout ce qui se trouve avant le premier « = », pour ne conserver que la date. À ce moment du script, la variable $ENDDATE doit contenir quelque chose du type Feb 14 08:21:55 2019 GMT. Ce format est très compliqué à traiter avec Zabbix. Je vais donc le convertir en timestamp
date -d "$ENDDATE" +%s 2>/dev/null || date +%s # Convertit la date en timestamp et l'affiche. Si ça échoue, affiche le timestamp actuel de manière à générer une alerte

Le résultat du script :

$ sh sslenddate.sh palc.fr 172.16.0.166 443 15:35
1550132515

Le timestamp 1550132515 correspond au 14 février 2019, ce qui correspond bien a la date de fin de validité du certificat actuel.

Si je fais un test invalide, j’obtiens le timestamp actuel :

$ sh sslenddate.sh example.com 92.243.24.84 443
1543415837

Ça permets de générer automatiquement une alerte si la date de fin de validité du certificat ne peut pas être déterminée.

L’item Zabbix

L’item Zabbix se paramètre comme suit :

Les paramètres importants sont :

      • Type : External Check
      • Key : sslenddate.sh[palc.fr,92.243.24.84,443]palc.fr correspond au DNS à tester, 92.243.24.8 à l’IP du serveur et 443 au port HTTPS. Seul le premier paramètre est obligatoires. Les deux derniers servent pour des cas spécifiques, par exemple si le certificat est sur plusieurs serveurs différents derrière un répartiteur de charge
    • Update interval (in sec) : 86400 ⇒ le test sera fait une fois par jour.

Cet item retourne le timestamp correspondant à la fin de validité du certificat SSL.

Le trigger Zabbix

Le trigger Zabbix se paramètre comme suit :

Ici j’utilise une opération mathématique entre deux valeurs. Il y a trois valeurs importantes :

  • {SV-TCA-EXCDGF03:sslenddate.sh[palc.fr,92.243.24.84,443].last(0)} : Le timestamp d’expiration du certificat SSL
  • {SV-TCA-EXCDGF03sslenddate.sh[palc.fr,92.243.24.84,443].now(0)} : La date actuelle
  • 2592000 : Le délai en secondes avant de générer l’alerte. Ça corresponds à 30 jours

Donc, si la date d’expiration du certificat est inférieure à la date actuelle plus trente jours, le trigger génère une alerte.

Il est possible de modifier le délai, ou de mettre plusieurs triggers avec des délais et des niveaux de sévérité différents, si besoin.