MeteorJS, Nginx, mongodb, iptables… продакшен

Здравствуйте, меня зовут Александр Зеленин, и я веб-разработчик сисадмин.

К сожалению, вся информация о полноценной развётки приложения на MeteorJS довольно разрозненна, и приходится в ручную решать миллион задач. В этой статье я хочу разобрать самую базовую (но уже достаточную для безопасной работы в продакшене) настройку сервера и последующий процесс ручного деплоя.

Разворачивать будем на Ubuntu 16, но, в целом, схема на 99% совпадает и для Debian 8.

На самом деле я близко не сисадмин, так что буду очень рад предложенным правкам, но в целом схема достаточно рабочая.

В качестве файловой системы при установке выбираем XFS — монга с ней хорошо дружит.

Готовим SSH

Если доступ у нас рутовый, то первое что надо сделать — создать пользователя.

adduser zav # Используйте свой %username% вместо zav apt-get install sudo  usermod -aG sudo zav # Добавляем нашего пользователя в группу sudo,                       # что бы иметь возможность далее всё делать из-под него vi /etc/ssh/sshd_config # Если вы совсем не можете в vi — используйте тут nano вместо vi.                          # Если он не установлен — apt-get install nano

Нам надо изменить порт на случайный, по вашему выбору — это сразу защитит от части атак на ssh сервера.

Port 355 # Обычно изначально стоит Port 22 PermitRootLogin no # Запрещаем вход под рутом

Перезагружаем SSH

/etc/init.d/ssh restart

Теперь переподключаемся по SSH на новый порт (355) на нового, свежесозданного пользователя.

Подготавливаем второй диск и tmpfs (что бы разместить монгу в памяти)

Я использую 2 раздельных физических диска что бы минимизировать шанс отказа.
Вы можете размещать всё на 1 на свой страх и риск, схема от этого не изменится.
В этом случае можете точно так же использовать папку /secondary, но не монтировать в неё диск.

sudo cfdisk /dev/sdb # имя второго диска (sdb) может отличаться.                      # Выбираем создать новый, 100% места, запись sudo mkfs.xfs /dev/sdb1 # создаем ФС на втором диске sudo mkdir /secondary sudo vi /etc/fstab # делаем авто-монтирование при запуске системы

Добавляем в конец (если второго диска нет — первую строку опускаем)
tmpfs — файловая система будет расположена в оперативной памяти. Т.е. запись в раздел /data/inmemory будет делать запись в оперативную память, а не на диск. Надо понимать что при перезагрузке оно очищается. size задает размер данной области. Ваша задача что бы её хватило на размещение монги со всеми индексами. В моём случае оперативной памяти 128Gb, соответственно под tmpfs выделено 32 гигабайта.

/dev/sdb1       /secondary      xfs     defaults        0       2 tmpfs           /data/inmemory  tmpfs   size=25%        0       0

Ставим монгу, настраиваем реплику, подготавливаем реалтайм бекап

Актуальный способ установки можно посмотреть на официальном сайте.
В данном примере идёт установка версии 3.4 на Ubuntu 16.

Устанавливаем:

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6 echo "deb [ arch=amd64,arm64 ] http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list sudo apt-get update sudo apt-get install mongodb-org

Создаем папки, конфиги и ставим права:

sudo mkdir /data sudo mkdir /data/db sudo mkdir /data/inmemory sudo mkdir /secondary/data sudo mkdir /secondary/data/db sudo vi /data/mongod-memory.conf

Подход, в котором primary в памяти может быть опасен, т.к. в случае внезапного выключения питания данные, которые не успели перейти в реплики — теряются. В моём случае потеря 1-2 секунд не страшна, т.к. всё приложение пишется с учетом возможности восстановления из любой позиции. Финансовые данные пишутся с параметром, подтверждающим что данные уже есть на реплике (т.е. на диске).
Если ваше приложение к такому не готово — вы можете отказаться от memory раздела и делать всё классически. В целом для этого достаточно будет убрать монтирование tmpfs и немного изменить конфиг, сделав его схожим с mongod-sec-d1.conf

mongod-memory.conf — primary, in memory

processManagement:   fork: true    pidFilePath: "/data/inmemory/mongod.pid"  storage:   dbPath: "/data/inmemory"   journal:     enabled: false # Отключаем журналирование                    # т.к. в памяти в случае падения оно нас всё равно не спасёт   indexBuildRetry: true   wiredTiger:     engineConfig:       cacheSizeGB: 8 # Ограничиваем размер кеша.                       # Я, честно говоря, не уверен, но его можно убрать полностью,                      # т.к. это то, что монга для оптимизации поместить                       # в оперативную память, а данный инстанс уже весь в ней  systemLog:   destination: "file"   path: "/var/log/mongodb/mongodb.log" # Для всех экземпляров указываем один инстанс.                                        # Теоретически тут можно получить просадку,                                         # если монга в памяти начнёт писать                                        # на диск, но при нормальном функционировании                                         # эти события редки (ошибки, медленный запросы)   logAppend: true   quiet: false   verbosity: 0   logRotate: "reopen"   timeStampFormat: "iso8601-local"  net:   bindIp: 127.0.0.1 # Делаем монгу доступной только для локального интерфейса                     # в случае если настраиваем реалтайм бекап добавляем внешний интерфейс   port: 27000 # указываем на каких портах будем размещать,                # они должны быть разные для разных инстансов в пределах сервера   http:     enabled: false # отключаем доступ по http и прочим интерфейсам     JSONPEnabled: false     RESTInterfaceEnabled: false   ssl: # ssl нам тоже не нужен в данном случае     mode: disabled  security: # Мы будем настраивать корректный доступ по правам,            #что бы клиенты видели только то, что им надо   authorization: "enabled"   keyFile: "/data/mongod-keyfile" # Это ключ для общения реплик между собой   javascriptEnabled: false # Запрещаем исполнение JS в БД.  replication:   oplogSizeMB: 4096 # Указываем максимальный размер oplog'а.                      # случае падения реплик их восстановление будет быстрым                     # только если они отстали не более чем на размер oplog'a.                      # Корректный размер можно определить                     # в зависимости от приложения — как часто идёт изменение БД?   replSetName: "consulwar"   enableMajorityReadConcern: false # указываем что мы НЕ дожидаемся подтверждение                                     # от реплики на запись для возврата значений поиска.  operationProfiling:   slowOpThresholdMs: 30 # указываем сколько максимум может выполнятся запрос,                          # после чего будет считаться медленным                         # и записан в лог, для последующей ручной обработки   mode: "slowOp"

sudo vi /data/mongod-sec-d1.conf

mongod-sec-d1.conf — secondary, disk 1

По большей части конфиг повторяется, разница всего в паре мест.
Но оставляю для удобства

processManagement:   fork: true   pidFilePath: "/data/db/mongod.pid"  storage:   dbPath: "/data/db"   journal:     enabled: true # Обращаем внимание что тут журнал уже включен,                    # диск у нас считается надёным источником   indexBuildRetry: true   wiredTiger:     engineConfig:       cacheSizeGB: 8 # Фактически, даже если по какой-то причине Primary упадёт,                       # secondary будет использовать память но только для чтения  systemLog:   destination: "file"   path: "/var/log/mongodb/mongodb.log"   logAppend: true   quiet: false   verbosity: 0   logRotate: "reopen"   timeStampFormat: "iso8601-local"  net:   bindIp: 127.0.0.1   port: 27001   http:     enabled: false     JSONPEnabled: false     RESTInterfaceEnabled: false   ssl:     mode: disabled  security:   authorization: "enabled"   keyFile: "/data/mongod-keyfile"   javascriptEnabled: false  replication:   oplogSizeMB: 4096   replSetName: "consulwar"   enableMajorityReadConcern: false  operationProfiling:   slowOpThresholdMs: 30   mode: "slowOp"

sudo vi /data/mongod-sec-d2.conf

mongod-sec-d2.conf — secondary, disk 2

Разница, по сути, только в пути до БД и в зависимости от используемого порта.

processManagement:   fork: true   pidFilePath: "/secondary/data/db/mongod.pid"  storage:   dbPath: "/secondary/data/db"   journal:     enabled: true   indexBuildRetry: true   wiredTiger:     engineConfig:       cacheSizeGB: 8  systemLog:   destination: "file"   path: "/var/log/mongodb/mongodb.log"   logAppend: true   quiet: false   verbosity: 0   logRotate: "reopen"   timeStampFormat: "iso8601-local"  net:   bindIp: 127.0.0.1   port: 27002   http:     enabled: false     JSONPEnabled: false     RESTInterfaceEnabled: false   ssl:     mode: disabled  security:   authorization: "enabled"   keyFile: "/data/mongod-keyfile"   javascriptEnabled: false  replication:   oplogSizeMB: 4096   replSetName: "consulwar"   enableMajorityReadConcern: false  operationProfiling:   slowOpThresholdMs: 30   mode: "slowOp"

Добавляем ключ для корректной работы реплики, устанавливаем права на папки

sudo openssl rand -base64 741 > ~/mongod-keyfile sudo mv mongod-keyfile /data/mongod-keyfile sudo chmod 600 /data/mongod-keyfile sudo chown mongodb:mongodb -R /data sudo chown mongodb:mongodb -R /secondary/data

Создаем скрипты автозапуска

sudo apt-get install numactl sudo mv /lib/systemd/system/mongod.service /lib/systemd/system/mongod@.service sudo vi /lib/systemd/system/mongod@.service

mongod@.service

@ в названии сервиса означает что мы можем запускать его с параметрами.
Данный скрипт устанавливает все необходимые параметры ОС для работы с монгой — удобно.

[Unit] Description= Mongo Database on %i After=network.target  [Service] Type=forking ExecStartPre=/bin/sh -c '/bin/echo never > /sys/kernel/mm/transparent_hugepage/enabled' ExecStartPre=/bin/sh -c '/bin/echo never > /sys/kernel/mm/transparent_hugepage/defrag' User=mongodb Group=mongodb PermissionsStartOnly=true ExecStart=/usr/bin/numactl --interleave=all /usr/bin/mongod --config /data/mongod-%i.conf LimitFSIZE=infinity LimitCPU=infinity LimitAS=infinity LimitNOFILE=64000 LimitNPROC=64000 TasksMax=infinity TasksAccounting=false  [Install] WantedBy=multi-user.target

Говорим БД запускаться при старте системы и так же запускаем экземпляры прямо сейчас.
Наш параметр устанавливается после @, например, memory укажет использовать при запуске /data/mongod-memory.conf

sudo systemctl enable mongod@memory sudo systemctl enable mongod@sec-d1 sudo systemctl enable mongod@sec-d2 sudo service start mongod@memory sudo service start mongod@sec-d1 sudo service start mongod@sec-d2

Подключаемся к монге, инициализируем реплику, устанавливаем пользователей

mongo localhost:27000/admin

Выполняем в консоли монги

rs.initiate({   _id: "consulwar", // название реплики   version: 1,   protocolVersion: 1,   writeConcernMajorityJournalDefault: false, // Говорим что бы реплика не дожидалась                                               // подтверждения о записи от других   configsvr: false, // Указываем, что это не конфигурационный сервер для кластера                      // (кластер — тема отдельной статьи)   members: [     {       _id: 0, // id начинаем от нуля и далее инкрементим       host: 'localhost:27000', // по какому адресу доступна данная монга       arbiterOnly: false, // может ли отдавать и хранить данные       buildIndexes: true, // указываем что строить индексы надо       hidden: false, // не скрытая, т.е. приложение к ней может подключиться       priority: 100, // приоритет при выборе Primary — чем больше, тем приоритетнее       slaveDelay: 0, // задержка репликации. Нам задержка не нужна.       votes: 1 // может ли голосовать при выборе Primary     },     {       _id: 1,       host: 'localhost:27001',       arbiterOnly: false,       buildIndexes: true,       hidden: false,       priority: 99, // Приоритет ниже       slaveDelay: 0,       votes: 1     },     {       _id: 2,       host: 'localhost:27002',       arbiterOnly: false,       buildIndexes: true,       hidden: false,       priority: 98,       slaveDelay: 0,       votes: 1     }   ],   settings: {     chainingAllowed : true, // Разрешаем репликам читать друг с друга, а не только с мастера     electionTimeoutMillis : 5000, // Указываем время на переголосование,                                    // в случае падения одной из БД. ~7 секунд.                                    // Если все экземпляры у нас на 1 машине                                    // можем уменьшить до 500мс, скажем     catchUpTimeoutMillis : 2000   } });  // Создаем первого пользователя, он должен иметь права root'а db.createUser({user: 'zav', pwd: '7Am9859dcb82jJh', roles: ['root']});  // Выходим - ctrl+c, ctrl+c

Подключаемся под нашим пользователем

mongo localhost:27000/admin -u zav -p '7Am9859dcb82jJh' --authenticationDatabase admin

Добавляем пользователя для работы с приложением

use consulwar // вместо consulwar название основной БД вашего приложения db.createUser({     user: 'consulwar',      pwd: '37q4Re7m432dtDq',      roles: [{ // Права на чтение и запись в нашу БД.                // Включает ещё ряд прав, типа создание индексов и т.п.         role: "readWrite", db: "consulwar"      }, { // Права на чтение oplog'а, для быстрой работы приложения метеора         role: 'read', db: 'local'     }] });

Теперь довольно важный и сложный вопрос — реалтайм репликация для бекапа. Целиком он рассмотрен не будет, ввиду необходимости настройки ещё ряда оборудования, чего я бы хотел избежать в данной статье.

Настройка реалтайм-бекапа

Реплицировать мы будем на внешний сервер (иначе в чём смысл бекпап? :-)).
На внешнем сервере должна быть установлена монга схожим образом.
Примерный конфиг:

mongod-backup.conf

processManagement:   fork: true   pidFilePath: "/data/db/mongod.pid"  storage:   dbPath: "/data/db"   journal:     enabled: true   indexBuildRetry: false # Отключаем построение индексов   wiredTiger:     engineConfig:       cacheSizeGB: 0 # Отключаем кеш  systemLog:   destination: "file"   path: "/var/log/mongodb/mongodb.log"   logAppend: true   quiet: false   verbosity: 0   logRotate: "reopen"   timeStampFormat: "iso8601-local"  net:   bindIp: 222.222.222.222 # Данный экземпляр должен быть доступен из-вне   port: 27000   http:     enabled: false # но не по http, само собой     JSONPEnabled: false     RESTInterfaceEnabled: false   ssl:     mode: disabled  security:   authorization: "enabled"   keyFile: "/data/mongod-keyfile" # mongod-keyfile берем с основного сервера   javascriptEnabled: false  replication:   oplogSizeMB: 0   replSetName: "consulwar"   enableMajorityReadConcern: false  operationProfiling:   mode: "off" # нам не нужно профилирование на бекапе

В фаерволе сервера-бекапа разрешаем коннект на 27000 порт ТОЛЬКО с IP сервера-приложения/бд.
Тоже самое, на сервере приложения/БД в bindIp указываем смотреть ещё и во внешний интерфейс (ip внешний сервера), и в iptables разрешаем доступ на 27000-27002 порты ТОЛЬКО с ip севера-бекапа.

При инициализации/реинициализации реплики добавляем

{     _id: 4,     host: '222.222.222.222:27000', // собственно интерейс, на который смотрит бекап     arbiterOnly: false,     buildIndexes: false, // не строим индексы вообще на бекапе      hidden: true, // скрытый! Используется ТОЛЬКО для хранения информации     priority: 0, // Не участвует в выборах     slaveDelay: 0, // Задержка бекапа нам не нужна,                     // но может использоваться если нужно пару бекапов в реалтайме "час назад"     votes: 0 // Не участвует в голосовании }

Готово! Теперь данные будут литься в реалтайме ещё и во внешний бекап, что очень круто.
В случае полного краха приложения мы можем инициализировать реплику точно так же, и она восстановится из бекапа.
Поверьте, это намного быстрее чем mongodump/mongorestore (по личным прикидкам в 25-100 раз).

Nodejs, npm, app user, meteor build

Устанавливаем ноду, ставим модуль n, ставим им версию ноды 4.8.1 (последняя, официально поддерживаемая метеором версия).
Устанавливаем pm2, т.к. именно им будем запускать процессы.

sudo apt-get install nodejs sudo apt-get install npm sudo npm install -g n sudo n 4.8.1 sudo npm install pm2@latest -g

Добавляем пользователя, из-под которого будет всё запускаться и который будет отвечать за деплой

sudo adduser consulwar

Заходим за данного пользователя

su consulwar mkdir app mkdir app/current

На локальной машине заходим в директорию с нашим meteor проектом.
Создаем папку для билдов, собираем проект в эту папку.

mkdir ../build meteor build ../build/ --architecture os.linux.x86_64

Полученный архив загружаем на наш сервер, например, по sftp. Заходим под нашим пользователем для приложения.
Загружаем в папку ~/app.
Заходим по ssh за нашего пользователя (consulwar у меня).

cd app mkdir 20170429 # создаем папку по сегодняшней дате tar -xvzf consulwar-master.tar.gz -C 20170429 ln -s 20170429/bundle ~/app/current # Создаем симлинк, что бы быстро переключаться (cd current/programs/server && npm install) vi pm2.config.js # создаем конфиг для нашего pm2

pm2.config.js

var settings = { ... }; // Объект заменяем объектом из settings.json вашего приложения  var instances = 10; // Сколько экземпляров запускаем? Советую не более N-1                     // Где N — количество ядер  var apps = [];  for (var i = 0; i < instances; i++) {   apps.push({     "script": "/home/consulwar/app/current/bundle/main.js", // укажите корректный путь     "exec_mode": "fork", // т.к. рулить будем через Nginx, создаем форками     "name": "consulwar", // имя приложения в списке процессов     "env": {       "ROOT_URL": "http://consulwar.ru/", // Адрес приложения       "HTTP_FORWARDED_COUNT": 1, // Указываем количество прокси перед приложением                                  // Что бы корректно разрулить IP пользователя в приложении       "PORT": 3000 + i, // Порты начинают с 3000 (3000, 3001, 3002...)       "MONGO_URL": "mongodb://consulwar:37q4Re7m432dtDq@localhost:27000,localhost:27001,localhost:27002/consulwar?replicaSet=consulwar&readPreference=primary&authSource=consulwar",       "MONGO_OPLOG_URL": "mongodb://consulwar:37q4Re7m432dtDq@localhost:27000,clocalhost:27001,localhost:27002/local?replicaSet=consulwar&authSource=consulwar",       "METEOR_SETTINGS": settings     }   }); }  module.exports = {   apps : apps }

И запускаем

pm2 startup pm2.js # Выведет команду, которую надо выполнить для автозапуска после перезагрузки ... # Выполняем команду pm2 start pm2.js pm2 status # Валидируем, что всё запустилось. В случае ошибок можем посмотреть pm2 logs

Готово, приложение развернуто и уже должно быть доступно по ip сервера/адресу с указанием порта, например http://consulwar.ru:3000

Nginx балансировка

sudo apt-get install software-properties-common sudo add-apt-repository ppa:nginx/stable sudo apt-get install nginx sudo vi /etc/nginx/nginx.conf

nginx.conf

user www-data; # из-под кого запускаем nginx worker_processes  6; # Указываем количество воркеров worker_rlimit_nofile 65535;  error_log  /var/log/nginx/error.log warn; pid        /var/run/nginx.pid;  events {     worker_connections  4000; # Кол-во подключений на воркера                               # В нашем случае мы можем обработать 6 * 4000 = 24 000 запросов                                # в момент времени }  http {     map $http_upgrade $connection_upgrade { # Для корретной установки сокет-подключения         default upgrade;         ''      close;     }      include       /etc/nginx/mime.types;     default_type  application/octet-stream;      log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '                       '$status $body_bytes_sent "$http_referer" '                       '"$http_user_agent" "$http_x_forwarded_for"';      access_log  /var/log/nginx/access.log  main;      server_tokens off;      sendfile on; # Для отправки статики     tcp_nopush on;     tcp_nodelay on;     keepalive_timeout 65;      gzip on; # Включаем gzip     gzip_comp_level 6;     gzip_vary on;     gzip_proxied any;     gzip_buffers 16 8k;     gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;     gzip_static on; # Разрешаем отдачу файлов с .gz на конце. Например, main.js.gz будет отдаваться при запросе main.js     gzip_http_version 1.1;     gzip_disable "MSIE [1-6]\.(?!.*SV1)"      proxy_connect_timeout      60;     proxy_read_timeout         620;     proxy_send_timeout         320;     proxy_set_header        X-Real-IP       $remote_addr;     proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;      upstream backends {         #ip_hash; # указываем если нам надо что бы пользователь всегда подключался к одному и тому же экземпляру приложения         least_conn; # Я выбрал что бы подключение отдавалось наименее нагруженному процессу          # Прописываем каждый из интерфейсов         # Я запускал их 10, значит тут 10 записей         server 127.0.0.1:3000 weight=5 max_fails=3 fail_timeout=60;          server 127.0.0.1:3001 weight=5 max_fails=3 fail_timeout=60;         server 127.0.0.1:3002 weight=5 max_fails=3 fail_timeout=60;         server 127.0.0.1:3003 weight=5 max_fails=3 fail_timeout=60;         server 127.0.0.1:3004 weight=5 max_fails=3 fail_timeout=60;         server 127.0.0.1:3005 weight=5 max_fails=3 fail_timeout=60;         server 127.0.0.1:3006 weight=5 max_fails=3 fail_timeout=60;         server 127.0.0.1:3007 weight=5 max_fails=3 fail_timeout=60;         server 127.0.0.1:3008 weight=5 max_fails=3 fail_timeout=60;         server 127.0.0.1:3009 weight=5 max_fails=3 fail_timeout=60;          # Интерфейс, который будет отрабатывать в случае недоступности приложения         server 127.0.0.1:3100 backup;     }      # Указываем пути до ssl сертификатов. Мы их создадим чуть позже     ssl_certificate /etc/letsencrypt/live/consulwar.ru/fullchain.pem;     ssl_certificate_key /etc/letsencrypt/live/consulwar.ru/privkey.pem;      ssl_dhparam /etc/ssl/certs/dhparam.pem;      ssl_stapling on;      ssl_protocols TLSv1 TLSv1.1 TLSv1.2;     ssl_prefer_server_ciphers on;     ssl_ciphers         'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:!RC4:!aNULL:!eNULL:!MD5:!EXPORT:!EXP:!LOW:!SEED:!CAMELLIA:!IDEA:!PSK:!SRP:!SSLv:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';      server {         server_name consulwar.ru;         # указываем слушать 80 и 443 порты         listen 80;         listen 443 ssl http2;          # любой запрос переадресовываем на одно из наших приложений          location / {             proxy_pass http://backends;              proxy_set_header Upgrade $http_upgrade;             proxy_set_header Connection $connection_upgrade;             proxy_set_header X-Forwarded-For $remote_addr;         }          # Статику раздаем с помошью nginx         location ~* \.(jpg|jpeg|gif|ico|png)$ {             root /home/consulwar/app/current/programs/web.browser/app;         }          # Основной css и js файл лежит отдельно, добавляем правило дополнительное         location ~* "^/[a-z0-9]{40}\.(css|js)$" {             root /home/consulwar/app/current/programs/web.browser;         }          location ~ "^/packages" {             root /home/consulwar/app/current/programs/web.browser;         }          # В случае если у вас никаких систем мониторинга не стоит,          # можно убрать следующую конструкцию.         location /nginx_status {             stub_status on;             access_log off;             allow 127.0.0.1;             deny all;         }          # Для получения SSL сертификата         location ~ "^/.well-known" {             root /home/consulwar/app/current/programs/web.browser/app/.well-known;         }     }      include /etc/nginx/conf.d/*.conf;     client_max_body_size 128m; }

Перезапускаем nginx

sudo service nginx restart

Получаем SSL от Let’s Enctypt.
Само собой, домен уже должен быть привязан к этому IP адресу.

sudo apt-get install letsencrypt sudo letsencrypt certonly -a webroot --webroot-path=/home/consulwar/app/current/programs/web.browser/app -d consulwar.ru sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048 # Генерируем дополнительный сертификат безопасности

Вжух! SSL Работает

iptables

sudo vi /etc/network/if-up.d/00-iptables

00-iptables

#!/bin/sh iptables-restore < /etc/firewall.conf ip6tables-restore < /etc/firewall6.conf

sudo chmod +x /etc/network/if-up.d/00-iptables apt-get install xtables-addons-dkms sudo vi /etc/firewall.conf

firewall.conf

*filter :INPUT ACCEPT [193038:64086301] :FORWARD DROP [0:0] :OUTPUT ACCEPT [194475:60580083] -A INPUT -i lo -j ACCEPT # Разрешаем локальное общение -A INPUT -m state --state RELATED,ESTABLISHED -p all -j ACCEPT # Разрешаем существующие подключения  # Разрешаем SSH -A INPUT -m state --state NEW -p tcp -m multiport --dport 355 -j ACCEPT  # Доступ к Nginx -A INPUT -m state --state NEW -p tcp -m multiport --dport 80,443 -j ACCEPT  # Ловушка для кривых подключений, будет тратить ресурсы атакующего, а не сервера -A INPUT -p tcp -m tcp -j TARPIT  # Дропаем всю фигню -A INPUT -p udp -j DROP   COMMIT

sudo vi /etc/firewall6.conf # оставляем пустым или заполняем что вам надо sudo iptables-restore < /etc/firewall.conf

itables настроены, и лишние порты закрыты.

DONE!

Если понравится подход/формат — выложу настройку почтовой системы, мониторинга инфраструктуры, CI через Bamboo и ещё ряд используемых у нас вещей.

Вполне вероятно что что-то я упустил (но в целом прямо вот так должно работать) — спрашивайте, дополню.
Надеюсь, у кого-то это теперь займет меньше времени, чем у меня 🙂

PS: У нас открыта вакансия front-end разработчика.

ссылка на оригинал статьи https://habrahabr.ru/post/327624/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *