Во время установки и получения сертификата с помощью Ansible — возник вопрос с доступом к серверу для верификации, т.к. доступ к портам 80 и 443 ограничен на уровне Azure Network Security Group.
Для certbot
можно было бы использовать manual верификацию через DNS — но тут требовалось или ручная обработка, или боль с Azure DNS API — и не факт, что она работала бы.
Другое решение — ограничить доступ к порту 80 на уровне NGINX с помощью правил allow/deny
и открыть этот порт в Azure.
Далее, сервер на порту 80 будет выполнять редирект на 443, где дополнительно используется обычная HTTP-авторизация.
Для установки и получения сертификата Lets Encrypt — используется Ansible роль jaywink.letsencrypt
.
Используется сетап из предыдущеего поста — Ansible: ansible-galaxy – репозиторий ролей и Jenkins VM provision.
Содержание
Lets Encrypt и Ansible
Роль jaywink.letsencrypt
вызывается из файла provision.yml
, который сейчас выглядит следующим образом:
- hosts: all become: true roles: - role: common - role: jnv.unattended-upgrades unattended_mail: [email protected] unattended_automatic_reboot: true unattended_automatic_reboot_time: 03:00 - role: mongrelion.docker - role: jaywink.letsencrypt letsencrypt_domain: "{{ inventory_hostname }}" letsencrypt_email: [email protected] letsencrypt_request_www: false letsencrypt_pause_services: nginx - role: nginx - role: SoInteractive.prometheus prometheus_external_labels: monitoring: dev
letsencrypt
роль выполнит установку certbot
, потом остановит веб-сервер, указанный в letsencrypt_pause_services
(тут — nginx
), выполнит верификацию и получит сертификат.
Полностью его команда будет выглядеть так:
"cmd": "./certbot-auto --renew-by-default certonly --standalone --expand --text -n --no-self-upgrade -m '[email protected]' --agree-tos --domains dev.monitor.domain.ms 2>&1"
Для верификации — certbot
использует порт 80, который мы и откроем в Azure Network Security Group.
NGINX и Ansible
Роль nginx
описана в посте Ansible: пример установки NGINX и в её шаблоне ничего не менялось, кроме адреса бекенда в переменных инвентори-файла.
Теперь — обновим шаблон NGINX, и выполним следующее:
- добавим
server {}
на порту 80 с правиламиallow/deny
, который будет выполнять редирект на порт 443ssl
; - уже в сервере, слушающем порт 443 — HTTP-авторизация, плюс доступ будет ограничен правилами Network Security Group в Azure
Шаблоны NGINX:
[simterm]
$ ls -l roles/nginx/templates/ total 8 -rw-r--r-- 1 setevoy setevoy 875 Sep 20 17:31 nginx.conf -rw-r--r-- 1 setevoy setevoy 1032 Sep 20 17:31 nginx_vhost.conf
[/simterm]
Приводим файл настроек виртуалхоста к такому виду:
upstream {{ upstream_name }} { server {{ upstream_url }}; } server { server_name www.{{ inventory_hostname }}; listen 80; return 301 https://{{ inventory_hostname }}$request_uri; } server { server_name {{ inventory_hostname }}; listen 80; allow {{ nginx_allow_kiev_ip }}; allow {{ nginx_allow_berlin_ip }}; deny all; return 301 https://{{ inventory_hostname }}$request_uri; } server { server_name {{ inventory_hostname }}; listen 443 ssl; access_log /var/log/nginx/{{ inventory_hostname }}-access.log proxy; error_log /var/log/nginx/{{ inventory_hostname }}-error.log notice; ssl on; ssl_certificate /etc/letsencrypt/live/{{ inventory_hostname }}/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/{{ inventory_hostname }}/privkey.pem; root /var/www/{{ inventory_hostname }}; location / { proxy_redirect off; 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 http://{{ upstream_name }}$request_uri; } }
В файл provision.yml
для роли nginx
добавляем две переменные с IP, с которых будет разрешён доступ:
... - role: nginx nginx_allow_kiev_ip: 194.***.***.45 nginx_allow_berlin_ip: 37.***.***.130 ...
В шаблоне провижена группы ресурсов (из поста Azure: provisioning с Resource Manager, Jenkins и Groovy) — обновляем правила Network Security Group и открываем доступ к порту 80 отовсюду:
... { "name": "HTTP_All_Allow", "properties": { "description": "HTTP All Allow", "protocol": "Tcp", "sourcePortRange": "*", "destinationPortRange": "80", "sourceAddressPrefix": "*", "destinationAddressPrefix": "*", "access": "Allow", "priority": 100, "direction": "Inbound" } }, ...
HTTP авторизация
Последним шагом — надо добавить HTTP авторизацию.
В файл шаблона NGINX добавляем:
... root /var/www/{{ inventory_hostname }}; auth_basic_user_file /var/www/{{ inventory_hostname }}/.htaccess; auth_basic "Password-protected Area"; location / { ...
В каталоге роли nginx
— создаём каталог files
:
[simterm]
$ mkdir roles/nginx/files
[/simterm]
В и нём — файл .htaccess
с логином-паролем:
[simterm]
$ htpasswd -c roles/nginx/files/.htpasswd jmadmin New password: Re-type new password: Adding password for user jmadmin
[/simterm]
В файл roles/nginx/tasks/main.yml
добавляем копирование файла на хост:
... - name: Copy .htaccess template: src=files/.htpasswd dest=/var/www/{{ inventory_hostname }}/.htaccess ...
Готово:
[simterm]
$ git add -A && git commit -m "HTTP Auth" && git push
[/simterm]
Из логов Jenkins:
... TASK [jaywink.letsencrypt : debug] ********************************************* ok: [dev.monitor.domain.ms] => { "msg": [ "IMPORTANT NOTES:", " - Congratulations! Your certificate and chain have been saved at", " /etc/letsencrypt/live/dev.monitor.domain.ms/fullchain.pem. Y ...
HTTP Auth:
... TASK [nginx : Copy .htaccess] ************************************************** sudo -H -S -n sudo -H -S -n ...
Готово.