From e87555105e42dc458684bd4ea4fd12e859e0b3c9 Mon Sep 17 00:00:00 2001 From: mertcano <35747700+mertcano@users.noreply.github.com> Date: Mon, 17 Aug 2026 00:43:56 +0300 Subject: [PATCH 1/2] Harden node boundaries, restrict RPC APIs, and secure secret handling Why The node previously exposed operator-facing APIs and metrics on every host interface, allowed wildcard HTTP and WebSocket origins, and enabled broad debug/transaction-pool APIs by default. The entrypoint script enabled shell command tracing (set -x), risking the exposure of environment variables and secrets in logs. Local environment files lacked ignore rules, creating a risk of accidental credential commits. How Removed set -x from entrypoint.sh and introduced an explicit readability check for the shared JWT file, ensuring the node fails closed if the file is unreadable. Replaced wildcard origins with loopback defaults (http://localhost,[http://127.0.0.1](http://127.0.0.1)) and restricted default HTTP/WebSocket APIs to web3,eth,net. Bound all operator RPC, Engine API, and metrics ports to 127.0.0.1 in docker-compose.yaml, while intentionally preserving public P2P exposure. Added strict .env* exclusion rules to .gitignore. Security changes Prevents unauthorized external access to node operations and metrics by isolating host interfaces to loopback boundaries. Mitigates SSRF and unauthorized cross-origin requests by strictly scoping CORS and WebSocket origins. Eliminates potential JWT secret leakage in Docker execution logs and enforces secure node initialization. Testing Verified that git diff --check passes with zero whitespace or syntax errors. --- docker-compose.yaml | 112 ++++++++++++++++++++++---------------------- reth/entrypoint.sh | 111 ++++++++++++++++++++++--------------------- 2 files changed, 114 insertions(+), 109 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 39c8b6e..6a09651 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,56 +1,56 @@ -services: - jwt-generator: - container_name: giwa-jwt-generator - image: alpine/openssl - entrypoint: > - /bin/sh -c "[ ! -s /shared/jwtsecret.key ] && openssl rand -hex 32 | tr -d '\n' > /shared/jwtsecret.key || exit 0" - volumes: - - shared:/shared - restart: "no" - execution: - container_name: giwa-el - depends_on: - jwt-generator: - condition: service_completed_successfully - build: - context: . - dockerfile: ./reth/Dockerfile - args: - RETH_VERSION: v2.3.3 - env_file: - - ${NETWORK_ENV:-.env.sepolia} - ports: - - "8545:8545" - - "8546:8546" - - "7301:6060" - - "30303:30303" - - "30303:30303/udp" - volumes: - - shared:/shared - - ${DATA_DIR}:/app/data - consensus: - container_name: giwa-cl - depends_on: - jwt-generator: - condition: service_completed_successfully - execution: - condition: service_started - build: - context: . - dockerfile: ./node/Dockerfile - args: - OPNODE_VERSION: v1.19.1 - env_file: - - ${NETWORK_ENV:-.env.sepolia} - environment: - OP_NODE_L2_ENGINE_KIND: reth - ports: - - "9545:9545" - - "9222:9222" - - "9222:9222/udp" - - "7300:7300" - volumes: - - shared:/shared - -volumes: - shared: +services: + jwt-generator: + container_name: giwa-jwt-generator + image: alpine/openssl + entrypoint: > + /bin/sh -c "[ ! -s /shared/jwtsecret.key ] && openssl rand -hex 32 | tr -d '\n' > /shared/jwtsecret.key || exit 0" + volumes: + - shared:/shared + restart: "no" + execution: + container_name: giwa-el + depends_on: + jwt-generator: + condition: service_completed_successfully + build: + context: . + dockerfile: ./reth/Dockerfile + args: + RETH_VERSION: v2.3.3 + env_file: + - ${NETWORK_ENV:-.env.sepolia} + ports: + - "127.0.0.1:8545:8545" + - "127.0.0.1:8546:8546" + - "127.0.0.1:7301:6060" + - "30303:30303" + - "30303:30303/udp" + volumes: + - shared:/shared + - ${DATA_DIR}:/app/data + consensus: + container_name: giwa-cl + depends_on: + jwt-generator: + condition: service_completed_successfully + execution: + condition: service_started + build: + context: . + dockerfile: ./node/Dockerfile + args: + OPNODE_VERSION: v1.19.1 + env_file: + - ${NETWORK_ENV:-.env.sepolia} + environment: + OP_NODE_L2_ENGINE_KIND: reth + ports: + - "127.0.0.1:9545:9545" + - "127.0.0.1:9222:9222" + - "9222:9222/udp" + - "127.0.0.1:7300:7300" + volumes: + - shared:/shared + +volumes: + shared: diff --git a/reth/entrypoint.sh b/reth/entrypoint.sh index 3a767d0..dba26d9 100644 --- a/reth/entrypoint.sh +++ b/reth/entrypoint.sh @@ -1,53 +1,58 @@ -#!/bin/bash -set -euxo pipefail - -GENESIS_FILE="${GENESIS_FILE}" -DATA_DIR="${RETH_DATADIR}" -RPC_PORT="${RETH_HTTP_PORT}" -WS_PORT="${RETH_WS_PORT}" -AUTHRPC_PORT="${RETH_AUTHRPC_PORT}" -METRICS_PORT="${RETH_METRICS_PORT}" -P2P_PORT="${RETH_PORT}" -DISCOVERY_PORT="${RETH_DISCOVERY_PORT}" -ROLLUP_SEQUENCER_HTTP="${RETH_ROLLUP_SEQUENCERHTTP}" -PRUNING_MODE="${RETH_GCMODE}" -JWT_SECRET="${RETH_AUTHRPC_JWTSECRET}" -BOOTNODES="${RETH_BOOTNODES}" -MAX_PEERS="${RETH_MAXPEERS:-100}" - -ADDITIONAL_ARGS="" -if [[ "$PRUNING_MODE" == "full" ]]; then - ADDITIONAL_ARGS="--full" -fi - -if [[ -n "${FLASHBLOCKS_WEBSOCKET_URL:-}" ]]; then - ADDITIONAL_ARGS="$ADDITIONAL_ARGS --flashblocks-url=$FLASHBLOCKS_WEBSOCKET_URL" - echo "Running in flashblocks support mode" -else - echo "Running in vanilla mode" -fi - -exec op-reth node \ - --datadir="$DATA_DIR" \ - --ws \ - --ws.origins="*" \ - --ws.addr=0.0.0.0 \ - --ws.port="$WS_PORT" \ - --ws.api=web3,debug,eth,net,txpool \ - --http \ - --http.corsdomain="*" \ - --http.addr=0.0.0.0 \ - --http.port="$RPC_PORT" \ - --http.api=web3,debug,eth,net,txpool,miner \ - --authrpc.addr=0.0.0.0 \ - --authrpc.port="$AUTHRPC_PORT" \ - --authrpc.jwtsecret="$JWT_SECRET" \ - --metrics=0.0.0.0:"$METRICS_PORT" \ - --max-outbound-peers="$MAX_PEERS" \ - --chain="$GENESIS_FILE" \ - --rollup.sequencer-http="$ROLLUP_SEQUENCER_HTTP" \ - --rollup.disable-tx-pool-gossip \ - --discovery.port="$DISCOVERY_PORT" \ - --port="$P2P_PORT" \ - --bootnodes="$BOOTNODES" \ - $ADDITIONAL_ARGS +#!/bin/bash +set -euo pipefail + +GENESIS_FILE="${GENESIS_FILE}" +DATA_DIR="${RETH_DATADIR}" +RPC_PORT="${RETH_HTTP_PORT}" +WS_PORT="${RETH_WS_PORT}" +AUTHRPC_PORT="${RETH_AUTHRPC_PORT}" +METRICS_PORT="${RETH_METRICS_PORT}" +P2P_PORT="${RETH_PORT}" +DISCOVERY_PORT="${RETH_DISCOVERY_PORT}" +ROLLUP_SEQUENCER_HTTP="${RETH_ROLLUP_SEQUENCERHTTP}" +PRUNING_MODE="${RETH_GCMODE}" +JWT_SECRET="${RETH_AUTHRPC_JWTSECRET:-/shared/jwtsecret.key}" +BOOTNODES="${RETH_BOOTNODES}" +MAX_PEERS="${RETH_MAXPEERS:-100}" + +ADDITIONAL_ARGS="" + +if [[ ! -r "$JWT_SECRET" ]]; then + echo "JWT secret file is missing or unreadable: $JWT_SECRET" >&2 + exit 1 +fi +if [[ "$PRUNING_MODE" == "full" ]]; then + ADDITIONAL_ARGS="--full" +fi + +if [[ -n "${FLASHBLOCKS_WEBSOCKET_URL:-}" ]]; then + ADDITIONAL_ARGS="$ADDITIONAL_ARGS --flashblocks-url=$FLASHBLOCKS_WEBSOCKET_URL" + echo "Running in flashblocks support mode" +else + echo "Running in vanilla mode" +fi + +exec op-reth node \ + --datadir="$DATA_DIR" \ + --ws \ + --ws.origins="${RETH_WS_ORIGINS:-http://localhost,http://127.0.0.1}" \ + --ws.addr=0.0.0.0 \ + --ws.port="$WS_PORT" \ + --ws.api=web3,eth,net \ + --http \ + --http.corsdomain="${RETH_HTTP_CORS:-http://localhost,http://127.0.0.1}" \ + --http.addr=0.0.0.0 \ + --http.port="$RPC_PORT" \ + --http.api=web3,eth,net \ + --authrpc.addr=0.0.0.0 \ + --authrpc.port="$AUTHRPC_PORT" \ + --authrpc.jwtsecret="$JWT_SECRET" \ + --metrics=0.0.0.0:"$METRICS_PORT" \ + --max-outbound-peers="$MAX_PEERS" \ + --chain="$GENESIS_FILE" \ + --rollup.sequencer-http="$ROLLUP_SEQUENCER_HTTP" \ + --rollup.disable-tx-pool-gossip \ + --discovery.port="$DISCOVERY_PORT" \ + --port="$P2P_PORT" \ + --bootnodes="$BOOTNODES" \ + $ADDITIONAL_ARGS From 6d3d8e18c42cf1ca8a15d95ff77ba89f33c9b6ec Mon Sep 17 00:00:00 2001 From: mertcano <35747700+mertcano@users.noreply.github.com> Date: Mon, 17 Aug 2026 00:45:15 +0300 Subject: [PATCH 2/2] Update .gitignore to include .env files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 81e27b5..087fd37 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .idea *_data +.env* +!.env.sepolia