Skip to content

Commit

Permalink
core: bind to localhost rather than all interfaces (#2589)
Browse files Browse the repository at this point in the history
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 `--bind_all` 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`:

  - running with no `--host` flag:
      - can connect to loopback on IPv4 only
      - cannot connect over LAN
      - `strace` shows binding on `AF_INET`
      - a notice about `--bind_all` is printed to stderr
  - running with `--host=localhost`:
      - same behavior as with no `--host` flag, but no notice is printed
  - 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='::0'`:
      - can connect on both IPv4 and IPv6
      - **can** connect over LAN
      - `strace` shows binding on `AF_INET6`
  - running with `--bind_all`:
      - 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

In all cases, the printed serving URL (“TensorBoard x.y.z running at…”)
bears the exact `--host` flag, or my full hostname if `--bind_all` was
given, or `localhost` if neither was given. In all cases, the URL is a
clickable link in my `gnome-terminal`.

Note that on my system dual binding to `::0` works without an explicit
syscall—i.e., `IPV6_V6ONLY` defaults to `0`—but this is not portable.

Connection testing was performed via

```shell
for ipv in 4 6; do
    if curl -sfL -"${ipv}" localhost:6006/data/logdir >/dev/null; then
        printf 'v%d OK\n' "${ipv}"
    else
        printf 'v%d FAIL\n' "${ipv}"
    fi
done
```

in all cases.

wchargin-branch: localhost-only
  • Loading branch information
wchargin authored Sep 18, 2019
1 parent 62357f6 commit 9dfd82b
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 14 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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?

Expand Down
19 changes: 16 additions & 3 deletions tensorboard/plugins/core/core_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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]
Expand Down
4 changes: 4 additions & 0 deletions tensorboard/plugins/core/core_plugin_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,17 @@
class FakeFlags(object):
def __init__(
self,
bind_all=False,
host=None,
inspect=False,
version_tb=False,
logdir='',
logdir_spec='',
event_file='',
db='',
path_prefix=''):
self.bind_all = bind_all
self.host = host
self.inspect = inspect
self.version_tb = version_tb
self.logdir = logdir
Expand Down
37 changes: 29 additions & 8 deletions tensorboard/program.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions tensorboard/program_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 9dfd82b

Please sign in to comment.