init: Initial commit
This commit is contained in:
155
.docs/architecture.md
Normal file
155
.docs/architecture.md
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
# Architecture
|
||||||
|
|
||||||
|
## System Overview
|
||||||
|
|
||||||
|
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 │
|
||||||
|
└─────────────────────────┬───────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ Gateway (chi Router) │
|
||||||
|
│ ┌──────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Middleware Pipeline │ │
|
||||||
|
│ │ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │ │
|
||||||
|
│ │ │ Request ID │→ │ Real IP │→ │ Logger │ │ │
|
||||||
|
│ │ └────────────┘ └────────────┘ └──────────────┘ │ │
|
||||||
|
│ └──────────────────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────┬───────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ Route Handler │
|
||||||
|
│ ┌──────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Per-Route Middleware Chain │ │
|
||||||
|
│ │ ┌────────────┐ ┌─────────┐ ┌──────────────┐ │ │
|
||||||
|
│ │ │ Strip │→ │ WAF │→ │ Auth │ │ │
|
||||||
|
│ │ │ Prefix │ │ Filter │ │ Middleware │ │ │
|
||||||
|
│ │ └────────────┘ └─────────┘ └──────────────┘ │ │
|
||||||
|
│ └──────────────────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────┬───────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ Reverse Proxy (httputil.ReverseProxy) │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ Backend Service │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Architecture
|
||||||
|
|
||||||
|
### Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
app/
|
||||||
|
├── main.go # Application entry point
|
||||||
|
├── config/ # Configuration management
|
||||||
|
│ ├── config.go # Config interface
|
||||||
|
│ ├── file.go # File-based config loader
|
||||||
|
│ └── types.go # Configuration type definitions
|
||||||
|
├── server/ # Core server components
|
||||||
|
│ ├── gateway.go # Main gateway server
|
||||||
|
│ ├── routes.go # Route handler creation
|
||||||
|
│ └── proxy.go # Host-based virtual hosting
|
||||||
|
├── 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
|
||||||
|
```
|
||||||
|
|
||||||
|
### Core Components
|
||||||
|
|
||||||
|
#### Gateway (`server/gateway.go`)
|
||||||
|
|
||||||
|
The Gateway is the main server component that:
|
||||||
|
- Initializes the chi router
|
||||||
|
- Applies global middleware (request_id, real_ip, logger)
|
||||||
|
- 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
|
||||||
|
|
||||||
|
#### Configuration (`config/`)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
## Middleware Chain
|
||||||
|
|
||||||
|
### Global Middleware
|
||||||
|
|
||||||
|
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 |
|
||||||
|
|
||||||
|
### Per-Route Middleware
|
||||||
|
|
||||||
|
Applied in order 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
|
||||||
|
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()`
|
||||||
|
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
|
||||||
203
.docs/configuration.md
Normal file
203
.docs/configuration.md
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
# Configuration
|
||||||
|
|
||||||
|
The gateway is configured via a single YAML file (`gateway.yaml`). This document describes all available configuration options.
|
||||||
|
|
||||||
|
## Configuration File Structure
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
gateway:
|
||||||
|
host: "0.0.0.0"
|
||||||
|
port: 8080
|
||||||
|
features:
|
||||||
|
- request_id
|
||||||
|
- real_ip
|
||||||
|
- logger
|
||||||
|
|
||||||
|
proxies:
|
||||||
|
- id: "proxy-id"
|
||||||
|
host: "example.com"
|
||||||
|
target: "http://backend:3000"
|
||||||
|
|
||||||
|
apis:
|
||||||
|
- id: "api-id"
|
||||||
|
target: "https://api.example.com"
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- id: "route-id"
|
||||||
|
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
|
||||||
|
|
||||||
|
### Gateway
|
||||||
|
|
||||||
|
Global gateway configuration.
|
||||||
|
|
||||||
|
| Field | Type | Description | Default |
|
||||||
|
|-------|------|-------------|---------|
|
||||||
|
| `host` | string | Host address to bind to | Required |
|
||||||
|
| `port` | integer | Port number | Required |
|
||||||
|
| `features` | array | Global middleware features | Required |
|
||||||
|
|
||||||
|
#### Features
|
||||||
|
|
||||||
|
Available global features:
|
||||||
|
|
||||||
|
| Feature | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `request_id` | Adds unique request ID to each request |
|
||||||
|
| `real_ip` | Determines real client IP from headers |
|
||||||
|
| `logger` | Logs all HTTP requests |
|
||||||
|
|
||||||
|
### Proxies
|
||||||
|
|
||||||
|
Virtual hosting configuration for host-based routing.
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `id` | string | Unique proxy identifier |
|
||||||
|
| `host` | string | Domain/host name to match |
|
||||||
|
| `target` | string | Backend URL to proxy to |
|
||||||
|
|
||||||
|
### APIs
|
||||||
|
|
||||||
|
Backend service definitions referenced by routes.
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `id` | string | Unique API identifier (referenced by routes) |
|
||||||
|
| `target` | string | Backend URL |
|
||||||
|
|
||||||
|
### Routes
|
||||||
|
|
||||||
|
Route definitions with security policies.
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `id` | string | Unique route identifier (must match API ID) |
|
||||||
|
| `path` | string | Chi route pattern (e.g., `/api/v1/*`) |
|
||||||
|
| `strip_prefix` | object | Prefix stripping configuration |
|
||||||
|
| `security` | object | Security policies (auth, WAF) |
|
||||||
|
|
||||||
|
#### Strip Prefix
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `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)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
routes:
|
||||||
|
- id: "public-api"
|
||||||
|
path: "/public/*"
|
||||||
|
strip_prefix:
|
||||||
|
enabled: true
|
||||||
|
prefix: "/public"
|
||||||
|
security:
|
||||||
|
auth:
|
||||||
|
enabled: false
|
||||||
|
waf:
|
||||||
|
enabled: true
|
||||||
|
methods: ["GET", "POST"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Protected API with API Key
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
routes:
|
||||||
|
- id: "protected-api"
|
||||||
|
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)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
routes:
|
||||||
|
- id: "mixed-api"
|
||||||
|
path: "/api/*"
|
||||||
|
security:
|
||||||
|
auth:
|
||||||
|
enabled: true
|
||||||
|
header: "Authorization"
|
||||||
|
path:
|
||||||
|
include: ["/api/admin/*", "/api/users/*/profile"]
|
||||||
|
exclude: ["/api/health", "/api/public/*"]
|
||||||
|
waf:
|
||||||
|
enabled: true
|
||||||
|
methods: ["*"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Loading
|
||||||
|
|
||||||
|
The gateway loads configuration from a file path relative to the binary:
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg, err := config.NewFileConfig("../gateway.yaml")
|
||||||
|
```
|
||||||
|
|
||||||
|
For Docker deployments, mount the config file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- ./gateway.yaml:/app/gateway.yaml:ro
|
||||||
|
```
|
||||||
267
.docs/deployment.md
Normal file
267
.docs/deployment.md
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
# Deployment
|
||||||
|
|
||||||
|
## Docker Deployment
|
||||||
|
|
||||||
|
### Using Docker Compose (Recommended)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
1. Pull the image `git.secnex.io/secnex/api-gateway:latest`
|
||||||
|
2. Build locally if the image cannot be pulled
|
||||||
|
3. Start the gateway on port 8080
|
||||||
|
|
||||||
|
### Manual Docker Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build the image
|
||||||
|
docker build -t secnex-gateway .
|
||||||
|
|
||||||
|
# Run the container
|
||||||
|
docker run -d \
|
||||||
|
-p 8080:8080 \
|
||||||
|
-v $(pwd)/gateway.yaml:/app/gateway.yaml:ro \
|
||||||
|
--name gateway \
|
||||||
|
secnex-gateway
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Image from Registry
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker pull git.secnex.io/secnex/api-gateway:latest
|
||||||
|
docker run -d \
|
||||||
|
-p 8080:8080 \
|
||||||
|
-v $(pwd)/gateway.yaml:/app/gateway.yaml:ro \
|
||||||
|
--name gateway \
|
||||||
|
git.secnex.io/secnex/api-gateway:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Management
|
||||||
|
|
||||||
|
### Production Configuration
|
||||||
|
|
||||||
|
Create a production-specific `gateway.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
gateway:
|
||||||
|
host: "0.0.0.0"
|
||||||
|
port: 8080
|
||||||
|
features:
|
||||||
|
- request_id
|
||||||
|
- real_ip
|
||||||
|
- logger
|
||||||
|
|
||||||
|
# ... rest of configuration
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment-Specific Configs
|
||||||
|
|
||||||
|
Use multiple config files:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Development
|
||||||
|
docker run -v $(pwd)/gateway.dev.yaml:/app/gateway.yaml ...
|
||||||
|
|
||||||
|
# Production
|
||||||
|
docker run -v $(pwd)/gateway.prod.yaml:/app/gateway.yaml ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Kubernetes Deployment
|
||||||
|
|
||||||
|
### Deployment Manifest
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: api-gateway
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: api-gateway
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: api-gateway
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: gateway
|
||||||
|
image: git.secnex.io/secnex/api-gateway:latest
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
volumeMounts:
|
||||||
|
- name: config
|
||||||
|
mountPath: /app/gateway.yaml
|
||||||
|
subPath: gateway.yaml
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "128Mi"
|
||||||
|
cpu: "100m"
|
||||||
|
limits:
|
||||||
|
memory: "256Mi"
|
||||||
|
cpu: "500m"
|
||||||
|
volumes:
|
||||||
|
- name: config
|
||||||
|
configMap:
|
||||||
|
name: gateway-config
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: gateway-config
|
||||||
|
data:
|
||||||
|
gateway.yaml: |
|
||||||
|
gateway:
|
||||||
|
host: "0.0.0.0"
|
||||||
|
port: 8080
|
||||||
|
features:
|
||||||
|
- request_id
|
||||||
|
- real_ip
|
||||||
|
- logger
|
||||||
|
# ... rest of configuration
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: api-gateway
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: api-gateway
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 8080
|
||||||
|
type: LoadBalancer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Apply to Kubernetes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl apply -f k8s-deployment.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Health Checks
|
||||||
|
|
||||||
|
The gateway provides a health check endpoint:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8080/_/health
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kubernetes Liveness/Readiness Probes
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /_/health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /_/health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reverse Proxy Setup
|
||||||
|
|
||||||
|
### Nginx Reverse Proxy
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
upstream gateway {
|
||||||
|
server localhost:8080;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name api.example.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://gateway;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Monitoring
|
||||||
|
|
||||||
|
### Log Collection
|
||||||
|
|
||||||
|
Logs are output in JSON format. Configure your log collector:
|
||||||
|
|
||||||
|
**Fluentd example:**
|
||||||
|
```xml
|
||||||
|
<source>
|
||||||
|
@type tail
|
||||||
|
path /var/log/containers/gateway*.log
|
||||||
|
pos_file /var/log/gateway.log.pos
|
||||||
|
tag gateway.*
|
||||||
|
<parse>
|
||||||
|
@type json
|
||||||
|
</parse>
|
||||||
|
</source>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Metrics
|
||||||
|
|
||||||
|
Consider adding Prometheus metrics for production deployments:
|
||||||
|
- Request count by route
|
||||||
|
- Response time histograms
|
||||||
|
- Error rates
|
||||||
|
- Backend connection status
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
### Production Checklist
|
||||||
|
|
||||||
|
- [ ] Use TLS/HTTPS in production
|
||||||
|
- [ ] Restrict gateway configuration file permissions
|
||||||
|
- [ ] Use secrets management for API keys
|
||||||
|
- [ ] Enable rate limiting
|
||||||
|
- [ ] Configure WAF rules appropriately
|
||||||
|
- [ ] Regular security updates
|
||||||
|
- [ ] Log monitoring and alerting
|
||||||
|
- [ ] Backup configuration files
|
||||||
|
|
||||||
|
### TLS Termination
|
||||||
|
|
||||||
|
Options for TLS:
|
||||||
|
1. **At the gateway** - Configure Go server with TLS
|
||||||
|
2. **At reverse proxy** - Use Nginx/HAProxy with TLS
|
||||||
|
3. **At cloud load balancer** - Use AWS ALB, GCP LB, etc.
|
||||||
|
|
||||||
|
## Scaling
|
||||||
|
|
||||||
|
### Horizontal Scaling
|
||||||
|
|
||||||
|
Run multiple instances behind a load balancer:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml
|
||||||
|
services:
|
||||||
|
gateway:
|
||||||
|
deploy:
|
||||||
|
replicas: 3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Resource Limits
|
||||||
|
|
||||||
|
Configure appropriate resource limits:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: "512Mi"
|
||||||
|
cpu: "1000m"
|
||||||
|
reservations:
|
||||||
|
memory: "256Mi"
|
||||||
|
cpu: "500m"
|
||||||
|
```
|
||||||
185
.docs/development.md
Normal file
185
.docs/development.md
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
# Development
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Go 1.25.5 or later
|
||||||
|
- Git
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. Clone the repository:
|
||||||
|
```bash
|
||||||
|
git clone git.secnex.io/secnex/gateway.git
|
||||||
|
cd gateway
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install dependencies:
|
||||||
|
```bash
|
||||||
|
cd app
|
||||||
|
go mod download
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
gateway/
|
||||||
|
├── app/ # Application source code
|
||||||
|
│ ├── main.go # Entry point
|
||||||
|
│ ├── config/ # Configuration management
|
||||||
|
│ ├── server/ # Core server components
|
||||||
|
│ ├── middlewares/ # HTTP middleware
|
||||||
|
│ ├── handlers/ # Request handlers
|
||||||
|
│ └── res/ # Response utilities
|
||||||
|
├── .docs/ # Documentation
|
||||||
|
├── gateway.yaml # Configuration file
|
||||||
|
├── Dockerfile # Docker image definition
|
||||||
|
├── docker-compose.yml # Docker Compose configuration
|
||||||
|
└── .gitignore # Git ignore rules
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running Locally
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd app
|
||||||
|
go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all tests
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
# Run tests with coverage
|
||||||
|
go test -cover ./...
|
||||||
|
|
||||||
|
# Run tests for specific package
|
||||||
|
go test ./config
|
||||||
|
go test ./server
|
||||||
|
go test ./middlewares
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build binary
|
||||||
|
cd app
|
||||||
|
go build -o gateway main.go
|
||||||
|
|
||||||
|
# Build for Linux
|
||||||
|
GOOS=linux go build -o gateway-linux main.go
|
||||||
|
|
||||||
|
# Build for macOS (ARM64)
|
||||||
|
GOOS=darwin GOARCH=arm64 go build -o gateway-darwin-arm64 main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
- Follow standard Go conventions
|
||||||
|
- Use `gofmt` for formatting
|
||||||
|
- Keep functions focused and small
|
||||||
|
- Add comments for exported types and functions
|
||||||
|
- Use meaningful variable names
|
||||||
|
|
||||||
|
## Adding Features
|
||||||
|
|
||||||
|
### Adding a New Middleware
|
||||||
|
|
||||||
|
Create a new file in `app/middlewares/`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package middlewares
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func MyMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Your logic here
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then apply it in `server/routes.go`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
if route.Security.CustomMiddleware.Enabled {
|
||||||
|
handlers[route.Path] = middlewares.MyMiddleware(handlers[route.Path])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding a New Configuration Field
|
||||||
|
|
||||||
|
1. Update `config/types.go` to add the field to the struct
|
||||||
|
2. Update `gateway.yaml` with the new configuration
|
||||||
|
3. Update configuration documentation in `.docs/configuration.md`
|
||||||
|
|
||||||
|
### Adding a New Handler
|
||||||
|
|
||||||
|
Add your handler in `app/handlers/route.go`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func MyCustomHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Your logic here
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
When making changes to the codebase, update the relevant documentation:
|
||||||
|
|
||||||
|
- **Architecture changes** → `.docs/architecture.md`
|
||||||
|
- **Configuration changes** → `.docs/configuration.md`
|
||||||
|
- **New features or usage changes** → `.docs/usage.md`
|
||||||
|
- **Deployment changes** → `.docs/deployment.md`
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
### Enable Debug Logging
|
||||||
|
|
||||||
|
Debug logging is enabled by default in `main.go`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
masterlog.SetLevel(masterlog.LevelDebug)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Delve
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install Delve
|
||||||
|
go install github.com/go-delve/delve/cmd/dlv@latest
|
||||||
|
|
||||||
|
# Debug main.go
|
||||||
|
cd app
|
||||||
|
dlv debug main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
## Internal Module
|
||||||
|
|
||||||
|
The project uses an internal Go module:
|
||||||
|
```
|
||||||
|
git.secnex.io/secnex/gateway
|
||||||
|
```
|
||||||
|
|
||||||
|
When importing packages within the project, use this path:
|
||||||
|
```go
|
||||||
|
import "git.secnex.io/secnex/gateway/config"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Release Process
|
||||||
|
|
||||||
|
1. Update version in documentation
|
||||||
|
2. Tag the release:
|
||||||
|
```bash
|
||||||
|
git tag -a v1.0.0 -m "Release v1.0.0"
|
||||||
|
git push origin v1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Build Docker image:
|
||||||
|
```bash
|
||||||
|
docker build -t git.secnex.io/secnex/api-gateway:v1.0.0 .
|
||||||
|
```
|
||||||
45
.docs/index.md
Normal file
45
.docs/index.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# SecNex API Gateway Documentation
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run locally
|
||||||
|
cd app
|
||||||
|
go run main.go
|
||||||
|
|
||||||
|
# Run with Docker
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [Architecture](./architecture.md) - System architecture and component design
|
||||||
|
- [Configuration](./configuration.md) - Configuration reference and examples
|
||||||
|
- [Usage](./usage.md) - How to use the gateway
|
||||||
|
- [Development](./development.md) - Development setup and guide
|
||||||
|
- [Deployment](./deployment.md) - Deployment instructions
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
## Health Check
|
||||||
|
|
||||||
|
The gateway provides a health check endpoint:
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /_/health
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns `200 OK` when the gateway is running.
|
||||||
176
.docs/usage.md
Normal file
176
.docs/usage.md
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
# Usage
|
||||||
|
|
||||||
|
## Running the Gateway
|
||||||
|
|
||||||
|
### Local Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd app
|
||||||
|
go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
The gateway will start on the configured host and port (default: `0.0.0.0:8080`).
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pull and run (builds locally if image not available)
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker compose logs -f gateway
|
||||||
|
|
||||||
|
# Stop
|
||||||
|
docker compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build from Source
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd app
|
||||||
|
go build -o gateway main.go
|
||||||
|
./gateway
|
||||||
|
```
|
||||||
|
|
||||||
|
## Health Check
|
||||||
|
|
||||||
|
Check if the gateway is running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8080/_/health
|
||||||
|
```
|
||||||
|
|
||||||
|
Response: `OK`
|
||||||
|
|
||||||
|
## Making Requests
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
```
|
||||||
|
|
||||||
|
## Route Examples
|
||||||
|
|
||||||
|
### Strip Prefix Example
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
```yaml
|
||||||
|
routes:
|
||||||
|
- id: "api"
|
||||||
|
path: "/api/v1/*"
|
||||||
|
strip_prefix:
|
||||||
|
enabled: true
|
||||||
|
prefix: "/api/v1"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Request flow:**
|
||||||
|
- Client requests: `/api/v1/users/123`
|
||||||
|
- Gateway strips: `/api/v1`
|
||||||
|
- Backend receives: `/users/123`
|
||||||
|
|
||||||
|
### Authentication Example
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
```yaml
|
||||||
|
security:
|
||||||
|
auth:
|
||||||
|
enabled: true
|
||||||
|
type: "api_key"
|
||||||
|
header: "X-Api-Key"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Valid request:**
|
||||||
|
```bash
|
||||||
|
curl -H "X-Api-Key: secret123" http://localhost:8080/api/v1/data
|
||||||
|
```
|
||||||
|
|
||||||
|
**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
|
||||||
|
```
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
The gateway uses structured logging via `masterlog`. Logs include:
|
||||||
|
|
||||||
|
- Request ID (if enabled)
|
||||||
|
- Client IP (if real_ip enabled)
|
||||||
|
- Request method and path
|
||||||
|
- Response status
|
||||||
|
|
||||||
|
**Example log output:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"level": "info",
|
||||||
|
"msg": "Registering route",
|
||||||
|
"path": "/api/v1/dev/*"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Gateway fails to start
|
||||||
|
|
||||||
|
1. Check if port is already in use:
|
||||||
|
```bash
|
||||||
|
lsof -i :8080
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Verify configuration file exists and is valid YAML
|
||||||
|
|
||||||
|
3. Check logs for detailed error messages
|
||||||
|
|
||||||
|
### 401 Unauthorized
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
|
### Backend connection errors
|
||||||
|
|
||||||
|
- Verify API target URL is correct
|
||||||
|
- Check backend service is running
|
||||||
|
- Verify network connectivity
|
||||||
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
- The gateway uses Go's `httputil.ReverseProxy` for efficient proxying
|
||||||
|
- Keep-alive connections are reused by default
|
||||||
|
- Consider connection pooling for high-traffic scenarios
|
||||||
|
- Monitor memory usage with high request volumes
|
||||||
45
.gitignore
vendored
Normal file
45
.gitignore
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool
|
||||||
|
*.out
|
||||||
|
cover.html
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
go.work.sum
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
vendor/
|
||||||
|
|
||||||
|
# Build output
|
||||||
|
/bin/
|
||||||
|
/dist/
|
||||||
|
|
||||||
|
# IDE specific files
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Environment files
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
103
CLAUDE.md
Normal file
103
CLAUDE.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## Build and Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd app
|
||||||
|
go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
The gateway loads configuration from `gateway.yaml` (sibling to the `app/` directory).
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
### Core Components
|
||||||
|
|
||||||
|
**Gateway (`server/gateway.go`)**
|
||||||
|
- Main server using chi/v5 router
|
||||||
|
- Dynamically enables features via config: `request_id`, `real_ip`, `logger`
|
||||||
|
- 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)
|
||||||
|
|
||||||
|
**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
|
||||||
|
|
||||||
|
**Configuration Flow**
|
||||||
|
1. `config.NewFileConfig("../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
|
||||||
|
|
||||||
|
### Middleware Chain (per route)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
**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/*`)
|
||||||
|
- `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
|
||||||
|
```
|
||||||
|
|
||||||
|
Internal imports use this path (e.g., `git.secnex.io/secnex/gateway/config`).
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
|
||||||
|
Uses `git.secnex.io/secnex/masterlog` with:
|
||||||
|
- JSON encoder
|
||||||
|
- Pseudonymizer for sensitive fields (`user_id`, `email`, `ip`)
|
||||||
|
- Debug-level logging enabled in main.go
|
||||||
33
Dockerfile
Normal file
33
Dockerfile
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Build stage
|
||||||
|
FROM golang:1.25.5-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
# Copy go mod files (for better caching)
|
||||||
|
COPY app/go.mod app/go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY app/ ./
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o gateway .
|
||||||
|
|
||||||
|
# Runtime stage
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
RUN apk --no-cache add ca-certificates
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy binary from builder
|
||||||
|
COPY --from=builder /build/gateway .
|
||||||
|
|
||||||
|
# Copy configuration
|
||||||
|
COPY gateway.yaml .
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Run the gateway
|
||||||
|
CMD ["./gateway"]
|
||||||
11
app/config/config.go
Normal file
11
app/config/config.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type Config interface {
|
||||||
|
GetConfiguration() *Configuration
|
||||||
|
GetGatewayConfiguration() *GatewayConfiguration
|
||||||
|
GetFeatures() []string
|
||||||
|
GetRoutes() []RouteConfiguration
|
||||||
|
GetApis() []ApiConfiguration
|
||||||
|
GetHost() string
|
||||||
|
GetPort() int
|
||||||
|
}
|
||||||
60
app/config/file.go
Normal file
60
app/config/file.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"go.yaml.in/yaml/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileConfig struct {
|
||||||
|
filePath string
|
||||||
|
config *Configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFileConfig(filePath string) (*FileConfig, error) {
|
||||||
|
c := &FileConfig{filePath: filePath, config: &Configuration{}}
|
||||||
|
if err := c.loadConfig(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FileConfig) loadConfig() error {
|
||||||
|
data, err := os.ReadFile(c.filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return yaml.Unmarshal(data, c.config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FileConfig) GetConfiguration() *Configuration {
|
||||||
|
return c.config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FileConfig) GetGatewayConfiguration() *GatewayConfiguration {
|
||||||
|
return &c.config.Gateway
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FileConfig) GetRoutes() []RouteConfiguration {
|
||||||
|
return c.config.Routes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FileConfig) GetProxies() []ProxyConfiguration {
|
||||||
|
return c.config.Proxies
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FileConfig) GetHost() string {
|
||||||
|
return c.config.Gateway.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FileConfig) GetPort() int {
|
||||||
|
return c.config.Gateway.Port
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FileConfig) GetApis() []ApiConfiguration {
|
||||||
|
return c.config.Apis
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FileConfig) GetFeatures() []string {
|
||||||
|
return c.config.Gateway.Features
|
||||||
|
}
|
||||||
57
app/config/types.go
Normal file
57
app/config/types.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type Configuration struct {
|
||||||
|
Gateway GatewayConfiguration `yaml:"gateway"`
|
||||||
|
Apis []ApiConfiguration `yaml:"apis"`
|
||||||
|
Routes []RouteConfiguration `yaml:"routes"`
|
||||||
|
Proxies []ProxyConfiguration `yaml:"proxies"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GatewayConfiguration struct {
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
Features []string `yaml:"features"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RouteConfiguration struct {
|
||||||
|
ID string `yaml:"id"`
|
||||||
|
Path string `yaml:"path"`
|
||||||
|
StripPrefix struct {
|
||||||
|
Enabled bool `yaml:"enabled"`
|
||||||
|
Prefix string `yaml:"prefix"`
|
||||||
|
} `yaml:"strip_prefix"`
|
||||||
|
Security SecurityConfiguration `yaml:"security"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SecurityConfiguration struct {
|
||||||
|
Auth AuthConfiguration `yaml:"auth"`
|
||||||
|
WAF WAFConfiguration `yaml:"waf"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WAFConfiguration struct {
|
||||||
|
Enabled bool `yaml:"enabled"`
|
||||||
|
Methods []string `yaml:"methods"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthConfiguration struct {
|
||||||
|
Enabled bool `yaml:"enabled"`
|
||||||
|
Type string `yaml:"type"`
|
||||||
|
Header string `yaml:"header"`
|
||||||
|
Path AuthPathConfiguration `yaml:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthPathConfiguration struct {
|
||||||
|
Include []string `yaml:"include"`
|
||||||
|
Exclude []string `yaml:"exclude"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProxyConfiguration struct {
|
||||||
|
ID string `yaml:"id"`
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
Target string `yaml:"target"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApiConfiguration struct {
|
||||||
|
ID string `yaml:"id"`
|
||||||
|
Target string `yaml:"target"`
|
||||||
|
}
|
||||||
9
app/go.mod
Normal file
9
app/go.mod
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
module git.secnex.io/secnex/api-gateway
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.secnex.io/secnex/masterlog v0.1.0
|
||||||
|
github.com/go-chi/chi/v5 v5.2.4
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4
|
||||||
|
)
|
||||||
8
app/go.sum
Normal file
8
app/go.sum
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
git.secnex.io/secnex/masterlog v0.1.0 h1:74j9CATpfeK0lxpWIQC9ag9083akwG8khi5BwLedD8E=
|
||||||
|
git.secnex.io/secnex/masterlog v0.1.0/go.mod h1:OnDlwEzdkKMnqY+G5O9kHdhoJ6fH1llbVdXpgSc5SdM=
|
||||||
|
github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4=
|
||||||
|
github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
1
app/handlers/route.go
Normal file
1
app/handlers/route.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package handlers
|
||||||
29
app/main.go
Normal file
29
app/main.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/api-gateway/config"
|
||||||
|
"git.secnex.io/secnex/api-gateway/server"
|
||||||
|
"git.secnex.io/secnex/masterlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
masterlog.SetLevel(masterlog.LevelDebug)
|
||||||
|
masterlog.AddEncoder(&masterlog.JSONEncoder{})
|
||||||
|
pseudonymizer := masterlog.NewPseudonymizerFromString("your-secret-key")
|
||||||
|
masterlog.SetPseudonymizer(pseudonymizer)
|
||||||
|
masterlog.AddSensitiveFields("user_id", "email", "ip")
|
||||||
|
|
||||||
|
cfg, err := config.NewFileConfig("../gateway.yaml")
|
||||||
|
if err != nil {
|
||||||
|
masterlog.Error("Failed to load config", map[string]interface{}{
|
||||||
|
"error": err,
|
||||||
|
})
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
gateway := server.NewGateway(cfg)
|
||||||
|
routes := server.NewRoutes(cfg.GetRoutes(), cfg.GetApis())
|
||||||
|
gateway.SetRoutes(routes)
|
||||||
|
gateway.Start()
|
||||||
|
}
|
||||||
61
app/middlewares/auth.go
Normal file
61
app/middlewares/auth.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/api-gateway/config"
|
||||||
|
"git.secnex.io/secnex/api-gateway/res"
|
||||||
|
"git.secnex.io/secnex/masterlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func authPathMatches(pattern, requestPath string) bool {
|
||||||
|
if pattern == "*" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if pattern == requestPath {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if strings.Contains(pattern, "*") {
|
||||||
|
matched, _ := path.Match(pattern, requestPath)
|
||||||
|
return matched
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func Auth(next http.Handler, authType string, authHeader string, authPath config.AuthPathConfiguration) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
masterlog.Debug("Auth middleware", map[string]interface{}{
|
||||||
|
"path": r.URL.Path,
|
||||||
|
"include": authPath.Include,
|
||||||
|
"exclude": authPath.Exclude,
|
||||||
|
})
|
||||||
|
if len(authPath.Include) > 0 {
|
||||||
|
matched := false
|
||||||
|
for _, include := range authPath.Include {
|
||||||
|
if authPathMatches(include, r.URL.Path) {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, exclude := range authPath.Exclude {
|
||||||
|
if authPathMatches(exclude, r.URL.Path) {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.Header.Get(authHeader) == "" {
|
||||||
|
res.Unauthorized(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.Header.Del(authHeader)
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
27
app/middlewares/waf.go
Normal file
27
app/middlewares/waf.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/api-gateway/config"
|
||||||
|
"git.secnex.io/secnex/api-gateway/res"
|
||||||
|
)
|
||||||
|
|
||||||
|
func WAF(next http.Handler, wafConfig config.WAFConfiguration) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !wafConfig.Enabled {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if slices.Contains(wafConfig.Methods, "*") {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !slices.Contains(wafConfig.Methods, r.Method) {
|
||||||
|
res.Forbidden(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
21
app/res/error.go
Normal file
21
app/res/error.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package res
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrorResponse struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
Code int `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unauthorized(w http.ResponseWriter) {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
json.NewEncoder(w).Encode(ErrorResponse{Message: "Unauthorized", Code: http.StatusUnauthorized})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Forbidden(w http.ResponseWriter) {
|
||||||
|
w.WriteHeader(http.StatusForbidden)
|
||||||
|
json.NewEncoder(w).Encode(ErrorResponse{Message: "Forbidden", Code: http.StatusForbidden})
|
||||||
|
}
|
||||||
67
app/server/gateway.go
Normal file
67
app/server/gateway.go
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/api-gateway/config"
|
||||||
|
"git.secnex.io/secnex/masterlog"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Gateway struct {
|
||||||
|
router *chi.Mux
|
||||||
|
config config.Config
|
||||||
|
routes *Routes
|
||||||
|
proxy *Proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGateway(config config.Config) *Gateway {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
|
||||||
|
for _, feature := range config.GetFeatures() {
|
||||||
|
switch feature {
|
||||||
|
case "request_id":
|
||||||
|
r.Use(middleware.RequestID)
|
||||||
|
case "real_ip":
|
||||||
|
r.Use(middleware.RealIP)
|
||||||
|
case "logger":
|
||||||
|
r.Use(middleware.Logger)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Route("/_/health", func(r chi.Router) {
|
||||||
|
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte("OK"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return &Gateway{config: config, router: r, routes: nil, proxy: nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Gateway) SetRoutes(routes *Routes) {
|
||||||
|
g.routes = routes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Gateway) SetProxy(proxy *Proxy) {
|
||||||
|
g.proxy = proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Gateway) Start() {
|
||||||
|
masterlog.Info("Starting gateway", map[string]interface{}{
|
||||||
|
"host": g.config.GetGatewayConfiguration().Host,
|
||||||
|
"port": g.config.GetGatewayConfiguration().Port,
|
||||||
|
})
|
||||||
|
for path, handler := range g.routes.handlers {
|
||||||
|
masterlog.Info("Registering route", map[string]interface{}{
|
||||||
|
"path": path,
|
||||||
|
})
|
||||||
|
g.router.Handle(path, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
gatewayConfig := g.config.GetGatewayConfiguration()
|
||||||
|
|
||||||
|
http.ListenAndServe(fmt.Sprintf("%s:%d", gatewayConfig.Host, gatewayConfig.Port), g.router)
|
||||||
|
}
|
||||||
42
app/server/proxy.go
Normal file
42
app/server/proxy.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/api-gateway/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Proxy struct {
|
||||||
|
proxies []config.ProxyConfiguration
|
||||||
|
handlers map[string]http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProxy(proxies []config.ProxyConfiguration) *Proxy {
|
||||||
|
handlers := make(map[string]http.Handler)
|
||||||
|
for _, proxy := range proxies {
|
||||||
|
backend, err := url.Parse(proxy.Target)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to parse proxy target: %v", err)
|
||||||
|
}
|
||||||
|
p := httputil.NewSingleHostReverseProxy(backend)
|
||||||
|
originalDirector := p.Director
|
||||||
|
p.Director = func(r *http.Request) {
|
||||||
|
originalDirector(r)
|
||||||
|
r.Host = backend.Host
|
||||||
|
}
|
||||||
|
handlers[proxy.Host] = p
|
||||||
|
}
|
||||||
|
return &Proxy{proxies: proxies, handlers: handlers}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
handler, ok := p.handlers[r.Host]
|
||||||
|
if !ok {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handler.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
85
app/server/routes.go
Normal file
85
app/server/routes.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"git.secnex.io/secnex/api-gateway/config"
|
||||||
|
"git.secnex.io/secnex/api-gateway/middlewares"
|
||||||
|
"git.secnex.io/secnex/masterlog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Routes struct {
|
||||||
|
routes []config.RouteConfiguration
|
||||||
|
handlers map[string]http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRoutes(routes []config.RouteConfiguration, apis []config.ApiConfiguration) *Routes {
|
||||||
|
handlers := createHandlers(routes, apis)
|
||||||
|
return &Routes{routes: routes, handlers: handlers}
|
||||||
|
}
|
||||||
|
|
||||||
|
func findApi(apis []config.ApiConfiguration, id string) *config.ApiConfiguration {
|
||||||
|
for _, api := range apis {
|
||||||
|
if api.ID == id {
|
||||||
|
return &api
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createHandlers(routes []config.RouteConfiguration, apis []config.ApiConfiguration) map[string]http.Handler {
|
||||||
|
handlers := make(map[string]http.Handler)
|
||||||
|
for _, route := range routes {
|
||||||
|
masterlog.Debug("Creating handler for route", map[string]interface{}{
|
||||||
|
"path": route.Path,
|
||||||
|
"id": route.ID,
|
||||||
|
})
|
||||||
|
api := findApi(apis, route.ID)
|
||||||
|
if api == nil {
|
||||||
|
log.Fatalf("API not found: %s", route.ID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
backendUrl, err := url.Parse(
|
||||||
|
api.Target,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to parse backend URL: %v", err)
|
||||||
|
}
|
||||||
|
proxy := httputil.NewSingleHostReverseProxy(backendUrl)
|
||||||
|
handlers[route.Path] = proxy
|
||||||
|
if route.StripPrefix.Enabled {
|
||||||
|
masterlog.Debug("Stripping prefix", map[string]interface{}{
|
||||||
|
"id": route.ID,
|
||||||
|
"path": route.Path,
|
||||||
|
"prefix": route.StripPrefix.Prefix,
|
||||||
|
})
|
||||||
|
handlers[route.Path] = http.StripPrefix(route.StripPrefix.Prefix, handlers[route.Path])
|
||||||
|
}
|
||||||
|
if route.Security.WAF.Enabled {
|
||||||
|
masterlog.Debug("Applying WAF middleware", map[string]interface{}{
|
||||||
|
"id": route.ID,
|
||||||
|
"path": route.Path,
|
||||||
|
"methods": route.Security.WAF.Methods,
|
||||||
|
})
|
||||||
|
handlers[route.Path] = middlewares.WAF(handlers[route.Path], route.Security.WAF)
|
||||||
|
}
|
||||||
|
if route.Security.Auth.Enabled {
|
||||||
|
masterlog.Debug("Applying auth middleware", map[string]interface{}{
|
||||||
|
"id": route.ID,
|
||||||
|
"path": route.Path,
|
||||||
|
"type": route.Security.Auth.Type,
|
||||||
|
"header": route.Security.Auth.Header,
|
||||||
|
})
|
||||||
|
handlers[route.Path] = middlewares.Auth(
|
||||||
|
handlers[route.Path],
|
||||||
|
route.Security.Auth.Type,
|
||||||
|
route.Security.Auth.Header,
|
||||||
|
route.Security.Auth.Path,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handlers
|
||||||
|
}
|
||||||
11
docker-compose.yml
Normal file
11
docker-compose.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
services:
|
||||||
|
gateway:
|
||||||
|
image: git.secnex.io/secnex/api-gateway:latest
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
volumes:
|
||||||
|
- ./gateway.yaml:/app/gateway.yaml:ro
|
||||||
|
restart: unless-stopped
|
||||||
57
gateway.yaml
Normal file
57
gateway.yaml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
gateway:
|
||||||
|
host: "0.0.0.0"
|
||||||
|
port: 8080
|
||||||
|
features:
|
||||||
|
- request_id
|
||||||
|
- real_ip
|
||||||
|
- logger
|
||||||
|
|
||||||
|
proxies:
|
||||||
|
- id: "secnex-dev"
|
||||||
|
host: "dev.secnex.io"
|
||||||
|
target: "http://localhost:3003"
|
||||||
|
|
||||||
|
apis:
|
||||||
|
- id: "secnex-dev"
|
||||||
|
target: "https://httpbin.org"
|
||||||
|
- id: "secnex-public"
|
||||||
|
target: "https://httpbin.org"
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- id: "secnex-dev"
|
||||||
|
path: "/api/v1/dev/*"
|
||||||
|
strip_prefix:
|
||||||
|
enabled: true
|
||||||
|
prefix: "/api/v1/dev"
|
||||||
|
security:
|
||||||
|
auth:
|
||||||
|
enabled: true
|
||||||
|
type: "api_key"
|
||||||
|
header: "X-Api-Key"
|
||||||
|
path:
|
||||||
|
include: []
|
||||||
|
exclude: []
|
||||||
|
waf:
|
||||||
|
enabled: true
|
||||||
|
methods: ["*"]
|
||||||
|
ip_addresses:
|
||||||
|
allow:
|
||||||
|
- "127.0.0.1"
|
||||||
|
block:
|
||||||
|
- "127.0.0.2"
|
||||||
|
- id: "secnex-public"
|
||||||
|
path: "/api/v1/public/*"
|
||||||
|
strip_prefix:
|
||||||
|
enabled: true
|
||||||
|
prefix: "/api/v1/public"
|
||||||
|
security:
|
||||||
|
auth:
|
||||||
|
enabled: false
|
||||||
|
type: "session"
|
||||||
|
header: "Authorization"
|
||||||
|
path:
|
||||||
|
include: []
|
||||||
|
exclude: []
|
||||||
|
waf:
|
||||||
|
enabled: true
|
||||||
|
methods: ["GET"]
|
||||||
Reference in New Issue
Block a user