uboot: (firmwareOdroidC2/C4) don't invoke patch tool, use patches = [] instead

https://github.com/NixOS/nixpkgs/blob/master/pkgs/stdenv/generic/setup.sh#L948
this can do it nicely.

Signed-off-by: Anton Arapov <anton@deadbeef.mx>
This commit is contained in:
Anton Arapov 2021-04-03 12:58:10 +02:00 committed by Alan Daniels
commit 56de2bcd43
30691 changed files with 3076956 additions and 0 deletions

View file

@ -0,0 +1,25 @@
version: 1
# In systemd's journal, loglevel is implicitly stored, so let's omit it
# from the message text.
formatters:
journal_fmt:
format: '%(name)s: [%(request)s] %(message)s'
filters:
context:
(): synapse.util.logcontext.LoggingContextFilter
request: ""
handlers:
journal:
class: systemd.journal.JournalHandler
formatter: journal_fmt
filters: [context]
SYSLOG_IDENTIFIER: synapse
root:
level: INFO
handlers: [journal]
disable_existing_loggers: False

View file

@ -0,0 +1,774 @@
{ config, lib, options, pkgs, ... }:
with lib;
let
cfg = config.services.matrix-synapse;
format = pkgs.formats.yaml {};
# remove null values from the final configuration
finalSettings = lib.filterAttrsRecursive (_: v: v != null) cfg.settings;
configFile = format.generate "homeserver.yaml" finalSettings;
logConfigFile = format.generate "log_config.yaml" cfg.logConfig;
pluginsEnv = cfg.package.python.buildEnv.override {
extraLibs = cfg.plugins;
};
usePostgresql = cfg.settings.database.name == "psycopg2";
hasLocalPostgresDB = let args = cfg.settings.database.args; in
usePostgresql && (!(args ? host) || (elem args.host [ "localhost" "127.0.0.1" "::1" ]));
registerNewMatrixUser =
let
isIpv6 = x: lib.length (lib.splitString ":" x) > 1;
listener =
lib.findFirst (
listener: lib.any (
resource: lib.any (
name: name == "client"
) resource.names
) listener.resources
) (lib.last cfg.settings.listeners) cfg.settings.listeners;
# FIXME: Handle cases with missing client listener properly,
# don't rely on lib.last, this will not work.
# add a tail, so that without any bind_addresses we still have a useable address
bindAddress = head (listener.bind_addresses ++ [ "127.0.0.1" ]);
listenerProtocol = if listener.tls
then "https"
else "http";
in
pkgs.writeShellScriptBin "matrix-synapse-register_new_matrix_user" ''
exec ${cfg.package}/bin/register_new_matrix_user \
$@ \
${lib.concatMapStringsSep " " (x: "-c ${x}") ([ configFile ] ++ cfg.extraConfigFiles)} \
"${listenerProtocol}://${
if (isIpv6 bindAddress) then
"[${bindAddress}]"
else
"${bindAddress}"
}:${builtins.toString listener.port}/"
'';
in {
imports = [
(mkRemovedOptionModule [ "services" "matrix-synapse" "trusted_third_party_id_servers" ] ''
The `trusted_third_party_id_servers` option as been removed in `matrix-synapse` v1.4.0
as the behavior is now obsolete.
'')
(mkRemovedOptionModule [ "services" "matrix-synapse" "create_local_database" ] ''
Database configuration must be done manually. An exemplary setup is demonstrated in
<nixpkgs/nixos/tests/matrix-synapse.nix>
'')
(mkRemovedOptionModule [ "services" "matrix-synapse" "web_client" ] "")
(mkRemovedOptionModule [ "services" "matrix-synapse" "room_invite_state_types" ] ''
You may add additional event types via
`services.matrix-synapse.room_prejoin_state.additional_event_types` and
disable the default events via
`services.matrix-synapse.room_prejoin_state.disable_default_event_types`.
'')
# options that don't exist in synapse anymore
(mkRemovedOptionModule [ "services" "matrix-synapse" "bind_host" ] "Use listener settings instead." )
(mkRemovedOptionModule [ "services" "matrix-synapse" "bind_port" ] "Use listener settings instead." )
(mkRemovedOptionModule [ "services" "matrix-synapse" "expire_access_tokens" ] "" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "no_tls" ] "It is no longer supported by synapse." )
(mkRemovedOptionModule [ "services" "matrix-synapse" "tls_dh_param_path" ] "It was removed from synapse." )
(mkRemovedOptionModule [ "services" "matrix-synapse" "unsecure_port" ] "Use settings.listeners instead." )
(mkRemovedOptionModule [ "services" "matrix-synapse" "user_creation_max_duration" ] "It is no longer supported by synapse." )
(mkRemovedOptionModule [ "services" "matrix-synapse" "verbose" ] "Use a log config instead." )
# options that were moved into rfc42 style settigns
(mkRemovedOptionModule [ "services" "matrix-synapse" "app_service_config_files" ] "Use settings.app_service_config_files instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "database_args" ] "Use settings.database.args instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "database_name" ] "Use settings.database.args.database instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "database_type" ] "Use settings.database.name instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "database_user" ] "Use settings.database.args.user instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "dynamic_thumbnails" ] "Use settings.dynamic_thumbnails instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "enable_metrics" ] "Use settings.enable_metrics instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "enable_registration" ] "Use settings.enable_registration instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "extraConfig" ] "Use settings instead." )
(mkRemovedOptionModule [ "services" "matrix-synapse" "listeners" ] "Use settings.listeners instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "logConfig" ] "Use settings.log_config instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "max_image_pixels" ] "Use settings.max_image_pixels instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "max_upload_size" ] "Use settings.max_upload_size instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "presence" "enabled" ] "Use settings.presence.enabled instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "public_baseurl" ] "Use settings.public_baseurl instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "report_stats" ] "Use settings.report_stats instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "server_name" ] "Use settings.server_name instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "servers" ] "Use settings.trusted_key_servers instead." )
(mkRemovedOptionModule [ "services" "matrix-synapse" "tls_certificate_path" ] "Use settings.tls_certificate_path instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "tls_private_key_path" ] "Use settings.tls_private_key_path instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "turn_shared_secret" ] "Use settings.turn_shared_secret instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "turn_uris" ] "Use settings.turn_uris instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "turn_user_lifetime" ] "Use settings.turn_user_lifetime instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_enabled" ] "Use settings.url_preview_enabled instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_ip_range_blacklist" ] "Use settings.url_preview_ip_range_blacklist instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_ip_range_whitelist" ] "Use settings.url_preview_ip_range_whitelist instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "url_preview_url_blacklist" ] "Use settings.url_preview_url_blacklist instead" )
# options that are too specific to mention them explicitly in settings
(mkRemovedOptionModule [ "services" "matrix-synapse" "account_threepid_delegates" "email" ] "Use settings.account_threepid_delegates.email instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "account_threepid_delegates" "msisdn" ] "Use settings.account_threepid_delegates.msisdn instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "allow_guest_access" ] "Use settings.allow_guest_access instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "bcrypt_rounds" ] "Use settings.bcrypt_rounds instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "enable_registration_captcha" ] "Use settings.enable_registration_captcha instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "event_cache_size" ] "Use settings.event_cache_size instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_concurrent" ] "Use settings.rc_federation.concurrent instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_reject_limit" ] "Use settings.rc_federation.reject_limit instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_sleep_delay" ] "Use settings.rc_federation.sleep_delay instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_sleep_limit" ] "Use settings.rc_federation.sleep_limit instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "federation_rc_window_size" ] "Use settings.rc_federation.window_size instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "key_refresh_interval" ] "Use settings.key_refresh_interval instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "rc_messages_burst_count" ] "Use settings.rc_messages.burst_count instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "rc_messages_per_second" ] "Use settings.rc_messages.per_second instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "recaptcha_private_key" ] "Use settings.recaptcha_private_key instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "recaptcha_public_key" ] "Use settings.recaptcha_public_key instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "redaction_retention_period" ] "Use settings.redaction_retention_period instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "room_prejoin_state" "additional_event_types" ] "Use settings.room_prejoin_state.additional_event_types instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "room_prejoin_state" "disable_default_event_types" ] "Use settings.room_prejoin-state.disable_default_event_types instead" )
# Options that should be passed via extraConfigFiles, so they are not persisted into the nix store
(mkRemovedOptionModule [ "services" "matrix-synapse" "macaroon_secret_key" ] "Pass this value via extraConfigFiles instead" )
(mkRemovedOptionModule [ "services" "matrix-synapse" "registration_shared_secret" ] "Pass this value via extraConfigFiles instead" )
];
options = {
services.matrix-synapse = {
enable = mkEnableOption "matrix.org synapse";
configFile = mkOption {
type = types.path;
readOnly = true;
description = ''
Path to the configuration file on the target system. Useful to configure e.g. workers
that also need this.
'';
};
package = mkOption {
type = types.package;
default = pkgs.matrix-synapse;
defaultText = literalExpression "pkgs.matrix-synapse";
description = ''
Overridable attribute of the matrix synapse server package to use.
'';
};
plugins = mkOption {
type = types.listOf types.package;
default = [ ];
example = literalExpression ''
with config.services.matrix-synapse.package.plugins; [
matrix-synapse-ldap3
matrix-synapse-pam
];
'';
description = ''
List of additional Matrix plugins to make available.
'';
};
withJemalloc = mkOption {
type = types.bool;
default = false;
description = ''
Whether to preload jemalloc to reduce memory fragmentation and overall usage.
'';
};
dataDir = mkOption {
type = types.str;
default = "/var/lib/matrix-synapse";
description = ''
The directory where matrix-synapse stores its stateful data such as
certificates, media and uploads.
'';
};
settings = mkOption {
default = {};
description = ''
The primary synapse configuration. See the
<link xlink:href="https://github.com/matrix-org/synapse/blob/v${cfg.package.version}/docs/sample_config.yaml">sample configuration</link>
for possible values.
Secrets should be passed in by using the <literal>extraConfigFiles</literal> option.
'';
type = with types; submodule {
freeformType = format.type;
options = {
# This is a reduced set of popular options and defaults
# Do not add every available option here, they can be specified
# by the user at their own discretion. This is a freeform type!
server_name = mkOption {
type = types.str;
example = "example.com";
default = config.networking.hostName;
defaultText = literalExpression "config.networking.hostName";
description = ''
The domain name of the server, with optional explicit port.
This is used by remote servers to look up the server address.
This is also the last part of your UserID.
The server_name cannot be changed later so it is important to configure this correctly before you start Synapse.
'';
};
enable_registration = mkOption {
type = types.bool;
default = false;
description = ''
Enable registration for new users.
'';
};
registration_shared_secret = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
If set, allows registration by anyone who also has the shared
secret, even if registration is otherwise disabled.
Secrets should be passed in via <literal>extraConfigFiles</literal>!
'';
};
macaroon_secret_key = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Secret key for authentication tokens. If none is specified,
the registration_shared_secret is used, if one is given; otherwise,
a secret key is derived from the signing key.
Secrets should be passed in via <literal>extraConfigFiles</literal>!
'';
};
enable_metrics = mkOption {
type = types.bool;
default = false;
description = ''
Enable collection and rendering of performance metrics
'';
};
report_stats = mkOption {
type = types.bool;
default = false;
description = ''
Whether or not to report anonymized homeserver usage statistics.
'';
};
signing_key_path = mkOption {
type = types.path;
default = "${cfg.dataDir}/homeserver.signing.key";
description = ''
Path to the signing key to sign messages with.
'';
};
pid_file = mkOption {
type = types.path;
default = "/run/matrix-synapse.pid";
readOnly = true;
description = ''
The file to store the PID in.
'';
};
log_config = mkOption {
type = types.path;
default = ./matrix-synapse-log_config.yaml;
description = ''
The file that holds the logging configuration.
'';
};
media_store_path = mkOption {
type = types.path;
default = if lib.versionAtLeast config.system.stateVersion "22.05"
then "${cfg.dataDir}/media_store"
else "${cfg.dataDir}/media";
defaultText = "${cfg.dataDir}/media_store for when system.stateVersion is at least 22.05, ${cfg.dataDir}/media when lower than 22.05";
description = ''
Directory where uploaded images and attachments are stored.
'';
};
public_baseurl = mkOption {
type = types.nullOr types.str;
default = null;
example = "https://example.com:8448/";
description = ''
The public-facing base URL for the client API (not including _matrix/...)
'';
};
tls_certificate_path = mkOption {
type = types.nullOr types.str;
default = null;
example = "/var/lib/acme/example.com/fullchain.pem";
description = ''
PEM encoded X509 certificate for TLS.
You can replace the self-signed certificate that synapse
autogenerates on launch with your own SSL certificate + key pair
if you like. Any required intermediary certificates can be
appended after the primary certificate in hierarchical order.
'';
};
tls_private_key_path = mkOption {
type = types.nullOr types.str;
default = null;
example = "/var/lib/acme/example.com/key.pem";
description = ''
PEM encoded private key for TLS. Specify null if synapse is not
speaking TLS directly.
'';
};
presence.enabled = mkOption {
type = types.bool;
default = true;
example = false;
description = ''
Whether to enable presence tracking.
Presence tracking allows users to see the state (e.g online/offline)
of other local and remote users.
'';
};
listeners = mkOption {
type = types.listOf (types.submodule {
options = {
port = mkOption {
type = types.port;
example = 8448;
description = ''
The port to listen for HTTP(S) requests on.
'';
};
bind_addresses = mkOption {
type = types.listOf types.str;
default = [
"::1"
"127.0.0.1"
];
example = literalExpression ''
[
"::"
"0.0.0.0"
]
'';
description = ''
IP addresses to bind the listener to.
'';
};
type = mkOption {
type = types.enum [
"http"
"manhole"
"metrics"
"replication"
];
default = "http";
example = "metrics";
description = ''
The type of the listener, usually http.
'';
};
tls = mkOption {
type = types.bool;
default = true;
example = false;
description = ''
Whether to enable TLS on the listener socket.
'';
};
x_forwarded = mkOption {
type = types.bool;
default = false;
example = true;
description = ''
Use the X-Forwarded-For (XFF) header as the client IP and not the
actual client IP.
'';
};
resources = mkOption {
type = types.listOf (types.submodule {
options = {
names = mkOption {
type = types.listOf (types.enum [
"client"
"consent"
"federation"
"keys"
"media"
"metrics"
"openid"
"replication"
"static"
]);
description = ''
List of resources to host on this listener.
'';
example = [
"client"
];
};
compress = mkOption {
type = types.bool;
description = ''
Should synapse compress HTTP responses to clients that support it?
This should be disabled if running synapse behind a load balancer
that can do automatic compression.
'';
};
};
});
description = ''
List of HTTP resources to serve on this listener.
'';
};
};
});
default = [ {
port = 8008;
bind_addresses = [ "127.0.0.1" ];
type = "http";
tls = false;
x_forwarded = true;
resources = [ {
names = [ "client" ];
compress = true;
} {
names = [ "federation" ];
compress = false;
} ];
} ];
description = ''
List of ports that Synapse should listen on, their purpose and their configuration.
'';
};
database.name = mkOption {
type = types.enum [
"sqlite3"
"psycopg2"
];
default = if versionAtLeast config.system.stateVersion "18.03"
then "psycopg2"
else "sqlite3";
defaultText = literalExpression ''
if versionAtLeast config.system.stateVersion "18.03"
then "psycopg2"
else "sqlite3"
'';
description = ''
The database engine name. Can be sqlite3 or psycopg2.
'';
};
database.args.database = mkOption {
type = types.str;
default = {
sqlite3 = "${cfg.dataDir}/homeserver.db";
psycopg2 = "matrix-synapse";
}.${cfg.settings.database.name};
defaultText = literalExpression ''
{
sqlite3 = "''${${options.services.matrix-synapse.dataDir}}/homeserver.db";
psycopg2 = "matrix-synapse";
}.''${${options.services.matrix-synapse.settings}.database.name};
'';
description = ''
Name of the database when using the psycopg2 backend,
path to the database location when using sqlite3.
'';
};
database.args.user = mkOption {
type = types.nullOr types.str;
default = {
sqlite3 = null;
psycopg2 = "matrix-synapse";
}.${cfg.settings.database.name};
description = ''
Username to connect with psycopg2, set to null
when using sqlite3.
'';
};
url_preview_enabled = mkOption {
type = types.bool;
default = true;
example = false;
description = ''
Is the preview URL API enabled? If enabled, you *must* specify an
explicit url_preview_ip_range_blacklist of IPs that the spider is
denied from accessing.
'';
};
url_preview_ip_range_blacklist = mkOption {
type = types.listOf types.str;
default = [
"10.0.0.0/8"
"100.64.0.0/10"
"127.0.0.0/8"
"169.254.0.0/16"
"172.16.0.0/12"
"192.0.0.0/24"
"192.0.2.0/24"
"192.168.0.0/16"
"192.88.99.0/24"
"198.18.0.0/15"
"198.51.100.0/24"
"2001:db8::/32"
"203.0.113.0/24"
"224.0.0.0/4"
"::1/128"
"fc00::/7"
"fe80::/10"
"fec0::/10"
"ff00::/8"
];
description = ''
List of IP address CIDR ranges that the URL preview spider is denied
from accessing.
'';
};
url_preview_ip_range_whitelist = mkOption {
type = types.listOf types.str;
default = [];
description = ''
List of IP address CIDR ranges that the URL preview spider is allowed
to access even if they are specified in url_preview_ip_range_blacklist.
'';
};
url_preview_url_blacklist = mkOption {
type = types.listOf types.str;
default = [];
description = ''
Optional list of URL matches that the URL preview spider is
denied from accessing.
'';
};
max_upload_size = mkOption {
type = types.str;
default = "50M";
example = "100M";
description = ''
The largest allowed upload size in bytes
'';
};
max_image_pixels = mkOption {
type = types.str;
default = "32M";
example = "64M";
description = ''
Maximum number of pixels that will be thumbnailed
'';
};
dynamic_thumbnails = mkOption {
type = types.bool;
default = false;
example = true;
description = ''
Whether to generate new thumbnails on the fly to precisely match
the resolution requested by the client. If true then whenever
a new resolution is requested by the client the server will
generate a new thumbnail. If false the server will pick a thumbnail
from a precalculated list.
'';
};
turn_uris = mkOption {
type = types.listOf types.str;
default = [];
example = [
"turn:turn.example.com:3487?transport=udp"
"turn:turn.example.com:3487?transport=tcp"
"turns:turn.example.com:5349?transport=udp"
"turns:turn.example.com:5349?transport=tcp"
];
description = ''
The public URIs of the TURN server to give to clients
'';
};
turn_shared_secret = mkOption {
type = types.str;
default = "";
example = literalExpression ''
config.services.coturn.static-auth-secret
'';
description = ''
The shared secret used to compute passwords for the TURN server.
Secrets should be passed in via <literal>extraConfigFiles</literal>!
'';
};
trusted_key_servers = mkOption {
type = types.listOf (types.submodule {
options = {
server_name = mkOption {
type = types.str;
example = "matrix.org";
description = ''
Hostname of the trusted server.
'';
};
verify_keys = mkOption {
type = types.nullOr (types.attrsOf types.str);
default = null;
example = literalExpression ''
{
"ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw";
}
'';
description = ''
Attribute set from key id to base64 encoded public key.
If specified synapse will check that the response is signed
by at least one of the given keys.
'';
};
};
});
default = [ {
server_name = "matrix.org";
verify_keys = {
"ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw";
};
} ];
description = ''
The trusted servers to download signing keys from.
'';
};
app_service_config_files = mkOption {
type = types.listOf types.path;
default = [ ];
description = ''
A list of application service config file to use
'';
};
};
};
};
extraConfigFiles = mkOption {
type = types.listOf types.path;
default = [];
description = ''
Extra config files to include.
The configuration files will be included based on the command line
argument --config-path. This allows to configure secrets without
having to go through the Nix store, e.g. based on deployment keys if
NixOps is in use.
'';
};
};
};
config = mkIf cfg.enable {
assertions = [
{ assertion = hasLocalPostgresDB -> config.services.postgresql.enable;
message = ''
Cannot deploy matrix-synapse with a configuration for a local postgresql database
and a missing postgresql service. Since 20.03 it's mandatory to manually configure the
database (please read the thread in https://github.com/NixOS/nixpkgs/pull/80447 for
further reference).
If you
- try to deploy a fresh synapse, you need to configure the database yourself. An example
for this can be found in <nixpkgs/nixos/tests/matrix-synapse.nix>
- update your existing matrix-synapse instance, you simply need to add `services.postgresql.enable = true`
to your configuration.
For further information about this update, please read the release-notes of 20.03 carefully.
'';
}
];
services.matrix-synapse.configFile = configFile;
users.users.matrix-synapse = {
group = "matrix-synapse";
home = cfg.dataDir;
createHome = true;
shell = "${pkgs.bash}/bin/bash";
uid = config.ids.uids.matrix-synapse;
};
users.groups.matrix-synapse = {
gid = config.ids.gids.matrix-synapse;
};
systemd.services.matrix-synapse = {
description = "Synapse Matrix homeserver";
after = [ "network.target" ] ++ optional hasLocalPostgresDB "postgresql.service";
wantedBy = [ "multi-user.target" ];
preStart = ''
${cfg.package}/bin/synapse_homeserver \
--config-path ${configFile} \
--keys-directory ${cfg.dataDir} \
--generate-keys
'';
environment = {
PYTHONPATH = makeSearchPathOutput "lib" cfg.package.python.sitePackages [ pluginsEnv ];
} // optionalAttrs (cfg.withJemalloc) {
LD_PRELOAD = "${pkgs.jemalloc}/lib/libjemalloc.so";
};
serviceConfig = {
Type = "notify";
User = "matrix-synapse";
Group = "matrix-synapse";
WorkingDirectory = cfg.dataDir;
ExecStartPre = [ ("+" + (pkgs.writeShellScript "matrix-synapse-fix-permissions" ''
chown matrix-synapse:matrix-synapse ${cfg.dataDir}/homeserver.signing.key
chmod 0600 ${cfg.dataDir}/homeserver.signing.key
'')) ];
ExecStart = ''
${cfg.package}/bin/synapse_homeserver \
${ concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ configFile ] ++ cfg.extraConfigFiles) }
--keys-directory ${cfg.dataDir}
'';
ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID";
Restart = "on-failure";
UMask = "0077";
};
};
environment.systemPackages = [ registerNewMatrixUser ];
};
meta = {
buildDocsInSandbox = false;
doc = ./matrix-synapse.xml;
maintainers = teams.matrix.members;
};
}

View file

@ -0,0 +1,231 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:id="module-services-matrix">
<title>Matrix</title>
<para>
<link xlink:href="https://matrix.org/">Matrix</link> is an open standard for
interoperable, decentralised, real-time communication over IP. It can be used
to power Instant Messaging, VoIP/WebRTC signalling, Internet of Things
communication - or anywhere you need a standard HTTP API for publishing and
subscribing to data whilst tracking the conversation history.
</para>
<para>
This chapter will show you how to set up your own, self-hosted Matrix
homeserver using the Synapse reference homeserver, and how to serve your own
copy of the Element web client. See the
<link xlink:href="https://matrix.org/docs/projects/try-matrix-now.html">Try
Matrix Now!</link> overview page for links to Element Apps for Android and iOS,
desktop clients, as well as bridges to other networks and other projects
around Matrix.
</para>
<section xml:id="module-services-matrix-synapse">
<title>Synapse Homeserver</title>
<para>
<link xlink:href="https://github.com/matrix-org/synapse">Synapse</link> is
the reference homeserver implementation of Matrix from the core development
team at matrix.org. The following configuration example will set up a
synapse server for the <literal>example.org</literal> domain, served from
the host <literal>myhostname.example.org</literal>. For more information,
please refer to the
<link xlink:href="https://github.com/matrix-org/synapse#synapse-installation">
installation instructions of Synapse </link>.
<programlisting>
{ pkgs, lib, ... }:
let
fqdn =
let
join = hostName: domain: hostName + lib.optionalString (domain != null) ".${domain}";
in join config.networking.hostName config.networking.domain;
in {
networking = {
<link linkend="opt-networking.hostName">hostName</link> = "myhostname";
<link linkend="opt-networking.domain">domain</link> = "example.org";
};
<link linkend="opt-networking.firewall.allowedTCPPorts">networking.firewall.allowedTCPPorts</link> = [ 80 443 ];
<link linkend="opt-services.postgresql.enable">services.postgresql.enable</link> = true;
<link linkend="opt-services.postgresql.initialScript">services.postgresql.initialScript</link> = pkgs.writeText "synapse-init.sql" ''
CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
TEMPLATE template0
LC_COLLATE = "C"
LC_CTYPE = "C";
'';
services.nginx = {
<link linkend="opt-services.nginx.enable">enable</link> = true;
# only recommendedProxySettings and recommendedGzipSettings are strictly required,
# but the rest make sense as well
<link linkend="opt-services.nginx.recommendedTlsSettings">recommendedTlsSettings</link> = true;
<link linkend="opt-services.nginx.recommendedOptimisation">recommendedOptimisation</link> = true;
<link linkend="opt-services.nginx.recommendedGzipSettings">recommendedGzipSettings</link> = true;
<link linkend="opt-services.nginx.recommendedProxySettings">recommendedProxySettings</link> = true;
<link linkend="opt-services.nginx.virtualHosts">virtualHosts</link> = {
# This host section can be placed on a different host than the rest,
# i.e. to delegate from the host being accessible as ${config.networking.domain}
# to another host actually running the Matrix homeserver.
"${config.networking.domain}" = {
<link linkend="opt-services.nginx.virtualHosts._name_.enableACME">enableACME</link> = true;
<link linkend="opt-services.nginx.virtualHosts._name_.forceSSL">forceSSL</link> = true;
<link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.extraConfig">locations."= /.well-known/matrix/server".extraConfig</link> =
let
# use 443 instead of the default 8448 port to unite
# the client-server and server-server port for simplicity
server = { "m.server" = "${fqdn}:443"; };
in ''
add_header Content-Type application/json;
return 200 '${builtins.toJSON server}';
'';
<link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.extraConfig">locations."= /.well-known/matrix/client".extraConfig</link> =
let
client = {
"m.homeserver" = { "base_url" = "https://${fqdn}"; };
"m.identity_server" = { "base_url" = "https://vector.im"; };
};
# ACAO required to allow element-web on any URL to request this json file
in ''
add_header Content-Type application/json;
add_header Access-Control-Allow-Origin *;
return 200 '${builtins.toJSON client}';
'';
};
# Reverse proxy for Matrix client-server and server-server communication
${fqdn} = {
<link linkend="opt-services.nginx.virtualHosts._name_.enableACME">enableACME</link> = true;
<link linkend="opt-services.nginx.virtualHosts._name_.forceSSL">forceSSL</link> = true;
# Or do a redirect instead of the 404, or whatever is appropriate for you.
# But do not put a Matrix Web client here! See the Element web section below.
<link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.extraConfig">locations."/".extraConfig</link> = ''
return 404;
'';
# forward all Matrix API calls to the synapse Matrix homeserver
locations."/_matrix" = {
<link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.proxyPass">proxyPass</link> = "http://[::1]:8008"; # without a trailing /
};
};
};
};
services.matrix-synapse = {
<link linkend="opt-services.matrix-synapse.enable">enable</link> = true;
<link linkend="opt-services.matrix-synapse.settings.server_name">server_name</link> = config.networking.domain;
<link linkend="opt-services.matrix-synapse.settings.listeners">listeners</link> = [
{
<link linkend="opt-services.matrix-synapse.settings.listeners._.port">port</link> = 8008;
<link linkend="opt-services.matrix-synapse.settings.listeners._.bind_addresses">bind_addresses</link> = [ "::1" ];
<link linkend="opt-services.matrix-synapse.settings.listeners._.type">type</link> = "http";
<link linkend="opt-services.matrix-synapse.settings.listeners._.tls">tls</link> = false;
<link linkend="opt-services.matrix-synapse.settings.listeners._.x_forwarded">x_forwarded</link> = true;
<link linkend="opt-services.matrix-synapse.settings.listeners._.resources">resources</link> = [ {
<link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.names">names</link> = [ "client" ];
<link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.compress">compress</link> = true;
} {
<link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.names">names</link> = [ "federation" ];
<link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.compress">compress</link> = false;
} ];
}
];
};
}
</programlisting>
</para>
<para>
If the <code>A</code> and <code>AAAA</code> DNS records on
<literal>example.org</literal> do not point on the same host as the records
for <code>myhostname.example.org</code>, you can easily move the
<code>/.well-known</code> virtualHost section of the code to the host that
is serving <literal>example.org</literal>, while the rest stays on
<literal>myhostname.example.org</literal> with no other changes required.
This pattern also allows to seamlessly move the homeserver from
<literal>myhostname.example.org</literal> to
<literal>myotherhost.example.org</literal> by only changing the
<code>/.well-known</code> redirection target.
</para>
<para>
If you want to run a server with public registration by anybody, you can
then enable <literal><link linkend="opt-services.matrix-synapse.settings.enable_registration">services.matrix-synapse.settings.enable_registration</link> =
true;</literal>. Otherwise, or you can generate a registration secret with
<command>pwgen -s 64 1</command> and set it with
<option><link linkend="opt-services.matrix-synapse.settings.registration_shared_secret">services.matrix-synapse.settings.registration_shared_secret</link></option>.
To create a new user or admin, run the following after you have set the secret
and have rebuilt NixOS:
<screen>
<prompt>$ </prompt>nix run nixpkgs.matrix-synapse
<prompt>$ </prompt>register_new_matrix_user -k <replaceable>your-registration-shared-secret</replaceable> http://localhost:8008
<prompt>New user localpart: </prompt><replaceable>your-username</replaceable>
<prompt>Password:</prompt>
<prompt>Confirm password:</prompt>
<prompt>Make admin [no]:</prompt>
Success!
</screen>
In the example, this would create a user with the Matrix Identifier
<literal>@your-username:example.org</literal>. Note that the registration
secret ends up in the nix store and therefore is world-readable by any user
on your machine, so it makes sense to only temporarily activate the
<link linkend="opt-services.matrix-synapse.settings.registration_shared_secret">registration_shared_secret</link>
option until a better solution for NixOS is in place.
</para>
</section>
<section xml:id="module-services-matrix-element-web">
<title>Element (formerly known as Riot) Web Client</title>
<para>
<link xlink:href="https://github.com/vector-im/riot-web/">Element Web</link> is
the reference web client for Matrix and developed by the core team at
matrix.org. Element was formerly known as Riot.im, see the
<link xlink:href="https://element.io/blog/welcome-to-element/">Element introductory blog post</link>
for more information. The following snippet can be optionally added to the code before
to complete the synapse installation with a web client served at
<code>https://element.myhostname.example.org</code> and
<code>https://element.example.org</code>. Alternatively, you can use the hosted
copy at <link xlink:href="https://app.element.io/">https://app.element.io/</link>,
or use other web clients or native client applications. Due to the
<literal>/.well-known</literal> urls set up done above, many clients should
fill in the required connection details automatically when you enter your
Matrix Identifier. See
<link xlink:href="https://matrix.org/docs/projects/try-matrix-now.html">Try
Matrix Now!</link> for a list of existing clients and their supported
featureset.
<programlisting>
{
services.nginx.virtualHosts."element.${fqdn}" = {
<link linkend="opt-services.nginx.virtualHosts._name_.enableACME">enableACME</link> = true;
<link linkend="opt-services.nginx.virtualHosts._name_.forceSSL">forceSSL</link> = true;
<link linkend="opt-services.nginx.virtualHosts._name_.serverAliases">serverAliases</link> = [
"element.${config.networking.domain}"
];
<link linkend="opt-services.nginx.virtualHosts._name_.root">root</link> = pkgs.element-web.override {
conf = {
default_server_config."m.homeserver" = {
"base_url" = "https://${fqdn}";
"server_name" = "${fqdn}";
};
};
};
};
}
</programlisting>
</para>
<para>
Note that the Element developers do not recommend running Element and your Matrix
homeserver on the same fully-qualified domain name for security reasons. In
the example, this means that you should not reuse the
<literal>myhostname.example.org</literal> virtualHost to also serve Element,
but instead serve it on a different subdomain, like
<literal>element.example.org</literal> in the example. See the
<link xlink:href="https://github.com/vector-im/riot-web#important-security-note">Element
Important Security Notes</link> for more information on this subject.
</para>
</section>
</chapter>

