Archived
Compare commits
48
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f829ba3e7 | ||
|
|
dedd69dc42 | ||
|
|
f7f670ca4c | ||
|
|
55ba283c82 | ||
|
|
2d46d25a67 | ||
|
|
f5d29be041 | ||
|
|
e10a1572c3 | ||
|
|
578ef70aa9 | ||
|
|
e9fcbbbbcb | ||
|
|
f46ae18672 | ||
|
|
fc277294f3 | ||
|
|
f3e5ea67a0 | ||
|
|
7a8aebf679 | ||
|
|
a8d95aad02 | ||
|
|
dac5fbd574 | ||
|
|
da0c651c60 | ||
|
|
97019205da | ||
|
|
5487490b8e | ||
|
|
543ea432f0 | ||
|
|
c7bbf88dce | ||
|
|
d57145b31e | ||
|
|
1cbe80c0ca | ||
|
|
01ee261cf8 | ||
|
|
f6f30c675f | ||
|
|
60b80cbd96 | ||
|
|
27a8c7fad9 | ||
|
|
d9cee0a674 | ||
|
|
6c1891812e | ||
|
|
59316c982e | ||
|
|
9eca3bd719 | ||
|
|
f2f0fcf756 | ||
|
|
b4fb9c25f2 | ||
|
|
8955840f0a | ||
|
|
a4b49c9909 | ||
|
|
c0936a10e7 | ||
|
|
f4bbd6331d | ||
|
|
b99ba87cf6 | ||
|
|
2128353f9f | ||
|
|
bba054db85 | ||
|
|
b63a529a1d | ||
|
|
1ece0c75d9 | ||
|
|
548c6f5041 | ||
|
|
bae4c8171f | ||
|
|
3748c86049 | ||
|
|
9dd969cf4c | ||
|
|
7e4b2d33fb | ||
|
|
288d50fd33 | ||
|
|
f0e76f8aff |
+70
@@ -125,3 +125,73 @@ creation_rules:
|
|||||||
- *baremetal-gui
|
- *baremetal-gui
|
||||||
- *linode-gui
|
- *linode-gui
|
||||||
- *proxmox-gui
|
- *proxmox-gui
|
||||||
|
|
||||||
|
# IPA host keytabs (binary sops files).
|
||||||
|
# Each keytab is encrypted for all platform variants of that host so any
|
||||||
|
# deployed variant can decrypt it at boot. Run
|
||||||
|
# scripts/ipa/create-nixos-ipa-host-account.sh <hostname> to enroll a new
|
||||||
|
# host and produce the keytab; this section is updated by that script.
|
||||||
|
|
||||||
|
- path_regex: secrets/nix-cache\.keytab$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin
|
||||||
|
- *linode-nix-cache
|
||||||
|
- *lxc-nix-cache
|
||||||
|
- *proxmox-nix-cache
|
||||||
|
|
||||||
|
- path_regex: secrets/tailscale-router\.keytab$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin
|
||||||
|
- *linode-tailscale-router
|
||||||
|
- *lxc-tailscale-router
|
||||||
|
- *proxmox-tailscale-router
|
||||||
|
|
||||||
|
- path_regex: secrets/pxe-boot\.keytab$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin
|
||||||
|
- *lxc-pxe-boot
|
||||||
|
- *proxmox-pxe-boot
|
||||||
|
|
||||||
|
# nixos = the workstation (hosts/nixos/host.nix). All gui platform variants
|
||||||
|
# share the hostname "nixos" and must be able to decrypt at boot.
|
||||||
|
- path_regex: secrets/nixos\.keytab$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin
|
||||||
|
- *baremetal-gui
|
||||||
|
- *lxc-gui
|
||||||
|
- *proxmox-gui
|
||||||
|
- *linode-gui
|
||||||
|
|
||||||
|
- path_regex: secrets/server\.keytab$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin
|
||||||
|
- *linode-server
|
||||||
|
- *lxc-server
|
||||||
|
- *proxmox-server
|
||||||
|
|
||||||
|
- path_regex: secrets/docker\.keytab$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin
|
||||||
|
- *linode-docker
|
||||||
|
- *lxc-docker
|
||||||
|
- *proxmox-docker
|
||||||
|
|
||||||
|
- path_regex: secrets/tor-relay\.keytab$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin
|
||||||
|
- *lxc-tor-relay
|
||||||
|
|
||||||
|
- path_regex: secrets/nix-minimal\.keytab$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
- *admin
|
||||||
|
- *lxc-minimal
|
||||||
|
- *proxmox-minimal
|
||||||
|
- *linode-minimal
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIESDCCArCgAwIBAgIBATANBgkqhkiG9w0BAQsFADA1MRMwEQYDVQQKDApTV0VF
|
||||||
|
VC5IT01FMR4wHAYDVQQDDBVDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMjYwNzI2
|
||||||
|
MjExMzQxWhcNNDYwNzI2MjExMzQxWjA1MRMwEQYDVQQKDApTV0VFVC5IT01FMR4w
|
||||||
|
HAYDVQQDDBVDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggGiMA0GCSqGSIb3DQEBAQUA
|
||||||
|
A4IBjwAwggGKAoIBgQCzljYktbHdMGVJ6Wq0XQJuHLN6dkCSOgtoIzQtriPQkkNI
|
||||||
|
uo28LwobaiQQ8sX4kGRH/BTKnH8QlId/jug4Uc+sDHnABYu++AiOhPbBX8gCpRQ0
|
||||||
|
hebBjZiktHSBUEJR31siWOVdBoKBDJEoxehx7XUXvcxIJcaRN+LHYjO86nJN55HB
|
||||||
|
VwFU2JcYDk98c+144dFJxXdr++MjWe4Z/oVVU8JHIOtNtKhVhvij6oOSWxcYoJO/
|
||||||
|
S80LRj1vx/o6o/3G6bYug7PjY7JjZk/Oj61whijZkcsoO1MXSYI6UywJZGflv+ZB
|
||||||
|
7HyufdYAsK3WhE8O2FX3/kq64Ol83HNtoR8Dt68rTg1xpW6K45jS6iDPKueYGkb0
|
||||||
|
oSx7e++90VAW2PDhj6QQ3JJ4O5VQwrrecekJzUrAean0FOEbmgyi4PsEp1Vk6LDQ
|
||||||
|
SsIn1x0euyxVivQMlzNX2XrZL3urn1BNPqAdntXQMkR0Wl8sbUiJPe0kxG52CGXs
|
||||||
|
6yfNEXbPmVGcC0TBdGECAwEAAaNjMGEwHQYDVR0OBBYEFLh5QbI1UWMH0WR4z8bG
|
||||||
|
lhrOX3X5MB8GA1UdIwQYMBaAFLh5QbI1UWMH0WR4z8bGlhrOX3X5MA8GA1UdEwEB
|
||||||
|
/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMA0GCSqGSIb3DQEBCwUAA4IBgQCVodVN
|
||||||
|
owwo53OQe02QhtEbIur2PL7zIfvhvCTRD4J8gwpbMIqT7JQK0tV6Mvsg2L8yTb2O
|
||||||
|
KjrWeLKHGWaZZlhGSPTbkMFdb/Ls8M9FSnkc2bwcdWW3Z1lOiCjBYYqwLCG6JhvB
|
||||||
|
5SXVwWNJwXeasL2m7oFTSwhsqPpARJ2t25u2N35o+tqIoCjijKwkmEOT66N9EAbu
|
||||||
|
2VQjtYZWPkBtP4YCe0Ey6u4oy7sy8ThNAjOylZok+J4JW7QEFjK4Q/emhA4aQq5H
|
||||||
|
gg9qgMuG+5oi6D1g2Wy+fMTRBaukJtLYZbBpQMQhMYWg44uPp/2bbNPTID/nV1KB
|
||||||
|
GcPyHaskcVxPdYWxAPMwk3AeJXWyOq7atAPTF5sbk0kQQf2m+vyOqcli5CxRMUgV
|
||||||
|
rcyi9l6+dZW4U+38Q0ET5M3OuxNI4hA7kVY2cfTakXWNqh97+TIHnstblDhAxECK
|
||||||
|
6ZLMJQYUy7LqJTX84H27CBWLexEMjXwdr5HCV88Fj6mAK0fRufnIw5FeneA=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
Generated
+6
-6
@@ -173,11 +173,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1784350909,
|
"lastModified": 1785119570,
|
||||||
"narHash": "sha256-ZWyzLbS1yKUTeFJLmdVuWNnHttL333/ldJbEE+KzCrM=",
|
"narHash": "sha256-Rgs2xKnGLFWQscxUaXX07oyZeuMDOHEbqDOsgliLFGM=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "4ce190229c73d44536caa7072f6308fb2d8feeb3",
|
"rev": "d4fd24667c8cbef124bb70a20380cab75ec8474d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -259,11 +259,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1784432872,
|
"lastModified": 1785104993,
|
||||||
"narHash": "sha256-n3gKTBIV4ZA5VQpUakffBe3KGu4+mhPoA34rrqS0GkA=",
|
"narHash": "sha256-eKbrvPoAOFutbYMdbB3r5EQVmFxKv24iKqHPPUXA0gM=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "fd1462031fdee08f65fd0b4c6b64e22239a77870",
|
"rev": "8623c4c20aa4ca2f5fb81510d2944066c3fb0d96",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -1,6 +1,17 @@
|
|||||||
{ config, pkgs, lib, inputs, vars, ... }:
|
{ config, pkgs, lib, inputs, vars, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
|
imports = [
|
||||||
|
../docker/enable-service.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
nixpkgs.overlays = [
|
||||||
|
(final: prev: {
|
||||||
|
docker = prev.docker_29;
|
||||||
|
docker_cli = prev.docker_29;
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
inputs.nixos-conf-editor.packages.${pkgs.stdenv.hostPlatform.system}.nixos-conf-editor
|
inputs.nixos-conf-editor.packages.${pkgs.stdenv.hostPlatform.system}.nixos-conf-editor
|
||||||
nodejs
|
nodejs
|
||||||
@@ -18,7 +29,7 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
boot.loader.grub.useOSProber = true;
|
boot.loader.grub.useOSProber = true;
|
||||||
programs.direnv.enable = true;
|
programs.direnv.enable = true;
|
||||||
services = {
|
services = {
|
||||||
xserver = {
|
xserver = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
# Include the results of the hardware scan.
|
# Include the results of the hardware scan.
|
||||||
# ./hardware-configuration.nix
|
# ./hardware-configuration.nix
|
||||||
./set-locale.nix
|
./set-locale.nix
|
||||||
|
../ipa/client.nix
|
||||||
];
|
];
|
||||||
# Use the GRUB 2 boot loader.
|
# Use the GRUB 2 boot loader.
|
||||||
# boot.loader.grub.enable = true;
|
# boot.loader.grub.enable = true;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{ pkgs, ... }:
|
{ pkgs, vars, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
# virtualisation.docker.enable = true;
|
# virtualisation.docker.enable = true;
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
# experimental = true;
|
# experimental = true;
|
||||||
# };
|
# };
|
||||||
};
|
};
|
||||||
|
users.users.${vars.primaryUser}.extraGroups = [ "docker" ];
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
docker-compose
|
docker-compose
|
||||||
docker-buildx
|
docker-buildx
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
# Fully declarative FreeIPA domain membership.
|
||||||
|
#
|
||||||
|
# Imported by modules/common/configuration.nix — no per-host wiring needed.
|
||||||
|
# Enables itself automatically on any host that has a sops-encrypted keytab
|
||||||
|
# at secrets/<hostname>.keytab; is a no-op for all other hosts.
|
||||||
|
#
|
||||||
|
# To enroll a new host:
|
||||||
|
# 0. scripts/secrets/sync-host-keys.sh <flake-target>
|
||||||
|
# 1. scripts/ipa/create-nixos-ipa-host-account.sh [--ip <addr>] <hostname>
|
||||||
|
# (adds .sops.yaml rule, runs ipa host-add, encrypts keytab in one step)
|
||||||
|
# 2. git add secrets/<hostname>.keytab .sops.yaml && git commit
|
||||||
|
# 3. Deploy — no further steps required.
|
||||||
|
#
|
||||||
|
# Manual fallback (if the script isn't usable):
|
||||||
|
# a. On the FreeIPA server: ipa host-add <fqdn> [--ip-address=<ip>] --force
|
||||||
|
# b. On the FreeIPA server: ipa-getkeytab -s <ipa-server> -p host/<fqdn> -k /tmp/<host>.keytab
|
||||||
|
# c. From the repo root (path must match for sops creation rule to apply):
|
||||||
|
# cp /tmp/<host>.keytab secrets/<host>.keytab
|
||||||
|
# sops -e --input-type binary -i secrets/<host>.keytab
|
||||||
|
# d. Commit secrets/<host>.keytab and the updated .sops.yaml, then deploy.
|
||||||
|
#
|
||||||
|
# vars dependencies: homeDomain, ipaServer, domainControllerIp
|
||||||
|
|
||||||
|
{ config, lib, pkgs, vars, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
keytabPath = ../../secrets + "/${config.networking.hostName}.keytab";
|
||||||
|
enabled = builtins.pathExists keytabPath;
|
||||||
|
|
||||||
|
realm = lib.strings.toUpper vars.homeDomain;
|
||||||
|
fqdn = "${config.networking.hostName}.${vars.homeDomain}";
|
||||||
|
# "sweet.home" -> "dc=sweet,dc=home"
|
||||||
|
basedn = lib.strings.concatMapStringsSep "," (c: "dc=${c}") (lib.strings.splitString "." vars.homeDomain);
|
||||||
|
# security.ipa.certificate expects a derivation (package), not a raw path.
|
||||||
|
caCertPkg = pkgs.writeText "ipa-ca.crt" (builtins.readFile ../../certs/ipa-ca.crt);
|
||||||
|
in
|
||||||
|
lib.mkIf enabled {
|
||||||
|
networking.domain = lib.mkDefault vars.homeDomain;
|
||||||
|
networking.nameservers = lib.mkDefault [ vars.domainControllerIp ];
|
||||||
|
|
||||||
|
security.ipa = {
|
||||||
|
enable = true;
|
||||||
|
domain = vars.homeDomain;
|
||||||
|
realm = realm;
|
||||||
|
server = vars.ipaServer;
|
||||||
|
certificate = caCertPkg;
|
||||||
|
basedn = basedn;
|
||||||
|
ipaHostname = fqdn;
|
||||||
|
offlinePasswords = true;
|
||||||
|
cacheCredentials = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Fetch SSH public keys from IPA so users can log in with the key stored
|
||||||
|
# in their IPA profile rather than needing ~/.ssh/authorized_keys on every
|
||||||
|
# host. sss_ssh_authorizedkeys queries SSSD (which queries IPA LDAP).
|
||||||
|
#
|
||||||
|
# /nix/store is 1775 (group-writable by nixbld). OpenSSH 10.0+ rejects
|
||||||
|
# AuthorizedKeysCommand binaries whose path contains any group-writable
|
||||||
|
# component, silently skipping the command. Copy to /usr/local/bin (all
|
||||||
|
# components root-owned, 755) so the path passes sshd's safety check.
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d /usr/local 0755 root root - -"
|
||||||
|
"d /usr/local/bin 0755 root root - -"
|
||||||
|
"C+ /usr/local/bin/sss_ssh_authorizedkeys 0555 root root - ${pkgs.sssd}/bin/sss_ssh_authorizedkeys"
|
||||||
|
];
|
||||||
|
|
||||||
|
services.openssh.extraConfig = ''
|
||||||
|
AuthorizedKeysCommand /usr/local/bin/sss_ssh_authorizedkeys %u
|
||||||
|
AuthorizedKeysCommandUser nobody
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Create the home directory on first login if it doesn't exist yet.
|
||||||
|
# IPA users have no pre-created home on the host; without this sshd
|
||||||
|
# opens a session to a non-existent directory and resets the connection.
|
||||||
|
security.pam.services.sshd.makeHomeDir = true;
|
||||||
|
|
||||||
|
# Host keytab: pre-provisioned on the IPA server, sops-encrypted binary.
|
||||||
|
# Placed at /etc/krb5.keytab before SSSD starts so the host authenticates
|
||||||
|
# to IPA without running ipa-client-install.
|
||||||
|
sops.secrets."ipa-host-keytab" = {
|
||||||
|
sopsFile = keytabPath;
|
||||||
|
format = "binary";
|
||||||
|
path = "/etc/krb5.keytab";
|
||||||
|
owner = "root";
|
||||||
|
group = "root";
|
||||||
|
mode = "0600";
|
||||||
|
restartUnits = [ "sssd.service" ];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -19,6 +19,14 @@
|
|||||||
# tailnet-specific subdomain (vars.tailnetDomain) instead.
|
# tailnet-specific subdomain (vars.tailnetDomain) instead.
|
||||||
services.dnsmasq = {
|
services.dnsmasq = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
# NixOS's dnsmasq module defaults resolveLocalQueries to true, which adds
|
||||||
|
# 127.0.0.1 to networking.nameservers and makes dnsmasq bind to
|
||||||
|
# listen-address=127.0.0.1. This instance is not the host's local
|
||||||
|
# resolver — it only serves IPA's conditional forwarder for tailnet names.
|
||||||
|
# The host uses domainControllerIp directly (networking.nameservers in
|
||||||
|
# host.nix). Without this, all host DNS goes through dnsmasq, which has
|
||||||
|
# no upstream for general queries (no-resolv=true), breaking resolution.
|
||||||
|
resolveLocalQueries = false;
|
||||||
settings = {
|
settings = {
|
||||||
# Listen only on the LAN interface — not tailscale0 or loopback.
|
# Listen only on the LAN interface — not tailscale0 or loopback.
|
||||||
# bind-interfaces prevents dnsmasq from binding to 0.0.0.0 and
|
# bind-interfaces prevents dnsmasq from binding to 0.0.0.0 and
|
||||||
|
|||||||
@@ -88,6 +88,17 @@ export NIX_CACHE_HOST
|
|||||||
: "${LAN_DOMAIN:=gitea.lan.ddnsgeek.com}"
|
: "${LAN_DOMAIN:=gitea.lan.ddnsgeek.com}"
|
||||||
export LAN_DOMAIN
|
export LAN_DOMAIN
|
||||||
|
|
||||||
|
# Matches variables.nix's homeDomain -- the base LAN domain for service
|
||||||
|
# subdomains, FreeIPA Kerberos realm, and host FQDNs.
|
||||||
|
: "${HOME_DOMAIN:=sweet.home}"
|
||||||
|
export HOME_DOMAIN
|
||||||
|
|
||||||
|
# Matches variables.nix's ipaServer -- the FreeIPA server hostname.
|
||||||
|
# scripts/ipa/create-nixos-ipa-host-account.sh SSHes here to run
|
||||||
|
# ipa host-add and ipa-getkeytab.
|
||||||
|
: "${IPA_SERVER:=domain-controller.sweet.home}"
|
||||||
|
export IPA_SERVER
|
||||||
|
|
||||||
# nix_extra_opts: call as a plain statement (NOT inside $(...)/<(...) --
|
# nix_extra_opts: call as a plain statement (NOT inside $(...)/<(...) --
|
||||||
# that forks a subshell, and the whole point is exporting a decision back
|
# that forks a subshell, and the whole point is exporting a decision back
|
||||||
# into *this* shell) to populate the global NIX_OPTS array with whatever
|
# into *this* shell) to populate the global NIX_OPTS array with whatever
|
||||||
|
|||||||
Executable
+295
@@ -0,0 +1,295 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Add a NixOS host to the FreeIPA domain and produce a sops-encrypted keytab
|
||||||
|
# at secrets/<hostname>.keytab, ready for modules/ipa/client.nix.
|
||||||
|
#
|
||||||
|
# One command replaces three error-prone manual steps:
|
||||||
|
# 1. ipa host-add on the domain controller
|
||||||
|
# 2. ipa-getkeytab on the domain controller + SCP back
|
||||||
|
# 3. sops encrypt in-place (must be at secrets/<hostname>.keytab for
|
||||||
|
# the creation rule to match -- the common mistake that breaks sops)
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# scripts/ipa/create-nixos-ipa-host-account.sh [options] <hostname>
|
||||||
|
#
|
||||||
|
# Arguments:
|
||||||
|
# <hostname> Short hostname, e.g. "tailscale-router". The FQDN is
|
||||||
|
# derived as <hostname>.<HOME_DOMAIN>.
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# --ip <addr> Register this IP with the IPA host record (optional).
|
||||||
|
# --dc <host> SSH to this host to run IPA commands.
|
||||||
|
# Default: $IPA_SERVER (from env.sh / environment).
|
||||||
|
# --dc-user <u> SSH user on the domain controller. Default: wayne.
|
||||||
|
# --dry-run Print what would be done without making any changes.
|
||||||
|
# -h, --help Show this message.
|
||||||
|
#
|
||||||
|
# Prereqs:
|
||||||
|
# 1. Run from the repo root (so .sops.yaml and secrets/ are found).
|
||||||
|
# 2. SSH access to the domain controller as --dc-user (default: wayne)
|
||||||
|
# with passwordless sudo (or sudo cached). IPA commands and kinit run
|
||||||
|
# as root via sudo so the Kerberos ticket is in root's cache where all
|
||||||
|
# ipa tools expect it. If there's no valid ticket, the script runs
|
||||||
|
# `sudo kinit admin` interactively — you'll be prompted for the IPA
|
||||||
|
# admin password once. The password never touches this script.
|
||||||
|
# 3. The host's age key(s) must already be in .sops.yaml. Run
|
||||||
|
# scripts/secrets/sync-host-keys.sh <flake-target> first so the host
|
||||||
|
# can decrypt its own keytab on boot. This script adds the .sops.yaml
|
||||||
|
# creation rule for secrets/<hostname>.keytab automatically, but the
|
||||||
|
# host age key anchor (&lxc-<hostname> etc.) must already exist —
|
||||||
|
# otherwise only the admin key can decrypt the keytab and the deployed
|
||||||
|
# host will fail to read it.
|
||||||
|
# 4. sops in PATH, or Nix available to run it via `nix run`.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
|
||||||
|
# shellcheck source=../env.sh
|
||||||
|
source "${SCRIPT_DIR}/../env.sh"
|
||||||
|
|
||||||
|
# --- Argument parsing ---
|
||||||
|
|
||||||
|
DC_HOST="${IPA_SERVER}"
|
||||||
|
DC_USER="wayne"
|
||||||
|
IP_ADDR=""
|
||||||
|
DRY_RUN=false
|
||||||
|
TARGET=""
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
sed -n '/^# Usage:/,/^[^#]/{ /^#/{ s/^# \?//; p } }' "$0"
|
||||||
|
exit "${1:-0}"
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--ip) IP_ADDR="$2"; shift 2 ;;
|
||||||
|
--dc) DC_HOST="$2"; shift 2 ;;
|
||||||
|
--dc-user) DC_USER="$2"; shift 2 ;;
|
||||||
|
--dry-run) DRY_RUN=true; shift ;;
|
||||||
|
-h|--help) usage 0 ;;
|
||||||
|
-*) echo "Unknown flag: $1" >&2; usage 1 ;;
|
||||||
|
*)
|
||||||
|
if [[ -n "${TARGET}" ]]; then echo "Unexpected argument: $1" >&2; usage 1; fi
|
||||||
|
TARGET="$1"; shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "${TARGET}" ]]; then
|
||||||
|
echo "Error: hostname required." >&2
|
||||||
|
usage 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
FQDN="${TARGET}.${HOME_DOMAIN}"
|
||||||
|
KEYTAB_SECRET="${REPO_ROOT}/secrets/${TARGET}.keytab"
|
||||||
|
# Temp path on the domain controller — use a name that won't collide.
|
||||||
|
DC_TMP="/tmp/nixos-keytab-${TARGET}-$$.keytab"
|
||||||
|
|
||||||
|
# --- Helpers ---
|
||||||
|
|
||||||
|
log() { echo "==> $*"; }
|
||||||
|
logn() { echo " $*"; }
|
||||||
|
|
||||||
|
run() {
|
||||||
|
if $DRY_RUN; then
|
||||||
|
echo "[dry-run] $*"
|
||||||
|
else
|
||||||
|
"$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
dc_run() {
|
||||||
|
# Run a command string on the domain controller via SSH.
|
||||||
|
if $DRY_RUN; then
|
||||||
|
echo "[dry-run] ssh ${DC_USER}@${DC_HOST} $*"
|
||||||
|
else
|
||||||
|
ssh "${DC_USER}@${DC_HOST}" "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Locate sops ---
|
||||||
|
|
||||||
|
if command -v sops &>/dev/null; then
|
||||||
|
SOPS_CMD=(sops)
|
||||||
|
else
|
||||||
|
log "sops not in PATH — will use 'nix run nixpkgs#sops'"
|
||||||
|
SOPS_CMD=(nix run "nixpkgs#sops" --)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Preflight checks ---
|
||||||
|
|
||||||
|
cd "${REPO_ROOT}"
|
||||||
|
|
||||||
|
[[ -f .sops.yaml ]] || { echo "Error: .sops.yaml not found — run from repo root." >&2; exit 1; }
|
||||||
|
[[ -d secrets ]] || { echo "Error: secrets/ not found — run from repo root." >&2; exit 1; }
|
||||||
|
|
||||||
|
# --- Step 1: Ensure .sops.yaml has a creation rule for this keytab ---
|
||||||
|
#
|
||||||
|
# sops matches creation rules against the PATH of the file being encrypted,
|
||||||
|
# not the output path. To match secrets/<hostname>.keytab, the file must
|
||||||
|
# already be at that path when sops -e -i is called. The creation rule must
|
||||||
|
# also exist at that point or sops will refuse with "no matching creation
|
||||||
|
# rules found."
|
||||||
|
|
||||||
|
log "Checking .sops.yaml for creation rule: secrets/${TARGET}.keytab"
|
||||||
|
|
||||||
|
RULE_EXISTS=false
|
||||||
|
# Match "path_regex: secrets/<hostname>...keytab" — using .*keytab rather
|
||||||
|
# than \.keytab because the file stores the regex verbatim (\.keytab = two
|
||||||
|
# chars: backslash + dot), which a BRE \. (= escaped literal dot) won't span.
|
||||||
|
if grep -q "path_regex: secrets/${TARGET}.*keytab" .sops.yaml 2>/dev/null; then
|
||||||
|
RULE_EXISTS=true
|
||||||
|
logn "Rule already exists — skipping addition."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! $RULE_EXISTS; then
|
||||||
|
# Collect which platform-variant age anchors exist in .sops.yaml for this
|
||||||
|
# hostname. The keytab is platform-agnostic (same FQDN regardless of
|
||||||
|
# whether lxc/proxmox/linode variant is deployed), so all platform anchors
|
||||||
|
# that have been registered get added as recipients.
|
||||||
|
RECIPIENTS=("*admin")
|
||||||
|
for platform in lxc proxmox linode; do
|
||||||
|
anchor="${platform}-${TARGET}"
|
||||||
|
if grep -q "^ - &${anchor} " .sops.yaml; then
|
||||||
|
RECIPIENTS+=("*${anchor}")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ${#RECIPIENTS[@]} -eq 1 ]]; then
|
||||||
|
echo "Warning: no platform age keys found for '${TARGET}' in .sops.yaml." >&2
|
||||||
|
echo " Run scripts/secrets/sync-host-keys.sh <flake-target> first," >&2
|
||||||
|
echo " otherwise only the admin key can decrypt the keytab and the" >&2
|
||||||
|
echo " deployed host won't be able to read it at boot." >&2
|
||||||
|
echo " Continuing with admin-only encryption..." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build the indented recipient list for the YAML block.
|
||||||
|
RECIPIENT_YAML=""
|
||||||
|
for r in "${RECIPIENTS[@]}"; do
|
||||||
|
RECIPIENT_YAML+=" - ${r}"$'\n'
|
||||||
|
done
|
||||||
|
RECIPIENT_YAML="${RECIPIENT_YAML%$'\n'}" # strip trailing newline
|
||||||
|
|
||||||
|
NEW_RULE="
|
||||||
|
# Host keytab for ${TARGET} FreeIPA enrollment (binary sops file).
|
||||||
|
# Generated by scripts/ipa/create-nixos-ipa-host-account.sh.
|
||||||
|
- path_regex: secrets/${TARGET}\\.keytab\$
|
||||||
|
key_groups:
|
||||||
|
- age:
|
||||||
|
${RECIPIENT_YAML}"
|
||||||
|
|
||||||
|
if $DRY_RUN; then
|
||||||
|
echo "[dry-run] Would append to .sops.yaml:"
|
||||||
|
echo "${NEW_RULE}"
|
||||||
|
else
|
||||||
|
logn "Adding creation rule (recipients: ${RECIPIENTS[*]})"
|
||||||
|
printf '%s\n' "${NEW_RULE}" >> .sops.yaml
|
||||||
|
logn "Added."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Step 2: Add IPA host account (idempotent) ---
|
||||||
|
|
||||||
|
log "Adding FreeIPA host account: ${FQDN}"
|
||||||
|
|
||||||
|
# Ensure there's a valid admin Kerberos ticket on the DC.
|
||||||
|
# ipa host-add and ipa-getkeytab both need one. All IPA commands run via
|
||||||
|
# sudo so the ticket must be in root's cache — check and refresh as root.
|
||||||
|
# ssh -t allocates a PTY so kinit (and sudo if needed) can prompt normally;
|
||||||
|
# no password ever touches this script or the shell history.
|
||||||
|
if ! $DRY_RUN; then
|
||||||
|
if ! ssh "${DC_USER}@${DC_HOST}" "sudo klist -s" &>/dev/null; then
|
||||||
|
log "No valid Kerberos ticket on ${DC_HOST} — running sudo kinit admin"
|
||||||
|
ssh -t "${DC_USER}@${DC_HOST}" "sudo kinit admin"
|
||||||
|
if ! ssh "${DC_USER}@${DC_HOST}" "sudo klist -s" &>/dev/null; then
|
||||||
|
echo "Error: kinit admin failed or produced no valid ticket." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
logn "Kerberos ticket on ${DC_HOST} is valid."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
IP_FLAG=""
|
||||||
|
[[ -n "${IP_ADDR}" ]] && IP_FLAG="--ip-address=${IP_ADDR}"
|
||||||
|
|
||||||
|
# --force: create the host record even if DNS doesn't resolve it yet.
|
||||||
|
if $DRY_RUN; then
|
||||||
|
echo "[dry-run] ssh ${DC_USER}@${DC_HOST} sudo ipa host-add '${FQDN}' ${IP_FLAG} --force"
|
||||||
|
else
|
||||||
|
HOST_ADD_OUT=$(ssh "${DC_USER}@${DC_HOST}" "sudo ipa host-add '${FQDN}' ${IP_FLAG} --force 2>&1") \
|
||||||
|
&& HOST_ADD_RC=0 || HOST_ADD_RC=$?
|
||||||
|
if [[ $HOST_ADD_RC -eq 0 ]]; then
|
||||||
|
echo "${HOST_ADD_OUT}"
|
||||||
|
elif echo "${HOST_ADD_OUT}" | grep -q "already exists"; then
|
||||||
|
logn "(host already registered)"
|
||||||
|
else
|
||||||
|
echo "Error: ipa host-add failed (exit ${HOST_ADD_RC}):" >&2
|
||||||
|
echo "${HOST_ADD_OUT}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Step 3: Fetch the keytab from the domain controller ---
|
||||||
|
|
||||||
|
log "Fetching keytab for host/${FQDN}"
|
||||||
|
|
||||||
|
# Remove the plaintext keytab if the script aborts before encryption completes.
|
||||||
|
# The trap is cleared at the end of step 4 once sops has encrypted it in-place.
|
||||||
|
trap 'rm -f "${KEYTAB_SECRET}"' EXIT
|
||||||
|
|
||||||
|
dc_run "sudo ipa-getkeytab -s '${IPA_SERVER}' -p 'host/${FQDN}' -k '${DC_TMP}'"
|
||||||
|
|
||||||
|
if $DRY_RUN; then
|
||||||
|
echo "[dry-run] Would stream ${DC_USER}@${DC_HOST}:${DC_TMP} → secrets/${TARGET}.keytab"
|
||||||
|
else
|
||||||
|
logn "Streaming keytab from ${DC_HOST}:${DC_TMP} → secrets/${TARGET}.keytab"
|
||||||
|
# scp can't read a root-owned temp file as ${DC_USER}; pipe through sudo cat instead.
|
||||||
|
ssh "${DC_USER}@${DC_HOST}" "sudo cat '${DC_TMP}'" > "${KEYTAB_SECRET}"
|
||||||
|
|
||||||
|
logn "Removing temp file on ${DC_HOST}"
|
||||||
|
dc_run "sudo rm -f '${DC_TMP}'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Step 4: Encrypt in-place ---
|
||||||
|
#
|
||||||
|
# The file must already be at secrets/<hostname>.keytab (done above) so
|
||||||
|
# sops matches the creation rule by path. Using -i (in-place) rather than
|
||||||
|
# stdout redirect keeps the path intact through the encrypt call.
|
||||||
|
|
||||||
|
log "Encrypting secrets/${TARGET}.keytab in-place with sops"
|
||||||
|
run "${SOPS_CMD[@]}" -e --input-type binary -i "${KEYTAB_SECRET}"
|
||||||
|
|
||||||
|
# Encryption succeeded — the file is now sops-encrypted; cancel the cleanup trap.
|
||||||
|
trap - EXIT
|
||||||
|
|
||||||
|
# --- Done ---
|
||||||
|
|
||||||
|
if ! $DRY_RUN; then
|
||||||
|
echo ""
|
||||||
|
echo "Done. secrets/${TARGET}.keytab is sops-encrypted and ready."
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " 1. Verify: grep '\"data\": \"ENC' secrets/${TARGET}.keytab"
|
||||||
|
echo " 2. Stage and commit:"
|
||||||
|
echo " git add secrets/${TARGET}.keytab .sops.yaml"
|
||||||
|
echo " git commit -m 'secrets: add IPA keytab for ${TARGET}'"
|
||||||
|
echo " 3. Add to hosts/${TARGET}/host.nix (networking block and imports):"
|
||||||
|
echo ""
|
||||||
|
echo " networking = {"
|
||||||
|
echo " hostName = \"${TARGET}\";"
|
||||||
|
echo " domain = vars.homeDomain; # required for Kerberos FQDN"
|
||||||
|
echo " nameservers = [ vars.domainControllerIp ]; # IPA DNS"
|
||||||
|
echo " ..."
|
||||||
|
echo " };"
|
||||||
|
echo ""
|
||||||
|
echo " imports = ["
|
||||||
|
echo " (import ../../modules/ipa/client.nix {"
|
||||||
|
echo " keytabSopsFile = ../../secrets/${TARGET}.keytab;"
|
||||||
|
echo " caCertFile = ../../certs/ipa-ca.crt;"
|
||||||
|
echo " })"
|
||||||
|
echo " ];"
|
||||||
|
echo ""
|
||||||
|
echo " 4. Deploy: nixos-rebuild switch (or create-proxmox-resource.sh)"
|
||||||
|
fi
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"data": "ENC[AES256_GCM,data:z2bazlADvtWkGcY4an7NgZgIDSDq8KmYpSq8tcBleW/33HlElMNhdmje8m8hd7WxgrVvCQDdlAfodBLj2mjbgSwBptlPX2zbwcmJdm0g0Ope+YyQZucIio7NnBaYa+OK8meWqmxO2WEeOk+hnNfNPbt8MkzF/FMMbP8xv9wSTKP9CDdmO7s8rdg8G1JxVfn9dyaSDUTuA2JGNtbh+osd4CtNCV/bc84MjXDZTgTfT0W/uv8VH3iPNT7BhQbU9FIY4dsFRYBf758VtJ+3mTBiQkR38IevJBvhFB7Wiqn118LSBgjdTKJnBwmGo6lMdo0LRYHEgUnb2ejhWMNie19x+LEAErDki0RBmjc2BbnOF908kUlW2fUPo5fz/hlfqlhDLS1uOxESKhfnKt7s5qzWiMX/nohpknVfyMbmu9j/M571z9sgFnJAylpW3NLp53lX8N9mINwRGXsyL/ijbNE=,iv:M/MNquAGN+lIIyVVvFRgKR6DKAhLclc/OY1HV5Xwoog=,tag:eEuURMErSqsoh+l9ZaHY9Q==,type:str]",
|
||||||
|
"sops": {
|
||||||
|
"age": [
|
||||||
|
{
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqZlUxRG0zR3IzMlpDL3Vl\nNmRWQXUvc1NDek5wbWtKVEFTcTlxL3ZoWldNCmJVMkFtbDkxN1FGQ2VLTlhBUW5Q\nZ3VzL0UydUQ2YnlUMmtqa1Z0d0FSVFUKLS0tIGJFelc4Rm84T3ZHSGZDdnNUbUFM\nRXZHV3N3aVlSOXhUZk04UFRMWU5TbFUKhPU80PVYuDFUCxu1CA8+W8bqkr+Ne2fh\n+nBUPJbGxfN9TyD9tUC77AMbcL1R2L5x+SSAh0bEgSWE24/RjsuVKw==\n-----END AGE ENCRYPTED FILE-----\n",
|
||||||
|
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3M1o3S2FibHV6N2tIOFEx\nRytJRDFIUU05azNDbWlpaDdFQXFRWlBUNnlFCnBwNVB0SEVwL3NWNS9aeG4xSUI1\nV3NOcUFxUUhaODYyWXdFMEhWeFpvancKLS0tIHY4NHFQRGJwUURQbWZ2cGlSdm9s\nOFJodGZkekk3UnBRRUwxb2YrQ1ZPcU0K0tCapb1hfVHFSNpmESXexYa5k9OE9Tha\n51QpU4mZHKGrnWPK/kyj7rHiz95TLsmwUdA8Q2hOiYNSxEaVBjYDQw==\n-----END AGE ENCRYPTED FILE-----\n",
|
||||||
|
"recipient": "age1jcx3yajjhghn8qh8za3yeu8nxykzlg3p4nrv03vnfvzl0mzayg2qmg940e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkTmNrbUFoVFVGcytzQkk1\nMUxDdmJGL0xDcUNZaDFRQnUwNXoyMU90SEVvCi9EaTlVOGlMeUZtQzRJS1J2TTJY\neWdkMGNFMWZzQXVKOU5KcXVvalk1ekkKLS0tIDN2MlRQcG5aQnN3bUMrSDdvc09X\nM29hQ0pJRzk1amRHaS9mRlhrb2FMZG8KEWkSSP+MqGRU75qo7ctOCL7qHhyCM3l4\nL+ga1XOKxRsQkKQPozCi5Bm+k28W9hIDaC34Rw9difxICM9Z1rAbOA==\n-----END AGE ENCRYPTED FILE-----\n",
|
||||||
|
"recipient": "age1ufg390ydrmma849t9xfkxxl5xvdkk6mngnlzhmy7mvuaje8sgcmsmnq6l7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1TUtxVjJ2STFpU3RNdWpJ\nVjcwUHVORVN6L1pKVC9TeWt4cTNoR2pPWVRBCkR4QVJ1bHFUa2Z5aWw1bG0vQWVy\ncjNGRDl5REZzbzdZWTFMRkU1eHFTc1kKLS0tIHNLVDFiRkRKTHhrSEx2S0J1R3cx\nSkdEQWJya1ptTTRqUjJZT3FFaFhkd0kKgecHrnzW9+Eb+b7c0z1yR+Y0Czsr9Kjh\nx1UUOfleHntJUCs8oYcOogKknEnhBJJoJ5oe/gWruRSwle1fs3cBqQ==\n-----END AGE ENCRYPTED FILE-----\n",
|
||||||
|
"recipient": "age1jlltcv5jcnm40z5k0q6hv053k2rqpqvemtuecdwn527uw8uqz4es3x7m68"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastmodified": "2026-07-27T21:22:58Z",
|
||||||
|
"mac": "ENC[AES256_GCM,data:VG2ygV4X6yMxGlJgq3sN4GAzgVqiD06noyMxB2yb/FCmiFVYZAH/9LqF//G7MTInfIjFrTDyoHAny+m/ztUrJkHXI1Fzg0U8R/zYyQ6wj/GguL0gyhA70uriQhvHsRHefHPa7Km61Blo9cME6CBJWejtRvh1IMw9itAioVrSIZE=,iv:zjxNG6IYxkcNqLk08NADoDiIbS6at86y136SBzadW1U=,tag:sj4ZRR7ixU8h7aOUJmTCYg==,type:str]",
|
||||||
|
"version": "3.13.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"data": "ENC[AES256_GCM,data:Q++XWxg9tvY7ugT8+8FWCC5jgOfQ1+LYLsnqN0/WGxTf5XT2VmoXvszcE/ow2BUOBG3qBTXS3OHYFPwmj1GgaBHB8rpdXX6+LveSBE2gmx1VR+NUDTxy+0/DBq411MK9n/J9eIYcybdFIE11biFSAob9EgfxBF5roCDXIPDPyVCSe6LyhNvqYPnGQsfHCbejSewLTcRQEiguP9BX96CpMPIpmaB9fHN25t5RWCgMI7MtacrRxRsyKg44+2FZstXdZrp2Wv9u86BxqdAFqtZE8qpPeGdrdzluZx9jhnw0wZPzHdKg5wS7/UrLCb0UxIQiDxDMDuuBuLGkKXfAhb6SGZU41wWAWYk548iGRUaG+79BO4HhRZNObOFvfsjpMXEfcH/Vbv/wHVY4OpJUPe/ZWK4wpL9YrY4nT0t62HH7MirOy1uhwLE50h2+6dHKj6ur5G7thkSqgzVWXQ==,iv:zaRBwS+gfXLhH30havn6Q2+oPWuLV3qBfbOj00kewlQ=,tag:Vr5iEQ2u+9YNahryhgzwSw==,type:str]",
|
||||||
|
"sops": {
|
||||||
|
"age": [
|
||||||
|
{
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArTGJnaVhleUtsWnlITE1s\nNmRCaW1QWTFNSG5LRFdmb1lzRlV5NWl3YTBNCmF6eUN6RkJBZzc0MmJJa0dKTW01\ncVh4K1VLR2lURUpKQXpxNGpQNnpSUlUKLS0tIFJONnJFWkNCR3pqalRsUW9POVBj\nQ0pFQ3ltKzBETTVXTW5sV1ppWTFJc1kKzxUboNZO+Nwn2eTWy11VP9w1pRswCHaJ\nE2dYU0oUOClVzc0oSuIJxraG6TPj1N4WGC24gS+UmpkmSuCiOeZBsw==\n-----END AGE ENCRYPTED FILE-----\n",
|
||||||
|
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDSmxNR1I3dUFKT0xUVG9h\nWkRIakNVeWRQOEN3blNRVjZlWHF4K2NRa0hBClRiOXlmTTJ4T2JTUEw2c1l0R2N2\nMnRwdDA5bEZlQWJRTm9vUmNKclBSU1EKLS0tIFcyeDFjbTZyVEVDUjN1VzU1VHly\nNWNDMW9rTXY2bHNWYVR0SmtMckovUzQKhTWr6yFVW9am3okCiIswwqR5+/p9OLmB\nWCgPtwoFaBt1RjUXPK4/eS4LlucR2K6V/mNMn4xVsnkIl193U9632g==\n-----END AGE ENCRYPTED FILE-----\n",
|
||||||
|
"recipient": "age16j42pdc5dr6wnj7xayhkqdj2rny9u68fcqejs50hqq42scssh4gsnrrnlt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2ME9CbmVGcENRWksyL0pW\nOEJyUGhpbGMwWlBhVXBSeXQ2MW1EWnFuR0E4CnMwU0pjdk1YMzF5ZEhTVFlBaHZq\nam94UWVGbjhZSEx6VHBmem9JRWgwYzAKLS0tIHFJZWRyRjRHNzhXdDJSYWN3bDlR\nYVp3eGJWWkh3Y09ZWElyclZQN1ZSVFkKjR32//EcFAdMjVlNgky5zvVkwXwEN68D\nrkTuHKjiO5aV7yAQGPkdNw0UM0oRGF0u4YF3oOUcZfSvnKgDeoi2Zw==\n-----END AGE ENCRYPTED FILE-----\n",
|
||||||
|
"recipient": "age1ug787sgt6st6k82fgkrug2lzltw4qsukrrqqs3w27ewwqj8rg4hsxcmylz"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastmodified": "2026-07-28T00:27:46Z",
|
||||||
|
"mac": "ENC[AES256_GCM,data:AbJIHYcFpeanQsJ3x7RPL9Yjlg5BJgkepKax0fL9L/PpA03Antab93iUNG95Mp6k/duovp8Jm445lbuppDZq1dh9ij/deBa8GbzJ50wwEe9zMc3EwRKScpqZEhRPF7KlJsIjsHJyd8NkcI5ji49XkHb4Ae1//8zG5HpVgy+3b04=,iv:uxQCbMwMIfP5S1dbsvIx3F79YWEguxwox8T0YZvUBdc=,tag:3utjmBGDmPc8q4JjaXvCkA==,type:str]",
|
||||||
|
"version": "3.13.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"data": "ENC[AES256_GCM,data:+/1AAVha+86abQIX8tebqPJ6BIcCCiTJgPe0OfFOwA2Mcx0NJdiPtQgyeo3G8fAolIQOFkk5reL+GUhlOz4iEbPOCWwFCckdH0tXiCiVcD35qQNMRurY8HmpM5FWFnyzBkKuyNZ8okPuo2+fA3NQh3gs94rpm2kBsfS18xvrlv9b8u1JvAxlH6GRMN1uUgFGmOXlpGAVjDUFiKUHN8tRSZpsxG0aKKPBZ82JdKgYfCiCQifS1366gIFrsjGriUC+P8wkadRnD1JE2ZAGSL7wJLaRmzA4LAMGXFGbitOsFxKxX3q8VWEXaaL+9h4rTe0WCrvmgDM9NUeVHpfRiJrLvQgrFZMUxuZF66vQVsLybjIWpYYJcjC6nfR0U3lMa9gjzOTMPxm8HdGKC53FhLM1HiPoe1a2Nno283fV0uaByMPmo257O6l1CXqb6KoZOGFccm7fKR/VnFAS1AInF+szI7/r+UcaTW6LwYeV3OwLE4a8b4OiqTLgCu6v,iv:kUlVXF4Yl5HGvoLwu9loiuuDVtz0kARRiUU3K+BPL2A=,tag:pVaGzQAX9Etoqc5XMGmNeg==,type:str]",
|
||||||
|
"sops": {
|
||||||
|
"age": [
|
||||||
|
{
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6OUVUdkoxM1M5THlYZ1ky\nYWJ5RjhtRGoxTldwRkI2T1p6dHR4Y3gwR1ZrCjRDS1hTM01lZ0FSalBYQ1B0bENs\nUytXNmlrUkV4TmJkMUM1QW1ZaExIcGMKLS0tIHdHKzdEcUxvM0ZYUEp0a2d6SHp5\nYXQydy9uNG15V2FhUUd4NEFTMTNNMEUKkIzKEoYzoVs+nhnpkHFgDkQqrWykatND\ntsNxcr1SXSKeEW1m/QpXZnn/aW3zSQR09PqOHf7PYU47/AdkwrUaAg==\n-----END AGE ENCRYPTED FILE-----\n",
|
||||||
|
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByZENrck42dWJjR2hYUVBl\ncERtRjR6dTdING5nWXlBc3o3eXFhaSs5NkFRCk43QXFUeTBJR0U5dG9YTDRmQ0Yx\naEo2blUvUXNZY3dpbEZRTUx1elNzdkkKLS0tIHJwNjRNM3V6WktWZ1BlUUtLRTNI\nMTVEbCtYbllIbTdxTGozWUluS3pIRXMKZCruDIkD/JofdAHWgPuaaKTDsz408ZkY\n77mhO8J+kd03qwt6qhFC5KF1lyjhwEnqrOE195+R/8Yl7hA/DsL2ZQ==\n-----END AGE ENCRYPTED FILE-----\n",
|
||||||
|
"recipient": "age1k7d2du5mejsmv5rzavm4xwgpthqvcfsehduquv28nzs53zppa3kqngfxq2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxaC95RjYybzVlU2tYdHdU\naGJ4M2IvNkJwVlNIU0VCZEFUZk5VaksrVnpFCmJXS3luTG9WejR0Rk9DbHA3dngy\nRElFWjU1N2xuc3JaKzQ0L0U2Yktib1kKLS0tIFlrK3F0ZFh5Ync3ejVWMzRKTUx2\nZDhxdEMwa3B4TFZUcWdTdktDeGt1QUEKfgYnK2lW3cZuJGaw+bAKDipLuC4S5vK2\nxK2eJB5TP/xXrp0F3lx9sc2b1FOY9Vt9IQ7zVlBqJFkzJrcw8zYJJw==\n-----END AGE ENCRYPTED FILE-----\n",
|
||||||
|
"recipient": "age1zhfyuzlq40reuqlr34gf77852nhs3t6mqfzrqmas8z6sxk7tcfhsungrm0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwOGFkSXNxTUIzLzR1Skk5\nMGZveHFDN3YwVmpwT3VTYkppWEU1aGVpdkhRCjZtRC92bVphd0ZFbzFxVzRUcGk4\nMUd1aWdEbFRaM01FT25JSVRoMTRsNUUKLS0tIDAybmcrYjFVWkFYaGY5TFZjcjFO\nYlZoQjJjN2lFNzd4RlJuSHFlRlNCOFEKgUIPnL2/OJgz9oMYt86/llHa7adTkhs6\n8yGYGV1wtU9aYtUMIR907SfYyZ6M4z8jH2wwzpQLbwQQMLgkejl1jg==\n-----END AGE ENCRYPTED FILE-----\n",
|
||||||
|
"recipient": "age1f7usptjx9rv4rxauasve200gxtdt9jkqhhdqstlf20wvlm7u75rsjfw50m"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastmodified": "2026-07-27T23:14:51Z",
|
||||||
|
"mac": "ENC[AES256_GCM,data:CYhzR0Y29GXvWUBbb13s16v9hDQ4k4Xmd4FUpcyNtHe0L/IrDGkx1WHzAohNGZjGMNB9W3BgiEH85OoOo8r1F82El4/8s2prEJi8AARvQU3+2cmRjjhNCCYQWmJkoF/cZNpw6nVyWzZdfUpPvHjbuf6utlI3rtR0qiCpSo/32S0=,iv:XLKTU2Ute4jMkfRAbXiVGxqP9+fjSs6HwRv1JfTTe7w=,tag:oIqS/1HWQZ7mLWaO7GwLFA==,type:str]",
|
||||||
|
"version": "3.13.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
dockerIp = "192.168.2.225"; # docker Proxmox VM LAN IP
|
dockerIp = "192.168.2.225"; # docker Proxmox VM LAN IP
|
||||||
pbsIp = "192.168.2.244"; # Proxmox Backup Server LAN IP (not NixOS-managed)
|
pbsIp = "192.168.2.244"; # Proxmox Backup Server LAN IP (not NixOS-managed)
|
||||||
domainControllerIp = "192.168.2.253"; # FreeIPA domain controller / primary DNS (not NixOS-managed)
|
domainControllerIp = "192.168.2.253"; # FreeIPA domain controller / primary DNS (not NixOS-managed)
|
||||||
|
ipaServer = "domain-controller.sweet.home"; # FreeIPA server hostname (used by security.ipa and Kerberos; must be a resolvable FQDN, not an IP)
|
||||||
|
|
||||||
# Cross-host references (LAN hostnames/users other hosts reach over the network)
|
# Cross-host references (LAN hostnames/users other hosts reach over the network)
|
||||||
nixCacheHost = "nix-cache"; # substituter/remote-builder hostname
|
nixCacheHost = "nix-cache"; # substituter/remote-builder hostname
|
||||||
|
|||||||
Reference in New Issue
Block a user