2e751c0772
the conversion procedure is simple: - find all things that look like options, ie calls to either `mkOption` or `lib.mkOption` that take an attrset. remember the attrset as the option - for all options, find a `description` attribute who's value is not a call to `mdDoc` or `lib.mdDoc` - textually convert the entire value of the attribute to MD with a few simple regexes (the set from mdize-module.sh) - if the change produced a change in the manual output, discard - if the change kept the manual unchanged, add some text to the description to make sure we've actually found an option. if the manual changes this time, keep the converted description this procedure converts 80% of nixos options to markdown. around 2000 options remain to be inspected, but most of those fail the "does not change the manual output check": currently the MD conversion process does not faithfully convert docbook tags like <code> and <package>, so any option using such tags will not be converted at all.
223 lines
6.7 KiB
Nix
223 lines
6.7 KiB
Nix
{ config, pkgs, lib, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
cfg = config.services.heisenbridge;
|
|
|
|
pkg = config.services.heisenbridge.package;
|
|
bin = "${pkg}/bin/heisenbridge";
|
|
|
|
jsonType = (pkgs.formats.json { }).type;
|
|
|
|
registrationFile = "/var/lib/heisenbridge/registration.yml";
|
|
# JSON is a proper subset of YAML
|
|
bridgeConfig = builtins.toFile "heisenbridge-registration.yml" (builtins.toJSON {
|
|
id = "heisenbridge";
|
|
url = cfg.registrationUrl;
|
|
# Don't specify as_token and hs_token
|
|
rate_limited = false;
|
|
sender_localpart = "heisenbridge";
|
|
namespaces = cfg.namespaces;
|
|
});
|
|
in
|
|
{
|
|
options.services.heisenbridge = {
|
|
enable = mkEnableOption "the Matrix to IRC bridge";
|
|
|
|
package = mkOption {
|
|
type = types.package;
|
|
default = pkgs.heisenbridge;
|
|
defaultText = "pkgs.heisenbridge";
|
|
example = "pkgs.heisenbridge.override { … = …; }";
|
|
description = lib.mdDoc ''
|
|
Package of the application to run, exposed for overriding purposes.
|
|
'';
|
|
};
|
|
|
|
homeserver = mkOption {
|
|
type = types.str;
|
|
description = lib.mdDoc "The URL to the home server for client-server API calls";
|
|
example = "http://localhost:8008";
|
|
};
|
|
|
|
registrationUrl = mkOption {
|
|
type = types.str;
|
|
description = lib.mdDoc ''
|
|
The URL where the application service is listening for HS requests, from the Matrix HS perspective.#
|
|
The default value assumes the bridge runs on the same host as the home server, in the same network.
|
|
'';
|
|
example = "https://matrix.example.org";
|
|
default = "http://${cfg.address}:${toString cfg.port}";
|
|
defaultText = "http://$${cfg.address}:$${toString cfg.port}";
|
|
};
|
|
|
|
address = mkOption {
|
|
type = types.str;
|
|
description = lib.mdDoc "Address to listen on. IPv6 does not seem to be supported.";
|
|
default = "127.0.0.1";
|
|
example = "0.0.0.0";
|
|
};
|
|
|
|
port = mkOption {
|
|
type = types.port;
|
|
description = lib.mdDoc "The port to listen on";
|
|
default = 9898;
|
|
};
|
|
|
|
debug = mkOption {
|
|
type = types.bool;
|
|
description = lib.mdDoc "More verbose logging. Recommended during initial setup.";
|
|
default = false;
|
|
};
|
|
|
|
owner = mkOption {
|
|
type = types.nullOr types.str;
|
|
description = lib.mdDoc ''
|
|
Set owner MXID otherwise first talking local user will claim the bridge
|
|
'';
|
|
default = null;
|
|
example = "@admin:example.org";
|
|
};
|
|
|
|
namespaces = mkOption {
|
|
description = lib.mdDoc "Configure the 'namespaces' section of the registration.yml for the bridge and the server";
|
|
# TODO link to Matrix documentation of the format
|
|
type = types.submodule {
|
|
freeformType = jsonType;
|
|
};
|
|
|
|
default = {
|
|
users = [
|
|
{
|
|
regex = "@irc_.*";
|
|
exclusive = true;
|
|
}
|
|
];
|
|
aliases = [ ];
|
|
rooms = [ ];
|
|
};
|
|
};
|
|
|
|
identd.enable = mkEnableOption "identd service support";
|
|
identd.port = mkOption {
|
|
type = types.port;
|
|
description = lib.mdDoc "identd listen port";
|
|
default = 113;
|
|
};
|
|
|
|
extraArgs = mkOption {
|
|
type = types.listOf types.str;
|
|
description = lib.mdDoc "Heisenbridge is configured over the command line. Append extra arguments here";
|
|
default = [ ];
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
systemd.services.heisenbridge = {
|
|
description = "Matrix<->IRC bridge";
|
|
before = [ "matrix-synapse.service" ]; # So the registration file can be used by Synapse
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
preStart = ''
|
|
umask 077
|
|
set -e -u -o pipefail
|
|
|
|
if ! [ -f "${registrationFile}" ]; then
|
|
# Generate registration file if not present (actually, we only care about the tokens in it)
|
|
${bin} --generate --config ${registrationFile}
|
|
fi
|
|
|
|
# Overwrite the registration file with our generated one (the config may have changed since then),
|
|
# but keep the tokens. Two step procedure to be failure safe
|
|
${pkgs.yq}/bin/yq --slurp \
|
|
'.[0] + (.[1] | {as_token, hs_token})' \
|
|
${bridgeConfig} \
|
|
${registrationFile} \
|
|
> ${registrationFile}.new
|
|
mv -f ${registrationFile}.new ${registrationFile}
|
|
|
|
# Grant Synapse access to the registration
|
|
if ${getBin pkgs.glibc}/bin/getent group matrix-synapse > /dev/null; then
|
|
chgrp -v matrix-synapse ${registrationFile}
|
|
chmod -v g+r ${registrationFile}
|
|
fi
|
|
'';
|
|
|
|
serviceConfig = rec {
|
|
Type = "simple";
|
|
ExecStart = lib.concatStringsSep " " (
|
|
[
|
|
bin
|
|
(if cfg.debug then "-vvv" else "-v")
|
|
"--config"
|
|
registrationFile
|
|
"--listen-address"
|
|
(lib.escapeShellArg cfg.address)
|
|
"--listen-port"
|
|
(toString cfg.port)
|
|
]
|
|
++ (lib.optionals (cfg.owner != null) [
|
|
"--owner"
|
|
(lib.escapeShellArg cfg.owner)
|
|
])
|
|
++ (lib.optionals cfg.identd.enable [
|
|
"--identd"
|
|
"--identd-port"
|
|
(toString cfg.identd.port)
|
|
])
|
|
++ [
|
|
(lib.escapeShellArg cfg.homeserver)
|
|
]
|
|
++ (map (lib.escapeShellArg) cfg.extraArgs)
|
|
);
|
|
|
|
# Hardening options
|
|
|
|
User = "heisenbridge";
|
|
Group = "heisenbridge";
|
|
RuntimeDirectory = "heisenbridge";
|
|
RuntimeDirectoryMode = "0700";
|
|
StateDirectory = "heisenbridge";
|
|
StateDirectoryMode = "0755";
|
|
|
|
ProtectSystem = "strict";
|
|
ProtectHome = true;
|
|
PrivateTmp = true;
|
|
PrivateDevices = true;
|
|
ProtectKernelTunables = true;
|
|
ProtectControlGroups = true;
|
|
RestrictSUIDSGID = true;
|
|
PrivateMounts = true;
|
|
ProtectKernelModules = true;
|
|
ProtectKernelLogs = true;
|
|
ProtectHostname = true;
|
|
ProtectClock = true;
|
|
ProtectProc = "invisible";
|
|
ProcSubset = "pid";
|
|
RestrictNamespaces = true;
|
|
RemoveIPC = true;
|
|
UMask = "0077";
|
|
|
|
CapabilityBoundingSet = [ "CAP_CHOWN" ] ++ optional (cfg.port < 1024 || (cfg.identd.enable && cfg.identd.port < 1024)) "CAP_NET_BIND_SERVICE";
|
|
AmbientCapabilities = CapabilityBoundingSet;
|
|
NoNewPrivileges = true;
|
|
LockPersonality = true;
|
|
RestrictRealtime = true;
|
|
SystemCallFilter = ["@system-service" "~@privileged" "@chown"];
|
|
SystemCallArchitectures = "native";
|
|
RestrictAddressFamilies = "AF_INET AF_INET6";
|
|
};
|
|
};
|
|
|
|
users.groups.heisenbridge = {};
|
|
users.users.heisenbridge = {
|
|
description = "Service user for the Heisenbridge";
|
|
group = "heisenbridge";
|
|
isSystemUser = true;
|
|
};
|
|
};
|
|
|
|
meta.maintainers = [ lib.maintainers.piegames ];
|
|
}
|