From 4444daaca9f47fc363ff50ec0ac3f606495db9af Mon Sep 17 00:00:00 2001 From: Hornwitser Date: Tue, 20 May 2025 00:43:29 +0200 Subject: [PATCH] Load secrets from files Putting secrets into environment variables is problematic due to them being inherited by sub-processes, the ease as which these can be leaked in logs, and the lack of support for loading secrets into environment variables by systems such as systemd and docker. Change the loading of secrets to be done by loading the content of a file specified by an environment variable. --- nuxt.config.ts | 4 ++-- server/utils/signed-cookie.ts | 4 +++- server/web-push.ts | 9 +++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/nuxt.config.ts b/nuxt.config.ts index 0b9f795..e8217af 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -3,9 +3,9 @@ export default defineNuxtConfig({ compatibilityDate: '2024-11-01', devtools: { enabled: true }, runtimeConfig: { - cookieSecretKey: "", + cookieSecretKeyFile: "", vapidSubject: "", - vapidPrivateKey: "", + vapidPrivateKeyFile: "", public: { defaultTimezone: "Europe/Oslo", vapidPublicKey: "", diff --git a/server/utils/signed-cookie.ts b/server/utils/signed-cookie.ts index 345befb..33aac2b 100644 --- a/server/utils/signed-cookie.ts +++ b/server/utils/signed-cookie.ts @@ -1,4 +1,5 @@ import type { H3Event } from "h3"; +import * as fs from "node:fs/promises"; let cachedCookieSecret: CryptoKey; export async function useCookieSecret(event: H3Event) { @@ -6,9 +7,10 @@ export async function useCookieSecret(event: H3Event) { return cachedCookieSecret; const runtimeConfig = useRuntimeConfig(event); + if (!runtimeConfig.cookieSecretKeyFile) throw new Error("NUXT_COOKIE_SECRET_KEY_FILE not set."); return cachedCookieSecret = await crypto.subtle.importKey( "raw", - Buffer.from(runtimeConfig.cookieSecretKey, "base64url"), + Buffer.from(await fs.readFile(runtimeConfig.cookieSecretKeyFile, "utf-8"), "base64url"), { name: "HMAC", hash: "SHA-256" }, false, ["sign", "verify"], diff --git a/server/web-push.ts b/server/web-push.ts index c4c4f07..bf3a306 100644 --- a/server/web-push.ts +++ b/server/web-push.ts @@ -1,3 +1,4 @@ +import * as fs from "node:fs/promises"; import type { H3Event } from "h3"; import webPush from "web-push"; import { readSubscriptions, writeSubscriptions } from "~/server/database"; @@ -8,7 +9,7 @@ let cachedVapidDetails: { privateKey: string, } | undefined; -function useVapidDetails(event: H3Event) { +async function useVapidDetails(event: H3Event) { if (cachedVapidDetails) { return cachedVapidDetails; } @@ -16,17 +17,17 @@ function useVapidDetails(event: H3Event) { const runtimeConfig = useRuntimeConfig(event); if (!runtimeConfig.vapidSubject) throw new Error("NUXT_VAPID_SUBJECT not set.") if (!runtimeConfig.public.vapidPublicKey) throw new Error("NUXT_PUBLIC_VAPID_PUBLIC_KEY not set.") - if (!runtimeConfig.vapidPrivateKey) throw new Error("NUXT_VAPID_PRIVATE_KEY not set.") + if (!runtimeConfig.vapidPrivateKeyFile) throw new Error("NUXT_VAPID_PRIVATE_KEY_FILE not set.") return cachedVapidDetails = { subject: runtimeConfig.vapidSubject, publicKey: runtimeConfig.public.vapidPublicKey, - privateKey: runtimeConfig.vapidPrivateKey, + privateKey: await fs.readFile(runtimeConfig.vapidPrivateKeyFile, "utf-8"), } } export async function sendPush(event: H3Event, title: string, body: string) { - const vapidDetails = useVapidDetails(event); + const vapidDetails = await useVapidDetails(event); const payload = JSON.stringify({ title, body }); const subscriptions = await readSubscriptions(); console.log(`Sending "${payload}" to ${subscriptions.length} subscribers`);