nixos/db-rest: init
This commit is contained in:
parent
c9ff971b19
commit
8785ef0a2d
6 changed files with 297 additions and 0 deletions
|
@ -118,6 +118,8 @@ Use `services.pipewire.extraConfig` or `services.pipewire.configPackages` for Pi
|
||||||
Matter Controller Server exposing websocket connections for use with other services, notably Home Assistant.
|
Matter Controller Server exposing websocket connections for use with other services, notably Home Assistant.
|
||||||
Available as [services.matter-server](#opt-services.matter-server.enable)
|
Available as [services.matter-server](#opt-services.matter-server.enable)
|
||||||
|
|
||||||
|
- [db-rest](https://github.com/derhuerst/db-rest), a wrapper around Deutsche Bahn's internal API for public transport data. Available as [services.db-rest](#opt-services.db-rest.enable).
|
||||||
|
|
||||||
- [Anki Sync Server](https://docs.ankiweb.net/sync-server.html), the official sync server built into recent versions of Anki. Available as [services.anki-sync-server](#opt-services.anki-sync-server.enable).
|
- [Anki Sync Server](https://docs.ankiweb.net/sync-server.html), the official sync server built into recent versions of Anki. Available as [services.anki-sync-server](#opt-services.anki-sync-server.enable).
|
||||||
The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been marked deprecated and will be dropped after 24.05 due to lack of maintenance of the anki-sync-server softwares.
|
The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been marked deprecated and will be dropped after 24.05 due to lack of maintenance of the anki-sync-server softwares.
|
||||||
|
|
||||||
|
|
|
@ -690,6 +690,7 @@
|
||||||
./services/misc/clipmenu.nix
|
./services/misc/clipmenu.nix
|
||||||
./services/misc/confd.nix
|
./services/misc/confd.nix
|
||||||
./services/misc/cpuminer-cryptonight.nix
|
./services/misc/cpuminer-cryptonight.nix
|
||||||
|
./services/misc/db-rest.nix
|
||||||
./services/misc/devmon.nix
|
./services/misc/devmon.nix
|
||||||
./services/misc/dictd.nix
|
./services/misc/dictd.nix
|
||||||
./services/misc/disnix.nix
|
./services/misc/disnix.nix
|
||||||
|
|
182
nixos/modules/services/misc/db-rest.nix
Normal file
182
nixos/modules/services/misc/db-rest.nix
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) mkOption types mkIf mkMerge mkDefault mkEnableOption mkPackageOption maintainers;
|
||||||
|
cfg = config.services.db-rest;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
services.db-rest = {
|
||||||
|
enable = mkEnableOption "db-rest service";
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "db-rest";
|
||||||
|
description = "User account under which db-rest runs.";
|
||||||
|
};
|
||||||
|
|
||||||
|
group = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "db-rest";
|
||||||
|
description = "Group under which db-rest runs.";
|
||||||
|
};
|
||||||
|
|
||||||
|
host = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "127.0.0.1";
|
||||||
|
description = "The host address the db-rest server should listen on.";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 3000;
|
||||||
|
description = "The port the db-rest server should listen on.";
|
||||||
|
};
|
||||||
|
|
||||||
|
redis = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Enable caching with redis for db-rest.";
|
||||||
|
};
|
||||||
|
|
||||||
|
createLocally = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Configure a local redis server for db-rest.";
|
||||||
|
};
|
||||||
|
|
||||||
|
host = mkOption {
|
||||||
|
type = with types; nullOr str;
|
||||||
|
default = null;
|
||||||
|
description = "Redis host.";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = with types; nullOr port;
|
||||||
|
default = null;
|
||||||
|
description = "Redis port.";
|
||||||
|
};
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = with types; nullOr str;
|
||||||
|
default = null;
|
||||||
|
description = "Optional username used for authentication with redis.";
|
||||||
|
};
|
||||||
|
|
||||||
|
passwordFile = mkOption {
|
||||||
|
type = with types; nullOr path;
|
||||||
|
default = null;
|
||||||
|
example = "/run/keys/db-rest/pasword-redis-db";
|
||||||
|
description = "Path to a file containing the redis password.";
|
||||||
|
};
|
||||||
|
|
||||||
|
useSSL = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Use SSL if using a redis network connection.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
package = mkPackageOption pkgs "db-rest" { };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = (cfg.redis.enable && !cfg.redis.createLocally) -> (cfg.redis.host != null && cfg.redis.port != null);
|
||||||
|
message = ''
|
||||||
|
{option}`services.db-rest.redis.createLocally` and redis network connection ({option}`services.db-rest.redis.host` or {option}`services.db-rest.redis.port`) enabled. Disable either of them.
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = (cfg.redis.enable && !cfg.redis.createLocally) -> (cfg.redis.passwordFile != null);
|
||||||
|
message = ''
|
||||||
|
{option}`services.db-rest.redis.createLocally` is disabled, but {option}`services.db-rest.redis.passwordFile` is not set.
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
systemd.services.db-rest = mkMerge [
|
||||||
|
{
|
||||||
|
description = "db-rest service";
|
||||||
|
after = [ "network.target" ]
|
||||||
|
++ lib.optional cfg.redis.createLocally "redis-db-rest.service";
|
||||||
|
requires = lib.optional cfg.redis.createLocally "redis-db-rest.service";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "simple";
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = 5;
|
||||||
|
WorkingDirectory = cfg.package;
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
|
||||||
|
MemoryDenyWriteExecute = false;
|
||||||
|
LoadCredential = lib.optional (cfg.redis.enable && cfg.redis.passwordFile != null) "REDIS_PASSWORD:${cfg.redis.passwordFile}";
|
||||||
|
ExecStart = mkDefault "${cfg.package}/bin/db-rest";
|
||||||
|
|
||||||
|
RemoveIPC = true;
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateDevices = true;
|
||||||
|
ProtectClock = true;
|
||||||
|
ProtectKernelLogs = true;
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
PrivateMounts = true;
|
||||||
|
SystemCallArchitectures = "native";
|
||||||
|
ProtectHostname = true;
|
||||||
|
LockPersonality = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
RestrictRealtime = true;
|
||||||
|
RestrictSUIDSGID = true;
|
||||||
|
RestrictNamespaces = true;
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
ProtectProc = "invisible";
|
||||||
|
ProcSubset = "pid";
|
||||||
|
ProtectHome = true;
|
||||||
|
PrivateUsers = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
CapabilityBoundingSet = "";
|
||||||
|
};
|
||||||
|
environment = {
|
||||||
|
NODE_ENV = "production";
|
||||||
|
NODE_EXTRA_CA_CERTS = "/etc/ssl/certs/ca-certificates.crt";
|
||||||
|
HOSTNAME = cfg.host;
|
||||||
|
PORT = toString cfg.port;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
(mkIf cfg.redis.enable (if cfg.redis.createLocally then
|
||||||
|
{ environment.REDIS_URL = config.services.redis.servers.db-rest.unixSocket; }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
script =
|
||||||
|
let
|
||||||
|
username = lib.optionalString (cfg.redis.user != null) (cfg.redis.user);
|
||||||
|
host = cfg.redis.host;
|
||||||
|
port = toString cfg.redis.port;
|
||||||
|
protocol = if cfg.redis.useSSL then "rediss" else "redis";
|
||||||
|
in
|
||||||
|
''
|
||||||
|
export REDIS_URL="${protocol}://${username}:$(${config.systemd.package}/bin/systemd-creds cat REDIS_PASSWORD)@${host}:${port}"
|
||||||
|
exec ${cfg.package}/bin/db-rest
|
||||||
|
'';
|
||||||
|
}))
|
||||||
|
];
|
||||||
|
|
||||||
|
users.users = lib.mkMerge [
|
||||||
|
(lib.mkIf (cfg.user == "db-rest") {
|
||||||
|
db-rest = {
|
||||||
|
isSystemUser = true;
|
||||||
|
group = cfg.group;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
(lib.mkIf cfg.redis.createLocally { ${cfg.user}.extraGroups = [ "redis-db-rest" ]; })
|
||||||
|
];
|
||||||
|
|
||||||
|
users.groups = lib.mkIf (cfg.group == "db-rest") { db-rest = { }; };
|
||||||
|
|
||||||
|
services.redis.servers.db-rest.enable = cfg.redis.enable && cfg.redis.createLocally;
|
||||||
|
};
|
||||||
|
meta.maintainers = with maintainers; [ marie ];
|
||||||
|
}
|
|
@ -236,6 +236,7 @@ in {
|
||||||
darling = handleTest ./darling.nix {};
|
darling = handleTest ./darling.nix {};
|
||||||
dae = handleTest ./dae.nix {};
|
dae = handleTest ./dae.nix {};
|
||||||
davis = handleTest ./davis.nix {};
|
davis = handleTest ./davis.nix {};
|
||||||
|
db-rest = handleTest ./db-rest.nix {};
|
||||||
dconf = handleTest ./dconf.nix {};
|
dconf = handleTest ./dconf.nix {};
|
||||||
deconz = handleTest ./deconz.nix {};
|
deconz = handleTest ./deconz.nix {};
|
||||||
deepin = handleTest ./deepin.nix {};
|
deepin = handleTest ./deepin.nix {};
|
||||||
|
|
107
nixos/tests/db-rest.nix
Normal file
107
nixos/tests/db-rest.nix
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
import ./make-test-python.nix ({ pkgs, ... }:
|
||||||
|
{
|
||||||
|
name = "db-rest";
|
||||||
|
meta.maintainers = with pkgs.lib.maintainers; [ marie ];
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
database = {
|
||||||
|
networking = {
|
||||||
|
interfaces.eth1 = {
|
||||||
|
ipv4.addresses = [
|
||||||
|
{ address = "192.168.2.10"; prefixLength = 24; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
firewall.allowedTCPPorts = [ 31638 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.redis.servers.db-rest = {
|
||||||
|
enable = true;
|
||||||
|
bind = "0.0.0.0";
|
||||||
|
requirePass = "choochoo";
|
||||||
|
port = 31638;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
serverWithTcp = { pkgs, ... }: {
|
||||||
|
environment = {
|
||||||
|
etc = {
|
||||||
|
"db-rest/password-redis-db".text = ''
|
||||||
|
choochoo
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking = {
|
||||||
|
interfaces.eth1 = {
|
||||||
|
ipv4.addresses = [
|
||||||
|
{ address = "192.168.2.11"; prefixLength = 24; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
firewall.allowedTCPPorts = [ 3000 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.db-rest = {
|
||||||
|
enable = true;
|
||||||
|
host = "0.0.0.0";
|
||||||
|
redis = {
|
||||||
|
enable = true;
|
||||||
|
createLocally = false;
|
||||||
|
host = "192.168.2.10";
|
||||||
|
port = 31638;
|
||||||
|
passwordFile = "/etc/db-rest/password-redis-db";
|
||||||
|
useSSL = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
serverWithUnixSocket = { pkgs, ... }: {
|
||||||
|
networking = {
|
||||||
|
interfaces.eth1 = {
|
||||||
|
ipv4.addresses = [
|
||||||
|
{ address = "192.168.2.12"; prefixLength = 24; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
firewall.allowedTCPPorts = [ 3000 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.db-rest = {
|
||||||
|
enable = true;
|
||||||
|
host = "0.0.0.0";
|
||||||
|
redis = {
|
||||||
|
enable = true;
|
||||||
|
createLocally = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
client = {
|
||||||
|
environment.systemPackages = [ pkgs.jq ];
|
||||||
|
networking = {
|
||||||
|
interfaces.eth1 = {
|
||||||
|
ipv4.addresses = [
|
||||||
|
{ address = "192.168.2.13"; prefixLength = 24; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = ''
|
||||||
|
start_all()
|
||||||
|
|
||||||
|
with subtest("db-rest redis with TCP socket"):
|
||||||
|
database.wait_for_unit("redis-db-rest.service")
|
||||||
|
database.wait_for_open_port(31638)
|
||||||
|
|
||||||
|
serverWithTcp.wait_for_unit("db-rest.service")
|
||||||
|
serverWithTcp.wait_for_open_port(3000)
|
||||||
|
|
||||||
|
client.succeed("curl --fail --get http://192.168.2.11:3000/stations --data-urlencode 'query=Köln Hbf' | jq -r '.\"8000207\".name' | grep 'Köln Hbf'")
|
||||||
|
|
||||||
|
with subtest("db-rest redis with Unix socket"):
|
||||||
|
serverWithUnixSocket.wait_for_unit("db-rest.service")
|
||||||
|
serverWithUnixSocket.wait_for_open_port(3000)
|
||||||
|
|
||||||
|
client.succeed("curl --fail --get http://192.168.2.12:3000/stations --data-urlencode 'query=Köln Hbf' | jq -r '.\"8000207\".name' | grep 'Köln Hbf'")
|
||||||
|
'';
|
||||||
|
})
|
|
@ -4,6 +4,7 @@
|
||||||
, nodejs_18
|
, nodejs_18
|
||||||
, nix-update-script
|
, nix-update-script
|
||||||
, fetchpatch
|
, fetchpatch
|
||||||
|
, nixosTests
|
||||||
}:
|
}:
|
||||||
buildNpmPackage rec {
|
buildNpmPackage rec {
|
||||||
pname = "db-rest";
|
pname = "db-rest";
|
||||||
|
@ -25,6 +26,9 @@ buildNpmPackage rec {
|
||||||
'';
|
'';
|
||||||
|
|
||||||
passthru.updateScript = nix-update-script { };
|
passthru.updateScript = nix-update-script { };
|
||||||
|
passthru.tests = {
|
||||||
|
inherit (nixosTests) db-rest;
|
||||||
|
};
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
description = "A clean REST API wrapping around the Deutsche Bahn API";
|
description = "A clean REST API wrapping around the Deutsche Bahn API";
|
||||||
|
|
Loading…
Reference in a new issue