Certificates for Onion Services
Сертификаты для onion-сервисов
Onion-сервисы изначально не нуждаются в TLS-сертификатах, так как Tor уже обеспечивает шифрование и аутентификацию. Однако при доступе через HTTPS-прокси или при желании показать «зелёный замок» можно выпустить сертификат.
Возможные варианты
-
DV от публичного CA
- Let’s Encrypt, DigiCert и др. поддерживают домены
.onion
. - Потребуется подтвердить владение onion-доменом через ACME (HTTP-01 или DNS-01).
- Let’s Encrypt, DigiCert и др. поддерживают домены
-
Собственный CA
- Генерируем корневой сертификат и подписываем им конечные.
- Подходит для внутренних или тестовых сервисов; клиенты должны добавить корень в доверенные.
-
Self-signed
- Быстро, но вызывает предупреждение браузера.
- Использовать только для разработки.
Практика с Onionspray
- Встроенный модуль
onionspray-cert
автоматизирует выпуск Let’s Encrypt. - Для собственного CA:
onionspray root-ca init onionspray cert issue <onion-address>
- Готовые сертификаты складываются в
./certs/
.
Проверка
- Публичный DV: открыть onion-сайт в Tor Browser — замок зелёный.
- Свой CA: импортировать
rootCA.pem
в браузер/ОС.
Кратко
- Для публичных проектов — Let’s Encrypt.
- Для частных — собственный CA.
- Self-signed только для тестов.
Комментарии (25)
- Участники спорят, нужны ли onion-сайтам сертификаты CA, ведь адрес .onion уже криптографически привязан к публичному ключу.
- Основной аргумент «за» — совместимость: браузеры и стандарты (HTTP/2, платежи) требуют TLS-сертификат, чтобы включить расширенные функции.
- RFC 9799 описывает расширения ACME для выдачи таких сертификатов, включая возможность связывать обычные домены и .onion-адреса одним ключом.
- Критики считают это «политическим» требованием: шифрование уже есть, но из-за «TLS повсюду» приходится накладывать лишний слой.
- Практический вывод: для публичных сервисов сертификат нужен, чтобы «вписаться» в экосистему; для приватных каналов достаточно TOFU или ручной проверки.
I'm too dumb for Zig's new IO interface 💬 Длинная дискуссия
Zig 0.15 сменил IO: std.Io.Reader
и std.Io.Writer
.
Старый способ тормозил и путался из-за anytype
.
Подключаемся к www.openmymind.net:443
:
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.DebugAllocator(.{}).init;
defer gpa.deinit();
const a = gpa.allocator();
const s = try std.net.tcpConnectToHost(a, "www.openmymind.net", 443);
defer s.close();
var wbuf: [std.crypto.tls.max_ciphertext_record_len]u8 = undefined;
var rbuf: [std.crypto.tls.max_ciphertext_record_len]u8 = undefined;
var w = s.writer(&wbuf);
var r = s.reader(&rbuf);
var bundle = std.crypto.Certificate.Bundle{};
try bundle.rescan(a);
defer bundle.deinit(a);
var tls_buf: [std.crypto.tls.max_ciphertext_record_len]u8 = undefined;
var tls = try std.crypto.tls.Client.init(
r.interface(),
&w.interface,
.{
.ca = .{ .bundle = bundle },
.host = .{ .explicit = "www.openmymind.net" },
.read_buffer = &.{},
.write_buffer = &tls_buf,
},
);
defer tls.end() catch {};
try tls.writer.writeAll("GET / HTTP/1.1\r\n\r\n");
var out: [1024]u8 = undefined;
var fw = std.Io.Writer.fixed(&out);
const n = try tls.reader.stream(&fw, .limited(out.len));
std.debug.print("read: {s}\n", .{out[0..n]});
}
Ключевые моменты
stream.reader()
иwriter()
требуют буфер.- Чтобы передать их в
tls.Client
, нужно.interface()
и&interface
. tls.Client.init
обязательно проситca
,host
,read_buffer
,write_buffer
.- Чтение ответа:
tls.reader.stream
вstd.Io.Writer
.
Комментарии (161)
- Автору пришлось вручную вызывать flush у двух обёрток и разбираться с тем, что первое чтение всегда возвращает 0.
- Основная жалоба — отсутствие документации и примеров; сообщество отвечает «читай исходники», но участники признают, что API ещё нестабилен.
- Некоторые считают новый I/O-интерфейс элегантным, но сложным даже для «Hello, world!»; другие предпочитают прямое использование системных API.
- Zig позиционируется как язык системного программирования, близкий к C, с преимуществами вроде discriminated unions и comptime, но сопровождается постоянными breaking changes.