From e51d47b5ed064ffbeef802e2bd1cfd4c4bf6897c Mon Sep 17 00:00:00 2001 From: Vadim Chernyshev Date: Fri, 16 Aug 2024 15:07:57 +0300 Subject: [PATCH 1/2] Add settings to control IP address bindings --- docs/server-config.md | 4 ++++ tabpy/tabpy_server/app/app.py | 26 ++++++++++++++---------- tabpy/tabpy_server/app/app_parameters.py | 4 ++++ tabpy/tabpy_server/common/default.conf | 1 + 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/docs/server-config.md b/docs/server-config.md index c57219e5..cbaacc9b 100755 --- a/docs/server-config.md +++ b/docs/server-config.md @@ -58,6 +58,7 @@ at [`logging.config` documentation page](https://docs.python.org/3.6/library/log `[TabPy]` parameters: - `TABPY_PORT` - port for TabPy to listen on. Default value - `9004`. +- `TABPY_BIND_IP` - ip address to bind to for TabPy to listen on. Default value - `0.0.0.0`. - `TABPY_QUERY_OBJECT_PATH` - query objects location. Used with models, see [TabPy Tools documentation](tabpy-tools.md) for details. Default value - `/tmp/query_objects`. @@ -103,6 +104,7 @@ at [`logging.config` documentation page](https://docs.python.org/3.6/library/log - `TABPY_ARROWFLIGHT_PORT` - port for [Arrow Flight](https://arrow.apache.org/docs/format/Flight.html) connection used in streaming mode. Default value is 13622. +- `TABPY_ARROWFLIGHT_BIND_IP` - ip address to bind to for Arrow Flight to listen on. Default value is `0.0.0.0`. ### Configuration File Example @@ -113,6 +115,7 @@ settings._ [TabPy] # TABPY_QUERY_OBJECT_PATH = /tmp/query_objects # TABPY_PORT = 9004 +# TABPY_BIND_IP = 0.0.0.0 # TABPY_STATE_PATH = /tabpy/tabpy_server # Where static pages live @@ -152,6 +155,7 @@ settings._ # Flight port defaults to 13622 if not set here. # TABPY_ARROW_ENABLE = True # TABPY_ARROWFLIGHT_PORT = 13622 +# TABPY_ARROWFLIGHT_BIND_IP = 0.0.0.0 [loggers] diff --git a/tabpy/tabpy_server/app/app.py b/tabpy/tabpy_server/app/app.py index 421464b0..118f39d2 100644 --- a/tabpy/tabpy_server/app/app.py +++ b/tabpy/tabpy_server/app/app.py @@ -96,8 +96,8 @@ def _initialize_ssl_context(self): if not hasattr(ssl.TLSVersion, min_tls): logger.warning(f"Unrecognized value for TABPY_MINIMUM_TLS_VERSION: {min_tls}") min_tls = "TLSv1_2" - - logger.info(f"Setting minimum TLS version to {min_tls}") + + logger.info(f"Setting minimum TLS version to {min_tls}") ssl_context.minimum_version = ssl.TLSVersion[min_tls] return ssl_context @@ -112,7 +112,7 @@ def _get_tls_certificates(self, config): tls_private_key = key_file.read() tls_certificates.append((tls_cert_chain, tls_private_key)) return tls_certificates - + def _get_arrow_server(self, config): verify_client = None tls_certificates = None @@ -121,7 +121,7 @@ def _get_arrow_server(self, config): scheme = "grpc+tls" tls_certificates = self._get_tls_certificates(config) - host = "0.0.0.0" + host = config.get(SettingsParameters.ArrowFlightBindIp) port = config.get(SettingsParameters.ArrowFlightPort) location = "{}://{}:{}".format(scheme, host, port) @@ -140,7 +140,7 @@ def _get_arrow_server(self, config): def run(self): application = self._create_tornado_web_app() - + init_model_evaluator(self.settings, self.tabpy_state, self.python_service) protocol = self.settings[SettingsParameters.TransferProtocol] @@ -158,14 +158,16 @@ def run(self): application.listen( self.settings[SettingsParameters.Port], + self.settings[SettingsParameters.BindIp], ssl_options=ssl_options, max_buffer_size=self.max_request_size, max_body_size=self.max_request_size, **settings, - ) + ) logger.info( - "Web service listening on port " + "Web service listening on " + f"{str(self.settings[SettingsParameters.BindIp])}:" f"{str(self.settings[SettingsParameters.Port])}" ) @@ -332,6 +334,7 @@ def _parse_config(self, config_file): settings_parameters = [ (SettingsParameters.Port, ConfigParameters.TABPY_PORT, 9004, None), + (SettingsParameters.BindIp, ConfigParameters.TABPY_BIND_IP, '0.0.0.0', None), (SettingsParameters.ServerVersion, None, __version__, None), (SettingsParameters.EvaluateEnabled, ConfigParameters.TABPY_EVALUATE_ENABLE, True, parser.getboolean), @@ -357,8 +360,9 @@ def _parse_config(self, config_file): 100, None), (SettingsParameters.GzipEnabled, ConfigParameters.TABPY_GZIP_ENABLE, True, parser.getboolean), - (SettingsParameters.ArrowEnabled, ConfigParameters.TABPY_ARROW_ENABLE, False, parser.getboolean), + (SettingsParameters.ArrowEnabled, ConfigParameters.TABPY_ARROW_ENABLE, False, parser.getboolean), (SettingsParameters.ArrowFlightPort, ConfigParameters.TABPY_ARROWFLIGHT_PORT, 13622, parser.getint), + (SettingsParameters.ArrowFlightBindIp, ConfigParameters.TABPY_ARROWFLIGHT_BIND_IP, '0.0.0.0', None), ] for setting, parameter, default_val, parse_function in settings_parameters: @@ -373,7 +377,7 @@ def _parse_config(self, config_file): ].lower() self._validate_transfer_protocol_settings() - + # Set max request size in bytes self.max_request_size = ( int(self.settings[SettingsParameters.MaxRequestSizeInMb]) * 1024 * 1024 @@ -493,12 +497,12 @@ def _handle_configuration_without_authentication(self): if self.disable_auth_warning == True: logger.info(std_no_auth_msg) - return + return confirm_no_auth_msg = "\nWARNING: This TabPy server is not currently configured for username/password authentication. " if self.settings[SettingsParameters.EvaluateEnabled]: - confirm_no_auth_msg += ("This means that, because the TABPY_EVALUATE_ENABLE feature is enabled, there is " + confirm_no_auth_msg += ("This means that, because the TABPY_EVALUATE_ENABLE feature is enabled, there is " "the potential that unauthenticated individuals may be able to remotely execute code on this machine. ") confirm_no_auth_msg += ("We strongly advise against proceeding without authentication as it poses a significant security risk.\n\n" diff --git a/tabpy/tabpy_server/app/app_parameters.py b/tabpy/tabpy_server/app/app_parameters.py index 3dab6c46..fe5f2657 100644 --- a/tabpy/tabpy_server/app/app_parameters.py +++ b/tabpy/tabpy_server/app/app_parameters.py @@ -5,6 +5,7 @@ class ConfigParameters: TABPY_PWD_FILE = "TABPY_PWD_FILE" TABPY_PORT = "TABPY_PORT" + TABPY_BIND_IP = "TABPY_BIND_IP" TABPY_QUERY_OBJECT_PATH = "TABPY_QUERY_OBJECT_PATH" TABPY_STATE_PATH = "TABPY_STATE_PATH" TABPY_TRANSFER_PROTOCOL = "TABPY_TRANSFER_PROTOCOL" @@ -21,6 +22,7 @@ class ConfigParameters: # Arrow specific settings TABPY_ARROW_ENABLE = "TABPY_ARROW_ENABLE" TABPY_ARROWFLIGHT_PORT = "TABPY_ARROWFLIGHT_PORT" + TABPY_ARROWFLIGHT_BIND_IP = "TABPY_ARROWFLIGHT_BIND_IP" class SettingsParameters: @@ -30,6 +32,7 @@ class SettingsParameters: TransferProtocol = "transfer_protocol" Port = "port" + BindIp = "bind_ip" ServerVersion = "server_version" UploadDir = "upload_dir" CertificateFile = "certificate_file" @@ -47,3 +50,4 @@ class SettingsParameters: # Arrow specific settings ArrowEnabled = "arrow_enabled" ArrowFlightPort = "arrowflight_port" + ArrowFlightBindIp = "arrowflight_bind_ip" diff --git a/tabpy/tabpy_server/common/default.conf b/tabpy/tabpy_server/common/default.conf index 17ee5e3b..c1b80430 100644 --- a/tabpy/tabpy_server/common/default.conf +++ b/tabpy/tabpy_server/common/default.conf @@ -1,6 +1,7 @@ [TabPy] # TABPY_QUERY_OBJECT_PATH = /tmp/query_objects # TABPY_PORT = 9004 +# TABPY_BIND_IP = 0.0.0.0 # TABPY_STATE_PATH = ./tabpy/tabpy_server # Where static pages live From 1e43ddcfe8b80100614e69eefa5f5b4fa4693d2e Mon Sep 17 00:00:00 2001 From: Vadim Chernyshev Date: Fri, 16 Aug 2024 15:35:53 +0300 Subject: [PATCH 2/2] hotfix/fix line length --- tabpy/tabpy_server/app/app.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tabpy/tabpy_server/app/app.py b/tabpy/tabpy_server/app/app.py index 118f39d2..57501584 100644 --- a/tabpy/tabpy_server/app/app.py +++ b/tabpy/tabpy_server/app/app.py @@ -360,9 +360,12 @@ def _parse_config(self, config_file): 100, None), (SettingsParameters.GzipEnabled, ConfigParameters.TABPY_GZIP_ENABLE, True, parser.getboolean), - (SettingsParameters.ArrowEnabled, ConfigParameters.TABPY_ARROW_ENABLE, False, parser.getboolean), - (SettingsParameters.ArrowFlightPort, ConfigParameters.TABPY_ARROWFLIGHT_PORT, 13622, parser.getint), - (SettingsParameters.ArrowFlightBindIp, ConfigParameters.TABPY_ARROWFLIGHT_BIND_IP, '0.0.0.0', None), + (SettingsParameters.ArrowEnabled, ConfigParameters.TABPY_ARROW_ENABLE, + False, parser.getboolean), + (SettingsParameters.ArrowFlightPort, ConfigParameters.TABPY_ARROWFLIGHT_PORT, + 13622, parser.getint), + (SettingsParameters.ArrowFlightBindIp, ConfigParameters.TABPY_ARROWFLIGHT_BIND_IP, + '0.0.0.0', None), ] for setting, parameter, default_val, parse_function in settings_parameters: @@ -502,8 +505,11 @@ def _handle_configuration_without_authentication(self): confirm_no_auth_msg = "\nWARNING: This TabPy server is not currently configured for username/password authentication. " if self.settings[SettingsParameters.EvaluateEnabled]: - confirm_no_auth_msg += ("This means that, because the TABPY_EVALUATE_ENABLE feature is enabled, there is " - "the potential that unauthenticated individuals may be able to remotely execute code on this machine. ") + confirm_no_auth_msg += ( + "This means that, because the TABPY_EVALUATE_ENABLE feature is enabled, there is " + "the potential that unauthenticated individuals may be able " + "to remotely execute code on this machine. " + ) confirm_no_auth_msg += ("We strongly advise against proceeding without authentication as it poses a significant security risk.\n\n" "Do you wish to proceed without authentication? (y/N): ")