From d5ea5a3f14681c5685ed7deb5cbf4008049073dd Mon Sep 17 00:00:00 2001 From: William Chargin Date: Wed, 21 Aug 2019 18:29:37 -0700 Subject: [PATCH 1/4] core: bind to localhost rather than all interfaces Summary: Prior to this change, TensorBoard would default to serving on the entire local network; now, TensorBoard serves to the local machine only, and the flag `--host='*'` can be used to dual-bind to IPv4 and IPv6 on the entire local network (the previous default). See #2387 and comments therein for details. Test Plan: On my Debian machine, running with `strace -e trace=%network`, and testing connection with `curl -4 localhost:6006/data/logdir` (or `-6`): - running with no `--host` flag, or `--host=localhost`: - can connect to loopback on IPv4 only - cannot connect over LAN - `strace` shows binding on `AF_INET` - running with `--host='::1'`: - can connect to loopback on IPv6 only - cannot connect over LAN - `strace` shows binding on `AF_INET6` - running with `--host=0.0.0.0`: - can connect to loopback on IPv4 only - **can** connect over LAN - `strace` shows binding on `AF_INET` - running with `--host='*'`: - can connect on both IPv4 and IPv6 - **can** connect over LAN - `strace` shows binding on `AF_INET6` with an additional syscall to `setsockopt(3, SOL_IPV6, IPV6_V6ONLY, [0], 4)` to facilitate the dual-binding, which is not present in any other tested case wchargin-branch: localhost-only --- tensorboard/plugins/core/core_plugin.py | 8 +++++--- tensorboard/program.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tensorboard/plugins/core/core_plugin.py b/tensorboard/plugins/core/core_plugin.py index 8a9e148bfc..68a25e063b 100644 --- a/tensorboard/plugins/core/core_plugin.py +++ b/tensorboard/plugins/core/core_plugin.py @@ -304,10 +304,12 @@ def define_flags(self, parser): '--host', metavar='ADDR', type=str, - default='', + default='localhost', help='''\ -What host to listen to. Defaults to serving on all interfaces. Other -commonly used values are 127.0.0.1 (localhost) and :: (for IPv6).\ +What host to listen to. Defaults to %(default)r, which will only be +accessible by the local machine. Pass the special value '*' to dual-bind +to both '::' and '0.0.0.0', which will serve to the entire local network +on both IPv4 and IPv6. ''') parser.add_argument( diff --git a/tensorboard/program.py b/tensorboard/program.py index a0fa7147a6..a55a621c3a 100644 --- a/tensorboard/program.py +++ b/tensorboard/program.py @@ -425,7 +425,7 @@ def __init__(self, wsgi_app, flags): # Without an explicit host, we default to serving on all interfaces, # and will attempt to serve both IPv4 and IPv6 traffic through one # socket. - self._auto_wildcard = not host + self._auto_wildcard = host == '*' if self._auto_wildcard: host = self._get_wildcard_address(port) From cd8adc8f9f49152e6d01eebfe24bc6df86d7b89b Mon Sep 17 00:00:00 2001 From: William Chargin Date: Wed, 18 Sep 2019 12:43:05 -0700 Subject: [PATCH 2/4] [update patch] wchargin-branch: localhost-only wchargin-source: c05af50a7db71f2ab0b762f05f02ddf24ab76103 --- tensorboard/plugins/core/core_plugin.py | 21 +++++++++--- tensorboard/plugins/core/core_plugin_test.py | 4 +++ tensorboard/program.py | 34 ++++++++++++++++---- tensorboard/program_test.py | 1 + 4 files changed, 48 insertions(+), 12 deletions(-) diff --git a/tensorboard/plugins/core/core_plugin.py b/tensorboard/plugins/core/core_plugin.py index 1d0a3b5ac1..e1fa23a24e 100644 --- a/tensorboard/plugins/core/core_plugin.py +++ b/tensorboard/plugins/core/core_plugin.py @@ -322,14 +322,23 @@ def define_flags(self, parser): '--host', metavar='ADDR', type=str, - default='localhost', + default=None, # like localhost, but prints a note about `--bind_all` help='''\ -What host to listen to. Defaults to %(default)r, which will only be -accessible by the local machine. Pass the special value '*' to dual-bind -to both '::' and '0.0.0.0', which will serve to the entire local network -on both IPv4 and IPv6. +What host to listen to (default: localhost). To serve to the entire local +network on both IPv4 and IPv6, see `--bind_all`, with which this option is +mutually exclusive. ''') + parser.add_argument( + '--bind_all', + action='store_true', + help='''\ +Serve on all public interfaces. This will expose your TensorBoard instance to +the network on both IPv4 and IPv6 (where available). Mutually exclusive with +`--host`. +''') + + parser.add_argument( '--port', metavar='PORT', @@ -555,6 +564,8 @@ def fix_flags(self, flags): 'For example `tensorboard --logdir mylogdir` ' 'or `tensorboard --db sqlite:~/.tensorboard.db`. ' 'Run `tensorboard --helpfull` for details and examples.') + elif flags.host is not None and flags.bind_all: + raise FlagsError('Must not specify both --host and --bind_all.') if flags.path_prefix.endswith('/'): flags.path_prefix = flags.path_prefix[:-1] diff --git a/tensorboard/plugins/core/core_plugin_test.py b/tensorboard/plugins/core/core_plugin_test.py index 562030ac35..22e3cab0f7 100644 --- a/tensorboard/plugins/core/core_plugin_test.py +++ b/tensorboard/plugins/core/core_plugin_test.py @@ -46,6 +46,8 @@ class FakeFlags(object): def __init__( self, + bind_all=False, + host=None, inspect=False, version_tb=False, logdir='', @@ -53,6 +55,8 @@ def __init__( event_file='', db='', path_prefix=''): + self.bind_all = bind_all + self.host = host self.inspect = inspect self.version_tb = version_tb self.logdir = logdir diff --git a/tensorboard/program.py b/tensorboard/program.py index 4bc0bcec63..a9f4c7b32b 100644 --- a/tensorboard/program.py +++ b/tensorboard/program.py @@ -218,8 +218,7 @@ def main(self, ignored_argv=('',)): return 0 try: server = self._make_server() - sys.stderr.write('TensorBoard %s at %s (Press CTRL+C to quit)\n' % - (version.VERSION, server.get_url())) + server.print_serving_message() sys.stderr.flush() self._register_info(server) server.serve_forever() @@ -326,6 +325,16 @@ def get_url(self): """Returns a URL at which this server should be reachable.""" raise NotImplementedError() + def print_serving_message(self): + """Prints a user-friendly message prior to server start. + + This will be called just before `serve_forever`. + """ + sys.stderr.write( + 'TensorBoard %s at %s (Press CTRL+C to quit)\n' + % (version.VERSION, self.get_url()) + ) + class TensorBoardServerException(Exception): """Exception raised by TensorBoardServer for user-friendly errors. @@ -413,12 +422,15 @@ def __init__(self, wsgi_app, flags): host = flags.host port = flags.port - # Without an explicit host, we default to serving on all interfaces, - # and will attempt to serve both IPv4 and IPv6 traffic through one - # socket. - self._auto_wildcard = host == '*' + self._auto_wildcard = flags.bind_all if self._auto_wildcard: + # Serve on all interfaces, and attempt to serve both IPv4 and IPv6 + # traffic through one socket. host = self._get_wildcard_address(port) + elif host is None: + host = 'localhost' + + self._host = host self._fix_werkzeug_logging() try: @@ -516,12 +528,20 @@ def get_url(self): if self._auto_wildcard: display_host = socket.gethostname() else: - host = self._flags.host + host = self.host display_host = ( '[%s]' % host if ':' in host and not host.startswith('[') else host) return 'http://%s:%d%s/' % (display_host, self.server_port, self._flags.path_prefix.rstrip('/')) + def print_serving_message(self): + if self._flags.host is None and not self._flags.bind_all: + sys.stderr.write( + 'Serving TensorBoard on localhost; to expose to the network, ' + 'use a proxy or pass --bind_all\n' + ) + super(WerkzeugServer, self).print_serving_message() + def _fix_werkzeug_logging(self): """Fix werkzeug logging setup so it inherits TensorBoard's log level. diff --git a/tensorboard/program_test.py b/tensorboard/program_test.py index e63d8dbd3e..919fa3d493 100644 --- a/tensorboard/program_test.py +++ b/tensorboard/program_test.py @@ -73,6 +73,7 @@ class _StubApplication(object): def make_flags(self, **kwargs): flags = argparse.Namespace() + kwargs.setdefault('bind_all', False) for k, v in six.iteritems(kwargs): setattr(flags, k, v) return flags From bcd154d618bd0f9051951b17d67ce0811b26b563 Mon Sep 17 00:00:00 2001 From: William Chargin Date: Wed, 18 Sep 2019 12:46:55 -0700 Subject: [PATCH 3/4] [update patch] wchargin-branch: localhost-only wchargin-source: f3749cf614b1530c0031a2380c63dc3a629206ba --- tensorboard/program.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tensorboard/program.py b/tensorboard/program.py index a9f4c7b32b..db12fa96d4 100644 --- a/tensorboard/program.py +++ b/tensorboard/program.py @@ -219,7 +219,6 @@ def main(self, ignored_argv=('',)): try: server = self._make_server() server.print_serving_message() - sys.stderr.flush() self._register_info(server) server.serve_forever() return 0 @@ -334,6 +333,7 @@ def print_serving_message(self): 'TensorBoard %s at %s (Press CTRL+C to quit)\n' % (version.VERSION, self.get_url()) ) + sys.stderr.flush() class TensorBoardServerException(Exception): @@ -540,6 +540,7 @@ def print_serving_message(self): 'Serving TensorBoard on localhost; to expose to the network, ' 'use a proxy or pass --bind_all\n' ) + sys.stderr.flush() super(WerkzeugServer, self).print_serving_message() def _fix_werkzeug_logging(self): From 958ab634a01861823730d4fba6084c9fa3dbee42 Mon Sep 17 00:00:00 2001 From: William Chargin Date: Wed, 18 Sep 2019 14:20:07 -0700 Subject: [PATCH 4/4] [update patch] wchargin-branch: localhost-only wchargin-source: 3fbdc41b7ce84cf605953a9cc3f5252380d88d13 --- README.md | 10 +++++++--- tensorboard/program.py | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5640d2f429..74cdd203a6 100644 --- a/README.md +++ b/README.md @@ -367,9 +367,13 @@ for some more information. ### I get a network security popup every time I run TensorBoard on a mac! -This is because by default, TensorBoard serves on host `0.0.0.0` which is -publicly accessible. You can stop the popups by specifying `--host localhost` at -startup. +Versions of TensorBoard prior to TensorBoard 2.0 would by default serve on host +`0.0.0.0`, which is publicly accessible. For those versions of TensorBoard, you +can stop the popups by specifying `--host localhost` at startup. + +In TensorBoard 2.0 and up, `--host localhost` is the default. Use `--bind_all` +to restore the old behavior of serving to the public network on both IPv4 and +IPv6. ### Can I run `tensorboard` without a TensorFlow installation? diff --git a/tensorboard/program.py b/tensorboard/program.py index db12fa96d4..2bb34581bb 100644 --- a/tensorboard/program.py +++ b/tensorboard/program.py @@ -528,7 +528,7 @@ def get_url(self): if self._auto_wildcard: display_host = socket.gethostname() else: - host = self.host + host = self._host display_host = ( '[%s]' % host if ':' in host and not host.startswith('[') else host) return 'http://%s:%d%s/' % (display_host, self.server_port,