From e1eaad381ddb1af70bc12351778b74f9cf98e187 Mon Sep 17 00:00:00 2001 From: Daniel STAN Date: Wed, 3 Jun 2015 19:44:06 +0200 Subject: [PATCH 1/6] fix possible import de client.py --- client.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client.py b/client.py index 5da07a8..65755bf 100755 --- a/client.py +++ b/client.py @@ -33,12 +33,14 @@ try: sys.path.append(os.path.expanduser("~/.config/%s/" % (bootstrap_cmd_name,))) import clientconfig as config except ImportError: - ducktape_display_error = sys.stderr.isatty() and not any([opt in sys.argv for opt in ["-q", "--quiet"]]) + ducktape_display_error = sys.stderr.isatty() and \ + not any([opt in sys.argv for opt in ["-q", "--quiet"]]) and \ + __name__ == '__main__' envspecified = os.getenv(envvar, None) if envspecified is None: if ducktape_display_error: sys.stderr.write(u"Va lire le fichier README.\n".encode("utf-8")) - sys.exit(1) + sys.exit(1) else: # On a spécifié à la main le dossier de conf try: From 6377ccb9e771f2917ddedf979d7c708ca76d6f15 Mon Sep 17 00:00:00 2001 From: Daniel STAN Date: Wed, 3 Jun 2015 19:47:08 +0200 Subject: [PATCH 2/6] =?UTF-8?q?tudor:=20m=C3=A0j=20fichiers=20conf=20perso?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- clientconfigs/tudor/clientconfig.py | 19 +++++++++++++++++-- serverconfigs/tudor/.gitignore | 1 - serverconfigs/tudor/cpasswords-server | 12 ++++++++++++ serverconfigs/tudor/serverconfig.py | 6 +++--- 4 files changed, 32 insertions(+), 6 deletions(-) delete mode 100644 serverconfigs/tudor/.gitignore create mode 100755 serverconfigs/tudor/cpasswords-server diff --git a/clientconfigs/tudor/clientconfig.py b/clientconfigs/tudor/clientconfig.py index d8120df..bc5a982 100644 --- a/clientconfigs/tudor/clientconfig.py +++ b/clientconfigs/tudor/clientconfig.py @@ -1,9 +1,24 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- +""" Configuration du client cranspasswords """ + +import os + + +#: Liste des serveurs sur lesquels ont peut récupérer des mots de passe. +#: +#: Sans précision du paramètre --server, la clé ``'default'`` sera utilisée. +#: +#: * ``'server_cmd'`` : La commande exécutée sur le client pour appeler +#: le script sur le serveur distant. servers = { 'default': { 'server_cmd': ['/home/dstan/cranspasswords/serverconfigs/tudor/cpasswords-server', ], - 'keep-alive': True, # <-- experimental, n'ouvre qu'une connexion - } + 'keep-alive': True, + }, + 'gladys': { + 'server_cmd': ['/usr/bin/ssh', 'home.b2moo.fr', '/home/dstan/cranspasswords/serverconfigs/tudor/cpasswords-server', ], + 'keep-alive': True, + }, } diff --git a/serverconfigs/tudor/.gitignore b/serverconfigs/tudor/.gitignore deleted file mode 100644 index 23cf86a..0000000 --- a/serverconfigs/tudor/.gitignore +++ /dev/null @@ -1 +0,0 @@ -cpasswords-server diff --git a/serverconfigs/tudor/cpasswords-server b/serverconfigs/tudor/cpasswords-server new file mode 100755 index 0000000..038c71b --- /dev/null +++ b/serverconfigs/tudor/cpasswords-server @@ -0,0 +1,12 @@ +#!/bin/bash + +# Où trouver le paquet python +PKG_DIR=~/cranspasswords + +# Où trouver la conf serveur +CONF=$PKG_DIR/serverconfigs/tudor + +# Binaire python +PYTHON=/usr/bin/python + +/usr/bin/env PYTHONPATH=$PKG_DIR CRANSPASSWORDS_SERVER_CONFIG_DIR=$CONF $PYTHON $PKG_DIR/cpasswords/server.py "$@" diff --git a/serverconfigs/tudor/serverconfig.py b/serverconfigs/tudor/serverconfig.py index 99cf6ca..6e136c5 100755 --- a/serverconfigs/tudor/serverconfig.py +++ b/serverconfigs/tudor/serverconfig.py @@ -30,10 +30,10 @@ KEYS = { } -ME = [u'dstan'] +_ME = [u'dstan'] #: Les roles utilisés pour savoir qui a le droit le lire/écrire quoi ROLES = { - 'moi': ME, - 'moi-w': ME, + 'moi': _ME, + 'moi-w': _ME, } From 74ee60e86cd6d9a6746658e267f29d3d25277789 Mon Sep 17 00:00:00 2001 From: Daniel STAN Date: Wed, 3 Jun 2015 19:52:02 +0200 Subject: [PATCH 3/6] =?UTF-8?q?d=C3=A9but=20de=20r=C3=A9plication?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cpasswords/clientlib.py | 25 +++++++++++++++++++++++++ server.py | 28 ++++++++++++++++++++++++++-- serverconfigs/tudor/serverconfig.py | 12 ++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 cpasswords/clientlib.py diff --git a/cpasswords/clientlib.py b/cpasswords/clientlib.py new file mode 100644 index 0000000..247df05 --- /dev/null +++ b/cpasswords/clientlib.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +"""Client class definition for cpasswords protocol. + +(WIP) +""" + +from cpasswords import client as _old_client + +class Client(object): + """A client connection.""" + + verbose = False + + def __init__(self, serverdata): + """ + serverdata should be a classic dict object (from eg a clientconfig + module) + """ + self.serverdata = serverdata + + def put_file(self, data): + """Send file to server""" + # TODO put code here + _old_client.put_files(self, [data]) diff --git a/server.py b/server.py index a26c2fd..27db200 100755 --- a/server.py +++ b/server.py @@ -17,10 +17,19 @@ import itertools from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart +try: + from cpasswords import clientlib +except ImportError: + print("Couldn't import clientlib. Remote sync may not work") + # Même problème que pour le client, il faut bootstraper le nom de la commande # Pour accéder à la config -cmd_name = os.path.split(sys.argv[0])[1].replace("-server", "") -sys.path.append("/etc/%s/" % (cmd_name,)) +conf_path = os.getenv('CRANSPASSWORDS_SERVER_CONFIG_DIR', None) +if not conf_path: + cmd_name = os.path.split(sys.argv[0])[1].replace("-server", "") + conf_path = "/etc/%s/" % (cmd_name,) + +sys.path.append(conf_path) import serverconfig MYUID = pwd.getpwuid(os.getuid())[0] @@ -190,6 +199,11 @@ def _putfile(filename, roles, contents): # Or fuck yourself writefile(filepath, json.dumps({'roles': roles, 'contents': contents})) + + data = {'filename': filename, 'roles': roles, 'contents': contents} + for client in _list_to_replicate(data): + client.put_file(data) + return [True, u"Modification effectuée."] @server_command('putfile', stdin_input=True, write=True) @@ -245,6 +259,16 @@ def backup(corps, fname, old): back.write((u'* %s: %s\n' % (str(datetime.datetime.now()), corps)).encode("utf-8")) back.close() +def _list_to_replicate(data): + """Renvoie une liste d'options clients sur lesquels appliquer relancer + la procédure (pour réplication auto)""" + roles = data.get('roles', []) + backups = getattr(serverconfig, 'BACKUP_ROLES', {}) + servers = getattr(serverconfig, 'BACKUP_SERVERS', {}) + + configs = set(name for role in roles for name in backups.get(role, [])) + return [ clientlib.Client(servers[name]) for name in configs ] + _notif_todo = [] def notification(action, fname, actor): """Enregistre une notification""" diff --git a/serverconfigs/tudor/serverconfig.py b/serverconfigs/tudor/serverconfig.py index 6e136c5..7c4e714 100755 --- a/serverconfigs/tudor/serverconfig.py +++ b/serverconfigs/tudor/serverconfig.py @@ -37,3 +37,15 @@ ROLES = { 'moi': _ME, 'moi-w': _ME, } + +BACKUP_SERVERS = { + 'gladys': { + 'server_cmd': ['/usr/bin/ssh', 'home.b2moo.fr', '/home/dstan/cranspasswords/serverconfigs/tudor/cpasswords-server', ], + 'keep-alive': True, + }, +} + + +BACKUP_ROLES = { + 'moi': ['gladys'], +} From 81326554d7df2decddf8689cebf2dee3b27d9f7c Mon Sep 17 00:00:00 2001 From: Daniel STAN Date: Sat, 7 Nov 2015 18:45:47 +0100 Subject: [PATCH 4/6] =?UTF-8?q?m=C3=A0j=20clientconfig=20tudor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO | 3 +++ clientconfigs/tudor/clientconfig.py | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/TODO b/TODO index d3b7e44..31faf73 100644 --- a/TODO +++ b/TODO @@ -37,3 +37,6 @@ index 6ba2a6e..6486e11 100755 parser.add_argument('fname', nargs='?', default=None, help="Nom du fichier à afficher") +ssh-keygen -y -f ~/test_agent > test_agent.pub +ssh-add ma_cle_prive +ssh-add -d ma_cle_publique diff --git a/clientconfigs/tudor/clientconfig.py b/clientconfigs/tudor/clientconfig.py index bc5a982..007a2fa 100644 --- a/clientconfigs/tudor/clientconfig.py +++ b/clientconfigs/tudor/clientconfig.py @@ -21,4 +21,12 @@ servers = { 'server_cmd': ['/usr/bin/ssh', 'home.b2moo.fr', '/home/dstan/cranspasswords/serverconfigs/tudor/cpasswords-server', ], 'keep-alive': True, }, + 'gladys-home': { + 'server_cmd': ['/usr/bin/ssh', 'gladys.home', '/home/dstan/cranspasswords/serverconfigs/tudor/cpasswords-server', ], + 'keep-alive': True, + }, + 'pimeys': { + 'server_cmd': ['/usr/bin/ssh', 'pimeys.fr', 'sudo', '-n', '/usr/local/bin/cranspasswords-server', ], + 'keep-alive': True, + }, } From 39db981f906bf218693f2f16699a409045e646b7 Mon Sep 17 00:00:00 2001 From: Daniel STAN Date: Tue, 10 Nov 2015 00:27:32 +0100 Subject: [PATCH 5/6] =?UTF-8?q?draft:=20gestion=20des=20cl=C3=A9s=20ssh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client.py | 72 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 17 deletions(-) diff --git a/client.py b/client.py index 65755bf..fbc4029 100755 --- a/client.py +++ b/client.py @@ -654,39 +654,77 @@ def show_file(options): return passfile = value (sin, sout) = gpg(options, 'decrypt') - content = passfile['contents'] # Kludge (broken db ?) + content = passfile['contents'] + + # Kludge (broken db ?) if type(content) == list: print("Eau dans le gaz") content = content[-1] + + # Déchiffre le contenu sin.write(content.encode("utf-8")) sin.close() texte = sout.read().decode("utf-8") - ntexte = u"" - hidden = False # Est-ce que le mot de passe a été caché ? - lines = texte.split('\n') + + # Est-ce une clé ssh ? + is_key = texte.startswith('-----BEGIN RSA PRIVATE KEY-----') + # Est-ce que le mot de passe a été caché ? (si non, on utilisera less) + is_hidden = is_key + # Texte avec mdp caché + filtered = u"" + # Ancien contenu du press papier old_clipboard = None - for line in lines: + + # Essaie de planquer le mot de passe + for line in texte.split('\n'): catchPass = None # On essaie de trouver le pass pour le cacher dans le clipboard # si ce n'est déjà fait et si c'est voulu - if not hidden and options.clipboard: + if not is_hidden and options.clipboard: catchPass = pass_regexp.match(line) if catchPass != None: - hidden = True - # On met le mdp dans le clipboard en mémorisant sont ancien contenu + is_hidden = True + # On met le mdp dans le clipboard en mémorisant son ancien contenu old_clipboard = clipboard(catchPass.group(1)) # Et donc on override l'affichage line = u"[Le mot de passe a été mis dans le presse papier]" - ntexte += line + '\n' - showbin = "cat" if hidden else "less" - proc = subprocess.Popen([showbin], stdin=subprocess.PIPE) - out = proc.stdin - raw = u"Fichier %s:\n\n%s-----\nVisible par: %s\n" % (fname, ntexte, ','.join(passfile['roles'])) - out.write(raw.encode("utf-8")) - out.close() - os.waitpid(proc.pid, 0) + filtered += line + '\n' + + if is_key: + filtered = u"La clé a été mise dans l'agent ssh" + shown = u"Fichier %s:\n\n%s-----\nVisible par: %s\n" % (fname, filtered, ','.join(passfile['roles'])) + + if is_key: + with tempfile.NamedTemporaryFile(suffix='') as key_file: + # Génère la clé publique correspondante + key_file.write(texte.encode('utf-8')) + key_file.flush() + pub = subprocess.check_output(['ssh-keygen', '-y', '-f', key_file.name]) + + # Charge en mémoire + subprocess.check_call(['ssh-add', key_file.name]) + + # On attend + print(shown.encode('utf-8')) + raw_input() + + # On met la clé publique en fichier puis on supprime + key_file.seek(0) + key_file.write(pub) + key_file.flush() + + subprocess.check_call(['ssh-add', '-d', key_file.name]) + else: + # Le binaire à utiliser + showbin = "cat" if is_hidden else "less" + proc = subprocess.Popen([showbin], stdin=subprocess.PIPE) + out = proc.stdin + out.write(shown.encode("utf-8")) + out.close() + os.waitpid(proc.pid, 0) + # Repope ancien pass - if options.clipboard: + if old_clipboard: saveclipboard(restore=True, old_clipboard=old_clipboard) @need_filename From b5c9d0d5ca6dc03e9e81843b0978b08bd8d912e1 Mon Sep 17 00:00:00 2001 From: Daniel STAN Date: Sun, 20 Dec 2015 20:53:13 +0100 Subject: [PATCH 6/6] fname -> filename dans fonction rmfile --- server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.py b/server.py index 27db200..d0f5bd1 100755 --- a/server.py +++ b/server.py @@ -242,7 +242,7 @@ def rmfile(filename): if validate(roles, 'w'): corps = u"Le fichier %s a été supprimé par %s." % (filename, MYUID) backup(corps, filename, old) - notification(u"Suppression", fname, MYUID) + notification(u"Suppression", filename, MYUID) os.remove(getpath(filename)) else: return u"Vous n'avez pas les droits d'écriture sur le fichier %s." % filename