MeteorJS, Nginx, mongodb, iptables… продакшен
Здравствуйте, меня зовут Александр Зеленин, и я веб-разработчик сисадмин.
К сожалению, вся информация о полноценной развёртке приложения на MeteorJS довольно разрозненна, и приходится вручную решать миллион задач. В этой статье я хочу разобрать самую базовую (но уже достаточную для безопасной работы в продакшене) настройку сервера и последующий процесс ручного деплоя.
Разворачивать будем на Ubuntu 16, но в целом схема на 99% совпадает и для Debian 8.
На самом деле я и близко не сисадмин, так что буду очень рад предложенным правкам, но в целом схема достаточно рабочая.
В качестве файловой системы при установке выбираем XFS — монга с ней хорошо дружит.
Если доступ у нас рутовый, то первое, что надо сделать — создать пользователя.
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), на нового, свежесозданного пользователя.
Я использую 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
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
По большей части конфиг повторяется, разница всего в паре мест.
Но оставляю для удобства
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
Разница, по сути, только в пути до БД и в зависимости от используемого порта.
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
@ в названии сервиса означает что мы можем запускать его с параметрами.
Данный скрипт устанавливает все необходимые параметры ОС для работы с монгой — удобно.
[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'
}]
});
Теперь довольно важный и сложный вопрос — реалтайм репликация для бекапа. Целиком он рассмотрен не будет, ввиду необходимости настройки ещё ряда оборудования, чего я бы хотел избежать в данной статье.
Реплицировать мы будем на внешний сервер (иначе в чём смысл бекапа? :-)).
На внешнем сервере должна быть установлена монга схожим образом.
Примерный конфиг:
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 раз).
Устанавливаем ноду, ставим модуль 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
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
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
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 Работает
sudo vi /etc/network/if-up.d/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
*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 разработчика.
Комментарии (8)
29 апреля 2017 в 22:30
0↑
↓
Полезно!29 апреля 2017 в 22:33 (комментарий был изменён)
0↑
↓
/etc/init.d/ssh restart
Всё же лучше использовать systemctl/service. Systemd же.-A INPUT -m state --state NEW -p tcp -m multiport --dport 355 -j ACCEPT
не понятно, зачем нужен multiport, если открывается только один порт.29 апреля 2017 в 22:43
0↑
↓
Дело в том, что лично у меня там было несколько портов :-)
А так хорошее замечание, да, спасибо.
29 апреля 2017 в 23:10
0↑
↓
Спасибо за интересную статью — у вас классный стиль изложения. Хотя, можно легко уменьшить в несколько раз, если использовать docker.29 апреля 2017 в 23:21 (комментарий был изменён)
+1↑
↓
Если ты хочешь решить проблему с помощью Docker, то теперь у тебя две проблемы :-)
Вообще ни одна строка тут бы не сэкономилась при использовании докера.
С этой логикой можно сказать — используй meteor up и он поставит всё за тебя. Но только разница в производительности будет катастрофической.
29 апреля 2017 в 23:36
+1↑
↓
интересные теги…30 апреля 2017 в 01:06
+2↑
↓
Я ожидал что тема не популярная. Ну т.е. там ну 10 рейтинга…, но отрицательный.
Пожалуйста, дайте обратный отклик — что не так?
Мне видится что вышла отличная статья, для тех кто разрабатывает на метеоре и переходит к этапу развёртывания.
Спасибо.30 апреля 2017 в 06:30
0↑
↓
Лично мне полезно, я в избранное добавлю. Половину и так знал, другую половину как-то делал иначе (или вообще не делал) —, но пост как шпаргалка очень нужен.