lundi 14 décembre 2015

fwbackups - le programme de backup equivalent à "Cobian Backup" pour Linux

Cela fait maintenant des années que j'utilise Cobian Backups sous Windows (Windows, c'est pour travail).

Mais je suis plutôt addict Linux à la maison. Cela faisait longtemps que je cherchais un système de backup plutôt proche des fonctionnalités de "Cobian Backup", simple et efficace.
Je l'ai trouvé, il s'appelle fwbackups et fonctionne en python (léger et très largement supporté par les plateforme Linux).

fwbackups de Diffingo

Utilisée depuis plusieurs mois maintenant, je me prépare à en faire le déploiement. Pourquoi ne pas faire la publicité d'une bonne solution.


Pour démarrer le programme de backup, il suffit de saisir la commande suivante sur une ligne de commande:

fwbackups

Téléchargement:
Installation:

Note:
Par le passé, j'avais trouvé "Areca Backup" vraiment génial mais ce dernier nécessitait l'installation de Java... pas trop a mon goût pour être tout à fait honnête. Entre Java et Python mon coeur balance franchement vers Python.
Cela ne signifie pas qu'une solution Java ne soit pas une bonne solution, loin de là, c'est juste une question de préférence personnel.


Voir aussi:

mercredi 11 novembre 2015

Electrabel - Une démarche spontanée par e-mail potentiellement dangereuse pour l'identité du client

Bonjour à tous et toutes,

Un blog n'est généralement pas le meilleur endroit pour se laisser aller à des réactions sur le vif... mais ce jour je suis choqué car j'ai vu des informations sensible être communiqué par e-mail (certes qui m'est adressée) par la société Electrabel elle-même.

Nous le savons tous, malgré de nombreux efforts de la part des prestataires, les boîtes e-mails sont régulièrement l'objet de tentative de piratage... ainsi il est préférable de ne pas y laisser trainer d'information trop personnelle. Mon beau-père s'est déjà fait avoir une fois où deux (je l'ai su car j'ai reçu des é-mails bizarres de sa part)... nous avons tous connu cela... cela pourrait aussi nous arriver.

Perso, je ne laisse pas d'information sensible, il serait bien que mes prestataires de service --dont Electrabel fait l'objet-- ne se permette pas d'en publier à ma place sur mon propre e-mail!

La quantité d'information que j'y ai trouvé dans deux e-mails est suffisant pour élaborer une tentative d'usurpation d'identité (j'y reviens plus loin). Eventuellement se faire envoyer une copie de facture (voire même savoir où l'on peut la récuperer, l'info est dans l'émail!) pour ensuite l'utiliser afin de s'ouvrir l'accès à d'autres services chez d'autres prestataires et facturé chez vous (bien entendu, sinon ce ne serait pas marrant).
PS: Il n'est pas rare qu'il soit demandé une copie de facture d'électricité et/ou de gaz pour prouver votre identité et le lieu de résidence. Ce qui rend encore plus grave la récente démarche e-mail opéré par Electrabel.

Heureusement il n'y a ni mon numero national, ni ma date de naissance (ouf!)... sinon tout le reste se trouve dans deux e-mail envoyé en moins de 30 jours.

L'adresse pub
Il faut savoir que nous avons plusieurs adresses e-mail... dont une qui se termine par ".pub@gmail.com".
Cette adresse est destinée aux sites avec un faible niveau de confiance ou à toute société se montrant un peu entreprenante en vue d'obtenir une adresse e-mail.
Inutile de dire que cette boite est remplie de spam et messages non désirés.
Seul les prestataires de confiance (n'envoyant pas d'émail non sollicité) reçoivent notre vraie adresse e-mail au terme d'une période de validation relativement longue.

Electrabel dispose de notre adresse ".pub@gmail.com". Electrabel était encore en période de validation et que l'email à été communiquée suite à démarche de type "commerciale". Ont ne sais jamais avec qui l'adresse pourrait être partagée et ce qu'il pourraient en faire. Cela doit remonter à un temps certains... nous ne nous en souvenions plus.

Un premier email d'Electrabel
Il y a un moment, nous avons reçu un e-mail réputé d'Electrabel mentionnant "Important - vos prochaines factures via e-mail". Cet e-mail nous demande de nous connecter sur le compte en ligne pour confirmer nos informations en vue de facturation électronique.
Ce mail contient un numéro de client et un code d'activation.
N'AYANT:
1) RIEN DEMANDé auprès d'Electrabel et
2) le lien proposé RENVOYANT VERS LE SITE P5TRC.EMV2.COM

