Add LICENSE, README, AGENTS.md, and deployment setup guide
Dual MIT/Apache-2.0 license, public-facing README with quick start and config reference, step-by-step deploy/README.md for Docker and systemd setups, and AGENTS.md for LLM-assisted development.
This commit is contained in:
156
AGENTS.md
Normal file
156
AGENTS.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# AGENTS.md
|
||||
|
||||
Guidance for LLM agents (and humans) working on this project.
|
||||
|
||||
## Project Overview
|
||||
|
||||
A memory-safe reverse proxy built with Rust/axum that replaces vulnerable nginx
|
||||
installations. Terminates TLS, routes requests by Host header to upstream
|
||||
services, enforces rate limits, and injects proxy headers. See `README.md` and
|
||||
`docs/architecture/` for full details.
|
||||
|
||||
## Build & Run
|
||||
|
||||
```bash
|
||||
cargo build # debug build
|
||||
cargo build --release # release build
|
||||
cargo test # run all tests (unit + integration)
|
||||
cargo test -- --nocapture # run tests with stdout visible
|
||||
cargo clippy # lint
|
||||
reverse-proxy --config config.toml # run (defaults to /etc/reverse-proxy/config.toml)
|
||||
reverse-proxy --validate --config config.toml # validate config only
|
||||
```
|
||||
|
||||
For a static binary with no libc dependency:
|
||||
|
||||
```bash
|
||||
cargo build --release --target x86_64-unknown-linux-musl
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── main.rs # Entry point, server startup, listener binding
|
||||
├── cli.rs # CLI parsing (clap), config loading, validation
|
||||
├── lib.rs # Library root, module declarations
|
||||
├── config/
|
||||
│ ├── static_config.rs # StaticConfig — immutable, requires restart
|
||||
│ ├── dynamic_config.rs# DynamicConfig — hot-reloadable via ArcSwap
|
||||
│ ├── validation.rs # Config validation rules (called at startup and reload)
|
||||
│ └── test_fixtures.rs # Test config generation helpers
|
||||
├── proxy/
|
||||
│ ├── handler.rs # Core reverse proxy handler (forward requests to upstream)
|
||||
│ ├── headers.rs # Proxy header injection (X-Real-IP, X-Forwarded-For, etc.)
|
||||
│ ├── body_limit.rs # Request body size limiting middleware
|
||||
│ ├── error.rs # Error response types (502, 504, 429, etc.)
|
||||
│ └── mod.rs # Router construction, client creation
|
||||
├── tls/
|
||||
│ ├── acceptor.rs # TLS acceptor setup (manual + ACME)
|
||||
│ ├── acme.rs # ACME certificate provisioning via rustls-acme
|
||||
│ ├── config.rs # TLS ServerConfig construction, cipher suites
|
||||
│ └── redirect.rs # HTTP → HTTPS 301 redirect listener
|
||||
├── rate_limit/
|
||||
│ ├── mod.rs # Rate limiting middleware, eviction task
|
||||
│ └── bucket.rs # Token bucket implementation (IPv4 /32, IPv6 /64)
|
||||
├── admin/
|
||||
│ ├── socket.rs # Unix domain socket admin API (reload, status)
|
||||
│ └── mod.rs
|
||||
├── health.rs # Health check endpoint on localhost:9900
|
||||
├── logging/
|
||||
│ ├── mod.rs # Logging init (file + stdout, ANSI disabled)
|
||||
│ └── format.rs # Structured log format (REQUEST, RATE_LIMIT, etc.)
|
||||
├── server.rs # HTTPS listener serving with ALPN detection
|
||||
├── shutdown.rs # Graceful shutdown (SIGTERM, SIGINT) + SIGHUP reload
|
||||
└── utils.rs # Shared utilities
|
||||
```
|
||||
|
||||
## Key Architecture Concepts
|
||||
|
||||
- **StaticConfig vs DynamicConfig**: Static config (bind addresses, TLS,
|
||||
ports) requires a restart. Dynamic config (sites, rate limits, body limits)
|
||||
can be reloaded at runtime via SIGHUP or admin socket, using `ArcSwap` for
|
||||
lock-free reads.
|
||||
- **Multi-listener**: `[[listeners]]` in TOML — each listener has its own bind
|
||||
address, TLS config, and site routing. Sites are collected into a global
|
||||
routing table at runtime.
|
||||
- **Edge proxy model**: The proxy is the edge — X-Forwarded-For is replaced
|
||||
(not appended), X-Real-IP is set from the connection's remote address.
|
||||
- **No `/health` on public listener**: Health checking is localhost:9900 only.
|
||||
The main listener does not intercept any paths.
|
||||
- **HTTP/2 client-facing only**: ALPN detects h2 vs http/1.1. Upstream
|
||||
connections are always HTTP/1.1.
|
||||
- **IPv6 rate limiting**: IPv6 addresses are normalized to /64 prefixes so
|
||||
addresses within the same /64 share a token bucket.
|
||||
|
||||
## Config Format
|
||||
|
||||
TOML. See `docs/architecture/config.md` for full schema. Key validation rules:
|
||||
|
||||
- `bind_addr` must be explicit (no `0.0.0.0`) unless `allow_wildcard_bind` is
|
||||
enabled via config or `--allow-wildcard-bind` CLI flag (OR logic)
|
||||
- Site `host` values must be unique across all listeners
|
||||
- `upstream` must be in `host:port` format (e.g., `gitea:3000`, `127.0.0.1:3000`)
|
||||
- ACME mode requires `acme_domains` (non-empty) and `acme_contact` (valid
|
||||
`mailto:` URI)
|
||||
- Manual mode requires `cert_path` and `key_path` pointing to readable files
|
||||
- `rate_limit.requests_per_second` and `rate_limit.burst` must be > 0
|
||||
- `body.limit_bytes` must be > 0
|
||||
- `http_port` must be 0 (disabled) or 1–65535; `https_port` must be 1–65535
|
||||
- `health_check_port` must not conflict with any listener's http_port or
|
||||
https_port on the same bind address
|
||||
|
||||
## Testing
|
||||
|
||||
Tests use `rcgen` for self-signed certificate generation and `reqwest` for
|
||||
HTTP client requests. Integration tests are in `tests/integration_test.rs`
|
||||
with helpers in `tests/helpers/`.
|
||||
|
||||
```bash
|
||||
cargo test # all tests
|
||||
cargo test --test integration # integration tests only
|
||||
cargo test --lib # unit tests only
|
||||
```
|
||||
|
||||
## Code Style
|
||||
|
||||
- No comments unless explicitly requested
|
||||
- Error handling uses `anyhow` for application code and `thiserror` for
|
||||
library error types
|
||||
- Structured logging with `tracing` — always `with_ansi(false)`
|
||||
- Config types implement `serde::Deserialize` for TOML parsing
|
||||
- All network operations use `tokio` async runtime
|
||||
|
||||
## Deployment Files
|
||||
|
||||
`deploy/` contains production-ready deployment configs:
|
||||
|
||||
- `Dockerfile` — multi-stage build (rust:alpine → alpine)
|
||||
- `docker-compose.yml` — complete setup with Gitea example
|
||||
- `reverse-proxy.service` — systemd unit file with security hardening
|
||||
- `fail2ban/` — filter and jail config for rate limit log parsing
|
||||
|
||||
See `deploy/README.md` for step-by-step setup instructions.
|
||||
|
||||
## Common Modifications
|
||||
|
||||
### Adding a new site
|
||||
|
||||
Add a `[[listeners.sites]]` entry to config and reload:
|
||||
|
||||
```bash
|
||||
echo "reload" | socat - UNIX-CONNECT:/run/reverse-proxy/admin.sock
|
||||
```
|
||||
|
||||
### Changing rate limits
|
||||
|
||||
Update `[rate_limit]` in config and reload (no restart needed).
|
||||
|
||||
### Changing bind address or TLS config
|
||||
|
||||
These are in StaticConfig — require a full process restart.
|
||||
|
||||
### Adding per-site timeouts
|
||||
|
||||
Set `upstream_connect_timeout_secs` and `upstream_request_timeout_secs` on a
|
||||
site definition. Defaults are 5s connect, 60s request.
|
||||
Reference in New Issue
Block a user