Corridor is a lightweight, configurable, and extensible API Gateway written in Go. It's designed to be the front door for your microservices, handling concerns like routing, load balancing, authentication, rate limiting, and more.
Please note that Corridor is in active development, and while it is functional, it may not yet be suitable for production use. The project is evolving, and contributions are welcome!
- Dynamic Routing: Route incoming requests to appropriate backend services based on path, host, and HTTP methods.
- Load Balancing: Distribute traffic across multiple instances of your backend services.
- Strategies: Round Robin, Random, Least Connections.
- Health Checks: Actively monitor the health of upstream service instances and automatically remove unhealthy instances from the load balancing pool.
- Supports readiness and liveness probes.
- Plugin Architecture: Extend gateway functionality with a flexible plugin system.
- Authentication:
- JWT: Validate JSON Web Tokens.
- API Key: Secure access using API keys, with support for hashed key storage and dynamic loading from MongoDB.
- Rate Limiting: Protect your services from abuse with configurable rate limits (per IP or per header value).
- Caching: Cache responses from upstream services to improve performance and reduce load.
- Path Rewriting: Modify request paths before forwarding them to upstream services.
- Request ID: Ensure every request has a unique ID for tracing and logging.
- Logging: Configurable request/response logging (JSON or text).
- TLS Termination: Handle HTTPS traffic by configuring SSL/TLS certificates.
- Configuration Driven: All aspects of the gateway are controlled via a YAML configuration file.
- Graceful Shutdown: Ensures in-flight requests are handled before shutting down.
-
Prerequisites:
- Go (version 1.23+ recommended)
- (Optional) MongoDB if using MongoDB for API key storage.
-
Configuration:
- Corridor uses a YAML file for configuration (e.g.,
config.yaml
). - A detailed sample configuration file,
config-sample.yaml
, is provided in theapp/
directory. It showcases all available options with explanations.
- Corridor uses a YAML file for configuration (e.g.,
-
Running Corridor:
- For running Corridor using containerized environments, refer to the Running Corridor with Containers section below.
- For running Corridor directly from source, ensure you have Go installed and run:
./corridor -config /path/to/your/config.yaml
The gateway's behavior is entirely defined by a YAML configuration file. Below is an overview of the main sections. Refer to config-sample.yaml
for detailed examples and all options.
This section contains settings that apply to the entire gateway.
global:
port: 8080
log_level: "info" # debug, info, warn, error, fatal
enable_https: false
default_timeout_ms: 10000
proxy_headers:
- "X-Request-ID"
- "X-Forwarded-For"
# Server timeouts (read_timeout_sec, write_timeout_sec, etc.)
# Transport timeouts for upstream connections (transport_dial_timeout_sec, etc.)
# fail_on_service_init_error: false
Key options include:
port
: Listening port for HTTP (and HTTPS if enabled).log_level
: Verbosity of gateway logs.enable_https: true/false
: Enables HTTPS. Requirestls
section.default_timeout_ms
: Default timeout for upstream requests.proxy_headers
: Standard headers likeX-Forwarded-For
to manage.- Server and Transport Timeouts: Fine-grained control over connection timeouts.
fail_on_service_init_error
: Determines if gateway startup fails if a service can't initialize.
Define your backend services that Corridor will proxy requests to.
services:
- name: "my-backend-service"
upstream_urls:
- "http://localhost:8081"
- "http://service1-instance2:8081"
load_balancing:
strategy: "round-robin" # round-robin, random, least-connections
health_checks:
ready: "/health/ready" # Prioritized if available
live: "/health/live" # Fallback if ready not defined
interval: 15 # Seconds
timeout: 5 # Seconds
unhealthy_threshold: 3
healthy_threshold: 2
name
: A unique name for the service.upstream_urls
: A list of URLs for the instances of this service.load_balancing
:strategy
: How to pick an upstream instance.health_checks
: Configuration for active health monitoring of upstream instances.
Define rules to match incoming requests and forward them to the configured services.
routes:
- id: "unique-route-id"
paths:
- "/api/v1/users/*" # Matches /api/v1/users/ and anything under it
- "/profile" # Exact match
methods: ["GET", "POST"] # Optional: If empty, matches all methods
host: "api.example.com" # Optional: Exact or wildcard (*.example.com)
service: "my-backend-service" # Name of the service to route to
plugins: # Optional: List of plugins to apply to this route
- name: "rate-limiting"
config:
requests_per_minute: 60
- name: "path-rewrite"
config:
regex: "^/api/v1/users(/.*)$"
replacement: "/users$1"
id
: A unique identifier for the route.paths
: List of URL path patterns. Supports exact matches and trailing*
for prefix matching.methods
: List of HTTP methods (case-insensitive).host
: Host header to match (exact or wildcard).service
: The name of the service (from theservices
section) to forward the request to.plugins
: A list of plugin configurations specific to this route.
This section allows for global configuration of plugins. Route-specific plugin configurations can override these global settings.
plugins:
authentication:
provider: "none" # Default: none, jwt, api-key
# JWT Settings (if provider: "jwt")
# jwt_secret: "your-hmac-secret"
# jwt_public_key_file: "/path/to/public.pem"
# jwt_algorithm: "HS256" # or RS256, ES256, etc.
# jwt_issuer: "auth.example.com"
# jwt_audience: "api.example.com"
# token_header_name: "Authorization"
# token_header_scheme: "Bearer"
# API Key Settings (if provider: "api-key")
# api_key_header_name: "X-API-Key"
# MongoDB for API Keys (Recommended for production API Key provider)
# mongodb_uri: "mongodb://localhost:27017"
# mongodb_database: "corridor_db"
# mongodb_collection_apikeys: "api_keys"
# mongodb_timeout_ms: 5000
# mongodb_apikeys_refresh_interval_sec: 300 # How often to refresh keys from DB
# YAML-based API Keys (Fallback or for simpler setups)
# api_keys:
# - client_id: "client1"
# lookup_key: "sha256_hex_of_plaintext_key"
# hashed_value: "bcrypt_hash_of_plaintext_key"
rate-limiting:
strategy: "ip-address" # or "header"
# header_name: "X-Client-ID" # if strategy is "header"
# requests_per_minute: 1000 # Default global limit
caching:
enabled: true
default_ttl_seconds: 300
logging:
format: "json" # or "text"
# request-id:
# header_name: "X-Request-ID"
authentication
:provider
:jwt
,api-key
, ornone
.- JWT specific settings:
jwt_secret
,jwt_public_key_file
,jwt_algorithm
,jwt_issuer
,jwt_audience
,token_header_name
,token_header_scheme
. - API Key specific settings:
api_key_header_name
. - MongoDB settings for API keys:
mongodb_uri
,mongodb_database
, etc., for dynamic and secure key storage. api_keys
list for YAML-based (hashed) API key definitions.
rate-limiting
:strategy
(ip-address
orheader
),header_name
,requests_per_minute
.caching
:enabled
,default_ttl_seconds
.logging
:format
(json
ortext
).request-id
:header_name
.
# Configure SSL/TLS certificates if `global.enable_https` is true. Supports multiple certificates for different hostnames (SNI).
```yaml
tls:
certificates:
- hostname: "api.example.com"
cert_file: "/path/to/api.example.com.crt"
key_file: "/path/to/api.example.com.key"
# - hostname: "*.anotherdomain.com"
# cert_file: "/path/to/anotherdomain.com.crt"
# key_file: "/path/to/anotherdomain.com.key"
Corridor's plugin system allows you to modify requests and responses or implement custom logic at various stages of the request lifecycle. Plugins are applied in the order they are defined in a route's configuration.
Currently available plugins:
- Authentication (
authentication
): Secures your APIs using JWTs or API Keys. - Rate Limiting (
rate-limiting
): Protects services from being overwhelmed. - Caching (
caching
): Caches upstream responses. - Path Rewriting (
path-rewrite
): Modifies the request path using regex. - Request ID (
request-id
): Ensures each request has a unique identifier. - Logging (
logging
): Provides structured request logging (though this is more of a core feature configured under plugins).
If you are using the API Key authentication provider with hashed keys (recommended), Corridor includes a built-in command to help you generate the necessary hashes.
Run the following command:
./corridor keygen "your_desired_plaintext_api_key"
Ensure the Corridor binary is in your PATH or use the direct path to it. Quote the API key if it contains spaces or special characters.
This tool will output:
- A plaintext API key (to give to your client).
- A
lookup_key
(SHA256 hash of the plaintext key). - A
hashed_value
(bcrypt hash of the plaintext key).
The lookup_key
and hashed_value
are what you store in your config.yaml
(under plugins.authentication.api_keys
) or in your MongoDB collection for API keys. Corridor never stores the plaintext API key.
Example:
./corridor keygen "your_desired_plaintext_api_key"
--- Generated API Key Details ---
Plaintext API Key: your_desired_plaintext_api_key
Lookup Key (SHA256 Hex for 'lookup_key' field): your_generated_sha256_hex_string
Hashed Value (bcrypt for 'hashed_value' field): $2a$10$your_generated_bcrypt_hash_string
Instructions:
1. Provide the 'Plaintext API Key' to your client. Corridor does NOT store this.
2. Store the 'Lookup Key' and 'Hashed Value' in your Corridor configuration (e.g., config.yaml or MongoDB).
Corridor can be easily run using Docker, Docker Compose, or deployed to Kubernetes. The official Docker image is available on Docker Hub: cloudresty/corridor:latest
.
You will need a config.yaml
file for Corridor to run.
a) Mounting config.yaml
from the host (Recommended for local development/testing):
Create your config.yaml
file in a directory on your host machine, for example, in ./corridor-config/config.yaml
.
# Assuming your config.yaml is in ./corridor-config/config.yaml
# and you want to map port 8080 on your host to port 8080 in the container.
docker run -d --name corridor \
-p 8080:8080 \
-v $(pwd)/corridor-config:/corridor/config \
cloudresty/corridor:latest -config /corridor/config/config.yaml
-d
: Run in detached mode.--name corridor
: Assign a name to the container.-p 8080:8080
: Map port 8080 of the host to port 8080 of the container (adjust if yourconfig.yaml
uses a different port).-v $(pwd)/corridor-config:/corridor/config
: Mounts the./corridor-config
directory from your current host path into/corridor/config
inside the container. Corridor will then look for/corridor/config/config.yaml
.cloudresty/corridor:latest
: The Docker image.-config /corridor/config/config.yaml
: Tells Corridor inside the container where to find the config file.
b) Building a custom Docker image with config.yaml
embedded (Less flexible for config changes):
Create a Dockerfile
:
FROM cloudresty/corridor:latest
# Copy your local config.yaml into the image
COPY ./my-corridor-config/config.yaml /corridor/config/config.yaml
# The default command in the base image should already point to /corridor/config.yaml
# If not, or if you place it elsewhere, you might need to adjust the CMD or ENTRYPOINT.
# The base image likely uses CMD ["/corridor/corridor", "-config", "/corridor/config/config.yaml"] or similar.
Then build and run:
docker build -t my-custom-corridor .
docker run -d --name corridor -p 8080:8080 my-custom-corridor
Create a docker-compose.yaml
file:
version: '3.8'
services:
corridor:
image: cloudresty/corridor:latest # Make sure you use a specific version in production and not just 'latest' which can change over time the behavior of your service.
container_name: corridor-gateway
ports:
- "80:8080" # HostPort:ContainerPort (adjust if your config uses a different port)
volumes:
- ./config/corridor/config.yaml:/corridor/config/config.yaml # Mounts ./config/corridor/config.yaml on host to /corridor/config/config.yaml in container
command: ["/corridor/corridor", "-config", "/corridor/config/config.yaml"] # Tells Corridor where to find the config
restart: unless-stopped
Place your config.yaml
inside a ./config/corridor/
directory relative to your docker-compose.yaml
.
Then run:
docker compose up -d
To stop:
docker compose down
Here's a basic example of deploying Corridor to Kubernetes.
a) Create a ConfigMap
for your config.yaml
:
# Replace ./path/to/your/config.yaml with the actual path to your configuration file
kubectl create configmap corridor-config --from-file=config.yaml=./path/to/your/config.yaml
b) Create a Deployment
and Service
:
Create a file named corridor-deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: corridor-deployment
labels:
app: corridor
spec:
replicas: 2 # Adjust as needed
selector:
matchLabels:
app: corridor
template:
metadata:
labels:
app: corridor
spec:
containers:
- name: corridor
image: cloudresty/corridor:latest
args: ["-config", "/corridor/config/config.yaml"]
ports:
- containerPort: 8080 # Port Corridor listens on (from your config.yaml)
volumeMounts:
- name: config-volume
mountPath: /corridor/config # Mount the ConfigMap content here
volumes:
- name: config-volume
configMap:
name: corridor-config # Name of the ConfigMap created earlier
---
apiVersion: v1
kind: Service
metadata:
name: corridor-service
spec:
selector:
app: corridor
ports:
- protocol: TCP
port: 80 # Port the service will be exposed on within the cluster
targetPort: 8080 # Port the Corridor container is listening on
type: LoadBalancer # Or NodePort, ClusterIP depending on your needs
Apply the deployment:
kubectl apply -f corridor-deployment.yaml
This example uses a LoadBalancer
service type, which might provision an external IP depending on your Kubernetes cloud provider. Adjust the service type and ports as needed for your environment.
Corridor is an evolving project. Contributions are welcome! Please see the CONTRIBUTING.md file for guidelines.
This README provides a general overview. Always refer to config-sample.yaml
for the most up-to-date and detailed configuration options.
Made with