Recherche Administrateur/trice Systèmes

Au sein du Service Opérations de la DSI de l'Université Lyon 2, nous cherchons un ou une Administrateur/trice Systèmes pour renforcer notre équipe et nous aider à relever des défis au quotidien.
Lieu de travail : campus de Bron (arrêt de tram T2 Europe Université).

  • Vous habitez la région Lyonnaise ou êtes mobile ;
  • Vous êtes motivée par les enjeux de la gestion d’un parc de plus de 600 serveurs Linux RedHat/CentOS (70%), Windows, FreeBSD ;
  • Les problématiques d’une ferme de virtualisation multi-site avec balance de charge, PRA, sauvegardes croisées ne vous font pas peur ;
  • Les infrastructures à fort enjeu de disponibilité, les outils d’automatisation, de monitoring et les SIEM vous intéressent ;
  • Vous êtes passionnée par les problématiques système et souhaitez évoluer dans un environnement riche et varié ;
  • Vous êtes curieuse, très rigoureuse et vous avez le sens du service.

Si vous vous reconnaissez dans ce profil, contactez-moi !

terminal - édition d'un script shell

Édition d'un script shell dans le terminal

Related posts

Recherche Administratrice/teur Systèmes en alternance

Au sein du Service Opérations de la DSI de l'Université Lyon 2, nous cherchons un ou une Administrateur/trice Systèmes en alternance pour nous aider à relever des défis au quotidien.
Lieu de travail : campus de Bron (arrêt de tram T2 Europe Université).

  • Vous habitez la région Lyonnaise ;
  • Vous êtes motivée par les enjeux de la gestion d’un parc de plus de 400 serveurs Linux RedHat/CentOS (70%), Windows, FreeBSD ;
  • Les problématiques d’une ferme de virtualisation multi-site avec balance de charge, PRA, sauvegardes croisées ne vous font pas peur ;
  • Les infrastructures à fort enjeu de disponibilité, les outils d’automatisation, de monitoring et les SIEM vous intéressent ;
  • Vous êtes passionnée par les problématiques système et souhaitez évoluer dans un environnement riche et varié ;
  • Vous êtes curieuse, très rigoureuse et vous avez le sens du service.

Si vous vous reconnaissez dans ce profil, contactez-moi !

terminal - édition d'un script shell

Édition d'un script shell dans le terminal

Related posts

Rotation des clés DKIM

La signature des emails par clé DKIM est une composante importante d'un système de messagerie sain et bien géré. Comme tout système cryptographique fonctionnant sur la base d'un couple clé publique / clé privée, il est nécessaire de renouveler les jeux de clés régulièrement.
En effet, plus longtemps une même clé est utilisée, plus elle a de chance d'être cassée. De la même manière, plus elle est petite, plus elle sera facilement cassée.
Comme toujours, avec la correspondance par email, rien n'est instantané. Contrairement à un serveur web par exemple qui pourrait changer de certificat SSL en quelques minutes et faire immédiatement disparaître l'ancien de la circulation, les clés DKIM doivent changer selon un roulement tuilé : quand la nouvelle clé remplace l'ancienne, cette dernière doit rester disponible encore quelques temps pour permettre la validation des signatures présentes dans des messages encore en cours de livraison.
Ainsi, le besoin de faire tourner régulièrement les clés DKIM tout en garantissant un tuilage, ajouté à la complexité (relative) d'une architecture mixte à base de serveur DNS, serveur de mail, logiciel de signature DKIM, rendent nécessaire la mise en place d'une procédure claire, fiable et automatique.

Évolution de l'utilisation de DKIM mesurée au niveau de l'antispam en entrée de mon serveur de mails

Évolution de l'utilisation de DKIM mesurée au niveau de l'antispam en entrée de mon serveur de mails, entre fin 2005 et début 2015

Pour mes propres besoins, j'ai conçu un script shell utilisable pour faire tourner les clés d'un domaine donné. Ce script s'appuie sur un serveur Bind local : la modification des zones DNS se fait par modification directe des fichiers. Il utilise l'implémentation DKIM de OpenDKIM.
Il serait tout à fait possible de concevoir un script qui utilise un serveur DNS distant ou local via la commande nsupdate(8), mais cette approche laisse la main totalement à Bind pour l'écriture des fichiers de zones. Il fait alors disparaître tous les commentaires, il change l'ordre des enregistrements, etc. Ce n'était pas souhaitable pour moi qui stocke des informations en commentaire dans mes fichiers de zones.

Prérequis pour utiliser ce script

