Делаем HTTPS сайтик с помощью Nginx + Docker Compose + Let’s Encrypt

Привет! Думаю, в последние 2 года каждый из нас испытал на себе тренд ухода в HTTPS. Про это говорят все: на конференциях, в подкастах и блогах. Причин несколько. Во-первых, тема сесурити стала сильно актуальнее, чем раньше, из-за многочисленных взломов. Во-вторых, появились бесплатные SSL/TLS Certificates. Давайте тогда же посмотрим, как просто прикрутить сертификат ко своему сайту?

Начнём попорядку. Есть сайт (и одноименная организация) — Let’s Encrypt. По итогам 2017 года, это самый крупный SSL регистратор. Он выдал огромное количество сертов. Ещё бы, ведь они бесплатны! То есть, мы будем получать свой сертификат тут.

Есть одна проблема: Let’s Encrypt выдаёт сертификаты лишь только на 3 месяца. А ведь это так утомительно продлять сертификаты, а если ещё и доменов много — то це біда. К счастью для нас, был разработан умный, интелектуальный бот — https://certbot.eff.org/, который будет жить с нами и сам, когда прийдёт время (например, раз в месяц) будет ходить и продлять сертификат. Вам даже делать ничего не прийдётся.

Остаётся понять, как же это всё дело скрестить. Ведь тут тебе и боты, и сертификаты, и nginx, и докеры, и шмокеры, и композы. Мутное дело, сразу видно. Но ясен красен за нас уже всё сделано. Корневая вещь, которая создана — это специальный контейнер для ЛетсЭнкрипта — https://hub.docker.com/r/jrcs/letsencrypt-nginx-proxy-companion/. Этот контейнер позволяет использовать Nginx в качестве прокси, а сам он будет отвечать за регистрацию и продление сертификатов.

Теперь нам надо всё это дело подружить с docker-compose. На самом деле, у letsencrypt-nginx-proxy-companion много документации и примеров, как это можно сделать. Лично мне понравился этот пример — https://github.com/evertramos/docker-compose-letsencrypt-nginx-proxy-companion.

Быстрый старт

Первое, что вы должны сделать — это clone проекта — https://github.com/evertramos/docker-compose-letsencrypt-nginx-proxy-companion. Из этого проекта нам понадобяться следующие файлы:

  • .env
  • run.sh
  • docker-compose.yml

Конечно же, в docker-compose.yml требуется добавить свои сервисы, которые вам нужны. Например так может выглядить итоговый композ-конфиг с одним бизнес-сервисом (контейнер gateway):

version: '3'
services:
  nginx-web:
    image: nginx
    labels:
        com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: "true"
    container_name: ${NGINX_WEB}
    restart: always
    ports:
      - "${IP}:80:80"
      - "${IP}:443:443"
    volumes:
      - ${NGINX_FILES_PATH}/conf.d:/etc/nginx/conf.d
      - ${NGINX_FILES_PATH}/vhost.d:/etc/nginx/vhost.d
      - ${NGINX_FILES_PATH}/html:/usr/share/nginx/html
      - ${NGINX_FILES_PATH}/certs:/etc/nginx/certs:ro
      - ${NGINX_FILES_PATH}/htpasswd:/etc/nginx/htpasswd:ro

  nginx-gen:
    image: jwilder/docker-gen
    command: -notify-sighup ${NGINX_WEB} -watch -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
    container_name: ${DOCKER_GEN}
    restart: always
    volumes:
      - ${NGINX_FILES_PATH}/conf.d:/etc/nginx/conf.d
      - ${NGINX_FILES_PATH}/vhost.d:/etc/nginx/vhost.d
      - ${NGINX_FILES_PATH}/html:/usr/share/nginx/html
      - ${NGINX_FILES_PATH}/certs:/etc/nginx/certs:ro
      - ${NGINX_FILES_PATH}/htpasswd:/etc/nginx/htpasswd:ro
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro

  nginx-letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion
    container_name: ${LETS_ENCRYPT}
    restart: always
    volumes:
      - ${NGINX_FILES_PATH}/conf.d:/etc/nginx/conf.d
      - ${NGINX_FILES_PATH}/vhost.d:/etc/nginx/vhost.d
      - ${NGINX_FILES_PATH}/html:/usr/share/nginx/html
      - ${NGINX_FILES_PATH}/certs:/etc/nginx/certs:rw
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      NGINX_DOCKER_GEN_CONTAINER: ${DOCKER_GEN}
      NGINX_PROXY_CONTAINER: ${NGINX_WEB}
      
  gateway:
    container_name: gateway
    build: ./gateway
    hostname: gateway
    restart: always
    environment:
      VIRTUAL_HOST: ${DOMAINS}
      LETSENCRYPT_HOST: ${DOMAINS}
      LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}       

networks:
  default:
    external:
      name: ${NETWORK}

Далее вам требуется немного поменять параметры окружения в .env. Итоговая версия файла может выглядить так:

#
# docker-compose-letsencrypt-nginx-proxy-companion
# 
# A Web Proxy using docker with NGINX and Let's Encrypt
# Using the great community docker-gen, nginx-proxy and docker-letsencrypt-nginx-proxy-companion
#
# This is the .env file to set up your webproxy enviornment

# Define the names for your local containers
NGINX_WEB=nginx-web
DOCKER_GEN=nginx-gen
LETS_ENCRYPT=nginx-letsencrypt

# Your external IP address
IP=5.45.80.213

# Network name
NETWORK=webproxy

# NGINX file path
NGINX_FILES_PATH=/etc/nginx/data

# Your domain (or domains)
DOMAINS=hixon.ru

# Your email for Let's Encrypt register
LETSENCRYPT_EMAIL=hixon@yandex.ru

После этого вам нужно закинуть ваши исходники, а также эти 3 файла: .env, docker-compose.yml и run.sh (логично предположить, что они будут лежать в коне вашего git-проекта) на сервер.

Далее, всё, что вам остаётся сделать — это запустить run.sh. Он сделает пару очевидных вещей: создаст сетку, получит актуальный шаблон для Nginx с гитхаба (если вы параноик, закэшиуйрте локально nginx.tmpl и убирите сетевой вызов в гитхаб), а также запустит ваш docker-compose.

Важно, что первый запуск у вас займёт порядка 5 минут. Ведь именно в этот момент времени вы будете получать сертификат.

К чему мы пришли?

У вас есть домен, защищенный сертификатом. Серт будет автоматически обновляться. При попытке открыть сайт по http, будет выполняться редирект на https (HTTP 301). Все статические ресурсы — css, js, images — доступны только по https.

С точки зрения инфраструктуры тут следующее решение. Есть прокси — nginx, который слушает на 80 и 443. Есть ваши бизнесовые контейнеры — например, «сайты». Каждый из них должен экспозить 80-й порт. 443-й порт экспозить не требуется. Других изменений энвайронмента тут не произошло.

Категории: Программирование