Journal Tunnel automatisé ssh plusieurs machines Passe Passe le NAT

Posté par  . Licence CC By‑SA.
13
20
mar.
2013

Sommaire

Voila, hier et aujourd'hui, n'ayant rien de particulier a faire, j'ai décidé de mettre un oeuvre une idée qui me trottait par la tête depuis quelque temps; Automatiser la configuration d'un tunnel ssh pour accéder a des machines qui se connectent depuis des endroits différents dans le monde;

Le problème:

Au fil du temps, la famille, les amis me demandent de leur installer un linux, ce que je fais volontiers; J'installe toujours la même distribution, debian squeeze de préférence, debian wheezy quand le matériel est trés récent, et souvent, de l'aide m'est demandée pour une imprimante par exemple, un programme qui n'est pas installé, etc.. et dans cas, me connecter en ssh sur la machine me permet d'éviter un déplacement; Mais probablement! La majorité sont derrière des routeurs, avec même des adresses ip dynamiques souvent, certains on même un portable et se connectent depuis des endroits différents, donc dyndns+configurer routeur c'est la galère, de plus il faut se souvenir des noms de domaines dyndns, la galère quoi.
Second problème, j'aime bien donner des mots de passe simples a ma famille, genre la même chose que le login, exposer ssh directement a internet est donc déconseillé pour des raisons évidentes de sécurité!

L'idée générale de solution:

Il faut soit un ordinateur directement connecté a internet (pour plus de simplicité), soit encore mieux un serveur dedié ou virtuel, avec l'accés root (dans mon cas j'utilise un serveur virtuel hetzner a 7.90 euros par mois), que nous apellerons MERLO
Les ordinateurs de la famille, des amis, que nous appelons hotes, se connectent automatiquement peu aprés le démarrage a MERLO en ssh et creent un tunnel pour exposer des ports préalablement définis (typiquement le port ssh, mais ça marche pour d'autres ports) afin qu'ils soient accessibles de l'extérieur.
Tout ou presque est automatisé chez chaque ordinateur hote

Avertissement:

Tout le code est crade, beaucoup de faute d'orthographe, j'ai teste uniquement sous debian squeeze, il faut surement l'adapter pour les autres distributions, c'est fait vite fait et mal fait, aucune gestion d'erreur, peu de fonctionnalités, etc…

Le detail:

Configuration du serveur MERLO:

Il faut bien sur avoir un serveur ssh d'installé, ainsi que python3 (par la même occasion j'ai fait mon premier programme python3)

    apt-get install python3 openssh-server

Dans le /etc/ssh/sshd_config, rajouter a la fin:

    GatewayPorts yes
    ClientAliveInterval 30
    ClientAliveCountMax 5

Il faut rajouter un utilisateur dédié à ça, donnons lui un nom au hasard, pourquoi pas CALAMAR, donc adduser CALAMAR, lui mettre un mot de passe pas trop pourri, et mettre a la racine du /home de CALAMAR (soit /HOME/CALAMAR) le fichier suivant numBasNumHaut.py:

from subprocess import Popen
import os
def ex(command):
    print(" on va executer: "+command)
    p = Popen(command, shell=True)
    sts = os.waitpid(p.pid, 0)[1]



import sys
class Connexion():
    def __init__(self,portHote,portLocal,monitor):
        self.portHote=portHote
        self.portLocal=portLocal
        self.monitor=monitor
class Hote():
    def __init__(self,nom):
        self.nom=nom
        self.cons=[]
    def ajoutConnexion(self,ph,pl,m):
        c=Connexion(ph,pl,m)
        self.cons.append(c)

import shelve
d=shelve.open(".dbmonirot")
def innit(db,key,valeur):
    if key in db:
        return db[key]
    else:
        db[key]=valeur
        return valeur

numBas= innit(d,"numBas",1500)
numHaut= innit(d,"numHaut",15000)
hotes= innit(d,"hotes",[])

def getBas():
    valeur=d["numBas"]
    d["numBas"]=valeur+1
    return valeur

def getHaut():
    valeur=d["numHaut"]
    d["numHaut"]=valeur+2
    return valeur

def ajoutHote(nom,listePorts):
    h=Hote(nom)
    retour=[]
    for p in listePorts:
        bas=getBas()
        haut=getHaut()
        retour.append((p,bas,haut))
        h.ajoutConnexion(p,bas,haut)
    hs=d["hotes"]
    hs.append(h)
    d["hotes"]=hs
    return retour

