Compare commits

..

143 Commits

Author SHA1 Message Date
John Lancaster
1d3847ec12 adjusted timings 2026-03-17 13:26:52 -05:00
John Lancaster
f3b503fc9e broke out the password file 2026-03-17 13:05:23 -05:00
John Lancaster
d9f1d9593b added to soteria 2026-03-17 13:05:12 -05:00
John Lancaster
6a7e78a19e started restic rest server 2026-03-17 13:05:02 -05:00
John Lancaster
61c1c5d80e prune 2026-03-17 13:00:47 -05:00
John Lancaster
754e7cc1a0 moved some excludes 2026-03-17 12:52:14 -05:00
John Lancaster
d9fcbe68ad restic mtls opts 2026-03-17 12:43:40 -05:00
John Lancaster
f8c40ff627 restic env vars 2026-03-17 11:57:40 -05:00
John Lancaster
e61bec46d2 customized timing 2026-03-16 14:18:37 -05:00
John Lancaster
27c8f6d86d added lifetime option 2026-03-16 12:39:51 -05:00
John Lancaster
ab5bda0c37 passing thru args in mtls-generate 2026-03-16 12:30:20 -05:00
John Lancaster
5fb80498b5 generalized mtls-renew script 2026-03-16 12:28:05 -05:00
John Lancaster
7b258b3eb9 prune 2026-03-16 12:12:20 -05:00
John Lancaster
a92fd22c65 indentation 2026-03-16 12:05:06 -05:00
John Lancaster
4af0cf7ca7 added mk functions for home manager side 2026-03-16 12:04:23 -05:00
John Lancaster
3af6ab0819 case structure 2026-03-16 08:37:40 -05:00
John Lancaster
2231c5910c broke out systemd service definitions 2026-03-16 08:27:33 -05:00
John Lancaster
853fe3c556 added mtls renewal service to motd 2026-03-16 08:27:09 -05:00
John Lancaster
1d210457b5 tweaked login-text 2026-03-16 08:23:04 -05:00
John Lancaster
acbd86f589 added restic user options 2026-03-16 08:02:28 -05:00
John Lancaster
30c4a648ff prune 2026-03-16 08:00:34 -05:00
John Lancaster
3f743280ee changing to real restic service 2026-03-15 23:08:24 -05:00
John Lancaster
621dda40eb changed certs dir 2026-03-15 22:09:12 -05:00
John Lancaster
e4767ad30d broke out certDir for home manager module 2026-03-15 22:03:42 -05:00
John Lancaster
e72b27e59d mtls home manager module 2026-03-15 21:27:44 -05:00
John Lancaster
67688c2aa6 added mtls renewal service 2026-03-15 21:12:03 -05:00
John Lancaster
3e2ad120fe provisioner option 2026-03-15 21:05:38 -05:00
John Lancaster
e38689fb82 disabled ssh user cert for janus 2026-03-15 21:03:20 -05:00
John Lancaster
ba72aec338 SAN args 2026-03-15 21:02:09 -05:00
John Lancaster
a8a9a73e08 working mtls for janus system 2026-03-15 20:52:34 -05:00
John Lancaster
3800ae7502 mtls options 2026-03-15 20:32:49 -05:00
John Lancaster
8a95c9f27a flake.lock update 2026-03-15 20:20:39 -05:00
John Lancaster
9466238db9 moved specifics 2026-03-15 20:19:41 -05:00
John Lancaster
dfefb9682f options organization 2026-03-15 20:18:01 -05:00
John Lancaster
832149305b commonized root CA pattern 2026-03-15 20:14:23 -05:00
John Lancaster
f9a8ad47e3 moved step-ssh-host service 2026-03-15 20:10:34 -05:00
John Lancaster
ddc4b4e5a4 added keep monthly 2026-03-15 18:47:06 -05:00
John Lancaster
0830a8d0a4 fixed rp-test 2026-03-15 18:46:27 -05:00
John Lancaster
e83f6939e8 started mtls stuff 2026-03-15 18:15:45 -05:00
John Lancaster
f5ae40c3e7 added ssh host cert renewal 2026-03-15 17:05:58 -05:00
John Lancaster
cd13e56e15 added login-text to janus 2026-03-15 16:54:48 -05:00
John Lancaster
3339cd6b0c removed default for principals option 2026-03-15 16:21:02 -05:00
John Lancaster
6315ac0143 provisioner explicitly defined 2026-03-15 16:16:59 -05:00
John Lancaster
b3bcfdcfcb step-ssh-user 2026-03-15 16:15:27 -05:00
John Lancaster
2ace9cd2dd formatting 2026-03-15 16:15:17 -05:00
John Lancaster
8735ef24d5 hostKeyFile variable 2026-03-15 15:52:45 -05:00
John Lancaster
85a1127e1d userCAPath variable 2026-03-15 15:51:09 -05:00
John Lancaster
9c26c962ff slight reorg 2026-03-15 15:44:24 -05:00
John Lancaster
da2de12193 moved sign-ssh-cert 2026-03-15 15:20:34 -05:00
John Lancaster
ff9a817ef8 better known_hosts 2026-03-15 15:06:48 -05:00
John Lancaster
ec501e3029 sign ssh cert working 2026-03-15 14:58:23 -05:00
John Lancaster
e1b093929c ssh certificates on desktop 2026-03-15 14:45:33 -05:00
John Lancaster
cdbfeb101d added check 2026-03-15 12:53:22 -05:00
John Lancaster
497b6f1f78 generating ssh user CA file 2026-03-15 12:48:35 -05:00
John Lancaster
52390daf45 ssh host cert working 2026-03-15 11:22:09 -05:00
John Lancaster
ec79dc824e WIP defaults.json 2026-03-15 09:57:47 -05:00
John Lancaster
fee012cee3 added jq 2026-03-14 14:09:37 -05:00
John Lancaster
cfc7f9d138 added janus root ca fingerprint 2026-03-14 12:46:55 -05:00
John Lancaster
9477330721 fixed live edit-secrets 2026-03-14 12:46:22 -05:00
John Lancaster
1d084311a3 enabled bash on LXCs 2026-03-14 12:46:09 -05:00
John Lancaster
ab3e1da951 prune 2026-03-14 12:21:52 -05:00
John Lancaster
6e319ffc9c janus updates 2026-03-14 12:13:17 -05:00
John Lancaster
e1bfcb825f formatting 2026-03-14 12:10:57 -05:00
John Lancaster
0606e7c01b added nixos.sops module 2026-03-14 12:09:59 -05:00
John Lancaster
788fdbdf91 prune 2026-03-14 11:57:53 -05:00
John Lancaster
4f3976a979 fixed sops-nix on host system 2026-03-14 11:41:44 -05:00
John Lancaster
d60a52edda prune 2026-03-14 11:38:51 -05:00
John Lancaster
fe71c9c3eb variable update 2026-03-14 11:37:37 -05:00
John Lancaster
8568129ba7 added test-nix host private key 2026-03-12 14:08:18 -05:00
John Lancaster
026f413e7b started SSH certs 2026-03-12 13:22:40 -05:00
John Lancaster
3bbe6d359e no password for sudo if admin 2026-03-12 12:49:02 -05:00
John Lancaster
9a22aba03a added sudo and step-client modules 2026-03-12 12:25:58 -05:00
John Lancaster
1825230029 testTarget variable 2026-03-12 11:31:29 -05:00
John Lancaster
5c7c0ab473 formatting 2026-03-12 11:31:20 -05:00
John Lancaster
c0b20ece01 test-nix update 2026-03-12 09:41:15 -05:00
John Lancaster
cae2ce81f4 switched test script 2026-03-12 09:27:43 -05:00
John Lancaster
cd14a37c8c nhms works again 2026-03-11 22:06:46 -05:00
John Lancaster
b4d74f8a8d removed function 2026-03-11 21:07:18 -05:00
John Lancaster
0daa3e8d15 genericLinux enable 2026-03-11 20:18:48 -05:00
John Lancaster
9a8f98d4fe moved imports 2026-03-11 18:50:16 -05:00
John Lancaster
30ffa5f234 moved base import 2026-03-11 18:35:59 -05:00
John Lancaster
c3e80bb91a commented out certs for restic 2026-03-11 18:31:41 -05:00
John Lancaster
7a5c96284f table 2026-03-11 18:30:04 -05:00
John Lancaster
ee1b2aafba reading base-profile.yaml into config 2026-03-11 18:02:00 -05:00
John Lancaster
cf2ba8731d reorg 2026-03-11 08:49:07 -05:00
John Lancaster
dac3b84ffb space optimization 2026-03-11 01:11:11 -05:00
John Lancaster
a26814ea6a flakeDir 2026-03-11 01:09:29 -05:00
John Lancaster
69b0388dbd added nix-ld 2026-03-11 00:56:26 -05:00
John Lancaster
ed8b9cf439 formatting 2026-03-11 00:56:16 -05:00
John Lancaster
95b35a509a added hostname to script 2026-03-11 00:44:23 -05:00
John Lancaster
7f8a8e0f49 started break out of zsh 2026-03-11 00:43:41 -05:00
John Lancaster
9bd2bb1a0f streamlined janus config 2026-03-11 00:27:46 -05:00
John Lancaster
ed5cecd24d import fixes 2026-03-11 00:22:14 -05:00
John Lancaster
ce55bbc194 moved rebuild 2026-03-10 23:39:23 -05:00
John Lancaster
0af3e51ebf import fixes 2026-03-10 23:28:05 -05:00
John Lancaster
f6d9ba27f0 user groups 2026-03-10 23:14:07 -05:00
John Lancaster
24c6ac52a7 user factory 2026-03-10 23:12:09 -05:00
John Lancaster
5e62bcd97c syntax sugar 2026-03-10 22:49:51 -05:00
John Lancaster
e351efa18b added meta section 2026-03-10 22:49:38 -05:00
John Lancaster
5ee73c1f44 moved home manager modules 2026-03-10 22:39:30 -05:00
John Lancaster
6c01156ffe moved ssh module 2026-03-10 22:37:03 -05:00
John Lancaster
d6d882a418 docker extra group 2026-03-10 22:36:55 -05:00
John Lancaster
93b9a1e5a9 test-push 2026-03-10 22:36:45 -05:00
John Lancaster
20f72768c5 ssh module reorg 2026-03-10 22:18:28 -05:00
John Lancaster
7eaa32f161 WIP 2026-03-10 21:48:44 -05:00
John Lancaster
95391fc713 started janus config 2026-03-10 16:31:56 -05:00
John Lancaster
fe800b19b1 comment 2026-03-10 16:31:49 -05:00
John Lancaster
c26bbdfd9c added lzd sehll alias 2026-03-10 00:19:05 -05:00
John Lancaster
3285a6a04f using new user module 2026-03-10 00:16:29 -05:00
John Lancaster
ed95bcf9d1 moved users folder 2026-03-09 23:14:53 -05:00
John Lancaster
86d6ab87e5 filled in sublime module for desktop 2026-03-09 22:08:38 -05:00
John Lancaster
8da025f57b development ssh targets 2026-03-09 01:25:54 -05:00
John Lancaster
3f1a1ea713 improved test-nix example 2026-03-09 01:20:27 -05:00
John Lancaster
50141d76a2 moved sops import 2026-03-09 01:20:15 -05:00
John Lancaster
6ba202edd3 fixed zsh prompt 2026-03-09 01:20:07 -05:00
John Lancaster
e3c29f8ba6 added first lxc host "test-nix" 2026-03-09 00:25:41 -05:00
John Lancaster
c773001a48 added nixos-rebuild to john-pc-ubuntu 2026-03-08 23:47:18 -05:00
John Lancaster
a5e5f2f1ea started lxc nixos module 2026-03-08 23:41:08 -05:00
John Lancaster
8039d84347 started lxc 2026-03-08 21:55:01 -05:00
John Lancaster
7d28205371 using config.xdg 2026-03-08 21:36:17 -05:00
John Lancaster
902f6ec023 fixed warning 2026-03-08 15:44:26 -05:00
John Lancaster
fe2dc5b09b lockfile update 2026-03-08 15:34:32 -05:00
John Lancaster
5d927f10e6 fixes 2026-03-08 15:33:30 -05:00
John Lancaster
a049bd539a fixed sops 2026-03-08 15:06:11 -05:00
John Lancaster
8b8edf9211 added resticprofile flake-part 2026-03-08 14:48:41 -05:00
John Lancaster
e9c54606e2 notes 2026-03-08 14:13:57 -05:00
John Lancaster
3e15d03778 fixed imports and added scripts 2026-03-08 14:13:52 -05:00
John Lancaster
1228151d5a moved files.nix 2026-03-08 13:32:01 -05:00
John Lancaster
b96b9b2409 separated programs directory 2026-03-08 13:31:33 -05:00
John Lancaster
f7735089df combined shell with shell-tools 2026-03-08 12:37:00 -05:00
John Lancaster
9432f35ce8 rename 2026-03-08 12:33:05 -05:00
John Lancaster
e25f7420ff working home manager switch without --impure 2026-03-08 12:31:03 -05:00
John Lancaster
c45a48039e notes 2026-03-08 11:55:09 -05:00
John Lancaster
5d14d515e0 separated home-manager module for user config 2026-03-08 11:38:26 -05:00
John Lancaster
0c1e633aaa converted more modules 2026-03-08 11:25:07 -05:00
John Lancaster
7d4f997c18 WIP started reorg for absorbing john-pc-ubuntu 2026-03-08 11:07:12 -05:00
John Lancaster
9af4544640 added notes and rebuilt flake 2026-03-08 10:49:23 -05:00
John Lancaster
c66e86da88 added xclip 2026-03-07 13:46:39 -06:00
John Lancaster
7ce36d0d3c changed ghostty starting position 2026-03-05 22:16:44 -06:00
John Lancaster
469f1217ac appdaemon username in match block 2026-02-25 08:28:21 -06:00
John Lancaster
acf2b17fd5 added appdaemon principal to ssh cert signing 2026-02-25 08:26:16 -06:00
John Lancaster
0bf2069b2c fix 2026-02-16 17:33:59 -06:00
John Lancaster
a37bdacf24 dotdir warning 2026-02-16 17:33:41 -06:00
49 changed files with 1850 additions and 333 deletions

View File

@@ -1,14 +1,17 @@
keys:
- &john-p14s age1f6drjusg866yscj8029tk4yfpgecklrvezldm02ankm6h8nnwu5s2u6ahy
- &john-pc age1ykcs39e62pz3xu6cedg8ea685kv5d5qsrhgkndygzm8rx30xd5ys5t3qxt
- &test-nix age1gvplss0ddmyf6vpjy363wu3n057vhm0j6n7tc94cxd8kadapypws5mtaj0
creation_rules:
- path_regex: \.yaml$
key_groups:
- age:
- *john-p14s
- *john-pc
- *test-nix
- path_regex: \.json$
key_groups:
- age:
- *john-p14s
- *john-pc
- *test-nix

View File

@@ -1,7 +1,77 @@
# A Dendritic Nix Flake
Regenerate flake and switch to it.
```shell
nix run ".#write-flake"
nix run .#write-flake
nix flake check
home-manager switch --flake .#desktop
```
```shell
nix flake show --all-systems
```
## Layout
- Everything under `./modules` gets auto-imported by `import-tree`
| Directory | Description |
| ------------- | ----------- |
| `hosts` | Provides either `homeConfigurations` or `nixosConfigurations` |
| `nix-tools` | Nix-specific tools, like factory functions for use in the flakes |
| `nixos` | `flake.modules.nixos` |
| `programs` | flakes that provide `flake.modules` attributes for specific programs |
| `services` | flakes that provide configuration for running services |
| `users` | flakes that provide configuration for individual users with the `flake.factory.user` factory |
## Mechanics
### Flake
The top-level flake is created using [flake-file](https://flake-file.oeiuwq.com/)
```shell
nix flake init -t github:vic/flake-file#default
...
nix run ".#write-flake"
```
This uses `inputs.flake-parts.lib.mkFlake` to create the flake.
The modules are automatically imported from the `./modules` using [import-tree](https://import-tree.oeiuwq.com/).
### [Home Manager](https://flake.parts/options/home-manager.html)
Uses the home-manager module of flake-parts, which is imported in `modules/home-manager/flake-parts.nix`
Looks for options:
- `flake.homeConfigurations`
- `flake.homeModules`
Show exported home configurations:
```shell
nix eval --apply builtins.attrNames .#homeConfigurations
```
### Testing
These commands would are useful for debugging
```shell
SYSTEM=lxc
# Just evaluate (fast, catches config errors)
nix eval .#nixosConfigurations.${SYSTEM}.config.system.build.toplevel
# Full build (slower, produces a bootable system)
nix build .#nixosConfigurations.${SYSTEM}.config.system.build.toplevel
```
### Remote Deploy
```shell
nixos-rebuild switch --flake .#lxc --target-host root@fded:fb16:653e:25da:be24:11ff:fea0:753f
```

97
flake.lock generated
View File

@@ -2,11 +2,11 @@
"nodes": {
"flake-file": {
"locked": {
"lastModified": 1771017549,
"narHash": "sha256-n68HeYEQJ67hMH2LPI0cERD2pkpNe5dyeOGg11uZ7rc=",
"lastModified": 1773554778,
"narHash": "sha256-keH0VNsci9e0Uwt3Msp/N+pltaP8Lb6lt09Q3WvDPw4=",
"owner": "vic",
"repo": "flake-file",
"rev": "9d89918faacdbd2ce26d0aa7298da0fecad8b437",
"rev": "f4780a86bd4c756475d839b286f8a40aabdbc802",
"type": "github"
},
"original": {
@@ -22,11 +22,11 @@
]
},
"locked": {
"lastModified": 1769996383,
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
"lastModified": 1772408722,
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
"type": "github"
},
"original": {
@@ -53,16 +53,34 @@
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1681202837,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1771102945,
"narHash": "sha256-e5NfW8NhC3qChR8bHVni/asrig/ZFzd1wzpq+cEE/tg=",
"lastModified": 1773608492,
"narHash": "sha256-QZteyExJYSQzgxqdsesDPbQgjctGG7iKV/6ooyQPITk=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "ff5e5d882c51f9a032479595cbab40fd04f56399",
"rev": "9a40ec3b78fc688d0908485887d355caa5666d18",
"type": "github"
},
"original": {
@@ -73,11 +91,11 @@
},
"import-tree": {
"locked": {
"lastModified": 1771045967,
"narHash": "sha256-oYO4poyw0Sb/db2PigqugMlDwsvwLg6CSpFrMUWxA3Q=",
"lastModified": 1773554199,
"narHash": "sha256-6apV5N1F5tTD8JY9AUGnkWmy56HqDPn4MNFRsq4Rg+s=",
"owner": "vic",
"repo": "import-tree",
"rev": "c968d3b54d12cf5d9c13f16f7c545a06c9d1fde6",
"rev": "c6ebc59c85ee54cfb68163d06d1a3149ce0fe431",
"type": "github"
},
"original": {
@@ -109,11 +127,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1770841267,
"narHash": "sha256-9xejG0KoqsoKEGp2kVbXRlEYtFFcDTHjidiuX8hGO44=",
"lastModified": 1773389992,
"narHash": "sha256-wvfdLLWJ2I9oEpDd9PfMA8osfIZicoQ5MT1jIwNs9Tk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "ec7c70d12ce2fc37cb92aff673dcdca89d187bae",
"rev": "c06b4ae3d6599a672a6210b7021d699c351eebda",
"type": "github"
},
"original": {
@@ -125,17 +143,31 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1771177547,
"narHash": "sha256-erxy9meNKMaKpKQpl8KfhZsVY4EtR4eaHT94jY98Ty0=",
"rev": "ac055f38c798b0d87695240c7b761b82fc7e5bc2",
"lastModified": 1773507054,
"narHash": "sha256-yzDBkI1CpeZrAt4l1nGvTOs3OFtXCS7a7Gi5Y1h878w=",
"rev": "e80236013dc8b77aa49ca90e7a12d86f5d8d64c9",
"type": "tarball",
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre946843.ac055f38c798/nixexprs.tar.xz"
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre963414.e80236013dc8/nixexprs.tar.xz"
},
"original": {
"type": "tarball",
"url": "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1682134069,
"narHash": "sha256-TnI/ZXSmRxQDt2sjRYK/8j8iha4B4zP2cnQCZZ3vp7k=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "fd901ef4bf93499374c5af385b2943f5801c0833",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"root": {
"inputs": {
"flake-file": "flake-file",
@@ -148,7 +180,7 @@
"nixpkgs"
],
"sops-nix": "sops-nix",
"systems": "systems_2"
"vscode-server": "vscode-server"
}
},
"sops-nix": {
@@ -158,11 +190,11 @@
]
},
"locked": {
"lastModified": 1770683991,
"narHash": "sha256-xVfPvXDf9QN3Eh9dV+Lw6IkWG42KSuQ1u2260HKvpnc=",
"lastModified": 1773550941,
"narHash": "sha256-wa/++bL2QeMUreNFBZEWluQfOYB0MnQIeGNMuaX9sfs=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "8b89f44c2cc4581e402111d928869fe7ba9f7033",
"rev": "c469b6885f0dcd5c7c56bd935a0f08dbcd9e79e1",
"type": "github"
},
"original": {
@@ -200,6 +232,25 @@
"repo": "default",
"type": "github"
}
},
"vscode-server": {
"inputs": {
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1770124655,
"narHash": "sha256-yHmd2B13EtBUPLJ+x0EaBwNkQr9LTne1arLVxT6hSnY=",
"owner": "nix-community",
"repo": "nixos-vscode-server",
"rev": "92ce71c3ba5a94f854e02d57b14af4997ab54ef0",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixos-vscode-server",
"type": "github"
}
}
},
"root": "root",

View File

@@ -1,7 +1,6 @@
# DO-NOT-EDIT. This file was auto-generated using github:vic/flake-file.
# Use `nix run .#write-flake` to regenerate it.
{
description = "A dendritic setup.";
outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } (inputs.import-tree ./modules);
@@ -23,7 +22,7 @@
inputs.nixpkgs.follows = "nixpkgs";
url = "github:Mic92/sops-nix";
};
systems.url = "github:nix-systems/default";
vscode-server.url = "github:nix-community/nixos-vscode-server";
};
}

11
keys/root_ca.crt Normal file
View File

@@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBlDCCATqgAwIBAgIRAKDvqOX8WVhJ/ev02Y1gXKQwCgYIKoZIzj0EAwIwKDEO
MAwGA1UEChMFSmFudXMxFjAUBgNVBAMTDUphbnVzIFJvb3QgQ0EwHhcNMjUxMjE4
MDYwMjI4WhcNMzUxMjE2MDYwMjI4WjAoMQ4wDAYDVQQKEwVKYW51czEWMBQGA1UE
AxMNSmFudXMgUm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABMt6kNpx
Q9vySc1N6F9jJObeQXZI+9f33E1cN4zbEuNpmtpRl0WaPa1AGNbSi5sIbiH7wDv2
llXfCqYWkeoCE5mjRTBDMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/
AgEBMB0GA1UdDgQWBBRo55byyyo2sePP+8zz+uM4mXNV+zAKBggqhkjOPQQDAgNI
ADBFAiB6zxTbvWMZWgDQhKGh+MnGQQ7f8UGhzinOfRG7a/HdOAIhAIVWt6MLl6QU
FOvl/qIFGd7YJeWU5aahPABVttxjSMn/
-----END CERTIFICATE-----

View File

@@ -1,3 +1,8 @@
janus:
fingerprint: ENC[AES256_GCM,data:A0eNE3nX8hVq8m4bNFvkTGR15Xh9QV44JpOJFyhZojPOTirRGEq/MCSNIqpKfoFUB4TXdRZIN95pAoNlO7Z2dQ==,iv:3TQ9ZcjRmnQzkNA5Cv/cpEIT8gTJ1w3cTjTVtxtuzq4=,tag:hIwcjdZ6R8UzUgh/VoDz3A==,type:str]
admin_jwk: ENC[AES256_GCM,data:HhsnsCsR87ItjEKP3MsHpxb/WZHvxRrMIhbBgW7qq/0=,iv:b5MKd5TFkxFjd1zY0Lfi2QDv4sLmyKn7riW8s/EfDeA=,tag:GkGfyAM2RtKzuo567bAjiA==,type:str]
test-nix:
ssh_host_key: ENC[AES256_GCM,data:04CRKNCycc8Dre44ERtytmX6NHdZOIds95ckYzmi/i74qEYyN/L5XsuKKmjH2WCn3brvK4pZLLIZWzxC5VJ85q+O8Tfd3qdFROTG5qYRvmK5huxphPaIWSUI2ZEdLqecpRtmq7Zeq+Xa2+JKC0ehg2sqavotkPM5vIeVsaStD5rMVSgnuGaTS3WuPPfEjJPfsCyGPup4ysgxKxfc3JXOR+T8t6w4ccG0Si6tGz6io0Sl9tgPzrYqwqTaMtWLUA50luJg+D8ZxpMjpqFNbIry2tKwettzwXtGz7xGYnjvuk+1sCdYl8PXOMRT2KwAy5usCJsWFRTXJzZ5kxzwAYLwmhlsfPuxwTrR5fRETJPglRP/1muq8piIZn6yHtq9dCSrNcDCZnjnjG+cby28yPLYKhDgVNF5DObob8e6Fp/nP00sdi4Bt1aHRR5pjK/9mpsWE6ikO83SUi4xSoL1LKZM2kC5DL8XBJP7nWQTwijp84m3htMKvrKfFo68G9xP4v/DhVcHLsoNGLytH5omAuGL,iv:0cAtrXlPBis8nYw1XT4kSPRuwVkq0XSh6jjY9mpewIA=,tag:WFsiOe/l8cVicOqX2C94xA==,type:str]
restic_password:
john_ubuntu: ENC[AES256_GCM,data:Q4lUaFFDgoK9k4kQj7hSVKaFDGW0T+6V+OpFU5R528R3EKM7YJMgcFX+sK3mWl9XA4/6E1GeINpIqOpx+FP5Cf/8qt9sXBXCmXXSYdA4IH3RS6a1NkcIVjsTMvpn4q/fslCeYN4LB+r4pBGmdca105miqVun8J69cZGwjZ+wuxrMAP+mdnHdSUPycjNWJJzmEa3waQsygAi4A5cAN5sigOPBxe2pCTh/FEKoTgWmzHGJvcjrzuL6wNOpQrkMWwTsHCtbe9dyMP/fQpoBgYDT4W9Rd4XHhbrooje+g3x48EL1rkRIVVNRavpRUih/mjcdJGzzJ6jZmLLcc1f7SZIKZht7f+ZcdZl3rKQB+WanZgK/KAgKBRCrbIk2eeBZwkcRSw5kmGFU7x0azdIipJYj+3KHHQS5S2VW4j2tQG74xK3qaNJcSMjpKmdI1dHcPf0x2ILaDDV9Ts0H4GTOB2zO9iGy7x2tdPd4tugxxk5rr5rphTZL3lgUf0Ri/qMkJh9I8CsjUdvRycHeIEUZPmEVaIqJC2jrd2pBslis5VWD/6PHQBCob07d1fcpIYox4YXM3GcLg3OxiD8nZ7DTzGRMhciZtTKKWbBT8qzPud4ZQvDkT5l+XOpeM13wXFIMa13CwOzYeyWjycED0VQ/i3XRw9+9lg3cosfxaPdaFtv4MjV7Od62G/UJw3OxaQOHM2y24N+Q0pSBoTdDAFwDCH/kcqZji6ZrVTu4Rad8opcILJJcqC+pjegDvBtUdDz+G3/dFiS6m8RYIRb7qB5yEX/lCgzlECmRS2XP8uraNJ5NN8rtk0gdBtaI9/78YyAjLLGkjIcIR4uJA5buCZBR9jIdqf4f52fowbx18VPrwFXN2mYX6mPsKbDmaz0ILHq/I9n8bS/KM7gIQmTw/RAUwnmL4IRu8zHn6nmIqj6d8AkjYx7s6pG8OF8LgfhqZT7tdPKCd+n/HnYn1fZGSw26zHzPd4nKnVV1e3NYvX6CVwVycerGs9elOKtOI8GsrWYyXzJbfC+nWxbHKI/t6sxyzTBrHPR4r6l/CchQF+SfBs5aELKExe7h325qBB2y6EFdkbxTj1tPGqxttp9xJB1LUyNtwsEAkpD44JNqPxZCYHQbeVE3Oo3TYtRUSVWREc1WNIsfSG/anScYUhDEah9YyIdiG+O71QqegunusLoxmpF2rQtx7shtAvJV3skDBB0tFDoQyIV+yPo7kPV0D7Ig+Ba+mk5ASJrT9DXZ53Q2CCTLAuslU4MP7g22RX8rU3s2hFJq8m7wvMwpqa9Tr6O38i1wX4PhG1VRMM8EIlMQnLWWKmni6NbOYiRxFYJMioxH5SyE4ODQYXy5YIuLoRsX8VR6UqJ1GZb7sJf3M1aPOFOHzTN9hnziTRRe1KCMoBrAghrqhvL1VRr9X5PYMxnjBh6o0d5YTN0WOGD2iEVbCzqxFXcxQZBBp4KyNAAhFhGNw8WUp/rVoyCYn7+OEYFspY4FmGMTYhvxbq5LEptXeQgOP2ggBkqsw8sYP9oj1cb4kzBNv8M7nR9kM1EqvZpFV47phoTbBeMY4DZOJodkASFVM5/7ijWy/M9rtWMFMCXKURKkAQJRFADs0KqMJ5osnFFnubX7vKgK736XXF4+wIQuuHqEsYDZ90ftInxq8sYRnb2FZ0EV4yc8qqnz++fjwrAA5EV+zhL6l6hum1zL8JkUJ1ICKZ905If6nIeoul8MY3B1Cz4W+osN2Wtl8OeJ2t3iZx3wk/unVa8uDZH1owu47He68e2V8vpYxOaW4/hLyy/XuL5DukETrhRjC+7GbEaKaCwoA2UwAdqU,iv:N8ek+tp16WiZgjTDxXb0CRXH+MbLsl/oZ/OwcOoVRO0=,tag:uIzCSX0R/EObF/RdWxj64w==,type:str]
api:
@@ -7,22 +12,31 @@ sops:
- recipient: age1f6drjusg866yscj8029tk4yfpgecklrvezldm02ankm6h8nnwu5s2u6ahy
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoWkxDSnlNT2Vua1ZXWC8r
SU9UMnhaVXVEVlZGL3dtYTBJSzNGbHVaSTJNCm9ZTFM3RndpRktUcWhwZk1Fc2dk
ZGtoWXdoOWVyK1F0YStSS3dsMkg2R28KLS0tIFkrdVFZNlVxRjhPaWdMZXl2elV3
TVpyTzFsNFNmd3FNU0tlMnlTOHNTQWsKfKdN4epZokF74bCNr9+jxulZJFBQM83P
quMhl+H85My8jAsEeC9CW7y2jdNPJkfk9gHun4ozoW8U7o6y5RLfJg==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0UEpja2kxdThZVWZhOGVP
S0NtSi84MjhnN0RORkh2NjZ4YlYvWS9kZDBNClFzYnVxWnhmQkpCRkRFVUx1RDdX
ZHFqYXRqYXM0cWJzcU5EeEtSR1BUVzAKLS0tIDdEY2pnVTJqWlNZVkZldXVYVmFH
dVNBRUVodU5sRnpVcG1GZ1RiZzhjTXMKefqBvvD/qZwcSHmFjUnleukVRLueG36Y
Q81KlwQweF2F8kHl7Bqsi+3hH1dZZbVm3vjuGpWFOoti7fowUV55Kw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1ykcs39e62pz3xu6cedg8ea685kv5d5qsrhgkndygzm8rx30xd5ys5t3qxt
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSakZRUnkraWtId2h3eUhB
REpkUHhYMm1MSmtFU2pvd1BpQ0xRTTlCWkZJCkxrTm1sdDBqclJ3RHR6VkllOFpo
ZXRtS2lsazRDS2lyRnZmT3FTTjJ6WUUKLS0tIExxNlFoeDhHQ3l5a1VvUHNRWUdw
Mms2UEhFSU82UWR5Z1VvU25qenJUQm8KtQeZDIfJIczm1l8ql/WmVEf8KI9dg0vw
9rNSjtBkEttVd21zUSOziG4513abllE8NFTkAc1z3HacuXpHTBnd5A==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqZFZxbDhVUWFEUGhPMlZI
SFdBYkpxSnAxTUZXbjVwQnlZQ3l1SWtuZGg0CmVBdnVHbTNUcmwvK01iMnZKZTJh
ajFla3kzYUl4ZWY3czA0WUdNM2lpVFUKLS0tIHo5Uk1pV296MXdnUTZGQ25haWZG
QWZDWGRaRDBhY1ZkZk5oTHY0ZVV2RXMKanv+WWRhf5nl+aw/T6QZFVQQmhV1DZfB
jkSzOAKOgPx7toYFmpq9E8fAH+zrMzDbxI2z2uyrOFI6v+QE0Ul/iQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-08-03T17:03:22Z"
mac: ENC[AES256_GCM,data:c3rcMHTRxbnpQoW5eLn0X1aCL1v2ft05UTcHaCuGiCaF3b/loVjEQr30pepBgR07PSleTIi375Y0Rj8ik8Ot3j+Zl5BR32bEtqf6gcWwz6oSmeORDrJS15698d7/avJl82/EC0ZN77j+fcdkWZrCJHb47HGfRxKl9L5HbyWasA4=,iv:g3d3C571uYpTTFixYZg+ztg8jTdof1g6Hb5gtRvpRkk=,tag:8kAxrUwUVeWvpYjWMDE+AA==,type:str]
- recipient: age1gvplss0ddmyf6vpjy363wu3n057vhm0j6n7tc94cxd8kadapypws5mtaj0
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZcGtMNi9RSG5aTVV5cWdT
NittUXN0Qnpld1YvOU50OUh3Z3ZiSzhHOHdNCnc4TmdYbS9QQnBLbldHSytIdkJl
R0psQWxkZTgyZTRzckkrTGpyNCsvR2sKLS0tIEdLb05aT2I2S3BKcFRrVmtvTGw5
Z1orRCtkTDVXSktuck5pTmV4K05qZHMKZlHHu07q+GnyDDgdwW2Ic3P23PmoSPwn
WuNLZdlZQleROaRb+zpD+9P1HGGJ3mWAlNlnmjGrRk453k1PbBQ5Og==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-03-15T15:06:29Z"
mac: ENC[AES256_GCM,data:cF/TJ8VkzrHRUrO5iGdRdlFtqV/5EQ15JwQKIywJvsh0NERK67T21czSP7923MiL0u5QTVPn/rO8R5E/8gBu3r8+fLq+CFl9PDQHEX2JhnYOD5WZR412WMZq3MVR94IMTOrQANMVpS4uhMyvnrqOe4AenxLDyzrYhkwf1KQh4w0=,iv:Qwy8z4uXGMlf+kTMNiE42M9l8LtSJ+O7diknRrsSeYI=,tag:qlCY9r8HnEDmq/jw59C/sg==,type:str]
unencrypted_suffix: _unencrypted
version: 3.10.2
version: 3.12.1

1
keys/ssh_user_ca.pub Normal file
View File

@@ -0,0 +1 @@
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNpgz9xYE3K+JeQ7vtDA07iWlp0xTXB+G8MBzX6/RluXs8E6v+ahx90M093EYIOImaW6npWQ0JnFtmZaet5l9Ao=

View File

View File

@@ -1,12 +1,7 @@
{ inputs, ... }:
{
imports = [
# https://github.com/vic/flake-file/tree/main/modules/dendritic
inputs.flake-file.flakeModules.dendritic
];
flake-file.description = "A dendritic setup.";
# flake.flakeModule = {
# imports = (inputs.import-tree ./home-manager);
# };
}

View File

@@ -1,10 +0,0 @@
{
flake.homeModules.bash = { pkgs, lib, ... }:
{
programs.bash = {
enable = true;
enableCompletion = true;
package = pkgs.bash;
};
};
}

View File

@@ -1,41 +0,0 @@
{ inputs, ... }:
{
flake.homeModules.shell = {pkgs, lib, ...}:
{
imports = with inputs.self.homeModules; [
# Shells
# bash
zsh
# Tools
shell-tools
files
docker
];
programs.eza = {
enable = true;
package = pkgs.eza;
enableBashIntegration = true;
enableZshIntegration = true;
};
home.shell.enableShellIntegration = true;
home.shellAliases = {
ls = "${lib.getExe pkgs.eza} -lgos type --no-time --follow-symlinks";
};
};
flake.homeModules.shell-tools = {pkgs, ...}: {
home.packages = with pkgs; [
wget
curl
cacert
busybox
gnugrep
dig
btop
uv
];
};
}

View File

@@ -1,109 +0,0 @@
{inputs, ... }:
let
userName = "john";
in
{
flake.homeModules.ssh = { pkgs, config, lib, ... }:
{
options.ssh = {
IdentityFile = lib.mkOption {
# Intentionally not using a path type here because that will end up with the private key getting copied into the store
type = lib.types.str;
default = "${config.home.homeDirectory}/.ssh/id_ed25519";
description = "Path to the SSH identity file.";
};
matchSets = {
appdaemon = lib.mkEnableOption "Enable AppDaemon SSH targets";
certs = lib.mkEnableOption "Enable Janus and Soteria SSH targets";
homelab = lib.mkEnableOption "Enable various Homelab targets";
};
};
# All this stuff has to be wrapped in a config attribute because of the presence of the options here?
config = let
identityFile = config.ssh.IdentityFile;
publicKeyFile = "${identityFile}.pub";
certificateFile = "${identityFile}-cert.pub";
userKnownHostsFile = "${config.home.homeDirectory}/.ssh/known_hosts";
in {
home.packages = [
(pkgs.writeShellScriptBin "sign-ssh-cert" ''
echo "Signing ${publicKeyFile}"
echo "Copy the Step-CA JWK Provisioner password from 1password"
step ssh certificate --sign \
--principal root --principal ${userName} \
--provisioner admin \
${userName} ${publicKeyFile}
'')
];
programs.ssh = {
enable = true;
enableDefaultConfig = false;
extraConfig = ''
SetEnv TERM="xterm-256color"
IdentityAgent ~/.1password/agent.sock
'';
matchBlocks = lib.mkMerge [
{
"*" = {
user = "john";
compression = false;
serverAliveInterval = 0;
serverAliveCountMax = 3;
identitiesOnly = true;
inherit identityFile certificateFile;
hashKnownHosts = false;
userKnownHostsFile = "${userKnownHostsFile}";
addKeysToAgent = "yes";
forwardAgent = false;
};
}
(lib.mkIf config.ssh.matchSets.appdaemon {
"appdaemon" = {
hostname = "192.168.1.242";
};
"ad-nix" = {
hostname = "192.168.1.201";
user = "appdaemon";
};
})
(lib.mkIf config.ssh.matchSets.certs {
"janus" = {
hostname = "janus.john-stream.com";
user = "root";
};
"soteria" = {
hostname = "soteria.john-stream.com";
user = "john";
};
})
(lib.mkIf config.ssh.matchSets.homelab {
"docs" = {
hostname = "192.168.1.110";
user = "root";
};
"gitea" = {
hostname = "192.168.1.104";
user = "john";
};
"hermes" = {
hostname = "192.168.1.150";
user = "root";
};
"panoptes" = {
hostname = "192.168.1.107";
user = "panoptes";
};
})
];
};
};
};
}

View File

@@ -1,30 +0,0 @@
{ inputs, self, ... }:
let
userName = "john";
in
{
flake.homeModules."${userName}" = {
home.username = userName;
home.homeDirectory = "/home/${userName}";
home.stateVersion = "25.11";
programs.git.settings.user.name = "John Lancaster";
programs.git.settings.user.email = "32917998+jsl12@users.noreply.github.com";
};
flake.modules.nixos.user =
{ pkgs, ... }:
{
users.users."${userName}" = {
name = "${userName}";
shell = pkgs.zsh;
};
programs.zsh.enable = true;
home-manager.users."${userName}" = {
imports = [
inputs.self.homeModules."${userName}"
];
};
};
}

View File

@@ -1,37 +0,0 @@
{
flake.homeModules.zsh = { pkgs, ... }:
{
programs.zsh = {
enable = true;
package = pkgs.zsh;
enableCompletion = true;
autosuggestion.enable = true;
# syntaxHighlighting.enable = true;
history = {
append = true;
ignoreAllDups = true;
ignorePatterns = [
"history"
"ls"
"eza"
"clear"
];
save = 1000;
size = 1000;
share = true;
};
oh-my-zsh = {
enable = true;
# theme = "risto";
theme = "agnoster";
plugins = [
"sudo"
"dotenv"
"git"
"ssh"
"ssh-agent"
];
};
};
};
}

55
modules/hosts/janus.nix Normal file
View File

@@ -0,0 +1,55 @@
{ inputs, ... }:
let
username = "john";
hostname = "janus";
caURL = "https://janus.john-stream.com/";
in
{
flake.nixosConfigurations."${hostname}" = inputs.nixpkgs.lib.nixosSystem {
modules = with inputs.self.modules; [
nixos.lxc
nixos.sops
nixos.step-ssh-host
inputs.home-manager.nixosModules.home-manager
nixos."${username}"
nixos.zsh
nixos.docker
nixos.login-text
nixos.mtls
{
networking.hostName = hostname;
step-ssh-host = {
hostname = hostname;
caURL = caURL;
};
mtls = {
enable = true;
subject = hostname;
caURL = caURL;
san = [
"${hostname}.john-stream.com"
"192.168.1.244"
];
};
home-manager.users."${username}" = {
imports = with inputs.self.modules.homeManager; [
sops
step-ssh-user
];
shell.program = "zsh";
docker.enable = true;
# step-ssh-user = {
# enable = true;
# principals = [ "${hostname}" ];
# };
ssh.matchSets = {
certs = true;
homelab = true;
};
};
}
];
};
}

View File

@@ -0,0 +1,104 @@
{ inputs, ... }:
let
username = "john";
hostname = "john-pc-ubuntu";
testTarget = "fded:fb16:653e:25da:be24:11ff:fea0:753f";
in
{
flake.modules.homeManager."${hostname}" = { pkgs, config, ... }:
let
flakeDir = "${config.xdg.configHome}/home-manager/jsl-dendritic";
certDir = "${config.mtls.certDir}";
CACert = "${certDir}/root_ca.crt";
mtlsBundle = "${certDir}/${config.mtls.bundleFilename}";
resticPasswordFile = "${config.xdg.configHome}/restic/password.txt";
in
{
imports = with inputs.self.modules.homeManager; [
rebuild
john
sops
step-ssh-user
mtls
restic
docker
desktop
];
targets.genericLinux.enable = true;
shell.program = "zsh";
home.username = "${username}";
home.homeDirectory = "/home/${username}";
home.packages = with pkgs; [
nixos-rebuild
(writeShellScriptBin "test-push" ''
nixos-rebuild switch --flake ${flakeDir}#soteria --target-host root@${testTarget}
'')
];
mtls = {
enable = true;
caURL = "https://janus.john-stream.com/";
provisioner = "admin";
subject = hostname;
san = [
"${hostname}"
"192.168.1.85"
"spiffe://john-stream.com/ubuntu"
];
lifetime = "1h";
renew.onCalendar = "*:1/10";
};
# TODO: Add host-specific settings here:
# - sops secret for `restic_password/john_ubuntu`
# - zsh RESTIC* session variables
# TODO: make this more restrictive, rather than allowing all unfree packages
nixpkgs.config.allowUnfree = true;
nixpkgs.config.permittedInsecurePackages = [ "openssl-1.1.1w" ];
homeManagerFlakeDir = flakeDir;
docker.enable = true;
step-ssh-user = {
enable = true;
principals = ["root" "${username}" "appdaemon"];
provisioner = "admin";
};
ssh = {
certificates.enable = true;
matchSets = {
certs = true;
appdaemon = true;
homelab = true;
dev = true;
};
};
sops.secrets."restic_password/john_ubuntu" = {
path = resticPasswordFile;
mode = "0400";
};
restic = {
passwordFile = resticPasswordFile;
OnCalendar = "*:0/15";
paths = [ "${config.xdg.userDirs.documents}" "/conf" ];
exclude = [
"/home/*/Pictures"
"/home/*/Videos"
"/home/*/go"
"/home/*/snap"
"/home/john/john-nas"
];
};
};
flake.homeConfigurations."${hostname}" = inputs.home-manager.lib.homeManagerConfiguration {
pkgs = import inputs.nixpkgs { system = "x86_64-linux"; };
modules = with inputs.self.modules; [
homeManager."${hostname}"
];
};
}

45
modules/hosts/soteria.nix Normal file
View File

@@ -0,0 +1,45 @@
{ inputs, ... }:
let
username = "john";
hostname = "soteria";
caURL = "https://janus.john-stream.com/";
in
{
flake.nixosConfigurations."${hostname}" = inputs.nixpkgs.lib.nixosSystem {
modules = with inputs.self.modules; [
nixos.lxc
nixos.sops
nixos.step-ssh-host
inputs.home-manager.nixosModules.home-manager
nixos."${username}"
nixos.zsh
nixos.login-text
nixos.mtls
nixos.restic-server
{
networking.hostName = hostname;
step-ssh-host = {
hostname = hostname;
caURL = caURL;
};
mtls = {
enable = true;
subject = hostname;
caURL = caURL;
san = [
"${hostname}.john-stream.com"
# "192.168.1.244"
];
};
home-manager.users."${username}" = {
imports = with inputs.self.modules.homeManager; [
sops
step-ssh-user
];
shell.program = "zsh";
};
}
];
};
}

View File

@@ -0,0 +1,31 @@
{ inputs, ... }:
let
username = "john";
hostname = "test-nix";
in
{
flake.nixosConfigurations."${hostname}" = inputs.nixpkgs.lib.nixosSystem {
modules = with inputs.self.modules; [
nixos.lxc
nixos.sops
nixos.step-ssh-host
inputs.home-manager.nixosModules.home-manager
nixos."${username}"
nixos.zsh
nixos.docker
{
home-manager.users."${username}" = {
imports = with inputs.self.modules.homeManager; [
sops
];
shell.program = "zsh";
docker.enable = true;
ssh.matchSets = {
certs = true;
homelab = true;
};
};
}
];
};
}

View File

@@ -1,12 +1,12 @@
{ inputs, ... }:
{
flake.homeModules.rebuild =
flake.modules.homeManager.rebuild =
{ pkgs, lib, config, ... }:
{
options = {
homeManagerFlakeDir = lib.mkOption {
type = lib.types.str;
default = "~/.config/home-manager/jsl-home";
default = "${config.xdg.configHome}/home-manager";
description = "Path to the home-manager flake directory.";
};
};
@@ -31,7 +31,8 @@
${nixBin} flake check
'')
(writeShellScriptBin "nhms" ''
${lib.getExe home-manager} switch --impure --flake ${flakeDir}
HOSTNAME=$(hostname -s)
${lib.getExe home-manager} switch --impure --flake ${flakeDir}#$HOSTNAME
'')
(writeShellScriptBin "nhmu" ''
${nixBin} flake update --flake ${flakeDir}

View File

@@ -0,0 +1,41 @@
# Lifted from:
# https://github.com/Doc-Steve/dendritic-design-with-flake-parts/blob/69edacdb5a4a6ca71d649bb8eb62cf8c630c8627/modules/users/bob%20%5BNDn%5D/bob.nix#L8
{ self, ... }:
{
config.flake.factory.user = username: isAdmin: {
nixos."${username}" = { lib, pkgs, ... }: {
users.users."${username}" = {
isNormalUser = true;
home = "/home/${username}";
extraGroups = [
"input"
"networkmanager"
] ++ lib.optionals isAdmin [
"docker"
"wheel"
];
};
# Removes password for sudo
security.sudo-rs = lib.mkIf isAdmin {
enable = true;
extraRules = [{
users = [ "${username}" ];
commands = [{
command = "ALL";
options = [ "NOPASSWD" ];
}];
}];
};
# https://github.com/Doc-Steve/dendritic-design-with-flake-parts/wiki/Dendritic_Aspects#multi-context-aspect
home-manager.users."${username}" = {
home.username = "${username}";
home.homeDirectory = "/home/${username}";
imports = [
self.modules.homeManager."${username}"
];
};
};
};
}

12
modules/nixos/games.nix Normal file
View File

@@ -0,0 +1,12 @@
{ ... }:
{
flake.modules.nixos.games = {
programs.steam = {
enable = true;
gamescopeSession.enable = true;
remotePlay.openFirewall = true;
dedicatedServer.openFirewall = true;
localNetworkGameTransfers.openFirewall = true;
};
};
}

View File

@@ -0,0 +1,46 @@
{ inputs, ... }: {
flake.modules.nixos.login-text = { config, ... }: {
programs.rust-motd = {
enable = true;
refreshInterval = "*:0/5";
order = [
"global"
"last_login"
"service_status"
# "uptime"
"memory"
"filesystems"
];
settings = {
global = {
time_format = "%Y-%m-%d %H:%M:%S %Z";
};
last_login = {
john = 3;
root = 3;
};
service_status = {
Docker = "docker";
SSH = "sshd.socket";
"SSH Cert Renewal" = "step-ssh-host-renew.timer";
"mTLS Renewal" = "mtls-renew.timer";
};
# This calculation is wrong for LXCs
# uptime = {
# prefix = "Uptime";
# };
memory = {
swap_pos = "beside";
};
filesystems = {
root = "/";
};
};
};
};
}

29
modules/nixos/lxc.nix Normal file
View File

@@ -0,0 +1,29 @@
{ inputs, ... }:
{
flake.modules.nixos.lxc = { pkgs, lib, ...}: {
imports = with inputs.self.modules.nixos; [
({ modulesPath, ... }: { imports = [ "${modulesPath}/virtualisation/proxmox-lxc.nix" ]; })
];
nixpkgs.hostPlatform = lib.mkForce "x86_64-linux";
system.stateVersion = "25.11";
nix.settings.experimental-features = [ "nix-command" "flakes" ];
environment.systemPackages = with pkgs; [ git zsh ];
# security.sudo-rs.enable = true;
programs.nix-ld.enable = true;
nix.optimise.automatic = true;
nix.gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 30d";
};
programs.bash.enable = true;
};
# Generic bootstrapping lxc, use a specific host file for more
flake.nixosConfigurations.lxc = inputs.nixpkgs.lib.nixosSystem {
modules = with inputs.self.modules.nixos; [
lxc
];
};
}

View File

@@ -1,26 +0,0 @@
{ inputs, ... }:
{
flake.homeModules.desktop =
{ pkgs, ... } :
{
xdg.enable = true;
imports = with inputs.self.homeModules; [
rebuild
john
ssh
git
shell
ghostty
sops
];
};
flake.homeConfigurations.desktop = inputs.home-manager.lib.homeManagerConfiguration {
pkgs = import inputs.nixpkgs { system = "x86_64-linux"; };
modules = with inputs.self.homeModules; [
desktop
];
};
}

10
modules/programs/bash.nix Normal file
View File

@@ -0,0 +1,10 @@
{
flake.modules.homeManager.bash = { pkgs, lib, config, ... }:
{
programs.bash = lib.mkIf (config.shell.program == "bash") {
enable = true;
enableCompletion = true;
package = pkgs.bash;
};
};
}

View File

@@ -0,0 +1,12 @@
# This module is for programs with GUIs that run in a desktop environment
{ inputs, ... }:
{
flake.modules.homeManager.desktop =
{
imports = with inputs.self.modules.homeManager; [
onepassword
ghostty
sublime
];
};
}

14
modules/programs/eza.nix Normal file
View File

@@ -0,0 +1,14 @@
{ inputs, pkgs, lib, ... }:
{
flake.modules.homeManager.eza = { pkgs, lib, ... }: {
programs.eza = {
enable = true;
package = pkgs.eza;
enableBashIntegration = true;
enableZshIntegration = true;
};
home.shellAliases = {
ls = "${lib.getExe pkgs.eza} -lgos type --no-time --follow-symlinks";
};
};
}

View File

@@ -1,5 +1,5 @@
{
flake.homeModules.files = { pkgs, ... }:
flake.modules.homeManager.files = { pkgs, ... }:
{
programs.lf.enable = true;
programs.lf.cmdKeybindings = {
@@ -8,6 +8,8 @@
home.packages = with pkgs; [
gdu
lf
# TODO: find a CLI file editor that's not insane
];
};
}

View File

@@ -7,14 +7,14 @@
};
};
flake.homeModules.ghostty = { config, pkgs, lib, ... }:
flake.modules.homeManager.ghostty = { config, pkgs, lib, ... }:
{
home.sessionVariables = {
TERMINAL = "ghostty";
};
targets.genericLinux.nixGL = {
packages = inputs.nixgl.packages.${pkgs.system};
packages = inputs.nixgl.packages.${pkgs.stdenv.hostPlatform.system};
defaultWrapper = "mesa";
installScripts = [ "mesa" ];
};
@@ -33,12 +33,14 @@
shell-integration-features = [ "no-title" "sudo" ];
gtk-single-instance = true;
window-position-x = 100;
window-position-y = 100;
window-position-x = 25;
window-position-y = 25;
# window-height = 40;
# window-width = 200;
window-padding-balance = true;
window-padding-x = 5;
window-padding-y = 5;
# window-padding-balance = true;
# window-padding-x = 5;
# window-padding-y = 5;
initial-window = true;
resize-overlay = "never";
@@ -52,8 +54,6 @@
"ctrl+s>k=goto_split:down"
];
window-height = 40;
window-width = 200;
};
};

View File

