From 95cb749b432377cfb4719029d323b309ca37de13 Mon Sep 17 00:00:00 2001 From: Sokhibjon Orzikulov Date: Mon, 23 Dec 2024 05:18:39 +0500 Subject: [PATCH] feat: nix module for easy deployment --- flake.nix | 2 +- module.nix | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++++ shell.nix | 2 +- 3 files changed, 213 insertions(+), 2 deletions(-) create mode 100644 module.nix diff --git a/flake.nix b/flake.nix index adeccf6..bc6e2d8 100644 --- a/flake.nix +++ b/flake.nix @@ -35,6 +35,6 @@ ) // { # Overlay module - # nixosModules.rustina.bot = import ./module.nix self; + nixosModules.rustina.bot = import ./module.nix self; }; } diff --git a/module.nix b/module.nix new file mode 100644 index 0000000..a553339 --- /dev/null +++ b/module.nix @@ -0,0 +1,211 @@ +# Template & Guide from +# https://github.com/reckenrode/nix-foundryvtt/blob/main/modules/foundryvtt/default.nix +flake: { + config, + lib, + pkgs, + ... +}: let + cfg = config.services.rustina.bot; + bot = flake.packages.${pkgs.stdenv.hostPlatform.system}.default; + + genArgs = {cfg}: let + token = cfg.token; + domain = cfg.webhook.domain or ""; + mode = + if cfg.webhook.enable + then "webhook" + else "polling"; + port = + if cfg.webhook.enable + then "--port ${toString cfg.webhook.port}" + else ""; + in + lib.strings.concatStringsSep " " [mode token domain port]; + + caddy = lib.mkIf (cfg.enable && cfg.webhook.enable && cfg.webhook.proxy == "caddy") { + services.caddy.virtualHosts = lib.debug.traceIf (builtins.isNull cfg.webhook.domain) "webhook.domain can't be null, please specicy it properly!" { + "${cfg.webhook.domain}" = { + extraConfig = '' + reverse_proxy 127.0.0.1:${toString cfg.webhook.port} + ''; + }; + }; + }; + + nginx = lib.mkIf (cfg.enable && cfg.webhook.enable && cfg.webhook.proxy == "nginx") { + services.nginx.virtualHosts = lib.debug.traceIf (builtins.isNull cfg.webhook.domain) "webhook.domain can't be null, please specicy it properly!" { + "${cfg.webhook.domain}" = { + addSSL = true; + enableACME = true; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString cfg.webhook.port}"; + proxyWebsockets = true; + }; + }; + }; + }; + + service = lib.mkIf cfg.enable { + users.users.${cfg.user} = { + description = "Rustina Bot management user"; + isSystemUser = true; + group = cfg.group; + }; + + users.groups.${cfg.group} = {}; + + systemd.services.rustina-bot = { + description = "Rustina Bot for managing telegram community"; + documentation = ["https://rust-lang.uz/"]; + + after = ["network-online.target"]; + wants = ["network-online.target"]; + wantedBy = ["multi-user.target"]; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + Restart = "always"; + ExecStart = "${lib.getBin cfg.package}/bin/bot ${genArgs {cfg = cfg;}}"; + StateDirectory = cfg.user; + StateDirectoryMode = "0750"; + # EnvironmentFile = cfg.secret; + CapabilityBoundingSet = [ + "AF_NETLINK" + "AF_INET" + "AF_INET6" + ]; + DeviceAllow = ["/dev/stdin r"]; + DevicePolicy = "strict"; + IPAddressAllow = "localhost"; + LockPersonality = true; + # MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectSystem = "strict"; + ReadOnlyPaths = ["/"]; + RemoveIPC = true; + RestrictAddressFamilies = [ + "AF_NETLINK" + "AF_INET" + "AF_INET6" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + "~@resources" + "@pkey" + ]; + UMask = "0027"; + }; + + # preStart = '' + # installedConfigFile="${config.services.rustina.bot.dataDir}/Config/options.json" + # install -d -m750 ${config.services.rustina.bot.dataDir}/Config + # rm -f "$installedConfigFile" && install -m640 ${configFile} "$installedConfigFile" + # ''; + }; + }; + + asserts = lib.mkIf cfg.enable { + warnings = [ + (lib.mkIf (cfg.webhook.enable && cfg.webhook.domain == null) "services.rustina.bot.webhook.domain must be set in order to properly generate certificate!") + ]; + + assertions = [ + { + assertion = cfg.token != null; + message = "services.rustina.bot.token must be set!"; + } + ]; + }; +in { + options = with lib; { + services.rustina.bot = { + enable = mkEnableOption '' + Rustina Bot: Telegram bot made by Uzbek Rust team for Rust Uzbekistan community. + ''; + + webhook = { + enable = mkEnableOption '' + Webhook method of deployment + ''; + + domain = mkOption { + type = with types; nullOr str; + default = null; + example = "rust-lang.uz"; + description = "Domain to use while adding configurations to web proxy server"; + }; + + proxy = mkOption { + type = with types; + nullOr (enum [ + "nginx" + "caddy" + ]); + default = "caddy"; + description = "Proxy reverse software for hosting webhook"; + }; + + port = mkOption { + type = types.int; + default = 8451; + description = "Port to use for passing over proxy"; + }; + }; + + token = mkOption { + type = with types; nullOr path; + default = null; + description = lib.mdDoc '' + Path to telegram bot token of Rustina manager. + ''; + }; + + user = mkOption { + type = types.str; + default = "rustina-bot"; + description = "User for running system + accessing keys"; + }; + + group = mkOption { + type = types.str; + default = "rustina-bot"; + description = "Group for running system + accessing keys"; + }; + + dataDir = mkOption { + type = types.str; + default = "/var/lib/rustina/bot"; + description = lib.mdDoc '' + The path where Rustina Bot keeps its config, data, and logs. + ''; + }; + + package = mkOption { + type = types.package; + default = bot; + description = '' + The Rustian Bot package to use with the service. + ''; + }; + }; + }; + + config = lib.mkMerge [asserts service caddy nginx]; +} diff --git a/shell.nix b/shell.nix index c725494..cec7527 100644 --- a/shell.nix +++ b/shell.nix @@ -58,7 +58,7 @@ in # Start watching for changes # Start watching for changes in the background - # cargo watch -x "run --bin xinuxmgr" & + # cargo watch -x "run --bin bot" & # Store the PID of the background process # CARGO_WATCH_PID=$!