Cron : le planificateur

Cron : le planificateur

J’aurais préféré “Cron : l’héritage” pour la blague, mais ça n’avait aucun sens vis-à-vis que ce qu’est cron… Et ça c’est quand même cron comme situtation !

Deuxième article depuis le retour de vacances… et je n’ai toujours pas envie de trop me fouler. On continue donc sur la lancée de la dernière fois avec un sujet court qui se digère assez bien.

Aujourd’hui on va parler de la planification des tâches sous Linux avec éventuellement quelques apartés sur son pendant chez Microsoft.

Chérie, t’as pensé au magnétoscope ?

Dans un passé lointain que les moins de vingt ans ne peuvent pas connaître, on n’avait pas de streaming ni même de Blu-Ray ou de DVD et on reposait sur des supports physiques à bande magnétique appelés VHS. Pour lire une VHS il fallait un magnétoscope et cet appareil permettait également d’enregistrer un programme qui passait à la TV. Utile non ?

Dans ce même passé lointain, je me foutais bien de la gueule des gens qui, accros à leur série du samedi soir, ne pouvait se permettre de les manquer pour rien au monde. Heureusement pour eux, ils n’étaient pas prisonniers de leur salon le samedi soir pour autant car un grand génie a un jour eu l’idée de permettre la programmation en avance des heures de début et de fin d’enregistrement pour le magnétoscope, éventuellement même avec une périodicité.

Ainsi, si Tony partait en vacances 3 semaines à Ibiza avec ses parents friqués, il pouvait programmer d’avance un enregistrement de 20h à 20h40 tous les samedis pour ne pas manquer son épisode de Derrick ! Bon, évidemment, tout cela était dans la limite de la bande magnétique disponible : si le mec part 3 mois au bout d’un moment y’a plus de place sur la cassette !

Et sur nos machines alors ?

Pour le même genre de raison, à savoir l’existence d’opérations qu’il ne faut surtout pas manquer ou de tâches récurrentes qu’on souhaite lancer à telle heure tel jour de la semaine ou toutes les 10 minutes, etc. on a eu la bonne idée d’ajouter des programmes dont c’est le job de suivre la date et l’heure de votre machine et de lancer automatiquement les bonnes choses au bon moment. C’est le travail de planification.

Sur vos machines Linux, le processus responsable de cette tâche est ‘cron’. Sur Windows, vous avez accès au planificateur de tâches.

Accès à la commande CRONTAB

Avant de passer aux commandes à proprement parler, sachez que planifier des tâches n’est pas forcément un droit de tous les utilisateurs. Crontab, qui permet de gérer les fichiers de configuration de cron, peut reposer sur un système de whitelist si le fichier /etc/cron.allow existe. Il liste alors à raison d’un login par ligne les seuls utilisateurs autorisés à utiliser la commande ‘crontab’. On peut à l’inverse avoir un système de blacklist si le fichier /etc/cron.deny existe. Il listera alors les utilisateurs qui ont interdiction d’utiliser la commande.

En l’absence de ces deux fichiers, les systèmes sous Debian et Ubuntu autorisent tous les utilisateurs à se créer une crontab et à l’administrer, mais ce n’est pas le cas de toutes les distributions de Linux.

LISTER ses cron JOBS

Pour lister les tâches qui ont un déclenchement automatique prévu pour votre login vous pouvez utiliser la commande ‘crontab -l’ :

> crontab -l
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

00 06 * * * /usr/bin/backup website >/dev/null 2>&1
00 06 * * * /usr/bin/backup utils >/dev/null 2>&1

Vous constaterez que j’ai pour ma part deux jobs planifiés. On reviendra sur les lignes qui commencent par SHELL et PATH à la fin de cet article.

Notez que vous pouvez également voir les “chrono tables” (ce qui a donné crontab par abréviation, littéralement les tables du temps) des autres utilisateurs si vous avez les droits sudo sur cette commande en utilisant l’option -u <login> :

> sudo crontab -l -u games
no crontab for games

Alors revenons à nos deux tâches planifiées qu’on avait plus haut. Comment doit-on lire chaque ligne ?

Vous remarquerez cinq champs avant la ligne de commande. Dans l’ordre, ils signifient minutes, heure, jour (numéro), mois, jour (dans la semaine 0 = dimanche puis on va jusqu’à 6). Utiliser une étoile pour un de ses champs veut dire “pour tous”.

Donc ici on a par exemple : tous les jours, à 6h du matin, lance la ligne de commande ‘/usr/bin/backup website >/dev/null 2>&1’. Idem pour le second cas.

Attention à ne pas abuser des ‘*’ sur les premiers champs, ce n’est pas un joker ça veut bien dire “pour tous”. Donc si on préfixe avec “* * * * * 01”, on dit clairement “le lundi exécute la commande chaque minute”… pas sûr que ce soit vraiment ce qu’on souhaitait planifier.

Il est possible de donner une plage en utilisant un tiret ‘-‘ et une autre valeur en utilisant une virgule ‘,’. Ainsi, “01,31 04,05, 1-15 1,6 *” (oui ça devient illisible, seuls les espaces font foi pour séparer les champs) veut dire : du 1er au 15 janvier et du 1er au 15 Juin , la commande se lance à 04:01, 04:31, 05:01, 05:31.

