refactor(k8s): Reorganize into 5-layer structure with production kustomizations
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
use std::env;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::TcpStream;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
const SIGNING_CERT_NAME: &str = "homelab-oidc";
|
||||
const PROVIDERS: &[&str] = &["grafana", "minio"];
|
||||
|
||||
fn main() {
|
||||
let base_url = env::var("AUTHENTIK_BASE_URL")
|
||||
.unwrap_or_else(|_| "http://localhost:7000".into());
|
||||
let token = env::var("AUTHENTIK_BOOTSTRAP_TOKEN")
|
||||
.expect("AUTHENTIK_BOOTSTRAP_TOKEN must be set");
|
||||
|
||||
let host = parse_host(&base_url);
|
||||
|
||||
let ts = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
let new_name = format!("{}-{}", SIGNING_CERT_NAME, ts);
|
||||
|
||||
let body = format!(
|
||||
r#"{{"common_name":"{}","validity_days":365,"key_size":4096}}"#,
|
||||
new_name
|
||||
);
|
||||
let resp = http(&host, &token, "POST", "/api/v3/crypto/certificatekeypairs/generate/", Some(&body));
|
||||
let new_pk = extract_str(&resp, "pk").expect("no pk in generate response");
|
||||
println!("created '{}' pk={}", new_name, new_pk);
|
||||
|
||||
for provider in PROVIDERS {
|
||||
let list = http(
|
||||
&host, &token, "GET",
|
||||
&format!("/api/v3/providers/oauth2/?name={}", provider),
|
||||
None,
|
||||
);
|
||||
let provider_pk = match extract_str(&list, "pk") {
|
||||
Some(pk) => pk,
|
||||
None => {
|
||||
eprintln!("WARN: provider '{}' not found — skipping", provider);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let patch = format!(r#"{{"signing_key":"{}"}}"#, new_pk);
|
||||
http(&host, &token, "PATCH",
|
||||
&format!("/api/v3/providers/oauth2/{}/", provider_pk),
|
||||
Some(&patch),
|
||||
);
|
||||
println!("rotated '{}' → signing_key={}", provider, new_pk);
|
||||
}
|
||||
|
||||
println!("rotation complete");
|
||||
}
|
||||
|
||||
fn parse_host(base_url: &str) -> String {
|
||||
let stripped = base_url
|
||||
.trim_start_matches("http://")
|
||||
.trim_start_matches("https://");
|
||||
let host = stripped.split('/').next().unwrap_or(stripped);
|
||||
if host.contains(':') {
|
||||
host.to_string()
|
||||
} else {
|
||||
format!("{}:80", host)
|
||||
}
|
||||
}
|
||||
|
||||
fn http(host: &str, token: &str, method: &str, path: &str, body: Option<&str>) -> String {
|
||||
let mut stream = TcpStream::connect(host)
|
||||
.unwrap_or_else(|e| panic!("connect {}: {}", host, e));
|
||||
|
||||
let body_str = body.unwrap_or("");
|
||||
let hostname = host.split(':').next().unwrap_or(host);
|
||||
let req = format!(
|
||||
"{method} {path} HTTP/1.1\r\n\
|
||||
Host: {hostname}\r\n\
|
||||
Authorization: Bearer {token}\r\n\
|
||||
Content-Type: application/json\r\n\
|
||||
Content-Length: {len}\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
{body_str}",
|
||||
len = body_str.len(),
|
||||
);
|
||||
stream.write_all(req.as_bytes()).unwrap();
|
||||
|
||||
let mut raw = String::new();
|
||||
stream.read_to_string(&mut raw).unwrap();
|
||||
|
||||
let (head, resp_body) = raw.split_once("\r\n\r\n").unwrap_or((&raw, ""));
|
||||
|
||||
let status: u16 = head.lines().next()
|
||||
.and_then(|l| l.split_whitespace().nth(1))
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(0);
|
||||
if status >= 400 {
|
||||
panic!("{} {} → HTTP {} — {}", method, path, status, resp_body.trim());
|
||||
}
|
||||
|
||||
resp_body.to_string()
|
||||
}
|
||||
|
||||
// Extracts the value of the first `"key": <value>` match in raw JSON.
|
||||
// Handles both quoted strings ("pk": "uuid") and bare numbers ("pk": 5).
|
||||
fn extract_str(json: &str, key: &str) -> Option<String> {
|
||||
let needle = format!("\"{}\":", key);
|
||||
let after_colon = json.find(&needle)? + needle.len();
|
||||
let rest = json[after_colon..].trim_start();
|
||||
if let Some(inner) = rest.strip_prefix('"') {
|
||||
Some(inner[..inner.find('"')?].to_string())
|
||||
} else {
|
||||
let end = rest.find(|c: char| c == ',' || c == '}' || c.is_ascii_whitespace())?;
|
||||
Some(rest[..end].to_string())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user