On se tutoie ?

Aller au contenu | Aller au menu | Aller à la recherche

jeudi, janvier 29 2009

Mettre à jour son statut twitter en shell

Je partage mon petit morceau de code pour ceux que ça intéresse:

twitter() {
  local email="you@example.com"
  local length=140
  echo "Enter password:"
  stty -echo
  read password
  stty echo
  echo "Enter status:"
  read status
  if [ ${#status} -le $length ]; then
    echo "Posting..."
    wget --user=$email --password=$password --post-data="status=$status" http://twitter.com/statuses/update.xml > /dev/null 2>&1
  else
    echo "Status is too long, max length is $length, current length is ${#status}."
  fi
} #twitter

La seule petite subtilité c'est le

stty -echo

qui désactive l'affichage de la saisie dans le terminal, et qui évite donc que notre mot de passe s'affiche à l'écran.
Tu n'as plus qu'à modifier ton email :-)

jeudi, juin 12 2008

Accéder à du contenu authentifié ... en 2 lignes de shell

Le souci : récupèrer du contenu protégé par un couple login / pass classique

Décidément les outils GNU n'ont pas fini de me surprendre.
Le dernier en date c'est wget.
Quand on voit la taille du man on se dit tout de suite qu'on peut faire un paquet de choses avec cet outil.

L'usage le plus courant de wget est le téléchargement de fichier sur la toile, que ceux ci soit stocké en http, ftp ...
Récemment je cherchais à récupérer la liste des flux que je consulte dans google reader.
Sous Reader l'export se fait au format standard OPML.

Bien sûr pour accéder à l'export il faut s'authentifier.
J'ai d'abord pensé faire un petit script ruby mais dans ce cas présent il y a mieux.
Si si !

S'authentifier avec sauvegarde des cookies

L'idée est de procéder en deux étapes :

  1. Se connecter en soumettant login / pass et récupérer le cookie pour le stocker dans le fichier de notre choix.
  2. Ré-utiliser ce cookie pour accéder au fichier qui nous intéresse [1].

Chaque étape se fait en une ligne de commande :

 
wget "https://www.google.com/accounts/ServiceLoginAuth?service=reader" --post-data="PersistentCookie=1&Email=email&Passwd=pass" --no-check-certificate --save-cookies="/tmp/gcookie" --output-document=/tmp/null 
wget "http://www.google.com/reader/subscriptions/export" --no-check-certificate --load-cookies="/tmp/gcookie" --output-document="~/opml"

J'ai volontairement utilisées les options longues qui sont réellement explicites.

Dans la première étape on accède à l'url d'authentification et on soumet le formulaire en POST [2] en lui passant les différentes valeurs (notre email, notre mot de passe, et l'option pour rester connecté à base de cookie).
La valeur 1 est la valeur de la checkbox cochée.

Le --no-check-certificate nous permet de passer la validation du certificat https comme c'est souvent le cas, notamment pour les certificats auto-signés.
Le --save-cookies nous permet de spécifier le path ou sera stocké le cookie. Si l'authentification échoue le fichier sera quand même créé mais uniquement avec les headers de wget.
Enfin la page récupérée ici (page qui nous indique le succès ou non de notre connexion) ne nous intéresse pas, elle est donc copié dans le trou noir qu'est /dev/null.

La deuxième étape reprend les mêmes options que la première mise à part le --load-cookies qui nous permet d'accèder à une page en se basant sur notre fichier de cookies précédemment créé.
C'est tout, ton fichier de souscriptions est désormais dans ton $HOME.

Difficile de faire plus rapide même si le script est minimaliste et ne vérifie pas si l'authentification s'est bien passée etc ...

Notes

[1] Et ne pas oublier de l'effacer une fois le fichier récupéré.

[2] D'ailleurs ça marche aussi en passant les paramètres et leurs valeurs respectives en GET.

mardi, janvier 22 2008

Utiliser le même ssh-agent entre plusieurs shells.

La problématique : se connecter entre ses différents machines, en ssh et sans mot de passe

Lorsqu'on commence à avoir des machines dans tous les coins on aime pouvoir y accèder facilement, et de partout.

Souvent on se retrouve avec un petit réseau local contenant plusieurs machines partageant un seul point d'accès à internet. C'est à partir de cette IP publique que l'on peut se connecter à son réseau interne de l'extérieur.

Le problème c'est que tant qu'IPv6 ne sera pas établi (et que ou l'on pourra disposer de plusieurs adresses publiques sans sourciller) on en est réduit à une seule adresse et on se retrouve avec une machine qui sert de passerelle pour rebondir dans son réseau interne.

Machine externe -> Passerelle -> Machine interne

Une solution basique : un jeu de clés privée / publique

Imaginons que l'on souhaite se connecter depuis la passerelle sur la machine interne. On va générer un jeu de clé :

$ ssh-keygen -t dsa

On peut utiliser le fichier par défaut, par contre il faut mettre une passphrase. Une fois notre paire de clés générée on va copier notre clé publique sur la machine sur laquelle on souhaite se connecter :

$ ssh-copy-id -i ~/.ssh/key.pub login@interne

Cela aura pour effet d'aller copier notre clé publique dans le fichier authorized_keys de la machine interne. Dès lors quand on voudra se connecter sur la machine interne depuis la passerelle c'est la passphrase qui sera demandée.

À ce niveau on peut penser avoir juste déplacé le problème : toujours un mot de passe à taper.

Toutefois, pour ne pas avoir à retaper sa passphrase à chaque connexion on peut utiliser ssh-agent.
Pour cela on rattache ssh-agent à un processus sur la passerelle, par exemple ssh-agent /bin/bash.

Cela aura pour effet de lancer un nouveau shell auquel sera rattaché l'agent.
Ensuite on peut rajouter les clés privées à gérer par notre agent : ssh-add ~/.ssh/id_dsa.

ssh-add va nous demander la passphrase, une fois entrée c'est lui qui interceptera les demandes de passphrases de ssh.

Dès lors je peux me connecter de ma passerelle vers ma machine interne sans mot de passe.

Aller un peu plus loin : utiliser le même agent pour faciliter les rebonds ssh

Soit, mais si je connecte depuis la machine externe vers la passerelle, et que depuis la passerelle je souhaite rouvrir une connexion vers la machine interne ma passphrase sera re-demandée.

Pourquoi ? Parce que notre agent est associé à notre shell (ssh-agent /bin/bash). En dehors de ce shell le problème est le même.

Si on y regarde de plus près, comment cela fonctionne t-il vraiment ?

Au lancement du ssh-agent une socket est créée dans le /tmp, du genre /tmp/ssh-xxxpiddushell/agent.piddushell
En fait le ssh-agent est un processus fils du shell nouvellement créé. En plus de ça notre commande génére plusieurs variables d'environnements qui ne sont accessibles qu'à l'intérieur du nouveau shell.

Regardons ça de plus près :

$ env | grep -i ssh

Deux variables nous intéressent :

SSH_AGENT_PID=22659
SSH_AUTH_SOCK=/tmp/ssh-WgkOE22658/agent.22658

Récupérer l'environnement de notre agent, et l'exporter dans le nouveau shell

On le voit ces informations sont facilement accessibles. Partant de là si on se connecte depuis la machine externe vers la passerelle et que l'on fait un export de ces variables dans le nouveau shell alloué lors de notre connexion ssh on peut parfaitement se reconnecter sur la machine interne sans taper de mot de passe :

$ export SSH_AGENT_PID=22659
$ export SSH_AUTH_SOCK=/tmp/ssh-WgkOE22658/agent.22658
$ ssh interne

L'idée c'est d'alouer automatiquement ces variables, en réalisant l'export ... dans notre .bashrc
En fait il y a deux façons de faire : soit on force le nom de la socket, avec un ssh-agent -a, soit on récupère les informations dynamiquement.

Je penche pour la deuxième qui m'évite de penser à passer cette option lorsque que je lance mon agent (et oui, geek == fainéant). En plus il faudra toujours récupérer l'id du pid.

Pour ça deux commandes à mettre dans le .bashrc :

export SSH_AGENT_PID=$(ps aux | grep -v 'grep' | grep ssh-agent | grep $(env | grep "USER" | sed s/USER=// | head -n 1) | head -n 1 | awk '{print $2}')

if [ "" != "$SSH_AGENT_PID" ]; then
  export SSH_AUTH_SOCK=$(find /tmp -iname "agent.$(ps -p $SSH_AGENT_PID -o ppid=)")
fi

Explications :


  • ps aux liste les processus
  • grep -v fait une recherche inverse c'est à dire qu'il exclut les résultats demandés. En l'occurence je demande à grep de s'exclure lui même, sinon la commande apparait dans la liste des résultats
  • grep ssh-agent fait ce qu'on lui demande, il recherche les lignes qui matchent.
  • grep $(env | grep "USER" | sed s/USER=// | head -n 1) fait un grep sur le nom de l'utilisateur. En effet il peut y avoir plusieurs agents sur la machine, appartenant à des utilisateurs différents.
  • head -n 1 renvoie la première ligne uniquement
  • awk '{print $2}' permet de splitter la ligne selon les espaces ou tabulations. Ici on renvoie le deuxième bloc qui contient le pid. On pourrait aussi utiliser cut.
if [ "" != "$SSH_AGENT_PID" ]; then
        export SSH_AUTH_SOCK=$(find /tmp -iname "agent.$(ps -p $SSH_AGENT_PID -o ppid=)")
fi

Explications :

Cette commande recherche la socket correspond à ce ssh-agent s'il existe (présence du pid).
La socket est nommée dans un format spécial : agent.pidduparentdel'agent ; il nous faut donc récupérer le pid parent du pid de l'agent, celui du shell donc.
C'est ce que réalise cette commande : ps -p $SSH_AGENT_PID -o ppid=

Ensuite c'est un find tout bête qui nous renvoie le full path de la socket.

Une fois ces commandes dans votre .bashrc c'est fini, lors de la connexion de externe vers passerelle, un shell est alloué, le bashrc sourcé et les variables d'environnements disponibles, on peut donc se connecter à la machine interne sans soucis.

Bien sûr pour faciliter le tout on peut utiliser un jeu de clés entre la machine externe et la passerelle.

Une pincée de sécurité en plus : empêcher les connexions ssh par mot de passe

Une fois tout ça en place pourquoi ne pas désactiver l'authentification par password ? Dans le fichier /etc/ssh/sshd_config :

PasswordAuthentication no

et au passage :

PermitRootLogin no

Ainsi on est sûr que seules les machines dont la clé publique se trouve sur la machine hôte pourront se connecter.
Toutefois il vaut mieux avoir au moins 3 machines pour utiliser ce système, ou conserver ses clés en lieu sûr, sinon au premier plantage votre machine distante devient inaccessible.
Dommage quand on n'a pas d'accès physique à celle-ci ...

mardi, septembre 11 2007

Tips #1

J'inaugure aujourd'hui une nouvelle catégorie de billets, j'ai nommé : les tips

Les tips regroupent des petites astuces glannées au fil des jours dont l'intérêt n'est pas assez important pour mériter un billet entier mais qui valent quand même le détour.

Les tips fonctionneront donc par lot de 10, ce qui m'évitera d'en poster trop souvent, car le contenu est assez brut de fonderie.

L'intérêt est donc clairement de lister toutes ces petites choses dont on a régulièrement besoin mais qu'on oublie, faute d'intérêt ou de pratique et que l'on perd un temps fou à rechercher lorsque l'on en a besoin.

Voilà donc la première salve :

  1. Ecrire du code dans dotclear : /// avec la syntaxe wiki
  2. Générateur de mot de passe «intelligent» sous debian : apg (Automatic Password Generator)
  3. Encrypter une archive en ligne de commande sous linux : zip -e foo.zip foo.txt foo2.txt
  4. Editer la liste des applications à lancer au démarrage sous xfce : xfce4-autostart-editor
  5. Visualiser plus d'informations sur ses périphériques usb qu'avec le classique lsusb : usbview (nécessite X11)
  6. Faire des opérations complexes sur les dates en shell, comme avoir la date d'il y a un mois (ce qui évitera bien des cauchemars pour les cas particuliers) :
    $ date --date '1 month ago'
    Qui a dit que le shell était has been ?
  7. Pour retourner à la page précédente avec la touche backspace sous firefox, il faut éditer la configuration. Tapes about:config dans ton navigateur, cherches la variable browser.backspace_action et passes la à 0. Sinon le raccourci 'standard' c'est ALT + left.
  8. Convertir une image de type jpeg en png : convert file.jpeg file.png (nécessite l'installation du puissant imagemagick). Note : Il est possible de faire des choses beaucoup plus élaborée : convert file.jpeg -resize 50% file.png, convert -rotate 90 file.jpg file2.jpg etc...
  9. Faire des calculs en shell : $((2 - 1))
  10. Lancer une commande shell depuis vim : :!macommande ; ex : :! echo 'hello world'. Ca peut être pratique pour exécuter et tester un script que l'on édite sans avoir à utiliser ctrl + z.