Полная предаутентификация для Exchange с помощью Netscaler

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Однажды в службе информационной безопасности решили, что не только лишь все должны иметь доступ к электронной почте снаружи периметра. Начали искать способы и поняли, что нам нужен продукт класса ADC. Решили попробовать реализовать с помощью имеющего у нас Citrix Netscaler, который берёт на себя функцию аутентификации и проверят, можно ли данному сотруднику разрешить доступ в почтовую систему. Получилось. Не зря система такая дорогая. Будет длинная статья-инструкция.

Статей на тему того, как подружить Microsoft Excnage Server и Cirtix Netscaler не то, чтобы на каждом углу, но найти можно. Все найденные статьи не закрывали поставленную задачу. В основном статьи заканчивались на переключателе контента (переключатель контента можно реализовать на HAProxy). Некоторые статьи описывали перехват веб-формы. Нам же необходимо было запретить доступ на основе членства в группе Active Directory. Получается, что мы сами себе придумали, что мы хотим совместить аутентификацию и авторизацию в одном флаконе и на одном экране логина.

Виртуальные каталоги Exchange для веб-почты используют стандартную аутентификацию (не на основе форм). Т.е. и для внутренних и для внешних подключений используются одни и те же почтовые сервера.

Как-то так получилось, что я "осторожно, сертифицированный специалист" и по одному и по другому продукту. В общем, списывать было не у кого, пришлось делать домашку самому.

Давайте сразу договоримся, что далее в тексте будем использовать 198.51.100.0/24 для внешних (белых) сетей, 192.168.0.0/16 для внутренних сетей. Пространство имён example.com для имён, доступных из сети интернет и examle.local для именования внутренних серверов.

Принцип работы такой:

Content Switch принимает соединение и передаёт его соответствующему виртуальному серверу. Виртуальный сервер производит аутентификацию. Проверка членства в группе производится с помощью механизма многофакторной аутентификации. Второй фактор не требует участия пользователя и происходит фоном. Если проверка пройдена, то пускаем, если нет - показываем пользователю сообщение, что ему в доступе отказано.

Попутно защищаем пользователя от перебора пароля. Если честно, то именно защита от перебора пароля была для ИТ основной задачей, поскольку именно к ИТ приходят пользователи, которые сменили пароль и забыли поменять его на телефоне, тем самым вызывая блокировку учётной записи.

Схему взаимосвязи сущностей Netscaler постарался отразить на изображении ниже. Какие-то детали мог упустить

Пойдём уже настраивать Netscaler. Будем делать это поэтапно. Пояснения будут по ходу.

Пояснения

Citrix Netscaler с версии 13 называется Citrix ADC

Стоит отметить, что для Exchnage 2013 необходимо опубликовать и OWA и ECP, а для Exchange 2016/2019 только OWA

Если у вас гибрид и Teams, то нельзя делать предаутентификацию на EWS (перестанет работать календарь в Teams)

В нашем примере сервера Exchange именуются exch-server-nn и находятся в сети 192.168.9.0/24. Если у вас Exchange 2013, то exch-server-nn - это сервера с ролью ClientAccess. Внутренний домен EXAMPLE.LOCAL, внешнее имя mail.example.com

1 Основные настройки

Создадим базовые сущности, необходимые для работы, а именно сервера, сервис-группы, мониторы, переключатель контента

#Create Server
add server exch-server-01 192.168.9.1
add server exch-server-02 192.168.9.2

#Create Service Groups
add serviceGroup svcgrp_ex2019pub_owa SSL -cip ENABLED X-Forwarded-For
add serviceGroup svcgrp_ex2019pub_async SSL -cip ENABLED X-Forwarded-For
add serviceGroup svcgrp_ex2019pub_rpc SSL -cip ENABLED X-Forwarded-For
add serviceGroup svcgrp_ex2019pub_ews SSL -cip ENABLED X-Forwarded-For
add serviceGroup svcgrp_ex2019pub_autodisover SSL -cip ENABLED X-Forwarded-For
add serviceGroup svcgrp_ex2019pub_oab SSL -cip ENABLED X-Forwarded-For
add serviceGroup svcgrp_ex2019pub_mapi SSL -cip ENABLED X-Forwarded-For
add serviceGroup svcgrp_ex2019pub_ecp SSL -cip ENABLED X-Forwarded-For

#Create monitors
add lb monitor mon_exch_owa HTTP-ECV -send "GET /owa/healthcheck.htm" recv 200 -LRTM DISABLED -secure YES
add lb monitor mon_exch_async HTTP-ECV -send "GET /Microsoft-Server-ActiveSync/healthcheck.htm" recv 200 -LRTM DISABLED -secure YES
add lb monitor mon_exch_rpc HTTP-ECV -send "GET /rpc/healthcheck.htm" recv 200 -LRTM DISABLED -secure YES
add lb monitor mon_exch_ews HTTP-ECV -send "GET /ews/healthcheck.htm" recv 200 -LRTM DISABLED -secure YES
add lb monitor mon_exch_autodiscover HTTP-ECV -send "GET /Autodiscover/healthcheck.htm" recv 200 -LRTM DISABLED -secure YES
add lb monitor mon_exch_oab HTTP-ECV -send "GET /oab/healthcheck.htm" recv 200 -LRTM DISABLED -secure YES
add lb monitor mon_exch_mapi HTTP-ECV -send "GET /mapi/healthcheck.htm" recv 200 -LRTM DISABLED -secure YES
add lb monitor mon_exch_ecp HTTP-ECV -send "GET /ecp/healthcheck.htm" recv 200 -LRTM DISABLED -secure YES

Соединяем одно с другим, а именно линкуем сервера и мониторы к группам

#Bind Service Groups
#Replace FQDN and ip address regarding your environment
bind servicegroup svcgrp_ex2019pub_owa exch-server-01 443
bind servicegroup svcgrp_ex2019pub_owa exch-server-02 443
bind serviceGroup svcgrp_ex2019pub_owa -monitorName mon_mail_owa
bind servicegroup svcgrp_ex2019pub_async exch-server-01 443
bind servicegroup svcgrp_ex2019pub_async exch-server-02 443
bind servicegroup svcgrp_ex2019pub_async -monitorName mon_mail_async
bind servicegroup svcgrp_ex2019pub_rpc exch-server-01 443
bind servicegroup svcgrp_ex2019pub_rpc exch-server-02 443
bind servicegroup svcgrp_ex2019pub_rpc -monitorName mon_mail_rpc
bind servicegroup svcgrp_ex2019pub_ews exch-server-01 443
bind servicegroup svcgrp_ex2019pub_ews exch-server-02 443
bind servicegroup svcgrp_ex2019pub_ews -monitorName mon_mail_ews
bind servicegroup svcgrp_ex2019pub_autodisover exch-server-01 443
bind servicegroup svcgrp_ex2019pub_autodisover exch-server-02 443
bind servicegroup svcgrp_ex2019pub_autodisover -monitorName mon_mail_autodiscover
bind servicegroup svcgrp_ex2019pub_oab exch-server-01 443
bind servicegroup svcgrp_ex2019pub_oab exch-server-02 443
bind servicegroup svcgrp_ex2019pub_oab -monitorName mon_mail_oab
bind servicegroup svcgrp_ex2019pub_mapi exch-server-01 443
bind servicegroup svcgrp_ex2019pub_mapi exch-server-02 443
bind servicegroup svcgrp_ex2019pub_mapi -monitorName mon_mail_mapi
bind servicegroup svcgrp_ex2019pub_ecp exch-server-01 443
bind servicegroup svcgrp_ex2019pub_ecp exch-server-02 443
bind servicegroup svcgrp_ex2019pub_ecp -monitorName mon_mail_ecp

Создадим виртуальные сервера и прилинкуем к ним соответствующие сервисные группы

#Create Load Balancer
add lb vserver lb_vs_ex2019pub_owa SSL 0.0.0.0 0 -persistenceType NONE
add lb vserver lb_vs_ex2019pub_async SSL 0.0.0.0 0 -persistenceType SRCIPDESTIP
add lb vserver lb_vs_ex2019pub_rpc SSL 0.0.0.0 0 -persistenceType SOURCEIP -timeout 30
add lb vserver lb_vs_ex2019pub_ews SSL 0.0.0.0 0 -persistenceType NONE
add lb vserver lb_vs_ex2019pub_autodiscover SSL 0.0.0.0 0 -persistenceType NONE -timeout 30
add lb vserver lb_vs_ex2019pub_oab SSL 0.0.0.0 0 -persistenceType NONE
add lb vserver lb_vs_ex2019pub_mapi SSL 0.0.0.0 0 -persistenceType SOURCEIP -timeout 30
add lb vserver lb_vs_ex2019pub_ecp SSL 0.0.0.0 0 -persistenceType NONE

#Bind Service Groups to vServer
bind lb vserver lb_vs_ex2019pub_owa svcgrp_ex2019pub_owa
bind lb vserver lb_vs_ex2019pub_async svcgrp_ex2019pub_async
bind lb vserver lb_vs_ex2019pub_rpc svcgrp_ex2019pub_rpc
bind lb vserver lb_vs_ex2019pub_ews svcgrp_ex2019pub_ews
bind lb vserver lb_vs_ex2019pub_autodiscover svcgrp_ex2019pub_autodisover
bind lb vserver lb_vs_ex2019pub_oab svcgrp_ex2019pub_oab
bind lb vserver lb_vs_ex2019pub_mapi svcgrp_ex2019pub_mapi
bind lb vserver lb_vs_ex2019pub_ecp svcgrp_ex2019pub_ecp

#Bind SSL certificate
#Replace certificate name
bind ssl vserver lb_vs_ex2019pub_owa -certkeyName 'mail.example.com'
bind ssl vserver lb_vs_ex2019pub_async -certkeyName 'mail.example.com'
bind ssl vserver lb_vs_ex2019pub_rpc -certkeyName 'mail.example.com'
bind ssl vserver lb_vs_ex2019pub_ews -certkeyName 'mail.example.com'
bind ssl vserver lb_vs_ex2019pub_autodiscover -certkeyName 'mail.example.com'
bind ssl vserver lb_vs_ex2019pub_oab -certkeyName 'mail.example.com'
bind ssl vserver lb_vs_ex2019pub_mapi -certkeyName 'mail.example.com'
bind ssl vserver lb_vs_ex2019pub_ecp -certkeyName 'mail.example.com'

Переходим к чуть более интересному - переключатель контента. Создадим политики на основании URL и прилинкуем соответствующие политики к соответствующим виртуальным серверам. Адрес 198.51.100.100 - это белый адрес, доступный из сети интернет (на всякий случай отмечу, что сеть 198.51.100.0/24 используется в примерах в документации и запрещена к маршрутизации) В качестве действия по умолчанию будет веб-почта.

#Create Content Switch
#Replace IP address of Content Switch
add cs vserver cs_ex2019pub_http HTTP 198.51.100.100 80
add cs vserver cs_ex2019pub_ssl SSL 198.51.100.100 443

#Replace certificate name
bind ssl vserver cs_ex2019pub_ssl -certkeyName 'mail.example.com'

#Create Content Switch Policies
add cs action cs_act_ex2019pub_owa -targetLBVserver lb_vs_ex2019pub_owa
add cs policy cs_pol_ex2019pub_owa -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/owa")' -action cs_act_ex2019pub_owa
add cs action cs_act_ex2019pub_ews -targetLBVserver lb_vs_ex2019pub_ews
add cs policy cs_pol_ex2019pub_ews -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/ews")' -action cs_act_ex2019pub_ews
add cs action cs_act_ex2019pub_autodiscover -targetLBVserver lb_vs_ex2019pub_autodiscover
add cs policy cs_pol_ex2019pub_autodiscover -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/autodiscover")' -action cs_act_ex2019pub_autodiscover
add cs action cs_act_ex2019pub_async -targetLBVserver lb_vs_ex2019pub_async
add cs policy cs_pol_ex2019pub_async -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/Microsoft-Server-ActiveSync")' -action cs_act_ex2019pub_async
add cs action cs_act_ex2019pub_oab -targetLBVserver lb_vs_ex2019pub_oab
add cs policy cs_pol_ex2019pub_oab -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/oab")' -action cs_act_ex2019pub_oab
add cs action cs_act_ex2019pub_mapi -targetLBVserver lb_vs_ex2019pub_mapi
add cs policy cs_pol_ex2019pub_mapi -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/mapi")' -action cs_act_ex2019pub_mapi
add cs action cs_act_ex2019pub_rpc -targetLBVserver lb_vs_ex2019pub_rpc
add cs policy cs_pol_ex2019pub_rpc -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/rpc")' -action cs_act_ex2019pub_rpc
add cs action cs_act_ex2019pub_ecp -targetLBVserver lb_vs_ex2019pub_ecp
add cs policy cs_pol_ex2019pub_ecp -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/ecp")' -action cs_act_ex2019pub_ecp
add cs policy cs_pol_ex2019pub_cgi -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/cgi")' -action cs_act_ex2019pub_owa


#Bind Content Switch Policies
bind cs vserver cs_ex2019pub_ssl -policyName cs_pol_ex2019pub_owa -priority 110
bind cs vserver cs_ex2019pub_ssl -policyName cs_pol_ex2019pub_ews -priority 120
bind cs vserver cs_ex2019pub_ssl -policyName cs_pol_ex2019pub_autodiscover -priority 130
bind cs vserver cs_ex2019pub_ssl -policyName cs_pol_ex2019pub_async -priority 140
bind cs vserver cs_ex2019pub_ssl -policyName cs_pol_ex2019pub_oab -priority 150
bind cs vserver cs_ex2019pub_ssl -policyName cs_pol_ex2019pub_mapi -priority 160
bind cs vserver cs_ex2019pub_ssl -policyName cs_pol_ex2019pub_rpc -priority 170
bind cs vserver cs_ex2019pub_ssl -policyName cs_pol_ex2019pub_ecp -priority 180
bind cs vserver cs_ex2019pub_ssl -policyName cs_pol_ex2019pub_cgi -priority 190

#default action
bind cs vserver cs_ex2019pub_ssl -lbvserver lb_vs_ex2019pub_owa

Для 80 порта команды не сохранились, поскольку делались из веб-интерфейса. Суть заключается в том, что создаётся ответчик resp_act_redirect_http_https_all, который перенаправляет на тот же самый url меняя http на https. Создан виртуальный сервер lb_vs_redirect_http_https_all, к которому прилинкована политика этого ответчика. Должно быть примерно так:

#Replace http to https
add responder action resp_act_redirect_http_https_all redirect '"https://"+HTTP.REQ.HOSTNAME+"/"'
add responder policy resp_pol_redirect_http_https_all 'true' resp_act_owa

#default action
bind cs vserver cs_ex2019pub_http -lbvserver lb_vs_redirect_http_https_all

очень важно сохранить конфигурацию :)

save ns config

В общем-то, в данном виде система уже может быть использована. Хотя на сейчас она представляет собой обычный обратный прокси. Хорошо, продолжим усложнять и накручивать.

2 Сервер аутентификации и авторизации, политика ldap

svc_read_netscaler@example.local - сервисная учётная запись в домене с правами чтения домена (достаточно группы Domain Users)

192.168.3.11 - VIP адрес балансировщика LDAP, за которым отвечают несколько котроллеров домена (в простом случае адрес контроллера домена)

"&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(proxyAddresses=*)" - LDAP фильтр, который выдаёт включенные учётные записи пользователей, имеющих почту на Exchange

maaa.examle.com - это адрес с веб-формой, на который происходит перенаправление для проверки логина/пароля. Это тоже сущность Netscaler, может выступать в роли SSO в пределах Netscaler.

Создадим LDAP сервера и политики к этим серверам. С проверкой по sAMAccountName и с проверкой по UserPrincipalName. При этом два сервера с отключенной аутентификацией понадобятся на следующем этапе.

# Create LDAP servers
add ldapAction auth_srv_ldap_ex2019pub_SAM -serverIP "192.168.3.11" -serverPort "389" -secType "TLS" -svrType "AD" -authTimeout 3 -ldapBindDn
"svc_read_netscaler@example.local" -ldapBase "DC=example,DC=local" -authentication enabled -ldapLoginName "sAMAccountName" -passwdChange ENABLED -
searchFilter "&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(proxyAddresses=*)" -groupAttrName "memberOf" -
subAttributeName "cn" -ssoNameAttribute "userPrincipalName" -email "mail" -requireUser YES -validateServerCert NO -nestedGroupExtraction OFF -followReferrals ON -
maxLDAPReferrals 1 -referralDNSLookup A-REC -nestedGroupExtraction ON -maxNestingLevel 2 -groupNameIdentifier "sAMAccountName" -groupSearchAttribute "memberOf" -
groupSearchSubAttribute "cn" -ldapbindDnPassword

add ldapAction auth_srv_ldap_ex2019pub_UPN -serverIP "192.168.3.11" -serverPort "389" -secType "TLS" -svrType "AD" -authTimeout 3 -ldapBindDn
"svc_read_netscaler@example.local" -ldapBase "DC=example,DC=local" -authentication enabled -ldapLoginName "userPrincipalName" -passwdChange ENABLED -
searchFilter "&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(proxyAddresses=*)" -groupAttrName "memberOf" -
subAttributeName "cn" -ssoNameAttribute "userPrincipalName" -email "mail" -requireUser YES -validateServerCert NO -nestedGroupExtraction OFF -followReferrals ON -
maxLDAPReferrals 1 -referralDNSLookup A-REC -nestedGroupExtraction ON -maxNestingLevel 2 -groupNameIdentifier "sAMAccountName" -groupSearchAttribute "memberOf" -
groupSearchSubAttribute "cn" -ldapbindDnPassword

add ldapAction auth_srv_ldap_ex2019pub_SAM_noAuth -serverIP "192.168.3.11" -serverPort "389" -secType "TLS" -svrType "AD" -authTimeout 3 -ldapBindDn
"svc_read_netscaler@example.local" -ldapBase "DC=example,DC=local" -authentication DISABLED -ldapLoginName "sAMAccountName" -passwdChange DISABLED -
searchFilter "&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(proxyAddresses=*)" -groupAttrName "memberOf" -
subAttributeName "cn" -ssoNameAttribute "userPrincipalName" -email "mail" -requireUser YES -validateServerCert NO -nestedGroupExtraction OFF -followReferrals ON -
maxLDAPReferrals 1 -referralDNSLookup A-REC -nestedGroupExtraction ON -maxNestingLevel 2 -groupNameIdentifier "sAMAccountName" -groupSearchAttribute "memberOf" -
groupSearchSubAttribute "cn" -ldapbindDnPassword

add ldapAction auth_srv_ldap_ex2019pub_UPN_noAuth -serverIP "192.168.3.11" -serverPort "389" -secType "TLS" -svrType "AD" -authTimeout 3 -ldapBindDn
"svc_read_netscaler@example.local" -ldapBase "DC=example,DC=local" -authentication DISABLED -ldapLoginName "userPrincipalName" -passwdChange
DISABLED -searchFilter "&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(proxyAddresses=*)" -groupAttrName "memberOf" -
subAttributeName "cn" -ssoNameAttribute "userPrincipalName" -email "mail" -requireUser YES -validateServerCert NO -nestedGroupExtraction OFF -followReferrals ON -
maxLDAPReferrals 1 -referralDNSLookup A-REC -nestedGroupExtraction ON -maxNestingLevel 2 -groupNameIdentifier "sAMAccountName" -groupSearchAttribute "memberOf" -
groupSearchSubAttribute "cn" -ldapbindDnPassword


#Create LDAP advanced policy
add authentication policy advauth_pol_ldap_ex2019pub_SAM -rule true -action auth_srv_ldap_ex2019pub_SAM
add authentication policy advauth_pol_ldap_ex2019pub_UPN -rule true -action auth_srv_ldap_ex2019pub_UPN
add authentication policy advauth_pol_ldap_ex2019pub_SAM_noAuth -rule true -action auth_srv_ldap_ex2019pub_SAM_noAuth
add authentication policy advauth_pol_ldap_ex2019pub_UPN_noAuth -rule true -action auth_srv_ldap_ex2019pub_UPN_noAuth

Теперь создадим AAA сервер и привяжем к нему две из вышесозданных политик. Как раз этот AAA сервер занимается защитой от перебора пароля - это у AAA штатный функционал

#Create AAA VServer
add authentication vserver aaa_vs_ex2019pub_ldap SSL 0.0.0.0 -maxLoginAttempts 3 -failedLoginTimeout 5
bind ssl vserver aaa_vs_ex2019pub_ldap -certkeyName 'mail.example.com'
bind authentication vserver aaa_vs_ex2019pub_ldap -portaltheme StoreFrontLogOnTheme


#Bind LDAP Policies to AAA vserver
bind authentication vserver aaa_vs_ex2019pub_ldap -policy advauth_pol_ldap_ex2019pub_SAM -priority 200 -gotoPriorityExpression NEXT
bind authentication vserver aaa_vs_ex2019pub_ldap -policy advauth_pol_ldap_ex2019pub_UPN -priority 210 -gotoPriorityExpression NEXT

Сервера созданы. Теперь определим политики сессий и прилинкуем их к AAA серверу

#Create AAA Session Policy
add tm sessionAction tm_act_ex2019pub_owa_sso -defaultAuthorization ALLOW -SSO ON -ssoDomain 'examle.local'

#add tm sessionPolicy tm_pol_ex2019pub_owa_sso_v2 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/owa/auth/logon.aspx")' tm_act_ex2019pub_owa_sso
add tm sessionPolicy tm_pol_ex2019pub_owa_sso 'ns_true' tm_act_ex2019pub_owa_sso

#Create SSO Form and Policies
add tm formSSOAction sso_profile_ex2019pub_owa -actionURL "/owa/auth.owa" -userField "username" -passwdField "password" -responsesize "60000" -ssoSuccessRule
'HTTP.RES.SET_COOKIE.COOKIE("cadata").VALUE("cadata").LENGTH.GT(70)' -nvtype DYNAMIC -submitMethod POST
add tm trafficAction traffic_prof_ex2019pub_owa -SSO ON -appTimeout 1 -formSSOAction sso_profile_ex2019pub_owa
add tm trafficAction traffic_prof_ex2019pub_owa_logout -InitiateLogout ON


