nix-config/hosts/heather/default.nix

137 lines
6 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, статический бинарник)
./nick.nix # Nick timer prototype (systemd, localhost:8787)
# Jupyter is intentionally disabled until it has a separate service identity.
# Keep its venv, notebooks and external credentials untouched.
./astrnet.nix # AstrNet (Sol): two WireGuard legs, FRR/BGP, ingress guard
./minecraft-api.nix # mesh-only typed Minecraft API, isolated DynamicUser
];
# 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;
}
];
# WireGuard tooling is also used by AstrNet. Keep this package and the
# diagnostics tools available in non-login SSH sessions.
environment.systemPackages = with pkgs; [
wireguard-tools
tmux
jq
wget
python3
];
# Static public pages used by the personal gcalcli OAuth application.
environment.etc = {
"gcalcli-site/index.html".source = ./gcalcli-site/index.html;
"gcalcli-site/privacy.html".source = ./gcalcli-site/privacy.html;
"gcalcli-site/terms.html".source = ./gcalcli-site/terms.html;
"gcalcli-site/style.css".source = ./gcalcli-site/style.css;
};
# Docker requires forwarding; AstrNet ingress is blocked by astrnet-guard.
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;
# The public dashboard is navigation only, never a Docker administration client.
assertions = [
{
assertion = let
services = config.virtualisation.arion.projects.heather.settings.services;
in !(services ? dockerproxy) && lib.all
(service: lib.all (mount: !(lib.hasInfix "docker.sock" mount)) service.service.volumes)
(lib.attrValues services);
message = "Heather web stack must not expose Docker socket access or a dockerproxy service.";
}
];
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)
7881 # MatrixRTC LiveKit ICE/TCP
3478 # coturn TURN (tcp+udp)
];
firewall.allowedUDPPorts = [
22000 # syncthing sync (QUIC)
10000 # jitsi jvb (RTP media)
7882 # MatrixRTC LiveKit ICE/UDP mux
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";
}