Skip to content

Nginx hardening + infra updates#87

Open
alastairong1 wants to merge 1 commit intomainfrom
alastair/nginx-hardening
Open

Nginx hardening + infra updates#87
alastairong1 wants to merge 1 commit intomainfrom
alastair/nginx-hardening

Conversation

@alastairong1
Copy link
Copy Markdown
Contributor

@alastairong1 alastairong1 commented Apr 20, 2026

Summary

  • Add nginx rate limiting (10r/s per IP, burst 20) with 429 status
  • Add security headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy)
  • Block common exploit scanner paths (PHP, ASP, Docker, ThinkPHP)
  • Set client_max_body_size 1m for API payloads
  • Enable recommended gzip and optimisation settings
  • Add Alastair SSH key to infra and ssh roles

Test plan

  • NixOS configuration evaluates without errors
  • cargo check passes
  • rainix-rs-static passes
  • Rate limiting returns 429 when exceeded in deployed nginx
  • Exploit scanner paths return 444 (connection drop) in deployed nginx
  • Security headers present in deployed responses

Summary by CodeRabbit

  • New Features

    • API request rate limiting implemented.
    • Scanner path blocking enabled.
  • Chores

    • Added infrastructure access for new team member.
    • Web server optimization and compression enabled.
    • HTTP headers added to the API endpoint.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 20, 2026

Warning

Rate limit exceeded

@findolor has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cbf781de-5e5b-4420-9752-78408041fb96

📥 Commits

Reviewing files that changed from the base of the PR and between b8ca651 and 2db92e9.

📒 Files selected for processing (2)
  • keys.nix
  • os.nix
📝 Walkthrough

Walkthrough

This PR adds a new SSH key entry for alastair to the authorized roles and implements Nginx security hardening with rate limiting, security headers, and scanner path blocking.

Changes

SSH Key and Role Addition

Layer / File(s) Summary
Key & Role Updates
keys.nix
Added keys.alastair SSH public key and appended alastair to both roles.infra and roles.ssh arrays.

Nginx Security Hardening

Layer / File(s) Summary
Optimization Presets
os.nix (lines 99–104)
Enabled services.nginx.recommendedOptimisation and services.nginx.recommendedGzipSettings for Nginx optimization and compression.
Rate Limiting & Security Headers
os.nix (lines 105–123)
Added limit_req_zone to appendHttpConfig for rate limiting (10r/s), and extended extraConfig with X-Content-Type-Options, X-Frame-Options, Referrer-Policy headers and client_max_body_size 1m.
Scanner Path Blocking & Per-Route Limiting
os.nix (lines 124–137)
Added locations rules returning 444 for common exploit-scanner paths (.git, .env, containers, ignition, vendor, public/index), and applied per-request rate limiting (limit_req zone=api burst=20 nodelay) with limit_req_status 429 to the root proxy route.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • Update keys #70: Updates the same roles.infra and roles.ssh role lists in keys.nix by extending them with new user entries.

Poem

🐰 A rabbit hops through keys so bright,
New access granted, secure and tight!
Rate limits dance, headers shield the way,
Scanners blocked by Nginx's play!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Nginx hardening + infra updates' accurately reflects the main changes: nginx security enhancements (rate limiting, headers, blocking exploit paths) and infrastructure updates (SSH key addition, role assignments).
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch alastair/nginx-hardening

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 3 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@findolor findolor changed the base branch from main to graphite-base/87 April 27, 2026 07:33
@findolor findolor force-pushed the alastair/nginx-hardening branch from 7d30c9b to 7d1d763 Compare April 27, 2026 07:33
@findolor findolor changed the base branch from graphite-base/87 to alastair/neutralize-metaboards April 27, 2026 07:33
Copy link
Copy Markdown
Collaborator

findolor commented Apr 27, 2026


How to use the Graphite Merge Queue

Add the label add-to-gt-merge-queue to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

Copy link
Copy Markdown
Collaborator

@findolor findolor left a comment

Choose a reason for hiding this comment

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

This review is given by Graphite

Critical Issues:

  1. sandbox = false (line 169) - This disables Nix build isolation, which is a security reduction. The PR description doesn't explain why this is needed. This should be justified or removed.
  2. Host SSH key change (line 6) - The host SSH key is completely changed, but this isn't mentioned in the PR description. This could break existing SSH connections and needs verification that it's intentional.
  3. Volume device path change (line 160) - Changing from st0x-rest-api-data to st0x-rest-api-data-v2 is a critical infrastructure change. If the new volume doesn't exist or isn't properly migrated, the system won't boot. This needs confirmation that the volume is ready.

Potential Issues:

  1. Rate limiting - 10r/s might be too restrictive for legitimate API usage patterns. Consider if burst=20 is sufficient for typical workflows.
  2. client_max_body_size 1m (line 121) - Verify that all API endpoints can work with max 1MB request bodies.
  3. Exploit blocking patterns (lines 125-131) - The regex ~ \\.(php|asp|aspx|jsp|cgi)$ might have edge cases. Also returning 444 provides no feedback to clients.

