Puppet : Autorise %wheel à passer root sans mot de passe

Puppet offre plusieurs façons de manipuler /etc/sudoers : en remplaçant le fichier entier (via une ressource file), en éditant ou en ajoutant une ligne précise (via file_line) ou via la ressource augeas.

Méconnu, augeas est un utilitaire – non lié à Puppet – permettant de manipuler un grand nombre de types de fichiers avec une syntaxe similaire à https://fr.wikipedia.org/wiki/XPathXPath. Contrairement à file et file_line, il a l’immense avantage de ne pas provoquer d’erreur de syntaxe.

Voici ce que cela donne pour éditer /etc/sudoers :

augeas { 'sudoers-wheel-nopasswd':
  incl    => '/etc/sudoers',
  lens    => 'Sudoers.lns',
  changes => 'set spec[user=\'%wheel\']/host_group/command/tag NOPASSWD',
  require => Package['augeas'],
}

Requière d’installer préalablement le package augeas :

if ! defined(Package['augeas']) {
  package { 'augeas': ensure => present, }
}

A noter qu’il existe un module tout fait pour éditer sudoers : saz/puppet-sudo, lequel s’appuie sur Augeas.

Cyberduck : Désactiver les notifications Bonjour

Le client FTP/SFTP/WebDav Cyberduck (pour Mac uniquement) a cette fâcheuse manie d’afficher une notification à chaque fois qu’un ordinateur avec Bonjour activé est détecté. A la longue, cela devient pénible en entreprise (beaucoup de postes) avec des notifications sans arrêt.

notifications

Pour désactiver ce comportement, pas d’option dans les préférences, la seule solution est de passer par la ligne de commande :

defaults write ch.sudo.cyberduck rendezvous.enable false

Relancer ensuite Cyberduck.

Source : https://this-is-useful.blogspot.fr/2010/08/disable-bonjour-in-cyberduck.html

Source de l’image : https://groups.google.com/forum/#!topic/cyberduck/12CYvKR7M34

Nettoyer les sessions Gitlab < 7.3 dans la base Redis

Prior to GitLab 7.3, user sessions did not automatically expire from Redis. If you have been running a large GitLab server (thousands of users) since before GitLab 7.3 we recommend cleaning up stale sessions to compact the Redis database after you upgrade to GitLab 7.3.

Les versions de Gitlab entre la 6.2 et la 7.2 stockent leurs sessions dans une base Redis sans définir de date d’expiration. Conséquence : celles-ci vont s’accumuler au fil du temps sans aucun intérêt.

La documentation de Gitlab donne deux commandes :

# Voir toutes les sessions stockées dans Redis
redis-cli keys '*' | grep '^[a-f0-9]\{32\}$' | wc -l

# Faire expirer toutes les sessions dans dix minutes
redis-cli keys '*' | grep '^[a-f0-9]\{32\}$' | awk '{ print "expire", $0, 600 }' | redis-cli

La commande ci-dessus pose un problème : à chaque exécution, il repositionne la date d’expiration à 600 secondes. Et ce, même si la session allait expirée avant ! Impossible donc de l’exécuter telle quelle via une tâche cron.

Voici un script shell conçu pour être exécuté via cron. Il s’inspire de celui la documentation mais ignore toutes les sessions qui ont déjà une date d’expiration et définit pour les autres une échéance à +1 heure :

#!/bin/sh
#
# Avant Gitlab 7.3, les sessions ne possèdent pas de date d'expiration.
#
# Cela a pour conséquence une augmentation progressive de la base Redis
# sans aucune plus-value.
#
# Ce script se charge de définir une date d'expiration pour toutes les sessions
# dont le nom est formaté sous la forme de 32 caractères hexadécimales
#
# Le script fournit sur la documentation de Gitlab propose une expiration d'un quart
# d'heure, ce qui me semble trop court, le délai ici est donc d'une heure.
#
# Source :
# https://docs.gitlab.com/ce/administration/operations/cleaning_up_redis_sessions.sh
#

# Durée d'une session en secondes
SESSION_EXPIRE="3600"

rcli="$(command -v redis-cli)"

# Pour voir le nombre de sessions dans la base
# $rcli keys '*' | grep '^[a-f0-9]\{32\}$' | wc -l

