grpc / grpc-node

gRPC for Node.js
https://grpc.io
Apache License 2.0
4.43k stars 640 forks source link

grpc-node TLS sample code is not complete. #273

Open GingerMoon opened 6 years ago

GingerMoon commented 6 years ago

Hi gurus, I am a newbee to node.js and grpc. So a good sample code is really appreciated.

The grpc-node TLS sample code is in https://grpc.io/docs/guides/auth.html#nodejs not complete. For example, no sample code for TLS client side. The strange thing is,
https://grpc.io/grpc/node/grpc.credentials.html

createSsl( [root_certs] [, private_key] [, cert_chain]) Create an SSL Credentials object. If using a client-side certificate, both the second and third arguments must be passed. Why are private key and cert chain needed for client side? They are not needed for grpc-go TLS client side. https://github.com/grpc/grpc-go/issues/1980
marzolfb commented 6 years ago

There is some confusion here and I'm not certain if you are trying to initiate a "normal" TLS connection to a server from the client side or if you are instead trying to connect to a server that performs client authentication via certs ("mutual TLS").

If it is the latter ("mutual TLS"), then you do need to pass the client cert (cert_chain) and private key for the cert (private_key) because the server you are connecting to requires it. Furthermore, the go example you point to (which I think is more pointing to the example here) is not doing mutual TLS (which requires providing a client-side cert/key) but rather it is just initiating a "normal" TLS connection to the server.

If it is the former (you are not doing mutual TLS), the optional [, private_key] [, cert_chain] parts would be omitted when making the call (which would match the go example you link to).

Hopefully that helps. I stumbled on this issue looking for other issues here and thought I would try to help clear things up.

celesteking commented 3 years ago

grpc nodejs docs are appalling. What shall I supply in root_certs? Buffer of what?

gja commented 3 years ago

@celesteking were you able to figure that out? We are having the same issue. In particular, node's root certs (require("tls").rootCertificates) are an array of strings

murgatroid99 commented 3 years ago

The root certs is supposed to be a Buffer loaded from a root certificates file. If you want to use the default public root certificates, just omit that argument. It is supposed to be used with self-signed certificates, or certificates that otherwise don't have a signing chain that includes one of the main root certificate authorities.

An example of self-signed certificates can be found in this directory: https://github.com/grpc/grpc-node/tree/master/test/data. The ca.pem file contains the root certificate.

gja commented 3 years ago

@murgatroid99 I don't believe the code works like that. The following example from the grpc-js, but I believe the native version works the same way. If ssl is missing, it looks for an environment variable called GRPC_DEFAULT_SSL_ROOTS_FILE_PATH (which is still a single .pem I believe).

https://github.com/grpc/grpc-node/blob/1d14203c382509c3f36132bd0244c99792cb6601/packages/grpc-js/src/tls-helpers.ts#L30

murgatroid99 commented 3 years ago

If that environment variable is not set, it defaults to using the built in root certificates.

Also, I'm not sure if this is clear: one .pem file can contain information for multiple certificates.

GingerMoon commented 3 years ago

Thank you guys for the time on this ticket. Since I am not working on this anymore, please feel free to close the ticket if needed.

yellowandy commented 3 years ago

Was also looking at just doing a TLS client connection from node (not mutual TLS) and couldn't get it working. The Java and Go equivalent are fairly straightforward but was unable to get it working in node.

sourinMT commented 3 years ago

Using credentials.createSsl() as credentials for a client side TLS connection(without any authentication) worked for me.

njavilas2015 commented 3 years ago

it is super easy to create a grpc ssl server you only have to import the certificates in my case with letsencrypt

let credentials = grpc.ServerCredentials.createSsl(null /fs.readFileSync('./certs/ca.crt')/, [{ cert_chain: fs.readFileSync('/etc/letsencrypt/fullchain.pem'), private_key: fs.readFileSync('/etc/letsencrypt/privkey.pem') }], false);

await server.bindAsync("0.0.0.0:50051", credentials, (error, port) => { if (error) console.log(error) console.log("listening grpc on *:50051"); server.start() });

nandor-magyar commented 2 years ago

Creating a server is straightforward based on the docs, it is the client-side what is lacking docs and possibly features.

I have managed to create server-side with a Node server, connecting golang clients, but I cannot add Node clients the same manner as with golang.

I would like to achieve server-side TLS. The same way as it is done in golang example & mentioned by the OP: https://grpc.io/docs/guides/auth/#with-server-authentication-ssltls

credentials.createSsl() is not working, and I see no option to provide the public key alone.

Authenticate using a public key, not using a CA, is this feature missing?

murgatroid99 commented 2 years ago

On the server side, you need to use ServerCredentials.createSsl().

nandor-magyar commented 2 years ago

Server-side, the node component being the client, ServerCredentials.createSsl() will work if the system already trusts the servers public key, otherwise it is to be set up. Providing the public cert file into the rootCert/first param works with a generated private/public key-pair.

ragauskl commented 2 years ago

I had a use case where I needed to test minimal setup of gRPC SSL on LAN with Node.js across multiple devices and without being familiar in detail with gRPC and SSL (plus everyone names root and intermediate certificates differently making it confusing sometimes) It took me couple days of frustration with lack of examples for server setup, so I made small server-client example for localhost in case anyone finds it useful: sample-grpc-ssl. It uses selfsigned certs with node-forge (Alternative with openssl here) or can replace them with letsencrypt or other

vthakuri commented 2 years ago

@murgatroid99 can you please give some sample code in nodejs for server to apply TLS

ServerCredentials.createSsl(). for this thing it will be really helpful or any link where in nodejs server side and client side TLS is setup sir please

xmlking commented 1 year ago

Example, if you have to use combine systemRootCerts and self-signed root cert as trust certs when gRPC client has to connect to both production server endpoint (server cert signed by public CA) as well as developoment server endpoint (server cert signed by self-signed CA)

import grpc from '@grpc/grpc-js';
import { readFileSync } from 'node:fs';
import tls from 'node:tls';
import { EOL } from 'node:os';
import type { ClientOptions } from '@grpc/grpc-js';
import type {VerifyOptions} from "@grpc/grpc-js/src/channel-credentials";

const systemRootCerts = Buffer.from(tls.rootCertificates.join(EOL));
const myRootCert = readFileSync('config/certs/ca-cert.pem')
const rootCerts  =  Buffer.concat([myRootCert, systemRootCerts])
const privateKey = readFileSync('config/certs/client-key.pem')
const certChain =   readFileSync('config/certs/client-cert.pem')
const verifyOptions: VerifyOptions = {}

const channelCredentials = grpc.credentials.createSsl(rootCerts, privateKey, certChain, verifyOptions)

console.debug('endpoints', endpoints);
// If no ServerName is set, infer the ServerName from the hostname we're connecting to.
const hostname = endpoints.engine?.split(':')[0] ?? 'localhost';
console.debug('engine hostname:', hostname);

const clientOptions: ClientOptions = {
    'grpc.ssl_target_name_override': hostname,
    'grpc.default_authority': hostname
};

const transport = new GrpcTransport({ host: endpoints.engine, channelCredentials, clientOptions });

const engineServiceClient = new EngineServiceClient(transport);
notmedia commented 1 year ago

I wrote an article about it - https://itnext.io/how-to-setup-and-test-tls-in-grpc-grpc-web-1b67cc4413e6