Notre premier réflexe est de penser à une tentative d’hameçonnage (phishing) en vue d'une collecte d'information pour usurpation d'identité.
Il ne s'agissait visiblement pas "POUR SURE" d'Electrabel... le mail est casé dans les SPAM et déclaré comme tel. En cas de SPAM, la première chose à faire est "de ne rien faire" et ne surtout pas signaler notre intérêt/existence au spammeur.

Il y a au moins 1 belge sur 2 raccordé chez Electrabel. Une tentative de Physhing/Hameçonnage à donc toutes les chance d'aboutir avec un certain succès.

Un deuxième E-Mail d'Electrabel
Ce jour, nous recevons un deuxième e-mail "Votre facture intermédiaire du 10 Novembre 2015".
Les liens pointes toujours vers P5TRC.EMV2.COM (donc toujours pas vers Electrabel.be!!! ni de connexion sécurisé via https).
Ni une, ni deux nous déclarons egalement ce nouvel émail comme SPAM!
Par contre nous restons interpellé par la présence de l'adresse de notre demeure dans l'émail.

Cette fois, nous trouvons:
* Notre numéro client
* Le montant d'une facture
* Une date d émission de facture
* notre adresse complète (reste plus qu'a faire appel à un annuaire pour connaitre le nom correspondant à l'adresse ou être perspicace avec l'adresse e-mail nom.prenom@blabla.com!)

Après vérification, nous avons constaté que toutes les informations communiquées sont "correctes" et communiquées par l'intermédiaire d'un média très discutable.
Les liens ne redirige pas vers le nom de domaine d'Electrabel (cela pourrait aussi bien être un site Chinois, vous n'imaginez pas le nombre d'email de ce genre reçu chaque jour) et les liens ne sont toujours pas sous HTTPS.
Il n'y a là aucun des indices permettant d'augmenter le niveau de confiance.

Electrabel - transmission non sécurisée de mes informations
Je ne doute pas un seul instant que des informaticiens consciencieux de chez Electrabel aient posés les bonnes questions concernant la communication d'informations sensibles via e-mail.
D'autant que tous les intermédiaires informatiques peuvent en prendre librement connaissance a notre insu (super pour les campagnes marketing, ou le réseau échelon).

Cher Electrabel:
  • Il faut être fou d'avoir communiqué un numéro de client et le code d'activation dans un seul e-mail!
    Quiconque peuvant accéder à cet information pourrait avoir des informations utiles pour usurper votre identité et/ou pour manipuler le helpdesk d'Electrabel! Se faire craquer sa boite e-mail n'est pas exceptionnel, les personnes plus agées restant les plus influencables/manipulables.
  • Vous demandez de vous connecter sur des liens pour y traiter des informations "à caractère financier" (mes factures) sans passer par le nom de domaine "Electrabel.be"!!!
  • Ont ne propose JAMAIS DE LIEN à suivre s'il y a un enjeux financier!!!! Faites comme les banques les gars!!!
  • Ne jamais inclure d'information sensible dans un e-mail!!!
    Mon numéro de client, mon code d'activation, mon adresse, mon montant facturé, la date d'émission de facture (et la communication qui va avec) par e-mail!!!
    Faut arrêter de rigoler les gars.
  • Sans compter que tout cette procédure est entamée sans mon consentement explicite... je suis sous le coup d'un accord par défaut d'opposition sur un premier e-mail ne présentant AUCUN indice de confiance! 
  • Vous m'excuserez mais P5TRC.EMV2.COM ce n'est pas Electrabel... sur ce plan c'est vraiment de l’amateurisme (je suis du métier).
    Si un email vous envoyait un lien pour vous connecter sur P5XCC.INV3.COM au lieu de GMAIL.com, vous n'iriez probablement pas faire une telle bêtise.... et bien, cela devrait être pareil pour Electrabel. 

C'est quoi le danger?
Les chevaux de Troie sont légion! Avec eux, personne ne peut savoir si quelqu'un d'autre à pris connaissance de ces mêmes infos sur votre e-mail (ou votre smartphone) à votre insu.
Au moins, avec une facture papier cette dernière est "soit ouverte, soit disparue"... il y a une trace palpable du délit!

Dans le cas de ce billet, et si la sécurité de votre email est compromise, il y a suffisamment d'informations pouvant aider un tiers à se faire passer pour vous... et probablement de pouvoir permettre d'abuser le Helpdesk Electrabel (je laisse ce jeu là a des journalistes, mais reste convaincu par le bien fondé de mon propos).
Avec Internet, une identité est devenue précieuse et fragile. Une fois utilisée par quelqu'un de malveillant, cela peut vous causer énormément de tords et de tracas (souvent financier).
Vous devez rester vigilent et ne pas autoriser qui que ce soit à communiquer par moyen électronique (l'email n'étant pas le plus sécurisé) des informations à votre propos que vous ne communiqueriez pas vous même!

Réagissez
Je reste atterré par un tel égarement alors qu'il suffit de "juste un peu de bon sens" pour voir ce qui saute littéralement aux yeux.

Moi je réagis et je marque ces e-mail comme "SPAM" auprès de GMail.
Rien ne me permet de savoir, pour sur, que l'émail que je recevrais demain sera effectivement émanant d' Electrabel ou quelqu'un qui voulant se fait passer pour cette société.

Soyez prudent et protégez le Net en marquant activement toute communication qui pourrait conduire à une imprudence future! (comme le cas présent)

Chers Electrabel, j'espère que vous ferez rapidement le nécessaire pour redresser la barre.
Vous me faites peur là!

jeudi 29 octobre 2015

Liste des processus - commande ps

la commande ps -ef permet de lister facilement les processus.... mais ce qui serait parfois utile de savoir, c'est l'état du processus.
Pour cela, nous avons la commande ps -aux  qui fourni un résultat similaire à ceci.

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   4464  2512 ?        Ss   sep17   0:02 /sbin/init
root         2  0.0  0.0      0     0 ?        S    sep17   0:00 [kthreadd]
root         3  0.0  0.0      0     0 ?        S    sep17   4:49 [ksoftirqd/0]

yyy
La colonne STAT nous indique le statut du processus dont voici les principaux
  • D - Uninterruptible sleep - En sommeil et ne pouvant pas être arrêté (généralement en attente d' IO)
  • R - Running - Exécuté ou processus exécutable (dans la queue d'exécution)
  • S - Sleep - en sommeil et pouvant être interrompu (en attente d'événement)
  • T - Arrêté, soit suite à u signal de contrôle de job/processus ou parce qu'il est en cours de débogage (traced).
  • X - processus mort (dead). Nous ne devrions jamais voir ce statut
  • Z - Zombie, processus terminé et non repris en charge (collecté/libéré) par son processus parent.

dimanche 11 octobre 2015

Activer Google comme moteur de recherche par Defaut dans Firefox

Nous sommes quand même nombreux à vouloir utiliser Google comme moteur de recherche dans Firefox.
Comme c'est un moteur commercial, il n'est pas installé par défaut... et la recherche sur le net est un peu fastidieuse.

Voici donc comment faire:
1) Télécharger le greffons/plug-in "Google Search" sur le site de mozilla.

Cliquer sur le lien suivant et installer le plug-in

https://addons.mozilla.org/en-US/firefox/addon/google-default/

2) Ensuite, allez cliquer sur l'icone en forme de loupe dans la barre de recherche.

Cela affiche un menu et tout en bas, vous pouvez voir "paramètres de recherche".
Cliquez sur le point de menu "Paramètres de recherche"

3) Dans la fenêtre des paramètres de recherche, sélectionnez simplement le moteur de votre choix.... dans notre cas "Google"


Voili, voilou,

mardi 6 octobre 2015

Inférence entre dictionnaire et paramètres nommés

Cas pratique
J'ai un cas pratique où je veux recharger des données depuis un fichier json (une fois chargé, j'ai une liste de dictionnaire) PUIS les injecter dans une DB à l'aide de sqlAlchemy.
In [57]: d[0]
Out[57]: 
{'actif': 'Y',
 'libe': 'Le carbone lorraine',
 'name': '0200',
 'pnet': 50.0,
 'sync_id': 1.0}

