Dịch vụ VPN thất bại tại EXEC: so sánh unit, tệp nhị phân và sudoers
Thất bại ở bước EXEC nghĩa là systemd không thể khởi động tệp nhị phân. Hãy so sánh đường dẫn trong unit, đường dẫn đã cài đặt và quyền trong sudoers trước khi thay đổi bất cứ điều gì.
Hôm qua đường hầm vẫn hoạt động, mà sáng nay unit đã chết với Failed at step EXEC spawning /usr/local/bin/vpnclient trong journal. Thông tin xác thực, tuyến đường và tài khoản không bao giờ xuất hiện trong câu đó. systemd đã phân giải ExecStart thành một đường dẫn, yêu cầu kernel thực thi nó, và kernel từ chối, thường là vì không có gì tồn tại ở đường dẫn đó hoặc tệp không thể thực thi. Hãy coi lỗi này là vấn đề tệp và argv cho đến khi lệnh đã phân tích chứng minh ngược lại.
Vì sao EXEC thất bại trước khi quá trình xác thực bắt đầu
systemd không chạy ExecStart qua một shell. Nó fork, dựng một vectơ đối số từ dòng lệnh và gọi execve trên phần tử đầu tiên. Nếu lời gọi đó thất bại, dịch vụ không bao giờ chạm tới mã riêng của ứng dụng khách VPN, nên việc sửa hồ sơ hay kiểm tra thông tin xác thực đều không giúp được gì.
Mã trạng thái tách nhóm lỗi này khỏi các sự cố thông thường. Một unit thoát với status=203/EXEC chưa bao giờ khởi động tiến trình; một unit khởi động rồi thoát với status=1 có thể đã thất bại ở bước xác thực, DNS hoặc định tuyến. Hãy đọc mã trước khi đọc nhật ký của ứng dụng khách, vì trong trường hợp đầu nhật ký của ứng dụng khách sẽ trống.
Đường dẫn trong journal là đường dẫn systemd đã dùng, không nhất thiết là đường dẫn bạn đã sửa. Các tệp drop-in và đầu ra của trình tạo có thể ghi đè tệp của nhà cung cấp, và một daemon-reload cũ để lại dòng cũ trong bộ nhớ. Vì vậy chẩn đoán đầu tiên là hỏi systemd xem nó coi lệnh là gì, thay vì mở tệp mà bạn nhớ đã thay đổi.
Ba nơi ghi lại đường dẫn của tệp nhị phân
Một ứng dụng khách VPN được cài từ gói phân phối, tập lệnh của nhà cung cấp và tarball bản phát hành có thể rơi vào ba tiền tố khác nhau. Unit, quy tắc đặc quyền và trình cài đặt mỗi bên đã ghi lại một trong các tiền tố đó, và lỗi xuất hiện khi hai trong ba không khớp.
| Record | Common value | How to read it |
|---|---|---|
| Unit or drop-in | /usr/local/bin/vpnclient | systemctl cat vpnclient.service |
| Installed binary | /usr/bin/vpnclient | command -v vpnclient, then ls -l the result |
| Sudoers grant | /opt/vpnclient/bin/vpnclient | sudo grep -r vpnclient /etc/sudoers.d/ |
Một sự không khớp cụ thể trông như sau: tập lệnh của nhà cung cấp cài vào /usr/local/bin, tarball được giải nén dưới /opt, và unit vẫn nêu đường dẫn cũ /usr/bin từ gói trước đó. Chỉ một trong các tệp đó tồn tại, và hiếm khi đó là tệp trong ExecStart.
Hãy quyết định đường dẫn nào là chuẩn trước khi thay đổi bất kỳ tệp nào. Nếu gói phân phối sở hữu tệp nhị phân, đường dẫn của gói là chuẩn và unit phải theo nó. Nếu tập lệnh của nhà cung cấp sở hữu, quy tắc tương tự áp dụng cho tiền tố của nhà cung cấp. Sao chép tệp nhị phân giữa các tiền tố tạo ra hai bản sao sẽ tách nhau ở lần nâng cấp tiếp theo.
Khi một lệnh trong dấu ngoặc kép trở thành tên tệp
Một kiểu lỗi thứ hai ẩn sau cùng dòng journal đó. Ai đó ghi điều này vào unit và mong ứng dụng khách kết nối trong lúc khởi động:
ExecStart=/usr/bin/"vpnclient connect office"systemd chia giá trị thành các từ, nên từ đầu tiên trở thành /usr/bin/vpnclient connect office, với dấu ngoặc kép bị bỏ đi như cú pháp nhóm. Sau đó kernel tìm một tệp có tên chứa dấu cách, và không có tệp nào như vậy. Lỗi giống hệt trường hợp thiếu tệp nhị phân dù tệp đã được cài.
Dạng được hỗ trợ là một tệp thực thi thật sự, theo sau là các đối số riêng biệt, ví dụ ExecStart=/usr/bin/vpnclient connect office. Nếu quy trình thực sự cần một đường ống hoặc một chuỗi, hãy đặt nó vào một tập lệnh và gọi tập lệnh, vì ExecStart không có &&, không có đường ống và không mở rộng glob.
Sửa unit, không phải hệ thống tệp
Cách sửa hấp dẫn là một liên kết tượng trưng từ đường dẫn mong đợi tới tệp nhị phân thật. Nó hoạt động cho tới khi gói được cập nhật và liên kết trỏ tới một tệp đã bị xóa, hoặc cho tới khi nhãn SELinux không còn khớp với mục tiêu. Nên sửa unit qua một drop-in để tệp của nhà cung cấp vẫn nguyên vẹn và các lần nâng cấp vẫn dự đoán được.
Quy tắc đặc quyền phải khớp với cùng đường dẫn và cùng các lệnh con. Một mục sudoers cho /usr/local/bin/vpnclient không cấp quyền cho /usr/bin/vpnclient, và một mục cho phép mọi đối số sẽ cho phép bất kỳ tiến trình cục bộ nào cấu hình lại hoặc dừng đường hầm. Hãy cấp hai động từ mà unit cần và giữ việc tháo gỡ ra ngoài nếu việc dừng dịch vụ đã đóng giao diện.
Đây là chỗ việc so sánh trả công: unit, cơ sở dữ liệu gói và sudoers đều phải nêu một đường dẫn. Khi chúng làm vậy, lỗi EXEC biến mất mà không cần khởi động lại và không cần một bản sao tệp nhị phân ở nơi thứ ba.
Chứng minh lệnh đã phân tích trước khi khởi động lại
sudo systemctl daemon-reload
sudo systemctl cat vpnclient.service
systemctl show -p ExecStart vpnclient.service
systemd-analyze verify /etc/systemd/system/vpnclient.service
sudo systemctl restart vpnclient.service
systemctl status vpnclient.service --no-pagersystemctl show là đầu ra quyết định vì nó in ra argv mà systemd thực sự phân tích, bao gồm mọi drop-in. systemd-analyze verify bắt được đường dẫn thiếu hoặc không thể thực thi trước khi bạn khởi động lại, và lệnh status sau khi khởi động lại cho biết tiến trình hiện có tồn tại hay không.
Nếu unit khởi động rồi thất bại, bạn đã rời khỏi nhóm EXEC và bước vào phần xử lý lỗi riêng của ứng dụng khách. Hãy tìm trong journal thông điệp của ứng dụng khách, không phải một thất bại execve khác. Giữ diff của unit và diff của sudoers cùng nhau trong bản ghi thay đổi, để lần nâng cấp tới không âm thầm đưa đường dẫn bị tách trở lại.