Merge pull request #258558 from hmenke/c2FmZQ

c2FmZQ: init at 0.4.8

authored by Peder Bergebakken Sundt and committed by GitHub e8ff5952 e6726841

+282
+2
nixos/doc/manual/release-notes/rl-2311.section.md
··· 125 125 126 126 - [Rosenpass](https://rosenpass.eu/), a service for post-quantum-secure VPNs with WireGuard. Available as [services.rosenpass](#opt-services.rosenpass.enable). 127 127 128 + - [c2FmZQ](https://github.com/c2FmZQ/c2FmZQ/), an application that can securely encrypt, store, and share files, including but not limited to pictures and videos. Available as [services.c2fmzq-server](#opt-services.c2fmzq-server.enable). 129 + 128 130 ## Backward Incompatibilities {#sec-release-23.11-incompatibilities} 129 131 130 132 - `network-online.target` has been fixed to no longer time out for systems with `networking.useDHCP = true` and `networking.useNetworkd = true`.
+1
nixos/modules/module-list.nix
··· 1232 1232 ./services/web-apps/atlassian/jira.nix 1233 1233 ./services/web-apps/audiobookshelf.nix 1234 1234 ./services/web-apps/bookstack.nix 1235 + ./services/web-apps/c2fmzq-server.nix 1235 1236 ./services/web-apps/calibre-web.nix 1236 1237 ./services/web-apps/coder.nix 1237 1238 ./services/web-apps/changedetection-io.nix
+42
nixos/modules/services/web-apps/c2fmzq-server.md
··· 1 + # c2FmZQ {#module-services-c2fmzq} 2 + 3 + c2FmZQ is an application that can securely encrypt, store, and share files, 4 + including but not limited to pictures and videos. 5 + 6 + The service `c2fmzq-server` can be enabled by setting 7 + ``` 8 + { 9 + services.c2fmzq-server.enable = true; 10 + } 11 + ``` 12 + This will spin up an instance of the server which is API-compatible with 13 + [Stingle Photos](https://stingle.org) and an experimental Progressive Web App 14 + (PWA) to interact with the storage via the browser. 15 + 16 + In principle the server can be exposed directly on a public interface and there 17 + are command line options to manage HTTPS certificates directly, but the module 18 + is designed to be served behind a reverse proxy or only accessed via localhost. 19 + 20 + ``` 21 + { 22 + services.c2fmzq-server = { 23 + enable = true; 24 + bindIP = "127.0.0.1"; # default 25 + port = 8080; # default 26 + }; 27 + 28 + services.nginx = { 29 + enable = true; 30 + recommendedProxySettings = true; 31 + virtualHosts."example.com" = { 32 + enableACME = true; 33 + forceSSL = true; 34 + locations."/" = { 35 + proxyPass = "http://127.0.0.1:8080"; 36 + }; 37 + }; 38 + }; 39 + } 40 + ``` 41 + 42 + For more information, see <https://github.com/c2FmZQ/c2FmZQ/>.
+125
nixos/modules/services/web-apps/c2fmzq-server.nix
··· 1 + { lib, pkgs, config, ... }: 2 + 3 + let 4 + inherit (lib) mkEnableOption mkPackageOption mkOption types; 5 + 6 + cfg = config.services.c2fmzq-server; 7 + 8 + argsFormat = { 9 + type = with lib.types; nullOr (oneOf [ bool int str ]); 10 + generate = lib.cli.toGNUCommandLineShell { }; 11 + }; 12 + in { 13 + options.services.c2fmzq-server = { 14 + enable = mkEnableOption "c2fmzq-server"; 15 + 16 + bindIP = mkOption { 17 + type = types.str; 18 + default = "127.0.0.1"; 19 + description = "The local address to use."; 20 + }; 21 + 22 + port = mkOption { 23 + type = types.port; 24 + default = 8080; 25 + description = "The local port to use."; 26 + }; 27 + 28 + passphraseFile = mkOption { 29 + type = types.str; 30 + example = "/run/secrets/c2fmzq/pwfile"; 31 + description = "Path to file containing the database passphrase"; 32 + }; 33 + 34 + package = mkPackageOption pkgs "c2fmzq" { }; 35 + 36 + settings = mkOption { 37 + type = types.submodule { 38 + freeformType = argsFormat.type; 39 + 40 + options = { 41 + address = mkOption { 42 + internal = true; 43 + type = types.str; 44 + default = "${cfg.bindIP}:${toString cfg.port}"; 45 + }; 46 + 47 + database = mkOption { 48 + type = types.str; 49 + default = "%S/c2fmzq-server/data"; 50 + description = "Path of the database"; 51 + }; 52 + 53 + verbose = mkOption { 54 + type = types.ints.between 1 3; 55 + default = 2; 56 + description = "The level of logging verbosity: 1:Error 2:Info 3:Debug"; 57 + }; 58 + }; 59 + }; 60 + description = '' 61 + Configuration for c2FmZQ-server passed as CLI arguments. 62 + Run {command}`c2FmZQ-server help` for supported values. 63 + ''; 64 + example = { 65 + verbose = 3; 66 + allow-new-accounts = true; 67 + auto-approve-new-accounts = true; 68 + encrypt-metadata = true; 69 + enable-webapp = true; 70 + }; 71 + }; 72 + }; 73 + 74 + config = lib.mkIf cfg.enable { 75 + systemd.services.c2fmzq-server = { 76 + description = "c2FmZQ-server"; 77 + documentation = [ "https://github.com/c2FmZQ/c2FmZQ/blob/main/README.md" ]; 78 + wantedBy = [ "multi-user.target" ]; 79 + after = [ "network.target" "network-online.target" ]; 80 + 81 + serviceConfig = { 82 + ExecStart = "${lib.getExe cfg.package} ${argsFormat.generate cfg.settings}"; 83 + AmbientCapabilities = ""; 84 + CapabilityBoundingSet = ""; 85 + DynamicUser = true; 86 + Environment = "C2FMZQ_PASSPHRASE_FILE=%d/passphrase-file"; 87 + IPAccounting = true; 88 + IPAddressAllow = cfg.bindIP; 89 + IPAddressDeny = "any"; 90 + LoadCredential = "passphrase-file:${cfg.passphraseFile}"; 91 + LockPersonality = true; 92 + MemoryDenyWriteExecute = true; 93 + NoNewPrivileges = true; 94 + PrivateDevices = true; 95 + PrivateIPC = true; 96 + PrivateTmp = true; 97 + PrivateUsers = true; 98 + ProtectClock = true; 99 + ProtectControlGroups = true; 100 + ProtectHome = true; 101 + ProtectHostname = true; 102 + ProtectKernelLogs = true; 103 + ProtectKernelModules = true; 104 + ProtectKernelTunables = true; 105 + ProtectProc = "invisible"; 106 + ProtectSystem = "strict"; 107 + RemoveIPC = true; 108 + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; 109 + RestrictNamespaces = true; 110 + RestrictRealtime = true; 111 + RestrictSUIDSGID = true; 112 + SocketBindAllow = cfg.port; 113 + SocketBindDeny = "any"; 114 + StateDirectory = "c2fmzq-server"; 115 + SystemCallArchitectures = "native"; 116 + SystemCallFilter = [ "@system-service" "~@privileged @obsolete" ]; 117 + }; 118 + }; 119 + }; 120 + 121 + meta = { 122 + doc = ./c2fmzq-server.md; 123 + maintainers = with lib.maintainers; [ hmenke ]; 124 + }; 125 + }
+1
nixos/tests/all-tests.nix
··· 153 153 budgie = handleTest ./budgie.nix {}; 154 154 buildbot = handleTest ./buildbot.nix {}; 155 155 buildkite-agents = handleTest ./buildkite-agents.nix {}; 156 + c2fmzq = handleTest ./c2fmzq.nix {}; 156 157 caddy = handleTest ./caddy.nix {}; 157 158 cadvisor = handleTestOn ["x86_64-linux"] ./cadvisor.nix {}; 158 159 cage = handleTest ./cage.nix {};
+75
nixos/tests/c2fmzq.nix
··· 1 + import ./make-test-python.nix ({ pkgs, lib, ... }: { 2 + name = "c2FmZQ"; 3 + meta.maintainers = with lib.maintainers; [ hmenke ]; 4 + 5 + nodes.machine = { 6 + services.c2fmzq-server = { 7 + enable = true; 8 + port = 8080; 9 + passphraseFile = builtins.toFile "pwfile" "hunter2"; # don't do this on real deployments 10 + settings = { 11 + verbose = 3; # debug 12 + }; 13 + }; 14 + environment = { 15 + sessionVariables = { 16 + C2FMZQ_PASSPHRASE = "lol"; 17 + C2FMZQ_API_SERVER = "http://localhost:8080"; 18 + }; 19 + systemPackages = [ 20 + pkgs.c2fmzq 21 + (pkgs.writeScriptBin "c2FmZQ-client-wrapper" '' 22 + #!${pkgs.expect}/bin/expect -f 23 + spawn c2FmZQ-client {*}$argv 24 + expect { 25 + "Enter password:" { send "$env(PASSWORD)\r" } 26 + "Type YES to confirm:" { send "YES\r" } 27 + timeout { exit 1 } 28 + eof { exit 0 } 29 + } 30 + interact 31 + '') 32 + ]; 33 + }; 34 + }; 35 + 36 + testScript = { nodes, ... }: '' 37 + machine.start() 38 + machine.wait_for_unit("c2fmzq-server.service") 39 + machine.wait_for_open_port(8080) 40 + 41 + with subtest("Create accounts for alice and bob"): 42 + machine.succeed("PASSWORD=foobar c2FmZQ-client-wrapper -- -v 3 create-account alice@example.com") 43 + machine.succeed("PASSWORD=fizzbuzz c2FmZQ-client-wrapper -- -v 3 create-account bob@example.com") 44 + 45 + with subtest("Log in as alice"): 46 + machine.succeed("PASSWORD=foobar c2FmZQ-client-wrapper -- -v 3 login alice@example.com") 47 + msg = machine.succeed("c2FmZQ-client -v 3 status") 48 + assert "Logged in as alice@example.com" in msg, f"ERROR: Not logged in as alice:\n{msg}" 49 + 50 + with subtest("Create a new album, upload a file, and delete the uploaded file"): 51 + machine.succeed("c2FmZQ-client -v 3 create-album 'Rarest Memes'") 52 + machine.succeed("echo 'pls do not steal' > meme.txt") 53 + machine.succeed("c2FmZQ-client -v 3 import meme.txt 'Rarest Memes'") 54 + machine.succeed("c2FmZQ-client -v 3 sync") 55 + machine.succeed("rm meme.txt") 56 + 57 + with subtest("Share the album with bob"): 58 + machine.succeed("c2FmZQ-client-wrapper -- -v 3 share 'Rarest Memes' bob@example.com") 59 + 60 + with subtest("Log in as bob"): 61 + machine.succeed("PASSWORD=fizzbuzz c2FmZQ-client-wrapper -- -v 3 login bob@example.com") 62 + msg = machine.succeed("c2FmZQ-client -v 3 status") 63 + assert "Logged in as bob@example.com" in msg, f"ERROR: Not logged in as bob:\n{msg}" 64 + 65 + with subtest("Download the shared file"): 66 + machine.succeed("c2FmZQ-client -v 3 download 'shared/Rarest Memes/meme.txt'") 67 + machine.succeed("c2FmZQ-client -v 3 export 'shared/Rarest Memes/meme.txt' .") 68 + msg = machine.succeed("cat meme.txt") 69 + assert "pls do not steal\n" == msg, f"File content is not the same:\n{msg}" 70 + 71 + with subtest("Test that PWA is served"): 72 + msg = machine.succeed("curl -sSfL http://localhost:8080") 73 + assert "c2FmZQ" in msg, f"Could not find 'c2FmZQ' in the output:\n{msg}" 74 + ''; 75 + })
+36
pkgs/by-name/c2/c2fmzq/package.nix
··· 1 + { lib 2 + , buildGoModule 3 + , fetchFromGitHub 4 + , nixosTests 5 + }: 6 + 7 + buildGoModule rec { 8 + pname = "c2FmZQ"; 9 + version = "0.4.8"; 10 + 11 + src = fetchFromGitHub { 12 + owner = "c2FmZQ"; 13 + repo = "c2FmZQ"; 14 + rev = "v${version}"; 15 + hash = "sha256-IYSmGzjTDMBgEMVZsi6CuUz6L7BzpmbrJYVPUhFr7rw="; 16 + }; 17 + 18 + ldflags = [ "-s" "-w" ]; 19 + 20 + sourceRoot = "source/c2FmZQ"; 21 + 22 + vendorHash = "sha256-Hz6P+ptn1i+8Ek3pp8j+iB8NN5Xks50jyZuT8Ullxbo="; 23 + 24 + subPackages = [ "c2FmZQ-client" "c2FmZQ-server" ]; 25 + 26 + passthru.tests = { inherit (nixosTests) c2fmzq; }; 27 + 28 + meta = with lib; { 29 + description = "Securely encrypt, store, and share files, including but not limited to pictures and videos"; 30 + homepage = "https://github.com/c2FmZQ/c2FmZQ"; 31 + license = licenses.gpl3Only; 32 + mainProgram = "c2FmZQ-server"; 33 + maintainers = with maintainers; [ hmenke ]; 34 + platforms = platforms.linux; 35 + }; 36 + }