nixos: describe asxpio-services as a NixOS system
asxpio-services is to move from Debian with Docker to NixOS with Podman. This is the target system; nothing is switched over yet. One flake holds what was spread over cloud-init, Ansible roles and hand-made units: network and WireGuard, users and SSH restrictions, firewall, the weekly image prune, and the four container stacks (Paper and Fossils Minecraft, SillyTavern, the Forgejo runner), each as a systemd unit under Podman. The runner keeps running as a container and starts jobs through Podman's Docker-compatible socket. Secrets (WireGuard keys, RCON and CurseForge keys) are encrypted with sops for the admin's age key and the server's SSH host key, so the repository holds no plaintext secret and CI needs none of them. Containers that own data wait for /opt/.restored. On a freshly installed disk a game server would otherwise generate a new world exactly where the restore is about to land. Tried on a throwaway server (.#rehearsal): install with nixos-anywhere keeping a given host key, secret decryption, all stacks, a real Paper server stopping with a full save, what CI jobs need from the engine (short image names, the nix-store volume, service containers by name, a kaniko build, the socket inside a job), an update over SSH and a reboot. migration/ has the scripts that move the data through object storage, and a workflow builds the system on every push and switches the host on main. Assisted-by: Claude:fable-5-2
This commit is contained in:
parent
e2ab74a6bd
commit
8bd9c653f7
16 changed files with 1028 additions and 0 deletions
125
nixos/modules/base.nix
Normal file
125
nixos/modules/base.nix
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
# Shared by every server: users, SSH, baseline services.
|
||||
{ lib, pkgs, ... }:
|
||||
let
|
||||
keys = {
|
||||
asxpi-meow = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC//ljrf65xbFyGmy/xeZs5GZvkmufCioqIGYguR3c8c/Ff9HiA4oMRT53Qs16KMuXRg62NusVQvJsvyPRSARpBFsoPiZ/YNBteWlqT1aVqeZwPOfUfFycQ2T8a9qtDnZ0WhamSlJMSAHQPkxXNA6S2ZE9y+dMzMc5qPeRwdsQmuvkxjual/35t9qTgCAG7LvKCKPJkLL5YpwLoqWYWTqSJ6RxNLTYNgIhJBZirzB5rNiFDDupnOuTKSpyRJNxyBSapNge5rCnGQYIeoJ4fi+FTi5wzjlC3hC0BN4Oc/zrm1nS9lyPXQS8KV5/vcQlPrvYZmv0LneeQ/gH/jJsSlwRp2qIatoASSfTlz1y1bOOYtPFojJPikXiXeZMCdlam1II6B94z1ybOIO8TqyKDVs4eHRFfYO72zv0sGsTr7t0C7+N9Wrpmhk38zdlLagZ6HO7KSyoAHm+fQc58fXmOhz4vfYrbmYRPgO0VO03fEb7lSFPPLaSseT0tayli05irJpc= asxpi";
|
||||
deploy = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGTDPg3mV55lO8uBnvpcGRxJ2vMRP0qB3Pxhg+C8rwj9 asxpio-deploy";
|
||||
zerrtika-workstation = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAB7fAC+8PwqEb1+B523nsdxvUPcTHd3ezHTp9XVe0CP zerrtika@cult-workstation";
|
||||
zerrtika-mac = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIELE7hTD4sC5raqjSxy2fXyM+q1CwwVuy9KLFgDR6SFl zerrtika@zerrtika-mac";
|
||||
};
|
||||
|
||||
admin = uid: sshKeys: {
|
||||
inherit uid;
|
||||
isNormalUser = true;
|
||||
extraGroups = [
|
||||
"wheel"
|
||||
"podman"
|
||||
];
|
||||
openssh.authorizedKeys.keys = sshKeys;
|
||||
};
|
||||
|
||||
# Shell access only from WireGuard and the private network
|
||||
trustedNetworks = [
|
||||
"10.20.0.0/24"
|
||||
"10.10.0.0/20"
|
||||
];
|
||||
shellUsers = [
|
||||
"asxpi"
|
||||
"zerrtika"
|
||||
"deploy"
|
||||
"root"
|
||||
];
|
||||
in
|
||||
{
|
||||
time.timeZone = "UTC";
|
||||
i18n.defaultLocale = "en_US.UTF-8";
|
||||
|
||||
# Users exist only as declared here
|
||||
users.mutableUsers = false;
|
||||
users.users = {
|
||||
root.openssh.authorizedKeys.keys = [
|
||||
keys.asxpi-meow
|
||||
keys.deploy
|
||||
keys.zerrtika-workstation
|
||||
];
|
||||
asxpi = admin 1000 [ keys.asxpi-meow ];
|
||||
deploy = admin 1001 [ keys.deploy ];
|
||||
zerrtika = admin 1002 [ keys.zerrtika-mac ];
|
||||
};
|
||||
security.sudo.wheelNeedsPassword = false;
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
openFirewall = false; # opened per interface by each host
|
||||
settings = {
|
||||
PasswordAuthentication = false;
|
||||
KbdInteractiveAuthentication = false;
|
||||
PermitRootLogin = "prohibit-password";
|
||||
X11Forwarding = false;
|
||||
MaxAuthTries = 3;
|
||||
MaxSessions = 5;
|
||||
ClientAliveInterval = 300;
|
||||
ClientAliveCountMax = 2;
|
||||
AllowUsers = lib.mkDefault (
|
||||
lib.concatMap (user: map (net: "${user}@${net}") trustedNetworks) shellUsers
|
||||
);
|
||||
Ciphers = [
|
||||
"chacha20-poly1305@openssh.com"
|
||||
"aes256-gcm@openssh.com"
|
||||
"aes128-gcm@openssh.com"
|
||||
"aes256-ctr"
|
||||
"aes192-ctr"
|
||||
"aes128-ctr"
|
||||
];
|
||||
Macs = [
|
||||
"hmac-sha2-512-etm@openssh.com"
|
||||
"hmac-sha2-256-etm@openssh.com"
|
||||
"hmac-sha2-512"
|
||||
"hmac-sha2-256"
|
||||
];
|
||||
KexAlgorithms = [
|
||||
"sntrup761x25519-sha512@openssh.com"
|
||||
"curve25519-sha256"
|
||||
"curve25519-sha256@libssh.org"
|
||||
"diffie-hellman-group-exchange-sha256"
|
||||
"diffie-hellman-group16-sha512"
|
||||
"diffie-hellman-group18-sha512"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
services.fail2ban.enable = true;
|
||||
services.journald.extraConfig = "SystemMaxUse=500M";
|
||||
|
||||
networking.firewall.enable = true;
|
||||
services.resolved.llmnr = "false"; # no name resolution service on the LAN side
|
||||
|
||||
nix = {
|
||||
settings = {
|
||||
experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
];
|
||||
trusted-users = [
|
||||
"root"
|
||||
"@wheel"
|
||||
];
|
||||
};
|
||||
gc = {
|
||||
automatic = true;
|
||||
dates = "weekly";
|
||||
options = "--delete-older-than 30d";
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
curl
|
||||
git
|
||||
htop
|
||||
ncdu
|
||||
rclone
|
||||
zstd
|
||||
tree
|
||||
vim
|
||||
];
|
||||
}
|
||||
44
nixos/modules/hetzner.nix
Normal file
44
nixos/modules/hetzner.nix
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Hetzner Cloud VM: BIOS boot from /dev/sda, virtio hardware.
|
||||
{ modulesPath, ... }:
|
||||
{
|
||||
imports = [ (modulesPath + "/profiles/qemu-guest.nix") ];
|
||||
|
||||
disko.devices.disk.main = {
|
||||
type = "disk";
|
||||
device = "/dev/sda";
|
||||
content = {
|
||||
type = "gpt";
|
||||
partitions = {
|
||||
boot = {
|
||||
size = "1M";
|
||||
type = "EF02"; # BIOS boot, for GRUB
|
||||
};
|
||||
ESP = {
|
||||
size = "512M";
|
||||
type = "EF00";
|
||||
content = {
|
||||
type = "filesystem";
|
||||
format = "vfat";
|
||||
mountpoint = "/boot";
|
||||
};
|
||||
};
|
||||
root = {
|
||||
size = "100%";
|
||||
content = {
|
||||
type = "filesystem";
|
||||
format = "ext4";
|
||||
mountpoint = "/";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
boot.loader.grub = {
|
||||
enable = true;
|
||||
efiSupport = true;
|
||||
efiInstallAsRemovable = true;
|
||||
};
|
||||
|
||||
services.qemuGuest.enable = true;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue