403Webshell
Server IP : 202.61.199.114  /  Your IP : 216.73.217.139
Web Server : nginx/1.22.1
System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64
User : web20 ( 1018)
PHP Version : 8.4.23
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /var/www/clients/client2/web31/web/crm/cli/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/clients/client2/web31/web/crm/cli/backup_run.php
<?php
/**
 * ArNi CRM – Backup-Orchestrator (läuft als root über den Backup-Wrapper).
 * Aufruf: php cli/backup_run.php <backup_id> <run_id>
 *
 * Erstellt ein (optional passwortgeschütztes) 7z-Archiv aus Datenbanken, Web-Verzeichnissen
 * und Konfigurations-Pfaden und legt es am Ziel ab (lokal / FTP / SFTP / Google Drive via rclone).
 * Protokolliert Verlauf/Ergebnis in crm_server_backup_runs. Kein Bootstrap/Session (root-CLI).
 */
if (PHP_SAPI !== 'cli') { fwrite(STDERR, "CLI only\n"); exit(2); }
$bid = (int)($argv[1] ?? 0);
$rid = (int)($argv[2] ?? 0);
if (!$bid || !$rid) { fwrite(STDERR, "usage: backup_run.php <backup_id> <run_id>\n"); exit(2); }

$cfg = require __DIR__ . '/../config/config.php';
$dbc = $cfg['db'];
$prefix = $cfg['app']['table_prefix'] ?? 'crm_';
$appkey = (string)($cfg['app']['key'] ?? 'arni-crm');
$bcfg = $cfg['backup'] ?? [];
$owner = $bcfg['owner'] ?? 'web31';
$localDir = $bcfg['local_dir'] ?? (__DIR__ . '/../storage/backups');
$mysqlDefaults = '/etc/mysql/debian.cnf';

$pdo = new PDO("mysql:host={$dbc['host']};dbname={$dbc['name']};charset=utf8mb4", $dbc['user'], $dbc['pass'], [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$T  = function ($t) use ($prefix) { return $prefix . $t; };

$dec = function ($enc) use ($appkey) {
    if (!$enc) return '';
    $raw = base64_decode($enc, true); if ($raw === false || strlen($raw) < 17) return '';
    $iv = substr($raw, 0, 16); $ct = substr($raw, 16);
    $p = openssl_decrypt($ct, 'aes-256-cbc', hash('sha256', $appkey, true), OPENSSL_RAW_DATA, $iv);
    return $p === false ? '' : $p;
};

$log = [];
$logf = function ($m) use (&$log) { $log[] = date('H:i:s') . '  ' . $m; };
$run = function ($argv, $stdin = null, $timeout = 7200) {
    $cmd = implode(' ', array_map('escapeshellarg', $argv));
    $desc = [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
    $p = proc_open($cmd, $desc, $pipes);
    if (!is_resource($p)) return ['code' => -1, 'out' => '', 'err' => 'proc_open failed'];
    if ($stdin !== null) fwrite($pipes[0], $stdin);
    fclose($pipes[0]);
    $out = stream_get_contents($pipes[1]); $err = stream_get_contents($pipes[2]);
    fclose($pipes[1]); fclose($pipes[2]);
    return ['code' => proc_close($p), 'out' => (string)$out, 'err' => (string)$err];
};
$mysql = function ($argv) use ($mysqlDefaults, $run) {
    $bin = is_readable($mysqlDefaults) ? ['mysql', '--defaults-file=' . $mysqlDefaults] : ['mysql'];
    return $run(array_merge($bin, $argv));
};

$finish = function ($status, $size = null, $location = null, $archiveName = null) use ($pdo, $T, $rid, &$log) {
    $pdo->prepare('UPDATE ' . $T('server_backup_runs') . ' SET finished_at=?, status=?, size_bytes=?, location=?, archive_name=?, log=? WHERE id=?')
        ->execute([date('Y-m-d H:i:s'), $status, $size, $location, $archiveName, mb_substr(implode("\n", $log), 0, 60000), $rid]);
};

// Job + Run laden
$job = $pdo->query('SELECT * FROM ' . $T('server_backups') . ' WHERE id=' . $bid)->fetch(PDO::FETCH_ASSOC);
if (!$job) { fwrite(STDERR, "Backup-Job $bid nicht gefunden\n"); exit(2); }
$pdo->prepare('UPDATE ' . $T('server_backup_runs') . ' SET status=? WHERE id=?')->execute(['running', $rid]);

$work = sys_get_temp_dir() . '/arni-backup-' . $rid;
@mkdir($work, 0700, true);
$slug = preg_replace('/[^A-Za-z0-9._-]+/', '-', $job['name']) ?: 'backup';
$archiveName = $slug . '_' . date('Ymd_His') . '.7z';
$tmpArchive = $work . '/' . $archiveName;
$paths = []; // was ins Archiv kommt

try {
    $logf('Backup „' . $job['name'] . '" gestartet.');

    // 1) Datenbanken
    if ((int)$job['content_db'] === 1) {
        $dbDir = $work . '/databases'; @mkdir($dbDir, 0700, true);
        $names = array_values(array_filter(array_map('trim', preg_split('/[\s,]+/', (string)$job['db_names']))));
        if (!$names) {
            $r = $mysql(['-N', '-e', 'SHOW DATABASES']);
            foreach (preg_split('/\r\n|\n/', $r['out']) as $n) {
                $n = trim($n);
                if ($n === '' || in_array($n, ['information_schema', 'performance_schema', 'mysql', 'sys'], true)) continue;
                $names[] = $n;
            }
        }
        $logf(count($names) . ' Datenbank(en) werden gesichert.');
        foreach ($names as $n) {
            if (!preg_match('/^[A-Za-z0-9_]+$/', $n)) { $logf('  übersprungen (ungültiger Name): ' . $n); continue; }
            $dumpBin = is_readable($mysqlDefaults) ? ['mariadb-dump', '--defaults-file=' . $mysqlDefaults] : ['mariadb-dump'];
            if (!trim(shell_exec('command -v mariadb-dump 2>/dev/null'))) $dumpBin[0] = 'mysqldump';
            $r = $run(array_merge($dumpBin, ['--single-transaction', '--quick', '--routines', '--triggers', '--default-character-set=utf8mb4', $n]));
            if ($r['code'] !== 0) { $logf('  FEHLER Dump ' . $n . ': ' . trim($r['err'])); continue; }
            file_put_contents($dbDir . '/' . $n . '.sql', $r['out']);
            $logf('  ' . $n . ' (' . round(strlen($r['out']) / 1024) . ' KB)');
        }
        $paths[] = $dbDir;
    }

    // 2) Web-Verzeichnisse + 3) Konfigurations-Pfade
    foreach (['content_web' => 'web_paths', 'content_config' => 'config_paths'] as $flag => $field) {
        if ((int)$job[$flag] !== 1) continue;
        foreach (preg_split('/\r\n|\n/', (string)$job[$field]) as $p) {
            $p = trim($p);
            if ($p === '' || $p[0] !== '/' || strpos($p, '..') !== false) continue;
            if (!file_exists($p)) { $logf('  Pfad fehlt, übersprungen: ' . $p); continue; }
            $paths[] = $p;
            $logf('  enthalten: ' . $p);
        }
    }

    if (!$paths) throw new RuntimeException('Nichts zu sichern (keine Inhalte ausgewählt/gefunden).');

    // 4) 7z-Archiv (optional passwortgeschützt, Header-Verschlüsselung)
    $pw = $dec($job['archive_pass_enc']);
    $args = ['7z', 'a', '-t7z', '-mx=5', '-bd', '-y'];
    if ($pw !== '') { $args[] = '-mhe=on'; $args[] = '-p' . $pw; }
    $args[] = $tmpArchive;
    foreach ($paths as $p) $args[] = $p;
    $logf('Erstelle Archiv' . ($pw !== '' ? ' (verschlüsselt)' : '') . ' …');
    $r = $run($args);
    if ($r['code'] !== 0 || !is_file($tmpArchive)) throw new RuntimeException('7z fehlgeschlagen: ' . trim($r['err'] ?: $r['out']));
    $size = filesize($tmpArchive);
    $logf('Archiv erstellt: ' . $archiveName . ' (' . round($size / 1048576, 1) . ' MB)');

    // 5) Ablage am Ziel
    $dest = $job['destination'];
    $location = '';
    if ($dest === 'local') {
        $dir = trim((string)$job['dest_path']) ?: $localDir;
        if ($dir[0] !== '/' || strpos($dir, '..') !== false) throw new RuntimeException('Ungültiger lokaler Pfad.');
        @mkdir($dir, 0750, true);
        $target = rtrim($dir, '/') . '/' . $archiveName;
        if (!rename($tmpArchive, $target)) throw new RuntimeException('Konnte Archiv nicht nach ' . $dir . ' verschieben.');
        @chmod($target, 0640);
        $oi = @posix_getpwnam($owner); if ($oi) @chown($target, $owner); if ($oi) @chgrp($target, $owner);
        $location = $target;
        $logf('Lokal abgelegt: ' . $target);
        // Retention
        $keep = (int)$job['retention'];
        if ($keep > 0) {
            $files = glob(rtrim($dir, '/') . '/' . $slug . '_*.7z') ?: [];
            usort($files, function ($a, $b) { return filemtime($b) <=> filemtime($a); });
            foreach (array_slice($files, $keep) as $old) { @unlink($old); $logf('  Retention: gelöscht ' . basename($old)); }
        }
    } elseif ($dest === 'ftp' || $dest === 'sftp') {
        $host = $job['dest_host']; $port = (int)$job['dest_port']; $user = $job['dest_user']; $pass = $dec($job['dest_pass_enc']);
        $remoteDir = trim((string)$job['dest_path'], '/');
        $scheme = $dest; $portPart = $port > 0 ? ':' . $port : '';
        $urlbase = $scheme . '://' . $host . $portPart . '/' . ($remoteDir !== '' ? $remoteDir . '/' : '');
        $cu = ['curl', '-sS', '--connect-timeout', '20', '-T', $tmpArchive, $urlbase . $archiveName, '--user', $user . ':' . $pass];
        if ($dest === 'ftp') $cu[] = '--ftp-create-dirs';
        if ($dest === 'sftp') { $cu[] = '-k'; }
        $logf('Upload via ' . strtoupper($dest) . ' → ' . $host . '/' . $remoteDir);
        $r = $run($cu);
        if ($r['code'] !== 0) throw new RuntimeException(strtoupper($dest) . '-Upload fehlgeschlagen: ' . trim($r['err'] ?: $r['out']));
        $location = $scheme . '://' . $host . '/' . $remoteDir . '/' . $archiveName;
        @unlink($tmpArchive);
        $logf('Upload erfolgreich.');
    } elseif ($dest === 'gdrive') {
        $remote = trim((string)$job['dest_remote']);
        $remoteDir = trim((string)$job['dest_path'], '/');
        if ($remote === '') throw new RuntimeException('Kein rclone-Remote angegeben.');
        $target = $remote . ($remoteDir !== '' ? $remoteDir : '');
        $logf('Upload nach Google Drive via rclone → ' . $target);
        $r = $run(['rclone', 'copy', '--retries', '2', $tmpArchive, $target]);
        if ($r['code'] !== 0) throw new RuntimeException('rclone fehlgeschlagen: ' . trim($r['err'] ?: $r['out']));
        $location = $target . '/' . $archiveName;
        @unlink($tmpArchive);
        $logf('Google-Drive-Upload erfolgreich.');
    } else {
        throw new RuntimeException('Unbekanntes Ziel: ' . $dest);
    }

    $finish('success', $size, $location, $archiveName);
    $logf('Fertig.');
} catch (\Throwable $e) {
    $logf('FEHLER: ' . $e->getMessage());
    $finish('failed', is_file($tmpArchive) ? filesize($tmpArchive) : null, null, $archiveName);
} finally {
    // Aufräumen (Arbeitsverzeichnis + evtl. temporäres Archiv)
    if (is_file($tmpArchive)) @unlink($tmpArchive);
    if (is_dir($work)) { $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($work, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST); foreach ($it as $f) { $f->isDir() ? @rmdir($f) : @unlink($f); } @rmdir($work); }
}
exit(0);

Youez - 2016 - github.com/yon3zu
LinuXploit