matrix-synapse: add automatic user creation
Some checks failed
checks / checks-impure (pull_request) Successful in 2m31s
buildbot/nix-eval Build done.

This commit is contained in:
Jörg Thalheim 2024-06-11 13:15:30 +02:00
parent a6a9f763db
commit e88c8ad78a
3 changed files with 243 additions and 19 deletions

View File

@ -4,7 +4,7 @@
name = "matrix-synapse";
nodes.machine =
{ self, lib, ... }:
{ config, self, lib, ... }:
{
imports = [
self.clanModules.matrix-synapse
@ -12,17 +12,47 @@
{
clanCore.machineName = "machine";
clanCore.clanDir = ./.;
clan.matrix-synapse = {
domain = "clan.test";
};
}
{
# secret override
clanCore.facts.services.matrix-synapse.secret.synapse-registration_shared_secret.path = "${./synapse-registration_shared_secret}";
services.nginx.virtualHosts."matrix.clan.test" = {
enableACME = lib.mkForce false;
forceSSL = lib.mkForce false;
};
clan.matrix-synapse.domain = "clan.test";
clan.matrix-synapse.users.admin.admin = true;
clan.matrix-synapse.users.someuser = { };
clanCore.facts.secretStore = "vm";
# because we use systemd-tmpfiles to copy the secrets, we need to a seperate systemd-tmpfiles call to provison them.
boot.postBootCommands = "${config.systemd.package}/bin/systemd-tmpfiles --create /etc/tmpfiles.d/00-vmsecrets.conf";
systemd.tmpfiles.settings."00-vmsecrets" = { # run before 00-nixos.conf
"/etc/secrets" = {
d.mode = "0700";
z.mode = "0700";
};
"/etc/secrets/synapse-registration_shared_secret" = {
f.argument = "registration_shared_secret: supersecret";
z = {
mode = "0400";
user = "root";
};
};
"/etc/secrets/matrix-password-admin" = {
f.argument = "matrix-password1";
z = {
mode = "0400";
user = "root";
};
};
"/etc/secrets/matrix-password-someuser" = {
f.argument = "matrix-password2";
z = {
mode = "0400";
user = "root";
};
};
};
}
];
};
@ -31,6 +61,12 @@
machine.wait_for_unit("matrix-synapse")
machine.succeed("${pkgs.netcat}/bin/nc -z -v ::1 8008")
machine.succeed("${pkgs.curl}/bin/curl -Ssf -L http://localhost/_matrix/static/ -H 'Host: matrix.clan.test'")
machine.systemctl("restart matrix-synapse >&2") # check if user creation is idempotent
machine.execute("journalctl -u matrix-synapse --no-pager >&2")
machine.wait_for_unit("matrix-synapse")
machine.succeed("${pkgs.netcat}/bin/nc -z -v ::1 8008")
machine.succeed("${pkgs.curl}/bin/curl -Ssf -L http://localhost/_matrix/static/ -H 'Host: matrix.clan.test'")
'';
}
)

View File

