Ao usar gRPC sobre TCP simples, o cliente estabelece um canal com o servidor assim (em ruby):
stub = Helloworld::Greeter::Stub.new(service_url, :this_channel_is_insecure)
mas então, quando eu implemento o TLS no servidor e coloco meu certificado LetsEncrypt no servidor, o cliente deve estabelecer uma conexão segura como esta (em ruby):
creds = GRPC::Core::Credentials.new(load_certs) # load_certs typically loads a CA roots file
stub = Helloworld::Greeter::Stub.new(service_url, creds)
Esse código com o comentário é retirado dos documentos oficiais do gRPC.
Minha pergunta é, por que o cliente precisa de um certificado aqui? Eu pensei que era o servidor que precisava de um certificado e o cliente garante que é válido. Quando meu navegador se conecta a sites seguros, ele traz seu próprio certificado para a mesa? Se não, por que o cliente gRPC precisa de um?
Pela minha leitura, o cliente conhece algumas autoridades confiáveis, a uma das quais a cadeia de certificados do servidor está vinculada. E o comentário no código acima se refere a um "arquivo raiz CA" que pode ser o certificado da autoridade no topo da cadeia. Então, talvez o cliente gRPC compare o certificado na raiz da cadeia do servidor com o seu próprio - mas se for esse o caso, o que acontece se obtiver a CA errada?
Todos os documentos e postagens explicando como estabelecer uma conexão segura de um cliente gRPC dizem para ler o certificado de um arquivo local, mas nenhum deles diz o que é esse certificado ou de onde vem.
o que estou perdendo?
EDITAR:
Eu descobri no meu computador um diretório com um monte do que parece ser certificados raiz de CA. /etc/ssl/certs/
Um deles parece ser a autoridade que verifica o LetsEncrypt, que eu uso no servidor, então tentei ler esse certificado no cliente assim:
GRPC::Core::ChannelCredentials.new(File.read('/etc/ssl/certs/ISRG_Root_X1.pem'))
mas só resultou neste erro
Falha no handshake com erro fatal SSL_ERROR_SSL: erro:1000007d:rotinas SSL:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED.
O gRPC destina-se principalmente a conectar serviços chamando procedimentos remotos, por exemplo, para microsserviços. Em contraste com a relação de confiança unilateral entre um servidor web e vários clientes de navegador, ambos os parceiros envolvidos devem confiar explicitamente um no outro para evitar ataques man-in-the-middle. O gRPC impõe isso por design para conexões protegidas por TLS.
No caso de uma conexão insegura (sem proteção TLS) (destinada apenas para fins de teste), para o servidor gRPC o parâmetro
:this_port_is_insecure
é passado para o(GRPC::RpcServer.new).add_http2_port
método e para cada cliente gRPC envolvido o parâmetrochannel_args: :this_channel_is_insecure
é passado para oCore::Stub.new
método.Por outro lado, no caso de uma conexão segura via TLS,
GRPC::Core::ServerCredentials.new client_ca_pem, [{private_key: server_key_pem, cert_chain: server_cert_pem}], true
deve ser passado para o servidor gRPC eGRPC::Core::ChannelCredentials.new server_ca_pem, client_key_pem, client_cert_pem
deve ser passado para cada cliente.Para todas as variáveis *_pem, carregue o conteúdo
File.read
dos respectivos arquivos PEM, fornecidos pelo seu centro de confiança ou talvez autogerados antes:server_ca_pem
eclient_ca_pem
pode ou não ser o mesmo. Use adicionalGRPC::Core::CallCredentials
se precisar proteger a relação serviço-cliente no nível da chamada.Guia de autenticação gRPC:
https://grpc.io/docs/guides/auth/
Exemplos de código Ruby:
https://github.com/grpc/grpc/blob/master/src/ruby/spec/channel_credentials_spec.rb