nixos/matrix-authentication-service: init

Co-authored-by: mkoppmann <dev@mkoppmann.at>
Co-authored-by: teutat3s <10206665+teutat3s@users.noreply.github.com>
Co-authored-by: FlashOnFire_ <flashonfire@proton.me>
Assisted-by: gemini-cli
This commit is contained in:
eymeric
2026-06-26 14:55:45 +07:00
co-authored by mkoppmann teutat3s FlashOnFire_
parent fe63b1af18
commit 0bb6415b55
3 changed files with 448 additions and 0 deletions
@@ -24,6 +24,8 @@
- [Unpackerr](https://unpackerr.zip), extracts downloads for Radarr, Sonarr, Lidarr, Readarr, and/or a Watch folder. Available as [services.unpackerr](#opt-services.unpackerr.enable).
- [Matrix Authentication Service](https://github.com/element-hq/matrix-authentication-service) is an OAuth2.0 and OpenID Connect provider for Matrix homeservers (such as Synapse). It replaces standard password authentication with modern OpenID Connect flows, and can delegate authentication to upstream OIDC providers. Available as [services.matrix-authentication-service](#opt-services.matrix-authentication-service.enable).
## Backward Incompatibilities {#sec-release-26.11-incompatibilities}
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
+1
View File
@@ -809,6 +809,7 @@
./services/matrix/hookshot.nix
./services/matrix/lk-jwt-service.nix
./services/matrix/matrix-alertmanager.nix
./services/matrix/matrix-authentication-service.nix
./services/matrix/maubot.nix
./services/matrix/mautrix-discord.nix
./services/matrix/mautrix-meta.nix
@@ -0,0 +1,445 @@
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
concatMapStringsSep
filter
filterAttrs
getExe
isAttrs
isList
mapAttrs
mkDefault
mkEnableOption
mkIf
mkOption
mkPackageOption
optional
optionalAttrs
types
;
cfg = config.services.matrix-authentication-service;
format = pkgs.formats.yaml { };
filterRecursiveNull =
o:
if isAttrs o then
mapAttrs (_: v: filterRecursiveNull v) (filterAttrs (_: v: v != null) o)
else if isList o then
map filterRecursiveNull (filter (v: v != null) o)
else
o;
# remove null values from the final configuration
finalSettings =
let
pruned = filterRecursiveNull cfg.settings;
in
if pruned ? upstream_oauth2 && pruned.upstream_oauth2 == { } then
removeAttrs pruned [ "upstream_oauth2" ]
else
pruned;
configFile = format.generate "config.yaml" finalSettings;
in
{
meta.maintainers = with lib.maintainers; [
eymeric
flashonfire
mkoppmann
skowalak
];
meta.teams = [ lib.teams.matrix ];
options.services.matrix-authentication-service = {
enable = mkEnableOption "Matrix Authentication Service";
package = mkPackageOption pkgs "matrix-authentication-service" { };
settings = mkOption {
default = { };
description = ''
The primary mas configuration. See the
[configuration reference](https://element-hq.github.io/matrix-authentication-service/usage/configuration.html)
for possible values.
Secrets should be passed in by using the `extraConfigFiles` option.
'';
type = types.submodule {
freeformType = format.type;
options = {
http.public_base = mkOption {
type = types.str;
default = "http://[::]:8080/";
description = ''
Public URL base used when building absolute public URLs.
'';
};
http.trusted_proxies = mkOption {
type = types.listOf types.str;
default = [
"127.0.0.1/8"
"::1/128"
];
description = ''
MAS can infer the client IP address from the X-Forwarded-For header. It will trust the value for this header only if the request comes from a trusted reverse proxy listed here.
'';
};
http.listeners = mkOption {
type = types.listOf (
types.submodule {
freeformType = format.type;
options = {
name = mkOption {
type = types.str;
example = "web";
description = ''
The name of the listener, used in logs and metrics.
'';
};
proxy_protocol = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable the PROXY protocol on the listener.
'';
};
resources = mkOption {
type = types.listOf (
types.submodule {
freeformType = format.type;
options = {
name = mkOption {
type = types.str;
description = ''
Serve the given resource.
'';
};
};
}
);
description = ''
List of resources to serve.
'';
};
binds = mkOption {
type = types.listOf (
types.submodule {
freeformType = format.type;
options = {
host = mkOption {
type = types.nullOr types.str;
description = ''
Listen on the given host.
'';
};
port = mkOption {
type = types.nullOr types.port;
description = ''
Listen on the given port.
'';
};
};
}
);
description = ''
List of addresses and ports to listen to.
'';
};
};
}
);
default = [
{
name = "web";
resources = [
{ name = "discovery"; }
{ name = "human"; }
{ name = "oauth"; }
{ name = "compat"; }
{ name = "graphql"; }
{ name = "assets"; }
];
binds = [
{
host = "0.0.0.0";
port = 8080;
}
];
proxy_protocol = false;
}
{
name = "internal";
resources = [
{ name = "health"; }
];
binds = [
{
host = "0.0.0.0";
port = 8081;
}
];
proxy_protocol = false;
}
];
description = ''
Each listener can serve multiple resources, and listen on multiple TCP ports or UNIX sockets.
'';
};
database.uri = mkOption {
type = types.str;
default = "postgresql:///matrix-authentication-service?host=/run/postgresql";
description = ''
The postgres connection string.
Refer to <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING>.
If you need to put secrets in the uri, please use the `extraConfigFiles` option.
'';
};
database.max_connections = mkOption {
type = types.ints.unsigned;
default = 10;
description = ''
Maximum number of connections for the connection pool.
'';
};
database.min_connections = mkOption {
type = types.ints.unsigned;
default = 0;
description = ''
Minimum number of connections for the connection pool.
'';
};
database.connect_timeout = mkOption {
type = types.ints.unsigned;
default = 30;
description = ''
Connection timeout for the connection pool.
'';
};
database.idle_timeout = mkOption {
type = types.ints.unsigned;
default = 600;
description = ''
Idle timeout for the connection pool.
'';
};
database.max_lifetime = mkOption {
type = types.ints.unsigned;
default = 1800;
description = ''
Maximum lifetime for the connection pool.
'';
};
passwords.enabled = mkOption {
type = types.bool;
default = true;
description = ''
Whether to enable the password database. If disabled, users will only be able to log in using upstream OIDC providers.
'';
};
passwords.schemes = mkOption {
type = types.listOf (
types.submodule {
freeformType = format.type;
options = {
version = mkOption {
type = types.ints.unsigned;
description = ''
Password scheme version.
'';
};
algorithm = mkOption {
type = types.str;
description = ''
Password scheme algorithm.
'';
};
};
}
);
default = [
{
version = 1;
algorithm = "argon2id";
}
];
description = ''
List of password hashing schemes being used. Only change this if you know what you're doing.
'';
};
passwords.minimum_complexity = mkOption {
type = types.enum [
0
1
2
3
4
];
default = 3;
description = ''
Minimum complexity required for passwords, estimated by the zxcvbn algorithm.
Must be between 0 and 4, default is 3. See <https://github.com/dropbox/zxcvbn#usage> for more information.
'';
};
matrix.homeserver = mkOption {
type = types.str;
default = "";
description = ''
Corresponds to the server_name in the Synapse configuration file.
'';
};
matrix.endpoint = mkOption {
type = types.str;
default = "";
description = ''
The URL to which the homeserver is accessible from the service.
'';
};
upstream_oauth2.providers = mkOption {
default = null;
type = types.nullOr (
types.listOf (
types.submodule {
freeformType = format.type;
options = {
id = mkOption {
type = types.nullOr types.str;
example = "01H8PKNWKKRPCBW4YGH1RWV279";
default = null;
description = ''
Unique id for the provider, must be a ULID, and can be generated using online tools like <https://www.ulidtools.com>.
'';
};
};
}
)
);
description = ''
Configuration of upstream providers
'';
};
};
};
};
createDatabase = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable and configure `services.postgresql` to ensure that the database user `matrix-authentication-service`
and the database `matrix-authentication-service` exist.
'';
};
extraConfigFiles = mkOption {
type = types.listOf types.str;
default = [ ];
description = ''
Extra config files to include.
The configuration files will be included based on the command line
argument --config. This allows to configure secrets without
having to go through the Nix store, e.g. based on deployment keys if
NixOps is in use.
'';
};
serviceDependencies = mkOption {
type = types.listOf types.str;
default = optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit;
defaultText = lib.literalExpression ''
lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit
'';
description = ''
List of Systemd services to require and wait for when starting the application service,
such as the Matrix homeserver if it's running on the same host.
'';
};
};
config = mkIf cfg.enable {
services.postgresql = mkIf cfg.createDatabase {
enable = true;
ensureDatabases = [ "matrix-authentication-service" ];
ensureUsers = [
{
name = "matrix-authentication-service";
ensureDBOwnership = true;
}
];
};
systemd.services.matrix-authentication-service = rec {
after = optional cfg.createDatabase "postgresql.service" ++ cfg.serviceDependencies;
wants = after;
wantedBy = [ "multi-user.target" ];
serviceConfig = {
DynamicUser = true;
ExecStartPre = ''
${getExe cfg.package} config check \
${concatMapStringsSep " " (x: "--config ${x}") ([ configFile ] ++ cfg.extraConfigFiles)}
'';
ExecStart = ''
${getExe cfg.package} server \
${concatMapStringsSep " " (x: "--config ${x}") ([ configFile ] ++ cfg.extraConfigFiles)}
'';
Restart = "on-failure";
RestartSec = "1s";
# Security Hardening
CapabilityBoundingSet = "";
AmbientCapabilities = "";
LockPersonality = true;
NoNewPrivileges = true;
PrivateTmp = true;
PrivateDevices = true;
PrivateMounts = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProcSubset = "pid";
ProtectSystem = "strict";
RemoveIPC = true;
RestrictAddressFamilies = [
"AF_UNIX"
"AF_INET"
"AF_INET6"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallErrorNumber = "EPERM";
SystemCallFilter = [
"@system-service"
];
UMask = "0077";
# Working and state directories
StateDirectory = "matrix-authentication-service";
StateDirectoryMode = "0700";
WorkingDirectory = "/var/lib/matrix-authentication-service";
};
};
};
}