@@ -20,11 +20,17 @@ import {
20
20
createSecureContext ,
21
21
PeerCertificate ,
22
22
SecureContext ,
23
+ checkServerIdentity ,
24
+ connect as tlsConnect
23
25
} from 'tls' ;
24
26
25
27
import { CallCredentials } from './call-credentials' ;
26
28
import { CIPHER_SUITES , getDefaultRootsData } from './tls-helpers' ;
27
29
import { CaCertificateUpdate , CaCertificateUpdateListener , CertificateProvider , IdentityCertificateUpdate , IdentityCertificateUpdateListener } from './certificate-provider' ;
30
+ import { Socket } from 'net' ;
31
+ import { ChannelOptions } from './channel-options' ;
32
+ import { GrpcUri , parseUri , splitHostPort } from './uri-parser' ;
33
+ import { getDefaultAuthority } from './resolver' ;
28
34
29
35
// eslint-disable-next-line @typescript-eslint/no-explicit-any
30
36
function verifyIsBufferOrNull ( obj : any , friendlyName : string ) : void {
@@ -57,6 +63,11 @@ export interface VerifyOptions {
57
63
rejectUnauthorized ?: boolean ;
58
64
}
59
65
66
+ export interface SecureConnector {
67
+ connect ( socket : Socket ) : Promise < Socket > ;
68
+ destroy ( ) : void ;
69
+ }
70
+
60
71
/**
61
72
* A class that contains credentials for communicating over a channel, as well
62
73
* as a set of per-call credentials, which are applied to every method call made
@@ -83,13 +94,6 @@ export abstract class ChannelCredentials {
83
94
return this . callCredentials ;
84
95
}
85
96
86
- /**
87
- * Gets a SecureContext object generated from input parameters if this
88
- * instance was created with createSsl, or null if this instance was created
89
- * with createInsecure.
90
- */
91
- abstract _getConnectionOptions ( ) : ConnectionOptions | null ;
92
-
93
97
/**
94
98
* Indicates whether this credentials object creates a secure channel.
95
99
*/
@@ -102,13 +106,7 @@ export abstract class ChannelCredentials {
102
106
*/
103
107
abstract _equals ( other : ChannelCredentials ) : boolean ;
104
108
105
- _ref ( ) : void {
106
- // Do nothing by default
107
- }
108
-
109
- _unref ( ) : void {
110
- // Do nothing by default
111
- }
109
+ abstract _createSecureConnector ( channelTarget : GrpcUri , options : ChannelOptions ) : SecureConnector ;
112
110
113
111
/**
114
112
* Return a new ChannelCredentials instance with a given set of credentials.
@@ -180,51 +178,111 @@ class InsecureChannelCredentialsImpl extends ChannelCredentials {
180
178
compose ( callCredentials : CallCredentials ) : never {
181
179
throw new Error ( 'Cannot compose insecure credentials' ) ;
182
180
}
183
-
184
- _getConnectionOptions ( ) : ConnectionOptions | null {
185
- return { } ;
186
- }
187
181
_isSecure ( ) : boolean {
188
182
return false ;
189
183
}
190
184
_equals ( other : ChannelCredentials ) : boolean {
191
185
return other instanceof InsecureChannelCredentialsImpl ;
192
186
}
187
+ _createSecureConnector ( channelTarget : GrpcUri , options : ChannelOptions ) : SecureConnector {
188
+ return {
189
+ connect ( socket ) {
190
+ return Promise . resolve ( socket ) ;
191
+ } ,
192
+ destroy ( ) { }
193
+ }
194
+ }
193
195
}
194
196
195
- class SecureChannelCredentialsImpl extends ChannelCredentials {
196
- connectionOptions : ConnectionOptions ;
197
+ function getConnectionOptions ( secureContext : SecureContext , verifyOptions : VerifyOptions , channelTarget : GrpcUri , options : ChannelOptions ) : ConnectionOptions {
198
+ const connectionOptions : ConnectionOptions = {
199
+ secureContext : secureContext
200
+ } ;
201
+ if ( verifyOptions . checkServerIdentity ) {
202
+ connectionOptions . checkServerIdentity = verifyOptions . checkServerIdentity ;
203
+ }
204
+ if ( verifyOptions . rejectUnauthorized !== undefined ) {
205
+ connectionOptions . rejectUnauthorized = verifyOptions . rejectUnauthorized ;
206
+ }
207
+ connectionOptions . ALPNProtocols = [ 'h2' ] ;
208
+ if ( options [ 'grpc.ssl_target_name_override' ] ) {
209
+ const sslTargetNameOverride = options [ 'grpc.ssl_target_name_override' ] ! ;
210
+ const originalCheckServerIdentity =
211
+ connectionOptions . checkServerIdentity ?? checkServerIdentity ;
212
+ connectionOptions . checkServerIdentity = (
213
+ host : string ,
214
+ cert : PeerCertificate
215
+ ) : Error | undefined => {
216
+ return originalCheckServerIdentity ( sslTargetNameOverride , cert ) ;
217
+ } ;
218
+ connectionOptions . servername = sslTargetNameOverride ;
219
+ } else {
220
+ if ( 'grpc.http_connect_target' in options ) {
221
+ /* This is more or less how servername will be set in createSession
222
+ * if a connection is successfully established through the proxy.
223
+ * If the proxy is not used, these connectionOptions are discarded
224
+ * anyway */
225
+ const targetPath = getDefaultAuthority (
226
+ parseUri ( options [ 'grpc.http_connect_target' ] as string ) ?? {
227
+ path : 'localhost' ,
228
+ }
229
+ ) ;
230
+ const hostPort = splitHostPort ( targetPath ) ;
231
+ connectionOptions . servername = hostPort ?. host ?? targetPath ;
232
+ }
233
+ }
234
+ if ( options [ 'grpc-node.tls_enable_trace' ] ) {
235
+ connectionOptions . enableTrace = true ;
236
+ }
197
237
238
+ let realTarget : GrpcUri = channelTarget ;
239
+ if ( 'grpc.http_connect_target' in options ) {
240
+ const parsedTarget = parseUri ( options [ 'grpc.http_connect_target' ] ! ) ;
241
+ if ( parsedTarget ) {
242
+ realTarget = parsedTarget ;
243
+ }
244
+ }
245
+ const targetPath = getDefaultAuthority ( realTarget ) ;
246
+ const hostPort = splitHostPort ( targetPath ) ;
247
+ const remoteHost = hostPort ?. host ?? targetPath ;
248
+ connectionOptions . host = remoteHost ;
249
+ connectionOptions . servername = remoteHost ;
250
+ return connectionOptions ;
251
+ }
252
+
253
+ class SecureConnectorImpl implements SecureConnector {
254
+ constructor ( private connectionOptions : ConnectionOptions ) {
255
+ }
256
+ connect ( socket : Socket ) : Promise < Socket > {
257
+ const tlsConnectOptions : ConnectionOptions = {
258
+ socket : socket ,
259
+ ...this . connectionOptions
260
+ } ;
261
+ return new Promise < Socket > ( ( resolve , reject ) => {
262
+ const tlsSocket = tlsConnect ( tlsConnectOptions , ( ) => {
263
+ resolve ( tlsSocket )
264
+ } ) ;
265
+ tlsSocket . on ( 'error' , ( error : Error ) => {
266
+ reject ( error ) ;
267
+ } ) ;
268
+ } ) ;
269
+ }
270
+ destroy ( ) { }
271
+ }
272
+
273
+ class SecureChannelCredentialsImpl extends ChannelCredentials {
198
274
constructor (
199
275
private secureContext : SecureContext ,
200
276
private verifyOptions : VerifyOptions
201
277
) {
202
278
super ( ) ;
203
- this . connectionOptions = {
204
- secureContext,
205
- } ;
206
- // Node asserts that this option is a function, so we cannot pass undefined
207
- if ( verifyOptions ?. checkServerIdentity ) {
208
- this . connectionOptions . checkServerIdentity =
209
- verifyOptions . checkServerIdentity ;
210
- }
211
-
212
- if ( verifyOptions ?. rejectUnauthorized !== undefined ) {
213
- this . connectionOptions . rejectUnauthorized =
214
- verifyOptions . rejectUnauthorized ;
215
- }
216
279
}
217
280
218
281
compose ( callCredentials : CallCredentials ) : ChannelCredentials {
219
282
const combinedCallCredentials =
220
283
this . callCredentials . compose ( callCredentials ) ;
221
284
return new ComposedChannelCredentialsImpl ( this , combinedCallCredentials ) ;
222
285
}
223
-
224
- _getConnectionOptions ( ) : ConnectionOptions | null {
225
- // Copy to prevent callers from mutating this.connectionOptions
226
- return { ...this . connectionOptions } ;
227
- }
228
286
_isSecure ( ) : boolean {
229
287
return true ;
230
288
}
@@ -242,6 +300,10 @@ class SecureChannelCredentialsImpl extends ChannelCredentials {
242
300
return false ;
243
301
}
244
302
}
303
+ _createSecureConnector ( channelTarget : GrpcUri , options : ChannelOptions ) : SecureConnector {
304
+ const connectionOptions = getConnectionOptions ( this . secureContext , this . verifyOptions , channelTarget , options ) ;
305
+ return new SecureConnectorImpl ( connectionOptions ) ;
306
+ }
245
307
}
246
308
247
309
class CertificateProviderChannelCredentialsImpl extends ChannelCredentials {
@@ -250,10 +312,38 @@ class CertificateProviderChannelCredentialsImpl extends ChannelCredentials {
250
312
private latestIdentityUpdate : IdentityCertificateUpdate | null = null ;
251
313
private caCertificateUpdateListener : CaCertificateUpdateListener = this . handleCaCertificateUpdate . bind ( this ) ;
252
314
private identityCertificateUpdateListener : IdentityCertificateUpdateListener = this . handleIdentityCertitificateUpdate . bind ( this ) ;
315
+ private static SecureConnectorImpl = class implements SecureConnector {
316
+ constructor ( private parent : CertificateProviderChannelCredentialsImpl , private channelTarget : GrpcUri , private options : ChannelOptions ) { }
317
+
318
+ connect ( socket : Socket ) : Promise < Socket > {
319
+ return new Promise ( ( resolve , reject ) => {
320
+ const secureContext = this . parent . getLatestSecureContext ( ) ;
321
+ if ( ! secureContext ) {
322
+ reject ( new Error ( 'Credentials not loaded' ) ) ;
323
+ return ;
324
+ }
325
+ const connnectionOptions = getConnectionOptions ( secureContext , this . parent . verifyOptions , this . channelTarget , this . options ) ;
326
+ const tlsConnectOptions : ConnectionOptions = {
327
+ socket : socket ,
328
+ ...connnectionOptions
329
+ }
330
+ const tlsSocket = tlsConnect ( tlsConnectOptions , ( ) => {
331
+ resolve ( tlsSocket )
332
+ } ) ;
333
+ tlsSocket . on ( 'error' , ( error : Error ) => {
334
+ reject ( error ) ;
335
+ } ) ;
336
+ } ) ;
337
+ }
338
+
339
+ destroy ( ) {
340
+ this . parent . unref ( ) ;
341
+ }
342
+ }
253
343
constructor (
254
344
private caCertificateProvider : CertificateProvider ,
255
345
private identityCertificateProvider : CertificateProvider | null ,
256
- private verifyOptions : VerifyOptions | null
346
+ private verifyOptions : VerifyOptions
257
347
) {
258
348
super ( ) ;
259
349
}
@@ -265,27 +355,6 @@ class CertificateProviderChannelCredentialsImpl extends ChannelCredentials {
265
355
combinedCallCredentials
266
356
) ;
267
357
}
268
- _getConnectionOptions ( ) : ConnectionOptions | null {
269
- if ( this . latestCaUpdate === null ) {
270
- return null ;
271
- }
272
- if ( this . identityCertificateProvider !== null && this . latestIdentityUpdate === null ) {
273
- return null ;
274
- }
275
- const secureContext : SecureContext = createSecureContext ( {
276
- ca : this . latestCaUpdate . caCertificate ,
277
- key : this . latestIdentityUpdate ?. privateKey ,
278
- cert : this . latestIdentityUpdate ?. certificate ,
279
- ciphers : CIPHER_SUITES
280
- } ) ;
281
- const options : ConnectionOptions = {
282
- secureContext : secureContext
283
- } ;
284
- if ( this . verifyOptions ?. checkServerIdentity ) {
285
- options . checkServerIdentity = this . verifyOptions . checkServerIdentity ;
286
- }
287
- return options ;
288
- }
289
358
_isSecure ( ) : boolean {
290
359
return true ;
291
360
}
@@ -301,20 +370,24 @@ class CertificateProviderChannelCredentialsImpl extends ChannelCredentials {
301
370
return false ;
302
371
}
303
372
}
304
- _ref ( ) : void {
373
+ private ref ( ) : void {
305
374
if ( this . refcount === 0 ) {
306
375
this . caCertificateProvider . addCaCertificateListener ( this . caCertificateUpdateListener ) ;
307
376
this . identityCertificateProvider ?. addIdentityCertificateListener ( this . identityCertificateUpdateListener ) ;
308
377
}
309
378
this . refcount += 1 ;
310
379
}
311
- _unref ( ) : void {
380
+ private unref ( ) : void {
312
381
this . refcount -= 1 ;
313
382
if ( this . refcount === 0 ) {
314
383
this . caCertificateProvider . removeCaCertificateListener ( this . caCertificateUpdateListener ) ;
315
384
this . identityCertificateProvider ?. removeIdentityCertificateListener ( this . identityCertificateUpdateListener ) ;
316
385
}
317
386
}
387
+ _createSecureConnector ( channelTarget : GrpcUri , options : ChannelOptions ) : SecureConnector {
388
+ this . ref ( ) ;
389
+ return new CertificateProviderChannelCredentialsImpl . SecureConnectorImpl ( this , channelTarget , options ) ;
390
+ }
318
391
319
392
private handleCaCertificateUpdate ( update : CaCertificateUpdate | null ) {
320
393
this . latestCaUpdate = update ;
@@ -323,10 +396,25 @@ class CertificateProviderChannelCredentialsImpl extends ChannelCredentials {
323
396
private handleIdentityCertitificateUpdate ( update : IdentityCertificateUpdate | null ) {
324
397
this . latestIdentityUpdate = update ;
325
398
}
399
+
400
+ private getLatestSecureContext ( ) : SecureContext | null {
401
+ if ( this . latestCaUpdate === null ) {
402
+ return null ;
403
+ }
404
+ if ( this . identityCertificateProvider !== null && this . latestIdentityUpdate === null ) {
405
+ return null ;
406
+ }
407
+ return createSecureContext ( {
408
+ ca : this . latestCaUpdate . caCertificate ,
409
+ key : this . latestIdentityUpdate ?. privateKey ,
410
+ cert : this . latestIdentityUpdate ?. certificate ,
411
+ ciphers : CIPHER_SUITES
412
+ } ) ;
413
+ }
326
414
}
327
415
328
416
export function createCertificateProviderChannelCredentials ( caCertificateProvider : CertificateProvider , identityCertificateProvider : CertificateProvider | null , verifyOptions ?: VerifyOptions ) {
329
- return new CertificateProviderChannelCredentialsImpl ( caCertificateProvider , identityCertificateProvider , verifyOptions ?? null ) ;
417
+ return new CertificateProviderChannelCredentialsImpl ( caCertificateProvider , identityCertificateProvider , verifyOptions ?? { } ) ;
330
418
}
331
419
332
420
class ComposedChannelCredentialsImpl extends ChannelCredentials {
@@ -347,10 +435,6 @@ class ComposedChannelCredentialsImpl extends ChannelCredentials {
347
435
combinedCallCredentials
348
436
) ;
349
437
}
350
-
351
- _getConnectionOptions ( ) : ConnectionOptions | null {
352
- return this . channelCredentials . _getConnectionOptions ( ) ;
353
- }
354
438
_isSecure ( ) : boolean {
355
439
return true ;
356
440
}
@@ -367,4 +451,7 @@ class ComposedChannelCredentialsImpl extends ChannelCredentials {
367
451
return false ;
368
452
}
369
453
}
454
+ _createSecureConnector ( channelTarget : GrpcUri , options : ChannelOptions ) : SecureConnector {
455
+ return this . channelCredentials . _createSecureConnector ( channelTarget , options ) ;
456
+ }
370
457
}
0 commit comments