diff options
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/auth.nix | 3 | ||||
| -rw-r--r-- | sys/boot.nix | 105 | ||||
| -rw-r--r-- | sys/default.nix | 14 | ||||
| -rw-r--r-- | sys/fs/btrfs.nix | 157 | ||||
| -rw-r--r-- | sys/fs/default.nix | 3 | ||||
| -rw-r--r-- | sys/fs/layout.nix | 57 | ||||
| -rw-r--r-- | sys/net.nix | 3 | ||||
| -rw-r--r-- | sys/nspawn.nix | 130 | ||||
| -rw-r--r-- | sys/options.nix | 5 | ||||
| -rw-r--r-- | sys/users.nix | 33 |
10 files changed, 326 insertions, 184 deletions
diff --git a/sys/auth.nix b/sys/auth.nix index e6e156d..e85543e 100644 --- a/sys/auth.nix +++ b/sys/auth.nix @@ -1,7 +1,8 @@ { lib, config, ... }: with lib; let cfg = config.local; -in { +in +{ config = { security.pam = { oath = { diff --git a/sys/boot.nix b/sys/boot.nix index 9e1ef85..5d37b25 100644 --- a/sys/boot.nix +++ b/sys/boot.nix @@ -1,7 +1,8 @@ { lib, config, ... }: with lib; let cfg = config.local; -in { +in +{ options.local = with lib.types; { loader = mkOption { type = enum [ "grub" "systemd-boot" ]; @@ -40,64 +41,70 @@ in { }; }; - initrd = let - crypt = cfg.crypt.toplevel; - headerPathEscaped = escapeShellArg "/initrd-boot/${crypt.headerFromBoot}"; - in { - availableKernelModules = cfg.initrdModules; - supportedFilesystems = [ "vfat" ]; + initrd = + let + crypt = cfg.crypt.toplevel; + headerPathEscaped = escapeShellArg "/initrd-boot/${crypt.headerFromBoot}"; + in + { + availableKernelModules = cfg.initrdModules; + supportedFilesystems = [ "vfat" ]; - preDeviceCommands = optionalString (crypt != null) '' - mkdir -p `dirname ${headerPathEscaped}` - touch ${headerPathEscaped} - ''; - - preLVMCommands = optionalString cfg.portable '' - sleep 2 #TODO - ''; + preDeviceCommands = optionalString (crypt != null) '' + mkdir -p `dirname ${headerPathEscaped}` + touch ${headerPathEscaped} + ''; - postMountCommands = let - fromRoot = path: escapeShellArg "/mnt-root/${path}"; - auxOpen = aux: '' - cryptsetup -v open \ - --header ${fromRoot aux.header} \ - --key-file ${fromRoot aux.keyfile} \ - ${aux.device} ${aux.target} + preLVMCommands = optionalString cfg.portable '' + sleep 2 #TODO ''; - in concatStringsSep "\n" (map auxOpen cfg.crypt.aux); - luks.devices = mkIf (crypt != null) { - "${crypt.target}" = { - inherit (crypt) device; - header = "/initrd-boot/${crypt.headerFromBoot}"; - preLVM = false; + postMountCommands = + let + fromRoot = path: escapeShellArg "/mnt-root/${path}"; + auxOpen = aux: '' + cryptsetup -v open \ + --header ${fromRoot aux.header} \ + --key-file ${fromRoot aux.keyfile} \ + ${aux.device} ${aux.target} + ''; + in + concatStringsSep "\n" (map auxOpen cfg.crypt.aux); + + luks.devices = mkIf (crypt != null) { + "${crypt.target}" = { + inherit (crypt) device; + header = "/initrd-boot/${crypt.headerFromBoot}"; + preLVM = false; - preOpenCommands = '' - mount -o ro -t vfat ${escapeShellArg cfg.fs.boot.device} /initrd-boot - ''; + preOpenCommands = '' + mount -o ro -t vfat ${escapeShellArg cfg.fs.boot.device} /initrd-boot + ''; - postOpenCommands = '' - umount /initrd-boot - ''; + postOpenCommands = '' + umount /initrd-boot + ''; + }; }; - }; - #network = { - # enable = true; + #network = { + # enable = true; - # ssh = { - # enable = true; - # port = 2234; - # }; - #}; - }; + # ssh = { + # enable = true; + # port = 2234; + # }; + #}; + }; }; - hardware.cpu = let - ucode.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; - in { - amd = mkIf (cfg.cpuVendor == "amd") ucode; - intel = mkIf (cfg.cpuVendor == "intel") ucode; - }; + hardware.cpu = + let + ucode.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + in + { + amd = mkIf (cfg.cpuVendor == "amd") ucode; + intel = mkIf (cfg.cpuVendor == "intel") ucode; + }; }; } diff --git a/sys/default.nix b/sys/default.nix index ef60f43..f19bc0a 100644 --- a/sys/default.nix +++ b/sys/default.nix @@ -1,21 +1,17 @@ -{ self, impermanence }: -{ lib, config, pkgs, modulesPath, ... }: +{ lib, config, pkgs, ... }: with lib; { imports = [ - "${modulesPath}/installer/scan/not-detected.nix" - impermanence.nixosModule - + ../env ./auth.nix ./boot.nix ./fs ./net.nix + ./nspawn.nix ./options.nix ./users.nix ]; config = { - nixpkgs.overlays = [ self.overlay ]; - # This value determines the NixOS release from which the default # settings for stateful data, like file locations and database versions # on your system were taken. It‘s perfectly fine and recommended to leave @@ -27,11 +23,11 @@ with lib; { nix = { package = pkgs.nixFlakes; extraOptions = '' - experimental-features = nix-command flakes + experimental-features = nix-command flakes repl-flake ''; }; - # shenvs necesita systemd 251 + # hm-isolation necesita systemd 251 systemd = mkIf (config.system.nixos.release == "22.05") { package = pkgs.unstable.systemd; }; diff --git a/sys/fs/btrfs.nix b/sys/fs/btrfs.nix index 87c9fca..809d35e 100644 --- a/sys/fs/btrfs.nix +++ b/sys/fs/btrfs.nix @@ -1,15 +1,16 @@ { lib, config, pkgs, ... }: with lib; let cfg = config.local; -in { +in +{ options.local = with lib.types; { snapperSubvols = mkOption { type = attrsOf str; - default = {}; + default = { }; }; fs.btrfs = mkOption { - default = []; + default = [ ]; type = attrsOf (submodule { options = { @@ -35,77 +36,83 @@ in { }; config = { - environment.systemPackages = optional (cfg.snapperSubvols != {}) pkgs.local.btclone; - - fileSystems = let - inherit (cfg) fs; - btrfs = { device, subvol, ssd, ... }: { - inherit device; - fsType = "btrfs"; - options = [ "noatime" "compress=zstd" "subvol=${subvol}" ] ++ optional ssd "ssd"; - }; - in mapAttrs (_: btrfs) cfg.fs.btrfs; - - local.snapperSubvols = let - snapperEntry = path: opts: { name = opts.snapper; value = path; }; - validEntry = _: opts: opts.snapper != null; - in mapAttrs' snapperEntry (filterAttrs validEntry cfg.fs.btrfs); - - services.snapper.configs = let - snapperConfig = _: subvolume: { - inherit subvolume; - - extraConfig = '' - # btrfs qgroup for space aware cleanup algorithms - QGROUP="" - - # fraction of the filesystems space the snapshots may use - SPACE_LIMIT="0.5" - - # fraction of the filesystems space that should be free - FREE_LIMIT="0.2" - - # users and groups allowed to work with config - ALLOW_USERS="" - ALLOW_GROUPS="" - - # sync users and groups from ALLOW_USERS and ALLOW_GROUPS to .snapshots - # directory - SYNC_ACL="no" - - # start comparing pre- and post-snapshot in background after creating - # post-snapshot - BACKGROUND_COMPARISON="yes" - - # run daily number cleanup - NUMBER_CLEANUP="yes" - - # limit for number cleanup - NUMBER_MIN_AGE="1800" - NUMBER_LIMIT="100" - NUMBER_LIMIT_IMPORTANT="10" - - # create hourly snapshots - TIMELINE_CREATE="yes" - - # cleanup hourly snapshots after some time - TIMELINE_CLEANUP="yes" - - # limits for timeline cleanup - TIMELINE_MIN_AGE="1800" - TIMELINE_LIMIT_HOURLY="24" - TIMELINE_LIMIT_DAILY="7" - TIMELINE_LIMIT_WEEKLY="4" - TIMELINE_LIMIT_MONTHLY="12" - TIMELINE_LIMIT_YEARLY="10" - - # cleanup empty pre-post-pairs - EMPTY_PRE_POST_CLEANUP="yes" - - # limits for empty pre-post-pair cleanup - EMPTY_PRE_POST_MIN_AGE="1800" - ''; - }; - in mapAttrs snapperConfig cfg.snapperSubvols; + environment.systemPackages = optional (cfg.snapperSubvols != { }) pkgs.local.btclone; + + fileSystems = + let + inherit (cfg) fs; + btrfs = { device, subvol, ssd, ... }: { + inherit device; + fsType = "btrfs"; + options = [ "noatime" "compress=zstd" "subvol=${subvol}" ] ++ optional ssd "ssd"; + }; + in + mapAttrs (_: btrfs) cfg.fs.btrfs; + + local.snapperSubvols = + let + snapperEntry = path: opts: { name = opts.snapper; value = path; }; + validEntry = _: opts: opts.snapper != null; + in + mapAttrs' snapperEntry (filterAttrs validEntry cfg.fs.btrfs); + + services.snapper.configs = + let + snapperConfig = _: subvolume: { + inherit subvolume; + + extraConfig = '' + # btrfs qgroup for space aware cleanup algorithms + QGROUP="" + + # fraction of the filesystems space the snapshots may use + SPACE_LIMIT="0.5" + + # fraction of the filesystems space that should be free + FREE_LIMIT="0.2" + + # users and groups allowed to work with config + ALLOW_USERS="" + ALLOW_GROUPS="" + + # sync users and groups from ALLOW_USERS and ALLOW_GROUPS to .snapshots + # directory + SYNC_ACL="no" + + # start comparing pre- and post-snapshot in background after creating + # post-snapshot + BACKGROUND_COMPARISON="yes" + + # run daily number cleanup + NUMBER_CLEANUP="yes" + + # limit for number cleanup + NUMBER_MIN_AGE="1800" + NUMBER_LIMIT="100" + NUMBER_LIMIT_IMPORTANT="10" + + # create hourly snapshots + TIMELINE_CREATE="yes" + + # cleanup hourly snapshots after some time + TIMELINE_CLEANUP="yes" + + # limits for timeline cleanup + TIMELINE_MIN_AGE="1800" + TIMELINE_LIMIT_HOURLY="24" + TIMELINE_LIMIT_DAILY="7" + TIMELINE_LIMIT_WEEKLY="4" + TIMELINE_LIMIT_MONTHLY="12" + TIMELINE_LIMIT_YEARLY="10" + + # cleanup empty pre-post-pairs + EMPTY_PRE_POST_CLEANUP="yes" + + # limits for empty pre-post-pair cleanup + EMPTY_PRE_POST_MIN_AGE="1800" + ''; + }; + in + mapAttrs snapperConfig cfg.snapperSubvols; }; } diff --git a/sys/fs/default.nix b/sys/fs/default.nix index d24e357..04b8acb 100644 --- a/sys/fs/default.nix +++ b/sys/fs/default.nix @@ -1,7 +1,8 @@ { lib, config, ... }: with lib; let cfg = config.local.fs; -in { +in +{ imports = [ ./btrfs.nix ./layout.nix ]; options.local.fs = with lib.types; { diff --git a/sys/fs/layout.nix b/sys/fs/layout.nix index 897cffe..7e1ac2e 100644 --- a/sys/fs/layout.nix +++ b/sys/fs/layout.nix @@ -1,7 +1,8 @@ { lib, config, ... }: with lib; let cfg = config.local; -in { +in +{ options.local.fs.layout = with lib.types; { sysHddBtrfs = mkOption { default = null; @@ -41,38 +42,40 @@ in { }; config = { - local.fs.btrfs = let - sysHddBtrfs = layout: { - "/" = { - inherit (layout.sys) device ssd; - subvol = layout.sys.root; - }; + local.fs.btrfs = + let + sysHddBtrfs = layout: { + "/" = { + inherit (layout.sys) device ssd; + subvol = layout.sys.root; + }; - "/toplevel" = { - inherit (layout.sys) device ssd; - subvol = layout.sys.toplevel; - }; + "/toplevel" = { + inherit (layout.sys) device ssd; + subvol = layout.sys.toplevel; + }; - "/hdd" = { - inherit (layout.hdd) device; - subvol = "/"; - ssd = false; - }; + "/hdd" = { + inherit (layout.hdd) device; + subvol = "/"; + ssd = false; + }; - "/home" = { - inherit (layout.hdd) device; - subvol = layout.hdd.home; - ssd = false; - snapper = "home"; + "/home" = { + inherit (layout.hdd) device; + subvol = layout.hdd.home; + ssd = false; + snapper = "home"; + }; }; - }; - inherit (cfg.fs) layout; + inherit (cfg.fs) layout; - layoutMaps = [ sysHddBtrfs ]; - layoutOpts = [ layout.sysHddBtrfs ]; - valid = filter ({ snd, ... }: snd != null) (zipLists layoutMaps layoutOpts); - in optionalAttrs (valid != []) ((head valid).fst (head valid).snd); + layoutMaps = [ sysHddBtrfs ]; + layoutOpts = [ layout.sysHddBtrfs ]; + valid = filter ({ snd, ... }: snd != null) (zipLists layoutMaps layoutOpts); + in + optionalAttrs (valid != [ ]) ((head valid).fst (head valid).snd); assertions = [ { diff --git a/sys/net.nix b/sys/net.nix index 30675e0..4075a12 100644 --- a/sys/net.nix +++ b/sys/net.nix @@ -1,7 +1,8 @@ { lib, config, pkgs, ... }: with lib; let cfg = config.local; -in { +in +{ options.local = with lib.types; { hostname = mkOption { type = str; diff --git a/sys/nspawn.nix b/sys/nspawn.nix new file mode 100644 index 0000000..6f1558c --- /dev/null +++ b/sys/nspawn.nix @@ -0,0 +1,130 @@ +{ lib, config, pkgs, ... }: +with lib; let + cfg = config.local; +in +{ + options.local.nspawn.dmz = with types; { + enable = mkEnableOption "DMZ services in a container"; + + net = mkOption { + type = str; + }; + + netBits = mkOption { + type = int; + }; + + hostAddr = mkOption { + type = str; + }; + + system = mkOption { + type = attrs; + }; + }; + + # Situación con os-release + # + # La idea aquí es poder hacer 'btrfs subvol create /var/lib/machines/foo' y + # dejar que systemd-nspawn y el activation script creen todo lo demás. Esto + # no sirve bien debido a la prueba barata que hace systemd para revisar si el + # árbol parece contener una imagen de sistema operativo. Esta prueba falla en + # dos momentos distintos: + # + # 1. Inmediatamente tras crear un árbol vacío, puesto que os-release no existe. + # La solución naive es 'mkdir rootfs/etc && touch rootfs/etc/os-release'. + # + # 2. Luego de reiniciar el contenedor una vez que NixOS ha preparado /etc, ya que + # systemd espera un archivo regular y no telera el symlink a la store. + # + # Resulta ser que systemd revisa tanto /etc/os-release como /usr/lib/os-release. + # NixOS evidentemente no usa la segunda ruta por ser FHS, así que la duct tape + # final es 'mkdir rootfs/usr/lib && touch rootfs/usr/lib/os-release'. + + config = mkIf cfg.nspawn.dmz.enable { + local = { + mailHost = { + mdaListen = cfg.nspawn.dmz.hostAddr; + saslPort = 11000; + lmtpPort = 11001; + }; + + nspawn.dmz = { + system = + let + containerModule = { ... }: { + #TODO: urgente: bloquear puertos de dovecot a non-postfix con iptables + config = { + boot.isContainer = true; + + local.mta = { + mdaAddr = cfg.mailHost.mdaListen; + inherit (cfg.mailHost) saslPort lmtpPort; + }; + }; + }; + in + pkgs.nixos [ ../dmz containerModule ]; + + net = "10.34.3.0"; + netBits = 28; + hostAddr = "10.34.3.1"; + }; + }; + + systemd = { + nspawn.dmz = { + execConfig.PrivateUsers = "pick"; + + filesConfig.BindReadOnly = [ + # idmap porque algunos hacks en nixpkgs (postfix-setup.service) + # asumen que la store es de root + "/nix/store:/nix/store:idmap" + "${cfg.nspawn.dmz.system.toplevel}/init:/sbin/init" + ]; + + networkConfig.Port = [ "tcp:25" "tcp:80" "tcp:443" "tcp:587" ]; + }; + + network.networks."40-ve-dmz" = { + matchConfig = { + Name = "ve-dmz"; + Driver = "veth"; + }; + + networkConfig = { + Address = "${cfg.nspawn.dmz.hostAddr}/${toString cfg.nspawn.dmz.netBits}"; + LinkLocalAddressing = "yes"; + DHCPServer = "yes"; + IPMasquerade = "both"; + LLDP = "yes"; + EmitLLDP = "customer-bridge"; + IPv6SendRA = "yes"; + }; + + # IP de contenedor fijada en hostAddr + 1 + dhcpServerConfig = { + PoolOffset = 2; + PoolSize = 1; + }; + }; + + services = { + dovecot2.after = [ "systemd-nspawn@dmz.service" ]; + + "systemd-nspawn@dmz" = { + overrideStrategy = "asDropin"; + + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + wantedBy = [ "machines.target" ]; + }; + }; + }; + + networking.firewall.interfaces.ve-dmz = { + allowedTCPPorts = [ cfg.mailHost.saslPort cfg.mailHost.lmtpPort ]; + allowedUDPPorts = [ 67 ]; # DHCP + }; + }; +} diff --git a/sys/options.nix b/sys/options.nix index f719522..cfb2827 100644 --- a/sys/options.nix +++ b/sys/options.nix @@ -1,7 +1,8 @@ { lib, ... }: with lib.types; let inherit (lib) mkOption; -in { +in +{ options.local = { portable = mkOption { type = bool; @@ -29,7 +30,7 @@ in { }; aux = mkOption { - default = []; + default = [ ]; type = listOf (submodule { options = { device = mkOption { diff --git a/sys/users.nix b/sys/users.nix index 8c8be6b..b84d1c0 100644 --- a/sys/users.nix +++ b/sys/users.nix @@ -1,7 +1,8 @@ { lib, config, pkgs, ... }: with lib; let cfg = config.local; -in { +in +{ config = { sound.enable = true; hardware.pulseaudio.enable = true; @@ -24,27 +25,21 @@ in { environment.pathsToLink = [ "/share/zsh" ]; - users.users = { - ale = { + users.users = mapAttrs + (username: user: { isNormalUser = true; - uid = 1000; - group = "ale"; - extraGroups = [ "users" "wheel" "adbusers" ]; - shell = pkgs.zsh; - }; - tutorias = { - isNormalUser = true; - uid = 1004; - group = "tutorias"; - extraGroups = [ "users" ]; - shell = pkgs.zsh; - }; - }; + inherit (user) uid; + description = user.gecos; + + group = username; + extraGroups = [ "users" ] ++ user.groups; + + shell = if user.allowLogin then pkgs.zsh else null; + }) + cfg.users; - users.groups = { - ale.gid = 1001; - tutorias.gid = 1007; + users.groups = mapAttrs (_: user: { inherit (user) gid; }) cfg.users // { adbusers.gid = 1008; }; }; |
