Bash Days | Linux | DevOps
23.1K subscribers
124 photos
22 videos
586 links
Авторский канал от действующего девопса

Самобытно про разработку, devops, linux, скрипты, тестирование, сисадминство, техдирство, пиэмство и за айтишную жизу.

Автор: Роман Шубин
Реклама: @maxgrue

Курс: @tormozilla_bot

РКН: https://two.su/bashdays
Download Telegram
😀😃😄😁😆

🔧 Утилиты: #utils
💳 Таблицы: #sheets
Скрипты: #bash
🎙 Мониторинг: #monitoring
🤔 Отладка: #debug
🎃 Линукс: #linux
✉️ Nginx: #nginx
📦 GIT: #git
📊 Mysql: #mysql
📱 Сервисы: #services
🔄 Девопс: #devops
🛡 Безопасность: #security
👻 Игры: #games
🌐 Сети: #networks
💬 Будни: #рабочиебудни
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11118
Будем расширять границы контента. Ща покажу магию, знать это мастхэв. Мотай на ус, если не индус знал, пригодится.

У нас в компании достаточно банальная инфраструктура, в которую входит балансировщик и несколько нод на которых крутится nodejs приложение с 3000м портом, короче говоря апиха.

bashdays-b1:443
-> bashdays-w1:3000
-> bashdays-w2:3000
-> bashdays-w3:3000
-> bashdays-w4:3000

К примеру от клиентов идут POST запросы к апихе, запросы попадают на сервер b1, затем nginx распределяет запросы по нодам w1-w4 и отдает в ответ json. Примитивная балансировка нагрузки, но достаточно стабильная.

В какой-то момент мне необходимо добавить еще одну ноду с новой версией nodejs приложения.

Назовем её bashdays-w5. Эта нода не должна обслуживать клиентов, но должна отдавать данные тестировщикам, чтобы те могли потыкать и протестировать в условиях продакшена. Ну а ты как думал? Тестируем на проде, можем себе позволить ))) 🎉

Городим в nginx на b1 такой велосипед:

map $http_x_backend $backend {
bashdays-w5 192.168.0.5:3000; # w5
default backend;
}

upstream backend {
server 192.168.0.1:3000; # w1
server 192.168.0.2:3000; # w2
server 192.168.0.3:3000; # w3
server 192.168.0.4:3000; # w4
}

location / {
add_header X-Upstream $upstream_addr;
proxy_pass http://$backend;
}

Объясняю что происходит, клиенты идут на b1 и попадают по умолчанию в upstream backend, где прописаны ноды w1-w4.

Тестировщики подставляют заголовок X-Backend: bashdays-w5 в своих запросах и попадают на w5. То есть для обычных пользователей ничего не изменилось, но QA теперь с помощью заголовка X-Backend: bashdays-w5 могут попасть на новую ноду и затыкать ее хоть до усрачки.

В location есть директива add_header X-Upstream, она выведет в ответе nginx на какую ноду ты попал, удобно для дебага.

В раздел map можно добавить еще кучу нод, которые не будут участвовать в обслуживании клиентов, но ты в любой момент сможешь на них попасть с помощью заголовка.

Если у тебя не апиха, а веб приложение, можешь воспользоваться плагином ModHeader, есть для фокса и хрома. В нём можно легко подставить все необходимые заголовки и попасть в жопу пальцем туда куда тебе нужно, или не нужно.

Конфигурация nginx это как стихи писать, есть какие-то базовые слова и правила, но в большинстве случаев 90% функционала вообще не используется. Потому что, документацию никто не читает и люди делают так, как научились или подсмотрели на stackoverflow. Вот такие пироги.

tags:
#nginx

🟢 Подпишись: @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
👍80
Сегодня мы будем делать с вами свой nginx plus (тот который платный), а именно реализуем плюшку «Active health checks».

