A reverse proxy for handling RPC requests with provider failover, configuration reloading, and JWT-based authentication.
graph TD
A[Client] --> B[NGINX Proxy]
B --> C{Valid Request?}
C -->|Yes| D[Providers Cache]
C -->|No| E[Return Error]
D --> F{Provider Available?}
F -->|Yes| G[Forward Request]
F -->|No| H[Return Error]
G --> I[Provider]
I --> J[Return Response]
J --> B
K[Providers checker service] --> D
L[Local providers.json] --> D
The nginx RPC proxy handles requests through the following process:
- Receives HTTP POST requests with chain/network in the URL path:
- Format 1:
/chain/network
- uses any available provider with failover across all providers - Format 2:
/chain/network/provider_type
- uses providers of the specified type with failover between instances of that type
- Format 1:
- Validates the chain/network format
- Looks up available providers for the requested chain/network combination
- For requests without provider_type:
- Attempts to forward the request to each available provider in sequence
- Returns the first successful response
- If all providers fail, returns a 502 error
- For requests with provider_type:
- Finds all providers matching the requested type
- Attempts to forward the request to each matching provider in sequence
- Returns the first successful response
- If no matching providers found, returns a 404 error
- If all matching providers fail, returns a 502 error
- Periodically reloads provider configuration to maintain up-to-date provider lists
The proxy now supports JSON-based configuration for authentication settings, shared with the Go Auth Service:
AUTH_CONFIG_FILE
- Path to JSON configuration file (default:/app/config.json
)GO_AUTH_SERVICE_URL
- URL for Go Auth Service (default:http://go-auth-service:8081
)CONFIG_HEALTH_CHECKER_URL
- URL for provider health checkerCUSTOM_DNS
- Custom DNS servers for provider resolutionRELOAD_INTERVAL
- Provider list reload interval in seconds
The proxy reads authentication settings from a shared JSON configuration file:
{
"algorithm": "argon2id",
"jwt_secret": "supersecret",
"puzzle_difficulty": 2,
"requests_per_token": 100,
"token_expiry_minutes": 10,
"argon2_params": {
"memory_kb": 32768,
"time": 1,
"threads": 4,
"key_len": 32
}
}
Used by nginx:
requests_per_token
- Maximum requests per JWT token before rate limitingtoken_expiry_minutes
- JWT token cache TTL in nginx shared memory
Used by Go Auth Service:
- All other parameters for puzzle generation and token validation
To run locally:
./build_docker_locally_run.sh
This will:
- Create a Docker network
- Build the Docker image
- Remove any existing container
- Start the proxy on port 8080
- Build the Docker image:
docker build -t rpc-proxy .
- Run the container:
docker run -d --name rpc-proxy \
--network rpc-network \
-p 8080:8080 \
-e CONFIG_HEALTH_CHECKER_URL=http://rpc-health-checker:8080/providers \
-e GO_AUTH_SERVICE_URL=http://go-auth-service:8081 \
-e AUTH_CONFIG_FILE=/app/config.json \
-v ./config.json:/app/config.json:ro \
rpc-proxy
The proxy fetches providers from:
- Primary source: URL specified in CONFIG_HEALTH_CHECKER_URL environment variable
- Fallback: Local providers.json file
The providers list is a JSON file with the following structure:
{
"chains": [
{
"name": "chain-name-lowercase",
"network": "network-name-lowercase",
"providers": [
{
"type": "provider-type",
"name": "provider-name",
"url": "http://provider1",
"authType": "no-auth|token-auth|basic-auth",
"authToken": "optional-token",
"authLogin": "optional-username",
"authPassword": "optional-password"
}
]
}
]
}
sequenceDiagram
participant Client
participant Proxy
participant AuthService
participant Provider
Client->>Proxy: POST /chain/network
Proxy->>Proxy: Check JWT token (cached)
alt Token not cached
Proxy->>AuthService: Validate JWT
AuthService-->>Proxy: Token valid/invalid
end
Proxy->>Proxy: Validate request
Proxy->>Proxy: Load providers
loop Try Providers
Proxy->>Provider: Forward request
Provider-->>Proxy: Return response/error
end
Proxy-->>Client: Return final response
Requests must be in one of two formats:
POST /chain/network
POST /chain/network/provider_type
With JSON body containing the RPC request.
Examples:
# Use any available provider with failover
curl -X POST http://localhost:8080/ethereum/mainnet \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
# Use Infura providers with failover between Infura instances
curl -X POST http://localhost:8080/ethereum/mainnet/infura \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
The proxy supports hybrid authentication:
Uses .htpasswd
file for credentials:
curl -X POST http://localhost:8080/ethereum/mainnet \
-u username:password \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
Requires solving an Argon2 puzzle to obtain a JWT token:
- Get puzzle:
GET /auth/puzzle
- Solve puzzle using Argon2id algorithm
- Submit solution:
POST /auth/solve
- Use returned JWT token in requests:
curl -X POST http://localhost:8080/ethereum/mainnet \
-H "Authorization: Bearer <jwt-token>" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
The nginx proxy uses Lua modules to manage configuration:
auth_config.lua
- Reads JSON config and provides authentication settingsauth_token_validator.lua
- Validates JWT tokens with caching and rate limitingprovider_loader.lua
- Manages provider list reloadingrequest_handler.lua
- Handles main request routing logic
Configuration is loaded once at worker startup and cached for performance.