In [58]: d[1]
Out[58]: 
{'actif': 'Y',
 'libe': 'BUSELURES',
 'name': '0201',
 'pnet': 50.0,
 'sync_id': 2.0}

Dans sqlAlchemy, j'ai définit un modèle pour mes tables.
En utilisant ce modèle (ici family_table), l'insertion est relativement simple:

q = family_table.insert()
q.execute( actif='Y', libe= 'Le carbone lorraine', name= '0200', pnet= 50.0, sync_id= 1.0 )

Mais ce qui serait vraiment cool, c'est appeler execute() directement avec le dictionnaire d[0], d[1], ...et se passer de la tache fastidieuse décrite ci-dessus. Comme sqlAlchemy utilise **kwargs, nous pouvons faire de l'inférence :-)

q = family_table.insert()
dic = d[0] # récuperer un dictionnaire
# La ligne suivante dit que dic doit être passé directement
# en tant que paramètre **kwargs de la fonction 
q.execute( **dic ) 

**KWargs - comment ça marche?
Définissons la fonction printkw() pour afficher les valeurs disponibles dans le paramètre **kwargs.
En fait, kwargs est un dictionnaire qui contient les paramètres nommés sous forme clé = valeur. Comme kwargs n'est pas un mot clé (mais une convention de nommage), on utilise le signe ** pour identifier kwargs (et ses propriétés extraordinaires) dans la déclaration de la fonction printkw()
Vous pourriez fort bien appeler ce paramètre **dico_des_parametre_nommes , mais il est préférable de respecter les conventions.

