nixos/hosts/faunus-ater.nix

437 lines
13 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,
...
}: let
sopsPath = key: config.sops.secrets.${key}.path;
scrapedExporters =
lib.attrsets.mapAttrsToList (expName: conf: {
job_name = expName;
static_configs = lib.singleton {
targets = lib.singleton "localhost:${builtins.toString conf.port}";
};
})
config.services.prometheus.exporters;
mkVirtHost = lib.attrsets.recursiveUpdate {
addSSL = true;
listenAddresses = [vpnIPv4 "[${vpnIPv6}]"];
sslTrustedCertificate = pkgs.writeText "ca.crt" (builtins.readFile ../secrets/ca.crt);
sslCertificateKey = sopsPath "nginx-cert-key";
sslCertificate = sopsPath "nginx-cert-crt";
};
vpnInterface = config.services.tailscale.interfaceName;
vpnIPv4 = "100.108.135.4";
vpnIPv6 = "fd7a:115c:a1e0:ab12:4843:cd96:626c:8704";
in {
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;
settings.printing.enable = 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";
};
# === Extend printing settings because sharing is caring ===
services.printing = {
listenAddresses = ["*:631"];
allowFrom = ["all" "@IF(${vpnInterface})"];
defaultShared = true;
browsing = true;
logLevel = "debug";
};
networking.firewall.interfaces.${vpnInterface} = {
allowedUDPPorts = [631];
allowedTCPPorts = [631 config.services.hydra.port];
};
hardware.printers = {
ensureDefaultPrinter = "Local";
ensurePrinters = lib.singleton {
description = "The fastest Boi in town!";
deviceUri = "usb://Samsung/ML-1640%20Series?serial=144QBAHS600499T.";
location = "@Home";
model = "samsung/ML-1640.ppd";
name = "Local";
ppdOptions = {
PageSize = "A4";
Resolution = "600dpi";
};
};
};
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";
};
};
# === Dim ===
# virtualisation.oci-containers.containers."dim" = {
# environment = {};
# image = "ghcr.io/dusk-labs/dim:dev";
# ports = lib.singleton "7999:8000";
# volumes = [
# # TODO: https://github.com/Dusk-Labs/dim/blob/master/docker-compose-template.yml
# "/srv/media.deletemesoon:/media:ro"
# ];
# #user = "${config.users.users.dim.name}:${config.users.groups.dim.name}";
# };
# === SheetAble ===
# virtualisation.oci-containers.containers."sheetable" = {
# environment = {
# CONFIG_PATH = "/app/config/";
# };
# image = "vallezw/sheetable";
# ports = lib.singleton "7998:8080";
# volumes = [
# # TODO: https://sheetable.net/docs/Installation/installation-docker
# ];
# };
# === Seafile ===
# services.seafile = {
# enable = true;
# adminEmail = "malte.tammena@pm.me";
# initialAdminPassword = "test";
# seafileSettings = {
# fileserver.host = "::1";
# };
# ccnetSettings.General.SERVICE_URL = "http://file.home";
# };
# === 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";
};
# 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 {
locations."/" = {
proxyPass = "http://localhost:${builtins.toString config.services.hydra.port}";
};
};
"cache.home" = mkVirtHost {
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_OCR_LANGUAGE = "deu";
PAPERLESS_CONSUMER_RECURSIVE = true;
PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS = true;
PAPERLESS_URL = "https://doc.home";
};
};
services.nginx.virtualHosts."doc.home" = mkVirtHost {
locations."/" = {
proxyPass = "http://[::1]:${builtins.toString config.services.paperless.port}";
proxyWebsockets = true;
};
};
# === Photoprism ===
services.photoprism = {
enable = true;
url = "http://foto.home";
port = 2342;
rootDir = "/data/dirty/photoprism";
environmentFile = config.sops.secrets."photoprism-env".path;
};
services.nginx.virtualHosts."foto.home" = mkVirtHost {
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;
};
# === Grafana ===
services.grafanaHome = {
enable = true;
nginx.listenAddresses = [vpnIPv4 "[${vpnIPv6}]"];
nginx.sslCertificate = sopsPath "nginx-cert-crt";
nginx.sslCertificateKey = sopsPath "nginx-cert-key";
grafana.adminPasswordFile = sopsPath "grafana-admin-password";
};
# === Prometheus ===
services.prometheus = {
enable = true;
enableReload = true;
exporters = {
fritzbox = {
enable = true;
gatewayAddress = "spof";
};
node = {
enable = true;
enabledCollectors = ["systemd"];
disabledCollectors = ["diskstats"];
};
};
scrapeConfigs = scrapedExporters;
};
systemd.services."prometheus-fritzbox-exporter".serviceConfig.EnvironmentFile = sopsPath "fritzbox-exporter-env";
# TODO: Yikes
systemd.services."prometheus-fritzbox-exporter".serviceConfig.ExecStart = let
cfg = config.services.prometheus.exporters.fritzbox;
in
lib.mkForce ''
${pkgs.prometheus-fritzbox-exporter}/bin/fritzbox_exporter \
-listen-address ${cfg.listenAddress}:${toString cfg.port} \
-gateway-url http://${cfg.gatewayAddress}:${toString cfg.gatewayPort} \
-gateway-luaurl http://${cfg.gatewayAddress} \
-metrics-file ${pkgs.prometheus-fritzbox-exporter}/share/metrics.json \
-lua-metrics-file ${pkgs.prometheus-fritzbox-exporter}/share/metrics-lua_cable.json
'';
# services.nginx.virtualHosts."media.home" = {
# locations."/" = {
# proxyPass = "http://127.0.0.1:7999";
# proxyWebsockets = true;
# };
# };
# services.nginx.virtualHosts."file.home" = {
# locations."/" = {
# proxyPass = "http://[::1]:${builtins.toString config.services.seafile.seafileSettings.fileserver.port}";
# proxyWebsockets = true;
# };
# };
# networking.firewall.allowedTCPPorts = [config.services.seafile.seafileSettings.fileserver.port];
# === Print Service ===
systemd.paths."print-all-files" = {
requires = ["printer.target"];
after = ["printer.target"];
wantedBy = ["default.target"];
pathConfig = {
DirectoryNotEmpty = "/srv/to-be-printed";
MakeDirectory = true;
DirectoryMode = "777";
Unit = "print-all-files.service";
};
};
systemd.services."print-all-files" = let
printAndDeleteFile = pkgs.writeShellApplication {
name = "print-and-delete-file";
runtimeInputs = [
pkgs.coreutils
pkgs.cups
];
text = ''
echo Printing "$1"
lp -- "$1"
rm "$1"
'';
};
script = pkgs.writeShellApplication {
name = "print-all-files-script";
runtimeInputs = [
pkgs.coreutils
printAndDeleteFile
];
text = ''
find . -type f -exec print-and-delete-file "{}" \;
'';
};
in {
requires = ["printer.target"];
after = ["printer.target"];
serviceConfig = {
WorkingDirectory = "/srv/to-be-printed";
ExecStart = "${script}/bin/print-all-files-script";
# Wait 15 seconds before restart to let the file load, if not present yet
RestartSec = "15";
};
};
users.users.sftp = {
description = "User used for all sftp stuff";
isNormalUser = true;
group = "sftp";
openssh.authorizedKeys.keyFiles = [
../secrets/users/malte/sftp-key.pub
../secrets/users/marie/sftp-key.pub
];
};
users.groups.sftp = {};
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 = {
"paperless-admin-password" = {};
"photoprism-env" = {};
"grafana-admin-password" = {
owner = config.users.users.grafana.name;
mode = "0400";
};
"nginx-cert-key" = {
owner = config.users.users.nginx.name;
mode = "0400";
};
"nginx-cert-crt" = {
owner = config.users.users.nginx.name;
mode = "0400";
};
"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";
};
};
# 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?
}