All checks were successful
Build and Deploy / deploy (push) Successful in 2m41s
380 lines
13 KiB
Diff
380 lines
13 KiB
Diff
From ab57092a60123e361cf0de1c1a314a9888c45219 Mon Sep 17 00:00:00 2001
|
|
From: Simon Gardling <titaniumtown@proton.me>
|
|
Date: Sat, 21 Mar 2026 09:24:39 -0400
|
|
Subject: [PATCH] temp
|
|
|
|
---
|
|
.../services/networking/firefox-syncserver.md | 23 +++
|
|
.../networking/firefox-syncserver.nix | 140 ++++++++++++++----
|
|
pkgs/by-name/sy/syncstorage-rs/package.nix | 49 ++++--
|
|
3 files changed, 174 insertions(+), 38 deletions(-)
|
|
|
|
diff --git a/nixos/modules/services/networking/firefox-syncserver.md b/nixos/modules/services/networking/firefox-syncserver.md
|
|
index 991e97f799d6..3bc45cfa5640 100644
|
|
--- a/nixos/modules/services/networking/firefox-syncserver.md
|
|
+++ b/nixos/modules/services/networking/firefox-syncserver.md
|
|
@@ -32,6 +32,29 @@ This configuration should never be used in production. It is not encrypted and
|
|
stores its secrets in a world-readable location.
|
|
:::
|
|
|
|
+## Database backends {#module-services-firefox-syncserver-database}
|
|
+
|
|
+The sync server supports MySQL/MariaDB (the default) and PostgreSQL as database
|
|
+backends. Set `database.type` to choose the backend:
|
|
+
|
|
+```nix
|
|
+{
|
|
+ services.firefox-syncserver = {
|
|
+ enable = true;
|
|
+ database.type = "postgresql";
|
|
+ secrets = "/run/secrets/firefox-syncserver";
|
|
+ singleNode = {
|
|
+ enable = true;
|
|
+ hostname = "localhost";
|
|
+ url = "http://localhost:5000";
|
|
+ };
|
|
+ };
|
|
+}
|
|
+```
|
|
+
|
|
+When `database.createLocally` is `true` (the default), the module will
|
|
+automatically enable and configure the corresponding database service.
|
|
+
|
|
## More detailed setup {#module-services-firefox-syncserver-configuration}
|
|
|
|
The `firefox-syncserver` service provides a number of options to make setting up
|
|
diff --git a/nixos/modules/services/networking/firefox-syncserver.nix b/nixos/modules/services/networking/firefox-syncserver.nix
|
|
index 6a50e49fc096..70a56314e323 100644
|
|
--- a/nixos/modules/services/networking/firefox-syncserver.nix
|
|
+++ b/nixos/modules/services/networking/firefox-syncserver.nix
|
|
@@ -13,7 +13,21 @@ let
|
|
defaultUser = "firefox-syncserver";
|
|
|
|
dbIsLocal = cfg.database.host == "localhost";
|
|
- dbURL = "mysql://${cfg.database.user}@${cfg.database.host}/${cfg.database.name}${lib.optionalString dbIsLocal "?socket=/run/mysqld/mysqld.sock"}";
|
|
+ dbIsMySQL = cfg.database.type == "mysql";
|
|
+ dbIsPostgreSQL = cfg.database.type == "postgresql";
|
|
+
|
|
+ dbURL =
|
|
+ if dbIsMySQL then
|
|
+ "mysql://${cfg.database.user}@${cfg.database.host}/${cfg.database.name}${lib.optionalString dbIsLocal "?socket=/run/mysqld/mysqld.sock"}"
|
|
+ else
|
|
+ "postgres://${cfg.database.user}@${cfg.database.host}/${cfg.database.name}${lib.optionalString dbIsLocal "?host=/run/postgresql"}";
|
|
+
|
|
+ # postgresql.target waits for postgresql-setup.service (which runs
|
|
+ # ensureDatabases / ensureUsers) to complete, avoiding race conditions
|
|
+ # where the syncserver starts before its database and role exist.
|
|
+ dbService = if dbIsMySQL then "mysql.service" else "postgresql.target";
|
|
+
|
|
+ syncserver = cfg.package.override { dbBackend = cfg.database.type; };
|
|
|
|
format = pkgs.formats.toml { };
|
|
settings = {
|
|
@@ -22,7 +36,7 @@ let
|
|
database_url = dbURL;
|
|
};
|
|
tokenserver = {
|
|
- node_type = "mysql";
|
|
+ node_type = if dbIsMySQL then "mysql" else "postgres";
|
|
database_url = dbURL;
|
|
fxa_email_domain = "api.accounts.firefox.com";
|
|
fxa_oauth_server_url = "https://oauth.accounts.firefox.com/v1";
|
|
@@ -41,7 +55,8 @@ let
|
|
};
|
|
};
|
|
configFile = format.generate "syncstorage.toml" (lib.recursiveUpdate settings cfg.settings);
|
|
- setupScript = pkgs.writeShellScript "firefox-syncserver-setup" ''
|
|
+
|
|
+ mysqlSetupScript = pkgs.writeShellScript "firefox-syncserver-setup" ''
|
|
set -euo pipefail
|
|
shopt -s inherit_errexit
|
|
|
|
@@ -79,6 +94,47 @@ let
|
|
echo "Single-node setup failed"
|
|
exit 1
|
|
'';
|
|
+
|
|
+ postgresqlSetupScript = pkgs.writeShellScript "firefox-syncserver-setup" ''
|
|
+ set -euo pipefail
|
|
+ shopt -s inherit_errexit
|
|
+
|
|
+ schema_configured() {
|
|
+ psql -d ${cfg.database.name} -tAc "SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'services')" | grep -q t
|
|
+ }
|
|
+
|
|
+ update_config() {
|
|
+ psql -d ${cfg.database.name} <<'EOF'
|
|
+ BEGIN;
|
|
+
|
|
+ INSERT INTO services (id, service, pattern)
|
|
+ VALUES (1, 'sync-1.5', '{node}/1.5/{uid}')
|
|
+ ON CONFLICT (id) DO UPDATE SET service = 'sync-1.5', pattern = '{node}/1.5/{uid}';
|
|
+ INSERT INTO nodes (id, service, node, available, current_load,
|
|
+ capacity, downed, backoff)
|
|
+ VALUES (1, 1, '${cfg.singleNode.url}', ${toString cfg.singleNode.capacity},
|
|
+ 0, ${toString cfg.singleNode.capacity}, 0, 0)
|
|
+ ON CONFLICT (id) DO UPDATE SET node = '${cfg.singleNode.url}', capacity = ${toString cfg.singleNode.capacity};
|
|
+
|
|
+ COMMIT;
|
|
+ EOF
|
|
+ }
|
|
+
|
|
+
|
|
+ for (( try = 0; try < 60; try++ )); do
|
|
+ if ! schema_configured; then
|
|
+ sleep 2
|
|
+ else
|
|
+ update_config
|
|
+ exit 0
|
|
+ fi
|
|
+ done
|
|
+
|
|
+ echo "Single-node setup failed"
|
|
+ exit 1
|
|
+ '';
|
|
+
|
|
+ setupScript = if dbIsMySQL then mysqlSetupScript else postgresqlSetupScript;
|
|
in
|
|
|
|
{
|
|
@@ -88,25 +144,26 @@ in
|
|
the Firefox Sync storage service.
|
|
|
|
Out of the box this will not be very useful unless you also configure at least
|
|
- one service and one nodes by inserting them into the mysql database manually, e.g.
|
|
- by running
|
|
-
|
|
- ```
|
|
- INSERT INTO `services` (`id`, `service`, `pattern`) VALUES ('1', 'sync-1.5', '{node}/1.5/{uid}');
|
|
- INSERT INTO `nodes` (`id`, `service`, `node`, `available`, `current_load`,
|
|
- `capacity`, `downed`, `backoff`)
|
|
- VALUES ('1', '1', 'https://mydomain.tld', '1', '0', '10', '0', '0');
|
|
- ```
|
|
+ one service and one nodes by inserting them into the database manually, e.g.
|
|
+ by running the equivalent SQL for your database backend.
|
|
|
|
{option}`${opt.singleNode.enable}` does this automatically when enabled
|
|
'';
|
|
|
|
package = lib.mkPackageOption pkgs "syncstorage-rs" { };
|
|
|
|
+ database.type = lib.mkOption {
|
|
+ type = lib.types.enum [
|
|
+ "mysql"
|
|
+ "postgresql"
|
|
+ ];
|
|
+ default = "mysql";
|
|
+ description = ''
|
|
+ Which database backend to use for storage.
|
|
+ '';
|
|
+ };
|
|
+
|
|
database.name = lib.mkOption {
|
|
- # the mysql module does not allow `-quoting without resorting to shell
|
|
- # escaping, so we restrict db names for forward compaitiblity should this
|
|
- # behavior ever change.
|
|
type = lib.types.strMatching "[a-z_][a-z0-9_]*";
|
|
default = defaultDatabase;
|
|
description = ''
|
|
@@ -117,9 +174,15 @@ in
|
|
|
|
database.user = lib.mkOption {
|
|
type = lib.types.str;
|
|
- default = defaultUser;
|
|
+ default = if dbIsPostgreSQL then defaultDatabase else defaultUser;
|
|
+ defaultText = lib.literalExpression ''
|
|
+ if database.type == "postgresql" then "${defaultDatabase}" else "${defaultUser}"
|
|
+ '';
|
|
description = ''
|
|
- Username for database connections.
|
|
+ Username for database connections. When using PostgreSQL with
|
|
+ `createLocally`, this defaults to the database name so that
|
|
+ `ensureDBOwnership` works (it requires user and database names
|
|
+ to match).
|
|
'';
|
|
};
|
|
|
|
@@ -137,7 +200,8 @@ in
|
|
default = true;
|
|
description = ''
|
|
Whether to create database and user on the local machine if they do not exist.
|
|
- This includes enabling unix domain socket authentication for the configured user.
|
|
+ This includes enabling the configured database service and setting up
|
|
+ authentication for the configured user.
|
|
'';
|
|
};
|
|
|
|
@@ -237,7 +301,7 @@ in
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
- services.mysql = lib.mkIf cfg.database.createLocally {
|
|
+ services.mysql = lib.mkIf (cfg.database.createLocally && dbIsMySQL) {
|
|
enable = true;
|
|
ensureDatabases = [ cfg.database.name ];
|
|
ensureUsers = [
|
|
@@ -250,16 +314,27 @@ in
|
|
];
|
|
};
|
|
|
|
+ services.postgresql = lib.mkIf (cfg.database.createLocally && dbIsPostgreSQL) {
|
|
+ enable = true;
|
|
+ ensureDatabases = [ cfg.database.name ];
|
|
+ ensureUsers = [
|
|
+ {
|
|
+ name = cfg.database.user;
|
|
+ ensureDBOwnership = true;
|
|
+ }
|
|
+ ];
|
|
+ };
|
|
+
|
|
systemd.services.firefox-syncserver = {
|
|
wantedBy = [ "multi-user.target" ];
|
|
- requires = lib.mkIf dbIsLocal [ "mysql.service" ];
|
|
- after = lib.mkIf dbIsLocal [ "mysql.service" ];
|
|
+ requires = lib.mkIf dbIsLocal [ dbService ];
|
|
+ after = lib.mkIf dbIsLocal [ dbService ];
|
|
restartTriggers = lib.optional cfg.singleNode.enable setupScript;
|
|
environment.RUST_LOG = cfg.logLevel;
|
|
serviceConfig = {
|
|
- User = defaultUser;
|
|
- Group = defaultUser;
|
|
- ExecStart = "${cfg.package}/bin/syncserver --config ${configFile}";
|
|
+ User = cfg.database.user;
|
|
+ Group = cfg.database.user;
|
|
+ ExecStart = "${syncserver}/bin/syncserver --config ${configFile}";
|
|
EnvironmentFile = lib.mkIf (cfg.secrets != null) "${cfg.secrets}";
|
|
|
|
# hardening
|
|
@@ -303,10 +378,19 @@ in
|
|
|
|
systemd.services.firefox-syncserver-setup = lib.mkIf cfg.singleNode.enable {
|
|
wantedBy = [ "firefox-syncserver.service" ];
|
|
- requires = [ "firefox-syncserver.service" ] ++ lib.optional dbIsLocal "mysql.service";
|
|
- after = [ "firefox-syncserver.service" ] ++ lib.optional dbIsLocal "mysql.service";
|
|
- path = [ config.services.mysql.package ];
|
|
- serviceConfig.ExecStart = [ "${setupScript}" ];
|
|
+ requires = [ "firefox-syncserver.service" ] ++ lib.optional dbIsLocal dbService;
|
|
+ after = [ "firefox-syncserver.service" ] ++ lib.optional dbIsLocal dbService;
|
|
+ path =
|
|
+ if dbIsMySQL then [ config.services.mysql.package ] else [ config.services.postgresql.package ];
|
|
+ serviceConfig = {
|
|
+ ExecStart = [ "${setupScript}" ];
|
|
+ }
|
|
+ // lib.optionalAttrs dbIsPostgreSQL {
|
|
+ # PostgreSQL peer authentication requires the system user to match the
|
|
+ # database user. Run as the superuser so we can access all databases.
|
|
+ User = "postgres";
|
|
+ Group = "postgres";
|
|
+ };
|
|
};
|
|
|
|
services.nginx.virtualHosts = lib.mkIf cfg.singleNode.enableNginx {
|
|
diff --git a/pkgs/by-name/sy/syncstorage-rs/package.nix b/pkgs/by-name/sy/syncstorage-rs/package.nix
|
|
index 39b2b53ab03c..944ed72525af 100644
|
|
--- a/pkgs/by-name/sy/syncstorage-rs/package.nix
|
|
+++ b/pkgs/by-name/sy/syncstorage-rs/package.nix
|
|
@@ -1,14 +1,18 @@
|
|
{
|
|
fetchFromGitHub,
|
|
+ fetchurl,
|
|
rustPlatform,
|
|
pkg-config,
|
|
python3,
|
|
cmake,
|
|
libmysqlclient,
|
|
+ libpq,
|
|
+ openssl,
|
|
makeBinaryWrapper,
|
|
lib,
|
|
nix-update-script,
|
|
nixosTests,
|
|
+ dbBackend ? "mysql",
|
|
}:
|
|
|
|
let
|
|
@@ -19,17 +23,23 @@ let
|
|
p.tokenlib
|
|
p.cryptography
|
|
]);
|
|
+ # utoipa-swagger-ui downloads Swagger UI assets at build time.
|
|
+ # Prefetch the archive for sandboxed builds.
|
|
+ swaggerUi = fetchurl {
|
|
+ url = "https://github.com/swagger-api/swagger-ui/archive/refs/tags/v5.17.14.zip";
|
|
+ hash = "sha256-SBJE0IEgl7Efuu73n3HZQrFxYX+cn5UU5jrL4T5xzNw=";
|
|
+ };
|
|
in
|
|
|
|
-rustPlatform.buildRustPackage rec {
|
|
+rustPlatform.buildRustPackage (finalAttrs: {
|
|
pname = "syncstorage-rs";
|
|
- version = "0.21.1-unstable-2026-01-26";
|
|
+ version = "0.21.1-unstable-2026-02-24";
|
|
|
|
src = fetchFromGitHub {
|
|
owner = "mozilla-services";
|
|
repo = "syncstorage-rs";
|
|
- rev = "11659d98f9c69948a0aab353437ce2036c388711";
|
|
- hash = "sha256-G37QvxTNh/C3gmKG0UYHI6QBr0F+KLGRNI/Sx33uOsc=";
|
|
+ rev = "50a739b58dc9ec81995f86e71d992aa14ccc450e";
|
|
+ hash = "sha256-idq0RGdwoV6GVuq36IVVVCFbyMTe8i/EpVWE59D/dhM=";
|
|
};
|
|
|
|
nativeBuildInputs = [
|
|
@@ -39,16 +49,35 @@ rustPlatform.buildRustPackage rec {
|
|
python3
|
|
];
|
|
|
|
- buildInputs = [
|
|
- libmysqlclient
|
|
- ];
|
|
+ buildInputs =
|
|
+ lib.optional (dbBackend == "mysql") libmysqlclient
|
|
+ ++ lib.optionals (dbBackend == "postgresql") [
|
|
+ libpq
|
|
+ openssl
|
|
+ ];
|
|
+
|
|
+ buildNoDefaultFeatures = true;
|
|
+ # The syncserver "postgres" feature only enables syncstorage-db/postgres.
|
|
+ # tokenserver-db/postgres must be enabled separately so the tokenserver
|
|
+ # can also connect to PostgreSQL (it dispatches on the URL scheme at runtime).
|
|
+ buildFeatures =
|
|
+ let
|
|
+ cargoFeature = if dbBackend == "postgresql" then "postgres" else dbBackend;
|
|
+ in
|
|
+ [
|
|
+ cargoFeature
|
|
+ "tokenserver-db/${cargoFeature}"
|
|
+ "py_verifier"
|
|
+ ];
|
|
+
|
|
+ SWAGGER_UI_DOWNLOAD_URL = "file://${swaggerUi}";
|
|
|
|
preFixup = ''
|
|
wrapProgram $out/bin/syncserver \
|
|
--prefix PATH : ${lib.makeBinPath [ pyFxADeps ]}
|
|
'';
|
|
|
|
- cargoHash = "sha256-9Dcf5mDyK/XjsKTlCPXTHoBkIq+FFPDg1zfK24Y9nHQ=";
|
|
+ cargoHash = "sha256-80EztkSX+SnmqsRWIXbChUB8AeV1Tp9WXoWNbDY8rUE=";
|
|
|
|
# almost all tests need a DB to test against
|
|
doCheck = false;
|
|
@@ -60,10 +89,10 @@ rustPlatform.buildRustPackage rec {
|
|
meta = {
|
|
description = "Mozilla Sync Storage built with Rust";
|
|
homepage = "https://github.com/mozilla-services/syncstorage-rs";
|
|
- changelog = "https://github.com/mozilla-services/syncstorage-rs/releases/tag/${version}";
|
|
+ changelog = "https://github.com/mozilla-services/syncstorage-rs/releases/tag/${finalAttrs.version}";
|
|
license = lib.licenses.mpl20;
|
|
maintainers = [ ];
|
|
platforms = lib.platforms.linux;
|
|
mainProgram = "syncserver";
|
|
};
|
|
-}
|
|
+})
|
|
--
|
|
2.53.0
|
|
|