nix-config/hosts/heather/default.nix

162 lines
6.9 KiB
Nix
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# heather — сервер на Yandex Cloud (NixOS 26.11).
# https://git.iscg.dev/mikl/nix-config
#
# Перенесено из muscari (переименование 2026-07-11).
# VM на Yandex Cloud, 2 vCPU / 8 GB / 64 GB SSD (network-ssd), Legacy BIOS.
# Образ NixOS собирается как qcow2 (см. ./image.nix, make-disk-image.nix) и
# заливается как boot disk в YC. НЕ через nixos-anywhere — kexec на YC хрупкий.
# См. memory/yandex-nixos-qcow2.md.
#
# Архитектура (2026-07-12, заменила k3s):
# Caddy (edge, TLS) → host-сервисы (syncthing) + docker-контейнеры (arion)
# Cilium/k3s выключены — overhead 1.3 GB был неоправдан для single-node.
# См. ./docker.nix, ./caddy.nix, ./syncthing.nix, ./arion-compose.nix.
{ config, pkgs, lib, ... }:
{
imports = [
./image.nix # YC guest profile + qcow2 build target
./docker.nix # docker + arion CLI (контейнеры)
./caddy.nix # edge proxy, TLS, роутинг по доменам
./syncthing.nix # sync + relay
./tuwunel-admin.nix # matrix admin web UI (systemd, статический бинарник)
./paseo.nix # paseo daemon (web UI для CLI-агентов, systemd + npm global)
./nick.nix # Nick timer prototype (systemd, localhost:8787)
./jupyter.nix # JupyterLab (systemd, localhost:8890, лимиты)
./mesh-clients.nix # Sol-style client pool (wg-clients1)
./mesh-egress.nix # Sol-style selected-destination egress policy
];
# Heather has 8 GiB RAM and runs several containers. Keep a 4 GiB
# swapfile so a transient application spike does not take down the host.
swapDevices = [
{
device = "/var/swapfile";
size = 4096;
}
];
# First server-to-server WireGuard backbone link: heather ↔ hyacinth.
# The private key is generated on the live host and stays outside the flake.
# Basic diagnostics/admin tools must be available even in non-login SSH
# sessions (and not depend on a stale Home Manager profile).
environment.systemPackages = with pkgs; [
wireguard-tools
tmux
jq
wget
python3
];
# NixOS uses scripted networking here (not systemd-networkd), so create the
# dummy identity interface through a small idempotent systemd unit.
systemd.services.mesh-identity = {
description = "Create mesh identity interface";
wantedBy = [ "network.target" ];
after = [ "wireguard-wg-mesh.service" "network-addresses-wg-mesh.service" ];
wants = [ "wireguard-wg-mesh.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = pkgs.writeShellScript "mesh-identity-up" ''
set -eu
${pkgs.iproute2}/bin/ip link show mesh0 >/dev/null 2>&1 || ${pkgs.iproute2}/bin/ip link add mesh0 type dummy
${pkgs.iproute2}/bin/ip addr replace 10.0.0.1/32 dev mesh0
${pkgs.iproute2}/bin/ip link set mesh0 up
'';
ExecStop = "${pkgs.iproute2}/bin/ip link del mesh0";
};
};
networking.wireguard.interfaces.wg-mesh = {
ips = [ "10.99.0.1/30" ];
listenPort = 51820;
privateKeyFile = "/var/lib/wireguard/wg-mesh.key";
# Keep WG as transport/crypto ACL; install the identity route explicitly below.
allowedIPsAsRoutes = false;
peers = [
{
publicKey = "BxTH8+MiA8Hv48UfpWJDVXIhbO5HgO2KTBEXIqqr23k=";
# Match all destinations for future policy routes; allowedIPsAsRoutes=false
# keeps this from installing a default route automatically.
allowedIPs = [ "0.0.0.0/0" ];
endpoint = "195.242.119.59:51820";
persistentKeepalive = 25;
}
];
};
networking.interfaces.wg-mesh.ipv4.routes = [
{ address = "10.0.0.2"; prefixLength = 32; }
];
# Needed later when this node becomes a transit/exit peer.
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
# Arion-проект как systemd-сервис: arion-compose.nix → docker-compose → up.
# NB: settings — это модуль с imports, НЕ `import ./arion-compose.nix`.
virtualisation.arion.backend = "docker";
virtualisation.arion.projects.heather = {
serviceName = "heather"; # systemd: heather.service
settings = {
imports = [ ./arion-compose.nix ];
};
};
# Applying the NixOS configuration must not run `arion up` for *all* services:
# in particular it would start the intentionally stopped Teable stack and
# touch production Matrix. After changing an image, update that one service
# explicitly with `arion --prebuilt-file <active-unit-compose> up -d --no-deps SERVICE`.
systemd.services.heather.restartIfChanged = false;
networking = {
hostName = "heather";
# Firewall выключен в ./image.nix (полагаемся на YC Security Groups).
# Порты тут только для документации — реально открывает YC SG.
firewall.allowedTCPPorts = [
22 # SSH
80 # HTTP (Caddy → redirect to HTTPS)
443 # HTTPS (Caddy, TLS termination)
2222 # forgejo git ssh (публично)
22000 # syncthing sync
22067 # strelaysrv (relay)
22070 # strelaysrv (status)
3478 # coturn TURN (tcp+udp)
8443 # xray VLESS+Reality (публичный прокси)
51820 # WireGuard mesh backbone
];
firewall.allowedUDPPorts = [
51820 # WireGuard mesh backbone
22000 # syncthing sync (QUIC)
10000 # jitsi jvb (RTP media)
3478 # coturn TURN
];
# coturn RTP relay range
firewall.allowedUDPPortRanges = [
{ from = 49152; to = 65535; } # coturn RTP relay
];
};
# useDHCP задаёт ./image.nix (mkDefault) — облачный профиль.
# SSH ключи для mikl: forgejo .keys endpoint с sha256-pinning.
# Poppy-ключ (этот ноутбук) лежит там последней строкой.
users.users.mikl.openssh.authorizedKeys.keyFiles = [
(pkgs.fetchurl {
url = "https://git.iscg.dev/mikl.keys";
sha256 = "sha256-Omc/3NJn0nM8SWeF33trFwOAVgSX0fXB5dfEakcRoTA=";
})
];
# Rescue-канал: root с тем же poppy-ключом. PermitRootLogin = prohibit-password
# (см. image.nix) пускает root только по ключу — парольного root-логина нет.
# Нужен, чтобы не оказаться запертым без root, если passwordless sudo сломается
# (как произошло на первом образе heather): ssh root@heather и nixos-rebuild.
users.users.root.openssh.authorizedKeys.keyFiles = [
(pkgs.fetchurl {
url = "https://git.iscg.dev/mikl.keys";
sha256 = "sha256-Omc/3NJn0nM8SWeF33trFwOAVgSX0fXB5dfEakcRoTA=";
})
];
# После первой установки 25.11 не трогаем (NixOS convention)
system.stateVersion = "25.11";
}