let
matrixBase =
{ config, ... }:
let
inherit (config.myLib) merge mkResticBackup;
inherit (config.networking) domain;
fqdn = "matrix.${domain}";
port = 8008;
in
{
services.restic.backups.matrix = mkResticBackup "matrix" {
paths = [ "/var/lib/matrix-synapse" ];
timerConfig = {
OnCalendar = "hourly";
Persistent = true;
};
};
systemd.services.matrix-synapse.serviceConfig = {
# sandboxing
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = true;
# fs restrictions
ReadWritePaths = [ "/var/lib/matrix-synapse" ];
# network restrictions
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
# misc
NoNewPrivileges = true;
RestrictSUIDSGID = true;
};
services.matrix-synapse = {
enable = true;
withJemalloc = true;
configureRedisLocally = true;
settings = {
server_name = domain;
listeners = [
{
inherit port;
bind_addresses = [ "::1" ];
type = "http";
tls = false;
x_forwarded = true; # behind reverse proxy
resources = [
{
names = [
"client"
"federation"
"media"
];
compress = false;
}
];
}
];
database.name = "sqlite3";
database.args.database = "/var/lib/matrix-synapse/homeserver.db";
log_config = "/var/lib/matrix-synapse/log.yaml";
log.root.level = "WARNING";
enable_registration = true;
registration_requires_token = true;
allow_public_rooms_without_auth = true;
allow_public_rooms_over_federation = true;
report_stats = false;
delete_stale_devices_after = "30d";
redis.enabled = true;
max_upload_size = "512M";
media_store_path = "/var/lib/matrix-synapse/media_store";
url_preview_enabled = true;
dynamic_thumbnails = true;
signing_key_path = config.age.secrets.matrixSigningKey.path;
registration_shared_secret = config.age.secrets.matrixRegistrationSecret.path;
trusted_key_servers = [ ];
extras = [
"url-preview"
"user-search"
];
};
};
services.nginx.virtualHosts.${fqdn} = merge config.services.nginx.sslTemplate {
extraConfig = ''
${config.services.nginx.goatCounterTemplate}
'';
locations."/_matrix".proxyPass = "http://[::1]:${toString port}";
locations."/_synapse/client".proxyPass = "http://[::1]:${toString port}";
locations."/_synapse/admin".proxyPass = "http://[::1]:${toString port}";
};
services.nginx.virtualHosts.${domain} = merge config.services.nginx.sslTemplate {
locations."/.well-known/matrix/client".extraConfig = ''
return 200 '{"m.homeserver": {"base_url": "https://${fqdn}"}}';
'';
locations."/.well-known/matrix/server".extraConfig = ''
return 200 '{"m.server": "${fqdn}:443"}';
'';
};
};
cinnyBase =
{
pkgs,
lib,
config,
...
}:
let
inherit (lib.strings) toJSON;
inherit (lib.lists) singleton;
inherit (config.networking) domain hostName;
inherit (config.myLib) merge;
fqdn = "chat.${domain}";
root = pkgs.cinny;
cinnyConfig = {
allowCustomHomeservers = false;
homeserverList = [ domain ];
defaultHomeserver = 0;
hashRouter = {
enabled = false;
basename = "/";
};
featuredCommunities = {
openAsDefault = false;
servers = [
domain
"matrix.org"
];
spaces = [ ];
rooms = [ ];
};
};
in
{
assertions = singleton {
assertion = config.services.matrix-synapse.enable;
message = "The Cinny module should be used on the host running Matrix, but you're trying to enable it on '${hostName}'.";
};
services.nginx.virtualHosts.${fqdn} = merge config.services.nginx.sslTemplate {
inherit root;
locations."= /config.json".extraConfig = # nginx
''
default_type application/json;
return 200 '${toJSON cinnyConfig}';
'';
locations."/".extraConfig = # nginx
''
proxy_hide_header Content-Security-Policy;
add_header Content-Security-Policy "script-src 'self' 'unsafe-inline' 'unsafe-eval' ${domain} *.${domain}; object-src 'self' ${domain} *.${domain}; img-src 'self' data: https: blob:; base-uri 'self'; frame-ancestors 'self';" always;
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Permissions-Policy "camera=(), geolocation=(), payment=(), usb=()" always;
add_header Referrer-Policy no-referrer always;
'';
extraConfig = # nginx
''
rewrite ^/config.json$ /config.json break;
rewrite ^/manifest.json$ /manifest.json break;
rewrite ^/sw.js$ /sw.js break;
rewrite ^/pdf.worker.min.js$ /pdf.worker.min.js break;
rewrite ^/public/(.*)$ /public/$1 break;
rewrite ^/assets/(.*)$ /assets/$1 break;
rewrite ^(.+)$ /index.html break;
'';
};
};
in
{
flake.modules.nixos.matrix = matrixBase;
flake.modules.nixos.cinny = cinnyBase;
}