Merge pull request 'wiki: add troubleshooting article — iOS Tailscale clients report HostName="localhost"' (#1) from code/majormac/tailscale-ios-hostname-fix into main

Reviewed-on: #1
This commit is contained in:
Marcus Summers 2026-04-30 10:00:01 -04:00
commit 85f8a5df2d
3 changed files with 120 additions and 2 deletions

View file

@ -1,6 +1,6 @@
---
created: 2026-03-15T06:37
updated: 2026-04-29T22:45
updated: 2026-04-29T23:55
---
# 🔧 General Troubleshooting
@ -14,6 +14,7 @@ Practical fixes for common Linux, networking, and application problems.
- [Mail Client Stops Receiving: Fail2ban IMAP Self-Ban](networking/fail2ban-imap-self-ban-mail-client.md)
- [firewalld: Mail Ports Wiped After Reload](networking/firewalld-mail-ports-reset.md)
- [Tailscale SSH: Unexpected Re-Authentication Prompt](networking/tailscale-ssh-reauth-prompt.md)
- [iOS Tailscale Clients Report HostName="localhost" — Breaks /etc/hosts Generators](networking/tailscale-status-json-hostname-localhost-ios.md)
- [rsync over Tailscale: Hung in TCP Teardown After Transfer Completes](networking/rsync-tailscale-teardown-stall.md)
- [Windows OpenSSH: WSL Default Shell Breaks Remote Commands](networking/windows-openssh-wsl-default-shell-breaks-remote-commands.md)
- [Pi-hole AI Blocklist Blocks Claude Desktop (ERR_CONNECTION_REFUSED)](networking/pihole-blocks-claude-desktop.md)

View file

@ -0,0 +1,116 @@
---
title: iOS Tailscale Clients Report HostName="localhost" — Breaks /etc/hosts Generators
domain: troubleshooting
category: networking
tags:
- tailscale
- ios
- postfix
- etc-hosts
- jq
status: published
created: 2026-04-29
updated: 2026-04-29
---
# iOS Tailscale Clients Report HostName="localhost" — Breaks /etc/hosts Generators
## Problem
A homegrown script that builds an `/etc/hosts` block from `tailscale status --json` silently corrupted the file the moment any iOS device joined the tailnet. After the next run, services bound to `localhost` started failing.
On the affected host (`majordiscord`), Postfix refused to start with:
```
postfix: fatal: parameter inet_interfaces: no local interface found for 100.127.114.10
```
`/etc/hosts` looked fine at the top — `127.0.0.1 localhost` was still present — but inside the Tailscale-managed block:
```
# TAILSCALE_START
100.84.42.102 tttpod
100.110.197.17 majortoot
100.95.55.40 localhost <-- WRONG (this is an iPhone)
100.84.165.52 majormail
...
100.127.114.10 localhost <-- WRONG (this is an iPad)
# TAILSCALE_END
```
When Postfix resolved `localhost` (because `inet_interfaces = localhost` in `main.cf`), the **last matching entry** in `/etc/hosts` won — a Tailscale IP that doesn't exist on this host — and the daemon died on bind.
## Root Cause
The script used `.HostName` from the Tailscale JSON:
```bash
tailscale status --json \
| jq -r '.Peer[] | "\(.TailscaleIPs[0]) \(.HostName)"' \
>> "$TEMP_HOSTS"
```
iOS Tailscale clients (iPhone, iPad) **always report `HostName: "localhost"`** in the JSON. iOS doesn't expose the real device name to apps the way macOS/Linux/Windows do, so the Tailscale client falls back to the literal string `localhost`.
Inspect it directly:
```bash
$ tailscale status --json | jq '.Peer[] | select(.OS == "iOS") | {DNSName, HostName, OS}'
{
"DNSName": "iphone171.tail7f2d9.ts.net.",
"HostName": "localhost",
"OS": "iOS"
}
{
"DNSName": "ipad166.tail7f2d9.ts.net.",
"HostName": "localhost",
"OS": "iOS"
}
```
Every iOS device contributes a line `<tailscale-ip> localhost` to `/etc/hosts`, hijacking the `localhost` lookup.
## Fix
Use `.DNSName` (the unique tailnet DNS name) and take the first dotted component instead of `.HostName`:
```bash
tailscale status --json \
| jq -r '.Peer[] | "\(.TailscaleIPs[0]) \(.DNSName | rtrimstr(".") | split(".")[0])"' \
>> "$TEMP_HOSTS"
```
`DNSName` is always set, always unique, and produces clean labels like `iphone171`, `ipad166`, `majorlab`, etc.
After patching the script and re-running it:
```bash
$ bash /root/update_tailscale_hosts.sh
$ systemctl restart postfix
$ systemctl is-active postfix
active
```
## Why It's Hard to Spot
- The corruption only triggers when an iOS device is in the tailnet — so the script "worked" for months.
- `/etc/hosts` files are commonly skimmed top-down. The bogus `localhost` line is buried in the Tailscale block, well below the legitimate `127.0.0.1 localhost` line, and looks superficially like a normal Tailscale entry.
- Postfix's error message names the IP, not `localhost`, so the connection to `/etc/hosts` isn't obvious.
- `getent hosts localhost` shows the *first* match (`127.0.0.1`), not the one Postfix's resolver actually picks for `inet_interfaces` lookup.
## Verification Checklist
If you suspect this on any host using a similar generator script:
```bash
# Any non-loopback "localhost" entries are bugs
grep -nE '^[0-9]+\..* localhost\s*$' /etc/hosts
# Look at iOS peers' HostName field
tailscale status --json | jq '.Peer[] | select(.OS == "iOS") | .HostName'
```
## Related
- [[majordiscord]] — affected host (incident logged 2026-04-29)
- [[Network Overview]] — Tailscale fleet topology

View file

@ -1,6 +1,6 @@
---
created: 2026-04-02T16:03
updated: 2026-04-29T22:45
updated: 2026-04-29T23:55
---
* [Home](index.md)
* [Linux & Sysadmin](01-linux/index.md)
@ -91,6 +91,7 @@ updated: 2026-04-29T22:45
* [Ollama Drops Off Tailscale When Mac Sleeps](05-troubleshooting/ollama-macos-sleep-tailscale-disconnect.md)
* [Ollama: `ollama run` with Piped Stdin Bypasses Chat Template + SYSTEM Prompt](05-troubleshooting/ollama-chat-template-pipe-stdin-bypass.md)
* [rsync over Tailscale: Hung in TCP Teardown After Transfer Completes](05-troubleshooting/networking/rsync-tailscale-teardown-stall.md)
* [iOS Tailscale Clients Report HostName="localhost" — Breaks /etc/hosts Generators](05-troubleshooting/networking/tailscale-status-json-hostname-localhost-ios.md)
* [macOS: Repeating Alert Tone from Mirrored iPhone Notification](05-troubleshooting/macos-mirrored-notification-alert-loop.md)
* [ClamAV CPU Spike: Safe Scheduling with nice/ionice](05-troubleshooting/security/clamscan-cpu-spike-nice-ionice.md)
* [Ansible: Vault Password File Not Found](05-troubleshooting/ansible-vault-password-file-missing.md)