Files
proxmox/docs/04-configuration/RPC_JWT_AUTHENTICATION.md
defiQUG cb47cce074 Complete markdown files cleanup and organization
- Organized 252 files across project
- Root directory: 187 → 2 files (98.9% reduction)
- Moved configuration guides to docs/04-configuration/
- Moved troubleshooting guides to docs/09-troubleshooting/
- Moved quick start guides to docs/01-getting-started/
- Moved reports to reports/ directory
- Archived temporary files
- Generated comprehensive reports and documentation
- Created maintenance scripts and guides

All files organized according to established standards.
2026-01-06 01:46:25 -08:00

7.0 KiB

JWT Authentication for Permissioned RPC Endpoints

Last Updated: 2025-12-24
Status: Active Configuration


Overview

JWT (JSON Web Token) authentication has been configured for the Permissioned RPC endpoints to provide secure, token-based access control.

Endpoints with JWT Authentication

  • HTTP RPC: https://rpc-http-prv.d-bis.org
  • WebSocket RPC: wss://rpc-ws-prv.d-bis.org

Endpoints without Authentication (Public)

  • HTTP RPC: https://rpc-http-pub.d-bis.org
  • WebSocket RPC: wss://rpc-ws-pub.d-bis.org

Architecture

VMID Mappings

VMID Type Domain Authentication IP
2501 Permissioned RPC rpc-http-prv.d-bis.org
rpc-ws-prv.d-bis.org
JWT Required 192.168.11.251
2502 Public RPC rpc-http-pub.d-bis.org
rpc-ws-pub.d-bis.org
No Auth 192.168.11.252

Request Flow with JWT

  1. Client makes request to https://rpc-http-prv.d-bis.org
  2. Nginx receives request and extracts JWT token from Authorization: Bearer <token> header
  3. Lua Script validates JWT token using secret key
  4. If valid: Request is proxied to Besu RPC (127.0.0.1:8545)
  5. If invalid: Returns 401 Unauthorized with error message

Setup

1. Configure JWT Authentication

Run the configuration script:

cd /home/intlc/projects/proxmox
./scripts/configure-nginx-jwt-auth.sh

This script will:

  • Install required packages (nginx, lua, lua-resty-jwt)
  • Generate JWT secret key
  • Configure Nginx with JWT validation
  • Set up both HTTP and WebSocket endpoints

2. Generate JWT Tokens

Use the token generation script:

# Generate token with default settings (username: rpc-user, expiry: 365 days)
./scripts/generate-jwt-token.sh

# Generate token with custom username and expiry
./scripts/generate-jwt-token.sh my-username 30  # 30 days expiry

The script will output:

  • The JWT token
  • Usage examples for testing

Usage

HTTP RPC with JWT

# Test with curl
curl -k \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' \
  https://rpc-http-prv.d-bis.org

WebSocket RPC with JWT

For WebSocket connections, include the JWT token in the Authorization header during the initial HTTP upgrade request:

// JavaScript example
const ws = new WebSocket('wss://rpc-ws-prv.d-bis.org', {
  headers: {
    'Authorization': 'Bearer YOUR_JWT_TOKEN'
  }
});

Using with MetaMask or dApps

Most Ethereum clients don't support custom headers. For these cases, you can:

  1. Use a proxy service that adds the JWT token
  2. Use the public endpoint (rpc-http-pub.d-bis.org) for read-only operations
  3. Implement custom authentication in your dApp

Token Management

Token Structure

JWT tokens contain:

  • Header: Algorithm (HS256) and type (JWT)
  • Payload:
    • sub: Username/subject
    • iat: Issued at (timestamp)
    • exp: Expiration (timestamp)
  • Signature: HMAC-SHA256 signature using the secret key

Token Expiry

Tokens expire after the specified number of days. To generate a new token:

./scripts/generate-jwt-token.sh username days

Revoking Tokens

JWT tokens cannot be revoked individually without changing the secret key. To revoke all tokens:

  1. Generate a new JWT secret on VMID 2501:

    ssh root@192.168.11.10 "pct exec 2501 -- openssl rand -base64 32 > /etc/nginx/jwt_secret"
    
  2. Restart Nginx:

    ssh root@192.168.11.10 "pct exec 2501 -- systemctl restart nginx"
    
  3. Generate new tokens for authorized users


Security Considerations

Secret Key Management

  • Location: /etc/nginx/jwt_secret on VMID 2501
  • Permissions: 600 (readable only by root)
  • Backup: Store securely, do not commit to version control

Best Practices

  1. Use strong secret keys: The script generates 32-byte random keys
  2. Set appropriate expiry: Don't create tokens with excessive expiry times
  3. Rotate secrets periodically: Change the secret key and regenerate tokens
  4. Monitor access logs: Check /var/log/nginx/rpc-http-prv-access.log for unauthorized attempts
  5. Use HTTPS only: All endpoints use HTTPS (port 443)

Rate Limiting

Consider adding rate limiting to prevent abuse:

limit_req_zone $binary_remote_addr zone=jwt_limit:10m rate=10r/s;

location / {
    limit_req zone=jwt_limit burst=20 nodelay;
    # ... JWT validation ...
}

Troubleshooting

401 Unauthorized

Error: {"error": "Missing Authorization header"}

Solution: Include the Authorization header:

curl -H "Authorization: Bearer YOUR_TOKEN" ...

Error: {"error": "Invalid or expired token"}

Solution:

  • Check token is correct (no extra spaces)
  • Verify token hasn't expired
  • Generate a new token if needed

500 Internal Server Error

Error: {"error": "Internal server error"}

Solution:

  • Check JWT secret exists: pct exec 2501 -- cat /etc/nginx/jwt_secret
  • Check lua-resty-jwt is installed: pct exec 2501 -- ls /usr/share/lua/5.1/resty/jwt.lua
  • Check Nginx error logs: pct exec 2501 -- tail -f /var/log/nginx/rpc-http-prv-error.log

Token Validation Fails

  1. Verify secret key matches:

    # On VMID 2501
    cat /etc/nginx/jwt_secret
    
  2. Regenerate token using the same secret:

    ./scripts/generate-jwt-token.sh
    
  3. Check token format: Should be three parts separated by dots: header.payload.signature


Testing

Test JWT Authentication

# 1. Generate a token
TOKEN=$(./scripts/generate-jwt-token.sh test-user 365 | grep -A 1 "Token:" | tail -1)

# 2. Test HTTP endpoint
curl -k \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' \
  https://rpc-http-prv.d-bis.org

# 3. Test without token (should fail)
curl -k \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' \
  https://rpc-http-prv.d-bis.org
# Expected: {"error": "Missing Authorization header"}

Test Health Endpoint (No Auth Required)

curl -k https://rpc-http-prv.d-bis.org/health
# Expected: healthy


Quick Reference

Generate Token:

./scripts/generate-jwt-token.sh [username] [days]

Use Token:

curl -H "Authorization: Bearer <token>" https://rpc-http-prv.d-bis.org

Check Secret:

ssh root@192.168.11.10 "pct exec 2501 -- cat /etc/nginx/jwt_secret"

View Logs:

ssh root@192.168.11.10 "pct exec 2501 -- tail -f /var/log/nginx/rpc-http-prv-access.log"

Last Updated: 2025-12-24