def printkw( **kwargs ):
   for k,v in kwargs.items():
      print( '%s = %r' % (k,v) )

Maintenant, si nous appelons la fonction printkw() avec des paramètres nommés, nous avons:

>>>printkw( name='hello', value=12, value2='test me' )
value2 = 'test me'
name = 'hello'
value = 12

Dans notre fonction printkw() nous pourrions facilement récupérer une valeur donnée avec l'instruction le_nom = kwargs['name']
Cela nous fournirait la valeur du paramètre "name".

Mais le plus intéressant, selon moi, réside au passage des paramètres par l'intermédiaire d'un dictionnaire. Il devient alors très facile de:
  • Passer de nombreuses valeurs d'une fonction à l'autre
  • De présenter une interface très flexible (surtout avec des DB) pour transmettre de nombreuses données
  • De faire une persistance des paramètres nommés dans une DB,Fichier, Json, service en ligne, etc
>>> d = { 'name':'hello', 'value':12, 'value2':'test me' }
>>> printkw( **d )
value2 = 'test me'
name = 'hello'
value = 12

La première ligne crée un dictionnaire avec les paramètre nommés (qui auraient pu provenir d'un fichier json comme dans l'intro de l'article).
La deuxième ligne appel printkw() en passant le dictionnaire d en tant que paramètres nommés (c'est à cela que sert ** )

Recharger un fichier json
Juste pour le fun, voici comment j'ai rechargé le fichier json dans la liste d.

import json
# retourne le nom de fichier pour les données famille
#    'c:\\python\\catflask-alpha\\toolbox\\family.json'
filename = json_filename( 'family' )
with open( filename ) as json_data:
    d = json.load( json_data )
    json_data.close()

vendredi 18 septembre 2015

Inspection des propriétés - petite technique pratique

Voici une petite technique intéressante que vous pouvez utiliser dans un python Interactif (comme IEP par exemple).

Cela fonctionne avec tous les objets... mais dans mon cas, c'est avec un module que j'ai utilisé cette technique. Dans l'article précédent, je voulais recharger le module model.py ... mais je voulais aussi m'assurer que mes objets user_table, group_table, permission_table, etc étaient maintenant disponible!

Les petits trucs à retenir de cet article seront
  [ attr for attr in dir(m) if '_table' in attr]
  [ '%s : %s' % (attr.ljust(25), getattr(m,attr)) for attr in dir(m) if '_table' in attr ]

Mais pour les plus curieux, je vous propose de lire les différentes étapes.

1) Récupérer la référence vers module
import sys
m = sys.modules['model']

2) Faire un dir() --> inefficace
Faire un dir() sur un module (ou un objet) peut retourner un contenu fastidieux à parcourir suivant la taille de l'objet. Dans le cas ci-présent, il n'y a pas moins de 193 entrées! Gloups!

dir( m )
Out[67]: 
['AliasOption',
 'AttributeExtension',
 'BIGINT',
 'BINARY',
 'BLOB',
 ...
 'with_parent',
 'with_polymorphic']

In [68]: len( dir(m) )
Out[68]: 193

