در تاریخ 20 فوریه 2026 ساعت 17:48 UTC، Cloudflare دچار اختلال شد: بخشی از مشتریانی که از سرویس Bring Your Own IP (BYOIP) استفاده می‌کردند، دیدند که مسیرهایشان از طریق Border Gateway Protocol (BGP) از شبکه withdrawn شدند و در نتیجه سرویس‌ها و اپلیکیشن‌های آن‌ها از اینترنت غیرقابل‌دسترس شدند ⚠️.

این مشکل ناشی از حمله سایبری یا فعالیت مخرب نبود؛ علت، تغییراتی بود که در نحوه مدیریت آدرس‌های IP وارد شده از طریق BYOIP اعمال شد و باعث شد Cloudflare به‌طور غیرعمدی prefixهای مشتریان را withdraw کند.

برای برخی مشتریان BYOIP، این رفتار به معنی timeout و خطا در اتصال در سراسر deploymentهای Cloudflare بود. همچنین سایت one.one.one.one (محل سرویس recursive DNS با آدرس 1.1.1.1) خطاهای HTTP 403 و پیام “Edge IP Restricted” را نشان می‌داد؛ اما رزولوشن DNS روی 1.1.1.1، از جمله DNS over HTTPS، دچار اختلال نشد ✅.

کل مدت این حادثه 6 ساعت و 7 دقیقه بود و بخش عمده زمان صرف بازگرداندن پیکربندی prefixها به حالت قبل از تغییر شد. مهندسان Cloudflare وقتی شروع به مشاهده شکست‌ها کردند، تغییر را revert کردند؛ اما پیش از آن تقریباً 1,100 prefix از BYOIP از شبکه withdrawn شده بودند. بعضی مشتریان توانستند با استفاده از داشبورد Cloudflare آدرس‌های خود را مجدداً advertise کنند و سرویس‌شان را بازگردانند. در نهایت همه پیکربندی‌ها بازگردانده شد و حادثه خاتمه یافت 🙏.

نحوه تأثیرگذاری به صورت عددی: از حدود 6,500 prefix تبلیغ‌شده به یک BGP peer، 4,306 عدد از آن‌ها مربوط به BYOIP بودند. طی بازه‌ای از 17:56 تا 18:46 UTC حدود 1,100 prefix از مجموع 6,500 withdrawn شدند؛ یعنی حدود 25% از BYOIPها بطور غیرعمدی حذف شدند. ما تأثیر را روی one.one.one.one تشخیص دادیم و قبل از اینکه prefixهای بیشتری تحت تأثیر قرار گیرند، تغییر را معکوس کردیم 🔍.

در ساعت 19:19 UTC راهنمایی‌ای منتشر شد که مشتریان می‌توانند با مراجعه به داشبورد، prefixهای خود را خودشان دوباره advertise کنند. در حدود 20:20 UTC بسیاری از تبلیغات بازگردانده شدند و حدود 800 prefix بازیابی شد. با این حال حدود 300 prefix باقی ماند که داشبورد نتوانست آن‌ها را اصلاح کند، چون تنظیمات سرویس آن‌ها به خاطر یک باگ نرم‌افزاری از edge حذف شده بود؛ این موارد نهایتاً توسط مهندسان به‌صورت دستی در 23:03 UTC بازگردانده شدند.

این اختلال همه مشتریان BYOIP را متاثر نکرد چون تغییر پیکربندی به‌صورت تدریجی (iterative) اعمال شد و نه یک‌باره. پس وقتی مشخص شد که این تغییر عامل مشکل است، آن را قبل از این که به همه مشتریان برسد revert کردیم.

برای مشتریانی که تحت تأثیر قرار گرفتند، اولین رفتار مشاهده‌شده «BGP Path Hunting» بود: کانکشن‌های کاربر نهایی میان شبکه‌ها جست‌وجو می‌کردند تا یک مسیر به IP مقصد پیدا کنند، تا زمانی که اتصال timeout شود. تا زمانی که prefix مجدداً advertise نشود، این حالت ادامه دارد و هر محصولی که BYOIP برای اعلان به اینترنت استفاده می‌کند، می‌تواند این loop-until-failure را تجربه کند.

چند نکته عملی درباره تاثیر سرویس‌ها:

Core CDN و Security Services: ترافیک به سمت Cloudflare جذب نمی‌شد و کاربران هنگام اتصال به سایت‌های میزبانی‌شده در آن رنج‌ها با خطا مواجه می‌شدند.

Spectrum: اپلیکیشن‌های Spectrum که روی BYOIP بودند، به‌خاطر جذب نشدن ترافیک به Cloudflare، نتوانستند ترافیک را proxy کنند.

Dedicated Egress: مشتریانی که از Gateway Dedicated Egress یا CDN Egress با BYOIP استفاده می‌کردند، قادر به ارسال ترافیک به مقصد نبودند.

Magic Transit: کاربران نهایی که به اپلیکیشن‌هایی محافظت‌شده توسط Magic Transit متصل می‌شدند، از اینترنت announce نمی‌شدند و با timeout و خطا در اتصال مواجه شدند.

علاوه بر این، گروهی از مشتریان نتوانستند با تغییر وضعیت prefix در داشبورد سرویس خود را بازیابی کنند؛ وقتی مهندسان شروع کردند به reannounce کردن prefixها، برخی مشتریان علیرغم اینکه آدرس‌هایشان announce شده بود، افزایش latency و شکست‌هایی را مشاهده کردند، چون تنظیمات addressing برخی کاربران به‌خاطر یک باگ از edge حذف شده و نیاز به پراپِگِیشن مجدد به edge داشت 🛠️.

برای فهم اینکه دقیقاً چه در بخش addressing شکست خورده، لازم است کمی درباره Addressing API توضیح بدهم: Addressing API منبع حقیقت (authoritative) آدرس‌های موجود روی شبکه Cloudflare است. هر تغییری در این dataset بلافاصله روی شبکه جهانی Cloudflare منعکس می‌شود. در حال حاضر مشتریان می‌توانند از طریق public-facing APIs آدرس‌هایشان را تنظیم کنند که این عملیات مجموعه‌ای از دیتابیس‌ها و operational workflows را تحریک می‌کند تا تغییرات را به edge منتقل کنند. یعنی تغییرات Addressing API فوری به edge propagate می‌شوند.

روند معمول تبلیغ یا پیکربندی آدرس‌ها در Cloudflare شامل این مراحل است: مشتریان اعلام advertise/withdraw از طریق Addressing API یا BGP Control؛ Addressing API به ماشین‌ها دستور تغییر تبلیغات prefix را می‌دهد؛ بعد از اینکه تعداد کافی از ماشین‌ها اعلان را دریافت کردند، BGP روی روترها آپدیت می‌شود؛ و در نهایت مشتریان می‌توانند محصولات Cloudflare را با service bindings به رنج‌های BYOIP متصل کنند.

Addressing API بسیاری از فرآیندها را اتومات می‌کند، اما هنوز بعضی اقدامات دستی لازم است و آن‌ها ریسک بالایی دارند چون در مجاورت Production انجام می‌شوند. به عنوان بخشی از برنامه Code Orange: Fail Small، هدف اصلاح این فرآیندها و حذف اکشن‌های دستی خطرناک و جایگزینی با workflowهای امن و health-mediated بوده است.

ریشه فنی حادثه چه بود؟ قطعه پیکربندی آسیب‌دیده تلاش می‌کرد خودکارسازی فرآیند حذف prefixها از سرویس BYOIP را انجام دهد؛ عملیاتی که فعلاً به‌صورت دستی انجام می‌شود. برای مدیریت لیست‌های بزرگ related objects، این کار به‌عنوان یک sub-task دوره‌ای نوشته شده بود که BYOIPهایی که باید حذف شوند را تشخیص داده و سپس حذف می‌کرد. متأسفانه این cleanup sub-task یک query به API زد که دارای باگ بود.

این هم query که توسط cleanup sub-task فراخوانی شد:

</p>
<p>resp, err := d.doRequest(ctx, http.MethodGet, `/v1/prefixes?pending_delete`, nil)</p>
<p>

و این قسمت مربوط به پیاده‌سازی API بود:

</p>
<p>if v := req.URL.Query().Get("pending_delete"); v != "" { // ignore other behavior and fetch pending objects from the ip_prefixes_deleted table</p>
<p>    prefixes, err := c.RO().IPPrefixes().FetchPrefixesPendingDeletion(ctx)</p>
<p>    if err != nil {</p>
<p>        api.RenderError(ctx, w, ErrInternalError)</p>
<p>        return</p>
<p>    }</p>
<p>    api.Render(ctx, w, http.StatusOK, renderIPPrefixAPIResponse(prefixes, nil))</p>
<p>    return</p>
<p>}</p>
<p>

مشکل دقیق این بود که کلاینت پارامتر pending_delete را بدون مقدار فرستاده بود؛ در نتیجه Query().Get(“pending_delete”) یک رشتهٔ خالی (“”) برمی‌گرداند. به‌طور غیرمنتظره، این باعث شد API به‌عنوان یک درخواست برای همه BYOIP prefixها پاسخ دهد (به‌جای فقط آن‌هایی که در صف حذف بودند). cleanup sub-task پاسخ کامل را دریافت و آن را به عنوان لیستِ همه prefixهایی که باید حذف شوند تفسیر کرد و سپس به‌تدریج همه BYOIP prefixها و آبجکت‌های وابسته را حذف کرد تا زمانی که اثر آن مشاهده و sub-task خاموش شد.

چرا این باگ در staging یا تست دیده نشد؟ محیط staging تلاش می‌کند تا داده‌هایی مشابه Production داشته باشد، اما برای این سناریو کافی نبود و mock data مورد استفاده نتوانست رفتار واقعی را شبیه‌سازی کند. با اینکه تست‌هایی برای این عملکرد وجود داشت، پوشش این سناریو در تست‌ها و محیط اجرا ناقص بود. تست‌ها و code review بیشتر روی مسیر self-service BYOIP مشتریان تمرکز داشت و سناریویی که task-runner خودش بدون ورودی صریح کاربر تغییرات روی دادهٔ کاربر اعمال کند، پوشش داده نشده بود.

چرا بازگردانی فوری انجام نشد؟ prefixهای تأثیرپذیر BYOIP همه به یک شکل متاثر نشده بودند، بنابراین نیاز به اقدامات بازیابی داده‌ای و پراکندگی state به edge وجود داشت. در قالب Code Orange: Fail Small، ما سیستمی در حال ساخت داریم که بتوان snapshots از state عملیاتی را با health-mediated deployments امن rollout کرد؛ اما این سیستم هنوز کامل نشده بود؛ بنابراین بازگردانی کامل نیازمند تعاملات دستی و زمان برای propagate به edge شد.

اقدامات اصلاحی و درس‌هایی که می‌گیریم (خلاصه و عملی برای SREها):

– بررسی و قاعده‌مند کردن پارامترهای API: پارامترهای query باید وجود و مقدار صریح داشته باشند؛ API باید به‌صورت قاطع در صورت ارسال پارامتر بدون مقدار رفتار ایمن داشته باشد (به‌طور مثال reject یا require explicit value).

– افزایش پوشش تست‌ها: اضافه کردن تست‌های انتها-به-انتها برای تسک‌های دوره‌ای (task-runner) که بدون ورودی کاربر اجرا می‌شوند و سناریوهایی که mock data را به چالش می‌کشند.

– hardening cleanup jobs: هر کار پاکسازی که ایندیسیو است باید اول dry-run، دوم validation و third-party checks داشته باشد و نهایتاً با feature flag یا gate اجرا شود.

– حذف یا محدودسازی اکشن‌های دستی نزدیک به Production: ادامه برنامه Code Orange: Fail Small جهت اتوماتیک کردن ایمن و health-mediated rolloutها و حذف عملیات دستی پرخطا.

– snapshots و rollbacks ایمن: پیاده‌سازی snapshots از state عملیاتی که بتوان به سرعت و با confidence به edge بازگرداند؛ و طراحی rolloutها به‌صورت canary/health-mediated تا blast radius کوچک‌تر شود.

– مانیتورینگ و هشدار سریع‌تر: افزودن مترهای دقیق‌تر روی advertising و withdraw در سطح prefix و alertهایی که بتوانند روند غیرعادی withdraw را قبل از تأثیر قابل توجه شناسایی کنند.

خلاصه: خطا ناشی از ترکیب یک cleanup task اشتباه با یک رفتار ناخواسته در API بود که باعث شد لیست کامل BYOIP prefixes برای حذف در نظر گرفته شود. تیم مهندسی تغییر را سریع revert کرد، ولی بازگردانی کامل به‌دلیل پیچیدگی state در edge و نیاز به بازیابی دستی چندصد prefix، چند ساعت طول کشید. ما متأسفیم بابت اختلال ایجادشده و در حال پیاده‌سازی تغییرات ساختاری و عملیاتی هستیم تا از تکرار چنین رویدادهایی جلوگیری شود 🚧.