feat: add nix server provision
This commit is contained in:
158
modules/nixos/tailscale-init.nix
Normal file
158
modules/nixos/tailscale-init.nix
Normal file
@@ -0,0 +1,158 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.nodeiwest;
|
||||
tailscaleOpenbaoCfg = cfg.tailscale.openbao;
|
||||
in
|
||||
{
|
||||
options.nodeiwest.tailscale.openbao = {
|
||||
enable = lib.mkEnableOption "fetching the Tailscale auth key from OpenBao";
|
||||
|
||||
namespace = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "it";
|
||||
description = "OpenBao namespace used when fetching the Tailscale auth key.";
|
||||
};
|
||||
|
||||
authPath = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "auth/approle";
|
||||
description = "OpenBao auth mount path used by the AppRole login.";
|
||||
};
|
||||
|
||||
secretPath = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "tailscale";
|
||||
description = "OpenBao secret path containing the Tailscale auth key.";
|
||||
};
|
||||
|
||||
field = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "auth_key";
|
||||
description = "Field in the OpenBao secret that contains the Tailscale auth key.";
|
||||
};
|
||||
|
||||
renderedAuthKeyFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/run/nodeiwest/tailscale-auth-key";
|
||||
description = "Runtime file rendered by OpenBao Agent and consumed by Tailscale autoconnect.";
|
||||
};
|
||||
|
||||
approle = {
|
||||
roleIdFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/var/lib/nodeiwest/openbao-approle-role-id";
|
||||
description = "Root-only file containing the OpenBao AppRole role_id.";
|
||||
};
|
||||
|
||||
secretIdFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/var/lib/nodeiwest/openbao-approle-secret-id";
|
||||
description = "Root-only file containing the OpenBao AppRole secret_id.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /var/lib/nodeiwest 0700 root root - -"
|
||||
"d /run/nodeiwest 0700 root root - -"
|
||||
];
|
||||
|
||||
services.tailscale = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
extraUpFlags = lib.optionals tailscaleOpenbaoCfg.enable [ "--ssh" ];
|
||||
authKeyFile = if tailscaleOpenbaoCfg.enable then tailscaleOpenbaoCfg.renderedAuthKeyFile else null;
|
||||
};
|
||||
|
||||
services.vault-agent.instances.tailscale = lib.mkIf tailscaleOpenbaoCfg.enable {
|
||||
package = pkgs.openbao;
|
||||
settings = {
|
||||
vault.address = cfg.openbao.address;
|
||||
auto_auth = {
|
||||
method = [
|
||||
{
|
||||
type = "approle";
|
||||
mount_path = tailscaleOpenbaoCfg.authPath;
|
||||
namespace = tailscaleOpenbaoCfg.namespace;
|
||||
config = {
|
||||
role_id_file_path = tailscaleOpenbaoCfg.approle.roleIdFile;
|
||||
secret_id_file_path = tailscaleOpenbaoCfg.approle.secretIdFile;
|
||||
remove_secret_id_file_after_reading = false;
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
template = [
|
||||
{
|
||||
contents = ''{{- with secret "${tailscaleOpenbaoCfg.secretPath}" -}}{{- if .Data.data -}}{{ index .Data.data "${tailscaleOpenbaoCfg.field}" }}{{- else -}}{{ index .Data "${tailscaleOpenbaoCfg.field}" }}{{- end -}}{{- end -}}'';
|
||||
destination = tailscaleOpenbaoCfg.renderedAuthKeyFile;
|
||||
perms = "0400";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.vault-agent-tailscale = lib.mkIf tailscaleOpenbaoCfg.enable {
|
||||
wants = [ "network-online.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
serviceConfig.Environment = [ "BAO_NAMESPACE=${tailscaleOpenbaoCfg.namespace}" ];
|
||||
};
|
||||
|
||||
systemd.services.nodeiwest-tailscale-authkey-ready = lib.mkIf tailscaleOpenbaoCfg.enable {
|
||||
description = "Wait for the Tailscale auth key rendered by OpenBao Agent";
|
||||
after = [ "vault-agent-tailscale.service" ];
|
||||
requires = [ "vault-agent-tailscale.service" ];
|
||||
before = [ "tailscaled-autoconnect.service" ];
|
||||
requiredBy = [ "tailscaled-autoconnect.service" ];
|
||||
path = [ pkgs.coreutils ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
};
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
|
||||
for _ in $(seq 1 60); do
|
||||
if [ -s ${lib.escapeShellArg tailscaleOpenbaoCfg.renderedAuthKeyFile} ]; then
|
||||
exit 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "Timed out waiting for rendered Tailscale auth key at ${tailscaleOpenbaoCfg.renderedAuthKeyFile}" >&2
|
||||
exit 1
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.services.tailscaled-autoconnect = lib.mkIf tailscaleOpenbaoCfg.enable {
|
||||
after = [
|
||||
"vault-agent-tailscale.service"
|
||||
"nodeiwest-tailscale-authkey-ready.service"
|
||||
];
|
||||
requires = [
|
||||
"vault-agent-tailscale.service"
|
||||
"nodeiwest-tailscale-authkey-ready.service"
|
||||
];
|
||||
serviceConfig.ExecStartPre = [
|
||||
"${lib.getExe' pkgs.coreutils "test"} -s ${tailscaleOpenbaoCfg.renderedAuthKeyFile}"
|
||||
];
|
||||
};
|
||||
|
||||
assertions = [
|
||||
{
|
||||
assertion =
|
||||
(!tailscaleOpenbaoCfg.enable)
|
||||
|| (
|
||||
tailscaleOpenbaoCfg.approle.roleIdFile != ""
|
||||
&& tailscaleOpenbaoCfg.approle.secretIdFile != ""
|
||||
);
|
||||
message = "AppRole roleIdFile and secretIdFile must be set when OpenBao-backed Tailscale enrollment is enabled.";
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user