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/plugins/core/core_plugin.py b/tensorboard/plugins/core/core_plugin.py index 32f8aa9f6e..e1fa23a24e 100644 --- a/tensorboard/plugins/core/core_plugin.py +++ b/tensorboard/plugins/core/core_plugin.py @@ -322,12 +322,23 @@ def define_flags(self, parser): '--host', metavar='ADDR', type=str, - default='', + default=None, # like localhost, but prints a note about `--bind_all` 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 (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', @@ -553,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 a1aaa4a0cb..2bb34581bb 100644 --- a/tensorboard/program.py +++ b/tensorboard/program.py @@ -218,9 +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())) - sys.stderr.flush() + server.print_serving_message() self._register_info(server) server.serve_forever() return 0 @@ -326,6 +324,17 @@ 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()) + ) + sys.stderr.flush() + 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 = not 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,21 @@ 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' + ) + sys.stderr.flush() + 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