Локальний SSL для розробки та тестів з Rails 5

4 хв. читання

Ваш Rails застосунок використовує SSL на продакшені? Гарною ідеєю буде використовувати SSL й локально. Все було б добре, якби б не проблеми при налаштуванні. Використання SSL з Capybara для приймального тестування традиційно вважалося ще складнішим. Але тепер усе змінилося.

ddd

У статті ми розглянемо як я налаштовути свій Rails 5 застосунок для локальної розробки та системних тестів через SSL з wildcard-сертифікатами.

Локальний SSL для розробки та тестів з Rails 5

Чому локальний SSL?

Спершу тред.

Підсумовуючи викладене, якщо ваш застосунок у production на SSL, ви захочете розробляти з SSL локально і використовувати його для тестування:

  1. Логіки URL (роутинг, історія, перенаправлення, застосування https).
  2. Відсутності попереджень про змішаний контент.
  3. CORS через межі (boundaries) http чи https.
  4. Фіч веб платформ (наприклад, геолокації), що потребують SSL (чи будуть потребувати).
  5. Безпечних веб-сокетів (wss) як оновлення https.
  6. Безпечних cookies, які поводяться дуже по-різному з http та https.
  7. https-заголовків, на зразок HSTS або CORS allow-*.
  8. Сторонніх інтеграцій, наприклад, на основі OAuth, що вимагають SSL.

Єдиним недоліком локального SSL є те, що його вважають складним у налаштуванні. Стаття та супутній репозиторій, rossta/local-ssl-demo-rails, допоможуть розібратися.

Як усе працює?

Загальний алгоритм, який ми будемо використовувати для налаштування Rails-застосунків для локальної розробки та тестів через SSL наступний:

  1. Перевизначити домен до localhost.
  2. Створити самозавірений SSL сертифікат.
  3. Вказати браузеру, що можна довіряти сертифікату.
  4. Налаштувати локальний сервер на використання сертифікату.

Існує велика кількість альтернатив до наведеного алгоритму.

Варто оглянути статтю Як налаштувати SSL на OSX, де розглядається аналогічне налаштування Node.js серверу.

Замість того, щоб організовувати довіру сертифікатам для кожного розроблюваного застосунку, ви можете створити власний центр сертифікації (СА) SSL. Таке налаштування буде заплутанішим, але, якщо браузер довіряє вашому локальному СА, ви можете уникнути процесу ручного налаштування кожного сертифікату.

Або застосуйте ngrok, автоматично налаштовуваний сервіс для запуску локального сервера через безпечний URL. Хоч такий підхід добре працює у разі локальної розробки, я наразі не знаю будь-кого, хто б використовував його для тестів або CI середовищ. Огляньте цю статтю аби дізнатися більше про ngrok.

Перевизначити домен до localhost

Для локального використання SSL з користувацьким доменом (тобто щось окрім localhost), вам необхідно знайти спосіб маршрутизації цього домену назад до вашого локального IP. У статті припустимо, що IP 127.0.0.1. Він може бути іншим, якщо ви розробляєте на окремій віртуальній машині, такій як Vagrant.

Ось кілька альтернатив використанню користувацького доменного імені для локальної розробки та тестування.

Ручне налаштування

Найпростіший підхід — додати кожен домен, який ви хочете використовувати, до вашого /etc/hosts файлу.

# /etc/hosts

127.0.0.1     localhost.ross

Недолік у тому, що /etc/hosts не підтримує wildcard-домени, тому вам необхідно буде додати рядок для кожного унікального домену, який плануєте використовувати.

Динамічні локальні домени

З метою досягнення гнучкості ви, можливо, захочете використовувати dnsmasq для маршрутизації довільних доменних імен у будь-якому місці. У нашому випадку ми можемо використовувати його для надсилання усього трафіку на користувацькому TLD назад до нашої локальної машини.

Наступний скрипт встановить та сконфігурує dnsmasq. Сервер dnsmasq вирішить усі запити домену верхнього рівня .ross на моїй локальній машині назад до 127.0.0.1 (замініть $(whoami) бажаним доменом верхнього рівня).

local_tld=$(whoami)
brew install dnsmasq
mkdir -pv $(brew --prefix)/etc
sudo cp -v $(brew --prefix dnsmasq)/homebrew.mxcl.dnsmasq.plist /Library/LaunchDaemons
sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist
sudo mkdir -pv /etc/resolver
echo "address=/.$local_tld/127.0.0.1" | sudo tee -a $(brew --prefix)/etc/dnsmasq.conf
echo "nameserver 127.0.0.1" | sudo tee /etc/resolver/$local_tld

Використовуйте зареєстровані доменні імена

Або придбайте домен та додайте А-запис, що вирішує appex та wildcard субдомени для 127.0.0.1. Такий підхід використовується такими відомими localhost-доменами, як lvh.me або xip.io.

Створити самозавірений SSL сертифікат

Наступний скрипт згенерує самозавірений сертифікат та приватний ключ для localhost.ross на моїй машині.

name=localhost.$(whoami)
openssl req \\
  -new \\
  -newkey rsa:2048 \\
  -sha256 \\
  -days 3650 \\
  -nodes \\
  -x509 \\
  -keyout $name.key \\
  -out $name.crt \\
  -config <(cat <<-EOF
  [req]
  distinguished_name = req_distinguished_name
  x509_extensions = v3_req
  prompt = no
  [req_distinguished_name]
  CN = $name
  [v3_req]
  keyUsage = keyEncipherment, dataEncipherment
  extendedKeyUsage = serverAuth
  subjectAltName = @alt_names
  [alt_names]
  DNS.1 = $name
  DNS.2 = *.$name
EOF
)

Файли ключ/сертифікат, що створені на моїй машині називатимуться localhost.ross.key та localhost.ross.crt.

Для генерації TLD для іншого домену, замініть localhost.$(whoami) на бажане доменне ім'я. Ви можете пропустити рядок DNS.2 = *.$name, якщо ви не потребуєте wildcard-субдоменів або якщо ви просто встановлюєте SSL для localhost.

Для Rails проектів, я, зазвичай, створюю окремі ключі/пари та переміщую кожну пару файлів до моєї Rails config директорії:

mkdir -p config/ssl
mv localhost.ross.key localhost.ross.crt config/ssl

Довіра сертифікату

На macOS ви можете організувати довіру сертифікату у System Keychain одним рядком:

sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain config/ssl/localhost.ross.crt

Існує багато постів, що наочно демонструють як зробити це вручну через застосунок Keychain — так також працюватиме.

Налаштувати локальний сервер

Тепер, коли у нас є довірений сертифікат, ми можемо сконфігурувати Puma з парою ключ/сертифікат для обслуговування локальних SSL запитів користувацьких доменів development і test. (Ви можете також використовувати Nginx з метою використання proxy для локальних запитів та налаштувати конфігурацію Nginx з вашим SSL сертифікатом).

У розробці

Спершу, я, зазвичай, розміщую самозавірений ключ та сертифікат у директорії мого Rails-проекту.

cd path/to/my/rails/app
mkdir config/ssl
mv path/to/localhost.ross.{key,crt} config.ssl

З Puma ми можемо прив'язати сервер до SSL URL при запуску, вказавши шляхи до пар ключ/сертифікат, які ми створили на попередньому кроці.

Тепер, при запуску Rails-серверу з кореня проекту для локальної розробки, я буду вказувати ssl-прив'язку наступним чином:

rails s -b 'ssl://127.0.0.1:3000?key=config/ssl/localhost.ross.key&cert=config/ssl/localhost.ross.crt'

Оскільки я використовую foreman для локального запуску застосунку, я розміщую команду у файлі Procfile.dev та заміняю номер порту змінною $PORT:

rails s -b 'ssl://127.0.0.1:$PORT?key=config/ssl/localhost.ross.key&cert=config/ssl/localhost.ross.crt'

Puma також дає змогу встановлювати прив'язку у конфігураційному файлі.

Якщо ви використовуєте Webpack з webpacker gem для об'єднання JavaScript та інших статичних ресурсів, ви можете встановити зв'язок з webpack-dev-server при розробці через SSL. Реалізуємо викладене у файлі config/webpacker.yml:

# config/webpacker.yml
development:
  <<: *default
  # ...
  dev_server:
    https: true

У більш ранніх версіях webpacker-dev-server, SSL-сертифікат генерується за потребою; ви можете організувати довіру до сертифіката вручну у Keychain окремо, щоб уникнути помилок у браузері.

У тестуванні

Використання вашого SSL сертифікату для локального Rails тестування в основному актуальне для системних тестів (так званих приймальних чи функціональних тестів). Там ми, зазвичай, використовуємо Capybara для запуску реального серверу застосунків. Налаштування Capybara для обробки запитів SSL було болючим та спричиняло деякий серверний monkeypatching, щоб зв'язати усе.

Починаючи з Capybara >= 3.1.0, стало набагато легше передати конфігурацію базового сервера Puma, щоб включити наші SSL сертифікати (commit). Для цього застосовується прив'язка, подібно до того, як ми робили при розробці:

Capybara.server = :puma, server: {
  Host: "ssl://#{Capybara.server_host}?key=config/ssl/localhost.ross.key&cert=config/ssl/localhost.ross.crt"
}

Системні тести також покладаються на веб-драйвер для контролю браузера; у більшості випадків ці веб-драйвери проігнорують попередження про неприпустимий сертифікат. Щоб отримати новіші headless-версії Chrome та Firefox для роботи з SSL, я виявив необхідність у додатковому налаштуванні.

По-перше, використаємо webdrivers gem, щоб переконатися, що у нас є останні бінарники chromedriver та geckodriver для тестування на Chrome та Firefox відповідно.

Раніше недійсні SSL сертифікати не могли працювати з headless-Chrome, тому в цьому режимі системні тести з SSL були неможливими. Але з останнім релізом Chrome 65 усе змінилося. Тепер ми можемо налаштувати headless Chrome-драйвер з можливістю приймати недійсні SSL сертифікати таким чином:

Capybara.register_driver(:headless_chrome_ssl) do |app|
  options = Selenium::WebDriver::Chrome::Options.new(
    args: %w[--headless --disable-gpu --no-sandbox --disable-web-security],
  )
  capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
    acceptInsecureCerts: true,
  )
  Capybara::Selenium::Driver.new(
    app,
    browser: :chrome,
    options: options,
    desired_capabilities: capabilities
  )
end

Можемо перейти до нашого нового драйвера у RSpec таким трюком:

RSpec.configure do |config|
  config.before(:each, type: :system, js: true) do
    driven_by :headless_chrome_ssl
  end
end

Для Firefox конфігурація драйвера подібна:

Capybara.register_driver(:headless_firefox_ssl) do |app|
  options = Selenium::WebDriver::Firefox::Options.new(args: %w[--headless])

  capabilities = Selenium::WebDriver::Remote::Capabilities.firefox(
    acceptInsecureCerts: true,
  )
  Capybara::Selenium::Driver.new(
    app,
    browser: :firefox,
    options: options,
    desired_capabilities: capabilities
  )
end

З нашим сервером та налаштованими драйверами, ми встановлюємо Capybara хост за замовчуванням з нашим користувацьким доменом на https:

Capybara.app_host = "https://www.localhost.ross"

Тепер усе налаштовано для запуску системних тестів через SSL!

Вимоги

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

$ openssl version
LibreSSL 2.2.7

$ ruby -v
ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin16]

$ /Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --version
Google Chrome 68.0.3440.106

$ /Applications/Firefox.app/Contents/MacOS/firefox --version
Mozilla Firefox 60.0.1

$ ~/.webdrivers/chromedriver -v
ChromeDriver 2.41.578706 (5f725d1b4f0a4acbf5259df887244095596231db)

$ ~/.webdrivers/geckodriver --version
geckodriver 0.21.0

Та обрані пакети Ruby у Gemfile:

gem 'rails', '~> 5.2.1'
gem 'puma', '~> 3.12'
gem 'webpacker', '~> 3.5.5' # optional

group :test do
  gem 'capybara', '~> 3.5.1'
  gem 'selenium-webdriver', '~> 3.14.0'
  gem 'webdrivers', '~> 3.3.3'
  gem 'rspec-rails', '~> 3.8.0' # optional
end

Інші версії цих інструментів також можуть добре працювати. Наприклад, конфігурація серверу Puma була додана в Capybara 3.1.0, а chromedriver додав підтримку для acceptInsecureCerts прапора у 2.35/Chrome 65. Просто майте на увазі, що з використанням старіших версій chromedriver можуть виникнути проблеми.

Я рекомендую пакет webdrivers, тому що він встановлює останні версії драйверів, включаючи chromedriver для Chrome та geckodriver для Firefox. Існує велика кількість постів, де можна знайти інструкцію з встановлення chromedriver з Homebrew або посилання на chromedriver-helper. Таким чином, ваші драйвери завжди будуть оновлюватись вчасно.

Висновок

Якщо ви скрізь використовуєте SSL, чому б не спробувати його на localhost? Налаштування SSL для розробки та тестування у Rails вимагає трохи зусиль. Але все стало набагато простішим з впровадженням системи тестів Rails та нещодавніми впровадженнями Puma, Capybara, Selenium, а також веб драйверів Chrome and Firefox.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 4.7K
Приєднався: 10 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація