Skip to content

Resolve IPV4Host from the allocated address when TargetHost is customized - #19973

Open
Krishnendu Samanta (krishnendu-2003) wants to merge 2 commits into
microsoft:mainfrom
krishnendu-2003:fix/19972-ipv4host-respects-targethost
Open

Resolve IPV4Host from the allocated address when TargetHost is customized#19973
Krishnendu Samanta (krishnendu-2003) wants to merge 2 commits into
microsoft:mainfrom
krishnendu-2003:fix/19972-ipv4host-respects-targethost

Conversation

@krishnendu-2003

Copy link
Copy Markdown

Fixes #19972

Problem

EndpointProperty.IPV4Host returned the literal "127.0.0.1" for any endpoint resolved in the localhost network context, without ever consulting the endpoint's configured or allocated address:

EndpointProperty.IPV4Host when networkContext == KnownNetworkIdentifiers.LocalhostNetwork => "127.0.0.1",

So WithEndpoint("tcp", e => { e.TargetHost = "[::1]"; e.IsProxied = false; }) changes the DCP port binding and the allocated endpoint, but every IPV4Host consumer stays pointed at 127.0.0.1. The most visible casualty is SqlServerServerResource, whose connection string is built from IPV4Host (src/Aspire.Hosting.SqlServer/SqlServerServerResource.cs:50) — the sql_check health check and the AddDatabase creation step both fail with "connection refused" because nothing is listening on 127.0.0.1:1433. The issue reports the same 127.0.0.1 data source for all eight TargetHost variants tried.

Fix

The loopback literal is only the correct answer while the endpoint address is localhost. DcpModelUtilities.NormalizeTargetHost already encodes exactly that distinction, so the shortcut is now gated on it:

TargetHost Normalized IPV4Host
(default) localhost localhost 127.0.0.1 — unchanged
myapp.dev.localhost localhost 127.0.0.1 — unchanged
0.0.0.0 / [::] localhost 127.0.0.1 — unchanged
machine-name localhost 127.0.0.1 — unchanged
[::1] [::1] allocated address
10.0.0.1 10.0.0.1 allocated address

Wildcard binds and arbitrary machine names keep the literal, since their allocated address is localhost and substituting it would defeat the purpose of IPV4Host (avoiding a name that may resolve to ::1). Only a TargetHost naming one specific address falls through to ResolveValueWithAllocatedAddress(), resolving to the same value EndpointProperty.Host returns.

Two details worth a reviewer's eye:

  • The guard reads the annotation through a new internal EndpointReference.EndpointAnnotationOrDefault, which returns null instead of throwing, so an undefined endpoint still answers immediately rather than turning this property into a missing-endpoint exception.
  • It reaches from ApplicationModel into Aspire.Hosting.Dcp for NormalizeTargetHost. Reusing it keeps one source of truth for the wildcard and machine-name mapping; happy to move that helper somewhere more neutral if you'd prefer.

An endpoint with a customized TargetHost now waits for allocation instead of answering synchronously. That matches EndpointProperty.Host, and the default path still returns immediately.

Tests

Added to tests/Aspire.Hosting.Tests/EndpointReferenceTests.cs:

  • GetValueAsync_IPV4Host_WithCustomTargetHost_UsesAllocatedAddressTargetHost = "[::1]" does not complete before allocation and resolves to [::1]. Verified this fails on the unpatched source.
  • GetValueAsync_IPV4Host_WithLocalhostTldTargetHost_ReturnsImmediately — a *.localhost TLD still returns 127.0.0.1 immediately.

EndpointReferenceTests, ExpressionResolverTests (98 tests) and Dcp.DcpExecutorTests (277) pass. Aspire.Hosting.SqlServer.Tests: 56 pass, 9 fail because no Docker daemon is available on this machine.

The end-to-end SQL Server scenario from the issue needs a container runtime and was not run.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HpvU2U6aPPWcG4XdPGLVNk

…ized

EndpointProperty.IPV4Host returned the literal "127.0.0.1" for any endpoint
resolved in the localhost network context, without consulting the endpoint's
configured or allocated address. Setting TargetHost to a specific address, such
as "[::1]", therefore changed the DCP port binding and the allocated endpoint
while leaving every IPV4Host consumer pointed at 127.0.0.1 -- most visibly the
SQL Server connection string, whose health check and AddDatabase step then fail
with "connection refused".

The loopback literal is only the right answer while the endpoint address is
"localhost". Gate the shortcut on NormalizeTargetHost mapping TargetHost to
localhost, which keeps the literal for the default, a *.localhost TLD, a
wildcard bind (0.0.0.0, ::) and an arbitrary machine name, and falls through to
the allocated address when TargetHost names one specific address.

Fixes microsoft#19972

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HpvU2U6aPPWcG4XdPGLVNk
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 19973

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 19973"