Il est recommandé d’utiliser, comme on le voit dans mon exemple, le chemin absolu des commandes qu’on veut utiliser. On écrira donc par exemple ‘/usr/bin/ls’ et non pas simplement ‘ls’. Pourquoi ? Eh bien pour des raisons de sécurité mais, comme d’habitude, on s’en reparle à la fin de l’article. Notez que si vous ne connaissez pas le chemin absolu d’une commande, l’utilitaire which vous donnera la réponse :

> which ls
/usr/bin/ls

On se fait un peu de structure avancée pour le préfixe ? Cron met à notre disposition un raccourci très utile quand on veut définir une récurrence absolue de notre commande, par exemple “toutes les 20 minutes”. Pour ce faire, il suffit d’utiliser un ‘*/<période>’ à la place de l’item en question. Ainsi “*/20 * * * *” (toutes les 20 minutes) est équivalent à “0,20,40 * * * *” mais beaucoup plus concis, surtout si on est sur une fraction non entière d’une heure, par exemple “toutes les 31 minutes” (je vous laisse faire la version non raccourcie pour l’exercice… bon chance) !

Autre chose très utile : cron met à notre disposition des alias pour adresser des temporalités spécifiques :

AliasÉquivalent
@yearlyUne fois par an à minuit le 1er Janvier ; “0 0 1 1 *”
@annualyPareil que @yearly
@monthlyUne fois par mois à minuit le 1er ; “0 0 1 * *”
@weeklyUne fois par semaine à minuit le dimanche ; “0 0 * * 0”
@dailyUne fois par jour à minuit ; “0 0 * * *”
@midnightPareil que @daily
@hourlyUne fois par heure à l’heure pile “0 * * * *”
@rebootUne fois à chaque démarrage de la machine

Bon ces alias, bien qu’utiles (surtout le @reboot qui ne peut pas être défini avec la syntaxe standard à cinq éléments), ne sont pas toujours ce qu’il vous faut.

De fait vous aurez peut-être noté que mes cron jobs n’utilisent pas la syntaxe @daily mais ont été faits à la main, tout simplement parce que je suis souvent encore en train de coder à minuit et que je n’ai pas envie que mes backups aient lieu au milieu d’une modification. J’ai donc choisi 6h du matin, heure à laquelle je suis rarement actif sur ma machine… parce que je dors !

Modifier sa chrono table

Pour modifier une chrono table, il faut utiliser la commande ‘crontab’ mais cette fois-ci avec l’option ‘-e’. Notez que la première fois il vous demandera quel éditeur de texte vous souhaitez utiliser. Saisissez le numéro correspondant à votre choix et validez avec enter.

> crontab -e

Select an editor.  To change later, run 'select-editor'.
  1. /usr/bin/joe
  2. /usr/bin/jstar
  3. /usr/bin/jpico
  4. /usr/bin/jmacs
  5. /bin/nano        <---- easiest
  6. /usr/bin/vim.basic
  7. /usr/bin/rjoe
  8. /usr/bin/vim.tiny
  9. /usr/bin/emacs

Choose 1-9 [5]:

Encore une fois, par défaut cela ouvrira la crontab associée à votre login, mais vous pouvez choisir celle d’un autre utilisateur avec l’option ‘-u’ et les droits sudo.

Notez que crontab, à l’instar de visudo, vérifie la cohérence de votre saisie avant de vous laisser quitter l’éditeur. Par exemple si je demande littéralement de lancer un job “tous les 36 du mois” :

0 0 36 * * echo "Coucou les amigos"

<tentative de "sauvegarder + quitter">

crontab: installing new crontab
"/tmp/crontab.KRBDPK/crontab":17: bad day-of-month
errors in crontab file, can't install.
Do you want to retry the same edit? (y/n)

LA “GROSSE” crontab

Notez que votre machine dispose d’un fichier plus global ‘/etc/crontab’ et éventuellement de sous-fichiers placés dans ‘/etc/cron.d/’. A l’instar de ce qui se fait avec le fichier des sudoers vous ne devriez jamais éditer ‘/etc/crontab’ car il peut être écrasé au moment de l’upgrade votre système. Toutefois, il est sans risque (d’écrasement) de créer un fichier cron dans ‘/etc/cron.d’.

La particularité de ces crontabs là c’est qu’elles ne sont pas associées à un utilisateur précis puisqu’elles sont définies au niveau du système complet. Cela a plusieurs implications :

  • On peut les éditer directement sans passer par la commande ‘crontab’ et on peut donc les casser (car aucune vérification de syntaxe ou de cohérence ne sera faite)
  • On doit préciser entre le préfixe d’horodatage et la commande le nom de l’utilisateur que le système doit émuler pour lancer la commande

Par exemple, dans ‘/etc/cron.d/test’ on trouvera peut-être “@daily games /usr/bin/test” qui veut en fait dire “chaque jour à minuit, lance la commande ‘sudo -u games /usr/bin/test’ (rappel sur l’utilisation de sudo ici).

