diff --git a/freqtrade/configuration.py b/freqtrade/configuration.py index 2a9e8fbd8..76726b293 100644 --- a/freqtrade/configuration.py +++ b/freqtrade/configuration.py @@ -6,7 +6,7 @@ import json import logging from argparse import Namespace from typing import Optional, Dict, Any -from jsonschema import Draft4Validator, validate +from jsonschema import Draft4Validator, validate, draft4_format_checker from jsonschema.exceptions import ValidationError, best_match import ccxt @@ -209,7 +209,7 @@ class Configuration(object): :return: Returns the config if valid, otherwise throw an exception """ try: - validate(conf, constants.CONF_SCHEMA) + validate(conf, constants.CONF_SCHEMA, format_checker=draft4_format_checker) return conf except ValidationError as exception: logger.critical( @@ -217,7 +217,9 @@ class Configuration(object): exception ) raise ValidationError( - best_match(Draft4Validator(constants.CONF_SCHEMA).iter_errors(conf)).message + best_match(Draft4Validator(constants.CONF_SCHEMA, + format_checker=draft4_format_checker) + .iter_errors(conf)).message ) def get_config(self) -> Dict[str, Any]: diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 5be01f977..8560bf8a3 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -85,6 +85,19 @@ CONF_SCHEMA = { }, 'required': ['enabled', 'token', 'chat_id'] }, + 'api_server': { + 'type': 'object', + 'properties': { + 'enabled': {'type': 'boolean'}, + 'listen_ip_address': { "format": "ipv4"}, + 'listen_port': { + 'type': 'integer', + "minimum": 1024, + "maximum": 65535 + }, + }, + 'required': ['enabled', 'listen_ip_address', 'listen_port'] + }, 'db_url': {'type': 'string'}, 'initial_state': {'type': 'string', 'enum': ['running', 'stopped']}, 'internals': { diff --git a/freqtrade/rpc/local_rest_server.py b/freqtrade/rpc/api_server.py similarity index 78% rename from freqtrade/rpc/local_rest_server.py rename to freqtrade/rpc/api_server.py index 92bb17be8..2a1ad0b8c 100644 --- a/freqtrade/rpc/local_rest_server.py +++ b/freqtrade/rpc/api_server.py @@ -6,26 +6,25 @@ from flask import Flask, request from flask_restful import Resource, Api from json import dumps from freqtrade.rpc.rpc import RPC, RPCException +from ipaddress import IPv4Address logger = logging.getLogger(__name__) +app = Flask(__name__) -class LocalRestSuperWrap(RPC): +class ApiServerSuperWrap(RPC): """ - This class is for REST cmd line client + This class is for REST calls across api server """ def __init__(self, freqtrade) -> None: """ - Init the LocalRestServer call, and init the super class RPC + Init the api server, and init the super class RPC :param freqtrade: Instance of a freqtrade bot :return: None """ super().__init__(freqtrade) - """ Constructor - :type interval: int - :param interval: Check interval, in seconds - """ - self.interval = int(1) + + self.interval = 1 thread = threading.Thread(target=self.run, args=(freqtrade,)) # extra comma as ref ! Tuple thread.daemon = True # Daemonize thread @@ -35,11 +34,10 @@ class LocalRestSuperWrap(RPC): def run(self, freqtrade): """ Method that runs forever """ self._config = freqtrade.config - app = Flask(__name__) """ - Define the application routes here - each Telegram command should have a like local substitute + Define the application routes here + each Telegram command should have a like local substitute """ @app.route("/") def hello(): @@ -97,17 +95,14 @@ class LocalRestSuperWrap(RPC): Section to handle configuration and running of the Rest serve also to check and warn if not bound to 127.0.0.1 as a security risk. """ + rest_ip = self._config['api_server']['listen_ip_address'] + rest_port = self._config['api_server']['listen_port'] - rest_ip = self._config['rest_cmd_line']['listen_ip_address'] - rest_port = self._config['rest_cmd_line']['listen_port'] - - if rest_ip != "127.0.0.1": - i=0 - while i < 10: - logger.info("SECURITY WARNING - Local Rest Server listening to external connections") - logger.info("SECURITY WARNING - This is insecure please set to 127.0.0.1 in config.json") - i += 1 + if not IPv4Address(rest_ip).is_loopback : + logger.info("SECURITY WARNING - Local Rest Server listening to external connections") + logger.info("SECURITY WARNING - This is insecure please set to your loopback, e.g 127.0.0.1 in config.json") # Run the Server logger.info('Starting Local Rest Server') app.run(host=rest_ip, port=rest_port) + diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py index d796b2454..9dddcdb80 100644 --- a/freqtrade/rpc/rpc_manager.py +++ b/freqtrade/rpc/rpc_manager.py @@ -23,11 +23,12 @@ class RPCManager(object): from freqtrade.rpc.telegram import Telegram self.registered_modules.append(Telegram(freqtrade)) - # Enable local rest server for cmd line control - if freqtrade.config['rest_cmd_line'].get('enabled', False): - logger.info('Enabling rpc.local_rest_server ...') - from freqtrade.rpc.local_rest_server import LocalRestSuperWrap - self.registered_modules.append(LocalRestSuperWrap(freqtrade)) + # Enable local rest api server for cmd line control + if freqtrade.config['api_server'].get('enabled', False): + logger.info('Enabling rpc.api_server') + from freqtrade.rpc.api_server import ApiServerSuperWrap + self.registered_modules.append(ApiServerSuperWrap(freqtrade)) + def cleanup(self) -> None: """ Stops all enabled rpc modules """