View file

@ -0,0 +1,242 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.mjolnir;
yamlConfig = {
inherit (cfg) dataPath managementRoom protectedRooms;
accessToken = "@ACCESS_TOKEN@"; # will be replaced in "generateConfig"
homeserverUrl =
if cfg.pantalaimon.enable then
"http://${cfg.pantalaimon.options.listenAddress}:${toString cfg.pantalaimon.options.listenPort}"
else
cfg.homeserverUrl;
rawHomeserverUrl = cfg.homeserverUrl;
pantalaimon = {
inherit (cfg.pantalaimon) username;
use = cfg.pantalaimon.enable;
password = "@PANTALAIMON_PASSWORD@"; # will be replaced in "generateConfig"
};
};
moduleConfigFile = pkgs.writeText "module-config.yaml" (
generators.toYAML { } (filterAttrs (_: v: v != null)
(fold recursiveUpdate { } [ yamlConfig cfg.settings ])));
# these config files will be merged one after the other to build the final config
configFiles = [
"${pkgs.mjolnir}/share/mjolnir/config/default.yaml"
moduleConfigFile
];
# this will generate the default.yaml file with all configFiles as inputs and
# replace all secret strings using replace-secret
generateConfig = pkgs.writeShellScript "mjolnir-generate-config" (
let
yqEvalStr = concatImapStringsSep " * " (pos: _: "select(fileIndex == ${toString (pos - 1)})") configFiles;
yqEvalArgs = concatStringsSep " " configFiles;
in
''
set -euo pipefail
umask 077
# mjolnir will try to load a config from "./config/default.yaml" in the working directory
# -> let's place the generated config there
mkdir -p ${cfg.dataPath}/config
# merge all config files into one, overriding settings of the previous one with the next config
# e.g. "eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' filea.yaml fileb.yaml" will merge filea.yaml with fileb.yaml
${pkgs.yq-go}/bin/yq eval-all -P '${yqEvalStr}' ${yqEvalArgs} > ${cfg.dataPath}/config/default.yaml
${optionalString (cfg.accessTokenFile != null) ''
${pkgs.replace-secret}/bin/replace-secret '@ACCESS_TOKEN@' '${cfg.accessTokenFile}' ${cfg.dataPath}/config/default.yaml
''}
${optionalString (cfg.pantalaimon.passwordFile != null) ''
${pkgs.replace-secret}/bin/replace-secret '@PANTALAIMON_PASSWORD@' '${cfg.pantalaimon.passwordFile}' ${cfg.dataPath}/config/default.yaml
''}
''
);
in
{
options.services.mjolnir = {
enable = mkEnableOption "Mjolnir, a moderation tool for Matrix";
homeserverUrl = mkOption {
type = types.str;
default = "https://matrix.org";
description = ''
Where the homeserver is located (client-server URL).
If <literal>pantalaimon.enable</literal> is <literal>true</literal>, this option will become the homeserver to which <literal>pantalaimon</literal> connects.
The listen address of <literal>pantalaimon</literal> will then become the <literal>homeserverUrl</literal> of <literal>mjolnir</literal>.
'';
};
accessTokenFile = mkOption {
type = with types; nullOr path;
default = null;
description = ''
File containing the matrix access token for the <literal>mjolnir</literal> user.
'';
};
pantalaimon = mkOption {
description = ''
<literal>pantalaimon</literal> options (enables E2E Encryption support).
This will create a <literal>pantalaimon</literal> instance with the name "mjolnir".
'';
default = { };
type = types.submodule {
options = {
enable = mkEnableOption ''
If true, accessToken is ignored and the username/password below will be
used instead. The access token of the bot will be stored in the dataPath.
'';
username = mkOption {
type = types.str;
description = "The username to login with.";
};
passwordFile = mkOption {
type = with types; nullOr path;
default = null;
description = ''
File containing the matrix password for the <literal>mjolnir</literal> user.
'';
};
options = mkOption {
type = types.submodule (import ./pantalaimon-options.nix);
default = { };
description = ''
passthrough additional options to the <literal>pantalaimon</literal> service.
'';
};
};
};
};
dataPath = mkOption {
type = types.path;
default = "/var/lib/mjolnir";
description = ''
The directory the bot should store various bits of information in.
'';
};
managementRoom = mkOption {
type = types.str;
default = "#moderators:example.org";
description = ''
The room ID where people can use the bot. The bot has no access controls, so
anyone in this room can use the bot - secure your room!
This should be a room alias or room ID - not a matrix.to URL.
Note: <literal>mjolnir</literal> is fairly verbose - expect a lot of messages from it.
'';
};
protectedRooms = mkOption {
type = types.listOf types.str;
default = [ ];
example = literalExpression ''
[
"https://matrix.to/#/#yourroom:example.org"
"https://matrix.to/#/#anotherroom:example.org"
]
'';
description = ''
A list of rooms to protect (matrix.to URLs).
'';
};
settings = mkOption {
default = { };
type = (pkgs.formats.yaml { }).type;
example = literalExpression ''
{
autojoinOnlyIfManager = true;
automaticallyRedactForReasons = [ "spam" "advertising" ];
}
'';
description = ''
Additional settings (see <link xlink:href="https://github.com/matrix-org/mjolnir/blob/main/config/default.yaml">mjolnir default config</link> for available settings). These settings will override settings made by the module config.
'';
};
};
config = mkIf config.services.mjolnir.enable {
assertions = [
{
assertion = !(cfg.pantalaimon.enable && cfg.pantalaimon.passwordFile == null);
message = "Specify pantalaimon.passwordFile";
}
{
assertion = !(cfg.pantalaimon.enable && cfg.accessTokenFile != null);
message = "Do not specify accessTokenFile when using pantalaimon";
}
{
assertion = !(!cfg.pantalaimon.enable && cfg.accessTokenFile == null);
message = "Specify accessTokenFile when not using pantalaimon";
}
];
services.pantalaimon-headless.instances."mjolnir" = mkIf cfg.pantalaimon.enable
{
homeserver = cfg.homeserverUrl;
} // cfg.pantalaimon.options;
systemd.services.mjolnir = {
description = "mjolnir - a moderation tool for Matrix";
wants = [ "network-online.target" ] ++ optionals (cfg.pantalaimon.enable) [ "pantalaimon-mjolnir.service" ];
after = [ "network-online.target" ] ++ optionals (cfg.pantalaimon.enable) [ "pantalaimon-mjolnir.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = ''${pkgs.mjolnir}/bin/mjolnir'';
ExecStartPre = [ generateConfig ];
WorkingDirectory = cfg.dataPath;
StateDirectory = "mjolnir";
StateDirectoryMode = "0700";
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
NoNewPrivileges = true;
PrivateDevices = true;
User = "mjolnir";
Restart = "on-failure";
/* TODO: wait for #102397 to be resolved. Then load secrets from $CREDENTIALS_DIRECTORY+"/NAME"
DynamicUser = true;
LoadCredential = [] ++
optionals (cfg.accessTokenFile != null) [
"access_token:${cfg.accessTokenFile}"
] ++
optionals (cfg.pantalaimon.passwordFile != null) [
"pantalaimon_password:${cfg.pantalaimon.passwordFile}"
];
*/
};
};
users = {
users.mjolnir = {
group = "mjolnir";
isSystemUser = true;
};
groups.mjolnir = { };
};
};
meta = {
doc = ./mjolnir.xml;
maintainers = with maintainers; [ jojosch ];
};
}

