Fixing broken /etc/hosts entries after a Windows sync introduces stray CR characters

When a Windows machine syncs a shared folder that contains /etc/hosts, the file often ends up with Windows‑style CRLF line endings.

If you’ve ever pulled a hosts file from a Windows share, you’ve probably noticed that the resolver starts acting weird. The glibc resolver stops at the carriage return (\r) and treats the rest of the line as part of the hostname. That means lookups for localhost, myserver, or any entry that follows a stray CR fail, and some services silently fall back to DNS—exposing the system to name‑resolution attacks.

Why stray CR matters

The resolver parses /etc/hosts line‑by‑line. A line that looks like

127.0.0.1\r localhost

is read as the IP 127.0.0.1 and the hostname localhost\r. The trailing CR is not trimmed, so the resolver never matches the exact string localhost. Applications that rely on the hosts file—ping, curl, systemd‑resolved, or even sshd when UseDNS no is set—will fail to resolve the host. In a production environment this can break health checks, container networking, or local authentication.

Because the resolver silently ignores the malformed entry, the problem can go unnoticed until a service starts using DNS instead of the intended local IP. That switch can introduce latency or, worse, expose the system to DNS spoofing if the external DNS server is compromised.

Detecting stray CR characters

The quickest way to spot CRs is to use grep with a literal carriage return:

grep -n $'\r' /etc/hosts

If any lines are returned, the file contains CRLF endings. od -c shows the raw bytes:

od -c /etc/hosts | head

Lines ending with \r\n will display \r before the newline. cat -v also reveals hidden characters:

cat -v /etc/hosts

A line that ends with ^M indicates a stray CR.

Fixing the file

The simplest fix is to strip CRs with dos2unix, which is available on most distributions:

sudo dos2unix /etc/hosts

If you prefer a one‑liner that works without external tools, sed can do the job:

sudo sed -i 's/\r$//' /etc/hosts

For a more robust approach that also normalises whitespace, use awk:

sudo awk '{ sub(/\r$/,""); print }' /etc/hosts > /tmp/hosts.tmp && sudo mv /tmp/hosts.tmp /etc/hosts

After cleaning, verify that the file contains only LF line endings:

file /etc/hosts
# /etc/hosts: ASCII text

If you want to enforce the format automatically, add a small wrapper script to your sync process:

#!/usr/bin/env bash
# sync-hosts.sh
rsync -avz --iconv=utf-8,utf-8 --no-whole-file "$1" /etc/hosts
dos2unix /etc/hosts

Call this script whenever the Windows machine updates the shared folder.

Preventing future sync issues

  1. Use rsync with --iconv – it converts line endings on the fly if you set the correct character set.

  2. Enable SMB “Unix extensions” – when Windows shares a folder with the unix extensions option, it preserves LF line endings.

  3. Store the hosts file in a Git repo – add a .gitattributes entry to force LF:

    /etc/hosts text eol=lf
    
  4. Run dos2unix automatically – add a cron job that checks /etc/hosts for CRs and cleans them.

  5. Use a Windows tool – configure the Windows sync client (e.g., robocopy or SyncToy) to use Unix line endings or to skip the file entirely.

Security considerations

The hosts file is a critical component of local name resolution. An attacker who can inject a malformed entry could trick services into connecting to the wrong IP. To mitigate this:

  • Restrict permissions: /etc/hosts should be owned by root and readable by all, writable only by root (chmod 644 /etc/hosts).

  • Use the immutable flag: chattr +i /etc/hosts prevents accidental edits, but remember to remove the flag (chattr -i) when you need to update it.

  • Audit changes: Enable auditd to log any modifications to /etc/hosts:

    auditctl -w /etc/hosts -p wa -k hosts-change
    
  • SELinux/AppArmor: Ensure the policy allows the resolver to read /etc/hosts but restricts write access to the root user only.

  • Validate after sync: A small script that runs grep -n $'\r' and exits with a non‑zero status if CRs are found can be integrated into CI pipelines or systemd unit files.

By treating /etc/hosts as a protected resource and automating its cleanup, you reduce the risk of both accidental misconfiguration and malicious tampering.


See also