(Багато налаштувань в мене вже є в бекапах сайту Академії чи інших моїх сайтів)
Вебсервер
Вебсервер — це програма або компʼютер із програмою, що обробляє запити та повертає відповіді по мережевому протоколу HTTP. Для будь-якого вебзастосунку потрібен вебсервер. Коли вебзастосунок розміщений на вебсервері, то вебсервер хостить цей застосунок.
Коли будь-який вебсервер отримує HTTP-запит, в залежності від того, як він сконфігурований, може статись один із декількох сценаріїв:
- вебсервер поверне готовий файл, який зберігається на компʼютері, де запущена програма вебсервера;
- вебсервер сам здійснить запит до іншого вебсерверу, отримає від нього відповідь, можливо модифікує її, та поверне у відповідь на отриманий запит;
- вебсервер відповість перенаправленням на іншу адресу або помилкою, якщо запит не коректний.
У цьому топіку ми розглянемо ці сценарії на прикладі простого вебзастосунку. Це буде вебсторінка із зображенням годинника та кнопкою. Коли ти натискаєш на кнопку, то на сторінці відображається поточний час. Застосунок складається з HTML-сторінки, зображення, та API, написаного на Python, яке і визначає поточний час. Також ми зробимо цей застосунок доступним у твоїй локальній мережі.
Встановлення Nginx
Для того, щоб розмістити вебзастосунок на сервері та зробити його доступним у мережі, нам потрібно встановити програму вебсервера на віртуальну машину та сконфігурувати сам вебсервер. Nginx можна встановити за допомогою пакетного менеджера:
Пакет Nginx містить конфігурацію сервісу Nginx. Перевіримо його статус, а також переконаємось, що сервіс автоматично запускатиметься під час кожного увімкнення комп'ютера:
Одразу після встановлення, Nginx сконфігурований повертати у відповідь на HTTP-запити сторінку за замовчуванням. Цю сторінку ми вже можемо подивитись у браузері, якщо введемо в адресному рядку IP-адресу віртуальної машини — ту саму, яку ти використовуєш, коли підключаєшся до неї по SSH.
За цією ж адресою така сторінка повинна відкриватись на будь-якому пристрої, підключеному до тієї самої мережі. Наприклад, якщо твій компʼютер і телефон зараз підключені до одного Wi-Fi, цей вебсайт ти можеш відкрити з телефону. Але це не той вебсайт, який нам потрібно запустити, тому повернемось у термінал.
Як саме Nginx обробляє HTTP-запити, визначають файли конфігурації. Головний файл конфігурації — /etc/nginx/nginx.conf.
Він містить такі налаштування, як версії протоколу TLS, шлях до файлів, у які програма Nginx пише свої логи, та посилання на інші додаткові файли конфігурації. Найцікавіші для нас файли розташовані в теці sites-enabled. Один сервіс Nginx, встановлений на одному компʼютері, дозволяє нам розміщувати скільки завгодно окремих сайтів, і конфігуруються ці сайти за допомогою файлів, розташованих у цій теці /etc/nginx/sites-enabled.
Зазирни у файл конфігурації сайту default, який створився автоматично після інсталяції /etc/nginx/sites-enabled/default . Як і в Bash, решіткою тут позначаються закоментовані рядки. Кожному сайту, який хоститься Nginx, відповідає один блок server, між фігурними рядками якого описані команди конфігурації. Кожна команда повинна закінчуватись крапкою з комою.
Перша команда в цьому блоці — listen, вказує IP-адресу і TCP-порт, на яких сайт очікуватиме HTTP-запити. Адреса може бути не вказана, тоді, якщо в серверу кілька IP-адрес, сайт буде доступний за всіма.
Дві двокрапки у квадратних дужках, вказані на місці адреси в другій команді listen, позначають, що цей сайт доступний також і за будь-якою адресою IPV6.
Порт, який вказаний в обох командах listen (80) — це порт, який використовується всіма програмами за замовчуванням для HTTP. До речі, для HTTPS за замовчуванням використовується порт 443.
Обидві команди listen тут закінчуються параметром default_server. Це означає, що саме цей сайт буде використовуватись за замовчуванням, коли прийде HTTP-запит на вказані за допомогою listen порти та адреси.
Хостинг статичного контенту
Коли вебсервер встановлений, ми можемо почати його налаштовувати. Спершу потрібно зклонувати репозиторій з файлами вебсайту на локальний комп'ютер:
Перенесемо файли нашого вебсайту на віртуальну машину — це можна зробити за допомогою scp:
Команді потрібно вказати source (директорія з файлами вебсайту), destination (домашня директорія на віртуальній машині) та параметр -r, оскільки ми копіюємо теку. Якщо в SSH-ключа нестандартне імʼя, то додатково потрібно вказати шлях до нього.
Тепер перейдемо до конфігурації сервера, для цього потрібно підключитись до нашої віртуальної машини:
Перша частина вебсайту — це статичний контент (HTML-документ та картинка), розташований у теці frontend. Налаштуємо його хостинг на Nginx. Спершу потрібно підготувати файли, які буде віддавати наш вебсервер. Їх можна зберегти в будь-якій директорії на сервері, але зазвичай такі файли зберігають в окремій теці в /var/www:
Працюватиме із цими файлами один із процесів Nginx, який називається worker process. Саме ці процеси обробляють вхідні HTTP-запити, а master-процес лише створює додаткові worker-процеси автоматично за необхідності. Подивитись на процеси nginx можна за допомогою ps:
Далі потрібно перевірити, чи правильно налаштовані права на файлах, які є нашим статичним контентом:
Оскільки ми створювали директорію timenow під користувачем root, то й тека належить root. Але поточні права дозволяють зчитувати ці файли всім, що нас влаштовує.
Тепер, коли тека з файлами готова, підготуймо конфіг сайту. Конфіг кожного сайту зручно зберігати в окремому файлі, адже Nginx зчитує всі файли з теки sites-enabled:
А ось і сам конфіг:
Тут ти можеш побачити команди listen для всіх IPv4 та IPv6-адрес на 80 порту. За допомогою root в Nginx вказується директорія, з якої сайт повертає статичний контент (тут треба вказати теку, яку ми підготували на попередньому кроці).
За допомогою server_name вказуються значення хостів, по яких відфільтровуватимуться запити на порт і адресу. Це також доменне імʼя, за яким буде доступний вебсайт. Тут можна вказувати декілька доменних імен через пробіл, але ми вкажемо лише одне — temenow.local.
Тепер можна підвантажити зміни в Nginx. Це можна зробити перезапустивши (тобто вимкнувши та увімкнувши) його сервіс. Якщо конфіг не містить помилок, сервіс запуститься:
Якщо сервіс не перезапустився, systemctl status поверне ім`я файлу та рядок, у якому є помилка.
Після перезавантаження, спробуємо зробити HTTP-запит до нашого вебсайту:
У відповідь ми отримаємо HTML-сторінку. Ми зробили запит до вебсервера за IP-адресою та вказали хедер Host з доменом, який ми використали в конфіг-файлі. Саме такий хедер додався б автоматично, якби ми спробували зробити HTTP-запит, використавши DNS-імʼя замість IP-адреси:
Але такого доменного імені поки що не існує. Щоб мати можливість відкривати вебсайт не вказуючи цей додатковий хост-хедер, наприклад, у браузері, нам потрібно трішки розібратись як працює DNS.
DNS для сайту
DNS (Domain Name System) — це велика, розподілена, ієрархічна база даних, яка зберігає доменні імена та IP-адреси, які їм відповідають. Коли ти відкриваєш вебсайт у браузері та якщо адреса цього вебсайту використовує доменне імʼя, то твій компʼютер, а точніше його операційна система, транслює це доменне імʼя в IP-адресу. Ця IP-адреса потім використовується, щоб встановити зʼєднання із сервером, який хостить вебсайт. Трансляція імені в IP-адресу відбувається у декілька етапів:
-
Операційна система перевіряє свій DNS-кеш. Цей кеш — це локальна база даних DNS-записів. Коли операційна система транслює якесь імʼя в IP-адресу, то вона зберігає ці значення в кеш, щоб не робити запит до DNS-серверів наступного разу, коли потрібно буде встановити мережеве підключення до іншої адреси.
-
Операційна система перевіряє, чи немає потрібного запису у файлі
hosts. У цьому файлі користувачі можуть самостійно вказувати доменні імена та IP-адреси, які їм відповідають. За замовчуванням у цьому файлі є лише один такий запис —127.0.0.1 localhost, де127.0.0.1— це спеціальна адреса, яка резервується компʼютерами для самих себе, аlocalhost— це стандартне доменне імʼя для такої IP-адреси. -
Операційна система перевіряє, який DNS-сервер вказаний у нього в конфігурації. Адреси DNS-серверів зазвичай отримуються компʼютером через DHCP під час підключення до мережі, хоча їх можна вказати вручну самостійно.
-
Операційна система здійснює запит до DNS-сервера та отримує від нього відповідь. Отримана відповідь кешується, тобто зберігається для наступних разів, і використовується для того, щоб встановити мережеве підключення до сервісу.
💡 Насправді DNS-кеш є тільки в Windows, в Linux і macOS він відсутній. У решті аспектів, трансляція доменного імені в IP-адресу на всіх операційних системах працює однаково.
Щоб мати можливість підключатись до нашого вебсайту, ми будемо додавати відповідний запис в hosts файл, адже власного DNS-серверу в нас немає. Таку зміну потрібно буде робити на будь-якому компʼютері, з якого ти захочеш відкрити вебсайт. Є ще кілька нюансів, на які слід звернути увагу:
-
Підключатись до вебсайту зможуть тільки ті компʼютери, які знаходяться в одній мережі з нашим вебсервером. Якщо твій ноутбук із віртуальною машиною підключений до Wi-Fi, то щоб відкрити вебсайт з іншого ноутбуку, він теж має бути підключений до того ж самого Wi-Fi.
-
Зараз наша віртуальна машина отримує свою IP-адресу через DHCP. Після того, як ти її перезавантажиш, ця IP-адреса може змінитись, відповідно її потрібно буде оновлювати у всіх
hosts-файлах, у які ти додаси запис.
DNS — це просто необхідна річ для хостингу вебзастосунків. Завдяки тому, що ми звертаємось до сайтів за доменним імʼям, а не за IP-адресою, ми можемо легко розміщувати на одному вебсервері багато вебсайтів із різними доменними іменами, і всі вони будуть працювати одночасно!
Налаштування DNS на macOS і Linux
У Linux та macOS hosts-файл розташований в одному й тому ж місці:
Принципи побудови цього файлу однакові на всіх операційних системах: # позначає коментар, а записи потрібно додавати у форматі IP адреса, потім через пробіл доменні імена, які їй відповідають.
Після додавання запису можна відкрити вебсайт у браузері: http://timenow.local/.
💡 Відкривати файл hosts потрібно з sudo.
Налаштування DNS на Windows
На Windows файл hosts теж не можна редагувати звичайним користувачам. Тому спочатку потрібно відкрити текстовий редактор Notepad з правами адміністратора. Запуск програми з правами адміністратора в Windows — це своєрідний аналог sudo. Далі за допомогою текстового редактора, запущеного з правами адміністратора, треба відкрити файл hosts за його стандартним шляхом у Windows: C:\Windows\System32\drives\etc\hosts. Цей файл виглядає дуже схожий на той, який ми бачили в macOS.
Додай запис у кінці та збережи файл. Далі потрібно очистити DNS-кеш. DNS-кеш потрібно чистити тільки якщо операційна система до цього транслювала потрібне нам доменне імʼя в якусь іншу адресу, яка відрізняється від тієї, що ми вказали в hosts. Але щоб попрактикуватися, ми все одно це зробимо. Запусти термінал PowerShell або cmd з правами адміністратора та виконай наступну команду:
Тепер ти можеш відкрити свій вебсайт зі свого, та з будь-якого іншого компʼютера, підключеного до твоєї мережі!
Усунення помилок Nginx
Вебсервер — це складна програма, у якої багато параметрів і можливостей. Помилитись при його конфігурації цілком нормально, головне знати, як знайти причину помилки або некоректної роботи.
Під час налаштування поведінки вебсервера Nginx, ми змінювали існуючі або створювати нові файли конфігурації. Для того, щоб сервіс Nginx взяв до уваги зміни, які ми внесли в конфігурації, ми перезапускали сервіс за допомогою systemctl. Спробуймо внести якусь зміну у файл конфігурації та перезапустити Nginx:
Коли ми перевіримо статус сервісу після перезапуску, ми побачимо, що він зупинений. Тут є і помилка, яка вказує на файл конфігурації та рядок із помилкою. Виправимо її та перезапустимо вебсервер:
Якщо будь-який із файлів конфігурації Nginx міститиме помилку, сервіс просто не запуститься, а повідомлення в інформації про його статус допоможе зʼясувати в чому саме проблема.
Якщо вебсервер працює, інформацію про те, що з ним відбувається, Nginx записує в спеціальні файли, які називаються лог-файлами, або просто логами. Ці логи розташовані в теці /var/log/nginx/. У міру того, як у цих файлах зʼявляються нові записи, Nginx архівує старі.
Спробуємо завантажити з нашого вебсайту якийсь неіснуючий файл та подивимось, яка інформація запишеться в логах. HTTP-запит, для зручності, здійснимо за допомогою curl з віртуальної машини, на якій розгорнутий наш вебсервер:
У відповідь ми отримаємо HTTP response code 404, тобто такого посилання не знайдено. Поглянемо, яка інформація записалась у логи. У Nginx є дві категорії логів:
-
Access log містить інформацію про всі HTTP-запити, які приходять на вебсервер
/var/log/nginx/access.log. Тут записується час, IP з якого здійснився запит, метод, URI, HTTP response code, розмір reponse body в байтах та інформація про тип пристрою, з якого здійснювався запит. Останній запис в access-лозі відповідає некоректному запиту, який ми здійснили. -
Error log містить інформацію про помилки, які виникали в Nginx при обробці запитів. Він зберігається в файл
/var/log/nginx/error.log. Цей лог також містить помилку, яка відповідає запиту, який ми здійснили напередодні зcurl.
Якщо ти стикаєшся з проблемами при роботі Nginx, статус сервісу та лог файли - це саме ті місця, де слід шукати більш детальну інформацію про проблему та підказки до того, як її вирішити. У першу чергу слід звертати увагу на статус самого сервісу і потім, якщо він запущений, перевіряти access та error-логи.
Запуск API
Друга частина сайту — це API, написаний на Python (devops_timenow-app/backend/api.py). Він викликається JavaScript-ом, що вбудований в HTML-сторінку, хостинг якої ми вже налаштували. API здійснює обчислення на стороні сервера, а саме перевіряє поточний час, та повертає відповідь. Наш API поки не запущений, саме через це в нас на сайті поки не працює кнопка.
API містить лише один ендпоінт ( time). Він написаний із використанням бібліотеки Flask, яка дозволяє запускати вебзастосунки, вказавши порт і IP-адресу, за якими застосунок має бути доступним.
Порт, на якому буде запущений API — 8080. Порт 80 ми не можемо використати, оскільки лише одна програма може використовувати один і той самий порт, а 80 вже використовується Nginx для вебсторінки зі статичним контентом.
Хост 0.0.0.0 позначає, що API буде приймати запити на всі IP, за якими доступний вебсервер. Така адреса тут — це те саме, що й не вказана IP-адреса в директиві listen у конфігурації вебсайту в Nginx.
Запустимо процес у терміналі:
cd devops_timenow-app/backend
sudo apt-get install python3-pip
pip3 install -r requirements.txt
Python3 api.py
Тепер він доступний у браузері: http://timenow.local:8080/time. API зараз — це окрема програма, яка працює на окремому порту 8080 через протокол HTTP. Вона поки не перевіряє хост-хедер, а просто відповідає на запити за допомогою Python-коду.
На цьому етапі посилання працюватиме як по IP, так і по доменному імені, яке ми використовуємо для нашого вебсервера.
Під'єднаємо API до нашої кнопки. Для цього треба лише підправити посилання у вже написаному JavaScript, щоб коли ми натискали кнопку, виконувався HTTP-запит до саме цього API:
Тепер, коли ми відкриємо наш вебсайт у браузері та спробуємо натиснути на кнопку, браузер має надіслати HTTP-запит до API, отримати у відповідь час і відобразити його на сторінці. Але кнопка все ще не працює.
Якщо ми відкриємо Developer Tools, то побачимо, що у всьому винен механізм, який називається cros-origin-resource-sharing, або скорочено — CORS — це один із механізмів захисту від cros-site-request-forgey-атак, який імплементовано у всіх сучасних браузерах. Ціль цього механізму — переконатись, що здійснити запит до API можна тільки з вебсайтів (origins), які дозволяє сам API. Він імплементований на стороні браузера та застосовується до API-викликів, які здійснюються з JavaScript. Ось як працює CORS:
-
Коли ми робимо запит з JavaScript до API, перед виконанням цього запиту браузер робить інший HTTP-запит зі спеціальним методом
OPTIONS. Такий запит називається pre-flight. -
У відповідь на нього браузер очікує від API інформацію про список сайтів, з яких можна звертатись до цього API. Ця інформація передається в response-хедері
Access-Control-Allow-Origin. -
Браузер порівнює адресу origin-вебсайту, JavaScript-код якого намагається здійснити виклик API з тим, що отримує в хедері
Access-Control-Allow-Origin, і виконує сам HTTP-запит лише якщо origin є в списку отриманих від сервера API. -
Список адрес, які повертаються API в хедері
Access-Control-Allow-Originназивається CORS policy. Конфігурація CORS-полісі — це одна з найчастіших задач при налаштуванні хостингу API, адже за замовчуванням вона пуста й заборонені всі origins.
Щоб кнопка на сайті змогла здійснювати запит до API та відображати час, нам потрібно налаштувати наш API так, щоб до відповідей на HTTP-запити з методом OPTIONS (або взагалі з будь-яким методом) додавався response-хедер Access-Control-Allow-Origin, який міститиме адресу нашої сторінки з кнопкою. Якщо API маленький, запущений лише на одному сервері та викликається лише однією сторінкою, то легше переписати його, щоб додати цей хедер, але зазвичай цю проблему вирішують іншим способом, який ми зараз розглянемо.
Налаштування реверс-проксі
Один із режимів роботи вебсервера — це коли він отримує вхідний запит, надсилає його якомусь іншому серверу, отримує відповідь та надсилає її назад клієнту. При цьому, вебсервер може модифікувати відповідь перед поверненням її клієнту, наприклад, додати HTTP response header. Цей режим роботи вебсервера називається реверс-проксі, і за допомогою нього ми будемо конфігурувати CORS policy.
Створи нову SSH-сесію до віртуальної машини та в ній створи новий конфіг сайту Nginx:
А ось і сам файл:
server {
listen 80;
listen [::]:80;
server_name timenow-api.local;
location / {
proxy_pass http://localhost:8080/;
}
}
Почнемо з блоку server, у який додамо команди listen для порту 80, та окремий server_name timenow-api.local, щоб наш API був доступний за окремим хостом на тому ж 80-му порту. Далі додамо конфігурацію реверс-проксі, а саме блок location. Для цього блоку потрібно вказати шлях на сайті, для якого налаштовується конфігурація, а в середині потрібно додати команду proxy_path, параметром якої буде адреса API. Тепер перезапустимо Nginx, додамо server name для API в hosts-файл та спробуємо відкрити API в браузері за допомогою реверс-проксі:
Додамо домен, який ми використали для API в hosts-файлі на своєму компʼютері, щоб легше здійснювати HTTP-запити до нього з браузера:
Реверс-проксі сконфігурований! Тепер у нас є адреса, за якою API доступний через Nginx. Залишилась дрібниця — додати хедер для CORS в конфігурацію реверс-проксі:
Це можна зробити додавши в середину блоку location команду add_header. Перший її параметр — це імʼя хедера, який потрібно додати, а другий — значення:
Ось так має виглядати файл конфігурації після такої зміни:
server {
listen 80;
listen [::]:80;
server_name timenow-ap.locali;
location / {
proxy_pass http://localhost:8080/;
add_header Access-Control-Allow-Origin http://timenow.local;
}
}
Перезавантажимо Nginx, щоб застосувати зміни у файлі конфіг, і подивимось, чи приходить цей хедер нам у відповідь, коли ми звертаємось до API через реверс-проксі:
sudo systemctl restart nginx
systemctl status nginx
curl -v http://timenow-api.local/time
curl -v http://timenow-api.local:8080/time
Як бачиш, якщо ми звертаємось до API через реверс-проксі, хедер приходить нам у відповідь, а якщо ми робимо запит напряму, потрібного хедера у відповіді ми не отримуємо.
Підправимо адресу API, яку використовує кнопка на нашому сайті, щоб вона зверталась до API через реверс-проксі:
Тепер після перезавантаження сторінки кнопка працює!
Але тут ще є місце для покращення. Уявимо, що в нас є можливість редагувати лише один DNS-запис — timenow.local. Це доволі поширена ситуація, оскільки для того, щоб зареєструвати доменне імʼя на публічно доступних DNS серверах, його потрібно купувати. Якщо в нас є можливість використовувати лише одне доменне імʼя, то обидві частини вебзастосунку потрібно розмістити з використанням одного server_name. Це можна зробити, перенісши блок location з конфігурацією нашого реверс-проксі в блок server, за допомогою якого ми налаштували хостинг статичного контенту.
Єдине, що слід у такому випадку поміняти для блоку location — це його шлях, наприклад, на /api/. З такою конфігурацією Nginx буде відфільтровувати всі запити, які приходять на 80-й порт для цього server_name. І якщо адреса запиту починатиметься з /api/, то цей запит перенаправиться та обробиться нашим реверс-проксі, тобто передасться на API. Решта запитів для цього хоста будуть оброблятися як запити на отримання статичного контенту. Ось так має виглядати результуючий файл конфігурації вебсайту:
server {
listen 80;
listen [::]:80;
root /var/www/timenow;
server_name timenow.local;
location /api/ {
proxy_pass http://localhost:8080/;
add_header Access-Control-Allow-Origin http://timenow.local;
}
}
Перезавантажимо Nginx і перевіримо, чи API доступний за новим ендпоінтом:
Після цієї зміни і API, і статичний контент доступні за різними адресами на одному домені. Залишилось лише підправити адресу API на нову в HTML-сторінці, щоб кнопка знову запрацювала:
Реверс проксі часто використовують для реалізації особливостей поведінки вебсайтів, які вже є на вебсерверах: додавання фільтрації по хост-хедеру, додавання response-хедерів, розміщення якогось окремого додатку за іншим шляхом на домені існуючого вебсайту тощо. За допомогою реверс-проксі можна навіть додати авторизацію на будь-який вебсайт.
Шифрування TLS
До цього моменту комунікація з нашим вебсайтом була не захищена. HTTP-трафік сам по собі ніяк не шифрується, тому будь-хто в нашій мережі має можливість переглядати, які дані ми передаємо на вебсервер, та які отримуємо у відповідь. Для вебсайту, який показує час, це не критично, а от якби це був вебсайт банку чи якогось іншого ресурсу — зовсім інша справа.
Перед тим, як перейти до практики, спочатку розберемось із теорією — протоколами шифрування для вебтрафіку:
-
HTTPS — це версія HTTP, яка використовує протокол SSL/TLS для шифрування трафіку, тобто запитів до вебсерверу та відповідей від нього.
-
SSL — це один із найперших протоколів шифрування трафіку, випущений ще в 1985 році компанією Netscape разом із їх браузером. Netscape випускав нові версії протоколу в міру того, як у старих знаходили вразливості, але вже в 1999 році передав контроль над протоколом міжнародній організації IETF (Internet Engineering Task Force). IETF займається стандартизацією протоколів, що використовуються Internet, наприклад, саме вони відповідають за те, як описаний протокол TCP та IP. IETF допрацювала SSL та випустила нову його версію — TLS. Саме тому коли говорять про шифрування вебтрафіку, іноді згадують SSL, іноді TLS, а іноді SSL/TLS.
-
TLS працює за схожим принципом з SSH. Він використовує асиметричне шифрування для встановлення зʼєднання між клієнтом і сервером, і для його роботи потрібна пара з публічного і приватного ключа, яку називають сертифікатом.
Для того, щоб браузер міг перевірити валідність сертифіката, він повинен містити інформацію про публічний ключ організації, яка входить у список Trusted Root Certificate Authorities.
Сертифікат має також набір властивостей, таких як дата видачі, дата, до якої він дійсний, імʼя організації, яка згенерувала цей сертифікат, та найголовніше — subject, або предмет сертифіката. Предметом сертифіката є DNS-імʼя, для якого цей сертифікат згенерований. Отримати дійсний сертифікат можна або купивши його в організації, яка продає сертифікати, або скориставшись безкоштовним центром видачі сертифікатів Let's Encrypt. В обох випадках процедура отримання сертифіката охоплює перевірку організацією, що видає сертифікат, чи точно саме вам належить DNS-імʼя.
Але годі теорії, перейдімо до практики. На даному етапі в нас немає доменного імені, власність якого ми можемо довести організації, що видає сертифікати, але ми можемо згенерувати TLS-сертифікат самостійно. Він буде підписаний не trusted root certificate authority, а нами, тому він називається self-signed. У Linux сертифікат можна згенерувати за допомогою програми OpenSSL:
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt
reqвказує на те, що ми хочемо згенерувати сертифікат;-x509вказує на те, що ми хочемо згенерувати самопідписаний сертифікат;-nodesвказує на те, що ми не хочемо захищати згенерований сертифікат паролем;-days 365вказує на термін придатності сертифіката в днях;-newkey rsa:2048вказує на те, що для нашого сертифіката ми також знерегуємо новий приватний ключ за алгоритмом RSA довжиною 2048 біт;- останні два параметри вказують файл, у який буде збережено приватний ключ сертифіката та його публічний ключ. Коли ми виконаємо цю команду, OpenSSL також попросить ввести властивості сертифіката, зокрема і його предмет, тобто DNS імʼя.
Налаштування TLS
Коли приватний і публічний ключі сертифіката згенеровані, ми можемо легко використати їх для конфігурації нашого вебсайту. Для Nginx треба зробити декілька змін:
Спершу треба підправити TCP порт у командах listeners. Додатково для listeners треба вказати параметр ssl:
Якщо ти використовуєш TLS шифрування, server_name повинен бути вказаний обовʼязково, і він повинен відповідати предмету сертифіката, інакше браузер вважатиме сертифікат невалідним для вебсайту.
Залишилось додати шляхи до файлів приватного та публічного ключів сертифіката, що ми згенерували. Це можна зробити командами ssl_certificate для файлу публічного ключа і ssl_certificate_keyдля приватного:
ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
Після всіх внесених змін твій файл конфігурації повинен виглядати ось так:
server {
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
root /var/www/timenow;
server_name timenow.local;
location /api/ {
proxy_pass http://localhost:8080/;
add_header Access-Control-Allow-Origin https://timenow.local;
}
}
Тепер перезавантаж сервіс вебсервера та перевір, чи працює сайт по HTTPS:
Щоб скрипт звертався до API по HTTPS, підправимо наш HTML:
Тепер якщо ти зайдеш на сайт з HTTPS, то отримаєш помилку. Ця помилка каже, що сертифікат, який ми отримали — невалідний, оскільки він не містить інформації про certificate authority. Це цілком очікувано, адже сертифікат self-signed, а ми просто тренуємось працювати з TLS на своєму комп'ютері. У Chrome цю помилку можна проігнорувати, тоді сайт завантажиться і працюватиме, але через HTTPS, використовуючи TLS-шифрування. До того ж якщо ти подивишся інформацію про сертифікат у браузері, то побачиш, що це саме той сертифікат, який ми згенерували раніше.
Уявимо, що в нашого вебсайту є користувачі, які звикли заходити на сайт по HTTP. Після того, як ми включили SSL на наших listener і змінили порт, вебсервер не відповідає на HTTP-запити коректно. Оскільки ми не видалили сайт за замовчуванням, наші звичайні HTTP запити потрапляють до нього, а якби конфігурації цього сайту не було б, ми б взагалі не змогли б отримати відповідь на запит HTTP.
Щоб це поправити, потрібно додати конфігурацію редіректу — настроїти вебсервер так, щоб коли користувач заходить на посилання по HTTP, вебсервер перенаправляв його на HTTPS. В Nginx для цього потрібно створити ще один блок server:
А ось і сам блок:
server {
listen 80;
listen [::]:80;
server_name timenow.local;
return 301 https://timenow.local$request_uri;
}
Тут можна побачити нову команду — return. Параметри, які потрібно використати для редіректу:
- HTTP-статус код для редіректу (301, moved permanently);
- адреса, на яку здійснюється редірет: https, timenow (власне наш домен);
$request_uri— змінна, яка позначає URI, для якого здійснювався запит.
Перезавантажимо вебсервер і протестуємо редірект:
Оскільки браузери можуть кешувати інформацію про редіректи, то відкриємо нове анонімне вікно в браузері та Developer Tools, щоб мати можливість переглядати HTTP-запити та відповіді. Тепер коли спробуємо перейти за старою HTTP-адресою нашого вебсайту, то отримаємо відповідь з HTTP статус кодом 301 — це і є наш редірект. Якщо знову проігноруємо помилку із сертифікатом, то наш вебсайт завантажиться по HTTPS.