Commit cd6631b4 authored by Ulysse Beaugnon's avatar Ulysse Beaugnon

Merge branch 'master' of https://git.erp5.org/repos/vifibnet

parents f8574d91 4d956fea
#!/usr/bin/env python #!/usr/bin/env python
import argparse, random, smtplib, sqlite3 import argparse, math, random, smtplib, sqlite3, string, time
from email.mime.text import MIMEText from email.mime.text import MIMEText
from SimpleXMLRPCServer import SimpleXMLRPCServer from functools import wraps
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
from OpenSSL import crypto from OpenSSL import crypto
import netaddr import traceback
class RequestHandler(SimpleXMLRPCRequestHandler):
def _dispatch(self, method, params):
return self.server._dispatch(method, (self,) + params)
class main(object): class main(object):
def __init__(self): def __init__(self):
self.cert_duration = 365 * 86400
# Command line parsing # Command line parsing
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Peer discovery http server for vifibnet') description='Peer discovery http server for vifibnet')
...@@ -18,46 +26,62 @@ class main(object): ...@@ -18,46 +26,62 @@ class main(object):
help='Path to ca.crt file') help='Path to ca.crt file')
_('--key', required=True, _('--key', required=True,
help='Path to certificate key') help='Path to certificate key')
_('--network', required=True, _('--mailhost', required=True,
help='Vifib subnet') help='SMTP server mail host')
config = parser.parser_arg() self.config = parser.parse_args()
# Database initializing # Database initializing
self.db = sqlite3.connect(config.db, isolation_level=None) self.db = sqlite3.connect(self.config.db, isolation_level=None)
self.db.execute("""CREATE TABLE IF NOT EXISTS peers (
prefix text primary key not null,
ip text not null,
port integer not null,
proto text not null)""")
self.db.execute("""CREATE TABLE IF NOT EXISTS tokens ( self.db.execute("""CREATE TABLE IF NOT EXISTS tokens (
token text primary key not null, token text primary key not null,
email text not null, email text not null,
prefix_len integer not null default 16, prefix_len integer not null,
date integer not null)""") date integer not null)""")
self.db.execute("""CREATE TABLE IF NOT EXISTS certificates ( try:
self.db.execute("""CREATE TABLE vifib (
prefix text primary key not null, prefix text primary key not null,
email text not null, email text,
cert text not null)""") cert text)""")
except sqlite3.OperationalError, e:
if e.args[0] == 'table vifib already exists':
pass
else:
raise RuntimeError
else:
self.db.execute("INSERT INTO vifib VALUES ('',null,null)")
# Loading certificates # Loading certificates
with open(config.ca) as f: with open(self.config.ca) as f:
self.ca = crypto.load_certificate(crypto.FILETYPE_PEM, f.read()) self.ca = crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
with open(config.key) as f: with open(self.config.key) as f:
self.key = crypto.load_privatekey(crypto.FILETYPE_PEM, f.read()) self.key = crypto.load_privatekey(crypto.FILETYPE_PEM, f.read())
# Get vifib network prefix
self.network = bin(self.ca.get_serial_number())[3:]
# Starting server # Starting server
server = SimpleXMLRPCServer(("localhost", 8000)) server = SimpleXMLRPCServer(("localhost", 8000), requestHandler=RequestHandler, allow_none=True)
server.register_instance(self) server.register_instance(self)
server.serve_forever() server.serve_forever()
def requestToken(self, email): def requestToken(self, handler, email):
while True: while True:
# Generating token # Generating token
token = ''.join(random.sample(string.ascii_lowercase, 8)) token = ''.join(random.sample(string.ascii_lowercase, 8))
# Updating database # Updating database
try: try:
self.db.execute("INSERT INTO tokens (?,?,null,?)", (token, email, int(time.time()))) self.db.execute("INSERT INTO tokens VALUES (?,?,?,?)", (token, email, 16, int(time.time())))
break break
except sqlite3.IntegrityError, e: except sqlite3.IntegrityError, e:
pass pass
# Creating and sending email # Creating and sending email
s = smtplib.SMTP('localhost') s = smtplib.SMTP(self.config.mailhost)
me = 'postmaster@vifibnet.com' me = 'postmaster@vifibnet.com'
msg = MIMEText('Hello world !\nYour token : %s' % (token,)) msg = MIMEText('Hello world !\nYour token : %s' % (token,))
msg['Subject'] = '[Vifibnet] Token Request' msg['Subject'] = '[Vifibnet] Token Request'
...@@ -66,45 +90,67 @@ class main(object): ...@@ -66,45 +90,67 @@ class main(object):
s.sendmail(me, email, msg.as_string()) s.sendmail(me, email, msg.as_string())
s.quit() s.quit()
def requestCertificate(self, token, cert_req): def _getPrefix(self, prefix_len):
n = len(cert_req_list) assert 0 < prefix_len <= 128 - len(self.network)
for prefix, in self.db.execute("""SELECT prefix FROM vifib WHERE length(prefix) <= ? AND cert is null
ORDER BY length(prefix) DESC""", (prefix_len,)):
while len(prefix) < prefix_len:
self.db.execute("UPDATE vifib SET prefix = ? WHERE prefix = ?", (prefix + '1', prefix))
prefix += '0'
self.db.execute("INSERT INTO vifib VALUES (?,null,null)", (prefix,))
return prefix
raise RuntimeError # TODO: raise better exception
def requestCertificate(self, handler, token, cert_req):
try:
req = crypto.load_certificate_request(crypto.FILETYPE_PEM, cert_req) req = crypto.load_certificate_request(crypto.FILETYPE_PEM, cert_req)
with self.db:
try: try:
# TODO : check syntax token, email, prefix_len, _ = self.db.execute("SELECT * FROM tokens WHERE token = ?", (token,)).next()
token, email, prefix_len, _ = self.db.execute("SELECT * FROM tokens WHERE token = ?", (token,)).fetchone() except StopIteration:
# TODO: return nice error message
raise
self.db.execute("DELETE FROM tokens WHERE token = ?", (token,)) self.db.execute("DELETE FROM tokens WHERE token = ?", (token,))
# Create a new prefix # Get a new prefix
# TODO : FIX ! prefix = self._getPrefix(prefix_len)
# i impair => ok
# récursif sinon
for i, prefix in enumerate(self.db.execute("""SELECT DISTINCT substr(prefix,1,?) FROM certificates
WHERE length(prefix) >= ? ORDER BY prefix""", (prefix_len, prefix_len))):
if i != int(prefix, 2):
pass
break
else:
prefix = i
# create certificate # Create certificate
cert = crypto.X509() cert = crypto.X509()
#cert.set_serial_number(serial) #cert.set_serial_number(serial)
#cert.gmtime_adj_notBefore(notBefore) cert.gmtime_adj_notBefore(0)
#cert.gmtime_adj_notAfter(notAfter) cert.gmtime_adj_notAfter(self.cert_duration)
cert.set_issuer(self.ca.get_subject()) cert.set_issuer(self.ca.get_subject())
cert.set_subject(req.get_subject()) subject = req.get_subject()
subject.serialNumber = "%u/%u" % (int(prefix, 2), prefix_len)
cert.set_subject(subject)
cert.set_pubkey(req.get_pubkey()) cert.set_pubkey(req.get_pubkey())
cert.sign(self.key, 'sha1') cert.sign(self.key, 'sha1')
cert = crypto.dump_certificate(crypto.FILETYPE_PEM, cert) cert = crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
# Insert certificate into db # Insert certificate into db
self.db.execute("INSERT INTO certificates (?,?)", (, email, cert) ) self.db.execute("UPDATE vifib SET email = ?, cert = ? WHERE prefix = ?", (email, cert, prefix) )
# Returning certificate
return cert return cert
except: Exception: except:
# TODO : what to do ? traceback.print_exc()
pass raise
def getCa(self, handler):
return crypto.dump_certificate(crypto.FILETYPE_PEM, self.ca)
def declare(self, handler, address):
# guess prefix from handler.client_address
ip1, ip2 = struct.unpack('>QQ', socket.inet_pton(socket.AF_INET6, handler.client_address)))
prefix = bin(ip1)[2:] + bin(ip2)]2:]
ip, port, proto = address
self.db.execute("INSERT INTO peers VALUES (?,?,?,?)", (prefix, ip, port, proto))
def getPeerList(self, handler, n, address):
assert 0 < n < 1000
self.declare(handler, address)
return self.db.execute("SELECT ip, port, proto FROM peers ORDER BY random() LIMIT ?", (n,)).fetchall()
if __name__ == "__main__": if __name__ == "__main__":
main() main()
#!/usr/bin/env python
from OpenSSL import crypto
import argparse, os, subprocess, xmlrpclib
def main():
parser = argparse.ArgumentParser(
description='Setup script for vifib')
_ = parser.add_argument
_('--server', required=True,
help='Address of the server delivering certifiactes')
_('--port', required=True, type=int,
help='Port to which connect on the server')
_('-d', '--dir', default='/etc/vifib',
help='Directory where the key and certificate will be stored')
_('-r', '--req', nargs='+',
help='''Certificate request additional arguments. For example :
--req name1 value1 name2 value2, to add attributes name1 and name2''')
config = parser.parse_args()
if config.req and len(config.req) % 2 == 1:
print "Sorry, request argument was incorrect, there must be an even number of request arguments"
os.exit(1)
# Get token
email = raw_input('Please enter your email address : ')
s = xmlrpclib.ServerProxy('http://%s:%u' % (config.server, config.port))
_ = s.requestToken(email)
token = raw_input('Please enter your token : ')
# Generate key and cert request
pkey = crypto.PKey()
pkey.generate_key(crypto.TYPE_RSA, 2048)
key = crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey)
req = crypto.X509Req()
subj = req.get_subject()
if config.req:
while len(config.req) > 1:
key = config.req.pop(0)
value = config.req.pop(0)
setattr(subj, key, value)
req.set_pubkey(pkey)
req.sign(pkey, 'sha1')
req = crypto.dump_certificate_request(crypto.FILETYPE_PEM, req)
# Get certificates
ca = s.getCa()
cert = s.requestCertificate(token,req)
# Generating dh file
subprocess.call(['openssl', 'dhparam', '-out', os.path.join(config.dir, 'dh2048.pem'), '2048'])
# Store cert and key
with open(os.path.join(config.dir, 'cert.key'), 'w') as f:
f.write(key)
with open(os.path.join(config.dir, 'cert.crt'), 'w') as f:
f.write(cert)
with open(os.path.join(config.dir, 'ca.pem'), 'w') as f:
f.write(ca)
print "Certificate setup complete."
if __name__ == "__main__":
main()
#!/usr/bin/env python #!/usr/bin/env python
import argparse, errno, os, select, sqlite3, subprocess, sys, time import argparse, errno, os, select, sqlite3, subprocess, sys, time, xmlrpclib
import traceback import traceback
import upnpigd import upnpigd
import openvpn import openvpn
...@@ -11,22 +11,34 @@ connection_dict = {} # to remember current connections we made ...@@ -11,22 +11,34 @@ connection_dict = {} # to remember current connections we made
free_interface_set = set(('client1', 'client2', 'client3', 'client4', 'client5', free_interface_set = set(('client1', 'client2', 'client3', 'client4', 'client5',
'client6', 'client7', 'client8', 'client9', 'client10')) 'client6', 'client7', 'client8', 'client9', 'client10'))
# TODO : How do we get our vifib ip ?
# TODO : flag in some way the peers that are connected to us so we don't connect to them # TODO : flag in some way the peers that are connected to us so we don't connect to them
# Or maybe we just don't care, # Or maybe we just don't care,
class PeersDB: class PeersDB:
def __init__(self, dbPath): def __init__(self, dbPath):
self.proxy = xmlrpclib.ServerProxy('http://%s:%u' % (config.server, config.server_port))
log.log('Connectiong to peers database', 4) log.log('Connectiong to peers database', 4)
self.db = sqlite3.connect(dbPath, isolation_level=None) self.db = sqlite3.connect(dbPath, isolation_level=None)
log.log('Initializing peers database', 4) log.log('Initializing peers database', 4)
self.db.execute("""CREATE TABLE IF NOT EXISTS peers try:
( id INTEGER PRIMARY KEY AUTOINCREMENT, self.db.execute("""CREATE TABLE peers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ip TEXT NOT NULL, ip TEXT NOT NULL,
port INTEGER NOT NULL, port INTEGER NOT NULL,
proto TEXT NOT NULL, proto TEXT NOT NULL,
used INTEGER NOT NULL)""") used INTEGER NOT NULL default 0)""")
self.db.execute("CREATE INDEX IF NOT EXISTS _peers_used ON peers(used)") self.db.execute("CREATE INDEX _peers_used ON peers(used)")
self.db.execute("UPDATE peers SET used = 0") self.db.execute("UPDATE peers SET used = 0")
except sqlite3.OperationalError, e:
if e.args[0] != 'table peers already exists':
raise RuntimeError
else:
self.populateDB(100)
def populateDB(self, n):
(ip, port) = upnpigd.GetExternalInfo(1194)
proto = 'udp'
self.db.executemany("INSERT INTO peers (ip, port, proto) VALUES ?", self.proxy.getPeerList(n, port, proto))
def getUnusedPeers(self, nPeers): def getUnusedPeers(self, nPeers):
return self.db.execute("SELECT id, ip, port, proto FROM peers WHERE used = 0 " return self.db.execute("SELECT id, ip, port, proto FROM peers WHERE used = 0 "
...@@ -40,6 +52,12 @@ class PeersDB: ...@@ -40,6 +52,12 @@ class PeersDB:
log.log('Updating peers database : unusing peer ' + str(id), 5) log.log('Updating peers database : unusing peer ' + str(id), 5)
self.db.execute("UPDATE peers SET used = 0 WHERE id = ?", (id,)) self.db.execute("UPDATE peers SET used = 0 WHERE id = ?", (id,))
def ipFromPrefix(prefix, prefix_len):
tmp = hew(int(prefix, 2))[2::]
ip = VIFIB_NET
for i in xrange(0, len(ip), 4):
ip += tmp[i:i+4] + ':'
ip += ':'
def startBabel(**kw): def startBabel(**kw):
args = ['babeld', args = ['babeld',
...@@ -65,6 +83,10 @@ def getConfig(): ...@@ -65,6 +83,10 @@ def getConfig():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Resilient virtual private network application') description='Resilient virtual private network application')
_ = parser.add_argument _ = parser.add_argument
_('--server', required=True,
help='Address for peer discovery server')
_('--server-port', required=True,
help='Peer discovery server port')
_('--log-directory', default='/var/log', _('--log-directory', default='/var/log',
help='Path to vifibnet logs directory') help='Path to vifibnet logs directory')
_('--client-count', default=2, type=int, _('--client-count', default=2, type=int,
...@@ -84,6 +106,8 @@ def getConfig(): ...@@ -84,6 +106,8 @@ def getConfig():
help='Path to babeld state-file') help='Path to babeld state-file')
_('--verbose', '-v', default=0, type=int, _('--verbose', '-v', default=0, type=int,
help='Defines the verbose level') help='Defines the verbose level')
_('--cert', required=True,
help='Path to the certificate file')
# Temporary args - to be removed # Temporary args - to be removed
_('--ip', required=True, _('--ip', required=True,
help='IPv6 of the server') help='IPv6 of the server')
...@@ -91,8 +115,18 @@ def getConfig(): ...@@ -91,8 +115,18 @@ def getConfig():
_('openvpn_args', nargs=argparse.REMAINDER, _('openvpn_args', nargs=argparse.REMAINDER,
help="Common OpenVPN options (e.g. certificates)") help="Common OpenVPN options (e.g. certificates)")
openvpn.config = config = parser.parse_args() openvpn.config = config = parser.parse_args()
with open(config.cert, 'r') as f:
cert = crypto.load_certificate(crypto.FILETYPE_PEM, f)
subject = cert.get_subject()
prefix_txt, prefix_len_txt = subject.serialNumber.split('/')
prefix = int(prefix_txt)
prefix_len = int(prefix_len_txt)
ip = ipFromPrefix(prefix)
print ip
if config.openvpn_args[0] == "--": if config.openvpn_args[0] == "--":
del config.openvpn_args[0] del config.openvpn_args[0]
config.openvpn_args.append('--cert')
config.openvpn_args.append(config.cert)
def startNewConnection(n): def startNewConnection(n):
try: try:
...@@ -100,7 +134,8 @@ def startNewConnection(n): ...@@ -100,7 +134,8 @@ def startNewConnection(n):
log.log('Establishing a connection with id %s (%s:%s)' % (id,ip,port), 2) log.log('Establishing a connection with id %s (%s:%s)' % (id,ip,port), 2)
iface = free_interface_set.pop() iface = free_interface_set.pop()
connection_dict[id] = ( openvpn.client( ip, '--dev', iface, '--proto', proto, '--rport', str(port), connection_dict[id] = ( openvpn.client( ip, '--dev', iface, '--proto', proto, '--rport', str(port),
stdout=os.open('%s/vifibnet.client.%s.log' % (config.log_directory, id), os.O_WRONLY|os.O_CREAT|os.O_TRUNC) ), stdout=os.open(os.path.join(config.log_directory, 'vifibnet.client.%s.log' % (id,)),
os.O_WRONLY|os.O_CREAT|os.O_TRUNC) ),
iface) iface)
peers_db.usePeer(id) peers_db.usePeer(id)
except KeyError: except KeyError:
...@@ -158,6 +193,7 @@ def main(): ...@@ -158,6 +193,7 @@ def main():
# Get arguments # Get arguments
getConfig() getConfig()
log.verbose = config.verbose log.verbose = config.verbose
# TODO: get proto to use ?
(externalIp, externalPort) = upnpigd.GetExternalInfo(1194) (externalIp, externalPort) = upnpigd.GetExternalInfo(1194)
# Setup database # Setup database
...@@ -177,7 +213,7 @@ def main(): ...@@ -177,7 +213,7 @@ def main():
# Establish connections # Establish connections
log.log('Starting openvpn server', 3) log.log('Starting openvpn server', 3)
serverProcess = openvpn.server(config.ip, write_pipe, '--dev', 'vifibnet', serverProcess = openvpn.server(config.ip, write_pipe, '--dev', 'vifibnet',
stdout=os.open('%s/vifibnet.server.log' % (config.log_directory,), os.O_WRONLY | os.O_CREAT | os.O_TRUNC)) stdout=os.open(os.path.join(config.log_directory, 'vifibnet.server.log'), os.O_WRONLY | os.O_CREAT | os.O_TRUNC))
startNewConnection(config.client_count) startNewConnection(config.client_count)
# Timed refresh initializing # Timed refresh initializing
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment