From 84c39b405972516a9be2e8be8729097f8691050f Mon Sep 17 00:00:00 2001 From: sinanmohd Date: Fri, 17 Oct 2025 08:31:18 +0530 Subject: chore(os/kay): refactor --- os/kay/modules/services/alina.nix | 30 ++++ os/kay/modules/services/cgit.nix | 34 ++++ os/kay/modules/services/github-runner.nix | 41 +++++ os/kay/modules/services/home-assistant.nix | 47 ++++++ os/kay/modules/services/immich.nix | 23 +++ os/kay/modules/services/iperf3.nix | 10 ++ os/kay/modules/services/mail.nix | 173 +++++++++++++++++++++ os/kay/modules/services/matrix/default.nix | 22 +++ os/kay/modules/services/matrix/dendrite.nix | 109 +++++++++++++ .../services/matrix/matrix-sliding-sync.nix | 123 +++++++++++++++ os/kay/modules/services/minio.nix | 36 +++++ os/kay/modules/services/nix-cache.nix | 12 ++ os/kay/modules/services/sftp.nix | 59 +++++++ 13 files changed, 719 insertions(+) create mode 100644 os/kay/modules/services/alina.nix create mode 100644 os/kay/modules/services/cgit.nix create mode 100644 os/kay/modules/services/github-runner.nix create mode 100644 os/kay/modules/services/home-assistant.nix create mode 100644 os/kay/modules/services/immich.nix create mode 100644 os/kay/modules/services/iperf3.nix create mode 100644 os/kay/modules/services/mail.nix create mode 100644 os/kay/modules/services/matrix/default.nix create mode 100644 os/kay/modules/services/matrix/dendrite.nix create mode 100644 os/kay/modules/services/matrix/matrix-sliding-sync.nix create mode 100644 os/kay/modules/services/minio.nix create mode 100644 os/kay/modules/services/nix-cache.nix create mode 100644 os/kay/modules/services/sftp.nix (limited to 'os/kay/modules/services') diff --git a/os/kay/modules/services/alina.nix b/os/kay/modules/services/alina.nix new file mode 100644 index 0000000..c567953 --- /dev/null +++ b/os/kay/modules/services/alina.nix @@ -0,0 +1,30 @@ +{ config, alina, ... }: +let + domain = "alinafs.com"; +in +{ + imports = [ alina.nixosModules.alina ]; + + sops.secrets."misc/alina" = { }; + + services.postgresql = { + ensureDatabases = [ "alina" ]; + ensureUsers = [ + { + name = "alina"; + ensureDBOwnership = true; + } + ]; + }; + + services.alina = { + enable = true; + port = 8006; + environmentFile = config.sops.secrets."misc/alina".path; + settings.server = { + data = "/hdd/alina"; + file_size_limit = 1024 * 1024 * 1024; # 1GB + public_url = "https://${domain}"; + }; + }; +} diff --git a/os/kay/modules/services/cgit.nix b/os/kay/modules/services/cgit.nix new file mode 100644 index 0000000..254cc80 --- /dev/null +++ b/os/kay/modules/services/cgit.nix @@ -0,0 +1,34 @@ +{ config, pkgs, ... }: + +let + domain = config.global.userdata.domain; + user = config.global.userdata.name; +in +{ + users.users.${user}.packages = with pkgs; [ git ]; + environment.systemPackages = with pkgs; [ + luajitPackages.luaossl + lua52Packages.luaossl + ]; + + services.cgit."git.${domain}" = { + enable = true; + nginx.virtualHost = "git.${domain}"; + scanPath = "/var/lib/git"; + settings = { + project-list = "/var/lib/git/project.list"; + remove-suffix = 1; + enable-commit-graph = 1; + root-title = "${user}'s git repository"; + root-desc = "how do i learn github anon"; + source-filter = "${pkgs.cgit}/lib/cgit/filters/syntax-highlighting.py"; + about-filter = "${pkgs.cgit}/lib/cgit/filters/about-formatting.sh"; + readme = ":README.md"; + footer = ""; + enable-blame = 1; + clone-url = "https://git.${domain}/$CGIT_REPO_URL"; + enable-log-filecount = 1; + enable-log-linecount = 1; + }; + }; +} diff --git a/os/kay/modules/services/github-runner.nix b/os/kay/modules/services/github-runner.nix new file mode 100644 index 0000000..dd4d48d --- /dev/null +++ b/os/kay/modules/services/github-runner.nix @@ -0,0 +1,41 @@ +{ config, ... }: +let + repo = "nocodb/nocodb"; + nocodbRunnerUser = "nocodbrunner"; + user = config.global.userdata.name; +in +{ + sops.secrets = { + "github-runner/nocodb-registration-token" = { }; + "github-runner/age-master-key" = { }; + }; + + # required by github:nocodb/nocodb docker builds + virtualisation.docker.enable = true; + users.groups.${nocodbRunnerUser} = { }; + users.extraGroups.docker.members = [ + user + nocodbRunnerUser + ]; + users.users.nocodbrunner = { + name = nocodbRunnerUser; + group = nocodbRunnerUser; + isSystemUser = true; + }; + services.github-runners.kay = { + user = nocodbRunnerUser; + group = nocodbRunnerUser; + enable = true; + noDefaultLabels = true; + extraLabels = [ "nix" ]; + tokenFile = config.sops.secrets."github-runner/nocodb-registration-token".path; + url = "https://github.com/${repo}"; + }; + + systemd.services."github-runner-kay" = { + environment.SOPS_AGE_KEY_FILE = "%d/age-master-key"; + serviceConfig.LoadCredential = "age-master-key:${ + config.sops.secrets."github-runner/age-master-key".path + }"; + }; +} diff --git a/os/kay/modules/services/home-assistant.nix b/os/kay/modules/services/home-assistant.nix new file mode 100644 index 0000000..65807f7 --- /dev/null +++ b/os/kay/modules/services/home-assistant.nix @@ -0,0 +1,47 @@ +{ pkgs, ... }: +{ + services.postgresql = { + enable = true; + + ensureDatabases = [ "hass" ]; + ensureUsers = [ + { + name = "hass"; + ensureDBOwnership = true; + } + ]; + }; + + services.home-assistant = { + enable = true; + package = + (pkgs.home-assistant.override { + extraPackages = py: with py; [ psycopg2 ]; + }).overrideAttrs + (oldAttrs: { + doInstallCheck = false; + }); + + extraComponents = [ + "analytics" + "google_translate" + "met" + "radio_browser" + "shopping_list" + "tplink" + "tuya" + "utility_meter" + ]; + + config = { + default_config = { }; + + recorder.db_url = "postgresql://@/hass"; + http = { + server_host = "127.0.0.1"; + trusted_proxies = [ "127.0.0.1" ]; + use_x_forwarded_for = true; + }; + }; + }; +} diff --git a/os/kay/modules/services/immich.nix b/os/kay/modules/services/immich.nix new file mode 100644 index 0000000..5e5eaf4 --- /dev/null +++ b/os/kay/modules/services/immich.nix @@ -0,0 +1,23 @@ +{ config, ... }: +let + domain = config.global.userdata.domain; + mediaLocation = "/hdd/immich"; +in +{ + services.immich = { + enable = true; + inherit mediaLocation; + settings.server.externalDomain = "https://immich.${domain}"; + }; + + systemd.tmpfiles.settings.immich.${mediaLocation}.d = { + group = config.services.immich.group; + user = config.services.immich.user; + mode = "0755"; + }; + + users.users.immich.extraGroups = [ + "video" + "render" + ]; +} diff --git a/os/kay/modules/services/iperf3.nix b/os/kay/modules/services/iperf3.nix new file mode 100644 index 0000000..2c8afef --- /dev/null +++ b/os/kay/modules/services/iperf3.nix @@ -0,0 +1,10 @@ +{ ... }: + +{ + services.iperf3 = { + enable = true; + + bind = "192.168.43.1"; + openFirewall = true; + }; +} diff --git a/os/kay/modules/services/mail.nix b/os/kay/modules/services/mail.nix new file mode 100644 index 0000000..685461f --- /dev/null +++ b/os/kay/modules/services/mail.nix @@ -0,0 +1,173 @@ +{ config, pkgs, ... }: +let + ipv6 = "2001:470:ee65::1337"; + domain = config.global.userdata.domain; + username = config.global.userdata.name; + email = [ + "${username}@${domain}" + + # used by github automation + # https://github.com/nocodb/nocodb/blob/32826d4b24e9285b898bb3547fdf550f81c930bb/nix/bumper/bumper.sh#L5 + "auto@${domain}" + # used by mail.sinanmohd.com + "postmaster@${domain}" + # used by ns1.sinanmohd.com + "hostmaster@${domain}" + ]; + + credentials_directory = "/run/credentials/stalwart-mail.service"; +in +{ + security.acme.certs.${domain}.postRun = "systemctl restart stalwart-mail.service"; + sops.secrets = { + "mail.${domain}/dkim_rsa" = { }; + "mail.${domain}/dkim_ed25519" = { }; + "mail.${domain}/password" = { }; + }; + + systemd.services.stalwart-mail.serviceConfig.LoadCredential = [ + "password:${config.sops.secrets."mail.${domain}/password".path}" + + "dkim_rsa:${config.sops.secrets."mail.${domain}/dkim_rsa".path}" + "dkim_ed25519:${config.sops.secrets."mail.${domain}/dkim_ed25519".path}" + + "cert:${config.security.acme.certs.${domain}.directory}/fullchain.pem" + "key:${config.security.acme.certs.${domain}.directory}/key.pem" + ]; + + services.postgresql = { + ensureDatabases = [ "stalwart" ]; + ensureUsers = [ + { + name = "stalwart"; + ensureDBOwnership = true; + } + ]; + }; + + services.stalwart-mail = { + enable = true; + openFirewall = true; + + settings = { + queue.outbound = { + ip-strategy = "ipv6_then_ipv4"; + source-ip.v6 = "['${ipv6}']"; + tls.starttls = "optional"; + }; + http.url = "'https://stalwart.${domain}'"; + + server = { + hostname = "mail.${domain}"; + listener = { + smtp = { + bind = [ + "[${ipv6}]:25" + "0.0.0.0:25" + ]; + protocol = "smtp"; + }; + submission = { + bind = "[::]:587"; + protocol = "smtp"; + }; + submissions = { + bind = "[::]:465"; + protocol = "smtp"; + tls.implicit = true; + }; + imaptls = { + bind = "[::]:993"; + protocol = "imap"; + tls.implicit = true; + }; + http = { + bind = "[::]:8085"; + protocol = "http"; + }; + }; + }; + + signature = { + rsa = { + private-key = "%{file:${credentials_directory}/dkim_rsa}%"; + inherit domain; + selector = "rsa"; + headers = [ + "From" + "To" + "Date" + "Subject" + "Message-ID" + ]; + algorithm = "rsa-sha-256"; + canonicalization = "simple/simple"; + + set-body-length = true; + expire = "2d"; + report = true; + }; + ed25519 = { + private-key = "%{file:${credentials_directory}/dkim_ed25519}%"; + inherit domain; + selector = "ed25519"; + headers = [ + "From" + "To" + "Date" + "Subject" + "Message-ID" + ]; + algorithm = "ed25519-sha256"; + canonicalization = "simple/simple"; + + set-body-length = true; + expire = "2d"; + report = true; + }; + }; + + certificate."default" = { + cert = "%{file:${credentials_directory}/cert}%"; + private-key = "%{file:${credentials_directory}/key}%"; + }; + + storage = { + data = "postgresql"; + fts = "postgresql"; + blob = "postgresql"; + lookup = "postgresql"; + directory = "memory"; + }; + store.postgresql = { + type = "postgresql"; + host = "localhost"; + database = "stalwart"; + user = "stalwart"; + timeout = "15s"; + tls.enable = false; + pool.max-connections = 10; + }; + + directory."memory" = { + type = "memory"; + + principals = [ + { + class = "admin"; + name = "${username}@${domain}"; + secret = "%{file:${credentials_directory}/password}%"; + inherit email; + } + { + # for mta-sts & dmarc reports + class = "individual"; + name = "reports@${domain}"; + secret = "%{file:${credentials_directory}/password}%"; + email = [ "reports@${domain}" ]; + } + ]; + }; + }; + }; +} diff --git a/os/kay/modules/services/matrix/default.nix b/os/kay/modules/services/matrix/default.nix new file mode 100644 index 0000000..1b9564d --- /dev/null +++ b/os/kay/modules/services/matrix/default.nix @@ -0,0 +1,22 @@ +{ config, ... }: +let + domain = config.global.userdata.domain; +in +{ + imports = [ + ./dendrite.nix + ./matrix-sliding-sync.nix + ]; + + sops.secrets."matrix-${domain}/sliding_sync" = { }; + + services.matrix-sliding-sync-dirty = { + enable = true; + environmentFile = config.sops.secrets."matrix-${domain}/sliding_sync".path; + + settings = { + SYNCV3_LOG_LEVEL = "warn"; + SYNCV3_SERVER = "http://127.0.0.1:${toString config.services.dendrite.httpPort}"; + }; + }; +} diff --git a/os/kay/modules/services/matrix/dendrite.nix b/os/kay/modules/services/matrix/dendrite.nix new file mode 100644 index 0000000..e66c5a5 --- /dev/null +++ b/os/kay/modules/services/matrix/dendrite.nix @@ -0,0 +1,109 @@ +{ config, ... }: + +let + domain = config.global.userdata.domain; + database = { + connection_string = "postgres:///dendrite?host=/run/postgresql"; + max_open_conns = 90; + max_idle_conns = 5; + conn_max_lifetime = -1; + }; +in +{ + sops.secrets."matrix-${domain}/key" = { }; + systemd.services.dendrite.after = [ "postgresql.service" ]; + + services = { + postgresql = { + ensureDatabases = [ "dendrite" ]; + ensureUsers = [ + { + name = "dendrite"; + ensureDBOwnership = true; + } + ]; + }; + + dendrite = { + enable = true; + loadCredential = [ + "private_key:${config.sops.secrets."matrix-${domain}/key".path}" + ]; + + settings = { + sync_api.search = { + enabled = true; + index_path = "/var/lib/dendrite/searchindex"; + }; + global = { + metrics.enabled = true; + server_name = domain; + private_key = "$CREDENTIALS_DIRECTORY/private_key"; + trusted_third_party_id_servers = [ + "matrix.org" + "vector.im" + ]; + inherit database; + }; + logging = [ + { + type = "std"; + level = "warn"; + } + ]; + mscs = { + inherit database; + mscs = [ "msc2836" ]; + }; + sync_api = { + inherit database; + real_ip_header = "X-Real-IP"; + }; + media_api = { + inherit database; + dynamic_thumbnails = true; + max_file_size_bytes = 12800000000; + }; + federation_api = { + inherit database; + send_max_retries = 8; + key_perspectives = [ + { + server_name = "matrix.org"; + keys = [ + { + key_id = "ed25519:auto"; + public_key = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw"; + } + { + key_id = "ed25519:a_RXGa"; + public_key = "l8Hft5qXKn1vfHrg3p4+W8gELQVo8N13JkluMfmn2sQ"; + } + ]; + } + ]; + }; + + app_service_api = { + inherit database; + }; + room_server = { + inherit database; + }; + push_server = { + inherit database; + }; + relay_api = { + inherit database; + }; + key_server = { + inherit database; + }; + user_api = { + account_database = database; + device_database = database; + }; + }; + }; + }; +} diff --git a/os/kay/modules/services/matrix/matrix-sliding-sync.nix b/os/kay/modules/services/matrix/matrix-sliding-sync.nix new file mode 100644 index 0000000..63d95ad --- /dev/null +++ b/os/kay/modules/services/matrix/matrix-sliding-sync.nix @@ -0,0 +1,123 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + cfg = config.services.matrix-sliding-sync-dirty; + matrix-sliding-sync = pkgs.callPackage ../../pkgs/matrix-sliding-sync.nix { }; +in +{ + imports = [ + (lib.mkRenamedOptionModule + [ "services" "matrix-synapse" "sliding-sync" ] + [ "services" "matrix-sliding-sync" ] + ) + ]; + + options.services.matrix-sliding-sync-dirty = { + enable = lib.mkEnableOption "sliding sync"; + + settings = lib.mkOption { + type = lib.types.submodule { + freeformType = with lib.types; attrsOf str; + options = { + SYNCV3_SERVER = lib.mkOption { + type = lib.types.str; + description = '' + The destination homeserver to talk to not including `/_matrix/` e.g `https://matrix.example.org`. + ''; + }; + + SYNCV3_DB = lib.mkOption { + type = lib.types.str; + default = "postgresql:///matrix-sliding-sync?host=/run/postgresql"; + description = '' + The postgres connection string. + Refer to . + ''; + }; + + SYNCV3_BINDADDR = lib.mkOption { + type = lib.types.str; + default = "127.0.0.1:8009"; + example = "[::]:8008"; + description = "The interface and port or path (for unix socket) to listen on."; + }; + + SYNCV3_LOG_LEVEL = lib.mkOption { + type = lib.types.enum [ + "trace" + "debug" + "info" + "warn" + "error" + "fatal" + ]; + default = "info"; + description = "The level of verbosity for messages logged."; + }; + }; + }; + default = { }; + description = '' + Freeform environment variables passed to the sliding sync proxy. + Refer to for all supported values. + ''; + }; + + createDatabase = lib.mkOption { + type = lib.types.bool; + default = true; + description = '' + Whether to enable and configure `services.postgres` to ensure that the database user `matrix-sliding-sync` + and the database `matrix-sliding-sync` exist. + ''; + }; + + environmentFile = lib.mkOption { + type = lib.types.str; + description = '' + Environment file as defined in {manpage}`systemd.exec(5)`. + + This must contain the {env}`SYNCV3_SECRET` variable which should + be generated with {command}`openssl rand -hex 32`. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + services.postgresql = lib.optionalAttrs cfg.createDatabase { + enable = true; + ensureDatabases = [ "matrix-sliding-sync" ]; + ensureUsers = [ + { + name = "matrix-sliding-sync"; + ensureDBOwnership = true; + } + ]; + }; + + systemd.services.matrix-sliding-sync = rec { + after = + lib.optional cfg.createDatabase "postgresql.service" + ++ lib.optional config.services.dendrite.enable "dendrite.service" + ++ lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit; + wants = after; + wantedBy = [ "multi-user.target" ]; + environment = cfg.settings; + serviceConfig = { + DynamicUser = true; + EnvironmentFile = cfg.environmentFile; + ExecStart = lib.getExe matrix-sliding-sync; + StateDirectory = "matrix-sliding-sync"; + WorkingDirectory = "%S/matrix-sliding-sync"; + RuntimeDirectory = "matrix-sliding-sync"; + Restart = "on-failure"; + RestartSec = "1s"; + }; + }; + }; +} diff --git a/os/kay/modules/services/minio.nix b/os/kay/modules/services/minio.nix new file mode 100644 index 0000000..d440e50 --- /dev/null +++ b/os/kay/modules/services/minio.nix @@ -0,0 +1,36 @@ +{ + config, + lib, + pkgs, + ... +}: +let + email = config.global.userdata.email; +in +{ + sops.secrets."misc/default_password" = { }; + systemd.services.minio.serviceConfig.LoadCredential = [ + "password:${config.sops.secrets."misc/default_password".path}" + ]; + + services.minio = { + enable = true; + consoleAddress = ":9003"; + + package = pkgs.stdenv.mkDerivation { + name = "minio-with-secrets"; + dontUnpack = true; + buildInputs = with pkgs; [ + makeWrapper + minio + ]; + installPhase = '' + mkdir -p $out/bin + makeWrapper ${lib.getExe pkgs.minio} $out/bin/minio \ + --run 'echo "Seting Minio Secrets"' \ + --set MINIO_ROOT_USER ${email} \ + --run 'export MINIO_ROOT_PASSWORD="$(cat "$CREDENTIALS_DIRECTORY"/password)"' + ''; + }; + }; +} diff --git a/os/kay/modules/services/nix-cache.nix b/os/kay/modules/services/nix-cache.nix new file mode 100644 index 0000000..9c81b56 --- /dev/null +++ b/os/kay/modules/services/nix-cache.nix @@ -0,0 +1,12 @@ +{ config, ... }: +let + keyname = "misc/nixbin.${config.global.userdata.domain}"; +in +{ + sops.secrets.${keyname} = { }; + + services.nix-serve = { + enable = true; + secretKeyFile = config.sops.secrets.${keyname}.path; + }; +} diff --git a/os/kay/modules/services/sftp.nix b/os/kay/modules/services/sftp.nix new file mode 100644 index 0000000..f75abc4 --- /dev/null +++ b/os/kay/modules/services/sftp.nix @@ -0,0 +1,59 @@ +{ config, ... }: + +let + storage = "/hdd/users"; + user = config.global.userdata.name; + pubKeys = config.users.users.${user}.openssh.authorizedKeys.keys; +in +{ + users = { + groups."sftp".members = [ ]; + + users."sftp" = { + group = "sftp"; + shell = "/run/current-system/sw/bin/nologin"; + isNormalUser = true; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFmA1dyV+o9gfoxlbVG0Y+dn3lVqdFs5fMqfxyNc5/Lr sftp@cez" + # https://github.com/zhanghai/MaterialFiles + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILxKrIaWRACi0oKfJRv6m3uUWKjKNyd9edbbFR5pAONH sftp@paq" + # samsung files only support PEM, hence RSA key + # https://r1.community.samsung.com/t5/galaxy-s/unable-to-remotely-connect-to-sftp-server-through-my-files/m-p/16347552/highlight/true#M105871 + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCqe7CcXJw+dhXKUVeuj1iGOcV7KhyiJ55PxhGDXfQdu1YS5gi/pBnOk39pL22+QBFZX0trU/JNHpCMZWyFp/Fz9GBxp2LJERHwkANu0lk0PJ7QZdg79YN5lKpWTo2GpA3gHHC555Rm5V5BknwbZwVXWvGhSR93g/2b6AjcSZn4ZUwFF8soSb2EYsRa7blVbBv2njV2SGI9FezfHBF+N3CNOP7kxk63Pilk9NEUQuvYF1tmF7z/zIXbyLNaLT1MJE8KCbayM7E/WZuonSBqFf3fsmQge0La/LveRehQHb503uHNHzlFHXdMMZQrzOAHHyFQUHhYECvhLNDhGJb1KrjZcEiKmqCMmvHCG4JssRdJB5mq6J0g05ZmMrKt0srIT6lginkHy89AKkqt83xHHvXhZEw40zoGcq2rZD1dPN3toNZL/uGaIK0u1eMxFbuVKK3OjMg2UwzaHX1DDZyJdRes5huG/uXTgN7xamUu/TIBOK+WgibJeNf93i3GbsYezTs= sftp@paq" + ] + ++ pubKeys; + }; + + users."nazer" = { + group = "sftp"; + shell = "/run/current-system/sw/bin/nologin"; + isNormalUser = true; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICV09w9Ovk9wk4Bhn/06iOn+Ss8lK3AmQAl8+lXHRycu nazu@pc" + ]; + }; + }; + + services.openssh = { + # support samsing files + settings = { + HostKeyAlgorithms = "+ssh-rsa"; + PubkeyAcceptedAlgorithms = "+ssh-rsa"; + Macs = [ "hmac-sha2-256" ]; + }; + + # sandboxing + extraConfig = '' + Match Group sftp + # chroot dir should be owned by root + # and sub dirs by %u + ChrootDirectory ${storage}/%u + ForceCommand internal-sftp + + PermitTunnel no + AllowAgentForwarding no + AllowTcpForwarding no + X11Forwarding no + ''; + }; +} -- cgit v1.2.3