Это когда у тебя к примеру 10 апстримов и если один из них дохнет, то какой-нибудь богатый клиент получит 502 ошибку, обозлится и ничего не купит. А должен автоматически попасть на другой, более живой апстрим и потратить денежку.

Само собой будем применять инновационные технологии и немного возьмем из фреймворка «гавно и палки». Ну а как ты хотел? Бест практик! На халяву и уксус сладкий, как говорится. Короче все элементарно. Погнали.

Ставим ванильный nginx и делаем так:

upstream backend {
server unix:/var/run/haproxy/backend.sock;
}

location / {
proxy_pass http://backend;
}

Ставим рядом haproxy, и конфигурируем блоки frontend и backend таким образом:

defaults
retries 3
timeout check 5s

frontend bashdays-backend
bind /var/run/haproxy/backend.sock user nginx mode 600
mode tcp
default_backend bashdays-backend
option tcplog

backend bashdays-backend
balance roundrobin
option tcplog
server bashdays-w1 192.168.0.1:80 check
server bashdays-w2 192.168.0.2:80 check
server bashdays-w3 192.168.0.3:80 check
server bashdays-w4 192.168.0.4:80 check

Что это за хрень, щас объясню. Смотри. Клиент идет на хуй корень / и попадает в nginx апстрим backend, а апстрим сконфигурирован на сокет backend.sock от haproxy.

Далее запрос клиента пролетает по сокету, как фанера над загнивающим парижем и попадает в блок haproxy «backend» где у меня описаны все бэкенд-сервера (w1, w2, w3, w4).

Если по какой-то причине к примеру сервер w2 отдаст концы, haproxy это задетектит (timeout check 5s) и закинет запрос клиента на рабочий инстанс, тем самым клиент получит статус 200. А 200 == заебись хорошо! 🗒

Ну а потом когда w2 очухается, то haproxy снова заберет его в свои сети и будет использовать на всю катушку, как провинившуюся падшую женщину.

Вот так это и работает. Вот так вот и живем. Балансировка нагрузки + roundrobin + active health checks

У haproxy еще есть встроенный визуальный мониторинг, но об этом я расскажу тебе в одной из следующих статей.

tags:
#nginx

🟢 Подпишись: @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
👍72
Ну вот и пятница на носу. Хотя какая разница какой день недели, если ты работаешь 24/7. Чем займёмся сегодня? Давай чём-нибудь простым и интересным.

Порой необходимо определить количество ядер на сервере. Понятно, ты можешь вызвать top/htop и визуально глянуть цифру.

Но мыж с тобой не пальцем деланные, нам надо всё по уму, как тру программисты. Чтобы можно было это в скрипт засунуть и далее плясать от полученного результата. 💃

Для подсчета количества ядер, есть несколько способов:

1. grep -wc '^processor' /proc/cpuinfo
2. getconf _NPROCESSORS_ONLN
3. nproc --all

Это не единственные способы заполучить желаемое, но самые релевантные. Кстати getconf актуален и для макаводов (osx).

Для чего нам знать количество ядер? Вопрос хороший, каждый для себя сам решает. Всё зависит от задач.

К примеру заскейлил ты тачку в Selectel, через их веб панельку, но nginx продолжает работать на старом конфиге. После этого очень желательно сразу переопределить в nginx.conf параметр worker_processes.

Если Nginx выполняет работу нагружающую процессор (например SSL или gzipping), то оптимально установить эту директиву в значение, равное количеству ядер процессора.

Также, директива worker_processes, умноженная на worker_connections из секции event, даст максимально возможное количество клиентов.

Конечно же справедливо всё это ансиблом (ansible) или террой (terraform) раскатать, но это дополнительный геморрой, который никому не нужен. По крайней мере, когда это нужно было еще вчера и нет возможности писать лапшу из плейбуков.

