diff --git a/.gitignore b/.gitignore index b75fae7..63cc0cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .stfolder secrets.nix +variables.nix archive/ glance/config.yaml result \ No newline at end of file diff --git a/squirtle.satstack.dev/configuration.nix b/squirtle.satstack.dev/configuration.nix new file mode 100644 index 0000000..9923bbc --- /dev/null +++ b/squirtle.satstack.dev/configuration.nix @@ -0,0 +1,346 @@ +{ config, pkgs, lib, unstablePkgs, ... }: + +let + importedVars = import ./variables.nix { inherit config pkgs lib unstablePkgs; }; +in +with importedVars; +{ + imports = [ + ./hardware-configuration.nix + ]; + boot = { + loader = { + systemd-boot.enable = true; + efi.canTouchEfiVariables = true; + }; + }; + networking = { + hostName = "${host_name}"; + firewall = { + # enable = false; # secure-node.nix enforces a firewall + allowedTCPPorts = [ + 22 + 80 + 443 + config.services.bitcoind.rpc.port + config.services.electrs.port + 9101 # lnd/prometheus + 9735 # lnd/p2p + 10009 # lnd/rpc + 10010 # lnd/rest + ]; + allowedTCPPortRanges = [ + { from = 4400; to = 4499; } + { from = 28332; to = 28334; } + ]; + }; + interfaces = { + eno1.ipv4.addresses = [{ + address = "192.168.0.44"; + prefixLength = 24; + }]; + }; + defaultGateway = { + address = "192.168.0.1"; + interface = "eno1"; + }; + nameservers = [ "1.1.1.1" "8.8.8.8" ]; + + wireguard.interfaces = let + secrets = import ./secrets.nix; + in { + lanturn = { + ips = [ "10.19.21.44/24" ]; + privateKey = "${secrets.wireguardPrivatekey}"; + peers = [{ + publicKey = "${wireguardPublicKey}"; + endpoint = "${lanturnAddress}:61420"; + allowedIPs = [ + "0.0.0.0/0" + ]; + }]; + }; + }; + # Adding routes this way is janky and probably means it's time to switch to systemd-networking for wireguard + localCommands = '' + ip route add ${lanturnAddress}/32 dev ${config.networking.defaultGateway.interface} via ${config.networking.defaultGateway.address} || true + ip route add 192.168.0.0/23 dev ${config.networking.defaultGateway.interface} via ${config.networking.defaultGateway.address} || true + ip route add 192.168.254.0/24 dev ${config.networking.defaultGateway.interface} via ${config.networking.defaultGateway.address} || true + ip route add 10.19.21.11/32 dev ${config.networking.defaultGateway.interface} via ${config.networking.defaultGateway.address} || true # timburr + ip route add 10.19.21.34/32 dev ${config.networking.defaultGateway.interface} via ${config.networking.defaultGateway.address} || true # weavile + ''; + }; + + time.timeZone = "America/Los_Angeles"; + + i18n.defaultLocale = "en_US.UTF-8"; + console = { + font = "Lat2-Terminus16"; +# keyMap = "us"; + useXkbConfig = true; # use xkbOptions in tty. + }; + + users = { + groups = { + bitcoind = {}; + lnd = {}; + }; + users = { + root = { + openssh.authorizedKeys.keyFiles = [ + # /etc/nixos/ssh/authorized_keys + ./ssh/authorized_keys + ]; + shell = pkgs.fish; # Set root's shell to fish + }; + pleb = { + openssh.authorizedKeys.keyFiles = [ + # /etc/nixos/ssh/authorized_keys + ./ssh/authorized_keys + ]; + isNormalUser = true; + extraGroups = [ "wheel" ]; + }; + + lnd = { + isSystemUser = true; + group = "lnd"; + home = "/var/lib/lnd"; + createHome = true; + description = "System account for lnd"; + shell = pkgs.fish; + openssh.authorizedKeys.keys = []; # fix ssh-agent warnings due to missing .ssh directory + packages = [ + unstablePkgs.lnd + ]; + # packages = with packagesForTesting; [ + # albyhub + # lnd + # ]; + }; + + }; + }; + + environment = { + variables = { EDITOR = "vim"; VISUAL = "vim"; }; + systemPackages = with pkgs; [ + cryptsetup + curl + difftastic + dig + dnsutils + doas + file + fzf + git + htop + iftop + iperf + jq + net-snmp # snmpwalk + netcat + nettools + parted + pass + psmisc + rsync + tcpdump + tmux + tree + unzip + vim + wget + whois + wireguard-tools + zip + ]; + shellInit = '' + pheonix() { + systemctl restart "$1" + journalctl -fu "$1" + } + ''; + }; + + programs = { + fish.enable = true; + vim = { + enable = true; + defaultEditor = true; + }; + bash = { + shellAliases = { + ll = "ls -lAF --classify --group-directories-first"; + l = "ls -lF --classify --group-directories-first"; + }; + # https://nixos.wiki/wiki/Fish + interactiveShellInit = interactiveShellInit; + }; + mtr.enable = true; + gnupg.agent = { + enable = true; + enableSSHSupport = true; + }; + }; + + security = { + sudo.enable = false; + doas = { + enable = true; + extraRules = [ + { + users = [ "pleb" ]; + persist = true; + } + ]; + }; + }; + + services = { + journald.extraConfig = "MaxRetentionSec=30day"; + + prometheus.exporters.node = { + enable = true; + port = 8030; +# openFirewall = true; + enabledCollectors = [ + "cpu.info" + "interrupts" + "netstat" + "vmstat" + "systemd" + "tcpstat" + "processes" + ]; + }; + + openssh.enable = true; + caddy = { + enable = false; + # package = caddyWithPlugins; + logFormat = "output discard"; + extraConfig = let + tlsConfig = '' + tls { + dns namecheap { + api_key {env.NAMECHEAP_API_KEY} + user {env.NAMECHEAP_API_USER} + api_endpoint https://api.namecheap.com/xml.response + } + } + ''; + in '' + ${host_fqdn} { # mempool + ${tlsConfig} + reverse_proxy http://127.0.0.1:${toString config.services.mempool.frontend.port} + } + ${host_fqdn}:4430 { # node_exporter + ${tlsConfig} + reverse_proxy http://127.0.0.1:8030 + } + + ${host_fqdn}:4431 { # albyhub + ${tlsConfig} + reverse_proxy http://127.0.0.1:8031 + } + + ${host_fqdn}:4432 { # lnd/prometheus + ${tlsConfig} + reverse_proxy http://127.0.0.1:9101 + } + + :9999 { + respond "success" + } + ''; + }; + }; + + systemd = { +# services.caddy = { # TODO: replace caddy with an override/overlay so we don't need this +# serviceConfig = { +# EnvironmentFile = "/var/src/secrets/namecheap"; +# ExecStart = [ +# "" # This empty string clears the existing ExecStart commands +# "/opt/bin/caddy run --config /etc/caddy/caddy_config --adapter caddyfile" +# ]; +# ExecReload = [ +# "" # This empty string clears the existing ExecReload commands +# "/opt/bin/caddy reload --config /etc/caddy/caddy_config --adapter caddyfile --force" +# ]; +# }; +# }; + services.lnd = { + description = "lightning network daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + unitConfig.ConditionPathIsMountPoint = "/mnt/${host_name}"; + + serviceConfig = { + Type = "notify"; + User = "lnd"; + Group = "lnd"; + ExecStart = "${unstablePkgs.lnd}/bin/lnd --configfile=/mnt/${host_name}/lnd/lnd.conf"; + Restart = "on-failure"; + RestartSec = "1m"; + }; + }; + + +# services.albyhub = { +# description = "lightning network daemon"; +# after = [ "network.target" ]; +# wantedBy = [ "multi-user.target" ]; +# unitConfig.ConditionPathIsMountPoint = "/mnt/${vars.host_name}"; + +# serviceConfig = { +# User = "lnd"; # albyhub uses the lnd admin macaroon +# Group = "lnd"; +# ExecStart = "${packagesForTesting.albyhub}/bin/http"; +# # Restart = "on-failure"; +# # RestartSec = "1m"; +# EnvironmentFile = "/mnt/${vars.host_name}/albyhub/.env"; +# WorkingDirectory = "/mnt/${vars.host_name}/albyhub"; +# }; +# }; + + + }; # systemd + + system.activationScripts.lndFishConfig = { + deps = [ "users" ]; # Ensure the lnd user and group are created before this script runs + text = '' + LND_HOME="/var/lib/lnd" + + # Ensure .config directory exists with correct owner/perms + ${pkgs.coreutils}/bin/install -d -o lnd -g lnd -m 0700 "$LND_HOME/.config" + + # Ensure .config/fish directory exists with correct owner/perms + ${pkgs.coreutils}/bin/install -d -o lnd -g lnd -m 0700 "$LND_HOME/.config/fish" + + # Install the config.fish file + ${pkgs.coreutils}/bin/install -o lnd -g lnd -m 0600 "${lndFishConfigFile}" "$LND_HOME/.config/fish/config.fish" + ''; + }; + + system.activationScripts.rootFishConfig = { + deps = [ "users" ]; # Ensure the root user is set up + text = '' + ROOT_HOME="/root" + + # Ensure .config directory exists with correct owner/perms + ${pkgs.coreutils}/bin/install -d -o root -g root -m 0700 "$ROOT_HOME/.config" + + # Ensure .config/fish directory exists with correct owner/perms + ${pkgs.coreutils}/bin/install -d -o root -g root -m 0700 "$ROOT_HOME/.config/fish" + + # Install the config.fish file + ${pkgs.coreutils}/bin/install -o root -g root -m 0600 "${rootFishConfigFile}" "$ROOT_HOME/.config/fish/config.fish" + ''; + }; + + nix.settings.experimental-features = [ "nix-command" "flakes" ]; + + system.stateVersion = "25.05"; +} diff --git a/squirtle.satstack.dev/flake.lock b/squirtle.satstack.dev/flake.lock new file mode 100644 index 0000000..7754bfd --- /dev/null +++ b/squirtle.satstack.dev/flake.lock @@ -0,0 +1,127 @@ +{ + "nodes": { + "extra-container": { + "inputs": { + "flake-utils": [ + "nix-bitcoin", + "flake-utils" + ], + "nixpkgs": [ + "nix-bitcoin", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1734005403, + "narHash": "sha256-vgh3TqfkFdnPxREBedw4MQehIDc3N8YyxBOB45n+AvU=", + "owner": "erikarvstedt", + "repo": "extra-container", + "rev": "f4de6c329b306a9d3a9798a30e060c166f781baa", + "type": "github" + }, + "original": { + "owner": "erikarvstedt", + "ref": "0.13", + "repo": "extra-container", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nix-bitcoin": { + "inputs": { + "extra-container": "extra-container", + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-unstable": "nixpkgs-unstable" + }, + "locked": { + "lastModified": 1734508046, + "narHash": "sha256-JN/PFBOVqWKc76zSdOunYoG5Q0m8W4zfrEh3V4EOIuk=", + "owner": "fort-nix", + "repo": "nix-bitcoin", + "rev": "33dbb41d581b86decf421cb3835c426d557e0e9c", + "type": "github" + }, + "original": { + "owner": "fort-nix", + "ref": "release", + "repo": "nix-bitcoin", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1734875076, + "narHash": "sha256-Pzyb+YNG5u3zP79zoi8HXYMs15Q5dfjDgwCdUI5B0nY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1807c2b91223227ad5599d7067a61665c52d1295", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-unstable": { + "locked": { + "lastModified": 1734126203, + "narHash": "sha256-0XovF7BYP50rTD2v4r55tR5MuBLet7q4xIz6Rgh3BBU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "71a6392e367b08525ee710a93af2e80083b5b3e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nix-bitcoin": "nix-bitcoin", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/squirtle.satstack.dev/flake.nix b/squirtle.satstack.dev/flake.nix new file mode 100644 index 0000000..ceaa34b --- /dev/null +++ b/squirtle.satstack.dev/flake.nix @@ -0,0 +1,93 @@ +{ + description = "NixOS configuration with nix-bitcoin"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05"; # mempool error: Node.js 18.x has reached End-Of-Life and has been removed + nixpkgs-2411.url = "github:NixOS/nixpkgs/nixos-24.11"; + nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable"; + nix-bitcoin.url = "github:fort-nix/nix-bitcoin/release"; + nix-bitcoin.inputs.nixpkgs.follows = "nixpkgs"; + }; + + outputs = { self, nixpkgs, nixpkgs-unstable, nixpkgs-2411, nix-bitcoin }: { + + nixosConfigurations.squirtle = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + specialArgs = { + unstablePkgs = import nixpkgs-unstable { + system = "x86_64-linux"; + }; + deprecatedPkgs = import nixpkgs-2411 { + system = "x86_64-linux"; + }; + }; + modules = [ + ./configuration.nix + + ({ config, pkgs, lib, unstablePkgs, deprecatedPkgs, ... }: { + nixpkgs.overlays = [ + (final: prev: { + # satisfy mempool's dependency on nodejs_18_x by pointing it at the 24.11 channel's NodeJS 18 + nodejs_18 = deprecatedPkgs.nodejs_18; + }) + ]; + }) + + nix-bitcoin.nixosModules.default + (nix-bitcoin + "/modules/presets/secure-node.nix") + { + nix-bitcoin = { + generateSecrets = true; + operator = { + enable = true; + name = "pleb"; + }; + onionServices.bitcoind.public = true; + }; + + services = { + bitcoind = { + enable = true; + # https://github.com/bitcoinknots/bitcoin + # package = config.nix-bitcoin.pkgs.bitcoind-knots; + disablewallet = true; + tor.enforce = false; # permit lan connections + rpc = { + address = "0.0.0.0"; + #port = 8332; + #threads = 6; + allowip = [ + "192.168.0.0/16" + "172.16.0.0/12" + "10.0.0.0/8" + ]; + }; + # dbCache = 1024; # defined in presets/secure-node.nix, so cannot be changed here + txindex = true; + zmqpubrawblock = "tcp://0.0.0.0:28332"; + zmqpubrawtx = "tcp://0.0.0.0:28333"; + extraConfig = '' + maxmempool=1024 + #zmqpubhashblock=tcp://0.0.0.0:28334 # dojo + maxorphantx=110 + # temporary fix for lnd versions earlier than v0.18.4 + deprecatedrpc=warnings + ''; + }; + electrs = { + enable = true; + address = "0.0.0.0"; + tor.enforce = false; # permit lan connections + }; + mempool.enable = true; + + # lnd autostarts on boot, but we want to wait for the mountpoint to be available + # moreover, we like using nix-bitcoin automatic secret generation for everything except lnd + # so instead of trying to make it work here, we configure lnd in configuration.nix + + }; + } + ]; + }; + }; +} \ No newline at end of file diff --git a/squirtle.satstack.dev/install.sh b/squirtle.satstack.dev/install.sh new file mode 100755 index 0000000..2513c9d --- /dev/null +++ b/squirtle.satstack.dev/install.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +set -e +set -x + +# Requires ssh access to target machine: ssh root@${TARGET} +TARGET=nixos + +function FORMAT_DISK () +{ + # Clear the beginning of the disk + dd if=/dev/zero of=/dev/nvme0n1 bs=1M count=8 + + # Create a new GPT partition table + parted /dev/nvme0n1 -- mklabel gpt + + # Create boot partition (ESP) + parted /dev/nvme0n1 -- mkpart primary fat32 1MiB 512MiB + parted /dev/nvme0n1 -- set 1 esp on + parted /dev/nvme0n1 -- name 1 squirtle_boot + + # Create crypt partition (256GB) + parted /dev/nvme0n1 -- mkpart primary 512MiB 256.5GiB + parted /dev/nvme0n1 -- name 2 squirtle_crypt + + # Create root partition (fills the rest of the drive) + parted /dev/nvme0n1 -- mkpart primary 256.5GiB 100% + parted /dev/nvme0n1 -- name 3 squirtle_root + + # Format the boot partition + mkfs.fat -F 32 -n boot /dev/nvme0n1p1 + + # Format the root partition + mkfs.ext4 -L nixos /dev/nvme0n1p3 + + # Mount the partitions + mount /dev/nvme0n1p3 /mnt + mkdir -p /mnt/boot + mount /dev/nvme0n1p1 /mnt/boot + + # Generate NixOS configuration + nixos-generate-config --root /mnt +} +# TODO: This part must be done manually, after the installation is complete +function ENCRYPT_DISK () +{ + # Set up LUKS encryption + cryptsetup luksFormat /dev/nvme0n1p2 + cryptsetup open /dev/nvme0n1p2 encrypted_squirtle + + # Format the encrypted partition + mkfs.ext4 -L crypted /dev/mapper/encrypted_squirtle + mkdir -p /mnt/squirtle +} + +echo "Install NixOS on $TARGET? Press enter to continue or ctrl+c to quit." +read + +ssh root@$TARGET "$(typeset -f FORMAT_DISK); FORMAT_DISK" + +scp configuration.nix secrets.nix flake.nix root@$TARGET:/mnt/etc/nixos/ + +# setup ssh access +ssh root@$TARGET mkdir -p /mnt/etc/nixos/ssh /etc/nixos/ssh +scp ~/.ssh/authorized_keys root@$TARGET:/mnt/etc/nixos/ssh/authorized_keys +scp ~/.ssh/authorized_keys root@$TARGET:/etc/nixos/ssh/authorized_keys + +# satstack.dev acme via namecheap api +#ssh root@$TARGET "mkdir -p /mnt/var/src/secrets && chmod 700 /mnt/var/src/secrets" +#echo "Prompting elevation for reckless satstack.dev acme secrets" +#file_content=$(doas cat /var/src/secrets/namecheap-satstack.dev) +#echo "$file_content" | ssh root@$TARGET "cat > /mnt/var/src/secrets/namecheap" + +ssh root@$TARGET "mkdir -p /var/src/secrets && chmod 700 /var/src/secrets" +#echo "$file_content" | ssh root@$TARGET "cat > /var/src/secrets/namecheap" + +ssh root@$TARGET nixos-install --flake /mnt/etc/nixos#squirtle + +## REMINDERS +# Don't forget to set your passwd +# To chroot: +#nixos-enter --root /mnt + +# Copy over ~/.config/fish from another host \ No newline at end of file