Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ca158ebf8 | ||
|
|
85f75812fc | ||
|
|
78822deb2d | ||
|
|
73ffd2057e | ||
|
|
ff653cdd86 | ||
|
|
62c1a63208 | ||
|
|
fedf0be4d4 | ||
|
|
e57f73bcfe | ||
|
|
0fffc56ca7 | ||
|
|
9381f54f66 | ||
|
|
c765ea319b | ||
|
|
2318eb48ed | ||
|
|
c2c9428805 | ||
|
|
54d5290d0a |
@@ -3,4 +3,3 @@
|
|||||||
result
|
result
|
||||||
result-*
|
result-*
|
||||||
run-vm-*
|
run-vm-*
|
||||||
.nixos-test-history
|
|
||||||
|
|||||||
Generated
+36
-193
@@ -53,69 +53,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"clan-community": {
|
|
||||||
"inputs": {
|
|
||||||
"clan-core": [
|
|
||||||
"clan-core"
|
|
||||||
],
|
|
||||||
"data-mesher": [
|
|
||||||
"clan-community",
|
|
||||||
"clan-core",
|
|
||||||
"data-mesher"
|
|
||||||
],
|
|
||||||
"disko": [
|
|
||||||
"clan-community",
|
|
||||||
"clan-core",
|
|
||||||
"disko"
|
|
||||||
],
|
|
||||||
"flake-parts": [
|
|
||||||
"flake-parts"
|
|
||||||
],
|
|
||||||
"nix-darwin": [
|
|
||||||
"clan-community",
|
|
||||||
"clan-core",
|
|
||||||
"nix-darwin"
|
|
||||||
],
|
|
||||||
"nix-github-actions": "nix-github-actions",
|
|
||||||
"nix-select": [
|
|
||||||
"clan-community",
|
|
||||||
"clan-core",
|
|
||||||
"nix-select"
|
|
||||||
],
|
|
||||||
"nix-unit": "nix-unit",
|
|
||||||
"nixpkgs": [
|
|
||||||
"clan-community",
|
|
||||||
"clan-core",
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"sops-nix": [
|
|
||||||
"clan-community",
|
|
||||||
"clan-core",
|
|
||||||
"sops-nix"
|
|
||||||
],
|
|
||||||
"systems": [
|
|
||||||
"clan-community",
|
|
||||||
"clan-core",
|
|
||||||
"systems"
|
|
||||||
],
|
|
||||||
"treefmt-nix": [
|
|
||||||
"treefmt-nix"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1784417092,
|
|
||||||
"narHash": "sha256-no5eDYWPUZZu4GtLufnA7b7CiZC4qAhCgf70gFiDLZk=",
|
|
||||||
"ref": "refs/heads/main",
|
|
||||||
"rev": "20371843d45f61217019e489d6857842dc8a0203",
|
|
||||||
"revCount": 73,
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://git.clan.lol/clan/clan-community"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://git.clan.lol/clan/clan-community"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"clan-core": {
|
"clan-core": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"data-mesher": "data-mesher",
|
"data-mesher": "data-mesher",
|
||||||
@@ -135,11 +72,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1788346295,
|
"lastModified": 1772411144,
|
||||||
"narHash": "sha256-k9Ol++FFhQuPya6ZNQwVhqZZkMvE1ytLtbdrbH5zkrI=",
|
"narHash": "sha256-WhXudztwPNnKXaqGX4DOqNfHzHdBSiGCvKGHM20pscw=",
|
||||||
"ref": "refs/heads/main",
|
"ref": "refs/heads/main",
|
||||||
"rev": "b15797faeca7494a7fe5fde2691d93affa3d3e37",
|
"rev": "92cc85bc24eb31ce5725e1e72753129810ce3fe9",
|
||||||
"revCount": 15239,
|
"revCount": 13201,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.clan.lol/clan/clan-core"
|
"url": "https://git.clan.lol/clan/clan-core"
|
||||||
},
|
},
|
||||||
@@ -164,11 +101,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1788308203,
|
"lastModified": 1772273147,
|
||||||
"narHash": "sha256-dCOb9YIJv9I2udjqukvjlGiTyOvGnO7zVbot0PxjBGk=",
|
"narHash": "sha256-Wzhoc6ifjTDZi8aVRH3fuLJPdd4ouNTTwwVhgoMcMek=",
|
||||||
"rev": "644c49bbf121b3e256bc83ce10b5d97813d9181d",
|
"rev": "d5de7a8d9e5726e678c94e62fe8ac3a809fee5da",
|
||||||
"type": "tarball",
|
"type": "tarball",
|
||||||
"url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/644c49bbf121b3e256bc83ce10b5d97813d9181d.tar.gz"
|
"url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/d5de7a8d9e5726e678c94e62fe8ac3a809fee5da.tar.gz"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"type": "tarball",
|
"type": "tarball",
|
||||||
@@ -203,11 +140,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1781152676,
|
"lastModified": 1771881364,
|
||||||
"narHash": "sha256-RxWs5ND31KzTG7wvMM+PMfUjyNpmIEr999lqNARaM5o=",
|
"narHash": "sha256-A5uE/hMium5of/QGC6JwF5TGoDAfpNtW00T0s9u/PN8=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "disko",
|
"repo": "disko",
|
||||||
"rev": "ff8702b4de27f72b4c78573dfb89ec74e36abdf1",
|
"rev": "a4cb7bf73f264d40560ba527f9280469f1f081c6",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -357,11 +294,11 @@
|
|||||||
"std": "std"
|
"std": "std"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787631179,
|
"lastModified": 1779175997,
|
||||||
"narHash": "sha256-l+Zt5XNTD0f0ChkzDJURfQlFAgANIMrGYrk51SZEJKU=",
|
"narHash": "sha256-Ps/4s3jwaZdLVEpO+1cRs54VbPbgMeXJUqa4CWSPJSY=",
|
||||||
"owner": "kurogeek",
|
"owner": "kurogeek",
|
||||||
"repo": "frappix",
|
"repo": "frappix",
|
||||||
"rev": "fcb797886ae753b26a6e4415a7f2c19ba6adcf3b",
|
"rev": "0f1b4bcfb8c3b976e808a57e491d10857a1a45ac",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -416,11 +353,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1788355065,
|
"lastModified": 1768068402,
|
||||||
"narHash": "sha256-gAz5oTI7ur34CfuakQduiQd/2ZhO62Bn2XXg08nZYYQ=",
|
"narHash": "sha256-bAXnnJZKJiF7Xr6eNW6+PhBf1lg2P1aFUO9+xgWkXfA=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "d9d750e4fc11c10cab2da677bdd31e427f3a3a71",
|
"rev": "8bc5473b6bc2b6e1529a9c4040411e1199c43b4c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -547,11 +484,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1786845137,
|
"lastModified": 1772379624,
|
||||||
"narHash": "sha256-oQFip+v0luP8NIxJzmiW4Wu8bILsbFWom5l0zonl8hQ=",
|
"narHash": "sha256-NG9LLTWlz4YiaTAiRGChbrzbVxBfX+Auq4Ab/SWmk4A=",
|
||||||
"owner": "nix-darwin",
|
"owner": "nix-darwin",
|
||||||
"repo": "nix-darwin",
|
"repo": "nix-darwin",
|
||||||
"rev": "4cff07de74b50e64bdd68cd4e722ab5b6b35ee48",
|
"rev": "52d061516108769656a8bd9c6e811c677ec5b462",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -560,49 +497,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nix-github-actions": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"clan-community",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1737420293,
|
|
||||||
"narHash": "sha256-F1G5ifvqTpJq7fdkT34e/Jy9VCyzd5XfJ9TO8fHhJWE=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "nix-github-actions",
|
|
||||||
"rev": "f4158fa080ef4503c8f4c820967d946c2af31ec9",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "nix-github-actions",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nix-github-actions_2": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"clan-community",
|
|
||||||
"nix-unit",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1737420293,
|
|
||||||
"narHash": "sha256-F1G5ifvqTpJq7fdkT34e/Jy9VCyzd5XfJ9TO8fHhJWE=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "nix-github-actions",
|
|
||||||
"rev": "f4158fa080ef4503c8f4c820967d946c2af31ec9",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "nix-github-actions",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nix-select": {
|
"nix-select": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1763303120,
|
"lastModified": 1763303120,
|
||||||
@@ -616,32 +510,6 @@
|
|||||||
"url": "https://git.clan.lol/clan/nix-select/archive/main.tar.gz"
|
"url": "https://git.clan.lol/clan/nix-select/archive/main.tar.gz"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nix-unit": {
|
|
||||||
"inputs": {
|
|
||||||
"nix-github-actions": "nix-github-actions_2",
|
|
||||||
"nixpkgs": [
|
|
||||||
"clan-community",
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"treefmt-nix": [
|
|
||||||
"clan-community",
|
|
||||||
"treefmt-nix"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1779338171,
|
|
||||||
"narHash": "sha256-affUbv/bwE8SLGhuWKniDr7SVO+Lo1XEPjCZdyU5kgQ=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "nix-unit",
|
|
||||||
"rev": "6ab1f232562a01d18b40d5ed6a58718c4f3a74bc",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "nix-unit",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixago": {
|
"nixago": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils_3",
|
"flake-utils": "flake-utils_3",
|
||||||
@@ -654,11 +522,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1746801636,
|
"lastModified": 1714086354,
|
||||||
"narHash": "sha256-dlcKfIXp/eqFHzFm+DzseXAWWlpVwyk9cTvCKGtVKkw=",
|
"narHash": "sha256-yKVQMxL9p7zCWUhnGhDzRVT8sDgHoI3V595lBK0C2YA=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nixago",
|
"repo": "nixago",
|
||||||
"rev": "8cc33f973ab3a891d8a41391e73ef451a783960b",
|
"rev": "5133633e9fe6b144c8e00e3b212cdbd5a173b63d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -667,29 +535,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixos-images": {
|
|
||||||
"inputs": {
|
|
||||||
"nixos-stable": [
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"nixos-unstable": [
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1784802994,
|
|
||||||
"narHash": "sha256-4PcD0Ibzdkh85G+70w5dLlR9YgQ2bmNIjiPPMSzO57w=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "nixos-images",
|
|
||||||
"rev": "6ece16b0c97986fe085122e796044add4cc3ff64",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "nixos-images",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1712920918,
|
"lastModified": 1712920918,
|
||||||
@@ -707,11 +552,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1788775869,
|
"lastModified": 1778458615,
|
||||||
"narHash": "sha256-JRf1lSypbvKj6AzUZHpUA6YC1DirVuitZWOnRwcm+Ww=",
|
"narHash": "sha256-cY07EsdhBJ8tFXPzDYevgqxRev9ZLxFonuq9wmq5kwg=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "58973d74f1893afe13f5902919803a0e99da0ca4",
|
"rev": "c6e5ca3c836a5f4dd9af9f2c1fc1c38f0fac988a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -752,15 +597,16 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787579760,
|
"lastModified": 1708640854,
|
||||||
"narHash": "sha256-WGhIEINOICx7bhV3WrSz0QTzv8uzaaa9AXvD1clIWpc=",
|
"narHash": "sha256-EpcAmvIS4ErqhXtVEfd2GPpU/E/s8CCRSfYzk6FZ/fY=",
|
||||||
"owner": "paisano-nix",
|
"owner": "paisano-nix",
|
||||||
"repo": "core",
|
"repo": "core",
|
||||||
"rev": "88739c84d308876714322eb9844ede6597c1a580",
|
"rev": "adcf742bc9463c08764ca9e6955bd5e7dcf3a3fe",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "paisano-nix",
|
"owner": "paisano-nix",
|
||||||
|
"ref": "0.2.0",
|
||||||
"repo": "core",
|
"repo": "core",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@@ -807,7 +653,6 @@
|
|||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"clan-community": "clan-community",
|
|
||||||
"clan-core": "clan-core",
|
"clan-core": "clan-core",
|
||||||
"devshell": "devshell",
|
"devshell": "devshell",
|
||||||
"flake-parts": "flake-parts",
|
"flake-parts": "flake-parts",
|
||||||
@@ -815,7 +660,6 @@
|
|||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
"import-tree": "import-tree",
|
"import-tree": "import-tree",
|
||||||
"liminix": "liminix",
|
"liminix": "liminix",
|
||||||
"nixos-images": "nixos-images",
|
|
||||||
"nixpkgs": "nixpkgs_2",
|
"nixpkgs": "nixpkgs_2",
|
||||||
"plasma-manager": "plasma-manager",
|
"plasma-manager": "plasma-manager",
|
||||||
"treefmt-nix": "treefmt-nix"
|
"treefmt-nix": "treefmt-nix"
|
||||||
@@ -829,11 +673,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1788337237,
|
"lastModified": 1772340640,
|
||||||
"narHash": "sha256-gkSH8VUtCo6hnysNmb9DbTuDepH2t5pv+QWjP75xKAk=",
|
"narHash": "sha256-1nq7+Kt5IUBD8Hu3nptVPbMf+22rNJoHT0t9L1X+GKA=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "fbf759290e0cb0a98dfc813a4eb7d53ad1dacb57",
|
"rev": "dec4d8eac700dcd2fe3c020857d3ee220ec147f1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -920,16 +764,15 @@
|
|||||||
},
|
},
|
||||||
"systems": {
|
"systems": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1774449309,
|
"lastModified": 1681028828,
|
||||||
"narHash": "sha256-brhZ8DmuGtzkCYHJg4HEd602amKm89Y9ytsFZ5uWD1w=",
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
"owner": "nix-systems",
|
"owner": "nix-systems",
|
||||||
"repo": "default",
|
"repo": "default",
|
||||||
"rev": "c29398b59d2048c4ab79345812849c9bd15e9150",
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nix-systems",
|
"owner": "nix-systems",
|
||||||
"ref": "future-26.11",
|
|
||||||
"repo": "default",
|
"repo": "default",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,6 @@
|
|||||||
inputs.treefmt-nix.follows = "treefmt-nix";
|
inputs.treefmt-nix.follows = "treefmt-nix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
clan-community = {
|
|
||||||
url = "git+https://git.clan.lol/clan/clan-community";
|
|
||||||
inputs.clan-core.follows = "clan-core";
|
|
||||||
inputs.flake-parts.follows = "flake-parts";
|
|
||||||
inputs.treefmt-nix.follows = "treefmt-nix";
|
|
||||||
};
|
|
||||||
devshell = {
|
devshell = {
|
||||||
url = "github:numtide/devshell";
|
url = "github:numtide/devshell";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
@@ -45,12 +39,6 @@
|
|||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
inputs.devshell.follows = "devshell";
|
inputs.devshell.follows = "devshell";
|
||||||
};
|
};
|
||||||
|
|
||||||
nixos-images = {
|
|
||||||
url = "github:nix-community/nixos-images";
|
|
||||||
inputs.nixos-unstable.follows = "nixpkgs";
|
|
||||||
inputs.nixos-stable.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
outputs =
|
outputs =
|
||||||
{
|
{
|
||||||
@@ -68,6 +56,7 @@
|
|||||||
./shell.nix
|
./shell.nix
|
||||||
|
|
||||||
./overlays
|
./overlays
|
||||||
|
./modules/nixos
|
||||||
./machines
|
./machines
|
||||||
./routers
|
./routers
|
||||||
./inventories
|
./inventories
|
||||||
@@ -83,26 +72,18 @@
|
|||||||
inherit system;
|
inherit system;
|
||||||
overlays = [
|
overlays = [
|
||||||
inputs.self.overlays.packagesOverlay
|
inputs.self.overlays.packagesOverlay
|
||||||
|
inputs.self.overlays.frappixFrappeOverlay
|
||||||
|
inputs.self.overlays.frappixLibsOverlay
|
||||||
|
inputs.self.overlays.frappixPythonOverlay
|
||||||
|
inputs.self.overlays.frappixToolsOverlay
|
||||||
];
|
];
|
||||||
config = { };
|
config = { };
|
||||||
};
|
};
|
||||||
packages.think = pkgs.think-gtcm;
|
packages.think = pkgs.think-gtcm;
|
||||||
packages.think-be = pkgs.think-backend-gtcm;
|
packages.think-be = pkgs.think-backend-gtcm;
|
||||||
packages.file-uploader = pkgs.gtcm-file-uploader;
|
packages.file-uploader = pkgs.gtcm-file-uploader;
|
||||||
packages.installer =
|
packages.erpnext_thailand = pkgs.erpnext_thailand;
|
||||||
(pkgs.nixos [
|
packages.thai_payroll = pkgs.thai_payroll;
|
||||||
inputs.nixos-images.nixosModules.image-installer
|
|
||||||
{
|
|
||||||
users.users.root.openssh.authorizedKeys.keys = [
|
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAB/raxJR8gASmquP63weHelbi+da2WBJR1DgzHPNz/f"
|
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDuhpzDHBPvn8nv8RH1MRomDOaXyP4GziQm7r3MZ1Syk"
|
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcZ/p1Ofa9liwIzPWzNtONhJ7+FUWd2lCz33r81t8+w kurogeek@kurogeek"
|
|
||||||
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIOJDRQfb1+7VK5tOe8W40iryfBWYRO6Uf1r2viDjmsJtAAAABHNzaDo="
|
|
||||||
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIDgsWq+G/tcr6eUQYT7+sJeBtRmOMabgFiIgIV44XNc6AAAABHNzaDo="
|
|
||||||
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIJMi3TAuwDtIeO4MsORlBZ31HzaV5bji1fFBPcC9/tWuAAAABHNzaDo="
|
|
||||||
];
|
|
||||||
}
|
|
||||||
]).config.system.build.isoImage;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
+27
-303
@@ -19,11 +19,13 @@
|
|||||||
w = [ "sirius" ];
|
w = [ "sirius" ];
|
||||||
b4l = [
|
b4l = [
|
||||||
"rigel"
|
"rigel"
|
||||||
|
"neptune"
|
||||||
"rana"
|
"rana"
|
||||||
"petra"
|
"petra"
|
||||||
"alasia"
|
"alasia"
|
||||||
];
|
];
|
||||||
phonebox = [
|
phonebox = [
|
||||||
|
"neptune"
|
||||||
"rigel"
|
"rigel"
|
||||||
"almach"
|
"almach"
|
||||||
"alpheratz"
|
"alpheratz"
|
||||||
@@ -31,173 +33,25 @@
|
|||||||
"adhil"
|
"adhil"
|
||||||
"buna"
|
"buna"
|
||||||
];
|
];
|
||||||
|
global-network = [
|
||||||
prometheus = [
|
|
||||||
"cursa"
|
|
||||||
"rigel"
|
|
||||||
"vega"
|
|
||||||
"buna"
|
|
||||||
];
|
|
||||||
|
|
||||||
dm-bootstrapper = [
|
|
||||||
"rigel"
|
|
||||||
"cursa"
|
|
||||||
"deneb"
|
|
||||||
"bosona"
|
|
||||||
"canopus"
|
|
||||||
];
|
|
||||||
|
|
||||||
dm-pull-deploy = [
|
|
||||||
"rana"
|
"rana"
|
||||||
"rigel"
|
"sirius"
|
||||||
"vega"
|
"hadar"
|
||||||
|
"procyon"
|
||||||
|
"alasia"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
instances = {
|
instances = {
|
||||||
|
|
||||||
borgbackup = {
|
|
||||||
module = {
|
|
||||||
name = "borgbackup";
|
|
||||||
input = "clan-core";
|
|
||||||
};
|
|
||||||
roles.client.machines."cursa".settings.destinations = {
|
|
||||||
alex = {
|
|
||||||
repo = "ssh://borg@10.0.10.225:2222/backup/cursa/backup";
|
|
||||||
rsh = "ssh -i /run/secrets/vars/borgbackup/borgbackup.ssh";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
roles.client.machines."hadar".settings.destinations = {
|
|
||||||
alex = {
|
|
||||||
repo = "ssh://borg@10.0.10.225:2222/backup/hadar/backup";
|
|
||||||
rsh = "ssh -i /run/secrets/vars/borgbackup/borgbackup.ssh";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
roles.client.machines."procyon".settings.destinations = {
|
|
||||||
alex = {
|
|
||||||
repo = "ssh://borg@10.0.10.225:2222/backup/procyon/backup";
|
|
||||||
rsh = "ssh -i /run/secrets/vars/borgbackup/borgbackup.ssh";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
roles.client.machines."bosona".settings.destinations = {
|
|
||||||
alex = {
|
|
||||||
repo = "ssh://borg@10.0.10.225:2222/backup/bosona/backup";
|
|
||||||
rsh = "ssh -i /run/secrets/vars/borgbackup/borgbackup.ssh";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
roles.client.machines."canopus".settings.destinations = {
|
|
||||||
alex = {
|
|
||||||
repo = "ssh://borg@10.0.10.225:2222/backup/canopus/backup";
|
|
||||||
rsh = "ssh -i /run/secrets/vars/borgbackup/borgbackup.ssh";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
roles.client.machines."deneb".settings.destinations = {
|
|
||||||
alex = {
|
|
||||||
repo = "ssh://borg@10.0.10.225:2222/backup/deneb/backup";
|
|
||||||
rsh = "ssh -i /run/secrets/vars/borgbackup/borgbackup.ssh";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
roles.client.machines."alasia".settings.destinations = {
|
|
||||||
alex = {
|
|
||||||
repo = "ssh://borg@10.0.10.225:2222/backup/alasia/backup";
|
|
||||||
rsh = "ssh -i /run/secrets/vars/borgbackup/borgbackup.ssh";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
data-mesher = {
|
|
||||||
module = {
|
|
||||||
name = "data-mesher";
|
|
||||||
input = "clan-core";
|
|
||||||
};
|
|
||||||
roles.bootstrap.tags = [ "dm-bootstrapper" ];
|
|
||||||
roles.default.tags = [ "all" ];
|
|
||||||
roles.default.settings.interfaces = [ "ygg" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
auto-pull-update = {
|
|
||||||
module = {
|
|
||||||
name = "dm-pull-deploy";
|
|
||||||
input = "clan-community";
|
|
||||||
};
|
|
||||||
roles.push.machines."rigel".settings = {
|
|
||||||
gitUrl = "https://git.b4l.co.th/newedge/infra.git";
|
|
||||||
branch = "main";
|
|
||||||
};
|
|
||||||
roles.push.extraModules = [
|
|
||||||
(
|
|
||||||
{ pkgs, config, ... }:
|
|
||||||
{
|
|
||||||
# work around until upstream is fixed
|
|
||||||
environment.systemPackages = [
|
|
||||||
(pkgs.writeShellApplication {
|
|
||||||
name = "custom-dm-send-deploy";
|
|
||||||
runtimeInputs = [
|
|
||||||
config.services.data-mesher.package
|
|
||||||
pkgs.git
|
|
||||||
pkgs.nix
|
|
||||||
pkgs.jq
|
|
||||||
];
|
|
||||||
text =
|
|
||||||
let
|
|
||||||
settings.gitUrl = "https://git.b4l.co.th/newedge/infra.git";
|
|
||||||
settings.branch = "main";
|
|
||||||
in
|
|
||||||
''
|
|
||||||
if [ $# -gt 1 ]; then
|
|
||||||
echo "Usage: dm-send-deploy [<flake-ref>]"
|
|
||||||
echo "Without arguments, sends the latest commit on '${settings.branch}' from ${settings.gitUrl}"
|
|
||||||
echo "Example: dm-send-deploy git+https://example.com/repo.git?rev=abc123..."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
KEY="${config.clan.core.vars.generators.dm-pull-deploy-signing-key.files."signing.key".path}"
|
|
||||||
if [ ! -r "$KEY" ]; then
|
|
||||||
echo "Error: cannot read signing key at $KEY (are you root?)"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $# -eq 1 ]; then
|
|
||||||
FLAKE_REF="$1"
|
|
||||||
else
|
|
||||||
REV=$(git ls-remote "${settings.gitUrl}" "refs/heads/${settings.branch}" | cut -f1)
|
|
||||||
if [ -z "$REV" ]; then
|
|
||||||
echo "Error: could not determine latest commit on ${settings.branch} from ${settings.gitUrl}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
FLAKE_REF="git+${settings.gitUrl}?rev=$REV"
|
|
||||||
fi
|
|
||||||
|
|
||||||
TMPFILE=$(mktemp)
|
|
||||||
trap 'rm -f "$TMPFILE"' EXIT
|
|
||||||
|
|
||||||
printf '%s' "$FLAKE_REF" > "$TMPFILE"
|
|
||||||
|
|
||||||
NETWORK_ID="${config.clan.core.vars.generators.data-mesher-network.files."network.pub".path}"
|
|
||||||
|
|
||||||
data-mesher file update "$TMPFILE" \
|
|
||||||
--url http://localhost:7331 \
|
|
||||||
--network-id "$NETWORK_ID" \
|
|
||||||
--key "$KEY" \
|
|
||||||
--name "dm_pull_deploy/target"
|
|
||||||
|
|
||||||
echo "Deployment target pushed: $FLAKE_REF"
|
|
||||||
'';
|
|
||||||
})
|
|
||||||
];
|
|
||||||
}
|
|
||||||
)
|
|
||||||
];
|
|
||||||
roles.default.tags = [ "dm-pull-deploy" ];
|
|
||||||
roles.default.settings.action = "switch";
|
|
||||||
};
|
|
||||||
|
|
||||||
sshd = {
|
sshd = {
|
||||||
roles.server.tags."all" = { };
|
roles.server.tags."all" = { };
|
||||||
roles.server.settings = {
|
roles.server.settings = {
|
||||||
authorizedKeys = {
|
authorizedKeys = {
|
||||||
"berwn" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAB/raxJR8gASmquP63weHelbi+da2WBJR1DgzHPNz/f";
|
"berwn" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAB/raxJR8gASmquP63weHelbi+da2WBJR1DgzHPNz/f";
|
||||||
"davhau" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDuhpzDHBPvn8nv8RH1MRomDOaXyP4GziQm7r3MZ1Syk";
|
"davhau" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDuhpzDHBPvn8nv8RH1MRomDOaXyP4GziQm7r3MZ1Syk";
|
||||||
|
"vi" =
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAmgyEGuY/r7SDlJgrzYmQqpcWS5W+fCzRi3OS59ne4W openpgp:0xFF687387";
|
||||||
"kurogeek" =
|
"kurogeek" =
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcZ/p1Ofa9liwIzPWzNtONhJ7+FUWd2lCz33r81t8+w kurogeek@kurogeek";
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcZ/p1Ofa9liwIzPWzNtONhJ7+FUWd2lCz33r81t8+w kurogeek@kurogeek";
|
||||||
"matthewcroughan" =
|
"matthewcroughan" =
|
||||||
@@ -248,13 +102,7 @@
|
|||||||
name = "zerotier";
|
name = "zerotier";
|
||||||
input = "clan-core";
|
input = "clan-core";
|
||||||
};
|
};
|
||||||
roles.controller.machines."vega" = {
|
roles.controller.machines."vega" = { };
|
||||||
settings.allowedIds = [
|
|
||||||
"dbe44c0287" # Alex-gateway
|
|
||||||
"b0e0b84fd3" # Alex
|
|
||||||
"2bd36db8cc" # kurogeek-thinkpad
|
|
||||||
];
|
|
||||||
};
|
|
||||||
roles.peer.tags.glom = { };
|
roles.peer.tags.glom = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -263,13 +111,7 @@
|
|||||||
name = "zerotier";
|
name = "zerotier";
|
||||||
input = "clan-core";
|
input = "clan-core";
|
||||||
};
|
};
|
||||||
roles.controller.machines."rigel" = {
|
roles.controller.machines."rigel" = { };
|
||||||
settings.allowedIds = [
|
|
||||||
"dbe44c0287" # Alex-gateway
|
|
||||||
"b0e0b84fd3" # Alex
|
|
||||||
"2bd36db8cc" # kurogeek-thinkpad
|
|
||||||
];
|
|
||||||
};
|
|
||||||
roles.peer.tags.b4l = { };
|
roles.peer.tags.b4l = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -287,24 +129,12 @@
|
|||||||
roles.peer.tags."poy" = { };
|
roles.peer.tags."poy" = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
internet = {
|
|
||||||
module.name = "internet";
|
|
||||||
roles.default.machines = {
|
|
||||||
ramus.settings.host = "5.223.63.55";
|
|
||||||
tangra.settings.host = "5.223.65.50";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
yggdrasil-global-network = {
|
yggdrasil-global-network = {
|
||||||
module = {
|
module = {
|
||||||
name = "yggdrasil";
|
name = "yggdrasil";
|
||||||
input = "clan-core";
|
input = "clan-core";
|
||||||
};
|
};
|
||||||
roles.default.tags."all" = { };
|
roles.default.tags."global-network" = { };
|
||||||
roles.default.settings.extraYggdrasilIPs = [
|
|
||||||
# kurogeek's laptop
|
|
||||||
"200:c8db:ea9b:5bdc:44ed:ad87:462a:6bd0"
|
|
||||||
];
|
|
||||||
roles.default.settings.extraPeers = [
|
roles.default.settings.extraPeers = [
|
||||||
"tls://ygg.jjolly.dev:3443"
|
"tls://ygg.jjolly.dev:3443"
|
||||||
"tls://[2602:fc24:18:7a42::1]:993"
|
"tls://[2602:fc24:18:7a42::1]:993"
|
||||||
@@ -320,30 +150,29 @@
|
|||||||
};
|
};
|
||||||
roles.server.machines."alasia".settings = {
|
roles.server.machines."alasia".settings = {
|
||||||
public_url = "tailvpn.public.newedge.house";
|
public_url = "tailvpn.public.newedge.house";
|
||||||
|
base_domain = "tailnet.newedge.house";
|
||||||
advertise_routes = [ "10.0.10.0/24" ];
|
advertise_routes = [ "10.0.10.0/24" ];
|
||||||
dns = {
|
|
||||||
magic_dns = true;
|
|
||||||
base_domain = "tailvpn.newedge.house";
|
|
||||||
nameservers = [
|
nameservers = [
|
||||||
"10.0.10.82"
|
"10.0.10.82"
|
||||||
"1.1.1.1"
|
"1.1.1.1"
|
||||||
"8.8.8.8"
|
"8.8.8.8"
|
||||||
];
|
];
|
||||||
extra_records = [
|
};
|
||||||
{
|
};
|
||||||
name = "poyerp.newedge.house";
|
|
||||||
type = "A";
|
yggdrasil-phone-network = {
|
||||||
value = "10.0.10.1";
|
module = {
|
||||||
}
|
name = "yggdrasil";
|
||||||
{
|
input = "clan-core";
|
||||||
name = "glomerp.newedge.house";
|
};
|
||||||
type = "A";
|
roles.default.tags."phonebox" = { };
|
||||||
value = "10.0.10.1";
|
roles.default.settings.extraPeers = [
|
||||||
}
|
"tls://ygg.jjolly.dev:3443"
|
||||||
|
"tls://[2602:fc24:18:7a42::1]:993"
|
||||||
|
"tcp://leo.node.3dt.net:9002"
|
||||||
|
"tcp://ygg-kcmo.incognet.io:8883"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
phonebox = {
|
phonebox = {
|
||||||
module = {
|
module = {
|
||||||
@@ -354,13 +183,6 @@
|
|||||||
roles.default.machines."adhil".settings = {
|
roles.default.machines."adhil".settings = {
|
||||||
ata-ethernet-iface = "end0";
|
ata-ethernet-iface = "end0";
|
||||||
};
|
};
|
||||||
roles.default.machines."rigel".settings = {
|
|
||||||
extraClientNumbers = [
|
|
||||||
"01"
|
|
||||||
"02"
|
|
||||||
];
|
|
||||||
extraFixedIPClient = { };
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pulse-stream = {
|
pulse-stream = {
|
||||||
@@ -455,104 +277,6 @@
|
|||||||
dataDir = "/mnt/hdd/samba";
|
dataDir = "/mnt/hdd/samba";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
wordpress = {
|
|
||||||
module = {
|
|
||||||
name = "wordpress";
|
|
||||||
input = "self";
|
|
||||||
};
|
|
||||||
roles.server.machines."tangra".settings = {
|
|
||||||
tenants = [
|
|
||||||
"poyfestival.com"
|
|
||||||
];
|
|
||||||
phpfpmOptions = ''
|
|
||||||
upload_max_filesize=64M
|
|
||||||
post_max_size=128M
|
|
||||||
'';
|
|
||||||
wpExtraConfig = ''
|
|
||||||
define('WP_MEMORY_LIMIT', '256M');
|
|
||||||
define('WP_DEBUG', false);
|
|
||||||
define('WP_DEBUG_DISPLAY', false);
|
|
||||||
define('WP_DEBUG_LOG', false);
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
prometheus-monitoring = {
|
|
||||||
module = {
|
|
||||||
name = "prometheus";
|
|
||||||
input = "self";
|
|
||||||
};
|
|
||||||
roles.server.machines."cursa".settings = {
|
|
||||||
matrix-alertmanager = {
|
|
||||||
enable = true;
|
|
||||||
homeserverUrl = "https://matrix-client.matrix.org";
|
|
||||||
matrixUser = "@nixapollo:matrix.org";
|
|
||||||
matrixRooms = [
|
|
||||||
{
|
|
||||||
receivers = [
|
|
||||||
"matrix"
|
|
||||||
];
|
|
||||||
roomId = "!rqIrWqPvsXqMgYpcNZ:matrix.org";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
roles.nodes.machines = {
|
|
||||||
vega.settings = {
|
|
||||||
exporters.smartctl = { };
|
|
||||||
exporters.zfs = { };
|
|
||||||
};
|
|
||||||
rigel.settings = {
|
|
||||||
exporters.smartctl = { };
|
|
||||||
};
|
|
||||||
sirius.settings = {
|
|
||||||
exporters.smartctl = { };
|
|
||||||
exporters.zfs = { };
|
|
||||||
};
|
|
||||||
buna.settings = {
|
|
||||||
exporters.smartctl = { };
|
|
||||||
};
|
|
||||||
mirach.settings = {
|
|
||||||
exporters.smartctl = { };
|
|
||||||
};
|
|
||||||
almach.settings = {
|
|
||||||
exporters.smartctl = { };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
wifi =
|
|
||||||
let
|
|
||||||
networks = {
|
|
||||||
home = { };
|
|
||||||
glom = { };
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
module.name = "wifi";
|
|
||||||
module.input = "clan-community";
|
|
||||||
|
|
||||||
roles.default = {
|
|
||||||
machines."buna".settings = { inherit networks; };
|
|
||||||
|
|
||||||
extraModules = [
|
|
||||||
(
|
|
||||||
{ lib, ... }:
|
|
||||||
{
|
|
||||||
# profile names match the network attr names above;
|
|
||||||
# 0 = retry forever for both (defaults: 4 autoconnect attempts, 3 auth attempts)
|
|
||||||
networking.networkmanager.ensureProfiles.profiles = lib.mapAttrs (_: _: {
|
|
||||||
connection.autoconnect-retries = 0;
|
|
||||||
connection.auth-retries = 0;
|
|
||||||
}) networks;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
+3
-6
@@ -19,7 +19,7 @@
|
|||||||
"installedAt": 1765277591
|
"installedAt": 1765277591
|
||||||
},
|
},
|
||||||
"buna": {
|
"buna": {
|
||||||
"installedAt": 1787123510
|
"installedAt": 1765343708
|
||||||
},
|
},
|
||||||
"rana": {
|
"rana": {
|
||||||
"installedAt": 1773134236
|
"installedAt": 1773134236
|
||||||
@@ -48,11 +48,8 @@
|
|||||||
"bosona": {
|
"bosona": {
|
||||||
"installedAt": 1779098893
|
"installedAt": 1779098893
|
||||||
},
|
},
|
||||||
"tangra": {
|
"saturn": {
|
||||||
"installedAt": 1779958921
|
"installedAt": 1779766873
|
||||||
},
|
|
||||||
"cursa": {
|
|
||||||
"installedAt": 1782187627
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+215
-64
@@ -25,7 +25,10 @@
|
|||||||
{
|
{
|
||||||
"index": 8,
|
"index": 8,
|
||||||
"attached_to": 0,
|
"attached_to": 0,
|
||||||
"class_list": ["pci", "bridge"],
|
"class_list": [
|
||||||
|
"pci",
|
||||||
|
"bridge"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "0004",
|
"hex": "0004",
|
||||||
"name": "PCI",
|
"name": "PCI",
|
||||||
@@ -73,7 +76,10 @@
|
|||||||
{
|
{
|
||||||
"index": 9,
|
"index": 9,
|
||||||
"attached_to": 0,
|
"attached_to": 0,
|
||||||
"class_list": ["pci", "bridge"],
|
"class_list": [
|
||||||
|
"pci",
|
||||||
|
"bridge"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "0004",
|
"hex": "0004",
|
||||||
"name": "PCI",
|
"name": "PCI",
|
||||||
@@ -125,7 +131,10 @@
|
|||||||
{
|
{
|
||||||
"index": 10,
|
"index": 10,
|
||||||
"attached_to": 0,
|
"attached_to": 0,
|
||||||
"class_list": ["pci", "bridge"],
|
"class_list": [
|
||||||
|
"pci",
|
||||||
|
"bridge"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "0004",
|
"hex": "0004",
|
||||||
"name": "PCI",
|
"name": "PCI",
|
||||||
@@ -173,7 +182,10 @@
|
|||||||
{
|
{
|
||||||
"index": 11,
|
"index": 11,
|
||||||
"attached_to": 0,
|
"attached_to": 0,
|
||||||
"class_list": ["pci", "bridge"],
|
"class_list": [
|
||||||
|
"pci",
|
||||||
|
"bridge"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "0004",
|
"hex": "0004",
|
||||||
"name": "PCI",
|
"name": "PCI",
|
||||||
@@ -229,7 +241,10 @@
|
|||||||
{
|
{
|
||||||
"index": 12,
|
"index": 12,
|
||||||
"attached_to": 0,
|
"attached_to": 0,
|
||||||
"class_list": ["pci", "bridge"],
|
"class_list": [
|
||||||
|
"pci",
|
||||||
|
"bridge"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "0004",
|
"hex": "0004",
|
||||||
"name": "PCI",
|
"name": "PCI",
|
||||||
@@ -282,14 +297,21 @@
|
|||||||
},
|
},
|
||||||
"driver": "piix4_smbus",
|
"driver": "piix4_smbus",
|
||||||
"driver_module": "i2c_piix4",
|
"driver_module": "i2c_piix4",
|
||||||
"drivers": ["piix4_smbus"],
|
"drivers": [
|
||||||
"driver_modules": ["i2c_piix4"],
|
"piix4_smbus"
|
||||||
|
],
|
||||||
|
"driver_modules": [
|
||||||
|
"i2c_piix4"
|
||||||
|
],
|
||||||
"module_alias": "pci:v00008086d00007113sv00001AF4sd00001100bc06sc80i00"
|
"module_alias": "pci:v00008086d00007113sv00001AF4sd00001100bc06sc80i00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"index": 17,
|
"index": 17,
|
||||||
"attached_to": 0,
|
"attached_to": 0,
|
||||||
"class_list": ["pci", "bridge"],
|
"class_list": [
|
||||||
|
"pci",
|
||||||
|
"bridge"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "0004",
|
"hex": "0004",
|
||||||
"name": "PCI",
|
"name": "PCI",
|
||||||
@@ -339,7 +361,11 @@
|
|||||||
{
|
{
|
||||||
"index": 22,
|
"index": 22,
|
||||||
"attached_to": 15,
|
"attached_to": 15,
|
||||||
"class_list": ["cdrom", "scsi", "block_device"],
|
"class_list": [
|
||||||
|
"cdrom",
|
||||||
|
"scsi",
|
||||||
|
"block_device"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "0084",
|
"hex": "0084",
|
||||||
"name": "SCSI",
|
"name": "SCSI",
|
||||||
@@ -396,8 +422,14 @@
|
|||||||
"unix_device_name2": "/dev/sg1",
|
"unix_device_name2": "/dev/sg1",
|
||||||
"driver": "ata_piix",
|
"driver": "ata_piix",
|
||||||
"driver_module": "ata_piix",
|
"driver_module": "ata_piix",
|
||||||
"drivers": ["ata_piix", "sr"],
|
"drivers": [
|
||||||
"driver_modules": ["ata_piix", "sr_mod"]
|
"ata_piix",
|
||||||
|
"sr"
|
||||||
|
],
|
||||||
|
"driver_modules": [
|
||||||
|
"ata_piix",
|
||||||
|
"sr_mod"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"cpu": [
|
"cpu": [
|
||||||
@@ -464,7 +496,9 @@
|
|||||||
"spectre_v2_user",
|
"spectre_v2_user",
|
||||||
"its"
|
"its"
|
||||||
],
|
],
|
||||||
"power_management": [""],
|
"power_management": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"bogo": 4224,
|
"bogo": 4224,
|
||||||
"cache": 16384,
|
"cache": 16384,
|
||||||
"page_size": 4096,
|
"page_size": 4096,
|
||||||
@@ -546,7 +580,9 @@
|
|||||||
"spectre_v2_user",
|
"spectre_v2_user",
|
||||||
"its"
|
"its"
|
||||||
],
|
],
|
||||||
"power_management": [""],
|
"power_management": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"bogo": 4224,
|
"bogo": 4224,
|
||||||
"cache": 16384,
|
"cache": 16384,
|
||||||
"page_size": 4096,
|
"page_size": 4096,
|
||||||
@@ -570,7 +606,11 @@
|
|||||||
{
|
{
|
||||||
"index": 23,
|
"index": 23,
|
||||||
"attached_to": 19,
|
"attached_to": 19,
|
||||||
"class_list": ["disk", "scsi", "block_device"],
|
"class_list": [
|
||||||
|
"disk",
|
||||||
|
"scsi",
|
||||||
|
"block_device"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "0084",
|
"hex": "0084",
|
||||||
"name": "SCSI",
|
"name": "SCSI",
|
||||||
@@ -634,15 +674,24 @@
|
|||||||
],
|
],
|
||||||
"driver": "virtio_scsi",
|
"driver": "virtio_scsi",
|
||||||
"driver_module": "virtio_scsi",
|
"driver_module": "virtio_scsi",
|
||||||
"drivers": ["sd", "virtio_scsi"],
|
"drivers": [
|
||||||
"driver_modules": ["sd_mod", "virtio_scsi"]
|
"sd",
|
||||||
|
"virtio_scsi"
|
||||||
|
],
|
||||||
|
"driver_modules": [
|
||||||
|
"sd_mod",
|
||||||
|
"virtio_scsi"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"graphics_card": [
|
"graphics_card": [
|
||||||
{
|
{
|
||||||
"index": 16,
|
"index": 16,
|
||||||
"attached_to": 0,
|
"attached_to": 0,
|
||||||
"class_list": ["graphics_card", "pci"],
|
"class_list": [
|
||||||
|
"graphics_card",
|
||||||
|
"pci"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "0004",
|
"hex": "0004",
|
||||||
"name": "PCI",
|
"name": "PCI",
|
||||||
@@ -699,8 +748,12 @@
|
|||||||
},
|
},
|
||||||
"driver": "bochs-drm",
|
"driver": "bochs-drm",
|
||||||
"driver_module": "bochs",
|
"driver_module": "bochs",
|
||||||
"drivers": ["bochs-drm"],
|
"drivers": [
|
||||||
"driver_modules": ["bochs"],
|
"bochs-drm"
|
||||||
|
],
|
||||||
|
"driver_modules": [
|
||||||
|
"bochs"
|
||||||
|
],
|
||||||
"module_alias": "pci:v00001234d00001111sv00001AF4sd00001100bc03sc00i00"
|
"module_alias": "pci:v00001234d00001111sv00001AF4sd00001100bc03sc00i00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -708,7 +761,10 @@
|
|||||||
{
|
{
|
||||||
"index": 24,
|
"index": 24,
|
||||||
"attached_to": 7,
|
"attached_to": 7,
|
||||||
"class_list": ["usb", "hub"],
|
"class_list": [
|
||||||
|
"usb",
|
||||||
|
"hub"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "0086",
|
"hex": "0086",
|
||||||
"name": "USB",
|
"name": "USB",
|
||||||
@@ -781,8 +837,12 @@
|
|||||||
"hotplug": "usb",
|
"hotplug": "usb",
|
||||||
"driver": "hub",
|
"driver": "hub",
|
||||||
"driver_module": "usbcore",
|
"driver_module": "usbcore",
|
||||||
"drivers": ["hub"],
|
"drivers": [
|
||||||
"driver_modules": ["usbcore"],
|
"hub"
|
||||||
|
],
|
||||||
|
"driver_modules": [
|
||||||
|
"usbcore"
|
||||||
|
],
|
||||||
"module_alias": "usb:v1D6Bp0001d0618dc09dsc00dp00ic09isc00ip00in00"
|
"module_alias": "usb:v1D6Bp0001d0618dc09dsc00dp00ic09isc00ip00in00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -790,7 +850,9 @@
|
|||||||
{
|
{
|
||||||
"index": 5,
|
"index": 5,
|
||||||
"attached_to": 0,
|
"attached_to": 0,
|
||||||
"class_list": ["memory"],
|
"class_list": [
|
||||||
|
"memory"
|
||||||
|
],
|
||||||
"base_class": {
|
"base_class": {
|
||||||
"hex": "0101",
|
"hex": "0101",
|
||||||
"name": "Internally Used Class",
|
"name": "Internally Used Class",
|
||||||
@@ -814,7 +876,9 @@
|
|||||||
{
|
{
|
||||||
"index": 21,
|
"index": 21,
|
||||||
"attached_to": 16,
|
"attached_to": 16,
|
||||||
"class_list": ["monitor"],
|
"class_list": [
|
||||||
|
"monitor"
|
||||||
|
],
|
||||||
"base_class": {
|
"base_class": {
|
||||||
"hex": "0100",
|
"hex": "0100",
|
||||||
"name": "Monitor",
|
"name": "Monitor",
|
||||||
@@ -960,7 +1024,10 @@
|
|||||||
{
|
{
|
||||||
"index": 25,
|
"index": 25,
|
||||||
"attached_to": 24,
|
"attached_to": 24,
|
||||||
"class_list": ["mouse", "usb"],
|
"class_list": [
|
||||||
|
"mouse",
|
||||||
|
"usb"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "0086",
|
"hex": "0086",
|
||||||
"name": "USB",
|
"name": "USB",
|
||||||
@@ -996,7 +1063,9 @@
|
|||||||
"model": "QEMU USB Tablet",
|
"model": "QEMU USB Tablet",
|
||||||
"sysfs_id": "/devices/pci0000:00/0000:00:01.2/usb1/1-1/1-1:1.0",
|
"sysfs_id": "/devices/pci0000:00/0000:00:01.2/usb1/1-1/1-1:1.0",
|
||||||
"sysfs_bus_id": "1-1:1.0",
|
"sysfs_bus_id": "1-1:1.0",
|
||||||
"unix_device_names": ["/dev/input/mice"],
|
"unix_device_names": [
|
||||||
|
"/dev/input/mice"
|
||||||
|
],
|
||||||
"unix_device_name2": "/dev/input/mouse0",
|
"unix_device_name2": "/dev/input/mouse0",
|
||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
@@ -1037,11 +1106,18 @@
|
|||||||
"hotplug": "usb",
|
"hotplug": "usb",
|
||||||
"driver": "usbhid",
|
"driver": "usbhid",
|
||||||
"driver_module": "usbhid",
|
"driver_module": "usbhid",
|
||||||
"drivers": ["usbhid"],
|
"drivers": [
|
||||||
"driver_modules": ["usbhid"],
|
"usbhid"
|
||||||
|
],
|
||||||
|
"driver_modules": [
|
||||||
|
"usbhid"
|
||||||
|
],
|
||||||
"driver_info": {
|
"driver_info": {
|
||||||
"type": "mouse",
|
"type": "mouse",
|
||||||
"db_entry_0": ["explorerps/2", "exps2"],
|
"db_entry_0": [
|
||||||
|
"explorerps/2",
|
||||||
|
"exps2"
|
||||||
|
],
|
||||||
"xf86": "explorerps/2",
|
"xf86": "explorerps/2",
|
||||||
"gpm": "exps2",
|
"gpm": "exps2",
|
||||||
"buttons": -1,
|
"buttons": -1,
|
||||||
@@ -1054,7 +1130,9 @@
|
|||||||
{
|
{
|
||||||
"index": 18,
|
"index": 18,
|
||||||
"attached_to": 13,
|
"attached_to": 13,
|
||||||
"class_list": ["network_controller"],
|
"class_list": [
|
||||||
|
"network_controller"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "008f",
|
"hex": "008f",
|
||||||
"name": "Virtio",
|
"name": "Virtio",
|
||||||
@@ -1079,7 +1157,9 @@
|
|||||||
"model": "Virtio Ethernet Card 0",
|
"model": "Virtio Ethernet Card 0",
|
||||||
"sysfs_id": "/devices/pci0000:00/0000:00:12.0/virtio1",
|
"sysfs_id": "/devices/pci0000:00/0000:00:12.0/virtio1",
|
||||||
"sysfs_bus_id": "virtio1",
|
"sysfs_bus_id": "virtio1",
|
||||||
"unix_device_names": ["ens18"],
|
"unix_device_names": [
|
||||||
|
"ens18"
|
||||||
|
],
|
||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"type": "hwaddr",
|
"type": "hwaddr",
|
||||||
@@ -1092,8 +1172,12 @@
|
|||||||
],
|
],
|
||||||
"driver": "virtio_net",
|
"driver": "virtio_net",
|
||||||
"driver_module": "virtio_net",
|
"driver_module": "virtio_net",
|
||||||
"drivers": ["virtio_net"],
|
"drivers": [
|
||||||
"driver_modules": ["virtio_net"],
|
"virtio_net"
|
||||||
|
],
|
||||||
|
"driver_modules": [
|
||||||
|
"virtio_net"
|
||||||
|
],
|
||||||
"module_alias": "virtio:d00000001v00001AF4"
|
"module_alias": "virtio:d00000001v00001AF4"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -1101,7 +1185,9 @@
|
|||||||
{
|
{
|
||||||
"index": 26,
|
"index": 26,
|
||||||
"attached_to": 18,
|
"attached_to": 18,
|
||||||
"class_list": ["network_interface"],
|
"class_list": [
|
||||||
|
"network_interface"
|
||||||
|
],
|
||||||
"base_class": {
|
"base_class": {
|
||||||
"hex": "0107",
|
"hex": "0107",
|
||||||
"name": "Network Interface",
|
"name": "Network Interface",
|
||||||
@@ -1115,7 +1201,9 @@
|
|||||||
"model": "Ethernet network interface",
|
"model": "Ethernet network interface",
|
||||||
"sysfs_id": "/class/net/ens18",
|
"sysfs_id": "/class/net/ens18",
|
||||||
"sysfs_device_link": "/devices/pci0000:00/0000:00:12.0/virtio1",
|
"sysfs_device_link": "/devices/pci0000:00/0000:00:12.0/virtio1",
|
||||||
"unix_device_names": ["ens18"],
|
"unix_device_names": [
|
||||||
|
"ens18"
|
||||||
|
],
|
||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"type": "hwaddr",
|
"type": "hwaddr",
|
||||||
@@ -1128,13 +1216,19 @@
|
|||||||
],
|
],
|
||||||
"driver": "virtio_net",
|
"driver": "virtio_net",
|
||||||
"driver_module": "virtio_net",
|
"driver_module": "virtio_net",
|
||||||
"drivers": ["virtio_net"],
|
"drivers": [
|
||||||
"driver_modules": ["virtio_net"]
|
"virtio_net"
|
||||||
|
],
|
||||||
|
"driver_modules": [
|
||||||
|
"virtio_net"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"index": 27,
|
"index": 27,
|
||||||
"attached_to": 0,
|
"attached_to": 0,
|
||||||
"class_list": ["network_interface"],
|
"class_list": [
|
||||||
|
"network_interface"
|
||||||
|
],
|
||||||
"base_class": {
|
"base_class": {
|
||||||
"hex": "0107",
|
"hex": "0107",
|
||||||
"name": "Network Interface",
|
"name": "Network Interface",
|
||||||
@@ -1147,14 +1241,19 @@
|
|||||||
},
|
},
|
||||||
"model": "Loopback network interface",
|
"model": "Loopback network interface",
|
||||||
"sysfs_id": "/class/net/lo",
|
"sysfs_id": "/class/net/lo",
|
||||||
"unix_device_names": ["lo"]
|
"unix_device_names": [
|
||||||
|
"lo"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"pci": [
|
"pci": [
|
||||||
{
|
{
|
||||||
"index": 13,
|
"index": 13,
|
||||||
"attached_to": 0,
|
"attached_to": 0,
|
||||||
"class_list": ["pci", "unknown"],
|
"class_list": [
|
||||||
|
"pci",
|
||||||
|
"unknown"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "0004",
|
"hex": "0004",
|
||||||
"name": "PCI",
|
"name": "PCI",
|
||||||
@@ -1211,14 +1310,21 @@
|
|||||||
},
|
},
|
||||||
"driver": "virtio-pci",
|
"driver": "virtio-pci",
|
||||||
"driver_module": "virtio_pci",
|
"driver_module": "virtio_pci",
|
||||||
"drivers": ["virtio-pci"],
|
"drivers": [
|
||||||
"driver_modules": ["virtio_pci"],
|
"virtio-pci"
|
||||||
|
],
|
||||||
|
"driver_modules": [
|
||||||
|
"virtio_pci"
|
||||||
|
],
|
||||||
"module_alias": "pci:v00001AF4d00001000sv00001AF4sd00000001bc02sc00i00"
|
"module_alias": "pci:v00001AF4d00001000sv00001AF4sd00000001bc02sc00i00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"index": 14,
|
"index": 14,
|
||||||
"attached_to": 0,
|
"attached_to": 0,
|
||||||
"class_list": ["pci", "unknown"],
|
"class_list": [
|
||||||
|
"pci",
|
||||||
|
"unknown"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "0004",
|
"hex": "0004",
|
||||||
"name": "PCI",
|
"name": "PCI",
|
||||||
@@ -1274,8 +1380,12 @@
|
|||||||
},
|
},
|
||||||
"driver": "virtio-pci",
|
"driver": "virtio-pci",
|
||||||
"driver_module": "virtio_pci",
|
"driver_module": "virtio_pci",
|
||||||
"drivers": ["virtio-pci"],
|
"drivers": [
|
||||||
"driver_modules": ["virtio_pci"],
|
"virtio-pci"
|
||||||
|
],
|
||||||
|
"driver_modules": [
|
||||||
|
"virtio_pci"
|
||||||
|
],
|
||||||
"module_alias": "pci:v00001AF4d00001002sv00001AF4sd00000005bc00scFFi00"
|
"module_alias": "pci:v00001AF4d00001002sv00001AF4sd00000005bc00scFFi00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -1283,7 +1393,10 @@
|
|||||||
{
|
{
|
||||||
"index": 6,
|
"index": 6,
|
||||||
"attached_to": 17,
|
"attached_to": 17,
|
||||||
"class_list": ["storage_controller", "pci"],
|
"class_list": [
|
||||||
|
"storage_controller",
|
||||||
|
"pci"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "0004",
|
"hex": "0004",
|
||||||
"name": "PCI",
|
"name": "PCI",
|
||||||
@@ -1340,14 +1453,21 @@
|
|||||||
},
|
},
|
||||||
"driver": "virtio-pci",
|
"driver": "virtio-pci",
|
||||||
"driver_module": "virtio_pci",
|
"driver_module": "virtio_pci",
|
||||||
"drivers": ["virtio-pci"],
|
"drivers": [
|
||||||
"driver_modules": ["virtio_pci"],
|
"virtio-pci"
|
||||||
|
],
|
||||||
|
"driver_modules": [
|
||||||
|
"virtio_pci"
|
||||||
|
],
|
||||||
"module_alias": "pci:v00001AF4d00001004sv00001AF4sd00000008bc01sc00i00"
|
"module_alias": "pci:v00001AF4d00001004sv00001AF4sd00000008bc01sc00i00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"index": 15,
|
"index": 15,
|
||||||
"attached_to": 0,
|
"attached_to": 0,
|
||||||
"class_list": ["storage_controller", "pci"],
|
"class_list": [
|
||||||
|
"storage_controller",
|
||||||
|
"pci"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "0004",
|
"hex": "0004",
|
||||||
"name": "PCI",
|
"name": "PCI",
|
||||||
@@ -1437,8 +1557,12 @@
|
|||||||
},
|
},
|
||||||
"driver": "ata_piix",
|
"driver": "ata_piix",
|
||||||
"driver_module": "ata_piix",
|
"driver_module": "ata_piix",
|
||||||
"drivers": ["ata_piix"],
|
"drivers": [
|
||||||
"driver_modules": ["ata_piix"],
|
"ata_piix"
|
||||||
|
],
|
||||||
|
"driver_modules": [
|
||||||
|
"ata_piix"
|
||||||
|
],
|
||||||
"module_alias": "pci:v00008086d00007010sv00001AF4sd00001100bc01sc01i80"
|
"module_alias": "pci:v00008086d00007010sv00001AF4sd00001100bc01sc01i80"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -1449,7 +1573,9 @@
|
|||||||
{
|
{
|
||||||
"index": 19,
|
"index": 19,
|
||||||
"attached_to": 6,
|
"attached_to": 6,
|
||||||
"class_list": ["unknown"],
|
"class_list": [
|
||||||
|
"unknown"
|
||||||
|
],
|
||||||
"base_class": {
|
"base_class": {
|
||||||
"hex": "0000",
|
"hex": "0000",
|
||||||
"name": "Unclassified device",
|
"name": "Unclassified device",
|
||||||
@@ -1467,14 +1593,20 @@
|
|||||||
"sysfs_bus_id": "virtio2",
|
"sysfs_bus_id": "virtio2",
|
||||||
"driver": "virtio_scsi",
|
"driver": "virtio_scsi",
|
||||||
"driver_module": "virtio_scsi",
|
"driver_module": "virtio_scsi",
|
||||||
"drivers": ["virtio_scsi"],
|
"drivers": [
|
||||||
"driver_modules": ["virtio_scsi"],
|
"virtio_scsi"
|
||||||
|
],
|
||||||
|
"driver_modules": [
|
||||||
|
"virtio_scsi"
|
||||||
|
],
|
||||||
"module_alias": "virtio:d00000008v00001AF4"
|
"module_alias": "virtio:d00000008v00001AF4"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"index": 20,
|
"index": 20,
|
||||||
"attached_to": 14,
|
"attached_to": 14,
|
||||||
"class_list": ["unknown"],
|
"class_list": [
|
||||||
|
"unknown"
|
||||||
|
],
|
||||||
"base_class": {
|
"base_class": {
|
||||||
"hex": "0000",
|
"hex": "0000",
|
||||||
"name": "Unclassified device",
|
"name": "Unclassified device",
|
||||||
@@ -1492,8 +1624,12 @@
|
|||||||
"sysfs_bus_id": "virtio0",
|
"sysfs_bus_id": "virtio0",
|
||||||
"driver": "virtio_balloon",
|
"driver": "virtio_balloon",
|
||||||
"driver_module": "virtio_balloon",
|
"driver_module": "virtio_balloon",
|
||||||
"drivers": ["virtio_balloon"],
|
"drivers": [
|
||||||
"driver_modules": ["virtio_balloon"],
|
"virtio_balloon"
|
||||||
|
],
|
||||||
|
"driver_modules": [
|
||||||
|
"virtio_balloon"
|
||||||
|
],
|
||||||
"module_alias": "virtio:d00000005v00001AF4"
|
"module_alias": "virtio:d00000005v00001AF4"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -1501,7 +1637,10 @@
|
|||||||
{
|
{
|
||||||
"index": 7,
|
"index": 7,
|
||||||
"attached_to": 0,
|
"attached_to": 0,
|
||||||
"class_list": ["usb_controller", "pci"],
|
"class_list": [
|
||||||
|
"usb_controller",
|
||||||
|
"pci"
|
||||||
|
],
|
||||||
"bus_type": {
|
"bus_type": {
|
||||||
"hex": "0004",
|
"hex": "0004",
|
||||||
"name": "PCI",
|
"name": "PCI",
|
||||||
@@ -1568,15 +1707,25 @@
|
|||||||
},
|
},
|
||||||
"driver": "uhci_hcd",
|
"driver": "uhci_hcd",
|
||||||
"driver_module": "uhci_hcd",
|
"driver_module": "uhci_hcd",
|
||||||
"drivers": ["uhci_hcd"],
|
"drivers": [
|
||||||
"driver_modules": ["uhci_hcd"],
|
"uhci_hcd"
|
||||||
|
],
|
||||||
|
"driver_modules": [
|
||||||
|
"uhci_hcd"
|
||||||
|
],
|
||||||
"driver_info": {
|
"driver_info": {
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"db_entry_0": ["uhci-hcd"],
|
"db_entry_0": [
|
||||||
|
"uhci-hcd"
|
||||||
|
],
|
||||||
"active": true,
|
"active": true,
|
||||||
"modprobe": true,
|
"modprobe": true,
|
||||||
"names": ["uhci-hcd"],
|
"names": [
|
||||||
"module_args": [""],
|
"uhci-hcd"
|
||||||
|
],
|
||||||
|
"module_args": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"conf": ""
|
"conf": ""
|
||||||
},
|
},
|
||||||
"module_alias": "pci:v00008086d00007020sv00001AF4sd00001100bc0Csc03i00"
|
"module_alias": "pci:v00008086d00007020sv00001AF4sd00001100bc0Csc03i00"
|
||||||
@@ -1689,7 +1838,9 @@
|
|||||||
"name": "RAM",
|
"name": "RAM",
|
||||||
"value": 7
|
"value": 7
|
||||||
},
|
},
|
||||||
"memory_type_details": ["Other"],
|
"memory_type_details": [
|
||||||
|
"Other"
|
||||||
|
],
|
||||||
"speed": 0
|
"speed": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
+615
-737
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@ in
|
|||||||
|
|
||||||
imports = [ inputs.frappix.nixosModules.x86_64-linux.frappix ];
|
imports = [ inputs.frappix.nixosModules.x86_64-linux.frappix ];
|
||||||
nixpkgs.overlays = [
|
nixpkgs.overlays = [
|
||||||
|
inputs.self.overlays.packagesOverlay
|
||||||
inputs.self.overlays.frappixFrappeOverlay
|
inputs.self.overlays.frappixFrappeOverlay
|
||||||
inputs.self.overlays.frappixLibsOverlay
|
inputs.self.overlays.frappixLibsOverlay
|
||||||
inputs.self.overlays.frappixPythonOverlay
|
inputs.self.overlays.frappixPythonOverlay
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
clan.core.settings.machine.description =
|
|
||||||
"VM machine for collecting prometheus metrics and fire alerts";
|
|
||||||
|
|
||||||
nixpkgs.hostPlatform = {
|
|
||||||
system = "x86_64-linux";
|
|
||||||
};
|
|
||||||
|
|
||||||
system.stateVersion = "25.11";
|
|
||||||
clan.core.sops.defaultGroups = [ "admins" ];
|
|
||||||
}
|
|
||||||
@@ -9,15 +9,7 @@
|
|||||||
];
|
];
|
||||||
clan = {
|
clan = {
|
||||||
meta.name = "NewEdgeClan";
|
meta.name = "NewEdgeClan";
|
||||||
machines = {
|
machines = { };
|
||||||
cursa = { ... }: { clan.core.state."vars".folders = [ "/var/lib" ]; };
|
|
||||||
hadar = { ... }: { clan.core.state."vars".folders = [ "/var/lib" ]; };
|
|
||||||
procyon = { ... }: { clan.core.state."vars".folders = [ "/var/lib" ]; };
|
|
||||||
bosona = { ... }: { clan.core.state."vars".folders = [ "/var/lib" ]; };
|
|
||||||
canopus = { ... }: { clan.core.state."vars".folders = [ "/var/lib" ]; };
|
|
||||||
deneb = { ... }: { clan.core.state."vars".folders = [ "/var/lib" ]; };
|
|
||||||
alasia = { ... }: { clan.core.state."vars".folders = [ "/var/lib" ]; };
|
|
||||||
};
|
|
||||||
secrets.age.plugins = [
|
secrets.age.plugins = [
|
||||||
"age-plugin-yubikey"
|
"age-plugin-yubikey"
|
||||||
"age-plugin-fido2-hmac"
|
"age-plugin-fido2-hmac"
|
||||||
|
|||||||
@@ -25,11 +25,8 @@ in
|
|||||||
inputs.self.overlays.frappixLibsOverlay
|
inputs.self.overlays.frappixLibsOverlay
|
||||||
inputs.self.overlays.frappixPythonOverlay
|
inputs.self.overlays.frappixPythonOverlay
|
||||||
inputs.self.overlays.frappixToolsOverlay
|
inputs.self.overlays.frappixToolsOverlay
|
||||||
inputs.self.overlays.frappixAppsOverlay
|
|
||||||
];
|
];
|
||||||
|
|
||||||
fonts.packages = [ pkgs.tlwg ];
|
|
||||||
|
|
||||||
clan.core.vars.generators.frappix = {
|
clan.core.vars.generators.frappix = {
|
||||||
files = {
|
files = {
|
||||||
sslCertificate.secret = false;
|
sslCertificate.secret = false;
|
||||||
@@ -59,8 +56,6 @@ in
|
|||||||
pkgs.frappix.erpnext
|
pkgs.frappix.erpnext
|
||||||
pkgs.frappix.hrms
|
pkgs.frappix.hrms
|
||||||
pkgs.frappix.crm
|
pkgs.frappix.crm
|
||||||
pkgs.frappix.posprinter
|
|
||||||
pkgs.frappix.default_thai_company
|
|
||||||
];
|
];
|
||||||
sites = {
|
sites = {
|
||||||
"${sitename}" = {
|
"${sitename}" = {
|
||||||
@@ -70,8 +65,6 @@ in
|
|||||||
"erpnext"
|
"erpnext"
|
||||||
"hrms"
|
"hrms"
|
||||||
"crm"
|
"crm"
|
||||||
"posprinter"
|
|
||||||
"default_thai_company"
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,6 +9,13 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
clan.core.settings.machine.description = "VM machine that host Inventree system for Poy";
|
clan.core.settings.machine.description = "VM machine that host Inventree system for Poy";
|
||||||
|
imports = [
|
||||||
|
inputs.self.nixosModules.inventree
|
||||||
|
];
|
||||||
|
|
||||||
|
nixpkgs.overlays = [
|
||||||
|
inputs.self.overlays.packagesOverlay
|
||||||
|
];
|
||||||
|
|
||||||
nixpkgs.hostPlatform = {
|
nixpkgs.hostPlatform = {
|
||||||
system = "x86_64-linux";
|
system = "x86_64-linux";
|
||||||
@@ -75,12 +82,19 @@ in
|
|||||||
|
|
||||||
services.inventree = {
|
services.inventree = {
|
||||||
enable = true;
|
enable = true;
|
||||||
inherit domain;
|
hostName = "${domain}";
|
||||||
|
config.site_url = "https://${config.services.inventree.hostName}";
|
||||||
secretKeyFile = config.clan.core.vars.generators.inventree.files.secret-key.path;
|
secretKeyFile = config.clan.core.vars.generators.inventree.files.secret-key.path;
|
||||||
adminPasswordFile = config.clan.core.vars.generators.inventree.files.admin-password.path;
|
config.oidc_private_key_file = config.clan.core.vars.generators.inventree.files.oidc-key.path;
|
||||||
settings.INVENTREE_SITE_URL = "https://${domain}";
|
config.adminPasswordFile = config.clan.core.vars.generators.inventree.files.admin-password.path;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# services.nginx.virtualHosts."${domain}" = {
|
||||||
|
# forceSSL = true;
|
||||||
|
# sslCertificate = config.clan.core.vars.generators.nginx.files.sslCert.path;
|
||||||
|
# sslCertificateKey = config.clan.core.vars.generators.nginx.files.sslKey.path;
|
||||||
|
# };
|
||||||
|
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "25.11";
|
||||||
clan.core.sops.defaultGroups = [ "admins" ];
|
clan.core.sops.defaultGroups = [ "admins" ];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
};
|
};
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "25.11";
|
||||||
clan.core.sops.defaultGroups = [ "admins" ];
|
clan.core.sops.defaultGroups = [ "admins" ];
|
||||||
|
clan.core.networking.targetHost = "root@[${config.clan.core.vars.generators.zerotier.files.zerotier-ip.value}]";
|
||||||
|
|
||||||
networking.interfaces.enx00e04c106368.useDHCP = true; # recovery
|
networking.interfaces.enx00e04c106368.useDHCP = true; # recovery
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,13 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
clan.core.settings.machine.description = "VM machine that host Inventree system for Glom";
|
clan.core.settings.machine.description = "VM machine that host Inventree system for Glom";
|
||||||
|
imports = [
|
||||||
|
inputs.self.nixosModules.inventree
|
||||||
|
];
|
||||||
|
|
||||||
|
nixpkgs.overlays = [
|
||||||
|
inputs.self.overlays.packagesOverlay
|
||||||
|
];
|
||||||
|
|
||||||
nixpkgs.hostPlatform = {
|
nixpkgs.hostPlatform = {
|
||||||
system = "x86_64-linux";
|
system = "x86_64-linux";
|
||||||
@@ -75,12 +82,19 @@ in
|
|||||||
|
|
||||||
services.inventree = {
|
services.inventree = {
|
||||||
enable = true;
|
enable = true;
|
||||||
inherit domain;
|
hostName = "${domain}";
|
||||||
|
config.site_url = "https://${config.services.inventree.hostName}";
|
||||||
secretKeyFile = config.clan.core.vars.generators.inventree.files.secret-key.path;
|
secretKeyFile = config.clan.core.vars.generators.inventree.files.secret-key.path;
|
||||||
adminPasswordFile = config.clan.core.vars.generators.inventree.files.admin-password.path;
|
config.oidc_private_key_file = config.clan.core.vars.generators.inventree.files.oidc-key.path;
|
||||||
settings.INVENTREE_SITE_URL = "https://${domain}";
|
config.adminPasswordFile = config.clan.core.vars.generators.inventree.files.admin-password.path;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# services.nginx.virtualHosts."${domain}" = {
|
||||||
|
# forceSSL = true;
|
||||||
|
# sslCertificate = config.clan.core.vars.generators.nginx.files.sslCert.path;
|
||||||
|
# sslCertificateKey = config.clan.core.vars.generators.nginx.files.sslKey.path;
|
||||||
|
# };
|
||||||
|
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "25.11";
|
||||||
clan.core.sops.defaultGroups = [ "admins" ];
|
clan.core.sops.defaultGroups = [ "admins" ];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
clan.core.settings.machine.description = "A Hetzner VPS machine own by Alex.";
|
clan.core.settings.machine.description = "A Hetzner VPS machine own by Alex.";
|
||||||
|
|
||||||
clan.core.sops.defaultGroups = [ "admins" ];
|
clan.core.sops.defaultGroups = [ "admins" ];
|
||||||
|
clan.core.networking.targetHost = "root@[${config.clan.core.vars.generators.zerotier.files.zerotier-ip.value}]";
|
||||||
|
|
||||||
clan.core.vars.generators.acme = {
|
clan.core.vars.generators.acme = {
|
||||||
share = true;
|
share = true;
|
||||||
|
|||||||
@@ -1,7 +1,18 @@
|
|||||||
{ config, ... }:
|
{ config, ... }:
|
||||||
{
|
{
|
||||||
|
imports = [
|
||||||
|
(import ../../lib/auto-accept-zerotier-members.nix {
|
||||||
|
memberIds = [
|
||||||
|
"dbe44c0287" # Alex-gateway
|
||||||
|
"b0e0b84fd3" # Alex
|
||||||
|
"2bd36db8cc" # kurogeek-thinkpad
|
||||||
|
];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "25.11";
|
||||||
clan.core.sops.defaultGroups = [ "admins" ];
|
clan.core.sops.defaultGroups = [ "admins" ];
|
||||||
|
clan.core.networking.targetHost = "root@[${config.clan.core.vars.generators.zerotier.files.zerotier-ip.value}]";
|
||||||
|
|
||||||
clan.core.settings.machine.description = "Zima board computer for testing in B4L";
|
clan.core.settings.machine.description = "Zima board computer for testing in B4L";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
{
|
||||||
|
inputs,
|
||||||
|
pkgs,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
sitename = "test.newedge.house";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
clan.core.settings.machine.description = "VM machine for test things";
|
||||||
|
|
||||||
|
imports = [ inputs.frappix.nixosModules.x86_64-linux.frappix ];
|
||||||
|
nixpkgs.overlays = [
|
||||||
|
inputs.self.overlays.packagesOverlay
|
||||||
|
inputs.self.overlays.frappixFrappeOverlay
|
||||||
|
inputs.self.overlays.frappixLibsOverlay
|
||||||
|
inputs.self.overlays.frappixPythonOverlay
|
||||||
|
inputs.self.overlays.frappixToolsOverlay
|
||||||
|
];
|
||||||
|
|
||||||
|
clan.core.vars.generators.frappix = {
|
||||||
|
files = {
|
||||||
|
sslCertificate.secret = false;
|
||||||
|
sslCertificateKey = {
|
||||||
|
owner = "nginx";
|
||||||
|
group = "nginx";
|
||||||
|
secret = true;
|
||||||
|
};
|
||||||
|
adminPassword.secret = true;
|
||||||
|
};
|
||||||
|
runtimeInputs = with pkgs; [
|
||||||
|
openssl
|
||||||
|
xkcdpass
|
||||||
|
];
|
||||||
|
script = ''
|
||||||
|
openssl req -x509 -newkey rsa:4096 -keyout $out/sslCertificateKey -out $out/sslCertificate -sha256 -days 3650 -nodes -subj "/C=TH/ST=ChiangMai/L=ChiangMai/O=localhost/CN=localhost"
|
||||||
|
xkcdpass --numwords 4 --delimiter - --count 1 | tr -d "\n" > "$out"/adminPassword
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
services.frappe = {
|
||||||
|
enable = true;
|
||||||
|
project = "glomerp";
|
||||||
|
gunicorn_workers = 2;
|
||||||
|
adminPassword = config.clan.core.vars.generators.frappix.files.adminPassword.path;
|
||||||
|
apps = [
|
||||||
|
pkgs.frappix.erpnext
|
||||||
|
pkgs.frappix.hrms
|
||||||
|
pkgs.erpnext_thailand
|
||||||
|
pkgs.thai_payroll
|
||||||
|
];
|
||||||
|
sites = {
|
||||||
|
"${sitename}" = {
|
||||||
|
domains = [ sitename ];
|
||||||
|
apps = [
|
||||||
|
"frappe"
|
||||||
|
"erpnext"
|
||||||
|
"hrms"
|
||||||
|
"erpnext_thailand"
|
||||||
|
"thai_payroll"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
services.nginx.virtualHosts."${sitename}" = {
|
||||||
|
sslCertificate = config.clan.core.vars.generators.frappix.files.sslCertificate.path;
|
||||||
|
sslCertificateKey = config.clan.core.vars.generators.frappix.files.sslCertificateKey.path;
|
||||||
|
};
|
||||||
|
|
||||||
|
nixpkgs.hostPlatform = {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
};
|
||||||
|
|
||||||
|
clan.core.vars.generators.nginx = {
|
||||||
|
files = {
|
||||||
|
sslCert = {
|
||||||
|
owner = "nginx";
|
||||||
|
group = "nginx";
|
||||||
|
secret = true;
|
||||||
|
};
|
||||||
|
sslKey = {
|
||||||
|
owner = "nginx";
|
||||||
|
group = "nginx";
|
||||||
|
secret = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
runtimeInputs = [
|
||||||
|
pkgs.openssl
|
||||||
|
];
|
||||||
|
script = ''
|
||||||
|
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
|
||||||
|
-keyout $out/sslKey \
|
||||||
|
-out $out/sslCert \
|
||||||
|
-subj "/CN=localhost"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [
|
||||||
|
80
|
||||||
|
443
|
||||||
|
];
|
||||||
|
|
||||||
|
system.stateVersion = "25.11";
|
||||||
|
clan.core.sops.defaultGroups = [ "admins" ];
|
||||||
|
}
|
||||||
@@ -475,7 +475,7 @@
|
|||||||
"fpu_exception": false,
|
"fpu_exception": false,
|
||||||
"cpuid_level": 13,
|
"cpuid_level": 13,
|
||||||
"write_protect": false,
|
"write_protect": false,
|
||||||
"tlb_size": 32764,
|
"tlb_size": 32766,
|
||||||
"clflush_size": 64,
|
"clflush_size": 64,
|
||||||
"cache_alignment": 128,
|
"cache_alignment": 128,
|
||||||
"address_sizes": {
|
"address_sizes": {
|
||||||
@@ -557,7 +557,7 @@
|
|||||||
"fpu_exception": false,
|
"fpu_exception": false,
|
||||||
"cpuid_level": 13,
|
"cpuid_level": 13,
|
||||||
"write_protect": false,
|
"write_protect": false,
|
||||||
"tlb_size": 32764,
|
"tlb_size": 32766,
|
||||||
"clflush_size": 64,
|
"clflush_size": 64,
|
||||||
"cache_alignment": 128,
|
"cache_alignment": 128,
|
||||||
"address_sizes": {
|
"address_sizes": {
|
||||||
@@ -1100,24 +1100,6 @@
|
|||||||
"network_interface": [
|
"network_interface": [
|
||||||
{
|
{
|
||||||
"index": 26,
|
"index": 26,
|
||||||
"attached_to": 0,
|
|
||||||
"class_list": ["network_interface"],
|
|
||||||
"base_class": {
|
|
||||||
"hex": "0107",
|
|
||||||
"name": "Network Interface",
|
|
||||||
"value": 263
|
|
||||||
},
|
|
||||||
"sub_class": {
|
|
||||||
"hex": "0000",
|
|
||||||
"name": "Loopback",
|
|
||||||
"value": 0
|
|
||||||
},
|
|
||||||
"model": "Loopback network interface",
|
|
||||||
"sysfs_id": "/class/net/lo",
|
|
||||||
"unix_device_names": ["lo"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"index": 27,
|
|
||||||
"attached_to": 18,
|
"attached_to": 18,
|
||||||
"class_list": ["network_interface"],
|
"class_list": ["network_interface"],
|
||||||
"base_class": {
|
"base_class": {
|
||||||
@@ -1148,6 +1130,24 @@
|
|||||||
"driver_module": "virtio_net",
|
"driver_module": "virtio_net",
|
||||||
"drivers": ["virtio_net"],
|
"drivers": ["virtio_net"],
|
||||||
"driver_modules": ["virtio_net"]
|
"driver_modules": ["virtio_net"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 27,
|
||||||
|
"attached_to": 0,
|
||||||
|
"class_list": ["network_interface"],
|
||||||
|
"base_class": {
|
||||||
|
"hex": "0107",
|
||||||
|
"name": "Network Interface",
|
||||||
|
"value": 263
|
||||||
|
},
|
||||||
|
"sub_class": {
|
||||||
|
"hex": "0000",
|
||||||
|
"name": "Loopback",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
"model": "Loopback network interface",
|
||||||
|
"sysfs_id": "/class/net/lo",
|
||||||
|
"unix_device_names": ["lo"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"pci": [
|
"pci": [
|
||||||
@@ -1587,8 +1587,8 @@
|
|||||||
"bios": {
|
"bios": {
|
||||||
"handle": 0,
|
"handle": 0,
|
||||||
"vendor": "Proxmox distribution of EDK II",
|
"vendor": "Proxmox distribution of EDK II",
|
||||||
"version": "4.2025.05-1~bpo12+1",
|
"version": "4.2025.02-4~bpo12+1",
|
||||||
"date": "03/12/2026",
|
"date": "07/10/2025",
|
||||||
"features": null,
|
"features": null,
|
||||||
"start_address": "0xe8000",
|
"start_address": "0xe8000",
|
||||||
"rom_size": 65536
|
"rom_size": 65536
|
||||||
@@ -5,6 +5,8 @@
|
|||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
|
self.nixosModules.common
|
||||||
|
|
||||||
./hardware-configuration.nix
|
./hardware-configuration.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -21,12 +23,15 @@
|
|||||||
nixpkgs.hostPlatform = {
|
nixpkgs.hostPlatform = {
|
||||||
system = "aarch64-linux";
|
system = "aarch64-linux";
|
||||||
};
|
};
|
||||||
nixpkgs.buildPlatform = {
|
|
||||||
system = "x86_64-linux";
|
|
||||||
};
|
|
||||||
|
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "25.11";
|
||||||
|
|
||||||
|
services.journald.extraConfig = ''
|
||||||
|
Storage=volatile
|
||||||
|
RuntimeMaxUse=30M
|
||||||
|
RuntimeMaxFileSize=10M
|
||||||
|
'';
|
||||||
|
|
||||||
services.udisks2.enable = false;
|
services.udisks2.enable = false;
|
||||||
|
|
||||||
nix.settings.log-lines = 25;
|
nix.settings.log-lines = 25;
|
||||||
@@ -49,25 +54,6 @@
|
|||||||
"sd_mod"
|
"sd_mod"
|
||||||
];
|
];
|
||||||
|
|
||||||
boot = {
|
|
||||||
consoleLogLevel = 0;
|
|
||||||
kernel.sysctl = {
|
|
||||||
"fs.suid_dumpable" = 0;
|
|
||||||
"kernel.core_pattern" = "/dev/null";
|
|
||||||
};
|
|
||||||
tmp = {
|
|
||||||
useTmpfs = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.journald.extraConfig = ''
|
|
||||||
Storage=none
|
|
||||||
'';
|
|
||||||
|
|
||||||
systemd = {
|
|
||||||
coredump.enable = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
fileSystems."/mnt/hdd" = {
|
fileSystems."/mnt/hdd" = {
|
||||||
device = "zdata/nas";
|
device = "zdata/nas";
|
||||||
fsType = "zfs";
|
fsType = "zfs";
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
system.stateVersion = "25.11";
|
|
||||||
nixpkgs.hostPlatform = {
|
|
||||||
system = "x86_64-linux";
|
|
||||||
};
|
|
||||||
|
|
||||||
clan.core.settings.name = "tangra";
|
|
||||||
clan.core.settings.machine.description =
|
|
||||||
"A Hetzner VPS machine own by Alex. This is a machine for poyfestival.com";
|
|
||||||
|
|
||||||
clan.core.sops.defaultGroups = [ "admins" ];
|
|
||||||
|
|
||||||
clan.core.vars.generators.acme = {
|
|
||||||
share = true;
|
|
||||||
files.email.secret = false;
|
|
||||||
|
|
||||||
prompts.email = {
|
|
||||||
type = "line";
|
|
||||||
description = "Email for ACME registeration";
|
|
||||||
};
|
|
||||||
|
|
||||||
script = ''
|
|
||||||
cat $prompts/email > $out/email
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
users.users.nginx.extraGroups = [ "acme" ];
|
|
||||||
|
|
||||||
security.acme.acceptTerms = true;
|
|
||||||
|
|
||||||
imports = [ ];
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
{ ... }:
|
|
||||||
let
|
|
||||||
hashDisk = disk: "os-${builtins.substring 0 5 (builtins.hashString "sha256" disk)}";
|
|
||||||
os = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_119349241";
|
|
||||||
in
|
|
||||||
{
|
|
||||||
|
|
||||||
boot.loader = {
|
|
||||||
systemd-boot = {
|
|
||||||
enable = true;
|
|
||||||
};
|
|
||||||
efi = {
|
|
||||||
canTouchEfiVariables = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
boot.zfs.forceImportRoot = true;
|
|
||||||
|
|
||||||
disko.devices = {
|
|
||||||
disk = {
|
|
||||||
"os-${hashDisk os}" = {
|
|
||||||
type = "disk";
|
|
||||||
device = os;
|
|
||||||
content = {
|
|
||||||
type = "gpt";
|
|
||||||
partitions = {
|
|
||||||
ESP = {
|
|
||||||
size = "1G";
|
|
||||||
type = "EF00";
|
|
||||||
content = {
|
|
||||||
type = "filesystem";
|
|
||||||
format = "vfat";
|
|
||||||
mountpoint = "/boot";
|
|
||||||
mountOptions = [ "nofail" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
system = {
|
|
||||||
size = "100%";
|
|
||||||
content = {
|
|
||||||
type = "zfs";
|
|
||||||
pool = "zroot";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
zpool = {
|
|
||||||
zroot = {
|
|
||||||
type = "zpool";
|
|
||||||
rootFsOptions = {
|
|
||||||
mountpoint = "none";
|
|
||||||
compression = "lz4";
|
|
||||||
acltype = "posixacl";
|
|
||||||
xattr = "sa";
|
|
||||||
"com.sun:auto-snapshot" = "true";
|
|
||||||
};
|
|
||||||
options.ashift = "12";
|
|
||||||
datasets = {
|
|
||||||
"root" = {
|
|
||||||
type = "zfs_fs";
|
|
||||||
options.mountpoint = "none";
|
|
||||||
};
|
|
||||||
"root/nixos" = {
|
|
||||||
type = "zfs_fs";
|
|
||||||
options.mountpoint = "/";
|
|
||||||
mountpoint = "/";
|
|
||||||
};
|
|
||||||
"root/home" = {
|
|
||||||
type = "zfs_fs";
|
|
||||||
options.mountpoint = "/home";
|
|
||||||
mountpoint = "/home";
|
|
||||||
};
|
|
||||||
"root/tmp" = {
|
|
||||||
type = "zfs_fs";
|
|
||||||
mountpoint = "/tmp";
|
|
||||||
options = {
|
|
||||||
mountpoint = "/tmp";
|
|
||||||
sync = "disabled";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -10,9 +10,17 @@
|
|||||||
|
|
||||||
(inputs.import-tree ./services)
|
(inputs.import-tree ./services)
|
||||||
|
|
||||||
|
(import ../../lib/auto-accept-zerotier-members.nix {
|
||||||
|
memberIds = [
|
||||||
|
"dbe44c0287" # Alex-gateway
|
||||||
|
"b0e0b84fd3" # Alex
|
||||||
|
"2bd36db8cc" # kurogeek-thinkpad
|
||||||
|
];
|
||||||
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
clan.core.sops.defaultGroups = [ "admins" ];
|
clan.core.sops.defaultGroups = [ "admins" ];
|
||||||
|
clan.core.networking.targetHost = "root@[${config.clan.core.vars.generators.zerotier.files.zerotier-ip.value}]";
|
||||||
|
|
||||||
clan.core.settings.machine.description = "Glom NAS";
|
clan.core.settings.machine.description = "Glom NAS";
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
# Apple Network
|
|
||||||
|
|
||||||
This service allows atalkd instances to connect to each other over vxlan over Yggdrasil. We need to use vxlan due to the support of broadcasting it provides. The service will form a network of star-topology vxlan using the underlying Yggdrasil network. With this vxlan network, we can have a private Apple Talk network over the internet.
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
{ clanLib, ... }:
|
|
||||||
{
|
|
||||||
_class = "clan.service";
|
|
||||||
manifest.name = "apple-network";
|
|
||||||
manifest.description = "This service will create an instance of `atalkd` for each peer to talk to Apple machines over Apple Talk protocol and automatically connect each peer using vxlan over Yggdrasil network to achieve Apple Talk over internet experience";
|
|
||||||
manifest.readme = builtins.readFile ./README.md;
|
|
||||||
manifest.categories = [ "Network" ];
|
|
||||||
|
|
||||||
roles.peer = {
|
|
||||||
description = "A gateway machine that allow classic Apple machines to connect to other Apple machines over the internet";
|
|
||||||
|
|
||||||
interface =
|
|
||||||
{ lib, ... }:
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
zone_name = lib.mkOption {
|
|
||||||
type = with lib.types; str;
|
|
||||||
description = "Zone name for Apple Talk protocol";
|
|
||||||
default = "Default";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
perInstance =
|
|
||||||
{ roles, settings, ... }:
|
|
||||||
{
|
|
||||||
nixosModule =
|
|
||||||
{
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
vxlanPort = 4789;
|
|
||||||
|
|
||||||
getYggdrasilIP =
|
|
||||||
machineName:
|
|
||||||
if config.clan.core.vars.generators.yggdrasil.files.address ? value then
|
|
||||||
clanLib.getPublicValue {
|
|
||||||
flake = config.clan.core.settings.directory;
|
|
||||||
machine = machineName;
|
|
||||||
generator = "yggdrasil";
|
|
||||||
file = "address";
|
|
||||||
default = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw "clanService/yggdrasil is required";
|
|
||||||
|
|
||||||
noSelfPeers = builtins.filter (peerName: peerName != config.clan.core.settings.machine.name) (
|
|
||||||
lib.mapAttrsToList (machineName: _: machineName) roles.peer.machines
|
|
||||||
);
|
|
||||||
|
|
||||||
sortedPeers = builtins.sort (x: y: x < y) (
|
|
||||||
lib.mapAttrsToList (machineName: _: machineName) roles.peer.machines
|
|
||||||
);
|
|
||||||
|
|
||||||
getMachineIndex = machineName: lib.lists.findFirstIndex (x: x == machineName) null sortedPeers;
|
|
||||||
|
|
||||||
selfVXAddress = "192.168.254.${
|
|
||||||
lib.toString ((getMachineIndex config.clan.core.settings.machine.name) + 1)
|
|
||||||
}/24";
|
|
||||||
in
|
|
||||||
{
|
|
||||||
|
|
||||||
services.atalkd = {
|
|
||||||
enable = true;
|
|
||||||
interfaces = {
|
|
||||||
vxlan.config = ''
|
|
||||||
-router -phase 2 -net 1 -zone "${settings.zone_name}"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.useNetworkd = true;
|
|
||||||
|
|
||||||
networking.firewall.interfaces."ygg".allowedUDPPorts = [ vxlanPort ];
|
|
||||||
|
|
||||||
boot.kernelModules = [ "vxlan" ];
|
|
||||||
|
|
||||||
systemd.network.netdevs =
|
|
||||||
builtins.listToAttrs (
|
|
||||||
map (
|
|
||||||
peerName:
|
|
||||||
(lib.nameValuePair "10-apl-vxlan-${peerName}" {
|
|
||||||
enable = true;
|
|
||||||
netdevConfig = {
|
|
||||||
Name = "vxlan-${peerName}";
|
|
||||||
Kind = "vxlan";
|
|
||||||
};
|
|
||||||
vxlanConfig = {
|
|
||||||
VNI = (getMachineIndex config.clan.core.settings.machine.name) + (getMachineIndex peerName);
|
|
||||||
Remote = getYggdrasilIP peerName;
|
|
||||||
DestinationPort = vxlanPort;
|
|
||||||
Independent = true;
|
|
||||||
};
|
|
||||||
})
|
|
||||||
) noSelfPeers
|
|
||||||
)
|
|
||||||
// {
|
|
||||||
"10-apl-vxlan" = {
|
|
||||||
enable = true;
|
|
||||||
netdevConfig = {
|
|
||||||
Kind = "bridge";
|
|
||||||
Name = "vxlan";
|
|
||||||
};
|
|
||||||
bridgeConfig = {
|
|
||||||
STP = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.network.networks =
|
|
||||||
builtins.listToAttrs (
|
|
||||||
map (
|
|
||||||
peerName:
|
|
||||||
(lib.nameValuePair "10-apl-vxlan-${peerName}" {
|
|
||||||
enable = true;
|
|
||||||
matchConfig.Name = "vxlan-${peerName}";
|
|
||||||
networkConfig.Bridge = "vxlan";
|
|
||||||
})
|
|
||||||
) noSelfPeers
|
|
||||||
)
|
|
||||||
// {
|
|
||||||
"10-apl-vxlan" = {
|
|
||||||
enable = true;
|
|
||||||
matchConfig.Name = "vxlan";
|
|
||||||
address = [ selfVXAddress ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
environment.systemPackages = [
|
|
||||||
pkgs.netatalk
|
|
||||||
pkgs.bridge-utils
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{ inputs, self, ... }:
|
|
||||||
let
|
|
||||||
module = ./default.nix;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
clan.modules = {
|
|
||||||
apple-network = module;
|
|
||||||
};
|
|
||||||
perSystem =
|
|
||||||
{ ... }:
|
|
||||||
{
|
|
||||||
clan.nixosTests.service-apple-network = {
|
|
||||||
imports = [ ./tests/vm/default.nix ];
|
|
||||||
_module.args = { inherit self inputs; };
|
|
||||||
|
|
||||||
clan.modules."@clan/apple-network" = module;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
{
|
|
||||||
self,
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
hostPkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
name = "service-apple-network";
|
|
||||||
|
|
||||||
result.update-vars =
|
|
||||||
let
|
|
||||||
relativeDir = lib.removePrefix "${self}/" (toString config.clan.directory);
|
|
||||||
in
|
|
||||||
hostPkgs.writeShellScriptBin "update-vars" ''
|
|
||||||
set -x
|
|
||||||
export PRJ_ROOT=$(git rev-parse --show-toplevel)
|
|
||||||
${
|
|
||||||
self.inputs.clan-core.packages.${hostPkgs.system}.clan-cli
|
|
||||||
}/bin/clan-generate-test-vars $PRJ_ROOT/${relativeDir} ${config.name}
|
|
||||||
'';
|
|
||||||
|
|
||||||
clan = {
|
|
||||||
directory = ./.;
|
|
||||||
test.useContainers = false;
|
|
||||||
|
|
||||||
inventory = {
|
|
||||||
meta.domain = "test.clan";
|
|
||||||
|
|
||||||
machines.peer1 = { };
|
|
||||||
machines.peer2 = { };
|
|
||||||
machines.peer3 = { };
|
|
||||||
|
|
||||||
instances = {
|
|
||||||
apple-network = {
|
|
||||||
module.name = "@clan/apple-network";
|
|
||||||
module.input = "self";
|
|
||||||
roles.peer.machines = {
|
|
||||||
peer1 = { };
|
|
||||||
peer2 = { };
|
|
||||||
peer3 = { };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
yggdrasil = {
|
|
||||||
module.name = "yggdrasil";
|
|
||||||
roles.default.tags.all = { };
|
|
||||||
roles.default.settings.extraPeers = [
|
|
||||||
"tls://ygg.jjolly.dev:3443"
|
|
||||||
"tls://[2602:fc24:18:7a42::1]:993"
|
|
||||||
"tcp://leo.node.3dt.net:9002"
|
|
||||||
"tcp://ygg-kcmo.incognet.io:8883"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
nodes = {
|
|
||||||
peer1 = { };
|
|
||||||
peer2 = { };
|
|
||||||
peer3 = { };
|
|
||||||
};
|
|
||||||
|
|
||||||
testScript = _: ''
|
|
||||||
# cannot test connectivity due to Yggdrasil's establishment
|
|
||||||
start_all()
|
|
||||||
peer1.wait_for_unit("atalkd")
|
|
||||||
peer1.succeed("systemctl status atalkd")
|
|
||||||
|
|
||||||
peer2.wait_for_unit("atalkd")
|
|
||||||
peer2.succeed("systemctl status atalkd")
|
|
||||||
|
|
||||||
peer3.wait_for_unit("atalkd")
|
|
||||||
peer3.succeed("systemctl status atalkd")
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"publickey": "age1f92zl8y70z5w9gn0l5leg2fhyvxy6m93dfh7n6ltgvuu9nyg6epsrw6t8x",
|
|
||||||
"type": "age"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"publickey": "age1u36fr87cru90vfznf5szasya9nhvvseh4atk8zsdj0rrsu0nnvwqsyt8g6",
|
|
||||||
"type": "age"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"publickey": "age1rka3368ltqc787xw99uu762lqfk7qxld9265u2t62fpxnp9xhufsc2q5rw",
|
|
||||||
"type": "age"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:hC4Lle6mXuTFq//wBHUTyH3X778u/gYJSXJOiQ7xUb+gnC8beH1olxL2ZhtUiMkktzhsPM/j6cxhltPp25UTeJnUOcoYesqZcp8=,iv:WOx2P2Cu4v58xM9ePjAw1gdA0fFnGG2UqEXH2iksGoo=,tag:43n1ibVSy2AeS9f5qQBKNQ==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtT244N1Q3Zk9pRjFFQlUr\ncDExSTBwSWk5UXVyMjhWclQwSFZaYnpTRFY0CnE5YStVdzMrS3BKYzYvc3V3QVFY\nbDVwVytoNERveU1xQllqNUFGV2hYbGMKLS0tIDJSUmkwMUQzL3Y0MU5qZTJ3eFN3\nNGdwamM1UDBPdzhvSWsyZzR6anZ0eFEKKnr1/7rf3fH1i7KUoZdilLDgb44K2qzn\nI9Y+7FbV4gzQUzKWjAQeFN37Z4Qiuy0xJVA0lw8KvM+NCxSZDfh3kw==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2026-07-03T10:46:56Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:hmpbOtra6EyNiPg7EsA3F4elHEO87JPUr4VKVppvzUwLX1HSj67WrU6k3rEoJT+k7Hq1lxnAzDEckSG1fUOap11rk8ksp1IAGR0+yqykC/Qj/Nl8wXqP84gr49bJK6xE/DDdCRCcBff2d6IpnehyEr8fGFEZbWGygblec8U8GPo=,iv:+M1C3rL22foowr0VD80jwrNFb5xz4k5JQ/DJm4ThKHk=,tag:wabFAC5NTCVGfIQQzm0yGg==,type:str]",
|
|
||||||
"version": "3.13.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../users/admin
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:ndWIEOqP2ybCN7Ak5g+8q3YG5rLQ7OuSv/vCbnMA8AK6Lby2JMGRkRGysqJdyEF7eh4w7PpZ/AtKPtPbUucjeTB5+8g76PPJTBE=,iv:EYy0GGqEBmIc5rSWP7Zleb5eQ0JzTz84HqeJPpCSpxk=,tag:iMgTUb9+wxE9S2kKoOEvsQ==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQTEF6OXN4K0dHdXhWWmt2\neG5wdkNIc2F0UnFEeHFLQkdTWkFpUWhTa0NBCmw2N2J1T0FJVE9JR3VBRWV0THh5\nVEZlbVZrcXAwK3BsYXFOZUF6c3lRSkUKLS0tIFRCQzNUaGFHdERWNWR4enVWRGpx\nbDEzRU9jWHgxWHovaENjbkFlZERIbkkKvAW31gTJg9izrWuAMeDHS9SWPQYtxRBY\necDCn8QORFrnDdxEusCnGJrAU4kmS3Y3CwSHCtEijs3JdIgg9wl/MA==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2026-07-03T10:47:04Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:y1U2vsdqR97fPAnp6iZiLY1KbWYmgDqgZ5bwIkWPCr7Gjjrn+qV+sc2T1BTVz3Zo0JOz4ScCc+f6snVsFKdCDU/NmDVFt/sqyH4NHUPRQ3StsQim8C7IkSwUtm3EhrqpLYFD21MEjrYYdHMHbWybxrOI0qI6mDwZymHJFeCLdQA=,iv:lVvPn69eQE/huwDmwePepfpi2l9ZOPQO7W20o4AJBFQ=,tag:7VPfjKZsBVJTZxNwTcI6sg==,type:str]",
|
|
||||||
"version": "3.13.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../users/admin
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:JPdetkhtF9eSmgEfCO2exXlGQ5KWLeIM3mnW5BfyTY8r0PxWSEGAwBskC1SiDdRERWB2VEbPiPgCNXMnGaYlDqSdZN6R9wSFvsQ=,iv:qGveNhUtgxvKnmdsPoPpffKyi5srlq0Pd6aW283B5C0=,tag:TcJsCrBlzfjXvztcRaxYFQ==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvdjNHa1RlcFJHazB5TXFu\nOUQ4REpCZm5Yc3ZhcVhwT1FqRHlvbnoxZWlFCnNrVUEyK2ZYSmlacmJ2b2QrclZl\nSDV1eWxXVmJxZlhMMXgrTWtjcjVhQlkKLS0tIHZPN3pCUVBLQmpRVGx6SDBQNDZo\nSitheGhXNGl5QW9HTGNsTTdTdWVwaE0KfuM5xZOPbR1lxkJPJUOVrtlW5Ujobemp\nid40reDqxKQxP3khJv8lf9ZoN8LAx2Iwh0DeAl+UJpsoVWgS7uI2qQ==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2026-07-07T04:54:10Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:Tt8yqzfAnSDe2n7KDt1Z/D73hkS1w7DsqIh+s5WljLzar9PEt5vUCLb8DIvBwITyzGAoHH1ym3v3NrnJWCdxajKExTMBf4aY3MJCtykeCG37701S/KxumjTW9/fJw74gcET5I6NafTWb6H5XHsrRaQX1Xnspcs8B7uLGZZ28dSM=,iv:Fo5UPVhnjuOx6Cp4grofHOiD2pm0TXuRRuoTwvxKn1s=,tag:VQ/TF1TiyJADj7/3RwPCAg==,type:str]",
|
|
||||||
"version": "3.13.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../users/admin
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
|
||||||
"type": "age"
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
26.11
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
200:e6cc:c86:f02d:b0d5:fba9:9b2c:eb77
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
../../../../../../sops/machines/peer1
|
|
||||||
-18
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:6YDp5S2TzEnhDgGhcz8cA3uBsfDt0Oc4JpztB1JUcg/+tDBbeaQN4MLxuZKstXn2Hb1mYtBLlX0vHw++T181Js2sIznyfC1lr68GOvQAp4NYgf23dXpdZk/CMPNUMiGOYVwKWQ/Z0/p0azJ6xV29dYQcM4Q9hys=,iv:Ijzz8hm0looCWoSpjg/rYKp9jhvzRuLboKWHrlwVeFA=,tag:kJcH/1Pu+9q9f2J68LETug==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYNDNEL3kvdEpEMjYyOFJG\nMk5adU04M3hxa21jTTZNM2dQREpmUlVES1NvCjh6eDNFZ1hzUVlTY3Nidnp3TkYr\nRVl4TFlrNUE3dVIzZzg2VWMwTFBYUXMKLS0tIFE0dVFQQmxwOFJ6YkdaQlh6dUlW\nTi8rdXdQbGR1eXpLcTdOandqQmxka3cKImbmJQo2YMYMSQD8kNTgol3VJ4YGIaoz\nl3AY4u/fobg5qlzVQwV33G0/Wf72Z2aTjSLnP4thnasQ89PdYvs7hA==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1f92zl8y70z5w9gn0l5leg2fhyvxy6m93dfh7n6ltgvuu9nyg6epsrw6t8x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiaWw5SU9KWmJnVVVmTVpr\nL1V6RnRYUGg2UnhXR1BnVTh5eFhnNFpidGdjCmdMeU1nZkZmTTVwbkNaVElFa0Z4\nVkdGdjc4c2lPWGNrVGxocE9iQUZ3QTAKLS0tIHIzTzRRbXVkM3I4ZVRxeSs5eHV5\nTUxOaytzb2MxQ2UybkVCYWVWVmVEcUEKPeK+CC7r3CP5BJqg9thWNHWjPBp/ueHU\nRif6T79kqf+c3B34qXwR0zfyM1+a0r9GAVVC2/q+Ha/As5sPJU39ww==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2026-07-03T10:46:56Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:PffiPU1GR9DojTb7TxzBkpivKV2ybmmq9h4pOOyq7p5BAIPF+JSe+BDtbItQ+Gn5LnitQRUVvhat8E2iYfz3JgNxNKcsNFU/mWtCz3PlqyiuRzUBtEL3zYfhdMi8hYYlVdfMumaK3VMk+b4uDGExR+MpURL97TFku47qRbzbILs=,iv:3//fj7WtIX2QNYB9ub1JKAFIn5vJeuZl6rqaEsX0oQc=,tag:njU+WAVq7xBgE0sna2dMKA==,type:str]",
|
|
||||||
"version": "3.13.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
../../../../../../sops/users/admin
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
8c99f9bc87e92795022b32698a440d6f9fe01d1110f867de8176bb190b92acab
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
26.11
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
200:eb95:7d1b:3ac3:62e7:24af:2274:3771
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
../../../../../../sops/machines/peer2
|
|
||||||
-18
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:vOwMqT12A5ap5hRvCGTjTUX4sXKO3N7KE8eQ+/SCcBWndU9/b2AehiNgplWWMGcYdmeDL9qI8V2pPkgBkQ/tWH6N8DccSM/zQzrBUkXvRApl6hvNZAWuBVf/P4MW4/EbRGtMg2tEXkaPDtRn2TsDK7ygmXmqArI=,iv:FkxFx/ClAftLu85S6Uq3ZOom+YDOpeWGlYLOFBmFfnk=,tag:zprw62qiu+hmLZEZbnqYXA==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0WVB1M2ttd0FocGhTSjNo\nUDFIcmpsL051TndWbkJVakJHbUxjY3Q3NjM0CnkrbFlHS1E4bkZJQTIvMDJ3VDFv\nNW1OdmwvalBEelN2d3YwbHVqTW94OFEKLS0tIGtpOWxCM29xdXhQL2Yzb094OG1n\nT21mc25yU1dJL29vekRHdldzdDR3dEkKoZ1dPRaPg8iVsuec6IhV3cLPchokV2wv\nu6qb92hsLQuyPHDA01Oey0jxmaYMQGtAVjo2zEM+E+zrKY+f2pHIUQ==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArcmo5d2x6c3J0YTkwMGJE\nanphd2FiUjdGdmdNeW0rL0FpcTVSM3dublZ3CnphajNVV2YrTGl2a3ZqMTJKeU4v\nTzZRS2F5MlA5Ny84cFFoNWZQZnpBZTgKLS0tIHVKQUJWcmlPdy9GNXNBYnpJcEVC\nNmJVMlpvSzZPdk1JY2hIZVEvc3cwV2MKE/cMJU/j5g62njJ49AfnLutmxkpz/gxX\nIqWe1m7Uxl/awVFa7F8N7TUkd0jLbAGXZaonDjiBxOTwg/jr/apnuQ==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1u36fr87cru90vfznf5szasya9nhvvseh4atk8zsdj0rrsu0nnvwqsyt8g6"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2026-07-03T10:47:04Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:wiIkwEDwGJZe0u8qLHthPkCXubeIGyHV8yBo8CsSengLVTOtRZ877zTrFvE74ATF1pGE3DFJ5qGRrOGgKJ4VOl043LbEBs0bxZC495pPMPYszimp2UAf/XE3QWlNi9p4Ce/kbvUDd0W3SRt4hIv6HOLWApiRoqfS2qSMUnBQeW0=,iv:Hs7EysUnje/iWZRhJLlqf8wuFJnIMpWfXWHrilfmM5I=,tag:Rgj9x4ZOM2fihKKNQ4ux3g==,type:str]",
|
|
||||||
"version": "3.13.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
../../../../../../sops/users/admin
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
8a354172629e4e8c6da86ec5e447411e4238cf7bb439b798fae56509a82d7472
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
26.11
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
200:3dcd:9b0d:103c:f953:1e8:e3a2:ed90
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
../../../../../../sops/machines/peer3
|
|
||||||
-18
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:7no9ZnfatNLEykz+JOUExxmaRzBr7B5PEcevETySJWHTCCjSULdRb2KLxbXP5OKWSuJbR8pJ+p+nZuyr7aOTyXrU+fgwf/Pff4KGaZbKf1t3HJun8wJ2q62uaFYu8ACbNFfwIzJ51SPGcO7IHxfjWn5k9zG7av4=,iv:+VNvFKAJ+HDCGZciug8SKHOcM1JZQUAbZpRT5en4xcU=,tag:3kcDHWIs9ITzTKDiyyYhnQ==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvZ0VFcGhPR0NrMGU4dStU\nZGpnMHZmbUF0R0NFNWQzNGVpZXhYQ24xNXk0CnJIYmJYMnBmeURLZytBcXNRNG5u\nZzk4cXdPdnJIU0JVam1NWFA3TkhuWFkKLS0tIDZYUEVGMUtCQWdSM3k4WEZhdUt3\naElkc3o5SXlFYTJLNUpGZ0ptNm96M2MKYKW5emktK2M+IYX0v/swCV1fgrQXQVlb\nmz11rN5fJWV5wvLcTtyU9imgZLB9MfRMyIYlHzD2HPbW+zr8Fe+bBQ==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZVDVxY3JrWk1TY3ZsazNz\naVl4Vk1vRUdkRTdlaDU1TE15dC96ZHlxQ1ZBCmYvSmh1QlBNRnJyRy9kcDR0cXhY\neDdPUXZ6b2FkWnBXbzA4bzU1aFo3bEUKLS0tIHMrb05Oa3FrRzh5VHN1RWRmUU9W\ndGZUYTVVQkIzM3FRRHp6NXlEdEU0VTgK7ibTaEcvbVTQjcbgeylu2s5tmI5kAadM\nFGiOgJhDFuzbHxXIEQlsCBIaXe6LlGhBo41VZbMxme5py6nJFTBHKg==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1rka3368ltqc787xw99uu762lqfk7qxld9265u2t62fpxnp9xhufsc2q5rw"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2026-07-07T04:54:10Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:gonhuGY61G3Gdtj7q23aTNYU95fjLMz2uVTsSX+Hw10LGUTEMM8LF/Phae9kr5BZKD8BYaIgH32As2+n0B9jZ4WV77A5hUe2NJze9d0A40P2MtMh9xFWWh7yEXXnx5dy1UDrthL2LBa7ebPspuaC0mFhRHdoJK3J2W1m41PGjZc=,iv:H4yNuE8i3UxVjAUIpnaFX6i8/DaO4uIlHrEY9VaSiRs=,tag:9lJtixSNv7tpox0V3ZoNnA==,type:str]",
|
|
||||||
"version": "3.13.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
../../../../../../sops/users/admin
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
e119327977e183567f0b8e2e8937c9d04708e296beed57792a1b6935eacf69d9
|
|
||||||
@@ -19,6 +19,12 @@
|
|||||||
description = "Public URL for accessing the instance";
|
description = "Public URL for accessing the instance";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
base_domain = lib.mkOption {
|
||||||
|
type = with lib.types; str;
|
||||||
|
default = "";
|
||||||
|
description = "Defines the base domain to create the hostnames for MagicDNS in Headscale. `base_domain` must be a FQDN, without the trailing dot. The FQDN of the hosts will be `hostname.base_domain (e.g. myhost.tailnet.example.com)";
|
||||||
|
};
|
||||||
|
|
||||||
advertise_routes = lib.mkOption {
|
advertise_routes = lib.mkOption {
|
||||||
type = with lib.types; listOf str;
|
type = with lib.types; listOf str;
|
||||||
default = [ ];
|
default = [ ];
|
||||||
@@ -26,13 +32,6 @@
|
|||||||
example = [ "192.168.1.0/24" ];
|
example = [ "192.168.1.0/24" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
dns = {
|
|
||||||
base_domain = lib.mkOption {
|
|
||||||
type = with lib.types; str;
|
|
||||||
default = "";
|
|
||||||
description = "Defines the base domain to create the hostnames for MagicDNS in Headscale. `base_domain` must be a FQDN, without the trailing dot. The FQDN of the hosts will be `hostname.base_domain (e.g. myhost.tailnet.example.com)";
|
|
||||||
};
|
|
||||||
|
|
||||||
nameservers = lib.mkOption {
|
nameservers = lib.mkOption {
|
||||||
type = with lib.types; listOf str;
|
type = with lib.types; listOf str;
|
||||||
default = [
|
default = [
|
||||||
@@ -42,42 +41,6 @@
|
|||||||
description = "List of nameservers to pass to Tailscale clients";
|
description = "List of nameservers to pass to Tailscale clients";
|
||||||
example = [ "10.0.10.1" ];
|
example = [ "10.0.10.1" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
magic_dns = lib.mkOption {
|
|
||||||
type = with lib.types; bool;
|
|
||||||
default = true;
|
|
||||||
description = "Whether to enable MagicDNS";
|
|
||||||
};
|
|
||||||
|
|
||||||
extra_records = lib.mkOption {
|
|
||||||
type =
|
|
||||||
with lib.types;
|
|
||||||
nullOr (
|
|
||||||
listOf (submodule {
|
|
||||||
options = {
|
|
||||||
name = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "DNS record name.";
|
|
||||||
example = "grafana.tailnet.example.com";
|
|
||||||
};
|
|
||||||
type = lib.mkOption {
|
|
||||||
type = lib.types.enum [
|
|
||||||
"A"
|
|
||||||
"AAAA"
|
|
||||||
];
|
|
||||||
description = "DNS record type.";
|
|
||||||
example = "A";
|
|
||||||
};
|
|
||||||
value = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "DNS record value (IP address).";
|
|
||||||
example = "100.64.0.3";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -207,11 +170,9 @@
|
|||||||
settings.server_url = "https://${settings.public_url}";
|
settings.server_url = "https://${settings.public_url}";
|
||||||
|
|
||||||
settings.dns = {
|
settings.dns = {
|
||||||
base_domain = settings.dns.base_domain;
|
base_domain = settings.base_domain;
|
||||||
override_local_dns = true;
|
override_local_dns = true;
|
||||||
nameservers.global = settings.dns.nameservers;
|
nameservers.global = settings.nameservers;
|
||||||
magic_dns = settings.dns.magic_dns;
|
|
||||||
extra_records = settings.dns.extra_records;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,8 @@
|
|||||||
"AutofillAddressEnabled" = false;
|
"AutofillAddressEnabled" = false;
|
||||||
"AutofillCreditCardEnabled" = false;
|
"AutofillCreditCardEnabled" = false;
|
||||||
"TranslateEnabled" = false;
|
"TranslateEnabled" = false;
|
||||||
|
"DnsOverHttpsMode" = "secure";
|
||||||
|
"DnsOverHttpsTemplates" = "https://dns.adguard-dns.com/dns-query";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,32 +81,14 @@
|
|||||||
inputs,
|
inputs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
|
||||||
dictionaries =
|
|
||||||
with pkgs;
|
|
||||||
(hunspellWithDicts (
|
|
||||||
with hunspellDicts;
|
|
||||||
[
|
|
||||||
en-us-large
|
|
||||||
th-th
|
|
||||||
]
|
|
||||||
));
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
imports = [ inputs.plasma-manager.homeModules.plasma-manager ];
|
imports = [ inputs.plasma-manager.homeModules.plasma-manager ];
|
||||||
|
|
||||||
systemd.user.sessionVariables = {
|
|
||||||
DICPATH = "${dictionaries}/share/hunspell";
|
|
||||||
};
|
|
||||||
|
|
||||||
home = {
|
home = {
|
||||||
homeDirectory = lib.mkForce "/home/${username}";
|
homeDirectory = lib.mkForce "/home/${username}";
|
||||||
stateVersion = osConfig.system.stateVersion;
|
stateVersion = osConfig.system.stateVersion;
|
||||||
|
|
||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
libreoffice-qt6
|
libreoffice-fresh
|
||||||
dictionaries
|
|
||||||
|
|
||||||
element-desktop
|
element-desktop
|
||||||
signal-desktop
|
signal-desktop
|
||||||
brave
|
brave
|
||||||
@@ -116,11 +100,6 @@
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
programs.chromium.package = pkgs.brave;
|
programs.chromium.package = pkgs.brave;
|
||||||
|
|
||||||
programs.firefox = {
|
|
||||||
enable = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.home-manager.enable = true;
|
programs.home-manager.enable = true;
|
||||||
services.syncthing.tray.enable = osConfig.services.syncthing.enable;
|
services.syncthing.tray.enable = osConfig.services.syncthing.enable;
|
||||||
programs.plasma.enable = true;
|
programs.plasma.enable = true;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
homeDirectory = lib.mkForce "/home/${username}";
|
homeDirectory = lib.mkForce "/home/${username}";
|
||||||
stateVersion = osConfig.system.stateVersion;
|
stateVersion = osConfig.system.stateVersion;
|
||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
libreoffice-stable
|
libreoffice-fresh
|
||||||
element-desktop
|
element-desktop
|
||||||
signal-desktop
|
signal-desktop
|
||||||
brave
|
brave
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
boot.supportedFilesystems = {
|
|
||||||
ntfs = true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -5,4 +5,5 @@
|
|||||||
|
|
||||||
services.displayManager.sddm.enable = lib.mkForce false;
|
services.displayManager.sddm.enable = lib.mkForce false;
|
||||||
services.displayManager.gdm.enable = true;
|
services.displayManager.gdm.enable = true;
|
||||||
|
services.displayManager.gdm.wayland = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
A peer to peer phone relay network built on top of yggdrasil.
|
|
||||||
|
|
||||||
Successor of the `phonebox` service with a global, decentralized number
|
|
||||||
directory. Every box is reachable from any yggdrasil node running this
|
|
||||||
service, not only from members of the same clan. A machine runs either
|
|
||||||
`phonebox` or `phonebox-global`, not both (they share the asterisk setup).
|
|
||||||
Regenerate vars with `clan vars generate --generator phonebox-global`.
|
|
||||||
|
|
||||||
## Numbers
|
|
||||||
|
|
||||||
Each box gets a random 6 digit number (100000-999999) when its vars are
|
|
||||||
generated. The number is signed with the box's yggdrasil private key, so a
|
|
||||||
number is cryptographically bound to the yggdrasil address it belongs to;
|
|
||||||
nobody can claim a number for an address they do not own. Regenerate the
|
|
||||||
`phonebox` vars to get a new number.
|
|
||||||
|
|
||||||
Dialing from a phone plugged into the ATA:
|
|
||||||
|
|
||||||
| dialed | reaches |
|
|
||||||
| ------------- | ------------------------------------------ |
|
|
||||||
| `NNNNNN` | the phone on box `NNNNNN` (line `00`) |
|
|
||||||
| `NNNNNNXX` | line `XX` on box `NNNNNN` |
|
|
||||||
| `00`, `01`... | local lines on this box |
|
|
||||||
| `888` | fax of the current number directory |
|
|
||||||
| `000` | fax echo test |
|
|
||||||
| `999` | hello world |
|
|
||||||
|
|
||||||
Caller ID on the callee is the caller's box number followed by its line, so
|
|
||||||
calling it back works.
|
|
||||||
|
|
||||||
## Directory
|
|
||||||
|
|
||||||
There is no central registry. Each box runs a serf agent on yggdrasil port
|
|
||||||
7946 that gossips its signed number record (`number`, `key`, `sig`, `owner`
|
|
||||||
tags) to every other box it knows about. On every membership change the
|
|
||||||
`phonebox-sync` handler verifies all records and writes the resulting
|
|
||||||
`number -> yggdrasil address` map to `/run/phonebox/numbers/`, which the
|
|
||||||
asterisk dialplan reads at call time.
|
|
||||||
|
|
||||||
Bootstrapping: boxes of the same clan join each other automatically. To
|
|
||||||
connect to boxes outside the clan add one of their yggdrasil addresses to
|
|
||||||
`extraPeers`; after the first successful join the agent remembers the whole
|
|
||||||
membership in `/var/lib/phonebox/serf.snapshot` and rejoins on its own.
|
|
||||||
|
|
||||||
`serf members -rpc-addr 127.0.0.1:7373` shows the live directory.
|
|
||||||
@@ -1,583 +0,0 @@
|
|||||||
{
|
|
||||||
clanLib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
_class = "clan.service";
|
|
||||||
manifest.name = "phonebox-global";
|
|
||||||
manifest.description = "A peer to peer phone relay network built on top of yggdrasil.";
|
|
||||||
manifest.readme = builtins.readFile ./README.md;
|
|
||||||
manifest.categories = [ "System" ];
|
|
||||||
|
|
||||||
roles.default = {
|
|
||||||
description = "a default server role";
|
|
||||||
interface =
|
|
||||||
{ lib, ... }:
|
|
||||||
{
|
|
||||||
options.ata-ethernet-iface = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "An Ethernet interface that connect to ATA box.";
|
|
||||||
default = "enp2s0";
|
|
||||||
};
|
|
||||||
options.extraClientNumbers = lib.mkOption {
|
|
||||||
type = with lib.types; listOf str;
|
|
||||||
description = "List of client suffix number.";
|
|
||||||
default = [ ];
|
|
||||||
};
|
|
||||||
|
|
||||||
options.extraFixedIPClient = lib.mkOption {
|
|
||||||
type = lib.types.attrsOf (
|
|
||||||
lib.types.submodule {
|
|
||||||
options = {
|
|
||||||
ip = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "IP address for this client";
|
|
||||||
};
|
|
||||||
|
|
||||||
name = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "Name of the client";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
description = "Extra client to be added to pjsip config as a fixed IP auth";
|
|
||||||
default = { };
|
|
||||||
example = {
|
|
||||||
"01" = {
|
|
||||||
ip = "192.168.1.3";
|
|
||||||
name = "bob";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
options.extraPeers = lib.mkOption {
|
|
||||||
type = with lib.types; listOf str;
|
|
||||||
description = ''
|
|
||||||
Yggdrasil addresses of phonebox nodes outside this clan to bootstrap
|
|
||||||
the number directory from. Any single reachable node is enough; the
|
|
||||||
directory is gossiped from there and remembered across restarts.
|
|
||||||
'';
|
|
||||||
default = [ ];
|
|
||||||
example = [ "200:1234:5678:9abc:def0:1234:5678:9abc" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
perInstance =
|
|
||||||
{
|
|
||||||
roles,
|
|
||||||
settings,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
|
|
||||||
nixosModule =
|
|
||||||
{
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
asterisk = pkgs.asterisk.overrideAttrs (old: {
|
|
||||||
propagatedNativeBuildInputs = [ pkgs.spandsp3 ];
|
|
||||||
});
|
|
||||||
|
|
||||||
machineName = config.clan.core.settings.machine.name;
|
|
||||||
machines = lib.attrNames roles.default.machines;
|
|
||||||
|
|
||||||
user = "asterisk";
|
|
||||||
faxDir = "/run/asterisk/fax";
|
|
||||||
rtpPortFrom = 10000;
|
|
||||||
rtpPortTo = 20000;
|
|
||||||
ata-interface = settings.ata-ethernet-iface;
|
|
||||||
|
|
||||||
# The ATA's own line. Remote callers reach it by dialing the 6
|
|
||||||
# digit box number alone or with this extension appended.
|
|
||||||
ataLine = "00";
|
|
||||||
sipPort = 5060;
|
|
||||||
|
|
||||||
# Well-known port of the phonebox directory gossip. Every phonebox
|
|
||||||
# node worldwide must agree on it, so it is not configurable.
|
|
||||||
directoryPort = 7946;
|
|
||||||
directoryRpc = "127.0.0.1:7373";
|
|
||||||
directoryUser = "phonebox";
|
|
||||||
directoryDir = "/run/phonebox";
|
|
||||||
|
|
||||||
phoneboxVars = config.clan.core.vars.generators.phonebox-global.files;
|
|
||||||
ownNumber = phoneboxVars.number.value;
|
|
||||||
ownerName = lib.trim phoneboxVars.owner-name.value;
|
|
||||||
|
|
||||||
getYggdrasilIP =
|
|
||||||
name:
|
|
||||||
lib.trim (
|
|
||||||
clanLib.getPublicValue {
|
|
||||||
flake = config.clan.core.settings.directory;
|
|
||||||
machine = name;
|
|
||||||
generator = "yggdrasil";
|
|
||||||
file = "address";
|
|
||||||
default = "";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
ownYggdrasilIP =
|
|
||||||
if config.clan.core.vars.generators.yggdrasil.files ? address then
|
|
||||||
config.clan.core.vars.generators.yggdrasil.files.address.value
|
|
||||||
else
|
|
||||||
throw "clanService/yggdrasil is required";
|
|
||||||
|
|
||||||
seedPeers = lib.unique (
|
|
||||||
lib.filter (ip: ip != "") (map getYggdrasilIP (lib.remove machineName machines))
|
|
||||||
++ settings.extraPeers
|
|
||||||
);
|
|
||||||
|
|
||||||
# Rebuilds the number -> yggdrasil address directory from the
|
|
||||||
# gossip membership. Every record is verified: the yggdrasil
|
|
||||||
# address is derived from the record's public key, so a node can
|
|
||||||
# only publish numbers for the address it actually owns, and the
|
|
||||||
# number is signed with the matching private key.
|
|
||||||
phoneboxSync = pkgs.writers.writePython3Bin "phonebox-sync" {
|
|
||||||
libraries = [ pkgs.python3Packages.cryptography ];
|
|
||||||
flakeIgnore = [ "E501" ];
|
|
||||||
} (builtins.readFile ./phonebox-sync.py);
|
|
||||||
|
|
||||||
phoneboxSyncWrapped = pkgs.writeShellApplication {
|
|
||||||
name = "phonebox-sync";
|
|
||||||
runtimeInputs = [ pkgs.serfdom ];
|
|
||||||
text = ''
|
|
||||||
exec ${lib.getExe phoneboxSync} ${directoryRpc} ${directoryDir}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
createContactListTiff = pkgs.writeShellApplication {
|
|
||||||
name = "create-contact-tiff";
|
|
||||||
text = ''
|
|
||||||
magick -background white -fill black -pointsize 20 -font DejaVu-Sans label:"$(cat ${directoryDir}/contacts.txt)" "$1"
|
|
||||||
magick "$1" -border 20x50 -bordercolor white "$1"
|
|
||||||
magick "$1" -resize 1728x -units PixelsPerInch -compress Group4 -density 204x196 -monochrome -depth 1 "$1"
|
|
||||||
'';
|
|
||||||
runtimeInputs = [ pkgs.imagemagick ];
|
|
||||||
};
|
|
||||||
|
|
||||||
genLocalSIPEndpoint =
|
|
||||||
{ localNumber }:
|
|
||||||
''
|
|
||||||
[${localNumber}](internal_endpoint)
|
|
||||||
aors=${localNumber}
|
|
||||||
auth=${localNumber}
|
|
||||||
|
|
||||||
[${localNumber}](userpass_auth)
|
|
||||||
username=${localNumber}
|
|
||||||
password=${localNumber}
|
|
||||||
|
|
||||||
[${localNumber}](dynamic_aor)
|
|
||||||
max_contacts=1
|
|
||||||
remove_existing=yes
|
|
||||||
'';
|
|
||||||
|
|
||||||
genLocalSIPEndpointV6 =
|
|
||||||
{ localNumber }:
|
|
||||||
''
|
|
||||||
[${localNumber}](internal_endpoint)
|
|
||||||
transport=transport-udp6
|
|
||||||
aors=${localNumber}
|
|
||||||
auth=${localNumber}
|
|
||||||
|
|
||||||
[${localNumber}](userpass_auth)
|
|
||||||
username=${localNumber}
|
|
||||||
password=${localNumber}
|
|
||||||
|
|
||||||
[${localNumber}](dynamic_aor)
|
|
||||||
max_contacts=1
|
|
||||||
'';
|
|
||||||
|
|
||||||
genLocalSIPIPEndpoint = number: ''
|
|
||||||
|
|
||||||
[${number}](internal_endpoint)
|
|
||||||
aors=${number}
|
|
||||||
auth=${number}
|
|
||||||
contact_deny=0.0.0.0/0
|
|
||||||
contact_deny=::/0
|
|
||||||
contact_permit=${settings.extraFixedIPClient.${number}.ip}/128
|
|
||||||
|
|
||||||
[${number}](dynamic_aor)
|
|
||||||
max_contacts=1
|
|
||||||
remove_existing=yes
|
|
||||||
|
|
||||||
[${number}](userpass_auth)
|
|
||||||
username=${number}
|
|
||||||
password=${number}
|
|
||||||
|
|
||||||
'';
|
|
||||||
|
|
||||||
genLocalExtenConf =
|
|
||||||
{ localNumber }:
|
|
||||||
''
|
|
||||||
exten => ${localNumber},1,Dial(PJSIP/${localNumber},20)
|
|
||||||
'';
|
|
||||||
|
|
||||||
localNumbers = [
|
|
||||||
ataLine
|
|
||||||
]
|
|
||||||
++ settings.extraClientNumbers
|
|
||||||
++ lib.attrNames settings.extraFixedIPClient;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
assertions = [
|
|
||||||
{
|
|
||||||
assertion = !config.networking.nftables.enable;
|
|
||||||
message = "phonebox-global: opening the yggdrasil interface to non-clan nodes is only implemented for the iptables firewall backend";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
clan.core.vars.generators.phonebox-global = {
|
|
||||||
files = {
|
|
||||||
number.secret = false;
|
|
||||||
public-key.secret = false;
|
|
||||||
signature.secret = false;
|
|
||||||
owner-name.secret = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
dependencies = [ "yggdrasil" ];
|
|
||||||
|
|
||||||
prompts.owner-name = {
|
|
||||||
persist = true;
|
|
||||||
type = "line";
|
|
||||||
description = "The owner's name for this unit";
|
|
||||||
};
|
|
||||||
|
|
||||||
runtimeInputs = with pkgs; [
|
|
||||||
coreutils
|
|
||||||
openssl
|
|
||||||
xxd
|
|
||||||
];
|
|
||||||
|
|
||||||
# A random 6 digit number (100000-999999), bound to this
|
|
||||||
# machine's yggdrasil identity by signing it with the yggdrasil
|
|
||||||
# private key. Other nodes accept the number only if the
|
|
||||||
# signature checks out and the key derives to the yggdrasil
|
|
||||||
# address the record is gossiped from. Regenerate to re-roll.
|
|
||||||
script = ''
|
|
||||||
cat $prompts/owner-name > $out/owner-name
|
|
||||||
shuf -i 100000-999999 -n 1 | tr -d '\n' > $out/number
|
|
||||||
openssl pkey -in $in/yggdrasil/privateKey -pubout -outform DER \
|
|
||||||
| tail -c 32 | xxd -p -c 64 | tr -d '\n' > $out/public-key
|
|
||||||
# openssl needs a regular file for one-shot ed25519 signing
|
|
||||||
printf 'phonebox:%s' "$(cat $out/number)" > $out/message
|
|
||||||
openssl pkeyutl -sign -rawin -inkey $in/yggdrasil/privateKey -in $out/message \
|
|
||||||
| base64 -w0 > $out/signature
|
|
||||||
rm $out/message
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.interfaces = {
|
|
||||||
${ata-interface} = {
|
|
||||||
useDHCP = false;
|
|
||||||
ipv4.addresses = [
|
|
||||||
{
|
|
||||||
address = "192.168.254.1";
|
|
||||||
prefixLength = 24;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.dnsmasq = {
|
|
||||||
enable = true;
|
|
||||||
|
|
||||||
settings = {
|
|
||||||
bind-dynamic = true;
|
|
||||||
listen-address = "192.168.254.1";
|
|
||||||
# enable-ra = true;
|
|
||||||
domain-needed = true;
|
|
||||||
domain = "localhost";
|
|
||||||
dhcp-range = [
|
|
||||||
"192.168.254.100,192.168.254.100,255.255.255.0,3m"
|
|
||||||
];
|
|
||||||
dhcp-leasefile = "/dev/null";
|
|
||||||
dhcp-option = [
|
|
||||||
"3,192.168.254.1"
|
|
||||||
];
|
|
||||||
interface = [ ata-interface ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.nginx = {
|
|
||||||
enable = true;
|
|
||||||
virtualHosts = {
|
|
||||||
"_" = {
|
|
||||||
locations."/" = {
|
|
||||||
proxyPass = "http://192.168.254.100";
|
|
||||||
extraConfig = ''
|
|
||||||
client_max_body_size 100M;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.firewall.allowedUDPPortRanges = [
|
|
||||||
{
|
|
||||||
from = rtpPortFrom;
|
|
||||||
to = rtpPortTo;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
networking.firewall.allowedUDPPorts = [
|
|
||||||
53
|
|
||||||
67
|
|
||||||
sipPort
|
|
||||||
];
|
|
||||||
networking.firewall.allowedTCPPorts = [
|
|
||||||
53
|
|
||||||
];
|
|
||||||
networking.firewall.interfaces = {
|
|
||||||
"zt+".allowedTCPPorts = [ 80 ];
|
|
||||||
ygg = {
|
|
||||||
allowedTCPPorts = [ directoryPort ];
|
|
||||||
allowedUDPPorts = [ directoryPort ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# clanService/yggdrasil drops everything on the ygg interface that
|
|
||||||
# does not come from a clan member. Phonebox is a global network,
|
|
||||||
# so let SIP, RTP and the directory gossip through from anyone.
|
|
||||||
networking.firewall.extraCommands = lib.mkAfter ''
|
|
||||||
if ip6tables -n -L ygg-input >/dev/null 2>&1; then
|
|
||||||
ip6tables -I ygg-input 1 -i ygg -p udp --dport ${toString sipPort} -j RETURN
|
|
||||||
ip6tables -I ygg-input 1 -i ygg -p udp --dport ${toString rtpPortFrom}:${toString rtpPortTo} -j RETURN
|
|
||||||
ip6tables -I ygg-input 1 -i ygg -p udp --dport ${toString directoryPort} -j RETURN
|
|
||||||
ip6tables -I ygg-input 1 -i ygg -p tcp --dport ${toString directoryPort} -j RETURN
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
|
|
||||||
users.users.${directoryUser} = {
|
|
||||||
isSystemUser = true;
|
|
||||||
group = directoryUser;
|
|
||||||
};
|
|
||||||
users.groups.${directoryUser} = { };
|
|
||||||
|
|
||||||
# Decentralized number directory: a serf gossip cluster over
|
|
||||||
# yggdrasil. Each node advertises its signed number record as
|
|
||||||
# tags; membership changes trigger phonebox-sync, which writes the
|
|
||||||
# verified number -> address map to ${directoryDir}/numbers/ for
|
|
||||||
# the dialplan. The snapshot lets a node rejoin from previously
|
|
||||||
# seen members, so seeds are only needed for the very first join.
|
|
||||||
systemd.services.phonebox-directory = {
|
|
||||||
description = "Phonebox number directory (serf gossip over yggdrasil)";
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
wants = [ "network-online.target" ];
|
|
||||||
after = [
|
|
||||||
"network-online.target"
|
|
||||||
"yggdrasil.service"
|
|
||||||
];
|
|
||||||
serviceConfig = {
|
|
||||||
User = directoryUser;
|
|
||||||
Group = directoryUser;
|
|
||||||
RuntimeDirectory = "phonebox";
|
|
||||||
RuntimeDirectoryMode = "0755";
|
|
||||||
RuntimeDirectoryPreserve = "yes";
|
|
||||||
StateDirectory = "phonebox";
|
|
||||||
Restart = "always";
|
|
||||||
RestartSec = 5;
|
|
||||||
ExecStart = lib.concatStringsSep " " (
|
|
||||||
[
|
|
||||||
(lib.getExe pkgs.serfdom)
|
|
||||||
"agent"
|
|
||||||
"-node=${machineName}-${ownNumber}"
|
|
||||||
"-bind=[::]:${toString directoryPort}"
|
|
||||||
"-advertise=[${ownYggdrasilIP}]:${toString directoryPort}"
|
|
||||||
"-rpc-addr=${directoryRpc}"
|
|
||||||
"-profile=wan"
|
|
||||||
"-snapshot=/var/lib/phonebox/serf.snapshot"
|
|
||||||
"-rejoin"
|
|
||||||
"-retry-max=0"
|
|
||||||
"-retry-interval=30s"
|
|
||||||
"-tag number=${ownNumber}"
|
|
||||||
"-tag key=${phoneboxVars.public-key.value}"
|
|
||||||
"-tag sig=${phoneboxVars.signature.value}"
|
|
||||||
"-tag ${lib.escapeShellArg "owner=${ownerName}"}"
|
|
||||||
"-event-handler=member-join,member-leave,member-failed,member-update,member-reap=${lib.getExe phoneboxSyncWrapped}"
|
|
||||||
]
|
|
||||||
++ map (ip: "-retry-join=[${ip}]:${toString directoryPort}") seedPeers
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.asterisk = {
|
|
||||||
enable = lib.mkDefault true;
|
|
||||||
package = lib.mkDefault asterisk;
|
|
||||||
confFiles = {
|
|
||||||
"logger.conf" = ''
|
|
||||||
[general]
|
|
||||||
dateformat = %F %T.%3q ; ISO 8601 date format with milliseconds
|
|
||||||
use_callids = yes
|
|
||||||
appendhostname = no
|
|
||||||
queue_log = yes
|
|
||||||
queue_log_to_file = no
|
|
||||||
queue_log_name = queue_log
|
|
||||||
queue_log_realtime_use_gmt = no
|
|
||||||
rotatestrategy = rotate
|
|
||||||
exec_after_rotate=gzip -9 $\{filename\}.2
|
|
||||||
[logfiles]
|
|
||||||
console => notice,warning,error
|
|
||||||
security => security
|
|
||||||
messages => notice,warning,error
|
|
||||||
full => notice,warning,error,verbose,dtmf,fax
|
|
||||||
syslog.local0 => notice,warning,error
|
|
||||||
'';
|
|
||||||
|
|
||||||
"modules.conf" = ''
|
|
||||||
[modules]
|
|
||||||
autoload=yes
|
|
||||||
|
|
||||||
load => res_fax_spandsp.so
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Dial plan config
|
|
||||||
"extensions.conf" = ''
|
|
||||||
[from-internal]
|
|
||||||
exten => 999,1,Answer()
|
|
||||||
same => n,Playback(hello-world)
|
|
||||||
same => n,Hangup()
|
|
||||||
|
|
||||||
exten => 000,1,Answer()
|
|
||||||
same => n,ReceiveFAX(${faxDir}/echo-''${UNIQUEID}.tiff)
|
|
||||||
same => n,Set(FAXFILE=${faxDir}/echo-''${UNIQUEID}.tiff)
|
|
||||||
same => n,Set(FAXECHO=true)
|
|
||||||
|
|
||||||
exten => 888,1,Answer()
|
|
||||||
same => n,Set(FAXFILE=${faxDir}/contact.tiff)
|
|
||||||
same => n,System(${lib.getExe createContactListTiff} ''${FAXFILE})
|
|
||||||
same => n,Set(FAXECHO=true)
|
|
||||||
same => n,Playback(vm-goodbye)
|
|
||||||
same => n,Wait(3)
|
|
||||||
|
|
||||||
exten => h,1,GotoIf($[''${FAXECHO}]?sendfax)
|
|
||||||
same => n,Hangup()
|
|
||||||
same => n(sendfax),Originate(PJSIP/${ataLine},app,SendFAX,''${FAXFILE})
|
|
||||||
same => n,Set(FAXECHO=false)
|
|
||||||
|
|
||||||
; 6 digit box number, optionally followed by a 2 digit line
|
|
||||||
exten => _[1-9]XXXXX,1,Goto(to-yggdrasil,''${EXTEN},1)
|
|
||||||
exten => _[1-9]XXXXXXX,1,Goto(to-yggdrasil,''${EXTEN},1)
|
|
||||||
|
|
||||||
''
|
|
||||||
+ lib.concatMapStrings (number: genLocalExtenConf { localNumber = number; }) localNumbers
|
|
||||||
+ ''
|
|
||||||
|
|
||||||
; Outbound: resolve the box number through the directory and
|
|
||||||
; send the call to that node over yggdrasil.
|
|
||||||
[to-yggdrasil]
|
|
||||||
exten => _[1-9]X.,1,Set(PEER=''${FILE(${directoryDir}/numbers/''${EXTEN:0:6})})
|
|
||||||
same => n,GotoIf($["''${PEER}" = ""]?unknown)
|
|
||||||
same => n,Set(CALLERID(num)=${ownNumber}''${CALLERID(num)})
|
|
||||||
same => n,Set(CALLERID(name)=${ownerName})
|
|
||||||
same => n,Dial(PJSIP/yggdrasil/sip:''${EXTEN}@[''${PEER}]:${toString sipPort},30)
|
|
||||||
same => n,Hangup()
|
|
||||||
same => n(unknown),Playback(ss-noservice)
|
|
||||||
same => n,Hangup()
|
|
||||||
|
|
||||||
; Inbound from any yggdrasil node: only our own number is served.
|
|
||||||
[from-yggdrasil]
|
|
||||||
exten => ${ownNumber},1,Dial(PJSIP/${ataLine},20)
|
|
||||||
exten => _${ownNumber}XX,1,Dial(PJSIP/''${EXTEN:6},20)
|
|
||||||
'';
|
|
||||||
|
|
||||||
"rtp.conf" = ''
|
|
||||||
[general]
|
|
||||||
rtpstart=${toString rtpPortFrom}
|
|
||||||
rtpend=${toString rtpPortTo}
|
|
||||||
'';
|
|
||||||
|
|
||||||
"pjsip.conf" = ''
|
|
||||||
[global]
|
|
||||||
type=global
|
|
||||||
; Local lines authenticate by username; everything else from
|
|
||||||
; the yggdrasil range is a remote phonebox node.
|
|
||||||
endpoint_identifier_order=username,ip,anonymous
|
|
||||||
|
|
||||||
[transport-udp]
|
|
||||||
type=transport
|
|
||||||
protocol=udp
|
|
||||||
bind=0.0.0.0
|
|
||||||
[transport-udp6]
|
|
||||||
type=transport
|
|
||||||
protocol=udp
|
|
||||||
bind=::
|
|
||||||
|
|
||||||
[base_endpoint](!)
|
|
||||||
type=endpoint
|
|
||||||
disallow=all
|
|
||||||
allow=ulaw,alaw,g722,gsm
|
|
||||||
direct_media=no
|
|
||||||
|
|
||||||
[internal_endpoint](!,base_endpoint)
|
|
||||||
context=from-internal
|
|
||||||
|
|
||||||
[userpass_auth](!)
|
|
||||||
type=auth
|
|
||||||
auth_type=userpass
|
|
||||||
|
|
||||||
[dynamic_aor](!)
|
|
||||||
type=aor
|
|
||||||
|
|
||||||
[yggdrasil](base_endpoint)
|
|
||||||
transport=transport-udp6
|
|
||||||
context=from-yggdrasil
|
|
||||||
|
|
||||||
[yggdrasil]
|
|
||||||
type=identify
|
|
||||||
endpoint=yggdrasil
|
|
||||||
match=200::/7
|
|
||||||
|
|
||||||
''
|
|
||||||
+ (genLocalSIPEndpoint { localNumber = ataLine; })
|
|
||||||
+ lib.concatMapStrings (
|
|
||||||
number: genLocalSIPEndpointV6 { localNumber = number; }
|
|
||||||
) settings.extraClientNumbers
|
|
||||||
+ lib.concatMapStrings genLocalSIPIPEndpoint (lib.attrNames settings.extraFixedIPClient);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
environment.systemPackages = [
|
|
||||||
createContactListTiff
|
|
||||||
phoneboxSyncWrapped
|
|
||||||
];
|
|
||||||
|
|
||||||
systemd.tmpfiles.rules = [
|
|
||||||
"d ${faxDir} 0755 ${user} ${user} - -"
|
|
||||||
];
|
|
||||||
|
|
||||||
systemd.services.asterisk-watcher = {
|
|
||||||
enable = true;
|
|
||||||
description = "Asterisk Configuration files watcher";
|
|
||||||
|
|
||||||
requires = [ "asterisk.service" ];
|
|
||||||
after = [ "network.target" ];
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
path = with pkgs; [
|
|
||||||
inotify-tools
|
|
||||||
asterisk
|
|
||||||
];
|
|
||||||
script = ''
|
|
||||||
inotifywait -m -e move /etc/asterisk |
|
|
||||||
while read path action file; do
|
|
||||||
case "$file" in
|
|
||||||
pjsip.conf)
|
|
||||||
echo "restarting pjsip"
|
|
||||||
asterisk -rx "pjsip reload"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
case "$file" in
|
|
||||||
extensions.conf)
|
|
||||||
echo "restarting core"
|
|
||||||
asterisk -rx "core restart now"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
inputs,
|
|
||||||
self,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
module = ./default.nix;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
clan.modules = {
|
|
||||||
phonebox-global = module;
|
|
||||||
};
|
|
||||||
perSystem =
|
|
||||||
{ pkgs, ... }:
|
|
||||||
{
|
|
||||||
clan.nixosTests.service-phonebox-global = {
|
|
||||||
imports = [ ./tests/vm/default.nix ];
|
|
||||||
_module.args = { inherit self inputs; };
|
|
||||||
|
|
||||||
clan.modules."@clan/phonebox-global" = module;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Unit tests for the directory sync logic (record verification,
|
|
||||||
# address derivation, collision handling, file output).
|
|
||||||
checks.phonebox-global-sync =
|
|
||||||
pkgs.runCommand "phonebox-global-sync-test"
|
|
||||||
{
|
|
||||||
nativeBuildInputs = [
|
|
||||||
(pkgs.python3.withPackages (ps: [ ps.cryptography ]))
|
|
||||||
pkgs.yggdrasil
|
|
||||||
];
|
|
||||||
PHONEBOX_SYNC = ./phonebox-sync.py;
|
|
||||||
}
|
|
||||||
''
|
|
||||||
python3 ${./tests/sync/test_sync.py} -v
|
|
||||||
touch $out
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
"""Rebuild the phonebox number directory from serf membership.
|
|
||||||
|
|
||||||
Usage: phonebox-sync <serf rpc addr> <state dir>
|
|
||||||
|
|
||||||
Runs as a serf event handler. Every alive member carries its number record
|
|
||||||
as tags (number, key, sig, owner). A record is accepted only when the
|
|
||||||
yggdrasil address it is gossiped from derives from `key` and `sig` is a
|
|
||||||
valid ed25519 signature of "phonebox:<number>" under that key, so a number
|
|
||||||
can only ever be bound to the address of the node that signed it.
|
|
||||||
|
|
||||||
Output, consumed by the asterisk dialplan:
|
|
||||||
<state dir>/numbers/<number> the yggdrasil address, no trailing newline
|
|
||||||
<state dir>/contacts.txt "<number> : <owner>" per line, for the fax
|
|
||||||
"""
|
|
||||||
|
|
||||||
import base64
|
|
||||||
import ipaddress
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import syslog
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
from cryptography.exceptions import InvalidSignature
|
|
||||||
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
|
|
||||||
|
|
||||||
|
|
||||||
def yggdrasil_address(public_key: bytes) -> ipaddress.IPv6Address:
|
|
||||||
"""Port of yggdrasil-go address.AddrForKey.
|
|
||||||
|
|
||||||
Invert the key; the address is 0x02, the count of leading one bits,
|
|
||||||
then the bits following the first zero, truncated to 128 bits.
|
|
||||||
"""
|
|
||||||
bits = "".join(f"{b ^ 0xFF:08b}" for b in public_key)
|
|
||||||
ones = len(bits) - len(bits.lstrip("1"))
|
|
||||||
body = int(bits[ones + 1:ones + 113], 2).to_bytes(14, "big")
|
|
||||||
return ipaddress.IPv6Address(bytes([0x02, ones]) + body)
|
|
||||||
|
|
||||||
|
|
||||||
def verified_record(member: dict) -> tuple[str, str, str, str]:
|
|
||||||
"""Return (number, key hex, address, owner) or raise ValueError."""
|
|
||||||
tags = member.get("tags", {})
|
|
||||||
try:
|
|
||||||
number, key_hex, sig = tags["number"], tags["key"], tags["sig"]
|
|
||||||
key = bytes.fromhex(key_hex)
|
|
||||||
host, _ = member["addr"].rsplit(":", 1)
|
|
||||||
address = ipaddress.IPv6Address(host.strip("[]"))
|
|
||||||
signature = base64.b64decode(sig, validate=True)
|
|
||||||
except (KeyError, ValueError) as e:
|
|
||||||
raise ValueError(f"malformed record: {e}")
|
|
||||||
if not (len(number) == 6 and number.isdigit() and number[0] != "0"):
|
|
||||||
raise ValueError(f"invalid number {number!r}")
|
|
||||||
if yggdrasil_address(key) != address:
|
|
||||||
raise ValueError(f"key does not derive to address {address}")
|
|
||||||
try:
|
|
||||||
Ed25519PublicKey.from_public_bytes(key).verify(signature, b"phonebox:" + number.encode())
|
|
||||||
except (ValueError, InvalidSignature):
|
|
||||||
raise ValueError(f"bad signature for number {number}")
|
|
||||||
return number, key_hex, str(address), tags.get("owner", "")
|
|
||||||
|
|
||||||
|
|
||||||
def directory(members: list[dict]) -> dict[str, tuple[str, str, str]]:
|
|
||||||
"""number -> (key hex, address, owner); on a collision the lowest key wins."""
|
|
||||||
book = {}
|
|
||||||
for member in members:
|
|
||||||
try:
|
|
||||||
number, key, address, owner = verified_record(member)
|
|
||||||
except ValueError as e:
|
|
||||||
syslog.syslog(syslog.LOG_WARNING, f"ignoring {member.get('name')}: {e}")
|
|
||||||
continue
|
|
||||||
if number in book:
|
|
||||||
syslog.syslog(syslog.LOG_WARNING, f"number {number} claimed by keys {book[number][0]} and {key}")
|
|
||||||
if book[number][0] <= key:
|
|
||||||
continue
|
|
||||||
book[number] = (key, address, owner)
|
|
||||||
return book
|
|
||||||
|
|
||||||
|
|
||||||
def write_atomic(path: str, content: str) -> None:
|
|
||||||
fd, tmp = tempfile.mkstemp(dir=os.path.dirname(path))
|
|
||||||
with os.fdopen(fd, "w") as f:
|
|
||||||
f.write(content)
|
|
||||||
os.chmod(tmp, 0o644)
|
|
||||||
os.replace(tmp, path)
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
rpc_addr, state_dir = sys.argv[1:]
|
|
||||||
syslog.openlog("phonebox-sync")
|
|
||||||
out = subprocess.run(
|
|
||||||
["serf", "members", "-format=json", "-status=alive", f"-rpc-addr={rpc_addr}"],
|
|
||||||
check=True, capture_output=True, text=True,
|
|
||||||
).stdout
|
|
||||||
book = directory(json.loads(out)["members"])
|
|
||||||
|
|
||||||
numbers_dir = os.path.join(state_dir, "numbers")
|
|
||||||
os.makedirs(numbers_dir, exist_ok=True)
|
|
||||||
for number, (_, address, _) in book.items():
|
|
||||||
write_atomic(os.path.join(numbers_dir, number), address)
|
|
||||||
for stale in set(os.listdir(numbers_dir)) - book.keys():
|
|
||||||
os.unlink(os.path.join(numbers_dir, stale))
|
|
||||||
write_atomic(
|
|
||||||
os.path.join(state_dir, "contacts.txt"),
|
|
||||||
"".join(f"{number}\t\t: \t\t{book[number][2]}\n" for number in sorted(book)),
|
|
||||||
)
|
|
||||||
syslog.syslog(syslog.LOG_INFO, f"directory has {len(book)} numbers")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
"""Tests for phonebox-sync: record verification and directory output.
|
|
||||||
|
|
||||||
Run via the `phonebox-global-sync` flake check; needs the `cryptography`
|
|
||||||
python package and the `yggdrasil` binary on PATH.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import base64
|
|
||||||
import importlib.util
|
|
||||||
import ipaddress
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import tempfile
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
|
|
||||||
|
|
||||||
spec = importlib.util.spec_from_file_location("phonebox_sync", os.environ["PHONEBOX_SYNC"])
|
|
||||||
sync = importlib.util.module_from_spec(spec)
|
|
||||||
spec.loader.exec_module(sync)
|
|
||||||
|
|
||||||
PORT = 7946
|
|
||||||
|
|
||||||
|
|
||||||
class Node:
|
|
||||||
"""A phonebox node identity: yggdrasil key, address and signed number."""
|
|
||||||
|
|
||||||
def __init__(self, number: str, owner: str = ""):
|
|
||||||
self.key = Ed25519PrivateKey.generate()
|
|
||||||
self.public = self.key.public_key().public_bytes_raw()
|
|
||||||
self.address = str(sync.yggdrasil_address(self.public))
|
|
||||||
self.number = number
|
|
||||||
self.owner = owner
|
|
||||||
|
|
||||||
def member(self, name="node", **overrides) -> dict:
|
|
||||||
tags = {
|
|
||||||
"number": self.number,
|
|
||||||
"key": self.public.hex(),
|
|
||||||
"sig": base64.b64encode(self.key.sign(b"phonebox:" + self.number.encode())).decode(),
|
|
||||||
"owner": self.owner,
|
|
||||||
}
|
|
||||||
member = {"name": name, "addr": f"[{self.address}]:{PORT}", "port": PORT, "status": "alive", "tags": tags}
|
|
||||||
for k, v in overrides.items():
|
|
||||||
(tags if k in tags else member)[k] = v
|
|
||||||
return member
|
|
||||||
|
|
||||||
|
|
||||||
def yggdrasil_binary_address(key: Ed25519PrivateKey) -> str:
|
|
||||||
private = key.private_bytes_raw() + key.public_key().public_bytes_raw()
|
|
||||||
out = subprocess.run(
|
|
||||||
["yggdrasil", "-useconf", "-address"],
|
|
||||||
input=json.dumps({"PrivateKey": private.hex()}),
|
|
||||||
check=True, capture_output=True, text=True,
|
|
||||||
)
|
|
||||||
return out.stdout.strip()
|
|
||||||
|
|
||||||
|
|
||||||
class AddressDerivation(unittest.TestCase):
|
|
||||||
def test_known_vector(self):
|
|
||||||
# A real clan machine key and the address yggdrasil assigned to it.
|
|
||||||
key = bytes.fromhex("0ec53986a2bdca8a43f74063aeab6a958fc697c528c83e7281365072926886f0")
|
|
||||||
self.assertEqual(str(sync.yggdrasil_address(key)), "204:2758:cf2b:a846:aeb7:8117:f38a:2a92")
|
|
||||||
|
|
||||||
def test_matches_yggdrasil_binary(self):
|
|
||||||
for _ in range(16):
|
|
||||||
key = Ed25519PrivateKey.generate()
|
|
||||||
expected = ipaddress.IPv6Address(yggdrasil_binary_address(key))
|
|
||||||
self.assertEqual(sync.yggdrasil_address(key.public_key().public_bytes_raw()), expected)
|
|
||||||
|
|
||||||
|
|
||||||
class RecordVerification(unittest.TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.node = Node("482913", owner="alice")
|
|
||||||
|
|
||||||
def test_valid_record(self):
|
|
||||||
number, key, address, owner = sync.verified_record(self.node.member())
|
|
||||||
self.assertEqual((number, key, address, owner), ("482913", self.node.public.hex(), self.node.address, "alice"))
|
|
||||||
|
|
||||||
def test_record_from_foreign_address_is_rejected(self):
|
|
||||||
other = Node("111111")
|
|
||||||
with self.assertRaisesRegex(ValueError, "does not derive"):
|
|
||||||
sync.verified_record(self.node.member(addr=f"[{other.address}]:{PORT}"))
|
|
||||||
|
|
||||||
def test_signature_over_other_number_is_rejected(self):
|
|
||||||
with self.assertRaisesRegex(ValueError, "bad signature"):
|
|
||||||
sync.verified_record(self.node.member(number="999999"))
|
|
||||||
|
|
||||||
def test_garbage_signature_is_rejected(self):
|
|
||||||
with self.assertRaisesRegex(ValueError, "malformed"):
|
|
||||||
sync.verified_record(self.node.member(sig="not base64!"))
|
|
||||||
|
|
||||||
def test_missing_tags_are_rejected(self):
|
|
||||||
with self.assertRaisesRegex(ValueError, "malformed"):
|
|
||||||
sync.verified_record({"name": "foreign", "addr": f"[{self.node.address}]:{PORT}", "tags": {}})
|
|
||||||
|
|
||||||
def test_number_must_be_six_digits_without_leading_zero(self):
|
|
||||||
for bad in ["012345", "12345", "1234567", "12a456", ""]:
|
|
||||||
node = Node(bad)
|
|
||||||
with self.assertRaisesRegex(ValueError, "invalid number", msg=bad):
|
|
||||||
sync.verified_record(node.member())
|
|
||||||
|
|
||||||
def test_owner_defaults_to_empty(self):
|
|
||||||
member = self.node.member()
|
|
||||||
del member["tags"]["owner"]
|
|
||||||
self.assertEqual(sync.verified_record(member)[3], "")
|
|
||||||
|
|
||||||
|
|
||||||
class Directory(unittest.TestCase):
|
|
||||||
def test_invalid_records_are_skipped_not_fatal(self):
|
|
||||||
good = Node("482913", owner="alice")
|
|
||||||
bad = Node("555555")
|
|
||||||
members = [bad.member(sig="AAAA"), good.member(), {"name": "x", "addr": "[200::1]:7946", "tags": {}}]
|
|
||||||
self.assertEqual(sync.directory(members), {"482913": (good.public.hex(), good.address, "alice")})
|
|
||||||
|
|
||||||
def test_collision_lowest_key_wins_regardless_of_order(self):
|
|
||||||
a, b = Node("482913"), Node("482913")
|
|
||||||
winner = min((a, b), key=lambda n: n.public.hex())
|
|
||||||
for members in ([a.member("a"), b.member("b")], [b.member("b"), a.member("a")]):
|
|
||||||
self.assertEqual(sync.directory(members)["482913"][1], winner.address)
|
|
||||||
|
|
||||||
|
|
||||||
class Main(unittest.TestCase):
|
|
||||||
def run_sync(self, members: list[dict], state: str) -> None:
|
|
||||||
bindir = tempfile.mkdtemp()
|
|
||||||
with open(os.path.join(bindir, "serf"), "w") as f:
|
|
||||||
f.write("#!/bin/sh\ncat <<'EOF'\n" + json.dumps({"members": members}) + "\nEOF\n")
|
|
||||||
os.chmod(os.path.join(bindir, "serf"), 0o755)
|
|
||||||
env_path = os.environ["PATH"]
|
|
||||||
os.environ["PATH"] = bindir + os.pathsep + env_path
|
|
||||||
argv = sys.argv
|
|
||||||
sys.argv = ["phonebox-sync", "127.0.0.1:7373", state]
|
|
||||||
try:
|
|
||||||
sync.main()
|
|
||||||
finally:
|
|
||||||
sys.argv = argv
|
|
||||||
os.environ["PATH"] = env_path
|
|
||||||
|
|
||||||
def test_writes_numbers_and_contacts_and_removes_stale(self):
|
|
||||||
state = tempfile.mkdtemp()
|
|
||||||
os.makedirs(os.path.join(state, "numbers"))
|
|
||||||
with open(os.path.join(state, "numbers", "111111"), "w") as f:
|
|
||||||
f.write("200::dead")
|
|
||||||
a, b = Node("482913", owner="alice"), Node("555555", owner="bob")
|
|
||||||
self.run_sync([b.member("b"), a.member("a")], state)
|
|
||||||
|
|
||||||
numbers = os.path.join(state, "numbers")
|
|
||||||
self.assertEqual(sorted(os.listdir(numbers)), ["482913", "555555"])
|
|
||||||
with open(os.path.join(numbers, "482913")) as f:
|
|
||||||
self.assertEqual(f.read(), a.address) # no trailing newline: read by FILE() in the dialplan
|
|
||||||
with open(os.path.join(state, "contacts.txt")) as f:
|
|
||||||
self.assertEqual(f.read(), "482913\t\t: \t\talice\n555555\t\t: \t\tbob\n")
|
|
||||||
|
|
||||||
def test_empty_membership_clears_directory(self):
|
|
||||||
state = tempfile.mkdtemp()
|
|
||||||
self.run_sync([Node("482913").member()], state)
|
|
||||||
self.run_sync([], state)
|
|
||||||
self.assertEqual(os.listdir(os.path.join(state, "numbers")), [])
|
|
||||||
with open(os.path.join(state, "contacts.txt")) as f:
|
|
||||||
self.assertEqual(f.read(), "")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
{
|
|
||||||
self,
|
|
||||||
hostPkgs,
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
name = "service-phonebox-global";
|
|
||||||
result.update-vars =
|
|
||||||
let
|
|
||||||
relativeDir = lib.removePrefix "${self}/" (toString config.clan.directory);
|
|
||||||
in
|
|
||||||
hostPkgs.writeShellScriptBin "update-vars" ''
|
|
||||||
set -x
|
|
||||||
export PRJ_ROOT=$(git rev-parse --show-toplevel)
|
|
||||||
${
|
|
||||||
self.inputs.clan-core.packages.${hostPkgs.system}.clan-cli
|
|
||||||
}/bin/clan-generate-test-vars $PRJ_ROOT/${relativeDir} ${config.name}
|
|
||||||
'';
|
|
||||||
|
|
||||||
clan = {
|
|
||||||
test.useContainers = false;
|
|
||||||
directory = ./.;
|
|
||||||
inventory = {
|
|
||||||
machines.server = { };
|
|
||||||
machines.nodeA = { };
|
|
||||||
|
|
||||||
instances = {
|
|
||||||
yggdrasil = {
|
|
||||||
module.name = "yggdrasil";
|
|
||||||
roles.default.machines.server = { };
|
|
||||||
roles.default.machines.nodeA = { };
|
|
||||||
};
|
|
||||||
phonebox-global-test = {
|
|
||||||
module.name = "@clan/phonebox-global";
|
|
||||||
module.input = "self";
|
|
||||||
roles.default.machines.server.settings.ata-ethernet-iface = "enp2s0";
|
|
||||||
roles.default.machines.nodeA.settings.ata-ethernet-iface = "enp2s0";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
nodes = {
|
|
||||||
server = { };
|
|
||||||
nodeA = { };
|
|
||||||
};
|
|
||||||
|
|
||||||
testScript =
|
|
||||||
{ nodes, ... }:
|
|
||||||
let
|
|
||||||
number = node: nodes.${node}.clan.core.vars.generators.phonebox-global.files.number.value;
|
|
||||||
address = node: nodes.${node}.clan.core.vars.generators.yggdrasil.files.address.value;
|
|
||||||
in
|
|
||||||
''
|
|
||||||
start_all()
|
|
||||||
|
|
||||||
for node in [server, nodeA]:
|
|
||||||
node.wait_for_unit("asterisk.service")
|
|
||||||
node.wait_for_unit("phonebox-directory.service")
|
|
||||||
|
|
||||||
# Directory converges: each node learns the other's number and address.
|
|
||||||
server.wait_until_succeeds("test -f /run/phonebox/numbers/${number "nodeA"}", timeout=300)
|
|
||||||
nodeA.wait_until_succeeds("test -f /run/phonebox/numbers/${number "server"}", timeout=300)
|
|
||||||
assert server.succeed("cat /run/phonebox/numbers/${number "nodeA"}") == "${address "nodeA"}"
|
|
||||||
assert nodeA.succeed("cat /run/phonebox/numbers/${number "server"}") == "${address "server"}"
|
|
||||||
assert "${number "nodeA"}" in server.succeed("cat /run/phonebox/contacts.txt")
|
|
||||||
|
|
||||||
# A call from server to nodeA's number is routed over yggdrasil and
|
|
||||||
# accepted by nodeA's asterisk (no phone is registered, so it fails
|
|
||||||
# after being identified, which is enough to prove the path).
|
|
||||||
nodeA.succeed("asterisk -rx 'pjsip set logger on'")
|
|
||||||
server.succeed("asterisk -rx 'channel originate Local/${number "nodeA"}@from-internal application Wait 3'")
|
|
||||||
nodeA.wait_until_succeeds("grep -q 'INVITE sip:${number "nodeA"}@' /var/log/asterisk/full", timeout=60)
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"publickey": "age18yf7r0dalnue5vwzanxl046fxsskhu9lzvd488ajhz7rp62mu94scmw3w4",
|
|
||||||
"type": "age"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"publickey": "age1sy8xkmssttcchjj0nkapk4tu530lrzszdp4lka6nn4gzlxam6e2s3xzp2j",
|
|
||||||
"type": "age"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:NkOeX6FboG7NOkdgiTgAsS+XjRoDU+AEYfMhpZXIJTYEM2dQti+PMGyizKRvr1wRnSGMp56XaX0bHdToBuf1gYruqAsEkKwqsYw=,iv:RFyO5/JybroNAKe+F3vv51l4OEu5XXNs+biTTH8poEg=,tag:zfPm0ZAjT2CC734EU+36KQ==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2RklrTFQxMll5VUxiaGVu\nNlRzeldTRnVrSG93TXYzc1YrRE9Nc1VqTHpNCjh4aHJMZmtpM2tDclNaT3d2SDhN\na1QxTXVFOHRuY3dpL1YxMTJKK3pUVmMKLS0tIHoyMGVyc01vak5EKzZONC9XUmFw\nMU5MRmx6UDROQzFiaTNuNXFiQTZoYmcK6wxypuP7vTnz//EcEt2pUNqUuKxjOaY3\nAmKToJQADJfzTsYGzpmWkkYaMHvG00v8Ja6TF6IxZr5SxUh4bdHr+w==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2026-09-18T02:02:10Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:kRpbcT5+I80iHPjPUnveQE9jpa65n0xPPQR/by4p1iM80ZKk9KH0cObB5hHhXO4BD1OqI79UJ3O9O7Jhmylgx5Uh8RNbK6wf26p4GO2Bvqh9r15NqbCZtXkoW30MTLLIs58Q7NeK9O2LgeckWrXLtyEytfhnTuoTSDFo8Sh8NQY=,iv:rcNow1Z4BN7bo1XERB6c+G6Gwl3tRU1VHvYmoDaLTl4=,tag:vy/5EB/1gw/KLLqXHSEV3g==,type:str]",
|
|
||||||
"version": "3.13.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../users/admin
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:Sy9T0zEgSGZ/qkz5kaRN2qlH2X+pFRibzb8FpWuknuSI/9lActSG2Loq4LhmhqPGC5MGVkQcz2pcVFI7afUjzDzejfJf4De1ka4=,iv:JNLXguWEwJXR2+WNl0v0nUvkESVAbccfeQSUijKbj2I=,tag:/AAFKncO+B/XmUZYTQnTTw==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqOXJoSXgvcEFXK2hWTlpi\nQWhCdnZPUCt5R2xEWFYxUnpWVlc1ei93TncwCjNKaVB0MFN1VUdheEwyUTBzcmFT\nb0RjTHp2MGE4cWIraCtZME9rQXAvVEkKLS0tIFpiZDhCN0NIY3FWTnlXaFhiMmtC\nTFJMUncvMnFRSEhNdHlhaWozcjlqYWsKLDH1VFG/QS3VIMn6Iwo2NmY5kdBiOPNR\nj4sOY6xBpWP5AiGUypGoX4Bm52gLgW5AdcBRIxxhFOekIv5G8wmRaw==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2026-09-18T02:02:23Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:DN97Dcpy29IZkBbwFVnMxnUnWYrgLKuvbANabkMCdpOJYgI1msYODltHJlR68Lk8Fmg25sXysvdbJOnc2cMH3pgcKxeyfhdAFwlIXdie1HRtTpZql15RF0fwPBGJjwL9YYubpZU7K4cNflGbjuOCEhp8bQOUYY8Ptd2deiDGGuw=,iv:bxTcKLUu6fcz7JKzStKiakgBeNx7aWCw6M7HolthfEo=,tag:/0nWBkdCPkpcWSpcfY/TfA==,type:str]",
|
|
||||||
"version": "3.13.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../users/admin
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
|
||||||
"type": "age"
|
|
||||||
}
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
392268
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
fake_line_value
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
cf28bbcd822efec197dfaa99fbe9004f78b4efa9724b4f453d05f12a61a0bb44
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
CrnzGbqfVORt00zeI99crpBRUMfGF+2uEfaHe1L3Y8k7CIojc+8TID40HPhwEgXNtC9/xr61mN5L0Vp4vpviBA==
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
26.11
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
200:61ae:8864:fba2:27c:d040:aacc:82d
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
../../../../../../sops/machines/nodeA
|
|
||||||
-18
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:hR33SvxgsU+hZnvF2hoQxE7n0IB6kLC9MgK0wS0NXpCht5NuPjTo5p51oUE8t95Dht2qlEdXAkGO39k/h42p54vhqcfuF7u/eqVMdX6pEJyOokFmsdDmdVZziyhZghrTI/nDlctxCPcqFkeCHU4SIBZs85NS8uk=,iv:Q8k8OXSS3lFw33UO8JiZYNkrH3s+Cvh0xnAbMiBmnNA=,tag:DtNYKgDYB2nSHIjvMdvu/w==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTSzk1RUhIekVJRStQd3Er\nbXlHUmtVb201dkRteVR1VFpoMTVFcTFPSG5NCmlPcVM5cWVJQ0g0VHNXaS9rTzh2\nWTIyQnRXakVCRWRQL0xuVUdDOEV0WEUKLS0tIE43RENWL1M2STF1VzdMcHJTNThh\ndHpqNE9MMnkrMlIxN2FEcjZ6anRIbDAK3rZ1lzO42bH37lb+zoeF6rKoVgI0TBST\n9EoVwom5xOKtmVAh5jobY/z4TGSD1BqQ6P2rQ/IM1Dtw3/18Yk7TXQ==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age18yf7r0dalnue5vwzanxl046fxsskhu9lzvd488ajhz7rp62mu94scmw3w4"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkdDFsSTZHUG54ZWNtQVdj\nZno4enBaRVlaQU1wNTBEVDVscS9Jc1JaTXhjClA3YUNQa3B2L0JXY25SSHRGbS94\nNVVNdHZ5VzdXcWpXZG1ZS1U4T0NPUkEKLS0tIFJLVmZyazU4dWUyVnpvRDRWbHNn\nZFQ3dlhxN0lwc0loWEZIOE0yMElRV0EKeUVy2QpLHbJ8JUCl07f74V+ZnRkGfDdP\n5W28yiLuq+LYfKUaFYeRDvSVFCQo6FiYJcBPGmnPF5gg1BIUtUUBYw==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2026-09-18T02:02:10Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:Oq1UCi+VYuuAkMmVBife740m6fiXC+PfkaQAii+FORyv8Zpj938cPrZzE9z/LO5Ixye5cbUHGRhZr0vM1egnv9nhIFyfGinKyE/BLEOXHxOtHnAXSQOJU2bWiR6w/QzVCTy6vTTVnqD2AtEfbKndIK1PJ0ASLvxMGaRqRsA/juE=,iv:xCEQg6DHV6hbyfBk62bUKA1lnC3HgQtkn2o8i2CFjhs=,tag:Zvt9tomYGoDFDQGjLZC59A==,type:str]",
|
|
||||||
"version": "3.13.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
../../../../../../sops/users/admin
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
cf28bbcd822efec197dfaa99fbe9004f78b4efa9724b4f453d05f12a61a0bb44
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
981794
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
fake_line_value
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
7291692f9a94948724377fc22055edde8e2773a581153317bc20bd5779736647
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
IwAOYcmClqi8K8EStthYMqWCr6WsxM3nD0CLDRvvb7I3CUyihXBjdghXLutDD/bFsHKzOkDVV/gLnTuRj9IEAg==
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
26.11
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
201:35ba:5b41:95ad:ade3:6f22:f7:7ea8
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
../../../../../../sops/machines/server
|
|
||||||
-18
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:H1ngxnRS+cFNSxe9K7C/xNS5hzXyeTWBLZI18TPvEyT5l/MSBeRuK6FZ/G0xUGQAJw4Xo1nyX2PZEYH5wjhnG0UziKAZeM2Oe0G/ilpNbHeMS+HLVriDETlMQz2Hf4Y1J6jOZqSTgFuPMVG5armpZUYTcW+p2zo=,iv:u9sO6eiFzxtT7uImnHLXT3aYJJxgby8d5H2ckfZdNIw=,tag:ZLl7AHHRF+jmr0tnujrpIg==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzdy9HVVFoa0N5ZnBDVDhM\ncm1vQzR3MUpSbW9KSjE0YmZueUlsY1gvUVc4CmlqaTBxMTNvZzdmdkJSMmMwN01N\nVGsvUzJ5Q055Tmk2L2ZUNjRpSHp0bE0KLS0tIGJhUUNDbGh1a09aeXMzWkl4K0F0\nZ0dYY1FnNCtkVXRVTVkyeXZPbFlVbUUK3a/V6XP6m8MDYPKwlu8z0cr4inuKGGNc\nYJjQw00ou3BdM/HOPZrMjYHtjg5WdtzYiFCOl8IRkeiDaEmXcOBjiw==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSbW9lcXZweHZ6b0F1eVIy\nd2RWV2lkOHpoM2RkZWxSVEFwU1h5aXNMWldnCk5lS1E4b1BDMmRYUDNHK2NhTlVT\nWGpLWm4rUnpFVWNvMHNzclJuemNGZkEKLS0tIGZxcmowODUydm1kL24yVHd4YXl4\nWVd5eWZMZ0dyS2dpTDhuNXo4S25Ha0EKvc3hwrmlP5JcH7eQeRfoq40w/QdnQL8s\n+DAwsajzRp6H3/XVTm+nDQ+Qoc3aenewZkk6Pgn+x43hAh9zwuvGkA==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1sy8xkmssttcchjj0nkapk4tu530lrzszdp4lka6nn4gzlxam6e2s3xzp2j"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2026-09-18T02:02:24Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:xrshyxQAnlSORTzxxcpt0ABaNWSCCVnwHl6oPUQ59reumF7BoAy8HmdSyWeEpIKBNo2Ane1xxXBHa62+w58r0WjcneNzy8yIBBwXr1BIRZPX83HrSz3+bENPtj6TPWdHpFZ5aDFM2jVlUvwIkn/4g+1B9FnQtoH8kuYBGe2uGCM=,iv:slMt0ag3Kp3iD89jQa/YDta4mJccKiSqcTCt8C+1Dzs=,tag:o56eg/BSHOtBUeNQqRjQDg==,type:str]",
|
|
||||||
"version": "3.13.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
../../../../../../sops/users/admin
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
7291692f9a94948724377fc22055edde8e2773a581153317bc20bd5779736647
|
|
||||||
@@ -24,36 +24,6 @@
|
|||||||
description = "";
|
description = "";
|
||||||
default = "";
|
default = "";
|
||||||
};
|
};
|
||||||
options.extraClientNumbers = lib.mkOption {
|
|
||||||
type = with lib.types; listOf str;
|
|
||||||
description = "List of client suffix number.";
|
|
||||||
default = [ ];
|
|
||||||
};
|
|
||||||
|
|
||||||
options.extraFixedIPClient = lib.mkOption {
|
|
||||||
type = lib.types.attrsOf (
|
|
||||||
lib.types.submodule {
|
|
||||||
options = {
|
|
||||||
ip = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "IP address for this client";
|
|
||||||
};
|
|
||||||
|
|
||||||
name = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "Name of the client";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
description = "Extra client to be added to pjsip config as a fixed IP auth";
|
|
||||||
example = {
|
|
||||||
"01" = {
|
|
||||||
ip = "192.168.1.3";
|
|
||||||
name = "bob";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
perInstance =
|
perInstance =
|
||||||
{
|
{
|
||||||
@@ -156,41 +126,6 @@
|
|||||||
remove_existing=yes
|
remove_existing=yes
|
||||||
'';
|
'';
|
||||||
|
|
||||||
genLocalSIPEndpointV6 =
|
|
||||||
{ localNumber }:
|
|
||||||
''
|
|
||||||
[${localNumber}](internal_endpoint)
|
|
||||||
transport=transport-udp6
|
|
||||||
aors=${localNumber}
|
|
||||||
auth=${localNumber}
|
|
||||||
|
|
||||||
[${localNumber}](userpass_auth)
|
|
||||||
username=${localNumber}
|
|
||||||
password=${localNumber}
|
|
||||||
|
|
||||||
[${localNumber}](dynamiic_aor)
|
|
||||||
max_contacts=1
|
|
||||||
'';
|
|
||||||
|
|
||||||
genLocalSIPIPEndpoint = number: ''
|
|
||||||
|
|
||||||
[${number}](internal_endpoint)
|
|
||||||
aors=${number}
|
|
||||||
auth=${number}
|
|
||||||
contact_deny=0.0.0.0/0
|
|
||||||
contact_deny=::/0
|
|
||||||
contact_permit=${settings.extraFixedIPClient.${number}.ip}/128
|
|
||||||
|
|
||||||
[${number}](dynamiic_aor)
|
|
||||||
max_contacts=1
|
|
||||||
remove_existing=yes
|
|
||||||
|
|
||||||
[${number}](userpass_auth)
|
|
||||||
username=${number}
|
|
||||||
password=${number}
|
|
||||||
|
|
||||||
'';
|
|
||||||
|
|
||||||
genLocalExtenConf =
|
genLocalExtenConf =
|
||||||
{ localNumber }:
|
{ localNumber }:
|
||||||
''
|
''
|
||||||
@@ -421,14 +356,6 @@
|
|||||||
+ (genLocalExtenConf {
|
+ (genLocalExtenConf {
|
||||||
localNumber = config.clan.core.vars.generators.phonebox.files.ata-local-number.value;
|
localNumber = config.clan.core.vars.generators.phonebox.files.ata-local-number.value;
|
||||||
})
|
})
|
||||||
+ lib.concatStringsSep "\n" (
|
|
||||||
builtins.map (number: genLocalExtenConf { localNumber = number; }) settings.extraClientNumbers
|
|
||||||
)
|
|
||||||
+ lib.concatStringsSep "\n" (
|
|
||||||
lib.mapAttrsToList (
|
|
||||||
number: _: genLocalExtenConf { localNumber = number; }
|
|
||||||
) settings.extraFixedIPClient
|
|
||||||
)
|
|
||||||
+ serverConf;
|
+ serverConf;
|
||||||
|
|
||||||
"rtp.conf" = ''
|
"rtp.conf" = ''
|
||||||
@@ -482,12 +409,6 @@
|
|||||||
+ (genLocalSIPEndpoint {
|
+ (genLocalSIPEndpoint {
|
||||||
localNumber = config.clan.core.vars.generators.phonebox.files.ata-local-number.value;
|
localNumber = config.clan.core.vars.generators.phonebox.files.ata-local-number.value;
|
||||||
})
|
})
|
||||||
+ lib.concatStringsSep "\n" (
|
|
||||||
builtins.map (number: genLocalSIPEndpointV6 { localNumber = number; }) settings.extraClientNumbers
|
|
||||||
)
|
|
||||||
+ lib.concatStringsSep "\n" (
|
|
||||||
lib.mapAttrsToList (number: _: genLocalSIPIPEndpoint number) settings.extraFixedIPClient
|
|
||||||
)
|
|
||||||
+ serverConf;
|
+ serverConf;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,308 +0,0 @@
|
|||||||
{ clanLib, ... }:
|
|
||||||
{
|
|
||||||
_class = "clan.service";
|
|
||||||
manifest.name = "prometheus";
|
|
||||||
manifest.description = "The Prometheus monitoring system and time series database.";
|
|
||||||
manifest.readme = builtins.readFile ./README.md;
|
|
||||||
manifest.categories = [ "System" ];
|
|
||||||
|
|
||||||
roles.server = {
|
|
||||||
description = "Prometheus server that scraps all data from nodes";
|
|
||||||
|
|
||||||
interface =
|
|
||||||
{ lib, ... }:
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
scrape_interval = lib.mkOption {
|
|
||||||
type = with lib.types; nullOr str;
|
|
||||||
default = "1m";
|
|
||||||
description = "How often to scrape targets. Default is 1 minutes";
|
|
||||||
};
|
|
||||||
extra_rules = lib.mkOption {
|
|
||||||
type = with lib.types; listOf attrs;
|
|
||||||
default = [ ];
|
|
||||||
description = "Additional rules for Prometheus";
|
|
||||||
};
|
|
||||||
default_receiver = lib.mkOption {
|
|
||||||
type = with lib.types; attrs;
|
|
||||||
default = {
|
|
||||||
name = "default";
|
|
||||||
};
|
|
||||||
description = "Definition of a default receiver, default is doing nothing";
|
|
||||||
};
|
|
||||||
matrix-alertmanager = {
|
|
||||||
enable = lib.mkOption {
|
|
||||||
type = with lib.types; bool;
|
|
||||||
default = false;
|
|
||||||
description = "Whether to enable `services.matrix-alertmanager`";
|
|
||||||
};
|
|
||||||
homeserverUrl = lib.mkOption {
|
|
||||||
type = with lib.types; str;
|
|
||||||
default = "https://matrix-client.matrix.org";
|
|
||||||
description = "URL of the Matrix homeserver to use";
|
|
||||||
};
|
|
||||||
matrixUser = lib.mkOption {
|
|
||||||
type = with lib.types; str;
|
|
||||||
description = "Matrix user for the bot";
|
|
||||||
};
|
|
||||||
matrixRooms = lib.mkOption {
|
|
||||||
type = lib.types.listOf (
|
|
||||||
lib.types.submodule {
|
|
||||||
options = {
|
|
||||||
receivers = lib.mkOption {
|
|
||||||
type = lib.types.listOf lib.types.str;
|
|
||||||
description = "List of receivers for this room";
|
|
||||||
};
|
|
||||||
roomId = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = "Matrix room ID";
|
|
||||||
apply =
|
|
||||||
x:
|
|
||||||
assert lib.assertMsg (lib.hasPrefix "!" x) "Matrix room ID must start with a '!'. Got: ${x}";
|
|
||||||
x;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
description = ''
|
|
||||||
Combination of Alertmanager receiver(s) and rooms for the bot to join.
|
|
||||||
Each Alertmanager receiver can be mapped to post to a matrix room.
|
|
||||||
|
|
||||||
Note, you must use a room ID and not a room alias/name. Room IDs start
|
|
||||||
with a "!".
|
|
||||||
'';
|
|
||||||
example = [
|
|
||||||
{
|
|
||||||
receivers = [
|
|
||||||
"receiver1"
|
|
||||||
"receiver2"
|
|
||||||
];
|
|
||||||
roomId = "!roomid@example.com";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
receivers = [ "receiver3" ];
|
|
||||||
roomId = "!differentroomid@example.com";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
perInstance =
|
|
||||||
{
|
|
||||||
settings,
|
|
||||||
roles,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
nixosModule =
|
|
||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
getYggdrasilIP =
|
|
||||||
machineName:
|
|
||||||
if config.clan.core.vars.generators.yggdrasil.files.address ? value then
|
|
||||||
clanLib.getPublicValue {
|
|
||||||
flake = config.clan.core.settings.directory;
|
|
||||||
machine = machineName;
|
|
||||||
generator = "yggdrasil";
|
|
||||||
file = "address";
|
|
||||||
default = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw "clanService/yggdrasil is required";
|
|
||||||
|
|
||||||
matrixRoomReceivers = lib.unique (
|
|
||||||
lib.concatMap (entry: entry.receivers) settings.matrix-alertmanager.matrixRooms
|
|
||||||
);
|
|
||||||
in
|
|
||||||
lib.mkMerge [
|
|
||||||
{
|
|
||||||
networking.firewall.allowedTCPPorts = [
|
|
||||||
9090
|
|
||||||
];
|
|
||||||
services.prometheus = {
|
|
||||||
enable = true;
|
|
||||||
|
|
||||||
globalConfig = {
|
|
||||||
scrape_interval = settings.scrape_interval;
|
|
||||||
};
|
|
||||||
|
|
||||||
alertmanagers = [
|
|
||||||
{
|
|
||||||
scheme = "http";
|
|
||||||
path_prefix = "/";
|
|
||||||
static_configs = [ { targets = [ "localhost:9093" ]; } ];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
alertmanager = {
|
|
||||||
enable = true;
|
|
||||||
configuration = {
|
|
||||||
global = {
|
|
||||||
resolve_timeout = "5m";
|
|
||||||
};
|
|
||||||
route = {
|
|
||||||
receiver = "default";
|
|
||||||
routes = map (mReceiver: { receiver = mReceiver; }) matrixRoomReceivers;
|
|
||||||
};
|
|
||||||
receivers = [
|
|
||||||
{ name = "default"; }
|
|
||||||
]
|
|
||||||
++ map (mReceiver: {
|
|
||||||
name = mReceiver;
|
|
||||||
webhook_configs = [
|
|
||||||
{
|
|
||||||
url_file = config.clan.core.vars.generators.prometheus.files.matrix-alertmanager-urlfile.path;
|
|
||||||
send_resolved = true;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}) matrixRoomReceivers;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
scrapeConfigs = lib.mapAttrsToList (machineName: machineVal: {
|
|
||||||
tls_config.insecure_skip_verify = true;
|
|
||||||
job_name = "${machineName}";
|
|
||||||
static_configs = lib.mapAttrsToList (
|
|
||||||
exporterName: exporterVal:
|
|
||||||
let
|
|
||||||
targetPort =
|
|
||||||
if exporterVal ? port then
|
|
||||||
exporterVal.port
|
|
||||||
else
|
|
||||||
config.services.prometheus.exporters."${exporterName}".port;
|
|
||||||
targetHost = getYggdrasilIP machineName;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
targets = [ "[${targetHost}]:${lib.toString targetPort}" ];
|
|
||||||
}
|
|
||||||
) machineVal.settings.exporters;
|
|
||||||
}) roles.nodes.machines;
|
|
||||||
|
|
||||||
rules = [
|
|
||||||
(builtins.toJSON {
|
|
||||||
groups = [
|
|
||||||
{
|
|
||||||
name = "default";
|
|
||||||
rules = [
|
|
||||||
{
|
|
||||||
alert = "NodesDown";
|
|
||||||
expr = "count by (job) (up == 0) > 0";
|
|
||||||
for = "1m";
|
|
||||||
labels = {
|
|
||||||
severity = "critical";
|
|
||||||
};
|
|
||||||
annotations.summary = "Node **{{ $labels.job }}** has been down for more than 1 minutes.";
|
|
||||||
}
|
|
||||||
{
|
|
||||||
alert = "SmartCtlErrors";
|
|
||||||
expr = "smartctl_device_error_log_count > 0";
|
|
||||||
for = "5m";
|
|
||||||
labels = {
|
|
||||||
severity = "critical";
|
|
||||||
};
|
|
||||||
annotations.summary = ''
|
|
||||||
Errors occur on **{{ $labels.job }}**
|
|
||||||
Disk {{ $labels.device }} {{ $value }}
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
{
|
|
||||||
alert = "ZFSPoolsHealth";
|
|
||||||
expr = "zfs_pool_health > 0";
|
|
||||||
for = "5m";
|
|
||||||
labels = {
|
|
||||||
severity = "critical";
|
|
||||||
};
|
|
||||||
annotations.summary = ''
|
|
||||||
Unhealthy Pool at **{{ $labels.job }}**
|
|
||||||
Pool {{ $labels.pool }} value {{ $value }}
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
]
|
|
||||||
++ settings.extra_rules;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
(lib.optionalAttrs settings.matrix-alertmanager.enable {
|
|
||||||
|
|
||||||
clan.core.vars.generators.prometheus = {
|
|
||||||
files.matrix-alertmanager-token.secret = true;
|
|
||||||
files.matrix-alertmanager-secret.secret = true;
|
|
||||||
files.matrix-alertmanager-urlfile = {
|
|
||||||
secret = true;
|
|
||||||
owner = "alertmanager";
|
|
||||||
group = "alertmanager";
|
|
||||||
};
|
|
||||||
script = ''
|
|
||||||
echo "" > $out/matrix-alertmanager-token
|
|
||||||
openssl rand -hex 32 > "$out"/matrix-alertmanager-secret
|
|
||||||
|
|
||||||
echo "http://localhost:3000/alerts?secret=$(cat $out/matrix-alertmanager-secret)" > $out/matrix-alertmanager-urlfile
|
|
||||||
'';
|
|
||||||
runtimeInputs = [
|
|
||||||
pkgs.openssl
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
services.matrix-alertmanager = lib.mkIf settings.matrix-alertmanager.enable {
|
|
||||||
enable = true;
|
|
||||||
tokenFile = config.clan.core.vars.generators.prometheus.files.matrix-alertmanager-token.path;
|
|
||||||
secretFile = config.clan.core.vars.generators.prometheus.files.matrix-alertmanager-secret.path;
|
|
||||||
homeserverUrl = settings.matrix-alertmanager.homeserverUrl;
|
|
||||||
matrixUser = settings.matrix-alertmanager.matrixUser;
|
|
||||||
matrixRooms = settings.matrix-alertmanager.matrixRooms;
|
|
||||||
};
|
|
||||||
})
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
roles.nodes = {
|
|
||||||
description = "A node will expose metrics for server to harvest";
|
|
||||||
|
|
||||||
interface =
|
|
||||||
{ lib, ... }:
|
|
||||||
{
|
|
||||||
options = {
|
|
||||||
exporters = lib.mkOption {
|
|
||||||
type = lib.types.attrsOf (lib.types.submodule { });
|
|
||||||
default = { };
|
|
||||||
description = "Mirror of services.prometheus.exporters";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
perInstance =
|
|
||||||
{ settings, ... }:
|
|
||||||
let
|
|
||||||
enabledExporters = builtins.mapAttrs (
|
|
||||||
name: value:
|
|
||||||
value
|
|
||||||
// {
|
|
||||||
enable = true;
|
|
||||||
openFirewall = true;
|
|
||||||
}
|
|
||||||
) settings.exporters;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
nixosModule =
|
|
||||||
{ ... }:
|
|
||||||
{
|
|
||||||
services.prometheus.exporters = enabledExporters;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{ self, inputs, ... }:
|
|
||||||
let
|
|
||||||
module = ./default.nix;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
clan.modules = {
|
|
||||||
prometheus = module;
|
|
||||||
};
|
|
||||||
perSystem =
|
|
||||||
{ ... }:
|
|
||||||
{
|
|
||||||
clan.nixosTests.service-prometheus = {
|
|
||||||
imports = [ ./tests/vm/default.nix ];
|
|
||||||
_module.args = { inherit self inputs; };
|
|
||||||
|
|
||||||
clan.modules."@clan/prometheus" = module;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
{
|
|
||||||
self,
|
|
||||||
hostPkgs,
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
name = "service-prometheus";
|
|
||||||
result.update-vars =
|
|
||||||
let
|
|
||||||
relativeDir = lib.removePrefix "${self}/" (toString config.clan.directory);
|
|
||||||
in
|
|
||||||
hostPkgs.writeShellScriptBin "update-vars" ''
|
|
||||||
set -x
|
|
||||||
export PRJ_ROOT=$(git rev-parse --show-toplevel)
|
|
||||||
${
|
|
||||||
self.inputs.clan-core.packages.${hostPkgs.system}.clan-cli
|
|
||||||
}/bin/clan-generate-test-vars $PRJ_ROOT/${relativeDir} ${config.name}
|
|
||||||
'';
|
|
||||||
|
|
||||||
clan = {
|
|
||||||
test.useContainers = false;
|
|
||||||
directory = ./.;
|
|
||||||
inventory = {
|
|
||||||
machines.server = { };
|
|
||||||
machines.nodeA = { };
|
|
||||||
|
|
||||||
instances = {
|
|
||||||
yggdrasil = {
|
|
||||||
module.name = "yggdrasil";
|
|
||||||
roles.default.machines.server = { };
|
|
||||||
roles.default.machines.nodeA = { };
|
|
||||||
};
|
|
||||||
|
|
||||||
prometheus = {
|
|
||||||
module.name = "@clan/prometheus";
|
|
||||||
module.input = "self";
|
|
||||||
roles.nodes.machines."nodeA".settings = {
|
|
||||||
exporters.smartctl = { };
|
|
||||||
};
|
|
||||||
roles.server.machines."server".settings = {
|
|
||||||
extra_rules = [
|
|
||||||
{
|
|
||||||
alert = "test";
|
|
||||||
expr = "zfs_pool_health > 0";
|
|
||||||
for = "5m";
|
|
||||||
labels = {
|
|
||||||
severity = "critical";
|
|
||||||
};
|
|
||||||
annotations.summary = ''
|
|
||||||
Unhealthy Pool at {{ $labels.job }}
|
|
||||||
Pool {{ $labels.pool }} value {{ $value }}
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
matrix-alertmanager = {
|
|
||||||
enable = true;
|
|
||||||
matrixUser = "test@matrixtest.org";
|
|
||||||
matrixRooms = [
|
|
||||||
{
|
|
||||||
roomId = "!testroom";
|
|
||||||
receivers = [ "matrix" ];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
nodes = {
|
|
||||||
server = { };
|
|
||||||
nodeA = { };
|
|
||||||
};
|
|
||||||
|
|
||||||
testScript =
|
|
||||||
{ nodes, ... }:
|
|
||||||
''
|
|
||||||
start_all()
|
|
||||||
|
|
||||||
server.wait_for_unit("prometheus.service")
|
|
||||||
|
|
||||||
nodeA.wait_for_unit("prometheus-smartctl-exporter.service")
|
|
||||||
nodeA.wait_for_open_port(9633)
|
|
||||||
|
|
||||||
nodeA.succeed("systemctl status prometheus-smartctl-exporter.service")
|
|
||||||
nodeA.succeed("curl http://localhost:9633/metrics")
|
|
||||||
|
|
||||||
|
|
||||||
server_ip = server.succeed("ip -4 addr show eth1 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'").strip()
|
|
||||||
nodeA_ip = nodeA.succeed("ip -4 addr show eth1 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'").strip()
|
|
||||||
|
|
||||||
server.succeed(f"ping -c 3 {nodeA_ip}")
|
|
||||||
server.succeed(f"curl -v http://{nodeA_ip}:9633/metrics")
|
|
||||||
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"publickey": "age1kxsp8pa8am6k333nxs4akjqkhht8gspznmlqz4pxn35h5dj4uv5qj6q6fl",
|
|
||||||
"type": "age"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user