mirror of
https://github.com/PurpleI2P/pyseeder
synced 2025-02-05 19:44:14 +00:00
Added reseed https server action + fixed some issues + refactored code
This commit is contained in:
parent
03af5634c1
commit
d7db5b1640
75
pyseeder.py
75
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
Normal file
52
pyseeder/actions.py
Normal file
@ -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)
|
@ -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
Normal file
34
pyseeder/server.py
Normal file
@ -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()
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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…
x
Reference in New Issue
Block a user