From 8ee4ea7fe14398e78f236882e60f6fc5358b5ca5 Mon Sep 17 00:00:00 2001 From: DashieTM Date: Sun, 4 Aug 2024 13:59:13 +0200 Subject: [PATCH] Add server configuration --- flake.nix | 18 +- hardware/marmo/configuration.nix | 11 +- hardware/overheating/configuration.nix | 19 +- hardware/server/configuration.nix | 345 +++++++++++++++++++++ hardware/server/hardware-configuration.nix | 62 ++++ hardware/server/mautrix-discord.nix | 250 +++++++++++++++ hardware/server/mautrix-whatsapp.nix | 250 +++++++++++++++ hardware/spaceship/configuration.nix | 4 +- modules/programs/nextcloud.nix | 2 +- programs/individual_configs/firefox.nix | 20 +- programs/individual_configs/keepass.nix | 8 +- programs/sync.nix | 5 +- secrets/secrets.yaml | 11 +- 13 files changed, 988 insertions(+), 17 deletions(-) create mode 100644 hardware/server/configuration.nix create mode 100644 hardware/server/hardware-configuration.nix create mode 100644 hardware/server/mautrix-discord.nix create mode 100644 hardware/server/mautrix-whatsapp.nix diff --git a/flake.nix b/flake.nix index 2793a63..f392710 100644 --- a/flake.nix +++ b/flake.nix @@ -4,6 +4,7 @@ inputs = { nixpkgs.url = "github:NixOs/nixpkgs/nixos-unstable"; + stable.url = "github:NixOs/nixpkgs/nixos-24.05"; nix-flatpak = { url = "github:gmodena/nix-flatpak"; @@ -50,6 +51,12 @@ outputs = { ... }@inputs: let + stable = import inputs.stable { + system = "x86_64-linux"; + config = { + allowUnfree = true; + }; + }; pkgs = import inputs.nixpkgs { system = "x86_64-linux"; overlays = [ @@ -62,7 +69,16 @@ dashielib = import ./lib { inherit inputs pkgs; }; in { - nixosConfigurations = (dashielib.build_systems [ "marmo" "overheating" "spaceship" ]); + nixosConfigurations = (dashielib.build_systems [ "marmo" "overheating" "spaceship" ]) // { + server = { + specialArgs = { + inherit inputs; pkgs = stable; + }; + modules = [ + ./hardware/server/configuration.nix + ]; + }; + }; }; nixConfig = { diff --git a/hardware/marmo/configuration.nix b/hardware/marmo/configuration.nix index 4327b2e..b9b90b2 100644 --- a/hardware/marmo/configuration.nix +++ b/hardware/marmo/configuration.nix @@ -1,4 +1,4 @@ -{ +{ config, ... }: { imports = [ ../../modules ]; @@ -25,5 +25,14 @@ greetd = { resolution = "3440x1440@180"; }; + nextcloud = { + synclist = [ + { + name = "document_sync"; + remote = "/Documents"; + local = "/home/${config.conf.username}/Documents"; + } + ]; + }; }; } diff --git a/hardware/overheating/configuration.nix b/hardware/overheating/configuration.nix index f2df9ca..5c4380d 100644 --- a/hardware/overheating/configuration.nix +++ b/hardware/overheating/configuration.nix @@ -1,4 +1,4 @@ -{ +{ config, ... }: { imports = [ ../../modules/conf.nix ]; @@ -30,5 +30,18 @@ greetd = { resolution = "3440x1440@180"; }; - }; -} + nextcloud = { + synclist = [ + { + name = "document_sync"; + remote = "/Documents"; + local = "/home/${config.conf.username}/Documents"; + } + { + name = "picture_sync"; + remote = "/Pictures"; + local = "/home/${config.conf.username}/Pictures"; + } + ]; + }; + } diff --git a/hardware/server/configuration.nix b/hardware/server/configuration.nix new file mode 100644 index 0000000..34c6bbe --- /dev/null +++ b/hardware/server/configuration.nix @@ -0,0 +1,345 @@ +{ config, pkgs, ... }: +let + nextcloud_pw = (builtins.readFile config.sops.secrets.nextcloud_server.path); + forgejo_pw = (builtins.readFile config.sops.secrets.forgejo_server.path); + matrix_pw = (builtins.readFile config.sops.secrets.matrix_server.path); + mautrix_signal_pw = (builtins.readFile config.sops.secrets.mautrix_signal_server.path); + mautrix_whatsapp_pw = (builtins.readFile config.sops.secrets.mautrix_whatsapp_server.path); + mautrix_discord_pw = (builtins.readFile config.sops.secrets.mautrix_discord_server.path); + + fqdn = "matrix.${config.networking.domain}"; + baseUrl = "https://${fqdn}"; + clientConfig."m.homeserver".base_url = baseUrl; + serverConfig."m.server" = "${fqdn}:443"; + mkWellKnown = data: '' + default_type application/json; + add_header Access-Control-Allow-Origin *; + return 200 '${builtins.toJSON data}'; + ''; +in +{ + networking.hostName = "dashie"; + networking.domain = "dashie.org"; + imports = [ + ./hardware-configuration.nix + ./mautrix-whatsapp.nix + ./mautrix-discord.nix + ]; + + sops = { + gnupg = { + home = "~/.gnupg"; + sshKeyPaths = [ ]; + }; + defaultSopsFile = ../../secrets/secrets.yaml; + secrets.nextcloud_server = { }; + secrets.nextcloud_admin = { }; + secrets.forgejo_server = { }; + secrets.matrix_server = { }; + secrets.mautrix_signal_server = { }; + secrets.mautrix_whatsapp_server = { }; + secrets.mautrix_discord_server = { }; + }; + + # Use the systemd-boot EFI boot loader. + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + boot.supportedFilesystems = [ "ntfs" ]; + + + # Set your time zone. + time.timeZone = "Europe/Zurich"; + + # Define a user account. Don't forget to set a password with ‘passwd’. + users.users.root.hashedPassword = "!"; + users.users.dashie = { + isNormalUser = true; + extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user. + packages = with pkgs; [ + neovim + fuse + ntfs3g + rsync + ]; + openssh.authorizedKeys.keyFiles = [ + /home/dashie/server.pub + ]; + }; + + services.openssh = { + enable = true; + settings.PasswordAuthentication = false; + }; + services.mautrix-whatsapp-dashie.enable = true; + services.mautrix-discord-dashie.enable = true; + services.matrix-synapse.enable = true; + services.mautrix-signal.enable = true; + services.matrix-synapse.settings = { + server_name = "matrix.dashie.org"; + database.name = "psycopg2"; + database.args.user = "matrix-synapse"; + database.args.password = "${matrix_pw}"; + public_baseurl = "https://matrix.dashie.org"; + enable_registration = true; + enable_registration_without_verification = true; + suppress_key_server_warning = true; + max_upload_size = "1G"; + listeners = [ + { + port = 8008; + bind_addresses = [ "::1" ]; + type = "http"; + tls = false; + x_forwarded = true; + resources = [ + { + names = [ "client" "federation" ]; + compress = true; + } + ]; + } + ]; + }; + services.mautrix-whatsapp-dashie.settings = { + appservice = { + id = "whatsapp"; + database = { + type = "postgres"; + uri = "postgresql:///mautrix_whatsapp?host=/run/postgresql&sslmode=disable&user=mautrix_whatsapp&password=${mautrix_whatsapp_pw}"; + }; + }; + bridge = { + encryption = { + allow = true; + default = true; + required = true; + }; + displayname_template = "{{if .BusinessName}}{{.BusinessName}}{{else if .PushName}}{{.PushName}}{{else}}{{.JID}}{{end}}"; + permissions = { + "@fabio.lenherr:matrix.org" = "admin"; + "@dashie:matrix.dashie.org" = "admin"; + }; + }; + }; + services.mautrix-signal.settings = { + appservice = { + id = "signal"; + database = { + type = "postgres"; + uri = "postgresql:///mautrix_signal?host=/run/postgresql&sslmode=disable&user=mautrix_signal&password=${mautrix_signal_pw}"; + }; + }; + bridge = { + encryption = { + allow = true; + default = true; + required = true; + }; + displayname_template = "{{or .ProfileName .PhoneNumber \"Unknown user\"}}"; + permissions = { + "@fabio.lenherr:matrix.org" = "admin"; + "@dashie:matrix.dashie.org" = "admin"; + }; + }; + }; + services.mautrix-discord-dashie.settings = { + appservice = { + id = "discord"; + database = { + type = "postgres"; + uri = "postgresql:///mautrix_discord?host=/run/postgresql&sslmode=disable&user=mautrix_discord&password=${mautrix_discord_pw}"; + }; + }; + bridge = { + displayname_template = "{{or .GlobalName .Username}}{{if .Bot}} (bot){{end}}"; + permissions = { + "@fabio.lenherr:matrix.org" = "admin"; + "@dashie:matrix.dashie.org" = "admin"; + }; + }; + }; + services.nginx = { + enable = true; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + }; + services.nginx.virtualHosts."dashie.org" = { + addSSL = true; + enableACME = true; + root = "/var/www/dashie.org/"; + }; + security.acme.certs."dashie.org".extraDomainNames = [ "cloud.dashie.org" "matrix.dashie.org" "git.dashie.org" "navi.dashie.org" ]; + services.nginx.virtualHosts."cloud.dashie.org" = { + addSSL = true; + enableACME = true; + locations."/*".proxyPass = "http://127.0.0.1:12002"; + }; + services.nginx.virtualHosts."git.dashie.org" = { + forceSSL = true; + enableACME = true; + locations."/".proxyPass = "http://127.0.0.1:3000"; + }; + services.nginx.virtualHosts."navi.dashie.org" = { + addSSL = true; + enableACME = true; + locations."/".proxyPass = "http://127.0.0.1:4533"; + }; + + services.nginx.virtualHosts."localhost" = { + listen = [ + { + addr = "0.0.0.0"; + port = 8448; + } + ]; + locations."/".proxyPass = "http://[::1]:8008"; + }; + + services.nginx.virtualHosts."matrix.dashie.org" = { + #addSSL = true; + forceSSL = true; + enableACME = true; + #locations."*" = { + locations."/".extraConfig = '' + return 404; + ''; + locations."/_matrix" = { + proxyPass = "http://[::1]:8008"; + }; + locations."/_synapse/client".proxyPass = "http://[::1]:8008"; + + locations."= /.well-known/matrix/server".extraConfig = mkWellKnown serverConfig; + # This is usually needed for homeserver discovery (from e.g. other Matrix clients). + # Further reference can be found in the upstream docs at + # https://spec.matrix.org/latest/client-server-api/#getwell-knownmatrixclient + locations."= /.well-known/matrix/client".extraConfig = mkWellKnown clientConfig; + extraConfig = + "client_max_body_size 2G;" + ; + }; + + services.nextcloud.enable = true; + services.nextcloud.hostName = "cloud.dashie.org"; + services.nextcloud.https = true; + services.nextcloud.config = { + adminpassFile = "${config.sops.nextcloud_admin.path}"; + dbuser = "nextcloud"; + dbhost = "/run/postgresql"; + dbname = "nextcloud"; + dbtype = "pgsql"; + dbpassFile = "${config.sops.secrets.nextcloud_server.path}"; + }; + services.nextcloud.settings = { + port = 12001; + trusted_domains = [ "cloud.dashie.org" "192.168.1.23" ]; + }; + services.forgejo = { + enable = true; + database.passwordFile = ./dbpw/forgejo; + settings = { + server.DOMAIN = "git.dashie.org"; + server.SSH_PORT = 12008; + server.SSH_LISTEN_PORT = 12008; + server.START_SSH_SERVER = true; + service.DISABLE_REGISTRATION = true; + }; + }; + services.navidrome.enable = true; + services.navidrome.settings = { + MusicFolder = "/var/lib/nextcloud/data/DashieTM/files/Share/Music"; + }; + systemd.services."nextcloud-setup" = { + requires = [ "postgresql.service" ]; + after = [ "postgresql.service" ]; + }; + services.postgresql = { + enable = true; + authentication = pkgs.lib.mkOverride 10 '' + #type database DBuser auth-method + local all all trust + host all all 127.0.0.1/32 trust + host all all ::1/128 trust + ''; + initialScript = pkgs.writeText "backend-initScript" '' + CREATE DATABASE nextcloud; + CREATE USER nextcloud WITH ENCRYPTED PASSWORD '${nextcloud_pw}'; + GRANT ALL PRIVILEGES ON DATABASE nextcloud TO nextcloud; + + CREATE DATABASE forgejo; + CREATE USER forgejo WITH ENCRYPTED PASSWORD '${forgejo_pw}'; + GRANT ALL PRIVILEGES ON DATABASE forgejo TO forgejo; + + + CREATE USER "matrix-synapse" WITH ENCRYPTED PASSWORD '${matrix_pw}' + SELECT 'CREATE DATABASE "matrix-synapse" LOCALE "C" ENCODING UTF8 TEMPLATE template0 OWNER "matrix-synapse"' + WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'matrix-synapse')\gexec + + CREATE USER mautrix_whatsapp WITH ENCRYPTED PASSWORD '${mautrix_whatsapp_pw}' + SELECT 'CREATE DATABASE "mautrix_whatsapp" LOCALE "C" ENCODING UTF8 TEMPLATE template0 OWNER "mautrix_whatsapp"' + WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mautrix_whatsapp')\gexec + + CREATE USER mautrix_signal WITH ENCRYPTED PASSWORD '${mautrix_signal_pw}' + SELECT 'CREATE DATABASE "mautrix_signal" LOCALE "C" ENCODING UTF8 TEMPLATE template0 OWNER "mautrix_signal"' + WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mautrix_signal')\gexec + + CREATE USER mautrix_discord WITH ENCRYPTED PASSWORD '${mautrix_discord_pw}' + SELECT 'CREATE DATABASE "mautrix_discord" LOCALE "C" ENCODING UTF8 TEMPLATE template0 OWNER "mautrix_discord"' + WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mautrix_discord')\gexec + ''; + }; + security.acme = { + acceptTerms = true; + defaults.email = "fabio.lenherr@gmail.com"; + }; + + networking.firewall = { + enable = true; + allowedTCPPorts = [ 22 80 443 4534 8448 12002 12004 12006 12008 ]; + }; + networking.firewall.allowPing = true; + services.samba = { + enable = true; + securityType = "user"; + openFirewall = true; + extraConfig = '' + workgroup = WORKGROUP + server string = smbnix + netbios name = smbnix + security = user + #use sendfile = yes + max protocol = smb3 + # note: localhost is the ipv6 localhost ::1 + hosts allow = 192.168.1. 127.0.0.1 localhost + hosts deny = 0.0.0.0/0 + guest account = nobody + map to guest = bad user + ''; + shares = { + public = { + path = "/mnt/Shares/Public"; + browseable = "yes"; + "read only" = "no"; + "guest ok" = "yes"; + "create mask" = "0644"; + "directory mask" = "0755"; + }; + }; + }; + services.samba-wsdd = { + enable = true; + openFirewall = true; + }; + services.cron = { + enable = true; + systemCronJobs = [ + "0 4 * * FRI nobody rsync -ato /var/lib/nextcloud/data /mnt/dump3/nextcloud" + "0 4 * * FRI nobody pg_dympall > /mnt/dump3/sqdump.sql" + ]; + }; + + hardware.cpu.intel.updateMicrocode = true; + system.stateVersion = "24.05"; +} + diff --git a/hardware/server/hardware-configuration.nix b/hardware/server/hardware-configuration.nix new file mode 100644 index 0000000..abbea50 --- /dev/null +++ b/hardware/server/hardware-configuration.nix @@ -0,0 +1,62 @@ +{ config, lib, modulesPath, ... }: { + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { + device = "/dev/disk/by-uuid/678ecbd1-a5ce-4530-a959-ffb48f76aa43"; + fsType = "btrfs"; + }; + + fileSystems."/var/lib/nextcloud" = + { + device = "/dev/disk/by-label/nextcloud"; + fsType = "ext4"; + }; + + fileSystems."/mnt/dump3" = + { + device = "/dev/disk/by-label/backup"; + fsType = "ext4"; + }; + + fileSystems."/mnt/dump1" = + { + device = "/dev/disk/by-uuid/CC60532860531912"; + fsType = "ntfs-3g"; + options = [ "rw" "uid=1000" ]; + }; + + fileSystems."/mnt/dump2" = + { + device = "/dev/disk/by-uuid/F46896AE68966EDC"; + fsType = "ntfs-3g"; + options = [ "rw" "uid=1000" ]; + }; + + fileSystems."/boot" = + { + device = "/dev/disk/by-uuid/B7BE-AB1C"; + fsType = "vfat"; + options = [ "fmask=0022" "dmask=0022" ]; + }; + + swapDevices = + [{ device = "/dev/disk/by-uuid/832dce11-b4c4-476c-ab28-bd98275a542c"; }]; + + # 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..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.enp0s31f6.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/hardware/server/mautrix-discord.nix b/hardware/server/mautrix-discord.nix new file mode 100644 index 0000000..99488dc --- /dev/null +++ b/hardware/server/mautrix-discord.nix @@ -0,0 +1,250 @@ +{ lib +, config +, pkgs +, ... +}: +let + cfg = config.services.mautrix-discord-dashie; + dataDir = "/var/lib/mautrix-discord"; + registrationFile = "${dataDir}/discord-registration.yaml"; + settingsFile = "${dataDir}/config.yaml"; + settingsFileUnsubstituted = settingsFormat.generate "mautrix-discord-config-unsubstituted.json" cfg.settings; + settingsFormat = pkgs.formats.json { }; + appservicePort = 29334; + + # to be used with a list of lib.mkIf values + optOneOf = lib.lists.findFirst (value: value.condition) (lib.mkIf false null); + mkDefaults = lib.mapAttrsRecursive (n: v: lib.mkDefault v); + defaultConfig = { + homeserver.address = "http://localhost:8448"; + appservice = { + hostname = "[::]"; + port = appservicePort; + database.type = "sqlite3"; + database.uri = "file:${dataDir}/mautrix-discord.db?_txlock=immediate"; + id = "discord"; + bot = { + username = "discordbot"; + displayname = "Discord Bridge Bot"; + }; + as_token = ""; + hs_token = ""; + }; + bridge = { + username_template = "discord_{{.}}"; + displayname_template = "{{or .ProfileName .PhoneNumber \"Unknown user\"}}"; + double_puppet_server_map = { }; + login_shared_secret_map = { }; + command_prefix = "!discord"; + permissions."*" = "relay"; + relay.enabled = true; + }; + logging = { + min_level = "info"; + writers = lib.singleton { + type = "stdout"; + format = "pretty-colored"; + time_format = " "; + }; + }; + }; + +in +{ + options.services.mautrix-discord-dashie = { + enable = lib.mkEnableOption "mautrix-discord, a Matrix-Discord puppeting bridge."; + + settings = lib.mkOption { + apply = lib.recursiveUpdate defaultConfig; + type = settingsFormat.type; + default = defaultConfig; + description = '' + {file}`config.yaml` configuration as a Nix attribute set. + Configuration options should match those described in + [example-config.yaml](https://github.com/mautrix/discord/blob/master/example-config.yaml). + Secret tokens should be specified using {option}`environmentFile` + instead of this world-readable attribute set. + ''; + example = { + appservice = { + database = { + type = "postgres"; + uri = "postgresql:///mautrix_discord?host=/run/postgresql"; + }; + id = "discord"; + ephemeral_events = false; + }; + bridge = { + history_sync = { + request_full_sync = true; + }; + private_chat_portal_meta = true; + mute_bridging = true; + encryption = { + allow = true; + default = true; + require = true; + }; + provisioning = { + shared_secret = "disable"; + }; + permissions = { + "example.com" = "user"; + }; + }; + }; + }; + + environmentFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = '' + File containing environment variables to be passed to the mautrix-discord service. + If an environment variable `MAUTRIX_DISCORD_BRIDGE_LOGIN_SHARED_SECRET` is set, + then its value will be used in the configuration file for the option + `login_shared_secret_map` without leaking it to the store, using the configured + `homeserver.domain` as key. + See [here](https://github.com/mautrix/discord/blob/main/example-config.yaml) + for the documentation of `login_shared_secret_map`. + ''; + }; + + serviceDependencies = lib.mkOption { + type = with lib.types; listOf str; + default = (lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit) + ++ (lib.optional config.services.matrix-conduit.enable "conduit.service"); + defaultText = lib.literalExpression '' + (optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit) + ++ (optional config.services.matrix-conduit.enable "conduit.service") + ''; + description = '' + List of systemd units to require and wait for when starting the application service. + ''; + }; + + registerToSynapse = lib.mkOption { + type = lib.types.bool; + default = config.services.matrix-synapse.enable; + defaultText = lib.literalExpression '' + config.services.matrix-synapse.enable + ''; + description = '' + Whether to add the bridge's app service registration file to + `services.matrix-synapse.settings.app_service_config_files`. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + + users.users.mautrix-discord = { + isSystemUser = true; + group = "mautrix-discord"; + home = dataDir; + description = "mautrix-discord bridge user"; + }; + + users.groups.mautrix-discord = { }; + + services.matrix-synapse = lib.mkIf cfg.registerToSynapse { + settings.app_service_config_files = [ registrationFile ]; + }; + systemd.services.matrix-synapse = lib.mkIf cfg.registerToSynapse { + serviceConfig.SupplementaryGroups = [ "mautrix-discord" ]; + }; + + # Note: this is defined here to avoid the docs depending on `config` + services.mautrix-discord-dashie.settings.homeserver = optOneOf (with config.services; [ + (lib.mkIf matrix-synapse.enable (mkDefaults { + domain = matrix-synapse.settings.server_name; + })) + (lib.mkIf matrix-conduit.enable (mkDefaults { + domain = matrix-conduit.settings.global.server_name; + address = "http://localhost:${toString matrix-conduit.settings.global.port}"; + })) + ]); + + systemd.services.mautrix-discord-dashie = { + description = "mautrix-discord, a Matrix-Discord puppeting bridge."; + + wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ] ++ cfg.serviceDependencies; + after = [ "network-online.target" ] ++ cfg.serviceDependencies; + # ffmpeg is required for conversion of voice messages + path = [ pkgs.ffmpeg-headless ]; + + preStart = '' + # substitute the settings file by environment variables + # in this case read from EnvironmentFile + test -f '${settingsFile}' && rm -f '${settingsFile}' + old_umask=$(umask) + umask 0177 + ${pkgs.envsubst}/bin/envsubst \ + -o '${settingsFile}' \ + -i '${settingsFileUnsubstituted}' + umask $old_umask + + # generate the appservice's registration file if absent + if [ ! -f '${registrationFile}' ]; then + ${pkgs.mautrix-discord}/bin/mautrix-discord \ + --generate-registration \ + --config='${settingsFile}' \ + --registration='${registrationFile}' + fi + chmod 640 ${registrationFile} + + umask 0177 + # 1. Overwrite registration tokens in config + # 2. If environment variable MAUTRIX_DISCORD_BRIDGE_LOGIN_SHARED_SECRET + # is set, set it as the login shared secret value for the configured + # homeserver domain. + ${pkgs.yq}/bin/yq -s '.[0].appservice.as_token = .[1].as_token + | .[0].appservice.hs_token = .[1].hs_token + | .[0] + | if env.MAUTRIX_DISCORD_BRIDGE_LOGIN_SHARED_SECRET then .bridge.login_shared_secret_map.[.homeserver.domain] = env.MAUTRIX_DISCORD_BRIDGE_LOGIN_SHARED_SECRET else . end' \ + '${settingsFile}' '${registrationFile}' > '${settingsFile}.tmp' + mv '${settingsFile}.tmp' '${settingsFile}' + umask $old_umask + ''; + + serviceConfig = { + User = "mautrix-discord"; + Group = "mautrix-discord"; + EnvironmentFile = cfg.environmentFile; + StateDirectory = baseNameOf dataDir; + WorkingDirectory = dataDir; + ExecStart = '' + ${pkgs.mautrix-discord}/bin/mautrix-discord \ + --config='${settingsFile}' \ + --registration='${registrationFile}' + ''; + 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"; + Restart = "on-failure"; + RestartSec = "30s"; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallErrorNumber = "EPERM"; + SystemCallFilter = [ "@system-service" ]; + Type = "simple"; + UMask = 0027; + }; + restartTriggers = [ settingsFileUnsubstituted ]; + }; + }; + meta.maintainers = with lib.maintainers; [ niklaskorz ]; +} + diff --git a/hardware/server/mautrix-whatsapp.nix b/hardware/server/mautrix-whatsapp.nix new file mode 100644 index 0000000..5c6832b --- /dev/null +++ b/hardware/server/mautrix-whatsapp.nix @@ -0,0 +1,250 @@ +{ lib +, config +, pkgs +, ... +}: +let + cfg = config.services.mautrix-whatsapp-dashie; + dataDir = "/var/lib/mautrix-whatsapp"; + registrationFile = "${dataDir}/whatsapp-registration.yaml"; + settingsFile = "${dataDir}/config.yaml"; + settingsFileUnsubstituted = settingsFormat.generate "mautrix-whatsapp-config-unsubstituted.json" cfg.settings; + settingsFormat = pkgs.formats.json { }; + appservicePort = 29318; + + # to be used with a list of lib.mkIf values + optOneOf = lib.lists.findFirst (value: value.condition) (lib.mkIf false null); + mkDefaults = lib.mapAttrsRecursive (n: v: lib.mkDefault v); + defaultConfig = { + homeserver.address = "http://localhost:8448"; + appservice = { + hostname = "[::]"; + port = appservicePort; + database.type = "sqlite3"; + database.uri = "file:${dataDir}/mautrix-whatsapp.db?_txlock=immediate"; + id = "whatsapp"; + bot = { + username = "whatsappbot"; + displayname = "Whatsapp Bridge Bot"; + }; + as_token = ""; + hs_token = ""; + }; + bridge = { + username_template = "whatsapp_{{.}}"; + displayname_template = "{{or .ProfileName .PhoneNumber \"Unknown user\"}}"; + double_puppet_server_map = { }; + login_shared_secret_map = { }; + command_prefix = "!whatsapp"; + permissions."*" = "relay"; + relay.enabled = true; + }; + logging = { + min_level = "info"; + writers = lib.singleton { + type = "stdout"; + format = "pretty-colored"; + time_format = " "; + }; + }; + }; + +in +{ + options.services.mautrix-whatsapp-dashie = { + enable = lib.mkEnableOption "mautrix-whatsapp, a Matrix-Whatsapp puppeting bridge."; + + settings = lib.mkOption { + apply = lib.recursiveUpdate defaultConfig; + type = settingsFormat.type; + default = defaultConfig; + description = '' + {file}`config.yaml` configuration as a Nix attribute set. + Configuration options should match those described in + [example-config.yaml](https://github.com/mautrix/whatsapp/blob/master/example-config.yaml). + Secret tokens should be specified using {option}`environmentFile` + instead of this world-readable attribute set. + ''; + example = { + appservice = { + database = { + type = "postgres"; + uri = "postgresql:///mautrix_whatsapp?host=/run/postgresql"; + }; + id = "whatsapp"; + ephemeral_events = false; + }; + bridge = { + history_sync = { + request_full_sync = true; + }; + private_chat_portal_meta = true; + mute_bridging = true; + encryption = { + allow = true; + default = true; + require = true; + }; + provisioning = { + shared_secret = "disable"; + }; + permissions = { + "example.com" = "user"; + }; + }; + }; + }; + + environmentFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = '' + File containing environment variables to be passed to the mautrix-whatsapp service. + If an environment variable `MAUTRIX_WHATSAPP_BRIDGE_LOGIN_SHARED_SECRET` is set, + then its value will be used in the configuration file for the option + `login_shared_secret_map` without leaking it to the store, using the configured + `homeserver.domain` as key. + See [here](https://github.com/mautrix/whatsapp/blob/main/example-config.yaml) + for the documentation of `login_shared_secret_map`. + ''; + }; + + serviceDependencies = lib.mkOption { + type = with lib.types; listOf str; + default = (lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit) + ++ (lib.optional config.services.matrix-conduit.enable "conduit.service"); + defaultText = lib.literalExpression '' + (optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit) + ++ (optional config.services.matrix-conduit.enable "conduit.service") + ''; + description = '' + List of systemd units to require and wait for when starting the application service. + ''; + }; + + registerToSynapse = lib.mkOption { + type = lib.types.bool; + default = config.services.matrix-synapse.enable; + defaultText = lib.literalExpression '' + config.services.matrix-synapse.enable + ''; + description = '' + Whether to add the bridge's app service registration file to + `services.matrix-synapse.settings.app_service_config_files`. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + + users.users.mautrix-whatsapp = { + isSystemUser = true; + group = "mautrix-whatsapp"; + home = dataDir; + description = "mautrix-whatsapp bridge user"; + }; + + users.groups.mautrix-whatsapp = { }; + + services.matrix-synapse = lib.mkIf cfg.registerToSynapse { + settings.app_service_config_files = [ registrationFile ]; + }; + systemd.services.matrix-synapse = lib.mkIf cfg.registerToSynapse { + serviceConfig.SupplementaryGroups = [ "mautrix-whatsapp" ]; + }; + + # Note: this is defined here to avoid the docs depending on `config` + services.mautrix-whatsapp-dashie.settings.homeserver = optOneOf (with config.services; [ + (lib.mkIf matrix-synapse.enable (mkDefaults { + domain = matrix-synapse.settings.server_name; + })) + (lib.mkIf matrix-conduit.enable (mkDefaults { + domain = matrix-conduit.settings.global.server_name; + address = "http://localhost:${toString matrix-conduit.settings.global.port}"; + })) + ]); + + systemd.services.mautrix-whatsapp-dashie = { + description = "mautrix-whatsapp, a Matrix-Whatsapp puppeting bridge."; + + wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ] ++ cfg.serviceDependencies; + after = [ "network-online.target" ] ++ cfg.serviceDependencies; + # ffmpeg is required for conversion of voice messages + path = [ pkgs.ffmpeg-headless ]; + + preStart = '' + # substitute the settings file by environment variables + # in this case read from EnvironmentFile + test -f '${settingsFile}' && rm -f '${settingsFile}' + old_umask=$(umask) + umask 0177 + ${pkgs.envsubst}/bin/envsubst \ + -o '${settingsFile}' \ + -i '${settingsFileUnsubstituted}' + umask $old_umask + + # generate the appservice's registration file if absent + if [ ! -f '${registrationFile}' ]; then + ${pkgs.mautrix-whatsapp}/bin/mautrix-whatsapp \ + --generate-registration \ + --config='${settingsFile}' \ + --registration='${registrationFile}' + fi + chmod 640 ${registrationFile} + + umask 0177 + # 1. Overwrite registration tokens in config + # 2. If environment variable MAUTRIX_WHATSAPP_BRIDGE_LOGIN_SHARED_SECRET + # is set, set it as the login shared secret value for the configured + # homeserver domain. + ${pkgs.yq}/bin/yq -s '.[0].appservice.as_token = .[1].as_token + | .[0].appservice.hs_token = .[1].hs_token + | .[0] + | if env.MAUTRIX_WHATSAPP_BRIDGE_LOGIN_SHARED_SECRET then .bridge.login_shared_secret_map.[.homeserver.domain] = env.MAUTRIX_WHATSAPP_BRIDGE_LOGIN_SHARED_SECRET else . end' \ + '${settingsFile}' '${registrationFile}' > '${settingsFile}.tmp' + mv '${settingsFile}.tmp' '${settingsFile}' + umask $old_umask + ''; + + serviceConfig = { + User = "mautrix-whatsapp"; + Group = "mautrix-whatsapp"; + EnvironmentFile = cfg.environmentFile; + StateDirectory = baseNameOf dataDir; + WorkingDirectory = dataDir; + ExecStart = '' + ${pkgs.mautrix-whatsapp}/bin/mautrix-whatsapp \ + --config='${settingsFile}' \ + --registration='${registrationFile}' + ''; + 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"; + Restart = "on-failure"; + RestartSec = "30s"; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallErrorNumber = "EPERM"; + SystemCallFilter = [ "@system-service" ]; + Type = "simple"; + UMask = 0027; + }; + restartTriggers = [ settingsFileUnsubstituted ]; + }; + }; + meta.maintainers = with lib.maintainers; [ niklaskorz ]; +} + diff --git a/hardware/spaceship/configuration.nix b/hardware/spaceship/configuration.nix index e236f69..a2629e7 100644 --- a/hardware/spaceship/configuration.nix +++ b/hardware/spaceship/configuration.nix @@ -93,12 +93,12 @@ in synclist = [ { name = "document_sync"; - remote = "Documents"; + remote = "/Documents"; local = "/home/${config.conf.username}/Documents"; } { name = "picture_sync"; - remote = "Pictures"; + remote = "/Pictures"; local = "/home/${config.conf.username}/Pictures"; } { diff --git a/modules/programs/nextcloud.nix b/modules/programs/nextcloud.nix index 7f91dce..7298065 100644 --- a/modules/programs/nextcloud.nix +++ b/modules/programs/nextcloud.nix @@ -1,4 +1,4 @@ -{ lib, config, pkgs, options, ... }: { +{ lib, ... }: { options.mods = { nextcloud = { enable = lib.mkOption { diff --git a/programs/individual_configs/firefox.nix b/programs/individual_configs/firefox.nix index 6457c05..a4789e0 100644 --- a/programs/individual_configs/firefox.nix +++ b/programs/individual_configs/firefox.nix @@ -12,6 +12,25 @@ DisablePocket = true; DisplayBookmarksToolbar = "never"; DisplayMenuBar = "default-off"; + CaptivePortal = false; + DisableFirefoxStudies = true; + DisableTelemetry = true; + DisableFirefoxAccounts = false; + NoDefaultBookmarks = true; + OfferToSaveLogins = false; + OfferToSaveLoginsDefault = false; + PasswordManagerEnabled = false; + FirefoxHome = { + Search = true; + Pocket = false; + Snippets = false; + TopSites = false; + Highlights = false; + }; + UserMessaging = { + ExtensionRecommendations = false; + SkipOnboarding = true; + }; }; profiles.${config.conf.username} = { extensions = with pkgs.nur.repos.rycee.firefox-addons; [ @@ -22,7 +41,6 @@ keepassxc-browser i-dont-care-about-cookies tokyo-night-v2 - ]; }; }; diff --git a/programs/individual_configs/keepass.nix b/programs/individual_configs/keepass.nix index 58ffdb3..276f75f 100644 --- a/programs/individual_configs/keepass.nix +++ b/programs/individual_configs/keepass.nix @@ -6,18 +6,18 @@ ConfigVersion=2 [Browser] - CustomProxyLocation= Enabled=true [GUI] ApplicationTheme=classic HidePasswords=true + MinimizeOnClose=true + MinimizeToTray=true + ShowTrayIcon=true TrayIconAppearance=monochrome-light [PasswordGenerator] - AdditionalChars= - ExcludedChars= - Length=18 + Length=30 [Security] EnableCopyOnDoubleClick=true diff --git a/programs/sync.nix b/programs/sync.nix index 281e4eb..ec7755c 100644 --- a/programs/sync.nix +++ b/programs/sync.nix @@ -1,4 +1,5 @@ -{ config, pkgs, options, lib, ... }: +# derived from NixOS wiki +{ config, pkgs, lib, ... }: let username = config.mods.nextcloud.username; password = config.sops.secrets.nextcloud.path; @@ -19,7 +20,7 @@ lib.mkIf config.mods.nextcloud.enable { }; Service = { Type = "simple"; - ExecStart = "${pkgs.nextcloud-client}/bin/nextcloudcmd -h --path ${opts.remote} ${opts.local} https://${username}:$(bat ${password})@${url}"; + ExecStart = "${pkgs.bash}/bin/bash -c '${pkgs.nextcloud-client}/bin/nextcloudcmd -h --path ${opts.remote} ${opts.local} https://${username}:$(bat ${password})@${url}'"; TimeoutStopSec = "180"; KillMode = "process"; KillSignal = "SIGINT"; diff --git a/secrets/secrets.yaml b/secrets/secrets.yaml index 9c2f429..173df49 100644 --- a/secrets/secrets.yaml +++ b/secrets/secrets.yaml @@ -6,14 +6,21 @@ lab_pub: ENC[AES256_GCM,data:rlHCiqGnoaPiQBaZQRT+bEjfNF7jNO4CGPoCOKJ1o7nv7i2jPy6 dashie_pub: ENC[AES256_GCM,data:k6JIJOKDJcGSW47Z8y0EYxNl/vaPRVbIn35CSA57snEzYnk5GpU+1NfPDniWoAGRkpIwicgN6kpzssRlKOmVudvwMejSLv4VkLRBjrsApVFECwoIBLUNGUSDaMcIwC/BYu4jfjGaozBj,iv:0EZ0rptLdmcuTU1BGOILaaDTrc7aZGJCCxgjUESqi0M=,tag:dlQs/ugBGxnSrNj/bRSJSw==,type:str] server_pub: ENC[AES256_GCM,data:87nTYzA8CykOPjfZS2As8+JB/ysJvHXFYbPIBA8Nus8Y3nI3Tl2F/f7mUVFBT+4mmOFTTwxghEnkpgTg/vzUm6W4wb19rIcv11eM7HYaGl5oI44a44rBJn2+PKlfIgXVgaY=,iv:O7I7kkZ44McXzCt3wH1cM3MJCShxu2O+0U0+Y6rwePo=,tag:q5D5AGMmFyiNhQNR8dRB+g==,type:str] nextcloud: ENC[AES256_GCM,data:hjpS1WKsQJ6U2XX3GAbVP93VBAE8hKUdBRD9nI5Yiw==,iv:QaJNScNaxLLArzHLutIWdgN4m+9F0+Ym0FOcL53ygeQ=,tag:PamHgZJ+rsb3Dno2kEZRpQ==,type:str] +nextcloud_server: ENC[AES256_GCM,data:ohp0y08skd/NL7KhPE6pfezghY7UVL+aYT0=,iv:Rc5cnej+721aNrJGkE6/nTtwYC6Jg54da5bKu6mH1zY=,tag:EfiGA1DT87hGtNdMFZVBVA==,type:str] +nextcloud_admin: ENC[AES256_GCM,data:yRpnyoQ+rSiwaQoTp3I=,iv:Ii8ge7nkmtX1bVq4vdwEaLc3QFSrt0fbyHao7IDgtf0=,tag:T5YbThFN6B9fdBU/jhqdmQ==,type:str] +forgejo_server: ENC[AES256_GCM,data:4RLdo5pRQ17QlbpFFciFDrRocPj1J9W0hh4=,iv:AaTjk/ysWGubHSwzigyBWs7CGAOHnrbK7B+gUGFXETw=,tag:5rXwLSSSthq7nVIw5mIhLw==,type:str] +matrix_server: ENC[AES256_GCM,data:fH+5kX6VyNUXzAmNkLEGf8KmhIWuTGsG3r0=,iv:B2ltogyJaT1zcyZfHdrtB4HfnLZuWMbC7LwCT+IIPlU=,tag:jlOjBdypkrdc8MGp1fqSBQ==,type:str] +mautrix_signal_server: ENC[AES256_GCM,data:xBHtTtf725wvSltd7EgP3u/GszsaKR1D/ng=,iv:KZorceuZJulvBYyOSKaFv0UxAgMzIuXnBSDmqeqZT80=,tag:k4Dqvq7n39q6rgfB9hB8/g==,type:str] +mautrix_whatsapp_server: ENC[AES256_GCM,data:Ap5NZ9+kkusMTJlmiH2vxj2fkp1RZPSOM5s=,iv:/F3sP/7bw0uIualG8E+Mtxp60xW8OlHBBZCui887oaA=,tag:CawIZEpmbmxRYhq2fb1vDw==,type:str] +mautrix_discord_server: ENC[AES256_GCM,data:8MU3URa52h0sDabl+6bYZ0z0ib/S8KzYb3k=,iv:uSqT0MsK1qcphyd+5xZZ8aDqxQhZX8mKBP+2tHHG04I=,tag:mdepj3ombSru96es+lFIQQ==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: [] - lastmodified: "2024-08-04T00:37:58Z" - mac: ENC[AES256_GCM,data:zpPT060VGVh16KA70at4k2Q57zmT6PpYwHgGLfXLoPTu+aq3UhwQDnV/7gOUEquzopAT/lanbYjZ70uZZkXgU/HkBHNF3MLLULPK4HrOWgcsm277WPrgLqT9yUG2H8yxZGxDmKvFA7wHQ4TWyvqOmfuGv89UPoyV9PBGZ7anO6s=,iv:JtPVYsvg+2qgUlf6IF157lmorK2FrK1A6Fo15rkEnXA=,tag:iLWhUcFqY5Mip++SUZEj3Q==,type:str] + lastmodified: "2024-08-04T11:57:52Z" + mac: ENC[AES256_GCM,data:pd8VsxocTuCAIOAXi94ltCfhqohmAIBbZBK/3WQSd0suyORcvSKrTYdvi/dZ/6x+bXgz0vEzKNanNR98eLU4Ff3ldvsT6RQA1Hjn85V4ouJqWBB//kj42gYSiIjn/1dib0hvyZyvm2mutKbkpxZkJxRZYAw2DR0yR/oPfNK3xG8=,iv:fnRC7vk/KMgRzJgn9ww9A0amQTEsOVhqUa5NLAvX+kA=,tag:bbfpvpbL2L/ctQPdz6nDRg==,type:str] pgp: - created_at: "2024-05-14T14:35:02Z" enc: |-