diff options
145 files changed, 11576 insertions, 0 deletions
diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..29b9040 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +max_line_length = 80 +trim_trailing_whitespace = true + +[*.nix] +indent_size = 2 +indent_style = space + +[*.{yaml,yml}] +indent_size = 2 +indent_style = space + +[*.{toml,tml}] +indent_size = 4 +indent_style = space diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a0e2b39 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.qcow2 +.direnv/ +.pre-commit-config.yaml +/result* diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 0000000..53af2a0 --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,2 @@ +shell=bash +disable=SC2164 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5a8e332 --- /dev/null +++ b/LICENSE @@ -0,0 +1,14 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. + diff --git a/configurations/default.nix b/configurations/default.nix new file mode 100644 index 0000000..e0741dc --- /dev/null +++ b/configurations/default.nix @@ -0,0 +1,139 @@ +{ + inputs, + lib, +}: +with lib; { + # TODO Make a generic wrapper for all hosts. + melian = let + system = "x86_64-linux"; + in + nixosSystem { + inherit system; + + modules = + attrValues inputs.self.nixosModules + ++ [./melian] + ++ (with inputs; + (with nixos-hardware.nixosModules; [ + common-pc-laptop-ssd + lenovo-thinkpad-t480 + ]) + ++ [nixpkgs.nixosModules.notDetected]); + + specialArgs = rec { + inherit inputs lib; + this = my.configurations.melian; + }; + }; + + manwe = let + system = "x86_64-linux"; + in + nixosSystem { + inherit system; + + modules = + attrValues inputs.self.nixosModules + ++ [./manwe] + ++ (with inputs; + (with nixos-hardware.nixosModules; [common-cpu-amd common-pc-ssd]) + ++ [ + nixpkgs.nixosModules.notDetected + "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix" + ]); + + specialArgs = rec { + inherit inputs lib; + this = my.configurations.manwe; + }; + }; + + varda = let + system = "x86_64-linux"; + in + nixosSystem { + inherit system; + + modules = + attrValues inputs.self.nixosModules + ++ [./varda] + ++ (with inputs; + (with nixos-hardware.nixosModules; [common-cpu-amd common-pc-ssd]) + ++ [ + nixpkgs.nixosModules.notDetected + "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix" + ]); + + specialArgs = rec { + inherit inputs lib; + this = my.configurations.varda; + }; + }; + + yavanna = let + system = "x86_64-linux"; + in + nixosSystem { + inherit system; + + modules = + attrValues inputs.self.nixosModules + ++ [./yavanna] + ++ (with inputs; + (with nixos-hardware.nixosModules; [common-cpu-intel common-pc-hdd]) + ++ [nixpkgs.nixosModules.notDetected]); + + specialArgs = rec { + inherit inputs lib; + this = my.configurations.yavanna; + }; + }; + + test-headless = let + system = "x86_64-linux"; + in + nixosSystem { + inherit system; + + modules = + attrValues inputs.self.nixosModules + ++ [./test-headless] + ++ (with inputs; [ + nixpkgs.nixosModules.notDetected + "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix" + ]); + + specialArgs = rec { + inherit inputs lib; + this = { + hostname = "test-headless"; + isHeadless = true; + isHeadful = false; + }; + }; + }; + + test-headful = let + system = "x86_64-linux"; + in + nixosSystem { + inherit system; + + modules = + attrValues inputs.self.nixosModules + ++ [./test-headful] + ++ (with inputs; [ + nixpkgs.nixosModules.notDetected + "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix" + ]); + + specialArgs = rec { + inherit inputs lib; + this = { + hostname = "test-headful"; + isHeadful = true; + isHeadless = false; + }; + }; + }; +} diff --git a/configurations/manwe/default.nix b/configurations/manwe/default.nix new file mode 100644 index 0000000..50acacd --- /dev/null +++ b/configurations/manwe/default.nix @@ -0,0 +1,171 @@ +{ + config, + inputs, + lib, + this, + ... +}: +with lib; { + imports = [ + ./mailserver.nix + ./webserver.nix + ]; + + secrets.wireguard-private-key-manwe.file = "${inputs.self}/secrets/wireguard-private-key-manwe"; + + nixfiles.modules = { + nsd = { + enable = true; + fqdn = "ns1.${config.networking.domain}"; + }; + unbound.enable = true; + + wireguard = { + privateKeyFile = config.secrets.wireguard-private-key-manwe.path; + server.enable = true; + }; + + acme.enable = true; + + monitoring.enable = true; + + gotify.enable = true; + matrix.dendrite = { + enable = true; + domain = my.domain.azahi; + }; + soju = { + enable = true; + domain = my.domain.azahi; + }; + }; + + services.prometheus = { + # TODO Make this generic. + scrapeConfigs = with my.configurations; + with config.services.prometheus.exporters; [ + { + job_name = "endlessh-go"; + static_configs = [ + { + targets = with config.nixfiles.modules.endlessh-go; [ + "${manwe.hostname}:${toString prometheusPort}" + "${varda.hostname}:${toString prometheusPort}" + "${yavanna.hostname}:${toString prometheusPort}" + ]; + } + ]; + } + { + job_name = "nginx"; + static_configs = [ + { + targets = with nginx; [ + "${manwe.hostname}:${toString port}" + "${varda.hostname}:${toString port}" + "${yavanna.hostname}:${toString port}" + ]; + } + ]; + } + { + job_name = "node"; + static_configs = [ + { + targets = with node; [ + "${manwe.hostname}:${toString port}" + "${varda.hostname}:${toString port}" + "${yavanna.hostname}:${toString port}" + ]; + } + ]; + } + { + job_name = "postgres"; + static_configs = [ + { + targets = with postgres; [ + "${manwe.hostname}:${toString port}" + "${varda.hostname}:${toString port}" + ]; + } + ]; + } + { + job_name = "unbound"; + static_configs = [ + { + targets = with unbound; ["${manwe.hostname}:${toString port}"]; + } + ]; + } + # { + # job_name = "wireguard"; + # static_configs = [{ + # targets = with wireguard; [ + # "${manwe.hostname}:${toString port}" + # ]; + # }]; + # } + ]; + }; + + networking = let + interface = "eth0"; + in { + interfaces.${interface} = { + ipv4.addresses = [ + { + inherit (this.ipv4) address; + prefixLength = 22; + } + ]; + + ipv6.addresses = [ + { + inherit (this.ipv6) address; + prefixLength = 64; + } + ]; + }; + + defaultGateway = { + inherit interface; + address = this.ipv4.gateway; + }; + + defaultGateway6 = { + inherit interface; + address = this.ipv6.gateway; + }; + + nat.externalInterface = interface; + }; + + boot = { + loader.grub = { + enable = true; + device = "/dev/sda"; + }; + + initrd.availableKernelModules = ["ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod"]; + }; + + fileSystems = { + "/boot" = { + device = "/dev/sda1"; + fsType = "xfs"; + options = ["noatime"]; + }; + + "/" = { + device = "/dev/sda2"; + fsType = "xfs"; + options = ["noatime"]; + }; + }; + + swapDevices = [{device = "/dev/sda3";}]; + + system.stateVersion = "22.05"; +} diff --git a/configurations/manwe/mailserver.nix b/configurations/manwe/mailserver.nix new file mode 100644 index 0000000..60a917b --- /dev/null +++ b/configurations/manwe/mailserver.nix @@ -0,0 +1,96 @@ +{ + config, + inputs, + lib, + ... +}: +with lib; { + imports = [inputs.simple-nixos-mailserver.nixosModule]; + + config = { + secrets = { + dkim-key-azahi-cc = { + file = "${inputs.self}/secrets/dkim-key-azahi-cc"; + path = "/var/dkim/${my.domain.azahi}.${config.mailserver.dkimSelector}.key"; + owner = "opendkim"; + group = "opendkim"; + }; + dkim-key-rohan-net = { + file = "${inputs.self}/secrets/dkim-key-rohan-net"; + path = "/var/dkim/${my.domain.rohan}.${config.mailserver.dkimSelector}.key"; + owner = "opendkim"; + group = "opendkim"; + }; + dkim-key-gondor-net = { + file = "${inputs.self}/secrets/dkim-key-gondor-net"; + path = "/var/dkim/${my.domain.gondor}.${config.mailserver.dkimSelector}.key"; + owner = "opendkim"; + group = "opendkim"; + }; + dkim-key-shire-me = { + file = "${inputs.self}/secrets/dkim-key-shire-me"; + path = "/var/dkim/${my.domain.shire}.${config.mailserver.dkimSelector}.key"; + owner = "opendkim"; + group = "opendkim"; + }; + }; + + nixfiles.modules.acme.enable = true; + + mailserver = let + cert = config.certs.${my.domain.shire}; + in { + enable = true; + + fqdn = config.networking.domain; + domains = with my.domain; [azahi gondor rohan shire]; + + localDnsResolver = false; + + certificateScheme = 1; + certificateFile = "${cert.directory}/fullchain.pem"; + keyFile = "${cert.directory}/key.pem"; + + lmtpSaveToDetailMailbox = "no"; + + loginAccounts = with my.domain; { + "azahi@${shire}" = { + hashedPassword = "[REDACTED]"; + aliases = [ + "@${azahi}" + "@${rohan}" + "@${gondor}" + "abuse@${shire}" + "admin@${shire}" + "ceo@${shire}" + "postmaster@${shire}" + ]; + }; + "samwise@${shire}" = { + hashedPassword = "[REDACTED]"; + aliases = ["chad@${shire}"]; + quota = "1G"; + }; + "pippin@${shire}" = { + hashedPassword = "[REDACTED]"; + quota = "1G"; + }; + "meriadoc@${shire}" = { + hashedPassword = "[REDACTED]"; + quota = "1G"; + }; + }; + }; + + services.fail2ban.jails = { + dovecot = '' + enabled = true + mode = aggressive + ''; + postfix = '' + enabled = true + mode = aggressive + ''; + }; + }; +} diff --git a/configurations/manwe/webserver.nix b/configurations/manwe/webserver.nix new file mode 100644 index 0000000..fd23432 --- /dev/null +++ b/configurations/manwe/webserver.nix @@ -0,0 +1,21 @@ +{ + inputs, + lib, + ... +}: +with lib; { + config.nixfiles.modules.nginx.virtualHosts = with my.domain; + { + ${shire}.locations."/".return = "301 https://www.youtube.com/watch?v=dQw4w9WgXcQ"; + ${azahi} = { + serverAliases = ["frodo.${gondor}" "frodo.${rohan}"]; + locations."/".root = inputs.azahi-cc; + }; + } + // (let + frodo = "301 https://frodo."; + in { + ${gondor}.locations."/".return = concatStrings [frodo gondor]; + ${rohan}.locations."/".return = concatStrings [frodo rohan]; + }); +} diff --git a/configurations/melian/default.nix b/configurations/melian/default.nix new file mode 100644 index 0000000..2f3da98 --- /dev/null +++ b/configurations/melian/default.nix @@ -0,0 +1,288 @@ +{ + config, + inputs, + lib, + pkgs, + ... +}: +with lib; { + secrets = { + wireguard-private-key-melian.file = "${inputs.self}/secrets/wireguard-private-key-melian"; + + syncthing-cert-melian = with config.services.syncthing; { + file = "${inputs.self}/secrets/syncthing-cert-melian"; + owner = user; + inherit group; + }; + + syncthing-key-melian = with config.services.syncthing; { + file = "${inputs.self}/secrets/syncthing-key-melian"; + owner = user; + inherit group; + }; + }; + + nixfiles.modules = { + profiles.dev = { + containers.enable = true; + sql.enable = true; + }; + + games = { + lutris.enable = true; + steam.enable = true; + }; + + wireguard = { + privateKeyFile = config.secrets.wireguard-private-key-melian.path; + client.enable = true; + }; + + syncthing = with config.secrets; { + enable = true; + key = syncthing-key-melian.path; + cert = syncthing-cert-melian.path; + }; + # ipfs.enable = true; # High CPU usage is really anoying. + + beets.enable = true; + bluetooth.enable = true; + chromium.enable = true; + libvirtd.enable = true; + mpd.enable = true; + qutebrowser.enable = true; + + throttled.enable = true; + }; + + hm = { + home.packages = with pkgs; [ + (aspellWithDicts (p: with p; [en ru])) + calibre + iaito + kotatogram-desktop + nheko + tor-browser + ]; + + accounts.email = { + maildirBasePath = "${config.my.home}/mail"; + + # TODO Move to my.nix. + accounts = let + base = { + mbsync = { + enable = true; + create = "both"; + expunge = "both"; + patterns = ["*"]; + }; + msmtp.enable = true; + mu.enable = true; + }; + + pass = path: "${pkgs.pass}/bin/pass show ${path}"; + in { + shire = + base + // { + address = my.email; + gpg = { + inherit (my.pgp) key; + signByDefault = true; + encryptByDefault = false; + }; + + primary = true; + + imap.host = "shire.me"; + smtp.host = "shire.me"; + userName = "azahi@shire.me"; + passwordCommand = pass "email/shire.me/azahi"; + }; + + yahoo = + base + // { + address = "a.gondor@yahoo.com"; + + imap.host = "imap.yahoo.com"; + smtp.host = "smtp.yahoo.com"; + userName = "a.gondor@yahoo.com"; + passwordCommand = pass "email/yahoo.com/a.gondor"; + }; + }; + }; + + programs = { + mbsync.enable = true; + msmtp.enable = true; + mu.enable = true; + + ssh.matchBlocks = let + mkBlock = { + hostname, + port ? 22022, + user ? "azahi", + }: { + inherit hostname port user; + identityFile = "${config.my.home}/.ssh/id_ed25519"; + }; + mkInternalHostname = hostname: "${hostname}.${my.domain.shire}"; + in + with my.configurations; { + # TODO Wrap this. + manwe = mkBlock {hostname = mkInternalHostname "manwe";}; + manwe-init = mkBlock {hostname = manwe.ipv4.address;}; + varda = mkBlock {hostname = mkInternalHostname "varda";}; + varda-init = mkBlock {hostname = varda.ipv4.address;}; + yavanna = mkBlock {hostname = mkInternalHostname "yavanna";}; + yavanna-init = mkBlock {hostname = yavanna.ipv4.address;}; + }; + }; + }; + + networking = { + interfaces = { + eth0.useDHCP = true; + wlan0.useDHCP = true; + }; + + wireless = { + enable = false; + iwd.enable = true; + userControlled.enable = true; + allowAuxiliaryImperativeNetworks = true; + }; + }; + + fileSystems = { + "/" = { + device = "/dev/disk/by-uuid/bb8b09dc-cc67-47e5-8280-532b17a9e62a"; + fsType = "xfs"; + options = ["noatime"]; + }; + + "/boot" = { + device = "/dev/disk/by-uuid/1083-C8A0"; + fsType = "vfat"; + }; + }; + + # Hibernation is not enabled because I don't use it. + swapDevices = [ + { + device = "/swapfile"; + size = 8 * 1024; + } + ]; + + boot = { + kernelPackages = mkForce pkgs.linuxPackages_xanmod_latest; + + # https://www.phoronix.com/scan.php?page=article&item=spectre-meltdown-2&num=11 + kernelParams = ["mitigations=off"]; + + # Just to make Lutris/Wine logs happy. + kernel.sysctl."dev.i915.perf_stream_paranoid" = 0; + + extraModprobeConfig = '' + options iwlwifi 11n_disable=1 + ''; + + initrd = { + kernelModules = []; + availableKernelModules = ["ahci" "nvme" "sd_mod" "usb_storage" "usbhid" "xhci_pci"]; + + luks.devices."root" = { + device = "/dev/disk/by-uuid/c1b46f24-eec0-47d2-a142-75ddfd7bb218"; + allowDiscards = true; + bypassWorkqueues = true; + }; + }; + kernelModules = ["kvm-intel"]; + + loader = { + efi.canTouchEfiVariables = true; + + systemd-boot = { + enable = true; + configurationLimit = 10; + }; + }; + }; + + hardware = { + trackpoint = { + enable = true; + speed = 500; + sensitivity = 250; + }; + + opengl.extraPackages = with pkgs; [ + intel-media-driver + libvdpau-va-gl + vaapiIntel + vaapiVdpau + ]; + }; + + powerManagement = let + modprobe = "${pkgs.kmod}/bin/modprobe"; + in { + enable = true; + + # This fixes an issue with not being able to suspend or wake up from suspend + # due to a kernel bug[1]. + # + # [1]: https://bbs.archlinux.org/viewtopic.php?id=270964 + # [1]: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/522998 + # [1]: https://bugs.launchpad.net/ubuntu/+source/pm-utils/+bug/562484/comments/3 + # [1]: https://gist.github.com/ioggstream/8f380d398aef989ac455b93b92d42048 + powerDownCommands = "${modprobe} -r xhci_pci"; + powerUpCommands = "${modprobe} xhci_pci"; + }; + + services = { + tlp = { + enable = false; + settings = { + START_CHARGE_THRESH_BAT0 = 75; + STOP_CHARGE_THRESH_BAT0 = 80; + RESTORE_THRESHOLDS_ON_BAT = 1; + }; + }; + + throttled.enable = mkForce false; + + thinkfan = { + enable = true; + + # Old-style configuration should be nullified because it does not support + # hwmon search and appends to valid configuration resulting in conflicts + # and crash. I probably should make a PR to remove what was introduced[1] + # before. + # + # [1]: https://github.com/NixOS/nixpkgs/commit/02b872310d6a6503639f5a71a14f00441f961bc9 + sensors = mkForce null; + fans = mkForce null; + levels = mkForce null; + + settings = { + sensors = [ + { + hwmon = "/sys/class/hwmon"; + name = "coretemp"; + indices = [1]; + } + ]; + fans = [{tpacpi = "/proc/acpi/ibm/fan";}]; + levels = [["level auto" 0 50] ["level disengaged" 50 32767]]; + }; + }; + + xserver.videoDrivers = ["intel" "modesetting"]; + }; + + system.stateVersion = "22.05"; +} diff --git a/configurations/test-headful/default.nix b/configurations/test-headful/default.nix new file mode 100644 index 0000000..fb3c276 --- /dev/null +++ b/configurations/test-headful/default.nix @@ -0,0 +1,27 @@ +{ + lib, + modulesPath, + ... +}: +with lib; { + imports = ["${modulesPath}/profiles/qemu-guest.nix"]; + + nixfiles.modules = { + dwm.enable = true; + kde.enable = false; + xmonad.enable = false; + }; + + boot.loader.systemd-boot.enable = true; + fileSystems."/".device = "/dev/null"; + + documentation = { + enable = mkForce false; + man = { + enable = mkForce false; + man-db.enable = mkForce false; + }; + }; + + system.stateVersion = "22.05"; +} diff --git a/configurations/test-headless/default.nix b/configurations/test-headless/default.nix new file mode 100644 index 0000000..c085e63 --- /dev/null +++ b/configurations/test-headless/default.nix @@ -0,0 +1,22 @@ +{ + lib, + modulesPath, + ... +}: +with lib; { + imports = ["${modulesPath}/profiles/qemu-guest.nix"]; + + # This will allow `nix flake check` tests to pass. + nixfiles.modules = { + endlessh-go.enable = mkForce false; + node-exporter.enable = mkForce false; + promtail.enable = mkForce false; + }; + + boot.loader.systemd-boot.enable = true; + fileSystems."/".device = "/dev/null"; + + documentation.enable = mkForce false; + + system.stateVersion = "22.05"; +} diff --git a/configurations/varda/default.nix b/configurations/varda/default.nix new file mode 100644 index 0000000..b9d84f7 --- /dev/null +++ b/configurations/varda/default.nix @@ -0,0 +1,95 @@ +{ + config, + inputs, + lib, + this, + ... +}: +with lib; { + secrets.wireguard-private-key-varda.file = "${inputs.self}/secrets/wireguard-private-key-varda"; + + nixfiles.modules = { + wireguard = { + privateKeyFile = config.secrets.wireguard-private-key-varda.path; + client.enable = true; + }; + + acme.enable = true; + + rss-bridge.enable = true; + radicale.enable = true; + }; + + networking = let + interface = "eth0"; + in { + interfaces.${interface} = { + ipv4.addresses = [ + { + inherit (this.ipv4) address; + prefixLength = 22; + } + ]; + + ipv6.addresses = [ + { + inherit (this.ipv6) address; + prefixLength = 64; + } + ]; + }; + + defaultGateway = { + inherit interface; + address = this.ipv4.gateway; + }; + + defaultGateway6 = { + inherit interface; + address = this.ipv6.gateway; + }; + }; + + boot = { + loader.grub = { + enable = true; + device = "/dev/sda"; + }; + + initrd = { + luks.devices.nixos = { + device = "/dev/sda2"; + allowDiscards = true; + bypassWorkqueues = true; + }; + + network = { + enable = true; + ssh = { + enable = true; + port = head config.services.openssh.ports; + hostKeys = map (k: k.path) config.services.openssh.hostKeys; + authorizedKeys = config.my.openssh.authorizedKeys.keys; + }; + }; + + availableKernelModules = ["ata_piix" "sd_mod" "sr_mod" "uhci_hcd" "virtio_pci" "virtio_scsi"]; + }; + }; + + fileSystems = { + "/boot" = { + device = "LABEL=boot"; + fsType = "xfs"; + options = ["noatime"]; + }; + + "/" = { + device = "LABEL=nixos"; + fsType = "xfs"; + options = ["noatime"]; + }; + }; + + system.stateVersion = "22.05"; +} diff --git a/configurations/yavanna/default.nix b/configurations/yavanna/default.nix new file mode 100644 index 0000000..b740da3 --- /dev/null +++ b/configurations/yavanna/default.nix @@ -0,0 +1,101 @@ +{ + config, + inputs, + lib, + this, + ... +}: +with lib; { + secrets = { + wireguard-private-key-yavanna.file = "${inputs.self}/secrets/wireguard-private-key-yavanna"; + + syncthing-cert-yavanna = with config.services.syncthing; { + file = "${inputs.self}/secrets/syncthing-cert-yavanna"; + owner = user; + inherit group; + }; + + syncthing-key-yavanna = with config.services.syncthing; { + file = "${inputs.self}/secrets/syncthing-key-yavanna"; + owner = user; + inherit group; + }; + }; + + nixfiles.modules = { + wireguard = { + privateKeyFile = config.secrets.wireguard-private-key-yavanna.path; + client.enable = true; + }; + + syncthing = with config.secrets; { + enable = true; + key = syncthing-key-yavanna.path; + cert = syncthing-cert-yavanna.path; + }; + + acme.enable = true; + + rtorrent = { + enable = true; + flood.enable = true; + }; + }; + + networking = let + interface = "eth0"; + in { + interfaces.${interface} = { + ipv4.addresses = [ + { + inherit (this.ipv4) address; + prefixLength = 24; + } + ]; + + ipv6.addresses = [ + { + inherit (this.ipv6) address; + prefixLength = 128; + } + ]; + }; + + defaultGateway = { + inherit interface; + address = this.ipv4.gateway; + }; + + defaultGateway6 = { + inherit interface; + address = this.ipv6.gateway; + }; + }; + + boot = { + loader.grub = { + enable = true; + device = "/dev/sda"; + }; + + initrd = { + availableKernelModules = ["uhci_hcd" "ahci"]; + kernelModules = ["nvme"]; + }; + }; + + fileSystems."/" = { + device = "/dev/sda1"; + fsType = "ext4"; + options = ["noatime"]; + }; + + swapDevices = [ + { + device = "/swapfile"; + size = 4 * 1024; + } + ]; + + system.stateVersion = "22.05"; +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..f7141a2 --- /dev/null +++ b/flake.lock @@ -0,0 +1,421 @@ +{ + "nodes": { + "agenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1652712410, + "narHash": "sha256-hMJ2TqLt0DleEnQFGUHK9sV2aAzJPU8pZeiZoqRozbE=", + "owner": "ryantm", + "repo": "agenix", + "rev": "7e5e58b98c3dcbf497543ff6f22591552ebfe65b", + "type": "github" + }, + "original": { + "owner": "ryantm", + "ref": "main", + "repo": "agenix", + "type": "github" + } + }, + "alertmanager-gotify": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1638488371, + "narHash": "sha256-WTzMc8W0+U7ifcaiwejFjnDNoBoK+CcaV6VIBLK3BrI=", + "ref": "refs/heads/master", + "rev": "b752ae3ca5974ab5a2d19a59c2e2960faeff699c", + "revCount": 35, + "type": "git", + "url": "https://git.mbosch.me/ma27/alertmanager-gotify" + }, + "original": { + "type": "git", + "url": "https://git.mbosch.me/ma27/alertmanager-gotify" + } + }, + "azahi-cc": { + "flake": false, + "locked": { + "lastModified": 1656772709, + "narHash": "sha256-WY46rBmxd5oZUYOkSoLHDKcIUzm/gqoP0VsTZgJ+S7w=", + "owner": "azahi", + "repo": "azahi.cc", + "rev": "84169cc00825ff294498f380e0ec28b0bd088c27", + "type": "gitlab" + }, + "original": { + "owner": "azahi", + "ref": "master", + "repo": "azahi.cc", + "type": "gitlab" + } + }, + "blobs": { + "flake": false, + "locked": { + "lastModified": 1604995301, + "narHash": "sha256-wcLzgLec6SGJA8fx1OEN1yV/Py5b+U5iyYpksUY/yLw=", + "owner": "simple-nixos-mailserver", + "repo": "blobs", + "rev": "2cccdf1ca48316f2cfd1c9a0017e8de5a7156265", + "type": "gitlab" + }, + "original": { + "owner": "simple-nixos-mailserver", + "repo": "blobs", + "type": "gitlab" + } + }, + "dns-nix": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1635273082, + "narHash": "sha256-EHiDP2jEa7Ai5ZwIf5uld9RVFcV77+2SUxjQXwJsJa0=", + "owner": "kirelagin", + "repo": "dns.nix", + "rev": "c7b9645da9c0ddce4f9de4ef27ec01bb8108039a", + "type": "github" + }, + "original": { + "owner": "kirelagin", + "ref": "master", + "repo": "dns.nix", + "type": "github" + } + }, + "emacs-overlay": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1659379767, + "narHash": "sha256-cfcutZL9YBqx2uTRfeLpic6baU/nwLlsp/hMnL/boDA=", + "owner": "nix-community", + "repo": "emacs-overlay", + "rev": "e911c43b99c7b9c94ee408c38b0c6e2c6a01132e", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "master", + "repo": "emacs-overlay", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1641205782, + "narHash": "sha256-4jY7RCWUoZ9cKD8co0/4tFARpWB+57+r1bLLvXNJliY=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "b7547d3eed6f32d06102ead8991ec52ab0a4f1a7", + "type": "github" + }, + "original": { + "owner": "edolstra", + "ref": "master", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-registry": { + "flake": false, + "locked": { + "lastModified": 1657791189, + "narHash": "sha256-Z2ckMWqybpJjIdGIPuHcYzTL7DPTbFKpBZJJgGfFUog=", + "owner": "NixOS", + "repo": "flake-registry", + "rev": "7a481cef7e86d6ceb4aec244d9bb085619360ba1", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "master", + "repo": "flake-registry", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1656928814, + "narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249", + "type": "github" + }, + "original": { + "owner": "numtide", + "ref": "master", + "repo": "flake-utils", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "utils": [ + "flake-utils" + ] + }, + "locked": { + "lastModified": 1659398318, + "narHash": "sha256-5wovS14I/DNXwfiMP402Ut2kxI58CO1wD943fboWMDw=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "77648a07e459adff69b2c4033a77b2cababb5843", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "master", + "repo": "home-manager", + "type": "github" + } + }, + "nixos-hardware": { + "locked": { + "lastModified": 1659356074, + "narHash": "sha256-UwV6hZZEtchvtiTCCD/ODEv1226eam8kEgEyQb7xB0E=", + "owner": "NixOS", + "repo": "nixos-hardware", + "rev": "ea3efc80f8ab83cb73aec39f4e76fe87afb15a08", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "master", + "repo": "nixos-hardware", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1659254610, + "narHash": "sha256-ee5W5MLWZ3kdx5hwOUs6trOJit+GeTDfG+Lg3rANKoc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "67f49b2a3854e8b5e3f9df4422225daa0985f451", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-master": { + "locked": { + "lastModified": 1659395920, + "narHash": "sha256-Am1N2FK8KJWpEg5Opt7xefw5YDRYmalWF8keybhx3pc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "ec9cb32147fbd46a4082cb119d274a0990caa390", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "master", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1659392573, + "narHash": "sha256-YHeOH+cypoB3ZCz7Dnh8/Nolrl2ZMSLsth2U3h+WqJ4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "760d3360f77eca32cc0cdfea403e89285d82a048", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-22.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nmap-vulners": { + "flake": false, + "locked": { + "lastModified": 1632035522, + "narHash": "sha256-Y3THM8cNVZdl4GVLI0d4c/EgAyBZeNLSlDsUMHD9UYk=", + "owner": "vulnersCom", + "repo": "nmap-vulners", + "rev": "bbf53dd085f8d810921ee00ccf85bdb329d59514", + "type": "github" + }, + "original": { + "owner": "vulnersCom", + "ref": "master", + "repo": "nmap-vulners", + "type": "github" + } + }, + "nmap-vulscan": { + "flake": false, + "locked": { + "lastModified": 1646989909, + "narHash": "sha256-hV9gCEQwoA2l7eg2pKFmZeUdPkzeF9tnWTswgw0P4A8=", + "owner": "scipag", + "repo": "vulscan", + "rev": "64bd0049894431d81517801c42b8585f9064a043", + "type": "github" + }, + "original": { + "owner": "scipag", + "ref": "master", + "repo": "vulscan", + "type": "github" + } + }, + "nur": { + "locked": { + "lastModified": 1659378417, + "narHash": "sha256-76cfWXqX2Yh8+BZYIM7ZyI7o/SfNTdz49bgT4UjGnhI=", + "owner": "nix-community", + "repo": "NUR", + "rev": "b7b48e67de148d87c3a0b798977a45eacbaac3d0", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "master", + "repo": "NUR", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1658611562, + "narHash": "sha256-jktQ3mRrFAiFzzmVxQXh+8IxZOEE4hfr7St3ncXeVy4=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "f436e6dbc10bb3500775785072a40eefe057b18e", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "master", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "agenix": "agenix", + "alertmanager-gotify": "alertmanager-gotify", + "azahi-cc": "azahi-cc", + "dns-nix": "dns-nix", + "emacs-overlay": "emacs-overlay", + "flake-registry": "flake-registry", + "flake-utils": "flake-utils", + "home-manager": "home-manager", + "nixos-hardware": "nixos-hardware", + "nixpkgs": "nixpkgs", + "nixpkgs-master": "nixpkgs-master", + "nixpkgs-stable": "nixpkgs-stable", + "nmap-vulners": "nmap-vulners", + "nmap-vulscan": "nmap-vulscan", + "nur": "nur", + "pre-commit-hooks": "pre-commit-hooks", + "simple-nixos-mailserver": "simple-nixos-mailserver", + "xmonad-ng": "xmonad-ng" + } + }, + "simple-nixos-mailserver": { + "inputs": { + "blobs": "blobs", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-22_05": [ + "nixpkgs-stable" + ], + "utils": [ + "flake-utils" + ] + }, + "locked": { + "lastModified": 1655930346, + "narHash": "sha256-ht56HHOzEhjeIgAv5ZNFjSVX/in1YlUs0HG9c1EUXTM=", + "owner": "simple-nixos-mailserver", + "repo": "nixos-mailserver", + "rev": "f535d8123c4761b2ed8138f3d202ea710a334a1d", + "type": "gitlab" + }, + "original": { + "owner": "simple-nixos-mailserver", + "ref": "master", + "repo": "nixos-mailserver", + "type": "gitlab" + } + }, + "xmonad-ng": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ], + "pre-commit-hooks": [ + "pre-commit-hooks" + ] + }, + "locked": { + "lastModified": 1644967454, + "narHash": "sha256-LYdzh5e5nITWbW7lFb9r5xva5k5YQ9rcFIsORgV7zZE=", + "owner": "azahi", + "repo": "xmonad-ng", + "rev": "0a150971e7cc05f518e290ad6d4d21b9311e6147", + "type": "gitlab" + }, + "original": { + "owner": "azahi", + "ref": "master", + "repo": "xmonad-ng", + "type": "gitlab" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..5f1ad07 --- /dev/null +++ b/flake.nix @@ -0,0 +1,242 @@ +{ + description = "nixfiles"; + + # `flake.lock` generation is not computable[1], thus it is impossible to add + # helper functions to manage inputs. + # + # [1]: https://github.com/NixOS/nix/issues/5373 + inputs = { + nixpkgs = { + type = "github"; + owner = "NixOS"; + repo = "nixpkgs"; + ref = "nixpkgs-unstable"; + }; + + nixpkgs-master = { + type = "github"; + owner = "NixOS"; + repo = "nixpkgs"; + ref = "master"; + }; + + nixpkgs-stable = { + type = "github"; + owner = "NixOS"; + repo = "nixpkgs"; + ref = "release-22.05"; + }; + + nixos-hardware = { + type = "github"; + owner = "NixOS"; + repo = "nixos-hardware"; + ref = "master"; + }; + + nur = { + type = "github"; + owner = "nix-community"; + repo = "NUR"; + ref = "master"; + }; + + home-manager = { + type = "github"; + owner = "nix-community"; + repo = "home-manager"; + ref = "master"; + inputs = { + nixpkgs.follows = "nixpkgs"; + utils.follows = "flake-utils"; + }; + }; + + emacs-overlay = { + type = "github"; + owner = "nix-community"; + repo = "emacs-overlay"; + ref = "master"; + inputs = { + flake-utils.follows = "flake-utils"; + nixpkgs.follows = "nixpkgs"; + }; + }; + + dns-nix = { + type = "github"; + owner = "kirelagin"; + repo = "dns.nix"; + ref = "master"; + inputs = { + flake-utils.follows = "flake-utils"; + nixpkgs.follows = "nixpkgs"; + }; + }; + + simple-nixos-mailserver = { + type = "gitlab"; + owner = "simple-nixos-mailserver"; + repo = "nixos-mailserver"; + ref = "master"; + inputs = { + nixpkgs-22_05.follows = "nixpkgs-stable"; + nixpkgs.follows = "nixpkgs"; + utils.follows = "flake-utils"; + }; + }; + + alertmanager-gotify = { + type = "git"; + url = "https://git.mbosch.me/ma27/alertmanager-gotify"; + inputs = { + flake-utils.follows = "flake-utils"; + nixpkgs.follows = "nixpkgs"; + }; + }; + + flake-utils = { + type = "github"; + owner = "numtide"; + repo = "flake-utils"; + ref = "master"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + flake-registry = { + type = "github"; + owner = "NixOS"; + repo = "flake-registry"; + ref = "master"; + flake = false; + }; + + agenix = { + type = "github"; + owner = "ryantm"; + repo = "agenix"; + ref = "main"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + pre-commit-hooks = { + type = "github"; + owner = "cachix"; + repo = "pre-commit-hooks.nix"; + ref = "master"; + inputs = { + flake-utils.follows = "flake-utils"; + nixpkgs.follows = "nixpkgs"; + }; + }; + + xmonad-ng = { + type = "gitlab"; + owner = "azahi"; + repo = "xmonad-ng"; + ref = "master"; + # type = "path"; + # path = "/home/azahi/src/xmonad-ng"; + inputs = { + flake-utils.follows = "flake-utils"; + nixpkgs.follows = "nixpkgs"; + pre-commit-hooks.follows = "pre-commit-hooks"; + }; + }; + + azahi-cc = { + type = "gitlab"; + owner = "azahi"; + repo = "azahi.cc"; + ref = "master"; + flake = false; + }; + + nmap-vulners = { + type = "github"; + owner = "vulnersCom"; + repo = "nmap-vulners"; + ref = "master"; + flake = false; + }; + + nmap-vulscan = { + type = "github"; + owner = "scipag"; + repo = "vulscan"; + ref = "master"; + flake = false; + }; + }; + + outputs = inputs: + with inputs; let + lib = nixpkgs.lib.extend (import ./lib); + in + flake-utils.lib.eachSystem ["x86_64-linux"] + (system: let + pkgs = import nixpkgs { + inherit system; + overlays = [self.overlays.default]; + }; + in { + packages.default = pkgs.nixfiles.override { + nixfilesSrc = "."; + }; + + apps.${system}.default = { + type = "app"; + package = "${self.packages.${system}.nixfiles}/bin/nixfiles"; + }; + + devShells.default = pkgs.mkShell { + inherit (self.checks.${system}.preCommit) shellHook; + packages = with pkgs; [ + pyright + python310 + rnix-lsp + ]; + }; + + formatter = pkgs.alejandra; + + checks.preCommit = pre-commit-hooks.lib.${system}.run { + src = ./.; + hooks = { + alejandra.enable = true; + black.enable = true; + flake8 = { + enable = true; + entry = "${pkgs.python310Packages.flake8}/bin/flake8"; + types = ["file" "python"]; + }; + isort.enable = true; + prettier = { + enable = true; + types = []; + files = "\\.css"; + }; + shellcheck.enable = true; + shfmt.enable = true; + statix.enable = true; + }; + }; + }) + // { + inherit lib; + + nixosModules.nixfiles = import ./modules/nixfiles; + + nixosConfigurations = + import ./configurations {inherit inputs lib;}; + + overlays.default = final: _: { + UltimMC = final.libsForQt5.callPackage ./packages/ultimmc.nix {}; + bruh = final.callPackage ./packages/bruh.nix {}; + mpv-autosub = final.callPackage ./packages/mpv-autosub.nix {}; + myip = final.callPackage ./packages/myip.nix {}; + nixfiles = final.callPackage ./packages/nixfiles.nix {}; + throttled = final.callPackage ./packages/throttled.nix {}; + }; + }; +} diff --git a/lib/default.nix b/lib/default.nix new file mode 100644 index 0000000..7a8dfbf --- /dev/null +++ b/lib/default.nix @@ -0,0 +1,20 @@ +lib: _: rec { + my = import ./my.nix lib; + + dns = import ./dns.nix lib; + + isEven = number: + assert (builtins.isInt number) || (builtins.isFloat number); + builtins.div number 2 == 0; + + isOdd = !isEven; + + pow = base: exponent: + assert (builtins.isInt base) && (builtins.isInt exponent); + assert exponent > 0; + builtins.foldl' (x: _: x * base) 1 (builtins.genList _ exponent); + + comcat = lib.concatStringsSep ","; + + mkTcpMem = min: ini: max: (lib.concatMapStrings (x: toString x + " ") [(pow 2 min) (pow 2 ini) (pow 2 max)]); +} diff --git a/lib/dns.nix b/lib/dns.nix new file mode 100644 index 0000000..e486f1c --- /dev/null +++ b/lib/dns.nix @@ -0,0 +1,14 @@ +_: { + const = { + quad9 = { + default = ["9.9.9.9" "149.112.112.112" "2620:fe::fe" "2620:fe::9"]; + unsecured = ["9.9.9.10" "149.112.112.10" "2620:fe::10" "2620:fe::fe:10"]; + ecs = ["9.9.9.11" "149.112.112.11" "2620:fe::11" "2620:fe::fe:11"]; + }; + cloudflare = { + default = ["1.1.1.1" "1.0.0.1" "2606:4700:4700::1111" "2606:4700:4700::1001"]; + blocking = ["1.1.1.2" "1.0.0.2" "2606:4700:4700::1112" "2606:4700:4700::1002"]; + blockingPlus = ["1.1.1.3" "1.0.0.3" "2606:4700:4700::1113" "2606:4700:4700::1003"]; + }; + }; +} diff --git a/lib/my.nix b/lib/my.nix new file mode 100644 index 0000000..f7c4141 --- /dev/null +++ b/lib/my.nix @@ -0,0 +1,202 @@ +lib: +with lib; + (evalModules { + modules = [ + { + options.configurations = mkOption { + description = "My configurations."; + type = with types; + attrsOf (submodule ({name, ...}: { + freeformType = attrs; + options = let + mkConfigurationTypeOption = type: + mkOption { + description = "Whether the machine is a ${type} one."; + type = bool; + default = false; + }; + in { + hostname = mkOption { + description = "The machine's hostname"; + type = str; + default = name; + readOnly = true; + }; + + isHeadless = mkConfigurationTypeOption "headless"; + isHeadful = mkConfigurationTypeOption "headful"; + isOther = mkConfigurationTypeOption "other"; + + ipv4 = { + address = mkOption { + description = "The machine's public IPv4 addresses."; + type = nullOr str; + default = null; + }; + gateway = mkOption { + description = "The machine's IPv4 gateway."; + type = nullOr str; + default = null; + }; + }; + ipv6 = { + address = mkOption { + description = "The machine's public IPv6 addresses."; + type = nullOr str; + default = null; + }; + gateway = mkOption { + description = "The machine's IPv6 gateway."; + type = nullOr str; + default = null; + }; + }; + + wireguard = { + ipv4.address = mkOption { + description = "The machine's internal IPv4 addresses."; + type = nullOr str; + default = null; + }; + ipv6.address = mkOption { + description = "The machine's internal IPv4 addresses."; + type = nullOr str; + default = null; + }; + publicKey = mkOption { + description = "The machine's public key."; + type = nullOr str; + default = null; + }; + }; + + domains = mkOption { + description = "External domains that resovle to this address."; + type = listOf str; + default = []; + }; + + syncthing.id = mkOption { + description = "Syncthing ID."; + type = nullOr str; + default = null; + }; + }; + })); + + default = {}; + }; + + config = { + _module.freeformType = types.attrs; + + fullname = "Azat Bahawi"; + username = "azahi"; + domain = { + azahi = "azahi.cc"; + rohan = "rohan.net"; + gondor = "gondor.net"; + shire = "shire.me"; + }; + email = "frodo@${my.domain.gondor}"; + pgp = { + key = "[REDACTED]"; + fingerprint = "[REDACTED]"; + grip = "[REDACTED]"; + }; + ssh = rec { + type = "ed25519"; + id = my.email; + key = "ssh-${type} [REDACTED] ${id}"; + }; + hashedPassword = "[REDACTED]"; + + configurations = { + manwe = { + isHeadless = true; + ipv4 = { + address = "[IPv4]"; + gateway = "[IPv4]"; + }; + ipv6 = { + address = "[IPv6]"; + gateway = "[IPv6]"; + }; + wireguard = { + ipv4.address = "10.69.0.1"; + ipv6.address = "fd69::0:1"; + publicKey = "[REDACTED]"; + }; + domains = with my.domain; [ + "frodo.${rohan}" + "frodo.${gondor}" + "gotify.${shire}" + "monitoring.${shire}" + azahi + rohan + gondor + shire + ]; + }; + varda = { + isHeadless = true; + ipv4 = { + address = "[IPv4]"; + gateway = "[IPv4]"; + }; + ipv6 = { + address = "[IPv6]"; + gateway = "[IPv6]"; + }; + wireguard = { + ipv4.address = "10.69.1.1"; + ipv6.address = "fd69::1:1"; + publicKey = "[REDACTED]"; + }; + domains = with my.domain; [ + "radicale.${shire}" + "rss-bridge.${shire}" + ]; + }; + yavanna = { + isHeadless = true; + ipv4 = { + address = "[IPv4]"; + gateway = "[IPv4]"; + }; + ipv6 = { + address = "[IPv6]"; + gateway = "[IPv6]"; + }; + wireguard = { + ipv4.address = "10.69.1.2"; + ipv6.address = "fd69::1:2"; + publicKey = "[REDACTED]"; + }; + domains = with my.domain; ["flood.${shire}"]; + syncthing.id = "[Syncthing ID]"; + }; + melian = { + isHeadful = true; + wireguard = { + ipv4.address = "10.69.4.1"; + ipv6.address = "fd69::4:1"; + publicKey = "[REDACTED]"; + }; + syncthing.id = "[Syncthing ID]"; + }; + gothmog = { + isOther = true; + wireguard = { + ipv4.address = "10.69.5.1"; + ipv6.address = "fd69::5:1"; + publicKey = "[REDACTED]"; + }; + syncthing.id = "[Syncthing ID]"; + }; + }; + }; + } + ]; + }) + .config diff --git a/modules/nixfiles/acme.nix b/modules/nixfiles/acme.nix new file mode 100644 index 0000000..196a6a5 --- /dev/null +++ b/modules/nixfiles/acme.nix @@ -0,0 +1,32 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.acme; +in { + imports = [ + (mkAliasOptionModule ["certs"] ["security" "acme" "certs"]) + ]; + + options.nixfiles.modules.acme = { + enable = mkEnableOption "Whether to enable ACME."; + + email = mkOption { + description = "Email for notifications."; + type = with types; str; + default = "admin+acme@${my.domain.shire}"; + }; + }; + + config = mkIf cfg.enable { + security.acme = { + acceptTerms = true; + defaults = { + inherit (cfg) email; + validMinDays = 60; + }; + }; + }; +} diff --git a/modules/nixfiles/alacritty.nix b/modules/nixfiles/alacritty.nix new file mode 100644 index 0000000..8b3e646 --- /dev/null +++ b/modules/nixfiles/alacritty.nix @@ -0,0 +1,119 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.alacritty; +in { + options.nixfiles.modules.alacritty.enable = + mkEnableOption "Whether to enable Alacritty terminal emulator."; + + config = mkIf cfg.enable { + hm.programs.alacritty = { + enable = true; + settings = with config.nixfiles.modules; { + window = { + padding = with config.fontScheme.monospaceFont; { + x = size; + y = size; + }; + dynamic_padding = false; + decorations = + if (kde.enable || gnome.enable) + then "full" + else "none"; + }; + font = with config.fontScheme.monospaceFont; { + normal = { + inherit family; + style = "Regular"; + }; + bold = { + inherit family; + style = "Bold"; + }; + italic = { + inherit family; + style = "Italic"; + }; + bold_italic = { + inherit family; + style = "Bold Italic"; + }; + inherit size; + }; + colors = with profiles.common.colourScheme; { + primary = {inherit background foreground;}; + cursor = { + text = "CellBackground"; + cursor = "CellForeground"; + }; + vi_mode_cursor = { + text = "CellBackground"; + cursor = "CellForeground"; + }; + search = { + matches = { + foreground = white; + background = red; + }; + focused_match = { + foreground = red; + background = black; + }; + bar = { + foreground = black; + background = white; + }; + }; + hints = { + start = { + foreground = black; + background = yellow; + }; + end = { + foreground = yellow; + background = black; + }; + line_indicator = { + foreground = null; + background = null; + }; + selection = { + text = "CellBackground"; + background = "CellForeground"; + }; + normal = { + inherit black red green yellow blue magenta cyan white; + }; + bright = { + inherit + brightBlack + brightRed + brightGreen + brightYellow + brightBlue + brightMagenta + brightCyan + brightWhite + ; + }; + }; + }; + bell = { + duration = 0; + command = null; # TODO notify-send? + }; + cursor = { + style = { + shape = "Block"; + blinking = "Off"; + }; + vi_mode_style = "Block"; + }; + selection.save_to_clipboard = true; # TODO autocutsel? + }; + }; + }; +} diff --git a/modules/nixfiles/alertmanager.nix b/modules/nixfiles/alertmanager.nix new file mode 100644 index 0000000..e067cd1 --- /dev/null +++ b/modules/nixfiles/alertmanager.nix @@ -0,0 +1,81 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.alertmanager; +in { + options.nixfiles.modules.alertmanager = { + enable = mkEnableOption "Whether to enable Alertmanager."; + + port = mkOption { + description = "Port."; + type = with types; port; + default = 30112; + }; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; nullOr str; + default = config.nixfiles.modules.monitoring.domain; + }; + + path = mkOption { + description = "Path."; + type = with types; str; + default = "/alertmanager"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules.nginx = with cfg; { + enable = true; + virtualHosts.${cfg.domain}.locations.${path} = { + proxyPass = "http://127.0.0.1:${toString port}${path}"; + extraConfig = '' + if ($internal != 1) { + return 403; + } + ''; + }; + }; + + services = let + acme = config.nixfiles.modules.acme.enable; + in { + prometheus.alertmanager = { + enable = true; + + listenAddress = "127.0.0.1"; + inherit (cfg) port; + + extraFlags = [ + "--web.external-url=http${ + optionalString acme "s" + }://${cfg.domain}${cfg.path}" + ]; + + # TODO Make an option. + configuration = { + global = { + smtp_from = "alertmanager@${my.domain.shire}"; + smtp_smarthost = "${my.domain.shire}:584"; + }; + + route = { + receiver = my.username; + group_by = ["alertname"]; + }; + + receivers = [ + { + name = my.username; + email_configs = [{to = "${my.username}+alert@${my.domain.shire}";}]; + } + ]; + }; + }; + }; + }; +} diff --git a/modules/nixfiles/aria2.nix b/modules/nixfiles/aria2.nix new file mode 100644 index 0000000..87f2109 --- /dev/null +++ b/modules/nixfiles/aria2.nix @@ -0,0 +1,48 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.aria2; +in { + options.nixfiles.modules.aria2.enable = + mkEnableOption "Whether to enable aria2."; + + config = mkIf cfg.enable { + hm = { + programs.aria2 = { + enable = true; + + settings = { + bt-max-peers = 128; + bt-save-metadata = true; + continue = true; + enable-dht = true; + enable-peer-exchange = true; + enable-rpc = false; + follow-torrent = true; + log-level = "info"; + max-connection-per-server = 16; + max-overall-upload-limit = "1K"; + max-tries = 5; + max-upload-limit = "1K"; + seed-ratio = 0.1; + seed-time = 0.1; + stream-piece-selector = "default"; + timeout = 60; + }; + }; + + xdg.desktopEntries.aria2c = { + name = "aria2"; + genericName = "Download Manager"; + exec = "${pkgs.aria2}/bin/aria2c"; + terminal = true; + categories = ["Application" "Network"]; + mimeType = ["application/x-bittorrent" "x-scheme-handler/magnet"]; + }; + }; + }; +} diff --git a/modules/nixfiles/aspell.nix b/modules/nixfiles/aspell.nix new file mode 100644 index 0000000..7669eb9 --- /dev/null +++ b/modules/nixfiles/aspell.nix @@ -0,0 +1,23 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.aspell; +in { + options.nixfiles.modules.aspell.enable = + mkEnableOption "Whether to enable GNU Aspell."; + + config = mkIf cfg.enable { + hm.home = { + file.".aspell.conf".text = '' + personal /dev/null + repl /dev/null + ''; + + packages = with pkgs; [(aspellWithDicts (p: with p; [en ru]))]; + }; + }; +} diff --git a/modules/nixfiles/bat.nix b/modules/nixfiles/bat.nix new file mode 100644 index 0000000..d4bb1d6 --- /dev/null +++ b/modules/nixfiles/bat.nix @@ -0,0 +1,39 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.bat; +in { + options.nixfiles.modules.bat.enable = mkEnableOption "Whether to enable Bat."; + + config = mkIf cfg.enable { + hm.programs = { + bat = { + enable = true; + config = { + style = "plain"; + tabs = "4"; + theme = "base16"; + wrap = "never"; + }; + }; + + bash = { + shellAliases = let + bat = "${pkgs.bat}/bin/bat"; + in { + bay = "${bat} --language=yaml --tabs 2"; + baj = "${bat} --language=json --tabs 2"; + }; + + initExtra = mkAfter '' + _complete_alias bay _bat bat + _complete_alias baj _bat bat + ''; + }; + }; + }; +} diff --git a/modules/nixfiles/beets.nix b/modules/nixfiles/beets.nix new file mode 100644 index 0000000..de4cd73 --- /dev/null +++ b/modules/nixfiles/beets.nix @@ -0,0 +1,73 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.beets; +in { + options.nixfiles.modules.beets.enable = + mkEnableOption "Whether to enable beets."; + + config = mkIf cfg.enable { + hm = let + beetsdir = "${config.hm.xdg.dataHome}/beets"; + in { + home.sessionVariables.BEETSDIR = beetsdir; + + programs = { + beets = { + enable = true; + + settings = { + library = "${beetsdir}/library.db"; + directory = config.userDirs.music; + plugins = "badfiles edit fetchart info mbsync scrub"; + original_date = true; + import = { + write = true; + copy = true; + move = false; + bell = true; + from_scratch = true; + }; + match = { + preferred = { + countries = [ + "JP" + "KR" + "TW" + "HK" + "CN" + "RU" + "NL" + "DE" + "AT" + "GB|UK" + "CA" + "AU" + "NZ" + "US" + ]; + original_year = true; + }; + }; + edit = { + albumfields = "album artist albumartist"; + itemfields = "track title album artist albumartist day month year genre"; + }; + fetchart = { + auto = true; + cautious = true; + cover_names = "cover Cover folder Folder art Art album Album front Front"; + sources = "filesystem coverart itunes amazon albumart wikipedia"; + }; + scrub.auto = true; + }; + }; + + bash.shellAliases.beet = "${config.hm.programs.beets.package}/bin/beet --config ${config.dirs.config}/beets/config.yaml"; + }; + }; + }; +} diff --git a/modules/nixfiles/bluetooth.nix b/modules/nixfiles/bluetooth.nix new file mode 100644 index 0000000..04e6b8d --- /dev/null +++ b/modules/nixfiles/bluetooth.nix @@ -0,0 +1,34 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.bluetooth; +in { + options.nixfiles.modules.bluetooth.enable = + mkEnableOption "Whether to enable Bluetooth support."; + + config = mkIf cfg.enable { + hardware.bluetooth = { + enable = true; + package = pkgs.bluezFull; + settings.General.FastConnectable = true; + }; + + environment = { + etc."bluetooth/input.conf".text = generators.toINI {} { + General = { + IdleTimeout = 15; + UserspaceHID = true; + }; + }; + + systemPackages = with pkgs; + with config.nixfiles.modules; + optional gnome.enable gnome.gnome-bluetooth + ++ optional kde.enable plasma5Packages.bluedevil; + }; + }; +} diff --git a/modules/nixfiles/broot.nix b/modules/nixfiles/broot.nix new file mode 100644 index 0000000..19cd1d6 --- /dev/null +++ b/modules/nixfiles/broot.nix @@ -0,0 +1,18 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.broot; +in { + options.nixfiles.modules.broot.enable = + mkEnableOption "Whether to enable broot."; + + config = mkIf cfg.enable { + hm.programs.broot = { + enable = true; + modal = true; + }; + }; +} diff --git a/modules/nixfiles/chromium.nix b/modules/nixfiles/chromium.nix new file mode 100644 index 0000000..1ec761c --- /dev/null +++ b/modules/nixfiles/chromium.nix @@ -0,0 +1,42 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.chromium; +in { + options.nixfiles.modules.chromium.enable = + mkEnableOption "Whether to enable Chromium."; + + config = mkIf cfg.enable { + hm = { + # home.sessionVariables.BROWSER = mkOverride 300 "chromium"; + + programs.chromium = { + enable = true; + + package = pkgs.chromium; + + extensions = + [ + {id = "cjpalhdlnbpafiamejdnhcphjbkeiagm";} # UBlock Origin + {id = "clngdbkpkpeebahjckkjfobafhncgmne";} # Stylus + {id = "cnojnbdhbhnkbcieeekonklommdnndci";} # Search By Image + {id = "doojmbjmlfjjnbmnoijecmcbfeoakpjm";} # NoScript + {id = "eimadpbcbfnmbkopoojfekhnkhdbieeh";} # Dark Reader + {id = "jinjaccalgkegednnccohejagnlnfdag";} # Violentmonkey + {id = "nibjojkomfdiaoajekhjakgkdhaomnch";} # IPFS Companion + {id = "nngceckbapebfimnlniiiahkandclblb";} # Bitwarden + {id = "pmcmeagblkinmogikoikkdjiligflglb";} # Privacy Redirect + ] + ++ optional config.nixfiles.modules.kde.enable { + id = "cimiefiiaegbelhefglklhhakcgmhkai"; # KDE Plasma Integration + }; + }; + }; + + services.psd.enable = true; + }; +} diff --git a/modules/nixfiles/common/console.nix b/modules/nixfiles/common/console.nix new file mode 100644 index 0000000..3c73695 --- /dev/null +++ b/modules/nixfiles/common/console.nix @@ -0,0 +1,6 @@ +{config, ...}: { + console = { + earlySetup = true; + useXkbConfig = config.services.xserver.enable; + }; +} diff --git a/modules/nixfiles/common/default.nix b/modules/nixfiles/common/default.nix new file mode 100644 index 0000000..d73ac53 --- /dev/null +++ b/modules/nixfiles/common/default.nix @@ -0,0 +1,19 @@ +_: { + imports = [ + ./documentation.nix + ./environment.nix + ./home-manager.nix + ./kernel.nix + ./locale.nix + ./networking.nix + ./nix.nix + ./secrets.nix + ./security.nix + ./services.nix + ./shell + ./systemd.nix + ./tmp.nix + ./users.nix + ./xdg.nix + ]; +} diff --git a/modules/nixfiles/common/documentation.nix b/modules/nixfiles/common/documentation.nix new file mode 100644 index 0000000..344d59d --- /dev/null +++ b/modules/nixfiles/common/documentation.nix @@ -0,0 +1,48 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; { + config = mkMerge [ + (mkIf this.isHeadful { + documentation = { + enable = true; + + dev.enable = true; + doc.enable = false; + info.enable = false; + nixos.enable = true; + + man = { + enable = true; + generateCaches = true; + man-db = { + enable = true; + manualPages = + (pkgs.buildEnv { + name = "man-paths"; + paths = with config; + environment.systemPackages ++ hm.home.packages; + pathsToLink = ["/share/man"]; + extraOutputsToInstall = ["man"]; + ignoreCollisions = true; + }) + .overrideAttrs (_: _: {__contentAddressed = true;}); + }; + }; + }; + + environment.sessionVariables = { + MANOPT = "--no-hyphenation"; + MANPAGER = "${pkgs.less}/bin/less -+F"; + }; + }) + (mkIf this.isHeadless { + hm.manual.manpages.enable = false; + documentation.enable = false; + }) + ]; +} diff --git a/modules/nixfiles/common/environment.nix b/modules/nixfiles/common/environment.nix new file mode 100644 index 0000000..9998441 --- /dev/null +++ b/modules/nixfiles/common/environment.nix @@ -0,0 +1,7 @@ +{lib, ...}: +with lib; { + environment = { + localBinInPath = true; + defaultPackages = mkForce []; + }; +} diff --git a/modules/nixfiles/common/home-manager.nix b/modules/nixfiles/common/home-manager.nix new file mode 100644 index 0000000..e4c5d3b --- /dev/null +++ b/modules/nixfiles/common/home-manager.nix @@ -0,0 +1,23 @@ +{ + config, + inputs, + lib, + ... +}: +with lib; { + imports = [ + inputs.home-manager.nixosModules.home-manager + (mkAliasOptionModule ["hm"] ["home-manager" "users" my.username]) + ]; + + hm.home = {inherit (config.system) stateVersion;}; + + home-manager = { + backupFileExtension = "bak"; + useUserPackages = true; + useGlobalPkgs = true; + verbose = true; + }; + + system.extraDependencies = [inputs.home-manager]; +} diff --git a/modules/nixfiles/common/kernel.nix b/modules/nixfiles/common/kernel.nix new file mode 100644 index 0000000..f6c096b --- /dev/null +++ b/modules/nixfiles/common/kernel.nix @@ -0,0 +1,32 @@ +{lib, ...}: +with lib; { + boot = { + kernelParams = ["hibernate=no"]; + + kernel.sysctl = { + "fs.file-max" = pow 2 17; + "fs.inotify.max_user_watches" = pow 2 19; + "fs.suid_dumpable" = 0; + "kernel.core_uses_pid" = 1; + "kernel.exec-shield" = 1; + "kernel.kptr_restrict" = 1; + "kernel.maps_protect" = 1; + "kernel.msgmax" = pow 2 16; + "kernel.msgmnb" = pow 2 16; + "kernel.pid_max" = pow 2 16; + "kernel.randomize_va_space" = 2; + "kernel.shmall" = pow 2 28; + "kernel.shmmax" = pow 2 28; + "kernel.sysrq" = 0; + "vm.dirty_background_bytes" = pow 2 22; + "vm.dirty_background_ratio" = 5; + "vm.dirty_bytes" = pow 2 22; + "vm.dirty_ratio" = 30; + "vm.min_free_kbytes" = pow 2 16; + "vm.mmap_min_addr" = pow 2 12; + "vm.overcommit_memory" = mkDefault 0; + "vm.overcommit_ratio" = mkDefault 50; + "vm.vfs_cache_pressure" = 50; + }; + }; +} diff --git a/modules/nixfiles/common/locale.nix b/modules/nixfiles/common/locale.nix new file mode 100644 index 0000000..34a738b --- /dev/null +++ b/modules/nixfiles/common/locale.nix @@ -0,0 +1,18 @@ +{lib, ...}: +with lib; { + i18n.defaultLocale = mkDefault "en_GB.UTF-8"; + + time.timeZone = mkDefault "Europe/Moscow"; + + # TODO Fcitx or UIM as a Japanese IME. + services.xserver = { + layout = comcat ["us" "ru"]; + xkbVariant = comcat ["" "phonetic"]; + xkbOptions = comcat [ + "terminate:ctrl_alt_bksp" + "caps:escape" + "compose:menu" + "grp:win_space_toggle" + ]; + }; +} diff --git a/modules/nixfiles/common/networking.nix b/modules/nixfiles/common/networking.nix new file mode 100644 index 0000000..0ff7e3d --- /dev/null +++ b/modules/nixfiles/common/networking.nix @@ -0,0 +1,66 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; { + hm.home.file.".digrc".text = '' + +answer + +multiline + +recurse + ''; + + networking = { + hostName = this.hostname; + hostId = substring 0 8 (builtins.hashString "md5" this.hostname); + domain = my.domain.shire; + + usePredictableInterfaceNames = false; + + useDHCP = false; + + nameservers = dns.const.quad9.default; + + hosts = { + "127.0.0.2" = mkForce []; + "::1" = mkForce []; + }; + + firewall = { + enable = true; + + logRefusedConnections = false; + logRefusedPackets = false; + + rejectPackets = false; + + allowPing = config.nixfiles.modules.profiles.headless.enable; + }; + }; + + environment = { + systemPackages = with pkgs; [dnsutils ldns myip rsync]; + + shellAliases = listToAttrs (map + ({ + name, + value, + }: + nameValuePair name "${pkgs.iproute2}/bin/${value}") [ + { + name = "bridge"; + value = "bridge -color=always"; + } + { + name = "ip"; + value = "ip -color=always"; + } + { + name = "tc"; + value = "tc -color=always"; + } + ]); + }; +} diff --git a/modules/nixfiles/common/nix.nix b/modules/nixfiles/common/nix.nix new file mode 100644 index 0000000..cc050f8 --- /dev/null +++ b/modules/nixfiles/common/nix.nix @@ -0,0 +1,173 @@ +{ + config, + inputs, + lib, + pkgs, + this, + ... +}: +with lib; let + nixfilesSrc = "${config.my.home}/src/nixfiles"; +in { + _module.args = let + importNixpkgs = nixpkgs: + import nixpkgs { + inherit (config.nixpkgs) localSystem crossSystem config; + }; + in rec { + pkgsMaster = importNixpkgs inputs.nixpkgs-master; + pkgsStable = importNixpkgs inputs.nixpkgs-stable; + pkgsRev = rev: sha256: + importNixpkgs (pkgs.fetchFromGitHub { + owner = "NixOS"; + repo = "nixpkgs"; + inherit rev sha256; + }); + pkgsPR = pr: pkgsRev "refs/pull/${toString pr}/head"; + pkgsLocal = importNixpkgs "${config.my.home}/src/nixpkgs"; + }; + + nix = let + filteredInputs = filterAttrs (n: _: n != "self") inputs; + in { + # https://github.com/NixOS/nix/blob/master/src/libutil/experimental-features.cc + extraOptions = '' + extra-experimental-features = ca-derivations + extra-experimental-features = flakes + extra-experimental-features = nix-command + extra-experimental-features = recursive-nix + flake-registry = ${inputs.flake-registry}/flake-registry.json + keep-derivations = true + keep-outputs = true + warn-dirty = false + ''; + + nixPath = + mapAttrsToList (n: v: "${n}=${v}") filteredInputs + ++ ["nixfiles=${nixfilesSrc}"]; + + registry = + mapAttrs (_: flake: {inherit flake;}) filteredInputs + // { + nixfiles.flake = inputs.self; + }; + + settings = { + trusted-users = ["root" "@wheel"]; + + substituters = [ + "https://azahi.cachix.org" + "https://cachix.cachix.org" + "https://mic92.cachix.org" + "https://nix-community.cachix.org" + "https://pre-commit-hooks.cachix.org" + ]; + trusted-public-keys = [ + "azahi.cachix.org-1:2bayb+iWYMAVw3ZdEpVg+NPOHCXncw7WMQ0ElX1GO3s=" + "cachix.cachix.org-1:eWNHQldwUO7G2VkjpnjDbWwy4KQ/HNxht7H4SSoMckM=" + "mic92.cachix.org-1:gi8IhgiT3CYZnJsaW7fxznzTkMUOn1RY4GmXdT/nXYQ=" + "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" + "pre-commit-hooks.cachix.org-1:Pkk3Panw5AW24TOv6kz3PvLhlH8puAsJTBbOPmBo7Rc=" + ]; + }; + }; + + nixpkgs = { + overlays = with inputs; [ + self.overlays.default + (_: super: + { + nix-bash-completions = + super.nix-bash-completions.overrideAttrs + (_: _: { + postPatch = '' + substituteInPlace _nix --replace 'nix nixos-option' 'nixos-option' + ''; + }); + nix-index = super.nix-index.override {nix = config.nix.package;}; + logcli = super.grafana-loki.overrideAttrs (_: _: { + subPackages = ["cmd/logcli"]; + preFixup = ""; + doCheck = false; + }); + helm = super.kubernetes-helm-wrapped.override { + plugins = with super.kubernetes-helmPlugins; [ + helm-diff + helm-secrets + ]; + }; + pgcli = super.pgcli.overrideAttrs (_: _: { + # https://github.com/NixOS/nixpkgs/pull/184533 + postPatch = '' + substituteInPlace setup.py \ + --replace "pgspecial>=1.13.1,<2.0.0" "pgspecial>=1.13.1" + ''; + }); + } + // (with super; let + np = nodePackages; + in { + dockerfile-language-server = np.dockerfile-language-server-nodejs; + editorconfig = editorconfig-core-c; + inherit (np) bash-language-server; + inherit (np) vim-language-server; + inherit (np) yaml-language-server; + json-language-server = np.vscode-json-languageserver; + k3d = kube3d; + lua-language-server = sumneko-lua-language-server; + nix-language-server = rnix-lsp; + telepresence = telepresence2; + tor-browser = tor-browser-bundle-bin; + })) + agenix.overlay + emacs-overlay.overlay + nur.overlay + xmonad-ng.overlay + ]; + + config.allowUnfree = true; + }; + + environment = { + sessionVariables.NIX_SHELL_PRESERVE_PROMPT = "1"; + + etc = { + nixpkgs.source = inputs.nixpkgs; + + gc-roots.text = + concatMapStrings (x: x + "\n") + (with inputs; [nixpkgs nixpkgs-master nixpkgs-stable]); + }; + + systemPackages = with pkgs; + optionals config.profile.headful [ + (pkgs.nixfiles.override { + nix = config.nix.package; + inherit nixfilesSrc; + }) + nix-top + nix-tree + ]; + }; + + hm.home.file.".nix-defexpr/default.nix".text = + optionalString this.isHeadful + ( + let + hostname = strings.escapeNixIdentifier this.hostname; + in '' + let + self = builtins.getFlake "nixfiles"; + configurations = self.nixosConfigurations; + local = configurations.${hostname}; + in rec { + inherit self; + inherit (self) inputs lib; + inherit (lib) my; + this = my.configurations.${hostname}; + inherit (local) config; + inherit (local.config.system.build) toplevel vm vmWithBootLoader manual; + } // configurations // local._module.args + '' + ); +} diff --git a/modules/nixfiles/common/secrets.nix b/modules/nixfiles/common/secrets.nix new file mode 100644 index 0000000..9e59716 --- /dev/null +++ b/modules/nixfiles/common/secrets.nix @@ -0,0 +1,47 @@ +{ + config, + inputs, + lib, + pkgs, + this, + ... +}: +with lib; { + imports = [ + inputs.agenix.nixosModule + (mkAliasOptionModule ["secrets"] ["age" "secrets"]) + ]; + + config = { + age = { + identityPaths = + if this.isHeadful + then ["${config.my.home}/.ssh/id_${my.ssh.type}"] + else + map (attr: attr.path) (filter (attr: attr.type == my.ssh.type) + config.services.openssh.hostKeys); + + # This can be used to auto-add all secrets, thus eleminating the need to + # specify path to each envrypted file. The drawback is that this will + # expose *all* secrets to all machines and try to decrypt them all even on + # machines where the secret will not be used. + # + # secrets = + # let + # secretsSourceDir = "${inputs.self}/age"; + # in + # mapAttrs' + # (name: _: + # nameValuePair name { + # file = "${secretsSourceDir}/${name}"; + # owner = mkDefault my.username; + # group = mkDefault config.my.group; + # }) + # (builtins.readDir secretsSourceDir); + }; + + environment.systemPackages = with pkgs; [agenix]; + + system.extraDependencies = [inputs.agenix]; + }; +} diff --git a/modules/nixfiles/common/security.nix b/modules/nixfiles/common/security.nix new file mode 100644 index 0000000..d47edc9 --- /dev/null +++ b/modules/nixfiles/common/security.nix @@ -0,0 +1,19 @@ +_: { + security = { + sudo = { + enable = true; + execWheelOnly = true; + wheelNeedsPassword = false; + extraConfig = '' + Defaults env_keep+="SSH_CONNECTION SSH_CLIENT SSH_TTY" + ''; + }; + + polkit.extraConfig = '' + polkit.addRule(function (action, subject) { + if (subject.isInGroup('wheel')) + return polkit.Result.YES; + }); + ''; + }; +} diff --git a/modules/nixfiles/common/services.nix b/modules/nixfiles/common/services.nix new file mode 100644 index 0000000..376c87d --- /dev/null +++ b/modules/nixfiles/common/services.nix @@ -0,0 +1,9 @@ +_: { + services = { + earlyoom.enable = true; + haveged.enable = true; + irqbalance.enable = true; + }; + + hardware.ksm.enable = true; +} diff --git a/modules/nixfiles/common/shell/default.nix b/modules/nixfiles/common/shell/default.nix new file mode 100644 index 0000000..7174443 --- /dev/null +++ b/modules/nixfiles/common/shell/default.nix @@ -0,0 +1,125 @@ +{ + lib, + pkgs, + this, + ... +}: +with lib; { + hm = { + programs = { + bash = { + enable = true; + + shellOptions = [ + "autocd" + "cdspell" + "checkjobs" + "checkwinsize" + "dirspell" + "extglob" + "globstar" + "histappend" + "histreedit" + "histverify" + ]; + + profileExtra = '' + export _PROFILE_SOURCED=1 + ''; + + initExtra = '' + set -o notify + + ${readFile ./functions.bash} + + GRC_ALIASES=true + source ${pkgs.grc}/etc/profile.d/grc.sh + + if [ -z $_PROFILE_SOURCED ] && [ -f $HOME/.profile ]; then + source $HOME/.profile + fi + ''; + + historyControl = ["ignoredups" "ignorespace"]; + }; + + command-not-found.enable = false; + + dircolors.enable = true; + }; + + home.packages = with pkgs; [grc]; + }; + + programs.command-not-found.enable = false; + + environment = { + shellAliases = + listToAttrs + (map + ({ + name, + value, + }: + nameValuePair name (with pkgs; let + pkg = + if this.isHeadful + then + (coreutils.overrideAttrs (_: super: { + patches = + super.patches + ++ [ + (fetchpatch { + url = "https://raw.githubusercontent.com/jarun/advcpmv/master/advcpmv-0.9-9.1.patch"; + sha256 = "sha256-d+SRT/R4xmfHLAdOr7m4R3WFiW64P5ZH6iqDvErYCyg="; + }) + ]; + })) + else coreutils; + in "${pkg}/bin/coreutils --coreutils-prog=${value}")) + ( + let + mkAlias = { + name ? head command, + command, + }: { + inherit name; + value = concatStringsSep " " command; + }; + + progressBar = optionalString this.isHeadful "--progress-bar"; + in [ + (mkAlias { + command = ["cp" "--interactive" "--recursive" progressBar]; + }) + (mkAlias {command = ["mv" "--interactive" progressBar];}) + (mkAlias {command = ["rm" "--interactive=once"];}) + (mkAlias {command = ["ln" "--interactive"];}) + (mkAlias {command = ["mkdir" "--parents"];}) + (mkAlias {command = ["rmdir" "--parents"];}) + (mkAlias { + name = "lower"; + command = ["tr" "'[:upper:]'" "'[:lower:]'"]; + }) + (mkAlias { + name = "upper"; + command = ["tr" "'[:lower:]'" "'[:upper:]'"]; + }) + (mkAlias { + name = "disk"; + command = [ + "df" + "--human-readable" + "--exclude-type=tmpfs" + "--exclude-type=devtmpfs" + "2>/dev/null" + ]; + }) + ] + )) + // genAttrs ["grep" "egrep" "fgrep"] + (name: "${pkgs.gnugrep}/bin/${name} --color=always"); + + systemPackages = with pkgs; [bash-completion bc gawk hr moreutils pv]; + }; +} diff --git a/modules/nixfiles/common/shell/functions.bash b/modules/nixfiles/common/shell/functions.bash new file mode 100644 index 0000000..c18104f --- /dev/null +++ b/modules/nixfiles/common/shell/functions.bash @@ -0,0 +1,73 @@ +_complete_alias() { + local alias_name=$1 + local base_function=$2 + local function_name=_alias_$alias_name + shift 2 + eval "$function_name() { + COMP_WORDS=( ${*@Q} \"\${COMP_WORDS[@]:1}\" ) + (( COMP_CWORD += $# - 1 )) + _completion_loader $1 + $base_function + }" + complete -F "$function_name" "$alias_name" +} + +function where() { + local s + s="$(type -P "$1")" + realpath "$s" +} +_complete_alias where _complete complete + +function what() { + local s + s="$(where "$1")" + printf "%s\n" "${s%/*/*}" +} +_complete_alias what _complete complete + +function cat() { + if (($# == 1)) && [[ -d $1 ]]; then + ll "$1" + else + command cat "$@" + fi +} + +function cd() { + builtin cd "$@" && + if ((${#FUNCNAME[@]} == 1)); then + ls + fi +} + +function mkcd() { + mkdir -p "$1" && builtin cd "$1" +} + +function mvcd() { + mv -i -- "$PWD" "$1" && builtin cd . +} + +function bak() { + local f + for f; do + cp -ai -- "$f" "$f.bak" + done +} + +function ubak() { + local f + for f; do + [[ $f == *.bak ]] || f="$f.bak" + mv -i -- "$f" "${f%.bak}" + done +} + +function dec2hex() { + printf "0x%X\n" "$1" +} + +function hex2dec() { + printf "%d\n" "0x$1" +} diff --git a/modules/nixfiles/common/systemd.nix b/modules/nixfiles/common/systemd.nix new file mode 100644 index 0000000..5c7282d --- /dev/null +++ b/modules/nixfiles/common/systemd.nix @@ -0,0 +1,22 @@ +{pkgs, ...}: { + hm.systemd.user.startServices = "sd-switch"; + + services.journald.extraConfig = '' + SystemMaxUse=5G + ''; + + systemd = let + extraConfig = '' + DefaultTimeoutStartSec=30s + DefaultTimeoutStopSec=15s + ''; + in { + inherit extraConfig; + user = {inherit extraConfig;}; + }; + + environment.sessionVariables = { + SYSTEMD_PAGER = "${pkgs.less}/bin/less"; + SYSTEMD_LESS = "FRSXMK"; + }; +} diff --git a/modules/nixfiles/common/tmp.nix b/modules/nixfiles/common/tmp.nix new file mode 100644 index 0000000..3fbf253 --- /dev/null +++ b/modules/nixfiles/common/tmp.nix @@ -0,0 +1,18 @@ +_: { + systemd.mounts = [ + { + what = "tmpfs"; + where = "/tmp"; + type = "tmpfs"; + mountConfig.Options = [ + "huge=within_size" + "mode=1777" + "noatime" + "nodev" + "nosuid" + "rw" + "size=25%" + ]; + } + ]; +} diff --git a/modules/nixfiles/common/users.nix b/modules/nixfiles/common/users.nix new file mode 100644 index 0000000..0878db6 --- /dev/null +++ b/modules/nixfiles/common/users.nix @@ -0,0 +1,21 @@ +{lib, ...}: +with lib; { + imports = [(mkAliasOptionModule ["my"] ["users" "users" my.username])]; + + users = { + mutableUsers = false; + + users = { + root.hashedPassword = "[REDACTED]"; + + ${my.username} = { + isNormalUser = true; + uid = 1000; + description = my.fullname; + inherit (my) hashedPassword; + openssh.authorizedKeys.keys = [my.ssh.key]; + extraGroups = ["wheel"]; + }; + }; + }; +} diff --git a/modules/nixfiles/common/xdg.nix b/modules/nixfiles/common/xdg.nix new file mode 100644 index 0000000..c05f5e3 --- /dev/null +++ b/modules/nixfiles/common/xdg.nix @@ -0,0 +1,86 @@ +{ + config, + lib, + this, + ... +}: +with lib; { + imports = let + withBase = s: ["home-manager" "users" my.username "xdg" s]; + in [ + (mkAliasOptionModule ["dirs" "cache"] (withBase "cacheHome")) + (mkAliasOptionModule ["dirs" "config"] (withBase "configHome")) + (mkAliasOptionModule ["dirs" "data"] (withBase "dataHome")) + (mkAliasOptionModule ["dirs" "state"] (withBase "stateHome")) + (mkAliasOptionModule ["userDirs"] (withBase "userDirs")) + ]; + + hm = { + xdg = mkMerge [ + { + enable = true; + + userDirs = let + inherit (config.my) home; + tmp = home + "/tmp"; + in { + enable = true; + + createDirectories = this.isHeadful; + + desktop = tmp; + documents = "${home}/doc"; + download = tmp; + music = tmp; + pictures = tmp; + videos = tmp; + templates = tmp; + publicShare = "${home}/share"; + }; + } + (mkIf this.isHeadful { + mimeApps = let + images = [ + "image/bmp" + "image/gif" + "image/jpeg" + "image/jpg" + "image/png" + "image/svg+xml" + "image/tiff" + "image/webp" + ]; + media = [ + "audio/aac" + "audio/flac" + "audio/mp3" + "audio/ogg" + "audio/wav" + "audio/webm" + "video/mkv" + "video/mp4" + "video/ogg" + "video/webm" + "video/x-matroska" + ]; + in { + enable = true; + + defaultApplications = + mkMerge + (mapAttrsToList (n: ms: genAttrs ms (_: ["${n}.desktop"])) { + aria2 = ["application/x-bittorrent" "x-scheme-handler/magnet"]; + emacsclient = ["x-scheme-handler/mailto"]; + firefox = [ + "text/html" + "x-scheme-handler/http" + "x-scheme-handler/https" + ]; + gwenview = images; + mpv = media; + }); + }; + }) + ]; + }; +} diff --git a/modules/nixfiles/curl.nix b/modules/nixfiles/curl.nix new file mode 100644 index 0000000..e7bee31 --- /dev/null +++ b/modules/nixfiles/curl.nix @@ -0,0 +1,38 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.curl; +in { + options.nixfiles.modules.curl.enable = + mkEnableOption "Wether to enable cURL."; + + config = mkIf cfg.enable { + hm.home.file.".curlrc".text = '' + connect-timeout = 60 + progress-bar + referer = ";auto" + remote-time + show-error + ''; + + environment.systemPackages = with pkgs; [ + curl + (writeShellScriptBin "0x0" '' + url="https://0x0.st" + form="file=@" + + if [ -t 0 ] && [ -n "$1" ]; then + form="$form$1" + else + form="$form-" + fi + + ${curl}/bin/curl --form "$form" "$url" + '') + ]; + }; +} diff --git a/modules/nixfiles/default.nix b/modules/nixfiles/default.nix new file mode 100644 index 0000000..8d26e7f --- /dev/null +++ b/modules/nixfiles/default.nix @@ -0,0 +1,80 @@ +{...}: { + imports = [ + ./acme.nix + ./alacritty.nix + ./alertmanager.nix + ./aria2.nix + ./aspell.nix + ./bat.nix + ./beets.nix + ./bluetooth.nix + ./broot.nix + ./chromium.nix + ./common + ./curl.nix + ./direnv.nix + ./docker.nix + ./dwm.nix + ./emacs.nix + ./endlessh-go.nix + ./endlessh.nix + ./fail2ban.nix + ./firefox + ./flatpak.nix + ./fonts.nix + ./games + ./git.nix + ./gnome.nix + ./gnupg.nix + ./gotify.nix + ./grafana.nix + ./htop.nix + ./hydra.nix + ./ipfs.nix + ./kde.nix + ./libvirtd.nix + ./lidarr.nix + ./loki.nix + ./lxc.nix + ./matrix + ./monitoring.nix + ./mpd.nix + ./mpv.nix + ./nextcloud.nix + ./nginx.nix + ./nmap.nix + ./node-exporter.nix + ./nsd.nix + ./openssh.nix + ./password-store.nix + ./podman.nix + ./postgresql.nix + ./profiles + ./prometheus.nix + ./promtail.nix + ./psd.nix + ./qutebrowser.nix + ./radarr.nix + ./radicale.nix + ./rss-bridge.nix + ./rtorrent.nix + ./searx.nix + ./shadowsocks.nix + ./soju.nix + ./sonarr.nix + ./sound.nix + ./subversion.nix + ./syncthing.nix + ./throttled.nix + ./tmux.nix + ./unbound.nix + ./vaultwarden.nix + ./vim + ./vscode.nix + ./wget.nix + ./wireguard.nix + ./x11.nix + ./xmonad.nix + ./zathura.nix + ]; +} diff --git a/modules/nixfiles/direnv.nix b/modules/nixfiles/direnv.nix new file mode 100644 index 0000000..aea7dd8 --- /dev/null +++ b/modules/nixfiles/direnv.nix @@ -0,0 +1,24 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.direnv; +in { + options.nixfiles.modules.direnv.enable = + mkEnableOption "Whether to enable direnv and Lorri."; + + config = mkIf cfg.enable { + hm = { + programs.direnv = { + enable = true; + nix-direnv.enable = true; + }; + + home.sessionVariables.DIRENV_LOG_FORMAT = ""; + }; + + services.lorri.enable = true; + }; +} diff --git a/modules/nixfiles/docker.nix b/modules/nixfiles/docker.nix new file mode 100644 index 0000000..d2e53d6 --- /dev/null +++ b/modules/nixfiles/docker.nix @@ -0,0 +1,34 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.docker; +in { + options.nixfiles.modules.docker.enable = + mkEnableOption "Whether to enable Docker."; + + config = mkIf cfg.enable { + secrets.containers-auth = { + file = "${inputs.self}/secrets/containers-auth"; + path = "${config.my.home}/.docker/config.json"; + owner = my.username; + }; + + virtualisation.docker.enable = true; + + environment.systemPackages = with pkgs; [docker-compose]; + + my.extraGroups = ["docker"]; + + hm.programs.bash = { + shellAliases.d = "${pkgs.docker}/bin/docker"; + + initExtra = mkAfter '' + _complete_alias d _docker docker + ''; + }; + }; +} diff --git a/modules/nixfiles/dwm.nix b/modules/nixfiles/dwm.nix new file mode 100644 index 0000000..86d15fb --- /dev/null +++ b/modules/nixfiles/dwm.nix @@ -0,0 +1,159 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.dwm; +in { + options.nixfiles.modules.dwm.enable = mkEnableOption "Whether to enable dwm."; + + config = mkIf cfg.enable { + nixfiles.modules.x11.enable = true; + + hm.xsession = { + enable = true; + + windowManager.command = let + pkg = pkgs.dwm.override { + conf = let + font = with config.fontScheme.monospaceFont; "${family}:size=${toString size}"; + colour = config.colourScheme; + in '' + static const unsigned int borderpx = 1; + static const unsigned int snap = 32; + static const int showbar = 1; + static const int topbar = 1; + + static const char *fonts[] = { + "${font}" + }; + + static const char *colors[][3] = { + [SchemeNorm] = { + "${colour.white}", + "${colour.black}", + "${colour.black}", + }, + [SchemeSel] = { + "${colour.black}", + "${colour.white}", + "${colour.white}", + }, + }; + + static const char *tags[] = { + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9" + }; + + static const Rule rules[] = { + { "Emacs", NULL, NULL, 1 << 0, 0, -1 }, + }; + + static const float mfact = 0.666; + static const int nmaster = 1; + static const int resizehints = 0; + static const int lockfullscreen = 1; + + static const Layout layouts[] = { + { "[]=", tile }, + { "><>", NULL }, + { "[M]", monocle }, + }; + + #define MODKEY Mod4Mask + #define TAGKEYS(KEY,TAG) \ + { MODKEY, KEY, view, { .ui = 1 << TAG } }, \ + { MODKEY|ControlMask, KEY, toggleview, { .ui = 1 << TAG } }, \ + { MODKEY|ShiftMask, KEY, tag, { .ui = 1 << TAG } }, \ + { MODKEY|ControlMask|ShiftMask, KEY, toggletag, { .ui = 1 << TAG } }, + + static char dmenumon[2] = "0"; + static const char *dmenucmd[] = { + "${pkgs.dmenu}/bin/dmenu_run", + "-m", dmenumon, + "-fn", "${font}", + "-nb", "${colour.black}", + "-nf", "${colour.white}", + "-sb", "${colour.white}", + "-sf", "${colour.black}", + NULL, + }; + static const char *termcmd[] = { + "${pkgs.alacritty}/bin/alacritty", + NULL, + }; + + static const Key keys[] = { + { MODKEY, XK_x, spawn, {.v = dmenucmd } }, + { MODKEY, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY|ShiftMask, XK_k, incnmaster, {.i = +1 } }, + { MODKEY|ShiftMask, XK_j, incnmaster, {.i = -1 } }, + { MODKEY, XK_comma, setmfact, {.f = -0.05} }, + { MODKEY, XK_period, setmfact, {.f = +0.05} }, + { MODKEY, XK_p, zoom, {0} }, + { MODKEY, XK_Tab, view, {0} }, + { MODKEY, XK_d, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_o, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_h, focusmon, {.i = -1 } }, + { MODKEY, XK_l, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_h, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_l, tagmon, {.i = +1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) + TAGKEYS( XK_4, 3) + TAGKEYS( XK_5, 4) + TAGKEYS( XK_6, 5) + TAGKEYS( XK_7, 6) + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) + { MODKEY|ShiftMask, XK_q, quit, {0} }, + }; + + static const Button buttons[] = { + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, + }; + ''; + }; + in "${pkg}/bin/dwm"; + }; + + hm.services.dwm-status = { + enable = true; + # package = pkgs.dwm-status.override { + # enableAlsaUtils = false; + # }; + order = ["audio" "backlight" "battery" "cpu_load" "network" "time"]; + }; + + services.xserver.displayManager.startx.enable = true; + }; +} diff --git a/modules/nixfiles/emacs.nix b/modules/nixfiles/emacs.nix new file mode 100644 index 0000000..230b965 --- /dev/null +++ b/modules/nixfiles/emacs.nix @@ -0,0 +1,69 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.emacs; +in { + options.nixfiles.modules.emacs.enable = + mkEnableOption "Whether to enable the GNU Emacs."; + + # TODO Nixify. + config = mkIf cfg.enable { + hm = { + home = { + packages = with pkgs; [ + cmigemo # :lang japanese + gcc # :lang (org +roam2) + gnuplot # :lang (org +gnuplot) + gnutls # :app irc + graphviz # :lang (org +roam2) + grip # :lang (markdown +grip) + maim # :lang (org +dragndrop) + pandoc # :lang org markdown latex + plantuml # :lang plantuml + pre-commit # :tools magit + sqlite # :lang (org +roam2) + texlive.combined.scheme-full # :lang org tex + xclip # :os (tty +osc) + ]; + + activation = { + symlinkMigemoFiles = '' + target="${config.dirs.data}/migemo" + [[ -L "$target" ]] && rm "$target" + ln -s ${pkgs.cmigemo}/share/migemo "$target" + ''; + symlinkSkkFiles = '' + target="${config.dirs.data}/skk" + [[ -L "$target" ]] && rm "$target" + ln -s ${pkgs.skk-dicts}/share "$target" + ''; + symlinkPlantumlFiles = '' + target="${config.dirs.data}/plantuml" + [[ -L "$target" ]] && rm "$target" + ln -s ${pkgs.plantuml}/lib "$target" + ''; + }; + }; + + programs.emacs = { + enable = true; + package = pkgs.emacs28.override {nativeComp = true;}; + extraPackages = p: + with p; [ + vterm # :term vterm + ]; + }; + + services.emacs = { + enable = true; + client.enable = true; + }; + }; + + fonts.fonts = with pkgs; [emacs-all-the-icons-fonts]; + }; +} diff --git a/modules/nixfiles/endlessh-go.nix b/modules/nixfiles/endlessh-go.nix new file mode 100644 index 0000000..8ab71ee --- /dev/null +++ b/modules/nixfiles/endlessh-go.nix @@ -0,0 +1,57 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.endlessh-go; +in { + options.nixfiles.modules.endlessh-go = { + enable = mkEnableOption "Whether to enable endlessh-go."; + + prometheusPort = mkOption { + description = "Prometheus port."; + type = with types; port; + default = 9119; + }; + }; + + config = let + port = 22; + in + mkIf cfg.enable { + assertions = [ + { + assertion = !(any (x: x == port) config.services.openssh.ports); + message = "Port ${toString port} is already occupied by OpenSSH"; + } + ]; + + systemd.services.endlessh-go = { + description = "Endlessh SSH Tarpit"; + requires = ["network-online.target"]; + serviceConfig = { + Restart = "always"; + ExecStart = concatStringsSep " " [ + "${pkgs.endlessh-go}/bin/endlessh-go" + "-conn_type=tcp4" + "-host=0.0.0.0" + "-port=${toString port}" + "-enable_prometheus" + "-prometheus_port=${toString cfg.prometheusPort}" + "-geoip_supplier=ip-api" + "-logtostderr" + "-v=1" + ]; + KillSignal = "SIGTERM"; + AmbientCapabilities = "CAP_NET_BIND_SERVICE"; + DynamicUser = true; + StateDirectory = "endlessh-go"; + }; + wantedBy = ["multi-user.target"]; + }; + + networking.firewall.allowedTCPPorts = [port]; + }; +} diff --git a/modules/nixfiles/endlessh.nix b/modules/nixfiles/endlessh.nix new file mode 100644 index 0000000..2871683 --- /dev/null +++ b/modules/nixfiles/endlessh.nix @@ -0,0 +1,45 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.endlessh; +in { + options.nixfiles.modules.endlessh.enable = + mkEnableOption "Whether to enable endlessh."; + + config = let + port = 22; + in + mkIf cfg.enable { + assertions = [ + { + assertion = !(any (x: x == port) config.services.openssh.ports); + message = "Port ${toString port} is already occupied by OpenSSH"; + } + ]; + + systemd.services.endlessh = { + description = "Endlessh SSH Tarpit"; + requires = ["network-online.target"]; + serviceConfig = { + Restart = "always"; + ExecStart = concatStringsSep " " [ + "${pkgs.endlessh}/bin/endlessh" + "-v" + "-4" + "-p ${toString port}" + ]; + KillSignal = "SIGTERM"; + AmbientCapabilities = "CAP_NET_BIND_SERVICE"; + DynamicUser = true; + StateDirectory = "endlessh"; + }; + wantedBy = ["multi-user.target"]; + }; + + networking.firewall.allowedTCPPorts = [port]; + }; +} diff --git a/modules/nixfiles/fail2ban.nix b/modules/nixfiles/fail2ban.nix new file mode 100644 index 0000000..56670e7 --- /dev/null +++ b/modules/nixfiles/fail2ban.nix @@ -0,0 +1,32 @@ +{ + config, + lib, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.fail2ban; +in { + options.nixfiles.modules.fail2ban.enable = + mkEnableOption "Whether to enable fail2ban service."; + + config = mkIf cfg.enable { + services.fail2ban = { + enable = true; + + bantime-increment = { + enable = true; + maxtime = "24h"; + rndtime = "8m"; + }; + + ignoreIP = + optionals (hasAttr "wireguard" this) + (with config.nixfiles.modules.wireguard; [ipv4.subnet ipv6.subnet]); + + jails.DEFAULT = '' + blocktype = DROP + ''; + }; + }; +} diff --git a/modules/nixfiles/firefox/default.nix b/modules/nixfiles/firefox/default.nix new file mode 100644 index 0000000..d7afdae --- /dev/null +++ b/modules/nixfiles/firefox/default.nix @@ -0,0 +1,52 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.firefox; +in { + options.nixfiles.modules.firefox.enable = mkEnableOption ""; + + config = mkIf cfg.enable { + hm = { + home = { + # sessionVariables.BROWSER = mkOverride 200 "firefox"; + + packages = with pkgs; [ + (writeShellScriptBin "firefox-vanilla" '' + ${config.hm.programs.firefox.package}/bin/firefox -p vanilla $@ + '') + profile-cleaner + ]; + }; + + programs.firefox = { + enable = true; + + package = pkgs.firefox.override { + cfg = with config.nixfiles.modules; { + enablePlasmaBrowserIntegration = kde.enable; + enableGnomeExtensions = gnome.enable; + }; + }; + + profiles.default = (import ./profile.nix) config.nixfiles.modules lib; + + extensions = with pkgs.nur.repos.rycee.firefox-addons; + [ + bitwarden + darkreader + ipfs-companion + noscript + privacy-redirect + stylus + ublock-origin + violentmonkey + ] + ++ optional config.nixfiles.modules.kde.enable plasma-integration; + }; + }; + }; +} diff --git a/modules/nixfiles/firefox/profile.nix b/modules/nixfiles/firefox/profile.nix new file mode 100644 index 0000000..3382b85 --- /dev/null +++ b/modules/nixfiles/firefox/profile.nix @@ -0,0 +1,479 @@ +modules: lib: +with lib; let + mkCssWithRoot = css: + mkMerge [ + (with modules.profiles.common.colourScheme; '' + :root { + --black: ${black}; + --red: ${red}; + --green: ${green}; + --yellow: ${yellow}; + --blue: ${blue}; + --magenta: ${magenta}; + --cyan: ${cyan}; + --white: ${white}; + --bright-black: ${brightBlack}; + --bright-red: ${brightRed}; + --bright-green: ${brightGreen}; + --bright-yellow: ${brightYellow}; + --bright-blue: ${brightBlue}; + --bright-magenta: ${brightMagenta}; + --bright-cyan: ${brightCyan}; + --bright-white: ${brightWhite}; + --background: ${background}; + --foreground: ${foreground}; + '') + (with modules.fonts.fontScheme; '' + --sans-serif-font-family: "${sansSerifFont.family}", "${sansSerifFontFallback.family}", sans-serif; + --sans-serif-font-size: ${toString sansSerifFont.size}; + --serif-font-family: "${serifFont.family}", "${serifFontFallback.family}", serif; + --serif-font-size: ${toString serifFont.size}; + --monospace-font-family: "${monospaceFont.family}", "${monospaceFontFallback.family}", monospace; + --monospace-font-size: ${toString monospaceFont.size}; + } + '') + (builtins.readFile css) + ]; +in { + id = 0; + + isDefault = true; + + userChrome = mkCssWithRoot ./userChrome.css; + + userContent = mkCssWithRoot ./userContent.css; + + settings = { + # Updates + # + "app.update.auto" = false; + "browser.search.update" = false; + "extensions.update.enabled" = false; + "extensions.update.autoUpdateDefault" = false; + # + "extensions.getAddons.cache.enabled" = false; + "extensions.getAddons.showPane" = false; + + # Telemetry + # + # https://bugzilla.mozilla.org/1195552 + "datareporting.healthreport.service.enabled" = false; + "datareporting.healthreport.uploadEnabled" = false; + "datareporting.policy.dataSubmissionEnabled" = false; + # https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/telemetry/internals/preferences.html + # https://medium.com/georg-fritzsche/data-preference-changes-in-firefox-58-2d5df9c428b5 + "toolkit.telemetry.unified" = false; + "toolkit.telemetry.server" = "data:,"; + "toolkit.telemetry.archive.enabled" = false; + "toolkit.telemetry.newProfilePing.enabled" = false; + "toolkit.telemetry.shutdownPingSender.enabled" = false; + "toolkit.telemetry.firstShutdownPing.enabled" = false; + "toolkit.telemetry.updatePing.enabled" = false; + "toolkit.telemetry.bhrPing.enabled" = false; # Background Hang Reporter + # https://blog.mozilla.org/data/2018/08/20/effectively-measuring-search-in-firefox + "toolkit.telemetry.coverage.opt-out" = true; + "toolkit.coverage.opt-out" = true; + "toolkit.coverage.endpoint.base" = ""; + + # Studies + # + # https://support.mozilla.org/en-US/kb/shield/ + "app.shield.optoutstudies.enabled" = false; + # https://mozilla.github.io/normandy/ + "app.normandy.enabled" = false; + "app.normandy.api_url" = ""; + + # Crash reports + # + "browser.tabs.crashReporting.sendReport" = false; + "breakpad.reportURL" = ""; + + # Captive Portal detection + # + # https://www.eff.org/deeplinks/2017/08/how-captive-portals-interfere-wireless-security-and-privacy + "captivedetect.canonicalURL" = ""; + "network.captive-portal-service.enabled" = false; + # https://bugzilla.mozilla.org/1460537 + "network.connectivity-service.enabled" = false; + + # Safe browsing + # + # https://feeding.cloud.geek.nz/posts/how-safe-browsing-works-in-firefox/ + # https://wiki.mozilla.org/Security/Safe_Browsing + # https://support.mozilla.org/kb/how-does-phishing-and-malware-protection-work + "browser.safebrowsing.malware.enabled" = false; + "browser.safebrowsing.passwords.enabled" = false; + "browser.safebrowsing.phishing.enabled" = false; + "browser.safebrowsing.downloads.enabled" = false; + "browser.safebrowsing.downloads.remote.enabled" = false; + "browser.safebrowsing.downloads.remote.url" = ""; + "browser.safebrowsing.downloads.remote.block_potentially_unwanted" = false; + "browser.safebrowsing.downloads.remote.block_uncommon" = false; + + # Implicit outbound + # + # https://developer.mozilla.org/docs/Web/HTTP/Link_prefetching_FAQ + "network.prefetch-next" = false; + # https://developer.mozilla.org/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control + "network.dns.disablePrefetch" = true; + "network.dns.disablePrefetchFromHTTPS" = true; + # + "network.predictor.enabled" = false; + "network.predictor.enable-prefetch" = false; + # https://news.slashdot.org/story/15/08/14/2321202/how-to-quash-firefoxs-silent-requests + "network.http.speculative-parallel-limit" = 0; + # https://www.bleepingcomputer.com/news/software/major-browsers-to-prevent-disabling-of-click-tracking-privacy-risk/ + "browser.send_pings" = false; + # https://trac.torproject.org/projects/tor/wiki/doc/TorifyHOWTO/WebBrowsers + "network.proxy.socks_remote_dns" = true; + # https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/26424 + "network.file.disable_unc_paths" = true; + + # Proxy + # + # https://blog.mozilla.org/security/2021/10/25/securing-the-proxy-api-for-firefox-add-ons/ + "network.proxy.failover_direct" = false; + # https://bugzilla.mozilla.org/buglist.cgi?bug_id=1732792,1733994,1733481 + "network.proxy.allow_bypass" = false; + + # DNS-over-HTTPS (DoH) + # + # https://hacks.mozilla.org/2018/05/a-cartoon-intro-to-dns-over-https/ + # https://wiki.mozilla.org/Security/DOH-resolver-policy + # https://blog.mozilla.org/mozilla/news/firefox-by-default-dns-over-https-rollout-in-canada/ + # https://www.eff.org/deeplinks/2020/12/dns-doh-and-odoh-oh-my-year-review-2020 ***/ + "network.trr.mode" = 5; + + # Location bar & search + # + "browser.fixup.alternate.enabled" = false; + # + "browser.urlbar.trimURLs" = false; + # + "browser.search.suggest.enabled" = false; + "browser.urlbar.suggest.searches" = false; + # https://bugzilla.mozilla.org/1348275 + "browser.urlbar.speculativeConnect.enabled" = false; + # https://bugzilla.mozilla.org/1642623 + "browser.urlbar.dnsResolveSingleWordsAfterSearch" = 0; + # https://blog.mozilla.org/data/2021/09/15/data-and-firefox-suggest/ + "browser.urlbar.suggest.quicksuggest.nonsponsored" = false; + "browser.urlbar.suggest.quicksuggest.sponsored" = false; + + # Search & form history + # https://blog.mindedsecurity.com/2011/10/autocompleteagain.html + # https://bugzilla.mozilla.org/381681 + "browser.formfill.enable" = false; + # https://wiki.mozilla.org/Firefox/Features/Form_Autofill + "extensions.formautofill.available" = "off"; + "extensions.formautofill.addresses.enabled" = false; + "extensions.formautofill.creditCards.available" = false; + "extensions.formautofill.creditCards.enabled" = false; + "extensions.formautofill.heuristics.enabled" = false; + + # Passwords + # + "signon.rememberSignons" = false; + + # Disk avoidance + # + # Disable caching to disk. + # This is a possible performance hit. Could be removed in favor of PSD? + "browser.cache.disk.enable" = false; + # + "browser.privatebrowsing.forceMediaMemoryCache" = true; + "media.memory_cache_max_size" = 65536; + # Extra session data + "browser.sessionstore.privacy_level" = 2; + # Reduce writes + "browser.sessionstore.interval" = 30000; + + # SSL/TLS + # + # Block connections (SSL_ERROR_UNSAFE_NEGOTIATION) to servers that don't support RFC 5746 + # https://wiki.mozilla.org/Security:Renegotiation + # https://datatracker.ietf.org/doc/html/rfc5746 + # https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-3555 + # https://www.ssllabs.com/ssl-pulse + "security.ssl.require_safe_negotiation" = true; + # Disable TLS1.3 0-RTT + # https://github.com/tlswg/tls13-spec/issues/1001 + # https://www.rfc-editor.org/rfc/rfc9001.html#name-replay-attacks-with-0-rtt + # https://blog.cloudflare.com/tls-1-3-overview-and-q-and-a/ + "security.tls.enable_0rtt_data" = false; + + # OCSP (Online Certificate Status Protocol) + # + # https://en.wikipedia.org/wiki/Ocsp + # https://scotthelme.co.uk/revocation-is-broken/ + # https://blog.mozilla.org/security/2013/07/29/ocsp-stapling-in-firefox/ + # https://www.imperialviolet.org/2014/04/19/revchecking.html + # "security.OCSP.require" = true; + + # HPKP (HTTP Public Key Pinning) + # + # https://blog.mozilla.org/security/2016/10/18/phasing-out-sha-1-on-the-public-web/ + "security.pki.sha1_enforcement_level" = 1; + # https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/16206 + "security.cert_pinning.enforcement_level" = 2; + # https://bugzilla.mozilla.org/buglist.cgi?bug_id=1429800,1670985 + # https://blog.mozilla.org/security/tag/crlite/ + "security.remote_settings.crlite_filters.enabled" = true; + "security.pki.crlite_mode" = 2; + + # Mixed content + # + "security.mixed_content.block_display_content" = true; + # Force HTTPS-only mode + # Exceptions are managed on the settings page + "dom.security.https_only_mode" = true; + "dom.security.https_only_mode_pbm" = true; + # https://bugzilla.mozilla.org/buglist.cgi?bug_id=1642387,1660945 + "dom.security.https_only_mode_send_http_background_request" = false; + + # UI + # + # Dispaly warning on the padlock for "broken security" + # https://wiki.mozilla.org/Security:Renegotiation + # https://bugzilla.mozilla.org/1353705 + "security.ssl.treat_unsafe_negotiation_as_broken" = true; + # https://github.com/pyllyukko/user.js/issues/210 + "browser.ssl_override_behavior" = 1; + # Display advanced information on Insecure Connection warning pages + "browser.xul.error_pages.expert_bad_cert" = true; + # Display "Not Secure" text on HTTP sites + "security.insecure_connection_text.enabled" = true; + # + "browser.privatebrowsing.infoEnabled" = false; + "browser.privatebrowsing.infoTitleEnabled" = false; + "browser.privatebrowsing.promoEnabled" = false; + "browser.privatebrowsing.promoTitleEnabled" = false; + "browser.privatebrowsing.vpnpromourl" = ""; + + # UX + # + "browser.urlbar.decodeURLsOnCopy" = true; + # + "browser.tabs.closeWindowWithLastTab" = true; + # + "general.autoScroll" = true; + "general.smoothScroll" = true; + + # Headers & referers + # + # "network.http.referer.XOriginPolicy" = 2; + # "network.http.referer.XOriginTrimmingPolicy" = 2; + + # Containers + # + # https://wiki.mozilla.org/Security/Contextual_Identity_Project/Containers + # https://addons.mozilla.org/firefox/addon/temporary-containers/ + # https://medium.com/@stoically/enhance-your-privacy-in-firefox-with-temporary-containers-33925cd6cd21 + # https://github.com/stoically/temporary-containers/wiki + "privacy.userContext.enabled" = true; + "privacy.userContext.ui.enabled" = true; + + # WebRTC + # + # Disable WebRTC + # https://browserleaks.com/webrtc + # https://groups.google.com/g/discuss-webrtc/c/6stQXi72BEU/m/2FwZd24UAQAJ + # https://datatracker.ietf.org/doc/html/draft-ietf-mmusic-mdns-ice-candidates#section-3.1.1 + # "media.peerconnection.enabled" = false; + + # DRM + # + # https://www.eff.org/deeplinks/2017/10/drms-dead-canary-how-we-just-lost-web-what-we-learned-it-and-what-we-need-do-next + "media.eme.enabled" = false; + # https://bugzilla.mozilla.org/show_bug.cgi?id=1451762#c55 + "browser.eme.ui.enabled" = false; + + # GMP (Gecko Media Plugins) + # + "media.gmp-provider.enabled" = false; + + # Disable autoplay of HTML5 media + "media.autoplay.enabled" = false; + "media.autoplay.default" = 5; + "media.autoplay.blocking_policy" = 2; + + # DOM (Document Object Model) + # + # Disable "Confirm you want to leave" dialog on page close + # https://developer.mozilla.org/docs/Web/Events/beforeunload + "dom.disable_beforeunload" = true; + # Prevent scripts from moving and resizing open windows + "dom.disable_window_move_resize" = true; + # Block popup windows + "dom.disable_open_during_load" = true; + # Limit events that can cause a popup + "dom.popup_allowed_events" = "click dblclick mousedown pointerdown"; + + # PDFjs + # + # "pdfjs.disabled" = true; + "pdfjs.enableScripting" = false; + + # Developer Tools + # + "devtools.accessibility.enabled" = false; + "devtools.application.enabled" = false; + "devtools.chrome.enabled" = true; + "devtools.dom.enabled" = true; + "devtools.enabled" = true; + "devtools.inspector.enabled" = true; + "devtools.jsonview.enabled" = true; + "devtools.memory.enabled" = true; + "devtools.netmonitor.enabled" = true; + "devtools.performance.enabled" = false; + "devtools.storage.enabled" = true; + "devtools.styleeditor.enabled" = true; + # This will prevent working with XUL + # https://gitlab.torproject.org/tpo/applications/tor-browser/-/issues/16222 + "devtools.debugger.enabled" = false; + "devtools.debugger.remote-enabled" = false; + + # Downloads + # + "browser.download.useDownloadDir" = false; + "browser.download.alwaysOpenPanel" = false; + "browser.download.manager.addToRecentDocs" = false; + "browser.download.autohideButton" = false; + + # Extensions + # + # https://mike.kaply.com/2012/02/21/understanding-add-on-scopes/ + # https://archive.is/DYjAM + "extensions.enabledScopes" = 5; + "extensions.autoDisableScopes" = 15; + # https://bugzilla.mozilla.org/buglist.cgi?bug_id=1659530,1681331 + "extensions.postDownloadThirdPartyPrompt" = false; + # Disable recommendations in about:addons panes + "extensions.htmlaboutaddons.recommendations.enabled" = false; + # Mozilla's trash + "extensions.pocket.enabled" = false; + "extensions.screenshots.disabled" = true; + "idenitity.fxaccounts.enabled" = false; + "reader.parse-on-load.enabled" = false; + + # ETP (Encahnced Tracking Protection) + # + # https://blog.mozilla.org/security/2021/02/23/total-cookie-protection/ + "browser.contentblocking.category" = "strict"; + "privacy.partition.serviceWorkers" = true; + + # Sanitize + # + "privacy.sanitize.sanitizeOnShutdown" = true; + "privacy.sanitize.timeSpan" = 0; + "privacy.clearOnShutdown.cache" = true; + "privacy.clearOnShutdown.cookies" = false; + "privacy.clearOnShutdown.downloads" = true; + "privacy.clearOnShutdown.formdata" = true; + "privacy.clearOnShutdown.history" = false; + "privacy.clearOnShutdown.offlineApps" = false; + "privacy.clearOnShutdown.sessions" = false; + "privacy.clearOnShutdown.siteSettings" = false; + "privacy.cpd.cache" = true; + "privacy.cpd.cookies" = false; + "privacy.cpd.downloads" = true; + "privacy.cpd.formdata" = true; + "privacy.cpd.history" = false; + "privacy.cpd.offlineApps" = false; + "privacy.cpd.passwords" = false; + "privacy.cpd.sessions" = false; + "privacy.cpd.siteSettings" = false; + + # Fingerprinting + # + # https://bugzilla.mozilla.org/418986 + # "privacy.resistFingerprinting" = true; + # https://bugzilla.mozilla.org/1448423 + # "browser.startup.blankWindow" = false; + + # Rice + # + "toolkit.legacyUserProfileCustomizations.stylesheets" = true; + # + "browser.startup.homepage" = "about:blank"; # TODO Custom? + "browser.startup.homepage_welcome_url" = ""; + "browser.startup.homepage_welcome_url.additional" = ""; + # + "browser.toolbars.bookmarks.visibility" = "never"; + # + "browser.tabs.inTitlebar" = 1; + # + "browser.newtabpage.enabled" = false; + "browser.newtabpage.enhanced" = false; + "browser.newtabpage.activity-stream.default.sites" = ""; + "browser.newtabpage.activity-stream.asrouter.disable-captive-portal-vpn-promo" = + true; + "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons" = false; + "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features" = + false; + "browser.newtabpage.activity-stream.discoverystream.enabled" = false; + "browser.newtabpage.activity-stream.feeds.discoverystreamfeed" = false; + "browser.newtabpage.activity-stream.feeds.places" = false; + "browser.newtabpage.activity-stream.feeds.sections" = false; + "browser.newtabpage.activity-stream.feeds.snippets" = false; + "browser.newtabpage.activity-stream.feeds.telemetry" = false; + "browser.newtabpage.activity-stream.feeds.topsites" = false; + "browser.newtabpage.activity-stream.showSearch" = false; + "browser.newtabpage.activity-stream.showSponsored" = false; + "browser.newtabpage.activity-stream.showSponsoredTopSites" = false; + "browser.newtabpage.activity-stream.telemetry" = false; + # + "layout.css.color-mix.enabled" = true; + # + "svg.context-properties.content.enabled" = true; + + # Annoyances + # + "browser.aboutConfig.showWarning" = false; + "browser.disableResetPrompt" = true; + "browser.newtabpage.introShown" = true; + "browser.onboarding.enabled" = false; + "browser.shell.checkDefaultBrowser" = false; + "browser.tabs.warnOnClose" = false; + "browser.tabs.warnOnCloseOtherTabs" = false; + "browser.tabs.warnOnOpen" = false; + "browser.tabs.warnOnQuitShortcut" = false; + "full-screen-api.warning.delay" = 0; + "full-screen-api.warning.timeout" = 0; + "permissions.default.shortcuts" = 2; + "security.dialog_enable_delay" = 0; + + # Geo + # + # Disable OS provider + "geo.provider.use_gpsd" = false; + # Use the Mozilla provider. Defaults to Google otherwise + "geo.provider.network.url" = "https://location.services.mozilla.com/v1/geolocate?key=%MOZILLA_API_KEY%"; + + # Unsorted + # + "accessibility.force_disabled" = 1; + "beacon.enabled" = false; + "browser.helperApps.deleteTempFileOnExit" = true; + "browser.pagethumbnails.capturing_disabled" = true; + "browser.region.network.url" = ""; + "browser.region.update.enabled" = false; + "browser.selfsupport.url" = ""; + "browser.uitour.enabled" = false; + "browser.uitour.url" = ""; + "intl.accept_languages" = "en-US, en"; + "layout.spellcheckDefault" = 2; + "middlemouse.contentLoadURL" = false; + "permissions.delegation.enabled" = false; + "permissions.manager.defaultsUrl" = ""; + "webchannel.allowObject.urlWhitelist" = ""; + "network.manage-offline-status" = false; + "xpinstall.signatures.required" = false; + + # Toolbar + # + "browser.uiCustomization.state" = '' + {"placements":{"widget-overflow-fixed-list":["ublock0_raymondhill_net-browser-action","_73a6fe31-595d-460b-a920-fcc0f8843232_-browser-action","_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action","_2e5ff8c8-32fe-46d0-9fc8-6b8986621f3c_-browser-action","_b7f9d2cd-d772-4302-8c3f-eb941af36f76_-browser-action","ipfs-firefox-addon_lidel_org-browser-action","addon_darkreader_org-browser-action","_7a7a4a92-a2a0-41d1-9fd7-1e92480d612d_-browser-action","_aecec67f-0d10-4fa7-b7c7-609a2db280cf_-browser-action"],"nav-bar":["back-button","forward-button","urlbar-container","save-to-pocket-button"],"toolbar-menubar":["menubar-items"],"TabsToolbar":["tabbrowser-tabs","new-tab-button","alltabs-button"],"PersonalToolbar":["personal-bookmarks"]},"seen":["addon_darkreader_org-browser-action","ipfs-firefox-addon_lidel_org-browser-action","plasma-browser-integration_kde_org-browser-action","ublock0_raymondhill_net-browser-action","_2e5ff8c8-32fe-46d0-9fc8-6b8986621f3c_-browser-action","_446900e4-71c2-419f-a6a7-df9c091e268b_-browser-action","_73a6fe31-595d-460b-a920-fcc0f8843232_-browser-action","_7a7a4a92-a2a0-41d1-9fd7-1e92480d612d_-browser-action","_aecec67f-0d10-4fa7-b7c7-609a2db280cf_-browser-action","_b7f9d2cd-d772-4302-8c3f-eb941af36f76_-browser-action","developer-button"],"dirtyAreaCache":["nav-bar","widget-overflow-fixed-list","toolbar-menubar","TabsToolbar","PersonalToolbar"],"currentVersion":17,"newElementCount":6} + ''; + }; +} diff --git a/modules/nixfiles/firefox/userChrome.css b/modules/nixfiles/firefox/userChrome.css new file mode 100644 index 0000000..4d83391 --- /dev/null +++ b/modules/nixfiles/firefox/userChrome.css @@ -0,0 +1,156 @@ +@-moz-document url(chrome://browser/content/browser.xul), url(chrome://browser/content/browser.xhtml) +{ + :root { + --toolbarbutton-border-radius: 0 !important; + --tab-border-radius: 0 !important; + --tab-block-margin: 0 !important; + --arrowpanel-border-radius: 0 !important; + } + + #PersonalToolbar toolbarbutton:not(:hover), + #bookmarks-toolbar-button:not(:hover) { + filter: grayscale(1) !important; + } + + .titlebar-spacer { + display: none !important; + } + + .tabbrowser-tab::after, + .tabbrowser-tab::before { + border: none !important; + } + + #urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity, + #urlbar[pageproxystate="valid"] > #identity-box.chromeUI, + #urlbar[pageproxystate="valid"] > #identity-box.extensionPage, + #urlbar-display-box { + border: none !important; + } + + .tab-close-button { + display: none !important; + } + + #tabbrowser-tabs:not([movingtab]) + > #tabbrowser-arrowscrollbox + > .tabbrowser-tab + > .tab-stack + > .tab-background[multiselected="true"], + #tabbrowser-tabs:not([movingtab]) + > #tabbrowser-arrowscrollbox + > .tabbrowser-tab + > .tab-stack + > .tab-background[selected="true"] { + background-image: none !important; + } + + #nav-bar:not([tabs-hidden="true"]) { + box-shadow: none; + } + + #tabbrowser-tabs[haspinnedtabs]:not([positionpinnedtabs]) + > #tabbrowser-arrowscrollbox + > .tabbrowser-tab[first-visible-unpinned-tab] { + margin-inline-start: 0 !important; + } + + .tab-background { + border-right: 0px solid var(--black) !important; + margin-left: -4px !important; + } + + .tabbrowser-tab:is([visuallyselected="true"], [multiselected]) + > .tab-stack + > .tab-background { + box-shadow: none !important; + } + + .tabbrowser-tab[last-visible-tab="true"] { + padding-inline-end: 0 !important; + } + + #tabs-newtab-button { + padding-left: 0 !important; + } + + #urlbar-input-container { + border: 3px solid var(--black) !important; + } + + #urlbar[focused="true"] > #urlbar-background { + box-shadow: none !important; + } + + #navigator-toolbox { + border: none !important; + } + + .bookmark-item .toolbarbutton-icon { + display: none; + } + + toolbarbutton.bookmark-item:not(.subviewbutton) { + min-width: 1.6em; + } + + #back-button, + #forward-button, + #context-bookmarklink, + #context-inspect-a11y, + #context-navigation, + #context-openlinkinusercontext-menu, + #context-pocket, + #context-print-selection, + #context-savelink, + #context-savelinktopocket, + #context-savepage, + #context-searchselect, + #context-selectall, + #context-sendimage, + #context-sendlinktodevice, + #context-sendlinktodevice, + #context-sendpagetodevice, + #context-viewsource, + #context_bookmarkTab, + #context_closeTabOptions, + #context_moveTabOptions, + #context_reopenInContainer, + #context_selectAllTabs, + #context_sendTabToDevice { + display: none !important; + } + + .identity-color-blue { + --identity-tab-color: var(--blue) !important; + --identity-icon-color: var(--blue) !important; + } + .identity-color-turquoise { + --identity-tab-color: var(--cyan) !important; + --identity-icon-color: var(--cyan) !important; + } + .identity-color-green { + --identity-tab-color: var(--green) !important; + --identity-icon-color: var(--green) !important; + } + .identity-color-yellow { + --identity-tab-color: var(--yellow) !important; + --identity-icon-color: var(--yellow) !important; + } + .identity-color-orange { + --identity-tab-color: var(--brightRed) !important; + --identity-icon-color: var(--brightRed) !important; + } + .identity-color-red { + --identity-tab-color: var(--red) !important; + --identity-icon-color: var(--red) !important; + } + .identity-color-pink { + --identity-tab-color: var(--brightMagenta) !important; + --identity-icon-color: var(--brightMagenta) !important; + } + .identity-color-purple { + --identity-tab-color: var(--magenta) !important; + --identity-icon-color: var(--magenta) !important; + } +} diff --git a/modules/nixfiles/firefox/userContent.css b/modules/nixfiles/firefox/userContent.css new file mode 100644 index 0000000..8426dc3 --- /dev/null +++ b/modules/nixfiles/firefox/userContent.css @@ -0,0 +1,210 @@ +@-moz-document url(about:blank), url(about:home), url(about:newtab), url(about:privatebrowsing) { + html, + body { + background: var(--background) !important; + } + + body { + display: none !important; + } +} + +@-moz-document media-document(all) { + body { + background-image: none !important; + background-color: var(--background) !important; + } +} + +@-moz-document url-prefix(https://gitlab.com/) +{ + code { + font-family: var(--monospace-font-family) !important; + font-size: var(--monospace-font-size) !important; + } +} + +@-moz-document url-prefix(https://github.com/), url-prefix(https://gist.github.com/) +{ + .blob-num, + .blob-code-inner { + font-family: var(--monospace-font-family) !important; + font-size: var(--monospace-font-size) !important; + } +} + +@-moz-document regexp("https:\/\/.*\.stackexchange\.com\/.*"), url-prefix(https://askubuntu.com/), url-prefix(https://serverfault.com/), url-prefix(https://stackoverflow.com/), url-prefix(https://superuser.com/) +{ + #footer, + #left-sidebar, + #noscript-warning, + #notify-container, + #post-form, + #sidebar, + .ai-start, + .bottom-notice, + .comment-user, + .d-flex.g4, + .d-flex.s-btn-group, + .js-add-link.comments-link, + .js-dismissable-hero, + .js-post-issue, + .js-show-link.comments-link, + .post-taglist, + .s-notice, + .s-topbar, + .site-header, + a.ws-nowrap { + display: none !important; + } + + #question-header .question-hyperlink { + font-family: var(--sans-serif-font-family); + } + + #mainbar { + width: 100% !important; + } + + #content { + margin-top: -50px !important; + border-width: 0 0 0 0 !important; + } +} + +@-moz-document regexp("https:\/\/habr\.com\/(ru|en)\/(article|company\/.*\/blog|post)\/.*") { + .Vue-Toastification__container, + .tm-article-presenter__meta, + .tm-article-snippet__labels, + .tm-article-sticky-panel, + .tm-base-layout__header, + .tm-block.tm-block_spacing-bottom, + .tm-block_spacing-around, + .tm-comment-footer, + .tm-comment__header, + .tm-footer, + .tm-footer-menu, + .tm-header, + .tm-notice, + .tm-page__header, + .tm-page__sidebar, + .tm-user-info, + vue-portal-target { + display: none !important; + } + + * { + transition: none !important; + } + + body { + font-family: var(--sans-serif-font-family) !important; + font-size: var(--sans-serif-font-size) !important; + } + + #app { + height: auto !important; + } + + .tm-article-presenter__footer, + .tm-page__main_has-sidebar { + margin-left: auto !important; + margin-right: auto !important; + max-width: 100% !important; + } + + .tm-page { + padding: 0 !important; + } + + .tm-comment__buttons { + margin: 0 !important; + } + + code { + font-family: var(--monospace-font-family) !important; + font-size: var(--monospace-font-size) !important; + } +} + +@-moz-document url-prefix(https://jisho.org) +{ + header, + footer { + display: none !important; + } +} + +@-moz-document url-prefix(https://search.nixos.org) +{ + .search-sidebar, + footer.container { + display: none !important; + } +} + +@-moz-document url-prefix(https://gog.com) +{ + .galaxy-section-wrapper { + display: none !important; + } +} + +@-moz-document url-prefix(https://steamdb.info) +{ + #steamdb-extension-protip { + display: none !important; + } +} + +@-moz-document regexp("https:\/\/\.*\.hh\.ru/.*") { + .HH-Supernova-Footer, + .index-dashboard-applicant__banners, + .notification-manager, + .resume-sidebar-background, + .supernova-overlay { + display: none !important; + } +} + +@-moz-document url-prefix(https://utaten.com/lyric) +{ + :root { + --kana-font-size: 20px; + --furigana-font-size: 14px; + } + + body { + font-family: var(--sans-serif-font-family) !important; + font-size: var(--kana-font-size) !important; + } + + .rb { + font-size: var(--kana-font-size) !important; + } + + .rt { + font-size: var(--furigana-font-size) !important; + } + + #footer__area, + #reviews, + #sidebar, + .btn_sbs, + .gaugeWrap, + .lyricData, + .movie_contents, + .newLyricWorkFooter, + .path::before, + .recommendAdTag, + .shareArea, + .sideBySideBanner, + .topBanner, + header { + display: none !important; + } + + #contents { + width: 100% !important; + } +} diff --git a/modules/nixfiles/flatpak.nix b/modules/nixfiles/flatpak.nix new file mode 100644 index 0000000..0dec763 --- /dev/null +++ b/modules/nixfiles/flatpak.nix @@ -0,0 +1,13 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.flatpak; +in { + options.nixfiles.modules.flatpak.enable = + mkEnableOption "Whether to enable Flatpak."; + + config = mkIf cfg.enable {services.flatpak.enable = true;}; +} diff --git a/modules/nixfiles/fonts.nix b/modules/nixfiles/fonts.nix new file mode 100644 index 0000000..29e2acb --- /dev/null +++ b/modules/nixfiles/fonts.nix @@ -0,0 +1,106 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.fonts; +in { + imports = [ + (mkAliasOptionModule ["fontScheme"] [ + "nixfiles" + "modules" + "fonts" + "fontScheme" + ]) + ]; + + options.nixfiles.modules.fonts = { + enable = mkEnableOption "Whether to enable fonts and their configurations."; + + fontScheme = let + mkFont = { + family, + style, + size, + }: { + family = mkOption { + description = "Family of the font."; + type = types.str; + default = family; + }; + style = mkOption { + description = "Style of the font."; + type = types.str; + default = style; + }; + size = mkOption { + description = "Size of the font."; + type = types.int; + default = size; + }; + }; + in { + serifFont = mkFont { + family = "Iosevka Etoile"; + style = "Regular"; + size = 16; + }; + + serifFontFallback = mkFont { + family = "Sarasa Gothic J"; + style = "Regular"; + size = 16; + }; + + sansSerifFont = mkFont { + family = "Iosevka Aile"; + style = "Regular"; + size = 16; + }; + + sansSerifFontFallback = mkFont { + family = "Sarasa Gothic J"; + style = "Regular"; + size = 16; + }; + + monospaceFont = mkFont { + family = "Iosevka"; + style = "Regular"; + size = 16; + }; + + monospaceFontFallback = mkFont { + family = "Sarasa Mono J"; + style = "Regular"; + size = 16; + }; + }; + }; + + config = mkMerge [ + (mkIf cfg.enable { + hm.fonts.fontconfig.enable = true; + + fonts = { + fonts = with pkgs; [iosevka sarasa-gothic]; + + fontconfig = { + enable = true; + + defaultFonts = { + monospace = ["Iosevka" "Sarasa Mono" "DejaVu Sans Mono" "Noto Sans Mono"]; + sansSerif = ["Iosevka Aile" "Sarasa Gothic" "DejaVu Sans" "Noto Sans"]; + serif = ["Iosevka Etoile" "Sarasa Gothic" "DejaVu Serif" "Noto Serif"]; + }; + }; + }; + }) + (mkIf (!cfg.enable) { + hm.fonts.fontconfig.enable = mkForce false; + fonts.fontconfig.enable = mkForce false; + }) + ]; +} diff --git a/modules/nixfiles/games/default.nix b/modules/nixfiles/games/default.nix new file mode 100644 index 0000000..532fc57 --- /dev/null +++ b/modules/nixfiles/games/default.nix @@ -0,0 +1,47 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.games; +in { + imports = [ + ./gamemode.nix + ./gog.nix + ./lutris.nix + ./mangohud.nix + ./minecraft.nix + ./steam-run.nix + ./steam.nix + ]; + + options.nixfiles.modules.games.enable32BitSupport = + mkEnableOption "Whether to enable support for games."; + + config = mkIf cfg.enable32BitSupport { + services = { + jack.alsa.support32Bit = config.services.jack.alsa.enable; + + pipewire.alsa.support32Bit = config.services.pipewire.alsa.enable; + + xserver.inputClassSections = [ + '' + Identifier "ds-touchpad" + Driver "libinput" + MatchProduct "Wireless Controller Touchpad" + Option "Ignore" "true" + '' + ]; + }; + + hardware = { + opengl = mkIf config.hardware.opengl.enable { + extraPackages32 = config.hardware.opengl.extraPackages; + driSupport32Bit = config.hardware.opengl.driSupport; + }; + + pulseaudio.support32Bit = config.hardware.pulseaudio.enable; + }; + }; +} diff --git a/modules/nixfiles/games/gamemode.nix b/modules/nixfiles/games/gamemode.nix new file mode 100644 index 0000000..a74b651 --- /dev/null +++ b/modules/nixfiles/games/gamemode.nix @@ -0,0 +1,13 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.games.gamemode; +in { + options.nixfiles.modules.games.gamemode.enable = + mkEnableOption "Whether to enable GameMode."; + + config = mkIf cfg.enable {programs.gamemode.enable = true;}; +} diff --git a/modules/nixfiles/games/gog.nix b/modules/nixfiles/games/gog.nix new file mode 100644 index 0000000..f1188a2 --- /dev/null +++ b/modules/nixfiles/games/gog.nix @@ -0,0 +1,19 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.games.gog; +in { + options.nixfiles.modules.games.gog.enable = + mkEnableOption + "Whether to enable GOG clients and the ability to run GOG games."; + + config = mkIf cfg.enable { + nixfiles.modules.games.steam-run.enable = true; + + hm.home.packages = with pkgs; [lgogdownloader]; + }; +} diff --git a/modules/nixfiles/games/lutris.nix b/modules/nixfiles/games/lutris.nix new file mode 100644 index 0000000..ec1eaa2 --- /dev/null +++ b/modules/nixfiles/games/lutris.nix @@ -0,0 +1,37 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.games.lutris; +in { + options.nixfiles.modules.games.lutris.enable = + mkEnableOption "Whether to enable Lutris."; + + config = mkIf cfg.enable { + nixfiles.modules.games = { + steam-run.enable = true; + gamemode.enable = true; + }; + + hm.home.packages = with pkgs; [ + (lutris.override { + lutris-unwrapped = lutris-unwrapped.override { + wine = buildFHSUserEnv { + # We don't really need Wine because Lutris downloads required + # runtime files for us. This feature is more robust because you can + # juggle different versions without manually rebuilding anything + # because nixpkgs cache was pruned. + name = "empty"; + }; + }; + steamSupport = false; + extraPkgs = _: [ + driversi686Linux.mesa # Battle.net + ]; + }) + ]; + }; +} diff --git a/modules/nixfiles/games/mangohud.nix b/modules/nixfiles/games/mangohud.nix new file mode 100644 index 0000000..21d5fd1 --- /dev/null +++ b/modules/nixfiles/games/mangohud.nix @@ -0,0 +1,13 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.games.mangohud; +in { + options.nixfiles.modules.games.mangohud.enable = + mkEnableOption "Whether to enable MangoHud."; + + config = mkIf cfg.enable {hm.programs.mangohud.enable = true;}; +} diff --git a/modules/nixfiles/games/minecraft.nix b/modules/nixfiles/games/minecraft.nix new file mode 100644 index 0000000..70a98ce --- /dev/null +++ b/modules/nixfiles/games/minecraft.nix @@ -0,0 +1,15 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.games.minecraft; +in { + options.nixfiles.modules.games.minecraft.enable = + mkEnableOption "Whether to enable Minecraft."; + + config = + mkIf cfg.enable {hm.home.packages = with pkgs; [jre nixfiles.UltimMC];}; +} diff --git a/modules/nixfiles/games/steam-run.nix b/modules/nixfiles/games/steam-run.nix new file mode 100644 index 0000000..983cc77 --- /dev/null +++ b/modules/nixfiles/games/steam-run.nix @@ -0,0 +1,59 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.games.steam-run; +in { + options.nixfiles.modules.games.steam-run = { + enable = mkEnableOption "Whether to enable native Steam runtime."; + + quirks = { + mountandblade = mkEnableOption ''Fix "Mount & Blade: Warband" issues.''; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules = { + games = { + enable32BitSupport = true; + gamemode.enable = true; + }; + }; + + hm.home.packages = with pkgs; [ + (steam.override { + extraLibraries = _: + with cfg.quirks; + optionals mountandblade [ + (glew.overrideAttrs (_: super: let + opname = super.pname; + in rec { + pname = "${opname}-mountandblade"; + inherit (super) version; + src = fetchurl { + url = "mirror://sourceforge/${opname}/${opname}-${version}.tgz"; + sha256 = "sha256-BN6R5+Z2MDm8EZQAlc2cf4gLq6ghlqd2X3J6wFqZPJU="; + }; + })) + (fmodex.overrideAttrs (_: super: let + opname = super.pname; + in rec { + pname = "${opname}-mountandblade"; + inherit (super) version; + installPhase = let + libPath = + makeLibraryPath [alsa-lib libpulseaudio stdenv.cc.cc]; + in '' + install -Dm755 api/lib/libfmodex64-${version}.so $out/lib/libfmodex64.so + patchelf --set-rpath ${libPath} $out/lib/libfmodex64.so + ''; + })) + ]; + }) + .run + ]; + }; +} diff --git a/modules/nixfiles/games/steam.nix b/modules/nixfiles/games/steam.nix new file mode 100644 index 0000000..c1d471e --- /dev/null +++ b/modules/nixfiles/games/steam.nix @@ -0,0 +1,21 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.games.steam; +in { + options.nixfiles.modules.games.steam.enable = + mkEnableOption "Whether to enable Steam runtime."; + + config = mkIf cfg.enable { + nixfiles.modules.games = { + enable32BitSupport = true; + gamemode.enable = true; + }; + + hm.home.packages = with pkgs; [steam]; + }; +} diff --git a/modules/nixfiles/git.nix b/modules/nixfiles/git.nix new file mode 100644 index 0000000..9008c2a --- /dev/null +++ b/modules/nixfiles/git.nix @@ -0,0 +1,125 @@ +{ + config, + lib, + inputs, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.git; +in { + options.nixfiles.modules.git.enable = + mkEnableOption "Whether to enable Git version control system."; + + config = mkIf cfg.enable { + secrets = { + glab-cli-config = { + file = "${inputs.self}/secrets/glab-cli-config"; + path = "${config.dirs.config}/glab-cli/config.yml"; + owner = my.username; + }; + gh-hosts = { + file = "${inputs.self}/secrets/gh-hosts"; + path = "${config.dirs.config}/gh/hosts.yml"; + owner = my.username; + }; + hut = { + file = "${inputs.self}/secrets/hut"; + path = "${config.dirs.config}/hut/config"; + owner = my.username; + }; + }; + + hm = { + home.packages = with pkgs; [glab hut]; + + programs = { + git = { + enable = true; + + package = pkgs.git.override { + sendEmailSupport = true; + withSsh = true; + }; + + userName = my.fullname; + userEmail = my.email; + signing = { + inherit (my.pgp) key; + signByDefault = true; + }; + + extraConfig = + { + advice.detachedHead = false; + color.ui = true; + core.whitespace = "trailing-space"; + diff = { + mnemonicPrefix = true; + renames = "copies"; + submodule = "log"; + }; + init.defaultBranch = "master"; + status.submoduleSummary = true; + github.user = my.username; + gitlab.user = my.username; + } + // mapAttrs' + (n: v: nameValuePair ''url "git@${v}:"'' {insteadOf = "${n}:";}) { + "bitbucket" = "bitbucket.com"; + "codeberg" = "codeberg.org"; + "github" = "github.com"; + "gitlab" = "gitlab.com"; + "sourcehut" = "git.sr.ht"; + }; + + aliases = let + git = "${config.hm.programs.git.package}/bin/git"; + curl = "${pkgs.curl}/bin/curl"; + in { + fuck = "!${git} reset --hard && ${git} clean -fdx"; + gud = ''commit -m "git gud"''; + wtc = "!${curl} -sq whatthecommit.com/index.txt | ${git} commit -F -"; + }; + + # All helper tool/editor generated files should go here. This must be + # kept relatively clean and void of any tooling/project-specific + # residual files. + ignores = [ + "*~" + ".ccls-cache/" + ".clangd/" + ".dir-locals.el" + ".gdb_history" + ".netrwhist" + "[._]*.s[a-v][a-z]" + "[._]*.sw[a-p]" + "[._]s[a-rt-v][a-z]" + "[._]ss[a-gi-z]" + "[._]sw[a-p]" + "\\#*\\#" + "compile_commands.json" + "cscope.*" + "vgcore.*" + ]; + }; + + gh = { + enable = true; + settings.git_protocol = "ssh"; + }; + + bash = { + shellAliases.gl = "${pkgs.glab}/bin/glab"; + initExtra = mkAfter "_complete_alias gl __start_glab glab"; + }; + }; + + xdg.configFile."glab-cli/aliases.yml".text = generators.toYAML {} { + ci = "pipeline ci"; + co = "mr checkout"; + li = "ci lint"; + }; + }; + }; +} diff --git a/modules/nixfiles/gnome.nix b/modules/nixfiles/gnome.nix new file mode 100644 index 0000000..2d0f6f6 --- /dev/null +++ b/modules/nixfiles/gnome.nix @@ -0,0 +1,65 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.gnome; +in { + options.nixfiles.modules.gnome.enable = + mkEnableOption "Whether to enable the GNOME desktop environment."; + + config = mkIf cfg.enable { + nixfiles.modules = { + gnupg.pinentry = mkForce "gnome"; + sound.enable = true; + x11.enable = true; + }; + + services = { + xserver = { + enable = true; + desktopManager.gnome.enable = true; + displayManager.gdm = { + enable = true; + wayland = false; + }; + }; + + gnome = { + core-os-services.enable = true; + core-shell.enable = true; + core-utilities.enable = false; + core-developer-tools.enable = false; + games.enable = false; + + chrome-gnome-shell.enable = false; + gnome-initial-setup.enable = false; + gnome-online-accounts.enable = false; + gnome-remote-desktop.enable = false; + gnome-settings-daemon.enable = true; + gnome-user-share.enable = false; + rygel.enable = false; + tracker-miners.enable = false; + tracker.enable = false; + }; + + dleyna-renderer.enable = false; + dleyna-server.enable = false; + }; + + environment = { + gnome = { + excludePackages = with pkgs.gnome; [ + geary + gnome-disk-utility + seahorse + sushi + ]; + }; + + systemPackages = with pkgs; [pinentry-gnome]; + }; + }; +} diff --git a/modules/nixfiles/gnupg.nix b/modules/nixfiles/gnupg.nix new file mode 100644 index 0000000..16b8264 --- /dev/null +++ b/modules/nixfiles/gnupg.nix @@ -0,0 +1,91 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.gnupg; +in { + options.nixfiles.modules.gnupg = { + enable = mkEnableOption "Whether to enable GnuPG."; + + pinentry = mkOption { + description = "Name of a pinentry implementation."; + type = types.str; + default = with config.nixfiles.modules; + if kde.enable + then "qt" + else if gnome.enable + then "gnome" + else "curses"; + }; + }; + + config = mkIf cfg.enable { + hm = { + programs.gpg = { + enable = true; + + settings = + { + display-charset = "utf-8"; + enable-progress-filter = true; + fixed-list-mode = true; + keyid-format = "0xlong"; + no-comments = true; + no-emit-version = true; + no-greeting = true; + with-fingerprint = true; + throw-keyids = false; + + use-agent = true; + + armor = true; + + no-random-seed-file = true; + + list-options = "show-uid-validity"; + verify-options = "show-uid-validity"; + } + // (let + cipherAlgos = ["AES256" "AES192" "AES"]; + compressionAlgos = ["ZLIB" "BZIP2" "ZIP" "Uncompressed"]; + digestAlgos = ["SHA512" "SHA384" "SHA256" "SHA224"]; + + cs = concatStringsSep " "; + in { + default-preference-list = + cs (digestAlgos ++ cipherAlgos ++ compressionAlgos); + + personal-cipher-preferences = cs cipherAlgos; + personal-compress-preferences = cs compressionAlgos; + personal-digest-preferences = cs digestAlgos; + + s2k-cipher-algo = head cipherAlgos; + s2k-digest-algo = head digestAlgos; + + digest-algo = head digestAlgos; + cert-digest-algo = head digestAlgos; + }); + }; + + services.gpg-agent = { + enable = true; + + enableSshSupport = true; + enableScDaemon = false; + + defaultCacheTtl = 999999; + defaultCacheTtlSsh = 999999; + maxCacheTtl = 999999; + maxCacheTtlSsh = 999999; + + grabKeyboardAndMouse = true; + + sshKeys = [my.pgp.grip]; + + pinentryFlavor = cfg.pinentry; + }; + }; + }; +} diff --git a/modules/nixfiles/gotify.nix b/modules/nixfiles/gotify.nix new file mode 100644 index 0000000..b2bf3ae --- /dev/null +++ b/modules/nixfiles/gotify.nix @@ -0,0 +1,66 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.gotify; +in { + options.nixfiles.modules.gotify = { + enable = mkEnableOption "Whether to enable Gotify."; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "gotify.${config.networking.domain}"; + }; + }; + + config = let + db = "gotify"; + in + mkIf cfg.enable { + nixfiles.modules = { + nginx = { + enable = true; + virtualHosts.${cfg.domain} = { + locations."/" = { + proxyPass = "http://127.0.0.1:${toString config.services.gotify.port}"; + proxyWebsockets = true; + }; + }; + }; + postgresql.enable = true; + }; + + services = { + gotify = { + enable = true; + port = 7665; + }; + + postgresql = { + ensureDatabases = [db]; + ensureUsers = [ + { + name = db; + ensurePermissions."DATABASE \"${db}\"" = "ALL PRIVILEGES"; + } + ]; + }; + }; + + systemd.services.gotify-server = { + after = ["network-online.target" "postgresql.service"]; + environment = { + GOTIFY_DATABASE_DIALECT = "postgres"; + GOTIFY_DATABASE_CONNECTION = concatStringsSep " " [ + "host=/run/postgresql" + "user=${db}" + "dbname=${db}" + "sslmode=disable" + ]; + }; + }; + }; +} diff --git a/modules/nixfiles/grafana.nix b/modules/nixfiles/grafana.nix new file mode 100644 index 0000000..4340f04 --- /dev/null +++ b/modules/nixfiles/grafana.nix @@ -0,0 +1,84 @@ +{ + config, + inputs, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.grafana; +in { + options.nixfiles.modules.grafana = { + enable = mkEnableOption "Whether to enable Grafana."; + + port = mkOption { + description = "Port."; + type = with types; port; + default = 30101; + }; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; nullOr str; + default = null; + }; + }; + + config = mkIf cfg.enable { + secrets = { + grafana-admin-password = { + file = "${inputs.self}/secrets/grafana-admin-password"; + owner = "grafana"; + group = "grafana"; + }; + grafana-key = { + file = "${inputs.self}/secrets/grafana-key"; + owner = "grafana"; + group = "grafana"; + }; + }; + + nixfiles.modules = { + nginx = { + enable = true; + virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://127.0.0.1:${toString cfg.port}"; + }; + postgresql.enable = true; + }; + + services = let + db = "grafana"; + in { + grafana = { + enable = true; + + inherit (cfg) domain port; + protocol = "http"; + addr = "127.0.0.1"; + + analytics.reporting.enable = false; + + database = { + type = "postgres"; + host = "/run/postgresql"; + name = db; + user = db; + }; + + security = with config.secrets; { + secretKeyFile = grafana-key.path; + adminPasswordFile = grafana-admin-password.path; + }; + }; + + postgresql = { + ensureDatabases = [db]; + ensureUsers = [ + { + name = db; + ensurePermissions."DATABASE \"${db}\"" = "ALL PRIVILEGES"; + } + ]; + }; + }; + }; +} diff --git a/modules/nixfiles/htop.nix b/modules/nixfiles/htop.nix new file mode 100644 index 0000000..9cb3e60 --- /dev/null +++ b/modules/nixfiles/htop.nix @@ -0,0 +1,57 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.htop; +in { + options.nixfiles.modules.htop.enable = + mkEnableOption "Whether to enable htop."; + + config = mkIf cfg.enable { + hm.programs.htop = { + enable = true; + + settings = with config.hm.lib.htop; { + fields = with fields; [ + PID + USER + PRIORITY + NICE + M_SIZE + M_RESIDENT + M_SHARE + STATE + PERCENT_CPU + PERCENT_MEM + TIME + COMM + ]; + account_guest_in_cpu_meter = 1; + detailed_cpu_time = 0; + enable_mouse = 0; + find_comm_in_cmdline = 1; + header_margin = 1; + hide_function_bar = 1; + hide_kernel_threads = 1; + hide_userland_threads = 1; + highlight_base_name = 1; + highlight_changes = 0; + highlight_changes_delay_secs = 1; + highlight_deleted_exe = 1; + highlight_megabytes = 1; + highlight_threads = 1; + shadow_other_users = 1; + show_cpu_frequency = 1; + show_cpu_usage = 1; + show_program_path = 0; + show_thread_names = 0; + strip_exe_from_cmdline = 1; + tree_view = 1; + tree_view_always_by_pid = 1; + update_process_names = 1; + }; + }; + }; +} diff --git a/modules/nixfiles/hydra.nix b/modules/nixfiles/hydra.nix new file mode 100644 index 0000000..4161eb3 --- /dev/null +++ b/modules/nixfiles/hydra.nix @@ -0,0 +1,56 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.hydra; +in { + options.nixfiles.modules.hydra = { + enable = mkEnableOption "Whether to enable Nginx."; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "hydra.${config.networking.domain}"; + }; + + port = mkOption { + description = "Port."; + type = with types; port; + default = 7754; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules = { + nginx = { + enable = true; + virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://127.0.0.1:${toString cfg.port}"; + }; + postgresql.enable = true; + }; + + services = let + db = "hydra"; + in { + hydra = { + enable = true; + listenHost = "127.0.0.1"; + inherit (cfg) port; + dbi = "dbi:Pg:dbname=${db};user=${db}"; + hydraURL = cfg.domain; + }; + + postgresql = { + ensureDatabases = [db]; + ensureUsers = [ + { + name = db; + ensurePermissions."DATABASE \"${db}\"" = "ALL PRIVILEGES"; + } + ]; + }; + }; + }; +} diff --git a/modules/nixfiles/ipfs.nix b/modules/nixfiles/ipfs.nix new file mode 100644 index 0000000..501a07c --- /dev/null +++ b/modules/nixfiles/ipfs.nix @@ -0,0 +1,168 @@ +{ + config, + lib, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.ipfs; + + swarmDefaultPort = 4001; + apiDefaultPort = 5001; + gatewayDefaultPort = 6001; +in { + options.nixfiles.modules.ipfs = { + enable = mkEnableOption "Whether to enable IPFS daemon."; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "ipfs.${config.networking.fqdn}"; + }; + + swarmPort = mkOption { + description = "Swarm port."; + type = with types; port; + default = + if this.isHeadless + then swarmDefaultPort + 990 + else swarmDefaultPort; + }; + + apiPort = mkOption { + description = "API port."; + type = with types; port; + default = + if this.isHeadless + then apiDefaultPort + 990 + else apiDefaultPort; + }; + + gatewayPort = mkOption { + description = "Gateway port."; + type = with types; port; + default = + if this.isHeadless + then gatewayDefaultPort + 990 + else gatewayDefaultPort; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + { + services.ipfs = { + enable = true; + + user = my.username; + inherit (config.my) group; + + dataDir = "${config.my.home}/.ipfs"; + + swarmAddress = let + port = toString cfg.swarmPort; + in + if this.isHeadless + then [ + "/ip4/127.0.0.1/tcp/${port}" + "/ip4/127.0.0.1/udp/${port}/quic" + ] + else [ + "/ip4/0.0.0.0/tcp/${port}" + "/ip6/::/tcp/${port}" + "/ip4/0.0.0.0/udp/${port}/quic" + "/ip6/::/udp/${port}/quic" + ]; + apiAddress = "/ip4/127.0.0.1/tcp/${toString cfg.apiPort}"; + gatewayAddress = "/ip4/127.0.0.1/tcp/${toString cfg.gatewayPort}"; + + autoMigrate = true; + autoMount = true; + emptyRepo = true; + enableGC = true; + + extraConfig = mkMerge [ + (let + filterAddresses = + [ + "/ip4/100.64.0.0/ipcidr/10" + "/ip4/169.254.0.0/ipcidr/16" + "/ip4/172.16.0.0/ipcidr/12" + "/ip4/192.0.0.0/ipcidr/24" + "/ip4/192.0.2.0/ipcidr/24" + "/ip4/192.168.0.0/ipcidr/16" + "/ip4/198.18.0.0/ipcidr/15" + "/ip4/198.51.100.0/ipcidr/24" + "/ip4/203.0.113.0/ipcidr/24" + "/ip4/240.0.0.0/ipcidr/4" + "/ip6/100::/ipcidr/64" + "/ip6/2001:2::/ipcidr/48" + "/ip6/2001:db8::/ipcidr/32" + "/ip6/fe80::/ipcidr/10" + ] + ++ optionals (!hasAttr "wireguard" this) [ + "/ip4/10.0.0.0/ipcidr/8" + "/ip6/fc00::/ipcidr/7" + ]; + in { + Addresses = with config.services.ipfs; { + # https://github.com/NixOS/nixpkgs/pull/165259 + # I think this shit broke inheritance... Gotta test more and make + # a PR I guess. + API = apiAddress; + Gateway = gatewayAddress; + Swarm = swarmAddress; + + NoAnnounce = filterAddresses; + }; + Swarm.AddrFilters = filterAddresses; + API.HTTPHeaders.Access-Control-Allow-Methods = ["GET" "POST" "PUT"]; + }) + (mkIf this.isHeadful { + API.HTTPHeaders.Access-Control-Allow-Origin = ["*"]; + }) + (mkIf this.isHeadless { + API.HTTPHeaders.Access-Control-Allow-Origin = ["https://${cfg.domain}" "https://api.${cfg.domain}"]; + }) + ]; + }; + + networking.firewall = rec { + allowedTCPPorts = [swarmDefaultPort]; + allowedUDPPorts = allowedTCPPorts; + }; + } + (mkIf this.isHeadless { + nixfiles.modules.nginx = { + enable = true; + virtualHosts = { + ${cfg.domain}.locations."/".proxyPass = "http://127.0.0.1:${toString cfg.gatewayPort}"; + "swarm.${cfg.domain}" = { + serverName = cfg.domain; + listen = [ + { + addr = "0.0.0.0"; + port = swarmDefaultPort; + } + { + addr = "[::0]"; + port = swarmDefaultPort; + } + ]; + locations."/".proxyPass = "http://127.0.0.1:${toString cfg.swarmPort}"; + }; + "api.${cfg.domain}" = { + # TODO Redirect "/" to "/webui" but keep other endpoints. + locations."/" = { + proxyPass = "http://127.0.0.1:${toString cfg.apiPort}"; + extraConfig = '' + if ($internal != 1) { + return 403; + } + ''; + }; + }; + }; + }; + }) + ]); +} diff --git a/modules/nixfiles/kde.nix b/modules/nixfiles/kde.nix new file mode 100644 index 0000000..571cb6d --- /dev/null +++ b/modules/nixfiles/kde.nix @@ -0,0 +1,27 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.kde; +in { + options.nixfiles.modules.kde.enable = + mkEnableOption "Whether to enable KDE Plasma 5."; + + config = mkIf cfg.enable { + nixfiles.modules = { + gnupg.pinentry = mkForce "qt"; + sound.enable = true; + x11.enable = true; + }; + + services.xserver = { + desktopManager.plasma5.enable = true; + displayManager.sddm.enable = true; + }; + + environment.systemPackages = with pkgs; [pinentry-qt]; + }; +} diff --git a/modules/nixfiles/libvirtd.nix b/modules/nixfiles/libvirtd.nix new file mode 100644 index 0000000..5ce37f0 --- /dev/null +++ b/modules/nixfiles/libvirtd.nix @@ -0,0 +1,40 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.libvirtd; +in { + options.nixfiles.modules.libvirtd.enable = + mkEnableOption "Wether to enable libvirtd."; + + config = mkIf cfg.enable { + virtualisation.libvirtd = { + enable = true; + + onBoot = "ignore"; + onShutdown = "shutdown"; + + qemu = { + package = pkgs.qemu_kvm; + runAsRoot = false; + + ovmf = { + enable = true; + packages = [pkgs.OVMFFull.fd]; + }; + + swtpm = { + enable = false; + package = pkgs.swtpm-tpm2; + }; + }; + }; + + environment.systemPackages = with pkgs; [virt-manager qemu-utils]; + + my.extraGroups = ["libvirtd"]; + }; +} diff --git a/modules/nixfiles/lidarr.nix b/modules/nixfiles/lidarr.nix new file mode 100644 index 0000000..54d1fe5 --- /dev/null +++ b/modules/nixfiles/lidarr.nix @@ -0,0 +1,27 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.lidarr; +in { + options.nixfiles.modules.lidarr = { + enable = mkEnableOption "Whether to enable Lidarr."; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "lidarr.${config.networking.fqdn}"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules.nginx = { + enable = true; + virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://127.0.0.1:8686"; + }; + + services.lidarr.enable = true; + }; +} diff --git a/modules/nixfiles/loki.nix b/modules/nixfiles/loki.nix new file mode 100644 index 0000000..27217bd --- /dev/null +++ b/modules/nixfiles/loki.nix @@ -0,0 +1,113 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.loki; +in { + options.nixfiles.modules.loki = { + # TODO Figure out why this shit refuses to work with my configuraiton. + enable = mkEnableOption "Whether to enable Loki."; + + port = mkOption { + description = "Port."; + type = with types; port; + default = 30171; + }; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = config.nixfiles.modules.monitoring.domain; + }; + + path = mkOption { + description = "Path."; + type = with types; str; + default = "/loki"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules.nginx = with cfg; { + enable = true; + virtualHosts.${domain}.locations.${path} = { + proxyPass = "http://127.0.0.1:${toString port}"; + extraConfig = '' + if ($internal != 1) { + return 403; + } + ''; + }; + }; + + services.loki = { + enable = true; + + configuration = rec { + auth_enabled = false; + + server = rec { + http_listen_address = "127.0.0.1"; + http_listen_port = cfg.port; + http_path_prefix = cfg.path; + + grpc_listen_address = "127.0.0.1"; + grpc_listen_port = http_listen_port + 1; + + log_level = "warn"; + }; + + common = rec { + path_prefix = "/var/lib/loki"; + storage.filesystem = { + chunks_directory = "${path_prefix}/chunker"; + rules_directory = "${path_prefix}/ruler"; + }; + replication_factor = 1; + instance_interface_names = ["lo"]; + ring = { + instance_addr = "127.0.0.1"; + kvstore.store = "inmemory"; + }; + }; + + ruler = { + rule_path = "${common.path_prefix}/ruler"; + storage = { + type = "local"; + local.directory = + pkgs.writeTextDir "ruler/ruler.yml" + (generators.toJSON {} {groups = [{name = "default";}];}); + }; + }; + + schema_config.configs = [ + { + from = "2020-01-01"; + store = "boltdb-shipper"; + object_store = "filesystem"; + schema = "v11"; + index = { + prefix = "index_"; + period = "24h"; + }; + chunks = { + prefix = "chunks_"; + period = "24h"; + }; + } + ]; + + analytics.reporting_enabled = false; + }; + }; + + systemd.tmpfiles.rules = [ + "d /var/lib/loki 0700 loki loki - -" + "d /var/lib/loki/ruler 0700 loki loki - -" + ]; + }; +} diff --git a/modules/nixfiles/lxc.nix b/modules/nixfiles/lxc.nix new file mode 100644 index 0000000..ea4d8a6 --- /dev/null +++ b/modules/nixfiles/lxc.nix @@ -0,0 +1,16 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.lxc; +in { + options.nixfiles.modules.lxc.enable = + mkEnableOption "Whether to enable LXC/LXD."; + + config = mkIf cfg.enable { + virtualisation.lxd.enable = true; + my.extraGroups = "lxd"; + }; +} diff --git a/modules/nixfiles/matrix/default.nix b/modules/nixfiles/matrix/default.nix new file mode 100644 index 0000000..bd221c4 --- /dev/null +++ b/modules/nixfiles/matrix/default.nix @@ -0,0 +1 @@ +_: {imports = [./dendrite.nix ./element.nix ./synapse.nix];} diff --git a/modules/nixfiles/matrix/dendrite.nix b/modules/nixfiles/matrix/dendrite.nix new file mode 100644 index 0000000..4e40e97 --- /dev/null +++ b/modules/nixfiles/matrix/dendrite.nix @@ -0,0 +1,148 @@ +{ + config, + lib, + inputs, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.matrix.dendrite; +in { + options.nixfiles.modules.matrix.dendrite = { + enable = mkEnableOption "Whether to enable Dendrite Matrix server."; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = config.networking.domain; + }; + }; + + config = mkIf cfg.enable { + secrets.dendrite-private-key = { + file = "${inputs.self}/secrets/dendrite-private-key"; + mode = "0444"; # User is dynamic. + }; + + nixfiles.modules = { + nginx = { + enable = true; + virtualHosts.${cfg.domain}.locations = { + "/_matrix".proxyPass = "http://127.0.0.1:${toString config.services.dendrite.httpPort}"; + "= /.well-known/matrix/server" = { + extraConfig = '' + add_header Content-Type application/json; + ''; + return = "200 '${ + generators.toJSON {} {"m.server" = "${cfg.domain}:443";} + }'"; + }; + "= /.well-known/matrix/client" = { + extraConfig = '' + add_header Content-Type application/json; + add_header Access-Control-Allow-Origin *; + ''; + return = "200 '${ + generators.toJSON {} { + "m.homeserver".base_url = "https://${cfg.domain}"; + } + }'"; + }; + }; + }; + postgresql.enable = true; + }; + + services = let + name = "dendrite"; + prefix = "${name}-"; + databaseNames = [ + "account" + "appservice" + "device" + "federation" + "key" + "media" + "mscs" + "room" + "sync" + ]; + databaseList = forEach databaseNames (x: concatStrings [prefix x]); + databaseAttr = genAttrs databaseNames (x: concatStrings [prefix x]); + + mkDatabaseConnection = database: "postgres://${name}@/${database}?sslmode=disable"; + in { + dendrite = { + enable = true; + httpPort = 8008; + settings = { + global = { + server_name = cfg.domain; + + private_key = config.secrets.dendrite-private-key.path; + + disable_federation = false; + }; + + app_service_api.database.connection_string = + mkDatabaseConnection databaseAttr.appservice; + + client_api = { + registration_disabled = true; + turn = {}; # TODO + }; + + federation_api.database.connection_string = + mkDatabaseConnection databaseAttr.federation; + + key_server.database.connection_string = + mkDatabaseConnection databaseAttr.key; + + media_api = { + database.connection_string = + mkDatabaseConnection databaseAttr.media; + base_path = "./media"; + }; + + mscs.database.connection_string = + mkDatabaseConnection databaseAttr.mscs; + + room_server.database.connection_string = + mkDatabaseConnection databaseAttr.room; + + sync_api.database.connection_string = + mkDatabaseConnection databaseAttr.sync; + + user_api = { + account_database.connection_string = + mkDatabaseConnection databaseAttr.account; + device_database.connection_string = + mkDatabaseConnection databaseAttr.device; + }; + + metrics.enabled = false; # TODO + }; + }; + + postgresql = { + ensureDatabases = databaseList; + ensureUsers = + map (x: { + inherit name; + ensurePermissions."DATABASE \"${x}\"" = "ALL PRIVILEGES"; + }) + databaseList; + }; + }; + + systemd.services.dendrite.serviceConfig.ExecStart = + mkForce + (concatStringsSep " " [ + "${pkgs.dendrite}/bin/dendrite-monolith-server" + "--config /run/dendrite/dendrite.yaml" + "--http-bind-address 127.0.0.1:${ + toString config.services.dendrite.httpPort + }" + ]); + }; +} diff --git a/modules/nixfiles/matrix/element.nix b/modules/nixfiles/matrix/element.nix new file mode 100644 index 0000000..79c9f97 --- /dev/null +++ b/modules/nixfiles/matrix/element.nix @@ -0,0 +1,59 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.matrix.element; +in { + options.nixfiles.modules.matrix.element = { + enable = mkEnableOption "Whether to enable Element Matrix web interface."; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; nullOr str; + default = "element.${config.networking.domain}"; + }; + + homeserver = mkOption { + description = "Default Matrix homeserver."; + type = with types; str; + default = my.domain.azahi; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = with config.nixfiles.modules.matrix; + (synapse.enable || dendrite.enable) && !(!synapse.enable && !dendrite.enable); + message = "Synapse or Dendrite must be enabled"; + } + ]; + + nixfiles.modules.nginx = with cfg; { + enable = true; + virtualHosts.${domain}.locations."/".root = pkgs.element-web.override { + conf = { + default_server_config."m.homeserver" = { + base_url = "https://${homeserver}"; + server_name = homeserver; + }; + disable_custom_urls = true; + disable_guests = true; + disable_login_language_selector = true; + disable_3pid_login = true; + brand = homeserver; + branding.authFooterLinks = [ + { + text = "Hosted on NixOS"; + url = "https://nixos.org"; + } + ]; + default_theme = "dark"; + }; + }; + }; + }; +} diff --git a/modules/nixfiles/matrix/synapse.nix b/modules/nixfiles/matrix/synapse.nix new file mode 100644 index 0000000..cde5f8e --- /dev/null +++ b/modules/nixfiles/matrix/synapse.nix @@ -0,0 +1,90 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.matrix.synapse; +in { + options.nixfiles.modules.matrix.synapse = { + enable = mkEnableOption "Whether to enable Synapse Matrix server."; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = config.networking.domain; + }; + }; + + config = let + bind_address = "127.0.0.1"; + port = 8448; + in + mkIf cfg.enable { + nixfiles.modules = { + nginx = { + enable = true; + virtualHosts.${cfg.domain}.locations = { + "~ ^(/_matrix|/_synapse/client)".proxyPass = "http://${bind_address}:${toString port}"; + "= /.well-known/matrix/server" = { + extraConfig = '' + add_header Content-Type application/json; + ''; + return = "200 '${ + generators.toJSON {} {"m.server" = "${cfg.domain}:443";} + }'"; + }; + "= /.well-known/matrix/client" = { + extraConfig = '' + add_header Content-Type application/json; + add_header Access-Control-Allow-Origin *; + ''; + return = "200 '${ + generators.toJSON {} { + "m.homeserver".base_url = "https://${cfg.domain}"; + } + }'"; + }; + }; + }; + postgresql.enable = true; + }; + + services = let + db = "synapse"; + in { + matrix-synapse = { + enable = true; + server_name = config.networking.domain; + + database_type = "psycopg2"; + database_name = db; + database_user = db; + + listeners = [ + { + inherit bind_address port; + type = "http"; + tls = false; + x_forwarded = true; + resources = [ + { + names = ["client" "federation"]; + compress = false; + } + ]; + } + ]; + }; + + postgresql.initialScript = pkgs.writeText "init-matrix-synapse.sql" '' + CREATE USER "${db}"; + CREATE DATABASE "${db}" WITH OWNER "${db}" + TEMPLATE template0 + LC_COLLATE = "C" + LC_CTYPE = "C"; + ''; + }; + }; +} diff --git a/modules/nixfiles/monitoring.nix b/modules/nixfiles/monitoring.nix new file mode 100644 index 0000000..6db74d4 --- /dev/null +++ b/modules/nixfiles/monitoring.nix @@ -0,0 +1,114 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.monitoring; +in { + options.nixfiles.modules.monitoring = { + enable = mkEnableOption '' + Whether to enable custom monitoring stack. + + Currently this configures and enables Grafana, Loki, Prometheus and + Alertmanager. + ''; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; nullOr str; + default = "monitoring.${config.networking.domain}"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules = { + grafana = { + enable = true; + inherit (cfg) domain; + }; + loki = { + enable = true; + inherit (cfg) domain; + }; + prometheus = { + enable = true; + inherit (cfg) domain; + }; + alertmanager = { + enable = true; + inherit (cfg) domain; + }; + }; + + services = { + grafana.provision = { + enable = true; + datasources = with config.nixfiles.modules; [ + { + name = "Prometheus"; + type = "prometheus"; + access = "proxy"; + url = with prometheus; "https://${domain}${path}"; + isDefault = true; + } + { + name = "Loki"; + type = "loki"; + access = "proxy"; + url = with loki; "https://${domain}${path}"; + } + ]; + # TODO Move dashboards to this repository. + dashboards = [ + { + name = "system"; + options.path = pkgs.fetchurl { + url = "https://gist.githubusercontent.com/azahi/b8951223e6850d88159b0c34749a20aa/raw/00c6928374b5d231dc3afe617165550868b8c233/System-1645616603173.json"; + sha256 = "sha256-vGlCOHT5Rp1K88Z8lkLGkvTDeFpgi967CSOb/797vwY="; + }; + } + { + name = "endlessh-go"; + options.path = pkgs.fetchurl { + url = "https://gist.githubusercontent.com/azahi/fd917e917fb53deacf3c6d7366bae2b2/raw/796530bd899e9328e423d18a462faf3c7d01c823/endlessh.json"; + sha256 = "sha256-/wJDiFlrHEa1f82pDFtG/T2GRKGlAur0dlQ8eeVJ3m4="; + }; + } + { + name = "unbound"; + options.path = pkgs.fetchurl { + url = "https://gist.githubusercontent.com/azahi/73fc27e22acc9a01898b2d3e21150aac/raw/37d03bc51253583d9265d6f2189242003092261e/Unbound-1645616052811.json"; + sha256 = "sha256-PXDCCa5qOg/Hj9CBId0OVUYmPlOjS05NxhDQ6zWxGp0="; + }; + } + { + name = "nginx"; + options.path = pkgs.fetchurl { + url = "https://raw.githubusercontent.com/nginxinc/nginx-prometheus-exporter/452953738bfcb8aac02ce6e6ac32f53479cad2aa/grafana/dashboard.json"; + sha256 = "sha256-HRDWsSQeLzybaJ1g5xZqtmhf8JPtGhv/6TD1aFsgJIw="; + }; + } + { + name = "postgersql"; + options.path = pkgs.fetchurl { + url = "https://raw.githubusercontent.com/lstn/misc-grafana-dashboards/051873e103119ff0edf9886dcdc0c6c0a3ee6702/dashboards/postgresql-database.json"; + sha256 = "sha256-awlj3BM3GaBBG/zJvxsVBRzoM8i6aY5dV34++DZFQHs="; + }; + } + ]; + }; + + loki.configuration.ruler.alertmanager_url = with config.nixfiles.modules.alertmanager; "https://${domain}${path}"; + + prometheus.alertmanagers = [ + { + scheme = "https"; + path_prefix = config.nixfiles.modules.alertmanager.path; + static_configs = [{targets = [cfg.domain];}]; + } + ]; + }; + }; +} diff --git a/modules/nixfiles/mpd.nix b/modules/nixfiles/mpd.nix new file mode 100644 index 0000000..b2962ab --- /dev/null +++ b/modules/nixfiles/mpd.nix @@ -0,0 +1,212 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.mpd; +in { + options.nixfiles.modules.mpd.enable = + mkEnableOption "Wether to enable MPD and its clients."; + + config = mkIf cfg.enable { + hm = { + home.packages = with pkgs; [mpc_cli]; + + services.mpd = { + enable = true; + + extraConfig = '' + restore_paused "yes" + + auto_update "no" + + replaygain "album" + replaygain_preamp "0" + replaygain_limit "yes" + + volume_normalization "no" + + zeroconf_enabled "no" + + audio_output { + type "pipewire" + name "piepwire" + } + ''; + }; + + programs.ncmpcpp = { + enable = true; + + settings = rec { + ncmpcpp_directory = "${config.hm.xdg.dataHome}/ncmpcpp"; + lyrics_directory = "${ncmpcpp_directory}/lyrics"; + + playlist_disable_highlight_delay = 1; + message_delay_time = 1; + + song_window_title_format = "{%a - }{%t}|{%f}"; + song_list_format = "{$6%t}|{$2%f}$1 $R{$8%b}$1 {$5%a}"; + song_columns_list_format = "(20)[red]{a} (30)[cyan]{b} (50)[blue]{t|f}"; + song_status_format = "{{$8%a$9{ $b-$/b $6%b$9 {(%y)} } - } '{%t}}|{%f}'"; + song_library_format = "{%n - }{%t}|{%f}"; + + now_playing_prefix = "$b"; + now_playing_suffix = "$/b"; + + selected_item_prefix = "$0"; + selected_item_suffix = "$9"; + modified_item_prefix = "$3> $9"; + + browser_playlist_prefix = "$2playlist$9 "; + browser_sort_format = "{%a - }{%t}|{%f} {(%l)}"; + + playlist_show_mpd_host = false; + playlist_show_remaining_time = false; + playlist_shorten_total_times = false; + playlist_separate_albums = false; + + playlist_display_mode = "classic"; + browser_display_mode = "classic"; + search_engine_display_mode = "classic"; + playlist_editor_display_mode = "classic"; + + incremental_seeking = true; + seek_time = 1; + + volume_change_step = 5; + + autocenter_mode = true; + centered_cursor = true; + + progressbar_look = "=*-"; + + default_place_to_search_in = "database"; + search_engine_default_search_mode = 1; + data_fetching_delay = false; + media_library_primary_tag = "album_artist"; + browser_sort_mode = "name"; + default_find_mode = "wrapped"; + default_tag_editor_pattern = "%n - %t"; + empty_tag_marker = "<blank>"; + tags_separator = " | "; + tag_editor_extended_numeration = true; + media_library_sort_by_mtime = false; + regular_expressions = "none"; + block_search_constraints_change_if_items_found = true; + + ignore_leading_the = true; + + enable_window_title = false; + + header_visibility = false; + statusbar_visibility = false; + titles_visibility = false; + + display_volume_level = false; + display_bitrate = false; + display_remaining_time = false; + + cyclic_scrolling = true; + lines_scrolled = 1; + + follow_now_playing_lyrics = false; + fetch_lyrics_for_current_song_in_background = false; + store_lyrics_in_song_dir = false; + + generate_win32_compatible_filenames = false; + allow_for_physical_item_deletion = false; + show_hidden_files_in_local_browser = false; + + screen_switcher_mode = "playlist, browser"; + startup_screen = "playlist"; + startup_slave_screen = ""; + startup_slave_screen_focus = false; + locked_screen_width_part = 50; + ask_for_locked_screen_width_part = true; + + jump_to_now_playing_song_at_start = false; + + ask_before_clearing_playlists = false; + + clock_display_seconds = false; + + mouse_support = false; + + external_editor = "${config.programs.vim.package}/bin/vim"; + use_console_editor = true; + + colors_enabled = true; + discard_colors_if_item_is_selected = true; + + empty_tag_color = "cyan"; + header_window_color = "cyan"; + volume_color = "cyan"; + state_line_color = "cyan"; + state_flags_color = "green"; + main_window_color = "blue"; + color1 = "cyan"; + color2 = "red"; + progressbar_color = "cyan"; + progressbar_elapsed_color = "white"; + statusbar_color = "yellow"; + window_border_color = "green"; + active_window_border = "red"; + }; + + bindings = [ + { + key = "j"; + command = "scroll_down"; + } + { + key = "k"; + command = "scroll_up"; + } + { + key = "J"; + command = ["select_item" "scroll_down"]; + } + { + key = "K"; + command = ["select_item" "scroll_up"]; + } + { + key = "h"; + command = ["previous_column" "master_screen"]; + } + { + key = "l"; + command = ["next_column" "slave_screen"]; + } + { + key = "g"; + command = "move_home"; + } + { + key = "G"; + command = "move_end"; + } + { + key = "d"; + command = [ + "delete_playlist_items" + "delete_browser_items" + "delete_stored_playlist" + ]; + } + { + key = "L"; + command = "show_lyrics"; + } + { + key = "H"; + command = "toggle_lyrics_fetcher"; + } + ]; + }; + }; + }; +} diff --git a/modules/nixfiles/mpv.nix b/modules/nixfiles/mpv.nix new file mode 100644 index 0000000..68628c5 --- /dev/null +++ b/modules/nixfiles/mpv.nix @@ -0,0 +1,133 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.mpv; +in { + options.nixfiles.modules.mpv.enable = mkEnableOption "Whether to enable MPV."; + + config = mkIf cfg.enable { + hm.programs = { + mpv = { + enable = true; + + package = with pkgs; + mpv-with-scripts.override { + scripts = with mpvScripts; [autoload mpv-autosub sponsorblock]; + }; + + bindings = { + "RIGHT" = "seek 10"; + "LEFT" = "seek -10"; + "UP" = "seek 60"; + "DOWN" = "seek -60"; + + "Shift+RIGHT" = "no-osd seek 1 exact"; + "Shift+LEFT" = "no-osd seek -1 exact"; + "Shift+UP" = "no-osd seek 5 exact"; + "Shift+DOWN" = "no-osd seek -5 exact"; + + "Alt+k" = "add sub-scale +0.1"; + "Alt+j" = "add sub-scale -0.1"; + + "B" = ''cycle-values background "#000000" "#ffffff"''; + }; + + profiles = { + "protocol.http".force-window = "immediate"; + "protocol.https".profile = "protocol.http"; + }; + + config = let + lang = comcat [ + "Japanese" + "japanese" + "jp" + "jpn" + "jaJP" + "ja-JP" + "English" + "english" + "en" + "eng" + "enUS" + "en-US" + "Russian" + "russian" + "ru" + "rus" + "ruRU" + "ru-RU" + ]; + in { + audio-display = "no"; + autofit-larger = "100%x95%"; + cursor-autohide = 1000; + force-seekable = "no"; + fullscreen = true; + load-unsafe-playlists = true; + msg-color = true; + msg-module = true; + prefetch-playlist = true; + save-position-on-quit = false; + screenshot-format = "png"; + screenshot-template = "%F [%p]"; + stop-screensaver = true; + term-osd-bar = true; + use-filedir-conf = true; + + osd-bar-align-y = 0; + osd-bar-h = 2; + osd-bar-w = 60; + osd-border-color = "#FF262626"; + osd-border-size = 2.5; + osd-color = "#FFFFFFFF"; + osd-duration = 2500; + osd-font-size = 40; + osd-fractions = true; + osd-level = 1; + osd-shadow-color = "#33000000"; + + osc = false; + + sub-auto = "fuzzy"; + sub-file-paths-append = "srt"; + sub-ass-force-margins = true; + sub-ass-force-style = "kerning=yes"; + sub-fix-timing = true; + sub-use-margins = true; + sub-font-size = 40; + sub-color = "#FFFFFFFF"; + sub-border-color = "#FF262626"; + sub-border-size = 2.5; + sub-shadow-offset = 1; + sub-shadow-color = "#33000000"; + sub-spacing = 0.5; + blend-subtitles = true; + + audio-file-auto = "fuzzy"; + volume = 100; + volume-max = 200; + + alang = lang; + slang = lang; + + ytdl = true; + ytdl-raw-options = ''sub-lang="${lang}",write-sub=''; + ytdl-format = "(bestvideo[height<=?1080][fps<=?60][protocol!=http_dash_segments])+(bestaudio[acodec=opus]/bestaudio)/best"; + }; + }; + + bash = { + shellAliases.cam = "${config.hm.programs.mpv.package}/bin/mpv av://v4l2:/dev/video0"; + + initExtra = mkAfter '' + _complete_alias cam _mpv mpv + ''; + }; + }; + }; +} diff --git a/modules/nixfiles/nextcloud.nix b/modules/nixfiles/nextcloud.nix new file mode 100644 index 0000000..d63f824 --- /dev/null +++ b/modules/nixfiles/nextcloud.nix @@ -0,0 +1,133 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.nextcloud; +in { + options.nixfiles.modules.nextcloud = { + enable = mkEnableOption "Whether to enable Nextcloud."; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "nextcloud.${config.networking.domain}"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules = { + nginx = { + enable = true; + virtualHosts.${cfg.domain} = {}; + }; + postgresql.enable = true; + }; + + services = let + db = "nextcloud"; + in { + nextcloud = mkMerge [ + { + enable = true; + package = pkgs.nextcloud23; + + hostName = cfg.domain; + + appstoreEnable = false; + + config = { + adminpassFile = null; # This needs to be set as secret. + + dbtype = "pgsql"; + dbhost = "/run/postgresql"; + dbuser = db; + dbname = db; + + defaultPhoneRegion = "RU"; + }; + + extraApps = let + mkNextcloudApp = { + name, + version, + sha256, + }: + pkgs.fetchNextcloudApp { + inherit name version sha256; + url = "https://github.com/nextcloud/${name}/archive/refs/tags/v${version}.tar.gz"; + }; + in { + contacts = mkNextcloudApp { + name = "contacts"; + version = "4.0.1"; + sha256 = "sha256-dXKsG8KmlUojeY5dUn/XsMD3KaSh4QcZFOGDdcqlSvE="; + }; + calendar = mkNextcloudApp { + name = "calendar"; + version = "3.0.5"; + sha256 = "sha256-aKUKm7fWJQxOWwma56Tv+GGIo+p0n30Nhoyt4XoxsjI="; + }; + files_rightclick = mkNextcloudApp { + name = "files_rightclick"; + version = "23.0.1"; + sha256 = "sha256-VYODzkvvGrtpyRoug/8UPKhAgfCx1ltP1JdGPiB/lts="; + }; + unsplash = mkNextcloudApp { + name = "unsplash"; + version = "1.2.4"; + sha256 = "sha256-KGSkBOrNu0nK0YvAPYaxEL/kZNoJQD1oBV2aUBxh6cI="; + }; + previewgenerator = mkNextcloudApp { + name = "previewgenerator"; + version = "3.4.1"; + sha256 = "sha256-IUdj0xWt5zHxQoiMv1bYyYTzekuOFrsRIe530QOwC/w="; + }; + bruteforcesettings = mkNextcloudApp { + name = "bruteforcesettings"; + version = "2.3.0"; + sha256 = "sha256-J7ujmiPaw8GI7vDfVPXEum2XAMWvahciP8C6iXgckdE="; + }; + }; + } + (mkIf config.nixfiles.modules.acme.enable { + https = true; + config.overwriteProtocol = "https"; + }) + ]; + + postgresql = { + ensureDatabases = [db]; + ensureUsers = [ + { + name = db; + ensurePermissions."DATABASE \"${db}\"" = "ALL PRIVILEGES"; + } + ]; + }; + }; + + systemd = { + services = { + nextcloud-setup.after = ["network-online.target" "postgresql.service"]; + + nextcloud-preview-generate-cron.serviceConfig = { + Type = "oneshot"; + User = "nextcloud"; + ExecStart = "${config.services.nextcloud.occ}/bin/nextcloud-occ preview:pre-generate"; + }; + }; + + timers.nextcloud-preview-generate = { + wantedBy = ["timers.target"]; + timerConfig = { + OnBootSec = "15m"; + OnUnitActiveSec = "15m"; + Unit = "nextcloud-preview-generate-cron.service"; + }; + }; + }; + }; +} diff --git a/modules/nixfiles/nginx.nix b/modules/nixfiles/nginx.nix new file mode 100644 index 0000000..35f5098 --- /dev/null +++ b/modules/nixfiles/nginx.nix @@ -0,0 +1,90 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.nginx; +in { + options.nixfiles.modules.nginx = { + enable = mkEnableOption "Whether to enable Nginx."; + + virtualHosts = mkOption { + description = "Attrset of virtual hosts."; + # Not sure how to "inherit" the type from the original Nixpkgs option. + # Just make sure it's compatible with service.nginx.virtualHosts, ok? + type = with types; anything; + default = null; + }; + }; + + config = mkIf cfg.enable { + services = { + nginx = { + enable = true; + enableReload = true; + + package = pkgs.nginxMainline; + + statusPage = true; + + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + + commonHttpConfig = concatStrings [ + '' + add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"; + '' + (optionalString (hasAttr "wireguard" this) + (with config.nixfiles.modules.wireguard; '' + geo $internal { + default 0; + 127.0.0.1/32 1; + ${ipv4.subnet} 1; + ${ipv6.subnet} 1; + } + '')) + ]; + + virtualHosts = + { + default = { + default = true; + rejectSSL = true; + locations."/".return = "444"; + }; + } + // (mkIf (cfg.virtualHosts != null) (mapAttrs (_: attr: + mkMerge [ + attr + (mkIf config.nixfiles.modules.acme.enable { + enableACME = true; + forceSSL = true; + }) + ]) + cfg.virtualHosts)); + }; + + fail2ban.jails = { + nginx-http-auth = '' + enabled = true + ''; + nginx-botsearch = '' + enabled = true + ''; + }; + + prometheus.exporters.nginx = { + enable = true; + listenAddress = mkDefault this.wireguard.ipv4.address; + port = mkDefault 9113; + }; + }; + + networking.firewall.allowedTCPPorts = [80 443]; + }; +} diff --git a/modules/nixfiles/nmap.nix b/modules/nixfiles/nmap.nix new file mode 100644 index 0000000..945cd59 --- /dev/null +++ b/modules/nixfiles/nmap.nix @@ -0,0 +1,42 @@ +{ + config, + lib, + pkgs, + inputs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.nmap; +in { + options.nixfiles.modules.nmap.enable = + mkEnableOption "Whether to enable Nmap."; + + config = mkIf cfg.enable { + hm.home = { + file = { + ".nmap/scripts/vulners/vulners.nse".source = "${inputs.nmap-vulners}/vulners.nse"; + ".nmap/scripts/vulscan/vulscan.nse".source = "${inputs.nmap-vulscan}/vulscan.nse"; + }; + packages = with pkgs; [nmap nmap-formatter]; + activation.regenerateNmapScriptDatabase = with pkgs; '' + # declare -a vulscandbs=( + # "cve" + # "exploitdb" + # "openvas" + # "osvdb" + # "scipvuldb" + # "securityfocus" + # "securitytracker" + # "xforce" + # ) + # for i in "''${vulscandbs[@]}"; do + # ${curl}/bin/curl \ + # -o "$HOME/.nmap/scripts/vulscan/$i.csv" \ + # "https://www.computec.ch/projekte/vulscan/download/$i.csv" + # done + + ${nmap}/bin/nmap --script-updatedb + ''; + }; + }; +} diff --git a/modules/nixfiles/node-exporter.nix b/modules/nixfiles/node-exporter.nix new file mode 100644 index 0000000..794c309 --- /dev/null +++ b/modules/nixfiles/node-exporter.nix @@ -0,0 +1,35 @@ +{ + config, + lib, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.node-exporter; +in { + options.nixfiles.modules.node-exporter.enable = + mkEnableOption "Whether to enable Prometheus Node Exporter."; + + config = mkIf cfg.enable { + services.prometheus.exporters.node = { + enable = true; + listenAddress = mkDefault this.wireguard.ipv4.address; + port = 9100; + enabledCollectors = [ + "buddyinfo" + "ethtool" + "interrupts" + "ksmd" + "lnstat" + "logind" + "mountstats" + "network_route" + "processes" + "qdisc" + "systemd" + "tcpstat" + "zoneinfo" + ]; + }; + }; +} diff --git a/modules/nixfiles/nsd.nix b/modules/nixfiles/nsd.nix new file mode 100644 index 0000000..f328b5c --- /dev/null +++ b/modules/nixfiles/nsd.nix @@ -0,0 +1,157 @@ +{ + config, + inputs, + lib, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.nsd; +in { + options.nixfiles.modules.nsd = { + enable = mkEnableOption "Whether to enable NSD."; + + fqdn = mkOption { + description = "FQDN of this nameserver."; + type = with types; str; + default = "ns.${config.networking.domain}"; + }; + }; + + config = mkIf cfg.enable { + services = { + nsd = { + enable = true; + interfaces = with this; [ipv4.address ipv6.address]; + ipTransparent = true; + ratelimit.enable = true; + + # TODO DNSSEC. + zones = let + dns = inputs.dns-nix.lib; + in + with dns.combinators; let + ips = hostname: + with my.configurations.${hostname}; { + A = [(a ipv4.address)]; + AAAA = [(aaaa ipv6.address)]; + }; + + # TODO Try moving DKIM keys somewhere in "my" or secrets maybe? + mkEmailEntries = { + domain ? my.domain.shire, + dkimKey ? null, + }: { + MX = [(mx.mx 10 "${domain}.")]; + TXT = [(spf.strict ["a" "mx"])]; + DMARC = [ + { + p = "quarantine"; + sp = "quarantine"; + rua = ["mailto:admin+rua@${domain}"]; + ruf = ["mailto:admin+ruf@${domain}"]; + } + ]; + DKIM = optional (dkimKey != null) { + selector = "mail"; + p = dkimKey; + }; + }; + + mkZone = { + domain, + sldIps ? (ips "manwe"), + extra ? {}, + }: { + ${domain}.data = dns.toString domain ({ + TTL = 60 * 60; + + SOA = { + nameServer = "${cfg.fqdn}."; + adminEmail = "admin+dns@${my.domain.shire}"; + serial = 2022081122; + }; + + NS = with my.domain; ["ns1.${shire}" "ns2.${shire}"]; + + CAA = letsEncrypt "admin+caa@${my.domain.shire}"; + } + // sldIps + // extra); + }; + in + mkMerge [ + (mkZone { + domain = my.domain.shire; + extra = + (mkEmailEntries { + dkimKey = "[DKIM]"; + }) + // { + subdomains = rec { + manwe = ips "manwe"; + "*.manwe" = manwe; + varda = ips "varda"; + "*.varda" = varda; + yavanna = ips "yavanna"; + "*.yavanna" = yavanna; + + ns1 = manwe; + # ns2 = varda; + + flood = yavanna; + gotify = manwe; + monitoring = manwe; + radicale = varda; + rss-bridge = varda; + vaultwarden = varda; + }; + }; + }) + (mkZone { + domain = my.domain.azahi; + extra = + (mkEmailEntries { + dkimKey = "[DKIM]"; + }) + // { + subdomains = { + github.CNAME = ["github.com/${my.username}"]; + gitlab.CNAME = ["gitlab.com/${my.username}"]; + }; + }; + }) + (mkZone { + domain = my.domain.gondor; + extra = + (mkEmailEntries { + dkimKey = "[DKIM]"; + }) + // { + subdomains.frodo = ips "manwe"; + }; + }) + (mkZone { + domain = my.domain.rohan; + extra = + (mkEmailEntries { + dkimKey = "[DKIM]"; + }) + // { + subdomains.frodo = ips "manwe"; + }; + }) + ]; + }; + + fail2ban.jails.nsd = '' + enabled = true + ''; + }; + + networking.firewall = rec { + allowedTCPPorts = [53]; + allowedUDPPorts = allowedTCPPorts; + }; + }; +} diff --git a/modules/nixfiles/openssh.nix b/modules/nixfiles/openssh.nix new file mode 100644 index 0000000..2f1559e --- /dev/null +++ b/modules/nixfiles/openssh.nix @@ -0,0 +1,52 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.openssh; +in { + options.nixfiles.modules.openssh = { + client.enable = mkEnableOption "Whether to enable OpenSSH client."; + server.enable = mkEnableOption "Whether to enable OpenSSH server."; + }; + + config = mkMerge [ + (mkIf cfg.client.enable { + hm = { + home.packages = with pkgs; [mosh sshfs]; + + programs.ssh = { + enable = true; + controlMaster = "auto"; + controlPersist = "24H"; + hashKnownHosts = true; + serverAliveCountMax = 30; + serverAliveInterval = 60; + }; + }; + }) + (mkIf cfg.server.enable { + programs.mosh.enable = true; + + services = let + port = 22022; + in { + openssh = { + enable = true; + ports = [port]; + logLevel = "VERBOSE"; + permitRootLogin = "no"; + passwordAuthentication = false; + }; + + fail2ban.jails.sshd = '' + enabled = true + mode = aggressive + port = ${toString port} + ''; + }; + }) + ]; +} diff --git a/modules/nixfiles/password-store.nix b/modules/nixfiles/password-store.nix new file mode 100644 index 0000000..19e2f2a --- /dev/null +++ b/modules/nixfiles/password-store.nix @@ -0,0 +1,33 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.password-store; +in { + options.nixfiles.modules.password-store.enable = + mkEnableOption "Whether to enable pass."; + + config = mkIf cfg.enable { + hm.programs = { + password-store = { + enable = true; + + package = pkgs.pass.withExtensions (p: with p; [pass-otp]); + + settings.PASSWORD_STORE_DIR = "${config.my.home}/.password-store"; + }; + + # A dirty little hack to make completions for "otp" to work. + bash.initExtra = let + completions = "${config.hm.programs.password-store.package}/share/bash-completion/completions"; + in + mkAfter '' + source ${completions}/pass-otp + source ${completions}/pass + ''; + }; + }; +} diff --git a/modules/nixfiles/podman.nix b/modules/nixfiles/podman.nix new file mode 100644 index 0000000..6c8b7e5 --- /dev/null +++ b/modules/nixfiles/podman.nix @@ -0,0 +1,35 @@ +{ + config, + lib, + inputs, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.podman; +in { + options.nixfiles.modules.podman.enable = + mkEnableOption "Whether to enable Podman."; + + config = mkIf cfg.enable { + secrets.containers-auth = { + file = "${inputs.self}/secrets/containers-auth"; + path = "${config.dirs.config}/containers/auth.json"; + owner = my.username; + }; + + virtualisation.podman.enable = true; + + environment.systemPackages = with pkgs; [podman-compose]; + + my.extraGroups = ["podman"]; + + hm.programs.bash = { + shellAliases.p = "${pkgs.podman}/bin/podman"; + + initExtra = mkAfter '' + _complete_alias p __start_podman podman + ''; + }; + }; +} diff --git a/modules/nixfiles/postgresql.nix b/modules/nixfiles/postgresql.nix new file mode 100644 index 0000000..23623af --- /dev/null +++ b/modules/nixfiles/postgresql.nix @@ -0,0 +1,53 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.postgresql; +in { + options.nixfiles.modules.postgresql.enable = + mkEnableOption "Whether to enable PostgeSQL."; + + config = mkIf cfg.enable { + hm = { + home.sessionVariables.PSQLRC = config.hm.xdg.configFile."psqlrc".target; + + xdg.configFile."psqlrc".text = '' + \set QUIET 1 + + \timing + \x auto + \pset null '[NULL]' + \set PROMPT1 '%[%033[1m%]%M %n@%/%R%[%033[0m%]% λ ' + \set PROMPT2 ' … > ' + \set VERBOSITY verbose + \set HISTCONTROL ignoredups + \set HISTFILE /dev/null + + \unset QUIET + ''; + }; + + services = { + postgresql = { + enable = true; + package = pkgs.postgresql_14; + # TODO Test if this is still required. + authentication = '' + local all all trust + host all all 127.0.0.1/32 trust + host all all ::1/128 trust + ''; + }; + + prometheus.exporters.postgres = { + enable = true; + listenAddress = mkDefault this.wireguard.ipv4.address; + port = mkDefault 9187; + }; + }; + }; +} diff --git a/modules/nixfiles/profiles/common.nix b/modules/nixfiles/profiles/common.nix new file mode 100644 index 0000000..dd287dc --- /dev/null +++ b/modules/nixfiles/profiles/common.nix @@ -0,0 +1,103 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.profiles.common; +in { + imports = [ + (mkAliasOptionModule ["colourScheme"] [ + "nixfiles" + "modules" + "profiles" + "common" + "colourScheme" + ]) + ]; + + options.nixfiles.modules.profiles.common = { + enable = mkEnableOption "The most common profiles of all profiles."; + + colourScheme = let + mkColour = default: + mkOption { + type = types.str; + inherit default; + description = "Color in a standard hexademical notation."; + example = "#000000"; + }; + in rec { + black = mkColour "#161719"; + red = mkColour "#cc6666"; + green = mkColour "#b5bd68"; + yellow = mkColour "#f0c674"; + blue = mkColour "#81a2be"; + magenta = mkColour "#b294bb"; + cyan = mkColour "#8abeb7"; + white = mkColour "#c5c8c6"; + + brightBlack = mkColour "#969896"; + brightRed = mkColour "#cc6666"; + brightGreen = mkColour "#b5bd68"; + brightYellow = mkColour "#f0c674"; + brightBlue = mkColour "#81a2be"; + brightMagenta = mkColour "#b294bb"; + brightCyan = mkColour "#8abeb7"; + brightWhite = mkColour "#ffffff"; + + background = black; + foreground = white; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = !(with this; isHeadless && isHeadful); + message = '' + The configuration cannot be both "headful" and "headless" at the same + time. + ''; + } + ]; + + profile = with this; { + headless = isHeadless; + headful = isHeadful; + }; + + nixfiles.modules = { + htop.enable = true; + tmux.enable = true; + vim.enable = true; + }; + + hm.home.language = { + collate = "C"; + messages = "C"; + }; + + programs.less = { + enable = true; + envVariables.LESSHISTFILE = "-"; + }; + + environment.systemPackages = with pkgs; [ + cryptsetup + ddrescue + file + git + gnupg + lshw + lsof + pciutils + psmisc + tree + usbutils + util-linux + ]; + }; +} diff --git a/modules/nixfiles/profiles/default.nix b/modules/nixfiles/profiles/default.nix new file mode 100644 index 0000000..3df88f8 --- /dev/null +++ b/modules/nixfiles/profiles/default.nix @@ -0,0 +1,5 @@ +{lib, ...}: { + imports = [./common.nix ./dev ./headful.nix ./headless.nix]; + + config.nixfiles.modules.profiles.common.enable = lib.mkDefault true; +} diff --git a/modules/nixfiles/profiles/dev/common.nix b/modules/nixfiles/profiles/dev/common.nix new file mode 100644 index 0000000..d6c44ea --- /dev/null +++ b/modules/nixfiles/profiles/dev/common.nix @@ -0,0 +1,290 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.profiles.dev.common; +in { + options.nixfiles.modules.profiles.dev.common.enable = mkEnableOption ""; + + config = mkIf cfg.enable { + nixfiles.modules = { + bat.enable = true; + curl.enable = true; + direnv.enable = true; + git.enable = true; + gnupg.enable = true; + nmap.enable = true; + wget.enable = true; + }; + + hm.home = { + file = { + ".editorconfig".text = '' + root = true + + [*] + charset = utf-8 + end_of_line = lf + indent_size = 4 + indent_style = space + insert_final_newline = true + max_line_length = 80 + trim_trailing_whitespace = true + + [*.nix] + indent_size = 2 + indent_style = space + + [*.{S,s,asm}] + indent_size = 4 + indent_style = tab + + [*.{C,H,c,c++,cc,cpp,cxx,h,h++,hh,hpp,hxx}] + indent_size = 4 + indent_style = tab + + [*.{bash,sh}] + indent_size = 4 + indent_style = tab + + [*.{cl,clj,el,l,lisp,lsp,rkt,scm,ss}] + indent_size = unset + indent_style = unset + + [*.go] + indent_size = 4 + indent_style = tab + + [*.{py,pyx}] + indent_size = 4 + indent_style = space + + [*.{hs,lhs}] + indent_size = 2 + indent_style = space + + [*.{html,xhtml,xml}] + indent_size = 4 + indent_style = tab + + [*.json] + indent_size = 2 + indent_style = space + + [*.{yaml,yml}] + indent_size = 2 + indent_style = space + + [*.{toml,tml}] + indent_size = 4 + indent_style = space + + [*.{py,pyx}] + indent_size = 4 + indent_style = space + max_line_length = 72 + + [*.zig] + indent_size = 4 + indent_style = tab + + [configure.ac] + indent_size = 4 + indent_style = tab + + [{Makefile*,*.mk}] + indent_size = 4 + indent_style = tab + + [{CMakeLists.txt,*.cmake}] + indent_size = 8 + indent_style = tab + + [*.tex] + indent_size = 4 + indent_style = tab + + [*.{md,adoc,rtf,txt}] + indent_size = 4 + indent_style = tab + ''; + + ".ghc/ghci.conf".source = ./ghci.conf; + + ".stack/config.yaml".text = generators.toYAML {} { + templates.params = rec { + author-name = my.fullname; + author-email = my.email; + copyright = "Copyright (c) ${author-name} <${author-email}>"; + github-username = my.username; + }; + }; + + ".clang-format".text = generators.toYAML {} { + AccessModifierOffset = -4; + AlignAfterOpenBracket = "Align"; + AlignConsecutiveAssignments = "Consecutive"; + AlignConsecutiveBitFields = "Consecutive"; + AlignConsecutiveDeclarations = "Consecutive"; + AlignConsecutiveMacros = "Consecutive"; + AlignEscapedNewlines = "Right"; + AlignOperands = "Align"; + AlignTrailingComments = false; + AllowAllArgumentsOnNextLine = false; + AllowAllConstructorInitializersOnNextLine = true; + AllowAllParametersOfDeclarationOnNextLine = true; + AllowShortBlocksOnASingleLine = "Never"; + AllowShortCaseLabelsOnASingleLine = false; + AllowShortEnumsOnASingleLine = false; + AllowShortFunctionsOnASingleLine = "None"; + AllowShortIfStatementsOnASingleLine = "Never"; + AllowShortLambdasOnASingleLine = "Inline"; + AllowShortLoopsOnASingleLine = false; + AlwaysBreakAfterDefinitionReturnType = "All"; + AlwaysBreakAfterReturnType = "AllDefinitions"; + AlwaysBreakBeforeMultilineStrings = false; + AlwaysBreakTemplateDeclarations = "Yes"; + BinPackArguments = false; + BinPackParameters = false; + BreakBeforeBinaryOperators = "None"; + BreakBeforeBraces = "Allman"; + BreakBeforeTernaryOperators = true; + BreakConstructorInitializers = "BeforeComma"; + BreakInheritanceList = "BeforeComma"; + BreakStringLiterals = true; + ColumnLimit = 80; + CommentPragmas = "^ IWYU pragma:"; + CompactNamespaces = false; + ConstructorInitializerAllOnOneLineOrOnePerLine = false; + ConstructorInitializerIndentWidth = 4; + ContinuationIndentWidth = 4; + Cpp11BracedListStyle = true; + DeriveLineEnding = false; + DerivePointerAlignment = false; + DisableFormat = false; + ExperimentalAutoDetectBinPacking = false; + FixNamespaceComments = true; + IncludeBlocks = "Regroup"; + IndentCaseBlocks = false; + IndentCaseLabels = false; + IndentExternBlock = "NoIndent"; + IndentGotoLabels = false; + IndentPPDirectives = "None"; + IndentWidth = 4; + IndentWrappedFunctionNames = false; + KeepEmptyLinesAtTheStartOfBlocks = false; + Language = "Cpp"; + MaxEmptyLinesToKeep = 1; + NamespaceIndentation = "None"; + PointerAlignment = "Left"; + ReflowComments = false; + SortIncludes = "CaseSensitive"; + SortUsingDeclarations = true; + SpaceAfterCStyleCast = false; + SpaceAfterLogicalNot = false; + SpaceAfterTemplateKeyword = true; + SpaceBeforeAssignmentOperators = true; + SpaceBeforeCpp11BracedList = false; + SpaceBeforeCtorInitializerColon = true; + SpaceBeforeInheritanceColon = true; + SpaceBeforeParens = "ControlStatements"; + SpaceBeforeRangeBasedForLoopColon = true; + SpaceInEmptyParentheses = false; + SpacesBeforeTrailingComments = 1; + SpacesInAngles = false; + SpacesInCStyleCastParentheses = false; + SpacesInContainerLiterals = false; + SpacesInParentheses = false; + SpacesInSquareBrackets = false; + Standard = "Latest"; + TabWidth = 4; + UseTab = "Always"; + }; + + ".gdbinit".text = '' + set confirm off + set verbose off + set editing off + + set history expansion on + + set height 0 + set width 0 + + handle SIGALRM nostop print nopass + handle SIGBUS stop print nopass + handle SIGPIPE nostop print nopass + handle SIGSEGV stop print nopass + + set print address on + set print elements 0 + set print object on + set print pretty on + set print repeats 0 + set print static-members on + set print vtbl on + + set output-radix 10 + + set demangle-style gnu-v3 + + set disassembly-flavor intel + + alias iv=info variables + + alias da=disassemble + + define fs + finish + step + end + + define btc + backtrace + continue + end + ''; + }; + + sessionVariables = { + CARGO_HOME = "${config.dirs.data}/cargo"; + GOPATH = "${config.dirs.data}/go"; + PYTHONSTARTUP = ./pystartup.py; + }; + + # TODO Probably should scrap most of these in favor of per-project + # shell.nix or flake.nix. + packages = with pkgs; [ + alejandra + bash-language-server + cloc + dockerfile-language-server + editorconfig + fd + fzf + htmlq + jc + jq + json-language-server + logcli + nix-language-server + nixfmt + nixpkgs-fmt + ripgrep + ripgrep-all + shellcheck + shfmt + treefmt + wrk + yaml-language-server + yamllint + yq + ]; + }; + + my.extraGroups = ["kvm"]; + }; +} diff --git a/modules/nixfiles/profiles/dev/containers/default.nix b/modules/nixfiles/profiles/dev/containers/default.nix new file mode 100644 index 0000000..d0e7ed7 --- /dev/null +++ b/modules/nixfiles/profiles/dev/containers/default.nix @@ -0,0 +1,73 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.profiles.dev.containers; +in { + options.nixfiles.modules.profiles.dev.containers.enable = + mkEnableOption "Wether to enable tools for working with Kubernetes."; + + config = mkIf cfg.enable { + nixfiles.modules = { + profiles.dev.common.enable = true; + podman.enable = true; + }; + + hm = { + home = { + sessionVariables = { + MINIKUBE_IN_STYLE = "false"; + WERF_DEV = "true"; + WERF_INSECURE_REGISTRY = "true"; + WERF_LOG_DEBUG = "true"; + WERF_LOG_PRETTY = "false"; + WERF_LOG_VERBOSE = "true"; + WERF_SYNCHRONIZATION = ":local"; + }; + + file.".minikube/config/config.json".text = generators.toJSON {} { + config.Rootless = true; + driver = "podman"; + container-runtime = "cri-o"; + }; + + packages = with pkgs; [ + buildah + chart-testing + cmctl + helm + kubectl + kubectx + kubescape + kubespy + minikube + skaffold + skopeo + stern + telepresence + werf + ]; + }; + + programs.bash = { + shellAliases = with pkgs; { + b = "${buildah}/bin/buildah"; + h = "${helm}/bin/helm"; + k = "${kubectl}/bin/kubectl"; + kns = "${kubectx}/bin/kubens"; + ktx = "${kubectx}/bin/kubectx"; + }; + initExtra = mkAfter '' + _complete_alias b _buildah buildah + _complete_alias h __start_helm helm + _complete_alias k __start_kubectl kubectl + _complete_alias kns _kube_namespaces kubens + _complete_alias ktx _kube_contexts kubectx + ''; + }; + }; + }; +} diff --git a/modules/nixfiles/profiles/dev/default.nix b/modules/nixfiles/profiles/dev/default.nix new file mode 100644 index 0000000..1e0bd01 --- /dev/null +++ b/modules/nixfiles/profiles/dev/default.nix @@ -0,0 +1 @@ +_: {imports = [./common.nix ./containers ./sql];} diff --git a/modules/nixfiles/profiles/dev/ghci.conf b/modules/nixfiles/profiles/dev/ghci.conf new file mode 100644 index 0000000..d672167 --- /dev/null +++ b/modules/nixfiles/profiles/dev/ghci.conf @@ -0,0 +1,35 @@ +:set -XBinaryLiterals +:set -XFlexibleContexts +:set -XNoMonomorphismRestriction + +:seti -XConstraintKinds +:seti -XDataKinds +:seti -XDeriveFunctor +:seti -XFlexibleInstances +:seti -XFunctionalDependencies +:seti -XGADTs +:seti -XLambdaCase +:seti -XMagicHash +:seti -XMultiParamTypeClasses +:seti -XMultiWayIf +:seti -XOverloadedLabels +:seti -XPackageImports +:seti -XPolyKinds +:seti -XRankNTypes +:seti -XScopedTypeVariables +:seti -XStandaloneDeriving +:seti -XTupleSections +:seti -XTypeFamilies +:seti -XTypeOperators +:seti -XUndecidableInstances + +:set +c +:set +m +:set +r +:set +s +:set +t + +:set prompt "\ESC[1;34m>\ESC[m\STX " +:set prompt-cont "\ESC[1;94m|\ESC[m\STX " + +:def hoogle \x -> pure (":!hoogle --color --count=10 \"" ++ x ++ "\"") diff --git a/modules/nixfiles/profiles/dev/pystartup.py b/modules/nixfiles/profiles/dev/pystartup.py new file mode 100644 index 0000000..1a78b55 --- /dev/null +++ b/modules/nixfiles/profiles/dev/pystartup.py @@ -0,0 +1,127 @@ +import atexit +import os +import readline +import rlcompleter +import sys +from code import InteractiveConsole +from tempfile import mkstemp + +readline.parse_and_bind("tab: complete") + + +class TermColors(dict): + color_templates = ( + ("Normal", "0"), + ("Black", "0;30"), + ("Red", "0;31"), + ("Green", "0;32"), + ("Brown", "0;33"), + ("Blue", "0;34"), + ("Purple", "0;35"), + ("Cyan", "0;36"), + ("LightGray", "0;37"), + ("DarkGray", "1;30"), + ("LightRed", "1;31"), + ("LightGreen", "1;32"), + ("Yellow", "1;33"), + ("LightBlue", "1;34"), + ("LightPurple", "1;35"), + ("LightCyan", "1;36"), + ("White", "1;37"), + ) + color_base = "\001\033[%sm\002" + + def __init__(self): + self.update( + dict([(k, self.color_base % v) for k, v in self.color_templates]) + ) + + +class Completer(object): + def save_history(self): + import readline + + readline.write_history_file(self.python_histfile) + + def __init__(self): + self.python_dir = os.path.expanduser( + "%s/python" % os.environ["XDG_DATA_HOME"] + ) + + if not os.path.exists(self.python_dir): + os.mkdir(self.python_dir) + + self.python_histfile = os.path.expanduser( + "%s/history" % self.python_dir + ) + + if os.path.exists(self.python_histfile): + readline.read_history_file(self.python_histfile) + + readline.set_history_length(1000) + atexit.register(self.save_history) + + +def DisplayHook(value): + if value is not None: + try: + import __builtin__ + + __builtin__._ = value + except ImportError: + __builtins__._ = value + + import pprint + + pprint.pprint(value) + del pprint + + +class EditableBufferInteractiveConsole(InteractiveConsole): + def __init__(self, *args, **kwargs): + self.last_buffer = [] + InteractiveConsole.__init__(self, *args, **kwargs) + + def runsource(self, source, *args): + self.last_buffer = [source.encode("utf-8")] + return InteractiveConsole.runsource(self, source, *args) + + def raw_input(self, *args): + line = InteractiveConsole.raw_input(self, *args) + + if line == EDIT_CMD: + tmp_fd, tmp_file = mkstemp(".py") + + os.write(tmp_fd, b"\n".join(self.last_buffer)) + os.close(tmp_fd) + + os.system("%s %s" % (EDITOR, tmp_file)) + + line = open(tmp_file).read() + + os.unlink(tmp_file) + tmp_file = "" + + lines = line.split("\n") + + for i in range(len(lines) - 1): + self.push(lines[i]) + + line = lines[-1] + return line + + +TC = TermColors() +ps1 = "%sλ%s %s>%s " +sys.ps1 = ps1 % (TC["Blue"], TC["Normal"], TC["White"], TC["Normal"]) +ps2 = " %s…%s %s>%s " +sys.ps2 = ps2 % (TC["Blue"], TC["Normal"], TC["White"], TC["Normal"]) +sys.displayhook = DisplayHook + +C = Completer() +EDITOR = os.environ.get("EDITOR", "vim") +EDIT_CMD = ":e" +C = EditableBufferInteractiveConsole(locals=locals()) +C.interact(banner="") + +sys.exit() diff --git a/modules/nixfiles/profiles/dev/sql/default.nix b/modules/nixfiles/profiles/dev/sql/default.nix new file mode 100644 index 0000000..deb3738 --- /dev/null +++ b/modules/nixfiles/profiles/dev/sql/default.nix @@ -0,0 +1,94 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.profiles.dev.sql; +in { + options.nixfiles.modules.profiles.dev.sql.enable = + mkEnableOption "Whether to enable SQL database management tools."; + + config = mkIf cfg.enable { + hm = { + home.packages = with pkgs; [pgcli litecli]; + + xdg = let + mainSection = { + destructive_warning = "True"; + enable_pager = "True"; + keyword_casing = "auto"; + less_chatty = "True"; + log_file = "/dev/null"; + log_level = "CRITICAL"; + multi_line = "False"; + syntax_style = "default"; + table_format = "fancy_grid"; + }; + + colorsSection = with config.nixfiles.modules.profiles.common.colourScheme; { + "arg-toolbar" = "noinherit bold"; + "arg-toolbar.text" = "nobold"; + "bottom-toolbar" = "bg:${black} ${white}"; + "bottom-toolbar.off" = "bg:${black} ${brightBlack}"; + "bottom-toolbar.on" = "bg:${black} ${brightWhite}"; + "bottom-toolbar.transaction.failed" = "bg:${black} ${red} bold"; + "bottom-toolbar.transaction.valid" = "bg:${black} ${green} bold"; + "completion-menu.completion" = "bg:${black} ${white}"; + "completion-menu.completion.current" = "bg:${white} ${black}"; + "completion-menu.meta.completion" = "bg:${black} ${yellow}"; + "completion-menu.meta.completion.current" = "bg:${yellow} ${black}"; + "completion-menu.multi-column-meta" = "bg:${yellow} ${black}"; + "scrollbar" = "bg:${black}"; + "scrollbar.arrow" = "bg:${black}"; + "search" = "bg:${magenta} ${brightWhite}"; + "search-toolbar" = "noinherit bold"; + "search-toolbar.text" = "nobold"; + "search.current" = "bg:${green} ${brightWhite}"; + "selected" = "bg:${blue} ${brightWhite}"; + "system-toolbar" = "noinherit bold"; + }; + + mkCliConfig = { + name, + custom, + }: { + "${name}/config" = { + text = generators.toINI {} { + main = mainSection // custom; + colors = mapAttrs (_: v: "'${v}'") colorsSection; + }; + }; + }; + in { + configFile = mkMerge (map mkCliConfig [ + { + name = "pgcli"; + custom = { + auto_expand = "True"; + casing_file = "/dev/null"; + expand = "True"; + history_file = "/dev/null"; + keyring = "False"; + multi_line_mode = "psql"; + on_error = "STOP"; + prompt = "'\\u@\\h:\\d> '"; + vi = "True"; + }; + } + { + name = "litecli"; + custom = { + audit_log = "/dev/null"; + key_bindings = "vi"; + prompt = "'\\d> '"; + prompt_continuation = "'-> '"; + auto_vertical_output = "True"; + }; + } + ]); + }; + }; + }; +} diff --git a/modules/nixfiles/profiles/headful.nix b/modules/nixfiles/profiles/headful.nix new file mode 100644 index 0000000..fa2f0d7 --- /dev/null +++ b/modules/nixfiles/profiles/headful.nix @@ -0,0 +1,69 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.profiles.headful; +in { + imports = [ + (mkAliasOptionModule ["profile" "headful"] [ + "nixfiles" + "modules" + "profiles" + "headful" + "enable" + ]) + ]; + + options.nixfiles.modules.profiles.headful.enable = + mkEnableOption "Wether to enable headful profile."; + + config = mkIf cfg.enable { + nixfiles.modules = { + alacritty.enable = true; + aria2.enable = true; + aspell.enable = true; + emacs.enable = true; + firefox.enable = true; + mpv.enable = true; + openssh.client.enable = true; + password-store.enable = true; + sound.enable = true; + x11.enable = true; + + dwm.enable = mkDefault false; + kde.enable = mkDefault true; + xmonad.enable = mkDefault false; + }; + + hm.home.packages = with pkgs; [convmv dos2unix]; + + hardware.opengl = { + enable = true; + driSupport = true; + }; + + programs = { + iftop.enable = true; + mtr.enable = true; + traceroute.enable = true; + + bash.shellAliases.open = "${pkgs.xdg-utils}/bin/xdg-open"; + }; + + services.upower.enable = true; + + environment.systemPackages = with pkgs; [ + arping + ethtool + inetutils + nethogs + socat + tcpdump + ]; + + my.extraGroups = ["audio" "video" "input"]; + }; +} diff --git a/modules/nixfiles/profiles/headless.nix b/modules/nixfiles/profiles/headless.nix new file mode 100644 index 0000000..9737344 --- /dev/null +++ b/modules/nixfiles/profiles/headless.nix @@ -0,0 +1,67 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.profiles.headless; +in { + imports = [ + (mkAliasOptionModule ["profile" "headless"] [ + "nixfiles" + "modules" + "profiles" + "headless" + "enable" + ]) + ]; + + options.nixfiles.modules.profiles.headless.enable = + mkEnableOption "Whether to enable headless profile."; + + config = mkIf cfg.enable { + nixfiles.modules = { + openssh.server.enable = true; + endlessh-go.enable = true; + + fail2ban.enable = true; + + node-exporter.enable = true; + promtail.enable = true; + }; + + hm.home.file = { + ".hushlogin".text = ""; + ".bash_history".source = + config.hm.lib.file.mkOutOfStoreSymlink "/dev/null"; + }; + + boot.kernelPackages = pkgs.linuxPackages_5_15_hardened; + + nix = { + gc = { + automatic = true; + dates = "weekly"; + options = "--delete-older-than 30d"; + }; + + optimise = { + automatic = true; + dates = ["weekly"]; + }; + }; + + i18n = { + # TODO Convert everything including PostgreSQL databases to the "en_US" + # locale or probably even the "C" one. + defaultLocale = mkForce "C"; + supportedLocales = mkForce ["en_US.UTF-8/UTF-8" "en_GB.UTF-8/UTF-8"]; + }; + security.polkit.enable = false; + services.udisks2.enable = false; + xdg.sounds.enable = false; + + environment.systemPackages = with pkgs; [alacritty.terminfo]; + }; +} diff --git a/modules/nixfiles/prometheus.nix b/modules/nixfiles/prometheus.nix new file mode 100644 index 0000000..b67dd2e --- /dev/null +++ b/modules/nixfiles/prometheus.nix @@ -0,0 +1,60 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.prometheus; +in { + options.nixfiles.modules.prometheus = { + enable = mkEnableOption "Whether to enable Prometheus."; + + port = mkOption { + description = "Port."; + type = with types; port; + default = 30111; + }; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = config.nixfiles.modules.monitoring.domain; + }; + + path = mkOption { + description = "Path."; + type = with types; str; + default = "/prometheus"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules.nginx = with cfg; { + enable = true; + virtualHosts.${domain}.locations.${path} = { + proxyPass = with cfg; "http://127.0.0.1:${toString port}"; + extraConfig = '' + if ($internal != 1) { + return 403; + } + ''; + }; + }; + + services.prometheus = with cfg; { + enable = true; + + listenAddress = "127.0.0.1"; + inherit port; + + extraFlags = [ + "--web.external-url=http${ + optionalString config.nixfiles.modules.acme.enable "s" + }://${domain}${path}" + "--storage.tsdb.retention.size=50GB" + "--storage.tsdb.retention.time=1y" + "--storage.tsdb.wal-compression" + ]; + }; + }; +} diff --git a/modules/nixfiles/promtail.nix b/modules/nixfiles/promtail.nix new file mode 100644 index 0000000..ba4e635 --- /dev/null +++ b/modules/nixfiles/promtail.nix @@ -0,0 +1,53 @@ +{ + config, + lib, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.promtail; +in { + options.nixfiles.modules.promtail = { + enable = mkEnableOption "Whether to enable Promtail."; + + loki = { + url = mkOption { + description = "Address of a listening Loki service."; + type = with types; str; + default = with config.nixfiles.modules.loki; "https://${domain}${path}"; + }; + }; + }; + + config = mkIf cfg.enable { + services.promtail = { + enable = true; + + configuration = { + server = rec { + http_listen_address = this.wireguard.ipv4.address; + http_listen_port = 30181; + + grpc_listen_address = this.wireguard.ipv4.address; + grpc_listen_port = http_listen_port + 1; + + log_level = "warn"; + }; + + clients = [{url = "${cfg.loki.url}/loki/api/v1/push";}]; + + positions.filename = "/tmp/positions.yaml"; + + scrape_configs = [ + { + job_name = "journal"; + journal = { + max_age = "24h"; + labels.job = "systemd-journal"; + }; + } + ]; + }; + }; + }; +} diff --git a/modules/nixfiles/psd.nix b/modules/nixfiles/psd.nix new file mode 100644 index 0000000..290c067 --- /dev/null +++ b/modules/nixfiles/psd.nix @@ -0,0 +1,60 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.psd; +in { + options.nixfiles.modules.psd.enable = + mkEnableOption "Whether to enable Profile Sync Daemon."; + + config = mkIf cfg.enable { + hm.home = { + file."${config.hm.xdg.configHome}/psd/psd.conf".text = '' + USE_OVERLAYFS="yes" + ''; + + packages = with pkgs; [profile-sync-daemon]; + }; + + systemd.user = { + services = { + psd = { + unitConfig = { + Description = "Profile-sync-daemon"; + Wants = ["psd-resync.service"]; + RequiresMountsFor = "/home/"; + After = ["local-fs.target"]; + }; + serviceConfig = { + RemainAfterExit = true; + ExecStart = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon startup"; + ExecStop = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon unsync"; + }; + wantedBy = ["graphical.target"]; + }; + + psd-resync = { + unitConfig = { + Description = "Profile-sync-daemon resync"; + After = ["psd.service"]; + Wants = ["psd-resync.timer"]; + BindsTo = ["psd.service"]; + }; + serviceConfig.ExecStart = "${pkgs.profile-sync-daemon}/bin/profile-sync-daemon resync"; + wantedBy = ["graphical.target"]; + }; + }; + + timers.psd-resync = { + unitConfig = { + Description = "Profile-sync-daemon resync timer"; + BindsTo = ["psd.service"]; + }; + timerConfig.OnUnitActiveSec = "1h"; + }; + }; + }; +} diff --git a/modules/nixfiles/qutebrowser.nix b/modules/nixfiles/qutebrowser.nix new file mode 100644 index 0000000..5af7e1f --- /dev/null +++ b/modules/nixfiles/qutebrowser.nix @@ -0,0 +1,547 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.qutebrowser; +in { + options.nixfiles.modules.qutebrowser.enable = + mkEnableOption "Whether to enable qutebrowser."; + + config = mkIf cfg.enable { + hm = { + # home.sessionVariables.BROWSER = mkOverride 400 "qutebrowser"; + + programs.qutebrowser = with config.nixfiles.modules; { + enable = true; + + package = pkgs.qutebrowser.override { + withMediaPlayback = false; + withPdfReader = false; + }; + + keyBindings.normal = mkIf mpv.enable { + "z" = let + mpv = "${config.hm.programs.mpv.package}/bin/mpv"; + in "hint links spawn --detach ${mpv} {hint-url}"; + }; + + searchEngines = rec { + aliexpress = "https://www.aliexpress.com/wholesale?SearchText={}"; + ansible = "https://galaxy.ansible.com/search?keywords={}"; + arch = "https://wiki.archlinux.org/?search={}"; + crates = "https://crates.io/search?q={}"; + crawl = "http://crawl.chaosforge.org/index.php?search={}"; + discogs = "https://www.discogs.com/search/?q={}"; + dockerdocs = "https://docs.docker.com/search/?q={}"; + dockerhub = "https://hub.docker.com/search?q={}"; + doublegis = "https://2gis.ru/search/{}"; + duckduckgo = "https://duckduckgo.com/?q={}'"; + dwarffortress = "https://dwarffortresswiki.org/index.php?search={}"; + ebay = "https://www.ebay.com/sch/i.html?_nkw={}"; + ecosia = "https://www.ecosia.org/search?q={}"; + factorio = "https://wiki.factorio.com/index.php?search={}"; + genius = "https://genius.com/search?q={}"; + github = "https://github.com/search?q={}"; + godocs = "https://godocs.io/?q={}"; + gogdb = "https://www.gogdb.org/products?search={}"; + google = "https://www.google.com/search?q={}"; + google-images = "https://www.google.com/search?q={}&tbm=isch"; + gopkgs = "https://pkg.go.dev/search?q={}"; + habr = "https://habr.com/ru/search/?q={}"; + hackage = "https://hackage.haskell.org/packages/search?terms={}"; + hackernews = "https://hn.algolia.com/?q={}"; + headhunter = "https://hh.ru/search/vacancy?st=searchVacancy&text={}"; + hoogle = "https://hoogle.haskell.org/?hoogle={}"; + jisho = "https://jisho.org/search/{}"; + kotobank = "https://kotobank.jp/gs/?q={}"; + kubernetes = "https://kubernetes.io/search/?q={}"; + lastfm = "https://www.last.fm/search?q={}"; + lobsters = "https://lobste.rs/search?q=test{}"; + mdn = "https://developer.mozilla.org/en-US/search?q={}"; + melpa = "https://melpa.org/#/?q={}"; + moddb = "https://www.moddb.com/search?q={}"; + musicbrainz = "https://musicbrainz.org/search?query={}"; + nix-issues = "https://github.com/NixOS/nix/issues?q={}"; + nix-prs = "https://github.com/NixOS/nix/pulls?q={}"; + nixos-flakes = "https://search.nixos.org/flakes?query={}"; + nixos-options = "https://search.nixos.org/options?query={}"; + nixos-packages = "https://search.nixos.org/packages?query={}"; + nixos-wiki = "https://nixos.wiki/index.php?search={}"; + nixpkgs-issues = "https://github.com/NixOS/nixpkgs/issues?q={}"; + nixpkgs-prs = "https://github.com/NixOS/nixpkgs/pulls?q={}"; + openstreetmap = "https://www.openstreetmap.org/search?query={}"; + ozon = "https://www.ozon.ru/search/?text={}"; + protondb = "https://www.protondb.com/search?q={}"; + pypi = "https://pypi.org/search/?q={}"; + pythondocs = "https://docs.python.org/3/search.html?q={}"; + rateyourmusic = "https://rateyourmusic.com/search?searchterm={}"; + riichi = "https://riichi.wiki/index.php?search={}"; + rustdoc = "https://doc.rust-lang.org/std/?search={}"; + searx = "https://searx.tiekoetter.com/search?q={}"; + slashdot = "https://slashdot.org/index2.pl?fhfilter={}"; + sourcehut = "https://sr.ht/projects?search={}"; + steam = "https://store.steampowered.com/search/?term={}"; + steamdb = "https://steamdb.info/search/?a=app&q={}"; + ubuntu = "https://wiki.ubuntu.com/Home?action=fullsearch&value={}"; + wikipedia-en = "https://en.wikipedia.org/w/index.php?search={}"; + wikipedia-ru = "https://ru.wikipedia.org/w/index.php?search={}"; + wikipedia-ja = "https://ja.wikipedia.org/w/index.php?search={}"; + wolphramalpha = "https://www.wolframalpha.com/input/?i={}"; + yahoo = "https://yahoo.com/search/?text={}"; + yahoo-images = "https://yahoo.com/images/search?text={}"; + yahoo-market = "https://market.yahoo.com/search?text={}"; + youtube = "https://yewtu.be/search?q={}"; + + aw = arch; + d = duckduckgo; + do = dockerhub; + docker = dockerhub; + dod = dockerdocs; + g = google; + gh = github; + h = hoogle; + k = kubernetes; + mb = musicbrainz; + n = nixos-options; + nw = nixos-wiki; + py = pypi; + pyd = pythondocs; + rym = rateyourmusic; + s = searx; + sh = sourcehut; + sr = sourcehut; + w = wikipedia-en; + wen = wikipedia-en; + wja = wikipedia-ja; + wru = wikipedia-ru; + y = yahoo; + yt = youtube; + }; + + settings = { + changelog_after_upgrade = "never"; + + content = { + autoplay = false; + cookies.accept = "all"; + default_encoding = "utf-8"; + desktop_capture = "ask"; + dns_prefetch = false; + geolocation = false; + headers.do_not_track = true; + javascript.enabled = true; + prefers_reduced_motion = true; + webgl = true; + + blocking = { + enabled = true; + method = "adblock"; + adblock.lists = [ + "https://easylist.to/easylist/easylist.txt" + "https://easylist.to/easylist/easyprivacy.txt" + "https://easylist.to/easylist/fanboy-social.txt" + "https://secure.fanboy.co.nz/fanboy-annoyance.txt" + "https://secure.fanboy.co.nz/fanboy-cookiemonster.txt" + ]; + }; + }; + + completion = { + height = "50%"; + show = "auto"; + shrink = true; + timestamp_format = "%y-%m-%d"; + min_chars = 3; + open_categories = ["bookmarks" "quickmarks" "history"]; + + scrollbar = { + width = 0; + padding = 0; + }; + }; + + downloads = { + location = { + directory = config.userDirs.download; + prompt = true; + }; + remove_finished = 0; + }; + + editor.command = [ + ( + if alacritty.enable + then "${pkgs.alacritty}/bin/alacritty" + else "${pkgs.xterm}/bin/xterm" + ) + "-e" + "${config.programs.vim.package}/bin/vim" + "-f" + "{}" + ]; + + hints = { + auto_follow = "unique-match"; + auto_follow_timeout = 0; + border = "0px"; + min_chars = 1; + scatter = false; + uppercase = false; + }; + + hints.radius = 0; + keyhint.radius = 0; + prompt.radius = 0; + + scrolling = { + bar = "never"; + smooth = false; + }; + + spellcheck.languages = ["en-GB" "en-US" "ru-RU"]; + + statusbar.position = "bottom"; + + tabs = { + position = "top"; + + title = { + alignment = "left"; + format = "{audio}{index} : {current_title}"; + format_pinned = "{audio}{index}"; + }; + + min_width = -1; + max_width = -1; + + indicator.width = 0; + + pinned = { + shrink = true; + frozen = false; + }; + + close_mouse_button = "middle"; + mousewheel_switching = false; + + background = true; + select_on_remove = "next"; + new_position = { + related = "next"; + unrelated = "last"; + }; + + favicons = { + show = "pinned"; + scale = 0.75; + }; + }; + + url = rec { + default_page = "about:blank"; + start_pages = [default_page]; + }; + + window = { + hide_decoration = false; # TODO Test in a WM. + title_format = "{perc}{current_title}{title_sep}qutebrowser"; + }; + + colors = with config.colourScheme; { + completion = rec { + fg = white; + match.fg = red; + odd.bg = black; + even.bg = odd.bg; + category = { + fg = white; + bg = black; + border = { + top = black; + bottom = black; + }; + }; + item.selected = { + fg = black; + bg = white; + border = { + top = white; + bottom = white; + }; + }; + scrollbar = { + fg = white; + bg = black; + }; + }; + contextmenu = { + menu = { + fg = white; + bg = black; + }; + selected = { + fg = black; + bg = white; + }; + disabled = { + fg = brightBlack; + bg = black; + }; + }; + downloads = { + bar.bg = black; + start = { + fg = green; + bg = black; + }; + stop = { + fg = yellow; + bg = black; + }; + error = { + fg = red; + bg = black; + }; + system = { + fg = "none"; + bg = "none"; + }; + }; + hints = { + fg = white; + match.fg = red; + bg = black; + }; + keyhint = { + fg = white; + suffix.fg = red; + bg = black; + }; + messages = { + error = rec { + bg = black; + fg = red; + border = bg; + }; + info = rec { + fg = blue; + bg = black; + border = bg; + }; + warning = rec { + fg = yellow; + bg = black; + border = bg; + }; + }; + prompts = rec { + fg = white; + bg = black; + selected = { + fg = black; + bg = white; + }; + border = bg; + }; + statusbar = { + normal = { + bg = black; + fg = white; + }; + command = { + bg = black; + fg = white; + }; + insert = { + bg = green; + fg = black; + }; + passthrough = { + bg = blue; + fg = black; + }; + private = { + bg = magenta; + fg = black; + }; + url = { + fg = blue; + hover.fg = brightBlue; + success = { + http.fg = brightGreen; + https.fg = brightGreen; + }; + warn.fg = brightYellow; + error.fg = brightRed; + }; + }; + tabs = rec { + bar.bg = black; + even = { + bg = black; + fg = white; + }; + odd = with even; {inherit bg fg;}; + selected = rec { + even = { + bg = white; + fg = black; + }; + odd = with even; {inherit bg fg;}; + }; + pinned = rec { + even = { + bg = brightBlack; + fg = brightWhite; + }; + odd = with even; {inherit bg fg;}; + }; + indicator = { + start = green; + stop = yellow; + error = red; + system = "none"; + }; + }; + webpage = { + bg = "white"; + darkmode = { + enabled = false; + algorithm = "lightness-cielab"; + contrast = 0.0; + grayscale = { + all = false; + images = 0.0; + }; + policy = { + images = "smart"; + page = "smart"; + }; + threshold = { + background = 0; + text = 256; + }; + }; + preferred_color_scheme = "auto"; + }; + }; + + fonts = + (with config.fontScheme.monospaceFont; { + default_family = family; + default_size = (toString size) + "pt"; + }) + // { + web = with config.fontScheme; { + family = rec { + standard = sans_serif; + fixed = monospaceFont.family; + serif = serifFont.family; + sans_serif = sansSerifFont.family; + cursive = null; + fantasy = null; + }; + size = rec { + default = sansSerifFont.size; + default_fixed = monospaceFont.size; + minimum = 0; + minimum_logical = default / 2; + }; + }; + } + // (listToAttrs + (map (name: nameValuePair name "default_size default_family") [ + "completion.category" + "completion.entry" + "contextmenu" + "debug_console" + "downloads" + "hints" + "keyhint" + "messages.error" + "messages.info" + "messages.warning" + "prompts" + "statusbar" + ])); + + qt = mkIf kde.enable { + force_platform = null; + force_platformtheme = "KDE"; + }; + }; + + extraConfig = + (let + mkPaddingDictionary = { + name, + bottom, + left, + right, + top, + }: let + n = "c.${name}.padding"; + b = "'bottom': ${toString bottom}"; + l = "'left': ${toString left}"; + r = "'right': ${toString right}"; + t = "'top': ${toString top}"; + in "${n} = {${b}, ${l}, ${r}, ${t}}"; + + final = map mkPaddingDictionary [ + { + name = "hints"; + bottom = 3; + left = 3; + right = 3; + top = 3; + } + { + name = "statusbar"; + bottom = 1; + left = 0; + right = 3; + top = 1; + } + { + name = "tabs"; + bottom = 1; + left = 6; + right = 6; + top = 1; + } + ]; + in + concatStringsSep "\n" final + "\n") + + (let + allowSetting = setting: url: "config.set('content.${setting}', True, '${url}')"; + + allowMediaCaptureSetting = url: [ + (allowSetting "desktop_capture" url) + (allowSetting "media.audio_video_capture" url) + ]; + allowedMediaCapture = flatten (map allowMediaCaptureSetting [ + "https://discord.com" + "https://meet.google.com" + "https://web.skype.com" + ]); + + allowNotificationsSetting = allowSetting "notifications.enabled"; + allowedNotifications = map allowNotificationsSetting [ + "https://discord.com" + "https://web.skype.com" + "https://web.telegram.org" + "https://web.whatsapp.com" + ]; + + final = allowedMediaCapture ++ allowedNotifications; + in + concatStringsSep "\n" final + "\n"); + }; + }; + + services.psd.enable = true; + }; +} diff --git a/modules/nixfiles/radarr.nix b/modules/nixfiles/radarr.nix new file mode 100644 index 0000000..f57efc9 --- /dev/null +++ b/modules/nixfiles/radarr.nix @@ -0,0 +1,27 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.radarr; +in { + options.nixfiles.modules.radarr = { + enable = mkEnableOption "Whether to enable Radarr."; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "radarr.${config.networking.fqdn}"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules.nginx = { + enable = true; + virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://127.0.0.1:7878"; + }; + + services.radarr.enable = true; + }; +} diff --git a/modules/nixfiles/radicale.nix b/modules/nixfiles/radicale.nix new file mode 100644 index 0000000..8286be1 --- /dev/null +++ b/modules/nixfiles/radicale.nix @@ -0,0 +1,48 @@ +{ + config, + inputs, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.radicale; +in { + options.nixfiles.modules.radicale = { + enable = mkEnableOption "Whether to enable Radicale."; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "radicale.${config.networking.domain}"; + }; + }; + + config = let + port = 5232; + in + mkIf cfg.enable { + secrets.radicale-htpasswd = { + file = "${inputs.self}/secrets/radicale-htpasswd"; + owner = "radicale"; + group = "radicale"; + }; + + nixfiles.modules.nginx = { + enable = true; + virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://127.0.0.1:${toString port}"; + }; + + services.radicale = { + enable = true; + settings = { + server.hosts = ["127.0.0.1:${toString port}"]; + web.type = "none"; + auth = { + type = "htpasswd"; + htpasswd_filename = config.secrets.radicale-htpasswd.path; + htpasswd_encryption = "bcrypt"; + }; + }; + }; + }; +} diff --git a/modules/nixfiles/rss-bridge.nix b/modules/nixfiles/rss-bridge.nix new file mode 100644 index 0000000..fe3a638 --- /dev/null +++ b/modules/nixfiles/rss-bridge.nix @@ -0,0 +1,33 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.rss-bridge; +in { + options.nixfiles.modules.rss-bridge = { + enable = mkEnableOption "Whether to enable rss-bridge."; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "rss-bridge.${config.networking.domain}"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules.nginx = { + enable = true; + virtualHosts.${cfg.domain} = {}; + }; + + services = { + rss-bridge = { + enable = true; + virtualHost = cfg.domain; + whitelist = ["LWNprev" "Phoronix"]; # TODO Expand. + }; + }; + }; +} diff --git a/modules/nixfiles/rtorrent.nix b/modules/nixfiles/rtorrent.nix new file mode 100644 index 0000000..2f1708c --- /dev/null +++ b/modules/nixfiles/rtorrent.nix @@ -0,0 +1,246 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.rtorrent; +in { + options.nixfiles.modules.rtorrent = { + enable = mkEnableOption "Whether to enable rTorrent."; + + flood = { + enable = mkEnableOption "Whether to enable Flood."; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "flood.${config.networking.domain}"; + }; + }; + }; + + config = let + user = "rtorrent"; + group = "rtorrent"; + baseDir = "/var/lib/rtorrent"; + rpcSocket = "/run/rtorrent/rpc.socket"; + in + mkIf cfg.enable (mkMerge [ + (let + port = 50000; + in { + systemd = { + services.rtorrent = { + description = "rTorrent"; + after = ["network.target" "local-fs.target"]; + serviceConfig = let + leechDir = "${baseDir}/leech"; + seedDir = "${baseDir}/seed"; + sessionDir = "${baseDir}/session"; + logDir = "${baseDir}/log"; + in { + User = user; + Group = group; + Type = "simple"; + KillMode = "process"; + KillSignal = "SIGHUP"; + ExecStartPre = concatStringsSep " " [ + "${pkgs.coreutils-full}/bin/mkdir -p" + leechDir + seedDir + sessionDir + logDir + ]; + ExecStart = let + configFile = let + moveCompleted = let + pkg = pkgs.writeShellApplication { + name = "move-completed"; + runtimeInputs = with pkgs; [ + coreutils-full + gnused + findutils + ]; + text = '' + set -x + + leech_path="$1" + seed_path="$2" + # seed_path="$(echo "$2" | sed 's@+@ @g;s@%@\\x@g' | xargs -0 printf '%b')" + + mkdir -p "$seed_path" + mv -u "$leech_path" "$seed_path" + ''; + }; + in "${pkg}/bin/move-completed"; + in + pkgs.writeText "rtorrent.rc" '' + method.insert = cfg.leech, private|const|string, (cat, "${leechDir}") + method.insert = cfg.seed, private|const|string, (cat, "${seedDir}") + method.insert = cfg.session, private|const|string, (cat, "${sessionDir}") + method.insert = cfg.log, private|const|string, (cat, "${logDir}") + method.insert = cfg.rpcsocket, private|const|string, (cat, "${rpcSocket}") + + directory.default.set = (cat, (cfg.leech)) + session.path.set = (cat, (cfg.session)) + + network.port_range.set = ${toString port}-${toString port} + network.port_random.set = no + + dht.mode.set = disable + protocol.pex.set = no + + trackers.use_udp.set = no + + protocol.encryption.set = allow_incoming,try_outgoing,enable_retry + + pieces.memory.max.set = 2048M + pieces.preload.type.set = 2 + + network.max_open_files.set = 1024 + network.max_open_sockets.set = 1024 + + network.http.max_open.set = 128 + + throttle.global_down.max_rate.set_kb = 0 + throttle.global_up.max_rate.set_kb = 0 + + encoding.add = UTF-8 + system.umask.set = 0027 + system.cwd.set = (directory.default) + + network.scgi.open_local = (cat, (cfg.rpcsocket)) + schedule = scgi_group, 0, 0, "\ + execute.nothrow=chown, \":${group}\", (cfg.rpcsocket)\ + " + schedule = scgi_permission, 0, 0, "\ + execute.nothrow=chmod, \"g+w,o=\", (cfg.rpcsocket)\ + " + + method.insert = d.move_completed, simple, "\ + d.directory.set=$argument.1=;\ + execute=${moveCompleted}, $argument.0=, $argument.1=;\ + d.save_full_session=\ + " + method.insert = d.leech_path, simple, "\ + if=(d.is_multi_file),\ + (cat, (d.directory), /),\ + (cat, (d.directory), /, (d.name))\ + " + method.insert = d.seed_path, simple, "\ + cat=$cfg.seed=, /, $d.custom1=\ + " + method.set_key = event.download.finished, move_complete, "\ + d.move_completed=$d.leech_path=, $d.seed_path=\ + " + + log.open_file = "log", (cat, (cfg.log), "/", "default.log") + log.add_output = "debug", "log" + log.execute = (cat, (cfg.log), "/", "execute.log") + ''; + in + concatStringsSep " " [ + "${pkgs.rtorrent}/bin/rtorrent" + "-n" + "-o system.daemon.set=true" + "-o import=${configFile}" + ]; + Restart = "on-failure"; + RestartSec = 3; + RuntimeDirectory = "rtorrent"; + RuntimeDirectoryMode = 755; + }; + wantedBy = ["multi-user.target"]; + }; + + tmpfiles.rules = ["d '${baseDir}' 0750 ${user} ${group} -"]; + }; + + users = { + users.${user} = { + inherit group; + shell = pkgs.bashInteractive; + home = baseDir; + description = "rTorrent"; + isSystemUser = true; + }; + groups.${group} = {}; + }; + my.extraGroups = [group]; + + networking.firewall.allowedTCPPorts = [port]; + + boot.kernel.sysctl = { + "net.core.rmem_max" = mkOverride 500 (pow 2 24); + "net.core.wmem_max" = mkOverride 500 (pow 2 24); + "net.ipv4.tcp_fin_timeout" = mkOverride 500 30; + "net.ipv4.tcp_rmem" = mkOverride 500 (mkTcpMem 12 23 24); + "net.ipv4.tcp_slow_start_after_idle" = 0; + "net.ipv4.tcp_tw_recycle" = mkOverride 500 1; + "net.ipv4.tcp_tw_reuse" = mkOverride 500 1; + "net.ipv4.tcp_wmem" = mkOverride 500 (mkTcpMem 12 23 24); + }; + }) + (let + port = 50001; + pkg = pkgs.nodePackages.flood; + in + mkIf cfg.flood.enable { + nixfiles.modules.nginx = { + enable = true; + virtualHosts.${cfg.flood.domain} = { + root = "${pkg}/lib/node_modules/flood/dist/assets"; + locations = { + "/" = { + tryFiles = "$uri /index.html"; + extraConfig = '' + if ($internal != 1) { + return 403; + } + ''; + }; + "/api" = { + proxyPass = "http://127.0.0.1:${toString port}"; + extraConfig = '' + proxy_buffering off; + proxy_cache off; + + if ($internal != 1) { + return 403; + } + ''; + }; + }; + }; + }; + + systemd.services.flood = { + description = "Flood"; + after = ["network.target" "rtorrent.service"]; + path = with pkgs; [mediainfo]; + serviceConfig = { + User = user; + Group = group; + Type = "simple"; + KillMode = "process"; + ExecStart = concatStringsSep " " [ + "${pkg}/bin/flood" + "--allowedpath=${baseDir}" + "--baseuri=/" + "--rundir=${baseDir}/flood" + "--host=127.0.0.1" + "--port=${toString port}" + "--rtsocket=${rpcSocket}" + "--ssl=false" + "--auth=none" + ]; + Restart = "on-failure"; + RestartSec = 3; + }; + wantedBy = ["multi-user.target"]; + }; + }) + ]); +} diff --git a/modules/nixfiles/searx.nix b/modules/nixfiles/searx.nix new file mode 100644 index 0000000..a5bb005 --- /dev/null +++ b/modules/nixfiles/searx.nix @@ -0,0 +1,81 @@ +{ + config, + inputs, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.searx; +in { + options.nixfiles.modules.searx = { + enable = mkEnableOption "Whether to enable SearX."; + + port = mkOption { + description = "Port."; + type = with types; port; + default = 61001; + }; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; nullOr str; + default = "searx.${config.networking.domain}"; + }; + }; + + config = mkIf cfg.enable { + secrets.searx-environment = { + file = "${inputs.self}/secrets/searx-environment"; + owner = "searx"; + group = "searx"; + }; + + nixfiles.modules.nginx = { + enable = true; + virtualHosts.${cfg.domain}.locations."/" = { + proxyPass = "http://127.0.0.1:${toString cfg.port}"; + extraConfig = '' + if ($internal != 1) { + return 403; + } + ''; + }; + }; + + services = { + searx = { + enable = true; + + settings = { + general = { + instance_name = cfg.domain; + contact_url = "mailto:admin+searx@${config.networking.domain}"; + git_url = false; + git_branch = false; + docs_url = false; + wiki_url = false; + twitter_url = false; + }; + server = { + bind_address = "127.0.0.1"; + inherit (cfg) port; + secret_key = "@SECRET_KEY@"; + base_url = false; + image_proxy = false; + default_http_headers = { + Referrer-Policy = "no-referrer"; + X-Content-Type-Options = "nosniff"; + X-Download-Options = "noopen"; + X-Robots-Tag = "noindex, nofollow, nosnippet, noarchive"; + }; + }; + search = { + safe_search = 0; + autocomplete = ""; + }; + }; + environmentFile = config.secrets.searx-environment.path; + }; + }; + }; +} diff --git a/modules/nixfiles/shadowsocks.nix b/modules/nixfiles/shadowsocks.nix new file mode 100644 index 0000000..c496caa --- /dev/null +++ b/modules/nixfiles/shadowsocks.nix @@ -0,0 +1,119 @@ +{ + config, + inputs, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.shadowsocks; +in { + options.nixfiles.modules.shadowsocks = { + enable = mkEnableOption "Whether to enable Shadowsocks."; + + port = mkOption { + type = with types; port; + default = 8388; + description = "Port."; + }; + }; + + config = mkIf cfg.enable { + secrets.shadowsocks-password = { + file = "${inputs.self}/secrets/shadowsocks-password"; + mode = "0444"; # User is dynamic. + }; + + services = { + shadowsocks = { + enable = true; + passwordFile = config.secrets.shadowsocks-password.path; + localAddress = ["0.0.0.0"]; + mode = "tcp_only"; + }; + + fail2ban.jails.shadowsocks-libev = '' + enabled = true + filter = shadowsocks-libev + port = ${toString cfg.port} + ''; + }; + + systemd.services.shadowsocks-libev.path = with pkgs; + mkForce [ + (writeShellApplication { + name = "ss-server"; + runtimeInputs = [shadowsocks-libev]; + text = let + # https://github.com/shadowsocks/shadowsocks-libev/blob/master/acl/server_block_local.acl + aclFile = writeText "outbound_block_list.acl" '' + [outbound_block_list] + 0.0.0.0/8 + 10.0.0.0/8 + 100.64.0.0/10 + 127.0.0.0/8 + 169.254.0.0/16 + 172.16.0.0/12 + 192.0.0.0/24 + 192.0.2.0/24 + 192.88.99.0/24 + 192.168.0.0/16 + 198.18.0.0/15 + 198.51.100.0/24 + 203.0.113.0/24 + 224.0.0.0/4 + 240.0.0.0/4 + 255.255.255.255/32 + ::1/128 + ::ffff:127.0.0.1/104 + fc00::/7 + fe80::/10 + ''; + in '' + ss-server --acl ${aclFile} "$@" + ''; + }) + coreutils-full + jq + ]; + + environment.etc = mkIf config.nixfiles.modules.fail2ban.enable { + "fail2ban/filter.d/shadowsocks-libev.conf".text = '' + [Definition] + failregex = ^.*failed to handshake with <ADDR>: authentication error$ + ignoreregex = + journalmatch = _SYSTEMD_UNIT=shadowsocks-libev.service + ''; + }; + + networking.firewall = { + allowedTCPPorts = [cfg.port]; + extraCommands = '' + iptables -A nixos-fw -p tcp --syn --dport ${ + toString cfg.port + } -m connlimit --connlimit-above 32 -j nixos-fw-refuse + ''; + }; + + boot.kernel.sysctl = { + "net.core.rmem_max" = mkOverride 100 (pow 2 26); + "net.core.wmem_max" = mkOverride 100 (pow 2 26); + "net.core.netdev_max_backlog" = pow 2 18; + "net.core.somaxconn" = pow 2 12; + "net.ipv4.tcp_syncookies" = 1; + "net.ipv4.tcp_tw_reuse" = mkOverride 100 1; + "net.ipv4.tcp_tw_recycle" = mkOverride 100 0; + "net.ipv4.tcp_fin_timeout" = mkOverride 100 30; + "net.ipv4.tcp_keepalive_time" = 60 * 20; + "net.ipv4.ip_local_port_range" = "10000 65000"; + "net.ipv4.tcp_max_syn_backlog" = pow 2 13; + "net.ipv4.tcp_max_tw_buckets" = pow 2 12; + "net.ipv4.tcp_fastopen" = 3; + "net.ipv4.tcp_mem" = mkOverride 100 (mkTcpMem 15 16 17); + "net.ipv4.tcp_rmem" = mkOverride 100 (mkTcpMem 12 16 26); + "net.ipv4.tcp_wmem" = mkOverride 100 (mkTcpMem 12 16 26); + "net.ipv4.tcp_mtu_probing" = 1; + "net.ipv4.tcp_congestion_control" = "hybla"; + }; + }; +} diff --git a/modules/nixfiles/soju.nix b/modules/nixfiles/soju.nix new file mode 100644 index 0000000..b586cbf --- /dev/null +++ b/modules/nixfiles/soju.nix @@ -0,0 +1,83 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.soju; +in { + options.nixfiles.modules.soju = { + enable = mkEnableOption "Whether to enable soju."; + + protocol = mkOption { + description = "Port."; + type = with types; enum ["ircs" "irc+insecure"]; + default = "irc+insecure"; + }; + + address = mkOption { + description = "Address."; + type = with types; str; + default = this.wireguard.ipv4.address; + }; + + port = mkOption { + description = "Port."; + type = with types; port; + default = 6667; + }; + + domain = mkOption { + description = "Domain."; + type = with types; str; + default = config.networking.fqdn; + }; + }; + + config = let + db = "soju"; + in + mkIf cfg.enable { + services.postgresql = { + ensureDatabases = [db]; + ensureUsers = [ + { + name = db; + ensurePermissions."DATABASE \"${db}\"" = "ALL PRIVILEGES"; + } + ]; + }; + + systemd.services.soju = { + description = "soju IRC bouncer"; + wantedBy = ["multi-user.target"]; + after = ["network-online.target" "postgresql.service"]; + serviceConfig = { + Restart = "always"; + ExecStart = let + configFile = pkgs.writeText "soju.conf" '' + listen ${cfg.protocol}://${cfg.address}:${toString cfg.port} + db postgres "${ + concatStringsSep " " [ + "host=/run/postgresql" + "user=${db}" + "dbname=${db}" + "sslmode=disable" + ] + }" + hostname ${cfg.domain} + title ${cfg.domain} + ''; + in + concatStringsSep " " [ + "${pkgs.soju}/bin/soju" + "-config ${configFile}" + ]; + DynamicUser = true; + StateDirectory = "soju"; + }; + }; + }; +} diff --git a/modules/nixfiles/sonarr.nix b/modules/nixfiles/sonarr.nix new file mode 100644 index 0000000..a2003f0 --- /dev/null +++ b/modules/nixfiles/sonarr.nix @@ -0,0 +1,27 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.sonarr; +in { + options.nixfiles.modules.sonarr = { + enable = mkEnableOption "Whether to enable Sonarr."; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "sonarr.${config.networking.fqdn}"; + }; + }; + + config = mkIf cfg.enable { + nixfiles.modules.nginx = { + enable = true; + virtualHosts.${cfg.domain}.locations."/".proxyPass = "http://127.0.0.1:8989"; + }; + + services.sonarr.enable = true; + }; +} diff --git a/modules/nixfiles/sound.nix b/modules/nixfiles/sound.nix new file mode 100644 index 0000000..b352f3e --- /dev/null +++ b/modules/nixfiles/sound.nix @@ -0,0 +1,21 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.sound; +in { + options.nixfiles.modules.sound.enable = + mkEnableOption "Whether to enable sound support."; + + config = mkIf cfg.enable { + services.pipewire = { + enable = true; + + alsa.enable = false; + jack.enable = false; + pulse.enable = true; + }; + }; +} diff --git a/modules/nixfiles/subversion.nix b/modules/nixfiles/subversion.nix new file mode 100644 index 0000000..1cfdca6 --- /dev/null +++ b/modules/nixfiles/subversion.nix @@ -0,0 +1,53 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.subversion; +in { + options.nixfiles.modules.subversion.enable = + mkEnableOption "Whether to enable Subversion version control system."; + + config = mkIf cfg.enable { + nixfiles.modules.gnupg.enable = true; + + hm.home = { + file = { + ".subversion/config".text = generators.toINI {} { + auth = { + password-stores = "gpg-agent"; + ssl-client-cert-file-prompt = "no"; + store-passwords = "yes"; + store-auth-creds = "yes"; + }; + helpers = { + editor-cmd = "${config.programs.vim.package}/bin/vim"; + diff-cmd = "${pkgs.colordiff}/bin/colordiff"; + }; + miscellany = { + global-ignores = with config.hm.programs.git; + optionalString (ignores != []) (concatStringsSep " " ignores); + diff-ignore-content-type = "no"; + }; + working-copy = { + exclusive-locking-clients = "svn"; + exclusive-locking = true; + busy-timeout = 10000; + }; + }; + + ".subversion/servers".text = generators.toINI {} { + global = { + store-auth-creds = "yes"; + store-passwords = "yes"; + store-plaintext-passwords = "yes"; + }; + }; + }; + + packages = with pkgs; [(subversionClient.override {saslSupport = true;})]; + }; + }; +} diff --git a/modules/nixfiles/syncthing.nix b/modules/nixfiles/syncthing.nix new file mode 100644 index 0000000..5a973cc --- /dev/null +++ b/modules/nixfiles/syncthing.nix @@ -0,0 +1,161 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.syncthing; +in { + options.nixfiles.modules.syncthing = { + enable = mkEnableOption "Whether to enable Syncthing."; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "syncthing.${config.networking.fqdn}"; + }; + + # TODO Make this simpler. + cert = mkOption { + description = "Path to the cert file."; + type = with types; nullOr string; + default = null; + }; + + # TODO Make this simpler. + key = mkOption { + description = "Path to the key file."; + type = with types; nullOr string; + default = null; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + { + assertions = [ + { + assertion = cfg.cert != null; + message = "Cert file needs to be specified."; + } + { + assertion = cfg.key != null; + message = "Key file needs to be specified."; + } + ]; + + services.syncthing = { + enable = true; + + user = my.username; + inherit (config.my) group; + + dataDir = config.my.home; + + guiAddress = "127.0.0.1:8384"; + + inherit (cfg) key cert; + + overrideDevices = true; + devices = mapAttrs (name: attr: + mkIf (attr.syncthing.id != null && hasAttr "wireguard" attr) { + inherit (attr.syncthing) id; + addresses = ["tcp://${name}.${config.networking.domain}:22000"]; + introducer = this.isHeadless; + }) + my.configurations; + + overrideFolders = true; + folders = let + filterDevices = f: + attrNames (filterAttrs (_: attr: + (attr.hostname != this.hostname) + && (attr.syncthing.id != null) + && f attr) + my.configurations); + all = filterDevices (_: true); + notHeadless = filterDevices (attr: !attr.isHeadless); + notOther = filterDevices (attr: !attr.isOther); + + simple = { + type = "simple"; + params.keep = "5"; + }; + trashcan = { + type = "trashcan"; + params.cleanoutDays = "30"; + }; + void = { + type = "external"; + params.versionPath = with pkgs; + writeShellScriptBin "backup" '' + ${coreutils-full}/bin/rm -rf $1/$2 + ''; + }; + in + with config.hm.xdg.userDirs; { + share = { + path = publicShare; + devices = notHeadless; + versioning = void; + }; + pass = { + path = + config.hm.programs.password-store.settings.PASSWORD_STORE_DIR; + devices = all; + versioning = trashcan; + }; + org = { + path = "${documents}/org"; + devices = all; + versioning = simple; + }; + roam = { + path = "${documents}/roam"; + devices = notOther; + versioning = simple; + }; + elfeed = { + path = "${config.my.home}/.elfeed"; + devices = notOther; + versioning = trashcan; + }; + vidya = { + path = "${documents}/vidya"; + devices = notOther; + versioning = void; + }; + }; + + extraOptions = { + gui = { + insecureAdminAccess = true; + insecureSkipHostcheck = this.isHeadless; + }; + options = { + # Only local discovery is used over VPN. + globalAnnounceEnabled = false; + relaysEnabled = false; + urAccepted = -1; + }; + }; + }; + + systemd.services.syncthing.environment.STNODEFAULTFOLDER = "yes"; + } + (mkIf this.isHeadless { + nixfiles.modules.nginx = { + enable = true; + virtualHosts.${cfg.domain}.locations."/" = { + proxyPass = "http://${config.services.syncthing.guiAddress}"; + extraConfig = '' + if ($internal != 1) { + return 403; + } + ''; + }; + }; + }) + ]); +} diff --git a/modules/nixfiles/throttled.nix b/modules/nixfiles/throttled.nix new file mode 100644 index 0000000..b7c7d8a --- /dev/null +++ b/modules/nixfiles/throttled.nix @@ -0,0 +1,117 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.throttled; +in { + options.nixfiles.modules.throttled.enable = + mkEnableOption "Whether to enable Throttled."; + + config = mkIf cfg.enable { + environment.etc."throttled.conf".text = '' + [GENERAL] + # Enable or disable the script execution + Enabled: True + # SYSFS path for checking if the system is running on AC power + Sysfs_Power_Path: /sys/class/power_supply/AC*/online + # Auto reload config on changes + Autoreload: True + + ## Settings to apply while connected to Battery power + [BATTERY] + # Update the registers every this many seconds + Update_Rate_s: 30 + # Max package power for time window #1 + PL1_Tdp_W: 29 + # Time window #1 duration + PL1_Duration_s: 28 + # Max package power for time window #2 + PL2_Tdp_W: 44 + # Time window #2 duration + PL2_Duration_S: 0.002 + # Max allowed temperature before throttling + Trip_Temp_C: 85 + # Set cTDP to normal=0, down=1 or up=2 (EXPERIMENTAL) + cTDP: 0 + # Disable BDPROCHOT (EXPERIMENTAL) + Disable_BDPROCHOT: False + + ## Settings to apply while connected to AC power + [AC] + # Update the registers every this many seconds + Update_Rate_s: 5 + # Max package power for time window #1 + PL1_Tdp_W: 44 + # Time window #1 duration + PL1_Duration_s: 28 + # Max package power for time window #2 + PL2_Tdp_W: 44 + # Time window #2 duration + PL2_Duration_S: 0.002 + # Max allowed temperature before throttling + Trip_Temp_C: 95 + # Set HWP energy performance hints to 'performance' on high load (EXPERIMENTAL) + # Uncomment only if you really want to use it + # HWP_Mode: False + # Set cTDP to normal=0, down=1 or up=2 (EXPERIMENTAL) + cTDP: 0 + # Disable BDPROCHOT (EXPERIMENTAL) + Disable_BDPROCHOT: False + + # All voltage values are expressed in mV and *MUST* be negative (i.e. undervolt)! + [UNDERVOLT.BATTERY] + # CPU core voltage offset (mV) + CORE: 0 + # Integrated GPU voltage offset (mV) + GPU: 0 + # CPU cache voltage offset (mV) + CACHE: 0 + # System Agent voltage offset (mV) + UNCORE: 0 + # Analog I/O voltage offset (mV) + ANALOGIO: 0 + + # All voltage values are expressed in mV and *MUST* be negative (i.e. undervolt)! + [UNDERVOLT.AC] + # CPU core voltage offset (mV) + CORE: 0 + # Integrated GPU voltage offset (mV) + GPU: 0 + # CPU cache voltage offset (mV) + CACHE: 0 + # System Agent voltage offset (mV) + UNCORE: 0 + # Analog I/O voltage offset (mV) + ANALOGIO: 0 + + # [ICCMAX.AC] + # # CPU core max current (A) + # CORE: + # # Integrated GPU max current (A) + # GPU: + # # CPU cache max current (A) + # CACHE: + + # [ICCMAX.BATTERY] + # # CPU core max current (A) + # CORE: + # # Integrated GPU max current (A) + # GPU: + # # CPU cache max current (A) + # CACHE: + ''; + + systemd.services.throttled = { + description = "Stop Intel throttling"; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.throttled}/opt/throttled/throttled.py"; + }; + environment.PYTHONUNBUFFERED = "1"; + wantedBy = ["multi-user.target"]; + }; + }; +} diff --git a/modules/nixfiles/tmux.nix b/modules/nixfiles/tmux.nix new file mode 100644 index 0000000..c90b0fc --- /dev/null +++ b/modules/nixfiles/tmux.nix @@ -0,0 +1,60 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.tmux; +in { + options.nixfiles.modules.tmux.enable = + mkEnableOption "Whether to enable tmux."; + + config = mkIf cfg.enable { + hm.programs.tmux = { + enable = true; + + aggressiveResize = true; + baseIndex = 1; + clock24 = true; + disableConfirmationPrompt = true; + escapeTime = 0; + historyLimit = 50000; + newSession = true; + resizeAmount = 10; + terminal = "screen-256color"; + + extraConfig = '' + set -g set-titles on + + set -g status-left "" + set -g status-right "" + + set -g detach-on-destroy off + + set -g status-keys emacs + set -g mode-keys vi + + bind h select-pane -L + bind j select-pane -D + bind k select-pane -U + bind l select-pane -R + + bind -r H resize-pane -L 10 + bind -r J resize-pane -D 10 + bind -r K resize-pane -U 10 + bind -r L resize-pane -R 10 + + bind < swap-pane -D + bind > swap-pane -U + + bind , swap-window -t -1 + bind . swap-window -t +1 + + bind Tab last-window + + bind _ split-window -v + bind | split-window -h + ''; + }; + }; +} diff --git a/modules/nixfiles/unbound.nix b/modules/nixfiles/unbound.nix new file mode 100644 index 0000000..af983a8 --- /dev/null +++ b/modules/nixfiles/unbound.nix @@ -0,0 +1,208 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.unbound; +in { + options.nixfiles.modules.unbound = { + enable = mkEnableOption '' + Whether to enable Unbound. + + This service is configured to be used in conjunction with a Wireguard + server located on the same host to enable internal name resolution with a + forwarding fallback via Quad9. + + Features included: Internal name resolution, Quad9 as a recursive + resolver, DNSSEC validation, Redis cache and adblocking via pgl.yoyo.org + lists. + ''; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = config.networking.domain; + }; + }; + + config = let + adblock-conf = "${config.services.unbound.stateDir}/adblock.conf"; + in + mkIf cfg.enable { + services = { + unbound = { + enable = true; + + package = pkgs.unbound-with-systemd.override { + withRedis = true; + withTFO = true; + }; + + settings = { + server = { + interface = with this.wireguard; [ + "127.0.0.1" + "::1" + ipv4.address + ipv6.address + ]; + + local-zone = + concatLists + (mapAttrsToList (h: _: [''"${h}.${cfg.domain}" redirect'']) + my.configurations); + local-data = concatLists (mapAttrsToList (hostname: let + domain = "${hostname}.${cfg.domain}"; + in + attr: (optionals (hasAttr "wireguard" attr) (with attr.wireguard; + [ + ''"${domain} 604800 IN A ${ipv4.address}"'' + ''"${domain} 604800 IN AAAA ${ipv6.address}"'' + ''"${domain}. A ${ipv4.address}"'' + ''"${domain}. AAAA ${ipv6.address}"'' + ] + ++ concatMap (domain: [ + ''"${domain}. A ${ipv4.address}"'' + ''"${domain}. AAAA ${ipv6.address}"'' + ]) + attr.domains))) + my.configurations); + local-data-ptr = concatLists (mapAttrsToList (hostname: let + domain = "${hostname}.${cfg.domain}"; + in + attr: (optionals (hasAttr "wireguard" attr) (with attr.wireguard; + [ + ''"${ipv4.address} ${domain}"'' + ''"${ipv6.address} ${domain}"'' + ] + ++ concatMap (domain: [ + ''"${ipv4.address} ${domain}"'' + ''"${ipv6.address} ${domain}"'' + ]) + attr.domains))) + my.configurations); + + access-control = with config.nixfiles.modules.wireguard; [ + "0.0.0.0/0 refuse" + "::/0 refuse" + "127.0.0.0/8 allow" + "::1/128 allow" + "${ipv4.subnet} allow" + "${ipv6.subnet} allow" + ]; + + private-domain = cfg.domain; + private-address = with config.nixfiles.modules.wireguard; [ + ipv4.subnet + ipv6.subnet + ]; + + domain-insecure = cfg.domain; + + prefetch = true; + prefetch-key = true; + + hide-identity = true; + hide-version = true; + + extended-statistics = true; + + include = ''"${adblock-conf}"''; + }; + + forward-zone = [ + { + name = "."; + forward-tls-upstream = true; + forward-addr = let + mkDnsOverTls = ips: auth: + map (ip: concatStrings [ip "@" auth]) ips; + in + mkDnsOverTls dns.const.quad9.default "853#dns.quad9.net"; + } + ]; + + cachedb = with config.services.redis.servers.unbound; { + backend = "redis"; + redis-server-host = bind; + redis-server-port = port; + }; + }; + + localControlSocketPath = "/run/unbound/unbound.socket"; + }; + + redis = { + servers.unbound = { + enable = true; + bind = "127.0.0.1"; + port = 6379; + }; + vmOverCommit = mkForce true; + }; + + prometheus.exporters = { + unbound = { + enable = true; + listenAddress = mkDefault this.wireguard.ipv4.address; + port = 9167; + fetchType = "uds"; + controlInterface = config.services.unbound.localControlSocketPath; + inherit (config.services.unbound) group user; + }; + + redis = { + enable = true; + listenAddress = mkDefault this.wireguard.ipv4.address; + port = mkDefault 9121; + extraFlags = with config.services.redis.servers.unbound; [ + "--redis.addr=redis://${bind}:${toString port}" + "--redis.user=${user}" + ]; + }; + }; + }; + + systemd = { + services = { + unbound.after = ["unbound-adblock-update.service"]; + + unbound-adblock-update = { + serviceConfig = with config.services.unbound; { + Type = "oneshot"; + User = user; + Group = group; + ExecStart = let + pkg = with pkgs; + writeShellApplication { + name = "unbound-adblock-update"; + runtimeInputs = [curl package]; + text = '' + curl \ + "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=unbound&showintro=0&mimetype=plaintext" \ + >${adblock-conf} + + if [[ -f "${localControlSocketPath}" ]]; then + unbound-control reload + fi + ''; + }; + in "${pkg}/bin/unbound-adblock-update"; + }; + }; + }; + + timers.unbound-adblock-update = { + requires = ["network-online.target"]; + timerConfig = { + OnUnitActiveSec = "1d"; + Unit = "unbound-adblock-update.service"; + }; + wantedBy = ["timers.target"]; + }; + }; + }; +} diff --git a/modules/nixfiles/vaultwarden.nix b/modules/nixfiles/vaultwarden.nix new file mode 100644 index 0000000..4f5bcb4 --- /dev/null +++ b/modules/nixfiles/vaultwarden.nix @@ -0,0 +1,117 @@ +{ + config, + inputs, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.vaultwarden; +in { + options.nixfiles.modules.vaultwarden = { + enable = mkEnableOption "Whether to enable Vaultwarden."; + + domain = mkOption { + description = "Domain name sans protocol scheme."; + type = with types; str; + default = "vaultwarden.${config.networking.domain}"; + }; + }; + + config = mkIf cfg.enable { + secrets.vaultwarden-environment = { + file = "${inputs.self}/secrets/vaultwarden-environment"; + owner = "vaultwarden"; + group = "vaultwarden"; + }; + + nixfiles.modules = { + nginx = { + enable = true; + virtualHosts.${cfg.domain} = with config.services.vaultwarden.config; { + locations."/" = { + proxyPass = "http://[${ROCKET_ADDRESS}]:${toString ROCKET_PORT}"; + proxyWebsockets = true; + }; + locations."/notifications/hub" = { + proxyPass = "http://[${WEBSOCKET_ADDRESS}]:${toString WEBSOCKET_PORT}"; + proxyWebsockets = true; + }; + locations."/notifications/hub/negotiate" = { + proxyPass = "http://[${ROCKET_ADDRESS}]:${toString ROCKET_PORT}"; + proxyWebsockets = true; + }; + }; + }; + postgresql.enable = true; + }; + + services = let + db = "vaultwarden"; + in { + vaultwarden = { + enable = true; + config = { + TZ = config.time.timeZone; + + WEB_VAULT_ENABLED = true; + + DOMAIN = optionalString (cfg.domain != null) "http://${cfg.domain}"; + + SIGNUPS_ALLOWED = false; + INVITATIONS_ALLOWED = true; + + ROCKET_ADDRESS = "::1"; + ROCKET_PORT = 8812; + + WEBSOCKET_ENABLED = true; + WEBSOCKET_ADDRESS = "::1"; + WEBSOCKET_PORT = 8813; + + LOG_LEVEL = "error"; + + DATABASE_URL = "postgresql://${db}@/${db}"; + }; + dbBackend = "postgresql"; + environmentFile = config.secrets.vaultwarden-environment.path; + }; + + postgresql = { + ensureDatabases = [db]; + ensureUsers = [ + { + name = db; + ensurePermissions."DATABASE \"${db}\"" = "ALL PRIVILEGES"; + } + ]; + }; + + fail2ban.jails = mkIf config.nixfiles.modules.fail2ban.enable { + vaultwarden = '' + enabled = true + filter = vaultwarden + port = http,https + ''; + vaultwarden-admin = '' + enabled = true + filter = vaultwarden-admin + port = http,https + ''; + }; + }; + + environment.etc = mkIf config.nixfiles.modules.fail2ban.enable { + "fail2ban/filter.d/vaultwarden.conf".text = '' + [Definition] + failregex = ^.*Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$ + ignoreregex = + journalmatch = _SYSTEMD_UNIT=vaultwarden.service + ''; + "fail2ban/filter.d/vaultwarden-admin.conf".text = '' + [Definition] + failregex = ^.*Invalid admin token\. IP: <ADDR>.*$ + ignoreregex = + journalmatch = _SYSTEMD_UNIT=vaultwarden.service + ''; + }; + }; +} diff --git a/modules/nixfiles/vim/default.nix b/modules/nixfiles/vim/default.nix new file mode 100644 index 0000000..cef4423 --- /dev/null +++ b/modules/nixfiles/vim/default.nix @@ -0,0 +1,56 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.vim; +in { + options.nixfiles.modules.vim.enable = + mkEnableOption "Whether to enable Vim text editor."; + + config = mkIf cfg.enable { + programs.vim.package = with pkgs; + ((vim_configurable.override {features = "normal";} + // listToAttrs + (map (name: nameValuePair name false) [ + "cscopeSupport" + "darwinSupport" + "guiSupport" + "luaSupport" + "multibyteSupport" + "netbeansSupport" + "nlsSupport" + "perlSupport" + "pythonSupport" + "rubySupport" + "tclSupport" + "ximSupport" + ])) + .overrideAttrs (_: super: { + configureFlags = + super.configureFlags + ++ ["--enable-gpm=no" "--enable-gui=no"]; + })) + .customize { + name = "vim"; + vimrcConfig = { + customRC = readFile ./rc.vim; + packages.myVimPackage.start = with vimPlugins; [ + editorconfig-vim + vim-eunuch + vim-nix + vim-sleuth + vim-surround + vim-unimpaired + ]; + }; + }; + + environment = { + systemPackages = [config.programs.vim.package]; + variables.EDITOR = mkOverride 100 "vim"; + }; + }; +} diff --git a/modules/nixfiles/vim/rc.vim b/modules/nixfiles/vim/rc.vim new file mode 100644 index 0000000..c53b2d2 --- /dev/null +++ b/modules/nixfiles/vim/rc.vim @@ -0,0 +1,252 @@ +set nocompatible + +let $VIMFILES = expand('<sfile>:p:h') + +let g:skip_defaults_vim = 1 + +let g:netrw_dirhistmax = 0 + +set autoread +set backspace=indent,eol,start +set clipboard=unnamed,unnamedplus +set diffopt+=iwhite +set hidden +set history=256 +set lazyredraw +set mouse= +set path+=** +set tabpagemax=50 +set viminfo= + +set cmdheight=1 +set display+=lastline +set fillchars=vert:\ " +set laststatus=2 +set modeline +set noshowmode +set ruler +set shortmess+=I +set textwidth=0 +set title + +set sessionoptions-=options +set viewoptions-=options + +set noerrorbells +set novisualbell + +set splitbelow +set splitright + +set complete= +set complete+=. +set complete+=b +set complete+=t +set completeopt= +set completeopt+=menu +set completeopt+=longest + +set gdefault +set hlsearch +set incsearch +set iskeyword+=- +set magic + +set foldmethod=marker +set nofoldenable + +set shortmess= +set shortmess+=I +set shortmess+=T +set shortmess+=a +set shortmess+=c +set shortmess+=t + +set nolist +set nowrap + +set scrolloff=10 +set sidescrolloff=10 + +set number +if v:version >= 700 + set numberwidth=3 +endif + +set wildignorecase +set wildmenu +set wildignore= + +set nobackup +set noswapfile +set noundofile +set nowritebackup + +set smartcase +set ignorecase + +set autoindent +set breakindent +set smartindent + +set expandtab +set shiftround +set shiftwidth=4 +set smarttab +set softtabstop=4 +set tabstop=4 + +autocmd BufEnter *.* :set colorcolumn= + +if &t_Co == 8 && $TERM !~# '^Eterm' + set t_Co=16 +endif + +if &listchars ==# 'eol:$' + set listchars=tab:>\ ,trail:-,extends:>,precedes:<,nbsp:+ +endif + +if v:version > 703 || v:version == 703 && has("patch541") + set formatoptions+=j +endif + +if has('path_extra') + setglobal tags-=./tags tags-=./tags; tags^=./tags; +endif + +if !has('nvim') && &ttimeoutlen == -1 + set ttimeout + set ttimeoutlen=100 +endif + +try + set encoding=utf-8 + scriptencoding utf-8 +catch +endtry + +try + if &fileencodings !~? "utf-8" + let g:added_fenc_utf8 = 1 + set fileencodings+=utf-8 + endif +catch +endtry + +if has('autocmd') + filetype plugin indent on + + if exists("+omnifunc") + autocmd Filetype * + \ if &omnifunc == "" | + \ setlocal omnifunc=syntaxcomplete#Complete | + \ endif + endif + + autocmd BufEnter * set noreadonly +endif + +if has('syntax') && !exists('g:syntax_on') + syntax enable +endif + +if !exists('g:loaded_matchit') && findfile('plugin/matchit.vim', &rtp) ==# '' + runtime! macros/matchit.vim +endif + +nnoremap <SPACE> <nop> +let mapleader=" " + +nnoremap :W :w +nnoremap :W! :w! +nnoremap :Q :q +nnoremap :Q! :q! + +nnoremap <Expr> j v:count ? 'j' : 'gj' +nnoremap <Expr> k v:count ? 'k' : 'gk' + +nnoremap J gt +nnoremap K gT + +nnoremap <C-a> ^h +vnoremap <C-a> ^h +nnoremap H ^h +vnoremap H ^h + +nnoremap <C-e> $ +vnoremap <C-e> $ +nnoremap L $ +vnoremap L $ + +nnoremap N Nzzzv +nnoremap n nzzzv + +inoremap <C-u> <C-g>u<C-u> +inoremap <C-w> <C-g>u<C-w> + +vnoremap < <gv +vnoremap > >gv +vnoremap <Tab> >gv +vnoremap <S-Tab> <gv +nnoremap <Tab> >>_ +nnoremap <S-Tab> <<_ + +nnoremap ]b :<C-u>bnext<CR> +nnoremap [b :<C-u>bprevious<CR> + +nnoremap <C-h> <C-w>h +nnoremap <C-j> <C-w>j +nnoremap <C-k> <C-w>k +nnoremap <C-l> <C-w>l + +nnoremap * /\<<C-r>=expand('<cword>')<CR>\><CR> +nnoremap # ?\<<C-r>=expand('<cword>')<CR>\><CR> + +nnoremap <C-L> :<C-u>nohlsearch<C-r>=has('diff')?'<Bar>diffupdate':''<CR><CR><C-l> + +cnoremap ;/ <C-r>=expand('%:p:h').'/'<CR> +cnoremap ;; <C-r>=expand('%:t')<CR> +cnoremap ;. <C-r>=expand('%:p:r')<CR> + +nnoremap <Leader>. :<C-u>lcd %:p:h<CR> + +nnoremap Q @q + +nnoremap <Leader>c ^v$h +nnoremap <Leader>v ggVG + +nnoremap <Leader>y "+y +nnoremap <Leader>Y "+Y + +nnoremap <Leader>p "+p +nnoremap <Leader>P "+P + +inoremap <C-v> <C-c>"+pi +cnoremap <C-v> <C-r>+ + +nmap <Leader>w :<C-u>w!<CR> +nmap <Leader>wq :<C-u>wq!<CR> + +nnoremap ZX :<C-u>qa!<CR> + +nnoremap <Leader>q :<C-u>q<CR> + +command WS w !sudo tee "%" >/dev/null + +function! SwitchCase() + normal! ~ + if strlen(getline('.')) != virtcol('.') + normal! h + endif +endfunction +nnoremap ~ :<C-u>call SwitchCase()<CR> + +function! s:GM() + execute 'normal! ^' + let first_col = virtcol('.') + execute 'normal! g_' + let last_col = virtcol('.') + execute 'normal! ' . (first_col + last_col) / 2 . '|' +endfunction +nnoremap gm :<C-u>call <SID>GM()<CR> +onoremap gm :<C-u>call <SID>GM()<CR> diff --git a/modules/nixfiles/vscode.nix b/modules/nixfiles/vscode.nix new file mode 100644 index 0000000..b41d037 --- /dev/null +++ b/modules/nixfiles/vscode.nix @@ -0,0 +1,172 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.vscode; +in { + options.nixfiles.modules.vscode = { + enable = mkEnableOption "Whether to enable VSCode."; + + package = with pkgs; + mkOption { + type = types.enum [vscodium vscode vscode-fhs]; + default = vscodium; + description = "Which package to use as a VSCode implementation."; + }; + + vim.enable = mkOption { + type = types.bool; + default = true; + description = "Whether to enable Vim emulation."; + }; + }; + + config = mkIf cfg.enable { + hm.programs.vscode = with config.nixfiles; + with modules; + with profiles; { + enable = true; + + inherit (cfg) package; + + extensions = with pkgs; + with vscode-extensions; + [editorconfig.editorconfig file-icons.file-icons redhat.vscode-yaml] + ++ optional cfg.vim.enable vscodevim.vim + ++ vscode-utils.extensionsFromVscodeMarketplace [ + { + name = "vscode-xml"; + publisher = "redhat"; + version = "0.20.0"; + sha256 = "sha256-GKBrf9s8n7Wv14RSfwyDma1dM0fGMvRkU/7v2DAcB9A="; + } + ]; + + userSettings = let + font = config.fontScheme.monospaceFont; + fontFamily = font.family; + fontSize = font.size; + in { + editor = + { + inherit fontFamily fontSize; + inlayHints = {inherit fontFamily fontSize;}; + codeLens = false; + cursorStyle = "block"; + detectIndentation = true; + minimap.enabled = false; + renderWhitespace = "trailing"; + rulers = [80 120]; + smoothScrolling = false; + tabCompletion = true; + } + // (let + surround = 10; + in { + cursorSurroundingLines = surround; + scrollBeyondLastColumn = surround; + }); + + keyboard.dispatch = "keyCode"; + + diffEditor.codeLens = false; + + files = { + autoSave = "off"; + enableTrash = false; + }; + + workbench = { + activityBar.visible = false; + editor.highlightModifiedTabs = true; + enableExperiments = false; + settings.enableNaturalLanguageSearch = false; + startupEditor = "none"; + tips.enabled = false; + tree.indent = 4; + welcomePage = { + walkthroughs.openOnInstall = false; + preferReducedMotion = true; + }; + }; + + debug.console = {inherit fontFamily fontSize;}; + + scm = { + inputFontFamily = fontFamily; + inputFontSize = fontSize; + }; + + extensions = { + autoCheckUpdates = false; + autoUpdate = false; + ignoreRecommendations = true; + }; + + terminal = { + external.linuxExec = + if alacritty.enable + then "${pkgs.alacritty}/bin/alacritty" + else "${pkgs.xterm}/bin/xterm}"; + + integrated = { + inherit fontFamily fontSize; + enableBell = true; + }; + }; + + update = { + mode = "none"; + showReleaseNotes = false; + }; + + telemetry = { + enableCrashReporter = false; + enableTelemetry = false; + }; + + security.workspace.trust.enabled = false; + + git.allowForcePush = true; + + vim = let + applyInputMethod = { + "ibus" = let + bin = "${pkgs.ibus}/bin/ibus"; + in { + enable = true; + defaultIM = "xkb:us::eng"; + obtainIMCmd = "${bin} engine"; + switchIMCmd = "${bin} engine {im}"; + }; + "fcitx" = let + bin = "${pkgs.fcitx}/bin/fcitx-remote"; + in { + enable = true; + defaultIM = "1"; + obtainIMCmd = bin; + switchIMCmd = "${bin} -t {im}"; + }; + }; + in + mkIf cfg.vim.enable rec { + easymotion = true; + easymotionMarkerFontFamily = fontFamily; + easymotionMarkerFontSize = fontSize; + + leader = " "; + + useSystemClipboard = true; + + autoSwitchInputMethod = let + inputMethod = config.i18n.inputMethod.enabled; + in + mkIf (inputMethod != null) applyInputMethod.${inputMethod}; + }; + }; + }; + }; +} diff --git a/modules/nixfiles/wget.nix b/modules/nixfiles/wget.nix new file mode 100644 index 0000000..9e4db27 --- /dev/null +++ b/modules/nixfiles/wget.nix @@ -0,0 +1,36 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.wget; +in { + options.nixfiles.modules.wget.enable = + mkEnableOption "Whether to enable wget."; + + config = mkIf cfg.enable { + hm = { + home.file.".wgetrc".text = '' + adjust_extension = on + dirstruct = off + follow_ftp = on + passive_ftp = off + progress = bar + quota = inf + reclevel = 5 + recursive = off + robots = off + timestamping = off + tries = 5 + wait = 0 + waitretry = 10 + ''; + + programs.bash.shellAliases.wget = "${pkgs.wget}/bin/wget --hsts-file=${config.hm.xdg.cacheHome}/wget-hsts"; + }; + + environment.systemPackages = with pkgs; [wget]; + }; +} diff --git a/modules/nixfiles/wireguard.nix b/modules/nixfiles/wireguard.nix new file mode 100644 index 0000000..1da3e74 --- /dev/null +++ b/modules/nixfiles/wireguard.nix @@ -0,0 +1,208 @@ +{ + config, + lib, + pkgs, + this, + ... +}: +with lib; let + cfg = config.nixfiles.modules.wireguard; +in { + options.nixfiles.modules.wireguard = { + # TODO Make this simpler. + privateKeyFile = mkOption { + description = "Path to the private key file."; + type = with types; nullOr string; + default = null; + }; + + client = { + enable = mkEnableOption "Whether to enable WireGuard client."; + + enableTrafficRouting = mkOption { + description = "Whether to enable traffic routing through the sever."; + type = with types; bool; + default = !this.isHeadless; + }; + }; + + server = { + enable = mkEnableOption '' + Whether to enable WireGuard server. This module requires + <option>networking.nat.externalInterface</option> to be set. + ''; + + ipv4.address = mkOption { + description = "IPv4 address to bind to."; + type = with types; str; + default = my.configurations.manwe.wireguard.ipv4.address; + }; + + ipv6.address = mkOption { + description = "IPv4 address to bind to."; + type = with types; str; + default = my.configurations.manwe.wireguard.ipv6.address; + }; + + address = mkOption { + description = "Endpoint address to use"; + type = with types; str; + default = my.configurations.manwe.ipv4.address; + }; + + port = mkOption { + description = "Endpoint port to use."; + type = with types; int; + default = 6969; + }; + + publicKey = mkOption { + description = "Server's public key."; + type = with types; str; + default = my.configurations.manwe.wireguard.publicKey; + }; + + peers = mkOption { + description = "List of peers."; + type = with types; listOf attrs; + default = mapAttrsToList (_: attr: + with attr; { + inherit (wireguard) publicKey; + allowedIPs = with wireguard; [ + "${ipv4.address}/32" + "${ipv6.address}/128" + ]; + }) (filterAttrs (_: attr: + attr.hostname != this.hostname && hasAttr "wireguard" attr) + my.configurations); + }; + }; + + interface = mkOption { + description = "Name of the interface to use WireGuard with."; + type = with types; str; + default = "wg69"; + }; + + ipv4.subnet = mkOption { + description = "CIDR notation for the IPv4 subnet to use over WireGuard."; + type = with types; str; + default = "10.69.0.0/16"; + }; + + ipv6.subnet = mkOption { + description = "CIDR notation for the IPv6 subnet to use over WireGuard."; + type = with types; str; + default = "fd69::/16"; + }; + }; + + config = + { + assertions = [ + { + assertion = cfg.privateKeyFile != null; + message = "Key file must be specified."; + } + { + assertion = config.security.sudo.enable; + message = "Sudo is not enabled."; + } + { + assertion = any (x: x == "wheel") config.my.extraGroups; + message = ''User is not in the "wheel" group.''; + } + ]; + } + // mkMerge [ + (mkIf (cfg.client.enable || cfg.server.enable) { + networking.firewall.trustedInterfaces = [cfg.interface]; + }) + (mkIf cfg.client.enable { + networking.wg-quick.interfaces.${cfg.interface} = mkMerge [ + (with this.wireguard; { + inherit (cfg) privateKeyFile; + address = ["${ipv4.address}/16" "${ipv6.address}/16"]; + }) + (with cfg.server; { + peers = [ + { + inherit publicKey; + endpoint = "${address}:${toString port}"; + allowedIPs = + if cfg.client.enableTrafficRouting + then [ + "0.0.0.0/0" + "::/0" + ] + else [ + cfg.ipv4.subnet + cfg.ipv6.subnet + ]; + persistentKeepalive = 25; + } + ]; + dns = [ + ipv4.address + ipv6.address + ]; # This assumes that the host has Unbound running. + }) + ]; + + environment.systemPackages = with pkgs; [ + (writeShellApplication { + name = "wg-toggle"; + runtimeInputs = [iproute2 jq wireguard-tools]; + text = '' + ip46() { + sudo ip -4 "$@" + sudo ip -6 "$@" + } + + fwmark=$(sudo wg show ${cfg.interface} fwmark) || exit + if ip -j rule list lookup "$fwmark" | jq -e 'length > 0' >/dev/null; then + ip46 rule del lookup main suppress_prefixlength 0 + ip46 rule del lookup "$fwmark" + else + ip46 rule add not fwmark "$fwmark" lookup "$fwmark" + ip46 rule add lookup main suppress_prefixlength 0 + fi + ''; + }) + ]; + }) + (mkIf cfg.server.enable { + networking = { + wireguard = { + enable = true; + interfaces.${cfg.interface} = with cfg.server; { + inherit (cfg) privateKeyFile; + ips = ["${ipv4.address}/16" "${ipv6.address}/16"]; + listenPort = port; + inherit peers; + allowedIPsAsRoutes = false; + }; + }; + + nat = { + enable = true; + enableIPv6 = true; + + internalInterfaces = [cfg.interface]; + internalIPs = [cfg.ipv4.subnet]; + internalIPv6s = [cfg.ipv6.subnet]; + }; + + firewall.allowedUDPPorts = [cfg.server.port]; + }; + + # # TODO Dashboard for this. + # services.prometheus.exporters.wireguard = { + # enable = true; + # listenAddress = mkDefault this.wireguard.ipv4.address; + # withRemoteIp = true; + # port = 9586; + # }; + }) + ]; +} diff --git a/modules/nixfiles/x11.nix b/modules/nixfiles/x11.nix new file mode 100644 index 0000000..ff0feaf --- /dev/null +++ b/modules/nixfiles/x11.nix @@ -0,0 +1,110 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.x11; +in { + options.nixfiles.modules.x11.enable = mkEnableOption "Whether to enable X11."; + + config = mkIf cfg.enable { + nixfiles.modules.fonts.enable = true; + + hm = { + home = { + packages = with pkgs; + [xclip xdotool] + ++ (with xorg; [ + xdpyinfo + xdriinfo + xev + xfontsel + xkill + xlsatoms + xlsclients + xlsfonts + xprop + xrandr + xwininfo + ]); + + sessionVariables = with config.dirs; { + XCOMPOSEFILE = "${cache}/XComposeFile"; + XCOMPOSECACHE = "${cache}/XComposeCache"; + }; + }; + + xsession.scriptPath = ".xinitrc"; + + xresources.properties = with config.nixfiles.modules; let + font = with config.fontScheme.monospaceFont; "${family}:style=${style}:size=${toString size}"; + in + { + "*.font" = font; + + "Xft.antialias" = 1; + "Xft.autohint" = 0; + "Xft.dpi" = 96; + "Xft.hinting" = 1; + "Xft.hintstyle" = "hintslight"; + "Xft.lcdfilter" = "lcddefault"; + "Xft.rgba" = "rgb"; + } + // (with profiles.common.colourScheme; { + "*.color0" = black; + "*.color8" = brightBlack; + "*.color1" = red; + "*.color9" = brightRed; + "*.color2" = green; + "*.color10" = brightGreen; + "*.color3" = yellow; + "*.color11" = brightYellow; + "*.color4" = blue; + "*.color12" = brightBlue; + "*.color5" = magenta; + "*.color13" = brightMagenta; + "*.color6" = cyan; + "*.color14" = brightCyan; + "*.color7" = white; + "*.color15" = brightWhite; + + "*.background" = background; + "*.foreground" = foreground; + }); + }; + + services.xserver = { + enable = true; + + tty = mkDefault 1; + + autoRepeatDelay = 200; + autoRepeatInterval = 25; + + libinput.enable = true; + + monitorSection = '' + Option "DPMS" "false" + ''; + + serverFlagsSection = '' + Option "BlankTime" "0" + Option "OffTime" "0" + Option "StandbyTime" "0" + Option "SuspendTime" "0" + ''; + + inputClassSections = [ + '' + Identifier "Mouse" + MatchIsPointer "yes" + Option "AccelerationNumerator" "2" + Option "AccelerationDenominator" "1" + Option "AccelerationThreshold" "4" + '' + ]; + }; + }; +} diff --git a/modules/nixfiles/xmonad.nix b/modules/nixfiles/xmonad.nix new file mode 100644 index 0000000..566ec53 --- /dev/null +++ b/modules/nixfiles/xmonad.nix @@ -0,0 +1,28 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.nixfiles.modules.xmonad; +in { + options.nixfiles.modules.xmonad.enable = + mkEnableOption "Whether to enable XMonad."; + + config = mkIf cfg.enable { + nixfiles.modules.x11.enable = true; + + hm = { + xsession = { + enable = true; + + scriptPath = ".xinitrc"; + + windowManager.command = "${pkgs.xmonad-ng}/bin/xmonad-ng"; + }; + }; + + services.xserver.displayManager.startx.enable = true; + }; +} diff --git a/modules/nixfiles/zathura.nix b/modules/nixfiles/zathura.nix new file mode 100644 index 0000000..d4e11e6 --- /dev/null +++ b/modules/nixfiles/zathura.nix @@ -0,0 +1,123 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.nixfiles.modules.zathura; +in { + options.nixfiles.modules.zathura.enable = + mkEnableOption "Whether to enable zathura PDF reader."; + + config = mkIf cfg.enable { + hm.programs.zathura = with config.nixfiles.modules; { + enable = true; + + options = + (with profiles.common.colourScheme; { + default-fg = white; + default-bg = black; + + statusbar-fg = black; + statusbar-bg = white; + + inputbar-fg = black; + inputbar-bg = brightGreen; + + notification-fg = black; + notification-bg = brightBlue; + + notification-warning-fg = black; + notification-warning-bg = brightYellow; + + notification-error-fg = black; + notification-error-bg = brightRed; + + highlight-color = brightYellow; + highlight-active-color = yellow; + + completion-fg = brightWhite; + completion-bg = brightBlack; + + completion-highlight-fg = black; + completion-highlight-bg = brightRed; + + completion-group-fg = black; + completion-group-bg = brightRed; + + recolor-darkcolor = black; + recolor-lightcolor = white; + }) + // { + recolor = true; + recolor-keephue = false; + recolor-reverse-video = false; + + highlight-transparency = "0.3"; + + font = fonts.fontScheme.monospaceFont.family; + + n-completion-items = 10; + + guioptions = ""; + + statusbar-basename = true; + statusbar-home-tilde = true; + + statusbar-h-padding = 0; + statusbar-v-padding = 0; + + window-height = 800; + window-width = 600; + + window-icon = ""; + + abort-clear-search = true; + + incremental-search = true; + + adjust-open = "best-fit"; + + advance-pages-per-row = false; + + database = "sqlite"; + + dbus-service = false; + + page-padding = 0; + + pages-per-row = 1; + + render-loading = false; + + show-directories = true; + show-hidden = true; + show-recent = 10; + + link-zoom = true; + link-hadjust = true; + + window-title-basename = true; + window-title-home-tilde = true; + window-title-page = true; + + zoom-center = false; + zoom-max = 1000; + zoom-min = 10; + zoom-step = 10; + + scroll-hstep = -1; + scroll-step = 40; + scroll-full-overlap = 0; + scroll-wrap = true; + scroll-page-aware = false; + + selection-clipboard = + if (kde.enable || gnome.enable) + then "clipboard" + else "primary"; + selection-notification = false; + }; + }; + }; +} diff --git a/packages/bruh.nix b/packages/bruh.nix new file mode 100644 index 0000000..a555fcc --- /dev/null +++ b/packages/bruh.nix @@ -0,0 +1,17 @@ +{ + fetchFromGitHub, + stdenv, +}: +stdenv.mkDerivation rec { + pname = "bruh"; + version = "2.1"; + + src = fetchFromGitHub { + owner = "kejpies"; + repo = pname; + rev = version; + hash = "sha256-Uw6Qes0IZkkfBchFnvnX9l1ZG5T5pyExmV7yUJLPOJ0="; + }; + + makeFlags = ["PREFIX=$(out)"]; +} diff --git a/packages/mpv-autosub.nix b/packages/mpv-autosub.nix new file mode 100644 index 0000000..cebab76 --- /dev/null +++ b/packages/mpv-autosub.nix @@ -0,0 +1,28 @@ +{ + fetchFromGitHub, + python3Packages, + stdenvNoCC, +}: +stdenvNoCC.mkDerivation rec { + pname = "mpv-autosub"; + version = "unstable-"; + + src = fetchFromGitHub { + owner = "davidde"; + repo = pname; + rev = "35115355bd339681f97d067538356c29e5b14afa"; + hash = "sha256-BKT/Tzwl5ZA4fbdc/cxz0+CYc1zyY/KOXc58x5GYow0="; + }; + + postPatch = '' + substituteInPlace autosub.lua \ + --replace '/home/david/.local/bin/subliminal' \ + '${python3Packages.subliminal}/bin/subliminal' + ''; + + installPhase = '' + install -Dm644 autosub.lua $out/share/mpv/scripts/autosub.lua + ''; + + passthru.scriptName = "autosub.lua"; +} diff --git a/packages/myip.nix b/packages/myip.nix new file mode 100644 index 0000000..a830139 --- /dev/null +++ b/packages/myip.nix @@ -0,0 +1,12 @@ +{ + dnsutils, + writeShellApplication, +}: +writeShellApplication { + name = "myip"; + runtimeInputs = [dnsutils]; + text = '' + dig -4 +short @resolver1.opendns.com myip.opendns.com A + dig -6 +short @resolver1.opendns.com myip.opendns.com AAAA + ''; +} diff --git a/packages/nixfiles.nix b/packages/nixfiles.nix new file mode 100644 index 0000000..90a5865 --- /dev/null +++ b/packages/nixfiles.nix @@ -0,0 +1,82 @@ +{ + git, + jq, + lib, + nix, + nixfilesSrc ? "$HOME/src/nixfiles", + openssh, + writeShellApplication, +}: +writeShellApplication { + name = "nixfiles"; + runtimeInputs = [git jq nix openssh]; + text = '' + nixfiles=${lib.escapeShellArg nixfilesSrc} + cmd="$1" + shift + case $cmd in + update) + if (( $# )); then + args=() + for input do args+=(--update-input "$input"); done + exec nix flake lock "$nixfiles" "''${args[@]}" + else + exec nix flake update -v "$nixfiles" + fi + ;; + rev) + nix flake metadata --json nixfiles | + if (( $# )); then + jq -r --arg input "$1" '.locks.nodes[$input].locked.rev' + else + jq -r '.revision // "dirty"' + fi + ;; + repl|eval|build) + args=() + for arg do case $arg in + -w|--wip) args+=(--override-flake config "$nixfiles") + ;; + *) args+=("$arg") + esac done + set -- "''${args[@]}" + ;;& + repl) + exec nix repl ~/.nix-defexpr "$@" + ;; + eval) + exec nix eval -f ~/.nix-defexpr --json "$@" | jq -r . + ;; + build) + exec nix build -f ~/.nix-defexpr --json "$@" | jq -r . + ;; + home) + attr="nixosConfigurations.$HOSTNAME.config.hm.home.activationPackage" + export VERBOSE=1 + exec nix shell --verbose "$nixfiles#$attr" "$@" --command home-manager-generation + ;; + specialise) + name=$1 + shift + exec sudo /run/current-system/specialisation/"$name"/bin/switch-to-configuration switch + ;; + revert) + exec sudo /nix/var/nix/profiles/system/bin/switch-to-configuration switch + ;; + test-headless) + exec nixos-rebuild build-vm --verbose --print-build-logs --flake "$nixfiles#test-headless" "$@" + ;; + test-headful) + exec nixos-rebuild build-vm --verbose --print-build-logs --flake "$nixfiles#test-headful" "$@" + ;; + @*) + host="''${cmd#@}" + hostname="$(ssh -q "$host" 'echo "$HOSTNAME"')" + exec nixos-rebuild -v --flake "$nixfiles#$hostname" --target-host "$host" --use-remote-sudo "$@" + ;; + *) + exec nixos-rebuild -v --flake "$nixfiles" --use-remote-sudo "$cmd" "$@" + ;; + esac + ''; +} diff --git a/packages/throttled.nix b/packages/throttled.nix new file mode 100644 index 0000000..ff896cf --- /dev/null +++ b/packages/throttled.nix @@ -0,0 +1,36 @@ +{ + fetchFromGitHub, + kmod, + python3, + stdenv, +}: +stdenv.mkDerivation rec { + pname = "throttled"; + version = "unstable-2022-06-30"; + + src = fetchFromGitHub { + owner = "erpalma"; + repo = pname; + rev = "ab9641a93a409bdc7fca6889ff23ca5a685e5a77"; + hash = "sha256-Zd+rTsVDkw9mq6k2OkMy+HxyYsl3ADy1fEM/1e/jxAs="; + }; + + nativeBuildInputs = with python3.pkgs; [wrapPython]; + + pythonPath = + (with python3.pkgs; [configparser dbus-python pygobject3]) + ++ [kmod]; + + installPhase = '' + runHook preInstall + + install -Dm755 -t $out/opt/throttled throttled.py + install -Dm644 -t $out/opt/throttled mmio.py + + runHook postInstall + ''; + + postFixup = '' + wrapPythonProgramsIn $out/opt/throttled "$out $pythonPath" + ''; +} diff --git a/packages/ultimmc.nix b/packages/ultimmc.nix new file mode 100644 index 0000000..e90d5bd --- /dev/null +++ b/packages/ultimmc.nix @@ -0,0 +1,63 @@ +{ + cmake, + fetchFromGitHub, + jdk, + makeDesktopItem, + qtbase, + stdenv, + wrapQtAppsHook, + zlib, +}: +stdenv.mkDerivation rec { + pname = "UltimMC"; + version = "unstable-2022-06-09"; + + src = fetchFromGitHub { + owner = "UltimMC"; + repo = "Launcher"; + rev = "b7c9b27ed6fb047da7fab5e4cee99b298b9e9811"; + hash = "sha256-xmkflvtOSLMTuzAKovgOLjgpbR8j53bkGV3QZRsQItw="; + fetchSubmodules = true; + }; + + nativeBuildInputs = [cmake jdk wrapQtAppsHook]; + + buildInputs = [qtbase zlib]; + + postPatch = '' + substituteInPlace CMakeLists.txt \ + --replace 'LIBRARY_DEST_DIR "bin"' 'LIBRARY_DEST_DIR "lib"' \ + --replace 'JARS_DEST_DIR "bin/jars"' 'JARS_DEST_DIR "share/${pname}/jar"' + + substituteInPlace launcher/Application.cpp \ + --replace '"jars"' '"../share/${pname}/jar"'; + ''; + + cmakeFlags = ["-DCMAKE_BUILD_TYPE=Release"]; + + desktopItem = makeDesktopItem { + name = pname; + exec = pname; + icon = pname; + desktopName = pname; + genericName = "Custom Minecraft launcher"; + categories = ["Game"]; + }; + + dontWrapQtApps = true; + + postInstall = '' + rm $out/${pname} + + wrapProgram $out/bin/${pname} \ + --add-flags "--dir \''${XDG_DATA_HOME:-~/.local/share}/${pname}" \ + "''${qtWrapperArgs[@]}" + + install -Dm644 \ + ${desktopItem}/share/applications/${pname}.desktop \ + $out/share/applications/${pname}.desktop + install -Dm644 \ + $src/notsecrets/logo.svg \ + $out/share/icons/hicolor/scalable/apps/${pname}.svg + ''; +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..0f8a3bb --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[tool.black] +line-length = 79 +target-version = ['py310'] diff --git a/readme.org b/readme.org new file mode 100644 index 0000000..a2c097a --- /dev/null +++ b/readme.org @@ -0,0 +1,42 @@ +#+options: ':t *:t -:t ::t <:t H:3 \n:nil ^:t arch:headline author:t +#+options: broken-links:nil c:nil creator:nil d:(not "LOGBOOK") date:t e:t +#+options: email:nil f:t inline:t num:nil p:nil pri:nil prop:nil stat:t tags:t +#+options: tasks:t tex:t timestamp:t title:t toc:t todo:t |:t +#+title: nixfiles +#+date: <2022-03-08 Tue> +#+author: Azat Bahawi +#+email: azat@bahawi.net +#+language: en +#+select_tags: export +#+exclude_tags: noexport +#+creator: Emacs 27.2 (Org mode 9.5) + +An IaC recipe for my digital infrastructure and configurations. An evolution of +the "dotfiles" thingy I had going for several years. + +If you stumbled on this repository on GitHub, GitLab and such, the version you +are currently looking at is a /"stripped"/ down rendition of *nixfiles* where IP +addresses, domain names, secrets and other sensitive information was removed or +replaced with gibberish. This is done so that you can get a general +understanding of how stuff is made without me spilling the beans too much... + +If you are looking to get into Nix and NixOS, I /highly/ suggest to take this +only as a reference and not just mindlessly copy-paste everything. + +* Inspiration and Credits + +Big thanks to everyone involved with Nix/NixOS/Nixpkgs and everything around +these! Hopefully, the Nix ecosystem will get a lot more traction and the +long-awaited industry-wide adoption. + +[[https://c.tenor.com/zJvexdmTjA4AAAAC/im-doing-my-part-serious.gif]] + +Special thanks for these fine people from whom I stole a lot of ideas for this +project: +- [[https://github.com/Mic92/dotfiles][Mic92]] +- [[https://github.com/codygman/hci][codygman]] +- [[https://github.com/davidtwco/veritas][davidtwco]] +- [[https://github.com/grahamc/nixos-config][grahamc]] +- [[https://github.com/gytis-ivaskevicius/nixfiles][gytis-ivaskevicius]] +- [[https://github.com/hlissner/dotfiles][hlissner]] +- [[https://github.com/ncfavier/config][ncfavier]] diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..3f15fc5 --- /dev/null +++ b/tox.ini @@ -0,0 +1,2 @@ +[flake8] +ignore = F401 |