- Passwort-Reset

cd management
    npm run reset-password -- admin "neues-passwort"

  - Agent-Hardening
    backend/.env unterstützt jetzt:

    ALLOWED_MANAGEMENT_IPS="127.0.0.1,DEINE-MANAGEMENT-IP"

    Wenn gesetzt, akzeptiert der Agent nur Requests von diesen IPs.

  - Audit-Log
    Management speichert Aktionen wie Login, Logout, Node-Änderungen, Schedule-Updates, Backup/Restore, Settings-Änderungen.
  - Job-History
    Management speichert gestartete Backup/Restore/Scheduler-Jobs mit Node, VM, Typ, Agent-Job-ID und Status.
  - Operations-Seite
    Neue UI-Seite Operations mit:
      - Job History
      - Audit Log
  - systemd Templates

    deploy/systemd/incus-backup-agent.service
    deploy/systemd/incus-backup-management.service

  - Deployment-Doku

    docs/deployment.md
This commit is contained in:
Philipp
2026-05-21 10:14:33 +02:00
parent aae54167a1
commit e32ebbc734
21 changed files with 417 additions and 13 deletions
+1
View File
@@ -9,3 +9,4 @@ RESTIC_KEEP_WEEKLY=0
RESTIC_KEEP_MONTHLY=0
PORT=3000
API_TOKEN=""
ALLOWED_MANAGEMENT_IPS=""
+9
View File
@@ -17,6 +17,10 @@ export const requiredEnv = [
export const config = {
port: Number(process.env.PORT || 3000),
apiToken: process.env.API_TOKEN || '',
allowedManagementIps: (process.env.ALLOWED_MANAGEMENT_IPS || '')
.split(',')
.map((value) => value.trim())
.filter(Boolean),
zfsPoolName: process.env.ZFS_POOL_NAME || '',
resticEnv: {
AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID || '',
@@ -44,6 +48,7 @@ export const editableEnv = [
{ key: 'RESTIC_KEEP_MONTHLY', label: 'Keep monthly snapshots', required: false, secret: false },
{ key: 'PORT', label: 'API port', required: false, secret: false },
{ key: 'API_TOKEN', label: 'API token', required: false, secret: true },
{ key: 'ALLOWED_MANAGEMENT_IPS', label: 'Allowed management IPs', required: false, secret: false },
];
export function missingEnvVars() {
@@ -108,6 +113,10 @@ function applyRuntimeEnv(values) {
}
config.port = Number(process.env.PORT || 3000);
config.apiToken = process.env.API_TOKEN || '';
config.allowedManagementIps = (process.env.ALLOWED_MANAGEMENT_IPS || '')
.split(',')
.map((value) => value.trim())
.filter(Boolean);
config.zfsPoolName = process.env.ZFS_POOL_NAME || '';
config.resticEnv.AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID || '';
config.resticEnv.AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY || '';
+8
View File
@@ -17,6 +17,10 @@ app.use(cors());
app.use(express.json());
app.use((req, res, next) => {
if (config.allowedManagementIps.length && !config.allowedManagementIps.includes(normalizeIp(req.ip))) {
res.status(403).json({ error: 'Forbidden management source.' });
return;
}
if (!config.apiToken) {
next();
return;
@@ -29,6 +33,10 @@ app.use((req, res, next) => {
res.status(401).json({ error: 'Unauthorized.' });
});
function normalizeIp(value) {
return String(value || '').replace(/^::ffff:/, '');
}
app.use('/api/health', healthRouter);
app.use('/api/vms', vmsRouter);
app.use('/api/snapshots', snapshotsRouter);