# Expiration au bout d'une heure - 3600 secondes
$rcli keys '*' | grep '^[a-f0-9]\{32\}$' | while read LINE
do
  # Identifiant de la session
  key_id="$(echo $LINE | awk '{ print $0 }')"

  # Récupération de la date d'échéance correspondante
  key_ttl="$($rcli ttl $key_id)"

  # On ne prend que les sessions dont le TTL est -1 (= pas d'expiration)
  test "${key_ttl}" = -1 || continue

  echo "$key_id = $key_ttl"

  # Définit une date d'expiration
  $rcli expire "${key_id}" "${SESSION_EXPIRE}"
done

L’idéal étant évidemment de passer à une version plus récente de Gitlab 🙂

Docker CE et Fedora 26

D’après la documentation d’installation de Docker pour Fedora, il suffit d’ajouter le repository docker-ce-stable :

sudo dnf config-manager \
    --add-repo \
    https://download.docker.com/linux/fedora/docker-ce.repo

Mais si l’on installe ensuite le package docker-ce comme indiqué :

[simon@simon-laptop ~]$ sudo dnf install docker-ce
Dernière vérification de l’expiration des métadonnées effectuée il y a 0:00:06 le dim. 20 août 2017 10:25:11 CEST.
Aucun paquet docker-ce disponible.

Ce que la documentation omet de dire, au 20 août 2017, c’est que docker-ce n’est pas encore disponible dans la branche stable pour Fedora 26, il faut impérativement utiliser la branche testing (il n’est pas non plus disponible dans edge) :

sudo dnf config-manager --set-enabled docker-ce-test

Et ensuite, magie :

[simon@simon-laptop ~]$ sudo dnf install docker-ce
Dernière vérification de l’expiration des métadonnées effectuée il y a 0:00:06 le dim. 20 août 2017 10:33:41 CEST.
Dépendances résolues.
================================================================================
 Paquet              Architecture
                              Version                    Dépôt            Taille
================================================================================
Installation de :
 docker-ce           x86_64   17.07.0.ce-0.3.rc3.fc26    docker-ce-test    21 M
Installation des dépendances:
 container-selinux   noarch   2:2.21-1.fc26              updates           33 k

EWA de Matthieu Biasotto

51x2twpi0ol

Synopsis :

Elle s’appelle Ewa. Elle est particulière. Elle ne doit jamais se regarder dans un miroir. Jamais. Son don étrange est une malédiction qu’elle ne maîtrise pas. Son passé est difficile à porter. Il lui est impossible de partager son secret. Elle ne peut faire confiance à personne.

Enfermée « pour son bien » à Miedzeska, dans une pension pour filles au cœur de la Pologne, Ewa survit entre humiliations et sévices. Elle serre les dents en rêvant d’évasion, mais personne ne s’échappe de cet internat.

Alors pourquoi des filles disparaissent-elles sans laisser de traces ? Que deviennent-elles ? Et surtout… qui sera la prochaine ? Ewa ne doit jamais céder à l’appel des miroirs, elle le sait. Elle a juré. Et si la vérité se cachait dans son reflet ?

C’est probablement l’un des livres qui m’a le plus marqué de ces derniers mois. Impossible de décrocher, je l’ai lu en quelques jours. On croit comprendre, on croit savoir qui est « gentil », qui est « méchant » mais tout s’embrouille au fil du livre et la fin en est presque choquante – en tout cas surprenante. Je recommande à tous ceux qui aiment les thrillers avec une petite touche de fantastique 🙂

Petite note négative : à la fin, ce livre renvoi vers Persécutée, du même auteur, en le présentant comme faisant parti du même univers (« L’histoire nous en dira davantage dans Persécutée »). Je l’ai lu, la fin est aussi imprévisible mais j’ai moins accroché. On est dans la même famille Malinowski mais l’histoire a peu de rapport avec Ewa. Aucun fantastique dedans.

Lien (non sponsorisé) vers Amazon : EWA de Matthieu Biasotto

Script Powershell pour trier ses vidéos

Je possède près d’un 1 To de vidéos sur un disque externe… lequel est régulièrement plein avec les nouveautés.

Je cherchais donc un moyen de pouvoir trier les trier facilement en les ouvrant une à une dans VLC histoire de voir quelques extraits et me rappeler l’histoire puis m’afficher un popup pour la conserver (choix par défaut) ou l’effacer.

Voici un petit script Powershell que j’ai fait pour l’occasion :

