https://github.com/shirubasoft/aspire-cloudflared
Cloudflare Tunnel resource for Aspire
https://github.com/shirubasoft/aspire-cloudflared
aspire cloudflare cloudflare-tunnels cloudflared
Last synced: 4 months ago
JSON representation
Cloudflare Tunnel resource for Aspire
- Host: GitHub
- URL: https://github.com/shirubasoft/aspire-cloudflared
- Owner: shirubasoft
- License: mit
- Created: 2026-01-01T20:43:55.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2026-01-02T23:14:40.000Z (5 months ago)
- Last Synced: 2026-01-07T14:55:05.167Z (5 months ago)
- Topics: aspire, cloudflare, cloudflare-tunnels, cloudflared
- Language: C#
- Homepage:
- Size: 40 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# Aspire.Cloudflared
A .NET Aspire integration for [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/), enabling you to expose local services to the internet through Cloudflare's network during development and deployment.
## Features
- **Automatic Tunnel Creation**: Tunnels are automatically created via the Cloudflare API during local development
- **DNS Record Management**: DNS CNAME records are automatically created for your hostnames
- **Ingress Configuration**: Tunnel routes are automatically configured to point to your services
- **Aspire Dashboard Integration**: Monitor tunnel status directly in the Aspire dashboard
## Installation
Install the NuGet package in your AppHost project:
```bash
dotnet add package Aspire.Cloudflared
```
Or add it directly to your AppHost `.csproj` file:
```xml
```
## Quick Start
```csharp
using Aspire.Cloudflared;
var builder = DistributedApplication.CreateBuilder(args);
// Add your service (e.g., a simple nginx container)
var webApp = builder.AddContainer("web-app", "nginx", "alpine")
.WithHttpEndpoint(targetPort: 80, name: "http");
// Create a Cloudflare tunnel
var tunnel = builder.AddCloudflareTunnel("my-tunnel");
// Expose your service through the tunnel
webApp.WithCloudflareTunnel(tunnel, hostname: "myapp.example.com");
builder.Build().Run();
```
When you run the application, Aspire will prompt you for:
- **Account ID**: Your Cloudflare account ID
- **API Token**: A Cloudflare API token with the required permissions
## Getting Your Cloudflare Account ID
1. Log in to the [Cloudflare Dashboard](https://dash.cloudflare.com/)
2. Select any domain in your account
3. On the **Overview** page, scroll down to find **Account ID** in the right sidebar
4. Copy the Account ID value
## Creating a Cloudflare API Token
The integration requires a Cloudflare API token with specific permissions to create tunnels and DNS records.
### Step-by-Step Guide
1. Go to the [Cloudflare Dashboard](https://dash.cloudflare.com/)
2. Navigate to **My Profile** → **API Tokens** (or for account tokens: **Manage Account** → **API Tokens**)
3. Click **Create Token**
4. Select **Create Custom Token**
5. Configure the token with the following settings:
### Required Permissions
| Permission | Access Level | Scope | Description |
|------------|--------------|-------|-------------|
| **Account** → **Cloudflare Tunnel** | Edit | Your Account | Create, read, and manage tunnels |
| **Zone** → **Zone** | Read | All zones (or specific zones) | Look up zone IDs for DNS records |
| **Zone** → **DNS** | Edit | All zones (or specific zones) | Create CNAME records for tunnel routes |
### Token Configuration Screenshot Reference
Your custom token configuration should look like this:
```
Token name: Aspire Cloudflared Development
Permissions:
├── Account
│ └── Cloudflare Tunnel: Edit
└── Zone
├── Zone: Read
└── DNS: Edit
Account Resources: Include → Your Account
Zone Resources: Include → All zones (or specific zones you'll use)
```
6. Click **Continue to summary**
7. Review the permissions and click **Create Token**
8. **Copy the token immediately** — it will only be shown once!
### Verifying Your Token
You can verify your token works by running:
```bash
curl "https://api.cloudflare.com/client/v4/user/tokens/verify" \
--header "Authorization: Bearer YOUR_API_TOKEN"
```
A successful response will show:
```json
{
"success": true,
"result": {
"status": "active"
}
}
```
## API Reference
### AddCloudflareTunnel
Creates a Cloudflare tunnel resource that can be used to expose services.
```csharp
IResourceBuilder AddCloudflareTunnel(
this IDistributedApplicationBuilder builder,
string name,
int? metricsPort = null)
```
**Parameters:**
- `name`: The name of the tunnel (used in Cloudflare and as the resource name in Aspire)
- `metricsPort`: Optional port for the cloudflared metrics endpoint
**Required Parameters (prompted at runtime):**
- `{name}-account-id`: Your Cloudflare account ID
- `{name}-api-token`: Your Cloudflare API token with tunnel permissions
### WithCloudflareTunnel
Exposes a resource's endpoint through a Cloudflare tunnel.
```csharp
IResourceBuilder WithCloudflareTunnel(
this IResourceBuilder builder,
IResourceBuilder tunnel,
string hostname,
string endpointName = "http")
where T : IResourceWithEndpoints
```
**Parameters:**
- `tunnel`: The Cloudflare tunnel resource to route through
- `hostname`: The public hostname for this route (e.g., `"api.example.com"`)
- `endpointName`: The name of the endpoint to expose (defaults to `"http"`)
## Complete Example
```csharp
using Aspire.Cloudflared;
var builder = DistributedApplication.CreateBuilder(args);
// Create a Cloudflare tunnel
var tunnel = builder.AddCloudflareTunnel("my-cloudflare-tunnel");
// Example 1: Expose an nginx container
var nginx = builder.AddContainer("hello-world", "nginx", "alpine")
.WithHttpEndpoint(targetPort: 80, name: "http");
nginx.WithCloudflareTunnel(tunnel, hostname: "hello.example.com");
// Example 2: Expose a .NET project
var api = builder.AddProject("api")
.WithHttpEndpoint(port: 5000, name: "http");
api.WithCloudflareTunnel(tunnel, hostname: "api.example.com");
// Example 3: Expose multiple services through the same tunnel
var frontend = builder.AddContainer("frontend", "my-frontend", "latest")
.WithHttpEndpoint(targetPort: 3000, name: "http");
frontend.WithCloudflareTunnel(tunnel, hostname: "app.example.com");
builder.Build().Run();
```
## Deployment Considerations
### ⚠️ Important: Production Deployment
During **local development** (run mode), the integration automatically:
- Creates the tunnel if it doesn't exist
- Retrieves the tunnel token
- Configures DNS records and ingress routes
However, when **deploying to production** (publish mode), you must manually provide the tunnel token:
1. **Create the tunnel** (if not already created during development):
```bash
cloudflared tunnel create my-tunnel-name
```
2. **Get the tunnel token**:
```bash
cloudflared tunnel token my-tunnel-name
```
Or retrieve it from the [Cloudflare Zero Trust Dashboard](https://one.dash.cloudflare.com/) under **Networks** → **Tunnels**.
3. **Configure the token** as an environment variable or secret in your deployment:
- The parameter name will be `{tunnel-name}-tunnel-token`
- For example: `my-cloudflare-tunnel-tunnel-token`
### Why Manual Token for Deployment?
The automatic tunnel creation uses the Cloudflare API with your development credentials. In production:
- You don't want API tokens with broad permissions in your deployment
- Tunnel tokens are scoped specifically to running the tunnel
- This follows the principle of least privilege
## How It Works
### Run Mode (Local Development)
1. **Tunnel Installer** creates/finds the tunnel via Cloudflare API
2. **Tunnel Token** is retrieved and passed to the cloudflared container
3. **Route Installer** creates DNS CNAME records pointing to `{tunnel-id}.cfargotunnel.com`
4. **Ingress Configuration** is updated with routes from hostname to your services
5. **Cloudflared Container** starts and connects to Cloudflare's network
### Publish Mode (Production)
1. **Tunnel Token** must be provided as a parameter
2. **Cloudflared Container** starts with the provided token
3. **DNS and Ingress** should be pre-configured in Cloudflare (from development or manually)
## Troubleshooting
### "Zone Not Found" Error
This means the API token doesn't have access to the domain's zone. Ensure:
- The domain is registered in your Cloudflare account
- The API token has **Zone: Read** permission for that zone
- The zone is active (not pending)
### "Tunnel Token Not Available" Error
The tunnel installer hasn't completed yet. This usually resolves automatically. If it persists:
- Check the Aspire dashboard for installer status
- Verify your API token has **Cloudflare Tunnel: Edit** permission
- Check the logs for API errors
### DNS Record Already Exists
This is normal if you've run the application before. The integration will skip creating duplicate records.
### Tunnel Not Connecting
- Verify your API token is valid and not expired
- Check that the tunnel token is correct
- Ensure no firewall is blocking outbound connections to Cloudflare
## Security Best Practices
1. **Never commit API tokens** to source control
2. **Use User Secrets** for local development:
```bash
dotnet user-secrets set "Parameters:my-tunnel-api-token" "your-token-here"
dotnet user-secrets set "Parameters:my-tunnel-account-id" "your-account-id"
```
3. **Rotate tokens** if they may have been exposed
4. **Limit token scope** to only the zones you need
5. **Use account-owned tokens** for shared development environments
## License
See [LICENSE](LICENSE) for details.