Ejemplo n.º 1
0
def initialize_services(controller, services_config):
    """
    Load keys for services listed in the config
    """

    # Load the keys and config for each onion service
    for service in services_config:
        try:
            service_key = util.key_decrypt_prompt(service.get("key"))
        except OSError as e:
            if e.errno == errno.ENOENT:
                logger.error("Private key file %s could not be found. "
                             "Relative paths in the config file are loaded "
                             "relative to the config file directory.",
                             service.get("key"))
                sys.exit(1)
            else:
                raise
        # Key file was read but a valid private key was not found.
        if not service_key:
            logger.error("Private key %s could not be loaded. It is a not "
                         "valid 1024 bit PEM encoded RSA private key",
                         service.get("key"))
            sys.exit(1)
        else:
            # Successfully imported the private key
            onion_address = util.calc_onion_address(service_key)
            logger.debug("Loaded private key for service %s.onion.",
                         onion_address)

        # Load all instances for the current onion service
        instance_config = service.get("instances", [])
        if not instance_config:
            logger.error("Could not load any instances for service "
                         "%s.onion.", onion_address)
            sys.exit(1)
        else:
            instances = []
            for instance in instance_config:
                instances.append(onionbalance.instance.Instance(
                    controller=controller,
                    onion_address=instance.get("address"),
                    authentication_cookie=instance.get("auth")
                ))

            logger.info("Loaded %d instances for service %s.onion.",
                        len(instances), onion_address)

        # Store service configuration in config.services global
        config.services.append(onionbalance.service.Service(
            controller=controller,
            service_key=service_key,
            instances=instances
        ))

        # Store a global reference to current controller connection
        config.controller = controller
Ejemplo n.º 2
0
def initialize_services(controller, services_config):
    """
    Load keys for services listed in the config
    """

    # Load the keys and config for each onion service
    for service in services_config:
        try:
            service_key = util.key_decrypt_prompt(service.get("key"))
        except OSError as e:
            if e.errno == errno.ENOENT:
                logger.error(
                    "Private key file %s could not be found. "
                    "Relative paths in the config file are loaded "
                    "relative to the config file directory.",
                    service.get("key"))
                sys.exit(1)
            else:
                raise
        # Key file was read but a valid private key was not found.
        if not service_key:
            logger.error(
                "Private key %s could not be loaded. It is a not "
                "valid 1024 bit PEM encoded RSA private key",
                service.get("key"))
            sys.exit(1)
        else:
            # Successfully imported the private key
            onion_address = util.calc_onion_address(service_key)
            logger.debug("Loaded private key for service %s.onion.",
                         onion_address)

        # Load all instances for the current onion service
        instance_config = service.get("instances", [])
        if not instance_config:
            logger.error(
                "Could not load and instances for service "
                "%s.onion.", onion_address)
            sys.exit(1)
        else:
            instances = []
            for instance in instance_config:
                instances.append(
                    onionbalance.instance.Instance(
                        controller=controller,
                        onion_address=instance.get("address"),
                        authentication_cookie=instance.get("auth")))

            logger.info("Loaded %d instances for service %s.onion.",
                        len(instances), onion_address)

        # Store service configuration in config.services global
        config.services.append(
            onionbalance.service.Service(controller=controller,
                                         service_key=service_key,
                                         instances=instances))
