Comments 43
По-умолчанию memcache прослушивает весь UDP трафик на порте 11211 в последней версии.
На даже минимально нормально настроенном сервере (с портами наружу) совершенно по барабану кто где что слушает.
Т.е. тупо не хватало чего-нибудь типа белого списка по accept на входящие, например простейшего:
# NTP
iptables -A INPUT -i $device -m state --state NEW -p udp --dport 123 -j ACCEPT
...
# memcached
iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 11211 -j ACCEPT
...
А ещё лучше чего-нибудь следующего вида (хотя бы для udp, если вообще нужен):
...
# memcached from my-subnet only:
iptables -A INPUT -i $device -m state --state NEW -s $my_sub_net -p tcp --dport 11211 -j ACCEPT
iptables -A INPUT -i $device -m state --state NEW -s $my_sub_net -p udp --dport 11211 -j ACCEPT
...
Детский сад, честное слово.
С connectionless протоколами нужно быть особенно осторожным. Ударение на всегда.
Если сегодня какой-нибудь сервис не слушает UDP, то завтра каким-нибудь коммитом оно всё может измениться.
Так то ведь все читают changelog после каждого обновления для каждой софтины. Ага.
Чего это?
То значит что совершенно новые (brand NEW) пакеты на порту (в общем случае самый первый пакет в передаче) пробросится в ACCEPT chain.
Каким местом как раз это должно "снижать производительности сетевой подсистемы"?
Это же не ESTABLISHED, RELATED и т.д. Ну и соответственно keep-alive и иже с ним вообще не затронуты.
Хотя даже на NEW оно не просаживается (зависит от количества правил на самом деле).
Чтобы не быть голословным, вот вам короткий коннект (2048 payload size), на локали для несильно быстрой машинки с 10Gbit, сильно multi-threaded:
- # 500 правил -m state --state NEW (для разных портов)
+ # нет правил -m state --state NEW (пустая iptables)
- connect 516.796 µs/# - 1935.0 #/sec
+ connect 518.054 µs/# - 1930.3 #/sec
- payload 18.3720 µs/# - 54430.7 #/sec
+ payload 18.3589 µs/# - 54469.6 #/sec
Т.е. никак не влияет (небольшое отличие — то разброс и погрешности измерения).
При этом и макс. задержка 24µs на payload пакете в обоих случаях.
Я думаю задержка на свиче там большую роль играет (ибо бюджетный).
Ну или если мне не верите см. Netfilter Performance Testing / Figure 15...
Т.е. тут вопрос-то лишь — для скольких правил (в одной "топологии")…
Ну если вы например банить так будете, то нужно ipset оборачивать и т.д.
Ну и во вторых, то был пример. Т.е. юзайте что-нить другое вместо iptables (или в связке) если нужно.
Или вы про statefull connection tracking?
Я не сисадмин и точно не скажу, но statefull инфо насколько я понимаю в любом случае используется при разборе/дефрагментации пакетов даже для stateless (т.е. как минимум чтобы раскидать по портам/соединениям на layer-4, дропнуть INVALID пакеты, закрыть соединение по inactivity-timeout и т.д.)…
При том что дважды оно конечно не должно (да и врядли будет) осуществляться, т. е. если есть какой-то dedicated firewal на передней линии, осуществляющий проверку plausibility и разбор пакетов, то собственно на сервере оно или выключаемо или уже обладает необходимой информацией, чтобы пометить тот же state пакета как NEW.
Со stateless протоколами все несколько сложнее, но не суть.
Как бы объяснить — ну вот у вас на порт 80 прилетело 250 пакетов от 5 соединений, как совсем без connection tracking можно раскидать их на 5 сокетов (соответствующих собственно каждому соединению).
Т.е. грубо говоря это то, чем acceptor от listener отличаются на «сетевом» уровне в ядре.
Соответственно в идеале мы говорим про единственный условный переход типа
`if (packet->state & NEW) goto jmp_chain`.
Хотя, возможно что когда-то это и было не очень перфомантно реализовано в каком-нибудь kernel 2.2, но уже например очень даже ничего в kernel 2.4 (я уже молчу про последние).
Детский сад, честное слово.
Да, мы поняли, что вы умный.
Видите ли, детский не детский, — это не важно.
Важно, что такое существует массово.
Видимо, подавляющее большинство админов существенно глупее вас.
Вы это хотели сказать своим постом?
Да, мы поняли, что вы умный.
Спасибо. А мы — это кто? Вас несколько? Скажите остальным, чтобы другого спикера выбрали.
Видите ли, детский не детский, — это не важно.
Вижу… это действительно не важно.
Важно, что такое существует массово.
Это не так, ну а если всё таки, то нужно с этим бороться. В том числе пропагандой "серверной" гигиены.
Видимо, подавляющее большинство админов существенно глупее вас.
Нет — подавляющее большинство админов как раз таки много умнее меня в этой области (ибо я не админ). А то меньшинство (вы не из них случаем?) — или пофигисты и/или личный VPS (что хочу то и делаю) и/или опыта мало… Ну или допускаю возможно не совсем компетентны в этом конкретном случае.
Вы это хотели сказать своим постом?
Вам лично я ничего не хотел сказать извиняйте если всё-таки макнул, я не нарочно.
Т.е. тупо не хватало чего-нибудь типа белого списка по accept на входящие, например простейшего:
# memcached
iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 11211 -j ACCEPT
А где сам белый список?
Грубо говоря, все входящие соединения с изнанки запрещены, кроме source+порт+протокол+итд (к тем службам) которые вами **явно** разрешены.
Всё остальное от лукавого.
Имхо, чем настраивать iptables, проще настроить сервисы слушать только на 127.0.0.1.
И в конце не полениться проверить выхлоп netstat -nl
чем настраивать iptables, проще настроить сервисы слушать только loopback.
Ну если так, то чем тогда вообще unix-socket не угодил (я про /var/run/memcached/memcached.sock
).
Вопрос-то не про проще, а как правильно...
Система из одной машины? Всегда ли порты службы за NAT (не торчат наружу)?
Если завтра что-то поставится/расширится на другой порт/протокол (вы видимо всегда читаете changelog-и всего чего апдейтите)?
- Вы разницу между открыть слушающее соединение и изменить правило в firewall (netfiltering), в контексте "делегирования привелегий" представляете?
Кто может создать listener? (кто угодно), а кто может открыть порт? (рут/судоер)
И т.д. и т.п… На самом деле есть куча всего, почему то, что вы написали (без firewall) — как минимум не комильфо.
Ну если так, то чем тогда вообще unix-socket не угодил
Не все слушает unix socket.
dns/ntp/почта для локальных нужд, или какой-то свой сервис, который просто не умеет в unix socket.
Если завтра что-то поставится/расширится на другой порт/протокол (вы видимо всегда читаете changelog-и всего чего апдейтите)?
Да в том-то и дело, что не очень читаю и апдейчу.
Мне проще новый сервис один раз настроить на localhost и больше не думать об этом.
Но это наверное применимо, только когда машина одна.
Если машин несколько и они ходят друг к другу на какие-то внутренние службы, проще будет уже вариант с iptables (там есть разные способы, но они уже более сложные).
Мне проще новый сервис один раз настроить на localhost и больше не думать об этом.
Ну из такой логики, проще не есть сахар и зубы вообще не чистить или не пачкаться и мыться раз в полгода… Однако, всё таки, чревато.
Просто поверьте, что торчать открытыми портами наружу — не есть гуд (в независимости от использования тех портов).
Как пример:
допустим у вас есть сервис XYZ-1.0 слушающий порт 1234. Вы его настроили в xyz.conf, например:
[XYZ]
LocalHostOnly = true
А завтра по обновлению на версию 1.1 случилось:
- опцию убрали и заменили на
ListenerAddr = 127.0.0.1, ...
(т.е. при апдейте вы проигнорили/проглядели N варнингов подряд, что LocalHostOnly is obsolete — т.е. есть устаревшая штука и закончится с версией 1.1); - оно было всегда
localhostonly
, а конфиг xyz.conf вдруг стал case-sesitive (и вашаLocalHostOnly
теперь просто переменная INI-файла без смысловой нагрузки); - что-то (апдейтом) или кто-то (у вас несколько "поваров" у одной кастрюли, а шеф-повару следить лень) затер весь этот конфиг и/или инклюд оригинальным-стоковым, где теперь
ListenerAddr = any
; - если то был инклюд, он вдруг потерялся (и после рестарта действует стоковый конфиг с listen from anywhere).
- у версии 1.1 был баг, который внезапно игнорит LocalHostOnly-опцию;
- у совтины появился плагин (и он активирован по умолчанию ибо "необходим как вода"), слушающий порт 1235 с пробросом JSON-а развернутого в формат XYZ на listener к порту 1234;
- и т.д. и т.п.
Смысл понятен, надеюсь...
Вы сейчас привели экзотические примеры, но я с вами согласен.
А потом оказалось что забыл сделать последний бекап и долго вспоминал что я там такого наваял.
Надеюсь достаточно «экзотично» (как раз про то, ИМХО)…
Многие сервисы по-дефолту биндятся еще и на ipv6, который по-дефолту опять же, часто включен. И получаем мы открытый сервис. Так что либо бинд на локалхост, либо отключение ipv6 либо (зачем?) iptables6
Хабрахабру попытались налить почти 300 Гбит/с memcached amplificated UDP.
Чисто в качестве интереса, а там видно (можно собрать статистику) сколькимя хостами оно там разлилось на 300 гиг/с?
Где-то 1300 хостов:
так как GFW намертво отсекает UDP
А можно по подробнее...
Просто как тогда объяснить что я их очень часто вижу с UDP.
Например вырезка из статистики IDS, нескольких первых попавшихся с UDP из Китая (я обрезал, их сотни если что):
{
"116.224.143.250" : {
"country" : "CN",
"bad-rank" : 4.49,
"ports" : [4970],
"protos" : ["UDP", "TCP"],
"reason" : [
"REJECT UDP IN=net0 OUT= MAC= SRC=116.224.143.250 DST=... LEN=58 TOS=0x00 PREC=0x00 TTL=45 ID=15969 PROTO=UDP SPT=1024 DPT=4970 LEN=38",
"REJECT TCP IN=net0 OUT= MAC= SRC=116.224.143.250 DST=... LEN=52 TOS=0x00 PREC=0x00 TTL=45 ID=20296 DF PROTO=TCP SPT=56047 DPT=4970 WINDOW=8192 RES=0x00 SYN URGP=0"
]
},
"124.133.103.134" : {
"country" : "CN",
"bad-rank" : 3.54,
"ports" : [4970],
"protos" : ["UDP", "TCP"],
"reason" : [
"REJECT UDP IN=net0 OUT= MAC= SRC=124.133.103.134 DST=... LEN=58 TOS=0x00 PREC=0x00 TTL=111 ID=17056 PROTO=UDP SPT=20489 DPT=4970 LEN=38",
"REJECT TCP IN=net0 OUT= MAC= SRC=124.133.103.134 DST=... LEN=52 TOS=0x00 PREC=0x00 TTL=111 ID=16732 DF PROTO=TCP SPT=57876 DPT=4970 WINDOW=8192 RES=0x00 SYN URGP=0"
]
},
"144.12.61.238" : {
"country" : "CN",
"bad-rank" : 4.36,
"ports" : [4970],
"protos" : ["UDP", "TCP"],
"reason" : [
"REJECT UDP IN=net0 OUT= MAC= SRC=144.12.61.238 DST=... LEN=58 TOS=0x00 PREC=0x00 TTL=108 ID=15447 PROTO=UDP SPT=1027 DPT=4970 LEN=38",
"REJECT TCP IN=net0 OUT= MAC= SRC=144.12.61.238 DST=... LEN=52 TOS=0x00 PREC=0x00 TTL=108 ID=9926 DF PROTO=TCP SPT=56111 DPT=4970 WINDOW=8192 RES=0x00 SYN URGP=0"
]
},
"180.154.225.61" : {
"country" : "CN",
"bad-rank" : 5.77,
"ports" : [4970],
"protos" : ["UDP", "TCP"],
"reason" : [
"REJECT UDP IN=net0 OUT= MAC= SRC=180.154.225.61 DST=... LEN=58 TOS=0x00 PREC=0x00 TTL=109 ID=29301 PROTO=UDP SPT=11590 DPT=4970 LEN=38",
"REJECT TCP IN=net0 OUT= MAC= SRC=180.154.225.61 DST=... LEN=52 TOS=0x00 PREC=0x00 TTL=109 ID=445 DF PROTO=TCP SPT=56461 DPT=4970 WINDOW=8192 RES=0x00 SYN URGP=0"
]
},
"122.143.151.48" : {
"country" : "CN",
"bad-rank" : 6.77,
"ports" : [4970],
"protos" : ["UDP", "TCP"],
"reason" : [
"REJECT UDP IN=net0 OUT= MAC= SRC=122.143.151.48 DST=... LEN=58 TOS=0x00 PREC=0x00 TTL=47 ID=24052 PROTO=UDP SPT=9691 DPT=4970 LEN=38",
"REJECT TCP IN=net0 OUT= MAC= SRC=122.143.151.48 DST=... LEN=60 TOS=0x00 PREC=0x00 TTL=47 ID=7668 DF PROTO=TCP SPT=9671 DPT=4970 WINDOW=64240 RES=0x00 SYN URGP=0"
]
},
"114.92.110.43" : {
"country" : "CN",
"bad-rank" : 4.58,
"ports" : [4970],
"protos" : ["UDP", "TCP"],
"reason" : [
"REJECT UDP IN=net0 OUT= MAC= SRC=114.92.110.43 DST=... LEN=58 TOS=0x00 PREC=0x00 TTL=110 ID=24714 PROTO=UDP SPT=12852 DPT=4970 LEN=38",
"REJECT TCP IN=net0 OUT= MAC= SRC=114.92.110.43 DST=... LEN=52 TOS=0x00 PREC=0x00 TTL=110 ID=24716 DF PROTO=TCP SPT=62254 DPT=4970 WINDOW=64240 RES=0x00 SYN URGP=0"
]
},
"183.0.88.171" : {
"country" : "CN",
"bad-rank" : 3.81,
"ports" : [4970],
"protos" : ["UDP"],
"reason" : [
"REJECT UDP IN=net0 OUT= MAC= SRC=183.0.88.171 DST=... LEN=116 TOS=0x00 PREC=0x00 TTL=109 ID=11172 PROTO=UDP SPT=7854 DPT=4970 LEN=96"
]
},
"124.78.150.69" : {
"country" : "CN",
"bad-rank" : 4.51,
"ports" : [4970],
"protos" : ["UDP"],
"reason" : [
"REJECT UDP IN=net0 OUT= MAC= SRC=124.78.150.69 DST=... LEN=58 TOS=0x00 PREC=0x00 TTL=46 ID=4839 DF PROTO=UDP SPT=60001 DPT=4970 LEN=38"
]
}
}
Избирательный какой-то GFW… или всё-таки не "намертво"?
Тогда будет известно, что где открыло и куда имеет доступ.
А еще лучший Jail by default (для Linux полагаю chroot by default). Любой процесс помещать в chroot и давать доступ только к тому, что ему нужно.
Подобная практика защитит от 99% попыток взлома.
Ну и конечно port scramble не повредит там где это возможно.
2. Ну так это скорее не уязвимость, а дебилы, не удосужившие настроить firewall.
В CentOs 7 (не знаю как в 7.4) по умолчанию файервол закрывает все.
Дебилы, налейте минусов. Посмотрим сколько вас.
прошу Вашей помощи, по-возможности.
Мой сайт пострадал и продолжает страдать от такой же DDOS атаки.
В конфиге memcached прописал следующее:
OPTIONS "-l 127.0.0.1 -U 0"
то есть для memcached отключил привязку к UDP-порту.
Стало гораздо лучше, но сайт всё равно тормозит.
Подскажите, пожалуйста, что ещё можно сделать?
Буду рад любому конструктивному совету.
Спасибо!
UPDATED: Амплифицированные memcached DDoS-атаки на 500 Гбит/с прокатились по всей Европе