diff --git a/infra/blockchain-devnet/README.md b/infra/blockchain-devnet/README.md index fda2eac..0f4a597 100644 --- a/infra/blockchain-devnet/README.md +++ b/infra/blockchain-devnet/README.md @@ -27,6 +27,16 @@ The lightweight local explorer is available at: http://127.0.0.1:8089 ``` +The local RPC gateway is available at: + +```text +http://127.0.0.1:18545 +``` + +Use the gateway when you want to practice RPC protection patterns such as method +restriction, rate limiting, CORS headers, and request logging. Keep the raw +Anvil RPC for direct local development. + From another LAN host, use the Debian host IP: ```text @@ -49,6 +59,7 @@ curl -sS -X POST http://127.0.0.1:8545 \ ```bash BLOCKCHAIN_RPC_PORT=8545 +BLOCKCHAIN_RPC_GATEWAY_PORT=18545 BLOCKCHAIN_EXPLORER_PORT=8089 BLOCKCHAIN_CHAIN_ID=31337 BLOCKCHAIN_BLOCK_TIME=2 diff --git a/infra/blockchain-devnet/docker-compose.yml b/infra/blockchain-devnet/docker-compose.yml index 3ab227d..e14ae29 100644 --- a/infra/blockchain-devnet/docker-compose.yml +++ b/infra/blockchain-devnet/docker-compose.yml @@ -28,3 +28,14 @@ services: volumes: - ./explorer:/usr/share/nginx/html:ro restart: unless-stopped + + rpc-gateway: + image: nginx:1.27-alpine + container_name: homelab-blockchain-rpc-gateway + depends_on: + - anvil + ports: + - "${BLOCKCHAIN_RPC_GATEWAY_PORT:-18545}:80" + volumes: + - ./nginx/rpc-gateway.conf:/etc/nginx/conf.d/default.conf:ro + restart: unless-stopped diff --git a/infra/blockchain-devnet/nginx/rpc-gateway.conf b/infra/blockchain-devnet/nginx/rpc-gateway.conf new file mode 100644 index 0000000..795c022 --- /dev/null +++ b/infra/blockchain-devnet/nginx/rpc-gateway.conf @@ -0,0 +1,45 @@ +limit_req_zone $binary_remote_addr zone=rpc_per_ip:10m rate=10r/s; + +upstream anvil_rpc { + server anvil:8545; + keepalive 16; +} + +server { + listen 80; + server_name _; + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log warn; + + location = /healthz { + access_log off; + return 200 "ok\n"; + } + + location / { + if ($request_method = OPTIONS) { + add_header Access-Control-Allow-Origin "*" always; + add_header Access-Control-Allow-Methods "POST, OPTIONS" always; + add_header Access-Control-Allow-Headers "content-type" always; + add_header Access-Control-Max-Age 86400 always; + return 204; + } + + limit_req zone=rpc_per_ip burst=20 nodelay; + limit_except POST { + deny all; + } + + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 60s; + proxy_send_timeout 60s; + + add_header Access-Control-Allow-Origin "*" always; + proxy_pass http://anvil_rpc; + } +} diff --git a/scripts/blockchain-devnet b/scripts/blockchain-devnet index 8fc83a3..a27fb71 100755 --- a/scripts/blockchain-devnet +++ b/scripts/blockchain-devnet @@ -4,6 +4,7 @@ set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" STACK_DIR="${REPO_ROOT}/infra/blockchain-devnet" RPC_URL="${BLOCKCHAIN_RPC_URL:-http://127.0.0.1:${BLOCKCHAIN_RPC_PORT:-8545}}" +RPC_GATEWAY_URL="${BLOCKCHAIN_RPC_GATEWAY_URL:-http://127.0.0.1:${BLOCKCHAIN_RPC_GATEWAY_PORT:-18545}}" usage() { cat <<'EOF' @@ -36,6 +37,7 @@ case "${1:-status}" in echo echo "RPC endpoint: ${RPC_URL}" + echo "RPC gateway: ${RPC_GATEWAY_URL}" if rpc_call eth_chainId >/tmp/homelab-blockchain-chainid.json 2>/tmp/homelab-blockchain-rpc.err; then printf 'chainId: ' cat /tmp/homelab-blockchain-chainid.json @@ -61,7 +63,8 @@ case "${1:-status}" in docker compose logs -f --tail=100 ;; rpc) - echo "$RPC_URL" + echo "raw=${RPC_URL}" + echo "gateway=${RPC_GATEWAY_URL}" ;; -h|--help|help) usage