Ejemplo n.º 3
0
def generate_config():
    """
    Entry point for interactive config file generation.
    """

    # Parse initial command line options
    args = parse_cmd_args().parse_args()

    # Simplify the logging output for the command line tool
    logger = log.get_config_generator_logger()

    logger.info("Beginning OnionBalance config generation.")

    # If CLI options have been provided, don't enter interactive mode
    # Crude check to see if any options beside --verbosity are set.
    verbose = True if '-v' in sys.argv else False

    if ((len(sys.argv) > 1 and not verbose) or len(sys.argv) > 3 or
            args.no_interactive):
        interactive = False
        logger.info("Entering non-interactive mode.")
    else:
        interactive = True
        logger.info("No command line arguments found, entering interactive "
                    "mode.")

    logger.setLevel(logging.__dict__[args.verbosity.upper()])

    # Check if output directory exists, if not try create it
    output_path = None
    if interactive:
        output_path = input("Enter path to store generated config "
                            "[{}]: ".format(os.path.abspath(args.output)))
    output_path = output_path or args.output
    try:
        util.try_make_dir(output_path)
    except OSError:
        logger.exception("Problem encountered when trying to create the "
                         "output directory %s.", os.path.abspath(output_path))
    else:
        logger.debug("Created the output directory '%s'.",
                     os.path.abspath(output_path))

    # The output directory should be empty to avoid having conflict keys
    # or config files.
    if not util.is_directory_empty(output_path):
        logger.error("The specified output directory is not empty. Please "
                     "delete any files and folders or specify another output "
                     "directory.")
        sys.exit(1)

    # Load master key if specified
    key_path = None
    if interactive:
        # Read key path from user
        key_path = input("Enter path to master service private key "
                         "(Leave empty to generate a key): ")
    key_path = args.key or key_path
    if key_path:
        if not os.path.isfile(key_path):
            logger.error("The specified master service private key '%s' "
                         "could not be found. Please confirm the path and "
                         "file permissions are correct.", key_path)
            sys.exit(1)
        else:
            # Try load the specified private key file
            master_key = util.key_decrypt_prompt(key_path)
            if not master_key:
                logger.error("The specified master private key %s could not "
                             "be loaded.", os.path.abspath(master_key))
                sys.exit(1)
            else:
                master_onion_address = util.calc_onion_address(master_key)
                logger.info("Successfully loaded a master key for service "
                            "%s.onion.", master_onion_address)

    else:
        # No key specified, begin generating a new one.
        master_key = Crypto.PublicKey.RSA.generate(1024)
        master_onion_address = util.calc_onion_address(master_key)
        logger.debug("Created a new master key for service %s.onion.",
                     master_onion_address)

    # Finished loading/generating master key, now try generate keys for
    # each service instance
    num_instances = None
    if interactive:
        num_instances = input("Number of instance services to create "
                              "[{}]: ".format(args.num_instances))
        # Cast to int if a number was specified
        try:
            num_instances = int(num_instances)
        except ValueError:
            num_instances = None
    num_instances = num_instances or args.num_instances
    logger.debug("Creating %d service instances.", num_instances)

    tag = None
    if interactive:
        tag = input("Provide a tag name to group these instances "
                    "[{}]: ".format(args.tag))
    tag = tag or args.tag

    # Create HiddenServicePort line for instance torrc file
    service_virtual_port = None
    if interactive:
        service_virtual_port = input("Specify the service virtual port (for "
                                     "client connections) [{}]: ".format(
                                         args.service_virtual_port))
    service_virtual_port = service_virtual_port or args.service_virtual_port

    service_target = None
    if interactive:
        # In interactive mode, change default target to match the specified
        # virtual port
        default_service_target = u'127.0.0.1:{}'.format(service_virtual_port)
        service_target = input("Specify the service target IP and port (where "
                               "your service is listening) [{}]: ".format(
                                   default_service_target))
        service_target = service_target or default_service_target
    service_target = service_target or args.service_target
    torrc_port_line = u'HiddenServicePort {} {}'.format(service_virtual_port,
                                                        service_target)

    instances = []
    for i in range(0, num_instances):
        instance_key = Crypto.PublicKey.RSA.generate(1024)
        instance_address = util.calc_onion_address(instance_key)
        logger.debug("Created a key for instance %s.onion.",
                     instance_address)
        instances.append((instance_address, instance_key))

    # Write master service key to directory
    master_passphrase = None
    if interactive:
        master_passphrase = getpass.getpass(
            "Provide an optional password to encrypt the master private "
            "key (Not encrypted if no password is specified): ")
    master_passphrase = master_passphrase or args.password

    # Finished reading input, starting to write config files.
    master_dir = os.path.join(output_path, 'master')
    util.try_make_dir(master_dir)
    master_key_file = os.path.join(master_dir,
                                   '{}.key'.format(master_onion_address))
    with open(master_key_file, "wb") as key_file:
        os.chmod(master_key_file, 384)  # chmod 0600 in decimal
        key_file.write(master_key.exportKey(passphrase=master_passphrase))
        logger.debug("Successfully wrote master key to file %s.",
                     os.path.abspath(master_key_file))

    # Create YAML OnionBalance settings file for these instances
    service_data = {'key': '{}.key'.format(master_onion_address)}
    service_data['instances'] = [{'address': address,
                                  'name': '{}{}'.format(tag, i+1)} for
                                 i, (address, _) in enumerate(instances)]
    settings_data = {'services': [service_data]}
    config_yaml = yaml.dump(settings_data, default_flow_style=False)

    config_file_path = os.path.join(master_dir, 'config.yaml')
    with open(config_file_path, "w") as config_file:
        config_file.write(u"# OnionBalance Config File\n")
        config_file.write(config_yaml)
        logger.info("Wrote master service config file '%s'.",
                    os.path.abspath(config_file_path))

    # Write master service torrc
    master_torrc_path = os.path.join(master_dir, 'torrc-server')
    master_torrc_template = pkg_resources.resource_string(__name__,
                                                          'data/torrc-server')
    with open(master_torrc_path, "w") as master_torrc_file:
        master_torrc_file.write(master_torrc_template.decode('utf-8'))

    # Try generate config files for each service instance
    for i, (instance_address, instance_key) in enumerate(instances):
        # Create a numbered directory for instance
        instance_dir = os.path.join(output_path, '{}{}'.format(tag, i+1))
        instance_key_dir = os.path.join(instance_dir, instance_address)
        util.try_make_dir(instance_key_dir)
        os.chmod(instance_key_dir, 1472)  # chmod 2700 in decimal

        instance_key_file = os.path.join(instance_key_dir, 'private_key')
        with open(instance_key_file, "wb") as key_file:
            os.chmod(instance_key_file, 384)  # chmod 0600 in decimal
            key_file.write(instance_key.exportKey())
            logger.debug("Successfully wrote key for instance %s.onion to "
                         "file.", instance_address)

        # Write torrc file for each instance
        instance_torrc = os.path.join(instance_dir, 'instance_torrc')
        instance_torrc_template = pkg_resources.resource_string(
            __name__, 'data/torrc-instance')
        with open(instance_torrc, "w") as torrc_file:
            torrc_file.write(instance_torrc_template.decode('utf-8'))
            # The ./ relative path prevents Tor from raising relative
            # path warnings. The relative path may need to be edited manual
            # to work on Windows systems.
            torrc_file.write(u"HiddenServiceDir {}\n".format(
                instance_address))
            torrc_file.write(u"{}\n".format(torrc_port_line))

    # Output final status message
    logger.info("Done! Successfully generated an OnionBalance config and %d "
                "instance keys for service %s.onion.",
                num_instances, master_onion_address)

    sys.exit(0)
