frasertweedale
frasertweedale

Reputation: 5644

Vagrant: do not map hostname to loopback address in /etc/hosts

I am using Vagrant (v1.7.2) to provision Linux (Fedora 22) hosts, and the vagrant-hostmanager plugin (v1.6.1) to write /etc/hosts so that hosts can access each other.

My Vagrantfile:

Vagrant.configure(2) do |config|

  config.vm.box = "workshop"

  config.hostmanager.enabled = true
  config.hostmanager.include_offline = true

  config.vm.define "server" do |server|
    server.vm.network "private_network", ip: "192.168.33.10"
    server.vm.hostname = "server.local"
  end

  config.vm.define "client" do |client|
    client.vm.network "private_network", ip: "192.168.33.20"
    client.vm.hostname = "client.local"
  end

end

When I vagrant up, the server VM has the following /etc/hosts:

127.0.0.1 server.ipademo.local server
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

## vagrant-hostmanager-start
192.168.33.10   server.ipademo.local

192.168.33.20   client.ipademo.local

## vagrant-hostmanager-end

(For VM client, substitute s/server/client/ on the first line only.)

Because Vagrant >= 1.5.0 runs the vagrant-hostmanager plugin before provisioning, I also tried running hostmanager during provisions by changing the Vagrantfile to:

  config.hostmanager.enabled = false
  config.hostmanager.include_offline = true
  config.vm.provision :hostmanager

This had the same outcome.

Problem description

The 127.0.0.1 <fqdn> <shortname> line conflicts with the information added by vagrant-hostmanager. I need to suppress the association of the hostname to the loopback address, so that on every VM the hostname resolves to the private network address, as added by vagrant-hostmanager.

How can I accomplish this?

Upvotes: 3

Views: 3525

Answers (2)

KarlKFI
KarlKFI

Reputation: 3112

Most linux distributions have the same issue caused by the change_host_name vagrant capability (redhat, debian, arch, etc) when the hostname is set via Vagrant.

The following line from change_host_name prepends the vm hostname and vm name as aliases to 127.0.0.1 when the hostname is set, which conflicts with vagrant-hostmanager's aliasing.

sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts

Vagrant authors have said that this is intentional and that they aren't planning to fix it, because it works as-is, as long as you don't modify /etc/hosts (which vagrant-hostmanager does).

Interestingly, many tools (dig, host, nslookup) use the LAST specified alias, which works fine with the defualt Vagrant behavior. Unfortunately, other tools, namely ones using gethostbyname or /etc/nsswitch.conf, have different behavior.

Ping, for example, seems to use the first alias in /etc/hosts, while Curl (when compiled to use NSS) uses multiple aliases as fallback from top down.

Because of this, having an alias match multiple IPs is generally discouraged.

The following Vagrantfile workaround should be sufficient:

config.hostmanager.enabled = true

name = "name"
hostname = "hostname"

config.vm.define name do |machine|
  machine.vm.hostname = hostname
  machine.vm.provision :shell, inline: "sed -i'' '/^127.0.0.1\\t#{hostname}\\t#{name}$/d' /etc/hosts"
  ...
end

Upvotes: 1

frasertweedale
frasertweedale

Reputation: 5644

The cause of the problem is Vagrant's change host name capability for Fedora guests. In particular, in plugins/guests/fedora/cap/change_host_name.rb:

def update_etc_hosts                             
  ip_address = '([0-9]{1,3}\.){3}[0-9]{1,3}'
  search     = "^(#{ip_address})\\s+#{Regexp.escape(current_hostname)}(\\s.*)?$"
  replace    = "\\1 #{fqdn} #{short_hostname}"
  expression = ['s', search, replace, 'g'].join('@')

  sudo("sed -ri '#{expression}' /etc/hosts")
end

The update_etc_hosts method replaces the original hostname (for Fedora, this is localhost.localdomain, bound to the loopback address 127.0.0.1) with the new hostname. It then updates /etc/hostname with the short hostname, although system calls still return the full hostname because it appears in /etc/hosts.

The solution

I provided additional provisioners (which run after the above described hackery takes place) to:

  1. Query the long hostname (FQDN) and write it back to /etc/hostname. This is needed so hostname --fqdn actually returns the full hostname after we repair /etc/hosts in the next step.

  2. Restore the loopback line in /etc/hosts so that the machine's hostname resolves to the private network address as set by vagrant-hostmanager.

The order is critical. Here's the Vagrantfile code:

# Vagrant's "change host name" sets the short host name.
# Before we undo the /etc/hosts silliness (see below) let's
# reset /etc/hostname to the *full* host name
#
config.vm.provision "shell",
  inline: "hostname --fqdn > /etc/hostname && hostname -F /etc/hostname"

# Vagrant's "change host name" capability for Fedora
# maps hostname to loopback, conflicting with hostmanager.
# We must repair /etc/hosts
#
config.vm.provision "shell",
  inline: "sed -ri 's/127\.0\.0\.1\s.*/127.0.0.1 localhost localhost.localdomain/' /etc/hosts"

Upvotes: 2

Related Questions