Skip to content

feat(matic): add filesystem hardening and immutable root protection - #1251

Merged
shunkakinoki merged 2 commits into
mainfrom
add-safe-rm-linux
Mar 22, 2026
Merged

feat(matic): add filesystem hardening and immutable root protection#1251
shunkakinoki merged 2 commits into
mainfrom
add-safe-rm-linux

Conversation

@shunkakinoki

@shunkakinoki shunkakinoki commented Mar 22, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds kernel sysctl hardening (fs.protected_regular, fs.protected_fifos, fs.protected_symlinks, fs.protected_hardlinks) to prevent symlink/hardlink attacks and unsafe file creation in sticky directories
  • Adds a systemd oneshot service that sets chattr +i / at boot, preventing rm -rf / by blocking creation/removal of top-level entries without affecting normal operations within subdirectories

Test plan

  • nixos-rebuild switch completes without errors
  • lsattr -d / shows immutable flag (i) after boot
  • rm -rf / is blocked (Permission denied on top-level entries)
  • Normal file operations within subdirectories work as expected
  • systemctl stop immutable-root removes the flag when needed

🤖 Generated with Claude Code


Summary by cubic

Adds filesystem hardening and immutable root protection for the matic host. Symlink/hardlink defenses are enabled, and / is set immutable at boot to block rm -rf / while normal subdirectory operations continue to work.

  • New Features
    • Enable kernel sysctls: fs.protected_regular=2, fs.protected_fifos=2, fs.protected_symlinks=1, fs.protected_hardlinks=1.
    • Add immutable-root systemd oneshot service that runs chattr +i / at boot and supports ExecStop with chattr -i / (via e2fsprogs).

Written for commit 41230ba. Summary will update on new commits.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 22, 2026 04:03
@mesa-dot-dev

mesa-dot-dev Bot commented Mar 22, 2026

Copy link
Copy Markdown

You do not have enough credits to review this pull request. Please purchase more credits to continue.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the system's security posture by introducing robust filesystem protections. It aims to prevent common attack vectors related to symbolic and hard links, and provides a critical safeguard against accidental or malicious modification of the root filesystem, ensuring system stability and integrity.

Highlights

  • Filesystem Hardening: Implemented kernel sysctl parameters (fs.protected_regular, fs.protected_fifos, fs.protected_symlinks, fs.protected_hardlinks) to mitigate symlink/hardlink attacks and prevent unsafe file creation in sticky directories.
  • Immutable Root Protection: Introduced a systemd oneshot service that applies an immutable flag (chattr +i /) to the root directory at boot, safeguarding against accidental deletion of top-level entries (e.g., rm -rf /) while allowing normal operations within subdirectories.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai

coderabbitai Bot commented Mar 22, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The change adds filesystem security hardening to a NixOS host configuration by introducing kernel-level protections through sysctl settings (fs.protected_regular, fs.protected_fifos, fs.protected_symlinks, fs.protected_hardlinks) and a new systemd oneshot service that toggles the immutability flag on the root filesystem at system startup and shutdown.

Changes

Cohort / File(s) Summary
Filesystem Hardening Configuration
named-hosts/matic/default.nix
Added kernel sysctl filesystem protections for regular files, FIFOs, symlinks, and hardlinks. Introduced systemd.services.immutable-root oneshot service that applies chattr +i / at startup and chattr -i / at stop with RemainAfterExit = true to persist state.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested labels

enhancement

Poem

🐰 A rabbit hops through hardened grounds,
With sysctl guards and chattr's sounds,
Root lies still, immutable and bright,
Protected fifo, symlink tight,
Security blooms in the midnight light! 🌙

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding filesystem hardening (kernel sysctl protections) and immutable root protection (systemd service). It is concise and clearly reflects the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The pull request description clearly and accurately describes the filesystem hardening changes being made, including kernel sysctl protections and the immutable root protection service.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-safe-rm-linux

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

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

@mesa-dot-dev

mesa-dot-dev Bot commented Mar 22, 2026

Copy link
Copy Markdown

Mesa Description

TL;DR

Adds filesystem hardening and immutable root protection for the matic host, enabling symlink/hardlink defenses and setting the root directory immutable at boot to block rm -rf / while allowing normal subdirectory operations.

What changed?

  • named-hosts/matic/default.nix: Enhanced filesystem security by implementing kernel-level sysctl hardening for various file types and introduced a new systemd service to make the root directory immutable at boot, preventing accidental top-level entry removal.

Description generated by Mesa. Update settings

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No issues found across 1 file

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces valuable security hardening through kernel sysctl parameters and a mechanism to protect the root filesystem from accidental deletion. The filesystem hardening settings are appropriate and well-implemented. However, the approach for creating an immutable root using a systemd service has a critical flaw: it will interfere with the standard NixOS update process (nixos-rebuild switch) by preventing necessary modifications to top-level directories. I have provided a more robust solution using NixOS activation scripts (preActivation and postActivation) that seamlessly integrates with the system update lifecycle, ensuring both protection and maintainability.

Comment on lines +100 to +110
# Immutable root — prevents rm -rf / by blocking top-level entry removal
systemd.services.immutable-root = {
description = "Set immutable flag on /";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = "${pkgs.e2fsprogs}/bin/chattr +i /";
ExecStop = "${pkgs.e2fsprogs}/bin/chattr -i /";
};
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

critical

The current implementation using a systemd service to make the root filesystem immutable will break the standard NixOS update process (nixos-rebuild switch). When the service is active, chattr +i / prevents NixOS activation scripts from modifying files and symlinks in top-level directories (e.g., in /etc), which is a necessary part of an update. This will cause nixos-rebuild switch to fail.

