Hai site, một mạng con, một hub WireGuard: hãy làm nó chạy
Cả hai LAN đều ở 10.10.7.0/24, nên hub không thể chọn site theo đích. Hãy cấp alias cho mỗi dịch vụ và dịch tại cổng của nó.
Hai site đều dùng cùng một dải LAN, và chính sự trùng lặp không phải là thứ thực sự phá vỡ thiết kế. Hub phải chọn site chỉ dựa trên địa chỉ đích, và hai đích giống hệt nhau không cho nó gì để chọn. Đánh số lại một văn phòng đang chạy để vừa một đường hầm thường là một cuộc trao đổi tệ hơn so với việc cấp cho mỗi dịch vụ được công bố một địa chỉ riêng mà đường hầm có thể chở. Thiết kế dưới đây công bố một dịch vụ HTTPS cho mỗi site và không gì khác.
Vì sao sự trùng lặp không phải là vấn đề thật
WireGuard chuyển tiếp theo đích: AllowedIPs của một peer quyết định peer nào nhận gói tin. Hai peer không thể cùng công bố 10.10.7.0/24 mà không có một bên thắng mọi lần tra cứu. Đó là thuộc tính của bảng peer chứ không phải của đường hầm, nên giải pháp ở đây là một quy hoạch địa chỉ chứ không phải mẹo định tuyến.
Kế hoạch là ngừng công bố LAN hoàn toàn. Thay vào đó hãy công bố một alias cho mỗi dịch vụ, một địa chỉ cho mỗi site, và để cổng của mỗi site dịch alias đó thành ứng dụng thật của nó.
Người dùng định tuyến hai địa chỉ duy nhất, không bao giờ thấy tiền tố mơ hồ, và không có gì thay đổi ở cả hai văn phòng. Điều này cũng nói rõ ranh giới một cách trung thực: bạn đang công bố một dịch vụ HTTPS từ mỗi site, không phải hợp nhất hai mạng. Các quy tắc sau vẫn nhỏ vì phát biểu hẹp. Một phát biểu rộng hơn sẽ cần quy tắc rộng hơn, và đó là cách những thiết kế này trở nên phức tạp.
Cho mỗi site một alias mà người dùng có thể định tuyến
Chọn địa chỉ từ một dải không xung đột với bất cứ thứ gì trên máy của người dùng, hub hay một trong hai LAN. Ở đây hub là 10.66.0.1, người dùng là 10.66.0.10, các cổng là 10.66.0.21 và 10.66.0.22, và hai alias là 10.77.1.20 và 10.77.2.20. Hub công bố mỗi alias thông qua peer sở hữu nó, nên một gói tin đi ra được gửi tới một alias chỉ có thể rời đi qua site khớp:
[Interface]
Address = 10.66.0.1/32
ListenPort = 51820
PrivateKey = HUB_KEY
[Peer]
PublicKey = USER_PUBLIC_KEY
AllowedIPs = 10.66.0.10/32
[Peer]
PublicKey = SITE_A_PUBLIC_KEY
AllowedIPs = 10.66.0.21/32, 10.77.1.20/32
[Peer]
PublicKey = SITE_B_PUBLIC_KEY
AllowedIPs = 10.66.0.22/32, 10.77.2.20/32
Phía người dùng chỉ cần hai alias, điều này giữ cấu hình máy khách không chứa cả hai tiền tố LAN. Không có gì trong cấu hình đó nhắc tới bất kỳ LAN thật nào:
[Interface]
Address = 10.66.0.10/32
PrivateKey = USER_KEY
[Peer]
PublicKey = HUB_PUBLIC_KEY
Endpoint = hub.example.net:51820
AllowedIPs = 10.77.1.20/32, 10.77.2.20/32
PersistentKeepalive = 25
Mỗi cổng công bố hub và nguồn của người dùng, không gì khác, nên một site không bao giờ biết LAN của site kia. Keepalive duy trì một ánh xạ NAT trên đường lên của site; nó không cho phép lưu lượng, và tường lửa vẫn quyết định điều gì được chấp nhận. Sao chép cấu hình của cổng này sang cổng kia là ổn miễn là khóa riêng, địa chỉ giao diện và alias đều thay đổi.
Hub phải cho phép gì giữa hai peer của nó
Hub chuyển tiếp gói tin giữa hai peer của chính nó, đó là quyết định trong chuỗi forward chứ không phải quyết định đầu vào. Bật chuyển tiếp IPv4 bằng cơ chế thông thường của nền tảng, rồi cho phép đúng những cặp mà thiết kế cần.
Quy tắc dưới đây cho phép người dùng tới một alias và một cổng, và cho phép lưu lượng trả về thông qua theo dõi kết nối. Nó bỏ mọi thứ khác trên đường forward, và đó chính là điểm mấu chốt:
nft add table inet hub_transit
nft add chain inet hub_transit forward '{ type filter hook forward priority 0 ; policy drop ; }'
nft add rule inet hub_transit forward ct state established,related accept
nft add rule inet hub_transit forward iifname wg0 oifname wg0 tcp dport 443 ip saddr 10.66.0.10 ip daddr 10.77.1.20 accept
Hai chi tiết đáng nêu vì chúng gây ra những thất bại âm thầm. Một accept trong một chuỗi cơ sở không ghi đè một drop ở chuỗi khác, nên hãy hợp nhất điều này với chính sách hiện có thay vì nạp nó bên cạnh. Và địa chỉ nguồn nhận diện thiết bị, không phải người; hãy giữ xác thực ứng dụng tách khỏi việc thu nhận mạng.
Nơi alias trở thành địa chỉ thật
Site A nhận một yêu cầu tới 10.77.1.20, viết lại đích thành ứng dụng thật và viết lại nguồn thành địa chỉ LAN của chính nó để phản hồi có thể quay về mà không cần tuyến tới đường hầm. Làm cả hai phép dịch ở một chỗ giữ phần còn lại của mạng không biết gì về alias. Các quy tắc dưới đây là toàn bộ cơ chế:
nft add table ip site_a_nat
nft add chain ip site_a_nat pre '{ type nat hook prerouting priority -100 ; policy accept ; }'
nft add chain ip site_a_nat post '{ type nat hook postrouting priority 100 ; policy accept ; }'
nft add rule ip site_a_nat pre iifname wg0 tcp dport 443 ip saddr 10.66.0.10 ip daddr 10.77.1.20 dnat to 10.10.7.20:443
nft add rule ip site_a_nat post iifname wg0 oifname lan0 tcp dport 443 ip saddr 10.66.0.10 ip daddr 10.10.7.20 snat to 10.10.7.2
Sau đó chính sách forward của site A phải cho phép luồng đã dịch đi từ giao diện đường hầm sang giao diện LAN, cộng với chiều trả về. Hook forward thấy đích sau khi dịch đích và trước khi viết lại nguồn, đó là lý do quy tắc nêu tên địa chỉ của ứng dụng thật. Hãy đọc hai hook đó theo thứ tự ấy mỗi khi một quy tắc có vẻ không khớp với gì. Một quy tắc forward viết ngược với alias không bao giờ khớp được, vì alias không còn tồn tại ở hook đó.
Site B lặp lại điều này với alias riêng và khóa riêng. Nó không được dùng lại khóa riêng của site A, vì hai peer chia sẻ một danh tính là một lỗi cấu hình khó thấy và dễ tạo ra bằng cách sao chép một tệp. Hãy giữ hai tài liệu cổng giống hệt nhau trừ những giá trị bắt buộc phải khác, để sự khác biệt nổi bật khi rà soát. Một tệp được sao chép là nguồn gốc thường gặp của lỗi đó.
Việc viết lại nguồn có một cái giá. Ứng dụng thấy cổng là máy khách của mình, nên nhật ký của nó không còn cho biết người dùng nào đã kết nối. Hãy giữ dấu vết kiểm toán của cổng, và đừng làm yếu đăng nhập ứng dụng để bù cho một danh tính mạng mà phép dịch đã loại bỏ.
Cách chứng minh cả hai tên đã công bố đều trả lời
Cấp cho mỗi alias một tên DNS và cấu hình ứng dụng cùng chứng chỉ cho tên đó, vì phép dịch không viết lại chuyển hướng hay URL tuyệt đối được nhúng. Dùng một tên cho mỗi site để sự không khớp chứng chỉ không bao giờ bị nhầm thành lỗi định tuyến.
Kiểm tra từ máy khách thật, xác nhận site nào đã trả lời, và kiểm tra cả một cổng không được phép. Dùng kết nối mới sau mọi thay đổi NAT, vì trạng thái đã thiết lập giữ ánh xạ cũ:
wg show wg0 allowed-ips
sudo nft list ruleset
curl -sS -o /dev/null -w "%{http_code}\n" https://site-a.example.net/
Một alias trống hoặc bất ngờ trong lệnh đầu tiên nghĩa là việc chọn peer sai trước cả khi bất kỳ quy tắc tường lửa nào có ý nghĩa. Nếu alias đúng mà tên vẫn hỏng, vấn đề đã chuyển sang DNS và chứng chỉ. Khi cả hai tên đều trả lời và cổng thứ ba vẫn đóng, thiết kế làm đúng điều nó tuyên bố: hai dịch vụ được công bố hẹp, không phải hai LAN hợp nhất.