blob: ea7bff2b24e971c4ea8d3a9f2961b0fff022eef7 [file] [log] [blame]
Piotr Dobrowolskib67ae482021-01-31 10:35:38 +01001local kube = import "../../../kube/kube.libsonnet";
2
3{
4 local app = self,
5 local cfg = app.cfg,
6 cfg:: {
7 image: error "cfg.image needs to be set",
8 storageClassName: error "cfg.storrageClassName needs to be set",
9
10 # webDomain is the domain name at which synapse instance will run
11 webDomain: error "cfg.webDomain must be set",
12 # serverName is the server part of the MXID this homeserver will cover
13 serverName: error "cfg.serverName must be set",
14
15 cas: { enable: false },
16 oidc: { enable: false },
17
18 macaroonSecretKey: error "cfg.macaroonSecretKey needs to be set",
19 registrationSharedSecret: error "cfg.registationSharedSecret needs to be set",
20 workerReplicationSecret: error "cfg.workerReplicationSecret needs to be set",
21 },
22
23 ns:: error "ns needs to be provided",
24 postgres:: error "postgres needs to be provided",
25 redis:: error "redis needs to be provided",
26
27 // See matrix-ng.libsonnet for description
28 appservices:: error "appservices need to be provided",
29
30 dataVolume: app.ns.Contain(kube.PersistentVolumeClaim("synapse-data-waw3")) {
31 spec+: {
32 storageClassName: cfg.storageClassName,
33 accessModes: [ "ReadWriteOnce" ],
34 resources: {
35 requests: {
36 storage: "50Gi",
37 },
38 },
39 },
40 },
41
42 // homeserver.yaml that will be used to run synapse (in configMap).
43 // This is based off of //app/matrix/lib/synapse/homeserver.yaml with some fields overriden per
44 // deployment.
45 config:: (std.native("parseYaml"))(importstr "synapse/homeserver-ng.yaml")[0] {
46 server_name: cfg.serverName,
47 public_baseurl: "https://%s" % [cfg.webDomain],
48 signing_key_path: "/secrets/homeserver_signing_key",
49 app_service_config_files: [
50 "/appservices/%s/registration.yaml" % [k]
51 for k in std.objectFields(app.appservices)
52 ],
53 } + (if cfg.cas.enable then {
54 cas_config: {
55 enabled: true,
56 server_url: "https://%s/_cas" % [cfg.webDomain],
57 service_url: "https://%s" % [cfg.webDomain],
58 },
59 } else {}),
60
61 configMap: app.ns.Contain(kube.ConfigMap("synapse")) {
62 data: {
63 "homeserver.yaml": std.manifestYamlDoc(app.config),
64 "log.config": importstr "synapse/log.config",
65 },
66 },
67
68 // homeserver-secrets.yaml contains all the templated secret variables from
69 // base homeserver.yaml passed as yaml-encoded environment variable.
70 // $(ENVVAR)-encoded variables are resolved by Kubernetes on pod startup
71 secretsConfig:: (std.native("parseYaml"))(importstr "synapse/homeserver-secrets.yaml")[0] {
72 } + (if cfg.oidc.enable then {
73 oidc_config: cfg.oidc.config {
74 enabled: true,
75 client_secret: "$(OIDC_CLIENT_SECRET)",
76 },
77 } else {}),
78
79 # Synapse process Deployment/StatefulSet base resource.
80 SynapseWorker(name, workerType, builder):: app.ns.Contain(builder(name)) {
81 local worker = self,
82 cfg:: {
83 # Configuration customization. Can contain environment substitution
84 # syntax, as used in worker_name value.
85 localConfig: {
86 worker_app: workerType,
87 worker_name: "$(POD_NAME)",
88
89 # The replication listener on the main synapse process.
90 worker_replication_host: "synapse-replication-master",
91 worker_replication_http_port: 9093,
92 },
93
94 # Mount app.dataVolume in /data
95 mountData: false,
96 },
97
98 spec+: {
99 replicas: 1,
100 template+: {
101 spec+: {
102 volumes_: {
103 config: kube.ConfigMapVolume(app.configMap),
104 secrets: { secret: { secretName: "synapse" } },
105 } + {
106 [k]: { secret: { secretName: "appservice-%s-registration" % [k] } }
107 for k in std.objectFields(app.appservices)
108 } + if worker.cfg.mountData then {
109 data: kube.PersistentVolumeClaimVolume(app.dataVolume),
110 } else {},
111 containers_: {
112 web: kube.Container("synapse") {
113 image: cfg.image,
114 command: [
115 "/bin/sh", "-c", |||
116 set -e
117 echo "${X_SECRETS_CONFIG}" > /tmp/secrets.yaml
118 echo "${X_LOCAL_CONFIG}" > /tmp/local.yaml
119 exec python -m ${SYNAPSE_WORKER} --config-path /conf/homeserver.yaml --config-path /tmp/secrets.yaml --config-path /tmp/local.yaml
120 |||
121 ],
122 ports_: {
123 http: { containerPort: 8008 },
124 metrics: { containerPort: 9092 },
125 replication: { containerPort: 9093 },
126 },
127 env_: {
128 SYNAPSE_WORKER: workerType,
129
130 SYNAPSE_MACAROON_SECRET_KEY: cfg.macaroonSecretKey,
131 SYNAPSE_REGISTRATION_SHARED_SECRET: cfg.registrationSharedSecret,
132 WORKER_REPLICATION_SECRET: cfg.workerReplicationSecret,
133 POSTGRES_PASSWORD: app.postgres.cfg.password,
134 REDIS_PASSWORD: app.redis.cfg.password,
135 POD_NAME: { fieldRef: { fieldPath: "metadata.name" } },
136 OIDC_CLIENT_SECRET: if cfg.oidc.enable then cfg.oidc.config.client_secret else "",
137
138 X_SECRETS_CONFIG: std.manifestYamlDoc(app.secretsConfig),
139 X_LOCAL_CONFIG: std.manifestYamlDoc(worker.cfg.localConfig),
140 },
141 volumeMounts_: {
142 config: { mountPath: "/conf", },
143 secrets: { mountPath: "/secrets" },
144 } + {
145 [k]: { mountPath: "/appservices/%s" % [k] }
146 for k in std.objectFields(app.appservices)
147 } + if worker.cfg.mountData then {
148 data: { mountPath: "/data" },
149 } else {},
150 },
151 },
152 securityContext: {
153 runAsUser: 991,
154 runAsGroup: 991,
155 fsGroup: 991,
156 },
157 },
158 },
159 },
160 },
161
162 # Synapse main process
163 main: {
164 deployment: app.SynapseWorker("synapse", "synapse.app.homeserver", kube.Deployment) {
165 cfg+: {
166 # Main process doesn't need any configuration customization
167 localConfig: {}
168 },
169 },
170 svc: app.ns.Contain(kube.Service("synapse")) {
171 target_pod:: app.main.deployment.spec.template,
172 },
173 replicationSvc: app.ns.Contain(kube.Service("synapse-replication-master")) {
174 target_pod:: app.main.deployment.spec.template,
175 spec+: {
176 ports: [
177 { port: 9093, name: 'replication', targetPort: 9093 },
178 ],
179 },
180 },
181 },
182
183 genericWorker: {
184 # Synapse generic worker deployment
185 deployment: app.SynapseWorker("synapse-generic", "synapse.app.generic_worker", kube.StatefulSet) {
186 cfg+: {
187 localConfig+: {
188 worker_listeners: [{
189 type: "http",
190 port: 8008,
191 x_forwarded: true,
192 bind_addresses: ["::"],
193 resources: [{ names: ["client", "federation"]}],
194 }],
195 },
196 },
197 },
198 svc: app.ns.Contain(kube.Service("synapse-generic")) {
199 target_pod:: app.genericWorker.deployment.spec.template,
200 },
201
202 # Following paths can be handled by generic workers.
203 # See: https://github.com/matrix-org/synapse/blob/master/docs/workers.md
204 paths:: [
205 "/_matrix/client/(v2_alpha|r0)/sync",
206 "/_matrix/client/(api/v1|v2_alpha|r0)/events",
207 "/_matrix/client/(api/v1|r0)/initialSync",
208 "/_matrix/client/(api/v1|r0)/rooms/[^/]+/initialSync",
209 "/_matrix/client/(api/v1|r0|unstable)/publicRooms",
210 "/_matrix/client/(api/v1|r0|unstable)/rooms/.*/joined_members",
211 "/_matrix/client/(api/v1|r0|unstable)/rooms/.*/context/.*",
212 "/_matrix/client/(api/v1|r0|unstable)/rooms/.*/members",
213 "/_matrix/client/(api/v1|r0|unstable)/rooms/.*/state",
214 "/_matrix/client/(api/v1|r0|unstable)/account/3pid",
215 "/_matrix/client/(api/v1|r0|unstable)/keys/query",
216 "/_matrix/client/(api/v1|r0|unstable)/keys/changes",
217 "/_matrix/client/versions",
218 "/_matrix/client/(api/v1|r0|unstable)/voip/turnServer",
219 "/_matrix/client/(api/v1|r0|unstable)/joined_groups",
220 "/_matrix/client/(api/v1|r0|unstable)/publicised_groups",
221 "/_matrix/client/(api/v1|r0|unstable)/publicised_groups/",
222 # Blocked by https://github.com/matrix-org/synapse/issues/8966
223 # "/_matrix/client/(api/v1|r0|unstable)/login",
224 # "/_matrix/client/(r0|unstable)/register",
225 # "/_matrix/client/(r0|unstable)/auth/.*/fallback/web",
226 "/_matrix/client/(api/v1|r0|unstable)/rooms/.*/send",
227 "/_matrix/client/(api/v1|r0|unstable)/rooms/.*/state/",
228 "/_matrix/client/(api/v1|r0|unstable)/rooms/.*/(join|invite|leave|ban|unban|kick)",
229 "/_matrix/client/(api/v1|r0|unstable)/join/",
230 "/_matrix/client/(api/v1|r0|unstable)/profile/",
231 "/_matrix/federation/v1/event/",
232 "/_matrix/federation/v1/state/",
233 "/_matrix/federation/v1/state_ids/",
234 "/_matrix/federation/v1/backfill/",
235 "/_matrix/federation/v1/get_missing_events/",
236 "/_matrix/federation/v1/publicRooms",
237 "/_matrix/federation/v1/query/",
238 "/_matrix/federation/v1/make_join/",
239 "/_matrix/federation/v1/make_leave/",
240 "/_matrix/federation/v1/send_join/",
241 "/_matrix/federation/v2/send_join/",
242 "/_matrix/federation/v1/send_leave/",
243 "/_matrix/federation/v2/send_leave/",
244 "/_matrix/federation/v1/invite/",
245 "/_matrix/federation/v2/invite/",
246 "/_matrix/federation/v1/query_auth/",
247 "/_matrix/federation/v1/event_auth/",
248 "/_matrix/federation/v1/exchange_third_party_invite/",
249 "/_matrix/federation/v1/user/devices/",
250 "/_matrix/federation/v1/get_groups_publicised",
251 "/_matrix/key/v2/query",
252 "/_matrix/federation/v1/send/",
253 ],
254 },
255
256 # Synapse media worker. This handles access to uploads and media stored in app.dataVolume
257 mediaWorker: {
258 deployment: app.SynapseWorker("synapse-media", "synapse.app.media_repository", kube.StatefulSet) {
259 cfg+: {
260 mountData: true,
261 localConfig+: {
262 worker_listeners: [{
263 type: "http",
264 port: 8008,
265 x_forwarded: true,
266 bind_addresses: ["::"],
267 resources: [{ names: ["media"]}],
268 }],
269 },
270 },
271 },
272 svc: app.ns.Contain(kube.Service("synapse-media")) {
273 target_pod:: app.mediaWorker.deployment.spec.template,
274 },
275 },
276}