@ -0,0 +1,84 @@
From df45634a92944dcab4edb02fb5e478911c58fdd6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= <joerg@thalheim.io>
Date: Tue, 11 Jun 2024 11:40:47 +0200
Subject: [PATCH] register_new_matrix_user: add password-file flag
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
getpass in python exist on stdin to be a tty, hence we cannot just pipe
into register_new_matrix_user. --password-file instead works better and
it would also allow the use of stdin if /dev/stdin is passed.
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
debian/register_new_matrix_user.ronn | 6 +++++-
synapse/_scripts/register_new_matrix_user.py | 16 ++++++++++++++--
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/debian/register_new_matrix_user.ronn b/debian/register_new_matrix_user.ronn
index 0410b1f4c..e39bef448 100644
--- a/debian/register_new_matrix_user.ronn
+++ b/debian/register_new_matrix_user.ronn
@@ -32,7 +32,11 @@ A sample YAML file accepted by `register_new_matrix_user` is described below:
* `-p`, `--password`:
New password for user. Will prompt if omitted. Supplying the password
- on the command line is not recommended. Use the STDIN instead.
+ on the command line is not recommended. Use password-file if possible.
+
+ * `--password-file`:
+ File containing the new password for user. Will prompt if omitted.
+ This is a more secure alternative to specifying the password on the command line.
* `-a`, `--admin`:
Register new user as an admin. Will prompt if omitted.
diff --git a/synapse/_scripts/register_new_matrix_user.py b/synapse/_scripts/register_new_matrix_user.py
index 77a7129ee..f067e6832 100644
--- a/synapse/_scripts/register_new_matrix_user.py
+++ b/synapse/_scripts/register_new_matrix_user.py
@@ -173,12 +173,18 @@ def main() -> None:
default=None,
help="Local part of the new user. Will prompt if omitted.",
)
- parser.add_argument(
+ password_group = parser.add_mutually_exclusive_group()
+ password_group.add_argument(
"-p",
"--password",
default=None,
help="New password for user. Will prompt if omitted.",
)
+ password_group.add_argument(
+ "--password-file",
+ default=None,
+ help="File containing the new password for user. Will prompt if omitted.",
+ )
parser.add_argument(
"-t",
"--user_type",
@@ -247,6 +253,12 @@ def main() -> None:
print(_NO_SHARED_SECRET_OPTS_ERROR, file=sys.stderr)
sys.exit(1)
+ password = ""
+ if args.password_file:
+ password = _read_file(args.password_file, "password-file").strip()
+ else:
+ password = args.password
+
if args.server_url:
server_url = args.server_url
elif config is not None:
@@ -270,7 +282,7 @@ def main() -> None:
admin = args.admin
register_new_user(
- args.user, args.password, server_url, secret, admin, args.user_type
+ args.user, password, server_url, secret, admin, args.user_type
)
--
2.44.1

View File

@ -16,16 +16,58 @@ let
> $out/config.json < ${pkgs.element-web}/config.json
ln -s $out/config.json $out/config.${nginx-vhost}.json
'';
# FIXME: This was taken from upstream. Drop this when our patch is upstream
synapseCfg = config.services.matrix-synapse;
wantedExtras =
synapseCfg.extras
++ lib.optional (synapseCfg.settings ? oidc_providers) "oidc"
++ lib.optional (synapseCfg.settings ? jwt_config) "jwt"
++ lib.optional (synapseCfg.settings ? saml2_config) "saml2"
++ lib.optional (synapseCfg.settings ? redis) "redis"
++ lib.optional (synapseCfg.settings ? sentry) "sentry"
++ lib.optional (synapseCfg.settings ? user_directory) "user-search"
++ lib.optional (synapseCfg.settings.url_preview_enabled) "url-preview"
++ lib.optional (synapseCfg.settings.database.name == "psycopg2") "postgres";
in
{
options.services.matrix-synapse.package = lib.mkOption { readOnly = false; };
options.clan.matrix-synapse = {
domain = lib.mkOption {
type = lib.types.str;
description = "The domain name of the matrix server";
example = "example.com";
};
users = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule (
{ name, ... }:
{
options = {
name = lib.mkOption {
type = lib.types.str;
default = name;
description = "The name of the user";
};
admin = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether the user should be an admin";
};
};
}
)
);
description = "A list of users. Not that only new users will be created and existing ones are not modified.";
example.alice = {
admin = true;
};
};
};
imports = [
../postgresql
(lib.mkRemovedOptionModule [
"clan"
"matrix-synapse"
@ -36,6 +78,17 @@ in
];
config = {
services.matrix-synapse = {
package = lib.mkForce (
pkgs.matrix-synapse.override {
matrix-synapse-unwrapped = pkgs.matrix-synapse.unwrapped.overrideAttrs (old: {
doInstallCheck = false; # too slow, nixpkgs maintainer already run this.
patches = [ ./0001-register_new_matrix_user-add-password-file-flag.patch ];
});
extras = wantedExtras;
plugins = synapseCfg.plugins;
}
);
enable = true;
settings = {
server_name = cfg.domain;
@ -70,7 +123,10 @@ in
};
extraConfigFiles = [ "/run/synapse-registration-shared-secret.yaml" ];
};
systemd.tmpfiles.settings."synapse" = {
security.wrappers = lib.mkForce { }; # unsupported in unprivileged containers
systemd.tmpfiles.settings."01-matrix" = {
"/run/synapse-registration-shared-secret.yaml" = {
C.argument =
config.clanCore.facts.services.matrix-synapse.secret.synapse-registration_shared_secret.path;
@ -90,16 +146,64 @@ in
OWNER = "matrix-synapse";
};
clanCore.facts.services."matrix-synapse" = {
secret."synapse-registration_shared_secret" = { };
generator.path = with pkgs; [
coreutils
pwgen
];
generator.script = ''
echo "registration_shared_secret: $(pwgen -s 32 1)" > "$secrets"/synapse-registration_shared_secret
'';
};
clanCore.facts.services =
{
"matrix-synapse" = {
secret."synapse-registration_shared_secret" = { };
generator.path = with pkgs; [
coreutils
pwgen
];
generator.script = ''
echo "registration_shared_secret: $(pwgen -s 32 1)" > "$secrets"/synapse-registration_shared_secret
'';
};
}
// lib.mapAttrs' (
name: user:
lib.nameValuePair "matrix-password-${user.name}" {
secret."matrix-password-${user.name}" = { };
generator.path = with pkgs; [
coreutils
pwgen
];
generator.script = ''
xkcdpass -n 4 -d - > "$secrets"/${lib.escapeShellArg "matrix-password-${user.name}"}
'';
}
) cfg.users;
systemd.services.matrix-synapse =
let
usersScript =
''
while ! ${pkgs.netcat}/bin/nc -z -v ::1 8008; do
if ! kill -0 "$MAINPID"; then exit 1; fi
sleep 1;
done
headers=$(mktemp)
trap 'rm -f "$headers"' EXIT
cat > "$headers" <<EOF
Authorization: Bearer $(cat /run/synapse-registration-shared-secret.yaml| sed -n 's/registration_shared_secret: //p')
EOF
''
+ lib.concatMapStringsSep "\n" (user: ''
# only create user if it doesn't exist
if ! curl --header "$headers" "http://localhost:8008/_synapse/admin/v1/whois/${user.name}@${cfg.domain}" >&2; then
/run/current-system/sw/bin/matrix-synapse-register_new_matrix_user --password-file ${
config.clanCore.facts.services."matrix-password-${user.name}".secret."matrix-password-${user.name}".path
} --user "${user.name}" ${if user.admin then "--admin" else "--no-admin"}
fi
'') (lib.attrValues cfg.users);
in
{
path = [ pkgs.curl ];
serviceConfig.ExecStartPost = [
(''+${pkgs.writeShellScript "matrix-synapse-create-users" usersScript}'')
];
};
services.nginx = {
enable = true;