headplane/nix/module.nix
Igor Ramazanov 03a7e51384
feat: add a nix flake (#132)
* feat: wip `nix`

Add initial code to be used when working with `nix` and `NixOS`.

* a Nix flake
* building a package
* a NixOS module

* feat: build `hp_agent` with `nix`

* feat: add `hp_agent` as a flake output

* feat: nix: start `headplane` after `headscale`

* feat: do not rely on `git` for versioning

It causes lots of pain when building with `nix` for a local `flake.nix`.

Not sure if it's a good general solution:
* now it requires a manual step of updating `./version` on each release.
* we're losing commit hash abbreviation, like `0.5.3-5-gbe5a291`

I guess, this can be fixed by installing git-pre-commit-hook, but even
then it'd be wrong, because the output of `git describe --tags --always`
won't be the same before and after commit.

* feat: include `hp_agent` to the NixOS module

* fix: version error message

* fix: use relative path imports in `nix`

* fix: NixOS module: generate `/etc/headplane/config.yaml` from `services.headplane.settings`

* fix: NixOS module: allow passing `bool` in `services.headplane.settings.*`

* fix: NixOS module: fix `/etc/headplane/config.yaml` generation

* docs: add Nix/NixOS docs

* feat: nix: read version from `package.json`

* fix: nix: fix `agent` env vars configuration

* feat: nix: add `services.headplane.agent.debug` option

* fix: delete unnecessary `version` file

* fix: nix: remove unnecessary `sed` substitutions

A left over from previous versions.
See: https://github.com/tale/headplane/issues/95#issue-2807487849

* feat: nix: do not hardcode `headplane-agent` configuration environment variables

To make the module more flexible and to reduce the dependency on the
config API.

* docs: improve `Nix` documentation

* Reflect recent changes.
* Link `Nix` in README

* feat: nix: setup Nix CI

* feat: nix: CI without depending on flakehub.com

* chore: begin bundling all deps into the server

* fix: loosen headscale config validation

* fix: navigate back to machines page on node deletion

* fix: slice off trailing slash if present on headscale url

* feat: switch to a central singleton handler

This also adds support for Headscale TLS installations

* fix: shamefully-hoist dependencies

* fix: handle localized number fields

* fix: revert dependency bundling for now

* chore: cleanup and remove from readme for now

---------

Co-authored-by: Aarnav Tale <aarnavtale@icloud.com>
2025-03-18 00:31:37 -04:00

113 lines
2.9 KiB
Nix

{
config,
lib,
pkgs,
...
}: let
inherit
(lib)
attrsToList
listToAttrs
map
mkEnableOption
mkIf
mkOption
mkPackageOption
typeOf
types
;
cfg = config.services.headplane;
settingsFormat = pkgs.formats.yaml {};
settingsFile = settingsFormat.generate "headplane-config.yaml" cfg.settings;
agentEnv = listToAttrs (map (n: {
name = n.name;
value =
if ((typeOf n.value) == "bool")
then
(
if (n.value)
then "true"
else "false"
)
else n.value;
}) (attrsToList cfg.agent.settings));
in {
options.services.headplane = {
enable = mkEnableOption "headplane";
package = mkPackageOption pkgs "headplane" {};
settings = mkOption {
type = types.submodule {
freeformType = settingsFormat.type;
};
default = {};
description = "Headplane config, generates a YAML config. See: https://github.com/tale/headplane/blob/main/config.example.yaml.";
};
agent = mkOption {
type = types.submodule {
options = {
enable = mkEnableOption "headplane-agent";
package = mkPackageOption pkgs "headplane-agent" {};
settings = mkOption {
type = types.attrsOf [types.str types.bool];
description = "Headplane agent env vars config. See: https://github.com/tale/headplane/blob/main/docs/Headplane-Agent.md";
default = {};
};
};
};
};
};
config = mkIf cfg.enable {
environment = {
systemPackages = [cfg.package];
etc."headplane/config.yaml".source = "${settingsFile}";
};
systemd.services.headplane-agent =
mkIf cfg.agent.enable
{
description = "Headplane side-running agent";
wantedBy = ["multi-user.target"];
after = ["headplane.service"];
requires = ["headplane.service"];
environment = agentEnv;
serviceConfig = {
User = config.services.headscale.user;
Group = config.services.headscale.group;
ExecStart = "${pkgs.headplane-agent}/bin/hp_agent";
Restart = "always";
RestartSec = 5;
# TODO: Harden `systemd` security according to the "The Principle of Least Power".
# See: `$ systemd-analyze security headplane-agent`.
};
};
systemd.services.headplane = {
description = "Headscale Web UI";
wantedBy = ["multi-user.target"];
after = ["headscale.service"];
requires = ["headscale.service"];
serviceConfig = {
User = config.services.headscale.user;
Group = config.services.headscale.group;
ExecStart = "${pkgs.headplane}/bin/headplane";
Restart = "always";
RestartSec = 5;
# TODO: Harden `systemd` security according to the "The Principle of Least Power".
# See: `$ systemd-analyze security headplane`.
};
};
};
}