A more idiomatic and robust approach in NixOS is to use system.preActivation and system.postActivation scripts. This ensures the root filesystem is automatically made mutable before an update begins and is made immutable again immediately after it completes, requiring no manual intervention.

        # Immutable root — prevents rm -rf / by blocking top-level entry removal
        # This is handled by pre/post activation scripts to allow `nixos-rebuild switch` to work.
        system.preActivation = ''
          echo "Making root mutable for system update..."
          # Don't fail if the flag isn't set (e.g., on first boot).
          ${pkgs.e2fsprogs}/bin/chattr -i / || true
        '';
        system.postActivation = ''
          echo "Making root immutable..."
          ${pkgs.e2fsprogs}/bin/chattr +i /
        '';

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@named-hosts/matic/default.nix`:
- Around line 101-109: The unit systemd.services.immutable-root flips /
immutable (ExecStart/ExecStop) but has no ordering constraints; add an After=
stanza to systemd.services.immutable-root so it runs only after filesystem
mounts and any services that may write to / (e.g. local-fs.target and
systemd-remount-fs.service, plus your distro package/update services), or move
its activation to a later target (create/use a late-boot/graphical.target and
set WantedBy accordingly) so the chattr +i runs after boot-critical and
root-mutating units complete.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8c1f6acb-18a9-4314-b688-4fbbf776b2b7

📥 Commits

Reviewing files that changed from the base of the PR and between b50815a and f01bf80.

📒 Files selected for processing (1)
  • named-hosts/matic/default.nix

Comment on lines +101 to +109
systemd.services.immutable-root = {
description = "Set immutable flag on /";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = "${pkgs.e2fsprogs}/bin/chattr +i /";
ExecStop = "${pkgs.e2fsprogs}/bin/chattr -i /";
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In systemd, if a service is only WantedBy=multi-user.targetand has noAfter=dependency, can it start in parallel with other units wanted bymulti-user.target?

💡 Result:

Yes, a systemd service with only WantedBy=multi-user.target and no After= (or other ordering dependencies like Before=) can start in parallel with other units that are also wanted by multi-user.target. Systemd activates all units wanted by a target (via Wants= or WantedBy=) when the target is reached. Without explicit ordering dependencies (After=/Before=), these units have no required sequence and are scheduled as parallel jobs. Systemd's design emphasizes parallel activation to speed up boot, as confirmed in official documentation: "NB: ordering and requirement dependencies are orthogonal. If only a requirement dependency exists between two units (e.g. foo.service requires bar.service), but no ordering dependency (e.g. foo.service after bar.service) and both are requested to start, they will be started in parallel." (systemd(1) man page). Units wanted by multi-user.target typically share common implicit or default dependencies (e.g., After=local-fs.target), but among themselves, without After=, they activate concurrently.

Citations:


Add explicit After= dependencies or use a later target to prevent boot failures.

The immutable-root service sets the immutable flag on / but lacks ordering constraints. With only WantedBy=multi-user.target and no After=, systemd will activate this unit in parallel with other multi-user services. Any peer service that needs to write to root-level filesystem locations will fail once the immutable bit is flipped (line 107). Either add After= edges for all root-mutating units, or move this behind a later target that executes after boot-critical services complete.

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

In `@named-hosts/matic/default.nix` around lines 101 - 109, The unit
systemd.services.immutable-root flips / immutable (ExecStart/ExecStop) but has
no ordering constraints; add an After= stanza to systemd.services.immutable-root
so it runs only after filesystem mounts and any services that may write to /
(e.g. local-fs.target and systemd-remount-fs.service, plus your distro
package/update services), or move its activation to a later target (create/use a
late-boot/graphical.target and set WantedBy accordingly) so the chattr +i runs
after boot-critical and root-mutating units complete.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds host-level filesystem hardening for the matic NixOS configuration, aiming to reduce common local FS attack vectors and add an extra guardrail against destructive operations on /.

Changes:

  • Enables kernel sysctl protections for symlink/hardlink handling and sticky-directory unsafe file creation.
  • Adds a systemd oneshot service to set the immutable attribute on / at boot (and remove it when the unit is stopped).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +100 to +103
# Immutable root — prevents rm -rf / by blocking top-level entry removal
systemd.services.immutable-root = {
description = "Set immutable flag on /";
wantedBy = [ "multi-user.target" ];

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

The comment/PR intent says this “prevents rm -rf /”, but chattr +i / only prevents creating/removing top-level entries under / (e.g., /etc itself). It does not prevent deletion of contents inside existing directories (e.g., rm -rf --no-preserve-root /etc/* would still succeed), so this provides a false sense of protection. Please either (a) reword the comment (and PR description/test plan) to match the actual behavior, or (b) change the approach if the goal is to prevent destructive recursive deletion of the filesystem contents.

Copilot uses AI. Check for mistakes.
Comment on lines +101 to +109
systemd.services.immutable-root = {
description = "Set immutable flag on /";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = "${pkgs.e2fsprogs}/bin/chattr +i /";
ExecStop = "${pkgs.e2fsprogs}/bin/chattr -i /";
};

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

immutable-root is wanted by multi-user.target but has no explicit ordering. To avoid racing with activation/tmpfiles work that might still need to create top-level mountpoints/directories on first boot (or after config changes), consider ordering it after nixos-activation.service, systemd-tmpfiles-setup.service, and local-fs.target (and optionally making it requires = [ "local-fs.target" ]). This makes the hardening less likely to cause boot-time failures.

Copilot uses AI. Check for mistakes.
@shunkakinoki
shunkakinoki merged commit 4b8399f into main Mar 22, 2026
29 of 33 checks passed
@shunkakinoki
shunkakinoki deleted the add-safe-rm-linux branch March 22, 2026 09:27
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