@findolor findolor force-pushed the alastair/neutralize-metaboards branch from 56061d9 to b63a8ef Compare April 30, 2026 11:19
@findolor findolor force-pushed the alastair/nginx-hardening branch from b14e075 to 4cd0e5a Compare April 30, 2026 11:19
@findolor findolor changed the base branch from alastair/neutralize-metaboards to graphite-base/87 April 30, 2026 11:48
@findolor findolor force-pushed the alastair/nginx-hardening branch from 4cd0e5a to e92b5a1 Compare April 30, 2026 11:48
@findolor findolor changed the base branch from graphite-base/87 to main April 30, 2026 11:48
@findolor findolor force-pushed the alastair/nginx-hardening branch from e92b5a1 to b8ca651 Compare May 4, 2026 08:54
@findolor findolor dismissed their stale review May 4, 2026 08:55

took over this PR myself

@findolor findolor self-assigned this May 4, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
keys.nix (1)

17-18: Existing .age files must be re-encrypted after this merge — alastair cannot decrypt them until that happens.

Age/rage encryption is fixed at the time the file is encrypted. The current terraform.tfstate.age and terraform.tfvars.age were sealed with the old recipient list (no alastair). Adding the key to keys.nix only affects future invocations of encryptState / encryptVars in infra/default.nix. Until those functions are re-run and the .age files are committed with the new ciphertext, alastair will not be able to decrypt either secrets file — despite appearing in both roles.

Action required: after merging, run encryptState and encryptVars (or the equivalent nix task), commit the updated .age files, and verify decryption succeeds with alastair's key.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@keys.nix` around lines 17 - 18, The .age files were encrypted before adding
alastair to keys.nix, so re-encrypt the existing secrets with the new recipient
list: open infra/default.nix and run encryptState and encryptVars (or the
project’s equivalent nix task) to regenerate terraform.tfstate.age and
terraform.tfvars.age, commit the updated .age files, and verify decryption using
alastair’s key to confirm they can now decrypt; note that merely adding alastair
to keys.nix does not retroactively re-encrypt existing files.
os.nix (1)

102-103: ⚡ Quick win

Consider BREACH exposure when recommendedGzipSettings is combined with HTTPS.

An application is vulnerable to BREACH when all three conditions are present simultaneously: HTTP compression is enabled, user input is reflected in responses, and secrets appear in the response body. Since this is a public REST API over HTTPS, if any endpoint returns compressed responses that mix user-controlled content with sensitive values (tokens, API keys, etc.), BREACH applies.

If any such endpoints exist, a targeted mitigation is straightforward:

🛡️ Suggested per-location opt-out
 locations."/" = {
   proxyPass = "http://127.0.0.1:8000";
   extraConfig = ''
+    # Disable gzip on endpoints that return secrets alongside user input (BREACH)
+    gzip off;
     limit_req zone=api burst=20 nodelay;
     limit_req_status 429;
   '';
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@os.nix` around lines 102 - 103, The current config enables HTTP compression
via the recommendedGzipSettings variable which, when combined with HTTPS and
reflected user input, can create a BREACH risk; either disable compression
globally by setting recommendedGzipSettings = false or implement a per-endpoint
opt-out so responses that may include secrets skip gzip—add a per-route flag
(e.g., recommendedGzipSettingsPerLocation or compress: false) and ensure server
middleware (the gzip/compression handler) checks that flag before compressing
responses that reflect user input or include secrets.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@os.nix`:
- Around line 125-130: Change the two location keys that use the case-sensitive
regex operator "~" to use the case-insensitive operator "~*" so that
locations."~ \\\\.(php|asp|aspx|jsp|cgi)$" becomes locations."~*
\\\\.(php|asp|aspx|jsp|cgi)$" and locations."~
^/(containers|_ignition|vendor|public/index)" becomes locations."~*
^/(containers|_ignition|vendor|public/index)"; update both location keys
accordingly.

---

Nitpick comments:
In `@keys.nix`:
- Around line 17-18: The .age files were encrypted before adding alastair to
keys.nix, so re-encrypt the existing secrets with the new recipient list: open
infra/default.nix and run encryptState and encryptVars (or the project’s
equivalent nix task) to regenerate terraform.tfstate.age and
terraform.tfvars.age, commit the updated .age files, and verify decryption using
alastair’s key to confirm they can now decrypt; note that merely adding alastair
to keys.nix does not retroactively re-encrypt existing files.

In `@os.nix`:
- Around line 102-103: The current config enables HTTP compression via the
recommendedGzipSettings variable which, when combined with HTTPS and reflected
user input, can create a BREACH risk; either disable compression globally by
setting recommendedGzipSettings = false or implement a per-endpoint opt-out so
responses that may include secrets skip gzip—add a per-route flag (e.g.,
recommendedGzipSettingsPerLocation or compress: false) and ensure server
middleware (the gzip/compression handler) checks that flag before compressing
responses that reflect user input or include secrets.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1848b948-8393-4cf4-a9ee-140934ad16f4

📥 Commits

Reviewing files that changed from the base of the PR and between 2ca2a6b and b8ca651.

📒 Files selected for processing (2)
  • keys.nix
  • os.nix

Comment thread os.nix Outdated
@findolor findolor force-pushed the alastair/nginx-hardening branch from b8ca651 to 2db92e9 Compare May 4, 2026 09:54
@findolor findolor requested review from JuaniRios and hardyjosh May 4, 2026 10:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants