Script móc của OpenVPN có thể dùng đường hầm vừa tạo không?
OpenVPN chạy các móc bên trong quá trình khởi động của chính nó, nên một script cần đường hầm phải chờ một đường hầm đang chờ chính nó. Hãy tách công việc ra và chọn móc theo sự kiện.
Việc một script móc có tới được phía bên kia đường hầm hay không phụ thuộc vào ba điều kiện: nó gắn với sự kiện nào, OpenVPN còn nằm trong chuỗi khởi động của mình khi script chạy hay không, và công việc đã được tách khỏi chuỗi đó hay chưa. Hầu hết script thất bại ở điều kiện thứ ba. Thêm một giấc ngủ xử lý vấn đề vòng đời như vấn đề thời điểm, và nó chỉ hiệu quả vừa đủ để gây nhầm lẫn.
Trước hết hãy quyết định sự kiện, rồi cơ chế hoãn công việc, rồi nơi công việc đã hoãn chạy. Ba quyết định đó độc lập với nhau, và trộn lẫn chúng là cách một móc hai dòng biến thành cả một buổi chiều.
Vì sao móc phải chờ một đường hầm đang chờ nó
OpenVPN gọi các móc của mình một cách đồng bộ, bên trong cùng tiến trình di chuyển các gói. up kích hoạt khi thiết bị tun hoặc tap đã tồn tại nhưng trước khi thêm bất kỳ tuyến nào, còn route-up kích hoạt sau các tuyến; không thời điểm nào trong hai thời điểm đó hứa rằng phía bên kia sẽ trả lời. Chừng nào script của bất kỳ móc nào còn chạy, daemon không chuyển tiếp, nên một lệnh mong lưu lượng đi qua đường hầm sẽ chờ một đường hầm đang chờ chính lệnh đó. Sự chờ vòng tròn này là lý do một lần chạy thủ công thành công còn móc thì hết thời gian.
Bản vá hiển nhiên, ngủ vài giây, đôi khi hiệu quả vì đầu xa tự dựng lên, chứ không phải vì giấc ngủ sửa được điều gì. Một trợ lý hỏi về mức sẵn sàng là câu trả lời khác: móc trả về, việc chuyển tiếp bắt đầu, và trợ lý có được thứ nó cần khi thứ đó thực sự ở đó.
Chọn móc theo sự kiện, không theo sự tiện lợi
Gắn up với công việc chỉ cần thiết bị, route-up với công việc cần tuyến, và để dành client-connect cùng client-disconnect cho các tác vụ theo từng máy khách ở phía máy chủ. Việc dọn dẹp khi ngắt kết nối phải chịu được chạy hai lần, vì kết nối lại qua một giao diện thường trực không theo cùng chuỗi như một lần khởi động lại đầy đủ. Trên máy chủ, client-connect kích hoạt khi máy khách xác thực chứ không phải khi daemon khởi động, điều này đáng xác nhận trước khi chuyển logic tới đó. Nếu công việc thuộc vòng đời của dịch vụ chứ không thuộc một kết nối, một đơn vị systemd là chỗ ở tốt hơn bất kỳ móc nào.
Các lệnh móc do daemon thực thi trực tiếp, không có shell ở giữa, nên ifconfig_remote trong dòng up đến như văn bản nguyên văn. Hãy bọc lệnh trong một shell rõ ràng, hoặc giữ logic trong một script đọc môi trường. ipchange là một chỉ thị thêm đối số của riêng nó: nó trao địa chỉ và cổng cho lệnh, nên một script viết cho không ai thấy hai đối số không ngờ.
Ba cách tách công việc
Lựa chọn đầu tiên là đưa trợ lý ra nền từ bên trong móc: khởi chạy nó bằng setsid hoặc systemd-run --scope, đóng các bộ mô tả kế thừa, và thoát ngay với số không. Cách thứ hai là để móc ghi một tệp trạng thái nhỏ và để bộ hẹn giờ systemd hoặc một đơn vị đường dẫn hành động trên đó, điều này giữ móc đơn giản và đặt chính sách thử lại ở nơi có nhật ký. Cách thứ ba là bỏ hẳn móc và chuyển công việc sang một đơn vị được sắp sau dịch vụ VPN, đây là câu trả lời đúng khi công việc hoàn toàn không theo kết nối. Cả ba chia sẻ một quy tắc: móc phải trả về trước khi nó cần đường hầm.
Dù chọn cách nào, hãy làm trợ lý có tính lũy đẳng và quan sát được. Hai lần gọi chồng lấn không được thực hiện cùng một hành động không thể đảo ngược hai lần, và một trợ lý thất bại phải để lại thứ gì đó con người đọc được.
Tách lỗi sandbox khỏi lỗi vòng đời
Khi một script chạy từ dấu nhắc root và thất bại với tư cách dịch vụ, nguyên nhân thường là sandbox chứ không phải mạng. Một ProtectHome hạn chế, một ProtectSystem chỉ đọc, hoặc một thư mục làm việc không tồn tại có thể khiến script hoặc dữ liệu của nó không thể tiếp cận, và trên một máy chủ SELinux tệp cũng cần ngữ cảnh cho phép thực thi. Đọc đơn vị bằng systemctl cat openvpn-server@server và nhật ký bằng journalctl -u openvpn-server@server -n 80, rồi tìm lỗi quyền hoặc ngữ cảnh thay vì thời gian chờ kết nối. Vô hiệu hóa sandbox nên là bước cuối cùng, không phải bước đầu tiên.
Lỗi vòng đời trông khác: móc khởi động, trợ lý chạy, và ổ cắm đơn giản là chưa có ở đó. Hãy ghi một dòng có dấu thời gian từ chính móc, ngắt kết nối rồi kết nối lại, và đọc các sự kiện theo thứ tự. Khi trợ lý thực sự cần đường hầm, hãy để nó hỏi về mức sẵn sàng thay vì ngủ một khoảng cố định.
Ba trường khiến báo cáo tiếp theo hữu ích: móc nào đã chạy, dùng tài khoản nào, và dừng ở đâu. Không có chúng, mọi cuộc điều tra sau đó đều bắt đầu lại từ đầu.