Add dual-channel notifications — ntfy (already there) + email via Send-MailMessage / Yandex SMTP, mirroring VDS msmtp pipeline. Email: noreply@snolla.com -> vitya.kuznetsov@gmail.com. Verified delivered (DKIM pass, SPF pass) via run #2 + manual smoke. Settings sourced from C:\sites\snolla\Web.config <mailSettings> (Yandex SMTP), cross-checked against pass-store snolla-smtp/full-env. Same creds, same account as VDS backup msmtp uses. SMTP port: 587 STARTTLS, NOT 465 implicit-TLS: .NET SmtpClient / Send-MailMessage support only STARTTLS. Yandex accepts both; we use 587 for native PS tooling. VDS msmtp uses 465 implicit-TLS — both work, different tools. Size in email: switched from `rclone size --json | ConvertFrom-Json` (parses fail when rclone NOTICE stderr leaks into stdout) to local Get-ChildItem on C:\sites\snolla. Instant, no JSON dance. setup.ps1: ned params for SMTP creds + OPS_NOTIFY_EMAIL; config.env template extended. README.md: notifications section split into ntfy + email subsections, new SMTP-port + size-calc decisions in Decisions log. Run #2 (12:30:03) and run #3 (12:33) both produced email delivery receipts in user inbox. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
122 lines
5.1 KiB
PowerShell
122 lines
5.1 KiB
PowerShell
#requires -Version 5.1
|
||
#requires -RunAsAdministrator
|
||
<#
|
||
.SYNOPSIS
|
||
One-time setup для RUVDS daily backup → kreknin.
|
||
Run on RUVDS as Administrator. Idempotent.
|
||
|
||
.DESCRIPTION
|
||
1. Generate ed25519 SSH key (`C:\ProgramData\backup\kreknin-key`).
|
||
2. Install rclone v1.74+ to `C:\Program Files\rclone\rclone.exe`.
|
||
3. Write rclone.conf (SFTP remote `kreknin` без host-key validation —
|
||
see Decisions log).
|
||
4. Write config.env с ntfy creds (chmod-equiv via icacls SYSTEM+Administrators).
|
||
5. Deploy run.ps1 (companion file в this folder).
|
||
6. Register ScheduledTask `RUVDS-Backup-Daily` daily @ 04:30 MSK as SYSTEM.
|
||
|
||
User action required between steps 1 и 6: add `kreknin-key.pub` to
|
||
`/volume1/homes/vitya/.ssh/authorized_keys` on kreknin Synology.
|
||
#>
|
||
|
||
[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',
|
||
[string]$SmtpPass = 'JP44ajqZ3K#W',
|
||
[string]$OpsNotifyEmail = 'vitya.kuznetsov@gmail.com',
|
||
[string]$ScheduleAt = '04:30'
|
||
)
|
||
$ErrorActionPreference = 'Stop'
|
||
$base = 'C:\ProgramData\backup'
|
||
New-Item -ItemType Directory -Path $base -Force | Out-Null
|
||
|
||
# 1. SSH key
|
||
$keyPath = "$base\kreknin-key"
|
||
if (-not (Test-Path $keyPath)) {
|
||
ssh-keygen -t ed25519 -f $keyPath -N '""' -C 'ruvds-backup' -q
|
||
& cmd.exe /c "icacls `"$keyPath`" /inheritance:r /grant `"SYSTEM:(F)`" /grant `"BUILTIN\Administrators:(F)`"" | Out-Null
|
||
Write-Host "[OK] SSH key generated: $keyPath"
|
||
Write-Host "ADD TO KREKNIN authorized_keys:"
|
||
Get-Content "$keyPath.pub"
|
||
Read-Host "Press Enter when pubkey added to kreknin"
|
||
} else {
|
||
Write-Host "[SKIP] SSH key exists"
|
||
}
|
||
|
||
# 2. rclone
|
||
$rcloneExe = 'C:\Program Files\rclone\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
|
||
New-Item -ItemType Directory 'C:\Program Files\rclone' -Force | Out-Null
|
||
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_file (rclone go-sftp lib чувствителен к формату; ssh-key auth = trust enough)
|
||
@"
|
||
[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 `"BUILTIN\Administrators:(F)`"" | Out-Null
|
||
Write-Host "[OK] rclone.conf written"
|
||
|
||
# 4. config.env (ntfy + SMTP creds)
|
||
@"
|
||
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
|
||
"@ | Set-Content "$base\config.env" -Encoding ASCII -Force
|
||
& cmd.exe /c "icacls `"$base\config.env`" /inheritance:r /grant `"SYSTEM:(F)`" /grant `"BUILTIN\Administrators:(F)`"" | Out-Null
|
||
Write-Host "[OK] config.env written"
|
||
|
||
# 5. Deploy run.ps1 (assumed alongside this setup.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 `"BUILTIN\Administrators:(F)`"" | Out-Null
|
||
Write-Host "[OK] run.ps1 deployed"
|
||
|
||
# 6. Scheduled Task
|
||
$taskName = 'RUVDS-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 `
|
||
-ExecutionTimeLimit (New-TimeSpan -Hours 2)
|
||
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Force | Out-Null
|
||
Write-Host "[OK] ScheduledTask '$taskName' registered ($ScheduleAt daily, SYSTEM, 2h max)"
|
||
|
||
Write-Host ""
|
||
Write-Host "=== Setup complete. Test with: Start-ScheduledTask -TaskName '$taskName' ==="
|
||
Write-Host "Log: $base\logs\<date>.log"
|