diff --git a/api/api-module.nix b/api/api-module.nix index 9ee6fb5..c2ec863 100644 --- a/api/api-module.nix +++ b/api/api-module.nix @@ -12,18 +12,12 @@ in { options.services.selfprivacy-api = { enable = mkOption { - default = false; + default = true; type = types.bool; description = '' Enable SelfPrivacy API service ''; }; - token = mkOption { - type = types.str; - description = '' - SelfPrivacy API token - ''; - }; enableSwagger = mkOption { default = false; type = types.bool; @@ -31,30 +25,12 @@ in Enable Swagger UI ''; }; - b2AccountId = mkOption { - type = types.str; - description = '' - B2 account ID - ''; - }; - b2AccountKey = mkOption { - type = types.str; - description = '' - B2 account key - ''; - }; b2Bucket = mkOption { type = types.str; description = '' B2 bucket ''; }; - resticPassword = mkOption { - type = types.str; - description = '' - Restic password - ''; - }; }; config = lib.mkIf cfg.enable { @@ -64,12 +40,8 @@ in inherit (config.environment.sessionVariables) NIX_PATH; HOME = "/root"; PYTHONUNBUFFERED = "1"; - AUTH_TOKEN = cfg.token; ENABLE_SWAGGER = (if cfg.enableSwagger then "1" else "0"); - B2_ACCOUNT_ID = cfg.b2AccountId; - B2_ACCOUNT_KEY = cfg.b2AccountKey; B2_BUCKET = cfg.b2Bucket; - RESTIC_PASSWORD = cfg.resticPassword; } // config.networking.proxy.envVars; path = [ "/var/" "/var/dkim/" pkgs.coreutils pkgs.gnutar pkgs.xz.bin pkgs.gzip pkgs.gitMinimal config.nix.package.out pkgs.nixos-rebuild pkgs.restic pkgs.mkpasswd ]; after = [ "network-online.target" ]; diff --git a/api/api.nix b/api/api.nix index ff4da06..c966f70 100644 --- a/api/api.nix +++ b/api/api.nix @@ -2,12 +2,8 @@ { services.selfprivacy-api = { enable = true; - token = config.services.userdata.api.token; enableSwagger = config.services.userdata.api.enableSwagger; - b2AccountId = config.services.userdata.backblaze.accountId; - b2AccountKey = config.services.userdata.backblaze.accountKey; b2Bucket = config.services.userdata.backblaze.bucket; - resticPassword = config.services.userdata.resticPassword; }; users.users."selfprivacy-api" = { diff --git a/backup/restic.nix b/backup/restic.nix index 5e5cad0..55dd939 100644 --- a/backup/restic.nix +++ b/backup/restic.nix @@ -26,11 +26,4 @@ in isSystemUser = true; group = "restic"; }; - environment.etc."restic/resticPasswd".text = '' - ${cfg.resticPassword} - ''; - environment.etc."restic/s3Passwd".text = '' - AWS_ACCESS_KEY_ID=${cfg.backblaze.accountId} - AWS_SECRET_ACCESS_KEY=${cfg.backblaze.accountKey} - ''; } diff --git a/configuration.nix b/configuration.nix index 5b68de8..d69cc2f 100644 --- a/configuration.nix +++ b/configuration.nix @@ -58,12 +58,16 @@ in }; environment.systemPackages = with pkgs; [ git + jq ]; environment.variables = { DOMAIN = config.services.userdata.domain; }; - system.autoUpgrade.enable = config.services.userdata.autoUpgrade.enable; - system.autoUpgrade.allowReboot = config.services.userdata.autoUpgrade.allowReboot; + system.autoUpgrade = { + enable = config.services.userdata.autoUpgrade.enable; + allowReboot = config.services.userdata.autoUpgrade.allowReboot; + channel = "https://channel.selfprivacy.org/nixos-selfpricacy"; + }; nix = { optimise.automatic = true; gc = { @@ -86,4 +90,4 @@ in enable = true; }; }; -} \ No newline at end of file +} diff --git a/files.nix b/files.nix index 9806e31..f970924 100644 --- a/files.nix +++ b/files.nix @@ -5,33 +5,78 @@ in { systemd.tmpfiles.rules = let - nextcloudDBPass = builtins.replaceStrings [ "\n" "\"" "\\" "%" ] [ "\\n" "\\\"" "\\\\" "%%" ] cfg.nextcloud.databasePassword; - nextcloudAdminPass = builtins.replaceStrings [ "\n" "\"" "\\" "%" ] [ "\\n" "\\\"" "\\\\" "%%" ] cfg.nextcloud.adminPassword; - resticPass = builtins.replaceStrings [ "\n" "\"" "\\" "%" ] [ "\\n" "\\\"" "\\\\" "%%" ] cfg.resticPassword; domain = builtins.replaceStrings [ "\n" "\"" "\\" "%" ] [ "\\n" "\\\"" "\\\\" "%%" ] cfg.domain; - cloudflareCredentials = builtins.replaceStrings [ "\n" "\"" "\\" "%" ] [ "\\n" "\\\"" "\\\\" "%%" ] '' - CF_API_KEY=${cfg.cloudflare.apiKey} - CLOUDFLARE_DNS_API_TOKEN=${cfg.cloudflare.apiKey} - CLOUDFLARE_ZONE_API_TOKEN=${cfg.cloudflare.apiKey} - ''; - rcloneConfig = builtins.replaceStrings [ "\n" "\"" "\\" "%" ] [ "\\n" "\\\"" "\\\\" "%%" ] '' - [backblaze] - type = b2 - account = ${cfg.backblaze.accountId} - key = ${cfg.backblaze.accountKey} - ''; in [ (if cfg.bitwarden.enable then "d /var/lib/bitwarden 0777 vaultwarden vaultwarden -" else "") (if cfg.bitwarden.enable then "d /var/lib/bitwarden/backup 0777 vaultwarden vaultwarden -" else "") (if cfg.pleroma.enable then "d /var/lib/pleroma 0700 pleroma pleroma - -" else "") "d /var/lib/restic 0600 restic - - -" - "f+ /var/lib/restic/pass 0400 restic - - ${resticPass}" - "f+ /root/.config/rclone/rclone.conf 0400 root root - ${rcloneConfig}" (if cfg.pleroma.enable then "f /var/lib/pleroma/secrets.exs 0755 pleroma pleroma - -" else "") "f+ /var/domain 0444 selfprivacy-api selfprivacy-api - ${domain}" - (if cfg.nextcloud.enable then "f+ /var/lib/nextcloud/db-pass 0440 nextcloud nextcloud - ${nextcloudDBPass}" else "") - (if cfg.nextcloud.enable then "f+ /var/lib/nextcloud/admin-pass 0440 nextcloud nextcloud - ${nextcloudAdminPass}" else "") - "f+ /var/lib/cloudflare/Credentials.ini 0440 nginx acmerecievers - ${cloudflareCredentials}" ]; + system.activationScripts = + let + jq = "${pkgs.jq}/bin/jq"; + sed = "${pkgs.gnused}/bin/sed"; + in + { + nextcloudSecrets = + if cfg.nextcloud.enable then '' + cat /etc/nixos/userdata/userdata.json | ${jq} -r '.nextcloud.databasePassword' > /var/lib/nextcloud/db-pass + chmod 0440 /var/lib/nextcloud/db-pass + chown nextcloud:nextcloud /var/lib/nextcloud/db-pass + + cat /etc/nixos/userdata/userdata.json | ${jq} -r '.nextcloud.adminPassword' > /var/lib/nextcloud/admin-pass + chmod 0440 /var/lib/nextcloud/admin-pass + chown nextcloud:nextcloud /var/lib/nextcloud/admin-pass + '' + else '' + rm -f /var/lib/nextcloud/db-pass + rm -f /var/lib/nextcloud/admin-pass + ''; + cloudflareCredentials = '' + mkdir -p /var/lib/cloudflare + chmod 0440 /var/lib/cloudflare + chown nginx:acmerecievers /var/lib/cloudflare + echo 'CF_API_KEY=REPLACEME' > /var/lib/cloudflare/Credentials.ini + echo 'CLOUDFLARE_DNS_API_TOKEN=REPLACEME' >> /var/lib/cloudflare/Credentials.ini + echo 'CLOUDFLARE_ZONE_API_TOKEN=REPLACEME' >> /var/lib/cloudflare/Credentials.ini + ${sed} -i "s/REPLACEME/$(cat /etc/nixos/userdata/userdata.json | ${jq} -r '.cloudflare.apiKey')/g" /var/lib/cloudflare/Credentials.ini + chmod 0440 /var/lib/cloudflare/Credentials.ini + chown nginx:acmerecievers /var/lib/cloudflare/Credentials.ini + ''; + resticCredentials = '' + mkdir -p /root/.config/rclone + chmod 0400 /root/.config/rclone + chown root:root /root/.config/rclone + echo '[backblaze]' > /root/.config/rclone/rclone.conf + echo 'type = b2' >> /root/.config/rclone/rclone.conf + echo 'account = REPLACEME1' >> /root/.config/rclone/rclone.conf + echo 'key = REPLACEME2' >> /root/.config/rclone/rclone.conf + + ${sed} -i "s/REPLACEME1/$(cat /etc/nixos/userdata/userdata.json | ${jq} -r '.backblaze.accountId')/g" /root/.config/rclone/rclone.conf + ${sed} -i "s/REPLACEME2/$(cat /etc/nixos/userdata/userdata.json | ${jq} -r '.backblaze.accountKey')/g" /root/.config/rclone/rclone.conf + + chmod 0400 /root/.config/rclone/rclone.conf + chown root:root /root/.config/rclone/rclone.conf + + cat /etc/nixos/userdata/userdata.json | ${jq} -r '.resticPassword' > /var/lib/restic/pass + chmod 0400 /var/lib/restic/pass + chown restic /var/lib/restic/pass + ''; + pleromaCredentials = + if cfg.pleroma.enable then '' + echo 'import Config' > /var/lib/pleroma/secrets.exs + echo 'config :pleroma, Pleroma.Repo,' >> /var/lib/pleroma/secrets.exs + echo ' password: "REPLACEME"' >> /var/lib/pleroma/secrets.exs + + ${sed} -i "s/REPLACEME/$(cat /etc/nixos/userdata/userdata.json | ${jq} -r '.databasePassword')/g" /var/lib/pleroma/secrets.exs + + chmod 0750 /var/lib/pleroma/secrets.exs + chown pleroma:pleroma /var/lib/pleroma/secrets.exs + '' else '' + rm -f /var/lib/pleroma/secrets.exs + ''; + }; } diff --git a/passmgr/bitwarden.nix b/passmgr/bitwarden.nix index 7730458..0a3ff6c 100644 --- a/passmgr/bitwarden.nix +++ b/passmgr/bitwarden.nix @@ -3,7 +3,7 @@ let cfg = config.services.userdata; in { - services.bitwarden_rs = { + services.vaultwarden = { enable = cfg.bitwarden.enable; dbBackend = "sqlite"; backupDir = "/var/lib/bitwarden/backup"; diff --git a/social/config.exs b/social/config.exs index 8782dea..340c4e8 100644 --- a/social/config.exs +++ b/social/config.exs @@ -22,9 +22,8 @@ config :pleroma, :media_proxy, config :pleroma, Pleroma.Repo, adapter: Ecto.Adapters.Postgres, username: "pleroma", - password: "$DB_PASSWORD", database: "pleroma", - hostname: "localhost", + socket_dir: "/run/postgresql", pool_size: 10 #config :web_push_encryption, :vapid_details, @@ -41,4 +40,4 @@ config :pleroma, :http_security, #config :joken, default_signer: "" -config :pleroma, configurable_from_database: false +config :pleroma, configurable_from_database: true diff --git a/social/pleroma-module.nix b/social/pleroma-module.nix deleted file mode 100644 index 45b4b9e..0000000 --- a/social/pleroma-module.nix +++ /dev/null @@ -1,133 +0,0 @@ -{ config, options, lib, pkgs, stdenv, ... }: -let - cfg = config.services.pleroma; -in -{ - options = { - services.pleroma = with lib; { - enable = mkEnableOption "pleroma"; - - package = mkOption { - type = types.package; - default = pkgs.pleroma-otp; - description = "Pleroma package to use."; - }; - - user = mkOption { - type = types.str; - default = "pleroma"; - description = "User account under which pleroma runs."; - }; - - group = mkOption { - type = types.str; - default = "pleroma"; - description = "Group account under which pleroma runs."; - }; - - stateDir = mkOption { - type = types.str; - default = "/var/lib/pleroma"; - readOnly = true; - description = "Directory where the pleroma service will save the uploads and static files."; - }; - - configs = mkOption { - type = with types; listOf str; - description = '' - Pleroma public configuration. - This list gets appended from left to - right into /etc/pleroma/config.exs. Elixir evaluates its - configuration imperatively, meaning you can override a - setting by appending a new str to this NixOS option list. - DO NOT STORE ANY PLEROMA SECRET - HERE, use - services.pleroma.secretConfigFile - instead. - This setting is going to be stored in a file part of - the Nix store. The Nix store being world-readable, it's not - the right place to store any secret - Have a look to Pleroma section in the NixOS manual for more - informations. - ''; - }; - - secretConfigFile = mkOption { - type = types.str; - default = "/var/lib/pleroma/secrets.exs"; - description = '' - Path to the file containing your secret pleroma configuration. - DO NOT POINT THIS OPTION TO THE NIX - STORE, the store being world-readable, it'll - compromise all your secrets. - ''; - }; - }; - }; - - config = lib.mkIf cfg.enable { - users = { - users."${cfg.user}" = { - description = "Pleroma user"; - home = cfg.stateDir; - extraGroups = [ cfg.group ]; - }; - groups."${cfg.group}" = { }; - }; - - environment.systemPackages = [ cfg.package ]; - - environment.etc."/pleroma/config.exs".text = '' - ${lib.concatMapStrings (x: "${x}") cfg.configs} - # The lau/tzdata library is trying to download the latest - # timezone database in the OTP priv directory by default. - # This directory being in the store, it's read-only. - # Setting that up to a more appropriate location. - config :tzdata, :data_dir, "/var/lib/pleroma/elixir_tzdata_data" - import_config "${cfg.secretConfigFile}" - ''; - - systemd.services.pleroma = { - description = "Pleroma social network"; - after = [ "network-online.target" "postgresql.service" ]; - wantedBy = [ "multi-user.target" ]; - restartTriggers = [ config.environment.etc."/pleroma/config.exs".source ]; - serviceConfig = { - User = cfg.user; - Group = cfg.group; - Type = "exec"; - WorkingDirectory = "~"; - StateDirectory = "pleroma pleroma/static pleroma/uploads"; - StateDirectoryMode = "700"; - - # Checking the conf file is there then running the database - # migration before each service start, just in case there are - # some pending ones. - # - # It's sub-optimal as we'll always run this, even if pleroma - # has not been updated. But the no-op process is pretty fast. - # Better be safe than sorry migration-wise. - ExecStartPre = - let preScript = pkgs.writers.writeBashBin "pleromaStartPre" - "${cfg.package}/bin/pleroma_ctl migrate"; - in "${preScript}/bin/pleromaStartPre"; - - ExecStart = "${cfg.package}/bin/pleroma start"; - ExecStop = "${cfg.package}/bin/pleroma stop"; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; - - # Systemd sandboxing directives. - # Taken from the upstream contrib systemd service at - # pleroma/installation/pleroma.service - PrivateTmp = true; - ProtectHome = true; - ProtectSystem = "full"; - PrivateDevices = false; - NoNewPrivileges = true; - CapabilityBoundingSet = "~CAP_SYS_ADMIN"; - }; - }; - - }; - meta.maintainers = with lib.maintainers; [ ninjatrappeur ]; -} diff --git a/social/pleroma-package.nix b/social/pleroma-package.nix deleted file mode 100644 index 38f41e9..0000000 --- a/social/pleroma-package.nix +++ /dev/null @@ -1,69 +0,0 @@ -{ lib -, stdenv -, autoPatchelfHook -, fetchurl -, file -, makeWrapper -, ncurses -, nixosTests -, openssl -, unzip -, zlib -}: -stdenv.mkDerivation { - pname = "pleroma-otp"; - version = "2.3.0"; - - # To find the latest binary release stable link, have a look at - # the CI pipeline for the latest commit of the stable branch - # https://git.pleroma.social/pleroma/pleroma/-/tree/stable - src = { - aarch64-linux = fetchurl { - url = "https://git.pleroma.social/pleroma/pleroma/-/jobs/182392/artifacts/download"; - sha256 = "1drpd6xh7m2damxi5impb8jwvjl6m3qv5yxynl12i8g66vi3rbwf"; - }; - x86_64-linux = fetchurl { - url = "https://git.pleroma.social/pleroma/pleroma/-/jobs/182388/artifacts/download"; - sha256 = "1c6l04gga9iigm249ywwcrjg6wzy8iiid652mws3j9dnl71w2sim"; - }; - }."${stdenv.hostPlatform.system}"; - - nativeBuildInputs = [ unzip ]; - - buildInputs = [ - autoPatchelfHook - file - makeWrapper - ncurses - openssl - zlib - ]; - - # mkDerivation fails to detect the zip nature of $src due to the - # missing .zip extension. - # Let's unpack the archive explicitely. - unpackCmd = "unzip $curSrc"; - - installPhase = '' - mkdir $out - cp -r * $out''; - - # Pleroma is using the project's root path (here the store path) - # as its TMPDIR. - # Patching it to move the tmp dir to the actual tmpdir - postFixup = '' - wrapProgram $out/bin/pleroma --set-default RELEASE_TMP "/tmp" - wrapProgram $out/bin/pleroma_ctl --set-default RELEASE_TMP "/tmp"''; - - passthru.tests = { - pleroma = nixosTests.pleroma; - }; - - meta = with lib; { - description = "ActivityPub microblogging server"; - homepage = https://git.pleroma.social/pleroma/pleroma; - license = licenses.agpl3; - maintainers = with maintainers; [ ninjatrappeur ]; - platforms = [ "x86_64-linux" "aarch64-linux" ]; - }; -} diff --git a/social/pleroma.nix b/social/pleroma.nix index 49f59ad..9ffb88c 100644 --- a/social/pleroma.nix +++ b/social/pleroma.nix @@ -3,11 +3,6 @@ let cfg = config.services.userdata; in { - nixpkgs.overlays = [ - (self: super: { - pleroma-otp = self.callPackage ./pleroma-package.nix { }; - }) - ]; services = { pleroma = { enable = cfg.pleroma.enable; @@ -15,8 +10,8 @@ in group = "pleroma"; configs = [ (builtins.replaceStrings - [ "$DOMAIN" "$LUSER" "$DB_PASSWORD" ] - [ cfg.domain cfg.username cfg.databasePassword ] + [ "$DOMAIN" "$LUSER" ] + [ cfg.domain cfg.username ] (builtins.readFile ./config.exs)) ]; }; @@ -24,10 +19,21 @@ in enable = true; package = pkgs.postgresql_12; initialScript = "/etc/setup.psql"; + ensureDatabases = [ + "pleroma" + ]; + ensureUsers = [ + { + name = "pleroma"; + ensurePermissions = { + "DATABASE pleroma" = "ALL PRIVILEGES"; + }; + } + ]; }; }; environment.etc."setup.psql".text = '' - CREATE USER pleroma WITH ENCRYPTED PASSWORD '${cfg.databasePassword}'; + CREATE USER pleroma; CREATE DATABASE pleroma OWNER pleroma; \c pleroma; --Extensions made by ecto.migrate that need superuser access diff --git a/userdata/tokens_schema.json b/userdata/tokens_schema.json new file mode 100644 index 0000000..2e85065 --- /dev/null +++ b/userdata/tokens_schema.json @@ -0,0 +1,72 @@ +{ + "$schema": "http://json-schema.org/schema#", + "$id": "https://git.selfprivacy.org/inex/selfprivacy-nixos-config/raw/branch/master/userdata/tokens_schema.json", + "type": "object", + "properties": { + "tokens": { + "type": "array", + "items": { + "type": "object", + "properties": { + "token": { + "type": "string" + }, + "name": { + "type": "string" + }, + "date": { + "type": "string" + } + }, + "required": [ + "token", + "name", + "date" + ] + } + }, + "recovery_token": { + "type": "object", + "properties": { + "token": { + "type": "string" + }, + "date": { + "type": "string" + }, + "expiration": { + "type": "string" + }, + "uses_left": { + "type": "integer" + } + }, + "required": [ + "token", + "date" + ] + }, + "new_device": { + "type": "object", + "properties": { + "token": { + "type": "string" + }, + "date": { + "type": "string" + }, + "expiration": { + "type": "string" + } + }, + "required": [ + "token", + "date", + "expiration" + ] + } + }, + "required": [ + "tokens" + ] +} \ No newline at end of file diff --git a/users.nix b/users.nix index a3128b2..285d89b 100644 --- a/users.nix +++ b/users.nix @@ -17,7 +17,7 @@ in value = { isNormalUser = true; hashedPassword = user.hashedPassword; - openssh.authorizedKeys.keys = (if user ? sshKeys then user.sshKeys else []); + openssh.authorizedKeys.keys = (if user ? sshKeys then user.sshKeys else [ ]); }; }) cfg.users); diff --git a/variables-module.nix b/variables-module.nix index 47acdfb..2b0a7ea 100644 --- a/variables-module.nix +++ b/variables-module.nix @@ -11,10 +11,6 @@ let in { options.services.userdata = { - enable = mkOption { - default = true; - type = types.nullOr types.bool; - }; # General server options hostname = mkOption { description = "The hostname of the server."; @@ -71,12 +67,6 @@ in # API options # ############### api = { - token = mkOption { - description = '' - API token used to authenticate with the server - ''; - type = types.nullOr types.str; - }; enableSwagger = mkOption { default = true; description = '' @@ -100,30 +90,10 @@ in description = "Bucket name used for userdata backups"; type = types.nullOr types.str; }; - accountId = mkOption { - description = "Backblaze B2 Account ID"; - type = types.nullOr types.str; - }; - accountKey = mkOption { - description = "Backblaze B2 Account Key."; - type = types.nullOr types.str; - }; - }; - cloudflare = { - apiKey = mkOption { - description = "Cloudflare API Key."; - type = types.nullOr types.str; - }; }; ############## # Services # ############## - databasePassword = mkOption { - description = '' - Password for the database - ''; - type = types.nullOr types.str; - }; bitwarden = { enable = mkOption { default = false; @@ -141,18 +111,6 @@ in default = true; type = types.nullOr types.bool; }; - databasePassword = mkOption { - description = '' - Password for the nextcloud database - ''; - type = types.nullOr types.str; - }; - adminPassword = mkOption { - description = '' - Password for the nextcloud admin user - ''; - type = types.nullOr types.str; - }; }; pleroma = { enable = mkOption { @@ -172,15 +130,6 @@ in type = types.nullOr types.bool; }; }; - ############# - # Backups # - ############# - resticPassword = mkOption { - description = '' - Password for the restic - ''; - type = types.nullOr types.str; - }; ######### # SSH # ######### diff --git a/variables.nix b/variables.nix index 79fb145..32bd57c 100644 --- a/variables.nix +++ b/variables.nix @@ -1,6 +1,49 @@ -{ pkgs, ... }: +{ pkgs, lib, ... }: +let + jsonData = builtins.fromJSON (builtins.readFile ./userdata/userdata.json); +in { - services = { - userdata = builtins.fromJSON (builtins.readFile ./userdata/userdata.json); + services.userdata = { + hostname = lib.attrsets.attrByPath [ "hostname" ] null jsonData; + domain = lib.attrsets.attrByPath [ "domain" ] null jsonData; + timezone = lib.attrsets.attrByPath [ "timezone" ] "Europe/Uzhgorod" jsonData; + autoUpgrade = { + enable = lib.attrsets.attrByPath [ "autoUpgrade" "enable" ] true jsonData; + allowReboot = lib.attrsets.attrByPath [ "autoUpgrade" "allowReboot" ] true jsonData; + }; + username = lib.attrsets.attrByPath [ "username" ] null jsonData; + hashedMasterPassword = lib.attrsets.attrByPath [ "hashedMasterPassword" ] null jsonData; + sshKeys = lib.attrsets.attrByPath [ "sshKeys" ] [ ] jsonData; + api = { + enableSwagger = lib.attrsets.attrByPath [ "api" "enableSwagger" ] false jsonData; + skippedMigrations = lib.attrsets.attrByPath [ "api" "skippedMigrations" ] [ ] jsonData; + }; + backblaze = { + bucket = lib.attrsets.attrByPath [ "backblaze" "bucket" ] "" jsonData; + }; + bitwarden = { + enable = lib.attrsets.attrByPath [ "bitwarden" "enable" ] false jsonData; + }; + gitea = { + enable = lib.attrsets.attrByPath [ "gitea" "enable" ] false jsonData; + }; + nextcloud = { + enable = lib.attrsets.attrByPath [ "nextcloud" "enable" ] false jsonData; + }; + pleroma = { + enable = lib.attrsets.attrByPath [ "pleroma" "enable" ] false jsonData; + }; + jitsi = { + enable = lib.attrsets.attrByPath [ "jitsi" "enable" ] false jsonData; + }; + ocserv = { + enable = lib.attrsets.attrByPath [ "ocserv" "enable" ] false jsonData; + }; + ssh = { + enable = lib.attrsets.attrByPath [ "ssh" "enable" ] true jsonData; + rootKeys = lib.attrsets.attrByPath [ "ssh" "rootKeys" ] [ "" ] jsonData; + passwordAuthentication = lib.attrsets.attrByPath [ "ssh" "passwordAuthentication" ] true jsonData; + }; + users = lib.attrsets.attrByPath [ "users" ] [ ] jsonData; }; }