Commit ee5cc384 authored by Eric Wolf's avatar Eric Wolf Committed by Yt
Browse files

lemmy: Support secret options

This commit implements #101777 by merging
the config with an external file at startup.
parent b5eafe65
Loading
Loading
Loading
Loading
+24 −2
Original line number Diff line number Diff line
@@ -77,6 +77,11 @@ in
      };
    };

    secretFile = mkOption {
      type = with types; nullOr path;
      default = null;
      description = lib.mdDoc "Path to a secret JSON configuration file which is merged at runtime with the one generated from {option}`services.lemmy.settings`.";
    };
  };

  config =
@@ -197,11 +202,14 @@ in
        }
      ];

      systemd.services.lemmy = {
      systemd.services.lemmy = let
        configFile = settingsFormat.generate "config.hjson" cfg.settings;
        mergedConfig = "/run/lemmy/config.hjson";
      in {
        description = "Lemmy server";

        environment = {
          LEMMY_CONFIG_LOCATION = "${settingsFormat.generate "config.hjson" cfg.settings}";
          LEMMY_CONFIG_LOCATION = if cfg.secretFile == null then configFile else mergedConfig;
          LEMMY_DATABASE_URL = if cfg.database.uri != null then cfg.database.uri else (mkIf (cfg.database.createLocally) "postgres:///lemmy?host=/run/postgresql&user=lemmy");
        };

@@ -216,10 +224,24 @@ in

        requires = lib.optionals cfg.database.createLocally [ "postgresql.service" ];

        path = mkIf (cfg.secretFile != null) [ pkgs.jq ];

        # merge the two configs and prevent others from reading the result
        # if somehow $CREDENTIALS_DIRECTORY is not set we fail
        preStart = mkIf (cfg.secretFile != null) ''
          set -u
          umask 177
          jq --slurp '.[0] * .[1]' ${lib.escapeShellArg configFile} "$CREDENTIALS_DIRECTORY/secretFile" > ${lib.escapeShellArg mergedConfig}
        '';

        serviceConfig = {
          DynamicUser = true;
          RuntimeDirectory = "lemmy";
          ExecStart = "${cfg.server.package}/bin/lemmy_server";
          LoadCredential = mkIf (cfg.secretFile != null) "secretFile:${toString cfg.secretFile}";
          PrivateTmp = true;
          MemoryDenyWriteExecute = true;
          NoNewPrivileges = true;
        };
      };

+16 −2
Original line number Diff line number Diff line
@@ -22,16 +22,24 @@ in
          # Without setup, the /feeds/* and /nodeinfo/* API endpoints won't return 200
          setup = {
            admin_username = "mightyiam";
            admin_password = "ThisIsWhatIUseEverywhereTryIt";
            site_name = "Lemmy FTW";
            admin_email = "mightyiam@example.com";
          };
          # https://github.com/LemmyNet/lemmy/blob/50efb1d519c63a7007a07f11cc8a11487703c70d/crates/utils/src/settings/mod.rs#L52
          database.uri = "postgres:///lemmy?host=/run/postgresql&user=lemmy";
        };
        secretFile = /etc/lemmy-config.hjson;
        caddy.enable = true;
      };

      environment.etc."lemmy-config.hjson".text = ''
        {
          "setup": {
            "admin_password": "ThisIsWhatIUseEverywhereTryIt"
          }
        }
      '';

      networking.firewall.allowedTCPPorts = [ 80 ];

      # pict-rs seems to need more than 1025114112 bytes
@@ -42,8 +50,14 @@ in
  testScript = ''
    server = ${lemmyNodeName}

    with subtest("the backend starts and responds"):
    with subtest("the merged config is secure"):
        server.wait_for_unit("lemmy.service")
        config_permissions = server.succeed("stat --format %A /run/lemmy/config.hjson").rstrip()
        assert config_permissions == "-rw-------", f"merged config permissions {config_permissions} are insecure"
        directory_permissions = server.succeed("stat --format %A /run/lemmy").rstrip()
        assert directory_permissions[5] == directory_permissions[8] == "-", "merged config can be replaced"

    with subtest("the backend starts and responds"):
        server.wait_for_open_port(${toString backendPort})
        server.succeed("curl --fail localhost:${toString backendPort}/api/v3/site")