View file

@ -0,0 +1,134 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:id="module-services-mjolnir">
<title>Mjolnir (Matrix Moderation Tool)</title>
<para>
This chapter will show you how to set up your own, self-hosted
<link xlink:href="https://github.com/matrix-org/mjolnir">Mjolnir</link>
instance.
</para>
<para>
As an all-in-one moderation tool, it can protect your server from
malicious invites, spam messages, and whatever else you don't want.
In addition to server-level protection, Mjolnir is great for communities
wanting to protect their rooms without having to use their personal
accounts for moderation.
</para>
<para>
The bot by default includes support for bans, redactions, anti-spam,
server ACLs, room directory changes, room alias transfers, account
deactivation, room shutdown, and more.
</para>
<para>
See the <link xlink:href="https://github.com/matrix-org/mjolnir#readme">README</link>
page and the <link xlink:href="https://github.com/matrix-org/mjolnir/blob/main/docs/moderators.md">Moderator's guide</link>
for additional instructions on how to setup and use Mjolnir.
</para>
<para>
For <link linkend="opt-services.mjolnir.settings">additional settings</link>
see <link xlink:href="https://github.com/matrix-org/mjolnir/blob/main/config/default.yaml">the default configuration</link>.
</para>
<section xml:id="module-services-mjolnir-setup">
<title>Mjolnir Setup</title>
<para>
First create a new Room which will be used as a management room for Mjolnir. In
this room, Mjolnir will log possible errors and debugging information. You'll
need to set this Room-ID in <link linkend="opt-services.mjolnir.managementRoom">services.mjolnir.managementRoom</link>.
</para>
<para>
Next, create a new user for Mjolnir on your homeserver, if not present already.
</para>
<para>
The Mjolnir Matrix user expects to be free of any rate limiting.
See <link xlink:href="https://github.com/matrix-org/synapse/issues/6286">Synapse #6286</link>
for an example on how to achieve this.
</para>
<para>
If you want Mjolnir to be able to deactivate users, move room aliases, shutdown rooms, etc.
you'll need to make the Mjolnir user a Matrix server admin.
</para>
<para>
Now invite the Mjolnir user to the management room.
</para>
<para>
It is recommended to use <link xlink:href="https://github.com/matrix-org/pantalaimon">Pantalaimon</link>,
so your management room can be encrypted. This also applies if you are looking to moderate an encrypted room.
</para>
<para>
To enable the Pantalaimon E2E Proxy for mjolnir, enable
<link linkend="opt-services.mjolnir.pantalaimon.enable">services.mjolnir.pantalaimon</link>. This will
autoconfigure a new Pantalaimon instance, which will connect to the homeserver
set in <link linkend="opt-services.mjolnir.homeserverUrl">services.mjolnir.homeserverUrl</link> and Mjolnir itself
will be configured to connect to the new Pantalaimon instance.
</para>
<programlisting>
{
services.mjolnir = {
enable = true;
<link linkend="opt-services.mjolnir.homeserverUrl">homeserverUrl</link> = "https://matrix.domain.tld";
<link linkend="opt-services.mjolnir.pantalaimon">pantalaimon</link> = {
<link linkend="opt-services.mjolnir.pantalaimon.enable">enable</link> = true;
<link linkend="opt-services.mjolnir.pantalaimon.username">username</link> = "mjolnir";
<link linkend="opt-services.mjolnir.pantalaimon.passwordFile">passwordFile</link> = "/run/secrets/mjolnir-password";
};
<link linkend="opt-services.mjolnir.protectedRooms">protectedRooms</link> = [
"https://matrix.to/#/!xxx:domain.tld"
];
<link linkend="opt-services.mjolnir.managementRoom">managementRoom</link> = "!yyy:domain.tld";
};
}
</programlisting>
<section xml:id="module-services-mjolnir-setup-ems">
<title>Element Matrix Services (EMS)</title>
<para>
If you are using a managed <link xlink:href="https://ems.element.io/">"Element Matrix Services (EMS)"</link>
server, you will need to consent to the terms and conditions. Upon startup, an error
log entry with a URL to the consent page will be generated.
</para>
</section>
</section>
<section xml:id="module-services-mjolnir-matrix-synapse-antispam">
<title>Synapse Antispam Module</title>
<para>
A Synapse module is also available to apply the same rulesets the bot
uses across an entire homeserver.
</para>
<para>
To use the Antispam Module, add <package>matrix-synapse-plugins.matrix-synapse-mjolnir-antispam</package>
to the Synapse plugin list and enable the <literal>mjolnir.Module</literal> module.
</para>
<programlisting>
{
services.matrix-synapse = {
plugins = with pkgs; [
matrix-synapse-plugins.matrix-synapse-mjolnir-antispam
];
extraConfig = ''
modules:
- module: mjolnir.Module
config:
# Prevent servers/users in the ban lists from inviting users on this
# server to rooms. Default true.
block_invites: true
# Flag messages sent by servers/users in the ban lists as spam. Currently
# this means that spammy messages will appear as empty to users. Default
# false.
block_messages: false
# Remove users from the user directory search by filtering matrix IDs and
# display names by the entries in the user ban list. Default false.
block_usernames: false
# The room IDs of the ban lists to honour. Unlike other parts of Mjolnir,
# this list cannot be room aliases or permalinks. This server is expected
# to already be joined to the room - Mjolnir will not automatically join
# these rooms.
ban_lists:
- "!roomid:example.org"
'';
};
}
</programlisting>
</section>
</chapter>

View file

@ -0,0 +1,70 @@
{ config, lib, name, ... }:
with lib;
{
options = {
dataPath = mkOption {
type = types.path;
default = "/var/lib/pantalaimon-${name}";
description = ''
The directory where <literal>pantalaimon</literal> should store its state such as the database file.
'';
};
logLevel = mkOption {
type = types.enum [ "info" "warning" "error" "debug" ];
default = "warning";
description = ''
Set the log level of the daemon.
'';
};
homeserver = mkOption {
type = types.str;
example = "https://matrix.org";
description = ''
The URI of the homeserver that the <literal>pantalaimon</literal> proxy should
forward requests to, without the matrix API path but including
the http(s) schema.
'';
};
ssl = mkOption {
type = types.bool;
default = true;
description = ''
Whether or not SSL verification should be enabled for outgoing
connections to the homeserver.
'';
};
listenAddress = mkOption {
type = types.str;
default = "localhost";
description = ''
The address where the daemon will listen to client connections
for this homeserver.
'';
};
listenPort = mkOption {
type = types.port;
default = 8009;
description = ''
The port where the daemon will listen to client connections for
this homeserver. Note that the listen address/port combination
needs to be unique between different homeservers.
'';
};
extraSettings = mkOption {
type = types.attrs;
default = { };
description = ''
Extra configuration options. See
<link xlink:href="https://github.com/matrix-org/pantalaimon/blob/master/docs/man/pantalaimon.5.md">pantalaimon(5)</link>
for available options.
'';
};
};
}

