diff --git a/hosts/elisabeth/fs.nix b/hosts/elisabeth/fs.nix index 24ea2e7..3abaac0 100644 --- a/hosts/elisabeth/fs.nix +++ b/hosts/elisabeth/fs.nix @@ -123,6 +123,34 @@ }; }; + wireguard.scrtiny-patrick.server = { + host = config.secrets.secrets.global.domains.web; + port = 51831; + reservedAddresses = ["10.44.0.0/16" "fd00:1766::/112"]; + openFirewall = true; + }; + services.scrutiny = { + enable = true; + openFirewall = true; + collector = { + enable = true; + settings.host.id = "elisabeth"; + }; + }; + environment.persistence."/persist".directories = [ + { + directory = "/var/lib/influxdb2"; + mode = "0700"; + user = "influxdb2"; + } + ]; + environment.persistence."/state".directories = [ + { + directory = "/var/lib/private/scrutiny"; + mode = "0700"; + } + ]; + fileSystems."/state".neededForBoot = true; fileSystems."/persist".neededForBoot = true; boot.initrd.systemd.services."zfs-import-panzer".after = ["cryptsetup.target"]; diff --git a/hosts/elisabeth/guests.nix b/hosts/elisabeth/guests.nix index 61a7bfc..d826150 100644 --- a/hosts/elisabeth/guests.nix +++ b/hosts/elisabeth/guests.nix @@ -200,10 +200,10 @@ in { } ]) (blockOf "paperless" {maxBodySize = "5G";}) - #(blockOf "ttrss" {port = 80;}) + (blockOf "ttrss" {port = 80;}) (blockOf "yourspotify" {port = 80;}) (blockOf "apispotify" { - port = 80; + port = 3000; upstream = "yourspotify"; }) (blockOf "nextcloud" { @@ -308,7 +308,7 @@ in { // mkContainer "vaultwarden" {} // mkContainer "ddclient" {} // mkContainer "ollama" {} - #// mkContainer "ttrss" {} + // mkContainer "ttrss" {} // mkContainer "yourspotify" {} // mkContainer "netbird" {} // mkContainer "kanidm" {} diff --git a/hosts/elisabeth/secrets/netbird/generated/dhparams.pem.age b/hosts/elisabeth/secrets/netbird/generated/dhparams.pem.age deleted file mode 100644 index 2e98730..0000000 Binary files a/hosts/elisabeth/secrets/netbird/generated/dhparams.pem.age and /dev/null differ diff --git a/hosts/elisabeth/secrets/yourspotify/spotifyPublic.age b/hosts/elisabeth/secrets/yourspotify/spotifyPublic.age deleted file mode 100644 index adc3e17..0000000 Binary files a/hosts/elisabeth/secrets/yourspotify/spotifyPublic.age and /dev/null differ diff --git a/modules/config/users.nix b/modules/config/users.nix index fcf72ed..59f1628 100644 --- a/modules/config/users.nix +++ b/modules/config/users.nix @@ -32,6 +32,7 @@ authelia-main = uidGid 222; kanidm = uidGid 223; oauth2_proxy = uidGid 224; + influxdb2 = uidGid 225; paperless = uidGid 315; systemd-oom = uidGid 300; systemd-coredump = uidGid 301; diff --git a/modules/services/your_spotify_m.nix b/modules/services/your_spotify_m.nix index 9e97b50..3eb2ffe 100644 --- a/modules/services/your_spotify_m.nix +++ b/modules/services/your_spotify_m.nix @@ -17,6 +17,7 @@ mkPackageOption optionalAttrs types + mkDefault ; cfg = config.services.your_spotify; @@ -32,7 +33,7 @@ configFile = pkgs.writeText "your_spotify.env" (concatStrings (mapAttrsToList (name: value: "${name}=${value}\n") configEnv)); in { options.services.your_spotify = let - inherit (types) nullOr port str path bool package; + inherit (types) nullOr port str path package; in { enable = mkEnableOption "your_spotify"; @@ -51,35 +52,35 @@ in { clientPackage = mkOption { type = package; - default = cfg.package.client.override {apiEndpoint = cfg.settings.API_ENDPOINT;}; description = "Client package to use."; }; - spotifyPublicFile = mkOption { - type = path; - description = '' - The public key of your Spotify application - [Creating the Spotify Application](https://github.com/Yooooomi/your_spotify#creating-the-spotify-application) - ''; - }; spotifySecretFile = mkOption { type = path; description = '' - The secret key of your Spotify application - [Creating the Spotify Application](https://github.com/Yooooomi/your_spotify#creating-the-spotify-application) - Note that you may want to set this using the `environmentFile` config option to prevent - your secret from being world-readable in the nix store. + A file containing the secret key of your Spotify application. + Refer to: [Creating the Spotify Application](https://github.com/Yooooomi/your_spotify#creating-the-spotify-application). ''; }; settings = mkOption { + description = '' + Your Spotify Configuration. Refer to [Your Spotify](https://github.com/Yooooomi/your_spotify) for definitions and values. + ''; + example = lib.literalExpression '' + { + CLIENT_ENDPOINT = "https://example.com"; + API_ENDPOINT = "https://api.example.com"; + SPOTIFY_PUBLIC = "spotify_client_id"; + } + ''; type = types.submodule { freeformType = types.attrsOf types.str; options = { CLIENT_ENDPOINT = mkOption { type = str; description = '' - The endpoint of your web application + The endpoint of your web application. Has to include a protocol Prefix (e.g. `http://`) ''; example = "https://your_spotify.example.org"; @@ -93,28 +94,18 @@ in { internet. Has to include a protocol Prefix (e.g. `http://`) ''; - default = "https://localhost:3000"; + example = "https://localhost:3000"; }; - CORS = mkOption { - type = nullOr str; - description = '' - List of comma-separated origin allowed, or nothing to allow any origin - ''; - default = null; - }; - MAX_IMPORT_CACHESIZE = mkOption { + SPOTIFY_PUBLIC = mkOption { type = str; description = '' - The maximum element in the cache when importing data from an outside source, - more cache means less requests to Spotify, resulting in faster imports + The public client ID of your Spotify application. + Refer to: [Creating the Spotify Application](https://github.com/Yooooomi/your_spotify#creating-the-spotify-application) ''; - default = "Infinite"; }; MONGO_ENDPOINT = mkOption { type = str; - description = '' - The endpoint of the Mongo database. - ''; + description = ''The endpoint of the Mongo database.''; default = "mongodb://localhost:27017/your_spotify"; }; PORT = mkOption { @@ -122,47 +113,19 @@ in { description = "The port of the api server"; default = 3000; }; - TIMEZONE = mkOption { - type = str; - description = '' - The timezone of your stats, only affects read requests since data is saved with UTC time - ''; - default = "Europe/Paris"; - }; - LOG_LEVEL = mkOption { - type = str; - description = '' - The log level, debug is useful if you encouter any bugs - ''; - default = "info"; - }; - COOKIE_VALIDITY_MS = mkOption { - type = str; - description = '' - Validity time of the authentication cookie - ''; - default = "1h"; - }; - MONGO_NO_ADMIN_RIGHTS = mkOption { - type = bool; - description = '' - Do not ask for admin right on the Mongo database - ''; - default = false; - }; }; }; }; }; config = mkIf cfg.enable { + services.your_spotify.clientPackage = mkDefault (cfg.package.client.override {apiEndpoint = cfg.settings.API_ENDPOINT;}); systemd.services.your_spotify = { after = ["network.target"]; script = '' - export SPOTIFY_PUBLIC=$(< "$CREDENTIALS_DIRECTORY/SPOTIFY_PUBLIC") export SPOTIFY_SECRET=$(< "$CREDENTIALS_DIRECTORY/SPOTIFY_SECRET") - ${pkgs.your_spotify}/bin/your_spotify_migrate - exec ${pkgs.your_spotify}/bin/your_spotify_server + ${lib.getExe' cfg.package "your_spotify_migrate"} + exec ${lib.getExe cfg.package} ''; serviceConfig = { User = "your_spotify"; @@ -176,26 +139,23 @@ in { StateDirectoryMode = "0700"; Restart = "always"; - LoadCredential = ["SPOTIFY_PUBLIC:${cfg.spotifyPublicFile}" "SPOTIFY_SECRET:${cfg.spotifySecretFile}"]; + LoadCredential = ["SPOTIFY_SECRET:${cfg.spotifySecretFile}"]; # Hardening CapabilityBoundingSet = ""; LockPersonality = true; - #MemoryDenyWriteExecute = true; - #NoNewPrivileges = true; # Implied by DynamicUser + #MemoryDenyWriteExecute = true; # Leads to coredump because V8 does JIT PrivateUsers = true; - #PrivateTmp = true; # Implied by DynamicUser ProtectClock = true; ProtectControlGroups = true; ProtectHome = true; - ProtectHostname = false; # breaks bwrap - ProtectKernelLogs = false; # breaks bwrap + ProtectHostname = true; + ProtectKernelLogs = true; ProtectKernelModules = true; - ProtectKernelTunables = false; # breaks bwrap + ProtectKernelTunables = true; ProtectProc = "invisible"; - ProcSubset = "all"; # Using "pid" breaks bwrap + ProcSubset = "pid"; ProtectSystem = "strict"; - #RemoveIPC = true; # Implied by DynamicUser RestrictAddressFamilies = [ "AF_INET" "AF_INET6" @@ -203,7 +163,6 @@ in { ]; RestrictNamespaces = true; RestrictRealtime = true; - #RestrictSUIDSGID = true; # Implied by DynamicUser SystemCallArchitectures = "native"; SystemCallFilter = [ "@system-service" @@ -218,6 +177,8 @@ in { virtualHosts.${cfg.nginxVirtualHost} = { root = cfg.clientPackage; locations."/".extraConfig = '' + add_header Content-Security-Policy "frame-ancestors 'none';" ; + add_header X-Content-Type-Options "nosniff" ; try_files = $uri $uri/ /index.html ; ''; }; @@ -226,4 +187,5 @@ in { enable = true; }; }; + meta.maintainers = with lib.maintainers; [patrickdag]; } diff --git a/modules/services/yourspotify.nix b/modules/services/yourspotify.nix index 707818f..dea2c77 100644 --- a/modules/services/yourspotify.nix +++ b/modules/services/yourspotify.nix @@ -13,19 +13,14 @@ mode = "440"; rekeyFile = config.node.secretsDir + "/spotifySecret.age"; }; - age.secrets.spotifyPublic = { - owner = "root"; - mode = "440"; - rekeyFile = config.node.secretsDir + "/spotifyPublic.age"; - }; services.your_spotify = { enable = true; spotifySecretFile = config.age.secrets.spotifySecret.path; - spotifyPublicFile = config.age.secrets.spotifyPublic.path; settings = { + SPOTIFY_PUBLIC = "5397a3f2a75949459da343a5e7851bd9"; CLIENT_ENDPOINT = "https://sptfy.${config.secrets.secrets.global.domains.web}"; API_ENDPOINT = "https://apisptfy.${config.secrets.secrets.global.domains.web}"; - MONGO_NO_ADMIN_RIGHTS = false; + MONGO_NO_ADMIN_RIGHTS = "false"; }; enableLocalDB = true; nginxVirtualHost = "sptfy.${config.secrets.secrets.global.domains.web}"; diff --git a/pkgs/your_spotify.nix b/pkgs/your_spotify.nix index 6bacf5a..b02da18 100644 --- a/pkgs/your_spotify.nix +++ b/pkgs/your_spotify.nix @@ -1,29 +1,30 @@ { - mkYarnPackage, + callPackage, fetchFromGitHub, fetchYarnDeps, - makeWrapper, - nodejs, - yarn, - prefetch-yarn-deps, lib, - callPackage, + makeWrapper, + mkYarnPackage, + nodejs, + prefetch-yarn-deps, + yarn, }: let - version = "1.8.0"; + version = "1.10.1"; src = fetchFromGitHub { owner = "Yooooomi"; repo = "your_spotify"; rev = "refs/tags/${version}"; - hash = "sha256-umm7J5ADY2fl+tjs6Qeda5MX2P55u0eCqwW+DWLK8Kc="; + hash = "sha256-e82j2blGxQLWAlBNuAnFvlD9vwMk4/mRI0Vf7vuaPA0="; }; client = callPackage ./your_spotify_client.nix {inherit src version;}; in mkYarnPackage rec { inherit version src; - pname = "your_spotify"; + pname = "your_spotify_server"; + name = "your_spotify_server-${version}"; offlineCache = fetchYarnDeps { yarnLock = src + "/yarn.lock"; - hash = "sha256-pj6owoEPx9gdtFvXF8E89A+Thhe/7m0+OJU6Ttc6ooA="; + hash = "sha256-5SgknaRVzgO2Dzc8MhAaM8UERWMv+PrItzevoWHbWnA="; }; configurePhase = '' @@ -32,33 +33,47 @@ in export HOME=$(mktemp -d) yarn config --offline set yarn-offline-mirror $offlineCache fixup-yarn-lock yarn.lock - yarn install --offline --frozen-lockfile --ignore-platform --ignore-scripts --no-progress --non-interactive - patchShebangs node_modules/ - runHook postConfigure ''; buildPhase = '' runHook preBuild - ls -lah + + yarn install --offline --frozen-lockfile --ignore-platform --ignore-scripts --no-progress --non-interactive + patchShebangs node_modules/ + pushd ./apps/server/ yarn --offline run build popd + + rm -r node_modules + export NODE_ENV="production" + yarn install --offline --frozen-lockfile --ignore-platform --ignore-scripts --no-progress --non-interactive + patchShebangs node_modules/ + runHook postBuild ''; nativeBuildInputs = [makeWrapper yarn prefetch-yarn-deps]; installPhase = '' - mkdir -p $out - cp -r node_modules $out/node_modules + mkdir -p $out/share/your_spotify + cp -r node_modules $out/share/your_spotify/node_modules cp -r ./apps/server/{lib,package.json} $out mkdir -p $out/bin makeWrapper ${lib.escapeShellArg (lib.getExe nodejs)} "$out/bin/your_spotify_migrate" \ - --add-flags "$out/lib/migrations.js" + --add-flags "$out/lib/migrations.js" --set NODE_PATH "$out/share/your_spotify/node_modules" makeWrapper ${lib.escapeShellArg (lib.getExe nodejs)} "$out/bin/your_spotify_server" \ - --add-flags "$out/lib/index.js" + --add-flags "$out/lib/index.js" --set NODE_PATH "$out/share/your_spotify/node_modules" ''; doDist = false; passthru = { inherit client; }; + meta = with lib; { + homepage = "https://github.com/Yooooomi/your_spotify"; + changelog = "https://github.com/Yooooomi/your_spotify/releases/tag/${version}"; + description = "Self-hosted application that tracks what you listen and offers you a dashboard to explore statistics about it"; + license = licenses.gpl3Only; + maintainers = with maintainers; [patrickdag]; + mainProgram = "your_spotify_server"; + }; } diff --git a/pkgs/your_spotify_client.nix b/pkgs/your_spotify_client.nix index 05fbcf6..e764023 100644 --- a/pkgs/your_spotify_client.nix +++ b/pkgs/your_spotify_client.nix @@ -1,19 +1,20 @@ { - mkYarnPackage, - makeWrapper, + apiEndpoint ? "http://localhost:3000", fetchYarnDeps, - apiEndpoint ? "localhost:8080", + your_spotify, + mkYarnPackage, + prefetch-yarn-deps, src, version, yarn, - prefetch-yarn-deps, }: mkYarnPackage rec { inherit version src; pname = "your_spotify_client"; + name = "your_spotify_client-${version}"; offlineCache = fetchYarnDeps { yarnLock = src + "/yarn.lock"; - hash = "sha256-pj6owoEPx9gdtFvXF8E89A+Thhe/7m0+OJU6Ttc6ooA="; + hash = "sha256-5SgknaRVzgO2Dzc8MhAaM8UERWMv+PrItzevoWHbWnA="; }; configurePhase = '' runHook preConfigure @@ -29,18 +30,26 @@ mkYarnPackage rec { buildPhase = '' runHook preBuild pushd ./apps/client/ - pwd yarn --offline run build + export API_ENDPOINT="${apiEndpoint}" + substituteInPlace scripts/run/variables.sh --replace-quiet '/app/apps/client/' "./" + + chmod +x ./scripts/run/variables.sh + patchShebangs --build ./scripts/run/variables.sh + + ./scripts/run/variables.sh + popd runHook postBuild ''; - nativeBuildInputs = [makeWrapper yarn prefetch-yarn-deps]; + nativeBuildInputs = [yarn prefetch-yarn-deps]; installPhase = '' mkdir -p $out cp -r ./apps/client/build/* $out - substituteInPlace $out/variables-template.js --replace-quiet '__API_ENDPOINT__' "${apiEndpoint}" - mv $out/variables-template.js $out/variables.js ''; doDist = false; + meta = { + inherit (your_spotify.meta) homepage changelog description license maintainers; + }; } diff --git a/secrets/secrets.nix.age b/secrets/secrets.nix.age index 5330234..e48814c 100644 Binary files a/secrets/secrets.nix.age and b/secrets/secrets.nix.age differ diff --git a/secrets/wireguard/scrtiny-patrick/keys/elisabeth.age b/secrets/wireguard/scrtiny-patrick/keys/elisabeth.age new file mode 100644 index 0000000..f594cab --- /dev/null +++ b/secrets/wireguard/scrtiny-patrick/keys/elisabeth.age @@ -0,0 +1,16 @@ +age-encryption.org/v1 +-> X25519 dUgBIIGKuR7jCfDHrVWvku15MhegXbRaGVAJvQ4G+FE +G1B2zJrK5P8jW5fwboLcwnUw6sTQ+rolXsZdLkh1NYg +-> piv-p256 XTQkUA AqCwFM9PXH41lIClU3/8w7nB334kYtXwy29qqZvKD6W6 +2PvTP9Ru5i4W9N3N5BOre6OoctsVNXzMK3HS22VFgRE +-> piv-p256 ZFgiIw Ao/vA/FGcaNm3VonTzkZYM7rQZ3pYlXYcH6OHxIPxyBG +Jseiz60VbsygKZMZu1GpkoDsyoGwl23/+CFe6DL9aR4 +-> piv-p256 5vmPtQ A8TcW3GJHKfN+kRUH3KSYTi2Tcn7hAyN/PpsA5GqwD+d +O2D6CO0rp7SFqOz//p2gbl6HCPD55qNbnqRnwNUrSDo +-> piv-p256 ZFgiIw AtDrLdrYjUegvT1qi2X3X8CyqJU05Z48iAe01piNm5G3 +hrnn4uALqPzNsMOcdBFdqSmwV+L0r5sxSXWcTZz980I +-> k"-grease E[ZXL RpX[yO>J a3h4 a +ooUbsf5wnVPZ8Y0yjm8IrQMP+9oqQGIqcdi2fiGiKN2Q2gL8FGRcIRIEE0ZdpQIp +/06QStoFSQOgoI13SqpoHhg5cHSzWT4S2z0UFCpKsLZDZ+q1S7bxV1ST +--- zaXNrqNEe3bTj3oSGJljjj1ruZ5sBLBWRijLRr8Jxk4 +Z݅.PF3BC tO@T̔6 s:1wR}*%- l,lda,V \ No newline at end of file diff --git a/secrets/wireguard/scrtiny-patrick/keys/elisabeth.pub b/secrets/wireguard/scrtiny-patrick/keys/elisabeth.pub new file mode 100644 index 0000000..ca8ee00 --- /dev/null +++ b/secrets/wireguard/scrtiny-patrick/keys/elisabeth.pub @@ -0,0 +1 @@ +QPZrzkguRzNQDyMfnC6O5b49QZRCAsv28N91nR2lal0=