Ejemplo n.º 4
0
def generate_config():
    """
    Entry point for interactive config file generation.
    """

    # Parse initial command line options
    args = parse_cmd_args().parse_args()

    # Simplify the logging output for the command line tool
    logger = log.get_config_generator_logger()

    logger.info("Beginning OnionBalance config generation.")

    # If CLI options have been provided, don't enter interactive mode
    # Crude check to see if any options beside --verbosity are set.
    verbose = True if '-v' in sys.argv else False

    if ((len(sys.argv) > 1 and not verbose) or len(sys.argv) > 3
            or args.no_interactive):
        interactive = False
        logger.info("Entering non-interactive mode.")
    else:
        interactive = True
        logger.info("No command line arguments found, entering interactive "
                    "mode.")

    logger.setLevel(logging.__dict__[args.verbosity.upper()])

    # Check if output directory exists, if not try create it
    output_path = None
    if interactive:
        output_path = input("Enter path to store generated config "
                            "[{}]: ".format(os.path.abspath(args.output)))
    output_path = output_path or args.output
    try:
        util.try_make_dir(output_path)
    except OSError:
        logger.exception(
            "Problem encountered when trying to create the "
            "output directory %s.", os.path.abspath(output_path))
    else:
        logger.debug("Created the output directory '%s'.",
                     os.path.abspath(output_path))

    # The output directory should be empty to avoid having conflict keys
    # or config files.
    if not util.is_directory_empty(output_path):
        logger.error("The specified output directory is not empty. Please "
                     "delete any files and folders or specify another output "
                     "directory.")
        sys.exit(1)

    # Load master key if specified
    key_path = None
    if interactive:
        # Read key path from user
        key_path = input("Enter path to master service private key "
                         "(Leave empty to generate a key): ")
    key_path = args.key or key_path
    if key_path:
        if not os.path.isfile(key_path):
            logger.error(
                "The specified master service private key '%s' "
                "could not be found. Please confirm the path and "
                "file permissions are correct.", key_path)
            sys.exit(1)
        else:
            # Try load the specified private key file
            master_key = util.key_decrypt_prompt(key_path)
            if not master_key:
                logger.error(
                    "The specified master private key %s could not "
                    "be loaded.", os.path.abspath(master_key))
                sys.exit(1)
            else:
                master_onion_address = util.calc_onion_address(master_key)
                logger.info(
                    "Successfully loaded a master key for service "
                    "%s.onion.", master_onion_address)

    else:
        # No key specified, begin generating a new one.
        master_key = Crypto.PublicKey.RSA.generate(1024)
        master_onion_address = util.calc_onion_address(master_key)
        logger.debug("Created a new master key for service %s.onion.",
                     master_onion_address)

    # Finished loading/generating master key, now try generate keys for
    # each service instance
    num_instances = None
    if interactive:
        num_instances = input("Number of instance services to create "
                              "[{}]: ".format(args.num_instances))
        # Cast to int if a number was specified
        try:
            num_instances = int(num_instances)
        except ValueError:
            num_instances = None
    num_instances = num_instances or args.num_instances
    logger.debug("Creating %d service instances.", num_instances)

    tag = None
    if interactive:
        tag = input("Provide a tag name to group these instances "
                    "[{}]: ".format(args.tag))
    tag = tag or args.tag

    # Create HiddenServicePort line for instance torrc file
    service_virtual_port = None
    if interactive:
        service_virtual_port = input("Specify the service virtual port (for "
                                     "client connections) [{}]: ".format(
                                         args.service_virtual_port))
    service_virtual_port = service_virtual_port or args.service_virtual_port

    service_target = None
    if interactive:
        # In interactive mode, change default target to match the specified
        # virtual port
        default_service_target = u'127.0.0.1:{}'.format(service_virtual_port)
        service_target = input(
            "Specify the service target IP and port (where "
            "your service is listening) [{}]: ".format(default_service_target))
        service_target = service_target or default_service_target
    service_target = service_target or args.service_target
    torrc_port_line = u'HiddenServicePort {} {}'.format(
        service_virtual_port, service_target)

    instances = []
    for i in range(0, num_instances):
        instance_key = Crypto.PublicKey.RSA.generate(1024)
        instance_address = util.calc_onion_address(instance_key)
        logger.debug("Created a key for instance %s.onion.", instance_address)
        instances.append((instance_address, instance_key))

    # Write master service key to directory
    master_passphrase = None
    if interactive:
        master_passphrase = getpass.getpass(
            "Provide an optional password to encrypt the master private "
            "key (Not encrypted if no password is specified): ")
    master_passphrase = master_passphrase or args.password

    # Finished reading input, starting to write config files.
    master_dir = os.path.join(output_path, 'master')
    util.try_make_dir(master_dir)
    master_key_file = os.path.join(master_dir,
                                   '{}.key'.format(master_onion_address))
    with open(master_key_file, "wb") as key_file:
        os.chmod(master_key_file, 384)  # chmod 0600 in decimal
        key_file.write(master_key.exportKey(passphrase=master_passphrase))
        logger.debug("Successfully wrote master key to file %s.",
                     os.path.abspath(master_key_file))

    # Create YAML OnionBalance settings file for these instances
    service_data = {'key': '{}.key'.format(master_onion_address)}
    service_data['instances'] = [{
        'address': address,
        'name': '{}{}'.format(tag, i + 1)
    } for i, (address, _) in enumerate(instances)]
    settings_data = {'services': [service_data]}
    config_yaml = yaml.safe_dump(settings_data, default_flow_style=False)

    config_file_path = os.path.join(master_dir, 'config.yaml')
    with open(config_file_path, "w") as config_file:
        config_file.write(u"# OnionBalance Config File\n")
        config_file.write(config_yaml)
        logger.info("Wrote master service config file '%s'.",
                    os.path.abspath(config_file_path))

    # Write master service torrc
    master_torrc_path = os.path.join(master_dir, 'torrc-server')
    master_torrc_template = pkg_resources.resource_string(
        __name__, 'data/torrc-server')
    with open(master_torrc_path, "w") as master_torrc_file:
        master_torrc_file.write(master_torrc_template.decode('utf-8'))

    # Try generate config files for each service instance
    for i, (instance_address, instance_key) in enumerate(instances):
        # Create a numbered directory for instance
        instance_dir = os.path.join(output_path, '{}{}'.format(tag, i + 1))
        instance_key_dir = os.path.join(instance_dir, instance_address)
        util.try_make_dir(instance_key_dir)
        os.chmod(instance_key_dir, 1472)  # chmod 2700 in decimal

        instance_key_file = os.path.join(instance_key_dir, 'private_key')
        with open(instance_key_file, "wb") as key_file:
            os.chmod(instance_key_file, 384)  # chmod 0600 in decimal
            key_file.write(instance_key.exportKey())
            logger.debug(
                "Successfully wrote key for instance %s.onion to "
                "file.", instance_address)

        # Write torrc file for each instance
        instance_torrc = os.path.join(instance_dir, 'instance_torrc')
        instance_torrc_template = pkg_resources.resource_string(
            __name__, 'data/torrc-instance')
        with open(instance_torrc, "w") as torrc_file:
            torrc_file.write(instance_torrc_template.decode('utf-8'))
            # The ./ relative path prevents Tor from raising relative
            # path warnings. The relative path may need to be edited manual
            # to work on Windows systems.
            torrc_file.write(u"HiddenServiceDir {}\n".format(instance_address))
            torrc_file.write(u"{}\n".format(torrc_port_line))

    # Output final status message
    logger.info(
        "Done! Successfully generated an OnionBalance config and %d "
        "instance keys for service %s.onion.", num_instances,
        master_onion_address)

    sys.exit(0)