Add initial Portal version

This commit is contained in:
2026-04-09 22:16:22 +02:00
commit d526c1ff4a
51 changed files with 10752 additions and 0 deletions

92
src/lib/openbao.ts Normal file
View File

@@ -0,0 +1,92 @@
import { readFileSync } from "fs";
const OPENBAO_ADDR =
process.env.OPENBAO_ADDR || "http://openbao.openbao.svc:8200";
const SA_TOKEN_PATH =
"/var/run/secrets/kubernetes.io/serviceaccount/token";
const K8S_AUTH_ROLE = process.env.OPENBAO_K8S_ROLE || "pieced-portal";
const K8S_AUTH_MOUNT = process.env.OPENBAO_K8S_MOUNT || "kubernetes";
let cachedToken: { token: string; expiresAt: number } | null = null;
async function authenticate(): Promise<string> {
if (cachedToken && Date.now() < cachedToken.expiresAt - 30_000) {
return cachedToken.token;
}
const jwt = readFileSync(SA_TOKEN_PATH, "utf-8").trim();
const res = await fetch(
`${OPENBAO_ADDR}/v1/auth/${K8S_AUTH_MOUNT}/login`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ role: K8S_AUTH_ROLE, jwt }),
}
);
if (!res.ok) {
const body = await res.text();
throw new Error(`OpenBao K8s auth failed: ${res.status} ${body}`);
}
const data = await res.json();
const token = data.auth.client_token as string;
const leaseDuration = (data.auth.lease_duration as number) || 3600;
cachedToken = {
token,
expiresAt: Date.now() + leaseDuration * 1000,
};
return token;
}
/**
* Write secrets for a tenant package to OpenBao KV v2.
* Path: secret/data/tenants/{tenantId}/{packageId}
*/
export async function writePackageSecrets(
tenantId: string,
packageId: string,
secrets: Record<string, string>
): Promise<void> {
const token = await authenticate();
const path = `secret/data/tenants/${tenantId}/${packageId}`;
const res = await fetch(`${OPENBAO_ADDR}/v1/${path}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Vault-Token": token,
},
body: JSON.stringify({ data: secrets }),
});
if (!res.ok) {
const body = await res.text();
throw new Error(`OpenBao write failed: ${res.status} ${body}`);
}
}
/**
* Delete secrets for a tenant package from OpenBao KV v2.
* Uses metadata delete to remove all versions.
*/
export async function deletePackageSecrets(
tenantId: string,
packageId: string
): Promise<void> {
const token = await authenticate();
const path = `secret/metadata/tenants/${tenantId}/${packageId}`;
const res = await fetch(`${OPENBAO_ADDR}/v1/${path}`, {
method: "DELETE",
headers: { "X-Vault-Token": token },
});
if (!res.ok && res.status !== 404) {
const body = await res.text();
throw new Error(`OpenBao delete failed: ${res.status} ${body}`);
}
}