From 32c15893483f8e903bbbed237e68c443ece3f781 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Thu, 29 Aug 2024 16:37:49 -0300 Subject: [PATCH 1/3] feat: Block cheat codes in anvil Adds a second api to the mainnet-fork, located at `public-PUBLIC_API_KEY`, in which all cheat codes are blocked. We define a cheat code as a method in the evm, hardhat, or anvil namespaces. We use njs for parsing the JSON RPC request body and testing the method namespace. The PUBLIC_API_KEY is only set for provernet, for other networks, we load the same API_KEY secret as always. This PR also enables logging for anvil, including a logrotate config to ensure we don't fill up the disk just with logs. --- iac/mainnet-fork/Earthfile | 20 ++++++++----- iac/mainnet-fork/etc/anvil.logrotate.conf | 9 ++++++ iac/mainnet-fork/nginx/gateway.conf | 22 +++++++++++++-- iac/mainnet-fork/nginx/nginx.conf | 1 + .../nginx/njs/anvil_validation.js | 28 +++++++++++++++++++ iac/mainnet-fork/redeploy | 2 +- iac/mainnet-fork/scripts/install_nginx.sh | 19 +++++++++++++ iac/mainnet-fork/scripts/run_nginx_anvil.sh | 12 +++++--- iac/mainnet-fork/terraform/main.tf | 26 ++++------------- iac/mainnet-fork/terraform/variables.tf | 4 +++ 10 files changed, 108 insertions(+), 35 deletions(-) create mode 100644 iac/mainnet-fork/etc/anvil.logrotate.conf create mode 100644 iac/mainnet-fork/nginx/njs/anvil_validation.js create mode 100755 iac/mainnet-fork/scripts/install_nginx.sh diff --git a/iac/mainnet-fork/Earthfile b/iac/mainnet-fork/Earthfile index 67c0ad257d1..c0028dcae5d 100644 --- a/iac/mainnet-fork/Earthfile +++ b/iac/mainnet-fork/Earthfile @@ -4,24 +4,30 @@ build: FROM ubuntu:focal # Install nginx - RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections - RUN apt-get update && apt install -y git curl nginx - - # Copy nginx config - COPY . . - COPY nginx/gateway.conf /etc/nginx/gateway.conf - COPY nginx/nginx.conf /etc/nginx/nginx.conf + COPY ./scripts/install_nginx.sh ./scripts/install_nginx.sh + RUN ./scripts/install_nginx.sh # Install foundry + COPY ./scripts/install_foundry.sh ./scripts/install_foundry.sh RUN ./scripts/install_foundry.sh ENV PATH="./foundry/bin:${PATH}" + # Copy nginx config + COPY ./scripts ./scripts + COPY ./redeploy ./redeploy + COPY ./nginx/ /etc/nginx/ + COPY --chmod 640 ./etc/anvil.logrotate.conf /etc/logrotate.d/anvil + # Expose port 80 EXPOSE 80 # Set entrypoint. ENTRYPOINT ["sh", "-c", "./scripts/run_nginx_anvil.sh"] +export-local: + FROM +build + SAVE IMAGE aztecprotocol/mainnet-fork + export-mainnet-fork: FROM +build ARG DIST_TAG="devnet" diff --git a/iac/mainnet-fork/etc/anvil.logrotate.conf b/iac/mainnet-fork/etc/anvil.logrotate.conf new file mode 100644 index 00000000000..1ee786d3d46 --- /dev/null +++ b/iac/mainnet-fork/etc/anvil.logrotate.conf @@ -0,0 +1,9 @@ +/var/log/anvil/*.log { + daily + missingok + rotate 14 + size 50M + compress + notifempty + copytruncate +} \ No newline at end of file diff --git a/iac/mainnet-fork/nginx/gateway.conf b/iac/mainnet-fork/nginx/gateway.conf index 74f889b9eff..aa10d0e7d34 100644 --- a/iac/mainnet-fork/nginx/gateway.conf +++ b/iac/mainnet-fork/nginx/gateway.conf @@ -2,13 +2,29 @@ server { listen 80 default_server; listen 8545; - location = /{{API_KEY}} { + location /{{ADMIN_API_KEY}} { proxy_pass http://0.0.0.0:8544; - rewrite ^/{{API_KEY}}(.*) /$1 break; + rewrite ^/{{ADMIN_API_KEY}}(.*) /$1 break; + } + + location /public-{{PUBLIC_API_KEY}} { + client_body_buffer_size 20M; + client_body_in_single_buffer on; + js_import main from njs/anvil_validation.js; + js_content main.authorize; + } + + location @anvil { + proxy_pass http://0.0.0.0:8544; + rewrite ^/({{ADMIN_API_KEY}}|public-{{PUBLIC_API_KEY}})(.*) /$2 break; } # Error responses - error_page 404 = @400; # Treat invalid paths as bad requests + error_page 404 = @404; + location @404 { + return 404 '{"error":"Resource not found"}'; + } + proxy_intercept_errors on; # Do not send backend errors to client default_type application/json; # If no content-type, assume JSON } \ No newline at end of file diff --git a/iac/mainnet-fork/nginx/nginx.conf b/iac/mainnet-fork/nginx/nginx.conf index 5e078c52cc8..5896990ebf3 100644 --- a/iac/mainnet-fork/nginx/nginx.conf +++ b/iac/mainnet-fork/nginx/nginx.conf @@ -1,3 +1,4 @@ +load_module modules/ngx_http_js_module.so; events { worker_connections 768; diff --git a/iac/mainnet-fork/nginx/njs/anvil_validation.js b/iac/mainnet-fork/nginx/njs/anvil_validation.js new file mode 100644 index 00000000000..d6aca30daf6 --- /dev/null +++ b/iac/mainnet-fork/nginx/njs/anvil_validation.js @@ -0,0 +1,28 @@ +// Authorizes a request based on the method of the JSON-RPC request body, blocking all cheat codes. +// See https://github.com/nginx/njs-examples?tab=readme-ov-file#authorizing-requests-based-on-request-body-content-http-authorization-request-body +function authorize(r) { + try { + if (r.requestText) { + const body = JSON.parse(r.requestText); + if (body && body.method) { + const method = body.method.replace(/\s+/g).toLowerCase(); + if ( + method.startsWith("evm_") || + method.startsWith("hardhat_") || + method.startsWith("anvil_") + ) { + const error = "Restricted method " + method; + r.error(error); + r.return(401, JSON.stringify({ error })); + return; + } + } + } + r.internalRedirect("@anvil"); + } catch (e) { + r.error("JSON.parse exception: " + e); + r.return(400, JSON.stringify({ error: "Error parsing request" })); + } +} + +export default { authorize }; diff --git a/iac/mainnet-fork/redeploy b/iac/mainnet-fork/redeploy index 00750edc07d..b8626c4cff2 100644 --- a/iac/mainnet-fork/redeploy +++ b/iac/mainnet-fork/redeploy @@ -1 +1 @@ -3 +4 diff --git a/iac/mainnet-fork/scripts/install_nginx.sh b/iac/mainnet-fork/scripts/install_nginx.sh new file mode 100755 index 00000000000..b2a877f8711 --- /dev/null +++ b/iac/mainnet-fork/scripts/install_nginx.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -eu + +# See https://nginx.org/en/linux_packages.html#Ubuntu +echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections + +apt-get update && apt install -y curl gnupg2 ca-certificates lsb-release ubuntu-keyring + +curl -sS https://nginx.org/keys/nginx_signing.key | gpg --dearmor \ + | tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null + +echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \ + http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \ + | tee /etc/apt/sources.list.d/nginx.list + +echo "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \ + | tee /etc/apt/preferences.d/99nginx + +apt-get update && apt install -y git curl nginx nginx-module-njs \ No newline at end of file diff --git a/iac/mainnet-fork/scripts/run_nginx_anvil.sh b/iac/mainnet-fork/scripts/run_nginx_anvil.sh index a6d39a6e996..76c79d7f09e 100755 --- a/iac/mainnet-fork/scripts/run_nginx_anvil.sh +++ b/iac/mainnet-fork/scripts/run_nginx_anvil.sh @@ -2,9 +2,10 @@ set -eum pipefail -# Replace API_KEY in nginx config -echo "Replacing api key with $API_KEY in nginx config..." -sed -i 's/{{API_KEY}}/'$API_KEY'/' /etc/nginx/gateway.conf +# Replace API_KEYs in nginx config +echo "Replacing api keys in nginx config..." +sed -i 's/{{PUBLIC_API_KEY}}/'$PUBLIC_API_KEY'/g' /etc/nginx/gateway.conf +sed -i 's/{{ADMIN_API_KEY}}/'$API_KEY'/g' /etc/nginx/gateway.conf # Run nginx and anvil alongside each other trap 'kill $(jobs -p)' SIGTERM @@ -21,8 +22,11 @@ echo "result: ${MNEMONIC_STRIPPED:0:10}..." # Data directory for anvil state mkdir -p /data +# Log directory for anvil +mkdir -p /var/log/anvil/ + # Run anvil silently -.foundry/bin/anvil --silent --block-time 12 --host $HOST -p $PORT -m "$MNEMONIC_STRIPPED" -f=https://mainnet.infura.io/v3/$INFURA_API_KEY --chain-id=$L1_CHAIN_ID --fork-block-number=15918000 --block-base-fee-per-gas=10 -s=$SNAPSHOT_FREQUENCY --state=./data/state --balance=1000000000000000000 >/dev/null & +.foundry/bin/anvil --block-time 12 --host $HOST -p $PORT -m "$MNEMONIC_STRIPPED" -f=https://mainnet.infura.io/v3/$INFURA_API_KEY --chain-id=$L1_CHAIN_ID --fork-block-number=15918000 --block-base-fee-per-gas=10 -s=$SNAPSHOT_FREQUENCY --state=./data/state --balance=1000000000000000000 >>/var/log/anvil/anvil.log & echo "Waiting for ethereum host at $ETHEREUM_HOST..." while ! curl -s $ETHEREUM_HOST >/dev/null; do sleep 1; done diff --git a/iac/mainnet-fork/terraform/main.tf b/iac/mainnet-fork/terraform/main.tf index daee91393ae..1303bc62723 100644 --- a/iac/mainnet-fork/terraform/main.tf +++ b/iac/mainnet-fork/terraform/main.tf @@ -119,26 +119,12 @@ resource "aws_ecs_task_definition" "aztec_mainnet_fork" { image = "${var.DOCKERHUB_ACCOUNT}/mainnet-fork:${var.DEPLOY_TAG}" essential = true environment = [ - { - name = "API_KEY" - value = "${var.API_KEY}" - }, - { - name = "MNEMONIC" - value = "${var.FORK_MNEMONIC}" - }, - { - name = "INFURA_API_KEY" - value = "${var.INFURA_API_KEY}" - }, - { - name = "L1_CHAIN_ID" - value = "${var.L1_CHAIN_ID}" - }, - { - name = "SNAPSHOT_FREQUENCY" - value = "15" - } + { name = "API_KEY", value = "${var.API_KEY}" }, + { name = "PUBLIC_API_KEY", value = "${var.PUBLIC_API_KEY}" }, + { name = "MNEMONIC", value = "${var.FORK_MNEMONIC}" }, + { name = "INFURA_API_KEY", value = "${var.INFURA_API_KEY}" }, + { name = "L1_CHAIN_ID", value = "${var.L1_CHAIN_ID}" }, + { name = "SNAPSHOT_FREQUENCY", value = "15" } ] mountPoints = [ { diff --git a/iac/mainnet-fork/terraform/variables.tf b/iac/mainnet-fork/terraform/variables.tf index 7e52e53eb5c..5b95c958dbe 100644 --- a/iac/mainnet-fork/terraform/variables.tf +++ b/iac/mainnet-fork/terraform/variables.tf @@ -10,6 +10,10 @@ variable "API_KEY" { type = string } +variable "PUBLIC_API_KEY" { + type = string +} + variable "DOCKERHUB_ACCOUNT" { type = string } From d7e164de11d3420c6a9ae79913f9e505d706e415 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Thu, 29 Aug 2024 16:48:45 -0300 Subject: [PATCH 2/3] fix comment --- iac/mainnet-fork/scripts/run_nginx_anvil.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iac/mainnet-fork/scripts/run_nginx_anvil.sh b/iac/mainnet-fork/scripts/run_nginx_anvil.sh index 76c79d7f09e..8c433f199ae 100755 --- a/iac/mainnet-fork/scripts/run_nginx_anvil.sh +++ b/iac/mainnet-fork/scripts/run_nginx_anvil.sh @@ -25,7 +25,7 @@ mkdir -p /data # Log directory for anvil mkdir -p /var/log/anvil/ -# Run anvil silently +# Run anvil logging to /var/log/anvil .foundry/bin/anvil --block-time 12 --host $HOST -p $PORT -m "$MNEMONIC_STRIPPED" -f=https://mainnet.infura.io/v3/$INFURA_API_KEY --chain-id=$L1_CHAIN_ID --fork-block-number=15918000 --block-base-fee-per-gas=10 -s=$SNAPSHOT_FREQUENCY --state=./data/state --balance=1000000000000000000 >>/var/log/anvil/anvil.log & echo "Waiting for ethereum host at $ETHEREUM_HOST..." From 363f7c4b056c8e88e512ac0d0faa0a07aca9f364 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Thu, 29 Aug 2024 16:48:53 -0300 Subject: [PATCH 3/3] handle deployment --- .github/workflows/devnet-deploys.yml | 10 ++++++++++ build-system/scripts/deploy_terraform | 1 + 2 files changed, 11 insertions(+) diff --git a/.github/workflows/devnet-deploys.yml b/.github/workflows/devnet-deploys.yml index 310abb22370..09e3dce2d5d 100644 --- a/.github/workflows/devnet-deploys.yml +++ b/.github/workflows/devnet-deploys.yml @@ -33,6 +33,7 @@ env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} API_KEY: ${{ secrets.DEVNET_API_KEY }} + PUBLIC_API_KEY: ${{ secrets.DEVNET_API_KEY }} FORK_MNEMONIC: ${{ secrets.FORK_MNEMONIC }} CONTRACT_PUBLISHER_PRIVATE_KEY: ${{ secrets.CONTRACT_PUBLISHER_PRIVATE_KEY }} CONTRACT_S3_BUCKET: s3://static.aztec.network @@ -64,6 +65,7 @@ env: # Anvil TF_VAR_FORK_MNEMONIC: ${{ secrets.FORK_MNEMONIC }} TF_VAR_INFURA_API_KEY: ${{ secrets.INFURA_API_KEY }} + TF_VAR_PUBLIC_API_KEY: ${{ secrets.DEVNET_API_KEY }} # Faucet TF_VAR_FAUCET_ACCOUNT_INDEX: 9 @@ -105,6 +107,7 @@ jobs: deploy_tag: ${{ steps.set_network_vars.outputs.deploy_tag }} branch_name: ${{ steps.set_network_vars.outputs.branch_name }} network_api_key: ${{ steps.set_network_vars.outputs.network_api_key }} + network_public_api_key: ${{ steps.set_network_vars.outputs.network_public_api_key }} agents_per_prover: ${{ steps.set_network_vars.outputs.agents_per_prover }} bot_interval: ${{ steps.set_network_vars.outputs.bot_interval }} node_tcp_range_start: ${{ steps.set_network_vars.outputs.node_tcp_range_start }} @@ -131,6 +134,7 @@ jobs: echo "deploy_tag=devnet" >> $GITHUB_OUTPUT echo "branch_name=devnet" >> $GITHUB_OUTPUT echo "network_api_key=DEVNET_API_KEY" >> $GITHUB_OUTPUT + echo "network_public_api_key=DEVNET_API_KEY" >> $GITHUB_OUTPUT echo "agents_per_prover=4" >> $GITHUB_OUTPUT echo "bot_interval=180" >> $GITHUB_OUTPUT echo "node_tcp_range_start=40100" >> $GITHUB_OUTPUT @@ -151,6 +155,7 @@ jobs: echo "deploy_tag=provernet" >> $GITHUB_OUTPUT echo "branch_name=provernet" >> $GITHUB_OUTPUT echo "network_api_key=PROVERNET_API_KEY" >> $GITHUB_OUTPUT + echo "network_public_api_key=PROVERNET_PUBLIC_API_KEY" >> $GITHUB_OUTPUT echo "agents_per_prover=4" >> $GITHUB_OUTPUT echo "bot_interval=300" >> $GITHUB_OUTPUT echo "node_tcp_range_start=40200" >> $GITHUB_OUTPUT @@ -171,6 +176,7 @@ jobs: echo "deploy_tag=alphanet" >> $GITHUB_OUTPUT echo "branch_name=alphanet" >> $GITHUB_OUTPUT echo "network_api_key=ALPHANET_API_KEY" >> $GITHUB_OUTPUT + echo "network_public_api_key=ALPHANET_API_KEY" >> $GITHUB_OUTPUT echo "agents_per_prover=1" >> $GITHUB_OUTPUT echo "bot_interval=30" >> $GITHUB_OUTPUT echo "node_tcp_range_start=40000" >> $GITHUB_OUTPUT @@ -200,6 +206,8 @@ jobs: TF_VAR_DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} TF_VAR_API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + PUBLIC_API_KEY: ${{ secrets[needs.set-network.outputs.network_public_api_key] }} + TF_VAR_PUBLIC_API_KEY: ${{ secrets[needs.set-network.outputs.network_public_api_key] }} API_KEY_NAME: ${{ needs.set-network.outputs.network_api_key }} runs-on: ${{ github.actor }}-x86 steps: @@ -443,6 +451,8 @@ jobs: TF_VAR_DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} TF_VAR_API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + PUBLIC_API_KEY: ${{ secrets[needs.set-network.outputs.network_public_api_key] }} + TF_VAR_PUBLIC_API_KEY: ${{ secrets[needs.set-network.outputs.network_public_api_key] }} TF_VAR_AGENTS_PER_PROVER: ${{ needs.set-network.outputs.agents_per_prover }} TF_VAR_BOT_TX_INTERVAL_SECONDS: ${{ needs.set-network.outputs.bot_interval }} TF_VAR_NODE_LB_RULE_PRIORITY: ${{ needs.set-network.outputs.node_lb_priority_range_start }} diff --git a/build-system/scripts/deploy_terraform b/build-system/scripts/deploy_terraform index aa11408fb67..d938e594340 100755 --- a/build-system/scripts/deploy_terraform +++ b/build-system/scripts/deploy_terraform @@ -29,6 +29,7 @@ export TF_VAR_DOCKERHUB_ACCOUNT=$DOCKERHUB_ACCOUNT export TF_VAR_FORK_MNEMONIC=$FORK_MNEMONIC export TF_VAR_INFURA_API_KEY=$INFURA_API_KEY export TF_VAR_API_KEY=$FORK_API_KEY +export TF_VAR_PUBLIC_API_KEY=${PUBLIC_FORK_API_KEY:-$FORK_API_KEY} export TF_VAR_L1_CHAIN_ID=$CHAIN_ID # If given a repository name, use it to construct and set/override the backend key.