This commit is contained in:
Amaury JOLY
2026-03-10 18:51:49 +01:00
commit d6a66c16b8
25 changed files with 1653 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
# Module: Bluetooth Configuration
# Description: Enables Bluetooth with dual controller mode and experimental features
# Services: bluetooth, blueman (GUI manager)
{ config, lib, ... }:
{
options.custom.bluetooth = {
enable = lib.mkEnableOption "Bluetooth support with blueman GUI";
powerOnBoot = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Power on Bluetooth adapter on boot";
};
};
config = lib.mkIf config.custom.bluetooth.enable {
hardware.bluetooth = {
enable = true;
powerOnBoot = config.custom.bluetooth.powerOnBoot;
settings = {
General = {
ControllerMode = "dual";
Privacy = "device";
JustWorksRepairing = "always";
Experimental = true;
};
};
};
services.blueman.enable = true;
};
}

View File

@@ -0,0 +1,36 @@
# Module: Laptop Secrets & Base Configuration
# Description: Secrets management (sops-nix) and keyboard layout
# Services: sops-nix
# Dependencies: sops-nix for secrets management
# Note: Other laptop features (gaming, virtualization, etc.) are in separate modules
{ customConfig, ... }:
let
userHome = "/home/${customConfig.username}";
in
{
sops.validateSopsFiles = false;
sops.age.keyFile = "${userHome}/.config/sops/age/keys.txt";
# WiFi networks configuration - entire network list encrypted
sops.secrets.wifi-networks = {
path = "/run/secrets/wifi-networks.conf";
sopsFile = ../../secrets/wifi-networks.yaml;
format = "yaml";
key = "wifi-networks";
owner = "wpa_supplicant";
group = "wpa_supplicant";
mode = "0400";
};
sops.secrets.zwift = {
path = "${userHome}/.config/zwift/config";
sopsFile = ../../secrets/zwift.yaml;
owner = customConfig.username;
group = "users";
mode = "0400";
};
services.xserver.xkb.layout = "fr";
}

View File

@@ -0,0 +1,10 @@
{ pkgs, ... }:
{
services.fprintd.enable = true;
services.fprintd.tod.enable = true;
services.fprintd.tod.driver = pkgs.libfprint-2-tod1-goodix-550a;
security.pam.services.login.fprintAuth = true;
security.pam.services.sudo.fprintAuth = true;
}

29
modules/laptop/gaming.nix Normal file
View File

@@ -0,0 +1,29 @@
# Module: Gaming Support
# Description: Enables Steam and gamepad drivers (xpadneo for Xbox controllers)
# Services: Steam, steam-hardware
{ config, lib, pkgs, ... }:
{
options.custom.gaming = {
enable = lib.mkEnableOption "gaming support (Steam, gamepad drivers)";
enableXpadneo = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable xpadneo driver for Xbox controllers";
};
};
config = lib.mkIf config.custom.gaming.enable {
hardware.steam-hardware.enable = true;
programs.steam = {
enable = true;
};
boot.extraModulePackages = lib.mkIf config.custom.gaming.enableXpadneo [
pkgs.linuxPackages.xpadneo
];
};
}

21
modules/laptop/power.nix Normal file
View File

@@ -0,0 +1,21 @@
# Module: Power Management
# Description: CPU frequency governor and power management settings
# Services: powerManagement
{ config, lib, ... }:
{
options.custom.power = {
enable = lib.mkEnableOption "power management configuration";
cpuGovernor = lib.mkOption {
type = lib.types.str;
default = "powersave";
description = "CPU frequency governor (powersave, performance, ondemand, etc.)";
};
};
config = lib.mkIf config.custom.power.enable {
powerManagement.cpuFreqGovernor = config.custom.power.cpuGovernor;
};
}

View File