[void] [System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

# Demande de sélectionner un répertoire
$OpenFolderDialog = New-Object System.Windows.Forms.FolderBrowserDialog
$OpenFolderDialog.SelectedPath = [System.Environment+SpecialFolder]::UserProfile
$OpenFolderDialog.ShowDialog()
$SearchPath = $OpenFolderDialog.SelectedPath

# Parcours récursif du répertoire
$SearchDirInfo = New-Object System.IO.DirectoryInfo $SearchPath
$SearchEnumerator = $SearchDirInfo.EnumerateFiles("*", [System.IO.SearchOption]::AllDirectories).GetEnumerator()

foreach ($Result in $SearchEnumerator)
{
    # Affiche le chemin dans la console
    Write-Host "Found" $Result.FullName

    # Ouvre VLC
    & 'C:\Program Files\VideoLAN\VLC\vlc.exe' $Result.FullName | Out-Null

    # Pose la question de le conserver
    $Answer = [System.Windows.Forms.MessageBox]::Show( `
        [string]::Concat("Conserver ", $Result.Name, " ?"), `
        "Conserver le fichier ?", `
        [System.Windows.MessageBoxButton]::YesNo,
        [System.Windows.Forms.MessageBoxIcon]::Question,
        [System.Windows.Forms.MessageBoxDefaultButton]::Button1
    )

    if ($Answer -eq [System.Windows.MessageBoxResult]::Yes)
    {
        Write-Host "A garder !"
    }
    elseif ($Answer -eq [System.Windows.MessageBoxResult]::No)
    {
        Write-Host "A supprimer !"
        Remove-Item -Verbose $Result.FullName
    }
    else
    {
        Write-Error "Oops, réponse invalide"
        break
    }
}

Résultat, je le lance depuis l’explorateur (clic-droit > Exécuter avec Powershell), il me demande de sélectionner le répertoire de départ :

Capture-1

Puis, pour chaque vidéo, m’ouvre VLC pour que je visionne quelques extraits et, à la fermeture, pose la question de la conservation :

Capture-2

Si le script est ouvert depuis une invite Powershell, on peut voir clairement le chemin du fichier et s’il a était conservé ou supprimé :

Capture-2

Bonus : J’avais fait il y a quelques temps un script équivalent en shell, moins évolué et non documenté (pas drôle sinon). Il utilise zenity pour afficher le dialogue et indique également la date de création du fichier. Le voici :

#!/bin/sh
find "${PWD}" -type f | sort -R | while read -r f
do
    vlc --no-qt-error-dialogs  "${f}" >/dev/null
    dc="$(stat "${f}" | sed 's/\\/\\\\/')"
    if ! zenity --question --text="Conserver '${f}' ?\nDate de création : ${dc}" --no-wrap
    then
        rm -fv "${f}"
    fi
done

Utilisation :

cd /mnt/usb && sh script.sh

Puppet : contain vs include

Si votre code Puppet s’organise avec des rôles et des profiles (ce qui est très fortement recommandé), il y a de fortes chances que vous ayez du code ressemblant à ceci :

class roles::webserver {
   include profiles::base
   include profiles::apache
}

A l’exécution, surprise, l’ordre d’exécution n’est pas forcément celui attendu. Dans l’exemple ci-dessus, profiles::apache pourrait très bien passer avant.

Pour forcer l’ordre, on va donc utiliser des chaining arrows :

class roles::webserver {
   include profiles::base
   include profiles::apache

   Class['profiles::base'] ->
   Class['profiles::apache']
}

Cette approche se voit régulièrement et pose pourtant un problème de taille : l’ordre ne pas récursif, autrement dit il ne s’applique pas aux include que contiennent profiles::base et profiles::apache.

Ainsi, si profiles::apache inclut lui-même apache, il est tout fait possible que apache vienne s’exécuter avant ce qu’il y a dans profiles::base.

La bonne approche est d’utiliser le mot-clé contain qui, couplé avec les chaining arrows, garanti l’ordre d’exécution comme attendu (d’abord profiles::base puis profiles::apache) :

class roles::webserver {
   contain profiles::base
   contain profiles::apache

   Class['profiles::base'] ->
   Class['profiles::apache']
}

Note : si votre code est bien fait, la question de l’ordre d’exécution ne devrait pas se poser. Lorsque vous écrivez votre code, pensez à bien mettre un require/notify sur toutes les ressources qui dépendent de quelque chose.

Petite astuce pour vérifier que les dépendances (require et notify) sont correctement définies, exécuter Puppet avec l’option ordering à random. Avec cette option, Puppet « secoue tout » et, à chaque exécution, change l’ordre (en tenant compte des dépendances, heureusement :)). rspec-puppet a aussi un réglage pour activer ce comportement.