- Être root sur le serveur ;
- Disposer d'un serveur DNS Bind, ou compatible (c'est OpenDKIM qui écrit les enregistrements DNS), maître pour les zones concernées par la rotation de clés ;
- Disposer sur la même machine d'une installation OpenDKIM fonctionnelle ;
- Avoir une version de sed qui supporte l'option -i (in place).

Comportement

Le premier argument obligatoire du script est l'action : renouvellement ("new") ou nettoyage ("clean"). Le second argument obligatoire est le domaine cible et le troisième argument (facultatif) est la longueur des nouvelles clés.
Pour l'action "new" et un domaine donné, le script renouvelle l'ensemble des clés DKIM. Par défaut, il génère des clés de la même longueur que celles qu'il remplace, et les sélecteurs sont nommés en `date "+%Y%m"` suivi d'un tiret et des 8 premiers caractères retournés par la commande uuidgen(1). Si une longueur de clé est imposée en argument, l'ensemble des clés du domaine utilisera cette nouvelle valeur. Pour l'action "clean" et un domaine donné, le script vidange les anciennes clés.
Pour assurer un tuilage d'une semaine par exemple, il faut donc le jour J lancer le script avec "new", et à J+7 lancer le script avec "clean" sur le même domaine. Ainsi, avant le jour J, la clé N est utilisée pour signer les messages. À partir du jour J, la clé N+1 est utilisée pour signer les messages, mais la clé N est encore disponible pour vérifier de vieux messages qui seraient encore en circulation. À partir de J+7, l'ancienne clé N disparaît.

Syntaxe des fichiers de zone DNS

Deux contraintes ici :
- Le numéro de série de la zone doit être seul sur sa ligne et suivi du commentaire " ; Serial" pour que le script puisse facilement le localiser et l'incrémenter.
- Les enregistrements DNS des clés DKIM doivent être en inclusion dans le fichier de la zone et résider respectivement dans le fichier dkim-active.${DOMAIN} pour les clés actives et dans le fichier dkim-retired.${DOMAIN} pour les clés inactives. Cela donne pour le fichier de zone patpro.net :

...
$INCLUDE "/etc/namedb/master/dkim-active.patpro.net"
$INCLUDE "/etc/namedb/master/dkim-retired.patpro.net"
...

Ainsi, le script peut très facilement déplacer les clés actives vers le fichier dkim-retired quand de nouvelles clés sont fabriquées et intégrées à dkim-active.

OpenDKIM

Les clés doivent être rangées par domaine, dans des répertoires qui portent le nom du domaine. Cela donne chez moi :

opendkim/patpro.net/201501-62e79011.private
opendkim/patpro.net/201501-62e79011.txt
opendkim/patpro.net/201501-82a3edc5.private
opendkim/patpro.net/201501-82a3edc5.txt
opendkim/proniewski.net/201502-e47af46c.private
opendkim/proniewski.net/201502-e47af46c.txt

Les clés utilisées doivent être référencées dans un fichier KeyTable unique que le script analyse pour retrouver les sélecteurs à remplacer, et réécrit avec les nouveaux sélecteurs.

Utilisation

crontab pour renouvellement automatique mensuel :

0 0 1 * *    /usr/local/sbin/rotate-dkim.sh new patpro.net
0 0 8 * *    /usr/local/sbin/rotate-dkim.sh clean patpro.net

ligne de commande pour changer la taille des clés d'un domaine :

rotate-dkim.sh new proniewski.net 2048

Corpus Scripti

#!/usr/local/bin/bash
# rotate DKIM keys
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# settings
ACTION=${1}
DOMAIN=${2}
NBITS=${3}; NBITS=$(( $NBITS+0 )) # force number
CHROOT=/var/db/opendkim
GENKEY=/usr/local/sbin/opendkim-genkey
KeyTable=/usr/local/etc/opendkim/KeyTable
ZONEROOT=/etc/namedb/master
USERGROUP=root:opendkim
ARCHIVE=/tmp/
MAXKEY=4096
MINKEY=512

onerror() {
case "$1" in
	"args")
		echo "usage: $(basename $0) new|clean domain.name [bits]"; exit 1
		;;
	"chroot")
		echo "\$CHROOT must exist and be a directory"; exit 1
		;;
	"genkey")
		echo "opendkim-genkey not found or not executable"; exit 1
		;;
	"ktable")
		echo "KeyTable not found"; exit 1
		;;
	"zoneroot")
		echo "\$ZONEROOT must exist and be a directory"; exit 1
		;;
	"usergroup")
		echo "User and group must exist"; exit 1
		;;
	"archive")
		echo "\$ARCHIVE must exist and be a directory"; exit 1
		;;
	"nbits")
		echo "Bits length must be greater than $MINKEY, and should not be greater than $MAXKEY"; exit 1
		;;
esac
}

# Checks
[ $# -ge 2 ] || onerror "args"
[ ${ACTION} = "new" -o ${ACTION} = "clean" ] || onerror "args"
[ -d ${CHROOT} ] || onerror "chroot"
[ -d ${ZONEROOT} ] || onerror "zoneroot"
[ -d ${ARCHIVE} ] || onerror "archive"
[ -x ${GENKEY} ] || onerror "genkey"
[ -f ${KeyTable} ] || onerror "ktable"
pw usershow ${USERGROUP/:*/} >/dev/null 2>&1 || onerror "usergroup"
pw groupshow ${USERGROUP/*:/} >/dev/null 2>&1 || onerror "usergroup"
if [ $NBITS -ne 0 ]; then
	[ $NBITS -gt $MINKEY ] || onerror "nbits"
	[ $NBITS -le $MAXKEY ] || onerror "nbits"
fi


archive() {
	echo "Putting away files for old DKIM selector ${1}"
	mv -v ${CHROOT}/${DOMAIN}/${1}.* ${ARCHIVE}
}

if [ ${ACTION} = "new" ]; then
	# Keytable: update SELECTOR
	# structure: 
	# arbitrary-name signing-domain:selector:keypath
	# for each line matching DOMAIN: 
	# - get SELECTOR. 
	# - key path derived from CHROOT/SELECTOR

	# get SELECTOR for DOMAIN:
	SELECTORLIST=( $(awk '/^[^#].* '${DOMAIN}':/ {split($2,a,":"); print a[2]}' ${KeyTable}) )

	# for each SELECTOR we must:
	# - destroy/archive private key 
	# - create a new SELECTOR + key + DNS
	# - rewrite KeyTable
	echo ""
	echo "Selector list for domain $DOMAIN has ${#SELECTORLIST[*]} member-s, lets renew each one:"
	for OLDSELECTOR in ${SELECTORLIST[@]}; do
		if [ $NBITS -ne 0 ]; then
			echo "Bits length set on command line to ${NBITS}, overriding current private key bits length"
			BITS=${NBITS}
		else
			echo -n "Get bits length for private key ${OLDSELECTOR}.private: "
			BITS=$(openssl rsa -in ${CHROOT}/${DOMAIN}/${OLDSELECTOR}.private -text -noout | awk '/Private-Key/ {gsub(/[^0-9]/,"",$2); print $2}')
			echo "$BITS"
		fi		
		echo -n "Create new selector: "
		UUID=$(uuidgen)
		SELECTOR="$(date "+%Y%m")-${UUID/-*/}"
		echo "${SELECTOR}"
		echo "Create new private key with ${SELECTOR}:"
		${GENKEY} -v -b ${BITS} -d ${DOMAIN} -D ${CHROOT}/${DOMAIN} -r -s ${SELECTOR}
		# permission adjustments:
		chown ${USERGROUP} ${CHROOT}/${DOMAIN}/${SELECTOR}.*
		chmod u=r,g=r ${CHROOT}/${DOMAIN}/${SELECTOR}.*
		ls -l ${CHROOT}/${DOMAIN}/${SELECTOR}.*
		echo -n "Update KeyTable: "
		sed -i .bkp -e 's/'$OLDSELECTOR'/'${SELECTOR}'/g' ${KeyTable}
		[ $? -eq 0 ] && echo "OK"
		archive ${OLDSELECTOR}
	done
	echo ""
	echo "End of selector renewal."
	# every SELECTOR for DOMAIN is updated, now deal with DNS
fi

echo ""
# calculate new serial for zone $DOMAIN
echo -n "Get old DNS zone serial for ${DOMAIN} and increment: "
NEWSERIAL=$(date +%Y%m%d%I)
OLDSERIAL=$(awk '/ ; Serial/ {print $1}' ${ZONEROOT}/${DOMAIN})
while [ $OLDSERIAL -ge $NEWSERIAL ]; do
	let NEWSERIAL=(NEWSERIAL+1)
done
echo "$OLDSERIAL -> $NEWSERIAL"
#
# Update SERIAL of DNS zone.
echo -n "Change serial in zone file: "
sed -i .bkp -e 's/'$OLDSERIAL' ; Serial/'${NEWSERIAL}' ; Serial/g' ${ZONEROOT}/${DOMAIN}
[ $? -eq 0 ] && echo "OK"

if [ ${ACTION} = "new" ]; then
	echo -n "Move old public keys to dkim-retired.${DOMAIN}: "
	cat ${ZONEROOT}/dkim-active.${DOMAIN} > ${ZONEROOT}/dkim-retired.${DOMAIN}
	[ $? -eq 0 ] && echo "OK"
	echo -n "Copy new public keys to dkim-active.${DOMAIN}: "
	cat ${CHROOT}/${DOMAIN}/*.txt > ${ZONEROOT}/dkim-active.${DOMAIN}
	[ $? -eq 0 ] && echo "OK"
elif [ ${ACTION} = "clean" ]; then
	echo -n "Clean old public keys of dkim-retired.${DOMAIN}: "
	echo "" > ${ZONEROOT}/dkim-retired.${DOMAIN}
	[ $? -eq 0 ] && echo "OK"
fi

echo ""
echo "Everything looks good, lets refresh world:"
named-checkzone ${DOMAIN} ${ZONEROOT}/${DOMAIN}
service named restart
[ ${ACTION} = "new" ] && service milter-opendkim restart

Les commentaires dans le code sont suffisants pour expliquer chaque étape du déroulement du script.

Bibliographie

DKIM
M3AAWG DKIM Key Rotation Best Common Practices (PDF)

Related posts

Création de mots de passe en ligne de commande

keychainLa création de nouveaux mots de passe est une activité assez peu passionnante, d'autant que dans certains contextes on peut être amené à répéter l'opération plusieurs fois par jour. Plutôt que d'avoir à réfléchir - et donc de produire un résultat influencé par les travers de l'esprit humain - il est préférable de s'en remettre totalement à la machine.
L'aléa est bien meilleur, et le résultat est obtenu bien plus facilement. Il est bien sûr question ici de vrais mots de passe : large palette de caractères, longueur correcte, totalement aléatoires.

Voici deux fonctions assez simples qui trouveront leur place tout naturellement dans votre .bashrc ou .bash_profile sur *BSD (FreeBSD, Mac OS X, etc.). La seule condition étant de disposer des commandes jot et rs déjà présentées dans les articles "jot et seq, créer des séquences en ligne de commande" et surtout "Les bons mots de passe…".

Cette première fonction crée 5 mots de passe aléatoires dont les caractères sont pris entre les codes ASCII 32 (espace) et 126 (tilde). Par défaut, ces mots de passe font 25 caractères de long, mais la commande génère en fait un tirage de 500 caractères, ce qui permet de fabriquer 5 mots de passe de 100 chiffres, lettres, symboles en indiquant la longueur souhaitée comme paramètre. L'astuce ici tient dans l'utilisation de la tabulation comme séparateur en sortie de jot, ce qui permet d'avoir l'espace présent dans les mots de passe en sortie.

mdp-complexe() {
jot -s $'\t' -r -c 500 32 126 | rs -c$'\t' -g0 0 ${1-25} | head -5
}

Exemple d'utilisation :

$ mdp-complexe 
fnc\Y}02PSc6}{;0`T}LOeN?\
5W Y1Y8!o<VlTA2vuE}SU)g?]
'kGhRa=u|hJW#;6aDd[A&UR_/
n|$_f3S?[`pO2":+0e<;aTi\v
.nU\i.PhbPE5TfuXY)g+DiN~g

En imposant la longueur :

$ mdp-complexe 50
9Tu1StsL $Gk@K<X\R)xKv{JXYe|g>L^2T;|*GMASee@HX.Epj
T%2 N;VvLFVi]s>_~xpo^~vD8b2.Fb$02ay9sLgjW#,TQ>8JAO
7EyWUC)Cqm2F,72mIf$#vh3<sTx%j>*v't{Fump=Fb5cJeZir$
RfS638d~a.T#StF.t3%Z*`RG)/?#; H?liH2DielVvs#4IBp0a
G`OyIOzpYtt5Sh!92/zdp7^C98r.OtIV8Fk_ilYCvVI6~e@17T

Cette seconde fonction est basée sur la première, mais un filtrage est imposé à la sortie de jot pour ne garder qu'un sous-ensemble des caractères générés. Ici, les caractères a-z, A-Z, 0-9, _, / et - sont conservés. L'approche est relativement simple : il suffit d'ajouter à la liste entre [ ] les caractères que l'on souhaite conserver dans les mots de passe :

mdp-simple() {
jot -s $'\t' -r -c 500 32 126 | sed "s,[^a-zA-Z0-9_/-]"$'\t'",,g"|\
 rs -c$'\t' -g0 0 ${1-25} | head -5
}

Ce qui donne :

$ mdp-simple 
y0q/EKoZKN9ZqGGhr1triaB0l
VMFUNYNpopAu9NleSv0BxuuTh
W07eE4ef1A99yAl0UmP6EHo6_
9X4CWx7qgML6qxn9Y0nxhONpr
lw_eY4DYY414mrllNxe/FSUfM

Stockez ensuite le mot de passe choisi dans votre trousseau de mots de passe sécurisé.

Related posts

Créer un trou noir DNS

Même si la question a déjà été largement traitée, je livre ici ma propre version d'un trou noir DNS. Le DNS blackhole, en anglais dans le texte, permet par exemple de s'assurer (dans une certaine mesure) que les utilisateurs de votre réseau ne peuvent pas se connecter trop facilement à des sites web vecteurs d'infection.
Le fonctionnement est simple : vous avez un réseau local, chez vous ou dans votre entreprise, et vous mettez à disposition de vos utilisateurs un serveur DHCP pour que les équipements qui se connectent puissent obtenir une adresse IP. Je pars du principe que sur ce réseau local, vous disposez aussi d'un serveur DNS. Un utilisateur se connecte avec votre réseau : le serveur DHCP lui donne une adresse IP pour que la nouvelle machine puisse parler sur le réseau, et fourni aussi l'adresse de votre serveur DNS. C'est donc par ce serveur que vous maitrisez que la machine de l'utilisateur va convertir les noms de domaine (www.patpro.net) en adresse IP (193.30.227.216).
Comme vous avez la maîtrise du serveur DNS, vous avez la maîtrise de la résolution des noms de domaine. Vous pouvez donc décider de bloquer la résolution de certains noms de sites web qui posent problème (gros pourvoyeurs de malware, régies publicitaires, facebook, etc.).

Je vais me baser ici sur la liste de malwaredomains.com qui regroupe environ 20000 noms de domaine qui sont, ou ont été, impliqués dans la distribution de malware (en général en infectant le visiteur imprudent). Il s'agit donc pour moi de défendre mon réseau en protégeant les utilisateurs.

Les informations sont largement inspirées - voire pompées - de la prose anglophone de Paul.

Pré-requis : un serveur DNS BIND que vous maîtrisez, et un réseau avec serveur DHCP qui indique l'adresse IP du DNS, de sorte que les clients qui se connectent utiliseront par défaut le serveur DNS sus-mentionné. Il faut bash, curl, et jot (sur linux il faut remplacer jot par seq).

Obtenir la liste des zones DNS à mettre en trou noir.

Préparer le lieu de stockage :

mkdir /etc/namedb/blackhole

J'utilise le script suivant, à lancer en root via une crontab (une fois par semaine suffit) :

#!/usr/local/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
ZONEFILE=/etc/namedb/blackhole/spywaredomains.zones
MIRRORS[1]="http://mirror1.malwaredomains.com/files/spywaredomains.zones"
MIRRORS[2]="http://mirror2.malwaredomains.com/files/spywaredomains.zones"
MIRRORS[3]="http://dns-bh.sagadc.org/spywaredomains.zones"

# fonctionne sur BSD uniquement, pour linux il faut
# remplacer par `seq`
CHOOSE=$(jot -r 1 1 ${#MIRRORS[@]})

if [[ "$(/usr/local/bin/curl ${MIRRORS[CHOOSE]} -z ${ZONEFILE} -o ${ZONEFILE}_new -s -L -w %{http_code})" == "200" ]]; then
	rm -f ${ZONEFILE}_old
	mv ${ZONEFILE} ${ZONEFILE}_old
	mv ${ZONEFILE}_new ${ZONEFILE}
	named-checkconf -z | grep -v ": loaded serial"
	if [ ${PIPESTATUS[0]} -eq 0 ]; then
		service named restart
	else
		echo "Error loading new zone file, reversing..."
		mv ${ZONEFILE} ${ZONEFILE}_invalid_$(date "+%Y%m%d")
		mv ${ZONEFILE}_old ${ZONEFILE}
	fi
fi

C'est loin d'être foolproof, mais ça fait le travail. Les options de curl permettent de ne télécharger le fichier que si il a été modifié depuis votre dernière mise à jour. le script choisit un miroir au hasard au moment de faire le comparatif ce qui évite de charger toujours le même serveur.

Configurer BIND pour gérer les zones téléchargées.

Il faut ajouter dans la configuration du serveur DNS une directive d'inclusion du fichier téléchargé ci-dessus. Il suffit pour cela d'ajouter la ligne suivante à la fin du fichier /etc/named/named.conf :

 include "/etc/namedb/blackhole/spywaredomains.zones";

Le fichier spywaredomains.zones fait pointer chaque zone vers un unique fichier de définition qu'il convient de créer et de renseigner : /etc/namedb/blockeddomain.hosts.
Ce fichier contient une définition minimaliste de zone DNS BIND. Faites-la pointer vers un serveur web existant, cela vous permettra d'afficher à vos utilisateurs une page web d'explication :

$TTL    86400   ; one day
@       IN     SOA    NOM-DE-VOTRE-SERV-DNS. NOM-DE-VOTRE-SERV-DNS. (
	1
	28800   ; refresh  8 hours
	7200    ; retry    2 hours
	864000  ; expire  10 days
	86400 ) ; min ttl  1 day

	NS     NOM-DE-VOTRE-SERV-DNS.

	       A       IP-DE-VOTRE-SERVEUR-WEB
*	IN     A       IP-DE-VOTRE-SERVEUR-WEB

Si vous ne souhaitez pas informer vos utilisateurs, remplacez simplement IP-DE-VOTRE-SERVEUR-WEB par 127.0.0.1. Mais je recommande tout de même la publication d'une page web d'information sur la quelle les utilisateurs pourront arriver si ils tentent de joindre un domaine présent dans la liste noire.

Avec ça, votre serveur DNS est transformé en trou noir pour l'ensemble des domaines répertoriés chez malwaredomains.com. En analysant les logs de votre serveur web, vous pourrez aussi déterminer rapidement qui sur votre réseau est infecté ou risque de l'être.

Références.

edit : correction d'une coquille dans le script

Related posts

S’envoyer des SMS multilignes via l’API de free-mobile

Free vient de mettre à disposition de ses abonnés Free-mobile un moyen simple de générer via une API accessible en ligne des SMS à soi-même. L'utilisation est globalement très simple et tient presque toutes ses promesses (actuellement il ne semble pas possible de faire du POST, seul le GET fonctionne).
Il suffit aux intéressés de se rendre sur leur interface de gestion de compte Free-mobile, dans la section "Mes options", et d'y activer (vers le bas de la page) l'option ad-hoc. Une fois l'option activée, Free vous gratifie d'un mot de passe dédié à cet usage, et vous renseigne un peu sur le fonctionnement du service :

free-mobile-notification

Une fois l'option activée, l'utilisation se résume à une requête HTTP de cette forme :

curl 'https://smsapi.free-mobile.fr/sendmsg?user=***&pass=***&msg=coucou'

Vous recevez alors sur votre téléphone Free-mobile le SMS contenant le message "coucou".

La création d'un message multiligne est un peu plus délicate. La plupart des plateforme Unix/Linux envoie en guise de séparateur de ligne un \n qui se traduit par %0a une fois "url-encodé". Malheureusement, l'API n'accepte pas ce %0a et le message sera tronqué à la première occurrence.
Par contre, le séparateur de ligne \r, encodé en %0d est bien accepté par l'API Free-mobile. Il faut donc, avant d'envoyer un message sur plusieurs lignes, traduire les \n en \r.

J'ai conçu un script shell pour mes besoins personnels. Il prend ses données en entrée standard, par exemple via un pipe :

echo "mon message" | sms.sh

Ce qui est plus souple pour moi dans bien des situations, que de devoir invoquer le script et de lui servir le message comme argument, par exemple :

autresms.sh mon message

Voici donc le code de mon script qui prend les messages via stdin, et qui converti les \n en \r. Notez que le SMS à l'arrivée ne contient pas de saut de ligne, donc prévoyez des espaces en fin de ligne si vous ne voulez pas que la fin de ligne soit collée au début de la ligne suivante.

#!/usr/local/bin/bash  
#
# push notification via smsapi.free-mobile.fr
#
LOGGEROPT="-p security.notice -t SMS"
REPLOGG=1
REPMAIL=1
MAILTO=root
USR="****"
PSW="****"
URL="https://smsapi.free-mobile.fr/sendmsg"
CURL=/usr/local/bin/curl

report() {
        [ ${REPLOGG} -eq 1 ] && echo $@ | logger ${LOGGEROPT}
        [ ${REPMAIL} -eq 1 ] && echo $@ | mail -s "SMS $@" ${MAILTO}
}

eval $(${CURL} --insecure -G --write-out "c=%{http_code} u=%{url_effective}" \
        -o /dev/null -L -d user=${USR} -d pass=${PSW} --data-urlencode msg="$(cat|tr '\n' '\r')" ${URL} 2>/dev/null | tr '&' ';')

echo ${c} ${u}\&pass=***\&msg=${msg} | logger ${LOGGEROPT}

case ${c} in
        "200")
                exit 0
                ;;
        "400")
                report "parameter missing" ; exit 400
                ;;
        "402")
                report "too many SMS..." ; exit 402
                ;;
        "403")
                report "service unavailable for user, or wrong credentials" ; exit 403
                ;;
        "500")
                report "server error, try later" ; exit 500
                ;;
        *)
                report "unexpected result" ; exit 600
                ;;
esac

Utilisation :

echo "mon message 
sur plusieurs 
lignes" | sms.sh

Il reste à faire :
- ajouter l'enregistrement dans les log système de chaque utilisation du script sms.sh
- ajouter la gestion des codes de retour HTTP autres que 200.

Attention
Pour bénéficier d'une gestion d'erreur et d'une trace dans les log de la machine j'ai utilisé une grosse ruse très sale avec eval. Cela permet de créer des variables à la volée à partir de données fournie par l'utilisateur. C'est éminemment risqué car tout ce est qui généré par c=%{http_code} u=%{url_effective} sera évalué sans distinction (avec potentiellement des exécutions de code).
Pour un système de monitoring c'est un risque négligeable : les messages sont connus et fixés par le logiciel qui les génère, sans interaction avec l'utilisateur. Pour une utilisation dans tout autre mécanisme la prudence est requise.

Related posts

Les bons mots de passe…

jtr-crossword-10-donation_design Donner des conseils en matière de mot de passe, c'est délicat. Ça implique en général de croire un minimum dans le système de protection par mot de passe. Et comme toute la sécurité en général, le mot de passe est un compromis, et comme tout compromis, il est donc faillible. Je crois personnellement de moins en moins au principe du mot de passe pour protéger des informations vraiment vitales. Sans doute suis-je encouragé dans cette impression par des démonstrations récentes de casse de mots de passe complexes-mais-humainement-utilisables.
Je ne prendrai qu'un exemple : des mots de passe aussi complexes en apparence que "momof3g8kids" ou "qeadzcwrsfxv1331" peuvent être cassés en quelques heures.
En fait, dès l'instant où vous enregistrez un mot de passe sur un site web vous n'avez aucune maitrise (ni information en général) sur la qualité du stockage de ce mot de passe. Souvent, mais pas toujours, le mot de passe est chiffré, ou hashé. Mais rien ne vous garantit que cette protection est suffisante. Compte tenu de la puissance des machines et logiciels actuels, je doute sincèrement qu'elle le soit.
La probabilité pour qu'on casse votre mot de passe par des tentatives d'authentification successives sur votre compte hotmail est extrêmement faible. Par contre, celle qu'un pirate vole la base de mot de passe du forum d'équitation auquel vous êtes inscrit n'est pas négligeable (piratage d'un serveur de jeu en ligne de RockYou : plus de 32 millions de mots de passe aspirés - stockés en clair...).
Une fois la base de mots de passe dérobée, le pirate n'a plus qu'à s'y attaquer, si ils ne sont pas directement stockés en clair bien sûr. Quelques heures après en général, il aura cassé un échantillon non négligeable de mots de passe, dont chacun est associé à des données intéressantes telles que login, adresse email, etc.
À partir de là, le pirate va pouvoir tester ces mots de passe / login / emails pour se connecter à des sites comme facebook, gmail, hotmail, apple store, etc.
En 2010 je conseillais d'utiliser des mots de passe par type de risques, ce qui évitait d'en avoir un différent pour chaque site tout en cloisonnant un peu les choses. Depuis, je suis un peu plus désabusé. Avoir une grosse poignée de bons mots de passe ne semble plus suffisant. Je me garderai aussi à présent de donner quelque conseil que ce soit tant le problème a pris une ampleur hors de tout contrôle (PRISM, etc.).

Voici ce que je fais depuis quelques temps, vous n'êtes pas obligé de faire pareil :

- une adresse email différente pour chaque site sur le quel je m'inscris
- un mot de passe différent à chaque fois
- un mot de passe long, aléatoire et donc impossible à retenir

Le tout est stocké dans une application locale de type keychain. Ce trousseau n'est pas sauvegardé dans le cloud (mais il est tout de même sauvegardé, hein). Le trousseau est protégé par un mot de passe fiable que j'ai dans la tête et qui n'est pas utilisé ailleurs.

Mes mots de passe ressemblent à ça désormais :

xwMxTgX7g@*Gd&BW31dlc`ME<
j?UI?@CQe`=p5D(-w<q{FUOWb
&X52&/*.U4Otm/rH?oT-VhTIn
$kUT5_XP\ncw9cYM_\8NHbIm*
...

Des chaînes aléatoires de 25 caractères, choisis parmi 93 lettres, chiffres et symboles. Autant dire qu'avec ça, j'ai bon espoir que mon mot de passe se trouve dans les derniers à être craqués lors d'une attaque. En prime comme je n'utilise chacun que pour un site unique, la valeur ajoutée de la casse de ce mot de passe est très faible.

Si vous êtes sur BSD (Mac OS X, FreeBSD…) vous pouvez utiliser la commande jot pour créer autant de mots de passe que vous souhaitez :

jot -r -c 200 33 126 | rs -g0 0 25

-r : random
-c 200 : 200 caractères
33 126 : du "!" au "~" dans la table ASCII (man ascii)
rs -g0 0 25 : re-formate en ligne de 25 caractères

Avec ça, je vais bien tenir 2 ou 3 ans avant de devoir allonger les chaînes…

Related posts

Munin plugins for CRM114

I'm using CRM114-based SpamAssassin plugin for spam filtering at work, and at home. Client-side, I'm able to check CRM114 contribution by a simple look at headers of an email message. But that won't tell you how CRM114 is behaving on the server side. The main concern on the server, is to check the two "databases" spam.css and nonspam.css.
I've chosen to monitor both average packing density and documents learned metrics for both files. The first one goes from 0 to 1. When its value reaches 0.9 and beyond, you must make sure your antispam filtering does not become sluggish.
The second one represents the number of emails from which CRM114 has been trained. Basically, CRM114 is only trained from its mistakes : if an email is flagged as spam by mistake, you can train CRM114 to learn it as nonspam, but if an email is properly flagged as spam, you can't train CRM114 to learn it as spam.

I've designed two Munin plugins, they run on FreeBSD but portage to another UNIX is just a matter of path.

Monitor "average packing density" (crm114_packingdensity): shows how much your CRM114 bases are encumbered

#!/usr/local/bin/bash
#
# Parameters:
#
# 	config   (required)
# 	autoconf (optional - used by munin-config)
#
# Magick markers (optional - used by munin-config and som installation
# scripts):
#%# family=auto
#%# capabilities=autoconf

export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# config
if [ "$1" = "config" ]; then
    echo 'graph_title CRM114 css file stat average packing density'
    echo 'graph_vlabel Packing density'
    echo 'graph_category ANTISPAM'
    echo 'graph_args --upper-limit 1 --lower-limit 0'
    echo 'graph_info This graph shows the average packing density for CRM114 css files'
    echo 'spam.label spam'
    echo 'nonspam.label nonspam'
    exit 0
fi

cssutil -r -b /var/amavis/.crm114/spam.css | awk '/Average packing density/ {print "spam.value "$5}'
cssutil -r -b /var/amavis/.crm114/nonspam.css | awk '/Average packing density/ {print "nonspam.value "$5}'

Monitor "documents learned" (crm114_documentslearned): shows how many emails CRM114 has learned from

#!/usr/local/bin/bash
#
# Parameters:
#
# 	config   (required)
# 	autoconf (optional - used by munin-config)
#
# Magick markers (optional - used by munin-config and som installation
# scripts):
#%# family=auto
#%# capabilities=autoconf

export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# config
if [ "$1" = "config" ]; then
    echo 'graph_title CRM114 css file stat documents learned'
    echo 'graph_vlabel Documents learned'
    echo 'graph_category ANTISPAM'
    echo 'graph_args --lower-limit 0'
    echo 'graph_info This graph shows the documents learned count for CRM114 css files'
    echo 'spam.label spam'
    echo 'nonspam.label nonspam'
    exit 0
fi

cssutil -r -b /var/amavis/.crm114/spam.css | awk '/Documents learned/ {print "spam.value "$4}'
cssutil -r -b /var/amavis/.crm114/nonspam.css | awk '/Documents learned/ {print "nonspam.value "$4}'

Both plugins need read access to spam.css and nonspam.css, depending on your setup it will require special privileges. You might want to add in /usr/local/etc/munin/plugin-conf.d/plugins.conf:

[crm114_*]
user root
Related posts