feat!: setup pihole entry generation, add treeminer to minecraft-main
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
keys:
|
keys:
|
||||||
- &luca age1qu9y0dn5a704dggwmpaaurxqrhxm0qn8czgv5phka56y48sw7u8qkyn637
|
- &luca age13rqgrxh0fm23n3krf6v7yrrlnhhvs8256cusxqfs2l5xz8rgavssdhte4r
|
||||||
|
|
||||||
creation_rules:
|
creation_rules:
|
||||||
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
|
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
|
||||||
|
|||||||
1642
nix/homelab/Cargo.lock
generated
1642
nix/homelab/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,10 @@ edition = "2024"
|
|||||||
anyhow = "1.0.100"
|
anyhow = "1.0.100"
|
||||||
clap = { version = "4.5.53", features = ["derive"] }
|
clap = { version = "4.5.53", features = ["derive"] }
|
||||||
nom = "8.0.0"
|
nom = "8.0.0"
|
||||||
|
reqwest = { version = "0.13.1", features = ["json"] }
|
||||||
serde = { version = "1.0.228", features = ["serde_derive"] }
|
serde = { version = "1.0.228", features = ["serde_derive"] }
|
||||||
ssh2 = "0.9.5"
|
ssh2 = "0.9.5"
|
||||||
thiserror = "2.0.17"
|
thiserror = "2.0.17"
|
||||||
|
tokio = { version = "1.49.0", features = ["macros", "rt"] }
|
||||||
toml = "0.9.10"
|
toml = "0.9.10"
|
||||||
|
urlencoding = "2"
|
||||||
|
|||||||
@@ -94,3 +94,17 @@ routes = [
|
|||||||
private = true
|
private = true
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[pihole]
|
||||||
|
url = "https://pihole.lucalise.ca"
|
||||||
|
password_file = "/run/secrets/pihole_password"
|
||||||
|
extra_hosts = [
|
||||||
|
"192.168.27.12 mc-rocket.privatedns.org",
|
||||||
|
"192.168.27.12 mc-rocket-creative.privatedns.org",
|
||||||
|
"192.168.27.12 mc-rocket-creative.duckdns.org",
|
||||||
|
"192.168.27.12 git.lucalise.ca"
|
||||||
|
]
|
||||||
|
|
||||||
|
[router]
|
||||||
|
host = "192.168.15.1:22"
|
||||||
|
lease_file = "/var/dhcpd/var/db/dhcpd.leases"
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ minecraftServer:
|
|||||||
port: 25575
|
port: 25575
|
||||||
existingSecret: rcon-credentials
|
existingSecret: rcon-credentials
|
||||||
secretKey: rcon-password
|
secretKey: rcon-password
|
||||||
|
modrinth:
|
||||||
|
projects:
|
||||||
|
- treeminer
|
||||||
|
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
kubernetes.io/hostname: kube
|
kubernetes.io/hostname: kube
|
||||||
|
|||||||
@@ -11,6 +11,6 @@ minecraftRouter:
|
|||||||
- externalHostname: "mc-rocket.privatedns.org"
|
- externalHostname: "mc-rocket.privatedns.org"
|
||||||
host: "minecraft-main"
|
host: "minecraft-main"
|
||||||
port: 25565
|
port: 25565
|
||||||
- externalHostname: "mc-rocket-creative.privatedns.org"
|
- externalHostname: "mc-rocket-creative.duckdns.org"
|
||||||
host: "minecraft-creative"
|
host: "minecraft-creative"
|
||||||
port: 25565
|
port: 25565
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ serviceDns:
|
|||||||
type: LoadBalancer
|
type: LoadBalancer
|
||||||
mixedService: true
|
mixedService: true
|
||||||
annotations:
|
annotations:
|
||||||
metallb.universe.tf/loadBalancerIPs: "192.168.18.32"
|
metallb.universe.tf/loadBalancerIPs: "192.168.27.13"
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ spec:
|
|||||||
name: http
|
name: http
|
||||||
env:
|
env:
|
||||||
- name: PUID
|
- name: PUID
|
||||||
value: "1000"
|
value: "0"
|
||||||
- name: PGID
|
- name: PGID
|
||||||
value: "1000"
|
value: "0"
|
||||||
- name: TZ
|
- name: TZ
|
||||||
value: "America/Vancouver"
|
value: "America/Vancouver"
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ pub enum Error {
|
|||||||
IO(#[from] std::io::Error),
|
IO(#[from] std::io::Error),
|
||||||
#[error("command return non 0 exit code: {0}")]
|
#[error("command return non 0 exit code: {0}")]
|
||||||
ExitCode(i32),
|
ExitCode(i32),
|
||||||
|
#[error("HTTP error: {0}")]
|
||||||
|
Http(#[from] reqwest::Error),
|
||||||
|
#[error("Pi-hole API error: {0}")]
|
||||||
|
PiHole(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ mod commands;
|
|||||||
mod dns;
|
mod dns;
|
||||||
mod error;
|
mod error;
|
||||||
mod lease_parser;
|
mod lease_parser;
|
||||||
|
mod pihole;
|
||||||
mod transport;
|
mod transport;
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
@@ -18,6 +20,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
dns::Router,
|
dns::Router,
|
||||||
lease_parser::{BindingState, Lease},
|
lease_parser::{BindingState, Lease},
|
||||||
|
pihole::PiHoleClient,
|
||||||
transport::SSHTransport,
|
transport::SSHTransport,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -41,6 +44,21 @@ pub enum HelperError {
|
|||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
routes: Vec<Route>,
|
routes: Vec<Route>,
|
||||||
|
pihole: Option<PiHoleConfig>,
|
||||||
|
router: Option<RouterConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct PiHoleConfig {
|
||||||
|
url: String,
|
||||||
|
password_file: String,
|
||||||
|
extra_hosts: Option<HashSet<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct RouterConfig {
|
||||||
|
host: String,
|
||||||
|
lease_file: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_config<T: AsRef<Path>>(path: T) -> anyhow::Result<Config> {
|
pub fn parse_config<T: AsRef<Path>>(path: T) -> anyhow::Result<Config> {
|
||||||
@@ -51,7 +69,8 @@ pub fn parse_config<T: AsRef<Path>>(path: T) -> anyhow::Result<Config> {
|
|||||||
Ok(toml::from_slice::<Config>(&bytes)?)
|
Ok(toml::from_slice::<Config>(&bytes)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
#[tokio::main(flavor = "current_thread")]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
match &cli.command {
|
match &cli.command {
|
||||||
@@ -60,18 +79,58 @@ fn main() -> anyhow::Result<()> {
|
|||||||
generate_routes(&config)?;
|
generate_routes(&config)?;
|
||||||
}
|
}
|
||||||
Some(Commands::SyncDNS {}) => {
|
Some(Commands::SyncDNS {}) => {
|
||||||
let r = Router::new(SSHTransport::new("192.168.15.1:22")?);
|
let config = parse_config("./config.toml")?;
|
||||||
let leases = r
|
let pihole_config = config
|
||||||
.dhcp_leases("/var/dhcpd/var/db/dhcpd.leases")?
|
.pihole
|
||||||
|
.context("pihole configuration is necessary for syncing dns")?;
|
||||||
|
let router_config = config
|
||||||
|
.router
|
||||||
|
.context("router configuration is necessary for syncing dns")?;
|
||||||
|
|
||||||
|
let password = std::fs::read_to_string(&pihole_config.password_file)
|
||||||
|
.context(format!(
|
||||||
|
"failed to read pihole password from {}",
|
||||||
|
pihole_config.password_file
|
||||||
|
))?
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let leases = tokio::task::spawn_blocking(move || -> anyhow::Result<Vec<Lease>> {
|
||||||
|
let r = Router::new(SSHTransport::new(&router_config.host)?);
|
||||||
|
let leases = r
|
||||||
|
.dhcp_leases(&router_config.lease_file)?
|
||||||
|
.into_iter()
|
||||||
|
.filter(|l| {
|
||||||
|
l.binding_state == BindingState::Active && l.client_hostname.is_some()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Ok(leases)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
let mut desired = leases
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|l| {
|
.map(|l| {
|
||||||
if !(l.binding_state == BindingState::Active && l.client_hostname.is_some()) {
|
format!(
|
||||||
return false;
|
"{} {}",
|
||||||
}
|
l.ip,
|
||||||
true
|
l.client_hostname.expect("filtered for Some above")
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.collect::<Vec<Lease>>();
|
.collect::<HashSet<String>>();
|
||||||
println!("{:#?}", leases);
|
if let Some(extra_hosts) = pihole_config.extra_hosts {
|
||||||
|
desired.extend(extra_hosts);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Found {} active leases with hostnames", desired.len());
|
||||||
|
|
||||||
|
let client = PiHoleClient::new(&pihole_config.url, &password).await?;
|
||||||
|
|
||||||
|
let stats = client.sync_hosts(desired).await?;
|
||||||
|
println!(
|
||||||
|
"Sync complete: added {}, removed {}",
|
||||||
|
stats.added, stats.removed
|
||||||
|
);
|
||||||
}
|
}
|
||||||
None => Cli::command().print_long_help()?,
|
None => Cli::command().print_long_help()?,
|
||||||
}
|
}
|
||||||
|
|||||||
171
nix/homelab/src/pihole.rs
Normal file
171
nix/homelab/src/pihole.rs
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use reqwest::Client;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::error::{Error, Result};
|
||||||
|
|
||||||
|
pub struct PiHoleClient {
|
||||||
|
client: Client,
|
||||||
|
base_url: String,
|
||||||
|
sid: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SyncStats {
|
||||||
|
pub added: usize,
|
||||||
|
pub removed: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct AuthResponse {
|
||||||
|
session: Session,
|
||||||
|
error: Option<AuthError>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Session {
|
||||||
|
valid: bool,
|
||||||
|
sid: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct AuthError {
|
||||||
|
message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct AuthRequest {
|
||||||
|
password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct ConfigResponse {
|
||||||
|
config: DnsConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct DnsConfig {
|
||||||
|
dns: DnsHosts,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct DnsHosts {
|
||||||
|
hosts: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PiHoleClient {
|
||||||
|
pub async fn new(base_url: &str, password: &str) -> Result<Self> {
|
||||||
|
let client = Client::new();
|
||||||
|
let url = format!("{}/api/auth", base_url.trim_end_matches('/'));
|
||||||
|
|
||||||
|
let response = client
|
||||||
|
.post(&url)
|
||||||
|
.json(&AuthRequest {
|
||||||
|
password: password.to_string(),
|
||||||
|
})
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let auth: AuthResponse = response.json().await?;
|
||||||
|
|
||||||
|
if !auth.session.valid {
|
||||||
|
return Err(Error::PiHole(format!(
|
||||||
|
"authentication failed: {}",
|
||||||
|
auth.error.unwrap().message
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let sid = auth.session.sid.ok_or_else(|| {
|
||||||
|
Error::PiHole("authentication succeeded but no session ID returned".to_string())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
client,
|
||||||
|
base_url: base_url.trim_end_matches('/').to_string(),
|
||||||
|
sid,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_hosts(&self) -> Result<HashSet<String>> {
|
||||||
|
let url = format!("{}/api/config/dns/hosts", self.base_url);
|
||||||
|
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.get(&url)
|
||||||
|
.header("X-FTL-SID", &self.sid)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if let Err(e) = response.error_for_status_ref() {
|
||||||
|
return Err(Error::PiHole(format!("failed to get hosts: {}", e)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let config: ConfigResponse = response.json().await?;
|
||||||
|
Ok(config.config.dns.hosts.into_iter().collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_host(&self, entry: &str) -> Result<()> {
|
||||||
|
let encoded = urlencoding::encode(entry);
|
||||||
|
let url = format!("{}/api/config/dns/hosts/{}", self.base_url, encoded);
|
||||||
|
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.put(&url)
|
||||||
|
.header("X-FTL-SID", &self.sid)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if let Err(e) = response.error_for_status_ref() {
|
||||||
|
let body = response.text().await.unwrap_or_default();
|
||||||
|
return Err(Error::PiHole(format!(
|
||||||
|
"failed to add host '{}': {} - {}",
|
||||||
|
entry, e, body
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_host(&self, entry: &str) -> Result<()> {
|
||||||
|
let encoded = urlencoding::encode(entry);
|
||||||
|
let url = format!("{}/api/config/dns/hosts/{}", self.base_url, encoded);
|
||||||
|
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.delete(&url)
|
||||||
|
.header("X-FTL-SID", &self.sid)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if let Err(e) = response.error_for_status_ref() {
|
||||||
|
let body = response.text().await.unwrap_or_default();
|
||||||
|
return Err(Error::PiHole(format!(
|
||||||
|
"failed to delete host '{}': {} - {}",
|
||||||
|
entry, e, body
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn sync_hosts(&self, desired: HashSet<String>) -> Result<SyncStats> {
|
||||||
|
let current = self.get_hosts().await?;
|
||||||
|
|
||||||
|
let to_delete = current.difference(&desired).cloned().collect::<Vec<_>>();
|
||||||
|
let to_add = desired.difference(¤t).cloned().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for entry in &to_delete {
|
||||||
|
self.delete_host(entry).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in &to_add {
|
||||||
|
self.add_host(entry).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(SyncStats {
|
||||||
|
added: to_add.len(),
|
||||||
|
removed: to_delete.len(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -78,13 +78,12 @@
|
|||||||
helmfile
|
helmfile
|
||||||
jless
|
jless
|
||||||
fd
|
fd
|
||||||
|
dig
|
||||||
];
|
];
|
||||||
programs.nix-ld.enable = lib.mkDefault true;
|
programs.nix-ld.enable = lib.mkDefault true;
|
||||||
programs.zsh.enable = lib.mkDefault true;
|
programs.zsh.enable = lib.mkDefault true;
|
||||||
services.openssh.enable = lib.mkDefault true;
|
services.openssh.enable = lib.mkDefault true;
|
||||||
hardware.enableAllFirmware = true;
|
hardware.enableAllFirmware = true;
|
||||||
sops.defaultSopsFile = ../../secrets/sops.yaml;
|
|
||||||
sops.age.sshKeyPaths = [ "/etc/ssh/id_ed25519" ];
|
|
||||||
programs.gnupg.agent = {
|
programs.gnupg.agent = {
|
||||||
enable = true;
|
enable = true;
|
||||||
enableSSHSupport = true;
|
enableSSHSupport = true;
|
||||||
@@ -98,5 +97,18 @@
|
|||||||
defaultEditor = true;
|
defaultEditor = true;
|
||||||
vimAlias = true;
|
vimAlias = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sops = {
|
||||||
|
defaultSopsFile = ../../secrets/secrets.yaml;
|
||||||
|
age.sshKeyPaths = [ "/home/luca/.ssh/id_ed25519" ];
|
||||||
|
secrets = {
|
||||||
|
"pihole_password" = {
|
||||||
|
owner = "luca";
|
||||||
|
};
|
||||||
|
"k3s_token" = {
|
||||||
|
owner = "luca";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,13 +17,6 @@
|
|||||||
enable = true;
|
enable = true;
|
||||||
dns = "systemd-resolved";
|
dns = "systemd-resolved";
|
||||||
};
|
};
|
||||||
# networking.extraHosts = ''
|
|
||||||
# 75.157.238.86 traefik.lucalise.ca
|
|
||||||
# 75.157.238.86 media.lucalise.ca
|
|
||||||
# 75.157.238.86 git.lucalise.ca
|
|
||||||
# 75.157.238.86 storage.lucalise.ca
|
|
||||||
# 75.157.238.86 home-assistant.lucalise.ca
|
|
||||||
# '';
|
|
||||||
|
|
||||||
services.resolved = {
|
services.resolved = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -32,13 +25,12 @@
|
|||||||
"1.0.0.1"
|
"1.0.0.1"
|
||||||
];
|
];
|
||||||
domains = [
|
domains = [
|
||||||
"consul"
|
"~."
|
||||||
"service.consul"
|
|
||||||
"node.consul"
|
|
||||||
];
|
];
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
[Resolve]
|
[Resolve]
|
||||||
DNS=192.168.20.5:8600
|
DNS=192.168.27.13:53
|
||||||
|
ResolveUnicastSingleLabel=yes
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
17
secrets/secrets.yaml
Normal file
17
secrets/secrets.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
pihole_password: ENC[AES256_GCM,data:G3CZJBY=,iv:hlSzQjx58VvPHyGg1ACsGaU+IIB9AIHRpg7d+iv/yfM=,tag:Xx8voi71SPkt49dg4zMXKw==,type:str]
|
||||||
|
k3s_token: ENC[AES256_GCM,data:5/HQiwLReJl17Ga2N+TDSQ==,iv:6OUU4eWC1vKVkmTQqFi+gsz9Hhu8zvkkAKsM6YEWXP8=,tag:+92zg7XN8+gxIb+gbmPy3Q==,type:str]
|
||||||
|
sops:
|
||||||
|
age:
|
||||||
|
- recipient: age13rqgrxh0fm23n3krf6v7yrrlnhhvs8256cusxqfs2l5xz8rgavssdhte4r
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmM1NaM1dxcVJ1allzWkNN
|
||||||
|
OGxiVU5TaUJLdXNRcGxvSWFLTGpnNFRNV2lVCnNpZ3JhdzRBQU95T25OaTEybXov
|
||||||
|
RENXSXRSbDYyN0JhYWE1eGpRRmlodm8KLS0tIFZYdFI4UG1kZ1dFaWdIMW05VFov
|
||||||
|
eUVXbXhnZnJuRlFRUUZDZTlYSTFPTGsKBLPIKq6inYtfvS0EsHIg6DAxflRI1fk4
|
||||||
|
cuuP0HbytzqbCON5pl2ArQtD53N7pn+meT3B26mCVoC+130NQMN8Xg==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
lastmodified: "2026-01-04T07:24:16Z"
|
||||||
|
mac: ENC[AES256_GCM,data:X/Giw8DMwwVOVUFkKCenzUdC3IPloJhSkZggPM+2HwVoVqadXNFCdFn+UOdPYJG8veSKVAP7QnerdIw/ZZ/3DKXZSi+pkM5g7Nfe9Dsw/ES398LC5oZjUyi9GRwpT9YzMHhQXNCH4fnt4wo+niSl0wGGjo3RYHL1iSaIaXoASqM=,iv:VtY53DWZb301pYspTGubxyaG69P2uhaGngnCcDQQvNw=,tag:Mk8MFrn3zAGNVlHTpQuxCA==,type:str]
|
||||||
|
unencrypted_suffix: _unencrypted
|
||||||
|
version: 3.11.0
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
win_pw: ENC[AES256_GCM,data:TGsPs+6wFQ==,iv:7KTF9YuPGDRJE3zcZgt6WJVIKEOp1DkOckP6QY4c9Rk=,tag:kVzkKEuSoP3vEakQT46/aQ==,type:str]
|
|
||||||
sops:
|
|
||||||
age:
|
|
||||||
- recipient: age1qu9y0dn5a704dggwmpaaurxqrhxm0qn8czgv5phka56y48sw7u8qkyn637
|
|
||||||
enc: |
|
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBReVNmSm82KzF1dHYrWlRj
|
|
||||||
ZnRLMU4yK2ZBQ0huRmNPL3NZV05lWXBaVG1vCm5vcFJOQ3hUeUMzUTI2TDd2TUJq
|
|
||||||
NGdVbEtncTdWczVjWWxWNFh2Z3g4Q28KLS0tIFBkUnlTdEZNZkszQi9yQTg5K0hl
|
|
||||||
RmQzN1Y2SUVlT1pGYXV0SW1vb0dHNDgKWchy7XFkxpGuhly4ZefRFZc6+oqcWJzI
|
|
||||||
HJqnVLiGI6jSKOXT7WV1d+g0Qt4zHGe9tquHxi4BNdxu81lNPVE3iQ==
|
|
||||||
-----END AGE ENCRYPTED FILE-----
|
|
||||||
lastmodified: "2025-08-26T05:15:00Z"
|
|
||||||
mac: ENC[AES256_GCM,data:670OWObSti3BN4AigbDcRvwud/JH/lUzQeWnUXgaST//FIfX6fY293AN0GJ2+3+C+K4Jd80QYh0DThaagMeTBw9/uJTQ10sER7MeknlWzOxmBEBl0fbvHn/t5v6H1yZ4XtcNV8p3RSiSC93k3z2tI4ERLsDzqBGyzkHXZGww4hc=,iv:oq4DxQWzPb80XiCD2WYaRDkqHNeBNUDiKWEMzQSDD/w=,tag:jT3JFUcC8DmHCh4Y6L++Vg==,type:str]
|
|
||||||
unencrypted_suffix: _unencrypted
|
|
||||||
version: 3.10.2
|
|
||||||
Reference in New Issue
Block a user