From: self Date: Sat, 22 Jul 2023 23:14:02 +0000 (-0700) Subject: fix federation between lemmy and other activitypub services X-Git-Url: http://these/git/%22https:/join-lemmy.org/static/gitweb.js?a=commitdiff_plain;h=c34cc3ee554e4d821d103fd6b1ce30621b3b08d8;p=awful.systems.git fix federation between lemmy and other activitypub services --- diff --git a/lemmy/dev/module.nix b/lemmy/dev/module.nix index 8ae458d..97887c0 100644 --- a/lemmy/dev/module.nix +++ b/lemmy/dev/module.nix @@ -154,12 +154,30 @@ in { }; }; - services.nginx = mkIf cfg.nginx.enable { + services.nginx = let + ui = "http://127.0.0.1:${toString cfg.ui.port}"; + backend = "http://127.0.0.1:${toString cfg.settings.port}"; + in mkIf cfg.nginx.enable { enable = mkDefault true; - virtualHosts."${cfg.settings.hostname}".locations = let - ui = "http://127.0.0.1:${toString cfg.ui.port}"; - backend = "http://127.0.0.1:${toString cfg.settings.port}"; - in { + appendHttpConfig = '' + map "$request_method:$http_accept" $proxpass { + # If no explicit matches exists below, send traffic to lemmy-ui + default "${ui}"; + + # GET/HEAD requests that accepts ActivityPub or Linked Data JSON should go to lemmy. + # + # These requests are used by Mastodon and other fediverse instances to look up profile information, + # discover site information and so on. + "~^(?:GET|HEAD):.*?application\/(?:activity|ld)\+json" "${backend}"; + + # All non-GET/HEAD requests should go to lemmy + # + # Rather than calling out POST, PUT, DELETE, PATCH, CONNECT and all the verbs manually + # we simply negate the GET|HEAD pattern from above and accept all possibly $http_accept values + "~^(?!(GET|HEAD)).*:" "${backend}"; + } + ''; + virtualHosts."${cfg.settings.hostname}".locations = { "~ ^/(api|pictrs|feeds|nodeinfo|.well-known)" = { # backend requests proxyPass = backend; @@ -171,17 +189,6 @@ in { proxyPass = "$proxpass"; recommendedProxySettings = true; extraConfig = '' - set $proxpass "${ui}"; - if ($http_accept = "application/activity+json") { - set $proxpass "${backend}"; - } - if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") { - set $proxpass "${backend}"; - } - if ($request_method = POST) { - set $proxpass "${backend}"; - } - # Cuts off the trailing slash on URLs to make them valid rewrite ^(.+)/+$ $1 permanent; ''; @@ -238,7 +245,8 @@ in { environment = { LEMMY_UI_HOST = "127.0.0.1:${toString cfg.ui.port}"; - LEMMY_UI_LEMMY_INTERNAL_HOST = "127.0.0.1:${toString cfg.settings.port}"; + LEMMY_UI_LEMMY_INTERNAL_HOST = + "127.0.0.1:${toString cfg.settings.port}"; LEMMY_UI_LEMMY_EXTERNAL_HOST = cfg.settings.hostname; LEMMY_UI_HTTPS = "false"; }; diff --git a/lemmy/prod/module.nix b/lemmy/prod/module.nix index 58cacb8..dc24bd7 100644 --- a/lemmy/prod/module.nix +++ b/lemmy/prod/module.nix @@ -3,40 +3,44 @@ with lib; let cfg = config.services.lemmy-prod; settingsFormat = pkgs.formats.json { }; -in -{ +in { imports = [ - (mkRemovedOptionModule [ "services" "lemmy-prod" "jwtSecretPath" ] "As of v0.13.0, Lemmy auto-generates the JWT secret.") + (mkRemovedOptionModule [ "services" "lemmy-prod" "jwtSecretPath" ] + "As of v0.13.0, Lemmy auto-generates the JWT secret.") ]; options.services.lemmy-prod = { - enable = mkEnableOption (lib.mdDoc "lemmy a federated alternative to reddit in rust"); + enable = mkEnableOption + (lib.mdDoc "lemmy a federated alternative to reddit in rust"); - server = { - package = mkPackageOptionMD pkgs "lemmy-server" {}; - }; + server = { package = mkPackageOptionMD pkgs "lemmy-server" { }; }; ui = { - package = mkPackageOptionMD pkgs "lemmy-ui" {}; + package = mkPackageOptionMD pkgs "lemmy-ui" { }; port = mkOption { type = types.port; default = 1234; - description = lib.mdDoc "Port where lemmy-ui should listen for incoming requests."; + description = + lib.mdDoc "Port where lemmy-ui should listen for incoming requests."; }; }; - caddy.enable = mkEnableOption (lib.mdDoc "exposing lemmy with the caddy reverse proxy"); - nginx.enable = mkEnableOption (lib.mdDoc "exposing lemmy with the nginx reverse proxy"); + caddy.enable = + mkEnableOption (lib.mdDoc "exposing lemmy with the caddy reverse proxy"); + nginx.enable = + mkEnableOption (lib.mdDoc "exposing lemmy with the nginx reverse proxy"); database = { - createLocally = mkEnableOption (lib.mdDoc "creation of database on the instance"); + createLocally = + mkEnableOption (lib.mdDoc "creation of database on the instance"); uri = mkOption { type = with types; nullOr str; default = null; - description = lib.mdDoc "The connection URI to use. Takes priority over the configuration file if set."; + description = lib.mdDoc + "The connection URI to use. Takes priority over the configuration file if set."; }; }; @@ -50,13 +54,15 @@ in options.hostname = mkOption { type = types.str; default = null; - description = lib.mdDoc "The domain name of your instance (eg 'lemmy.ml')."; + description = + lib.mdDoc "The domain name of your instance (eg 'lemmy.ml')."; }; options.port = mkOption { type = types.port; default = 8536; - description = lib.mdDoc "Port where lemmy should listen for incoming requests."; + description = + lib.mdDoc "Port where lemmy should listen for incoming requests."; }; options.captcha = { @@ -76,178 +82,193 @@ in }; - config = - lib.mkIf cfg.enable { - services.lemmy-prod.settings = (mapAttrs (name: mkDefault) - { - bind = "127.0.0.1"; - tls_enabled = true; - pictrs_url = with config.services.pict-rs; "http://${address}:${toString port}"; - actor_name_max_length = 20; - - rate_limit.message = 180; - rate_limit.message_per_second = 60; - rate_limit.post = 6; - rate_limit.post_per_second = 600; - rate_limit.register = 3; - rate_limit.register_per_second = 3600; - rate_limit.image = 6; - rate_limit.image_per_second = 3600; - } // { - database = mapAttrs (name: mkDefault) { - user = "lemmy"; - host = "/run/postgresql"; - port = 5432; - database = "lemmy"; - pool_size = 5; - }; - }); - - services.postgresql = mkIf cfg.database.createLocally { - enable = true; - ensureDatabases = [ cfg.settings.database.database ]; - ensureUsers = [{ - name = cfg.settings.database.user; - ensurePermissions."DATABASE ${cfg.settings.database.database}" = "ALL PRIVILEGES"; - }]; + config = lib.mkIf cfg.enable { + services.lemmy-prod.settings = (mapAttrs (name: mkDefault) { + bind = "127.0.0.1"; + tls_enabled = true; + pictrs_url = with config.services.pict-rs; + "http://${address}:${toString port}"; + actor_name_max_length = 20; + + rate_limit.message = 180; + rate_limit.message_per_second = 60; + rate_limit.post = 6; + rate_limit.post_per_second = 600; + rate_limit.register = 3; + rate_limit.register_per_second = 3600; + rate_limit.image = 6; + rate_limit.image_per_second = 3600; + } // { + database = mapAttrs (name: mkDefault) { + user = "lemmy"; + host = "/run/postgresql"; + port = 5432; + database = "lemmy"; + pool_size = 5; }; + }); + + services.postgresql = mkIf cfg.database.createLocally { + enable = true; + ensureDatabases = [ cfg.settings.database.database ]; + ensureUsers = [{ + name = cfg.settings.database.user; + ensurePermissions."DATABASE ${cfg.settings.database.database}" = + "ALL PRIVILEGES"; + }]; + }; - services.pict-rs.enable = true; + services.pict-rs.enable = true; + + services.caddy = mkIf cfg.caddy.enable { + enable = mkDefault true; + virtualHosts."${cfg.settings.hostname}" = { + extraConfig = '' + handle_path /static/* { + root * ${cfg.ui.package}/dist + file_server + } + @for_backend { + path /api/* /pictrs/* /feeds/* /nodeinfo/* + } + handle @for_backend { + reverse_proxy 127.0.0.1:${toString cfg.settings.port} + } + @post { + method POST + } + handle @post { + reverse_proxy 127.0.0.1:${toString cfg.settings.port} + } + @jsonld { + header Accept "application/activity+json" + header Accept "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" + } + handle @jsonld { + reverse_proxy 127.0.0.1:${toString cfg.settings.port} + } + handle { + reverse_proxy 127.0.0.1:${toString cfg.ui.port} + } + ''; + }; + }; - services.caddy = mkIf cfg.caddy.enable { - enable = mkDefault true; - virtualHosts."${cfg.settings.hostname}" = { + services.nginx = let + ui = "http://127.0.0.1:${toString cfg.ui.port}"; + backend = "http://127.0.0.1:${toString cfg.settings.port}"; + in mkIf cfg.nginx.enable { + enable = mkDefault true; + appendHttpConfig = '' + map "$request_method:$http_accept" $proxpass { + # If no explicit matches exists below, send traffic to lemmy-ui + default "${ui}"; + + # GET/HEAD requests that accepts ActivityPub or Linked Data JSON should go to lemmy. + # + # These requests are used by Mastodon and other fediverse instances to look up profile information, + # discover site information and so on. + "~^(?:GET|HEAD):.*?application\/(?:activity|ld)\+json" "${backend}"; + + # All non-GET/HEAD requests should go to lemmy + # + # Rather than calling out POST, PUT, DELETE, PATCH, CONNECT and all the verbs manually + # we simply negate the GET|HEAD pattern from above and accept all possibly $http_accept values + "~^(?!(GET|HEAD)).*:" "${backend}"; + } + ''; + virtualHosts."${cfg.settings.hostname}".locations = { + "~ ^/(api|pictrs|feeds|nodeinfo|.well-known)" = { + # backend requests + proxyPass = backend; + proxyWebsockets = true; + recommendedProxySettings = true; + }; + "/" = { + # mixed frontend and backend requests, based on the request headers + proxyPass = "$proxpass"; + recommendedProxySettings = true; extraConfig = '' - handle_path /static/* { - root * ${cfg.ui.package}/dist - file_server - } - @for_backend { - path /api/* /pictrs/* /feeds/* /nodeinfo/* - } - handle @for_backend { - reverse_proxy 127.0.0.1:${toString cfg.settings.port} - } - @post { - method POST - } - handle @post { - reverse_proxy 127.0.0.1:${toString cfg.settings.port} - } - @jsonld { - header Accept "application/activity+json" - header Accept "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - } - handle @jsonld { - reverse_proxy 127.0.0.1:${toString cfg.settings.port} - } - handle { - reverse_proxy 127.0.0.1:${toString cfg.ui.port} - } + # Cuts off the trailing slash on URLs to make them valid + rewrite ^(.+)/+$ $1 permanent; ''; }; }; + }; - services.nginx = mkIf cfg.nginx.enable { - enable = mkDefault true; - virtualHosts."${cfg.settings.hostname}".locations = let - ui = "http://127.0.0.1:${toString cfg.ui.port}"; - backend = "http://127.0.0.1:${toString cfg.settings.port}"; - in { - "~ ^/(api|pictrs|feeds|nodeinfo|.well-known)" = { - # backend requests - proxyPass = backend; - proxyWebsockets = true; - recommendedProxySettings = true; - }; - "/" = { - # mixed frontend and backend requests, based on the request headers - proxyPass = "$proxpass"; - recommendedProxySettings = true; - extraConfig = '' - set $proxpass "${ui}"; - if ($http_accept = "application/activity+json") { - set $proxpass "${backend}"; - } - if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") { - set $proxpass "${backend}"; - } - if ($request_method = POST) { - set $proxpass "${backend}"; - } - - # Cuts off the trailing slash on URLs to make them valid - rewrite ^(.+)/+$ $1 permanent; - ''; - }; - }; + assertions = [ + { + assertion = cfg.database.createLocally -> cfg.settings.database.host + == "localhost" || cfg.settings.database.host == "/run/postgresql"; + message = + "if you want to create the database locally, you need to use a local database"; + } + { + assertion = (!(hasAttrByPath [ "federation" ] cfg.settings)) + && (!(hasAttrByPath [ "federation" "enabled" ] cfg.settings)); + message = + "`services.lemmy.settings.federation` was removed in 0.17.0 and no longer has any effect"; + } + ]; + + systemd.services.lemmy-prod = { + description = "Lemmy server (production)"; + + environment = { + LEMMY_CONFIG_LOCATION = + "${settingsFormat.generate "config.hjson" cfg.settings}"; + LEMMY_DATABASE_URL = mkIf (cfg.database.uri != null) cfg.database.uri; }; - assertions = [ - { - assertion = cfg.database.createLocally -> cfg.settings.database.host == "localhost" || cfg.settings.database.host == "/run/postgresql"; - message = "if you want to create the database locally, you need to use a local database"; - } - { - assertion = (!(hasAttrByPath ["federation"] cfg.settings)) && (!(hasAttrByPath ["federation" "enabled"] cfg.settings)); - message = "`services.lemmy.settings.federation` was removed in 0.17.0 and no longer has any effect"; - } + documentation = [ + "https://join-lemmy.org/docs/en/admins/from_scratch.html" + "https://join-lemmy.org/docs/en/" ]; - systemd.services.lemmy-prod = { - description = "Lemmy server (production)"; - - environment = { - LEMMY_CONFIG_LOCATION = "${settingsFormat.generate "config.hjson" cfg.settings}"; - LEMMY_DATABASE_URL = mkIf (cfg.database.uri != null) cfg.database.uri; - }; - - documentation = [ - "https://join-lemmy.org/docs/en/admins/from_scratch.html" - "https://join-lemmy.org/docs/en/" - ]; - - wantedBy = [ "multi-user.target" ]; + wantedBy = [ "multi-user.target" ]; - after = [ "pict-rs.service" ] ++ lib.optionals cfg.database.createLocally [ "postgresql.service" ]; + after = [ "pict-rs.service" ] + ++ lib.optionals cfg.database.createLocally [ "postgresql.service" ]; - requires = lib.optionals cfg.database.createLocally [ "postgresql.service" ]; + requires = + lib.optionals cfg.database.createLocally [ "postgresql.service" ]; - serviceConfig = { - DynamicUser = true; - RuntimeDirectory = "lemmy"; - ExecStart = "${cfg.server.package}/bin/lemmy_server"; - }; + serviceConfig = { + DynamicUser = true; + RuntimeDirectory = "lemmy"; + ExecStart = "${cfg.server.package}/bin/lemmy_server"; }; + }; - systemd.services.lemmy-ui-prod = { - description = "Lemmy UI (production)"; + systemd.services.lemmy-ui-prod = { + description = "Lemmy UI (production)"; - environment = { - LEMMY_UI_HOST = "127.0.0.1:${toString cfg.ui.port}"; - LEMMY_INTERNAL_HOST = "127.0.0.1:${toString cfg.settings.port}"; - LEMMY_EXTERNAL_HOST = cfg.settings.hostname; - LEMMY_HTTPS = "false"; - NODE_ENV = "production"; - }; + environment = { + LEMMY_UI_HOST = "127.0.0.1:${toString cfg.ui.port}"; + LEMMY_INTERNAL_HOST = "127.0.0.1:${toString cfg.settings.port}"; + LEMMY_EXTERNAL_HOST = cfg.settings.hostname; + LEMMY_HTTPS = "false"; + NODE_ENV = "production"; + }; - documentation = [ - "https://join-lemmy.org/docs/en/admins/from_scratch.html" - "https://join-lemmy.org/docs/en/" - ]; + documentation = [ + "https://join-lemmy.org/docs/en/admins/from_scratch.html" + "https://join-lemmy.org/docs/en/" + ]; - wantedBy = [ "multi-user.target" ]; + wantedBy = [ "multi-user.target" ]; - after = [ "lemmy-prod.service" ]; + after = [ "lemmy-prod.service" ]; - requires = [ "lemmy-prod.service" ]; + requires = [ "lemmy-prod.service" ]; - serviceConfig = { - DynamicUser = true; - WorkingDirectory = "${cfg.ui.package}"; - ExecStart = "${pkgs.nodejs}/bin/node ${cfg.ui.package}/dist/js/server.js"; - }; + serviceConfig = { + DynamicUser = true; + WorkingDirectory = "${cfg.ui.package}"; + ExecStart = + "${pkgs.nodejs}/bin/node ${cfg.ui.package}/dist/js/server.js"; }; }; + }; } diff --git a/lemmy/staging/module.nix b/lemmy/staging/module.nix index 87a9063..575ff63 100644 --- a/lemmy/staging/module.nix +++ b/lemmy/staging/module.nix @@ -154,12 +154,30 @@ in { }; }; - services.nginx = mkIf cfg.nginx.enable { + services.nginx = let + ui = "http://127.0.0.1:${toString cfg.ui.port}"; + backend = "http://127.0.0.1:${toString cfg.settings.port}"; + in mkIf cfg.nginx.enable { enable = mkDefault true; - virtualHosts."${cfg.settings.hostname}".locations = let - ui = "http://127.0.0.1:${toString cfg.ui.port}"; - backend = "http://127.0.0.1:${toString cfg.settings.port}"; - in { + appendHttpConfig = '' + map "$request_method:$http_accept" $proxpass { + # If no explicit matches exists below, send traffic to lemmy-ui + default "${ui}"; + + # GET/HEAD requests that accepts ActivityPub or Linked Data JSON should go to lemmy. + # + # These requests are used by Mastodon and other fediverse instances to look up profile information, + # discover site information and so on. + "~^(?:GET|HEAD):.*?application\/(?:activity|ld)\+json" "${backend}"; + + # All non-GET/HEAD requests should go to lemmy + # + # Rather than calling out POST, PUT, DELETE, PATCH, CONNECT and all the verbs manually + # we simply negate the GET|HEAD pattern from above and accept all possibly $http_accept values + "~^(?!(GET|HEAD)).*:" "${backend}"; + } + ''; + virtualHosts."${cfg.settings.hostname}".locations = { "~ ^/(api|pictrs|feeds|nodeinfo|.well-known)" = { # backend requests proxyPass = backend; @@ -171,17 +189,6 @@ in { proxyPass = "$proxpass"; recommendedProxySettings = true; extraConfig = '' - set $proxpass "${ui}"; - if ($http_accept = "application/activity+json") { - set $proxpass "${backend}"; - } - if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") { - set $proxpass "${backend}"; - } - if ($request_method = POST) { - set $proxpass "${backend}"; - } - # Cuts off the trailing slash on URLs to make them valid rewrite ^(.+)/+$ $1 permanent; ''; @@ -238,7 +245,8 @@ in { environment = { LEMMY_UI_HOST = "127.0.0.1:${toString cfg.ui.port}"; - LEMMY_UI_LEMMY_INTERNAL_HOST = "127.0.0.1:${toString cfg.settings.port}"; + LEMMY_UI_LEMMY_INTERNAL_HOST = + "127.0.0.1:${toString cfg.settings.port}"; LEMMY_UI_LEMMY_EXTERNAL_HOST = cfg.settings.hostname; LEMMY_UI_HTTPS = "false"; };