@@ -1,5 +1,5 @@
{
flake.homeModules.git = { config, lib, ... }:
flake.modules.homeManager.git = { config, lib, ... }:
{
programs.git = {
enable = true;

View File

@@ -0,0 +1,5 @@
{
flake.modules.homeManager.onepassword = {
# TODO: Port `_1password = true` behavior into an explicit Home Manager module.
};
}

View File

@@ -0,0 +1,37 @@
# This module provides all the shell options
{ inputs, lib, ... }:
{
flake.modules.homeManager.shell-tools = { config, pkgs, ... }: {
options.shell.program = lib.mkOption {
type = lib.types.enum [ "bash" "zsh" ];
default = "zsh";
description = "Which interactive shell configuration to enable.";
};
imports = with inputs.self.modules.homeManager; [
bash
zsh
# Tools
eza
files
];
config = {
home.shell.enableShellIntegration = true;
programs.zsh.enable = lib.mkForce (config.shell.program == "zsh");
home.packages = with pkgs; [
wget
curl
cacert
busybox
gnugrep
dig
btop
uv
xclip
jq
];
};
};
}

View File

@@ -9,15 +9,28 @@ in
sops-nix.url = "github:Mic92/sops-nix";
sops-nix.inputs.nixpkgs.follows = "nixpkgs";
};
flake.modules.nixos.sops = {
imports = with inputs.sops-nix.nixosModules; [
sops
];
sops.defaultSopsFile = ../../keys/secrets.yaml;
sops.secrets."test-nix/ssh_host_key" = {
owner = "john";
path = "/home/john/.ssh/host_key";
};
};
# Define the homeModules that are used by flake-parts
# https://flake.parts/options/home-manager.html#opt-flake.homeModules
flake.homeModules.sops = { inputs, config, pkgs, lib, ... }:
# https://flake.parts/options/home-manager.html#opt-flake.modules.homeManager
flake.modules.homeManager.sops = { inputs, config, pkgs, lib, ... }:
let
sopsBin = lib.getExe pkgs.sops;
sopsConfigPath = ../../.sops.yaml;
sopsSecretsPath = ../../keys/secrets.yaml;
ageKeyFile = "${config.xdg.configHome}/sops/age/keys.txt";
flakeDir = "${config.xdg.configHome}/home-manager/jsl-dendritic";
in
{
home.packages = with pkgs; [
@@ -30,12 +43,12 @@ in
echo -n "Created ${ageKeyFile}: "
echo $(show-age-key)
'')
(writeShellScriptBin "show-age-key" "exec ${lib.getExe' pkgs.age "age-keygen"} -y ${ageKeyFile}")
(writeShellScriptBin "edit-secrets" "exec ${sopsBin} --config ${sopsConfigPath} ${sopsSecretsPath}")
(writeShellScriptBin "ls-secrets" "exec ${lib.getExe pkgs.eza} -alT --follow-symlinks ~/.config/sops-nix/secrets")
(writeShellScriptBin "show-age-key" "${lib.getExe' pkgs.age "age-keygen"} -y ${ageKeyFile}")
(writeShellScriptBin "edit-secrets" "${sopsBin} --config ${sopsConfigPath} ${flakeDir}/keys/secrets.yaml")
(writeShellScriptBin "ls-secrets" "${lib.getExe pkgs.eza} -alT --follow-symlinks ~/.config/sops-nix/secrets")
];
programs.zsh.shellAliases.sops = "exec ${sopsBin} --config ${sopsConfigPath}";
home.shellAliases.sops = "${sopsBin} --config ${sopsConfigPath}";
imports = [
# This import makes the sops config attribute available below
@@ -49,18 +62,11 @@ in
# Option definitions for the sops home-manager module:
# https://github.com/Mic92/sops-nix/blob/master/modules/home-manager/sops.nix
sops = {
defaultSopsFile = "${sopsSecretsPath}";
defaultSopsFile = sopsSecretsPath;
defaultSopsFormat = "yaml";
age.sshKeyPaths = [ "${config.ssh.IdentityFile}" ];
# Not sure any of these are necessary
# age.sshKeyPaths = [ "${config.sshIdentityFile}" ];
# age.keyFile = "${ageKeyFile}";
# age.generateKey = true;
# secrets."api/gmail_client_secret" = {
# path = "${config.xdg.configHome}/resticprofile/dendrite.txt";
# };
secrets."api/gmail_client_secret" = { };
templates."gmail_creds" = {
path = "${config.xdg.configHome}/sops-nix/gmail_api_credentials.json";
content = ''

View File

@@ -0,0 +1,69 @@
{ inputs, ... }:
let
caURL = "https://janus.john-stream.com/";
stepFingerprint = "2036c44f7b5901566ff7611ea6c927291ecc6d2dd00779c0eead70ec77fa10d6";
in
{
#
# Home Manager Module
#
flake.modules.homeManager.step-ssh-user = { config, pkgs, lib, ... }:
let
cfg = config.step-ssh-user;
firstPrincipal = lib.head cfg.principals;
principalArgs = lib.concatMapStringsSep " "
(principal: "--principal \"${principal}\"") cfg.principals;
in
{
options.step-ssh-user = {
enable = lib.mkEnableOption "opionated step client config for SSH certs";
caURL = lib.mkOption {
type = lib.types.str;
default = "${caURL}";
};
fingerprint = lib.mkOption {
type = lib.types.str;
default = "${stepFingerprint}";
};
rootCertFile = {
path = lib.mkOption {
type = lib.types.str;
description = "String path to where the root_ca.crt file will be stored for the user";
default = ".step/certs/root_ca.crt";
};
source = lib.mkOption {
type = lib.types.path;
description = "Nix path to the root cert file within the repo";
default = ../../keys/root_ca.crt;
};
};
provisioner = lib.mkOption {
type = lib.types.str;
default = "admin";
};
principals = lib.mkOption {
type = lib.types.listOf lib.types.str;
# default = [ ];
};
};
config = lib.mkIf cfg.enable {
home.file."${cfg.rootCertFile.path}".source = cfg.rootCertFile.source;
home.file.".step/config/defaults.json".text = builtins.toJSON {
"ca-url" = cfg.caURL;
fingerprint = cfg.fingerprint;
root = "${config.home.homeDirectory}/${cfg.rootCertFile.path}";
};
sops.secrets."janus/admin_jwk".mode = "0400";
home.packages = with pkgs; [
(writeShellScriptBin "sign-ssh-cert" ''
${lib.getExe pkgs.step-cli} ssh certificate \
--sign \
${principalArgs} \
--provisioner "${cfg.provisioner}" \
--provisioner-password-file "${config.sops.secrets."janus/admin_jwk".path}" \
"${firstPrincipal}" "${config.ssh.IdentityFile}.pub"
'')
];
};
};
}

View File

@@ -0,0 +1,8 @@
{ inputs, pkgs, ... }:
{
flake.modules.homeManager.sublime = { pkgs, lib, ... }: {
home.packages = with pkgs; [
sublime4
];
};
}

View File

@@ -0,0 +1,21 @@
{
flake.modules.homeManager.vscode = { pkgs, ... }:
{
programs.vscode = {
enable = true;
package = pkgs.vscode;
profiles.default.extensions = with pkgs.vscode-extensions; [
mhutchie.git-graph
ms-vscode-remote.vscode-remote-extensionpack
ms-python.python
ms-python.vscode-pylance
ms-toolsai.jupyter
charliermarsh.ruff
github.vscode-pull-request-github
github.vscode-github-actions
github.copilot
catppuccin.catppuccin-vsc
];
};
};
}

54
modules/programs/zsh.nix Normal file
View File

@@ -0,0 +1,54 @@
{ inputs, ... }:
let
username = "john";
in
{
flake.modules = {
nixos.zsh = { pkgs, ... }: {
users.users."${username}".shell = pkgs.zsh;
programs.zsh.enable = true;
# Already being imported by the john.nix module
# home-manager.sharedModules = [
# inputs.self.modules.homeManager.zsh
# ];
};
homeManager.zsh = { pkgs, config, ... }: {
programs.zsh = {
enable = true;
package = pkgs.zsh;
enableCompletion = true;
autosuggestion.enable = true;
# syntaxHighlighting.enable = true;
initContent = "HOST=$(hostname -s)";
dotDir = "${config.xdg.configHome}/zsh";
history = {
append = true;
ignoreAllDups = true;
ignorePatterns = [
"history"
"ls"
"eza"
"clear"
];
save = 1000;
size = 1000;
share = true;
};
oh-my-zsh = {
enable = true;
# theme = "risto";
theme = "agnoster";
plugins = [
"sudo"
"dotenv"
"git"
"ssh"
"ssh-agent"
];
};
};
};
};
}

View File

@@ -1,5 +1,15 @@
{ inputs, ... }:
{
flake.homeModules.docker = { config, lib, pkgs, ... }:
flake.modules.nixos.docker = {
virtualisation.docker = {
enable = true;
};
home-manager.sharedModules = with inputs.self.modules.homeManager; [
docker
];
};
flake.modules.homeManager.docker = { config, lib, pkgs, ... }:
{
options.docker = {
enable = lib.mkEnableOption "Docker tools and utilities";
@@ -13,10 +23,10 @@
docker
docker-compose
lazydocker
(pkgs.writeShellScriptBin "test-docker" ''
echo "Hello from docker.nix!"
'')
];
home.shellAliases = {
lzd = "lazydocker";
};
};
};
}

View File

@@ -0,0 +1,109 @@
{ inputs, ... }: {
flake.modules.nixos.restic-server = { config, pkgs, lib, ... }: {
services.restic.server = {
enable = true;
dataDir = "/mnt/restic";
listenAddress = "0.0.0.0:8080";
extraFlags = [ "--no-auth" ];
};
};
flake.modules.homeManager.restic = { config, pkgs, lib, ... }:
let
cfg = config.restic;
in
{
options.restic = {
repoName = lib.mkOption {
description = "Name of the restic repo to use";
type = lib.types.str;
default = "john-ubuntu";
};
passwordFile = lib.mkOption {
description = "String path to the restic password file";
type = lib.types.str;
};
paths = lib.mkOption {
description = "List of string paths to include in the backup";
type = lib.types.listOf lib.types.str;
default = [ ];
};
exclude = lib.mkOption {
description = "List of string paths to include in the backup. There are already some common ones included by default.";
type = lib.types.listOf lib.types.str;
default = [ ];
};
OnCalendar = lib.mkOption {
description = "";
type = lib.types.str;
};
RandomizedDelaySec = lib.mkOption {
description = "";
type = lib.types.str;
default = "1m";
};
};
config = let
resticRepository = "rest:https://soteria.john-stream.com/${cfg.repoName}";
caCert = "${config.mtls.certDir}/root_ca.crt";
mtlsClientCert = "${config.mtls.certDir}/${config.mtls.bundleFilename}";
in
{
home.sessionVariables = {
RESTIC_REPOSITORY = resticRepository;
RESTIC_PASSWORD_FILE = cfg.passwordFile;
RESTIC_CACERT = caCert;
RESTIC_TLS_CLIENT_CERT = mtlsClientCert;
};
# This is necessary because the restic service in home manager doesn't otherwise expose these options.
systemd.user.services."restic-backups-${cfg.repoName}".Service.Environment = [
"RESTIC_CACERT=${caCert}"
"RESTIC_TLS_CLIENT_CERT=${mtlsClientCert}"
];
services.restic = {
enable = true;
backups.${cfg.repoName} = {
repository = resticRepository;
passwordFile = cfg.passwordFile;
paths = cfg.paths;
timerConfig = {
OnCalendar = cfg.OnCalendar;
RandomizedDelaySec = cfg.RandomizedDelaySec;
Persistent = true;
};
runCheck = true;
pruneOpts = [
"--keep-last 10"
"--keep-hourly 8"
"--keep-daily 14"
"--keep-weekly 8"
"--keep-monthly 12"
];
exclude = cfg.exclude ++ [
".cache"
".devenv"
".rustup"
".cargo"
".venv"
".pyenv"
".vscode*"
"data/postgres"
"build"
"dist"
"__pycache__"
"*.log"
"*.egg-info"
"*.csv"
"*.m4a"
".local/share/Steam"
".local/share/Trash"
];
};
};
};
};
}

183
modules/services/ssh.nix Normal file
View File

@@ -0,0 +1,183 @@
{inputs, ... }:
let
userName = "john";
sshHostCAPubKey = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNug18oLH0vZxnibXJzMJvTWFPZTnSlhCDDVi+rHhgnIum6ZXQ4SF+VHOOAM5BbzZmMKitNJ5lcrGP15Eur7DzQ=";
in
{
flake.modules.nixos.ssh = { pkgs, config, lib, ... }:
let
cfg = config.ssh;
userCAPath = "ssh/ssh_user_ca.pub";
hostKeyFile = "ssh/ssh_host_ed25519_key";
in
{
options.ssh = {
certificates = {
enable = lib.mkEnableOption "Enable SSH host certificates";
userCA = lib.mkOption {
type = lib.types.path;
default = ../../keys/ssh_user_ca.pub;
};
};
};
config = {
services.openssh = {
enable = true;
# require public key authentication for better security
settings = lib.mkMerge [
{
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
HostKey = "/etc/${hostKeyFile}";
}
(lib.mkIf cfg.certificates.enable {
TrustedUserCAKeys = "/etc/${userCAPath}";
HostCertificate = "/etc/${hostKeyFile}-cert.pub";
})
];
};
environment.etc."${userCAPath}" = lib.mkIf cfg.certificates.enable {
source = cfg.certificates.userCA;
};
programs.ssh.knownHosts = lib.mkIf cfg.certificates.enable {
"192.168.1.*" = {
certAuthority = true;
publicKey = sshHostCAPubKey;
};
"*.john-stream.com" = {
certAuthority = true;
publicKey = sshHostCAPubKey;
};
};
};
};
flake.modules.homeManager.ssh = { pkgs, config, lib, ... }:
{
options.ssh = {
IdentityFile = lib.mkOption {
# Intentionally not using a path type here because that will end up with the private key getting copied into the store
type = lib.types.str;
default = "${config.home.homeDirectory}/.ssh/id_ed25519";
description = "Path to the SSH identity file.";
};
certificates = {
enable = lib.mkEnableOption "Enable SSH user certificates";
# sshCertProvisioner = lib.mkOption {
# type = lib.types.str;
# default = "admin";
# };
};
knownHostsFile = lib.mkOption {
type = lib.types.str;
default = "${config.home.homeDirectory}/.ssh/known_hosts";
};
matchSets = {
appdaemon = lib.mkEnableOption "Enable AppDaemon SSH targets";
certs = lib.mkEnableOption "Enable Janus and Soteria SSH targets";
homelab = lib.mkEnableOption "Enable various Homelab targets";
dev = lib.mkEnableOption "Enable development targets";
};
};
# All this stuff has to be wrapped in a config attribute because of the presence of the options here?
config = let
cfg = config.ssh;
identityFile = cfg.IdentityFile;
publicKeyFile = "${identityFile}.pub";
certificateFile = "${identityFile}-cert.pub";
provisionerPasswordPath = config.sops.secrets."janus/admin_jwk".path;
in {
home.file.".ssh/known_hosts" = {
text = lib.concatStringsSep "\n" (
[
"fded:fb16:653e:25da:be24:11ff:fea0:753f ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ9ZqiWPrCwHjxFCiu0lT4rlQs7KyMapxKJQQ5PJP1eh"
]
++ (lib.optional cfg.certificates.enable "@cert-authority 192.168.1.* ${sshHostCAPubKey}")
++ (lib.optional cfg.certificates.enable "@cert-authority *.john-stream.com ${sshHostCAPubKey}")
);
};
programs.ssh = {
enable = true;
enableDefaultConfig = false;
extraConfig = ''
SetEnv TERM="xterm-256color"
IdentityAgent ~/.1password/agent.sock
'';
matchBlocks = lib.mkMerge [
{
"*" = {
user = "john";
compression = false;
serverAliveInterval = 0;
serverAliveCountMax = 3;
identitiesOnly = true;
inherit identityFile certificateFile;
hashKnownHosts = false;
userKnownHostsFile = cfg.knownHostsFile;
addKeysToAgent = "yes";
forwardAgent = false;
};
}
(lib.mkIf cfg.matchSets.appdaemon {
"appdaemon" = {
hostname = "192.168.1.242";
user = "appdaemon";
};
"ad-nix" = {
hostname = "192.168.1.201";
user = "appdaemon";
};
})
(lib.mkIf cfg.matchSets.certs {
"janus" = {
hostname = "janus.john-stream.com";
user = "root";
};
"soteria" = {
hostname = "soteria.john-stream.com";
user = "john";
};
})
(lib.mkIf cfg.matchSets.homelab {
"docs" = {
hostname = "192.168.1.110";
user = "root";
};
"gitea" = {
hostname = "192.168.1.104";
user = "john";
};
"hermes" = {
hostname = "192.168.1.150";
user = "root";
};
"panoptes" = {
hostname = "192.168.1.107";
user = "panoptes";
};
})
(lib.mkIf cfg.matchSets.dev {
"test-nix" = {
hostname = "fded:fb16:653e:25da:be24:11ff:fea0:753f";
user = "john";
identityFile = identityFile;
};
})
];
};
};
};
}

View File

@@ -0,0 +1,129 @@
{
"root": "/etc/step-ca/certs/root_ca.crt",
"federatedRoots": null,
"crt": "/etc/step-ca/certs/intermediate_ca.crt",
"key": "/etc/step-ca/secrets/intermediate_ca_key",
"address": ":443",
"insecureAddress": "",
"dnsNames": [
"janus.john-stream.com",
"192.168.1.113"
],
"ssh": {
"hostKey": "/etc/step-ca/secrets/ssh_host_ca_key",
"userKey": "/etc/step-ca/secrets/ssh_user_ca_key"
},
"logger": {
"format": "text"
},
"db": {
"type": "badgerv2",
"dataSource": "/var/lib/step-ca/db",
"badgerFileLoadingMode": ""
},
"authority": {
"provisioners": [
{
"type": "ACME",
"name": "acme"
},
{
"type": "SSHPOP",
"name": "sshpop",
"claims": {
"enableSSHCA": true
}
},
{
"type": "JWK",
"name": "admin",
"key": {
"use": "sig",
"kty": "EC",
"kid": "xoxgOJFbveSLIL2gm1Yu5ZiRb9v8Jxe44F56i3v-Nf8",
"crv": "P-256",
"alg": "ES256",
"x": "zFO8hPx_eH0Iyz7UJI-w8ODMusEKCZ28M76sGWmWYxA",
"y": "XIWLLyKDzqxV9UH-2KeAkKPDrgLoPrxxW9-PzkXggME"
},
"encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjYwMDAwMCwicDJzIjoiUVJnTnJVTF9KcmxJYkJMVTlGNVRPZyJ9.DMu7xBNCq5pr-_--YTxNr5Hrcqy6ZmSVHsWurfVXL7Hk0Q3vyYRxiw.h-CnFiYc-DhxThI3.plx3_Qa_0kU-2TwnqFNfAfGnCpfQ2e0iiCMLruNHbLMnHeXQ1BysHBqps45_02zZXIRdHoDgYGtXRSfcdUYYoS0pLoPzC6m301ZFNSAFdRVlSZ3Q6VmWdixPXXnEB4EgSKTT_wxR33L8t9OpFzD85KfY-b_Un1l99ufjCnfg-EYkcICTn_G4-8bcW3eFIvJ6setzu-l0jHMhLQdIweqncn9on9xBXBD-ANhZfP95P2BJt-APqCi8eqiAvn_vClovdg0PxzRwOVDvWREz66FDw-HTU7xDtGO9hACopT5tfZOXDoykgZw1mJsq9NEq9ZzvKG2hvyk1UXtExxrNtFo.5q1OfGU4Amo4Si-vpeI42g",
"claims": {
"enableSSHCA": true,
"disableRenewal": false,
"allowRenewalAfterExpiry": false,
"disableSmallstepExtensions": false
},
"options": {
"x509": {},
"ssh": {}
}
}
],
"template": {},
"backdate": "1m0s"
},
"tls": {
"cipherSuites": [
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
],
"minVersion": 1.2,
"maxVersion": 1.3,
"renegotiation": false
},
"templates": {
"ssh": {
"user": [
{
"name": "config.tpl",
"type": "snippet",
"template": "templates/ssh/config.tpl",
"path": "~/.ssh/config",
"comment": "#"
},
{
"name": "step_includes.tpl",
"type": "prepend-line",
"template": "templates/ssh/step_includes.tpl",
"path": "${STEPPATH}/ssh/includes",
"comment": "#"
},
{
"name": "step_config.tpl",
"type": "file",
"template": "templates/ssh/step_config.tpl",
"path": "ssh/config",
"comment": "#"
},
{
"name": "known_hosts.tpl",
"type": "file",
"template": "templates/ssh/known_hosts.tpl",
"path": "ssh/known_hosts",
"comment": "#"
}
],
"host": [
{
"name": "sshd_config.tpl",
"type": "snippet",
"template": "templates/ssh/sshd_config.tpl",
"path": "/etc/ssh/sshd_config",
"comment": "#",
"requires": [
"Certificate",
"Key"
]
},
{
"name": "ca.tpl",
"type": "snippet",
"template": "templates/ssh/ca.tpl",
"path": "/etc/ssh/ca.pub",
"comment": "#"
}
]
}
},
"commonName": "Step Online CA"
}

View File

@@ -0,0 +1,306 @@
{ inputs, lib, ... }:
let
# Options that will be in common between
opts = {
enable = lib.mkEnableOption "Enable mTLS";
caURL = lib.mkOption {
description = "URL to the certificate authority";
type = lib.types.str;
};
subject = lib.mkOption {
description = "The Common Name, DNS Name, or IP address that will be set as the Subject Common Name for the certificate. If no Subject Alternative Names (SANs) are configured (via the --san flag) then the subject will be set as the only SAN.";
type = lib.types.str;
};
keyFilename = lib.mkOption {
description = "String filename for the private key";
type = lib.types.str;
default = "key.pem";
};
certFilename = lib.mkOption {
description = "String filename for the public certificate";
type = lib.types.str;
default = "cert.pem";
};
bundleFilename = lib.mkOption {
description = "String filename for the mTLS key bundle";
type = lib.types.str;
default = "mtls.pem";
};
san = lib.mkOption {
description = "List of SAN to give the mTLS cert";
type = lib.types.listOf lib.types.str;
default = [ ];
};
provisioner = lib.mkOption {
type = lib.types.str;
default = "admin";
};
lifetime = lib.mkOption {
type = lib.types.str;
default = "6h";
};
renew = {
enable = lib.mkOption {
description = "Enable automatic mTLS certificate renewal using a systemd timer.";
type = lib.types.bool;
default = true;
};
onCalendar = lib.mkOption {
description = "systemd OnCalendar schedule for mTLS certificate renewal checks.";
type = lib.types.str;
default = "*:1/15";
};
randomizedDelaySec = lib.mkOption {
description = "Randomized delay added to renewal timer runs to avoid synchronized renewals.";
type = lib.types.str;
default = "5m";
};
user = lib.mkOption {
description = "User account to run the mTLS renewal service as.";
type = lib.types.str;
default = "root";
};
group = lib.mkOption {
description = "Group to run the mTLS renewal service as. Defaults to the configured renewal user when null.";
type = lib.types.nullOr lib.types.str;
default = null;
};
reloadUnits = lib.mkOption {
description = "systemd units to try-reload-or-restart after a successful certificate renewal.";
type = lib.types.listOf lib.types.str;
default = [ ];
};
postCommands = lib.mkOption {
description = "Shell commands to run after a successful certificate renewal.";
type = lib.types.listOf lib.types.lines;
default = [ ];
};
};
};
mkMtlsRenewScript = {
pkgs,
tlsCert,
tlsKey,
mtlsBundle,
reloadUnits ? [ ],
postCommands ? [ ],
systemctlArgs ? [ ],
}:
let
renewReloadScript = lib.concatMapStringsSep "\n" (unit: ''
if ${lib.getExe' pkgs.systemd "systemctl"} ${lib.escapeShellArgs systemctlArgs} --quiet is-active "${unit}"; then
${lib.getExe' pkgs.systemd "systemctl"} ${lib.escapeShellArgs systemctlArgs} try-reload-or-restart "${unit}"
fi
'') reloadUnits;
renewPostCommands = lib.concatStringsSep "\n" postCommands;
in
pkgs.writeShellScriptBin "mtls-renew" ''
set -euo pipefail
if ${lib.getExe pkgs.step-cli} certificate needs-renewal "${tlsCert}"; then
echo "Renewing mTLS certificate"
else
echo "Skipping renew"
exit "$?"
fi
${lib.getExe pkgs.step-cli} ca renew --force "${tlsCert}" "${tlsKey}"
umask 077
${lib.getExe' pkgs.coreutils "cat"} "${tlsCert}" "${tlsKey}" > "${mtlsBundle}"
${renewReloadScript}
${renewPostCommands}
'';
mkNixosMtlsRenewService = {
pkgs,
tlsCert,
tlsKey,
mtlsBundle,
reloadUnits ? [ ],
postCommands ? [ ],
user ? "root",
group ? null,
}:
let
serviceGroup = if group == null then user else group;
renewScript = mkMtlsRenewScript {
inherit pkgs tlsCert tlsKey mtlsBundle reloadUnits postCommands;
};
in
{
description = "Renew the mTLS certificate when Smallstep marks it ready";
wantedBy = [ ];
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
User = user;
Group = serviceGroup;
ExecStart = lib.getExe renewScript;
};
};
mkNixosMtlsRenewTimer = {
onCalendar,
randomizedDelaySec,
unit ? "mtls-renew.service",
}: {
description = "Periodic Smallstep renewal for the mTLS certificate";
wantedBy = [ "timers.target" ];
timerConfig = {
Persistent = true;
OnCalendar = onCalendar;
AccuracySec = "1us";
RandomizedDelaySec = randomizedDelaySec;
Unit = unit;
};
};
mkHomeManagerMtlsRenewService = {
pkgs,
tlsCert,
tlsKey,
mtlsBundle,
reloadUnits ? [ ],
postCommands ? [ ],
}:
let
renewScript = mkMtlsRenewScript {
inherit pkgs tlsCert tlsKey mtlsBundle reloadUnits postCommands;
systemctlArgs = [ "--user" ];
};
in
{
Unit = {
Description = "Renew the mTLS certificate when Smallstep marks it ready";
After = [ "network-online.target" ];
Wants = [ "network-online.target" ];
};
Service = {
Type = "oneshot";
ExecStart = lib.getExe renewScript;
};
};
mkHomeManagerMtlsRenewTimer = {
onCalendar,
randomizedDelaySec,
unit ? "mtls-renew.service",
}: {
Unit = {
Description = "Periodic Smallstep renewal for the mTLS certificate";
};
Timer = {
Persistent = true;
OnCalendar = onCalendar;
AccuracySec = "1us";
RandomizedDelaySec = randomizedDelaySec;
Unit = unit;
};
Install = {
WantedBy = [ "timers.target" ];
};
};
in
{
flake.modules.nixos.mtls = { config, lib, pkgs, ... }:
let
cfg = config.mtls;
certDir = "/etc/step/certs";
tlsKey = "${certDir}/${cfg.keyFilename}";
tlsCert = "${certDir}/${cfg.certFilename}";
mtlsBundle = "${certDir}/${cfg.bundleFilename}";
rootCA = "${certDir}/root_ca.crt";
sanArgs = lib.concatMapStringsSep " " (san: "--san \"${san}\"") cfg.san;
in
{
options.mtls = opts;
config = lib.mkIf cfg.enable {
environment.systemPackages = with pkgs; lib.optionals cfg.enable [
(writeShellScriptBin "mtls-generate" ''
set -euo pipefail
${lib.getExe pkgs.step-cli} ca certificate \
${cfg.subject} ${tlsCert} ${tlsKey} \
--ca-url ${cfg.caURL} \
--root ${rootCA} \
--provisioner ${cfg.provisioner} \
--not-before=-5m --not-after=${cfg.lifetime} \
${sanArgs} \
"$@"
cat ${tlsCert} ${tlsKey} > ${mtlsBundle}
'')
(writeShellScriptBin "mtls-check" ''
${lib.getExe pkgs.openssl} x509 \
-noout -subject -issuer \
-ext subjectAltName,extendedKeyUsage \
-enddate -in ${mtlsBundle}
'')
];
systemd.services.mtls-renew = lib.mkIf cfg.renew.enable (mkNixosMtlsRenewService {
inherit pkgs tlsCert tlsKey mtlsBundle;
inherit (cfg.renew) reloadUnits postCommands user group;
});
systemd.timers.mtls-renew = lib.mkIf cfg.renew.enable (mkNixosMtlsRenewTimer {
inherit (cfg.renew) onCalendar randomizedDelaySec;
});
};
};
flake.modules.homeManager.mtls = { config, lib, pkgs, ... }:
let
cfg = config.mtls;
certDir = cfg.certDir;
tlsKey = "${certDir}/${cfg.keyFilename}";
tlsCert = "${certDir}/${cfg.certFilename}";
mtlsBundle = "${certDir}/${cfg.bundleFilename}";
rootCA = "${certDir}/root_ca.crt";
sanArgs = lib.concatMapStringsSep " " (san: "--san \"${san}\"") cfg.san;
in
{
options.mtls = opts // {
certDir = lib.mkOption {
description = "String path to where the mtls certs will be stored.";
type = lib.types.str;
default ="${config.home.homeDirectory}/.step/certs";
};
};
config = {
home.packages = with pkgs; lib.optionals cfg.enable [
step-cli
(writeShellScriptBin "mtls-generate" ''
set -euo pipefail
${lib.getExe pkgs.step-cli} ca certificate \
${cfg.subject} ${tlsCert} ${tlsKey} \
--not-before=-5m --not-after=${cfg.lifetime} \
--provisioner ${cfg.provisioner} \
${sanArgs} \
"$@"
cat ${tlsCert} ${tlsKey} > ${mtlsBundle}
'')
(writeShellScriptBin "mtls-check" ''
${lib.getExe pkgs.openssl} x509 \
-noout -subject -issuer \
-ext subjectAltName,extendedKeyUsage \
-enddate -in ${mtlsBundle}
'')
(mkMtlsRenewScript { inherit pkgs tlsCert tlsKey mtlsBundle; })
];
systemd.user.services.mtls-renew = lib.mkIf cfg.renew.enable (mkHomeManagerMtlsRenewService {
inherit pkgs tlsCert tlsKey mtlsBundle;
inherit (cfg.renew) reloadUnits postCommands;
});
systemd.user.timers.mtls-renew = lib.mkIf cfg.renew.enable (mkHomeManagerMtlsRenewTimer {
inherit (cfg.renew) onCalendar randomizedDelaySec;
});
};
};
}

View File

@@ -0,0 +1,116 @@
{ inputs, ... }: {
flake.modules.nixos.step-ssh-host = { config, pkgs, lib, ... }:
let
cfg = config.step-ssh-host;
rootCertPath = "/etc/step/certs/root_ca.crt";
provisionerPasswordPath = config.sops.secrets."janus/admin_jwk".path;
sshKeyPath = "/etc/ssh/ssh_host_ed25519_key";
sshCertPath = "${sshKeyPath}-cert.pub";
in
{
# NixOS Options
options.step-ssh-host = {
hostname = lib.mkOption {
description = "Networking host name";
type = lib.types.str;
};
caURL = lib.mkOption {
description = "URL for the certificate authority";
type = lib.types.str;
};
rootCertFile = {
path = lib.mkOption {
description = "String path to where the root_ca.crt file will be stored for the user";
type = lib.types.str;
default = "step/certs/root_ca.crt";
};
source = lib.mkOption {
description = "Nix path to the root cert file within the repo";
type = lib.types.path;
default = ../../../keys/root_ca.crt;
};
};
provisioner = lib.mkOption {
description = "Provisioner inside Step CA to use for the SSH certificates";
type = lib.types.str;
default = "admin";
};
};
imports = with inputs.self.modules.nixos; [ ssh ];
# NixOS Config
config = {
ssh.certificates.enable = true;
sops.secrets."janus/admin_jwk" = {
owner = "root";
group = "root";
mode = "0400";
};
networking.nameservers = [ "192.168.1.150" ];
networking.dhcpcd.extraConfig = "nohook resolv.conf";
environment.etc."${cfg.rootCertFile.path}".source = cfg.rootCertFile.source;
environment.systemPackages = with pkgs; [
step-cli
(writeShellScriptBin "ssh-host-cert-renew" ''
${lib.getExe pkgs.step-cli} ssh certificate \
--host --sign \
--root "${rootCertPath}" \
--ca-url ${cfg.caURL} \
--provisioner "${cfg.provisioner}" \
--provisioner-password-file "${provisionerPasswordPath}" \
--principal "${cfg.hostname}" \
--principal "${cfg.hostname}.john-stream.com" \
"${cfg.hostname}" "${sshKeyPath}.pub"
'')
(writeShellScriptBin "ssh-host-cert-check" "${lib.getExe' pkgs.openssh "ssh-keygen"} -Lf ${sshCertPath}")
];
systemd.services.step-ssh-host-renew = {
description = "Renew Step SSH host certificate if needed";
wantedBy = [ ];
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
path = [ pkgs.step-cli pkgs.openssh pkgs.coreutils pkgs.systemd ];
serviceConfig = {
Type = "oneshot";
User = "root";
Group = "root";
};
script = ''
set -euo pipefail
if ${lib.getExe pkgs.step-cli} ssh needs-renewal "${sshCertPath}" --expires-in "4h"; then
echo "Renewing SSH host certificate"
else
rc=$?
if [ "$rc" -eq 1 ]; then
echo "SSH host cert does not need renewal"
exit 0
fi
if [ "$rc" -eq 2 ]; then
echo "SSH host cert missing: ${sshCertPath}" >&2
exit 1
fi
echo "step ssh needs-renewal failed with rc=$rc" >&2
exit "$rc"
fi
'';
};
systemd.timers.step-ssh-host-renew = {
description = "Periodic Step SSH host certificate renewal";
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "5m";
OnUnitActiveSec = "4h";
RandomizedDelaySec = "15m";
Persistent = true;
Unit = "step-ssh-host-renew.service";
};
};
};
};
}

View File

@@ -0,0 +1,22 @@
{ inputs, ... }:
let
ipAddress = "0.0.0.0";
in
{
flake.modules.nixos.step-ca = { pkgs, ... }: {
# https://github.com/NixOS/nixpkgs/blob/nixos-23.05/nixos/modules/services/security/step-ca.nix
services.step-ca = {
enable = true;
openFirewall = true;
address = ipAddress;
port = 8443;
# https://smallstep.com/docs/step-ca/configuration/#configuration-options
settings = {
root = "";
crt = "";
};
};
environment.systemPackages = with pkgs; [ step-ca step-cli ];
};
}

46
modules/users/john.nix Normal file
View File

@@ -0,0 +1,46 @@
{ inputs, ... }:
let
username = "john";
in
{
flake = {
meta.users."${username}" = {
email = "32917998+jsl12@users.noreply.github.com";
name = "John Lancaster";
username = "${username}";
key = "";
keygrip = [
];
authorizedKeys = [
# "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIAUa4dcg1TWc4pW++uodyhX4eOqrX/QYIxFWtEP7HFJ john@john-pc-ubuntu"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMOkGLo4N/L3RYvaIZ1FmePlxa1HK0fMciZxKtRhN58F root@janus"
];
};
modules = {
nixos."${username}" = {
imports = [
(inputs.self.factory.user username true).nixos."${username}"
];
users.users."${username}" = {
openssh.authorizedKeys.keys = inputs.self.meta.users."${username}".authorizedKeys;
extraGroups = [ "docker" ];
};
};
homeManager."${username}" = with inputs.self.meta.users."${username}"; {
home.stateVersion = "25.11";
xdg.enable = true;
programs.git.settings.user.name = name;
programs.git.settings.user.email = email;
imports = with inputs.self.modules.homeManager; [
ssh
shell-tools
git
];
};
};
};
}

50
resticprofile/base.nix Normal file
View File

@@ -0,0 +1,50 @@
{
base = {
repository = "local:/mnt/backup";
password-file = "{{ .ConfigDir }}/password.txt";
status-file = "{{ .ConfigDir }}/backup-status.json";
retention = {
after-backup = true;
keep-last = "10";
keep-hourly = "8";
keep-daily = "14";
keep-weekly = "8";
};
backup = {
verbose = true;
exclude = [
".cache"
".devenv"
".rustup"
".cargo"
".venv"
".pyenv"
".vscode*"
"data/postgres"
"build"
"__pycache__"
"*.log"
"*.egg-info"
"*.csv"
"*.m4a"
".local/share/Steam"
".local/share/Trash"
"build"
"dist"
"/home/*/Pictures"
"/home/*/Videos"
"/home/*/go"
"/home/*/snap"
"/home/john/john-nas"
];
schedule-permission = "user";
schedule-priority = "background";
check-after = true;
};
prune = {
schedule-permission = "user";
schedule-lock-wait = "1h";
};
};
}

29
scripts/inspect.sh Executable file
View File

@@ -0,0 +1,29 @@
#!/usr/bin/env sh
NIX_EVAL_CMD="nix eval --json --no-warn-dirty --apply builtins.attrNames"
list_flake_attr() {
attr="$1"
flake="${2:-.}"
echo "${attr} [${flake}]:"
$NIX_EVAL_CMD "${flake}#${attr}" | jq -r '" - " + .[]'
echo
}
list_home_modules() {
list_flake_attr "homeModules" "$1"
}
list_home_configurations() {
list_flake_attr "homeConfigurations" "$1"
}
list_nixos_configurations() {
list_flake_attr "nixosConfigurations" "$1"
}
current_dir=$(readlink -f .)
flake="${1:-$current_dir}"
list_home_modules "$flake"
list_home_configurations "$flake"
list_nixos_configurations "$flake"

24
scripts/switch.sh Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env bash
FLAKE_DIR="/home/john/.config/home-manager/jsl-dendritic"
is_in_array() {
local needle="$1"
shift
local arr=("$@")
[[ " ${arr[*]} " == *" ${needle} "* ]] && echo true || echo false
}
hostname_exists() {
NIX_EVAL_CMD="nix eval --json --no-warn-dirty --apply builtins.attrNames"
mapfile -t HOME_CONFIGS < <(${NIX_EVAL_CMD} ${FLAKE_DIR}#homeConfigurations | jq -r '.[]')
is_in_array "$(hostname)" "${HOME_CONFIGS[@]}"
}
HOSTNAME=$(hostname)
if [[ "$(hostname_exists)" == "true" ]]; then
home-manager switch --flake "${FLAKE_DIR}#${HOSTNAME}"
else
echo "No homeConfiguration found for hostname '${HOSTNAME}'." >&2
exit 1
fi