mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-29 03:50:18 +00:00
Complete documentation overhaul for Pulse v4.24.0 release covering all new features and operational procedures. Documentation Updates (19 files): P0 Release-Critical: - Operations: Rewrote ADAPTIVE_POLLING_ROLLOUT.md as GA operations runbook - Operations: Updated ADAPTIVE_POLLING_MANAGEMENT_ENDPOINTS.md with DEFERRED status - Operations: Enhanced audit-log-rotation.md with scheduler health checks - Security: Updated proxy hardening docs with rate limit defaults - Docker: Added runtime logging and rollback procedures P1 Deployment & Integration: - KUBERNETES.md: Runtime logging config, adaptive polling, post-upgrade verification - PORT_CONFIGURATION.md: Service naming, change tracking via update history - REVERSE_PROXY.md: Rate limit headers, error pass-through, v4.24.0 verification - PROXY_AUTH.md, OIDC.md, WEBHOOKS.md: Runtime logging integration - TROUBLESHOOTING.md, VM_DISK_MONITORING.md, zfs-monitoring.md: Updated workflows Features Documented: - X-RateLimit-* headers for all API responses - Updates rollback workflow (UI & CLI) - Scheduler health API with rich metadata - Runtime logging configuration (no restart required) - Adaptive polling (GA, enabled by default) - Enhanced audit logging - Circuit breakers and dead-letter queue Supporting Changes: - Discovery service enhancements - Config handlers updates - Sensor proxy installer improvements Total Changes: 1,626 insertions(+), 622 deletions(-) Files Modified: 24 (19 docs, 5 code) All documentation is production-ready for v4.24.0 release.
367 lines
9.7 KiB
Markdown
367 lines
9.7 KiB
Markdown
# Reverse Proxy Configuration
|
|
|
|
Pulse uses WebSockets for real-time updates. Your reverse proxy **MUST** support WebSocket connections or Pulse will not work correctly.
|
|
|
|
## Important Requirements
|
|
|
|
1. **WebSocket Support Required** - Enable WebSocket proxying
|
|
2. **Proxy Headers** - Forward original host and IP headers
|
|
3. **Timeouts** - Increase timeouts for long-lived connections (7 days recommended)
|
|
4. **Buffer Sizes** - Increase for large state updates (64KB recommended)
|
|
5. **Rate Limit Headers (v4.24.0+)** - Pass through `X-RateLimit-*` and `Retry-After` headers
|
|
6. **Error Pass-through** - Don't intercept 429 responses (rate limits)
|
|
|
|
## Authentication with Reverse Proxy
|
|
|
|
If you're using authentication at the reverse proxy level (Authentik, Authelia, etc.), you can disable Pulse's built-in authentication to avoid double login prompts:
|
|
|
|
```bash
|
|
# In your .env file or environment
|
|
DISABLE_AUTH=true
|
|
```
|
|
|
|
When `DISABLE_AUTH=true` is set:
|
|
- Pulse's built-in authentication is completely bypassed
|
|
- All endpoints become accessible without authentication
|
|
- The reverse proxy handles all authentication and authorization
|
|
- A warning is logged on startup to confirm auth is disabled
|
|
|
|
⚠️ **Warning**: Only use `DISABLE_AUTH=true` if your reverse proxy provides authentication. Never expose Pulse directly to the internet with authentication disabled.
|
|
|
|
## Nginx
|
|
|
|
```nginx
|
|
server {
|
|
listen 80;
|
|
server_name pulse.example.com;
|
|
|
|
# Redirect to HTTPS
|
|
return 301 https://$server_name$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name pulse.example.com;
|
|
|
|
# SSL configuration
|
|
ssl_certificate /path/to/cert.pem;
|
|
ssl_certificate_key /path/to/key.pem;
|
|
|
|
# Proxy settings
|
|
location / {
|
|
proxy_pass http://localhost:7655;
|
|
proxy_http_version 1.1;
|
|
|
|
# Required for WebSocket
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
|
|
# Proxy headers
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
|
|
# Timeouts for WebSocket and adaptive polling (v4.24.0+)
|
|
proxy_connect_timeout 7d;
|
|
proxy_send_timeout 7d;
|
|
proxy_read_timeout 7d;
|
|
|
|
# Disable buffering for real-time updates
|
|
proxy_buffering off;
|
|
|
|
# IMPORTANT (v4.24.0+): Don't intercept error responses
|
|
# This ensures 429 rate limit responses reach the client
|
|
proxy_intercept_errors off;
|
|
|
|
# Increase buffer sizes for large messages
|
|
proxy_buffer_size 64k;
|
|
proxy_buffers 8 64k;
|
|
proxy_busy_buffers_size 128k;
|
|
}
|
|
|
|
# API endpoints (optional, same config as above)
|
|
location /api/ {
|
|
proxy_pass http://localhost:7655/api/;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_connect_timeout 7d;
|
|
proxy_send_timeout 7d;
|
|
proxy_read_timeout 7d;
|
|
proxy_buffering off;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Caddy v2
|
|
|
|
Caddy automatically handles WebSocket upgrades when reverse proxying.
|
|
|
|
```caddy
|
|
pulse.example.com {
|
|
reverse_proxy localhost:7655
|
|
}
|
|
```
|
|
|
|
For more control:
|
|
|
|
```caddy
|
|
pulse.example.com {
|
|
reverse_proxy localhost:7655 {
|
|
# Headers automatically handled by Caddy
|
|
header_up Host {host}
|
|
header_up X-Real-IP {remote}
|
|
header_up X-Forwarded-For {remote}
|
|
header_up X-Forwarded-Proto {scheme}
|
|
|
|
# Increase timeouts for WebSocket
|
|
transport http {
|
|
dial_timeout 30s
|
|
response_header_timeout 30s
|
|
read_timeout 0
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Apache
|
|
|
|
```apache
|
|
<VirtualHost *:443>
|
|
ServerName pulse.example.com
|
|
|
|
SSLEngine on
|
|
SSLCertificateFile /path/to/cert.pem
|
|
SSLCertificateKeyFile /path/to/key.pem
|
|
|
|
# Enable necessary modules:
|
|
# a2enmod proxy proxy_http proxy_wstunnel headers
|
|
|
|
# WebSocket proxy
|
|
RewriteEngine On
|
|
RewriteCond %{HTTP:Upgrade} websocket [NC]
|
|
RewriteCond %{HTTP:Connection} upgrade [NC]
|
|
RewriteRule ^/?(.*) "ws://localhost:7655/$1" [P,L]
|
|
|
|
# Regular HTTP proxy
|
|
ProxyPass / http://localhost:7655/
|
|
ProxyPassReverse / http://localhost:7655/
|
|
|
|
# Preserve host headers
|
|
ProxyPreserveHost On
|
|
|
|
# Forward real IP
|
|
RequestHeader set X-Real-IP "%{REMOTE_ADDR}s"
|
|
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"
|
|
RequestHeader set X-Forwarded-Proto "https"
|
|
|
|
# Disable buffering
|
|
ProxyIOBufferSize 65536
|
|
</VirtualHost>
|
|
```
|
|
|
|
## Traefik
|
|
|
|
```yaml
|
|
# docker-compose.yml
|
|
services:
|
|
pulse:
|
|
image: rcourtman/pulse:latest
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.pulse.rule=Host(`pulse.example.com`)"
|
|
- "traefik.http.routers.pulse.tls=true"
|
|
- "traefik.http.services.pulse.loadbalancer.server.port=7655"
|
|
# WebSocket support is automatic in Traefik 2.x+
|
|
```
|
|
|
|
Or using Traefik file configuration:
|
|
|
|
```yaml
|
|
# traefik-dynamic.yml
|
|
http:
|
|
routers:
|
|
pulse:
|
|
rule: "Host(`pulse.example.com`)"
|
|
service: pulse
|
|
tls: {}
|
|
|
|
services:
|
|
pulse:
|
|
loadBalancer:
|
|
servers:
|
|
- url: "http://localhost:7655"
|
|
```
|
|
|
|
## HAProxy
|
|
|
|
```haproxy
|
|
frontend https
|
|
bind *:443 ssl crt /path/to/cert.pem
|
|
|
|
# ACL for Pulse
|
|
acl host_pulse hdr(host) -i pulse.example.com
|
|
|
|
# WebSocket detection
|
|
acl is_websocket hdr(Upgrade) -i websocket
|
|
|
|
# Use backend
|
|
use_backend pulse if host_pulse
|
|
|
|
backend pulse
|
|
# Health check
|
|
option httpchk GET /api/health
|
|
|
|
# WebSocket support
|
|
option http-server-close
|
|
option forwardfor
|
|
|
|
# Timeouts for WebSocket
|
|
timeout client 3600s
|
|
timeout server 3600s
|
|
timeout tunnel 3600s
|
|
|
|
# Backend server
|
|
server pulse1 localhost:7655 check
|
|
```
|
|
|
|
## Cloudflare Tunnel
|
|
|
|
If using Cloudflare Tunnel (cloudflared):
|
|
|
|
```yaml
|
|
# config.yml
|
|
tunnel: YOUR_TUNNEL_ID
|
|
credentials-file: /path/to/credentials.json
|
|
|
|
ingress:
|
|
- hostname: pulse.example.com
|
|
service: http://localhost:7655
|
|
originRequest:
|
|
# Enable WebSocket
|
|
noTLSVerify: false
|
|
connectTimeout: 30s
|
|
# No additional config needed - WebSockets work by default
|
|
- service: http_status:404
|
|
```
|
|
|
|
## Testing WebSocket Connection
|
|
|
|
After configuring your reverse proxy, test that WebSockets work:
|
|
|
|
```bash
|
|
# Test basic connectivity
|
|
curl https://pulse.example.com/api/health
|
|
|
|
# Test WebSocket upgrade (should return 101 Switching Protocols)
|
|
curl -i -N \
|
|
-H "Connection: Upgrade" \
|
|
-H "Upgrade: websocket" \
|
|
-H "Sec-WebSocket-Version: 13" \
|
|
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
|
|
https://pulse.example.com/api/ws
|
|
```
|
|
|
|
### v4.24.0+ Verification
|
|
|
|
**Test scheduler health API** (adaptive polling):
|
|
```bash
|
|
curl -s https://pulse.example.com/api/monitoring/scheduler/health | jq
|
|
```
|
|
|
|
**Expected response:**
|
|
- `enabled: true`
|
|
- Queue depth reasonable
|
|
- No stuck circuit breakers
|
|
- WebSocket connection remains open
|
|
|
|
**Verify rate limit headers** (v4.24.0+):
|
|
```bash
|
|
curl -I https://pulse.example.com/api/version
|
|
```
|
|
|
|
**Look for headers:**
|
|
```
|
|
HTTP/1.1 200 OK
|
|
X-RateLimit-Limit: 500
|
|
X-RateLimit-Remaining: 499
|
|
X-RateLimit-Reset: 1698765432
|
|
```
|
|
|
|
**Test rate limiting:**
|
|
```bash
|
|
# Trigger rate limit (requires many requests)
|
|
for i in {1..600}; do curl -s https://pulse.example.com/api/health > /dev/null; done
|
|
|
|
# Should eventually see:
|
|
curl -I https://pulse.example.com/api/health
|
|
# HTTP/1.1 429 Too Many Requests
|
|
# X-RateLimit-Limit: 500
|
|
# X-RateLimit-Remaining: 0
|
|
# Retry-After: 60
|
|
```
|
|
|
|
**Important:** If you don't see rate limit headers or 429 responses are converted to 502/504, verify `proxy_intercept_errors off` (Nginx) or equivalent is set.
|
|
|
|
In browser console (F12):
|
|
```javascript
|
|
// Test WebSocket connection
|
|
const ws = new WebSocket('wss://pulse.example.com/api/ws');
|
|
ws.onopen = () => console.log('WebSocket connected!');
|
|
ws.onmessage = (e) => console.log('Received:', e.data);
|
|
ws.onerror = (e) => console.error('WebSocket error:', e);
|
|
```
|
|
|
|
## Common Issues
|
|
|
|
### "Connection Lost" or no real-time updates
|
|
- WebSocket upgrade not configured correctly
|
|
- Check proxy passes `Upgrade` and `Connection` headers
|
|
- Verify timeouts are increased for long connections
|
|
|
|
### CORS errors
|
|
- Pulse handles CORS internally
|
|
- Don't add additional CORS headers in proxy
|
|
- If needed, set `ALLOWED_ORIGINS` in Pulse configuration
|
|
|
|
### 502 Bad Gateway
|
|
- Pulse not running on expected port (default 7655)
|
|
- Check with: `curl http://localhost:7655/api/health`
|
|
- Verify Pulse service: `systemctl status pulse` (use `pulse-backend` if you're on a legacy unit)
|
|
|
|
### WebSocket closes immediately
|
|
- Timeout too short in proxy configuration
|
|
- Increase `proxy_read_timeout` (Nginx) or equivalent
|
|
- Set to at least 3600s (1 hour) or more
|
|
|
|
## Security Recommendations
|
|
|
|
1. **Always use HTTPS** for production deployments
|
|
2. **Set proper headers** to prevent clickjacking:
|
|
```nginx
|
|
add_header X-Frame-Options "SAMEORIGIN";
|
|
add_header X-Content-Type-Options "nosniff";
|
|
```
|
|
3. **Rate limiting** for API endpoints:
|
|
```nginx
|
|
limit_req_zone $binary_remote_addr zone=pulse:10m rate=30r/s;
|
|
limit_req zone=pulse burst=50 nodelay;
|
|
```
|
|
4. **Hide proxy version**:
|
|
```nginx
|
|
proxy_hide_header X-Powered-By;
|
|
server_tokens off;
|
|
```
|
|
|
|
## Support
|
|
|
|
If WebSockets still don't work after following this guide:
|
|
1. Check browser console for errors (F12)
|
|
2. Verify Pulse logs: `journalctl -u pulse -f`
|
|
3. Test without proxy first: `http://your-server:7655`
|
|
4. Report issues: https://github.com/rcourtman/Pulse/issues
|