Daily backup windows-host (DESKTOP-NSEF0UK) → kreknin via rclone SFTP. Pattern parallels ruvds-backup-daily-kreknin 🟢. Components (5): - MSSQL container: docker exec BACKUP DATABASE × 5 DBs (MoreThenCms, StayerCalculator, StayerPrice, stostayer, TireService) с COMPRESSION, INIT, FORMAT → docker cp → rclone sync - Sites: C:\sites\ - MinIO data: C:\Users\vitya\projects\docker\diskstation\minio\data - Traefik: C:\Users\vitya\projects\docker\diskstation\traefik\ - IIS config: applicationHost.config + Backup-WebConfiguration export Schedule: daily 03:00 MSK (sequential с RUVDS 04:30 + VDS 05:00). Wake-To-Run enabled — машина просыпается из standby на backup. SYSTEM principal (full access к C:\sites + Cert store + IIS metadata). Retention 7 daily snapshots. Notifications: dual-channel — ntfy `vds-backup` topic (shared) + email via Yandex SMTP 587 STARTTLS, noreply@snolla.com → ops gmail. Same creds как другие backup pipelines. Decisions log в README: - rclone в `C:\ProgramData\backup\rclone.exe` (не Program Files) — избегаем admin requirement на user-context staging. - icacls SID `*S-1-5-32-544` (well-known Administrators) — locale-safe для RU/EN Windows (BUILTIN\Administrators не парсится на ru-locale). - MSSQL backup via sqlcmd 18 с -C (trust cert) — TLS-required даже на localhost в новых mssql tools. - MSSQL_SA_PASS в config.env (windows-host не имеет pass setup); TODO pass-on-Windows long-term. Pre-staging уже сделано (ssh-key + kreknin authorized_keys via VDS pivot, rclone в ProgramData, SFTP smoke OK). setup.ps1 (elevated) пройдёт idempotent через staged steps, only Register-ScheduledTask fresh. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
129 lines
5.5 KiB
PowerShell
129 lines
5.5 KiB
PowerShell
#requires -Version 5.1
|
|
#requires -RunAsAdministrator
|
|
<#
|
|
.SYNOPSIS
|
|
One-time setup для windows-host daily fallback backup → kreknin.
|
|
Run on windows-host (DESKTOP-NSEF0UK) as Administrator. Idempotent.
|
|
|
|
.DESCRIPTION
|
|
1. Generate ed25519 SSH key (C:\ProgramData\backup\kreknin-key).
|
|
2. Install rclone (single .exe).
|
|
3. Write rclone.conf (SFTP remote `kreknin`, no host-key validation).
|
|
4. Write config.env (ntfy + SMTP + MSSQL SA creds; ACL SYSTEM+Administrators).
|
|
5. Deploy run.ps1.
|
|
6. Register ScheduledTask 'WindowsHost-Backup-Daily' daily 03:00 MSK as SYSTEM,
|
|
Wake-To-Run enabled чтобы запускался даже если машина в standby.
|
|
|
|
User action required between step 1 and 6: добавить kreknin-key.pub
|
|
в /volume1/homes/vitya/.ssh/authorized_keys на kreknin Synology.
|
|
(Alt: dispatch via VDS pivot — см. .tasks/windows-host-fallback-backup-daily.md)
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$NtfyUser = 'vitya',
|
|
[string]$NtfyPass = 'Pryakhin9',
|
|
[string]$NtfyUrl = 'https://ntfy.vds.kzntsv.site',
|
|
[string]$NtfyTopic = 'vds-backup',
|
|
[string]$SmtpHost = 'smtp.yandex.ru',
|
|
[int]$SmtpPort = 587,
|
|
[string]$SmtpFrom = 'noreply@snolla.com',
|
|
[string]$SmtpUser = 'noreply@snolla.com',
|
|
[Parameter(Mandatory=$true)][string]$SmtpPass,
|
|
[Parameter(Mandatory=$true)][string]$MssqlSaPass,
|
|
[string]$OpsNotifyEmail = 'vitya.kuznetsov@gmail.com',
|
|
[string]$ScheduleAt = '03:00'
|
|
)
|
|
$ErrorActionPreference = 'Stop'
|
|
$base = 'C:\ProgramData\backup'
|
|
New-Item -ItemType Directory -Path $base -Force | Out-Null
|
|
# Locale-safe Administrators SID (RU/EN Windows compatible)
|
|
$adminSid = '*S-1-5-32-544'
|
|
|
|
# 1. SSH key
|
|
$keyPath = "$base\kreknin-key"
|
|
if (-not (Test-Path $keyPath)) {
|
|
ssh-keygen -t ed25519 -f $keyPath -N '""' -C 'windows-host-backup' -q
|
|
& cmd.exe /c "icacls `"$keyPath`" /inheritance:r /grant `"SYSTEM:(F)`" /grant `"$adminSid`:(F)`"" | Out-Null
|
|
Write-Host "[OK] SSH key generated: $keyPath"
|
|
Write-Host ""
|
|
Write-Host "ADD THIS PUBKEY TO KREKNIN /var/services/homes/vitya/.ssh/authorized_keys:"
|
|
Get-Content "$keyPath.pub"
|
|
Write-Host ""
|
|
Read-Host "Press Enter after pubkey deployed to kreknin"
|
|
} else {
|
|
Write-Host "[SKIP] SSH key exists: $keyPath"
|
|
}
|
|
|
|
# 2. rclone — installed in ProgramData (не Program Files), не нужен elevated install
|
|
$rcloneExe = "$base\rclone.exe"
|
|
if (-not (Test-Path $rcloneExe)) {
|
|
$ver = (Invoke-RestMethod 'https://downloads.rclone.org/version.txt' -UseBasicParsing).Trim() -replace '^rclone\s+v|^v',''
|
|
$url = "https://downloads.rclone.org/v$ver/rclone-v$ver-windows-amd64.zip"
|
|
$tmp = "$env:TEMP\rclone.zip"
|
|
Invoke-WebRequest $url -OutFile $tmp -UseBasicParsing
|
|
if (Test-Path "$env:TEMP\rclone") { Remove-Item "$env:TEMP\rclone" -Recurse -Force }
|
|
Expand-Archive $tmp -DestinationPath "$env:TEMP\rclone" -Force
|
|
Copy-Item "$env:TEMP\rclone\rclone-*-windows-amd64\rclone.exe" $rcloneExe -Force
|
|
Write-Host "[OK] rclone installed: v$ver"
|
|
} else {
|
|
Write-Host "[SKIP] rclone installed: $(& $rcloneExe --version | Select-Object -First 1)"
|
|
}
|
|
|
|
# 3. rclone.conf (no known_hosts pin — rclone go-sftp doesn't parse keyscan cleanly; key-auth = sufficient trust)
|
|
@"
|
|
[kreknin]
|
|
type = sftp
|
|
host = 195.19.90.188
|
|
user = vitya
|
|
key_file = $keyPath
|
|
disable_hashcheck = true
|
|
"@ | Set-Content "$base\rclone.conf" -Encoding ASCII -Force
|
|
& cmd.exe /c "icacls `"$base\rclone.conf`" /inheritance:r /grant `"SYSTEM:(F)`" /grant `"$adminSid`:(F)`"" | Out-Null
|
|
Write-Host "[OK] rclone.conf written"
|
|
|
|
# 4. config.env
|
|
@"
|
|
NTFY_URL=$NtfyUrl
|
|
NTFY_TOPIC=$NtfyTopic
|
|
NTFY_USER=$NtfyUser
|
|
NTFY_PASS=$NtfyPass
|
|
SMTP_HOST=$SmtpHost
|
|
SMTP_PORT=$SmtpPort
|
|
SMTP_FROM=$SmtpFrom
|
|
SMTP_USER=$SmtpUser
|
|
SMTP_PASS=$SmtpPass
|
|
OPS_NOTIFY_EMAIL=$OpsNotifyEmail
|
|
MSSQL_SA_PASS=$MssqlSaPass
|
|
"@ | Set-Content "$base\config.env" -Encoding ASCII -Force
|
|
& cmd.exe /c "icacls `"$base\config.env`" /inheritance:r /grant `"SYSTEM:(F)`" /grant `"$adminSid`:(F)`"" | Out-Null
|
|
Write-Host "[OK] config.env written (ntfy + SMTP + MSSQL_SA_PASS)"
|
|
|
|
# 5. Deploy run.ps1
|
|
$srcRun = Join-Path $PSScriptRoot 'run.ps1'
|
|
if (-not (Test-Path $srcRun)) { throw "run.ps1 not found alongside setup.ps1 ($srcRun)" }
|
|
Copy-Item $srcRun "$base\run.ps1" -Force
|
|
& cmd.exe /c "icacls `"$base\run.ps1`" /inheritance:r /grant `"SYSTEM:(F)`" /grant `"$adminSid`:(F)`"" | Out-Null
|
|
Write-Host "[OK] run.ps1 deployed"
|
|
|
|
# 6. Scheduled Task (Wake-To-Run чтобы запускался даже если машина в standby)
|
|
$taskName = 'WindowsHost-Backup-Daily'
|
|
if (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue) {
|
|
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
|
|
}
|
|
$action = New-ScheduledTaskAction -Execute 'powershell.exe' `
|
|
-Argument "-NoProfile -ExecutionPolicy Bypass -File $base\run.ps1"
|
|
$trigger = New-ScheduledTaskTrigger -Daily -At $ScheduleAt
|
|
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
|
|
$settings = New-ScheduledTaskSettingsSet `
|
|
-AllowStartIfOnBatteries -DontStopIfGoingOnBatteries `
|
|
-StartWhenAvailable -MultipleInstances IgnoreNew `
|
|
-WakeToRun `
|
|
-ExecutionTimeLimit (New-TimeSpan -Hours 3)
|
|
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Force | Out-Null
|
|
Write-Host "[OK] ScheduledTask '$taskName' registered ($ScheduleAt daily, SYSTEM, Wake-To-Run, 3h max)"
|
|
|
|
Write-Host ""
|
|
Write-Host "=== Setup complete. Smoke: Start-ScheduledTask -TaskName '$taskName' ==="
|
|
Write-Host "Log: $base\logs\<date>.log"
|