Browse Source

Added reseed https server action + fixed some issues + refactored code

pull/1/head
l-n-s 8 years ago
parent
commit
d7db5b1640
  1. 73
      pyseeder.py
  2. 52
      pyseeder/actions.py
  3. 12
      pyseeder/crypto.py
  4. 34
      pyseeder/server.py
  5. 19
      pyseeder/su3file.py
  6. 2
      pyseeder/transport.py
  7. 2
      pyseeder/transports/git.py
  8. 11
      pyseeder/utils.py

73
pyseeder.py

@ -1,48 +1,12 @@
#! /usr/bin/env python3 #! /usr/bin/env python3
import os, os.path import os
import sys import sys
from getpass import getpass
import argparse import argparse
from pyseeder.crypto import keygen
from pyseeder.su3file import SU3File
import pyseeder.transport import pyseeder.transport
import pyseeder.actions
from pyseeder.utils import PyseederException from pyseeder.utils import PyseederException
def keygen_action(args):
"""Sub-command to generate keys"""
priv_key_password = getpass("Set private key password: ").encode("utf-8")
keygen(args.cert, args.private_key, priv_key_password, args.signer_id)
def reseed_action(args):
"""Sub-command to generate reseed file"""
priv_key_password = input().encode("utf-8")
su3file = SU3File(args.signer_id)
su3file.reseed(args.netdb)
su3file.write(args.outfile, args.private_key, priv_key_password)
def transport_pull_action(args):
"""Sub-command for downloading su3 file"""
import random
random.shuffle(args.urls)
for u in args.urls:
if pyseeder.transport.download(u, args.outfile):
return True
raise PyseederException("Failed to download su3 file")
def transport_push_action(args):
"""Sub-command for uploading su3 file with transports"""
if not os.path.isfile(args.config):
raise PyseederException("Can't read transports config file")
import configparser
config = configparser.ConfigParser()
config.read(args.config)
pyseeder.transport.upload(args.file, config)
def main(): def main():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(title="actions", subparsers = parser.add_subparsers(title="actions",
@ -60,8 +24,8 @@ def main():
kg_parser.add_argument("--private-key", default="data/priv_key.pem", kg_parser.add_argument("--private-key", default="data/priv_key.pem",
help="RSA private key (default: data/priv_key.pem)") help="RSA private key (default: data/priv_key.pem)")
kg_parser.add_argument("--cert", required=True, kg_parser.add_argument("--cert", required=True,
help="Certificate (example: output/user_at_mail.i2p.crt)") help="Certificate (example: data/user_at_mail.i2p.crt)")
kg_parser.set_defaults(func=keygen_action) kg_parser.set_defaults(func=pyseeder.actions.keygen)
rs_parser = subparsers.add_parser( rs_parser = subparsers.add_parser(
@ -80,7 +44,7 @@ echo $YOUR_PASSWORD | %(prog)s --netdb /path/to/netDb \\
help="Output file (default: output/i2pseeds.su3)") help="Output file (default: output/i2pseeds.su3)")
rs_parser.add_argument("--netdb", required=True, rs_parser.add_argument("--netdb", required=True,
help="Path to netDb folder (example: ~/.i2pd/netDb)") help="Path to netDb folder (example: ~/.i2pd/netDb)")
rs_parser.set_defaults(func=reseed_action) rs_parser.set_defaults(func=pyseeder.actions.reseed)
tpull_parser = subparsers.add_parser( tpull_parser = subparsers.add_parser(
@ -96,7 +60,7 @@ echo $YOUR_PASSWORD | %(prog)s --netdb /path/to/netDb \\
mainline I2P (like https://reseed.i2p-projekt.de/)""") mainline I2P (like https://reseed.i2p-projekt.de/)""")
tpull_parser.add_argument("-o", "--outfile", default="output/i2pseeds.su3", tpull_parser.add_argument("-o", "--outfile", default="output/i2pseeds.su3",
help="Output file (default: output/i2pseeds.su3)") help="Output file (default: output/i2pseeds.su3)")
tpull_parser.set_defaults(func=transport_pull_action) tpull_parser.set_defaults(func=pyseeder.actions.transport_pull)
tpush_parser = subparsers.add_parser( tpush_parser = subparsers.add_parser(
@ -108,7 +72,30 @@ echo $YOUR_PASSWORD | %(prog)s --netdb /path/to/netDb \\
help="Transports config file (default: transports.ini)") help="Transports config file (default: transports.ini)")
tpush_parser.add_argument("-f", "--file", default="output/i2pseeds.su3", tpush_parser.add_argument("-f", "--file", default="output/i2pseeds.su3",
help=".su3 file (default: output/i2pseeds.su3)") help=".su3 file (default: output/i2pseeds.su3)")
tpush_parser.set_defaults(func=transport_push_action) tpush_parser.set_defaults(func=pyseeder.actions.transport_push)
serve_parser = subparsers.add_parser(
"serve",
description="""Run HTTPS reseeding server
(in production use nginx instead, please).
Will ask for a private key password""",
usage="""%(prog)s --port 8443 --host 127.0.0.1 \\
--private-key data/priv_key.pem \\
--cert data/user_at_mail.i2p.crt \\
--file output/i2pseeds.su3"""
)
serve_parser.add_argument("--host", default="0.0.0.0",
help="Host listening for clients (default: 0.0.0.0)")
serve_parser.add_argument("--port", default=8443,
help="Port listening for clients (default: 8443)")
serve_parser.add_argument("--private-key", default="data/priv_key.pem",
help="RSA private key (default: data/priv_key.pem)")
serve_parser.add_argument("--cert", required=True,
help="Certificate (example: data/user_at_mail.i2p.crt)")
serve_parser.add_argument("-f", "--file", default="output/i2pseeds.su3",
help=".su3 file (default: output/i2pseeds.su3)")
serve_parser.set_defaults(func=pyseeder.actions.serve)
args = parser.parse_args() args = parser.parse_args()
if hasattr(args, "func"): if hasattr(args, "func"):

52
pyseeder/actions.py

@ -0,0 +1,52 @@
"""Action functions for argparser"""
import pyseeder.transport
import pyseeder.actions
from pyseeder.utils import PyseederException, check_readable, check_writable
def keygen(args):
"""Sub-command to generate keys"""
for f in [args.cert, args.private_key]: check_writable(f)
from pyseeder.crypto import keygen
from getpass import getpass
priv_key_password = getpass("Set private key password: ").encode("utf-8")
keygen(args.cert, args.private_key, priv_key_password, args.signer_id)
def reseed(args):
"""Sub-command to generate reseed file"""
check_writable(args.outfile)
for f in [args.netdb, args.private_key]: check_readable(f)
from pyseeder.su3file import SU3File
priv_key_password = input().encode("utf-8")
su3file = SU3File(args.signer_id)
su3file.reseed(args.netdb)
su3file.write(args.outfile, args.private_key, priv_key_password)
def transport_pull(args):
"""Sub-command for downloading su3 file"""
import random
random.shuffle(args.urls)
for u in args.urls:
if pyseeder.transport.download(u, args.outfile):
return True
raise PyseederException("Failed to download su3 file")
def transport_push(args):
"""Sub-command for uploading su3 file with transports"""
check_readable(args.config)
import configparser
config = configparser.ConfigParser()
config.read(args.config)
pyseeder.transport.upload(args.file, config)
def serve(args):
"""Sub-command to start HTTPS reseed server"""
for f in [args.private_key, args.cert, args.file]: check_readable(f)
import pyseeder.server
pyseeder.server.run_server(args.host, args.port, args.private_key,
args.cert, args.file)

12
pyseeder/crypto.py

@ -1,4 +1,4 @@
import os, os.path import os
import random import random
import sys import sys
import datetime import datetime
@ -15,10 +15,6 @@ from cryptography.hazmat.primitives.asymmetric import padding
def keygen(pub_key, priv_key, priv_key_password, user_id): def keygen(pub_key, priv_key, priv_key_password, user_id):
"""Generate new private key and certificate RSA_SHA512_4096""" """Generate new private key and certificate RSA_SHA512_4096"""
for f in [pub_key, priv_key]:
if not os.access(os.path.dirname(f) or ".", os.W_OK):
raise PyseederException("Can't write {}, access forbidden").format(f)
# Generate our key # Generate our key
key = rsa.generate_private_key(public_exponent=65537, key_size=4096, key = rsa.generate_private_key(public_exponent=65537, key_size=4096,
backend=default_backend()) backend=default_backend())
@ -63,12 +59,6 @@ def keygen(pub_key, priv_key, priv_key_password, user_id):
def append_signature(target_file, priv_key, priv_key_password): def append_signature(target_file, priv_key, priv_key_password):
"""Append signature to the end of file""" """Append signature to the end of file"""
if not os.path.exists(priv_key):
raise PyseederException("Wrong private key path")
if not os.access(priv_key, os.R_OK):
raise PyseederException("Can't read private key, access forbidden")
with open(target_file, "rb") as f: with open(target_file, "rb") as f:
contents = f.read() contents = f.read()

34
pyseeder/server.py

@ -0,0 +1,34 @@
import http.server
import urllib.parse
import ssl
class ReseedHandler(http.server.SimpleHTTPRequestHandler):
"""Handles reseeding requests"""
i2pseeds_file = ""
server_version = "Pyseeder Server"
sys_version = ""
def do_GET(self):
path = urllib.parse.urlparse(self.path).path
if path == "/i2pseeds.su3":
self.send_response(200)
self.send_header("Content-Type", "application/octet-stream")
self.end_headers()
with open(self.i2pseeds_file, 'rb') as f:
self.wfile.write(f.read())
else:
self.send_error(404, "Not found")
def run_server(host, port, priv_key, cert, i2pseeds_file):
"""Start HTTPS server"""
Handler = ReseedHandler
Handler.i2pseeds_file = i2pseeds_file
httpd = http.server.HTTPServer((host, int(port)), Handler)
httpd.socket = ssl.wrap_socket(httpd.socket, server_side=True,
keyfile=priv_key, certfile=cert, ssl_version=ssl.PROTOCOL_TLSv1)
try:
httpd.serve_forever()
except KeyboardInterrupt:
exit()

19
pyseeder/su3file.py

@ -1,4 +1,4 @@
import os, os.path import os
import time, datetime import time, datetime
import random import random
import io import io
@ -27,9 +27,6 @@ class SU3File:
def write(self, filename, priv_key, priv_key_password): def write(self, filename, priv_key, priv_key_password):
"""Write file to disc""" """Write file to disc"""
if not os.access(os.path.dirname(filename) or ".", os.W_OK):
raise PyseederException("Can't write su3 file, access forbidden")
nullbyte = bytes([0]) nullbyte = bytes([0])
with open(filename, "wb") as f: with open(filename, "wb") as f:
f.write("I2Psu3".encode("utf-8")) f.write("I2Psu3".encode("utf-8"))
@ -58,12 +55,6 @@ class SU3File:
zip_file = io.BytesIO() zip_file = io.BytesIO()
dat_files = [] dat_files = []
if not os.path.exists(netdb):
raise PyseederException("Wrong netDb path")
if not os.access(netdb, os.R_OK):
raise PyseederException("Can't read netDb, access forbidden")
for root, dirs, files in os.walk(netdb): for root, dirs, files in os.walk(netdb):
for f in files: for f in files:
if f.endswith(".dat"): if f.endswith(".dat"):
@ -71,11 +62,13 @@ class SU3File:
# may be not older than 10h # may be not older than 10h
dat_files.append(os.path.join(root, f)) dat_files.append(os.path.join(root, f))
if len(dat_files) < 100: if len(dat_files) == 0:
raise PyseederException("Can't get enough netDb entries. Wrong netDb path?") raise PyseederException("Can't get enough netDb entries")
elif len(dat_files) > 75:
dat_files = random.sample(dat_files, 75)
with ZipFile(zip_file, "w", compression=ZIP_DEFLATED) as zf: with ZipFile(zip_file, "w", compression=ZIP_DEFLATED) as zf:
for f in random.sample(dat_files, 75): for f in dat_files:
zf.write(f, arcname=os.path.split(f)[1]) zf.write(f, arcname=os.path.split(f)[1])
self.FILE_TYPE = 0x00 self.FILE_TYPE = 0x00

2
pyseeder/transport.py

@ -1,7 +1,7 @@
"""Module for managing transport tasks""" """Module for managing transport tasks"""
import urllib.request import urllib.request
from urllib.error import URLError from urllib.error import URLError
import os, os.path import os
import importlib import importlib
from pyseeder.utils import PyseederException from pyseeder.utils import PyseederException

2
pyseeder/transports/git.py

@ -1,6 +1,6 @@
"""Git transport plugin""" """Git transport plugin"""
import subprocess import subprocess
import os, os.path import os
from shutil import copyfile from shutil import copyfile
from pyseeder.utils import TransportException from pyseeder.utils import TransportException

11
pyseeder/utils.py

@ -1,7 +1,18 @@
"""Various code""" """Various code"""
import os
class PyseederException(Exception): class PyseederException(Exception):
pass pass
class TransportException(PyseederException): class TransportException(PyseederException):
pass pass
def check_readable(f):
"""Checks if path exists and readable"""
if not os.path.exists(f) or not os.access(f, os.R_OK):
raise PyseederException("Error accessing path: {}".format(f))
def check_writable(f):
"""Checks if path is writable"""
if not os.access(os.path.dirname(f) or ".", os.W_OK):
raise PyseederException("Path is not writable: {}".format(f))

Loading…
Cancel
Save