Author SHA1 Message Date
Renovate Bot 2c9eb36d57 chore(deps): lock file maintenance flake inputs
renovate/stability-days Updates have not met minimum release age requirement
CI / flake (pull_request) Failing after 24s
2026-07-02 15:03:01 +00:00
8 changed files with 30 additions and 353 deletions
-27
View File
@@ -52,33 +52,6 @@ The home config is also exposed for use beyond these hosts:
(`inputs.<this>.homeModules.default`). Consumers must supply the module args (`inputs.<this>.homeModules.default`). Consumers must supply the module args
these expect: `inputs` always, `identity` for git/desktop, `portable` for sway. these expect: `inputs` always, `identity` for git/desktop, `portable` for sway.
## Directory authentication (SSSD → Authentik LDAP)
Every NixOS host authenticates users against the Authentik LDAP outpost via
SSSD, implemented in [`modules/sssd.nix`](./modules/sssd.nix) and enabled by
default through the `services.authentikLdap.enable` option (added to
`baseModules`). The **EDaaS** WSL box opts out
(`services.authentikLdap.enable = false`) as a work-managed environment; the
macOS host is unaffected (SSSD is Linux-only).
- Connects over LDAPS to `ldap.lyrapup.pet:636`, search base
`dc=ldap,dc=goauthentik,dc=io`, binding as
`cn=sssd-bind,ou=users,dc=ldap,dc=goauthentik,dc=io`.
- The schema mappings match Authentik's non-standard object classes
(`goauthentik.io/ldap/user`, `goauthentik.io/ldap/group`) over the POSIX
attributes (`uid`, `uidNumber`, `gidNumber`, `homeDirectory`).
- Home directories are created on first login (`pam_mkhomedir`).
### Secrets (agenix)
The LDAP bind credential is an [agenix](https://github.com/ryantm/agenix)
secret, decrypted at activation with each host's SSH host key. The decrypted
plaintext is a full `sssd.conf` drop-in delivered to
`/etc/sssd/conf.d/01-ldap-authtok.conf`, so the password never enters the Nix
store. Owner setup (host recipient keys, encrypting the bind password, DNS for
`ldap.lyrapup.pet`, rebuild) is documented in
[`secrets/README.md`](./secrets/README.md).
## Applying ## Applying
```sh ```sh
Generated
+30 -52
View File
@@ -25,11 +25,11 @@
}, },
"locked": { "locked": {
"dir": "pkgs/firefox-addons", "dir": "pkgs/firefox-addons",
"lastModified": 1782014564, "lastModified": 1782964936,
"narHash": "sha256-F/royQHyJAyKWKrV8AaG4Yf1yjzxa+PFk5xvTdvBrzk=", "narHash": "sha256-wXEBDr7/dFQYhVpDwCKc9fkrYQQE4x0bdirX1bsLBGA=",
"owner": "rycee", "owner": "rycee",
"repo": "nur-expressions", "repo": "nur-expressions",
"rev": "d6668e34bbce788459883a1097bf0ee170f49c61", "rev": "64feee871e0373dd6121e412c3fb12e372d1bfb5",
"type": "gitlab" "type": "gitlab"
}, },
"original": { "original": {
@@ -93,11 +93,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1778716662, "lastModified": 1782949081,
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=", "narHash": "sha256-vp6Y/Grm98ESt6ceOkWiHWyZRDV3J1RID4w+6NWK9yA=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb", "rev": "17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -130,17 +130,16 @@
"git-hooks": { "git-hooks": {
"inputs": { "inputs": {
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
] ]
}, },
"locked": { "locked": {
"lastModified": 1781733627, "lastModified": 1782908218,
"narHash": "sha256-U3yTuGBnmXvXoQI3qkpfEDsn9RovQPAjN7ndRco+3u0=", "narHash": "sha256-wLMOrPgVyeF3XmP+qfYcLqnVdTxikdcSvbIY7rA9jTA=",
"owner": "cachix", "owner": "cachix",
"repo": "git-hooks.nix", "repo": "git-hooks.nix",
"rev": "3bbec39bc90eadfa031e6f3b77272f3f60803e39", "rev": "9f7e99119ece7705299595299f3b031f39356de1",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -149,27 +148,6 @@
"type": "github" "type": "github"
} }
}, },
"gitignore": {
"inputs": {
"nixpkgs": [
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"home-manager": { "home-manager": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@@ -177,11 +155,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1781981105, "lastModified": 1782704057,
"narHash": "sha256-/1nNBbA7PrSQpTc9Qazkhl4kIPg+TNl0CjxS3UQJKlw=", "narHash": "sha256-G1I1gd32F7mp9LAe1DaZ4ZL7NX5gyiKwdCMwro1Vrck=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "7bfff44b465909f69a442701293bc0badcf476dc", "rev": "868d0a692de703c2de98fab61968e4e310b7c28e",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -253,11 +231,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1782030356, "lastModified": 1782636943,
"narHash": "sha256-h4WpMr455AfRub0FXBaon6Vcpe0waUyJ4GivIW6oyd4=", "narHash": "sha256-ripjZa7BBLwL1uS5VJF3s/VpZpWt5ZIQEvkJ/FJNpQw=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nix-index-database", "repo": "nix-index-database",
"rev": "3017088b49efd404f78e3b104f553b97e4af786b", "rev": "058b1f9381fa79fcda49982370a750ff92dbba43",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -274,11 +252,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1781520503, "lastModified": 1782930066,
"narHash": "sha256-XuqQQG1qRyc3o8ld937sDLQNx+QrGV852KJ0dNglJDg=", "narHash": "sha256-DIidHEvUpyFYxS2/Fl6PCMCPumLaV1epqT51BbOdEAQ=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nixos-apple-silicon", "repo": "nixos-apple-silicon",
"rev": "43043ad207529650f9fa68e1705f7cf9c08bfdeb", "rev": "94cf7943ad3e6ec166858070d426ca9a71250f75",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -294,11 +272,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1781622756, "lastModified": 1782562157,
"narHash": "sha256-JrPh4M6S7aPsEE9tOENuZrxC6o2szSLlK+t4+nLke9s=", "narHash": "sha256-a7+T6QSeowynwZ1ZJJbP8T8ntAytvrui8kFGJmIZt2c=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixos-hardware", "repo": "nixos-hardware",
"rev": "08018c72174a4df5657f8d94178ac69fb9c243e5", "rev": "a9cf7546a938c737b079e738de73934a13de9784",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -330,11 +308,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1781216227, "lastModified": 1782847225,
"narHash": "sha256-9mUW6gNwoN2SWc/l0fW4svPNOulXLl8ijqKyeSOGgJE=", "narHash": "sha256-JC9PjqKYG9ve5U8aDOLQipp3+KLANBHUvGdLZlxzdKI=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "a0374025a863d007d98e3297f6aa46cc3141c2f0", "rev": "95ca1e203c0750115fd4a6f17d5a245dfe6b1edd",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -346,11 +324,11 @@
}, },
"nixpkgs-unstable": { "nixpkgs-unstable": {
"locked": { "locked": {
"lastModified": 1781577229, "lastModified": 1782723713,
"narHash": "sha256-lrp67w8AulE9Ks53n27I45ADSzbOCn4H+CNW1Ck8B+8=", "narHash": "sha256-oPXCU/SSUokcGaJREHibG1CBX3+s/W7orDWQOZDsEeQ=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "567a49d1913ce81ac6e9582e3553dd90a955875f", "rev": "b5aa0fbd538984f6e3d201be0005b4463d8b09f8",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -369,11 +347,11 @@
"systems": "systems" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1781971008, "lastModified": 1782919967,
"narHash": "sha256-T2u2RQZWKvD1J+TgcxjiJr8IymBr/PrUNeAGhMZFZU4=", "narHash": "sha256-pRwjfB5HQJ3m8J8bOR43pPHtHI7VUJSqwLA3P06cOY0=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nixvim", "repo": "nixvim",
"rev": "7afca458f064f166d3a9c98db3b41a984fe46492", "rev": "667c8471f4a0fb24d702d1a61af8609f1a5f1ba6",
"type": "github" "type": "github"
}, },
"original": { "original": {
-11
View File
@@ -46,15 +46,6 @@
url = "github:cachix/git-hooks.nix"; url = "github:cachix/git-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
# agenix: age-encrypted secrets, decrypted at activation with each host's
# SSH host key. Provides the SSSD LDAP bind credential (secrets/, see
# modules/sssd.nix). The darwin module is intentionally unused (SSSD is
# Linux-only).
agenix = {
url = "github:ryantm/agenix";
inputs.nixpkgs.follows = "nixpkgs";
inputs.home-manager.follows = "home-manager";
};
# Declarative Neovim (the editor; see home/editor.nix). Release # Declarative Neovim (the editor; see home/editor.nix). Release
# branch matched to the pinned nixpkgs (26.05); follows our nixpkgs to keep a # branch matched to the pinned nixpkgs (26.05); follows our nixpkgs to keep a
# single nixpkgs in the closure. editor.nix sets programs.nixvim.nixpkgs.source # single nixpkgs in the closure. editor.nix sets programs.nixvim.nixpkgs.source
@@ -132,9 +123,7 @@
./modules/users.nix ./modules/users.nix
./modules/common-nixos.nix ./modules/common-nixos.nix
./modules/features.nix ./modules/features.nix
./modules/sssd.nix
commonModule commonModule
inputs.agenix.nixosModules.default
home-manager.nixosModules.home-manager home-manager.nixosModules.home-manager
{ {
home-manager.useGlobalPkgs = true; home-manager.useGlobalPkgs = true;
-5
View File
@@ -62,11 +62,6 @@
features.swayDesktop.enable = false; features.swayDesktop.enable = false;
# Opt out of fleet-wide SSSD/Authentik LDAP auth: this is a work-managed WSL
# box, not part of the personal directory. Every other NixOS host inherits the
# default-true from modules/sssd.nix.
services.authentikLdap.enable = false;
# NOTE: this user's systemd --user lingering -- so the home-manager renovate # NOTE: this user's systemd --user lingering -- so the home-manager renovate
# timer fires without an open login session -- is enabled from the host table # timer fires without an open login session -- is enabled from the host table
# in flake.nix (users.emmathorpe.linger = true) and applied by # in flake.nix (users.emmathorpe.linger = true) and applied by
-130
View File
@@ -1,130 +0,0 @@
# Authentik LDAP authentication for NixOS hosts.
#
# Wires SSSD (System Security Services Daemon) to the Authentik LDAP outpost so
# every Linux host authenticates users against the same directory that backs the
# SSO stack. Enabled by default on every NixOS host via baseModules; the EDaaS
# WSL box opts out (services.authentikLdap.enable = false) because it is a
# work-managed Windows-hosted environment.
#
# The Authentik LDAP provider exposes NON-standard object classes/attributes
# (goauthentik.io/ldap/user, goauthentik.io/ldap/group) alongside the POSIX
# attributes (uid, uidNumber, gidNumber, homeDirectory), so the schema mappings
# below are explicit rather than relying on an RFC2307 default.
#
# The bind password is NOT inlined: services.sssd.config renders to the world-
# readable Nix store, so the credential is delivered out-of-band by agenix as an
# sssd.conf drop-in under /etc/sssd/conf.d/ (SSSD merges conf.d/*.conf after the
# main file). See secrets/README.md.
{
config,
lib,
...
}:
let
cfg = config.services.authentikLdap;
# Directory coordinates for the Authentik LDAP provider.
ldapUri = "ldaps://ldap.lyrapup.pet:636";
searchBase = "dc=ldap,dc=goauthentik,dc=io";
bindDn = "cn=sssd-bind,ou=users,dc=ldap,dc=goauthentik,dc=io";
in
{
options.services.authentikLdap.enable =
lib.mkEnableOption "SSSD authentication against the Authentik LDAP outpost"
// {
default = true;
};
config = lib.mkIf cfg.enable {
services.sssd = {
enable = true;
# Non-secret sssd.conf. The bind password is injected separately via the
# agenix conf.d drop-in (ldap_default_authtok lives there, not here) to
# keep it out of the Nix store.
config = ''
[sssd]
config_file_version = 2
services = nss, pam
domains = default
[nss]
# Do not walk the whole directory for `getent passwd` etc.
filter_users = root
filter_groups = root
[pam]
[domain/default]
# --- Providers --------------------------------------------------------
id_provider = ldap
auth_provider = ldap
chpass_provider = none
access_provider = permit
# --- Connection -------------------------------------------------------
ldap_uri = ${ldapUri}
ldap_search_base = ${searchBase}
ldap_default_bind_dn = ${bindDn}
ldap_default_authtok_type = password
# ldap_default_authtok is supplied by the agenix drop-in in conf.d.
# --- TLS (LDAPS on 636; no StartTLS) ---------------------------------
ldap_id_use_start_tls = false
ldap_tls_reqcert = demand
# --- Schema: Authentik LDAP provider ---------------------------------
# Authentik returns DN-valued group membership (member/memberOf), so
# rfc2307bis (not rfc2307) is the correct base schema.
ldap_schema = rfc2307bis
# Users: goauthentik.io/ldap/user, keyed by uid; POSIX attrs are
# standard names (uidNumber/gidNumber/homeDirectory).
ldap_user_object_class = goauthentik.io/ldap/user
ldap_user_name = uid
ldap_user_uid_number = uidNumber
ldap_user_gid_number = gidNumber
ldap_user_home_directory = homeDirectory
ldap_user_gecos = displayName
ldap_user_shell = loginShell
# Groups: goauthentik.io/ldap/group, keyed by cn.
ldap_group_object_class = goauthentik.io/ldap/group
ldap_group_name = cn
ldap_group_gid_number = gidNumber
ldap_group_member = member
# --- Behaviour --------------------------------------------------------
cache_credentials = true
enumerate = false
'';
};
# agenix delivers the bind password as an sssd.conf drop-in. The decrypted
# plaintext IS a valid conf.d snippet:
#
# [domain/default]
# ldap_default_authtok = <the bind password>
#
# SSSD requires conf.d files to be root-owned and 0600 or it ignores them.
age.secrets.ldap-bind = {
file = ../secrets/ldap-bind.age;
path = "/etc/sssd/conf.d/01-ldap-authtok.conf";
owner = "root";
group = "root";
mode = "0600";
};
# Restart SSSD when the credential drop-in changes. agenix writes secrets in
# a system activation script that runs before systemd (re)starts services on
# a `switch`, so the file is present by the time sssd starts; the trigger
# picks up rotations of the bind password.
systemd.services.sssd.restartTriggers = [ config.age.secrets.ldap-bind.path ];
# Create home directories on first login for LDAP users (they have no
# locally-provisioned home). NixOS wires nss + the SSSD PAM stack when
# services.sssd.enable is true; mkHomeDir adds pam_mkhomedir to it.
security.pam.services.login.makeHomeDir = true;
security.pam.services.sshd.makeHomeDir = true;
};
}
-92
View File
@@ -1,92 +0,0 @@
# Secrets (agenix)
Encrypted secrets for the fleet, managed with [agenix](https://github.com/ryantm/agenix).
Each secret is an age-encrypted file (`*.age`) encrypted to a set of recipient
public keys declared in [`secrets.nix`](./secrets.nix). A host decrypts its
secrets at activation using its SSH **host** key
(`/etc/ssh/ssh_host_ed25519_key`), so every host that must read a secret has to
be listed as a recipient for it.
`secrets.nix` is read only by the `agenix` CLI. It is never imported into the
NixOS evaluation.
## Secrets in this repo
| File | Purpose | Recipients |
| --------------- | ----------------------------------------------------------------------- | ----------------------------------- |
| `ldap-bind.age` | SSSD → Authentik LDAP bind credential, as an `sssd.conf` drop-in snippet | all SSSD-enabled hosts (not EDaaS) |
Consumed by [`modules/sssd.nix`](../modules/sssd.nix) via
`age.secrets.ldap-bind.path`, which places the decrypted snippet at
`/etc/sssd/conf.d/01-ldap-authtok.conf`.
> **`ldap-bind.age` is not committed yet.** Only `ldap-bind.age.PLACEHOLDER`
> ships in this change (real host recipient keys and the real password were not
> available when it was written). Follow the steps below to create the real
> secret, then delete the `.PLACEHOLDER`.
## Owner setup checklist
Run these once (per new host or when the bind password rotates):
### 1. Collect host recipient keys
On each SSSD-enabled host (all Linux hosts **except** EDaaS):
```sh
cat /etc/ssh/ssh_host_ed25519_key.pub
```
Paste each value into the matching placeholder in `secrets.nix`, replacing the
`AAAA_PLACEHOLDER_REPLACE_ME_*` strings. (Optionally uncomment and set `admin`
to an operator user key so the secret can be edited off-host.)
### 2. Encrypt the bind password
The plaintext must be a **full sssd.conf drop-in snippet**, because SSSD cannot
read `ldap_default_authtok` from a separate file — it only merges `conf.d/*.conf`.
The content is exactly:
```ini
[domain/default]
ldap_default_authtok = <the sssd-bind service-account password>
```
Use the password of the `sssd-bind` (Terraform: `sssd-bind`) Authentik LDAP
service account. Then, from the repo root:
```sh
# Requires the agenix CLI: `nix run github:ryantm/agenix -- -e secrets/ldap-bind.age`
cd secrets
agenix -e ldap-bind.age
```
An `$EDITOR` opens; paste the two-line snippet above, save, quit. agenix writes
the encrypted `ldap-bind.age`. Commit it and delete `ldap-bind.age.PLACEHOLDER`.
### 3. Rekey after changing recipients
If you add/remove hosts in `secrets.nix`, re-encrypt every secret to the new
recipient set:
```sh
cd secrets
agenix -r
```
### 4. DNS
`ldap.lyrapup.pet` must resolve to the Authentik LDAP outpost and serve LDAPS on
port 636 with a certificate the hosts trust (`ldap_tls_reqcert = demand`). If the
cert is not from a system-trusted CA, add it to the hosts' trust store
(`security.pki.certificateFiles`) or relax `ldap_tls_reqcert` in
`modules/sssd.nix`.
### 5. Rebuild
```sh
sudo nixos-rebuild switch --flake .#<host>
```
Verify with `getent passwd <ldap-user>` and `id <ldap-user>`.
-21
View File
@@ -1,21 +0,0 @@
THIS IS A PLACEHOLDER, NOT A REAL AGE SECRET.
The real secrets/ldap-bind.age is produced by the repo owner with `agenix -e`
(see secrets/README.md) and is a binary age-encrypted blob. It is intentionally
NOT committed here because:
* the real host age recipients are not available to the author of this change
(they are each host's /etc/ssh/ssh_host_ed25519_key.pub), and
* fabricating an encrypted blob or fake host keys would be misleading.
Committing this file as `ldap-bind.age` would let modules/sssd.nix reference
`../secrets/ldap-bind.age` and evaluate, but SSSD would fail to decrypt it at
runtime. Do ONE of the following before deploying:
1. Preferred: generate the real secret (secrets/README.md), commit it as
secrets/ldap-bind.age, and delete this .PLACEHOLDER file.
The decrypted plaintext must be a valid sssd.conf drop-in (NOT the bare
password):
[domain/default]
ldap_default_authtok = <the sssd-bind service-account password>
-15
View File
@@ -1,15 +0,0 @@
let
lyrathorpe-mbp = "ssh-ed25519 AAAA_PLACEHOLDER_REPLACE_ME_mbp";
lyrathorpe-t400 = "ssh-ed25519 AAAA_PLACEHOLDER_REPLACE_ME_t400";
lyrathorpe-macpro31 = "ssh-ed25519 AAAA_PLACEHOLDER_REPLACE_ME_macpro31";
lyrathorpe-rpi5 = "ssh-ed25519 AAAA_PLACEHOLDER_REPLACE_ME_rpi5";
sssdHosts = [
lyrathorpe-mbp
lyrathorpe-t400
lyrathorpe-macpro31
lyrathorpe-rpi5
];
in
{
"ldap-bind.age".publicKeys = sssdHosts;
}