Per-User Hosts File Configuration: Alternative to /etc/hosts for Multi-Machine LAN Environments


2 views

When working across multiple machines in a small LAN environment, managing hostname resolution becomes challenging. The standard /etc/hosts approach presents several limitations:

# Traditional /etc/hosts entry
192.168.1.10   server1
192.168.1.11   server2
  • Requires root privileges to modify
  • Changes affect all users on the system
  • Synchronization across machines is cumbersome

Several approaches exist for per-user host resolution:

1. Custom DNS Resolution with getent

Create a ~/.hosts file and modify your shell configuration:

# ~/.bashrc or ~/.zshrc addition
resolve_host() {
    local host=$1
    grep "^[0-9]" ~/.hosts | awk -v host="$host" '$2 == host {print $1}'
}

alias myping='ping $(resolve_host target)'

2. Using dnsmasq with Local Configuration

For more advanced setups, configure dnsmasq to read per-user hosts files:

# ~/.config/dnsmasq.conf
addn-hosts=/home/user/.hosts

Then run a personal dnsmasq instance:

dnsmasq --no-daemon --conf-file=/home/user/.config/dnsmasq.conf

3. NetworkManager's connection-specific DNS

Modern Linux systems using NetworkManager support per-connection DNS:

# nmcli example
nmcli connection modify eth0 ipv4.dns "192.168.1.1"
nmcli connection modify eth0 +ipv4.dns-search "mydomain.lan"

For keeping hosts files in sync across machines, consider this rsync wrapper:

#!/bin/bash
# sync-hosts.sh
RSYNC_OPTS="-avz --delete"
TARGETS=("user@machine1:/home/user/.hosts" "user@machine2:/home/user/.hosts")

for target in "${TARGETS[@]}"; do
    rsync $RSYNC_OPTS ~/.hosts "$target"
done
  • Restrict .hosts file permissions: chmod 600 ~/.hosts
  • Validate IP addresses before syncing
  • Consider using SSH keys for rsync transfers

Here's a complete example of the custom resolver approach:

# Sample ~/.hosts
192.168.1.100   nas.local
192.168.1.101   dev.local

# ~/.bashrc additions
export HOSTALIASES=~/.hosts
function resolve() {
    [ -f ~/.hosts ] && getent hosts $1 || getent ahosts $1 | head -n1 | awk '{print $1}'
}

# Usage examples:
ping $(resolve nas.local)
ssh user@$(resolve dev.local)

While /etc/hosts provides system-wide hostname resolution, it lacks user-specific flexibility. In LAN environments where machines frequently disconnect (making DNS servers impractical), we need a more dynamic solution.

Create a ~/.hosts file with the same format as /etc/hosts:

127.0.0.1       localhost
192.168.1.10    fileserver.lan
192.168.1.20    printer.lan

Method 1: Custom Resolver via LD_PRELOAD

Create a shared library to intercept DNS lookups:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>

struct hostent *gethostbyname(const char *name) {
    FILE *fp = fopen(".hosts", "r");
    if (fp) {
        // Parse .hosts file
        // Return matched entry
        fclose(fp);
    }
    return NULL; // Fallback to normal resolution
}

Compile with: gcc -shared -fPIC -o libcustomhosts.so customhosts.c

Add to ~/.bashrc: export LD_PRELOAD=/path/to/libcustomhosts.so

Method 2: Bash Alias Wrapper

For simpler cases, create a bash function:

resolve_host() {
    local host=$1
    # Check .hosts first
    while read -r ip hnames; do
        if [[ " ${hnames} " == *" ${host} "* ]]; then
            echo $ip
            return
        fi
    done < ~/.hosts 2>/dev/null
    
    # Fallback to normal resolution
    getent hosts "$host"
}

alias myping='ping $(resolve_host $1)'

Set up rsync in cron to keep .hosts files consistent across machines:

# In crontab -e
*/5 * * * * rsync -az user@centralmachine:.hosts ~/.hosts
  • Set proper permissions: chmod 600 ~/.hosts
  • Validate IP formats when syncing
  • Consider using SSH for rsync transfers

For more complex scenarios:

  • Use dnsmasq with its --hostsdir option
  • Implement a simple HTTP DNS server that reads from user-specific files
  • Consider Zeroconf/Bonjour for small LANs