diff --git a/.gitignore b/.gitignore index 564e37f..e259209 100644 --- a/.gitignore +++ b/.gitignore @@ -20,10 +20,9 @@ build/Release .env.test.local .env.production.local .env.local -# wrangler project -.dev.vars -.wrangler/ # Others .direnv/ # Template rendering artifacts public/*.html +# Nix build result +result diff --git a/README.md b/README.md index 054687a..2192a10 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,15 @@ +# Nixifying a new host + +If you have a bunch of SSH keys in your SSH agent and get errors when trying to SSH into a fresh host, you may need to temporarily add the following config to your SSH config (obviously change the details for your case). +The key is the line "IdentitiesOnly yes" along with an identity file. + +``` +Host 188.245.194.78 + User root + IdentityFile ~/.ssh/hetzner_personal_root.pub + IdentitiesOnly yes +``` + # Adding yourself to the webring (Please only follow this if you're an Epesooj insider. Random PRs will be closed for now.) diff --git a/code_server.nix b/code_server.nix new file mode 100644 index 0000000..5fceb14 --- /dev/null +++ b/code_server.nix @@ -0,0 +1,168 @@ +{ self, moduleWithSystem, ... }: { + flake.nixosModules.code-server = moduleWithSystem ( + { ... }: # Note: only explicit parameters are passed to this. + { pkgs, modulesPath, lib, ... }: { + imports = [ + self.inputs.disko.nixosModules.disko + (modulesPath + "/installer/scan/not-detected.nix") + (modulesPath + "/profiles/headless.nix") + (modulesPath + "/profiles/minimal.nix") + (modulesPath + "/profiles/qemu-guest.nix") + ./code_server_disk.nix + ]; + + system.stateVersion = "24.11"; + + boot.kernelParams = [ "zfs.zfs_arc_max=536870912" ]; + boot.zfs.extraPools = [ "zroot" ]; + boot.initrd.postMountCommands = lib.mkAfter '' + zfs rollback -r zroot/root@blank + ''; + + services.zfs.autoScrub.enable = true; + boot.loader.grub = { + enable = true; + # No need to set devices, disko will add all devices that have an EF02 partition to the list already. + # devices = []; + efiSupport = true; + efiInstallAsRemovable = true; + }; + + fileSystems = { + "/var/lib/systemd" = { + device = "/persisted/var/lib/systemd"; + options = [ "bind" ]; + }; + "/var/lib/forgejo" = { + device = "/persisted/var/lib/forgejo"; + options = [ "bind" ]; + }; + }; + + networking.hostId = "9f1dfd86"; # Required by ZFS. + networking.useNetworkd = true; + networking.firewall.logRefusedConnections = false; + + nix.gc.automatic = true; + nix.gc.dates = "02:15"; + + services.cloud-init = { + enable = true; + network.enable = true; + settings = { + datasource_list = [ "Hetzner" ]; + + # The NixOS cloud-init settings declares the entire `system_info` with `lib.mkDefault`, so we need to copy the defaults from it here and make the changes we want to make. + system_info = { + paths = { + cloud_dir = "/persisted/var/lib/cloud"; + }; + distro = "nixos"; + network = { + renderers = [ "networkd" ]; + activators = [ "networkd" ]; + }; + }; + + cloud_init_modules = [ + "migrator" + "seed_random" + "bootcmd" + ]; + + cloud_config_modules = [ + "ssh-import-id" + "timezone" + "runcmd" + "ssh" + ]; + + cloud_final_modules = [ + "keys-to-console" + "final-message" + "power-state-change" + ]; + }; + }; + + services.openssh = { + enable = true; + hostKeys = [ + { + path = "/persisted/etc/ssh/ssh_host_ed25519_key"; + type = "ed25519"; + } + ]; + settings = { + PasswordAuthentication = false; + }; + }; + + users.users.root = { + home = lib.mkForce "/persisted/root"; + }; + + networking.firewall.allowedTCPPorts = [ 80 443 ]; + # UDP allowed for HTTP/3. + networking.firewall.allowedUDPPorts = [ 80 443 ]; + + services.caddy = { + enable = true; + + globalConfig = '' + # Comment this if building the prod image. The following is only useful for testing. + local_certs + skip_install_trust + ''; + + virtualHosts."code.akols.com".extraConfig = '' + encode zstd gzip + reverse_proxy http://127.0.0.1:3000 + + # @exclude_lfs not path *.git/info/lfs* + + # basic_auth @exclude_lfs { + # boardgamers $2a$14$Tni1.M8JUU4EXyWlVTL2jetDlWPamXtXZlYZizm2DtU.cwyLetbCm + # } + ''; + }; + + services.forgejo = { + enable = true; + + package = pkgs.forgejo; + lfs.enable = true; + + settings = { + service = { + DISABLE_REGISTRATION = true; + }; + + database = { + SQLITE_JOURNAL_MODE = "WAL"; + }; + + cache = { + ADAPTER = "twoqueue"; + HOST = "{\"size\":100,\"recent_ratio\":0.25,\"ghost_ratio\":0.5}"; + }; + + server = { + HTTP_ADDR = "127.0.0.1"; + HTTP_PORT = 3000; + DOMAIN = "code.akols.com"; + ROOT_URL = "https://code.akols.com"; + }; + + session = { + COOKIE_SECURE = true; + }; + + security = { + LOGIN_REMEMBER_DAYS = 365; + }; + }; + }; + } + ); +} diff --git a/code_server_disk.nix b/code_server_disk.nix new file mode 100644 index 0000000..ddb372e --- /dev/null +++ b/code_server_disk.nix @@ -0,0 +1,89 @@ +{ lib, ... }: +{ + disko.devices = { + disk.disk1 = { + device = lib.mkDefault "/dev/sda"; + type = "disk"; + content = { + type = "gpt"; + partitions = { + boot = { + size = "1M"; + type = "EF02"; + priority = 1; + }; + esp = { + name = "ESP"; + size = "500M"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + }; + }; + zfs = { + name = "zfs"; + size = "100%"; + content = { + type = "zfs"; + pool = "zroot"; + }; + }; + }; + }; + }; + + zpool = { + zroot = { + type = "zpool"; + mode = ""; + # May cause issues if we end up adding any pools that will be imported before boot, but at the time this was added, we only had a "zroot" pool that was always imported at boot via `boot.zfs.extraPools`, so this was fine. + options.cachefile = "none"; + rootFsOptions = { + compression = "zstd"; + "com.sun:auto-snapshot" = "false"; + canmount = "off"; + }; + mountpoint = null; + postCreateHook = '' + zfs list -t snapshot -H -o name | grep -E '^zroot/root@blank$' || zfs snapshot zroot/root@blank + ''; + postMountHook = '' + mkdir -p /mnt/persisted/var/lib/systemd + mkdir -p /mnt/persisted/etc/ssh + mkdir -p /mnt/persisted/secrets + mkdir -p /mnt/persisted/root + ''; + + datasets = { + root = { + type = "zfs_fs"; + mountpoint = "/"; + options.canmount = "on"; + }; + nix = { + type = "zfs_fs"; + mountpoint = "/nix"; + options.canmount = "on"; + }; + replicated = { + type = "zfs_fs"; + mountpoint = null; + options.canmount = "off"; + }; + "replicated/home" = { + type = "zfs_fs"; + mountpoint = "/home"; + options.canmount = "on"; + }; + "replicated/persisted" = { + type = "zfs_fs"; + mountpoint = "/persisted"; + options.canmount = "on"; + }; + }; + }; + }; + }; +} diff --git a/flake.lock b/flake.lock index 0faa826..32a38dd 100644 --- a/flake.lock +++ b/flake.lock @@ -1,15 +1,53 @@ { "nodes": { + "disko": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1739841949, + "narHash": "sha256-lSOXdgW/1zi/SSu7xp71v+55D5Egz8ACv0STkj7fhbs=", + "owner": "nix-community", + "repo": "disko", + "rev": "15dbf8cebd8e2655a883b74547108e089f051bf0", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "disko", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1738453229, + "narHash": "sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm+zmZ7vxbJdo=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "32ea77a06711b758da0ad9bd6a844c5740a87abd", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" }, "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -27,11 +65,11 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1726716330, - "narHash": "sha256-mIuOP4I51eFLquRaxMKx67pHmhatZrcVPjfHL98v/M8=", + "lastModified": 1732053863, + "narHash": "sha256-DCIVdlb81Fct2uwzbtnawLBC/U03U2hqx8trqTJB7WA=", "owner": "oxalica", "repo": "nil", - "rev": "c8e8ce72442a164d89d3fdeaae0bcc405f8c015a", + "rev": "2e24c9834e3bb5aa2a3701d3713b43a6fb106362", "type": "github" }, "original": { @@ -42,11 +80,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1727348695, - "narHash": "sha256-J+PeFKSDV+pHL7ukkfpVzCOO7mBSrrpJ3svwBFABbhI=", + "lastModified": 1740126099, + "narHash": "sha256-ozoOtE2hGsqh4XkTJFsrTkNxkRgShxpQxDynaPZUGxk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "1925c603f17fc89f4c8f6bf6f631a802ad85d784", + "rev": "32fb99ba93fea2798be0e997ea331dd78167f814", "type": "github" }, "original": { @@ -56,8 +94,22 @@ "type": "github" } }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1738452942, + "narHash": "sha256-vJzFZGaCpnmo7I6i416HaBLpC+hvcURh/BQwROcGIp8=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz" + } + }, "root": { "inputs": { + "disko": "disko", + "flake-parts": "flake-parts", "nil": "nil", "nixpkgs": "nixpkgs" } @@ -70,11 +122,11 @@ ] }, "locked": { - "lastModified": 1726453838, - "narHash": "sha256-pupsow4L79SBfNwT6vh/5RAbVZuhngIA0RTCZksXmZY=", + "lastModified": 1731983527, + "narHash": "sha256-JECaBgC0pQ91Hq3W4unH6K9to8s2Zl2sPNu7bLOv4ek=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "ca2e79cd22625d214b8437c2c4080ce79bd9f7d2", + "rev": "71287228d96e9568e1e70c6bbfa3f992d145947b", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 6a95f8d..49c3e9c 100644 --- a/flake.nix +++ b/flake.nix @@ -1,8 +1,14 @@ { - description = "Epesooj webring."; + description = "Epesooj webring and machine."; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + + disko = { + url = "github:nix-community/disko"; + inputs.nixpkgs.follows = "nixpkgs"; + }; nil = { url = "github:oxalica/nil"; @@ -10,23 +16,61 @@ }; }; - outputs = { self, nixpkgs, nil, ... }: - let - pkgs = import nixpkgs { - system = "x86_64-linux"; - }; - in - { - devShells.x86_64-linux = { - default = pkgs.mkShell { - packages = with pkgs; [ - nodePackages_latest.nodejs + outputs = inputs@{ flake-parts, nil, ... }: + flake-parts.lib.mkFlake { inherit inputs; } ({ config, getSystem, ... }: { + systems = [ "x86_64-linux" ]; - # Both of these used with VSCode. - nixpkgs-fmt - nil.packages.${system}.default + imports = [ + ./code_server.nix + ]; + + flake = + let + codeServerModules = [ + config.flake.nixosModules.code-server + { + networking.hostName = "epesooj-code-0001"; + } ]; + in + { + nixosConfigurations = { + code = inputs.nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + modules = codeServerModules; + }; + }; + + colmena = { + meta.nixpkgs = import inputs.nixpkgs { + system = "x86_64-linux"; + }; + + code = { + deployment = { + targetHost = "epesooj-code-0001"; # Requires an SSH config with information for this host. + }; + + imports = codeServerModules; + }; + }; + }; + + perSystem = { system, pkgs, ... }: { + devShells = { + default = pkgs.mkShell { + packages = with pkgs; [ + just + nodePackages_latest.nodejs + + colmena + + # Both of these used with VSCode. + nixpkgs-fmt + nil.packages.${system}.default + ]; + }; }; }; - }; + }); } diff --git a/justfile b/justfile new file mode 100644 index 0000000..bd926c2 --- /dev/null +++ b/justfile @@ -0,0 +1,8 @@ +default: + @just --list --justfile {{justfile()}} + +build_nixos_config host_type="code": + nix build .#nixosConfigurations.{{host_type}}.config.system.build.toplevel + +nixify_host hostname host_type="code": + nix run github:nix-community/nixos-anywhere -- --flake .#{{host_type}} --target-host root@{{hostname}}