Unverified Commit aaf817bb authored by Pol Dellaiera's avatar Pol Dellaiera Committed by GitHub
Browse files

nixos/glance: allow specifying secret settings (#395551)

parents b4667dba 27d160b6
Loading
Loading
Loading
Loading
+56 −7
Original line number Diff line number Diff line
@@ -8,15 +8,27 @@ let
  cfg = config.services.glance;

  inherit (lib)
    catAttrs
    concatMapStrings
    getExe
    mkEnableOption
    mkPackageOption
    mkOption
    mkIf
    getExe
    mkOption
    mkPackageOption
    types
    ;

  inherit (builtins)
    concatLists
    isAttrs
    isList
    attrNames
    getAttr
    ;

  settingsFormat = pkgs.formats.yaml { };
  settingsFile = settingsFormat.generate "glance.yaml" cfg.settings;
  mergedSettingsFile = "/run/glance/glance.yaml";
in
{
  options.services.glance = {
@@ -69,7 +81,9 @@ in
                      { type = "calendar"; }
                      {
                        type = "weather";
                        location = "Nivelles, Belgium";
                        location = {
                          _secret = "/var/lib/secrets/glance/location";
                        };
                      }
                    ];
                  }
@@ -84,6 +98,13 @@ in
        Configuration written to a yaml file that is read by glance. See
        <https://github.com/glanceapp/glance/blob/main/docs/configuration.md>
        for more.

        Settings containing secret data should be set to an
        attribute set containing the attribute
        <literal>_secret</literal> - a string pointing to a file
        containing the value the option should be set to. See the
        example in `services.glance.settings.pages` at the weather widget
        with a location secret to get a better picture of this.
      '';
    };

@@ -102,13 +123,41 @@ in
      description = "Glance feed dashboard server";
      wantedBy = [ "multi-user.target" ];
      after = [ "network.target" ];
      path = [ pkgs.replace-secret ];

      serviceConfig = {
        ExecStart =
        ExecStartPre =
          let
            glance-yaml = settingsFormat.generate "glance.yaml" cfg.settings;
            findSecrets =
              data:
              if isAttrs data then
                if data ? _secret then
                  [ data ]
                else
                  concatLists (map (attr: findSecrets (getAttr attr data)) (attrNames data))
              else if isList data then
                concatLists (map findSecrets data)
              else
                [ ];
            secretPaths = catAttrs "_secret" (findSecrets cfg.settings);
            mkSecretReplacement = secretPath: ''
              replace-secret ${
                lib.escapeShellArgs [
                  "_secret: ${secretPath}"
                  secretPath
                  mergedSettingsFile
                ]
              }
            '';
            secretReplacements = concatMapStrings mkSecretReplacement secretPaths;
          in
          "${getExe cfg.package} --config ${glance-yaml}";
          # Use "+" to run as root because the secrets may not be accessible to glance
          "+"
          + pkgs.writeShellScript "glance-start-pre" ''
            install -m 600 -o $USER ${settingsFile} ${mergedSettingsFile}
            ${secretReplacements}
          '';
        ExecStart = "${getExe cfg.package} --config ${mergedSettingsFile}";
        WorkingDirectory = "/var/lib/glance";
        StateDirectory = "glance";
        RuntimeDirectory = "glance";
+42 −6
Original line number Diff line number Diff line
@@ -5,19 +5,47 @@

  nodes = {
    machine_default =
      { pkgs, ... }:
      { ... }:
      {
        services.glance = {
          enable = true;
        };
      };

    machine_custom_port =
    machine_configured =
      { pkgs, ... }:
      let
        # Do not use this in production. This will make the secret world-readable
        # in the Nix store
        secrets.glance-location.path = builtins.toString (
          pkgs.writeText "location-secret" "Nivelles, Belgium"
        );
      in
      {
        services.glance = {
          enable = true;
          settings.server.port = 5678;
          settings = {
            server.port = 5678;
            pages = [
              {
                name = "Home";
                columns = [
                  {
                    size = "full";
                    widgets = [
                      { type = "calendar"; }
                      {
                        type = "weather";
                        location = {
                          _secret = secrets.glance-location.path;
                        };
                      }
                    ];
                  }
                ];
              }
            ];
          };
        };
      };
  };
@@ -25,23 +53,31 @@
  extraPythonPackages =
    p: with p; [
      beautifulsoup4
      pyyaml
      types-pyyaml
      types-beautifulsoup4
    ];

  testScript = ''
    from bs4 import BeautifulSoup
    import yaml

    machine_default.start()
    machine_default.wait_for_unit("glance.service")
    machine_default.wait_for_open_port(8080)

    machine_custom_port.start()
    machine_custom_port.wait_for_unit("glance.service")
    machine_custom_port.wait_for_open_port(5678)
    machine_configured.start()
    machine_configured.wait_for_unit("glance.service")
    machine_configured.wait_for_open_port(5678)

    soup = BeautifulSoup(machine_default.succeed("curl http://localhost:8080"))
    expected_version = "v${config.nodes.machine_default.services.glance.package.version}"
    assert any(a.text == expected_version for a in soup.select(".footer a"))

    yaml_contents = machine_configured.succeed("cat /run/glance/glance.yaml")
    yaml_parsed = yaml.load(yaml_contents, Loader=yaml.FullLoader)
    location = yaml_parsed["pages"][0]["columns"][0]["widgets"][1]["location"]
    assert location == "Nivelles, Belgium"
  '';

  meta.maintainers = [ lib.maintainers.drupol ];