This commit is contained in:
2024-10-16 20:24:40 +02:00
parent b7bfb0f62a
commit c681eb3139
110 changed files with 2924 additions and 720 deletions

1
hosts/fw-new/channel Normal file
View File

@@ -0,0 +1 @@
https://channels.nixos.org/nixos-23.11

View File

@@ -0,0 +1,109 @@
{ lib, pkgs, ... }: {
imports = [
./fleet.nix
./utils/bento.nix
./utils/modules/sops.nix
./utils/modules/lego/lego.nix
./utils/modules/nginx.nix
./utils/modules/autoupgrade.nix
./utils/modules/promtail
# ./utils/modules/borgbackup.nix
# ./utils/modules/netdata.nix
# fw
./modules/networking.nix
./modules/firewall.nix
# ./modules/dhcp4.nix
./modules/unbound.nix
./modules/avahi.nix
./modules/openconnect.nix
./modules/wireguard.nix
./modules/podman.nix
./modules/omada.nix
# ./modules/ddclient.nix
# ./modules/wol.nix
# microvm
./modules/microvm.nix
./modules/gitea-vm.nix
# web
./modules/web
# git
# ./modules/gitea.nix
./modules/fwmetrics.nix
# ./modules/firefox-sync.nix
# home assistant
./modules/home-assistant
# ./modules/deconz.nix
# ./modules/mopidy.nix
./modules/mosquitto.nix
./modules/snapserver.nix
# gaming
# ./modules/palworld.nix
# ./modules/ark-survival-evolved.nix
./hardware-configuration.nix
];
nixpkgs.config.permittedInsecurePackages = [
"openssl-1.1.1w"
];
nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
"mongodb"
];
time.timeZone = "Europe/Vienna";
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
sops.defaultSopsFile = ./secrets.yaml;
environment.systemPackages = with pkgs; [
# bento
conntrack-tools # view network connection states
ethtool # manage NIC settings (offload, NIC feeatures, ...)
git
htop # to see the system load
tcpdump # view network traffic
vim # my preferred editor
wol
inotify-tools
];
nix.gc = {
automatic = true;
options = "--delete-older-than 60d";
};
# services.auto-cpufreq.enable = true;
# services.auto-cpufreq.settings = {
# charger = {
# governor = "powersave";
# turbo = "auto";
# };
# };
# zramSwap.enable = true;
networking.hostName = "fw-new";
services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
services.logind.extraConfig = "RuntimeDirectorySize=8G";
# backups
# borgbackup.repo = "u149513-sub2@u149513-sub2.your-backup.de:borg";
system.stateVersion = "23.11";
}

1
hosts/fw-new/fleet.nix Symbolic link
View File

@@ -0,0 +1 @@
../../fleet.nix

View File

@@ -0,0 +1,97 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
boot = {
loader.systemd-boot.enable = true;
loader.efi.canTouchEfiVariables = true;
kernelPackages = pkgs.linuxPackagesFor (pkgs.callPackage ./pkgs/kernel/vendor.nix {});
kernel.sysctl = {
"kernel.printk" = "1 4 1 7";
};
supportedFilesystems = lib.mkForce [ "vfat" "fat32" "exfat" "ext4" "btrfs" ];
initrd.includeDefaultModules = lib.mkForce false;
initrd.availableKernelModules = lib.mkForce [ "nvme" "mmc_block" "hid" "dm_mod" "dm_crypt" "input_leds" ];
# kernelParams copy from Armbian's /boot/armbianEnv.txt & /boot/boot.cmd
kernelParams = [
"rootwait"
"earlycon" # enable early console, so we can see the boot messages via serial port / HDMI
"consoleblank=0" # disable console blanking(screen saver)
"console=ttyS2,1500000" # serial port
"console=tty1" # HDMI
# docker optimizations
"cgroup_enable=cpuset"
"cgroup_memory=1"
"cgroup_enable=memory"
"swapaccount=1"
];
};
hardware = {
deviceTree = {
# https://github.com/armbian/build/blob/f9d7117/config/boards/orangepi5-plus.wip#L10C51-L10C51
name = "rockchip/rk3588-orangepi-5-plus.dtb";
overlays = [
];
};
enableRedistributableFirmware = lib.mkForce true;
firmware = [
(pkgs.callPackage ./pkgs/orangepi-firmware {})
];
opengl.package =
(
(pkgs.mesa.override {
galliumDrivers = ["panfrost" "swrast"];
vulkanDrivers = ["swrast"];
})
.overrideAttrs (_: {
pname = "mesa-panfork";
version = "23.0.0-panfork";
src = pkgs.fetchFromGitLab {
owner = "panfork";
repo = "mesa";
rev = "120202c675749c5ef81ae4c8cdc30019b4de08f4"; # branch: csf
hash = "sha256-4eZHMiYS+sRDHNBtLZTA8ELZnLns7yT3USU5YQswxQ0=";
};
})
)
.drivers;
};
fileSystems."/" =
{ device = "/dev/disk/by-uuid/c49c99cd-9a91-485d-8572-8f83df262907";
fsType = "ext4";
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/12CE-A600";
fsType = "vfat";
};
swapDevices =
[ { device = "/dev/disk/by-uuid/1f3fe124-c1f6-4e3f-9e89-4aa6d87d9853"; }
];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.enP3p49s0.useDHCP = lib.mkDefault true;
# networking.interfaces.enP4p65s0.useDHCP = lib.mkDefault true;
# networking.interfaces.enu1u3c2.useDHCP = lib.mkDefault true;
# networking.interfaces.wlP2p33s0.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
}

View File

@@ -0,0 +1,24 @@
{ config, pkgs, ... }:
{
virtualisation.oci-containers.backend = "podman";
virtualisation.oci-containers.containers = {
ark = {
image = "hermsi/ark-server:latest";
autoStart = true;
environmentFiles = [
config.sops.secrets.ark.path
];
volumes = [
"/var/lib/ark/app:/app/"
"/var/lib/ark/backup:/home/steam/ARK-Backups"
];
extraOptions = [
"--network=server"
"--ip=10.42.97.201"
];
};
};
sops.secrets.ark = {};
}

View File

@@ -0,0 +1,10 @@
{ ... }: {
services.avahi = {
enable = true;
reflector = true;
allowInterfaces = [
"server"
"lan"
];
};
}

View File

@@ -0,0 +1,23 @@
{ config, ... }:
{
services.ddclient = {
enable = true;
use = "if, if=wan";
protocol = "hetzner";
# server = "https://dns.hetzner.com/api/v1/";
username = "dominik.polakovics@cloonar.com";
passwordFile = config.sops.secrets.ddclient.path;
zone = "cloonar.com";
domains = [
"fw.cloonar.com"
"vpn.cloonar.com"
"git.cloonar.com"
"palworld.cloonar.com"
"matrix.cloonar.com"
];
};
sops.secrets.ddclient = {
# owner = config.systemd.services.ddclient.serviceConfig.User;
};
}

View File

@@ -0,0 +1,24 @@
{ config, pkgs, ... }: {
virtualisation = {
oci-containers.containers = {
deconz = {
autoStart = false;
image = "marthoc/deconz";
volumes = [
"/etc/localtime:/etc/localtime:ro"
"/var/lib/deconz:/root/.local/share/dresden-elektronik/deCONZ"
];
environment = {
DECONZ_DEVICE = "/dev/ttyACM0";
TZ = "Europe/Vienna";
};
extraOptions = [
"--network=server"
"--ip=10.42.97.22"
"--device=/dev/ttyACM0"
"--hostname=deconz"
];
};
};
};
}

View File

@@ -0,0 +1,376 @@
{ ... }: {
services.kea.dhcp4 = {
enable = true;
settings = {
interfaces-config = {
interfaces = [
"lan"
"server"
"infrastructure"
"multimedia"
"smart"
"guest"
];
};
lease-database = {
name = "/var/lib/kea/dhcp4.leases";
persist = true;
type = "memfile";
};
rebind-timer = 2000;
renew-timer = 1000;
subnet4 = [
{
pools = [
{
pool = "10.42.112.100 - 10.42.112.240";
}
];
subnet = "10.42.112.0/24";
interface = "lan";
option-data = [
{
name = "routers";
data = "10.42.112.1";
}
{
name = "domain-name";
data = "cloonar.com";
}
{
name = "domain-search";
data = "cloonar.com";
}
{
name = "domain-name-servers";
data = "10.42.112.1";
}
];
reservations = [
{
hw-address = "04:7c:16:d5:63:5e";
ip-address = "10.42.112.5";
server-hostname = "omada.cloonar.com";
}
{
hw-address = "30:05:5c:56:62:37";
ip-address = "10.42.112.100";
server-hostname = "brn30055c566237.cloonar.com";
}
];
}
{
pools = [
{
pool = "10.42.113.100 - 10.42.113.240";
}
];
subnet = "10.42.113.0/24";
interface = "server";
option-data = [
{
name = "routers";
data = "10.42.113.1";
}
{
name = "domain-name";
data = "cloonar.com";
}
{
name = "domain-name-servers";
data = "10.42.113.1";
}
];
reservations = [
{
hw-address = "1a:c4:04:6e:29:bd";
ip-address = "10.42.113.2";
server-hostname = "omada.cloonar.com";
}
{
hw-address = "02:00:00:00:00:03";
ip-address = "10.42.113.5";
server-hostname = "web-02.cloonar.com";
}
{
hw-address = "ea:db:d4:c1:18:ba";
ip-address = "10.42.113.50";
server-hostname = "git.cloonar.com";
}
{
hw-address = "c2:4f:64:dd:13:0c";
ip-address = "10.42.113.20";
server-hostname = "home-assistant.cloonar.com";
}
{
hw-address = "1a:c4:04:6e:29:02";
ip-address = "10.42.113.25";
server-hostname = "deconz.cloonar.com";
}
];
}
{
pools = [
{
pool = "10.42.117.100 - 10.42.117.240";
}
];
subnet = "10.42.117.0/24";
interface = "infrastructure";
option-data = [
{
name = "routers";
data = "10.42.117.1";
}
{
name = "domain-name";
data = "cloonar.com";
}
{
name = "domain-name-servers";
data = "10.42.117.1";
}
{
name = "capwap-ac-v4";
code = 138;
data = "10.42.117.2";
}
];
reservations = [
];
}
{
pools = [
{
pool = "10.42.115.100 - 10.42.115.240";
}
];
subnet = "10.42.115.0/24";
interface = "multimedia";
option-data = [
{
name = "routers";
data = "10.42.115.1";
}
{
name = "domain-name";
data = "cloonar.multimedia";
}
{
name = "domain-name-servers";
data = "10.42.115.1";
}
];
reservations = [
{
hw-address = "c4:a7:2b:c7:ea:30";
ip-address = "10.42.115.10";
hostname = "metz.cloonar.multimedia";
}
{
hw-address = "f0:2f:9e:d4:3b:21";
ip-address = "10.42.115.11";
hostname = "firetv-living";
}
{
hw-address = "bc:33:29:ed:24:f0";
ip-address = "10.42.115.12";
hostname = "ps5";
}
{
hw-address = "e4:2a:ac:32:3f:79";
ip-address = "10.42.115.13";
hostname = "xbox";
}
{
hw-address = "98:b6:e9:b6:ef:f4";
ip-address = "10.42.115.14";
hostname = "switch";
}
{
hw-address = "f0:2f:9e:c1:74:72";
ip-address = "10.42.115.21";
hostname = "firetv-bedroom";
}
{
hw-address = "30:05:5c:56:62:37";
ip-address = "10.42.115.100";
server-hostname = "brn30055c566237";
}
];
}
{
pools = [
{
pool = "10.42.127.10 - 10.42.127.254";
}
];
subnet = "10.42.127.0/24";
interface = "guest";
option-data = [
{
name = "routers";
data = "10.42.127.1";
}
{
name = "domain-name-servers";
data = "9.9.9.9";
}
];
}
{
pools = [
{
pool = "10.42.116.100 - 10.42.116.240";
}
];
subnet = "10.42.116.0/24";
interface = "smart";
option-data = [
{
name = "routers";
data = "10.42.116.1";
}
{
name = "domain-name";
data = "cloonar.smart";
}
{
name = "domain-name-servers";
data = "10.42.116.1";
}
];
reservations = [
# need fixed ips for all shelly devices
# living room 1 - 14
# 10.42.100.2 # bulb1
# 10.42.100.3 # bulb2
# 10.42.100.4 # bulb3
# 10.42.100.5 # bulb4
# 10.42.100.6 # bulb5
# 10.42.100.7 # bulb6
# 10.42.100.8 # piano
# 10.42.100.9 # switch
# 10.42.100.10 # steamdeck
# kitchen:
# 10.42.100.17 # coffee
# 10.42.100.18 # bar
# bedroom:
# 10.42.100.33 # switch
# 10.42.100.34 # button1
# 10.42.100.35 # button2
# 10.42.100.36 # readingled1
# 10.42.100.37 # readingled2
# 10.42.100.38 # bedled
# bath:
# 10.42.100.49 # switch
# 10.42.100.50 # bulb1
# 10.42.100.51 # bulb2
# 10.42.100.52 # smallswitch
# 10.42.100.53 # ht
# hallway:
# 10.42.100.65 # switch
# 10.42.100.66 # bulb1
# 10.42.100.67 # bulb2
# 10.42.100.68 # bulb3
# 10.42.100.69 # bulb4
# toilet:
# 10.42.100.81 # switch
# 10.42.100.82 # bulb
# storage:
# 10.42.100.97 # switch
{
hw-address = "60:a4:23:97:4a:ec";
ip-address = "10.42.116.21";
server-hostname = "shellymotionsensor-60A423974AEC";
}
{
hw-address = "8c:aa:b5:61:6f:e2";
ip-address = "10.42.116.103";
server-hostname = "ShellyBulbDuo-8CAAB5616FE2";
}
{
hw-address = "8c:aa:b5:61:6e:9e";
ip-address = "10.42.116.104";
server-hostname = "ShellyBulbDuo-8CAAB5616E9E";
}
{
hw-address = "cc:50:e3:bc:27:64";
ip-address = "10.42.116.112";
server-hostname = "Nuki_Bridge_1A753F72";
}
{
hw-address = "e8:db:84:a9:ea:be";
ip-address = "10.42.116.117";
server-hostname = "ShellyBulbDuo-E8DB84A9EABE";
}
{
hw-address = "e8:db:84:a9:d1:8b";
ip-address = "10.42.116.119";
server-hostname = "shellycolorbulb-E8DB84A9D18B";
}
{
hw-address = "3c:61:05:e5:96:e0";
ip-address = "10.42.116.120";
server-hostname = "shellycolorbulb-3C6105E596E0";
}
{
hw-address = "e8:db:84:a9:d7:ef";
ip-address = "10.42.116.121";
server-hostname = "shellycolorbulb-E8DB84A9D7EF";
}
{
hw-address = "e8:db:84:aa:51:aa";
ip-address = "10.42.116.122";
server-hostname = "shellycolorbulb-E8DB84AA51AA";
}
{
hw-address = "34:94:54:79:bc:57";
ip-address = "10.42.116.130";
server-hostname = "shellycolorbulb-34945479bc57";
}
{
hw-address = "48:55:19:d9:a1:b2";
ip-address = "10.42.116.131";
server-hostname = "shellycolorbulb-485519d9a1b2";
}
{
hw-address = "48:55:19:d9:ae:95";
ip-address = "10.42.116.132";
server-hostname = "shellycolorbulb-485519d9ae95";
}
{
hw-address = "48:55:19:d9:4a:28";
ip-address = "10.42.116.133";
server-hostname = "shellycolorbulb-485519d94a28";
}
{
hw-address = "48:55:19:da:6b:6a";
ip-address = "10.42.116.134";
server-hostname = "shellycolorbulb-485519da6b6a";
}
{
hw-address = "48:55:19:d9:e0:18";
ip-address = "10.42.116.135";
server-hostname = "shellycolorbulb-485519d9e018";
}
{
hw-address = "34:6f:24:f3:af:ad";
ip-address = "10.42.116.137";
server-hostname = "daikin86604";
}
{
hw-address = "34:6f:24:c1:f8:54";
ip-address = "10.42.116.139";
server-hostname = "daikin53800";
}
];
}
];
valid-lifetime = 4000;
};
};
}

View File

@@ -0,0 +1,83 @@
{ config, pkgs, ... }:
let
domain = "sync.cloonar.com";
in {
sops.secrets.firefox-sync = { };
security.acme.certs."${domain}" = {
group = "nginx";
};
containers."firefox-sync" = {
autoStart = true;
ephemeral = false; # because of ssh key
privateNetwork = true;
hostBridge = "server";
hostAddress = "10.42.97.1";
localAddress = "10.42.97.51/24";
bindMounts = {
"/run/secrets/firefox-sync" = {
hostPath = "/run/secrets/firefox-sync";
isReadOnly = true;
};
"/var/lib/acme/${domain}/" = {
hostPath = "${config.security.acme.certs.${domain}.directory}";
isReadOnly = true;
};
};
config = { lib, config, pkgs, ... }: {
networking = {
hostName = "firefox-sync";
useHostResolvConf = false;
defaultGateway = {
address = "10.42.97.1";
interface = "eth0";
};
firewall.enable = false;
nameservers = [ "10.42.97.1" ];
};
services.nginx.enable = true;
services.nginx.virtualHosts."${domain}" = {
sslCertificate = "/var/lib/acme/${domain}/fullchain.pem";
sslCertificateKey = "/var/lib/acme/${domain}/key.pem";
sslTrustedCertificate = "/var/lib/acme/${domain}/chain.pem";
listen = [
{
addr = "0.0.0.0";
ssl = true;
port = 5000;
}
];
locations."/" = {
proxyPass = "http://localhost:5001/";
recommendedProxySettings = true;
};
};
services.mysql.package = pkgs.mariadb;
services.firefox-syncserver = {
enable = true;
singleNode = {
enable = true;
enableNginx = false;
hostname = domain;
};
settings = {
port = 5001;
tokenserver.enable = true;
};
secrets = "/run/secrets/firefox-sync";
logLevel = "trace";
};
services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
system.stateVersion = "23.05";
};
};
}

View File

@@ -0,0 +1,160 @@
{ pkgs, ... }: {
networking = {
firewall.checkReversePath = false;
nat.enable = false;
nftables = {
enable = true;
tables = {
"cloonar-fw" = {
family = "inet";
content = ''
chain output {
type filter hook output priority 100; policy accept;
}
chain rpfilter {
type filter hook prerouting priority mangle + 10; policy drop;
meta nfproto ipv4 udp sport . udp dport { 68 . 67, 67 . 68 } accept comment "DHCPv4 client/server"
fib saddr . mark . iif oif exists accept
}
chain input {
type filter hook input priority filter; policy drop;
iifname "lo" accept comment "trusted interfaces"
iifname "lan" counter accept comment "Spice"
ct state vmap { invalid : drop, established : accept, related : accept, new : jump input-allow, untracked : jump input-allow }
tcp flags syn / fin,syn,rst,ack log prefix "refused connection: " level info
}
chain input-allow {
udp dport != { 53, 5353 } ct state new limit rate over 1/second burst 10 packets drop comment "rate limit for new connections"
iifname lo accept
iifname "wan" udp dport 51820 counter accept comment "Wireguard traffic"
iifname "wan" tcp dport 9273 counter accept comment "Prometheus traffic"
iifname "lan" tcp dport 5931 counter accept comment "Spice"
iifname { "wan", "server", "vserver", "vm-*", "lan", "wg_cloonar" } counter accept comment "allow trusted to router"
iifname { "multimedia", "smart", "infrastructure", "podman0" } udp dport { 53, 5353 } counter accept comment "DNS"
iifname { "wan", "multimedia" } icmp type { echo-request, destination-unreachable, time-exceeded } counter accept comment "Allow select ICMP"
# Accept mDNS for avahi reflection
iifname "server" ip saddr 10.42.113.20/32 tcp dport { llmnr } counter accept
iifname "server" ip saddr 10.42.113.20/32 udp dport { mdns, llmnr } counter accept
# Allow all returning traffic
ct state { established, related } counter accept
# Allow returning traffic from wrwks and drop everthing else
iifname "wrwks" ct state { established, related } counter accept
iifname "wrwks" drop
# Allow returning traffic from wg_epicenter and drop everthing else
iifname "wg_epicenter" ct state { established, related } counter accept
iifname "wg_epicenter" drop
# Allow returning traffic from wg_ghetto_at and drop everthing else
iifname "wg_ghetto_at" ct state { established, related } counter accept
iifname "wg_ghetto_at" drop
# Allow returning traffic from wan and drop everthing else
iifname "wan" ct state { established, related } accept comment "Allow established traffic"
iifname "wan" icmp type { echo-request, destination-unreachable, time-exceeded } counter accept comment "Allow select ICMP"
iifname "wan" counter drop comment "Drop all other unsolicited traffic from wan"
limit rate 60/minute burst 100 packets log prefix "Input - Drop: " comment "Log any unmatched traffic"
}
chain forward {
type filter hook forward priority filter; policy drop;
iifname "wg_cloonar" counter accept comment "test wireguard"
iifname "wg_cloonar" oifname lo counter accept comment "wireguard to server"
# enable flow offloading for better throughput
# ip protocol { tcp, udp } flow offload @f
# broadcast
iifname "server" oifname { "lan", "multimedia" } udp dport { 9 } counter accept comment "wakeonlan"
# multimedia airplay
iifname "multimedia" oifname { "lan" } counter accept
iifname "multimedia" oifname "server" tcp dport { 1704, 1705 } counter accept
iifname "lan" oifname "server" udp dport { 5000, 5353, 6001 - 6011 } counter accept
# avahi
iifname "server" ip saddr 10.42.113.20/32 oifname { "lan" } counter accept
# smart home coap
iifname "smart" oifname "server" ip daddr 10.42.113.20/32 udp dport { 5683 } counter accept
iifname "smart" oifname "server" ip daddr 10.42.113.20/32 tcp dport { 1883 } counter accept
# Forward to git server
oifname "server" ip daddr 10.42.113.50 tcp dport { 22 } counter accept
oifname "server" ip daddr 10.42.113.5 tcp dport { 80, 443 } counter accept
# lan and vpn to any
# TODO: disable wan when finished
iifname { "lan", "server", "vserver", "wg_cloonar" } oifname { "lan", "vb-*", "vm-*", "server", "vserver", "infrastructure", "multimedia", "smart", "wg_cloonar" } counter log prefix "basic forward allow rule" accept
iifname { "lan", "server", "wg_cloonar" } oifname { "wrwks", "wg_epicenter", "wg_ghetto_at" } counter accept
iifname { "infrastructure" } oifname { "server", "vserver" } counter accept
iifname { "lan", "wan" } udp dport { 8211, 27015 } counter accept comment "palworld"
# accept palword server
iifname { "wan", "lan" } oifname "podman0" udp dport { 8211, 27015 } counter accept comment "palworld"
# forward to ark server
oifname "server" ip daddr 10.42.113.201 tcp dport { 27020 } counter accept comment "ark survival evolved"
oifname "server" ip daddr 10.42.113.201 udp dport { 7777, 7778, 27015 } counter accept comment "ark survival evolved"
# firefox-sync
oifname "server" ip daddr 10.42.113.51 tcp dport { 5000 } counter accept comment "firefox-sync"
# allow all established, related
ct state { established, related } accept comment "Allow established traffic"
# Allow trusted network WAN access
iifname {
"lan",
"infrastructure",
"server",
"vserver",
"multimedia",
"smart",
"wg_cloonar",
"podman*",
"guest",
"vb-*",
"vm-*",
} oifname {
"wan",
} counter accept comment "Allow trusted LAN to WAN"
limit rate 60/minute burst 100 packets log prefix "Forward - Drop: " comment "Log any unmatched traffic"
}
'';
};
"cloonar-nat" = {
family = "ip";
content = ''
chain prerouting {
type nat hook prerouting priority filter; policy accept;
iifname "server" ip daddr 10.42.96.255 udp dport { 9 } dnat to 10.42.96.255
# iifname "wan" tcp dport { 22 } dnat to 10.42.113.50
iifname "wan" tcp dport { 80, 443 } dnat to 10.42.113.5
iifname "wan" tcp dport { 5000 } dnat to 10.42.113.51
iifname { "wan", "lan" } udp dport { 7777, 7778, 27015 } dnat to 10.42.113.201
iifname { "wan", "lan" } tcp dport { 27020 } dnat to 10.42.113.201
}
# Setup NAT masquerading on external interfaces
chain postrouting {
type nat hook postrouting priority filter; policy accept;
oifname { "wan", "wg_cloonar", "wrwks", "wg_epicenter", "wg_ghetto_at" } masquerade
iifname { "wan", "wg_cloonar" } ip daddr 10.42.113.50 masquerade
iifname { "wan", "wg_cloonar" } ip daddr 10.42.113.51 masquerade
iifname { "wan", "wg_cloonar" } ip daddr 10.42.113.201 masquerade
}
'';
};
};
};
};
}

View File

@@ -0,0 +1,30 @@
{ config, pkgs, ... }:
let
configure_prom = builtins.toFile "prometheus.yml" ''
scrape_configs:
- job_name: 'server'
stream_parse: true
static_configs:
- targets:
- ${config.networking.hostName}:9100
'';
in {
sops.secrets.victoria-agent-env = {
sopsFile = ../utils/modules/victoriametrics/secrets.yaml;
};
services.prometheus.exporters.node.enable = true;
systemd.services.export-fw-to-prometheus = {
path = with pkgs; [victoriametrics];
enable = true;
after = ["network-online.target"];
wants = ["network-online.target"];
wantedBy = ["multi-user.target"];
script = "vmagent -promscrape.config=${configure_prom} -envflag.enable -remoteWrite.url=https://victoria-server.cloonar.com/api/v1/write";
serviceConfig = {
EnvironmentFile=config.sops.secrets.victoria-agent-env.path;
};
};
}

View File

@@ -0,0 +1,175 @@
{ config, nixpkgs, pkgs, ... }: let
hostname = "git";
json = pkgs.formats.json { };
pkgs-with-gitea = import (builtins.fetchGit {
name = "new-gitea";
url = "https://github.com/nixos/nixpkgs/";
rev = "159be5db480d1df880a0135ca0bfed84c2f88353";
}) {};
in {
microvm.vms = {
gitea = {
config = {
microvm = {
hypervisor = "cloud-hypervisor";
shares = [
{
source = "/nix/store";
mountPoint = "/nix/.ro-store";
tag = "ro-store";
proto = "virtiofs";
}
{
source = "/var/lib/acme/git.cloonar.com";
mountPoint = "/var/lib/acme/${hostname}.cloonar.com";
tag = "ro-cert";
proto = "virtiofs";
}
];
interfaces = [
{
type = "tap";
id = "vm-${hostname}";
mac = "02:00:00:00:00:01";
}
];
};
imports = [
../fleet.nix
];
environment.systemPackages = with pkgs; [
vim # my preferred editor
];
networking = {
hostName = hostname;
firewall = {
enable = true;
allowedTCPPorts = [ 22 80 443 ];
};
};
services.nginx.enable = true;
services.nginx.virtualHosts."${hostname}.cloonar.com" = {
sslCertificate = "/var/lib/acme/${hostname}.cloonar.com/fullchain.pem";
sslCertificateKey = "/var/lib/acme/${hostname}.cloonar.com/key.pem";
sslTrustedCertificate = "/var/lib/acme/${hostname}.cloonar.com/chain.pem";
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:3001/";
};
};
services.gitea = {
enable = true;
package = pkgs-with-gitea.gitea;
appName = "Cloonar Gitea server"; # Give the site a name
settings = {
server = {
ROOT_URL = "https://${hostname}.cloonar.com/";
HTTP_PORT = 3001;
DOMAIN = "${hostname}.cloonar.com";
};
openid = {
ENABLE_OPENID_SIGNIN = true;
ENABLE_OPENID_SIGNUP = true;
WHITELISTED_URIS = "auth.cloonar.com";
};
service = {
DISABLE_REGISTRATION = true;
ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
SHOW_REGISTRATION_BUTTON = false;
};
actions.ENABLED=true;
};
};
services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
system.stateVersion = "22.05";
};
};
gitea-runner = {
config = {
microvm = {
mem = 12288;
shares = [
{
source = "/nix/store";
mountPoint = "/nix/.ro-store";
tag = "ro-store";
proto = "virtiofs";
}
{
source = "/run/secrets";
mountPoint = "/run/secrets";
tag = "ro-token";
proto = "virtiofs";
}
];
volumes = [
{
image = "rootfs.img";
mountPoint = "/";
size = 102400;
}
];
interfaces = [
{
type = "tap";
id = "vm-gitea-runner";
mac = "02:00:00:00:00:02";
}
];
};
environment.systemPackages = with pkgs; [
vim # my preferred editor
];
networking.hostName = "gitea-runner";
virtualisation.podman.enable = true;
services.gitea-actions-runner.instances.vm = {
enable = true;
url = "https://git.cloonar.com";
name = "vm";
tokenFile = "/run/secrets/gitea-runner-token";
labels = [
"ubuntu-latest:docker://shivammathur/node:latest"
];
settings = {
container = {
network = "podman";
};
};
};
services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
system.stateVersion = "22.05";
};
};
};
sops.secrets.gitea-runner-token = {};
environment = {
systemPackages = [
pkgs.qemu
pkgs.quickemu
];
};
}

View File

@@ -0,0 +1,110 @@
{ config, pkgs, ... }:
let
cids = import ../modules/staticids.nix;
domain = "git.cloonar.com";
user = {
isSystemUser = true;
uid = cids.uids.gitea;
group = "gitea";
home = "/var/lib/gitea";
createHome = true;
};
group = {
gid = cids.gids.gitea;
};
in
{
users.users.gitea = user;
users.groups.gitea = group;
security.acme.certs."${domain}" = {
group = "nginx";
};
containers.git = {
autoStart = true;
ephemeral = false; # because of ssh key
privateNetwork = true;
hostBridge = "server";
hostAddress = "10.42.97.1";
localAddress = "10.42.97.50/24";
bindMounts = {
"/var/lib/gitea" = {
hostPath = "/var/lib/gitea/";
isReadOnly = false;
};
"/var/lib/acme/gitea/" = {
hostPath = "${config.security.acme.certs.${domain}.directory}";
isReadOnly = true;
};
};
config = { lib, config, pkgs, ... }: {
imports = [
../fleet.nix
];
environment.systemPackages = with pkgs; [
vim # my preferred editor
];
networking = {
hostName = "git";
useHostResolvConf = false;
defaultGateway = {
address = "10.42.96.1";
interface = "eth0";
};
firewall.enable = false;
nameservers = [ "10.42.97.1" ];
};
services.nginx.enable = true;
services.nginx.virtualHosts."${domain}" = {
sslCertificate = "/var/lib/acme/gitea/fullchain.pem";
sslCertificateKey = "/var/lib/acme/gitea/key.pem";
sslTrustedCertificate = "/var/lib/acme/gitea/chain.pem";
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:3001/";
};
};
services.gitea = {
enable = true;
appName = "Cloonar Gitea server"; # Give the site a name
settings = {
server = {
ROOT_URL = "https://${domain}/";
HTTP_PORT = 3001;
DOMAIN = domain;
};
openid = {
ENABLE_OPENID_SIGNIN = false;
ENABLE_OPENID_SIGNUP = true;
WHITELISTED_URIS = "auth.cloonar.com";
};
service = {
DISABLE_REGISTRATION = false;
ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
SHOW_REGISTRATION_BUTTON = false;
};
actions.ENABLED=true;
};
};
services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
users.users.gitea = user;
users.groups.gitea = group;
system.stateVersion = "23.05";
};
};
sops.secrets.gitea-runner = {};
}

View File

@@ -0,0 +1,60 @@
{ config, ... }: {
services.home-assistant.config = {
sensor = [
{
platform = "rest";
name = "creality extruder";
resource = "http://k1c-63e9.cloonar.smart:7125/printer/objects/query?extruder";
value_template = "OK";
json_attributes_path = "$.result.status.extruder";
json_attributes = [
"pressure_advance"
"power"
"target"
"temperature"
];
}
{
platform = "rest";
name = "creality print stats";
resource = "http://k1c-63e9.cloonar.smart:7125/printer/objects/query?print_stats";
value_template = "OK";
json_attributes_path = "$.result.status.print_stats";
json_attributes = [
"filename"
"total_duration"
"print_duration"
"filament_used"
"state"
"message"
];
}
{
platform = "template";
sensors = {
crality_hotend_actual = {
friendly_name = "Hot End Actual";
value_template = "{{ state_attr('sensor.creality_extruder', 'temperature') | float | round(1) }}";
device_class = "temperature";
unit_of_measurement = "°C";
};
};
}
];
"automation 3d printer state" = {
alias = "3d printer state change";
trigger = [
{
platform = "template";
value_template = "{{ state_attr('sensor.creality_print_stats','state') == 'standby' }}";
}
];
action = {
service = "notify.mobile_app_dominiks_iphone";
data = {
message = "Printer status changed to {{ state_attr('sensor.creality_print_stats','state') }}";
};
};
};
};
}

View File

@@ -0,0 +1,113 @@
{
services.home-assistant.extraComponents = [
"daikin"
"enocean"
];
services.home-assistant.config = {
sensor = [
{
name = "Living Room Window Handle 2";
platform = "enocean";
id = [ 129 0 227 53 ];
device_class = "windowhandle";
}
{
name = "Living Room Window Handle 1";
platform = "enocean";
id = [ 129 0 229 8 ];
device_class = "windowhandle";
}
];
"automation ac_livingroom" = {
alias = "ac_livingroom";
trigger = [
{
platform = "state";
entity_id = "sensor.windowhandle_living_room_window_handle_1";
to = [ "open" "tilt" ];
}
{
platform = "state";
entity_id = "sensor.windowhandle_living_room_window_handle_2";
to = [ "open" "tilt" ];
}
];
action = {
service = "climate.set_hvac_mode";
target = {
entity_id = "climate.living_room";
};
data = {
hvac_mode = "off";
};
};
};
"automation ac_eco" = {
alias = "ac_eco";
trigger = {
platform = "state";
entity_id = [
"climate.living_room"
"climate.bedroom"
];
to = [
"heat"
"cold"
];
};
action = {
service = "climate.set_preset_mode";
target = {
entity_id = "{{ trigger.entity_id }}";
};
data = {
preset_mode = "eco";
};
};
};
"automation bedroom_ac_on" = {
alias = "bedroom ac on";
trigger = {
platform = "time";
at = "00:30:00";
};
action = {
choose = [
{
conditions = [ "{{ states('sensor.bedroom_ac_inside_temperature') > 25 and states('sensor.bedroom_ac_outside_temperature') > 22 }}" ];
sequence = [
{
service = "climate.set_hvac_mode";
target = {
entity_id = "climate.bedroom";
};
data = {
hvac_mode = "cold";
};
}
];
}
];
};
};
"automation bedroom_ac_off" = {
alias = "bedroom ac on";
trigger = {
platform = "template";
value_template = ''
{{ now().timestamp() | timestamp_custom('%H:%M') == (as_timestamp(strptime(states('sensor.bedtime_alarm'), "%H:%M")) - 1800) | timestamp_custom('%H:%M', false) }}
'';
};
action = {
service = "climate.set_hvac_mode";
target = {
entity_id = "climate.bedroom";
};
data = {
hvac_mode = "off";
};
};
};
};
}

View File

@@ -0,0 +1,91 @@
{
services.home-assistant.config = {
sensor = [
{
platform = "template";
sensors = {
sensors_lowest_battery_level = {
friendly_name = "Lowest battery level (Sensors)";
entity_id = "sun.sun";
device_class = "battery";
unit_of_measurement = "%";
value_template = ''
{% set domains = ['sensor', 'battery'] %}
{% set ns = namespace(min_batt=100, entities=[]) %}
{%- set exclude_sensors = ['sensor.sensors_lowest_battery_level','sensor.dominiks_iphone_battery_level'] -%}
{% for domain in domains %}
{% set ns.entities = states[domain] %}
{% for sensor in exclude_sensors %}
{% set ns.entities = ns.entities | rejectattr('entity_id', 'equalto', sensor) %}
{% endfor %}
{% set batt_sensors = ns.entities | selectattr('attributes.device_class','equalto','battery') | map(attribute='state') | reject('equalto', 'unknown') | reject('equalto', 'None') | map('int') | reject('equalto', 0) | list %}
{% set batt_attrs = ns.entities | selectattr('attributes.battery_level','defined') | map(attribute='attributes.battery_level') | reject('equalto', 'unknown') | reject('equalto', 'None') | map('int') | reject('equalto', 0) | list %}
{% set batt_lvls = batt_sensors + batt_attrs %}
{% if batt_lvls|length > 0 %}
{% set _min = batt_lvls|min %}
{% if _min < ns.min_batt %}
{% set ns.min_batt = _min %}
{% endif %}
{% endif %}
{% endfor %}
{{ ns.min_batt }}
'';
};
};
}
];
binary_sensor = [
{
platform = "template";
sensors = {
sensor_low_battery = {
value_template = "{{ states('sensor.sensors_lowest_battery_level')|int <= 30 }}";
friendly_name = "A sensor has low battery";
device_class = "problem";
};
};
}
];
alert = {
sensor_low_battery = {
name = "Sensor has low battery!";
message = ''
{% set domains = ['sensor', 'battery'] %}
{% set threshold = 30 %}
{%- set exclude_entities = ['sensor.sensors_lowest_battery_level','sensor.dominiks_iphone_battery_level','sensor.roborock_s8_pro_ultra_battery'] -%}
Sensors are below 50% battery:
{% for domain in domains %}
{% for item in states[domain] %}
{% if item.entity_id not in exclude_entities %}
{% if item.attributes.battery_level is defined %}
{% set level = item.attributes.battery_level|int %}
{% if level > 0 and level < threshold %}
- {{ item.attributes.friendly_name }} ({{ item.attributes['battery_level']|int}}%)
{%- endif -%}
{% endif %}
{% if item.attributes.device_class is defined and item.attributes.device_class == 'battery' %}
{% set level = item.state|int %}
{% if level > 0 and level <= threshold %}
- {{ item.attributes.friendly_name }} ({{ item.state|int }}%)
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}
'';
entity_id = "binary_sensor.sensor_low_battery";
state = "on";
repeat = [
5
60
360
];
skip_first = true;
can_acknowledge = true;
notifiers = [
"NotificationGroup"
];
};
};
};
}

View File

@@ -0,0 +1,254 @@
{ config, pkgs, ... }:
let
domain = "home-assistant.cloonar.com";
release2405 = import <nixos-24.05> { config = config.nixpkgs.config; };
pkgs-with-home-assistant = import (builtins.fetchGit {
name = "new-home-assistant";
url = "https://github.com/nixos/nixpkgs/";
rev = "268bb5090a3c6ac5e1615b38542a868b52ef8088";
}) {};
in
{
users.users.hass = {
home = "/var/lib/hass";
createHome = true;
group = "hass";
uid = config.ids.uids.hass;
extraGroups = [ "dialout" ];
};
users.groups.hass.gid = config.ids.gids.hass;
security.acme.certs."${domain}" = {
group = "nginx";
};
sops.secrets."home-assistant-secrets.yaml" = {
owner = "hass";
restartUnits = [ "container@hass.service" ];
};
sops.secrets."home-assistant-ldap" = {
owner = "hass";
};
containers.hass = {
autoStart = true;
ephemeral = false;
privateNetwork = true;
hostBridge = "server";
hostAddress = "10.42.113.1";
localAddress = "10.42.113.20/24";
extraFlags = [
"--capability=CAP_NET_ADMIN"
];
# allowedDevices = [
# {
# modifier = "rwm";
# node = "char-usb_device";
# }
# {
# modifier = "rwm";
# node = "char-ttyUSB";
# }
# ];
bindMounts = {
# "/dev/ttyUSB0" = {
# hostPath = "/dev/ttyUSB0";
# isReadOnly = false;
# };
"/etc/localtime" = {
hostPath = "/etc/localtime";
};
"/var/lib/hass" = {
hostPath = "/var/lib/hass/";
isReadOnly = false;
};
"/var/lib/acme/hass/" = {
hostPath = "${config.security.acme.certs.${domain}.directory}";
};
"/run/secrets/home-assistant-ldap" = {
hostPath = config.sops.secrets."home-assistant-ldap".path;
};
"/var/lib/hass/secrets.yaml" = {
hostPath = config.sops.secrets."home-assistant-secrets.yaml".path;
};
};
config = { lib, config, pkgs, ... }: {
imports = [
./3dprinter.nix
./ac.nix
# ./aeg.nix
./battery.nix
./electricity.nix
./enocean.nix
./ldap.nix
./light.nix
./locks.nix
./multimedia.nix
./music.nix
./notify.nix
./pc.nix
./pushover.nix
./presense.nix
./roborock.nix
./scene-switch.nix
./shelly.nix
./sleep.nix
./snapcast.nix
];
networking = {
hostName = "home-assistant";
useHostResolvConf = false;
defaultGateway = {
address = "10.42.96.1";
interface = "eth0";
};
firewall.enable = false;
nameservers = [ "10.42.97.1" ];
};
environment.systemPackages = [
pkgs.wol
pkgs.mariadb
];
services.nginx.enable = true;
services.nginx.virtualHosts."${domain}" = {
sslCertificate = "/var/lib/acme/hass/fullchain.pem";
sslCertificateKey = "/var/lib/acme/hass/key.pem";
sslTrustedCertificate = "/var/lib/acme/hass/chain.pem";
forceSSL = true;
extraConfig = ''
proxy_buffering off;
'';
locations."/".extraConfig = ''
proxy_pass http://127.0.0.1:8123;
proxy_set_header Host $host;
proxy_redirect http:// https://;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
'';
};
services.home-assistant = {
package = pkgs-with-home-assistant.home-assistant;
enable = true;
};
services.home-assistant.extraComponents = [
"mobile_app"
"backup"
"denonavr"
"androidtv"
"rainbird"
"zha"
"tplink_omada"
];
services.mysql = {
enable = true;
package = pkgs.mariadb;
ensureDatabases = [ "hass" ];
};
services.mysqlBackup = {
enable = true;
databases = [ "hass" ];
};
services.home-assistant.config =
let
hiddenEntities = [
"sensor.last_boot"
"sensor.date"
];
in
{
recorder = {
db_url = "mysql://hass@localhost/hass?unix_socket=/var/run/mysqld/mysqld.sock";
};
homeassistant = {
name = "Home";
latitude = "!secret home_latitude";
longitude = "!secret home_longitude";
elevation = "!secret home_elevation";
unit_system = "metric";
currency = "EUR";
country = "AT";
time_zone = "Europe/Vienna";
external_url = "https://${domain}";
};
zone = {
name = "Home";
latitude = "!secret home_latitude";
longitude = "!secret home_longitude";
radius = 35;
icon = "mdi:account-multiple";
};
automation = "!include automations.yaml";
frontend = { };
http = {
use_x_forwarded_for = true;
trusted_proxies = [
"127.0.0.1"
"::1"
];
};
api = { };
history.exclude = {
entities = hiddenEntities;
domains = [
"automation"
"updater"
];
};
"map" = { };
enocean = {
device = "/dev/ttyUSB0";
};
# logbook.exclude.entities = "hiddenEntities";
logger = {
default = "info";
};
#icloud = {
# username = "!secret icloud_email";
# password = "!secret icloud_password";
# with_family = true;
#};
network = { };
zeroconf = { };
system_health = { };
default_config = { };
system_log = { };
sensor = [
{
platform = "template";
sensors.bedtime_alarm = {
friendly_name = "Bedtime Alarm";
value_template = "09:00";
};
}
];
};
services.mosquitto = {
enable = true;
listeners = [
{
acl = [ "pattern readwrite #" ];
omitPasswordAuth = true;
settings.allow_anonymous = true;
}
];
};
users.users.hass.extraGroups = [ "dialout" ];
system.stateVersion = "23.05";
};
};
}

View File

@@ -0,0 +1,28 @@
{ config, ... }:
let
unstable = import
(builtins.fetchTarball https://github.com/nixos/nixpkgs/tarball/nixpkgs-unstable)
# reuse the current configuration
{ config = config.nixpkgs.config; };
in {
services.home-assistant.customComponents = with unstable.home-assistant-custom-components; [
epex_spot
];
services.home-assistant.config = {
sensor = [
{
platform = "template";
sensors = {
electricity_price = {
friendly_name = "Current Price of electricity";
unit_of_measurement = "EUR/kWh";
value_template = ''
{{ (((states('sensor.epex_spot_data_price') | int ) / 1000) + (0.0149 + 0.0053 + 0.00866)) | float }}
'';
};
};
}
];
};
}

View File

@@ -0,0 +1,19 @@
{
services.home-assistant.config = {
"binary_sensor pc_0" = [
{
platform = "enocean";
id = [ 254 235 105 198 ];
name = "enocean_switch_pc";
}
];
sensor = [
{
name = "Bathroom HT";
platform = "enocean";
id = [ 5 41 146 251 ];
device_class = "temperature";
}
];
};
}

View File

@@ -0,0 +1,56 @@
{ pkgs
, config
, lib
, ... }:
let
ldap-auth-sh = pkgs.stdenv.mkDerivation {
name = "ldap-auth-sh";
src = pkgs.fetchFromGitHub {
owner = "efficiosoft";
repo = "ldap-auth-sh";
rev = "93b2c00413942908139e37c7432a12bcb705ac87";
sha256 = "1pymp6ki353aqkigr89g7hg5x1mny68m31c3inxf1zr26n5s2kz8";
};
nativeBuildInputs = [ pkgs.makeWrapper ];
installPhase = ''
mkdir -p $out/etc
cat > $out/etc/home-assistant.cfg << 'EOF'
CLIENT="ldapsearch"
SERVER="ldaps://ldap.cloonar.com:636"
USERDN="cn=home-assistant,ou=system,ou=users,dc=cloonar,dc=com"
PW="$(</run/secrets/home-assistant-ldap)"
BASEDN="ou=users,dc=cloonar,dc=com"
SCOPE="one"
FILTER="(&(objectClass=cloonarUser)(memberOf=cn=HomeAssistant,ou=groups,dc=cloonar,dc=com)(mail=$(ldap_dn_escape "$username")))"
USERNAME_PATTERN='^[a-z|A-Z|0-9|_|-|.|@]+$'
on_auth_success() {
# print the meta entries for use in HA
if echo "$output" | grep -qE '^(dn|DN):: '; then
# ldapsearch base64 encodes non-ascii
output=$(echo "$output" | sed -n -e "s/^\(dn\|DN\)\s*::\s*\(.*\)$/\2/p" | base64 -d)
else
output=$(echo "$output" | sed -n -e "s/^\(dn\|DN\)\s*:\s*\(.*\)$/\2/p")
fi
name=$(echo "$output" | sed -nr 's/^cn=([^,]+).*/\1/Ip')
[ -z "$name" ] || echo "name=$name"
}
EOF
install -D -m755 ldap-auth.sh $out/bin/ldap-auth.sh
wrapProgram $out/bin/ldap-auth.sh \
--prefix PATH : ${lib.makeBinPath [pkgs.openldap pkgs.coreutils pkgs.gnused pkgs.gnugrep]} \
--add-flags "$out/etc/home-assistant.cfg"
'';
};
in
{
services.home-assistant.config.homeassistant.auth_providers = [
{
type = "command_line";
command = "${ldap-auth-sh}/bin/ldap-auth.sh";
meta = true;
}
];
}

View File

@@ -0,0 +1,403 @@
{
services.home-assistant.extraComponents = [
"deconz"
"shelly"
"sun"
"nanoleaf"
];
services.home-assistant.config = {
homeassistant = {
customize_domain = {
light = {
assumed_state = false;
};
};
};
"automation light_sunrise" = {
alias = "light_sunrise";
trigger = {
platform = "sun";
event = "sunrise";
};
action = {
service = "light.turn_on";
target = {
entity_id = "{{ states.light | selectattr(\"state\",\"eq\",\"on\") | map(attribute=\"entity_id\") | list }}";
};
data = {
brightness_pct = 254;
color_temp = 250;
};
};
};
"automation light_sunset" = {
alias = "light_sunset";
trigger = {
platform = "sun";
event = "sunset";
};
action = {
service = "light.turn_on";
target = {
entity_id = "{{ states.light | selectattr(\"state\",\"eq\",\"on\") | map(attribute=\"entity_id\") | list }}";
};
data = {
brightness_pct = 30;
color_temp = 450;
};
};
};
"automation light_on" = {
alias = "light_on";
trigger = {
platform = "state";
entity_id = [
"light.bedroom_lights"
"light.kitchen_lights"
"light.livingroom_lights"
"light.hallway_lights"
"light.bathroom_lights"
"light.toilett_lights"
"light.storage_lights"
];
to = "on";
};
action = [
{
choose = [
{
conditions = [ "{{ state_attr('sun.sun', 'elevation') < 5 and trigger.entity_id == 'light.toilett_lights' }}" ];
sequence = [
{
delay = 10;
}
{
service = "light.turn_on";
target = {
entity_id = "{{ trigger.entity_id }}";
};
data = {
brightness_pct = 10;
color_temp = 450;
};
}
];
}
{
conditions = [ "{{ state_attr('sun.sun', 'elevation') < 5 and trigger.entity_id == 'light.hallway_lights' }}" ];
sequence = [
{
delay = 10;
}
{
service = "light.turn_on";
target = {
entity_id = "{{ trigger.entity_id }}";
};
data = {
brightness_pct = 1;
color_temp = 450;
};
}
];
}
{
conditions = [ "{{ state_attr('sun.sun', 'elevation') < 5 and trigger.entity_id == 'light.bathroom_lights' }}" ];
sequence = [
{
delay = 10;
}
{
service = "light.turn_on";
target = {
entity_id = "{{ trigger.entity_id }}";
};
data = {
brightness_pct = 30;
color_temp = 450;
};
}
];
}
{
conditions = [ "{{ state_attr('sun.sun', 'elevation') < 5 and trigger.entity_id == 'light.livingroom_lights' }}" ];
sequence = [
{
delay = 10;
}
{
service = "light.turn_on";
target = {
entity_id = "{{ trigger.entity_id }}";
};
data = {
brightness_pct = 5;
color_temp = 450;
};
}
];
}
{
conditions = [ "{{ state_attr('sun.sun', 'elevation') < 5 and trigger.entity_id == 'light.bedroom_lights' }}" ];
sequence = [
{
delay = 10;
}
{
service = "light.turn_on";
target = {
entity_id = "light.bedroom_lights";
};
data = {
brightness_pct = 5;
color_temp = 450;
};
}
];
}
{
conditions = [ "{{ state_attr('sun.sun', 'elevation') < 5 and trigger.entity_id == 'light.kitchen_lights' }}" ];
sequence = [
{
delay = 10;
}
{
service = "light.turn_on";
target = {
entity_id = "light.kitchen_lights";
};
data = {
brightness_pct = 30;
color_temp = 450;
};
}
];
}
{
conditions = [ "{{ state_attr('sun.sun', 'elevation') > 4 }}" ];
sequence = [
{
delay = 10;
}
{
service = "light.turn_on";
target = {
entity_id = "{{ trigger.entity_id }}";
};
data = {
brightness_pct = 100;
color_temp = 250;
};
}
];
}
];
}
];
};
"automation bathroom light small" = {
alias = "bathroom light small";
mode = "restart";
trigger = {
platform = "state";
entity_id = [
"light.bathroom_switch_channel_1"
];
from = "on";
to = "off";
};
action = [
{
service = "switch.turn_off";
target = {
entity_id = "switch.bathroom_small";
};
}
];
};
"automation bathroom light" = {
alias = "bathroom light";
mode = "restart";
trigger = {
platform = "state";
entity_id = [
"light.bathroom_switch_channel_1"
];
from = "off";
to = "on";
};
action = [
{
delay = 3600;
}
{
service = "light.turn_off";
target = {
entity_id = "light.bathroom_switch_channel_1";
};
}
];
};
"automation bed_led" = {
alias = "bed_led";
mode = "restart";
trigger = {
platform = "state";
entity_id = [
"light.bedroom_led"
];
from = "off";
to = "on";
};
action = [
{
delay = 10800;
}
{
service = "light.turn_off";
target = {
entity_id = "{{ trigger.entity_id }}";
};
}
];
};
"automation hallway_motion" = {
alias = "Hallway Motion";
trigger = {
platform = "state";
entity_id = "binary_sensor.hallway_motion_motion";
};
action = {
service_template = "light.turn_{{ trigger.to_state.state }}";
target = {
entity_id = "light.hallway_lights";
};
};
};
"automation bed_button_1" = {
alias = "bed_button_1";
trigger = {
platform = "event";
event_type = "shelly.click";
event_data = {
device = "shellybutton1-E8DB84AA196D";
};
};
action = [
{
choose = [
{
conditions = [ "{{ trigger.event.data.click_type == \"single\" }}" ];
sequence = [
{
service = "light.toggle";
entity_id = "light.bed_reading_1";
}
];
}
{
conditions = [ "{{ trigger.event.data.click_type == \"double\" }}" ];
sequence = [
{
service = "light.toggle";
entity_id = "light.bedroom_lights";
}
];
}
{
conditions = [ "{{ trigger.event.data.click_type == \"triple\" }}" ];
sequence = [
{
service = "light.toggle";
entity_id = "light.bedroom_bed";
}
];
}
];
}
];
};
"automation bed_button_2" = {
alias = "bed_button_2";
trigger = {
platform = "event";
event_type = "shelly.click";
event_data = {
device = "shellybutton1-E8DB84AA136D";
};
};
action = [
{
choose = [
{
conditions = [ "{{ trigger.event.data.click_type == \"single\" }}" ];
sequence = [
{
service = "light.toggle";
entity_id = "light.bed_reading_2";
}
];
}
{
conditions = [ "{{ trigger.event.data.click_type == \"double\" }}" ];
sequence = [
{
service = "light.toggle";
entity_id = "light.bedroom_lights";
}
];
}
{
conditions = [ "{{ trigger.event.data.click_type == \"triple\" }}" ];
sequence = [
{
service = "light.toggle";
entity_id = "light.bedroom_bed";
}
];
}
];
}
];
};
light = [
{
platform = "switch";
name = "Livingroom Switch";
entity_id = "switch.livingroom_switch";
}
{
platform = "group";
name = "Livingroom Lights";
all = true;
entities = [
"light.livingroom_switch"
"light.living_bulb_1"
"light.living_bulb_2"
"light.living_bulb_3"
"light.living_bulb_4"
"light.living_bulb_5"
"light.living_bulb_6"
];
}
{
platform = "switch";
name = "Bedroom Switch";
entity_id = "switch.bedroom_switch";
}
{
platform = "group";
name = "Bedroom Lights";
all = true;
entities = [
"light.bedroom_switch"
"light.bedroom_bulb_1"
"light.bedroom_bulb_2"
"light.bedroom_bulb_3"
"light.bedroom_bulb_4"
];
}
];
};
}

View File

@@ -0,0 +1,170 @@
{
services.home-assistant.extraComponents = [
"nuki"
];
services.home-assistant.config = {
"automation house_door" = {
alias = "house_door";
mode = "restart";
trigger = {
platform = "state";
entity_id = [
"device_tracker.dominiks_iphone"
];
from = "not_home";
to = "home";
};
action = [
{
service = "lock.unlock";
target = {
entity_id = "lock.house_door";
};
}
{
delay = "00:05:00";
}
{
service = "lock.lock";
target = {
entity_id = "lock.house_door";
};
}
];
};
"automation house_door_ring" = {
alias = "house_door_ring";
trigger = {
platform = "event";
event_type = "nuki_event";
event_data = {
type = "ring";
};
};
action = [
{
choose = [
{
conditions = [ "{{ state.house_door == \"unlocked\" }}" ];
sequence = [
{
service = "lock.lock";
target = {
entity_id = "lock.house_door";
};
}
];
}
{
conditions = [ "{{ state.house_door != \"unlocked\" }}" ];
sequence = [
{
service = "notify.mobile_app_dominiks_iphone";
data = {
message = "Someone is at the door!";
actions = [
{
action = "action_open";
title = "Open house door";
}
{
action = "action_ignore";
title = "Ignore";
}
];
};
}
{
wait_for_trigger = [
{
platform = "event";
event_type = "mobile_app_notification_action";
event_data = {
action = "{{ action_open }}";
};
}
{
platform = "event";
event_type = "mobile_app_notification_action";
event_data = {
action = "{{ action_ignore }}";
};
}
];
}
{
choose = [
{
conditions = "{{ wait.trigger.event.data.action == action_open }}";
sequence = [{
service = "lock.open";
target = {
entity_id = "lock.house_door";
};
}];
}
];
}
];
}
];
}
];
};
binary_sensor = [
{
platform = "template";
sensors = {
lock_critical_battery = {
value_template = ''
{% set domains = ['lock'] %}
{% set ns = namespace(crit=battery_critical, entities=[]) %}
{% for domain in domains %}
{% set batt_critical = states[domain] | selectattr('attributes.battery_critical','defined') | map(attribute='attributes.battery_critical') | reject('equalto', 'unknown') | reject('equalto', 'None') | map('int') | reject('equalto', 0) | list %}
{% if batt_critical|length > 0 %}
{% set ns.battery_critical = true %}
{% endif %}
{% endfor %}
{{ ns.battery_critical }}
'';
friendly_name = "A lock has critical battery";
device_class = "problem";
};
};
}
];
alert = {
battery_critical = {
name = "Lock has low battery!";
message = ''
{%- set domains = ['lock'] -%}
Lock battery is critical:
{%- for domain in domains -%}
{%- for item in states[domain] -%}
{%- if item.attributes.battery_critical is defined -%}
{% if item.attributes.battery_critical %}
- {{ item.attributes.friendly_name }}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- endfor -%}
'';
entity_id = "binary_sensor.lock_critical_battery";
state = "on";
repeat = [
5
60
360
];
skip_first = true;
can_acknowledge = true;
notifiers = [
"NotificationGroup"
];
};
};
};
}

View File

@@ -0,0 +1,436 @@
{
services.home-assistant.extraComponents = [
"ping"
"broadlink"
"androidtv"
"samsungtv"
];
services.home-assistant.config = {
ios = {
actions = [
{
name = "Home Cinema";
label.text = "Home Cinema";
icon = {
icon = "theater";
color = "#ffffff";
};
show_in_watch = true;
}
];
};
binary_sensor = [
{
name = "xbox";
platform = "ping";
host = "xbox.cloonar.multimedia";
count = 2;
scan_interval = 5;
}
{
name = "ps5";
platform = "ping";
host = "ps5.cloonar.multimedia";
count = 2;
scan_interval = 5;
}
{
name = "steamdeck";
platform = "ping";
host = "steamdeck.cloonar.com";
count = 2;
scan_interval = 5;
}
{
platform = "template";
sensors = {
multimedia_device_on = {
friendly_name = "Any multimedia device on";
device_class = "connectivity";
value_template = ''
{% if is_state('binary_sensor.ps5', 'on') or is_state('binary_sensor.xbox', 'on') or (states('media_player.fire_tv_firetv_living_cloonar_multimedia') != 'off' and states('media_player.fire_tv_firetv_living_cloonar_multimedia') != 'unavailable') or (is_state('binary_sensor.steamdeck', 'on') and (states('sensor.steamdeck_power') | float > 5)) %}
on
{% else %}
off
{% endif %}
'';
};
};
}
];
script = {
turn_on_tv = {
sequence = [
{
choose = [
{
conditions = [
{
condition = "state";
entity_id = "media_player.android_tv_metz_cloonar_multimedia";
state = "unavailable";
}
];
sequence = [
{
service = "remote.send_command";
target = {
entity_id = "remote.rmproplus";
};
data = {
num_repeats = 1;
delay_secs = 0.4;
hold_secs = 0;
command = "b64:JgBOAJaSFREVNRU2FTUVERURFRAVERURFTUVNhU1FREVERUQFREVERUQFTYVNRURFREVEBURFTYVNRURFRAVNhU1FTYVNRUABfmWkhURFQANBQAAAAAAAAAAAAA=";
};
}
];
}
{
conditions = [
{
condition = "state";
entity_id = "media_player.android_tv_metz_cloonar_multimedia";
state = "off";
}
];
sequence = [
{
service = "media_player.turn_on";
target = {
entity_id = "media_player.android_tv_metz_cloonar_multimedia";
};
}
];
}
];
}
];
};
};
"automation steamdeck on" = {
alias = "steamdeck on";
trigger = {
platform = "template";
value_template = "{% if is_state('binary_sensor.steamdeck', 'on') and (states('sensor.steamdeck_power') | float > 5) %}true{% endif %}";
};
action = [
{
service = "denonavr.get_command";
target = {
entity_id = "media_player.marantz_sr6015";
};
data = {
command = "/goform/formiPhoneAppDirect.xml?SIDVD";
};
}
];
};
"automation xbox on" = {
alias = "xbox on";
trigger = {
platform = "state";
entity_id = "binary_sensor.xbox";
to = "on";
};
action = [
{
service = "denonavr.get_command";
target = {
entity_id = "media_player.marantz_sr6015";
};
data = {
command = "/goform/formiPhoneAppDirect.xml?SIGAME";
};
}
];
};
"automation firetv on" = {
alias = "firetv on";
trigger = {
platform = "state";
entity_id = "media_player.fire_tv_firetv_living_cloonar_multimedia";
from = "off";
};
action = [
{
service = "denonavr.get_command";
target = {
entity_id = "media_player.marantz_sr6015";
};
data = {
command = "/goform/formiPhoneAppDirect.xml?SIMPLAY";
};
}
];
};
"automation ps5 on" = {
alias = "ps5 on";
trigger = {
platform = "state";
entity_id = "binary_sensor.ps5";
to = "on";
};
action = [
{
service = "denonavr.get_command";
target = {
entity_id = "media_player.marantz_sr6015";
};
data = {
command = "/goform/formiPhoneAppDirect.xml?SIBD";
};
}
];
};
"automation all multimedia off" = {
alias = "all multimedia off";
trigger = {
platform = "state";
entity_id = "binary_sensor.multimedia_device_on";
to = "off";
};
action = [
{
service = "media_player.turn_off";
target = {
entity_id = "media_player.android_tv_metz_cloonar_multimedia";
};
}
{
service = "denonavr.get_command";
target = {
entity_id = "media_player.marantz_sr6015";
};
data = {
command = "/goform/formiPhoneAppDirect.xml?PWSTANDBY";
};
}
# silverscreen up
{
service = "remote.send_command";
target = {
entity_id = "remote.rmproplus";
};
data = {
num_repeats = 2;
delay_secs = 1;
hold_secs = 0;
command = "b64:sgBqAgkaBBoJCRsJHBoKGgoJGgQaCQkaBAgbGwoIHAgcGwkJGwgAARkbCRsJGwkJGgQaCgkaBAgbCRsbCQkbGwkJGgQIGxwJGwkJGxsJCRwIHBoKCBsECBsbCAQIGwkAARgbChoKGgoJGxsJCRoECBsJHBsJCRoEGgkJGwkcGgobCQkbGwkJGwkbGwoIHAkbGwkJGwkAARgbCRsJGwoIGxwJCRsJGwkbGwoIGxwIChoKGhwJGwkJHBsJCRsJGxsJCRsJHBsJCRsJAAEYGwkbCRsKCBscCQkbCRsJGxsJCRwbCQkbCRsbCRsJCRscCQgcCRocCQkbCRsbCQobCQABGBsJGwkbCQkbHAkJGwkbCRsbCQkbGwoJGwkbGwkbCQkbGwoIHAkbGwkJGgobGwkKGwkAARccCRsJGwkJHBsJCRsJGwkbGwkJGxsKCRsIHBsJGwkJGxsKCRoJGxwJCRsJGxsJChsIAAEZGwgcCRsJCRscCQkbCRsJGhwJCRscCQkaChsbCRsJCRscCQgcCRocCQkbCRsbCggcCQABGBsJGwkbCggcGwkJGwkbCRsbCggcGgoJGwkbGwkbCggcGwkJGwkbGwkJHAgcGwkJGwkAARgbChoKGgoJGhwJCRsJGwkcGgoJGxsJCRsJGxsJHAkJGxsJCRsJGhwJCRwJGhwJCRsJAAEYGwoaChsJCRsbCQkaChsJGxwJCRsbCQkbCRsbChsJCRsbCQkbCRsbCgkbCRsbCQkcCAABFwQaChsJGwkJGxsKCBwIHAgcGwkJGxsKCBwIGwQaCRsJCRwaCggcCBwbCQkbCRwaCggcCAAF3AAAAAAAAAAAAAAAAAAA";
};
}
# turn off beamer
{
service = "remote.send_command";
target = {
entity_id = "remote.rmproplus";
};
data = {
num_repeats = 2;
delay_secs = 1;
hold_secs = 0;
command = "b64:JgDaAAABKZMUERMSExITEhMSExETEhMSExITEhMSExETNxQ2ExITEhMSEzcTNxM3ExITEhM3ExITNxMSEhITEhM3EzcTEhM3EwAFyAABKJQUERMSEhITEhMSExITEhMSEhITEhMSExITNxM3ExITEhMREzcTNxQ3EhITEhM3ExITNxMSExITEhM3EzcTEhM3EwAFyAABKJQUERMSExETEhMSExITEhMSExETEhMSExITNxM3ExITEhMREzcTOBI4ExETEhM3ExITNxMSExITEhM3EzcTEhM3E5IGAA0FAAAAAAAAAAAAAAAAAAA=";
};
}
];
};
"automation all_multimedia_on" = {
alias = "all multimedia on";
trigger = {
platform = "state";
entity_id = "binary_sensor.multimedia_device_on";
to = "on";
};
action = [
{
service = "script.turn_on";
target = {
entity_id = "script.turn_on_tv";
};
}
{
delay = 5;
}
{
service = "androidtv.adb_command";
target = {
entity_id = "media_player.android_tv_metz_cloonar_multimedia";
};
data = {
command = "adb shell am start -a android.intent.action.VIEW -d content://android.media.tv/passthrough/com.mediatek.tvinput%2F.hdmi.HDMIInputService%2FHDMI100004";
};
}
];
};
"automation bedroom tv off" = {
alias = "bedroom tv off";
trigger = {
platform = "state";
entity_id = "media_player.fire_tv_firetv_bedroom_cloonar_multimedia";
to = "off";
};
action = [
{
service = "media_player.turn_off";
target = {
entity_id = "media_player.samsung_7_series_55";
};
}
];
};
"automation multimedia scene switch" = {
alias = "multimedia scene switch";
trigger = [
{
platform = "event";
event_type = "button_pressed";
event_data = {
id = [ 254 235 105 198 ];
onoff = 1;
};
}
{
platform = "event";
event_type = "ios.action_fired";
event_data = {
actionID = "Home Cinema";
};
}
];
condition = {
condition = "state";
entity_id = "binary_sensor.multimedia_device_on";
state = "on";
};
action = [
{
choose = [
{
conditions = [
{
condition = "or";
conditions = [
{
condition = "state";
entity_id = "media_player.android_tv_metz_cloonar_multimedia";
state = "on";
}
{
condition = "state";
entity_id = "media_player.android_tv_metz_cloonar_multimedia";
state = "idle";
}
];
}
];
sequence = [
# silver screen down
{
service = "remote.send_command";
target = {
entity_id = "remote.rmproplus";
};
data = {
num_repeats = 2;
delay_secs = 1;
hold_secs = 0;
command = "b64:sQs0AB0JCxsLGx0IHQgLGh0ICxoLGx0JCxodCQobCxoLAAEXHQgdCR0JCxodCQsbCxsLGx0JCxoAAAAA";
};
}
# turn on beamer
{
service = "remote.send_command";
target = {
entity_id = "remote.rmproplus";
};
data = {
num_repeats = 1;
delay_secs = 0.4;
hold_secs = 0;
command = "b64:JgAgAQABKZMUERMSExETEhMSExITEhMSExETEhMSExITNxM3ExITERM3EzgSOBM3ExETEhM3ExITEhMSExITERM3EzcTEhM3EwAFyAABKZMTEhMRExITEhMSExITEhMRExITEhMSExITNxM3ExITERM3EzcTNxM3ExITEhM3ExITEhMSExETEhM3EzcTEhM3EwAFyAABKZMUERMRExITEhMSExITERMSExITEhMSExITNxM3ExISEhM3EzcTNxM3ExITEhM3ExITEhMSExETEhM3EzcTEhM3EwAFxwABKZQUERMRFBETEhMSExITEhISExITEhMSExITNxM3ExITERM3EzcTNxM3FBETEhM3ExITEhMSExITERM3EzcTEhM3EwANBQAAAAAAAAAA";
};
}
{
service = "media_player.turn_off";
target = {
entity_id = "media_player.android_tv_metz_cloonar_multimedia";
};
}
{
service = "media_player.turn_on";
target = {
entity_id = "media_player.marantz_sr6015";
};
}
];
}
{
conditions = [
{
condition = "or";
conditions = [
{
condition = "state";
entity_id = "media_player.android_tv_metz_cloonar_multimedia";
state = "off";
}
{
condition = "state";
entity_id = "media_player.android_tv_metz_cloonar_multimedia";
state = "unavailable";
}
];
}
];
sequence = [
{
service = "remote.send_command";
target = {
entity_id = "remote.rmproplus";
};
data = {
num_repeats = 2;
delay_secs = 1;
hold_secs = 0;
command = "b64:sgBqAgkaBBoJCRsJHBoKGgoJGgQaCQkaBAgbGwoIHAgcGwkJGwgAARkbCRsJGwkJGgQaCgkaBAgbCRsbCQkbGwkJGgQIGxwJGwkJGxsJCRwIHBoKCBsECBsbCAQIGwkAARgbChoKGgoJGxsJCRoECBsJHBsJCRoEGgkJGwkcGgobCQkbGwkJGwkbGwoIHAkbGwkJGwkAARgbCRsJGwoIGxwJCRsJGwkbGwoIGxwIChoKGhwJGwkJHBsJCRsJGxsJCRsJHBsJCRsJAAEYGwkbCRsKCBscCQkbCRsJGxsJCRwbCQkbCRsbCRsJCRscCQgcCRocCQkbCRsbCQobCQABGBsJGwkbCQkbHAkJGwkbCRsbCQkbGwoJGwkbGwkbCQkbGwoIHAkbGwkJGgobGwkKGwkAARccCRsJGwkJHBsJCRsJGwkbGwkJGxsKCRsIHBsJGwkJGxsKCRoJGxwJCRsJGxsJChsIAAEZGwgcCRsJCRscCQkbCRsJGhwJCRscCQkaChsbCRsJCRscCQgcCRocCQkbCRsbCggcCQABGBsJGwkbCggcGwkJGwkbCRsbCggcGgoJGwkbGwkbCggcGwkJGwkbGwkJHAgcGwkJGwkAARgbChoKGgoJGhwJCRsJGwkcGgoJGxsJCRsJGxsJHAkJGxsJCRsJGhwJCRwJGhwJCRsJAAEYGwoaChsJCRsbCQkaChsJGxwJCRsbCQkbCRsbChsJCRsbCQkbCRsbCgkbCRsbCQkcCAABFwQaChsJGwkJGxsKCBwIHAgcGwkJGxsKCBwIGwQaCRsJCRwaCggcCBwbCQkbCRwaCggcCAAF3AAAAAAAAAAAAAAAAAAA";
};
}
# turn off beamer
{
service = "remote.send_command";
target = {
entity_id = "remote.rmproplus";
};
data = {
num_repeats = 2;
delay_secs = 1;
hold_secs = 0;
command = "b64:JgDaAAABKZMUERMSExITEhMSExETEhMSExITEhMSExETNxQ2ExITEhMSEzcTNxM3ExITEhM3ExITNxMSEhITEhM3EzcTEhM3EwAFyAABKJQUERMSEhITEhMSExITEhMSEhITEhMSExITNxM3ExITEhMREzcTNxQ3EhITEhM3ExITNxMSExITEhM3EzcTEhM3EwAFyAABKJQUERMSExETEhMSExITEhMSExETEhMSExITNxM3ExITEhMREzcTOBI4ExETEhM3ExITNxMSExITEhM3EzcTEhM3E5IGAA0FAAAAAAAAAAAAAAAAAAA=";
};
}
{
service = "script.turn_on";
target = {
entity_id = "script.turn_on_tv";
};
}
];
}
];
}
{
delay = 5;
}
{
service = "androidtv.adb_command";
target = {
entity_id = "media_player.android_tv_metz_cloonar_multimedia";
};
data = {
command = "adb shell am start -a android.intent.action.VIEW -d content://android.media.tv/passthrough/com.mediatek.tvinput%2F.hdmi.HDMIInputService%2FHDMI100004";
};
}
];
};
};
}

View File

@@ -0,0 +1,22 @@
{
services.home-assistant.config = {
"automation toilet music" = {
alias = "toilet music";
trigger = {
platform = "state";
entity_id = "light.toilett_lights";
};
action = [
{
service = "media_player.volume_mute";
target = {
entity_id = "media_player.music_toilet_snapcast_client";
};
data = {
is_volume_muted = "{{ trigger.to_state.state == 'off' }}";
};
}
];
};
};
}

View File

@@ -0,0 +1,15 @@
{
services.home-assistant.config = {
notify = [
{
name = "NotificationGroup";
platform = "group";
services = [
{
service = "pushover_dominik";
}
];
}
];
};
}

View File

@@ -0,0 +1,71 @@
{
services.home-assistant.extraComponents = [
"wake_on_lan"
];
services.home-assistant.config = {
ios = {
actions = [
{
name = "Turn on PC";
label.text = "Turn on PC";
icon = {
icon = "controller";
color = "#ffffff";
};
show_in_watch = true;
}
];
};
wake_on_lan = {};
"automation pc_switch" = {
alias = "switch pc";
trigger = {
platform = "event";
event_type = "button_pressed";
event_data = {
id = [ 254 235 105 198 ];
onoff = 0;
};
};
action = {
service = "script.turn_on";
target = {
entity_id = "script.turn_on_pc";
};
};
};
script = {
turn_on_pc = {
sequence = [
{
service = "wake_on_lan.send_magic_packet";
data = {
mac = "04:7c:16:d5:63:5e";
broadcast_address = "10.42.96.5";
broadcast_port = 9;
};
}
];
};
};
"automation turn on pc" = {
trigger = [
{
platform = "event";
event_type = "ios.action_fired";
event_data = {
actionID = "Turn on PC";
};
}
];
action = [
{
service = "script.turn_on";
target = {
entity_id = "script.turn_on_pc";
};
}
];
};
};
}

View File

@@ -0,0 +1,138 @@
{
services.home-assistant.extraComponents = [
"daikin"
"enocean"
];
services.home-assistant.config = {
"automation presense kitchen" = {
alias = "presense kitchen";
mode = "restart";
trigger = {
platform = "state";
entity_id = [
"sensor.presense_kitchen"
];
};
action = [
{
choose = [
{
conditions = [ "{{ trigger.to_state.state == \"\" }}" ];
sequence = [
];
}
{
conditions = [ "{{ trigger.to_state.state != \"\" }}" ];
sequence = [
{
service = "light.turn_on";
entity_id = "light.kitchen_lights";
}
];
}
];
}
];
};
sensor = [
{
platform = "mqtt_room";
device_id = "dominiksiphone";
name = "Dominiks iPhone";
state_topic = "espresense/devices/dominiksiphone";
timeout = 10;
away_timeout = 120;
}
{
platform = "template";
sensors = {
presense_devices = {
friendly_name = "Presense Devices";
value_template = "dominiks_iphone";
};
};
}
{
platform = "template";
sensors = {
presense_livingroom = {
friendly_name = "Presense Livingroom";
value_template = ''
{% set room = "livingroom" %}
{% set presense = namespace(list=[]) %}
{% set presense_list = [] %}
{% set device_list = states('sensor.presense_devices').split(',') %}
{% for device in device_list %}
{% if is_state('sensor.' + device, room) %}
{% set presense.list = presense.list + [device] %}
{% endif %}
{% endfor %}
{{ presense.list | join("") }}
'';
};
};
}
{
platform = "template";
sensors = {
presense_kitchen = {
friendly_name = "Presense Kitchen";
value_template = ''
{% set room = "kitchen" %}
{% set presense = namespace(list=[]) %}
{% set presense_list = [] %}
{% set device_list = states('sensor.presense_devices').split(',') %}
{% for device in device_list %}
{% if is_state('sensor.' + device, room) %}
{% set presense.list = presense.list + [device] %}
{% endif %}
{% endfor %}
{{ presense.list | join("") }}
'';
};
};
}
{
platform = "template";
sensors = {
presense_hallway = {
friendly_name = "Presense Hallway";
value_template = ''
{% set room = "hallway" %}
{% set presense = namespace(list=[]) %}
{% set presense_list = [] %}
{% set device_list = states('sensor.presense_devices').split(',') %}
{% for device in device_list %}
{% if is_state('sensor.' + device, room) %}
{% set presense.list = presense.list + [device] %}
{% endif %}
{% endfor %}
{{ presense.list | join("") }}
'';
};
};
}
{
platform = "template";
sensors = {
presense_bedroom = {
friendly_name = "Presense Bedroom";
value_template = ''
{% set room = "bedroom" %}
{% set presense = namespace(list=[]) %}
{% set presense_list = [] %}
{% set device_list = states('sensor.presense_devices').split(',') %}
{% for device in device_list %}
{% if is_state('sensor.' + device, room) %}
{% set presense.list = presense.list + [device] %}
{% endif %}
{% endfor %}
{{ presense.list | join("") }}
'';
};
};
}
];
};
}

View File

@@ -0,0 +1,16 @@
{
services.home-assistant.extraComponents = [
"pushover"
];
# services.home-assistant.config = {
# notify = [
# {
# name = "pushover_dominik";
# platform = "pushover";
# api_key = "!secret pushover_dominik_api_key";
# user_key = "!secret pushover_dominik_user_key";
# }
# ];
# };
}

View File

@@ -0,0 +1,27 @@
{
services.home-assistant.extraComponents = [
"roborock"
];
services.home-assistant.config = {
"automation roborock" = {
alias = "roborock";
trigger = {
platform = "state";
entity_id = [
"person.dominik"
];
from = "home";
to = "not_home";
};
action = [
{
service = "vacuum.start";
target = {
device_id = "136c307ff46cd968d08e9f9d20886755";
};
}
];
};
};
}

View File

@@ -0,0 +1,20 @@
{
services.home-assistant.config = {
"automation scene_switch" = {
alias = "switch scene";
trigger = {
platform = "event";
event_type = "button_pressed";
event_data = {
id = [ 254 242 234 134 ];
};
};
action = {
service_template = "switch.turn_on";
data_template = {
entity_id = "switch.computer";
};
};
};
};
}

View File

@@ -0,0 +1,113 @@
{ lib, ... }:
let
colorbulbs = [
{ name = "Living Bulb 1"; id = "34945479BC57"; }
{ name = "Living Bulb 2"; id = "485519D9A1B2"; }
{ name = "Living Bulb 3"; id = "485519D9AE95"; }
{ name = "Living Bulb 4"; id = "485519D94A28"; }
{ name = "Living Bulb 5"; id = "485519DA6B6A"; }
{ name = "Living Bulb 6"; id = "485519D9E018"; }
{ name = "Bedroom Bulb 1"; id = "08f9e06f4eb4"; }
{ name = "Bedroom Bulb 2"; id = "485519ee0ed9"; }
{ name = "Bedroom Bulb 3"; id = "08f9e06fe779"; }
{ name = "Bedroom Bulb 4"; id = "485519ee00a0"; }
];
switches = [];
proswitches = [
{ name = "Livingroom Switch"; id = "shellyplus2pm-e86beae5d5d8"; relay = "0"; }
{ name = "Kitchen Switch"; id = "shellyplus2pm-e86beae5d5d8"; relay = "1"; }
{ name = "Bedroom Switch"; id = "shelly1pmminig3-34b7da933fe0"; relay = "0"; }
{ name = "Hallway Circuit"; id = "shellypro3-c8f09e894448"; relay = "0"; }
{ name = "Bathroom Circuit"; id = "shellypro3-c8f09e894448"; relay = "1"; }
{ name = "Kitchen Circuit"; id = "shellypro3-c8f09e894448"; relay = "2"; }
];
in {
services.home-assistant.extraComponents = [
"shelly"
];
services.home-assistant.config = {
mqtt = {
switch = builtins.concatLists [
(builtins.map (switch:
let
unique_id = builtins.replaceStrings [" "] ["_"] switch.name;
in {
name = switch.name;
unique_id = unique_id;
state_topic = "shellies/shellyswitch25-${switch.id}/relay/${switch.relay}";
command_topic = "shellies/shellyswitch25-${switch.id}/relay/${switch.relay}/command";
payload_on = "on";
payload_off = "off";
}
) switches)
(builtins.map (switch:
let
unique_id = builtins.replaceStrings [" "] ["_"] switch.name;
in {
name = switch.name;
unique_id = unique_id;
state_topic = "shellies/${switch.id}/status/switch:${switch.relay}";
value_template = "{{ value_json.output }}";
state_on = true;
state_off = false;
command_topic = "shellies/${switch.id}/rpc";
payload_on = "{\"id\":${switch.relay}, \"src\":\"homeassistant\", \"method\":\"Switch.Set\", \"params\":{\"id\":${switch.relay}, \"on\":true}}";
payload_off = "{\"id\":${switch.relay}, \"src\":\"homeassistant\", \"method\":\"Switch.Set\", \"params\":{\"id\":${switch.relay}, \"on\":false}}";
availability_topic = "shellies/${switch.id}/online";
payload_available = "true";
payload_not_available = "false";
}
) proswitches)
];
light = builtins.map (bulb:
let
unique_id = builtins.replaceStrings [" "] ["_"] bulb.name;
in {
name = bulb.name;
unique_id = "${unique_id}";
schema = "template";
state_topic = "shellies/shellycolorbulb-${bulb.id}/color/0/status";
state_template = "{% if value_json.ison %}on{% else %}off{% endif %}";
command_topic = "shellies/shellycolorbulb-${bulb.id}/color/0/set";
command_on_template = ''
{
"turn": "on",
"effect": 0,
{%- if red is defined and green is defined and blue is defined -%}
"mode": "color",
"red": {{ red }},
"green": {{ green }},
"blue": {{ blue }},
{%- endif -%}
{%- if brightness is defined -%}
"gain": {{brightness | float | multiply(0.3922) | round(0)}},
"brightness": {{brightness | float | multiply(0.3922) | round(0)}},
{%- endif -%}
{% if color_temp is defined %}
"mode": "white",
"temp":{{ (1/(color_temp | float)) | multiply(1000000) | round(0) }},
{% endif %}
}
'';
command_off_template = ''
{
"turn": "off"
}
'';
brightness_template = "{{ value_json.brightness | float | multiply(2.55) | round(0) }}";
color_temp_template = "{{ 1000000 | multiply(1/(value_json.temp | float)) | round(0) }}";
red_template = "{{ value_json.red }}";
green_template = "{{ value_json.green }}";
blue_template = "{{ value_json.blue }}";
max_mireds = 333;
min_mireds = 154;
}) colorbulbs;
};
};
}

View File

@@ -0,0 +1,74 @@
{
services.home-assistant.config = {
"automation wakeup" = {
alias = "wakeup";
trigger = {
platform = "time";
at = "input_datetime.wakeup";
};
action = [
{
service = "switch.turn_on";
entity_id = "switch.coffee";
}
{
delay = 1700;
}
{
service = "switch.turn_on";
entity_id = "switch.78_8c_b5_fe_41_62_port_2_poe"; # livingroom
}
{
service = "switch.turn_on";
entity_id = "switch.78_8c_b5_fe_41_62_port_3_poe"; # garden
}
];
};
"automation sleep" = {
alias = "sleep";
trigger = [
{
platform = "event";
event_type = "shelly.click";
event_data = {
device = "shellybutton1-E8DB84AA196D";
};
}
{
platform = "event";
event_type = "shelly.click";
event_data = {
device = "shellybutton1-E8DB84AA136D";
};
}
];
action = [
{
choose = [
{
conditions = [ "{{ trigger.event.data.click_type == \"long\" }}" ];
sequence = [
{
service = "light.turn_off";
entity_id = "all";
}
{
service = "switch.turn_off";
entity_id = "switch.coffee";
}
{
service = "switch.turn_off";
entity_id = "switch.78_8c_b5_fe_41_62_port_2_poe";
}
{
service = "switch.turn_off";
entity_id = "switch.78_8c_b5_fe_41_62_port_3_poe";
}
];
}
];
}
];
};
};
}

View File

@@ -0,0 +1,31 @@
{
services.home-assistant = {
extraComponents = [ "snapcast" ];
config = {
"automation piano" = {
alias = "piano";
trigger = {
platform = "state";
entity_id = "media_player.music_piano_snapcast_client";
attribute = "is_volume_muted";
};
condition = [
{
condition = "template";
value_template = "{{ trigger.from_state.state != 'unavailable' }}";
}
{
condition = "template";
value_template = "{{ state_attr('media_player.music_piano_snapcast_client', 'is_volume_muted') == true or state_attr('media_player.music_piano_snapcast_client', 'is_volume_muted') == false }}";
}
];
action = {
service = "switch.turn_on";
target = {
entity_id = "switch.piano_switch_power";
};
};
};
};
};
}

View File

@@ -0,0 +1,8 @@
{ nixpkgs, ...}:
{
imports = [ (builtins.fetchGit {
url = "https://github.com/astro/microvm.nix";
} + "/nixos-modules/host") ];
systemd.network.networks."31-server".matchConfig.Name = [ "vm-*" ];
}

View File

@@ -0,0 +1,59 @@
{ pkgs, lib, ... }:
let
mopidy-autoplay = pkgs.python3Packages.buildPythonApplication rec {
pname = "Mopidy-Autoplay";
version = "0.2.3";
src = pkgs.python3Packages.fetchPypi {
inherit pname version;
sha256 = "sha256-E2Q+Cn2LWSbfoT/gFzUfChwl67Mv17uKmX2woFz/3YM=";
};
propagatedBuildInputs = [
pkgs.mopidy
] ++ (with pkgs.python3Packages; [
configobj
]);
# no tests implemented
doCheck = false;
meta = with lib; {
homepage = "https://codeberg.org/sph/mopidy-autoplay";
};
};
in
{
services.mopidy = {
enable = true;
extensionPackages = [ pkgs.mopidy-iris pkgs.mopidy-tunein mopidy-autoplay ];
configuration = ''
[audio]
output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! filesink location=/run/snapserver/mopidy
[file]
enabled = false
[autoplay]
enabled = true
'';
};
services.nginx.virtualHosts."mopidy.cloonar.com" = {
forceSSL = true;
enableACME = true;
acmeRoot = null;
extraConfig = ''
proxy_buffering off;
'';
locations."/".extraConfig = ''
proxy_pass http://127.0.0.1:6680;
proxy_set_header Host $host;
proxy_redirect http:// https://;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
'';
};
}

View File

@@ -0,0 +1,36 @@
{ config, pkgs, ... }:
{
services.mosquitto = {
enable = true;
listeners = [
{
users."espresense" = {
password = "insecure-password";
acl = [ "readwrite espresense/#" ];
};
users."home-assistant" = {
hashedPassword = "$7$101$7uaagoQWQ3ICJ/wg$5cWZs4ae4DjToe44bOzpDopPv1kRaaVD+zF6BE64yDJH2/MBqXfD6f2/o9M/65ArhV92DAK+txXRYsEcZLl45A==";
acl = [ "readwrite #" ];
};
users."ps5-mqtt" = {
password = "insecure-password";
acl = [ "readwrite #" ];
};
users."shairport-mqtt" = {
password = "insecure-password";
acl = [ "readwrite #" ];
};
users."shelly" = {
password = "password";
acl = [ "readwrite shellies/#" ];
};
}
];
};
# networking.firewall = {
# allowedTCPPorts = [ 1883 ];
# };
}

View File

@@ -0,0 +1,110 @@
{ ... }: {
boot.kernel.sysctl = {
# if you use ipv4, this is all you need
"net.ipv4.conf.all.forwarding" = true;
# If you want to use it for ipv6
"net.ipv6.conf.all.forwarding" = false;
};
systemd.network = {
enable = true;
wait-online.anyInterface = true;
links = {
"10-wan" = {
matchConfig.PermanentMACAddress = "c0:74:2b:fd:9a:7f";
linkConfig.Name = "wan";
};
};
netdevs = {
"30-server".netdevConfig = {
Kind = "bridge";
Name = "server";
};
};
networks = {
"31-server" = {
matchConfig.Name = [ "vserver" ];
# Attach to the bridge that was configured above
networkConfig.Bridge = "server";
};
};
};
networking = {
useDHCP = false;
# Define VLANS
nameservers = [ "10.42.97.1" ];
# resolvconf.enable = false;
vlans = {
lan = {
id = 95;
interface = "enP3p49s0";
};
vserver = {
id = 97;
interface = "enP3p49s0";
};
multimedia = {
id = 98;
interface = "enP3p49s0";
};
smart = {
id = 99;
interface = "enP3p49s0";
};
infrastructure = {
id = 100;
interface = "enP3p49s0";
};
guest = {
id = 111;
interface = "enP3p49s0";
};
};
interfaces = {
# Don't request DHCP on the physical interfaces
lan.useDHCP = false;
enP3p49s0.useDHCP = false;
# Handle the VLANs
wan.useDHCP = true;
lan = {
ipv4.addresses = [{
address = "10.42.95.1";
prefixLength = 24;
}];
};
server = {
ipv4.addresses = [{
address = "10.42.97.1";
prefixLength = 24;
}];
};
multimedia = {
ipv4.addresses = [{
address = "10.42.98.1";
prefixLength = 24;
}];
};
smart = {
ipv4.addresses = [{
address = "10.42.99.1";
prefixLength = 24;
}];
};
infrastructure = {
ipv4.addresses = [{
address = "10.42.100.1";
prefixLength = 24;
}];
};
guest = {
ipv4.addresses = [{
address = "10.42.111.1";
prefixLength = 24;
}];
};
};
};
}

View File

@@ -0,0 +1,29 @@
{ config, pkgs, ... }: {
users.users.omada = {
isSystemUser = true;
group = "omada";
home = "/var/lib/omada";
createHome = true;
};
users.groups.omada = { };
users.groups.docker.members = [ "omada" ];
# TODO: check if we can run docker service as other user than root
virtualisation = {
oci-containers.containers = {
omada = {
autoStart = false;
image = "mbentley/omada-controller:5.14.26.1";
volumes = [
"/var/lib/omada/data:/opt/tplink/EAPController/data"
"/var/lib/omada/logs:/opt/tplink/EAPController/logs"
];
extraOptions = [
"--network=server"
"--mac-address=1a:c4:04:6e:29:bd"
"--ip=10.42.97.2"
];
};
};
};
}

View File

@@ -0,0 +1,41 @@
{ config, pkgs, stdenv, ... }:
let
vpnc = pkgs.writeShellScript "vpnc" ''
export INTERNAL_IP4_DNS=
. ${pkgs.vpnc-scripts}/bin/vpnc-script
'';
in
{
sops.secrets.wrwks_vpn_key = {};
networking.openconnect.interfaces = {
wrwks = {
gateway = "vpn.wrwks.at";
passwordFile = config.sops.secrets.wrwks_vpn_key.path;
protocol = "anyconnect";
user = "exdpolakovics@wrwks.local";
extraOptions = {
authgroup = "WRWKS-SSL-VPN-Service";
script = "${vpnc}";
};
};
};
systemd.services.openconnect-wrwks-keepalive = {
serviceConfig.Type = "oneshot";
path = with pkgs; [ bash inetutils ];
script = ''
ping -c 2 stage.wsw.at
'';
};
systemd.timers.openconnect-wrwks-keepalive = {
wantedBy = [ "timers.target" ];
partOf = [ "openconnect-wrwks-keepalive.service" ];
timerConfig = {
OnCalendar = "*:0/1";
Unit = "openconnect-wrwks-keepalive.service";
};
};
}

View File

@@ -0,0 +1,44 @@
{ config, pkgs, ... }:
{
virtualisation.oci-containers.backend = "podman";
virtualisation.oci-containers.containers = {
palworld = {
image = "thijsvanloef/palworld-server-docker:latest";
autoStart = false;
ports = [
"8211:8211/udp"
"27015:27015/udp"
];
environmentFiles = [
config.sops.secrets.palworld.path
];
volumes = [
"/var/lib/palworld/:/palworld/"
];
};
};
systemd.timers."restart-palworld" = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "*-*-* 3:00:00";
Unit = "restart-palworld.service";
};
};
systemd.services."restart-palworld" = {
script = ''
set -eu
if ${pkgs.systemd}/bin/systemctl is-active --quiet podman-palworld.service; then
${pkgs.systemd}/bin/systemctl restart podman-palworld.service
fi
'';
serviceConfig = {
Type = "oneshot";
User = "root";
};
};
sops.secrets.palworld = {};
}

View File

@@ -0,0 +1,77 @@
{ pkgs, ... }:
let
cids = import ../modules/staticids.nix;
json = pkgs.formats.json { };
update-containers = pkgs.writeShellScriptBin "update-containers" ''
SUDO=""
if [[ $(id -u) -ne 0 ]]; then
SUDO="sudo"
fi
images=$($SUDO ${pkgs.podman}/bin/podman ps -a --format="{{.Image}}" | sort -u)
for image in $images
do
$SUDO ${pkgs.podman}/bin/podman pull $image
done
'';
in {
users.groups.podman.gid = cids.gids.podman;
virtualisation = {
# containers.containersConf.settings = {
# containers.dns_servers = [ "10.42.97.1" ];
# };
podman = {
enable = true;
dockerCompat = true;
# defaultNetwork.settings = {
# dns_enabled = true; # Enable DNS resolution in the podman network.
# };
};
};
environment.etc."containers/networks/server.json" = {
source = json.generate "server.json" ({
name = "server";
id = "d3a55d6bcc28571c124b4e65cdf1831339045d296858f79e7130fa70da9c0904";
driver = "bridge";
network_interface = "server";
ipv6_enabled = false;
internal = false;
dns_enabled = false;
subnets = [
{
subnet = "10.42.97.0/24";
gateway = "10.42.97.1";
}
];
ipam_options = {
driver = "host-local";
};
});
};
systemd.timers = {
# ...
updatecontainers = {
timerConfig = {
Unit = "updatecontainers.service";
OnCalendar = "02:00";
};
wantedBy = [ "timers.target" ];
};
# ...
};
systemd.services = {
# ...
updatecontainers = {
serviceConfig = {
Type = "oneshot";
ExecStart = "${update-containers}/bin/update-containers";
};
};
# ...
};
}

View File

@@ -0,0 +1,20 @@
{ config, pkgs, ... }:
{
services.postgresql = {
enable = true;
ensureDatabases = [ "mydatabase" ];
identMap = ''
# ArbitraryMapName systemUser DBUser
superuser_map root postgres
superuser_map postgres postgres
# Let other names login as themselves
superuser_map /^(.*)$ \1
'';
authentication = pkgs.lib.mkOverride 10 ''
#type database DBuser auth-method optional_ident_map
local sameuser all peer map=superuser_map
'';
};
};

View File

@@ -0,0 +1,133 @@
{ pkgs, config, python3Packages, ... }:
let
domain = "snapcast.cloonar.com";
snapweb = pkgs.stdenv.mkDerivation {
pname = "snapweb";
version = "0.8";
src = pkgs.fetchzip {
url = "https://github.com/badaix/snapweb/releases/download/v0.8.0/snapweb.zip";
sha256 = "sha256-IpT1pcuzcM8kqWJUX3xxpRQHlfPNsrwhemLmY0PyzjI=";
stripRoot = false;
};
installPhase = ''
mkdir -p $out
cp -r $src/* $out/
'';
};
in
{
security.acme.certs."${domain}" = {
group = "nginx";
};
containers.snapcast = {
autoStart = true;
ephemeral = false; # because of ssh key
privateNetwork = true;
hostBridge = "server";
hostAddress = "10.42.97.1";
localAddress = "10.42.97.21/24";
bindMounts = {
"/var/lib/acme/snapcast/" = {
hostPath = "${config.security.acme.certs.${domain}.directory}";
isReadOnly = true;
};
};
config = { lib, config, pkgs, python3Packages, ... }:
let
shairport-sync = pkgs.shairport-sync.overrideAttrs (_: {
configureFlags = [
"--with-alsa" "--with-pipe" "--with-pa" "--with-stdout"
"--with-avahi" "--with-ssl=openssl" "--with-soxr"
"--without-configfiles"
"--sysconfdir=/etc"
"--with-metadata"
];
});
in
{
networking = {
hostName = "snapcast";
useHostResolvConf = false;
defaultGateway = {
address = "10.42.96.1";
interface = "eth0";
};
nameservers = [ "10.42.97.1" ];
firewall.enable = false;
};
environment.etc = {
# Creates /etc/nanorc
shairport = {
text = ''
whatever you want to put in the file goes here.
metadata =
{
enabled = "yes"; // set this to yes to get Shairport Sync to solicit metadata from the source and to pass it on via a pipe
include_cover_art = "yes"; // set to "yes" to get Shairport Sync to solicit cover art from the source and pass it via the pipe. You must also set "enabled" to "yes".
cover_art_cache_directory = "/tmp/shairport-sync/.cache/coverart"; // artwork will be stored in this directory if the dbus or MPRIS interfaces are enabled or if the MQTT client is in use. Set it to "" to prevent caching, which may be useful on some systems
pipe_name = "/tmp/shairport-sync-metadata";
pipe_timeout = 5000; // wait for this number of milliseconds for a blocked pipe to unblock before giving up
};
'';
# The UNIX file mode bits
mode = "0440";
};
};
services.snapserver = {
enable = true;
codec = "flac";
http.enable = true;
http.docRoot = "${snapweb}/";
streams.mopidy = {
type = "pipe";
location = "/run/snapserver/mopidy";
};
streams.airplay = {
type = "airplay";
location = "${shairport-sync}/bin/shairport-sync";
query = {
devicename = "Multi Room New";
port = "5000";
params = "--mdns=avahi";
};
};
streams.mixed = {
type = "meta";
location = "/airplay/mopidy";
};
};
services.avahi.enable = true;
services.avahi.publish.enable = true;
services.avahi.publish.userServices = true;
services.nginx.enable = true;
services.nginx.virtualHosts."snapcast.cloonar.com" = {
sslCertificate = "/var/lib/acme/snapcast/fullchain.pem";
sslCertificateKey = "/var/lib/acme/snapcast/key.pem";
sslTrustedCertificate = "/var/lib/acme/snapcast/chain.pem";
forceSSL = true;
extraConfig = ''
proxy_buffering off;
'';
locations."/".extraConfig = ''
proxy_pass http://127.0.0.1:1780;
proxy_set_header Host $host;
proxy_redirect http:// https://;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
'';
};
system.stateVersion = "23.05";
};
};
}

View File

@@ -0,0 +1,14 @@
{
uids = {
unbound = 10001;
gitea = 10002;
gitea-runner = 10003;
podman = 10004;
};
gids = {
unbound = 10001;
gitea = 10002;
gitea-runner = 10003;
podman = 10004;
};
}

View File

@@ -0,0 +1,58 @@
{ pkgs, ... }:
{
systemd.services.sysbox = {
description = "Sysbox container runtime";
documentation = [ "https://github.com/nestybox/sysbox" ];
bindsTo = [ "sysbox-mgr.service" "sysbox-fs.service" ];
after = [ "sysbox-mgr.service" "sysbox-fs.service" ];
before = [ "docker.service" "containerd.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "exec";
ExecStart = ''
/bin/sh -c "${pkgs.sysbox}/bin/sysbox-runc --version && ${pkgs.sysbox}/bin/sysbox-mgr --version && ${pkgs.sysbox}/bin/sysbox-fs --version && ${pkgs.coreutils-full}/bin/sleep infinity"
'';
};
};
systemd.services.sysbox-fs = {
description = "sysbox-fs (part of the Sysbox container runtime)";
partOf = [ "sysbox.service" ];
after = [ "sysbox-mgr.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "notify";
ExecStart = "${pkgs.sysbox}/bin/sysbox-fs";
TimeoutStartSec = 10;
TimeoutStopSec = 10;
StartLimitInterval = 0;
NotifyAccess = "main";
OOMScoreAdjust = -500;
# The number of files opened by sysbox-fs is a function of the number of
# containers and the workloads within them. Thus we set the limit to
# infinite so to prevent "too many open files" errors.
LimitNOFILE = "infinity";
LimitNPROC = "infinity";
};
};
systemd.services.sysbox-mgr = {
description = "sysbox-mgr (part of the Sysbox container runtime)";
partOf = [ "sysbox.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "notify";
ExecStart = "${pkgs.sysbox}/bin/sysbox-mgr";
TimeoutStartSec = 45;
TimeoutStopSec = 90;
StartLimitInterval = 0;
NotifyAccess = "main";
OOMScoreAdjust = -500;
# The number of files opened by sysbox-fs is a function of the number of
# containers and the workloads within them. Thus we set the limit to
# infinite so to prevent "too many open files" errors.
LimitNOFILE = "infinity";
LimitNPROC = "infinity";
};
};
}

View File

@@ -0,0 +1,339 @@
{ config, pkgs, ... }:
let
cids = import ../modules/staticids.nix;
domain = "ns.cloonar.com";
adblockLocalZones = pkgs.stdenv.mkDerivation {
name = "unbound-zones-adblock";
src = (pkgs.fetchFromGitHub {
owner = "StevenBlack";
repo = "hosts";
rev = "3.0.0";
sha256 = "01g6pc9s1ah2w1cbf6bvi424762hkbpbgja9585a0w99cq0n6bxv";
} + "/hosts");
phases = [ "installPhase" ];
installPhase = ''
${pkgs.gawk}/bin/awk '{sub(/\r$/,"")} {sub(/^127\.0\.0\.1/,"0.0.0.0")} BEGIN { OFS = "" } NF == 2 && $1 == "0.0.0.0" { print "local-zone: \"", $2, "\" static"}' $src | tr '[:upper:]' '[:lower:]' | sort -u > $out
'';
};
cfg = {
remote-control.control-enable = true;
server = {
include = [
"\"${adblockLocalZones}\""
];
interface = [ "0.0.0.0" "::0" ];
interface-automatic = "yes";
access-control = [
"127.0.0.0/8 allow"
"10.42.96.0/24 allow"
"10.42.97.0/24 allow"
"10.42.98.0/24 allow"
"10.42.99.0/24 allow"
"10.42.101.0/24 allow"
"0.0.0.0/0 allow"
];
tls-cert-bundle = "/etc/ssl/certs/ca-certificates.crt";
local-zone = "\"cloonar.com\" transparent";
local-data = [
"\"localhost A 127.0.0.1\""
"\"localhost.cloonar.com A 127.0.0.1\""
"\"localhost AAAA ::1\""
"\"localhost.cloonar.com AAAA ::1\""
"\"fw.cloonar.com A 10.42.97.1\""
"\"fw A 10.42.97.1\""
"\"pc.cloonar.com IN A 10.42.96.5\""
"\"omada.cloonar.com IN A 10.42.97.2\""
"\"switch.cloonar.com IN A 10.42.97.10\""
"\"mopidy.cloonar.com IN A 10.42.97.21\""
"\"deconz.cloonar.com IN A 10.42.97.22\""
"\"brn30055c566237.cloonar.com IN A 10.42.96.100\""
"\"snapcast.cloonar.com IN A 10.42.97.21\""
"\"home-assistant.cloonar.com IN A 10.42.97.20\""
"\"web-02.cloonar.com IN A 10.42.97.5\""
"\"support.cloonar.com IN A 10.42.97.5\""
"\"git.cloonar.com IN A 10.42.97.50\""
"\"sync.cloonar.com IN A 10.42.97.51\""
"\"feeds.cloonar.com IN A 188.34.191.144\""
# "\"paraclub.cloonar.dev IN A 49.12.244.139\""
# "\"api.paraclub.cloonar.dev IN A 49.12.244.139\""
# "\"module.paraclub.cloonar.dev IN A 49.12.244.139\""
# "\"tandem.paraclub.cloonar.dev IN A 49.12.244.139\""
"\"stage.wsw.at IN A 10.254.235.22\""
"\"prod.wsw.at IN A 10.254.217.23\""
"\"piwik.wohnservice-wien.at IN A 10.254.240.109\""
"\"wohnservice-wien.at IN A 10.254.240.109\""
"\"mieterhilfe.at IN A 10.254.240.109\""
"\"wohnpartner-wien.at IN A 10.254.240.109\""
"\"new.wohnberatung-wien.at IN A 10.254.240.109\""
"\"wohnberatung-wien.at IN A 10.254.240.109\""
"\"wienbautvor.at IN A 10.254.240.109\""
"\"wienwohntbesser.at IN A 10.254.240.109\""
"\"b.wohnservice-wien.at IN A 10.254.240.109\""
"\"b.mieterhilfe.at IN A 10.254.240.109\""
"\"b.wohnpartner-wien.at IN A 10.254.240.109\""
"\"b.wohnberatung-wien.at IN A 10.254.240.109\""
"\"b.wienbautvor.at IN A 10.254.240.109\""
"\"b.wienwohntbesser.at IN A 10.254.240.109\""
"\"a.wohnservice-wien.at IN A 10.254.240.109\""
"\"a.wohnpartner-wien.at IN A 10.254.240.109\""
"\"a.stage.wohnservice-wien.at IN A 10.254.240.110\""
"\"a.stage.mieterhilfe.at IN A 10.254.240.110\""
"\"a.stage.wohnpartner-wien.at IN A 10.254.240.110\""
"\"a.stage.wohnberatung-wien.at IN A 10.254.240.110\""
"\"a.stage.wienbautvor.at IN A 10.254.240.110\""
"\"a.stage.wienwohntbesser.at IN A 10.254.240.110\""
"\"b.stage.wohnservice-wien.at IN A 10.254.240.110\""
"\"b.stage.mieterhilfe.at IN A 10.254.240.110\""
"\"b.stage.wohnpartner-wien.at IN A 10.254.240.110\""
"\"b.stage.new.wohnberatung-wien.at IN A 10.254.240.110\""
"\"b.stage.wohnberatung-wien.at IN A 10.254.240.110\""
"\"b.stage.wienbautvor.at IN A 10.254.240.110\""
"\"b.stage.wienwohntbesser.at IN A 10.254.240.110\""
"\"upgrade-staging.wohnservice-wien.at IN A 10.254.240.110\""
"\"upgrade-staging.mieterhilfe.at IN A 10.254.240.110\""
"\"upgrade-staging.wohnpartner-wien.at IN A 10.254.240.110\""
"\"upgrade-staging.wohnberatung-wien.at IN A 10.254.240.110\""
"\"upgrade-staging.wienbautvor.at IN A 10.254.240.110\""
"\"upgrade-staging.wienwohntbesser.at IN A 10.254.240.110\""
"\"conf.wrwks.at IN A 10.254.240.105\""
"\"web.hilgenberg-gmbh.de IN A 91.107.197.169\""
"\"deconz.cloonar.multimedia IN A 10.42.97.22\""
"\"metz.cloonar.multimedia IN A 10.42.99.10\""
# "\"ps5.cloonar.multimedia IN A 10.42.99.12\""
"\"xbox.cloonar.multimedia IN A 10.42.99.13\""
# "\"switch.cloonar.multimedia IN A 10.42.99.14\""
#living room
"\"shellycolorbulb-livingroom-1.cloonar.smart IN A 10.42.100.2\""
"\"shellycolorbulb-livingroom-2.cloonar.smart IN A 10.42.100.3\""
"\"shellycolorbulb-livingroom-3.cloonar.smart IN A 10.42.100.4\""
"\"shellycolorbulb-livingroom-4.cloonar.smart IN A 10.42.100.5\""
"\"shellycolorbulb-livingroom-5.cloonar.smart IN A 10.42.100.6\""
"\"shellycolorbulb-livingroom-6.cloonar.smart IN A 10.42.100.7\""
"\"shellyuni-livingroom-1.cloonar.smart IN A 10.42.100.8\""
"\"shellyswitch25-livingroom-1.cloonar.smart IN A 10.42.100.9\""
"\"shellyplug-s-living-1.cloonar.smart IN A 10.42.100.10\""
"\"shellyplug-s-living-2.cloonar.smart IN A 10.42.100.11\""
# kitchen
"\"shellyplug-s-kitchen-1.cloonar.smart IN A 10.42.100.17\""
"\"shellyrgbw2-kitchen-1.cloonar.smart IN A 10.42.100.18\""
#bedroom
"\"shelly1-bedroom-1.cloonar.smart IN A 10.42.100.33\""
"\"shellybutton1-bedroom-1.cloonar.smart IN A 10.42.100.34\""
"\"shellybutton1-bedroom-2.cloonar.smart IN A 10.42.100.35\"" # todo
"\"shellyrgbw2-bedroom-1.cloonar.smart IN A 10.42.100.36\""
"\"shellyrgbw2-bedroom-2.cloonar.smart IN A 10.42.100.37\""
"\"shellyrgbw2-bedroom-3.cloonar.smart IN A 10.42.100.38\""
"\"shellycolorbulb-bedroom-1.cloonar.smart IN A 10.42.100.39\""
"\"shellycolorbulb-bedroom-2.cloonar.smart IN A 10.42.100.40\""
"\"shellycolorbulb-bedroom-3.cloonar.smart IN A 10.42.100.41\""
"\"shellycolorbulb-bedroom-4.cloonar.smart IN A 10.42.100.42\""
# bath
"\"shellyswitch25-bath-1.cloonar.smart IN A 10.42.100.49\""
"\"shellybulbduo-bath-1.cloonar.smart IN A 10.42.100.50\""
"\"shellybulbduo-bath-2.cloonar.smart IN A 10.42.100.51\""
"\"shelly1pm-bath-1.cloonar.smart IN A 10.42.100.52\""
"\"shellyht-bath-1.cloonar.smart IN A 10.42.100.53\"" # todo
# hallway
"\"shelly1-hallway-1.cloonar.smart IN A 10.42.100.65\""
"\"shellycolorbulb-hallway-1.cloonar.smart IN A 10.42.100.66\""
"\"shellycolorbulb-hallway-2.cloonar.smart IN A 10.42.100.67\""
"\"shellycolorbulb-hallway-3.cloonar.smart IN A 10.42.100.68\""
"\"shellycolorbulb-hallway-4.cloonar.smart IN A 10.42.100.69\""
"\"shellyem3.cloonar.smart IN A 10.42.100.70\""
"\"shellypro-1.cloonar.smart IN A 10.42.100.71\""
"\"shellypro-2.cloonar.smart IN A 10.42.100.72\""
# toilet
"\"shelly1-toilet-1.cloonar.smart IN A 10.42.100.81\""
"\"shellybulbduo-toilet-1.cloonar.smart IN A 10.42.100.82\""
# storage
"\"shelly1-storage-1.cloonar.smart IN A 10.42.100.97\""
"\"shellyplug-storage-1.cloonar.smart IN A 10.42.100.98\""
"\"brn30055c566237.cloonar.multimedia IN A 10.42.99.100\""
"\"ddl-warez.to IN A 172.67.184.30\""
"\"cdnjs.cloudflare.com IN A 104.17.24.14\""
];
local-data-ptr = [
"\"127.0.0.1 localhost\""
"\"::1 localhost\""
"\"10.42.97.1 fw.cloonar.com\""
"\"10.42.97.20 home-assistant.cloonar.com\""
"\"10.42.97.21 snapcast.cloonar.com\""
"\"10.42.97.22 deconz.cloonar.com\""
"\"10.42.97.50 git.cloonar.com\""
"\"10.254.235.22 stage.wsw.at\""
"\"10.254.217.23 prod.wsw.at\""
"\"10.254.240.109 wohnservice-wien.at\""
"\"10.254.240.110 a.stage.wohnservice-wien.at\""
"\"172.67.184.30 ddl-warez.to\""
"\"104.17.24.14 cdnjs.cloudflare.com\""
];
# ssl-upstream = "yes";
};
forward-zone = [
{
name = "local.ghetto.at.";
forward-tls-upstream = "no";
forward-addr = [
"10.43.97.1"
];
}
{
name = "ghetto.at.local.";
forward-tls-upstream = "no";
forward-addr = [
"10.43.97.1"
];
}
{
name = "epicenter.works.";
forward-tls-upstream = "no";
forward-addr = [
"10.50.60.1"
];
}
{
name = "akvorrat.at.";
forward-tls-upstream = "no";
forward-addr = [
"10.50.60.1"
];
}
{
name = "epicenter.intra.";
forward-tls-upstream = "no";
forward-addr = [
"10.14.1.1"
];
}
{
name = "intra.epicenter.works.";
forward-tls-upstream = "no";
forward-addr = [
"10.14.1.1"
];
}
{
name = ".";
forward-tls-upstream = "yes";
forward-first = "no";
forward-addr = [
"9.9.9.9@853#dns9.quad9.net"
"149.112.112.11@853#dns11.quad9.net"
];
}
];
};
in {
users.users.unbound = {
group = "unbound";
isSystemUser = true;
uid = cids.uids.unbound;
};
users.groups.unbound = {
gid = cids.gids.unbound;
};
security.acme.certs."${domain}" = {
group = "unbound";
};
security.acme.certs."fw.cloonar.com" = {
group = "unbound";
};
services.resolved.enable = false;
services.unbound = {
enable = true;
settings = cfg;
};
# systemd.services.unbound-sync = {
# enable = true;
# path = with pkgs; [ unbound inotify-tools ];
# script = ''
# function readFile() {
# if [[ "''\$2" == "A" ]] ; then
# cat "''\$1" | tail -n +2 | while IFS=, read -r address hwaddr client_id valid_lifetime expire subnet_id fqdn_fwd fqdn_rev hostname state user_context
# do
# echo "''\${address},''\${hostname}"
# done
# else
# cat "''\$1" | tail -n +2 | while IFS=, read -r address duid valid_lifetime expire subnet_id pref_lifetime lease_type iaid prefix_len fqdn_fwd fqdn_rev hostname hwaddr state user_context hwtype hwaddr_source
# do
# echo "''\${address},''\${hostname}"
# done
# fi
# }
#
# function readFileUnique() {
# readFile "''\$1" ''\$2 | uniq | while IFS=, read -r address hostname
# do
# if echo "''\${1}" | grep -Eq '.*\.(cloonar.com|cloonar.multimedia|cloonar.smart)'; then
# echo ''\${hostname} ''\$2 ''\${address}
# unbound-control local_data ''\${hostname} ''\$2 ''\${address}
# if [[ "''\$2" == "A" ]] ; then
# echo ''\${address} | while IFS=. read -r ip0 ip1 ip2 ip3
# do
# unbound-control local_data ''\${ip3}.''\${ip2}.''\${ip1}.''\${ip0}.ip4.arpa. PTR ''\${hostname}
# unbound-control local_data ''\${ip3}.''\${ip2}.''\${ip1}.''\${ip0}.in-addr.arpa. PTR ''\${hostname}
# done
# fi
# else
# if [[ "''\$2" == "A" ]] ; then
# echo ''\${address} | while IFS=. read -r ip0 ip1 ip2 ip3
# do
# if [[ "''\${hostname}" != "" ]]; then
# domain=cloonar.com
# if [[ "''\${ip2}" == 99 ]]; then
# domain=cloonar.multimedia
# fi
# if [[ "''\${ip2}" == 100 ]]; then
# domain=cloonar.smart
# fi
# if [[ "''\${hostname}" != *. ]]; then
# unbound-control local_data ''\${hostname}.''\${domain} ''\$2 ''\${address}
# else
# unbound-control local_data ''\${hostname}''\${domain} ''\$2 ''\${address}
# fi
#
# fi
# unbound-control local_data ''\${ip3}.''\${ip2}.''\${ip1}.''\${ip0}.ip4.arpa. PTR ''\${hostname}
# unbound-control local_data ''\${ip3}.''\${ip2}.''\${ip1}.''\${ip0}.in-addr.arpa. PTR ''\${hostname}
# done
# fi
# fi
# done
# }
#
# function syncFile() {
# # readFileUnique "''\$1" "''\$2"
# while true; do
# readFileUnique "''\$1" "''\$2"
# sleep 10
# done
# }
#
# syncFile "/var/lib/kea/dhcp4.leases" A &
# # syncFile "/var/lib/kea/dhcp6.leases" AAAA &
# wait
# '';
# wants = [ "network-online.target" "unbound.service" ];
# after = [ "network-online.target" "unbound.service" ];
# partOf = [ "unbound.service" ];
# wantedBy = [ "multi-user.target" ];
# };
networking.firewall.allowedUDPPorts = [ 53 5353 ];
}

View File

@@ -0,0 +1,39 @@
{ config, pkgs, ... }:
let
update-containers = pkgs.writeShellScriptBin "update-containers" ''
SUDO=""
if [[ $(id -u) -ne 0 ]]; then
SUDO="sudo"
fi
images=$($SUDO ${pkgs.podman}/bin/podman ps -a --format="{{.Image}}" | sort -u)
for image in $images
do
$SUDO ${pkgs.podman}/bin/podman pull $image
done
'';
in {
systemd.timers = {
# ...
updatecontainers = {
timerConfig = {
Unit = "updatecontainers.service";
OnCalendar = "02:00";
};
wantedBy = [ "timers.target" ];
};
# ...
};
systemd.services = {
# ...
updatecontainers = {
serviceConfig = {
Type = "oneshot";
ExecStart = "update-containers";
};
};
# ...
};
}

View File

@@ -0,0 +1,113 @@
{ lib, nixpkgs, pkgs, ... }: let
hostname = "web-02";
json = pkgs.formats.json { };
impermanence = builtins.fetchTarball "https://github.com/nix-community/impermanence/archive/master.tar.gz";
in {
microvm.vms = {
web = {
config = {
microvm = {
mem = 4096;
# hypervisor = "cloud-hypervisor";
shares = [
{
source = "/nix/store";
mountPoint = "/nix/.ro-store";
tag = "ro-store";
proto = "virtiofs";
}
{
source = "/var/lib/microvms/persist/web-02";
mountPoint = "/persist";
tag = "persist";
proto = "virtiofs";
}
];
volumes = [
{
image = "rootfs.img";
mountPoint = "/";
size = 102400;
}
];
interfaces = [
{
type = "tap";
id = "vm-${hostname}";
mac = "02:00:00:00:00:03";
}
];
};
imports = [
"${impermanence}/nixos.nix"
../../utils/modules/sops.nix
../../utils/modules/lego/lego.nix
# ../../utils/modules/borgbackup.nix
# ./zammad.nix
./proxies.nix
];
time.timeZone = "Europe/Vienna";
systemd.network.networks."10-lan" = {
matchConfig.PermanentMACAddress = "02:00:00:00:00:03";
address = [ "10.42.97.5/24" ];
gateway = [ "10.42.97.1" ];
dns = [ "10.42.97.1" ];
};
fileSystems."/persist".neededForBoot = lib.mkForce true;
environment.persistence."/persist-local" = {
directories = [
"/var/lib/zammad"
"/var/lib/postgresql"
"/var/log"
"/var/lib/systemd/coredump"
];
};
environment.systemPackages = with pkgs; [
vim # my preferred editor
];
networking.hostName = hostname;
services.openssh = {
enable = true;
hostKeys = [
{
path = "/persist/etc/ssh/ssh_host_ed25519_key";
type = "ed25519";
}
{
path = "/persist/etc/ssh/ssh_host_rsa_key";
type = "rsa";
bits = 4096;
}
];
};
users.users.root.openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/2SAFm50kraB1fepAizox/QRXxB7WbqVbH+5OPalDT47VIJGNKOKhixQoqhABHxEoLxdf/C83wxlCVlPV9poLfDgVkA3Lyt5r3tSFQ6QjjOJAgchWamMsxxyGBedhKvhiEzcr/Lxytnoz3kjDG8fqQJwEpdqMmJoMUfyL2Rqp16u+FQ7d5aJtwO8EUqovhMaNO7rggjPpV/uMOg+tBxxmscliN7DLuP4EMTA/FwXVzcFNbOx3K9BdpMRAaSJt4SWcJO2cS2KHA5n/H+PQI7nz5KN3Yr/upJN5fROhi/SHvK39QOx12Pv7FCuWlc+oR68vLaoCKYhnkl3DnCfc7A7"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIRQuPqH5fdX3KEw7DXzWEdO3AlUn1oSmtJtHB71ICoH Generated By Termius"
];
# backups
# borgbackup.repo = "u149513-sub2@u149513-sub2.your-backup.de:borg";
sops.age.sshKeyPaths = [ "/persist/etc/ssh/ssh_host_ed25519_key" ];
sops.defaultSopsFile = ./secrets.yaml;
networking.firewall = {
enable = true;
allowedTCPPorts = [ 22 80 443 ];
};
system.stateVersion = "22.05";
};
};
};
}

View File

@@ -0,0 +1,10 @@
{ ... }: {
services.nginx.virtualHosts."git.cloonar.com" = {
forceSSL = true;
enableACME = true;
acmeRoot = null;
locations."/" = {
proxyPass = "https://git.cloonar.com/";
};
};
}

View File

@@ -0,0 +1,32 @@
borg-passphrase: ENC[AES256_GCM,data:2WjoqMRmXvW9EGMmpMYhrC0Qt0Dk7QWlbEncZPdK2SxVljEoFibjVEr6jeYdAx6UkaXdjk9pD3PBbls2tWt0TiNQdh8=,iv:bHzASNjqqfPsQ/1w/oM7x0FubAzzRkn+iWrZlenU9rs=,tag:ektqi0rqEywg9YGybPQesw==,type:str]
borg-ssh-key: ENC[AES256_GCM,data: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,iv:U3+fjacm8+gZAjPQNz2mjFYTUbLyltTaPiSKb3lvCmk=,tag:ZR6zI1UijDayIvH3v35Hqg==,type:str]
zammad-key-base: ENC[AES256_GCM,data:HO9MuwcwjryuXr5No8sCPfso5bpLtQCoczrC/R214ecVIFwwH1uhMeNO8Tlh6EjRLPo7aVTSz87Vx5yaNVezvHCs55G6TT9mcNS/v/V7sbFz9dNIgbFblY3gFIAa4cViioYc71wdb7d4Tta7qhse5zQ41KhAqCWuGDgFErQA4Oc=,iv:b1wY8fW0psircSlNXwDjPzNWK8NyAMNqegitNcqV6U4=,tag:oQ7nyO9TKOOu6IF7ODzpPA==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age16veg3fmvpfm7a89a9fc8dvvsxmsthlm70nfxqspr6t8vnf9wkcwsvdq38d
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoUWdTYlRjWDJvemF5Q2sr
VCtrS2dTTGRwUlNIWHd0WkVCRkRMcGhuTzE0ClNic1FmQ05UNWQwbGc4TUFMNGlI
K0RhK2pqUGY3UElmK1pNUEkxV2xGUTQKLS0tIFRORE9JTDRZK0MwZUJoc2xlcHFH
bmp3ZW14TVdCMHhkSi84NE5neDdrY3cKYfgu7aqvG6wQmEFhmzieXFGoQpyffPXj
jiHrAPjBBFy21wdYf0nQXNMzekqOMJwOj0oNA2b5omprPxjB9uns4Q==
-----END AGE ENCRYPTED FILE-----
- recipient: age1gjm4c3swt8u88e36gf2qlg3syxfc0ly94u64c42f2tsf24npw4csa6e4fw
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUUjQxWnBMQXo3QmF1STUw
bHh1NDhvQXZIQ2RiOUx5OU5Wc3BVSEJDUEZVCmVzeFk5SWpMbVV4VUdsRmhiaWwz
bTJDY1pJRXJvNUdCSXJqQ3Byd3lWN2sKLS0tIHRKdXRNc1BYcURBRVNlenk1OEl3
Q05BN0VnQ0haeHBobWhRV0EzL3dLSEkKWlALiX5mvG8y0WUc8yFWMbcpSRrSGoQx
SHaOlDCjYvViZ7GPRLqnSwDGZ1clC6JsTbwKXrMsWdZBKvSO/VIWQw==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-08-16T11:12:23Z"
mac: ENC[AES256_GCM,data:nMLxD/WP3LxLTECQ/wQjiDW3F2Lx8yeMTkNIg97eipebVZwTLiVGg4t+sVzen+X3t4tPixO2a72mWMtIVQKs8d2MzkydLh+LjYItUBP+uw/rnCjB0zfxiPN883+FO6q4+BoT0JJc4LUHbgQQWEDnKaqld4/ICE1xJbPZVEJWo40=,iv:JenHaRqB8ZVDRV5rUOgMURflqQzfOrt9pHege2oiT7g=,tag:xv0p2oW1P0FPqcrRoQ/6tw==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.8.1

View File

@@ -0,0 +1,120 @@
{ config, pkgs, ... }:
{
services.zammad = {
enable = true;
port = 3010;
secretKeyBaseFile = config.sops.secrets.zammad-key-base.path;
database = {
createLocally = true;
};
};
services.nginx.enable = true;
services.nginx.virtualHosts."support.cloonar.com" = {
forceSSL = true;
enableACME = true;
acmeRoot = null;
extraConfig = ''
# Virtual endpoint created by nginx to forward auth requests.
location /authelia {
internal;
set $upstream_authelia https://auth.cloonar.com/api/verify;
proxy_pass_request_body off;
proxy_pass $upstream_authelia;
proxy_set_header Content-Length "";
# Timeout if the real server is dead
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
# [REQUIRED] Needed by Authelia to check authorizations of the resource.
# Provide either X-Original-URL and X-Forwarded-Proto or
# X-Forwarded-Proto, X-Forwarded-Host and X-Forwarded-Uri or both.
# Those headers will be used by Authelia to deduce the target url of the user.
# Basic Proxy Config
client_body_buffer_size 128k;
proxy_set_header Host $host;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Forwarded-Ssl on;
proxy_redirect http:// $scheme://;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 4 32k;
# Advanced Proxy Config
send_timeout 5m;
proxy_read_timeout 240;
proxy_send_timeout 240;
proxy_connect_timeout 240;
}
'';
locations."/" = {
proxyPass = "http://127.0.0.1:3010";
proxyWebsockets = true;
extraConfig =
"proxy_set_header X-Forwarded-Proto 'https';" +
"proxy_set_header X-Forwarded-Ssl on;" +
"proxy_connect_timeout 300;" +
"proxy_send_timeout 300;" +
"proxy_read_timeout 300;" +
"send_timeout 300;"
;
};
locations."/auth/sso" = {
proxyPass = "http://127.0.0.1:3010";
proxyWebsockets = true;
extraConfig = ''
# Basic Authelia Config
# Send a subsequent request to Authelia to verify if the user is authenticated
# and has the right permissions to access the resource.
auth_request /authelia;
# Set the `target_url` variable based on the request. It will be used to build the portal
# URL with the correct redirection parameter.
auth_request_set $target_url $scheme://$http_host$request_uri;
# Set the X-Forwarded-User and X-Forwarded-Groups with the headers
# returned by Authelia for the backends which can consume them.
# This is not safe, as the backend must make sure that they come from the
# proxy. In the future, it's gonna be safe to just use OAuth.
auth_request_set $user $upstream_http_remote_user;
auth_request_set $groups $upstream_http_remote_groups;
auth_request_set $name $upstream_http_remote_name;
auth_request_set $email $upstream_http_remote_email;
proxy_set_header Remote-User $user;
proxy_set_header Remote-Groups $groups;
proxy_set_header Remote-Name $name;
proxy_set_header Remote-Email $email;
# If Authelia returns 401, then nginx redirects the user to the login portal.
# If it returns 200, then the request pass through to the backend.
# For other type of errors, nginx will handle them as usual.
error_page 401 =302 https://auth.cloonar.com/?rd=$target_url;
'';
};
locations."/ws" = {
proxyPass = "http://127.0.0.1:6042";
proxyWebsockets = true;
extraConfig =
"proxy_set_header X-Forwarded-Proto 'https';" +
"proxy_set_header X-Forwarded-Ssl on;" +
"proxy_read_timeout 86400;" +
"send_timeout 300;"
;
};
};
sops.secrets = {
zammad-key-base.owner = "zammad";
};
services.postgresqlBackup.enable = true;
services.postgresqlBackup.databases = [ "zammad" ];
}

View File

@@ -0,0 +1,53 @@
{ config, ... }: {
sops.secrets.wg_cloonar_key = {};
sops.secrets.wg_epicenter_works_key = {};
sops.secrets.wg_epicenter_works_psk = {};
sops.secrets.wg_ghetto_at_key = {};
# https://wiki.archlinux.org/title/WireGuard#Loop_routing
networking.wireguard.interfaces = {
wg_cloonar = {
ips = [ "10.42.114.1/24" ];
listenPort = 51820;
# publicKey: TKQVDmBnf9av46kQxLQSBDhAeaK8r1zh8zpU64zuc1Q=
privateKeyFile = config.sops.secrets.wg_cloonar_key.path;
peers = [
{ # Notebook
publicKey = "YdlRGsjh4hS3OMJI+t6SZ2eGXKbs0wZBXWudHW4NyS8=";
allowedIPs = [ "10.42.114.201/32" ];
}
{ # iPhone
publicKey = "nkm10abmwt2G8gJXnpqel6QW5T8aSaxiqqGjE8va/A0=";
allowedIPs = [ "10.42.114.202/32" ];
}
];
};
wg_epicenter = {
ips = [ "10.50.60.6/32" ];
privateKeyFile = config.sops.secrets.wg_epicenter_works_key.path;
peers = [
{
endpoint = "5.9.131.17:51821";
publicKey = "T7jPGSapSudtKyWwi2nu+2hjjse96I4U3lccRHZWd2s=";
presharedKeyFile = config.sops.secrets.wg_epicenter_works_psk.path;
allowedIPs = [ "10.14.1.0/24" "10.14.2.0/24" "10.14.11.0/24" "10.14.40.0/24" "10.25.0.0/24" "10.50.60.0/24" ];
}
];
};
wg_ghetto_at = {
ips = [ "10.43.98.2/32" ];
# publicKey: o0FsoHL7ymwuDYmWA5N1mngbGT1sZJnhK6zhJkuEtzE=
privateKeyFile = config.sops.secrets.wg_ghetto_at_key.path;
peers = [
{
endpoint = "vpn.ghetto.at:51820";
publicKey = "v4pr6tzS0Xpwh/mWTohxxvCRaAj2B4bqtJnNOu9v2Xs=";
allowedIPs = [ "10.43.0.0/16" ];
}
];
};
};
networking.firewall.allowedUDPPorts = [ 51820 ];
}

View File

@@ -0,0 +1,16 @@
{ pkgs, ... }:
let
wolScript = pkgs.writeScriptBin "wol-script" ''
case $1 in
"gaming")
${pkgs.wol}/bin/wol -i 10.42.96.255 78:8c:b5:fe:41:62
};
"") echo "Usage: $0 <hostname>"; exit 1;;
esac
'';
in
{
environment.systemPackages = [
wolScript
];
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
# args of buildLinux:
# https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/os-specific/linux/kernel/generic.nix
# Note that this method will use the deconfig in source tree,
# commbined the common configuration defined in pkgs/os-specific/linux/kernel/common-config.nix, which is suitable for a NixOS system.
# but it't not suitable for embedded systems, so we comment it out.
# ================================================================
# If you already have a generated configuration file, you can build a kernel that uses it with pkgs.linuxManualConfig
# The difference between deconfig and the generated configuration file is that the generated configuration file is more complete,
#
{
fetchFromGitHub,
linuxManualConfig,
ubootTools,
...
}:
(linuxManualConfig rec {
modDirVersion = "6.1.43";
# modDirVersion = "5.10.65";
version = "${modDirVersion}-xunlong-rk3588";
extraMeta.branch = "6.1";
# extraMeta.branch = "5.10";
# https://github.com/orangepi-xunlong/linux-orangepi/tree/orange-pi-6.1-rk35xx
src = fetchFromGitHub {
owner = "orangepi-xunlong";
repo = "linux-orangepi";
# rev = "122b41d84d018af909a766e48f3f90cbea9868e0";
# hash = "sha256-kOhxDP1hbrrIriOXizgZoB0I+3/JWOPcOCdNeXcPJV0=";
# rev = "eb1c681e5184e51d8ce1f351559d149d17f48b57";
# hash = "sha256-kOhxDP1hbrrIriOXizgZoB0I+3/JWOPcOCdNeXcPJV0=";
rev = "752c0d0a12fdce201da45852287b48382caa8c0f";
hash = "sha256-tVu/3SF/+s+Z6ytKvuY+ZwqsXUlm40yOZ/O5kfNfUYc=";
};
# Steps to the generated kernel config file
# 1. git clone --depth 1 https://github.com/armbian/linux-rockchip.git -b rk-6.1-rkr1
# 2. put https://github.com/armbian/build/blob/main/config/kernel/linux-rk35xx-vendor.config to linux-rockchip/arch/arm64/configs/rk35xx_vendor_defconfig
# 3. run `nix develop .#fhsEnv` in this project to enter the fhs test environment defined here.
# 4. `cd linux-rockchip` and `make rk35xx_vendor_defconfig` to configure the kernel.
# 5. Then use `make menuconfig` in kernel's root directory to view and customize the kernel(like enable/disable rknpu, rkflash, ACPI(for UEFI) etc).
# 6. copy the generated .config to ./pkgs/kernel/rk35xx_vendor_config and commit it.
#
configfile = ./rk35xx_vendor_config;
allowImportFromDerivation = true;
})
.overrideAttrs (old: {
name = "k"; # dodge uboot length limits
# nativeBuildInputs = old.nativeBuildInputs ++ [ubootTools];
})

View File

@@ -0,0 +1,17 @@
{
stdenv,
fetchurl,
}:
stdenv.mkDerivation {
pname = "mali-g610-firmware";
version = "g21p0-01eac0";
src = fetchurl {
url = "https://github.com/JeffyCN/mirrors/raw/e08ced3e0235b25a7ba2a3aeefd0e2fcbd434b68/firmware/g610/mali_csffw.bin";
hash = "sha256-jnyCGlXKHDRcx59hJDYW3SX8NbgfCQlG8wKIbWdxLfU=";
};
buildCommand = ''
install -Dm444 $src $out/lib/firmware/mali_csffw.bin
'';
}

View File

@@ -0,0 +1,23 @@
{ fetchFromGitHub, stdenvNoCC, ... }: stdenvNoCC.mkDerivation {
pname = "orangepi-firmware";
version = "2024.01.24";
dontBuild = true;
dontFixup = true;
compressFirmware = false;
src = fetchFromGitHub {
owner = "orangepi-xunlong";
repo = "firmware";
rev = "76ead17a1770459560042a9a7c43fe615bbce5e7";
hash = "sha256-mltaup92LTGbuCXeGTMdoFloX3vZRbaUFVbh6lwveFs=";
};
installPhase = ''
runHook preInstall
mkdir -p $out/lib/firmware
cp -a * $out/lib/firmware/
runHook postInstall
'';
}

54
hosts/fw-new/secrets.yaml Normal file
View File

@@ -0,0 +1,54 @@
borg-passphrase: ENC[AES256_GCM,data:jHb+yXK0RqNdVYtWiueztZFlHC/xQ6ZiAOUcLt6BxmZQewuL3mh4AZ+lQdmA/4EaaTTIhVMR3xFx5fU6b2CtNLiGb/0=,iv:IW09B1EE1OupMCOvv13MXRYiMsD4VmIfyYONUyrPX1c=,tag:3ankeLOaDJkwRUGCd72DuA==,type:str]
borg-ssh-key: ENC[AES256_GCM,data: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,iv:9E1uiPqM3Hh4KWtL8haxm6PRm2VPc+DggrA135FvfB8=,tag:QSOgzVH9IBMgZxJvUhvY2w==,type:str]
ddclient: ENC[AES256_GCM,data:EaXjXS/bwL3S/Fr+rzQ7dXA1eIzeFpHH7H+SvoNhVSg=,iv:3BzjnJG5yT1W8ob2nm0oUlr+sSJ73W/ctl48xyxeeWM=,tag:TqKSwfxF0V1v5T8VT/qblw==,type:str]
wrwks_vpn_key: ENC[AES256_GCM,data:gGipXC8JJO59b4KWMSo0+r761raQl7RzgBuUbXmPEKlZR21bs5XRAQalzDCFNtjcpNkXiGqAHCLkDTtjPagMsw==,iv:MH1EBJEOdQDEgm9E0F884fynhsH8KiS5QSc605XbASQ=,tag:FUM1eptHS0rpt6ILyQjGOg==,type:str]
wg_cloonar_key: ENC[AES256_GCM,data:Dtp6I5J0jU5LLVwEFU4DFCpUngPRmFMebGXnk2oSwsKtsir/DtRBFG7ictM=,iv:1Abx/EAZRJrRQURljofzUYDgJpuREriX0nSrFbH5Npw=,tag:l4uFl9Uc+W0XeLVfLGmgZA==,type:str]
wg_epicenter_works_key: ENC[AES256_GCM,data:LeLjfwfaz+loWyHYRgIMIPzHzlOnhl9tluKcQFgdes6r+deft1JfnUzDuF0=,iv:DKrc3I+U2hWDH8nnc8ZQeaVtA1eVXu7SXdTn1fxHoH4=,tag:V0PL0GrL2NEPVslAZa801A==,type:str]
wg_epicenter_works_psk: ENC[AES256_GCM,data:Den3NDWdP013Or6/2Vll1igUahuRSNW4hu+nDa5vkr93bbveQTaWFT4TD4U=,iv:r3UsD3+3lUIP2X3Grti7wpXTQBXtu1/MdrycEmpZfsI=,tag:ghbAcxmjGVOe9jCZsmFzjA==,type:str]
wg_ghetto_at_key: ENC[AES256_GCM,data:OIHmoy3SpIi9aefZnZ1PzpyHbEso18ceoTULf2eQkx1rJbaxC6PD1lma7eQ=,iv:u0eFjHHOBzPTmBvBEQsYY5flcBayiAQKd6e7RyiPwJI=,tag:731C9wvv8bA5fuuQq+weVQ==,type:str]
gitea-runner: ENC[AES256_GCM,data:IRx9QzbLJrkF/DYvpVf2012BiSBnHZJe10opkRO2kJuegdb0denW3mvmnU4isoj7jO/0QyN6HZHlHb5ihC7fFl4LavPDVjAAhZPynkpDw9IHFeqZDUSPzxQsq7FibKmfEpEmWEz+Npe8JI1kl694XYV/kqErKa3JrZS7Jm8zFcv7DSY/V5bdy4Is8ZSRtHiP/aVzFdsvjwtissCDnCl7zRZjXUcN0FssvPHBZHxLuc68EoagIw1aVSzkvSVBXer4rFdlefjskFelRnUr3pvm188=,iv:VnvPFDFGz/QyfQmZxQFB3J2ReqaHdRaypb2Vnq7Dthw=,tag:19rx0nlmXLj/6yPRAFGigA==,type:str]
gitea-runner-token: ENC[AES256_GCM,data:Nd0vsnuJficsdZaqeBZXa9vD7PLMdDtV9sMX0TxUSEMNU7Reu3HLCWuvP0easPU=,iv:4mrfQc1tobg/QiExUuWST6iU9TdNwiS1BMmOnQqCFZU=,tag:85aRoD3IkRq3mcoPdLKaBQ==,type:str]
drone: ENC[AES256_GCM,data:S8WTZqGHfcdpSojavZ87GdE5dagcTAdHBVQEbHHgnB4V7aczS6c5QdEJxK920Pjpf6o54OOQYniVsPiiXSxwjExDKPzhs/DG2hfigmf8RgfkP+3tF2W0KiPmV2jxog8w226ZKnI+hSBs8tuIfJBhrpY7Y/YNmTPfq+cnnLS8ibYqytcpzoogI9I8THzHCu3r+yejoGSyTMs9L4gPhOjz5aK4UV6V,iv:zqN/aSBI3xGGNDnpHPGyQnQP2YZOGUk6dAGtON/QlHU=,tag:o9YFDKAB5uR9lPmChyxB8g==,type:str]
home-assistant-ldap: ENC[AES256_GCM,data:uZEPbSnkgQYSd8ev6FD8TRHWWr+vusadtMcvP7KKL2AZAV0h1hga5fODN6I5u0DNL9hq2pNM+FwU0E/svWLRww==,iv:IhmUgSu34NaAY+kUZehx40uymydUYYAyte1aGqQ33/8=,tag:BKFCJPr7Vz4EG78ry/ZD7g==,type:str]
home-assistant-secrets.yaml: ENC[AES256_GCM,data:m7uOVo7hPk/RmqqRS6y7NKoMKsR9Bdi1ntatsZdDOAbJMjZmZL2FgPEHi/zF73zCfRfTOca3dwpulR3WXZ9Ic1sbUIggmusJMg4Gellw1CUhx7SbQN5nieAbPbB9GVxMuV4OakD1u7Swz8JggDT6IwojSnuD5omCRCyUH1wvKB+Re59q6EStderlm5MJNVFlVrbKVbLKLcw4yRgTh34BGnTTjcJmgSlQjO1ciu2B7YQmdl0Fw6d8AdbEzgB5TFG5ONc85UhJDE8Wlw==,iv:GCtpcVChN2UMWtfnWURozCfVj2YbRPqp/bH4Jjntybs=,tag:pcxP7gTBtXMNT5iyW5YXTw==,type:str]
palworld: ENC[AES256_GCM,data:rdqChPt4gSJHS1D60+HJ+4m5mg35JbC+pOmevK21Y95QyAIeyBLVGhRYlOaUcqdZM2e4atyTTSf6z4nHsm539ddCbW7J2DCdF5PQkrAGDmmdTVq+jyJAT8gTrbXXCglT1wvFYY5dbf2NKA4ASJIA8bdVNuwRZU0CtFiishzLuc9m8ZcGCNwQ/+xkMZgkUAHYRlEJAZyMpXR6KkFftiR05JRAFczD4N7GXPPe+vyvgXg7QBGtf20Qd4SGBUw0zI/SNTRmifHUuc4Z6+Fe9JHgvTc3uFcTMVnty0fEuL+a29liaVdAFq8BnqJfc5CNV401ZSUeMbG41lCn1cegP/WChs9J6HXNrhWDgiXa6ln++NoKcfOHIfZVbYOCoOxFR6+YWeBU2+sHmdwI9j5XQf5Ly2hmg12j0Ds2Cn8k4PG5aQP+HT2bedqyxwSt6fi97A0Osnh4ig7+DzYAjSNLewbYLzVdK39VdvB9hqLto+yFS3gAaeYOHwPwtqa+COI85c55lHiyKHlSwPhBqYaaiDu00lQTUzq9R5vz6F/l+T3bUjuna5RryUu8yhnk5DyK834KycTOg4ETcZTqro6prfiEBxc+Utsc9JvEtZgwFv6fsVLOu7nHxuiYuvseZ4YA8LlYdwPJboMPO2XsuhwWtT1uz/rh2orH7/vsXvzA/kF8NFemWBEMVLYA8byC5ze8doiGDYp4T5AAf10nJB1ceQ==,iv:gs78fxhvo9KlTaR5nzs12/LdgPChSFPHD2k4VQp3ARo=,tag:lpWBOi9xh2cWkS+71KD/UQ==,type:str]
ark: ENC[AES256_GCM,data:YYGyzoVIKI9Ac1zGOr0BEpd3fgBsvp1hSwAvfO07/EQdg8ufMWUkNvqNHDKN62ZK5A1NnY3JTA1p4gyZ4ryQeAOsbwqU1GSk2YKHFyPeEnpLz/Ml82KMsv7XPGXuKRXZ4v3UcLu0R8k1Q0gQsMWo4FjCs3FF5mVtJG/YWxxbCYHoBLJ/di5p0DgjuFgJBQknYBpuLzr+yIoeqEyN7XcGYAJO53trEJuOOxLILULifkqISHjZ66i5F1fHW0iUdRbmeWV4aOAeOrsQqXYv,iv:gJwV5ip84zHqpU0l0uESfWWOtcgihMvEEdLaeI+twcU=,tag:sy8udVQsKxV/jOqwhJmWAg==,type:str]
firefox-sync: ENC[AES256_GCM,data:uAJAdyKAuXRuqCFl8742vIejU5RnAPpUxUFCC0s0QeXZR5oH2YOrDh+3vKUmckW4V1cIhSHoe+4+I4HuU5E73DDrJThfIzBEw+spo4HXwZf5KBtu3ujgX6/fSTlPWV7pEsDDsZ0y6ziKPADBDym8yEk0bU9nRedvTBUhVryo3aolzF/c+gJvdeDvKUYa8+8=,iv:yuvE4KG7z7Rp9ZNlLiJ2rh0keed3DuvrELzsfJu4+bs=,tag:HFo1A53Eva31NJ8fRE7TlA==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age16veg3fmvpfm7a89a9fc8dvvsxmsthlm70nfxqspr6t8vnf9wkcwsvdq38d
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1YlN0a1M2cStpbUtMMWFZ
RzQrMGZmbkN2c01yOHhvbllwQUVpcWhmU3lrCkQxeHNQb2pKa3pOYnB3aEFjTGl1
c1IvSnZnTS9JMFJ1L1E0cXRybEJ6KzQKLS0tIDdPNTNwZDdMRzhyVzNzdXRESlZO
TkRXeUsxTWpodWtIT3Mza3o3SlZGdUkK/U6+p4rYGLhTWSHPOysau+iCoWseiLht
oT8a2hp9dSh1ofseyBfgeDeBN7Td9Z9FTBXBgcM911Sdq3VffQJHgw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1v6p8dan2t3w9h94fz4flldl32082j3s9x6zqq7u5j66keth9aphsd6pvch
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBheVYzaDRndjhXMmhYaGdC
ZFcyUlZNd28wbFdsUEk2OWt5aEYwSzBsWVFrCnZjOHg2bXFPNlgwa3E3NkZlOXpJ
T2llSXJLNmcwWVVYdDdJY24xV1laWmMKLS0tIFhwTFdKaHk4NG91L2Y3OUZ4eHhD
V000QkdMWUhBV3E3dklnbTgvQVFUVG8KRkTaCoXdzF6+di4o9MoZIVUtM7YCxfiF
3PP2lurWxmSmGDhD7OwIgM+EQ0sKViDbcvGs6Oo8BKClgSx7i9kvPg==
-----END AGE ENCRYPTED FILE-----
- recipient: age12msc2c6drsaw0yk2hjlaw0q0lyq0emjx5e8rq7qc7ql689k593kqfmhss2
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKbUMxNy9VTkJkMkszcUdx
MjJlRDk4TnoxMVEzSDdIK3J5dktWWHl5MHl3CmtjS013OXlqSjNhTlNBWURTRmht
eFVLRU1Kbm5OdUtHRm5Nb3NGdzBwWHMKLS0tIE51M2tnaEUzMlRIeDEzZjhxV3RH
clE0QWFvRit2N1hsaDlUcUpDbFdhUlEKA+8ukUbm61s2B7XzbBclbmL1G+cHP9DO
XGOzmtpNm/kPKZCj9CuMBB3Ze4pEQglv66YQPafzQhmP4LMoWrOQrA==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-08-02T22:57:14Z"
mac: ENC[AES256_GCM,data:U9/pKXdqXMvjQgyTIGz0JG+88aBXVgp29Fmm0OE66KMArkX8ungcEtdnGYKhD0gFJKLrKZZY5V8oyAXEq95D+Bh8ZnfmQibYw04cPldc6kTZstsrpbzrWVfn6sqG/ih12oXdsLws+H6IeN+O2qGZHDIVjvPufAdJ3A2X+Yakahg=,iv:mG+dGv3l/PNhggvlujLxDGU5z47qVA9sOTUbU2b2dPo=,tag:Rz2av33iwa9aYR7c0cviEg==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.8.1

1
hosts/fw-new/utils Symbolic link
View File

@@ -0,0 +1 @@
../../utils