Compare commits

...

2 Commits

Author SHA1 Message Date
Berwn de7d950596 Format tree with treefmt 2026-06-16 16:53:00 +07:00
Berwn cf0d796bee Add treefmt formatter (nix fmt + flake check gate) 2026-06-16 16:53:00 +07:00
11 changed files with 185 additions and 54 deletions
+5 -4
View File
@@ -4,9 +4,9 @@
meta.domain = "cnx-network.internal";
inventory.machines = {
control = {};
ns1 = {};
ns2 = {};
control = { };
ns1 = { };
ns2 = { };
};
inventory.instances = {
@@ -15,7 +15,8 @@
roles.default.tags.all = { };
roles.default.settings.allowedKeys = {
"berwn" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIENAjhGQGraQoAjJzsomKP8GAmQPeGL1rNRNHgRcLqtT";
"kurogeek" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcZ/p1Ofa9liwIzPWzNtONhJ7+FUWd2lCz33r81t8+w kurogeek@kurogeek";
"kurogeek" =
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEcZ/p1Ofa9liwIzPWzNtONhJ7+FUWd2lCz33r81t8+w kurogeek@kurogeek";
};
};
Generated
+22 -1
View File
@@ -166,7 +166,8 @@
"nixpkgs": [
"clan-core",
"nixpkgs"
]
],
"treefmt-nix": "treefmt-nix_2"
}
},
"sops-nix": {
@@ -225,6 +226,26 @@
"repo": "treefmt-nix",
"type": "github"
}
},
"treefmt-nix_2": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1780220602,
"narHash": "sha256-eynAfOmbmxJnkp7YewvCEbShNnnYJ9gLLqkzsYtBPeM=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "db947814a175b7ca6ded66e21383d938df01c227",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",
+31 -17
View File
@@ -1,6 +1,8 @@
{
inputs.clan-core.url = "https://git.clan.lol/clan/clan-core/archive/25.11.tar.gz";
inputs.nixpkgs.follows = "clan-core/nixpkgs";
inputs.treefmt-nix.url = "github:numtide/treefmt-nix";
inputs.treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
outputs =
{
@@ -26,29 +28,41 @@
# };
# overlays = [];
# };
secrets.age.plugins = [
"age-plugin-yubikey"
"age-plugin-fido2-hmac"
];
secrets.age.plugins = [
"age-plugin-yubikey"
"age-plugin-fido2-hmac"
];
};
systems = [
"x86_64-linux"
"aarch64-linux"
"aarch64-darwin"
"x86_64-darwin"
];
forAllSystems = nixpkgs.lib.genAttrs systems;
pkgsFor = system: clan-core.inputs.nixpkgs.legacyPackages.${system};
treefmtFor = system: inputs.treefmt-nix.lib.evalModule (pkgsFor system) ./fmt.nix;
in
{
inherit (clan.config) nixosConfigurations nixosModules clanInternals;
clan = clan.config;
# `nix fmt` and the `nix flake check` formatting gate.
formatter = forAllSystems (system: (treefmtFor system).config.build.wrapper);
checks = forAllSystems (system: {
formatting = (treefmtFor system).config.build.check self;
});
# Add the Clan cli tool to the dev shell.
# Use "nix develop" to enter the dev shell.
devShells =
nixpkgs.lib.genAttrs
[
"x86_64-linux"
"aarch64-linux"
"aarch64-darwin"
"x86_64-darwin"
]
(system: {
default = clan-core.inputs.nixpkgs.legacyPackages.${system}.mkShell {
packages = [ clan-core.packages.${system}.clan-cli ];
};
});
devShells = forAllSystems (system: {
default = (pkgsFor system).mkShell {
packages = [
clan-core.packages.${system}.clan-cli
(treefmtFor system).config.build.wrapper
];
};
});
};
}
+32
View File
@@ -0,0 +1,32 @@
# treefmt config, evaluated per-system in flake.nix and exposed as
# `nix fmt` (the formatter) plus a `nix flake check` formatting gate.
{ ... }:
{
projectRootFile = "flake.nix";
programs = {
nixfmt.enable = true;
prettier.enable = true;
yamlfmt.enable = true;
shfmt.enable = true;
};
settings = {
on-unmatched = "fatal";
global.excludes = [
# Secrets and clan-managed state — never reformat.
"sops/*"
"vars/*"
"inventory.json"
# Generated — don't reformat (regeneration would churn the diff).
"*facter.json"
# No formatter, or reformatting would corrupt them.
"*.zone" # Knot zone files
"flake.lock"
".envrc"
".gitignore"
];
};
}
+1 -1
View File
@@ -1,7 +1,7 @@
# ---
# schema = "single-disk"
# [placeholders]
# mainDisk = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_120729781"
# mainDisk = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_120729781"
# ---
# This file was automatically generated!
# CHANGING this configuration requires wiping and reinstalling the machine
+4 -1
View File
@@ -70,6 +70,9 @@ in
"dnssec-signing" = true;
"dnssec-policy" = "cnx";
notify = [ "ns2" ];
acl = [ "acl_ns2" "acl_acme" ]; # ns2 transfers; acme_ddns key does DNS-01 updates
acl = [
"acl_ns2"
"acl_acme"
]; # ns2 transfers; acme_ddns key does DNS-01 updates
}) domains;
}
+1 -1
View File
@@ -1,7 +1,7 @@
# ---
# schema = "single-disk"
# [placeholders]
# mainDisk = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_120730960"
# mainDisk = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_120730960"
# ---
# This file was automatically generated!
# CHANGING this configuration requires wiping and reinstalling the machine
+1 -1
View File
@@ -1,7 +1,7 @@
# ---
# schema = "single-disk"
# [placeholders]
# mainDisk = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_120731321"
# mainDisk = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_120731321"
# ---
# This file was automatically generated!
# CHANGING this configuration requires wiping and reinstalling the machine
+38 -6
View File
@@ -33,17 +33,49 @@ in
# Including the key via keyFiles keeps the secret out of the Nix store.
keyFiles = [ config.clan.core.vars.generators.dns-tsig.files."tsig.conf".path ];
settings = {
server.listen = [ "0.0.0.0@53" "::@53" ];
log = [ { target = "syslog"; any = "info"; } ];
server.listen = [
"0.0.0.0@53"
"::@53"
];
log = [
{
target = "syslog";
any = "info";
}
];
remote = [
{ id = "ns1"; address = [ ns1zt ]; key = "cnx_xfr"; }
{ id = "ns2"; address = [ ns2zt ]; key = "cnx_xfr"; }
{
id = "ns1";
address = [ ns1zt ];
key = "cnx_xfr";
}
{
id = "ns2";
address = [ ns2zt ];
key = "cnx_xfr";
}
];
acl = [
{ id = "acl_ns1"; address = [ ns1zt ]; key = "cnx_xfr"; action = [ "transfer" "notify" ]; }
{ id = "acl_ns2"; address = [ ns2zt ]; key = "cnx_xfr"; action = [ "transfer" "notify" ]; }
{
id = "acl_ns1";
address = [ ns1zt ];
key = "cnx_xfr";
action = [
"transfer"
"notify"
];
}
{
id = "acl_ns2";
address = [ ns2zt ];
key = "cnx_xfr";
action = [
"transfer"
"notify"
];
}
];
};
};
+22 -4
View File
@@ -4,7 +4,10 @@
# Public SSH (22) is intentionally absent: admin access rides the ZeroTier mesh
# (inside UDP 9993), with emergency-access as the console fallback.
let
world = [ "0.0.0.0/0" "::/0" ];
world = [
"0.0.0.0/0"
"::/0"
];
zerotier = {
direction = "in";
@@ -22,14 +25,29 @@ let
};
dnsRules = [
{ direction = "in"; protocol = "udp"; port = "53"; source_ips = world; description = "DNS (UDP)"; }
{ direction = "in"; protocol = "tcp"; port = "53"; source_ips = world; description = "DNS (TCP)"; }
{
direction = "in";
protocol = "udp";
port = "53";
source_ips = world;
description = "DNS (UDP)";
}
{
direction = "in";
protocol = "tcp";
port = "53";
source_ips = world;
description = "DNS (TCP)";
}
zerotier
ping
];
in
{
"clan-control" = [ zerotier ping ];
"clan-control" = [
zerotier
ping
];
"clan-ns1" = dnsRules;
"clan-ns2" = dnsRules;
}
+28 -18
View File
@@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.cnx.hetznerFirewall;
in
@@ -29,8 +34,7 @@ in
tokenFile = lib.mkOption {
type = lib.types.path;
default = config.clan.core.vars.generators.hetzner-firewall.files.token.path;
defaultText = lib.literalExpression
"config.clan.core.vars.generators.hetzner-firewall.files.token.path";
defaultText = lib.literalExpression "config.clan.core.vars.generators.hetzner-firewall.files.token.path";
description = "File holding the Hetzner Cloud API token (Read & Write).";
};
};
@@ -48,7 +52,11 @@ in
description = "Sync Hetzner Cloud firewall rules from Nix config";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
path = [ pkgs.curl pkgs.jq pkgs.coreutils ];
path = [
pkgs.curl
pkgs.jq
pkgs.coreutils
];
environment.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
serviceConfig = {
Type = "oneshot";
@@ -71,20 +79,22 @@ in
curl -fsS -H @"$hdr" -H "Content-Type: application/json" "$@"
}
${lib.concatStringsSep "\n" (lib.mapAttrsToList (fwName: rules: ''
name=${lib.escapeShellArg fwName}
rules=${lib.escapeShellArg (builtins.toJSON rules)}
id="$(hapi "$api/firewalls?name=$name" | jq -r '.firewalls[0].id // empty')"
if [ -z "$id" ]; then
echo "hetzner-firewall: creating $name"
jq -n --arg name "$name" --argjson rules "$rules" '{name: $name, rules: $rules}' \
| hapi -X POST --data-binary @- "$api/firewalls" > /dev/null
else
echo "hetzner-firewall: setting rules on $name (id $id)"
jq -n --argjson rules "$rules" '{rules: $rules}' \
| hapi -X POST --data-binary @- "$api/firewalls/$id/actions/set_rules" > /dev/null
fi
'') cfg.firewalls)}
${lib.concatStringsSep "\n" (
lib.mapAttrsToList (fwName: rules: ''
name=${lib.escapeShellArg fwName}
rules=${lib.escapeShellArg (builtins.toJSON rules)}
id="$(hapi "$api/firewalls?name=$name" | jq -r '.firewalls[0].id // empty')"
if [ -z "$id" ]; then
echo "hetzner-firewall: creating $name"
jq -n --arg name "$name" --argjson rules "$rules" '{name: $name, rules: $rules}' \
| hapi -X POST --data-binary @- "$api/firewalls" > /dev/null
else
echo "hetzner-firewall: setting rules on $name (id $id)"
jq -n --argjson rules "$rules" '{rules: $rules}' \
| hapi -X POST --data-binary @- "$api/firewalls/$id/actions/set_rules" > /dev/null
fi
'') cfg.firewalls
)}
'';
};