Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Faucet] Support ratelimiting on Firebase JWT #15525

Merged
merged 1 commit into from
Dec 10, 2024
Merged

Conversation

banool
Copy link
Contributor

@banool banool commented Dec 6, 2024

Description

This PR supports configuring the Redis ratelimiter to ratelimit by either IP or JWT.

I asked @geekflyer to transfer the firebase-token fork to aptos-labs, I'll use that once it's done.

How Has This Been Tested?

Run a localnet:

aptos node run-localnet --no-faucet

Run Redis:

redis-server

Run the faucet:

cargo run -p aptos-faucet-service -- run -c ~/faucet.yaml

The config looks like this:

# Config to run the tap with the Minter funding backend, configured to run against testnet.
---
server_config:
  api_path_base: ""
  listen_port: 8092
metrics_server_config: {}
bypasser_configs:
  - type: "AuthToken"
    file: "/tmp/bypass-auth-tokens.txt"
checker_configs:
  - type: "RedisRatelimit"
    database_address: localhost
    max_requests_per_day: 5
    ratelimit_key_provider_config:
      type: "Jwt"
      identity_platform_gcp_project: <redacted>
funder_config:
  type: "MintFunder"
  # This uses the DNS name exposed by the tap fullnode Service, which is a
  # bespoke fullnode deployment for just these faucet instances.
  node_url: "http://127.0.0.1:8080"
  chain_id: 4
  key_file_path: /Users/dport/.aptos/testnet/mint.key
  # 1 APT.
  maximum_amount: 100000000
  # 100 APT.
  maximum_amount_with_bypass: 10000000000
  do_not_delegate: false
  mint_account_address: "0xA550C18"
  transaction_expiration_secs: 15
  wait_for_outstanding_txns_secs: 20
handler_config:
  use_helpful_errors: true
  return_rejections_early: true

See that a request without a JWT gets rejected:

$ curl -H 'Content-Type: application/json' http://127.0.0.1:8092/fund -d '{"address": "0xd"}'
{"message":"Error(46): The x-is-jwt header must be present and set to 'true': []","error_code":"CheckerError","rejection_reasons":[],"txn_hashes":[]}
$ curl -H 'x-is-jwt: true' -H 'Content-Type: application/json' http://127.0.0.1:8092/fund -d '{"address": "0xd"}'
{"message":"Error(46): Either the Authorization header is missing or it is not in the form of 'Bearer <token>': []","error_code":"CheckerError","rejection_reasons":[],"txn_hashes":[]}
$ curl -H 'x-is-jwt: true' -H 'authorization: Bearer blah' -H 'Content-Type: application/json' http://127.0.0.1:8092/fund -d '{"address": "0xd"}'
{"message":"Error(46): Failed to verify JWT token: []","error_code":"CheckerError","rejection_reasons":[],"txn_hashes":[]}

See that a request with a valid JWT passes:

$ export JWT='<redacted>'
$ curl -H 'x-is-jwt: true' -H "authorization: Bearer $JWT" -H 'Content-Type: application/json' http://127.0.0.1:8092/fund -d '{"address": "0xd"}'
{"txn_hashes":["e3c023004d9407136553457230bc9c06ded513f4346d852041721f403189f668"]}

See that it eventually gets ratelimited:

$ curl -H 'x-is-jwt: true' -H "authorization: Bearer $JWT" -H 'Content-Type: application/json' http://127.0.0.1:8092/fund -d '{"address": "0xd"}'
{"message":"Request rejected by 1 checkers","error_code":"Rejected","rejection_reasons":[{"reason":"You have reached the maximum allowed number of requests per day: 5","code":"UsageLimitExhausted"}],"txn_hashes":[]}

See that using an API key in /tmp/bypass-auth-tokens.txt lets me bypass the limit:

$ curl -H "authorization: Bearer 123" -H 'Content-Type: application/json' http://127.0.0.1:8092/fund -d '{"address": "0xd"}'
{"txn_hashes":["639040d2a3dae3dcbbb3f2057ea8da3b6061765ed3581cb71ca077e1f726814f"]}

I also verified that the IP based ratelimiting still works as before.

See also that the integration tests pass:

aptos node run-localnet --no-faucet --force-restart --assume-yes
cp /Users/dport/.aptos/testnet/mint.key /tmp
cd crates/aptos-faucet/integration-tests
poetry install
redis-server
redis-cli flushall
DOCKER_DEFAULT_PLATFORM=linux/amd64 poetry run python main.py --base-network testnet --skip-node
running 7 tests
test server::run::test::test_transfer_health ... ignored
test server::run::test::test_bypassers ... ok
test server::run::test::test_checkers ... ok
test server::run::test::test_redis_ratelimiter ... ok
test server::run::test::test_mint_funder_wait_for_txns ... ok
test server::run::test::test_mint_funder ... ok
test server::run::test::test_maximum_amount_with_bypass ... ok

