Ba điều quyết định nên dùng nguồn thông tin xác thực nào: có người ngồi ở bảng điều khiển hay không, bí mật có thể sống trên đĩa hay không và một tiến trình khác có phải trả lời lời nhắc hay không. Chọn sai thì ứng dụng khách báo lỗi stdin trong khi mật khẩu hoàn toàn hợp lệ, và điều đó khiến phần lớn người dùng tìm đến máy chủ mà không có lý do. Hãy chọn đường dẫn trước; thông báo lỗi chỉ có nghĩa khi bạn biết đường dẫn nào tạo ra chúng.

Ba điều kiện quyết định

Lời nhắc trên bảng điều khiển là đường dẫn duy nhất cần con người, nên cũng là đường dẫn duy nhất âm thầm thất bại trong một unit systemd hoặc một container. Nếu một người khởi chạy ứng dụng khách và gõ mật khẩu, không có tệp nào tham gia và không có gì nhạy cảm rơi xuống đĩa. Nếu ứng dụng khách khởi chạy lúc khởi động máy, chính lời nhắc đó trở thành treo, và thời gian chờ thường hiện ra như lỗi kết nối chung chứ không phải lỗi thông tin xác thực.

Tệp thông tin xác thực là câu trả lời đúng khi tiến trình khởi chạy không có giám sát và bí mật có thể tồn tại trên đĩa dưới một bộ quyền được kiểm soát. Khi bí mật không được ghi ra chút nào - chính sách chỉ dùng kho, token ngắn hạn - bạn cần một luồng mà ứng dụng khách mở được, nghĩa là trên thực tế thay thế tiến trình hoặc một trình phụ ghi vào bộ mô tả tệp. Giao diện quản lý bao trùm trường hợp thứ tư: một bộ điều khiển đã quản lý vòng đời đường hầm và có thể trả lời câu hỏi qua socket.

  • Người ở bảng điều khiển, không tự động hóa: lời nhắc tương tác.
  • Khởi chạy không giám sát, bí mật được lưu: tệp thông tin xác thực với chế độ 0600.
  • Khởi chạy không giám sát, không được lưu bí mật: thay thế tiến trình hoặc fd do tác tử bí mật ghi vào.
  • Một tiến trình khác sở hữu ứng dụng khách: giao diện quản lý.

Khi hai câu trả lời đều phù hợp, hãy chọn câu giữ bí mật ngoài argv và ngoài lịch sử shell. Mật khẩu được truyền làm tham số chương trình có thể bị mọi người dùng cục bộ đọc qua danh sách tiến trình, và việc phơi bày đó rất khó hoàn tác.

Mỗi đường dẫn đầu vào thực sự làm gì

--auth-user-pass một mình sẽ hỏi tên người dùng và mật khẩu ở bảng điều khiển. Khi được cho tên tệp, ứng dụng khách lấy dòng đầu làm tên người dùng và dòng thứ hai làm mật khẩu; không có gì hỗ trợ dấu ngoặc kép, chú thích hay cú pháp khóa-giá trị. Đó là toàn bộ định dạng, nên ai chỉnh sửa nó với kỳ vọng một tệp INI sẽ làm hỏng nó. Khối <auth-user-pass> nội tuyến giữ đúng hai dòng đó trong hồ sơ, khiến bản thân hồ sơ thành bí mật và đặt nó vào cùng lớp xử lý như khóa riêng.

Hai đường còn lại là luồng và socket. OpenVPN chỉ cần một bộ mô tả tệp đọc được, nên nó chấp nhận thay thế tiến trình, một mục /dev/fd được ống hỗ trợ hoặc một tên tệp thực chất là FIFO. Giao diện quản lý khác về bản chất: ứng dụng khách hỏi tiến trình giám sát các giá trị thay vì tự đọc. Sự khác biệt đó quan trọng khi kết nối lại: tệp thông tin xác thực, FIFO mà trình phụ bí mật có thể ghi lại và giao diện quản lý có thể cung cấp lại mà không cần người; thay thế tiến trình thì không, vì bộ mô tả bị tiêu hết ở lần đọc đầu tiên.

Tệp phải có đúng hai dòng

Hai kiểu hỏng bao trùm phần lớn tệp thông tin xác thực bị lỗi. Trình soạn thảo Windows để lại kết thúc CRLF, ký tự xuống dòng trở thành một phần của mật khẩu, và máy chủ từ chối giá trị trông đúng trên màn hình. Dòng thứ hai trống - dòng trống ở chỗ lẽ ra phải có mật khẩu - để ứng dụng khách với mật khẩu trống, và máy chủ từ chối dù giá trị trên màn hình trông đúng. Một dòng mới thừa sau mật khẩu là vô hại. Hãy ghi tệp từ shell, không phải từ trình soạn thảo, và kiểm tra các byte:

install -m 600 /dev/null /etc/openvpn/client.creds
printf '%s\n%s\n' "$VPN_USER" "$VPN_PASS" > /etc/openvpn/client.creds
od -c /etc/openvpn/client.creds | head -3

Đường dẫn quan trọng như nội dung. Đường dẫn tương đối được phân giải theo thư mục làm việc của tiến trình, mà với một dịch vụ thì hiếm khi là thư mục bạn đã kiểm thử. Dùng đường dẫn tuyệt đối, đặt tệp thuộc sở hữu của tài khoản chạy ứng dụng khách và giữ chế độ 0600 để nhóm và người khác không đọc được. Hướng dẫn OpenVPN 2.6 liệt kê các dạng được chấp nhận của chỉ thị; hãy xem trước khi tự nghĩ ra dạng thứ ba.

Hãy kiểm tra tệp trước khi đổ lỗi cho máy chủ. Chạy openvpn --config client.ovpn --auth-user-pass /etc/openvpn/client.creds --verb 4 sẽ in ra tệp có được mở hay không và sau đó thông tin xác thực có được chấp nhận hay không, điều này cho biết nên đổi lớp nào tiếp theo.

Luồng, stdin và những lỗi chúng sinh ra

Đưa mật khẩu qua ống là việc đầu tiên phần lớn người ta thử và là đường dẫn nhiều bất ngờ nhất. Thay thế tiến trình hoạt động vì ứng dụng khách chỉ cần một bộ mô tả đọc được: openvpn --config client.ovpn --verb 3 --auth-user-pass <(printf '%s\n%s\n' "$VPN_USER" "$VPN_PASS"). Truyền /dev/stdin chỉ hoạt động khi stdin vẫn mở và đọc được ngay lúc ứng dụng khách hỏi, điều này không đúng trong một đường ống đã tiêu thụ nó.

ERROR: could not read Auth username from stdin có hai nguyên nhân khác nhau. Ở một số ứng dụng khách 2.3.x, thông báo đến từ khiếm khuyết xử lý stdin mà các bản phát hành 2.3 sau đó đã sửa, nên một ứng dụng khách cũ như vậy chỉ cải thiện khi nâng cấp. Ở ứng dụng khách hiện tại, cùng cách diễn đạt nghĩa là ứng dụng khách đã yêu cầu nhập từ bảng điều khiển và stdin không phải terminal - đúng tình huống trong unit systemd, tác vụ cron hoặc container khởi chạy mà không có -t.

auth-nocache thêm vòng thứ ba trông như mật khẩu hỏng. Nó vứt thông tin xác thực khỏi bộ nhớ sau khi dùng, nên lần kết nối lại do thay đổi mạng không có gì để phát lại và lời nhắc quay lại. Đó là chỉ thị hoạt động đúng như thiết kế. auth-nocache thay đổi thứ ứng dụng khách giữ trong bộ nhớ, không phải thứ máy chủ hay nhà cung cấp danh tính nghĩ về tài khoản, nên toàn bộ cách sửa là cung cấp lại chính tệp đó, đọc lại khi khởi động lại - và với một kết nối được quản lý, việc cung cấp đó phải đến từ kho bí mật đã được phê duyệt, không phải mật khẩu trong script.

Giao diện quản lý khi lời nhắc thuộc về tiến trình khác

Bộ điều khiển đã giám sát đường hầm cũng có thể trả lời các câu hỏi của nó. Khởi chạy ứng dụng khách với --management 127.0.0.1 7505, đọc các dòng trạng thái và theo dõi >PASSWORD:Need 'Auth' username/password. Bộ điều khiển trả lời bằng username "Auth" alice rồi password "Auth" ... trên các dòng lệnh riêng. Token 'Auth' lặp lại định danh từ yêu cầu và phải khớp, nếu không câu trả lời bị bỏ qua và ứng dụng khách chờ.

Cái giá là một máy trạng thái giờ thuộc về bạn: thứ tự kết nối, khởi động lại ứng dụng khách và một socket cục bộ mà người dùng khác trên máy không được chạm tới. Thứ bạn mua là thông tin xác thực được cung cấp lúc chạy, không có tệp trên đĩa và không có lời nhắc có thể treo. Nếu không có gì khác trong ngăn xếp của bạn đã nói giao thức này, đường dẫn này thêm một thành phần phải bảo trì.

Những thất bại trông như vấn đề thông tin xác thực

Một cuộc bắt tay TLS không bao giờ hoàn tất, hoặc VERIFY ERROR: depth=0, error=certificate signature failure, là vấn đề tin cậy và xảy ra trước khi bất kỳ tên người dùng nào được đọc. Ứng dụng khách proto tcp trỏ tới một trình lắng nghe UDP cũng thất bại ở đúng chỗ đó: các gói tin không bao giờ tới giai đoạn xác thực, nên mọi mật khẩu đều trông sai do liên tưởng. Giữ bật kiểm tra chứng chỉ trước khi thông tin xác thực được gửi đi; một lời nhắc quen thuộc không nói gì về việc ai nhận mật khẩu. Hãy kiểm tra chuỗi chứng chỉ, tên chung và phương thức truyền trước khi chạm vào thông tin xác thực.

Lỗi shell trong script lắp hồ sơ là dấu vết sai thứ hai. Thiếu một dấu cách bên trong [ ! -f "$file" ] sinh ra command not found, script dừng sớm, hồ sơ không bao giờ được dựng lại và ứng dụng khách khởi chạy với bản trước. Lỗi đó thuộc về shell, không phải xác thực, và gõ lại mật khẩu bao nhiêu lần cũng không xóa được nó.

Không phải mọi lời nhắc trong luồng này đều là thông tin xác thực này. Cụm mật khẩu khóa riêng, đăng nhập proxy HTTP và mật khẩu dịch vụ là ba bí mật khác nhau được hỏi ở các giai đoạn khác nhau, và cụm mật khẩu khóa thường do ứng dụng khách hoặc tác tử xử lý chứ không phải auth-user-pass. Nếu lời nhắc xuất hiện trước khi phiên TLS được thiết lập, bạn đang trả lời sai câu hỏi và không việc đặt lại tài khoản nào giúp được. Khi đã loại trừ lời nhắc sai, các kiểm tra còn lại bắt đầu từ lớp sớm nhất:

  • Đọc lỗi giao thức đầu tiên, không phải dòng thông tin xác thực cuối cùng: TLS, rồi xác thực, rồi push.
  • Xác nhận tệp có hai dòng, không có ký tự xuống dòng kiểu carriage return và được tham chiếu bằng đường dẫn tuyệt đối.
  • Xác nhận hồ sơ và dòng lệnh không mâu thuẫn về chỉ thị.
  • Tách "thông tin xác thực được chấp nhận" khỏi "lưu lượng đang chảy" - định tuyến và DNS là lớp muộn hơn.

Khi ứng dụng khách đến giai đoạn push, hãy ngừng nhìn vào thông tin xác thực và bắt đầu đọc các tuyến và máy chủ DNS mà máy chủ đã gửi. Đó là vấn đề tiếp theo, và nó không liên quan gì đến mật khẩu bạn vừa sửa.

Bài viết liên quan