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. 75
      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

75
pyseeder.py

@ -1,48 +1,12 @@ @@ -1,48 +1,12 @@
#! /usr/bin/env python3
import os, os.path
import os
import sys
from getpass import getpass
import argparse
from pyseeder.crypto import keygen
from pyseeder.su3file import SU3File
import pyseeder.transport
import pyseeder.actions
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():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(title="actions",
@ -60,8 +24,8 @@ def main(): @@ -60,8 +24,8 @@ def main():
kg_parser.add_argument("--private-key", default="data/priv_key.pem",
help="RSA private key (default: data/priv_key.pem)")
kg_parser.add_argument("--cert", required=True,
help="Certificate (example: output/user_at_mail.i2p.crt)")
kg_parser.set_defaults(func=keygen_action)
help="Certificate (example: data/user_at_mail.i2p.crt)")
kg_parser.set_defaults(func=pyseeder.actions.keygen)
rs_parser = subparsers.add_parser(
@ -80,7 +44,7 @@ echo $YOUR_PASSWORD | %(prog)s --netdb /path/to/netDb \\ @@ -80,7 +44,7 @@ echo $YOUR_PASSWORD | %(prog)s --netdb /path/to/netDb \\
help="Output file (default: output/i2pseeds.su3)")
rs_parser.add_argument("--netdb", required=True,
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(
@ -96,7 +60,7 @@ echo $YOUR_PASSWORD | %(prog)s --netdb /path/to/netDb \\ @@ -96,7 +60,7 @@ echo $YOUR_PASSWORD | %(prog)s --netdb /path/to/netDb \\
mainline I2P (like https://reseed.i2p-projekt.de/)""")
tpull_parser.add_argument("-o", "--outfile", 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(
@ -108,7 +72,30 @@ echo $YOUR_PASSWORD | %(prog)s --netdb /path/to/netDb \\ @@ -108,7 +72,30 @@ echo $YOUR_PASSWORD | %(prog)s --netdb /path/to/netDb \\
help="Transports config file (default: transports.ini)")
tpush_parser.add_argument("-f", "--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()
if hasattr(args, "func"):

52
pyseeder/actions.py

@ -0,0 +1,52 @@ @@ -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 @@ @@ -1,4 +1,4 @@
import os, os.path
import os
import random
import sys
import datetime
@ -15,10 +15,6 @@ from cryptography.hazmat.primitives.asymmetric import padding @@ -15,10 +15,6 @@ from cryptography.hazmat.primitives.asymmetric import padding
def keygen(pub_key, priv_key, priv_key_password, user_id):
"""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
key = rsa.generate_private_key(public_exponent=65537, key_size=4096,
backend=default_backend())
@ -63,12 +59,6 @@ def keygen(pub_key, priv_key, priv_key_password, user_id): @@ -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):
"""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:
contents = f.read()

34
pyseeder/server.py

@ -0,0 +1,34 @@ @@ -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 @@ @@ -1,4 +1,4 @@
import os, os.path
import os
import time, datetime
import random
import io
@ -27,9 +27,6 @@ class SU3File: @@ -27,9 +27,6 @@ class SU3File:
def write(self, filename, priv_key, priv_key_password):
"""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])
with open(filename, "wb") as f:
f.write("I2Psu3".encode("utf-8"))
@ -58,12 +55,6 @@ class SU3File: @@ -58,12 +55,6 @@ class SU3File:
zip_file = io.BytesIO()
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 f in files:
if f.endswith(".dat"):
@ -71,11 +62,13 @@ class SU3File: @@ -71,11 +62,13 @@ class SU3File:
# may be not older than 10h
dat_files.append(os.path.join(root, f))
if len(dat_files) < 100:
raise PyseederException("Can't get enough netDb entries. Wrong netDb path?")
if len(dat_files) == 0:
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:
for f in random.sample(dat_files, 75):
for f in dat_files:
zf.write(f, arcname=os.path.split(f)[1])
self.FILE_TYPE = 0x00

2
pyseeder/transport.py

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

2
pyseeder/transports/git.py

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

11
pyseeder/utils.py

@ -1,7 +1,18 @@ @@ -1,7 +1,18 @@
"""Various code"""
import os
class PyseederException(Exception):
pass
class TransportException(PyseederException):
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