Vikky avatar

Виктор Шостак
   

Готовим боевой VDS на Debian 8 Jessie


Привет и добро пожаловать, странник из поисковой системы! Ты наконец-то нашёл то, что так долго искал. Да-да, это полный мануал по организации и настройке боевого VDS на Debian 8 Jessie с нуля, без лишней лирики. Всё ниже — было обкатано лично на многих десятках серверов.

Помните, что данная статья не претендует на истину в последней инстанции. Если у вас «что-то пошло не так», то автор не несёт никакой ответственности. Думайте, пожалуйста, перед тем, как копипастить в интернете! Во всех остальных случаях — милости прошу в комментарии.

В своей работе (и для личных проектов) я использую Python (Django/Flask) и PHP (Laravel), поэтому конфиг сервера будет максимально общим:

  • Веб-сервер Nginx 1.11+ (HTTP/2 + ALPN)
  • РСУБД PostgreSQL 9.6+
  • SSL Let's Encrypt
  • Git

Выбор облачного сервера и его параметров

На данный момент, я отдаю предпочтение облачным серверам от Simple Cloud. Во-первых, потому что сочетание «цена/качество» лучшее в этом сегменте. Во-вторых, из-за гибкой системы расширения параметров VDS, если ресурсов стало мало. В-третьих, потому что служба поддержки готова помочь 24/7/365 (причём именно «помочь», а не «послать» в гугл).

И немаловажный плюс — это российская компания, которая держит сервера в России (дата-центры в Петербурге или Москве — на ваш выбор). Это особенно важно в наше время, когда за одним числом могут заблокировать, например, LinkedIn из-за хранения данных о россиянах за бугром.

Для всех текущих проектов — мне хватает VDS за 350 рублей в месяц с параметрами:

  • 1x ядро CPU
  • 2GB RAM
  • 20GB SSD диск
  • система виртуализации KVM

Подготовка Debian к обновлению

После того, как вы зарегистрировались и добавили новый облачный VDS, на почту придёт письмо с root доступом и IP-адресом (для примера я буду использовать такой: 100.200.300.400).

Открываем консоль (PuTTY для Windows или нативную для macOS или Linux) и пишем:

$ ssh root@100.200.300.400

Вводим пароль из письма и мы уже на сервере. Так как система свежая, сразу же зададим locale, чтобы больше не наблюдать ошибку «Setting locale failed. Please check that your locale settings...»:

$ nano /etc/default/locale

Откроется текстовый редактор nano с содержимым файла. Заменяем всё на эти строки:

LANGUAGE="en_US.UTF-8"
LC_ALL="en_US.UTF-8"
LANG="en_US.UTF-8"
LC_TYPE="en_US.UTF-8"

Сохраняем изменения (Ctrl+O) и выходим из редактора (Ctrl+X). Далее, запускаем установку и реконфигурацию для локали:

$ locale-gen en_US.UTF-8 && dpkg-reconfigure locales

Рекомендую после этого перезагрузить VDS, иначе всё равно будете наблюдать ошибку «Setting locale failed...». Сделать это очень просто прямо из панели управления Simple Cloud.

Отлично, пора двигаться дальше! Давайте удалим предустановленную версию Nginx:

$ apt-get remove nginx nginx-common

Теперь можно проверить список репозиториев, которые будут использованы для обновления:

$ nano /etc/apt/sources.list

Откроется редактор с содержимым файла sources.list. Я использую следующий набор репозиториев, но вы можете оставить тот, который был по-умолчанию:

deb http://cdn.debian.net/debian jessie main contrib non-free
deb http://cdn.debian.net/debian jessie-updates main contrib non-free
deb http://security.debian.org/ jessie/updates main contrib non-free
deb http://http.debian.net/debian jessie-backports main

Сразу же добавим репозиторий для PostgreSQL, который потребуется в дальнейшем:

deb http://apt.postgresql.org/pub/repos/apt/ jessie-pgdg main

На начало февраля 2017 года, в репозитории Debian 8 всё ещё лежит Nginx собраный с OpenSSL 1.0.1t (без поддержки ALPN). Поэтому, мы намеренно берём его из Ubuntu 16.04 Xenial, где OpenSSL уже имеет нужную нам версию — 1.0.2h.

Без паники! Пакет полностью совместим с Debian и не будет тянуть за собой каких-либо зависимостей — только Nginx.

В конец вашего sources.list добавляем следующие строки:

deb http://ftp.ru.debian.org/debian/ jessie-backports main contrib non-free
deb-src http://ftp.ru.debian.org/debian/ jessie-backports main contrib non-free
deb http://nginx.org/packages/mainline/ubuntu/ xenial nginx
deb-src http://nginx.org/packages/mainline/ubuntu/ xenial nginx

Сохраняем файл и добавляем ключ для установки Nginx и PostgreSQL:

$ cd /tmp/
$ wget http://nginx.org/keys/nginx_signing.key
$ wget https://www.postgresql.org/media/keys/ACCC4CF8.asc
$ apt-key add nginx_signing.key && apt-key add ACCC4CF8.asc

Хорошо. Наконец-то можно обновить все пакеты, а также, установить Nginx, PostgreSQL и Git:

$ apt-get update && apt-get upgrade
$ apt-get install nginx postgresql-9.6 postgresql-contrib-9.6 git

После установки, выполним «размаскировку» процесса Nginx и перезапустим его:

$ systemctl unmask nginx && systemctl restart nginx

Настройка пользователя и SSH

Считается плохим тоном дальнейшая работа под root пользователем. Поэтому, создадим нового супер-юзера и дадим ему все привелегии. Также, будем хранить все наши сайты и SSL в домашней директории этого пользователя (то есть, в /home/myuser/, где myuser — это имя пользователя):

$ adduser myuser

Добавим его в группу супер-юзеров:

$ usermod -a -G sudo myuser

И обезопасим SSH:

$ nano /etc/ssh/sshd_config

В открывшемся файле настроек, меняем стандартный Port 22 на любой (в промежутке от 39999 до 65535) и отключаем авторизацию для root пользователя: PermitRootLogin no. Сохраняем файл и перезапускаем демон ssh:

$ /etc/init.d/ssh restart

Теперь можно зайти на сервер под пользователем myuser (тут XXXX — это номер выбранного порта):

$ ssh myuser@100.200.300.400 -p XXXX

Для последующей работы с привелегиями root пользователя, необходимо каждую команду запускать через sudo. Если система «ругается» на эту команду, значит данный пакет не установлен. Установите его командой: apt-get install sudo

Установка и настройка Let's Encrypt для домена

Первое. Установим certbot из официального репозитория EFF:

$ sudo git clone https://github.com/certbot/certbot /opt/letsencrypt

Добавим конфиг Nginx для домена mydomain.com:

$ sudo nano /etc/nginx/conf.d/mydomain.com.conf

В открывшийся пустой файл напишем:

server {
    listen 80;

    server_name mydomain.com;

    location /.well-known/acme-challenge {
        root /home/myuser/letsencrypt;
    }
}

Перезапускаем Nginx с проверкой конфигурации:

$ sudo nginx -t && sudo nginx -s reload

Далее, создадим рабочую директорию для SSL сертификатов:

$ sudo mkdir /home/myuser/letsencrypt && sudo chgrp www-data /home/myuser/letsencrypt

Создаём конфиг mydomain.com.conf для получения сертификата домена и размещаем его в директории /home/myuser/letsencrypt/configs/:

domains = mydomain.com
rsa-key-size = 2048
server = https://acme-v01.api.letsencrypt.org/directory
email = my.email@gmail.com
text = True
authenticator = webroot
webroot-path = /home/myuser/letsencrypt/

Уф. Теперь просто запускаем процесс получения SSL:

$ cd /opt/letsencrypt
$ sudo ./certbot-auto --config /home/myuser/letsencrypt/configs/mydomain.com.conf certonly

Если вы увидели надпись «Congratulations! Your certificate and chain have been saved at...», то сертификат успешно получен.

Немного отдохнём. Для лучшей безопасности, создадим специальный DHE ключ (этот процесс может занять от 5 до 20 минут, в зависимости от железа вашего VDS):

$ sudo openssl dhparam -out /etc/letsencrypt/dhparam.pem 2048

Чтобы автоматизировать обновление сертификата (раз в три месяца), будем использовать CRON. Для этого создадим bash скрипт renew.sh со следующим содержимым (в папке /home/myuser/letsencrypt/):

#!/bin/sh

cd /opt/letsencrypt/
./certbot-auto --config /home/myuser/letsencrypt/configs/mydomain.com.conf certonly

if [ $? -ne 0 ]
 then
        ERRORLOG=`tail /home/myuser/letsencrypt/error.log`
        echo -e "The Let's Encrypt cert has not been renewed! \n \n" \
                 $ERRORLOG
 else
        nginx -s reload
fi

exit 0

Очень хорошо! Теперь добавим этот файл в задания CRON:

$ sudo crontab -e

В конце открывшегося файла пишем:

0 0 1 JAN,MAR,MAY,JUL,SEP,NOV * /home/myuser/letsencrypt/renew.sh

Поздравляю! Теперь у вас есть бесплатный и самообновляемый SSL сертификат для домена.

Завершаем настройку Nginx

Не буду расписывать каждый пункт конфигурации. Это вы можете самостоятельно изучить в официальной документации. Полный листинг моего боевого файла /etc/nginx/nginx.conf:

user myuser myuser;
worker_processes auto;

pid /var/run/nginx.pid;

events {
    worker_connections 1024;
    accept_mutex off;
    multi_accept on;
    use epoll;
}

http {
    access_log off;
    server_tokens off;

    server_names_hash_bucket_size 64;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    error_log /var/log/nginx/error.log crit;

    keepalive_timeout  5;
    keepalive_requests 100;

    client_max_body_size 1m;
    client_body_timeout 10;
    reset_timedout_connection on;
    send_timeout 2;
    sendfile on;
    tcp_nodelay on;
    tcp_nopush on;

    gzip on;
    gzip_comp_level 5;
    gzip_disable "msie6";
    gzip_types "*";

    open_file_cache max=200000 inactive=20s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;

    ssl_prefer_server_ciphers on;
    ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
    ssl_session_cache shared:SSL:5m;
    ssl_session_timeout 1h;
    ssl_dhparam /etc/letsencrypt/dhparam.pem;

    add_header Strict-Transport-Security "max-age=15768000" always;

    include /etc/nginx/conf.d/*.conf;
}

Теперь изменим конфиг для нашего сайта /etc/nginx/conf.d/mydomain.com.conf. Сделаем редирект на HTTPS (без www) и включим поддержку HTTP/2 (ALPN):

server {
    listen 80;
    listen [::]:80;
    server_name mydomain.com www.mydomain.com;
    return 301 https://mydomain.com$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name www.mydomain.com;
    return 301 https://mydomain.com$request_uri;

    ssl_certificate /etc/letsencrypt/live/mydomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mydomain.com/privkey.pem;
}

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

    server_name mydomain.com;

    ssl_certificate /etc/letsencrypt/live/mydomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mydomain.com/privkey.pem;

    root /home/myuser/mydomain.com/public_html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    location /.well-known/acme-challenge {
        root /home/myuser/letsencrypt;
    }
}

Чтобы закрепить результат, перезапускаем Nginx (с проверкой конфигурации):

$ sudo nginx -t && sudo nginx -s reload

Настройка PostgreSQL

Начинаем начальную настройку:

$ sudo -u postgres psql

Откроется консоль PostgreSQL под стандартным пользователем postgres. Теперь создадим нового пользователя, базу данных и настроим права:

> CREATE DATABASE myproject;
> CREATE USER myprojectuser WITH PASSWORD 'password';
> ALTER ROLE myprojectuser SET client_encoding TO 'utf8';
> ALTER ROLE myprojectuser SET default_transaction_isolation TO 'read committed';
> ALTER ROLE myprojectuser SET timezone TO 'UTC';
> GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;
> \q

Ускорим работу системы

Для этого установим пакеты Prelink (для создания статичных адресов библиотек) и Preload (следит за файлами наиболее часто используемых приложений, и загружает их в память, когда система простаивает):

$ apt-get install prelink && apt-get install preload

Далее, редактируем файл /etc/default/prelink:

$ nano /etc/default/prelink

Находим строку PRELINKING=unknown и меняем её на PRELINKING=yes. Сохраняем и запускаем:

$ /etc/cron.daily/prelink

Финальные приготовления и запуск

Перезагружаем сервер. Заходим, проверяем, что всё работает и только после этого создаём полную резервную копию системы. Вот и всё. Мы справились, мы молодцы!

Теперь осталось только сохранить ссылку на этот мануал у себя на стене в социальной сети, чтобы рассказать друзьям! :D