Three connected changes, all triggered by the same outage. base_domain leaves mgaction.town. That zone has a wildcard A+AAAA pointing at neptun, and DNS wildcards match multi-label names, so jupiter.hosts.mgaction.town resolved publicly to NEPTUN and Caddy proxied to itself -- a silent loop rather than a lookup failure. Nesting the tailnet inside the LAN domain as orbit.sol keeps the theme and resolves unambiguously, since tailscale matches routes by longest suffix. override_local_dns = true with pihole as the only global nameserver, so roaming devices get ad blocking and .sol names off-LAN. With it false, globalResolvers land in the netmap's FallbackResolvers, which a phone with carrier DNS never consults. No public fallback is listed on purpose: tailscale treats the list as a set, so a second entry would let queries slip past the filter whenever mercury is slow. The cost is that mercury is now a single point of failure for tailnet DNS. neptun and mercury opt out individually. mercury would otherwise resolve through itself. neptun must not depend on a Pi behind a domestic line to renew the certificates for the control server every other node needs -- and it is circular besides, since tailscaled has to resolve vpn.mgaction.town to connect at all. Instead neptun runs a dnsmasq stub forwarding just orbit.sol to MagicDNS on 100.100.100.100, which tailscaled answers whenever it is running regardless of --accept-dns. That resolves jupiter live, so the hardcoded /etc/hosts pin is gone. Also sets dns.nameservers.split explicitly: nixpkgs renders its own dns.split option one level too high, but headscale reads dns.nameservers.split (hscontrol/types/config.go:722) and so does headplane, whose DNS page dies on the missing key with "Cannot convert undefined or null to object". The module's option is dead as written. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
61 lines
2.1 KiB
Nix
61 lines
2.1 KiB
Nix
{ pkgs, ... }:
|
|
|
|
# Shared base for all hosts: user, SSH hardening, nix settings, packages.
|
|
{
|
|
# ---- User ----
|
|
users.users.darman = {
|
|
isNormalUser = true;
|
|
description = "darman";
|
|
extraGroups = [ "wheel" "networkmanager" ];
|
|
openssh.authorizedKeys.keys = [
|
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGZpkPVhzi1zG5JI9hWyUgdyvNIQbp4ts4jw3idpMhhN erik@laptop"
|
|
];
|
|
};
|
|
# Costs a password prompt on every `./scripts/deploy switch` (nixos-rebuild
|
|
# --use-remote-sudo). Worth it: darman's key is the only thing between the
|
|
# public internet and root on neptun. The password is darman_password from
|
|
# each host's sops file.
|
|
security.sudo.wheelNeedsPassword = true;
|
|
|
|
# ---- SSH (key-only) ----
|
|
services.openssh = {
|
|
enable = true;
|
|
settings = {
|
|
PasswordAuthentication = false;
|
|
PermitRootLogin = "no";
|
|
};
|
|
};
|
|
|
|
# ---- Nix ----
|
|
nix.settings = {
|
|
experimental-features = [ "nix-command" "flakes" ];
|
|
# trust wheel so `nixos-rebuild --target-host darman@…` can push closures.
|
|
trusted-users = [ "root" "@wheel" ];
|
|
# authentik (services/identity/authentik.nix) comes from authentik-nix,
|
|
# which cache.nixos.org doesn't carry — without this it's ~400 local
|
|
# derivations (npm, rust, python). NOTE: the closure is built on whatever
|
|
# machine runs ./scripts/deploy, so the LAPTOP needs these two lines too,
|
|
# in /etc/nix/nix.custom.conf (Determinate Nix rewrites nix.conf).
|
|
extra-substituters = [ "https://nix-community.cachix.org" ];
|
|
extra-trusted-public-keys = [
|
|
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
|
|
];
|
|
};
|
|
nix.gc = {
|
|
automatic = true;
|
|
dates = "weekly";
|
|
options = "--delete-older-than 30d";
|
|
};
|
|
|
|
environment.systemPackages = with pkgs; [ vim git htop tmux curl wget ];
|
|
|
|
# ---- Locale / firewall base ----
|
|
time.timeZone = "Europe/Berlin";
|
|
i18n.defaultLocale = "en_US.UTF-8";
|
|
|
|
# Firewall on, ssh always allowed. Service modules add their own ports
|
|
# (samba via openFirewall, caddy 80/443, tailscale trusts tailscale0).
|
|
networking.firewall.enable = true;
|
|
networking.firewall.allowedTCPPorts = [ 22 ];
|
|
}
|