Xin chào Habr, tên tôi là Ilya, tôi làm việc trong nhóm nền tảng tại Exness. Chúng tôi phát triển và triển khai các thành phần cơ sở hạ tầng cốt lõi mà nhóm phát triển sản phẩm của chúng tôi sử dụng.
Trong bài viết này, tôi muốn chia sẻ kinh nghiệm triển khai công nghệ SNI mã hóa (ESNI) trong cơ sở hạ tầng của các trang web công cộng.

Việc sử dụng công nghệ này sẽ tăng mức độ bảo mật khi làm việc với một trang web công cộng và tuân thủ các tiêu chuẩn bảo mật nội bộ được Công ty áp dụng.
Trước hết, tôi muốn chỉ ra rằng công nghệ này chưa được chuẩn hóa và vẫn đang trong giai đoạn dự thảo, nhưng CloudFlare và Mozilla đã hỗ trợ nó (trong ). Điều này thúc đẩy chúng tôi thực hiện một thí nghiệm như vậy.
Một chút lý thuyết
ESNI là phần mở rộng của giao thức TLS 1.3 cho phép mã hóa SNI trong thông báo bắt tay TLS "Xin chào khách hàng". Đây là giao diện của Client Hello khi có hỗ trợ ESNI (thay vì SNI thông thường, chúng tôi thấy ESNI):

Để sử dụng ESNI, bạn cần ba thành phần:
- DNS;
- Hỗ trợ khách hàng;
- Hỗ trợ phía máy chủ.
DNS
Bạn cần thêm hai bản ghi DNS – AVà TXT (Bản ghi TXT chứa khóa chung mà khách hàng có thể mã hóa SNI) - xem bên dưới. Ngoài ra phải có sự hỗ trợ DoH (DNS qua HTTPS) vì các máy khách khả dụng (xem bên dưới) không bật hỗ trợ ESNI nếu không có DoH. Điều này là hợp lý, vì ESNI ngụ ý mã hóa tên của tài nguyên mà chúng ta đang truy cập, nghĩa là việc truy cập DNS qua UDP là vô nghĩa. Hơn nữa, việc sử dụng cho phép bạn bảo vệ khỏi các cuộc tấn công đầu độc bộ đệm trong trường hợp này.
Hiện đang có sẵn , trong số đó:
CloudFlare (Kiểm tra Trình duyệt của tôi → SNI được mã hóa → Tìm hiểu thêm) rằng máy chủ của họ đã hỗ trợ ESNI, nghĩa là đối với máy chủ CloudFlare trong DNS, chúng tôi có ít nhất hai bản ghi - A và TXT. Trong ví dụ bên dưới, chúng tôi truy vấn DNS của Google (qua HTTPS):
А lối vào:
curl 'https://dns.google.com/resolve?name=www.cloudflare.com&type=A'
-s -H 'accept: application/dns+json'
{
"Status": 0,
"TC": false,
"RD": true,
"RA": true,
"AD": true,
"CD": false,
"Question": [
{
"name": "www.cloudflare.com.",
"type": 1
}
],
"Answer": [
{
"name": "www.cloudflare.com.",
"type": 1,
"TTL": 257,
"data": "104.17.210.9"
},
{
"name": "www.cloudflare.com.",
"type": 1,
"TTL": 257,
"data": "104.17.209.9"
}
]
}
TXT bản ghi, yêu cầu được tạo theo mẫu _esni.FQDN:
curl 'https://dns.google.com/resolve?name=_esni.www.cloudflare.com&type=TXT'
-s -H 'accept: application/dns+json'
{
"Status": 0,
"TC": false,
"RD": true,
"RA": true,
"AD": true,
"CD": false,
"Question": [
{
"name": "_esni.www.cloudflare.com.",
"type": 16
}
],
"Answer": [
{
"name": "_esni.www.cloudflare.com.",
"type": 16,
"TTL": 1799,
"data": ""/wEUgUKlACQAHQAg9SiAYQ9aUseUZr47HYHvF5jkt3aZ5802eAMJPhRz1QgAAhMBAQQAAAAAXtUmAAAAAABe3Q8AAAA=""
}
],
"Comment": "Response from 2400:cb00:2049:1::a29f:209."
}
Vì vậy, từ góc độ DNS, chúng ta nên sử dụng DoH (tốt nhất là với DNSSEC) và thêm hai mục nhập.
Hỗ trợ khách hàng
Nếu chúng ta đang nói về trình duyệt thì hiện tại . Dưới đây là hướng dẫn về cách kích hoạt hỗ trợ ESNI và DoH trong FireFox. Sau khi trình duyệt được cấu hình, chúng ta sẽ thấy một cái gì đó như thế này:

để kiểm tra trình duyệt.
Tất nhiên, TLS 1.3 phải được sử dụng để hỗ trợ ESNI, vì ESNI là phần mở rộng của TLS 1.3.
Với mục đích thử nghiệm phần phụ trợ với sự hỗ trợ của ESNI, chúng tôi đã triển khai ứng dụng khách trên go, Nhưng nhiều hơn về điều này sau.
Hỗ trợ phía máy chủ
Hiện tại, ESNI không được hỗ trợ bởi các máy chủ web như nginx/apache, v.v., vì chúng hoạt động với TLS thông qua OpenSSL/BoringSSL, vốn không hỗ trợ ESNI chính thức.
Do đó, chúng tôi đã quyết định tạo thành phần giao diện người dùng của riêng mình (proxy ngược ESNI), thành phần này sẽ hỗ trợ chấm dứt TLS 1.3 với lưu lượng ESNI và proxy HTTP(S) lên thượng nguồn, không hỗ trợ ESNI. Điều này cho phép sử dụng công nghệ trong cơ sở hạ tầng hiện có mà không thay đổi các thành phần chính - nghĩa là sử dụng các máy chủ web hiện tại không hỗ trợ ESNI.
Để rõ ràng, đây là một sơ đồ:

Tôi lưu ý rằng proxy được thiết kế với khả năng chấm dứt kết nối TLS mà không cần ESNI, để hỗ trợ các máy khách không có ESNI. Ngoài ra, giao thức liên lạc với thượng nguồn có thể là HTTP hoặc HTTPS với phiên bản TLS thấp hơn 1.3 (nếu thượng nguồn không hỗ trợ 1.3). Đề án này mang lại sự linh hoạt tối đa.
Triển khai hỗ trợ ESNI trên go chúng tôi đã mượn từ . Tôi muốn lưu ý ngay rằng bản thân việc triển khai không hề đơn giản vì nó liên quan đến những thay đổi trong thư viện chuẩn tiền điện tử / tls và do đó cần phải “vá” GOROOT trước khi lắp ráp.
Để tạo khóa ESNI, chúng tôi đã sử dụng (cũng là đứa con tinh thần của CloudFlare). Các khóa này được sử dụng để mã hóa/giải mã SNI.
Chúng tôi đã thử nghiệm bản dựng bằng Go 1.13 trên Linux (Debian, Alpine) và MacOS.
Một vài lời về tính năng hoạt động
Proxy ngược ESNI cung cấp các số liệu ở định dạng Prometheus, chẳng hạn như rps, độ trễ ngược dòng và mã phản hồi, số lần bắt tay TLS không thành công/thành công và thời lượng bắt tay TLS. Thoạt nhìn, điều này có vẻ đủ để đánh giá cách proxy xử lý lưu lượng truy cập.
Chúng tôi cũng đã thực hiện kiểm tra tải trước khi sử dụng. Kết quả dưới đây:
wrk -t50 -c1000 -d360s 'https://esni-rev-proxy.npw:443' --timeout 15s
Running 6m test @ https://esni-rev-proxy.npw:443
50 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.77s 1.21s 7.20s 65.43%
Req/Sec 13.78 8.84 140.00 83.70%
206357 requests in 6.00m, 6.08GB read
Requests/sec: 573.07
Transfer/sec: 17.28MB
Chúng tôi đã thực hiện thử nghiệm tải hoàn toàn định tính để so sánh sơ đồ sử dụng proxy ngược ESNI và không sử dụng proxy ngược. Chúng tôi đã "đổ" lưu lượng truy cập cục bộ để loại bỏ "nhiễu" trong các thành phần trung gian.
Vì vậy, với sự hỗ trợ của ESNI và ủy quyền ngược dòng từ HTTP, chúng tôi đã nhận được khoảng ~550 rps từ một phiên bản, với mức tiêu thụ CPU/RAM trung bình của proxy ngược ESNI:
- Mức sử dụng CPU 80% (4 vCPU, 4 GB RAM máy chủ, Linux)
- 130 MB bộ nhớ RSS

Để so sánh, RPS cho cùng một nginx ngược dòng không có chấm dứt TLS (giao thức HTTP) là ~ 1100:
wrk -t50 -c1000 -d360s 'http://lb.npw:80' –-timeout 15s
Running 6m test @ http://lb.npw:80
50 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.11s 2.30s 15.00s 90.94%
Req/Sec 23.25 13.55 282.00 79.25%
393093 requests in 6.00m, 11.35GB read
Socket errors: connect 0, read 0, write 0, timeout 9555
Non-2xx or 3xx responses: 8111
Requests/sec: 1091.62
Transfer/sec: 32.27MB
Sự xuất hiện của lỗi hết thời gian chờ cho thấy có sự thiếu hụt tài nguyên (chúng tôi đã sử dụng máy chủ 4 vCPU, 4 GB RAM, Linux), và trên thực tế, RPS tiềm năng còn cao hơn (chúng tôi đã nhận được số liệu lên đến 2700 RPS trên các nguồn mạnh hơn).
Tóm lại, tôi lưu ý rằng công nghệ ESNI có vẻ khá hứa hẹn. Vẫn còn nhiều câu hỏi mở, chẳng hạn như vấn đề lưu trữ khóa ESNI công khai trong DNS và xoay khóa ESNI - những vấn đề này đang được thảo luận tích cực và phiên bản mới nhất của dự thảo ESNI (tại thời điểm viết bài) đã có sẵn. .
Nguồn: www.habr.com
