При использовании gRPC поверх обычного TCP клиент устанавливает канал с сервером следующим образом (в ruby):
stub = Helloworld::Greeter::Stub.new(service_url, :this_channel_is_insecure)
но затем, когда я реализую TLS на сервере и помещаю свой сертификат LetsEncrypt на сервер, клиент должен установить такое безопасное соединение (в ruby):
creds = GRPC::Core::Credentials.new(load_certs) # load_certs typically loads a CA roots file
stub = Helloworld::Greeter::Stub.new(service_url, creds)
Этот код с комментарием взят из официальных документов gRPC.
У меня вопрос: зачем клиенту здесь сертификат? Я думал, что сертификат нужен серверу, а клиент проверяет его действительность. Когда мой браузер подключается к защищенным веб-сайтам, приносит ли он собственный сертификат в таблицу? Если нет, то зачем он нужен gRPC-клиенту?
Из того, что я читал, клиент знает о некоторых доверенных центрах, с одним из которых связана цепочка сертификатов сервера. И комментарий в приведенном выше коде относится к «корневому файлу CA», который может быть сертификатом органа в верхней части цепочки. Так что, возможно, клиент gRPC сравнивает сертификат в корне цепочки сервера со своим собственным - но если это так, что произойдет, если он получит неправильный CA?
Все документы и сообщения, объясняющие, как установить безопасное соединение от клиента gRPC, говорят о чтении сертификата из локального файла, но ни один из них не говорит, что это за сертификат или откуда он.
Что мне не хватает?
РЕДАКТИРОВАТЬ:
Я обнаружил на своем компьютере каталог с кучей корневых сертификатов ЦС. /etc/ssl/certs/
Один из них, похоже, является органом, проверяющим LetsEncrypt, который я использую на сервере, поэтому я попытался прочитать этот сертификат в клиенте следующим образом:
GRPC::Core::ChannelCredentials.new(File.read('/etc/ssl/certs/ISRG_Root_X1.pem'))
но это привело только к этой ошибке
Не удалось выполнить рукопожатие с фатальной ошибкой SSL_ERROR_SSL: error: 1000007d: SSL-процедуры: OPENSSL_internal: CERTIFICATE_VERIFY_FAILED.
gRPC в первую очередь предназначен для подключения служб путем вызова удаленных процедур, например для микросервисов. В отличие от односторонних доверительных отношений между веб-сервером и несколькими клиентами браузера, обе вовлеченные партнеры должны явно доверять друг другу, чтобы избежать атак «злоумышленник в середине». gRPC предусматривает это специально для подключений, защищенных TLS.
В случае небезопасного (не защищенного TLS) соединения (предназначенного только для целей тестирования) для сервера gRPC параметр :this_port_is_insecure
передается в (GRPC::RpcServer.new).add_http2_port
метод и для каждого задействованного клиента gRPC параметр channel_args: :this_channel_is_insecure
передается в Core::Stub.new
метод.
Напротив, в случае защищенного соединения через TLS, GRPC::Core::ServerCredentials.new client_ca_pem, [{private_key: server_key_pem, cert_chain: server_cert_pem}], true
должен быть передан для сервера gRPC и GRPC::Core::ChannelCredentials.new server_ca_pem, client_key_pem, client_cert_pem
необходимо пройти для каждого клиента.
Для всех переменных * _pem загрузите контент через File.read
из соответствующих файлов PEM, предоставленных вашим центром доверия или, возможно, самостоятельно созданных ранее:
server_ca_pem
и client_ca_pem
может или не может быть тем же самым. Используйте дополнительные GRPC::Core::CallCredentials
если вам нужно защитить отношения между сервисом и клиентом на уровне звонка.
Руководство по аутентификации gRPC:
https://grpc.io/docs/guides/auth/
Примеры кода Ruby:
https://github.com/grpc/grpc/blob/master/src/ruby/spec/channel_credentials_spec.rb