Self-host your own digital island
1{ config, pkgs, lib, ... }: 2 3let cfg = config.eilean; in 4{ 5 options.eilean.matrix = { 6 enable = lib.mkEnableOption "matrix"; 7 turn = lib.mkOption { 8 type = lib.types.bool; 9 default = true; 10 }; 11 }; 12 13 config = lib.mkIf cfg.matrix.enable { 14 services.postgresql.enable = true; 15 services.postgresql.package = pkgs.postgresql_13; 16 services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" '' 17 CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse'; 18 CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse" 19 TEMPLATE template0 20 LC_COLLATE = "C" 21 LC_CTYPE = "C"; 22 ''; 23 24 services.nginx = { 25 enable = true; 26 # only recommendedProxySettings and recommendedGzipSettings are strictly required, 27 # but the rest make sense as well 28 recommendedTlsSettings = true; 29 recommendedOptimisation = true; 30 recommendedGzipSettings = true; 31 recommendedProxySettings = true; 32 33 virtualHosts = { 34 # This host section can be placed on a different host than the rest, 35 # i.e. to delegate from the host being accessible as ${config.networking.domain} 36 # to another host actually running the Matrix homeserver. 37 "${config.networking.domain}" = { 38 enableACME = true; 39 forceSSL = true; 40 41 locations."= /.well-known/matrix/server".extraConfig = 42 let 43 # use 443 instead of the default 8448 port to unite 44 # the client-server and server-server port for simplicity 45 server = { "m.server" = "matrix.${config.networking.domain}:443"; }; 46 in '' 47 default_type application/json; 48 return 200 '${builtins.toJSON server}'; 49 ''; 50 locations."= /.well-known/matrix/client".extraConfig = 51 let 52 client = { 53 "m.homeserver" = { "base_url" = "https://matrix.${config.networking.domain}"; }; 54 "m.identity_server" = { "base_url" = "https://vector.im"; }; 55 }; 56 # ACAO required to allow element-web on any URL to request this json file 57 # set other headers due to https://github.com/yandex/gixy/blob/master/docs/en/plugins/addheaderredefinition.md 58 in '' 59 default_type application/json; 60 add_header Access-Control-Allow-Origin *; 61 add_header Strict-Transport-Security max-age=31536000 always; 62 add_header X-Frame-Options SAMEORIGIN always; 63 add_header X-Content-Type-Options nosniff always; 64 add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; frame-src 'self'; frame-ancestors 'self'; form-action 'self';" always; 65 add_header Referrer-Policy 'same-origin'; 66 return 200 '${builtins.toJSON client}'; 67 ''; 68 }; 69 70 # Reverse proxy for Matrix client-server and server-server communication 71 "matrix.${config.networking.domain}" = { 72 enableACME = true; 73 forceSSL = true; 74 75 # Or do a redirect instead of the 404, or whatever is appropriate for you. 76 # But do not put a Matrix Web client here! See the Element web section below. 77 locations."/".extraConfig = '' 78 return 404; 79 ''; 80 81 # forward all Matrix API calls to the synapse Matrix homeserver 82 locations."/_matrix" = { 83 proxyPass = "http://127.0.0.1:8008"; # without a trailing / 84 #proxyPassReverse = "http://127.0.0.1:8008"; # without a trailing / 85 }; 86 }; 87 }; 88 }; 89 90 services.matrix-synapse = { 91 enable = true; 92 settings = lib.mkMerge [ 93 { 94 server_name = config.networking.domain; 95 enable_registration = true; 96 registration_requires_token = true; 97 registration_shared_secret_path = "${config.eilean.secretsDir}/matrix-shared-secret"; 98 listeners = [ 99 { 100 port = 8008; 101 bind_addresses = [ "::1" "127.0.0.1" ]; 102 type = "http"; 103 tls = false; 104 x_forwarded = true; 105 resources = [ 106 { 107 names = [ "client" "federation" ]; 108 compress = false; 109 } 110 ]; 111 } 112 ]; 113 max_upload_size = "100M"; 114 } 115 (lib.mkIf cfg.matrix.turn { 116 turn_uris = with config.services.coturn; [ 117 "turn:${realm}:3478?transport=udp" 118 "turn:${realm}:3478?transport=tcp" 119 "turns:${realm}:5349?transport=udp" 120 "turns:${realm}:5349?transport=tcp" 121 ]; 122 turn_user_lifetime = "1h"; 123 }) 124 ]; 125 extraConfigFiles = [ "${config.eilean.secretsDir}/matrix-turn-shared-secret" ]; 126 }; 127 128 eilean.turn.enable = lib.mkIf cfg.matrix.turn true; 129 130 eilean.dns.enable = true; 131 eilean.services.dns.zones.${config.networking.domain}.records = [ 132 { 133 name = "matrix"; 134 type = "CNAME"; 135 data = "vps"; 136 } 137 ]; 138 }; 139}