Add server configuration

This commit is contained in:
DashieTM 2024-08-04 13:59:13 +02:00
parent ee2b51bbc1
commit 8ee4ea7fe1
13 changed files with 988 additions and 17 deletions

View file

@ -4,6 +4,7 @@
inputs = inputs =
{ {
nixpkgs.url = "github:NixOs/nixpkgs/nixos-unstable"; nixpkgs.url = "github:NixOs/nixpkgs/nixos-unstable";
stable.url = "github:NixOs/nixpkgs/nixos-24.05";
nix-flatpak = { nix-flatpak = {
url = "github:gmodena/nix-flatpak"; url = "github:gmodena/nix-flatpak";
@ -50,6 +51,12 @@
outputs = { ... }@inputs: outputs = { ... }@inputs:
let let
stable = import inputs.stable {
system = "x86_64-linux";
config = {
allowUnfree = true;
};
};
pkgs = import inputs.nixpkgs { pkgs = import inputs.nixpkgs {
system = "x86_64-linux"; system = "x86_64-linux";
overlays = [ overlays = [
@ -62,7 +69,16 @@
dashielib = import ./lib { inherit inputs pkgs; }; dashielib = import ./lib { inherit inputs pkgs; };
in 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 = { nixConfig = {

View file

@ -1,4 +1,4 @@
{ { config, ... }: {
imports = [ imports = [
../../modules ../../modules
]; ];
@ -25,5 +25,14 @@
greetd = { greetd = {
resolution = "3440x1440@180"; resolution = "3440x1440@180";
}; };
nextcloud = {
synclist = [
{
name = "document_sync";
remote = "/Documents";
local = "/home/${config.conf.username}/Documents";
}
];
};
}; };
} }

View file

@ -1,4 +1,4 @@
{ { config, ... }: {
imports = [ imports = [
../../modules/conf.nix ../../modules/conf.nix
]; ];
@ -30,5 +30,18 @@
greetd = { greetd = {
resolution = "3440x1440@180"; 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";
}
];
};
}

View file

@ -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";
}

View file

@ -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.<interface>.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;
}

View file

@ -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 ];
}

View file

@ -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 ];
}

View file

@ -93,12 +93,12 @@ in
synclist = [ synclist = [
{ {
name = "document_sync"; name = "document_sync";
remote = "Documents"; remote = "/Documents";
local = "/home/${config.conf.username}/Documents"; local = "/home/${config.conf.username}/Documents";
} }
{ {
name = "picture_sync"; name = "picture_sync";
remote = "Pictures"; remote = "/Pictures";
local = "/home/${config.conf.username}/Pictures"; local = "/home/${config.conf.username}/Pictures";
} }
{ {

View file

@ -1,4 +1,4 @@
{ lib, config, pkgs, options, ... }: { { lib, ... }: {
options.mods = { options.mods = {
nextcloud = { nextcloud = {
enable = lib.mkOption { enable = lib.mkOption {

View file

@ -12,6 +12,25 @@
DisablePocket = true; DisablePocket = true;
DisplayBookmarksToolbar = "never"; DisplayBookmarksToolbar = "never";
DisplayMenuBar = "default-off"; 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} = { profiles.${config.conf.username} = {
extensions = with pkgs.nur.repos.rycee.firefox-addons; [ extensions = with pkgs.nur.repos.rycee.firefox-addons; [
@ -22,7 +41,6 @@
keepassxc-browser keepassxc-browser
i-dont-care-about-cookies i-dont-care-about-cookies
tokyo-night-v2 tokyo-night-v2
]; ];
}; };
}; };

View file

@ -6,18 +6,18 @@
ConfigVersion=2 ConfigVersion=2
[Browser] [Browser]
CustomProxyLocation=
Enabled=true Enabled=true
[GUI] [GUI]
ApplicationTheme=classic ApplicationTheme=classic
HidePasswords=true HidePasswords=true
MinimizeOnClose=true
MinimizeToTray=true
ShowTrayIcon=true
TrayIconAppearance=monochrome-light TrayIconAppearance=monochrome-light
[PasswordGenerator] [PasswordGenerator]
AdditionalChars= Length=30
ExcludedChars=
Length=18
[Security] [Security]
EnableCopyOnDoubleClick=true EnableCopyOnDoubleClick=true

View file

@ -1,4 +1,5 @@
{ config, pkgs, options, lib, ... }: # derived from NixOS wiki
{ config, pkgs, lib, ... }:
let let
username = config.mods.nextcloud.username; username = config.mods.nextcloud.username;
password = config.sops.secrets.nextcloud.path; password = config.sops.secrets.nextcloud.path;
@ -19,7 +20,7 @@ lib.mkIf config.mods.nextcloud.enable {
}; };
Service = { Service = {
Type = "simple"; 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"; TimeoutStopSec = "180";
KillMode = "process"; KillMode = "process";
KillSignal = "SIGINT"; KillSignal = "SIGINT";

View file

@ -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] 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] 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: 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: sops:
kms: [] kms: []
gcp_kms: [] gcp_kms: []
azure_kv: [] azure_kv: []
hc_vault: [] hc_vault: []
age: [] age: []
lastmodified: "2024-08-04T00:37:58Z" lastmodified: "2024-08-04T11:57:52Z"
mac: ENC[AES256_GCM,data:zpPT060VGVh16KA70at4k2Q57zmT6PpYwHgGLfXLoPTu+aq3UhwQDnV/7gOUEquzopAT/lanbYjZ70uZZkXgU/HkBHNF3MLLULPK4HrOWgcsm277WPrgLqT9yUG2H8yxZGxDmKvFA7wHQ4TWyvqOmfuGv89UPoyV9PBGZ7anO6s=,iv:JtPVYsvg+2qgUlf6IF157lmorK2FrK1A6Fo15rkEnXA=,tag:iLWhUcFqY5Mip++SUZEj3Q==,type:str] mac: ENC[AES256_GCM,data:pd8VsxocTuCAIOAXi94ltCfhqohmAIBbZBK/3WQSd0suyORcvSKrTYdvi/dZ/6x+bXgz0vEzKNanNR98eLU4Ff3ldvsT6RQA1Hjn85V4ouJqWBB//kj42gYSiIjn/1dib0hvyZyvm2mutKbkpxZkJxRZYAw2DR0yR/oPfNK3xG8=,iv:fnRC7vk/KMgRzJgn9ww9A0amQTEsOVhqUa5NLAvX+kA=,tag:bbfpvpbL2L/ctQPdz6nDRg==,type:str]
pgp: pgp:
- created_at: "2024-05-14T14:35:02Z" - created_at: "2024-05-14T14:35:02Z"
enc: |- enc: |-