Skip to content

Commit 2a853d2

Browse files
authored
Support TLS for ingress (#27)
* Swap to caddy and support TLS * Add logging * test pass * check if timing was issue * Raf style AI review - Added `os.Remove(d.paths.CaddyPIDFile())` when `waitForAdmin` times out to prevent stale PID files - Changed `append(redirectRoutes, routes...)` to `slices.Concat(redirectRoutes, routes)` to avoid mutating original slices - Created `DNSProvider` type with constants `DNSProviderNone`, `DNSProviderCloudflare`, `DNSProviderRoute53` - Added `ParseDNSProvider()` function that returns an error for unknown providers - Server now fails to start if an invalid DNS provider is configured - Updated `tls` field description: "Use with match.port=443 for standard HTTPS" - Replaced Envoy references with Caddy - Added `CADDY_STOP_ON_SHUTDOWN` with note to set `false` for production - Added TLS Ingress section documenting ACME configuration for Cloudflare/Route53 - Added all new environment variables to the table - Created `waitForProcessExit()` helper that polls every 100ms - `Stop()` now uses polling instead of `time.Sleep(2 * time.Second)` - Added debug logging when `Process.Kill()` fails in `Stop()` - `TestCreateIngress_TLSWithoutACME` verifies error when TLS requested but ACME not configured - Removed separate `logging` section from Caddy JSON config - Caddy now writes JSON logs to stderr, captured to `caddy.log` by `daemon.go` - Log forwarder now tails `CaddyLogFile()` instead of separate `CaddySystemLog()` - Removed unused `CaddySystemLog()` path method - `TestGenerateConfig_MixedTLSAndNonTLS` verifies correct behavior with both TLS and non-TLS rules in the same ingress - Removed unused `ErrCaddyNotRunning` * More review Summary of Changes 1. Fixed non-deterministic listenAddrs order (`lib/ingress/config.go`) - Ports are now collected and sorted before building listen addresses - Added `TestGenerateConfig_DeterministicOrder` test to verify consistent output 2. Fixed DNS challenge config for Caddy modules (`lib/ingress/config.go`) - Updated `buildDNSChallengeConfig()` to use correct Caddy DNS module format - Consolidated propagation settings to apply to all providers: - `DNS_PROPAGATION_TIMEOUT` - works for both Cloudflare and Route53 - `DNS_RESOLVERS` - custom resolvers for propagation checking 3. Updated Makefile for xcaddy builds - Changed from downloading pre-built Caddy to building with xcaddy - Added `build-caddy` and `build-caddy-binaries` targets - Includes `github.com/caddy-dns/cloudflare` and `github.com/caddy-dns/route53` modules 4. Added test for XDG_DATA_HOME / storage paths (`lib/ingress/config_test.go`) - `TestGenerateConfig_StoragePath` verifies storage configuration is correct - Updated `TestGenerateConfig_EmptyIngresses` to check storage section 5. Used slices.Concat for safer append (`lib/ingress/manager.go`) - Changed `append(existingIngresses, ingress)` to `slices.Concat(existingIngresses, []Ingress{ingress})` 6. Match config path in findCaddyPID (`lib/ingress/daemon.go`) - Now matches both "caddy run" and the specific config path - Prevents collision with other Caddy/hypeman instances 7. Reduced admin API timeout to 10s (`lib/ingress/daemon.go`) - Changed from 30s to 10s for faster failure detection 8. Fixed CaddyStopOnShutdown default (`cmd/api/config/config.go`) - Default is now `false` (Caddy persists through hypeman restarts) - Removed "(default: X)" comments from struct fields 9. Updated .env.example - Added all CADDY_* and ACME_* variables - Documented all three Route53 auth methods 10. AWS Route53 authentication methods (`lib/ingress/config.go`) - **Method 1**: Explicit credentials (`AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY`) - **Method 2**: Named profile (`AWS_PROFILE`) - **Method 3**: IAM role/instance profile (leave credentials empty) * fix build * error handle port taken * 404 hint: didn't match hostname * version check * Fix caddy shutdown * Configurable allowed domains * Domain allow list * Match ingress by partial id and name * Use SNI in test * Tls integration tests passes locally * Add a few logs * Use DNS for discovery and support instance name matching * Accept using sudo for tests, get caching and parallelism * Remove route53 for now * Docker login work when run tests with root * Fix one more spot with unauth'd docker pull * Review Summary of Changes 1. Wildcard & Pattern Matching Logic (`lib/ingress/config.go`) - Added handling for global wildcard `*` pattern that matches any domain - Added comprehensive documentation explaining wildcard behavior - Restructured code for clarity with detailed comments 2. Edge Case Tests (`lib/ingress/config_test.go`) - Added 12 new test cases covering: - Global `*` wildcard matching - Single-char subdomains - Hyphenated and numeric subdomains - Empty prefix handling - Apex domain + wildcard combination - Empty patterns in lists 3. DNS Provider Constants (`lib/ingress/config.go`) - Added `caddyProviderCloudflare` constant for Caddy module name - Added `SupportedDNSProviders()` helper function for future-proof error messages - Updated `ParseDNSProvider()` to use dynamic error message - Updated `buildDNSChallengeConfig()` to use constant and log warnings for unknown providers 4. Context Propagation (`lib/dns/server.go`) - Added `ctx` field to Server struct to store base context - Store context from `Start()` for use in resolver calls - Updated `handleAQuery()` to use stored context instead of `context.Background()` - Improved AAAA comment explaining intentional empty response behavior 5. Duration Validation (`lib/providers/providers.go`) - Added validation for `DNS_PROPAGATION_TIMEOUT` at startup - Validates Go duration format (e.g., `2m`, `120s`, `1h`) - Provides helpful error message with expected format examples 6. Consistency Improvements - **`lib/ingress/manager.go`**: Replaced slice comparison with `strings.HasPrefix()` for ID prefix matching - **`lib/ingress/daemon.go`**: Added documented constants for polling intervals (`adminPollInterval`, `processExitPollInterval`) 7. TLS Documentation (`lib/ingress/README.md`) - Expanded TLS/HTTPS section with: - TLS requirements section - Detailed `TLS_ALLOWED_DOMAINS` documentation with pattern matching table - Wildcard behavior explanation - Example configurations - Warning scenarios documentation - Updated ACME/TLS Settings table with new variables * Address review comments 1. Removed Route53 Documentation (.env.example) - Removed the AWS Route53 DNS provider documentation section since it's not actually implemented - Updated ACME_DNS_PROVIDER comment to only mention cloudflare 2. Changed Default Ports to 0 for Random Assignment - lib/dns/server.go: Changed DefaultPort from 5353 to 0 with a comment explaining this prevents conflicts on shared dev machines - cmd/api/config/config.go: Changed CADDY_ADMIN_PORT default from 2019 to 0 with an explanatory comment - Updated .env.example to reflect the new default 3. Fixed DNS Server Context Handling (lib/dns/server.go) - Removed the stored ctx field from the Server struct - Added a new resolverTimeout constant (5 seconds) - Updated handleAQuery to create a fresh context.Background() with timeout for each DNS query - This ensures DNS queries don't fail if the parent context is cancelled during shutdown 4. Added Comment in lib/ingress/daemon.go - Added a comment explaining why context.Background() is intentionally used for the startup wait (to ensure it isn't cancelled if the parent context times out) 5. Simplified Test Code (lib/instances/manager_test.go) - Removed the confusing if envPath := ...; true construct - Simplified to just the directory walk loop for loading .env files 6. Added Startup Warning (lib/ingress/manager.go) - Added a check in Initialize() that warns if any existing TLS ingress has a hostname not in the allowed domains list - Logs the ingress name, hostname, and allowed domains for easier debugging
1 parent 71e4964 commit 2a853d2

35 files changed

+3541
-1422
lines changed

.air.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ tmp_dir = "tmp"
1616
include_dir = []
1717
include_ext = ["go", "tpl", "tmpl", "html", "yaml"]
1818
include_file = []
19-
kill_delay = "0s"
19+
kill_delay = "5s"
2020
log = "build-errors.log"
2121
poll = false
2222
poll_interval = 0
2323
post_cmd = []
2424
rerun = false
2525
rerun_delay = 500
26-
send_interrupt = false
26+
send_interrupt = true
2727
stop_on_error = false
2828

2929
[color]

.env.example

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,78 @@
1+
# Required
12
JWT_SECRET='your-secret-key-here'
2-
DATA_DIR=/home/your-user/hypeman/.datadir # or leave unset to default to /var/lib/hypeman
3+
4+
# Data directory (default: /var/lib/hypeman)
5+
DATA_DIR=/var/lib/hypeman
6+
7+
# Server configuration
8+
# PORT=8080
9+
10+
# Network configuration
11+
# BRIDGE_NAME=vmbr0
12+
# SUBNET_CIDR=10.100.0.0/16
13+
# SUBNET_GATEWAY= # empty = derived from SUBNET_CIDR
14+
# UPLINK_INTERFACE= # empty = auto-detect from default route
15+
# DNS_SERVER=1.1.1.1
16+
17+
# Logging
18+
# LOG_LEVEL=info # debug, info, warn, error
19+
20+
# Caddy / Ingress configuration
21+
# CADDY_LISTEN_ADDRESS=0.0.0.0
22+
# CADDY_ADMIN_ADDRESS=127.0.0.1
23+
# CADDY_ADMIN_PORT=0 # 0 = random port (prevents conflicts on shared dev machines)
24+
# CADDY_STOP_ON_SHUTDOWN=false # Set to true if you want Caddy to stop when hypeman stops
25+
26+
# =============================================================================
27+
# TLS / ACME Configuration (for HTTPS ingresses)
28+
# =============================================================================
29+
# Required for TLS ingresses:
30+
31+
# ACME_DNS_PROVIDER=cloudflare
32+
33+
# IMPORTANT: You must specify which domains are allowed for TLS certificates.
34+
# This prevents typos and ensures you only request certificates for domains you control.
35+
# TLS_ALLOWED_DOMAINS=*.example.com,api.other.com
36+
# Supports:
37+
# - Exact matches: api.example.com
38+
# - Wildcard subdomains: *.example.com (matches foo.example.com, NOT foo.bar.example.com)
39+
# If not set, no TLS ingresses are allowed.
40+
41+
# Optional ACME settings:
42+
# ACME_CA= # empty = Let's Encrypt production
43+
# Use https://acme-staging-v02.api.letsencrypt.org/directory for testing
44+
45+
# DNS propagation settings (applies to all providers):
46+
# DNS_PROPAGATION_TIMEOUT=2m # Max time to wait for DNS propagation
47+
# DNS_RESOLVERS=1.1.1.1,8.8.8.8 # Custom DNS resolvers for propagation checking
48+
49+
# -----------------------------------------------------------------------------
50+
# Cloudflare DNS Provider (ACME_DNS_PROVIDER=cloudflare)
51+
# -----------------------------------------------------------------------------
52+
# CLOUDFLARE_API_TOKEN=your-api-token
53+
# Token needs Zone:DNS:Edit permissions for the domains you want certificates for
54+
# =============================================================================
55+
# OpenTelemetry Configuration
56+
# =============================================================================
57+
# OTEL_ENABLED=false
58+
# OTEL_ENDPOINT=127.0.0.1:4317
59+
# OTEL_SERVICE_NAME=hypeman
60+
# OTEL_SERVICE_INSTANCE_ID= # default: hostname
61+
# OTEL_INSECURE=true
62+
# ENV=dev # deployment environment
63+
64+
# =============================================================================
65+
# Resource Limits
66+
# =============================================================================
67+
# Per-instance limits
68+
# MAX_VCPUS_PER_INSTANCE=16
69+
# MAX_MEMORY_PER_INSTANCE=32GB
70+
71+
# Aggregate limits (0 or empty = unlimited)
72+
# MAX_TOTAL_VCPUS=0
73+
# MAX_TOTAL_MEMORY=
74+
# MAX_TOTAL_VOLUME_STORAGE=
75+
76+
# Other limits
77+
# MAX_CONCURRENT_BUILDS=1
78+
# MAX_OVERLAY_SIZE=100GB

.github/workflows/test.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,14 @@ jobs:
3535
run: make build
3636

3737
- name: Run tests
38+
env:
39+
# Docker auth for tests running as root (sudo)
40+
DOCKER_CONFIG: /home/debianuser/.docker
41+
# TLS/ACME testing (optional - tests will skip if not configured)
42+
ACME_EMAIL: ${{ secrets.ACME_EMAIL }}
43+
ACME_DNS_PROVIDER: "cloudflare"
44+
ACME_CA: "https://acme-staging-v02.api.letsencrypt.org/directory"
45+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
46+
TLS_TEST_DOMAIN: "test.hypeman-development.com"
47+
TLS_ALLOWED_DOMAINS: '*.hypeman-development.com'
3848
run: make test

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@ cloud-hypervisor/**
2222
lib/system/exec_agent/exec-agent
2323

2424
# Envoy binaries
25-
lib/ingress/binaries/envoy/*/*/envoy
25+
lib/ingress/binaries/**
2626
dist/**

Makefile

Lines changed: 68 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
SHELL := /bin/bash
2-
.PHONY: oapi-generate generate-vmm-client generate-wire generate-all dev build test install-tools gen-jwt download-ch-binaries download-ch-spec ensure-ch-binaries download-envoy-binaries ensure-envoy-binaries release-prep
2+
.PHONY: oapi-generate generate-vmm-client generate-wire generate-all dev build test install-tools gen-jwt download-ch-binaries download-ch-spec ensure-ch-binaries build-caddy-binaries build-caddy ensure-caddy-binaries release-prep
33

44
# Directory where local binaries will be installed
55
BIN_DIR ?= $(CURDIR)/bin
@@ -12,6 +12,7 @@ OAPI_CODEGEN ?= $(BIN_DIR)/oapi-codegen
1212
AIR ?= $(BIN_DIR)/air
1313
WIRE ?= $(BIN_DIR)/wire
1414
GODOTENV ?= $(BIN_DIR)/godotenv
15+
XCADDY ?= $(BIN_DIR)/xcaddy
1516

1617
# Install oapi-codegen
1718
$(OAPI_CODEGEN): | $(BIN_DIR)
@@ -29,7 +30,11 @@ $(WIRE): | $(BIN_DIR)
2930
$(GODOTENV): | $(BIN_DIR)
3031
GOBIN=$(BIN_DIR) go install github.com/joho/godotenv/cmd/godotenv@latest
3132

32-
install-tools: $(OAPI_CODEGEN) $(AIR) $(WIRE) $(GODOTENV)
33+
# Install xcaddy for building Caddy with plugins
34+
$(XCADDY): | $(BIN_DIR)
35+
GOBIN=$(BIN_DIR) go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
36+
37+
install-tools: $(OAPI_CODEGEN) $(AIR) $(WIRE) $(GODOTENV) $(XCADDY)
3338

3439
# Download Cloud Hypervisor binaries
3540
download-ch-binaries:
@@ -49,18 +54,46 @@ download-ch-binaries:
4954
@chmod +x lib/vmm/binaries/cloud-hypervisor/v*/*/cloud-hypervisor
5055
@echo "Binaries downloaded successfully"
5156

52-
# Download Envoy binaries
53-
download-envoy-binaries:
54-
@echo "Downloading Envoy binaries..."
55-
@mkdir -p lib/ingress/binaries/envoy/v1.36/{x86_64,aarch64}
56-
@echo "Downloading Envoy v1.36.3 for x86_64..."
57-
@curl -L -o lib/ingress/binaries/envoy/v1.36/x86_64/envoy \
58-
https://github.com/envoyproxy/envoy/releases/download/v1.36.3/envoy-1.36.3-linux-x86_64
59-
@echo "Downloading Envoy v1.36.3 for aarch64..."
60-
@curl -L -o lib/ingress/binaries/envoy/v1.36/aarch64/envoy \
61-
https://github.com/envoyproxy/envoy/releases/download/v1.36.3/envoy-1.36.3-linux-aarch_64
62-
@chmod +x lib/ingress/binaries/envoy/v1.36/*/envoy
63-
@echo "Envoy binaries downloaded successfully"
57+
# Caddy version and modules
58+
CADDY_VERSION := v2.10.2
59+
CADDY_DNS_MODULES := --with github.com/caddy-dns/cloudflare
60+
61+
# Build Caddy with DNS modules using xcaddy
62+
# xcaddy builds Caddy from source with the specified modules
63+
build-caddy-binaries: $(XCADDY)
64+
@echo "Building Caddy $(CADDY_VERSION) with DNS modules..."
65+
@mkdir -p lib/ingress/binaries/caddy/$(CADDY_VERSION)/x86_64
66+
@mkdir -p lib/ingress/binaries/caddy/$(CADDY_VERSION)/aarch64
67+
@echo "Building Caddy $(CADDY_VERSION) for x86_64..."
68+
GOOS=linux GOARCH=amd64 $(XCADDY) build $(CADDY_VERSION) \
69+
$(CADDY_DNS_MODULES) \
70+
--output lib/ingress/binaries/caddy/$(CADDY_VERSION)/x86_64/caddy
71+
@echo "Building Caddy $(CADDY_VERSION) for aarch64..."
72+
GOOS=linux GOARCH=arm64 $(XCADDY) build $(CADDY_VERSION) \
73+
$(CADDY_DNS_MODULES) \
74+
--output lib/ingress/binaries/caddy/$(CADDY_VERSION)/aarch64/caddy
75+
@chmod +x lib/ingress/binaries/caddy/$(CADDY_VERSION)/*/caddy
76+
@echo "Caddy binaries built successfully with DNS modules"
77+
78+
# Build Caddy for current architecture only (faster for development)
79+
build-caddy: $(XCADDY)
80+
@echo "Building Caddy $(CADDY_VERSION) with DNS modules for current architecture..."
81+
@ARCH=$$(uname -m); \
82+
if [ "$$ARCH" = "x86_64" ]; then \
83+
CADDY_ARCH=x86_64; \
84+
GOARCH=amd64; \
85+
elif [ "$$ARCH" = "aarch64" ] || [ "$$ARCH" = "arm64" ]; then \
86+
CADDY_ARCH=aarch64; \
87+
GOARCH=arm64; \
88+
else \
89+
echo "Unsupported architecture: $$ARCH"; exit 1; \
90+
fi; \
91+
mkdir -p lib/ingress/binaries/caddy/$(CADDY_VERSION)/$$CADDY_ARCH; \
92+
GOOS=linux GOARCH=$$GOARCH $(XCADDY) build $(CADDY_VERSION) \
93+
$(CADDY_DNS_MODULES) \
94+
--output lib/ingress/binaries/caddy/$(CADDY_VERSION)/$$CADDY_ARCH/caddy; \
95+
chmod +x lib/ingress/binaries/caddy/$(CADDY_VERSION)/$$CADDY_ARCH/caddy
96+
@echo "Caddy binary built successfully"
6497

6598
# Download Cloud Hypervisor API spec
6699
download-ch-spec:
@@ -107,12 +140,20 @@ ensure-ch-binaries:
107140
$(MAKE) download-ch-binaries; \
108141
fi
109142

110-
# Check if Envoy binaries exist, download if missing
111-
.PHONY: ensure-envoy-binaries
112-
ensure-envoy-binaries:
113-
@if [ ! -f lib/ingress/binaries/envoy/v1.36/x86_64/envoy ]; then \
114-
echo "Envoy binaries not found, downloading..."; \
115-
$(MAKE) download-envoy-binaries; \
143+
# Check if Caddy binaries exist, build if missing
144+
.PHONY: ensure-caddy-binaries
145+
ensure-caddy-binaries:
146+
@ARCH=$$(uname -m); \
147+
if [ "$$ARCH" = "x86_64" ]; then \
148+
CADDY_ARCH=x86_64; \
149+
elif [ "$$ARCH" = "aarch64" ] || [ "$$ARCH" = "arm64" ]; then \
150+
CADDY_ARCH=aarch64; \
151+
else \
152+
echo "Unsupported architecture: $$ARCH"; exit 1; \
153+
fi; \
154+
if [ ! -f lib/ingress/binaries/caddy/$(CADDY_VERSION)/$$CADDY_ARCH/caddy ]; then \
155+
echo "Caddy binary not found, building with xcaddy..."; \
156+
$(MAKE) build-caddy; \
116157
fi
117158

118159
# Build exec-agent (guest binary) into its own directory for embedding
@@ -121,7 +162,7 @@ lib/system/exec_agent/exec-agent: lib/system/exec_agent/main.go
121162
cd lib/system/exec_agent && CGO_ENABLED=0 go build -ldflags="-s -w" -o exec-agent .
122163

123164
# Build the binary
124-
build: ensure-ch-binaries ensure-envoy-binaries lib/system/exec_agent/exec-agent | $(BIN_DIR)
165+
build: ensure-ch-binaries ensure-caddy-binaries lib/system/exec_agent/exec-agent | $(BIN_DIR)
125166
go build -tags containers_image_openpgp -o $(BIN_DIR)/hypeman ./cmd/api
126167

127168
# Build exec CLI
@@ -132,45 +173,18 @@ build-exec: | $(BIN_DIR)
132173
build-all: build build-exec
133174

134175
# Run in development mode with hot reload
135-
dev: $(AIR)
176+
dev: ensure-ch-binaries ensure-caddy-binaries lib/system/exec_agent/exec-agent $(AIR)
136177
$(AIR) -c .air.toml
137178

138-
# Run tests
139-
# Compile test binaries and grant network capabilities (runs as user, not root)
179+
# Run tests (as root for network capabilities, enables caching and parallelism)
140180
# Usage: make test - runs all tests
141181
# make test TEST=TestCreateInstanceWithNetwork - runs specific test
142-
test: ensure-ch-binaries ensure-envoy-binaries lib/system/exec_agent/exec-agent
143-
@echo "Building test binaries..."
144-
@mkdir -p $(BIN_DIR)/tests
145-
@for pkg in $$(go list -tags containers_image_openpgp ./...); do \
146-
pkg_name=$$(basename $$pkg); \
147-
go test -c -tags containers_image_openpgp -o $(BIN_DIR)/tests/$$pkg_name.test $$pkg 2>/dev/null || true; \
148-
done
149-
@echo "Granting capabilities to test binaries..."
150-
@for test in $(BIN_DIR)/tests/*.test; do \
151-
if [ -f "$$test" ]; then \
152-
sudo setcap 'cap_net_admin,cap_net_bind_service=+eip' $$test 2>/dev/null || true; \
153-
fi; \
154-
done
155-
@echo "Running tests as current user with capabilities..."
182+
test: ensure-ch-binaries ensure-caddy-binaries lib/system/exec_agent/exec-agent
156183
@if [ -n "$(TEST)" ]; then \
157184
echo "Running specific test: $(TEST)"; \
158-
for test in $(BIN_DIR)/tests/*.test; do \
159-
if [ -f "$$test" ]; then \
160-
echo ""; \
161-
echo "Checking $$(basename $$test) for $(TEST)..."; \
162-
$$test -test.run=$(TEST) -test.v -test.timeout=60s 2>&1 | grep -q "PASS\|FAIL" && \
163-
$$test -test.run=$(TEST) -test.v -test.timeout=60s || true; \
164-
fi; \
165-
done; \
185+
sudo env "PATH=$$PATH" "DOCKER_CONFIG=$${DOCKER_CONFIG:-$$HOME/.docker}" go test -tags containers_image_openpgp -run=$(TEST) -v -timeout=180s ./...; \
166186
else \
167-
for test in $(BIN_DIR)/tests/*.test; do \
168-
if [ -f "$$test" ]; then \
169-
echo ""; \
170-
echo "Running $$(basename $$test)..."; \
171-
$$test -test.v -test.parallel=10 -test.timeout=60s || exit 1; \
172-
fi; \
173-
done; \
187+
sudo env "PATH=$$PATH" "DOCKER_CONFIG=$${DOCKER_CONFIG:-$$HOME/.docker}" go test -tags containers_image_openpgp -v -timeout=180s ./...; \
174188
fi
175189

176190
# Generate JWT token for testing
@@ -189,5 +203,5 @@ clean:
189203

190204
# Prepare for release build (called by GoReleaser)
191205
# Downloads all embedded binaries and builds embedded components
192-
release-prep: download-ch-binaries download-envoy-binaries lib/system/exec_agent/exec-agent
206+
release-prep: download-ch-binaries ensure-caddy-binaries lib/system/exec_agent/exec-agent
193207
go mod tidy

README.md

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ getcap ./bin/hypeman
6060

6161
**File Descriptor Limits:**
6262

63-
Envoy (used for ingress) requires a higher file descriptor limit than the default on some systems (root defaults to 1024 on many systems). If you see "Too many open files" errors, increase the limit:
63+
Caddy (used for ingress) requires a higher file descriptor limit than the default on some systems. If you see "Too many open files" errors, increase the limit:
6464

6565
```bash
6666
# Check current limit (also check with: sudo bash -c 'ulimit -n')
@@ -98,11 +98,18 @@ Hypeman can be configured using the following environment variables:
9898
| `OTEL_ENDPOINT` | OTLP gRPC endpoint | `127.0.0.1:4317` |
9999
| `OTEL_SERVICE_INSTANCE_ID` | Instance ID for telemetry (differentiates multiple servers) | hostname |
100100
| `LOG_LEVEL` | Default log level (debug, info, warn, error) | `info` |
101-
| `LOG_LEVEL_<SUBSYSTEM>` | Per-subsystem log level (API, IMAGES, INSTANCES, NETWORK, VOLUMES, VMM, SYSTEM, EXEC) | inherits default |
102-
| `ENVOY_LISTEN_ADDRESS` | Address for Envoy ingress listeners | `0.0.0.0` |
103-
| `ENVOY_ADMIN_ADDRESS` | Address for Envoy admin API | `127.0.0.1` |
104-
| `ENVOY_ADMIN_PORT` | Port for Envoy admin API | `9901` |
105-
| `ENVOY_STOP_ON_SHUTDOWN` | Stop Envoy when hypeman shuts down (if false, Envoy continues running) | `false` |
101+
| `LOG_LEVEL_<SUBSYSTEM>` | Per-subsystem log level (API, IMAGES, INSTANCES, NETWORK, VOLUMES, VMM, SYSTEM, EXEC, CADDY) | inherits default |
102+
| `CADDY_LISTEN_ADDRESS` | Address for Caddy ingress listeners | `0.0.0.0` |
103+
| `CADDY_ADMIN_ADDRESS` | Address for Caddy admin API | `127.0.0.1` |
104+
| `CADDY_ADMIN_PORT` | Port for Caddy admin API | `2019` |
105+
| `CADDY_STOP_ON_SHUTDOWN` | Stop Caddy when hypeman shuts down (set to `true` for dev) | `false` |
106+
| `ACME_EMAIL` | Email for ACME certificate registration (required for TLS ingresses) | _(empty)_ |
107+
| `ACME_DNS_PROVIDER` | DNS provider for ACME challenges: `cloudflare` | _(empty)_ |
108+
| `ACME_CA` | ACME CA URL (empty = Let's Encrypt production) | _(empty)_ |
109+
| `TLS_ALLOWED_DOMAINS` | Comma-separated allowed domains for TLS (e.g., `*.example.com,api.other.com`) | _(empty)_ |
110+
| `DNS_PROPAGATION_TIMEOUT` | Max time to wait for DNS propagation (e.g., `2m`) | _(empty)_ |
111+
| `DNS_RESOLVERS` | Comma-separated DNS resolvers for propagation checking | _(empty)_ |
112+
| `CLOUDFLARE_API_TOKEN` | Cloudflare API token (when using `cloudflare` provider) | _(empty)_ |
106113

107114
**Important: Subnet Configuration**
108115

@@ -111,7 +118,7 @@ The default subnet `10.100.0.0/16` is chosen to avoid common conflicts. Hypeman
111118
If you need a different subnet, set `SUBNET_CIDR` in your environment. The gateway is automatically derived as the first IP in the subnet (e.g., `10.100.0.0/16``10.100.0.1`).
112119

113120
**Alternative subnets if needed:**
114-
- `172.30.0.0/16` - Private range between common Docker (172.17.x.x) and AWS (172.31.x.x) ranges
121+
- `172.30.0.0/16` - Private range between common Docker (172.17.x.x) and cloud provider (172.31.x.x) ranges
115122
- `10.200.0.0/16` - Another private range option
116123

117124
**Example:**
@@ -144,6 +151,39 @@ ip route show
144151
```
145152
Pick the interface used by the default route (usually the line starting with `default`). Avoid using local bridges like `docker0`, `br-...`, `virbr0`, or `vmbr0` as the uplink; those are typically internal virtual networks, not your actual internet-facing interface.
146153

154+
**TLS Ingress (HTTPS)**
155+
156+
Hypeman uses Caddy with automatic ACME certificates for TLS termination. Certificates are issued via DNS-01 challenges (Cloudflare).
157+
158+
To enable TLS ingresses:
159+
160+
1. Configure ACME credentials in your `.env`:
161+
```bash
162+
# Required for any TLS ingress
163+
164+
165+
# For Cloudflare
166+
ACME_DNS_PROVIDER=cloudflare
167+
CLOUDFLARE_API_TOKEN=your-api-token
168+
```
169+
170+
2. Create an ingress with TLS enabled:
171+
```bash
172+
curl -X POST http://localhost:8080/v1/ingresses \
173+
-H "Content-Type: application/json" \
174+
-d '{
175+
"name": "my-https-app",
176+
"rules": [{
177+
"match": {"hostname": "app.example.com", "port": 443},
178+
"target": {"instance": "my-instance", "port": 8080},
179+
"tls": true,
180+
"redirect_http": true
181+
}]
182+
}'
183+
```
184+
185+
Certificates are stored in `$DATA_DIR/caddy/data/` and auto-renewed by Caddy.
186+
147187
**Setup:**
148188

149189
```bash

0 commit comments

Comments
 (0)