diff --git a/.docs/architecture.md b/.docs/architecture.md index 53f9f4e..12d476b 100644 --- a/.docs/architecture.md +++ b/.docs/architecture.md @@ -5,40 +5,40 @@ The SecNex API Gateway follows a modular architecture with clear separation of concerns. The system is built using the chi/v5 HTTP router and implements a middleware pipeline pattern. ``` -┌─────────────────────────────────────────────────────────────┐ -│ Client Request │ -└─────────────────────────┬───────────────────────────────────┘ +┌────────────────────────────────────────────────────────────┐ +│ Client Request │ +└─────────────────────────┬──────────────────────────────────┘ │ ▼ -┌─────────────────────────────────────────────────────────────┐ -│ Gateway (chi Router) │ +┌────────────────────────────────────────────────────────────┐ +│ Gateway (chi Router) │ │ ┌──────────────────────────────────────────────────────┐ │ -│ │ Middleware Pipeline │ │ -│ │ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │ │ -│ │ │ Request ID │→ │ Real IP │→ │ Logger │ │ │ -│ │ └────────────┘ └────────────┘ └──────────────┘ │ │ +│ │ Global Middleware Pipeline │ │ +│ │ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │ │ +│ │ │ Request ID │→ │ Real IP │→ │ Logger │ │ │ +│ │ └────────────┘ └────────────┘ └──────────────┘ │ │ │ └──────────────────────────────────────────────────────┘ │ -└─────────────────────────┬───────────────────────────────────┘ +└─────────────────────────┬──────────────────────────────────┘ │ ▼ -┌─────────────────────────────────────────────────────────────┐ -│ Route Handler │ +┌────────────────────────────────────────────────────────────┐ +│ Route Handler │ │ ┌──────────────────────────────────────────────────────┐ │ -│ │ Per-Route Middleware Chain │ │ -│ │ ┌────────────┐ ┌─────────┐ ┌──────────────┐ │ │ -│ │ │ Strip │→ │ WAF │→ │ Auth │ │ │ -│ │ │ Prefix │ │ Filter │ │ Middleware │ │ │ -│ │ └────────────┘ └─────────┘ └──────────────┘ │ │ +│ │ Per-Route Middleware Chain │ │ +│ │ ┌────────────┐ ┌──────────────┐ │ │ +│ │ │ Strip │→ │ Reverse │ │ │ +│ │ │ Prefix │ │ Proxy │ │ │ +│ │ └────────────┘ └──────────────┘ │ │ │ └──────────────────────────────────────────────────────┘ │ -└─────────────────────────┬───────────────────────────────────┘ +└─────────────────────────┬──────────────────────────────────┘ │ ▼ -┌─────────────────────────────────────────────────────────────┐ -│ Reverse Proxy (httputil.ReverseProxy) │ -│ │ │ -│ ▼ │ -│ Backend Service │ -└─────────────────────────────────────────────────────────────┘ +┌────────────────────────────────────────────────────────────┐ +│ Reverse Proxy (httputil.ReverseProxy) │ +│ │ │ +│ ▼ │ +│ Backend Service │ +└────────────────────────────────────────────────────────────┘ ``` ## Component Architecture @@ -51,18 +51,17 @@ app/ ├── config/ # Configuration management │ ├── config.go # Config interface │ ├── file.go # File-based config loader -│ └── types.go # Configuration type definitions +│ └── database.go # Database configuration ├── server/ # Core server components │ ├── gateway.go # Main gateway server -│ ├── routes.go # Route handler creation -│ └── proxy.go # Host-based virtual hosting +│ ├── routes.go # Route registration +│ ├── api.go # API definitions +│ ├── host.go # Host definitions +│ └── target.go # Target (backend) definitions ├── middlewares/ # HTTP middleware -│ ├── auth.go # Authentication middleware -│ └── waf.go # Web Application Firewall -├── handlers/ # Request handlers -│ └── route.go # Route handlers (placeholder) -└── res/ # HTTP responses - └── error.go # Error response utilities +│ ├── host.go # Host logging middleware +│ └── logger.go # Structured logging middleware +└── utils/ # Utility functions ``` ### Core Components @@ -71,25 +70,39 @@ app/ The Gateway is the main server component that: - Initializes the chi router -- Applies global middleware (request_id, real_ip, logger) +- Applies global middleware (request_id, real_ip, logger, host) +- Configures proxy directors for each API - Registers route handlers - Starts the HTTP server #### Routes (`server/routes.go`) -The Routes component creates handlers for each configured route: -1. Finds the matching API backend target -2. Creates a reverse proxy using `httputil.NewSingleHostReverseProxy` -3. Optionally strips prefix from the request path -4. Applies middleware chain: WAF → Auth +The Routes component handles: +- Creating route handlers from configuration +- Applying strip prefix middleware +- Registering routes with chi router (method-agnostic) +- Connecting routes to API backends -#### Configuration (`config/`) +#### API (`server/api.go`) -Configuration is loaded from YAML and provides: -- Gateway settings (host, port, features) -- Route definitions with security policies -- API backend targets -- Proxy configurations for virtual hosting +API definitions that: +- Link hosts to backend targets +- Implement `http.Handler` interface for proxying +- Delegate requests to the reverse proxy + +#### Hosts (`server/host.go`) + +Host definitions for: +- Virtual hosting support +- Domain-based routing +- Host header validation + +#### Targets (`server/target.go`) + +Target (backend) definitions that: +- Store backend service URLs +- Create `httputil.ReverseProxy` instances +- Handle proxy configuration ## Middleware Chain @@ -100,56 +113,56 @@ Applied to all requests via chi middleware: | Feature | Description | |---------|-------------| | `request_id` | Adds unique request ID to context | -| `real_ip` | Determines the real client IP | -| `logger` | Logs HTTP requests | +| `real_ip` | Determines the real client IP from headers | +| `logger` | Logs HTTP requests with structured JSON output | +| `host` | Logs the host header for each request | ### Per-Route Middleware -Applied in order to each route handler: +Applied to each route handler: 1. **StripPrefix** - Removes specified prefix from request path before proxying -2. **WAF** - Filters requests based on HTTP method -3. **Auth** - Validates authentication credentials ## Request Flow 1. **Client Request** → Gateway receives HTTP request -2. **Global Middleware** → Request ID, Real IP, Logging applied -3. **Route Matching** → Chi matches route pattern (e.g., `/api/v1/dev/*`) -4. **Per-Route Middleware** → StripPrefix → WAF → Auth +2. **Global Middleware** → Request ID, Real IP, Host logging, Logger applied +3. **Route Matching** → Chi matches route pattern (e.g., `/api/v1/*`) +4. **Per-Route Middleware** → StripPrefix (if enabled) 5. **Reverse Proxy** → Request forwarded to backend API 6. **Response** → Backend response returned to client -## Authentication Flow - -The authentication middleware supports path-based filtering: - -``` -┌─────────────────────┐ -│ Include Set? │ -└──────────┬──────────┘ - │ - ┌─────┴─────┐ - │ Yes │ No - ▼ ▼ -┌─────────┐ ┌─────────────┐ -│ Match │ │ Exclude │ -│ Include?│ │ Set? │ -└────┬────┘ └──────┬──────┘ - │ │ - ┌──┴──┐ ┌──┴──┐ - │Yes │No │Yes │No - ▼ ▼ ▼ ▼ -┌────┐┌────┐ ┌────┐┌────┐ -│Auth││Skip│ │Skip││Auth│ -└────┘└────┘ └────┘└────┘ -``` - ## Configuration Flow -1. Load `gateway.yaml` via `config.NewFileConfig()` +1. Load `gateway.yaml` via `config.NewFile()` 2. Parse YAML into `Configuration` struct -3. Create Gateway with configuration -4. Create Routes from route definitions and API targets -5. Register handlers with chi router -6. Start HTTP server +3. Create Hosts from configuration +4. Create Targets from configuration +5. Create APIs (linking Hosts to Targets) +6. Create Routes (linking Routes to APIs) +7. Initialize Gateway with all components +8. Configure proxy directors +9. Register routes with chi router +10. Start HTTP server + +## Logging + +The gateway uses structured JSON logging via `masterlog`: + +- **HTTP Request Logging** - method, path, status, duration, host, IP +- **Gateway Events** - startup, route registration, proxy configuration +- **Sensitive Field Pseudonymization** - user_id, email, ip fields are pseudonymized + +Example log output: +```json +{ + "level": "info", + "msg": "HTTP Request", + "method": "GET", + "path": "/api/v1/users", + "status": 200, + "duration": "45ms", + "host": "localhost:8080", + "ip": "127.0.0.1:52342" +} +``` diff --git a/.docs/configuration.md b/.docs/configuration.md index e7df178..c3f3f48 100644 --- a/.docs/configuration.md +++ b/.docs/configuration.md @@ -12,33 +12,30 @@ gateway: - request_id - real_ip - logger + - host -proxies: - - id: "proxy-id" - host: "example.com" - target: "http://backend:3000" +hosts: + - id: "host-001" + name: "localhost" + domain: "localhost:8080" + +targets: + - id: "target-001" + name: "httpbin" + url: "https://httpbin.org" apis: - - id: "api-id" - target: "https://api.example.com" + - id: "api-001" + host: "host-001" + target: "target-001" routes: - - id: "route-id" + - id: "route-001" + api: "api-001" path: "/api/v1/*" strip_prefix: enabled: true prefix: "/api/v1" - security: - auth: - enabled: true - type: "api_key" - header: "X-Api-Key" - path: - include: [] - exclude: [] - waf: - enabled: true - methods: ["GET", "POST"] ``` ## Sections @@ -61,37 +58,49 @@ Available global features: |---------|-------------| | `request_id` | Adds unique request ID to each request | | `real_ip` | Determines real client IP from headers | -| `logger` | Logs all HTTP requests | +| `logger` | Logs all HTTP requests with structured JSON | +| `host` | Logs the host header for each request | -### Proxies +### Hosts -Virtual hosting configuration for host-based routing. +Virtual hosting configuration for domain-based routing. | Field | Type | Description | |-------|------|-------------| -| `id` | string | Unique proxy identifier | -| `host` | string | Domain/host name to match | -| `target` | string | Backend URL to proxy to | +| `id` | string | Unique host identifier (referenced by APIs) | +| `name` | string | Human-readable name | +| `domain` | string | Domain/host name to match | + +### Targets + +Backend service definitions referenced by APIs. + +| Field | Type | Description | +|-------|------|-------------| +| `id` | string | Unique target identifier (referenced by APIs) | +| `name` | string | Human-readable name | +| `url` | string | Backend URL to proxy to | ### APIs -Backend service definitions referenced by routes. +Links hosts to backend targets. | Field | Type | Description | |-------|------|-------------| | `id` | string | Unique API identifier (referenced by routes) | -| `target` | string | Backend URL | +| `host` | string | Host ID to use | +| `target` | string | Target ID to proxy to | ### Routes -Route definitions with security policies. +Route definitions with path patterns and prefix stripping. | Field | Type | Description | |-------|------|-------------| -| `id` | string | Unique route identifier (must match API ID) | +| `id` | string | Unique route identifier | +| `api` | string | API ID to use for this route | | `path` | string | Chi route pattern (e.g., `/api/v1/*`) | | `strip_prefix` | object | Prefix stripping configuration | -| `security` | object | Security policies (auth, WAF) | #### Strip Prefix @@ -100,91 +109,112 @@ Route definitions with security policies. | `enabled` | boolean | Enable prefix stripping | | `prefix` | string | Prefix to remove from path | -#### Security - -##### Authentication - -| Field | Type | Description | -|-------|------|-------------| -| `enabled` | boolean | Enable authentication | -| `type` | string | Auth type (`api_key`, `session`, etc.) | -| `header` | string | Header name to validate | -| `path` | object | Path-based filtering | - -##### Auth Path Filtering - -| Field | Type | Description | -|-------|------|-------------| -| `include` | array | Paths that require auth (empty = all) | -| `exclude` | array | Paths that skip auth | - -**Include/Exclude Logic:** -- If `include` is set → only matching paths require auth -- If `include` is empty → all paths require auth except `exclude` - -Wildcards (`*`) are supported in path patterns. - -##### WAF (Web Application Firewall) - -| Field | Type | Description | -|-------|------|-------------| -| `enabled` | boolean | Enable WAF | -| `methods` | array | Allowed HTTP methods (`["*"]` for all) | - ## Example Configurations -### Public API (No Auth) +### Simple Proxy (No Prefix Stripping) ```yaml +gateway: + host: "0.0.0.0" + port: 8080 + features: + - logger + +hosts: + - id: "host-001" + name: "localhost" + domain: "localhost:8080" + +targets: + - id: "target-001" + name: "backend" + url: "https://api.example.com" + +apis: + - id: "api-001" + host: "host-001" + target: "target-001" + routes: - - id: "public-api" - path: "/public/*" - strip_prefix: - enabled: true - prefix: "/public" - security: - auth: - enabled: false - waf: - enabled: true - methods: ["GET", "POST"] + - id: "route-001" + api: "api-001" + path: "/api/*" ``` -### Protected API with API Key +**Request flow:** +- Client requests: `/api/users/123` +- Backend receives: `/api/users/123` + +### Prefix Stripping ```yaml routes: - - id: "protected-api" + - id: "route-001" + api: "api-001" path: "/api/v1/*" strip_prefix: enabled: true prefix: "/api/v1" - security: - auth: - enabled: true - type: "api_key" - header: "X-Api-Key" - waf: - enabled: true - methods: ["*"] ``` -### Mixed Auth (Path-based) +**Request flow:** +- Client requests: `/api/v1/users/123` +- Gateway strips: `/api/v1` +- Backend receives: `/users/123` + +### Multiple Routes ```yaml routes: - - id: "mixed-api" + - id: "public-route" + api: "api-001" + path: "/public/*" + strip_prefix: + enabled: true + prefix: "/public" + + - id: "api-route" + api: "api-001" + path: "/api/v1/*" + strip_prefix: + enabled: true + prefix: "/api/v1" +``` + +### Multiple Backends + +```yaml +hosts: + - id: "host-001" + name: "api-host" + domain: "api.example.com" + - id: "host-002" + name: "admin-host" + domain: "admin.example.com" + +targets: + - id: "target-001" + name: "api-backend" + url: "https://api-backend.internal" + - id: "target-002" + name: "admin-backend" + url: "https://admin-backend.internal" + +apis: + - id: "api-001" + host: "host-001" + target: "target-001" + - id: "api-002" + host: "host-002" + target: "target-002" + +routes: + - id: "route-001" + api: "api-001" path: "/api/*" - security: - auth: - enabled: true - header: "Authorization" - path: - include: ["/api/admin/*", "/api/users/*/profile"] - exclude: ["/api/health", "/api/public/*"] - waf: - enabled: true - methods: ["*"] + - id: "route-002" + api: "api-002" + path: "/admin/*" ``` ## Configuration Loading @@ -192,7 +222,7 @@ routes: The gateway loads configuration from a file path relative to the binary: ```go -cfg, err := config.NewFileConfig("../gateway.yaml") +cfg, err := config.NewFile("../gateway.yaml") ``` For Docker deployments, mount the config file: @@ -201,3 +231,16 @@ For Docker deployments, mount the config file: volumes: - ./gateway.yaml:/app/gateway.yaml:ro ``` + +## Chi Route Patterns + +The gateway uses chi/v5 routing patterns. Common patterns: + +| Pattern | Matches | Example | +|---------|---------|---------| +| `/api/*` | `/api/` and any subpath | `/api/users`, `/api/users/123` | +| `/api/v1/*` | `/api/v1/` and any subpath | `/api/v1/users` | +| `/users/{id}` | `/users/` with any value | `/users/123` | +| `/files/*` | `/files/` and any subpath | `/files/doc.pdf` | + +Note: `/*` matches zero or more path segments. diff --git a/.docs/index.md b/.docs/index.md index 02c2fe9..9afbd67 100644 --- a/.docs/index.md +++ b/.docs/index.md @@ -4,7 +4,7 @@ Welcome to the official documentation for the SecNex API Gateway. ## Overview -SecNex API Gateway is a high-performance, configurable API gateway built in Go. It provides reverse proxy capabilities with built-in security features including authentication and web application firewall (WAF) functionality. +SecNex API Gateway is a high-performance, configurable API gateway built in Go. It provides reverse proxy capabilities with path-based routing, prefix stripping, and structured logging. ## Quick Start @@ -28,11 +28,11 @@ docker compose up -d ## Features - **Reverse Proxy** Route requests to backend services -- **Authentication** Header-based authentication (API key, session tokens) -- **WAF** Web Application Firewall with method filtering and IP-based access control -- **Path-based Routing** Configurable route patterns with prefix stripping -- **Middleware Pipeline** Extensible middleware chain for custom logic -- **Logging** Structured logging with sensitive field pseudonymization +- **Path-based Routing** Configurable route patterns with chi/v5 +- **Prefix Stripping** Remove path prefixes before proxying +- **Host-based Routing** Virtual hosting support +- **Middleware Pipeline** Extensible middleware chain (request ID, real IP, logging) +- **Logging** Structured JSON logging with sensitive field pseudonymization ## Health Check diff --git a/.docs/usage.md b/.docs/usage.md index 4596110..b999881 100644 --- a/.docs/usage.md +++ b/.docs/usage.md @@ -47,14 +47,13 @@ Response: `OK` After configuring routes in `gateway.yaml`, make requests to the gateway: ```bash -# Example: Request to a route configured at /api/v1/dev/* -curl http://localhost:8080/api/v1/dev/users +# Example: Request to a route configured at /api/v1/* +curl http://localhost:8080/api/v1/users -# With API key authentication -curl -H "X-Api-Key: your-key-here" http://localhost:8080/api/v1/dev/data - -# With session authentication -curl -H "Authorization: Bearer token" http://localhost:8080/api/v1/protected +# All HTTP methods are supported +curl -X POST http://localhost:8080/api/v1/data +curl -X PUT http://localhost:8080/api/v1/data/123 +curl -X DELETE http://localhost:8080/api/v1/data/123 ``` ## Route Examples @@ -65,6 +64,7 @@ curl -H "Authorization: Bearer token" http://localhost:8080/api/v1/protected ```yaml routes: - id: "api" + api: "api-001" path: "/api/v1/*" strip_prefix: enabled: true @@ -76,65 +76,57 @@ routes: - Gateway strips: `/api/v1` - Backend receives: `/users/123` -### Authentication Example +### Multiple Routes Example **Configuration:** ```yaml -security: - auth: - enabled: true - type: "api_key" - header: "X-Api-Key" +routes: + - id: "public-route" + api: "api-001" + path: "/public/*" + strip_prefix: + enabled: true + prefix: "/public" + + - id: "api-route" + api: "api-001" + path: "/api/v1/*" + strip_prefix: + enabled: true + prefix: "/api/v1" ``` -**Valid request:** +**Requests:** ```bash -curl -H "X-Api-Key: secret123" http://localhost:8080/api/v1/data -``` +# Public route - backend receives /status +curl http://localhost:8080/public/status -**Invalid request (missing header):** -```bash -curl http://localhost:8080/api/v1/data -# Returns: 401 Unauthorized -``` - -### WAF Method Filtering Example - -**Configuration:** -```yaml -security: - waf: - enabled: true - methods: ["GET", "POST"] -``` - -**Allowed:** -```bash -curl -X GET http://localhost:8080/api/v1/data -curl -X POST http://localhost:8080/api/v1/data -``` - -**Blocked:** -```bash -curl -X DELETE http://localhost:8080/api/v1/data -# Returns: 403 Forbidden +# API route - backend receives /users +curl http://localhost:8080/api/v1/users ``` ## Logging -The gateway uses structured logging via `masterlog`. Logs include: +The gateway uses structured JSON logging via `masterlog`. All HTTP requests are logged with: - Request ID (if enabled) - Client IP (if real_ip enabled) - Request method and path -- Response status +- Response status code +- Request duration +- Host header **Example log output:** ```json { "level": "info", - "msg": "Registering route", - "path": "/api/v1/dev/*" + "msg": "HTTP Request", + "method": "GET", + "path": "/api/v1/users", + "status": 200, + "duration": "45ms", + "host": "localhost:8080", + "ip": "127.0.0.1:52342" } ``` @@ -147,30 +139,116 @@ The gateway uses structured logging via `masterlog`. Logs include: lsof -i :8080 ``` -2. Verify configuration file exists and is valid YAML +2. Verify configuration file exists and is valid YAML: +```bash +cat ../gateway.yaml +``` 3. Check logs for detailed error messages -### 401 Unauthorized +### 404 Not Found -- Verify auth header is included -- Check header name matches configuration -- Verify path is not excluded from auth - -### 403 Forbidden - -- Check WAF method configuration -- Verify HTTP method is allowed +- Verify the route path matches your request +- Check chi route pattern syntax (use `/*` for wildcards) +- Ensure the route is registered (check startup logs) ### Backend connection errors -- Verify API target URL is correct -- Check backend service is running -- Verify network connectivity +- Verify target URL is correct in configuration +- Check backend service is running and accessible +- Verify network connectivity from gateway to backend +- Check DNS resolution if using domain names + +### Route not matching + +- Ensure request path matches the route pattern exactly +- Remember that `/*` matches zero or more path segments +- Check that multiple routes don't have conflicting patterns ## Performance Considerations - The gateway uses Go's `httputil.ReverseProxy` for efficient proxying +- HTTP/2 is supported when both client and backend support it - Keep-alive connections are reused by default - Consider connection pooling for high-traffic scenarios - Monitor memory usage with high request volumes + +## Common Patterns + +### API Versioning + +```yaml +routes: + - id: "v1-api" + api: "api-001" + path: "/api/v1/*" + strip_prefix: + enabled: true + prefix: "/api/v1" + + - id: "v2-api" + api: "api-001" + path: "/api/v2/*" + strip_prefix: + enabled: true + prefix: "/api/v2" +``` + +### Microservices Routing + +```yaml +targets: + - id: "user-service" + name: "User Service" + url: "https://user-service.internal" + - id: "order-service" + name: "Order Service" + url: "https://order-service.internal" + +apis: + - id: "user-api" + host: "host-001" + target: "user-service" + - id: "order-api" + host: "host-001" + target: "order-service" + +routes: + - id: "users-route" + api: "user-api" + path: "/users/*" + strip_prefix: + enabled: true + prefix: "/users" + + - id: "orders-route" + api: "order-api" + path: "/orders/*" + strip_prefix: + enabled: true + prefix: "/orders" +``` + +### External API Proxy + +```yaml +targets: + - id: "external-api" + name: "External API" + url: "https://api.external.com" + +apis: + - id: "external" + host: "host-001" + target: "external-api" + +routes: + - id: "external-proxy" + api: "external" + path: "/proxy/external/*" + strip_prefix: + enabled: true + prefix: "/proxy/external" +``` + +Request to `/proxy/external/users` becomes `/users` on the external API. diff --git a/CLAUDE.md b/CLAUDE.md index 2482c4e..89a0972 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -SecNex API Gateway is a Go-based API gateway built with chi/v5 routing. It acts as a reverse proxy with configurable security features including authentication and WAF (Web Application Firewall) capabilities. +SecNex API Gateway is a Go-based API gateway built with chi/v5 routing. It acts as a reverse proxy with configurable features including path-based routing, prefix stripping, and structured logging. ## Build and Run @@ -23,77 +23,79 @@ The gateway loads configuration from `gateway.yaml` (sibling to the `app/` direc app/ ├── main.go # Entry point ├── config/ # Configuration loading and types -├── server/ # Gateway, routes, and proxy setup -├── middlewares/ # Auth and WAF middleware -├── handlers/ # Route handlers (currently empty) -└── res/ # HTTP error responses +├── server/ # Gateway, routes, hosts, targets, and API definitions +├── middlewares/ # HTTP middleware (logger, host) +└── utils/ # Utility functions ``` ### Core Components **Gateway (`server/gateway.go`)** - Main server using chi/v5 router -- Dynamically enables features via config: `request_id`, `real_ip`, `logger` +- Dynamically enables features via config: `request_id`, `real_ip`, `logger`, `host` +- Configures proxy directors for each API - Registers route handlers and starts HTTP server **Routes (`server/routes.go`)** - Creates handlers for each route configured in `gateway.yaml` - For each route: 1. Finds the matching API backend target - 2. Creates a reverse proxy using `httputil.NewSingleHostReverseProxy` - 3. Optionally strips prefix from the request path - 4. Applies WAF middleware (method filtering) - 5. Applies Auth middleware (header-based authentication) + 2. Creates a handler with optional strip prefix middleware + 3. Registers with chi router using `r.Handle()` (method-agnostic) -**Proxy (`server/proxy.go`)** -- Host-based virtual hosting proxy (separate from routes) -- Maps `proxy.host` to a backend target -- Currently unused in main.go but available +**API (`server/api.go`)** +- Links hosts to backend targets +- Implements `http.Handler` interface +- Delegates to the reverse proxy -**Configuration Flow** -1. `config.NewFileConfig("../gateway.yaml")` loads YAML +**Hosts (`server/host.go`)** +- Host definitions for virtual hosting +- Domain-based routing support + +**Targets (`server/target.go`)** +- Backend service definitions +- Creates `httputil.NewSingleHostReverseProxy` instances + +### Configuration Flow + +1. `config.NewFile("../gateway.yaml")` loads YAML 2. `server.NewGateway(cfg)` creates gateway with chi router -3. `server.NewRoutes(cfg.GetRoutes(), cfg.GetApis())` creates handlers -4. `gateway.SetRoutes(routes)` registers handlers +3. `server.NewRoutes(cfg, apis)` creates route handlers +4. `routes.Register(g.router)` registers handlers +5. Gateway starts HTTP server -### Middleware Chain (per route) +### Middleware Chain -Middlewares are applied in order via decorator pattern in `server/routes.go:createHandlers`: -1. StripPrefix (if enabled) - removes prefix from path before proxying -2. WAF - filters by HTTP method (or `["*"]` for all methods) -3. Auth - validates presence of auth header +**Global Middleware** (applied via chi middleware): +1. RequestID - adds unique request ID +2. RealIP - determines real client IP +3. Logger - structured JSON logging +4. Host - logs host header + +**Per-Route Middleware** (applied in order): +1. StripPrefix - removes prefix from path before proxying (if enabled) ### Key Implementation Details -**Auth Middleware** (`middlewares/auth.go`) -- Checks for presence of configured header (e.g., `X-Api-Key`, `Authorization`) -- Path-based filtering via `include`/`exclude` patterns: - - If `include` is set → only those paths require auth - - If `include` is empty → all paths require auth except `exclude` patterns -- Supports wildcard patterns (`*`) in path matching -- Deletes the auth header before forwarding to backend - -**WAF Configuration** (`middlewares/waf.go`) -- Currently only implements HTTP method filtering -- `methods: ["*"]` allows all methods -- `methods: ["GET"]` only allows GET +**Strip Prefix Middleware** (`server/routes.go`) +- Removes specified prefix from request path before proxying +- Ensures resulting path starts with `/` **Route Handler Pattern** Each route in `gateway.yaml` must have: -- `id` - must match an API entry -- `path` - chi route pattern (e.g., `/api/v1/dev/*`) +- `id` - unique route identifier +- `api` - API ID to use (references an API entry) +- `path` - chi route pattern (e.g., `/api/v1/*`) - `strip_prefix` - optional prefix removal -- `security.auth` - optional auth middleware -- `security.waf` - optional method filtering ### Module Structure The codebase uses an internal Go module: ``` -git.secnex.io/secnex/gateway +git.secnex.io/secnex/api-gateway ``` -Internal imports use this path (e.g., `git.secnex.io/secnex/gateway/config`). +Internal imports use this path (e.g., `git.secnex.io/secnex/api-gateway/config`). ### Logging @@ -101,3 +103,48 @@ Uses `git.secnex.io/secnex/masterlog` with: - JSON encoder - Pseudonymizer for sensitive fields (`user_id`, `email`, `ip`) - Debug-level logging enabled in main.go +- Custom `LoggerMiddleware` for structured HTTP request logging + +### Dependencies + +- `github.com/go-chi/chi/v5` - HTTP router +- `git.secnex.io/secnex/masterlog` - Structured logging +- `go.yaml.in/yaml/v3` - YAML configuration parsing +- `gorm.io/gorm` - Database ORM (for future use) +- `gorm.io/driver/postgres` - PostgreSQL driver (for future use) + +## Configuration Reference + +```yaml +gateway: + host: "0.0.0.0" + port: 8080 + features: + - request_id + - real_ip + - logger + - host + +hosts: + - id: "host-001" + name: "localhost" + domain: "localhost:8080" + +targets: + - id: "target-001" + name: "httpbin" + url: "https://httpbin.org" + +apis: + - id: "api-001" + host: "host-001" + target: "target-001" + +routes: + - id: "route-001" + api: "api-001" + path: "/api/v1/*" + strip_prefix: + enabled: true + prefix: "/api/v1" +``` diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c4b2581 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 SecNex GmbH + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 5710737..79bd4ec 100644 --- a/README.md +++ b/README.md @@ -1 +1,104 @@ -# SecNex API Gateway \ No newline at end of file +# SecNex API Gateway + +![Go Version](https://img.shields.io/badge/Go-1.25.5-00ADD8?style=flat&logo=go) +![License](https://img.shields.io/badge/License-MIT-blue?style=flat) + +A high-performance, configurable API gateway built in Go with chi/v5 routing. Provides reverse proxy capabilities with path-based routing, prefix stripping, and structured logging. + +## Features + +- **Reverse Proxy** - Route requests to backend services via HTTP reverse proxy +- **Path-based Routing** - Configurable route patterns with chi/v5 router +- **Prefix Stripping** - Remove path prefixes before forwarding to backends +- **Host-based Routing** - Virtual hosting support via host headers +- **Middleware Pipeline** - Extensible middleware chain (request ID, real IP, logging) +- **Structured Logging** - JSON logging with sensitive field pseudonymization + +## Quick Start + +```bash +# Clone the repository +git clone https://git.secnex.io/secnex/gateway.git +cd gateway/app + +# Run the gateway +go run main.go +``` + +The gateway loads configuration from `../gateway.yaml` and starts on the configured host/port (default: `0.0.0.0:8080`). + +## Health Check + +```bash +curl http://localhost:8080/_/health +``` + +Returns `200 OK` when the gateway is running. + +## Documentation + +- [Documentation](https://git.secnex.io/secnex/gateway/-/tree/main/.docs) + - [Architecture](https://git.secnex.io/secnex/gateway/-/blob/main/.docs/architecture.md) + - [Configuration](https://git.secnex.io/secnex/gateway/-/blob/main/.docs/configuration.md) + - [Usage](https://git.secnex.io/secnex/gateway/-/blob/main/.docs/usage.md) + - [Development](https://git.secnex.io/secnex/gateway/-/blob/main/.docs/development.md) + - [Deployment](https://git.secnex.io/secnex/gateway/-/blob/main/.docs/deployment.md) + +## Example Configuration + +```yaml +gateway: + host: "0.0.0.0" + port: 8080 + features: + - request_id + - real_ip + - logger + - host + +hosts: + - id: "host-001" + name: "localhost" + domain: "localhost:8080" + +targets: + - id: "target-001" + name: "httpbin" + url: "https://httpbin.org" + +apis: + - id: "api-001" + host: "host-001" + target: "target-001" + +routes: + - id: "route-001" + api: "api-001" + path: "/api/v1/*" + strip_prefix: + enabled: true + prefix: "/api/v1" +``` + +## Project Structure + +``` +app/ +├── main.go # Entry point +├── config/ # Configuration loading +├── server/ # Gateway, routes, proxy +├── middlewares/ # HTTP middleware +└── utils/ # Utility functions +``` + +## Requirements + +- Go 1.25.5 or later + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +--- + +Made with ❤️ by [SecNex](https://secnex.io) diff --git a/gateway.yaml b/gateway.yaml index 04af8dd..82e1b35 100644 --- a/gateway.yaml +++ b/gateway.yaml @@ -2,25 +2,36 @@ gateway: host: "0.0.0.0" port: 8080 features: + - host - request_id - real_ip - logger -proxies: - - id: "proxy-id" - host: "example.com" - target: "http://backend:3000" +hosts: + - id: "host-001" + name: "host-001" + domain: "localhost:8080" + - id: "host-002" + name: "host-002" + domain: "api.deinserver.co" + +targets: + - id: "target-001" + name: "target-001" + url: "https://httpbin.org" apis: - - id: "api-id" - target: "https://api.example.com" + - id: "api-001" + host: "host-001" + target: "target-001" routes: - - id: "route-id" - path: "/api/v1/*" + - id: "route-001" + api: "api-001" + path: "/api/v1/public/*" strip_prefix: enabled: true - prefix: "/api/v1" + prefix: "/api/v1/public" security: auth: enabled: true @@ -31,4 +42,4 @@ routes: exclude: [] waf: enabled: true - methods: ["GET", "POST"] \ No newline at end of file + methods: ["GET", "POST"]