Compare commits

...

34 Commits

Author SHA1 Message Date
Berwn d8bbf08c7a Add mx1 to secret vars/shared/mail-dmarc-cred/passphrase 2026-06-21 03:28:02 +07:00
Berwn e6036d9d1b Add mx1 to secret vars/shared/mail-dmarc-cred/hash 2026-06-21 03:28:01 +07:00
Berwn f7b64617b9 Update vars via generator mail-dmarc-cred for machine control 2026-06-21 03:27:56 +07:00
Berwn 60db8c60b0 Add parsedmarc DMARC report analyzer on control
Deliver cnx.email DMARC aggregate/forensic reports to a dedicated dmarc@cnx.email
mailbox on mx1 and analyze them with parsedmarc on control, storing parsed
reports in a local loopback Elasticsearch and visualizing via the auto-provisioned
Grafana dashboard. parsedmarc fetches the mailbox over IMAPS across the mesh
(mx1.cnx.email pinned to its mesh address so TLS still validates), using a shared
mail-dmarc-cred clan var so mx1's mailserver and control see the same password.
2026-06-21 03:27:23 +07:00
Berwn b8bea27a9c Update runbook docs for web01 reverse proxy and per-host ACME keys
Reflect web01 in the machines table and monitoring scrape list, note Grafana is
now also published publicly via web01's reverse proxy, add the CNX Uptime
dashboard, and document the dedicated acme_mx1/acme_web01 DNS-01 keys.
2026-06-21 03:17:51 +07:00
Berwn 415a050f6a Scrape web01 node_exporter into VictoriaMetrics
Add web01 to the mesh map and the node scrape job so it appears on the
uptime/host dashboards alongside the other hosts.
2026-06-21 03:08:56 +07:00
Berwn 3f3f4118c1 Use Singapore time (UTC+8) for mx1 and web01
Both hosts are in the Singapore region, not UTC+3.
2026-06-21 03:07:57 +07:00
Berwn dfdeb84ab8 Set time.timeZone on mx1 and web01
Both had NTP (timesyncd) enabled but no timezone, unlike control/ns1/ns2.
Default to Etc/GMT-3 to match the majority of hosts.
2026-06-21 03:07:31 +07:00
Berwn 48bf7fb250 Add web01 public reverse proxy with DNS-01 wildcard TLS
web01 terminates TLS for grafana.cnx.network and proxies to Grafana on
control over the mesh. Caddy serves a *.cnx.network wildcard cert obtained
via ACME DNS-01, using a dedicated acme_web01 TSIG key scoped on ns1 to
_acme-challenge on the cnx.network zone only. Ports 80/443 are the only
public exposure (80 just redirects); admin and the backend ride ZeroTier.

Also reload Caddy on cert renewal for both web01 and mx1, since both
reference the cert via explicit tls file paths and would otherwise keep
serving a stale cert after a silent renewal.
2026-06-21 03:05:54 +07:00
Berwn 86a2928825 update(inventory.json): Installed web01 2026-06-21 02:28:43 +07:00
Berwn f6da01ba18 Add web01 to secret vars/shared/dns-acme-web01-secret/secret 2026-06-21 02:26:44 +07:00
Berwn eeed40bcb5 Update vars via generator dns-acme-web01-rfc2136 for machine web01 2026-06-21 02:26:44 +07:00
Berwn aac8f9d8e6 Update vars via generator dns-acme-web01-knot for machine ns1 2026-06-21 02:26:43 +07:00
Berwn f5874bc337 Update vars via generator zerotier for machine web01 2026-06-21 02:26:33 +07:00
Berwn 2481d4bf92 Update vars via generator tor_tor for machine web01 2026-06-21 02:26:32 +07:00
Berwn 2d8096ee57 Update vars via generator state-version for machine web01 2026-06-21 02:26:30 +07:00
Berwn 1a4a749d78 Update vars via generator root-password for machine web01 2026-06-21 02:26:30 +07:00
Berwn 1c779d8013 Update vars via generator openssh for machine web01 2026-06-21 02:26:30 +07:00
Berwn 9c4e036b09 Update vars via generator emergency-access for machine web01 2026-06-21 02:26:30 +07:00
Berwn 8139b91fbc Add machine web01 to secrets 2026-06-21 02:26:30 +07:00
Berwn c436389619 Update secret web01-age.key 2026-06-21 02:26:29 +07:00
Berwn 9fc97e65b2 Update vars via generator dns-acme-web01-secret for machine ns1 2026-06-21 02:26:29 +07:00
Berwn bd84bf7c85 Set disk schema of machine: web01 to single-disk 2026-06-21 02:25:24 +07:00
Berwn 848dc0dff7 machines/web01/facter.json: update hardware configuration 2026-06-21 02:23:00 +07:00
Berwn 95aff44f86 Add machine web01 2026-06-21 01:58:59 +07:00
Berwn f42569e992 Add provisioned Grafana uptime dashboard for all hosts 2026-06-21 01:57:08 +07:00
Berwn 1dd3aadb97 Add mail.cnx.email client alias as a cert SAN
A mail.cnx.email CNAME (-> mx1.cnx.email) lets clients (Thunderbird etc.)
use a friendly hostname for submission/IMAP. To avoid a TLS name
mismatch the cert now carries mail.cnx.email as a SAN, so the acme_mx1
key is authorized to write _acme-challenge.mail too. The MX still points
at mx1.cnx.email and --reuse-key keeps the DANE TLSA digest valid across
the re-issue.
2026-06-18 15:01:03 +07:00
Berwn dc21348727 Format drifted files to satisfy the treefmt flake-check gate
Pure formatting (nixfmt/prettier/yamlfmt); no behavior change. These
files predate the current treefmt config and were failing nix flake
check; reformatting them makes the gate green again.
2026-06-18 14:49:48 +07:00
Berwn 1cb6f39ea2 Add declarative SNM mail stack on mx1 with DNS-01, DANE, MTA-STS
mx1 runs Simple NixOS Mailserver (Postfix/Dovecot/Rspamd/OpenDKIM) for
cnx.email. The TLS cert is obtained via ACME DNS-01 using a dedicated,
scoped TSIG key (acme_mx1) that ns1 authorizes for only
_acme-challenge.mx1 and _acme-challenge.mta-sts on the cnx.email zone, so
the credential can write nothing else. Mailbox passwords are auto-minted
by a clan vars generator (four-word passphrase + number).

