mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-21 18:11:27 -07:00
update skills
This commit is contained in:
129
.agents/skills/cloudflare-deploy/references/tunnel/README.md
Normal file
129
.agents/skills/cloudflare-deploy/references/tunnel/README.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# Cloudflare Tunnel
|
||||
|
||||
Secure outbound-only connections between infrastructure and Cloudflare's global network.
|
||||
|
||||
## Overview
|
||||
|
||||
Cloudflare Tunnel (formerly Argo Tunnel) enables:
|
||||
- **Outbound-only connections** - No inbound ports or firewall changes
|
||||
- **Public hostname routing** - Expose local services to internet
|
||||
- **Private network access** - Connect internal networks via WARP
|
||||
- **Zero Trust integration** - Built-in access policies
|
||||
|
||||
**Architecture**: Tunnel (persistent object) → Replica (`cloudflared` process) → Origin services
|
||||
|
||||
**Terminology:**
|
||||
- **Tunnel**: Named persistent object with UUID
|
||||
- **Replica**: Individual `cloudflared` process connected to tunnel
|
||||
- **Config Source**: Where ingress rules stored (local file vs Cloudflare dashboard)
|
||||
- **Connector**: Legacy term for replica
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Local Config
|
||||
```bash
|
||||
# Install cloudflared
|
||||
brew install cloudflared # macOS
|
||||
|
||||
# Authenticate
|
||||
cloudflared tunnel login
|
||||
|
||||
# Create tunnel
|
||||
cloudflared tunnel create my-tunnel
|
||||
|
||||
# Route DNS
|
||||
cloudflared tunnel route dns my-tunnel app.example.com
|
||||
|
||||
# Run tunnel
|
||||
cloudflared tunnel run my-tunnel
|
||||
```
|
||||
|
||||
### Dashboard Config (Recommended)
|
||||
1. **Zero Trust** > **Networks** > **Tunnels** > **Create**
|
||||
2. Name tunnel, copy token
|
||||
3. Configure routes in dashboard
|
||||
4. Run: `cloudflared tunnel --no-autoupdate run --token <TOKEN>`
|
||||
|
||||
## Decision Tree
|
||||
|
||||
**Choose config source:**
|
||||
```
|
||||
Need centralized config updates?
|
||||
├─ Yes → Token-based (dashboard config)
|
||||
└─ No → Local config file
|
||||
|
||||
Multiple environments (dev/staging/prod)?
|
||||
├─ Yes → Local config (version controlled)
|
||||
└─ No → Either works
|
||||
|
||||
Need firewall approval?
|
||||
└─ See networking.md first
|
||||
```
|
||||
|
||||
## Core Commands
|
||||
|
||||
```bash
|
||||
# Tunnel lifecycle
|
||||
cloudflared tunnel create <name>
|
||||
cloudflared tunnel list
|
||||
cloudflared tunnel info <name>
|
||||
cloudflared tunnel delete <name>
|
||||
|
||||
# DNS routing
|
||||
cloudflared tunnel route dns <tunnel> <hostname>
|
||||
cloudflared tunnel route list
|
||||
|
||||
# Private network
|
||||
cloudflared tunnel route ip add 10.0.0.0/8 <tunnel>
|
||||
|
||||
# Run tunnel
|
||||
cloudflared tunnel run <name>
|
||||
```
|
||||
|
||||
## Configuration Example
|
||||
|
||||
```yaml
|
||||
# ~/.cloudflared/config.yml
|
||||
tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
|
||||
credentials-file: /root/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
|
||||
|
||||
ingress:
|
||||
- hostname: app.example.com
|
||||
service: http://localhost:8000
|
||||
- hostname: api.example.com
|
||||
service: https://localhost:8443
|
||||
originRequest:
|
||||
noTLSVerify: true
|
||||
- service: http_status:404
|
||||
```
|
||||
|
||||
## Reading Order
|
||||
|
||||
**New to Cloudflare Tunnel:**
|
||||
1. This README (overview, quick start)
|
||||
2. [networking.md](./networking.md) - Firewall rules, connectivity pre-checks
|
||||
3. [configuration.md](./configuration.md) - Config file options, ingress rules
|
||||
4. [patterns.md](./patterns.md) - Docker, Kubernetes, production deployment
|
||||
5. [gotchas.md](./gotchas.md) - Troubleshooting, best practices
|
||||
|
||||
**Enterprise deployment:**
|
||||
1. [networking.md](./networking.md) - Corporate firewall requirements
|
||||
2. [gotchas.md](./gotchas.md) - HA setup, security best practices
|
||||
3. [patterns.md](./patterns.md) - Kubernetes, rolling updates
|
||||
|
||||
**Programmatic control:**
|
||||
1. [api.md](./api.md) - REST API, TypeScript SDK
|
||||
|
||||
## In This Reference
|
||||
|
||||
- [networking.md](./networking.md) - Firewall rules, ports, connectivity pre-checks
|
||||
- [configuration.md](./configuration.md) - Config file options, ingress rules, TLS settings
|
||||
- [api.md](./api.md) - REST API, TypeScript SDK, token-based tunnels
|
||||
- [patterns.md](./patterns.md) - Docker, Kubernetes, Terraform, HA, use cases
|
||||
- [gotchas.md](./gotchas.md) - Troubleshooting, limitations, best practices
|
||||
|
||||
## See Also
|
||||
|
||||
- [workers](../workers/) - Workers with Tunnel integration
|
||||
- [access](../access/) - Zero Trust access policies
|
||||
- [warp](../warp/) - WARP client for private networks
|
||||
193
.agents/skills/cloudflare-deploy/references/tunnel/api.md
Normal file
193
.agents/skills/cloudflare-deploy/references/tunnel/api.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# Tunnel API
|
||||
|
||||
## Cloudflare API Access
|
||||
|
||||
**Base URL**: `https://api.cloudflare.com/client/v4`
|
||||
|
||||
**Authentication**:
|
||||
```bash
|
||||
Authorization: Bearer ${CF_API_TOKEN}
|
||||
```
|
||||
|
||||
## TypeScript SDK
|
||||
|
||||
Install: `npm install cloudflare`
|
||||
|
||||
```typescript
|
||||
import Cloudflare from 'cloudflare';
|
||||
|
||||
const cf = new Cloudflare({
|
||||
apiToken: process.env.CF_API_TOKEN,
|
||||
});
|
||||
|
||||
const accountId = process.env.CF_ACCOUNT_ID;
|
||||
```
|
||||
|
||||
## Create Tunnel
|
||||
|
||||
### cURL
|
||||
```bash
|
||||
curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data '{
|
||||
"name": "my-tunnel",
|
||||
"tunnel_secret": "<base64-secret>"
|
||||
}'
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
```typescript
|
||||
const tunnel = await cf.zeroTrust.tunnels.create({
|
||||
account_id: accountId,
|
||||
name: 'my-tunnel',
|
||||
tunnel_secret: Buffer.from(crypto.randomBytes(32)).toString('base64'),
|
||||
});
|
||||
|
||||
console.log(`Tunnel ID: ${tunnel.id}`);
|
||||
```
|
||||
|
||||
## List Tunnels
|
||||
|
||||
### cURL
|
||||
```bash
|
||||
curl -X GET "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}"
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
```typescript
|
||||
const tunnels = await cf.zeroTrust.tunnels.list({
|
||||
account_id: accountId,
|
||||
});
|
||||
|
||||
for (const tunnel of tunnels.result) {
|
||||
console.log(`${tunnel.name}: ${tunnel.id}`);
|
||||
}
|
||||
```
|
||||
|
||||
## Get Tunnel Info
|
||||
|
||||
### cURL
|
||||
```bash
|
||||
curl -X GET "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}"
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
```typescript
|
||||
const tunnel = await cf.zeroTrust.tunnels.get(tunnelId, {
|
||||
account_id: accountId,
|
||||
});
|
||||
|
||||
console.log(`Status: ${tunnel.status}`);
|
||||
console.log(`Connections: ${tunnel.connections?.length || 0}`);
|
||||
```
|
||||
|
||||
## Update Tunnel Config
|
||||
|
||||
### cURL
|
||||
```bash
|
||||
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}/configurations" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data '{
|
||||
"config": {
|
||||
"ingress": [
|
||||
{"hostname": "app.example.com", "service": "http://localhost:8000"},
|
||||
{"service": "http_status:404"}
|
||||
]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
```typescript
|
||||
const config = await cf.zeroTrust.tunnels.configurations.update(
|
||||
tunnelId,
|
||||
{
|
||||
account_id: accountId,
|
||||
config: {
|
||||
ingress: [
|
||||
{ hostname: 'app.example.com', service: 'http://localhost:8000' },
|
||||
{ service: 'http_status:404' },
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## Delete Tunnel
|
||||
|
||||
### cURL
|
||||
```bash
|
||||
curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}"
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
```typescript
|
||||
await cf.zeroTrust.tunnels.delete(tunnelId, {
|
||||
account_id: accountId,
|
||||
});
|
||||
```
|
||||
|
||||
## Token-Based Tunnels (Config Source: Cloudflare)
|
||||
|
||||
Token-based tunnels store config in Cloudflare dashboard instead of local files.
|
||||
|
||||
### Via Dashboard
|
||||
1. **Zero Trust** > **Networks** > **Tunnels**
|
||||
2. **Create a tunnel** > **Cloudflared**
|
||||
3. Configure routes in dashboard
|
||||
4. Copy token
|
||||
5. Run on origin:
|
||||
```bash
|
||||
cloudflared service install <TOKEN>
|
||||
```
|
||||
|
||||
### Via Token
|
||||
```bash
|
||||
# Run with token (no config file needed)
|
||||
cloudflared tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
|
||||
|
||||
# Docker
|
||||
docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
|
||||
```
|
||||
|
||||
### Get Tunnel Token (TypeScript)
|
||||
```typescript
|
||||
// Get tunnel to retrieve token
|
||||
const tunnel = await cf.zeroTrust.tunnels.get(tunnelId, {
|
||||
account_id: accountId,
|
||||
});
|
||||
|
||||
// Token available in tunnel.token (only for config source: cloudflare)
|
||||
const token = tunnel.token;
|
||||
```
|
||||
|
||||
## DNS Routes API
|
||||
|
||||
```bash
|
||||
# Create DNS route
|
||||
curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}/connections" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||
--data '{"hostname": "app.example.com"}'
|
||||
|
||||
# Delete route
|
||||
curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}/connections/{route_id}" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}"
|
||||
```
|
||||
|
||||
## Private Network Routes API
|
||||
|
||||
```bash
|
||||
# Add IP route
|
||||
curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}/routes" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||
--data '{"ip_network": "10.0.0.0/8"}'
|
||||
|
||||
# List IP routes
|
||||
curl -X GET "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}/routes" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}"
|
||||
```
|
||||
@@ -0,0 +1,157 @@
|
||||
# Tunnel Configuration
|
||||
|
||||
## Config Source
|
||||
|
||||
Tunnels use one of two config sources:
|
||||
|
||||
| Config Source | Storage | Updates | Use Case |
|
||||
|---------------|---------|---------|----------|
|
||||
| Local | `config.yml` file | Edit file, restart | Dev, multi-env, version control |
|
||||
| Cloudflare | Dashboard/API | Instant, no restart | Production, centralized management |
|
||||
|
||||
**Token-based tunnels** = config source: Cloudflare
|
||||
**Locally-managed tunnels** = config source: local
|
||||
|
||||
## Config File Location
|
||||
|
||||
```
|
||||
~/.cloudflared/config.yml # User config
|
||||
/etc/cloudflared/config.yml # System-wide (Linux)
|
||||
```
|
||||
|
||||
## Basic Structure
|
||||
|
||||
```yaml
|
||||
tunnel: <UUID>
|
||||
credentials-file: /path/to/<UUID>.json
|
||||
|
||||
ingress:
|
||||
- hostname: app.example.com
|
||||
service: http://localhost:8000
|
||||
- service: http_status:404 # Required catch-all
|
||||
```
|
||||
|
||||
## Ingress Rules
|
||||
|
||||
Rules evaluated **top to bottom**, first match wins.
|
||||
|
||||
```yaml
|
||||
ingress:
|
||||
# Exact hostname + path regex
|
||||
- hostname: static.example.com
|
||||
path: \.(jpg|png|css|js)$
|
||||
service: https://localhost:8001
|
||||
|
||||
# Wildcard hostname
|
||||
- hostname: "*.example.com"
|
||||
service: https://localhost:8002
|
||||
|
||||
# Path only (all hostnames)
|
||||
- path: /api/.*
|
||||
service: http://localhost:9000
|
||||
|
||||
# Catch-all (required)
|
||||
- service: http_status:404
|
||||
```
|
||||
|
||||
**Validation**:
|
||||
```bash
|
||||
cloudflared tunnel ingress validate
|
||||
cloudflared tunnel ingress rule https://foo.example.com
|
||||
```
|
||||
|
||||
## Service Types
|
||||
|
||||
| Protocol | Format | Client Requirement |
|
||||
|----------|--------|-------------------|
|
||||
| HTTP | `http://localhost:8000` | Browser |
|
||||
| HTTPS | `https://localhost:8443` | Browser |
|
||||
| TCP | `tcp://localhost:2222` | `cloudflared access tcp` |
|
||||
| SSH | `ssh://localhost:22` | `cloudflared access ssh` |
|
||||
| RDP | `rdp://localhost:3389` | `cloudflared access rdp` |
|
||||
| Unix | `unix:/path/to/socket` | Browser |
|
||||
| Test | `hello_world` | Browser |
|
||||
|
||||
## Origin Configuration
|
||||
|
||||
### Connection Settings
|
||||
```yaml
|
||||
originRequest:
|
||||
connectTimeout: 30s
|
||||
tlsTimeout: 10s
|
||||
tcpKeepAlive: 30s
|
||||
keepAliveTimeout: 90s
|
||||
keepAliveConnections: 100
|
||||
```
|
||||
|
||||
### TLS Settings
|
||||
```yaml
|
||||
originRequest:
|
||||
noTLSVerify: true # Disable cert verification
|
||||
originServerName: "app.internal" # Override SNI
|
||||
caPool: /path/to/ca.pem # Custom CA
|
||||
```
|
||||
|
||||
### HTTP Settings
|
||||
```yaml
|
||||
originRequest:
|
||||
disableChunkedEncoding: true
|
||||
httpHostHeader: "app.internal"
|
||||
http2Origin: true
|
||||
```
|
||||
|
||||
## Private Network Mode
|
||||
|
||||
```yaml
|
||||
tunnel: <UUID>
|
||||
credentials-file: /path/to/creds.json
|
||||
|
||||
warp-routing:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
```bash
|
||||
cloudflared tunnel route ip add 10.0.0.0/8 my-tunnel
|
||||
cloudflared tunnel route ip add 192.168.1.100/32 my-tunnel
|
||||
```
|
||||
|
||||
## Config Source Comparison
|
||||
|
||||
### Local Config
|
||||
```yaml
|
||||
# config.yml
|
||||
tunnel: <UUID>
|
||||
credentials-file: /path/to/<UUID>.json
|
||||
|
||||
ingress:
|
||||
- hostname: app.example.com
|
||||
service: http://localhost:8000
|
||||
- service: http_status:404
|
||||
```
|
||||
|
||||
```bash
|
||||
cloudflared tunnel run my-tunnel
|
||||
```
|
||||
|
||||
**Pros:** Version control, multi-environment, offline edits
|
||||
**Cons:** Requires file distribution, manual restarts
|
||||
|
||||
### Cloudflare Config (Token-Based)
|
||||
```bash
|
||||
# No config file needed
|
||||
cloudflared tunnel --no-autoupdate run --token <TOKEN>
|
||||
```
|
||||
|
||||
Configure routes in dashboard: **Zero Trust** > **Networks** > **Tunnels** > [Tunnel] > **Public Hostname**
|
||||
|
||||
**Pros:** Centralized updates, no file management, instant route changes
|
||||
**Cons:** Requires dashboard/API access, less portable
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
TUNNEL_TOKEN=<token> # Token for config source: cloudflare
|
||||
TUNNEL_ORIGIN_CERT=/path/to/cert.pem # Override cert path (local config)
|
||||
NO_AUTOUPDATE=true # Disable auto-updates
|
||||
TUNNEL_LOGLEVEL=debug # Log level
|
||||
```
|
||||
147
.agents/skills/cloudflare-deploy/references/tunnel/gotchas.md
Normal file
147
.agents/skills/cloudflare-deploy/references/tunnel/gotchas.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# Tunnel Gotchas
|
||||
|
||||
## Common Errors
|
||||
|
||||
### "Error 1016 (Origin DNS Error)"
|
||||
|
||||
**Cause:** Tunnel not running or not connected
|
||||
**Solution:**
|
||||
```bash
|
||||
cloudflared tunnel info my-tunnel # Check status
|
||||
ps aux | grep cloudflared # Verify running
|
||||
journalctl -u cloudflared -n 100 # Check logs
|
||||
```
|
||||
|
||||
### "Self-signed certificate rejected"
|
||||
|
||||
**Cause:** Origin using self-signed certificate
|
||||
**Solution:**
|
||||
```yaml
|
||||
originRequest:
|
||||
noTLSVerify: true # Dev only
|
||||
caPool: /path/to/ca.pem # Custom CA
|
||||
```
|
||||
|
||||
### "Connection timeout"
|
||||
|
||||
**Cause:** Origin slow to respond or timeout settings too low
|
||||
**Solution:**
|
||||
```yaml
|
||||
originRequest:
|
||||
connectTimeout: 60s
|
||||
tlsTimeout: 20s
|
||||
keepAliveTimeout: 120s
|
||||
```
|
||||
|
||||
### "Tunnel not starting"
|
||||
|
||||
**Cause:** Invalid config, missing credentials, or tunnel doesn't exist
|
||||
**Solution:**
|
||||
```bash
|
||||
cloudflared tunnel ingress validate # Validate config
|
||||
ls -la ~/.cloudflared/*.json # Verify credentials
|
||||
cloudflared tunnel list # Verify tunnel exists
|
||||
```
|
||||
|
||||
### "Connection already registered"
|
||||
|
||||
**Cause:** Multiple replicas with same connector ID or stale connection
|
||||
**Solution:**
|
||||
```bash
|
||||
# Check active connections
|
||||
cloudflared tunnel info my-tunnel
|
||||
|
||||
# Wait 60s for stale connection cleanup, or restart with new connector ID
|
||||
cloudflared tunnel run my-tunnel
|
||||
```
|
||||
|
||||
### "Tunnel credentials rotated but connections fail"
|
||||
|
||||
**Cause:** Old cloudflared processes using expired credentials
|
||||
**Solution:**
|
||||
```bash
|
||||
# Stop all cloudflared processes
|
||||
pkill cloudflared
|
||||
|
||||
# Verify stopped
|
||||
ps aux | grep cloudflared
|
||||
|
||||
# Restart with new credentials
|
||||
cloudflared tunnel run my-tunnel
|
||||
```
|
||||
|
||||
## Limits
|
||||
|
||||
| Resource/Limit | Value | Notes |
|
||||
|----------------|-------|-------|
|
||||
| Free tier | Unlimited tunnels | Unlimited traffic |
|
||||
| Tunnel replicas | 1000 per tunnel | Max concurrent |
|
||||
| Connection duration | No hard limit | Hours to days |
|
||||
| Long-lived connections | May drop during updates | WebSocket, SSH, UDP |
|
||||
| Replica registration | ~5s TTL | Old replica dropped after 5s no heartbeat |
|
||||
| Token rotation grace | 24 hours | Old tokens work during grace period |
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Security
|
||||
1. Use token-based tunnels (config source: cloudflare) for centralized control
|
||||
2. Enable Access policies for sensitive services
|
||||
3. Rotate tunnel credentials regularly
|
||||
4. After rotation: stop all old cloudflared processes within 24h grace period
|
||||
5. Verify TLS certs (`noTLSVerify: false`)
|
||||
6. Restrict `bastion` service type
|
||||
|
||||
### Performance
|
||||
1. Run multiple replicas for HA (2-4 typical, load balanced automatically)
|
||||
2. Replicas share same tunnel UUID, get unique connector IDs
|
||||
3. Place `cloudflared` close to origin (same network)
|
||||
4. Use HTTP/2 for gRPC (`http2Origin: true`)
|
||||
5. Tune keepalive for long-lived connections
|
||||
6. Monitor connection counts
|
||||
|
||||
### Configuration
|
||||
1. Use environment variables for secrets
|
||||
2. Version control config files
|
||||
3. Validate before deploying (`cloudflared tunnel ingress validate`)
|
||||
4. Test rules (`cloudflared tunnel ingress rule <URL>`)
|
||||
5. Document rule order (first match wins)
|
||||
|
||||
### Operations
|
||||
1. Monitor tunnel health in dashboard (shows active replicas)
|
||||
2. Set up disconnect alerts (when replica count drops to 0)
|
||||
3. Graceful shutdown for config updates
|
||||
4. Update replicas in rolling fashion (update 1, wait, update next)
|
||||
5. Keep `cloudflared` updated (1 year support window)
|
||||
6. Use `--no-autoupdate` in prod; control updates manually
|
||||
|
||||
## Debug Mode
|
||||
|
||||
```bash
|
||||
cloudflared tunnel --loglevel debug run my-tunnel
|
||||
cloudflared tunnel ingress rule https://app.example.com
|
||||
```
|
||||
|
||||
## Migration Strategies
|
||||
|
||||
### From Ngrok
|
||||
```yaml
|
||||
# Ngrok: ngrok http 8000
|
||||
# Cloudflare Tunnel:
|
||||
ingress:
|
||||
- hostname: app.example.com
|
||||
service: http://localhost:8000
|
||||
- service: http_status:404
|
||||
```
|
||||
|
||||
### From VPN
|
||||
```yaml
|
||||
# Replace VPN with private network routing
|
||||
warp-routing:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
```bash
|
||||
cloudflared tunnel route ip add 10.0.0.0/8 my-tunnel
|
||||
```
|
||||
|
||||
Users install WARP client instead of VPN.
|
||||
168
.agents/skills/cloudflare-deploy/references/tunnel/networking.md
Normal file
168
.agents/skills/cloudflare-deploy/references/tunnel/networking.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# Tunnel Networking
|
||||
|
||||
## Connectivity Requirements
|
||||
|
||||
### Outbound Ports
|
||||
|
||||
Cloudflared requires outbound access on:
|
||||
|
||||
| Port | Protocol | Purpose | Required |
|
||||
|------|----------|---------|----------|
|
||||
| 7844 | TCP/UDP | Primary tunnel protocol (QUIC) | Yes |
|
||||
| 443 | TCP | Fallback (HTTP/2) | Yes |
|
||||
|
||||
**Network path:**
|
||||
```
|
||||
cloudflared → edge.argotunnel.com:7844 (preferred)
|
||||
cloudflared → region.argotunnel.com:443 (fallback)
|
||||
```
|
||||
|
||||
### Firewall Rules
|
||||
|
||||
#### Minimal (Production)
|
||||
```bash
|
||||
# Outbound only
|
||||
ALLOW tcp/udp 7844 to *.argotunnel.com
|
||||
ALLOW tcp 443 to *.argotunnel.com
|
||||
```
|
||||
|
||||
#### Full (Recommended)
|
||||
```bash
|
||||
# Tunnel connectivity
|
||||
ALLOW tcp/udp 7844 to *.argotunnel.com
|
||||
ALLOW tcp 443 to *.argotunnel.com
|
||||
|
||||
# API access (for token-based tunnels)
|
||||
ALLOW tcp 443 to api.cloudflare.com
|
||||
|
||||
# Updates (optional)
|
||||
ALLOW tcp 443 to github.com
|
||||
ALLOW tcp 443 to objects.githubusercontent.com
|
||||
```
|
||||
|
||||
### IP Ranges
|
||||
|
||||
Cloudflare Anycast IPs (tunnel endpoints):
|
||||
```
|
||||
# IPv4
|
||||
198.41.192.0/24
|
||||
198.41.200.0/24
|
||||
|
||||
# IPv6
|
||||
2606:4700::/32
|
||||
```
|
||||
|
||||
**Note:** Use DNS resolution for `*.argotunnel.com` rather than hardcoding IPs. Cloudflare may add edge locations.
|
||||
|
||||
## Pre-Flight Check
|
||||
|
||||
Test connectivity before deploying:
|
||||
|
||||
```bash
|
||||
# Test DNS resolution
|
||||
dig edge.argotunnel.com +short
|
||||
|
||||
# Test port 7844 (QUIC/UDP)
|
||||
nc -zvu edge.argotunnel.com 7844
|
||||
|
||||
# Test port 443 (HTTP/2 fallback)
|
||||
nc -zv edge.argotunnel.com 443
|
||||
|
||||
# Test with cloudflared
|
||||
cloudflared tunnel --loglevel debug run my-tunnel
|
||||
# Look for "Registered tunnel connection"
|
||||
```
|
||||
|
||||
### Common Connectivity Errors
|
||||
|
||||
| Error | Cause | Solution |
|
||||
|-------|-------|----------|
|
||||
| "no such host" | DNS blocked | Allow port 53 UDP/TCP |
|
||||
| "context deadline exceeded" | Port 7844 blocked | Allow UDP/TCP 7844 |
|
||||
| "TLS handshake timeout" | Port 443 blocked | Allow TCP 443, disable SSL inspection |
|
||||
|
||||
## Protocol Selection
|
||||
|
||||
Cloudflared automatically selects protocol:
|
||||
|
||||
| Protocol | Port | Priority | Use Case |
|
||||
|----------|------|----------|----------|
|
||||
| QUIC | 7844 UDP | 1st (preferred) | Low latency, best performance |
|
||||
| HTTP/2 | 443 TCP | 2nd (fallback) | QUIC blocked by firewall |
|
||||
|
||||
**Force HTTP/2 fallback:**
|
||||
```bash
|
||||
cloudflared tunnel --protocol http2 run my-tunnel
|
||||
```
|
||||
|
||||
**Verify active protocol:**
|
||||
```bash
|
||||
cloudflared tunnel info my-tunnel
|
||||
# Shows "connections" with protocol type
|
||||
```
|
||||
|
||||
## Private Network Routing
|
||||
|
||||
### WARP Client Requirements
|
||||
|
||||
Users accessing private IPs via WARP need:
|
||||
|
||||
```bash
|
||||
# Outbound (WARP client)
|
||||
ALLOW udp 500,4500 to 162.159.*.* (IPsec)
|
||||
ALLOW udp 2408 to 162.159.*.* (WireGuard)
|
||||
ALLOW tcp 443 to *.cloudflareclient.com
|
||||
```
|
||||
|
||||
### Split Tunnel Configuration
|
||||
|
||||
Route only private networks through tunnel:
|
||||
|
||||
```yaml
|
||||
# warp-routing config
|
||||
warp-routing:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
```bash
|
||||
# Add specific routes
|
||||
cloudflared tunnel route ip add 10.0.0.0/8 my-tunnel
|
||||
cloudflared tunnel route ip add 172.16.0.0/12 my-tunnel
|
||||
cloudflared tunnel route ip add 192.168.0.0/16 my-tunnel
|
||||
```
|
||||
|
||||
WARP users can access these IPs without VPN.
|
||||
|
||||
## Network Diagnostics
|
||||
|
||||
### Connection Diagnostics
|
||||
|
||||
```bash
|
||||
# Check edge selection and connection health
|
||||
cloudflared tunnel info my-tunnel --output json | jq '.connections[]'
|
||||
|
||||
# Enable metrics endpoint
|
||||
cloudflared tunnel --metrics localhost:9090 run my-tunnel
|
||||
curl localhost:9090/metrics | grep cloudflared_tunnel
|
||||
|
||||
# Test latency
|
||||
curl -w "time_total: %{time_total}\n" -o /dev/null https://myapp.example.com
|
||||
```
|
||||
|
||||
## Corporate Network Considerations
|
||||
|
||||
Cloudflared honors proxy environment variables (`HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`).
|
||||
|
||||
If corporate proxy intercepts TLS, add corporate root CA to system trust store.
|
||||
|
||||
## Bandwidth and Rate Limits
|
||||
|
||||
| Limit | Value | Notes |
|
||||
|-------|-------|-------|
|
||||
| Request size | 100 MB | Single HTTP request |
|
||||
| Upload speed | No hard limit | Governed by network/plan |
|
||||
| Concurrent connections | 1000 per tunnel | Across all replicas |
|
||||
| Requests per second | No limit | Subject to DDoS detection |
|
||||
|
||||
**Large file transfers:**
|
||||
Use R2 or Workers with chunked uploads instead of streaming through tunnel.
|
||||
192
.agents/skills/cloudflare-deploy/references/tunnel/patterns.md
Normal file
192
.agents/skills/cloudflare-deploy/references/tunnel/patterns.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# Tunnel Patterns
|
||||
|
||||
## Docker Deployment
|
||||
|
||||
### Token-Based (Recommended)
|
||||
```yaml
|
||||
services:
|
||||
cloudflared:
|
||||
image: cloudflare/cloudflared:latest
|
||||
command: tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Local Config
|
||||
```yaml
|
||||
services:
|
||||
cloudflared:
|
||||
image: cloudflare/cloudflared:latest
|
||||
volumes:
|
||||
- ./config.yml:/etc/cloudflared/config.yml:ro
|
||||
- ./credentials.json:/etc/cloudflared/credentials.json:ro
|
||||
command: tunnel run
|
||||
```
|
||||
|
||||
## Kubernetes Deployment
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: cloudflared
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: cloudflared
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: cloudflared
|
||||
spec:
|
||||
containers:
|
||||
- name: cloudflared
|
||||
image: cloudflare/cloudflared:latest
|
||||
args:
|
||||
- tunnel
|
||||
- --no-autoupdate
|
||||
- run
|
||||
- --token
|
||||
- $(TUNNEL_TOKEN)
|
||||
env:
|
||||
- name: TUNNEL_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: tunnel-credentials
|
||||
key: token
|
||||
```
|
||||
|
||||
## High Availability
|
||||
|
||||
```yaml
|
||||
# Same config on multiple servers
|
||||
tunnel: <UUID>
|
||||
credentials-file: /path/to/creds.json
|
||||
|
||||
ingress:
|
||||
- hostname: app.example.com
|
||||
service: http://localhost:8000
|
||||
- service: http_status:404
|
||||
```
|
||||
|
||||
Run same config on multiple machines. Cloudflare automatically load balances. Long-lived connections (WebSocket, SSH) may drop during updates.
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Web Application
|
||||
```yaml
|
||||
ingress:
|
||||
- hostname: myapp.example.com
|
||||
service: http://localhost:3000
|
||||
- service: http_status:404
|
||||
```
|
||||
|
||||
### SSH Access
|
||||
```yaml
|
||||
ingress:
|
||||
- hostname: ssh.example.com
|
||||
service: ssh://localhost:22
|
||||
- service: http_status:404
|
||||
```
|
||||
|
||||
Client: `cloudflared access ssh --hostname ssh.example.com`
|
||||
|
||||
### gRPC Service
|
||||
```yaml
|
||||
ingress:
|
||||
- hostname: grpc.example.com
|
||||
service: http://localhost:50051
|
||||
originRequest:
|
||||
http2Origin: true
|
||||
- service: http_status:404
|
||||
```
|
||||
|
||||
## Infrastructure as Code
|
||||
|
||||
### Terraform
|
||||
|
||||
```hcl
|
||||
resource "random_id" "tunnel_secret" {
|
||||
byte_length = 32
|
||||
}
|
||||
|
||||
resource "cloudflare_tunnel" "app" {
|
||||
account_id = var.cloudflare_account_id
|
||||
name = "app-tunnel"
|
||||
secret = random_id.tunnel_secret.b64_std
|
||||
}
|
||||
|
||||
resource "cloudflare_tunnel_config" "app" {
|
||||
account_id = var.cloudflare_account_id
|
||||
tunnel_id = cloudflare_tunnel.app.id
|
||||
config {
|
||||
ingress_rule {
|
||||
hostname = "app.example.com"
|
||||
service = "http://localhost:8000"
|
||||
}
|
||||
ingress_rule { service = "http_status:404" }
|
||||
}
|
||||
}
|
||||
|
||||
resource "cloudflare_record" "app" {
|
||||
zone_id = var.cloudflare_zone_id
|
||||
name = "app"
|
||||
value = cloudflare_tunnel.app.cname
|
||||
type = "CNAME"
|
||||
proxied = true
|
||||
}
|
||||
|
||||
output "tunnel_token" {
|
||||
value = cloudflare_tunnel.app.tunnel_token
|
||||
sensitive = true
|
||||
}
|
||||
```
|
||||
|
||||
### Pulumi
|
||||
|
||||
```typescript
|
||||
import * as cloudflare from "@pulumi/cloudflare";
|
||||
import * as random from "@pulumi/random";
|
||||
|
||||
const secret = new random.RandomId("secret", { byteLength: 32 });
|
||||
|
||||
const tunnel = new cloudflare.ZeroTrustTunnelCloudflared("tunnel", {
|
||||
accountId: accountId,
|
||||
name: "app-tunnel",
|
||||
secret: secret.b64Std,
|
||||
});
|
||||
|
||||
const config = new cloudflare.ZeroTrustTunnelCloudflaredConfig("config", {
|
||||
accountId: accountId,
|
||||
tunnelId: tunnel.id,
|
||||
config: {
|
||||
ingressRules: [
|
||||
{ hostname: "app.example.com", service: "http://localhost:8000" },
|
||||
{ service: "http_status:404" },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
new cloudflare.Record("dns", {
|
||||
zoneId: zoneId,
|
||||
name: "app",
|
||||
value: tunnel.cname,
|
||||
type: "CNAME",
|
||||
proxied: true,
|
||||
});
|
||||
```
|
||||
|
||||
## Service Installation
|
||||
|
||||
### Linux systemd
|
||||
```bash
|
||||
cloudflared service install
|
||||
systemctl start cloudflared && systemctl enable cloudflared
|
||||
journalctl -u cloudflared -f # Logs
|
||||
```
|
||||
|
||||
### macOS launchd
|
||||
```bash
|
||||
sudo cloudflared service install
|
||||
sudo launchctl start com.cloudflare.cloudflared
|
||||
```
|
||||
Reference in New Issue
Block a user