Key Areas to Review

Ensure the checks make sense, find any security issues, etc.

Type of Change

  • New feature
  • Bug fix
  • Breaking change
  • Performance improvement
  • Refactoring
  • Dependency update
  • Documentation update
  • Tests

Which Components or Systems Does This Change Impact?

  • Validator Node
  • Full Node (API, Indexer, etc.)
  • Move/Aptos Virtual Machine
  • Aptos Framework
  • Aptos CLI/SDK
  • Developer Infrastructure
  • Move Compiler
  • Other (specify)

Checklist

  • I have read and followed the CONTRIBUTING doc
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I identified and added all stakeholders and component owners affected by this change as reviewers
  • I tested both happy and unhappy path of the functionality
  • I have made corresponding changes to the documentation

@banool banool requested a review from gregnazario as a code owner December 6, 2024 18:11
Copy link

trunk-io bot commented Dec 6, 2024

⏱️ 2h 55m total CI duration on this PR
Slowest 15 Jobs Cumulative Duration Recent Runs
execution-performance / single-node-performance 23m 🟩
rust-cargo-deny 14m 🟩🟩🟩🟩🟩 (+3 more)
rust-move-tests 13m 🟩
rust-move-tests 13m 🟩
rust-move-tests 12m 🟩
rust-move-tests 12m 🟩
rust-move-tests 12m 🟩
test-target-determinator 12m 🟩🟩🟩
rust-move-tests 11m
check-dynamic-deps 11m 🟩🟩🟩🟩🟩 (+3 more)
rust-move-tests 10m
rust-move-tests 6m 🟥
rust-doc-tests 5m 🟩
execution-performance / test-target-determinator 4m 🟩
general-lints 4m 🟩🟩🟩🟩🟩 (+3 more)

🚨 1 job on the last run was significantly faster/slower than expected

Job Duration vs 7d avg Delta
execution-performance / single-node-performance 23m 15m +58%

settingsfeedbackdocs ⋅ learn more about trunk.io

@banool banool force-pushed the banool/faucet-jwt branch from acdbced to a653ee4 Compare December 6, 2024 18:41
Comment on lines 67 to 82
headers
.get(X_IS_JWT_HEADER)
.and_then(|v| v.to_str().ok())
.map(|v| v.eq_ignore_ascii_case("true"))
.ok_or_else(|| {
AptosTapError::new(
format!(
"The {} header must be present and set to 'true'",
X_IS_JWT_HEADER
),
AptosTapErrorCode::AuthTokenInvalid,
)
})?;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current code checks for the presence of the x-is-jwt header but silently accepts any value other than true. This creates a potential security issue where a header value of false or any other string would pass validation. Consider replacing the .map() with .and_then() to explicitly validate that the header value is true and return an error for all other values. For example:

.and_then(|v| {
    if v.eq_ignore_ascii_case("true") {
        Some(())
    } else {
        None
    }
})

Spotted by Graphite Reviewer

Is this helpful? React 👍 or 👎 to let us know.

@banool banool force-pushed the banool/faucet-jwt branch 3 times, most recently from 2abd113 to c023f20 Compare December 6, 2024 19:47
@banool banool added the CICD:build-images when this label is present github actions will start build+push rust images from the PR. label Dec 6, 2024
Copy link
Contributor

@gregnazario gregnazario left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@banool banool enabled auto-merge (squash) December 10, 2024 22:35

This comment has been minimized.

This comment has been minimized.

This comment has been minimized.

Copy link
Contributor

✅ Forge suite compat success on 3c6e693a27339e73520f41030dce8fc9cd504967 ==> 1f99e0d5755fa6d5848848ee7f20bd8512cfcc25

