inital config

This commit is contained in:
Joshua Falkenberg
2026-09-10 13:31:28 +02:00
commit d2ab7241e4
18 changed files with 553 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
# Nix build results
result
result-*
# direnv
.direnv
.envrc
# Secrets / private keys
*.key
*.pem
*.agekey
AGE-SECRET-KEY-*
*.secret
.sops.yaml
# Local/private hardware information
*.local
# macOS
.DS_Store
# Editors
.vscode/
.idea/
.zed/
+14
View File
@@ -0,0 +1,14 @@
keys:
- &yubikey_14321697 age1yubikey1q26trpwnpym6t9m5qlcfm00kddy3tzex7u8r6ck7sqkq39wxc5wwwafuq89
- &yubikey_32464919 age1yubikey1qgvu222dwg2gtvc4mq2txk79t4ep5gk9qwgfh520lc3m4kyxjmtgzexyljy
- &recovery_bitwarden age1r993gvlratqk5h5seuzrd8fjv356csnrvtdt5ugcfdu3cw66hdls9gtuwd
- &host AGE_HOST_D
creation_rules:
- path_regex: secrets/.*\.yaml$
key_groups:
- age:
- *yubikey_14321697
- *yubikey_32464919
- *recovery_bitwarden
- *host
+24
View File
@@ -0,0 +1,24 @@
# NixOS Config
TODO...
## Neuinstallation
### X. Nach der Installation
#### X.X. Bootstrap-Skript ausführen
Nach der Installation ist es notwendig das Bootstrap-Skript auszuführen.
Dafür muss zunächst der Key ausgewählt werden (Yubikey oder Bitwarden):
```bash
sudo env SOPS_AGE_KEY_FILE=/home/user/nixos-config/age/yubikey-14321697.txt /home/user/nixos-config/scripts/bootstrap-host.sh
```
```bash
sudo env SOPS_AGE_KEY_FILE=/home/user/nixos-config/age/yubikey-32464919.txt /home/user/nixos-config/scripts/bootstrap-host.sh
```
Für den Recovery-Key aus Bitwarden, muss dieser zunächst exportiert werden und als .txt Datei gespeichert werden.
```bash
sudo env SOPS_AGE_KEY_FILE=/path/to/recovery-keyfile.txt /home/user/nixos-config/scripts/bootstrap-host.sh
```
+9
View File
@@ -0,0 +1,9 @@
# Serial: 14321697, Slot: 1
# Name: age identity 34bb9100
# Created: Thu, 10 Sep 2026 06:31:58 +0000
# PIN policy: Once (A PIN is required once per session, if set)
# Touch policy: Always (A physical touch is required for every decryption)
# Recipient: age1yubikey1q26trpwnpym6t9m5qlcfm00kddy3tzex7u8r6ck7sqkq39wxc5wwwafuq89
AGE-PLUGIN-YUBIKEY-1YXYD5QYZXJAEZQQF8YHJ5
+9
View File
@@ -0,0 +1,9 @@
# Serial: 32464919, Slot: 1
# Name: age identity 1b6d4f63
# Created: Thu, 10 Sep 2026 06:46:10 +0000
# PIN policy: Once (A PIN is required once per session, if set)
# Touch policy: Always (A physical touch is required for every decryption)
# Recipient: age1yubikey1qgvu222dwg2gtvc4mq2txk79t4ep5gk9qwgfh520lc3m4kyxjmtgzexyljy
AGE-PLUGIN-YUBIKEY-1ZASW7QVZRDK57CCKHC0YW
+49
View File
@@ -0,0 +1,49 @@
{
description = "Declarative NixOS desktop configuration";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
impermanence.url = "github:nix-community/impermanence";
sops-nix.url = "github:Mic92/sops-nix";
disko.url = "github:nix-community/disko";
};
outputs =
{
nixpkgs,
home-manager,
impermanence,
sops-nix,
disko,
...
}:
{
nixosConfigurations.desktop = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
disko.nixosModules.disko
impermanence.nixosModules.impermanence
sops-nix.nixosModules.sops
home-manager.nixosModules.home-manager
./hosts/desktop
./modules/base.nix
./modules/boot.nix
./modules/ephemeral-root.nix
./modules/impermanence.nix
./modules/persistence.nix
./modules/secrets.nix
./modules/desktop.nix
];
};
};
}
+16
View File
@@ -0,0 +1,16 @@
{ pkgs, ... }:
{
home.stateVersion = "25.11";
programs.home-manager.enable = true;
programs.git = {
enable = true;
};
home.packages = with pkgs; [
age
sops
];
}
+30
View File
@@ -0,0 +1,30 @@
{ ... }:
{
imports = [
./disko.nix
];
networking.hostName = "desktop";
users.users.user = {
isNormalUser = true;
home = "/home/user";
extraGroups = [
"wheel"
"networkmanager"
];
};
security.sudo.wheelNeedsPassword = false;
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
users.user = import ../../home/user.nix;
};
system.stateVersion = "25.11";
}
+60
View File
@@ -0,0 +1,60 @@
{
disko.devices.disk.main = {
type = "disk";
device = "/dev/disk/by-id/ata-Vi550_S3_4935445284600580";
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
luks = {
size = "100%";
label="luks";
content = {
type = "luks";
name = "cryptroot";
settings = {
allowDiscards = true;
};
content = {
type = "btrfs";
subvolumes = {
"/@nix" = {
mountpoint = "/nix";
mountOptions = [
"compress=zstd"
"noatime"
];
};
"/@persist" = {
mountpoint = "/persist";
mountOptions = [
"compress=zstd"
"noatime"
];
};
};
};
};
};
};
};
};
}
+29
View File
@@ -0,0 +1,29 @@
{ pkgs, ... }:
{
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
nixpkgs.config.allowUnfree = true;
networking.networkmanager.enable = true;
time.timeZone = "Europe/Berlin";
i18n.defaultLocale = "de_DE.UTF-8";
console.keyMap = "de";
environment.systemPackages = with pkgs; [
git
vim
curl
wget
htop
tree
];
programs.git.enable = true;
}
+24
View File
@@ -0,0 +1,24 @@
{ ... }:
{
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.plymouth.enable = true;
boot.initrd.systemd.enable = true;
boot.kernelParams = [
"quiet"
"splash"
];
boot.consoleLogLevel = 3;
boot.initrd.verbose = false;
boot.initrd.luks.devices."cryptroot" = {
device = "/dev/disk/by-partlabel/luks";
};
fileSystems."/".device = "/dev/mapper/cryptroot";
}
+7
View File
@@ -0,0 +1,7 @@
{ ... }:
{
hardware.graphics = {
enable = true;
};
}
+16
View File
@@ -0,0 +1,16 @@
{ ... }:
{
fileSystems."/" = {
device = "tmpfs";
fsType = "tmpfs";
options = [
"mode=755"
"nosuid"
"nodev"
];
neededForBoot = true;
};
}
+4
View File
@@ -0,0 +1,4 @@
{ ... }:
{
}
+25
View File
@@ -0,0 +1,25 @@
{ ... }:
{
fileSystems."/persist".neededForBoot = true;
environment.persistence."/persist" = {
hideMounts = true;
directories = [
{
directory = "/home/user/nixos-config";
user = "user";
group = "users";
}
"/var/lib/nixos"
];
files = [
"/etc/machine-id"
"/etc/ssh/ssh_host_ed25519_key"
"/etc/ssh/ssh_host_ed25519_key.pub"
];
};
}
+5
View File
@@ -0,0 +1,5 @@
{ ... }:
{
sops.age.keyFile = "/persist/keys/host-age.key";
}
+168
View File
@@ -0,0 +1,168 @@
#!/usr/bin/env bash
set -euo pipefail
REPO_DIR="/home/user/nixos-config"
PERSIST_DIR="/persist"
KEY_DIR="${PERSIST_DIR}/keys"
HOST_KEY="${KEY_DIR}/host-age.key"
SOPS_TEMPLATE="${REPO_DIR}/.sops.yaml.template"
LOCAL_SOPS="${REPO_DIR}/.sops.yaml"
echo "==> NixOS host bootstrap"
if [[ "${EUID}" -ne 0 ]]; then
echo "ERROR: Dieses Script muss als root ausgeführt werden."
exit 1
fi
if ! command -v age-keygen >/dev/null 2>&1; then
echo "ERROR: age-keygen wurde nicht gefunden."
exit 1
fi
if ! command -v sops >/dev/null 2>&1; then
echo "ERROR: sops wurde nicht gefunden."
exit 1
fi
if [[ -z "${SOPS_AGE_KEY_FILE:-}" ]]; then
echo "ERROR: SOPS_AGE_KEY_FILE ist nicht gesetzt."
echo
echo "Beispiel:"
echo
echo " SOPS_AGE_KEY_FILE=${REPO_DIR}/age/yubikey-a.txt \\"
echo " sudo -E ${REPO_DIR}/scripts/bootstrap-host.sh"
exit 1
fi
if [[ ! -f "${SOPS_AGE_KEY_FILE}" ]]; then
echo "ERROR: SOPS_AGE_KEY_FILE existiert nicht:"
echo " ${SOPS_AGE_KEY_FILE}"
exit 1
fi
if ! mountpoint -q "${PERSIST_DIR}"; then
echo "ERROR: ${PERSIST_DIR} ist nicht gemountet."
exit 1
fi
if [[ ! -d "${REPO_DIR}" ]]; then
echo "ERROR: Repository fehlt:"
echo " ${REPO_DIR}"
exit 1
fi
if [[ ! -f "${SOPS_TEMPLATE}" ]]; then
echo "ERROR: SOPS template fehlt:"
echo " ${SOPS_TEMPLATE}"
exit 1
fi
if [[ -e "${HOST_KEY}" ]]; then
echo "ERROR: Host-Key existiert bereits:"
echo " ${HOST_KEY}"
echo
echo "Es wird nichts überschrieben."
exit 1
fi
if [[ -e "${LOCAL_SOPS}" ]]; then
echo "ERROR: Lokale .sops.yaml existiert bereits:"
echo " ${LOCAL_SOPS}"
echo
echo "Es wird nichts überschrieben."
exit 1
fi
echo
echo "==> SOPS identity:"
echo " ${SOPS_AGE_KEY_FILE}"
echo
echo "==> Erzeuge Host-age-Key"
install -d -m 0700 "${KEY_DIR}"
age-keygen -o "${HOST_KEY}"
chmod 0600 "${HOST_KEY}"
chown root:root "${HOST_KEY}"
HOST_RECIPIENT="$(age-keygen -y "${HOST_KEY}")"
echo
echo "==> Host recipient:"
echo
echo " ${HOST_RECIPIENT}"
echo
echo "==> Erzeuge lokale .sops.yaml"
sed "s/AGE_HOST_D/${HOST_RECIPIENT}/g" \
"${SOPS_TEMPLATE}" > "${LOCAL_SOPS}"
chmod 0600 "${LOCAL_SOPS}"
echo
echo "============================================================"
echo "Der Host-Key wurde erzeugt."
echo
echo "Private Key:"
echo " ${HOST_KEY}"
echo
echo "Lokale SOPS-Konfiguration:"
echo " ${LOCAL_SOPS}"
echo
echo "Jetzt werden die vorhandenen Secrets auf"
echo "YubiKey A + YubiKey B + Recovery + Host"
echo "rekeyed."
echo "============================================================"
echo
read -r -p "Rekeying jetzt durchführen? [y/N] " answer
if [[ "${answer}" != "y" && "${answer}" != "Y" ]]; then
echo
echo "Abgebrochen."
echo
echo "Der Host-Key und .sops.yaml wurden bereits erzeugt."
echo "Das Rekeying wurde NICHT durchgeführt."
exit 0
fi
shopt -s nullglob
secret_files=( "${REPO_DIR}"/secrets/*.yaml )
shopt -u nullglob
if [[ "${#secret_files[@]}" -eq 0 ]]; then
echo
echo "Keine secrets/*.yaml gefunden."
echo "Host-Key wurde erfolgreich erzeugt."
exit 0
fi
echo
echo "==> Rekeye Secrets"
for secret in "${secret_files[@]}"; do
echo
echo "Updating:"
echo " ${secret}"
SOPS_AGE_KEY_FILE="${SOPS_AGE_KEY_FILE}" \
sops updatekeys --yes "${secret}"
done
echo
echo "==> Bootstrap abgeschlossen."
echo
echo "Host recipient:"
echo " ${HOST_RECIPIENT}"
echo
echo "Host key:"
echo " ${HOST_KEY}"
echo
echo "SOPS config:"
echo " ${LOCAL_SOPS}"
+38
View File
@@ -0,0 +1,38 @@
test:
message: ENC[AES256_GCM,data:eF4xvJYIkGg1,iv:CVf9AA5hjPBq+wZLpOjmcOZhve2Tsyk/eULXdOp9Q/s=,tag:BrRYXcHOuftG0Vap4bK4ig==,type:str]
number: ENC[AES256_GCM,data:M8M=,iv:hgv/5PHEIS1ENu2UGMsKMc+liHMjEafd6ITyZvpGnb8=,tag:Xs/cUc/Ny17Ka8qtQHWMUw==,type:int]
sops:
age:
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IE5MdVJBQSBBOStPcWx0
YkhRNkFKNmhNeHlvQWFYRDJ2UmNMR2lyTWZEV3MrbldmSWd1Sgp4a0FidVJwZ2tv
V2JlZjRQT05xZlpoTEFiVUdKVTRVemx3a0dnTm9MRENjCi0tLSBQMGE0aXVQRFQx
NjVEWEtNcldHUWlXbmhQRWxXdzk5bzd1RjNmaFdRS1Z3Crl/J6U3WSvIZRreNIXO
aTqjTZaahbHjX4pmkDRmSd2o5x1oNksutIoWO5l32tX8SEU3quDj9bDymZIJBp8X
86g=
-----END AGE ENCRYPTED FILE-----
recipient: age1yubikey1q26trpwnpym6t9m5qlcfm00kddy3tzex7u8r6ck7sqkq39wxc5wwwafuq89
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IEcyMVBZdyBBdktHQ0px
ZVBMNDhteElxVXlUczd5K2I5K25iejNCOEZkTjFDRkwzcUlJWAptRmlpazhsbkY5
QnRhamtXR1JObFlTY1FjZ1hIUmxzTTZEYXVrZzlaY2lBCi0tLSBSUlAxOFZlcTFn
cEFRR2tIT2liSVAxaHhkcWs4MlQ1cVBTeW9vUm9PcW5VCpQ82CSSMYHMK5Su/An9
o6Ybb//z92Xm1fbuCv/1E/yQg12sOClbsJvNPWbwynoWkAmVzureuDzXNgIsv2oA
Mt4=
-----END AGE ENCRYPTED FILE-----
recipient: age1yubikey1qgvu222dwg2gtvc4mq2txk79t4ep5gk9qwgfh520lc3m4kyxjmtgzexyljy
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzMWthMjdFTElpbzcvcFFQ
UzdzbUtpSkVQQWpWTVpxNUcwamlqOE5Nd2hVCkZNeUdvOVF0eWxwZ2F5WlY0UHNv
QlFlNUdRSlh6M0xub1dUNkNTWVdxZzQKLS0tIGNQcGdzazZTRll1MEhuZzlxZUM0
UEdvV0xsRzRRRUZxZ0NsUFpUSEpKZ2sKQxxSJOQen+whTB4awc7EGSUJO/CWmlOc
Srryah6025tEfH+scMxYJcpSMKrQlQ778yvK92xxSJbFgoQi0sReoA==
-----END AGE ENCRYPTED FILE-----
recipient: age1r993gvlratqk5h5seuzrd8fjv356csnrvtdt5ugcfdu3cw66hdls9gtuwd
lastmodified: "2026-09-10T08:56:29Z"
mac: ENC[AES256_GCM,data:YOEymqQFlK/4iaQpcA3e7OtuTnqz/52iS+TAc2sSIdKP2TV9NG6tr32Fn66AQrjFfYmfY81LIYFRnqRNV1IH9rccNTvxxklF6wKCz8p0/ESqpKbDEaNsoRzaTHCNRvDu3t8GvrxudDtRXQtQ94yx/WCk/M7XHQuY0LbagJv0Jm0=,iv:AIyGRpYuAsxaD2Rm/CZMUCLEkHCR3ImvJ/GoRwG9FAk=,tag:Fr5T15lfHUv1cIvJmXwZkA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.3