1 điểm bởi GN⁺ 2024-03-31 | 1 bình luận | Chia sẻ qua WhatsApp
  • TablePlus vẫn tiếp tục theo dõi tình trạng máy chủ dù các nỗ lực DDoS đã kéo dài nhiều tuần, không chặn IP và cũng không bật chế độ Cloudflare “Under Attack”
  • Trong 30 ngày gần đây có khoảng 6 triệu lần thử tải xuống tệp cài đặt, riêng 5 ngày gần nhất là 800.126 lần, với kích thước tệp khoảng 200MB cho mỗi lượt tải
  • Ngay cả khi bị tấn công, mức sử dụng CPU của máy chủ phần lớn vẫn chỉ ở 0~1%, và hệ thống được cấu hình để khoảng 8 dịch vụ API cùng cơ sở dữ liệu có thể xử lý hàng tỷ yêu cầu mỗi tháng mà không cần cache
  • Backend được gom thành dịch vụ monolithic theo từng ứng dụng, và được triển khai lên VPS mới dưới dạng một binary duy nhất, không dùng Docker, Kubernetes hay môi trường runtime
  • Cấu hình đơn giản như framework Go/Rust, lập chỉ mục cơ sở dữ liệu, tách riêng DB log, reverse proxy Nginx, Cloudflare CDN/R2 và throttling khi gửi email giúp giảm chi phí tấn công và độ phức tạp vận hành

Các nỗ lực DDoS kéo dài nhiều tuần

  • Có người đã gửi hàng trăm triệu yêu cầu đến máy chủ của TablePlus trong nhiều tuần và cố gắng tải xuống tệp cài đặt hàng triệu lần
  • Số lần thử tải xuống tệp cài đặt đạt 800.126 lần trong 5 ngày gần đây và khoảng 6 triệu lần trong 30 ngày gần nhất
    • Mỗi tệp cài đặt có dung lượng khoảng 200MB
  • Xét theo các lệnh gọi API trong 30 ngày gần đây, phần lớn lưu lượng đến từ EU, đặc biệt là Germany và United Kingdom
    • Con số này không bao gồm các yêu cầu tải xuống và lưu lượng CDN
  • Tại thời điểm viết bài, cuộc tấn công vẫn đang tiếp diễn

Cách ứng phó thực tế: thiết kế để chịu tải thay vì chặn

  • Không chặn địa chỉ IP của kẻ tấn công
  • Có sử dụng Cloudflare nhưng không bật chế độ “Under Attack”
  • Ngay cả khi bị tấn công, CPU máy chủ phần lớn vẫn ở mức 0~1%, gần như nhàn rỗi
  • Vì dịch vụ có thể xử lý hàng tỷ yêu cầu mỗi tháng mà không gặp vấn đề và chi phí cũng không quá lớn, nên hầu như không cần biện pháp bổ sung

Thiết kế backend đơn giản

  • Thiết kế ứng dụng của TablePlus hướng đến sự đơn giản, và các dịch vụ backend cũng được giữ ở cấu hình tối thiểu nhiều nhất có thể
  • Không dùng các dịch vụ render bên thứ ba như Vercel hay Netlify vì chúng có thể trở thành nút thắt hoặc tạo ra hóa đơn ngoài dự kiến khi bị tấn công
  • Họ cho rằng dùng web server tự vận hành có thể tránh được những giới hạn đó
  • Trước đây, monolith có thể trở thành nút thắt do VPS và bộ xử lý yếu, nhưng VPS hiện nay cung cấp CPU đa lõi, RAM lớn và SSD nhanh
    • SSD nhanh và RAM giúp cải thiện đáng kể hiệu năng cơ sở dữ liệu
    • Nếu triển khai đúng cách, một dịch vụ monolith chạy trên một instance duy nhất cũng có thể xử lý hàng tỷ yêu cầu mỗi tháng

Cách vận hành monolith theo từng ứng dụng

  • Tích hợp API, website, email, thanh toán và các thành phần cần thiết của từng ứng dụng vào một dịch vụ duy nhất
  • Việc triển khai chỉ cần một tệp cấu hình, build và deploy là xong
  • Khi cần chuyển dịch vụ sang nhà cung cấp cloud khác hoặc triển khai mới, có thể xử lý rất nhanh
  • Ít phụ thuộc nên dễ debug và xác định nút thắt
    • Ngay cả khi có lỗi xảy ra, số dịch vụ cần kiểm tra cũng chỉ là một hoặc rất ít
  • Ví dụ các framework monolith có thể chọn gồm:
    • Golang: Echo, Gin
    • PHP: Laravel
    • Ruby: Rails
    • Rust: Actix, Rocket, Warp

Các nguyên tắc cấu hình để xử lý hàng tỷ yêu cầu mỗi tháng

  • Chọn web framework hiệu năng cao, và TablePlus ưu tiên Golang cùng Rust
  • Khi tập dữ liệu lớn hơn, hãy tạo index trong cơ sở dữ liệu để giảm thời gian truy vấn
  • Tách riêng cơ sở dữ liệu chính với cơ sở dữ liệu log và usage để bảo vệ hiệu năng của nghiệp vụ cốt lõi
    • Cơ sở dữ liệu chính dùng cho dữ liệu không thay đổi hoặc không tăng kích thước theo thời gian
    • Cơ sở dữ liệu log và usage dùng cho dữ liệu tiếp tục tăng theo thời gian
  • Đặt reverse proxy trước các API cốt lõi để xử lý và phân phối yêu cầu
    • Điều này hữu ích khi cần nhiều máy chủ
    • TablePlus dùng Nginx
  • Đặt mọi thứ sau Cloudflare và cấu hình phù hợp cache, Argo, cũng như full SSL giữa Cloudflare và máy chủ
  • Dùng CDN có khả năng bảo vệ DDoS
    • TablePlus ưu tiên Cloudflare R2 và CDN hơn Amazon CloudFront + S3 để giảm chi phí và tăng khả năng bảo vệ
  • Nếu đặt các tệp tải xuống lớn trên VPS mà không có CDN hay cache, băng thông sẽ bị tiêu hao rất nhanh
    • TablePlus dùng Cloudflare CDN với băng thông không giới hạn
    • Nếu bật Argo trên cùng domain, lưu lượng Cloudflare CDN có thể đi qua Argo, và băng thông Argo không miễn phí nên chi phí có thể tăng mạnh
  • Với các yêu cầu mà máy chủ phải gửi email như quên mật khẩu, cần dùng throttling để bảo vệ hệ thống gửi mail

Cách triển khai: chạy binary không dùng container

  • TablePlus giữ quy trình triển khai đơn giản nhất có thể, không dùng Docker, Kubernetes, container hay thiết lập môi trường runtime riêng
  • Đơn vị triển khai là binary
    • Chỉ cần chép sang máy chủ Linux rồi chạy như một process
    • Cách này được xem là tương tự như chạy ứng dụng TablePlus trên macOS
  • Có thể giao cho Linux Systemctl giám sát process và tự khởi động lại nếu xảy ra lỗi nghiêm trọng
  • Họ chạy theo cách native để không lãng phí CPU/RAM cho các lớp trung gian như VM, ảo hóa hay trình quản lý hạ tầng bên thứ ba
  • Go và Rust được chọn vì là ngôn ngữ hiệu năng cao và có thể tạo tệp binary để triển khai

Các tính năng bảo vệ nên bật nếu dùng Vercel

  • Vercel cung cấp các tính năng bảo vệ website trong tình huống như vậy là Spend ManagementAttack Challenge Mode
    • Spend Management: có thể đặt giới hạn chi tiêu mềm hoặc cứng
    • Attack Challenge Mode: tương tự chế độ “Under Attack” của Cloudflare
  • Nếu dùng Vercel thì cần bật các tính năng đó

1 bình luận

 
GN⁺ 2024-03-31
Các ý kiến trên Hacker News
  • Bài này tạo cảm giác tự khen mình quá mức. “1 tỷ request mỗi tháng” chỉ là vài trăm request mỗi giây, khá nhỏ nhặt, và cũng khó gọi là DDoS
    Hơn nữa, site nằm sau Cloudflare, vốn là CDN, nên càng khó hiểu vì sao lại xem như họ đã làm điều gì đó đáng kể về mặt hiệu năng
    Ví dụ, không có tình huống hợp lý nào mà một file 200MB lại không được cache và phục vụ qua CDN. Không đáng để tự hào rằng server ứng dụng không đọc 200MB từ đĩa rồi copy sang client cho mỗi lượt tải xuống; làm vậy thì rõ ràng là thiết kế quá tệ

    • Nếu file 200MB đang nói tới là bản tải xuống ứng dụng client TablePlus tại https://tableplus.com/download, thì bản cho Windows là 183MB và thực tế đang được cache trên Cloudflare

      # curl -v https://files.tableplus.com/windows/5.9.2/TablePlusSetup.exec > /dev/null

      < cache-control: max-age=691200

      < cf-cache-status: HIT

      < age: 2980

    • Thông thường, đây chẳng phải là việc mà ở một công ty FAANG người ta sẽ cho rằng cần hơn 1.000 developer sao? Tôi không hiểu sao có thể chỉ gọi là tự khen mình đối với một việc mà ngay cả các công ty lớn cũng thường xuyên không làm được

    • Khó mà chỉ nhìn như vài trăm request mỗi giây. Mật độ request không phân bố đều theo thời gian, và thường có thể tăng lên tới 20 lần mức trung bình

  • 4TB mỗi tháng thật ra khó xem là một cuộc tấn công DDoS, không phải sao? Nếu là 4TB mỗi giờ thì có thể gọi là DDoS, nhưng 4TB mỗi tháng chỉ là 1,5MB mỗi giây
    6 triệu request mỗi tháng cũng chỉ là 2 request mỗi giây. Ở quy mô này, việc vận hành bằng một dịch vụ monolith có vẻ không quan trọng lắm, đặc biệt nếu xét rằng Cloudflare xử lý phần lớn request bằng cache CDN

    • Phần áp đảo của web là các site WordPress đặt trên hosting đa tenant giá 5–20 đô/tháng, còn tổ chức lớn hơn thì thường là Drupal. Không cài cache và kết nối thẳng vào database, nên phần lớn site trên Internet có thể sập chỉ với 4 request mỗi giây

      Nghe có vẻ điên rồ, nhưng thực tế là vậy. Trước đây tôi từng quản lý các dự án chống DDoS, WAF, firewall và bảo mật cho khách hàng ở Cloudflare, và thường xuyên thấy site của khách hàng bị kéo sập chỉ bằng web scraping hoặc mức HTTP request rất nhỏ và thấp

      Những con số lớn chiếm headline, nhưng thứ mà phần lớn mọi người thực sự cảm nhận lại là những con số nhỏ

    • Tấn công từ chối dịch vụ có thể thực hiện chỉ với vài KB mỗi giờ. Nếu chạm vào API endpoint nặng của dịch vụ mục tiêu thì chỉ riêng việc đó cũng có thể làm dịch vụ sập, còn Distributed chỉ có nghĩa là nhiều máy cùng tấn công một lúc

      DDoS không nhất thiết phải có throughput khổng lồ. Chỉ là những vụ xuất hiện trên tin tức thường là các cuộc tấn công lớn

      Mục tiêu của kiểu tấn công này có thể là đốt hạn mức băng thông của origin server, hoặc khiến chi phí băng thông trở nên không thể gánh nổi. Điều đó có thể làm rất rẻ chỉ bằng một hoặc vài máy tấn công. Hầu hết datacenter và nhà cung cấp hosting đều có giới hạn băng thông hoặc tính phí sau một mức nhất định, và quá nhiều công ty bị tấn công chỉ nhận ra sau khi nhận hóa đơn không thể chi trả

    • Có một website tải game với tổng số người chơi khoảng 50. Hầu như chẳng ai dùng, và người thật tải game có lẽ chỉ vài lần mỗi tháng
      Vậy mà riêng một file zip 2GB đã tạo ra 5TB traffic trong tháng trước, và chuyện này diễn ra suốt nhiều năm. Đây không phải DDoS, mà chỉ là bot/crawler cấu hình sai, đi theo mọi link và không biết hủy tải xuống

    • Có vẻ là khoảng 1TB mỗi ngày, nhưng dù vậy vẫn là rất nhỏ

    • Có lẽ còn tùy traffic biến động thất thường đến mức nào

  • Đây chỉ là một site marketing tĩnh cho ứng dụng desktop. Không có diễn đàn thảo luận, feedback thì xử lý qua GitHub issues
    Khá lạ khi khoe rằng việc triển khai site marketing tĩnh đơn giản đến mức nào, và rằng các file tĩnh vẫn chịu được vài triệu lượt tải mỗi ngày. Và việc giảm thiểu ở đây là do Cloudflare làm, không phải họ. Nếu lượng traffic nhỏ như thế này mà cần giảm thiểu

    Nếu tôi gặp kiểu “tấn công” này, có lẽ tôi còn chẳng nhận ra cho tới khi nhận email hằng tháng của Cloudflare kiểu “đã truyền X TB, tiết kiệm gần 100% băng thông”

    Bản thân ứng dụng thì tôi thích và có thể giới thiệu

    • Tôi cũng thích ứng dụng này, nhưng bài viết này nghe như nhờ ChatGPT tạo một bài marketing kỳ lạ. Tổng thể nghe không hợp lý, nhất là với những ai từng trải qua tấn công DDoS thật sự
    • Tôi thì lại muốn thấy nhiều hơn việc “khoe triển khai đơn giản đến mức nào”. Càng nhiều người nhận ra hạ tầng phức tạp quá mức của họ không phải là tối ưu thì càng tốt
  • Theo mô tả, chuyện này trông giống “lạm dụng dịch vụ vô nghĩa một cách khó chịu” với khoảng 8TB traffic bổ sung mỗi tháng hơn là một cuộc tấn công DDoS
    Có thể bỏ qua kiểu lạm dụng như vậy nếu nó không gây vấn đề về chi phí hay cạn tài nguyên, nhưng những câu chuyện kiểu “hóa ra 80% dung lượng của fleet autoscaling chẳng làm việc hữu ích nào cả” phổ biến đến mức đáng buồn, nên ít nhất cũng cần theo dõi

    Phần khó chịu nhất trong kiểu lạm dụng này thường là log. Phần lớn log bị lấp đầy bởi cùng những host lặp đi lặp lại các hành động vô nghĩa như nhau. Nếu kho lưu trữ log rẻ và dư dả thì không phải vấn đề lớn, nhưng có một cách tự động phân loại một số traffic là lạm dụng và hạn chế xử lý thường nhật chắc chắn là ý tưởng hay

    Tuy nhiên, nói thì dễ hơn làm. Tôi không đếm nổi đã bao nhiêu lần thêm logic phân loại vào inbound SMTP server để bắt những hành vi lạm dụng trắng trợn và ngu ngốc, nhưng lần nào nó cũng bắt nhầm cả những kịch bản còn hợp lệ ở vùng ranh giới

    Dành quá nhiều thời gian cho các chuỗi rabbit hole nối tiếp nhau rất dễ khiến bạn không làm được việc thực sự. Vì vậy đôi khi nên thuê ngoài; hoặc nếu việc đó quá phiền hay quá đắt, thì dù khó chịu cũng có khi tốt hơn là cứ bỏ qua lạm dụng

    • Trên AWS, 8TB outbound traffic là 595 đô/tháng ngay cả khi tính 1TB miễn phí mỗi tháng, còn ở Hetzner thì bắt đầu từ dưới 10 đô
      DigitalOcean, chẳng hạn, tính 30 đô cho 3TB vượt quá mức 5TB đi kèm gói s-4vcpu-8gb-intel, còn Linode là 15 đô cho phần vượt quá 3TB. Vì vậy tôi nghĩ bài viết vẫn có ý
    • Nếu cứ tiếp tục bỏ qua, chẳng khác nào khuyến khích họ làm những việc còn đáng ngờ hơn. Chính thái độ đó đã biến Internet ngày nay gần như thành một đầm lầy
  • Đây không phải là một “cuộc tấn công” đáng chú ý. Chỉ một bash script chạy mất kiểm soát trên máy của ai đó cũng có thể làm được
    “50 triệu request mỗi tháng từ Anh” trung bình còn chưa tới 20 request/giây. Tôi kỳ vọng chỉ một server Go đơn lẻ, không cần tối ưu lớn, cũng xử lý được lượng request gấp 250 lần như vậy

    Bản thân lời khuyên không hẳn là tệ, nhưng các con số đó không phải bằng chứng cho lời khuyên. Với một dịch vụ Go HTTP API, tôi kỳ vọng trên một VPS nhỏ đến trung bình, dù có một ít thao tác cơ sở dữ liệu và định dạng JSON, vẫn xử lý được 5 nghìn request/giây mà không cần tối ưu. Con số này dựa trên kinh nghiệm từng triển khai hàng chục dịch vụ tương tự ở nơi nhận hàng tỷ request mỗi ngày và lúc cao điểm vượt 500 nghìn request/giây

    • Nếu lượng traffic như vậy dồn vào API bằng các request lặp lại, mà tất cả đều đến từ những vị trí không đáng ngờ, điều đầu tiên nên nghĩ tới không phải DDoS mà là liệu có vô tình đưa vòng lặp retry vô hạn vào mã client hay không
  • Việc chuyện này không gây thiệt hại là tốt, nhưng nếu xem đây là lời khuyên thì còn thiếu khá nhiều phần nên hơi đáng lo
    Ưu tiên phân phối binary thay vì Docker thì ổn, nhưng host chạy binary đó sẽ xử lý thế nào? Một trong những lý do dùng container là để đóng gói các thiết lập tăng cường bảo mật cùng với bản triển khai, để sau này khi cần mở rộng vẫn có thể tin rằng cấu hình bảo mật giữa các node là giống nhau

    Monolith được nói tới ở đây có thể đặt trên một VPS duy nhất, điều đó tốt và cũng rẻ. Nhưng nếu nó crash hoặc phần cứng hỏng vì lý do nào đó thì có thể phát sinh downtime khá lớn

    Một điều đáng lo nữa là nếu gom mọi thứ vào monolith, bạn sẽ mất phòng thủ theo chiều sâu trong stack ứng dụng. Nếu ai đó xuyên thủng ứng dụng qua frontend, họ có thể đi thẳng tới kho dữ liệu backend. Vì vậy nhiều người đặt kho dữ liệu sau một web service nội bộ, rồi chặn bằng security group trong mạng riêng tách khỏi frontend, nhằm giới hạn bề mặt tấn công ở mức những thao tác có thể thực hiện qua trình duyệt

    • Không có thế giới nào mà việc tăng bề mặt tấn công lại làm bảo mật tốt hơn

    • Nếu nghĩ rằng cài Docker là host đã được bảo mật thì sai. Khi mở rộng, các host bổ sung sẽ được cấu hình thế nào?

      Dùng Docker thì không chỉ container mà cả host, tức dịch vụ chạy container, cũng phải an toàn. Khi mở rộng và triển khai thêm host, chúng cũng phải an toàn y như vậy

      Nếu dùng infrastructure as code và configuration as code, thì về bản chất không khác nhau là mấy giữa việc triển khai binary sau phần cấu hình hệ thống hay triển khai Docker

    • Có những công cụ giúp làm cho cấu hình “bare metal” có thể tái lập ở một mức độ nào đó. Ví dụ như NixOS, Ansible, và build image Amazon AMI

    • Tôi luôn không hiểu lắm câu “xuyên thủng ứng dụng qua frontend”. SQL injection đụng trực tiếp tới kho dữ liệu mà không cần thực thi mã từ xa, còn XSS thì ảnh hưởng ngang sang người dùng khác
      Vậy làm sao từ frontend lại có thể tự do đi tới toàn bộ backend? Người ta đang chạy một trình thông dịch JavaScript có quyền truy cập shell bên trong dịch vụ Go API rồi gọi eval trên input của người dùng à? Về mặt kỹ thuật nghe quá gượng ép

    • Cuối cùng chẳng phải đó là bảo mật bằng sự mơ hồ sao? Kiểu làm phức tạp đến mức chính chúng ta cũng không hiểu thì người khác cũng sẽ không hiểu?

      Điều quan trọng không phải là dựng thêm nhiều bức tường, mà là làm cho tường không thể bị phá. Interface làm giảm hiệu năng và tăng độ phức tạp

  • Cá nhân tôi thấy cách diễn đạt này hơi khó chịu. “1 tỷ request mỗi tháng” tương đương khoảng 370 request/giây. Đó là mức một server được cấu hình tốt cũng có thể xử lý, và chắc chắn dưới 10 máy là đủ
    Chỉ một bash script độc hại đơn lẻ cũng có thể tạo ra lượng traffic cỡ đó

    • Nhờ microservice mà chúng tôi cần 45 node Kubernetes để xử lý 1000 request/giây
    • Đúng nếu các request đó được phân bố đều theo thời gian. Nhưng trong tình huống bị tấn công, request có thể đột ngột tăng vọt rồi phẳng lại, và tính trên 30 ngày vẫn có thể là 1 tỷ request mỗi tháng
    • Không cần một server được cấu hình tốt; sau khi JVM JIT hoạt động thì một core cũng làm được
  • Có lẽ tôi mất cảm giác thực tế, nhưng vài tỷ request mỗi tháng nghe không có gì ghê gớm. Cái này được xem là một cuộc tấn công DDoS lớn à?

    • Còn tùy ai trả chi phí. Nếu tự host thì không thành vấn đề, nhưng với serverless thì mức đó thường cũng tốn kém, đặc biệt nếu đó là traffic không có giá trị
    • Với một API được thiết kế hợp lý, 300–400 API request/giây không phải tải nặng. 300–400 request/giây cho file tĩnh thì còn nhỏ hơn cả hạt đậu phộng
    • Đúng. 1 tỷ request mỗi tháng trung bình là 380 request/giây nên không cao lắm
    • Còn phụ thuộc nhiều yếu tố. Thông thường hạ tầng được provision theo mức sử dụng dự kiến, và phần dư thừa quá mức là lãng phí
  • Tôi thích đoạn “tạo một dịch vụ monolith cho từng app, dễ triển khai và bảo trì. Không Docker, không Kubernetes, không dependency, không runtime environment, chỉ một file binary duy nhất có thể triển khai lên bất kỳ VPS mới nào”

    • Tôi không trực tiếp dùng TablePlus. Nếu đây là nói về website marketing thì hiển nhiên không cần Kubernetes
      Nếu là nói về ứng dụng thì việc không có dependency khiến tôi thấy lạ. Không lưu dữ liệu và ghi log sao?

    • Đây là một ưu điểm thật sự tuyệt vời của Golang. Chỉ cần dựng VPS, áp dụng các giá trị mặc định hợp lý, rồi cross-compile và chạy binary

      So với triển khai Python, Node, PHP thì ít độ phức tạp không cần thiết hơn rất nhiều

      Giá mà việc chạy và giữ cho database server sống cũng đơn giản như vậy

  • Nhìn tiêu đề, tôi đã kỳ vọng nội dung hợp với swole doge hơn. Tôi đồng ý với khá nhiều lời khuyên, nhưng đứng sau Cloudflare thì tuyệt đối không phải là “không có gì”
    Tùy phân bố traffic, có thể chỉ VPS không Cloudflare cũng đã chịu được tốt, và quy mô cũng không có vẻ lớn đến vậy. Nếu có các thống kê chi tiết hơn như số request/giây và Cloudflare đã chặn bao nhiêu trước khi tới origin thì sẽ thú vị hơn

    Một vụ DDoS tầng 7 từ Nga nhắm vào doanh nghiệp Thụy Điển mà tôi biết lớn đến mức các nhà cung cấp lớn cũng gục vì vấn đề dung lượng. Bao gồm cả Verizon, Azure Frontdoor, Cloudflare và GCP load balancer. Trước quy mô như vậy, chiến lược này tuyệt đối không hiệu quả