FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix: generate a random ADMINFORTH_SECRET in create-app and warn on sh… · devforth/adminforth@5b78c56 · GitHub

Repository navigation

Commit 5b78c56

Browse files
committed
fix: generate a random ADMINFORTH_SECRET in create-app and warn on short secrets
1 parent 6830edd commit 5b78c56

21 files changed

Lines changed: 318 additions & 17 deletions
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
node_modules
2+
.env
23
{{#if sqliteFile}}
34
{{ sqliteFile }}
45
{{/if}}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Secrets live in the gitignored .env; this committed file only lists which ones each developer must create.
2+
# create-app already wrote a random ADMINFORTH_SECRET into .env on the machine that scaffolded the project.
3+
# On a fresh clone, generate your own — see "First-time setup" in README.md (openssl rand -hex 32).
4+
ADMINFORTH_SECRET=
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
# Add only sensitive local environment variables here; non-sensitive local variables should go to .env.local
1+
# Add only sensitive local environment variables here; non-sensitive local variables should go to .env.local
2+
# This file is gitignored. Each developer and each deployment needs its own ADMINFORTH_SECRET.
3+
4+
ADMINFORTH_SECRET={{{adminforthSecret}}}

‎adminforth/commands/createApp/templates/.env.local.hbs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# Add only non-sensitive local environment variables here so all team members can use them with minimal setup
22
# For sensitive local environment variables, use .env and explain to team members how to set them, ideally with a .env.example
33

4-
ADMINFORTH_SECRET=123
54
NODE_ENV=development
65
DEBUG_LEVEL=info
76
AF_DEBUG_LEVEL=info

‎adminforth/commands/createApp/templates/readme.md.hbs‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ Create the initial migration and apply it to the database:
2323
```
2424
{{/if}}
2525

26+
First-time setup on a fresh clone: `create-app` wrote a random `ADMINFORTH_SECRET` into `.env`, but that file is gitignored, so every developer and every deployment creates their own (see `.env.example`). The command below only creates `.env` when it does not exist yet:
27+
28+
```bash
29+
# On Windows (PowerShell): node -e "require('fs').writeFileSync('.env', 'ADMINFORTH_SECRET=' + require('crypto').randomBytes(32).toString('hex') + '\n', { flag: 'wx' })"
30+
[ -f .env ] || (umask 077; echo "ADMINFORTH_SECRET=$(openssl rand -hex 32)" > .env)
31+
```
32+
2633
Start the server:
2734

2835
```bash
@@ -48,8 +55,12 @@ Your colleagues will need to pull the changes and run `{{packageManagerRun}} mig
4855
You have Dockerfile ready for production deployment. You can test the build with:
4956

5057
```bash
58+
# Generate a signing key once and keep it in your secret store (never commit it).
59+
# On Windows without openssl: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
60+
export ADMINFORTH_SECRET="$(openssl rand -hex 32)"
61+
5162
docker build -t {{appName}}-image .
52-
docker run -p 3500:3500 -e ADMINFORTH_SECRET=123 {{#if sqliteFile}}-v $(pwd)/db:/code/db {{/if}}{{appName}}-image
63+
docker run -p 3500:3500 -e ADMINFORTH_SECRET="$ADMINFORTH_SECRET" {{#if sqliteFile}}-v $(pwd)/db:/code/db {{/if}}{{appName}}-image
5364
```
5465

5566
To set non-sensitive environment variables in production, use `.env.prod` file.

‎adminforth/commands/createApp/utils.js‎

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Listr } from 'listr2'
99
import { fileURLToPath, pathToFileURL } from 'url';
1010
import {ConnectionString} from 'connection-string';
1111
import { exec } from 'child_process';
12+
import crypto from 'crypto';
1213

1314
import Handlebars from 'handlebars';
1415
import { promisify } from 'util';
@@ -454,7 +455,7 @@ function getPackageManagerTemplateData(useNpm, nodeMajor) {
454455
};
455456
}
456457

457-
async function writeTemplateFiles(dirname, cwd, useNpm, includePrismaMigrations, options) {
458+
export async function writeTemplateFiles(dirname, cwd, useNpm, includePrismaMigrations, options) {
458459
const {
459460
dbUrl, prismaDbUrl, appName, provider, existingDb, nodeMajor,
460461
dbUrlProd, prismaDbUrlProd, sqliteFile
@@ -540,10 +541,17 @@ async function writeTemplateFiles(dirname, cwd, useNpm, includePrismaMigrations,
540541
data: {},
541542
},
542543
{
543-
// We'll write .env using the same content as .env.sample
544+
// gitignored: holds the JWT signing key, unique per developer and per deployment
544545
src: '.env.hbs',
545546
dest: '.env',
546-
data: { dbUrl, prismaDbUrl: resolvedPrismaDbUrl },
547+
data: { adminforthSecret: crypto.randomBytes(32).toString('hex') },
548+
mode: 0o600,
549+
},
550+
{
551+
// committed: tells the next developer which secrets to create locally
552+
src: '.env.example.hbs',
553+
dest: '.env.example',
554+
data: {},
547555
},
548556
{
549557
src: 'adminuser.ts.hbs',
@@ -635,7 +643,11 @@ async function writeTemplateFiles(dirname, cwd, useNpm, includePrismaMigrations,
635643
...packageManagerTemplateData,
636644
...task.data,
637645
});
638-
await fs.promises.writeFile(destPath, compiled);
646+
await fs.promises.writeFile(destPath, compiled, task.mode ? { mode: task.mode } : undefined);
647+
if (task.mode) {
648+
// writeFile's mode is masked by the umask and ignored on overwrite; enforce it unconditionally
649+
await fs.promises.chmod(destPath, task.mode);
650+
}
639651
}
640652
}
641653
}

‎adminforth/documentation/docs/tutorial/001-gettingStarted.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ myadmin/
100100
├── package.json # Project dependencies
101101
├── pnpm-workspace.yaml
102102
├── tsconfig.json # TypeScript configuration
103-
├── .env # Env vars like tokens, secrets that should not be in version control
103+
├── .env # Env vars like tokens, secrets that should not be in version control (ADMINFORTH_SECRET is generated here for you)
104+
├── .env.example # Committed template listing the secrets each developer must create locally
104105
├── .env.local # General local environment variables
105106
└── .gitignore
106107

‎adminforth/documentation/docs/tutorial/01-helloWorld.md‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,24 @@ Create two files in your project's root directory:
4747
Put the following content to the `.env.local` file:
4848

4949
```bash title="./.env.local"
50-
ADMINFORTH_SECRET=123
5150
NODE_ENV=development
5251
DATABASE_URL=sqlite://.db.sqlite
5352
PRISMA_DATABASE_URL=file:.db.sqlite
5453
```
5554

55+
Generate a signing key into the gitignored `.env` file, readable by you only (AdminForth logs a warning at startup if the secret is shorter than 16 characters; on Windows without openssl use `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`):
56+
57+
```bash
58+
(umask 077; echo "ADMINFORTH_SECRET=$(openssl rand -hex 32)" > .env)
59+
```
60+
61+
Make sure `.env` never reaches the repository or a Docker image:
62+
63+
```bash
64+
echo ".env" >> .gitignore
65+
echo ".env" >> .dockerignore
66+
```
67+
5668
> ☝️ Production best practices:
5769
>
5870
> 1) Most likely you not need `.env` file at all, instead you should use environment variables (from Docker, Kubernetes, Operating System, etc.)

‎adminforth/documentation/docs/tutorial/05-deploy.md‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,12 @@ docker build -t myadminapp .
3333
And run container with:
3434

3535
```bash
36+
# Generate a signing key once and keep it in your secret store (never commit it).
37+
# On Windows without openssl: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
38+
export ADMINFORTH_SECRET="$(openssl rand -hex 32)"
39+
3640
docker run -p 3500:3500 \
37-
-e ADMINFORTH_SECRET=CHANGEME \
41+
-e ADMINFORTH_SECRET="$ADMINFORTH_SECRET" \
3842
-v $(pwd)/db:/code/db \
3943
myadminapp
4044
```
@@ -115,7 +119,7 @@ services:
115119
build: ./adminforth-app
116120
environment:
117121
- NODE_ENV=production
118-
- ADMINFORTH_SECRET=!CHANGEME! # ☝️ replace with your secret
122+
- ADMINFORTH_SECRET=${ADMINFORTH_SECRET:?generate one with openssl rand -hex 32} # ☝️ export it in the shell or put it in a gitignored .env next to this compose file; compose refuses to start when it is unset
119123
labels:
120124
- "traefik.enable=true"
121125
- "traefik.http.routers.adminforth.tls=true"

‎adminforth/index.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,11 @@ class AdminForth implements IAdminForth {
620620
ADMINFORTH_SECRET variable is used to sign JWT tokens
621621
`);
622622
}
623+
if (adminforthSecret.length < 16) {
624+
afLogger.warn(`ADMINFORTH_SECRET is too short (${adminforthSecret.length} characters). ` +
625+
'It is the key that signs every auth cookie: a guessable value lets anyone forge a session for any user. ' +
626+
'Generate one with: openssl rand -hex 32');
627+
}
623628
}
624629

625630
async getAllTables(): Promise<{ [dataSourceId: string]: string[] }> {

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL