Skip to content

Commit ab35c37

Browse files
committed
tcp ingress?
1 parent d79a403 commit ab35c37

File tree

1 file changed

+187
-0
lines changed

1 file changed

+187
-0
lines changed
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
---
2+
name: TCP Ingress Implementation
3+
overview: Add TCP ingress support to Hypeman using Caddy Layer 4 for TLS+SNI-based routing. TCP ingress will be separate from HTTP ingress, require TLS, and support pattern-based hostname routing to VM instances.
4+
todos:
5+
- id: caddy-l4-build
6+
content: Add caddy-l4 module to Makefile and rebuild Caddy binary
7+
status: pending
8+
- id: tcp-types
9+
content: Create TCP ingress types, validation, and storage (tcp_types.go, tcp_storage.go)
10+
status: pending
11+
- id: l4-config
12+
content: Implement L4 config generation with SNI pattern routing (tcp_config.go)
13+
status: pending
14+
- id: port-validation
15+
content: Add port conflict detection and reserved ports support (ports.go)
16+
status: pending
17+
- id: manager
18+
content: Extend manager with TCP ingress CRUD operations
19+
status: pending
20+
- id: openapi
21+
content: Add TCP ingress schemas and endpoints to openapi.yaml, regenerate
22+
status: pending
23+
- id: api-handlers
24+
content: Create TCP ingress API handlers (tcp_ingress.go)
25+
status: pending
26+
---
27+
28+
# TCP Ingress Implementation Plan
29+
30+
## Summary
31+
32+
Add TCP ingress support using Caddy L4 module. TCP ingresses require TLS (for SNI-based routing), run on separate ports from HTTP ingresses, and use the same pattern routing (`{instance}.example.com`) as HTTP ingresses.
33+
34+
## Architecture
35+
36+
```mermaid
37+
flowchart TB
38+
subgraph external [External Traffic]
39+
HTTP[HTTP/S :80/:443]
40+
TCP[TCP+TLS :22/:5432/etc]
41+
end
42+
43+
subgraph caddy [Caddy]
44+
HTTPApp[HTTP App]
45+
L4App[Layer4 App]
46+
end
47+
48+
subgraph internal [Internal]
49+
DNS[DNS Server]
50+
VMs[VM Instances]
51+
end
52+
53+
HTTP --> HTTPApp
54+
TCP --> L4App
55+
HTTPApp --> DNS
56+
L4App --> DNS
57+
DNS --> VMs
58+
```
59+
60+
**Key constraint:** A port can be owned by either HTTP ingress OR TCP ingress, not both. Reserved ports (hypeman API, host SSH) are blocked from both.
61+
62+
## Implementation
63+
64+
### Phase 1: Caddy L4 Module
65+
66+
Add `caddy-l4` to the custom Caddy build in [`Makefile`](hypeman/Makefile):
67+
68+
```makefile
69+
CADDY_DNS_MODULES := --with github.com/caddy-dns/cloudflare
70+
CADDY_L4_MODULES := --with github.com/mholt/caddy-l4
71+
```
72+
73+
Rebuild Caddy binaries and update embedded binary hash.
74+
75+
### Phase 2: Types and Storage
76+
77+
Create [`lib/ingress/tcp_types.go`](hypeman/lib/ingress/tcp_types.go) with:
78+
79+
```go
80+
type TCPIngress struct {
81+
ID string `json:"id"`
82+
Name string `json:"name"`
83+
ListenPort int `json:"listen_port"`
84+
Rules []TCPIngressRule `json:"rules"`
85+
CreatedAt time.Time `json:"created_at"`
86+
}
87+
88+
type TCPIngressRule struct {
89+
Match TCPIngressMatch `json:"match"`
90+
Target TCPIngressTarget `json:"target"`
91+
}
92+
93+
type TCPIngressMatch struct {
94+
SNI string `json:"sni"` // e.g., "{instance}.example.com" or "mydb.example.com"
95+
}
96+
97+
type TCPIngressTarget struct {
98+
Instance string `json:"instance"` // e.g., "{instance}" or "my-postgres-vm"
99+
Port int `json:"port"` // e.g., 22, 5432
100+
}
101+
```
102+
103+
Storage in [`lib/ingress/tcp_storage.go`](hypeman/lib/ingress/tcp_storage.go):
104+
105+
- Path: `{dataDir}/tcp-ingresses/{id}.json`
106+
- Same pattern as HTTP ingress storage
107+
108+
### Phase 3: Config Generator
109+
110+
Extend [`lib/ingress/config.go`](hypeman/lib/ingress/config.go):
111+
112+
1. Add `buildL4Config(tcpIngresses []TCPIngress)` method
113+
2. Modify `GenerateConfig()` to include both `http` and `layer4` apps
114+
3. For pattern SNI matching, use `sni_regexp` with captures:
115+
116+
- Pattern `{instance}.example.com` becomes regex `^(.+)\.example\.com$`
117+
- Dial uses `{tls.regexp.1}.hypeman.internal:port`
118+
119+
Example L4 config output:
120+
121+
```json
122+
{
123+
"apps": {
124+
"layer4": {
125+
"servers": {
126+
"tcp-22": {
127+
"listen": ["0.0.0.0:22"],
128+
"routes": [{
129+
"match": [{"tls": {"sni_regexp": {"pattern": "^(.+)\\.example\\.com$"}}}],
130+
"handle": [
131+
{"handler": "tls"},
132+
{"handler": "proxy", "upstreams": [{"dial": ["{tls.regexp.1}.hypeman.internal:22"]}]}
133+
]
134+
}]
135+
}
136+
}
137+
}
138+
}
139+
}
140+
```
141+
142+
143+
144+
### Phase 4: Manager Extension
145+
146+
Extend [`lib/ingress/manager.go`](hypeman/lib/ingress/manager.go) or create new `tcp_manager.go`:
147+
148+
1. Add `TCPManager` interface with `Create`, `Get`, `List`, `Delete`
149+
2. Port conflict validation:
150+
151+
- Check against existing HTTP ingress ports
152+
- Check against existing TCP ingress ports
153+
- Check against reserved ports (configurable list)
154+
155+
3. Pattern validation (reuse `ParsePattern` from HTTP ingress)
156+
4. Instance validation for literal targets
157+
158+
### Phase 5: API Endpoints
159+
160+
Add to [`openapi.yaml`](hypeman/openapi.yaml):
161+
162+
- `GET /tcp-ingresses` - List all TCP ingresses
163+
- `POST /tcp-ingresses` - Create TCP ingress
164+
- `GET /tcp-ingresses/{id}` - Get TCP ingress by ID/name
165+
- `DELETE /tcp-ingresses/{id}` - Delete TCP ingress
166+
167+
Create [`cmd/api/api/tcp_ingress.go`](hypeman/cmd/api/api/tcp_ingress.go) with handlers.
168+
169+
### Phase 6: Reserved Ports
170+
171+
Add configuration for reserved ports in [`cmd/api/config/config.go`](hypeman/cmd/api/config/config.go):
172+
173+
```go
174+
// RESERVED_PORTS environment variable (comma-separated)
175+
// Default: "2019,2222" (Caddy admin, host SSH)
176+
ReservedPorts []int
177+
```
178+
179+
Validate in both HTTP and TCP ingress creation.
180+
181+
## Files to Create/Modify
182+
183+
| File | Action | Description ||------|--------|-------------|| `Makefile` | Modify | Add caddy-l4 to build || `lib/ingress/binaries*.go` | Modify | Update embedded binary refs after rebuild || `lib/ingress/tcp_types.go` | Create | TCP ingress types and validation || `lib/ingress/tcp_storage.go` | Create | TCP ingress persistence || `lib/ingress/tcp_config.go` | Create | L4 config generation || `lib/ingress/manager.go` | Modify | Add TCP ingress management || `lib/ingress/ports.go` | Create | Port conflict and reservation logic || `openapi.yaml` | Modify | Add TCP ingress schemas and endpoints || `lib/oapi/oapi.go` | Regenerate | After openapi.yaml changes || `cmd/api/api/tcp_ingress.go` | Create | API handlers || `cmd/api/api/api.go` | Modify | Wire up TCP ingress manager || `cmd/api/config/config.go` | Modify | Add RESERVED_PORTS config |
184+
185+
## Testing Strategy
186+
187+
1. Unit tests for pattern parsing (`{instance}.example.com` → regex)

0 commit comments

Comments
 (0)