11 KiB
11 KiB
layout, title
| layout | title |
|---|---|
| default | Architecture |
Architecture
System Overview
The proxy system consists of three main components working together to provide high-performance reverse proxying with automated SSL management.
┌──────────────────────────────────────────────────────────────┐
│ Internet │
└─────────────────────────┬────────────────────────────────────┘
│ HTTPS/HTTP
▼
┌──────────────────────────────────────────────────────────────┐
│ OpenResty/Nginx │
│ ┌────────────────┐ ┌──────────────┐ ┌─────────────────┐ │
│ │ SSL Termination│ │ Host Routing │ │ Request Proxying│ │
│ │ (lua-resty- │ │ (targetinfo. │ │ │ │
│ │ auto-ssl) │ │ lua) │ │ │ │
│ └────────────────┘ └──────────────┘ └─────────────────┘ │
└────────────┬──────────────────┬───────────────────────────┬──┘
│ │ │
Let's Encrypt Unix Socket Backend
HTTP-01 Lookup Query Services
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────────────────────┐
│ Node.js Application │
│ ┌──────────────┐ ┌────────────────┐ ┌─────────────────┐ │
│ │ Services │ │ Models │ │ Routes │ │
│ │ - host_lookup│ │ - Host │ │ - /api/host │ │
│ │ - scheduler │ │ - DNS Provider │ │ - /api/dns │ │
│ └──────────────┘ │ - User │ │ - /api/user │ │
│ │ - Auth │ │ - /api/auth │ │
│ └────────────────┘ │ - /api/cert │ │
│ └─────────────────┘ │
└────────────┬────────────────────┬────────────────────────────┘
│ │
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ Redis │ │ DNS Providers │
│ - Host configs │ │ - CloudFlare │
│ - User accounts │ │ - DigitalOcean │
│ - SSL certs │ │ - PorkBun │
│ - Auth tokens │ │ (DNS-01 challenges) │
└──────────────────────┘ └──────────────────────┘
Component Details
OpenResty/Nginx (Frontend)
Responsibilities:
- Accept incoming HTTP/HTTPS requests
- SSL termination using lua-resty-auto-ssl
- Host-based routing decisions
- Proxy requests to backend services
Key Features:
- HTTP-01 ACME challenge handling for automatic SSL
- Lua-based host lookup via Unix socket
- High-performance event-driven architecture
- Support for WebSocket connections
Configuration Files:
/etc/openresty/nginx.conf- Main configuration/etc/openresty/autossl.conf- Let's Encrypt integration/etc/openresty/sites-enabled/000-proxy- Proxy configuration/usr/local/openresty/lualib/targetinfo.lua- Host lookup module
Node.js Application (Backend)
Responsibilities:
- API for host/user/DNS management
- Wildcard SSL certificate orchestration
- Host lookup tree maintenance
- User authentication and authorization
Directory Structure:
nodejs/
├── bin/www # Application entry point
├── models/ # Data models
│ ├── host.js # Host configuration and lookup
│ ├── auth.js # Authentication logic
│ ├── user.js # User management
│ └── dns_provider/ # DNS provider implementations
├── routes/ # API endpoints
│ ├── host.js # Host CRUD operations
│ ├── dns.js # DNS provider management
│ ├── user.js # User management
│ └── auth.js # Authentication
├── services/ # Background services
│ ├── host_lookup.js # Unix socket server
│ └── host_scheduler.js # Cert renewal scheduler
├── middleware/ # Express middleware
│ └── auth.js # Authentication middleware
└── utils/ # Utility modules
└── unix_socket_json.js # Unix socket server
Redis (Data Store)
Stored Data:
- Host configurations (domain, IP, port, SSL settings)
- User accounts and hashed passwords
- Authentication tokens
- SSL certificates (for wildcard domains)
- DNS provider credentials
- Domain-to-provider mappings
Key Prefixes:
proxy_Host_<hostname> # Host configuration
proxy_User_<username> # User account
proxy_AuthToken_<token> # Auth tokens
proxy_DnsProvider_<id> # DNS provider
proxy_Domain_<domain> # Domain info
<hostname>:latest # SSL certificate cache
Request Flow
Standard HTTP/HTTPS Request
- Client sends HTTPS request to
app.example.com - OpenResty receives request, terminates SSL
- Lua script (
targetinfo.lua) queries Redis for host config - If cache miss, Lua queries Node.js via Unix socket
- Node.js performs host lookup (supports wildcards)
- Response returned with target IP and port
- OpenResty proxies request to backend service
- Response proxied back to client
Wildcard SSL Certificate Request
- User creates wildcard host (
*.example.com) via API - Node.js validates domain has DNS provider configured
- Let's Encrypt DNS-01 challenge initiated
- DNS provider API creates TXT record (
_acme-challenge.example.com) - Let's Encrypt validates TXT record
- Certificate generated and stored in Redis
- DNS provider cleans up TXT record
- Background scheduler monitors expiration, renews 30 days before expiry
Host Lookup Algorithm
The lookup tree enables sophisticated domain matching:
Input: "api.v1.example.com"
Tree Structure:
{
"com": {
"example": {
"*": { // Matches api.example.com
"#record": {...}
},
"v1": {
"api": { // Matches api.v1.example.com (exact)
"#record": {...}
}
}
}
}
}
Priority: Exact > Single wildcard (*) > Double wildcard (**)
Wildcard Types:
example.com- Exact match only*.example.com- Matchessub.example.com(single level)**.example.com- Matches any depth (sub.deep.example.com)api.*.example.com- Matchesapi.v1.example.com,api.v2.example.com
Security Architecture
Authentication Flow
- User sends credentials to
/api/auth/login - Credentials validated against stored hash (bcrypt)
- Token generated and stored in Redis with TTL
- Token returned to client
- Subsequent requests include token in
auth-tokenheader - Middleware validates token before processing request
SSL Certificate Security
- Private keys stored only in Redis (memory/disk based on config)
- Fallback certificates used when SNI unavailable
- Let's Encrypt rate limiting respected
- DNS provider credentials marked as
isPrivate(not returned in API)
Unix Socket Communication
- Socket file:
/var/run/proxy_lookup.socket - Permissions:
777(container-safe, single-use deployment) - Protocol: JSON over Unix stream socket
- Buffer handling: Accumulates partial messages until complete JSON
Performance Optimizations
Caching Strategy
- Redis cache - Primary host configuration storage
- Lookup tree - In-memory host lookup (rebuilt on changes)
- OpenResty cache - Reduces Unix socket calls
- Wildcard parent caching - Stores resolved wildcard parents
Unix Socket vs HTTP API
Unix socket chosen over HTTP for host lookups:
- Lower latency - No TCP overhead
- Higher throughput - No HTTP parsing
- Simpler - Direct JSON communication
- Secure - Filesystem permissions, no network exposure
Scalability Considerations
Current Architecture
- Single instance - OpenResty + Node.js + Redis on one server
- Vertical scaling - Add CPU/RAM as needed
- Limitations - Unix socket ties OpenResty to Node.js on same host
Future Scaling Options
- Redis cluster - Distribute data storage
- Multiple OpenResty instances - Load balance incoming requests
- Stateless Node.js - Run multiple API instances
- Replace Unix socket - Use TCP/HTTP for cross-host communication
- Separate cert management - Dedicated service for wildcard SSL
Monitoring and Observability
Logs
- OpenResty:
/var/log/nginx/access.log,/var/log/nginx/error.log - Node.js:
journalctl -u proxy.service - Redis:
redis-cli MONITOR
Health Checks
- Node.js API:
curl http://localhost:3000/api/host - Redis:
redis-cli PING - OpenResty:
systemctl status openresty - Unix socket:
ls -la /var/run/proxy_lookup.socket
Metrics to Monitor
- Request rate and response times
- SSL certificate expiration dates
- Redis memory usage
- Host lookup cache hit rate
- Background service execution times