add tm trafficPolicy traffic_pol_ex2019pub_owa 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/owa/auth/logon.aspx")' traffic_prof_ex2019pub_owa
add tm trafficPolicy traffic_pol_ex2019pub_owa_logout 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/owa/logoff.owa")' traffic_prof_ex2019pub_owa_logout

#Bind Traffic Session Policy to the AAA vServer
bind authentication vserver aaa_vs_ex2019pub_ldap -policy tm_pol_ex2019pub_owa_sso -priority 100 -gotoPriorityExpression NEXT

Далее необходимо добавить правило в переключатель контента, созданный а первой части, чтобы переключатель понимал, как поступать с запросами, идущими на maaa.examle.com

#Content Switch for AAA
#Replace AuthenticationHost FQDN
add cs action cs_act_ex2019pub_maaa -targetVserver aaa_vs_ex2019pub_ldap
add cs policy cs_pol_ex2019pub_maaa -rule 'HTTP.REQ.HOSTNAME.SET_TEXT_MODE(IGNORECASE).EQ("maaa.examle.com")' -action cs_act_ex2019pub_maaa
bind cs vserver cs_ex2019pub_ssl -policyName cs_pol_ex2019pub_maaa -priority 90

Осталось лёгким движением руки превратить брюки в шорты, а если это не получилось, то привязать созданный AAA сервер к ранее созданным (в первой части) виртуальным серверам. К веб почте и панели управления привязываем веб форму аутентификации, к остальным протоколам basic аутентификацию.

Снова отмечу, что в Exchange 2016/2019 не следует выпускать наружу ECP.

#Bind SSO Policies to the OWA vServer
bind lb vserver lb_vs_ex2019pub_owa -policyName traffic_pol_ex2019pub_owa -priority 100 -gotoPriorityExpression END -type REQUEST
bind lb vserver lb_vs_ex2019pub_owa -policyName traffic_pol_ex2019pub_owa_logout -priority 110 -gotoPriorityExpression END -type REQUEST

#Set FBA and 401 authentication
#Replace AuthenticationHost FQDN
set lb vserver lb_vs_ex2019pub_owa -Authentication ON -authnVsName aaa_vs_ex2019pub_ldap -AuthenticationHost maaa.examle.com
set lb vserver lb_vs_ex2019pub_ecp -Authentication ON -authnVsName aaa_vs_ex2019pub_ldap -AuthenticationHost maaa.examle.com
set lb vserver lb_vs_ex2019pub_async -authn401 ON -authnVsName aaa_vs_ex2019pub_ldap
set lb vserver lb_vs_ex2019pub_rpc -authn401 ON -authnVsName aaa_vs_ex2019pub_ldap
set lb vserver lb_vs_ex2019pub_ews -authn401 ON -authnVsName aaa_vs_ex2019pub_ldap
set lb vserver lb_vs_ex2019pub_oab -authn401 ON -authnVsName aaa_vs_ex2019pub_ldap
set lb vserver lb_vs_ex2019pub_mapi -authn401 ON -authnVsName aaa_vs_ex2019pub_ldap
set lb vserver lb_vs_ex2019pub_autodiscover -authn401 ON -authnVsName aaa_vs_ex2019pub_ldap

ну и конечно же сохраняемся save ns config, зря что ли в детстве в Doom играли.

Прекрасно! Настроен перехват аутентификации веб формой и basic. Но и эта реализация требует дальнейшей доработки, поскольку basic нас не устраивает, мы же возомнили себе, что мы специалисты и нам надо всё делать правильно. Поэтому продолжаем усложнять.

3 Negotiate

Кириллические логины НЕ работают, только латиница. Реализуем перехват аутентификации по NTLM, который необходим для того, чтобы в дальнейшем настроить авторизацию. Для этого использовал функционал двухфакторной аутентификации. Второй фактор скрыт от пользователя и представляет из себя извлечение групп на учётной записи. Применён на сервисы RPC, OAB, EWS, MAPI, AutoDiscover

svc_masa19 - учётная запись в домене с правами ограниченного делегирования.

https://mail.example.local/mapi/ - ресурс, по которому доступен почтовый сервер. (В данной статье не описано, но суть в том, что имя mail.example.local живёт на балансировщике и за этим именем отвечает несколько серверов)

Приступим. Создадим в Active Directory учётную запись svc_masa19 (для любопытных это расшифровывается как mail alternate service account)

На Nеtscaler создадим KCD аккаунт и политику к нему. Здесь в примере будет способ заведения учётной записи по паролю, хотя в приличных обществах такие учётные записи принято передавать в стороннюю систему с помощью keytab файла, что вполне себе поддерживает Netscaler.

# Create kerberos account and session policy for SSO
add aaa kcdAccount kcd_acc_svc_masa19 -realmStr EXAMPLE.LOCAL -delegatedUser svc_masa19 -kcdPassword

add tm sessionAction tm_act_ex2019pub_kcd -defaultAuthorization ALLOW -SSO ON -ssoDomain 'EXAMPLE.LOCAL' -sessTimeout 10 -ssoCredential PRIMARY -httpOnlyCookie NO -kcdAccount kcd_acc_svc_masa19
add tm sessionPolicy tm_pol_ex2019pub_kcd 'ns_true' tm_act_ex2019pub_kcd

Дальше будет немного магии. Для начала создадим действие и политику согласования. Затем создадим схему схему логина и метки к ней. Прилинкуем к меткам LDAP без проверки подлинности, созданные на втором этапе. Это нужно для того, чтобы получить список групп на учётной записи.

После чего создадим ещё один AAA сервер, к которому прилинкуем проверку подлинности по согласованию и укажем, что в качестве второго фактора использовать созданную только что метку.

# Add Negotiate authentication advanced policy and action
add negotiateAction neg_srv_ex2019pub_svc_masa19 -domain EXAMPLE.LOCAL -domainUser svc_masa19 -NTLMPath "https://mail.example.local/mapi/" -
domainUserPasswd
add authentication policy advauth_pol_neg_ex2019pub_svc_masa19 -rule true -action neg_srv_ex2019pub_svc_masa19

# Create login schema and policy label
add authentication loginSchema login_ex2019pub_ldap -authenticationSchema noschema
add authentication policyLabel pol_lab_ex2019pub_ldap -loginSchema login_ex2019pub_ldap

#Bind LDAP policy to policy label
bind authentication policylabel pol_lab_ex2019pub_ldap -policyName advauth_pol_ldap_ex2019pub_SAM_noAuth -priority 200 -gotoPriorityExpression NEXT
bind authentication policylabel pol_lab_ex2019pub_ldap -policyName advauth_pol_ldap_ex2019pub_UPN_noAuth -priority 210 -gotoPriorityExpression NEXT

# Create a new AAA vserver that uses KCD and bind the cert
add authentication vserver aaa_vs_ex2019pub_negotiate_2fa SSL 0.0.0.0 -maxLoginAttempts 3 -failedLoginTimeout 5
bind ssl vserver aaa_vs_ex2019pub_negotiate_2fa -certkeyName 'mail.example.com'

# bind the negotiate and SSO policies to the new AAA Server
bind authentication vserver aaa_vs_ex2019pub_negotiate_2fa -policy tm_pol_ex2019pub_kcd -priority 100
bind authentication vserver aaa_vs_ex2019pub_negotiate_2fa -policy advauth_pol_neg_ex2019pub_svc_masa19 -priority 100 -gotoPriorityExpression NEXT -nextFactor pol_lab_ex2019pub_ldap

Остаётся прилинковать одно к другому, а именно AAA сервер аутентификации по согласованию прилинковать к виртуальным серверам RPC, MAPI, OAB, EWS, Autodiscover

# bind the new AAA Server to the Load balancers that need KCD
set lb vserver lb_vs_ex2019pub_mapi -authnVsName aaa_vs_ex2019pub_negotiate_2fa
set lb vserver lb_vs_ex2019pub_rpc -authnVsName aaa_vs_ex2019pub_negotiate_2fa
set lb vserver lb_vs_ex2019pub_ews -authnVsName aaa_vs_ex2019pub_negotiate_2fa
set lb vserver lb_vs_ex2019pub_oab -authnVsName aaa_vs_ex2019pub_negotiate_2fa
set lb vserver lb_vs_ex2019pub_autodiscover -authnVsName aaa_vs_ex2019pub_negotiate_2fa

помним про save ns config

Для учётной записи svc_masa19 необходимо произвести настройки. Добавить делегирование

На Exchange серверах необходимо разрешить аутентификацию согласованием

Get-OutlookAnywhere -server exch-server-01 | Set-OutlookAnywhere -IISAuthenticationMethods Negotiate,Ntlm,Basic

Казалось бы всё, задача решена. Если вдруг кто-то дочитал до этого места и всё ещё внимателен, то заметил, что до сих пор не решена задача от службы безопасности, а именно, пускать кого можно и не пускать, кого нельзя. Поэтому

4 Авторизация

В Active Directory есть группа deny_mail.example.com_access, членам которой запрещено подключаться к почте из вне.

Создадим политику авторизации и прилинкуем её ко всем виртуальным серверам, обслуживающим веб протоколы

#add and bind authorization policy
add authorization policy pol_auth_mail.example.com_deny "AAA.USER.IS_MEMBER_OF(\"deny_mail.example.com_access\")" DENY

# authorization on base and negotiate vservers
bind lb vs lb_vs_ex2019pub_async -policyName pol_auth_mail.example.com_deny -priority 100 -gotoPriorityExpression END -type REQUEST
bind lb vs lb_vs_ex2019pub_rpc -policyName pol_auth_mail.example.com_deny -priority 100 -gotoPriorityExpression END -type REQUEST
bind lb vs lb_vs_ex2019pub_ews -policyName pol_auth_mail.example.com_deny -priority 100 -gotoPriorityExpression END -type REQUEST
bind lb vs lb_vs_ex2019pub_oab -policyName pol_auth_mail.example.com_deny -priority 100 -gotoPriorityExpression END -type REQUEST
bind lb vs lb_vs_ex2019pub_mapi -policyName pol_auth_mail.example.com_deny -priority 100 -gotoPriorityExpression END -type REQUEST
bind lb vs lb_vs_ex2019pub_autodiscover -policyName pol_auth_mail.example.com_deny -priority 100 -gotoPriorityExpression END -type REQUEST

Добавим немного вежливости, уведомим пользователя, что ему отказано во входе. Для этого создадим простую html страничку

Перейти AppExpert\Responder\Responder HTML Pages
Вручную создать страницу mail.example.com_deny. Настаиваю не разгоняться с форматированием и обойтись текстом и тегами абзаца или обрыва строки.

После чего привяжем вывод этой страницы к соответствующим критериям

#add responder policy and action
add responder action resp_act_ex2019pub_denypage respondwithhtmlpage mail.example.com_deny -responseStatusCode 200 -reasonPhrase '\"Access denied. Contact to your servicedesk.\"'
add responder policy resp_pol_ex2019pub_denypage 'AAA.USER.IS_MEMBER_OF(\"deny_mail.example.com_access\") && HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(\"/owa/logoff.owa\").NOT' resp_act_ex2019pub_denypage

#bind policy to vservers
bind lb vs lb_vs_ex2019pub_owa -policyName resp_pol_ex2019pub_denypage -priority 100 -gotoPriorityExpression END -type REQUEST
bind lb vs lb_vs_ex2019pub_ecp -policyName resp_pol_ex2019pub_denypage -priority 100 -gotoPriorityExpression END -type REQUEST

save ns config

Вот теперь действительно готово


Ссылки, которые навели на решение

Не самой простой для чтения, но наиболее полной мне кажется эта статья

https://citrixguyblog.com/2017/07/22/citrix-netscaler-loadbalancing-exchange-20132016-walkthrough-guide/

Ещё неплохие статьи для понимая процесса

https://github.com/slauger/netscaler_docs/blob/master/exchange2016.adoc
https://support.citrix.com/article/CTX222454 - How to Configure the NetScaler for Kerberos Constrained Delegation

И ещё куча документации на сайте Citrix и на https://www.carlstalhood.com/

Источник: https://habr.com/ru/post/693428/


Интересные статьи

Интересные статьи

Привет, Хабр! В своей первой статье я бы хотел поделиться опытом в развертывании Spring Boot приложения. Но для начала небольшое отступление, которое должно ответить на вопросы зачем и почему.Недавно ...
Всем привет! Меня зовут Илья Козиев, я работаю в Управлении экспериментальных систем машинного обучения SberDevices над вопросами генерации текстового контента. В этой статье я хочу представить эффект...
Привет, друзья! В одной из предыдущих статей я рассказывал об оптимизации изображений с помощью Imgproxy и их кешировании на клиенте с помощью сервис-воркера. В этой статье я хочу рассказать ва...
Всем привет, я - Денис, Product Manager в банке, а до этого перезапускал вебинарную платформу VirtualRoom.Сегодня я расскажу про свой скрипт, который отмечал меня на лекциях в аспирантуре мехмата МГУ....
Введение Ansible — это мощная система управления конфигурацией, используемая для настройки и управления инфраструктурой и приложениями в различных окружениях. Хотя Ansible обеспечивает ...