security: scope privileges and stop a world-readable secret copy (#335)

This commit is contained in:
ryan4yin | 二花 authored and GitHub committed 2026-10-01 21:18:34 +08:00
1 parent d690f45183
commit 4909f6357b
11 files changed
+106 -45

No files matched your search

-21
View File
@@ -10,27 +10,6 @@
outputs = inputs: import ./outputs inputs;
# the nixConfig here only affects the flake itself, not the system configuration!
# for more information, see:
# https://nixos-and-flakes.thiscute.world/nix-store/add-binary-cache-servers
nixConfig = {
# substituers will be appended to the default substituters when fetching packages
extra-substituters = [
"https://cache.numtide.com"
"https://catppuccin.cachix.org"
# "https://nix-gaming.cachix.org"
# "https://nixpkgs-wayland.cachix.org"
# "https://install.determinate.systems"
];
extra-trusted-public-keys = [
"niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g="
"catppuccin.cachix.org-1:noG/4HkbhJb+lUAdKrph6LaozJvAeEEZj4N732IysmU="
# "nix-gaming.cachix.org-1:nbjlureqMbRAxR1gJ/f3hxemL9svXaZF/Ees8vCUUs4="
# "nixpkgs-wayland.cachix.org-1:3lwxaILxMRkVhehr5StQprHdEo4IrE8sRho9R9HOLYA="
# "cache.flakehub.com-3:hJuILl5sVK4iKm86JzgdXW12Y2Hwd5G07qKtHTOcDCM="
];
};
# This is the standard format for flake.nix. `inputs` are the dependencies of the flake,
# Each item in `inputs` will be passed as a parameter to the `outputs` function after being pulled and built.
inputs = {
+19 -10
View File
@@ -1,7 +1,6 @@
{
pkgs,
config,
myvars,
...
}:
{
@@ -12,12 +11,19 @@
"flakes"
];
# given the users in this list the right to specify additional substituters via:
# 1. `nixConfig.substituers` in `flake.nix`
# 2. command line args `--options substituers http://xxx`
trusted-users = [ myvars.username ];
# NOTE: the normal user is deliberately NOT a trusted-user. That would
# grant the right to inject arbitrary substituters (via `nixConfig` in a
# flake, or `--option substituters ...`), which is a known root-equivalent
# vector through the daemon. Every substituter we want is instead listed in
# the system substituters below, which the daemon applies for every user.
# substituers that will be considered before the official ones(https://cache.nixos.org)
# WARNING: `substituters` + `trusted-public-keys` are a security boundary,
# not a convenience list. Nix downloads prebuilt store paths from a
# substituter, and a trusted key means "trust anything signed by it" —
# together they let a cache inject arbitrary store paths into your system,
# which are then run as root at activation. Only add caches you fully trust.
# The official https://cache.nixos.org is always trusted and is appended
# automatically, so it does not need to be listed here.
substituters = [
# cache mirror located in China
# status: https://mirrors.ustc.edu.cn/status/
@@ -28,15 +34,18 @@
# "https://mirrors.sustech.edu.cn/nix-channels/store"
"https://mirrors.tuna.tsinghua.edu.cn/nix-channels/store"
"https://nix-community.cachix.org"
"https://catppuccin.cachix.org"
# numtide's cache (niks3), which serves the prebuilt packages of the
# numtide/llm-agents.nix flake input used for the AI agent CLIs.
"https://cache.numtide.com"
# my own cache server, currently not used.
# "https://ryan4yin.cachix.org"
];
# WARNING: every key here trusts *everything* that key signs. Add one only
# for a cache you trust as much as the official one — a careless key hands
# a third party the ability to ship you arbitrary store paths.
trusted-public-keys = [
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
"catppuccin.cachix.org-1:noG/4HkbhJb+lUAdKrph6LaozJvAeEEZj4N732IysmU="
"niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g="
"ryan4yin.cachix.org-1:Gbk27ZU5AYpGS9i3ssoLlwdvMIh0NxG0w8it/cv9kbU="
];
builders-use-substitutes = true;
+3 -2
View File
@@ -1,7 +1,8 @@
{ lib, ... }:
{
# Secure by default: firewall ON everywhere unless a host explicitly disables it
# (servers disable it in modules/nixos/server/{server,server-aarch64}.nix).
# Secure by default: firewall ON everywhere. Servers run the same shared
# firewall (modules/nixos/base/networking/firewall.nix) as every other host,
# as defence in depth behind the router.
networking.firewall.enable = lib.mkDefault true;
# Enable the OpenSSH daemon.
services.openssh = {
+9 -1
View File
@@ -7,6 +7,15 @@
# Don't allow mutation of users outside the config.
users.mutableUsers = false;
# Root-equivalent / over-broad groups are deliberately NOT added to the
# user's `extraGroups` below, e.g.:
# docker / podman -> rootful container socket (~ root)
# libvirtd -> polkit grants it `org.libvirt.unix.manage` (~ root)
# disk -> raw block devices (~ root)
# input -> keylogging
# Membership is a *passwordless* path to root, so run those commands with
# `sudo` instead (e.g. `sudo virsh`). The groups below exist only for the
# services that need them; `security-container-groups` guards the main ones.
users.groups = {
"${myvars.username}" = { };
podman = { };
@@ -36,7 +45,6 @@
"networkmanager" # for nmtui / nmcli
"wireshark"
"adbusers" # android debugging
"libvirtd" # virt-viewer / qemu
"fileshare"
];
};
+1
View File
@@ -1,6 +1,7 @@
{ ... }:
{
imports = [
./remote-building.nix
../base
../../base
];
@@ -0,0 +1,12 @@
{
lib,
outputs,
...
}:
lib.genAttrs (builtins.attrNames outputs.nixosConfigurations) (_: {
userHasDockerAccess = false;
userHasPodmanAccess = false;
userHasDiskAccess = false;
userHasKvmAccess = false;
userHasInputAccess = false;
})
@@ -0,0 +1,24 @@
{
lib,
outputs,
...
}:
lib.genAttrs (builtins.attrNames outputs.nixosConfigurations) (
name:
let
userGroups = outputs.nixosConfigurations.${name}.config.users.users.ryan.extraGroups;
in
{
# Groups that grant root-equivalent or otherwise over-broad access. The
# normal user must never be a member of any of them:
# docker / podman -> the rootful container socket (~ root)
# disk -> raw block devices (~ root)
# kvm -> /dev/kvm
# input -> read/forge input events (keylogging)
userHasDockerAccess = builtins.elem "docker" userGroups;
userHasPodmanAccess = builtins.elem "podman" userGroups;
userHasDiskAccess = builtins.elem "disk" userGroups;
userHasKvmAccess = builtins.elem "kvm" userGroups;
userHasInputAccess = builtins.elem "input" userGroups;
}
)
@@ -1,4 +1,12 @@
{
lib,
outputs,
...
}:
lib.genAttrs (builtins.attrNames outputs.nixosConfigurations) (_: {
userHasDockerAccess = false;
userHasPodmanAccess = false;
}
userHasDiskAccess = false;
userHasKvmAccess = false;
userHasInputAccess = false;
})
@@ -1,8 +1,24 @@
{ outputs, ... }:
let
userGroups = outputs.nixosConfigurations.ai-niri.config.users.users.ryan.extraGroups;
in
{
userHasDockerAccess = builtins.elem "docker" userGroups;
userHasPodmanAccess = builtins.elem "podman" userGroups;
}
lib,
outputs,
...
}:
lib.genAttrs (builtins.attrNames outputs.nixosConfigurations) (
name:
let
userGroups = outputs.nixosConfigurations.${name}.config.users.users.ryan.extraGroups;
in
{
# Groups that grant root-equivalent or otherwise over-broad access. The
# normal user must never be a member of any of them:
# docker / podman -> the rootful container socket (~ root)
# disk -> raw block devices (~ root)
# kvm -> /dev/kvm
# input -> read/forge input events (keylogging)
userHasDockerAccess = builtins.elem "docker" userGroups;
userHasPodmanAccess = builtins.elem "podman" userGroups;
userHasDiskAccess = builtins.elem "disk" userGroups;
userHasKvmAccess = builtins.elem "kvm" userGroups;
userHasInputAccess = builtins.elem "input" userGroups;
}
)
+6 -3
View File
@@ -139,11 +139,14 @@ in
mode = "0000";
};
# The following secrets are used by home-manager modules
# So we need to make then readable by the user
# Used by a home-manager module, so it must be readable by the user.
# Setting `mode` makes environment.etc copy the file instead of
# symlinking the runtime secret, so the owner must be set as well —
# otherwise it becomes a world-readable, root-owned copy of the secret.
"agenix/nushell-secrets.nu" = {
source = config.age.secrets."nushell-secrets.nu".path;
mode = "0644"; # both the original file and the symlink should be readable and executable by the user
mode = "0400";
user = myvars.username;
};
};
})