Compatibility test results for 3c6e693a27339e73520f41030dce8fc9cd504967 ==> 1f99e0d5755fa6d5848848ee7f20bd8512cfcc25 (PR)
1. Check liveness of validators at old version: 3c6e693a27339e73520f41030dce8fc9cd504967
compatibility::simple-validator-upgrade::liveness-check : committed: 16540.12 txn/s, latency: 2055.77 ms, (p50: 2100 ms, p70: 2200, p90: 2400 ms, p99: 4000 ms), latency samples: 531880
2. Upgrading first Validator to new version: 1f99e0d5755fa6d5848848ee7f20bd8512cfcc25
compatibility::simple-validator-upgrade::single-validator-upgrading : committed: 6291.01 txn/s, latency: 4593.56 ms, (p50: 5300 ms, p70: 5400, p90: 5500 ms, p99: 5700 ms), latency samples: 113280
compatibility::simple-validator-upgrade::single-validator-upgrade : committed: 6185.52 txn/s, latency: 5353.29 ms, (p50: 5800 ms, p70: 5900, p90: 6000 ms, p99: 6100 ms), latency samples: 216520
3. Upgrading rest of first batch to new version: 1f99e0d5755fa6d5848848ee7f20bd8512cfcc25
compatibility::simple-validator-upgrade::half-validator-upgrading : committed: 7215.23 txn/s, latency: 3924.44 ms, (p50: 4400 ms, p70: 4800, p90: 4900 ms, p99: 5100 ms), latency samples: 132460
compatibility::simple-validator-upgrade::half-validator-upgrade : committed: 7370.48 txn/s, latency: 4422.72 ms, (p50: 4700 ms, p70: 4800, p90: 5100 ms, p99: 5200 ms), latency samples: 245680
4. upgrading second batch to new version: 1f99e0d5755fa6d5848848ee7f20bd8512cfcc25
compatibility::simple-validator-upgrade::rest-validator-upgrading : committed: 10696.30 txn/s, latency: 2615.19 ms, (p50: 2900 ms, p70: 3000, p90: 3200 ms, p99: 3400 ms), latency samples: 190820
compatibility::simple-validator-upgrade::rest-validator-upgrade : committed: 11201.60 txn/s, latency: 2866.28 ms, (p50: 3000 ms, p70: 3100, p90: 3200 ms, p99: 3500 ms), latency samples: 364720
5. check swarm health
Compatibility test for 3c6e693a27339e73520f41030dce8fc9cd504967 ==> 1f99e0d5755fa6d5848848ee7f20bd8512cfcc25 passed
Test Ok

Copy link
Contributor

✅ Forge suite realistic_env_max_load success on 1f99e0d5755fa6d5848848ee7f20bd8512cfcc25

two traffics test: inner traffic : committed: 14874.42 txn/s, latency: 2668.98 ms, (p50: 2700 ms, p70: 2700, p90: 3000 ms, p99: 3200 ms), latency samples: 5655640
two traffics test : committed: 99.98 txn/s, latency: 1393.68 ms, (p50: 1400 ms, p70: 1400, p90: 1500 ms, p99: 2200 ms), latency samples: 1780
Latency breakdown for phase 0: ["MempoolToBlockCreation: max: 1.526, avg: 1.481", "ConsensusProposalToOrdered: max: 0.320, avg: 0.289", "ConsensusOrderedToCommit: max: 0.371, avg: 0.361", "ConsensusProposalToCommit: max: 0.657, avg: 0.650"]
Max non-epoch-change gap was: 0 rounds at version 0 (avg 0.00) [limit 4], 0.77s no progress at version 30683 (avg 0.20s) [limit 15].
Max epoch-change gap was: 0 rounds at version 0 (avg 0.00) [limit 4], 0.64s no progress at version 2235412 (avg 0.64s) [limit 16].
Test Ok

Copy link
Contributor

✅ Forge suite framework_upgrade success on 3c6e693a27339e73520f41030dce8fc9cd504967 ==> 1f99e0d5755fa6d5848848ee7f20bd8512cfcc25

Compatibility test results for 3c6e693a27339e73520f41030dce8fc9cd504967 ==> 1f99e0d5755fa6d5848848ee7f20bd8512cfcc25 (PR)
Upgrade the nodes to version: 1f99e0d5755fa6d5848848ee7f20bd8512cfcc25
framework_upgrade::framework-upgrade::full-framework-upgrade : committed: 1437.55 txn/s, submitted: 1439.85 txn/s, failed submission: 2.30 txn/s, expired: 2.30 txn/s, latency: 2227.79 ms, (p50: 2100 ms, p70: 2400, p90: 3300 ms, p99: 4400 ms), latency samples: 124840
framework_upgrade::framework-upgrade::full-framework-upgrade : committed: 1310.55 txn/s, submitted: 1312.09 txn/s, failed submission: 1.54 txn/s, expired: 1.54 txn/s, latency: 2277.79 ms, (p50: 2100 ms, p70: 2400, p90: 3300 ms, p99: 5200 ms), latency samples: 119280
5. check swarm health
Compatibility test for 3c6e693a27339e73520f41030dce8fc9cd504967 ==> 1f99e0d5755fa6d5848848ee7f20bd8512cfcc25 passed
Upgrade the remaining nodes to version: 1f99e0d5755fa6d5848848ee7f20bd8512cfcc25
framework_upgrade::framework-upgrade::full-framework-upgrade : committed: 1012.43 txn/s, submitted: 1014.26 txn/s, failed submission: 1.83 txn/s, expired: 1.83 txn/s, latency: 3018.23 ms, (p50: 2400 ms, p70: 3000, p90: 5500 ms, p99: 8100 ms), latency samples: 88560
Test Ok

@banool banool merged commit 06f1824 into main Dec 10, 2024
90 checks passed
@banool banool deleted the banool/faucet-jwt branch December 10, 2024 23:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CICD:build-images when this label is present github actions will start build+push rust images from the PR.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants