एक अंधेरे डेटासेंटर में बाढ़ की तरह बहती पन्ना-हरी रोशनी की धाराएँ, जो एक चमकते अपस्ट्रीम डिफ़्लेक्टर के चारों ओर बँटती और मुड़ती हैं, जिनमें से बस कुछ साफ़ रेखाएँ ही पीछे खड़े मैट-ब्लैक सर्वर ब्लेड तक पहुँच पाती हैं
नेटवर्क गाइड

VPS को DDoS के दौरान भी ऑनलाइन रखें

तीन बिल्कुल अलग हमले एक ही नाम — DDoS — रखते हैं, और आप जो भी सलाह पढ़ते हैं उसमें से लगभग हर एक इसलिए बेकार साबित होती है क्योंकि वह तीनों को एक ही चीज़ मान लेती है: वह एक भरे हुए अपलिंक का जवाब किसी nginx डायरेक्टिव से देती है, या एक चालाक layer-7 flood का जवाब एक बड़े सर्वर से। काम का सवाल कभी यह नहीं होता कि “मैं इसे कैसे ब्लॉक करूँ”, बल्कि यह होता है कि “यह भौतिक रूप से किस लेयर पर रोका जा सकता है, और उस लेयर का मालिक कौन है”। यह पेज किराए के एक Linux बॉक्स के लिए ठीक यही जवाब देता है — नेटवर्क आपके ऊपर, आपके देखने से पहले ही, क्या सोख लेता है; मशीन के भीतर वाकई क्या काम आता है; और सबसे असरदार कदम अमूमन पते के ज़रिए ढूँढे जाने लायक ही न रहना क्यों होता है।

इस नेटवर्क पर volumetric mitigation हमेशा चालू रहती है और इसे चालू करने के लिए आपको कुछ नहीं करना — 10 Gbps से कम की floods चुपचाप सोख ली जाती हैं, 10–100 Gbps को transit provider पर scrub किया जाता है, और इससे ऊपर कुछ भी हो तो उसे एक समर्पित scrubbing path पर भेज दिया जाता है — ये सीमाएँ किसी मार्केटिंग विशेषण की तरह नहीं, बल्कि डॉक्यूमेंटेशन में लिखित रूप में दर्ज हैं। यही एक host आपको ईमानदारी से दे सकता है, और उसकी सीमा भी यही है। और जो ट्रैफ़िक असल में छोटे सर्वरों को गिराता है, उसके लिए यह समस्या का सबसे कम दिलचस्प आधा हिस्सा है।

क्योंकि जो हमले किसी $5 वाले VPS को भरोसेमंद तरीके से मार गिराते हैं, वे वे 340 Gbps वाले राक्षस नहीं होते जो ख़बरों में आते हैं। वे होते हैं 40,000 पैकेट प्रति सेकंड की छोटी SYNs, जो एक ऐसी state table को भर देती हैं जिसे आपने कभी देखा ही नहीं, या 300 रिक्वेस्ट प्रति सेकंड आपकी साइट के उस एक URL पर जो एक डेटाबेस क्वेरी चलाता है — ऐसा ट्रैफ़िक जो एक बिल्कुल स्वस्थ इंटरफ़ेस पर पहुँचता है, ऐसी पाइप से होकर जो भरने के आस-पास भी नहीं है, और जिसे कोई भी upstream scrubber आपके यूज़र्स से अलग नहीं पहचान सकता। इन्हें संभालना आपका काम है, और इन्हें लगभग बीस लाइनों के configuration से संभाला जाता है — बशर्ते आप पहले यह तय कर लें कि इन तीनों में से आप किसे देख रहे हैं।

आगे जो कुछ है वह Debian 13 या Ubuntu 24.04, nftables, और nginx मान कर चलता है, और यह भी मान कर चलता है कि आप पहले घंटे की hardening पहले ही कर चुके हैं — जो सर्वर अब भी उन ports पर जवाब दे रहा हो जिन्हें वह सर्व नहीं करता, वह बचाव के लिए तैयार नहीं है।

तीन हमले, एक नाम

“DDoS” एक इरादे का नाम है, किसी तंत्र (mechanism) का नहीं, और इन तंत्रों में आपस में लगभग कुछ भी समान नहीं है। इन्हें ठीक से अलग-अलग बाँटना कोई बाल की खाल निकालना नहीं है: यही असल काम है, क्योंकि हर एक को रोका सिर्फ़ एक ही लेयर पर जा सकता है, और बाकी सब लेयर पर वह अदृश्य रहता है।

Volumetric floods आपकी bandwidth को निशाना बनाती हैं। UDP amplification — DNS, NTP, memcached, और हाल में कोई भी ऐसी सर्विस जो एक छोटे सवाल का बड़ा जवाब देती है — एक हमलावर को अपनी 1 Gbps क्षमता को आप पर तानी गई 50 Gbps में बदलने देती है। निशाना लिंक होता है, सर्वर नहीं। इस दौरान आपका CPU बस बोर बैठा रहेगा।

Protocol और state हमले आपके kernel की एक सीमित table को निशाना बनाते हैं। एक SYN flood accept queue को ख़त्म करने की कोशिश करती है; एक सामान्य small-packet flood connection tracking को ख़त्म करने की कोशिश करती है। दोनों को पैकेट प्रति सेकंड में मापा जाता है, बिट्स प्रति सेकंड में नहीं, और दोनों किसी मशीन को ऐसे लिंक पर भी मार सकती हैं जो 97% खाली पड़ा हो। यही वह श्रेणी है जो छोटे सर्वरों को गिराती है, और यही वह श्रेणी है जिसे ज़्यादातर गाइड छोड़ देती हैं।

Application-layer floods आपके CPU या आपके डेटाबेस को निशाना बनाती हैं, ऐसी रिक्वेस्ट्स के ज़रिए जो असली रिक्वेस्ट्स से अलग नहीं पहचानी जा सकतीं क्योंकि वे हैं ही असली। एक search endpoint पर सौ रिक्वेस्ट प्रति सेकंड किसी नेटवर्क के लिए कुछ भी नहीं है, और किसी PHP एप्लिकेशन के लिए जानलेवा है। कोई upstream scrubber इसे आपके लिए फ़िल्टर नहीं कर सकता: बाहर से यह बिल्कुल सफलता जैसा दिखता है।

एक चौथी चीज़ भी होती है जो इन तीनों का भेस पहनकर आती है और असल में हमला होती ही नहीं: कहीं अच्छा प्रदर्शन कर गया कोई लिंक, आपका अपना कोई बदतमीज़ client, या बिना तमीज़ वाला कोई crawler। इसे सबसे पहले ख़ारिज कर देना मुफ़्त है, और यह शर्मिंदा करने वाली बात है कि यही जवाब कितनी बार निकलता है।

जो हिस्सा आप बॉक्स के भीतर से ठीक नहीं कर सकते

अगर 60 Gbps आपके पते पर तानी गई है और आपका port 1 Gbps का port है, तो उन पैकेट्स के बारे में फ़ैसला आपसे कई hops पहले, आपके ऊपर के किसी router में लिया जाता है — उस चीज़ से बहुत पहले जिसे आप administer करते हैं। आपका firewall उन्हें कभी देखता ही नहीं। देख ही नहीं सकता: उन्हें एक ऐसा लिंक बचाने के लिए गिरा दिया गया था जो आपका है ही नहीं। volumetric हमलों के बारे में यही सबसे अहम संरचनात्मक (structural) तथ्य है, और यही वजह है कि “DDoS के ख़िलाफ़ अपना firewall हार्डन करें” जैसी सलाह ज़्यादातर बकवास होती है।

तो असल मायने रखने वाले सवाल बस यही हैं कि आपका provider अपने-आप क्या करता है, और उसकी सीमाएँ कहाँ बैठती हैं। हमारी सीमाएँ वादे की तरह नहीं, प्रकाशित रूप में दी गई हैं: 10 Gbps से कम को बिना किसी दिखने वाले असर के सोख लिया जाता है, 10–100 Gbps को transit provider पर scrub किया जाता है और आपको हल्की सी latency बढ़ोतरी महसूस हो सकती है, और 100 Gbps से ऊपर prefix को एक समर्पित scrubbing path पर announce कर दिया जाता है — latency ज़्यादा साफ़ तौर पर बढ़ती है, पर सर्विस पहुँच में बनी रहती है। किसी पते को null-route करना सिर्फ़ उन लगातार चलने वाले हमलों के लिए विचार में आता है जो पूरे PoP को ख़तरे में डालें, और अगर ऐसा हो तो हम आपको मिनटों के भीतर बता देते हैं। न कुछ ख़रीदना है, न कुछ चालू करना है।

scrubbing के बारे में दो बातें जानने लायक हैं, जो vendors शायद ही कभी अपने-आप बताते हैं। पहली यह कि आपकी सुरक्षा सिर्फ़ आपके अपने बचाव से नहीं, बल्कि पड़ोस से भी होती है: आपके अपस्ट्रीम /20 को साझा करने वाले किसी ग्राहक पर हुआ हमला किसी के भी prefix तक पहुँचने से पहले सोख लिया जाता है — Paris में एक पड़ोसी range पर तानी गई क़रीब 340 Gbps की flood हमारे यहाँ बिना किसी नापे जाने लायक असर के गुज़र गई, और यही वह नतीजा है जिसे कोई नोटिस ही नहीं करता। दूसरी बात यह कि scrubbers heuristics होते हैं, और heuristics कभी-कभी ग़लत होते हैं: इसी नेटवर्क पर, एक बार एक scrubber ने किसी पड़ोसी पर हुए हमले को फ़िल्टर करते हुए नौ मिनट तक असली, वैध connections के ख़िलाफ़ TCP resets भेजे। दोनों घटनाएँ पब्लिक incident log में दर्ज हैं। अगर आपके sessions किसी और के हमले के दौरान timeout जैसा नहीं, बल्कि सक्रिय reset जैसा दिखने वाला व्यवहार करते हुए मरें, तो यह एक असली failure mode है और एक घंटे तक स्थानीय स्तर पर debug करने की बजाय इसकी रिपोर्ट करना बेहतर है।

बदलाव करने से पहले, नब्बे सेकंड की नापतौल

हर ग़लत DDoS प्रतिक्रिया की शुरुआत किसी ऐसे बदलाव से होती है जो यह जाने बिना किया गया कि हो क्या रहा है। पहले चार नंबर हासिल करें। ये एक ही स्क्रीन पर समा जाते हैं, और ये आपके लिए लेयर का नाम बता देते हैं।

# 1. bits vs packets  — which axis is saturated?
sar -n DEV 1 5            # or: ifstat -i eth0 1

# 2. socket states     — SYN-RECV piling up means a SYN flood
ss -s
ss -tan state syn-recv | wc -l

# 3. kernel drop counters (these are the ones that matter)
nstat -az | grep -E 'ListenDrops|ListenOverflows|SyncookiesSent|TCPReqQFullDrop'
dmesg -T | tail -20       # look for: nf_conntrack: table full, dropping packet

# 4. connection tracking headroom
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max

इन्हें साथ में पढ़ें। ज़्यादा bits प्रति सेकंड, कम packets प्रति सेकंड, कम CPU — यह एक volumetric हमला है, और आपका काम बस इसकी पुष्टि करना और टाइप करना बंद कर देना है। ज़्यादा packets प्रति सेकंड, कम bits प्रति सेकंड — यानी ढेर सारे बहुत छोटे पैकेट्स — यह एक state हमला है; फ़ौरन SyncookiesSent और conntrack count देखें। दोनों धुरियों पर मामूली ट्रैफ़िक, पर CPU पूरी तरह अटका हुआ — यह layer 7 है, और इसका जवाब आपके web server और आपके डेटाबेस में है, आपके firewall में नहीं।

एक counter-intuitive पहलू समझ लेने लायक है: एक असली volumetric flood के दौरान, आपका इंटरफ़ेस लगभग शांत दिख सकता है। आप बचे हुओं को देख रहे होते हैं — जो पहले से भरे हुए लिंक से होकर गुज़र पाया, या जो upstream scrubber ने आगे जाने दिया। कोई इंटरफ़ेस जो 1 Gbps के port पर 900 Mbps दिखा रहा हो, जबकि users पूरी तरह अनपहुँच की शिकायत कर रहे हों — यह किसी हमले के ख़िलाफ़ सबूत नहीं है। यह ख़ुद हमले की ही शक्ल है।

फिर जाँच लें कि ट्रैफ़िक कहीं वाकई असली तो नहीं। दस सेकंड के लिए अपने access log पर tail -f चलाएँ: अगर एक ही path हज़ारों अलग-अलग पतों से बिना किसी referrer के बार-बार दोहराया जा रहा हो, तो यह एक हमला है; सामान्य browsers से सामान्य paths की एक बिखरी हुई रेंज एक audience है, और उसे rate-limit करना हमलावर का काम उसके लिए ख़ुद कर देना होगा।

हमले से पहले निशाना छोटा करें, उसके दौरान नहीं

हर खुला port एक ऐसी queue है जिसे कोई हमलावर भर सकता है, और हर पैकेट जिसे आपके guest को देखना पड़ता है, उसे गिराने पर भी आपको CPU और एक conntrack entry की क़ीमत चुकानी पड़ती है। सबसे सस्ता पैकेट वह है जो आपकी मशीन तक पहुँचता ही नहीं — और यही वजह है कि guest के ऊपर filtering करना, guest के भीतर filtering करने से कहीं ज़्यादा मूल्यवान है।

यहाँ दो लेयर हैं, और वे एक चीज़ नहीं हैं। edge filter वैकल्पिक है, प्रति सर्वर कॉन्फ़िगर होता है, stateful और drop-by-default है, और यह hypervisor पर चलता है — जो ट्रैफ़िक आप वहाँ रिजेक्ट करते हैं, वह आपके virtual NIC तक पहुँचता ही नहीं, तो इसकी आपको न कोई CPU क़ीमत चुकानी पड़ती है, न मेमोरी, न कोई state-table entry। guest firewall — आपका nftables ruleset — पूरी तरह आपका है, और हम इसे कभी नहीं छूते। जो पैटर्न किसी हमले से टकराने के बाद भी टिका रहता है, वह है edge पर मोटी, स्थिर layer-4 सच्चाई बयान करना (“यह मशीन सिर्फ़ इन पतों से 80, 443 और SSH सर्व करती है, और कुछ भी मौजूद नहीं है”), और बारीक़, आपके application के साथ बदलने वाले काम के लिए guest ruleset को बचाकर रखना। दोनों का ब्यौरा firewall डॉक्यूमेंटेशन में है।

फिर जो बचा है उसे भी संकरा करें। SSH को सिर्फ़ उन्हीं पतों तक सीमित रखना जहाँ से आप वाकई administer करते हैं, यहाँ कोई hardening की मामूली सजावट नहीं है — यह आपकी मशीन से connection-exhaustion हमलों की पूरी एक श्रेणी ही हटा देता है। 127.0.0.1 या किसी WireGuard पते से bind किया गया डेटाबेस इंटरनेट से बिल्कुल भी flood नहीं किया जा सकता। और अगर आप विशुद्ध रूप से internal control plane सर्व करते हैं, तो उसे किसी पासवर्ड वाले पब्लिक port के बजाय WireGuard इंटरफ़ेस के पीछे रखें।

SYN floods और accept queue

एक SYN flood handshake का फ़ायदा उठाती है: हमलावर connection requests की एक धारा भेजता है और उन्हें कभी पूरा नहीं करता, और हर एक kernel की SYN queue में एक slot घेरे रखती है जब तक कि वह timeout न हो जाए। queue भर जाने पर वैध handshakes गिरा दी जाती हैं — आपकी सर्विस चालू है, सुन रही है, और अनपहुँच है।

Linux के पास नब्बे के दशक से ही इसका एक साफ़-सुथरा जवाब है, और यह डिफ़ॉल्ट रूप से चालू है। SYN cookies kernel को आधे-खुले connections के लिए मेमोरी allocate करना पूरी तरह बंद करने देती हैं: यह connection state को उस sequence number में एनकोड कर देता है जो वह वापस भेजता है, और अगर client handshake पूरा करता है तो उसे फिर से बना लेता है। इसे मान मत लें, इसकी पुष्टि करें, और burst झेलने लायक जगह queues को दें:

# /etc/sysctl.d/99-flood.conf
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 8192
net.core.somaxconn = 8192
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_abort_on_overflow = 0

sysctl --system

tcp_synack_retries = 2 दिखने से कहीं ज़्यादा मायने रखता है: डिफ़ॉल्ट पाँच का मतलब है कि एक जाली handshake kernel में लगभग तीन मिनट तक जगह घेरे रहता है, और दो इसे घटाकर लगभग सात सेकंड कर देता है। somaxconn बढ़ाना काम का सिर्फ़ आधा हिस्सा है — queue की गहराई उस मान और application ने जो माँगा उसमें से जो भी छोटा हो, वही होती है, इसलिए nginx को listen 443 ssl backlog=8192; और एक reload चाहिए, तभी kernel की सेटिंग का कोई मतलब बनता है। यह उस क्लासिक मामले जैसा है जहाँ कोई sysctl लागू हुआ तो दिखता है, पर करता कुछ नहीं।

अगर आप flood को application तक पहुँचने ही न देना चाहें, तो nftables kernel की तरफ़ से handshakes पूरी कर सकता है और सिर्फ़ उन्हीं connections को आगे भेज सकता है जो असली निकलें:

table inet filter {
  chain input {
    type filter hook input priority filter; policy drop;
    ct state established,related accept
    iif lo accept
    tcp dport { 80, 443 } ct state new synproxy mss 1460 wscale 7 timestamp sack-perm
    tcp dport { 80, 443 } accept
    ip protocol icmp icmp type echo-request limit rate 5/second accept
  }
}

synproxy को भरोसा करने से पहले किसी ऐसी मशीन पर टेस्ट करें जिस तक आप console से भी पहुँच सकें; ग़लत कॉन्फ़िगर होने पर, यह ख़ुद को अपने ही सर्वर से firewall कर बाहर कर देने का एक बेहतरीन तरीक़ा है। ज़्यादातर लोगों के लिए ऊपर वाला sysctl ब्लॉक ही काफ़ी है, और ईमानदार क्रम यह है: पहले syncookies, फिर backlog, और synproxy सिर्फ़ तब जब आपने नाप कर देख लिया हो कि पहले दोनों काफ़ी नहीं थे।

वह table जिसे तब तक कोई नहीं देखता, जब तक वह भर न जाए

यही वह चीज़ है जो अनुभवी लोगों को भी पकड़ लेती है। एक stateful firewall को हर वह flow याद रखना पड़ता है जो उसने देखा है, और यह याददाश्त nf_conntrack है — एक hash table, जिसका आकार boot के वक़्त तय हो जाता है। एक बार यह भर जाए, तो kernel नए connections गिराने लगता है — सारे के सारे, हमलावर हों या ग्राहक, कोई फ़र्क़ नहीं — और dmesg में एक लाइन लॉग कर देता है जिसे कोई देख ही नहीं रहा:

nf_conntrack: table full, dropping packet

यह इतनी असरदार क्यों है, इसकी वजह महज़ अंकगणित है। किसी छोटे VPS पर डिफ़ॉल्ट table में कहीं दसियों हज़ार की निचली रेंज में entries होती हैं, और किसी भी नए source पते से आया हर पैकेट एक नई entry बना देता है — UDP पैकेट्स भी, जिन पैकेट्स को आप गिराते हैं वे भी, और flood भी। जाली (spoofed) sources से आने वाले बीस हज़ार पैकेट प्रति सेकंड इसे दो सेकंड से भी कम में भर देते हैं, वह भी बस कुछ मेगाबिट ढोने वाले लिंक पर। आपका bandwidth graph कुछ भी नहीं दिखाएगा।

# /etc/sysctl.d/99-conntrack.conf
net.netfilter.nf_conntrack_max = 262144
net.netfilter.nf_conntrack_buckets = 65536
net.netfilter.nf_conntrack_tcp_timeout_established = 3600
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 20
net.netfilter.nf_conntrack_udp_timeout = 20

size से ज़्यादा फ़ायदा timeouts से होता है। किसी established TCP flow की डिफ़ॉल्ट उम्र पाँच दिन है, यानी एक हफ़्ते से चालू सर्वर उन connections के लिए भी state पकड़े बैठा है जो मंगलवार को ही ख़त्म हो चुके थे; किसी भी ऐसी चीज़ के लिए जो idle SSH session न हो, एक घंटा काफ़ी से ज़्यादा है, और net.ipv4.tcp_keepalive_time = 600 उन्हें दुर्घटनावश नहीं, बल्कि ठीक तरह से ज़िंदा रखता है। साइज़ तय करते वक़्त प्रति entry लगभग 300 बाइट kernel मेमोरी का बजट रखें: 262,144 entries का मतलब है क़रीब 80 MB, जो 4 GB वाली मशीन पर ठीक है, और ठीक नहीं है अगर आप इसे एक करोड़ पर सेट कर दें सिर्फ़ इसलिए कि किसी forum ने ऐसा कहा था।

अगर आप कुछ ऐसा चलाते हैं जो genuinely stateless और high-volume है — कोई authoritative DNS server, कोई पब्लिक game server, कोई Tor relay — तो बेहतर जवाब है उसे ट्रैक करना पूरी तरह बंद कर देना। ऐसी सर्विस के लिए connection tracking जिसके कोई मायने रखने वाले connections होते ही नहीं, सिर्फ़ शुद्ध लागत है:

table ip raw {
  chain prerouting {
    type filter hook prerouting priority raw; policy accept;
    udp dport 51820 notrack
    tcp dport 9001 notrack
  }
}

याद रखें कि ऐसा ट्रैफ़िक फिर आपके ct state established नियमों को दरकिनार कर जाता है, इसलिए इसे filter chain में स्पष्ट accept rules चाहिए होते हैं। यही समझौता है: उस port के लिए आप statefulness छोड़ते हैं, और बदले में एक ऐसी table पाते हैं जिसे भरा ही नहीं जा सकता।

Layer 7: वह flood जो बिल्कुल आपके ग्राहकों जैसी दिखती है

किसी छोटे सर्वर पर सबसे असरदार हमला कोई flood होती ही नहीं। यह होती हैं कुछ सौ सोच-समझकर चुनी गई HTTP requests प्रति सेकंड, हर एक बिल्कुल वैध, हर एक उस अकेले endpoint पर वार करती हुई जो कोई unindexed क्वेरी चलाता है या कोई uncached page रेंडर करता है। अर्थशास्त्र क्रूर है: वह request हमलावर को बस कुछ सौ bytes में पड़ती है, और आपको 200 मिलीसेकंड CPU और एक डेटाबेस connection में पड़ती है। जितनी भी मात्रा में आप सर्व कर सकें, आप वह दौड़ हर हाल में हार जाते हैं।

सहज प्रवृत्ति हमलावर को ब्लॉक करने की होती है। हज़ारों residential पतों में फैले किसी botnet के ख़िलाफ़, जिसमें हर एक दो requests प्रति सेकंड भेज रहा हो, IP के आधार पर ब्लॉक करना सिर्फ़ नाटक है — कोई भी per-address rate limit जो आपके असली users को गुज़रने दे, कभी ट्रिगर ही नहीं होगा, और आप पूरा outage rules जोड़ने में बिता देंगे जबकि साइट डाउन ही रहेगी। जीतने वाली चाल है गिनती नहीं, बल्कि क़ीमत बदलना।

