dns.bibica.net v3 – Mosdns-x Native

Trên dns.bibica.net, ở phiên bản v1 tôi dùng AdGuard Home (Caddy), v2 đổi sang dùng Mosdns-x (Caddy + Redis), hiệu năng tốt cả, vì DNS yêu cầu phần cứng rất nhẹ, có điều cảm giác cấu hình dài dòng, phức tạp quá mức cần thiết, vì Mosdns-x có thể hoạt động độc lập rất tốt

Mosdns-x Native sử dụng Mosdns-x PR, viết sửa 1 số tính năng so với mosdns-x gốc, cài thêm lego hỗ trợ tạo SSL, lego chỉ gọi khi tạo mới hoặc gia hạn SSL, không chạy ngầm, nên không tốn CPU và RAM

  • Cập nhật Go và thư viện phụ thuộc lên phiên bản mới nhất
  • Thêm các kiểm tra, làm sạch DNS và tùy chọn cho phép bật/tắt 1 số tính năng từ tầng server, giúp giảm overhead do không cần tạo context plugin, đây là thay đổi quan trọng, vì chỉ phải kiểm tra 1 lần từ đầu, các plugin viết thêm không cần kiểm tra nữa
    • ANY luôn bị chặn, đúng 1 question, Opcode = QUERY, Qclass = INET, header phải là truy vấn thuần
    • Tự động chuyển domain về chữ thường
    • Chặn truy vấn IPv6 AAAA (nếu bật block_aaaa)
    • Chặn truy vấn ngược PTR (nếu bật block_ptr)
    • Chặn truy vấn HTTPS (nếu bật block_https)
    • Chặn domain không có dot, ví dụ localhost (nếu bật block_no_dot)
    • Xóa phần mở rộng EDNS (nếu bật strip_edns0)
  • Các giao thức TCP, UDP, DoH, DoH3, DoT và DoQ được điều chỉnh và tối ưu lại, tập trung sửa lỗi, nâng cao hiệu suất và độ ổn định
  • Health check và redirect:
    • Thêm health_path cung cấp endpoint kiểm tra tình trạng hoạt động
    • Thêm redirect_url tự động chuyển hướng các request không phải DNS (ngoại trừ health_path/dns-query) tới URL tùy chỉnh
  • Bảo mật và quyền riêng tư:
    • Tắt hoàn toàn việc ghi nhật ký IP client để đảm bảo tính ẩn danh, kể cả khi bật logging
  • SSL
    • Tốc độ kết nối: cải thiện khả năng kết nối lại sau khi restart server, các khóa được lưu tại thư mục key để hỗ trợ 0-RTT và TLS session resumption
    • Thuật toán mã hóa: sử dụng X25519 và ECDSA (P-256), tiêu tốn ít CPU hơn và tạo chứng chỉ nhỏ hơn so với Post-Quantum (ML-KEM/Kyber)
    • Hỗ trợ nén certificate bằng brotli nếu client hỗ trợ (chỉ hoạt động trên DoH/DoT)
    • Kiểm tra certFile, keyFile khi có renew lại chính xác hơn
  • Nén gói tin DNS trước khi gửi tới client msg_buf
  • Cache: thay đổi rất nhiều so với mosdns-x gốc, không dùng cache_everything mà quản lý theo pipeline, ECS chỉ được xử lý tại vị trí đặt plugin ecs trong file yaml
    • Sử dụng GetMsgHash tạo key cache 8 byte (uint64 hashing) nhỏ và nhanh hơn
    • Khi đạt giới hạn size, hệ thống tái sử dụng (reuse) phần tử cũ thay vì cấp phát mới, cơ chế này loại bỏ overhead từ GC và tránh tăng RAM đột biến khi traffic cao
    • Lazy cache: viết lại đơn giản hơn, loại bỏ các xử lý dư thừa
    • Thêm cleaner_interval cho phép tùy chỉnh thời gian dọn key cache
    • Hỗ trợ bộ nhớ đệm NXDOMAIN
    • Tối ưu concurrent_lru, listlru … giúp lưu và xóa cache hiệu quả hơn
  • ECS
    • Chuẩn hóa ECS: các IP trong cùng dải 1.2.3.41.2.3.5 đều được quy về 1.2.3.0/24, đảm bảo cache hoạt động chính xác
  • Upstream
    • Khi có >=2 upstream, chạy song song (parallel)
    • Sử dụng X25519 và ECDSA (P-256), tiêu tốn ít CPU hơn và tạo chứng chỉ nhỏ hơn so với Post-Quantum (ML-KEM/Kyber)
  • Matcher
    • response_matcherquery_matcher viết lại rất nhiều để tăng hiệu quả xử lý
  • Plugin: hầu hết plugin đã loại bỏ validation do đã xử lý từ tầng server, giúp giảm độ trễ toàn hệ thống
    • redirect: loại bỏ CNAME nhưng giữ lại A/AAAA theo tên truy vấn gốc, đồng bộ với plugin _no_cname
    • limit_ip: giới hạn tối đa 2 địa chỉ IP trong mỗi phản hồi DNS
    • dynamic_domain_collector: tự động đọc và ghi domain vào file
    • query_summary: tắt ghi nhật ký IP client và hạn chế ghi các lỗi nhẹ ở mức info

Gần 95% file gốc của dự án mosdns-x gốc đều có sửa qua, 1 số thay đổi hiệu quả, còn lại ở mức rất nhỏ, cache là thứ viết lại nhiều nhất, từ sửa đơn giản, cho tới viết mới hoàn toàn, đều thấy không hiệu quả, bản cache hiện tại đang duy trì cân bằng mọi thứ, không thay đổi quá nhiều

Reinstall OS

  • Trên 1 VPS mới nên reinstall bản mặc định, tránh mọi lỗi linh tinh phát sinh, phiên bản của bin456789 có sẵn chọn port, cài vào là coi như VPS đã tự đổi port SSH, đỡ 1 số thao tác thủ công
sudo -s
cd ~
curl -O https://raw.githubusercontent.com/bin456789/reinstall/main/reinstall.sh
bash reinstall.sh debian 13
# root with a default password 123@@@

Basic Optimization

  • Cài đặt một số ứng dụng thiết yếu, tinh chỉnh cơ bản ban đầu, chú ý là bản script này xóa khá sạch các ứng dụng còn sót trên Debian 13, mặc định tắt logs hệ thống
apt install -y curl sudo && curl -fsSL go.bibica.net/vps | sudo bash -s -- -no-log -no-docker
  • Xong xuôi reboot lại VPS cho sạch sẽ

Cài đặt Mosdns-x Native và lego

Cá nhân duy trì cài đặt tất cả mọi thứ vào thư mục /home, nên làm bash script cài đặt Mosdns-x Native và lego tự động vào /home

wget -qO /home/setup-mosdns-x-native.sh https://go.bibica.net/mosdns-x && sudo bash /home/setup-mosdns-x-native.sh
  • Nhập domain muốn dùng và thông tin Cloudflare API Token

Mặc định SSL tự động gia hạn mỗi ngày, cập nhập các bộ lọc quảng cáo và domain tlds cũng đã chạy tự động, không cần quan tâm thêm

Bash script xử lý cơ bản các lỗi khi tạo SSL, lưu key Cloudflare API Token, cài đặt lại hoặc đổi domain khác chỉ cần chạy lại setup-mosdns-x-native.sh

Nếu đổi VPS, copy toàn bộ dữ liệu từ /home, chép sang /home trên VPS mới, chạy setup-mosdns-x-native.sh chọn tùy chọn 2 Restore

  • Mosdns-x Native tạo sẵn 1 số command, khi dùng gỡ phải gõ nhiều, nhớ duy nhất dns -h là đủ
MosDNS-X Commands:
  dns start    - Start service
  dns stop     - Stop service
  dns restart  - Restart service
  dns status   - Show status
  dns log      - View logs
  dns update   - Update to latest
  dns -v       - Show version

Cá nhân hay dùng dns restart khi cập nhập cấu hình, thi thoảng chạy dns update để cập nhập Mosdns-x PR và lego lên bản mới nhất

Tùy chỉnh Geo Firewall v3 – cài đặt thủ công

Với các dịch vụ DNS public, nên cài firewall, hạn chế các kết nối rác từ các tool, bot khác

nano /home/setup-geo-firewall.sh

Mặc định cho phép user IP Việt Nam, Singapore, Japan, Hong Kong truy cập vào VPS qua TCP port 22 2224 53 443 853, UDP port 53 443 853

# Allowed countries (ISO 3166-1 alpha-2 codes)
ALLOW_COUNTRIES=("VN" "SG" "JP" "HK")

# Services to be protected (ports open to allowed countries)
ALLOW_TCP_PORTS=("53" "443" "853")
ALLOW_UDP_PORTS=("53" "443" "853")
NO_RATELIMIT_TCP_PORTS=("22" "2224")  # GeoIP protected, but no rate-limit (e.g. SSH)

# Allow ICMP echo-request (ping) from allowed countries
ENABLE_PING=false

# RATE LIMIT (per IP, per port)
# If an IP sends more than RATE packets/sec (after BURST absorbed), it gets penalized.
# While penalized, only THROTTLE_RATE packets/sec are allowed for PENALTY_TIME seconds.
ENABLE_RATE_LIMIT=true
RATE_LIMIT_UDP=250          # Max packets/sec per IP (UDP)
RATE_LIMIT_TCP=500          # Max packets/sec per IP (TCP)
BURST_UDP=5000              # Allowed spike before rate-limit kicks in
BURST_TCP=5000              # Allowed spike before rate-limit kicks in
THROTTLE_RATE=5             # Packets/sec allowed while penalized
PENALTY_TIME=5              # Penalty duration (seconds, auto-refreshed if abuse continues)

Cần mở thêm port, thêm vào ALLOW_TCP_PORTS, ALLOW_UDP_PORTS, hoặc NO_RATELIMIT_TCP_PORTS các giá trị khác, dùng như mặc định là được

  • Điều chỉnh xong chạy cài đặt firewall
/home/setup-geo-firewall.sh

DoH DoT DoQ DoH3 đã cấu hình mở port sẵn, nếu VPS chạy qua các dịch vụ cloud như Amazon, Google, Oralce, Tencent …. cần vào quản trị mở các port TCP 53 443 853 – UDP 53 443 853

watch -n 1 "for s in \$(ipset list -n | grep rl_pen); do count=\$(ipset list \$s | grep 'Number of entries' | awk '{print \$NF}'); if [ \$count -gt 0 ]; then echo \"--- \$s (\$count) ---\"; ipset list \$s | sed -n '/Members:/,\$p' | tail -n +2; fi; done"

Có thể chạy lệnh bên trên, kiểm tra IP nào đang bị Rate Limit chặn

Bản Geo Firewall v3 theo mình là đủ hiệu quả cho các nhu cầu sử dụng DNS thông thường, cũng gọi là nhà có khóa cửa 😀

Kiểm tra tỷ lệ cache và tốc độ các giao thức

Giai đoạn đầu sử dụng, có thể bật ghi log

  • Sửa log level thành info, bỏ comment ở dòng - _query_summary trong /home/mosdns-x/config/config.yaml
log:
  level: info # Options: debug, info, error, warn
  file: "log/mosdns.log"

Chạy ít ngày, khi lượng log đủ nhiều, có thể kiểm tra tỷ lệ cache, tốc độ các giao thức bằng đoạn code bên dưới

awk -v exclude_str="dnscheck.tools check-dns.net leaktest.net bibica.net" '
BEGIN {
    # Split the exclusion string into an array
    n = split(exclude_str, ex_arr, " ")
}

# Match specific protocols
/"protocol": "(h2|h3|tls|quic)"/ {
    # Extract domain name
    match($0, /"qname": "([^"]+)"/); domain = substr($0, RSTART+10, RLENGTH-11)
    
    # Check if domain contains any of the excluded strings
    skip = 0
    for (i=1; i<=n; i++) {
        if (index(domain, ex_arr[i])) {
            skip = 1
            break
        }
    }
    if (skip) next

    # Extract protocol
    match($0, /"protocol": "([^"]+)"/); proto = substr($0, RSTART+13, RLENGTH-14)
    
    # Extract elapsed time string
    match($0, /"elapsed": "([0-9.]+)(µs|ms)"/); 
    e = substr($0, RSTART, RLENGTH)
    gsub(/"elapsed": "|"/, "", e)
    
    # Convert time to microseconds
    match(e, /[0-9.]+/); time = substr(e, RSTART, RLENGTH)
    
    # Counter for domain frequency
    domain_count[domain]++

    if (index(e, "ms")) { 
        time *= 1000
        nc[proto]++; nct[proto] += time
        total_nc++; total_nct += time
    } else { 
        c[proto]++; ct[proto] += time
        total_c++; total_ct += time
    }
    tot[proto] += time; cnt[proto]++
    
    print time "\t" proto "\t" domain > "/tmp/mosdns_slow.txt"
}

function fmt(us) { return us >= 1000 ? sprintf("%.2fms", us/1000) : sprintf("%.2fµs", us) }

END {
    total = total_c + total_nc
    if (total == 0) { print "No data found."; exit }

    print "=== TOTAL ==="
    printf "Cache: %s (%d, %.1f%%) | Non-cache: %s (%d, %.1f%%) | Total: %d\n\n", 
        fmt(total_ct/total_c), total_c, total_c*100/total, 
        fmt(total_nct/total_nc), total_nc, total_nc*100/total, total
    
    print "=== PER PROTOCOL ==="
    for (p in tot) {
        ca = c[p] ? ct[p]/c[p] : 0; nca = nc[p] ? nct[p]/nc[p] : 0; t = c[p]+nc[p]
        printf "%s: Cache=%s (%d, %.1f%%) | Non-cache=%s (%d, %.1f%%) | Total=%d\n", 
            p, fmt(ca), c[p], c[p]*100/t, fmt(nca), nc[p], nc[p]*100/t, t
        cache[p] = ca; noncache[p] = nca
    }
    
    print "\n=== SUMMARY ==="
    n1=0; for (p in cache) { cl[n1]=p; cv[n1]=cache[p]; n1++ }
    for (i=0; i<n1; i++) for (j=i+1; j<n1; j++) 
        if (cv[i] > cv[j]) { t=cv[i]; cv[i]=cv[j]; cv[j]=t; t=cl[i]; cl[i]=cl[j]; cl[j]=t }
    
    n2=0; for (p in noncache) { ncl[n2]=p; ncv[n2]=noncache[p]; n2++ }
    for (i=0; i<n2; i++) for (j=i+1; j<n2; j++) 
        if (ncv[i] > ncv[j]) { t=ncv[i]; ncv[i]=ncv[j]; ncv[j]=t; t=ncl[i]; ncl[i]=ncl[j]; ncl[j]=t }
    
    printf "Cache: "; for (i=0; i<n1; i++) printf "%s%s (%s)", i?" > ":"", cl[i], fmt(cv[i]); print ""
    printf "Non-cache: "; for (i=0; i<n2; i++) printf "%s%s (%s)", i?" > ":"", ncl[i], fmt(ncv[i]); print ""
    
    print "\n=== TOP 10 MOST FREQUENT ==="
    for (d in domain_count) print domain_count[d] "\t" d > "/tmp/mosdns_freq.txt"
    close("/tmp/mosdns_freq.txt")
    system("sort -rn /tmp/mosdns_freq.txt | head -10 | awk '\''{printf \"%2d. %-5d hits - %s\\n\", NR, $1, $2}'\''")
    system("rm -f /tmp/mosdns_freq.txt")

    print "\n=== TOP 10 SLOWEST ==="
    close("/tmp/mosdns_slow.txt")
    system("sort -rn /tmp/mosdns_slow.txt | head -10 | awk '\''function f(u){return u>=1000?sprintf(\"%.2fms\",u/1000):sprintf(\"%.2fµs\",u)} {printf \"%2d. %-8s - %-5s - %s\\n\", NR, f($1), $2, $3}'\''")
    system("rm -f /tmp/mosdns_slow.txt")
}' /home/mosdns-x/log/mosdns.log

Lượng log lưu lại càng nhiều, sẽ ít sai số hơn, thường sau khi DNS server của bạn, chạy vài ngày, cache HIT > 90% thì không cần quan tâm tới giao thức DoH hay DoQ gì nữa, vì load từ cache, thường < 100µs, tức là < 0.1ms, mà 1000ms mới bằng …. 1 giây 😀 giao thức nào cũng na ná nhau cả

Kiểm tra các log lỗi

awk '/"resp_rcode": [1-9]/ || /"resp_rcode": [0-9][0-9]+/' /home/mosdns-x/log/mosdns.log > /home/mosdns-x/log/error.txt

Nó sẽ tạo ra file /home/mosdns-x/log/error.txt, ghi lại các resp_rcode khác 0, kiểm tra xem các domain này gặp vấn đề gì để có hướng sử lý

Chạy theo dõi ít hôm, thấy ổn định rồi thì tắt log đi cho nhẹ

Kết luận

Cá nhân vẫn thích dùng Caddy và chạy mọi thứ qua Docker, quản lý quen thuộc hơn, còn dùng trần như bản v3 thì được cái đơn giản, mosdns-x sập hay lỗi, kết luận ngay do mosdns-x, đỡ phải tìm hiểu nguyên nhân do ứng dụng nào

Mosdns-x Native chặn khá nhiều request không cần thiết (PTR ANY HTTPS) kiểm tra domain đầu vào, nên giảm khá nhiều request rác, thường sau 1 ngày sử dụng, các domain được cache đầy đủ, hiệu năng gần như kịch trần

Theo tác giải thì Mosdns-x có thể đáp ứng 1000 user cùng sử dụng mà dùng loanh quanh 5% CPU trên các VPS cấu hình thấp, thèng dns.bibica.net trung bình 50 user, load average < 1%, nói chung trừ khi bạn có 10.000 user cùng dùng, cần theo dõi thêm, còn dưới mức này VPS cấu hình thấp nhất, giá rẻ nhất dùng vẫn thừa


Related Posts

Chính sách bình luận: Chúng tôi rất trân trọng các bình luận của bạn và cảm ơn thời gian bạn dành để chia sẻ ý tưởng và phản hồi.
Ghi chú: Những bình luận được xác định là spam hoặc chỉ mang tính quảng cáo sẽ bị xóa.

• Để cải thiện trải nghiệm bình luận, chúng tôi khuyến khích bạn tạo một tài khoản Gravatar. Thêm avatar vào tài khoản Gravatar sẽ giúp bình luận của bạn dễ nhận diện hơn đối với các thành viên khác.

✂️ Sao chép và 📋 Dán Emoji 💪 giúp bình luận thêm sinh động và thú vị!