This repository has no description
5.2 kB
164 lines
1{ pkgs, lib, ... }:
2let
3 pkg = pkgs.matrix-zulip-bridge;
4 bin = lib.getExe pkg;
5
6 address = "127.0.0.1";
7 port = 28464;
8 homeserver = "http://[::1]:8008";
9 owner = "@lassulus:lassul.us";
10
11 registrationFile = "/var/lib/matrix-zulip-bridge/registration.yml";
12
13 # JSON is a proper subset of YAML. We render our desired registration and
14 # merge in the tokens from the bridge-generated file in preStart, mirroring
15 # the upstream heisenbridge module (this bridge shares its architecture).
16 bridgeConfig = builtins.toFile "matrix-zulip-bridge-registration.yml" (
17 builtins.toJSON {
18 id = "zulipbridge";
19 url = "http://${address}:${toString port}";
20 rate_limited = false;
21 sender_localpart = "zulipbridge";
22 namespaces = {
23 users = [
24 {
25 regex = "@zulip_.*:lassul\\.us";
26 exclusive = true;
27 }
28 {
29 regex = "@zulipbridge:lassul\\.us";
30 exclusive = true;
31 }
32 ];
33 aliases = [ ];
34 rooms = [ ];
35 };
36 }
37 );
38in
39{
40 # Upstream zulip-emoji-mapping (a bridge dependency) is a Poetry project
41 # (version lives in [tool.poetry]) with no [build-system] table, but nixpkgs
42 # builds it with setuptools. Setuptools can't read the poetry version, so the
43 # wheel metadata gets 0.0.0 while the derivation declares 1.0.1, tripping
44 # pythonMetadataCheckPhase. Build with poetry-core and add the missing
45 # [build-system] table so the version is picked up correctly.
46 nixpkgs.overlays = [
47 (_final: prev: {
48 pythonPackagesExtensions = prev.pythonPackagesExtensions ++ [
49 (pyfinal: pyprev: {
50 zulip-emoji-mapping = pyprev.zulip-emoji-mapping.overridePythonAttrs (old: {
51 build-system = [ pyfinal.poetry-core ];
52 postPatch = (old.postPatch or "") + ''
53 cat >> pyproject.toml <<'EOF'
54
55 [build-system]
56 requires = ["poetry-core"]
57 build-backend = "poetry.core.masonry.api"
58 EOF
59 '';
60 });
61 })
62 ];
63 })
64 ];
65
66 systemd.services.matrix-zulip-bridge = {
67 description = "Matrix<->Zulip bridge";
68 before = [ "matrix-synapse.service" ]; # So the registration file can be used by Synapse
69 wantedBy = [ "multi-user.target" ];
70
71 preStart = ''
72 umask 077
73 set -e -u -o pipefail
74
75 if ! [ -f "${registrationFile}" ]; then
76 # Generate registration file if not present (we only care about the tokens in it)
77 ${bin} --generate --config ${registrationFile}
78 fi
79
80 # Overwrite the registration file with our generated one (the config may have
81 # changed since then), but keep the tokens. Two step procedure to be failure safe.
82 ${pkgs.yq}/bin/yq --slurp \
83 '.[0] + (.[1] | {as_token, hs_token})' \
84 ${bridgeConfig} \
85 ${registrationFile} \
86 > ${registrationFile}.new
87 mv -f ${registrationFile}.new ${registrationFile}
88
89 # Grant Synapse access to the registration
90 if ${pkgs.getent}/bin/getent group matrix-synapse > /dev/null; then
91 chgrp -v matrix-synapse ${registrationFile}
92 chmod -v g+r ${registrationFile}
93 fi
94 '';
95
96 serviceConfig = {
97 Type = "simple";
98 ExecStart = lib.concatStringsSep " " [
99 bin
100 "-v"
101 "--config"
102 registrationFile
103 "--listen-address"
104 (lib.escapeShellArg address)
105 "--listen-port"
106 (toString port)
107 "--owner"
108 (lib.escapeShellArg owner)
109 (lib.escapeShellArg homeserver)
110 ];
111
112 # Hardening options (mirrors upstream heisenbridge module)
113 User = "matrix-zulip-bridge";
114 Group = "matrix-zulip-bridge";
115 RuntimeDirectory = "matrix-zulip-bridge";
116 RuntimeDirectoryMode = "0700";
117 StateDirectory = "matrix-zulip-bridge";
118 StateDirectoryMode = "0755";
119
120 ProtectSystem = "strict";
121 ProtectHome = true;
122 PrivateTmp = true;
123 PrivateDevices = true;
124 ProtectKernelTunables = true;
125 ProtectControlGroups = true;
126 RestrictSUIDSGID = true;
127 PrivateMounts = true;
128 ProtectKernelModules = true;
129 ProtectKernelLogs = true;
130 ProtectHostname = true;
131 ProtectClock = true;
132 ProtectProc = "invisible";
133 ProcSubset = "pid";
134 RestrictNamespaces = true;
135 RemoveIPC = true;
136 UMask = "0077";
137
138 CapabilityBoundingSet = [ "CAP_CHOWN" ];
139 AmbientCapabilities = [ "CAP_CHOWN" ];
140 NoNewPrivileges = true;
141 LockPersonality = true;
142 RestrictRealtime = true;
143 SystemCallFilter = [
144 "@system-service"
145 "~@privileged"
146 "@chown"
147 ];
148 SystemCallArchitectures = "native";
149 RestrictAddressFamilies = "AF_INET AF_INET6";
150 };
151 };
152
153 # Wire the registration into Synapse (merges with the other bridges' lists).
154 services.matrix-synapse.settings.app_service_config_files = [
155 registrationFile
156 ];
157
158 users.groups.matrix-zulip-bridge = { };
159 users.users.matrix-zulip-bridge = {
160 description = "Service user for the Matrix Zulip bridge";
161 group = "matrix-zulip-bridge";
162 isSystemUser = true;
163 };
164}