Il est recommandé de ne jamais créer de fichiers dans ‘/etc/cron.d/’ et, encore une fois, de ne jamais éditer ‘/etc/crontab’ directement. Si vous avez besoin qu’une commande soit lancée par cron au nom d’un utilisateur, il est recommandé de créer une entrée dans la cron table propre à ce dernier. Pour reprendre l’exemple ci-dessus, on mettra donc plutôt “@daily /user/bin/test” dans la crontab de l’utilisateur ‘games’ via la commande ‘sudo crontab -u games -e’.

Cron et sécurité

Le moment que vous attendiez tous (ou pas) est arrivé ! On parle des risques et implications en termes de sécurité.

PATH

PATH est ce qu’on appelle une variable d’environnement. On n’en a pas encore parlé mais je ferai sans doute un article futur sur cette notion. Retenez simplement que ça désigne un ensemble de variables dont le périmètre peut varier : session utilisateur, shell actuellement ouvert, etc. et qui est partagée par tout le monde dans ce périmètre.

$PATH (posez pas la question sur le ‘$’, encore une fois on en reparlera) est utilisée pour savoir où chercher les commandes que vous tapez. Je m’explique. On a vu plus haut que ‘ls’ appelle en fait ‘/usr/bin/ls’. Le système utilise $PATH pour savoir où chercher lorsqu’on tape uniquement le nom de la commande.

Si on reprend ce qu’on avait dans ma crontab, on peut lire “PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin” (ici le périmètre de la variable $PATH est restreint aux seules commandes qui seront lancées par cron via cette chrono table ; notez que le ‘:’ est un séparateur). Cela veut dire qu’en cas de commande tapée sans son chemin absolu, le système va chercher un fichier exécutable qui a ce nom dans /usr/local/sbin, puis s’il n’a rien trouvé il passera à /usr/local/bin, etc. jusqu’à chercher dans /usr/bin.

SUBSTITUER UN EXECUTABLE

Il existe des risques de sécurité inhérents à l’utilisation des chrono tables. Comme d’habitude, l’attaquant veut principalement obtenir un shell avec les droits de root. Lorsqu’il s’attaque aux crontabs, l’idée de base est toujours la même : exploiter quelque chose qui va se lancer automatiquement pour parvenir à ses fins.

On va généralement tirer profit d’une commande qui sera exécutée avec les droits root, ce qui peut être le cas si elle est dans la crontab de root, dans /etc/crontab (ou un sous-fichier de /etc/cron.d) avec root mis en utilisateur ou (cas à ne jamais sous-estimer) si elle est dans la crontab de n’importe qui mais que l’exécutable en question appartient à root et a son SUID placé.

Dans le dernier cas (SUID), si c’est une faille exploitable cela veut dire qu’on va modifier l’exécutable en question pour y placer notre déploiement de shell root. Donc on parle d’un fichier appartenant à root, dont le SUID est placé, modifiable par tout le monde et qui est présent dans une crontab… autant vous dire que la personne qui a mis ça en place cherche la merde !

Pour les deux autres cas c’est différent. Il peut s’agir encore une fois d’un exécutable modifiable par tout le monde (mauvaise idée si c’est dans la crontab de root, soyez vigilant) ou (et là la digression sur PATH va prendre tout son sens) d’une exploitation de la variable $PATH. En effet, les premiers chemins de $PATH sont testés en premier. Et il serait donc bien qu’il ne soit pas trop simple d’écrire dans ces répertoires, parce que sinon votre attaquant pourra y placer un script à lui qui porte le bon nom de fichier et hop : c’est son script que cron va lancer au lieu de la tâche planifiée.

Ce dernier cas est plus courant qu’on ne le croit : les gens partagent une machine et créent un dossier à sa racine pour mettre les exécutables maison qu’ils développent (/scripts ou /utils par exemple). Comme son utilisation est partagée, on le rend open bar en lecture/écriture. Comme on veut que ces outils maison soient toujours exécutés en priorité, on édite les $PATH de tout le monde pour qu’il soit “/utils:<les autres chemins>”. Vous sentez venir le truc qui pue ?

Les exploitations de $PATH ne sont pas cantonnées à cron, elles sont possibles à tout niveau de votre système et il faut donc toujours être vigilant. Toutefois, il y a une parade très simple à cela : utilisez des chemins absolus.

Eh oui, je ne vous ai pas dit ça pour rire tout à l’heure. Si vous utilisez un chemin absolu, le système n’a pas besoin de chercher l’exécutable et n’utilise donc pas la variable $PATH. C’est fastidieux à chaque fois que vous tapez une ligne de commande – et c’est pour ça qu’on ne le fait jamais – mais dans le cadre d’un fichier de configuration – de crontab à tout hasard – cela ne vous coûtera que quelques secondes de votre temps et pourrait vous éviter des problèmes sur le long terme. Pensez-y !


One response to “Cron : le planificateur”

  1. Tifred Avatar

    C’est bien, tu laisses le choix de l’éditeur sans donner ton avis. Même si vim est le meilleur

Leave a Reply

Your email address will not be published.