EST. 2026 — บันทึกงานเซิร์ฟเวอร์จริง SYSADMIN · SECURITY · AI · DIRECTADMIN เล่มที่ 01
THE SYSADMIN ALMANAC
หน้าหลัก / Security / Googlebot โดนไฟร์วอลล์ตัวเองแบนถาวร จนเว็บหายจากผลค้นหา
Security

Googlebot โดนไฟร์วอลล์ตัวเองแบนถาวร จนเว็บหายจากผลค้นหา

โดย Ton-Or ·14 ส.ค. 2026·อ่าน 10 นาที
Googlebot โดนไฟร์วอลล์ตัวเองแบนถาวร จนเว็บหายจากผลค้นหา

ไฟร์วอลล์ที่ตั้งใจปกป้องเว็บ กลับกลายเป็นตัวที่ทำให้เว็บหายจากผลค้นหาเสียเอง เพราะมันมองไม่ออกว่าอันไหนคือคนร้าย อันไหนคือบอทที่พาลูกค้ามาให้

พอดีผมนั่งไล่ตรวจว่าทำไมเว็บบางตัวบนเครื่องถึงมีทราฟฟิกจากผลค้นหาลดลงผิดปกติ ตั้งใจว่าจะไปดูฝั่ง robots.txt กับ sitemap ก่อน แต่ยังไม่ทันได้เปิดอะไรเลย ผมลองยิงเช็คไอพีของ Googlebot กับไฟร์วอลล์บนเครื่องดูเล่น ๆ ปรากฏว่ามันอยู่ในรายการแบนถาวรมาตั้งแต่สามวันก่อน ผมเลยหยุดงานอื่นแล้วมาไล่เรื่องนี้ทั้งวัน สุดท้ายได้ระบบกันซ้ำที่ทำงานเองทุกวันมาหนึ่งชุด อยากเอามาเล่าให้ฟังเพราะเดาว่าอีกหลายเครื่องก็เป็นอยู่โดยไม่รู้ตัวเหมือนกัน

ชื่อโดเมนกับไอพีของเซิร์ฟเวอร์ในบทความเป็นของสมมติ ส่วนช่วงไอพีของบอทเป็นของจริงเพราะเป็นข้อมูลสาธารณะที่ผู้ให้บริการประกาศเอง และตัวเลขทั้งหมดมาจากเครื่องจริงที่ผมดูแล

เช็คก่อนว่าบอทเข้าถึงเครื่องเราได้จริงไหม

คำสั่งที่ผมอยากให้ทุกคนลองเดี๋ยวนี้เลย ใช้เวลาสิบวินาที

# ถามไฟร์วอลล์ตรง ๆ ว่ารู้จักไอพีนี้ไหม
csf -g 66.249.74.100

# ไล่หาช่วงไอพีของบอทในรายการแบนถาวร
grep -E '^(66\.249\.|40\.77\.167\.|52\.167\.144\.|157\.55\.39\.|207\.46\.13\.)' /etc/csf/csf.deny

# ดูรายการแบนชั่วคราวด้วย บางทีของจริงอยู่ตรงนี้
csf -t | grep -E '66\.249\.|40\.77\.'

ผลที่ผมได้ตอนนั้นคือบรรทัดนี้

66.249.74.100 # lfd: (BFM) mod_security1=25 - Sat Jun  9 17:35:02 2026

ตัวเลข mod_security1=25 คือหัวใจของเรื่องทั้งหมด มันแปลว่าไอพีนี้ทำให้ WAF ส่งสัญญาณเตือนครบ 25 ครั้ง ระบบป้องกันการเดารหัสผ่านเลยแบนถาวรให้อัตโนมัติ และมีอีกหนึ่งรายการเป็นบอทของเครื่องมือวิเคราะห์ SEO เจ้าหนึ่งที่โดนแบนไปตั้งแต่หกวันก่อนหน้าด้วยเหตุผลเดียวกัน

ตัวนับที่แบนมันไม่ใช่ตัวที่คนส่วนใหญ่คิด

