This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

modules: rename from 3modules

+735 -735
+2 -2
AGENTS.md
··· 6 6 7 7 This is lassulus's personal NixOS/nix-darwin configuration repository, built on clan-core for managing multiple machines. It includes: 8 8 - Multiple machine configurations in `machines/` 9 - - Configuration modules in `configs/` and `3modules/` 9 + - Configuration modules in `configs/` and `modules/` 10 10 - Custom packages in `pkgs/` 11 11 - packages which could in theory be upstreamed to nixkgs should go into the pkgs folder, in the tools folder we add tools we write ourself which are more complex or not upstreamable 12 12 ··· 23 23 ### Module System 24 24 - `configs/`: Main configuration modules 25 25 - `configs/default.nix`: imported by all machines 26 - - `3modules/`: Custom NixOS module definitions 26 + - `modules/`: Custom NixOS module definitions 27 27 - Stockholm modules are also imported for krebs infrastructure 28 28 29 29 ## Common Development Commands
-9
3modules/default.nix
··· 1 - _: { 2 - imports = [ 3 - ./klem.nix 4 - ./screenlock.nix 5 - ./fetchWallpaper.nix 6 - ./sync-containers3.nix 7 - ./parley.nix 8 - ]; 9 - }
-85
3modules/fetchWallpaper.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - ... 6 - }: 7 - 8 - let 9 - cfg = config.lass.fetchWallpaper; 10 - 11 - fetchWallpaperScript = pkgs.writers.writeDash "fetchWallpaper" '' 12 - set -eufx 13 - 14 - curl -v -s -o wallpaper.tmp -z wallpaper.tmp ${lib.escapeShellArg cfg.url} && cp wallpaper.tmp wallpaper 15 - feh --no-fehbg --bg-scale wallpaper 16 - ''; 17 - 18 - in 19 - { 20 - options.lass.fetchWallpaper = { 21 - enable = lib.mkEnableOption "fetch wallpaper"; 22 - url = lib.mkOption { 23 - type = lib.types.str; 24 - }; 25 - timerConfig = lib.mkOption { 26 - type = lib.types.unspecified; 27 - default = { 28 - OnCalendar = "*:00,10,20,30,40,50"; 29 - }; 30 - }; 31 - display = lib.mkOption { 32 - type = lib.types.str; 33 - default = ":${toString config.services.xserver.display}"; 34 - }; 35 - unitConfig = lib.mkOption { 36 - type = lib.types.attrsOf lib.types.str; 37 - description = "Extra unit configuration for fetchWallpaper to define conditions and assertions for the unit"; 38 - example = lib.literalExample '' 39 - # do not start when running on umts 40 - { ConditionPathExists = "!/var/run/ppp0.pid"; } 41 - ''; 42 - default = { }; 43 - }; 44 - }; 45 - 46 - config = lib.mkIf cfg.enable { 47 - systemd.timers.fetchWallpaper = { 48 - description = "fetch wallpaper timer"; 49 - wantedBy = [ "timers.target" ]; 50 - 51 - timerConfig = cfg.timerConfig; 52 - }; 53 - systemd.services.fetchWallpaper = { 54 - description = "fetch wallpaper"; 55 - after = [ "network.target" ]; 56 - 57 - path = with pkgs; [ 58 - curl 59 - feh 60 - ]; 61 - 62 - environment = { 63 - URL = cfg.url; 64 - DISPLAY = cfg.display; 65 - }; 66 - restartIfChanged = true; 67 - 68 - serviceConfig = { 69 - Type = "simple"; 70 - ExecStart = fetchWallpaperScript; 71 - StateDirectory = "wallpaper"; 72 - StateDirectoryMode = "0755"; 73 - WorkingDirectory = "/var/lib/wallpaper"; 74 - }; 75 - 76 - unitConfig = cfg.unitConfig; 77 - }; 78 - 79 - users.users.wallpaper = { 80 - isSystemUser = true; 81 - group = "wallpaper"; 82 - }; 83 - users.groups.wallpaper = { }; 84 - }; 85 - }
-95
3modules/klem.nix
··· 1 - { 2 - self, 3 - config, 4 - lib, 5 - pkgs, 6 - ... 7 - }: 8 - with lib; 9 - let 10 - cfg = config.lass.klem; 11 - slib = self.inputs.stockholm.lib; 12 - in 13 - { 14 - options.lass.klem = mkOption { 15 - default = { }; 16 - type = types.attrsOf ( 17 - types.submodule ( 18 - { config, ... }: 19 - { 20 - options = { 21 - target = mkOption { 22 - default = ".*"; 23 - description = '' 24 - regex of valid targets 25 - can be shown with xclip -selection clipboard -t TARGETS 26 - the first hit is taken as target argument 27 - ''; 28 - type = types.str; 29 - }; 30 - script = mkOption { 31 - description = '' 32 - file to run if entry is selected 33 - ''; 34 - type = types.path; 35 - }; 36 - label = mkOption { 37 - default = config._module.args.name; 38 - description = '' 39 - label to show in dmenu for this script 40 - ''; 41 - type = types.str; 42 - }; 43 - }; 44 - } 45 - ) 46 - ); 47 - }; 48 - config = 49 - let 50 - klem = pkgs.writers.writeDashBin "klem" '' 51 - set -x 52 - 53 - labels="" 54 - # match filetype against patterns 55 - ${concatMapStringsSep "\n" (script: '' 56 - ${pkgs.xclip}/bin/xclip -selection clipboard -target TARGETS -out \ 57 - | ${pkgs.gnugrep}/bin/grep -q '${script.target}' 58 - if [ $? -eq 0 ]; then 59 - labels="$labels:${script.label}" 60 - fi 61 - '') (attrValues cfg)} 62 - 63 - #remove empty line, feed into dmenu 64 - script=$(echo "$labels" \ 65 - | ${pkgs.gnused}/bin/sed 's/^://;s/:/\n/g' \ 66 - | ${pkgs.dmenu}/bin/dmenu) 67 - 68 - #run the chosen script 69 - case $script in 70 - ${concatMapStringsSep "\n" ( 71 - script: 72 - slib.indent '' 73 - ${script.label}) 74 - target=$(${pkgs.xclip}/bin/xclip -selection clipboard -target TARGETS -out \ 75 - | ${pkgs.gnugrep}/bin/grep '${script.target}' \ 76 - | ${pkgs.gnugrep}/bin/grep -v TARGETS \ 77 - | ${pkgs.coreutils}/bin/head -1) 78 - ${pkgs.xclip}/bin/xclip -selection clipboard -target "$target" -out \ 79 - | ${script.script} \ 80 - | ${pkgs.xclip}/bin/xclip -selection clipboard -in 81 - ;; 82 - '' 83 - ) (attrValues cfg)} 84 - esac 85 - ''; 86 - in 87 - mkIf (cfg != { }) { 88 - environment.systemPackages = [ klem ]; 89 - nixpkgs.overlays = [ 90 - (_self: _super: { 91 - klem = klem; 92 - }) 93 - ]; 94 - }; 95 - }
-141
3modules/parley.nix
··· 1 - { 2 - self, 3 - config, 4 - lib, 5 - pkgs, 6 - ... 7 - }: 8 - let 9 - cfg = config.lass.parley; 10 - generators = config.clan.core.vars.generators; 11 - in 12 - { 13 - options.lass.parley = { 14 - enable = lib.mkEnableOption "parley, federated chat that speaks plain IRC"; 15 - 16 - package = lib.mkOption { 17 - type = lib.types.package; 18 - default = self.packages.${pkgs.stdenv.hostPlatform.system}.parley; 19 - }; 20 - 21 - settings = lib.mkOption { 22 - type = lib.types.attrsOf lib.types.str; 23 - default = { }; 24 - example = { 25 - PARLEY_DOMAIN = "example.com"; 26 - PARLEY_ENDPOINT = "https://chat.example.com"; 27 - }; 28 - description = "PARLEY_* environment variables for parleyd (upstream README, Configuration)."; 29 - }; 30 - 31 - users = lib.mkOption { 32 - default = { }; 33 - description = '' 34 - Local accounts. Each gets a generated password 35 - (`clan vars get <machine> parley-user-<nick>/password`); after every 36 - start the account is created, or reset to its declared role and 37 - password. Accounts not listed here are left alone. 38 - ''; 39 - type = lib.types.attrsOf ( 40 - lib.types.submodule { 41 - options.role = lib.mkOption { 42 - type = lib.types.enum [ 43 - "admin" 44 - "user" 45 - "bot" 46 - ]; 47 - default = "user"; 48 - }; 49 - } 50 - ); 51 - }; 52 - }; 53 - 54 - config = lib.mkIf cfg.enable { 55 - lass.parley.settings = { 56 - PARLEY_DATA_DIR = "/var/lib/parley"; 57 - PARLEY_IRC_LISTEN = lib.mkDefault "127.0.0.1:6667"; 58 - PARLEY_HTTP_LISTEN = lib.mkDefault "127.0.0.1:8443"; 59 - }; 60 - 61 - clan.core.vars.generators = { 62 - parley = { 63 - # The instance key is the domain's identity: peers cache its public 64 - # half, so regenerating it is a key rotation every peer has to re-learn. 65 - files."identity.key" = { }; 66 - files."env" = { }; 67 - runtimeInputs = [ pkgs.openssl ]; 68 - script = '' 69 - # parley's key format: base64 of a 32-byte ed25519 seed 70 - openssl rand -base64 32 > "$out/identity.key" 71 - echo "PARLEY_ADMIN_TOKEN=$(openssl rand -hex 32)" > "$out/env" 72 - ''; 73 - }; 74 - } 75 - // lib.mapAttrs' ( 76 - nick: _: 77 - lib.nameValuePair "parley-user-${nick}" { 78 - files."password" = { }; 79 - runtimeInputs = [ pkgs.openssl ]; 80 - script = '' 81 - openssl rand -hex 24 > "$out/password" 82 - ''; 83 - } 84 - ) cfg.users; 85 - 86 - users.users.parley = { 87 - isSystemUser = true; 88 - group = "parley"; 89 - }; 90 - users.groups.parley = { }; 91 - 92 - environment.systemPackages = [ cfg.package ]; 93 - 94 - systemd.services.parley = { 95 - wantedBy = [ "multi-user.target" ]; 96 - after = [ "network-online.target" ]; 97 - wants = [ "network-online.target" ]; 98 - environment = cfg.settings; 99 - path = [ 100 - cfg.package 101 - pkgs.curl 102 - ]; 103 - preStart = '' 104 - install -m 0600 "$CREDENTIALS_DIRECTORY/identity.key" identity.key 105 - ''; 106 - postStart = lib.optionalString (cfg.users != { }) '' 107 - export PARLEY_ENDPOINT=http://$PARLEY_HTTP_LISTEN 108 - until curl -fs -o /dev/null "$PARLEY_ENDPOINT/healthz"; do sleep 1; done 109 - ${lib.concatStrings ( 110 - lib.mapAttrsToList (nick: user: '' 111 - pw="$CREDENTIALS_DIRECTORY/user-${nick}" 112 - parleyctl accounts update ${nick} -role ${user.role} -password-stdin < "$pw" > /dev/null \ 113 - || parleyctl accounts create ${nick} -role ${user.role} -password-stdin < "$pw" > /dev/null 114 - '') cfg.users 115 - )} 116 - ''; 117 - serviceConfig = { 118 - ExecStart = lib.getExe' cfg.package "parleyd"; 119 - User = "parley"; 120 - Group = "parley"; 121 - StateDirectory = "parley"; 122 - StateDirectoryMode = "0700"; 123 - WorkingDirectory = "/var/lib/parley"; 124 - UMask = "0077"; 125 - EnvironmentFile = generators.parley.files."env".path; 126 - LoadCredential = [ 127 - "identity.key:${generators.parley.files."identity.key".path}" 128 - ] 129 - ++ lib.mapAttrsToList ( 130 - nick: _: "user-${nick}:${generators."parley-user-${nick}".files."password".path}" 131 - ) cfg.users; 132 - Restart = "on-failure"; 133 - NoNewPrivileges = true; 134 - ProtectSystem = "strict"; 135 - ProtectHome = true; 136 - PrivateTmp = true; 137 - PrivateDevices = true; 138 - }; 139 - }; 140 - }; 141 - }
-43
3modules/screenlock.nix
··· 1 - { 2 - pkgs, 3 - lib, 4 - config, 5 - ... 6 - }: 7 - let 8 - cfg = config.lass.screenlock; 9 - 10 - out = { 11 - options.lass.screenlock = api; 12 - config = lib.mkIf cfg.enable imp; 13 - }; 14 - 15 - api = { 16 - enable = lib.mkEnableOption "screenlock"; 17 - command = lib.mkOption { 18 - type = lib.types.path; 19 - default = pkgs.writers.writeDash "screenlock" '' 20 - ${pkgs.xlockmore}/bin/xlock -mode life1d -size 1 21 - sleep 3 22 - ''; 23 - }; 24 - }; 25 - 26 - imp = { 27 - systemd.services.screenlock = { 28 - before = [ "sleep.target" ]; 29 - requiredBy = [ "sleep.target" ]; 30 - environment = { 31 - DISPLAY = ":${toString config.services.xserver.display}"; 32 - }; 33 - serviceConfig = { 34 - SyslogIdentifier = "screenlock"; 35 - ExecStart = cfg.command; 36 - Type = "simple"; 37 - User = "lass"; 38 - }; 39 - }; 40 - }; 41 - 42 - in 43 - out
-358
3modules/sync-containers3.nix
··· 1 - { 2 - config, 3 - lib, 4 - pkgs, 5 - ... 6 - }: 7 - let 8 - cfg = config.krebs.sync-containers3; 9 - in 10 - { 11 - options.krebs.sync-containers3 = { 12 - inContainer = { 13 - enable = lib.mkEnableOption "container config for syncing"; 14 - pubkey = lib.mkOption { 15 - type = lib.types.str; 16 - }; 17 - }; 18 - containers = lib.mkOption { 19 - default = { }; 20 - type = lib.types.attrsOf ( 21 - lib.types.submodule ( 22 - { config, ... }: 23 - { 24 - options = { 25 - name = lib.mkOption { 26 - type = lib.types.str; 27 - default = config._module.args.name; 28 - }; 29 - sshKey = lib.mkOption { 30 - type = lib.types.str; 31 - }; 32 - luksKey = lib.mkOption { 33 - type = lib.types.str; 34 - default = config.sshKey; 35 - }; 36 - ephemeral = lib.mkOption { 37 - type = lib.types.bool; 38 - default = false; 39 - }; 40 - runContainer = lib.mkOption { 41 - type = lib.types.bool; 42 - default = true; 43 - }; 44 - startCommand = lib.mkOption { 45 - type = lib.types.str; 46 - default = '' 47 - set -efu 48 - mkdir -p /var/state/var_src 49 - ln -Tfrs /var/state/var_src /var/src 50 - if test -e /var/src/nixos-config; then 51 - /run/current-system/sw/bin/nixos-rebuild -I /var/src switch || : 52 - fi 53 - ''; 54 - }; 55 - hostname = lib.mkOption { 56 - type = lib.types.str; 57 - description = '' 58 - hostname of the container, 59 - this is continuously checked by ping and the container is restarted if unreachable 60 - ''; 61 - default = config.name; 62 - }; 63 - }; 64 - } 65 - ) 66 - ); 67 - }; 68 - }; 69 - config = lib.mkMerge [ 70 - (lib.mkIf (cfg.containers != { }) { 71 - 72 - containers = lib.mapAttrs' ( 73 - _n: ctr: 74 - lib.nameValuePair ctr.name { 75 - config = { 76 - environment.systemPackages = [ 77 - pkgs.dhcpcd 78 - pkgs.git 79 - pkgs.jq 80 - ]; 81 - networking.useDHCP = lib.mkForce true; 82 - networking.useHostResolvConf = false; 83 - services.resolved.enable = true; 84 - systemd.services.autoswitch = { 85 - environment = { 86 - NIX_REMOTE = "daemon"; 87 - }; 88 - wantedBy = [ "multi-user.target" ]; 89 - serviceConfig = { 90 - ExecStart = pkgs.writers.writeDash "autoswitch" ctr.startCommand; 91 - # keep retrying transient failures (e.g. a GitHub 5xx while 92 - # resolving the flake at boot) so we self-heal instead of 93 - # staying silently stale. RestartSec is well above the default 94 - # StartLimitIntervalSec, so the restart rate limiter never trips 95 - # and we retry until it succeeds. 96 - Restart = "on-failure"; 97 - RestartSec = "120s"; 98 - }; 99 - unitConfig.X-StopOnRemoval = false; 100 - }; 101 - # get rid of stateVersion not set warning; 102 - system.stateVersion = config.system.nixos.release; 103 - }; 104 - autoStart = false; 105 - enableTun = true; 106 - ephemeral = ctr.ephemeral; 107 - privateNetwork = true; 108 - hostBridge = "ctr0"; 109 - bindMounts = { 110 - "/var/lib/self/disk" = { 111 - hostPath = "/var/lib/sync-containers3/${ctr.name}/disk"; 112 - isReadOnly = false; 113 - }; 114 - "/var/state" = { 115 - hostPath = "/var/lib/sync-containers3/${ctr.name}/state"; 116 - isReadOnly = false; 117 - }; 118 - }; 119 - } 120 - ) (lib.filterAttrs (_: ctr: ctr.runContainer) cfg.containers); 121 - 122 - systemd.services = lib.foldr lib.recursiveUpdate { } ( 123 - lib.flatten ( 124 - map (ctr: [ 125 - { 126 - "${ctr.name}_syncer" = { 127 - path = with pkgs; [ 128 - coreutils 129 - inetutils 130 - rsync 131 - openssh 132 - systemd 133 - util-linux 134 - ]; 135 - startAt = "*:0/1"; 136 - serviceConfig = { 137 - User = "${ctr.name}_container"; 138 - LoadCredential = [ 139 - "ssh_key:${ctr.sshKey}" 140 - ]; 141 - ExecCondition = pkgs.writers.writeDash "${ctr.name}_checker" '' 142 - set -efu 143 - ! systemctl is-active --quiet container@${ctr.name}.service 144 - ''; 145 - ExecStart = pkgs.writers.writeDash "${ctr.name}_syncer" '' 146 - set -efux 147 - flock -n "$HOME"/sync.lock ${pkgs.writers.writeDash "${ctr.name}-sync" '' 148 - set -efux 149 - if ping -c 1 ${ctr.hostname}; then 150 - nice --adjustment=30 rsync -a -e "ssh -i $CREDENTIALS_DIRECTORY/ssh_key" --timeout=30 --inplace --sparse container_sync@${ctr.hostname}:disk "$HOME"/disk.rsync 151 - touch "$HOME"/incomplete 152 - nice --adjustment=30 rsync --inplace "$HOME"/disk.rsync "$HOME"/disk 153 - rm -f "$HOME"/incomplete 154 - fi 155 - ''} 156 - ''; 157 - }; 158 - }; 159 - } 160 - { 161 - "${ctr.name}_watcher" = lib.mkIf ctr.runContainer { 162 - path = with pkgs; [ 163 - coreutils 164 - inetutils 165 - cryptsetup 166 - mount 167 - util-linux 168 - retry 169 - ]; 170 - serviceConfig = { 171 - ExecStart = pkgs.writers.writeDash "${ctr.name}_watcher" '' 172 - set -efux 173 - while sleep 5; do 174 - if ! $(retry -t 10 -d 10 -- ping -q -c 1 ${ctr.hostname} > /dev/null); then 175 - echo 'container seems dead, killing' 176 - break 177 - fi 178 - done 179 - /run/current-system/sw/bin/nixos-container stop ${ctr.name} || : 180 - umount /var/lib/sync-containers3/${ctr.name}/state || : 181 - cryptsetup luksClose ${ctr.name} || : 182 - ''; 183 - }; 184 - }; 185 - } 186 - { 187 - "${ctr.name}_scheduler" = lib.mkIf ctr.runContainer { 188 - wantedBy = [ "multi-user.target" ]; 189 - path = with pkgs; [ 190 - coreutils 191 - inetutils 192 - cryptsetup 193 - mount 194 - util-linux 195 - systemd 196 - retry 197 - ]; 198 - serviceConfig = { 199 - Restart = "always"; 200 - RestartSec = "30s"; 201 - ExecStart = pkgs.writers.writeBash "${ctr.name}_scheduler" '' 202 - set -efu 203 - cryptsetup luksOpen --key-file ${ctr.luksKey} /var/lib/sync-containers3/${ctr.name}/disk ${ctr.name} || : 204 - mkdir -p /var/lib/sync-containers3/${ctr.name}/state 205 - mountpoint /var/lib/sync-containers3/${ctr.name}/state || mount /dev/mapper/${ctr.name} /var/lib/sync-containers3/${ctr.name}/state 206 - /run/current-system/sw/bin/nixos-container start ${ctr.name} 207 - # wait for system to become reachable for the first time 208 - systemctl start ${ctr.name}_watcher.service 209 - retry -t 10 -d 10 -- ping -q -c 1 ${ctr.hostname} > /dev/null 210 - while systemctl is-active container@${ctr.name}.service >/dev/null && ping -q -c 3 ${ctr.hostname} >/dev/null; do 211 - sleep 10 212 - done 213 - ''; 214 - }; 215 - }; 216 - } 217 - { 218 - "container@${ctr.name}" = lib.mkIf ctr.runContainer { 219 - serviceConfig = { 220 - ExecStop = pkgs.writers.writeDash "remove_interface" '' 221 - ${pkgs.iproute2}/bin/ip link del vb-${ctr.name} 222 - ''; 223 - ExecStartPost = [ 224 - (pkgs.writers.writeDash "bind-to-bridge" '' 225 - ${pkgs.iproute2}/bin/ip link set "vb-$INSTANCE" master ctr0 226 - '') 227 - ]; 228 - }; 229 - }; 230 - } 231 - ]) (lib.attrValues cfg.containers) 232 - ) 233 - ); 234 - 235 - systemd.timers = lib.mapAttrs' ( 236 - _n: ctr: 237 - lib.nameValuePair "${ctr.name}_syncer" { 238 - timerConfig = { 239 - RandomizedDelaySec = 100; 240 - }; 241 - } 242 - ) cfg.containers; 243 - 244 - users.groups = lib.mapAttrs' ( 245 - _: ctr: 246 - lib.nameValuePair "${ctr.name}_container" { 247 - } 248 - ) cfg.containers; 249 - users.users = lib.mapAttrs' ( 250 - _: ctr: 251 - lib.nameValuePair "${ctr.name}_container" ({ 252 - group = "${ctr.name}_container"; 253 - isNormalUser = true; 254 - home = "/var/lib/sync-containers3/${ctr.name}"; 255 - createHome = true; 256 - homeMode = "705"; 257 - }) 258 - ) cfg.containers; 259 - 260 - environment.systemPackages = lib.mapAttrsToList ( 261 - _: ctr: 262 - (pkgs.writers.writeDashBin "${ctr.name}_init" '' 263 - set -efux 264 - export PATH=${ 265 - lib.makeBinPath [ 266 - pkgs.coreutils 267 - pkgs.cryptsetup 268 - pkgs.libxfs.bin 269 - ] 270 - }:$PATH 271 - truncate -s 5G /var/lib/sync-containers3/${ctr.name}/disk 272 - cryptsetup luksFormat /var/lib/sync-containers3/${ctr.name}/disk ${ctr.luksKey} 273 - cryptsetup luksOpen --key-file ${ctr.luksKey} /var/lib/sync-containers3/${ctr.name}/disk ${ctr.name} 274 - mkfs.xfs /dev/mapper/${ctr.name} 275 - mkdir -p /var/lib/sync-containers3/${ctr.name}/state 276 - mountpoint /var/lib/sync-containers3/${ctr.name}/state || mount /dev/mapper/${ctr.name} /var/lib/sync-containers3/${ctr.name}/state 277 - /run/current-system/sw/bin/nixos-container start ${ctr.name} 278 - /run/current-system/sw/bin/nixos-container run ${ctr.name} -- ${pkgs.writeDash "init" '' 279 - mkdir -p /var/state 280 - ''} 281 - '') 282 - ) cfg.containers; 283 - }) 284 - (lib.mkIf (cfg.containers != { }) { 285 - # networking 286 - 287 - boot.kernel.sysctl."net.ipv4.ip_forward" = lib.mkForce 1; 288 - systemd.network.networks.ctr0 = { 289 - name = "ctr0"; 290 - address = [ 291 - "10.233.0.1/24" 292 - ]; 293 - networkConfig = { 294 - ConfigureWithoutCarrier = true; 295 - DHCPServer = "yes"; 296 - }; 297 - dhcpServerConfig = { 298 - DNS = "9.9.9.9"; 299 - }; 300 - }; 301 - systemd.network.netdevs.ctr0.netdevConfig = { 302 - Kind = "bridge"; 303 - Name = "ctr0"; 304 - }; 305 - networking.networkmanager.unmanaged = [ "ctr0" ]; 306 - krebs.iptables.tables.filter.INPUT.rules = [ 307 - { 308 - predicate = "-i ctr0"; 309 - target = "ACCEPT"; 310 - } 311 - ]; 312 - krebs.iptables.tables.filter.FORWARD.rules = [ 313 - { 314 - predicate = "-i ctr0"; 315 - target = "ACCEPT"; 316 - } 317 - { 318 - predicate = "-o ctr0"; 319 - target = "ACCEPT"; 320 - } 321 - ]; 322 - krebs.iptables.tables.nat.POSTROUTING.rules = [ 323 - { 324 - v6 = false; 325 - predicate = "-s 10.233.0.0/24"; 326 - target = "MASQUERADE"; 327 - } 328 - ]; 329 - }) 330 - (lib.mkIf cfg.inContainer.enable { 331 - users.groups.container_sync = { }; 332 - users.users.container_sync = { 333 - group = "container_sync"; 334 - isNormalUser = true; 335 - home = "/var/lib/self"; 336 - createHome = true; 337 - openssh.authorizedKeys.keys = [ 338 - cfg.inContainer.pubkey 339 - ]; 340 - }; 341 - 342 - networking.useHostResolvConf = false; 343 - networking.useNetworkd = true; 344 - services.resolved = { 345 - enable = true; 346 - settings.Resolve.Domains = [ "~." ]; 347 - }; 348 - systemd.network = { 349 - enable = true; 350 - networks.eth0 = { 351 - matchConfig.Name = "eth0"; 352 - DHCP = "yes"; 353 - dhcpV4Config.UseDNS = true; 354 - }; 355 - }; 356 - }) 357 - ]; 358 - }
+2 -2
configs/default.nix
··· 16 16 ]; 17 17 18 18 imports = [ 19 - # Import 3modules 20 - ../3modules 19 + # Import modules 20 + ../modules 21 21 22 22 # Import stockholm krebs module 23 23 self.inputs.stockholm.nixosModules.krebs
+9
modules/default.nix
··· 1 + _: { 2 + imports = [ 3 + ./klem.nix 4 + ./screenlock.nix 5 + ./fetchWallpaper.nix 6 + ./sync-containers3.nix 7 + ./parley.nix 8 + ]; 9 + }
+85
modules/fetchWallpaper.nix
··· 1 + { 2 + config, 3 + lib, 4 + pkgs, 5 + ... 6 + }: 7 + 8 + let 9 + cfg = config.lass.fetchWallpaper; 10 + 11 + fetchWallpaperScript = pkgs.writers.writeDash "fetchWallpaper" '' 12 + set -eufx 13 + 14 + curl -v -s -o wallpaper.tmp -z wallpaper.tmp ${lib.escapeShellArg cfg.url} && cp wallpaper.tmp wallpaper 15 + feh --no-fehbg --bg-scale wallpaper 16 + ''; 17 + 18 + in 19 + { 20 + options.lass.fetchWallpaper = { 21 + enable = lib.mkEnableOption "fetch wallpaper"; 22 + url = lib.mkOption { 23 + type = lib.types.str; 24 + }; 25 + timerConfig = lib.mkOption { 26 + type = lib.types.unspecified; 27 + default = { 28 + OnCalendar = "*:00,10,20,30,40,50"; 29 + }; 30 + }; 31 + display = lib.mkOption { 32 + type = lib.types.str; 33 + default = ":${toString config.services.xserver.display}"; 34 + }; 35 + unitConfig = lib.mkOption { 36 + type = lib.types.attrsOf lib.types.str; 37 + description = "Extra unit configuration for fetchWallpaper to define conditions and assertions for the unit"; 38 + example = lib.literalExample '' 39 + # do not start when running on umts 40 + { ConditionPathExists = "!/var/run/ppp0.pid"; } 41 + ''; 42 + default = { }; 43 + }; 44 + }; 45 + 46 + config = lib.mkIf cfg.enable { 47 + systemd.timers.fetchWallpaper = { 48 + description = "fetch wallpaper timer"; 49 + wantedBy = [ "timers.target" ]; 50 + 51 + timerConfig = cfg.timerConfig; 52 + }; 53 + systemd.services.fetchWallpaper = { 54 + description = "fetch wallpaper"; 55 + after = [ "network.target" ]; 56 + 57 + path = with pkgs; [ 58 + curl 59 + feh 60 + ]; 61 + 62 + environment = { 63 + URL = cfg.url; 64 + DISPLAY = cfg.display; 65 + }; 66 + restartIfChanged = true; 67 + 68 + serviceConfig = { 69 + Type = "simple"; 70 + ExecStart = fetchWallpaperScript; 71 + StateDirectory = "wallpaper"; 72 + StateDirectoryMode = "0755"; 73 + WorkingDirectory = "/var/lib/wallpaper"; 74 + }; 75 + 76 + unitConfig = cfg.unitConfig; 77 + }; 78 + 79 + users.users.wallpaper = { 80 + isSystemUser = true; 81 + group = "wallpaper"; 82 + }; 83 + users.groups.wallpaper = { }; 84 + }; 85 + }
+95
modules/klem.nix
··· 1 + { 2 + self, 3 + config, 4 + lib, 5 + pkgs, 6 + ... 7 + }: 8 + with lib; 9 + let 10 + cfg = config.lass.klem; 11 + slib = self.inputs.stockholm.lib; 12 + in 13 + { 14 + options.lass.klem = mkOption { 15 + default = { }; 16 + type = types.attrsOf ( 17 + types.submodule ( 18 + { config, ... }: 19 + { 20 + options = { 21 + target = mkOption { 22 + default = ".*"; 23 + description = '' 24 + regex of valid targets 25 + can be shown with xclip -selection clipboard -t TARGETS 26 + the first hit is taken as target argument 27 + ''; 28 + type = types.str; 29 + }; 30 + script = mkOption { 31 + description = '' 32 + file to run if entry is selected 33 + ''; 34 + type = types.path; 35 + }; 36 + label = mkOption { 37 + default = config._module.args.name; 38 + description = '' 39 + label to show in dmenu for this script 40 + ''; 41 + type = types.str; 42 + }; 43 + }; 44 + } 45 + ) 46 + ); 47 + }; 48 + config = 49 + let 50 + klem = pkgs.writers.writeDashBin "klem" '' 51 + set -x 52 + 53 + labels="" 54 + # match filetype against patterns 55 + ${concatMapStringsSep "\n" (script: '' 56 + ${pkgs.xclip}/bin/xclip -selection clipboard -target TARGETS -out \ 57 + | ${pkgs.gnugrep}/bin/grep -q '${script.target}' 58 + if [ $? -eq 0 ]; then 59 + labels="$labels:${script.label}" 60 + fi 61 + '') (attrValues cfg)} 62 + 63 + #remove empty line, feed into dmenu 64 + script=$(echo "$labels" \ 65 + | ${pkgs.gnused}/bin/sed 's/^://;s/:/\n/g' \ 66 + | ${pkgs.dmenu}/bin/dmenu) 67 + 68 + #run the chosen script 69 + case $script in 70 + ${concatMapStringsSep "\n" ( 71 + script: 72 + slib.indent '' 73 + ${script.label}) 74 + target=$(${pkgs.xclip}/bin/xclip -selection clipboard -target TARGETS -out \ 75 + | ${pkgs.gnugrep}/bin/grep '${script.target}' \ 76 + | ${pkgs.gnugrep}/bin/grep -v TARGETS \ 77 + | ${pkgs.coreutils}/bin/head -1) 78 + ${pkgs.xclip}/bin/xclip -selection clipboard -target "$target" -out \ 79 + | ${script.script} \ 80 + | ${pkgs.xclip}/bin/xclip -selection clipboard -in 81 + ;; 82 + '' 83 + ) (attrValues cfg)} 84 + esac 85 + ''; 86 + in 87 + mkIf (cfg != { }) { 88 + environment.systemPackages = [ klem ]; 89 + nixpkgs.overlays = [ 90 + (_self: _super: { 91 + klem = klem; 92 + }) 93 + ]; 94 + }; 95 + }
+141
modules/parley.nix
··· 1 + { 2 + self, 3 + config, 4 + lib, 5 + pkgs, 6 + ... 7 + }: 8 + let 9 + cfg = config.lass.parley; 10 + generators = config.clan.core.vars.generators; 11 + in 12 + { 13 + options.lass.parley = { 14 + enable = lib.mkEnableOption "parley, federated chat that speaks plain IRC"; 15 + 16 + package = lib.mkOption { 17 + type = lib.types.package; 18 + default = self.packages.${pkgs.stdenv.hostPlatform.system}.parley; 19 + }; 20 + 21 + settings = lib.mkOption { 22 + type = lib.types.attrsOf lib.types.str; 23 + default = { }; 24 + example = { 25 + PARLEY_DOMAIN = "example.com"; 26 + PARLEY_ENDPOINT = "https://chat.example.com"; 27 + }; 28 + description = "PARLEY_* environment variables for parleyd (upstream README, Configuration)."; 29 + }; 30 + 31 + users = lib.mkOption { 32 + default = { }; 33 + description = '' 34 + Local accounts. Each gets a generated password 35 + (`clan vars get <machine> parley-user-<nick>/password`); after every 36 + start the account is created, or reset to its declared role and 37 + password. Accounts not listed here are left alone. 38 + ''; 39 + type = lib.types.attrsOf ( 40 + lib.types.submodule { 41 + options.role = lib.mkOption { 42 + type = lib.types.enum [ 43 + "admin" 44 + "user" 45 + "bot" 46 + ]; 47 + default = "user"; 48 + }; 49 + } 50 + ); 51 + }; 52 + }; 53 + 54 + config = lib.mkIf cfg.enable { 55 + lass.parley.settings = { 56 + PARLEY_DATA_DIR = "/var/lib/parley"; 57 + PARLEY_IRC_LISTEN = lib.mkDefault "127.0.0.1:6667"; 58 + PARLEY_HTTP_LISTEN = lib.mkDefault "127.0.0.1:8443"; 59 + }; 60 + 61 + clan.core.vars.generators = { 62 + parley = { 63 + # The instance key is the domain's identity: peers cache its public 64 + # half, so regenerating it is a key rotation every peer has to re-learn. 65 + files."identity.key" = { }; 66 + files."env" = { }; 67 + runtimeInputs = [ pkgs.openssl ]; 68 + script = '' 69 + # parley's key format: base64 of a 32-byte ed25519 seed 70 + openssl rand -base64 32 > "$out/identity.key" 71 + echo "PARLEY_ADMIN_TOKEN=$(openssl rand -hex 32)" > "$out/env" 72 + ''; 73 + }; 74 + } 75 + // lib.mapAttrs' ( 76 + nick: _: 77 + lib.nameValuePair "parley-user-${nick}" { 78 + files."password" = { }; 79 + runtimeInputs = [ pkgs.openssl ]; 80 + script = '' 81 + openssl rand -hex 24 > "$out/password" 82 + ''; 83 + } 84 + ) cfg.users; 85 + 86 + users.users.parley = { 87 + isSystemUser = true; 88 + group = "parley"; 89 + }; 90 + users.groups.parley = { }; 91 + 92 + environment.systemPackages = [ cfg.package ]; 93 + 94 + systemd.services.parley = { 95 + wantedBy = [ "multi-user.target" ]; 96 + after = [ "network-online.target" ]; 97 + wants = [ "network-online.target" ]; 98 + environment = cfg.settings; 99 + path = [ 100 + cfg.package 101 + pkgs.curl 102 + ]; 103 + preStart = '' 104 + install -m 0600 "$CREDENTIALS_DIRECTORY/identity.key" identity.key 105 + ''; 106 + postStart = lib.optionalString (cfg.users != { }) '' 107 + export PARLEY_ENDPOINT=http://$PARLEY_HTTP_LISTEN 108 + until curl -fs -o /dev/null "$PARLEY_ENDPOINT/healthz"; do sleep 1; done 109 + ${lib.concatStrings ( 110 + lib.mapAttrsToList (nick: user: '' 111 + pw="$CREDENTIALS_DIRECTORY/user-${nick}" 112 + parleyctl accounts update ${nick} -role ${user.role} -password-stdin < "$pw" > /dev/null \ 113 + || parleyctl accounts create ${nick} -role ${user.role} -password-stdin < "$pw" > /dev/null 114 + '') cfg.users 115 + )} 116 + ''; 117 + serviceConfig = { 118 + ExecStart = lib.getExe' cfg.package "parleyd"; 119 + User = "parley"; 120 + Group = "parley"; 121 + StateDirectory = "parley"; 122 + StateDirectoryMode = "0700"; 123 + WorkingDirectory = "/var/lib/parley"; 124 + UMask = "0077"; 125 + EnvironmentFile = generators.parley.files."env".path; 126 + LoadCredential = [ 127 + "identity.key:${generators.parley.files."identity.key".path}" 128 + ] 129 + ++ lib.mapAttrsToList ( 130 + nick: _: "user-${nick}:${generators."parley-user-${nick}".files."password".path}" 131 + ) cfg.users; 132 + Restart = "on-failure"; 133 + NoNewPrivileges = true; 134 + ProtectSystem = "strict"; 135 + ProtectHome = true; 136 + PrivateTmp = true; 137 + PrivateDevices = true; 138 + }; 139 + }; 140 + }; 141 + }
+43
modules/screenlock.nix
··· 1 + { 2 + pkgs, 3 + lib, 4 + config, 5 + ... 6 + }: 7 + let 8 + cfg = config.lass.screenlock; 9 + 10 + out = { 11 + options.lass.screenlock = api; 12 + config = lib.mkIf cfg.enable imp; 13 + }; 14 + 15 + api = { 16 + enable = lib.mkEnableOption "screenlock"; 17 + command = lib.mkOption { 18 + type = lib.types.path; 19 + default = pkgs.writers.writeDash "screenlock" '' 20 + ${pkgs.xlockmore}/bin/xlock -mode life1d -size 1 21 + sleep 3 22 + ''; 23 + }; 24 + }; 25 + 26 + imp = { 27 + systemd.services.screenlock = { 28 + before = [ "sleep.target" ]; 29 + requiredBy = [ "sleep.target" ]; 30 + environment = { 31 + DISPLAY = ":${toString config.services.xserver.display}"; 32 + }; 33 + serviceConfig = { 34 + SyslogIdentifier = "screenlock"; 35 + ExecStart = cfg.command; 36 + Type = "simple"; 37 + User = "lass"; 38 + }; 39 + }; 40 + }; 41 + 42 + in 43 + out
+358
modules/sync-containers3.nix
··· 1 + { 2 + config, 3 + lib, 4 + pkgs, 5 + ... 6 + }: 7 + let 8 + cfg = config.krebs.sync-containers3; 9 + in 10 + { 11 + options.krebs.sync-containers3 = { 12 + inContainer = { 13 + enable = lib.mkEnableOption "container config for syncing"; 14 + pubkey = lib.mkOption { 15 + type = lib.types.str; 16 + }; 17 + }; 18 + containers = lib.mkOption { 19 + default = { }; 20 + type = lib.types.attrsOf ( 21 + lib.types.submodule ( 22 + { config, ... }: 23 + { 24 + options = { 25 + name = lib.mkOption { 26 + type = lib.types.str; 27 + default = config._module.args.name; 28 + }; 29 + sshKey = lib.mkOption { 30 + type = lib.types.str; 31 + }; 32 + luksKey = lib.mkOption { 33 + type = lib.types.str; 34 + default = config.sshKey; 35 + }; 36 + ephemeral = lib.mkOption { 37 + type = lib.types.bool; 38 + default = false; 39 + }; 40 + runContainer = lib.mkOption { 41 + type = lib.types.bool; 42 + default = true; 43 + }; 44 + startCommand = lib.mkOption { 45 + type = lib.types.str; 46 + default = '' 47 + set -efu 48 + mkdir -p /var/state/var_src 49 + ln -Tfrs /var/state/var_src /var/src 50 + if test -e /var/src/nixos-config; then 51 + /run/current-system/sw/bin/nixos-rebuild -I /var/src switch || : 52 + fi 53 + ''; 54 + }; 55 + hostname = lib.mkOption { 56 + type = lib.types.str; 57 + description = '' 58 + hostname of the container, 59 + this is continuously checked by ping and the container is restarted if unreachable 60 + ''; 61 + default = config.name; 62 + }; 63 + }; 64 + } 65 + ) 66 + ); 67 + }; 68 + }; 69 + config = lib.mkMerge [ 70 + (lib.mkIf (cfg.containers != { }) { 71 + 72 + containers = lib.mapAttrs' ( 73 + _n: ctr: 74 + lib.nameValuePair ctr.name { 75 + config = { 76 + environment.systemPackages = [ 77 + pkgs.dhcpcd 78 + pkgs.git 79 + pkgs.jq 80 + ]; 81 + networking.useDHCP = lib.mkForce true; 82 + networking.useHostResolvConf = false; 83 + services.resolved.enable = true; 84 + systemd.services.autoswitch = { 85 + environment = { 86 + NIX_REMOTE = "daemon"; 87 + }; 88 + wantedBy = [ "multi-user.target" ]; 89 + serviceConfig = { 90 + ExecStart = pkgs.writers.writeDash "autoswitch" ctr.startCommand; 91 + # keep retrying transient failures (e.g. a GitHub 5xx while 92 + # resolving the flake at boot) so we self-heal instead of 93 + # staying silently stale. RestartSec is well above the default 94 + # StartLimitIntervalSec, so the restart rate limiter never trips 95 + # and we retry until it succeeds. 96 + Restart = "on-failure"; 97 + RestartSec = "120s"; 98 + }; 99 + unitConfig.X-StopOnRemoval = false; 100 + }; 101 + # get rid of stateVersion not set warning; 102 + system.stateVersion = config.system.nixos.release; 103 + }; 104 + autoStart = false; 105 + enableTun = true; 106 + ephemeral = ctr.ephemeral; 107 + privateNetwork = true; 108 + hostBridge = "ctr0"; 109 + bindMounts = { 110 + "/var/lib/self/disk" = { 111 + hostPath = "/var/lib/sync-containers3/${ctr.name}/disk"; 112 + isReadOnly = false; 113 + }; 114 + "/var/state" = { 115 + hostPath = "/var/lib/sync-containers3/${ctr.name}/state"; 116 + isReadOnly = false; 117 + }; 118 + }; 119 + } 120 + ) (lib.filterAttrs (_: ctr: ctr.runContainer) cfg.containers); 121 + 122 + systemd.services = lib.foldr lib.recursiveUpdate { } ( 123 + lib.flatten ( 124 + map (ctr: [ 125 + { 126 + "${ctr.name}_syncer" = { 127 + path = with pkgs; [ 128 + coreutils 129 + inetutils 130 + rsync 131 + openssh 132 + systemd 133 + util-linux 134 + ]; 135 + startAt = "*:0/1"; 136 + serviceConfig = { 137 + User = "${ctr.name}_container"; 138 + LoadCredential = [ 139 + "ssh_key:${ctr.sshKey}" 140 + ]; 141 + ExecCondition = pkgs.writers.writeDash "${ctr.name}_checker" '' 142 + set -efu 143 + ! systemctl is-active --quiet container@${ctr.name}.service 144 + ''; 145 + ExecStart = pkgs.writers.writeDash "${ctr.name}_syncer" '' 146 + set -efux 147 + flock -n "$HOME"/sync.lock ${pkgs.writers.writeDash "${ctr.name}-sync" '' 148 + set -efux 149 + if ping -c 1 ${ctr.hostname}; then 150 + nice --adjustment=30 rsync -a -e "ssh -i $CREDENTIALS_DIRECTORY/ssh_key" --timeout=30 --inplace --sparse container_sync@${ctr.hostname}:disk "$HOME"/disk.rsync 151 + touch "$HOME"/incomplete 152 + nice --adjustment=30 rsync --inplace "$HOME"/disk.rsync "$HOME"/disk 153 + rm -f "$HOME"/incomplete 154 + fi 155 + ''} 156 + ''; 157 + }; 158 + }; 159 + } 160 + { 161 + "${ctr.name}_watcher" = lib.mkIf ctr.runContainer { 162 + path = with pkgs; [ 163 + coreutils 164 + inetutils 165 + cryptsetup 166 + mount 167 + util-linux 168 + retry 169 + ]; 170 + serviceConfig = { 171 + ExecStart = pkgs.writers.writeDash "${ctr.name}_watcher" '' 172 + set -efux 173 + while sleep 5; do 174 + if ! $(retry -t 10 -d 10 -- ping -q -c 1 ${ctr.hostname} > /dev/null); then 175 + echo 'container seems dead, killing' 176 + break 177 + fi 178 + done 179 + /run/current-system/sw/bin/nixos-container stop ${ctr.name} || : 180 + umount /var/lib/sync-containers3/${ctr.name}/state || : 181 + cryptsetup luksClose ${ctr.name} || : 182 + ''; 183 + }; 184 + }; 185 + } 186 + { 187 + "${ctr.name}_scheduler" = lib.mkIf ctr.runContainer { 188 + wantedBy = [ "multi-user.target" ]; 189 + path = with pkgs; [ 190 + coreutils 191 + inetutils 192 + cryptsetup 193 + mount 194 + util-linux 195 + systemd 196 + retry 197 + ]; 198 + serviceConfig = { 199 + Restart = "always"; 200 + RestartSec = "30s"; 201 + ExecStart = pkgs.writers.writeBash "${ctr.name}_scheduler" '' 202 + set -efu 203 + cryptsetup luksOpen --key-file ${ctr.luksKey} /var/lib/sync-containers3/${ctr.name}/disk ${ctr.name} || : 204 + mkdir -p /var/lib/sync-containers3/${ctr.name}/state 205 + mountpoint /var/lib/sync-containers3/${ctr.name}/state || mount /dev/mapper/${ctr.name} /var/lib/sync-containers3/${ctr.name}/state 206 + /run/current-system/sw/bin/nixos-container start ${ctr.name} 207 + # wait for system to become reachable for the first time 208 + systemctl start ${ctr.name}_watcher.service 209 + retry -t 10 -d 10 -- ping -q -c 1 ${ctr.hostname} > /dev/null 210 + while systemctl is-active container@${ctr.name}.service >/dev/null && ping -q -c 3 ${ctr.hostname} >/dev/null; do 211 + sleep 10 212 + done 213 + ''; 214 + }; 215 + }; 216 + } 217 + { 218 + "container@${ctr.name}" = lib.mkIf ctr.runContainer { 219 + serviceConfig = { 220 + ExecStop = pkgs.writers.writeDash "remove_interface" '' 221 + ${pkgs.iproute2}/bin/ip link del vb-${ctr.name} 222 + ''; 223 + ExecStartPost = [ 224 + (pkgs.writers.writeDash "bind-to-bridge" '' 225 + ${pkgs.iproute2}/bin/ip link set "vb-$INSTANCE" master ctr0 226 + '') 227 + ]; 228 + }; 229 + }; 230 + } 231 + ]) (lib.attrValues cfg.containers) 232 + ) 233 + ); 234 + 235 + systemd.timers = lib.mapAttrs' ( 236 + _n: ctr: 237 + lib.nameValuePair "${ctr.name}_syncer" { 238 + timerConfig = { 239 + RandomizedDelaySec = 100; 240 + }; 241 + } 242 + ) cfg.containers; 243 + 244 + users.groups = lib.mapAttrs' ( 245 + _: ctr: 246 + lib.nameValuePair "${ctr.name}_container" { 247 + } 248 + ) cfg.containers; 249 + users.users = lib.mapAttrs' ( 250 + _: ctr: 251 + lib.nameValuePair "${ctr.name}_container" ({ 252 + group = "${ctr.name}_container"; 253 + isNormalUser = true; 254 + home = "/var/lib/sync-containers3/${ctr.name}"; 255 + createHome = true; 256 + homeMode = "705"; 257 + }) 258 + ) cfg.containers; 259 + 260 + environment.systemPackages = lib.mapAttrsToList ( 261 + _: ctr: 262 + (pkgs.writers.writeDashBin "${ctr.name}_init" '' 263 + set -efux 264 + export PATH=${ 265 + lib.makeBinPath [ 266 + pkgs.coreutils 267 + pkgs.cryptsetup 268 + pkgs.libxfs.bin 269 + ] 270 + }:$PATH 271 + truncate -s 5G /var/lib/sync-containers3/${ctr.name}/disk 272 + cryptsetup luksFormat /var/lib/sync-containers3/${ctr.name}/disk ${ctr.luksKey} 273 + cryptsetup luksOpen --key-file ${ctr.luksKey} /var/lib/sync-containers3/${ctr.name}/disk ${ctr.name} 274 + mkfs.xfs /dev/mapper/${ctr.name} 275 + mkdir -p /var/lib/sync-containers3/${ctr.name}/state 276 + mountpoint /var/lib/sync-containers3/${ctr.name}/state || mount /dev/mapper/${ctr.name} /var/lib/sync-containers3/${ctr.name}/state 277 + /run/current-system/sw/bin/nixos-container start ${ctr.name} 278 + /run/current-system/sw/bin/nixos-container run ${ctr.name} -- ${pkgs.writeDash "init" '' 279 + mkdir -p /var/state 280 + ''} 281 + '') 282 + ) cfg.containers; 283 + }) 284 + (lib.mkIf (cfg.containers != { }) { 285 + # networking 286 + 287 + boot.kernel.sysctl."net.ipv4.ip_forward" = lib.mkForce 1; 288 + systemd.network.networks.ctr0 = { 289 + name = "ctr0"; 290 + address = [ 291 + "10.233.0.1/24" 292 + ]; 293 + networkConfig = { 294 + ConfigureWithoutCarrier = true; 295 + DHCPServer = "yes"; 296 + }; 297 + dhcpServerConfig = { 298 + DNS = "9.9.9.9"; 299 + }; 300 + }; 301 + systemd.network.netdevs.ctr0.netdevConfig = { 302 + Kind = "bridge"; 303 + Name = "ctr0"; 304 + }; 305 + networking.networkmanager.unmanaged = [ "ctr0" ]; 306 + krebs.iptables.tables.filter.INPUT.rules = [ 307 + { 308 + predicate = "-i ctr0"; 309 + target = "ACCEPT"; 310 + } 311 + ]; 312 + krebs.iptables.tables.filter.FORWARD.rules = [ 313 + { 314 + predicate = "-i ctr0"; 315 + target = "ACCEPT"; 316 + } 317 + { 318 + predicate = "-o ctr0"; 319 + target = "ACCEPT"; 320 + } 321 + ]; 322 + krebs.iptables.tables.nat.POSTROUTING.rules = [ 323 + { 324 + v6 = false; 325 + predicate = "-s 10.233.0.0/24"; 326 + target = "MASQUERADE"; 327 + } 328 + ]; 329 + }) 330 + (lib.mkIf cfg.inContainer.enable { 331 + users.groups.container_sync = { }; 332 + users.users.container_sync = { 333 + group = "container_sync"; 334 + isNormalUser = true; 335 + home = "/var/lib/self"; 336 + createHome = true; 337 + openssh.authorizedKeys.keys = [ 338 + cfg.inContainer.pubkey 339 + ]; 340 + }; 341 + 342 + networking.useHostResolvConf = false; 343 + networking.useNetworkd = true; 344 + services.resolved = { 345 + enable = true; 346 + settings.Resolve.Domains = [ "~." ]; 347 + }; 348 + systemd.network = { 349 + enable = true; 350 + networks.eth0 = { 351 + matchConfig.Name = "eth0"; 352 + DHCP = "yes"; 353 + dhcpV4Config.UseDNS = true; 354 + }; 355 + }; 356 + }) 357 + ]; 358 + }