Hook hoạt động lần đầu, và sau ba lần kết nối lại, chuỗi giữ ba bản sao của cùng một quy tắc. Không có gì hỏng ồn ào; script chỉ làm đúng điều được bảo mỗi lần chạy. Hook VPN là một thao tác cấu hình có thể chạy nhiều hơn một lần, nên nó cần chủ sở hữu, trạng thái mong muốn và một thay đổi được tuần tự hóa. Đi theo thứ tự đó và các bản trùng sẽ dừng.

Giao quyền sở hữu cho các quy tắc trước khi viết bất kỳ quy tắc nào

Quyết định chuỗi hoặc bảng nào thuộc về tích hợp này trước khi viết một quy tắc nào. Một chuỗi riêng do hook tạo và xóa sạch là hình thức sở hữu rõ ràng nhất, vì việc dọn dẹp sau đó có thể xóa chuỗi ấy mà không chạm vào chính sách của người khác. Nếu buộc phải thêm quy tắc vào một chuỗi chia sẻ, hãy gắn thẻ bằng chú thích để lần tìm kiếm sau tìm đúng quy tắc của bạn. Quyền sở hữu là thứ khiến việc gỡ bỏ an toàn, và gỡ bỏ là phần âm thầm thất bại.

iptables -N VPN_HOOK 2>/dev/null || true
iptables -C INPUT -j VPN_HOOK 2>/dev/null || iptables -I INPUT -j VPN_HOOK
iptables -S VPN_HOOK

Chú thích ở đây không phải trang trí. Đó là khác biệt giữa gỡ quy tắc của bạn và gỡ một quy tắc tình cờ trông giống, và nó tồn tại trong bộ quy tắc đã lưu. Viết chú thích một lần vào một biến và dùng cùng chuỗi đó trong mọi lệnh thêm, kiểm tra hoặc xóa.

Kiểm tra trạng thái mong muốn thay vì thêm lại

Viết ra điều gì phải tồn tại khi đường hầm đang hoạt động: giao diện nào, nguồn và đích nào, giao thức nào và cổng nào. Sau đó diễn đạt mỗi quy tắc như một lần kiểm tra tiếp theo là một lần chèn, để lần chạy thứ hai tìm thấy quy tắc và không làm gì. Lệnh kiểm tra kết thúc bằng không khi quy tắc tồn tại, và đó chính là tín hiệu mà script cần. Tài liệu tham chiếu iptables ghi lại các dạng kiểm tra, thêm và chèn được dùng ở đây.

iptables -C VPN_HOOK -i tun0 -p tcp --dport 443 -j ACCEPT ||
  iptables -A VPN_HOOK -i tun0 -p tcp --dport 443 -j ACCEPT

Cẩn thận với vị trí. Kiểm tra một quy tắc và chèn lên đầu có thể dời một quyền cho phép lên trước một lệnh từ chối có chủ đích trước đây đứng đầu, nên lần chạy thứ hai thay đổi hành vi dù không thêm gì. Thêm ở cuối bên trong chuỗi của riêng bạn, và kiểm soát thứ tự bằng vị trí của bước nhảy vào chuỗi đó. Nếu thứ tự bên trong chuỗi quan trọng, hãy dựng lại chuỗi từ một danh sách đã biết thay vì vá từng quy tắc một.

Dừng lại nếu quy tắc cần thiết không thể diễn đạt như một phép kiểm tra ổn định. Điều đó thường có nghĩa là trạng thái mong muốn vẫn còn mơ hồ.

Tuần tự hóa thay đổi thay vì hy vọng nó giữ được

Hai lần gọi hook có thể chồng lên nhau, và cả hai đều có thể thấy một quy tắc vắng mặt trước khi một trong hai thêm nó. Tùy chọn chờ của xtables khiến một lệnh chờ khóa, nhưng nó không làm cho chuỗi kiểm-tra-rồi-thêm trở nên nguyên tử qua hai lệnh. Tuần tự hóa toàn bộ thao tác bằng khóa của riêng bạn, hoặc dùng một trình quản lý áp dụng lô đã khai báo như một giao dịch duy nhất. nftables làm được điều sau, và đó là một lý do để ưu tiên nó trên các máy chủ nơi nó có sẵn.

flock /run/vpn-hook.lock /usr/local/sbin/vpn-firewall-apply
nft -f /etc/nftables/vpn-hook.nft

Dù chọn cơ chế nào, hãy giữ phần quan trọng ngắn và làm cho nó có thể vào lại: một hook hỏng giữa chừng phải an toàn khi chạy lại. Ghi lại kết quả để lần chạy lặp lại hiện ra trong nhật ký thay vì bị suy ra từ số lượng quy tắc. Nếu hai giám sát khác nhau có thể gọi hook, hãy sửa điều đó trước; không khóa nào sửa được hai chủ sở hữu.

Chỉ gỡ bỏ những gì bạn sở hữu

Việc gỡ bỏ phải phân biệt một kết nối thay thế với kết nối mà nó đã cho nghỉ hưu, và phải sống sót qua một sự kiện không bao giờ được dọn dẹp gọn gàng. Xóa bằng đúng các định danh bạn đã dùng để thêm, và không bao giờ xóa sạch một chuỗi chia sẻ để đơn giản hóa việc dọn dẹp. Sau một lần dừng cứng, hãy chạy một lượt đối soát ở lần khởi động kế tiếp thay vì cho rằng hook hạ đã chạy.

iptables -D VPN_HOOK -i tun0 -p tcp --dport 443 -j ACCEPT
iptables -F VPN_HOOK
ip -brief link show type tun

Tuổi thọ của giao diện cũng thuộc về thiết kế. Kết nối lại có thể tạo một thiết bị với tên khác, nên một quy tắc mã hóa cứng tên đường hầm thứ hai trở nên lỗi thời trong khi vẫn trông hợp lệ. Ưu tiên danh tính mà chính kết nối bộc lộ, và đọc lại nó mỗi lần chạy. Kiểm tra ba sự kiện riêng biệt: kết nối lặp lại, kết nối thay thế và tắt máy. Lưu lượng được phép phải sống sót qua cả ba mà không trùng lặp, và các dịch vụ không liên quan phải không bị chạm tới.

Cuộc đi bộ kết thúc khi hook có chủ sở hữu, một phép kiểm tra cho mỗi quy tắc nó thêm, một đường đi được tuần tự hóa và một lần gỡ bỏ chỉ xóa trạng thái của chính nó. Một hook an toàn khi chạy hai lần đáng giá hơn một hook thông minh một lần.

Bài viết liên quan