This repository has been archived on 2026-09-17. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
nixos-config/hosts/pinix/localServices/dex.nix
pijukka b633fe5b4d host(pinix): commit the Dex + Headscale + LLDAP identity stack
Codex bootstrapped this as an uncommitted, parallel identity/control
plane under domain2 (lukasrosier.com) alongside the router migration;
committing it now that it's been exercised together this session:

- LLDAP admin login verified against a freshly rotated, pinix-only
  admin secret (pinixLldapAdmin.age, distinct from the lldapAdmin secret
  shared with blunix -- rotating one no longer risks the other).
- Dex OIDC discovery and the LDAP connector work end-to-end through
  Traefik (dex.lukasrosier.com, head.lukasrosier.com).
- Headscale's OIDC `allowed_groups` gate is intentionally not set yet:
  any account in this still-empty LLDAP directory may register a node
  for now. Re-add the "intern" gate once real accounts exist and the
  bootstrap is done.
- tnix successfully joined headscale-pinix via a pre-auth key as a
  second, parallel tailscale identity (not yet made declarative).

Not committed: tailnet-dns.nix (a separate, more speculative netns'd
resolver experiment for this tailnet, untouched today) and its
default.nix import.

This remains a parallel control plane during bootstrap -- see
docs/pinix-identity-stack.md. tailscale.nix on tnix/pinix, the existing
blunix-controlled tailnet, and 0ff.space DNS are unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C5FvfdKcQjEFQ4aLrepcS1
2026-09-13 16:38:02 +02:00

103 lines
3.3 KiB
Nix

{ config, pkgs, ... }:
{
# Keep the LDAP bind password out of Dex's generated configuration. The
# Dex LDAP connector expands $LDAP_BIND_PW after parsing the YAML, so this
# covers the connector without putting the secret in the store.
systemd.services.dex-ldap-env = {
description = "Render the pinix Dex LDAP bind password into a runtime env file";
before = [ "dex.service" ];
requiredBy = [ "dex.service" ];
after = [ "lldap.service" ];
wants = [ "lldap.service" ];
restartTriggers = [ config.age.secrets.pinixLldapAdmin.file ];
path = [ pkgs.coreutils pkgs.gnused ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
RuntimeDirectory = "dex-ldap-env";
RuntimeDirectoryMode = "0700";
ExecStart = "${pkgs.writeShellScript "pinix-dex-ldap-env" (builtins.readFile ../../../scripts/dex-ldap-env.sh)} ${config.age.secrets.pinixLldapAdmin.path} /run/dex-ldap-env/bindpw.env";
};
};
services.dex = {
enable = true;
environmentFile = "/run/dex-ldap-env/bindpw.env";
settings = {
logger.level = "info";
issuer = "https://dex.${config.domain2}/dex";
# Unlike the old blunix module, keep Dex's signing keys and sessions
# across a restart. StateDirectory is created for the DynamicUser by
# systemd and remains outside the Nix store.
storage = {
type = "sqlite3";
config.file = "/var/lib/dex/dex.db";
};
web.http = "127.0.0.1:5556";
# This is a separate control plane during bootstrap. Existing clients
# continue using blunix until the new LLDAP account/group and a new
# Headscale pre-auth key have been created manually.
staticClients = [
{
id = "headscale";
name = "headscale";
redirectURIs = [ "https://head.${config.domain2}/oidc/callback" ];
secretFile = config.age.secrets.oidcHeadscale.path;
}
];
connectors = [
{
type = "ldap";
id = "ldap";
name = "LDAP";
config = {
host = "127.0.0.1:3890";
insecureNoSSL = true;
startTLS = false;
bindDN = "uid=admin,ou=people,${config.ldapBaseDN2}";
bindPW = "$LDAP_BIND_PW";
userSearch = {
baseDN = "ou=people,${config.ldapBaseDN2}";
username = "uid";
idAttr = "uid";
emailAttr = "mail";
nameAttr = "displayName";
preferredUsernameAttr = "uid";
};
groupSearch = {
baseDN = "ou=groups,${config.ldapBaseDN2}";
filter = "(objectClass=groupOfUniqueNames)";
userMatchers = [
{
userAttr = "DN";
groupAttr = "member";
}
];
nameAttr = "cn";
};
};
}
];
};
};
systemd.services.dex = {
after = [ "lldap.service" ];
wants = [ "lldap.service" ];
serviceConfig = {
StateDirectory = "dex";
StateDirectoryMode = "0700";
Restart = "on-failure";
RestartSec = "5s";
};
restartTriggers = [
config.age.secrets.pinixLldapAdmin.file
config.age.secrets.oidcHeadscale.file
];
};
}