diff --git a/README.md b/README.md index fd781a6..e27d26d 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,16 @@ # scripts -Коллекция скриптов для администрирования серверов: PostgreSQL для 1С, Windows-инфраструктура (1С, RD Gateway), мониторинг Zabbix. +Коллекция скриптов для администрирования инфраструктуры: PostgreSQL для 1С, Windows (1С, RD Gateway, развёртывание), MikroTik RouterOS, Proxmox, мониторинг Zabbix. + +Сюда консолидировано содержимое репозиториев `RouterOS`, `proxmox` и `win` — теперь всё в одном месте. ## Структура ``` -windows/ PowerShell-скрипты для Windows +windows/ PowerShell-скрипты для Windows (+ oobe/ — автоустановка Windows) pgsql/ PostgreSQL: установка, бэкапы, отказоустойчивость +routeros/ Скрипты и конфиги MikroTik RouterOS (+ docs/) +proxmox/ Скрипты для Proxmox VE linux/ Linux: тесты железа, конфиги окружения zabbix/ Шаблоны Zabbix ``` @@ -18,6 +22,18 @@ zabbix/ Шаблоны Zabbix | `1CDefenderExclusion.ps1` | Добавляет исключения 1С в Windows Defender: каталоги платформы и файловых баз (из `ibases.v8i` всех профилей), расширения и процессы. Запуск от администратора. | | `Replace-1C-Ibases.ps1` | Раздаёт эталонный `ibases.v8i` (список баз 1С) пользователям группы `1C_all` с бэкапом старых файлов. Интерактивный выбор пользователей. | | `Update-RDGWCertificate.ps1` | Получение/продление сертификата Let's Encrypt через Posh-ACME (DNS-плагин Beget) и установка на RD Gateway. Для планировщика: одно задание раз в сутки с наивысшими правами. Сохранять в UTF-8 with BOM. | +| `1CDefenderExclusion.bat` | Лаунчер для `1CDefenderExclusion.ps1` (запуск двойным кликом). | + +### windows/oobe/ + +Автоматизация чистой установки Windows. + +| Файл | Назначение | +|---|---| +| `AutoUnattend.xml` | Файл ответов для автоустановки Windows: локали, пропуск сетевой настройки, создание локального администратора `Admin`. | +| `oobe.ps1` | Автоматизация этапа OOBE через скачивание файла ответов. | + +> ⚠️ Создаётся локальный администратор `Admin/Admin` — сразу после установки смените пароль. Заглушки `CHANGE_ME` в конфиге замените своими значениями. ## pgsql/ @@ -42,6 +58,27 @@ zabbix/ Шаблоны Zabbix > ⚠️ Перед использованием замените в конфигах и скриптах IP-адреса, пароль keepalived и учётные данные репликации на свои (пароль репликации — через `~postgres/.pgpass`, права 600). +## routeros/ + +Скрипты и конфиги для MikroTik RouterOS. + +| Файл | Назначение | +|---|---| +| `autorun.scr` | Шаблон первичной настройки роутера: адресация, отключение небезопасных сервисов (telnet, ftp, api и т.д.), NTP, часовой пояс. Замените `CHANGE_ME` на свой пароль. | +| `check-isp.rsc` | Проверка доступности двух провайдеров пингом и сброс соединений упавшего канала (для dual-WAN). | +| `cloudflare-ip-updater.rsc` | Обновление address-list `CF` актуальными IPv4-подсетями Cloudflare (по расписанию). | +| `telegram-ip-updater.rsc` | Обновление address-list `TG` актуальными IPv4-подсетями Telegram (по расписанию). | +| `ospf_filter_rules` | Фильтры OSPF: принимать только частные подсети (RFC 1918). | +| `routerboard_fwupgrade` | Задание планировщика: автообновление прошивки RouterBOARD с перезагрузкой. | +| `routerboard_starwars` | Имперский марш на системном динамике после загрузки. | +| `docs/` | Документы: чек-лист настройки роутера, продуктовая матрица MikroTik. | + +## proxmox/ + +| Скрипт | Назначение | +|---|---| +| `chr_install.sh` | Развёртывание VM с MikroTik CHR на Proxmox VE: скачивание образа нужной версии, создание диска и виртуальной машины. Интерактивный. | + ## linux/ | Файл | Назначение | diff --git a/proxmox/chr_install.sh b/proxmox/chr_install.sh new file mode 100644 index 0000000..f2fe9ed --- /dev/null +++ b/proxmox/chr_install.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +#vars +version="nil" +vmID="nil" + +echo "############## Start of Script ############## + +## Checking if temp dir is available..." +if [ -d /root/temp ] +then + echo "-- Directory exists!" +else + echo "-- Creating temp dir!" + mkdir /root/temp +fi +# Ask user for version +echo "## Preparing for image download and VM creation!" +read -p "Please input CHR version to deploy (6.38.2, 6.40.1, etc):" version +# Check if image is available and download if needed +if [ -f /root/temp/chr-$version.img ] +then + echo "-- CHR image is available." +else + echo "-- Downloading CHR $version image file." + cd /root/temp + echo "---------------------------------------------------------------------------" + wget https://download.mikrotik.com/routeros/$version/chr-$version.img.zip + unzip chr-$version.img.zip + echo "---------------------------------------------------------------------------" +fi +if [ ! -f /root/temp/chr-$version.img ] +then + echo "-- Error downloading CHR $version image file!" + exit 0 +fi +# List already existing VM's and ask for vmID +echo "== Printing list of VM's on this hypervisor!" +qm list +echo "== Printing list of CT's on this hypervisor!" +pct list +echo "" +read -p "Please Enter free vm ID to use:" vmID +echo "" +# Create storage dir for VM if needed. +if [ -f /etc/pve/nodes/pve/qemu-server/$vmID.conf ] +then + echo "-- VM exists! Try another vm ID!" + exit 0 +fi +if [ -f /etc/pve/nodes/pve/lxc/$vmID.conf ] +then + echo "-- CT exists! Try another vm ID!" + exit 0 +fi +echo "-- Creating VM image dir!" +mkdir /var/lib/vz/images/$vmID +# Creating qcow2 image for CHR. +read -p "Please input image size, GB:" imgsize +echo "-- Converting image to qcow2 format " +qemu-img convert \ + -f raw \ + -O qcow2 \ + /root/temp/chr-$version.img \ + /var/lib/vz/images/$vmID/vm-$vmID-disk-1.qcow2 +if [ $imgsize -ne 0 ] +then + echo "-- Resize image to $imgsize GB" + qemu-img resize \ + /var/lib/vz/images/$vmID/vm-$vmID-disk-1.qcow2 +${imgsize}G +fi +# Creating VM +echo "-- Creating new CHR VM" +qm create $vmID \ + --name chr-$version \ + --net0 virtio,bridge=vmbr0 \ + --bootdisk virtio0 \ + --ostype l26 \ + --memory 256 \ + --onboot no \ + --sockets 1 \ + --cores 1 \ + --virtio0 local:$vmID/vm-$vmID-disk-1.qcow2 +echo "############## End of Script ##############" diff --git a/routeros/autorun.scr b/routeros/autorun.scr new file mode 100644 index 0000000..01fbbbb --- /dev/null +++ b/routeros/autorun.scr @@ -0,0 +1,30 @@ +/ip address +add address= interface=ether1 network=10.0.0.1 +/ip route +add dst-address=0.0.0.0/0 gateway=10.0.0.1 +/ip dhcp-client +add interface=ether1 disabled=yes +/user +set 0 password="CHANGE_ME" +/ip service +set telnet disabled=yes +set ftp disabled=yes +set www disabled=yes +set api disabled=yes +set api-ssl disabled=yes +/ip neighbor discovery-settings +set discover-interface-list=none +/tool mac-server +set allowed-interface-list=none +/tool mac-server mac-winbox +set allowed-interface-list=none +/tool mac-server ping +set enabled=no +/ipv6 settings +set disable-ipv6=yes +/system clock +set time-zone-name=Asia/Yekaterinburg +/system ntp client +set enabled=yes +/system ntp client servers +add address=pool.ntp.org diff --git a/routeros/check-isp.rsc b/routeros/check-isp.rsc new file mode 100644 index 0000000..8d2d7e3 --- /dev/null +++ b/routeros/check-isp.rsc @@ -0,0 +1,20 @@ +:local PingCount 5; +:local CheckIp1 77.88.8.8; +:local CheckIp2 77.88.8.1; +:local isp1 [/ping $CheckIp1 count=$PingCount interface="ether1"]; +:local isp2 [/ping $CheckIp2 count=$PingCount interface="ether2"]; + +:if ($isp1=0) do={ +:delay 2 +:log warning "ISP1 down"; +/ip firewall connection remove [ find where connection-mark="con-isp1"]; +:delay 2 +:log warning "ISP1 connection reset"; +} +:if ($isp2=0) do={ +:delay 2 +:log warning "ISP2 down"; +/ip firewall connection remove [ find where connection-mark="con-isp2"]; +:delay 2 +:log warning "ISP2 connection reset"; +} diff --git a/routeros/cloudflare-ip-updater.rsc b/routeros/cloudflare-ip-updater.rsc new file mode 100644 index 0000000..7100ed1 --- /dev/null +++ b/routeros/cloudflare-ip-updater.rsc @@ -0,0 +1,129 @@ +# CloudFlare IP Address List Updater (IPv4 only) +# Работает на RouterOS 6.x и 7.x +# Скачивает и обновляет список IPv4-адресов CloudFlare +# +# Установка: +# /system script add name=cloudflare-ip-updater source={ ... код ... } +# /system script run cloudflare-ip-updater +# +# Автозапуск: +# /system scheduler add name=cloudflare-update interval=7d on-event=cloudflare-ip-updater start-time=03:31:00 + +:local url "https://www.cloudflare.com/ips-v4/#" +:local listName "CF" +:local tempFile "cloudflare-ips.txt" + +# --- 1. Удаляем старый файл --- +:do { + /file remove $tempFile + :delay 1s +} on-error={} + +:log info "CloudFlare IP updater: Downloading $url..." + +# --- 2. Скачиваем файл --- +:do { + /tool fetch url=$url dst-path=$tempFile +} on-error={ + :log error "CloudFlare IP updater: Fetch command failed" + :error "Fetch failed" +} + +# --- 3. Ждём завершения загрузки --- +:local maxWait 30 +:local waited 0 +:while ([:len [/file find name=$tempFile]] = 0 && $waited < $maxWait) do={ + :delay 1s + :set waited ($waited + 1) +} + +:if ($waited >= $maxWait) do={ + :log error "CloudFlare IP updater: Download timeout" + :error "Download timeout" +} + +:delay 2s + +# --- 4. Проверка файла --- +:if ([:len [/file find name=$tempFile]] = 0) do={ + :log error "CloudFlare IP updater: File not found after download" + :error "Download failed" +} + +:local fileSize [/file get $tempFile size] +:if ($fileSize = 0) do={ + :log error "CloudFlare IP updater: Downloaded file is empty" + /file remove $tempFile + :error "Empty file" +} + +:log info "CloudFlare IP updater: File downloaded successfully ($fileSize bytes)" + +# --- 5. Очищаем старые записи --- +:local oldCount [:len [/ip firewall address-list find list=$listName]] +:if ($oldCount > 0) do={ + /ip firewall address-list remove [find list=$listName] + :log info "cloudflare IP updater: Removed $oldCount old entries from '$listName'" +} + +# --- 6. Читаем файл и добавляем новые записи --- +:local content [/file get $tempFile contents] +:local start 0 +:local len [:len $content] +:local count 0 +:local skipped 0 + +:while ($start < $len) do={ + :local pos [:find $content "\n" $start] + :if ([:typeof $pos] = "nil") do={ + :set pos $len + } + + :local line [:pick $content $start $pos] + + # Убираем \r + :local crPos [:find $line "\r"] + :if ([:typeof $crPos] != "nil") do={ + :set line [:pick $line 0 $crPos] + } + + # Убираем пробелы в начале + :while ([:len $line] > 0 && [:pick $line 0 1] = " ") do={ + :set line [:pick $line 1 [:len $line]] + } + + # Убираем пробелы в конце + :while ([:len $line] > 0 && [:pick $line ([:len $line] - 1)] = " ") do={ + :set line [:pick $line 0 ([:len $line] - 1)] + } + + # Пропускаем пустые строки и комментарии + :if ([:len $line] > 0 && [:pick $line 0 1] != "#") do={ + # Простая проверка: если есть двоеточие - это IPv6, пропускаем + :local colonPos [:find $line ":"] + + :if ([:typeof $colonPos] = "nil") do={ + # Нет двоеточия - пробуем добавить как IPv4 + :do { + /ip firewall address-list add list=$listName address=$line comment="cloudflare IPv4" + :set count ($count + 1) + } on-error={ + :set skipped ($skipped + 1) + :log warning "cloudflare IP updater: Skipped invalid IPv4 line: '$line'" + } + } else={ + :set skipped ($skipped + 1) + } + } + :set start ($pos + 1) +} + +# --- 7. Убираем временный файл --- +/file remove $tempFile + +# --- 8. Финальный лог --- +:if ($count > 0) do={ + :log info "cloudflare IP updater: Successfully added $count IPv4 entries to address list '$listName' (skipped $skipped IPv6 entries)" +} else={ + :log warning "cloudflare IP updater: No valid IPv4 entries found! (skipped $skipped entries)" +} diff --git a/routeros/docs/mikrotik_check-list_router.pdf b/routeros/docs/mikrotik_check-list_router.pdf new file mode 100644 index 0000000..7bb69af Binary files /dev/null and b/routeros/docs/mikrotik_check-list_router.pdf differ diff --git a/routeros/docs/mikrotik_product_matrix_26012101.xlsx b/routeros/docs/mikrotik_product_matrix_26012101.xlsx new file mode 100644 index 0000000..ab1ec17 Binary files /dev/null and b/routeros/docs/mikrotik_product_matrix_26012101.xlsx differ diff --git a/routeros/ospf_filter_rules b/routeros/ospf_filter_rules new file mode 100644 index 0000000..f0a3771 --- /dev/null +++ b/routeros/ospf_filter_rules @@ -0,0 +1,7 @@ +/routing filter rule +add chain=ospf-in disabled=no rule="if (dst in 192.168.0.0/16 && dst-len in 16-32) {accept;}" +add chain=ospf-in disabled=no rule="if (dst in 10.0.0.0/8 && dst-len in 8-32) {accept;}" +add chain=ospf-in disabled=no rule="if (dst in 172.16.0.0/12 && dst-len in 12-32) {accept;}" +add chain=ospf-out disabled=no rule="if (dst in 192.168.0.0/16 && dst-len in 16-32) {accept;}" +add chain=ospf-out disabled=no rule="if (dst in 10.0.0.0/8 && dst-len in 8-32) {accept;}" +add chain=ospf-out disabled=no rule="if (dst in 172.16.0.0/12 && dst-len in 12-32) {accept;}" diff --git a/routeros/routerboard_fwupgrade b/routeros/routerboard_fwupgrade new file mode 100644 index 0000000..4b955fd --- /dev/null +++ b/routeros/routerboard_fwupgrade @@ -0,0 +1,6 @@ +/system scheduler +add name=routerboard_fwupgrade on-event="if ([/system routerboard get current-firmware] != [/system routerboard get upgrade-firmware]) do={\r\ + \n/system routerboard upgrade\r\ + \n:delay 15s\r\ + \n/system reboot\r\ + \n}" policy=reboot,read,write,sensitive start-time=startup diff --git a/routeros/routerboard_starwars b/routeros/routerboard_starwars new file mode 100644 index 0000000..8e4f126 --- /dev/null +++ b/routeros/routerboard_starwars @@ -0,0 +1,23 @@ +/system scheduler +add name=starwars on-event=":delay 15;\r\ + \n \r\ + \n:beep frequency=500 length=500ms;:delay 500ms;\r\ + \n:beep frequency=500 length=500ms;:delay 500ms;\r\ + \n:beep frequency=500 length=500ms;:delay 500ms;\r\ + \n:beep frequency=400 length=500ms;:delay 400ms;\r\ + \n:beep frequency=600 length=200ms;:delay 100ms;\r\ + \n:beep frequency=500 length=500ms;:delay 500ms;\r\ + \n:beep frequency=400 length=500ms;:delay 400ms;\r\ + \n:beep frequency=600 length=200ms;:delay 100ms;\r\ + \n:beep frequency=500 length=500ms;:delay 1000ms;\r\ + \n \r\ + \n:beep frequency=750 length=500ms;:delay 500ms;\r\ + \n:beep frequency=750 length=500ms;:delay 500ms;\r\ + \n:beep frequency=750 length=500ms;:delay 500ms;\r\ + \n:beep frequency=810 length=500ms;:delay 400ms;\r\ + \n:beep frequency=600 length=200ms;:delay 100ms;\r\ + \n:beep frequency=470 length=500ms;:delay 500ms;\r\ + \n:beep frequency=400 length=500ms;:delay 400ms;\r\ + \n:beep frequency=600 length=200ms;:delay 100ms;\r\ + \n:beep frequency=500 length=500ms;:delay 1000ms;" policy=reboot,read,write \ + start-time=startup diff --git a/routeros/telegram-ip-updater.rsc b/routeros/telegram-ip-updater.rsc new file mode 100644 index 0000000..3c9f66d --- /dev/null +++ b/routeros/telegram-ip-updater.rsc @@ -0,0 +1,129 @@ +# Telegram IP Address List Updater (IPv4 only) +# Работает на RouterOS 6.x и 7.x +# Скачивает и обновляет список IPv4-адресов Telegram +# +# Установка: +# /system script add name=telegram-ip-updater source={ ... код ... } +# /system script run telegram-ip-updater +# +# Автозапуск: +# /system scheduler add name=telegram-update interval=1d on-event=telegram-ip-updater start-time=03:30:00 + +:local url "https://core.telegram.org/resources/cidr.txt" +:local listName "TG" +:local tempFile "telegram-ips.txt" + +# --- 1. Удаляем старый файл --- +:do { + /file remove $tempFile + :delay 1s +} on-error={} + +:log info "Telegram IP updater: Downloading $url..." + +# --- 2. Скачиваем файл --- +:do { + /tool fetch url=$url dst-path=$tempFile +} on-error={ + :log error "Telegram IP updater: Fetch command failed" + :error "Fetch failed" +} + +# --- 3. Ждём завершения загрузки --- +:local maxWait 30 +:local waited 0 +:while ([:len [/file find name=$tempFile]] = 0 && $waited < $maxWait) do={ + :delay 1s + :set waited ($waited + 1) +} + +:if ($waited >= $maxWait) do={ + :log error "Telegram IP updater: Download timeout" + :error "Download timeout" +} + +:delay 2s + +# --- 4. Проверка файла --- +:if ([:len [/file find name=$tempFile]] = 0) do={ + :log error "Telegram IP updater: File not found after download" + :error "Download failed" +} + +:local fileSize [/file get $tempFile size] +:if ($fileSize = 0) do={ + :log error "Telegram IP updater: Downloaded file is empty" + /file remove $tempFile + :error "Empty file" +} + +:log info "Telegram IP updater: File downloaded successfully ($fileSize bytes)" + +# --- 5. Очищаем старые записи --- +:local oldCount [:len [/ip firewall address-list find list=$listName]] +:if ($oldCount > 0) do={ + /ip firewall address-list remove [find list=$listName] + :log info "Telegram IP updater: Removed $oldCount old entries from '$listName'" +} + +# --- 6. Читаем файл и добавляем новые записи --- +:local content [/file get $tempFile contents] +:local start 0 +:local len [:len $content] +:local count 0 +:local skipped 0 + +:while ($start < $len) do={ + :local pos [:find $content "\n" $start] + :if ([:typeof $pos] = "nil") do={ + :set pos $len + } + + :local line [:pick $content $start $pos] + + # Убираем \r + :local crPos [:find $line "\r"] + :if ([:typeof $crPos] != "nil") do={ + :set line [:pick $line 0 $crPos] + } + + # Убираем пробелы в начале + :while ([:len $line] > 0 && [:pick $line 0 1] = " ") do={ + :set line [:pick $line 1 [:len $line]] + } + + # Убираем пробелы в конце + :while ([:len $line] > 0 && [:pick $line ([:len $line] - 1)] = " ") do={ + :set line [:pick $line 0 ([:len $line] - 1)] + } + + # Пропускаем пустые строки и комментарии + :if ([:len $line] > 0 && [:pick $line 0 1] != "#") do={ + # Простая проверка: если есть двоеточие - это IPv6, пропускаем + :local colonPos [:find $line ":"] + + :if ([:typeof $colonPos] = "nil") do={ + # Нет двоеточия - пробуем добавить как IPv4 + :do { + /ip firewall address-list add list=$listName address=$line comment="Telegram IPv4" + :set count ($count + 1) + } on-error={ + :set skipped ($skipped + 1) + :log warning "Telegram IP updater: Skipped invalid IPv4 line: '$line'" + } + } else={ + :set skipped ($skipped + 1) + } + } + :set start ($pos + 1) +} + +# --- 7. Убираем временный файл --- +/file remove $tempFile + +# --- 8. Финальный лог --- +:if ($count > 0) do={ + :log info "Telegram IP updater: Successfully added $count IPv4 entries to address list '$listName' (skipped $skipped IPv6 entries)" +} else={ + :log warning "Telegram IP updater: No valid IPv4 entries found! (skipped $skipped entries)" +} diff --git a/windows/1CDefenderExclusion.bat b/windows/1CDefenderExclusion.bat new file mode 100644 index 0000000..8a8d085 --- /dev/null +++ b/windows/1CDefenderExclusion.bat @@ -0,0 +1 @@ +powershell -executionpolicy RemoteSigned -file "%~dp0\1CDefenderExclusion.ps1" diff --git a/windows/oobe/AutoUnattend.xml b/windows/oobe/AutoUnattend.xml new file mode 100644 index 0000000..bd0bd97 --- /dev/null +++ b/windows/oobe/AutoUnattend.xml @@ -0,0 +1,86 @@ + + + + + + en-US; ru-RU + ru-RU + ru-RU + ru-RU + + + + + + + + true + + + + + + + en-US; ru-RU + ru-RU + ru-RU + ru-RU + + + + + true + true + 3 + + true + + + + + Administrators + Admin + + + + + + + + diff --git a/windows/oobe/oobe.ps1 b/windows/oobe/oobe.ps1 new file mode 100644 index 0000000..8f3cbe4 --- /dev/null +++ b/windows/oobe/oobe.ps1 @@ -0,0 +1,44 @@ +# Automating OOBE by downloading an answer file from the internet +# https://www.outsidethebox.ms/22491/#internet +# # # # # # # # # # # # # # # # # # # # ################ + +# # # # # # # # # # # # # # # # # # # # ################ +# Pre-checks: if not in OOBE, exit out +# https://oofhours.com/2023/09/15/detecting-when-you-are-in-oobe/ + +$TypeDef = @" +using System; +using System.Text; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Api +{ + public class Kernel32 + { + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern int OOBEComplete(ref int bIsOOBEComplete); + } +} +"@ +Add-Type -TypeDefinition $TypeDef -Language CSharp + +$IsOOBEComplete = $false +$hr = [Api.Kernel32]::OOBEComplete([ref] $IsOOBEComplete) +if ($IsOOBEComplete) { + Write-Host "Not in OOBE, nothing to do." + exit 0 +} + +# # # # # # # # # # # # # # # # # # # # ################ +# Download the answer file and point sysprep to it +Write-Host "Your username and password will be: Admin/Admin" -ForegroundColor DarkGreen +$uri = "https://bit.ly/491HWxy" +$answer = "$env:temp\UnattendOOBE.xml" +(Invoke-RestMethod -Uri $uri).OuterXml | Out-File -FilePath $answer -Encoding utf8 -Force +foreach ($letter in $((Get-Volume).DriveLetter)) { + if (Test-Path "$($letter):\Windows\System32\Sysprep\sysprep.exe") { + Invoke-Expression "$($letter):\Windows\System32\Sysprep\sysprep.exe /reboot /oobe /unattend:$answer" + break + } +}