Partie 2 : twisted , serveur SSH et un shell.

Publié le 26 septembre 2008 par Mikebrant

Allez, 2° et dernière partie.
On va voir comment mettre en place un shell pour l'utilisateur.

Il faut reprendre le code de la 1° partie( ici )  :

from twisted.conch.ssh import factory, keys
from twisted.cred import portal
from twisted.conch import checkers
from twisted.python.randbytes import secureRandom
from Crypto.PublicKey import DSA
from twisted.internet import reactor
from monRealm import MonRealm
import os
def cles():
  cle = DSA.generate(4096, secureRandom)#on génère une clé
  if not os.path.exists('public.key') :
  fichier = open('public.key','w')
  fichier.write(keys.Key(cle).public().toString('openssh') )
  fichier.close()
  if not os.path.exists('private.key') :
  fichier = open('private.key','w')
  fichier.write(keys.Key(cle).toString('openssh') )
  fichier.close()
if __name__ == '__main__':
  monFactory = factory.SSHFactory()
  monFactory.portal = portal.Portal( MonRealm() )
  monFactory.portal.registerChecker( checkers.UNIXPasswordDatabase() )
  cles()
  monFactory.publicKeys = {'ssh-dss': keys.Key.fromFile('public.key') }
  monFactory.privateKeys = {'ssh-dss': keys.Key.fromFile('private.key') }
  reactor.listenTCP(2222, monFactory)
  reactor.run()


Mettez-tous dans le fichier index.py .

Comme vous pouvez le remarquer, on va commencer par créer notre classe MonRealm , qui se trouvera dans monRealm.py.

 MonRealm est la classe qui va renvoyer un avatar  en fonction de l'avatarID.

Un Realm doit obligatoirement implémenter l'interface IRealm.
NB : En python, il n'y a pas véritablement d'interfaces, c'est pourquoi on utilise le module interface de zope pour implémenter IRealm .
IRealm contient une méthode : requestAvatar() , que doit donc contenir MonRealm.

Voici donc son code( monRealm.py) :

#-*- coding:Utf-8 -*-

from twisted.cred import portal
from zope.interface import implements
from twisted.cred.portal import IRealm

class MonRealm:
     implements(portal.IRealm)
     def requestAvatar(self, avatarID, mind, *interfaces):
          return interfaces[0], MonAvatar(avatarID), None


requestAvatar() prend 3 arguments :
avatarID : c'est notre avatarID , ici ca va être l'utilisateur avec lequel on souhaite se connecter sur le serveur
mind : on s'en contrefou .
interfaces : la liste des interfaces ( cf partie 1 si vous ne comprenez pas) disponibles pour l'utilisateur. On retourne la 1° dans tous les cas : IConchUser.

requestAvatar()  retourne un tuple contenant l'interface utilisée, notre objet Avatar (devant implémenter interface[0] ) et None .

Donc maintenant place à MonAvatar.
Premièrement, notre classe doit logiquement implémenter notre interface : IConchUser : Twisted possède la classe ConchUser l'implémentant, on va donc pouvoir s'en servir .
Deuxièmement, notre utilisateur doit avoir un shell.

Si on cherche bien dans la doc de Twisted, on tombe sur la classe UnixConchUser , héritant de ConchUser et implémentant un shell.
Parfait ! c'est ce qu'il nous faut ; même plus besoin de coder notre classe Avatar.
On a plus qu'à remplacer par UnixConchUser .

Voilà ce que donne finalement  MonRealm :

#-*- coding:Utf-8 -*-

from twisted.cred import portal
from zope.interface import implements
from twisted.cred.portal import IRealm
from twisted.conch import unix

class MonRealm:
     implements(portal.IRealm)
     def requestAvatar(self, avatarID, mind, *interfaces):
          return interfaces[0], unix.UnixConchUser(avatarID),None

 
Notre serveur SSH est enfin prêt, il écoute sur le port 2222.
On peut s'y connecter via les utilisateurs du système, et on aura droit à un joli shell.
Voici les 2 fichiers  pour créer notre serveur :

 fichier index.py

#-*- coding:Utf-8 -*-
from twisted.conch.ssh import factory, keys
from twisted.cred import portal
from twisted.conch import checkers
from twisted.python.randbytes import secureRandom
from Crypto.PublicKey import DSA
from twisted.internet import reactor
import os
from monRealm import MonRealm
 
def cles():
   
  cle = DSA.generate(4096, secureRandom)#on génère une clé
   
  if not os.path.exists('public.key') :
  fichier = open('public.key','w')
  fichier.write(keys.Key(cle).public().toString('openssh') )
  fichier.close()
  if not os.path.exists('private.key') :
  fichier = open('private.key','w')
  fichier.write(keys.Key(cle).toString('openssh') )
  fichier.close()
   
   
 
if __name__ == '__main__':
   
  monFactory = factory.SSHFactory()
  monFactory.portal = portal.Portal( MonRealm() )
  monFactory.portal.registerChecker( checkers.UNIXPasswordDatabase() )
   
  cles()
  monFactory.publicKeys = {'ssh-dss': keys.Key.fromFile('public.key') }
  monFactory.privateKeys = {'ssh-dss': keys.Key.fromFile('private.key') }
   
  reactor.listenTCP(2222, monFactory)
  reactor.run()

fichier monRealm.py

 #-*- coding:Utf-8 -*-
from twisted.cred import portal
from zope.interface import implements
from twisted.cred.portal import IRealm
from twisted.conch import unix
class MonRealm:
  implements(portal.IRealm)
   
  def requestAvatar(self, avatarId, mind, *interfaces):
  return interfaces[0], unix.UnixConchUser(avatarId), None

C'est fini , cette partie fut beaucoup plus courte que je ne pensais...
À bientôt pour de nouvelles aventures avec Twisted !