diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix index 39ed914994c1..6a3baf98a004 100644 --- a/nixos/modules/misc/ids.nix +++ b/nixos/modules/misc/ids.nix @@ -245,6 +245,7 @@ opendkim = 221; dspam = 222; gale = 223; + matrix-synapse = 224; # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399! @@ -467,6 +468,7 @@ opendkim = 221; dspam = 222; gale = 223; + matrix-synapse = 224; # When adding a gid, make sure it doesn't match an existing # uid. Users and groups with the same name should have equal diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index d9e8c2da5b32..4f125b09afbf 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -214,6 +214,7 @@ ./services/misc/gpsd.nix ./services/misc/ihaskell.nix ./services/misc/mathics.nix + ./services/misc/matrix-synapse.nix ./services/misc/mbpfan.nix ./services/misc/mediatomb.nix ./services/misc/mesos-master.nix diff --git a/nixos/modules/services/misc/matrix-synapse-log_config.yaml b/nixos/modules/services/misc/matrix-synapse-log_config.yaml new file mode 100644 index 000000000000..d85bdd1208f9 --- /dev/null +++ b/nixos/modules/services/misc/matrix-synapse-log_config.yaml @@ -0,0 +1,25 @@ +version: 1 + +# In systemd's journal, loglevel is implicitly stored, so let's omit it +# from the message text. +formatters: + journal_fmt: + format: '%(name)s: [%(request)s] %(message)s' + +filters: + context: + (): synapse.util.logcontext.LoggingContextFilter + request: "" + +handlers: + journal: + class: systemd.journal.JournalHandler + formatter: journal_fmt + filters: [context] + SYSLOG_IDENTIFIER: synapse + +root: + level: INFO + handlers: [journal] + +disable_existing_loggers: False diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix new file mode 100644 index 000000000000..27c5a38e6b88 --- /dev/null +++ b/nixos/modules/services/misc/matrix-synapse.nix @@ -0,0 +1,279 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.matrix-synapse; + logConfigFile = pkgs.writeText "log_config.yaml" cfg.logConfig; + configFile = pkgs.writeText "homeserver.yaml" '' +tls_certificate_path: "${cfg.tls_certificate_path}" +tls_private_key_path: "${cfg.tls_private_key_path}" +tls_dh_params_path: "${cfg.tls_dh_params_path}" +no_tls: ${if cfg.no_tls then "true" else "false"} +bind_port: ${toString cfg.bind_port} +unsecure_port: ${toString cfg.unsecure_port} +bind_host: "${cfg.bind_host}" +server_name: "${cfg.server_name}" +pid_file: "/var/run/matrix-synapse.pid" +web_client: ${if cfg.web_client then "true" else "false"} +database: { + name: "${cfg.database_type}", + args: { + ${concatStringsSep ",\n " ( + mapAttrsToList (n: v: "\"${n}\": ${v}") cfg.database_args + )} + } +} +log_file: "/var/log/matrix-synapse/homeserver.log" +log_config: "${logConfigFile}" +media_store_path: "/var/lib/matrix-synapse/media" +recaptcha_private_key: "${cfg.recaptcha_private_key}" +recaptcha_public_key: "${cfg.recaptcha_public_key}" +enable_registration_captcha: ${if cfg.enable_registration_captcha then "true" else "false"} +turn_uris: ${if (length cfg.turn_uris) == 0 then "[]" else ("\n" + (concatStringsSep "\n" (map (s: "- " + s) cfg.turn_uris)))} +turn_shared_secret: "${cfg.turn_shared_secret}" +enable_registration: ${if cfg.enable_registration then "true" else "false"} +${optionalString (cfg.registration_shared_secret != "") '' +registration_shared_secret: "${cfg.registration_shared_secret}" +''} +enable_metrics: ${if cfg.enable_metrics then "true" else "false"} +report_stats: ${if cfg.report_stats then "true" else "false"} +signing_key_path: "/var/lib/matrix-synapse/homeserver.signing.key" +perspectives: + servers: { + ${concatStringsSep "},\n" (mapAttrsToList (n: v: '' + "${n}": { + "verify_keys": { + ${concatStringsSep "},\n" (mapAttrsToList (n: v: '' + "${n}": { + "key": "${v}" + }'') v)} + } + '') cfg.servers)} + } + } +${cfg.extraConfig} +''; +in { + options = { + services.matrix-synapse = { + enable = mkEnableOption "matrix.org synapse"; + package = mkOption { + type = types.package; + default = pkgs.matrix-synapse; + description = '' + Overridable attribute of the matrix synapse server package to use. + ''; + }; + no_tls = mkOption { + type = types.bool; + default = false; + description = '' + Don't bind to the https port + ''; + }; + tls_certificate_path = mkOption { + type = types.path; + default = "/var/lib/matrix-synapse/homeserver.tls.crt"; + description = '' + PEM encoded X509 certificate for TLS + ''; + }; + tls_private_key_path = mkOption { + type = types.path; + default = "/var/lib/matrix-synapse/homeserver.tls.key"; + description = '' + PEM encoded private key for TLS + ''; + }; + tls_dh_params_path = mkOption { + type = types.path; + default = "/var/lib/matrix-synapse/homeserver.tls.dh"; + description = '' + PEM dh parameters for ephemeral keys + ''; + }; + bind_port = mkOption { + type = types.int; + default = 8448; + description = '' + The port to listen for HTTPS requests on. + For when matrix traffic is sent directly to synapse. + ''; + }; + unsecure_port = mkOption { + type = types.int; + default = 8008; + description = '' + The port to listen for HTTP requests on. + For when matrix traffic passes through loadbalancer that unwraps TLS. + ''; + }; + bind_host = mkOption { + type = types.str; + default = ""; + description = '' + Local interface to listen on. + The empty string will cause synapse to listen on all interfaces. + ''; + }; + server_name = mkOption { + type = types.str; + description = '' + The domain name of the server, with optional explicit port. + This is used by remote servers to connect to this server, + e.g. matrix.org, localhost:8080, etc. + This is also the last part of your UserID. + ''; + }; + web_client = mkOption { + type = types.bool; + default = false; + description = '' + Whether to serve a web client from the HTTP/HTTPS root resource. + ''; + }; + database_type = mkOption { + type = types.enum [ "sqlite3" "psycopg2" ]; + default = "sqlite3"; + description = '' + The database engine name. Can be sqlite or psycopg2. + ''; + }; + database_args = mkOption { + type = types.attrs; + default = { + database = "/var/lib/matrix-synapse/homeserver.db"; + }; + description = '' + Arguments to pass to the engine. + ''; + }; + recaptcha_private_key = mkOption { + type = types.str; + default = ""; + description = '' + This Home Server's ReCAPTCHA private key. + ''; + }; + recaptcha_public_key = mkOption { + type = types.str; + default = ""; + description = '' + This Home Server's ReCAPTCHA public key. + ''; + }; + enable_registration_captcha = mkOption { + type = types.bool; + default = false; + description = '' + Enables ReCaptcha checks when registering, preventing signup + unless a captcha is answered. Requires a valid ReCaptcha + public/private key. + ''; + }; + turn_uris = mkOption { + type = types.listOf types.str; + default = []; + description = '' + The public URIs of the TURN server to give to clients + ''; + }; + turn_shared_secret = mkOption { + type = types.str; + default = ""; + description = '' + The shared secret used to compute passwords for the TURN server + ''; + }; + enable_registration = mkOption { + type = types.bool; + default = false; + description = '' + Enable registration for new users. + ''; + }; + registration_shared_secret = mkOption { + type = types.str; + default = ""; + description = '' + If set, allows registration by anyone who also has the shared + secret, even if registration is otherwise disabled. + ''; + }; + enable_metrics = mkOption { + type = types.bool; + default = false; + description = '' + Enable collection and rendering of performance metrics + ''; + }; + report_stats = mkOption { + type = types.bool; + default = false; + description = '' + ''; + }; + servers = mkOption { + type = types.attrs; + default = { + "matrix.org" = { + "ed25519:auto" = "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw"; + }; + }; + description = '' + The trusted servers to download signing keys from. + ''; + }; + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra config options for matrix-synapse. + ''; + }; + logConfig = mkOption { + type = types.lines; + default = readFile ./matrix-synapse-log_config.yaml; + description = '' + A yaml python logging config file + ''; + }; + }; + }; + + config = mkIf cfg.enable { + users.extraUsers = [ + { name = "matrix-synapse"; + group = "matrix-synapse"; + home = "/var/lib/matrix-synapse"; + createHome = true; + shell = "${pkgs.bash}/bin/bash"; + uid = config.ids.uids.matrix-synapse; + } ]; + + users.extraGroups = [ + { name = "matrix-synapse"; + gid = config.ids.gids.matrix-synapse; + } ]; + + systemd.services.matrix-synapse = { + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + preStart = '' + mkdir -p /var/lib/matrix-synapse + chmod 700 /var/lib/matrix-synapse + chown -R matrix-synapse:matrix-synapse /var/lib/matrix-synapse + ${cfg.package}/bin/homeserver --config-path ${configFile} --generate-keys + ''; + serviceConfig = { + Type = "simple"; + User = "matrix-synapse"; + Group = "matrix-synapse"; + WorkingDirectory = "/var/lib/matrix-synapse"; + PermissionsStartOnly = true; + ExecStart = "${cfg.package}/bin/homeserver --config-path ${configFile}"; + }; + }; + }; +} diff --git a/pkgs/servers/matrix-synapse/default.nix b/pkgs/servers/matrix-synapse/default.nix new file mode 100644 index 000000000000..66d9e7258f4c --- /dev/null +++ b/pkgs/servers/matrix-synapse/default.nix @@ -0,0 +1,45 @@ +{ pkgs, stdenv, buildPythonPackage, pythonPackages, fetchurl, fetchFromGitHub }: +let + matrix-angular-sdk = buildPythonPackage rec { + name = "matrix-angular-sdk-${version}"; + version = "0.6.6"; + + src = fetchurl { + url = "https://pypi.python.org/packages/source/m/matrix-angular-sdk/matrix-angular-sdk-${version}.tar.gz"; + sha256 = "1vknhmibb8gh8lng50va2cdvng5xm7vqv9dl680m3gj38pg0bv8a"; + }; + }; +in +buildPythonPackage rec { + name = "matrix-synapse-${version}"; + version = "0.12.0"; + + src = fetchFromGitHub { + owner = "matrix-org"; + repo = "synapse"; + rev = "f35f8d06ea58e2d0cdccd82924c7a44fd93f4c38"; + sha256 = "0b0k1am9lh0qglagc06m91qs26ybv37k7wpbg5333x8jaf5d1si4"; + }; + + patches = [ ./matrix-synapse.patch ]; + + propagatedBuildInputs = with pythonPackages; [ + blist canonicaljson daemonize dateutil frozendict pillow pybcrypt pyasn1 + pydenticon pymacaroons-pynacl pynacl pyopenssl pysaml2 pytz requests2 + service-identity signedjson systemd twisted15 ujson unpaddedbase64 pyyaml + matrix-angular-sdk + ]; + + # Checks fail because of Tox. + doCheck = false; + + buildInputs = with pythonPackages; [ + mock setuptoolsTrial + ]; + + meta = { + homepage = https://matrix.org; + description = "Matrix reference homeserver"; + license = stdenv.lib.licenses.asl20; + }; +} diff --git a/pkgs/servers/matrix-synapse/matrix-synapse.patch b/pkgs/servers/matrix-synapse/matrix-synapse.patch new file mode 100644 index 000000000000..a6a393ea56c3 --- /dev/null +++ b/pkgs/servers/matrix-synapse/matrix-synapse.patch @@ -0,0 +1,20 @@ +diff --git a/homeserver b/homeserver +new file mode 120000 +index 0000000..2f1d413 +--- /dev/null ++++ b/homeserver +@@ -0,0 +1 @@ ++synapse/app/homeserver.py +\ No newline at end of file +diff --git a/setup.py b/setup.py +index 9d24761..f3e6a00 100755 +--- a/setup.py ++++ b/setup.py +@@ -85,6 +85,6 @@ setup( + include_package_data=True, + zip_safe=False, + long_description=long_description, +- scripts=["synctl"] + glob.glob("scripts/*"), ++ scripts=["synctl", "homeserver"] + glob.glob("scripts/*"), + cmdclass={'test': Tox}, + ) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 82708cd30e52..761bbed35184 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -2076,6 +2076,8 @@ let makebootfat = callPackage ../tools/misc/makebootfat { }; + matrix-synapse = callPackage ../servers/matrix-synapse { }; + memtester = callPackage ../tools/system/memtester { }; minidlna = callPackage ../tools/networking/minidlna { }; diff --git a/pkgs/top-level/python-packages.nix b/pkgs/top-level/python-packages.nix index ec17d9434a07..22324178843e 100644 --- a/pkgs/top-level/python-packages.nix +++ b/pkgs/top-level/python-packages.nix @@ -17823,7 +17823,7 @@ in modules // { md5 = "f16f4237c9ee483a0cd13208849d96ad"; }; - propagatedBuildInputs = with self; [ twisted ]; + propagatedBuildInputs = with self; [ twisted15 ]; meta = { description = "setuptools plug-in that helps run unit tests built with the \"Trial\" framework (from Twisted)"; @@ -17852,11 +17852,11 @@ in modules // { simplejson = buildPythonPackage (rec { - name = "simplejson-3.3.0"; + name = "simplejson-3.8.1"; src = pkgs.fetchurl { url = "http://pypi.python.org/packages/source/s/simplejson/${name}.tar.gz"; - md5 = "0e29b393bceac8081fa4e93ff9f6a001"; + sha256 = "14r4l4rcsyf87p2j4ycsbb017n4vzxfmv285rq2gny4w47rwi2j2"; }; meta = { @@ -19758,6 +19758,35 @@ in modules // { }; }; + twisted15 = buildPythonPackage rec { + disabled = isPy3k; + + name = "Twisted-15.5.0"; + src = pkgs.fetchurl { + url = "https://pypi.python.org/packages/source/T/Twisted/${name}.tar.bz2"; + sha256 = "0zy18lcrris4aaslil5k12i13k56c32hzfdv6h10kbnzl026h158"; + }; + + propagatedBuildInputs = with self; [ zope_interface ]; + + # Generate Twisted's plug-in cache. Twited users must do it as well. See + # http://twistedmatrix.com/documents/current/core/howto/plugin.html#auto3 + # and http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=477103 for + # details. + postInstall = "$out/bin/twistd --help > /dev/null"; + + meta = { + homepage = http://twistedmatrix.com/; + description = "Twisted, an event-driven networking engine written in Python"; + longDescription = '' + Twisted is an event-driven networking engine written in Python + and licensed under the MIT license. + ''; + license = licenses.mit; + maintainers = [ ]; + }; + }; + tzlocal = buildPythonPackage rec { name = "tzlocal-1.1.1"; @@ -22583,6 +22612,133 @@ in modules // { }; }; + blist = buildPythonPackage rec { + name = "blist-${version}"; + version = "1.3.6"; + disabled = isPyPy; + + src = pkgs.fetchurl { + url = "https://pypi.python.org/packages/source/b/blist/blist-${version}.tar.gz"; + sha256 = "1hqz9pqbwx0czvq9bjdqjqh5bwfksva1is0anfazig81n18c84is"; + }; + }; + + canonicaljson = buildPythonPackage rec { + name = "canonicaljson-${version}"; + version = "1.0.0"; + + src = pkgs.fetchgit { + url = "https://github.com/matrix-org/python-canonicaljson.git"; + rev = "refs/tags/v${version}"; + sha256 = "29802d0effacd26ca1d6eccc8d4c7e4f543a194754ba89263861e87f44a83f0c"; + }; + + propagatedBuildInputs = with self; [ + frozendict simplejson + ]; + }; + + daemonize = buildPythonPackage rec { + name = "daemonize-${version}"; + version = "2.4.2"; + + src = pkgs.fetchurl { + url = "https://pypi.python.org/packages/source/d/daemonize/daemonize-${version}.tar.gz"; + sha256 = "0y139sq657bpzfv6k0aqm4071z4s40i6ybpni9qvngvdcz6r86n2"; + }; + }; + + frozendict = buildPythonPackage rec { + name = "frozendict-${version}"; + version = "0.5"; + + src = pkgs.fetchurl { + url = "https://pypi.python.org/packages/source/f/frozendict/frozendict-0.5.tar.gz"; + sha256 = "0m4kg6hbadvf99if78nx01q7qnbyhdw3x4znl5dasgciyi54432n"; + }; + }; + + pydenticon = buildPythonPackage rec { + name = "pydenticon-${version}"; + version = "0.2"; + + src = pkgs.fetchurl { + url = "https://pypi.python.org/packages/source/p/pydenticon/pydenticon-0.2.tar.gz"; + sha256 = "035dawcspgjw2rksbnn863s7b0i9ac8cc1nshshvd1l837ir1czp"; + }; + propagatedBuildInputs = with self; [ + pillow mock + ]; + }; + + pymacaroons-pynacl = buildPythonPackage rec { + name = "pymacaroons-pynacl-${version}"; + version = "0.9.3"; + + src = pkgs.fetchgit { + url = "https://github.com/matrix-org/pymacaroons.git"; + rev = "refs/tags/v${version}"; + sha256 = "481a486520f5a3ad2761c3cd3954d2b08f456a94fb080aaa4ad1e68ddc705b52"; + }; + + propagatedBuildInputs = with self; [ pynacl six ]; + }; + + pynacl = buildPythonPackage rec { + name = "pynacl-${version}"; + version = "0.3.0"; + + src = pkgs.fetchurl { + url = "https://pypi.python.org/packages/source/P/PyNaCl/PyNaCl-0.3.0.tar.gz"; + sha256 = "1hknxlp3a3f8njn19w92p8nhzl9jkfwzhv5fmxhmyq2m8hqrfj8j"; + }; + + propagatedBuildInputs = with self; [pkgs.libsodium six cffi pycparser pytest]; + }; + + service-identity = buildPythonPackage rec { + name = "service-identity-${version}"; + version = "14.0.0"; + + src = pkgs.fetchurl { + url = "https://pypi.python.org/packages/source/s/service_identity/service_identity-${version}.tar.gz"; + sha256 = "0njg9bklkkp4rl2b9vsfh9aasxy3w2dmjkv9cq34jn65lwcs619i"; + }; + + propagatedBuildInputs = with self; [ + characteristic pyasn1 pyasn1-modules pyopenssl idna + ]; + + buildInputs = with self; [ + pytest + ]; + }; + + signedjson = buildPythonPackage rec { + name = "signedjson-${version}"; + version = "1.0.0"; + + src = pkgs.fetchgit { + url = "https://github.com/matrix-org/python-signedjson.git"; + rev = "refs/tags/v${version}"; + sha256 = "4ef1c89ea85846632d711a37a2e6aae1348c62b9d62ed0e80428b4a00642e9df"; + }; + + propagatedBuildInputs = with self; [ + canonicaljson unpaddedbase64 pynacl + ]; + }; + + unpaddedbase64 = buildPythonPackage rec { + name = "unpaddedbase64-${version}"; + version = "1.0.1"; + + src = pkgs.fetchgit { + url = "https://github.com/matrix-org/python-unpaddedbase64.git"; + rev = "refs/tags/v${version}"; + sha256 = "f221240a6d414c4244ab906b1dc8983c4d1114acb778cb857f6fc50d710be502"; + }; + }; thumbor = buildPythonPackage rec {