feat: move things around

This commit is contained in:
eric
2026-03-18 17:41:10 +01:00
parent f558ab4ba9
commit 1de34c1869
22 changed files with 54 additions and 2692 deletions

View File

@@ -1,10 +1,7 @@
{ pkgs, ... }:
let
nodeiwestHelper = pkgs.callPackage ../../pkgs/helpers { };
in
{ pkgs, deployment, ... }:
{
home.packages = [
pkgs.python3
nodeiwestHelper
deployment.packages.${pkgs.system}.nodeiwest-helper
];
}

View File

@@ -14,5 +14,6 @@
openbao
colmena
# etc.
sops
];
}

View File

@@ -1,101 +0,0 @@
{
config,
lib,
self,
...
}:
let
cfg = config.nodeiwest;
trustedUserCAKeysPath = "/etc/ssh/trusted-user-ca-keys.pem";
in
{
imports = [ ./tailscale-init.nix ];
options.nodeiwest = {
openbao.address = lib.mkOption {
type = lib.types.str;
default = "https://secrets.api.nodeiwest.se";
description = "Remote OpenBao address that hosts should use as clients.";
example = "https://secrets.api.nodeiwest.se";
};
homeManagerUsers = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [
"root"
"deploy"
];
description = "Users that should receive the shared Home Manager company profile.";
example = [
"root"
"deploy"
];
};
ssh.userCAPublicKeys = lib.mkOption {
type = lib.types.listOf lib.types.singleLineStr;
default = [ ];
description = "OpenBao SSH user CA public keys trusted by sshd for user certificate authentication.";
example = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBExampleOpenBaoUserCA openbao-user-ca"
];
};
};
config = {
networking.firewall.allowedTCPPorts = [
22
80
443
];
services.openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
PubkeyAuthentication = true;
PermitRootLogin = "prohibit-password";
}
// lib.optionalAttrs (cfg.ssh.userCAPublicKeys != [ ]) {
TrustedUserCAKeys = trustedUserCAKeysPath;
};
};
users.groups.deploy = { };
users.users.deploy = {
isNormalUser = true;
group = "deploy";
createHome = true;
extraGroups = [ "wheel" ];
};
services.traefik = {
enable = true;
staticConfigOptions = {
api.dashboard = true;
entryPoints.web.address = ":80";
entryPoints.websecure.address = ":443";
ping = { };
};
dynamicConfigOptions = lib.mkMerge [ ];
};
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
users = lib.genAttrs cfg.homeManagerUsers (_: {
imports = [ self.homeManagerModules.default ];
home.stateVersion = config.system.stateVersion;
});
};
environment.etc = lib.mkIf (cfg.ssh.userCAPublicKeys != [ ]) {
"ssh/trusted-user-ca-keys.pem".text = lib.concatStringsSep "\n" cfg.ssh.userCAPublicKeys + "\n";
};
environment.variables = {
BAO_ADDR = cfg.openbao.address;
};
};
}

View File

@@ -1,155 +0,0 @@
{
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 = "CLIENT_SECRET";
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.";
}
];
};
}