58. Самоподписанный SSL сертификат.


После настройки любого веб сервера или приложения, при его открытии браузер всегда предупреждает что соединение небезопасно. Большинство людей просто жмут на «Дополнительно» и "продолжить" и всё работает. НО! Если есть желание сделать всё правильно и защитить веб-интерфейсы и приложения в сети, то необходимо использовать SSL сертификаты. В этой статье разберём что это такое, почему важно настроить их правильно, и как это легко сделать во внутренней сети.

Теория.


Cертификат SSL является частью протокола TLS и у него есть две основные цели:
- шифрование соединения.
- подтверждение доверия между клиентом и сервером.


Практически в любом браузере при открытии веб-сайта можно заметить маленький значок замка
в адресной строке, который говорит о том, что соединение безопасно. Это значит что веб-сайт использует шифрование с https и можно доверять издателю этого ssl-сертификата.


Разберём строение и работу сертификата подробнее.
Это как виртуальные удостоверения личности для веб-сайтов. Когда заходим на защищённый сайт (по адресу "https://"), браузер смотрит на его сертификат и убеждается, что сайт действительно принадлежит тому, за кого себя выдает.
Сертификаты создаются по определенным правилам (стандарт x509), чтобы они были понятны для всех систем. Однако важно знать, что сертификаты работают в сочетании с шифрованием данных, чтобы обеспечить защиту информации и конфиденциальность. Например, логины, пароли или номера кредитных карт, передаются в зашифрованном виде.


Теперь об открытом и закрытом ключах.
У каждого сайта есть два ключа.
Открытый ключ - используется для шифрования информации, отправляемой клиентом на сервер. Этот ключ сервер выдаёт любому, кто хочет отправить ему сообщение. Т.е. открытый ключ любого сервера/сайта находится в свободном доступе.
Закрытый ключ - уникален для каждого сервера и известен только ему самому. Когда зашифрованное сообщение приходит от клиента, только сервер может его открыть, используя свой закрытый ключ.
Таким образом, с помощью сертификата SSL устанавливаются доверенные связи между клиентом и сервером, шифруются данные отправляемые клиентом, и могут быть расшифрованы только на сервере.


Шифрование соединения.

Вот что происходит при открытии веб-сайта:
- Клиент (в нашем случае браузер) отправляет запрос на сервер
- Сервер в ответ отправляет свой ssl-сертификат, который содержит открытый ключ.
- Запускается так называемый процесс рукопожатия TLS (TLS Handshake) в котором данные отправляются на сервер зашифрованными на основании только что полученного открытого ключа.
- Во время процесса рукопожатия TLS, клиент и сервер "договариваются" об используемых
стандартах связи, и они генерируют новые ключи для этого сеанса общения.
- И только теперь начинается полноценный обмен данными, которые передаются в зашифрованном виде.


Это очень упрощённая схема того, что происходит. Самое главное это понять один важный принцип: ssl-сертификат всегда содержит открытый ключ, который является общим
для всех, кто подключается к серверу. С его помощью клиент шифрует сообщения перед отправкой на сервер. И обязательно есть закрытый ключ, который остаётся на сервере, для расшифровки сообщений полученных от клиента.

Подтверждение доверия между клиентом и сервером.

В процессе сеанса связи очень важно убедиться что клиент разговаривает с правильным сервером. Если не проверять "личность" сервера, то возможен тип атаки называемый Man In the Middle (человек между). Это когда нам нужно подключиться к серверу, скажем 123, а по какой-то причине вдруг подключились к серверу 456, но сертификат (паспорт) которого говорит что это сервер 123. При таком типе атак, возможна "прослушка" т.е. расшифровка сообщений между клиентом и сервером, и как следствие - утечка данных.
Поэтому в ssl-сертификате присутствует идентификатор сервера, чтобы клиент мог проверить и решить, доверять ли серверу, или это это кто-то кто выдаёт себя за нужный сервер, но при этом им не является.

Основные параметры сертификата:
- Срок действия с какой-то даты по какую- дату.
- Кому выдан, или адрес сервера. Сертификат может быть выдан как на отдельное доменное имя, так и на доменное имя со всеми поддоменами автоматически. Такой тип сертификата называется WildCard.
- Кем выдан. Орган выдавший сертификат, сам должен быть сертифицирован и валидирован.
- Кем подписан. Орган заверивший сертификат так же должен быть сертифицировани и валидирован.
Под валидацей подразумевается всё то же соответствие данных указанных в сертификате и совпадение их с действительностью. Всё это вместе называют цепочкой сертификатов.

пример цепочки сертификатов хостинга этого блога. каждый из сертификатов можно посмотреть и убедиться в его валидности.
 
Из  примера видно что сертификат был выдан так называемым промежуточным Центром Сертификации (CA - Certification Authority), а тот в свою очередь выдан/заверен корневым (Root) или главным ЦС. Поэтому это и называется цепочкой, и браузер всегда пытается подтвердить её чтобы выйти к корневому ЦС, чтобы принять решение о доверии целевому серверу.

Все основные сертификаты корневых ЦС находятся в специальном хранилище на каждом устройстве и обновляются во время получения апдейтов операционной системы. Именно поэтому каждый браузер по умолчанию может проверить тот или иной сертификат как промежуточного ЦС, так и целевого сервера.

Итог теории.

- Есть сертификат конечного устройства выданный и заверенный промежуточным ЦС.
- Промежуточный ЦС имеет свои сертификаты выданные и заверенные корневым ЦС.
- Сертификаты корневых ЦС распространяются и есть на каждом устройстве, для валидации промежуточных ЦС.
- Всё вышеперечисленное называется цепочкой сертификатов.
- Сертификат конечного устройства содержит данные об адресе устройства, и о том, кем этот сертификат выдан/заверен.
- На сервере хранятся 2 ключа: для расшифровки и шифрования сообщений. Это правило относится и к работе ЦС. 

Самоподписанные сертификаты.

После того как разобрались с принципом работы сертификации, может возникнуть вопрос - для чего нужен самоподписанный сертификат, если обычный можно просто получить от того же Let's Encrypt совершенно бесплатно?!

Дело в том, что когда необходимо получить сертификат от публичного центра сертификации, есть некоторые ограничения и требования, потому что этим сертификатам автоматически доверяет каждый браузер. Поэтому промежуточные ЦС всегда требуют подтверждения права собственности или владения доменом или адресом, на который выдаётся сертификат.
В случае использования интернет домена, для владельца не составляет труда пройти процедуру верификации и доказать что именно этот домен принадлежит ему.

При использовании доменных имён или айпи адресов внутри локальной сети, такое подтверждение невозможно. Например у тебя есть есть 192.168.1.X и у соседа дома тоже такой же пул адресов, но ваши сети не связаны между собой. Как доказать внешнему регистрирующему органу что именно сосед или ты являетесь уникальным владельцем этих адресов? То же самое касается и доменных имён внутри сети. Снаружи их нельзя проверить, и тем более установить владельца.

Решение №1.

Теоретически, можно использовать внешнее доменное имя, server1.chto-to-tam.com которое будет указывать на внутренний айпи адрес в твоей сети, например 192.168.1.2, на котором работает какой-то веб сервер. И если эта сеть подключена к интернет, то зайдя с любого устройства из этой же сети на адрес домена server1.chto-to-tam.com, браузер будет перенаправлен на 192.168.1.2. И в свою очередь, так же можно получить сертификат на это доменное имя, и тогда браузер не будет выдавать предупреждений о небезопасном сайте, и соединение с сервером будет шифрованным.

Плюсы этого способа:
- Официальный сертификат от официального поставщика (ЦС).
- Как следствие первого пункта - сайты с этим сертификатом будут открываться без ошибок на любом устройстве (нет необходимости обновлять его вручную на каждом девайсе).

Минусы данного способа:
- Сертификат штука платная. Бесплатные выдаются сроком на 3 месяца. Чтобы работали скрипты/программы автоматического обновления и скачивания сертификатов, необходимо открывать порты на раутере. А это крайне небезопасно.
- Весь интернет знает о твоих внутренних адресах, хоть и не может их открыть (не исключаем попытки направленного взлома)
- Если сеть отключилась от интернета, то определение соответствий внутренних адресов и доменных имён (резолвинг) может перестать работать. А  значит останется возможность заходить на сервер только по айпи или внутрисетевому имени. 
- При попытке зайти на тот же сервер не по имени, а по айпи браузер выдаст предупреждение о небезопасном подключении.

Решение №2.

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

Плюсы:
- Нулевая стоимость.
- Работает даже в сети не подключенной к интернет.
- Можно самостоятельно всегда перевыпустить с новыми данными в зависимости от требований.
- Максимальная безопасность. Никаких открытых портов наружу и никакой связи с внешним миром для получения/обновления сертификата. 

Минусы:
- Самодельный заверитель не знаком никому в мире, в отдельных случаях теоретически могут выскакивать предупреждения от разных программ.
- Если не хочется чтобы на како-то устройстве были предупреждения в браузере, на это устройство нужно скачать и поставить сертификат вручную.
- Всю процедуру по выпуску и обновлению сертификатов надо выполнять каждые Х лет (под окончание срока годности текущего сертификата).

Практика

Будем использовать решение №2. Ради решения №1 не было бы смысла в этом материале. 

Создание промежуточного ЦС.

Для этого мы будем использовать инструмент с открытым исходным кодом open ssl. Этот инструмент установлен почти на всех дистрибутивах Linux, но если вдруг по какой-то причине этот пакет отсутствует, то его можно легко скачать и установить с помощью пары команд.
Под Windows же есть замечательный продукт Win32/Win64 OpenSSL.

Для начала нужен ключ, с помощью которого будет выдан целевой сертификат для ЦС.
Для этого создадим новый RSA ключ, это закрытый ключ центра сертификации, которым никогда нельзя ни с кем делиться. Поэтому при генерации этого ключа, будет использовано AES шифрование и пароль. Который, естественно, надо сохранить и не сообщать никому.

Ключ будет создан с 4096 битным шифрованием т.к. оно более надёжно чем шифрование по умолчанию.
Команда для этого, с сохранением ключа в определённую папку выглядит так: openssl genrsa -aes256 -out c:\certs\ca-key.pem 4096

После того как ключ создан, сгенерируем непосредственно сам сертификат ЦС. Он будет действителен в течение определённого времени, которое указывается в днях. Если установить слишком короткий временной промежуток, то обновлять этот сертификат на устройствах придётся очень часто. Поэтому можно указать временной промежуток сразу в несколько лет.
Команда для создания такого сертификата на 10 лет, с сохранением его в ту же папку где и ключ будет выглядеть так: openssl req -new -x509 -sha256 -days 3650 -key c:\certs\ca-key.pem -out c:\certs\ca.pem

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

После того как сертификат создан, можно убедиться что в нём прописаны нужные параметры, и отобразить его свойства в удобочитаемом формате. Для этого можно воспользоваться командой openssl x509 -in c:\certs\ca.pem -text

Среди прочего можно увидеть срок годности этого сертификата

А так же то, что он является сертификатом ЦС.

Создание самоподписанного сертификата.

Для начала стоит определиться как именно будут работать и выдаваться сертификаты в домашней сети, только на айпи адреса или на какие-то внутренние доменные имена. Если используется сетевой домен, то можно выдать WildCard на всё локальное имя с поддоменами, и  на этом закончить. Если есть привычка/необходимость заходить на домашние веб сервера по айпи адресу, то тогда нужно выдавать сертификат для каждого сервера по отдельности, и в каждом из них прописывать нужный айпи.

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

Создаём ключ с помощью следующей команды: openssl genrsa -out c:\certs\cert-key.pem 4096

Итак, у нас есть центр сертификации и есть ключ шифрования для будущего сертификата. Однако, перед тем как сам сертификат будет создан (т.е. выдан в виде файла), нужно попросить центр сертификации его заверить. Для этого создаётся запрос на получение сертификата. Команда запроса выглядит следующим образом: openssl req -new -sha256 -subj "/CN=yourcn" -key c:\certs\cert-key.pem -out c:\certs\cert.csr
Стоит обратить внимание на параметр "/CN=yourcn". Ранее, протокол обязывал указать здесь данные для валидации сертификата, однако сейчас здесь можно указывать любые данные. так что yourcn можно заменить на что нравится.

Предпоследний шаг. Создадим файл конфигурации, который будет содержать в себе адреса серверов в локальной сети, из которого потом создадим один уникальный сертификат для всех. На самом деле, если следовать правилам безопасности, сертификат должен быть один для каждого сервера. Но в обычном домашнем и не производственном окружении этим правилом можно пренебречь. Команда для создания файла конфигурации будет выглядеть примерно так: echo subjectAltName=IP:192.168.0.1,IP:10.0.0.1,IP:172.16.0.1 >> c:\certs\extfile.cnf
Если необходимо указать ДНС имя, а не айпи адрес, то используется запись вида DNS:your-dns.record.

Ну и наконец создание итогового сертификата. Команда для него соберёт в себя все ранее созданные файлы и итоговый её вариант будет выглядеть примерно так: openssl x509 -req -sha256 -days 3650 -in c:\certs\cert.csr -CA c:\certs\ca.pem -CAkey c:\certs\ca-key.pem -out c:\certs\cert.pem -extfile c:\certs\extfile.cnf -CAcreateserial

Последний штрих для красоты. Объединим 2 сертификата в 1, чтобы создать цепочку сертификатов: type c:\certs\cert.pem c:\certs\ca.pem > c:\certs\fullchain.pem.

Применение сертификатов.

Добавление промежуточного ЦС в Windows.

Для начала добавим сертификат ЦС на компьютер с ОС Windows. Можно сделать это через PowerShell (конечно же запущенной с админскими правами), командой Import-Certificate -FilePath "C:\certs\ca.pem" -CertStoreLocation Cert:\LocalMachine\Root. А можно и просто из командной строки с помощью certutil.exe -addstore root C:\certs\ca.pem.

Установка в Proxmox.

Перед началом крайне рекомендуется убедиться что есть и работает доступ в консоль сервера через любой SSH клиент. Или же есть возможность работать с сервером напрямую, через подключенные клавиатуру и монитор. Это нужно на случай непредвиденных ситуаций.


В интерфейсе заходим в раздел сертификатов и добавляем из созданных файлов следующие: файл cert-key.pem добавляем в раздел Private Key, в раздел Certificate Chain добавляем файл fullchain.pem

Synology DSM 7.2

Для Synology DSM 7.2-64570 Update 2 добавляем следующие созданные файлы:

Home Assistant.

Для начала установим (если ещё не поставлена) и включим (если выключена) Самбу.
После чего, подключившись через любой файловый менеджер скопируем в папку SSL пару ранее созданных файлов: цепочку сертификатов и ключ к сертификату. Так же скопируем и файл Центра Сертификации - ca.pem, он понадобится чуть позже.
Файл cert-key.pem после копирования в папку назначения, там же и переименовываем в privkey.pem.
На этом этапе рекомендуется сделать снэпшот системы, на случай если вдруг что-то пойдёт не так, и чтобы была возможность быстро откатиться назад к рабочему состоянию. 
После чего редактируем файл configuration.yaml и добавляем в раздел http:
  ssl_certificate: /ssl/fullchain.pem
  ssl_key: /ssl/privkey.pem
Осталось сохранить файл и сделать обычную перезагрузку через меню "режим разработчика". Теперь система будет доступна по тому же адресу, но уже по протоколу HTTPS.
Если всё работает как надо, то не забываем удалить сделанный ранее снимок.

Настройка проверки срока годности.

В Home Assistant есть замечательная интеграция по проверке срока действия сертификата.

С её помощью можно отслеживать даты истечения времени действия сертификата на любом веб ресурсе, для того, чтобы заранее принять меры по обновлению.

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

Настройка состоит из двух этапов:
1. Указать хост, сертификат которого будем проверять с помощью интеграции.
2. Добавить в Home Assistant сертификат ЦС (по аналогии как это сделали на компе с Windows).
С какого из них начать - разницы особой нет. За исключением того, что если делать в указанном порядке, то один раз в системном логе будет строчка что система не смогла проверить самоподписанный сертификат. Но это не критично.


Итак, при подключении к ХА с компьютера, сертификат выдаваемый сервером проверяется центром сертификации установленном на самом компе (клиенте). 
Чтобы такой же процесс происходил внутри ХА, то необходимо в его хранилище Центров Сертификации добавить свой - свежесозданный.
Для этого и пригодится вышеупомянутый ca.pem.

Важно сразу отметить ещё один момент.
При каждом обновлении системы, список доверенных центров сертификации обновляется, и самоподписанный центр сертификации будет удалён.

Чтобы максимально просто и быстро решить проблему стёртого ЦС, создадим новый сервис в самом ХА. Он будет копировать файл ЦС в нужную директорию и обновлять список всех ЦС в системе.

для этого в файле configuration.yaml добавим 2 строчки:
shell_command:
  certupd: "cp /ssl/ca.pem /etc/ssl/certs/ && update-ca-certificates"
после чего перезагрузим систему.


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

Теперь уже можно добавить интеграцию (если не сделали этого раньше) и введя адрес Home Assistant + его порт, увидеть что всё работает.

Ну а сделать автоматизацию, предупреждающую за неделю до окончании срока действия сертификата, можно сделать с помощью шаблона: {{ (as_timestamp(states('sensor.name')) - as_timestamp(now())) / 86400 < 7 }}

CloudFlare.

Не забываем сделать необходимые изменения если пользуемся этим сервисом для доступа снаружи. Сменить протокол с HTTP на HTTPS для тех хостов, у кого он поменялся, и в настройках для них же отключить проверку TLS.

Android

Закидываем файл ca.pem на телефон любым удобным способом.
В настройках смартфона переходим в Пароли и безопасность -> Конфиденциальность -> Шифрование и учётные данные -> Хранилище учётных данных  (Security -> Encryption and Credentials -> Install a certificate -> CA Certificate)
Выбираем хранилище - телефон, и идём в ту папку куда был сохранён файл.
Тыкаем в него - готово.

Устранение проблем.

Если вдруг по какой-либо причине на сервер были загружены ошибочные файлы и в результате этого интерфейс не грузится/открывается, выполняем следующие действия:

Proxmox.

- подключаемся по ssh в консоль проксмокса (или же работаем напрямую).
- удаляем пару файлов с помощью команд rm /etc/pve/local/pveproxy-ssl.key и rm /etc/pve/local/pveproxy-ssl.pem
- обновляем хранилище сертификатов pvecm updatecerts --force
- перезапускаем интерфейс systemctl restart pveproxy

Home Assistant

- Подключаемся по смб как для заливки файлов
- Открываем configuration.yaml
- Комментируем добавленные строки (поставить # в самом начале).
- Сохраняем изменения в файле
- Перезагружаем ХА.


Обновление сертификатов.

Как уже было упомянуто выше, у каждого сертификата есть срок годности, по истечении которого (а лучше заранее) его необходимо обновить.
Т.к. в рассматриваемом примере было создано 2 сертификата (один для ЦС и второй непосредственно для сервера) на один и тот же срок, то очевидно что оба они станут недействительными в одно и то же время. Поэтому есть изначально стоит продумать стратегию выдачи сертификатов. И для этого есть 2 варианта на выбор
  1. Сертификат для ЦС делается на срок гораздо больший чем сертификат для сервера.
  2. Оба сертификата выдаются на один и тот же период времени.
Логично предположить что в условиях домашней сети/лаборатории лучше сделать оба сертификата на один долгий срок, чтобы реже делать процедуру обновления.

Порядок действий для обновлений:

Обновление сертификата центра сертификации (ЦС):

  • Создать новый ключ для ЦС (если это необходимо): openssl genrsa -aes256 -out c:\certs\new_ca-key.pem 4096.
  • Создать новый ЦС сертификат указав новый ключ: openssl req -new -x509 -sha256 -days 3650 -key c:\certs\new_ca-key.pem -out c:\certs\new_ca.pem.
  • Заменить старый ЦС сертификат новым на всех клиентских устройствах .

Обновление самоподписанного сертификата:

  • Создать новый ключ шифрования: openssl genrsa -out c:\certs\new_cert-key.pem 4096.
  • Создать новый запрос на получение сертификата (CSR) указав новый ключ: openssl req -new -sha256 -subj "/CN=yourcn" -key c:\certs\new_cert-key.pem -out c:\certs\new_cert.csr.
  • Запросить у ЦС новый сертификат указав новый CSR: openssl x509 -req -sha256 -days 3650 -in c:\certs\new_cert.csr -CA c:\certs\new_ca.pem -CAkey c:\certs\new_ca-key.pem -out c:\certs\new_cert.pem -CAcreateserial.
  • Объединить новый сертификат с цепочкой сертификатов: type c:\certs\new_cert.pem c:\certs\new_ca.pem > c:\certs\new_fullchain.pem.
  • Заменить старый самоподписанный сертификат на новый на всех серверах где он используется.
На этом всё :)

Комментарии