refactor: systems/ folder

This commit is contained in:
arcnmx 2024-01-06 16:35:54 -08:00
parent a06248ba9a
commit 41b772c6d1
22 changed files with 10 additions and 10 deletions

99
systems/tewi/access.nix Normal file
View file

@ -0,0 +1,99 @@
{
config,
lib,
meta,
pkgs,
...
}:
with lib; {
services.nginx.virtualHosts = mkMerge [
(mkIf (config.networking.hostName == "tewi") {
"gensokyo.zone" = {
locations."/" = {
root = pkgs.gensokyoZone;
};
};
"z2m.gensokyo.zone" = {
extraConfig = ''
auth_request /validate;
error_page 401 = @error401;
'';
locations = {
"/" = {
proxyPass = "http://127.0.0.1:8072";
extraConfig = ''
add_header Access-Control-Allow-Origin https://login.gensokyo.zone;
add_header Access-Control-Allow-Origin https://id.gensokyo.zone;
proxy_set_header X-Vouch-User $auth_resp_x_vouch_user;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
'';
};
"@error401" = {
extraConfig = ''
return 302 https://login.gensokyo.zone/login?url=$scheme://$http_host$request_uri&vouch-failcount=$auth_resp_failcount&X-Vouch-Token=$auth_resp_jwt&error=$auth_resp_err;
'';
};
"/validate" = {
recommendedProxySettings = false;
proxyPass = "http://127.0.0.1:30746/validate";
extraConfig = ''
proxy_set_header Host $host;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
auth_request_set $auth_resp_x_vouch_user $upstream_http_x_vouch_user;
auth_request_set $auth_resp_jwt $upstream_http_x_vouch_jwt;
auth_request_set $auth_resp_err $upstream_http_x_vouch_err;
auth_request_set $auth_resp_failcount $upstream_http_x_vouch_failcount;
'';
};
};
};
})
(mkIf (config.networking.hostName != "tewi") {
"home.${config.networking.domain}" = {
locations = {
"/" = {
proxyPass = meta.tailnet.yukari.pp 4 8123;
extraConfig = ''
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
'';
};
};
};
"cloud.kittywit.ch" = {
locations = {
"/".proxyPass = meta.tailnet.yukari.ppp 4 80 "nextcloud/";
};
};
"plex.kittywit.ch" = {
locations = {
"/" = {
proxyPass = meta.tailnet.yukari.pp 4 32400;
extraConfig = ''
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_buffering off;
proxy_set_header X-Plex-Client-Identifier $http_x_plex_client_identifier;
proxy_set_header X-Plex-Device $http_x_plex_device;
proxy_set_header X-Plex-Device-Name $http_x_plex_device_name;
proxy_set_header X-Plex-Platform $http_x_plex_platform;
proxy_set_header X-Plex-Platform-Version $http_x_plex_platform_version;
proxy_set_header X-Plex-Product $http_x_plex_product;
proxy_set_header X-Plex-Token $http_x_plex_token;
proxy_set_header X-Plex-Version $http_x_plex_version;
proxy_set_header X-Plex-Nocache $http_x_plex_nocache;
proxy_set_header X-Plex-Provides $http_x_plex_provides;
proxy_set_header X-Plex-Device-Vendor $http_x_plex_device_vendor;
proxy_set_header X-Plex-Model $http_x_plex_model;
'';
};
};
};
})
];
}

View file

@ -0,0 +1,40 @@
{ lib
, buildPythonPackage
, fetchFromGitHub
, aiofiles
, cryptography
, protobuf
, setuptools
, pytestCheckHook
}:
buildPythonPackage rec {
pname = "androidtvremote2";
version = "0.0.13";
format = "pyproject";
src = fetchFromGitHub {
owner = "tronikos";
repo = pname;
rev = "v${version}";
hash = "sha256-+9VVUIvM//Fxv1a/+PAKWSQE8/TgBZzeTisgMqj6KPU=";
};
nativeBuildInputs = [
setuptools
];
propagatedBuildInputs = [
aiofiles
cryptography
protobuf
];
checkInputs = [
pytestCheckHook
];
pythonImportsCheck = [
"androidtvremote2"
];
}

View file

@ -0,0 +1,75 @@
{ pkgs, config, utils, lib, ... }: let
inherit (lib) mapAttrsToList mapAttrs' nameValuePair splitString last singleton
mkIf mkMerge mkForce;
inherit (config) services;
inherit (services.kanidm.serverSettings) domain;
cfg = services.cloudflared;
apartment = "131222b0-9db0-4168-96f5-7d45ec51c3be";
shadowTunnel = {
${apartment}.ingress.deluge = {
hostname._secret = config.sops.secrets.cloudflared-tunnel-apartment-deluge.path;
service = "http://localhost:${toString services.deluge.web.port}";
};
};
in {
sops.secrets.cloudflared-tunnel-apartment.owner = services.cloudflared.user;
sops.secrets.cloudflared-tunnel-apartment-deluge.owner = services.cloudflared.user;
services.cloudflared = {
enable = true;
tunnels = {
${apartment} = {
credentialsFile = config.sops.secrets.cloudflared-tunnel-apartment.path;
default = "http_status:404";
ingress = mapAttrs' (prefix: nameValuePair "${prefix}${domain}") {
"".service = "http://localhost:80";
"home.".service = "http://localhost:${toString services.home-assistant.config.http.server_port}";
"z2m.".service = "http://localhost:80";
"login.".service = "http://localhost:${toString services.vouch-proxy.settings.vouch.port}";
"id." = let
inherit (services.kanidm.serverSettings) bindaddress;
port = last (splitString ":" bindaddress);
in {
service = "https://127.0.0.1:${port}";
originRequest.noTLSVerify = true;
};
};
};
};
};
systemd.services = let
filterConfig = lib.filterAttrsRecursive (_: v: ! builtins.elem v [ null [ ] { } ]);
mapIngress = hostname: ingress: {
inherit hostname;
} // filterConfig (filterConfig ingress);
in mkIf cfg.enable (mapAttrs' (uuid: tunnel: let
RuntimeDirectory = "cloudflared-tunnel-${uuid}";
configPath = "/run/${RuntimeDirectory}/config.yml";
settings = {
tunnel = uuid;
credentials-file = tunnel.credentialsFile;
ingress = mapAttrsToList mapIngress tunnel.ingress
++ mapAttrsToList mapIngress shadowTunnel.${uuid}.ingress or { }
++ singleton { service = tunnel.default; };
};
in nameValuePair "cloudflared-tunnel-${uuid}" (mkMerge [
{
after = [ "tailscale-autoconnect.service" ];
serviceConfig = {
RestartSec = 10;
};
}
(mkIf (shadowTunnel.${uuid} or { } != { }) {
serviceConfig = {
inherit RuntimeDirectory;
ExecStart = mkForce [
"${cfg.package}/bin/cloudflared tunnel --config=${configPath} --no-autoupdate run"
];
ExecStartPre = [
(pkgs.writeShellScript "cloudflared-tunnel-${uuid}-prepare" ''
${utils.genJqSecretsReplacementSnippet settings configPath}
'')
];
};
})
])) cfg.tunnels);
}

55
systems/tewi/deluge.nix Normal file
View file

@ -0,0 +1,55 @@
{ config, utils, lib, ... }: let
inherit (lib) mkAfter;
cfg = config.services.deluge;
shadowDir = "/mnt/shadow";
mediaDir = "${shadowDir}/deluge";
in {
sops.secrets.deluge-auth = {
inherit (cfg) group;
owner = cfg.user;
};
services.deluge = {
enable = true;
declarative = true;
openFirewall = true;
web = {
enable = true;
};
config = {
download_location = "${mediaDir}/download";
move_completed_path = "${mediaDir}/complete";
move_completed = true;
max_upload_speed = 10.0;
#share_ratio_limit = 2.0;
max_connections_global = 1024;
max_connections_per_second = 50;
max_active_limit = 100;
max_active_downloading = 75;
max_upload_slots_global = 25;
max_active_seeding = 1;
allow_remote = true;
daemon_port = 58846;
listen_ports = [ 6881 6889 ];
random_port = false;
};
authFile = config.sops.secrets.deluge-auth.path;
};
systemd.services = {
deluged = {
bindsTo = [
"${utils.escapeSystemdPath shadowDir}.mount"
];
unitConfig = {
RequiresMountsFor = [
shadowDir
];
};
};
};
systemd.tmpfiles.rules = mkAfter [
# work around https://github.com/NixOS/nixpkgs/blob/8f40f2f90b9c9032d1b824442cfbbe0dbabd0dbd/nixos/modules/services/torrent/deluge.nix#L205-L210
# (this is dumb, there's no guarantee the disk is even mounted)
"z '${cfg.config.move_completed_path}' 0775 ${cfg.user} ${cfg.group}"
"x '${mediaDir}/*'"
];
}

View file

@ -0,0 +1,260 @@
{
pkgs,
config,
lib,
...
}: let
cfg = config.services.home-assistant;
inherit (lib.attrsets) attrNames filterAttrs mapAttrs' nameValuePair;
inherit (lib.strings) hasPrefix;
in {
# MDNS
services.avahi.enable = true;
networking.firewall.allowedTCPPorts = [
# Home Assistant
cfg.config.http.server_port
] ++ map ({ port, ... }: port) cfg.config.homekit;
networking.firewall.allowedUDPPorts = [
# MDNS
5353
];
networking.firewall.allowedUDPPortRanges = [
# Chromecast
{
from = 32768;
to = 60999;
}
];
sops.secrets = {
ha-integration = {
owner = "hass";
path = "${cfg.configDir}/integration.yaml";
};
ha-secrets = {
owner = "hass";
path = "${cfg.configDir}/secrets.yaml";
};
};
systemd.services.home-assistant = {
# UI-editable config files
preStart = lib.mkBefore ''
touch ${cfg.configDir}/{automations,scenes,scripts,manual,homekit_entity_config,homekit_include_entities}.yaml
'';
};
services.home-assistant = {
enable = true;
config = {
homeassistant = {
name = "Gensokyo";
unit_system = "metric";
latitude = "!secret home_lat";
longitude = "!secret home_long";
elevation = "!secret home_asl";
currency = "CAD";
country = "CA";
time_zone = "America/Vancouver";
external_url = "https://home.gensokyo.zone";
packages = {
manual = "!include manual.yaml";
};
};
frontend = {
themes = "!include_dir_merge_named themes";
};
powercalc = {
};
utility_meter = {
};
withings = {
use_webhook = true;
};
logger = {
default = "info";
};
http = {
cors_allowed_origins = [
"https://google.com"
"https://www.home-assistant.io"
];
use_x_forwarded_for = "true";
trusted_proxies = [
"127.0.0.0/24"
"200::/7"
"100.64.0.0/10"
"fd7a:115c:a1e0:ab12::/64"
"::1"
];
};
recorder = {
db_url = "postgresql://@/hass";
auto_purge = true;
purge_keep_days = 14;
commit_interval = 1;
exclude = {
domains = [
"automation"
"updater"
];
entity_globs = [
"sensor.weather_*"
"sensor.date_*"
];
entities = [
"sun.sun"
"sensor.last_boot"
"sensor.date"
"sensor.time"
];
event_types = [
"call_service"
];
};
};
google_assistant = {
project_id = "gensokyo-5cfaf";
service_account = "!include integration.yaml";
report_state = true;
exposed_domains = [
"scene"
"script"
"climate"
#"sensor"
];
entity_config = {};
};
homekit = [ {
name = "Tewi";
port = 21063;
ip_address = "10.1.1.38";
filter = let
inherit (cfg.config) google_assistant;
in {
include_domains = google_assistant.exposed_domains;
include_entities = "!include homekit_include_entities.yaml";
};
entity_config = "!include homekit_entity_config.yaml";
} ];
tts = [
{
platform = "google_translate";
service_name = "google_say";
}
];
# https://nixos.wiki/wiki/Home_Assistant#Combine_declarative_and_UI_defined_automations
"automation manual" = [];
"automation ui" = "!include automations.yaml";
# https://nixos.wiki/wiki/Home_Assistant#Combine_declarative_and_UI_defined_scenes
"scene manual" = [];
"scene ui" = "!include scenes.yaml";
"script manual" = [];
"script ui" = "!include scripts.yaml";
counter = {};
device_tracker = {};
energy = {};
group = {};
history = {};
input_boolean = {};
input_button = {};
input_datetime = {};
input_number = {};
input_select = {};
input_text = {};
logbook = {};
schedule = {};
map = {};
media_source = {};
media_player = [
{
platform = "mpd";
name = "Shanghai MPD";
host = "10.1.1.32";
password = "!secret mpd-shanghai-password";
}
];
mobile_app = {};
my = {};
person = {};
prometheus = {};
ssdp = {};
switch = {};
stream = {};
sun = {};
system_health = {};
tag = {};
template = {};
timer = {};
webhook = {};
wake_on_lan = {};
zeroconf = {};
zone = {};
sensor = {};
};
package = let
inherit (lib) warn versionOlder elem;
inherit (cfg.package) python;
hasBrother = elem "brother" cfg.extraComponents;
# https://github.com/pysnmp/pysnmp/issues/51
needsPyasn1pin = if versionOlder python.pkgs.pysnmplib.version "6.0"
then true
else warn "pyasn1 pin likely no longer needed" false;
pyasn1prefix = "${python.pkgs.pysnmp-pyasn1}/${python.sitePackages}";
home-assistant = pkgs.home-assistant.override {
packageOverrides = self: super: {
};
};
in home-assistant.overrideAttrs (old: {
makeWrapperArgs = old.makeWrapperArgs ++ lib.optional (hasBrother && needsPyasn1pin) "--prefix PYTHONPATH : ${pyasn1prefix}";
disabledTests = old.disabledTests or [ ] ++ [
"test_check_config"
];
});
extraPackages = python3Packages:
with python3Packages; [
psycopg2
aiohomekit
securetar
getmac # for upnp integration
python-otbr-api
protobuf3
adb-shell
(callPackage ./androidtvremote2.nix { })
(aiogithubapi.overrideAttrs (_: {doInstallCheck = false;}))
];
extraComponents = [
"automation"
"scene"
"script"
"zha"
"esphome"
"apple_tv"
"spotify"
"default_config"
"brother"
"ipp"
"androidtv"
"cast"
"plex"
"environment_canada"
"met"
"generic_thermostat"
"google"
"google_assistant"
"google_cloud"
"google_translate"
"homekit"
"mpd"
"mqtt"
"shopping_list"
"tile"
"wake_on_lan"
"withings"
"wled"
"zeroconf"
];
};
}

48
systems/tewi/kanidm.nix Normal file
View file

@ -0,0 +1,48 @@
{
pkgs,
config,
...
}: let
conf = import ./snakeoil-certs.nix;
domain = conf.domain;
unencryptedCert = with pkgs;
runCommand "kanidm-cert" {
domain = "id.gensokyo.zone";
nativeBuildInputs = [minica];
} ''
install -d $out
cd $out
minica \
--ca-key ca.key.pem \
--ca-cert ca.cert.pem \
--domains $domain
cat $domain/cert.pem ca.cert.pem > $domain.pem
'';
in {
networking.firewall.allowedTCPPorts = [
8081
636
];
services.kanidm = {
enableServer = true;
enablePam = false;
enableClient = true;
clientSettings = {
uri = "https://id.gensokyo.zone";
verify_ca = true;
verify_hostnames = true;
};
serverSettings = {
domain = "gensokyo.zone";
origin = "https://id.gensokyo.zone";
role = "WriteReplica";
log_level = "info";
db_fs_type = "zfs";
bindaddress = "0.0.0.0:8081";
ldapbindaddress = "0.0.0.0:636";
tls_chain = "${unencryptedCert}/${unencryptedCert.domain}.pem";
tls_key = "${unencryptedCert}/${unencryptedCert.domain}/key.pem";
};
};
}

View file

@ -0,0 +1,47 @@
{ config, utils, lib, ... }: with lib; let
cfg = config.services.mediatomb;
shadowDir = "/mnt/shadow";
inherit (config.services) deluge;
in {
services.mediatomb = {
enable = true;
openFirewall = true;
port = 4152;
serverName = config.networking.hostName;
uuid = "082fd344-bf69-5b72-a68f-a5a4d88e76b2";
mediaDirectories = [
{
path = "${shadowDir}/media";
recursive = true;
hidden-files = false;
}
(mkIf deluge.enable {
path = builtins.dirOf deluge.config.download_location;
recursive = true;
hidden-files = false;
})
];
};
systemd.services.mediatomb = {
confinement.enable = true;
bindsTo = [
"${utils.escapeSystemdPath shadowDir}.mount"
];
unitConfig = {
RequiresMountsFor = [
shadowDir
];
};
serviceConfig = {
RestartSec = 15;
StateDirectory = cfg.package.pname;
BindReadOnlyPaths = mkMerge [
(map (path: "${shadowDir}/media/${path}") [
"anime" "movies" "tv" "unsorted"
"music" "music-to-import" "music-raw"
])
(mkIf deluge.enable [ deluge.config.move_completed_path ])
];
};
};
}

View file

@ -0,0 +1,57 @@
{
config,
lib,
...
}: {
networking.firewall.allowedTCPPorts = [
1883
];
sops.secrets = {
z2m-pass.owner = "mosquitto";
systemd-pass.owner = "mosquitto";
hass-pass.owner = "mosquitto";
espresense-pass.owner = "mosquitto";
};
services.mosquitto = {
enable = true;
persistence = true;
listeners = [
{
acl = [
"pattern readwrite #"
];
users = {
z2m = {
passwordFile = config.sops.secrets.z2m-pass.path;
acl = [
"readwrite #"
];
};
espresense = {
passwordFile = config.sops.secrets.espresense-pass.path;
acl = [
"readwrite #"
];
};
systemd = {
passwordFile = config.sops.secrets.systemd-pass.path;
acl = [
"readwrite #"
];
};
hass = {
passwordFile = config.sops.secrets.hass-pass.path;
acl = [
"readwrite #"
];
};
};
settings = {
allow_anonymous = false;
};
}
];
};
}

35
systems/tewi/nginx.nix Normal file
View file

@ -0,0 +1,35 @@
{
config,
lib,
pkgs,
...
}:
with lib; {
networking.firewall.allowedTCPPorts = [
443
80
];
services.nginx = {
enable = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedTlsSettings = false;
commonHttpConfig = ''
map $scheme $hsts_header {
https "max-age=31536000; includeSubdomains; preload";
}
#add_header Strict-Transport-Security $hsts_header;
#add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always;
add_header 'Referrer-Policy' 'origin-when-cross-origin';
#add_header X-Frame-Options DENY;
#add_header X-Content-Type-Options nosniff;
#add_header X-XSS-Protection "1; mode=block";
#proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
'';
clientMaxBodySize = "512m";
virtualHosts = {
};
};
}

253
systems/tewi/nixos.nix Normal file
View file

@ -0,0 +1,253 @@
{
meta,
config,
lib,
utils,
pkgs,
modulesPath,
...
}: let
inherit (lib) mkIf;
hddopts = ["luks" "discard" "noauto" "nofail"];
md = {
shadow = rec {
name = "shadowlegend";
device = "/dev/md/${name}";
unit = utils.escapeSystemdPath device + ".device";
where = "/mnt/shadow";
mount = utils.escapeSystemdPath where + ".mount";
service = "md-shadow.service";
disk = "/dev/disk/by-uuid/84aafe0e-132a-4ee5-8c5c-c4a396b999bf";
cryptDisks =
lib.flip lib.mapAttrs {
seagate0 = {
device = "/dev/disk/by-uuid/78880135-6455-4603-ae07-4e044a77b740";
keyFile = "/root/ST4000DM000-1F21.key";
options = hddopts;
};
hgst = {
device = "/dev/disk/by-uuid/4033c877-fa1f-4f75-b9de-07be84f83afa";
keyFile = "/root/HGST-HDN724040AL.key";
options = hddopts;
};
} (disk: attrs:
attrs
// {
service = "systemd-cryptsetup@${disk}.service";
});
};
};
in {
imports = with meta;
[
(modulesPath + "/installer/scan/not-detected.nix")
nixos.sops
nixos.tailscale
inputs.systemd2mqtt.nixosModules.default
./access.nix
./syncplay.nix
./kanidm.nix
./vouch.nix
./home-assistant.nix
./zigbee2mqtt.nix
./mosquitto.nix
./postgres.nix
./nginx.nix
./mediatomb.nix
./deluge.nix
./cloudflared.nix
];
boot.supportedFilesystems = ["nfs"];
services.udev.extraRules = ''
SUBSYSTEM=="tty", GROUP="input", MODE="0660"
'';
services.cockroachdb.locality = "provider=local,network=gensokyo,host=${config.networking.hostName}";
sops.defaultSopsFile = ./secrets.yaml;
networking = {
useNetworkd = true;
useDHCP = false;
};
services.resolved.enable = true;
environment.systemPackages = [
pkgs.cryptsetup
];
boot = {
loader = {
systemd-boot = {
enable = true;
};
efi = {
canTouchEfiVariables = true;
};
};
initrd = {
availableKernelModules = ["xhci_pci" "ehci_pci" "ahci" "usbhid" "usb_storage" "sd_mod"];
};
kernelModules = ["kvm-intel"];
};
services.openiscsi = {
enable = true;
enableAutoLoginOut = true;
name = "";
};
services.systemd2mqtt = {
enable = true;
user = "root";
mqtt = {
url = "tcp://localhost:1883";
username = "systemd";
};
units = {
${md.shadow.mount} = {};
"mediatomb.service" = mkIf config.services.mediatomb.enable {};
};
};
environment.etc = {
"iscsi/initiatorname.iscsi" = lib.mkForce {
source = config.sops.secrets.openiscsi-config.path;
};
crypttab.text = let
inherit (lib) concatStringsSep mapAttrsToList;
cryptOpts = lib.concatStringsSep ",";
in
concatStringsSep "\n" (mapAttrsToList (
disk: {
device,
keyFile,
options,
...
}: "${disk} ${device} ${keyFile} ${cryptOpts options}"
)
md.shadow.cryptDisks);
};
sops.secrets = {
openiscsi-config = {};
openiscsi-env = mkIf config.services.openiscsi.enableAutoLoginOut { };
systemd2mqtt-env = {};
};
fileSystems = {
"/" = {
device = "/dev/disk/by-uuid/6c5d82b1-5d11-4c72-96c6-5f90e6ce57f5";
fsType = "ext4";
};
"/boot" = {
device = "/dev/disk/by-uuid/85DC-72FA";
fsType = "vfat";
};
${md.shadow.where} = {
device = md.shadow.disk;
fsType = "xfs";
options = [
"x-systemd.automount"
"noauto" "nofail"
"x-systemd.requires=${md.shadow.service}"
"x-systemd.after=${md.shadow.service}"
"x-systemd.after=${md.shadow.unit}"
];
};
};
systemd = let
inherit (lib) getExe;
serviceName = lib.removeSuffix ".service";
toSystemdIni = pkgs.lib.generators.toINI {
listsAsDuplicateKeys = true;
};
cryptServices = lib.mapAttrsToList (_: {service, ...}: service) md.shadow.cryptDisks;
in {
services = {
nfs-mountd = {
wants = ["network-online.target"];
};
mdmonitor.enable = false;
${serviceName md.shadow.service} = rec {
restartIfChanged = false;
wants = cryptServices ++ [ "iscsi.service" ];
bindsTo = cryptServices;
after = wants;
serviceConfig = {
Type = "oneshot";
RemainAfterExit = "true";
ExecStartPre = [
"-${getExe pkgs.mdadm} --assemble --scan"
];
ExecStart = [
"${getExe pkgs.mdadm} --detail ${md.shadow.device}"
];
ExecStop = [
"${getExe pkgs.mdadm} --stop ${md.shadow.device}"
];
};
};
iscsid = rec {
wantedBy = cryptServices;
before = wantedBy;
};
iscsi = let
cfg = config.services.openiscsi;
in mkIf cfg.enableAutoLoginOut rec {
wantedBy = cryptServices;
before = wantedBy;
serviceConfig = {
EnvironmentFile = [ config.sops.secrets.openiscsi-env.path ];
ExecStartPre = [
"${cfg.package}/bin/iscsiadm --mode discoverydb --type sendtargets --portal $DISCOVER_PORTAL --discover"
];
};
};
systemd2mqtt = mkIf config.services.systemd2mqtt.enable rec {
requires = mkIf config.services.mosquitto.enable ["mosquitto.service"];
after = requires;
serviceConfig.EnvironmentFile = [
config.sops.secrets.systemd2mqtt-env.path
];
};
};
units = {
${md.shadow.mount} = {
overrideStrategy = "asDropin";
text = toSystemdIni {
Unit.BindsTo = [
md.shadow.service
];
};
};
};
network = {
networks.eno1 = {
inherit (config.systemd.network.links.eno1) matchConfig;
networkConfig = {
DHCP = "yes";
DNSDefaultRoute = true;
MulticastDNS = true;
};
};
links.eno1 = {
matchConfig = {
Type = "ether";
Driver = "e1000e";
};
linkConfig = {
WakeOnLan = "magic";
};
};
};
};
swapDevices = lib.singleton {
device = "/dev/disk/by-uuid/137605d3-5e3f-47c8-8070-6783ce651932";
};
system.stateVersion = "21.05";
}

11
systems/tewi/postgres.nix Normal file
View file

@ -0,0 +1,11 @@
{ config, pkgs, ... }: {
services.postgresql = {
enable = true;
package = pkgs.postgresql_14;
ensureDatabases = [ "hass" ];
ensureUsers = [{
name = "hass";
ensureDBOwnership = true;
}];
};
}

82
systems/tewi/secrets.yaml Normal file
View file

@ -0,0 +1,82 @@
espresense-pass: ENC[AES256_GCM,data:wGwUiDHkm5xpVTYxuTy2hQ==,iv:U5Ys+AFEkT7ThbR/qgh5VLNTBRsG72zYqOIO09HFTTc=,tag:h3wNg8nUKOYekpkdJelbOw==,type:str]
hass-pass: ENC[AES256_GCM,data:LvoI4sQ77HpYdmNoPLQ=,iv:oAQGTqBh1sf4fbuWGs9AqCE1yS8IApyhEQDUG+yQk7k=,tag:sBPdLuLTJ8OMoZYzUdmnAQ==,type:str]
systemd-pass: ENC[AES256_GCM,data:3bEqqWsnBHOgzD95YqwDvg==,iv:ack6EGhE2GzxwRi3gwj1A19Tzi2PJ9iiisMrKozPV/M=,tag:uCR51yn9dAG2x9DCfo1mGQ==,type:str]
z2m-pass: ENC[AES256_GCM,data:1bqOab8EQbniAMeL9XRmDg==,iv:uUU3kbuCRIGaueTPE54EHwm4IGwUu+67O4gPYZmd1h4=,tag:iceTSLsRuADiOgZ5cnlnjw==,type:str]
tailscale-key: ENC[AES256_GCM,data:dGqnKoCFSF6ZmeptOP7bGy4HYDdUCC1oTdXpiUURDgXl/FltOKExby0=,iv:c8yN1XLk3ZAAzkBozzHJ9BWerWdiNQG/p8e46j8cZyo=,tag:E5Ey5R+t372yLE6XegoOrA==,type:str]
vouch-client-secret: ENC[AES256_GCM,data:4MZL99JM4AeUcUfZ8a335utxgqvdH5PCc1R3KAvuOGpaWFGmU7CaD3vV5eLJ62gJ,iv:n1xbPBHi2TcZ12lm7LqItv2aOo7dkgzRh10uxFsy3yM=,tag:+fmJzYMhbiUae/kSyWbT5Q==,type:str]
vouch-jwt: ENC[AES256_GCM,data:XDalZtedsBNnDYApmWpdYR9yHBvNXA2DlMmKyCPmcMlqTlbAIVL702/HzTaWLvwpgVXpn3pgG8hNXm9rUE764Q==,iv:qyvGCsildhYgzQiYQ4M0H6eFYrKp8aTkwEeZywpQqHM=,tag:ogtAgvpYE43VPhLhD4NuNA==,type:str]
openiscsi-config: ENC[AES256_GCM,data:xyZVJRzR4vK+UAtq3+/QcszLIlcHXYifHnFKm5tVbFUj3c7PjxYGLkvXZfFvERStewdNIQ==,iv:BcbEupXiLECXwfETaVOqfHQ+vkBbrGxkQn54WBYug54=,tag:e0cddYTQAfzSk2AhvzJFvA==,type:str]
openiscsi-env: ENC[AES256_GCM,data:uAlnrtk64UQukKBWHYrH5J4Ys+GIpu5zDg==,iv:7ahUk9nocs4cSgtr/A4G0Xhlp7pZj/bUlUDLMMYEAMk=,tag:rE2mdBGT3kZqyoDIaKUY3w==,type:str]
systemd2mqtt-env: ENC[AES256_GCM,data:Zo3+acCcMWgai2ERKbmOlI0hvdkOlNviBqeLb1ALuA==,iv:NxXBDCEevBRqMDY9/3z/Uq2+vENswkYTgTa82wKc32U=,tag:01WUphYRJrwmHv9HE4ac8w==,type:str]
z2m-secret: ENC[AES256_GCM,data:SCxz8nbB/QhfPcAzSEDHMpiQnjv+j0xLtg/20qf5ZEe3P5YRaiKXMSqdw6MX7uQtGh8T44raEgS8PFuGKXY423GV/MNPSzMl16DLBwU5P7TL6lYT97uVYRIqWMKqtPy/1f155743wH8HsJvslmg=,iv:Yw9dvH1dBq+vxHvKm0eeHlqVHRdUuzL71mDTbIF7DDg=,tag:bCiDNSwq7P21TwblvVGq6A==,type:str]
deluge-auth: ENC[AES256_GCM,data:qJP/CztnN7RV4Z3pP+jbH1B0zzBm8oa3n3X0pecEVe7UI3+NOSwFaQCBD7Q7JDxzh+qTNdQ/wWi7w0XJDG+aRIikgDG28S9RjdPL/w==,iv:GUEwmuk3JWMgsXsDgDrObW657WcN6wcYAsgXhK4Dvx0=,tag:vZMQ67j5kWBWOa6ZqCaQHw==,type:str]
syncplay-env: ENC[AES256_GCM,data:MzL/Q4ihwVX+QgdWl20PfpCP8hiPd3uc00FuTJ+gsVN7EJOoDlTyA2pgfw75eklQgWa0r9T+3u3gigo7jxrBqmgD2oYDFrZNKrHyrXlxALQ=,iv:AO7hcXucPqJkCa3u3Y7nrgfIsw9f8fbWBc5g7Kb77cM=,tag:G+URgzoVrwiS6TjEgRy9rg==,type:str]
ha-secrets: ENC[AES256_GCM,data:sw+sxrDyNIOAkHSA2fPLqtHR32iKIaZDdgJ1ZcQTeOqlgdZDhK5XzXiBkpZBTwKswUc2UPDOANsn+YRusX2tUvLYjdxZz4jIBKJuaW3aEtOcxjmG55KYxHmTzSbR4IhpuZ+1wDhvp4UiUqyD273Lt6LTFDmohZAVqwxJc2k4YTZJXiofVf8ywcXPMcS+zIMxb5nBX9EJqgJ80pnBI8vZeCg4pFv/Ux2OoS+PTnkYHnsywf57NA23YcKqnsbJoZwi3e4ooM2erygv0DuoP7QBnBdEf5RKSziECDTDPUFQVNYIOEAgJIYOD/GtZmWMx4SadsmrgTB1RqtAdPnvuuD3jLYW20zci8CxmI4F/h/+8lxUH/TGiG2bUmPYvwTE1eW1cLK91XhxXp6ORyWMGYcR98ryGL00ctD/BaZLdk7FYbQMTF2y2vx5vWAABTnYLsv6H9csmD4O7OeNkd9k+YQB/atENAXRgIiGDvkkbNoRkRU5SzIIHGcjjU7GC6hT+LDportPuhUW+g6KJj0ENOCI0yXNkIfa6LXAJcCmdovJCIbdw1HCGHuu+dc5rSWueiOWo+2rr8loytZTTMwevTaldfrNyu42K+bZI2BkPdXzkPQDeV8y5PmfnXfdXthmmnZbYRyVYUd1OH9L,iv:zxpazCPJTWmuw7/BNj90G89aGyk3fCqBB+RCyKW6QwY=,tag:5zSnrZOxo8G2Wg4LNtEsaQ==,type:str]
cloudflared-tunnel-apartment: ENC[AES256_GCM,data:ccqV0jqrnbSz/r+C8v5FTWCtWVDHNdMwhiYHmjyrSLRt0xlcfSzqLhlTPMOfcEcded/NgiM9x3KrLm3hjW8GJpnaPGmQhP4GwBFEJ81R7dzXwpzbj0jbkj/sxxE7MEyofxfF+H2g5Kk55Yu148thxOB5Q4w2c6ZT16bHEXD20Cr88+T8chKQycfCWec0tE1WGmJYM3yrnaNvaey4oh4YWl+RApzxuA==,iv:KNjWDOF6PV8SVmXwzgom3wI+mjfnUSAzyuVIkDMgWHI=,tag:q9wOCGsXF5BOKEnt8jEAog==,type:str]
cloudflared-tunnel-apartment-deluge: ENC[AES256_GCM,data:BG4ak9wQQjDanJ+RqI33PrAx8g==,iv:s1c/6pWQZim73VjLiJmvR2ly7ktAQf7rJ6Yx34EFddo=,tag:nNu/Re0U5HJSb8JLFu2NMA==,type:str]
ha-integration: ENC[AES256_GCM,data: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,iv:tRzbBW/YFMp2vw26M9ediGY49GuxvyV2ijZ1W7mjURQ=,tag:L4ACYnVzdarztrjlsX3cAQ==,type:str]
sops:
shamir_threshold: 1
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age17haatqc7gpk9t690affyqcvwmhmz0us95en2r7qpqzw29tpq3ffspld0cf
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2a0xYL1ZUemk0NzExb0N0
Z1lGcEpTL3Q5U1BHUnJjVktrQUFrNjZKRnhrCm41MW9tbUFzbCtrem5JMXBuMGRv
Tk1kaWdaYU8yT3F0NmdHWVA1SlNmQU0KLS0tIGlmM2ZlSFBpc1RCRHhKb21iVVNZ
OS9BSForMEJPaUtaNi8rYXJRV3dJZXMKfz+v2KzomXM+OZL43AGyYt05oIuh0OTM
jZ4CbkL93bVw+IWY7iZumAskBJycBR2BwOnBlza/1e/jjLeRxkziew==
-----END AGE ENCRYPTED FILE-----
- recipient: age172nhlv3py990k2rgw64hy27hffmnpv6ssxyu9fepww7zxfgg347qna4gzt
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkeWhlWEI5N2w5S2gyMjhi
MjBMRDRIdktSYmxEQ1k4ZDh3dmg0TTdzdVFBCnRtMTFjVDdaNEpFckpyeUc5cFRH
Q2xsV04zODVTV0t1bURDK0ptejE1VTgKLS0tIFluUTVmQnpvUUVPZzdKWkZxdnB0
VndVTG0zQWhsUHcwTkFjK2ZPdzRPUUEKJ3flgZ6/s+TjlFgzsANYaOFiEPQuE4zR
7npNUDFLe26Q32G3j/lLSBzZZfKoOC5SOSp9TB8eWMYSxfNnXEIu0g==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-01-06T20:31:10Z"
mac: ENC[AES256_GCM,data:A9llofFkb5PfjS3KzcINuBuU9TbIFpDswmhBNJ3VpXJSvkrWLVhc3T4TcLotqffXtvidptngjiP0qcK4wM6oXcpfhRs9Td1MDtQQ8B9ICQknmt7ukIDU3FneJuVkxvSpJiNsAIH1x5ZCFzp9Dt550gloYSLklzzl3eozLOwcQ1U=,iv:wV4rjaC9+x+ALxRhlUys2Ao4Fv8E+9QyF7Rf3Kzs15g=,tag:xJzJkgNks3LdkoZEqpJQNg==,type:str]
pgp:
- created_at: "2023-03-10T17:06:53Z"
enc: |
-----BEGIN PGP MESSAGE-----
hQIMA82M54yws73UARAAnk1rE3kQa0KYvvdn335ekY7m9pp3CraVVckOTi7Jkbhr
Fud8P7EmF4pp1O/ibQXRChK3xUVPrO8v3tIMFSVeRPyeE/8Seo/cINSKpBZbC3LA
eKekxl1GzNVzrhEZjZ/Huu9o8qtih5lFwqKbNrB3HGh7NnkFycf0gLMNod++I5Eh
ib+LdMJA/R5oudPKp46P0NFY+/TjB6lfV/AQC3GtxcKJ9tAECH5CHhN67pthkhQ4
F2nJEPl0XD64U7FVpPBXUl1t03X1W33Z6EK6RWsQkb+JS3IegyutKnrWZbyz243f
MKmhbZEQ4gJjz6FZBH2rMD0E0YuH+OZsC+YMgMw2gRgd7RIzoO1ipOu4EKYHoB9s
oVoC8J/qvtP1UJgfXUnRA7rk9X9qaxk/1kKUiwiyx2NQo/tX3shcemXKjoYQMHW7
6opIe2PFEoOktbdewR3gZbkKPNHw+s6ajgCgoAWije9flouS39hhr0c9z/2FOjDk
nK29r3A7xsthZebRzs37075b/ZlynUhiWBKjZzJ5WW70XSve9om9T5vasjxk7/uA
Hi4bKltNrlbzqoqiDB0JgOTnns98azerCa7SwEgmO475Se344XY5KoxJS1WApsqB
Pe41SjVbhrinpVEy9we4ZBr1BHu9WEF844+yPBpLgARrF0R6GIqD6RDgfo71cDHS
XAGaHnj5eMdjEASeJ+KHR5zbwWeUssyeJWdzpK0MJcr9ItLt6LMD3brbvlacCGMY
P+DuHm5No7rWNWATykRQ3bBF3v1IEPh1wa7MLLjtQfvEEwfQD0l8Bgou1Sft
=eZUS
-----END PGP MESSAGE-----
fp: CD8CE78CB0B3BDD4
- created_at: "2023-03-10T17:06:53Z"
enc: |
-----BEGIN PGP MESSAGE-----
hQEMA2W9MER3HLb7AQgAt7a6OVIgJo8NHx7atPm68MckNqvYCs61jZUyEEZcrktc
ZkgGhR9IK5jSRZYYCVkZgfj1fikdAv6fF7GotEIJmdgcrQml3VzpAjpIyYuu1ilt
bybLp+ryoiE0pK9YF5Bl9vnZ4R+5m8SeAy6Z9WS7O7phxLCkAQ+dCQByyGD1Q4Zn
RRF+jIG6o2DnVu3wvkIs6s7dVWEDWJKh8sui97aOAzL5sLevT07WaeDC6LIikkhi
KMmvm3HgWghklDvMUTjw0MG3/k9qvg1kW5pQ2ZWivuCeMXA+NFAX1Epx61uZmgxf
8313IEfv4gXDXC2xCwmdOn0G6swktqdkY02t8ldFeNJcAXQ8PpieQ3aadGTvK6R9
0SgQ4MifOqnNMUDn1FvrfvrXRYHkc7qoyU+8PTzlQ1WCWYJvkrHS1ufFubeA57oJ
Kbf3xIXqe/8xP6uOw1/MEh4c3HeGbY7+ieW8miI=
=3NVV
-----END PGP MESSAGE-----
fp: 65BD3044771CB6FB
unencrypted_suffix: _unencrypted
version: 3.8.1

