image


Краткая суть:


Пошаговая инструкция по проксированию Unifi-контроллера через Nginx по http или https.


Конфиги, понятные примеры, комментарии.


Пролог:


Наша компания занимается разработкой мобильных приложений, и как вы могли догадаться, в офисе находится огромное количество различных девайсов, все они любят Wi-Fi и не просто Wi-Fi, а бесшовный, чтобы можно было свободно погулять по коридору, не прерывая разговоры по Skype, и просто наслаждаться всеми плодами непрерывной вафли.


Для этих целей решили взять несколько Unifi тарелок с целью не заморачиваться с настройкой и чтобы все работало из коробки. По-крупному все так и было за одним НО, который и послужил причиной создания этого tutorial: контроллер тарелок умеет работать только через https, а так же к нему не полагается нормальной инструкции по проксированию через Nginx, существующая, мягко говоря, оставляет желать лучшего, а так же не предусматривает сценарий работы с HTTP.


Требуемый уровень подготовки:


Будем считать, что азы Nginx известны, куда помещать конфиги и так понятно, а сам веб сервер тоже развернут. Кроме этого, подразумевается, что контроллер посуды уже настроен. Если с этим возникают сложности, то у Ubiquity есть отличная инструкция для Linux и Windows. Но в дальнейшем будем считать, что мы все здесь прожженные линуксоиды.


Вся конфигурация проверялась на Unifi Controller v5.2.9, Nginx v1.10.1, Debian Jessie, Java 8


И так, поехали.


HTTP:


Все просто, нужно только положить конфиг куда надо, но потребуются определенные пояснения и предупреждения.


Всем, кому лень думать читать, а просто надо, чтобы все работало, можно скопировать конфиг и заменить в нем server_name на подходящий, а так же адрес/порт в блоке upstream, если контроллер расположен не в локалхосте:


upstream unifi {
    server 127.0.0.1:8443;
}

server {
    listen 80;
    server_name unifi.domain.com;

    error_log /var/log/unifi/nginx.log;
    access_log /var/log/unifi/access.log;

    proxy_ssl_verify off;
    proxy_ssl_session_reuse on;
    proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    proxy_cache off;
    proxy_store off;

    server_tokens off;

    location / {
        proxy_set_header Referer "";
        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_pass https://unifi;   
        proxy_redirect https:// http://;
        header_filter_by_lua_block {
            if ngx.header["Set-Cookie"] then 
                ngx.header["Set-Cookie"] = { 
                    string.gsub(ngx.header["Set-Cookie"][1], "(.*)Secure;(.*)", "%1%2"),
                    string.gsub(ngx.header["Set-Cookie"][2], "(.*)Secure(.*)", "%1%2") 
                }
            end
        }
    }
}

Если Nginx собран из исходников, то нужно отдельно установить модуль ngx_http_lua_module.


Теперь можно перезагрузить Nginx:


sudo service nginx reload

Нюансы:
Так как контроллер из коробки поддерживает только HTTPS, то для его проксирования через HTTP нам придется модифицировать Cookie, отрезав флаг secure, и в принципе отключить любую валидацию ssl upstream сервера. Ушлые админы кинут в меня тапком, сказав, что можно добавить доверенный сертификат, предварительно прописав его в контроллер, но мы этот кейс рассматривать не будем в виду его избыточной сложности.


Тем не менее, не рекомендуется держать инфраструктурные вещи на HTTP. Только если совсем нет сертификата, а хочется, чтобы контроллер красиво торчал наружу.


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


HTTPS с дефолтным сертификатом контроллера:


Это ленивый вариант HTTPS. Контроллер имеет свой ssl сертификат из коробки, но его можно заменить на любой другой. И вместо того, чтобы его скурпулезно настраивать, мы просто попросим Nginx соглашаться на все.


Простыми словами, весь трафик идет из интернетов сети по HTTPS до Nginx, а от Nginx к контроллеру уже незащищенный. Если Unifi контроллер и Nginx находятся в пределах локалхоста или доверенной сети, то это наш случай:


upstream unifi {
    server 127.0.0.1:8443;
}

# Редиректим на HTTPS
server {
    listen       80;
    server_name  unifi.domain.com;
    server_tokens off;
    rewrite ^ https://$http_host$request_uri?;
}

server {
    listen 443 ssl;
    server_name unifi.domain.com;

    error_log /var/log/unifi/nginx.log;
    access_log /var/log/unifi/access.log;  

    ssl on;
    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 5m;
    add_header Strict-Transport-Security "max-age=31536000" always;
    server_tokens off;

    proxy_ssl_verify off;
    proxy_ssl_session_reuse on;
    proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    proxy_cache off;
    proxy_store off;

    location / {
        proxy_set_header Referer "";
        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_pass https://unifi;
    }
}

Дополнительные настройки

Можно сделать перманентный редирект, заменив


rewrite ^ https://$http_host$request_uri?;

на


rewrite ^ https://$http_host$request_uri? permanent;

Но лучше этим заниматься, когда вы убедились в правильной работе всей системы.


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


  • Путь до сертификата ssl_certificate
  • Путь до ключа сертификата ssl_certificate_key
  • Имя сервера обоих серверов(ssl и редирект) server_name
  • Порт и адрес upstream сервера при необходимости

И не забываем делать reload Nginx:


sudo service nginx reload

Вы можете спросить: "Но мэн, почему в твоем конфиге два сервера, хотя у меня он один?". Я отвечу: один сервер принимает ssl трафик, проксируя его контроллеру, а другой редиректит на первый, если к нам обратились по HTTP, чтобы не подвергать опасности наши дыры безопасности.


А теперь осталось самое сладкое, для настоящих параноиков серьезных администраторов.


HTTPS с кастомным сертификатом контроллера


Предположим, что Nginx и контроллер находятся теперь не в доверенной сети и мы не можем быть на 100% уверены в безопасности соединения между ними. В таком случае нам придется воспользоваться выданным ssl сертификатом для нашего домена на котором будет находиться контроллер, либо сгенерировать свой.


Процедуру генерации self signed сертификата мы описывать здесь не будем, она только косвенно касается данного tutorial. Считаем, что уже все готово и мы молодцы.


И так, убедимся, что у нас есть на руках crt и key файлы, они нам потребуются, чтобы подложить наш сертификат в Unifi контроллер. Будем считать, что они называются unifi.crt и unifi.key соответственно.


Теперь нам нужно сгенерировать keystore файл для контроллера. Следующие команды нужно выполнять в папке с unifi.crt и unifi.key в корне. Не важно, где лежит эта папка, нам потребуется в итоге только финальный файл:


openssl pkcs12 -export -in unifi.crt -inkey unifi.key -out unifi.p12

keytool -importkeystore -deststorepass aircontrolenterprise -destkeypass aircontrolenterprise -destkeystore keystore -srckeystore unifi.p12 -srcstoretype PKCS12 -srcstorepass aircontrolenterprise

Теперь берем полученный keystore и перемещаем его в папку data в корне контроллера. Дефолтное местоположение корневой папки: /usr/lib/unifi. Файл в итоге должен оказаться по следующему пути: /usr/lib/unifi/data/keystore


P12 документик нам не потребуется, можно его удалить или оставить на память, поставив в рамочку.


Теперь рестартим сервис Unifi:


sudo service unifi restart

С контроллером никаких манипуляций больше не потребуется, теперь дело за Nginx. Наш конфиг будет мало отличаться от предыдущего варианта, за исключением факта, что мы уже не будем слепо принимать любой сертификат. Если сертификат self signed, то укажем Nginx доверять ему.


Случай для выданного сертификата


Конфиг Nginx:


upstream unifi {
    server 127.0.0.1:8443;
}

server {
    listen       80;
    server_name  unifi.domain.com;
    server_tokens off;
    rewrite ^ https://$http_host$request_uri?;
}

server {
    listen 443 ssl;
    server_name unifi.domain.com;

    error_log /var/log/unifi/nginx.log;
    access_log /var/log/unifi/access.log;  

    ssl on;
    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 5m;
    add_header Strict-Transport-Security "max-age=31536000" always;
    server_tokens off;

    proxy_ssl_session_reuse on;

    location / {
        proxy_set_header Referer "";
        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_pass https://unifi;
    }
}

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


  • Путь до сертификата ssl_certificate
  • Путь до ключа сертификата ssl_certificate_key
  • Имя сервера обоих серверов(ssl и редирект) server_name
  • Порт и адрес upstream сервера при необходимости

Теперь self signed сертификат:


Нам потребуются два уже знакомых файла из прошлых шагов: unifi.crt и unifi.key. Для выданного сертификата они нам не требовались, так как Nginx умненький и умеет валидировать их в бюро сертифкации. Здесь же нам придется ему явно их указать.


Конфиг Nginx:


upstream unifi {
    server 127.0.0.1:8443;
}

server {
    listen       80;
    server_name  unifi.domain.com;
    server_tokens off;
    rewrite ^ https://$http_host$request_uri?;
}

server {
    listen 443 ssl;
    server_name unifi.domain.com;

    error_log /var/log/unifi/nginx.log;
    access_log /var/log/unifi/access.log;  

    ssl on;
    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 5m;
    add_header Strict-Transport-Security "max-age=31536000" always;
    server_tokens off;

    proxy_ssl_verify on;
    proxy_ssl_certificate     /etc/nginx/ssl/unifi.crt;
    proxy_ssl_certificate_key /etc/nginx/ssl/unifi.key;
    proxy_ssl_trusted_certificate /etc/nginx/ssl/unifi.crt;
    proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    proxy_ssl_session_reuse on;

    location / {
        proxy_set_header Referer "";
        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_pass https://unifi;
    }
}

Как и выше, не забываем заменять параметры конфига на свои:


  • Путь до сертификата ssl_certificate
  • Путь до ключа сертификата ssl_certificate_key
  • Имя сервера обоих серверов(ssl и редирект) server_name
  • Порт и адрес upstream сервера при необходимости
    А также
  • Путь до unifi.crt и unifi.key файлов (их можно положить куда угодно. Я бы рекомендовал сделать отдельную папку в корне Nginx, чтобы ничего не потерялось)

Теперь можно перезагрузить Nginx:


sudo service nginx reload



Вот и все, мы добились желаемого. Наш контроллер теперь доступен по указанному адресу.


Картинка с результатом

image


Буду рад ответить на любые вопросы и принять обратную связь.


P.S. Прочитав комментарии, хочу поблагодарить всех ответивших, приятно читать ваши отзывы.
Составлю мини-faq наиболее популярных вопросов:


1. Почему именно Nginx, а не %software_name%?
Ответ: Компания использовала Nginx, нужно было решать задачу через него.


2. Как статья связана с Unifi? Это же %common_techonology_name%!
Ответ: Целью было написать узкий туториал конкретно для Unifi + Nginx, так как далеко не все испытывающих сложности с его настройкой могут четко сформулировать нужный им стек технологий.

Поделиться с друзьями
-->

Комментарии (16)


  1. artemlight
    04.12.2016 12:39
    +1

    А чем haproxy не устроил?


    1. Mehdzor
      04.12.2016 16:01
      +1

      Так исторически сложилось, что в компании используется Nginx. Нужно было решать вопрос с имеющимися технологиями.
      Цель туториала — помочь в настройке именно Nginx.


  1. Zolg
    04.12.2016 13:16
    +4

    А в чем специфика конкретно UniFi?
    Статью можно спокойно озаглавить «использование nginx в качестве обратного прокси (с бонусными главами про ssl-stripping, ssl-offloading и аутентификацию бэкенда)». Коих миллион.


    1. Mehdzor
      04.12.2016 16:14

      Особенность Unifi в том, что он работает только с ssl. Большинство self-hosted софтин из коробки общаются по http и их настройка сводится к тому, чтобы проксировать им чистый трафик.

      Проще говоря, эта статья не для тех кто может сформулировать понятие ssl-stripping и ssl-offloading и знает их применение ;)

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


      1. VGusev2007
        05.12.2016 10:03

        Огромное спасибо за статью! Весьма актуально, например для нас. Если бы статья была бы более общего плана — было бы сложнее. А так, можно настроить все что нужно, и за одно и для других сервисов приспособить.


        1. Mehdzor
          05.12.2016 11:49

          Рад, что помогло.
          Приятно слышать, что намерение сделать узкий туториал оправдалось. Самого не раз выручали подобные статьи на хабре.


  1. Kostyanych
    04.12.2016 15:12
    +1

    Я не совсем понял проблему которую вы решаете… :(
    У меня Unifi Controller 5.2.9 работающий на CloudKey, все настройки по умолчанию.
    И у меня никак кроме как по https с ним работать не получается.


    1. Mehdzor
      04.12.2016 16:18

      Решаю проблему проксирования контроллера на кастомном железе через Nginx.
      CloudKey — это здоровская вещь, но мы посчитали ее лишней для нас.


  1. alexkulikovskikh
    04.12.2016 16:27

    Вопрос немного не по теме
    Насколько бесшовно происходит переход между точками доступа, с отсутствием поддержки 802.11r/k? Например во время звонка Skype и прогулки по офису.


    1. Mehdzor
      04.12.2016 16:30

      Боюсь, что я на него не смогу ответить полноценно. Но если коротко, то мы у себя не замечали сложностей со Skype.


  1. Markuzzz
    04.12.2016 20:49

    2 раза перечитал вводную часть поста, но так и не понял, зачем вот это вот все? Для ssl-offloading'а? Чтобы опубликовать контроллер с валидным сертификатом? Для каких-то целей ИБ? Интересуюсь потому что у самих тоже Unifi, но контроллер работает из коробки, за периметр не смотрит, а с self-signed сертификатом для целей администрирования можно смириться. Тарелки, кстати, общаются с контроллером по http на порту 8080/tcp, если верить netstat'у и tcpdump'у.


    1. Mehdzor
      04.12.2016 20:50

      Если контроллер за периметр не торчит, то вам это и ни к чему.
      Туториал о том как вытащить наружу контроллер безопасно (и не только) с помощью Nginx.


  1. deemru
    04.12.2016 20:50

    Слишком тяжело. Вы слышали про caddy?


    Вот пример конфигурации для домашнего Unifi (сертификат получается и обновляется автоматически):


    unifi.deem.ru {
        gzip
        header / Strict-Transport-Security "max-age=31536000"
        proxy / https://10.173.3.15:8443 {
            insecure_skip_verify
            transparent
        }
    }

    Данный конфиг даёт вот такой результат:


    image


    Пруф: https://www.ssllabs.com/ssltest/analyze.html?d=unifi.deem.ru


    1. Mehdzor
      04.12.2016 20:54

      На один из комментариев уже отвечал, что статья именно о NGINX(!). Разумеется есть и альтернативы, возможно, даже лучше подходящие для данного конкретного решения.
      P.S. Если опустить различные не дефолтные настройки для SSL, то получится не менее емко :)


  1. Tihon_V
    04.12.2016 22:57
    +2

    Действительно хочется кинуть тапком.

    Почему не поставить тот же certbot который будет раз в 3 дня обновлять валидный сертификат от Lets Encrypt и дергать по хуку «systemctl reload nginx»?!


    1. Mehdzor
      04.12.2016 23:17

      Идея классная) Даешь нескучные сертификаты!