Skip to content

Prefer localhost over 0.0.0.0 when in "development" mode - #1699

Closed
schneems wants to merge 1 commit into
puma:masterfrom
schneems:schneems/rails-server-environment
Closed

schneems wants to merge 1 commit into
puma:masterfrom
schneems:schneems/rails-server-environment

Conversation

@schneems

@schneems schneems commented Jan 4, 2019

Copy link
Copy Markdown
Member

When booting Puma via a rails server command it is desired to bind to localhost over 0.0.0.0. It is less secure to run a development application on 0.0.0.0.

To understand the differences here is a SO post https://stackoverflow.com/questions/20778771/what-is-the-difference-between-0-0-0-0-127-0-0-1-and-localhost

This PR works by detecting the "environment" option passed by default from Rails https://github.com/rails/rails/blob/663f6cc14fd01650546ed508efb3f7ad8e30135c/railties/lib/rails/commands/server/server_command.rb#L175. When it is set to "development" then localhost is used.

When booting Puma via a `rails server` command it is desired to bind to `localhost` over `0.0.0.0`. It is less secure to run a development application on `0.0.0.0`.

To understand the differences here is a SO post https://stackoverflow.com/questions/20778771/what-is-the-difference-between-0-0-0-0-127-0-0-1-and-localhost

This PR works by detecting the "environment" option passed by default from Rails https://github.com/rails/rails/blob/663f6cc14fd01650546ed508efb3f7ad8e30135c/railties/lib/rails/commands/server/server_command.rb#L175. When it is set to "development" then `localhost` is used.
schneems added a commit to schneems/puma that referenced this pull request Jan 4, 2019
This issue is somewhat tricky. When Rails is booted via `rails server` there are two types of configuration options passed, ones specified directly by a user like `rails s -p 3001` will always "win".

For any other config that is not explicitly passed in, puma will consider it a "default". For example when you run `rails s` (without -p) then the default port will be 3000.

There is one other way to configure puma though, and that is via a config file:

```
# config/puma.rb
port 3002
```

This is the order of precedence for configuration

1) Anything the user explicitly passes to `rails s`
2) Config specified in `config/puma.rb` file
3) Default values passed in via `rails s`
4) Defaults values stored in puma

This fallback mechanism works well except in the case of calling `port` in a `config/puma.rb` file. To understand look at the [old method definition](https://github.com/puma/puma/blob/2668597ec1dd9546d83db9f2ec5ad092add483e6/lib/puma/dsl.rb#L140-L145):

```
def port(port, host=nil)
  host ||= Configuration::DefaultTCPHost
  bind "tcp://#{host}:#{port}"
end
```

When the `port` method gets called, even if the user did not specify a `host` the `Configuration::DefaultTCPHost` will be used, which is a problem for local development because it defaults to `0.0.0.0`. [SO about 0.0.0.0 versus localhost](https://stackoverflow.com/questions/20778771/what-is-the-difference-between-0-0-0-0-127-0-0-1-and-localhost).

In this case, while a user did directly specify a port, they did not specify a host, so you would expect the `rails s` defaults passed in to take affect.

To make Puma respect that the host coming from `rails s` has more precedence than it's own default host, we must introduce the ability to set and retrieve a default_host value.

This is then used in the rack handler so when `rails s` passes in `:Host => "localhost"` then it is used instead of reverting to `0.0.0.0`.

The issue with puma#1699 is the test was wrong, it would have failed if a config file was present with a `port` invocation.
@schneems schneems closed this Jan 4, 2019
schneems added a commit to schneems/puma that referenced this pull request Jan 4, 2019
This issue is somewhat tricky. When Rails is booted via `rails server` there are two types of configuration options passed, ones specified directly by a user like `rails s -p 3001` will always "win".

For any other config that is not explicitly passed in, puma will consider it a "default". For example when you run `rails s` (without -p) then the default port will be 3000.

There is one other way to configure puma though, and that is via a config file:

```
# config/puma.rb
port 3002
```

This is the order of precedence for configuration

1) Anything the user explicitly passes to `rails s`
2) Config specified in `config/puma.rb` file
3) Default values passed in via `rails s`
4) Defaults values stored in puma

This fallback mechanism works well except in the case of calling `port` in a `config/puma.rb` file. To understand look at the [old method definition](https://github.com/puma/puma/blob/2668597ec1dd9546d83db9f2ec5ad092add483e6/lib/puma/dsl.rb#L140-L145):

```
def port(port, host=nil)
  host ||= Configuration::DefaultTCPHost
  bind "tcp://#{host}:#{port}"
end
```

When the `port` method gets called, even if the user did not specify a `host` the `Configuration::DefaultTCPHost` will be used, which is a problem for local development because it defaults to `0.0.0.0`. [SO about 0.0.0.0 versus localhost](https://stackoverflow.com/questions/20778771/what-is-the-difference-between-0-0-0-0-127-0-0-1-and-localhost).

In this case, while a user did directly specify a port, they did not specify a host, so you would expect the `rails s` defaults passed in to take affect.

To make Puma respect that the host coming from `rails s` has more precedence than it's own default host, we must introduce the ability to set and retrieve a default_host value.

This is then used in the rack handler so when `rails s` passes in `:Host => "localhost"` then it is used instead of reverting to `0.0.0.0`.

The issue with puma#1699 is the test was wrong, it would have failed if a config file was present with a `port` invocation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants