Разворачивание HAproxy и настройка единой точки входа для RDP на основе ACL

Данная статья в кратце рассказывает как реолизовать единую точку входа используя HAproxy и ACL для RDP серверов.
Все делалось на базе Ubuntu Server. И на других дистрибутивах Linux могут требовать дополнительные шаги по установки.
Данная страница служит напоминием автору, как это реализованно для быстрого разворачивание подобного на другом железе и отладке работы уже существующих вариантов.

Установка HAproxy


Для быстрой установки HAproxy переходим на этот сайт. И выбираем, какая у нас ОСь и какой версии HAproxy нас интересует. Лучше всего выбирать версию 3.0 LTS.

Сайт


Получаем готовый набор команд для установки в терминале.
Первый и самый простой этап выполнен. Переходим на этап настройки Haproxy.

Конфигурация HAproxy


После установки идем в конфиг HAproxy для настройки. Он находиться по пути /etc/haproxy/haproxy.cfg
Пример конфига для единой точки входа:
Замените #IPrds* на IP своего RDS

global
    log 127.0.0.1 local0
    log 127.0.0.1 local1 notice
    maxconn 1000
    user haproxy
    group haproxy
    daemon
    stats socket /tmp/haproxy.sock
    ssl-server-verify none


defaults
    mode tcp
    option tcplog
    option tcp-smart-accept
    option tcp-smart-connect

frontend ft_rdp
  mode tcp
  bind *:3389 name rdp
 #timeout client 1h
  persist rdp-cookie
  log global
  option tcplog
  tcp-request inspect-delay 2s
  tcp-request content accept if RDP_COOKIE
  acl remoteRDSH01_ACL req.rdp_cookie(mstshash),bytes(0,19) -m str -i -f /etc/haproxy/remoteRDSH01
  acl remoteRDSH02_ACL req.rdp_cookie(mstshash),bytes(0,19) -m str -i -f /etc/haproxy/remoteRDSH02
  acl remoteRDSH03_ACL req.rdp_cookie(mstshash),bytes(0,19) -m str -i -f /etc/haproxy/remoteRDSH03
  acl remoteRDSH04_ACL req.rdp_cookie(mstshash),bytes(0,19) -m str -i -f /etc/haproxy/remoteRDSH04
  acl remoteRDS01_ACL req.rdp_cookie(mstshash),bytes(0,19) -m str -i -f /etc/haproxy/remoteRDS01
  acl remoteRDS02_ACL req.rdp_cookie(mstshash),bytes(0,19) -m str -i -f /etc/haproxy/remoteRDS02
  acl remoteRDS03_ACL req.rdp_cookie(mstshash),bytes(0,19) -m str -i -f /etc/haproxy/remoteRDS03
  acl remoteRDS04_ACL req.rdp_cookie(mstshash),bytes(0,19) -m str -i -f /etc/haproxy/remoteRDS04
  acl remoteRDS05_ACL req.rdp_cookie(mstshash),bytes(0,19) -m str -i -f /etc/haproxy/remoteRDS05
  acl remoteRDS06_ACL req.rdp_cookie(mstshash),bytes(0,19) -m str -i -f /etc/haproxy/remoteRDS06


  use_backend  remoteRDSH01_BK if remoteRDSH01_ACL
  use_backend  remoteRDSH02_BK if remoteRDSH02_ACL
  use_backend  remoteRDSH03_BK if remoteRDSH03_ACL
  use_backend  remoteRDSH04_BK if remoteRDSH04_ACL
  use_backend  remoteRDS01_BK if remoteRDS01_ACL
  use_backend  remoteRDS02_BK if remoteRDS02_ACL
  use_backend  remoteRDS03_BK if remoteRDS03_ACL
  use_backend  remoteRDS04_BK if remoteRDS04_ACL
  use_backend  remoteRDS05_BK if remoteRDS05_ACL
  use_backend  remoteRDS06_BK if remoteRDS06_ACL

  default_backend DF_RDP

backend remoteRDSH01_BK
  mode tcp
  balance leastconn
  log global
  stick-table type string len 32 size 10k expire 8h
  stick on req.rdp_cookie(mstshash),bytes(0,19)
  option tcplog
  tcp-check connect port 3389
  default-server inter 3s rise 2 fall 3
  server TS02 #IPrds1:3389 weight 10 check

backend remoteRDSH02_BK
  mode tcp
  balance leastconn
  log global
  stick-table type string len 32 size 10k expire 8h
  stick on req.rdp_cookie(mstshash),bytes(0,19)
  option tcplog
  tcp-check connect port 3389
  default-server inter 3s rise 2 fall 3
  server TS03 #IPrds2:3389 weight 10 check

backend remoteRDSH03_BK
  mode tcp
  balance leastconn
  log global
  stick-table type string len 32 size 10k expire 8h
  stick on req.rdp_cookie(mstshash),bytes(0,19)
  option tcplog
  tcp-check connect port 3389
  default-server inter 3s rise 2 fall 3
  server TS04 #IPrds3:3389 weight 10 check

backend remoteRDSH04_BK
  mode tcp
  balance leastconn
  log global
  stick-table type string len 32 size 10k expire 8h
  stick on req.rdp_cookie(mstshash),bytes(0,19)
  option tcplog
  tcp-check connect port 3389
  default-server inter 3s rise 2 fall 3
  server TS05 #IPrds4:3389 weight 10 check

backend remoteRDS01_BK
  mode tcp
  balance leastconn
  log global
  stick-table type string len 32 size 10k expire 8h
  stick on req.rdp_cookie(mstshash),bytes(0,19)
  option tcplog
  tcp-check connect port 3389
  default-server inter 3s rise 2 fall 3
  server TS06 #IPrds5:3389 weight 10 check

backend remoteRDS02_BK
  mode tcp
  balance leastconn
  log global
  stick-table type string len 32 size 10k expire 8h
  stick on req.rdp_cookie(mstshash),bytes(0,19)
  option tcplog
  tcp-check connect port 3389
  default-server inter 3s rise 2 fall 3
  server TS07 #IPrds6:3389 weight 10 check

backend remoteRDS03_BK
  mode tcp
  balance leastconn
  log global
  stick-table type string len 32 size 10k expire 8h
  stick on req.rdp_cookie(mstshash),bytes(0,19)
  option tcplog
  tcp-check connect port 3389
  default-server inter 3s rise 2 fall 3
  server TS08 #IPrds7:3389 weight 10 check

backend remoteRDS04_BK
  mode tcp
  balance leastconn
  log global
  stick-table type string len 32 size 10k expire 8h
  stick on req.rdp_cookie(mstshash),bytes(0,19)
  option tcplog
  tcp-check connect port 3389
  default-server inter 3s rise 2 fall 3
  server TS09 #IPrds8:3389 weight 10 check

backend remoteRDS05_BK
  mode tcp
  balance leastconn
  log global
  stick-table type string len 32 size 10k expire 8h
  stick on req.rdp_cookie(mstshash),bytes(0,19)
  option tcplog
  tcp-check connect port 3389
  default-server inter 3s rise 2 fall 3
  server TS10 #IPrds9:3389 weight 10 check

backend remoteRDS06_BK
  mode tcp
  balance leastconn
  log global
  stick-table type string len 32 size 10k expire 8h
  stick on req.rdp_cookie(mstshash),bytes(0,19)
  option tcplog
  tcp-check connect port 3389
  default-server inter 3s rise 2 fall 3
  server TS11 #IPrds10:3389 weight 10 check


backend DF_RDP
  mode tcp
  balance leastconn
  log global
  option tcplog
  stick-table type string len 32 size 10k expire 8h
  stick on rdp_cookie(mstshash),bytes(0,6)
  option tcp-check
  tcp-check connect port 3389
  default-server inter 3s rise 2 fall 3
  server TS01 #IPrds11:3389 weight 10 check

listen stats
 bind *:9000
 mode http
 stats enable
 #stats hide-version
 stats show-node
 stats realm Haproxy\ Statistics
 stats uri /


Теперь давайте объясню главные моменты, что написано в этом файле.



acl remoteRDSH##_ACL req.rdp_cookie(mstshash),bytes(0,19) -m str -i -f /etc/haproxy/remoteRDSHxx
Данная команда обозначает, что для для RDS## требуется RDP куки и смотрят первые 20 байтов и сравнивает их с файлом ACL



use_backend remoteRDSHxx_BK if remoteRDSHxx_ACL
Обозначает, что если RDP куки нашлась в ACL, то перенаправить на нужный RDS.



Так же в конфиге есть DF_RDP. Он нужен что бы отсылать всех пользовтелей, что нет ни в одном ACL отпровлять туда. Так же если его не будет то мы не сможите подключиться. Поэтому можите написать IP которого нет. Костыль? Да :)



И самое последние. Если вы откроите браузер и передйте на IP:9000 машины, где крутиться HAproxy вы получите статистику.

Статистика работы HAproxy

Создание ACL для HAproxy


Стоит учесть, что файлы ACL /etc/haproxy/remoteRDSHxx ДОЛЖНЫ быть созданы ЗАРАНЕЕ. Иначе HAproxy откажется запускаться. Файлы могут быть полностью пустыми. Так что для простоты просто используйте команду:


touch remoteRDSHxx


В RDP куки НЕ передается имя пользователя ЕСЛИ ОНО НА РУССКОМ. Создаем пользователей ТОЛЬКО на Английском.
Редактировать ACL на горячую нельзя. Изменения вступят в силу лишь ПОСЛЕ перезапуска HAproxy.



Заполнение ACL и как это делать правильно. Стоит сразу определить, что используют пользователи:

  • Windows с стандартным RDP-клиентом / Толстый клиент
  • Linux и его зоопарк RDP-клиентов / Тонкий клиент

Windows


Если у нас ПК не в домене. И подключение происходит просто по имени пользователя то проблем у нас нет. Можем спокойно открывать файлы с ACL и пишем всех пользователей с новой строчки. Как тут:

admin
1060Admin
960Admin


Если же у нас ПК в домене. То стоит учесть, стандартный RDP клиент Windows брезгует и при подключение к HAproxy не передает ИМЯ пользователя, а первые 9 СИМВОЛОВ. Если же проварчивать такое на прямую то клиент будет передавать имя пользователя.
Есть два решения данной проблемы:

  • Использовать АЛЬТЕРАНТИВНЫЙ клиент RDP под Windows (как пример mRemoteNG)
  • Сделать костыль со стороны ACL. (Быстрое решение проблемы, но неправильное)


С первым вариантом как бы все понятно. А второй требует внимания. В ACL мы будем писать не просто имя пользователя, а по вот такой форме:

Имя_Пользователя@Домен


Но мы должны оставить ТОЛЬКО 9 символов. Это лишь быстрое решение проблемы, стоит рассмотреть вариант смены RDP клиента.

Linux


Если же у нас Linux машина и/или тонкий клиент, то мы можем спокойно писать имя пользователя, не важно в домене ли ПК или нет. Клиента под Linux передают столько символов сколько напишут и ТОЛЬКО имя пользователя даже если пользователь писал домен.

Пример заполненго ACL