nixos/hosts/faunus-ater.nix
Malte Tammena 480d398c93 chore(users): drop sftp related things for marie/malte
Was only used for the remote printing service, which is also dropped with this commit.
2023-11-18 11:23:32 +01:00

417 lines
14 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.

{
pkgs,
lib,
config,
inputs,
...
}: let
sopsPath = key: config.sops.secrets.${key}.path;
mkVirtHost = certificateName:
lib.attrsets.recursiveUpdate {
addSSL = true;
listenAddresses = [vpnIPv4 "[${vpnIPv6}]"];
sslTrustedCertificate = pkgs.writeText "ca.crt" (builtins.readFile ../secrets/ca.crt);
sslCertificateKey = sopsPath "certificate-key-${certificateName}";
sslCertificate = pkgs.writeText "${certificateName}.crt" (builtins.readFile ../secrets/pub/${certificateName}.crt);
};
vpnIPv4 = config.state.vpn.ipv4;
vpnIPv6 = config.state.vpn.ipv6;
in {
imports = [
inputs.nixos-hardware.nixosModules.common-cpu-intel #-cpu-only
../modules/nginx-reverse-proxy.nix
../hardware/asrock-z370-i3-black-box.nix
];
config = {
networking.hostName = "faunus-ater";
networking.hostId = "a4d7bec4";
networking.interfaces.eno1.useDHCP = true;
# === Make sure ZFS works ===
# TODO: Update and think of some automatic way of keeping this up to date.
boot.kernelPackages = pkgs.linuxPackages_5_15;
# === Can't handle this ===
systemd.enableEmergencyMode = false;
# === Settings ===
settings.ssh.openOutsideVPN = true;
# === ZFS services ===
services.zfs.trim.enable = true;
services.zfs.autoScrub.enable = true;
services.zfs.autoScrub.pools = ["rpool"];
# === Additional services ===
services.fwupd.enable = true;
powerManagement = {
enable = true;
powertop.enable = true;
cpuFreqGovernor = "powersave";
};
# === Git.home, because everything else sucks ===
services.gogs = {
enable = true;
stateDir = "/data/dirty/gogs";
appName = "Malte's Secret Git Stash";
cookieSecure = true;
database.passwordFile = sopsPath "gogs-database-password";
httpPort = config.state.services.git.port;
rootUrl = "https://git.tammena.me/";
domain = "git.tammena.me";
# FIXME: Remove after upstream fix of database type
extraConfig = ''
[database]
TYPE = sqlite3
[auth]
DISABLE_REGISTRATION = true
SHOW_REGISTRATION_BUTTON = false
[server]
SSH_PORT = 22222
'';
};
services.nginx.virtualHosts."git.home" = mkVirtHost "git-home" {
locations."/" = {
proxyPass = "http://${config.services.gogs.httpAddress}:${builtins.toString config.services.gogs.httpPort}";
proxyWebsockets = true;
};
};
virtualisation.oci-containers.backend = "podman";
virtualisation.podman = {
enable = true;
dockerCompat = true;
extraPackages = with pkgs; [zfs];
};
# Override storage driver
virtualisation.containers.storage.settings = {
storage = {
driver = "zfs";
graphroot = "/var/lib/containers/storage";
runroot = "/run/containers/storage";
};
};
virtualisation.oci-containers.containers."timetagger" = {
image = "ghcr.io/almarklein/timetagger:v23.2.1";
ports = ["5873:5873"];
environment = {
TIMETAGGER_BIND = "0.0.0.0:5873";
TIMETAGGER_DATADIR = "/root/_timetagger";
TIMETAGGER_LOG_LEVEL = "info";
TIMETAGGER_CREDENTIALS = "malte:$2a$08$P.e3SD0cnPK0P4mFYShELuoa37.1e1dEqE8MWa6LJ/kSJfje1BdBi,marie:$2a$08$ubOZWO510y5bgwIl0O4Ne.dKZdWoHqEMzvs56L6esqvLfBJ/6OgYm";
};
volumes = [
"/data/dirty/timetagger:/root/_timetagger"
];
};
services.nginx.virtualHosts."time.home" = mkVirtHost "time-home" {
locations."/" = {
proxyPass = "http://127.0.0.1:5873";
proxyWebsockets = true;
};
};
services.nginx.virtualHosts."todo.home" = mkVirtHost "todo-home" {
locations."/" = {
proxyPass = "http://127.0.0.1:7372";
proxyWebsockets = true;
};
};
services.nginx.virtualHosts."support.home" = mkVirtHost "support-home" {
locations."/" = {
proxyPass = "http://127.0.0.1:9999";
proxyWebsockets = true;
};
};
services.nginx.virtualHosts."config.home" = mkVirtHost "config-home" {
locations."/" = {
proxyPass = "http://127.0.0.1:8123";
proxyWebsockets = true;
};
};
virtualisation.oci-containers.containers.home-assistant = {
volumes = ["/data/dirty/home-assistant:/config"];
environment.TZ = "Europe/Berlin";
image = "ghcr.io/home-assistant/home-assistant:2023.9";
ports = [
"8123:8123"
"1400:1400/tcp"
];
extraOptions = [
# TODO: Fix the path of the zigbee controller using udev
"--device=/dev/serial/by-id/usb-Silicon_Labs_Sonoff_Zigbee_3.0_USB_Dongle_Plus_0001-if00-port0"
"--device=/dev/ttyUSB0"
"--cap-add=CAP_NET_RAW,CAP_NET_BIND_SERVICE"
];
};
# For SONOS
networking.firewall.allowedTCPPorts = [1400];
# === HYDRA & Friends. ===
services.hydra = {
enable = true;
package = pkgs.hydra;
notificationSender = "hydra@home";
hydraURL = "http://faunus-ater:${builtins.toString config.services.hydra.port}";
minimumDiskFree = 10;
useSubstitutes = true;
};
services.nix-serve = {
enable = true;
secretKeyFile = sopsPath "nix-store-signing-key";
# FIXME: Remove once fixed upstream
package = pkgs.nix-serve.override {
nix = pkgs.nixVersions.nix_2_12;
};
};
# Build on other machines aswell if possible
nix.buildMachines = [
{
hostName = "localhost";
maxJobs = 4;
speedFactor = 1;
sshKey = sopsPath "hydra-overseer-key";
sshUser = "hydra-minion";
systems = ["x86_64-linux" "i686-linux"];
}
{
hostName = "helix-texta";
maxJobs = 4;
speedFactor = 2;
sshKey = sopsPath "hydra-overseer-key";
sshUser = "hydra-minion";
supportedFeatures = ["kvm" "big-parallel"];
systems = ["x86_64-linux" "i686-linux"];
}
{
hostName = "murex-pecten";
maxJobs = 4;
speedFactor = 4;
sshKey = sopsPath "hydra-overseer-key";
sshUser = "hydra-minion";
supportedFeatures = ["kvm" "big-parallel"];
systems = ["x86_64-linux" "i686-linux"];
}
];
# TODO: This doesn't seem to work
programs.ssh.extraConfig = ''
Host *
StrictHostKeyChecking accept-new
'';
nix.extraOptions = ''
allowed-uris = http:// https://
'';
systemd.services."hydra-initial-setup" = {
description = "Setup hydra admin password once";
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
LoadCredential = "USER_PW:${sopsPath "hydra-admin-password"}";
};
wantedBy = lib.singleton "multi-user.target";
requires = lib.singleton "hydra-init.service";
after = lib.singleton "hydra-init.service";
environment = {
inherit (config.systemd.services.hydra-init.environment) HYDRA_DBI;
};
script = let
hydra-create-user = "${pkgs.hydra}/bin/hydra-create-user";
in ''
if [ ! -e ~hydra/.setup-is-complete ]; then
# create admin user
${hydra-create-user} admin --full-name 'Admin Mc. Admining' --email-address 'admin@faunus-ater' --password "$USER_PW" --role admin || exit 1
# done
touch ~hydra/.setup-is-complete
fi
'';
};
services.nginx.virtualHosts = {
"hydra.home" = mkVirtHost "hydra-home" {
locations."/" = {
proxyPass = "http://localhost:${builtins.toString config.services.hydra.port}";
};
};
"cache.home" = mkVirtHost "cache-home" {
locations."/" = {
proxyPass = "http://localhost:${builtins.toString config.services.nix-serve.port}";
};
};
};
# === PAPERLESS service, save me! ===
services.paperless = {
enable = true;
address = "[::1]";
passwordFile = sopsPath "paperless-admin-password";
dataDir = "/data/dirty/paperless";
extraConfig = {
PAPERLESS_CONSUMER_DELETE_DUPLICATES = true;
PAPERLESS_CONSUMER_RECURSIVE = true;
PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS = true;
PAPERLESS_FILENAME_FORMAT = "{created_year}/{correspondent}/{created_year}-{created_month}-{created_day}-{document_type}-{title}-{tag_list}";
PAPERLESS_OCR_LANGUAGE = "deu";
PAPERLESS_URL = "https://doc.home";
};
};
services.nginx.virtualHosts."doc.home" = mkVirtHost "doc-home" {
locations."/" = {
proxyPass = "http://[::1]:${builtins.toString config.services.paperless.port}";
proxyWebsockets = true;
};
};
# === Komga, for my reading needs ===
services.komga = {
enable = true;
port = config.state.services.read.port;
stateDir = "/data/dirty/komga";
};
services.nginx.virtualHosts."read.home" = mkVirtHost "read-home" {
locations."/" = {
proxyPass = "http://[::1]:${builtins.toString config.services.komga.port}";
proxyWebsockets = true;
};
};
# === Trilium ===
services.trilium-server = {
enable = true;
port = 10302;
dataDir = "/data/dirty/trilium";
};
services.nginx.virtualHosts."note.home" = mkVirtHost "note-home" {
locations."/" = {
proxyPass = "http://${config.services.trilium-server.host}:${builtins.toString config.services.trilium-server.port}";
proxyWebsockets = true;
};
};
# === Photoprism ===
services.photoprism = {
enable = true;
port = 2342;
storagePath = "/data/dirty/photoprism/storage";
originalsPath = "/data/dirty/photoprism/originals";
importPath = "/data/dirty/photoprism/import";
passwordFile = sopsPath "photoprism-admin-password";
settings = {
PHOTOPRISM_SESSION_MAXAGE = "31536000";
PHOTOPRISM_SESSION_TIMEOUT = "31536000";
PHOTOPRISM_UPLOAD_NSFW = "true";
PHOTOPRISM_DETECT_NSFW = "true";
PHOTOPRISM_SITE_URL = "https://foto.home";
PHOTOPRISM_SITE_TITLE = "PhotoPrism";
PHOTOPRISM_SITE_CAPTION = "All the pictures!";
PHOTOPRISM_SITE_DESCRIPTION = "";
PHOTOPRISM_SITE_AUTHOR = "";
};
};
# TODO: Why does it not work without these? :/
systemd.services.photoprism.serviceConfig.User = lib.mkForce null;
systemd.services.photoprism.serviceConfig.Group = lib.mkForce null;
systemd.services.photoprism.serviceConfig.DynamicUser = lib.mkForce false;
systemd.services.photoprism.serviceConfig.SystemCallFilter = lib.mkForce [];
services.nginx.virtualHosts."foto.home" = mkVirtHost "foto-home" {
locations."/" = {
proxyPass = "http://localhost:${builtins.toString config.services.photoprism.port}";
proxyWebsockets = true;
};
extraConfig = ''
client_max_body_size 500M;
'';
};
# === Restic User Backup ===
services.resticConfigured = {
enable = true;
rootDir = "/data/dirty/restic";
openFirewall = true;
};
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
# === BACKUPS ===
services.restic.backups = {
# Make sure my 'active IO' disk get's saved once a day
zdirty = {
initialize = true;
repository = "/data/archive/dirty.bak";
timerConfig.OnCalendar = "daily";
paths = lib.singleton "/data/dirty";
pruneOpts = [
"--keep-daily 1"
"--keep-weekly 1"
"--keep-monthly 1"
"--keep-yearly 5"
];
passwordFile = sopsPath "internal-restic-password";
};
};
# === RUNTIME SECRETS ===
sops.defaultSopsFile = ../secrets/hosts/faunus-ater/secrets.yaml;
sops.age.sshKeyPaths = ["/etc/ssh/ssh_host_ed25519_key"];
sops.secrets = let
nginxSecret = {
owner = config.users.users.nginx.name;
mode = "0400";
};
in {
"certificate-key-config-home" = nginxSecret;
"certificate-key-todo-home" = nginxSecret;
"certificate-key-time-home" = nginxSecret;
"certificate-key-support-home" = nginxSecret;
"certificate-key-hydra-home" = nginxSecret;
"certificate-key-cache-home" = nginxSecret;
"certificate-key-doc-home" = nginxSecret;
"certificate-key-read-home" = nginxSecret;
"certificate-key-note-home" = nginxSecret;
"certificate-key-foto-home" = nginxSecret;
"certificate-key-listen-home" = nginxSecret;
"certificate-key-git-home" = nginxSecret;
"paperless-admin-password" = {};
"photoprism-admin-password" = {};
"nginx-cert-key" = nginxSecret;
"nginx-cert-crt" = nginxSecret;
"fritzbox-exporter-env" = {};
"internal-restic-password" = {};
"nix-store-signing-key" = {};
"hydra-admin-password" = {
owner = config.users.users.hydra.name;
mode = "0400";
};
"hydra-overseer-key" = {
owner = config.users.users.hydra.name;
mode = "0440";
};
"gogs-database-password" = {
owner = config.users.users.gogs.name;
mode = "0400";
};
};
# All services that run here, that should be exposed need to be exposed on the VPN
networking.firewall.interfaces.${config.services.tailscale.interfaceName}.allowedTCPPorts = let
selectPort = _: config: config.port;
filterRunningHereAndExposed = lib.attrsets.filterAttrs (_: conf: conf.host == config.networking.hostName && conf ? external && conf.external);
in
lib.attrsets.mapAttrsToList selectPort (filterRunningHereAndExposed config.state.services);
# This value determines the NixOS release from which the default
# settings for stateful data, like file locations and database versions
# on your system were taken. Its perfectly fine and recommended to leave
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "22.05"; # Did you read the comment?
};
}