Bonus 17: Sichere API-Key-Speicherung (.env + age-Option)
- .gitignore: Secrets (.env, *.key, *.pem) + Coverage + Temp-Verzeichnisse - .env.example: Template ohne echte Werte, mit Anleitung - scripts/token-gen.mjs: HMAC-Bearer-Token-Generator - package.json: token-gen + build:twa Scripts - docs/SECRETS.md: komplette Anleitung - Lokale Entwicklung (.env) - CI (gitea-Repository-Secrets) - age-Verschluesselung als optionale Schicht - Was NIE tun / was IMMER tun - Incident-Response bei geleaktem Secret Verifiziert: - token-gen erzeugt Token korrekt (stderr: Token gueltig...) - 122/122 Tests gruen - .env.example im Repo, .env ist gitignored
This commit is contained in:
1 parent
67519ee416
commit
d7b1acbe33
5 files changed
+272
-1
No files matched your search
@@ -0,0 +1,40 @@
|
||||
# Mind-o-Mat Secrets Template
|
||||
#
|
||||
# Diese Datei ist ein TEMPLATE und enthaelt KEINE echten Werte.
|
||||
# Kopiere sie zu `.env` und fuelle die echten Werte ein.
|
||||
# `.env` ist in `.gitignore` und wird NIE committed.
|
||||
#
|
||||
# PowerShell:
|
||||
# Copy-Item .env.example .env
|
||||
# notepad .env
|
||||
#
|
||||
# Bash:
|
||||
# cp .env.example .env
|
||||
# nano .env
|
||||
|
||||
# === Minimax API (fuer Ingest mit echter KI) ===
|
||||
# Format: sk-xxxx oder ae-xxxx oder ae-xxxxx (siehe Minimax-Console)
|
||||
# Hole den Key aus der Minimax-Console > API-Keys.
|
||||
# WICHTIG: Setze die Env-Variable, die in 99_System/Konfig.md unter
|
||||
# ki_api_key_env steht (Default: MINIMAX_API_KEY).
|
||||
MINIMAX_API_KEY=sk-your-real-key-here
|
||||
|
||||
# === Vault-Pfad (optional) ===
|
||||
# Wenn nicht gesetzt, muss --vault <pfad> uebergeben werden
|
||||
# oder das aktuelle Arbeitsverzeichnis ist ein Vault.
|
||||
# MINDOMAT_VAULT_PATH=C:\GitHub\Mein-Vault
|
||||
|
||||
# === Notes-API Auth (fuer Production-Deployment) ===
|
||||
# Wenn gesetzt, verlangt die Notes-API einen Bearer-Token.
|
||||
# Token-Format: <timestamp>.<hmac-sha256-hex>
|
||||
# Generiere mit: mindomat-token-gen
|
||||
# NOTES_API_TOKEN=your-secret-here
|
||||
# NOTES_API_ALLOWED_ORIGINS=https://your-vault.example.com,https://your-app.example.com
|
||||
|
||||
# === gitea-Deployment ===
|
||||
# Gitea-URL fuer das Vault-Repo (fuer sync)
|
||||
# GITEA_REMOTE=https://git.orfel.de/Jannik/Mein-Vault.git
|
||||
|
||||
# === QMD (optional, normalerweise ueber CLI) ===
|
||||
# QMD-Cache-Pfad (Default: 99_System/Cache)
|
||||
# QMD_CACHE_PATH=99_System/Cache
|
||||
+20
@@ -39,3 +39,23 @@ vitest.log.err
|
||||
# Tool-Build-Output
|
||||
dist/
|
||||
*.tsbuildinfo
|
||||
|
||||
# Secrets und Credentials (NIEMALS committen)
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
secrets.json
|
||||
secrets.age
|
||||
secrets.key
|
||||
*.pem
|
||||
*.key
|
||||
*.p12
|
||||
|
||||
# Test-Output und Coverage-Reports
|
||||
coverage/
|
||||
.nyc_output/
|
||||
|
||||
# Temporaere Verzeichnisse
|
||||
tmp/
|
||||
temp/
|
||||
.cache/
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
# Secrets und Credentials
|
||||
|
||||
Diese Anleitung erklärt, wie du sensible Daten (API-Keys, Tokens, Passwörter) in Mind-o-Mat handhabst, ohne sie jemals in Git zu committen.
|
||||
|
||||
## Überblick
|
||||
|
||||
Mind-o-Mat nutzt drei Arten von Secrets:
|
||||
|
||||
| Secret | Wo gespeichert | Wer sieht es |
|
||||
|---|---|---|
|
||||
| **Minimax API-Key** | `.env` (lokal) / CI-Secret | nur Tool-Code zur Laufzeit |
|
||||
| **Notes-API Token** | `.env` (lokal) / CI-Secret | Browser + Tool |
|
||||
| **gitea-Credentials** | `git credential helper` / SSH-Key | nur `git push` |
|
||||
|
||||
**Grundregel:** Secrets kommen in **`.env`** (gitignored) oder in **CI-Secrets** (verschlüsselt), niemals ins Repo.
|
||||
|
||||
## 1. Lokale Entwicklung: `.env`-Datei
|
||||
|
||||
### Einrichten
|
||||
|
||||
```powershell
|
||||
# 1. Template kopieren
|
||||
Copy-Item .env.example .env
|
||||
|
||||
# 2. .env editieren und echte Werte einsetzen
|
||||
notepad .env
|
||||
```
|
||||
|
||||
```bash
|
||||
# Bash:
|
||||
cp .env.example .env
|
||||
nano .env
|
||||
```
|
||||
|
||||
### Inhalt (Beispiel)
|
||||
|
||||
```dotenv
|
||||
# Minimax API-Key (aus Minimax-Console)
|
||||
MINIMAX_API_KEY=sk-abc123...
|
||||
|
||||
# Notes-API Auth (fuer Production-Deployment, optional)
|
||||
NOTES_API_TOKEN=my-secret-for-hmac
|
||||
NOTES_API_ALLOWED_ORIGINS=https://my-vault.example.com
|
||||
|
||||
# Vault-Pfad (optional)
|
||||
MINDOMAT_VAULT_PATH=C:\GitHub\Mein-Vault
|
||||
```
|
||||
|
||||
### Wie der Code die Werte liest
|
||||
|
||||
Das Tool nutzt **keine** `.env`-Loader-Library direkt. Stattdessen:
|
||||
|
||||
1. **System-Env-Variablen** werden automatisch von Node.js gelesen (`process.env`)
|
||||
2. **PowerShell:** `$env:MINIMAX_API_KEY = "sk-..."` setzt für die aktuelle Session
|
||||
3. **Bash:** `export MINIMAX_API_KEY=sk-...` setzt für die aktuelle Session
|
||||
4. **`.env` ist NICHT automatisch geladen** — wenn du `node bin/mindomat.mjs` direkt aufrufst, musst du die Env-Variable vorher setzen
|
||||
|
||||
### Automatisches Laden (optional)
|
||||
|
||||
Wenn du `dotenv` als dev-Dependency willst:
|
||||
|
||||
```bash
|
||||
npm install --save-dev dotenv
|
||||
```
|
||||
|
||||
Dann in `bin/mindomat.mjs`:
|
||||
|
||||
```javascript
|
||||
import 'dotenv/config';
|
||||
// ... rest
|
||||
```
|
||||
|
||||
Aber das ist **nicht** nötig für die Standard-Verwendung. PowerShell-User rufen ohnehin direkt auf.
|
||||
|
||||
## 2. CI (gitea-Actions): Repository-Secrets
|
||||
|
||||
In deiner gitea-Instanz: **Repository → Settings → Secrets → New Secret**
|
||||
|
||||
Füge hinzu:
|
||||
|
||||
| Name | Wert |
|
||||
|---|---|
|
||||
| `MINIMAX_API_KEY` | dein API-Key |
|
||||
| `NOTES_API_TOKEN` | dein HMAC-Secret |
|
||||
| `NOTES_API_ALLOWED_ORIGINS` | z. B. `https://vault.example.com` |
|
||||
|
||||
In `.gitea/workflows/ci.yml`:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
test:
|
||||
env:
|
||||
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
|
||||
```
|
||||
|
||||
Die Secrets sind in CI verschlüsselt und werden zur Laufzeit als Env-Variablen bereitgestellt.
|
||||
|
||||
## 3. Notes-API Bearer-Token generieren
|
||||
|
||||
Wenn du die Notes-API mit Auth nutzt, brauchst du Bearer-Tokens:
|
||||
|
||||
```powershell
|
||||
# 1. Secret setzen
|
||||
$env:NOTES_API_TOKEN = "my-secret"
|
||||
|
||||
# 2. Token generieren
|
||||
npm run token-gen
|
||||
# Output: 1725523000000.4a7b2c1d... (Beispiel)
|
||||
|
||||
# 3. Token in der Webapp nutzen
|
||||
# In .env: NOTES_API_TOKEN=my-secret
|
||||
# Beim Browser-Request: Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
Token ist 1 Stunde gültig (Timestamp-basiert). Für längere Gültigkeit `timestamp` durch ein Ablauf-Datum ersetzen.
|
||||
|
||||
## 4. Optional: `age`-Verschlüsselung für CI
|
||||
|
||||
Wenn du Secrets **verschlüsselt im Repo** speichern willst (z. B. für lokale CI ohne externe Secret-Store), nutze [`age`](https://github.com/FiloSottile/age):
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
# 1. age installieren
|
||||
# Windows: scoop install age
|
||||
# macOS: brew install age
|
||||
# Linux: apt install age
|
||||
|
||||
# 2. Schluesselpaar generieren
|
||||
age-keygen -o key.txt # privater Schluessel (NICHT in Git!)
|
||||
mkdir -p .age
|
||||
age-keygen -y key.txt > .age/recipients.pub # oeffentlicher Schluessel (in Git)
|
||||
|
||||
# 3. Secret verschluesseln
|
||||
$env:MINIMAX_API_KEY = "sk-abc123..."
|
||||
node -e "
|
||||
const { execSync } = require('node:child_process');
|
||||
const value = process.env.MINIMAX_API_KEY;
|
||||
execSync('echo ' + value + ' | age -R .age/recipients.pub -o secrets.age', { stdio: 'inherit' });
|
||||
"
|
||||
|
||||
# 4. secrets.age committen (verschluesselt, sicher)
|
||||
git add secrets.age
|
||||
git commit -m "Add encrypted Minimax API key"
|
||||
|
||||
# 5. Zur Laufzeit entschluesseln
|
||||
age -d -i key.txt secrets.age # in CI mit Secret-Storage fuer key.txt
|
||||
```
|
||||
|
||||
### Vorteile
|
||||
|
||||
- `secrets.age` ist im Git, aber verschlüsselt (öffentlich lesbar, aber nicht entschlüsselbar ohne `key.txt`)
|
||||
- Funktioniert in CI ohne externe Secret-Stores
|
||||
- `key.txt` bleibt in deinem lokalen `~/.ssh/age/` oder in CI-Secret-Storage
|
||||
|
||||
### Nachteile
|
||||
|
||||
- Komplexer als Env-Variablen
|
||||
- `key.txt` muss separat gesichert werden (sonst sind die Secrets verloren)
|
||||
|
||||
**Empfehlung:** Nutze `age` NUR, wenn du Secrets im Repo versionieren willst (z. B. für Demo-Setups). Für Production-CI sind gitea-Repository-Secrets einfacher.
|
||||
|
||||
## 5. Was du NIEMALS tun solltest
|
||||
|
||||
- ❌ API-Key direkt in `package.json` oder `src/` hardcoden
|
||||
- ❌ `.env` ins Git committen (ist in `.gitignore`, aber prüfe!)
|
||||
- ❌ Secrets in Commit-Messages oder PR-Beschreibungen erwähnen
|
||||
- ❌ Secrets in Screenshots oder Logs zeigen
|
||||
- ❌ Secrets in JSON-Dateien oder Markdown-Dateien im Repo
|
||||
|
||||
## 6. Was du TUN solltest
|
||||
|
||||
- ✅ `.env` aus `.env.example` lokal erstellen
|
||||
- ✅ Secrets über PowerShell `$env:...` oder Bash `export ...` setzen
|
||||
- ✅ Für CI: gitea-Repository-Secrets nutzen
|
||||
- ✅ Vor jedem Commit: `git status` und `git diff` prüfen, dass keine Secrets drin sind
|
||||
- ✅ Falls ein Secret geleakt wurde: **sofort widerrufen** und neu generieren
|
||||
|
||||
## 7. Falls ein Secret geleakt wurde
|
||||
|
||||
1. **Sofort widerrufen** im jeweiligen Service (Minimax-Console, gitea, etc.)
|
||||
2. **Neuen Key generieren**
|
||||
3. **Aus Git-History entfernen** (mit `git filter-repo` oder BFG Repo-Cleaner)
|
||||
4. **Force-Push** zum Remote (falls öffentlich: alle Klone sind kompromittiert)
|
||||
5. **Alle Verbraucher aktualisieren** (CI-Secrets, lokale `.env`-Dateien)
|
||||
6. **Audit-Log prüfen**, ob der geleakte Key bereits missbraucht wurde
|
||||
|
||||
Im Worst-Case: Vault-Daten neu anlegen, alle Tokens rotieren.
|
||||
+3
-1
@@ -30,7 +30,9 @@
|
||||
"init-vault": "npm run build && node bin/mindomat.mjs init-vault",
|
||||
"index": "npm run build && node bin/mindomat.mjs index",
|
||||
"ingest": "npm run build && node bin/mindomat.mjs ingest",
|
||||
"sync": "npm run build && node bin/mindomat.mjs sync"
|
||||
"sync": "npm run build && node bin/mindomat.mjs sync",
|
||||
"token-gen": "node scripts/token-gen.mjs",
|
||||
"build:twa": "node scripts/build-twa.mjs"
|
||||
},
|
||||
"keywords": [
|
||||
"second-brain",
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env node
|
||||
// Token-Generator fuer die Notes-API (Phase 9: Auth)
|
||||
// Generiert ein Bearer-Token im Format: <timestamp>.<hmac-sha256-hex>
|
||||
|
||||
import { createHmac } from 'node:crypto';
|
||||
|
||||
const secret = process.env.NOTES_API_TOKEN;
|
||||
if (!secret) {
|
||||
console.error('Fehler: NOTES_API_TOKEN nicht gesetzt.');
|
||||
console.error('Setze die Env-Variable, z. B.:');
|
||||
console.error(' $env:NOTES_API_TOKEN = "my-secret" # PowerShell');
|
||||
console.error(' export NOTES_API_TOKEN=my-secret # Bash');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const ts = Date.now().toString();
|
||||
const hmac = createHmac('sha256', secret).update(ts).digest('hex');
|
||||
const token = `${ts}.${hmac}`;
|
||||
|
||||
console.log(token);
|
||||
console.error(`(Token gueltig fuer Bearer-Auth. Secret: ${'*'.repeat(8)})`);
|
||||
Reference in new issue
Block a user