diff --git a/Dockerfile b/Dockerfile index 4257ae086..fda2e1dc7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,6 +46,7 @@ COPY --from=builder /app/cli-out/full/ . # Copy static data as it is not part of the build COPY static-data ./static-data ARG SKIP_ENV_VALIDATION='true' +ARG CI='true' ARG DISABLE_REDIS_LOGS='true' RUN corepack enable pnpm && pnpm build @@ -58,6 +59,8 @@ RUN mkdir /appdata RUN mkdir /appdata/db RUN mkdir /appdata/redis VOLUME /appdata +RUN mkdir /secrets +VOLUME /secrets @@ -71,6 +74,7 @@ RUN chmod +x /usr/bin/homarr # Don't run production as root RUN chown -R nextjs:nodejs /appdata +RUN chown -R nextjs:nodejs /secrets RUN mkdir -p /var/cache/nginx && chown -R nextjs:nodejs /var/cache/nginx && \ mkdir -p /var/log/nginx && chown -R nextjs:nodejs /var/log/nginx && \ mkdir -p /var/lib/nginx && chown -R nextjs:nodejs /var/lib/nginx && \ @@ -93,6 +97,7 @@ COPY --from=installer --chown=nextjs:nodejs /app/apps/nextjs/.next/standalone ./ COPY --from=installer --chown=nextjs:nodejs /app/apps/nextjs/.next/static ./apps/nextjs/.next/static COPY --from=installer --chown=nextjs:nodejs /app/apps/nextjs/public ./apps/nextjs/public COPY --chown=nextjs:nodejs scripts/run.sh ./run.sh +COPY --chown=nextjs:nodejs scripts/generateEncryptionKey.js ./generateEncryptionKey.js COPY --chown=nextjs:nodejs packages/redis/redis.conf /app/redis.conf COPY --chown=nextjs:nodejs nginx.conf /etc/nginx/templates/nginx.conf diff --git a/packages/common/package.json b/packages/common/package.json index 48c59cf1e..654d577ad 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -25,6 +25,7 @@ }, "prettier": "@homarr/prettier-config", "dependencies": { + "@homarr/log": "workspace:^0.1.0", "dayjs": "^1.11.13", "next": "^14.2.14", "react": "^18.3.1", diff --git a/packages/common/src/encryption.ts b/packages/common/src/encryption.ts index 70dee629a..cd253a89a 100644 --- a/packages/common/src/encryption.ts +++ b/packages/common/src/encryption.ts @@ -1,7 +1,20 @@ import crypto from "crypto"; +import { logger } from "@homarr/log"; + const algorithm = "aes-256-cbc"; //Using AES encryption -const key = Buffer.from("1d71cceced68159ba59a277d056a66173613052cbeeccbfbd15ab1c909455a4d", "hex"); // TODO: generate with const data = crypto.randomBytes(32).toString('hex') +const fallbackKey = "0000000000000000000000000000000000000000000000000000000000000000"; +const encryptionKey = process.env.ENCRYPTION_KEY ?? fallbackKey; // Fallback to a default key for local development +if (encryptionKey === fallbackKey) { + logger.warn("Using a fallback encryption key, stored secrets are not secure"); + + // We never want to use the fallback key in production + if (process.env.NODE_ENV === "production" && process.env.CI !== "true") { + throw new Error("Encryption key is not set"); + } +} + +const key = Buffer.from(encryptionKey, "hex"); export function encryptSecret(text: string): `${string}.${string}` { const initializationVector = crypto.randomBytes(16); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1af97ba93..80b57623c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -659,6 +659,9 @@ importers: packages/common: dependencies: + '@homarr/log': + specifier: workspace:^0.1.0 + version: link:../log dayjs: specifier: ^1.11.13 version: 1.11.13 @@ -11294,7 +11297,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.5 esutils@2.0.3: {} diff --git a/scripts/generateEncryptionKey.js b/scripts/generateEncryptionKey.js new file mode 100644 index 000000000..1fc7fdbfe --- /dev/null +++ b/scripts/generateEncryptionKey.js @@ -0,0 +1,7 @@ +// This script generates a random encryption key +// This key is used to encrypt and decrypt the integration secrets +// In production it is generated in run.sh and stored in the environment variable ENCRYPTION_KEY +// during runtime, it's also stored in a file. + +const crypto = require("crypto"); +console.log(crypto.randomBytes(32).toString("hex")); diff --git a/scripts/run.sh b/scripts/run.sh index 386a48cee..f53752502 100644 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -6,6 +6,19 @@ else node ./db/migrations/$DB_DIALECT/migrate.cjs ./db/migrations/$DB_DIALECT fi +# Generates an encryption key if it doesn't exist and saves it to /secrets/encryptionKey +# Also sets the ENCRYPTION_KEY environment variable +encryptionKey="" +if [ -r /secrets/encryptionKey ]; then + echo "Encryption key already exists" + encryptionKey=$(cat /secrets/encryptionKey) +else + echo "Generating encryption key" + encryptionKey=$(node ./generateEncryptionKey.js) + echo $encryptionKey > /secrets/encryptionKey +fi +export ENCRYPTION_KEY=$encryptionKey + # Start nginx proxy # 1. Replace the HOSTNAME in the nginx template file # 2. Create the nginx configuration file from the template