Free

Để Claude xóa IP origin của bạn khỏi DNS công khai

IP origin sau Cloudflare bị rò qua subdomain deploy — ba repo, mười bốn giây, fix gọn một lượt


Chiều hôm đó tôi nhìn GitHub một lúc. Ba repo đóng PR lúc 16:20:31, 16:20:38 và 16:20:45 — cách nhau mười bốn giây.

  • smarts #38: deploy.smarts.mddeploy.smarts
  • how2claude #13: deploy.how2claude.comdeploy.how2claude
  • pickful #118: deploy.pickful.ai / deploy.pickful.xyzdeploy.pickful / deploy.pickful-alpha

Cả ba bản fix đều cùng một loại bug. Ban đầu tôi chỉ trò chuyện với Claude về một trục trặc deploy không liên quan trong how2claude. Tiện thể, nó liếc qua config/deploy.yml và bảo tôi rằng IP origin của máy đó đang lộ liễu trên DNS công khai.

Một ảo giác đã đánh lừa tôi suốt nhiều tháng

Tất cả dự án của tôi đều ở sau Cloudflare. Đám mây nhỏ màu cam trong dashboard Cloudflare có nghĩa là "record được proxy" — request HTTP đến edge node Cloudflare trước, rồi mới đến origin của tôi. IP origin không bao giờ xuất hiện trong response DNS; cái DNS trả về là một IP anycast của Cloudflare. Phần đó đúng.

Nhưng tôi deploy bằng Kamal. Kamal SSH vào server để chạy docker. SSH không thể đi qua HTTP proxy, nên tôi cần một hostname không được Cloudflare proxy để SSH. Cấu hình của tôi khi đó:

# config/deploy.yml
servers:
  web:
    - deploy.how2claude.com   # ← record A trỏ thẳng đến IP origin, mây xám (DNS only)

Trong Cloudflare, how2claude.comwww.how2claude.com màu cam. deploy.how2claude.com màu xám. Nó phải xám, nếu không SSH không vào được máy.

Xám có nghĩa là DNS công khai. Bất kỳ ai cũng có thể chạy:

$ dig deploy.how2claude.com +short
<IP origin của tôi>

Và quy ước đặt tên deploy.<domain> tự thân nó đã là một dấu hiệu — quét subdomain deploy.* trên một danh sách domain SaaS phổ biến, bạn có cả mớ IP origin lẽ ra phải nấp sau Cloudflare.

Vượt qua WAF Cloudflare, vượt qua rate limiting, vượt qua bảo vệ DDoS. Chỉ một lệnh dig.

Tại sao Claude nhận ra

Hôm đó chúng tôi nói chuyện về thứ khác. Tôi nhờ nó review một tài liệu deploy mà tôi vừa viết; nó mở config/deploy.yml để đối chiếu, đến dòng 7 — - deploy.how2claude.com — dừng lại một chút, rồi nói đại loại:

Hostname này resolve qua DNS công khai đúng không? Nghĩa là bất kỳ ai chạy DNS query đều lấy được IP origin đó, và edge proxy của Cloudflare bị né.

Tôi sững lại một giây. Việc này tôi lẽ ra phải thấy từ lâu — chỉ cần liếc thêm cái biểu tượng mây xám khi cấu hình Cloudflare và tự hỏi nó có nghĩa gì là đủ — nhưng tôi không. Tôi coi cái hostname đó là gì đó "nội bộ", chỉ vì prefix là deploy.. Não tôi lặng lẽ cấp cho nó một sự riêng tư mà nó chưa từng có.

Claude không có thiên kiến đó. Nó đọc một string trong field yaml và đặt câu hỏi máy móc: cái string này biến thành IP bằng cách nào? Đáp: DNS công khai. Kết luận: IP origin là công khai.

Cách fix: alias trong /etc/hosts

Cách fix hóa ra đơn giản đến phát ngượng. Kamal nhờ SSH client nội bộ resolve hostname, nên hostname chỉ cần resolve được trên máy của tôi — không cần resolve được trên toàn internet.

Rút gọn hostname trong yaml:

servers:
  web:
    - deploy.how2claude    # ← chú ý: không có .com

Rồi thêm một dòng vào /etc/hosts:

198.51.100.42  deploy.how2claude

Runner CI chạy deploy cũng cần dòng tương tự (env var, hoặc trực tiếp echo >> /etc/hosts trong workflow).

Kết quả:

  • Không có record deploy.how2claude.* nào tồn tại trong DNS công khai ở bất cứ đâu
  • Kẻ tấn công không thể lấy IP qua DNS — ít nhất con đường này đã bịt
  • Mọi kamal deploy / app exec / app logs vẫn chạy bình thường, vì /etc/hosts được tra cứu trước DNS
  • Cloudflare gọn hơn: cái subdomain mây xám gượng gạo đó biến mất

Accessory database cũng cần sửa tương tự:

accessories:
  db:
    image: postgres:17
    host: deploy.how2claude   # ← cùng hostname

Claude lan việc này sang cả ba repo

Đây mới là phần tôi muốn ghi lại.

Sau khi fix how2claude xong, tôi định chuyển sang việc khác. Claude dừng tôi: "Anh cũng dùng Kamal cho smartspickful đúng không? Để tôi kiểm tra hai cái đó."

Nó đi kiểm tra.

  • smarts: deploy.smarts.md — cùng vấn đề, record A công khai
  • pickful: deploy.pickful.ai (production), deploy.pickful.xyz (alpha), thêm một deploy-test1.pickful.ai đã chết từ lâu và một deploy.staging.yml tham chiếu domain blockgeek.com đã thoái hồi

Pickful rối nhất trong ba cái vì có nhiều destination (production / alpha / test2) và lịch sử cồng kềnh. Tiện tay Claude dọn luôn destination staging và test1 đã chết — không ai dùng nữa, chỉ là noise.

Commit cuối cùng:

smarts       6482472   2026-04-27 16:20:31  Use private deploy.smarts alias instead of public deploy.smarts.md (#38)
how2claude   ccc0344   2026-04-27 16:20:38  Use private deploy.how2claude alias instead of public deploy.how2claude.com (#13)
pickful      e6bf9af   2026-04-27 16:20:45  Deploy config hygiene + privatize hostnames (#118)

Mười bốn giây. Tất nhiên đó chỉ là lúc GitHub xả hàng đợi merge — công việc thật trải rộng trong hai giờ trước đó. Nhưng hình dạng hiệu quả là: một phát hiện, ba repo được fix.

Nếu chỉ một mình tôi, kịch bản thực tế là: fix ở how2claude, ghi một TODO "làm smarts và pickful nữa", rồi nhìn TODO đó nằm trong list ba tháng. Tôi đã chứng kiến chính xác chuỗi đó nhiều lần.

Một checklist bạn có thể copy

Nếu bạn deploy bằng Kamal, Capistrano, hay bất kỳ tool deploy dựa trên SSH nào:

  1. Mở config/deploy*.yml và grep host: cùng servers:. Liệt kê mọi hostname xuất hiện.
  2. Chạy dig +short cho từng cái. Cái nào trả về IP origin thay vì rỗng là đang rò.
  3. Đi qua dashboard CDN (Cloudflare v.v.) và để ý subdomain "mây xám" — đó là tập ứng viên.
  4. Đổi tên thành alias không TLD (deploy.<project>), và đặt IP vào /etc/hosts.
  5. Cập nhật cả deploy job ở CI. Trên GitHub Actions: echo "$IP deploy.<project>" | sudo tee -a /etc/hosts.
  6. Xóa record A công khai gốc.

Nếu team bạn quản nhiều dự án dùng chung một mẫu hạ tầng, viết grep one-liner quét tất cả deploy*.yml trên mọi repo còn đáng tin hơn việc kiểm từng cái một.

Bài học thật sự

Cloudflare không hỏng. Kamal cũng không hỏng. Cả hai tool đều làm đúng phần việc của chúng.

Bài học là: giá trị mặc định lặng lẽ đánh lừa bạn. deploy.<domain-của-bạn>.com nghe như tên nội bộ, nhưng DNS không có khái niệm "nội bộ" — record A là record A, và một khi đã được công bố thì nó công khai. Một cái tên có thể tạo cho bạn ảo giác về sự riêng tư, và cái biểu tượng mây xám nhỏ trong dashboard Cloudflare chỉ là phiên bản trực quan của cùng ảo giác đó: nó ngồi đó im lặng, không có cảnh báo đỏ, nhưng thực ra nó đang nói "record này không đi qua tôi".

Hãy để Claude đọc cấu hình deploy của bạn một lần. Nó không có ảo giác đó.