3) Utilisons une compréhension list
Avec une simple compréhension liste basé sur dir(), nous allons pouvoir filtrer le contenu retourné à l'aide de l'instruction if.
Comme dir() liste les attribut, il suffit de tester si la chaine de caractère '_table' apparaît dans le nom de l'attribut.
C'est magique :-)

[ attr for attr in dir(m) if '_table' in attr]
Out[69]: 
['group_permission_table',
 'group_table',
 'permission_table',
 'user_group_table',
 'user_table']

4) Avec les valeurs svp
Avoir les noms des attributs c'est bien, avec leur valeur c'est encore mieux!
Cette fois-ci, nous utilisons getattr() pour obtenir la valeur et nous justifions avec ljust() pour le confort de lecture.

In [72]: [ '%s : %s' % (attr.ljust(25), getattr(m,attr)) for attr in dir(m) if '_table' in attr ]
Out[72]: 
['group_permission_table    : tf_group_permission',
 'group_table               : tf_group',
 'permission_table          : tf_permission',
 'user_group_table          : tf_user_group',
 'user_table                : tf_user']

Attention, c'est uniquement pour faire de l'investigation en live!!!

5) Avec le représentation des valeurs
Si vous jouez avec python depuis un moment, vous avez certainement déjà rencontré la fonction __repr()__. Cette dernière est utilisée par l'interpréteur pour afficher plus de détails  sur les objets (et pas uniquement leur adresse).
Si cela est pratique, cela peut aussi fournir du contenu très... touffu selon les circonstances.

Cette version du script remplace uniquement un des %s par %r (pour demander l'affichage de la "représentation").

In [73]: [ '%s : %r' % (attr.ljust(25), getattr(m,attr)) for attr in dir(m) if '_table' in attr ]
Out[73]: 
["group_permission_table    : Table('tf_group_permission', MetaData(bind=Engine(postgresql://dom:***@hp-minty/dom)), Column('id_permission', Integer(), ForeignKey('tf_permission.id'), table=), Column('id_group', Integer(), ForeignKey('tf_group.id'), table=), schema=None)",
 "group_table               : Table('tf_group', MetaData(bind=Engine(postgresql://dom:***@hp-minty/dom)), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('group_name', Unicode(length=16), table=, nullable=False), schema=None)",
 "permission_table          : Table('tf_permission', MetaData(bind=Engine(postgresql://dom:***@hp-minty/dom)), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('permission_name', Unicode(length=16), table=, nullable=False), schema=None)",
 "user_group_table          : Table('tf_user_group', MetaData(bind=Engine(postgresql://dom:***@hp-minty/dom)), Column('user_id', Integer(), ForeignKey('tf_user.id'), table=, primary_key=True, nullable=False), Column('group_id', Integer(), ForeignKey('tf_group.id'), table=, primary_key=True, nullable=False), schema=None)",
 "user_table                : Table('tf_user', MetaData(bind=Engine(postgresql://dom:***@hp-minty/dom)), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('user_name', Unicode(length=16), table=, nullable=False), Column('password', Unicode(length=40), table=, nullable=False), Column('display_name', Unicode(length=255), table=, default=ColumnDefault('')), Column('created', DateTime(), table=, default=ColumnDefault(. at 0x04560B70>)), schema=None)"]
Comme vous pouvez le constaté, c'est nettement plus chargé... et avec le retour à la ligne, c'est franchement illisible)
Comme quoi "le mieux est parfois l'ennemi du bien".

Voila, Happy Python Hacking ;-)

Recharger un module Python à la volée

Il y a peu, je vous parlais d'IEP dans ce billet "IEP: un éditeur Python interactif convivial (Windows, Mac, Linux)".

Environnement bien pratique pour faire des tests et je découvre justement SqlAlchemy.
J'étais justement entrain de coder un petit module de test model.py lorsque je me suis demandé comment recharger mon module!
IEP - édition d'un module "model.py"

J'avais déjà fait un import model ... mais ayant modifié le code, je voulais savoir comment le recharger.
Voici donc comment faire...

Option 1 - à la dure
import sys
del( sys.modules['model'] ) # virer le module "model.py"
from model import *         # ré-importer le module "model.py"

Option 2 - importlib.reload()
import importlib
import sys
a = sys.modules['model'] # Référence vers le module
importlib.reload( a )    # Demander à Python de recharge le module