42
systems/tewi/syncplay.nix Normal file
View file

@ -0,0 +1,42 @@
{
config,
lib,
pkgs,
utils,
...
}:
with lib; let
cfg = config.services.syncplay;
args =
[
"--disable-ready"
"--port"
cfg.port
]
++ optionals (cfg.certDir != null) ["--tls" cfg.certDir];
in {
sops.secrets.syncplay-env.owner = cfg.user;
users.users.${cfg.user} = {
inherit (cfg) group;
isSystemUser = true;
home = "/var/lib/syncplay";
};
users.groups.${cfg.group} = {};
networking.firewall.allowedTCPPorts = [cfg.port];
services.syncplay = {
enable = true;
user = "syncplay";
};
systemd.services.syncplay = mkIf cfg.enable {
serviceConfig = {
StateDirectory = "syncplay";
EnvironmentFile = singleton config.sops.secrets.syncplay-env.path;
ExecStart = mkForce [
"${pkgs.syncplay-nogui}/bin/syncplay-server ${utils.escapeSystemdExecArgs args}"
];
};
};
}

121
systems/tewi/vouch.nix Normal file
View file

@ -0,0 +1,121 @@
{
config,
utils,
pkgs,
lib,
...
}: {
options = with lib; let
origin = "https://id.gensokyo.zone";
in {
services.vouch-proxy = {
settings = {
vouch = {
cookie = {
domain = mkOption {
type = types.nullOr types.str;
default = "gensokyo.zone";
};
secure = mkOption {
type = types.bool;
default = true;
};
};
port = mkOption {
type = lib.types.port;
default = 30746;
};
listen = mkOption {
type = types.nullOr types.str;
default = "127.0.0.1";
};
allowAllUsers = mkOption {
type = types.bool;
default = true;
};
};
oauth = {
auth_url = mkOption {
type = types.str;
default = "${origin}/ui/oauth2";
};
token_url = mkOption {
type = types.str;
default = "${origin}/oauth2/token";
};
user_info_url = mkOption {
type = types.str;
default = "${origin}/oauth2/openid/vouch/userinfo";
};
scopes = mkOption {
type = types.listOf types.str;
default = ["openid" "email" "profile"];
};
callback_url = mkOption {
type = types.str;
default = "https://login.gensokyo.zone/auth";
};
provider = mkOption {
type = types.nullOr types.str;
default = "oidc";
};
code_challenge_method = mkOption {
type = types.str;
default = "S256";
};
client_id = mkOption {
type = types.str;
default = "vouch";
};
};
};
};
};
config = {
services.vouch-proxy.settings = {
vouch.cookie.secure = false;
};
sops.secrets = {
vouch-jwt.owner = "vouch-proxy";
vouch-client-secret.owner = "vouch-proxy";
};
systemd.services.vouch-proxy = {
description = "Vouch-proxy";
after = ["network.target"];
wantedBy = ["multi-user.target"];
serviceConfig = {
ExecStart = let
recursiveMergeAttrs = listOfAttrsets: lib.fold (attrset: acc: lib.recursiveUpdate attrset acc) {} listOfAttrsets;
settings = recursiveMergeAttrs [
config.services.vouch-proxy.settings
{
oauth.client_secret._secret = config.sops.secrets.vouch-client-secret.path;
vouch.jwt.secret._secret = config.sops.secrets.vouch-jwt.path;
}
];
in
pkgs.writeShellScript "vouch-proxy-start" ''
${utils.genJqSecretsReplacementSnippet settings "/run/vouch-proxy/vouch-config.json"}
${pkgs.vouch-proxy}/bin/vouch-proxy -config /run/vouch-proxy/vouch-config.json
'';
Restart = "on-failure";
RestartSec = 5;
WorkingDirectory = "/var/lib/vouch-proxy";
StateDirectory = "vouch-proxy";
RuntimeDirectory = "vouch-proxy";
User = "vouch-proxy";
Group = "vouch-proxy";
StartLimitBurst = 3;
};
};
users.users.vouch-proxy = {
isSystemUser = true;
group = "vouch-proxy";
};
users.groups.vouch-proxy = {};
};
}

View file

@ -0,0 +1,45 @@
{
config,
lib,
...
}: {
networking.firewall.allowedTCPPorts = [
# Zigbee2MQTT Frontend
8072
];
sops.secrets.z2m-secret = {
owner = "zigbee2mqtt";
path = "${config.services.zigbee2mqtt.dataDir}/secret.yaml";
};
users.groups.input.members = ["zigbee2mqtt"];
services.zigbee2mqtt = {
enable = true;
settings = {
advanced = {
log_level = "info";
network_key = "!secret network_key";
};
mqtt = {
server = "mqtt://127.0.0.1:1883";
user = "z2m";
password = "!secret z2m_pass";
};
homeassistant = true;
permit_join = false;
frontend = {
port = 8072;
};
serial = {
port = "/dev/ttyUSB0";
};
availability = {
# minutes
active.timeout = 10;
passive.timeout = 60 * 50;
};
};
};
}