В данной статье описан деплой статического сайта на Fedora-дроплет с помощью rsync. В данном случае для генерации статики использовался Hugo, но большая часть статьи применима к любому генератору.

Предпосылки

Для деплоя будет использоваться пользователь nameless. Предположим, что он уже существует, и для него настроен SSH-доступ по ключу.

Также для сайта зарегистрировано доменное имя.

Подготовка и первый деплой

Создадим директорию для сайта со всеми необходимыми правами:

install --mode 775 --owner nameless --group nameless --directory /srv/maximov.space

На локальной машине сгенерируем сайт, выполнив команду hugo, и скопируем полученные файлы с помощью rsync:

rsync -avz --delete public/ nameless@maximov.space:/srv/maximov.space/

Настройка Nginx

Для отдачи статики используется Nginx. Настроено автоматическое перенаправление с HTTP на HTTPS:

server {
  listen 80;
  listen [::]:80;

  server_name maximov.space;
  return 301 https://$host$request_uri;
}

server {
  listen 443 http2 ssl;
  listen [::]:443 http2 ssl;

  server_name maximov.space;

  ssl_certificate "/etc/letsencrypt/live/maximov.space/fullchain.pem";
  ssl_certificate_key "/etc/letsencrypt/live/maximov.space/privkey.pem";
  ssl_trusted_certificate "/etc/letsencrypt/live/maximov.space/chain.pem";

  include /etc/nginx/ssl_params.conf;

  sendfile on;
  keepalive_timeout 65;
  gzip on;

  root /srv/maximov.space;
  autoindex on;
}
Файл /etc/nginx/sites-available/maximov.space.conf

Т.к. DNS у меня рулится через DigitalOcean, то для получения и обновления сертификатов я использовал плагин certbot-dns-digitalocean для дефолтного клиента LetsEcrypt. Этот процесс хорошо раскрыт в документации, так что я его расписывать не буду.

Настройки SSL вынесены в отдельный файл:

ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;

ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES2\
56-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;

# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;

# OCSP Stapling ---
# fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on;
Файл /etc/nginx/ssl_params.conf

После проверки, что конфиг Nginx валиден (nginx -t), необходимо его перезагрузить следующей командой:

systemctl reload nginx

Если сейчас попытаться открыть сайт в браузере, мы получим ошибку 403.

Исправление ошибок

Т.к. файлы сайта у нас доступны на чтение для всех, то очевидно, что ошибка связана с SELinux.

Поищем причину в /var/log/audit/audit.log, увидим следующую запись:

type=AVC msg=audit(1562159838.126:2777690): avc:  denied  { getattr } for  pid=31630 comm="nginx" path="/srv/maximov.space/index.html" dev="vda1" ino=265896 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:var_t:s0 tclass=file permissive=0

Сообщение означает, что процессу Nginx отказано в доступе к файлу index.html.

Причина в том, что файлы сайта, которые находятся внутри /srv/maximov.space/ не имеют SELinux-типа httpd_sys_content_t, в чём можно убедиться, просмотрев выдачу команды semanage fcontext -l | grep httpd_sys_content_t. Этот тип используется для статического веб-контента, доступного в режиме чтения процессу nginx и скриптам, которые он запускает. Чтобы добавить этот тип к файлам сайта, необходимо выполнить следующую команду:

semanage fcontext -a -t httpd_sys_content_t '/srv/maximov.space(/.*)?'

Чтобы это изменение вступило в силу, необходимо также обновить SELinux-контекст для файлов сайта.

restorecon -Rv /srv/maximov.space

Заключение

Вот так мы с минимальными усилиями настроили деплой и хостинг блога на Hugo на Linux VPS. В следующей статье я планирую настроить деплой в процессе CI.