Deno: Deno.ListenOptions hostname defaults to 127.0.0.1 instead of 0.0.0.0

Created on 7 May 2020  路  9Comments  路  Source: denoland/deno

Summary:

When a hostname is not provided to a method which uses Deno.ListenOptions, the process binds to 127.0.0.1 instead of 0.0.0.0 (all addresses). This will cause connections to all addresses other than localhost and 127.0.0.1 to fail.

lib.deno.d.ts#L1416:

export interface ListenOptions {
  /** The port to listen on. */
  port: number;
  /** A literal IP address or host name that can be resolved to an IP address.
   * If not specified, defaults to `0.0.0.0`. */
  hostname?: string;
}

Not sure if it's a bug or if the docs need be changed because of a design change since they were last updated.

Repro:

Try connecting to both of the examples below (using deno run --allow-net) using 3 different hostnames:

  1. http://localhost:3000
  2. http://127.0.0.1:3000
  3. <YOUR_LAN_IP_ADDRESS>:3000 (will fail in the first example, but not the second)

Using default hostname

import {serve} from 'https://deno.land/std/http/mod.ts';

const port = 3000;

const server = serve({port}); // use default hostname
console.log(`Server running on port ${port}`);

for await (const req of server) {
  req.respond({body: 'hello world'});
}

Providing explicit hostname 0.0.0.0

import {serve} from 'https://deno.land/std/http/mod.ts';

const hostname = '0.0.0.0';
const port = 3000;

const server = serve({hostname, port}); // specify 0.0.0.0 as hostname (all addresses)
console.log(`Server running on port ${port}`);

for await (const req of server) {
  req.respond({body: 'hello world'});
}

Most helpful comment

Binding only to the localhost by default seem a better situation than binding globally. It feels if you want to open yourself up to the world, you would want to be explicit about it. So I see the current behaviour as desirable.

What do other platforms do?

All 9 comments

Binding only to the localhost by default seem a better situation than binding globally. It feels if you want to open yourself up to the world, you would want to be explicit about it. So I see the current behaviour as desirable.

What do other platforms do?

Deno aims to provide a productive and secure scripting environment for the modern programmer.

@kitsonk I agree鈥攂eing biased toward security is a good idea for the default configuration.

I disagree with Kitson. I think most people want to listen on 0.0.0.0 by default. I'd accept a PR to change it.

Also it listens only on IPv4, I would expect it to listen on both protocols by default. Sorry if it's unrelated.

From Node's docs

If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available, or the unspecified IPv4 address (0.0.0.0) otherwise.
In most operating systems, listening to the unspecified IPv6 address (::) may cause the net.Server to also listen on the unspecified IPv4 address (0.0.0.0).

Based on this, Deno.ListenOptions hostname should work for either case.

I'll take a crack at this!

Been thinking about this one and doing some cursory digging into how other frameworks handle this. I was wondering if we'd be open to introducing an attribute like ipv6Only in ListenOptions so that folks can be explicit about how to handle the hostname.

So if you did something like this:

const listener = Deno.listen({ port: 4500, ipv6Only: true });

The hostname will be follow ipv6 i.e. 127.0.0.1 and if this isn't specified, it defaults to 0.0.0.0

Thoughts?

@shortdiv yes I think, an ipv6 flag makes sense since everyone might not use it, but if ipv6 is present I think It binds to [::1] (localhost) or some ipv6 address like [xxxx::xxxx:xxxx:xxxx:xxxx], also this is handled in the codebase differently where there is a check on this done saying if the address/hostname passed to the listen function is starting with '[' and ending with ']'. So, will have to handle that also. But as of now to answer @1player 's question, for it to bind on ipv6 also, you'll have to pass in the hostname as an ipv6 address.

I personally am not a fan of defaulting to 127.0.0.1 or 0.0.0.0, or defaulting to IPv4-only for that matter.

The Node approach is better:

listening to the unspecified IPv6 address (::) may cause the net.Server to also listen on the unspecified IPv4 address (0.0.0.0).

[::] on POSIX operating systems means "listen on all interfaces on both IPv4 and IPv6", which is a better default in 2020, especially since "localhost" might be preferrably resolve to the IPv6 address [::1] and we want to be able to connect to our app by using only the "localhost" hostname.

So, in my opinion we should replicate the Node behaviour, as IPv6 is a reality and supporting it out of the box is better than having users explicitely enable it in their code.

EDIT: all major operating systems listen on both protocols when given an IPv6 listening address. So [::] or [::1] is a better default than any other alternative and while still being backwards-compatible with IPv4.

@1player yes whatever you said actually makes sense. But I'm hoping that this kind of functionality to explicitly bind to ipv6 is a constraint set up, keeping security in mind. But by default binding to ipv6 without a flag would make the user not being aware that he's doing that.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

motss picture motss  路  3Comments

ry picture ry  路  3Comments

CruxCv picture CruxCv  路  3Comments

zugende picture zugende  路  3Comments

ry picture ry  路  3Comments