View file

@ -0,0 +1,70 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.pantalaimon-headless;
iniFmt = pkgs.formats.ini { };
mkConfigFile = name: instanceConfig: iniFmt.generate "pantalaimon.conf" {
Default = {
LogLevel = instanceConfig.logLevel;
Notifications = false;
};
${name} = (recursiveUpdate
{
Homeserver = instanceConfig.homeserver;
ListenAddress = instanceConfig.listenAddress;
ListenPort = instanceConfig.listenPort;
SSL = instanceConfig.ssl;
# Set some settings to prevent user interaction for headless operation
IgnoreVerification = true;
UseKeyring = false;
}
instanceConfig.extraSettings
);
};
mkPantalaimonService = name: instanceConfig:
nameValuePair "pantalaimon-${name}" {
description = "pantalaimon instance ${name} - E2EE aware proxy daemon for matrix clients";
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = ''${pkgs.pantalaimon-headless}/bin/pantalaimon --config ${mkConfigFile name instanceConfig} --data-path ${instanceConfig.dataPath}'';
Restart = "on-failure";
DynamicUser = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateTmp = true;
ProtectHome = true;
ProtectSystem = "strict";
StateDirectory = "pantalaimon-${name}";
};
};
in
{
options.services.pantalaimon-headless.instances = mkOption {
default = { };
type = types.attrsOf (types.submodule (import ./pantalaimon-options.nix));
description = ''
Declarative instance config.
Note: to use pantalaimon interactively, e.g. for a Matrix client which does not
support End-to-end encryption (like <literal>fractal</literal>), refer to the home-manager module.
'';
};
config = mkIf (config.services.pantalaimon-headless.instances != { })
{
systemd.services = mapAttrs' mkPantalaimonService config.services.pantalaimon-headless.instances;
};
meta = {
maintainers = with maintainers; [ jojosch ];
};
}