DANE TLSA (3 1 1) is published for _25._tcp.mx1; --reuse-key keeps the
key digest stable across renewals. MTA-STS is enforced via a Caddy vhost
serving the policy on :443 from the same cert (mta-sts SAN). Firewall
opens 25/587/465/143/993/443; 80 stays closed.
2026-06-18 14:47:20 +07:00
Berwn 026a26dd53 Add ns1 to secret vars/shared/dns-acme-mx1-secret/secret 2026-06-18 14:11:40 +07:00
Berwn 7e5d50b260 Update vars via generator dns-acme-mx1-knot for machine ns1 2026-06-18 14:11:40 +07:00
Berwn 312de984c1 Update vars via generator dns-acme-rfc2136 for machine mx1 2026-06-18 14:11:40 +07:00
Berwn d76aa8cc8d Update vars via generator mail-passwd-postmaster-at-cnx-email for machine mx1 2026-06-18 14:11:36 +07:00
Berwn 0a78cad06e Update vars via generator dns-acme-mx1-secret for machine mx1 2026-06-18 14:11:36 +07:00
109 changed files with 4518 additions and 54 deletions
+1
View File
@@ -24,6 +24,7 @@ in
ns1 = { };
ns2 = { };
mx1 = { };
web01 = { };
};
inventory.instances = {
+18 -6
View File
@@ -61,13 +61,25 @@ requires re-submitting the DS.
## ACME DNS-01
A dedicated TSIG key (`acme_ddns`), scoped by `acl_acme` to `TXT` updates at or
under `_acme-challenge.<zone>` on `ns1` only. Knot signs the record and transfers
it to `ns2`, which never needs this key. Retrieve the client config with:
Certificates are issued by `_acme-challenge` TXT updates that `ns1` accepts over
TSIG, signs, and transfers to `ns2` (which never needs these keys). Each consumer
gets its **own** key, scoped by an ACL to exactly the owner names it needs and
attached only to the zone it lives in — so a leaked key can write nothing but its
own challenges.
```
clan vars get ns1 dns-acme-tsig/acme.conf
```
- **`acme_ddns`** (`acl_acme`) — the general key, scoped to `TXT` at or under
`_acme-challenge.<zone>` and attached to every zone. Client config:
```
clan vars get ns1 dns-acme-tsig/acme.conf
```
- **`acme_mx1`** (`acl_acme_mx1`) — held only by `mx1`, scoped to
`_acme-challenge.{mx1,mta-sts,mail}` and attached only to `cnx.email` (the mail
cert plus its MTA-STS and client-alias SANs). Secret shared via the
`dns-acme-mx1-secret` generator.
- **`acme_web01`** (`acl_acme_web01`) — held only by `web01`, scoped to
`_acme-challenge` and attached only to `cnx.network` (where the wildcard
`*.cnx.network` challenge lands, at the apex). Secret shared via the
`dns-acme-web01-secret` generator.
## Runbook: stale secondary
+34 -7
View File
@@ -1,6 +1,7 @@
# Monitoring
Metrics and dashboards live on `control`, reachable only over the ZeroTier mesh.
Metrics and logs live on `control` over the ZeroTier mesh; the Grafana dashboards
are also published publicly through `web01` (see [Dashboards](#dashboards)).
## Collection
@@ -18,8 +19,8 @@ Metrics and dashboards live on `control`, reachable only over the ZeroTier mesh.
## Storage & scraping
**VictoriaMetrics** on `control`, bound to `127.0.0.1:8428`, 180-day retention
(`modules/monitoring/server.nix`). It scrapes `control` over loopback and `ns1`/
`ns2` over the mesh.
(`modules/monitoring/server.nix`). It scrapes `control` over loopback and
`ns1`/`ns2`/`mx1`/`web01` over the mesh.
> The scraper dials IPv4-only by default, so mesh (IPv6) targets need
> `extraOptions = [ "-enableTCP6" ]`. Without it, ns1/ns2 are dropped with
@@ -31,8 +32,10 @@ Metrics and dashboards live on `control`, reachable only over the ZeroTier mesh.
## Dashboards
**Grafana** on `control` (`:3000`), mesh-only, anonymous access disabled. The
admin password is a clan var:
**Grafana** on `control` (`:3000`), anonymous access disabled. Reachable directly
over the mesh, and publicly at `https://grafana.cnx.network` via `web01`'s reverse
proxy (TLS termination — see [Overview](./overview.md)). The admin password is a
clan var:
```
clan vars get control grafana-admin/password
@@ -46,6 +49,30 @@ there is picked up):
outside-in DNS probes.
- **CNX Backups** (`backups.json`) — borgbackup job health, time since the last
run, and per-job state. See [Backups](./backups.md).
- **CNX Uptime** (`uptime.json`) — per-host up/down status, current uptime,
availability over the selected window, and up/down history. Label-driven, so
every scraped host appears automatically.
- **parsedmarc** — DMARC aggregate/forensic report viewer. Auto-provisioned by
the `parsedmarc` module (not from `dashboards/`); reads its own Elasticsearch
datasource, not VictoriaMetrics. See [DMARC reports](#dmarc-reports) below.
## DMARC reports
The `cnx.email` DMARC record (`rua`/`ruf`) points at the `dmarc@cnx.email`
mailbox on `mx1`. **parsedmarc** on `control` (`modules/monitoring/parsedmarc.nix`)
polls that mailbox over IMAPS, parses the XML reports, and stores them in a local
**Elasticsearch** (`127.0.0.1:9200`, loopback-only); Grafana renders them via the
auto-provisioned parsedmarc dashboard + Elasticsearch datasource.
The IMAP fetch rides the **mesh**, not the public net: `control` pins
`mx1.cnx.email` to mx1's mesh address in `/etc/hosts`, so TLS still validates
against the public cert while the bytes stay on the overlay. The mailbox
passphrase is the shared `mail-dmarc-cred` clan var (so both mx1's mailserver and
control's parsedmarc see the same value):
```
clan vars get mx1 mail-dmarc-cred/passphrase
```
## Logs
@@ -53,8 +80,8 @@ there is picked up):
(`modules/monitoring/server.nix`). All three hosts ship journald to it via
systemd's own `services.journald.upload` → the `/insert/journald` endpoint
(`modules/monitoring/exporters.nix`); no extra agent. `control` uploads over
loopback so its logs survive a mesh outage, `ns1`/`ns2` push over the mesh, and
9428 is firewall-scoped to the mesh like everything else.
loopback so its logs survive a mesh outage, the other hosts push over the mesh,
and 9428 is firewall-scoped to the mesh like everything else.
> Same IPv4-only default as the scraper: VictoriaLogs binds `0.0.0.0:9428` for a
> bare `:9428`, so mesh (IPv6) pushes from ns1/ns2 are refused until you pass
+7 -6
View File
@@ -6,12 +6,13 @@ this book is built from `docs/` and served on `control` over the ZeroTier mesh.
## Machines
| Machine | Role | Public IPv4 | Public IPv6 |
| --------- | -------------------------------------- | ---------------- | --------------------------- |
| `control` | ZeroTier controller, monitoring, docs | `77.42.68.181` | `2a01:4f9:c013:e6d0::1` |
| `ns1` | Knot DNS **primary** (master) | `46.224.170.206` | `2a01:4f8:c014:b5c5::1` |
| `ns2` | Knot DNS **secondary** (slave) | `157.180.70.82` | `2a01:4f9:c014:6d87::1` |
| `mx1` | Mail server (**MX** for cnx.email) | `5.223.65.38` | `2a01:4ff:2f0:1963::1` |
| Machine | Role | Public IPv4 | Public IPv6 |
| --------- | -------------------------------------- | ---------------- | ----------------------- |
| `control` | ZeroTier controller, monitoring, docs | `77.42.68.181` | `2a01:4f9:c013:e6d0::1` |
| `ns1` | Knot DNS **primary** (master) | `46.224.170.206` | `2a01:4f8:c014:b5c5::1` |
| `ns2` | Knot DNS **secondary** (slave) | `157.180.70.82` | `2a01:4f9:c014:6d87::1` |
| `mx1` | Mail server (**MX** for cnx.email) | `5.223.65.38` | `2a01:4ff:2f0:1963::1` |
| `web01` | Public reverse proxy (TLS termination) | `5.223.55.246` | `2a01:4ff:2f0:2d8f::1` |
## Access
Generated
+105
View File
@@ -1,5 +1,21 @@
{
"nodes": {
"blobs": {
"flake": false,
"locked": {
"lastModified": 1604995301,
"narHash": "sha256-wcLzgLec6SGJA8fx1OEN1yV/Py5b+U5iyYpksUY/yLw=",
"owner": "simple-nixos-mailserver",
"repo": "blobs",
"rev": "2cccdf1ca48316f2cfd1c9a0017e8de5a7156265",
"type": "gitlab"
},
"original": {
"owner": "simple-nixos-mailserver",
"repo": "blobs",
"type": "gitlab"
}
},
"clan-core": {
"inputs": {
"data-mesher": "data-mesher",
@@ -73,6 +89,22 @@
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
@@ -94,6 +126,54 @@
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": [
"nixos-mailserver",
"flake-compat"
],
"gitignore": "gitignore",
"nixpkgs": [
"nixos-mailserver",
"nixpkgs"
]
},
"locked": {
"lastModified": 1772893680,
"narHash": "sha256-JDqZMgxUTCq85ObSaFw0HhE+lvdOre1lx9iI6vYyOEs=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "8baab586afc9c9b57645a734c820e4ac0a604af9",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"nixos-mailserver",
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nix-darwin": {
"inputs": {
"nixpkgs": [
@@ -144,6 +224,30 @@
"type": "github"
}
},
"nixos-mailserver": {
"inputs": {
"blobs": "blobs",
"flake-compat": "flake-compat",
"git-hooks": "git-hooks",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1773912645,
"narHash": "sha256-QHzRqq6gh+t3F/QU9DkP7X63dDDcuIQmaDz12p7ANTg=",
"owner": "simple-nixos-mailserver",
"repo": "nixos-mailserver",
"rev": "25e6dbb8fca3b6e779c5a46fd03bd760b2165bb5",
"type": "gitlab"
},
"original": {
"owner": "simple-nixos-mailserver",
"ref": "nixos-25.11",
"repo": "nixos-mailserver",
"type": "gitlab"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1778003029,
@@ -163,6 +267,7 @@
"root": {
"inputs": {
"clan-core": "clan-core",
"nixos-mailserver": "nixos-mailserver",
"nixpkgs": [
"clan-core",
"nixpkgs"
+3
View File
@@ -3,6 +3,9 @@
inputs.nixpkgs.follows = "clan-core/nixpkgs";
inputs.treefmt-nix.url = "github:numtide/treefmt-nix";
inputs.treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
# Simple NixOS Mailserver, pinned to the branch matching clan-core's nixpkgs.
inputs.nixos-mailserver.url = "gitlab:simple-nixos-mailserver/nixos-mailserver/nixos-25.11";
inputs.nixos-mailserver.inputs.nixpkgs.follows = "nixpkgs";
outputs =
{
+1
View File
@@ -24,6 +24,7 @@
# No formatter, or reformatting would corrupt them.
"*.zone" # Knot zone files
"docs/book.toml" # mdBook config; no TOML formatter enabled
"flake.lock"
".envrc"
".gitignore"
+3
View File
@@ -11,6 +11,9 @@
},
"mx1": {
"installedAt": 1781757322
},
"web01": {
"installedAt": 1781983723
}
}
}
+1
View File
@@ -10,6 +10,7 @@ in
../../modules/monitoring/server.nix
../../modules/monitoring/blackbox.nix
../../modules/monitoring/alerts.nix
../../modules/monitoring/parsedmarc.nix
../../modules/docs.nix
];
+4 -4
View File
@@ -1,9 +1,11 @@
{ config, ... }:
{ config, inputs, ... }:
let
hosts = import ../../modules/hosts.nix;
in
{
imports = [
inputs.nixos-mailserver.nixosModules.default
../../modules/mail.nix
../../modules/static-ipv6.nix
../../modules/monitoring/exporters.nix
];
@@ -16,8 +18,6 @@ in
address = hosts.${config.networking.hostName}.ipv6;
};
time.timeZone = "Etc/GMT-8"; # UTC+8 (Singapore, fixed offset, no DST)
services.timesyncd.enable = true;
# Mail host backing the cnx.email MX (mx1.cnx.email -> 5.223.65.38).
# SMTP/IMAP services to be configured.
}
+1 -1
View File
@@ -1,7 +1,7 @@
# ---
# schema = "single-disk"
# [placeholders]
# mainDisk = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_117494657"
# mainDisk = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_117494657"
# ---
# This file was automatically generated!
# CHANGING this configuration requires wiping and reinstalling the machine
+79 -7
View File
@@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
domains = import ../../modules/dns/domains.nix;
mesh = import ../../modules/mesh-hosts.nix { inherit config lib; };
@@ -7,6 +12,8 @@ in
{
imports = [
../../modules/dns/authoritative.nix
../../modules/dns/acme-mx1-secret.nix
../../modules/dns/acme-web01-secret.nix
../../modules/static-ipv6.nix
../../modules/monitoring/exporters.nix
];
@@ -33,11 +40,9 @@ in
time.timeZone = "Etc/GMT-1"; # UTC+1 (fixed offset, no DST)
services.timesyncd.enable = true;
# ACME DNS-01 (RFC 2136): a dedicated TSIG key, scoped to ns1 only, that an
# external ACME client uses to write _acme-challenge TXT records. acl_acme
# (referenced by each zone below) limits the key to TXT updates at or under
# _acme-challenge.<zone>; Knot then signs the record and transfers it to ns2,
# which never needs this key. Retrieve the secret for the client with:
# ACME DNS-01 (RFC 2136), general key. A dedicated TSIG key scoped by acl_acme
# (referenced by every zone below) to TXT updates at or under _acme-challenge.
# Retrieve the client config with:
# clan vars get ns1 dns-acme-tsig/acme.conf
clan.core.vars.generators.dns-acme-tsig = {
files."acme.conf" = {
@@ -51,8 +56,47 @@ in
'';
};
# ACME DNS-01, dedicated mx1 key. A *separate* TSIG key (acme_mx1) that only
# mx1 holds, rendered from the shared secret (generator dns-acme-mx1-secret,
# imported above). acl_acme_mx1 scopes it to TXT updates at exactly
# _acme-challenge.{mx1,mta-sts,mail} (the mail cert and its MTA-STS + client-
# alias SANs), and it is attached only to the cnx.email zone below — so this
# credential can write nothing but mx1's own cert challenges.
clan.core.vars.generators.dns-acme-mx1-knot = {
files."acme.conf" = {
secret = true;
owner = "knot";
group = "knot";
};
dependencies = [ "dns-acme-mx1-secret" ];
script = ''
printf 'key:\n - id: acme_mx1\n algorithm: hmac-sha256\n secret: %s\n' \
"$(cat "$in"/dns-acme-mx1-secret/secret)" > "$out"/acme.conf
'';
};
# ACME DNS-01, dedicated web01 key. A *separate* TSIG key (acme_web01) that only
# web01 holds, rendered from the shared secret (generator dns-acme-web01-secret,
# imported above). acl_acme_web01 scopes it to TXT updates at _acme-challenge on
# the cnx.network zone — the owner the wildcard *.cnx.network challenge uses — so
# this credential can write nothing but web01's own cert challenges.
clan.core.vars.generators.dns-acme-web01-knot = {
files."acme.conf" = {
secret = true;
owner = "knot";
group = "knot";
};
dependencies = [ "dns-acme-web01-secret" ];
script = ''
printf 'key:\n - id: acme_web01\n algorithm: hmac-sha256\n secret: %s\n' \
"$(cat "$in"/dns-acme-web01-secret/secret)" > "$out"/acme.conf
'';
};
services.knot.keyFiles = [
config.clan.core.vars.generators.dns-acme-tsig.files."acme.conf".path
config.clan.core.vars.generators.dns-acme-mx1-knot.files."acme.conf".path
config.clan.core.vars.generators.dns-acme-web01-knot.files."acme.conf".path
];
services.knot.settings.acl = [
@@ -65,6 +109,30 @@ in
"update-owner-match" = "sub-or-equal";
"update-owner-name" = [ "_acme-challenge" ];
}
{
id = "acl_acme_mx1";
key = "acme_mx1";
action = [ "update" ];
"update-type" = [ "TXT" ];
"update-owner" = "name";
"update-owner-match" = "sub-or-equal";
"update-owner-name" = [
"_acme-challenge.mx1"
"_acme-challenge.mta-sts"
"_acme-challenge.mail"
];
}
{
id = "acl_acme_web01";
key = "acme_web01";
action = [ "update" ];
"update-type" = [ "TXT" ];
"update-owner" = "name";
"update-owner-match" = "sub-or-equal";
# Wildcard *.cnx.network places its challenge at _acme-challenge.cnx.network,
# i.e. _acme-challenge at the cnx.network apex (where this acl is attached).
"update-owner-name" = [ "_acme-challenge" ];
}
];
# Automatic DNSSEC signing policy (primary only). ECDSA P-256/SHA-256 with
@@ -93,9 +161,13 @@ in
"dnssec-signing" = true;
"dnssec-policy" = "cnx";
notify = [ "ns2" ];
# ns2 transfers; acme_ddns does general DNS-01 updates. The dedicated
# acme_mx1 key is attached only to cnx.email, so it can't touch other zones.
acl = [
"acl_ns2"
"acl_acme"
]; # ns2 transfers; acme_ddns key does DNS-01 updates
]
++ lib.optionals (d == "cnx.email") [ "acl_acme_mx1" ]
++ lib.optionals (d == "cnx.network") [ "acl_acme_web01" ];
}) domains;
}
+22
View File
@@ -0,0 +1,22 @@
{ config, ... }:
let
hosts = import ../../modules/hosts.nix;
in
{
imports = [
../../modules/static-ipv6.nix
../../modules/monitoring/exporters.nix
../../modules/web-proxy.nix
];
clan.core.sops.defaultGroups = [ "admins" ];
# Public IPv6 (from modules/hosts.nix); SLAAC doesn't bring it up here.
cnx.staticIPv6 = {
enable = true;
address = hosts.${config.networking.hostName}.ipv6;
};
time.timeZone = "Etc/GMT-8"; # UTC+8 (Singapore, fixed offset, no DST)
services.timesyncd.enable = true;
}
+50
View File
@@ -0,0 +1,50 @@
# ---
# schema = "single-disk"
# [placeholders]
# mainDisk = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_108706511"
# ---
# This file was automatically generated!
# CHANGING this configuration requires wiping and reinstalling the machine
{
boot.loader.grub.efiSupport = true;
boot.loader.grub.efiInstallAsRemovable = true;
boot.loader.grub.enable = true;
disko.devices = {
disk = {
main = {
name = "main-ddd46ebf135244608078712d6ec76691";
device = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_108706511";
type = "disk";
content = {
type = "gpt";
partitions = {
"boot" = {
size = "1M";
type = "EF02"; # for grub MBR
priority = 1;
};
ESP = {
type = "EF00";
size = "500M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}
File diff suppressed because it is too large Load Diff
+19
View File
@@ -0,0 +1,19 @@
# Shared TSIG secret for the dedicated acme_mx1 key.
#
# This key lets mx1 — and only mx1 — write _acme-challenge.mx1.cnx.email TXT
# records on ns1 to obtain its mail TLS cert via ACME DNS-01. ns1 scopes it with
# acl_acme_mx1 (attached only to the cnx.email zone) so the credential can touch
# nothing else. ns1 renders this secret into a Knot key file; mx1 into a lego
# rfc2136 env file; both must carry the same secret, hence one shared generator
# with a per-host renderer that depends on it. Imported by ns1 and (via mail.nix)
# mx1.
{ pkgs, ... }:
{
clan.core.vars.generators.dns-acme-mx1-secret = {
share = true;
files."secret".secret = true;
runtimeInputs = [ pkgs.openssl ];
# 32 random bytes, base64 — a valid hmac-sha256 TSIG secret.
script = ''openssl rand -base64 32 | tr -d '\n' > "$out"/secret'';
};
}
+19
View File
@@ -0,0 +1,19 @@
# Shared TSIG secret for the dedicated acme_web01 key.
#
# This key lets web01 — and only web01 — write _acme-challenge.cnx.network TXT
# records on ns1 to obtain its wildcard (*.cnx.network) TLS cert via ACME DNS-01.
# ns1 scopes it with acl_acme_web01 (attached only to the cnx.network zone) so the
# credential can touch nothing else. ns1 renders this secret into a Knot key file;
# web01 into a lego rfc2136 env file; both must carry the same secret, hence one
# shared generator with a per-host renderer that depends on it. Imported by ns1
# and (via web-proxy.nix) web01.
{ pkgs, ... }:
{
clan.core.vars.generators.dns-acme-web01-secret = {
share = true;
files."secret".secret = true;
runtimeInputs = [ pkgs.openssl ];
# 32 random bytes, base64 — a valid hmac-sha256 TSIG secret.
script = ''openssl rand -base64 32 | tr -d '\n' > "$out"/secret'';
};
}
+6 -1
View File
@@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
# ZeroTier addresses — zone transfers run over the mesh, not the public net.
mesh = import ../mesh-hosts.nix { inherit config lib; };
+29 -1
View File
@@ -13,6 +13,34 @@ $TTL 3600
; ---- Mail ----
mx1 IN A 5.223.65.38
mx1 IN AAAA 2a01:4ff:2f0:1963::1
; Client-facing alias for IMAP/submission (Thunderbird etc.); the cert carries
; mail.cnx.email as a SAN. The MX must never point here (CNAMEs are illegal MX
; targets) — server-to-server delivery and DANE stay on mx1.cnx.email.
mail IN CNAME mx1.cnx.email.
@ IN MX 10 mx1.cnx.email.
@ IN TXT "v=spf1 mx -all"
_dmarc IN TXT "v=DMARC1; p=quarantine; rua=mailto:postmaster@cnx.email"
; Aggregate (rua) + forensic (ruf) reports go to the dmarc@cnx.email mailbox,
; which parsedmarc on control polls and feeds into Grafana. fo=1 asks reporters
; to send a forensic report on any SPF/DKIM failure.
_dmarc IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@cnx.email; ruf=mailto:dmarc@cnx.email; fo=1"
; ---- DANE / TLSA ----
; "3 1 1" = DANE-EE, SPKI, SHA-256: the digest of mx1's certificate public key.
; Valid because the zone is DNSSEC-signed and the lego cert uses --reuse-key, so
; the key (and thus this digest) is stable across renewals. Compute it AFTER the
; first issuance and paste the hex below:
; ssh mx1 'openssl x509 -in /var/lib/acme/mx1.cnx.email/cert.pem -noout -pubkey \
; | openssl pkey -pubin -outform DER | openssl dgst -sha256 -binary | xxd -p -c256'
_25._tcp.mx1 IN TLSA 3 1 1 bd9a51f60b6d2dd20f18b3553d2795053ac52f87567a46bc892006bb58506404
; ---- MTA-STS ----
; Policy host (A/AAAA point at mx1); the _mta-sts TXT id MUST be bumped whenever
; the policy file in modules/mail.nix changes, or senders keep the cached policy.
mta-sts IN A 5.223.65.38
mta-sts IN AAAA 2a01:4ff:2f0:1963::1
_mta-sts IN TXT "v=STSv1; id=2026061801"
mail._domainkey IN TXT ( "v=DKIM1; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr9QxTs5dLtY76bo156+Tp0GUoE554rMwIooIYa2MMYHNs8zPb0thFmaCKGAINdHKNIq2phXAlk51iBTfdqXjx7gVWSrs+ftykqO3b5hUjgImsgqPWGUTzy5/bUgcDELiD9KKEyKYD3+ebZEw6d0uvBvEsA6a1CPzOsufoCDtyKjByCuQzkCBrK25TUHFolGvEYcZexR0LSF+8hMss"
"xyw9NYiPpTXVCWQJnrZZpuOBiX0K2l5CAXVyuT/B5RcBXlAUhBTp3390VEhL0wAZMTOnvtvBYK3NnsTIh96fkh6MfWmre7Fi9hEq//xGf40N5/aomMjJrJdqFZJLZpDotb/XwIDAQAB"
)
+7
View File
@@ -25,3 +25,10 @@ control IN AAAA fd06:1bad:ece2:92ad:ba99:9306:1bad:ece2
;@ IN A <web-ipv4>
;www IN CNAME cnx.network.
monitor IN A 5.223.66.36
; ---- web01 (public reverse proxy / TLS termination) ----
; Serves a wildcard *.cnx.network TLS cert (ACME DNS-01) and forwards to internal
; services over the mesh. Add a vhost in modules/web-proxy.nix and a CNAME here.
web01 IN A 5.223.55.246
web01 IN AAAA 2a01:4ff:2f0:2d8f::1
grafana IN CNAME web01.cnx.network.
+40 -8
View File
@@ -24,16 +24,45 @@ let
description = "ICMP (ping / PMTUD)";
};
# Inbound mail only. mx1 is the MX for cnx.email, so other servers deliver on
# 25. Submission (587/465) and IMAP (993) stay closed until the mail stack and
# mailboxes exist — admin access rides the mesh, same as the other hosts.
smtp = {
# Public mail ports for mx1 (MX for cnx.email). 25 is server-to-server
# delivery; 587/465 are client submission; 143/993 are IMAP. 443 serves only the
# MTA-STS policy (https://mta-sts.cnx.email/.well-known/mta-sts.txt); the cert
# itself uses ACME DNS-01 so port 80 stays closed. Admin still rides the mesh.
mailPort = port: description: {
direction = "in";
protocol = "tcp";
port = "25";
inherit port;
source_ips = world;
description = "SMTP (inbound mail)";
inherit description;
};
mailRules = [
(mailPort "25" "SMTP (inbound mail)")
(mailPort "587" "Submission (STARTTLS)")
(mailPort "465" "Submission (implicit TLS)")
(mailPort "143" "IMAP (STARTTLS)")
(mailPort "993" "IMAP (implicit TLS)")
(mailPort "443" "MTA-STS policy (HTTPS)")
];
# web01 is a public reverse proxy with TLS termination. 443 serves the proxy;
# 80 only carries Caddy's HTTP->HTTPS redirect (the cert uses ACME DNS-01, not
# HTTP-01). Admin rides the mesh.
webRules = [
{
direction = "in";
protocol = "tcp";
port = "80";
source_ips = world;
description = "HTTP (redirect to HTTPS)";
}
{
direction = "in";
protocol = "tcp";
port = "443";
source_ips = world;
description = "HTTPS (reverse proxy / TLS termination)";
}
];
dnsRules = [
{
@@ -61,8 +90,11 @@ in
];
"clan-ns1" = dnsRules;
"clan-ns2" = dnsRules;
"clan-mx1" = [
smtp
"clan-mx1" = mailRules ++ [
zerotier
ping
];
"clan-web01" = webRules ++ [
zerotier
ping
];
+4
View File
@@ -25,4 +25,8 @@
ipv4 = "5.223.65.38";
ipv6 = "2a01:4ff:2f0:1963::1";
};
web01 = {
ipv4 = "5.223.55.246";
ipv6 = "2a01:4ff:2f0:2d8f::1";
};
}
+26
View File
@@ -0,0 +1,26 @@
# Shared credential for the dmarc@cnx.email mailbox.
#
# DMARC aggregate/forensic reports are delivered to dmarc@cnx.email on mx1;
# parsedmarc on control fetches them over IMAPS across the mesh and needs the
# *plaintext* passphrase, while mx1's mailserver only needs the sha-512 hash.
# clan vars secrets are per-machine, so this generator is shared (share = true)
# to make the same value available on both hosts. Files are root-owned: SNM reads
# the hash as root, and parsedmarc's ExecStartPre reads the passphrase as root.
# Imported by mx1 (via mail.nix) and control (via monitoring/parsedmarc.nix).
{ pkgs, ... }:
{
clan.core.vars.generators.mail-dmarc-cred = {
share = true;
files."passphrase".secret = true; # read by parsedmarc on control
files."hash".secret = true; # consumed by the mailserver on mx1
runtimeInputs = [
pkgs.xkcdpass
pkgs.mkpasswd
];
script = ''
pass="$(xkcdpass --numwords=4 --delimiter=- --case=lower)-$((RANDOM % 90 + 10))"
printf '%s' "$pass" > "$out"/passphrase
printf '%s' "$pass" | mkpasswd -s -m sha-512 > "$out"/hash
'';
};
}
+161
View File
@@ -0,0 +1,161 @@
# Declarative mail stack for mx1 (Simple NixOS Mailserver: Postfix + Dovecot +
# Rspamd + OpenDKIM). Imported by machines/mx1 alongside the SNM flake module.
#
# Mailboxes are virtual (not system users): each address below is a login account
# whose password is auto-generated by a clan vars generator as a four-word
# passphrase with a trailing number (e.g. otter-lantern-cobalt-driftwood-42). The
# generator stores both the passphrase and its sha-512 hash. To add a mailbox:
# append the address to `accounts`, run `clan vars generate mx1`, redeploy mx1,
# then hand the passphrase to the user:
# clan vars get mx1 mail-passwd-<addr>/passphrase
# (addr with @ and . replaced by -at- and -, e.g. mail-passwd-postmaster-at-cnx-email)
{
config,
lib,
pkgs,
...
}:
let
hosts = import ./hosts.nix;
fqdn = "mx1.cnx.email";
mtaStsHost = "mta-sts.cnx.email";
# Client-facing alias (CNAME -> mx1) so Thunderbird etc. can use mail.cnx.email
# for submission/IMAP; added as a cert SAN so TLS validates against that name.
clientHost = "mail.cnx.email";
# MTA-STS policy served at https://mta-sts.cnx.email/.well-known/mta-sts.txt.
# enforce = a sending MTA that fetched this must use a valid, MX-matching TLS
# cert or refuse to deliver. Bump the _mta-sts TXT id (in the zone) whenever
# this changes.
mtaStsPolicy = pkgs.writeText "mta-sts.txt" ''
version: STSv1
mode: enforce
mx: ${fqdn}
max_age: 604800
'';
# The mailboxes mx1 serves. postmaster is required by RFC 5321.
accounts = [
"postmaster@cnx.email"
];
genName = addr: "mail-passwd-" + lib.replaceStrings [ "@" "." ] [ "-at-" "-" ] addr;
passwdGenerators = lib.listToAttrs (
map (addr: {
name = genName addr;
value = {
files."passphrase".secret = true; # retrievable to hand to the user
files."hash".secret = true; # consumed by SNM's hashedPasswordFile
runtimeInputs = [
pkgs.xkcdpass
pkgs.mkpasswd
];
script = ''
pass="$(xkcdpass --numwords=4 --delimiter=- --case=lower)-$((RANDOM % 90 + 10))"
printf '%s' "$pass" > "$out"/passphrase
printf '%s' "$pass" | mkpasswd -s -m sha-512 > "$out"/hash
'';
};
}) accounts
);
loginAccounts =
lib.listToAttrs (
map (addr: {
name = addr;
value.hashedPasswordFile = config.clan.core.vars.generators.${genName addr}.files."hash".path;
}) accounts
)
// {
# DMARC report inbox (rua/ruf target in the cnx.email zone). Its password
# comes from the *shared* mail-dmarc-cred generator instead of the per-machine
# set above, so parsedmarc on control can read the same passphrase over the
# mesh. Retrieve it with: clan vars get mx1 mail-dmarc-cred/passphrase
"dmarc@cnx.email".hashedPasswordFile =
config.clan.core.vars.generators.mail-dmarc-cred.files."hash".path;
};
in
{
imports = [
./dns/acme-mx1-secret.nix
./mail-dmarc-cred.nix
];
clan.core.vars.generators = passwdGenerators // {
# Render the shared acme_mx1 TSIG secret into a lego rfc2136 env file. lego
# (via security.acme below) uses it to write the _acme-challenge.mx1.cnx.email
# TXT record to ns1, which authorizes the acme_mx1 key for exactly that owner.
dns-acme-rfc2136 = {
files."rfc2136.env".secret = true; # root-owned; systemd reads it as root
dependencies = [ "dns-acme-mx1-secret" ];
script = ''
printf 'RFC2136_NAMESERVER=${hosts.ns1.ipv4}:53\nRFC2136_TSIG_ALGORITHM=hmac-sha256.\nRFC2136_TSIG_KEY=acme_mx1\nRFC2136_TSIG_SECRET=%s\n' \
"$(cat "$in"/dns-acme-mx1-secret/secret)" > "$out"/rfc2136.env
'';
};
};
mailserver = {
enable = true;
# Fresh install: declare the latest layout the nixos-25.11 branch ships (3),
# so SNM uses the current dovecot mail directory layout with nothing to migrate.
stateVersion = 3;
inherit fqdn;
domains = [ "cnx.email" ];
inherit loginAccounts;
# Consume a security.acme cert we obtain ourselves via DNS-01 (below); no
# web server and no inbound HTTP needed, so port 80 stays closed. Add the
# MTA-STS host as a SAN so the one cert also covers the policy endpoint.
certificateScheme = "acme";
certificateDomains = [
mtaStsHost
clientHost
];
dkimSelector = "mail";
};
security.acme = {
acceptTerms = true;
defaults.email = "postmaster@cnx.email";
certs.${fqdn} = {
dnsProvider = "rfc2136";
environmentFile = config.clan.core.vars.generators.dns-acme-rfc2136.files."rfc2136.env".path;
# ns1 is the only nameserver that accepts the acme_mx1 UPDATE; check
# propagation against it directly rather than a public resolver.
dnsResolver = "${hosts.ns1.ipv4}:53";
# Keep the private key fixed across renewals so the DANE TLSA "3 1 1"
# record (public-key digest, published in the zone) stays valid.
extraLegoRenewFlags = [ "--reuse-key" ];
# Caddy serves the MTA-STS endpoint from explicit cert file paths, so it
# won't notice a renewal on its own — reload it whenever the cert changes.
# (Merges with the postfix/dovecot reloads SNM wires up for this cert.)
reloadServices = [ "caddy.service" ];
};
};
# The mail cert is owned group=acme (SNM adds postfix/dovecot); Caddy serves the
# MTA-STS endpoint from the same cert, so it needs to read the key too.
users.users.caddy.extraGroups = [ "acme" ];
# MTA-STS policy endpoint, served by Caddy (same web server as control's docs).
# The explicit `tls cert key` points at the lego-issued mail cert (which carries
# mta-sts.cnx.email as a SAN) and disables Caddy's automatic ACME, so no extra
# issuance happens and the DANE TLSA key stays stable. Only :443 is opened.
services.caddy = {
enable = true;
virtualHosts.${mtaStsHost}.extraConfig = ''
tls /var/lib/acme/${fqdn}/cert.pem /var/lib/acme/${fqdn}/key.pem
root * ${pkgs.writeTextDir ".well-known/mta-sts.txt" (builtins.readFile mtaStsPolicy)}
file_server
'';
};
# DKIM private keys are generated on first start under this dir. They're
# regenerable (rotate + republish the TXT), but declaring the path as clan
# state lets a borg client back it up to avoid a needless DNS round-trip on
# restore. Wiring mx1 into the borgbackup instance is a separate step.
clan.core.state.mail-dkim.folders = [ config.mailserver.dkimKeyDirectory ];
}
+8 -3
View File
@@ -11,10 +11,15 @@ let
dir = config.clan.core.settings.directory;
readVar =
machine: file:
builtins.readFile "${dir}/vars/per-machine/${machine}/zerotier/${file}/value";
machine: file: builtins.readFile "${dir}/vars/per-machine/${machine}/zerotier/${file}/value";
hosts = lib.genAttrs [ "control" "ns1" "ns2" "mx1" ] (m: readVar m "zerotier-ip");
hosts = lib.genAttrs [
"control"
"ns1"
"ns2"
"mx1"
"web01"
] (m: readVar m "zerotier-ip");
# RFC 4193 prefix of this ZeroTier network: fd + the 8-byte network id + the
# 0x9993 marker. The network id is a public var on the controller (control).
+23 -5
View File
@@ -26,7 +26,10 @@
"fieldConfig": {
"defaults": {
"color": { "mode": "thresholds" },
"thresholds": { "mode": "absolute", "steps": [{ "color": "green", "value": null }] },
"thresholds": {
"mode": "absolute",
"steps": [{ "color": "green", "value": null }]
},
"noValue": "no data",
"mappings": [
{
@@ -41,7 +44,11 @@
"overrides": []
},
"options": {
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
},
"colorMode": "background",
"graphMode": "none",
"textMode": "auto",
@@ -72,7 +79,11 @@
"overrides": []
},
"options": {
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
},
"colorMode": "none",
"graphMode": "none",
"textMode": "auto",
@@ -110,7 +121,11 @@
"overrides": []
},
"options": {
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
},
"colorMode": "background",
"graphMode": "none",
"textMode": "auto",
@@ -168,7 +183,10 @@
"id": 6,
"datasource": { "type": "prometheus", "uid": "victoriametrics" },
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 6 },
"fieldConfig": { "defaults": { "unit": "short", "min": 0, "max": 1 }, "overrides": [] },
"fieldConfig": {
"defaults": { "unit": "short", "min": 0, "max": 1 },
"overrides": []
},
"targets": [
{
"refId": "A",
+17 -5
View File
@@ -224,7 +224,10 @@
"options": { "showHeader": true },
"fieldConfig": {
"defaults": {
"custom": { "align": "auto", "cellOptions": { "type": "color-background" } },
"custom": {
"align": "auto",
"cellOptions": { "type": "color-background" }
},
"thresholds": {
"mode": "absolute",
"steps": [
@@ -245,15 +248,21 @@
"overrides": [
{
"matcher": { "id": "byName", "options": "zone" },
"properties": [{ "id": "custom.cellOptions", "value": { "type": "auto" } }]
"properties": [
{ "id": "custom.cellOptions", "value": { "type": "auto" } }
]
},
{
"matcher": { "id": "byName", "options": "query" },
"properties": [{ "id": "custom.cellOptions", "value": { "type": "auto" } }]
"properties": [
{ "id": "custom.cellOptions", "value": { "type": "auto" } }
]
},
{
"matcher": { "id": "byName", "options": "instance" },
"properties": [{ "id": "custom.cellOptions", "value": { "type": "auto" } }]
"properties": [
{ "id": "custom.cellOptions", "value": { "type": "auto" } }
]
}
]
},
@@ -283,7 +292,10 @@
"id": 22,
"datasource": { "type": "prometheus", "uid": "victoriametrics" },
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 43 },
"fieldConfig": { "defaults": { "unit": "short", "min": 0, "max": 1 }, "overrides": [] },
"fieldConfig": {
"defaults": { "unit": "short", "min": 0, "max": 1 },
"overrides": []
},
"targets": [
{
"refId": "A",
+194
View File
@@ -0,0 +1,194 @@
{
"uid": "cnx-uptime",
"title": "CNX Uptime",
"tags": ["uptime", "availability", "cnx"],
"timezone": "browser",
"schemaVersion": 39,
"version": 1,
"refresh": "30s",
"time": { "from": "now-24h", "to": "now" },
"templating": { "list": [] },
"annotations": { "list": [] },
"panels": [
{
"type": "row",
"title": "Uptime",
"id": 1,
"gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }
},
{
"type": "stat",
"title": "Host status",
"description": "Whether VictoriaMetrics is currently able to scrape each host's node_exporter. UP means the host (and its mesh path) is reachable; DOWN means the scrape failed. One tile per machine.",
"id": 2,
"datasource": { "type": "prometheus", "uid": "victoriametrics" },
"gridPos": { "h": 6, "w": 12, "x": 0, "y": 1 },
"fieldConfig": {
"defaults": {
"color": { "mode": "thresholds" },
"thresholds": {
"mode": "absolute",
"steps": [{ "color": "green", "value": null }]
},
"noValue": "no data",
"mappings": [
{
"type": "value",
"options": {
"0": { "text": "DOWN", "color": "red", "index": 0 },
"1": { "text": "UP", "color": "green", "index": 1 }
}
}
]
},
"overrides": []
},
"options": {
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
},
"colorMode": "background",
"graphMode": "none",
"textMode": "value_and_name",
"orientation": "auto"
},
"targets": [
{
"refId": "A",
"datasource": { "type": "prometheus", "uid": "victoriametrics" },
"expr": "up{job=\"node\"}",
"legendFormat": "{{instance}}",
"instant": true
}
]
},
{
"type": "stat",
"title": "Current uptime",
"description": "Time since each host last booted (now - node_boot_time_seconds). A value that drops back to near zero means the host rebooted.",
"id": 3,
"datasource": { "type": "prometheus", "uid": "victoriametrics" },
"gridPos": { "h": 6, "w": 12, "x": 12, "y": 1 },
"fieldConfig": {
"defaults": {
"unit": "dtdurations",
"color": { "mode": "fixed", "fixedColor": "text" },
"noValue": "no data"
},
"overrides": []
},
"options": {
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
},
"colorMode": "none",
"graphMode": "none",
"textMode": "value_and_name",
"orientation": "auto"
},
"targets": [
{
"refId": "A",
"datasource": { "type": "prometheus", "uid": "victoriametrics" },
"expr": "time() - node_boot_time_seconds{job=\"node\"}",
"legendFormat": "{{instance}}",
"instant": true
}
]
},
{
"type": "bargauge",
"title": "Availability over window",
"description": "Fraction of successful scrapes over the selected time range, per host (avg of up over $__range). 100% means every scrape in the window succeeded; dips reveal flapping or outages. Red below 99%.",
"id": 4,
"datasource": { "type": "prometheus", "uid": "victoriametrics" },
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 7 },
"fieldConfig": {
"defaults": {
"unit": "percent",
"min": 0,
"max": 100,
"color": { "mode": "thresholds" },
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "red", "value": null },
{ "color": "yellow", "value": 99 },
{ "color": "green", "value": 99.9 }
]
},
"noValue": "no data"
},
"overrides": []
},
"options": {
"displayMode": "gradient",
"orientation": "horizontal",
"showUnfilled": true,
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
}
},
"targets": [
{
"refId": "A",
"datasource": { "type": "prometheus", "uid": "victoriametrics" },
"expr": "avg_over_time(up{job=\"node\"}[$__range]) * 100",
"legendFormat": "{{instance}}",
"instant": true
}
]
},
{
"type": "timeseries",
"title": "Uptime over time",
"description": "Host uptime across the window. The line should climb steadily; a reset to zero marks a reboot.",
"id": 5,
"datasource": { "type": "prometheus", "uid": "victoriametrics" },
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 7 },
"fieldConfig": {
"defaults": { "unit": "s", "custom": { "fillOpacity": 0 } },
"overrides": []
},
"targets": [
{
"refId": "A",
"datasource": { "type": "prometheus", "uid": "victoriametrics" },
"expr": "time() - node_boot_time_seconds{job=\"node\"}",
"legendFormat": "{{instance}}"
}
]
},
{
"type": "timeseries",
"title": "Up/down history",
"description": "1 while a host's node_exporter was scrapeable, 0 while it was not. Gaps to zero are outages or lost mesh connectivity.",
"id": 6,
"datasource": { "type": "prometheus", "uid": "victoriametrics" },
"gridPos": { "h": 6, "w": 24, "x": 0, "y": 15 },
"fieldConfig": {
"defaults": {
"unit": "short",
"min": 0,
"max": 1,
"custom": { "fillOpacity": 20, "lineInterpolation": "stepAfter" }
},
"overrides": []
},
"targets": [
{
"refId": "A",
"datasource": { "type": "prometheus", "uid": "victoriametrics" },
"expr": "up{job=\"node\"}",
"legendFormat": "{{instance}}"
}
]
}
]
}
+60
View File
@@ -0,0 +1,60 @@
# DMARC report analyzer, imported by control only. parsedmarc fetches the
# aggregate/forensic reports that land in the dmarc@cnx.email mailbox on mx1,
# parses the XML, and stores results in a local Elasticsearch; the official
# parsedmarc dashboard + an Elasticsearch datasource are auto-provisioned into
# the Grafana instance that server.nix already runs on this host.
#
# IMAP runs over the ZeroTier mesh, not the public net: we pin mx1.cnx.email to
# its mesh address in /etc/hosts so TLS still validates against the public
# Let's Encrypt cert (primary domain mx1.cnx.email) while the bytes stay on the
# overlay. The mailbox passphrase is the shared mail-dmarc-cred secret; parsedmarc
# reads it as root in its ExecStartPre, so root-owned (clan default) is fine.
{ config, lib, ... }:
let
mesh = import ../mesh-hosts.nix { inherit config lib; };
in
{
imports = [ ../mail-dmarc-cred.nix ];
# Elasticsearch 7.x is under the (unfree) Elastic License; allow just this one
# package rather than opening allowUnfree globally.
nixpkgs.config.allowUnfreePredicate = pkg: lib.getName pkg == "elasticsearch";
# Keep mx1's IMAP traffic on the mesh while presenting the public cert name.
networking.hosts.${mesh.hosts.mx1} = [ "mx1.cnx.email" ];
services.parsedmarc = {
enable = true;
provision = {
# Local Elasticsearch on 127.0.0.1:9200 (loopback; no firewall change).
# datasource + dashboard default to true once ES and Grafana are both on.
elasticsearch = true;
# GeoIP needs a MaxMind account/license key; skip it (reports still parse,
# just without source-IP geolocation).
geoIp = false;
grafana = {
datasource = true;
dashboard = true;
};
};
settings = {
imap = {
host = "mx1.cnx.email";
port = 993;
ssl = true;
user = "dmarc@cnx.email";
password = {
_secret = config.clan.core.vars.generators.mail-dmarc-cred.files."passphrase".path;
};
};
mailbox = {
watch = true; # IMAP IDLE: process reports as they arrive
delete = false; # archive processed reports, don't delete
};
general = {
save_aggregate = true;
save_forensic = true;
};
};
};
}
+1
View File
@@ -46,6 +46,7 @@ in
(target "ns1" (v6 mesh.hosts.ns1) 9100)
(target "ns2" (v6 mesh.hosts.ns2) 9100)
(target "mx1" (v6 mesh.hosts.mx1) 9100)
(target "web01" (v6 mesh.hosts.web01) 9100)
];
}
{
+73
View File
@@ -0,0 +1,73 @@
# Public reverse proxy with TLS termination for web01. Caddy fronts internal
# services and forwards to them over the ZeroTier mesh, never the public net.
# The cert is a single wildcard (*.cnx.network) obtained via ACME DNS-01, so
# adding a vhost needs no new issuance. Public ports: 443 for the proxy and 80
# only for Caddy's HTTP->HTTPS redirect (issuance never uses inbound HTTP).
{
config,
lib,
pkgs,
...
}:
let
mesh = import ./mesh-hosts.nix { inherit config lib; };
hosts = import ./hosts.nix;
certName = "cnx.network";
in
{
imports = [ ./dns/acme-web01-secret.nix ];
# Render the shared acme_web01 TSIG secret into a lego rfc2136 env file. lego
# (via security.acme below) uses it to write _acme-challenge.cnx.network TXT
# records on ns1, which authorizes the acme_web01 key for exactly that owner.
clan.core.vars.generators.dns-acme-web01-rfc2136 = {
files."rfc2136.env".secret = true; # root-owned; systemd reads it as root
dependencies = [ "dns-acme-web01-secret" ];
script = ''
printf 'RFC2136_NAMESERVER=${hosts.ns1.ipv4}:53\nRFC2136_TSIG_ALGORITHM=hmac-sha256.\nRFC2136_TSIG_KEY=acme_web01\nRFC2136_TSIG_SECRET=%s\n' \
"$(cat "$in"/dns-acme-web01-secret/secret)" > "$out"/rfc2136.env
'';
};
security.acme = {
acceptTerms = true;
defaults.email = "postmaster@cnx.email";
# One wildcard cert for every vhost this proxy serves, via DNS-01 (so issuance
# never depends on inbound HTTP). Port 80 is open only for Caddy's
# HTTP->HTTPS redirect, not for ACME.
certs.${certName} = {
domain = "*.cnx.network";
extraDomainNames = [ "cnx.network" ];
dnsProvider = "rfc2136";
environmentFile = config.clan.core.vars.generators.dns-acme-web01-rfc2136.files."rfc2136.env".path;
# ns1 is the only nameserver that accepts the acme_web01 UPDATE; check
# propagation against it directly rather than a public resolver.
dnsResolver = "${hosts.ns1.ipv4}:53";
# Caddy reads the cert from explicit file paths (tls directive below), so it
# won't notice a renewal on its own — reload it whenever the cert changes.
reloadServices = [ "caddy.service" ];
};
};
# The lego-issued cert is owned group=acme; Caddy needs to read the key.
users.users.caddy.extraGroups = [ "acme" ];
# Reverse proxy. The explicit `tls cert key` points Caddy at the wildcard cert
# and disables its automatic ACME, so no extra issuance happens. Backends are
# dialed over the mesh by their ZeroTier address (mesh.hosts.<name>).
services.caddy = {
enable = true;
virtualHosts."grafana.cnx.network".extraConfig = ''
tls /var/lib/acme/${certName}/cert.pem /var/lib/acme/${certName}/key.pem
reverse_proxy http://[${mesh.hosts.control}]:3000
'';
};
# 443 serves the proxy; 80 only carries Caddy's automatic HTTP->HTTPS redirect
# (the Hetzner cloud firewall also scopes these in
# modules/hetzner-firewall-rules.nix). Admin still rides the mesh.
networking.firewall.allowedTCPPorts = [
80
443
];
}
+6
View File
@@ -0,0 +1,6 @@
[
{
"publickey": "age1yey6gxgsyl4tj6ek0tve2pckt6qersqspk66ukkzum8mrr6zppqsj4jn3m",
"type": "age"
}
]
+1
View File
@@ -0,0 +1 @@
../../../groups/admins
+18
View File
@@ -0,0 +1,18 @@
{
"data": "ENC[AES256_GCM,data:u+O5nWbFlvp6SGyHJggfkCLCT0ZuDy89e/VGGQNdt3yYzgdNmnrtd+2q+Ft3MtoOSSCLvStriGQzfLhcqEgqGgt3PqfIzCO1IG4=,iv:GyrZ1XUiOZe1I1Z/HebTy2NM2tfDHxIH5zGVk7HD+xQ=,tag:js6fbXWajVZSxt0hmnnA5g==,type:str]",
"sops": {
"age": [
{
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWenArV1pZMURzNElTeGlC\nRSttUFRwM2E5d2htVDlFcTkwVDIrV2JxQjE0CmE0NUZ4UERHdHpqYk9uK01GOTFQ\ndW5UNlRrY0hvU2pNQmpSanh5RG5YbVkKLS0tIFdYQmkva1NORG80U3dDSWszZTlO\nOXViWUxMTVR1NE00cjdpVXpVS1J5YU0KHBkeKAJZDc+R1GLKwDYLyQBlEW7tPnMh\nf3tsUvtD0flqPAXNeDgyOmKufP7U6oDy/OriFC9+zYQbWyEEc6CZHg==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yubikey1qd859y9ehz2ya8j2cftwrtmdeqhuk7r7yc52zp64wpff6068gwrac3q6nsa",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFWcTVydyBBL05xT1BJ\nSldCSkFhaFErWDNMeXMvRksvekkwNUZLZFVtSmUydVRrNG9JTwpFaHNsVThZN2gw\nMlNxUXhnN2xYNFluK1hadGxzMFZuaWR3cVFRYW9mWndVCi0tLSBUUmRMSnJCeTRM\nUVdKY3hzWVRkQkFuQ3FaRDVZSEp1b2N1ajg5RnlhT3VRCjX/vWj0We88ATiz808w\nz60RL0BvDGJ6m1BNmqAdtfCCClH33YXQBGrKT2E5elvOTl0iOCrT7HPjzXxJZXuw\nkKg=\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2026-06-20T19:26:29Z",
"mac": "ENC[AES256_GCM,data:uD+L3op6ZPtvmLreJ7S4GE6bQuItV87w1LTMFvjI1Kb5+Z0sXlL0TpYO8WLx8X0yaL3HddwlmBKYQGp/OlRPqZMFDbQuK25oeKB54jfm6YDn66rrMQJl1FOw68fLJaHYNjNelEg/bj2WG7YpfZBoWO67MW6F+44Rg4XF85w/1x0=,iv:X9TmEL5JkcC9waLumWpgpBwp3YWLMslZi++dv2HZ0mk=,tag:i7c1CxT1Xv/+T/jY9E0cdg==,type:str]",
"version": "3.12.1"
}
}
+1
View File
@@ -0,0 +1 @@
../../../users/berwn
@@ -0,0 +1 @@
../../../../../../sops/groups/admins
@@ -0,0 +1 @@
../../../../../../sops/machines/mx1
@@ -0,0 +1,22 @@
{
"data": "ENC[AES256_GCM,data:Lkwniu1Pmu+kGb94ncTteN/CkBYE47+UJKRSij5APKyPa6wQkc33S00WVLSXkG6I/XGeRUAXxNpM7G3WqmkluBtJnuYdQ3+nLVdarDy015Zu207LbpaYBuDyMU4e5pxH2ekIGnyL7diDI/3/GG+fVrO4xxdrFPWaB0YDcD8+5mtpshUqa+4rDrU9CSikuRo+dkAHveX1+MFfpF0aJmRw2MMwXO4=,iv:LIOftLZQ63yEPJ5S08t97jGGkSUK1LxSMnvy9lEm070=,tag:+yYbq9RU91KB+6r9eC9/fg==,type:str]",
"sops": {
"age": [
{
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDS0Fra0tVdHIwREVYRC9l\nZFNwek0zMWYvTkRBT1RKR3p1bTN4SmZncGxVClRoOFRNcklEeWllMW4wdzRCaXNX\nbWtNYUJNM2dGaDZqVGtVY0twZVhEY1UKLS0tIFUxZ1QyMWNLRGJrYkJTbTJCT0Fy\nd2xjbWthVG1JUW5XZmVSK3lWc2NLRGMK6/g42P7ZvAk+t2GmZammNxTLFMudK+Qv\nZt3YUF0+EYKlEENgtjku7SSZ7UElQ5NZNrldlk8ZYLIVTul+8XuvrQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1l5hw95p5h4sthrgn0usms9yfkwwmcvv34tjgrtv9s4e6x39chacshgxavs",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJQ1ZMRzEreERmM1V3OFhC\ncEhZYjZ2ZGFWc1kveUpLM0MyYlB6S25jR3cwCkhjaTBpS3dHZG51V0RmN1ZVcmRk\nYnIwSlRKVXVoQ1NqdFQ1M2lPazEyRTgKLS0tIGRFTFRaNUMvdU5Fc3A2Nk10NWVw\nWlZOKzJLUDNwTXY4dm9uZTY0cWg4aDgKYDIEQHgomMuJFHHvtt3BbN4tuBiEcboc\nH6K4NmnDE6wMa/1EGGHrjCFb+tUdZSL/zgf5uVOXnXA6d9BEeCGLNQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yubikey1qd859y9ehz2ya8j2cftwrtmdeqhuk7r7yc52zp64wpff6068gwrac3q6nsa",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFWcTVydyBBNk9Od2FL\neEV2Myt0eGI5emF6TlRGS3A3RGNHZ3o3WCtQUDdpYllCTFY1RwpyU2xyNkFIVmhV\nY0g1NDJHRy95SzRrTnRPTGc5ZXdZYTZtMyswWkZIUVdnCi0tLSBWU0Fsak42TUF1\ncndtNFpDUjVhb3RpeWU1eDd0UGtnUTJXeHNiVmNtUTFZCvUQDFKntKn+mZSuDR00\niTu/TdmOu7s89JvirWtFavSZhBzOoW8eXdX/SJCLVy08wdTjb2ksqDdxn0ceiqgL\nLOU=\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2026-06-18T07:11:40Z",
"mac": "ENC[AES256_GCM,data:8hd2LItBnmG06HnUQ0avOOnbA4+JAPkJf/Wneqo/YexT/saEK+roa+iMkL2DxtcZK5UahPkJ+wT4q3MfNkOnrdbHmQHUUIkDSX+RxLatMtOseYxg8h9wT6MZehuxkRpN4Y9RlpQu/+l3zKQNbXfsfUj6i91fjH6lcxSYPHNO8ug=,iv:T8hYVO0houthJhFmV2UoOK8d5Z3sevv1pRvhdf5qaXk=,tag:2Q5bi8y+hq1TSkQ45Cmf5Q==,type:str]",
"version": "3.12.1"
}
}
@@ -0,0 +1 @@
../../../../../../sops/users/berwn
@@ -0,0 +1 @@
../../../../../../sops/groups/admins
@@ -0,0 +1 @@
../../../../../../sops/machines/mx1
@@ -0,0 +1,22 @@
{
"data": "ENC[AES256_GCM,data:SZIlr7nzcs5EUMVUXQ1KJhrd9JZY7ZR9EUpJ9Eygkpd2sAyMNtV0jMFyfW82PyvLP9/bqXf/5BUR98NuwvMsRmLrb4emEraNJH4qVfS/4s0kXySIJeA6XeMHB7GSuxoh5K/3pUR4tbdFSCM=,iv:grRynVFombEdRp0LfmPHIximhh4rlbQTjqJCjbGhRlw=,tag:qZZCnN63clfKKsb/WQNkbw==,type:str]",
"sops": {
"age": [
{
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFWUFGNDBLM3VCMFh2Sjc2\nY1FkOVhta3REK1Y0MUZlY1IxTHorRCtuOWxJClJqK2hjT2p0VFBEeE1xd1JDUTBk\nNzR4eG5zNGJnalVGb2ovRHVqUUpla1EKLS0tIEJMYTU3MlVKOHdvaUhhM2dsWEoy\nRVh6SHc2cVdTTW4xZlhpc3U1aTRLT0UK+WYlVCCJ1bUsuF/vy6+mSU0gpM8FGHDE\n9HfyAHPZLR30ZtkRHSq8LQ137hxmBKSUjv5ztyHc781tLkx9j3lRwQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1l5hw95p5h4sthrgn0usms9yfkwwmcvv34tjgrtv9s4e6x39chacshgxavs",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYMVlBOWVRRitmcVBINGJK\nakxWZEdNeHh5QWIrSHJmVzhDRDl6MUhhdUVnClRFcXZNTGYrazNxWG5UVi8vVWlu\nK3NaN004MVhrR1R2YVpZcnArRmVVUjAKLS0tIG90aXpEVHZLVDMrQWhYMHg2bWZj\nN3B2SmpHTnEzNFZ4cXBmM3paRmhCMjAKZa1OlhBcW+4J0sR+aWv0lkLqDh+73Gay\nKl6ltN1EQI7ISH2azQFahzoz6XV8cyYUHAQaHaYZuyCZNb/XbG9VmQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yubikey1qd859y9ehz2ya8j2cftwrtmdeqhuk7r7yc52zp64wpff6068gwrac3q6nsa",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFWcTVydyBBcHF1OUdP\nQk1VVTdla3ZSc1VUa2MrT0IwanpmRWNsVHlYc21jM3dXNkg4RwpSSENkYUVJa2lS\nTk1pb3NhNkVPU3hPeVVPUE1MOGxTNHpsYXJGSVRQVmVBCi0tLSBFVE96Q0luZ1Jv\nODd2eUl1aWhOR08wUFpPZHFkZEd5MktNU1R2ajBoTERzCrmM40bnvt2iHQERfrN9\n8724ZmXn4YpAiN/FwKpPJ+iF2atpPDbUb2PFG2s6s2kJISMCrpoblZHBTYbG322M\nGgw=\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2026-06-18T07:11:36Z",
"mac": "ENC[AES256_GCM,data:bgu8Pv90Ljc1e6uG5oSDY4IwM85ZqiEQ2Uy0xt6Iu6/f8G/K/sh/+N3ZwcXAV3cpAsa5Cde8vHO8JgmAmcYN1frEgb9+rJyfD/sVvIEmy++WgLKIoUngcRyxX7tk+lLGv03tF09HcfoA+P4d5zNxZq5hR20jkT+2t3Y0nw7XN1M=,iv:gbvg2/5SBg3a2H/bvMFuF1bWSVHTppP59s7ivthe6TI=,tag:6zSYNSG2qHKv9YMLVyvSbA==,type:str]",
"version": "3.12.1"
}
}
@@ -0,0 +1 @@
../../../../../../sops/users/berwn
@@ -0,0 +1 @@
../../../../../../sops/groups/admins
@@ -0,0 +1 @@
../../../../../../sops/machines/mx1
@@ -0,0 +1,22 @@
{
"data": "ENC[AES256_GCM,data:FYx5Vll+asvRpC56Wk7ZAB6tdGabWIR25fRD/fw5aTgLz3+wU6K3RkDy,iv:rPD4rRduxodp9e6PGSD9V3zDPaTTAxeSNpC3Q/Umi/k=,tag:DiIahCPXn/DOSgJunryXHg==,type:str]",
"sops": {
"age": [
{
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrVGRuNnBYL2NJUFFFZWho\nVDM1d1YxUTJLT2lma1NuYWtaNEVpNnUxV0NrClBOb2tBR0Z2a2lMMTNMeStTK2ln\nZjZ1cEFRZjRTTld5M2pNV1c4aHZlZzAKLS0tIHdRNnB2R1E3M1hVamtJWk9wWE1Y\nWG1yKzF5QkFQbWZZUTVObS9jcUtQVmMKdj+SEz7TcCe5Fk5B65EPBvGC0OWVafax\nws4qgi3O4CNQkVoOx4Jq6aWF0I7a0dR0mG2ERPeVUJKt6Kzap/KJ7w==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1l5hw95p5h4sthrgn0usms9yfkwwmcvv34tjgrtv9s4e6x39chacshgxavs",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFbXhLYUhXSzZZNm40OUdJ\nZ0x3VTB3WDBPSzQ3RU03dS94RmNRMEM1ekg4Cmt0SDMxY1hsL0t6RXRZV29ObWJt\nUFFiZEFFL3p1RytVdXpXeU9sVlU1THMKLS0tIHptRlZqVS9NU3RydGJzbkZ4a05P\nVkRUNDErdklTV2ZWeVNIdVRmVTdxSWsKTH3olIgtm+rM7CsKeVq3GVYk+Y2JcoZ2\nE6/KdwBOsRDFvQpw6vuNVUD0LDDWh0T3+V4+3f9YBn1qdqWHFDA52Q==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yubikey1qd859y9ehz2ya8j2cftwrtmdeqhuk7r7yc52zp64wpff6068gwrac3q6nsa",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFWcTVydyBBODdOY293\nWXZYWUNuOUxwcUh1Y05VcEFrQTZ3VnJiT3ArN1JFZTZLOFl6RApib2RnN3JnS3ZQ\nbFhFakNiVFdsR2tRa3JUVmJaZzJTY1ZOaUcyQlMrcm5jCi0tLSA1Z04vRTJ4NzVC\ncG5XYW1ZRUd4QkJMTGlJbEUwMmpTSnk4RXBTclRrOGN3Cj7t6HYbS8kdKaIYWMms\n74vQn/HJvnYnIwUqEsf3z8QzTfsXtPB4ueA1NjftyvlKMRozuKxb+ULFv6YkZNzX\nxvo=\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2026-06-18T07:11:36Z",
"mac": "ENC[AES256_GCM,data:Jl/TzaI0c0v3JwJHDSvDUZEGKpgMGgFD1UoWC7qbc6LC+vVOpDjcm3WlfXfy2ljHpaqd74dl2kvy2ra75htI5YuLAisTqrPXhm+8km4tLzQzZOHz3JRIW+0fgnVC8z+GFrOHsz8CCc9SCX03HTyOlugvq0PD2sp4hz6Wgmp4cMc=,iv:Q/qUtb968vhyyRQyDYIP3WE49GLWExkmbarSZz9jPhM=,tag:M0crVWRGl2xvrAJEZvfFaA==,type:str]",
"version": "3.12.1"
}
}
@@ -0,0 +1 @@
../../../../../../sops/users/berwn
@@ -0,0 +1 @@
../../../../../../sops/groups/admins
@@ -0,0 +1 @@
../../../../../../sops/machines/ns1
@@ -0,0 +1,22 @@
{
"data": "ENC[AES256_GCM,data:GYJB+ctvohxqGBWjaWFWXQ0Mr8yPoYH/e1oNgJvIKHYtu7e7QIPyReFgYI/AcK4ZbwDZs6n+eYXv/hpw8IvPdyDD5f2u6fKcHrg2SYiF/uSJtfQXkgppy2HjpBnYC392fMe/QBEqtA+TZA==,iv:eQvNHROk/7fdQ1wESRLXF00A8KZ5n75OC85TMTt9u48=,tag:j6BS/8z144iewR9yiV8RZw==,type:str]",
"sops": {
"age": [
{
"recipient": "age1fanu282vm7njjweqhrpcfcwpttuhce8js4tsyfry98l0neaqpewqs5s7nt",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2TVM1UndkNEF0YjhqUGM1\nTDltckpEVnlmQ1BQNUxycWV6RWR3S1pOT2xrCkY4Qld3d1NxeFNGdUQ2Uzd6bTFR\nRllnZ1JEaHV6NmtaUEFrQjB4QUpvTTgKLS0tIDhHclZZbGtyU2J2clo3SXYwK25w\nNlA3dVF2T3VJZUp4ZVZHSE1qTHp1eDgK0bpZc/QzBtxTpuK63t3iICbh/ppVXFgz\nBkye9movY2s/+OT6KTl8+CW7WYuyWTWPOTuzHmuj+IZPDJuhmxG2WA==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2OEJtLzVyTHBKVmo3eUZh\ndEIxWnFiN0xoMDRZMVBYL1pITTYzY1Y3bGtVCm8xMEhRc25Xb2tLUjU1VU9PU3dL\nRG9VRFhVSWlmSCsyV2x0MVZsZjcxbmMKLS0tIGxTeDhrWjhhUVQwUjZVQUs1VU5N\nQzJSSmYxWWZUdjUxdG5mRGVHWHpLT2MKhHFZa+qAY3UCOJMWXlzqcV0w2GhY3gbY\nKKbUB8e+dpkoNsS2GPPASS7xYm9LNPn5R4+GzCBsB3HqvYFethQiaw==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yubikey1qd859y9ehz2ya8j2cftwrtmdeqhuk7r7yc52zp64wpff6068gwrac3q6nsa",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFWcTVydyBBOG1RemV2\nWHA2aHoyZGR2d0I1WThVNmlnUklWWndjbGZWNTRoUjVIVDJTSwpabXpqMW9wUWVC\nUUlyN0VlYThkVXpKMFRlMjBsbTBCUUFEYUJSRjdMbEVrCi0tLSBxc1cwalZ3aUtp\nNTUwVTR2NjFGbUl4MzNQdStwQzR4WDQvc0txSjU5a0I4CsupjMhWkfV7N3aLXv4y\n6tjb3ukWuJLWy1x5hHllDiQU7JXjKhnxonqRxVlyrnAxybzQVmY+8ndaDCu4jOm1\npPI=\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2026-06-18T07:11:40Z",
"mac": "ENC[AES256_GCM,data:4olEQSz2jb25fwWMp0IdXkrqY4NYPQrKAizpvB7D/W8sq6tHKcR1JN8umrwBNk2pcDP119DXHK38b32+UzhYYVkDFGWfZaIc8sj+FY1u7394zui8JcOOiDUxxiyo+R9OeevC4xOV8gPWnAhpBRBnNHvhiU9daBahRmYf7mdubag=,iv:PsSmSS0qN0AJ9q7JfuidoL9ngu6nnkgfVSu0PGOwB3U=,tag:f7E9GTb+AHSDFyc4pJXXiw==,type:str]",
"version": "3.12.1"
}
}
@@ -0,0 +1 @@
../../../../../../sops/users/berwn
@@ -0,0 +1 @@
../../../../../../sops/groups/admins
@@ -0,0 +1 @@
../../../../../../sops/machines/ns1
@@ -0,0 +1,22 @@
{
"data": "ENC[AES256_GCM,data:hunVccO40N4RQ5oOB/yJi9CFCYAMGoYkjPCG7pRXng6Rd2vqE9Dx2CmmAV3w1gkFm0Bxk29QIIZA25bL3Y2sAE8h0xG3myNOKUg9M6cSisoSy6NwtBJjl3AEjeOaQ18k6PhBB/d2/qo6a++d,iv:NPZKfBy7O8Zp2rlj8ZB2uQx3alzwGrWOoio7XOZtvvM=,tag:WDtFUT/TA8vIcKuX58oaUQ==,type:str]",
"sops": {
"age": [
{
"recipient": "age1fanu282vm7njjweqhrpcfcwpttuhce8js4tsyfry98l0neaqpewqs5s7nt",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBuMyt0eFBJOWplVXV2NXhD\nbGF6RjJhdi9MSUMrdGlHNnBOcXB3MG5pQ1drCmFnZm9jTVZ0dWFrME9iSmtNMUFv\nNDlDaU5nc3dLQ25QRUpVNkh3Wmk4RWMKLS0tIHozSTNHQXhmRnpsUEF1UTd0SFdP\nT2xTLzRCQVhNcm5FOURieGk2SHhzam8K3E5swVSS2+69kE3lbRnc88melsTUKyH6\nZMvsAVYy1Fwy+wfo5UJzlpXux5+5Lv05eSrpOvWj9aA+fjHG8UdRZQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsK3gwRS9nRXFYbE44bDRm\nSWJBb1NFSnBoT2JlQUZra25TOEpuY1FESjJVCndMSHQvYUszN3UyZDUzTi85OFVN\nQUR3aC9JK2tZZ2FuTEJyeFNvc0ZnWTgKLS0tIGphVlorTDhwYTJNbnRzOXNOZXJG\nM2t1VElJZzY5dEJCUDFuU1lTcUpCbWcK0fyj4CB9Y0kYDrIyvWidAwRSzCUbFcWi\nRnIukpyLwJzMIt4M5tIisxbvX/Eer9TfSXK8blNZ+fvCwr+qyEZgPQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yubikey1qd859y9ehz2ya8j2cftwrtmdeqhuk7r7yc52zp64wpff6068gwrac3q6nsa",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFWcTVydyBBdXFaM1Nj\nRkl3OUFXczg4bGRuNmwwMm9WaFVrQ05aYWFTMWoyaENQSmdPTQpqa0JIb1Zva3ZJ\nZTBjZSsrNHpzdzVBRjg5MnNMSFo2aXR0eXRpVXY3dys4Ci0tLSBBTDBPZzdMNWw5\neGttaWxnT3A5ZnV0RnczRnI3OXdiWm9Mc2UvRE1YaElNCrrZWEZzx3kzwX3p9z14\nEh5eB7NBDqMUnnIzdaz7cgD/SKeKbBAKXAeCpyUvE/EEqyh8PCLKNG3J1dmb7XRp\n+rc=\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2026-06-20T19:26:43Z",
"mac": "ENC[AES256_GCM,data:TRwxqjf/HiVuuLe0weRJEDvpPUyapQAxs2m3LFmIMdWv43XQh2MCV95IwegFDnZHe/qd9V+TsvobeXoYeJalCIFrqwW9HphqeaKpWuUw/+IuvLtH11Jm6a1Pxe6fDt75Y28YgZYJt8QSH7qaSkrCANmeoYIVBAeI77byYQqHS3o=,iv:ryK76YC1j0UvgkY28f+v9cRwjeDvlpSxHcOCUiYhu/o=,tag:nNuL3CmYWRcCBDA4scy8dA==,type:str]",
"version": "3.12.1"
}
}
@@ -0,0 +1 @@
../../../../../../sops/users/berwn
@@ -0,0 +1 @@
../../../../../../sops/groups/admins
@@ -0,0 +1 @@
../../../../../../sops/machines/web01
@@ -0,0 +1,22 @@
{
"data": "ENC[AES256_GCM,data:xE+wW3x5ZFqFbP7Q1RuG8zSZPgV/J/Rw+92NBI6U8c0OR5KQ9Gr+mnW323g5oUPU+bUod0eBh5Iv/ciYenBYwhIbANHFzr6ZO8qt7n1JMzwjLezfuVI21R09PdS/x1rjyvqGvOr9XQYZYCIYOLShXtEqjl6jkvZrbZ1qkpdyiUqsR/dqnTRsrmMlaYVGQ/WeR7l9FOp02DyonYT8R2X0jIKy99H+9g==,iv:t1mOOLJyCyRi5f83mG0kRZJdxKFKVTIIaP1JOoRDFkc=,tag:oVDO11HTbk/r0cFSwVh/kw==,type:str]",
"sops": {
"age": [
{
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArTE9OVjh5SzhZeGhobGls\nR1hJOEsrcTRjMU5GYnpsM1BldTRCZ0J5T0FZCmJKRzNUN0JZZnFPVTRmWGZDSnhF\nYnVER0VIaUxTTWxMTGlCb0hSR2Z4NjQKLS0tIDBpMFF6NWhxR3Rsd3UyVWdvRDEr\nSi80YjdHSFBuZmJQMmZVc1J2YUxLN1kK7ApaZOgNt/lqZDJreBLUXgQnsdOk5x/3\nTeVlKGYlLug7X7IsgyND09rWiaaM1mICXaA5tahdWwsyoFEfu5n4Iw==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yey6gxgsyl4tj6ek0tve2pckt6qersqspk66ukkzum8mrr6zppqsj4jn3m",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsNnM0T3p6K28zM3FBZVJn\nVXZMSmhncCtaUHpySEJVSDNIbWtZc1F2VFJzCmcrNlpPQXhpdU12NGxibklQUmJW\nT1JCajZTOE43VWU5Q1NKR2lVbmtjUUEKLS0tIEN0cm5LajdCS3FPMFRSR3IwbUE4\nTHB4anBXZ0JraVkvQ3hNcE9zLzV5YkkKofqjv0Oe3y56HXO0SZG7G6A6vatuE3Jv\n3bGnRwCo6MCz9DMpTU/bKrGnmpQhHMYoGK5DmFhV21nH6Kwh+bnqjw==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yubikey1qd859y9ehz2ya8j2cftwrtmdeqhuk7r7yc52zp64wpff6068gwrac3q6nsa",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFWcTVydyBBaEZpUTBI\nTTJ4WjNQclBabEs0SDFsbTBUaEI2eldOblR3eEp3ckRwaVJSVgpQY3YwNDJyUDRC\nTWpjN0pWTDJxZm5aaWI0aThpcUtWemdCTFRjZUtRcWFnCi0tLSBjVFc0WWMvOEZW\nNjJyeGxvZUtjMlhXMU5PWndJbnVxZDZZTEFhQWM2YmI0CnA0SDzXPJb+K6NxR45E\ns+t5k+20F8cGyemSgmBb/RWB0vxA9HYpMpUWJsll0oMxT54lRtsziANTPAPMyr1J\n7ZU=\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2026-06-20T19:26:44Z",
"mac": "ENC[AES256_GCM,data:IdjbnUrkcU+mXUBSwE/gtW0ACBVXkXr+qI3gOyY7RlWnD8wxAiMQY2TIM8edmrY7fc39G9c8cwx/p6HpLUJXNEeXUZzDdoXO0HqOl8Fxs6LZtykzTt4zGldbEYdpguuyV0aIxT7C9OI10GRaa0RjpP0XPpr4wc0/CT9/F8Q22qI=,iv:lhEreXIvuE7SBQIgZ0cFGbAYLdb1Ez6hH2v3O8+jOUY=,tag:GfcOGiZqVBPxbagNtjLEQQ==,type:str]",
"version": "3.12.1"
}
}
@@ -0,0 +1 @@
../../../../../../sops/users/berwn
@@ -0,0 +1 @@
$6$2/XqJoWZP62VbWNk$I3QeJOZoDBueIsymuifFs4ey7dXOLfl5rp4g15DKmGvD4WuueYiTQRxBY4mxbDRL19uL8U02NJ07JpfMs/vBL.
@@ -0,0 +1 @@
../../../../../../sops/groups/admins
@@ -0,0 +1,18 @@
{
"data": "ENC[AES256_GCM,data:hZuIBcV9zWv27qrGxi6vU9rq8x+MKYizx5SGKZ0sCCU3CvI=,iv:E3CNJCCVgei6+iKnUNCbKFhtCLqs8uQZtkFqYeP56so=,tag:IELHXoZI/7i4h4OLbxTmeg==,type:str]",
"sops": {
"age": [
{
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlL09jYjIwTWVnQUJIMHc1\nRHhJUGg3YVkzbHc5SG5PV3RSUkhwdCtBYjNVCmx3d1ZzYm5aYTdGSnAzTGRGeWFo\ncWU4V0NjVExjOE9iT2VkY2lTUGpWT3MKLS0tIDg3eXYrbTQyTDIvcFVxYVBpZkx3\nL2RKcWFoMzVGd0pvMDdoYThHYWpqWWcKr4KSWWSMlPkSJPWFYIApOnLNNbmSQx1J\nw0sAKKmmKHQbxfgNFaAKBHXILvTG3mqKOaJnN/t9G9ncUcPnrAYXQg==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yubikey1qd859y9ehz2ya8j2cftwrtmdeqhuk7r7yc52zp64wpff6068gwrac3q6nsa",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFWcTVydyBBKzRUZVMx\nMkF6L2JVaG9HT0dJUmFBY2xKaEZtbTZBWU1MOWVRZU1ocnRybwpQdHZJbXpZc0FQ\nbUU5ai9Nc3VDUGxMbGJFcG52bG9iN0MzM21OKzZFMlZjCi0tLSBKRVZpQWhJNFVC\nSnhJVVJySUg4QkVqcXVKSGErbG9PeXdGbjRXbWs4ZzkwCgJrL+X4olZNxWBX5EbD\nmVjYUXC/UZBAOZYThCSpKRSUc8Ri9wIvzwwOn2VLxPNuMfA+u40e3V+iDQI2D5vC\nCes=\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2026-06-20T19:26:30Z",
"mac": "ENC[AES256_GCM,data:amqgVdVVIBhmukvno/wTgC9rn3vJPhjM1bIkk9IMzooX+58wVnOsbaYCmyNekLZzP+9CimILFChXZomuXlHfCAt7q70b1DB6paRMeR1FKzeW83xCsjf+AylZj7y5x4zQelHTHppUKA5gUPjhjDUAx3qvpY/kgdEm8Zy1MhlEb58=,iv:GyZ+eGkk1aBiUftC9UOdNSipymR3teotW1ZEN2RDB3o=,tag:UifamkVXfYpuZc4O1DhsKA==,type:str]",
"version": "3.12.1"
}
}
@@ -0,0 +1 @@
../../../../../../sops/users/berwn
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILejqOdcr/7ttLRY0JEv6HcZ9x+pJd7p4T6yQRQ3VvW3
@@ -0,0 +1 @@
../../../../../../sops/groups/admins
@@ -0,0 +1 @@
../../../../../../sops/machines/web01
@@ -0,0 +1,22 @@
{
"data": "ENC[AES256_GCM,data:HuCJOdq4/WjX8oYQrjGWIMYykxm4LDoQEpT1FS6pa0oapRKg0a7ayqvKZSK+gXyLS5RjM/qWLtPCQgfUkILIPiXmSXCRElkgVhIE0H1msmbBraXhg3RdScjF7nJVHFdzWcZyh6LSNzgHk7GOCl5CEhK4/FPzYLSLOzd34J0g3QjXhlsDDe6fe8VAHNeBikbj/8hsqWhzQqeenJqNIat6TfxgknedmqhhgnuwPc0Zp99peHOc/qcErn4NENsSNqqCBO03yxhuipjb4yahGjHcMHe2qDVyU6/h0HlRsi7tjyQ6QJCmNUoAbhJfnC7ybWMRvOWKbafMw4NKA2yzV3nr6Z+HaE5cz2f7s4CkMHnue+npjARahSdPHm8rKEV/4z20t/bLTEmH2gyfrIo8PtyCLeOEuc4eb9DoGGgZaoVmFpJEx9Oy3dgXAA16VvB1jpcwp48F3ODW/MPEMCzvmvyENP6ERzgLWfsx+HfMt1UnNECnVHbgQ1toEzGrurQYKGyg0AO9,iv:TAstH1iU+msI9ORXZVfLU88iBY+/EoW+dHwi7WrbPmM=,tag:vPACqxlMZCtV44dWmhVC5Q==,type:str]",
"sops": {
"age": [
{
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0TndoeEwwT1YyNG02cXdT\nWFNabUpBM25ZcDJ3V1NtcG5rTlJuaHkvbFZZCm1lNXRRS3NUa2lUa3VRYTZ1MVBF\nUzhtc3czWG9nS2R0OTlzUU1BdE54azgKLS0tIFRUZ0JUZ3gvRHoyK3l5OER5dS8v\nZGs0cXQ5TVBDQTRMaXJUK1lmRHFZS3cKljcBSKYg/dJk5RQda+H5GWwCBSuZiFyX\nGUkV6NQ/ae9BJ8QChI64Ur3t8uRW5m3nWtc85faxK6HRKx8owW7BLQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yey6gxgsyl4tj6ek0tve2pckt6qersqspk66ukkzum8mrr6zppqsj4jn3m",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtSFVXbnJjcXFrcWs5WEZK\nUVI1Y3pCNzFqOWNJVkNpRHQzY3NoN0F3dXgwCkFxOTZnbHJ5eUpaWDJFczNsd2lH\nVjZmcEpwbUhKVzNETnp0MHdsaEVkTXcKLS0tIExzeGFRQ0lxSUt6MFVqcUNXbVdE\nNHdNa2ZsWDBPanQ3aWFabGVhUVlJc0EK3owAjyU++LLO9gzei7YxQVkFFGUOKjAe\nHwB1fMOdaxynbSahrLDiLTqg+Mgpa2HibX42KJZaz9x7eZ0BZRja1w==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yubikey1qd859y9ehz2ya8j2cftwrtmdeqhuk7r7yc52zp64wpff6068gwrac3q6nsa",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFWcTVydyBBMVh6V0ZX\nYXplbk9XcG5ScmdkQlZhMnlHeWFuVzdMeFQ4YkM4SmIrRUdRYgpPU0R4SDRZeTdt\nV1R6cEhDM3dyMG9Ga1pTSjRBYWxCV0RKOEhVWGxXY1RvCi0tLSA1MnRzei9EakNw\ncHI3Z0Q5T0ZGRG1yUTg2S2QyL2FyeVpIaldtVVpGZEtJCtlzVqD4YdLxn3doApaQ\ny1CtiPrYJCmxpwAlmDdD7JtI09uteTHTvoEJ3mww0vq4ubBw8es7DarrMVStyvWO\nn3s=\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2026-06-20T19:26:30Z",
"mac": "ENC[AES256_GCM,data:19FtQfyusFAikxdNVh2LUTMv/ak5TKXPf7OrjW37GDfsc9nFF2OxxqAWEo+20CacWXA1AGBvCLZYqVUdPwLSHMOlhsWqI8SNrmbW0cb9DcX30qaTtz9HO7tipfAuAf/0ZtE1Z1NIpRoaMwm8kn6fzzn0jGZ29W3jnKa8o/XmqIA=,iv:ip4q/5SA3Gch5tgl2KUetDF0faBMLtnKr0tziQTCSqM=,tag:yGvWmvuMDZqkwNzQHJ3GNg==,type:str]",
"version": "3.12.1"
}
}
@@ -0,0 +1 @@
../../../../../../sops/users/berwn
@@ -0,0 +1 @@
../../../../../../sops/groups/admins
@@ -0,0 +1 @@
../../../../../../sops/machines/web01
@@ -0,0 +1,22 @@
{
"data": "ENC[AES256_GCM,data:51hcHMIxXU3+SepHjw5Pr6C5hcBq2OpcoCIGnokGxudRpeR85yjhBrG7PXgmRvUEN+8JDe4QgvLxNtwk1cLBAFKjAZlDmI67ONf+zRIzlPKEEM1Zt4rjpbncpc1zWPdnLGJuyHU6PdX6aQ==,iv:fmgInavAI5dFqmvPaVmxCQBJOOkPDPCQm75NRNVZD0I=,tag:yp2d9VRiJyCfSTA9dzFuww==,type:str]",
"sops": {
"age": [
{
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2c2VPUzRXMUN4MkN5VEtG\nQ0ZsaERON1RxOHdPV2ExU0tTSWhhS3NHS3cwCklXczlsRWVwYTZYcDMrazc3OW44\nMHlxbk5TNXVhcUdjcklHQWl5VDdieXcKLS0tIFE1Zll4N0VDcmE2UytHTDE1QnZN\ncXdDS2ZKbU83ZFBCc0xpTmNBWmdaeGcKYxXG0owNFBP018n4PDYoDLzHG+Z5sSoz\nPjKV/aNQZvn2qRAulXD2lembRZvEEsViBETfnOFV3zNqmWSuKQ20sg==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yey6gxgsyl4tj6ek0tve2pckt6qersqspk66ukkzum8mrr6zppqsj4jn3m",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQcDh6OUkwbVpzQnF1Vjcv\nRnd2eUp5d2lXeGhUS1lIU1VsOE5hOFZJb0NvClRUdEpsZEdyb3lBTi9DNTN5eUo1\ndDdTT05xY2kzTW1DUVgzbDBUZHIxTGsKLS0tIGFNQjhadnkzSWRvWkMxMnlvR2xV\nUDVkTWdMZlVMQmdVUzRsOGovc1g1QnMK9ciVz6d+sV5QelnGzduoKO3l9rZhE6H0\nDgZ6I4ebONSXr87CYzlZ/ZJmaQ1Gca/qppdvmONtNYZdcUvcxJWaVQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yubikey1qd859y9ehz2ya8j2cftwrtmdeqhuk7r7yc52zp64wpff6068gwrac3q6nsa",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFWcTVydyBBdXVuWVNk\nc0h4b21GQVlrb2hHOVRqdUZUbnpLa0R4MXJzZ2VIblhYaXBxcworY0RMNVN4aEVZ\nYWJMc05YYVdRVENLQjhQMllPVEhkNHgvcDhUOWN6RjlVCi0tLSAxRWJkWkdWQmtU\naHNkRWNoL0RCcEpyS1VvNFZvV01BTGFSWHZHc1poUUU4Cls9GVVnlzcQp/Si7blT\nsw7EOLeqWsap9pnZrNUzOZ5CAGH4pIryJtbnTJdR4RC/ohWFGZoIPd+MYghpA8Uh\nVa4=\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2026-06-20T19:26:30Z",
"mac": "ENC[AES256_GCM,data:Q6OM/6Xsqj9R/rWmbxKBmIZ2z0F556uBjWwGBiN/MXprPYJpQ+bqxwB1E9U+t/xDDAprn5zKRB7emXAc1DYr00SfAFdJL0WQF79t38+b4YJD+uxaXTGT2czhDODMofNwhpUJ2YrC4IWDwt1ZKJcWSIYG2rQevKaL9chjakC1ZPU=,iv:VsQMopPCsJ9W1IDzUuNv1U0Iv5vpwZ3EXhkuqVV8+RU=,tag:ujcjDgd2wTC8xHcqzMLqkA==,type:str]",
"version": "3.12.1"
}
}
@@ -0,0 +1 @@
../../../../../../sops/users/berwn
@@ -0,0 +1 @@
../../../../../../sops/groups/admins
@@ -0,0 +1,18 @@
{
"data": "ENC[AES256_GCM,data:gE+MLYLJ8S1o3zKYTL/ZDCbNObr0eaGpDjoqOTg87nDceyjOpvF28Ii9,iv:cZyTOtPH6NR5kAfkvU5vuLk9agyU+3csRH+qLbh3ixQ=,tag:RV/At3rf8P3CRpp7ipStAw==,type:str]",
"sops": {
"age": [
{
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpTlYzUWMrQ3A2b3FLUzNl\nenRkb2lvRXkrT3dOaU5ndzRUNmN2c0FwSUdZCmdjVlhhb0I2M2FMVUpBU3U4Rmpu\nL2tHVUlDMjZYcE1jYUd2UXY5eDZhOU0KLS0tIGMxRWFDMFUzU1ZWT0ZkT2ViQmJM\nNVNXRUxuaDJ5TEZiRzdjbmRtV2o1WXMKaTjenRv6FOScMa0bVI2mGAk4rIazF0jt\naqGv8DHMxfBBeG713V+qEKLTOeQOaGalffIOPARD+LAaDJpJ5p8PNw==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yubikey1qd859y9ehz2ya8j2cftwrtmdeqhuk7r7yc52zp64wpff6068gwrac3q6nsa",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFWcTVydyBBalF2OE91\nL0dRME9ONXZUTWV0MEhFWmYveGp6NzhlMGZyaGczeTB0T2JXUwoweGxFN3hUbkp2\nRTduNXBNbW52Q3QvU283aEgyOGVuaWZvb2dvWGd4cFVJCi0tLSBpd2FIRCtYd0F6\nZ050YTRGQktrNkliNWovZXhSUEYzbzhFclJUWS9XeGxrClGhlUZvZtllc7DQMkHv\nQawFKfC4ghyi3VzKsE0eAF9qZFACcFnT2lAni+q+gyFjoprseEqpGvFSzkVZ5f41\nge4=\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2026-06-20T19:26:30Z",
"mac": "ENC[AES256_GCM,data:eyHBLejUSyGReOi853jnzlvcPs8Ps/PpsE+iQ19LX/G7jAqTcfA1mcJwnRj4DKpFFVKVY5D/nA3RZh9k1sOIPfNVRYW54OUxhx5MegBr+aPuv3v5STAMoEzppbcRmrpEIk5WCMnW6IF5WWgxDi9cUveGw91ydelgr+2nEAaPTQI=,iv:vn3Xi/QibbShdqAvO73gyiCxAfDDeNm7/VKKI3qKtTc=,tag:oN+cz82HeQ8ynjEi0MTQTg==,type:str]",
"version": "3.12.1"
}
}
@@ -0,0 +1 @@
../../../../../../sops/users/berwn
@@ -0,0 +1 @@
25.11
@@ -0,0 +1 @@
../../../../../../sops/groups/admins
@@ -0,0 +1 @@
../../../../../../sops/machines/web01
@@ -0,0 +1,22 @@
{
"data": "ENC[AES256_GCM,data:5DOEg1faBwKP6NOJqDakx0u0AxaTgFUWuQOLaVtWVyqgsmB3ewr1u43GrDTTSuGiUkE2sdFlOMOtfie5ILby,iv:p7MJS3sPuuPzS1kHDfB1aqLBYhhMEDdxalJiItCXzKE=,tag:a8H2LCON5H/yGgsc5oiruQ==,type:str]",
"sops": {
"age": [
{
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByMkhGVGVzNkVkS2FjNXNC\nK2IvWG9kcHI2MUtOK3hRaG5IQkJYUnJ1WmpJCk91UnlIRjQyOFY5R09vSXhRcG84\neVdOc0tnNUM3WmlDZ2tDL3NQMHBDNjQKLS0tIGp3L1dxVGs4V0RoUTc3SVVaQnIr\naDYrUjkyS1Z4NzBGaEJQV2tLdEZWaFUK/w0UJTSjPqd0eStX05t4eXGQl4k0b7aV\nMAoP3HpayNBpS2K6s+U/L2siXNL7kOhV9JK5oy/bHhOg77wDTyPIxA==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yey6gxgsyl4tj6ek0tve2pckt6qersqspk66ukkzum8mrr6zppqsj4jn3m",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6MGYxNmlLQktCK0VoS2hw\nRlBFRFErWk5YR0U3a0JqdkVMaTJCWmRrTDNRCjZ4V0hBcnhPSFNpVXhiQnp2Y2ty\nQitnbFVQbHp4V2pnMkFGUi9BRlV4QUUKLS0tIHp1SERLcWRjUmVxQTBOQytFWmIx\nZzkvTlp0WTF6QVBEQS92YlVvRWV6R3cK1bcu6p2WzjUdvEt2oObNhcTsCQ3cRspN\nAuTK/yJuVkpO5e5Fdqv5FDAnBbXSIu6u5ETao+w22wx3KifwBLoU/Q==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yubikey1qd859y9ehz2ya8j2cftwrtmdeqhuk7r7yc52zp64wpff6068gwrac3q6nsa",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFWcTVydyBBNU56eDhu\nY3N2bUdPSjIzREYrMTEySjRKbU9EanZ1d2k2RjhuNy9GMWN0ZgpYUWNidUFqM09u\nRkllRWRPNmU1bXo0d0VkZHllVitXUjF1THZEakZxeTFzCi0tLSBZTG9KRm9rcFJZ\nZnRNMWRiSjUvZVJrOVE3OXJNUnJYcjFBREpFY3JrRi9jCszy3Qg2juzFSa4M8KPP\nGQOPoECn2M6JTnM28zFVsBQTM4TlDKvqhWvF7Mcg7H5WwaoPcx/jh9IDpn9HLTzt\nH/k=\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2026-06-20T19:26:32Z",
"mac": "ENC[AES256_GCM,data:WKZbfq9lZpMbt9ukGW1T17F1qwSmjI8b1uJ3Eq1j9KOL4g+GOLvQnVQ7EkW125Q+A4HGplBCswFyKDGugmnIX9GgHYQ60In/uqQCoZ+b+LWBUMPbDKnMeBxHjbfR9utU0rT9dSyGuYUryeYZp0TTfGXfKvUbPMlwQHKENVoAx5U=,iv:CHzy62kFQAWoT+bbYELgQYAen0naknow+xFmvK5CMak=,tag:VVsC5hJ6Fh79/EX/9yqIIA==,type:str]",
"version": "3.12.1"
}
}
+1
View File
@@ -0,0 +1 @@
../../../../../../sops/users/berwn
@@ -0,0 +1 @@
../../../../../../sops/groups/admins
@@ -0,0 +1 @@
../../../../../../sops/machines/web01
@@ -0,0 +1,22 @@
{
"data": "ENC[AES256_GCM,data:Nsf/majWr/OSPQ2X0GcAyimYqpNp+z12EijFGnXk4sicyDqH4dxagVcMBFhnvme0JOJ0mXPpKfgEVBYjYr7NbSffnwrOwPzXfxdlWnw8r6SoW7JI9jkfdll0kDcdt15s,iv:cjGPpl38zogCLrcrSMUckPHzGkSMelGY1XZw8M++mmw=,tag:e+QxoLPnagcMFoGAh+gqgw==,type:str]",
"sops": {
"age": [
{
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEcnRpWTZPdjR2akMwTmJG\nNnFrV2pVK0dReEZVM3lGbktYM0l5Qi83T0ZnCmxQV0xmaE5ocmNQNG5kZzVNRW1R\nMFI3M3Ira2prU1V2ZysrQWxlMktJaVkKLS0tIDhBa0J5eHRKT2xGUWxHeTlDcDky\nQ1NnQ2hhbURuMDJKRkp5alV1WmJpNHMK4nrPd4VvBXyd1/uhrVDMnPTe9GNs82X8\n6ygCzPoTdsQ1jDTVE/UeOY0D2ZjVJz4e9aU1mFXokgpbyKsAdAPlfQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yey6gxgsyl4tj6ek0tve2pckt6qersqspk66ukkzum8mrr6zppqsj4jn3m",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4c2VGVkZvWUR5QU5jUEVY\nWHk2N1FQemZ0STZKenJCaEw3SXVvKzVNYWhrCitlWDNMQkpuOUF2K1pBK3ZIankz\nZk5ZUXNtaEprNTFsOEFyQnZ4YUwyNlkKLS0tIDhYbmxubjhyR1Z3QlpIaWVweTFJ\nd2ZaKzBuUUtFWnRRSEdrNmduYUc0QmMKubs2U1/kAmkygXxQYL+YDd75pelKQDmf\nFr9nrhUTINGwVjbFQpj2BUchCH6Q6AhGJpzdW3mY4LFya/u2WkadjQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yubikey1qd859y9ehz2ya8j2cftwrtmdeqhuk7r7yc52zp64wpff6068gwrac3q6nsa",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFWcTVydyBBem0yRC91\ndWNjbDZQSHdUZktTMGhiaFhjT25DMUxEM3FaUGtrVWYwekhvMApaU2FaVWNtbHpO\nY09HZmNhU2hXSmpUb3Qya00zaU1ISzNtSFoyVm83aXNjCi0tLSBndEhHdVFsZDZi\nbHBYUEIyME9yTjBJT0ZoNENuZ05DUEl2dGpUbWFaYmE4ChNBrn1NivTKAu37u6oy\nJt6nl0dFa784h80w6dWOxV+ot6SRo6gnmlacgUM4F/mq+p4VmARToPeve9uu5nXb\nglw=\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2026-06-20T19:26:32Z",
"mac": "ENC[AES256_GCM,data:N9XkxT8QdxqjX33ESP+UZc8fGI2rSl2jwuV1kgLx1u5VM7khpQyiv7fVNkKjPiZp+O1flkrxTFAS1z2ZPzkcjnF2+qYjREuRUr1y7yrRb48i7o5NLzhHPPbRodwhLuJAurEiWBfRuMceuEaztIpYS7nXUCyyW68qX4s2Mbuj36c=,iv:9FRDNRU0kfKuixxDmuojNS1a4VXs/V29eVhIHFquUEY=,tag:cqrb71HvPoQg1i5svTf52w==,type:str]",
"version": "3.12.1"
}
}
@@ -0,0 +1 @@
../../../../../../sops/users/berwn
@@ -0,0 +1 @@
../../../../../../sops/groups/admins
@@ -0,0 +1 @@
../../../../../../sops/machines/web01
@@ -0,0 +1,22 @@
{
"data": "ENC[AES256_GCM,data:x2yLufbE0itvC3labeOTHECNtcss8VCrYcTEUkPA0zus9s1FAv918DXtrPaTbblJrxsZaVF2IGMu5W1IxIc0pDZOeP44EbfXJHgHSq4z09csz+42pRAnzW6ogVhb9AouLQnELiRuWCBmKiyXGHPjtLYEouve1B2ExaHQcR40F/uU4PX4K03nbY4uljR9lRMO0NFBMS+8/T+7h58UJKFEzaxiVpvpmt9o0SetO74V7KOg/mPnV22I6+mo7H0AwdXRsawAps7AJsFGZbS2cnX2/nwnEjljAxW0hhJJJCHYRKljZS9mUWL/AxKFtlt8YzNX+o8OyMu7wcEH1emUg/Qp1gtq7hI8vUMjOYORYZuk,iv:4CMDEFFEk2DqTSVnIVVtOdE1Cz5IjOtMSEOXRZ/egWs=,tag:8MMFVTh0WGgrY81EelxkxQ==,type:str]",
"sops": {
"age": [
{
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLRG8rQVNQSUg2YVJZZFV1\nV09odWFNbnNSWFBnUXgzc2dHMEV1WS8xUTF3Cm5kMTM3STlYaXNXYXg5NXhxdFVP\nQk95V2E0R2NFTEI4ZkE1eFVJbC8rWkEKLS0tIElIRzQ0MS9nRzZGN0hveU1vS0Jp\nbTl3V0NreXpiK09jOWlPeVJaTTFDalEK6KXNLyh+XnsNRtQWgTW1UZii5SkzUZRg\ndO8Kg5dOeI3nreELnoQfQsq80PwFHxJTUsUaddOyFfP85+25GGpniA==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yey6gxgsyl4tj6ek0tve2pckt6qersqspk66ukkzum8mrr6zppqsj4jn3m",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2U0U0aVh0SkIyakZxNTVv\nZFEycU9SU0ZodzdPMUhBck1jVzBjeHFqRUdvCkszNFpjRmxwcnVqNFFZdTlhM3dQ\nRDBGWEtwTTNMV2plbksrajhjc1JlTVkKLS0tIGlMcktHdk5yWSs5N2tRT1Y1SzJl\nVjkvMUphcFh4Ry9vREhvWVIybjFUYlUKDRvpu7GEOR6MJ4QgiSe1W1CoqbHTuMx/\nccqLkJHhPqSP6PZP3RO9XWNWOMaqp654G05vO+VJ3sHiTJyrx/XeiQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yubikey1qd859y9ehz2ya8j2cftwrtmdeqhuk7r7yc52zp64wpff6068gwrac3q6nsa",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFWcTVydyBBcGkwZ1Nk\nQm1JT2FpMUxZWnFXVU82bUpJbVdqalBsQ1ZUM1VqM041aFNxQQowWUEvNXhKQjh6\nMTllM0EwYkQwZ3RpUThPTDdkbi9lTjJUQmpJTDRuV1hZCi0tLSBncERXNk84T3FG\nTTNXcEtuTko3YzdGdm5HcXZyek5pQThjd1NaVkppYkVzCkcNa/kkb6DZR1hdvkl2\nNpjc4x9V0ORwZ0b+L07kML1CKTyfuUNgr57dxsolagydEVSZNGW0WMG2lolNwod/\nGJs=\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2026-06-20T19:26:33Z",
"mac": "ENC[AES256_GCM,data:IIDRaejzWhEhfeHtViHaGDMALaflIipzdmMiLzJygHS8epNOBuRWQOLn9nDsL8reUgU7ezODaK7wuSy6gPbzUnS7FGYXkbLd+PVx5+fJgqha4nY/0WBNplmgdm02B+MqpSFC47ChB62uzKXZmuU6Jf/inRelf+csCGBadU3yayk=,iv:O15ueEPeugmO25bjwJHOkn3IVT8yQuw/2sz6q88n/Jw=,tag:d4Hl3U31sRxE8LaVMgfVrw==,type:str]",
"version": "3.12.1"
}
}
@@ -0,0 +1 @@
../../../../../../sops/users/berwn
@@ -0,0 +1 @@
fd06:1bad:ece2:92ad:ba99:93c6:5eb7:bfbd
+1
View File
@@ -0,0 +1 @@
../../../../../sops/groups/admins
+1
View File
@@ -0,0 +1 @@
../../../../../sops/machines/mx1
+1
View File
@@ -0,0 +1 @@
../../../../../sops/machines/ns1
@@ -0,0 +1,27 @@
{
"data": "ENC[AES256_GCM,data:77a5VqyU7Q3Gg5ngvFK6bC+0n4pg6ndpFQWMqM3AOdjjlZfoIwHjEXZzSO8=,iv:Kw/AkO3PpHeIprdqea+ZOLrmxwr6/mDSF/5veg97sb4=,tag:brxb6U0D2Dw7Cq/tRGSh2A==,type:str]",
"sops": {
"age": [
{
"recipient": "age1fanu282vm7njjweqhrpcfcwpttuhce8js4tsyfry98l0neaqpewqs5s7nt",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOOC9WRENVd2FoMkQrMHpa\nNFI4YmtUQkZITmF2eEV3UnBRTWpIR25ROTFZClNaM1U1eU4xYTN4OVd0SDg2Ylp0\nYTFZSEZMaVRrU3B5eWVKOWVsMVR1QkkKLS0tIDJmc2gwb1IwbWZCejdBa3d5L0xh\nNUlWbm1IbTdyTVNFK0FEMHpERDNmeDAK+9FDHeJpb6Bh+utFfLRhOrtdjzx3eJQt\nPhM6Mf1K+kFV37iMj5yOFLq51tiyen5J53ExS6ppe0XxYJfKKtb2UQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5dEZLaGdlbnZZRFFhSWVs\nOE1RL2pLcW5rOThaSGszUnJGTloxR3FGUEJBCmluR2xyMWdlYXRoWVFmTnVTYWFp\ndHByemJyRFNqVG80TElGSWhjbDM5K0UKLS0tIGI4R0JRdFNYa011UU9sL24xT0sx\nQVl5Q25iWW9ET3YzRUVTa3RBL09UbTQKDJ/ikDMDP3ATh8lahqmvys4gNXj7gfR5\nTMG+DMLoqdOekd+iUIU3Wb/eGg2MsUXdN4UjyTOI4hOlN03Iofhb1w==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yubikey1qd859y9ehz2ya8j2cftwrtmdeqhuk7r7yc52zp64wpff6068gwrac3q6nsa",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFWcTVydyBBN0lIa3Rj\nc2g5L2syenRZdjlyR202RjROdmpVdWVmajBHMXc2Rnovcy9pZQoxSzBwQlVKVlE4\ndUs3TzhIUmRkSjU4cER1S0wwcTE4a1U2aFVKNVZkM2RnCi0tLSB3bE0ydC93U2FG\nWmpiME1FcEJZRmw1bXI5NzVaZE1aREdJVFJCM2FWZGNZCj0dJhb69fNDEfNJUnjX\nKcWKH0kAlttmduIdnN1j9VHuNtfcmKJncCV5WYwfzwErsOc6HB0pTw6bnJBUbtbO\nmiQ=\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1l5hw95p5h4sthrgn0usms9yfkwwmcvv34tjgrtv9s4e6x39chacshgxavs",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmaTBwcmg2OEFveEQwSm9v\ndFc2V0tlY0lVejdjeGNTWGwxeExBcVljd0hjCkR4cnlndG50RGZJQm1HdTdqUmRt\nT3RzamlBeHYwcEpVMHhEKzV0eE9VSW8KLS0tIGpBdnpCdU5FSFRZSW83aWpocUJD\nWkQvYTdrb1lWTTlIZG9DK0VOTlAvYk0KG0zbImkrDloCXN/XPKR5uWsL39CagOnk\n8qZTtQepja4RQAagOoGanybY0OK8sCCK/4NSsetaProyuXGQLBEWDg==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2026-06-18T07:11:36Z",
"mac": "ENC[AES256_GCM,data:K3gMR4fz+/H9SJ3zyOk1lyskn+2eqgy28IltRKH3gSGJHLqgFhkYJZ3lSwvpAv9uDC2PQ8XFQMrM2SHAjhgAbyR5TEN/TZHk2RSWbjsj7m0r7TlCwawyxesU/+ih/pOu6XLG13BPakNEfc+ak+DiG+SfdpMpgdUEHX+ZEPgQC+I=,iv:JHLZEwtzth1nsocyrVw+iGOPbOZEBvryaxD8zW+p72M=,tag:as0o1kT5pweVRXHxP0QRrg==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.12.1"
}
}
+1
View File
@@ -0,0 +1 @@
../../../../../sops/users/berwn
@@ -0,0 +1 @@
../../../../../sops/groups/admins
@@ -0,0 +1 @@
../../../../../sops/machines/ns1
@@ -0,0 +1 @@
../../../../../sops/machines/web01
@@ -0,0 +1,27 @@
{
"data": "ENC[AES256_GCM,data:zuuXCjHZTXCZLD6lB6UFw9BE8VOEXmRW3w6QBDREkH5PieT7myP8kn4zroQ=,iv:Hb5uNJRORGExiqm9+oPjxZjAq8vcsStqFqslNnixuFA=,tag:aQ4NT5ayG95E4ROKyfLZXA==,type:str]",
"sops": {
"age": [
{
"recipient": "age1yubikey1qd859y9ehz2ya8j2cftwrtmdeqhuk7r7yc52zp64wpff6068gwrac3q6nsa",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IHFWcTVydyBBcXNUZm1u\nK3A5dlVwS3M0c0xmb1dzK2xCa0hvcGJ4SSsva3FWcGRTZUJ3bQo2Wmw1VFRtSDFL\nMzkwQVRHSlpuenRsR3h1czVUZVRiU3owN3dTS0V5M2NnCi0tLSBvMWRBaWppbFl6\nU01GNVFwYS8yemR6RGVsRTRQY3g4VzhpRGh2L2h0NnVJCj44SxR5IyUAAnegpO/a\n9sPCmmzSrJNELMn3uH/HDV58LZfvIWCNl6QC/op6pF/n9l6wRv2dQPc3+9LlXPKr\neaU=\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1fanu282vm7njjweqhrpcfcwpttuhce8js4tsyfry98l0neaqpewqs5s7nt",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1bmh3b3lMKyt3eGpGSXVq\nb1c1enhIcXpxaDcweE9ZeDNMS2VXejdpUG53CkRUK2x1OGc3Vld2bjVxd1RPM1VS\nNW9UZC8zSnJZdTJEeGJLV3VrRUQzSjQKLS0tIFNnVFJ6MzJkaXE1U2pJRnBpdXB2\neTFWOW1ZTTQ5a2xZNFNMTmN4K0RabFEKQuA6j2XigQsjrx7MzwXZmaROxXVKzrkL\nZl+kOkFj0J3MWHIwWPrUlCnP5u/7Eb/UYOU8tkJ2DD1CzH4vansR1g==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1yey6gxgsyl4tj6ek0tve2pckt6qersqspk66ukkzum8mrr6zppqsj4jn3m",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1UXpvcXNnbmhXTVFDd3FF\nVGpMaElTNXdZY0xzdkRRQUw4dXVEMm1xNGs0Cmp2bkExUkZCSDVwOWZBMzlPdUdO\nUU13SnpKYVR2eWxGUzhWeFBTeVJTRTAKLS0tIEtIekNoT2V0bFRzZnhRYzdud0VR\nUTROdmpHNVYzbmlYNVB4aTlnZ2U2VzAKUJrNHqx4LLbbmUEuTedviDDgGEDMXna3\nasaL1fU7G1XMTMHOIM9200JzEPW0P13pO6D65t/bueeljyZy8wlnAA==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkNXRSZWdpanBiZ285V09X\ncWs4Z0tVZDNRaktNejR3ZTFKTDFxcEFLOFVVCnoxbXdocWR5aE9saUlzL0RCWDRt\nZTFlKzFQR3NaQWR4MUFMUHVPMmhseEUKLS0tIEtHVWtiZ1RadzdzdURHQmdRd1JI\nNXViS1ZMcjhsazBwai83Smd5VFRXdmMKA8XLlBHBfmzzkEZhbOVpcOPQkgYHzpzT\nloG7LOjEt/BA20I8nhuIIZ3InCEwFXmhFzfmSKadXFZxvX75OsMZ4A==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2026-06-20T19:26:29Z",
"mac": "ENC[AES256_GCM,data:b9Id+7Sh9fCl4iWhLwiEMjiAIEKEz5L7c4eiFynxjPTyCY9RTEeBwTPOh1Fk1czV0TuKNvwlAkPlkpKBNFWGeEopBQtxUx/Gm1fmNJq25oRuLPqRMd1P/8JrkWOoMwL4pbQsnnOeQSy8SJf4044/jANFyTzC6qou/uIDqMLM2Zc=,iv:OGxOAE2whJBjJHABLPq1gC0DXEUzaqCkAWmRxhRc4lo=,tag:EzbEUdUpRSXxDmGIO/BeuA==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.12.1"
}
}
+1
View File
@@ -0,0 +1 @@
../../../../../sops/users/berwn
+1
View File
@@ -0,0 +1 @@
../../../../../sops/groups/admins

Some files were not shown because too many files have changed in this diff Show More