fix: secure deploy API, remove Docker-in-Docker, add DB backup
- Add web+superadmin middleware to /api/deploy routes - Return JSON 403 in EnsureUserIsSuperadmin for API requests - Remove Docker CLI and docker.sock from app container - Replace docker compose restart with supervisorctl - Add mysqldump backup step before migrations
This commit is contained in:
+1
-10
@@ -33,13 +33,6 @@ RUN apt-get update && apt-get install -y \
|
||||
&& curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
|
||||
&& apt-get install -y nodejs \
|
||||
&& npm install -g npm \
|
||||
# Docker CLI for deploy script
|
||||
&& apt-get install -y ca-certificates curl gnupg \
|
||||
&& install -m 0755 -d /etc/apt/keyrings \
|
||||
&& curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
|
||||
&& chmod a+r /etc/apt/keyrings/docker.gpg \
|
||||
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $(. /etc/os-release && echo $VERSION_CODENAME) stable" > /etc/apt/sources.list.d/docker.list \
|
||||
&& apt-get update && apt-get install -y docker-ce-cli docker-compose-plugin \
|
||||
# Очистка кэша для уменьшения размера образа
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
@@ -63,10 +56,8 @@ RUN curl -sS https://getcomposer.org/installer | php -- \
|
||||
RUN mkdir -p storage/framework/views storage/framework/sessions storage/framework/cache storage/logs bootstrap/cache
|
||||
|
||||
# Добавляем www-data в группу с GID=1000 (совпадает с GID хост-пользователя)
|
||||
# и в группу docker для доступа к Docker socket при деплое
|
||||
RUN groupadd -g 1000 hostgroup 2>/dev/null || true \
|
||||
&& groupadd docker 2>/dev/null || true \
|
||||
&& usermod -aG hostgroup -G docker www-data
|
||||
&& usermod -aG hostgroup www-data
|
||||
|
||||
# Установка правильных прав доступа
|
||||
RUN chown -R www-data:www-data storage bootstrap/cache \
|
||||
|
||||
@@ -13,7 +13,7 @@ class DeployRunCommand extends Command
|
||||
|
||||
private string $statusFile = '/tmp/deploy-status.json';
|
||||
private string $lockFile = '/tmp/deploy.lock';
|
||||
private int $totalSteps = 12;
|
||||
private int $totalSteps = 13;
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
@@ -102,19 +102,36 @@ class DeployRunCommand extends Command
|
||||
$this->execCommand('composer install --no-dev --no-interaction --prefer-dist --no-cache');
|
||||
});
|
||||
|
||||
$this->executeStep(4, 'Migrate', function () {
|
||||
$this->executeStep(4, 'Database backup', function () {
|
||||
$backupDir = storage_path('app/backups');
|
||||
if (!is_dir($backupDir)) {
|
||||
mkdir($backupDir, 0755, true);
|
||||
}
|
||||
$filename = 'backup_' . now()->format('Y-m-d_H-i-s') . '.sql.gz';
|
||||
$filepath = $backupDir . '/' . $filename;
|
||||
$dbHost = env('DB_HOST', 'db');
|
||||
$dbName = env('DB_DATABASE', 'ntspi_db');
|
||||
$dbUser = env('DB_USERNAME', 'admin');
|
||||
$dbPass = env('DB_PASSWORD', 'secret');
|
||||
$this->execCommand(
|
||||
"mysqldump -h {$dbHost} -u {$dbUser} -p{$dbPass} {$dbName} | gzip > {$filepath}"
|
||||
);
|
||||
$this->addLog("Backup saved: {$filepath}");
|
||||
});
|
||||
|
||||
$this->executeStep(5, 'Migrate', function () {
|
||||
$this->execCommand('php artisan migrate --force');
|
||||
});
|
||||
|
||||
$this->executeStep(5, 'NPM install', function () {
|
||||
$this->executeStep(6, 'NPM install', function () {
|
||||
$this->execCommand('npm install --legacy-peer-deps');
|
||||
});
|
||||
|
||||
$this->executeStep(6, 'NPM build', function () {
|
||||
$this->executeStep(7, 'NPM build', function () {
|
||||
$this->execCommand('npm run build');
|
||||
});
|
||||
|
||||
$this->executeStep(7, 'Cache clear', function () {
|
||||
$this->executeStep(8, 'Cache clear', function () {
|
||||
$this->execCommand('php artisan cache:clear');
|
||||
$this->execCommand('php artisan config:clear');
|
||||
$this->execCommand('php artisan route:clear');
|
||||
@@ -123,7 +140,7 @@ class DeployRunCommand extends Command
|
||||
$this->execCommand('php artisan filament:optimize-clear');
|
||||
});
|
||||
|
||||
$this->executeStep(8, 'Cache warm', function () {
|
||||
$this->executeStep(9, 'Cache warm', function () {
|
||||
$this->execCommand('php artisan routes:register');
|
||||
$this->execCommand('php artisan route:cache');
|
||||
$this->execCommand('php artisan view:cache');
|
||||
@@ -132,21 +149,21 @@ class DeployRunCommand extends Command
|
||||
$this->execCommand('php artisan filament:optimize');
|
||||
});
|
||||
|
||||
$this->executeStep(9, 'Fix permissions', function () {
|
||||
$this->executeStep(10, 'Fix permissions', function () {
|
||||
$this->execCommand('chown -R www-data:www-data storage bootstrap/cache');
|
||||
$this->execCommand('chmod -R 775 storage bootstrap/cache');
|
||||
});
|
||||
|
||||
$this->executeStep(10, 'Restart containers', function () {
|
||||
$this->execCommand('docker compose down');
|
||||
$this->execCommand('docker compose up -d --remove-orphans');
|
||||
$this->executeStep(11, 'Restart processes', function () {
|
||||
$this->execCommand('supervisorctl restart inertia-ssr');
|
||||
$this->execCommand('supervisorctl restart cron');
|
||||
});
|
||||
|
||||
$this->executeStep(11, 'Wait 10 seconds', function () {
|
||||
sleep(10);
|
||||
$this->executeStep(12, 'Restart queue workers', function () {
|
||||
$this->execCommand('php artisan queue:restart');
|
||||
});
|
||||
|
||||
$this->executeStep(12, 'Maintenance off', function () {
|
||||
$this->executeStep(13, 'Maintenance off', function () {
|
||||
$this->execCommand('php artisan up');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,5 +3,7 @@
|
||||
use App\Containers\Dashboard\UI\API\Controllers\DeployController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::post('/deploy', [DeployController::class, 'store']);
|
||||
Route::get('/deploy/status', [DeployController::class, 'status']);
|
||||
Route::middleware(['web', 'superadmin'])->group(function () {
|
||||
Route::post('/deploy', [DeployController::class, 'store']);
|
||||
Route::get('/deploy/status', [DeployController::class, 'status']);
|
||||
});
|
||||
|
||||
@@ -18,6 +18,10 @@ class EnsureUserIsSuperadmin
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (!Auth::check() || !Auth::user()->hasRole('super_admin')) {
|
||||
if ($request->expectsJson() || $request->is('api/*')) {
|
||||
return response()->json(['error' => 'Forbidden'], 403);
|
||||
}
|
||||
|
||||
return Inertia::render('Error', ['status' => 403])
|
||||
->toResponse($request)
|
||||
->setStatusCode(403);
|
||||
|
||||
@@ -36,7 +36,6 @@ services:
|
||||
dockerfile: _docker/app/Dockerfile
|
||||
volumes:
|
||||
- ./:/var/www
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
container_name: ntspi-php
|
||||
restart: always
|
||||
ports:
|
||||
|
||||
Reference in New Issue
Block a user