1 Commits

Author SHA1 Message Date
b3ca6dbac3 WIP: home manager 2025-02-01 18:27:05 -08:00
7 changed files with 254 additions and 864 deletions

53
flake.lock generated
View File

@@ -1,25 +1,5 @@
{ {
"nodes": { "nodes": {
"home-manager": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1740432748,
"narHash": "sha256-BCeFtoJ/+LrZc03viRJWHfzAqqG8gPu/ikZeurv05xs=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "c12dcc9b61429b2ad437a7d4974399ad8f910319",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"nixlib": { "nixlib": {
"locked": { "locked": {
"lastModified": 1736643958, "lastModified": 1736643958,
@@ -58,11 +38,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1740367490, "lastModified": 1738142207,
"narHash": "sha256-WGaHVAjcrv+Cun7zPlI41SerRtfknGQap281+AakSAw=", "narHash": "sha256-NGqpVVxNAHwIicXpgaVqJEJWeyqzoQJ9oc8lnK9+WC4=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "0196c0175e9191c474c26ab5548db27ef5d34b05", "rev": "9d3ae807ebd2981d593cddd0080856873139aa40",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -72,35 +52,10 @@
"type": "github" "type": "github"
} }
}, },
"plasma-manager": {
"inputs": {
"home-manager": [
"home-manager"
],
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1739557722,
"narHash": "sha256-XikzLpPUDYiNyJ4w2SfRShdbSkIgE3btYdxCGInmtc4=",
"owner": "nix-community",
"repo": "plasma-manager",
"rev": "1f3e1f38dedbbb8aad77e184fb54ec518e2d9522",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "plasma-manager",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"home-manager": "home-manager",
"nixos-generators": "nixos-generators", "nixos-generators": "nixos-generators",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs"
"plasma-manager": "plasma-manager"
} }
} }
}, },

View File

@@ -5,22 +5,17 @@
nixpkgs = { nixpkgs = {
url = "github:NixOS/nixpkgs/nixos-unstable"; url = "github:NixOS/nixpkgs/nixos-unstable";
}; };
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
plasma-manager = {
url = "github:nix-community/plasma-manager";
inputs.nixpkgs.follows = "nixpkgs";
inputs.home-manager.follows = "home-manager";
};
nixos-generators = { nixos-generators = {
url = "github:nix-community/nixos-generators"; url = "github:nix-community/nixos-generators";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
}; };
outputs = { self, nixpkgs, home-manager, plasma-manager, nixos-generators }: { outputs = { self, nixpkgs, nixos-generators, home-manager }: {
nixosConfigurations = { nixosConfigurations = {
Yura-PC = nixpkgs.lib.nixosSystem { Yura-PC = nixpkgs.lib.nixosSystem {
system = "x86_64-linux"; system = "x86_64-linux";
@@ -28,17 +23,6 @@
./modules ./modules
./hosts/common.nix ./hosts/common.nix
./hosts/Yura-PC ./hosts/Yura-PC
# https://nix-community.github.io/home-manager/index.xhtml#sec-flakes-nixos-module
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.sharedModules = [ plasma-manager.homeManagerModules.plasma-manager ];
home-manager.users.cazzzer = import ./home;
# Optionally, use home-manager.extraSpecialArgs to pass
# arguments to home.nix
}
]; ];
}; };
VM = nixpkgs.lib.nixosSystem { VM = nixpkgs.lib.nixosSystem {
@@ -47,6 +31,16 @@
./modules ./modules
./hosts/common.nix ./hosts/common.nix
./hosts/vm ./hosts/vm
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.jdoe = import ./home.nix;
# Optionally, use home-manager.extraSpecialArgs to pass
# arguments to home.nix
}
]; ];
}; };
router = nixpkgs.lib.nixosSystem { router = nixpkgs.lib.nixosSystem {

View File

@@ -1,241 +0,0 @@
{ config, lib, pkgs, ... }:
let
defaultFont = {
family = "Noto Sans";
pointSize = 14;
};
in
{
# Home Manager needs a bit of information about you and the paths it should
# manage.
home.username = "cazzzer";
home.homeDirectory = "/home/cazzzer";
# Let Home Manager install and manage itself.
programs.home-manager.enable = true;
home.sessionVariables = {
EDITOR = "micro";
SHELL = "fish";
};
services.gnome-keyring = {
enable = true;
components = [ "pkcs11" "ssh" ];
};
services.darkman = {
enable = true;
settings = {
lat = 37.3387;
lng = -121.8853;
};
lightModeScripts = {
plasma-color = "plasma-apply-colorscheme BreezeLight";
};
darkModeScripts = {
plasma-color = "plasma-apply-colorscheme BreezeDark";
};
};
programs.fish = {
enable = true;
shellInit = "set fish_greeting";
shellAliases = {
# Replace ls with exa
ls = "exa -al --color=always --group-directories-first --icons"; # preferred listing
la = "exa -a --color=always --group-directories-first --icons"; # all files and dirs
ll = "exa -l --color=always --group-directories-first --icons"; # long format
lt = "exa -aT --color=always --group-directories-first --icons"; # tree listing
"l." = "exa -a | rg '^\.'"; # show only dotfiles
# Replace cat with bat
cat = "bat";
};
# alias for nix shell with flake packages
functions.add.body = ''
set -x packages 'nixpkgs#'(string join ' nixpkgs#' $argv)
nix shell $packages --command fish
'';
interactiveShellInit = ''
fastfetch
'';
};
programs.starship = {
enable = true;
enableFishIntegration = true;
settings = {
format = lib.concatStrings [
"$all"
"$time"
"$cmd_duration"
"$line_break"
"$jobs"
"$status"
"$character"
];
username = {
format = " [$user]($style)@";
style_user = "bold red";
style_root = "bold red";
show_always = true;
};
hostname = {
format = "[$hostname]($style) in ";
style = "bold dimmed red";
ssh_only = false;
};
directory = {
style = "purple";
truncation_length = 0;
truncate_to_repo = true;
truncation_symbol = "repo: ";
};
git_status = {
style = "white";
ahead = "\${count}";
diverged = "\${ahead_count}\${behind_count}";
behind = "\${count}";
deleted = "x";
};
cmd_duration = {
min_time = 1000;
format = "took [$duration]($style) ";
};
time = {
format = " 🕙 $time($style) ";
time_format = "%T";
style = "bright-white";
disabled = false;
};
character = {
success_symbol = " [λ](bold red)";
error_symbol = " [×](bold red)";
};
status = {
symbol = "🔴";
format = "[\\[$symbol$status_common_meaning$status_signal_name$status_maybe_int\\]]($style)";
map_symbol = true;
disabled = false;
};
};
};
programs.plasma = {
enable = true;
overrideConfig = true;
workspace.iconTheme = "Tela-circle";
fonts = {
general = defaultFont;
fixedWidth = defaultFont // { family = "Hack"; };
small = defaultFont // { pointSize = defaultFont.pointSize - 2; };
toolbar = defaultFont;
menu = defaultFont;
windowTitle = defaultFont;
};
input.keyboard.layouts = [
{ layout = "us"; displayName = "us"; }
{ layout = "minimak-4"; displayName = "us4"; }
{ layout = "ru"; displayName = "ru"; }
];
kwin.virtualDesktops.number = 2;
session.sessionRestore.restoreOpenApplicationsOnLogin = "startWithEmptySession";
shortcuts = {
# kmix.mic_mute = "ScrollLock";
kmix.mic_mute = ["Microphone Mute" "ScrollLock" "Meta+Volume Mute,Microphone Mute" "Meta+Volume Mute,Mute Microphone"];
plasmashell.show-barcode = "Meta+M";
kwin."Window Maximize" = [ "Meta+F" "Meta+PgUp,Maximize Window" ];
"KDE Keyboard Layout Switcher"."Switch to Next Keyboard Layout" = "Meta+Space";
};
hotkeys.commands."launch-konsole" = {
name = "Launch Konsole";
key = "Meta+Alt+C";
command = "konsole";
};
configFile = {
kdeglobals.KDE.AnimationDurationFactor = 0.5;
kdeglobals.General.accentColorFromWallpaper = true;
kwinrc.Wayland.InputMethod = {
value = "org.fcitx.Fcitx5.desktop";
shellExpand = true;
};
dolphinrc.General.ShowFullPath = true;
kactivitymanagerdrc = {
activities."809dc779-bf5b-49e6-8e3f-cbe283cb05b6" = "Default";
activities."b34a506d-ac4f-4797-8c08-6ef45bc49341" = "Fun";
activities-icons."809dc779-bf5b-49e6-8e3f-cbe283cb05b6" = "keyboard";
activities-icons."b34a506d-ac4f-4797-8c08-6ef45bc49341" = "preferences-desktop-gaming";
};
};
};
xdg.configFile = {
"fcitx5/conf/wayland.conf".text = "Allow Overriding System XKB Settings=False";
};
# This value determines the Home Manager release that your configuration is
# compatible with. This helps avoid breakage when a new Home Manager release
# introduces backwards incompatible changes.
#
# You should not change this value, even if you update Home Manager. If you do
# want to update the value, then make sure to first check the Home Manager
# release notes.
home.stateVersion = "24.11"; # Please read the comment before changing.
# The home.packages option allows you to install Nix packages into your
# environment.
# home.packages = [
# # Adds the 'hello' command to your environment. It prints a friendly
# # "Hello, world!" when run.
# pkgs.hello
# # It is sometimes useful to fine-tune packages, for example, by applying
# # overrides. You can do that directly here, just don't forget the
# # parentheses. Maybe you want to install Nerd Fonts with a limited number of
# # fonts?
# (pkgs.nerdfonts.override { fonts = [ "FantasqueSansMono" ]; })
# # You can also create simple shell scripts directly inside your
# # configuration. For example, this adds a command 'my-hello' to your
# # environment:
# (pkgs.writeShellScriptBin "my-hello" ''
# echo "Hello, ${config.home.username}!"
# '')
# ];
# Home Manager is pretty good at managing dotfiles. The primary way to manage
# plain files is through 'home.file'.
# home.file = {
# # Building this configuration will create a copy of 'dotfiles/screenrc' in
# # the Nix store. Activating the configuration will then make '~/.screenrc' a
# # symlink to the Nix store copy.
# ".screenrc".source = dotfiles/screenrc;
# # You can also set the file content immediately.
# ".gradle/gradle.properties".text = ''
# org.gradle.console=verbose
# org.gradle.daemon.idletimeout=3600000
# '';
# };
# Home Manager can also manage your environment variables through
# 'home.sessionVariables'. These will be explicitly sourced when using a
# shell provided by Home Manager. If you don't want to manage your shell
# through Home Manager then you have to manually source 'hm-session-vars.sh'
# located at either
#
# ~/.nix-profile/etc/profile.d/hm-session-vars.sh
#
# or
#
# ~/.local/state/nix/profiles/profile/etc/profile.d/hm-session-vars.sh
#
# or
#
# /etc/profiles/per-user/cazzzer/etc/profile.d/hm-session-vars.sh
#
# home.sessionVariables = {
# EDITOR = "emacs";
# };
}

View File

@@ -125,10 +125,6 @@
# Nix # Nix
nixd nixd
# Gleam
gleam
beamMinimal26Packages.erlang
]; ];
}; };
@@ -156,6 +152,7 @@
virtualisation.docker.enable = true; virtualisation.docker.enable = true;
virtualisation.docker.enableOnBoot = false; virtualisation.docker.enableOnBoot = false;
virtualisation.docker.package = pkgs.docker_27; virtualisation.docker.package = pkgs.docker_27;
virtualisation.docker.storageDriver = "zfs";
# https://discourse.nixos.org/t/firefox-does-not-use-kde-window-decorations-and-cursor/32132/3 # https://discourse.nixos.org/t/firefox-does-not-use-kde-window-decorations-and-cursor/32132/3
# programs.dconf.enable = true; # programs.dconf.enable = true;
@@ -225,10 +222,9 @@
gnome-keyring # config for this and some others gnome-keyring # config for this and some others
gnumake gnumake
helix helix
jetbrains-toolbox # or maybe do invidual ones?
# jetbrains.rust-rover # jetbrains.rust-rover
# jetbrains.goland
jetbrains.clion jetbrains.clion
jetbrains.idea-ultimate
jetbrains.pycharm-professional jetbrains.pycharm-professional
jetbrains.webstorm jetbrains.webstorm
android-studio android-studio
@@ -265,8 +261,8 @@
# Open ports in the firewall. # Open ports in the firewall.
# networking.nftables.enable = true; # networking.nftables.enable = true;
networking.firewall.allowedTCPPorts = [ 8080 22000 ]; networking.firewall.allowedTCPPorts = [ 8080 ];
networking.firewall.allowedUDPPorts = [ 22000 ]; # networking.firewall.allowedUDPPorts = [ ... ];
# Or disable the firewall altogether. # Or disable the firewall altogether.
# networking.firewall.enable = false; # networking.firewall.enable = false;

View File

@@ -1,4 +1,11 @@
{ config, pkgs, ... }: { { config, pkgs, inputs, ... }: {
imports = [
inputs.home-manager.nixosModules.home-manager
];
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
# Allow unfree packages # Allow unfree packages
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;

View File

@@ -2,285 +2,31 @@
let let
domain = "cazzzer.com"; domain = "cazzzer.com";
ldomain = "l.${domain}"; ldomain = "l.${domain}";
sysdomain = "sys.${domain}";
links = {
wanMAC = "bc:24:11:09:49:b0";
lanMAC = "bc:24:11:48:9c:85";
wanLL = "fe80::be24:11ff:fe09:49b0";
lanLL = "fe80::be24:11ff:fe48:9c85";
};
mkIfConfig = { if_wan = "wan";
name_, if_lan = "lan";
domain_, if_lan10 = "lan.10";
p4_, # /24 if_lan20 = "lan.20";
p6_, # /64
ulaPrefix_, # /64
token? 1,
ip6Token_? "::${toString token}",
ulaToken_? "::${toString token}",
}: rec {
name = name_;
domain = domain_;
p4 = p4_;
p4Size = 24;
net4 = "${p4}.0/${toString p4Size}";
addr4 = "${p4}.${toString token}";
addr4Sized = "${addr4}/${toString p4Size}";
p6 = p6_;
p6Size = 64;
net6 = "${p6}::/${toString p6Size}";
ip6Token = ip6Token_;
addr6 = "${p6}${ip6Token}";
addr6Sized = "${addr6}/${toString p6Size}";
ulaPrefix = ulaPrefix_;
ulaSize = 64;
ulaNet = "${ulaPrefix}::/${toString ulaSize}";
ulaToken = ulaToken_;
ulaAddr = "${ulaPrefix}${ulaToken}";
ulaAddrSized = "${ulaAddr}/${toString ulaSize}";
};
p4 = "10.17"; # .0.0/16 wan_ip4 = "192.168.1.61/24";
pdFromWan = ""; # ::/60 wan_gw4 = "192.168.1.254";
ulaPrefix = "fdab:07d3:581d"; # ::/48
ifs = rec {
wan = rec {
name = "wan";
addr4 = "192.168.1.62";
p4Size = 24;
addr4Sized = "${addr4}/${toString p4Size}";
gw4 = "192.168.1.254";
};
lan = mkIfConfig {
name_ = "lan";
domain_ = "lan.${ldomain}";
p4_ = "${p4}.1"; # .0/24
p6_ = "${pdFromWan}f"; # ::/64
ulaPrefix_ = "${ulaPrefix}:0001"; # ::/64
};
lan10 = mkIfConfig {
name_ = "${lan.name}.10";
domain_ = "lab.${ldomain}";
p4_ = "${p4}.10"; # .0/24
p6_ = "${pdFromWan}9"; # ::/64
ulaPrefix_ = "${ulaPrefix}:0010"; # ::/64
};
lan20 = mkIfConfig {
name_ = "${lan.name}.20";
domain_ = "life.${ldomain}";
p4_ = "${p4}.20"; # .0/24
p6_ = "${pdFromWan}0"; # ::/64 managed by Att box
ulaPrefix_ = "${ulaPrefix}:0020"; # ::/64
ip6Token_ = "::2:1"; # override ipv6 for lan20, since the Att box uses ::1 here
};
lan30 = mkIfConfig {
name_ = "${lan.name}.30";
domain_ = "iot.${ldomain}";
p4_ = "${p4}.30"; # .0/24
p6_ = "${pdFromWan}c"; # ::/64
ulaPrefix_ = "${ulaPrefix}:0030"; # ::/64
};
lan40 = mkIfConfig {
name_ = "${lan.name}.40";
domain_ = "kube.${ldomain}";
p4_ = "${p4}.40"; # .0/24
p6_ = "${pdFromWan}b"; # ::/64
ulaPrefix_ = "${ulaPrefix}:0040"; # ::/64
};
lan50 = mkIfConfig {
name_ = "${lan.name}.50";
domain_ = "prox.${ldomain}";
p4_ = "${p4}.50"; # .0/24
p6_ = "${pdFromWan}a"; # ::/64
ulaPrefix_ = "${ulaPrefix}:0050"; # ::/64
};
};
# Reservations added to Kea lan_p4 = "10.19.1"; # .0/24
reservations.lan.v4.reservations = [ lan10_p4 = "10.19.10"; # .0/24
{ lan20_p4 = "10.19.20"; # .0/24
hw-address = "64:66:b3:78:9c:09";
hostname = "openwrt";
ip-address = "${ifs.lan.p4}.2";
}
{
hw-address = "40:86:cb:19:9d:70";
hostname = "dlink-switchy";
ip-address = "${ifs.lan.p4}.3";
}
{
hw-address = "6c:cd:d6:af:4f:6f";
hostname = "netgear-switchy";
ip-address = "${ifs.lan.p4}.4";
}
{
hw-address = "74:d4:35:1d:0e:80";
hostname = "pve-1";
ip-address = "${ifs.lan.p4}.5";
}
{
hw-address = "00:25:90:f3:d0:e0";
hostname = "pve-2";
ip-address = "${ifs.lan.p4}.6";
}
{
hw-address = "a8:a1:59:d0:57:87";
hostname = "pve-3";
ip-address = "${ifs.lan.p4}.7";
}
{
hw-address = "22:d0:43:c6:31:92";
hostname = "truenas";
ip-address = "${ifs.lan.p4}.10";
}
{
hw-address = "1e:d5:56:ec:c7:4a";
hostname = "debbi";
ip-address = "${ifs.lan.p4}.11";
}
{
hw-address = "ee:42:75:2e:f1:a6";
hostname = "etappi";
ip-address = "${ifs.lan.p4}.12";
}
];
reservations.lan.v6.reservations = [ pd_from_wan = ""; # ::/60
{ lan_p6 = "${pd_from_wan}9"; # ::/64
duid = "00:03:00:01:64:66:b3:78:9c:09"; lan10_p6 = "${pd_from_wan}a"; # ::/64
hostname = "openwrt"; lan20_p6 = "${pd_from_wan}2"; # ::/64
ip-addresses = [ "${ifs.lan.p6}::1:2" ];
}
{
duid = "00:01:00:01:2e:c0:63:23:22:d0:43:c6:31:92";
hostname = "truenas";
ip-addresses = [ "${ifs.lan.p6}::10:1" ];
}
{
duid = "00:02:00:00:ab:11:09:41:25:21:32:71:e3:77";
hostname = "debbi";
ip-addresses = [ "${ifs.lan.p6}::11:1" ];
}
{
duid = "00:02:00:00:ab:11:6b:56:93:72:0b:3c:84:11";
hostname = "etappi";
ip-addresses = [ "${ifs.lan.p6}::12:1" ];
}
];
reservations.lan20.v4.reservations = [ ula_p = "fdab:07d3:581d"; # ::/48
{ lan_ula_p = "${ula_p}:0001"; # ::/64
# Router lan10_ula_p = "${ula_p}:0010"; # ::/64
hw-address = "1c:3b:f3:da:5f:cc"; lan20_ula_p = "${ula_p}:0020"; # ::/64
hostname = "archer-ax3000"; lan_ula_addr = "${lan_ula_p}::1";
ip-address = "${ifs.lan20.p4}.2"; lan10_ula_addr = "${lan10_ula_p}::1";
} lan20_ula_addr = "${lan20_ula_p}::1";
{
# Printer
hw-address = "30:cd:a7:c5:40:71";
hostname = "SEC30CDA7C54071";
ip-address = "${ifs.lan20.p4}.9";
}
{
# 3D Printer
hw-address = "20:f8:5e:ff:ae:5f";
hostname = "GS_ffae5f";
ip-address = "${ifs.lan20.p4}.11";
}
{
hw-address = "70:85:c2:d8:87:3f";
hostname = "Yura-PC";
ip-address = "${ifs.lan20.p4}.40";
}
];
alpinaDomains = [
"|"
"|nc."
"|sonarr."
"|radarr."
"|prowlarr."
"|qbit."
"|gitea."
"|traefik."
"|auth."
"||s3."
"|minio."
"|jellyfin."
"|whoami."
"|grafana."
"|influxdb."
"|uptime."
"|opnsense."
"|vpgen."
"|woodpecker."
"||pgrok."
"|sync."
];
mkVlanDev = { id, name }: {
netdevConfig = {
Kind = "vlan";
Name = name;
};
vlanConfig.Id = id;
};
mkLanConfig = ifObj: {
matchConfig.Name = ifObj.name;
networkConfig = {
IPv4Forwarding = true;
IPv6SendRA = (ifObj.name != ifs.lan10.name); # TODO: temporary test, remove
Address = [ ifObj.addr4Sized ifObj.addr6Sized ifObj.ulaAddrSized ];
};
ipv6Prefixes = [
{
Prefix = ifObj.net6;
Assign = true;
# Token = [ "static::1" "eui64" ];
Token = [ "static:${ifObj.ip6Token}" ];
}
{
Prefix = ifObj.ulaNet;
Assign = true;
Token = [ "static:${ifObj.ulaToken}" ];
}
];
ipv6RoutePrefixes = [ { Route = "${ulaPrefix}::/48"; } ];
ipv6SendRAConfig = {
# don't manage the att box subnet
# should work fine either way though
Managed = (ifObj.p6 != "${pdFromWan}0");
OtherInformation = (ifObj.p6 != "${pdFromWan}0");
EmitDNS = true;
DNS = [ ifObj.ulaAddr ];
};
};
mkDhcp4Subnet = id: ifObj: {
id = id;
subnet = ifObj.net4;
pools = [ { pool = "${ifObj.p4}.100 - ${ifObj.p4}.199"; } ];
ddns-qualifying-suffix = "4.${ifObj.domain}";
option-data = [
{ name = "routers"; data = ifObj.addr4; }
{ name = "domain-name-servers"; data = ifObj.addr4; }
{ name = "domain-name"; data = "4.${ifObj.domain}"; }
];
};
mkDhcp6Subnet = id: ifObj: {
id = id;
interface = ifObj.name;
subnet = ifObj.net6;
rapid-commit = true;
pools = [ { pool = "${ifObj.p6}::1:1000/116"; } ];
ddns-qualifying-suffix = "6.${ifObj.domain}";
option-data = [
{ name = "domain-search"; data = "6.${ifObj.domain}"; }
];
};
in in
{ {
imports = imports =
@@ -295,20 +41,11 @@ in
"sysrq_always_enabled=1" "sysrq_always_enabled=1"
]; ];
boot.loader.timeout = 2;
boot.loader.systemd-boot.configurationLimit = 5; boot.loader.systemd-boot.configurationLimit = 5;
boot.kernelPackages = pkgs.linuxKernel.packages.linux_6_12; boot.kernelPackages = pkgs.linuxKernel.packages.linux_6_12;
boot.growPartition = true; boot.growPartition = true;
boot.kernel.sysctl = {
"net.ipv4.conf.all.forwarding" = true;
"net.ipv6.conf.all.forwarding" = true;
# https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes
# For upstream quic dns
"net.core.wmem_max" = 7500000;
"net.core.rmem_max" = 7500000;
};
environment.etc.hosts.mode = "0644";
networking.hostName = "grouter"; networking.hostName = "grouter";
# It is impossible to do multiple prefix requests with networkd, # It is impossible to do multiple prefix requests with networkd,
@@ -325,34 +62,30 @@ in
nohook resolv.conf, yp, hostname, ntp nohook resolv.conf, yp, hostname, ntp
option rapid_commit option rapid_commit
interface ${ifs.wan.name} interface ${if_wan}
ipv6rs ipv6rs
dhcp6 dhcp6
duid
ipv6only
nohook resolv.conf, yp, hostname, ntp
option rapid_commit
# this doesn't play well with networkd # this doesn't play well with networkd
# ia_na # ia_na
# ia_pd 1 ${ifs.lan.name}/0 # ia_pd 1 ${if_lan}/0
# ia_pd 2 ${ifs.lan10.name}/0 # ia_pd 2 ${if_lan10}/0
# ia_pd 3 ${ifs.lan20.name}/0 # ia_pd 3 ${if_lan20}/0
# request the leases just for routing (so that the att box knows we're here) # request the leases just for routing (so that the att box knows we're here)
# actual ip assignments are static, based on $pdFromWan # actual ip assignments are static, based on $pd_from_wan
# ia_pd 1/${ifs.lan.net6} - ia_pd 1 -
ia_pd 10/${ifs.lan10.net6} - ia_pd 2 -
# ia_pd 20/${pdFromWan}d::/64 - # for opnsense (legacy services) # ia_pd 3 -
# ia_pd 30/${ifs.lan30.net6} - # ia_pd 4 -
# ia_pd 40/${ifs.lan40.net6} - # ia_pd 5 -
# ia_pd 50/${ifs.lan50.net6} - # ia_pd 6 -
# ia_pd 7 - # ia_pd 7 -
# ia_pd 8 - # ia_pd 8 -
''; '';
networking.useNetworkd = false; networking.useNetworkd = true;
systemd.network.enable = false; systemd.network.enable = true;
systemd.network = { systemd.network = {
# Global options # Global options
config.networkConfig = { config.networkConfig = {
@@ -365,190 +98,165 @@ in
# https://nixos.org/manual/nixos/stable/#sec-rename-ifs # https://nixos.org/manual/nixos/stable/#sec-rename-ifs
links = { links = {
"10-wan" = { "10-wan" = {
matchConfig.PermanentMACAddress = links.wanMAC; matchConfig.PermanentMACAddress = "bc:24:11:4f:c9:c4";
linkConfig.Name = ifs.wan.name; linkConfig.Name = if_wan;
}; };
"10-lan" = { "10-lan" = {
matchConfig.PermanentMACAddress = links.lanMAC; matchConfig.PermanentMACAddress = "bc:24:11:83:d8:de";
linkConfig.Name = ifs.lan.name; linkConfig.Name = if_lan;
}; };
}; };
netdevs = { netdevs = {
"10-vlan10" = mkVlanDev { id = 10; name = ifs.lan10.name; }; "10-vlan10" = {
"10-vlan20" = mkVlanDev { id = 20; name = ifs.lan20.name; }; netdevConfig = {
"10-vlan30" = mkVlanDev { id = 30; name = ifs.lan30.name; }; Kind = "vlan";
"10-vlan40" = mkVlanDev { id = 40; name = ifs.lan40.name; }; Name = if_lan10;
"10-vlan50" = mkVlanDev { id = 50; name = ifs.lan50.name; }; };
vlanConfig.Id = 10;
};
"10-vlan20" = {
netdevConfig = {
Kind = "vlan";
Name = if_lan20;
};
vlanConfig.Id = 20;
};
}; };
networks = { networks = {
"10-wan" = { "10-wan" = {
matchConfig.Name = ifs.wan.name; matchConfig.Name = if_wan;
networkConfig = { networkConfig = {
# start a DHCP Client for IPv4 Addressing/Routing # start a DHCP Client for IPv4 Addressing/Routing
# DHCP = "ipv4"; # DHCP = "ipv4";
# accept Router Advertisements for Stateless IPv6 Autoconfiguraton (SLAAC) # accept Router Advertisements for Stateless IPv6 Autoconfiguraton (SLAAC)
# let dhcpcd handle this # let dhcpcd handle this
Address = [ ifs.wan.addr4Sized ]; Address = [ wan_ip4 ];
IPv6AcceptRA = false; IPv6AcceptRA = false;
}; };
routes = [ { Gateway = ifs.wan.gw4; } ]; routes = [ { Gateway = wan_gw4; } ];
# make routing on this interface a dependency for network-online.target # make routing on this interface a dependency for network-online.target
linkConfig.RequiredForOnline = "routable"; linkConfig.RequiredForOnline = "routable";
}; };
"20-lan" = (mkLanConfig ifs.lan) // { "20-lan" = {
matchConfig.Name = "lan";
vlan = [ vlan = [
ifs.lan10.name if_lan10
ifs.lan20.name if_lan20
ifs.lan30.name
ifs.lan40.name
ifs.lan50.name
]; ];
}; networkConfig = {
# "30-vlan10" = mkLanConfig ifs.lan10; IPv4Forwarding = true;
"30-vlan20" = mkLanConfig ifs.lan20 // { IPv6SendRA = true;
routes = [ Address = [ "${lan_p4}.1/24" ];
};
ipv6Prefixes = [
{ {
# OPNsense subnet route # AddressAutoconfiguration = false;
Destination = "${pdFromWan}d::/64"; Prefix = "${lan_p6}::/64";
Gateway = "fe80::1efd:8ff:fe71:954e"; Assign = true;
# Token = [ "static:::1" "eui64" ];
Token = [ "static:::1" ];
}
{
Prefix = "${lan_ula_p}::/64";
Assign = true;
Token = [ "static:::1" ];
}
];
ipv6SendRAConfig = {
Managed = true;
OtherInformation = true;
EmitDNS = true;
DNS = [ lan_ula_addr ];
};
};
"30-vlan10" = {
matchConfig.Name = if_lan10;
networkConfig = {
IPv6SendRA = true;
Address = [ "${lan10_p4}.1/24" ];
};
ipv6Prefixes = [
{
Prefix = "${lan10_p6}::/64";
Assign = true;
Token = [ "static:::1" ];
}
{
Prefix = "${lan10_ula_p}::/64";
Assign = true;
Token = [ "static:::1" ];
}
];
};
"30-vlan20" = {
matchConfig.Name = if_lan20;
networkConfig = {
IPv6SendRA = true;
Address = [ "${lan20_p4}.1/24" ];
};
ipv6Prefixes = [
{
Prefix = "${lan20_p6}::/64";
Assign = true;
Token = [ "static:::1" ];
}
{
Prefix = "${lan20_ula_p}::/64";
Assign = true;
Token = [ "static:::1" ];
} }
]; ];
}; };
"30-vlan30" = mkLanConfig ifs.lan30;
"30-vlan40" = mkLanConfig ifs.lan40;
"30-vlan50" = mkLanConfig ifs.lan50;
}; };
}; };
networking.vlans = {
${ifs.lan10.name} = { id = 10; interface = ifs.lan.name; };
};
networking.interfaces = {
${ifs.wan.name} = {
useDHCP = true;
ipv4.addresses = [ { address = ifs.wan.addr4; prefixLength = ifs.wan.p4Size; } ];
ipv6.addresses = [
{
address = "${pdFromWan}0::1:1";
prefixLength = 64;
}
];
ipv4.routes = [
{ address = "0.0.0.0"; prefixLength = 0; via = ifs.wan.gw4; }
];
};
${ifs.lan10.name} = {
ipv4.addresses = [ { address = ifs.lan10.addr4; prefixLength = ifs.lan10.p4Size; } ];
ipv6.addresses = [
{
address = ifs.lan10.addr6;
prefixLength = ifs.lan10.p6Size;
}
{
address = ifs.lan10.ulaAddr;
prefixLength = ifs.lan10.ulaSize;
}
];
};
};
networking.dhcpcd.allowInterfaces = [ ifs.wan.name ];
services.radvd.enable = true;
services.radvd.config = ''
interface ${ifs.lan10.name} {
RDNSS ${ifs.lan.ulaAddr} {
};
AdvSendAdvert on;
# MinRtrAdvInterval 3;
# MaxRtrAdvInterval 10;
AdvManagedFlag on;
# AdvOtherConfigFlag on;
prefix ${ifs.lan10.net6} {
AdvOnLink on;
AdvAutonomous on;
};
prefix ${ifs.lan10.ulaNet} {
AdvOnLink on;
AdvAutonomous on;
};
route ${ulaPrefix}::/48 {
};
};
'';
networking.firewall.enable = false; networking.firewall.enable = false;
networking.nftables.enable = true; networking.nftables.enable = true;
networking.nftables.tables.firewall = { networking.nftables.tables.firewall = {
family = "inet"; family = "inet";
content = '' content = ''
define ZONE_WAN_IFS = { ${ifs.wan.name} } define WAN_IF = "${if_wan}"
define ZONE_LAN_IFS = { define LAN_IF = "${if_lan}"
${ifs.lan.name}, define LAN_IPV4_SUBNET = ${lan_p4}.0/24
${ifs.lan10.name}, define LAN_IPV6_SUBNET = ${lan_p6}::/64
${ifs.lan20.name}, define LAN_IPV6_ULA = ${lan_ula_p}::/64
${ifs.lan30.name}, define LAN_IPV4_HOST = ${lan_p4}.100
${ifs.lan40.name}, define LAN_IPV6_HOST = ${lan_p6}::1:1000
${ifs.lan50.name},
}
define OPNSENSE_P6 = ${pdFromWan}d::/64
define RFC1918 = { 10.0.0.0/8, 172.12.0.0/12, 192.168.0.0/16 }
define ALLOWED_TCP_PORTS = { ssh, https } define ALLOWED_TCP_PORTS = { ssh, https, 19999 }
define ALLOWED_UDP_PORTS = { bootps, dhcpv6-server, domain } define ALLOWED_UDP_PORTS = { domain }
map port_forward_v4 {
type inet_proto . inet_service : ipv4_addr . inet_service
elements = {
tcp . 8006 : ${ifs.lan50.p4}.10 . 8006
}
}
set port_forward_v6 {
type inet_proto . ipv6_addr . inet_service
elements = {
tcp . ${ifs.lan.p6}::11:1 . https,
tcp . ${ifs.lan.p6}:1cd5:56ff:feec:c74a . https,
}
}
chain input { chain input {
type filter hook input priority filter; policy drop; type filter hook input priority filter; policy drop;
# Drop router adverts from self
# peculiarity due to wan and lan20 being bridged
# TODO: figure out a less jank way to do this
iifname $ZONE_WAN_IFS ip6 saddr ${links.lanLL} icmpv6 type nd-router-advert log drop
iifname $ZONE_WAN_IFS ip6 saddr fe80::be24:11ff:fe83:d8de icmpv6 type nd-router-advert log drop
# iifname $ZONE_WAN_IFS ip6 saddr ${links.lanLL} log drop
# iifname $ZONE_LAN_IFS ip6 saddr ${links.wanLL} log drop
ip6 nexthdr icmpv6 log prefix "ICMPv6: " accept
# Allow established and related connections # Allow established and related connections
# All icmp stuff should (theoretically) be handled by ct related
# https://serverfault.com/a/632363
ct state established,related accept ct state established,related accept
# However, that doesn't happen for router advertisements from what I can tell # Allow all traffic from loopback interface
# TODO: more testing iifname lo accept
# Allow ICMPv6 on local addrs
ip6 nexthdr icmpv6 ip6 saddr { fe80::/10, ${pdFromWan}0::/60 } accept # Allow ICMPv6 on link local addrs
ip6 nexthdr icmpv6 ip6 saddr fe80::/10 accept
ip6 nexthdr icmpv6 ip6 daddr fe80::/10 accept # TODO: not sure if necessary ip6 nexthdr icmpv6 ip6 daddr fe80::/10 accept # TODO: not sure if necessary
# Allow all traffic from loopback interface # Allow all ICMPv6 from LAN
iif lo accept iifname $LAN_IF ip6 saddr { $LAN_IPV6_SUBNET, $LAN_IPV6_ULA } ip6 nexthdr icmpv6 accept
# Allow DHCPv6 client traffic # Allow DHCPv6 client traffic
ip6 daddr { fe80::/10, ff02::/16 } th dport dhcpv6-server accept ip6 daddr { fe80::/10, ff02::/16 } udp dport dhcpv6-server accept
# WAN zone input rules # Allow all ICMP from LAN
iifname $ZONE_WAN_IFS jump zone_wan_input iifname $LAN_IF ip saddr $LAN_IPV4_SUBNET ip protocol icmp accept
# LAN zone input rules
iifname $ZONE_LAN_IFS accept
iifname $ZONE_LAN_IFS jump zone_lan_input
ip6 saddr $OPNSENSE_P6 jump zone_lan_input
# log # Allow specific services from LAN
iifname $LAN_IF ip saddr $LAN_IPV4_SUBNET tcp dport $ALLOWED_TCP_PORTS accept
iifname $LAN_IF ip6 saddr { $LAN_IPV6_SUBNET, $LAN_IPV6_ULA } tcp dport $ALLOWED_TCP_PORTS accept
iifname $LAN_IF ip saddr $LAN_IPV4_SUBNET udp dport $ALLOWED_UDP_PORTS accept
iifname $LAN_IF ip6 saddr { $LAN_IPV6_SUBNET, $LAN_IPV6_ULA } udp dport $ALLOWED_UDP_PORTS accept
# Allow SSH from WAN (if needed)
iifname $WAN_IF tcp dport ssh accept
} }
chain forward { chain forward {
@@ -557,55 +265,20 @@ in
# Allow established and related connections # Allow established and related connections
ct state established,related accept ct state established,related accept
# WAN zone forward rules
iifname $ZONE_WAN_IFS jump zone_wan_forward
# LAN zone forward rules
iifname $ZONE_LAN_IFS jump zone_lan_forward
ip6 saddr $OPNSENSE_P6 jump zone_lan_forward
}
chain zone_wan_input {
# Allow SSH from WAN (if needed)
tcp dport ssh accept
}
chain zone_wan_forward {
# Port forwarding # Port forwarding
ct status dnat accept iifname $WAN_IF tcp dport https ip daddr $LAN_IPV4_HOST accept
# Allowed IPv6 ports # Allowed IPv6 ports
meta l4proto . ip6 daddr . th dport @port_forward_v6 accept iifname $WAN_IF tcp dport https ip6 daddr $LAN_IPV6_HOST accept
}
chain zone_lan_input { # Allow traffic from LAN to WAN
# Allow all ICMPv6 from LAN iifname $LAN_IF ip saddr $LAN_IPV4_SUBNET oifname $WAN_IF accept
ip6 nexthdr icmpv6 accept iifname $LAN_IF ip6 saddr $LAN_IPV6_SUBNET oifname $WAN_IF accept
# Allow all ICMP from LAN
ip protocol icmp accept
# Allow specific services from LAN
tcp dport $ALLOWED_TCP_PORTS accept
udp dport $ALLOWED_UDP_PORTS accept
}
chain zone_lan_forward {
# Allow port forwarded targets
# ct status dnat accept
# Allow all traffic from LAN to WAN, except ULAs
oifname $ZONE_WAN_IFS ip6 saddr fd00::/8 drop # Not sure if needed
oifname $ZONE_WAN_IFS accept;
# Allow traffic between LANs
oifname $ZONE_LAN_IFS accept
} }
chain output { chain output {
# Accept anything out of self by default # Accept anything out of self by default
type filter hook output priority filter; policy accept; type filter hook output priority filter; policy accept;
# NAT reflection
# oif lo ip daddr != 127.0.0.0/8 dnat ip to meta l4proto . th dport map @port_forward_v4
} }
chain prerouting { chain prerouting {
@@ -613,7 +286,7 @@ in
type nat hook prerouting priority dstnat; policy accept; type nat hook prerouting priority dstnat; policy accept;
# Port forwarding # Port forwarding
fib daddr type local dnat ip to meta l4proto . th dport map @port_forward_v4 iifname $WAN_IF tcp dport https dnat ip to $LAN_IPV4_HOST
} }
chain postrouting { chain postrouting {
@@ -621,10 +294,12 @@ in
type nat hook postrouting priority srcnat; policy accept; type nat hook postrouting priority srcnat; policy accept;
# Masquerade LAN addrs # Masquerade LAN addrs
oifname $ZONE_WAN_IFS ip saddr $RFC1918 masquerade # theoretically shouldn't need to check the input interface here,
# as it would be filtered by the forwarding rules
oifname $WAN_IF ip saddr $LAN_IPV4_SUBNET masquerade
# Optional IPv6 masquerading (big L if enabled, don't forget to allow forwarding) # Optional IPv6 masquerading (big L if enabled)
# oifname $ZONE_WAN_IFS ip6 saddr fd00::/8 masquerade # oifname $WAN_IF ip6 saddr $LAN_IPV6_ULA masquerade
} }
''; '';
}; };
@@ -632,55 +307,79 @@ in
services.kea.dhcp4.enable = true; services.kea.dhcp4.enable = true;
services.kea.dhcp4.settings = { services.kea.dhcp4.settings = {
interfaces-config.interfaces = [ interfaces-config.interfaces = [
# ifs.lan.name if_lan
ifs.lan10.name
# ifs.lan20.name
# ifs.lan30.name
# ifs.lan40.name
# ifs.lan50.name
]; ];
dhcp-ddns.enable-updates = true; dhcp-ddns.enable-updates = true;
ddns-qualifying-suffix = "4.default.${ldomain}"; ddns-qualifying-suffix = "default.${ldomain}";
subnet4 = [ subnet4 = [
# ((mkDhcp4Subnet 1 ifs.lan) // reservations.lan.v4) {
(mkDhcp4Subnet 10 ifs.lan10) id = 1;
# ((mkDhcp4Subnet 20 ifs.lan20) // reservations.lan20.v4) subnet = "${lan_p4}.0/24";
# (mkDhcp4Subnet 30 ifs.lan30) ddns-qualifying-suffix = "lan.${ldomain}";
# (mkDhcp4Subnet 40 ifs.lan40) pools = [ { pool = "${lan_p4}.100 - ${lan_p4}.199"; } ];
# (mkDhcp4Subnet 50 ifs.lan50) option-data = [
{
name = "routers";
data = "${lan_p4}.1";
}
{
name = "domain-name-servers";
data = "${lan_p4}.1";
}
];
reservations = [
{
hw-address = "bc:24:11:b7:27:4d";
hostname = "archy";
ip-address = "${lan_p4}.69";
}
];
}
]; ];
}; };
services.kea.dhcp6.enable = true; services.kea.dhcp6.enable = true;
services.kea.dhcp6.settings = { services.kea.dhcp6.settings = {
interfaces-config.interfaces = [ interfaces-config.interfaces = [
# ifs.lan.name if_lan
ifs.lan10.name
# ifs.lan20.name # Managed by Att box
# ifs.lan30.name
# ifs.lan40.name
# ifs.lan50.name
]; ];
# TODO: https://kea.readthedocs.io/en/latest/arm/ddns.html#dual-stack-environments # TODO: https://kea.readthedocs.io/en/latest/arm/ddns.html#dual-stack-environments
dhcp-ddns.enable-updates = true; dhcp-ddns.enable-updates = true;
ddns-qualifying-suffix = "6.default.${ldomain}"; ddns-qualifying-suffix = "default6.${ldomain}";
subnet6 = [ subnet6 = [
# ((mkDhcp6Subnet 1 ifs.lan) // reservations.lan.v6) {
(mkDhcp6Subnet 10 ifs.lan10) id = 1;
# (mkDhcp6Subnet 30 ifs.lan30) interface = if_lan;
# (mkDhcp6Subnet 40 ifs.lan40) subnet = "${lan_p6}::/64";
# (mkDhcp6Subnet 50 ifs.lan50) ddns-qualifying-suffix = "lan6.${ldomain}";
rapid-commit = true;
pools = [ { pool = "${lan_p6}::1:1000/116"; } ];
reservations = [
{
duid = "00:04:59:c3:ce:9a:08:cf:fb:b7:fe:74:9c:e3:b7:44:bf:01";
hostname = "archy";
ip-addresses = [ "${lan_p6}::69" ];
}
];
}
]; ];
}; };
services.kea.dhcp-ddns.enable = true; services.kea.dhcp-ddns.enable = true;
services.kea.dhcp-ddns.settings = { services.kea.dhcp-ddns.settings = {
forward-ddns.ddns-domains = [ forward-ddns = {
{ ddns-domains = [
name = "${ldomain}."; {
dns-servers = [ { ip-address = "::1"; port = 1053; } ]; name = "${ldomain}.";
} dns-servers = [
]; {
ip-address = "::1";
port = 1053;
}
];
}
];
};
}; };
services.resolved.enable = false; services.resolved.enable = false;
@@ -695,33 +394,24 @@ in
upstream_dns = [ upstream_dns = [
"quic://p0.freedns.controld.com" # Default upstream "quic://p0.freedns.controld.com" # Default upstream
"[/${ldomain}/][::1]:1053" # Local domains to Knot (ddns) "[/${ldomain}/][::1]:1053" # Local domains to Knot (ddns)
"[/home/][${ifs.lan.ulaPrefix}::250]" # .home domains to opnsense (temporary)
]; ];
}; };
# https://adguard-dns.io/kb/general/dns-filtering-syntax/ # https://adguard-dns.io/kb/general/dns-filtering-syntax/
user_rules = [ user_rules = [
# DNS rewrites # DNS rewrites
"|grouter.${domain}^$dnsrewrite=${ifs.lan.ulaAddr}" "|grouter.${domain}^$dnsrewrite=${lan_ula_addr}"
"|pve-1.${sysdomain}^$dnsrewrite=${ifs.lan.p4}.5"
"|pve-3.${sysdomain}^$dnsrewrite=${ifs.lan.p4}.7"
"|pve-1.${sysdomain}^$dnsrewrite=${ifs.lan.ulaPrefix}::5:1"
"|pve-3.${sysdomain}^$dnsrewrite=${ifs.lan.ulaPrefix}::7:1"
"||lab.${domain}^$dnsrewrite=${ifs.lan.p6}::12:1"
"||lab.${domain}^$dnsrewrite=${ifs.lan.p4}.12"
# Allowed exceptions # Allowed exceptions
"@@||googleads.g.doubleclick.net" "@@||googleads.g.doubleclick.net"
] ];
# Alpina DNS rewrites
++ map (host: "${host}${domain}^$dnsrewrite=${ifs.lan.p6}:1cd5:56ff:feec:c74a") alpinaDomains
++ map (host: "${host}${domain}^$dnsrewrite=${ifs.lan.p4}.11") alpinaDomains;
}; };
services.knot.enable = true; services.knot.enable = true;
services.knot.settings = { services.knot.settings = {
# server.listen = "0.0.0.0@1053"; server = {
server.listen = "::1@1053"; # listen = "0.0.0.0@1053";
listen = "::1@1053";
};
# TODO: templates # TODO: templates
zone = [ zone = [
{ {
@@ -814,8 +504,7 @@ in
# Enable the KDE Plasma Desktop Environment. # Enable the KDE Plasma Desktop Environment.
# Useful for debugging with wireshark. # Useful for debugging with wireshark.
# services.displayManager.sddm.enable = true; services.displayManager.sddm.enable = false;
hardware.graphics.enable = true;
services.displayManager.sddm.wayland.enable = true; services.displayManager.sddm.wayland.enable = true;
services.desktopManager.plasma6.enable = true; services.desktopManager.plasma6.enable = true;
# No need for audio in VM # No need for audio in VM

View File

@@ -9,21 +9,11 @@ in {
options = { options = {
mods.kb-input = { mods.kb-input = {
enable = lib.mkEnableOption "input method and custom keyboard layout"; enable = lib.mkEnableOption "input method and custom keyboard layout";
enableMinimak = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable Minimak keyboard layout";
};
enableFcitx = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable Fcitx5 input method";
};
}; };
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
services.xserver.xkb.extraLayouts = lib.mkIf cfg.enableMinimak { services.xserver.xkb.extraLayouts = {
minimak-4 = { minimak-4 = {
description = "English (US, Minimak-4)"; description = "English (US, Minimak-4)";
languages = [ "eng" ]; languages = [ "eng" ];
@@ -41,9 +31,9 @@ in {
}; };
}; };
i18n.inputMethod = lib.mkIf cfg.enableFcitx { i18n.inputMethod = {
enable = true;
type = "fcitx5"; type = "fcitx5";
enable = true;
fcitx5.waylandFrontend = true; fcitx5.waylandFrontend = true;
fcitx5.plasma6Support = true; fcitx5.plasma6Support = true;
fcitx5.addons = [ pkgs.fcitx5-mozc ]; fcitx5.addons = [ pkgs.fcitx5-mozc ];