@@ -0,0 +1,33 @@
# Module: Printing Configuration
# Description: CUPS printing service with configured printers
# Services: printing (CUPS)
{ config, lib, ... }:
{
options.custom.printing = {
enable = lib.mkEnableOption "printing support (CUPS)";
printers = lib.mkOption {
type = lib.types.listOf lib.types.attrs;
default = [];
description = "List of printers to configure";
};
defaultPrinter = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Default printer name";
};
};
config = lib.mkIf config.custom.printing.enable {
services.printing.enable = true;
hardware.printers = lib.mkIf (config.custom.printing.printers != []) {
ensurePrinters = config.custom.printing.printers;
ensureDefaultPrinter = lib.mkIf (config.custom.printing.defaultPrinter != null)
config.custom.printing.defaultPrinter;
};
};
}

60
modules/laptop/users.nix Normal file
View File

@@ -0,0 +1,60 @@
# Module: User Configuration
# Description: Defines the main user 'alice' with groups, permissions, and user packages
# Packages: Browsers (Firefox), Office (LibreOffice), Development (VSCode, Git),
# Media (VLC, Spotify), Communication (Slack, Thunderbird), and more
{ pkgs, customConfig, ... }:
{
users.users."${customConfig.username}" = {
isNormalUser = true;
home = "/home/${customConfig.username}";
# Base groups - docker/vboxusers are added by virtualization.nix if enabled
extraGroups = [ "wheel" "audio" "dialout" "plugdev" ];
packages = with pkgs; [
# Browsers & Web
firefox
# Office & Productivity
libreoffice
onlyoffice-desktopeditors
obsidian
ticktick
nextcloud-client
# Development
neovim
git
vscode
zotero
tcpdump
pandoc
libsecret
# Communication
slack
thunderbird
discord
# Media & Creative
vlc
spotify
mixxx
pympress
# Gaming & Entertainment
prismlauncher # Minecraft launcher
widelands # Strategy game
wasistlos # Game
moonlight-qt # Game streaming
# System & Cloud
rclone
fuse3
pavucontrol
tree
sops
age
];
};
}

View File

@@ -0,0 +1,42 @@
# Module: Virtualization
# Description: Docker and VirtualBox virtualization support
# Services: Docker daemon, VirtualBox
{ config, lib, pkgs, customConfig, ... }:
{
options.custom.virtualization = {
docker = {
enable = lib.mkEnableOption "Docker container runtime";
dnsServers = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "172.17.0.1" ];
description = "DNS servers for Docker containers (points to dnscrypt-proxy)";
};
};
virtualbox = {
enable = lib.mkEnableOption "VirtualBox virtualization";
};
};
config = lib.mkMerge [
(lib.mkIf config.custom.virtualization.docker.enable {
virtualisation.docker = {
enable = true;
daemon.settings = {
# Docker DNS points to dnscrypt-proxy configured in net.nix
dns = config.custom.virtualization.docker.dnsServers;
};
};
users.users."${customConfig.username}".extraGroups = [ "docker" ];
})
(lib.mkIf config.custom.virtualization.virtualbox.enable {
virtualisation.virtualbox.host.enable = true;
users.users."${customConfig.username}".extraGroups = [ "vboxusers" ];
})
];
}

35
modules/laptop/zwift.nix Normal file
View File

@@ -0,0 +1,35 @@
# Module: Zwift Configuration
# Description: Configures Zwift cycling simulator via Docker with proper networking
# Services: Zwift Docker container
# Ports: UDP 3022, 3024 / TCP 21587, 21588
{ config, lib, pkgs, customConfig, ... }:
{
options.custom.zwift = {
enable = lib.mkEnableOption "Zwift cycling simulator";
};
config = lib.mkIf config.custom.zwift.enable {
programs.zwift = {
enable = true;
image = "docker.io/netbrain/zwift";
version = "latest"; # FIXME: Pin to specific version for reproducibility
containerTool = "docker";
zwiftWorkoutDir = "/var/lib/zwift/workouts";
zwiftActivityDir = "/var/lib/zwift/activities";
zwiftLogDir = "/var/lib/zwift/logs";
zwiftScreenshotsDir = "/var/lib/zwift/screenshots";
zwiftFg = true;
networking = "bridge";
# Use actual user UID/GID instead of hardcoded 1000
zwiftUid = toString config.users.users."${customConfig.username}".uid;
zwiftGid = toString config.users.groups.users.gid;
};
networking.firewall = {
allowedUDPPorts = [ 3022 3024 ];
allowedTCPPorts = [ 21587 21588 ];
};
};
}

63
modules/nixos/base.nix Normal file
View File

@@ -0,0 +1,63 @@
# Module: Base System Configuration
# Description: Core NixOS configuration with Nix settings, base packages, fonts,
# localization (FR), Fish shell, and security (GPG)
# Services: gvfs, udisks2, gnupg-agent
{ pkgs, customConfig, ... }:
{
nix.settings = {
experimental-features = [ "nix-command" "flakes" ];
substituters = [
"https://cache.nixos.org/"
"https://parsec-cloud.cachix.org"
];
trusted-public-keys = [
"parsec-cloud.cachix.org-1:MuWfCBKBfuUWqwB6xKFK0armIJ+A+Mi++HohuB6YvTk="
];
};
programs.nh = {
enable = true;
clean.enable = true;
clean.extraArgs = "--keep-since 4d --keep 3";
flake = builtins.toString customConfig.configFlakePath;
};
nixpkgs.config.allowUnfree = true;
time.timeZone = customConfig.timezone;
services.gvfs.enable = true;
services.udisks2.enable = true;
i18n.defaultLocale = customConfig.locale;
programs.fish.enable = true;
users.defaultUserShell = pkgs.fish;
environment.systemPackages = with pkgs; [
wget
jq
linuxPackages.cpupower
element-desktop
ntfs3g
zip
unzip
];
fonts.packages = with pkgs; [
nerd-fonts.dejavu-sans-mono
nerd-fonts.droid-sans-mono
];
programs.gnupg.agent = {
enable = true;
enableSSHSupport = true;
};
# WARNING: DO NOT CHANGE this value after installation!
# See: https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion
system.stateVersion = "24.05";
}

View File

@@ -0,0 +1,31 @@
# Module: i3 Window Manager Configuration
# Description: Enables X11 with i3 window manager and associated desktop tools
# Services: xserver with i3
# Packages: alacritty (terminal), tint2 (panel), rofi (launcher), i3lock, dunst
{ pkgs, ... }:
{
services.xserver.enable = true;
services.xserver.windowManager.i3.enable = true;
services.xserver.autorun = true;
environment.systemPackages = with pkgs; [
alacritty
tint2
awesome
maim
xclip
dunst
xss-lock
dex
rofi
i3status
i3blocks
oh-my-posh
glances
arandr
nautilus
brightnessctl
];
}

98
modules/nixos/net.nix Normal file
View File

