Dịch vụ khởi động, đọc hồ sơ mà không phàn nàn gì rồi chết ở một dòng trông chẳng liên quan: nó không mở được ca.crt. Không có gì thay đổi ở chứng chỉ, và đường dẫn hồ sơ trong tệp unit là tuyệt đối. Chính sự kết hợp đó là manh mối. Đường dẫn bạn đưa chỉ định vị một tệp, còn các tên bên trong tệp đó được phân giải ở một nơi hoàn toàn khác, nên một lần khởi chạy trông đầy đủ điều kiện vẫn có thể đang đọc khóa của nó từ một thư mục mà không ai định trước.

Lỗi này có ít nguyên nhân, và nên tách chúng theo thứ tự: thư mục cơ sở mà tiến trình thực sự có, bối cảnh khởi chạy của dịch vụ, và quyền hoặc sandbox quanh tệp khi đường dẫn đã đúng. Mỗi nguyên nhân để lại một dấu vết khác nhau — trong unit, trong tiến trình và trên hệ thống tệp.

Đường dẫn hồ sơ không phải thư mục cơ sở

Hãy hình dung một tiến trình bắt đầu trong /tmp và được trao /etc/vpn/prod/tenant-a.ovpn. Bên trong hồ sơ đó, dòng ca ca.crt nêu một đường dẫn tương đối. Không có gì trong đối số đã định vị hồ sơ ghi lại tên đó, nên tiến trình tìm /tmp/ca.crt và báo thiếu tệp.

Điều gây bối rối là thứ tự các lỗi. OpenVPN phân tích cấu hình trước và mở các tệp được tham chiếu khi gặp chúng, nên một lỗi đánh máy trong hồ sơ được báo trước khi bất kỳ khóa nào bị chạm vào, trong khi chứng chỉ thiếu chỉ xuất hiện khi việc phân tích tới được chỗ đó. Vì vậy một hồ sơ tải được là bằng chứng cú pháp đúng, không phải bằng chứng rằng các tệp nó nêu đã được tìm thấy.

Sổ tay phân biệt điều gì: --config và --cd

Sổ tay OpenVPN 2.6 mô tả hai tùy chọn khác nhau. Một tùy chọn tải tệp cấu hình. Tùy chọn kia đổi thư mục của tiến trình trước khi cấu hình, khóa, script và các tệp khác được đọc, và yêu cầu một thư mục tuyệt đối không tham chiếu tới thư mục hiện tại.

openvpn --cd /etc/vpn/prod --config tenant-a.ovpn

Lệnh gọi đó là một dạng, không phải chỉ dẫn khởi chạy hồ sơ của người khác. Điều nó mang lại là tính xác định: cơ sở cho mọi tên tương đối trong chuỗi được nêu trong lệnh thay vì thừa hưởng từ bất kỳ shell hay trình quản lý nào đã khởi động tiến trình. Phương án khác cũng hợp lệ và thường tốt hơn cho một dịch vụ: để yên thư mục làm việc và biến mọi tệp được tham chiếu thành tuyệt đối bên trong hồ sơ.

Dù chọn cách nào, hãy rà soát toàn bộ chuỗi chứ không chỉ lỗi đầu tiên. Một hồ sơ có thể kéo vào tệp cấu hình khác, và các tên tương đối của tệp được đưa vào được phân giải theo cùng thư mục tiến trình, không phải theo thư mục của tệp đã đưa chúng vào. Đường dẫn nhật ký, tệp xác thực và script phụ đều theo cùng quy tắc.

Dịch vụ khởi động ở nơi khác

Một shell tương tác trong thư mục hồ sơ không chứng minh gì về một unit đã đóng gói. Các trình quản lý đặt thư mục làm việc riêng, và một script bọc có thể đổi lại nó trước khi tệp nhị phân chạy. Unit thất bại là lần khởi chạy duy nhất đáng để suy xét.

systemctl show -p WorkingDirectory -p ExecStart --value openvpn@tenant-a
ls -l /proc/$(pgrep -n openvpn)/cwd

Lệnh thứ hai cho thấy thư mục mà tiến trình đang sống thực sự giữ, và điều đó đáng tin hơn tệp unit khi có lớp bọc. Nếu hai bên không khớp, lớp bọc là câu trả lời và unit là dấu vết đánh lừa.

Sau khi tra cứu: quyền, chroot và sandbox

Một thư mục cơ sở đúng vẫn có thể cho ra cùng thông báo, vì phân giải và truy cập là hai bước riêng. Tiến trình cần quyền thực thi trên mọi thư mục cha và quyền đọc trên tệp, và một unit dịch vụ che giấu một phần hệ thống tệp sẽ thay đổi những gì tiến trình có thể thấy.

Ba biến thể phổ biến cho ra cùng một lỗi. Tệp chỉ cho root đọc trong khi dịch vụ từ bỏ quyền trước khi mở nó. Unit đặt một khung nhìn bảo vệ hệ thống tệp không bao gồm thư mục hồ sơ. Daemon chạy với chroot, nên đường dẫn tồn tại trên máy chủ không tồn tại bên trong khung nhìn đó.

Chẩn đoán bằng quyền sở hữu tệp và danh tính tiến trình, không phải bằng cách nới lỏng quyền. Làm cho khóa riêng ai cũng đọc được để dập lỗi này là đổi một thất bại ồn ào lấy một thất bại im lặng, và khóa chính là phần của thư mục mà bạn ít muốn phơi ra nhất.

Chứng minh bản sửa bằng chính lần khởi chạy của bạn

Khởi động lại unit thật từ một thư mục không liên quan tới hồ sơ và theo dõi đúng dòng khởi động đó. Sau đó xác nhận tiến trình đang giữ thư mục dự định trong khi chạy, và chứng chỉ nó tải là chứng chỉ bạn mong đợi.

systemctl restart openvpn@tenant-a
systemctl status openvpn@tenant-a --no-pager
journalctl -u openvpn@tenant-a -n 20 --no-pager

Khi việc tải đường dẫn thành công, xác thực và định tuyến vẫn là các giai đoạn riêng với thất bại riêng, và một kết nối lên được nhưng không cho lưu lượng đi qua là một cuộc điều tra khác. Bản sửa ở đây chỉ liên quan tới việc các tệp đến từ thư mục nào, và nó nên hiện ra trong lệnh gọi thay vì được người chạy tay lần cuối ghi nhớ.

Nếu hồ sơ được triển khai bởi một trình quản lý cấu hình, hãy đặt thư mục cơ sở hoặc tên tệp tuyệt đối ở đó luôn. Một bản sửa tay trên một máy chủ là phiên bản của vấn đề này sẽ quay lại sau lần dựng lại tiếp theo.

Bài viết liên quan