{ config, lib, pkgs, ... }: let inherit (lib) mkDefault mkIf mkMerge mkOption mkOverride types; cfg = config.beatific; mkFlag = description: mkOption { type = types.bool; inherit description; default = true; }; in { options = { beatific = { # The host name is reused for beatific-specific configuration. # The bulk of common config is handled in beatific.defaults below, but # having one option without a default ensures that the module cannot be # imported accidentally. hostName = mkOption { type = types.str; description = "Hostname"; }; isLighthouse = mkOption { type = types.bool; description = "Whether this host is a Nebula lighthouse"; default = false; }; extraPrograms = mkOption { type = types.bool; description = "Additional default programs"; default = false; }; # Groups of related defaults can be disabled by flipping off the switches here: # beatific.defaults.${category} = false; # They default to true because the point is to do these things by default. defaults = { time = mkFlag "Default time zone and NTP"; i18n = mkFlag "Default locale settings"; programs = mkFlag "Default installed programs"; ssh = mkFlag "Default sshd settings"; nebula = mkFlag "Default beatific nebula settings"; tvb = mkFlag "Default tvb account"; tvbSync = mkFlag "Configure system syncthing for tvb"; hosts = mkFlag "Default 10.22.20.* DNS host entries"; }; }; }; config = mkMerge [ { # Options to always set networking.hostName = cfg.hostName; nix.extraOptions = "experimental-features = nix-command flakes"; # Link /etc/nixos to the flake source environment.etc.nixos.source = ./..; environment.etc."bashrc.local".source = ./bashrc; environment.shellAliases = { # Shortcut for nixos-rebuild nr = "sudo nixos-rebuild --fast --flake $HOME/nixos-configs"; # Always preserve mode, ownership, ts with copy cp = "cp -rp"; xo = "xdg-open"; smv = "rsync -v --remove-source-files"; ffprobe = "ffprobe -hide_banner"; ffmpeg = "ffmpeg -hide_banner"; ".." = "cd .."; "..." = "cd ../.."; "...." = "cd ../../.."; "....." = "cd ../../../.."; }; security.sudo.extraRules = [{ users = [ "tvb" ]; commands = [ { command = "/run/current-system/sw/bin/nixos-rebuild"; options = [ "NOPASSWD" ]; } ]; }]; } (mkIf cfg.defaults.time { # mkDefault time zone to make it easy to configure it to non-UTC time.timeZone = mkDefault "UTC"; services.ntp.enable = true; services.ntp.servers = [ "time.nist.gov" ]; }) (mkIf cfg.defaults.i18n { # en_US.UTF-8 i18n.defaultLocale = "en_US.UTF-8"; i18n.extraLocaleSettings = { LC_ADDRESS = "en_US.UTF-8"; LC_IDENTIFICATION = "en_US.UTF-8"; LC_MEASUREMENT = "en_US.UTF-8"; LC_MONETARY = "en_US.UTF-8"; LC_NAME = "en_US.UTF-8"; LC_NUMERIC = "en_US.UTF-8"; LC_PAPER = "en_US.UTF-8"; LC_TELEPHONE = "en_US.UTF-8"; LC_TIME = "en_US.UTF-8"; }; }) (mkIf cfg.defaults.programs { environment.systemPackages = with pkgs; [ bat # colorized and numbered `less` bc # Terminal calculator curl # omnipotent URL tool duf # disk-free checker exiftool # media tag tool ffmpeg # omnipotent media tool file # file type inspector htmlq # jq for html jq # jq for json nebula # vpn poppler_utils # provides pdfto* utils, allows lesspipe to read pdfs psmisc # provides killall python3 # second-best language for everything ripgrep # fast file searcher rsync # incremental remote copy sqlite # omnipotent database tree # directory tree view unzip # .zip archive tool vim # terminal editor viu # terminal image "viewer" wget # web fetcher zip # .zip archive tool (writeShellScriptBin "clip" '' ${xclip}/bin/xclip -sel c < "$1" '') ]; programs = { git = { enable = true; config = { init.defaultBranch = "master"; merge.conflictstyle = "diff3"; alias = { amend = "commit --amend"; fixup = "commit --amend --no-edit"; pick = "cherry-pick"; }; }; }; htop.enable = true; }; # The nixpkgs default is "nano", so we go one priority higher environment.variables.EDITOR = mkOverride 999 "vim"; }) (mkIf cfg.extraPrograms { environment.systemPackages = with pkgs; [ (pkgs.writeShellScriptBin "ebook-convert" '' exec ${pkgs.calibre}/bin/ebook-convert "$@" '') imagemagick # image convertion cli puddletag # mp3 tag editor tesseract # OCR engine ]; }) (mkIf cfg.defaults.ssh { services.openssh.enable = true; services.openssh.settings.PrintMotd = true; environment.etc."motd".text = let ascii = import ./ascii.nix; in ascii.${cfg.hostName} or ascii.beatific; networking.firewall.allowPing = true; networking.firewall.allowedTCPPorts = [ 22 ]; }) (mkIf cfg.defaults.nebula { services.nebula.networks.beatific = let empyreanExternalDns = "vpn.alogoulogoi.com"; empyreanInternalIp = "10.22.20.1"; nebulaPort = 4242; in { enable = true; # The lighthouse only listens on the designated subdomain listen.host = if cfg.isLighthouse then empyreanExternalDns else "0.0.0.0"; listen.port = nebulaPort; # Standard certificate paths ca = "/etc/nebula/beatific/beatific.crt"; cert = "/etc/nebula/beatific/${cfg.hostName}.crt"; key = "/etc/nebula/beatific/${cfg.hostName}.key"; isLighthouse = cfg.isLighthouse; # Non-lighthouses connect to the lighthouse at empyrean # This should be a VPN address in the static host map lighthouses = mkIf (! cfg.isLighthouse) [ empyreanInternalIp ]; # Currently there is no VPN-level traffic filtering firewall.outbound = [ { port = "any"; proto = "any"; host = "any"; } ]; firewall.inbound = [ { port = "any"; proto = "any"; host = "any"; } ]; # Map the lighthouse address to its public address staticHostMap = { ${empyreanInternalIp} = [ "${empyreanExternalDns}:${toString nebulaPort}" ]; }; settings = { # Enable UDP holepunching both ways, which allows nodes to establish more direct connections with each other punchy = { punch = true; response = true; }; }; }; }) (mkIf cfg.defaults.tvb { users.groups.tvb = {}; users.users.tvb = { isNormalUser = true; group = "tvb"; extraGroups = [ "wheel" ]; initialPassword = "password"; openssh.authorizedKeys.keyFiles = (import ../keys).tvb; }; }) (mkIf cfg.defaults.tvbSync { # I haven't gotten user services to work correctly yet, # so for now, tvb monopolizes the system syncthing instance. # Adding users in the future should probably involve multiple # system services so as not to require login to sync. services.syncthing = { enable = true; configDir = "/home/tvb/.config/syncthing"; # this doesn't prevent syncthing from putting sync points in other locations, it's just a default # normally it would make sense to put it at ~ but see https://github.com/NixOS/nixpkgs/pull/273693 dataDir = "/home/tvb/.config/syncthing"; openDefaultPorts = true; user = "tvb"; group = "tvb"; }; }) (mkIf cfg.defaults.hosts { # Create *.home host entries for all the beatific members networking.hosts = { "10.22.20.1" = [ "empyrean.home" ]; "10.22.20.2" = [ "catacomb.home" "mopidy.home.ktvb.site" ]; "10.22.20.3" = [ "palamas.home" ]; "10.22.20.4" = [ "stagirite.home" ]; "10.22.20.5" = [ "vagrant.home" ]; "10.22.20.6" = [ "unfolder.home" ]; "10.22.20.7" = [ "centroid.home" ]; "10.22.20.8" = [ "backyard.home" "pool.backyard.home" "mirror.backyard.home" "jellyfin.home.ktvb.site" ]; "10.22.20.9" = [ "imperium.home" ]; }; }) ]; }