GSZ-mail ali lRSKGk{OJy0K91D/7sxR2(<9@/dL@V+=dEjD
AD Пароль режима восстановления служб каталогов DSRM: >xz6DArh2gtO5~wY#Eo&,OypaKbE.UcVgf5?
msexadm e,Of$(G;OK>!Y&(~Ph)Y+U;%A1ygJ>{QJcqG
IPBan Web jgynKwsolJ84c6G@Vx=yF:OW~F#{3tt;D,HC
pve.symfio.net root lM0se2oF9M1HPpT2ebUuMmv1d
Ключи
7WJV6-H9RMH-F4267-3R2KG-F6PBYQXYKC-7H87P-YKC2Q-XRVQ7-GTJP2 YCQY7-BNTF6-R337H-69FGX-P39TY Enterprise до 100 БД и до 16Тб на БД Standard до 5 БД и до 1Тб на БДПосле установки Exchange запускаем либо руками Exchange Shell в PowerShell
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn
Либо добавляем автоматический старт
New-Item -Path $PROFILE -Type File -Force
notepad $PROFILE
# Добавить строку:
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn -ErrorAction SilentlyContinue
В настройках ставим (enp5s0 - основной интерфейс - железный)
nano /etc/network/interfaces
auto enp5s0
iface enp5s0 inet manual
auto vmbr0
iface vmbr0 inet static
address 195.201.108.88/26
gateway 195.201.108.65
bridge-ports enp5s0
bridge-stp off
bridge-fd 0
auto vmbr1
iface vmbr1 inet static
address 192.168.1.1/24
netmask 255.255.255.0
bridge-ports none
bridge-stp off
bridge-fd 0
post-up echo 1 > /proc/sys/net/ipv4/ip_forward
ifreload -a
Настраиваем проброс на firewalld с 25 и 587 портов на 25 и 587, а также проброс 3389 порта на VM Exchange
firewall-cmd --zone="public" --add-forward-port=port=25:proto=tcp:toport=25:toaddr=192.168.1.20 --permanent
firewall-cmd --zone="public" --add-forward-port=port=587:proto=tcp:toport=587:toaddr=192.168.1.20 --permanent
firewall-cmd --zone="public" --add-forward-port=port=45586:proto=tcp:toport=3389:toaddr=192.168.1.20 --permanent
firewall-cmd --reload
Разрешаем DNS для внутренней сети
firewall-cmd --permanent --zone=trusted --add-interface=vmbr1
firewall-cmd --permanent --zone=trusted --add-service=dns
firewall-cmd --reload
nano /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600
banaction = firewallcmd-ipset
sender = sy-proxmox@symfio.de
destemail = sy-infra@mail.symfio.de
action = %(action_mwl)s
[sshd]
enabled = true
bantime = 1864000
maxretry = 3
port = 10022
backend = systemd
action = firewallcmd-ipset[chain=INPUT]
[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
[nginx-limit-req]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
[nginx-botsearch]
enabled = true
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 2
[postfix]
enabled = true
#port = smtp,submission,smtps
port = smtp
filter = postfix[mode=aggressive]
backend = systemd
findtime = 86400
bantime = 604800
maxretry = 3
Если установлен postfix, то проверяем и меняем, чтобы он слушал только на локальных интерфейсах, а не на всех, потому как он перехватывает трафик на 25 порту, даже если порт проброшен и только потом через него трафик идет на VM с Exchange
postconf -e inet_interfaces = loopback-only
systemctl restart postfix
postconf inet_interfaces
ss -tlnp | grep :25
LISTEN 127.0.0.1:25 ← Только localhost!
LISTEN [::1]:25 ← Только localhost!
⚠️ В итоге для фильтрации спама postfix нужен как никогда. Если оставлять как есть, то только контекстный антиспам, которого сильно мало: 80-85% спама проходит. Релей Microsoft Edge обеспечит только блокировку по спискам RBL (неплохо, но это очень мало). Есть облачное решение от Microsoft - хорошее, но платное, не зря они убрали по максимуму хоть какую-то фильтрацию спама, что была раньше из локальных продуктов. Поэтому единственный оптимальный вариант ставить postfix.
nano /etc/postfix/main.cf
# Базовые параметры
myhostname = mail.gabelstapler-gebraucht.at
mydomain = gabelstapler-gebraucht.at
myorigin = $mydomain
# Принимать почту для этих доменов (входящая почта)
relay_domains =
gabelstapler-gebraucht.at,
gabelstapler-zentrum.de,
gabelstapler-zentrum.ch
# НЕ доставлять локально (все на Exchange), также без проверок существования получателей (LDAP - local_recipient_maps - пустой)
smtpd_banner = $myhostname Microsoft ESMTP MAIL Service ready
mydestination = $myhostname, pve.symfio.net, sy-proxmox, localhost.localdomain, localhost
local_recipient_maps =
# Маршрутизация на Exchange (исходящая почта)
transport_maps = hash:/etc/postfix/transport
# Сеть
inet_protocols = all
inet_interfaces = all
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 192.168.1.20
# SMTP клиент настройки (для отправки на Exchange)
smtp_host_lookup = dns, native
smtp_connect_timeout = 30s
smtp_helo_timeout = 30s
# Можно отключить TLS для внутреннего Exchange - я не отключал
smtp_tls_security_level = may
smtp_tls_loglevel = 1
# Ограничения для атак перебором (можно уменьшить параметры)
smtpd_client_connection_count_limit = 50
smtpd_client_connection_rate_limit = 100
nano /etc/postfix/transport
# Все три домена идут на Exchange
gabelstapler-gebraucht.at smtp:[192.168.1.20]:25
gabelstapler-zentrum.de smtp:[192.168.1.20]:25
gabelstapler-zentrum.ch smtp:[192.168.1.20]:25
# Wildcard для поддоменов (если нужно)
.gabelstapler-gebraucht.at smtp:[192.168.1.20]:25
.gabelstapler-zentrum.de smtp:[192.168.1.20]:25
.gabelstapler-zentrum.ch smtp:[192.168.1.20]:25
# Создать hash базу для transport
postmap /etc/postfix/transport
postfix check
postfix reload
Можно добавить по подключению к внутреннему smtp-серверу (релей, Exchange), я не ставил в своем конфиге
# как резолвить hostname в случае при отправке почты в релее (в случае задания в transport доменных имен) сначала DNS, потом /etc/hosts) - дефолт dns
smtp_host_lookup = dns, native
# таймаут подключения к удаленному SMTP - дефолт 30s
smtp_connect_timeout = 30s
# таймаут ожидания ответа на HELO/EHLO - дефолт 300s
smtp_helo_timeout = 30s
# Максимальное количество одновременных подключений с одного IP. Защита от DDoS. 10 достаточно для легитимных серверов
smtpd_client_connection_count_limit = 10
#Максимальное количество подключений в течении 1 минуты (рейт). Защита от DDoS. 30 подключений для нормального mail flow хватит. В случае превышения:
# warning: Connection rate limit exceeded
# NOQUEUE: reject: CONNECT from unknown[1.2.3.4]: 421 4.7.0 Error: too many connections
smtpd_client_connection_rate_limit = 30
# Задержка перед ответом на каждую ошибочную команду SMTP. Замедляет spam-ботов и bruteforce-атаки. 5s - сильно замедлит ботов, но вообще не проблема для легитимных серверов
smtpd_error_sleep_time = 5s
# После скольких ошибок применяется smtpd_error_sleep_time. 3 - дать шанс ошибок нормальному серверу с некоторыми проблемами
smtpd_soft_error_limit = 3
# После скольких ошибок разрывать соединение (421 Too many errors, goodbye). 10 - ясно проблемный сервер или бот. При превышении:
# too many errors after RCPT from unknown[5.6.7.8]
# disconnect from unknown[5.6.7.8] commands=5/10
# 5 - успешных команд, 10 - ошибочных, например, поздоровались и клиент начал слать левые RCPT TO, AUTH fail и т.д.
smtpd_hard_error_limit = 10
# Таймауты между командами - т.е. клиент подключился с ним поприветствовались и ждем следующую команду (EHLO, MAIL FROM, RCPT TO, DATA) - если боль 300s, то таймаут. 300s - с головой хватит для медленных хостов, чтобы не иметь явную DOS slowloris-атаку, когда атакующий тупо держит множество соединений - просто поддерживая их активными - переполняется количество соединений и наступает отказ в обслуживании.
smtpd_timeout = 300s
smtpd_starttls_timeout = 300s
# Максимум получателей в одной сессии (чтобы не было спама, но рассылки разрешены). Спам обычно тысячи получателей.
# Пример:
# MAIL FROM:<sender@example.com>
# RCPT TO:<user1@domain.com> # получатель 1
# RCPT TO:<user2@domain.com> # получатель 2
# ...
# RCPT TO:<user50@domain.com> # получатель 50 → OK
# RCPT TO:<user51@domain.com> # получатель 51 → "452 Too many recipients"
# DATA
# Subject: Newsletter
# ...
smtpd_recipient_limit = 50
# Сколько лишних получателей после перебора лимита, до дисконнекта
# Клиент отправляет: RCPT TO 1, 2, 3... 50 → OK
# Получатель 51 → "452 Too many recipients"
# Клиент продолжает: 52, 53... 100 → всё еще принимает
# Получатель 101 → "421 Disconnect" (overshoot превышен)
smtpd_recipient_overshoot_limit = 100
# Сколько AUTH попыток разрешено за 1 минуту. 10 за минуту - оптимально
# Атакующий пробует:
# AUTH user1:password1
# AUTH user1:password2
# AUTH user1:password3
# ...
smtpd_client_auth_rate_limit = 10
# Сколько нераспознанных/мусорных SMTP команд разрешено за сессию. Т.е. все что не HELO/EHLO/MAIL/RCPT/DATA/QUIT/AUTH
# Пример:
# Клиент: EHLO example.com → OK
# Клиент: BLAHBLAH → junk (счетчик +1)
# Клиент: XYZABC → junk (счетчик +2)
# ... после 100 junk команд → disconnect
smtpd_junk_command_limit = 50
| Параметр | Строгие (high security) |
Средние (balanced) | Мягкие (permissive) |
|---|---|---|---|
| connection_count_limit | 5 | 10 | 50 |
| connection_rate_limit | 10 | 30 | 100 |
| error_sleep_time | 10s | 5s | 1s |
| soft_error_limit | 1 | 3 | 5 |
| hard_error_limit | 5 | 10 | 20 |
| Средние (balanced) - защищают от атак, но не блокируют легитимную почту. |
# Смотрим новые события
journalctl -u postfix -f | grep -E 'too many|error|limit'
# Смотрим события в логе, которые уже были и также отказы (NOQUEUE)
journalctl -u postfix | grep -E 'too many|error|limit|NOQUEUE'
Для варианта 1 Конфигурация Exchange Send Connector:
New-SendConnector -Name "Internet Send Connector" `
-Usage Internet `
-AddressSpaces * `
-SourceTransportServers NAME-EXCHANGE-SERVER `
-DNSRoutingEnabled $true
Для варианта 2
Set-SendConnector "Internet Send Connector" `
-SmartHosts 192.168.1.X `
-DNSRoutingEnabled $false
В postfix main.cf нужно разрешить Exchange отправлять через нас и также разрешить прохождение фильтрации и проверки для исходящей почты
smtpd_relay_restrictions =
permit_mynetworks,
reject_unauth_destination
smtpd_sender_restrictions =
permit_mynetworks,
reject_unknown_sender_domain
Преисущества:
apt-get install postfix-ldap
Настраиваем main.cf на проверку получателей через LDAP
relay_recipient_maps = ldap:/etc/postfix/ldap-recipients.cf
Настраиваем подключение по LDAP (пароль в ldap-recipients.cf без кавычек)
nano /etc/postfix/ldap-recipients.cf
# Подключение к Active Directory Exchange
server_host = 192.168.1.20
server_port = 389
version = 3
bind = yes
bind_dn = CN=postfix,CN=Users,DC=gsz,DC=local
bind_pw = XrOc5YSWPCBOmrjPOmzJMvuykv13dlU8
chase_referrals = no
# Поиск получателей
search_base = DC=gsz,DC=local
query_filter = (|(mail=%s)(proxyAddresses=smtp:%s))
result_attribute = mail
# Опционально: TLS
start_tls = yes
tls_require_cert = no
# В ldap-recipients.cf опционально, что вернуть (email адрес). В моем конфиге нет - он и так возвращает email
result_attribute = mail
Создаем служебную учетку в AD
New-ADUser -Name "postfix" `
-SamAccountName "postfix.gsz.local" `
-UserPrincipalName "postfix@gsz.local" `
-AccountPassword (ConvertTo-SecureString "XrOc5YSWPCBOmrjPOmzJMvuykv13dlU8" -AsPlainText -Force) `
-Enabled $true `
-PasswordNeverExpires $true
Если получатель не найден в LDAP - postfix должен отклонить с 550 User unknown. Тоже самое сообщение будет, если нет подключения к LDAP по разным причинам - он же не находит email.
proxyAddresses=smtp:%s - это дополнительные email алиасы в Exchange/AD. Пример: Основной email (mail): info@gabelstapler-gebraucht.at Алиасы (proxyAddresses): smtp:kontakt@gabelstapler-gebraucht.at smtp:sales@gabelstapler-gebraucht.at Фильтр (|(mail=%s)(proxyAddresses=smtp:%s)) проверяет ОБА - и основной адрес, и все алиасы, чтобы postfix принимал письма на любой из них.
Алиасы proxyAddresses создаются в Exchange автоматически или вручную:
apt-get install ldap-utils
ldapsearch -x -H ldap://192.168.1.20 \
-D "CN=postfix,CN=Users,DC=gsz,DC=local" \
-w "PASSWORD" \
-b "DC=gsz,DC=local" \
"(mail=info@gabelstapler-gebraucht.at)"
# Проверка работы через конфиг postfix'а и ldap-recipients.cf работы ldap
postmap -q info@gabelstapler-gebraucht.at ldap:/etc/postfix/ldap-recipients.cf
nano /etc/postfix/main.cf
myhostname = mail.gabelstapler-gebraucht.at
mydomain = gabelstapler-gebraucht.at
myorigin = $mydomain
smtpd_banner = $myhostname Microsoft ESMTP MAIL Service ready
mydestination = $myhostname, pve.symfio.net, sy-proxmox, localhost.localdomain, localhost
smtpd_tls_security_level = may
smtpd_tls_cert_file = /etc/letsencrypt/live/autodiscover.gabelstapler-gebraucht.at/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/autodiscover.gabelstapler-gebraucht.at/privkey.pem
inet_protocols = all
inet_interfaces = all
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 192.168.1.20
relay_domains =
gabelstapler-gebraucht.at,
gabelstapler-zentrum.de,
gabelstapler-zentrum.ch
transport_maps = hash:/etc/postfix/transport
# Connection limits
smtpd_client_connection_count_limit = 10
smtpd_client_connection_rate_limit = 30
smtpd_error_sleep_time = 5s
smtpd_soft_error_limit = 3
smtpd_hard_error_limit = 10
smtpd_timeout = 300s
smtpd_starttls_timeout = 300s
smtpd_recipient_limit = 50
smtpd_recipient_overshoot_limit = 100
smtpd_client_auth_rate_limit = 10
smtpd_junk_command_limit = 50
message_size_limit = 131072000
relay_recipient_maps = ldap:/etc/postfix/ldap-recipients.cf
rbl_reply_maps = hash:/etc/postfix/dnsbl-reply-map
address_verify_sender = <>
smtpd_helo_required = yes
smtpd_helo_restrictions =
permit_mynetworks
check_helo_access hash:/etc/postfix/helo_access
permit_sasl_authenticated
reject_invalid_helo_hostname
reject_non_fqdn_helo_hostname
reject_unknown_helo_hostname
smtpd_sender_restrictions =
permit_mynetworks
check_sender_access hash:/etc/postfix/sender
permit_sasl_authenticated
reject_non_fqdn_sender
reject_unknown_sender_domain
smtpd_client_restrictions =
permit_mynetworks
permit_sasl_authenticated
# check_recipient_access pcre:/etc/postfix/recipient_access.pcre
# Greylist:
# check_policy_service inet:127.0.0.1:10023
reject_unknown_client_hostname
reject_unknown_reverse_client_hostname
smtpd_relay_restrictions =
permit_mynetworks
permit_sasl_authenticated
reject_unauth_destination
smtpd_recipient_restrictions =
# whitelists:
permit_mynetworks
permit_sasl_authenticated
check_client_access hash:/etc/postfix/client_whitelist
reject_unauth_destination
reject_rhsbl_sender isp6kcxuifx4wmciyxx2aqedjy.dbl.dq.spamhaus.net=127.0.1.[2..99]
reject_rhsbl_helo isp6kcxuifx4wmciyxx2aqedjy.dbl.dq.spamhaus.net=127.0.1.[2..99]
reject_rhsbl_reverse_client isp6kcxuifx4wmciyxx2aqedjy.dbl.dq.spamhaus.net=127.0.1.[2..99]
reject_rhsbl_sender isp6kcxuifx4wmciyxx2aqedjy.zrd.dq.spamhaus.net=127.0.2.[2..24]
reject_rhsbl_helo isp6kcxuifx4wmciyxx2aqedjy.zrd.dq.spamhaus.net=127.0.2.[2..24]
reject_rhsbl_reverse_client isp6kcxuifx4wmciyxx2aqedjy.zrd.dq.spamhaus.net=127.0.2.[2..24]
reject_rbl_client isp6kcxuifx4wmciyxx2aqedjy.zen.dq.spamhaus.net=127.0.0.[2..255]
reject_rbl_client all.s5h.net
reject_rbl_client MTH44A3D82031209E86.noptr.spamrats.com=127.0.0.37
reject_rbl_client MTH44A3D82031209E86.spam.spamrats.com=127.0.0.38
reject_rbl_client MTH44A3D82031209E86.dyna.spamrats.com=127.0.0.36
# host check's:
# reject_rbl_client zen.spamhaus.org
# reject_rbl_client bl.spamcop.net
# reject_rbl_client list.dsbl.org
# reject_rbl_client rbl.mail-abuse.org
# reject_rbl_client spamsources.fabel.dk
# reject_rbl_client cbl.abuseat.org
# reject_rbl_client dul.dnsbl.sorbs.net
# RCPT TO check's:
reject_non_fqdn_recipient
reject_unauth_pipelining
reject_unlisted_recipient
Проверяем работу TLS и, что корректно выводит сертификат openssl s_client -connect localhost:25 -starttls smtp
⚠️ Для удаления из базы спама s5h.net (просто с того хоста):
curl -4 http://www.usenix.org.uk/content/rblremove
nano /etc/postfix/transport
# Домены
gabelstapler-gebraucht.at smtp:[192.168.1.20]:25
gabelstapler-zentrum.de smtp:[192.168.1.20]:25
gabelstapler-zentrum.ch smtp:[192.168.1.20]:25
# Wildcard для поддоменов
.gabelstapler-gebraucht.at smtp:[192.168.1.20]:25
.gabelstapler-zentrum.de smtp:[192.168.1.20]:25
.gabelstapler-zentrum.ch smtp:[192.168.1.20]:25
nano /etc/postfix/ldap-recipients.cf
# Подключение к Active Directory Exchange
server_host = 192.168.1.20
server_port = 389
version = 3
bind = yes
bind_dn = CN=postfix,CN=Users,DC=gsz,DC=local
bind_pw = XrOc5YSWPCBOmrjPOmzJMvuykv13dlU8
chase_referrals = no
# Поиск получателей
search_base = DC=gsz,DC=local
query_filter = (|(mail=%s)(proxyAddresses=smtp:%s))
result_attribute = mail
# Опционально: TLS
start_tls = yes
tls_require_cert = no
nano /etc/postfix/dnsbl-reply-map
isp6kcxuifx4wmciyxx2aqedjy.sbl.dq.spamhaus.net=127.0.0.[2..255] $rbl_code Service unavailable; $rbl_class [$rbl_what] blocked using sbl.spamhaus.org${rbl_reason?; $rbl_reason}
isp6kcxuifx4wmciyxx2aqedjy.xbl.dq.spamhaus.net=127.0.0.[2..255] $rbl_code Service unavailable; $rbl_class [$rbl_what] blocked using xbl.spamhaus.org${rbl_reason?; $rbl_reason}
isp6kcxuifx4wmciyxx2aqedjy.pbl.dq.spamhaus.net=127.0.0.[2..255] $rbl_code Service unavailable; $rbl_class [$rbl_what] blocked using pbl.spamhaus.org${rbl_reason?; $rbl_reason}
isp6kcxuifx4wmciyxx2aqedjy.sbl-xbl.dq.spamhaus.net=127.0.0.[2..255] $rbl_code Service unavailable; $rbl_class [$rbl_what] blocked using sbl-xbl.spamhaus.org${rbl_reason?; $rbl_reason}
isp6kcxuifx4wmciyxx2aqedjy.zen.dq.spamhaus.net=127.0.0.[2..255] $rbl_code Service unavailable; $rbl_class [$rbl_what] blocked using zen.spamhaus.org${rbl_reason?; $rbl_reason}
isp6kcxuifx4wmciyxx2aqedjy.dbl.dq.spamhaus.net=127.0.1.[2..99] $rbl_code Service unavailable; $rbl_class [$rbl_what] blocked using dbl.spamhaus.org${rbl_reason?; $rbl_reason}
isp6kcxuifx4wmciyxx2aqedjy.zrd.dq.spamhaus.net=127.0.2.[2..24] $rbl_code Service unavailable; $rbl_class [$rbl_what] blocked using zrd.spamhaus.org${rbl_reason?; $rbl_reason}
nano /etc/postfix/helo_access
nano /etc/postfix/sender
eumail.docusign.net OK
mailfr.eumail.docusign.net OK
mailcl.eumail.docusign.net OK
dse@docusign.net OK
dse_demo@docusign.net OK
dse_na2@docusign.net OK
dse_na3@docusign.net OK
dse_na4@docusign.net OK
dse@eumail.docusign.net OK
dse@camail.docusign.net OK
dse@aumail.docusign.net OK
dse@fedmail.docusign.net OK
nano client_whitelist
# Mailgun whitelist для Facebook лидов
198.244.56.64 OK
69.72.42.14 OK
69.72.46.205 OK
69.72.43.2 OK
mailgun.net OK
send.mailgun.net OK
Не забываем выполнить postmap
postmap /etc/postfix/transport
postmap /etc/postfix/dnsbl-reply-map
postmap /etc/postfix/helo_access
postmap /etc/postfix/sender
postmap /etc/postfix/client_whitelist
postfix check
postfix reload
# Проверить что домены релеятся
postmap -q gabelstapler-gebraucht.at hash:/etc/postfix/transport
# Должно вывести: smtp:[192.168.1.20]:25
# Проверить transport маршрутизацию
postmap -q gabelstapler-gebraucht.at hash:/etc/postfix/transport
# Проверить relay доступ
postmap -q gabelstapler-zentrum.de regexp:/etc/postfix/relay_domains
# Проверить ldap-запрос
postmap -q info@gabelstapler-gebraucht.at ldap:/etc/postfix/ldap-recipients.cf
# Отправить тестовое письмо
echo "Test" | mail -s "Test" user@gabelstapler-gebraucht.at
# Проверить очередь
postqueue -p
# Последние с watch дальнейших
journalctl -u postfix -f
# За последний час, 10 часов с нормальным просмотром в less - без курсора вправо-влево
journalctl -u postfix --since "1 hour ago" | less
journalctl -u postfix --since "10 hour ago" | less
LOKI Вариант 1: Promtail читает journald (рекомендуется)
server: http_listen_port: 9080 grpc_listen_port: 0
positions: filename: /tmp/positions.yaml
clients: - url: https://loki.example.com/loki/api/v1/push # bearer_token: YOUR_TOKEN # если нужна авторизация
scrape_configs: - job_name: postfix journal: max_age: 12h labels: job: postfix host: sy-proxmox service: mail relabel_configs: # Фильтр только Postfix - source_labels: ['__journal__systemd_unit'] regex: 'postfix.*' action: keep # Добавить имя unit в label - source_labels: ['__journal__systemd_unit'] target_label: 'unit'
Вариант 2: Promtail через syslog pipe
scrape_configs: - job_name: syslog syslog: listen_address: 127.0.0.1:1514 labels: job: syslog host: sy-proxmox relabel_configs: - source_labels: ['__syslog_message_app_name'] regex: 'postfix.*' action: keep - source_labels: ['__syslog_message_app_name'] target_label: 'app'
Настроить rsyslog для отправки в Promtail:
if $programname startswith 'postfix' then @@127.0.0.1:1514
Установка Promtail
wget https://github.com/grafana/loki/releases/download/v2.9.3/promtail-linux-amd64.zip unzip promtail-linux-amd64.zip chmod +x promtail-linux-amd64 mv promtail-linux-amd64 /usr/local/bin/promtail
cat > /etc/systemd/system/promtail.service <<EOF
[Unit]
Description=Promtail service
After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/promtail -config.file=/etc/promtail/config.yml
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
# Запустить
systemctl daemon-reload
systemctl enable promtail
systemctl start promtail
Проверка логов в Loki
{job="postfix"} |= "reject" {job="postfix", unit="postfix@-.service"} |= "NOQUEUE" {job="postfix"} | json | line_format "{{.MESSAGE}}"
Рекомендация: Используйте Вариант 1 (journald) - проще и напрямую без rsyslog.
Настройка Exchange Send Connector: Set-SendConnector "Internet Send Connector" -SmartHosts 127.0.0.1 -DNSRoutingEnabled $false
Настраиваем nginx, чтобы трафик на 443 порт с нужных нам доменов шел сразу на VM Exchange
nano /etc/nginx/sites-available/proxy
# Upstream для Proxmox Web UI
upstream proxmox_backend {
server 127.0.0.1:8006;
}
# Upstream для Exchange виртуалки
upstream exchange_backend {
server 192.168.1.20:443; # Внутренний IP виртуалки с Exchange
}
map $http_host $backend_host {
default mail.gabelstapler-gebraucht.at;
mail.gabelstapler-gebraucht.at mail.gabelstapler-gebraucht.at;
autodiscover.gabelstapler-gebraucht.at autodiscover.gabelstapler-gebraucht.at;
mail.gabelstapler-zentrum.de mail.gabelstapler-gebraucht.at;
autodiscover.gabelstapler-zentrum.de autodiscover.gabelstapler-gebraucht.at;
mail.gabelstapler-zentrum.ch mail.gabelstapler-gebraucht.at;
autodiscover.gabelstapler-zentrum.ch autodiscover.gabelstapler-gebraucht.at;
}
# Proxmox VE Web UI
server {
listen 443 ssl;
http2 on;
server_name pve.symfio.net;
ssl_certificate /etc/letsencrypt/live/pve.symfio.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/pve.symfio.net/privkey.pem;
location / {
proxy_pass https://proxmox_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Proxmox WebSocket support
proxy_buffering off;
client_max_body_size 0;
proxy_connect_timeout 3600s;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
send_timeout 3600s;
}
}
server {
listen 443 ssl;
http2 on;
server_name mail.gabelstapler-gebraucht.at mail.gabelstapler-zentrum.de mail.gabelstapler-zentrum.ch;
ssl_certificate /etc/letsencrypt/live/autodiscover.gabelstapler-gebraucht.at/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/autodiscover.gabelstapler-gebraucht.at/privkey.pem;
location / {
proxy_pass https://exchange_backend;
proxy_http_version 1.1;
proxy_set_header Host $backend_host; # важно: Exchange должен видеть mail.gabelstapler-gebraucht.at
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
client_max_body_size 50M;
proxy_read_timeout 300s;
proxy_ssl_verify off; # игнорировать self-signed сертификат Exchange
proxy_ssl_session_reuse on;
}
}
server {
listen 443 ssl;
http2 on;
server_name autodiscover.gabelstapler-gebraucht.at autodiscover.gabelstapler-zentrum.de autodiscover.gabelstapler-zentrum.ch;
ssl_certificate /etc/letsencrypt/live/autodiscover.gabelstapler-gebraucht.at/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/autodiscover.gabelstapler-gebraucht.at/privkey.pem;
location / {
proxy_pass https://exchange_backend;
proxy_http_version 1.1;
proxy_set_header Host autodiscover.gabelstapler-gebraucht.at;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
client_max_body_size 50M;
proxy_ssl_verify off; # игнорировать self-signed сертификат Exchange
proxy_ssl_session_reuse on;
}
}
# HTTP → HTTPS redirection
server {
listen 80;
server_name mail.gabelstapler-gebraucht.at autodiscover.gabelstapler-gebraucht.at mail.gabelstapler-zentrum.de autodiscover.gabelstapler-zentrum.de mail.gabelstapler-zentrum.ch autodiscover.gabelstapler-zentrum.ch;
return 301 https://$host$request_uri;
}
Генерируем сертификат
certbot certonly --nginx --key-type rsa -d mail.gabelstapler-gebraucht.at -d autodiscover.gabelstapler-gebraucht.at
Включаем конфиг сайтов ln -s /etc/nginx/sites-available/proxy /etc/nginx/sites-enabled/proxy
Выделяем отдельный диск для БД, логов Exchange Заходим в control admintools -> Active Directory Domains and Trusts ->
В PowerShell
Install-WindowsFeature Server-Media-Foundation, NET-Framework-45-Features, RPC-over-HTTP-proxy, RSAT-Clustering, RSAT-Clustering-CmdInterface, RSAT-Clustering-Mgmt, RSAT-Clustering-PowerShell, WAS-Process-Model, Web-Asp-Net45, Web-Basic-Auth, Web-Client-Auth, Web-Digest-Auth, Web-Dir-Browsing, Web-Dyn-Compression, Web-Http-Errors, Web-Http-Logging, Web-Http-Redirect, Web-Http-Tracing, Web-ISAPI-Ext, Web-ISAPI-Filter, Web-Lgcy-Mgmt-Console, Web-Metabase, Web-Mgmt-Console, Web-Mgmt-Service, Web-Net-Ext45, Web-Request-Monitor, Web-Server, Web-Stat-Compression, Web-Static-Content, Web-Windows-Auth, Web-WMI, Windows-Identity-Foundation, RSAT-ADDS
Расширяем схему С установочного диска Exchange .\Setup.EXE /IAcceptExchangeServerLicenseTerms_DiagnosticDataON /PrepareSchema Если несколько Domain Controller, то делаем репликацию между доменами repadmin /syncall
Расширяем AD Имя организации: любое, которое хотим использовать в Exchange .\Setup.EXE /IAcceptExchangeServerLicenseTerms_DiagnosticDataON /PrepareAD /OrganizationName:"HeavilyArmedNerd" Должны создаться новые необходимые группы безопасности в AD: Microsoft Exchange Security Groups Если несколько Domain Controller, то делаем репликацию между доменами repadmin /syncall
Устнавливаем Exchange .\Setup.exe
На хосте с AD (только, если один домен или все домены будут расширяться за счет наращивания количества хостов - один домен = один контроллер домена = один хост) DNS Manager -> Forward Lookup Zones -> HeavilyArmedNerd.local (наш домен) -> New Host (A or AAAA) ->
_tcp -> Other New Records -> SRV ->_autodiscover_tcpПроверяем и заходим на Exchange Admin Center: https://mail.heavilyarmednerd.local/ecp Login: heavilyarmednerd.local\exadm Password: PASSWORD_exadm Language: English Time zone: Berlin
Если нужно меняем политики mail flow -> email address policies ->
Переносим БД, логи servers -> databases ->
Get-MailboxDatabase
Get-MailboxDatabase -Server SR-MAIL01
Get-MailboxDatabase -Identity "Mailbox Database 0284994124" | Set-MailboxDatabase -Name DB01
Move-DatabasePath -Identity DB -EdbFilePath D:\DB\EDB\db.edb -LogFolderPath D:\DB\LOGS\
servers -> databases -> ... Mount -> Yes
Добавить новый ящик Создаем в AD User'а, соответственно к тому названию ящика, который нам нужен. Например, farid.mika recipients -> mailboxes -> + ->
Get-ADForest | Set-ADForest -UPNSuffixes @{Add="gabelstapler-zentrum.ch","gabelstapler-gebraucht.at","gabelstapler-zentrum.de"}
# Name - какой угодно - для отображения используется, нагляднее сам домен
New-AcceptedDomain -Name "gabelstapler-zentrum.ch" -DomainName "gabelstapler-zentrum.ch" -DomainType Authoritative
New-AcceptedDomain -Name "gabelstapler-gebraucht.at" -DomainName "gabelstapler-gebraucht.at" -DomainType Authoritative
New-AcceptedDomain -Name "gabelstapler-zentrum.de" -DomainName "gabelstapler-zentrum.de" -DomainType Authoritative
# Внутренний домен по-умолчанию для Accepted Domains нормально
Get-AcceptedDomain | Format-Table Name, DomainName, DomainType, Default
# Верный и рабочий вариант. В GUI - не дает. %d - отображаемое имя, чтобы не ругался на одинаковые псевдонимы. Псевдонимы еще нужно отдельно задать равные SAM
New-EmailAddressPolicy -Name "GSZ Austria" `
-IncludedRecipients MailboxUsers `
-RecipientContainer "gsz.local/AT" `
-EnabledEmailAddressTemplates "SMTP:%d@gabelstapler-gebraucht.at" `
-Priority 1
New-EmailAddressPolicy -Name "GSZ Germany" `
-IncludedRecipients MailboxUsers `
-RecipientContainer "gsz.local/DE" `
-EnabledEmailAddressTemplates "SMTP:%d@gabelstapler-zentrum.de" `
-Priority 2
New-EmailAddressPolicy -Name "GSZ Switzerland" `
-IncludedRecipients MailboxUsers `
-RecipientContainer "gsz.local/CH" `
-EnabledEmailAddressTemplates "SMTP:%d@gabelstapler-zentrum.ch" `
-Priority 3
# Применить политику. Что делает - проходит акки и применяет. Exchange обычно просит нажать применить
Update-EmailAddressPolicy "GSZ Austria"
# Политики с OU=AT
New-EmailAddressPolicy -Name "GSZ Austria" -RecipientContainer "OU=AT,DC=gsz,DC=local" -IncludedRecipients UserMailbox -EnabledEmailAddressTemplates "SMTP:@gabelstapler-gebraucht.at" -Priority 1
New-EmailAddressPolicy -Name "GSZ Germany" -RecipientContainer "OU=DE,DC=gsz,DC=local" -IncludedRecipients UserMailbox -EnabledEmailAddressTemplates "SMTP:@gabelstapler-zentrum.de" -Priority 1
# С CustomAttribute1=AT
New-EmailAddressPolicy -Name "GSZ Austria" -RecipientFilter {RecipientType -eq "UserMailbox" -and CustomAttribute1 -eq "AT"} -EnabledEmailAddressTemplates "SMTP:@gabelstapler-gebraucht.at" -Priority 1
# Проверяем текущие (обычно 0, не установлено)
Get-OwaVirtualDirectory | fl *language*, *client*
# Устанавливаем немецкий по-умолчанию (форма входа тоже будет на немецком для клиентов)
Get-OwaVirtualDirectory | Set-OwaVirtualDirectory -DefaultClientLanguage 1031 -LogonAndErrorLanguage 1031
iisreset /stop
iisreset /start
# Смотрим региональные настройки и таймзону у пользователя (Если установить до входа первый раз в пользователя, то форма ввода языка и таймзоны не появится)
Get-MailboxRegionalConfiguration -Identity "sec@gabelstapler-zentrum.de"
# Устанавливаем немецкую (Deutsch (Deutschland) и (UTC+01:00) Амстердам, Берлин, Вена...)
Set-MailboxRegionalConfiguration -Identity "sec@gabelstapler-zentrum.de" -Language "de-DE" -TimeZone "W. Europe Standard Time" -DateFormat "dd.MM.yyyy" -TimeFormat "HH:mm" -LocalizeDefaultFolderName:$true
New-ADUser -Name "test" -SamAccountName "test-gsz.gsz.at" -UserPrincipalName "test-gsz@gabelstapler-gebraucht.at" -Path "OU=AT,DC=gsz,DC=local" -AccountPassword (ConvertTo-SecureString "x40o!06Oo" -AsPlainText -Force) -Enabled $true -PasswordNeverExpires $true -OtherAttributes @{extensionAttribute1="AT"}
New-ADUser -Name "info" -SamAccountName "info.gsz.at" -UserPrincipalName "info@gabelstapler-gebraucht.at" -Path "OU=AT,DC=gsz,DC=local" -AccountPassword (ConvertTo-SecureString "TktU05JFP5*8" -AsPlainText -Force) -Enabled $true -PasswordNeverExpires $true -OtherAttributes @{extensionAttribute1="AT"}
New-ADUser -Name "test" -SamAccountName "test-gsz.gsz.de" -UserPrincipalName "test-gsz@gabelstapler-zentrum.de" -Path "OU=DE,DC=gsz,DC=local" -AccountPassword (ConvertTo-SecureString "x40o!06Oo" -AsPlainText -Force) -Enabled $true -PasswordNeverExpires $true -OtherAttributes @{extensionAttribute1="DE"}
# Если прийдется вручную из-за сбоя в скрипте добавлять пользователей с email в Exchange, то вот готовая команда (задать email, sam-имя[до 20 символов], пароль) и все - готовый аккаунт
$acc = "wolfgang.buchholz@gabelstapler-zentrum.de"; $sam = "w.buchholz.gsz.de"; $pw = "39mg%q34N"; $acc -notmatch '^(.+)@(.+)\.([a-z]{2,})$'; $nm = $Matches[1]; New-ADUser -Name $nm -SamAccountName $sam -UserPrincipalName $acc -Path "OU=DE,DC=gsz,DC=local" -AccountPassword (ConvertTo-SecureString $pw -AsPlainText -Force) -Enabled $true -PasswordNeverExpires $true -OtherAttributes @{extensionAttribute1="DE"}; $sam = (Get-ADUser -Filter {UserPrincipalName -eq $acc}).SamAccountName; Enable-Mailbox -Identity $sam -Alias $sam; Set-Mailbox -Identity $acc -PrimarySmtpAddress $acc
Просмотр всех аккаунтов AD с CN/Name, pre-Windows 2000 логином, основным логином (user@domain) и PasswordNeverExpires
Get-ADUser -Filter * -Properties DisplayName,sAMAccountName,UserPrincipalName,PasswordNeverExpires | Select-Object Name, SamAccountName, UserPrincipalName, @{Name='PasswordNeverExpires';Expression={$_.PasswordNeverExpires}}
# Получить полную информацию о пользователе в AD (SamAccountName)
Get-ADUser "info.gsz.at" -Properties * | Format-List Name, SamAccountName, UserPrincipalName, EmailAddress, Enabled, PasswordExpired, LockedOut, DistinguishedName
# Просмотр включенных сервисов у пользователя (CAS)
Get-CASMailbox "info@gabelstapler-gebraucht.at" | Format-List *Enabled*
Enable-Mailbox -Identity "test-gsz@gabelstapler-gebraucht.at" -Alias "test-gsz.gsz.at"
Set-Mailbox -Identity "test-gsz@gabelstapler-gebraucht.at" -PrimarySmtpAddress
"test-gsz@gabelstapler-gebraucht.at"
Или SAM брать автоматически
$sam = (Get-ADUser -Filter {UserPrincipalName -eq "test-gsz@gabelstapler-gebraucht.at"}).SamAccountName
Enable-Mailbox -Identity $sam -Alias $sam
Set-Mailbox -Identity "test-gsz@gabelstapler-gebraucht.at" -PrimarySmtpAddress
"test-gsz@gabelstapler-gebraucht.at"
Enable-Mailbox -Identity "test-gsz@gabelstapler-gebraucht.at"
Set-Mailbox -Identity "test-gsz@gabelstapler-gebraucht.at" -PrimarySmtpAddress
"test-gsz@gabelstapler-gebraucht.at"
# или так
Set-Mailbox -Identity "test-gsz.gsz.at" -PrimarySmtpAddress "test-gsz@gabelstapler-gebraucht.at"
Enable-Mailbox -Identity "info"
Set-Mailbox -Identity "info@gabelstapler-zentrum.ch" -PrimarySmtpAddress
"info@gabelstapler-zentrum.ch"
Enable-Mailbox -Identity "info"
Set-Mailbox "info" -PrimarySmtpAddress "info@gabelstapler-zentrum.de"
Массово для всех пользователей в AT
Get-ADUser -Filter * -SearchBase "OU=AT,DC=gsz,DC=local" | ForEach-Object {
Enable-Mailbox -Identity $_.SamAccountName -Alias $_.SamAccountName
}
Массово для всех пользователей в DE
Get-ADUser -Filter * -SearchBase "OU=DE,DC=gsz,DC=local" | ForEach-Object {
Enable-Mailbox -Identity $_.SamAccountName -Alias $_.SamAccountName
}
Массово для всех пользователей в CH
Get-ADUser -Filter * -SearchBase "OU=CH,DC=gsz,DC=local" | ForEach-Object {
Enable-Mailbox -Identity $_.SamAccountName -Alias $_.SamAccountName
}
# Получить информацию о почтовом ящике
Get-Mailbox "info@gabelstapler-gebraucht.at" | Format-List Name, Alias, UserPrincipalName, PrimarySmtpAddress, SamAccountName
Get-MailboxFolderStatistics -Identity "kevin.nania@gabelstapler-zentrum.de" |
Where-Object {$_.Name -eq "Posteingang"} |
Select Name, ItemsInFolder, FolderSize
Get-MailboxFolderStatistics -Identity "kevin.nania@gabelstapler-zentrum.de" |
Where-Object {$_.Name -like "*Posteing*"} |
Select Name, ItemsInFolder, FolderSize
Get-MailboxFolderStatistics -Identity "kevin.nania@gabelstapler-zentrum.de" |
Where-Object {$_.Name -in @("Posteingang", "Gesendete Elemente", "Gel")} |
Select Name, ItemsInFolder, FolderSize
В OWA имеется глюк:
Отличное решение для админов. Минимум телодвижений New-GSZUser.ps1 (C:\scripts - добавляем папку в системную PATH)
#Requires -Modules ActiveDirectory
# Run from Exchange Management Shell or load snap-in:
# Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn
<#
.SYNOPSIS
Creates AD user and enables mailbox for GSZ domains.
.DESCRIPTION
Prompts for email, SamAccountName, and password.
Automatically determines OU and extensionAttribute1 from email TLD.
.EXAMPLE
.\New-GSZUser.ps1
#>
# --- Input ---
$acc = Read-Host "Email (UserPrincipalName)"
# --- Validation ---
if ($acc -notmatch '^.+@.+\.[a-z]{2,}$') {
Write-Error "Invalid email format: $acc"
exit 1
}
$nm = $acc.Split('@')[0]
$tld = $acc.Split('.')[-1] # de, ch, at
$suffix = ".gsz.$tld" # .gsz.de, .gsz.ch, .gsz.at
$maxPrefix = 20 - $suffix.Length # max chars for name part
# --- SamAccountName generation (smart truncation) ---
$prefix = $nm
if ($prefix.Length -gt $maxPrefix) {
$parts = $nm.Split('.')
if ($parts.Count -ge 2) {
# firstname.lastname → f.lastname
$short = $parts[0][0].ToString() + '.' + ($parts[1..($parts.Count-1)] -join '.')
if ($short.Length -le $maxPrefix) {
$prefix = $short
} else {
# f.lastname still too long → truncate lastname
$prefix = $short.Substring(0, $maxPrefix)
}
} else {
$prefix = $prefix.Substring(0, $maxPrefix)
}
}
$sam = "$prefix$suffix"
do {
$tooLong = $sam.Length -gt 20
if ($tooLong) {
Write-Host "SamAccountName '$sam' exceeds 20 chars ($($sam.Length))!" -ForegroundColor Red
$prefix = $prefix.Substring(0, $maxPrefix)
$sam = "$prefix$suffix"
}
$input = Read-Host "SamAccountName [$sam] (Enter=accept, or type prefix, e.g. '$($nm.Substring(0, [Math]::Min($nm.Length, 5)))')"
if ($input -ne '') {
$prefix = $input
$sam = "$prefix$suffix"
}
} while ($sam.Length -gt 20)
$tld = $tld.ToUpper() # DE, CH, AT for OU and attribute
# --- Password generation ---
$chars = 'abcdefghkmnpqrstuvwxyzABCDEFGHKMNPQRSTUVWXYZ23456789!@#$%&*'
$genPw = -join (1..14 | ForEach-Object { $chars[(Get-Random -Maximum $chars.Length)] })
$inputPw = Read-Host "Password [$genPw] (Enter=accept, or type your own)"
if ($inputPw -eq '') { $inputPw = $genPw }
$pw = ConvertTo-SecureString $inputPw -AsPlainText -Force
# --- Derived parameters ---
$ou = "OU=$tld,DC=gsz,DC=local"
$attr = @{ extensionAttribute1 = $tld }
# --- Summary ---
Write-Host "`n--- New User Summary ---" -ForegroundColor Cyan
Write-Host "Name: $nm"
Write-Host "UPN: $acc"
Write-Host "SamAccountName: $sam"
Write-Host "OU: $ou"
Write-Host "extensionAttribute1: $tld"
Write-Host "------------------------`n" -ForegroundColor Cyan
$confirm = Read-Host "Proceed? (Y/N)"
if ($confirm -ne 'Y') {
Write-Host "Cancelled." -ForegroundColor Yellow
exit 0
}
# --- Create AD User ---
New-ADUser `
-Name $nm `
-SamAccountName $sam `
-UserPrincipalName $acc `
-Path $ou `
-AccountPassword $pw `
-Enabled $true `
-PasswordNeverExpires $true `
-OtherAttributes $attr
Write-Host "AD user created." -ForegroundColor Green
# --- Enable Mailbox ---
$adUser = Get-ADUser -Filter { UserPrincipalName -eq $acc }
if (-not $adUser) {
Write-Error "User not found in AD after creation."
exit 1
}
Enable-Mailbox -Identity $adUser.SamAccountName -Alias $adUser.SamAccountName
Set-Mailbox -Identity $acc -PrimarySmtpAddress $acc
Write-Host "Mailbox enabled and SMTP set." -ForegroundColor Green
# Проверяем включем ли Scripting Agent
Get-CmdletExtensionAgent "Scripting Agent"
# Включаем
Enable-CmdletExtensionAgent "Scripting Agent"
iisreset /stop
iisreset /start
# Проверяем в какой папке делаем агента
$env:ExchangeInstallPath + "Bin\CmdletExtensionAgents\"
# c:\Program Files\Microsoft\Exchange Server\V15\Bin\CmdletExtensionAgents\
ScriptingAgentConfig.xml (создаем в этой папке)
<?xml version="1.0" encoding="utf-8" ?>
<Configuration version="1.0">
<Feature Name="MailboxProvisioning" Cmdlets="New-Mailbox">
<ApiCall Name="OnComplete">
<![CDATA[
$logPath = "C:\Program Files\Microsoft\Exchange Server\V15\Logging\ScriptingAgent\ScriptingAgent.log"
if ($succeeded) {
try {
$params = $provisioningHandler.UserSpecifiedParameters
$userAlias = $params["Alias"]
$upn = $params["UserPrincipalName"]
$securePassword = $params["Password"]
$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePassword)
$plainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - Processing: Alias=$userAlias, UPN=$upn" | Out-File $logPath -Append
Start-Sleep -Seconds 3
$searcher = New-Object DirectoryServices.DirectorySearcher
$searcher.Filter = "(mail=$upn)"
$result = $searcher.FindOne()
if ($result) {
$adUser = $result.GetDirectoryEntry()
$oldSam = $adUser.Properties["sAMAccountName"].Value
$uac = [int]$adUser.Properties["userAccountControl"].Value
$adUser.Properties["userAccountControl"].Value = $uac -bor 0x10000
if ($oldSam -ne $userAlias) {
$adUser.Properties["sAMAccountName"].Value = $userAlias
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - sAMAccountName: $oldSam -> $userAlias" | Out-File $logPath -Append
}
$noteText = "Password: $plainPassword | Created: $(Get-Date -Format 'yyyy-MM-dd HH:mm')"
$adUser.Properties["info"].Value = $noteText
$adUser.CommitChanges()
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - SUCCESS: $userAlias | PasswordNeverExpires=ON" | Out-File $logPath -Append
}
else {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - ERROR: User not found by mail=$upn" | Out-File $logPath -Append
}
Set-MailboxRegionalConfiguration -Identity $upn -Language "de-DE" -TimeZone "W. Europe Standard Time" -DateFormat "dd.MM.yyyy" -TimeFormat "HH:mm" -LocalizeDefaultFolderName:$true
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - Regional settings applied for $upn" | Out-File $logPath -Append
}
catch {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - ERROR: $($_.Exception.Message)" | Out-File $logPath -Append
}
}
else {
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - SKIPPED: succeeded=False" | Out-File $logPath -Append
}
]]>
</ApiCall>
</Feature>
</Configuration>
Все работает - правильно создается пользователь и сразу при входе имеет правильные настройки - не нужно форму заполнять Язык и Таймзона
# Создаем Send Connector для всех внешних доменов (*)
New-SendConnector `
-Name "Internet Send Connector" `
-Usage Internet `
-AddressSpaces "*" `
-SourceTransportServers "MAIL" `
-Enabled $true `
-DNSRoutingEnabled $true `
-UseExternalDNSServersEnabled $false `
-Fqdn "mail.gabelstapler-gebraucht.at"
# Проверяем и смотрим детали
Get-SendConnector "Internet Send Connector" | Format-List Name,Enabled,AddressSpaces,DNSRoutingEnabled,SourceTransportServers,Fqdn
# Если сам не отправил повторяем отправку в очередеи Unreachable (обычно автоматом подхватывает)
Retry-Queue mail\Unreachable
# Смотрим какие есть, порты, кто может подключаться
Get-ReceiveConnector | Format-List Name, Bindings, RemoteIPRanges, PermissionGroups
# Проверка службы
Get-Service MSExchangeTransport | Format-List Name,Status,StartType
# Проверить очередь сообщений
Get-Queue
# Детали застрявших сообщений
Get-Queue | Where-Object {$_.MessageCount -gt 0} | Format-List
# Посмотреть конкретные сообщения в очереди
Get-Message -Queue "имя_очереди-Identity" | Format-List
# Cмотрим: Очередь Submission - сообщения ждут отправки, Очередь Unreachable - не могут доставить, Status Retry - пытается переотправить, LastError - покажет причину
# Список Send Connectors
Get-SendConnector | Format-List Name, Enabled, AddressSpaces, SmartHosts, SourceTransportServers
# Детали конкретного коннектора
Get-SendConnector "Default Send Connector" | Format-List *
# Важные параметры: Enabled: True - должен быть включен, AddressSpaces: {*} - должен быть * для отправки в интернет, SmartHosts: {} - должно быть пусто (прямая отправка) или SMTP relay если используешь, SourceTransportServers - должен быть твой сервер MAIL
🚫 Конфигурация ниже неверная, команды верные ⚠️ Важные замечания:
# Проверяем, чтобы ExternalUrl был https://mail.gabelstapler-gebraucht.at", какие есть методы аутентификации - Basic Authentication должна быть включена - для Интернета она единственно возможная, т.е. ExternalClientAuthenticationMethod: Basic (не Ntlm!), IISAuthenticationMethods должен содержать Basic, MAPI/HTTP должен быть включен, формат логина: info@gabelstapler-gebraucht.at (а не GSZ\info)
# Outlook Anywhere (RPC over HTTP)
Get-OutlookAnywhere | Format-List Server,ExternalHostname,InternalHostname,ExternalClientAuthenticationMethod,InternalClientAuthenticationMethod,IISAuthenticationMethods,SSLOffloading
Get-OutlookAnywhere -Server MAIL | Format-List *
# Если OutlookAnywhere нет - создаем
New-OutlookAnywhere -Server MAIL `
-ExternalHostname "mail.gabelstapler-gebraucht.at" `
-InternalHostname "mail.gsz.local" `
-ExternalClientAuthenticationMethod Basic `
-InternalClientAuthenticationMethod Ntlm `
-ExternalClientsRequireSsl $true `
-InternalClientsRequireSsl $true `
-DefaultAuthenticationMethod Basic
# MAPI/HTTP Configuration
Get-OrganizationConfig | Format-List MapiHttpEnabled
# Если False - включаем
Set-OrganizationConfig -MapiHttpEnabled $true
Get-MapiVirtualDirectory | Format-List Server,ExternalUrl,InternalUrl,IISAuthenticationMethods
# Если нет Basic, то включаем
Set-MapiVirtualDirectory -Identity "MAIL\mapi (Default Web Site)" `
-IISAuthenticationMethods @('Basic','Ntlm')
# Web Services (EWS)
Get-WebServicesVirtualDirectory | Format-List Server,ExternalUrl,InternalUrl,InternalNTLMAuthenticationEnabled,ExternalAuthenticationMethods,InternalAuthenticationMethods,BasicAuthentication,WindowsAuthentication
# OWA Authentication
Get-OwaVirtualDirectory | Format-List Server,ExternalUrl,InternalUrl,BasicAuthentication,WindowsAuthentication,FormsAuthentication
# ActiveSync
Get-ActiveSyncVirtualDirectory | Format-List Server,ExternalUrl,InternalUrl,BasicAuthEnabled,WindowsAuthEnabled
# AutoDiscover Service
Get-ClientAccessService | Format-List Name,AutoDiscoverServiceInternalUri
# OAB (Offline Address Book)
Get-OabVirtualDirectory | Format-List Identity,WindowsAuthentication
Если True, выключаем
Set-OabVirtualDirectory -Identity "MAIL\OAB (Default Web Site)" -WindowsAuthentication $false
# Restart IIS
iisreset /stop
Start-Sleep -Seconds 3
iisreset /start
# 🧪 Тест Autodiscover с детальным выводом на машине с Outlook)
Test-OutlookConnectivity -ProbeIdentity "OutlookMapiHttp\MAIL" `
-MailboxCredential (Get-Credential) `
-Verbose
# Проверка autodiscover с подробным выводом, подключением и получением xml-файла
curl -v -u "info@gabelstapler-gebraucht.at:TktU05JFP5*8" -H "Content-Type: text/xml" --data-binary @autodiscover.xml https://autodiscover.gabelstapler-gebraucht.at/Autodiscover/Autodiscover.xml
# отправляемый autodiscover.xml
<?xml version="1.0" encoding="utf-8"?>
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
<Request>
<EMailAddress>info@gabelstapler-gebraucht.at</EMailAddress>
<AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
</Request>
</Autodiscover>
# Проверка autodiscover, если есть перенаправление
# Проверить HTTP (lowercase)
curl -I http://gabelstapler-gebraucht.at/autodiscover/autodiscover.xml
# Ожидается: 301 → https://autodiscover.gabelstapler-gebraucht.at/autodiscover/autodiscover.xml
# Проверить HTTPS (lowercase)
curl -I https://gabelstapler-gebraucht.at/autodiscover/autodiscover.xml
# Ожидается: 301 → https://autodiscover.gabelstapler-gebraucht.at/autodiscover/autodiscover.xml
# Проверить HTTPS (capitalize)
curl -I https://gabelstapler-gebraucht.at/Autodiscover/Autodiscover.xml
# Ожидается: 301 → https://autodiscover.gabelstapler-gebraucht.at/Autodiscover/Autodiscover.xml
# Другой вариант использовать Microsoft Remote Connectivity Analyzer
# https://testconnectivity.microsoft.com/
# Exchange Server → Exchange ActiveSync → Обычная проверка
# Вводим
# - Email: info@gabelstapler-gebraucht.at
# - Password: пароль
# Запускаем тест и раскрываем весь вывод и смотрим ошибки
# Настраиваем Outlook Anywhere (RPC over HTTPS)
Set-OutlookAnywhere -Identity "MAIL\Rpc (Default Web Site)" `
-ExternalHostname "mail.gabelstapler-gebraucht.at" `
-InternalHostname "mail.gsz.local" `
-ExternalClientAuthenticationMethod Basic `
-InternalClientAuthenticationMethod Ntlm `
-ExternalClientsRequireSsl $true `
-InternalClientsRequireSsl $true
# Включаем Basic Authentication на Virtual Directories
# Autodiscover
Set-AutodiscoverVirtualDirectory -Identity "MAIL\Autodiscover (Default Web Site)" `
-BasicAuthentication $true `
-WindowsAuthentication $true
# EWS
Set-WebServicesVirtualDirectory -Identity "MAIL\EWS (Default Web Site)" `
-BasicAuthentication $true `
-WindowsAuthentication $true
# MAPI
Set-MapiVirtualDirectory -Identity "MAIL\mapi (Default Web Site)" `
-IISAuthenticationMethods @('Basic','Ntlm','Negotiate')
# OAB
Set-OabVirtualDirectory -Identity "MAIL\OAB (Default Web Site)" `
-BasicAuthentication $true `
-WindowsAuthentication $true
# Restart IIS
iisreset /stop
Start-Sleep -Seconds 3
iisreset /start
# Проверяем конфигурацию
# Outlook Anywhere
Get-OutlookAnywhere | Format-List Server,ExternalHostname,ExternalClientAuthenticationMethod,ExternalClientsRequireSsl
# Autodiscover
Get-AutodiscoverVirtualDirectory | Format-List Server,ExternalUrl,BasicAuthentication
# EWS
Get-WebServicesVirtualDirectory | Format-List Server,ExternalUrl,BasicAuthentication
# При дальнейших попытках использовать не Basic аутентификацию, делаем
# По сути ВЕЗДЕ нужно проставить только Basic аутентификацию, иначе лезет с доменной (она первой всегда идет у клиента) сохраняет ее и бесконечно пытается снова и снова. Также на машине,где уже это было стоит удалить доменный ключ (который конечно не доменный - а тот, который появляется с запросом введи пароль)
Set-AutodiscoverVirtualDirectory -Identity "MAIL\Autodiscover (Default Web Site)" `
-BasicAuthentication $true `
-WindowsAuthentication $false `
-DigestAuthentication $false
Set-MapiVirtualDirectory -Identity "MAIL\mapi (Default Web Site)" `
-IISAuthenticationMethods @('Basic')
# OAB (Offline Address Book)
Get-OabVirtualDirectory | Format-List Identity,WindowsAuthentication
Если True, выключаем
Set-OabVirtualDirectory -Identity "MAIL\OAB (Default Web Site)" -WindowsAuthentication $false
# Проверка
Get-AutodiscoverVirtualDirectory | Format-List Server,BasicAuthentication,WindowsAuthentication,DigestAuthentication
Get-MapiVirtualDirectory | Format-List Server,IISAuthenticationMethods
# Restart IIS
iisreset /stop
Start-Sleep -Seconds 3
iisreset /start
# Смотрим на клиенте ключи
cmdkey /list | Select-String -Pattern "gabelstapler" -Context 1,1
И удаляем доменные (которые не рабочие)
# cmdkey /delete:Domain:target=mail.gabelstapler-gebraucht.at
Проверяем наличие RPC-компонента и его файлов
Get-WindowsFeature RPC-over-HTTP-proxy | Select-Object *
# Проверяем, что IIS знает про правильный путь
& "$env:SystemRoot\System32\inetsrv\appcmd.exe" list config "Default Web Site/Rpc" -section:system.webServer/handlers
# Проверяем IsapiCgiRestriction (должен разрешать RpcProxy.dll)
& "$env:SystemRoot\System32\inetsrv\appcmd.exe" list config -section:system.webServer/security/isapiCgiRestriction | Select-String -Pattern "RpcProxy"
# Тест что файл доступен из IIS
Test-Path "C:\Windows\System32\RpcProxy\RpcProxy.dll"
Get-Item "C:\Windows\System32\RpcProxy\RpcProxy.dll" | Format-List FullName,Length,LastWriteTime
Если RPC-компонента нет, то устанавливаем его
Install-WindowsFeature RPC-over-HTTP-proxy -Restart
iisreset /restart
Test-Path "C:\Windows\System32\RpcProxy\RpcProxy.dll"
Get-Item "C:\Windows\System32\RpcProxy\RpcProxy.dll" | Format-List FullName,Length,LastWriteTime
# Проверяем коннекс к RPC
curl -v -u "info@gabelstapler-gebraucht.at:TktU05JFP5*8" \
--request RPC_IN_DATA \
https://mail.gabelstapler-gebraucht.at/rpc/rpcproxy.dll
# Должно вернуть HTTP 401 или успешный ответ (не 404 и не 500)
# Также проверяем RPC в Microsoft Connectivity Analyzer
# - https://testconnectivity.microsoft.com/
# - Exchange Server → Outlook Connectivity
# Проверяем
Get-MailboxDatabase | FL Name,*quota*
# Устанавливаем
Set-MailboxDatabase "Mailbox_DB_GSZ" `
-IssueWarningQuota Unlimited `
-ProhibitSendQuota Unlimited `
-ProhibitSendReceiveQuota Unlimited
# Если хочется лимитировать, то тогда так
Set-MailboxDatabase "Mailbox_DB_GSZ" `
-IssueWarningQuota 45GB `
-ProhibitSendQuota 48GB `
-ProhibitSendReceiveQuota 50GB
Для того, чтобы не блочили нужно настроить SPF, DMARC, DKIM
В DNS для gabelstapler-gebraucht.at Type: TXT Name: @ (или пустое или gabelstapler-gebraucht.at) Value: v=spf1 ip4:195.201.108.88 ~all TTL: 3600 (или 1 hour) example.com. IN TXT "v=spf1 mx -all" В DNS для mail.gabelstapler-gebraucht.at mailserver.example.com. IN TXT "v=spf1 a -all" Проверка dig TXT gabelstapler-gebraucht.at +short Должно показать: "v=spf1 ip4:195.201.108.88 ~all"
Reverse DNS (PTR запись) настраивается через Hetzner Пишем в поддержку: Hello, dear support Please configure Reverse DNS (PTR record) for IP 195.201.108.88: 195.201.108.88 → mail.gabelstapler-gebraucht.at Sincerely, Kirill Udalov Проверка dig -x 195.201.108.88 +short Должно показать: mail.gabelstapler-gebraucht.at.
Type: TXT Name: _dmarc.gabelstapler-gebraucht.at Value: v=DMARC1; p=none; rua=mailto:postmaster@gabelstapler-gebraucht.at или rua=mailto:dmarc@gabelstapler-gebraucht.at Нужно создать такой почтовый ящик, иначе при нормальном антиспаме будет отбивать почту на несуществующий ящик и это правильно. или убрать rua= из DMARC (не буду получать отчеты): _dmarc.gabelstapler-gebraucht.at IN TXT "v=DMARC1; p=none"
Встроенный DKIM Microsoft убрала из Exchange (теперь только в O365) https://github.com/Pro/dkim-exchange/releases Качаем последнюю версию. Распаковываем. Запускаем конфигуратор, выбираем версию, Install. Далее он сам устанавливает, прописывает в Transport Agent себя, отдельным агентом Exchange DkimSigner и перезапускает сервисы. Программа Configuration.DkimSigner.exe появляется в папке C:\Program Files\Exchange DkimSigner. С помощью нее можно управлять агентом, поменять настройки DKIM, сгенерировать ключ. Нужно сгенерировать ключ (все остальное по-умолчанию): -Add -Domain name -Selector: default -Generate new key -Check -Copy to clipboard Сделать DNS-запись: Name: default._domainkey.gabelstapler-gebraucht.at Value: v=DKIM1; k=rsa; p=MIIBIjANBgkqhki... Проверяем:
dig +short TXT default._domainkey.gabelstapler-gebraucht.at

# Дополнительные команды и
# Распаковываем
Expand-Archive -Path C:\Users\msexadm\Downloads\ExchangeDkimSigner-3.x.x.zip -DestinationPath "C:\Program Files\Exchange DkimSigner"
# Проверяем установку
Get-TransportAgent
# Должен появиться: Exchange DkimSigner
# Включаем Agent
Enable-TransportAgent -Identity "Exchange DkimSigner"
# Перезапускаем Transport Service
Restart-Service MSExchangeTransport
# Получить PUBLIC KEY для DNS
Get-Content "C:\Program Files\Exchange DkimSigner\keys\gabelstapler-gebraucht.at.pem"
# C:\Program Files\Exchange DkimSigner\settings.xml
# XML для понимания структуры, но лучше автоматическая генерация
<?xml version="1.0" encoding="utf-8"?>
<Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Loglevel>3</Loglevel>
<SigningAlgorithm>RsaSha256</SigningAlgorithm>
<HeaderCanonicalization>Simple</HeaderCanonicalization>
<BodyCanonicalization>Simple</BodyCanonicalization>
<HeadersToSign>
<string>From</string>
<string>Subject</string>
<string>To</string>
<string>Date</string>
<string>MessageId</string>
</HeadersToSign>
<Domains>
<DomainElement>
<Domain>gabelstapler-gebraucht.at</Domain>
<Selector>default</Selector>
<PrivateKeyFile>gabelstapler-gebraucht.at.pem</PrivateKeyFile>
</DomainElement>
</Domains>
</Settings>
Включить антиспам-агенты
& $env:ExchangeInstallPath\Scripts\Install-AntiSpamAgents.ps1
Рестартуем транспорт
Restart-Service MSExchangeTransport
# Настройка антиспам фильтров
# Content Filter (основной)
Set-ContentFilterConfig -Enabled $true -SCLRejectEnabled $true -SCLRejectThreshold 7 -SCLDeleteEnabled $true -SCLDeleteThreshold 9
# Connection Filter (RBL) - по сути в Exchange - НЕ РАБОТАЕТ - передано все во внешние релеи: облачный за деньги, Microsoft Edge или postfix
Set-IPBlockListProvidersConfig -Enabled $true
Add-IPBlockListProvider -Name "Spamhaus" -LookupDomain "zen.spamhaus.org" -Enabled $true
Add-IPBlockListProvider -Name "SpamCop" -LookupDomain "bl.spamcop.net" -Enabled $true
Add-IPBlockListProvider -Name "Barracuda" -LookupDomain "b.barracudacentral.org" -Enabled $true
# Проверяем
Get-IPBlockListProvider
# Sender Filter
Set-SenderFilterConfig -Enabled $true -BlankSenderBlockingEnabled $true
# SenderId - мягкая проверка - Exchange добавляет заголовок X-MS-Exchange-Organization-SenderIdResult: Fail, но если на входе postfix с нормальным антиспамом, то лучше это старье отключить
# мягкая проверка
Set-SenderIdConfig -SpoofedDomainAction StampStatus
Set-SenderIdConfig -TempErrorAction StampStatus
# или вообще отключаем
Set-SenderIdConfig -Enabled $false
# Проверяем какие фильтры включены и что включено
Get-TransportAgent
Get-ContentFilterConfig | Format-Table Name,Enabled
Get-SenderFilterConfig | Format-Table Name,Enabled
Get-SenderIDConfig | Format-Table Name,Enabled
Get-SenderReputationConfig | Format-Table Name,Enabled
Get-ContentFilterConfig | Format-List *Enabled,RejectionResponse,*Postmark*,Bypassed*,Quarantine*
Get-SenderFilterConfig | Format-List *Enabled,*Block*
Get-SenderIDConfig | Format-List *Enabled*,*Action,Bypassed*
Get-SenderReputationConfig | Format-List *Enabled*,*Proxy*,*Block*,*Ports*
Get-ContentFilterPhrase | Format-Table -Auto Influence,Phrase
Get-IPBlockListConfig | Format-List *Enabled,*Response
# Проверяем права у коннекторов на получение
Get-ReceiveConnector | Select-Object Name, PermissionGroups
Get-ReceiveConnector | Format-Table Name, PermissionGroups -AutoSize
Get-ReceiveConnector | Format-List *
Прежде чем менять права или забирать их у кого-то смотрим порты и думаем
Get-ReceiveConnector
# Отключать AnonymousUsers НЕЛЬЗЯ на Default Frontend MAIL - это 25 порт, иначе никто из почтовых серверов не сможет доставлять нам почту (они не аутентифицируются на Exchange сервере) на relay, остальные оставляем
❌ Get-ReceiveConnector "Default Frontend MAIL" | Set-ReceiveConnector -PermissionGroups ExchangeServers,ExchangeLegacyServers
# Включить TLS
Get-ReceiveConnector | Select-Object Name, RequireTLS, AuthMechanism
Get-ReceiveConnector | Select-Object Name, RequireTLS, AuthMechanism, TlsCertificateName | Format-List
Set-ReceiveConnector "Default Frontend *" -RequireTLS $true
# Ограничить размер писем (Если необходимо)
Set-TransportConfig -MaxReceiveSize 25MB -MaxSendSize 25MB
Встроенные IP Block List (аналог fail2ban) Заблокировать IP после брутфорса
Add-IPBlockListEntry -IPAddress 192.168.1.100
Add-IPBlockListEntry -IPRange 192.168.1.0/24
Посмотреть список
Get-IPBlockListEntry
https://github.com/DigitalRuby/IPBan Мониторит Failed Login Events (Event ID 4625) Автоматически добавляет правила в Windows Firewall
$ProgressPreference = 'SilentlyContinue'; [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/DigitalRuby/IPBan/master/IPBanCore/Windows/Scripts/install_latest.ps1'))
Start-Service IPBan
<appSettings>
<add key="FailedLoginAttemptsBeforeBan" value="5"/>
<add key="BanTime" value="01:00:00"/> <!-- 1 час -->
<add key="ExpireTime" value="1.00:00:00"/> <!-- 1 день -->
<add key="ProcessToRunOnBan" value=""/> <!-- опционально -->
</appSettings>
Есть веб-интерфейс Включить в конфигурации: C:\Program Files\IPBan\DigitalRuby.IPBan.dll.config или ipban.config
<appSettings>
<!-- Включить HTTP сервер -->
<add key="UseHttpServer" value="true"/>
<add key="HttpServerPort" value="52664"/>
<!-- Опционально: авторизация -->
<add key="HttpServerApiKey" value="your-secret-key-here"/>
</appSettings>
Restart-Service IPBan
Разрешаем в файерволе коннекты
New-NetFirewallRule -DisplayName "IPBan HTTP" -Direction Inbound -LocalPort 52664 -Protocol TCP -Action Allow
Get-NetFirewallRule -DisplayName "*IPBan*" | Select DisplayName, Enabled
Проверка, что слушает на порту (должен быть протокол TCP)
netstat -ano | findstr 52664
Get-NetTCPConnection -LocalPort 52664 -ErrorAction SilentlyContinue
Доступ: http://localhost:52664 или http://ip:52664 Задаем API ключ - это пароль для доступа к веб-интерфейсу IPBan. файл конфигурации ipban.config:
<appSettings>
<add key="UseHttpServer" value="true"/>
<add key="HttpServerPort" value="52664"/>
<!-- Любая строка - это твой пароль -->
<add key="HttpServerApiKey" value="MySecretPassword123!"/>
</appSettings>
При открытии http://localhost:52664 появится окно авторизации: Username: пустое, Password: МойПароль-APIКлюч
Invoke-RestMethod -Uri "http://localhost:52664/api/banned" -Headers @{ "Authorization" = "МойПароль-APIКлюч" }
Если API-ключ и пароль не нужен
<appSettings>
<add key="UseHttpServer" value="true"/>
<add key="HttpServerPort" value="52664"/>
<!-- Без HttpServerApiKey - доступ без пароля -->
</appSettings>
C:\Program Files\IPBan\logfile.txt C:\ProgramData\IPBan\logfile.txt Логи в реальном времени:
Get-Content "C:\Program Files\IPBan\logfile.txt" -Wait -Tail 50
Get-Content "C:\Program Files\IPBan\logfile.txt" | Select-String "bann"
Get-Content "C:\Program Files\IPBan\logfile.txt" | Select-String "bann" | Select-Object -Last 50
# Список всех блокировок IPBan
# Нормальным списком без ...
Get-NetFirewallRule -DisplayName "IPBan*" | ForEach-Object {
$filter = $_ | Get-NetFirewallAddressFilter
Write-Host "`n=== $($_.DisplayName) (Enabled: $($_.Enabled)) ===" -ForegroundColor Cyan
$filter.RemoteAddress | ForEach-Object { Write-Host " $_" }
}
# Разблокировать
$ipToUnblock = "192.168.1.100"
Get-NetFirewallRule -DisplayName "IPBan*" | ForEach-Object {
$filter = $_ | Get-NetFirewallAddressFilter
if ($filter.RemoteAddress -contains $ipToUnblock) {
Write-Host "Found in: $($_.DisplayName)" -ForegroundColor Yellow
$newIPs = $filter.RemoteAddress | Where-Object { $_ -ne $ipToUnblock }
Set-NetFirewallAddressFilter -InputObject $filter -RemoteAddress $newIPs
Write-Host "Removed $ipToUnblock from $($_.DisplayName)" -ForegroundColor Green
}
}
Get-NetFirewallRule -DisplayName "IPBan*" | Get-NetFirewallAddressFilter
# Детальнее
Get-NetFirewallRule -DisplayName "IPBan*" | ForEach-Object {
$filter = $_ | Get-NetFirewallAddressFilter
[PSCustomObject]@{
Name = $_.DisplayName
Enabled = $_.Enabled
BlockedIPs = ($filter.RemoteAddress -join ", ")
}
} | Format-Table -AutoSize
Get-NetFirewallRule -DisplayName "IPBan*" | ForEach-Object {
$rule = $_
$filter = $_ | Get-NetFirewallAddressFilter
[PSCustomObject]@{
Name = $rule.DisplayName
Enabled = $rule.Enabled
RemoteAddress = $filter.RemoteAddress
}
} | Format-Table -AutoSize
# Экспорт в .csv
Get-NetFirewallRule -DisplayName "IPBan*" | Get-NetFirewallAddressFilter | Select RemoteAddress | Export-Csv -Path "C:\temp\ipban-blocked.csv" -NoTypeInformation
unblock-ip.cmd (для запуска под пользователем с привилегиями админа, если нужно для обычного пользователя - один раз вводим пароль, далее он запоминает)
@echo off
if "%1"=="" (
echo Usage: unblock-ip.cmd IP_ADDRESS
exit /b 1
)
echo Starting unblock process for IP: %1
echo.
start /wait runas /user:GSZ\msexadm /savecred "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\scripts\unblock-ip.ps1 -IP %1"
timeout /t 2 /nobreak >nul
echo.
echo ========================================
echo Result:
echo ========================================
type C:\scripts\unblock-ip.log
echo.
unblock-ip.ps1 (сам скрипт)
param([Parameter(Mandatory=$true)][string]$IP)
$logFile = "C:\Scripts\unblock-ip.log"
function Write-Log {
param($Message, $Color = "White")
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logMessage = "[$timestamp] $Message"
Add-Content -Path $logFile -Value $logMessage
Write-Host $Message -ForegroundColor $Color
}
# Очистить старый лог
"" | Out-File $logFile -Force
Write-Log "========================================" "Cyan"
Write-Log "Unblocking IP: $IP" "Cyan"
Write-Log "========================================" "Cyan"
$found = $false
try {
Get-NetFirewallRule -DisplayName "IPBan*" | ForEach-Object {
$filter = $_ | Get-NetFirewallAddressFilter
if ($filter.RemoteAddress -contains $IP) {
Write-Log "`nFound in: $($_.DisplayName)" "Yellow"
$newIPs = $filter.RemoteAddress | Where-Object { $_ -ne $IP }
Write-Log "Removing IP from firewall rule..." "Yellow"
Set-NetFirewallAddressFilter -InputObject $filter -RemoteAddress $newIPs
Write-Log "Successfully removed $IP from $($_.DisplayName)" "Green"
$found = $true
}
}
if (-not $found) {
Write-Log "`nIP $IP not found in any IPBan rules" "Red"
}
} catch {
Write-Log "`nERROR: $_" "Red"
Write-Log $_.Exception.Message "Red"
}
Write-Log "`n========================================" "Cyan"
Write-Log "Done!" "Cyan"
Write-Log "========================================" "Cyan"
по-умолчанию, 1 день (01:00:00:00) BanTime - на какое время банить ExpireTime - время хранения IP адреса, который имел неудачные логины. Это больше, чем окно в fail2ban. Если IP разблокирован, то ExpireTime может продолжать хранить в БД этот IP и помнить о неудачных попытках. После попыток вновь сделать логин фэйл IP адрес сразу банится и банится уже на большее время.
Get-Content "C:\Program Files\IPBan\ipban.config" | Select-String "BanTime|ExpireTime"
или
Get-Content "C:\ProgramData\IPBan\ipban.config" | Select-String "BanTime"
```Формат BanTime
```xml wrap
<!-- Формат: DD:HH:MM:SS или просто часы -->
<add key="BanTime" value="01:00:00:00"/> <!-- 1 день -->
<add key="BanTime" value="7:00:00:00"/> <!-- 7 дней -->
<add key="BanTime" value="00:01:00:00"/> <!-- 1 час -->
<add key="BanTime" value="00:00:30:00"/> <!-- 30 минут -->
<add key="ExpireTime" value="15:00:00:00"/> <~-- 15 дней -->
Через BanFile (постоянная блокировка)
<!-- Постоянно заблокированные IP (не разблокируются по таймеру) -->
<add key="BanFile" value="C:\ProgramData\IPBan\banlist.txt"/>
IPBan хранит данные в SQLite C:\ProgramData\IPBan\ipban.sqlite Установить SQLite
choco install sqlite
Запрос
sqlite3.exe "C:\ProgramData\IPBan\ipban.sqlite" "SELECT * FROM IPAddresses WHERE BanDate IS NOT NULL ORDER BY BanDate DESC LIMIT 100;"
# Получить список заблокированных IP
Invoke-RestMethod -Uri "http://localhost:52664/api/banned" -Headers @{"Authorization"="your-secret-key-here"}
# Разблокировать IP
Invoke-RestMethod -Uri "http://localhost:52664/api/unban/1.2.3.4" -Method POST -Headers @{"Authorization"="your-secret-key-here"}
IPBan пишет в Event Log
Последние события IPBan
Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName='IPBan'} -MaxEvents 100 | Select TimeCreated, Message | Format-Table -AutoSize
Только блокировки
Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName='IPBan'} -MaxEvents 100 | Where-Object {$_.Message -like "*ban*"}
loki.source.file "ipban" {
targets = [
{__path__ = "C:/Program Files/IPBan/logfile.txt"},
]
forward_to = [loki.write.default.receiver]
}
Делаем сертификат на несколько доменов в одном сертификате, обязательно тип rsa (для Exchange, но и в nginx будет прекрасно работать) certbot certonly --nginx --key-type rsa -d mail.gabelstapler-gebraucht.at -d autodiscover.gabelstapler-gebraucht.at Конвертируем сертификат в pfx-формат (пароль обязателен, иначе не даст - любой самый простой можно) openssl pkcs12 -export -inkey /etc/letsencrypt/live/autodiscover.gabelstapler-gebraucht.at/privkey.pem -in /etc/letsencrypt/live/autodiscover.gabelstapler-gebraucht.at/fullchain.pem -out /tmp/mail-gs.pfx -name "mail.gabelstapler-gebraucht.at" Импортируем pfx из certbot'овского хоста на хост Exchange через GUI Центр администрирования Exchange (ecp) Серверы -> сертификаты -> ... -> Импорт сертификата Exchange -> выбираем наш сервер Exchange -> SMTP, IMAP -> При запросе соглашаемся заменить старый сертификат
Смотрим, что добавились сервисы и он в хранилище, также видим самоподписанный или подписанный авторизированным центром сертификации Get-ExchangeCertificate | Select-Object Subject, Thumbprint, Services, IsSelfSigned | Format-Table -AutoSize Только сервисы сертификаты, которые включены на сервис SMTP и когда закончится Get-ExchangeCertificate | Where-Object {$.Services -like "SMTP"} | Select-Object Subject, Thumbprint, NotAfter, Services, IsSelfSigned | Format-Table -AutoSize Просмотр конкретного сертификата $oldCert = Get-ExchangeCertificate -Thumbprint "ECFE72F9598D0D4C95ED5E24E842886C137392E5" oldCert | Format-List Subject, Services, DnsNameList Или так Get-ExchangeCertificate | Where-Object {.Subject -like "mail.gabelstapler-gebraucht.at"} | Format-List Subject, Thumbprint, DnsNameList, NotAfter Включить сертификат на сервис IIS Enable-ExchangeCertificate -Thumbprint "ECFE72F9598D0D4C95ED5E24E842886C137392E5" -Services IIS -Force Чтобы поставить сертификат на конкретный коннектор делаем Get-ReceiveConnector $TLSCert = Get-ExchangeCertificate -Thumbprint "038B1DC5B9BE6E87C90F416E83C3980BEE77A15F" (($TLSCert.Subject)" Ставим на 25 и 587 порты на коннекторы получения Set-ReceiveConnector "Default Frontend MAIL" -TlsCertificateName $TLSCertName Set-ReceiveConnector "Client Frontend MAIL" -TlsCertificateName $TLSCertName Проверяем Get-ReceiveConnector | Format-List Identity, Bindings, Enabled, TlsCertificateName или Get-ReceiveConnector | Format-Table Identity, Bindings, Enabled, TlsCertificateName Теперь ставим на 25 порт на коннектор отправки Get-SendConnector | ft Identity, AddressSpaces, TlsCertificateName, Enabled Set-SendConnector "Internet Send Connector" -TlsCertificateName $TLSCertName Get-SendConnector | Format-Table Name, Port, TlsCertificateName, TlsAuthLevel Restart-Service MSExchangeTransport
# Глобальные ограничения по транспорту
Get-TransportConfig | Format-List MaxSendSize,MaxReceiveSize
# Ставим по транспорту 150Мб на отправку и прием
Set-TransportConfig -MaxSendSize 150MB -MaxReceiveSize 150MB
# Ограничения на получения разных типов сообщений
Get-ReceiveConnector | Format-List Name,MaxMessageSize
# На получение глобальное установить 150Мб по всем типам
Get-ReceiveConnector | Set-ReceiveConnector -MaxMessageSize 150MB
# Ограничения на отправку
Get-SendConnector | Format-List Name,MaxMessageSize
# Установить ограничения на отправку в 150Mб
Set-SendConnector "Internet Send Connector" -MaxMessageSize 150MB
# Ограничения по конкретному ящику
Get-Mailbox "erich.janusch@gabelstapler-gebraucht.at" | Format-List MaxSendSize,MaxReceiveSize
Restart-Service MSExchangeTransport
# Если импортируются письма, то и эту еще службу
Restart-Service MSExchangeMailboxReplication
# Смотрим триал или нет
Get-ExchangeServer | Format-List Name, Edition, AdminDisplayVersion, IsExchangeTrialEdition
# Ставим ключ
Set-ExchangeServer -Identity "MAIL" -ProductKey "YCQY7-BNTF6-R337H-69FGX-P39TY"
# Рестартуем сервисы
Restart-Service MSExchangeIS
Restart-Service MSExchangeServiceHost
# Проверяем
Get-ExchangeServer | Format-List Name, Edition, AdminDisplayVersion, IsExchangeTrialEdition
Входящая почта:
Исходящая почта:
# Включить Protocol Logging
Get-ReceiveConnector "Default Frontend MAIL" | Format-List ProtocolLoggingLevel
Get-ReceiveConnector "Client Frontend MAIL" | Format-List ProtocolLoggingLevel
Get-SendConnector | Select-Object Name, ProtocolLoggingLevel
Set-ReceiveConnector "Default Frontend MAIL" -ProtocolLoggingLevel Verbose
Set-ReceiveConnector "Client Frontend MAIL" -ProtocolLoggingLevel Verbose
Set-SendConnector "Internet Send Connector" -ProtocolLoggingLevel Verbose
Set-FrontEndTransportService MAIL -AgentLogEnabled $true -AgentLogPath "C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\FrontEnd\AgentLog"
Set-FrontEndTransportService MAIL -AgentLogEnabled $true -AgentLogPath "C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\FrontEnd\AgentLog"
Restart-Service MSExchangeTransport
Restart-Service MSExchangeFrontendTransport
Если нужно отправить тестовое письмо из ps shell'а
Send-MailMessage -From "test-gsz@gabelstapler-gebraucht.at" `
-To "siv.revan@yahoo.com" `
-Subject "TEST Outbound Logging" `
-Body "Testing outbound SMTP logs" `
-SmtpServer "localhost"
# Просмотр Receive логов с фильтрацией локальных адресов
Get-Content "C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\FrontEnd\ProtocolLog\SmtpReceive\RECV*.LOG" |
Where-Object {$_ -notmatch "127\.0\.0\.1" -and $_ -notmatch "192\.168\." -and $_ -match "MAIL FROM"} |
Select-Object -First 20
# Просмотр Receive логов с сортировкой времени по убыванию - сверху время начала, снизу время окончания
Get-ChildItem "C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\FrontEnd\ProtocolLog\SmtpReceive\" |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1 |
Get-Content -Tail 100
# Просмотр получения почты с фильтром по MAIL FROM
Get-Content "C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\FrontEnd\ProtocolLog\SmtpReceive\RECV*.LOG" |
Select-String -Pattern "MAIL FROM" |
Select-Object -First 10
# Тоже самое, но с фильтрацией по RCPT TO - кому т.е.
Get-Content "C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\FrontEnd\ProtocolLog\SmtpReceive\RECV*.LOG" |
Select-String -Pattern "RCPT TO" |
Select-Object -First 100
# Просмотр получения почты по id-сессии, который нашли в предыдущем выводе
Get-Content "C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\FrontEnd\ProtocolLog\SmtpReceive\RECV*.LOG" |
Select-String -Pattern "08DE307A43494652" |
Select-Object -First 10
# Просмотр отправки с фильтрации по RCPT TO
Get-ChildItem "C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\Hub\ProtocolLog\SmtpSend\" -Filter "SEND*.LOG" |
Sort-Object LastWriteTime -Descending |
Select-Object -First 5 |
Select-String -Pattern "RCPT TO" |
Select-Object -First 100
# Просмотр отправки почты по id-сессии, которую нашли в предыдущем выводе
Get-ChildItem "C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\Hub\ProtocolLog\SmtpSend\" -Filter "SEND*.LOG" |
Sort-Object LastWriteTime -Descending |
Select-Object -First 5 |
Select-String -Pattern "08DE34DA862309EF" |
Select-Object -First 100
# Просмотр логов хождения входящей и исходящей почты на внутренний порт 2525 (очень редко когда нужно) с сортировкой от меньшего времени к большему
Get-ChildItem "C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\FrontEnd\ProtocolLog\SmtpSend\" -Filter "SEND*.LOG" |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1 |
Get-Content -Tail 50
# Просмотр логов фильтрации спама
Get-AgentLog -TransportService Hub -Start (Get-Date).AddHours(-24) | where {$_.Action -ne 'AcceptMessage'} | ft Date,Agent,Action,SmtpResponse
Get-AgentLog -TransportService FrontEnd -Start (Get-Date).AddHours(-1)
Get-AgentLog -TransportService Hub -Start (Get-Date).AddHours(-24) | where {$_.Agent -like "*Content*"} | ft Date,Agent,Action,SmtpResponse
Get-Content "C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\FrontEnd\ProtocolLog\SmtpReceive\RECV*.LOG" |
Select-String -Pattern "as spam" |
Select-Object -Last 100
Get-AgentLog -TransportService Hub -Start (Get-Date).AddHours(-24)
# Кто отправлял почту, IP, имя хоста, тема в течении 1 дня
Get-MessageTrackingLog -Start (Get-Date).AddDays(-1) -EventId SUBMIT |
Select-Object Timestamp, Sender, ClientIP, ClientHostname, MessageSubject |
Where-Object {$_.ClientIP -ne "::1" -and $_.ClientIP -ne "127.0.0.1"} |
Format-Table -AutoSize
# Кто получал, от кого и через какой коннектор, тема в течении 1 дня
Get-MessageTrackingLog -Start (Get-Date).AddDays(-1) -EventId RECEIVE -Source SMTP |
Where-Object {$_.ClientIP -eq "192.168.1.20"} |
Select-Object Timestamp, Sender, Recipients, ConnectorId, MessageSubject |
Format-Table -AutoSize
# IP отправителя, имя хоста отправителя, адрес, кому послал за последние 10 минут
Get-MessageTrackingLog -Start (Get-Date).AddMinutes(-10) -EventId RECEIVE -Source SMTP |
Select-Object Timestamp, ClientIP, ClientHostname, Sender, Recipients |
Format-Table -AutoSize
# Показывает от кого письмо, кому, полную тему, размер (последнее пусто обычно) по отправителю содержащему имя no-reply
Get-MessageTrackingLog -Start (Get-Date).AddDays(-1) |
Where-Object {$_.Sender -like "*no-reply*"} |
Select-Object Timestamp, Sender, Recipients, MessageSubject, MessageSize |
Format-List
# Получить последнее письмо
$msg = Get-MessageTrackingLog -Start (Get-Date).AddHours(-1) -EventId RECEIVE -ResultSize 1
Посмотреть MessageID
$msg | Select-Object MessageId, ClientIP, Sender
# Проверка Event Log (все ошибк MSExchange, если нужно конкретный источник, то указываем, например, MSExchangeTransport)
Get-EventLog -LogName Application -Source "MSExchange*" -EntryType Error,Warning -Newest 20 | Format-List TimeGenerated,Source,Message
# Тоже, но с нормальной сортировкой
Get-EventLog -LogName Application -Source "MSExchange*" -EntryType Error,Warning -Newest 20 | Sort-Object TimeGenerated | Format-List TimeGenerated,Source,Message
Обычный режим работы с логами накапливает transaction logs бесконечно. Хорошо для инкрементального бэкапирования, можно использовать и для регулярного full-backup копирования, но невозможно при залитии огромного количества данных (временно отключать). Чтобы transaction logs ушли - нужно делать full-backup Что делает Circular Logging: логи не накапливаются и не забивают диск, автоматически перезаписывает старые логи, размер логов всегда небольшой 10-20 MB, но нельзя восстановить базу на конкретный момент между бэкапами, только Full Backup - incremental/differential не работают.
Включение режима Circular Logging (логи удаляются сразу)
Set-MailboxDatabase "Mailbox_DB_GSZ" -CircularLoggingEnabled $true
Dismount-Database "Mailbox_DB_GSZ" -Confirm:$false
Mount-Database "Mailbox_DB_GSZ"
Отключение режима Circular Logging и включение обычного режима ведения логов
Set-MailboxDatabase "Mailbox_DB_GSZ" -CircularLoggingEnabled $false
Dismount-Database "Mailbox_DB_GSZ" -Confirm:$false
Mount-Database "Mailbox_DB_GSZ"
Get-ChildItem "D:\db\logs\*.log" | Measure-Object -Property Length -Sum | Select-Object @{N='Size GB';E={[math]::Round($_.Sum/1GB,2)}}, Count
Даже, если еще кажется есть место (реальная ситуация - было еще 7Гб) Exchange может начать отклонять такие письма (postfix перед ним очень помогает), в логах:
Jan 20 14:00:13 sy-proxmox postfix/smtp[4742]: 40A854603E4: to=<kh.haller@gabelstapler-zentrum.de>, relay=192.168.1.20[192.168.1.20]:25, delay=12994, delays=12994/0.01/0/0.01, dsn=4.3.1, status=deferred (host 192.168.1.20[192.168.1.20] said: 452 4.3.1 Insufficient system resources (UsedDiskSpace[C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\data\Queue]) (in reply to end of DATA command))
Jan 20 14:00:13 sy-proxmox postfix/smtp[4550]: 9FC45460424: to=<info@gabelstapler-zentrum.de>, relay=192.168.1.20[192.168.1.20]:25, delay=8772, delays=8772/0/0/0.01, dsn=4.3.1, status=deferred (host 192.168.1.20[192.168.1.20] said: 452 4.3.1 Insufficient system resources (UsedDiskSpace[C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\data\Queue]) (in reply to end of DATA command))
Jan 20 14:00:13 sy-proxmox postfix/smtp[4742]: 40A854603E4: to=<negrusa@gabelstapler-zentrum.de>, relay=192.168.1.20[192.168.1.20]:25, delay=12994, delays=12994/0.01/0/0.01, dsn=4.3.1, status=deferred (host 192.168.1.20[192.168.1.20] said: 452 4.3.1 Insufficient system resources (UsedDiskSpace[C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\data\Queue]) (in reply to end of DATA command))
Jan 20 14:00:13 sy-proxmox postfix/smtp[4744]: 4B32C4603E0: to=<erich.janusch@gabelstapler-zentrum.de>, relay=192.168.1.20[192.168.1.20]:25, delay=13061, delays=13061/0.02/0/0.01, dsn=4.3.1, status=deferred (host 192.168.1.20[192.168.1.20] said: 452 4.3.1 Insufficient system resources (UsedDiskSpace[C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\data\Queue]) (in reply to end of DATA command))
Jan 20 14:00:13 sy-proxmox postfix/smtp[4742]: DE4A7460422: to=<info@gabelstapler-zentrum.ch>, relay=192.168.1.20[192.168.1.20]:25, delay=8840, delays=8840/0.05/0/0, dsn=4.3.1, status=deferred (host 192.168.1.20[192.168.1.20] said: 452 4.3.1 Insufficient system resources (UsedDiskSpace[C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\data\Queue]) (in reply to end of DATA command))
Jan 20 14:00:13 sy-proxmox postfix/smtp[4743]: 4584F4603DC: to=<kevin.nania@gabelstapler-zentrum.de>, relay=192.168.1.20[192.168.1.20]:25, delay=13179, delays=13179/0.02/0/0.04, dsn=4.3.1, status=deferred (host 192.168.1.20[192.168.1.20] said: 452 4.3.1 Insufficient system resources (UsedDiskSpace[C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\data\Queue]) (in reply to end of DATA command))
Jan 20 14:00:13 sy-proxmox postfix/smtp[4550]: 6CEA846047E: to=<sebastian.rohloff@gabelstapler-zentrum.de>, relay=192.168.1.20[192.168.1.20]:25, delay=510, delays=510/0.04/0/0.01, dsn=4.3.1, status=deferred (host 192.168.1.20[192.168.1.20] said: 452 4.3.1 Insufficient system resources (UsedDiskSpace[C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\data\Queue]) (in reply to end of DATA command))
# Место на дисках
Get-WmiObject Win32_LogicalDisk | Select DeviceID, @{N='FreeGB';E={[math]::Round($_.FreeSpace/1GB,2)}}
# Размер папки Queue (очереди)
(Get-ChildItem "C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\data\Queue" -Recurse | Measure-Object Length -Sum).Sum / 1GB
# Ошибки с back pressure в EventLog
Get-EventLog -LogName Application -Source "MSExchange*" -Newest 20 | Where {$_.Message -like "*back pressure*"}
# Текущий статус Back Pressure
[xml]$bp = Get-ExchangeDiagnosticInfo -Process EdgeTransport -Component ResourceThrottling
$bp.Diagnostics.Components.ResourceThrottling.ResourceTracker.ResourceMeter | ft Resource, Pressure, CurrentResourceUse
# Смотрим DatabaseUsedSpace, UsedDiskSpace, UsedDiskSpace.
- 89% - замедление
- 99% - блокировка (High)
#Рестарт может помочь очереди пойти дальше, если есть еще немного места, но очистка необходима
Restart-Service MSExchangeTransport
# Логи IIS
(Get-ChildItem C:\inetpub\logs -Recurse | Measure-Object Length -Sum).Sum / 1GB
# Логи Exchange
(Get-ChildItem "C:\Program Files\Microsoft\Exchange Server\V15\Logging" -Recurse | Measure-Object Length -Sum).Sum / 1GB
# Windows Temp
(Get-ChildItem C:\Windows\Temp -Recurse -ErrorAction SilentlyContinue | Measure-Object Length -Sum).Sum / 1GB
# IIS логи старше 7 дней
Get-ChildItem C:\inetpub\logs -Recurse -File | Where {$_.LastWriteTime -lt (Get-Date).AddDays(-14)} | Remove-Item -Force -ErrorAction SilentlyContinue
# Exchange логи старше 7 дней
Get-ChildItem "C:\Program Files\Microsoft\Exchange Server\V15\Logging" -Recurse -File | Where {$_.LastWriteTime -lt (Get-Date).AddDays(-7)} | Remove-Item -Force -ErrorAction SilentlyContinue
# Проверяем
(Get-ChildItem "C:\Program Files\Microsoft\Exchange Server\V15\Logging" -Recurse | Measure-Object Length -Sum).Sum / 1GB
# Самые большие папки логов
Get-ChildItem "C:\Program Files\Microsoft\Exchange Server\V15\Logging" -Directory | ForEach-Object {
>> [PSCustomObject]@{
>> Folder = $_.Name
>> SizeGB = [math]::Round((Get-ChildItem $_.FullName -Recurse -File | Measure-Object Length -Sum).Sum / 1GB, 2)
>> }
>> } | Sort-Object SizeGB -Descending | Select -First 15
# Если логи держатся, то тогда только останавливаем все службы и повторяем очистку
Get-Service *Exchange* | Stop-Service -Force
# Посмотреть текущие настройки ротации логов
Get-TransportService | fl *log*path*, *log*maxage*
# Установить автоочистку логов старше 7 дней
Set-TransportService -Identity $env:COMPUTERNAME -MessageTrackingLogMaxAge 7.00:00:00 -ConnectivityLogMaxAge
7.00:00:00 -ReceiveProtocolLogMaxAge 7.00:00:00 -SendProtocolLogMaxAge 7.00:00:00
Set-TransportService -Identity $env:COMPUTERNAME ` -ConnectivityLogMaxAge 7.00:00:00 `
-MessageTrackingLogMaxAge 7.00:00:00 ` -IrmLogMaxAge 7.00:00:00 `
-ActiveUserStatisticsLogMaxAge 7.00:00:00 `
-ServerStatisticsLogMaxAge 7.00:00:00 `
-ReceiveProtocolLogMaxAge 7.00:00:00 `
-SendProtocolLogMaxAge 7.00:00:00 `
-TransportSyncLogMaxAge 7.00:00:00 `
-TransportSyncHubHealthLogMaxAge 7.00:00:00
Restart-Service MSExchangeTransport
Get-ChildItem "C:\Program Files\Microsoft\Exchange Server\V15\Logging" -Recurse -File | Where {$_.LastWriteTime -lt (Get-Date).AddDays(-7)} | Remove-Item -Force -ErrorAction SilentlyContinue
Get-ChildItem C:\inetpub\logs -Recurse -File | Where {$_.LastWriteTime -lt (Get-Date).AddDays(-7)} | Remove-Item -Force
Get-ChildItem "C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs" -Recurse -File | Where {$_.LastWriteTime -lt (Get-Date).AddDays(-7)} | Remove-Item -Force -ErrorAction SilentlyContinue
Get-ChildItem "C:\Program Files\Microsoft\Exchange Server\V15\Logging\HttpProxy" -Recurse -File | Where {$_.LastWriteTime -lt (Get-Date).AddDays(-3)} | Remove-Item -Force -ErrorAction SilentlyContinue
Get-ChildItem "C:\Program Files\Microsoft\Exchange Server\V15\Logging\Ews" -Recurse -File | Where {$_.LastWriteTime -lt (Get-Date).AddDays(-3)} | Remove-Item -Force -ErrorAction SilentlyContinue
Get-ChildItem "C:\Program Files\Microsoft\Exchange Server\V15\Logging\Diagnostics" -Recurse -File | Where {$_.LastWriteTime -lt (Get-Date).AddDays(-1)} | Remove-Item -Force -ErrorAction SilentlyContinue
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-ExecutionPolicy Bypass -File c:\scripts\logrotate.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At 3:00AM
Register-ScheduledTask -TaskName "CleanExchangeLogs" -Action $action -Trigger $trigger -User "SYSTEM" -RunLevel Highest
# Смотрим сколько висит в очереди postfix
postqueue -p | tail -1
# Подробнее, что именно висит
mailq
# Если надо, то форсим отправку из очереди
postqueue -f
Просмотр свойств пользователя домена с логином info и свойства срок действия пароля не ограничен Get-ADUser -Identity "info" -Properties PasswordNeverExpires | Select Name,PasswordNeverExpires Просмотр свойств пользователя домена по почте Get-ADUser -Filter "UserPrincipalName -eq 'k.lechner@gabelstapler-gebraucht.at'" -Properties PasswordNeverExpires Просмотр всех пользователей домена в OU с проверкой установки флага срок действия пароля не ограничен Get-ADUser -Filter * -SearchBase "OU=AT,DC=gsz,DC=local" -Properties PasswordNeverExpires,UserPrincipalName | Select Name,UserPrincipalName,PasswordNeverExpires Просмотр конкретного пользователя OU и флага срок действия пароля не ограничен Get-ADUser -Filter "Name -eq 'k.lechner'" -SearchBase "OU=AT,DC=gsz,DC=local" -Properties PasswordNeverExpires Установка у всех пользователей OU флага срок действия пароля не ограничен Get-ADUser -Filter * -SearchBase "OU=AT,DC=gsz,DC=local" | Set-ADUser -PasswordNeverExpires $true
# На внешний адрес
Set-Mailbox -Identity "user@domain.com" -ForwardingSMTPAddress "external@gmail.com"
# На внутренний адрес
# Самый лучший вариант (просто добавляется почта - ничего не пересылается - просто почта идет с этих ящика[ов] на u.stern)
Set-Mailbox "u.stern" -EmailAddresses @{Add="uli.stern@gabelstapler-zentrum.ch"}
# А это уже пересылка
Set-Mailbox -Identity "source@domain.com" -ForwardingAddress "target@domain.com"
# Сохранять копию в почтовом ящике
Set-Mailbox -Identity "source@domain.com" `
-ForwardingAddress "target@domain.com" `
-DeliverToMailboxAndForward $true
# Только пересылка без сохранения копии
Set-Mailbox -Identity "source@domain.com" `
-ForwardingAddress "target@domain.com" `
-DeliverToMailboxAndForward $false
# Нескольким адресам
# Лучший вариант. Делаем правило на уровне транспорта
New-TransportRule -Name "Redirect sy-infra to team" `
-SentTo "sy-infra@gabelstapler-zentrum.de" `
-RedirectMessageTo "team.kirill@symfio.de","stratyuk.igor@symfio.de"
# С копией в почтовом ящике
New-TransportRule -Name "Forward user@domain to multiple" `
-SentTo "source@domain.com" `
-BlindCopyTo "recipient1@domain.com","recipient2@gmail.com","recipient3@yahoo.com"
# Просмотр правила с поиском названия по частичному совпадению (email) и выводом куда приходит и куда переадресовывается
Get-TransportRule -Identity "*sy-infra*" | ft Identity, Priority, SentTo, RedirectMessageTo
# Создать группу (НО!!! Тут минус сначала нужно будет создать для этих адресов Mail Contact)
New-DistributionGroup -Name "Forward Group" -Members "user1@domain.com","user2@gmail.com"
# Настроить forwarding на группу
Set-Mailbox -Identity "source@domain.com" -ForwardingAddress "Forward Group"
# или на уровне пользователя (НО!!! Нужно включать релей для всех или только для этого домена, что не очень)
New-InboxRule -Mailbox "source@domain.com" `
-Name "Forward to multiple" `
-ForwardTo "recipient1@domain.com","recipient2@gmail.com"
# Только пересылка без сообщения в исходном ящике
New-InboxRule -Mailbox "user@domain.com" `
-Name "Redirect" `
-RedirectTo "recipient@domain.com"
# Пересылка + удаление (не оставлять в Inbox)
New-InboxRule -Mailbox "user@domain.com" `
-Name "Forward and Delete" `
-ForwardTo "recipient@domain.com" `
-DeleteMessage $true
# Проверить правило
Get-InboxRule -Mailbox "user@domain.com" | fl Name, Enabled, ForwardTo, RedirectTo, DeleteMessage
# Удалить конкретное правило
Remove-InboxRule -Mailbox "user@domain.com" -Identity "RuleName" -Confirm:$false
# Удалить все правила для ящика
Get-InboxRule -Mailbox "user@domain.com" | Remove-InboxRule -Confirm:$false
# Проверить настройки
Get-Mailbox "source@domain.com" | fl ForwardingAddress, ForwardingSMTPAddress, DeliverToMailboxAndForward
# Отключить forwarding
Set-Mailbox -Identity "source@domain.com" -ForwardingAddress $null -ForwardingSMTPAddress $null
# Поиск по ящику в теме письма слова `Seitenstapler`
Search-Mailbox -Identity "erich.janusch@gabelstapler-gebraucht.at" -SearchQuery 'subject:"Seitenstapler"' -EstimateResultOnly
# Поиск и УДАЛЕНИЕ - ОСТОРОЖНО!!!
Search-Mailbox -Identity "info@gabelstapler-gebraucht.at" -DeleteContent -Force
# Просмотр папки и ее размера
Get-MailboxFolderStatistics "k.lechner@gabelstapler-gebraucht.at" | Where-Object {$_.Name -like "*_0"} | Select-Object Name, ItemsInFolder, FolderSize
Скрипт для создания ящиков из файла Запускаем так (только из Exchange Shell):
.\create_new-mbox.ps1 -InputFile users_at.txt
# ===== ПАРАМЕТРЫ =====
param(
[Parameter(Mandatory=$true)]
[string]$InputFile
)
# Проверка существования файла
if (-not (Test-Path $InputFile)) {
Write-Host "ОШИБКА: Файл не найден: $InputFile" -ForegroundColor Red
exit 1
}
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Массовое создание пользователей AD + Exchange" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
# Счетчики
$successCount = 0
$errorCount = 0
# Читаем файл построчно
Get-Content $InputFile | ForEach-Object {
$line = $_.Trim()
# Пропускаем пустые строки и комментарии
if ([string]::IsNullOrWhiteSpace($line) -or $line.StartsWith("#")) {
return
}
# Парсим строку: email <tab/space> password
$parts = $line -split '\s+', 2
if ($parts.Count -ne 2) {
Write-Host "ОШИБКА: Неверный формат строки: $line" -ForegroundColor Red
$errorCount++
return
}
$email = $parts[0]
$password = $parts[1]
# Парсим email: test-gsz@gabelstapler-gebraucht.at
if ($email -notmatch '^(.+)@(.+)\.([a-z]{2,})$') {
Write-Host "ОШИБКА: Неверный формат email: $email" -ForegroundColor Red
$errorCount++
return
}
$name = $Matches[1] # test-gsz
$domain = $Matches[2] + "." + $Matches[3] # gabelstapler-gebraucht.at
$regionalDomain = $Matches[3] # at
# Формируем параметры
$samAccountName = "$name.gsz.$regionalDomain" # test-gsz.gsz.at
$upn = $email # test-gsz@gabelstapler-gebraucht.at
$ou = "OU=$($regionalDomain.ToUpper()),DC=gsz,DC=local" # OU=AT,DC=gsz,DC=local
$extensionAttr = $regionalDomain.ToUpper() # AT
Write-Host "Создание пользователя: $email" -ForegroundColor Yellow
Write-Host " Name: $name"
Write-Host " SamAccountName: $samAccountName"
Write-Host " UPN: $upn"
Write-Host " OU: $ou"
try {
# Создание AD User
New-ADUser `
-Name $name `
-SamAccountName $samAccountName `
-UserPrincipalName $upn `
-Path $ou `
-AccountPassword (ConvertTo-SecureString $password -AsPlainText -Force) `
-Enabled $true `
-PasswordNeverExpires $true `
-OtherAttributes @{extensionAttribute1=$extensionAttr} `
-ErrorAction Stop
Write-Host " ✓ AD User создан" -ForegroundColor Green
# Небольшая пауза для репликации AD
Start-Sleep -Seconds 2
# Создание Exchange Mailbox
Enable-Mailbox -Identity $samAccountName -Alias $samAccountName -ErrorAction Stop | Out-Null
Write-Host " ✓ Mailbox включен" -ForegroundColor Green
Set-Mailbox -Identity $upn -PrimarySmtpAddress $upn -ErrorAction Stop
Write-Host " ✓ Primary SMTP установлен" -ForegroundColor Green
$successCount++
Write-Host ""
} catch {
Write-Host " ✗ ОШИБКА: $($_.Exception.Message)" -ForegroundColor Red
$errorCount++
Write-Host ""
}
}
# Итоги
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Успешно создано: $successCount" -ForegroundColor Green
Write-Host "Ошибок: $errorCount" -ForegroundColor Red
Write-Host "========================================" -ForegroundColor Cyan
Импорт PST создает папку при импорте в корне ящика с названием, например, Inbox_0 или Posteingang_0. Он НЕ сливает папки даже с одинаковым названием. Перемещение в Outlook'е - это очень и очень долго. По сути долгая синхронизация одного места, потом другого, еще и каждого объекта. Для больших ящиков не вариант. Поиск в Exchange - старье и не работает на то, чтобы можно было переносить все содержимое в нужную папку никак. Есть скрипт под EWS
# Качаем nuget-cli (nuget.exe) и выполняем
cd C:\temp\ews
nuget install Microsoft.Exchange.WebServices -Version 2.2.0
# Скачиваем папку скриптов
https://github.com/David-Barrett-MS/PowerShell-EWS-Scripts/archive/refs/heads/master.zip
# Распаковываем - нам нужна только Legacy и внутри все скрипты
# Переименовываем ее в PowerShell-EWS-Scripts и перемещаем в C:\scripts
cd C:\scripts\PowerShell-EWS-Scripts
# Копируем dll - только она и нужна в папку со скриптами
Copy-Item "C:\temp\ews\Microsoft.Exchange.WebServices.2.2.0\lib\40\Microsoft.Exchange.WebServices.dll" -Destination "C:\scripts\PowerShell-EWS-Scripts\"
# Смотрим пути, если нужно
Get-MailboxFolderStatistics "a.negrusa@gabelstapler-zentrum.ch" |
Where {$_.Name -like "*Sent*"} |
Select Name, FolderPath
# Указываем креды - владельца ящика (админа у меня не работало - как-то можно, но не работало)
$cred = Get-Credential
# Перемещаем содержимое папок (\ - разделитель подпапок). В принципе Delete - лишняя опция - он все равно все перемещает в папку указанную слева от знака =. По идее можно указывать несколько перемещений за раз.
.\Merge-MailboxFolder.ps1 `
-SourceMailbox "a.negrusa@gabelstapler-zentrum.ch" `
-MergeFolderList @{"Posteingang" = "Posteingang_0"} `
-TargetMailbox "a.negrusa@gabelstapler-zentrum.ch" `
-EwsUrl "https://mail.gsz.local/EWS/Exchange.asmx" `
-Credentials $cred `
-Delete
.\Merge-MailboxFolder.ps1 `
-SourceMailbox "a.negrusa@gabelstapler-zentrum.ch" `
-MergeFolderList @{"Gesendete Elemente" = "Posteingang_0\Sent"} `
-TargetMailbox "a.negrusa@gabelstapler-zentrum.ch" `
-EwsUrl "https://mail.gsz.local/EWS/Exchange.asmx" `
-Credentials $cred `
-Delete
.\Merge-MailboxFolder.ps1 `
-SourceMailbox "a.negrusa@gabelstapler-zentrum.ch" `
-MergeFolderList @{"Junk-E-Mail" = "Posteingang_0\Spam"} `
-TargetMailbox "a.negrusa@gabelstapler-zentrum.ch" `
-EwsUrl "https://mail.gsz.local/EWS/Exchange.asmx" `
-Credentials $cred `
-Delete
.\Merge-MailboxFolder.ps1 `
-SourceMailbox "a.negrusa@gabelstapler-zentrum.ch" `
-MergeFolderList @{"Gelöschte Elemente" = "Posteingang_0\Trash"} `
-TargetMailbox "a.negrusa@gabelstapler-zentrum.ch" `
-EwsUrl "https://mail.gsz.local/EWS/Exchange.asmx" `
-Credentials $cred `
-Delete
Импорт PST очень выгоден - намного быстрее, чем IMAP Sync. 10-50Гб в час в сравнении с 1-5Гб в час, также с возможной потерей структуры, иерархии папок, множества ошибок и дубликатов.
New-MailboxImportRequest `
-Mailbox "kevin.nania@gabelstapler-zentrum.de" `
-FilePath "\\MAIL\import\backup_gsz.de_kevin.nania_20251223.pst" `
-TargetRootFolder "" `
-BadItemLimit 500 `
-LargeItemLimit 20000 `
-AcceptLargeDataLoss
Get-MailboxImportRequest | Get-MailboxImportRequestStatistics
Get-MailboxImportRequest | Get-MailboxImportRequestStatistics | Format-List *Large*,*Bad*,Name
Get-MailboxImportRequest -Mailbox "kevin.nania@gabelstapler-zentrum.de" | Get-MailboxImportRequestStatistics -IncludeReport | Format-List
# Сборный скрипт для переноса папок Inbox, Sent, Draft, Notes, Spam, Trash в соответствующие немецкие
$usr = "m.walker@gabelstapler-zentrum.de"; $pwd = "Fp2N44dDv88?"; $cred = New-Object System.Management.Automation.PSCredential($usr, (ConvertTo-SecureString $pwd -AsPlainText -Force)); .\Merge-MailboxFolder.ps1 -SourceMailbox $usr -MergeFolderList @{"Posteingang" = "Posteingang_0"} -TargetMailbox $usr -EwsUrl "https://mail.gsz.local/EWS/Exchange.asmx" -Credentials $cred; .\Merge-MailboxFolder.ps1 -SourceMailbox $usr -MergeFolderList @{"Gesendete Elemente" = "Posteingang_0\Sent"} -TargetMailbox $usr -EwsUrl "https://mail.gsz.local/EWS/Exchange.asmx" -Credentials $cred; .\Merge-MailboxFolder.ps1 -SourceMailbox $usr -MergeFolderList @{"Entwürfe" = "Posteingang_0\Drafts"} -TargetMailbox $usr -EwsUrl "https://mail.gsz.local/EWS/Exchange.asmx" -Credentials $cred; .\Merge-MailboxFolder.ps1 -SourceMailbox $usr -MergeFolderList @{"Junk-E-Mail" = "Posteingang_0\Spam"} -TargetMailbox $usr -EwsUrl "https://mail.gsz.local/EWS/Exchange.asmx" -Credentials $cred; .\Merge-MailboxFolder.ps1 -SourceMailbox $usr -MergeFolderList @{"Gelöschte Elemente" = "Posteingang_0\Trash"} -TargetMailbox $usr -EwsUrl "https://mail.gsz.local/EWS/Exchange.asmx" -Credentials $cred; .\Merge-MailboxFolder.ps1 -SourceMailbox $usr -MergeFolderList @{"Notizen" = "Posteingang_0\Notes"} -TargetMailbox $usr -EwsUrl "https://mail.gsz.local/EWS/Exchange.asmx" -Credentials $cred
# Или по отдельным командам
$cred = Get-Credential
.\Merge-MailboxFolder.ps1 `
-SourceMailbox "kevin.nania@gabelstapler-zentrum.de" `
-MergeFolderList @{"Posteingang" = "Inbox"} `
-TargetMailbox "kevin.nania@gabelstapler-zentrum.de" `
-EwsUrl "https://mail.gsz.local/EWS/Exchange.asmx" `
-Credentials $cred
.\Merge-MailboxFolder.ps1 `
-SourceMailbox "kevin.nania@gabelstapler-zentrum.de" `
-MergeFolderList @{"Gesendete Elemente" = "Inbox\Sent"} `
-TargetMailbox "kevin.nania@gabelstapler-zentrum.de" `
-EwsUrl "https://mail.gsz.local/EWS/Exchange.asmx" `
-Credentials $cred
.\Merge-MailboxFolder.ps1 `
-SourceMailbox "kevin.nania@gabelstapler-zentrum.de" `
-MergeFolderList @{"Junk-E-Mail" = "Inbox\Spam"} `
-TargetMailbox "kevin.nania@gabelstapler-zentrum.de" `
-EwsUrl "https://mail.gsz.local/EWS/Exchange.asmx" `
-Credentials $cred
.\Merge-MailboxFolder.ps1 `
-SourceMailbox "kevin.nania@gabelstapler-zentrum.de" `
-MergeFolderList @{"Gelöschte Elemente" = "Inbox\Trash"} `
-TargetMailbox "kevin.nania@gabelstapler-zentrum.de" `
-EwsUrl "https://mail.gsz.local/EWS/Exchange.asmx" `
-Credentials $cred
# Удаление всех успешных импортов
Get-MailboxImportRequest -Status Completed | Remove-MailboxImportRequest
# Удаление конкретного импорта (например, проблемы с ним)
Remove-MailboxImportRequest -Identity "max.rietzler@gabelstapler-zentrum.de\MailboxImport" -Confirm:$false
Если PST уже большой (60 GB), можно экспортировать по годам (PST Файл -> Экспорт в Outlook и можно выбрать года, даты)
Проверить формат файла PST Get-ItemProperty "C:\path\to\file.pst" | Select Length, FullName
Проверить права на импорт у пользователя Get-ManagementRoleAssignment -Role "Mailbox Import Export" -RoleAssignee "msexadm"
Проверить службу Microsoft Exchange Mailbox Replication Get-Service MSExchangeMailboxReplication
Способ 1: New-MailboxImportRequest (рекомендуемый)
1.1. Подготовка - дать права на импорт:
New-ManagementRoleAssignment -Role "Mailbox Import Export" -User "Administrator"
1.2. Расшарить папку с PST файлами:
PST файлы должны быть на сетевой шаре (UNC путь), локальные пути не работают! Т.е. не так "C:\Temp\file.pst", а так "\MAIL\PST-Import\file.pst"
New-Item -Path "C:\PST_Import" -ItemType Directory
New-SmbShare -Name "import" -Path "D:\import" -FullAccess "GSZ\Administrator","GSZ\info" New-SmbShare -Name "import" -Path "D:\import" -FullAccess "env:USERNAME"
Проверить шару Test-Path "\MAIL\import\backup_gsz.at_info_20251203.pst"
Проверить, что шара существует Get-SmbShare -Name "import"
Проверить права на шару Get-SmbShareAccess -Name "import"
Дать права на SMB share для Exchange Trusted Subsystem Grant-SmbShareAccess -Name "import" -AccountName "GSZ\Exchange Trusted Subsystem" -AccessRight Full -Force
Проверить Get-SmbShareAccess -Name "import"
Если есть VM agent qm list | grep -i mail Копирование с хоста proxmox в VM qm guest exec 100 -- cp /srv/pst-upload/large.pst C:\PST-Import\
или очень простой способ (поднять HTTP сервер) переходим в папку с pst-файлами cd /srv/pst Запустить простой HTTP сервер python3 -m http.server 8000 На VM открываем браузер с IP внутренним хоста и портом 8000, качаем или Invoke-WebRequest -Uri "http://192.168.1.1:8000/file1.pst" -OutFile "D:\import\file1.pst"
1.3. Дать права Exchange на шару:
$Acl = Get-Acl "C:\PST_Import" $Rule = New-Object System.Security.AccessControl.FileSystemAccessRule("Exchange Trusted Subsystem", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow") Rule) Set-Acl "C:\PST_Import" $Acl
1.4. Импорт PST в почтовый ящик: Get-PSSnapin | Where-Object {$_.Name -like "Exchange"} Должно быть не пусто, а Microsoft.Exchange.Management.PowerShell.SnapIn Если нет, то загрузить модули Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn Проверить, что команда доступна Get-Command MailboxImport | Format-List Name, PSSnapin, Module
или icacls "D:\import" /grant "GSZ\Exchange Trusted Subsystem:(OI)(CI)F" /T
Проверить права icacls D:\import icacls D:\import\backup_gsz.at_info_20251203.pst
BadItemLimit - сколько битых писем можно пропустить без остановок, LargeItemLimit - импортировать до N больших писем, т.е. для Exchange больше 10-35Мб, AcceptLargeDataLoss - импорт продолжается даже при потере большого количества данных, Priority High - высокий приоритет импорта. Рекомендации: старые PST (10+ лет) -BadItemLimit 500 -AcceptLargeDataLoss, новые PST (1-3 года) -BadItemLimit 50, тестовый -BadItemLimit 0 New-MailboxImportRequest -Mailbox "user" -FilePath "file.pst" -BadItemLimit 1000 -LargeItemLimit 100 -AcceptLargeDataLoss -Priority High
Рабочий вариант импорта New-MailboxImportRequest -Mailbox "leads@gabelstapler-gebraucht.at" -FilePath "\MAIL\import\backup_gsz.at_leads_20251204.pst" -TargetRootFolder "/" -BadItemLimit 500 -LargeItemLimit 20000 -AcceptLargeDataLoss
Очистка ящика от писем (папки остаются) Search-Mailbox -Identity "info@gabelstapler-gebraucht.at" -DeleteContent -Force
Поиск в почтовом ящике (можно искать где угодно) Search-Mailbox -Identity "erich.janusch@gabelstapler-gebraucht.at" -SearchQuery 'subject:"Seitenstapler"' -EstimateResultOnly
НЕ РАБОТАЕТ!!!!! Перемещение из папок импорта в папки пользователя Get-MailboxFolderStatistics k.lechner@gabelstapler-gebraucht.at | ? {$_.FolderPath -like "*Posteingang_0*"} | ft Name,FolderPath,ItemsInFolder,FolderId -Auto folderId"" -TargetMailbox k.lechner@gabelstapler-gebraucht.at -TargetFolder "Posteingang" -LogLevel Suppress
НЕ РАБОТАЕТ!!!! Search-Mailbox -Identity "k.lechner@gabelstapler-gebraucht.at" -SearchQuery "All" -SourceFolders "Posteingang_0" -TargetMailbox "k.lechner@gabelstapler-gebraucht.at" -TargetFolder "Posteingang" -SearchResultSizeLimit Unlimited -DeleteContent ` -Force
Проверки результатов и статистика Get-MailboxImportRequest | Get-MailboxImportRequestStatistics
Get-MailboxImportRequest -Mailbox "l.lechner@gabelstapler-gebraucht.at" | Get-MailboxImportRequestStatistics -IncludeReport | Format-List
Get-MailboxImportRequest | Get-MailboxImportRequestStatistics | Format-List Large,Bad,Name
Get-MailboxImportRequest | Get-MailboxImportRequestStatistics | Format-List DisplayName, Status, PercentComplete, BytesTransferred, ItemsTransferred
Удалить завершенные запросы Get-MailboxImportRequest -Status Completed | Remove-MailboxImportRequest
Get-MailboxImportRequest | Remove-MailboxImportRequest
New-MailboxImportRequest -Mailbox "info@gabelstapler-gebraucht.at" -FilePath "\MAIL\import\backup_gsz.at_info_20251203.pst" -BadItemLimit 500 -LargeItemLimit 10000 -AcceptLargeDataLoss -Priority High
New-MailboxImportRequest -Mailbox "user@gabelstapler-zentrum.ch" ` -FilePath "\EX01\PST_Import\user1.pst"
New-MailboxImportRequest -Mailbox "user@gabelstapler-zentrum.ch" -FilePath "\\EX01\PST_Import\user1.pst" -TargetRootFolder "Imported"
New-MailboxImportRequest -Mailbox "user@gabelstapler-zentrum.ch" -FilePath "\\EX01\PST_Import\user1.pst" -IncludeFolders "#Inbox#/*","#SentItems#"
New-MailboxImportRequest -Mailbox "user@gabelstapler-zentrum.ch" -FilePath "\\EX01\PST_Import\user1.pst" -ExcludeFolders "#Junk Email#","#Deleted Items#"
1.5. Мониторинг импорта:
Get-MailboxImportRequest | Get-MailboxImportRequestStatistics
Лог битых элементов Get-MailboxImportRequest | Get-MailboxImportRequestStatistics -IncludeReport | Select -ExpandProperty Report
Подробный статус Get-MailboxImportRequest | Get-MailboxImportRequestStatistics | Format-List DisplayName, Status, PercentComplete, BytesTransferred, ItemsTransferred
Get-MailboxImportRequest -Mailbox "user@gabelstapler-zentrum.ch" | Get-MailboxImportRequestStatistics -IncludeReport | Format-List
Статистика по большим письмам и проблемным - сколько указали в последнем запросе и сколько было отфильтровано (не вошло в ящик) Get-Mailbox "erich.janusch@gabelstapler-gebraucht.at" | Format-List MaxSendSize,MaxReceiveSize
Get-MailboxImportRequest | Format-List Mailbox, Status, PercentComplete
При неудаче или критичных ошибках - удаляем импорт - в ящик при Failed не добавляются письма Get-MailboxImportRequest | Remove-MailboxImportRequest -Confirm:$false
Можно и очистить весь ящик - но лучше в КРАЙНИХ СЛУЧАЯХ и осторожно Search-Mailbox -Identity "info@gabelstapler-gebraucht.at" -DeleteContent -Force
1.6. После завершения - очистка:
Get-MailboxImportRequest -Status Completed | Remove-MailboxImportRequest
Get-MailboxImportRequest | Remove-MailboxImportRequest
Просмотр всех папок ящика Get-MailboxFolderStatistics -Identity "info@gabelstapler-gebraucht.at" | Format-Table Name, FolderType
Get-MailboxFolderStatistics "k.lechner@gabelstapler-gebraucht.at" | Where-Object {$_.Name -like "*_0"} | Select-Object Name, ItemsInFolder, FolderSize
Проверяем язык ящика Get-MailboxRegionalConfiguration -Identity "info@gabelstapler-gebraucht.at" | Format-List Language, DateFormat, TimeFormat
Если нужно переводим на другой язык Set-MailboxRegionalConfiguration -Identity "info@gabelstapler-gebraucht.at" -Language "en-US" -LocalizeDefaultFolderName или Set-MailboxRegionalConfiguration -Identity "info@gabelstapler-gebraucht.at" -Language "de-DE" -LocalizeDefaultFolderName указать формат даты (при переводе требует, если разные - европейский и американский) - тут европейский Set-MailboxRegionalConfiguration -Identity "info@gabelstapler-gebraucht.at" -Language "en-US" -DateFormat "dd/MM/yyyy" -TimeFormat "HH:mm" -LocalizeDefaultFolderName американский формат дат Set-MailboxRegionalConfiguration -Identity "info@gabelstapler-gebraucht.at" -Language "en-US" -DateFormat "MM/dd/yyyy" -TimeFormat "h:mm tt" -LocalizeDefaultFolderName
Способ 2: Массовый импорт нескольких PST
2.1. Если имена PST совпадают с email:
$PSTFiles = Get-ChildItem "\EX01\PST_Import*.pst"
foreach ($PST in $PSTFiles) { $Mailbox = $PST.BaseName # user1@gabelstapler-zentrum.ch
Write-Host "Importing $($PST.Name) to $Mailbox..."
New-MailboxImportRequest -Mailbox $Mailbox -FilePath $PST.FullName -TargetRootFolder "Imported from PST"
}
Get-MailboxImportRequest | Format-Table Mailbox, Status, PercentComplete
2.2. Импорт из CSV с маппингом:
Создай файл C:\PST_Import\mapping.csv:
PST,Mailbox,TargetFolder user1.pst,john@gabelstapler-zentrum.ch,Imported user2.pst,anna@gabelstapler-zentrum.ch,Old Mail user3.pst,info@gabelstapler-zentrum.ch,Archive
$Mapping = Import-Csv "C:\PST_Import\mapping.csv"
foreach ($Item in $Mapping) { ($Item.PST)"
Write-Host "Importing $($Item.PST) to $($Item.Mailbox)..."
New-MailboxImportRequest -Mailbox $Item.Mailbox `
-FilePath $PSTPath `
-TargetRootFolder $Item.TargetFolder
}
Способ 3: Outlook (для единичных случаев)
Если нужно импортировать один-два PST:
Минус: Требует Outlook на клиенте и ручную работу.
⚠️ Важные нюансы:
Размер и время:
| Размер PST | Примерное время импорта |
|---|---|
| 1 GB | 5-15 минут |
| 5 GB | 30-60 минут |
| 10 GB | 1-2 часа |
| 50 GB | 5-10 часов |
Ограничения:
Проблемы с большими PST:
Если PST больше 20 GB - разбей его:
New-MailboxImportRequest -Mailbox "user" -FilePath "\server\pst\large.pst" -IncludeFolders "#Inbox#" New-MailboxImportRequest -Mailbox "user" -FilePath "\server\pst\large.pst" -IncludeFolders "#SentItems#"
Дубликаты:
По умолчанию дубликаты не создаются (AssociatedMessagesCopyOption = DoNotCopy).
Если нужно копировать все включая дубликаты: New-MailboxImportRequest -Mailbox "user" -FilePath "\server\pst\file.pst" -AssociatedMessagesCopyOption Copy
📊 Полный workflow импорта:
New-ManagementRoleAssignment -Role "Mailbox Import Export" -User "Administrator"
New-Item -Path "C:\PST_Import" -ItemType Directory New-SmbShare -Name "PST_Import" -Path "C:\PST_Import" -FullAccess "Everyone"
$Acl = Get-Acl "C:\PST_Import" $Rule = New-Object System.Security.AccessControl.FileSystemAccessRule("Exchange Trusted Subsystem", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow") Rule) Set-Acl "C:\PST_Import" $Acl
New-MailboxImportRequest -Mailbox "user@gabelstapler-zentrum.ch" -FilePath "\EX01\PST_Import\user.pst"
Get-MailboxImportRequest | Get-MailboxImportRequestStatistics | Format-Table Mailbox, Status, PercentComplete, BytesTransferred
Get-MailboxImportRequest -Status Completed | Remove-MailboxImportRequest
Некрософт решил принудить всех ходить на свои сервера и платить денежку )). В общем, отдельные пользователи, у которых скорее всего есть акк в майкрософт при добавлении и потом при использовании Outlook'а пытаются коннектиться в outlook.com Некрософта и получать оттуда autodiscover.xml. Чтобы это отключить добавляем в реестр:
reg add "HKCU\Software\Microsoft\Office\16.0\Outlook\AutoDiscover" /v ExcludeExplicitO365Endpoint /t REG_DWORD /d 1 /f
Операция успешно завершена.
reg add "HKCU\Software\Microsoft\Office\16.0\Outlook\Setup" /v DisableOffice365SimplifiedAccountCreation /t REG_DWORD /d 1 /f
Операция успешно завершена.
reg add "HKCU\Software\Microsoft\Office\16.0\Common\Identity" /v EnableADAL /t REG_DWORD /d 0 /f
Операция успешно завершена.
После удаления и очищения корзины, удаленные элементы появляются в скрытой папке. Из нее удаляется автоматически каждые 14 дней. Но можно для очищения места удалить руками
Get-MailboxFolderStatistics -Identity "erich.janusch@gabelstapler-zentrum.de" -FolderScope RecoverableItems |
ft Name, FolderSize, ItemsInFolder
Search-Mailbox -Identity "erich.janusch@gabelstapler-zentrum.de" `
-SearchDumpsterOnly -DeleteContent -Force
# Просмотр всех папок ящика, где размер данных больше 0
Get-MailboxFolderStatistics -Identity "m.haneder@gabelstapler-zentrum.de" |
Where-Object {$_.ItemsInFolder -gt 0} |
Sort-Object ItemsInFolder -Descending |
Select -First 15 FolderPath, ItemsInFolder, FolderSize
# Делаем роль на основании базовых админских прав Mail Recipients для управления пользователями
New-ManagementRole -Name "Domain Admin GSZ" -Parent "Mail Recipients"
# Делаем scope, чтобы можно было ограничить управление только конкретными доменами
New-ManagementScope -Name "GSZ Domains" -RecipientRestrictionFilter {(EmailAddresses -like "*@gabelstapler-zentrum.de") -or (EmailAddresses -like "*@gabelstapler-zentrum.ch") -or (EmailAddresses -like "*@gabelstapler-gebraucht.at")}
# Создаем пользователя с ролью и scope (добавится в AD также)
New-Mailbox -Name "msexmgmt" -UserPrincipalName "msexmgmt@gsz.local" -Password (ConvertTo-SecureString "Hevzywtd+=1943" -AsPlainText -Force)
New-ManagementRoleAssignment -Role "Domain Admin GSZ" -User "msexmgmt@gsz.local" -CustomRecipientWriteScope "GSZ Domains"
# Добавляем пользователя текущего в руководителем группы и потом уже добавляем msmgmt к группе (только руководителям дает права)
$currentManagers = (Get-RoleGroup "Recipient Management").ManagedBy
$newManagers = $currentManagers + "msexadm"
Set-RoleGroup "Recipient Management" -ManagedBy $newManagers
Get-RoleGroup "Recipient Management" | Format-List Name,ManagedBy
Name : Recipient Management
ManagedBy : {gsz.local/Microsoft Exchange Security Groups/Organization Management, gsz.local/Users/msexadm}
Add-RoleGroupMember -Identity "Recipient Management" -Member "msexmgmt@gsz.local"
# Добавляем роль для управления Forwarding на уровне транспорта (scope не нужен - тут глобальные права нужны)
New-ManagementRole -Name "GSZ Transport Forwarding" -Parent "Transport Rules"
Get-ManagementRoleEntry "GSZ Transport Forwarding\*" | Measure-Object
Get-ManagementRoleEntry "GSZ Transport Forwarding\*" |
Where-Object { $_.Name -notlike "*Get-TransportRule" -and
$_.Name -notlike "*New-TransportRule" -and
$_.Name -notlike "*Set-TransportRule" -and
$_.Name -notlike "*Remove-TransportRule" -and
$_.Name -notlike "*Enable-TransportRule" -and
$_.Name -notlike "*Disable-TransportRule" } |
Remove-ManagementRoleEntry -Confirm:$false
Get-ManagementRoleEntry "GSZ Transport Forwarding\*" | Format-Table Name -AutoSize
Get-ManagementRoleEntry "GSZ Transport Forwarding\*" | Measure-Object
New-ManagementRoleAssignment -Role "GSZ Transport Forwarding" -User "msexmgmt@gsz.local"
Get-ManagementRoleAssignment -RoleAssignee "msexmgmt@gsz.local" | Where-Object {$_.RoleAssigneeType
-eq "User"} | Format-Table Role -AutoSize
# Добавим роль Reset Password, иначе либо AD, либо через сброс пароля при следующем логине (эта роль под scope - т.е. ограничить можно конкретными доменами)
New-ManagementRoleAssignment -Role "Reset Password" -User "msexmgmt@gsz.local" -CustomRecipientWriteScope "GSZ Domains"
Get-ManagementRoleAssignment -RoleAssignee "msexmgmt@gsz.local" | Where-Object {$_.RoleAssigneeType
-eq "User"} | Format-Table Role,CustomRecipientWriteScope -AutoSize
Да, можно ограничить доступ администратора только к определённым доменам через Role Based Access Control (RBAC) в Exchange.
Решение: Management Role Assignment
New-ManagementRole -Name "Domain Admin GSZ-DE" -Parent "Recipient Management"
New-ManagementRole -Name "Domain Admin GSZ-DE" -Parent "Mail Recipients"
New-ManagementScope -Name "GSZ-DE Domain" -RecipientRestrictionFilter
New-ManagementScope -Name "GSZ Domains" -RecipientRestrictionFilter {(EmailAddresses -like "@gabelstapler-zentrum.de") -or (EmailAddresses -like "@gabelstapler-gebraucht.at")}
New-Mailbox -Name "Admin GSZ-DE" -UserPrincipalName "admin-de@gabelstapler-zentrum.de" -Password (ConvertTo-SecureString "Password123!" -AsPlainText -Force)
New-ManagementRoleAssignment -Role "Domain Admin GSZ-DE" -User "admin-de@gabelstapler-zentrum.de" -CustomRecipientWriteScope "GSZ-DE Domain"
Add-RoleGroupMember -Identity "View-Only Organization Management" -Member "admin-de@gabelstapler-zentrum.de"
Проверка
Get-ManagementRoleAssignment -RoleAssignee "admin-de@gabelstapler-zentrum.de" | fl Role,RecipientWriteScope
Get-ManagementScope "GSZ-DE Domain" | fl
Что получится:
Дополнительные ограничения
Если нужно ограничить ещё больше:
New-ManagementRoleAssignment -Role "View-Only Recipients" -User "viewer@domain.com" -CustomRecipientWriteScope "GSZ-DE Domain"
New-ManagementRoleAssignment -Role "Distribution Groups" -User "groups-admin@domain.com" -CustomRecipientWriteScope "GSZ-DE Domain"
Это стандартный способ делегирования администрирования в Exchange - очень гибкий и правильный подход.
Enterprise Agreements: 4965437 Если не получается через Авто, то можно через веб - все придуманное, кроме Enterprise Agreements https://activate.microsoft.com/
🌐 Вариант 1: Exchange Online (облако)
Базовые планы (за пользователя/месяц):
Реалистичные планы (с Outlook desktop):
💰 Расчет для 50 пользователей (облако):
| План | Стоимость/год |
|---|---|
| Exchange Online Plan 1 (без Outlook desktop) | $2,400 |
| Exchange Online Plan 2 (без Outlook desktop) | $4,800 |
| Microsoft 365 Business Standard | $7,500 |
| Microsoft 365 E3 | $21,600 |
Дополнительные расходы (облако):
✅ Плюсы облака:
❌ Минусы облака:
🏢 Вариант 2: Exchange Server On-Premises (свой сервер)
ВНИМАНИЕ: С 2025 года Exchange Server переходит на Subscription Edition (SE) - подписочную модель!
Лицензирование: Server + CAL (на каждого пользователя/устройство)
Стоимость лицензий (2025, после повышения цен на 10-20%):
💰 Расчет для 50 пользователей (on-prem):
| Компонент | Стоимость/год |
|---|---|
| Exchange Server SE | $800 |
| Exchange Standard CAL x50 | $5,000-5,500 |
| Exchange Enterprise CAL x50 (опционально) | +$2,500-3,000 |
| Windows Server Standard (2 cores) | ~$1,000 |
| Windows Server CAL x50 | ~$2,000-2,500 |
| Итого лицензии: | $8,800-11,800/год |
Дополнительные расходы (on-prem):
Железо (начальные инвестиции):
Инфраструктура и обслуживание:
Обновления и поддержка:
✅ Плюсы on-premises:
❌ Минусы on-premises:
💡 Скрытые расходы (для обоих вариантов):
Email security/compliance:
Migration costs (если мигрируешь):
Training:
📊 Итоговое сравнение для 50 пользователей:
| Статья расходов | Exchange Online (облако) | Exchange On-Prem |
|---|---|---|
| Год 1 | $7,500-21,600 | $13,800-23,800 + железо ($5-12k) |
| Год 2-5 | $7,500-21,600/год | $8,800-11,800/год + электричество + админ |
| TCO за 5 лет | $37,500-108,000 | $62,000-95,000 + админ ($150-400k) |
🎯 Вывод и рекомендация:
Для большинства случаев Exchange Online дешевле и проще:
500 пользователей + compliance требования: on-premises может быть оправдан
❗ С 2025 года Microsoft активно удорожает on-premises лицензии (+10-20%), чтобы стимулировать переход в облако.
Альтернативы Exchange:
Диск с установкой Exchange:
Системный диск:
Диск с очередью сообщений (Message Queue):
Дополнительно:
Фактическое потребление места:
Exchange Server 2019 CU15 установка (без почты):
| Компонент | Размер |
|---|---|
| Exchange binaries (Program Files\Microsoft\Exchange Server\V15\Bin) | ~4-6 GB |
| Setup logs (ExchangeSetupLogs) | ~500 MB - 1 GB |
| Transport Queue (по умолчанию) | ~500 MB - 1 GB |
| IIS метабаза и конфигурации | ~200-500 MB |
| Временные файлы установки | ~2-3 GB (можно удалить после установки) |
| Windows prerequisites (NET Framework, VC++ redist, и т.д.) | ~3-5 GB |
| ИТОГО (после установки): | ~10-15 GB |
Во время установки:
Временные требования:
После установки можно удалить временные файлы и освободить ~25-35 GB.
Prerequisites (зависимости Windows):
Обязательные компоненты Windows:
| Компонент | Размер |
|---|---|
| .NET Framework 4.8 | ~500 MB |
| Visual C++ 2013 Redistributable | ~50 MB |
| Visual C++ 2012 Redistributable | ~50 MB |
| Unified Communications Managed API 4.0 | ~50 MB |
| URL Rewrite Module | ~10 MB |
| Windows Features (AD DS Tools, RPC over HTTP, RSAT) | ~500 MB - 1 GB |
| ИТОГО prerequisites: | ~1.5-2 GB |
Итоговые рекомендации по диску:
Для установки Exchange Server 2019 CU15 (без почтовых БД):
| Сценарий | Минимум | Рекомендуется |
|---|---|---|
| Только бинарники Exchange | 10 GB | 20 GB |
| Exchange + prerequisites + логи | 15 GB | 30 GB |
| С учетом обновлений и буфера | 20 GB | 50 GB |
Разбивка по дискам (best practice):
Рекомендуемая структура дисков:
C:\ (Системный диск) ├── Windows + prerequisites: ~20-30 GB └── Свободно для работы: ~20 GB └── ИТОГО: 50-60 GB
D:\ (Exchange binaries) ├── Exchange Server binaries: ~10 GB ├── Logs: ~5 GB └── Буфер: ~15 GB └── ИТОГО: 30-40 GB
E:\ (Mailbox databases) - НЕ считаем, это почта F:\ (Transaction logs) - НЕ считаем, это почта
🎯 Краткий ответ:
Для Exchange Server 2019 CU15 (только софт, без почты):
Windows Server prerequisites: +2-3 GB
ИТОГО для чистой установки Exchange 2019 CU15 с зависимостями: ~15-20 GB фактически, 50 GB рекомендуется.
Это без учета почтовых баз данных и логов транзакций (которые обычно на отдельных дисках).
Get-ExchangeServer | Format-List Name, Edition, AdminDisplayVersion или подробнее Get-ExchangeServer | Format-List Name, Edition, AdminDisplayVersion, IsExchangeTrialEdition
Get-ExchangeServer | Select-Object Name, Edition, IsExchangeTrialEdition, @{Name="ProductKey";Expression={(Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\ExchangeServer\v15\Setup").ProductKey}}
Редакции:
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\ExchangeServer\v15\Setup" | Select-Object ProductKey
Get-ExchangeServer | Where-Object {$.IsExchangeTrialEdition -eq $true} | Select-Object Name, @{Name="DaysRemaining";Expression={(New-TimeSpan -Start (Get-Date) -End $.TrialEditionExpiry).Days}}
Name DaysRemaining
GSZ-MAIL
На некоторых версия не пишется сколько осталось Тогда так: $installed = (Get-Item "C:\Program Files\Microsoft\Exchange Server\V15\Bin\ExSetup.exe").CreationTime; $elapsed = (New-TimeSpan -Start $installed -End (Get-Date)).Days; Write-Host "Дней с установки: $elapsed"; Write-Host "Осталось примерно: $(180 - $elapsed) дней"
Дней с установки: 321 Осталось примерно: -141 дней
На нелиц установке 141 дней превышения - это норма ))
Set-ExchangeServer -Identity "SERVER-NAME" -ProductKey "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX"
Set-ExchangeServer -Identity "GSZ-MAIL" -ProductKey "YCQY7-BNTF6-R337H-69FGX-P39TY"
Restart-Service MSExchangeIS Restart-Service MSExchangeServiceHost
Exchange НЕ проверяет CAL на уровне сервера - это honor system (честность). Но можно посмотреть подключенных пользователей:
Get-Mailbox -ResultSize Unlimited | Measure-Object
Get-Mailbox -RecipientTypeDetails UserMailbox | Measure-Object
Get-Mailbox -Filter {ArchiveGuid -ne $null} | Measure-Object # Archive Get-Mailbox -Filter {RetentionPolicy -ne $null} | Measure-Object # Retention
Get-WinEvent -LogName Application -Source "MSExchange*" | Where-Object {$.Message -like "license" -or $.Message -like "trial"} | Select-Object TimeCreated, Message | Format-List
Типичные проблемы:
Если Trial версия: Edition: Trial IsExchangeTrialEdition: True Нужно ввести Product Key для конвертации в Standard/Enterprise.
Если ProductKey пустой: Set-ExchangeServer -ProductKey "ВАRШ-КЛЮч-СЮДА"
После ввода ключа:
Запусти первую команду и покажи результат - разберёмся какая у тебя версия и активирована ли.
Этап 1: Внешний DNS (у регистратора)
gabelstapler-zentrum.ch. MX 10 mail.gabelstapler-zentrum.ch.
mail.gabelstapler-zentrum.ch. A <ВНЕШНИЙ IP Exchange>
autodiscover.gabelstapler-zentrum.ch. A <ВНЕШНИЙ IP Exchange>
gabelstapler-zentrum.ch. TXT "v=spf1 mx a:mail.gabelstapler-zentrum.ch -all"
gabelstapler-zentrum.de. MX 10 mail.gabelstapler-zentrum.ch.
autodiscover.gabelstapler-zentrum.de. A <ВНЕШНИЙ IP Exchange>
gabelstapler-zentrum.de. TXT "v=spf1 mx a:mail.gabelstapler-zentrum.ch -all"
Примечание: MX для .de указывает на тот же mail.gabelstapler-zentrum.ch - один Exchange обслуживает оба домена.
Что НЕ меняется:
| Запись | Действие |
|---|---|
| gabelstapler-zentrum.ch (A) | ❌ НЕ ТРОГАЕМ (сайт) |
| www.gabelstapler-zentrum.ch | ❌ НЕ ТРОГАЕМ |
| gabelstapler-zentrum.de (A) | ❌ НЕ ТРОГАЕМ (сайт) |
| www.gabelstapler-zentrum.de | ❌ НЕ ТРОГАЕМ |
Этап 2: AD DNS (на Domain Controller)
Поскольку все клиенты внешние, AD DNS нужен ТОЛЬКО для самого Exchange сервера.
Что нужно сделать:
Exchange должен резолвить свои собственные имена. Добавь записи в AD DNS:
$ExchangeIP = "YOUR_EXCHANGE_IP" # IP который видит сам Exchange
Add-DnsServerPrimaryZone -Name "gabelstapler-zentrum.ch" -ReplicationScope "Forest"
Add-DnsServerResourceRecordA -ZoneName "gabelstapler-zentrum.ch" -Name "mail" -IPv4Address $ExchangeIP Add-DnsServerResourceRecordA -ZoneName "gabelstapler-zentrum.ch" -Name "autodiscover" -IPv4Address $ExchangeIP
Add-DnsServerPrimaryZone -Name "gabelstapler-zentrum.de" -ReplicationScope "Forest"
Add-DnsServerResourceRecordA -ZoneName "gabelstapler-zentrum.de" -Name "autodiscover" -IPv4Address $ExchangeIP
Или проще - настрой Exchange использовать внешний DNS (Google/Cloudflare):
Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses "8.8.8.8","8.8.4.4"
Тогда AD DNS зоны для gabelstapler-zentrum.ch/.de не нужны - Exchange будет резолвить через внешний DNS.
Этап 3: Проверка DNS
Проверка с любого компьютера (или с Exchange сервера):
nslookup -type=MX gabelstapler-zentrum.ch 8.8.8.8 nslookup -type=MX gabelstapler-zentrum.de 8.8.8.8
nslookup mail.gabelstapler-zentrum.ch 8.8.8.8 nslookup autodiscover.gabelstapler-zentrum.ch 8.8.8.8 nslookup autodiscover.gabelstapler-zentrum.de 8.8.8.8
nslookup -type=TXT gabelstapler-zentrum.ch 8.8.8.8 Скорее так вернее nslookup -type=TXT gabelstapler-zentrum.ch
Ожидаемый результат:
Этап 4: Firewall / NAT (до настройки Exchange!)
Открыть порты на внешнем IP Exchange:
| Порт | Протокол | Назначение | Обязательно |
|---|---|---|---|
| 25 | TCP | SMTP (входящая почта) | ✅ ДА |
| 443 | TCP | HTTPS (OWA, ECP, EWS, ActiveSync, Autodiscover, MAPI) | ✅ ДА |
| 587 | TCP | SMTP Submission (отправка с аутентификацией) | ✅ ДА |
| 993 | TCP | IMAPS | ⚠️ Если нужен IMAP |
| 995 | TCP | POP3S | ⚠️ Если нужен POP3 |
Проверка портов:
telnet mail.gabelstapler-zentrum.ch 25 telnet mail.gabelstapler-zentrum.ch 443
Или онлайн: https://www.yougetsignal.com/tools/open-ports/
Этап 5: Настройка Exchange
5.1. Добавление Accepted Domains:
New-AcceptedDomain -Name "GSZ CH" -DomainName "gabelstapler-zentrum.ch" -DomainType Authoritative New-AcceptedDomain -Name "GSZ DE" -DomainName "gabelstapler-zentrum.de" -DomainType Authoritative
Get-AcceptedDomain
5.2. Email Address Policy:
New-EmailAddressPolicy -Name "GSZ Email Policy" -RecipientFilter "RecipientType -eq 'UserMailbox'" -EnabledEmailAddressTemplates "SMTP:%g.%s@gabelstapler-zentrum.ch", "smtp:%g.%s@gabelstapler-zentrum.de" ` -Priority 1
Update-EmailAddressPolicy -Identity "GSZ Email Policy"
Результат:
5.3. Настройка Virtual Directories (URLs):
$Server = (Get-ExchangeServer).Name
Set-OwaVirtualDirectory -Identity "$Server\OWA (Default Web Site)" -ExternalUrl "https://mail.gabelstapler-zentrum.ch/owa" -InternalUrl "https://mail.gabelstapler-zentrum.ch/owa"
Set-EcpVirtualDirectory -Identity "$Server\ECP (Default Web Site)" -ExternalUrl "https://mail.gabelstapler-zentrum.ch/ecp" -InternalUrl "https://mail.gabelstapler-zentrum.ch/ecp"
Set-ActiveSyncVirtualDirectory -Identity "$Server\Microsoft-Server-ActiveSync (Default Web Site)" -ExternalUrl "https://mail.gabelstapler-zentrum.ch/Microsoft-Server-ActiveSync" -InternalUrl "https://mail.gabelstapler-zentrum.ch/Microsoft-Server-ActiveSync"
Set-WebServicesVirtualDirectory -Identity "$Server\EWS (Default Web Site)" -ExternalUrl "https://mail.gabelstapler-zentrum.ch/EWS/Exchange.asmx" -InternalUrl "https://mail.gabelstapler-zentrum.ch/EWS/Exchange.asmx"
Set-OabVirtualDirectory -Identity "$Server\OAB (Default Web Site)" -ExternalUrl "https://mail.gabelstapler-zentrum.ch/OAB" -InternalUrl "https://mail.gabelstapler-zentrum.ch/OAB"
Set-MapiVirtualDirectory -Identity "$Server\mapi (Default Web Site)" -ExternalUrl "https://mail.gabelstapler-zentrum.ch/mapi" -InternalUrl "https://mail.gabelstapler-zentrum.ch/mapi"
Set-OutlookAnywhere -Identity "$Server\RPC (Default Web Site)" -ExternalHostname "mail.gabelstapler-zentrum.ch" -InternalHostname "mail.gabelstapler-zentrum.ch" -ExternalClientsRequireSsl $true -InternalClientsRequireSsl $true ` -DefaultAuthenticationMethod NTLM
5.4. Настройка Autodiscover:
Set-ClientAccessService -Identity $Server ` -AutoDiscoverServiceInternalUri "https://mail.gabelstapler-zentrum.ch/Autodiscover/Autodiscover.xml"
5.5. Проверка настроек:
Get-OwaVirtualDirectory | Select-Object Server, InternalUrl, ExternalUrl Get-EcpVirtualDirectory | Select-Object Server, InternalUrl, ExternalUrl Get-WebServicesVirtualDirectory | Select-Object Server, InternalUrl, ExternalUrl Get-ActiveSyncVirtualDirectory | Select-Object Server, InternalUrl, ExternalUrl Get-MapiVirtualDirectory | Select-Object Server, InternalUrl, ExternalUrl Get-OabVirtualDirectory | Select-Object Server, InternalUrl, ExternalUrl Get-OutlookAnywhere | Select-Object Server, InternalHostname, ExternalHostname Get-ClientAccessService | Select-Object Name, AutoDiscoverServiceInternalUri
Этап 6: SSL сертификат
6.1. Создание запроса на сертификат (CSR):
$CSR = New-ExchangeCertificate -GenerateRequest -SubjectName "CN=mail.gabelstapler-zentrum.ch, O=Gabelstapler Zentrum, L=Zurich, C=CH" -DomainName "mail.gabelstapler-zentrum.ch", "autodiscover.gabelstapler-zentrum.ch", "autodiscover.gabelstapler-zentrum.de" ` -PrivateKeyExportable $true
Set-Content -Path "C:\exchange_csr.txt" -Value $CSR
notepad C:\exchange_csr.txt
6.2. Получить сертификат:
Вариант A: Let's Encrypt (бесплатно)
Используй win-acme (https://www.win-acme.com/):
wacs.exe
Вариант B: Коммерческий сертификат
6.3. Импортировать сертификат:
Import-ExchangeCertificate -FileData ([System.IO.File]::ReadAllBytes("C:\certificate.pfx")) ` -Password (ConvertTo-SecureString -String "YourPassword" -AsPlainText -Force)
Import-ExchangeCertificate -FileData ([System.IO.File]::ReadAllBytes("C:\certificate.cer"))
6.4. Активировать сертификат:
Get-ExchangeCertificate | Format-List Subject, Thumbprint, Services, NotAfter
Thumbprint = (Get-ExchangeCertificate | Where-Object {_.Subject -like "mail.gabelstapler-zentrum.ch"}).Thumbprint
Enable-ExchangeCertificate -Thumbprint $Thumbprint -Services IIS,SMTP,POP,IMAP
6.5. Проверка сертификата:
Get-ExchangeCertificate | Where-Object {$_.Services -match "IIS"} | Format-List Subject, Thumbprint, Services, NotAfter
Или в браузере: открой https://mail.gabelstapler-zentrum.ch/owa и проверь замочек.
Этап 7: Send Connector (исходящая почта)
7.1. Создание Send Connector:
New-SendConnector -Name "Internet Send Connector" -Usage Internet -AddressSpaces "SMTP:*;1" -IsScopedConnector $false -DNSRoutingEnabled $true -UseExternalDNSServersEnabled $true -SourceTransportServers $Server
Get-SendConnector | Format-List Name, AddressSpaces, DNSRoutingEnabled
7.2. Настройка External DNS для SMTP:
Set-TransportService -Identity $Server -ExternalDNSServers "8.8.8.8","8.8.4.4"
Этап 8: Receive Connector (входящая почта)
8.1. Проверка существующих коннекторов:
Get-ReceiveConnector | Format-List Name, Bindings, RemoteIPRanges, PermissionGroups
8.2. Настройка коннектора для приема из интернета:
Connector = Get-ReceiveConnector | Where-Object {_.Name -like "Default Frontend"}
Set-ReceiveConnector -Identity $Connector.Identity ` -PermissionGroups AnonymousUsers, ExchangeServers, ExchangeLegacyServers
Get-ReceiveConnector -Identity $Connector.Identity | Format-List Name, PermissionGroups
Этап 9: Создание почтового ящика (тест)
New-Mailbox -Name "Test User" -UserPrincipalName "test@gabelstapler-zentrum.ch" -Alias "test" -FirstName "Test" -LastName "User" -Password (ConvertTo-SecureString "P@ssw0rd123!" -AsPlainText -Force) -ResetPasswordOnNextLogon $false
Get-Mailbox -Identity "test" | Select-Object -ExpandProperty EmailAddresses
Set-Mailbox -Identity "test" -EmailAddresses @
Этап 10: Тестирование
10.1. Тест OWA (браузер):
10.2. Тест Autodiscover:
Онлайн тест (рекомендую): 7. Открой: https://testconnectivity.microsoft.com/ 8. Выбери: "Outlook Connectivity" 9. Введи: test@gabelstapler-zentrum.ch и пароль 10. Запусти тест
Или через PowerShell на Exchange: Test-OutlookWebServices -Identity "test@gabelstapler-zentrum.ch" ` -MailboxCredential (Get-Credential)
10.3. Тест входящей почты:
10.4. Тест исходящей почты:
10.5. Тест мобильного клиента:
Этап 11: Дополнительные настройки (опционально)
11.1. DKIM (подпись писем):
New-DkimSigningConfig -DomainName "gabelstapler-zentrum.ch" -Enabled $true New-DkimSigningConfig -DomainName "gabelstapler-zentrum.de" -Enabled $true
Get-DkimSigningConfig -Identity "gabelstapler-zentrum.ch" | Format-List Selector1CNAME, Selector2CNAME
Добавь CNAME записи в DNS.
11.2. DMARC:
_dmarc.gabelstapler-zentrum.ch. TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@gabelstapler-zentrum.ch" _dmarc.gabelstapler-zentrum.de. TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@gabelstapler-zentrum.de"
11.3. Антиспам (если нужен):
Exchange 2019 имеет встроенный антиспам, но он отключен по умолчанию на Mailbox role.
& $env:ExchangeInstallPath\Scripts\Install-AntiSpamAgents.ps1 Restart-Service MSExchangeTransport
📊 Итоговая схема:
┌─────────────────────────────────────────────────────────┐ │ ВНЕШНИЙ DNS │ ├─────────────────────────────────────────────────────────┤ │ gabelstapler-zentrum.ch │ │ ├── A → Веб-сервер (НЕ МЕНЯЕТСЯ) │ │ ├── MX → mail.gabelstapler-zentrum.ch │ │ ├── mail → Внешний IP Exchange │ │ ├── autodiscover → Внешний IP Exchange │ │ ├── TXT (SPF) → "v=spf1 mx -all" │ │ └── TXT (DMARC) → "v=DMARC1; p=quarantine" │ │ │ │ gabelstapler-zentrum.de │ │ ├── A → Веб-сервер (НЕ МЕНЯЕТСЯ) │ │ ├── MX → mail.gabelstapler-zentrum.ch │ │ ├── autodiscover → Внешний IP Exchange │ │ ├── TXT (SPF) → "v=spf1 mx -all" │ │ └── TXT (DMARC) → "v=DMARC1; p=quarantine" │ └─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐ │ EXCHANGE SERVER │ ├─────────────────────────────────────────────────────────┤ │ Accepted Domains: │ │ - gabelstapler-zentrum.ch (Authoritative) │ │ - gabelstapler-zentrum.de (Authoritative) │ │ │ │ Email адреса пользователей: │ │ - user@gabelstapler-zentrum.ch (Primary) │ │ - user@gabelstapler-zentrum.de (Secondary) │ │ │ │ URLs: │ │ - https://mail.gabelstapler-zentrum.ch/owa │ │ - https://mail.gabelstapler-zentrum.ch/ecp │ │ │ │ SSL сертификат: │ │ - mail.gabelstapler-zentrum.ch │ │ - autodiscover.gabelstapler-zentrum.ch │ │ - autodiscover.gabelstapler-zentrum.de │ │ │ │ Порты (NAT/Firewall): │ │ - 25 (SMTP) │ │ - 443 (HTTPS) │ │ - 587 (Submission) │ └─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐ │ КЛИЕНТЫ (все внешние) │ ├─────────────────────────────────────────────────────────┤ │ Outlook (Windows/Mac): │ │ - Autodiscover автонастройка │ │ │ │ OWA (браузер): │ │ - https://mail.gabelstapler-zentrum.ch/owa │ │ │ │ Мобильные (iOS/Android): │ │ - ActiveSync через autodiscover │ └─────────────────────────────────────────────────────────┘
✅ Чеклист:
DNS:
Firewall:
Exchange:
SSL:
Тестирование:
Опционально:
Exchange Mailbox включает все антиспам функции без Edge Transport:
Заходим в Exchange Management Shell: & $env:ExchangeInstallPath\Scripts\Install-AntiSpamAgents.ps1
Restart-Service MSExchangeTransport
Доступные фильтры:
Set-ContentFilterConfig -Enabled $true -SCLRejectEnabled $true -SCLRejectThreshold 7 -SCLDeleteEnabled $true -SCLDeleteThreshold 9
Set-IPBlockListProvidersConfig -Enabled $true Add-IPBlockListProvider -Name "Spamhaus" -LookupDomain "zen.spamhaus.org" -Enabled $true
Set-SenderFilterConfig -Enabled $true -BlankSenderBlockingEnabled $true
IPBan через Failed Request Filtering (IIS):
Install-WindowsFeature Web-IP-Security
appcmd set config "Default Web Site/owa" /section:system.webServer/security/dynamicIpSecurity /denyAction:Deny /enableLoggingOnlyMode:false
appcmd set config "Default Web Site/owa" /section:dynamicIpSecurity /denyByConcurrentRequests.enabled:true /denyByConcurrentRequests.maxConcurrentRequests:5
Или IPBan (рекомендую):
Get-ReceiveConnector | Set-ReceiveConnector -PermissionGroups ExchangeServers,ExchangeLegacyServers
Set-ReceiveConnector "Default Frontend *" -RequireTLS $true
Set-TransportConfig -MaxReceiveSize 25MB -MaxSendSize 25MB
Итого для твоего случая:
Этого достаточно для малой/средней организации. Edge нужен только если требования высокие или есть DMZ.
Встроенные механизмы Exchange (неплохие):
a) Account Lockout Policy (на уровне AD) Да, это не fail2ban, но настраивается гибко:
Account Lockout Policy
Account lockout threshold: 5 попыток Account lockout duration: 30 минут (автоматическая разблокировка!) Reset account lockout counter after: 30 минут
b) Exchange Throttling Policy (встроенная защита от flood)
Get-ThrottlingPolicy | fl
New-ThrottlingPolicy -Name "Anti-Brute-Force" -EWSMaxConcurrency 3 -EWSPercentTimeInAD 50 -EWSPercentTimeInCAS 50 -EWSMaxSubscriptions 5 -OWAMaxConcurrency 5 -ImapMaxConcurrency 3 -PopMaxConcurrency 3 -RcaMaxConcurrency 5 ` -EwsCutoffBalance 3600000
Set-Mailbox -Identity "user@domain.com" -ThrottlingPolicy "Anti-Brute-Force"
c) IP Block List (аналог fail2ban)
Add-IPBlockListEntry -IPAddress 192.168.1.100 Add-IPBlockListEntry -IPRange 192.168.1.0/24
Get-IPBlockListEntry
Сторонние решения (аналоги fail2ban для Windows):
a) EvtxToElk + Elastic SIEM (бесплатный enterprise-подход)
b) IP Ban (открытый аналог fail2ban для Windows)
Invoke-WebRequest -Uri "https://github.com/DigitalRuby/IPBan/releases/latest/download/IPBan-Windows-x64.zip" -OutFile "IPBan.zip" Expand-Archive -Path "IPBan.zip" -DestinationPath "C:\IPBan" cd C:\IPBan .\IPBanService.exe install Start-Service IPBan
Конфиг C:\IPBan\ipban.config:
c) WAIL (Windows Advanced Intrusion Lockout) - платный, но мощный
d) Использовать Cloudflare / AWS WAF перед Exchange (если публичный доступ)
IPBan имеет встроенный веб-интерфейс:
Включить в конфигурации:
Файл: C:\Program Files\IPBan\DigitalRuby.IPBan.dll.config или ipban.config
<appSettings>
<!-- Включить HTTP сервер -->
<add key="UseHttpServer" value="true"/>
<add key="HttpServerPort" value="52664"/>
<!-- Опционально: авторизация -->
<add key="HttpServerApiKey" value="your-secret-key-here"/>
</appSettings>
Перезапустить службу: Restart-Service IPBan
Разрешаем в файерволе коннекты New-NetFirewallRule -DisplayName "IPBan HTTP" -Direction Inbound -LocalPort 52664 -Protocol TCP -Action Allow Get-NetFirewallRule -DisplayName "IPBan" | Select DisplayName, Enabled
Проверка, что слушает на порту (должен быть протокол TCP) netstat -ano | findstr 52664 Get-NetTCPConnection -LocalPort 52664 -ErrorAction SilentlyContinue
Доступ:
● API ключ - это пароль для доступа к веб-интерфейсу IPBan.
Зачем нужен:
Чтобы не кто попало мог заходить на http://server-ip:52664 и смотреть/управлять блокировками.
Как задать:
В файле конфигурации ipban.config:
<!-- Любая строка - это твой пароль -->
<add key="HttpServerApiKey" value="MySecretPassword123!"/>
Как использовать:
Через браузер:
При открытии http://localhost:52664 появится окно авторизации:
Через PowerShell/API:
Invoke-RestMethod -Uri "http://localhost:52664/api/banned" -Headers @
Можно ли без ключа?
Да, просто не указывай HttpServerApiKey:
Но тогда любой кто знает IP сервера сможет зайти на веб-интерфейс.
Рекомендация: Задай любой сложный пароль для безопасности.
Get-NetFirewallRule -DisplayName "IPBan*" | ForEach-Object {
$filter = $_ | Get-NetFirewallAddressFilter [PSCustomObject]@{ Name = $_.DisplayName Enabled = $_.Enabled BlockedIPs = ($filter.RemoteAddress -join ", ") }} | Format-Table -AutoSize
Файлы логов: C:\Program Files\IPBan\logfile.txt C:\ProgramData\IPBan\logfile.txt
Просмотр в реальном времени:
Get-Content "C:\Program Files\IPBan\logfile.txt" -Wait -Tail 50
Get-Content "C:\Program Files\IPBan\logfile.txt" | Select-String "banned"
Все правила IPBan:
Get-NetFirewallRule -DisplayName "IPBan*" | Get-NetFirewallAddressFilter
Get-NetFirewallRule -DisplayName "IPBan*" | ForEach-Object { $rule = $_ $filter = $_ | Get-NetFirewallAddressFilter [PSCustomObject]@{ Name = $rule.DisplayName Enabled = $rule.Enabled RemoteAddress = $filter.RemoteAddress } } | Format-Table -AutoSize
Get-NetFirewallRule -DisplayName "IPBan*" | Get-NetFirewallAddressFilter | Select RemoteAddress | Export-Csv -Path "C:\temp\ipban-blocked.csv" -NoTypeInformation
IPBan хранит данные в SQLite: C:\ProgramData\IPBan\ipban.sqlite
Запрос к БД:
sqlite3.exe "C:\ProgramData\IPBan\ipban.sqlite" "SELECT * FROM IPAddresses WHERE BanDate IS NOT NULL ORDER BY BanDate DESC LIMIT 100;"
Invoke-RestMethod -Uri "http://localhost:52664/api/banned" -Headers @
Invoke-RestMethod -Uri "http://localhost:52664/api/unban/1.2.3.4" -Method POST -Headers @
IPBan пишет в Event Log:
Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName='IPBan'} -MaxEvents 100 | Select TimeCreated, Message | Format-Table -AutoSize
Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName='IPBan'} -MaxEvents 100 | Where-Object {$_.Message -like "banned"}
Если уже настроил Grafana Alloy для Exchange логов, добавь IPBan логи:
loki.source.file "ipban" { targets = [ {path = "C:/Program Files/IPBan/logfile.txt"}, ] forward_to = [loki.write.default.receiver] }
Рекомендация:
Включи HTTP сервер IPBan - это самый удобный способ. Получишь:
Плюс логи в Loki для долгосрочного хранения и анализа.
Get-NetFirewallRule -DisplayName "IPBan*" | ForEach-Object { $filter = $_ | Get-NetFirewallAddressFilter [PSCustomObject]@{ Name = $*.DisplayName Enabled = filter.RemoteAddress -join ", ") } } | Format-Table -AutoSize
Get-Content "C:\Program Files\IPBan\logfile.txt" | Select-String "banned" | Select-Object -Last 50
Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName='IPBan'} -MaxEvents 100 | Where-Object {$_.Message -like "ban"}
Отправляй логи IPBan в Loki через Alloy - получишь визуализацию в Grafana.
Get-Content "C:\Program Files\IPBan\ipban.config" | Select-String "BanTime"
Get-Content "C:\ProgramData\IPBan\ipban.config" | Select-String "BanTime"
Формат BanTime:
Или через BanFile (постоянная блокировка):
Проверить текущее значение:
Get-Content "C:\Program Files\IPBan\ipban.config" | Select-String "BanTime|ExpireTime"
По умолчанию:
Если не указано - обычно 1 день (01:00:00:00).
Рекомендуемая защита для Exchange:
Минимум:
Дополнительно: 4. Multi-Factor Authentication (MFA) - лучшая защита 5. Geo-blocking через firewall (если доступ только из определенных стран) 6. Certificate-based auth для администраторов
Если пользователи НЕ логинятся в Windows (нет рабочих станций в домене), а работают ТОЛЬКО с почтой (Outlook/OWA), то:
✅ ПРОСТОЕ РЕШЕНИЕ - UPN Suffix!
Install-ADDSForest -DomainName "gabelstapler.local"
Get-ADForest | Set-ADForest -UPNSuffixes @
New-ADUser -Name "Info CH" -SamAccountName "info.ch" -UserPrincipalName "info@gabelstapler-zentrum.ch" -AccountPassword (ConvertTo-SecureString "Pass123" -AsPlainText -Force) -Enabled $true
New-ADUser -Name "Info AT" -SamAccountName "info.at" -UserPrincipalName "info@gabelstapler-gebraucht.at" -AccountPassword (ConvertTo-SecureString "Pass456" -AsPlainText -Force) -Enabled $true
New-ADUser -Name "Info DE" -SamAccountName "info.de" -UserPrincipalName "info@gabelstapler-zentrum.de" -AccountPassword (ConvertTo-SecureString "Pass789" -AsPlainText -Force) -Enabled $true
Enable-Mailbox "info.ch" Set-Mailbox "info.ch" -PrimarySmtpAddress "info@gabelstapler-zentrum.ch"
Enable-Mailbox "info.at" Set-Mailbox "info.at" -PrimarySmtpAddress "info@gabelstapler-gebraucht.at"
Enable-Mailbox "info.de" Set-Mailbox "info.de" -PrimarySmtpAddress "info@gabelstapler-zentrum.de"
🎯 Что получится:
Вход в OWA / Outlook:
Пользователь CH: Логин: info@gabelstapler-zentrum.ch Пароль: Pass123 Email: info@gabelstapler-zentrum.ch
Пользователь AT: Логин: info@gabelstapler-gebraucht.at Пароль: Pass456 Email: info@gabelstapler-gebraucht.at
Пользователь DE: Логин: info@gabelstapler-zentrum.de Пароль: Pass789 Email: info@gabelstapler-zentrum.de
Exchange видит UPN как логин! Для пользователей это выглядит как их email.
📧 Autodiscover будет работать правильно:
Outlook запрашивает: info@gabelstapler-zentrum.ch → autodiscover.gabelstapler-zentrum.ch → Exchange: находит пользователя с UPN = info@gabelstapler-zentrum.ch → Настройки профиля применяются
🔧 Полная настройка (пошагово):
Шаг 1: Создать AD Forest
Install-ADDSForest -DomainName "gabelstapler.local"
Шаг 2: Добавить UPN суффиксы
Get-ADForest | Set-ADForest -UPNSuffixes @
Get-ADForest | Select -Expand UPNSuffixes
Шаг 3: Установить Exchange
(Стандартная установка)
Шаг 4: Добавить Accepted Domains
New-AcceptedDomain -Name "CH" -DomainName "gabelstapler-zentrum.ch" -DomainType Authoritative New-AcceptedDomain -Name "AT" -DomainName "gabelstapler-gebraucht.at" -DomainType Authoritative New-AcceptedDomain -Name "DE" -DomainName "gabelstapler-zentrum.de" -DomainType Authoritative
Шаг 5: Создать пользователей
New-ADUser -Name "Info CH" -SamAccountName "info.ch" -UserPrincipalName "info@gabelstapler-zentrum.ch" -Path "CN=Users,DC=gabelstapler,DC=local" -AccountPassword (Read-Host "Password for CH" -AsSecureString) ` -Enabled $true
Enable-Mailbox "info.ch" Set-Mailbox "info.ch" -PrimarySmtpAddress "info@gabelstapler-zentrum.ch"
Шаг 6: Настроить DNS + SSL
gabelstapler-zentrum.ch MX 10 mail.gabelstapler-zentrum.ch. mail.gabelstapler-zentrum.ch A 203.0.113.10 autodiscover.gabelstapler-zentrum.ch CNAME mail.gabelstapler-zentrum.ch.
gabelstapler-gebraucht.at MX 10 mail.gabelstapler-gebraucht.at. mail.gabelstapler-gebraucht.at A 203.0.113.10 autodiscover.gabelstapler-gebraucht.at CNAME mail.gabelstapler-gebraucht.at.
✅ Результат:
Пользователи видят:
В AD на самом деле:
Один сервер, один AD домен, всё работает!
🎯 Это то что вам нужно?
Это стандартный сценарий для хостинг провайдеров Exchange!
Разбираем DNS - это важно.
🌐 Два типа DNS: Внутренний и Публичный
У вас Nginx снаружи, поэтому схема немного особенная.
📋 DNS Архитектура
Интернет ↓ Публичный DNS (у регистратора) ↓ Nginx (reverse proxy) - 203.0.113.10 ↓ Exchange Server - 10.0.0.20 (внутренний IP) ↑ Внутренний DNS (Windows DNS на DC) - 10.0.0.10
1️⃣ Публичный DNS (у регистратора доменов)
gabelstapler-zentrum.ch. MX 10 mail.gabelstapler-zentrum.ch.
mail.gabelstapler-zentrum.ch. A 203.0.113.10
autodiscover.gabelstapler-zentrum.ch. CNAME mail.gabelstapler-zentrum.ch.
webmail.gabelstapler-zentrum.ch. CNAME mail.gabelstapler-zentrum.ch.
gabelstapler-zentrum.ch. TXT "v=spf1 ip4:203.0.113.10 -all"
_dmarc.gabelstapler-zentrum.ch. TXT "v=DMARC1; p=none; rua=mailto:admin@gabelstapler-zentrum.ch"
Для gabelstapler-gebraucht.at:
gabelstapler-gebraucht.at. MX 10 mail.gabelstapler-gebraucht.at. mail.gabelstapler-gebraucht.at. A 203.0.113.10 autodiscover.gabelstapler-gebraucht.at. CNAME mail.gabelstapler-gebraucht.at. gabelstapler-gebraucht.at. TXT "v=spf1 ip4:203.0.113.10 -all"
gabelstapler-zentrum.de. MX 10 mail.gabelstapler-zentrum.de. mail.gabelstapler-zentrum.de. A 203.0.113.10 autodiscover.gabelstapler-zentrum.de. CNAME mail.gabelstapler-zentrum.de. gabelstapler-zentrum.de. TXT "v=spf1 ip4:203.0.113.10 -all"
Все A записи указывают на Nginx (203.0.113.10)
2️⃣ Внутренний DNS (Windows DNS на DC)
❌ НЕ НУЖНО создавать зоны для email доменов!
Почему: Это публичные домены, они резолвятся через публичный DNS.
✅ Что НУЖНО на Windows DNS:
Только зона для AD домена: gabelstapler.local (AD-integrated zone) ├─ _ldap._tcp.gabelstapler.local (автоматически) ├─ _kerberos._tcp.gabelstapler.local (автоматически) ├─ _gc._tcp.gabelstapler.local (автоматически) └─ dc1.gabelstapler.local A 10.0.0.10
Для email доменов на Windows DNS делать НИЧЕГО не нужно!
🔧 Настройка DNS на Exchange сервере
Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses 10.0.0.10,8.8.8.8
Важно: Exchange должен видеть DC через DNS для работы с AD.
🌍 Как работает резолв:
Внешний пользователь отправляет письмо на info@gabelstapler-zentrum.ch:
Пользователь настраивает Outlook:
Внутри сети (если Exchange запрашивает DNS):
Exchange Server резолвит: ├─ gabelstapler.local → через 10.0.0.10 (Windows DNS) └─ gabelstapler-zentrum.ch → через 8.8.8.8 (публичный DNS) или forwarder
📝 Практический чеклист DNS:
✅ На публичном DNS (у регистратора):
Для КАЖДОГО email домена: [x] MX запись → mail.domain.tld → A запись (Nginx IP) [x] A запись mail.domain.tld → 203.0.113.10 [x] CNAME autodiscover.domain.tld → mail.domain.tld [x] TXT SPF запись
✅ На Windows DNS (DC):
[x] Зона gabelstapler.local (создается автоматически при Install-ADDSForest) [x] A запись для DC: dc1.gabelstapler.local → 10.0.0.10 [x] (опционально) A запись для Exchange: exchange.gabelstapler.local → 10.0.0.20
❌ НЕ создавать на Windows DNS:
[-] Зоны для gabelstapler-zentrum.ch (ПУБЛИЧНЫЙ домен!) [-] Зоны для gabelstapler-gebraucht.at [-] Зоны для gabelstapler-zentrum.de
🔍 Проверка DNS:
Resolve-DnsName gabelstapler.local
Resolve-DnsName gabelstapler-zentrum.ch -Type MX
Resolve-DnsName mail.gabelstapler-zentrum.ch
Resolve-DnsName autodiscover.gabelstapler-zentrum.ch
🎯 Итого:
Windows DNS:
Публичный DNS (у регистратора):
Nginx:
Exchange:
Каждый child-домен должен быть на отдельном контролере домена Nginx reverse proxy уже настроен с certbot SSL Нужны отдельные пользователи: info в домене AT и info в домене CH (разные люди!)
Это требует AD Forest с child domains. Правильное решение для вашего случая.
🔧 Часть 1: Nginx + Certbot + Exchange
Ваша текущая конфигурация Nginx:
server { listen 443 ssl http2; server_name mail.gabelstapler-zentrum.ch autodiscover.gabelstapler-zentrum.ch;
ssl_certificate /etc/letsencrypt/live/mail.gabelstapler-zentrum.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mail.gabelstapler-zentrum.ch/privkey.pem;
location / {
proxy_pass https://exchange-server:443;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
server { listen 443 ssl http2; server_name mail.gabelstapler-gebraucht.at autodiscover.gabelstapler-gebraucht.at;
ssl_certificate /etc/letsencrypt/live/mail.gabelstapler-gebraucht.at/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mail.gabelstapler-gebraucht.at/privkey.pem;
location / {
proxy_pass https://exchange-server:443; # ТОТ ЖЕ Exchange!
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
stream { server { listen 25; proxy_pass exchange-server:25; } }
Получение сертификатов через certbot:
certbot certonly --nginx -d mail.gabelstapler-gebraucht.at -d autodiscover.gabelstapler-gebraucht.at certbot certonly --nginx -d mail.gabelstapler-zentrum.de -d autodiscover.gabelstapler-zentrum.de
certbot certificates
✅ Результат:
Nginx обрабатывает SSL, Exchange получает запросы с правильным Host header. Exchange понимает какой домен запрошен.
🌲 Часть 2: AD Forest с дочерними доменами
Архитектура для вашего случая:
AD Forest: gabelstapler.local (корневой домен)
├─ gabelstapler.local (root domain) │ └─ Служебные учетки (администраторы, сервисные аккаунты) │ ├─ ch.gabelstapler.local (child domain для .ch) │ ├─ DC: dc1-ch.ch.gabelstapler.local │ ├─ DC: dc2-ch.ch.gabelstapler.local (backup) │ └─ Users: │ ├─ info (login: ch\info или info@ch.gabelstapler.local) │ │ └─ Email: info@gabelstapler-zentrum.ch │ └─ verkauf (login: ch\verkauf) │ └─ Email: verkauf@gabelstapler-zentrum.ch │ └─ at.gabelstapler.local (child domain для .at) ├─ DC: dc1-at.at.gabelstapler.local ├─ DC: dc2-at.at.gabelstapler.local (backup) └─ Users: ├─ info (login: at\info или info@at.gabelstapler.local) │ └─ Email: info@gabelstapler-gebraucht.at └─ service (login: at\service) └─ Email: service@gabelstapler-gebraucht.at
Теперь у вас РАЗНЫЕ пользователи info в разных доменах!
🛠️ Пошаговая настройка
Шаг 1: Подготовка DNS (критично!)
gabelstapler.local. IN A 10.0.0.10 (DC root) _ldap._tcp.gabelstapler.local. IN SRV (автоматически AD)
ch.gabelstapler.local. IN NS dc1-ch.ch.gabelstapler.local. dc1-ch.ch.gabelstapler.local. IN A 10.0.1.10 _ldap._tcp.ch.gabelstapler.local. IN SRV (автоматически AD)
at.gabelstapler.local. IN NS dc1-at.at.gabelstapler.local. dc1-at.at.gabelstapler.local. IN A 10.0.2.10 _ldap._tcp.at.gabelstapler.local. IN SRV (автоматически AD)
Важно: DNS должен быть настроен ДО создания дочерних доменов!
Шаг 2: Создание дочернего домена CH
Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools
Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses 10.0.0.10
Install-ADDSDomain -NewDomainName "ch" -ParentDomainName "gabelstapler.local" -DomainType ChildDomain -InstallDns:true -Credential (Get-Credential "gabelstapler\\Administrator") \ -SafeModeAdministratorPassword (Read-Host "DSRM Password" -AsSecureString)
Restart-Computer
После перезагрузки: Домен ch.gabelstapler.local создан!
Шаг 3: Создание дочернего домена AT
Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses 10.0.0.10
Install-ADDSDomain -NewDomainName "at" -ParentDomainName "gabelstapler.local" -DomainType ChildDomain -InstallDns:true -Credential (Get-Credential "gabelstapler\\Administrator") \ -SafeModeAdministratorPassword (Read-Host "DSRM Password" -AsSecureString)
Restart-Computer
Шаг 4: Создание пользователей в дочерних доменах
New-ADUser -Name "Info CH" -SamAccountName "info" -UserPrincipalName "info@ch.gabelstapler.local" -Path "CN=Users,DC=ch,DC=gabelstapler,DC=local" -AccountPassword (Read-Host "Password" -AsSecureString) ` -Enabled $true
New-ADUser -Name "Info AT" -SamAccountName "info" -UserPrincipalName "info@at.gabelstapler.local" -Path "CN=Users,DC=at,DC=gabelstapler,DC=local" -AccountPassword (Read-Host "Password" -AsSecureString) ` -Enabled $true
Теперь у вас ДВА разных пользователя info!
Шаг 5: Exchange и дочерние домены
Exchange автоматически видит все домены в Forest!
Get-ADForest | Select-Object Domains
New-AcceptedDomain -Name "Zentrum CH" -DomainName "gabelstapler-zentrum.ch" -DomainType Authoritative New-AcceptedDomain -Name "Gebraucht AT" -DomainName "gabelstapler-gebraucht.at" -DomainType Authoritative
Enable-Mailbox -Identity "ch\info" -Alias "info.ch" Set-Mailbox -Identity "ch\info" -PrimarySmtpAddress "info@gabelstapler-zentrum.ch"
Enable-Mailbox -Identity "at\info" -Alias "info.at" Set-Mailbox -Identity "at\info" -PrimarySmtpAddress "info@gabelstapler-gebraucht.at"
Get-Mailbox | Select Name, PrimarySmtpAddress, Database
🔐 Аутентификация пользователей
Пользователи логинятся по-разному:
Пользователь из CH:
Пользователь из AT:
В Outlook: Вводят ch\info или at\info при настройке профиля.
📋 Минимальная инфраструктура
Серверы: ├─ DC Root (gabelstapler.local): │ └─ Windows Server (10.0.0.10) │ ├─ DC CH (ch.gabelstapler.local): │ ├─ Windows Server 1 (10.0.1.10) - primary DC │ └─ Windows Server 2 (10.0.1.11) - backup DC (рекомендуется) │ ├─ DC AT (at.gabelstapler.local): │ ├─ Windows Server 1 (10.0.2.10) - primary DC │ └─ Windows Server 2 (10.0.2.11) - backup DC (рекомендуется) │ └─ Exchange Server: └─ Может быть в любом домене (обычно в root) └─ Обслуживает ВСЕ домены Forest
Минимум: 4 сервера (1 root DC, 1 DC для CH, 1 DC для AT, 1 Exchange) Рекомендуется: 7 серверов (добавить backup DC для каждого домена)
🌐 DNS конфигурация (финальная)
Внутренний DNS (AD-integrated):
gabelstapler.local. IN A 10.0.0.10 _ldap._tcp.gabelstapler.local. IN SRV ...
ch.gabelstapler.local. IN NS dc1-ch.ch.gabelstapler.local. dc1-ch.ch.gabelstapler.local. IN A 10.0.1.10
at.gabelstapler.local. IN NS dc1-at.at.gabelstapler.local. dc1-at.at.gabelstapler.local. IN A 10.0.2.10
exchange.gabelstapler.local. IN A 10.0.0.20
Публичный DNS (для email):
gabelstapler-zentrum.ch. MX 10 mail.gabelstapler-zentrum.ch. mail.gabelstapler-zentrum.ch. A 203.0.113.10 (Nginx IP) autodiscover.gabelstapler-zentrum.ch. CNAME mail.gabelstapler-zentrum.ch.
gabelstapler-gebraucht.at. MX 10 mail.gabelstapler-gebraucht.at. mail.gabelstapler-gebraucht.at. A 203.0.113.10 (Nginx IP) autodiscover.gabelstapler-gebraucht.at. CNAME mail.gabelstapler-gebraucht.at.
✅ Проверка работы
Get-ADForest | Select Domains
Get-ADTrust -Filter *
Get-ADUser -Filter * -Server ch.gabelstapler.local | Select Name, UserPrincipalName Get-ADUser -Filter * -Server at.gabelstapler.local | Select Name, UserPrincipalName
Get-Mailbox | Select Name, PrimarySmtpAddress, Database, UserPrincipalName
Send-MailMessage -From "info@gabelstapler-zentrum.ch" -To "test@gmail.com" -Subject "Test CH" Send-MailMessage -From "info@gabelstapler-gebraucht.at" -To "test@gmail.com" -Subject "Test AT"
💡 Итого: Что вы получаете
✅ Отдельные AD домены: ch.gabelstapler.local, at.gabelstapler.local ✅ Одинаковые логины в разных доменах: ch\info и at\info (разные люди!) ✅ Разные email адреса: info@gabelstapler-zentrum.ch vs info@gabelstapler-gebraucht.at ✅ Один Exchange обслуживает все домены ✅ Nginx с certbot SSL для каждого домена ✅ Правильная структура для роста (можно добавлять домены)
⚠️ Что нужно учесть
Удалить домен и пересоздать структуру правильно - это разумное решение. Разбираем порядок действий.
🔍 Шаг 1: Проверяем текущую ситуацию
● Сначала нужно понять что у вас сейчас есть:
Get-ADDomain | Select Name, Forest, DomainMode
Get-ADDomainController -Filter * | Select Name, IPv4Address, OperatingSystem
Get-ExchangeServer | Select Name, ServerRole, AdminDisplayVersion
Get-AcceptedDomain | Format-Table Name, DomainName, DomainType, Default
Get-Mailbox | Select Name, PrimarySmtpAddress, Database
Get-EmailAddressPolicy | Select Name, Priority, EnabledEmailAddressTemplates
Запустите эти команды и покажите вывод - я скажу что нужно удалять.
🗑️ Шаг 2: Удаление из Exchange (СНАЧАЛА!)
● Критично: Exchange нужно очистить ДО удаления AD домена!
2.1. Удалить все mailbox пользователей
Get-Mailbox | Select Name, PrimarySmtpAddress
Get-Mailbox | Disable-Mailbox -Confirm:$false
Get-Mailbox | Remove-Mailbox -Permanent:false
⚠️ Внимание: -Permanent:$true удаляет mailbox навсегда (нельзя восстановить)!
2.2. Удалить Email Address Policies
Get-EmailAddressPolicy
Get-EmailAddressPolicy | Where-Object {_.Name -ne "Default Policy"} | Remove-EmailAddressPolicy -Confirm:false
2.3. Удалить Accepted Domains (кроме дефолтного)
Get-AcceptedDomain
Get-AcceptedDomain | Where-Object {$_.Default -eq false} | Remove-AcceptedDomain -Confirm:false
Важно: Домен помеченный как Default удалить нельзя. Его можно будет удалить только после демонтажа Exchange.
🔥 Шаг 3: Полное удаление Exchange (если нужно с нуля)
Вариант A: Если Exchange нужен дальше - пропустите этот шаг, оставьте Exchange как есть.
Вариант B: Если хотите переустановить Exchange - удаляем:
Get-Service Exchange | Stop-Service -Force
.\Setup.exe /Mode:Uninstall /IAcceptExchangeServerLicenseTerms_DiagnosticDataON
Get-ADObject -Filter 'ObjectClass -eq "msExchExchangeServer"' | Remove-ADObject -Recursive -Confirm:$false
⚠️ Если оставляете Exchange: Просто очистите mailbox и домены (шаг 2), Exchange останется для новой структуры.
Если удаляется домен AD и остается домен в Exchange (который дефолтный и который Exchange не дает удалить). В итоге Exchange ищет старый домен, падает и не дает обычными способами удалить себя (ибо проверки ошибок, а они есть). Создаем файл powershell exchange_remove_nuke.ps1
Write-Host "=== FORCE UNINSTALL EXCHANGE - SIMPLE ===" -ForegroundColor Red
# 1. Kill все процессы Exchange
Write-Host "Stopping all Exchange processes..." -ForegroundColor Yellow
Get-Process | Where-Object {$_.Name -like "*Exchange*"} | Stop-Process -Force -ErrorAction SilentlyContinue
# 2. Остановить и удалить службы
Write-Host "Removing Exchange services..." -ForegroundColor Yellow
Get-Service | Where-Object {$_.Name -like "*Exchange*"} | ForEach-Object {
Write-Host " Removing service: $($_.Name)" -ForegroundColor Gray
Stop-Service $_.Name -Force -ErrorAction SilentlyContinue
sc.exe delete $_.Name 2>&1 | Out-Null
}
# 3. Удалить файлы Exchange
Write-Host "Removing Exchange files..." -ForegroundColor Yellow
if (Test-Path "C:\Program Files\Microsoft\Exchange Server") {
Remove-Item "C:\Program Files\Microsoft\Exchange Server" -Recurse -Force -ErrorAction SilentlyContinue
}
if (Test-Path "C:\ExchangeSetupLogs") {
Remove-Item "C:\ExchangeSetupLogs" -Recurse -Force -ErrorAction SilentlyContinue
}
# 4. Удалить Registry
Write-Host "Cleaning Registry..." -ForegroundColor Yellow
if (Test-Path "HKLM:\SOFTWARE\Microsoft\ExchangeServer") {
Remove-Item "HKLM:\SOFTWARE\Microsoft\ExchangeServer" -Recurse -Force -Confirm:$false -ErrorAction
SilentlyContinue
}
if (Test-Path "HKLM:\SOFTWARE\Microsoft\Exchange") {
Remove-Item "HKLM:\SOFTWARE\Microsoft\Exchange" -Recurse -Force -Confirm:$false -ErrorAction SilentlyContinue
}
# Удалить службы из Registry
Write-Host "Removing Exchange services from Registry..." -ForegroundColor Yellow
Get-ChildItem "HKLM:\SYSTEM\CurrentControlSet\Services" -ErrorAction SilentlyContinue |
Where-Object {$_.PSChildName -like "*MSExchange*"} |
ForEach-Object {
Write-Host " Removing: $($_.PSChildName)" -ForegroundColor Gray
Remove-Item $_.PSPath -Recurse -Force -Confirm:$false -ErrorAction SilentlyContinue
}
# 5. Удалить IIS сайты через appcmd
Write-Host "Removing IIS sites and app pools..." -ForegroundColor Yellow
$appcmd = "$env:SystemRoot\System32\inetsrv\appcmd.exe"
if (Test-Path $appcmd) {
# Удалить сайты
& $appcmd list site 2>&1 | Out-String | Select-String 'SITE "([^"]*Exchange[^"]*)"' | ForEach-Object {
$siteName = $_.Matches.Groups[1].Value
Write-Host " Removing site: $siteName" -ForegroundColor Gray
& $appcmd delete site $siteName 2>&1 | Out-Null
}
# Удалить app pools
& $appcmd list apppool 2>&1 | Out-String | Select-String 'APPPOOL "([^"]*Exchange[^"]*)"' | ForEach-Object {
$poolName = $_.Matches.Groups[1].Value
Write-Host " Removing apppool: $poolName" -ForegroundColor Gray
& $appcmd delete apppool $poolName 2>&1 | Out-Null
}
}
# 6. Удалить через Programs
Write-Host "Removing via WMI (this may take a while)..." -ForegroundColor Yellow
$app = Get-WmiObject -Class Win32_Product -ErrorAction SilentlyContinue | Where-Object {$_.Name -like "*Exchange
Server*"}
if ($app) {
Write-Host " Uninstalling Exchange via MSI..." -ForegroundColor Yellow
$result = $app.Uninstall()
if ($result.ReturnValue -eq 0) {
Write-Host " MSI uninstall completed" -ForegroundColor Green
}
}
Write-Host ""
Write-Host "=== EXCHANGE REMOVAL COMPLETED ===" -ForegroundColor Green
Write-Host "*** REBOOT REQUIRED ***" -ForegroundColor Red
Write-Host ""
Write-Host "After reboot, verify with:" -ForegroundColor Cyan
Write-Host " Get-Service *Exchange*" -ForegroundColor White
Write-Host " Test-Path 'C:\Program Files\Microsoft\Exchange Server'" -ForegroundColor White
powershell -ExecutionPolicy Bypass -File exchange_remove_nuke.ps1
Restart-Computer
# После перезагрузки проверяем, что Exchange удалён
# Проверить службы (должно быть пусто)
Get-Service *Exchange*
# Проверить файлы (должно быть False)
Test-Path "C:\Program Files\Microsoft\Exchange Server"
# Проверить Registry (должно быть False)
Test-Path "HKLM:\SOFTWARE\Microsoft\ExchangeServer"
# Файлы могут остаться еще из-за того, что их держали, если остались, то удаляем уже после перезагрузки окончательно
Remove-Item "C:\Program Files\Microsoft\Exchange Server" -Recurse -Force
Чистим IIS создаем iis_clean.ps1
cd $env:SystemRoot\System32\inetsrv
# Удалить приложения
.\appcmd list app | Select-String "PowerShell|Exchange|owa|ecp|autodiscover|mapi|ActiveSync|EWS|OAB" |
ForEach-Object {
if ($_ -match 'APP "([^"]+)"') {
$appName = $matches[1]
Write-Host "Deleting app: $appName" -ForegroundColor Yellow
.\appcmd delete app $appName 2>&1 | Out-Null
}
}
# Удалить сайты
.\appcmd list site | Select-String "Exchange" | ForEach-Object {
if ($_ -match 'SITE "([^"]+)"') {
$siteName = $matches[1]
Write-Host "Deleting site: $siteName" -ForegroundColor Yellow
.\appcmd delete site $siteName 2>&1 | Out-Null
}
}
# Удалить app pools
.\appcmd list apppool | Select-String "Exchange" | ForEach-Object {
if ($_ -match 'APPPOOL "([^"]+)"') {
$poolName = $matches[1]
Write-Host "Deleting pool: $poolName" -ForegroundColor Yellow
.\appcmd delete apppool $poolName 2>&1 | Out-Null
}
}
iisreset /restart
Write-Host "Done!" -ForegroundColor Green
Запускаем
powershell -ExecutionPolicy Bypass -File exchange_remove_nuke.ps1
🗑️ Шаг 4: Демонтаж Domain Controller
● Если у вас ЕДИНСТВЕННЫЙ DC в домене (и вы хотите удалить весь Forest):
Ставим пароль, который будет у Администратора после перезагрузки $LocalAdminPassword = ConvertTo-SecureString "P@ssw0rd123" -AsPlainText -Force
Uninstall-ADDSDomainController -LocalAdministratorPassword true -ForceRemoval:true -IgnoreLastDnsServerForZone:false
Если у вас несколько DC:
Uninstall-ADDSDomainController -LocalAdministratorPassword (Read-Host "Local Admin Password" -AsSecureString) -Confirm:$false
Uninstall-ADDSDomainController -LocalAdministratorPassword (Read-Host "Local Admin Password" -AsSecureString) -LastDomainControllerInDomain:true` -Confirm:$false
🆕 Шаг 5: Создание новой структуры AD Forest
● 5.1. Создать корневой домен (Root Domain)
Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools
Install-ADDSForest -DomainName "gabelstapler.local" -DomainNetbiosName "GABELSTAPLER" -ForestMode "WinThreshold" -DomainMode "WinThreshold" -InstallDns:$true -SafeModeAdministratorPassword (Read-Host "DSRM Password" -AsSecureString) ` -Confirm:$false
Restart-Computer
5.2. Добавить второй DC для надежности (рекомендуется)
Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses 10.0.0.10
Install-ADDSDomainController -DomainName "gabelstapler.local" -InstallDns:$true -Credential (Get-Credential "GABELSTAPLER\Administrator") -SafeModeAdministratorPassword (Read-Host "DSRM Password" -AsSecureString)
Restart-Computer
5.3. Создать дочерний домен CH
Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses 10.0.0.10
Install-ADDSDomain -NewDomainName "ch" -ParentDomainName "gabelstapler.local" -DomainType ChildDomain -InstallDns:true -Credential (Get-Credential "GABELSTAPLER\\Administrator") \ -SafeModeAdministratorPassword (Read-Host "DSRM Password" -AsSecureString)
Restart-Computer
5.4. Создать дочерний домен AT
Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses 10.0.0.10
Install-ADDSDomain -NewDomainName "at" -ParentDomainName "gabelstapler.local" -DomainType ChildDomain -InstallDns:true -Credential (Get-Credential "GABELSTAPLER\\Administrator") \ -SafeModeAdministratorPassword (Read-Host "DSRM Password" -AsSecureString)
Restart-Computer
🔄 Шаг 6: Переустановка/настройка Exchange
Если Exchange остался после очистки (Вариант A):
.\Setup.exe /PrepareSchema /IAcceptExchangeServerLicenseTerms_DiagnosticDataON .\Setup.exe /PrepareAD /OrganizationName:"Gabelstapler" /IAcceptExchangeServerLicenseTerms_DiagnosticDataON .\Setup.exe /PrepareDomain /IAcceptExchangeServerLicenseTerms_DiagnosticDataON
New-AcceptedDomain -Name "CH" -DomainName "gabelstapler-zentrum.ch" -DomainType Authoritative New-AcceptedDomain -Name "AT" -DomainName "gabelstapler-gebraucht.at" -DomainType Authoritative
Если удалили Exchange (Вариант B):
.\Setup.exe /Mode:Install /Role:Mailbox /IAcceptExchangeServerLicenseTerms_DiagnosticDataON
✅ Финальная проверка
Get-ADForest | Select RootDomain, Domains
Get-ADTrust -Filter * | Select Source, Target, Direction, TrustType
Get-ExchangeServer
Get-AcceptedDomain
📋 Порядок действий (краткий чеклист)
⚠️ Важные моменты
У вас есть тестовая среда или это сразу production? Если production - рекомендую сначала потренироваться на виртуальных машинах.
Основные шаги:
Шаг 1: Установка роли AD DS (Active Directory Domain Services)
Через Server Manager (GUI):
Через PowerShell: Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools
Шаг 2: Повышение сервера до контроллера домена
После установки роли AD DS появится уведомление в Server Manager.
Шаг 3: Конфигурация домена
Для НОВОГО леса (первый контроллер): 10. Выбрать "Add a new forest" 11. Указать имя корневого домена (например: gsz.local или ad.gabelstapler-zentrum.ch) 12. Выбрать функциональный уровень леса и домена (Windows Server 2016 или выше) 13. Установить флажок DNS Server 14. Задать пароль для Directory Services Restore Mode (DSRM) 15. Путь к базе данных AD, логам и SYSVOL (можно оставить по умолчанию) 16. Проверить настройки и нажать Install
Через PowerShell (новый лес): Install-ADDSForest -DomainName "gsz.local" -DomainNetbiosName "GSZ" -ForestMode "WinThreshold" -DomainMode "WinThreshold" -InstallDns:$true -SafeModeAdministratorPassword (ConvertTo-SecureString "P@ssw0rd123!" -AsPlainText -Force) ` -Force:$true
Для добавления контроллера в СУЩЕСТВУЮЩИЙ домен: 17. Выбрать "Add a domain controller to an existing domain" 18. Указать имя домена 19. Предоставить учетные данные с правами Domain Admin 20. Выбрать сайт AD 21. Установить флажки DNS Server и Global Catalog 22. Задать DSRM пароль 23. Install
Через PowerShell (дополнительный DC): Install-ADDSDomainController -DomainName "gsz.local" -InstallDns:true ` -Credential (Get-Credential)` -SafeModeAdministratorPassword (ConvertTo-SecureString "P@ssw0rd123!" -AsPlainText -Force) \` -Force:true
Шаг 4: Перезагрузка
Сервер автоматически перезагрузится после установки.
Шаг 5: Первоначальная настройка AD
5.1. Настройка сайтов AD и подсетей:
Get-ADReplicationSite -Filter * | Rename-ADObject -NewName "MainOffice"
New-ADReplicationSubnet -Name "192.168.1.0/24" -Site "MainOffice"
Через GUI: 24. Открыть Active Directory Sites and Services (dssite.msc) 25. Развернуть Sites → Default-First-Site-Name 26. Правый клик → Rename 27. Правый клик на Subnets → New Subnet 28. Указать подсеть и выбрать сайт
5.2. Настройка зоны обратного просмотра DNS:
Add-DnsServerPrimaryZone -NetworkID "192.168.1.0/24" -ReplicationScope "Forest"
Через GUI: 29. Открыть DNS Manager (dnsmgmt.msc) 30. Развернуть сервер → Reverse Lookup Zones 31. Правый клик → New Zone 32. Выбрать Primary zone 33. Указать Network ID (192.168.1)
Шаг 6: Проверка установки
Get-ADDomain
Get-ADForest
Get-ADDomainController -Filter *
Get-DnsServerZone
repadmin /showrepl dcdiag /v
📊 Терминология AD (из статьи):
| Термин | Описание |
|---|---|
| Лес (Forest) | Верхний уровень иерархии AD, объединяет один или несколько доменов |
| Домен (Domain) | Логическая группа объектов (пользователи, компьютеры) с общей базой данных |
| Контроллер домена (DC) | Сервер с ролью AD DS, хранит копию базы данных AD |
| Сайт (Site) | Физическое расположение, связанное с IP-подсетями |
| Global Catalog | Частичная реплика всех объектов леса для быстрого поиска |
| DSRM | Режим восстановления служб каталогов |
⚠️ Важные примечания:
🔗 Связь с Exchange:
После установки AD можно переходить к: 39. PrepareSchema - расширение схемы AD для Exchange 40. PrepareAD - подготовка AD для Exchange 41. Установка Exchange Server
Предварительные требования:
Пошаговая инструкция:
Шаг 1: Проверка уровня функциональности леса и домена
На Domain Controller:
Get-ADForest | Select-Object ForestMode
Get-ADDomain | Select-Object DomainMode 3. Ожидаемый результат: - Windows2012R2Forest или выше - Windows2012R2Domain или выше 4. Если уровни ниже - повысь:
Set-ADForestMode -Identity your-domain.com -ForestMode Windows2012R2Forest
Set-ADDomainMode -Identity your-domain.com -DomainMode Windows2012R2Domain
Шаг 2: Проверка прав текущего пользователя
whoami /groups | findstr "Enterprise Admins" whoami /groups | findstr "Schema Admins" 2. Если НЕТ в группах - добавь себя:
Вариант B (через PowerShell): Add-ADGroupMember -Identity "Enterprise Admins" -Members "YourUsername" Add-ADGroupMember -Identity "Schema Admins" -Members "YourUsername"
Шаг 3: Подготовка схемы Active Directory (Schema Prep)
На сервере где будешь устанавливать Exchange (или на DC):
Шаг 4: Подготовка AD домена (Prepare AD)
Шаг 5: Подготовка всех доменов в лесу (если многодоменный лес)
Если у тебя несколько доменов в лесу:
Если один домен - этот шаг можно пропустить (PrepareAD уже подготовил текущий домен).
Шаг 6: Проверка репликации изменений AD
ВАЖНО: Подожди пока изменения реплицируются на все Domain Controllers!
Шаг 7: Проверка готовности AD
Get-ADObject -Filter {objectClass -eq 'msExchOrganizationContainer'} -SearchBase "CN=Configuration,DC=your-domain,DC=com"
Get-ADObject -Filter {Name -eq 'Microsoft Exchange System Objects'} 22. Проверь права:
Get-ADGroup -Filter {Name -like "Exchange"}
✅ Готово к установке Exchange!
После успешного выполнения всех шагов AD готов для установки Exchange Server 2019.
| Ошибка | Причина | Решение |
|---|---|---|
| Access Denied | Нет прав Schema/Enterprise Admin | Добавь себя в группы (Шаг 2) |
| Schema Master not available | Schema Master DC выключен | Включи Schema Master DC |
| Organization name invalid | Спецсимволы в имени | Используй только буквы/цифры |
| Forest level too low | Функциональный уровень <2012 R2 | Повысь уровень (Шаг 1) |
📝 Краткий чеклист:
Да, можно ограничить доступ администратора только к определённым доменам через Role Based Access Control (RBAC) в Exchange.
Решение: Management Role Assignment
New-ManagementRole -Name "Domain Admin GSZ-DE" -Parent "Recipient Management"
New-ManagementRole -Name "Domain Admin GSZ-DE" -Parent "Mail Recipients"
New-ManagementScope -Name "GSZ-DE Domain" -RecipientRestrictionFilter
New-ManagementScope -Name "GSZ Domains" -RecipientRestrictionFilter {(EmailAddresses -like "@gabelstapler-zentrum.de") -or (EmailAddresses -like "@gabelstapler-gebraucht.at")}
New-Mailbox -Name "Admin GSZ-DE" -UserPrincipalName "admin-de@gabelstapler-zentrum.de" -Password (ConvertTo-SecureString "Password123!" -AsPlainText -Force)
New-ManagementRoleAssignment -Role "Domain Admin GSZ-DE" -User "admin-de@gabelstapler-zentrum.de" -CustomRecipientWriteScope "GSZ-DE Domain"
Add-RoleGroupMember -Identity "View-Only Organization Management" -Member "admin-de@gabelstapler-zentrum.de"
Проверка
Get-ManagementRoleAssignment -RoleAssignee "admin-de@gabelstapler-zentrum.de" | fl Role,RecipientWriteScope
Get-ManagementScope "GSZ-DE Domain" | fl
Что получится:
Дополнительные ограничения
Если нужно ограничить ещё больше:
New-ManagementRoleAssignment -Role "View-Only Recipients" -User "viewer@domain.com" -CustomRecipientWriteScope "GSZ-DE Domain"
New-ManagementRoleAssignment -Role "Distribution Groups" -User "groups-admin@domain.com" -CustomRecipientWriteScope "GSZ-DE Domain"
Это стандартный способ делегирования администрирования в Exchange - очень гибкий и правильный подход.
The network ports that are required for email clients to access mailboxes and other services in the Exchange organization are described in the following diagram and table.
Notes:
| Purpose | Ports | Comments |
|---|---|---|
| Encrypted web connections are used by the following clients and services: - Autodiscover service - Exchange ActiveSync - Exchange Web Services (EWS) - Offline address book (OAB) distribution - Outlook Anywhere (RPC over HTTP) - Outlook MAPI over HTTP - Outlook on the web (formerly known as Outlook Web App) |
443/TCP (HTTPS) | For more information about these clients and services, see the following topics: - Autodiscover service in Exchange Server - Exchange ActiveSync - EWS reference for Exchange - Offline address books in Exchange Server - Outlook Anywhere - MAPI over HTTP in Exchange Server |
| Unencrypted web connections are used by the following clients and services: - Internet calendar publishing - Outlook on the web (redirect to 443/TCP) - Autodiscover (fallback when 443/TCP isn't available) |
80/TCP (HTTP) | Whenever possible, we recommend using encrypted web connections on 443/TCP to help protect data and credentials. However, you may find that some services must be configured to use unencrypted web connections on 80/TCP to the Client Access services on Mailbox servers. For more information about these clients and services, see the following topics: - Enable Internet Calendar Publishing - Autodiscover service in Exchange Server |
| IMAP4 clients | 143/TCP (IMAP), 993/TCP (secure IMAP) | IMAP4 is disabled by default. For more information, see POP3 and IMAP4 in Exchange Server. The IMAP4 service in the Client Access services on the Mailbox server proxies connections to the IMAP4 Backend service on a Mailbox server. |
| POP3 clients | 110/TCP (POP3), 995/TCP (secure POP3) | POP3 is disabled by default. For more information, see POP3 and IMAP4 in Exchange Server. The POP3 service in the Client Access services on the Mailbox server proxies connections to the POP3 Backend service on a Mailbox server. |
| SMTP clients (authenticated) | 587/TCP (authenticated SMTP) | The default Received connector named "Client Frontend " in the Front End Transport service listens for authenticated SMTP client submissions on port 587. Note: If you have email clients that are only able to submit authenticated SMTP email on port 25, you can modify the network adapter bindings of the client Receive connector to also listen for authenticated SMTP email submissions on port 25. |
How mail is delivered to and from your Exchange organization depends on your Exchange topology. The most important factor is whether you have a subscribed Edge Transport server deployed in your perimeter network.
The network ports that are required for mail flow in an Exchange organization that has only Mailbox servers are described in the following diagram and table.
| Purpose | Ports | Source | Destination | Comments |
|---|---|---|---|---|
| Inbound mail | 25/TCP (SMTP) | Internet (any) | Mailbox server | The default Receive connector named "Default Frontend " in the Front End Transport service listens for anonymous inbound SMTP mail on port 25. Mail is relayed from the Front End Transport service to the Transport service on a Mailbox server using the implicit and invisible intra-organization Send connector that automatically routes mail between Exchange servers in the same organization. For more information, see Implicit Send connectors. |
| Outbound mail | 25/TCP (SMTP) | Mailbox server | Internet (any) | By default, Exchange doesn't create any Send connectors that allow you to send mail to the internet. You have to create Send connectors manually. For more information, see Create a Send connector to send mail to the internet. |
| Outbound mail (if proxied through the Front End transport service) | 25/TCP (SMTP) | Mailbox server | Internet (any) | Outbound mail is proxied through the Front End Transport service only when a Send connector is configured with Proxy through Client Access server in the Exchange admin center or -FrontEndProxyEnabled $true in the Exchange Management Shell.In this case, the default Receive connector named "Outbound Proxy Frontend " in the Front End Transport service listens for outbound mail from the Transport service on a Mailbox server. For more information, see Configure Send connectors to proxy outbound mail. |
| DNS for name resolution of the next mail hop (not pictured) | 53/UDP,53/TCP (DNS) | Mailbox server | DNS server | See the Name resolution section in this topic. |
A subscribed Edge Transport server that's installed in your perimeter network affects mail flow in the following ways:
For more information, see Mail flow and the transport pipeline.
The network ports that are required for mail flow in Exchange organizations that have Edge Transport servers are described in the following diagram and table.
| Purpose | Ports | Source | Destination | Comments |
|---|---|---|---|---|
| Inbound mail - Internet to Edge Transport server | 25/TCP (SMTP) | Internet (any) | Edge Transport server | The default Receive connector named "Default internal Receive connector " on the Edge Transport server listens for anonymous SMTP mail on port 25. |
| Inbound mail - Edge Transport server to internal Exchange organization | 25/TCP (SMTP) | Edge Transport server | Mailbox servers in the subscribed Active Directory site | The default Send connector named "EdgeSync - Inbound to " relays inbound mail on port 25 to any Mailbox server in the subscribed Active Directory site. For more information, see Send connectors created automatically by the Edge Subscription. The default Receive connector named "Default Frontend " in the Front End Transport service on the Mailbox server listens for all inbound mail (including mail from Exchange 2013 or later Edge Transport servers) on port 25. |
| Outbound mail - Internal Exchange organization to Edge Transport server | 25/TCP (SMTP) | Mailbox servers in the subscribed Active Directory site | Edge Transport servers | Outbound mail always bypasses the Front End Transport service on Mailbox servers. Mail is relayed from the Transport service on any Mailbox server in the subscribed Active Directory site to an Edge Transport server using the implicit and invisible intra-organization Send connector that automatically routes mail between Exchange servers in the same organization. The default Receive connector named "Default internal Receive connector " on the Edge Transport server listens for SMTP mail on port 25 from the Transport service on any Mailbox server in the subscribed Active Directory site. |
| Outbound mail - Edge Transport server to internet | 25/TCP (SMTP) | Edge Transport server | Internet (any) | The default Send connector named "EdgeSync - to Internet" relays outbound mail on port 25 from the Edge Transport server to the internet. |
| EdgeSync synchronization | 50636/TCP (secure LDAP) | Mailbox servers in the subscribed Active Directory site that participate in EdgeSync synchronization | Edge Transport servers | When the Edge Transport server is subscribed to the Active Directory site, all Mailbox servers that exist in the site at the time participate in EdgeSync synchronization. However, any Mailbox servers that you add later don't automatically participate in EdgeSync synchronization. |
| DNS for name resolution of the next mail hop (not pictured) | 53/UDP,53/TCP (DNS) | Edge Transport server | DNS server | See the Name resolution section later in this topic. |
| Open proxy server detection in sender reputation (not pictured) | see comments | Edge Transport server | Internet | By default, sender reputation (the Protocol Analysis agent) uses open proxy server detection as one of the criteria to calculate the sender reputation level (SRL) of the source messaging server. For more information, see Sender reputation and the Protocol Analysis agent. Open proxy server detection uses the following protocols and TCP ports to test source messaging servers for open proxy: - SOCKS4, SOCKS5: 1081, 1080 - Wingate, Telnet, Cisco: 23 - HTTP CONNECT, HTTP POST: 6588, 3128, 80 Also, if your organization uses a proxy server to control outbound internet traffic, you need to define the proxy server name, type, and TCP port that sender reputation requires to access the internet for open proxy server detection. Alternatively, you can disable open proxy server detection in sender reputation. For more information, see Sender reputation procedures. |
DNS resolution of the next mail hop is a fundamental part of mail flow in any Exchange organization. Exchange servers that are responsible for receiving inbound mail or delivering outbound mail must be able to resolve both internal and external host names for proper mail routing. And all internal Exchange servers must be able to resolve internal host names for proper mail routing. There are many different ways to design a DNS infrastructure, but the important result is to ensure name resolution for the next hop is working properly for all of your Exchange servers.