Проще уж из скрипта узнать количество ядер, все посчитать и тем же sed’ом зареврайтить нужный тебе конфиг. Вешаешь его на крон, а оно следит за твоими ядрами и в нужный момент делает всё автоматически, хоть там каждую минуту горизонтально-вертикальным масштабированием занимайся.

Прототип без ифов, так, для наглядности:

#!/bin/bash

CPU_CORES=$(grep -wc '^processor' /proc/cpuinfo)

sed -i "s/worker_processes.*/worker_processes $CPU_CORES;/" /etc/nginx/nginx.conf

systemctl reload nginx

Бери на вооружение, пригодится. 😮‍💨

Ну а я желаю тебе хороших предстоящих выходных и береги себя! Мож чо на выходные еще закину интересного почитать.

tags: #linux #nginx

🟢 Подпишись: @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
👍95
Привет. Как оказалось, не все знают что nginx позволяет нативно писать логи в json формате. Вообще в продакшене это бест-практика. Для этого в конфиг nginx.conf нужно добавить такой блок:

log_format nginx_json escape=json '{"time_local":"$time_local", "cloud_ip":"$http_x_real_ip", "ip":"$http_cf_connecting_ip", "connection":"$connection", "request_completion":"$request_completion", "status":"$status", "rtime":"$request_time", "rlenght":"$request_length", "content_type":"$content_type", "content_length":"$content_length", "bytes_sent":"$bytes_sent", "body_bytes_sent":"$body_bytes_sent", "raddr":"$remote_addr", "ruser":"$remote_user","upstream_addr":"$upstream_addr", "upstr_resp_time":"$upstream_response_time", "upstr_cache_status":"$upstream_cache_status", "upst_status":"$upstream_status", "gzip_ratio":"$gzip_ratio", "http_x_forwarded_for":"$http_x_forwarded_for", "proxy":"$proxy_host:$proxy_port", "proxy_int_body_length":"$proxy_internal_body_length", "server_proto":"$server_protocol", "rmethod":"$request_method", "uri":"$uri", "args":"$args", "http_referer":"$http_referer", "uag":"$http_user_agent"}';

access_log /var/log/nginx/access.log nginx_json;
error_log /var/log/nginx/error.log;


Получаем максимальную информацию о клиенте в json формате, которую пробиваем средствами nginx. И теперь все это можно легко скармливать всяким эластикам и т.п. без парсинга и гроков. Так сказать — избавляемся от костылей.

Но хранить логи в файлах, такое себе. Поэтому мастхэв запихать логи непременно в system journal.

Забиваем пару строк в nginx.conf и всё логирование будет перенаправлено в system journal.

error_log syslog:server=unix:/dev/log;
access_log syslog:server=unix:/dev/log nginx_json;


А можно комбинировать, писать одновременно логи в файл в дефолтном шаблоне nginx’а и параллельно писать в system journal в json. Я иногда такое проворачиваю для дебага, где в логах требуется вывести лишь время, айпишник и ури-ури. Типа такого:

log_format nginx_json ...
log_format nginx_default ...

access_log /var/log/nginx/access_json.log nginx_json;
access_log /var/log/nginx/access_default.log nginx_default;


Но конечно если есть Kibana, дела с отладкой в разы становятся лучше. Ну а чтобы посмотреть логи в system journal, воспользуйся такой командой: journalctl -f

Сегодня если руки дойдут, обсудим судо-мудо или другие линукс кишочки. Давай, хорошей тебе пятницы, увидимся!

tags: #nginx

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
👍154
Поднимал тут у себя огородик с wg-easy, всё ок. Но из коробки, морда для создания ключей работает по протоколу http. Вроде ничего страшного, но не серьезно. Ну и настраивать nginx + acme.sh тоже капец лень.

Пока искал готовое решение (кстати нашел нативный мануал от wg-easy), набрел на интересную штуку. Называется Nginx Proxy Manager.

Короче это морда для nginx, чтобы не руками конфиг задрачивать, а мышкой галочки натыкивать + из коробки нативная интеграция с letsencrypt.

Возможно у нас в чатике где-то это уже и пролетало, но я не видал.

Поднимается за минуту через docker-compose, без лишних велосипедов и костылей. Дружит с sqlite и mysql. Есть система распределения прав, каждый юзер может редактировать свои локейшены и хосты. Поддерживает малину и arm архитектуры. Ну и естественно есть гибкий Access List и Basic Auth.

Киллер-фича: Можно генерить wildcard ssl, выбираешь из большого выпадающего списка к примеру cloudflare и оно тебе DNS Challenge делает. Это прям порадовало.

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

В общем рекомендую как для локальной разработки, так и для каких-то своих сервисов вроде bitwarden и т.п.

Картинки у них на сайте посмотрите, не буду сюда запихивать.

🌐 https://nginxproxymanager.com/

tags: #nginx #services

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
98
Привет, тут на днях в нашем чатике пролетала софтина которая делает этакий ханипот на 22 порту. И все кто ломится по ssh просто вязнут в этом болоте.

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

Как это бывает: переезжаешь на другой сервер, перетащил проект в zip архиве, распаковал, а сам zip не удалил. И теперь любой кто узнает имя архива, сможет его скачать, со всеми паролями и явками. Ну либо как в распространенных движках, есть всем известная папка, где могут находиться эти бэкапы.

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

Как с этим бороться? Ничего не бэкапить в папки проекта, удалять промежуточные файлы если что-то переносил, настраивать политики nginx чтобы 403 отдавал и т.п.

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

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

Давай настроем такой ханипот на примере nginx

Добавляем в nginx конфиги:

location ~* "^/(new|old|bkup|tmp|temp|upload|ftp|sql|file|www|drupal|joomla|wordpress|x|user|admin|a|b|r|rezerv|arch|arx|111|archive|auth|backup|clients|com|dat|dump|engine|files|home|html|index|master|media|my|mysql|old|site|sql|website|wordpress)\.tar.gz$" {
access_log /var/log/nginx/honeypot.log;
default_type application/zip;
root /var/www/bashdayz/files/backup;
rewrite ^(.*)$ /backup break;
max_ranges 0;
limit_rate 4k;
limit_conn addr 1;
}

# а это в секцию http
limit_conn_zone $binary_remote_addr zone=addr:10m;


Указываем список на что будем триггериться. Ограничиваем скорость, ограничиваем число потоков, запрещаем докачку.

Создаем файл затычку размером 1гиг, этого вполне хватит. Но можешь конечно сделать и больше.

dd if=/dev/zero of=/var/www/bashdayz/files/backup bs=1G count=1


Вот и всё. Теперь если кто-то попытается вытянуть у тебя бэкап к примеру mysql.tar.gz, то будет страдать. Идея думаю тебе понятна, вокруг нее можно городить вообще сумасшедшие схемы. Но опять же это всего лишь концепт, из которого можно сделать что-то большое и нужное.

Я одно время применял этот способ в случае, когда коллеги просят выдать бэкап базы. Обычно я даю прямую ссылку на скачивание. Но бывают ситуации, когда разработчики ведут себя как зажравшиеся ЧСВешные мудаки. И на такой случай у меня для них была специальная ссылка для скачивания бэкапа.

Ничего особенного, но с сюрпризом, бэкап никогда не скачается. Тащить 10 гигабайт со скоростью dial-up ну такое себе. А когда те начинали орать как свинья из avp, мой ответ был простой - проблема на твоей стороне. У меня видишь все работает и показываю скриншот с нормальной скоростью.

Но таким быть фу, заподлостроением пусть школьники занимаются. Мы с вами взрослые и ответственные ребята. Правда? )

Ладно, рад всех видеть. Всем хорошей рабочей недели!

tags: #nginx #networks

💩 @bashdays
Please open Telegram to view this post
VIEW IN TELEGRAM
154
Event loop из семи залуп.

✔️ Сегодня запускаем bash скрипт из nginx по локейшену.

Идея такая, при открытии урла в браузере https://bashdays.com/bashdays.sh запускается bash скрипт, который лежит на сервере в папке /tmp/bashdays.sh.

А зачем это нужно? Элементарно — чтобы запустить Bash скрипт!

Но никогда так не делай.


Чтобы подобное реализовать, тебе понадобится fcgiwrap либо lua. Из обычного nginx, который в коробке, это сделать невозможно. Политика безопасности, вся хуйня.

Я возьму fcgiwrap, не хочу компилить модуль lua. Если у тебя есть lua, можешь через него подобное реализовать. Примеры ниже.

Ставим врапер:

apt install fcgiwrap


Закидываем скрипт в /tmp/bashdays.sh

#!/bin/bash

echo "Content-type: text/html"
echo ""
echo "<h1>Hello Bashdays</h1>"
echo "<p>This is a test bash script.</p>"

echo "hello bashdays" > file.txt


В конфиге nginx городим локейшен:

location /bashdays.sh {
fastcgi_pass unix:/var/run/fcgiwrap.socket;
fastcgi_param SCRIPT_FILENAME /tmp/bashdays.sh;
include fastcgi_params;
}


Заранее убедись что сокет fcgiwrap создался и лежит в нужном месте. На этом собственно всё. Да, надо сделать еще nginx reload.

Открываем https://bashdays.com/bashdays.sh и лицезрим «Hello Bashdays This is a test bash script.».

Но это еще не все. В папке /tmp появился файл file.txt. Как видишь все получилось. Дальше включай фантазию и твори.

Для lua это будет выглядеть как-то так:


location /bashdays.sh {
content_by_lua_block {
os.execute("/tmp/bashdays.sh")
}
}


Я этот код не проверял, он всяко не работает, на коленке накидал, но думаю главное тут суть.

Кстати через php наверное тоже можно башник дернуть, там сокет так же подкинуть и через «хуй-пизда-лопата» запустить скрипт.

Вообще промышлять таким - фу! Если ты кому-то расскажешь, то тебя выгонят ссаными тряпками. Всё чисто ради экспериментов. Изучай!

tags: #linux #nginx #bash

@ВАSНDАYS | BАSHDАYS.CОM
Please open Telegram to view this post
VIEW IN TELEGRAM
72
Здрасти мои хорошие!

Рассмотрим ситуацию — злодеи устроили нагрузочное тестирование и натравили на твой ламповый стартап какой-нибудь «яндекс-танк».

😲 Подключить DDosGuard или Кратор!

Эт я пеню, ценник на такие услуги — моё почтение, да и по функционалу там очень даже всё урезано. А cloudflare по регламенту компании запрещен. Замкнутый круг.

Ну так что делать то?

Да блядь nginx затюнить и отбить всю эту хуйню-муйню. Понятно дело оно особо при дидосах не спасет, но со сканерами и «танками» заебись справится.

В nginx есть параметр limit_req_zone, он то нам и пригодится. Этот параметр ограничивает количество одновременных запросов с одного айпишника.

Поехали настраивать.

Создаем файл /etc/nginx/conf.d/assholes.conf

geo $limited {  
default 1;
192.168.1.1 0; # Этот IP не лимитируется
10.0.0.0/24 0; # 10.0.0.0/24 тоже без ограничений
}

map $limited $limit {
1 $binary_remote_addr;
0 "";
}

limit_req_zone $limit zone=bashdays:20m rate=200r/s;


limit_req_zone - ключ для отслеживания запросов.

$binary_remote_addr - системная переменная nginx, внутри хранит ip адрес клиента.

zone=bashdays:20m - произвольное имя зоны, 20m это объём памяти в мегабайтах для хранения данных. В этой зоне хранятся данные о количестве запросов от каждого уникального клиента.

rate=200r/s - ограничение равное 200 запросов в секунды с одного ip клиента.

Для рейта есть такая табличка:

Сценарий                    rate      burst
-------------------------------------------
API с высокой нагрузкой 100r/s 200
Средний веб-сайт 50r/s 100
Анти-DDoS защита 10r/s 20
CDN или кеширующий прокси 500r/s 1000


тестируем так:

ab -n 1000 -c 50 http://bashdays.ru/
wrk -t4 -c100 -d10s http://bashdays.ru/


И смотрим нет ли ошибок 503 Слишком дохуя запросов и корректно ли отрабатывает лимит.

🅰️🅰️
Сука! А как рассчитать эту 20m памяти? Щаа…

Максимальное количество клиентов = размер зоны в байтах / размерзаписинаклиента


Пардон, хуйню сморозил, давай на котиках:

Пример расчёта для 20m (20 мегабайт = 20 × 1024 × 1024 = 20 971 520 байт):

20 971 520 / 128 = 163 840


То есть, зона 20m может хранить лимиты примерно для 163 тысяч уникальных клиентов одновременно.

А откуда взялось 128?

Как я написал выше nginx хранит каждого уникального клиента (ключ $binary_remote_addr или другое значение) в зоне, используя примерно 128 байт на запись.

Есть такая табличка:

< 20 000 = 4m
~80 000 = 10m
~160 000 = 20m
500 000+ = 64m


Да, еще можно настроить burst для резких пиков. burst позволяет временно превышать rate, прежде чем включится жёсткий лимит.

Например:

limit_req zone=bashdays burst=400 nodelay;


Клиенту разрешается до 400 мгновенных запросов, а затем он попадает в ограничение 200r/s.

nodelay означает, что первые 400 запросов проходят сразу, а потом начинается строгий лимит.

Теперь по мапингам и гео хуйне.

В первом блоке про гео:

1. Все пользователи получат $limited = 1 (по умолчанию).
2. Те, кто с 192.168.1.1 или из 10.0.0.0/24, получат $limited = 0 и не будут ограничены.

Во втором блоке про мапинг:

1. Если $limited = 1, то $limit = $binary_remote_addr (IP-адрес клиента).
2. Если $limited = 0, то $limit = "", и клиент не попадает в limit_req_zone.

Короче делаем что-то вроде белого списка и кто в него не входит — идёт нахуй! Надеюсь понятно объяснил.

Теперь чтобы вся эта поебота заработала, нужно прописать в нужный локейшен:

location / {  
limit_req zone=bashdays burst=10 nodelay;
try_files $uri $uri/ /index.php?$args;
}


Про burst и nodelay выше уже рассказывал.


Ну а чтобы отдавать нужный статус при достижении лимита, делаем:

location / {  
limit_req_status 429;
limit_req zone=bashdays burst=10 nodelay;
try_files $uri $uri/ /index.php?$args;
}


Возвращаем 429 Слишком дохуя запросов, вместо стандартного 503.

Бездумно это настраивать не советую, могут пострадать обычные пользователи, а вот если всё вдумчиво сделать — спасешь свой стартап от злых писек.

➡️ Ну и сыпь в комменты свои варианты!

tags: #nginx #devops #security

🔔 @bashdays➡️ @gitgate
Please open Telegram to view this post
VIEW IN TELEGRAM
84
Очередные грабли на которые в LF многие наступили.

Есть nginx с такой конфигурацией:

server {
listen 80;
server_name _;

location /app {
proxy_pass http://localhost:5000;
}
}


И есть приложение:

from flask import
Flask app = Flask(__name__)

@app.route('/')

def hello():
return 'Привет, Linux Factory!'

if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)


Плюс к нему Dockerfile:

FROM python:3.8-slim
WORKDIR /app COPY .
/app RUN pip install -r requirements.txt
EXPOSE 5000
CMD ["python", "app.py"]


Собираем и запускаем контейнер:

docker build -t bashdays .
docker run -d -p 5000:5000 bashdays


Идем в браузере на http://bashdays.ru/app и получаем хуй с маслом:

Not Found. The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.


Важно!
Уже по этой ошибке можно сделать какие-то выводы, эт не стандартная ошибка от nginx, а значит запрос отлично доходит до приложения. Но где-то внутри приложения теряется. Тренируй насмотренность, у nginx ошибки обычно по центру выводятся, а тут align вправо.

Еще как вариант, можешь остановить контейнер и еще раз в браузере тыкнуться, если получишь 502 Плохой шлюх, то значит что nginx нормально всё у тебя передает запросы на Flask. Но где-то внутри Flask происходит какое-то гавнище.

А что случилось? Всёж верно? Локейшен в nginx прописан, прокси-пасс тоже присутствует, где ответ от приложения?

Все дело в слэшах!

 location /app {
proxy_pass http://localhost:5000/;
}


В конце порта 5000 ставим слэш и проверяем. Хуяк и оно начинает работать!

Что вообще происходит?

Когда мы делаем локейшен без последнего слэша в прокси-пассе, nginx проксирует запросы на приложение без изменения пути.

То есть приложение app.py получает роут /app а его там естественно нет. А есть просто корень /.

А когда мы добавляем слэш, nginx добавляет путь после /app к целевому урлу.

Короче опять заумно расписал, давай снова на котиках!

➡️ Без слэша запрос /app проксируется на http://localhost:5000/app, что не совпадает с маршрутом, настроенным в Flask.

➡️ С слэшем запрос /app проксируется на http://localhost:5000, и приложение Flask обслуживает его как корневой путь /.

В общем с конечным слэшем nginx «удаляет» часть пути /app и отправляет запрос на корень приложения Flask.

Вот и вся наука. Грабли прям жизненные и порой доставляют проблемы с многочасовым дебагом.

Так что держи это в голове, если с виду вроде все правильно, но не работает, пиздани в конце слэш, типа метод тыка.

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

tags: #nginx #devops #linuxfactory

🔔 @bashdays➡️ @gitgate
Please open Telegram to view this post
VIEW IN TELEGRAM
115
Вчера многие обратили внимание на конструкцию в nginx:

server_name _;


Что это блядь за «чёрточка» вместо сервер_нейма?

Если техническим языком — это подстановочное имя хоста.

Пусть на сервере прописано так:

server {
listen 80;
server_name _;
return 444;
}

server {
listen 80;
server_name bashdays.ru;
return 200;
}

server {
listen 80;
server_name linuxfactory.ru;
return 200;
}


Если в браузере откроем bashdays.ru или linuxfactory.ru, то получим статус 200.

А как получить 444, тот самый с «чёрточкой»?

Просто! Например, зайти на сервер по его IP адресу. Nginx не сможет сопоставить доменные имена, которые прописаны в конфиге и отдаст статус 444.

То есть «чёрточка» это что-то типа заглушки. Nginx не воспринимает его как специальный символ, это обычное имя хоста, которое можно использовать для обработки запросов, не соответствующих другим явно указанным доменам.

Обычно эту «чёрточку» прописывают в файле defaults, чтобы тем, кто попал на сервер по IP адресу увидел, то что ты захочешь, а не рандомный сайт который у тебя висит на сервере.

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

А в сочетании с return 444 (No Response) используется для закрытия нежелательных запросов или для обработки некорректных запросов.

Если «чёрточка» не используется, то вместо неё можно просто прописать default_server в директиве listen, и блок будет работать аналогично.

server {
listen 80 default_server;
return 444;
}


Символ _ не имеет специального значения в самом Nginx. Это просто удобное соглашение, принятое сообществом.


Вот так вот и живём, изучай!

tags: #nginx #devops #linuxfactory

🔔 @bashdays➡️ @gitgate
Please open Telegram to view this post
VIEW IN TELEGRAM
179
Ребята с LF порой интересуются — а нахуя в nginx нужна поддержка lua? Можно какой-то пример, но только не Hello World?

Конечно можно!

Понадобилось мне как-то реализовать продвинутый basic auth, чтобы nginx сходил в mysql базу и сопоставил введенные учетные данные.

А как блядь это сделать?

Тут-то и пригодится Lua!

Для начала создаем базу (nginx_auth) и табличку users. Ну и в ней колонки username и password. В username храним имя пользователя, в password захэшированный пароль:

SHA1('$UpperPa$$word')


Эту команду выполняешь в mysql, в ответ он выплюнет тебе хэш, его и нужно будет заебенить в колонку password.

Дальше конфигуряем nginx:

    location / {
content_by_lua_file /etc/nginx/lua/auth.lua;
}

location @authenticated {
root /var/www/bashdays/htdocs/site;
index index.html;
}


В nginx само собой добавляем модуль Lua, Как его собрать можешь в гугле посмотреть, ну либо взять какойнить openresty.


Ну и сам скрипт кидаем в /etc/nginx/lua/auth.lua;

local mysql = require "resty.mysql"
local sha1 = require "resty.sha1"
local resty_string = require "resty.string"
local db, err = mysql:new()

-- Установка таймаута
if not db then
ngx.log(ngx.ERR, "failed to create mysql object: ", err)
return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end

db:set_timeout(1000)

-- Подключение к базе данных
local res, err = db:connect{
host = "db",
port = 3306,
database = "nginx_auth",
user = "nginx_user",
password = "password",
charset = "utf8",
}

if not res then
ngx.log(ngx.ERR, "failed to connect to MySQL: ", err)
return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end

-- Извлекаем данные из заголовков Authorization
local auth = ngx.var.http_authorization

if not auth or not auth:find("Basic ") then
ngx.header["WWW-Authenticate"] = 'Basic realm="Restricted Area"'
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
end

-- Парсим Basic Auth заголовок
local encoded = auth:sub(7) -- Убираем "Basic " из начала
local decoded = ngx.decode_base64(encoded)

if not decoded then
ngx.header["WWW-Authenticate"] = 'Basic realm="Restricted Area"'
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
end

-- Разделяем строку на имя пользователя и пароль
local username, password = decoded:match("([^:]+):(.+)")

if not username or not password then
ngx.header["WWW-Authenticate"] = 'Basic realm="Restricted Area"'
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
end

-- Хешируем пароль
local sha1_obj = sha1:new()
sha1_obj:update(password)
local password_hash = resty_string.to_hex(sha1_obj:final())

-- Проверяем пользователя и пароль в базе данных
local sql = string.format("SELECT * FROM users WHERE username = '%s' AND password = '%s'", username, password_hash)
local res, err = db:query(sql)

if not res or #res == 0 then
ngx.header["WWW-Authenticate"] = 'Basic realm="Restricted Area"'
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
end

-- Передаем управление Nginx для загрузки страницы
ngx.exec("@authenticated")


Всё! Релоадим nginx и получаем прокаченный Basic Auth.

Сам скрипт не претендует на совершенство и очень простой, объяснять его не вижу смысла. Пробегись глазами и всё сам поймешь.


Теперь nginx лезет в базу и при успешном исходе кидает пользователя в локейшен @authenticated.

Да, скрипт работает на mariadb. Для всяких перкон и т.п. возможно нужно будет переделать SELECT.

Вот такие пироги, вот тебе и заебачее применение Lua!

➡️ Кстати, если увидел вектор атаки, поделись в комментах.

tags: #linux #devops #lua #nginx

🔔 @bashdays➡️ @gitgate
Please open Telegram to view this post
VIEW IN TELEGRAM
45