Skip to content

Latest commit

 

History

History
439 lines (327 loc) · 26.5 KB

File metadata and controls

439 lines (327 loc) · 26.5 KB

AgentBox HTTPS Reverse Proxy Investigation

Date: 2025-07-15 Goal: Find the best Microsoft-ecosystem-aligned HTTPS proxy for AgentBox Context: Ephemeral dev containers on Azure Container Instances (ACI), accessed via code-server + ttyd + SSH. Needs TLS termination, Entra ID auth, WebSocket support, wildcard routing, low cost.


Comparison Table

Option Est. Cost/mo Auto TLS Entra ID Auth WebSocket Complexity Microsoft-native
1. Container Apps (direct) ~$5–15 ✅ Free managed certs ✅ Built-in Easy Auth ✅ Native ⭐ Low ✅ Yes
2. Container Apps as proxy ~$5–10 ✅ Free managed certs ✅ Built-in Easy Auth ✅ Native ⭐⭐ Low-Med ✅ Yes
3. Application Gateway ~$20–40+ ✅ (bring cert or Key Vault) ⚠️ Requires custom integration ✅ Native ⭐⭐⭐ High ✅ Yes
4. Azure Front Door ~$35–50+ ✅ Auto-managed ⚠️ WAF rules only, no Easy Auth ✅ Yes ⭐⭐⭐ High ✅ Yes
5. Azure API Management ~$0–5 (Consumption) ✅ Managed ✅ Entra ID policies ✅ (not on Consumption tier) ⭐⭐⭐ High ✅ Yes
6. Caddy on Container Apps ~$5–10 ✅ Let's Encrypt via Caddy ✅ Easy Auth + forward_auth ✅ Native ⭐⭐ Medium ⚠️ Caddy is OSS
7. Traefik on Container Apps ~$5–10 ✅ Let's Encrypt via Traefik ✅ Easy Auth + ForwardAuth ✅ Native ⭐⭐ Medium ⚠️ Traefik is OSS
8. YARP on Container Apps ~$5–10 ✅ Via Container Apps ✅ Easy Auth or middleware ✅ Yes (.NET) ⭐⭐ Medium ✅ Yes (Microsoft)
9. Static Web Apps + Functions ~$0–9 ✅ Auto-managed ✅ Built-in ❌ No WebSocket proxy ⭐⭐ Medium ✅ Yes
10. ACI direct (no proxy) ~$0 ❌ Self-signed only ❌ None N/A ⭐ Low ✅ Yes

Detailed Findings

1. Azure Container Apps — Run AgentBox Containers Directly

Concept: Skip ACI entirely. Run each AgentBox container as a Container App with built-in ingress, TLS, and Easy Auth.

Findings:

  • Container Apps provides built-in HTTP ingress with automatic TLS termination using free managed certificates (via DigiCert). Supports custom domains and wildcard subdomains via CNAME records. (Source)
  • WebSocket support is native — the ingress explicitly supports "HTTP/1.1, HTTP/2, WebSocket, and gRPC". (Source)
  • Built-in Entra ID authentication (Easy Auth) runs as a sidecar container that intercepts all requests before they reach the app. Supports Microsoft Entra ID, GitHub, Google, and custom OIDC. Can be set to "Require authentication" to block unauthenticated traffic. (Source)
  • Free managed certificates are available for custom domains. Requirement: CNAME must point directly to the Container App (no intermediate CNAME like Cloudflare). Certificates auto-renew. (Source)
  • Custom domains: Supports wildcard DNS via CNAME. Each subdomain (e.g., project1.agentbox.networg.com) can be routed to a different Container App, or a single proxy app can route by hostname.

Cost (Consumption Plan):

  • Free tier: 180,000 vCPU-seconds, 360,000 GiB-seconds, 2M requests/month per subscription.
  • Beyond free tier: ~$0.000024/vCPU-second active, ~$0.000003/GiB-second active.
  • Scale to zero — no charge when idle.
  • For a proxy container (0.25 vCPU, 0.5 GiB) running 24/7: ~$5–8/mo.
  • (Source)

Wildcard routing limitation: Container Apps free managed certs require per-subdomain CNAME validation — no wildcard certificates. You'd need to add each subdomain programmatically via az containerapp hostname bind. Alternatively, use a bring-your-own wildcard certificate.

AgentBox containers on Container Apps directly?

  • Possible but suboptimal. Each AgentBox container runs code-server (port 8080), ttyd (port 7681), and sshd (port 2222). Container Apps supports additional TCP ports (up to 5 per app), so multi-port is feasible.
  • Concern: Container Apps is optimized for HTTP microservices that scale on demand. AgentBox containers are long-running, stateful dev environments. ACI is a better fit for this workload pattern.
  • Verdict: Use Container Apps for the proxy layer, keep ACI for workload containers.

2. Azure Container Apps as Reverse Proxy to ACI

Concept: Run a lightweight reverse proxy container on Container Apps. Container Apps handles TLS + Easy Auth. The proxy routes authenticated requests to ACI containers.

This is the recommended architecture pattern. It combines:

  • Container Apps free managed TLS (or bring-your-own wildcard cert)
  • Container Apps built-in Easy Auth for Entra ID login
  • A simple proxy container (nginx, Caddy, YARP, or custom) that routes to ACI backends
  • Container Apps native WebSocket pass-through

How it works:

  1. User visits https://project1.agentbox.networg.com
  2. Container Apps terminates TLS (free managed cert)
  3. Easy Auth sidecar authenticates user via Entra ID, injects X-MS-CLIENT-PRINCIPAL headers
  4. Request reaches the proxy container
  5. Proxy checks authorization (does this user own this container?) and routes to the ACI container's IP/FQDN
  6. WebSocket connections pass through transparently

Cost: ~$5–10/mo for a single always-on proxy container (0.25 vCPU, 0.5 GiB).


3. Azure Application Gateway

Concept: Azure-native L7 load balancer with TLS termination.

Findings:

  • Full L7 load balancer with SSL/TLS termination, URL-based routing, cookie-based session affinity. (Source)
  • WebSocket: Native support, no configuration needed. Standard HTTP listeners handle WebSocket upgrade transparently. Timeout values must be set higher than ping intervals. (Source)
  • TLS: Supports certificates from Azure Key Vault. No free auto-managed certs — you must provide your own or integrate with Let's Encrypt externally.
  • Entra ID auth: Not built-in. Application Gateway is a network appliance, not an application platform. You'd need to add authentication at the app level or chain with another service. Can integrate with Azure AD via custom WAF rules or by pairing with APIM, but it's not turnkey.
  • Wildcard routing: Supports multi-site listeners with wildcard hostnames (*.agentbox.networg.com).

Cost:

  • V2 SKU: ~$0.246/gateway-hour fixed + ~$0.008/capacity-unit-hour.
  • Minimum ~$18/mo just for the fixed gateway cost (running 24/7).
  • With light traffic: ~$20–30/mo.
  • (Source)

Verdict: Overkill and expensive for this use case. No built-in auth. Better suited for enterprise production workloads.


4. Azure Front Door

Concept: Global CDN + L7 load balancer with WAF and auto-TLS.

Findings:

  • Global edge network with 118+ PoPs, auto-TLS with free managed certificates, WAF integration. (Source)
  • WebSocket: Supported (proxies WebSocket traffic after HTTP upgrade).
  • TLS: Free auto-rotating managed SSL certificates included.
  • Entra ID auth: Not built-in as Easy Auth. Front Door is a CDN/edge service. Auth must be handled at the origin (app layer) or via WAF rules that inspect JWT tokens. No turnkey Entra ID login flow.
  • Wildcard routing: Supports wildcard custom domains.

Cost:

  • Standard tier: $35/mo base fee + $0.083/GB data transfer + $0.009/10K requests.
  • Premium tier: $330/mo base fee (includes WAF).
  • For our low-traffic scenario: $35–40/mo minimum on Standard.
  • (Source)

Verdict: Way too expensive for 5-10 users. Designed for global-scale applications. No built-in auth flow.


5. Azure API Management (APIM)

Concept: API gateway with built-in auth policies.

Findings:

  • Comprehensive API gateway with Entra ID integration, JWT validation policies, rate limiting. (Source)
  • WebSocket: Supported via WebSocket passthrough API, BUT not on the Consumption tier. Requires Developer, Basic, Standard, or Premium tier. (Source)
  • Entra ID auth: Built-in JWT validation policy (validate-jwt). Can validate Entra ID tokens on every request. Very powerful for API scenarios.
  • TLS: Managed TLS on the gateway endpoint.

Cost:

  • Consumption tier: First 1M calls free, then $0.035/10K calls. But no WebSocket support.
  • Developer tier: ~$50/mo (not production-grade, no SLA).
  • Basic v2: Pricing not fully public yet, estimated ~$150+/mo.
  • (Source)

Verdict: WebSocket limitation on Consumption tier is a dealbreaker. Other tiers are far too expensive. APIM is designed for API management, not reverse proxying dev containers.


6. Caddy on Container Apps (Current Plan)

Concept: Caddy reverse proxy running on Container Apps with Easy Auth.

Findings:

  • Caddy provides automatic HTTPS via Let's Encrypt, simple configuration, built-in forward_auth directive for delegating auth decisions.
  • When deployed on Container Apps, you get double TLS options: Container Apps manages the external TLS cert, Caddy handles internal routing.
  • forward_auth integrates well with Easy Auth's /.auth/me endpoint for authorization checks.
  • WebSocket: Caddy handles WebSocket pass-through natively.
  • Caddy is written in Go, lightweight (~30MB binary).

Why the user is hesitant: Caddy is an open-source project by a small company (Dapperware). Not a "Microsoft choice." However, it's production-grade and widely used.

Cost: Same as option 2 — ~$5–10/mo on Container Apps.

Verdict: Technically excellent, but the user wants something more Microsoft-aligned.


7. Traefik on Container Apps

Concept: Traefik reverse proxy as a replacement for Caddy.

Findings:

  • Cloud-native reverse proxy with 3.3B+ downloads and 55K+ GitHub stars. (Source)
  • Has ForwardAuth middleware (equivalent to Caddy's forward_auth).
  • Built-in Let's Encrypt support with multiple ACME challenge providers.
  • Native Docker, Kubernetes, and file-based service discovery.
  • WebSocket: Full WebSocket support (pass-through).
  • More complex configuration than Caddy (YAML/TOML vs Caddyfile).

Microsoft alignment: Traefik is an open-source project by Traefik Labs (French company). No more "Microsoft" than Caddy. Used by many enterprises but not a Microsoft product.

Cost: Same as Caddy — ~$5–10/mo on Container Apps.

Verdict: Powerful but more complex than Caddy, with no Microsoft alignment advantage. Not recommended over Caddy for this use case.


8. YARP (Yet Another Reverse Proxy) on Container Apps ⭐

Concept: Microsoft's own .NET reverse proxy library, running as a small ASP.NET app on Container Apps.

Findings:

  • YARP is a Microsoft-built reverse proxy library for .NET, developed by the ASP.NET team. Used internally at Microsoft for Azure services. (Source)
  • Not a standalone binary — it's a NuGet package (Yarp.ReverseProxy) that you embed in an ASP.NET Core application. You write a small C# program that configures routes and clusters.
  • TLS termination: When deployed on Container Apps, TLS is handled by Container Apps (not YARP). YARP handles HTTP-level routing internally.
  • WebSocket: Fully supported. YARP proxies WebSocket connections as part of standard HTTP upgrade handling. It's built on ASP.NET Core's Kestrel server which has excellent WebSocket support.
  • Entra ID auth: Two approaches:
    1. Use Container Apps Easy Auth (recommended) — YARP sees pre-authenticated requests with identity headers.
    2. Use ASP.NET Core's built-in Microsoft.Identity.Web package for Entra ID authentication directly in the YARP app. This gives you fine-grained authorization middleware in C# — e.g., check if the user owns the container before proxying.
  • Dynamic routing: YARP supports programmatic route configuration. You can dynamically add/remove routes as containers are created/destroyed via an API or config store.
  • Maturity: Production-grade. Version 2.x. Used in Azure App Service, Azure Active Directory, and other Microsoft services.

Architecture with YARP:

User → Container Apps (TLS + Easy Auth) → YARP (.NET app)
  → Reads X-MS-CLIENT-PRINCIPAL headers
  → Checks authorization (user owns container?)
  → Proxies to ACI container IP:port
  → WebSocket pass-through works natively

Sample YARP proxy (minimal):

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()
    .LoadFromMemory(routes, clusters); // dynamic config
var app = builder.Build();
app.MapReverseProxy();
app.Run();

Cost: Same as other Container Apps options — ~$5–10/mo.

Verdict: Best Microsoft-native option. Gives you C# authorization logic, native .NET ecosystem, and Microsoft's own engineering. Slightly more code than Caddy's Caddyfile, but much more flexible for per-container authorization.


9. Azure Static Web Apps + Azure Functions Proxy

Concept: Use SWA for the dashboard, Azure Functions to proxy traffic to ACI.

Findings:

  • Static Web Apps has built-in Entra ID auth (Microsoft identity platform). Pre-configured, no setup needed. (Source)
  • Azure Functions linked to SWA can serve as API endpoints.
  • WebSocket: Dealbreaker. Azure Functions does not support WebSocket connections. Functions are HTTP request/response only (triggers and bindings limited to HTTP). There is no way to proxy a long-lived WebSocket connection through a Function. (Source)
  • SWA + Functions is great for the management dashboard but cannot proxy code-server/ttyd traffic.

Cost: Free tier for SWA. Functions consumption: effectively free for low volume.

Verdict: Use for the dashboard/management UI, not for the container proxy. WebSocket support is completely absent.


10. ACI Direct — Skip the Proxy Entirely

Concept: Each ACI container gets a public IP with a DNS label. Terminate TLS directly in the container.

Findings:

  • ACI assigns public IPs with FQDN labels like my-project.westeurope.azurecontainer.io. (Source)
  • ACI does NOT support managed TLS certificates. You must handle TLS yourself.
  • Microsoft documents a sidecar pattern where an nginx container handles TLS within the ACI container group, using self-signed or manually-provided certificates. (Source)
  • No wildcard DNS — each container gets its own *.azurecontainer.io subdomain.
  • No built-in auth — you'd have to implement authentication in each container.
  • Let's Encrypt in ACI: Technically possible with Caddy or certbot as a sidecar, but operationally painful (certificate storage, DNS challenges, renewal coordination).

Cost: $0 additional (already paying for ACI compute).

Verdict: Not viable. No auth, no managed TLS, no unified domain. Every container is its own island.


WebSocket Support Deep-Dive

WebSocket support is non-negotiable for AgentBox. Both code-server and ttyd rely on persistent WebSocket connections for their IDE and terminal interfaces.

Option WebSocket Support Notes
Container Apps ingress ✅ Full Explicitly listed as supported. HTTP/1.1 and HTTP/2.
Application Gateway ✅ Full Native support, all sizes. Set timeout > 20s for ping/pong.
Azure Front Door ✅ Full Proxies WebSocket after HTTP upgrade.
APIM Consumption ❌ No WebSocket API not available on Consumption tier.
APIM Developer+ ✅ Full WebSocket passthrough API. One-to-one client-backend mapping.
Caddy ✅ Full Transparent proxy of WebSocket connections.
Traefik ✅ Full Transparent proxy of WebSocket connections.
YARP (.NET) ✅ Full Built on Kestrel; handles HTTP upgrade natively.
Azure Functions ❌ No HTTP triggers only. No persistent connections.
ACI sidecar (nginx) ✅ Full Nginx/Caddy in sidecar handles WebSocket.

Winner: Any option except APIM Consumption and Azure Functions.


Cost Analysis

Scenario: 5–10 users, ~20 containers active at peak, light traffic (dev tool access, not high-throughput APIs).

Option Fixed Cost Variable Cost Estimated Total/mo
Container Apps proxy (Consumption) ~$0 (free tier covers idle) ~$5–8 for always-on proxy $5–10
Application Gateway V2 ~$18 fixed gateway ~$2–5 capacity units $20–25
Azure Front Door Standard $35 base fee ~$1–3 data/requests $36–40
APIM Consumption $0 ~$0–3 per-call charges $0–3 (but no WS!)
APIM Developer ~$50 fixed $0 $50 (no SLA)
SWA + Functions $0–9 ~$0 $0–9 (no WS!)
ACI direct $0 additional $0 $0 (no auth/TLS)

Cheapest viable options: Container Apps proxy (Consumption plan) at ~$5–10/mo.


Recommendation: Top 3 Options

🥇 #1: YARP on Container Apps (Recommended)

Why: Maximum Microsoft alignment + full control + low cost.

Criterion Score
Microsoft-native ✅ YARP is a Microsoft product, .NET is Microsoft's platform
Entra ID auth ✅ Easy Auth built-in + ASP.NET Microsoft.Identity.Web for fine-grained authZ
WebSocket ✅ Full support via Kestrel
Cost ✅ ~$5–10/mo on Container Apps Consumption
Complexity ⭐⭐ Requires writing a small .NET app (~50–100 lines for basic proxy)
Dynamic routing ✅ Programmatic route config — add/remove ACI backends via API
Authorization ✅ Write C# middleware to check container ownership

Architecture:

                    ┌──────────────────────────────┐
                    │   Azure Container Apps        │
Internet ──HTTPS──▶ │  ┌─────────┐  ┌────────────┐ │     ┌──────────┐
  *.agentbox.       │  │Easy Auth│→ │ YARP (.NET)│─┼────▶│ ACI #1   │
  networg.com       │  │(sidecar)│  │  Proxy App │ │     │code-server│
                    │  └─────────┘  └────────────┘ │     └──────────┘
                    │       TLS + Entra ID          │     ┌──────────┐
                    │       Free managed certs       │────▶│ ACI #2   │
                    └──────────────────────────────┘     └──────────┘

Pros:

  • 100% Microsoft stack (Azure + .NET + YARP)
  • Easy Auth handles Entra ID login — zero auth code for the login flow
  • YARP gives you C# authorization middleware for per-container access control
  • Dynamic routing — programmatically update routes as containers are created/destroyed
  • Can integrate with Azure SDK to query ACI state, verify container ownership
  • Container Apps handles TLS certificates automatically

Cons:

  • Must maintain a small .NET application (vs. Caddy's config-only approach)
  • Slightly steeper learning curve if team is not .NET-familiar

🥈 #2: Container Apps with Built-in Proxy (No Custom Proxy)

Why: Simplest possible architecture. Zero proxy code.

Concept: Run each AgentBox container as a Container App (not ACI). Let Container Apps handle everything — ingress, TLS, auth, routing.

Criterion Score
Microsoft-native ✅ Fully Azure-managed
Entra ID auth ✅ Built-in Easy Auth
WebSocket ✅ Native
Cost ⚠️ ~$10–20/mo (multiple containers, but scale-to-zero helps)
Complexity ⭐ Minimal — pure Azure config, no custom code
Dynamic routing ⚠️ Each container is its own Container App — must manage custom domains per app
Authorization ⚠️ Easy Auth authenticates but doesn't do per-container authZ natively

Trade-offs:

  • Moving workloads from ACI to Container Apps changes the operational model.
  • Each AgentBox instance is a separate Container App with its own custom domain binding.
  • Per-container authorization still requires custom logic (could be a small middleware).
  • Managed cert provisioning per subdomain (no wildcard) adds operational overhead.
  • ACI may be more cost-effective for long-running stateful containers.

🥉 #3: Caddy on Container Apps (Current Plan, Improved)

Why: Battle-tested, simple config, proven WebSocket support.

Criterion Score
Microsoft-native ⚠️ Caddy is OSS (Go), not Microsoft. Container Apps + Easy Auth are Microsoft.
Entra ID auth ✅ Easy Auth + forward_auth directive
WebSocket ✅ Full
Cost ✅ ~$5–10/mo
Complexity ⭐ Very simple Caddyfile config
Dynamic routing ⚠️ Requires config reloads or Caddy API for dynamic upstreams

Why it's still good: If the team values simplicity over Microsoft purity, Caddy is the fastest path to a working system. The "not Microsoft" concern is only about Caddy itself — the hosting (Container Apps), auth (Easy Auth), and TLS (Azure managed certs) are all Microsoft.


Clear Verdict

Replace Caddy with YARP on Azure Container Apps

YARP is the Microsoft-native answer. It's built by the ASP.NET team, used internally at Microsoft, and gives you:

  1. Microsoft ecosystem alignment — .NET + Azure Container Apps + Easy Auth + Azure SDK
  2. Cost — ~$5–10/mo on Container Apps Consumption plan (same as Caddy)
  3. WebSocket — Full support via Kestrel
  4. Entra ID — Easy Auth for login + C# middleware for per-container authorization
  5. Dynamic routing — Programmatically configure routes as containers are created/destroyed
  6. Authorization power — Write real C# logic to check container ownership, shared access, admin roles

What this means for the architecture:

Component Before (Caddy plan) After (YARP plan)
Proxy runtime Caddy (Go) YARP ASP.NET app (C#)
Hosting Container Apps Container Apps (no change)
TLS Container Apps managed certs Container Apps managed certs (no change)
Auth (login) Easy Auth Easy Auth (no change)
Auth (per-container) Caddy forward_auth + custom API YARP middleware (built-in)
Routing config Caddyfile / Caddy API YARP in-memory config / IConfiguration
WebSocket Caddy pass-through YARP/Kestrel pass-through
Backend containers ACI ACI (no change)

The only thing that changes is the proxy process itself — from a Caddy binary with a Caddyfile to a small .NET app with YARP. Everything else in the architecture stays the same.


Appendix: Eliminated Options Summary

Option Reason for elimination
Application Gateway Too expensive (~$20+/mo), no built-in auth, high complexity
Azure Front Door Way too expensive ($35+/mo base), designed for global scale
APIM Consumption No WebSocket support on Consumption tier
APIM Developer+ Too expensive ($50+/mo), wrong tool for this job
Azure Functions proxy No WebSocket support — dealbreaker
ACI direct No managed TLS, no auth, no unified domain
Traefik No advantage over Caddy, more complex config, also not Microsoft

Sources