Открытием это стало, поскольку тот же
curl, с которым обычно приходится иметь дело, ведёт себя иначе. Чтобы в этом убедиться, создадим ключ и сертификат, выписав его на имя localhost, и запустим с ними простой веб-сервер:# Creates files cert.pem and key.pem
$ openssl req -x509 -keyout key.pem -out cert.pem \
-days 365 -nodes -subj '/CN=localhost/'
# Check there is no SAN in certificate
$ openssl x509 -in cert.pem -noout -ext subjectAltName
No extensions in certificate
$ openssl s_server -key key.pem -cert cert.pem -WWW -port 4433
Веб-сервер будет отдавать файлы из рабочей директории, так что для тестирования запросим любой из имеющихся - например,
cert.pem.В соседнем терминале подключимся к серверу при помощи
curl. Поскольку сертификат, который отдаёт сервер, самоподписанный, используем его же для валидации подключения:$ curl https://localhost:4433/cert.pem --cacert cert.pem
-----BEGIN CERTIFICATE-----
MIIDCTCCAfGgAwIBAgIUO6d0vnNmNST77...
...
Как мы видим, сертификат вполне удовлетворил
curl. Теперь попробуем обратиться к тому же серверу, но по другому имени:# Using '--resolve' to avoid messing with /etc/hosts
$ curl https://example.com:4433/cert.pem --cacert cert.pem \
--resolve 'example.com:4433:127.0.0.1'
curl: (60) SSL: certificate subject name 'localhost' does not match
target host name 'example.com'
Как и ожидалось, теперь сертификат не принимается - ничего необычного.
Возьмём теперь HTTPS-клиента, написанного на Go, который будет подключаться к тому же серверу. Убедимся, что версия
go не менее 1.17, и запустим клиент:$ go version
go version go1.22.1 linux/amd64
$ CERT=cert.pem URL='https://localhost:4433/' go run main.go
2025/12/22 00:33:23 Error making request: Get "https://localhost:4433/":
tls: failed to verify certificate:
x509: certificate relies on legacy Common Name field, use SANs instead
exit status 1
Несмотря на соответствие доменного имени и сертификата клиент отказался подключаться к серверу.
В версиях
go до 1.16 включительно это поведение можно было переопределить, указав переменную GODEBUG=x509ignoreCN=0, однако уже с версии 1.17 изменить поведение стандартной библиотеки нельзя. Единственный способ заставить клиента подключиться - выписать сертификат, содержащий соответствующее поле SAN. Перевыпишем сертификат crt.pem с ключом -addext и перезапустим сервер:$ openssl req -x509 -keyout key.pem -out cert.pem \
-days 365 -nodes -subj '/CN=localhost/' \
-addext 'subjectAltName = DNS:localhost'
# Check SAN exists now
$ openssl x509 -in cert.pem -noout -ext subjectAltName
X509v3 Subject Alternative Name:
DNS:localhost
$ openssl s_server -key key.pem -cert cert.pem -WWW -port 4433
Теперь клиент сможет подключиться и также загрузить файл:
$ CERT=cert.pem URL='https://localhost:4433/' go run main.go
-----BEGIN CERTIFICATE-----
MIIDCTCCAfGgAwIBAgIUO6d0vnNmNST77...
...
Оставляя за скобками недовольство по поводу такого неоднозначного изменения в Go (проблему решали день), выводы хочется сделать следующие:
1. необходимо внимательно следить за содержимым сертификатов - расширения уже давно стали неотъемлемой частью сертификата;
2.
openssl по умолчанию выписывает сертификат, который не принимается многими клиентами - важно помнить о ключе -addext.Кстати,
mkcert, имеющийся в репозиториях Ubuntu, создаст ту же пару файлов при при помощи короткой команды mkcert localhost, однако сразу с расширением SAN и не требуя запоминать десяток неочевидных ключей. В этом вопросе он однозначно обходит openssl :)