@github-actions github-actions Bot added the area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication label Sep 7, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The public IPV4Host documentation now contradicts its new ability to return IPv6 addresses.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Fixes custom TargetHost handling so endpoint consumers use the allocated address instead of always using IPv4 loopback.

Changes:

  • Gates the 127.0.0.1 shortcut on normalized localhost binding.
  • Adds regression tests for IPv6 and localhost-TLD targets.
File summaries
File Description
src/Aspire.Hosting/ApplicationModel/EndpointReference.cs Resolves customized target hosts from allocation.
tests/Aspire.Hosting.Tests/EndpointReferenceTests.cs Covers custom IPv6 and localhost-TLD behavior.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/Aspire.Hosting/ApplicationModel/EndpointReference.cs
The enum member was documented as "The IPv4 address of the endpoint", which no
longer holds once a TargetHost naming one specific address resolves to whatever
the orchestrator allocated. Spell out both halves of the contract.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HpvU2U6aPPWcG4XdPGLVNk

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The focused implementation matches DCP normalization behavior and includes appropriate regression coverage.

Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@krishnendu-2003

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@danegsta

Copy link
Copy Markdown
Member

My primary concern with this PR is that service discovery—particularly for containers—has many layers where being intentional about IPv4 versus IPv6 is important. I’m resistant to changing IPV4Host so that it can return an IPv6 address. This property was specifically introduced to produce an IPv4 address in scenarios where resolving localhost to IPv6 would cause the consumer to fail. Changing its documentation does not eliminate the existing behavioral contract or the assumptions made by its consumers.

I agree that Aspire has a broader problem to solve around IPv4 and IPv6 networking, but I believe that solution will require new endpoint semantics. For example, we might introduce an IPHost property that honors the explicitly selected address family and potentially an IPV6Host counterpart to IPV4Host. That would allow consumers to opt into the appropriate behavior rather than making a property with IPV4 in its name return IPv6 addresses.

There are also gaps in how container endpoints are configured that this change does not address. TargetHost currently influences the DCP proxy binding, the container’s public port binding, and the address at which DCP expects the workload to produce the service. With the proxy enabled, setting TargetHost to [::1] therefore causes DCP to expect an IPv6 producer even though the SQL Server container listens on IPv4. With the proxy disabled, Docker may sometimes translate an IPv6 host publication to an IPv4 container listener, but that behavior depends on the container runtime and network configuration and does not work consistently—for example, on a dual-stack network.

A complete solution likely requires independent controls for the public binding, proxy producer address, and workload listener address. We will also need controls over the default container network’s address-family configuration: IPv4-only, IPv6-only, or dual-stack. Until those pieces are modeled separately, I don’t think changing IPV4Host is sufficient or safe as the general fix for this issue.

@danegsta David Negstad (danegsta) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned about the implications of this change. I agree that there's a gap here that we need to improve, but this change has implications for networking and service discovery that need to be considered very carefully and probably as part of a larger overhaul to better support intentional and explicit IPv6 usage for various parts of the service networking stack.

@krishnendu-2003

Copy link
Copy Markdown
Author

Thanks, this makes sense. I agree that changing IPV4Host to return an IPv6 address breaks an important existing contract, so I don't think the current approach should move forward as-is.

The underlying issue seems to be that we're currently conflating several different endpoint concepts through TargetHost: the public host binding, the address used by the DCP proxy to reach the producer, and the address family/listener used by the workload itself.

I think the cleaner direction is to leave IPV4Host strictly IPv4 and introduce explicit service-discovery semantics such as IPHost, with an IPV6Host counterpart if needed. IPHost could represent the intentionally selected address family rather than weakening the contract of IPV4Host.

For containers, that would still need to be paired with separate controls for the public binding, proxy producer address, and workload listener, plus explicit IPv4/IPv6/dual-stack network configuration.

I'm happy to rework this in that direction. Would you prefer that broader endpoint/networking model to be designed in a separate issue first and close/narrow this PR, rather than extending #19973 into that larger change?

@krishnendu-2003

Copy link
Copy Markdown
Author

Since the broader IPv4/IPv6 problem clearly extends beyond the scope of the relatively small changes in this PR, would it make sense to narrow this PR to the parts that are independently safe and useful, merge those, and open a separate issue for the larger endpoint/networking model?

That follow-up issue could cover IPHost/IPV6Host, separating public binding from the proxy producer and workload listener addresses, and explicit IPv4/IPv6/dual-stack container network configuration.

I think that would let us preserve the existing IPV4Host contract while still keeping the useful incremental changes from this PR, rather than expanding this PR into a much larger networking redesign.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AddSqlServer connection string ignores TargetHost: EndpointProperty.IPV4Host is hard-coded to 127.0.0.1 in the localhost network context

3 participants