[Bug] Normalization of hostnames #1166

Open
opened 2025-12-29 02:28:41 +01:00 by adam · 4 comments
Owner

Originally created by @griffer on GitHub (Dec 1, 2025).

Is this a support request?

  • This is not a support request

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

First off, love Headscale, it works great!

I think I found a bug in hostname handling.

When a client reports a hostname like "My-PC!", it fails ValidateHostname() and gets replaced with "invalid-xyz". Headscales code has a NormaliseHostname() function that would sanitize this to "my-pc".

The affected code paths are:

  • ApplyHostnameFromHostInfo() hscontrol/types/node.go
  • EnsureHostname() in hscontrol/util/util.go

Both use ValidateHostname() (reject if invalid), instead of NormaliseHostname() (sanitize and accept).

This seems like an oversight? The NormaliseHostname function exists but isn't being used.

Expected Behavior

Expected: Hostname "My-PC!" → sanitized to "my-pc"
Actual: Hostname "My-PC!" → rejected, becomes "invalid-abc123"

Steps To Reproduce

  1. Register a node with hostname containing invalid characters:

On client with hostname "My-PC!"

tailscale up --login-server https://your-headscale-server

  1. List nodes on server:
    headscale nodes list

  2. Expected: Node appears as my-pc

  3. Actual: Node appears as invalid-abc123

Environment

- Headscale version: v0.27.1
- Deployment: Docker
- OS: Ubuntu 24.04.3 LTS
- Docker: 29.0.2

Runtime environment

  • Headscale is behind a (reverse) proxy
  • Headscale runs in a container

Debug information

This is a code logic issue, so dont believe debug log is useful.

Originally created by @griffer on GitHub (Dec 1, 2025). ### Is this a support request? - [x] This is not a support request ### Is there an existing issue for this? - [x] I have searched the existing issues ### Current Behavior First off, love Headscale, it works great! I think I found a bug in hostname handling. When a client reports a hostname like "My-PC!", it fails ValidateHostname() and gets replaced with "invalid-xyz". Headscales code has a NormaliseHostname() function that would sanitize this to "my-pc". The affected code paths are: - ApplyHostnameFromHostInfo() *hscontrol/types/node.go* - EnsureHostname() in *hscontrol/util/util.go* Both use ValidateHostname() (reject if invalid), instead of NormaliseHostname() (sanitize and accept). This seems like an oversight? The NormaliseHostname function exists but isn't being used. ### Expected Behavior Expected: Hostname "My-PC!" → sanitized to "my-pc" Actual: Hostname "My-PC!" → rejected, becomes "invalid-abc123" ### Steps To Reproduce 1. Register a node with hostname containing invalid characters: ### On client with hostname "My-PC!" tailscale up --login-server https://your-headscale-server 2. List nodes on server: headscale nodes list 3. Expected: Node appears as my-pc 4. Actual: Node appears as invalid-abc123 ### Environment ```markdown - Headscale version: v0.27.1 - Deployment: Docker - OS: Ubuntu 24.04.3 LTS - Docker: 29.0.2 ``` ### Runtime environment - [x] Headscale is behind a (reverse) proxy - [x] Headscale runs in a container ### Debug information This is a code logic issue, so dont believe debug log is useful.
adam added the bug label 2025-12-29 02:28:41 +01:00
Author
Owner

@roganlynch commented on GitHub (Dec 13, 2025):

I believe that this is the "as designed" behavior your are observing. The Changelog indicates that the first function downcases improperly cased strings while the second synthesizes a DNS compatible string when a string contains an illegal character (outside of dns hostname spec)
More rigorous hostname formulation might be desirable, but that's really more the client's prerogative - the client needs to self-identify as something dns-flavored - not "I'm a Pretty Mac!" or some other nonsense. The invalid-1a2b3c garbage is intentionally painful - so as to drive home that point. If you want it to work, then do so, one.host.at.a.time.

@roganlynch commented on GitHub (Dec 13, 2025): I believe that this is the "as designed" behavior your are observing. The Changelog indicates that the first function downcases improperly cased strings while the second synthesizes a DNS compatible string when a string contains an illegal character (outside of dns hostname spec) More rigorous hostname formulation might be desirable, but that's really more the client's prerogative - the client needs to self-identify as something dns-flavored - not "I'm a Pretty Mac!" or some other nonsense. The invalid-1a2b3c garbage is intentionally painful - so as to drive home that point. If you want it to work, then do so, one.host.at.a.time.
Author
Owner

@aalmenar commented on GitHub (Dec 13, 2025):

@roganlynch this behaviour it's not like the upstream tailscale service. Also, not in all case you have the possibility to fix every host in the tailnet, if you have users (Like i have at home) each one will login to headscale using my oidc but im not gonna be following each of them to fix the hostname to be DNS compatible.

I have a user who as "My pretty macbook", i don't expect hostname to be "invalid-1a2b3c" but "my-pretty-macbook", from there if i want to rename it, also breaks the nicety of using magicdns. i dont expect to be perfect conversion, but close enough to the original name, i dont expect emojis and international characters or any idna conversion but something like white spaces can be converted to dashes.

@aalmenar commented on GitHub (Dec 13, 2025): @roganlynch this behaviour it's not like the upstream tailscale service. Also, not in all case you have the possibility to fix every host in the tailnet, if you have users (Like i have at home) each one will login to headscale using my oidc but im not gonna be following each of them to fix the hostname to be DNS compatible. I have a user who as "My pretty macbook", i don't expect hostname to be "invalid-1a2b3c" but "my-pretty-macbook", from there if i want to rename it, also breaks the nicety of using magicdns. i dont expect to be perfect conversion, but close enough to the original name, i dont expect emojis and international characters or any idna conversion but something like white spaces can be converted to dashes.
Author
Owner

@griffer commented on GitHub (Dec 15, 2025):

@roganlynch This may be intentional behavior. My observation was primarily about internal consistency in the codebase rather than client responsibility.

There is already a NormaliseHostname() function that produces a DNS-compatible hostname by sanitizing invalid characters (e.g., "My-PC!" => "my-pc"). However, the current logic paths (ApplyHostnameFromHostInfo() and EnsureHostname()) reject such hostnames via ValidateHostname() instead of applying normalization.

In my local setup, I tested a minimal change that applies NormaliseHostname() prior to validation. With that adjustment, hostnames containing minor invalid characters are consistently normalized and no longer replaced with invalid-*, without adding new behavior beyond what already exists.

I raised this mainly to clarify whether the existing normalization function is intentionally unused in these paths or whether its absence is incidental.

@griffer commented on GitHub (Dec 15, 2025): @roganlynch This may be intentional behavior. My observation was primarily about internal consistency in the codebase rather than client responsibility. There is already a NormaliseHostname() function that produces a DNS-compatible hostname by sanitizing invalid characters (e.g., "My-PC!" => "my-pc"). However, the current logic paths (ApplyHostnameFromHostInfo() and EnsureHostname()) reject such hostnames via ValidateHostname() instead of applying normalization. In my local setup, I tested a minimal change that applies NormaliseHostname() prior to validation. With that adjustment, hostnames containing minor invalid characters are consistently normalized and no longer replaced with invalid-*, without adding new behavior beyond what already exists. I raised this mainly to clarify whether the existing normalization function is intentionally unused in these paths or whether its absence is incidental.
Author
Owner

@roganlynch commented on GitHub (Dec 16, 2025):

Thank you for the clarification. Sounds like you could/should just submit a
PR with your commit if you done some amount of validation, link the issue
with the PR and let the maintainers decide.

Would seem like a no-brainer.

On Mon, Dec 15, 2025 at 12:32 AM griffer @.***> wrote:

griffer left a comment (juanfont/headscale#2926)
https://github.com/juanfont/headscale/issues/2926#issuecomment-3654412894

@roganlynch https://github.com/roganlynch This may be intentional
behavior. My observation was primarily about internal consistency in the
codebase rather than client responsibility.

There is already a NormaliseHostname() function that produces a
DNS-compatible hostname by sanitizing invalid characters (e.g., "My-PC!" =>
"my-pc"). However, the current logic paths (ApplyHostnameFromHostInfo() and
EnsureHostname()) reject such hostnames via ValidateHostname() instead of
applying normalization.

In my local setup, I tested a minimal change that applies
NormaliseHostname() prior to validation. With that adjustment, hostnames
containing minor invalid characters are consistently normalized and no
longer replaced with invalid-*, without adding new behavior beyond what
already exists.

I raised this mainly to clarify whether the existing normalization
function is intentionally unused in these paths or whether its absence is
incidental.


Reply to this email directly, view it on GitHub
https://github.com/juanfont/headscale/issues/2926#issuecomment-3654412894,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AKU36CLRB4YWEGWDXNOPUBL4BZW3PAVCNFSM6AAAAACNVCGMBKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTMNJUGQYTEOBZGQ
.
You are receiving this because you were mentioned.Message ID:
@.***>

--
tel: (408) 426-8649

@roganlynch commented on GitHub (Dec 16, 2025): Thank you for the clarification. Sounds like you could/should just submit a PR with your commit if you done some amount of validation, link the issue with the PR and let the maintainers decide. Would seem like a no-brainer. On Mon, Dec 15, 2025 at 12:32 AM griffer ***@***.***> wrote: > *griffer* left a comment (juanfont/headscale#2926) > <https://github.com/juanfont/headscale/issues/2926#issuecomment-3654412894> > > @roganlynch <https://github.com/roganlynch> This may be intentional > behavior. My observation was primarily about internal consistency in the > codebase rather than client responsibility. > > There is already a NormaliseHostname() function that produces a > DNS-compatible hostname by sanitizing invalid characters (e.g., "My-PC!" => > "my-pc"). However, the current logic paths (ApplyHostnameFromHostInfo() and > EnsureHostname()) reject such hostnames via ValidateHostname() instead of > applying normalization. > > In my local setup, I tested a minimal change that applies > NormaliseHostname() prior to validation. With that adjustment, hostnames > containing minor invalid characters are consistently normalized and no > longer replaced with invalid-*, without adding new behavior beyond what > already exists. > > I raised this mainly to clarify whether the existing normalization > function is intentionally unused in these paths or whether its absence is > incidental. > > — > Reply to this email directly, view it on GitHub > <https://github.com/juanfont/headscale/issues/2926#issuecomment-3654412894>, > or unsubscribe > <https://github.com/notifications/unsubscribe-auth/AKU36CLRB4YWEGWDXNOPUBL4BZW3PAVCNFSM6AAAAACNVCGMBKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTMNJUGQYTEOBZGQ> > . > You are receiving this because you were mentioned.Message ID: > ***@***.***> > -- tel: (408) 426-8649
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/headscale#1166