feat(hakurei): kanidm access

This commit is contained in:
arcnmx 2024-01-23 11:54:59 -08:00
parent a8cd175500
commit b85e850dd6
10 changed files with 322 additions and 35 deletions

View file

@ -9,6 +9,7 @@ let
inherit (lib.strings) concatMapStringsSep;
inherit (lib.lists) optionals;
inherit (config.services) tailscale;
inherit (config.services.nginx) virtualHosts;
inherit (config.networking.access) cidrForNetwork;
cfg = config.services.kanidm;
access = config.services.nginx.access.kanidm;
@ -39,17 +40,26 @@ in {
};
domain = mkOption {
type = str;
default = "id.${config.networking.domain}";
};
localDomain = mkOption {
type = str;
default = "id.local.${config.networking.domain}";
};
tailDomain = mkOption {
type = str;
default = "id.tail.${config.networking.domain}";
};
port = mkOption {
type = port;
};
ldapPort = mkOption {
type = port;
};
useACMEHost = mkOption {
type = nullOr str;
default = virtualHosts.${access.domain}.useACMEHost;
};
};
config = {
services.nginx = {
@ -59,7 +69,17 @@ in {
port = mkOptionDefault cfg.server.frontend.port;
ldapPort = mkOptionDefault cfg.server.ldap.port;
};
streamConfig = ''
streamConfig = let
inherit (config.security.acme) certs;
sslConfig = if access.useACMEHost != null then ''
ssl_certificate ${certs.${access.useACMEHost}.directory}/fullchain.pem;
ssl_certificate_key ${certs.${access.useACMEHost}.directory}/key.pem;
ssl_trusted_certificate ${certs.${access.useACMEHost}.directory}/chain.pem;
'' else ''
ssl_certificate ${cfg.serverSettings.tls_chain};
ssl_certificate_key ${cfg.serverSettings.tls_key};
'';
in ''
server {
listen 0.0.0.0:389;
listen [::]:389;
@ -71,8 +91,7 @@ in {
server {
listen 0.0.0.0:636 ssl;
listen [::]:636 ssl;
ssl_certificate ${cfg.serverSettings.tls_chain};
ssl_certificate_key ${cfg.serverSettings.tls_key};
${sslConfig}
proxy_pass ${access.host}:${toString access.ldapPort};
proxy_ssl on;
proxy_ssl_verify off;
@ -84,10 +103,14 @@ in {
inherit locations;
};
${access.localDomain} = {
inherit (virtualHosts.${access.domain}) useACMEHost;
addSSL = mkDefault (access.useACMEHost != null || virtualHosts.${access.domain}.forceSSL);
local.enable = true;
inherit locations;
};
"id.tail.${config.networking.domain}" = mkIf config.services.tailscale.enable {
${access.tailDomain} = mkIf config.services.tailscale.enable {
inherit (virtualHosts.${access.domain}) useACMEHost;
addSSL = mkDefault (access.useACMEHost != null || virtualHosts.${access.domain}.forceSSL);
local.enable = true;
inherit locations;
};

View file

@ -4,20 +4,39 @@
pkgs,
...
}: let
inherit (lib.modules) mkIf mkDefault;
inherit (lib.options) mkOption;
inherit (lib.modules) mkIf mkMerge mkDefault;
inherit (lib.strings) escapeRegex;
inherit (lib.lists) singleton optional;
inherit (config.services) tailscale;
inherit (config.services.nginx) virtualHosts;
access = config.services.nginx.access.proxmox;
proxyPass = "https://reisen.local.gensokyo.zone:8006/";
unencrypted = pkgs.mkSnakeOil {
name = "prox-local-cert";
domain = singleton "prox.local.${config.networking.domain}"
++ optional tailscale.enable "prox.tail.${config.networking.domain}";
};
sslCertificate = unencrypted.fullchain;
sslCertificateKey = unencrypted.key;
sslHost = { config, ... }: {
sslCertificate = mkIf (!config.enableACME && config.useACMEHost == null) unencrypted.fullchain;
sslCertificateKey = mkIf (!config.enableACME && config.useACMEHost == null) unencrypted.key;
};
in {
services.nginx.virtualHosts."prox.${config.networking.domain}" = {
options.services.nginx.access.proxmox = with lib.types; {
domain = mkOption {
type = str;
default = "prox.${config.networking.domain}";
};
localDomain = mkOption {
type = str;
default = "prox.local.${config.networking.domain}";
};
tailDomain = mkOption {
type = str;
default = "prox.tail.${config.networking.domain}";
};
};
config.services.nginx.virtualHosts = let
locations."/" = {
extraConfig = ''
if ($http_x_forwarded_proto = http) {
@ -65,26 +84,31 @@ in {
internal;
'';
};
};
services.nginx.virtualHosts."prox.local.${config.networking.domain}" = {
local.enable = mkDefault true;
forceSSL = mkDefault true;
inherit sslCertificate sslCertificateKey;
locations."/" = {
proxy.websocket.enable = true;
inherit proxyPass;
};
};
services.nginx.virtualHosts."prox.tail.${config.networking.domain}" = mkIf tailscale.enable {
local.enable = mkDefault true;
inherit sslCertificate sslCertificateKey;
locations."/" = {
proxy.websocket.enable = true;
inherit proxyPass;
in {
${access.domain} = {
inherit locations;
};
${access.localDomain} = mkMerge [ {
inherit (virtualHosts.${access.domain}) useACMEHost;
local.enable = mkDefault true;
forceSSL = mkDefault true;
locations."/" = {
proxy.websocket.enable = true;
inherit proxyPass;
};
} sslHost ];
${access.tailDomain} = mkIf tailscale.enable (mkMerge [ {
inherit (virtualHosts.${access.domain}) useACMEHost;
addSSL = mkDefault true;
local.enable = mkDefault true;
locations."/" = {
proxy.websocket.enable = true;
inherit proxyPass;
};
} sslHost ]);
};
sops.secrets.access-proxmox = {
config.sops.secrets.access-proxmox = {
sopsFile = mkDefault ../secrets/access-proxmox.yaml;
owner = config.services.nginx.user;
group = config.services.nginx.group;

51
nixos/acme.nix Normal file
View file

@ -0,0 +1,51 @@
{
config,
lib,
...
}: let
inherit (lib.modules) mkMerge mkDefault;
inherit (lib.strings) removePrefix splitString concatStringsSep;
inherit (lib.lists) head optional;
cfg = config.security.acme;
mkHash = with builtins; val: substring 0 20 (hashString "sha256" val);
mkAccountHash = { server ? null, keyType, email }: mkHash "${toString server} ${keyType} ${email}";
mkHost = server: head (splitString "/" (removePrefix "https://" server));
mkAccountDir = { server ? null, email, keyType }: concatStringsSep "/" ([
accountDirRoot
(mkAccountHash { inherit server email keyType; })
] ++ optional (server != null) (
mkHost server
) ++ [
cfg.defaults.email
]);
accountDirRoot = "/var/lib/acme/.lego/accounts";
addr = concatStringsSep "@" [ "gensokyo" "arcn.mx" ];
in {
security.acme = {
acceptTerms = true;
defaults = {
email = mkDefault addr;
server = mkDefault "https://acme-v02.api.letsencrypt.org/directory";
keyType = mkDefault "ec384";
dnsProvider = mkDefault "cloudflare";
credentialFiles = {
CLOUDFLARE_EMAIL_FILE = config.sops.secrets.acme_cloudflare_email.path;
CLOUDFLARE_API_KEY_FILE = config.sops.secrets.acme_cloudflare_api_key.path;
};
};
};
sops.secrets = let
accountDir = mkAccountDir { inherit (cfg.defaults) server email keyType; };
acmeSecret = {
sopsFile = mkDefault ./secrets/acme.yaml;
owner = "acme";
group = "nginx";
};
in {
acme_account_key = mkMerge [ acmeSecret {
path = accountDir + "/keys/${cfg.defaults.email}.key";
} ];
acme_cloudflare_email = acmeSecret;
acme_cloudflare_api_key = acmeSecret;
};
}

78
nixos/secrets/acme.yaml Normal file
View file

@ -0,0 +1,78 @@
acme_account_key: ENC[AES256_GCM,data:2UobeSLGw/GMXEeB+fdjG4jnDhWtm1j3U5SOZYwwJEF70g5z9DW1r2yqkj2/nBBgYUR6UvDo2f66iRCurRJtBPIf3KOOwtef1+MiD7QARgqDQoKCf8IUTS4SittHC0nYLFp08uZ3zXI8P4aqXwZdQT+DH5ARtZo4CaRWXa7JW2fkCe5v/Myz7LhvDZmpcitvPFTOpsZoHnUdPC2/cnTdUw/h2rEt1iNYVnchhHAt+AF9IMLZT5USotc9H4vpfXAqTcMwgI6tHvsJnnxorSVg7yfSYJUEiaFgYaqO6sITZffW+CXeuWdrg+EZB03VHmxJ5fwee2tCDyogSQyB6fDcnySK/bdzZhJBIE5fsfr9D7xuR6bUptuqjR/5STnqCYGO,iv:wJe/ta9hxeppqUI5hBErBS6rffhki8eiogSGwPDI/nE=,tag:gzJz4guO/hz9zxwSbs85Uw==,type:str]
acme_account_url: ENC[AES256_GCM,data:wsZvniUCTS8EcjCCKzUjKgeTCUiNNS5nGI4jq1N7R9tzr+Ng2o2rpMLkXsQhVHHlEA38pvLA/+pe,iv:XxS2kzFEWA33QQR+RkHQo0JN22MIliETYOqNSnS7rTc=,tag:GOJ9sgRx5u+7siCN7Fb3LQ==,type:str]
acme_cloudflare_email: ENC[AES256_GCM,data:AwOryq31gjMWyEbEOA==,iv:SHNpv3d8fj47o0t/k3Q5JwjJwlA+UKW8pJC5uUJjuJw=,tag:AZbV8wciD0b6o3lcRnywcQ==,type:str]
acme_cloudflare_api_key: ENC[AES256_GCM,data:nadEPYM6QTgRiP2gmER1wN9tPBiW6ToKaIcIOGfQkBXZvzrlMw==,iv:a+3ujE4Cobvh3VSXmSH4iLsXggM5m4uOPj8ygQvPRGc=,tag:h+ZwCPUiMca42nK1JybLFQ==,type:str]
sops:
shamir_threshold: 1
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age12ze362pu5mza6ef9akrptr7hfe4auaqul4rkta7kyy2tnrstqensgmujeq
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzVldrQU16K0tMYTlFcFoz
cVV0ZnJQbThFbncxdUdGaS84OHF0VXZmREZFCkdzcEc2elpZTHIwMWVKeS9KNnZp
RG9aM0cwYXgyczhXOWhOSXFhOTJEVjQKLS0tIDJ6cDNzaCtFVjBjeFp6cFNiUGtM
eFpJSzFtN3hrZnRSeHRPT1lqc2JhOXMKCu/RpSyXajKtbSqEUJwLH2KfxV8D88q3
KSD5j7oKxZvaX+41B5GKqKw9aIj2Tbsif6BlI8OLaF6o2JkDSEvUvA==
-----END AGE ENCRYPTED FILE-----
- recipient: age1a2quf2ekkj94ygu7wgvhrvh44fwn32c0l2cwvgvjh23wst90s54szdsvgr
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHbXRDbk0yRmh6TUFvOVVC
YjM0Qno0eWRyT3d0eXp4eXhtR0drN252aW53CjVJdk9SMkd5dHhTdFQ0TzVBamlW
bG1SN1pBU0tCRDJyZGRZb0JIeDNNb2MKLS0tIENYOWd2d0psL2l3WWs0Slg1eHR0
NW54YTF0a1VOdDdjYjNXSjg3T0phd0UKgQR/MgKOm/WtjQBWxDxNkHshens3bARL
NE+CsmOmmpUtgbCPXO2oS1pPLZvd5v6Lgv6TQFradklDZcvdQvRaUg==
-----END AGE ENCRYPTED FILE-----
- recipient: age16klpkaut5759dut8mdm3jn0rnp8w6kxyvs9n6ntqrdsayjtd7upqlvw489
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArakp2SHpzT3NDd0tMcmZB
R3JLMUNHZWprbHNLa2E1QWZpaU92V1hpb1RNCmFaM3NGRGl6T0F5d2E5aEpQQnoz
Slc0ODdmVm9KWmdsZXpWM25pdDczbk0KLS0tIGt1TGM4ODZCd0lzVm54TWVzS2pq
TEdEOGdyQ2tBUEkxS0h0Yk1UbjJDWFUKtLA3MJAWMjfd03rKBaW3aIIMJS/OkRqL
Tu4JrcL5Lw/Tj7SU0dwxTsp+fGHuXsvQSO2z9BQmy9h7k7hSgSrRSw==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-01-23T18:41:11Z"
mac: ENC[AES256_GCM,data:45SskHZXCnEMERHc3R4hD4cQ/ihWnUdFunTPgrBGYdo2fyCMbWyAdSQOMl0u6FkC+NbG66lzgBVXBPnNfw2GX8iVLJ3VQyRW5WA4YW1x1wLbocO+XkMtJmwtiPiOrJfjbs3mZfWmt5hQZI/gQsonQrccD9/UxWip+Hal4prhY50=,iv:1ka9lW88NoaAEgRp5TxC2L6q+kVR25HxxQGVsPlaGho=,tag:gPYKvKcZp5k4TA1Dfl8C8Q==,type:str]
pgp:
- created_at: "2024-01-23T17:18:24Z"
enc: |-
-----BEGIN PGP MESSAGE-----
hQIMA82M54yws73UARAAmBYoYHSUmHR1gRvVlfaSaj2fu1Q4UxBfhj1gvu4SdvAZ
g6bZ108uFJR8XZX/P9SveA9ZBCmtu2Yrdtmp88q79l4A/pGWB15xF2TYFegjarED
ARGAry0yH9oxSOo30lB+1C21nwiJZg3NTOty6kE5Y01vsM7pddp8tvkIemJ4+MO0
aOkTrPCkoV7K9DTQ0OoEUI6U09F01hpFAOegHFWh6XAv3NDQXGA3tY1hYbi5SZ70
i1uSEljL2dQTajbcssoT8G+2238kbmT+qjIiIOD0I/BmjR3/4z1VwOY1mndHo63C
dTdZaXweG06HwJxlUoO1tvwcf5mw8ImQ10+qfpzBtAHF8JChRRPAgPta4yBUo334
XcspomSPIOcp1gLoRd37z4whdzc8UXseTzdF72gRlH/t6+cL0EMIapoIwcK+jKfZ
0Hw0vNyK2RCAnYI82vxzj56iGgNx4RMVsruibFIDqXqy4i8ZdBnTuJ2rsM1suaE6
q0/pjb0qtaUSaV5WNpXcH1Um5sc2zWzXg4au/fMFsLkWF+7EJuubz4cT86HSBBXG
OPssQI4ODxBI0YPPT+uBdEqE3O23zZ2Onv92KFGfuu/3ybKTBvRun4uv8vaBHecN
4bEVjeVtYu9DnAKVsRkvj207G3KYPEjQB2R1Jjqn9Wd8nSg8QmlkDFAaye6+tB7S
XgHkTeeVgCZD/EKSyDWm9zZ/2/JpL0BiBFY4lWhVnqczS+kPFyJpdX1UgTJUnhBr
ZyLY5IIQT0d/FYUHPAuhOF6ziLIV1ZYqN0/TMb96kWXT730lnPMn9Avt7bJFlvQ=
=31ty
-----END PGP MESSAGE-----
fp: CD8CE78CB0B3BDD4
- created_at: "2024-01-23T17:18:24Z"
enc: |-
-----BEGIN PGP MESSAGE-----
hQEMA2W9MER3HLb7AQf9EP7uWwz9NmO3Cgvsw+dVKhGO4Ym2aNS2OfxuEf8nXlL3
dKf8M9FEF7daSlH0wETE5ZfwSE+UMqjJCxP+A9cCm9YW+5Y+4YAy/4oOPCCn4ggj
t5rwaVj9gfy8UzFibo060WOnBA0QCufChdm0FKAC6CdulrUGsi8NMFz48oVzPRoj
TPzetpVFIa9x7XpcVUo4lbt34JYvDnlVKsZA+wvNMzbnjwS2a5KBwDjsTlNdCyr/
5ayhifUUBcXRn/nGCRQ4zvL6ze09jkqwABNkGB9O37WnOFoyX1hfeDqMOHeQVstK
96VIa+H7LJamBZneAtQBUX8Wel07Sr/3JIkHcfg8K9JeAXePG5SIM251zkLkBOJj
zV3P98h2zAOBcJ3FBw0VlIJtegYSv/UxsmPaxxEghvBC4WVvDMahPw9lT/YlkUne
U4GZO3rNC+KFEUx1tlf3qE8IbioVJ9SJ+967uCmzng==
=i3ou
-----END PGP MESSAGE-----
fp: 65BD3044771CB6FB
unencrypted_suffix: _unencrypted
version: 3.8.1

View file

@ -1,9 +1,14 @@
{
config,
meta,
lib,
access,
...
}: {
}: let
inherit (lib.modules) mkIf mkMerge;
mediabox = access.systemFor "mediabox";
tei = access.systemFor "tei";
in {
imports = let
inherit (meta) nixos;
in [
@ -12,7 +17,10 @@
nixos.reisen-ct
nixos.tailscale
nixos.cloudflared
nixos.acme
nixos.nginx
nixos.access.global
nixos.access.kanidm
nixos.access.proxmox
nixos.access.plex
];
@ -33,10 +41,56 @@
};
};
services.nginx.access = {
plex.url = let
system = access.systemFor "mediabox";
in "http://${system.networking.access.hostnameForNetwork.local}:32400";
security.acme.certs = let
inherit (config.services) nginx;
inherit (nginx) access;
in {
${access.kanidm.domain} = {
inherit (nginx) group;
extraDomainNames = mkMerge [
[ access.kanidm.localDomain ]
(mkIf config.services.tailscale.enable [ access.kanidm.tailDomain ])
];
};
${access.proxmox.domain} = {
inherit (nginx) group;
extraDomainNames = mkMerge [
[ access.proxmox.localDomain ]
(mkIf config.services.tailscale.enable [ access.proxmox.tailDomain ])
];
};
${access.plex.domain} = {
inherit (nginx) group;
extraDomainNames = [ access.plex.localDomain ];
};
};
services.nginx = let
inherit (config.services.nginx) access;
inherit (mediabox.services) plex;
inherit (tei.services) kanidm;
in {
access.plex = assert plex.enable; {
url = "http://${mediabox.networking.access.hostnameForNetwork.local}:32400";
};
access.kanidm = assert kanidm.enableServer; {
domain = kanidm.server.frontend.domain;
host = tei.networking.access.hostnameForNetwork.local;
port = kanidm.server.frontend.port;
ldapPort = kanidm.server.ldap.port;
};
virtualHosts = {
${access.kanidm.domain} = {
useACMEHost = access.kanidm.domain;
};
${access.proxmox.domain} = {
useACMEHost = access.proxmox.domain;
};
${access.plex.domain} = {
addSSL = true;
useACMEHost = access.plex.domain;
};
};
};
systemd.network.networks.eth0 = {

View file

@ -14,7 +14,6 @@
nixos.access.gensokyo
nixos.access.zigbee2mqtt
nixos.access.home-assistant
nixos.access.kanidm
nixos.vouch
nixos.kanidm
nixos.mosquitto
@ -26,6 +25,10 @@
sops.defaultSopsFile = ./secrets.yaml;
services.kanidm = {
server.openFirewall = true;
};
systemd.network.networks.eth0 = {
name = "eth0";
matchConfig = {

View file

@ -20,3 +20,8 @@ output "acme_account_key" {
sensitive = true
value = tls_private_key.acme_account_key.private_key_pem
}
output "acme_account_url" {
sensitive = true
value = tls_private_key.acme_account_key.id
}

49
tf/cloudflare_dns.tf Normal file
View file

@ -0,0 +1,49 @@
resource "cloudflare_api_token" "dyndns" {
name = "infra-dyndns"
policy {
# https://developers.cloudflare.com/api/tokens/create/permissions
permission_groups = [
"c8fed203ed3043cba015a93ad1616f1f", # Zone Read
"82e64a83756745bbbb1c9c2701bf816b", # DNS Read
"4755a26eedb94da69e1066d98aa820be", # DNS Write
]
resources = {
"com.cloudflare.api.account.zone.${cloudflare_zone.gensokyo-zone_zone.id}" = "*"
}
}
}
output "cloudflare_dyndns_token" {
sensitive = true
value = cloudflare_api_token.dyndns.value
}
variable "dyndns_record_name" {
type = string
}
resource "cloudflare_record" "dyndns_a" {
name = var.dyndns_record_name
proxied = false
ttl = 300
type = "A"
value = "127.0.0.1"
zone_id = cloudflare_zone.gensokyo-zone_zone.id
}
resource "cloudflare_record" "dyndns_aaaa" {
name = var.dyndns_record_name
proxied = false
ttl = 300
type = "AAAA"
value = "::1"
zone_id = cloudflare_zone.gensokyo-zone_zone.id
}
output "cloudflare_dyndns_record_a" {
value = cloudflare_record.dyndns_a.id
}
output "cloudflare_dyndns_record_aaaa" {
value = cloudflare_record.dyndns_aaaa.id
}

View file

@ -17,6 +17,7 @@ module "hakurei_system_records" {
local_v6 = "fd0a::be24:11ff:fec4:66a7"
local_subdomains = [
"prox",
"id",
]
}
@ -30,7 +31,6 @@ module "tewi_system_records" {
local_v4 = "10.1.1.39"
local_v6 = "fd0a::be24:11ff:fecc:6657"
local_subdomains = [
"id",
"mqtt",
"z2m",
"home",

View file

@ -1,5 +1,5 @@
{
"data": "ENC[AES256_GCM,data:+OLe87CIr0i0eyw8xav9LVglS3SUF+CD5A7wr2EqOItsZhOnFyC3JzC/FR5GLlHQuix2Jdv6RWC6Qubsj6CVV+Ojxk5y1MFBYHTRSSvCR2NErb5mFn96NsPx0pUKtiYtDhk31GZClBrICNZd9J/CBWpcEuD8KQlvZHu/9lIp3dRepG9mo1F/bJC0NUO+eS9zds0tz0SmVNZn7FIz6+XK4DFUqCiFwMxqEAM9tkbRitkgaJFzP0nfVdtGSEnvvapnsFvneIW7yCPDBqNOjwIeyRgM1vyewvlf9lW2iL1dhv4gKvYmRvtEC8eq6LD9yK04N709SZUqxPpn9jO8QNIqHZgUsU0+9HymxSJmVvRZqaI1zS0HDAr4q+JuO+t1druEjZzyO5Pb9yNbXKcLK5x0ANOxhODnt5iQOnteq2KgSQWlgm5p0ol3WcDzlObMXXW+2R9KYwJ157AkkJck8d1QEtU8eSBsnJRBrdZwsaYzyfZ9M/GppzUIzHOGMIEByhskLIB7U+uR8K5eP70VvUyV4y2iQFdY5MxRrdtLMiQAe5RtLOMTTq5i6pZXpEF3hkGcYSVu2cHnioy1g4K4TPF4t3uiPZfGCQaz7jPssgwExpbkggz9efKYxADCV1otcqW3+0GUTHy+ZZorFJYhhINezQ8URXkGNr8b5rX12m1fb3i9HPb6AY0p10Oh5eetvBXVQK3oqrBYC63ZKCjMXHGkxAMFF/bLsjrppHaADTLld++u8ipVcamXBqBlVfIQGCpQkWRLcWX6MnPwkCNQQbWPYIHlEEjznF7S3jMKpBE56H+Pbaclejrr/ee7lFqcEBN5+P+WTVJqvTQ0f5W5Krq1O/Ouyrtp3ml00N+wZH3BMy/t+c03sE5rIv+/mdtF+wy0As5ibpC4ttTZ604N0bZwmd7PBhKHTXBvecOC8xofTvym7yoee23e5Q8WUVfOaz6Ph0rW/QvQSXUmdarPC2epz+9iTenGFvQ2gCKqUA==,iv:ZItahuq3XUdZdit6jpsTbedKPwb2cYw0omx33Rf6o9s=,tag:9sX7LQ58Z0qk9SSoPVuiCw==,type:str]",
"data": "ENC[AES256_GCM,data:B0yK5oRKy09y+IgkxVQgrTn4J1fD5crc8GPoJQfhaEi0iMmN7QhuVag8dyDAYVSQgeH/689hV5ugD11SKQqGqrpZ6VKMta7sATn4jykf6QD38R4jXbmRrvoEdgLzvCum+SAQ6DWR29uPaAKJt/02aOM5X+D2M+abwMEyW5CRfri2EvlQWlORd0PO/vSCJjgPbsXvXnJkzGj/mOjdMocL8e+d0uIkN57qSqSHdlZ1bFV/tqTjfTBaJ7wjJi1G8NywYl4cA8lyYyHqmTvWCvJ63OvDTfwQ7JjObjhcGAHvF4RyZV6nl9gBSwnYTdSd3aAbIBEPZwc5DQ6hO3i8p31XHcea2RbpHijjCY4CwxxplShlFGu5aYzwXGYQlDT5iOj9eir16Lx2B5wV8k8HP2wWzmo6gGziJDVa3bpeAnHxrWyfxUwVFXy/d5jbWmUkS7fUXqw9h0MdBmhERAylbQGLAEy4/kir74dlCxincIbVuRSpOWqGS4qDEvvBv7vYyWSjcOL/5df/azouMUwfXTb5JP5tZN2dx732SA4/e1nlGG5t+REM7XpXgUNjxoeh06eUN//3FhupEFbz9BnOeRdNNCPRhWZXSvTZjIXWT56bku+1+FJXojrH6y+yp253cVPN+pYKbSXWnqGUsPcr8d/nJgE71ICHNr+p3h2JpTYJM8vvOpdQI6fzz60dLbBcI13M3FC6ocsfPehXFo4n+HVzRNrMrzlrgJ44F2cQ1ZYr7OBKgBIf7i+3BnJ7UXEx9rSQ7RImyXN7i8ocD87vkUvr0og+7+IscYlaIsmhpr1cflI5oHkXsS3rVxBeiPH9P7TOc+5Pf1z5UCABW9Zg9yWBj+DGHS7/OnbrqkM+W8C7oojYDz+5AZSEiZMT0NCHbKp3UJzsn6jBLcdJWFk4t/Fh2+HBpj/5hlCPxh17dI2X6HBbSClPh3z6p37/WrMsnwg3Z5oJTK0wk+YEQv/NhD4VLGW3QvVOSKudQdI0nOcCMXxQ+2SG8pbQqPs86e7lXi8NI/uz6PQ+bT2gaQ==,iv:cdIQxTvksnA5ODSUcey/gWG/lluvFbzYLGkeBpW2vh0=,tag:A3ifsd2SsoS7tzjNsauczg==,type:str]",
"sops": {
"shamir_threshold": 1,
"kms": null,
@ -7,8 +7,8 @@
"azure_kv": null,
"hc_vault": null,
"age": null,
"lastmodified": "2024-01-23T17:02:46Z",
"mac": "ENC[AES256_GCM,data:+Oh6p7p325mSuHEWSC2aSD/WvSCelTpY7RV9fBli2TCxeFLdDGt6XTLZykFDjMq+5kHmJU2z3qTZm2bs0IbQ2h3pN5O5vL2+jJ17o3iKGwWvoim+2Nj3JChrzmUCLzqx04ZxojMrli2Af4eF/hDSHS4dcqTSxh/GiZeIPnoQwdA=,iv:veIp9YJ/CADUHpitvg3WMBiYzC1pEvevC14AhytfiE4=,tag:k6oYO2g1ng3gFLt6AbGSrg==,type:str]",
"lastmodified": "2024-01-23T19:16:29Z",
"mac": "ENC[AES256_GCM,data:EiUCkJ3G8I2KzTgiL64ijJf0Xwx5Q+Fau/UfaI/4D3LRRj5/vvl/Y5am80C44Yf19GqX7TxGdaK2vWItVaGzAOBIi7WRG4xjWGUEFUBZjtmL2hsN3fc76VMmaLb1OoSYvTf+CfgUcji8ddBhbj1olB490yROWxKQ5C1YFsr2Ksw=,iv:KR4joteYBKh22U5UkWKeVO8df6k3yCEP6/vcoZE2E0k=,tag:CsfBWCWUtUz+Dyk5pbp43A==,type:str]",
"pgp": [
{
"created_at": "2024-01-14T19:49:29Z",