Aspire
Tunnel a gateway to internal services
Expose a YARP or other gateway that proxies to internal services, and give those services the public address for webhook callbacks.
Use this when a gateway proxies to internal services
#Your AppHost has a gateway, for example a YARP project, that receives outside traffic and forwards it to internal services. Only the gateway gets a tunnel. The internal services still need the gateway’s public address, for example to register a webhook or sign-in callback that a third party calls back on.
The example on this page has a gateway named api-gateway in front of 2 services, service-a and service-b, and a tunnel named my-tunnel.
Add a tunnel to the gateway
#var builder = DistributedApplication.CreateBuilder(args);
// tunnelKey holds my-tunnel's connection key. secret: true makes it a secret parameter:
// Aspire masks it in the dashboard and reads it from Parameters:groundhog-tunnel-key in user secrets.
var tunnelKey = builder.AddParameter("groundhog-tunnel-key", secret: true);
var serviceA = builder.AddProject<Projects.ServiceA>("service-a");
var serviceB = builder.AddProject<Projects.ServiceB>("service-b");
var apiGateway = builder.AddProject<Projects.ApiGateway>("api-gateway")
.WithReference(serviceA)
.WithReference(serviceB);
var apiGatewayTunnel = builder.AddGroundhogTunnel("api-gateway-tunnel", "my-tunnel", tunnelKey)
.WithReference(apiGateway.GetEndpoint("https"));
serviceA.WithReference(apiGateway, apiGatewayTunnel);
serviceB.WithReference(apiGateway, apiGatewayTunnel);
builder.Build().Run();- The
api-gatewayproject reachesservice-aandservice-bthrough ordinary Aspire service discovery, inside your computer. - The
api-gateway-tunnelresource forwardshttps://my-tunnel.groundhog.unifinity.comtoapi-gateway’s https endpoint."api-gateway-tunnel"is the resource name you choose for the Aspire dashboard;"my-tunnel"is the tunnel’s name in Groundhog, which becomes its public host. See AddGroundhogTunnel parameters. - Aspire asks for
groundhog-tunnel-key,my-tunnel’s connection key, copied when you created the tunnel in the Groundhog dashboard. - The last 2 lines give
service-aandservice-bapi-gateway’s public address, as the next section explains.
Give internal services the public address
#WithReference(apiGateway, apiGatewayTunnel) works like Aspire dev tunnels. The service-a and service-b projects each get services__api-gateway__https__0 set to https://my-tunnel.groundhog.unifinity.com, so https://api-gateway resolves to the public address.
Only https is set, because the public address is always HTTPS. The value is filled in when each service starts, and neither service waits for the tunnel.
This needs client 0.4.0-preview.7 or later. If the tunnel forwards to a different resource than the one you reference, the AppHost build fails with a message naming both.
Build webhook callback addresses in a service
#In service-a, read api-gateway’s address from configuration and add the path api-gateway routes to service-a:
var apiGatewayAddress = builder.Configuration["services:api-gateway:https:0"]
?? throw new InvalidOperationException("The api-gateway address is not set.");
var webhookCallback = new Uri(new Uri(apiGatewayAddress), "/service-a/webhooks");The resulting webhookCallback is https://my-tunnel.groundhog.unifinity.com/service-a/webhooks. Register it with the third party: its requests reach api-gateway through the tunnel and api-gateway forwards them to service-a. See Register webhooks and sign-in callbacks for what to change when the address changes.
Add the gateway tunnel with WithGroundhogTunnel
#The one-line form works too. GetGroundhogTunnel returns the api-gateway-tunnel it added:
var apiGateway = builder.AddProject<Projects.ApiGateway>("api-gateway")
.WithReference(serviceA)
.WithReference(serviceB)
.WithGroundhogTunnel("my-tunnel");
var apiGatewayTunnel = apiGateway.GetGroundhogTunnel();
serviceA.WithReference(apiGateway, apiGatewayTunnel);
serviceB.WithReference(apiGateway, apiGatewayTunnel);Aspire then asks for groundhog-my-tunnel-key instead, and you can remove the tunnelKey parameter.
Pass the address as an environment variable instead
#With WithReference(apiGateway, apiGatewayTunnel), every call from service-a or service-b to https://api-gateway goes out through the public address. If those services also call api-gateway directly, keep service discovery local and pass the address as its own setting instead of the last 2 lines:
serviceA.WithEnvironment("PublicGatewayAddress", apiGatewayTunnel.Resource.PublicAddress);
serviceB.WithEnvironment("PublicGatewayAddress", apiGatewayTunnel.Resource.PublicAddress);Read it in service-a with builder.Configuration["PublicGatewayAddress"], which returns https://my-tunnel.groundhog.unifinity.com.
Allow internal service calls through visitor access rules
#Read the public host with forwarded headers
#The api-gateway project receives requests with the public Host, my-tunnel.groundhog.unifinity.com. YARP passes it to service-a and service-b as X-Forwarded-Host and X-Forwarded-Proto by default. For a service to see the public host and https scheme in its own requests, turn on forwarded headers in that service and trust api-gateway.