Tệp thông tin xác thực hay ổ cắm quản lý của OpenVPN: nên viết kịch bản cho cái nào
auth-user-pass bao phủ lời nhắc tài khoản; ổ cắm quản lý bao phủ cả bốn. So sánh mức phơi bày, chi phí kịch bản và cách mỗi loại thất bại trong một dịch vụ.
Một kịch bản khởi chạy OpenVPN mà không bao giờ kết nối thường không phải sự cố mạng: ứng dụng đang ngồi trước một lời nhắc mà không có thiết bị đầu cuối để vẽ nó lên. OpenVPN có đúng hai cách được hỗ trợ để trả lời lời nhắc đó mà không cần người, một tệp thông tin xác thực phía sau auth-user-pass và ổ cắm quản lý, và chúng không thể thay thế nhau. Tệp tốn một chỉ thị và trả lời một lời nhắc; ổ cắm trả lời mọi lời nhắc và trao cho bất cứ thứ gì mở được cổng loopback một kênh điều khiển không xác thực. Cái nào phù hợp với triển khai của bạn phụ thuộc vào việc lời nhắc nào thực sự đang chặn, và bạn có thể để yên bí mật nào.
Đặt tên cho lời nhắc trước khi chọn cơ chế
Bốn lời nhắc trông giống nhau trên thiết bị đầu cuối. Cặp tài khoản đến từ cấu hình PAM hoặc auth-user-pass-verify của máy chủ và được auth-user-pass trả lời. Cụm mật khẩu của khóa riêng thuộc về tệp khóa và được đọc qua --askpass hoặc gõ tay. Gói PKCS#12 hỏi mật khẩu riêng của nó. Xác thực proxy dùng tệp auth mà --http-proxy nhận, không phải thông tin xác thực tài khoản.
Khi OpenVPN chạy như con của systemd, PHP hoặc Node thì không có thiết bị đầu cuối, nên bất kỳ lời nhắc nào trong số này đều chặn một cách im lặng. Tiến trình ngồi đó, nhật ký dừng sau dòng mật mã và đơn vị cuối cùng hết thời gian chờ; sự im lặng đó là lý do người ta thêm vòng thử lại hoặc sleep thay vì xem lời nhắc nào đang chờ. Chỉ ổ cắm quản lý mới trả lời được cả bốn. Tệp thông tin xác thực trả lời cái đầu tiên và không gì khác.
auth-user-pass: một tệp, một lời nhắc
Tệp giữ tên người dùng ở dòng một và mật khẩu ở dòng hai, và không gì khác: không dấu ngoặc kép, không tên khóa, không chú thích. Đặt chế độ 600 và đặt chủ sở hữu là tài khoản chạy openvpn, mà với một dịch vụ thường là người dùng riêng chứ không phải đăng nhập của bạn. Tệp thuộc sở hữu root dưới /root là khác biệt giữa một lệnh chạy được trong shell của bạn và một đơn vị thất bại khi khởi động.
Hai chi tiết biến một tệp đúng thành lời nhắc. Trình soạn thảo lưu CRLF để lại ký tự xuống dòng trơ gắn vào mật khẩu, nên máy chủ từ chối mật khẩu trông đúng trong tệp; hãy kiểm tra bằng cat -A hoặc od -c thay vì bằng mắt. Và auth-user-pass không có đối số bên trong hồ sơ theo thiết kế nghĩa là «hỏi người dùng», điều mà chỉ thị thứ hai trên dòng lệnh có thể không ghi đè được, vì hồ sơ được phân tích ngay tại nơi nó nằm.
Thứ tự tùy chọn là cái bẫy thứ ba, và nó phụ thuộc phiên bản. Một số bản dựng OpenVPN 2.4 bỏ qua --auth-user-pass khi nó xuất hiện trước --config, nên cùng hai tùy chọn ở thứ tự kia vẫn kết nối. Đặt chỉ thị trong hồ sơ, hoặc sau --config trên dòng lệnh, và grep hồ sơ cho mọi lần xuất hiện trước khi thay đổi bất cứ điều gì khác.
auth-nocache thuộc cơ chế này nhưng không cung cấp gì. Nó giới hạn thời gian ứng dụng giữ thông tin xác thực trong bộ nhớ, điều này giúp sau một sự cố và không giúp gì khi thiếu tệp. Kết nối lại sau khi phiên phía máy chủ hết thời gian sẽ đọc lại tệp, nên thông tin xác thực từng chạy khi khởi động vẫn có thể hết hạn giữa ngày.
Ổ cắm quản lý: mọi lời nhắc, nhiều bề mặt hơn
Khởi chạy ứng dụng với một bộ lắng nghe và tuyên bố rõ rằng các lời nhắc sẽ được báo cáo trên đó, dùng các chỉ thị được ghi trong hướng dẫn OpenVPN 2.6:
management 127.0.0.1 3030
management-query-passwords
Từ bash, giữ kết nối trên một bộ mô tả tệp dự phòng và trả lời theo tên lời nhắc thay vì vị trí, vì kết nối lại có thể hỏi theo thứ tự khác:
exec 3<>/dev/tcp/127.0.0.1/3030
while read -r -u 3 line; do
case "$line" in
*"Need 'Auth' username/password"*)
echo 'username "Auth" alice' >&3
echo 'password "Auth" secret' >&3 ;;
*"Need 'Private Key' password"*)
echo 'password "Private Key" secret' >&3 ;;
esac
done
Ranh giới là vận chuyển. Giao thức quản lý là văn bản thuần trên TCP trong loopback, nên bộ lắng nghe không được công bố trên địa chỉ LAN hay đường hầm để bộ điều khiển từ xa tiếp cận; bộ điều khiển từ xa nghĩa là chuyển tiếp SSH, không phải liên kết rộng hơn. Daemon cộng đồng cũng chỉ nhận một ứng dụng quản lý tại một thời điểm, nên GUI hay màn hình giám sát không thể chia sẻ ổ cắm với kịch bản của bạn. Kết nối thứ hai bị từ chối, không xếp hàng, và lỗi trông như một kịch bản bị điếc.
Ổ cắm đó cũng là một kênh điều khiển. Không có mật khẩu quản lý, bất cứ thứ gì kết nối được tới 127.0.0.1:3030 đều có thể đọc trạng thái, giết đường hầm hoặc tự trả lời một lời nhắc. Mật khẩu quản lý và tệp thông tin xác thực là bí mật cùng loại, và cả hai đều đáng được chăm sóc như nhau.
Cái nào phù hợp với triển khai
Chọn tệp thông tin xác thực khi lời nhắc duy nhất là cặp tài khoản, ứng dụng chạy như dịch vụ với chủ sở hữu ổn định, và bạn có thể giữ một tệp 600 ngoài các bản sao lưu bạn không kiểm soát. Chọn ổ cắm quản lý khi có cụm mật khẩu khóa hoặc mật khẩu PKCS#12, khi lớp bao đã giám sát tiến trình, hoặc khi bạn cần theo dõi trạng thái và lời nhắc trong lúc gỡ lỗi kết nối lại.
Hai điều kiện dịch chuyển quyết định. Nếu lớp bao là /bin/sh, một đơn vị systemd gọi trực tiếp tệp nhị phân, hoặc một vùng chứa tối giản, thì keo ổ cắm chỉ cho bash không có sẵn và tệp là câu trả lời rẻ hơn. Nếu bí mật xoay vòng thường xuyên, không tệp nào là nhà tốt cho nó: hãy hỏi vòng khóa từ một lớp bao nhỏ và để đơn vị gọi lớp bao đó, điều này giữ việc xoay vòng ở một chỗ và mật khẩu ra khỏi lịch sử shell.
Thay thế tiến trình nằm giữa hai cách và không thuộc về cách nào. Dòng lệnh một dòng bên dưới hoạt động vì bash mở rộng nó trước khi openvpn khởi động, không phải vì ứng dụng hiểu cú pháp:
openvpn --config client.ovpn --auth-user-pass <(printf "%s
%s
" "$USER" "$PASS")
Nó hỏng dưới /bin/sh, trong một đơn vị bỏ qua shell và trong các ảnh không có bash hay /dev/fd. Hãy giữ nó cho thử nghiệm tương tác, không bao giờ là hợp đồng mà một dịch vụ phụ thuộc vào.
Bảo vệ bí mật, rồi xác minh từ nhật ký
Cả hai cơ chế đều để lại một bí mật mà tiến trình OpenVPN đọc được. Đừng bao giờ đặt mật khẩu trên dòng lệnh hoặc trong tệp đơn vị, nơi ps và systemctl hiển thị nó cho mọi người dùng cục bộ; hãy truyền đường dẫn tới tệp, không phải giá trị bên trong. Ở nơi chứng chỉ có thể thay thế cặp tài khoản, điều đó loại bỏ loại lời nhắc thay vì che giấu nó.
Một lần chạy đúng cho thấy chuỗi lời nhắc hoàn tất và phiên đạt «Initialization Sequence Completed». Nếu ứng dụng dừng ở cùng lời nhắc, việc khớp đã thất bại: in ra đúng những dòng mà ổ cắm đã gửi, kiểm tra dấu ngoặc kép và tên lời nhắc, và sửa mẫu thay vì thêm sleep. Một đường hầm báo thành công mà không mang gì là vấn đề định tuyến hoặc push, không phải thông tin xác thực.
Sau đó hãy thử kết nối lại, không chỉ lần khởi động đầu. Cắt phiên phía máy chủ hoặc chờ hết thời gian của nó; ứng dụng hỏi lại, và chính ở đó một tệp từng trông đúng khi khởi động cho thấy nó còn đọc được bởi tài khoản kết nối lại hay không.