commit 77a28951195e1139dc25f7f4019901ca62c9c810 Author: Mikael Voss Date: Sun Jul 21 16:55:31 2024 +0200 Initial import diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4b8b578 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Hidden files +.* +!.git* + +# Nix +/result diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..444bc43 --- /dev/null +++ b/flake.lock @@ -0,0 +1,111 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flakey-profile": { + "locked": { + "lastModified": 1712898590, + "narHash": "sha256-FhGIEU93VHAChKEXx905TSiPZKga69bWl1VB37FK//I=", + "owner": "lf-", + "repo": "flakey-profile", + "rev": "243c903fd8eadc0f63d205665a92d4df91d42d9d", + "type": "github" + }, + "original": { + "owner": "lf-", + "repo": "flakey-profile", + "type": "github" + } + }, + "lix": { + "flake": false, + "locked": { + "lastModified": 1720626042, + "narHash": "sha256-f8k+BezKdJfmE+k7zgBJiohtS3VkkriycdXYsKOm3sc=", + "rev": "2a4376be20d70feaa2b0e640c5041fb66ddc67ed", + "type": "tarball", + "url": "https://git.lix.systems/api/v1/repos/lix-project/lix/archive/2a4376be20d70feaa2b0e640c5041fb66ddc67ed.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://git.lix.systems/lix-project/lix/archive/2.90.0.tar.gz" + } + }, + "lix-module": { + "inputs": { + "flake-utils": "flake-utils", + "flakey-profile": "flakey-profile", + "lix": "lix", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1720641669, + "narHash": "sha256-yEO2cGNgzm9x/XxiDQI+WckSWnZX63R8aJLBRSXtYNE=", + "rev": "5c48c833c15bb80d127a398a8c2484d42fdd8257", + "type": "tarball", + "url": "https://git.lix.systems/api/v1/repos/lix-project/nixos-module/archive/5c48c833c15bb80d127a398a8c2484d42fdd8257.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://git.lix.systems/lix-project/nixos-module/archive/2.90.0.tar.gz" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1721379653, + "narHash": "sha256-8MUgifkJ7lkZs3u99UDZMB4kbOxvMEXQZ31FO3SopZ0=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "1d9c2c9b3e71b9ee663d11c5d298727dace8d374", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "lix-module": "lix-module", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..97dec9a --- /dev/null +++ b/flake.nix @@ -0,0 +1,75 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + + lix-module = { + url = "https://git.lix.systems/lix-project/nixos-module/archive/2.90.0.tar.gz"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + nixConfig = { + extra-substituters = [ + "https://cache.kyouma.net" + ]; + + extra-trusted-public-keys = [ + "cache.kyouma.net:Frjwu4q1rnwE/MnSTmX9yx86GNA/z3p/oElGvucLiZg=" + ]; + }; + + outputs = { self, nixpkgs, lix-module, ... }@inputs: + let + inherit (nixpkgs) lib; + + platforms = builtins.mapAttrs + (_: platform: lib.systems.elaborate platform) + (import ./platforms.nix); + + stdenv = import ./stdenv.nix; + + modules = lib.attrNames (builtins.readDir ./modules); + modName = lib.removeSuffix ".nix"; + in { + packages = builtins.mapAttrs (_: platform: + let + inherit (self.legacyPackages.${platform.system}.pkgs) callPackage; + in { + locale-archive-stub = callPackage ./pkgs/locale-archive-stub.nix { }; + }) platforms; + + legacyPackages = builtins.mapAttrs (name: platform: + import nixpkgs { + localSystem = builtins.currentSystem or platform.system; + crossSystem = platform; + overlays = [ + self.overlays.default + lix-module.overlays.default + ]; + + config = { + allowUnfree = true; + allowUnsupportedSystem = true; + replaceStdenv = stdenv; + replaceCrossStdenv = stdenv; + }; + }) platforms; + + overlays.default = import ./overlay.nix; + + nixosModules = builtins.listToAttrs + (builtins.map (mod: { name = modName mod; value = import ./modules/${mod}; }) modules) + // { default = { ... }: { imports = map (mod: self.nixosModules.${modName mod}) modules; }; }; + + nixosConfigurations = builtins.mapAttrs (_: platform: lib.nixosSystem { + specialArgs = { pkgs = self.legacyPackages.${platform.system}; }; + modules = [ self.nixosModules.default ./nixos.nix ]; + }) platforms; + + hydraJobs = { + nixosConfigurations = builtins.mapAttrs + (_: host: host.config.system.build.vm) + self.nixosConfigurations; + }; + }; +} diff --git a/modules/btrfs.nix b/modules/btrfs.nix new file mode 100644 index 0000000..8262244 --- /dev/null +++ b/modules/btrfs.nix @@ -0,0 +1,94 @@ +{ config, lib, pkgs, ... }: + +let + inherit (lib) + optionalString + + mkOption + types; + + cfg = config.idiosyn.btrfs; + + btrfs-scrub = pkgs.writeShellApplication { + name = "btrfs-scrub"; + runtimeInputs = with pkgs; [ util-linux btrfs-progs ]; + text = '' + findmnt --noheadings --output "SOURCE" --types btrfs --nofsroot | sort -u \ + | xargs -n 1 -P "$(nproc)" -r btrfs scrub start -B + ''; + }; + + btrfs-balance = pkgs.writeShellApplication { + name = "btrfs-balance"; + runtimeInputs = with pkgs; [ util-linux btrfs-progs ]; + text = '' + findmnt --types btrfs --output "SOURCE" --nofsroot --noheading | sort -u \ + | xargs -n 1 -r findmnt --first-only --noheadings --output "TARGET" --types btrfs --source \ + | xargs -n 1 -P "$(nproc)" -r btrfs balance start${optionalString (cfg.balance.dataUsage != null) " -dusage=${toString cfg.balance.dataUsage}"}${optionalString (cfg.balance.metadataUsage != null) " -musage=${toString cfg.balance.metadataUsage}"} + ''; + }; +in { + options = { + idiosyn.btrfs = { + scrub.timer = mkOption { + type = with types; nullOr nonEmptyStr; + default = null; + example = "weekly"; + description = '' + Realtime (wallclock) timer for regular scrubs + + See {manpage}systemd.time(7). + ''; + }; + + balance = { + timer = mkOption { + type = with types; nullOr nonEmptyStr; + default = null; + example = "weekly"; + description = '' + Realtime (wallclock) timer for regular balances. + + See {manpage}systemd.time(7). + ''; + }; + + dataUsage = mkOption { + type = with types; nullOr (ints.between 0 100); + default = 10; + description = '' + Balance only data block groups with usage below the given percentage. + ''; + }; + + metadataUsage = mkOption { + type = with types; nullOr (ints.between 0 100); + default = 5; + description = '' + Balance only metadata block groups with usage below the given percentage. + ''; + }; + }; + }; + }; + + config = { + systemd.services.btrfs-scrub = lib.mkIf (cfg.scrub.timer != null) { + startAt = cfg.scrub.timer; + unitConfig.ConditionACPower = true; + serviceConfig = { + Type = "exec"; + ExecStart = "${btrfs-scrub}/bin/btrfs-scrub"; + }; + }; + + systemd.services.btrfs-balance = lib.mkIf (cfg.balance.timer != null) { + startAt = cfg.balance.timer; + unitConfig.ConditionACPower = true; + serviceConfig = { + Type = "exec"; + ExecStart = "${btrfs-balance}/bin/btrfs-balance"; + }; + }; + }; +} diff --git a/modules/datacow.nix b/modules/datacow.nix new file mode 100644 index 0000000..66e7e86 --- /dev/null +++ b/modules/datacow.nix @@ -0,0 +1,16 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.services; + + noDataCow = dir: '' + mkdir -p ${lib.escapeShellArg dir} + ${pkgs.e2fsprogs}/bin/chattr +C ${lib.escapeShellArg dir} + ''; +in { + config = { + systemd.services.mysql.preStart = lib.mkIf cfg.mysql.enable + (lib.mkBefore (noDataCow cfg.mysql.dataDir)); + systemd.services.postgresql.preStart = lib.mkIf cfg.postgresql.enable + (lib.mkBefore (noDataCow cfg.postgresql.dataDir)); + }; +} diff --git a/modules/ephemeral.nix b/modules/ephemeral.nix new file mode 100644 index 0000000..d9ae340 --- /dev/null +++ b/modules/ephemeral.nix @@ -0,0 +1,210 @@ +{ config, pkgs, lib, ...}: + +with lib; let + cfg = config.idiosyn.ephemeral; + + device = mkOption { + type = types.nonEmptyStr; + description = mdDoc "Device to mount."; + }; + + options = mkOption { + type = with types; listOf nonEmptyStr; + readOnly = true; + description = mdDoc "Options used to mount the file system."; + }; + + extraOptions = mkOption { + type = with types; listOf nonEmptyStr; + default = [ ]; + description = mdDoc "Additional options used to mount the file system."; + }; + + filesystem = { + options = { + inherit device options extraOptions; + fsType = mkOption { + type = types.nonEmptyStr; + description = mdDoc "Type of the file system."; + }; + }; + }; + + tmpfs = { + options = { + inherit options extraOptions; + name = mkOption { + type = types.nonEmptyStr; + default = "none"; + description = mdDoc "Name of the file system."; + }; + + size = mkOption { + type = with types; either nonEmptyStr ints.positive; + description = mdDoc "Size of the file system."; + }; + + mode = mkOption { + type = types.nonEmptyStr; + description = mdDoc "Initial permissions of the root directory."; + }; + + uid = mkOption { + type = with types; either nonEmptyStr ints.unsigned; + default = 0; + description = mdDom "Initial user ID of the root directory"; + }; + + gid = mkOption { + type = with types; either nonEmptyStr ints.unsigned; + default = 0; + description = mdDom "Initial group ID of the root directory"; + }; + }; + }; + + subvol = { + options = { + inherit options extraOptions; + subvolume = mkOption { + type = with types; nullOr nonEmptyStr; + default = null; + description = mdDoc "Source path of the subvolume."; + }; + }; + }; + + ephemeralDefaults = { + "/" = { + size = "256m"; + mode = "755"; + uid = 0; + gid = 0; + options = [ "nodev" "noexec" "nosuid" ]; + extraOptions = [ ]; + }; + "/run/nix" = { + size = "80%"; + mode = "1775"; + uid = 0; + gid = "nixbld"; + options = [ "nodev" "nosuid" ]; + extraOptions = [ ]; + }; + "/tmp" = { + size = "256m"; + mode = "1777"; + uid = 0; + gid = 0; + options = [ "nodev" "noexec" "nosuid" ]; + extraOptions = [ ]; + }; + }; + + subvolumeDefaults = { + "/etc/keys" = { + options = [ "nodev" "noexec" "nosuid" ]; + extraOptions = [ "noatime" "compress=zstd" ]; + }; + "/nix" = { + options = [ "nodev" "nosuid" ]; + extraOptions = [ "noatime" "compress=zstd" ]; + }; + "/var" = { + options = [ "nodev" "noexec" "nosuid" ]; + extraOptions = [ "noatime" "compress=zstd" ]; + }; + }; +in { + options = { + idiosyn.ephemeral = { + enable = mkEnableOption "ephemeral filesystem"; + + device = mkOption { + type = types.nonEmptyStr; + description = mdDoc "Persistent btrfs device."; + }; + + boot = { + inherit device; + + fsType = mkOption { + type = types.nonEmptyStr; + }; + + options = mkOption { + inherit (options) type readOnly description; + default = [ "nodev" "noexec" "nosuid" ] + ++ optionals (cfg.boot.fsType == "vfat") [ "fmask=0137" "dmask=022" ] + ++ optionals (builtins.match "ext[34]" cfg.boot.fsType != null) [ "data=journal" ]; + }; + + extraOptions = mkOption { + inherit (extraOptions) type description; + default = [ "noatime" ]; + }; + }; + + ephemeral = mkOption { + type = with types; attrsOf (submodule tmpfs); + description = mdDoc "Ephemeral filesystems."; + default = ephemeralDefaults; + }; + + subvolumes = mkOption { + type = with types; attrsOf (submodule subvol); + description = mdDoc "Persistent subvolumes."; + default = subvolumeDefaults; + example = { + "/home" = { + options = [ "nodev" "noexec" "nosuid" ]; + extraOptions = [ "noatime" "compress=zstd" ]; + }; + }; + }; + }; + }; + + config = mkIf cfg.enable { + boot.initrd.availableKernelModules = [ "zstd" ]; + boot.supportedFilesystems = [ "btrfs" ]; + + environment.etc.machine-id.source = mkDefault "/etc/keys/machine-id"; + environment.etc.secureboot.source = mkDefault "/etc/keys/secureboot"; + + fileSystems = { + "/boot" = mkOverride 99 { + device = cfg.boot.device; + fsType = cfg.boot.fsType; + options = cfg.boot.options ++ cfg.boot.extraOptions; + }; + } // + (mapAttrs (key: val: mkOverride 99 { + fsType = "tmpfs"; + options = val.options ++ val.extraOptions + ++ [ + "strictatime" + "size=${toString val.size}" + "mode=${val.mode}" + "uid=${toString val.uid}" + "gid=${toString val.gid}" + "huge=within_size" + ]; + }) (ephemeralDefaults // cfg.ephemeral)) // + (mapAttrs (key: val: mkOverride 99 { + device = cfg.device; + fsType = "btrfs"; + options = val.options ++ val.extraOptions + ++ [ "subvol=${if (val ? subvolume && val.subvolume != null) then val.subvolume else key}" ]; + neededForBoot = true; + }) (subvolumeDefaults // cfg.subvolumes)); + + #nix.settings.build-dir = mkDefault "/run/nix"; + systemd.services.nix-daemon.environment.TMPDIR = mkDefault "/run/nix"; + + systemd.tmpfiles.rules = [ + "d /etc/keys 0751 root root" + "D /run/nix 1775 root nixbld 1d" + ]; + }; +} diff --git a/modules/iosched.nix b/modules/iosched.nix new file mode 100644 index 0000000..3f82a15 --- /dev/null +++ b/modules/iosched.nix @@ -0,0 +1,118 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.hardware.block; + escape = lib.strings.escape [ ''"'' ]; + + inherit (lib) + mkIf + mkOption + types + + concatStrings + mapAttrsToList + optionalString; +in { + options.hardware.block = { + defaultScheduler = mkOption { + type = with types; nullOr nonEmptyStr; + default = null; + description = '' + Default block I/O scheduler. + + Unless `null`, the value is assigned through a udev rule matching all + block devices. + ''; + example = "kyber"; + }; + + defaultSchedulerRotational = mkOption { + type = with types; nullOr nonEmptyStr; + default = null; + description = '' + Default block I/O scheduler for rotational drives (e.g. hard disks). + + Unless `null`, the value is assigned through a udev rule matching all + rotational block devices. + + This option takes precedence over + {option}`config.hardware.block.defaultScheduler`. + ''; + example = "bfq"; + }; + + scheduler = mkOption { + type = with types; attrsOf nonEmptyStr; + default = { }; + description = '' + Assign block I/O scheduler by device name pattern. + + Names are matched using the {manpage}`udev(7)` pattern syntax: + + `*` + : Matches zero or more characters. + + `?` + : Matches any single character. + + `[]` + : Matches any single character specified in the brackets. Ranges are + supported via the `-` character. + + `|` + : Separates alternative patterns. + + + Please note that overlapping patterns may produce unexpected results. + More complex configurations requiring these should instead be specified + directly through custom udev rules, for example via + [{option}`config.services.udev.extraRules`](#opt-services.udev.extraRules), + to ensure correct ordering. + + Available schedulers depend on the kernel configuration but modern + Linux systems typically support: + + `none` + : No‐operation scheduler with no re‐ordering of requests. Suitable + for devices with fast random I/O such as NVMe SSDs. + + [`mq-deadline`](https://www.kernel.org/doc/html/latest/block/deadline-iosched.html) + : Simple latency‐oriented general‐purpose scheduler. + + [`kyber`](https://www.kernel.org/doc/html/latest/block/kyber-iosched.html) + : Simple latency‐oriented scheduler for fast multi‐queue devices + like NVMe SSDs. + + [`bfq`](https://www.kernel.org/doc/html/latest/block/bfq-iosched.html) + : Complex fairness‐oriented scheduler. Higher processing overhead, + but good interactive response, especially with slower devices. + + + Schedulers assigned through this option take precedence over + {option}`config.hardware.block.defaultScheduler` and + {option}`config.hardware.block.defaultSchedulerRotational` but may be + overridden by other udev rules. + ''; + example = { + "mmcblk[0-9]*" = "bfq"; + "nvme[0-9]*" = "kyber"; + }; + }; + }; + + config = mkIf (cfg.defaultScheduler != null || + cfg.defaultSchedulerRotational != null || cfg.scheduler != { }) { + services.udev.packages = [ + (pkgs.writeTextDir "etc/udev/rules.d/98-block-io-scheduler.rules" + (optionalString (cfg.defaultScheduler != null) '' + SUBSYSTEM=="block", ACTION=="add|change", TEST=="queue/scheduler", ATTR{queue/scheduler}="${escape cfg.defaultScheduler}" + '' + optionalString (cfg.defaultSchedulerRotational != null) '' + SUBSYSTEM=="block", ACTION=="add|change", ATTR{queue/rotational}=="1", TEST=="queue/scheduler", ATTR{queue/scheduler}="${escape cfg.defaultSchedulerRotational}" + '' + concatStrings (mapAttrsToList (name: sched: '' + SUBSYSTEM=="block", ACTION=="add|change", KERNEL=="${escape name}", ATTR{queue/scheduler}="${escape sched}" + '') cfg.scheduler))) + ]; + }; + + meta.maintainers = with lib.maintainers; [ mvs ]; +} diff --git a/modules/musl.nix b/modules/musl.nix new file mode 100644 index 0000000..0efe098 --- /dev/null +++ b/modules/musl.nix @@ -0,0 +1,19 @@ +{ config, lib, pkgs, modulesPath, ... }: { + disabledModules = [ + (modulesPath + "/config/ldso.nix") + (modulesPath + "/config/stub-ld.nix") + (modulesPath + "/programs/nix-ld.nix") + ]; + + config = lib.mkIf pkgs.stdenv.hostPlatform.isMusl { + i18n.glibcLocales = lib.mkDefault (pkgs.callPackage ../pkgs/locale-archive-stub.nix { }); + i18n.supportedLocales = lib.mkDefault [ ]; + + programs.command-not-found.enable = lib.mkDefault false; + programs.less.lessopen = lib.mkDefault null; + + security.pam.services.login.updateWtmp = lib.mkForce false; + services.nscd.enable = lib.mkForce false; + system.nssModules = lib.mkForce [ ]; + }; +} diff --git a/modules/network.nix b/modules/network.nix new file mode 100644 index 0000000..a00b1e4 --- /dev/null +++ b/modules/network.nix @@ -0,0 +1,48 @@ +{ lib, ... }: { + networking.nameservers = lib.mkDefault [ + "[2a05:f480:1800:d2e::1]:853#resolve.solitary.social" + "80.240.30.163:853#resolve.solitary.social" + "[2a01:4f8:1c0c:6c89::1]:853#resolve.nyantec.com" + "116.203.220.161:853#resolve.nyantec.com" + ]; + + networking.nftables.enable = lib.mkDefault true; + networking.useNetworkd = lib.mkDefault true; + + services.resolved = { + enable = lib.mkDefault true; + dnsovertls = lib.mkDefault "true"; + dnssec = lib.mkDefault "true"; + }; + + systemd.network.networks."98-ethernet-default-dhcp" = { + matchConfig.Type = "ether"; + matchConfig.Name = "en*"; + + DHCP = lib.mkDefault "yes"; + dhcpV4Config.UseDNS = lib.mkDefault false; + dhcpV6Config.UseDNS = lib.mkDefault false; + ipv6AcceptRAConfig.Token = lib.mkDefault "prefixstable"; + + fairQueueingConfig.Pacing = lib.mkDefault true; + }; + + systemd.network.networks."98-wireless-client-dhcp" = { + matchConfig.Type = "wlan"; + matchConfig.WLANInterfaceType = "station"; + + DHCP = lib.mkDefault "yes"; + dhcpV4Config.UseDNS = lib.mkDefault false; + dhcpV4Config.RouteMetric = lib.mkDefault 1025; + dhcpV6Config.UseDNS = lib.mkDefault false; + ipv6AcceptRAConfig.Token = lib.mkDefault "prefixstable"; + ipv6AcceptRAConfig.RouteMetric = lib.mkDefault 1025; + + cakeConfig = { + Bandwidth = lib.mkDefault "100M"; + AutoRateIngress = lib.mkDefault true; + UseRawPacketSize = lib.mkDefault false; + PriorityQueueingPreset = lib.mkDefault "diffserv4"; + }; + }; +} diff --git a/modules/nix.nix b/modules/nix.nix new file mode 100644 index 0000000..3643658 --- /dev/null +++ b/modules/nix.nix @@ -0,0 +1,39 @@ +{ lib, pkgs, ... }: { + config = { + nix = { + channel.enable = lib.mkDefault false; + + daemonCPUSchedPolicy = lib.mkDefault "batch"; + daemonIOSchedClass = lib.mkDefault "best-effort"; + daemonIOSchedPriority = lib.mkDefault 7; + + gc = { + automatic = lib.mkDefault true; + dates = lib.mkDefault "weekly"; + randomizedDelaySec = lib.mkDefault "24h"; + options = lib.mkDefault "--delete-older-than 14d"; + }; + + settings = { + experimental-features = lib.mkDefault [ + "cgroups" + "nix-command" + "flakes" + "repl-flake" + ]; + + allowed-users = lib.mkDefault [ "@users" ]; + trusted-users = lib.mkDefault [ "@wheel" ]; + + builders-use-substitutes = lib.mkDefault true; + download-attempts = lib.mkDefault 8; + http-connections = lib.mkDefault 128; + max-substitution-jobs = lib.mkDefault 128; + preallocate-contents = lib.mkDefault true; + use-cgroups = lib.mkIf pkgs.stdenv.hostPlatform.isLinux (lib.mkDefault true); + use-xdg-base-directories = lib.mkDefault true; + }; + }; + }; + +} diff --git a/modules/ocsp.nix b/modules/ocsp.nix new file mode 100644 index 0000000..d280f50 --- /dev/null +++ b/modules/ocsp.nix @@ -0,0 +1,85 @@ +{ config, pkgs, lib, ...}: + +with lib; let + cfg = config.security.acme; + + script = pkgs.writeShellApplication { + name = "ocsp-query"; + runtimeInputs = with pkgs; [ openssl ]; + text = '' + cd "$1" + + tmp="$(mktemp ocsp.der.XXXXXXXXXX)" + trap 'rm -f "$tmp"' EXIT TERM + + url="$(openssl x509 -in cert.pem -noout -ocsp_uri)" + openssl ocsp -issuer chain.pem -cert cert.pem -url "$url" -respout "$tmp" + + chown "$(id -u):$(id -g)" "$tmp" + chmod 644 "$tmp" + mv "$tmp" ocsp.der + + ln -s -f ocsp.der full.ocsp + ''; + }; +in { + options.security.acme.ocspTimer = mkOption { + type = with types; nullOr nonEmptyStr; + default = "daily"; + description = mdDoc "Realtime (wall clock) timer for regular OCSP queries."; + }; + + config = mkIf (cfg.ocspTimer != null) { + systemd.services = mapAttrs' (cert: conf: nameValuePair "ocsp-${cert}" { + description = "Query OCSP endpoint for ${cert}"; + after = [ "network.target" "network-online.target" "acme-${cert}.service" ]; + wants = [ "network.target" "network-online.target" "acme-${cert}.service" ]; + + confinement.enable = true; + confinement.packages = with pkgs; [ openssl ]; + + serviceConfig = { + Type = "oneshot"; + + User = "acme"; + Group = conf.group; + UMask = "0022"; + + BindPaths = [ conf.directory ]; + + ExecStart = "${script}/bin/ocsp-query ${escapeShellArg conf.directory}"; + + ProtectProc = "noaccess"; + ProcSubset = "pid"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + PrivateIPC = true; + ProtectHostname = true; + ProtectClock = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + RestrictAddressFamilies = ["AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + LockPersonality = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RemoveIPC = true; + CapabilityBoundingSet = null; + NoNewPrivileges = true; + SystemCallFilter = [ "@system-service" "~@privileged" "@chown" ]; + SystemCallArchitectures = "native"; + DeviceAllow = null; + DevicePolicy = "closed"; + SocketBindDeny = "any"; + }; + }) cfg.certs; + + systemd.timers = mapAttrs' (cert: conf: nameValuePair "ocsp-${cert}" { + description = "Query OCSP endpoint for ${cert} regularly"; + timerConfig.OnCalendar = cfg.ocspTimer; + }) cfg.certs; + }; +} diff --git a/modules/openssh.nix b/modules/openssh.nix new file mode 100644 index 0000000..ffb997d --- /dev/null +++ b/modules/openssh.nix @@ -0,0 +1,57 @@ +{ lib, ...}: + +let + ciphers = [ + "chacha20-poly1305@openssh.com" + "aes256-gcm@openssh.com" + "aes128-gcm@openssh.com" + ]; + + sigAlgorithms = [ + "ssh-ed25519-cert-v01@openssh.com" + "ssh-ed25519" + ]; + + kexAlgorithms = [ + "sntrup761x25519-sha512@openssh.com" + "curve25519-sha256" + "curve25519-sha256@libssh.org" + ]; + + macs = [ + "umac-128-etm@openssh.com" + "hmac-sha2-512-etm@openssh.com" + "hmac-sha2-256-etm@openssh.com" + ]; +in { + programs.ssh = { + ciphers = lib.mkDefault ciphers; + kexAlgorithms = lib.mkDefault ciphers; + macs = lib.mkDefault macs; + hostKeyAlgorithms = lib.mkDefault sigAlgorithms; + pubkeyAcceptedKeyTypes = lib.mkDefault sigAlgorithms; + setXAuthLocation = lib.mkDefault false; + }; + + services.openssh = { + settings = { + PermitRootLogin = lib.mkDefault "no"; + + PasswordAuthentication = lib.mkDefault false; + KbdInteractiveAuthentication = lib.mkDefault false; + AuthenticationMethods = lib.mkDefault "publickey"; + + Ciphers = lib.mkDefault ciphers; + Macs = lib.mkDefault macs; + + KexAlgorithms = lib.mkDefault kexAlgorithms; + HostKeyAlgorithms = lib.mkDefault (lib.concatStringsSep "," sigAlgorithms); + PubkeyAcceptedAlgorithms = lib.mkDefault (lib.concatStringsSep "," sigAlgorithms); + + # Remove stale Unix sockets when forwarding + StreamLocalBindUnlink = lib.mkDefault true; + + ClientAliveInterval = lib.mkDefault 900; + }; + }; +} diff --git a/modules/prefs.nix b/modules/prefs.nix new file mode 100644 index 0000000..2d67b75 --- /dev/null +++ b/modules/prefs.nix @@ -0,0 +1,5 @@ +{ lib, ... }: { + config = { + services.dbus.implementation = lib.mkDefault "broker"; + }; +} diff --git a/nixos.nix b/nixos.nix new file mode 100644 index 0000000..79d9a13 --- /dev/null +++ b/nixos.nix @@ -0,0 +1,20 @@ +{ pkgs, ... }: { + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + fileSystems."/".label = "nixos"; + + nixpkgs = { inherit pkgs; }; + + security.sudo.wheelNeedsPassword = false; + services.getty.autologinUser = "nixos"; + + users.users.nixos = { + isNormalUser = true; + extraGroups = [ "wheel" ]; + }; + + system.stateVersion = "24.11"; + + virtualisation.vmVariant.virtualisation.diskImage = null; +} diff --git a/overlay.nix b/overlay.nix new file mode 100644 index 0000000..f3f1972 --- /dev/null +++ b/overlay.nix @@ -0,0 +1,54 @@ +final: prev: +let + inherit (final) lib callPackage; + + alpine-patches = callPackage ./pkgs/alpine-patches.nix { }; + gentoo-patches = callPackage ./pkgs/gentoo-patches.nix { }; +in { + keyutils = prev.keyutils.overrideAttrs (base: { + buildFlags = base.buildFlags or [ ] + ++ lib.optional final.keyutils.stdenv.cc.bintools.isLLVM + "LDFLAGS=-Wl,--undefined-version"; + }); + + cpp-utilities = prev.cpp-utilities.overrideAttrs (base: { + buildInputs = [ final.boost ]; + }); + + diffutils = prev.diffutils.overrideAttrs (_: { + # getopt tests fail + doCheck = false; + }); + + dpkg = prev.dpkg.overrideAttrs (_: { + postPatch = '' + patchShebangs . + + substituteInPlace lib/dpkg/dpkg.h \ + --replace '"dpkg-deb"' \"$out/bin/dpkg-deb\" \ + --replace '"dpkg-split"' \"$out/bin/dpkg-split\" \ + --replace '"dpkg-query"' \"$out/bin/dpkg-query\" \ + --replace '"dpkg-divert"' \"$out/bin/dpkg-divert\" \ + --replace '"dpkg-statoverride"' \"$out/bin/dpkg-statoverride\" \ + --replace '"dpkg-trigger"' \"$out/bin/dpkg-trigger\" \ + --replace '"dpkg"' \"$out/bin/dpkg\" \ + --replace '"debsig-verify"' \"$out/bin/debsig-verify\" \ + --replace '"rm"' \"${final.coreutils}/bin/rm\" \ + --replace '"cat"' \"${final.coreutils}/bin/cat\" \ + --replace '"diff"' \"${final.diffutils}/bin/diff\" \ + --replace '"ldconfig"' \"${final.coreutils}/bin/true\" + ''; + }).override { + glibc = null; + }; + + jemalloc = prev.jemalloc.override { + stdenv = final.gccStdenv; + }; + + time = prev.time.overrideAttrs (base: { + patches = base.patches or [ ] + ++ lib.optional final.time.stdenv.cc.isClang + "${gentoo-patches}/sys-process/time/files/time-1.9-implicit-func-decl-clang.patch"; + }); +} diff --git a/pkgs/alpine-patches.nix b/pkgs/alpine-patches.nix new file mode 100644 index 0000000..3ae668f --- /dev/null +++ b/pkgs/alpine-patches.nix @@ -0,0 +1,13 @@ +{ fetchGitLab, lib }: + +fetchGitLab { + domain = "gitlab.alpineline.org"; + owner = "alpine"; + repo = "aports"; + rev = "3.20-stable"; + + useFetchGit = true; + sparseCheckout = [ "*.patch" ]; + nonConeMode = true; + hash = lib.fakeFash; +} diff --git a/pkgs/gentoo-patches.nix b/pkgs/gentoo-patches.nix new file mode 100644 index 0000000..1b5f104 --- /dev/null +++ b/pkgs/gentoo-patches.nix @@ -0,0 +1,9 @@ +{ fetchgit }: + +fetchgit { + url = "https://anongit.gentoo.org/git/repo/gentoo.git"; + rev = "fa77d52a7ff39464c50707ca024725deab08b534"; + sparseCheckout = [ "*.patch" ]; + nonConeMode = true; + hash = "sha256-ZTrPeo8TjKSDAUyeJyWq1C8O9EXd9uhClHlphPsFXCA="; +} diff --git a/pkgs/locale-archive-stub.nix b/pkgs/locale-archive-stub.nix new file mode 100644 index 0000000..7a59c0e --- /dev/null +++ b/pkgs/locale-archive-stub.nix @@ -0,0 +1,21 @@ +{ stdenvNoCC, lib +, allLocales ? null +, locales ? null +}: + +stdenvNoCC.mkDerivation rec { + pname = "locale-archive-stub"; + version = "0"; + + buildCommand = '' + mkdir -p "$out/lib/locale" + touch "$out/lib/locale/locale-archive" + ''; + + meta = with lib; { + description = "locale-archive stub"; + license = licenses.cc0; + maintainers = with maintainers; [ mvs ]; + platforms = platforms.all; + }; +} diff --git a/platforms.nix b/platforms.nix new file mode 100644 index 0000000..56bc4a7 --- /dev/null +++ b/platforms.nix @@ -0,0 +1,23 @@ +{ + "x86_64-linux" = { + system = "x86_64-linux"; + #config = "x86_64-unknown-linux-musl"; + gcc.arch = "x86-64-v3"; + useLLVM = true; + linker = "lld"; + }; + "aarch64-linux" = { + system = "aarch64-linux"; + #config = "aarch64-unknown-linux-musl"; + gcc.arch = "armv8.2-a"; + useLLVM = true; + linker = "lld"; + }; + "riscv64-linux" = { + system = "aarch64-linux"; + #config = "riscv64-unknown-linux-musl"; + gcc.arch = "rv64imafdc_zicsr_zba_zbb"; + useLLVM = true; + linker = "lld"; + }; +} diff --git a/stdenv.nix b/stdenv.nix new file mode 100644 index 0000000..23a6f76 --- /dev/null +++ b/stdenv.nix @@ -0,0 +1,52 @@ +{ ... }@args: + +let + pkgs = args.buildPackages or args.pkgs; + stdenv = args.baseStdenv or pkgs.stdenv; + + inherit (pkgs) addAttrsToDerivation; + inherit (pkgs.lib) optionalAttrs optionals toList; + inherit (stdenv) buildPlatform targetPlatform; + + cflags = [ + "-pipe" # Prefer pipes over temporary files between stages + "-O2" + ] ++ optionals buildPlatform.useLLVM [ + "-flto=thin" + ]; + + ldflags = [ + "-O2" # Enable tail merging of strings + "--hash-style=gnu" # Produce only DT_GNU_HASH + ] ++ optionals buildPlatform.useLLVM [ + "--icf=safe" # Fold identical code where safe + "--lto-O2" + "--pack-dyn-relocs=relr" + ]; + + rustflags = [ + "-C opt-level=2" + ] ++ optionals buildPlatform.useLLVM [ + "-C lto=thin" + "-C linker-flavor=ld.lld" + "-C linker-plugin-lto" + ] ++ optionals (targetPlatform.isx86_64 && targetPlatform ? gcc.arch) [ + "-C target-cpu=${targetPlatform.gcc.arch}" + ] ++ map (flag: "-C link-arg=${flag}") ldflags; + +in addAttrsToDerivation (base: { + env = (base.env or { }) // optionalAttrs (!base ? NIX_CFLAGS_COMPILE) { + NIX_CFLAGS_COMPILE = + toString (toList base.env.NIX_CFLAGS_COMPILE or [ ] ++ cflags); + } // optionalAttrs (base ? env.NIX_LDFLAGS) { + NIX_LDFLAGS = + toString (toList base.env.NIX_LDFLAGS or [ ] ++ ldflags); + }; + + NIX_CFLAGS_LINK = toList base.NIX_CFLAGS_LINK or [ ] ++ map (flag: "-Wl,${flag}") ldflags; + NIX_RUSTFLAGS = toList base.NIX_RUSTFLAGS or [ ] ++ rustflags; +} // optionalAttrs (base ? env.NIX_CFLAGS) { + NIX_CFLAGS_COMPILE = toList base.NIX_CFLAGS_COMPILE or [ ] ++ cflags; +} // optionalAttrs (!base ? env.NIX_LDFLAGS) { + NIX_LDFLAGS = toList base.NIX_LDFLAGS or [ ] ++ ldflags; +}) stdenv