feat: initial commit
This commit is contained in:
commit
a28c2b5563
19 changed files with 314 additions and 0 deletions
6
.sops.yaml
Normal file
6
.sops.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
creation_rules:
|
||||
- path_regex: ^secrets/.*(?:$|\.(ya?ml|json|env|txt|key|pub))$
|
||||
key_groups:
|
||||
- age:
|
||||
- age1g5q3hwnpgsas682jkq0zmee3zqggucfe0v5ec0a6pv7wzexadehqne66cj
|
||||
- age1z7ee2jgwvnrx6dg96rqzhc7gncuancrhm8zaehkelwqkeh4dze7qlsajes
|
||||
7
config/openssh.nix
Normal file
7
config/openssh.nix
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
let
|
||||
secrets = import ../intermediate/secrets.nix;
|
||||
users = builtins.attrNames secrets.source.openssh.users;
|
||||
in
|
||||
rec {
|
||||
ssh_users = users;
|
||||
}
|
||||
15
config/secrets.nix
Normal file
15
config/secrets.nix
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
openssh = {
|
||||
users = {
|
||||
nudelerde = {
|
||||
pub_keys = {
|
||||
file = ../secrets/openssh/nudelerde/pub_keys;
|
||||
path = "/home/nudelerde/.ssh/authorized_keys";
|
||||
owner = "nudelerde";
|
||||
group = "users";
|
||||
mode = "0600";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
6
config/services.nix
Normal file
6
config/services.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
rec {
|
||||
git = {
|
||||
port = 3000;
|
||||
domain = "git.nudelerde.de";
|
||||
};
|
||||
}
|
||||
18
configuration.nix
Normal file
18
configuration.nix
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{ pkgs, ... }:
|
||||
let
|
||||
sopsNixVersion = "8f093d0d2f08f37317778bd94db5951d6cce6c46";
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
"${builtins.fetchTarball "https://github.com/Mic92/sops-nix/archive/${sopsNixVersion}.tar.gz"}/modules/sops"
|
||||
./hardware-configuration.nix
|
||||
./system
|
||||
./services
|
||||
./programs
|
||||
];
|
||||
|
||||
environment.systemPackages = with pkgs; [ wget tree ];
|
||||
|
||||
system.stateVersion = "25.11";
|
||||
}
|
||||
|
||||
25
hardware-configuration.nix
Normal file
25
hardware-configuration.nix
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Do not modify this file! It was generated by ‘nixos-generate-config’
|
||||
# and may be overwritten by future invocations. Please make changes
|
||||
# to /etc/nixos/configuration.nix instead.
|
||||
{ config, lib, pkgs, modulesPath, ... }:
|
||||
|
||||
{
|
||||
imports =
|
||||
[ (modulesPath + "/installer/scan/not-detected.nix")
|
||||
];
|
||||
|
||||
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "sd_mod" "rtsx_usb_sdmmc" ];
|
||||
boot.initrd.kernelModules = [ ];
|
||||
boot.kernelModules = [ ];
|
||||
boot.extraModulePackages = [ ];
|
||||
|
||||
fileSystems."/" =
|
||||
{ device = "/dev/disk/by-uuid/be213f84-29a2-400e-b239-c339eddb6107";
|
||||
fsType = "ext4";
|
||||
};
|
||||
|
||||
swapDevices = [ ];
|
||||
|
||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
||||
}
|
||||
79
intermediate/secrets.nix
Normal file
79
intermediate/secrets.nix
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
let
|
||||
lib = import <nixpkgs/lib>;
|
||||
secretsConfig = (import ../validation/secrets.nix).getSecretsConfig (import ../config/secrets.nix);
|
||||
|
||||
getRuntimePath = path:
|
||||
"/run/secrets/${builtins.concatStringsSep "_" path}";
|
||||
|
||||
defaultMetadata = {
|
||||
path = null;
|
||||
owner = null;
|
||||
group = null;
|
||||
mode = null;
|
||||
};
|
||||
|
||||
normalizeLeaf = path: node:
|
||||
if builtins.isString node || builtins.isPath node then
|
||||
{
|
||||
file = node;
|
||||
metadata = defaultMetadata;
|
||||
}
|
||||
else if builtins.isAttrs node && node ? file then
|
||||
{
|
||||
file = node.file;
|
||||
metadata = {
|
||||
path = node.path or null;
|
||||
owner = node.owner or null;
|
||||
group = node.group or null;
|
||||
mode = node.mode or null;
|
||||
};
|
||||
}
|
||||
else
|
||||
throw "Invalid secret leaf at ${builtins.concatStringsSep "." path}: must be string, path, or attrset with 'file'";
|
||||
|
||||
flattenTree = path: node:
|
||||
if builtins.isAttrs node && !(node ? file) then
|
||||
lib.concatMap (name:
|
||||
flattenTree (path ++ [ name ]) node.${name}
|
||||
) (builtins.attrNames node)
|
||||
else
|
||||
let
|
||||
normalized = normalizeLeaf path node;
|
||||
in
|
||||
[ {
|
||||
inherit path;
|
||||
file = normalized.file;
|
||||
metadata = normalized.metadata;
|
||||
} ];
|
||||
|
||||
entries = flattenTree [] secretsConfig;
|
||||
|
||||
isReady = entry:
|
||||
builtins.pathExists entry.file;
|
||||
|
||||
readyEntries = builtins.filter isReady entries;
|
||||
missingEntries = builtins.filter (entry: !(isReady entry)) entries;
|
||||
|
||||
mkSopsSecrets = sourceEntries:
|
||||
builtins.listToAttrs (map (entry:
|
||||
let
|
||||
secretName = builtins.concatStringsSep "_" entry.path;
|
||||
in
|
||||
{
|
||||
name = secretName;
|
||||
value = {
|
||||
sopsFile = entry.file;
|
||||
format = "binary";
|
||||
path = if entry.metadata.path != null then entry.metadata.path else getRuntimePath entry.path;
|
||||
owner = if entry.metadata.owner != null then entry.metadata.owner else "root";
|
||||
group = if entry.metadata.group != null then entry.metadata.group else "root";
|
||||
mode = if entry.metadata.mode != null then entry.metadata.mode else "0400";
|
||||
};
|
||||
}
|
||||
) sourceEntries);
|
||||
in
|
||||
{
|
||||
source = secretsConfig;
|
||||
byName = mkSopsSecrets readyEntries;
|
||||
missing = missingEntries;
|
||||
}
|
||||
6
programs/default.nix
Normal file
6
programs/default.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./git.nix
|
||||
];
|
||||
}
|
||||
16
programs/git.nix
Normal file
16
programs/git.nix
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
programs.git = {
|
||||
enable = true;
|
||||
config = {
|
||||
user = {
|
||||
name = "Katharina Heidenreich";
|
||||
email = "katharina.heidenreich02@gmail.com";
|
||||
};
|
||||
init.defaultBranch = "main";
|
||||
pull.rebase = false;
|
||||
core.editor = "nano";
|
||||
safe.directory = ["/etc/nixos"];
|
||||
};
|
||||
};
|
||||
}
|
||||
18
secrets/openssh/nudelerde/pub_keys
Normal file
18
secrets/openssh/nudelerde/pub_keys
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"data": "ENC[AES256_GCM,data:D+pLLrpjFljWBUJ4qZ8qo62Ff+nRPw8IVohCMqY7m6BcBlK90AH2AN0I34ci6rPh3F2XenmL51kMReAHlV8BuuXCDxDB37lIvzVagvfla9sBMx1i8wINFV+DnpL0w78=,iv:0+11qTje7wLSDlCOsTyjKQ0d6Drt1zYkDxL8T2bxvg4=,tag:QD/LxHmPjnZo1XS6/0zLmA==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1g5q3hwnpgsas682jkq0zmee3zqggucfe0v5ec0a6pv7wzexadehqne66cj",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3WER1bnJiMDNpK2hTOXhV\nV2g2TEFWTzBtMDFLOGtMb2doV3Q2eENlaHpVCktOa25QU1pNMStGVGtoZ04wZXB6\nVnBncTNURktYTFZUczBoRlhacFRYcUkKLS0tIG5FR3gvWmg4ZWtuUDRYZDBRU3lL\nU3NLeEVwQi9xdWF3MFYwTkZvcURMWmcKqtxEB+rHGQ7/RKd2CVTuJl/eDrGc9xrG\nou/4lCYVQL62EE/iOEsyTrzQhu3kKIhRdqICRz0R2b5fr0RLVRymWw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1z7ee2jgwvnrx6dg96rqzhc7gncuancrhm8zaehkelwqkeh4dze7qlsajes",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGMk1DUFpjdzF0RFhXR0NU\nV293cUY5RlhFYzZhWjJNaFZvbXExRUVtRWkwCjVBMWlnVzdyTTVBZDE0U0g2SXhV\nK2pCK0dJVWZmN0QwUkpqUE44QjdOTGcKLS0tIHR5bUxlS0R3VUVTZzN4bSs0NVp3\nQkVxMjdQNnBOaUtKUTAzWElvb3V3RjAKQYSjLzdGqHoCjyQfgHdR+knIAFS/LY7a\nZLOMKJXjPcdEDALnRQJS0ys7dkDebCABizWeJX1fCERkzVANv1UhyQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2026-04-08T18:01:44Z",
|
||||
"mac": "ENC[AES256_GCM,data:NlhzAXFHEVcXukSNRLAIJxWQ8GtYf0w/U3zkiDpH64fQs1yHjeRvF4BvQp83938jR5jkjVVdHwwXjPacztv+6zMzaMchUGglbc5dEx7OeLFmbM3gL8Q69pDrFjKo9g379pHH76JM3RWJLg+EaN2TDEU72fD9VtR9ak9V2/X4xfM=,iv:lwClzYes6Zu0aK8qVLgZ9qHgX+k/j3sNPqMamOnd7dg=,tag:BwIE4TBJEQpyBHN+Ivg0Dw==,type:str]",
|
||||
"version": "3.12.1"
|
||||
}
|
||||
}
|
||||
7
services/default.nix
Normal file
7
services/default.nix
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./openssh.nix
|
||||
./forgejo.nix
|
||||
];
|
||||
}
|
||||
22
services/forgejo.nix
Normal file
22
services/forgejo.nix
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{...}:
|
||||
let
|
||||
serv = import ../config/services.nix;
|
||||
git = serv.git;
|
||||
in
|
||||
{
|
||||
services.forgejo = {
|
||||
enable = true;
|
||||
database.type = "postgres";
|
||||
lfs.enable = true;
|
||||
settings = {
|
||||
server = {
|
||||
DOMAIN = git.domain;
|
||||
ROOT_URL = "https://${git.domain}/";
|
||||
HTTP_PORT = git.port;
|
||||
};
|
||||
service.DISABLE_REGISTRATION = true;
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ git.port ];
|
||||
}
|
||||
17
services/openssh.nix
Normal file
17
services/openssh.nix
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{ ... }:
|
||||
let
|
||||
opensshConfig = import ../config/openssh.nix;
|
||||
usersWithKeys = opensshConfig.ssh_users;
|
||||
in
|
||||
{
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings = {
|
||||
PasswordAuthentication = true;
|
||||
PermitRootLogin = "no";
|
||||
AllowUsers = usersWithKeys;
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 22 ];
|
||||
}
|
||||
5
system/boot.nix
Normal file
5
system/boot.nix
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{ ... }:
|
||||
{
|
||||
boot.loader.grub.enable = true;
|
||||
boot.loader.grub.device = "/dev/sda";
|
||||
}
|
||||
9
system/default.nix
Normal file
9
system/default.nix
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./sops.nix
|
||||
./users.nix
|
||||
./network.nix
|
||||
./boot.nix
|
||||
];
|
||||
}
|
||||
5
system/network.nix
Normal file
5
system/network.nix
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{ ... }:
|
||||
{
|
||||
networking.hostName = "tuserver";
|
||||
networking.networkmanager.enable = true;
|
||||
}
|
||||
14
system/sops.nix
Normal file
14
system/sops.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{ lib, ... }:
|
||||
|
||||
let
|
||||
secretData = import ../intermediate/secrets.nix;
|
||||
in
|
||||
{
|
||||
sops = {
|
||||
age.keyFile = "/var/lib/sops-nix/key.txt";
|
||||
secrets = secretData.byName;
|
||||
};
|
||||
|
||||
warnings = lib.optional (secretData.missing != [])
|
||||
"Some SOPS source files are missing or not yet encrypted; no runtime secrets will be provisioned for: ${builtins.concatStringsSep ", " (map (item: builtins.concatStringsSep "_" item.path) secretData.missing)}";
|
||||
}
|
||||
10
system/users.nix
Normal file
10
system/users.nix
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{ ... }:
|
||||
{
|
||||
users.mutableUsers = false;
|
||||
|
||||
users.users.nudelerde = {
|
||||
isNormalUser = true;
|
||||
extraGroups = [ "wheel" ];
|
||||
hashedPassword = "$y$j9T$nWURcrCMWKPzj1xAydtNU/$qbOuwWcLSWQBiDTw8WJ2sRZYtP7qnGShQDA2USRC/C0";
|
||||
};
|
||||
}
|
||||
29
validation/secrets.nix
Normal file
29
validation/secrets.nix
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
let
|
||||
lib = import <nixpkgs/lib>;
|
||||
helperNames = [ "getSecretsConfig" ];
|
||||
|
||||
validateTree = context: node:
|
||||
if builtins.isAttrs node then
|
||||
lib.mapAttrs (name: value:
|
||||
if lib.elem name helperNames then
|
||||
value
|
||||
else
|
||||
validateTree "${context}.${name}" value
|
||||
) node
|
||||
else
|
||||
throw "${context} must be an attrset at non-leaf level.";
|
||||
|
||||
getSecretsConfig = secretsConfig:
|
||||
let
|
||||
_ =
|
||||
if builtins.isAttrs secretsConfig then
|
||||
null
|
||||
else
|
||||
throw "config/secrets.nix must evaluate to an attrset.";
|
||||
__ = validateTree "config/secrets.nix" secretsConfig;
|
||||
in
|
||||
secretsConfig;
|
||||
in
|
||||
rec {
|
||||
inherit getSecretsConfig validateTree;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue