diff --git a/nixos/modules/services/misc/rmfakecloud.nix b/nixos/modules/services/misc/rmfakecloud.nix index 6cc87753aa25..d19a9969a3ff 100644 --- a/nixos/modules/services/misc/rmfakecloud.nix +++ b/nixos/modules/services/misc/rmfakecloud.nix @@ -1,4 +1,9 @@ -{ config, lib, pkgs, ... }: +{ + config, + lib, + pkgs, + ... +}: with lib; @@ -6,18 +11,13 @@ let cfg = config.services.rmfakecloud; serviceDataDir = "/var/lib/rmfakecloud"; -in { +in +{ options = { services.rmfakecloud = { enable = mkEnableOption "rmfakecloud remarkable self-hosted cloud"; - package = mkPackageOption pkgs "rmfakecloud" { - extraDescription = '' - ::: {.note} - The default does not include the web user interface. - ::: - ''; - }; + package = mkPackageOption pkgs "rmfakecloud" { }; storageUrl = mkOption { type = types.str; @@ -36,7 +36,12 @@ in { }; logLevel = mkOption { - type = types.enum [ "info" "debug" "warn" "error" ]; + type = types.enum [ + "info" + "debug" + "warn" + "error" + ]; default = "info"; description = '' Logging level. @@ -46,7 +51,9 @@ in { extraSettings = mkOption { type = with types; attrsOf str; default = { }; - example = { DATADIR = "/custom/path/for/rmfakecloud/data"; }; + example = { + DATADIR = "/custom/path/for/rmfakecloud/data"; + }; description = '' Extra settings in the form of a set of key-value pairs. For tokens and secrets, use `environmentFile` instead. @@ -106,11 +113,9 @@ in { Type = "simple"; Restart = "always"; - EnvironmentFile = - mkIf (cfg.environmentFile != null) cfg.environmentFile; + EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile; - AmbientCapabilities = - mkIf (cfg.port < 1024) [ "CAP_NET_BIND_SERVICE" ]; + AmbientCapabilities = mkIf (cfg.port < 1024) [ "CAP_NET_BIND_SERVICE" ]; DynamicUser = true; PrivateDevices = true; @@ -128,7 +133,10 @@ in { ProtectProc = "invisible"; ProcSubset = "pid"; RemoveIPC = true; - RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + ]; RestrictNamespaces = true; RestrictRealtime = true; RestrictSUIDSGID = true; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 14a1439eeae7..47c1ce41671b 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -876,6 +876,7 @@ in { retroarch = handleTest ./retroarch.nix {}; rke2 = handleTestOn ["aarch64-linux" "x86_64-linux"] ./rke2 {}; rkvm = handleTest ./rkvm {}; + rmfakecloud = runTest ./rmfakecloud.nix; robustirc-bridge = handleTest ./robustirc-bridge.nix {}; roundcube = handleTest ./roundcube.nix {}; rosenpass = handleTest ./rosenpass.nix {}; diff --git a/nixos/tests/rmfakecloud.nix b/nixos/tests/rmfakecloud.nix new file mode 100644 index 000000000000..f226c20b9577 --- /dev/null +++ b/nixos/tests/rmfakecloud.nix @@ -0,0 +1,67 @@ +{ pkgs, ... }: +{ + name = "rmfakecloud"; + meta = with pkgs.lib.maintainers; { + maintainers = [ martinetd ]; + }; + + nodes.machine = { + services.rmfakecloud = { + enable = true; + storageUrl = "https://local.appspot.com"; + }; + }; + + testScript = '' + machine.wait_for_unit("rmfakecloud.service") + machine.wait_for_open_port(3000) + + # first login creates user + login_token = machine.succeed(""" + curl -sSf -b cookie -c cookie -H "Content-Type: application/json" \ + -d'{"email":"test","password":"test"}' -X POST \ + http://localhost:3000/ui/api/login + """) + + # subsequent different pass or mail should fail, but same login works + machine.fail(""" + curl -sSf -H "Content-Type: application/json" \ + -d'{"email":"test","password":"test2"}' -X POST \ + http://localhost:3000/ui/api/login + """) + machine.fail(""" + curl -sSf -H "Content-Type: application/json" \ + -d'{"email":"test2","password":"test"}' -X POST + http://localhost:3000/ui/api/login + """) + machine.succeed(""" + curl -sSf -H "Content-Type: application/json" \ + -d'{"email":"test","password":"test"}' -X POST \ + http://localhost:3000/ui/api/login + """) + + # can get code from cookie or bearer + machine.succeed(""" + curl -sSf -b cookie -c cookie http://localhost:3000/ui/api/newcode + """) + newcode = machine.succeed(f""" + curl -sSf -H "Authorization: Bearer {login_token}" \ + http://localhost:3000/ui/api/newcode + """).strip('"') + + # ... but not junk + machine.fail(f""" + curl -sSf -H "Authorization: Bearer abc{login_token}" \ + http://localhost:3000/ui/api/newcode + """) + + # can connect "device" with said code + machine.succeed(f""" + curl -sSf -d '{{"code":"{newcode}", "deviceDesc": "desc", "deviceID":"rm100-123"}}' \ + http://localhost:3000/token/json/2/device/new + """) + + # for future improvements + machine.log(machine.execute("systemd-analyze security rmfakecloud.service")[1]) + ''; +} diff --git a/pkgs/by-name/rm/rmfakecloud/package.nix b/pkgs/by-name/rm/rmfakecloud/package.nix index 315ac8ec43b1..f68229b10a16 100644 --- a/pkgs/by-name/rm/rmfakecloud/package.nix +++ b/pkgs/by-name/rm/rmfakecloud/package.nix @@ -1,36 +1,54 @@ -{ lib, fetchFromGitHub, buildGoModule, callPackage, enableWebui ? true }: +{ + lib, + fetchFromGitHub, + buildGoModule, + callPackage, + enableWebui ? true, + nixosTests, +}: buildGoModule rec { pname = "rmfakecloud"; - version = "0.0.18"; + version = "0.0.21"; src = fetchFromGitHub { owner = "ddvk"; repo = pname; rev = "v${version}"; - hash = "sha256-J8oB5C5FYZTVq9zopHoL6WYpfTyiiyrQ4YSGu+2eaKw="; + hash = "sha256-Opx39FUo4Kzezi96D9iraA8gkqCPVfMf4LhxtVpsuNQ="; }; - vendorHash = "sha256-S43qNDAlDWhrkfSffCooveemR1Z7KXS18t97UoolgBM="; + vendorHash = "sha256-9tfxE03brUvCYusmewiqNpCkKyIS9qePqylrzDWrJLY="; ui = callPackage ./webui.nix { inherit version src; }; - postPatch = if enableWebui then '' - mkdir -p ui/build - cp -r ${ui}/* ui/build - '' else '' - sed -i '/go:/d' ui/assets.go - ''; + postPatch = + if enableWebui then + '' + mkdir -p ui/build + cp -r ${ui}/* ui/build + '' + else + '' + sed -i '/go:/d' ui/assets.go + ''; ldflags = [ - "-s" "-w" "-X main.version=v${version}" + "-s" + "-w" + "-X main.version=v${version}" ]; + passthru.tests.rmfakecloud = nixosTests.rmfakecloud; + meta = with lib; { description = "Host your own cloud for the Remarkable"; homepage = "https://ddvk.github.io/rmfakecloud/"; license = licenses.agpl3Only; - maintainers = with maintainers; [ pacien martinetd ]; + maintainers = with maintainers; [ + pacien + martinetd + ]; mainProgram = "rmfakecloud"; }; } diff --git a/pkgs/by-name/rm/rmfakecloud/webui.nix b/pkgs/by-name/rm/rmfakecloud/webui.nix index 91efa1c78d9d..928a624cba2b 100644 --- a/pkgs/by-name/rm/rmfakecloud/webui.nix +++ b/pkgs/by-name/rm/rmfakecloud/webui.nix @@ -7,7 +7,7 @@ stdenv.mkDerivation rec { yarnOfflineCache = fetchYarnDeps { yarnLock = "${src}/ui/yarn.lock"; - hash = "sha256-JLCrpzytMKejmW+WlM6yybsoIZiimiJdPG5dSIn1L14="; + hash = "sha256-9//uQ4ZLLTf2N1WSwsOwFjBuDmThuMtMXU4SzMljAMM="; }; nativeBuildInputs = [ fixup-yarn-lock yarn nodejs ];