Compare commits
422 Commits
fix/combob
...
remove-mod
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
956281ba38 | ||
|
|
3fb8b6587d | ||
|
|
6aee353b43 | ||
|
|
e109361e81 | ||
|
|
3c34f81a44 | ||
|
|
72e7c2e9b9 | ||
|
|
03968d8fbc | ||
|
|
2f27b3941e | ||
|
|
e9dc5b9ba6 | ||
|
|
e4ef885cd5 | ||
|
|
9fe457ebd5 | ||
|
|
4a51aa9316 | ||
|
|
308a10d6e6 | ||
|
|
90f513a08f | ||
|
|
4ddc61d132 | ||
|
|
fc0088e9ea | ||
|
|
71094f7fa1 | ||
|
|
a8516cf9c6 | ||
|
|
a89e2f877a | ||
|
|
ed78e49c47 | ||
|
|
3ef0a7919d | ||
|
|
36812d5f95 | ||
|
|
f5bcdb4ba0 | ||
|
|
b69ad0eca5 | ||
|
|
b221c29694 | ||
|
|
7dc7f09173 | ||
|
|
ec3d224e1d | ||
|
|
00c5312080 | ||
|
|
7811a56d2b | ||
|
|
e9401177b7 | ||
|
|
ef56258e8b | ||
|
|
c4d9b39a17 | ||
|
|
1f59b75c20 | ||
|
|
6b6da7b897 | ||
|
|
4391c19ee9 | ||
|
|
eb993b7060 | ||
|
|
08cb6993a8 | ||
|
|
872f640211 | ||
|
|
c58f7c573d | ||
|
|
7b807a0745 | ||
|
|
62805c66ff | ||
|
|
30b737ae1f | ||
|
|
cc41185f98 | ||
|
|
606aae7212 | ||
|
|
c31d884dc7 | ||
|
|
f546ce82f6 | ||
|
|
b173bc37f5 | ||
|
|
0c20cfb34a | ||
|
|
6c096a276d | ||
|
|
b7436b5b7f | ||
|
|
a84ab5d4bf | ||
|
|
a82ecbcbff | ||
|
|
4ae3abe8c2 | ||
|
|
90c7951704 | ||
|
|
116ff37156 | ||
|
|
f11df276a9 | ||
|
|
d44b43a937 | ||
|
|
716b74bc02 | ||
|
|
c85969c2b4 | ||
|
|
edb7dcc154 | ||
|
|
3586b4f48c | ||
|
|
9cdc6a27b6 | ||
|
|
ceecdc0eef | ||
|
|
96014c02c5 | ||
|
|
810a2c67f9 | ||
|
|
fbb28afb2f | ||
|
|
a6ef38dadd | ||
|
|
328e0b20ac | ||
|
|
7e77505316 | ||
|
|
245453b461 | ||
|
|
21e6a01cf3 | ||
|
|
302adf6f41 | ||
|
|
f754b88ae4 | ||
|
|
34d27e6bab | ||
|
|
5817713e39 | ||
|
|
cc283e88c9 | ||
|
|
1bb9f4741d | ||
|
|
0d26e991e6 | ||
|
|
961beda3e5 | ||
|
|
0a8a1d4354 | ||
|
|
daf8d8e80d | ||
|
|
011b2a5872 | ||
|
|
da06babcc2 | ||
|
|
c43eeb68a5 | ||
|
|
5e485a37f5 | ||
|
|
ce902bed0a | ||
|
|
a5d401b715 | ||
|
|
2637496059 | ||
|
|
87c8a4549b | ||
|
|
35e5f4a42a | ||
|
|
e4949755d7 | ||
|
|
b239c5bd88 | ||
|
|
4312e3fc2f | ||
|
|
62ef90e959 | ||
|
|
7fdbd2e3eb | ||
|
|
7daebd5ee0 | ||
|
|
cc8dd0564b | ||
|
|
23e52954c9 | ||
|
|
4717d1f149 | ||
|
|
e28f280036 | ||
|
|
6fa2a977df | ||
|
|
65dba2508f | ||
|
|
9884643070 | ||
|
|
5083992f7b | ||
|
|
6bd8839128 | ||
|
|
765bdb262a | ||
|
|
05c00fbe82 | ||
|
|
7e97734797 | ||
|
|
6384c4654e | ||
|
|
72d3ad09a4 | ||
|
|
a535450ec0 | ||
|
|
aaeb616f82 | ||
|
|
434edeaae1 | ||
|
|
a4efd3cb16 | ||
|
|
13131ccd6e | ||
|
|
3a8309b01f | ||
|
|
10065a7c8f | ||
|
|
176b54e29d | ||
|
|
be048d8307 | ||
|
|
52fcab30e7 | ||
|
|
d3b423328f | ||
|
|
1177e84dcc | ||
|
|
414952dfa3 | ||
|
|
24194011ac | ||
|
|
4f78a8ff94 | ||
|
|
068b5d4c1e | ||
|
|
adccef4757 | ||
|
|
980d94d47d | ||
|
|
a50b25eea2 | ||
|
|
017989841d | ||
|
|
c14a5fcc69 | ||
|
|
4f60345ba7 | ||
|
|
ece48d3b5f | ||
|
|
4eea8d24f0 | ||
|
|
49099df3fb | ||
|
|
62ccba9fb5 | ||
|
|
0b44770f1f | ||
|
|
61c3d7284a | ||
|
|
44b1be5ed4 | ||
|
|
88871bea69 | ||
|
|
5141ea047c | ||
|
|
ff6a03a646 | ||
|
|
bc379c985d | ||
|
|
69d8b029d6 | ||
|
|
f3617b0407 | ||
|
|
a5205681cc | ||
|
|
9880847d43 | ||
|
|
8aa88b22ab | ||
|
|
ff979eba61 | ||
|
|
5d1abbd303 | ||
|
|
92e9bb2ed8 | ||
|
|
ea75c9bfa9 | ||
|
|
2adf65482d | ||
|
|
5684ddf104 | ||
|
|
f74e444120 | ||
|
|
0ef57bfc8e | ||
|
|
8f43af3c48 | ||
|
|
eeaec583cb | ||
|
|
a9d1ff83f2 | ||
|
|
89cb22147c | ||
|
|
1006fc755e | ||
|
|
f100177df3 | ||
|
|
cbd3b08296 | ||
|
|
2608bee30a | ||
|
|
a29459a384 | ||
|
|
1abdd45821 | ||
|
|
b058fcc8eb | ||
|
|
24ae95a007 | ||
|
|
39510b613f | ||
|
|
dcdab61d13 | ||
|
|
f86fe07b63 | ||
|
|
84bf9fe3cf | ||
|
|
9737ce51b1 | ||
|
|
3c1c6c1942 | ||
|
|
0904c9da60 | ||
|
|
73e03c21f7 | ||
|
|
743f05a1b5 | ||
|
|
e28b8dc944 | ||
|
|
8d871aafe8 | ||
|
|
26559ff88b | ||
|
|
b39aead1db | ||
|
|
caae6c7a55 | ||
|
|
df90fb20a0 | ||
|
|
d6577ec05d | ||
|
|
9a66170aa1 | ||
|
|
3effd8fd9a | ||
|
|
86d6b42f4c | ||
|
|
5e2ffa9491 | ||
|
|
ba58d6d91a | ||
|
|
38e2d00bbc | ||
|
|
4f29f2e2ca | ||
|
|
5c23e24315 | ||
|
|
1009c61c9f | ||
|
|
0817f83b0b | ||
|
|
4f191f3ebe | ||
|
|
9f48b7a2fa | ||
|
|
b17466c84b | ||
|
|
d2814efcde | ||
|
|
6a5a83f57a | ||
|
|
9e9ab22c37 | ||
|
|
2840d3a5fc | ||
|
|
a305f98586 | ||
|
|
96fe387399 | ||
|
|
d022f26c2c | ||
|
|
d1f5a8e263 | ||
|
|
a6a5c1e21d | ||
|
|
f1867bdd7a | ||
|
|
ee8e44d255 | ||
|
|
f730f4fa06 | ||
|
|
567570e89c | ||
|
|
54797dd5f5 | ||
|
|
c70c588c1c | ||
|
|
bb6fab1168 | ||
|
|
0859a86ce0 | ||
|
|
1524dc963e | ||
|
|
eebd3fa4ec | ||
|
|
a1ff794d57 | ||
|
|
6693cda465 | ||
|
|
bf0691587d | ||
|
|
deecb966ce | ||
|
|
2d2d9c9dca | ||
|
|
e0e16de144 | ||
|
|
75c60a6103 | ||
|
|
1373670dfc | ||
|
|
03b13e9ed4 | ||
|
|
a79027c312 | ||
|
|
bdcdf4e788 | ||
|
|
e3ed9d7b4b | ||
|
|
ddf2b57b3f | ||
|
|
5ab3a164c8 | ||
|
|
073027f7c6 | ||
|
|
d0374c0d7c | ||
|
|
6137701532 | ||
|
|
1560c5f8cf | ||
|
|
5d884cecc2 | ||
|
|
8a3cade082 | ||
|
|
10b4389309 | ||
|
|
2879c72a89 | ||
|
|
547e912c4e | ||
|
|
87125f1ff7 | ||
|
|
804f606384 | ||
|
|
997c7de942 | ||
|
|
e7323999f2 | ||
|
|
31d3997358 | ||
|
|
62b748624d | ||
|
|
29f440a482 | ||
|
|
f15fd1be52 | ||
|
|
beaacf81c6 | ||
|
|
1ae023f4bf | ||
|
|
9becd4e0c4 | ||
|
|
454b09a67e | ||
|
|
787781c2ad | ||
|
|
469c6ba42d | ||
|
|
21f335fa15 | ||
|
|
d98b76e734 | ||
|
|
e2cb1fd83f | ||
|
|
91646b323a | ||
|
|
121548ffb7 | ||
|
|
966a3ee919 | ||
|
|
d007b0f1b3 | ||
|
|
034982bff2 | ||
|
|
7c37bddeea | ||
|
|
c1a87e5c6a | ||
|
|
e5bea3d49a | ||
|
|
40682972ef | ||
|
|
6035455cba | ||
|
|
9be53a9a63 | ||
|
|
29ec9dbe26 | ||
|
|
a1874c940e | ||
|
|
d115705cb4 | ||
|
|
eceb6eb999 | ||
|
|
d25cace522 | ||
|
|
3c6567e67d | ||
|
|
628e45293e | ||
|
|
954c14513a | ||
|
|
cb8a01d448 | ||
|
|
8e53e42b74 | ||
|
|
ed596a57f0 | ||
|
|
b6bccd218a | ||
|
|
1df9b6e97d | ||
|
|
58fa7ac32b | ||
|
|
110d1d4921 | ||
|
|
46aee098c4 | ||
|
|
9d6735e8c4 | ||
|
|
47c94c51b6 | ||
|
|
1eb567682c | ||
|
|
fcd83e7a60 | ||
|
|
af4b00408a | ||
|
|
aaff3b9b38 | ||
|
|
c13741602c | ||
|
|
6cbe221f44 | ||
|
|
3cf8f605d5 | ||
|
|
cc07e0ea44 | ||
|
|
ccb9340478 | ||
|
|
df096fe53b | ||
|
|
f248cc91ad | ||
|
|
1129862293 | ||
|
|
e2cb75784c | ||
|
|
a8d48b22f8 | ||
|
|
c0f2bcf751 | ||
|
|
20c23fa64b | ||
|
|
23573e16c4 | ||
|
|
eaee4e8cad | ||
|
|
10e43a8884 | ||
|
|
dc1cd03717 | ||
|
|
a71a5880c1 | ||
|
|
6b137f21de | ||
|
|
fbc14bf20f | ||
|
|
2f2f3b6898 | ||
|
|
3ae0f37bcb | ||
|
|
e49d432542 | ||
|
|
76955533cf | ||
|
|
d0ebc75135 | ||
|
|
40503306d1 | ||
|
|
da99407e74 | ||
|
|
915178765b | ||
|
|
518de45d41 | ||
|
|
7d23189c1c | ||
|
|
eec55f73a2 | ||
|
|
484d274c3c | ||
|
|
a4b20f9167 | ||
|
|
dc7291c62b | ||
|
|
a814a44bc6 | ||
|
|
86a6177126 | ||
|
|
4536a5b4f5 | ||
|
|
a9cfda9acb | ||
|
|
b9f60218d7 | ||
|
|
f69e28a133 | ||
|
|
1968230c28 | ||
|
|
9cad074732 | ||
|
|
4859a9ab7c | ||
|
|
b53ecdc89d | ||
|
|
19603e1a1c | ||
|
|
7d20f3a33b | ||
|
|
fa03c190f8 | ||
|
|
65101ad55a | ||
|
|
e5db3e269b | ||
|
|
073750e4c5 | ||
|
|
8bafbcb295 | ||
|
|
dbef6ced77 | ||
|
|
65e7f9e6ca | ||
|
|
e1062ed97c | ||
|
|
2eb1a56d8f | ||
|
|
0f499fc651 | ||
|
|
bcb7a1aa60 | ||
|
|
273c83ec27 | ||
|
|
c74d7857da | ||
|
|
11405966c6 | ||
|
|
220839598d | ||
|
|
44dcfa7844 | ||
|
|
98f685f3ca | ||
|
|
9e43285ba8 | ||
|
|
c0bc0417a6 | ||
|
|
c90b69d499 | ||
|
|
0240acdf3e | ||
|
|
92726ecebc | ||
|
|
b8e9546762 | ||
|
|
2039f034b1 | ||
|
|
0a329f43a8 | ||
|
|
bde0a2845c | ||
|
|
af3c6282c9 | ||
|
|
73ab4d2a6e | ||
|
|
cc269c4f58 | ||
|
|
20021a92ea | ||
|
|
7b54e9b033 | ||
|
|
7971eceb74 | ||
|
|
49a5763f69 | ||
|
|
10694e58c8 | ||
|
|
0d919c4fce | ||
|
|
8cccf757a8 | ||
|
|
80c8cc8628 | ||
|
|
ab63f0d7a4 | ||
|
|
06e0461ec9 | ||
|
|
60ba00dd8f | ||
|
|
90ef55f040 | ||
|
|
de81a5d810 | ||
|
|
3fe65f1f12 | ||
|
|
6bb998f9dd | ||
|
|
af7ce9b8ed | ||
|
|
b74193514d | ||
|
|
c33fd4e504 | ||
|
|
65f3cb562a | ||
|
|
355ff648d7 | ||
|
|
f314eb04d6 | ||
|
|
ebe206cdc0 | ||
|
|
2a138d3248 | ||
|
|
77810b1d4f | ||
|
|
77c840c9ba | ||
|
|
9df7e6df1e | ||
|
|
a5e51f658d | ||
|
|
98d5b3651b | ||
|
|
713a1a550e | ||
|
|
d51d656391 | ||
|
|
0f79af697e | ||
|
|
0119fc06ca | ||
|
|
5361261bd5 | ||
|
|
86e7bcc389 | ||
|
|
79281aba90 | ||
|
|
dade91c292 | ||
|
|
d285a0e716 | ||
|
|
a97128db17 | ||
|
|
ff7b49be5f | ||
|
|
0b816a2672 | ||
|
|
e6ec331da0 | ||
|
|
0b05b0b1ec | ||
|
|
efd9beba15 | ||
|
|
dc03a9183f | ||
|
|
ab3158ca07 | ||
|
|
75a1f7b67f | ||
|
|
d453720a57 | ||
|
|
a4331cc109 | ||
|
|
434ce1af49 | ||
|
|
488ee1ae63 | ||
|
|
fc2e619046 | ||
|
|
cf6c3604ca | ||
|
|
a3ea62caba | ||
|
|
e2e4837b29 | ||
|
|
96fc3d409a | ||
|
|
392f244361 | ||
|
|
d2529704d5 | ||
|
|
62a3503987 | ||
|
|
c39aa89e29 |
@@ -1,28 +0,0 @@
|
|||||||
name: "Update pinned clan-core for checks"
|
|
||||||
on:
|
|
||||||
repository_dispatch:
|
|
||||||
workflow_dispatch:
|
|
||||||
schedule:
|
|
||||||
- cron: "51 2 * * *"
|
|
||||||
jobs:
|
|
||||||
update-pinned-clan-core:
|
|
||||||
runs-on: nix
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- name: Update clan-core for checks
|
|
||||||
run: nix run .#update-clan-core-for-checks
|
|
||||||
- name: Create pull request
|
|
||||||
env:
|
|
||||||
CI_BOT_TOKEN: ${{ secrets.CI_BOT_TOKEN }}
|
|
||||||
run: |
|
|
||||||
export GIT_AUTHOR_NAME=clan-bot GIT_AUTHOR_EMAIL=clan-bot@clan.lol GIT_COMMITTER_NAME=clan-bot GIT_COMMITTER_EMAIL=clan-bot@clan.lol
|
|
||||||
git commit -am "Update pinned clan-core for checks"
|
|
||||||
|
|
||||||
# Use shared PR creation script
|
|
||||||
export PR_BRANCH="update-clan-core-for-checks"
|
|
||||||
export PR_TITLE="Update Clan Core for Checks"
|
|
||||||
export PR_BODY="This PR updates the pinned clan-core flake input that is used for checks."
|
|
||||||
|
|
||||||
./.gitea/workflows/create-pr.sh
|
|
||||||
@@ -19,8 +19,11 @@ jobs:
|
|||||||
uses: Mic92/update-flake-inputs-gitea@main
|
uses: Mic92/update-flake-inputs-gitea@main
|
||||||
with:
|
with:
|
||||||
# Exclude private flakes and update-clan-core checks flake
|
# Exclude private flakes and update-clan-core checks flake
|
||||||
|
exclude-patterns: "checks/impure/flake.nix"
|
||||||
exclude-patterns: "devFlake/private/flake.nix,checks/impure/flake.nix"
|
|
||||||
auto-merge: true
|
auto-merge: true
|
||||||
|
git-author-name: "clan-bot"
|
||||||
|
git-committer-name: "clan-bot"
|
||||||
|
git-author-email: "clan-bot@clan.lol"
|
||||||
|
git-committer-email: "clan-bot@clan.lol"
|
||||||
gitea-token: ${{ secrets.CI_BOT_TOKEN }}
|
gitea-token: ${{ secrets.CI_BOT_TOKEN }}
|
||||||
github-token: ${{ secrets.CI_BOT_GITHUB_TOKEN }}
|
github-token: ${{ secrets.CI_BOT_GITHUB_TOKEN }}
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
name: "Update private flake inputs"
|
|
||||||
on:
|
|
||||||
repository_dispatch:
|
|
||||||
workflow_dispatch:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 3 * * *" # Run daily at 3 AM
|
|
||||||
jobs:
|
|
||||||
update-private-flake:
|
|
||||||
runs-on: nix
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- name: Update private flake inputs
|
|
||||||
run: |
|
|
||||||
# Update the private flake lock file
|
|
||||||
cd devFlake/private
|
|
||||||
nix flake update
|
|
||||||
cd ../..
|
|
||||||
|
|
||||||
# Update the narHash
|
|
||||||
bash ./devFlake/update-private-narhash
|
|
||||||
- name: Create pull request
|
|
||||||
env:
|
|
||||||
CI_BOT_TOKEN: ${{ secrets.CI_BOT_TOKEN }}
|
|
||||||
run: |
|
|
||||||
export GIT_AUTHOR_NAME=clan-bot GIT_AUTHOR_EMAIL=clan-bot@clan.lol GIT_COMMITTER_NAME=clan-bot GIT_COMMITTER_EMAIL=clan-bot@clan.lol
|
|
||||||
|
|
||||||
# Check if there are any changes
|
|
||||||
if ! git diff --quiet; then
|
|
||||||
git add devFlake/private/flake.lock devFlake/private.narHash
|
|
||||||
git commit -m "Update dev flake"
|
|
||||||
|
|
||||||
# Use shared PR creation script
|
|
||||||
export PR_BRANCH="update-dev-flake"
|
|
||||||
export PR_TITLE="Update dev flake"
|
|
||||||
export PR_BODY="This PR updates the dev flake inputs and corresponding narHash."
|
|
||||||
else
|
|
||||||
echo "No changes detected in dev flake inputs"
|
|
||||||
fi
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -39,7 +39,6 @@ select
|
|||||||
# Generated files
|
# Generated files
|
||||||
pkgs/clan-app/ui/api/API.json
|
pkgs/clan-app/ui/api/API.json
|
||||||
pkgs/clan-app/ui/api/API.ts
|
pkgs/clan-app/ui/api/API.ts
|
||||||
pkgs/clan-app/ui/api/Inventory.ts
|
|
||||||
pkgs/clan-app/ui/api/modules_schemas.json
|
pkgs/clan-app/ui/api/modules_schemas.json
|
||||||
pkgs/clan-app/ui/api/schema.json
|
pkgs/clan-app/ui/api/schema.json
|
||||||
pkgs/clan-app/ui/.fonts
|
pkgs/clan-app/ui/.fonts
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
nixosModules/clanCore/vars/.* @lopter
|
|
||||||
pkgs/clan-cli/clan_cli/(secrets|vars)/.* @lopter
|
|
||||||
|
|||||||
@@ -1,210 +0,0 @@
|
|||||||
{ self, ... }:
|
|
||||||
{
|
|
||||||
clan.machines.test-backup = {
|
|
||||||
imports = [ self.nixosModules.test-backup ];
|
|
||||||
fileSystems."/".device = "/dev/null";
|
|
||||||
boot.loader.grub.device = "/dev/null";
|
|
||||||
};
|
|
||||||
clan.inventory.services = {
|
|
||||||
borgbackup.test-backup = {
|
|
||||||
roles.client.machines = [ "test-backup" ];
|
|
||||||
roles.server.machines = [ "test-backup" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
flake.nixosModules = {
|
|
||||||
test-backup =
|
|
||||||
{
|
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
dependencies =
|
|
||||||
[
|
|
||||||
pkgs.stdenv.drvPath
|
|
||||||
]
|
|
||||||
++ builtins.map (i: i.outPath) (builtins.attrValues (builtins.removeAttrs self.inputs [ "self" ]));
|
|
||||||
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
|
||||||
in
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
# Do not import inventory modules. They should be configured via 'clan.inventory'
|
|
||||||
#
|
|
||||||
# TODO: Configure localbackup via inventory
|
|
||||||
self.clanModules.localbackup
|
|
||||||
];
|
|
||||||
# Borgbackup overrides
|
|
||||||
services.borgbackup.repos.test-backups = {
|
|
||||||
path = "/var/lib/borgbackup/test-backups";
|
|
||||||
authorizedKeys = [ (builtins.readFile ../assets/ssh/pubkey) ];
|
|
||||||
};
|
|
||||||
clan.borgbackup.destinations.test-backup.repo = lib.mkForce "borg@machine:.";
|
|
||||||
|
|
||||||
clan.core.networking.targetHost = "machine";
|
|
||||||
networking.hostName = "machine";
|
|
||||||
|
|
||||||
programs.ssh.knownHosts = {
|
|
||||||
machine.hostNames = [ "machine" ];
|
|
||||||
machine.publicKey = builtins.readFile ../assets/ssh/pubkey;
|
|
||||||
};
|
|
||||||
|
|
||||||
services.openssh = {
|
|
||||||
enable = true;
|
|
||||||
settings.UsePAM = false;
|
|
||||||
settings.UseDns = false;
|
|
||||||
hostKeys = [
|
|
||||||
{
|
|
||||||
path = "/root/.ssh/id_ed25519";
|
|
||||||
type = "ed25519";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
users.users.root.openssh.authorizedKeys.keyFiles = [ ../assets/ssh/pubkey ];
|
|
||||||
|
|
||||||
# This is needed to unlock the user for sshd
|
|
||||||
# Because we use sshd without setuid binaries
|
|
||||||
users.users.borg.initialPassword = "hello";
|
|
||||||
|
|
||||||
systemd.tmpfiles.settings."vmsecrets" = {
|
|
||||||
"/root/.ssh/id_ed25519" = {
|
|
||||||
C.argument = "${../assets/ssh/privkey}";
|
|
||||||
z = {
|
|
||||||
mode = "0400";
|
|
||||||
user = "root";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
"/etc/secrets/ssh.id_ed25519" = {
|
|
||||||
C.argument = "${../assets/ssh/privkey}";
|
|
||||||
z = {
|
|
||||||
mode = "0400";
|
|
||||||
user = "root";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
"/etc/secrets/borgbackup/borgbackup.ssh" = {
|
|
||||||
C.argument = "${../assets/ssh/privkey}";
|
|
||||||
z = {
|
|
||||||
mode = "0400";
|
|
||||||
user = "root";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
"/etc/secrets/borgbackup/borgbackup.repokey" = {
|
|
||||||
C.argument = builtins.toString (pkgs.writeText "repokey" "repokey12345");
|
|
||||||
z = {
|
|
||||||
mode = "0400";
|
|
||||||
user = "root";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
clan.core.facts.secretStore = "vm";
|
|
||||||
clan.core.vars.settings.secretStore = "vm";
|
|
||||||
|
|
||||||
environment.systemPackages = [ self.packages.${pkgs.system}.clan-cli ];
|
|
||||||
environment.etc.install-closure.source = "${closureInfo}/store-paths";
|
|
||||||
nix.settings = {
|
|
||||||
substituters = lib.mkForce [ ];
|
|
||||||
hashed-mirrors = null;
|
|
||||||
connect-timeout = lib.mkForce 3;
|
|
||||||
flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
|
|
||||||
};
|
|
||||||
system.extraDependencies = dependencies;
|
|
||||||
clan.core.state.test-backups.folders = [ "/var/test-backups" ];
|
|
||||||
|
|
||||||
clan.core.state.test-service = {
|
|
||||||
preBackupScript = ''
|
|
||||||
touch /var/test-service/pre-backup-command
|
|
||||||
'';
|
|
||||||
preRestoreScript = ''
|
|
||||||
touch /var/test-service/pre-restore-command
|
|
||||||
'';
|
|
||||||
postRestoreScript = ''
|
|
||||||
touch /var/test-service/post-restore-command
|
|
||||||
'';
|
|
||||||
folders = [ "/var/test-service" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
fileSystems."/mnt/external-disk" = {
|
|
||||||
device = "/dev/vdb"; # created in tests with virtualisation.emptyDisks
|
|
||||||
autoFormat = true;
|
|
||||||
fsType = "ext4";
|
|
||||||
options = [
|
|
||||||
"defaults"
|
|
||||||
"noauto"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
clan.localbackup.targets.hdd = {
|
|
||||||
directory = "/mnt/external-disk";
|
|
||||||
preMountHook = ''
|
|
||||||
touch /run/mount-external-disk
|
|
||||||
'';
|
|
||||||
postUnmountHook = ''
|
|
||||||
touch /run/unmount-external-disk
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
perSystem =
|
|
||||||
{ pkgs, ... }:
|
|
||||||
let
|
|
||||||
clanCore = self.checks.x86_64-linux.clan-core-for-checks;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
checks = pkgs.lib.mkIf pkgs.stdenv.isLinux {
|
|
||||||
nixos-test-backups = self.clanLib.test.containerTest {
|
|
||||||
name = "nixos-test-backups";
|
|
||||||
nodes.machine = {
|
|
||||||
imports =
|
|
||||||
[
|
|
||||||
self.nixosModules.clanCore
|
|
||||||
# Some custom overrides for the backup tests
|
|
||||||
self.nixosModules.test-backup
|
|
||||||
]
|
|
||||||
++
|
|
||||||
# import the inventory generated nixosModules
|
|
||||||
self.clan.clanInternals.inventoryClass.machines.test-backup.machineImports;
|
|
||||||
clan.core.settings.directory = ./.;
|
|
||||||
};
|
|
||||||
|
|
||||||
testScript = ''
|
|
||||||
import json
|
|
||||||
start_all()
|
|
||||||
|
|
||||||
# dummy data
|
|
||||||
machine.succeed("mkdir -p /var/test-backups /var/test-service")
|
|
||||||
machine.succeed("echo testing > /var/test-backups/somefile")
|
|
||||||
|
|
||||||
# create
|
|
||||||
machine.succeed("clan backups create --debug --flake ${clanCore} test-backup")
|
|
||||||
machine.wait_until_succeeds("! systemctl is-active borgbackup-job-test-backup >&2")
|
|
||||||
machine.succeed("test -f /run/mount-external-disk")
|
|
||||||
machine.succeed("test -f /run/unmount-external-disk")
|
|
||||||
|
|
||||||
# list
|
|
||||||
backup_id = json.loads(machine.succeed("borg-job-test-backup list --json"))["archives"][0]["archive"]
|
|
||||||
out = machine.succeed("clan backups list --debug --flake ${clanCore} test-backup").strip()
|
|
||||||
print(out)
|
|
||||||
assert backup_id in out, f"backup {backup_id} not found in {out}"
|
|
||||||
localbackup_id = "hdd::/mnt/external-disk/snapshot.0"
|
|
||||||
assert localbackup_id in out, "localbackup not found in {out}"
|
|
||||||
|
|
||||||
## borgbackup restore
|
|
||||||
machine.succeed("rm -f /var/test-backups/somefile")
|
|
||||||
machine.succeed(f"clan backups restore --debug --flake ${clanCore} test-backup borgbackup 'test-backup::borg@machine:.::{backup_id}' >&2")
|
|
||||||
assert machine.succeed("cat /var/test-backups/somefile").strip() == "testing", "restore failed"
|
|
||||||
machine.succeed("test -f /var/test-service/pre-restore-command")
|
|
||||||
machine.succeed("test -f /var/test-service/post-restore-command")
|
|
||||||
machine.succeed("test -f /var/test-service/pre-backup-command")
|
|
||||||
|
|
||||||
## localbackup restore
|
|
||||||
machine.succeed("rm -rf /var/test-backups/somefile /var/test-service/ && mkdir -p /var/test-service")
|
|
||||||
machine.succeed(f"clan backups restore --debug --flake ${clanCore} test-backup localbackup '{localbackup_id}' >&2")
|
|
||||||
assert machine.succeed("cat /var/test-backups/somefile").strip() == "testing", "restore failed"
|
|
||||||
machine.succeed("test -f /var/test-service/pre-restore-command")
|
|
||||||
machine.succeed("test -f /var/test-service/post-restore-command")
|
|
||||||
machine.succeed("test -f /var/test-service/pre-backup-command")
|
|
||||||
'';
|
|
||||||
} { inherit pkgs self; };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
(
|
|
||||||
{ ... }:
|
|
||||||
{
|
|
||||||
name = "borgbackup";
|
|
||||||
|
|
||||||
nodes.machine =
|
|
||||||
{ self, pkgs, ... }:
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
self.clanModules.borgbackup
|
|
||||||
self.nixosModules.clanCore
|
|
||||||
{
|
|
||||||
services.openssh.enable = true;
|
|
||||||
services.borgbackup.repos.testrepo = {
|
|
||||||
authorizedKeys = [ (builtins.readFile ../assets/ssh/pubkey) ];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
{
|
|
||||||
clan.core.settings.directory = ./.;
|
|
||||||
clan.core.state.testState.folders = [ "/etc/state" ];
|
|
||||||
environment.etc.state.text = "hello world";
|
|
||||||
systemd.tmpfiles.settings."vmsecrets" = {
|
|
||||||
"/etc/secrets/borgbackup/borgbackup.ssh" = {
|
|
||||||
C.argument = "${../assets/ssh/privkey}";
|
|
||||||
z = {
|
|
||||||
mode = "0400";
|
|
||||||
user = "root";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
"/etc/secrets/borgbackup/borgbackup.repokey" = {
|
|
||||||
C.argument = builtins.toString (pkgs.writeText "repokey" "repokey12345");
|
|
||||||
z = {
|
|
||||||
mode = "0400";
|
|
||||||
user = "root";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
# clan.core.facts.secretStore = "vm";
|
|
||||||
clan.core.vars.settings.secretStore = "vm";
|
|
||||||
|
|
||||||
clan.borgbackup.destinations.test.repo = "borg@localhost:.";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
testScript = ''
|
|
||||||
start_all()
|
|
||||||
machine.systemctl("start --wait borgbackup-job-test.service")
|
|
||||||
assert "machine-test" in machine.succeed("BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes /run/current-system/sw/bin/borg-job-test list")
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{ fetchgit }:
|
{ fetchgit }:
|
||||||
fetchgit {
|
fetchgit {
|
||||||
url = "https://git.clan.lol/clan/clan-core.git";
|
url = "https://git.clan.lol/clan/clan-core.git";
|
||||||
rev = "ba8a80eccf091fc7f99aef3895e31617d3813d20";
|
rev = "5d884cecc2585a29b6a3596681839d081b4de192";
|
||||||
sha256 = "189srg4mc5y3prapm8day0x0wpibbqc72hrnl61agsmiq7cfmbkd";
|
sha256 = "09is1afmncamavb2q88qac37vmsijxzsy1iz1vr6gsyjq2rixaxc";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
self,
|
self,
|
||||||
lib,
|
lib,
|
||||||
inputs,
|
inputs,
|
||||||
|
privateInputs ? { },
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
@@ -33,7 +34,6 @@ in
|
|||||||
in
|
in
|
||||||
getClanCoreTestModules
|
getClanCoreTestModules
|
||||||
++ filter pathExists [
|
++ filter pathExists [
|
||||||
./backups/flake-module.nix
|
|
||||||
./devshell/flake-module.nix
|
./devshell/flake-module.nix
|
||||||
./flash/flake-module.nix
|
./flash/flake-module.nix
|
||||||
./impure/flake-module.nix
|
./impure/flake-module.nix
|
||||||
@@ -93,17 +93,15 @@ in
|
|||||||
|
|
||||||
# Base Tests
|
# Base Tests
|
||||||
nixos-test-secrets = self.clanLib.test.baseTest ./secrets nixosTestArgs;
|
nixos-test-secrets = self.clanLib.test.baseTest ./secrets nixosTestArgs;
|
||||||
nixos-test-borgbackup-legacy = self.clanLib.test.baseTest ./borgbackup-legacy nixosTestArgs;
|
|
||||||
nixos-test-wayland-proxy-virtwl = self.clanLib.test.baseTest ./wayland-proxy-virtwl nixosTestArgs;
|
nixos-test-wayland-proxy-virtwl = self.clanLib.test.baseTest ./wayland-proxy-virtwl nixosTestArgs;
|
||||||
|
|
||||||
# Container Tests
|
# Container Tests
|
||||||
nixos-test-container = self.clanLib.test.containerTest ./container nixosTestArgs;
|
nixos-test-container = self.clanLib.test.containerTest ./container nixosTestArgs;
|
||||||
nixos-test-zt-tcp-relay = self.clanLib.test.containerTest ./zt-tcp-relay nixosTestArgs;
|
|
||||||
nixos-test-matrix-synapse = self.clanLib.test.containerTest ./matrix-synapse nixosTestArgs;
|
|
||||||
nixos-test-user-firewall-iptables = self.clanLib.test.containerTest ./user-firewall/iptables.nix nixosTestArgs;
|
nixos-test-user-firewall-iptables = self.clanLib.test.containerTest ./user-firewall/iptables.nix nixosTestArgs;
|
||||||
nixos-test-user-firewall-nftables = self.clanLib.test.containerTest ./user-firewall/nftables.nix nixosTestArgs;
|
nixos-test-user-firewall-nftables = self.clanLib.test.containerTest ./user-firewall/nftables.nix nixosTestArgs;
|
||||||
|
|
||||||
service-dummy-test = import ./service-dummy-test nixosTestArgs;
|
service-dummy-test = import ./service-dummy-test nixosTestArgs;
|
||||||
|
wireguard = import ./wireguard nixosTestArgs;
|
||||||
service-dummy-test-from-flake = import ./service-dummy-test-from-flake nixosTestArgs;
|
service-dummy-test-from-flake = import ./service-dummy-test-from-flake nixosTestArgs;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -113,6 +111,8 @@ in
|
|||||||
"dont-depend-on-repo-root"
|
"dont-depend-on-repo-root"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Temporary workaround: Filter out docs package and devshell for aarch64-darwin due to CI builder hangs
|
||||||
|
# TODO: Remove this filter once macOS CI builder is updated
|
||||||
flakeOutputs =
|
flakeOutputs =
|
||||||
lib.mapAttrs' (
|
lib.mapAttrs' (
|
||||||
name: config: lib.nameValuePair "nixos-${name}" config.config.system.build.toplevel
|
name: config: lib.nameValuePair "nixos-${name}" config.config.system.build.toplevel
|
||||||
@@ -120,8 +120,18 @@ in
|
|||||||
// lib.mapAttrs' (
|
// lib.mapAttrs' (
|
||||||
name: config: lib.nameValuePair "darwin-${name}" config.config.system.build.toplevel
|
name: config: lib.nameValuePair "darwin-${name}" config.config.system.build.toplevel
|
||||||
) (self.darwinConfigurations or { })
|
) (self.darwinConfigurations or { })
|
||||||
// lib.mapAttrs' (n: lib.nameValuePair "package-${n}") packagesToBuild
|
// lib.mapAttrs' (n: lib.nameValuePair "package-${n}") (
|
||||||
// lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") self'.devShells
|
if system == "aarch64-darwin" then
|
||||||
|
lib.filterAttrs (n: _: n != "docs" && n != "deploy-docs" && n != "docs-options") packagesToBuild
|
||||||
|
else
|
||||||
|
packagesToBuild
|
||||||
|
)
|
||||||
|
// lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") (
|
||||||
|
if system == "aarch64-darwin" then
|
||||||
|
lib.filterAttrs (n: _: n != "docs") self'.devShells
|
||||||
|
else
|
||||||
|
self'.devShells
|
||||||
|
)
|
||||||
// lib.mapAttrs' (name: config: lib.nameValuePair "home-manager-${name}" config.activation-script) (
|
// lib.mapAttrs' (name: config: lib.nameValuePair "home-manager-${name}" config.activation-script) (
|
||||||
self'.legacyPackages.homeConfigurations or { }
|
self'.legacyPackages.homeConfigurations or { }
|
||||||
);
|
);
|
||||||
@@ -129,35 +139,8 @@ in
|
|||||||
nixosTests
|
nixosTests
|
||||||
// flakeOutputs
|
// flakeOutputs
|
||||||
// {
|
// {
|
||||||
# TODO: Automatically provide this check to downstream users to check their modules
|
|
||||||
clan-modules-json-compatible =
|
|
||||||
let
|
|
||||||
allSchemas = lib.mapAttrs (
|
|
||||||
_n: m:
|
|
||||||
let
|
|
||||||
schema =
|
|
||||||
(self.clanLib.evalService {
|
|
||||||
modules = [ m ];
|
|
||||||
prefix = [
|
|
||||||
"checks"
|
|
||||||
system
|
|
||||||
];
|
|
||||||
}).config.result.api.schema;
|
|
||||||
in
|
|
||||||
schema
|
|
||||||
) self.clan.modules;
|
|
||||||
in
|
|
||||||
pkgs.runCommand "combined-result"
|
|
||||||
{
|
|
||||||
schemaFile = builtins.toFile "schemas.json" (builtins.toJSON allSchemas);
|
|
||||||
}
|
|
||||||
''
|
|
||||||
mkdir -p $out
|
|
||||||
cat $schemaFile > $out/allSchemas.json
|
|
||||||
'';
|
|
||||||
|
|
||||||
clan-core-for-checks = pkgs.runCommand "clan-core-for-checks" { } ''
|
clan-core-for-checks = pkgs.runCommand "clan-core-for-checks" { } ''
|
||||||
cp -r ${pkgs.callPackage ./clan-core-for-checks.nix { }} $out
|
cp -r ${privateInputs.clan-core-for-checks} $out
|
||||||
chmod -R +w $out
|
chmod -R +w $out
|
||||||
cp ${../flake.lock} $out/flake.lock
|
cp ${../flake.lock} $out/flake.lock
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,8 @@
|
|||||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.toplevel
|
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.toplevel
|
||||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript
|
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript
|
||||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript.drvPath
|
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript.drvPath
|
||||||
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
]
|
||||||
|
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||||
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -60,6 +61,10 @@
|
|||||||
nodes.target = {
|
nodes.target = {
|
||||||
virtualisation.emptyDiskImages = [ 4096 ];
|
virtualisation.emptyDiskImages = [ 4096 ];
|
||||||
virtualisation.memorySize = 4096;
|
virtualisation.memorySize = 4096;
|
||||||
|
|
||||||
|
virtualisation.useNixStoreImage = true;
|
||||||
|
virtualisation.writableStore = true;
|
||||||
|
|
||||||
environment.systemPackages = [ self.packages.${pkgs.system}.clan-cli ];
|
environment.systemPackages = [ self.packages.${pkgs.system}.clan-cli ];
|
||||||
environment.etc."install-closure".source = "${closureInfo}/store-paths";
|
environment.etc."install-closure".source = "${closureInfo}/store-paths";
|
||||||
|
|
||||||
@@ -78,8 +83,8 @@
|
|||||||
start_all()
|
start_all()
|
||||||
|
|
||||||
# Some distros like to automount disks with spaces
|
# Some distros like to automount disks with spaces
|
||||||
machine.succeed('mkdir -p "/mnt/with spaces" && mkfs.ext4 /dev/vdb && mount /dev/vdb "/mnt/with spaces"')
|
machine.succeed('mkdir -p "/mnt/with spaces" && mkfs.ext4 /dev/vdc && mount /dev/vdc "/mnt/with spaces"')
|
||||||
machine.succeed("clan flash write --debug --flake ${self.checks.x86_64-linux.clan-core-for-checks} --yes --disk main /dev/vdb test-flash-machine-${pkgs.hostPlatform.system}")
|
machine.succeed("clan flash write --debug --flake ${self.checks.x86_64-linux.clan-core-for-checks} --yes --disk main /dev/vdc test-flash-machine-${pkgs.hostPlatform.system}")
|
||||||
'';
|
'';
|
||||||
} { inherit pkgs self; };
|
} { inherit pkgs self; };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
jobs=$(nproc)
|
jobs=$(nproc)
|
||||||
# Spawning worker in pytest is relatively slow, so we limit the number of jobs to 13
|
# Spawning worker in pytest is relatively slow, so we limit the number of jobs to 13
|
||||||
# (current number of impure tests)
|
# (current number of impure tests)
|
||||||
jobs="$((jobs > 13 ? 13 : jobs))"
|
jobs="$((jobs > 6 ? 6 : jobs))"
|
||||||
|
|
||||||
nix develop "$ROOT#clan-cli" -c bash -c "TMPDIR=/tmp python -m pytest -n $jobs -m impure ./clan_cli $@"
|
nix develop "$ROOT#clan-cli" -c bash -c "TMPDIR=/tmp python -m pytest -n $jobs -m impure ./clan_cli $@"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
self,
|
self,
|
||||||
lib,
|
lib,
|
||||||
|
privateInputs,
|
||||||
|
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
@@ -151,14 +152,15 @@
|
|||||||
let
|
let
|
||||||
closureInfo = pkgs.closureInfo {
|
closureInfo = pkgs.closureInfo {
|
||||||
rootPaths = [
|
rootPaths = [
|
||||||
self.checks.x86_64-linux.clan-core-for-checks
|
privateInputs.clan-core-for-checks
|
||||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.toplevel
|
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.toplevel
|
||||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.initialRamdisk
|
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.initialRamdisk
|
||||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.diskoScript
|
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.diskoScript
|
||||||
pkgs.stdenv.drvPath
|
pkgs.stdenv.drvPath
|
||||||
pkgs.bash.drvPath
|
pkgs.bash.drvPath
|
||||||
pkgs.buildPackages.xorg.lndir
|
pkgs.buildPackages.xorg.lndir
|
||||||
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
]
|
||||||
|
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
pkgs.lib.mkIf (pkgs.stdenv.isLinux && !pkgs.stdenv.isAarch64) {
|
pkgs.lib.mkIf (pkgs.stdenv.isLinux && !pkgs.stdenv.isAarch64) {
|
||||||
@@ -239,7 +241,7 @@
|
|||||||
target.shutdown()
|
target.shutdown()
|
||||||
except BrokenPipeError:
|
except BrokenPipeError:
|
||||||
# qemu has already exited
|
# qemu has already exited
|
||||||
pass
|
target.connected = False
|
||||||
|
|
||||||
# Create a new machine instance that boots from the installed system
|
# Create a new machine instance that boots from the installed system
|
||||||
installed_machine = create_test_machine(target, "${pkgs.qemu_test}", name="after_install")
|
installed_machine = create_test_machine(target, "${pkgs.qemu_test}", name="after_install")
|
||||||
|
|||||||
@@ -159,7 +159,8 @@ let
|
|||||||
pkgs.stdenv.drvPath
|
pkgs.stdenv.drvPath
|
||||||
pkgs.bash.drvPath
|
pkgs.bash.drvPath
|
||||||
pkgs.buildPackages.xorg.lndir
|
pkgs.buildPackages.xorg.lndir
|
||||||
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
]
|
||||||
|
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||||
};
|
};
|
||||||
|
|
||||||
in
|
in
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
(
|
|
||||||
{ pkgs, ... }:
|
|
||||||
{
|
|
||||||
name = "matrix-synapse";
|
|
||||||
|
|
||||||
nodes.machine =
|
|
||||||
{
|
|
||||||
config,
|
|
||||||
self,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
self.clanModules.matrix-synapse
|
|
||||||
self.nixosModules.clanCore
|
|
||||||
{
|
|
||||||
clan.core.settings.directory = ./.;
|
|
||||||
|
|
||||||
services.nginx.virtualHosts."matrix.clan.test" = {
|
|
||||||
enableACME = lib.mkForce false;
|
|
||||||
forceSSL = lib.mkForce false;
|
|
||||||
};
|
|
||||||
clan.nginx.acme.email = "admins@clan.lol";
|
|
||||||
clan.matrix-synapse = {
|
|
||||||
server_tld = "clan.test";
|
|
||||||
app_domain = "matrix.clan.test";
|
|
||||||
};
|
|
||||||
clan.matrix-synapse.users.admin.admin = true;
|
|
||||||
clan.matrix-synapse.users.someuser = { };
|
|
||||||
|
|
||||||
clan.core.facts.secretStore = "vm";
|
|
||||||
clan.core.vars.settings.secretStore = "vm";
|
|
||||||
clan.core.vars.settings.publicStore = "in_repo";
|
|
||||||
|
|
||||||
# because we use systemd-tmpfiles to copy the secrets, we need to a separate systemd-tmpfiles call to provision them.
|
|
||||||
boot.postBootCommands = "${config.systemd.package}/bin/systemd-tmpfiles --create /etc/tmpfiles.d/00-vmsecrets.conf";
|
|
||||||
|
|
||||||
systemd.tmpfiles.settings."00-vmsecrets" = {
|
|
||||||
# run before 00-nixos.conf
|
|
||||||
"/etc/secrets" = {
|
|
||||||
d.mode = "0700";
|
|
||||||
z.mode = "0700";
|
|
||||||
};
|
|
||||||
"/etc/secrets/matrix-synapse/synapse-registration_shared_secret" = {
|
|
||||||
f.argument = "supersecret";
|
|
||||||
z = {
|
|
||||||
mode = "0400";
|
|
||||||
user = "root";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
"/etc/secrets/matrix-password-admin/matrix-password-admin" = {
|
|
||||||
f.argument = "matrix-password1";
|
|
||||||
z = {
|
|
||||||
mode = "0400";
|
|
||||||
user = "root";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
"/etc/secrets/matrix-password-someuser/matrix-password-someuser" = {
|
|
||||||
f.argument = "matrix-password2";
|
|
||||||
z = {
|
|
||||||
mode = "0400";
|
|
||||||
user = "root";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
testScript = ''
|
|
||||||
start_all()
|
|
||||||
machine.wait_for_unit("matrix-synapse")
|
|
||||||
machine.succeed("${pkgs.netcat}/bin/nc -z -v ::1 8008")
|
|
||||||
machine.wait_until_succeeds("${pkgs.curl}/bin/curl -Ssf -L http://localhost/_matrix/static/ -H 'Host: matrix.clan.test'")
|
|
||||||
|
|
||||||
machine.systemctl("restart matrix-synapse >&2") # check if user creation is idempotent
|
|
||||||
machine.execute("journalctl -u matrix-synapse --no-pager >&2")
|
|
||||||
machine.wait_for_unit("matrix-synapse")
|
|
||||||
machine.succeed("${pkgs.netcat}/bin/nc -z -v ::1 8008")
|
|
||||||
machine.succeed("${pkgs.curl}/bin/curl -Ssf -L http://localhost/_matrix/static/ -H 'Host: matrix.clan.test'")
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
registration_shared_secret: supersecret
|
|
||||||
@@ -35,7 +35,8 @@
|
|||||||
pkgs.stdenv.drvPath
|
pkgs.stdenv.drvPath
|
||||||
pkgs.stdenvNoCC
|
pkgs.stdenvNoCC
|
||||||
self.nixosConfigurations.test-morph-machine.config.system.build.toplevel
|
self.nixosConfigurations.test-morph-machine.config.system.build.toplevel
|
||||||
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
]
|
||||||
|
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||||
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
||||||
in
|
in
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ nixosLib.runTest (
|
|||||||
|
|
||||||
# This tests the compatibility of the inventory
|
# This tests the compatibility of the inventory
|
||||||
# With the test framework
|
# With the test framework
|
||||||
# - legacy-modules
|
|
||||||
# - clan.service modules
|
# - clan.service modules
|
||||||
name = "service-dummy-test-from-flake";
|
name = "service-dummy-test-from-flake";
|
||||||
|
|
||||||
@@ -37,9 +36,6 @@ nixosLib.runTest (
|
|||||||
start_all()
|
start_all()
|
||||||
admin1.wait_for_unit("multi-user.target")
|
admin1.wait_for_unit("multi-user.target")
|
||||||
peer1.wait_for_unit("multi-user.target")
|
peer1.wait_for_unit("multi-user.target")
|
||||||
# Provided by the legacy module
|
|
||||||
print(admin1.succeed("systemctl status dummy-service"))
|
|
||||||
print(peer1.succeed("systemctl status dummy-service"))
|
|
||||||
|
|
||||||
# peer1 should have the 'hello' file
|
# peer1 should have the 'hello' file
|
||||||
peer1.succeed("cat ${nodes.peer1.clan.core.vars.generators.new-service.files.not-a-secret.path}")
|
peer1.succeed("cat ${nodes.peer1.clan.core.vars.generators.new-service.files.not-a-secret.path}")
|
||||||
|
|||||||
@@ -15,12 +15,6 @@
|
|||||||
meta.name = "foo";
|
meta.name = "foo";
|
||||||
machines.peer1 = { };
|
machines.peer1 = { };
|
||||||
machines.admin1 = { };
|
machines.admin1 = { };
|
||||||
services = {
|
|
||||||
legacy-module.default = {
|
|
||||||
roles.peer.machines = [ "peer1" ];
|
|
||||||
roles.admin.machines = [ "admin1" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
instances."test" = {
|
instances."test" = {
|
||||||
module.name = "new-service";
|
module.name = "new-service";
|
||||||
@@ -28,9 +22,6 @@
|
|||||||
roles.peer.machines.peer1 = { };
|
roles.peer.machines.peer1 = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
modules = {
|
|
||||||
legacy-module = ./legacy-module;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
modules.new-service = {
|
modules.new-service = {
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
description = "Set up dummy-module"
|
|
||||||
categories = ["System"]
|
|
||||||
features = [ "inventory" ]
|
|
||||||
|
|
||||||
[constraints]
|
|
||||||
roles.admin.min = 1
|
|
||||||
roles.admin.max = 1
|
|
||||||
---
|
|
||||||
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
imports = [
|
|
||||||
../shared.nix
|
|
||||||
];
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
imports = [
|
|
||||||
../shared.nix
|
|
||||||
];
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
{ config, ... }:
|
|
||||||
{
|
|
||||||
systemd.services.dummy-service = {
|
|
||||||
enable = true;
|
|
||||||
description = "Dummy service";
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
RemainAfterExit = true;
|
|
||||||
};
|
|
||||||
script = ''
|
|
||||||
generated_password_path="${config.clan.core.vars.generators.dummy-generator.files.generated-password.path}"
|
|
||||||
if [ ! -f "$generated_password_path" ]; then
|
|
||||||
echo "Generated password file not found: $generated_password_path"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
host_id_path="${config.clan.core.vars.generators.dummy-generator.files.host-id.path}"
|
|
||||||
if [ ! -e "$host_id_path" ]; then
|
|
||||||
echo "Host ID file not found: $host_id_path"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
# TODO: add and prompt and make it work in the test framework
|
|
||||||
clan.core.vars.generators.dummy-generator = {
|
|
||||||
files.host-id.secret = false;
|
|
||||||
files.generated-password.secret = true;
|
|
||||||
script = ''
|
|
||||||
echo $RANDOM > "$out"/host-id
|
|
||||||
echo $RANDOM > "$out"/generated-password
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -15,7 +15,6 @@ nixosLib.runTest (
|
|||||||
|
|
||||||
# This tests the compatibility of the inventory
|
# This tests the compatibility of the inventory
|
||||||
# With the test framework
|
# With the test framework
|
||||||
# - legacy-modules
|
|
||||||
# - clan.service modules
|
# - clan.service modules
|
||||||
name = "service-dummy-test";
|
name = "service-dummy-test";
|
||||||
|
|
||||||
@@ -24,12 +23,6 @@ nixosLib.runTest (
|
|||||||
inventory = {
|
inventory = {
|
||||||
machines.peer1 = { };
|
machines.peer1 = { };
|
||||||
machines.admin1 = { };
|
machines.admin1 = { };
|
||||||
services = {
|
|
||||||
legacy-module.default = {
|
|
||||||
roles.peer.machines = [ "peer1" ];
|
|
||||||
roles.admin.machines = [ "admin1" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
instances."test" = {
|
instances."test" = {
|
||||||
module.name = "new-service";
|
module.name = "new-service";
|
||||||
@@ -37,9 +30,6 @@ nixosLib.runTest (
|
|||||||
roles.peer.machines.peer1 = { };
|
roles.peer.machines.peer1 = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
modules = {
|
|
||||||
legacy-module = ./legacy-module;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
modules.new-service = {
|
modules.new-service = {
|
||||||
_class = "clan.service";
|
_class = "clan.service";
|
||||||
@@ -78,9 +68,6 @@ nixosLib.runTest (
|
|||||||
start_all()
|
start_all()
|
||||||
admin1.wait_for_unit("multi-user.target")
|
admin1.wait_for_unit("multi-user.target")
|
||||||
peer1.wait_for_unit("multi-user.target")
|
peer1.wait_for_unit("multi-user.target")
|
||||||
# Provided by the legacy module
|
|
||||||
print(admin1.succeed("systemctl status dummy-service"))
|
|
||||||
print(peer1.succeed("systemctl status dummy-service"))
|
|
||||||
|
|
||||||
# peer1 should have the 'hello' file
|
# peer1 should have the 'hello' file
|
||||||
peer1.succeed("cat ${nodes.peer1.clan.core.vars.generators.new-service.files.not-a-secret.path}")
|
peer1.succeed("cat ${nodes.peer1.clan.core.vars.generators.new-service.files.not-a-secret.path}")
|
||||||
|
|||||||
@@ -35,6 +35,13 @@
|
|||||||
services.openssh.enable = true;
|
services.openssh.enable = true;
|
||||||
services.openssh.settings.PasswordAuthentication = false;
|
services.openssh.settings.PasswordAuthentication = false;
|
||||||
users.users.root.openssh.authorizedKeys.keys = [ (builtins.readFile ../assets/ssh/pubkey) ];
|
users.users.root.openssh.authorizedKeys.keys = [ (builtins.readFile ../assets/ssh/pubkey) ];
|
||||||
|
services.openssh.knownHosts.localhost.publicKeyFile = ../assets/ssh/pubkey;
|
||||||
|
services.openssh.hostKeys = [
|
||||||
|
{
|
||||||
|
path = ../assets/ssh/privkey;
|
||||||
|
type = "ed25519";
|
||||||
|
}
|
||||||
|
];
|
||||||
security.sudo.wheelNeedsPassword = false;
|
security.sudo.wheelNeedsPassword = false;
|
||||||
|
|
||||||
boot.consoleLogLevel = lib.mkForce 100;
|
boot.consoleLogLevel = lib.mkForce 100;
|
||||||
@@ -99,12 +106,14 @@
|
|||||||
let
|
let
|
||||||
closureInfo = pkgs.closureInfo {
|
closureInfo = pkgs.closureInfo {
|
||||||
rootPaths = [
|
rootPaths = [
|
||||||
self.checks.x86_64-linux.clan-core-for-checks
|
self.packages.${pkgs.system}.clan-cli
|
||||||
|
self.checks.${pkgs.system}.clan-core-for-checks
|
||||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-update-machine.config.system.build.toplevel
|
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-update-machine.config.system.build.toplevel
|
||||||
pkgs.stdenv.drvPath
|
pkgs.stdenv.drvPath
|
||||||
pkgs.bash.drvPath
|
pkgs.bash.drvPath
|
||||||
pkgs.buildPackages.xorg.lndir
|
pkgs.buildPackages.xorg.lndir
|
||||||
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
]
|
||||||
|
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
self.clanLib.test.containerTest {
|
self.clanLib.test.containerTest {
|
||||||
@@ -151,14 +160,6 @@
|
|||||||
machine_config_path = os.path.join(flake_dir, "machines", "test-update-machine", "configuration.nix")
|
machine_config_path = os.path.join(flake_dir, "machines", "test-update-machine", "configuration.nix")
|
||||||
os.makedirs(os.path.dirname(machine_config_path), exist_ok=True)
|
os.makedirs(os.path.dirname(machine_config_path), exist_ok=True)
|
||||||
|
|
||||||
with open(machine_config_path, "w") as f:
|
|
||||||
f.write("""
|
|
||||||
{
|
|
||||||
environment.etc."update-successful".text = "ok";
|
|
||||||
}
|
|
||||||
""")
|
|
||||||
|
|
||||||
# Run clan update command
|
|
||||||
# Note: update command doesn't accept -i flag, SSH key must be in ssh-agent
|
# Note: update command doesn't accept -i flag, SSH key must be in ssh-agent
|
||||||
# Start ssh-agent and add the key
|
# Start ssh-agent and add the key
|
||||||
agent_output = subprocess.check_output(["${pkgs.openssh}/bin/ssh-agent", "-s"], text=True)
|
agent_output = subprocess.check_output(["${pkgs.openssh}/bin/ssh-agent", "-s"], text=True)
|
||||||
@@ -172,6 +173,90 @@
|
|||||||
subprocess.run(["${pkgs.openssh}/bin/ssh-add", ssh_conn.ssh_key], check=True)
|
subprocess.run(["${pkgs.openssh}/bin/ssh-add", ssh_conn.ssh_key], check=True)
|
||||||
|
|
||||||
|
|
||||||
|
##############
|
||||||
|
print("TEST: update with --build-host local")
|
||||||
|
with open(machine_config_path, "w") as f:
|
||||||
|
f.write("""
|
||||||
|
{
|
||||||
|
environment.etc."update-build-local-successful".text = "ok";
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
# rsync the flake into the container
|
||||||
|
os.environ["PATH"] = f"{os.environ['PATH']}:${pkgs.openssh}/bin"
|
||||||
|
subprocess.run(
|
||||||
|
[
|
||||||
|
"${pkgs.rsync}/bin/rsync",
|
||||||
|
"-a",
|
||||||
|
"--delete",
|
||||||
|
"-e",
|
||||||
|
"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no",
|
||||||
|
f"{str(flake_dir)}/",
|
||||||
|
f"root@192.168.1.1:/flake",
|
||||||
|
],
|
||||||
|
check=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# allow machine to ssh into itself
|
||||||
|
subprocess.run([
|
||||||
|
"ssh",
|
||||||
|
"-o", "UserKnownHostsFile=/dev/null",
|
||||||
|
"-o", "StrictHostKeyChecking=no",
|
||||||
|
f"root@192.168.1.1",
|
||||||
|
"mkdir -p /root/.ssh && chmod 700 /root/.ssh && echo \"$(cat \"${../assets/ssh/privkey}\")\" > /root/.ssh/id_ed25519 && chmod 600 /root/.ssh/id_ed25519",
|
||||||
|
], check=True)
|
||||||
|
|
||||||
|
# install the clan-cli package into the container's Nix store
|
||||||
|
subprocess.run(
|
||||||
|
[
|
||||||
|
"${pkgs.nix}/bin/nix",
|
||||||
|
"copy",
|
||||||
|
"--to",
|
||||||
|
"ssh://root@192.168.1.1",
|
||||||
|
"--no-check-sigs",
|
||||||
|
f"${self.packages.${pkgs.system}.clan-cli}",
|
||||||
|
"--extra-experimental-features", "nix-command flakes",
|
||||||
|
"--from", f"{os.environ["TMPDIR"]}/store"
|
||||||
|
],
|
||||||
|
check=True,
|
||||||
|
env={
|
||||||
|
**os.environ,
|
||||||
|
"NIX_SSHOPTS": "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Run ssh on the host to run the clan update command via --build-host local
|
||||||
|
subprocess.run([
|
||||||
|
"ssh",
|
||||||
|
"-o", "UserKnownHostsFile=/dev/null",
|
||||||
|
"-o", "StrictHostKeyChecking=no",
|
||||||
|
f"root@192.168.1.1",
|
||||||
|
"${self.packages.${pkgs.system}.clan-cli}/bin/clan",
|
||||||
|
"machines",
|
||||||
|
"update",
|
||||||
|
"--debug",
|
||||||
|
"--flake", "/flake",
|
||||||
|
"--host-key-check", "none",
|
||||||
|
"--upload-inputs", # Use local store instead of fetching from network
|
||||||
|
"--build-host", "localhost",
|
||||||
|
"test-update-machine",
|
||||||
|
"--target-host", f"root@localhost",
|
||||||
|
], check=True)
|
||||||
|
|
||||||
|
# Verify the update was successful
|
||||||
|
machine.succeed("test -f /etc/update-build-local-successful")
|
||||||
|
|
||||||
|
|
||||||
|
##############
|
||||||
|
print("TEST: update with --target-host")
|
||||||
|
|
||||||
|
with open(machine_config_path, "w") as f:
|
||||||
|
f.write("""
|
||||||
|
{
|
||||||
|
environment.etc."target-host-update-successful".text = "ok";
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
# Run clan update command
|
# Run clan update command
|
||||||
subprocess.run([
|
subprocess.run([
|
||||||
"${self.packages.${pkgs.system}.clan-cli-full}/bin/clan",
|
"${self.packages.${pkgs.system}.clan-cli-full}/bin/clan",
|
||||||
@@ -180,16 +265,18 @@
|
|||||||
"--debug",
|
"--debug",
|
||||||
"--flake", flake_dir,
|
"--flake", flake_dir,
|
||||||
"--host-key-check", "none",
|
"--host-key-check", "none",
|
||||||
"--fetch-local", # Use local store instead of fetching from network
|
"--upload-inputs", # Use local store instead of fetching from network
|
||||||
"test-update-machine",
|
"test-update-machine",
|
||||||
"--target-host", f"root@192.168.1.1:{ssh_conn.host_port}",
|
"--target-host", f"root@192.168.1.1:{ssh_conn.host_port}",
|
||||||
], check=True)
|
], check=True)
|
||||||
|
|
||||||
# Verify the update was successful
|
# Verify the update was successful
|
||||||
machine.succeed("test -f /etc/update-successful")
|
machine.succeed("test -f /etc/target-host-update-successful")
|
||||||
|
|
||||||
# Test update with --build-host
|
|
||||||
# Update configuration again to test build-host functionality
|
##############
|
||||||
|
print("TEST: update with --build-host")
|
||||||
|
# Update configuration again
|
||||||
with open(machine_config_path, "w") as f:
|
with open(machine_config_path, "w") as f:
|
||||||
f.write("""
|
f.write("""
|
||||||
{
|
{
|
||||||
@@ -205,24 +292,7 @@
|
|||||||
"--debug",
|
"--debug",
|
||||||
"--flake", flake_dir,
|
"--flake", flake_dir,
|
||||||
"--host-key-check", "none",
|
"--host-key-check", "none",
|
||||||
"--fetch-local", # Use local store instead of fetching from network
|
"--upload-inputs", # Use local store instead of fetching from network
|
||||||
"--build-host", f"root@192.168.1.1:{ssh_conn.host_port}",
|
|
||||||
"test-update-machine",
|
|
||||||
"--target-host", f"root@192.168.1.1:{ssh_conn.host_port}",
|
|
||||||
], check=True)
|
|
||||||
|
|
||||||
# Verify the second update was successful
|
|
||||||
machine.succeed("test -f /etc/build-host-update-successful")
|
|
||||||
|
|
||||||
# Run clan update command with --build-host
|
|
||||||
subprocess.run([
|
|
||||||
"${self.packages.${pkgs.system}.clan-cli-full}/bin/clan",
|
|
||||||
"machines",
|
|
||||||
"update",
|
|
||||||
"--debug",
|
|
||||||
"--flake", flake_dir,
|
|
||||||
"--host-key-check", "none",
|
|
||||||
"--fetch-local", # Use local store instead of fetching from network
|
|
||||||
"--build-host", f"root@192.168.1.1:{ssh_conn.host_port}",
|
"--build-host", f"root@192.168.1.1:{ssh_conn.host_port}",
|
||||||
"test-update-machine",
|
"test-update-machine",
|
||||||
"--target-host", f"root@192.168.1.1:{ssh_conn.host_port}",
|
"--target-host", f"root@192.168.1.1:{ssh_conn.host_port}",
|
||||||
|
|||||||
115
checks/wireguard/default.nix
Normal file
115
checks/wireguard/default.nix
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
nixosLib,
|
||||||
|
clan-core,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
nixosLib.runTest (
|
||||||
|
{ ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
machines = [
|
||||||
|
"controller1"
|
||||||
|
"controller2"
|
||||||
|
"peer1"
|
||||||
|
"peer2"
|
||||||
|
"peer3"
|
||||||
|
];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
clan-core.modules.nixosTest.clanTest
|
||||||
|
];
|
||||||
|
|
||||||
|
hostPkgs = pkgs;
|
||||||
|
|
||||||
|
name = "wireguard";
|
||||||
|
|
||||||
|
clan = {
|
||||||
|
directory = ./.;
|
||||||
|
modules."@clan/wireguard" = import ../../clanServices/wireguard/default.nix;
|
||||||
|
inventory = {
|
||||||
|
|
||||||
|
machines = lib.genAttrs machines (_: { });
|
||||||
|
|
||||||
|
instances = {
|
||||||
|
|
||||||
|
/*
|
||||||
|
wg-test-one
|
||||||
|
┌───────────────────────────────┐
|
||||||
|
│ ◄───────────── │
|
||||||
|
│ controller2 controller1
|
||||||
|
│ ▲ ─────────────► ▲ ▲
|
||||||
|
│ │ │ │ │ │ │ │ │
|
||||||
|
│ │ │ │ │ │ │ │ │
|
||||||
|
│ │ │ │ │ │ │ │ │
|
||||||
|
│ │ │ │ └───────────────┐ │ │ │ │
|
||||||
|
│ │ │ └──────────────┐ │ │ │ │ │
|
||||||
|
│ ▼ │ ▼ ▼ ▼
|
||||||
|
└─► peer2 │ peer1 peer3
|
||||||
|
│ ▲
|
||||||
|
└──────────┘
|
||||||
|
*/
|
||||||
|
|
||||||
|
wg-test-one = {
|
||||||
|
|
||||||
|
module.name = "@clan/wireguard";
|
||||||
|
module.input = "self";
|
||||||
|
|
||||||
|
roles.controller.machines."controller1".settings = {
|
||||||
|
endpoint = "192.168.1.1";
|
||||||
|
};
|
||||||
|
|
||||||
|
roles.controller.machines."controller2".settings = {
|
||||||
|
endpoint = "192.168.1.2";
|
||||||
|
};
|
||||||
|
|
||||||
|
roles.peer.machines = {
|
||||||
|
peer1.settings.controller = "controller1";
|
||||||
|
peer2.settings.controller = "controller2";
|
||||||
|
peer3.settings.controller = "controller1";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# TODO: Will this actually work with conflicting ports? Can we re-use interfaces?
|
||||||
|
#wg-test-two = {
|
||||||
|
# module.name = "@clan/wireguard";
|
||||||
|
|
||||||
|
# roles.controller.machines."controller1".settings = {
|
||||||
|
# endpoint = "192.168.1.1";
|
||||||
|
# port = 51922;
|
||||||
|
# };
|
||||||
|
|
||||||
|
# roles.peer.machines = {
|
||||||
|
# peer1 = { };
|
||||||
|
# };
|
||||||
|
#};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = ''
|
||||||
|
start_all()
|
||||||
|
|
||||||
|
# Show all addresses
|
||||||
|
machines = [peer1, peer2, peer3, controller1, controller2]
|
||||||
|
for m in machines:
|
||||||
|
m.systemctl("start network-online.target")
|
||||||
|
|
||||||
|
for m in machines:
|
||||||
|
m.wait_for_unit("network-online.target")
|
||||||
|
m.wait_for_unit("systemd-networkd.service")
|
||||||
|
|
||||||
|
print("\n\n" + "="*60)
|
||||||
|
print("STARTING PING TESTS")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
for m1 in machines:
|
||||||
|
for m2 in machines:
|
||||||
|
if m1 != m2:
|
||||||
|
print(f"\n--- Pinging from {m1.name} to {m2.name}.wg-test-one ---")
|
||||||
|
m1.wait_until_succeeds(f"ping -c1 {m2.name}.wg-test-one >&2")
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
)
|
||||||
6
checks/wireguard/sops/machines/controller1/key.json
Executable file
6
checks/wireguard/sops/machines/controller1/key.json
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"publickey": "age1rnkc2vmrupy9234clyu7fpur5kephuqs3v7qauaw5zeg00jqjdasefn3cc",
|
||||||
|
"type": "age"
|
||||||
|
}
|
||||||
|
]
|
||||||
6
checks/wireguard/sops/machines/controller2/key.json
Executable file
6
checks/wireguard/sops/machines/controller2/key.json
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"publickey": "age1t2hhg99d4p2yymuhngcy5ccutp8mvu7qwvg5cdhck303h9e7ha9qnlt635",
|
||||||
|
"type": "age"
|
||||||
|
}
|
||||||
|
]
|
||||||
6
checks/wireguard/sops/machines/peer1/key.json
Executable file
6
checks/wireguard/sops/machines/peer1/key.json
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"publickey": "age1jts52rzlqcwjc36jkp56a7fmjn3czr7kl9ta2spkfzhvfama33sqacrzzd",
|
||||||
|
"type": "age"
|
||||||
|
}
|
||||||
|
]
|
||||||
6
checks/wireguard/sops/machines/peer2/key.json
Executable file
6
checks/wireguard/sops/machines/peer2/key.json
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"publickey": "age12nqnp0zd435ckp5p0v2fv4p2x4cvur2mnxe8use2sx3fgy883vaq4ae75e",
|
||||||
|
"type": "age"
|
||||||
|
}
|
||||||
|
]
|
||||||
6
checks/wireguard/sops/machines/peer3/key.json
Executable file
6
checks/wireguard/sops/machines/peer3/key.json
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"publickey": "age1sglr4zp34drjfydzeweq43fz3uwpul3hkh53lsfa9drhuzwmkqyqn5jegp",
|
||||||
|
"type": "age"
|
||||||
|
}
|
||||||
|
]
|
||||||
15
checks/wireguard/sops/secrets/controller1-age.key/secret
Normal file
15
checks/wireguard/sops/secrets/controller1-age.key/secret
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"data": "ENC[AES256_GCM,data:zDF0RiBqaawpg+GaFkuLPomJ01Xu+lgY5JfUzaIk2j03XkCzIf8EMrmn6pRtBP3iUjPBm+gQSTQk6GHTONrixA5hRNyETV+UgQw=,iv:zUUCAGZ0cz4Tc2t/HOjVYNsdnrAOtid/Ns5ak7rnyCk=,tag:z43WtNSue4Ddf7AVu21IKA==,type:str]",
|
||||||
|
"sops": {
|
||||||
|
"age": [
|
||||||
|
{
|
||||||
|
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlY1NEdjAzQm5RMFZWY3BJ\nclp6c01FdlZFK3dOSDB4cHc1NTdwMXErMFJFCnIrRVFNZEFYOG1rVUhFd2xsbTJ2\nVkJHNmdOWXlOcHJoQ0QzM1VyZmxmcGcKLS0tIFk1cEx4dFdvNGRwK1FWdDZsb1lR\nV2d1RFZtNzZqVFdtQ1FzNStEcEgyUUkKx8tkxqJz/Ko3xgvhvd6IYiV/lRGmrY13\nUZpYWR9tsQwZAR9dLjCyVU3JRuXeGB1unXC1CO0Ff3R0A/PuuRHh+g==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastmodified": "2025-08-13T09:19:37Z",
|
||||||
|
"mac": "ENC[AES256_GCM,data:8RGOUhZ2LGmC9ugULwHDgdMrtdo9vzBm3BJmL4XTuNJKm0NlKfgNLi1E4n9DMQ+kD4hKvcwbiUcwSGE8jZD6sm7Sh3bJi/HZCoiWm/O/OIzstli2NNDBGvQBgyWZA5H+kDjZ6aEi6icNWIlm5gsty7KduABnf5B3p0Bn5Uf5Bio=,iv:sGZp0XF+mgocVzAfHF8ATdlSE/5zyz5WUSRMJqNeDQs=,tag:ymYVBRwF5BOSAu5ONU2qKw==,type:str]",
|
||||||
|
"unencrypted_suffix": "_unencrypted",
|
||||||
|
"version": "3.10.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
checks/wireguard/sops/secrets/controller1-age.key/users/admin
Symbolic link
1
checks/wireguard/sops/secrets/controller1-age.key/users/admin
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../users/admin
|
||||||
15
checks/wireguard/sops/secrets/controller2-age.key/secret
Normal file
15
checks/wireguard/sops/secrets/controller2-age.key/secret
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"data": "ENC[AES256_GCM,data:dHM7zWzqnC1QLRKYpbI2t63kOFnSaQy6ur9zlkLQf17Q03CNrqUsZtdEbwMnLR3llu7eVMhtvVRkXjEkvn3leb9HsNFmtk/DP70=,iv:roEZsBFqRypM106O5sehTzo7SySOJUJgAR738rTtOo8=,tag:VDd9/6uU0SAM7pWRLIUhUQ==,type:str]",
|
||||||
|
"sops": {
|
||||||
|
"age": [
|
||||||
|
{
|
||||||
|
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKTEVYUmVGbUtOcHZ4cnc3\nKzNETnlxaVRKYTI3eWVHdEoyc3l2SnhsZ1J3CnB2RnZrOXM5Uml6TThDUlZjY25J\nbkJ6eUZ2ckN1NWpNUU9IaE93UDJQdlEKLS0tIC95ZDhkU0R1VHhCdldxdW4zSmps\nN3NqL1cvd05hRTRPdDA3R2pzNUFFajgKS+DJH14fH9AvEAa3PoUC1jEqKAzTmExN\nl32FeHTHbGMo1PKeaFm+Eg0WSpAmFE7beBunc5B73SW30ok6x4FcQw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastmodified": "2025-08-13T09:19:47Z",
|
||||||
|
"mac": "ENC[AES256_GCM,data:77EnuBQyguvkCtobUg8/6zoLHjmeGDrSBZuIXOZBMxdbJjzhRg++qxQjuu6t0FoWATtz7u4Y3/jzUMGffr/N5HegqSq0D2bhv7AqJwBiVaOwd80fRTtM+YiP/zXsCk52Pj/Gadapg208bDPQ1BBDOyz/DrqZ7w//j+ARJjAnugI=,iv:IuTDmJKZEuHXJXjxrBw0gP2t6vpxAYEqbtpnVbavVCY=,tag:4EnpX6rOamtg1O+AaEQahQ==,type:str]",
|
||||||
|
"unencrypted_suffix": "_unencrypted",
|
||||||
|
"version": "3.10.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
checks/wireguard/sops/secrets/controller2-age.key/users/admin
Symbolic link
1
checks/wireguard/sops/secrets/controller2-age.key/users/admin
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../users/admin
|
||||||
15
checks/wireguard/sops/secrets/peer1-age.key/secret
Normal file
15
checks/wireguard/sops/secrets/peer1-age.key/secret
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"data": "ENC[AES256_GCM,data:wcSsqxTKiMAnzPwxs5DNjcSdLyjVQ9UOrZxfSbOkVfniwx6F7xz6dLNhaDq7MHQ0vRWpg28yNs7NHrp52bYFnb/+eZsis46WiCw=,iv:B4t1lvS2gC601MtsmZfEiEulLWvSGei3/LSajwFS9Vs=,tag:hnRXlZyYEFfLJUrw1SqbSQ==,type:str]",
|
||||||
|
"sops": {
|
||||||
|
"age": [
|
||||||
|
{
|
||||||
|
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAybUgya2VEdzMvRG1hdkpu\nM2pGNmcyVmcvYVZ1ZjJlY3A1bXFUUUtkMTI0CmJoRFZmejZjN2UxUXNuc1k5WnE2\nNmxIcnpNQ1lJZ3ZKSmhtSlVURXJTSUUKLS0tIGU4Wi9yZ3VYekJkVW9pNWFHblFk\na0gzbTVKUWdSam1sVjRUaUlTdVd5YWMKntRc9yb9VPOTMibp8QM5m57DilP01N/X\nPTQaw8oI40znnHdctTZz7S+W/3Te6sRnkOhFyalWmsKY0CWg/FELlA==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastmodified": "2025-08-13T09:19:58Z",
|
||||||
|
"mac": "ENC[AES256_GCM,data:8nq+ugkUJxE24lUIySySs/cAF8vnfqr936L/5F0O1QFwNrbpPmKRXkuwa6u0V+187L2952Id20Fym4ke59f3fJJsF840NCKDwDDZhBZ20q9GfOqIKImEom/Nzw6D0WXQLUT3w8EMyJ/F+UaJxnBNPR6f6+Kx4YgStYzCcA6Ahzg=,iv:VBPktEz7qwWBBnXE+xOP/EUVy7/AmNCHPoK56Yt/ZNc=,tag:qXONwOLFAlopymBEf5p4Sw==,type:str]",
|
||||||
|
"unencrypted_suffix": "_unencrypted",
|
||||||
|
"version": "3.10.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
checks/wireguard/sops/secrets/peer1-age.key/users/admin
Symbolic link
1
checks/wireguard/sops/secrets/peer1-age.key/users/admin
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../users/admin
|
||||||
15
checks/wireguard/sops/secrets/peer2-age.key/secret
Normal file
15
checks/wireguard/sops/secrets/peer2-age.key/secret
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"data": "ENC[AES256_GCM,data:4d3ri0EsDmWRtA8vzvpPRLMsSp4MIMKwvtn0n0pRY05uBPXs3KcjnweMPIeTE1nIhqnMR2o2MfLah5TCPpaFax9+wxIt74uacbg=,iv:0LBAldTC/hN4QLCxgXTl6d9UB8WmUTnj4sD2zHQuG2w=,tag:zr/RhG/AU4g9xj9l2BprKw==,type:str]",
|
||||||
|
"sops": {
|
||||||
|
"age": [
|
||||||
|
{
|
||||||
|
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvV0JnZDhlU1piU1g2cng0\ncytKOEZ6WlZlNGRGUjV3MmVMd2Nzc0ZwelgwCjBGdThCUGlXbVFYdnNoZWpJZ3Vm\nc2xkRXhxS09vdzltSVoxLzhFSVduak0KLS0tIE5DRjJ6cGxiVlB1eElHWXhxN1pJ\nYWtIMDMvb0Z6akJjUzlqeEFsNHkxL2cKpghv/QegnXimeqd9OPFouGM//jYvoVmw\n2d4mLT2JSMkEhpfGcqb6vswhdJfCiKuqr2B4bqwAnPMaykhsm8DFRQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastmodified": "2025-08-13T09:20:08Z",
|
||||||
|
"mac": "ENC[AES256_GCM,data:BzlQVAJ7HzcxNPKB3JhabqRX/uU0EElj172YecjmOflHnzz/s9xgfdAfJK/c53hXlX4LtGPnubH7a8jOolRq98zmZeBYE27+WLs2aN7Ufld6mYk90/i7u4CqR+Fh2Kfht04SlUJCjnS5A9bTPwU9XGRHJ0BiOhzTuSMUJTRaPRM=,iv:L50K5zc1o99Ix9nP0pb9PRH+VIN2yvq7JqKeVHxVXmc=,tag:XFLkSCsdbTPxbasDYYxcFQ==,type:str]",
|
||||||
|
"unencrypted_suffix": "_unencrypted",
|
||||||
|
"version": "3.10.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
checks/wireguard/sops/secrets/peer2-age.key/users/admin
Symbolic link
1
checks/wireguard/sops/secrets/peer2-age.key/users/admin
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../users/admin
|
||||||
15
checks/wireguard/sops/secrets/peer3-age.key/secret
Normal file
15
checks/wireguard/sops/secrets/peer3-age.key/secret
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"data": "ENC[AES256_GCM,data:qfLm6+g1vYnESCik9uyBeKsY6Ju2Gq3arnn2I8HHNO67Ri5BWbOQTvtz7WT8/q94RwVjv8SGeJ/fsJSpwLSrJSbqTZCPAnYwzzQ=,iv:PnA9Ao8RRELNhNQYbaorstc0KaIXRU7h3+lgDCXZFHk=,tag:VeLgYQYwqthYihIoQTwYiA==,type:str]",
|
||||||
|
"sops": {
|
||||||
|
"age": [
|
||||||
|
{
|
||||||
|
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNWVVQaDJFd0N3WHptRC9Z\nZTgxTWh5bnU1SkpqRWRXZnhPaFhpSVJmVEhrCjFvdHFYenNWaFNrdXlha09iS2xj\nOTZDcUNkcHkvTDUwNjM4Z3gxUkxreUEKLS0tIE5oY3Q2bWhsb2FSQTVGTWVSclJw\nWllrelRwT3duYjJJbTV0d3FwU1VuNlkK2eN3fHFX/sVUWom8TeZC9fddqnSCsC1+\nJRCZsG46uHDxqLcKIfdFWh++2t16XupQYk3kn+NUR/aMc3fR32Uwjw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastmodified": "2025-08-13T09:20:18Z",
|
||||||
|
"mac": "ENC[AES256_GCM,data:nUwsPcP1bsDjAHFjQ1NlVkTwyZY4B+BpzNkMx9gl0rE14j425HVLtlhlLndhRp+XMpnDldQppLAAtSdzMsrw8r5efNgTRl7cu4Fy/b9cHt84k7m0aou5lrGus9SV1bM7/fzC9Xm7CSXBcRzyDGVsKC6UBl1rx+ybh7HyAN05XSo=,iv:It57H+zUUNPkoN1D8sYwyZx5zIFIga7mydhGUHYBCGE=,tag:mBQdYqUpjPknbYa13qESyw==,type:str]",
|
||||||
|
"unencrypted_suffix": "_unencrypted",
|
||||||
|
"version": "3.10.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
checks/wireguard/sops/secrets/peer3-age.key/users/admin
Symbolic link
1
checks/wireguard/sops/secrets/peer3-age.key/users/admin
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../users/admin
|
||||||
4
checks/wireguard/sops/users/admin/key.json
Normal file
4
checks/wireguard/sops/users/admin/key.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||||
|
"type": "age"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../../../../sops/machines/controller1
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"data": "ENC[AES256_GCM,data:noe913+28JWkoDkGGMu++cc1+j5NPDoyIhWixdsowoiVO3cTWGkZ88SUGO5D,iv:ynYMljwqMcBdk8RpVcw/2Jflg2RCF28r4fKUgIAF8B4=,tag:+TsXDJgfUhKgg4iQVXKKlQ==,type:str]",
|
||||||
|
"sops": {
|
||||||
|
"age": [
|
||||||
|
{
|
||||||
|
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhYVRReTZBQ05GYmVBVjhS\nNXM5aFlhVzZRaVl6UHl6S3JnMC9Sb1dwZ1ZjCmVuS2dEVExYZWROVklUZWFCSnM2\nZnlxbVNseTM2c0Q0TjhsT3NzYmtqREUKLS0tIHBRTFpvVGt6d1cxZ2lFclRsUVhZ\nZDlWaG9PcXVrNUZKaEgxWndjUDVpYjgKt0eOhAgcYdkg9JSEakx4FjChLTn3pis+\njOkuGd4JfXMKcwC7vJV5ygQBxzVJSBw+RucP7sYCBPK0m8Voj94ntw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"recipient": "age1rnkc2vmrupy9234clyu7fpur5kephuqs3v7qauaw5zeg00jqjdasefn3cc",
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6MFJqNHNraG9DSnJZMFdz\ndU8zVXNTamxROFd1dWtuK2RiekhPdHhleVhFCi8zNWJDNXJMRUlDdjc4Q0UycTIz\nSGFGSmdnNU0wZWlDaTEwTzBqWjh6SFkKLS0tIEJOdjhOMDY2TUFLb3RPczNvMERx\nYkpSeW5VOXZvMlEvdm53MDE3aUFTNjgKyelSTjrTIR9I3rJd3krvzpsrKF1uGs4J\n4MtmQj0/3G+zPYZVBx7b3HF6B3f1Z7LYh05+z7nCnN/duXyPnDjNcg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastmodified": "2025-08-13T09:19:37Z",
|
||||||
|
"mac": "ENC[AES256_GCM,data:+DmIkPG/H6tCtf8CvB98E1QFXv08QfTcCB3CRsi+XWnIRBkryRd/Au9JahViHMdK7MED8WNf84NWTjY2yH4y824/DjI8XXNMF1iVMo0CqY42xbVHtUuhXrYeT+c8CyEw+M6zfy1jC0+Bm3WQWgagz1G6A9SZk3D2ycu0N08+axA=,iv:kwBjTYebIy5i2hagAajSwwuKnSkrM9GyrnbeQXB2e/w=,tag:EgKJ5gVGYj1NGFUduxLGfg==,type:str]",
|
||||||
|
"unencrypted_suffix": "_unencrypted",
|
||||||
|
"version": "3.10.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../../../../sops/users/admin
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
lQfR7GhivN87XoXruTGOPjVPhNu1Brt//wyc3pdwE20=
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
7470bb5c79df224a9b7f5a2259acd2e46db763c27e24cb3416c8b591cb328077
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
fd51:19c1:3b:f700
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../../../../sops/machines/controller2
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"data": "ENC[AES256_GCM,data:2kehACgvNgoYGPwnW7p86BR0yUu689Chth6qZf9zoJtuTY9ATS68dxDyBc5S,iv:qb2iDUtExegTeN3jt6SA8RnU61W5GDDhn56QXiQT4gw=,tag:pSGPICX5p6qlZ1WMVoIEYQ==,type:str]",
|
||||||
|
"sops": {
|
||||||
|
"age": [
|
||||||
|
{
|
||||||
|
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSTTR5TDY4RE9VYmlCK1dL\nWkVRcVZqVDlsbmQvUlJmdzF2b1Z1S0k3NngwCkFWNzRVaERtSmFsd0o2aFJOb0ZX\nSU9yUnVaNi9IUjJWeGRFcEpDUXo5WkEKLS0tIEczNkxiYnJsTWRoLzFhQVF1M21n\nWnZEdGV1N2N5d1FZQkJUQ1IrdGFLblkKPTpha2bxS8CCAMXWTDKX/WOcdvggaP3Y\nqewyahDNzb4ggP+LNKp55BtwFjdvoPoq4BpYOOgMRbQMMk+H1o9WFw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"recipient": "age1t2hhg99d4p2yymuhngcy5ccutp8mvu7qwvg5cdhck303h9e7ha9qnlt635",
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYcEZ6Tzk3M0pkV0tOdTBj\nenF2a0tHNnhBa0NrazMwV1VBbXBZR3pzSHpvCnBZOEU0VlFHS1FHcVpTTDdPczVV\nV0RFSlZ0VmIzWGoydEdKVXlIUE9OOEkKLS0tIFZ0cWVBR1loeVlWa2c4U3oweXE2\ncm1ja0JCS3U5Nk41dlAzV2NabDc2bDQKdgCDNnpRZlFPnEGlX6fo0SQX4yOB+E6r\ntnSwofR3xxZvkyme/6JJU5qBZXyCXEAhKMRkFyvJANXzMJAUo/Osow==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastmodified": "2025-08-13T09:19:48Z",
|
||||||
|
"mac": "ENC[AES256_GCM,data:e3EkL8vwRhLsec83Zi9DE3PKT+4RwgiffpN4QHcJKTgmDW6hzizWc5kAxbNWGJ9Qqe6sso2KY7tc+hg1lHEsmzjCbg153p8h+7lVI2XT6adi/CS8WZ2VpeL+0X9zDQCjqHmrESZAYFBdkLqO4jucdf0Pc3CKKD+N3BDDTwSUvHM=,iv:xvR7dJL8sdYen00ovrYT8PNxhB9XxSWDSRz1IK23I/o=,tag:OyhAvllBgfAp3eGeNpR/Nw==,type:str]",
|
||||||
|
"unencrypted_suffix": "_unencrypted",
|
||||||
|
"version": "3.10.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../../../../sops/users/admin
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
5Z7gbLFbXpEFfomW2pKyZBpZN5xvUtiqrIL0GVfNtQ8=
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
c3672fdb9fb31ddaf6572fc813cf7a8fe50488ef4e9d534c62d4f29da60a1a99
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
fd51:19c1:c1:aa00
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../../../../sops/machines/peer1
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"data": "ENC[AES256_GCM,data:b+akw85T3D9xc75CPLHucR//k7inpxKDvgpR8tCNKwNDRVjVHjcABhfZNLXW,iv:g11fZE8UI0MVh9GKdjR6leBlxa4wN7ZubozXG/VlBbw=,tag:0YkzWCW3zJ3Mt3br/jmTYw==,type:str]",
|
||||||
|
"sops": {
|
||||||
|
"age": [
|
||||||
|
{
|
||||||
|
"recipient": "age1jts52rzlqcwjc36jkp56a7fmjn3czr7kl9ta2spkfzhvfama33sqacrzzd",
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXWkJUR0pIa2xOSEw2dThm\nYlNuOHZCVW93Wkc5LzE4YmpUTHRkZlk3ckc4CnN4M3ZRMWNFVitCT3FyWkxaR0di\nb0NmSXFhRHJmTWg0d05OcWx1LytscEEKLS0tIEtleTFqU3JrRjVsdHpJeTNuVUhF\nWEtnOVlXVXRFamFSak5ia2F2b0JiTzAKlhOBZvZ4AN+QqAYQXvd6YNmgVS4gtkWT\nbV3bLNTgwtrDtet9NDHM8vdF+cn5RZxwFfgmTbDEow6Zm8EXfpxj/g==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6YVYyQkZqMTJYQTlyRG5Y\nbnJ2UkE1TS9FZkpSa2tQbk1hQjViMi9OcGk0CjFaZUdjU3JtNzh0bDFXdTdUVW4x\nanFqZHZjZjdzKzA2MC8vTWh3Uy82UGcKLS0tIDhyOFl3UGs3czdoMlpza3UvMlB1\nSE90MnpGc05sSCtmVWg0UVNVdmRvN2MKHlCr4U+7bsoYb+2fgT4mEseZCEjxrtLu\n55sR/4YH0vqMnIBnLTSA0e+WMrs3tQfseeJM5jY/ZNnpec1LbxkGTg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastmodified": "2025-08-13T09:19:58Z",
|
||||||
|
"mac": "ENC[AES256_GCM,data:gEoEC9D2Z7k5F8egaY1qPXT5/96FFVsyofSBivQ28Ir/9xHX2j40PAQrYRJUWsk/GAUMOyi52Wm7kPuacw+bBcdtQ0+MCDEmjkEnh1V83eZ/baey7iMmg05uO92MYY5o4e7ZkwzXoAeMCMcfO0GqjNvsYJHF1pSNa+UNDj+eflw=,iv:dnIYpvhAdvUDe9md53ll42krb0sxcHy/toqGc7JFxNA=,tag:0WkZU7GeKMD1DQTYaI+1dg==,type:str]",
|
||||||
|
"unencrypted_suffix": "_unencrypted",
|
||||||
|
"version": "3.10.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../../../../sops/users/admin
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
juK7P/92N2t2t680aLIRobHc3ts49CsZBvfZOyIKpUc=
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
b36142569a74a0de0f9b229f2a040ae33a22d53bef5e62aa6939912d0cda05ba
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
6987:50a0:9b93:4337
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../../../../sops/machines/peer2
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"data": "ENC[AES256_GCM,data:apX2sLwtq6iQgLJslFwiRMNBUe0XLzLQbhKfmb2pKiJG7jGNHUgHJz3Ls4Ca,iv:HTDatm3iD5wACTkkd3LdRNvJfnfg75RMtn9G6Q7Fqd4=,tag:Mfehlljnes5CFD1NJdk27A==,type:str]",
|
||||||
|
"sops": {
|
||||||
|
"age": [
|
||||||
|
{
|
||||||
|
"recipient": "age12nqnp0zd435ckp5p0v2fv4p2x4cvur2mnxe8use2sx3fgy883vaq4ae75e",
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVZzFyMUZsd2V2VWxOUmhP\nZE8yZTc4Q0RkZisxR25NemR1TzVDWmJZVjBVClA1MWhsU0xzSG16aUx3cWFWKzlG\nSkxrT09OTkVqLzlWejVESE1QWHVJaFkKLS0tIGxlaGVuWU43RXErNTB3c3FaUnM3\nT0N5M253anZkbnFkZWw2VHA0eWhxQW8Kd1PMtEX1h0Hd3fDLMi++gKJkzPi9FXUm\n+uYhx+pb+pJM+iLkPwP/q6AWC7T0T4bHfekkdzxrbsKMi73x/GrOiw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqVzRIMWdlNjVwTURyMFkv\nSUhiajZkZVNuWklRYit6cno4UzNDa2szOFN3CkQ2TWhHb25pbmR1MlBsRXNLL2lx\ncVZ3c3BsWXN2aS9UUVYvN3I4S0xUSmMKLS0tIE5FV0U5aXVUZk9XL0U0Z2ZSNGd5\nbU9zY3IvMlpSNVFLYkRNQUpUYVZOWFUK7j4Otzb8CJTcT7aAj9/irxHEDXh1HkTg\nzz7Ho8/ZncNtaCVHlHxjTgVW9d5aIx8fSsV9LRCFwHMtNzvwj1Nshg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastmodified": "2025-08-13T09:20:08Z",
|
||||||
|
"mac": "ENC[AES256_GCM,data:e7WNVEz78noHBiz6S3A6qNfop+yBXB3rYN0k4GvaQKz3b99naEHuqIF8Smzzt4XrbbiPKu2iLa5ddLBlqqsi32UQUB8JS9TY7hvW8ol+jpn0VxusGCXW9ThdDEsM/hXiPyr331C73zTvbOYI1hmcGMlJL9cunVRO9rkMtEqhEfo=,iv:6zt7wjIs1y5xDHNK+yLOwoOuUpY7/dOGJGT6UWAFeOg=,tag:gzFTgoxhoLzUV0lvzOhhfg==,type:str]",
|
||||||
|
"unencrypted_suffix": "_unencrypted",
|
||||||
|
"version": "3.10.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../../../../sops/users/admin
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
XI9uSaQRDBCb82cMnGzGJcbqRfDG/IXZobyeL+kV03k=
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
360f9fce4a984eb87ce2a673eb5341ecb89c0f62126548d45ef25ff5243dd646
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3b21:3ced:003e:89b3
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../../../../sops/machines/peer3
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"data": "ENC[AES256_GCM,data:Gluvjes/3oH5YsDq00JDJyJgoEFcj56smioMArPSt309MDGExYX2QsCzeO1q,iv:oBBJRDdTj/1dWEvzhdFKQ2WfeCKyavKMLmnMbqnU5PM=,tag:2WNFxKz2dWyVcybpm5N4iw==,type:str]",
|
||||||
|
"sops": {
|
||||||
|
"age": [
|
||||||
|
{
|
||||||
|
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtQWpjRmhZTFdPa2VSZkFN\nbUczMlY5bDBmMTdoMy8xcWxMaXpWVitMZGdjCnRWb2Y3eGpHU1hmNHRJVFBqbU5w\nVEZGdUIrQXk0U0dUUEZ6bE5EMFpTRHMKLS0tIGpYSmZmQThJUTlvTHpjc05ZVlM4\nQWhTOWxnUHZnYlJ3czE3ZUJ0L3ozWTQK3a7N0Zpzo4sUezYveqvKR49RUdJL23eD\n+cK5lk2xbtj+YHkeG+dg7UlHfDaicj0wnFH1KLuWmNd1ONa6eQp3BQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"recipient": "age1sglr4zp34drjfydzeweq43fz3uwpul3hkh53lsfa9drhuzwmkqyqn5jegp",
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3a2FOWlVsSkdnendrYmUz\ndEpuL1hZSWNFTUtDYm14S3V1aW9KS3hsazJRCkp2SkFFbi9hbGJpNks1MlNTL0s5\nTk5pcUMxaEJobkcvWmRGeU9jMkdNdzAKLS0tIDR6M0Y5eE1ETHJJejAzVW1EYy9v\nZCtPWHJPUkhuWnRzSGhMUUtTa280UmMKXvtnxyop7PmRvTOFkV80LziDjhGh93Pf\nYwhD/ByD/vMmr21Fd6PVHOX70FFT30BdnMc1/wt7c/0iAw4w4GoQsA==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastmodified": "2025-08-13T09:20:18Z",
|
||||||
|
"mac": "ENC[AES256_GCM,data:3nXMTma0UYXCco+EM8UW45cth7DVMboFBKyesL86GmaG6OlTkA2/25AeDrtSVO13a5c2jC6yNFK5dE6pSe5R9f0BoDF7d41mgc85zyn+LGECNWKC6hy6gADNSDD6RRuV1S3FisFQl1F1LD8LiSWmg/XNMZzChNlHYsCS8M+I84g=,iv:pu5VVXAVPmVoXy0BJ+hq5Ar8R0pZttKSYa4YS+dhDNc=,tag:xp1S/4qExnxMTGwhfLJrkA==,type:str]",
|
||||||
|
"unencrypted_suffix": "_unencrypted",
|
||||||
|
"version": "3.10.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../../../../sops/users/admin
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
t6qN4VGLR+VMhrBDNKQEXZVyRsEXs1/nGFRs5DI82F8=
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
e3facc99b73fe029d4c295f71829a83f421f38d82361cf412326398175da162a
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
e42b:bf85:33f4:f0b1
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
(
|
|
||||||
{ pkgs, ... }:
|
|
||||||
{
|
|
||||||
name = "zt-tcp-relay";
|
|
||||||
|
|
||||||
nodes.machine =
|
|
||||||
{ self, ... }:
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
self.nixosModules.clanCore
|
|
||||||
self.clanModules.zt-tcp-relay
|
|
||||||
{
|
|
||||||
clan.core.settings.directory = ./.;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
testScript = ''
|
|
||||||
start_all()
|
|
||||||
machine.wait_for_unit("zt-tcp-relay.service")
|
|
||||||
out = machine.succeed("${pkgs.netcat}/bin/nc -z -v localhost 4443")
|
|
||||||
print(out)
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
description = "Convenient Administration for the Clan App"
|
|
||||||
categories = ["Utility"]
|
|
||||||
features = [ "inventory", "deprecated" ]
|
|
||||||
---
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
imports = [ ./roles/default.nix ];
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
{ lib, config, ... }:
|
|
||||||
{
|
|
||||||
|
|
||||||
options.clan.admin = {
|
|
||||||
allowedKeys = lib.mkOption {
|
|
||||||
default = { };
|
|
||||||
type = lib.types.attrsOf lib.types.str;
|
|
||||||
description = "The allowed public keys for ssh access to the admin user";
|
|
||||||
example = {
|
|
||||||
"key_1" = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD...";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
# Bad practice.
|
|
||||||
# Should we add 'clanModules' to specialArgs?
|
|
||||||
imports = [
|
|
||||||
../../sshd
|
|
||||||
../../root-password
|
|
||||||
];
|
|
||||||
config = {
|
|
||||||
|
|
||||||
warnings = [
|
|
||||||
"The clan.admin module is deprecated and will be removed on 2025-07-15.
|
|
||||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
|
||||||
(https://docs.clan.lol/reference/clanServices)."
|
|
||||||
];
|
|
||||||
|
|
||||||
users.users.root.openssh.authorizedKeys.keys = builtins.attrValues config.clan.admin.allowedKeys;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
---
|
|
||||||
description = "Set up automatic upgrades"
|
|
||||||
categories = ["System"]
|
|
||||||
features = [ "inventory", "deprecated" ]
|
|
||||||
---
|
|
||||||
|
|
||||||
Whether to periodically upgrade NixOS to the latest version. If enabled, a
|
|
||||||
systemd timer will run `nixos-rebuild switch --upgrade` once a day.
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
cfg = config.clan.auto-upgrade;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.clan.auto-upgrade = {
|
|
||||||
flake = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "Flake reference";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
|
|
||||||
warnings = [
|
|
||||||
"The clan.auto-upgrade module is deprecated and will be removed on 2025-07-15.
|
|
||||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
|
||||||
(https://docs.clan.lol/reference/clanServices)."
|
|
||||||
];
|
|
||||||
|
|
||||||
system.autoUpgrade = {
|
|
||||||
inherit (cfg) flake;
|
|
||||||
enable = true;
|
|
||||||
dates = "02:00";
|
|
||||||
randomizedDelaySec = "45min";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
description = "Statically configure borgbackup with sane defaults."
|
|
||||||
---
|
|
||||||
!!! Danger "Deprecated"
|
|
||||||
Use [borgbackup](borgbackup.md) instead.
|
|
||||||
|
|
||||||
Don't use borgbackup-static through [inventory](../../concepts/inventory.md).
|
|
||||||
|
|
||||||
This module implements the `borgbackup` backend and implements sane defaults
|
|
||||||
for backup management through `borgbackup` for members of the clan.
|
|
||||||
|
|
||||||
Configure target machines where the backups should be sent to through `targets`.
|
|
||||||
|
|
||||||
Configure machines that should be backuped either through `includeMachines`
|
|
||||||
which will exclusively add the included machines to be backuped, or through
|
|
||||||
`excludeMachines`, which will add every machine except the excluded machine to the backup.
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
{ lib, config, ... }:
|
|
||||||
let
|
|
||||||
dir = config.clan.core.settings.directory;
|
|
||||||
machineDir = dir + "/machines/";
|
|
||||||
in
|
|
||||||
{
|
|
||||||
imports = [ ../borgbackup ];
|
|
||||||
|
|
||||||
options.clan.borgbackup-static = {
|
|
||||||
excludeMachines = lib.mkOption {
|
|
||||||
type = lib.types.listOf lib.types.str;
|
|
||||||
example = lib.literalExpression "[ config.clan.core.settings.machine.name ]";
|
|
||||||
default = [ ];
|
|
||||||
description = ''
|
|
||||||
Machines that should not be backuped.
|
|
||||||
Mutually exclusive with includeMachines.
|
|
||||||
If this is not empty, every other machine except the targets in the clan will be backuped by this module.
|
|
||||||
If includeMachines is set, only the included machines will be backuped.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
includeMachines = lib.mkOption {
|
|
||||||
type = lib.types.listOf lib.types.str;
|
|
||||||
example = lib.literalExpression "[ config.clan.core.settings.machine.name ]";
|
|
||||||
default = [ ];
|
|
||||||
description = ''
|
|
||||||
Machines that should be backuped.
|
|
||||||
Mutually exclusive with excludeMachines.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
targets = lib.mkOption {
|
|
||||||
type = lib.types.listOf lib.types.str;
|
|
||||||
default = [ ];
|
|
||||||
description = ''
|
|
||||||
Machines that should act as target machines for backups.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config.services.borgbackup.repos =
|
|
||||||
let
|
|
||||||
machines = builtins.readDir machineDir;
|
|
||||||
borgbackupIpMachinePath = machines: machineDir + machines + "/facts/borgbackup.ssh.pub";
|
|
||||||
filteredMachines =
|
|
||||||
if ((builtins.length config.clan.borgbackup-static.includeMachines) != 0) then
|
|
||||||
lib.filterAttrs (name: _: (lib.elem name config.clan.borgbackup-static.includeMachines)) machines
|
|
||||||
else
|
|
||||||
lib.filterAttrs (name: _: !(lib.elem name config.clan.borgbackup-static.excludeMachines)) machines;
|
|
||||||
machinesMaybeKey = lib.mapAttrsToList (
|
|
||||||
machine: _:
|
|
||||||
let
|
|
||||||
fullPath = borgbackupIpMachinePath machine;
|
|
||||||
in
|
|
||||||
if builtins.pathExists fullPath then machine else null
|
|
||||||
) filteredMachines;
|
|
||||||
machinesWithKey = lib.filter (x: x != null) machinesMaybeKey;
|
|
||||||
hosts = builtins.map (machine: {
|
|
||||||
name = machine;
|
|
||||||
value = {
|
|
||||||
path = "/var/lib/borgbackup/${machine}";
|
|
||||||
authorizedKeys = [ (builtins.readFile (borgbackupIpMachinePath machine)) ];
|
|
||||||
};
|
|
||||||
}) machinesWithKey;
|
|
||||||
in
|
|
||||||
lib.mkIf
|
|
||||||
(builtins.any (
|
|
||||||
target: target == config.clan.core.settings.machine.name
|
|
||||||
) config.clan.borgbackup-static.targets)
|
|
||||||
(if (builtins.listToAttrs hosts) != null then builtins.listToAttrs hosts else { });
|
|
||||||
|
|
||||||
config.clan.borgbackup.destinations =
|
|
||||||
let
|
|
||||||
destinations = builtins.map (d: {
|
|
||||||
name = d;
|
|
||||||
value = {
|
|
||||||
repo = "borg@${d}:/var/lib/borgbackup/${config.clan.core.settings.machine.name}";
|
|
||||||
};
|
|
||||||
}) config.clan.borgbackup-static.targets;
|
|
||||||
in
|
|
||||||
lib.mkIf (builtins.any (
|
|
||||||
target: target == config.clan.core.settings.machine.name
|
|
||||||
) config.clan.borgbackup-static.includeMachines) (builtins.listToAttrs destinations);
|
|
||||||
|
|
||||||
config.assertions = [
|
|
||||||
{
|
|
||||||
assertion =
|
|
||||||
!(
|
|
||||||
((builtins.length config.clan.borgbackup-static.excludeMachines) != 0)
|
|
||||||
&& ((builtins.length config.clan.borgbackup-static.includeMachines) != 0)
|
|
||||||
);
|
|
||||||
message = ''
|
|
||||||
The options:
|
|
||||||
config.clan.borgbackup-static.excludeMachines = [${builtins.toString config.clan.borgbackup-static.excludeMachines}]
|
|
||||||
and
|
|
||||||
config.clan.borgbackup-static.includeMachines = [${builtins.toString config.clan.borgbackup-static.includeMachines}]
|
|
||||||
are mutually exclusive.
|
|
||||||
Use excludeMachines to exclude certain machines and backup the other clan machines.
|
|
||||||
Use include machines to only backup certain machines.
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
];
|
|
||||||
config.warnings = lib.optional (
|
|
||||||
builtins.length config.clan.borgbackup-static.targets > 0
|
|
||||||
) "The borgbackup-static module is deprecated use the service via the inventory interface instead.";
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
description = "Efficient, deduplicating backup program with optional compression and secure encryption."
|
|
||||||
categories = ["System"]
|
|
||||||
features = [ "inventory", "deprecated" ]
|
|
||||||
---
|
|
||||||
BorgBackup (short: Borg) gives you:
|
|
||||||
|
|
||||||
- Space efficient storage of backups.
|
|
||||||
- Secure, authenticated encryption.
|
|
||||||
- Compression: lz4, zstd, zlib, lzma or none.
|
|
||||||
- Mountable backups with FUSE.
|
|
||||||
- Easy installation on multiple platforms: Linux, macOS, BSD, …
|
|
||||||
- Free software (BSD license).
|
|
||||||
- Backed by a large and active open-source community.
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
# Dont import this file
|
|
||||||
# It is only here for backwards compatibility.
|
|
||||||
# Dont author new modules with this file.
|
|
||||||
{
|
|
||||||
imports = [ ./roles/client.nix ];
|
|
||||||
}
|
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
# Instances might be empty, if the module is not used via the inventory
|
|
||||||
instances = config.clan.inventory.services.borgbackup or { };
|
|
||||||
# roles = { ${role_name} :: { machines :: [string] } }
|
|
||||||
allServers = lib.foldlAttrs (
|
|
||||||
acc: _instanceName: instanceConfig:
|
|
||||||
acc
|
|
||||||
++ (
|
|
||||||
if builtins.elem machineName instanceConfig.roles.client.machines then
|
|
||||||
instanceConfig.roles.server.machines
|
|
||||||
else
|
|
||||||
[ ]
|
|
||||||
)
|
|
||||||
) [ ] instances;
|
|
||||||
|
|
||||||
machineName = config.clan.core.settings.machine.name;
|
|
||||||
|
|
||||||
cfg = config.clan.borgbackup;
|
|
||||||
preBackupScript = ''
|
|
||||||
declare -A preCommandErrors
|
|
||||||
|
|
||||||
${lib.concatMapStringsSep "\n" (
|
|
||||||
state:
|
|
||||||
lib.optionalString (state.preBackupCommand != null) ''
|
|
||||||
echo "Running pre-backup command for ${state.name}"
|
|
||||||
if ! /run/current-system/sw/bin/${state.preBackupCommand}; then
|
|
||||||
preCommandErrors["${state.name}"]=1
|
|
||||||
fi
|
|
||||||
''
|
|
||||||
) (lib.attrValues config.clan.core.state)}
|
|
||||||
|
|
||||||
if [[ ''${#preCommandErrors[@]} -gt 0 ]]; then
|
|
||||||
echo "pre-backup commands failed for the following services:"
|
|
||||||
for state in "''${!preCommandErrors[@]}"; do
|
|
||||||
echo " $state"
|
|
||||||
done
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.clan.borgbackup.destinations = lib.mkOption {
|
|
||||||
type = lib.types.attrsOf (
|
|
||||||
lib.types.submodule (
|
|
||||||
{ name, ... }:
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
name = lib.mkOption {
|
|
||||||
type = lib.types.strMatching "^[a-zA-Z0-9._-]+$";
|
|
||||||
default = name;
|
|
||||||
description = "the name of the backup job";
|
|
||||||
};
|
|
||||||
repo = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "the borgbackup repository to backup to";
|
|
||||||
};
|
|
||||||
rsh = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
default = "ssh -i ${
|
|
||||||
config.clan.core.vars.generators.borgbackup.files."borgbackup.ssh".path
|
|
||||||
} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=Yes";
|
|
||||||
defaultText = "ssh -i \${config.clan.core.vars.generators.borgbackup.files.\"borgbackup.ssh\".path} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null";
|
|
||||||
description = "the rsh to use for the backup";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
default = { };
|
|
||||||
description = ''
|
|
||||||
destinations where the machine should be backuped to
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
options.clan.borgbackup.exclude = lib.mkOption {
|
|
||||||
type = lib.types.listOf lib.types.str;
|
|
||||||
example = [ "*.pyc" ];
|
|
||||||
default = [ ];
|
|
||||||
description = ''
|
|
||||||
Directories/Files to exclude from the backup.
|
|
||||||
Use * as a wildcard.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
|
|
||||||
warnings = [
|
|
||||||
"The clan.borgbackup module is deprecated and will be removed on 2025-07-15.
|
|
||||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
|
||||||
(https://docs.clan.lol/reference/clanServices)."
|
|
||||||
];
|
|
||||||
|
|
||||||
# Destinations
|
|
||||||
clan.borgbackup.destinations =
|
|
||||||
let
|
|
||||||
destinations = builtins.map (serverName: {
|
|
||||||
name = serverName;
|
|
||||||
value = {
|
|
||||||
repo = "borg@${serverName}:/var/lib/borgbackup/${machineName}";
|
|
||||||
};
|
|
||||||
}) allServers;
|
|
||||||
in
|
|
||||||
(builtins.listToAttrs destinations);
|
|
||||||
|
|
||||||
# Derived from the destinations
|
|
||||||
systemd.services = lib.mapAttrs' (
|
|
||||||
_: dest:
|
|
||||||
lib.nameValuePair "borgbackup-job-${dest.name}" {
|
|
||||||
# since borgbackup mounts the system read-only, we need to run in a
|
|
||||||
# ExecStartPre script, so we can generate additional files.
|
|
||||||
serviceConfig.ExecStartPre = [
|
|
||||||
''+${pkgs.writeShellScript "borgbackup-job-${dest.name}-pre-backup-commands" preBackupScript}''
|
|
||||||
];
|
|
||||||
}
|
|
||||||
) cfg.destinations;
|
|
||||||
|
|
||||||
services.borgbackup.jobs = lib.mapAttrs (_: dest: {
|
|
||||||
paths = lib.unique (
|
|
||||||
lib.flatten (map (state: state.folders) (lib.attrValues config.clan.core.state))
|
|
||||||
);
|
|
||||||
exclude = cfg.exclude;
|
|
||||||
repo = dest.repo;
|
|
||||||
environment.BORG_RSH = dest.rsh;
|
|
||||||
compression = "auto,zstd";
|
|
||||||
startAt = "*-*-* 01:00:00";
|
|
||||||
persistentTimer = true;
|
|
||||||
|
|
||||||
encryption = {
|
|
||||||
mode = "repokey";
|
|
||||||
passCommand = "cat ${config.clan.core.vars.generators.borgbackup.files."borgbackup.repokey".path}";
|
|
||||||
};
|
|
||||||
|
|
||||||
prune.keep = {
|
|
||||||
within = "1d"; # Keep all archives from the last day
|
|
||||||
daily = 7;
|
|
||||||
weekly = 4;
|
|
||||||
monthly = 0;
|
|
||||||
};
|
|
||||||
}) cfg.destinations;
|
|
||||||
|
|
||||||
environment.systemPackages = [
|
|
||||||
(pkgs.writeShellApplication {
|
|
||||||
name = "borgbackup-create";
|
|
||||||
runtimeInputs = [ config.systemd.package ];
|
|
||||||
text = ''
|
|
||||||
${lib.concatMapStringsSep "\n" (dest: ''
|
|
||||||
systemctl start borgbackup-job-${dest.name}
|
|
||||||
'') (lib.attrValues cfg.destinations)}
|
|
||||||
'';
|
|
||||||
})
|
|
||||||
(pkgs.writeShellApplication {
|
|
||||||
name = "borgbackup-list";
|
|
||||||
runtimeInputs = [ pkgs.jq ];
|
|
||||||
text = ''
|
|
||||||
(${
|
|
||||||
lib.concatMapStringsSep "\n" (
|
|
||||||
dest:
|
|
||||||
# we need yes here to skip the changed url verification
|
|
||||||
''echo y | /run/current-system/sw/bin/borg-job-${dest.name} list --json | jq '[.archives[] | {"name": ("${dest.name}::${dest.repo}::" + .name)}]' ''
|
|
||||||
) (lib.attrValues cfg.destinations)
|
|
||||||
}) | jq -s 'add // []'
|
|
||||||
'';
|
|
||||||
})
|
|
||||||
(pkgs.writeShellApplication {
|
|
||||||
name = "borgbackup-restore";
|
|
||||||
runtimeInputs = [ pkgs.gawk ];
|
|
||||||
text = ''
|
|
||||||
cd /
|
|
||||||
IFS=':' read -ra FOLDER <<< "''${FOLDERS-}"
|
|
||||||
job_name=$(echo "$NAME" | awk -F'::' '{print $1}')
|
|
||||||
backup_name=''${NAME#"$job_name"::}
|
|
||||||
if [[ ! -x /run/current-system/sw/bin/borg-job-"$job_name" ]]; then
|
|
||||||
echo "borg-job-$job_name not found: Backup name is invalid" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo y | /run/current-system/sw/bin/borg-job-"$job_name" extract "$backup_name" "''${FOLDER[@]}"
|
|
||||||
'';
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
clan.core.vars.generators.borgbackup = {
|
|
||||||
files."borgbackup.ssh.pub".secret = false;
|
|
||||||
files."borgbackup.ssh" = { };
|
|
||||||
files."borgbackup.repokey" = { };
|
|
||||||
|
|
||||||
migrateFact = "borgbackup";
|
|
||||||
runtimeInputs = [
|
|
||||||
pkgs.coreutils
|
|
||||||
pkgs.openssh
|
|
||||||
pkgs.xkcdpass
|
|
||||||
];
|
|
||||||
script = ''
|
|
||||||
ssh-keygen -t ed25519 -N "" -C "" -f "$out"/borgbackup.ssh
|
|
||||||
xkcdpass -n 4 -d - > "$out"/borgbackup.repokey
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
clan.core.backups.providers.borgbackup = {
|
|
||||||
list = "borgbackup-list";
|
|
||||||
create = "borgbackup-create";
|
|
||||||
restore = "borgbackup-restore";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
{ config, lib, ... }:
|
|
||||||
let
|
|
||||||
dir = config.clan.core.settings.directory;
|
|
||||||
machineDir = dir + "/vars/per-machine/";
|
|
||||||
machineName = config.clan.core.settings.machine.name;
|
|
||||||
|
|
||||||
# Instances might be empty, if the module is not used via the inventory
|
|
||||||
#
|
|
||||||
# Type: { ${instanceName} :: { roles :: Roles } }
|
|
||||||
# Roles :: { ${role_name} :: { machines :: [string] } }
|
|
||||||
instances = config.clan.inventory.services.borgbackup or { };
|
|
||||||
|
|
||||||
allClients = lib.foldlAttrs (
|
|
||||||
acc: _instanceName: instanceConfig:
|
|
||||||
acc
|
|
||||||
++ (
|
|
||||||
if (builtins.elem machineName instanceConfig.roles.server.machines) then
|
|
||||||
instanceConfig.roles.client.machines
|
|
||||||
else
|
|
||||||
[ ]
|
|
||||||
)
|
|
||||||
) [ ] instances;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
clan.borgbackup.directory = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
default = "/var/lib/borgbackup";
|
|
||||||
description = ''
|
|
||||||
The directory where the borgbackup repositories are stored.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
config.services.borgbackup.repos =
|
|
||||||
let
|
|
||||||
borgbackupIpMachinePath = machine: machineDir + machine + "/borgbackup/borgbackup.ssh.pub/value";
|
|
||||||
|
|
||||||
machinesMaybeKey = builtins.map (
|
|
||||||
machine:
|
|
||||||
let
|
|
||||||
fullPath = borgbackupIpMachinePath machine;
|
|
||||||
in
|
|
||||||
if builtins.pathExists fullPath then
|
|
||||||
machine
|
|
||||||
else
|
|
||||||
lib.warn ''
|
|
||||||
Machine ${machine} does not have a borgbackup key at ${fullPath},
|
|
||||||
run `clan vars generate ${machine}` to generate it.
|
|
||||||
'' null
|
|
||||||
) allClients;
|
|
||||||
|
|
||||||
machinesWithKey = lib.filter (x: x != null) machinesMaybeKey;
|
|
||||||
|
|
||||||
hosts = builtins.map (machine: {
|
|
||||||
name = machine;
|
|
||||||
value = {
|
|
||||||
path = "${config.clan.borgbackup.directory}/${machine}";
|
|
||||||
authorizedKeys = [ (builtins.readFile (borgbackupIpMachinePath machine)) ];
|
|
||||||
};
|
|
||||||
}) machinesWithKey;
|
|
||||||
in
|
|
||||||
if (builtins.listToAttrs hosts) != [ ] then builtins.listToAttrs hosts else { };
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
description = "Set up data-mesher"
|
|
||||||
categories = ["System"]
|
|
||||||
features = [ "inventory" ]
|
|
||||||
|
|
||||||
[constraints]
|
|
||||||
roles.admin.min = 1
|
|
||||||
roles.admin.max = 1
|
|
||||||
---
|
|
||||||
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
lib: {
|
|
||||||
|
|
||||||
machines =
|
|
||||||
config:
|
|
||||||
let
|
|
||||||
instanceNames = builtins.attrNames config.clan.inventory.services.data-mesher;
|
|
||||||
instanceName = builtins.head instanceNames;
|
|
||||||
dataMesherInstances = config.clan.inventory.services.data-mesher.${instanceName};
|
|
||||||
|
|
||||||
uniqueStrings = list: builtins.attrNames (builtins.groupBy lib.id list);
|
|
||||||
in
|
|
||||||
rec {
|
|
||||||
admins = dataMesherInstances.roles.admin.machines or [ ];
|
|
||||||
signers = dataMesherInstances.roles.signer.machines or [ ];
|
|
||||||
peers = dataMesherInstances.roles.peer.machines or [ ];
|
|
||||||
bootstrap = uniqueStrings (admins ++ signers);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
{ lib, config, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.clan.data-mesher;
|
|
||||||
|
|
||||||
dmLib = import ../lib.nix lib;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
../shared.nix
|
|
||||||
];
|
|
||||||
|
|
||||||
options.clan.data-mesher = {
|
|
||||||
network = {
|
|
||||||
tld = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
default = (config.networking.domain or "clan");
|
|
||||||
description = "Top level domain to use for the network";
|
|
||||||
};
|
|
||||||
|
|
||||||
hostTTL = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
default = "672h"; # 28 days
|
|
||||||
example = "24h";
|
|
||||||
description = "The TTL for hosts in the network, in the form of a Go time.Duration";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
|
|
||||||
warnings = [
|
|
||||||
"The clan.admin module is deprecated and will be removed on 2025-07-15.
|
|
||||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
|
||||||
(https://docs.clan.lol/reference/clanServices)."
|
|
||||||
];
|
|
||||||
|
|
||||||
services.data-mesher.initNetwork =
|
|
||||||
let
|
|
||||||
# for a given machine, read it's public key and remove any new lines
|
|
||||||
readHostKey =
|
|
||||||
machine:
|
|
||||||
let
|
|
||||||
path = "${config.clan.core.settings.directory}/vars/per-machine/${machine}/data-mesher-host-key/public_key/value";
|
|
||||||
in
|
|
||||||
builtins.elemAt (lib.splitString "\n" (builtins.readFile path)) 1;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
enable = true;
|
|
||||||
keyPath = config.clan.core.vars.generators.data-mesher-network-key.files.private_key.path;
|
|
||||||
|
|
||||||
tld = cfg.network.tld;
|
|
||||||
hostTTL = cfg.network.hostTTL;
|
|
||||||
|
|
||||||
# admin and signer host public keys
|
|
||||||
signingKeys = builtins.map readHostKey (dmLib.machines config).bootstrap;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
imports = [
|
|
||||||
../shared.nix
|
|
||||||
];
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
imports = [
|
|
||||||
../shared.nix
|
|
||||||
];
|
|
||||||
}
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
cfg = config.clan.data-mesher;
|
|
||||||
dmLib = import ./lib.nix lib;
|
|
||||||
|
|
||||||
# the default bootstrap nodes are any machines with the admin or signers role
|
|
||||||
# we iterate through those machines, determining an IP address for them based on their VPN
|
|
||||||
# currently only supports zerotier
|
|
||||||
defaultBootstrapNodes = builtins.foldl' (
|
|
||||||
urls: name:
|
|
||||||
let
|
|
||||||
|
|
||||||
ipPath = "${config.clan.core.settings.directory}/vars/per-machine/${name}/zerotier/zerotier-ip/value";
|
|
||||||
|
|
||||||
in
|
|
||||||
if builtins.pathExists ipPath then
|
|
||||||
let
|
|
||||||
ip = builtins.readFile ipPath;
|
|
||||||
in
|
|
||||||
urls ++ [ "[${ip}]:${builtins.toString cfg.network.port}" ]
|
|
||||||
else
|
|
||||||
urls
|
|
||||||
) [ ] (dmLib.machines config).bootstrap;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.clan.data-mesher = {
|
|
||||||
|
|
||||||
bootstrapNodes = lib.mkOption {
|
|
||||||
type = lib.types.nullOr (lib.types.listOf lib.types.str);
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
A list of bootstrap nodes that act as an initial gateway when joining
|
|
||||||
the cluster.
|
|
||||||
'';
|
|
||||||
example = [
|
|
||||||
"192.168.1.1:7946"
|
|
||||||
"192.168.1.2:7946"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
network = {
|
|
||||||
|
|
||||||
interface = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = ''
|
|
||||||
The interface over which cluster communication should be performed.
|
|
||||||
All the ip addresses associate with this interface will be part of
|
|
||||||
our host claim, including both ipv4 and ipv6.
|
|
||||||
|
|
||||||
This should be set to an internal/VPN interface.
|
|
||||||
'';
|
|
||||||
example = "tailscale0";
|
|
||||||
};
|
|
||||||
|
|
||||||
port = lib.mkOption {
|
|
||||||
type = lib.types.port;
|
|
||||||
default = 7946;
|
|
||||||
description = ''
|
|
||||||
Port to listen on for cluster communication.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
|
|
||||||
services.data-mesher = {
|
|
||||||
enable = true;
|
|
||||||
openFirewall = true;
|
|
||||||
|
|
||||||
settings = {
|
|
||||||
log_level = "warn";
|
|
||||||
state_dir = "/var/lib/data-mesher";
|
|
||||||
|
|
||||||
# read network id from vars
|
|
||||||
network.id = config.clan.core.vars.generators.data-mesher-network-key.files.public_key.value;
|
|
||||||
|
|
||||||
host = {
|
|
||||||
names = [ config.networking.hostName ];
|
|
||||||
key_path = config.clan.core.vars.generators.data-mesher-host-key.files.private_key.path;
|
|
||||||
};
|
|
||||||
|
|
||||||
cluster = {
|
|
||||||
port = cfg.network.port;
|
|
||||||
join_interval = "30s";
|
|
||||||
push_pull_interval = "30s";
|
|
||||||
|
|
||||||
interface = cfg.network.interface;
|
|
||||||
|
|
||||||
bootstrap_nodes = if cfg.bootstrapNodes == null then defaultBootstrapNodes else cfg.bootstrapNodes;
|
|
||||||
};
|
|
||||||
|
|
||||||
http.port = 7331;
|
|
||||||
http.interface = "lo";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Generate host key.
|
|
||||||
clan.core.vars.generators.data-mesher-host-key = {
|
|
||||||
files =
|
|
||||||
let
|
|
||||||
owner = config.users.users.data-mesher.name;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
private_key = {
|
|
||||||
inherit owner;
|
|
||||||
};
|
|
||||||
public_key.secret = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
runtimeInputs = [
|
|
||||||
config.services.data-mesher.package
|
|
||||||
];
|
|
||||||
|
|
||||||
script = ''
|
|
||||||
data-mesher generate keypair \
|
|
||||||
--public-key-path "$out"/public_key \
|
|
||||||
--private-key-path "$out"/private_key
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
clan.core.vars.generators.data-mesher-network-key = {
|
|
||||||
# generated once per clan
|
|
||||||
share = true;
|
|
||||||
|
|
||||||
files =
|
|
||||||
let
|
|
||||||
owner = config.users.users.data-mesher.name;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
private_key = {
|
|
||||||
inherit owner;
|
|
||||||
};
|
|
||||||
public_key.secret = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
runtimeInputs = [
|
|
||||||
config.services.data-mesher.package
|
|
||||||
];
|
|
||||||
|
|
||||||
script = ''
|
|
||||||
data-mesher generate keypair \
|
|
||||||
--public-key-path "$out"/public_key \
|
|
||||||
--private-key-path "$out"/private_key
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
description = "Email-based instant messaging for Desktop."
|
|
||||||
categories = ["Social"]
|
|
||||||
features = [ "inventory", "deprecated" ]
|
|
||||||
---
|
|
||||||
|
|
||||||
!!! info
|
|
||||||
This module will automatically configure an email server on the machine for handling the e-mail messaging seamlessly.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- [x] **Email-based**: Uses any email account as its backend.
|
|
||||||
- [x] **End-to-End Encryption**: Supports Autocrypt to automatically encrypt messages.
|
|
||||||
- [x] **No Phone Number Required**: Uses your email address instead of a phone number.
|
|
||||||
- [x] **Cross-Platform**: Available on desktop and mobile platforms.
|
|
||||||
- [x] **Automatic Server Setup**: Includes your own DeltaChat server for enhanced control and privacy.
|
|
||||||
- [ ] **Bake a cake**: This module cannot cake a bake.
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
imports = [ ./roles/default.nix ];
|
|
||||||
}
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
{
|
|
||||||
config,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
warnings = [
|
|
||||||
"The clan.deltachat module is deprecated and will be removed on 2025-07-15.
|
|
||||||
Please migrate to user-maintained configuration or the new equivalent clan services
|
|
||||||
(https://docs.clan.lol/reference/clanServices)."
|
|
||||||
];
|
|
||||||
|
|
||||||
networking.firewall.interfaces."zt+".allowedTCPPorts = [ 25 ]; # smtp with other hosts
|
|
||||||
environment.systemPackages = [ pkgs.deltachat-desktop ];
|
|
||||||
|
|
||||||
services.maddy =
|
|
||||||
let
|
|
||||||
domain = "${config.clan.core.settings.machine.name}.local";
|
|
||||||
in
|
|
||||||
{
|
|
||||||
enable = true;
|
|
||||||
primaryDomain = domain;
|
|
||||||
config = ''
|
|
||||||
# Minimal configuration with TLS disabled, adapted from upstream example
|
|
||||||
# configuration here https://github.com/foxcpp/maddy/blob/master/maddy.conf
|
|
||||||
# Do not use this in unencrypted networks!
|
|
||||||
|
|
||||||
auth.pass_table local_authdb {
|
|
||||||
table sql_table {
|
|
||||||
driver sqlite3
|
|
||||||
dsn credentials.db
|
|
||||||
table_name passwords
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
storage.imapsql local_mailboxes {
|
|
||||||
driver sqlite3
|
|
||||||
dsn imapsql.db
|
|
||||||
}
|
|
||||||
|
|
||||||
table.chain local_rewrites {
|
|
||||||
optional_step regexp "(.+)\+(.+)@(.+)" "$1@$3"
|
|
||||||
optional_step static {
|
|
||||||
entry postmaster postmaster@$(primary_domain)
|
|
||||||
}
|
|
||||||
optional_step file /etc/maddy/aliases
|
|
||||||
}
|
|
||||||
|
|
||||||
msgpipeline local_routing {
|
|
||||||
destination postmaster $(local_domains) {
|
|
||||||
modify {
|
|
||||||
replace_rcpt &local_rewrites
|
|
||||||
}
|
|
||||||
deliver_to &local_mailboxes
|
|
||||||
}
|
|
||||||
default_destination {
|
|
||||||
reject 550 5.1.1 "User doesn't exist"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
smtp tcp://[::]:25 {
|
|
||||||
limits {
|
|
||||||
all rate 20 1s
|
|
||||||
all concurrency 10
|
|
||||||
}
|
|
||||||
dmarc yes
|
|
||||||
check {
|
|
||||||
require_mx_record
|
|
||||||
dkim
|
|
||||||
spf
|
|
||||||
}
|
|
||||||
source $(local_domains) {
|
|
||||||
reject 501 5.1.8 "Use Submission for outgoing SMTP"
|
|
||||||
}
|
|
||||||
default_source {
|
|
||||||
destination postmaster $(local_domains) {
|
|
||||||
deliver_to &local_routing
|
|
||||||
}
|
|
||||||
default_destination {
|
|
||||||
reject 550 5.1.1 "User doesn't exist"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
submission tcp://[::1]:587 {
|
|
||||||
limits {
|
|
||||||
all rate 50 1s
|
|
||||||
}
|
|
||||||
auth &local_authdb
|
|
||||||
source $(local_domains) {
|
|
||||||
check {
|
|
||||||
authorize_sender {
|
|
||||||
prepare_email &local_rewrites
|
|
||||||
user_to_email identity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
destination postmaster $(local_domains) {
|
|
||||||
deliver_to &local_routing
|
|
||||||
}
|
|
||||||
default_destination {
|
|
||||||
modify {
|
|
||||||
dkim $(primary_domain) $(local_domains) default
|
|
||||||
}
|
|
||||||
deliver_to &remote_queue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default_source {
|
|
||||||
reject 501 5.1.8 "Non-local sender domain"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
target.remote outbound_delivery {
|
|
||||||
limits {
|
|
||||||
destination rate 20 1s
|
|
||||||
destination concurrency 10
|
|
||||||
}
|
|
||||||
mx_auth {
|
|
||||||
dane
|
|
||||||
mtasts {
|
|
||||||
cache fs
|
|
||||||
fs_dir mtasts_cache/
|
|
||||||
}
|
|
||||||
local_policy {
|
|
||||||
min_tls_level encrypted
|
|
||||||
min_mx_level none
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
target.queue remote_queue {
|
|
||||||
target &outbound_delivery
|
|
||||||
autogenerated_msg_domain $(primary_domain)
|
|
||||||
bounce {
|
|
||||||
destination postmaster $(local_domains) {
|
|
||||||
deliver_to &local_routing
|
|
||||||
}
|
|
||||||
default_destination {
|
|
||||||
reject 550 5.0.0 "Refusing to send DSNs to non-local addresses"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
imap tcp://[::1]:143 {
|
|
||||||
auth &local_authdb
|
|
||||||
storage &local_mailboxes
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
ensureAccounts = [ "user@${domain}" ];
|
|
||||||
ensureCredentials = {
|
|
||||||
"user@${domain}".passwordFile = pkgs.writeText "dummy" "foobar";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
description = "Generates a uuid for use in disk device naming"
|
|
||||||
features = [ "inventory" ]
|
|
||||||
categories = [ "System" ]
|
|
||||||
---
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
# Dont import this file
|
|
||||||
# It is only here for backwards compatibility.
|
|
||||||
# Dont author new modules with this file.
|
|
||||||
{
|
|
||||||
imports = [ ./roles/default.nix ];
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
{
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
config = {
|
|
||||||
|
|
||||||
warnings = [
|
|
||||||
''
|
|
||||||
The clan.disk-id module is deprecated and will be removed on 2025-07-15.
|
|
||||||
For migration see: https://docs.clan.lol/guides/migrations/disk-id/
|
|
||||||
|
|
||||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
||||||
!!! Please migrate. Otherwise you may not be able to boot your system after that date. !!!
|
|
||||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
||||||
''
|
|
||||||
];
|
|
||||||
clan.core.vars.generators.disk-id = {
|
|
||||||
files.diskId.secret = false;
|
|
||||||
runtimeInputs = [
|
|
||||||
pkgs.coreutils
|
|
||||||
pkgs.bash
|
|
||||||
];
|
|
||||||
script = ''
|
|
||||||
uuid=$(bash ${./uuid4.sh})
|
|
||||||
|
|
||||||
# Remove the hyphens from the UUID
|
|
||||||
uuid_no_hyphens=$(echo -n "$uuid" | tr -d '-')
|
|
||||||
|
|
||||||
echo -n "$uuid_no_hyphens" > "$out/diskId"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Read 16 bytes from /dev/urandom
|
|
||||||
uuid=$(dd if=/dev/urandom bs=1 count=16 2>/dev/null | od -An -tx1 | tr -d ' \n')
|
|
||||||
|
|
||||||
# Break the UUID into pieces and apply the required modifications
|
|
||||||
byte6=${uuid:12:2}
|
|
||||||
byte8=${uuid:16:2}
|
|
||||||
|
|
||||||
# Construct the correct version and variant
|
|
||||||
hex_byte6=$(printf "%x" $((0x$byte6 & 0x0F | 0x40)))
|
|
||||||
hex_byte8=$(printf "%x" $((0x$byte8 & 0x3F | 0x80)))
|
|
||||||
|
|
||||||
# Rebuild the UUID with the correct fields
|
|
||||||
uuid_v4="${uuid:0:12}${hex_byte6}${uuid:14:2}${hex_byte8}${uuid:18:14}"
|
|
||||||
|
|
||||||
# Format the UUID correctly 8-4-4-4-12
|
|
||||||
uuid_formatted="${uuid_v4:0:8}-${uuid_v4:8:4}-${uuid_v4:12:4}-${uuid_v4:16:4}-${uuid_v4:20:12}"
|
|
||||||
|
|
||||||
echo -n "$uuid_formatted"
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
description = "A dynamic DNS service to update domain IPs"
|
|
||||||
---
|
|
||||||
|
|
||||||
To understand the possible options that can be set visit the documentation of [ddns-updater](https://github.com/qdm12/ddns-updater?tab=readme-ov-file#versioned-documentation)
|
|
||||||
|
|
||||||
@@ -1,257 +0,0 @@
|
|||||||
{
|
|
||||||
config,
|
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
|
|
||||||
let
|
|
||||||
name = "dyndns";
|
|
||||||
cfg = config.clan.${name};
|
|
||||||
|
|
||||||
# We dedup secrets if they have the same provider + base domain
|
|
||||||
secret_id = opt: "${name}-${opt.provider}-${opt.domain}";
|
|
||||||
secret_path =
|
|
||||||
opt: config.clan.core.vars.generators."${secret_id opt}".files."${secret_id opt}".path;
|
|
||||||
|
|
||||||
# We check that a secret has not been set in extraSettings.
|
|
||||||
extraSettingsSafe =
|
|
||||||
opt:
|
|
||||||
if (builtins.hasAttr opt.secret_field_name opt.extraSettings) then
|
|
||||||
throw "Please do not set ${opt.secret_field_name} in extraSettings, it is automatically set by the dyndns module."
|
|
||||||
else
|
|
||||||
opt.extraSettings;
|
|
||||||
/*
|
|
||||||
We go from:
|
|
||||||
{home.example.com:{value:{domain:example.com,host:home, provider:namecheap}}}
|
|
||||||
To:
|
|
||||||
{settings: [{domain: example.com, host: home, provider: namecheap, password: dyndns-namecheap-example.com}]}
|
|
||||||
*/
|
|
||||||
service_config = {
|
|
||||||
settings = builtins.catAttrs "value" (
|
|
||||||
builtins.attrValues (
|
|
||||||
lib.mapAttrs (_: opt: {
|
|
||||||
value =
|
|
||||||
(extraSettingsSafe opt)
|
|
||||||
// {
|
|
||||||
domain = opt.domain;
|
|
||||||
provider = opt.provider;
|
|
||||||
}
|
|
||||||
// {
|
|
||||||
"${opt.secret_field_name}" = secret_id opt;
|
|
||||||
};
|
|
||||||
}) cfg.settings
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
secret_generator = _: opt: {
|
|
||||||
name = secret_id opt;
|
|
||||||
value = {
|
|
||||||
share = true;
|
|
||||||
migrateFact = "${secret_id opt}";
|
|
||||||
prompts.${secret_id opt} = {
|
|
||||||
type = "hidden";
|
|
||||||
persist = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.clan.${name} = {
|
|
||||||
server = {
|
|
||||||
enable = lib.mkEnableOption "dyndns webserver";
|
|
||||||
domain = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "Domain to serve the webservice on";
|
|
||||||
};
|
|
||||||
port = lib.mkOption {
|
|
||||||
type = lib.types.int;
|
|
||||||
default = 54805;
|
|
||||||
description = "Port to listen on";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
period = lib.mkOption {
|
|
||||||
type = lib.types.int;
|
|
||||||
default = 5;
|
|
||||||
description = "Domain update period in minutes";
|
|
||||||
};
|
|
||||||
|
|
||||||
settings = lib.mkOption {
|
|
||||||
type = lib.types.attrsOf (
|
|
||||||
lib.types.submodule (
|
|
||||||
{ ... }:
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
provider = lib.mkOption {
|
|
||||||
example = "namecheap";
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "The dyndns provider to use";
|
|
||||||
};
|
|
||||||
domain = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "example.com";
|
|
||||||
description = "The top level domain to update.";
|
|
||||||
};
|
|
||||||
secret_field_name = lib.mkOption {
|
|
||||||
example = [
|
|
||||||
"password"
|
|
||||||
"api_key"
|
|
||||||
];
|
|
||||||
type = lib.types.enum [
|
|
||||||
"password"
|
|
||||||
"token"
|
|
||||||
"api_key"
|
|
||||||
"secret_api_key"
|
|
||||||
];
|
|
||||||
default = "password";
|
|
||||||
description = "The field name for the secret";
|
|
||||||
};
|
|
||||||
# TODO: Ideally we would create a gigantic list of all possible settings / types
|
|
||||||
# optimally we would have a way to generate the options from the source code
|
|
||||||
extraSettings = lib.mkOption {
|
|
||||||
type = lib.types.attrsOf lib.types.str;
|
|
||||||
default = { };
|
|
||||||
description = ''
|
|
||||||
Extra settings for the provider.
|
|
||||||
Provider specific settings: https://github.com/qdm12/ddns-updater#configuration
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
default = [ ];
|
|
||||||
description = "Configuration for which domains to update";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
imports = [
|
|
||||||
../nginx
|
|
||||||
];
|
|
||||||
|
|
||||||
config = lib.mkMerge [
|
|
||||||
(lib.mkIf (cfg.settings != { }) {
|
|
||||||
clan.core.vars.generators = lib.mapAttrs' secret_generator cfg.settings;
|
|
||||||
|
|
||||||
users.groups.${name} = { };
|
|
||||||
users.users.${name} = {
|
|
||||||
group = name;
|
|
||||||
isSystemUser = true;
|
|
||||||
description = "User for ${name} service";
|
|
||||||
home = "/var/lib/${name}";
|
|
||||||
createHome = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
services.nginx = lib.mkIf cfg.server.enable {
|
|
||||||
enable = true;
|
|
||||||
virtualHosts = {
|
|
||||||
"${cfg.server.domain}" = {
|
|
||||||
forceSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
locations."/" = {
|
|
||||||
proxyPass = "http://localhost:${toString cfg.server.port}";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.${name} = {
|
|
||||||
path = [ ];
|
|
||||||
description = "Dynamic DNS updater";
|
|
||||||
after = [ "network.target" ];
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
environment = {
|
|
||||||
MYCONFIG = "${builtins.toJSON service_config}";
|
|
||||||
SERVER_ENABLED = if cfg.server.enable then "yes" else "no";
|
|
||||||
PERIOD = "${toString cfg.period}m";
|
|
||||||
LISTENING_ADDRESS = ":${toString cfg.server.port}";
|
|
||||||
};
|
|
||||||
|
|
||||||
serviceConfig =
|
|
||||||
let
|
|
||||||
pyscript =
|
|
||||||
pkgs.writers.writePython3Bin "generate_secret_config.py"
|
|
||||||
{
|
|
||||||
libraries = [ ];
|
|
||||||
doCheck = false;
|
|
||||||
}
|
|
||||||
''
|
|
||||||
import json
|
|
||||||
from pathlib import Path
|
|
||||||
import os
|
|
||||||
|
|
||||||
cred_dir = Path(os.getenv("CREDENTIALS_DIRECTORY"))
|
|
||||||
config_str = os.getenv("MYCONFIG")
|
|
||||||
|
|
||||||
|
|
||||||
def get_credential(name):
|
|
||||||
secret_p = cred_dir / name
|
|
||||||
with open(secret_p, 'r') as f:
|
|
||||||
return f.read().strip()
|
|
||||||
|
|
||||||
|
|
||||||
config = json.loads(config_str)
|
|
||||||
print(f"Config: {config}")
|
|
||||||
for attrset in config["settings"]:
|
|
||||||
if "password" in attrset:
|
|
||||||
attrset['password'] = get_credential(attrset['password'])
|
|
||||||
elif "token" in attrset:
|
|
||||||
attrset['token'] = get_credential(attrset['token'])
|
|
||||||
elif "secret_api_key" in attrset:
|
|
||||||
attrset['secret_api_key'] = get_credential(attrset['secret_api_key'])
|
|
||||||
elif "api_key" in attrset:
|
|
||||||
attrset['api_key'] = get_credential(attrset['api_key'])
|
|
||||||
else:
|
|
||||||
raise ValueError(f"Missing secret field in {attrset}")
|
|
||||||
|
|
||||||
# create directory data if it does not exist
|
|
||||||
data_dir = Path('data')
|
|
||||||
data_dir.mkdir(mode=0o770, exist_ok=True)
|
|
||||||
|
|
||||||
# Create a temporary config file
|
|
||||||
# with appropriate permissions
|
|
||||||
tmp_config_path = data_dir / '.config.json'
|
|
||||||
tmp_config_path.touch(mode=0o660, exist_ok=False)
|
|
||||||
|
|
||||||
# Write the config with secrets back
|
|
||||||
with open(tmp_config_path, 'w') as f:
|
|
||||||
f.write(json.dumps(config, indent=4))
|
|
||||||
|
|
||||||
# Move config into place
|
|
||||||
config_path = data_dir / 'config.json'
|
|
||||||
tmp_config_path.rename(config_path)
|
|
||||||
|
|
||||||
# Set file permissions to read
|
|
||||||
# and write only by the user and group
|
|
||||||
for file in data_dir.iterdir():
|
|
||||||
file.chmod(0o660)
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
{
|
|
||||||
ExecStartPre = lib.getExe pyscript;
|
|
||||||
ExecStart = lib.getExe pkgs.ddns-updater;
|
|
||||||
LoadCredential = lib.mapAttrsToList (_: opt: "${secret_id opt}:${secret_path opt}") cfg.settings;
|
|
||||||
User = name;
|
|
||||||
Group = name;
|
|
||||||
NoNewPrivileges = true;
|
|
||||||
PrivateTmp = true;
|
|
||||||
ProtectSystem = "strict";
|
|
||||||
ReadOnlyPaths = "/";
|
|
||||||
PrivateDevices = "yes";
|
|
||||||
ProtectKernelModules = "yes";
|
|
||||||
ProtectKernelTunables = "yes";
|
|
||||||
WorkingDirectory = "/var/lib/${name}";
|
|
||||||
ReadWritePaths = [
|
|
||||||
"/proc/self"
|
|
||||||
"/var/lib/${name}"
|
|
||||||
];
|
|
||||||
|
|
||||||
Restart = "always";
|
|
||||||
RestartSec = 60;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
})
|
|
||||||
];
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
description = "A modern IRC server"
|
|
||||||
categories = ["Social"]
|
|
||||||
features = [ "inventory", "deprecated" ]
|
|
||||||
---
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
imports = [ ./roles/default.nix ];
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user