remote.Tài liệuTrang chủ

Tổng quan về kỹ thuật

Tài liệu này mô tả các cơ chế cốt lõi trong mã hiện hành. Các trường thông báo hoàn chỉnh, định dạng khung và trình tự bắt tay đều có trong giao thức Wire.

Cả người vận hành và thiết bị đều kết nối ra ngoài với exchange

Các thiết bị gia đình và văn phòng chi nhánh thường nằm phía sau NAT, vì vậy exchange không thể quay số vào chúng. Cả hai bên bắt đầu kết nối WebSocket với exchange:

  • Các thiết bị kết nối với wss://<主机>/ws/device (ws:// để gỡ lỗi văn bản gốc), đăng ký bằng mã thông báo thiết bị trong khung đầu tiên.
  • Người vận hành kết nối với wss://<主机>/ws/operator bằng cách sử dụng mã thông báo của người vận hành để bắt tay.

exchange xử lý các quá trình xác thực, định tuyến và kiểm tra. --listen hoặc REMOTE_LISTEN xác định địa chỉ nghe của nó; proxy ngược ở phía trước chấm dứt TLS.

Hai không gian ID phiên

Người vận hành và thiết bị duy trì ID phiên độc lập. Người vận hành có thể đưa ID của chính họ vào các yêu cầu mà không cần chờ phân bổ máy chủ trước khi gửi các khung tiếp theo. ID phía thiết bị được phân bổ độc lập cho từng kết nối thiết bị hiện tại, tăng dần từ 1. Ngay cả khi hai nhà khai thác đều sử dụng ID cục bộ 1, họ vẫn nhận được các ID khác nhau trên thiết bị.

ID 0 được dành riêng cho các tin nhắn ở cấp độ kết nối. Đăng ký không thành công nếu không chèn tuyến đường nếu ID phía nhà điều hành là 0 hoặc đã được sử dụng trên kết nối của nhà điều hành đó.

Bảng dịch có dạng crates/exchange/src/hub/session_table.rs. hub.rs đính kèm bản dịch vào các kết nối riêng lẻ: ID thiết bị → ID nhà điều hành ở phía thiết bị và ID nhà điều hành → (kết nối thiết bị, ID thiết bị) ở phía nhà điều hành.

Chuyển tiếp chỉ ghi lại ID phiên

Các byte PTY và lệnh stdout/stderr sử dụng khung nhị phân. Tiêu đề chín byte chứa loại luồng một byte, session_id bốn byte và độ dài tải trọng bốn byte, theo sau là tải trọng thô. Định nghĩa của nó là trong crates/proto/src/frame.rs.

exchange chỉ thay thế session_id khi chuyển tiếp:

  • Người vận hành đối với thiết bị: crates/exchange/src/ws/operator.rs sử dụng DataFrame::with_session_id để thay thế ID phía thiết bị, sau đó đặt các byte được mã hóa vào hàng đợi gửi của thiết bị.
  • Thiết bị tới nhà điều hành: handle_data trong crates/exchange/src/ws/device.rs thay thế ID phía thiết bị bằng ID gốc của nhà điều hành. Nhận xét nguồn mô tả việc viết lại này.

Tải trọng đi qua không thay đổi. Định tuyến không kiểm tra tổ hợp phím PTY hoặc đầu ra lệnh; các khung không có phiên phù hợp sẽ bị loại bỏ. Thông báo điều khiển là các khung văn bản JSON. Các phiên mở và đóng cũng chuyển ID sang vùng ID của người nhận trong khi vẫn bảo toàn các lĩnh vực kinh doanh.

Kết nối lại thiết bị thay thế kết nối cũ

Khi một thiết bị kết nối lại, kết nối mới của nó sẽ vào bảng trực tuyến và thay thế bảng cũ mà người gọi sẽ đóng lại. Việc từ chối kết nối mới dưới dạng trùng lặp là không phù hợp: kết nối cũ nửa mở có thể vẫn được đăng ký, buộc thiết bị phải chờ hết thời gian chờ.

Mỗi kết nối thiết bị có epoch (UUID), được tạo trong Hub::register_device. Việc dọn dẹp ngắt kết nối sử dụng Hub::unregister_device, tháo thiết bị và chỉ đánh dấu thiết bị ngoại tuyến khi kỷ nguyên đã đăng ký khớp với kỷ nguyên dọn dẹp. Nếu việc dọn dẹp kết nối cũ diễn ra sau khi đăng ký kết nối mới thì các kỷ nguyên sẽ khác nhau, hàm trả về một danh sách trống và kết nối mới vẫn được đăng ký.

Hàng đợi gửi bị giới hạn; một hàng đợi đầy đủ sẽ đóng kết nối của nó

Mỗi WebSocket có hàng đợi dung lượng 512, được channel triển khai trong crates/exchange/src/hub/outbound.rs. Định tuyến giữ khóa bảng kết nối, do đó việc phân phối chỉ sử dụng Sender::try_send mà không cần đợi thiết bị ngang hàng dưới khóa.

Hàng đợi đầy đủ khiến try_send trả về SendError::Lagging và đặt tín hiệu đóng. Thông báo điều khiển Close bỏ qua hàng đợi và đặt tín hiệu tương tự, do đó, ngay cả hàng đợi bị chặn cũng có thể yêu cầu tắt máy. Cả thiết bị và nhà điều hành đều nhận được các vòng lặp đang chờ sender.closed() bên trong tokio::select! (crates/exchange/src/ws/device.rs, crates/exchange/src/ws/operator.rs). Sau khi được báo hiệu, quá trình chuyển tiếp kết thúc, sau đó hủy đăng ký thiết bị theo thời gian hoặc xóa sạch các phiên của nhà điều hành. Một thiết bị ngang hàng đã đánh rơi bộ thu sẽ tạo ra SendError::Closed, được phân biệt với một thiết bị tiêu dùng không thể theo kịp.

Mã thông báo chứa tiền tố và bí mật; chỉ có hàm băm argon2 của bí mật được lưu trữ

Mã thông báo văn bản gốc có dạng <prefix>.<secret>. split_token trong crates/exchange/src/auth.rs tách ra ở . đầu tiên, từ chối tiền tố trống hoặc bí mật vì không đúng định dạng.

Khi phát hành, tiền tố là 8 byte ngẫu nhiên được mã hóa dưới dạng thập lục phân và bí mật là 32 byte ngẫu nhiên được mã hóa dưới dạng base64url. Cơ sở dữ liệu chỉ lưu trữ hai cột:

  • token_prefix: bản rõ có chỉ mục duy nhất, được sử dụng cho một lần tra cứu trong quá trình xác minh.
  • token_hash: chuỗi PHC argon2id của bí mật, được tạo bởi hash_secret.

Tài khoản thiết bị và nhà điều hành sử dụng định dạng này. Việc xác minh truy xuất hàm băm theo tiền tố, sau đó so sánh bí mật với verify_secret. Bản rõ chỉ được trả lại khi phát hành và không thể khôi phục được từ cơ sở dữ liệu.

argon2 sử dụng 8 MiB bộ nhớ, hai lần lặp và tính song song 1, dưới mức mặc định của thư viện. Bí mật là các giá trị ngẫu nhiên 256-bit; việc băm chậm bổ sung không bổ sung thêm biện pháp bảo vệ cưỡng bức thực tế nào nhưng làm tăng độ trễ bắt tay khi các thiết bị kết nối lại với nhau. Các tham số này chỉ áp dụng cho loại mã thông báo ngẫu nhiên này.

Tài liệu liên quan