पहले cache करें, और miss को भी cache करें। एक full-page cache किसी महँगी request को एक मेमोरी-रीड में बदल देता है, और किसी हमले में सबसे ज़्यादा मायने रखने वाला directive वही है जो एक हज़ार साथ-साथ आए misses को एक हज़ार साथ-साथ चलने वाली डेटाबेस क्वेरीज़ बनने से रोकता है:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app:64m max_size=2g inactive=60m;

server {
  location / {
    proxy_cache app;
    proxy_cache_valid 200 301 302 10m;
    proxy_cache_lock on;              # one origin request per key, not a thousand
    proxy_cache_lock_timeout 5s;
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
    proxy_cache_background_update on; # serve stale, refresh behind it
    add_header X-Cache $upstream_cache_status always;
    proxy_pass http://127.0.0.1:8080;
  }
}

proxy_cache_lock और use_stale — यही दो लाइनें तय करती हैं कि कोई ट्रैफ़िक spike झेला जा सकता है या नहीं। इनके बिना, जिस पल कोई hot key expire होती है, हर उड़ान में मौजूद (in-flight) request एक origin request बन जाती है — वही भगदड़ (stampede) जो एक संभाले जा सकने वाले हमले को एक outage में बदल देती है। इनके साथ, आपका backend हर key पर हर interval में सिर्फ़ एक request सर्व करता है, और बाकी सबको थोड़ा-सा पुराना page मिल जाता है, जो हर उस स्थिति में सही समझौता है जहाँ विकल्प यह हो कि कोई page मिले ही नहीं।

फिर ख़ास तौर पर महँगे paths को rate-limit करें, उदारता से, और ऐसे तरीके से जो साफ़ नज़र आए:

limit_req_zone $binary_remote_addr zone=general:16m rate=20r/s;
limit_req_zone $binary_remote_addr zone=costly:16m  rate=2r/s;
limit_conn_zone $binary_remote_addr zone=conn:16m;
limit_req_status 429;
limit_conn_status 429;

server {
  limit_req  zone=general burst=40 nodelay;
  limit_conn conn 24;

  location /search { limit_req zone=costly burst=5 nodelay; proxy_pass http://127.0.0.1:8080; }
  location /login  { limit_req zone=costly burst=3 nodelay; proxy_pass http://127.0.0.1:8080; }
}

nodelay के बिना burst फ़ालतू requests को रिजेक्ट करने की बजाय queue कर देता है, जिसका मतलब हमले के दौरान यह होता है कि nginx आपकी तरफ़ से हज़ारों connections बड़े शालीन तरीक़े से खोले रखता है — आपने एक request flood को एक connection flood में बदल दिया है। nodelay इस्तेमाल करें, फ़ौरन 429 लौटाएँ, और उसे client पर संभालने के लिए छोड़ दें। और किसी भी proxy के पीछे, यह याद रखें कि जब तक आपने set_real_ip_from और real_ip_header सही तरह से कॉन्फ़िगर नहीं किए, तब तक $binary_remote_addr ख़ुद proxy ही होता है; आपके अपने front-end पते पर keyed कोई rate limit जिस पल पहली बार चलेगा, हर user को एक साथ बाहर कर देगा।

Slowloris-जैसे हमले — बहुत सारे connections, हर एक request को एक-एक byte करके टपकाता हुआ — nginx के event model को मुश्किल से ही परेशान करते हैं, पर फिर भी timeouts कसना समझदारी है: client_header_timeout 8s; client_body_timeout 8s; send_timeout 10s; reset_timedout_connection on;limit_conn के साथ मिलकर, यही पूरा बचाव है।

fail2ban DDoS टूल क्यों नहीं है

fail2ban सच में उपयोगी है, बस यह एक अलग समस्या के लिए बना है। यह log फ़ाइलें पढ़ता है, M मिनटों में N नाकामियों के बाद फ़ैसला लेता है, और एक firewall rule डाल देता है। इसका हर हिस्सा किसी flood के लिए ग़लत आकार का है।

यह बहुत धीमा है: चालीस सेकंड चलने वाला कोई burst ban window बंद होने से पहले ही ख़त्म हो चुका होता है। यह logs पढ़ता है, तो हमला हर request की पूरी क़ीमत पहले ही वसूल चुका होता है — parsing नुक़सान होने के बाद होती है। यह अलग-अलग पतों को ban करता है, तो दस हज़ार sources के ख़िलाफ़ यह या तो कुछ नहीं करता, या दस हज़ार linear rules डाल देता है, और उस मोड़ पर firewall ख़ुद ही bottleneck बन जाता है — और आप अपने हमलावर की तरफ़ से हमला ख़ुद पूरा कर देते हैं। और यह आपके logs से चलता है, तो कोई ऐसी flood जो log लाइनों से किसी disk को भर देने लायक बड़ी हो, सर्वर को किसी ऐसे रास्ते से गिरा सकती है जिसके बारे में आपने कभी सोचा ही नहीं।

अगर आपको dynamic blocking चाहिए, तो इसे data plane में करें, जहाँ lookup एक hash हो और expiry अपने-आप हो। एक nftables dynamic set साइज़ चाहे जो हो, O(1) रहता है, और अपने-आप भूल जाता है:

table inet filter {
  set flooders {
    type ipv4_addr
    flags dynamic, timeout
    timeout 10m
    size 65535
  }

  chain input {
    type filter hook input priority filter; policy drop;
    ct state established,related accept
    iif lo accept
    ip saddr @flooders drop
    tcp dport { 80, 443 } ct state new add @flooders { ip saddr limit rate over 50/second burst 100 packets } drop
    tcp dport { 80, 443 } accept
  }
}

नियम को ध्यान से पढ़ें, क्योंकि negation लोगों को उलझा देता है: पता set में तभी जोड़ा और गिराया जाता है जब rate limit से ऊपर हो। fail2ban को उसी काम के लिए रखें जिसमें यह अच्छा है — SSH और application logins के ख़िलाफ़ धीमी credential guessing, जहाँ किसी ख़ास पते पर इंसानी टाइमस्केल वाला ban बिल्कुल सही है।

हथियार मत बनिए: वह amplification जिसे आप होस्ट कर रहे हैं

हर बड़ा volumetric हमला उन सर्वरों से ताक़त पाता है जिनके operators को पता ही नहीं था कि वे इसमें शामिल हैं। इसका तंत्र एक ऐसी UDP सर्विस है जो एक छोटी जाली request का बड़ा जवाब देती है: कोई खुला DNS resolver जो पूछा गया था उसका 50 गुना लौटाता है, ग़लत कॉन्फ़िगर किया गया NTP और memcached इससे भी बुरे हैं, और कोई भी प्रोटोकॉल जो यह जाँचे बिना जवाब दे देता है कि पूछने वाला कौन है, इसका उम्मीदवार है।

इसके नतीजे victim तक पहुँचने से पहले आप पर गिरते हैं। आपका uplink आपके ही outbound जवाबों से भर जाता है, आपका provider आपके port से निकलने वाला लगातार दुरुपयोगी ट्रैफ़िक देखता है, और जो transit suppliers इसे पाते हैं वे उन ranges को null-route करना शुरू कर देते हैं जहाँ से यह आ रहा है — यही वजह है कि acceptable-use policy हमला शुरू करने और amplification पर सख़्त लाइन खींचती है, जबकि लगभग बाकी सब कुछ इजाज़त देती है। बिना जाने-बूझे reflector बन जाना, किसी ऐसे पते को गँवाने का सबसे तेज़ तरीक़ा है जिसका आपके इरादे से कोई लेना-देना ही नहीं था।

# bind a recursive resolver to localhost, never 0.0.0.0
# unbound: interface: 127.0.0.1  +  access-control: 0.0.0.0/0 refuse

# what is actually listening on a public address?
ss -ulpn
ss -tlpn

# from another machine, confirm you do not answer strangers
dig @your.server.ip example.com +short          # must time out
ntpq -c rv your.server.ip                        # must fail

अगर आप जान-बूझकर कोई पब्लिक UDP सर्विस चलाते हैं — कोई game server, कोई authoritative nameserver, कोई WireGuard endpoint — तो नियम बाकी हर जगह जैसा ही है: जवाब को rate-limit करें, request को कभी नहीं। bind और knot दोनों response rate limiting लागू करते हैं; इसे इस्तेमाल करें, क्योंकि यही फ़र्क़ है अपने users को सर्व करने और किसी और के हमले को आगे भेजने के बीच।

सबसे असरदार बचाव है खोजे जाने लायक ही न होना

ऊपर बताई गई हर बात उस हमले के लिए damage control है जो आपका पता पहले ही ढूँढ चुका है। यह करने लायक है, और यह दूसरे नंबर पर सबसे अच्छा तरीक़ा है। जिस origin का नाम कोई ले ही न सके, वह layer 3 या 4 पर हमला किए जाने लायक है ही नहीं, और जो पता ट्रैफ़िक सर्व करता है उसे उस पते से अलग रखना जिसे दुनिया जानती है — इस पूरे पेज की सबसे ज़्यादा फ़ायदा देने वाली चीज़ है।

Origin पते लगभग कभी किसी चतुर हमले से लीक नहीं होते। वे इतिहास और लापरवाही से लीक होते हैं, एक छोटी और अच्छी तरह जानी-पहचानी सूची में। पुराने DNS रिकॉर्ड्स आम तौर पर असली दोषी होते हैं: passive-DNS archives वह A record हमेशा के लिए याद रखते हैं जो आपके पास किसी front-end लगाने से पहले था। Certificate transparency logs पब्लिक और स्थायी होते हैं, तो किसी ऐसे hostname के लिए जारी किया गया certificate जो सीधे origin की तरफ़ इशारा करता था, इस बात का हस्ताक्षरित, timestamp लगा रिकॉर्ड बन जाता है कि आप पहले कहाँ रहते थे। बाहर जाने वाली mail हर संदेश के headers में भेजने वाले सर्वर का पता छाप देती है। और एक ख़ामोश वाली: आपका web server अपने bare IP पर जवाब देना, जो आपकी साइट के HTML के लिए address space स्कैन करने वाले किसी को भी बस कंटेंट के आधार पर एक दोपहर में आपको ढूँढ लेने देता है।

यह आख़िरी वाली एक दो-लाइन का फ़िक्स है, और लगभग कोई इसे करता ही नहीं। default server को हर उस चीज़ को अस्वीकार करने दें जो किसी ऐसे hostname के साथ नहीं आई जिसे आप सर्व करते हैं:

server {
  listen 80 default_server;
  listen 443 ssl default_server;
  ssl_reject_handshake on;      # nginx 1.19.4+: no certificate, no fingerprint
  return 444;                   # close without a response
}

फिर एक front चुनें। कोई commercial CDN सबसे जाहिर जवाब है, और इसके साथ एक असली क़ीमत आती है जो यहाँ मायने रखती है: आप एक ऐसी कंपनी जोड़ रहे हैं जो आपका TLS terminate करती है, आपका plaintext देखती है, आपका origin जानती है, और उस jurisdiction में legal process से बाध्य की जा सकती है जिसे आपने चुना ही नहीं — जो उस वजह का एक बड़ा हिस्सा बेअसर कर देती है जिसके लिए सर्वर बिना किसी नाम के crypto से ख़रीदा गया था। अगर आप फिर भी ऐसा करें, तो समझ लें कि origin कभी पब्लिक हुआ ही नहीं होना चाहिए था, और यह पूरा इंतज़ाम उसी पल फ़ेल हो जाता है जिस पल किसी को कोई पुराना रिकॉर्ड मिल जाए।

जो विकल्प आपकी ख़रीदी हुई property की ख़ासियत बचाए रखता है, वह है ख़ुद अपना front बनना। किसी दूसरे region में एक $5 वाला instance जो nginx को reverse proxy की तरह चलाए, जिसमें origin का edge filter सिर्फ़ उसी एक पते से ट्रैफ़िक स्वीकार करे और किसी और से नहीं — यह आपको एक क़ुर्बानी देने लायक पता देता है जिसे आप पाँच मिनट में renumber कर सकते हैं, और एक control plane जो सिर्फ़ आपको जवाब देता है। यह वही architecture है, बस तीसरे पक्ष के बिना। और जहाँ audience इसे इस्तेमाल कर सके, वहाँ Tor onion service IP को समीकरण से पूरी तरह हटा देती है — flood करने के लिए कोई पता ही नहीं बचता, हालाँकि onion services का अपना attack surface introduction points पर होता है, यही वजह है कि आधुनिक Tor ठीक इसी के लिए एक proof-of-work बचाव के साथ आता है।

देश, ASN और ट्रैफ़िक की शक्ल को ब्लॉक करना

देर-सवेर कोई न कोई पूरे देश को ब्लॉक करने का सुझाव देता ही है। यह भोंडा है, कभी-कभार सही भी होता है, और ज़्यादातर बस व्यस्त महसूस करने का एक तरीक़ा है।

यह तब बचाव-लायक है जब आपकी सर्विस का audience वाक़ई सीमित हो — कोई क्षेत्रीय game server, कोई internal टूल, कोई control panel — और आप false positives की ज़िम्मेदारी लेने को तैयार हों, जिनमें सफ़र पर निकले आपके अपने users और अजीब तरीक़े से route हुआ कोई भी शामिल है। यह किसी आधुनिक botnet के ख़िलाफ़ लगभग बेकार है, जो आपके अपने देश समेत हर देश में residential connections में बिखरा होता है, और किसी भी पब्लिक चीज़ के लिए यह सक्रिय रूप से नुक़सानदेह है: आप चुपचाप असली लोगों को खो देंगे और जो चले गए उन्हें कभी देख ही नहीं पाएँगे।

ASN के आधार पर ब्लॉक करना ज़्यादा पैना है। हमले का ट्रैफ़िक जो मुट्ठी भर hosting providers से निकलता है — घंटे के हिसाब से किराए पर लिए गए सस्ते VPS ranges — किसी देश-ब्लॉक से कहीं कम नुक़सान के साथ prefix के आधार पर गिराया जा सकता है, क्योंकि सामान्य users किसी datacenter से browse नहीं करते। संयोग से, scraping और credential stuffing भी ज़्यादातर datacenter ranges से ही आते हैं।

पर इस विचार का टिकाऊ रूप भौगोलिक है ही नहीं: यह ट्रैफ़िक के मूल के बजाय उसकी शक्ल पर filtering करना है। कोई हमला आम तौर पर कुछ संरचनात्मक साझा करता है — वही user agent, वही ग़ायब header, वही URL वही query parameter के साथ, वही TLS fingerprint, या कोई असली browser जो दूसरी request हमेशा भेजता है उसकी पूरी अनुपस्थिति। वह साझा गुण ढूँढ लें, और आप एक ऐसा rule लिख लेते हैं जिसकी कोई क़ीमत नहीं और जो हमलावर के पते बदलने पर भी टिका रहता है — और वह उन्हें आपके गिनने से कहीं तेज़ी से बदलेगा।

# in an attack: what do these requests have in common?
awk '{print $1}'  /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20   # addresses
awk -F'"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20   # user agents
awk '{print $7}'  /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20   # paths

अगर 90% requests किसी एक user agent की हों, तो आप एक फ़िक्स से बस नब्बे सेकंड दूर हैं। अगर टॉप बीस पते हर एक ट्रैफ़िक का सिर्फ़ 2% हों, तो पतों में ढूँढना बंद कर दें — यह एक distributed हमला है, और इसका जवाब है caching और rate limits।

जब पता जल चुका हो

कभी-कभी हमला किसी बेतरतीब पते पर नहीं, बल्कि व्यक्तिगत रूप से आप पर तय किया हुआ और लगातार चलने वाला होता है, और सही जवाब है उस पते का बचाव करना बंद कर देना और उसे छोड़ देना। यह हार नहीं है; किसी छोटी सर्विस के लिए यह अक्सर सबसे सस्ता संभव नतीजा होता है, और यह तभी तकलीफ़देह है जब आपने इसकी रिहर्सल न की हो।

इसे तेज़ बनाने वाली बात पहले से ही तय हो जाती है। DNS TTLs को हमेशा 300 सेकंड पर रखें — इसकी क़ीमत नगण्य है, और फ़ायदा यह है कि आप एक दिन की बजाय पाँच मिनट में हिल सकते हैं। अपने deployment को reproducible रखें, क्योंकि जिस सर्वर को आप फिर से बना सकते हैं, उसे आप हिला भी सकते हैं; अगर फिर से बनाने का मतलब यह याद करना हो कि मार्च में आपने क्या किया था, तो आपके पास कोई योजना नहीं, एक बंधक है। किसी दूसरे region में restore किए जाने लायक backup रखें, और किसी असली drill से जान लें कि restore में कितना वक़्त लगता है।

फिर असली क़दम छोटा होता है: किसी दूसरे region में deploy करें, restore करें, DNS छूने से पहले hostname override से नए पते पर सत्यापित करें, record पलटें, और पुराने सर्वर को इतनी देर तक चलने दें कि आख़िरी resolvers भी पकड़ लें। migration गाइड इस क्रम को ठीक से कवर करती है, और चाहे आप performance के लिए हिल रहे हों या इसलिए कि कोई नाराज़ है, क्रम वही रहता है।

billing मॉडल इस रिहर्सल को लगभग मुफ़्त बना देता है: शुल्क आपके balance के मुक़ाबले रोज़ाना prorated होते हैं, तो एक दूसरी मशीन जिसे आप एक दोपहर में deploy, test और destroy कर दें, बस कुछ सेंट में पड़ती है, और न कोई contract है, न कोई card, न रद्द करने के लिए कोई renewal। आपकी पहली renumbering किसी हमले के दौरान होने की कोई वजह नहीं बनती।

यह आपको क्या नहीं दिलाता

दो ईमानदार आख़िरी बातें, क्योंकि इस पेज का बाकी हिस्सा अपनी बनावट से ही आशावादी है।

पहली यह कि कोई भी host सब कुछ नहीं सोख सकता, और जो भी provider इसके उलट दावा करे, वह बेच रहा है। क्षमता सीमित है, scrubbing क्षमता और भी ज़्यादा सीमित है, और काफ़ी बड़े स्तर पर, हज़ारों ग्राहकों की रक्षा करने वाले किसी नेटवर्क के लिए आर्थिक रूप से तर्कसंगत क़दम यही होता है कि किसी एक पते को announce करना बंद कर दिया जाए। हम इसे उन्हीं लगातार चलने वाले हमलों के ख़िलाफ़ आख़िरी उपाय मानते हैं जो पूरे PoP को ख़तरे में डालें, और हम आपको बताते हैं, बजाय इसके कि आपको ख़ुद debug करने के लिए छोड़ दें — पर यह वादा कि ऐसा कभी नहीं होगा, झूठ होगा, और जो भी host ऐसा वादा करे, उस पर भरोसा कम कर देना चाहिए।

दूसरी यह कि जवाबी कार्रवाई मेन्यू में नहीं है। Booter और stresser services, टेस्टिंग टूल्स का भेस पहने हमले-किराए-पर-लेने की सेवाएँ हैं, ये ज़्यादातर मायने रखने वाले jurisdictions में ग़ैरक़ानूनी हैं, इन पर बारीक़ी से नज़र रखी जाती है, और ये यहाँ आपके सर्वर को उस policy के तहत terminate करवा देंगी जो बाकी लगभग सब कुछ इजाज़त देती है। जो asymmetry DDoS को हमलावर के लिए आकर्षक बनाती है, वही इसे बचाव करने वाले के लिए बेकार बना देती है: जिसके पास खोने के लिए कुछ भी न हो, उससे आप flood की होड़ में नहीं जीत सकते। सोखें, cache करें, हिलें, और इसे उबाऊ हो जाने दें — जो, हर उस व्यक्ति के लिए जो अपनी भेजी हुई ट्रैफ़िक की क़ीमत चुका रहा है, आख़िरकार हो ही जाता है।

  1. नब्बे सेकंड में लेयर का नाम तय करें

    किसी भी configuration बदलाव से पहले, दो axes और दो counters हासिल करें। Bits ज़्यादा और packets कम — यह volumetric है; packets ज़्यादा और bits कम — यह state हमला है; दोनों मामूली, पर CPU अटका हुआ — यह layer 7 है।

    sar -n DEV 1 5                       # bits/s and packets/s per interface
    ss -s                                # socket summary; watch synrecv
    nstat -az | grep -E 'ListenDrops|ListenOverflows|SyncookiesSent'
    cat /proc/sys/net/netfilter/nf_conntrack_count
    dmesg -T | grep -i conntrack | tail

    फिर सफलता और self-harm को ख़ारिज कर दें: दस सेकंड के लिए access log पर सरसरी नज़र डालें। अगर paths किसी इस्तेमाल हो रही वेबसाइट जैसे दिखें, तो आपके पास कोई audience या bug है, कोई हमलावर नहीं।

  2. kernel के लिए floods सोखना सस्ता बनाएँ

    दो फ़ाइलें, एक reload। ये किसी भी general-purpose सर्वर पर सुरक्षित हैं, और ये किसी छोटी मशीन के गिरने के दो सबसे आम तरीक़े हटा देती हैं।

    cat > /etc/sysctl.d/99-flood.conf <<'EOF'
    net.ipv4.tcp_syncookies = 1
    net.ipv4.tcp_max_syn_backlog = 8192
    net.core.somaxconn = 8192
    net.ipv4.tcp_synack_retries = 2
    net.ipv4.tcp_keepalive_time = 600
    net.netfilter.nf_conntrack_max = 262144
    net.netfilter.nf_conntrack_buckets = 65536
    net.netfilter.nf_conntrack_tcp_timeout_established = 3600
    net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 20
    EOF
    
    sysctl --system
    sysctl net.ipv4.tcp_syncookies net.netfilter.nf_conntrack_max   # verify, do not assume

    फिर application को भी इसके मुताबिक़ बनाएँ: nginx को listen 443 ssl backlog=8192; चाहिए, वरना अभी-अभी बढ़ाई गई kernel queue उस छोटे नंबर तक सीमित रह जाती है जो process ने माँगा था।

  3. जो कुछ आप सर्व नहीं करते, उसे guest के ऊपर से बंद करें

    यह गिनें कि असल में क्या सुन रहा है, तय करें कि किसे पब्लिक पते पर होना चाहिए, और मोटे नियमों को hypervisor edge filter तक धकेल दें ताकि ट्रैफ़िक आपके virtual NIC तक पहुँचे ही नहीं।

    ss -tlpn; ss -ulpn        # every public listener, with the process that owns it

    Server → Network → Firewall में, edge filter को डिफ़ॉल्ट रूप से drop पर सेट करें और सिर्फ़ उन्हीं ports को allow करें जिन्हें आप सर्व करते हैं, SSH को सिर्फ़ उन पतों तक सीमित रखते हुए जहाँ से आप administer करते हैं। guest nftables ruleset को अपनी दूसरी लेयर के तौर पर बनाए रखें — यह बेल्ट-और-ब्रेसेज़ है, या-तो-या-वाला मामला नहीं — और databases और admin interfaces को किसी पब्लिक port को firewall करने की बजाय 127.0.0.1 या किसी WireGuard पते से bind करें।

  4. महँगे path के आगे एक cache लगाएँ

    pages रेंडर करने वाली किसी भी चीज़ के लिए यह सबसे ज़्यादा मूल्य वाला अकेला बदलाव है। proxy_cache_lock on और proxy_cache_use_stale के साथ proxy_cache जोड़ें, ताकि एक हज़ार साथ-साथ आए misses एक अकेली origin request बन जाएँ, और बाकी सबको थोड़ा पुराना page मिल जाए।

    nginx -t && systemctl reload nginx
    curl -sI https://example.com/ | grep -i x-cache     # MISS, then HIT

    भरोसा करने से पहले hit rate की पुष्टि करें। ऐसा cache जो हर response पर मौजूद Set-Cookie की वजह से कभी hit ही न हो, इस पूरी कवायद में सुरक्षा का सबसे आम झूठा एहसास है — curl -sI से दो बार जाँचें और X-Cache पढ़ें।

  5. उन paths को rate-limit करें जो आपकी क़ीमत पर चलते हैं, विज़िटर की नहीं

    एक उदार global limit लगाएँ, और उन endpoints पर एक सख़्त limit जो डेटाबेस को छूते हैं। हमेशा nodelay, हमेशा किसी status code के साथ, कभी कोई ख़ामोश queue नहीं।

    limit_req_zone $binary_remote_addr zone=general:16m rate=20r/s;
    limit_req_zone $binary_remote_addr zone=costly:16m  rate=2r/s;
    limit_req_status 429;

    अगर nginx किसी proxy के पीछे बैठा है, तो पहले set_real_ip_from और real_ip_header कॉन्फ़िगर करें। आपके front-end के पते पर keyed कोई limit किसी हमलावर को throttle नहीं करता — यह पहली बार चलने पर हर किसी को एक साथ throttle कर देता है।

  6. origin पते को पब्लिक नज़र से बाहर करें

    bare IP को बेकार बना दें, फिर तय करें कि इसके आगे क्या रखना है। यह इनकार बस दो लाइनों का है, और scan-the-internet-for-your-HTML वाले रास्ते को हमेशा के लिए बंद कर देता है।

    server {
      listen 80 default_server;
      listen 443 ssl default_server;
      ssl_reject_handshake on;
      return 444;
    }

    फिर लीक्स को उसी क्रम में audit करें जिसमें वे आम तौर पर होती हैं: passive-DNS archives में पुराने A records, certificate-transparency logs में वे hostnames जो कभी origin पर resolve होते थे, और बाहर जाने वाली mail के headers। अगर इनमें से कोई भी अब भी पता बताता हो, तो कोई front-end आपको नहीं बचाएगा — पहले renumber करें, फिर front लगाएँ।

  7. नेटवर्क operator को बताएँ, आँकड़ों के साथ

    Volumetric mitigation अपने-आप होती है और इसके लिए किसी ticket की ज़रूरत नहीं, पर सबूत वाली रिपोर्ट किसी इंसान को यह पुष्टि करने देती है कि automation ने क्या किया, और उन failure modes को पकड़ने देती है जिन्हें वह ख़ुद नहीं देख सकता — जैसे कोई scrubber जो वैध sessions को reset कर रहा हो।

    destination पता भेजें, UTC शुरुआती समय, protocol और destination ports, दोनों axes पर वह rate जो आपने नापा, और यह कि आपके अपने counters पैकेट्स आते हुए दिखाते हैं या upstream ग़ायब होते हुए। tcpdump -ni eth0 -c 200 की पचास लाइनें, विवरण के एक पैराग्राफ़ से ज़्यादा काम की हैं। “साइट धीमी है” पर अमल नहीं किया जा सकता; “185.x.x.x, 14:02 UTC, UDP to 443, ~1.2 Mpps, इंटरफ़ेस पर 40 Kpps आते दिख रहे हैं” पर किया जा सकता है।

  8. जब कुछ भी ग़लत न हो, renumber की रिहर्सल करें

    आज ही DNS TTLs को 300 सेकंड पर सेट करें, और उन्हें वहीं छोड़ दें। फिर एक बार, शुरू से आख़िर तक, इस drill को उस मशीन पर करें जिसे आप एक घंटे बाद destroy कर देंगे।

    # deploy a second instance in another region, restore, verify by hostname override
    curl --resolve example.com:443:<new-ip> https://example.com/ -sI

    यह लिख लें कि इसमें आपको कितने मिनट लगे। यही नंबर आपका असली DDoS प्लान है — इस गाइड के किसी भी नियम से ज़्यादा — क्योंकि यही बताता है कि कोई तयशुदा हमला आपको कितनी देर तक ऑफ़लाइन रख सकता है। शुल्क आपके balance के मुक़ाबले रोज़ाना prorated होते हैं, तो पूरी रिहर्सल की क़ीमत बस कुछ सेंट होती है।

तुलना

हर हमला असल में कहाँ रोका जा सकता है

हर लेयर कुछ न कुछ रोकती है और बाकी सब के लिए अंधी होती है। जो ग़लती लोगों का पूरा वीकेंड खा जाती है, वह है किसी ऐसी लेयर पर बचाव करना जहाँ हमला शुरू से कभी दिखा ही नहीं।
लेयरक्या रोकती हैक्या नहीं रोकतीआपकी लागतकिसका नियंत्रण
Upstream scrubbingVolumetric floods — 10 Gbps से नीचे सोख ली जाती हैं, 100 तक scrub की जाती हैं, इससे ऊपर BGP से divertedजो कुछ भी सामान्य ट्रैफ़िक वॉल्यूम के भीतर समा जाए: state हमले, layer 7कुछ भी नहीं। हमेशा चालू, कोई ticket नहीं, divert के दौरान कभी-कभार latencyहम, अपने-आप
Hypervisor edge filterकिसी बंद port पर आने वाला सब कुछ, आपके virtual NIC तक पहुँचने से पहले — न CPU, न कोई state entryउन ports पर हमले जिन्हें आपको खुला रखना ही हैआपके लिखे नियमों के अलावा कुछ नहीं; stateful, drop-by-defaultआप, प्रति सर्वर
Guest firewall (nftables)synproxy के साथ SYN floods, per-source packet rates, अनचाहे protocolsवह ट्रैफ़िक जो आपके ऊपर की पाइप पहले ही भर चुका है — वह पहुँचता ही नहींहर पैकेट के लिए CPU और एक conntrack entry, गिराए गए पैकेट्स समेतआप, पूरी तरह
Kernel tuning (sysctl)Accept-queue और conntrack का ख़त्म होना — छोटे सर्वरों का क्लासिक क़ातिलकोई भी वैध, पूरा हो चुका connectionसमझदारी भरे conntrack size पर लगभग 80 MB RAM। दो फ़ाइलेंआप, पूरी तरह
Application (cache + rate limit)Layer-7 floods, request भगदड़ें, महँगे endpointsPacket-level हमले — वे web server तक पहुँचते ही नहींथोड़े stale pages, और उन users के लिए 429s जिन्हें आपने ग़लत आँकाआप, पूरी तरह
Reverse-proxy front-endorigin पर सीधे हमले — तानने के लिए कोई पब्लिक पता ही नहीं बचताfront पर ख़ुद हमले, और आपका origin IP लीक होने के बाद कुछ भी$5/माह, और patched रखने के लिए एक और मशीनआप, अगर इसे ख़ुद चलाएँ
Commercial CDNबड़े पैमाने पर layer-3/4 और layer-7 हमले, एक support queue के साथकोई origin जो कभी पब्लिक रहा हो, या जो अपने bare IP पर जवाब देता होकिसी तीसरे पक्ष से TLS termination, जो जानता है आप कौन हैं और जिसे legal process दी जा सकती हैउनका
Tor onion serviceहर IP-आधारित हमला — protocol में कोई पता होता ही नहींIntroduction-point flooding, यही वजह है कि आधुनिक Tor एक proof-of-work बचाव के साथ आता हैLatency, और Tor इस्तेमाल करने को तैयार audienceआप और नेटवर्क
FAQ

जवाब देने योग्य प्रश्न

क्या मुझे अपने सर्वर पर DDoS प्रोटेक्शन चालू करना पड़ता है?

नहीं। Volumetric mitigation PoPs के अपस्ट्रीम बैठी होती है और हमेशा चालू रहती है — न कोई प्रोडक्ट ख़रीदना है, न कोई स्विच पलटना है। 10 Gbps से कम को बिना किसी दिखने वाले असर के सोख लिया जाता है; 10–100 Gbps को transit provider पर scrub किया जाता है, जहाँ आपको हल्की सी latency बढ़त दिख सकती है; 100 Gbps से ऊपर prefix को एक समर्पित scrubbing path पर announce कर दिया जाता है, latency ज़्यादा साफ़ तौर पर बढ़ती है और सर्विस पहुँच में बनी रहती है। ये सीमाएँ डॉक्यूमेंटेशन में प्रकाशित हैं। जो चीज़ अपने-आप नहीं होती, वह है layer 4 से ऊपर का सब कुछ: state-table का ख़त्म होना और application-layer floods अपस्ट्रीम से सामान्य ट्रैफ़िक जैसी दिखती हैं और इन्हें आपकी मशीन पर ही संभालना पड़ता है।

अगर मुझ पर हमला हो, तो क्या आप मेरा IP null-route कर देंगे?

सिर्फ़ आख़िरी उपाय के तौर पर, और सिर्फ़ उन लगातार चलने वाले हमलों के लिए जो सिर्फ़ आपके सर्वर को नहीं बल्कि पूरे PoP को ख़तरे में डालें — और अगर ऐसा हो तो हम आपको इसे ख़ुद ढूँढने के लिए छोड़ने की बजाय मिनटों के भीतर बता देते हैं। जो भी host यह वादा करे कि ऐसा कभी नहीं होगा, वह नेटवर्क नहीं, मार्केटिंग बयान दे रहा है: काफ़ी बड़े स्तर पर, हज़ारों ग्राहकों की रक्षा करने का मतलब है आख़िरकार किसी एक पते को वापस लेना। असली सुरक्षा यह है कि आप ख़ुद को एक अनाकर्षक निशाना बना लें — origin पते को अप्रकाशित रखें, एक रिहर्सल किया हुआ renumbering प्लान रखें, और अपना restore time जान लें।

मेरी साइट डाउन है, पर bandwidth graph सामान्य दिख रहा है। हो क्या रहा है?

लगभग तय है कि यह state का ख़त्म होना है — क्लासिक मामला है एक भरी हुई nf_conntrack table। जाली (spoofed) sources से आने वाले बीस हज़ार छोटे पैकेट प्रति सेकंड कुछ ही मेगाबिट इस्तेमाल करते हुए एक डिफ़ॉल्ट table को सेकंडों में भर देते हैं, तो graph कुछ नहीं दिखाता और हर नया connection गिरा दिया जाता है। “table full, dropping packet” के लिए dmesg -T | grep conntrack जाँचें, और nf_conntrack_count की तुलना nf_conntrack_max से करें। दूसरी संभावना इसका उल्टा पहलू है: आपके ऊपर की पाइप पहले ही भरी है, तो आपका इंटरफ़ेस आपको हमला नहीं, बल्कि बचे हुओं को दिखा रहा है।

क्या एक बड़ा प्लान उस हमले से बच जाएगा जिससे एक छोटा प्लान नहीं बच पाता?

कभी-कभी, और उस वजह से नहीं जिसकी लोग उम्मीद करते हैं। ज़्यादा vCPU और RAM वाकई layer-7 floods और state के ख़त्म होने के ख़िलाफ़ मदद करते हैं, क्योंकि वे हमले CPU, memory और table space ख़त्म कर देते हैं। किसी volumetric flood के ख़िलाफ़ प्लान लगभग बेमानी है — पैकेट्स आपके port के अपस्ट्रीम ही गिरा दिए जाते हैं, चाहे उसके पीछे कुछ भी हो, और 2.5 Gbps का port आपको 40 Gbps से नहीं बचाता। upgrade करने से पहले caching और conntrack sizing ठीक करें; यह सस्ता है, और अक्सर यही असली समस्या निकलती है।

क्या मैं किसी no-KYC VPS के आगे Cloudflare या कोई और CDN लगा सकता हूँ?

तकनीकी रूप से हाँ, और यह अच्छी तरह काम करता है। इसे करने से पहले समझौता समझ लें: CDN आपका TLS terminate करता है, आपका plaintext देखता है, आपका origin पता जानता है, एक ऐसा account रखता है जो आपको email से और अक्सर payment method से पहचानता है, और उस jurisdiction में legal process से बाध्य किया जा सकता है जिसे आपने चुना ही नहीं। बिना किसी नाम के ख़रीदे गए सर्वर के लिए, यह ठीक उसी पक्ष को वापस ले आता है जिससे बचने के लिए यह पूरा इंतज़ाम किया गया था। अगर आपका threat model downtime है, exposure नहीं, तो यह एक वाजिब चुनाव है — पर origin कभी पब्लिक हुआ ही नहीं होना चाहिए, वरना कोई पुराना DNS रिकॉर्ड यह सब बेकार कर देता है। अगर आपके threat model में यह शामिल है कि कौन जानता है आप कहाँ हैं, तो इसकी बजाय किसी दूसरे instance पर अपना ख़ुद का reverse proxy चलाएँ, और origin को सिर्फ़ उसी से ट्रैफ़िक स्वीकार करने दें।

क्या DDoS रोकने के लिए fail2ban काफ़ी है?

नहीं, और यह कमज़ोर टूल नहीं, बल्कि ग़लत टूल है। यह logs parse करने के बाद इंसानी टाइमस्केल पर प्रतिक्रिया देता है, तो requests आपको जो क़ीमत चुकानी थी वह पहले ही चुका चुकी होती हैं; यह एक बार में एक पता ban करता है, जो दस हज़ार sources के ख़िलाफ़ कुछ नहीं करता और अगर आप इसे कोशिश करने दें तो आपके firewall को एक linear scan में बदल देता है; और यह उन logs पर निर्भर है जिन्हें कोई हमलावर ख़ुद flood कर सकता है। इसे उसी काम के लिए इस्तेमाल करें जिसमें यह बेहतरीन है — SSH और login forms के ख़िलाफ़ धीमी credential guessing — और flood की रफ़्तार पर होने वाली किसी भी चीज़ के लिए timeout वाला एक nftables dynamic set इस्तेमाल करें।

क्या मुझे पूरे देश या ASNs ब्लॉक कर देने चाहिए?

Country blocking सिर्फ़ तभी बचाव-लायक है जब आपका audience वाकई सीमित हो, और आप सफ़र पर निकले users और अजीब तरीक़े से route हुए users को खोना स्वीकार करें। यह किसी आधुनिक botnet के ख़िलाफ़ लगभग बेकार है, जो आपके अपने देश समेत हर जगह residential connections में फैला होता है। Datacenter ASNs को ब्लॉक करना ज़्यादा पैना है — सामान्य लोग किसी hosting range से browse नहीं करते — और बोनस के तौर पर scraping और credential stuffing भी पकड़ लेता है। बेहतर सहज-बोध यह है कि ट्रैफ़िक की शक्ल पर filter करें: कोई साझा user agent, कोई ग़ायब header, एक ही दोहराया गया path। यह नियम हमलावर के पते बदलने के बाद भी काम करता रहता है, और वह ऐसा मिनटों में कर देगा।

क्या Tor onion service चलाने से मैं सुरक्षित (immune) हो जाता हूँ?

IP-आधारित हमलों से immune, हाँ — protocol में कोई पता ही नहीं होता जिस पर कोई flood तानी जा सके, और यह वाकई एक अलग सुरक्षा मुद्रा है, बस कोई मामूली सुधार नहीं। यह पूरी तरह immune नहीं है: onion services पर उनके introduction points पर हमला हो सकता है, यही वजह है कि आधुनिक Tor एक proof-of-work बचाव के साथ आता है जो client के लिए flood करना महँगा बना देता है। इसमें latency की क़ीमत भी लगती है, और यह आपको सिर्फ़ उन्हीं audience तक सीमित करता है जो Tor इस्तेमाल करने को तैयार हों। बहुत से लोग दोनों चलाते हैं — पहुँच के लिए एक clearnet front, और एक onion पता जो तब भी काम करता रहता है जब clearnet वाला हमले की चपेट में हो।

Deploy your offshore server.

एक क्षेत्र चुनें। एक प्लान चुनें। एक कुंजी पेस्ट करें। भुगतान करें। अगले 47 सेकंड हमारे ज़िम्मे हैं।