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:
commit
85f8a5df2d
3 changed files with 120 additions and 2 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue