{ description = "C3D2 NixOS configurations"; inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-22.05"; nixpkgs-mobilizon.url = "github:minijackson/nixpkgs/init-mobilizon"; nixpkgs-openwebrx.url = "github:astro/nixpkgs/openwebrx"; nixos-hardware.url = "github:nixos/nixos-hardware"; fenix = { url = "github:nix-community/fenix"; inputs.nixpkgs.follows = "nixpkgs"; }; heliwatch = { url = "git+https://gitea.c3d2.de/astro/heliwatch.git"; inputs = { fenix.follows = "fenix"; nixpkgs.follows = "nixpkgs"; naersk.follows = "naersk"; }; }; hydra.url = "github:nixos/hydra"; hydra-ca.url = "github:thufschmitt/hydra/nix-ca"; microvm = { url = "github:astro/microvm.nix"; inputs.nixpkgs.follows = "nixpkgs"; }; naersk = { url = "github:nix-community/naersk"; inputs.nixpkgs.follows = "nixpkgs"; }; oparl-scraper = { url = "github:offenesdresden/ratsinfo-scraper/oparl"; flake = false; }; openwrt = { url = "git+https://git.openwrt.org/openwrt/openwrt.git?ref=openwrt-21.02"; flake = false; }; openwrt-imagebuilder = { url = "github:astro/nix-openwrt-imagebuilder"; inputs = { nixpkgs.follows = "nixpkgs"; openwrt.follows = "openwrt"; }; }; scrapers = { url = "git+https://gitea.c3d2.de/astro/scrapers.git"; flake = false; }; secrets = { url = "git+ssh://gitea@gitea.c3d2.de/c3d2-admins/secrets.git"; inputs = { nixpkgs.follows = "nixpkgs"; sops-nix.follows = "sops-nix"; }; }; sops-nix = { url = "github:Mic92/sops-nix"; inputs = { nixpkgs.follows = "nixpkgs"; }; }; spacemsg = { url = "github:astro/spacemsg"; flake = false; }; ticker = { url = "git+https://gitea.c3d2.de/astro/ticker.git"; inputs.nixpkgs.follows = "nixpkgs"; }; tigger = { url = "github:astro/tigger"; flake = false; }; yammat = { url = "git+https://gitea.c3d2.de/C3D2/yammat.git?ref=nix"; inputs.nixpkgs.follows = "nixpkgs"; }; zentralwerk = { url = "git+https://gitea.c3d2.de/zentralwerk/network.git"; inputs = { nixpkgs.follows = "nixpkgs"; nixpkgs-master.follows = "nixpkgs"; openwrt.follows = "openwrt"; openwrt-imagebuilder.follows = "openwrt-imagebuilder"; }; }; riscv64 = { url = "github:zhaofengli/nixos-riscv64"; inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = inputs@{ self, nixpkgs, secrets, nixos-hardware, zentralwerk, yammat, scrapers, spacemsg, tigger, ticker, heliwatch, sops-nix, naersk, fenix, microvm, riscv64, oparl-scraper, ... }: let inherit (nixpkgs) lib; forAllSystems = lib.genAttrs [ "aarch64-linux" "x86_64-linux" ]; extractZwHosts = { hosts4, hosts6, ... }: lib.recursiveUpdate ( builtins.foldl' (result: name: lib.recursiveUpdate result { "${name}".ip4 = hosts4."${name}"; } ) {} (builtins.attrNames hosts4) ) ( builtins.foldl' (result: ctx: builtins.foldl' (result: name: lib.recursiveUpdate result { "${name}".ip6 = hosts6."${ctx}"."${name}"; } ) result (builtins.attrNames hosts6."${ctx}") ) {} (builtins.attrNames hosts6) ); zwHostRegistry = { hosts = builtins.foldl' (result: net: lib.recursiveUpdate result (extractZwHosts zentralwerk.lib.config.site.net."${net}") ) {} [ "core" "c3d2" "serv" ]; }; extraHostRegistry.hosts = import ./host-registry.nix; hostRegistry = lib.recursiveUpdate zwHostRegistry extraHostRegistry; getHostAddr = name: let hostConf = hostRegistry.hosts."${name}"; in if hostConf ? ip6 then hostConf.ip6 else if hostConf ? ip4 then hostConf.ip4 else throw "Host ${name} has no ip4 or ip6 address"; in { overlay = import ./overlay; legacyPackages = lib.attrsets.mapAttrs (system: pkgs: pkgs.appendOverlays [ fenix.overlay naersk.overlay self.overlay ]) nixpkgs.legacyPackages; packages = lib.attrsets.mapAttrs (system: pkgs: let overlayPkgs = builtins.intersectAttrs (self.overlay {} {}) pkgs; in overlayPkgs // { list-upgradable = pkgs.writeScriptBin "list-upgradable" '' #! ${pkgs.runtimeShell} NORMAL="\033[0m" RED="\033[0;31m" YELLOW="\033[0;33m" GREEN="\033[0;32m" ${pkgs.lib.concatMapStringsSep "\n" (name: let addr = getHostAddr name; in nixpkgs.lib.optionalString (addr != null) '' echo -n -e "${name}: $RED" RUNNING=$(ssh -o PreferredAuthentications=publickey -o StrictHostKeyChecking=accept-new root@"${addr}" "readlink /run/current-system") if [ $? = 0 ] && [ -n "$RUNNING" ]; then CURRENT=$(nix eval --raw ".#nixosConfigurations.${name}.config.system.build.toplevel" 2>/dev/null) RUNNING_VER=$(basename $RUNNING|rev|cut -d - -f 1|rev) RUNNING_DATE=$(echo $RUNNING_VER|cut -d . -f 3) CURRENT_VER=$(basename $CURRENT|rev|cut -d - -f 1|rev) CURRENT_DATE=$(echo $CURRENT_VER|cut -d . -f 3) if [ "$RUNNING" = "$CURRENT" ]; then echo -e "$GREEN"current"$NORMAL $RUNNING_VER" elif [ $RUNNING_DATE -gt $CURRENT_DATE ]; then echo -e "$GREEN"newer"$NORMAL $RUNNING_VER > $CURRENT_VER" elif [ "$RUNNING_VER" = "$CURRENT_VER" ]; then echo -e "$YELLOW"modified"$NORMAL $RUNNING_VER" elif [ -n "$RUNNING_VER" ]; then echo -e "$RED"outdated"$NORMAL $RUNNING_VER < $CURRENT_VER" else echo -e "$RED"error"$NORMAL $RUNNING_VER" fi fi echo -n -e "$NORMAL" '') (builtins.attrNames self.nixosConfigurations)} ''; prebuild-all = pkgs.runCommandNoCC "prebuild-all" { preferLocalBuild = true; } '' mkdir $out ${pkgs.lib.concatMapStrings (name: '' ln -s ${self.nixosConfigurations."${name}".config.system.build.toplevel} name '') (builtins.attrNames self.nixosConfigurations)} ''; prebuild-all-remote = pkgs.writeScriptBin "prebuild-all" '' #!${pkgs.runtimeShell} -e nix copy --to ssh://$1 ${secrets} nix copy --to ssh://$1 ${self} set -x ssh $1 -- nix build -L --no-link ${ pkgs.lib.concatMapStringsSep " " (name: "${self}#nixosConfigurations.${name}.config.system.build.toplevel" ) (builtins.attrNames self.nixosConfigurations) } ''; } // builtins.foldl' (result: host: result // { # TODO: check if the ethernet address is reachable and if not, # execute wol on a machine in HQ. "${host}-wake" = pkgs.writeScriptBin "${host}-wake" '' #!${pkgs.runtimeShell} exec ${pkgs.wol}/bin/wol ${hostRegistry.hosts."${host}".ether} ''; }) {} (builtins.attrNames (nixpkgs.lib.filterAttrs (_: { wol ? false, ... }: wol) hostRegistry.hosts)) // builtins.foldl' (result: name: let host = getHostAddr name; target = ''root@"${host}"''; rebuildArg = "--flake ${self}#${name}"; hostConfig = self.nixosConfigurations."${name}".config; profile = hostConfig.system.build.toplevel; # let /var/lib/microvm/*/flake point to the flake-update branch so that # `microvm -u $NAME` updates to what hydra built today. selfRef = "git+https://gitea.c3d2.de/c3d2/nix-config?ref=flake-update"; in result // { # Generate a small script for copying this flake to the # remote machine and bulding and switching there. # Can be run with `nix run c3d2#…-nixos-rebuild switch` "${name}-nixos-rebuild" = pkgs.writeScriptBin "${name}-nixos-rebuild" '' #!${pkgs.runtimeShell} -ex [[ $(ssh ${target} cat /etc/hostname) == ${name} ]] nix copy --to ssh://${target} ${secrets} nix copy --to ssh://${target} ${self} ssh ${target} nixos-rebuild ${rebuildArg} "$@" ''; "${name}-nixos-rebuild-local" = pkgs.writeScriptBin "${name}-nixos-rebuild" '' #!${pkgs.runtimeShell} -ex [[ $(ssh ${target} cat /etc/hostname) == ${name} ]] nix copy --to ssh://${target} ${profile} ssh ${target} "${profile}/bin/switch-to-configuration $*" ''; "${name}-cleanup" = pkgs.writeScriptBin "${name}-cleanup" '' #!${pkgs.runtimeShell} -ex ssh ${target} "time nix-collect-garbage -d && time nix-store --optimise" ''; } // lib.optionalAttrs (hostConfig ? c3d2.deployment.server) { "microvm-update-${name}" = pkgs.writeScriptBin "microvm-update-${name}" '' #!${pkgs.runtimeShell} -e ${hostConfig.system.build.copyToServer} ${self} ${secrets} ${hostConfig.system.build.runOnServer} bash -e < flake [ -e old ] && nix store diff-closures ./old ./current systemctl restart microvm@${name}.service END ''; "microvm-update-${name}-local" = pkgs.writeScriptBin "microvm-update-${name}" '' #!${pkgs.runtimeShell} -e ${hostConfig.system.build.copyToServer} ${hostConfig.microvm.declaredRunner} ${hostConfig.system.build.runOnServer} bash -e < flake [ -e old ] && nix store diff-closures ./old ./current systemctl restart microvm@${name}.service END ''; }) {} (builtins.attrNames self.nixosConfigurations) // builtins.foldl' (result: host: let inherit (self.nixosConfigurations.${host}) config; in result // nixpkgs.lib.optionalAttrs (config.system.build ? vm) { # boot any machine in a microvm "${host}-vm" = (self.nixosConfigurations.${host} .extendModules { modules = [ microvm.nixosModules.microvm { microvm = { mem = nixpkgs.lib.mkForce 2048; hypervisor = nixpkgs.lib.mkForce "qemu"; socket = nixpkgs.lib.mkForce null; shares = [ { tag = "ro-store"; source = "/nix/store"; mountPoint = "/nix/.ro-store"; } ]; }; boot.isContainer = lib.mkForce false; users.users.root.password = ""; fileSystems."/".fsType = lib.mkForce "tmpfs"; services.getty.helpLine = '' Log in as "root" with an empty password. Use "reboot" to shut qemu down. ''; } ]; }) .config.microvm.declaredRunner; } // nixpkgs.lib.optionalAttrs (config.system.build ? tftproot) { "${host}-tftproot" = config.system.build.tftproot; } ) {} (builtins.attrNames self.nixosConfigurations) ) self.legacyPackages; nixosConfigurations = let nixosSystem' = # Our custom NixOS builder { nixpkgs ? inputs.nixpkgs, modules, extraArgs ? {}, specialArgs ? { }, system ? "x86_64-linux" }: nixpkgs.lib.nixosSystem { inherit specialArgs system; modules = [ ({ pkgs, ... }: { _module.args = extraArgs // { inherit hostRegistry inputs zentralwerk; }; nixpkgs = { overlays = [ self.overlay ]; }; }) self.nixosModules.c3d2 ./config/audio-server ./config/c3d2.nix ./config/stats.nix ./modules/pi-sensors.nix ] ++ modules; }; in { freifunk = nixosSystem' { modules = [ self.nixosModules.microvm ./hosts/containers/freifunk { nixpkgs.overlays = with secrets.overlays; [ freifunk ospf ]; sops.defaultSopsFile = "${secrets}/hosts/freifunk/secrets.yaml"; } ]; }; gitea = nixosSystem' { modules = [ self.nixosModules.microvm ./hosts/containers/gitea ]; }; glotzbert = nixosSystem' { modules = [ ./hosts/glotzbert nixos-hardware.nixosModules.common-cpu-intel nixos-hardware.nixosModules.common-pc-ssd secrets.nixosModules.admins { sops.defaultSopsFile = "${secrets}/hosts/glotzbert/secrets.yaml"; } ]; }; hedgedoc = nixosSystem' { modules = [ self.nixosModules.microvm { sops.defaultSopsFile = "${secrets}/hosts/hedgedoc/secrets.yaml"; } ./hosts/containers/hedgedoc ]; }; pulsebert = nixosSystem' { modules = [ ./hosts/pulsebert ]; system = "aarch64-linux"; }; radiobert = nixosSystem' { modules = [ ({ modulesPath, ... }: { nixpkgs.overlays = [ heliwatch.overlay ]; sops.defaultSopsFile = "${secrets}/hosts/radiobert/secrets.yaml"; }) ./hosts/radiobert ]; system = "aarch64-linux"; }; nncp = nixosSystem' { modules = [ self.nixosModules.microvm self.nixosModules.nncp ./hosts/containers/nncp ]; }; dacbert = nixosSystem' { modules = [ nixos-hardware.nixosModules.raspberry-pi-4 self.nixosModules.rpi-netboot ./hosts/dacbert ]; system = "aarch64-linux"; }; rpi-netboot = nixosSystem' { modules = [ { _module.args = { inherit nixpkgs; }; } nixos-hardware.nixosModules.raspberry-pi-4 self.nixosModules.rpi-netboot ./hosts/rpi-netboot ]; system = "aarch64-linux"; }; matemat = nixosSystem' { modules = [ self.nixosModules.microvm ./hosts/containers/matemat yammat.nixosModule secrets.nixosModules.admins { nixpkgs.overlays = [ secrets.overlays.matemat ]; } ]; }; scrape = nixosSystem' { modules = [ self.nixosModules.microvm ./hosts/containers/scrape { nixpkgs.overlays = [ secrets.overlays.scrape ]; _module.args = { inherit scrapers; }; } ]; }; dn42 = nixosSystem' { modules = [ self.nixosModules.microvm ./hosts/containers/dn42 { nixpkgs.overlays = [ secrets.overlays.dn42 ]; sops.defaultSopsFile = "${secrets}/hosts/dn42/secrets.yaml"; } ]; }; grafana = nixosSystem' { modules = [ self.nixosModules.microvm ./hosts/containers/grafana ]; }; hydra = nixosSystem' { modules = [ inputs.hydra.nixosModules.hydra ./hosts/hydra { sops.defaultSopsFile = "${secrets}/hosts/hydra/secrets.yaml"; } ]; specialArgs = { hydra-ca = inputs.hydra-ca; }; }; mucbot = nixosSystem' { modules = [ { _module.args = { inherit tigger; }; } self.nixosModules.microvm "${tigger}/module.nix" { nixpkgs.overlays = [ secrets.overlays.mucbot ]; } ./hosts/containers/mucbot ]; }; kibana = nixosSystem' { modules = [ ./config/lxc-container.nix ./hosts/containers/kibana ]; }; public-access-proxy = nixosSystem' { modules = [ self.nixosModules.microvm ./hosts/containers/public-access-proxy ]; }; ticker = nixosSystem' { modules = [ self.nixosModules.microvm ticker.nixosModules.ticker ./hosts/containers/ticker ]; }; spaceapi = nixosSystem' { modules = [ self.nixosModules.microvm "${spacemsg}/spaceapi/module.nix" ./hosts/containers/spaceapi ]; }; stream = nixosSystem' { modules = [ ./config/lxc-container.nix ./hosts/containers/stream ]; }; mobilizon = nixosSystem' { # TODO: pending https://github.com/NixOS/nixpkgs/pull/119132 nixpkgs = inputs.nixpkgs-mobilizon; modules = [ self.nixosModules.microvm ./hosts/containers/mobilizon ]; }; mail = nixosSystem' { modules = [ ./config/lxc-container.nix ./hosts/containers/mail ]; }; logging = nixosSystem' { modules = [ ./config/lxc-container.nix ./hosts/containers/logging ]; }; # TODO: requires config cleanup for nixos-22.05 # keycloak = nixosSystem' { # modules = [ # ./config/lxc-container.nix # ./hosts/containers/keycloak # { nixpkgs.overlays = with secrets.overlays; [ keycloak ]; } # ]; # }; c3d2-web = nixosSystem' { modules = [ { _module.args = { inherit nixpkgs; }; } self.nixosModules.microvm ./hosts/containers/c3d2-web { nixpkgs.overlays = [ secrets.overlays.c3d2-web ]; } ]; }; sdrweb = nixosSystem' { modules = [ self.nixosModules.microvm { nixpkgs.overlays = [ secrets.overlays.mucbot ]; } heliwatch.nixosModules.heliwatch ./hosts/containers/sdrweb ]; }; bind = nixosSystem' { modules = [ { nixpkgs.overlays = with secrets.overlays; [ bind ]; } self.nixosModules.microvm ./hosts/containers/bind ]; }; jabber = nixosSystem' { modules = [ { nixpkgs.overlays = with secrets.overlays; [ jabber ]; } self.nixosModules.microvm ./hosts/containers/jabber ]; }; blogs = nixosSystem' { modules = [ self.nixosModules.plume self.nixosModules.microvm ./hosts/containers/blogs { sops.defaultSopsFile = "${secrets}/hosts/blogs/secrets.yaml"; } ]; }; server9 = nixosSystem' { modules = [ ./hosts/server9 microvm.nixosModules.host self.nixosModules.cluster-network { _module.args = { inherit self; }; } ]; }; server10 = nixosSystem' { modules = [ ./hosts/server10 microvm.nixosModules.host self.nixosModules.cluster-network { _module.args = { inherit self; }; } ]; }; oparl = nixosSystem' { modules = [ self.nixosModules.microvm ./hosts/containers/oparl { _module.args = { inherit oparl-scraper; }; sops.defaultSopsFile = "${secrets}/hosts/oparl/secrets.yaml"; } ]; }; leon = nixosSystem' { modules = [ self.nixosModules.microvm ./hosts/containers/leon ]; }; nfsroot = nixosSystem' { modules = [ self.nixosModules.microvm ./hosts/containers/nfsroot { _module.args.tftproots = nixpkgs.lib.filterAttrs (name: _: builtins.match ".+-tftproot" name != null ) self.packages.x86_64-linux; } ]; }; riscbert = nixosSystem' { modules = [ riscv64.nixosModules.visionfive ./hosts/riscbert ]; system = "riscv64-linux"; }; direkthilfe = nixosSystem' { modules = [ self.nixosModules.microvm ./hosts/containers/direkthilfe ]; }; mediawiki = nixosSystem' { modules = [ self.nixosModules.microvm ./hosts/containers/mediawiki ]; }; }; nixosModule = self.nixosModules.c3d2; nixosModules = { c3d2 = { imports = [ sops-nix.nixosModule ./modules/c3d2.nix ./modules/nncp.nix ./modules/autoupdate.nix ]; c3d2.hosts = hostRegistry.hosts; c3d2.users = import ./users.nix; c3d2.nncp.neigh = import ./config/nncp-relays.nix; }; cluster-network = ./modules/cluster-network.nix; microvm.imports = [ microvm.nixosModules.microvm ./modules/microvm.nix ]; nncp = ./modules/nncp.nix; plume = { imports = [ ./modules/plume.nix ]; nixpkgs.overlays = [ fenix.overlay naersk.overlay ]; }; rpi-netboot = ./modules/rpi-netboot.nix; }; hydraJobs = builtins.mapAttrs (_: nixpkgs.lib.hydraJob) ( builtins.mapAttrs (_: nixosSystem: if nixosSystem.config ? microvm.declaredRunner then nixosSystem.config.microvm.declaredRunner else nixosSystem.config.system.build.toplevel ) self.nixosConfigurations // nixpkgs.lib.filterAttrs (name: _: builtins.match ".+-tftproot" name != null ) self.packages.aarch64-linux ); }; }