Bon, ce qu’il faut comprendre c’est le fonctionnement de awk. Un script awk c’est un ensemble de block {}, ceux-ci sont tous* évalués pour chaque ligne (ça peut se modifier si on veut mais dans l’énorme majorité des cas c’est des lignes). Avant chaque bloc on défini la condition de son exécution. On peut définir tout et n’importe quoi comme condition. Dernier truc important les blocs sont évalués dans l’ordre où ils apparaissent.
Donc pour ‘/^Explanation:/{exit}{print $0}’ On a 2 bloc et une condition. Pour être plus claire on peut le présenter ainsi :
/^Explanation:/
{
exit
}
{
print $0
}
Le premier bloc fait que lorsqu’awk rencontre une ligne qui commence par “Explanation:”, celui-ci s’arrête.
Le second fait que pour toute les lignes il les réécris sur sa sortie standard.
Le fait que les blocs soit évalués en séquence, fait qu’il va réécrire toute les lignes et qu’au moment où il rencontre la première ligne qui commence par “Explanation:”, il s’arrête avant de recopier cette ligne.
A noter que la condition /^Explanation:/ est un raccourcis de $0 ~ /^Explanation:/
Pour ce qui est de “NR<54”, NR c’est le numéro de la ligne donc le bloc qui suit est exécuté pour les numéros de lignes inférieur à 54. Le bloc n’existe pas ? Qu’à cela ne tienne awk considère que c’est le bloc {print $0}.
En écrivant cet explication j’ai trouvé une forme un peu plus courte de script awk pour remplacer :
par :
L’idée consiste alors à avoir une condition 1 qui est toujours vrai et qui est suivi d’aucun bloc donc de {print $0}.
Les blocs peuvent être précédé de BEGIN ou END ce qui signifie qu’ils s’exécute avant ou après le reste.
Bien que je n’aie pas encore tout digéré de tes explications, juste une précision :
awk est un langage à part entière et pas à proprement parler une commande bash : exact ?
le fait de placer ‘awk’ en tête de ligne, indique à bash que la ligne est écrite dans ce langage ?
L’un empêche pas l’autre, mais oui c’est un langage, tu peut écrire des scripts awk sans passer par un shell avec le shaban :
Non le shell traite tout de la même manière sauf ces propres fonction built-in. awk est une programme comme un autre (au même titre que bash ou grep), il prend en paramètre un ensemble d’arguments (-F par exemple) suivi d’une chaine qui contient le script (c’est pour ça qu’on le met entre guillemet) et éventuellement d’un (ou plusieurs) nom de fichier.
je reprends le script pour savoir si j’ai bien compris :
“set -e” = arrête en cas d’erreur
suivent 2 déclarations de variables
puis une sauvegarde
jusqu’à là, no problem, mon neurone enregistre mais arrive la ligne principale de “awk”:
je me demande comment fait bash (ou awk) pour chercher une ligne commençant par “Explanation:” quand il ne sait pas encore où chercher ?
C’est l’ordre qui m’interpelle : “cherche une ligne qui commence par “Explanation:”, arrête-toi avant, place dans ta mémoire tout ce qui précède et imprime tout ça dans /etc/apt/preferences” …
À quel moment on lui dit où chercher tout ça ?
Je comprendrais parfaitement si “$pref” était au début, il saurait où chercher.
Tout ce que je dis, c’est de la logique syntaxique française mais je sais que la logique informatique réagit différemment.
[quote=“ricardo”]je me demande comment fait bash (ou awk) pour chercher une ligne commençant par “Explanation:” quand il ne sait pas encore où chercher ?
C’est l’ordre qui m’interpelle : “cherche une ligne qui commence par “Explanation:”, arrête-toi avant, place dans ta mémoire tout ce qui précède et imprime tout ça dans /etc/apt/preferences” …
À quel moment on lui dit où chercher tout ça ?[/quote]
Je pense que je me suis mal exprimé, pour chaque ligne il regarde si chaque bloque correspond. Donc au début pour chaque ligne lue, le premier bloc ne correspond pas et le second oui. A la première ligne qui coorespond à Explanation, il exécute le bloc et n’exécute donc plus les autres parce qu’il exécute un exit.
Mais comme je suis motivé et que tu semble avoir du mal à repérer la limite entre le shell et awk une explication plus détaillée seras intéressante pour toi et d’autres visiteurs.
Alors la ligne c’est :
C’est le shell qui la lie. Lui il voit le >, donc pour lui cette ligne est de la forme :
Et il a la charge pour écrire tout ce qu’il lis de la sortie standard de la commande dans le fichier.
Ensuite il regarde la commande :
Ici il voit 3 mots :
[ul]
[li]awk[/li]
[li]’/^Explanation:/{exit}{print $0}’[/li]
[li]"$pref"[/li][/ul]
Le premier est la commande (c’est toujours comme ça même s’il vérifie que ce n’est pas une variable un alias, une fonction du shell ou une commande builtin), donc il va chercher un fichier nommé awk dans tout les dossiers qui se trouve dans la variable PATH. Au premier qu’il trouve, il le lance en lui donnant comme paramètre :
[ul]
[li]awk[/li]
[li]’/^Explanation:/{exit}{print $0}’[/li]
[li]"$pref"[/li][/ul]
(oui oui, il lui donne “awk” aussi)
A partir de ce moment là le shell a fait son boulot c’est awk qui entre en piste.
Awk, on va dire qu’il ignore son nom (ça ne nous intéresse pas ici), puis il vois donc les deux paramètres :
[ul]
[li]’/^Explanation:/{exit}{print $0}’[/li]
[li]"$pref"[/li][/ul]
Comme il n’a aucun paramètre qui commence par - il sait (c’est une convention on peut lire la description de celle-ci dans le man) que l’argument numéro 1 est le script a exécute, ensuite les arguments qui viennent après sont les fichiers à lire.
Voila pour le boot de la commande.
Ensuite vient l’algo. A ce moment là il sait quel fichier lire, il sait qu’il doit le lire ligne par ligne et il connait le code de son script.
Pour chaque ligne lue, il commence par vérifier si la ligne commence par Explanation, si c’est le cas il s’arrête.
Sinon il prend le bloc suivant et vérifie sa condition, elle renvoie vrai donc il l’exécute et écris la ligne sur sa sortie standard (à ce moment là le shell va la lire et l’écrire dans le fichier pointé par > dans la commande).
Ça c’est de l’explication “pour les nuls”, comme je les aime
Remarque bien que j’avais compris tout le processus de recherche ligne par ligne et ce que je viens d’apprendre, et qui m’interrogeait, c’est la lecture ENTIÈRE de la commande mais je comprends à l’instant que si j’avais bien regardé, je n’aurais pas eu à poser la question :
je n’avais pas prêté attention au ‘’ qui entourent cette commande.
Je la détaillais et c’est pour ça qu’elle me semblait illogique.
Enfin, ton cours est tout simplement magistral.
Qu’en penses-tu Michel, ne devrait-on pas déplacer ce fil dans “programmation”.
Au départ, il ne s’agissait que d’une question simple mais comme elle a “dégénéré” en cours …
on pourrait changer le titre en ‘awk quelque chose’
EDIT :
Testé = fonctionne parfaitement mais pense à modifier le pastbin avec ‘ref’, il ne connais pas
Ça mérite que tu fasses une page sur le wiki, je ne suis ptet pas le seul à être intéressé.
Ça fait longtemps que je me dis régulièrement que c’est le genre de truc qu’il faut publié (bien avant la mise en place de notre wiki).
C’est difficile d’être didactique et de savoir par où commencer. Si les débutants voient ça comme une montagne infranchissable faire apprendre le shell (en vrai j’ai vu des cours dessus pas terribles) et apriori aussi complexe.
Merci du compliment. Je publierais ça sans doute ce soir sur le wiki.
Ne pas oublier les "sudo"
Je viens de tester ‘in vivo’ et j’ai ajouté, pour moi, la mise à jour des paquets.
Super pour un fainéant comme moi (script = “maj” ; placé dans ~/bin) :
$ F12 (yakuake)
$ maj
$ pass sudo
Pourrais-tu faire un ls -lA /etc/apt/preferences juste après l’exécution de ton script, ricardo ?
Sauf erreur de ma part il devrait appartenir à ton user, pas à root, ce qui est un problème de sécurité (cf. mon message besoin-une-commande-simple-t35835-25.html#p361765 dont le principe s’applique aussi à ce cas).
En effet, il crée le fichier temporaire avec ton compte utilisateur, puis les commandes sudo ne font que le modifier sans toucher aux droits.
De manière générale, il vaut mieux éviter de mélanger des commandes utilisateur et des commandes root (sudo) dans un même script, mais plutôt exécuter tout le script en root : sudo maj plutôt que maj tout court.
Ou alors tu modifies la ligne tmpfile=$(sudo mktemp) mais c’est quand même moyen :
pour qui voudrait réutiliser ton script, tout le monde n’a pas sudo
ton script réside dans ton dossier utilisateur, il est donc modifiable facilement… il suffit de rajouter des commandes sudo à la fin pour qu’à la prochaine mise à jour tu les exécutes sans t’en rendre compte
Mieux vaudrait enlever tous les sudo à l’intérieur de ton script ; le déplacer dans /usr/local/bin ; # chown root:root /usr/local/bin/maj ; # chmod a+rx,a-w,u+w /usr/local/bin/maj ; enfin tu peux l’appeler avec $ sudo maj ou # maj sans risques de trous de sécurité.
Oui, tu as raison dans tout mais encore une fois, au départ, je voulais en faire un truc perso.
Mes scripts perso sont dans ~/bin mais tous en 700
pour le retour de …preferences, il est bien passé à ricardo mais en 600, ce qui représente déjà une certaine sécurité.
Sinon, on peut très bien ajouter une ligne “chown+chmod” à la fin du script pour le rétablir comme à l’origine : root en 600 ou 644, je ne me souviens plus du coup
Ça ne change rien au problème, si un programme malicieux parvient à s’exécuter sous ton compte utilisateur tu es marron (peu de chances que ça arrive, je te l’accorde, mais on ne base pas la sécurité d’une machine sur des statistiques : du moment que ça reste possible c’est mal).
C’est bien du 644 sur ma machine.
Mais comme je le disais, au pire vaut mieux mettre un sudo sur le mktemp, comme ça le fichier temporaire est créé directement avec les droits root et seul root a le pouvoir de le modifier. Seuls soucis restants : les deux points que j’ai cités précédemment.
Par contre si tu fais juste un chown/chmod à la fin du script, tu as une troisième faiblesse : une race-condition (attaque qui nécessite un bon timing pour fonctionner), même si contrairement à mon exemple précédent elle n’est exploitable que par ton utilisateur et non plus par n’importe quel utilisateur de ta machine.
Ça peut te paraître dérisoire cette histoire de race-conditions car il s’agit d’une fenêtre de quelques millisecondes, mais pour travailler dans un domaine où j’ai souvent besoin de recourir à la programmation parallèle, je peux te dire que si une telle faiblesse existe, alors elle est exploitable très facilement.
Après, tu fais ce que tu veux de mes conseils : je ne fais que t’informer des problèmes de sécurité que ton script provoque, libre à toi de peser le pour et le contre…
Tes conseils ne me dérangent pas du tout, au contraire, moi je suis l’élève qui essaie de comprendre.
Je vais modifier avec sudo sur mktmp.
Pour les scripts perso dans ~/bin, je crois ne pas être le seul, il me semble que Michel pratique aussi comme ça et c’est PascalHambourg qui me l’avait indiqué. Ce dernier n’est pas un “léger” en matière de sécu, il me semble.
Je rappelle que je suis seul sur mes machine et qu’en l’occurrence, il ne s’agit pas d’un serveur.
[quote=“ricardo”]Pour les scripts perso dans ~/bin, je crois ne pas être le seul, il me semble que Michel pratique aussi comme ça et c’est PascalHambourg qui me l’avait indiqué. Ce dernier n’est pas un “léger” en matière de sécu, il me semble.
Je rappelle que je suis seul sur mes machine et qu’en l’occurrence, il ne s’agit pas d’un serveur.[/quote]
J’ai plein de scripts perso aussi dans ~/bin, mais aucun qui nécessite des droits root. Ceux là je les mets dans /usr/local/(s)bin et je les chown/chmod de telle manière que seul root puisse les modifier.
Le problème c’est que si root exécute un script qu’un autre utilisateur peut modifier, ça veut dire que cet autre utilisateur a, de fait, un moyen non sécurisé pour faire ce qu’il veut en root (encore une fois, l’exemple ici c’est de rajouter une commande sudo arbitraire à la fin de ton script). Dès lors, il suffit de se choper un troyen sur son compte utilisateur pour que le système entier soit compromis (enfin, je dis troyen… ça pourrait aussi bien être un accès SSH, un accès direct à une session laissée ouverte, ou que sais-je).
Comme tu me l’as souvent répété (à raison), il est souvent beaucoup plus facile d’obtenir des droits utilisateur que les droits root… sauf que dans ce cas de figure précis, droits utilisateur = accès root complet au prochain lancement du script “maj”.
Je t’accorde que quand c’est un poste mono-utilisateur les risques sont fortement réduits, mais sans vouloir faire mon casse-couilles je trouve ça un peu paradoxal de rechercher des rootkits sur sa machine (qui ont besoin pour s’installer soit d’un accès root soit d’une faille de sécurité du kernel pour pouvoir élever leurs privilèges) tout en laissant des failles “béantes” comme ça (accessibles depuis un simple compte utilisateur).
[quote=“ricardo”]line 12: /tmp/tmp.oMORXk7tCN: Permission non accordée
il s’agit de la ligne de commande awk[/quote]
Le problème vient du fait que dans un même script tu jongles entre des commandes root et des commandes utilisateur.
Soit tu mets des sudo à chaque commande, soit tu vires complètement les sudo de ton script et tu le lances avec sudo maj. (pas taper )
@syam > En vrai c’est quoi la différence entre les sudo dans le script ou de lancer le scripts avec sudo ? Parce que si d’un coté tu crains que quelqu’un modifie le script tu va le sentir passer d’un coté comme de l’autre. D’un coté comme de l’autre l’important c’est les droit sur le fichier avec rien pour les autres (et éventuellement pour le groupe pour les paranos) et pas de droit d’exécution ni d’écriture sur le dossier pour les autres (et pour le groupe pour les paranos).
Mais sinon je ne vois pas la différence entre l’un et l’autre entre ajouter un sudo rm -rf et ajouter rm -rf l’attaquant s’adaptera.
Pour ce qui est de la race condition, d’un j’ai du mal à voir comment l’utiliser facilement. Il faut un notify sur le bon répertoire et prier pour que l’ordonanceur te choisisse. Mais sur tout qu’est ce qui pourrait arriver ? Par défaut l’attaquant peut lire le fichier, c’est ce qu’il y a aussi dans la configuration par défaut lors d’une installation si je ne me trompe pas. Le problème serait un droit d’écriture. Mais sinon en principe les paquets sont signés donc tenter un man in the middle c’est loin d’être trivial.
Sinon pour faire plus joli que mettre un sudo partout et ne pas mettre de sudo avant la commande et ne pas utiliser le sticky bit il y a ça :
if [[ $EUID -ne 0 ]]; then
exec sudo $0
exit -1
fi
En fait si, parce que sinon c’est du nihilisme. Il a était démontré que pour tout système, il existe un virus. C’est mathématiques ça ne repose pas sur les implémentation de l’époque. Au contraire ce concentrer sur les attaques plausibles, avant de regarder les attaques éventuelles c’est classique et c’est normal.
On est bien d’accord, c’est pour ça que je lui conseillais plutôt de sécuriser les droits de son script, ce qui résout entièrement le problème des éventuelles modifications non souhaitées.
(Pour répondre à la question, la seule différence entre les sudo dans le script ou de lancer le script avec sudo c’est la facilité d’écriture et de compréhension du script (moins il est chargé, plus il est lisible). Ta suggestion avec un exec sudo $0 est d’ailleurs très bien à ce niveau là.)
J’en ai vu de toutes les couleurs avec du code multithread / multiprocess, je peux te garantir que ça passerait sans souci. Peut-être pas du premier coup, mais ça finirait par passer. L’ordonnanceur finit toujours par choisir le mauvais thread / process, et généralement beaucoup plus souvent que ce qu’on pourrait croire (sauf bien sûr quand on veut déboguer le bazar, loi de Murphy oblige ).
Et avant que tu ne me reparles de statistiques, j’apporte une précision : un attaquant, s’il n’a pas mieux, peut très bien compter sur les statistiques pour réussir son attaque car il a tout à y gagner ; l’administrateur d’une machine ne peut pas se permettre de compter sur les statistiques pour se défendre contre les attaques, sauf bien sûr s’il n’existe aucune autre solution, car il a tout à y perdre.
En l’occurrence, dans cette situation précise tu as raison, c’est difficile. Mais on sait bien comment c’est, quand on trouve un motif (pattern) qui semble marcher on a tendance à le répéter. Autant prendre des bonnes habitudes dès le départ, ça évite de se faire avoir par la suite.
C’est une question de principe de fonctionnement plus que de situation précise, si tu préfères cette formulation : on n’utilise pas un motif qui est connu pour être problématique, même si dans une situation précise les problèmes sont limités ; on préférera utiliser un motif qui n’a pas de faiblesses et qui ne demandera souvent pas plus de réflexion à mettre en place (à condition de le connaître à l’avance bien sûr, ou que quelqu’un te l’explique).
En fait si, parce que sinon c’est du nihilisme. Il a était démontré que pour tout système, il existe un virus. C’est mathématiques ça ne repose pas sur les implémentation de l’époque. Au contraire ce concentrer sur les attaques plausibles, avant de regarder les attaques éventuelles c’est classique et c’est normal.
J’aime laisser les drosophiles en paix.[/quote]
Là on ne sera jamais d’accord : je ne dis pas de rechercher systématiquement toutes les failles possibles, mais quand on en trouve une on se doit de la corriger surtout quand la solution est évidente. En l’occurrence, un script comme ça (chown utilisateur, exécution root) permet une élévation de privilèges complètement triviale à réaliser une fois qu’on a accès à une session de l’utilisateur, c’est tout simplement une mauvaise pratique. Qu’on n’y pense pas lors de l’écriture est une chose, ça arrive même aux meilleurs, mais qu’on balaye ça d’un revers de main une fois qu’on l’a vu en se disant « de toutes façons je ne risque presque rien » ou autres « ça n’arrive qu’aux autres », ça va à l’encontre de toutes les règles de sécurité les plus élémentaires.
M’enfin ce n’est que mon avis, chacun voit midi à sa porte hein.
J’en ai vu de toutes les couleurs avec du code multithread / multiprocess, je peux te garantir que ça passerait sans souci. Peut-être pas du premier coup, mais ça finirait par passer. L’ordonnanceur finit toujours par choisir le mauvais thread / process, et généralement beaucoup plus souvent que ce qu’on pourrait croire (sauf bien sûr quand on veut déboguer le bazar, loi de Murphy oblige ).[/quote]
Je pense que ça n’a rien à voir pour la simple raison que c’est un évènement ponctuel.
Tu as raison, je n’avais pas vu cette proposition :
Je me suis donc rangé à ton point de vue Syam.
J’ai rajouté un alias comme proposé par Michel
Testé = OK
Je pense qu’il doit en être de même de mon script de sauvegarde
Il serait ptet judicieux que je le transfère aussi vers /usr/local/bin , en en supprimant les 3 ou 4 références à sudo