Compare commits

...

7 Commits

6 changed files with 156 additions and 27 deletions
@@ -0,0 +1,63 @@
-- CreateTable
CREATE TABLE "UnifiSite" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"siteId" TEXT NOT NULL,
"companyId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "UnifiSite_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "CatalogItem" (
"id" TEXT NOT NULL,
"cwCatalogId" INTEGER NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"customerDescription" TEXT,
"internalNotes" TEXT,
"manufacturer" TEXT,
"manufactureCwId" INTEGER,
"partNumber" TEXT,
"vendorName" TEXT,
"vendorSku" TEXT,
"vendorCwId" INTEGER,
"price" DOUBLE PRECISION NOT NULL,
"cost" DOUBLE PRECISION NOT NULL,
"inactive" BOOLEAN NOT NULL DEFAULT false,
"salesTaxable" BOOLEAN NOT NULL DEFAULT true,
"onHand" INTEGER NOT NULL DEFAULT 0,
"cwLastUpdated" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "CatalogItem_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "_LinkedItems" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL,
CONSTRAINT "_LinkedItems_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateIndex
CREATE UNIQUE INDEX "UnifiSite_siteId_key" ON "UnifiSite"("siteId");
-- CreateIndex
CREATE UNIQUE INDEX "CatalogItem_cwCatalogId_key" ON "CatalogItem"("cwCatalogId");
-- CreateIndex
CREATE INDEX "_LinkedItems_B_index" ON "_LinkedItems"("B");
-- AddForeignKey
ALTER TABLE "UnifiSite" ADD CONSTRAINT "UnifiSite_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "Company"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_LinkedItems" ADD CONSTRAINT "_LinkedItems_A_fkey" FOREIGN KEY ("A") REFERENCES "CatalogItem"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_LinkedItems" ADD CONSTRAINT "_LinkedItems_B_fkey" FOREIGN KEY ("B") REFERENCES "CatalogItem"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -6,12 +6,8 @@ export const generateSecureValue = (content: string) => {
// Generate a hash of the content // Generate a hash of the content
const hash = Password.hash(content); const hash = Password.hash(content);
// Parse the PKCS#1 PEM key into a proper KeyObject // Parse the PEM key into a proper KeyObject
const publicKey = crypto.createPublicKey({ const publicKey = crypto.createPublicKey(secureValuesPublicKey);
key: secureValuesPublicKey,
format: "pem",
type: "pkcs1",
});
// Encrypt the content using the .secureValues.pub public key // Encrypt the content using the .secureValues.pub public key
const encrypted = crypto.publicEncrypt( const encrypted = crypto.publicEncrypt(
+1 -5
View File
@@ -3,11 +3,7 @@ import crypto from "crypto";
import { secureValuesPrivateKey } from "../../constants"; import { secureValuesPrivateKey } from "../../constants";
import GenericError from "../../Errors/GenericError"; import GenericError from "../../Errors/GenericError";
const privateKey = crypto.createPrivateKey({ const privateKey = crypto.createPrivateKey(secureValuesPrivateKey);
key: secureValuesPrivateKey,
format: "pem",
type: "pkcs1",
});
export const readSecureValue = ( export const readSecureValue = (
encryptedContent: string, encryptedContent: string,
+65
View File
@@ -0,0 +1,65 @@
import { execSync } from "child_process";
const kubeconfig = "/Users/jroberts/projects/K8S-QuickDeploy/k8s.yaml";
function getKey(name: string): string {
const b64 = execSync(
`KUBECONFIG=${kubeconfig} kubectl get secret optima-keys-secret -n optima -o jsonpath="{.data.${name}}"`,
)
.toString()
.trim();
return Buffer.from(b64, "base64").toString("utf-8");
}
const privKeys = [
"ACCESS_TOKEN_PRIVATE_KEY",
"REFRESH_TOKEN_PRIVATE_KEY",
"PERMISSIONS_PRIVATE_KEY",
"SECURE_VALUES_PRIVATE_KEY",
];
const converted: Record<string, string> = {};
// Use openssl CLI to convert PKCS#1 to PKCS#8 (Bun's crypto has issues with some keys)
for (const k of privKeys) {
const pem = getKey(k);
const pkcs8 = execSync("openssl pkey -in /dev/stdin", {
input: pem,
}).toString();
converted[k] = pkcs8;
console.log(`${k}: converted to PKCS#8 ✅`);
}
const pubPem = getKey("SECURE_VALUES_PUBLIC_KEY");
const spki = execSync("openssl rsa -RSAPublicKey_in -pubout -in /dev/stdin", {
input: pubPem,
}).toString();
converted["SECURE_VALUES_PUBLIC_KEY"] = spki;
console.log("SECURE_VALUES_PUBLIC_KEY: converted to SPKI ✅");
// Generate kubectl command to recreate the secret with PKCS#8 keys
const args = Object.entries(converted)
.map(([k, v]) => `--from-literal=${k}='${v}'`)
.join(" \\\n ");
console.log("\n--- Delete and recreate secret with PKCS#8 keys ---\n");
console.log(
`KUBECONFIG=${kubeconfig} kubectl delete secret optima-keys-secret -n optima`,
);
console.log(
`KUBECONFIG=${kubeconfig} kubectl create secret generic optima-keys-secret -n optima \\\n ${args}`,
);
// Actually do it
console.log("\nApplying...");
execSync(
`KUBECONFIG=${kubeconfig} kubectl delete secret optima-keys-secret -n optima`,
);
const literals = Object.entries(converted).map(
([k, v]) => `--from-literal=${k}=${v}`,
);
const cmd = `KUBECONFIG=${kubeconfig} kubectl create secret generic optima-keys-secret -n optima ${literals.join(" ")}`;
execSync(cmd);
console.log("Secret recreated with PKCS#8 keys ✅");
+9 -5
View File
@@ -1,9 +1,9 @@
import keypair from "keypair"; import crypto from "crypto";
console.log(` console.log(`
Generating Private Keys Generating Private Keys
----------------- -----------------
This script will go through and genrate all the keys necessary for running the Credential Manager API locally. This script will go through and generate all the keys necessary for running the Credential Manager API locally.
This process might take several minutes. This process might take several minutes.
-----------------`); -----------------`);
@@ -42,9 +42,13 @@ await Promise.all(
if (!privExists || !pubExists) { if (!privExists || !pubExists) {
// Always regenerate both files together to ensure the key pair matches // Always regenerate both files together to ensure the key pair matches
console.log(`Generating '${v}' and '${pubPath}'...`); console.log(`Generating '${v}' and '${pubPath}'...`);
const keys = keypair({ bits: 4096 }); const { privateKey, publicKey } = crypto.generateKeyPairSync("rsa", {
await Bun.write(v, keys.private); modulusLength: 4096,
await Bun.write(pubPath, keys.public); privateKeyEncoding: { type: "pkcs8", format: "pem" },
publicKeyEncoding: { type: "spki", format: "pem" },
});
await Bun.write(v, privateKey);
await Bun.write(pubPath, publicKey);
} }
return; return;
}), }),
+16 -11
View File
@@ -1,4 +1,4 @@
import keypair from "keypair"; import crypto from "crypto";
import { mkdirSync } from "fs"; import { mkdirSync } from "fs";
const outputDir = "production-keys"; const outputDir = "production-keys";
@@ -19,14 +19,18 @@ const generatedKeys: Record<string, { private: string; public: string }> = {};
for (const name of keyFiles) { for (const name of keyFiles) {
console.log(`Generating '${name}' key pair (4096-bit RSA)...`); console.log(`Generating '${name}' key pair (4096-bit RSA)...`);
const keys = keypair({ bits: 4096 }); const { privateKey, publicKey } = crypto.generateKeyPairSync("rsa", {
generatedKeys[name] = keys; modulusLength: 4096,
privateKeyEncoding: { type: "pkcs8", format: "pem" },
publicKeyEncoding: { type: "spki", format: "pem" },
});
generatedKeys[name] = { private: privateKey, public: publicKey };
const privPath = `${outputDir}/${name}.key`; const privPath = `${outputDir}/${name}.key`;
const pubPath = `${outputDir}/${name}.pub`; const pubPath = `${outputDir}/${name}.pub`;
await Bun.write(privPath, keys.private); await Bun.write(privPath, privateKey);
await Bun.write(pubPath, keys.public); await Bun.write(pubPath, publicKey);
console.log(`${privPath}`); console.log(`${privPath}`);
console.log(`${pubPath}`); console.log(`${pubPath}`);
@@ -38,14 +42,15 @@ const toBase64 = (str: string) => Buffer.from(str).toString("base64");
const secretYaml = `apiVersion: v1 const secretYaml = `apiVersion: v1
kind: Secret kind: Secret
metadata: metadata:
name: optima-keys name: optima-keys-secret
namespace: optima
type: Opaque type: Opaque
data: data:
accessToken.key: ${toBase64(generatedKeys["accessToken"].private)} ACCESS_TOKEN_PRIVATE_KEY: ${toBase64(generatedKeys["accessToken"].private)}
refreshToken.key: ${toBase64(generatedKeys["refreshToken"].private)} REFRESH_TOKEN_PRIVATE_KEY: ${toBase64(generatedKeys["refreshToken"].private)}
permissions.key: ${toBase64(generatedKeys["permissions"].private)} PERMISSIONS_PRIVATE_KEY: ${toBase64(generatedKeys["permissions"].private)}
secureValues.key: ${toBase64(generatedKeys["secureValues"].private)} SECURE_VALUES_PRIVATE_KEY: ${toBase64(generatedKeys["secureValues"].private)}
secureValues.pub: ${toBase64(generatedKeys["secureValues"].public)} SECURE_VALUES_PUBLIC_KEY: ${toBase64(generatedKeys["secureValues"].public)}
`; `;
const secretPath = `${outputDir}/optima-keys-secret.yaml`; const secretPath = `${outputDir}/optima-keys-secret.yaml`;