@@ -0,0 +1,98 @@
# Module: Network Configuration
# Description: Network setup with dnscrypt-proxy for encrypted DNS, WiFi networks
# configuration via wpa_supplicant, and hostname settings
# Services: dnscrypt-proxy (primary + backup), wpa_supplicant
# Security: WiFi credentials stored via sops-nix secrets
{ config, lib, pkgs, customConfig, ... }:
let
backupToml = pkgs.writeText "dnscrypt-proxy-backup.toml" ''
listen_addresses = ["127.0.0.2:53"]
server_names = ["dns0-eu"]
[sources.public-resolvers]
urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md']
cache_file = '/var/lib/dnscrypt-proxy-backup/public-resolvers.md'
minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
refresh_delay = 72
'';
userHome = "/home/${customConfig.username}";
in
{
networking.nftables.enable = true;
networking.firewall = {
enable = true;
allowPing = true;
# allowedTCPPorts = [ ... ]; # keep closed by default
interfaces.docker0 = {
allowedUDPPorts = [ 53 ];
allowedTCPPorts = [ 53 ];
};
};
networking.hostName = customConfig.hostname;
# Pick only one of the below networking options.
networking.wireless.enable = true;
# networking.wireless.userControlled = true;
# networking.wireless.secretsFile = config.sops.secrets.wifi.path;
# Load encrypted WiFi networks configuration via wpa_supplicant include files.
# This is supported by the NixOS module and keeps SSIDs out of the Nix store.
networking.wireless.extraConfigFiles = lib.mkIf (config.sops.secrets ? wifi-networks) [
config.sops.secrets.wifi-networks.path
];
networking.wireless.enableHardening = false;
# systemd.services.wpa_supplicant.after = [ "sops-install-secrets.service" ];
# systemd.services.wpa_supplicant.requires = [ "sops-install-secrets.service" ];
# You can also define networks in Nix if you prefer (less secure - names visible):
# networking.wireless.networks = { ... };
networking.interfaces.lo.ipv4.addresses = [
{ address = "127.0.0.1"; prefixLength = 8; }
{ address = "127.0.0.2"; prefixLength = 8; }
];
networking.nameservers = [ "127.0.0.1" "127.0.0.2" ];
# networking.networkmanager.dns = "none";
services.resolved.enable = false;
services.dnscrypt-proxy = {
enable = true;
settings = {
listen_addresses = [ "127.0.0.1:53" "172.17.0.1:53" ];
server_names = [ "amaury" ];
bootstrap_resolvers = [];
sources = {};
static = {
"amaury".stamp = "sdns://AgcAAAAAAAAADTgyLjY0LjIzNy4yNDYADWFtYXVyeWpvbHkuZnIUL2Rucy1xdWVyeS9pZC1hbWF1cnk";
};
cache = true;
ignore_system_dns = true;
timeout = 5000;
};
};
systemd.services."dnscrypt-proxy-backup" = {
description = "dnscrypt-proxy backup (dns0-eu)";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${pkgs.dnscrypt-proxy}/bin/dnscrypt-proxy -config ${backupToml}";
Restart = "on-failure";
NoNewPrivileges = true;
DynamicUser = true;
AmbientCapabilities = "CAP_NET_BIND_SERVICE";
};
};
systemd.services.dnscrypt-proxy.serviceConfig = {
StateDirectory = "dnscrypt-proxy";
};
}

28
modules/nixos/parsec.nix Normal file
View File

@@ -0,0 +1,28 @@
# Module: Parsec Cloud Client
# Description: Installs Parsec Cloud client (v3) with CLI and GUI
# Dependencies: parsec-cloud-nix flake input
# Note: Requires increased Node.js heap size during build (workaround)
{ pkgs, parsec-cloud-nix, ... }:
let
pc = parsec-cloud-nix.packages.${pkgs.stdenv.hostPlatform.system};
# WORKAROUND: Parsec build runs out of memory without increased heap size
# This increases Node.js memory limit from default 512MB to 8GB
nativeBuildPatched = pc.parsec-cloud.v3.native-client-build.overrideAttrs (old: {
NODE_OPTIONS = "--max-old-space-size=8192";
});
parsecClientPatched = pc.parsec-cloud.v3.client.override {
native-client-build = nativeBuildPatched;
};
parsecCli = pc.parsec-cloud.v3.cli;
in
{
environment.systemPackages = [
parsecClientPatched
parsecCli
];
}

View File

@@ -0,0 +1,16 @@
# Module: Wireless Networks Configuration (Encrypted)
# Description: WiFi networks configuration fully encrypted with sops-nix
# Security: Network names, SSIDs, and all configuration stored in encrypted secrets
# Files: ~/.config/secrets/wifi-networks.yaml (encrypted with sops)
# Note: The actual networks are loaded at runtime from the encrypted file
{ config, lib, pkgs, ... }:
{
# WiFi networks are loaded from encrypted file at runtime
# The file is in wpa_supplicant.conf format and gets included by wpa_supplicant
# This approach keeps network names and configuration completely private
# Note: If wifi-networks secret doesn't exist yet, this won't cause errors
# You can still use the old method (networking.wireless.networks in Nix) if needed
}

13
modules/nixos/yubikey.nix Normal file
View File

@@ -0,0 +1,13 @@
# Module: YubiKey Authentication
# Description: Enables YubiKey-based PAM auth for login and sudo across systems
{ pkgs, ... }:
{
security.pam.services = {
login.u2fAuth = true;
sudo.u2fAuth = true;
};
services.udev.packages = [ pkgs.yubikey-personalization ];
}