docker-mailserver LXC für Proxmox: Stack + Admin-UI + Webmail + Hardening
- dms-lxc.sh: Proxmox-Host-Installer (unprivilegierter LXC, Debian 13, Docker), curl-Self-Download, Multi-Domain-DKIM, SnappyMail-Provisionierung, PVE-Firewall - Stack: docker-mailserver, Node-Admin-API (Supabase-Auth), React-Admin-UI (OPENBUREAU-Look), SnappyMail (Shibui-Theme), Rspamd-Web-UI, docker-socket-proxy - Admin: Postfächer/Aliase/Catch-all/Quota, editierbare Domains+Settings, Server (Quota/Queue über abgesicherte Bridge), Status & DNS - Hardening: no-new-privileges, Whitelisted exec-Bridge, Rspamd-Passwort, .env chmod 600, PVE-CT-Firewall, generisch/teilbar (keine festen Domains) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
// ---------------------------------------------------------------------------
|
||||
// auth.js — Supabase-Token-Prüfung (wie in OPENBUREAU)
|
||||
//
|
||||
// Die React-Admin-UI loggt sich per Supabase ein und schickt das Access-Token
|
||||
// als Authorization: Bearer <token>. Hier validieren wir es gegen Supabase
|
||||
// und prüfen, ob die E-Mail in ADMIN_ALLOWED_EMAILS steht.
|
||||
// ---------------------------------------------------------------------------
|
||||
import { createClient } from '@supabase/supabase-js';
|
||||
|
||||
const SUPABASE_URL = process.env.SUPABASE_URL;
|
||||
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY;
|
||||
const ALLOWED = (process.env.ADMIN_ALLOWED_EMAILS || '')
|
||||
.split(',')
|
||||
.map((s) => s.trim().toLowerCase())
|
||||
.filter(Boolean);
|
||||
|
||||
// Nur für lokale Tests: hebt die Auth komplett auf. Standardmäßig AUS.
|
||||
// Wird ausschließlich im docker-compose.local.yml gesetzt, niemals im Deploy.
|
||||
const AUTH_DISABLED = process.env.AUTH_DISABLED === 'true';
|
||||
if (AUTH_DISABLED) {
|
||||
console.warn('[auth] ⚠ AUTH_DISABLED=true — KEINE Authentifizierung! Nur für lokale Tests verwenden.');
|
||||
}
|
||||
|
||||
// Supabase-Client nur erstellen, wenn Auth aktiv und konfiguriert ist
|
||||
// (createClient wirft bei leerer URL — würde sonst den Start verhindern).
|
||||
let supabase = null;
|
||||
if (!AUTH_DISABLED) {
|
||||
if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
|
||||
console.warn('[auth] SUPABASE_URL / SUPABASE_ANON_KEY nicht gesetzt — Auth wird fehlschlagen.');
|
||||
} else {
|
||||
supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
|
||||
auth: { persistSession: false, autoRefreshToken: false },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function requireAdmin(req, res, next) {
|
||||
try {
|
||||
if (AUTH_DISABLED) {
|
||||
req.user = { email: 'dev@local.test' };
|
||||
return next();
|
||||
}
|
||||
if (!supabase) return res.status(500).json({ error: 'Auth nicht konfiguriert (SUPABASE_URL/ANON_KEY fehlen).' });
|
||||
const header = req.headers.authorization || '';
|
||||
const token = header.startsWith('Bearer ') ? header.slice(7) : null;
|
||||
if (!token) return res.status(401).json({ error: 'Kein Token.' });
|
||||
|
||||
const { data, error } = await supabase.auth.getUser(token);
|
||||
if (error || !data?.user) return res.status(401).json({ error: 'Token ungültig.' });
|
||||
|
||||
const email = (data.user.email || '').toLowerCase();
|
||||
if (ALLOWED.length && !ALLOWED.includes(email)) {
|
||||
return res.status(403).json({ error: 'Kein Admin-Zugriff für diese E-Mail.' });
|
||||
}
|
||||
req.user = { email };
|
||||
next();
|
||||
} catch (e) {
|
||||
res.status(500).json({ error: 'Auth-Fehler: ' + e.message });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user