Во время установки и получения сертификата с помощью 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 ...
Готово.