จุดที่ผมเสียเวลาไปพอสมควรคือการหาว่า "ใครเป็นคนสั่งแบน" เพราะบนเครื่อง DirectAdmin มีตัวนับที่แบนไอพีได้อยู่หลายตัว และมันไม่ใช่ตัวเดียวกัน ผมเลยนั่งไล่นับที่มาของทุกบรรทัดใน csf.deny ออกมาเป็นสถิติ

# นับว่าแต่ละบรรทัดมาจากตัวนับไหน
awk -F'#' '{print $2}' /etc/csf/csf.deny | sed 's/ - .*//' | sort | uniq -c | sort -rn | head

# จำนวนบรรทัดทั้งหมด เทียบกับเพดานที่ตั้งไว้
grep -vc '^#' /etc/csf/csf.deny
grep '^DENY_IP_LIMIT' /etc/csf/csf.conf
500 / 500csf.deny เต็มเพดานพอดี
408บรรทัดที่มาจากตัวนับ mod_security ของ BFM
25 ครั้งเกณฑ์ที่ BFM แบนถาวร
232บรรทัดจากตัวสแกนไฟล์ลับใน 7 วัน

สี่ร้อยกว่าบรรทัดจากห้าร้อย มาจากตัวนับตัวเดียว คือตัวที่นับสัญญาณเตือนของ WAF แล้วสะสมไว้ต่อไอพี พอครบ 25 ก็แบนถาวร ประเด็นคือมันนับ false positive รวมกับการโจมตีจริงในถังเดียวกัน กฎของ WAF ชุดมาตรฐานนั้นเข้มมาก เว็บที่มี URL หน้าตาแปลก ๆ นิดเดียวก็โดนนับได้แล้ว

ตัวนับของ BFM

25 ครั้ง แล้วแบนถาวร

อ่านล็อกของ WAF โดยตรง สะสมต่อไอพี ไม่มีรายการยกเว้นบอทมาให้ตั้งแต่ต้น ถ้าไม่ไปเพิ่มเอง Googlebot กับคนร้ายมีสิทธิ์เท่ากันทุกประการ

ตัวนับของ lfd

100 ครั้งใน 30 นาที

เกณฑ์สูงกว่ามาก และเป็นการแบนชั่วคราวก่อน ต้องโดนซ้ำหลายรอบถึงจะกลายเป็นถาวร ตัวนี้ไม่ค่อยเป็นปัญหากับบอทเท่าไหร่

ต้นเหตุจริง ๆ ของรอบนี้คือเว็บลูกค้าเว็บหนึ่งที่ URL ของหน้าแท็กไปตรงกับรูปแบบที่กฎ WAF ข้อหนึ่งมองว่าเป็นการยิง SQL ทุกครั้งที่ Googlebot เดินเก็บหน้าแท็ก ตัวนับก็เดินหนึ่งครั้ง เก็บไปเรื่อย ๆ ไม่กี่วันก็ครบ 25

เส้นทางจาก false positive สู่การหายจากผลค้นหาGooglebot เก็บหน้าแท็กURL ปกติของเว็บ→WAF เตือนfalse positive→ตัวนับเดินสะสมถึง 25→แบนถาวรเข้า ipset→Google เก็บหน้าไม่ได้อันดับหล่นตามมา

ที่ทำให้อาการนี้หายากคือเว็บยังเปิดได้ปกติทุกอย่างสำหรับคนทั่วไป ไม่มีใครแจ้งอะไรเข้ามา ในหน้าเครื่องมือของ search engine จะขึ้นแค่ว่าเข้าถึงไม่ได้ ซึ่งกว่าจะมีคนเห็นก็ผ่านไปหลายวันแล้ว

ปลดแบนเฉพาะหน้า แล้วต้องเช็คให้ครบสองที่

การปลดแบนไม่ยากครับ แต่มีจุดที่คนพลาดบ่อยคือลบออกจากไฟล์แล้วคิดว่าจบ ทั้งที่ตัวที่บล็อกจริงคือ ipset ในหน่วยความจำ

BFM นับ mod_security25 ครั้ง แบนถาวร ลง csf.denylfd100 ครั้ง/30 นาที ชั่วคราว (csf -t)ตัวสแกนไฟล์ลับ232 บรรทัดใน 7 วัน ลง csf.denyบล็อกรายประเทศชุด cc_* ไม่อยู่ใน csf.denyipset ในหน่วยความจำตัวบล็อกจริงGooglebotถูกตัดทิ้งปลดแบนแล้วต้องเช็ก ipset จริงคนทั่วไปยังเข้าเว็บได้ปกติ
ตัวที่แบนได้มีหลายตัวและบางตัวไม่เขียนลง csf.deny ปลดแบนแล้วต้องไปเช็กที่ ipset จริง ไม่ใช่เช็กแค่ไฟล์
# ปลดแบนถาวร (คำสั่งนี้จัดการทั้งไฟล์และ ipset ให้)
csf -dr 66.249.74.100

# ปลดแบนชั่วคราว ถ้ามี
csf -tr 66.249.74.100

# ยืนยันของจริงว่าไม่เหลือใน ipset แล้ว อย่าเชื่อแค่ไฟล์
ipset list chain_DENY 2>/dev/null | grep 66.249.74.100 && echo "[WARN] ยังอยู่" || echo "[OK] หลุดแล้ว"
csf -g 66.249.74.100

พอปลดเสร็จผมกลับไปแก้ต้นเหตุที่ฝั่ง WAF ด้วยการเพิ่มข้อยกเว้นเฉพาะกฎข้อนั้นกับเฉพาะ URL รูปแบบนั้นของเว็บนั้น ไม่ได้ปิดกฎทั้งข้อทั้งเครื่อง นี่คือส่วนที่ต้องระวังมากที่สุด เพราะเวลารำคาญ false positive แล้วคนมักจะปิดทั้งกฎ ซึ่งเปิดช่องให้ของจริงเข้ามาด้วย

แต่แก้แค่นี้ยังไม่พอครับ เพราะครั้งหน้าถ้ามีกฎอื่นเตือนผิดอีก ก็จะแบน Googlebot ซ้ำอีกอยู่ดี ต้องทำให้บอทที่เรารู้ว่าเป็นของจริงไม่มีวันถูกแบนได้เลย ตั้งแต่ต้น

แก้ถาวร ดึงช่วงไอพีทางการของบอทมาใส่รายการยกเว้นทุกวัน

ตอนแรกผมใส่ช่วงไอพีแบบเขียนมือลงไปก่อน แล้วก็รู้ตัวเร็วมากว่าวิธีนี้ใช้ไม่ได้ยาว เพราะผู้ให้บริการเปลี่ยนช่วงไอพีเรื่อย ๆ และเขาประกาศรายการทางการเป็นไฟล์ให้ดึงได้อยู่แล้ว ผมเลยลบของที่เขียนมือทิ้ง แล้วเขียนสคริปต์ดึงอัตโนมัติแทน

  1. ดึงรายการช่วงไอพีทางการของแต่ละเจ้าจาก URL ที่เขาประกาศไว้เอง ทั้ง Google, Bing, Apple, DuckDuckGo และช่วงของ CDN ที่เว็บบนเครื่องใช้อยู่
  2. ตรวจความถูกต้องของทุกบรรทัดด้วยไลบรารีจัดการไอพีจริง ไม่ใช่ regex ถ้าแปลงเป็นเครือข่ายไม่ได้ก็ทิ้งบรรทัดนั้น
  3. เขียนลงไฟล์ยกเว้นในบล็อกที่คั่นด้วยเครื่องหมายเริ่มต้นและสิ้นสุดของเราเอง ห้ามแตะบรรทัดนอกบล็อกที่คนอื่นเขียนไว้เด็ดขาด
  4. รีสตาร์ตตัว daemon เฉพาะตอนที่เนื้อหาเปลี่ยนจริงเท่านั้น ไม่ใช่รีสตาร์ตทุกวันเปล่า ๆ
  5. ไล่สแกนรายการแบนทั้งถาวรและชั่วคราว ถ้าเจอไอพีที่อยู่ในช่วงของบอท ให้ปลดแบนให้อัตโนมัติแล้วบันทึกไว้ในล็อก

หัวใจของสคริปต์อยู่ที่สามส่วนนี้ ผมยกมาเฉพาะส่วนที่สำคัญ

SOURCES = {
    "googlebot":   {"kind": "json", "urls": ["https://developers.google.com/static/search/apis/ipranges/googlebot.json"], "min": 5},
    "bingbot":     {"kind": "json", "urls": ["https://www.bing.com/toolbox/bingbot.json"], "min": 3},
    "applebot":    {"kind": "json", "urls": ["https://search.developer.apple.com/applebot.json"], "min": 3},
    "duckduckbot": {"kind": "json", "urls": ["https://duckduckgo.com/duckduckbot.json"], "min": 1},
    "cloudflare":  {"kind": "text", "urls": ["https://www.cloudflare.com/ips-v4",
                                             "https://www.cloudflare.com/ips-v6"], "min": 10},
}

def valid_nets(cands):
    """แปลงทุกบรรทัดเป็น network object จริง อันไหนแปลงไม่ได้ทิ้งทันที"""
    nets = []
    for c in cands:
        c = c.strip()
        if not c:
            continue
        try:
            nets.append(str(ipaddress.ip_network(c, strict=False)))
        except ValueError:
            pass
    return sorted(set(nets), key=lambda n: (":" in n, n))

def get_vendor(vendor, cfg):
    """ดึงของใหม่ ถ้าล้มเหลวหรือได้น้อยผิดปกติ ให้ใช้สำเนาที่แคชไว้แทน"""
    cands = []
    try:
        for u in cfg["urls"]:
            raw = fetch(u)
            cands += parse_json_prefixes(raw) if cfg["kind"] == "json" else raw.splitlines()
        nets = valid_nets(cands)
    except Exception as e:
        log(f"WARN {vendor}: fetch failed ({e}) -> using cache")
        nets = []
    cache = os.path.join(CACHE_DIR, vendor + ".list")
    if len(nets) >= cfg["min"]:
        open(cache, "w").write("\n".join(nets) + "\n")
        log(f"OK   {vendor}: {len(nets)} ranges (fresh)")
        return nets
    if os.path.exists(cache):
        nets = [l.strip() for l in open(cache) if l.strip()]
        log(f"WARN {vendor}: fresh list too small -> cache ({len(nets)} ranges)")
        return nets
    log(f"FAIL {vendor}: no fresh data and no cache - vendor skipped this run")
    return []
ค่า min ต่อเจ้า คือกันตัวเองพลาด

ถ้าวันไหนผู้ให้บริการเปลี่ยนรูปแบบไฟล์ หรือเน็ตเราหลุดกลางคัน สิ่งที่แย่ที่สุดคือได้รายการว่างเปล่ากลับมาแล้วสคริปต์เขียนทับของเดิมจนรายการยกเว้นหายเกลี้ยง เท่ากับเราปลดเกราะให้บอททุกตัวด้วยมือตัวเอง ค่า min กับสำเนาแคชต่อเจ้าเลยต้องมีไว้กันเรื่องนี้ ถ้าของใหม่ดูผิดปกติให้ใช้ของเก่าไปก่อนเสมอ ดีกว่าเขียนของว่างลงไป

ส่วนการเขียนลงไฟล์ ผมใช้เครื่องหมายคั่นบล็อกแล้วเขียนทับเฉพาะข้างในเท่านั้น

MARK_BEGIN = "# === BOT-WHITELIST-AUTO BEGIN (managed by script - DO NOT EDIT) ==="
MARK_END   = "# === BOT-WHITELIST-AUTO END ==="

def replace_block(path, block_lines):
    """แทนที่เฉพาะในบล็อก คืนค่า True ถ้าไฟล์เปลี่ยนจริง"""
    txt = open(path).read() if os.path.exists(path) else ""
    lines = txt.split("\n")
    block = [MARK_BEGIN] + block_lines + [MARK_END]
    if MARK_BEGIN in lines and MARK_END in lines:
        i, j = lines.index(MARK_BEGIN), lines.index(MARK_END)
        new = lines[:i] + block + lines[j + 1:]
    else:
        new = lines + ([""] if lines and lines[-1] else []) + block
    out = "\n".join(new)
    if not out.endswith("\n"):
        out += "\n"
    if out != txt:                       # เขียนเมื่อเนื้อหาต่างจริงเท่านั้น
        tmp = path + ".bwl-tmp"          # เขียนไฟล์ชั่วคราวแล้วสลับ กัน daemon อ่านไฟล์ครึ่ง ๆ กลาง ๆ
        with open(tmp, "w") as f:
            f.write(out)
        os.chmod(tmp, 0o600 if "csf" in path else 0o644)
        os.replace(tmp, path)
        return True
    return False

แล้วส่วนสุดท้ายคือไล่ปลดแบนย้อนหลังให้เอง อันนี้สำคัญกว่าที่คิด เพราะไอพีที่โดนแบนไปแล้วก่อนหน้าจะไม่หลุดออกมาเองแม้เราจะเพิ่มรายการยกเว้นทีหลัง

for ip, kind in banned_ips():                       # ทั้ง csf.deny และ csf -t
    addr = ipaddress.ip_address(ip)
    hit = next((v for net, v in nets_typed if addr in net), None)
    if hit:
        flag = "-dr" if kind == "perm" else "-tr"
        subprocess.run(["csf", flag, ip], capture_output=True, text=True)
        log(f"UNBAN {ip} ({kind}) - matched {hit} range")

ผลการรันรอบแรก

772ช่วงไอพีที่เขียนลงรายการยกเว้นของไฟร์วอลล์
750ช่วงที่ส่งต่อเข้ารายการยกเว้นของแผงควบคุม
1ไอพี CDN ที่โดนแบนถาวรอยู่ ถูกปลดทันทีในรอบแรก
0ความเปลี่ยนแปลงตอนรันซ้ำ ทำงานซ้ำได้ปลอดภัย

จุดที่ผมแยกสองรายการไม่เท่ากันคือ ช่วงของ CDN ผมใส่เฉพาะฝั่งไฟร์วอลล์ ไม่ใส่ในรายการของแผงควบคุม เพราะขอบเครือข่ายของ CDN ไม่ควรมีเหตุให้ต้องเดารหัสผ่านบริการอะไรบนเครื่องเราอยู่แล้ว ส่วนที่ต้องใส่ฝั่งไฟร์วอลล์เพราะถ้าเว็บที่วิ่งผ่าน CDN โดนโจมตี ตัวที่จะโดนแบนคือขอบของ CDN ซึ่งเท่ากับทำให้เว็บทุกตัวที่ใช้ CDN นั้นล่มพร้อมกันทั้งเครื่อง

ตั้ง cron แล้วอย่าลืมล็อก

# /etc/cron.d/bot-whitelist
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
10 5 * * * root /usr/local/sbin/bot-whitelist-update.py >> /var/log/bot-whitelist-update.log 2>&1
# /etc/logrotate.d/bot-whitelist
/var/log/bot-whitelist-update.log {
    weekly
    rotate 8
    compress
    missingok
    notifempty
    create 0640 root root
}

ผมตั้งไว้ตีห้าสิบนาที เหตุผลคือเลี่ยงนาทีที่ลงตัวพอดี ซึ่งเป็นเวลาที่งานตามเวลาของทั้งเครื่องชอบมากองกัน และเป็นช่วงก่อนที่ทราฟฟิกเช้าจะเข้ามา

กับดัก grep ที่ทำให้สคริปต์หลอกตัวเอง

ตรงนี้คือส่วนที่ผมอยากเน้นที่สุดในบทความ เพราะมันเป็นบั๊กที่เงียบมากและเจอยาก

นอกจากรายการยกเว้นตามไอพีแล้ว ยังมีรายการยกเว้นตามชื่อโฮสต์ย้อนกลับอีกไฟล์หนึ่ง ซึ่งใช้กับบอทที่เราตรวจสอบชื่อโดเมนได้ ผมเขียนสคริปต์ติดตั้งให้เติมสองบรรทัดนี้ลงไป

# ที่อยากได้ในไฟล์ csf.rignore
.googlebot.com
.search.msn.com

โค้ดตอนแรกของผมเขียนแบบนี้ ซึ่งดูไม่มีอะไรผิดเลย

# ผิด — เช็คว่ามีอยู่แล้วหรือยัง
grep -q '.googlebot.com' /etc/csf/csf.rignore || echo '.googlebot.com' >> /etc/csf/csf.rignore

รันแล้วไม่มีอะไรเกิดขึ้น สคริปต์บอกว่ามีอยู่แล้ว แต่พอเปิดไฟล์ดูจริงกลับไม่มีบรรทัดนั้นสักตัว สาเหตุคือไฟล์ต้นแบบที่ติดตั้งมาพร้อมโปรแกรมมีคำว่า .googlebot.com อยู่ในคอมเมนต์ตัวอย่าง ที่หัวไฟล์อยู่แล้ว

head -20 /etc/csf/csf.rignore
# Ignore file for reverse DNS lookups, e.g:
#
# .googlebot.com
# .search.msn.com
#

grep เลยเจอ แล้วตอบว่ามีอยู่แล้ว ทั้งที่บรรทัดที่เจอเป็นคอมเมนต์ที่ไม่มีผลอะไรเลย ซ้ำร้ายจุดในรูปแบบค้นหายังหมายถึงอักขระอะไรก็ได้อีกด้วย

เช็คว่ามีบรรทัดอยู่แล้วหรือยัง ต้องยึดหัวบรรทัดเสมอ

คอมเมนต์ตัวอย่างในไฟล์ต้นแบบของโปรแกรมส่วนใหญ่ มักจะเขียนตัวอย่างที่ตรงกับสิ่งที่เราจะเติมพอดี การ grep แบบลอย ๆ เลยเจอเสมอ และสคริปต์จะข้ามการเติมไปเงียบ ๆ โดยไม่มีข้อความผิดพลาดใด ๆ เราจะนึกว่าติดตั้งเรียบร้อยแล้วทั้งที่ยังไม่มีอะไรเกิดขึ้น อาการแบบนี้จะไปโผล่ตอนโดนแบนจริงเท่านั้น

# ถูก — ยึดหัวบรรทัด และหนีอักขระจุดให้เรียบร้อย
grep -qE '^\.googlebot\.com[[:space:]]*$' /etc/csf/csf.rignore \
  || echo '.googlebot.com' >> /etc/csf/csf.rignore

grep -qE '^\.search\.msn\.com[[:space:]]*$' /etc/csf/csf.rignore \
  || echo '.search.msn.com' >> /etc/csf/csf.rignore

# ตรวจของจริง นับเฉพาะบรรทัดที่ไม่ใช่คอมเมนต์
grep -vE '^\s*(#|$)' /etc/csf/csf.rignore

กฎที่ผมยึดตั้งแต่วันนั้นคือ เวลาเขียนสคริปต์แบบเติมบรรทัดถ้ายังไม่มี ให้ตรวจผลลัพธ์สุดท้ายจริง ๆ เสมอ ไม่ใช่เชื่อค่าที่ grep คืนกลับมา ต่างจากฝั่งไฟล์ที่จัดการด้วยบล็อกซึ่งเราเขียนทับทั้งก้อนอยู่แล้ว ปัญหานี้ไม่เกิด

อีกด้านหนึ่งของเหรียญเดียวกันที่ผมเจอในการตรวจรอบเดียวกันคือ grep หาไม่เจอทั้งที่มีอยู่ ตอนตรวจว่าเซิร์ฟเวอร์อ่านไอพีจริงของผู้ใช้หลัง CDN ได้ไหม ผมไล่ค้นในไฟล์ config หลักแล้วไม่เจอ เลยสรุปว่าไม่ได้ตั้ง แต่ความจริงคือแผงควบคุมมันแยกไฟล์นั้นไว้อีกไฟล์หนึ่งซึ่งถูก include เข้ามาอยู่แล้ว ถ้าผมเผลอเติมเข้าไปอีกชุด ตัวเว็บเซิร์ฟเวอร์จะฟ้องว่าตั้งค่าซ้ำแล้ว config พังทั้งเครื่องทันที บทเรียนคือให้ตรวจจากค่าที่โปรแกรมอ่านได้จริงหลังรวมทุกไฟล์แล้ว ไม่ใช่ตรวจจากไฟล์ใดไฟล์หนึ่ง

ผลข้างเคียงของการบล็อกทั้งประเทศ ที่ไม่มีใครนึกถึงตอนเปิด

เรื่องสุดท้ายที่โผล่มาระหว่างตรวจรอบนี้ ผมไล่เช็คว่าบอทของ search engine แต่ละเจ้าเข้าถึงเครื่องได้จริงไหม แล้วพบว่ามีเจ้าหนึ่งเข้าไม่ได้เลยแม้แต่ไอพีเดียว ทั้งที่ไม่มีชื่ออยู่ในรายการแบนใด ๆ

# ไอพีนี้ไม่ได้อยู่ใน csf.deny แต่เข้าไม่ได้ ทำไม
csf -g 5.255.253.10

# เพราะมันอยู่ในชุดไอพีของการบล็อกรายประเทศ
ipset list cc_ru | grep -cE '^5\.255\.|^87\.250\.224\.|^213\.180\.203\.|^141\.8\.'

# ดูว่าเปิดบล็อกประเทศไหนไว้บ้าง
grep -E '^CC_DENY|^CC_ALLOW' /etc/csf/csf.conf

ต้นเหตุคือมีการตั้งบล็อกทราฟฟิกขาเข้ารายประเทศไว้ตั้งแต่ตอนติดตั้ง เหตุผลตอนนั้นคือลดการโดนเดารหัสผ่าน ซึ่งได้ผลจริง แต่ผลข้างเคียงที่ไม่มีใครพูดถึงคือบอทของ search engine ที่ตั้งฐานอยู่ในประเทศนั้นก็เข้าไม่ได้ทั้งหมด ช่วงไอพีหลักของมันอยู่ในชุดที่ถูกบล็อกครบทุกช่วง เท่ากับเว็บทุกตัวบนเครื่องไม่มีวันขึ้นในผลค้นหาของเจ้านั้นเลย

ก่อนเปิดบล็อกรายประเทศ ให้ลิสต์ก่อนว่าเสียอะไรไปบ้าง

การบล็อกทั้งประเทศเป็นเครื่องมือที่หยาบมาก มันไม่ได้บล็อกแค่คนร้าย แต่บล็อกทุกอย่างที่มาจากที่นั่นรวมถึงบอทของ search engine ผู้ให้บริการอีเมล และลูกค้าจริงที่บังเอิญใช้ VPN ปลายทางประเทศนั้น ถ้าเว็บบนเครื่องไม่มีใครต้องการผู้อ่านจากประเทศนั้นเลยก็ยังพอคุยกันได้ แต่ต้องเป็นการตัดสินใจที่รู้ตัว ไม่ใช่ผลข้างเคียงที่เพิ่งมารู้ตอนตรวจ

รอบนี้ผมเอาประเทศนั้นออกจากรายการ แล้วโหลดกฎใหม่ จากนั้นยืนยันว่าไม่เหลือการอ้างถึงชุดนั้นในตารางกฎแล้วจริง

# แก้ค่าในไฟล์ แล้วโหลด allow/deny list ใหม่
sed -i 's/^CC_DENY_PORTS = "RU,KP,IR"/CC_DENY_PORTS = "KP,IR"/' /etc/csf/csf.conf
csf -ra

# ต้องได้ 0 คือไม่มีกฎไหนอ้างชุดนั้นอีกแล้ว
iptables-save | grep -c 'cc_ru'

# ชุดของประเทศที่ยังบล็อกอยู่ ต้องยังอยู่ครบ
ipset list -n | grep '^cc_'

# ยิงทดสอบว่าบอทเข้ามาได้แล้ว
csf -g 5.255.253.10

บอทปลอมก็มี อย่าเปิดให้ตามชื่อที่มันบอก

ปิดท้ายด้วยเรื่องที่ต้องระวังไปอีกทางหนึ่ง ระหว่างไล่ล็อกผมเจอทราฟฟิกที่แจ้ง user-agent ว่าเป็น Googlebot จำนวนไม่น้อยแต่มาจากช่วงไอพีที่ไม่ใช่ของ Google เลย

ของจริง

ยืนยันได้ด้วยไอพีและชื่อโฮสต์ย้อนกลับ

Googlebot อยู่ในช่วง 66.249.x เท่านั้น ส่วน Bing อยู่ที่ 40.77.167.x, 52.167.144.x, 157.55.39.x และ 207.46.13.x ทั้งหมดตรวจย้อนกลับได้ว่าชื่อโฮสต์ลงท้ายด้วยโดเมนของเจ้าของจริง

ของปลอม

ใช้ชื่อเขา แต่มาจากที่อื่น

ที่ผมเจอมาจากช่วงของผู้ให้บริการคลาวด์ทั่วไป ตัวพวกนี้ใช้ชื่อบอทเพื่อขอผ่านด่าน การที่ไฟร์วอลล์บล็อกมันคือทำถูกแล้ว ส่วนตัวผมถือว่าห้ามยกเว้นให้ตาม user-agent เด็ดขาด

นี่คือเหตุผลที่ผมยกเว้นตามช่วงไอพีทางการที่เจ้าของประกาศเอง กับชื่อโฮสต์ย้อนกลับเท่านั้น ไม่เคยยกเว้นตาม user-agent เพราะ user-agent ใครก็ปลอมได้ในสามวินาที ส่วนไอพีปลอมไม่ได้ถ้าเขาต้องรอรับข้อมูลกลับ

สรุปสิ่งที่ผมทำทั้งหมดในเคสนี้ ปลดแบนบอทที่โดนไปแล้ว แก้ข้อยกเว้นของ WAF ที่ต้นเหตุแบบเจาะจง วางสคริปต์ดึงช่วงไอพีทางการมาใส่รายการยกเว้นทุกวันพร้อมกลไกกันตัวเองพลาด ตั้ง cron กับ logrotate ให้เรียบร้อย และทบทวนการบล็อกรายประเทศว่าเรากำลังเสียใครไปบ้าง

ที่อยากฝากไว้คือ เครื่องที่มีระบบป้องกันอัตโนมัติหลายชั้นทับกัน มันจะมี "ตัวที่แบน" มากกว่าหนึ่งตัวเสมอ เวลามีอะไรเข้าไม่ได้อย่าเพิ่งดูแค่ไฟล์เดียว ให้ไล่ให้ครบว่าตัวไหนเป็นคนสั่ง ใครจะเอาสคริปต์ไปปรับใช้ ผมแนะนำให้เริ่มจากรันแบบดูผลอย่างเดียวก่อนสักสองสามวัน ดูล็อกว่ามันจะไปแตะอะไรบ้าง แล้วค่อยเปิดให้แก้ไฟล์จริง จะปรับรายชื่อผู้ให้บริการเพิ่มลดยังไงก็ตามหน้างานได้ตามสะดวกครับ ตรงไหนผมอธิบายตกหล่นหรือพิมพ์ผิด เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ </content>

อยากให้เราทำให้ไหม?

ให้เราช่วยติดตั้ง ตั้งค่า ย้ายระบบ หรือแก้ปัญหาเซิร์ฟเวอร์ เพื่อให้คุณมีเวลาไปทำงานที่สำคัญกว่า

คุยเรื่องงานกับเรา

เคยเจองานแบบนี้ไหม? ติดตรงไหน?

มาแลกวิธีกันต่อในบอร์ด LinuxThai

→ คุยต่อในบอร์ด

ความคิดเห็น 0

ยังไม่มีความคิดเห็น — เป็นคนแรกเลย