diff --git a/boxes/colony.nix b/boxes/colony.nix
index bc4f72b..d67bba0 100644
--- a/boxes/colony.nix
+++ b/boxes/colony.nix
@@ -1,29 +1,29 @@
{ lib, pkgs, inputs, ... }:
- {
- fileSystems = {
- "/persist" = {
- device = "/dev/disk/by-label/persist";
- fsType = "ext4";
- neededForBoot = true;
+{
+ fileSystems = {
+ "/persist" = {
+ device = "/dev/disk/by-label/persist";
+ fsType = "ext4";
+ neededForBoot = true;
+ };
+ };
+
+ networking = { };
+
+ my = {
+ firewall = {
+ trustedInterfaces = [ "blah" ];
+ nat = {
+ externalInterface = "eth0";
+ forwardPorts = [
+ {
+ proto = "tcp";
+ sourcePort = 2222;
+ destination = "127.0.0.1:22";
+ }
+ ];
};
};
-
- networking = {};
-
- my = {
- firewall = {
- trustedInterfaces = [ "blah" ];
- nat = {
- externalInterface = "eth0";
- forwardPorts = [
- {
- proto = "tcp";
- sourcePort = 2222;
- destination = "127.0.0.1:22";
- }
- ];
- };
- };
- server.enable = true;
- };
- }
+ server.enable = true;
+ };
+}
diff --git a/flake.nix b/flake.nix
index 4f61f41..9cc0948 100644
--- a/flake.nix
+++ b/flake.nix
@@ -55,27 +55,32 @@
lib = pkgsFlakes.unstable.lib;
pkgs' = mapAttrs (_: path: lib.my.mkPkgs path { overlays = [ libOverlay ]; }) pkgsFlakes;
- in {
+ in
+ {
inherit lib;
- nixosModules = mapAttrs (_: path: let path' = ./. + "/modules/${path}"; in {
- _file = path';
- imports = [ (import path') ];
- }) {
- common = "common.nix";
- build = "build.nix";
- dynamic-motd = "dynamic-motd.nix";
- tmproot = "tmproot.nix";
- firewall = "firewall.nix";
- server = "server.nix";
- };
+ nixosModules = mapAttrs
+ (_: path:
+ let path' = ./. + "/modules/${path}"; in
+ {
+ _file = path';
+ imports = [ (import path') ];
+ })
+ {
+ common = "common.nix";
+ build = "build.nix";
+ dynamic-motd = "dynamic-motd.nix";
+ tmproot = "tmproot.nix";
+ firewall = "firewall.nix";
+ server = "server.nix";
+ };
nixosConfigurations = import ./systems.nix { inherit lib pkgsFlakes inputs; modules = self.nixosModules; };
systems = mapAttrs (_: system: system.config.system.build.toplevel) self.nixosConfigurations;
vms = mapAttrs (_: system: system.config.my.build.devVM) self.nixosConfigurations;
apps =
- let apps' = {}
+ let apps' = { }
// addPrefix "vms/" (mapAttrs (name: vm: { type = "app"; program = "${vm}/bin/run-${name}-vm"; }) self.vms);
in { x86_64-linux = apps'; };
@@ -83,12 +88,13 @@
let
pkgs = pkgs'.unstable.${system};
flakePkg = f: f.defaultPackage.${system};
- in pkgs.mkShell {
- packages = map flakePkg [
- agenix
- deploy-rs
- ];
- }
- );
+ in
+ pkgs.mkShell {
+ packages = map flakePkg [
+ agenix
+ deploy-rs
+ ];
+ }
+ );
};
}
diff --git a/modules/build.nix b/modules/build.nix
index f291ed4..4c572e1 100644
--- a/modules/build.nix
+++ b/modules/build.nix
@@ -1,32 +1,33 @@
{ lib, extendModules, modulesPath, baseModules, options, config, ... }:
- let
- inherit (lib) mkOption;
- inherit (lib.my) mkBoolOpt;
+let
+ inherit (lib) mkOption;
+ inherit (lib.my) mkBoolOpt;
- cfg = config.my.build;
+ cfg = config.my.build;
- asDevVM = extendModules {
- # TODO: Hack because this is kinda broken on 21.11 (https://github.com/NixOS/nixpkgs/issues/148343)
- specialArgs = { inherit baseModules; };
- modules = [
- "${modulesPath}/virtualisation/qemu-vm.nix"
- ({ ... }: {
- my.boot.isDevVM = true;
- })
- ];
- };
- in {
- options.my = with lib.types; {
- boot.isDevVM = mkBoolOpt false;
- build = options.system.build;
- asDevVM = mkOption {
- inherit (asDevVM) type;
- default = {};
- visible = "shallow";
- };
+ asDevVM = extendModules {
+ # TODO: Hack because this is kinda broken on 21.11 (https://github.com/NixOS/nixpkgs/issues/148343)
+ specialArgs = { inherit baseModules; };
+ modules = [
+ "${modulesPath}/virtualisation/qemu-vm.nix"
+ ({ ... }: {
+ my.boot.isDevVM = true;
+ })
+ ];
+ };
+in
+{
+ options.my = with lib.types; {
+ boot.isDevVM = mkBoolOpt false;
+ build = options.system.build;
+ asDevVM = mkOption {
+ inherit (asDevVM) type;
+ default = { };
+ visible = "shallow";
};
+ };
- config.my.build = {
- devVM = config.my.asDevVM.system.build.vm;
- };
- }
+ config.my.build = {
+ devVM = config.my.asDevVM.system.build.vm;
+ };
+}
diff --git a/modules/common.nix b/modules/common.nix
index 22c81b7..d4ba558 100644
--- a/modules/common.nix
+++ b/modules/common.nix
@@ -1,93 +1,95 @@
{ lib, pkgs, inputs, system, config, options, ... }:
- let
- inherit (lib) mkIf mkDefault mkAliasDefinitions;
- inherit (lib.my) mkOpt;
- in {
- options.my = with lib.types; {
- user = mkOpt (attrsOf anything) {};
- };
+let
+ inherit (lib) mkIf mkDefault mkAliasDefinitions;
+ inherit (lib.my) mkOpt;
+in
+{
+ options.my = with lib.types; {
+ user = mkOpt (attrsOf anything) { };
+ };
- config =
- let
- defaultUsername = "dev";
- uname = config.my.user.name;
- in {
- my = {
- user = {
- name = mkDefault defaultUsername;
- isNormalUser = true;
- uid = mkDefault 1000;
- extraGroups = mkDefault [ "wheel" ];
- password = mkDefault "hunter2"; # TODO: secrets...
- };
- };
-
- time.timeZone = mkDefault "Europe/Dublin";
-
- users = {
- mutableUsers = false;
- users.${uname} = mkAliasDefinitions options.my.user;
- };
-
- security = {
- sudo.enable = mkDefault false;
- doas = {
- enable = mkDefault true;
- wheelNeedsPassword = mkDefault false;
- };
- };
-
- nix = {
- package = inputs.nix.defaultPackage.${system};
- extraOptions =
- ''
- experimental-features = nix-command flakes ca-derivations
- '';
- };
- nixpkgs = {
- config = {
- allowUnfree = true;
- };
- };
-
- boot = {
- # Use latest LTS release by default
- kernelPackages = mkDefault pkgs.linuxKernel.packages.linux_5_15;
- loader = {
- efi = {
- efiSysMountPoint = mkDefault "/boot";
- canTouchEfiVariables = mkDefault false;
- };
- systemd-boot = {
- enable = mkDefault true;
- editor = mkDefault true;
- consoleMode = mkDefault "max";
- configurationLimit = mkDefault 10;
- memtest86.enable = mkDefault true;
- };
- };
- };
-
- networking = {
- useDHCP = mkDefault false;
- enableIPv6 = mkDefault true;
- };
-
- environment.systemPackages = with pkgs; [
- bash-completion
- tree
- vim
- htop
- iperf3
- ];
-
- services.openssh = {
- enable = true;
- };
-
- system = {
- stateVersion = "21.11";
- configurationRevision = with inputs; mkIf (self ? rev) self.rev;
+ config =
+ let
+ defaultUsername = "dev";
+ uname = config.my.user.name;
+ in
+ {
+ my = {
+ user = {
+ name = mkDefault defaultUsername;
+ isNormalUser = true;
+ uid = mkDefault 1000;
+ extraGroups = mkDefault [ "wheel" ];
+ password = mkDefault "hunter2"; # TODO: secrets...
};
};
- }
+
+ time.timeZone = mkDefault "Europe/Dublin";
+
+ users = {
+ mutableUsers = false;
+ users.${uname} = mkAliasDefinitions options.my.user;
+ };
+
+ security = {
+ sudo.enable = mkDefault false;
+ doas = {
+ enable = mkDefault true;
+ wheelNeedsPassword = mkDefault false;
+ };
+ };
+
+ nix = {
+ package = inputs.nix.defaultPackage.${system};
+ extraOptions =
+ ''
+ experimental-features = nix-command flakes ca-derivations
+ '';
+ };
+ nixpkgs = {
+ config = {
+ allowUnfree = true;
+ };
+ };
+
+ boot = {
+ # Use latest LTS release by default
+ kernelPackages = mkDefault pkgs.linuxKernel.packages.linux_5_15;
+ loader = {
+ efi = {
+ efiSysMountPoint = mkDefault "/boot";
+ canTouchEfiVariables = mkDefault false;
+ };
+ systemd-boot = {
+ enable = mkDefault true;
+ editor = mkDefault true;
+ consoleMode = mkDefault "max";
+ configurationLimit = mkDefault 10;
+ memtest86.enable = mkDefault true;
+ };
+ };
+ };
+
+ networking = {
+ useDHCP = mkDefault false;
+ enableIPv6 = mkDefault true;
+ };
+
+ environment.systemPackages = with pkgs; [
+ bash-completion
+ tree
+ vim
+ htop
+ iperf3
+ ];
+
+ services.openssh = {
+ enable = true;
+ };
+
+ system = {
+ stateVersion = "21.11";
+ configurationRevision = with inputs; mkIf (self ? rev) self.rev;
+ };
+ };
+}
diff --git a/modules/dynamic-motd.nix b/modules/dynamic-motd.nix
index a05d1d6..21da418 100644
--- a/modules/dynamic-motd.nix
+++ b/modules/dynamic-motd.nix
@@ -1,24 +1,25 @@
{ lib, pkgs, config, ... }:
- let
- inherit (lib) optionalAttrs filterAttrs genAttrs mkIf mkDefault;
- inherit (lib.my) mkOpt mkBoolOpt;
+let
+ inherit (lib) optionalAttrs filterAttrs genAttrs mkIf mkDefault;
+ inherit (lib.my) mkOpt mkBoolOpt;
- cfg = config.my.dynamic-motd;
+ cfg = config.my.dynamic-motd;
- scriptBin = pkgs.writeShellScript "dynamic-motd-script" cfg.script;
- in {
- options.my.dynamic-motd = with lib.types; {
- enable = mkBoolOpt true;
- services = mkOpt (listOf str) [ "login" "ssh" ];
- script = mkOpt (nullOr lines) null;
- };
+ scriptBin = pkgs.writeShellScript "dynamic-motd-script" cfg.script;
+in
+{
+ options.my.dynamic-motd = with lib.types; {
+ enable = mkBoolOpt true;
+ services = mkOpt (listOf str) [ "login" "ssh" ];
+ script = mkOpt (nullOr lines) null;
+ };
- config = mkIf (cfg.enable && cfg.script != null) {
- security.pam.services = genAttrs cfg.services (s: {
- text = mkDefault
- ''
- session optional ${pkgs.pam}/lib/security/pam_exec.so stdout quiet ${scriptBin}
- '';
- });
- };
- }
+ config = mkIf (cfg.enable && cfg.script != null) {
+ security.pam.services = genAttrs cfg.services (s: {
+ text = mkDefault
+ ''
+ session optional ${pkgs.pam}/lib/security/pam_exec.so stdout quiet ${scriptBin}
+ '';
+ });
+ };
+}
diff --git a/modules/firewall.nix b/modules/firewall.nix
index 4310ea3..8f1dc99 100644
--- a/modules/firewall.nix
+++ b/modules/firewall.nix
@@ -1,166 +1,167 @@
{ lib, options, config, ... }:
- let
- inherit (lib) optionalString concatStringsSep concatMapStringsSep optionalAttrs mkIf mkDefault mkMerge mkOverride;
- inherit (lib.my) parseIPPort mkOpt mkBoolOpt dummyOption;
+let
+ inherit (lib) optionalString concatStringsSep concatMapStringsSep optionalAttrs mkIf mkDefault mkMerge mkOverride;
+ inherit (lib.my) parseIPPort mkOpt mkBoolOpt dummyOption;
- cfg = config.my.firewall;
- in {
- options.my.firewall = with lib.types; {
- enable = mkBoolOpt true;
- trustedInterfaces = options.networking.firewall.trustedInterfaces;
- tcp = {
- allowed = mkOpt (listOf (either port str)) [ "ssh" ];
- };
- udp = {
- allowed = mkOpt (listOf (either port str)) [];
- };
- extraRules = mkOpt lines "";
-
- nat = with options.networking.nat; {
- enable = mkBoolOpt true;
- inherit externalInterface forwardPorts;
- };
+ cfg = config.my.firewall;
+in
+{
+ options.my.firewall = with lib.types; {
+ enable = mkBoolOpt true;
+ trustedInterfaces = options.networking.firewall.trustedInterfaces;
+ tcp = {
+ allowed = mkOpt (listOf (either port str)) [ "ssh" ];
};
+ udp = {
+ allowed = mkOpt (listOf (either port str)) [ ];
+ };
+ extraRules = mkOpt lines "";
- config = mkIf cfg.enable (mkMerge [
- {
- networking = {
- firewall.enable = false;
- nftables = {
- enable = true;
- ruleset =
- let
- trusted' = "{ ${concatStringsSep ", " cfg.trustedInterfaces} }";
- in
- ''
- table inet filter {
- chain wan-tcp {
- ${concatMapStringsSep "\n " (p: "tcp dport ${toString p} accept") cfg.tcp.allowed}
- }
- chain wan-udp {
- ${concatMapStringsSep "\n " (p: "udp dport ${toString p} accept") cfg.udp.allowed}
- }
+ nat = with options.networking.nat; {
+ enable = mkBoolOpt true;
+ inherit externalInterface forwardPorts;
+ };
+ };
- chain wan {
- ip6 nexthdr icmpv6 icmpv6 type {
- destination-unreachable,
- packet-too-big,
- time-exceeded,
- parameter-problem,
- mld-listener-query,
- mld-listener-report,
- mld-listener-reduction,
- nd-router-solicit,
- nd-router-advert,
- nd-neighbor-solicit,
- nd-neighbor-advert,
- ind-neighbor-solicit,
- ind-neighbor-advert,
- mld2-listener-report,
- echo-request
- } accept
- ip protocol icmp icmp type {
- destination-unreachable,
- router-solicitation,
- router-advertisement,
- time-exceeded,
- parameter-problem,
- echo-request
- } accept
- ip protocol igmp accept
-
- ip protocol tcp tcp flags & (fin|syn|rst|ack) == syn ct state new jump wan-tcp
- ip protocol udp ct state new jump wan-udp
- }
-
- chain input {
- type filter hook input priority 0; policy drop;
-
- ct state established,related accept
- ct state invalid drop
-
- iif lo accept
- ${optionalString (cfg.trustedInterfaces != []) "iifname ${trusted'} accept\n"}
- jump wan
- }
- chain forward {
- type filter hook forward priority 0; policy drop;
- ${optionalString (cfg.trustedInterfaces != []) "\n iifname ${trusted'} accept\n"}
- ct state related,established accept
- }
- chain output {
- type filter hook output priority 0; policy accept;
- }
+ config = mkIf cfg.enable (mkMerge [
+ {
+ networking = {
+ firewall.enable = false;
+ nftables = {
+ enable = true;
+ ruleset =
+ let
+ trusted' = "{ ${concatStringsSep ", " cfg.trustedInterfaces} }";
+ in
+ ''
+ table inet filter {
+ chain wan-tcp {
+ ${concatMapStringsSep "\n " (p: "tcp dport ${toString p} accept") cfg.tcp.allowed}
+ }
+ chain wan-udp {
+ ${concatMapStringsSep "\n " (p: "udp dport ${toString p} accept") cfg.udp.allowed}
}
- table nat {
- chain prerouting {
- type nat hook prerouting priority 0;
- }
+ chain wan {
+ ip6 nexthdr icmpv6 icmpv6 type {
+ destination-unreachable,
+ packet-too-big,
+ time-exceeded,
+ parameter-problem,
+ mld-listener-query,
+ mld-listener-report,
+ mld-listener-reduction,
+ nd-router-solicit,
+ nd-router-advert,
+ nd-neighbor-solicit,
+ nd-neighbor-advert,
+ ind-neighbor-solicit,
+ ind-neighbor-advert,
+ mld2-listener-report,
+ echo-request
+ } accept
+ ip protocol icmp icmp type {
+ destination-unreachable,
+ router-solicitation,
+ router-advertisement,
+ time-exceeded,
+ parameter-problem,
+ echo-request
+ } accept
+ ip protocol igmp accept
- chain postrouting {
- type nat hook postrouting priority 100;
- }
+ ip protocol tcp tcp flags & (fin|syn|rst|ack) == syn ct state new jump wan-tcp
+ ip protocol udp ct state new jump wan-udp
}
- ${cfg.extraRules}
- '';
- };
+ chain input {
+ type filter hook input priority 0; policy drop;
+
+ ct state established,related accept
+ ct state invalid drop
+
+ iif lo accept
+ ${optionalString (cfg.trustedInterfaces != []) "iifname ${trusted'} accept\n"}
+ jump wan
+ }
+ chain forward {
+ type filter hook forward priority 0; policy drop;
+ ${optionalString (cfg.trustedInterfaces != []) "\n iifname ${trusted'} accept\n"}
+ ct state related,established accept
+ }
+ chain output {
+ type filter hook output priority 0; policy accept;
+ }
+ }
+
+ table nat {
+ chain prerouting {
+ type nat hook prerouting priority 0;
+ }
+
+ chain postrouting {
+ type nat hook postrouting priority 100;
+ }
+ }
+
+ ${cfg.extraRules}
+ '';
};
- }
- (mkIf cfg.nat.enable {
- assertions = [
- {
- assertion = (cfg.nat.forwardPorts != []) -> (cfg.nat.externalInterface != null);
- message = "my.firewall.nat.forwardPorts requires my.firewall.nat.externalInterface";
+ };
+ }
+ (mkIf cfg.nat.enable {
+ assertions = [
+ {
+ assertion = (cfg.nat.forwardPorts != [ ]) -> (cfg.nat.externalInterface != null);
+ message = "my.firewall.nat.forwardPorts requires my.firewall.nat.externalInterface";
+ }
+ ];
+
+ # Yoinked from nixpkgs/nixos/modules/services/networking/nat.nix
+ boot = {
+ kernel.sysctl = {
+ "net.ipv4.conf.all.forwarding" = mkOverride 99 true;
+ "net.ipv4.conf.default.forwarding" = mkOverride 99 true;
+ } // optionalAttrs config.networking.enableIPv6 {
+ # Do not prevent IPv6 autoconfiguration.
+ # See .
+ "net.ipv6.conf.all.accept_ra" = mkOverride 99 2;
+ "net.ipv6.conf.default.accept_ra" = mkOverride 99 2;
+
+ # Forward IPv6 packets.
+ "net.ipv6.conf.all.forwarding" = mkOverride 99 true;
+ "net.ipv6.conf.default.forwarding" = mkOverride 99 true;
+ };
+ };
+
+ my.firewall.extraRules =
+ let
+ makeFilter = f:
+ let
+ ipp = parseIPPort f.destination;
+ in
+ "ip${optionalString ipp.v6 "6"} daddr ${ipp.ip} ${f.proto} dport ${toString f.sourcePort} accept";
+ makeForward = f: "${f.proto} dport ${toString f.sourcePort} dnat to ${f.destination}";
+ in
+ ''
+ table inet filter {
+ chain filter-port-forwards {
+ ${concatMapStringsSep "\n " makeFilter cfg.nat.forwardPorts}
+ }
+ chain forward {
+ iifname ${cfg.nat.externalInterface} jump filter-port-forwards
+ }
}
- ];
- # Yoinked from nixpkgs/nixos/modules/services/networking/nat.nix
- boot = {
- kernel.sysctl = {
- "net.ipv4.conf.all.forwarding" = mkOverride 99 true;
- "net.ipv4.conf.default.forwarding" = mkOverride 99 true;
- } // optionalAttrs config.networking.enableIPv6 {
- # Do not prevent IPv6 autoconfiguration.
- # See .
- "net.ipv6.conf.all.accept_ra" = mkOverride 99 2;
- "net.ipv6.conf.default.accept_ra" = mkOverride 99 2;
-
- # Forward IPv6 packets.
- "net.ipv6.conf.all.forwarding" = mkOverride 99 true;
- "net.ipv6.conf.default.forwarding" = mkOverride 99 true;
- };
- };
-
- my.firewall.extraRules =
- let
- makeFilter = f:
- let
- ipp = parseIPPort f.destination;
- in
- "ip${optionalString ipp.v6 "6"} daddr ${ipp.ip} ${f.proto} dport ${toString f.sourcePort} accept";
- makeForward = f: "${f.proto} dport ${toString f.sourcePort} dnat to ${f.destination}";
- in
- ''
- table inet filter {
- chain filter-port-forwards {
- ${concatMapStringsSep "\n " makeFilter cfg.nat.forwardPorts}
- }
- chain forward {
- iifname ${cfg.nat.externalInterface} jump filter-port-forwards
- }
+ table nat {
+ chain port-forward {
+ ${concatMapStringsSep "\n " makeForward cfg.nat.forwardPorts}
}
-
- table nat {
- chain port-forward {
- ${concatMapStringsSep "\n " makeForward cfg.nat.forwardPorts}
- }
- chain prerouting {
- iifname ${cfg.nat.externalInterface} jump port-forward
- }
+ chain prerouting {
+ iifname ${cfg.nat.externalInterface} jump port-forward
}
- '';
- })
- ]);
- }
+ }
+ '';
+ })
+ ]);
+}
diff --git a/modules/server.nix b/modules/server.nix
index 5e7f58e..36615e5 100644
--- a/modules/server.nix
+++ b/modules/server.nix
@@ -1,10 +1,11 @@
{ config, lib, ... }:
- let
- inherit (lib) mkIf;
- inherit (lib.my) mkBoolOpt;
- in {
- options.my.server.enable = mkBoolOpt false;
- config = mkIf config.my.server.enable {
- services.getty.autologinUser = config.my.user.name;
- };
- }
+let
+ inherit (lib) mkIf;
+ inherit (lib.my) mkBoolOpt;
+in
+{
+ options.my.server.enable = mkBoolOpt false;
+ config = mkIf config.my.server.enable {
+ services.getty.autologinUser = config.my.user.name;
+ };
+}
diff --git a/modules/tmproot.nix b/modules/tmproot.nix
index d25bd87..8f9f46b 100644
--- a/modules/tmproot.nix
+++ b/modules/tmproot.nix
@@ -1,205 +1,206 @@
{ lib, pkgs, inputs, config, ... }:
- let
- inherit (builtins) elem;
- inherit (lib) concatStringsSep concatMap concatMapStringsSep mkIf mkDefault mkMerge mkForce mkVMOverride;
- inherit (lib.my) mkOpt mkBoolOpt mkVMOverride' dummyOption;
+let
+ inherit (builtins) elem;
+ inherit (lib) concatStringsSep concatMap concatMapStringsSep mkIf mkDefault mkMerge mkForce mkVMOverride;
+ inherit (lib.my) mkOpt mkBoolOpt mkVMOverride' dummyOption;
- cfg = config.my.tmproot;
+ cfg = config.my.tmproot;
- showUnsaved =
- ''
- #!${pkgs.python310}/bin/python
- import stat
- import sys
- import os
+ showUnsaved =
+ ''
+ #!${pkgs.python310}/bin/python
+ import stat
+ import sys
+ import os
- ignored = [
- ${concatStringsSep ",\n " (map (p: "'${p}'") cfg.unsaved.ignore)}
- ]
+ ignored = [
+ ${concatStringsSep ",\n " (map (p: "'${p}'") cfg.unsaved.ignore)}
+ ]
- base = '/'
- base_dev = os.stat(base).st_dev
+ base = '/'
+ base_dev = os.stat(base).st_dev
- def recurse(p, link=None):
- try:
- for ignore in ignored:
- if p.startswith(ignore):
- return
-
- st = os.lstat(p)
- if st.st_dev != base_dev:
+ def recurse(p, link=None):
+ try:
+ for ignore in ignored:
+ if p.startswith(ignore):
return
- if stat.S_ISLNK(st.st_mode):
- target = os.path.realpath(p, strict=False)
- if os.access(target, os.F_OK):
- recurse(target, link=p)
- return
- elif stat.S_ISDIR(st.st_mode):
- for e in os.listdir(p):
- recurse(os.path.join(p, e))
+ st = os.lstat(p)
+ if st.st_dev != base_dev:
+ return
+
+ if stat.S_ISLNK(st.st_mode):
+ target = os.path.realpath(p, strict=False)
+ if os.access(target, os.F_OK):
+ recurse(target, link=p)
return
+ elif stat.S_ISDIR(st.st_mode):
+ for e in os.listdir(p):
+ recurse(os.path.join(p, e))
+ return
- print(link or p)
- except PermissionError as ex:
- print(f'{p}: {ex.strerror}', file=sys.stderr)
+ print(link or p)
+ except PermissionError as ex:
+ print(f'{p}: {ex.strerror}', file=sys.stderr)
- recurse(base)
- '';
+ recurse(base)
+ '';
- rootDef = {
- device = "yeet";
- fsType = "tmpfs";
- options = [ "size=${cfg.size}" ];
+ rootDef = {
+ device = "yeet";
+ fsType = "tmpfs";
+ options = [ "size=${cfg.size}" ];
+ };
+in
+{
+ imports = [ inputs.impermanence.nixosModule ];
+
+ options = with lib.types; {
+ my.tmproot = {
+ enable = mkBoolOpt true;
+ persistDir = mkOpt str "/persist";
+ size = mkOpt str "2G";
+ unsaved = {
+ showMotd = mkBoolOpt true;
+ ignore = mkOpt (listOf str) [ ];
+ };
};
- in {
- imports = [ inputs.impermanence.nixosModule ];
- options = with lib.types; {
- my.tmproot = {
- enable = mkBoolOpt true;
- persistDir = mkOpt str "/persist";
- size = mkOpt str "2G";
- unsaved = {
- showMotd = mkBoolOpt true;
- ignore = mkOpt (listOf str) [];
- };
+ # Forward declare options that won't exist until the VM module is actually imported
+ virtualisation = {
+ diskImage = dummyOption;
+ };
+ };
+
+ config = mkIf cfg.enable (mkMerge [
+ {
+ assertions = [
+ {
+ assertion = config.fileSystems ? "${cfg.persistDir}";
+ message = "The 'fileSystems' option does not specify your persistence file system (${cfg.persistDir}).";
+ }
+ {
+ # I mean you probably _could_, but if you're doing tmproot... come on
+ assertion = !config.users.mutableUsers;
+ message = "users.mutableUsers is incompatible with tmproot";
+ }
+ ];
+
+ my.tmproot.unsaved.ignore = [
+ "/tmp"
+
+ # setup-etc.pl will create this for us
+ "/etc/NIXOS"
+
+ # Once mutableUsers is disabled, we should be all clear here
+ "/etc/passwd"
+ "/etc/group"
+ "/etc/shadow"
+ "/etc/subuid"
+ "/etc/subgid"
+
+ # Lock file for /etc/{passwd,shadow}
+ "/etc/.pwd.lock"
+
+ # systemd last updated? I presume they'll get updated on boot...
+ "/etc/.updated"
+ "/var/.updated"
+
+ # Specifies obsolete files that should be deleted on activation - we'll never have those!
+ "/etc/.clean"
+ ];
+
+ environment.systemPackages = [
+ (pkgs.writeScriptBin "tmproot-unsaved" showUnsaved)
+ ];
+
+ # Catch non-existent source directories that are needed for boot (see `pathsNeededForBoot` in
+ # nixos/lib/util.nix). We do this by monkey-patching the `waitDevice` function that would otherwise hang.
+ boot.initrd.postDeviceCommands =
+ ''
+ ensurePersistSource() {
+ [ -e "/mnt-root$1" ] && return
+ echo "Persistent source directory $1 does not exist, creating..."
+ install -dm "$2" "/mnt-root$1" || fail
+ }
+
+ _waitDevice() {
+ local device="$1"
+
+ ${concatMapStringsSep " || \\\n " (d:
+ let
+ sourceDir = "${d.persistentStoragePath}${d.directory}";
+ in
+ ''([ "$device" = "/mnt-root${sourceDir}" ] && ensurePersistSource "${sourceDir}" "${d.mode}")'')
+ config.environment.persistence."${cfg.persistDir}".directories}
+
+ waitDevice "$@"
+ }
+
+ type waitDevice > /dev/null || (echo "waitDevice is missing!"; fail)
+ alias waitDevice=_waitDevice
+ '';
+
+ environment.persistence."${cfg.persistDir}" = {
+ hideMounts = mkDefault true;
+ directories = [
+ "/var/log"
+ # In theory we'd include only the files needed individually (i.e. the {U,G}ID map files that track deleted
+ # users and groups), but `update-users-groups.pl` actually deletes the original files for "atomic update".
+ # Also the script runs before impermanence does.
+ "/var/lib/nixos"
+ "/var/lib/systemd"
+ ];
+ files = [
+ "/etc/machine-id"
+ ];
};
- # Forward declare options that won't exist until the VM module is actually imported
+ my.dynamic-motd.script = mkIf cfg.unsaved.showMotd
+ ''
+ tmprootUnsaved() {
+ local count="$(tmproot-unsaved | wc -l)"
+ [ $count -eq 0 ] && return
+
+ echo
+ echo -e "\t\e[31;1;4mWarning:\e[0m $count file(s) on / will be lost on shutdown!"
+ echo -e '\tTo see them, run `tmproot-unsaved` as root.'
+ echo -e '\tAdd these files to `environment.persistence."${cfg.persistDir}"` to keep them!'
+ echo -e '\tOtherwise, they can be ignored by adding to `my.tmproot.unsaved.ignore`.'
+ echo
+ }
+
+ tmprootUnsaved
+ '';
+
+ fileSystems."/" = rootDef;
+
virtualisation = {
- diskImage = dummyOption;
+ diskImage = "./.vms/${config.system.name}-persist.qcow2";
};
- };
+ }
+ (mkIf config.services.openssh.enable {
+ environment.persistence."${cfg.persistDir}".files =
+ concatMap (k: [ k.path "${k.path}.pub" ]) config.services.openssh.hostKeys;
+ })
+ (mkIf config.networking.resolvconf.enable {
+ my.tmproot.unsaved.ignore = [ "/etc/resolv.conf" ];
+ })
+ (mkIf config.security.doas.enable {
+ my.tmproot.unsaved.ignore = [ "/etc/doas.conf" ];
+ })
+ (mkIf config.my.boot.isDevVM {
+ my.tmproot.unsaved.ignore = [ "/nix" ];
- config = mkIf cfg.enable (mkMerge [
- {
- assertions = [
- {
- assertion = config.fileSystems ? "${cfg.persistDir}";
- message = "The 'fileSystems' option does not specify your persistence file system (${cfg.persistDir}).";
- }
- {
- # I mean you probably _could_, but if you're doing tmproot... come on
- assertion = !config.users.mutableUsers;
- message = "users.mutableUsers is incompatible with tmproot";
- }
- ];
-
- my.tmproot.unsaved.ignore = [
- "/tmp"
-
- # setup-etc.pl will create this for us
- "/etc/NIXOS"
-
- # Once mutableUsers is disabled, we should be all clear here
- "/etc/passwd"
- "/etc/group"
- "/etc/shadow"
- "/etc/subuid"
- "/etc/subgid"
-
- # Lock file for /etc/{passwd,shadow}
- "/etc/.pwd.lock"
-
- # systemd last updated? I presume they'll get updated on boot...
- "/etc/.updated"
- "/var/.updated"
-
- # Specifies obsolete files that should be deleted on activation - we'll never have those!
- "/etc/.clean"
- ];
-
- environment.systemPackages = [
- (pkgs.writeScriptBin "tmproot-unsaved" showUnsaved)
- ];
-
- # Catch non-existent source directories that are needed for boot (see `pathsNeededForBoot` in
- # nixos/lib/util.nix). We do this by monkey-patching the `waitDevice` function that would otherwise hang.
- boot.initrd.postDeviceCommands =
- ''
- ensurePersistSource() {
- [ -e "/mnt-root$1" ] && return
- echo "Persistent source directory $1 does not exist, creating..."
- install -dm "$2" "/mnt-root$1" || fail
- }
-
- _waitDevice() {
- local device="$1"
-
- ${concatMapStringsSep " || \\\n " (d:
- let
- sourceDir = "${d.persistentStoragePath}${d.directory}";
- in
- ''([ "$device" = "/mnt-root${sourceDir}" ] && ensurePersistSource "${sourceDir}" "${d.mode}")'')
- config.environment.persistence."${cfg.persistDir}".directories}
-
- waitDevice "$@"
- }
-
- type waitDevice > /dev/null || (echo "waitDevice is missing!"; fail)
- alias waitDevice=_waitDevice
- '';
-
- environment.persistence."${cfg.persistDir}" = {
- hideMounts = mkDefault true;
- directories = [
- "/var/log"
- # In theory we'd include only the files needed individually (i.e. the {U,G}ID map files that track deleted
- # users and groups), but `update-users-groups.pl` actually deletes the original files for "atomic update".
- # Also the script runs before impermanence does.
- "/var/lib/nixos"
- "/var/lib/systemd"
- ];
- files = [
- "/etc/machine-id"
- ];
+ fileSystems = mkVMOverride {
+ "/" = mkVMOverride' rootDef;
+ # Hijack the "root" device for persistence in the VM
+ "${cfg.persistDir}" = {
+ device = config.virtualisation.bootDevice;
+ neededForBoot = true;
};
-
- my.dynamic-motd.script = mkIf cfg.unsaved.showMotd
- ''
- tmprootUnsaved() {
- local count="$(tmproot-unsaved | wc -l)"
- [ $count -eq 0 ] && return
-
- echo
- echo -e "\t\e[31;1;4mWarning:\e[0m $count file(s) on / will be lost on shutdown!"
- echo -e '\tTo see them, run `tmproot-unsaved` as root.'
- echo -e '\tAdd these files to `environment.persistence."${cfg.persistDir}"` to keep them!'
- echo -e '\tOtherwise, they can be ignored by adding to `my.tmproot.unsaved.ignore`.'
- echo
- }
-
- tmprootUnsaved
- '';
-
- fileSystems."/" = rootDef;
-
- virtualisation = {
- diskImage = "./.vms/${config.system.name}-persist.qcow2";
- };
- }
- (mkIf config.services.openssh.enable {
- environment.persistence."${cfg.persistDir}".files =
- concatMap (k: [ k.path "${k.path}.pub" ]) config.services.openssh.hostKeys;
- })
- (mkIf config.networking.resolvconf.enable {
- my.tmproot.unsaved.ignore = [ "/etc/resolv.conf" ];
- })
- (mkIf config.security.doas.enable {
- my.tmproot.unsaved.ignore = [ "/etc/doas.conf" ];
- })
- (mkIf config.my.boot.isDevVM {
- my.tmproot.unsaved.ignore = [ "/nix" ];
-
- fileSystems = mkVMOverride {
- "/" = mkVMOverride' rootDef;
- # Hijack the "root" device for persistence in the VM
- "${cfg.persistDir}" = {
- device = config.virtualisation.bootDevice;
- neededForBoot = true;
- };
- };
- })
- ]);
- }
+ };
+ })
+ ]);
+}
diff --git a/systems.nix b/systems.nix
index b1101a7..d8501d0 100644
--- a/systems.nix
+++ b/systems.nix
@@ -1,35 +1,38 @@
{ lib, pkgsFlakes, inputs, modules }:
- let
- inherit (builtins) attrValues mapAttrs;
- inherit (lib) mkDefault;
+let
+ inherit (builtins) attrValues mapAttrs;
+ inherit (lib) mkDefault;
- mkSystem = name: {
+ mkSystem =
+ name: {
system,
nixpkgs ? "unstable",
config,
}:
- let
- pkgsFlake = pkgsFlakes.${nixpkgs};
- lib = pkgsFlake.lib;
- # TODO: This is mostly yoinked from nixpkgs/flake.nix master (as of 2022/02/11) since 21.11's version has hacky
- # vm build stuff that breaks our impl. REMOVE WHEN 22.05 IS OUT!
- nixosSystem' = args:
- import "${pkgsFlake}/nixos/lib/eval-config.nix" (args // {
- modules = args.modules ++ [ {
- system.nixos.versionSuffix =
- ".${lib.substring 0 8 pkgsFlake.lastModifiedDate}.${pkgsFlake.shortRev}";
- system.nixos.revision = pkgsFlake.rev;
- } ];
- });
- in nixosSystem' {
- inherit lib system;
- specialArgs = { inherit inputs system; };
- modules = attrValues modules ++ [ { networking.hostName = mkDefault name; } config ];
- };
- in mapAttrs mkSystem {
- colony = {
- system = "x86_64-linux";
- nixpkgs = "stable";
- config = boxes/colony.nix;
+ let
+ pkgsFlake = pkgsFlakes.${nixpkgs};
+ lib = pkgsFlake.lib;
+ # TODO: This is mostly yoinked from nixpkgs/flake.nix master (as of 2022/02/11) since 21.11's version has hacky
+ # vm build stuff that breaks our impl. REMOVE WHEN 22.05 IS OUT!
+ nixosSystem' = args:
+ import "${pkgsFlake}/nixos/lib/eval-config.nix" (args // {
+ modules = args.modules ++ [{
+ system.nixos.versionSuffix =
+ ".${lib.substring 0 8 pkgsFlake.lastModifiedDate}.${pkgsFlake.shortRev}";
+ system.nixos.revision = pkgsFlake.rev;
+ }];
+ });
+ in
+ nixosSystem' {
+ inherit lib system;
+ specialArgs = { inherit inputs system; };
+ modules = attrValues modules ++ [ { networking.hostName = mkDefault name; } config ];
};
- }
+in
+mapAttrs mkSystem {
+ colony = {
+ system = "x86_64-linux";
+ nixpkgs = "stable";
+ config = boxes/colony.nix;
+ };
+}
diff --git a/util.nix b/util.nix
index ab41242..78cf576 100644
--- a/util.nix
+++ b/util.nix
@@ -1,32 +1,34 @@
{ lib }:
- let
- inherit (builtins) replaceStrings elemAt;
- inherit (lib) genAttrs mapAttrs' types mkOption mkOverride;
- inherit (lib.flake) defaultSystems;
- in rec {
- addPrefix = prefix: mapAttrs' (n: v: { name = "${prefix}${n}"; value = v; });
- # Yoinked from nixpkgs/nixos/modules/services/networking/nat.nix
- isIPv6 = ip: builtins.length (lib.splitString ":" ip) > 2;
- parseIPPort = ipp:
- let
- v6 = isIPv6 ipp;
- matchIP = if v6 then "[[]([0-9a-fA-F:]+)[]]" else "([0-9.]+)";
- m = builtins.match "${matchIP}:([0-9-]+)" ipp;
- checked = v: if m == null then throw "bad ip:ports `${ipp}'" else v;
- in {
- inherit v6;
- ip = checked (elemAt m 0);
- ports = checked (replaceStrings ["-"] [":"] (elemAt m 1));
- };
-
- mkPkgs = path: args: genAttrs defaultSystems (system: import path (args // { inherit system; }));
-
- mkOpt = type: default: mkOption { inherit type default; };
- mkBoolOpt = default: mkOption {
- inherit default;
- type = types.bool;
- example = true;
+let
+ inherit (builtins) replaceStrings elemAt;
+ inherit (lib) genAttrs mapAttrs' types mkOption mkOverride;
+ inherit (lib.flake) defaultSystems;
+in
+rec {
+ addPrefix = prefix: mapAttrs' (n: v: { name = "${prefix}${n}"; value = v; });
+ # Yoinked from nixpkgs/nixos/modules/services/networking/nat.nix
+ isIPv6 = ip: builtins.length (lib.splitString ":" ip) > 2;
+ parseIPPort = ipp:
+ let
+ v6 = isIPv6 ipp;
+ matchIP = if v6 then "[[]([0-9a-fA-F:]+)[]]" else "([0-9.]+)";
+ m = builtins.match "${matchIP}:([0-9-]+)" ipp;
+ checked = v: if m == null then throw "bad ip:ports `${ipp}'" else v;
+ in
+ {
+ inherit v6;
+ ip = checked (elemAt m 0);
+ ports = checked (replaceStrings ["-"] [":"] (elemAt m 1));
};
- mkVMOverride' = mkOverride 9;
- dummyOption = mkOption {};
- }
+
+ mkPkgs = path: args: genAttrs defaultSystems (system: import path (args // { inherit system; }));
+
+ mkOpt = type: default: mkOption { inherit type default; };
+ mkBoolOpt = default: mkOption {
+ inherit default;
+ type = types.bool;
+ example = true;
+ };
+ mkVMOverride' = mkOverride 9;
+ dummyOption = mkOption { };
+}