if len(sys.argv)>2:
    nom=sys.argv[1]
    nums=[int(i) for i in sys.argv[2:]]
    retour=ajoutHote(nom,nums)
    for r in retour:
        print(r[0],r[1],r[2])
elif len(sys.argv)==2:
    for h in hotes:
        if sys.argv[1] in h.nom:
            print("on se connecte")
            ex("ssh pli@localhost -p "+str(h.cons[0].portLocal))
else:
    for h in hotes:
        print (h.nom)
        for c in h.cons:
            print ("\t",c.portHote,"->",c.portLocal)
d.close()

L'idée de ce fichier est de se rappeller quel ordinateur se connecte depuis quels ports, et vers ou ces ports sont redirigés, ainsi que le nom des ordinateurs hotes
Il y a trois manieres d'utiliser ce programme:
1) lister les ordinateurs et les ports enregistrés:

python3 numBasNumHaut.py

2) se connecter en ssh a un ordinateur dont on connait le nom, est qui est deja enregistré:

python3 numBasNumHaut.py bellemere

3) enregistrer un nouvel ordinateur, ainsi que les ports qui seront redirigés (cette commande est executée automatiquement depuis les hotes, a ne pas faire directement (cela n'aurait aucun effet)

python3 numBasNumHaut.py bellemere 22 443

Voila on a fini la configuration du serveur MERLO

Configuration des hotes (des machines des amis, de la famille, des petites copines):

Je viens juste d'installer un debian squeeze sur l'ordinateur KIKI de mon tonton, avec comme utilisateur principal POCHO que mon tonton va utiliser
La c'est un peu plus compliqué, beaucoup de réponses a faire; Se caler dans un petit répertoire tranquille d'un utilisateur normal, de POCHO par exemple:

    cd ~
    mkdir .monitor
    cd .monitor

et exécuter le programme suivant monirot.py avec comme arguments le nom que la machine aura sur MERLO (par exemple ORDITONTON) et les ports a rediriger (par exemple 22 pour ssh et 80 pour https:
python3 monirot.py ORDITONTON 22 80

from subprocess import Popen
import os,sys
ip="7.7.7.7" # IL FAUT METTRE ICI L'ip DE MERLO
login="CALAMAR"
maison=os.getcwd()
def ex(command):
    print(" on va executer: "+command)
    p = Popen(command, shell=True)
    sts = os.waitpid(p.pid, 0)[1]

if len(sys.argv)>2:
    nom=sys.argv[1]
    nums=[i for i in sys.argv[2:]]
    strii=nom+' '+' '.join(nums)
else:
    print("ERREUR AUCUN NOM NI PORT FOURNI")
    sys.exit(1)


ex("su -c 'apt-get install autossh ssh openssh-server'") #FOURNIR ICI LE MOT DE PASSE ROOT DE KIKI
ex("mkdir .ssh")
ex("ssh-keygen -f .ssh/id_rsa")
ex("ssh-copy-id -i "+maison+"/.ssh/id_rsa.pub '"+login+"@"+ip+"'") #FOURNIR ICI LE MOT DE PASSE DE CALAMAR SUR MERLO
ex("ssh "+login+"@"+ip+" 'python3 numBasNumHaut.py "+strii+"'>tmp.tmp") #FOURNIR ICI LE MOT DE PASSE DE CALAMAR SUR MERLO
ex("su -c 'adduser pli'") #FOURNIR ICI LE MOT DE PASSE ROOT DE KIKI
ex("su -c 'echo \"AllowUsers pli\nAllowGroups pli\" >> /etc/ssh/sshd_config'") #FOURNIR ICI LE MOT DE PASSE ROOT DE KIKI
ex("su -c 'service ssh restart'") #FOURNIR ICI LE MOT DE PASSE ROOT DE KIKI
conections=[]
with open("tmp.tmp") as f:
    for l in f.readlines():
        lt=l.split(" ")
        if len(lt)==3:
            a=int(lt[0])
            b=int(lt[1])
            c=int(lt[2])
            conections.append((a,b,c,lt[0]+".sh"))

ruu='''
#!/bin/bash
set +e
SSH_OPTIONS=" -i {}/.ssh/id_rsa"
# Always assume initial connection will be successful
export AUTOSSH_GATETIME=0
# Disable echo service, relying on SSH exiting itself
export AUTOSSH_PORT=0
#once proven, use (and rem out previous command):
autossh -f -- $SSH_OPTIONS -o 'ControlPath none' -R {}:localhost:{} {}@{} -N 2> /dev/null
'''
import shutil
shutil.copy("/etc/rc.local","tmprclocal")
for c in conections:
    f=open(c[3],"w")
    f.write(ruu.format(maison,c[1],c[0],login,ip))
    f.close()
    ex("chmod +x "+c[3])
    import fileinput
    for line in fileinput.FileInput("tmprclocal",inplace=1): 
        if "exit 0\n" in line:
            print("sleep 180 ")
            print("su "+os.getlogin()+" -c '"+maison+"/"+c[3]+"'")
        print (line.strip()),
ex("su -c 'cp tmprclocal /etc/rc.local'") #FOURNIR ICI LE MOT DE PASSE ROOT DE KIKI

Comme vous pouvez le voir, le script crée un utilisateur plip, auquel il vaut mieux fournir un mot de passe fort, et n'autorise l'accés ssh qu'à cet utilisateur!
Et voila, a chaque demarrage, le systeme attendra 180 secondes, et mettra en place le tunnel pour pouvoir se connecter depuis l'exterieur!
Bon au début je voulais commenter plus le code, mais j'ai la flemme, alors si vous avec des questions, je repondrais dans un commentaire

  • # et autossh, ca n'aurait pas été plus simple ?

    Posté par  . Évalué à 4.

    j'avoue j'ai pas tout lu, mais autossh me semble etre fait pour ca justement, pour ouvrir des connexions ssh des que la connexion internet est là.

    je l'utilisais dans l'autre sens, pour permettre à mon serveur internet chez un hebergeur d'utiliser le ldap de l'entreprise, ca marchait tres bien.

    • [^] # Re: et autossh, ca n'aurait pas été plus simple ?

      Posté par  . Évalué à 2.

      Oui effectivement, si tu avais lu, j'utilise autossh, je suppose que ça peut marcher sans la temporisation, mais j'ai pas testé;
      L'intérêt est surtout dans l'automatisation, en 5 minutes chrono sur un ordinateur j'installe ce système qui me permet d'y accéder des qu'il est connecté à internet quelque part dans le monde

  • # jeu, set et nat

    Posté par  (site web personnel) . Évalué à 3.

    j'aime bien donner des mots de passe simples a ma famille, genre la même chose que le login

    Pourquoi pas une phrase rigolote plutôt?

    Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

    • [^] # Re: jeu, set et nat

      Posté par  . Évalué à 2.

      pourquoi pas, je n'y avais pas pensé, mais bon certaines personnes tapent trés trés doucement, (81 ans), et dans ce cas non c'est pas terrible…

  • # Pourquoi faire simple lorsqu'il est possible de faire compliqué

    Posté par  . Évalué à 9.

    Installer OpenVPN prend moins de temps, et est plus efficace.
    Un coup de pare-feu sur le serveur central pour être tranquille, et c'est réglé.

    • [^] # Re: Pourquoi faire simple lorsqu'il est possible de faire compliqué

      Posté par  . Évalué à 1.

      Je me doutais bien qu'il y avait des solutions toutes prêtes pour ça, mais j'avais pas envie de me lancer dans la configuration d'un gros logiciel! Mais c'est pas seulement pour le ssh, j'ai aussi des serveurs http et https auxquels j'accède grâce a ce logiciel;

      • [^] # Re: Pourquoi faire simple lorsqu'il est possible de faire compliqué

        Posté par  (site web personnel, Mastodon) . Évalué à 2.

        Justement , utiliser un logiciel de VPN et en particulier OpenVPN te permet de faire aussi de l'accès aux serveurs HTTP, HTTPS ou autres. C'est fait pour !

        La création initiale du serveur VPN et du premier client peut s'avérer un peu difficile au premier abord, mais une fois que c'est fait, la création des clients suivants est aussi simple que :
        - générer les certificats ;
        - attribuer l'adresse IP du client (dans le cas d'IP fixes) ;
        - copier la configuration et les certificats sur le client ;
        - connecter et paramétrer la connexion au démarrage.

        J'avais démarré avec ce Howto de NBS System, en particulier la partie configuration. Plus besoin de compiler, OpenVPN est maintenant empaqueté dans la plupart des distributions Linux et BSD.

    • [^] # Re: Pourquoi faire simple lorsqu'il est possible de faire compliqué

      Posté par  . Évalué à 2.

      Je n'aime pas trop OpenVPN, par expérience OpenSSH est bien plus stable et surtout mille fois plus simple.
      Sinon tu fais comme tout le monde et tu installes Teamviewer, il y a un package pour Linux.

  • # openvpn ?

    Posté par  . Évalué à 6.

    Intuitivement, je serais plutôt parti sur de l'openvpn lancé au démarrage des clients vers le serveur, et un peu d'iptables sur les clients pour que les connexions entrantes en ssh ou autres ne soient possible que depuis le vpn. Suffit ensuite d'avoir soi-même un vpn vers le serveur, un peu de config dns et quelques routes, et hop, y a plus qu'a faire ssh geek@pctata.michu.corp depuis le portable à la terrasse du café de la plage en sirotant un long-island.

    J'avais un setup un peu similaire fut un temps pour me connecter à une box planquée derrière du nat en passant par un dédié.

    Faut pas gonfler Gérard Lambert quand il répare sa mobylette.

    • [^] # Re: openvpn ?

      Posté par  . Évalué à 2.

      Question de newsbee en VPN: est-ce que du coup un client qui se connecte se voit attribué une IP et est donc accessible des autres clients ou bien ?
      Merci.

      • [^] # Re: openvpn ?

        Posté par  . Évalué à 2.

        C'est comme on veut.
        Avec OpenVPN c'est intégré dans le logiciel : attribution automatique d'adresses, ou manuellement (ou même rien du tout, mode pont).
        Le routage est géré par le noyau, mais OpenVPN doit avoir certaines informations pour que ce soit ok.

        En général on fait en sorte que le routage fonctionne bien, et on utilise le pare-feu et/ou les réglages du VPN pour limiter les choses.

  • # Hidden service avec Tor

    Posté par  . Évalué à 4.

    On peut aussi utiliser Tor et son mécanisme de service caché.
    Je m'explique. Il faut pour cela installer Tor sur la machine distante ET la machine locale. Sur la machine distante il faut éditer /etc/tor/torrc pour y activer le service cacher pour SSH (seulement 2 lignes de configuration) puis il faut récupérer son adresse Tor qui est dans /var/lib/tor/hidden_service/hostname (c'est quelque-chose du genre ****************.onion).
    Ensuite, dés lors que la machine distante est allumé et en ligne je peux y accéder en SSH de cette manière : torify ssh user@****************.onion

  • # ssh sait faire ?

    Posté par  . Évalué à 3.

    Et utiliser ce genre de commande depuis les machines de ta famille (préalablement envoyée par mail) :

    ssh -R 19999:localhost:22 user@ton_ip

    Et depuis ta machine :

    ssh user_de_la_famille@localhost -p 19999

    Ça ne suffit pas ? C'est ce que je fais avec mes parents ou mes potes quand ils ont besoin d'un coup de main et qu'ils se trouvent derrière une machinBox…

    • [^] # Re: ssh sait faire ?

      Posté par  . Évalué à 0.

      Ben, ca suffirait ponctuellement, mais c'est pas trés pratique;
      En effet, certaines personnes tapent tres doucement, ne savent pas ouvrir un terminal, ne savent pas copier coller… enfin bon j'ai pas envie de passer 15 minutes au téléphone a leur expliquer la commande;

  • # Gestion de sources ?

    Posté par  . Évalué à 2.

    Si tu considères ton code crade, pourquoi ne pas le mettre sur git et le donner en pâtures aux hackers bienveillants ?
    Franchement, c'est plus sympa un git clone qu'un copier-coller depuis le navigateur ;)

  • # Gitso

    Posté par  (site web personnel) . Évalué à 3.

    En général quand un utilisateur lambda demande de l'aide, c'est sur son interface graphique… On a plus souvent besoin de lui montrer quelque chose sur l'écran que d'exécuter une commande SSH obscure dans son dos.

    Dans ce cas, en proprio tu as bien sûr TeamViewer (oui j'avoue, j'utilise ça, ça me simplifie grave la vie dans tout plein de situations) mais tu as également Gitso en full libre :
    http://code.google.com/p/gitso/

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.