From c4bb5cc60da5feb57cf389ad583ebbb4cdab720f Mon Sep 17 00:00:00 2001 From: James Watkins-Harvey Date: Tue, 17 Oct 2023 17:18:22 -0400 Subject: [PATCH] feat(client): Allow specifying grpc CallCredentials (#1261) --- packages/client/src/connection.ts | 55 +++++++-- packages/test/src/test-client-connection.ts | 108 +++++++++++++++++- packages/test/tls_certs/test-ca.crt | 31 +++++ packages/test/tls_certs/test-client-chain.crt | 61 ++++++++++ packages/test/tls_certs/test-client.key | 52 +++++++++ packages/test/tls_certs/test-server-chain.crt | 61 ++++++++++ packages/test/tls_certs/test-server.key | 52 +++++++++ 7 files changed, 407 insertions(+), 13 deletions(-) create mode 100644 packages/test/tls_certs/test-ca.crt create mode 100644 packages/test/tls_certs/test-client-chain.crt create mode 100644 packages/test/tls_certs/test-client.key create mode 100644 packages/test/tls_certs/test-server-chain.crt create mode 100644 packages/test/tls_certs/test-server.key diff --git a/packages/client/src/connection.ts b/packages/client/src/connection.ts index 1285dc889..331ddb709 100644 --- a/packages/client/src/connection.ts +++ b/packages/client/src/connection.ts @@ -21,21 +21,47 @@ export interface ConnectionOptions { address?: string; /** - * TLS configuration. - * Pass a falsy value to use a non-encrypted connection or `true` or `{}` to - * connect with TLS without any customization. + * TLS configuration. Pass a falsy value to use a non-encrypted connection, + * or `true` or `{}` to connect with TLS without any customization. + * + * For advanced scenario, a prebuilt {@link grpc.ChannelCredentials} object + * may instead be specified using the {@link credentials} property. * * Either {@link credentials} or this may be specified for configuring TLS + * + * @default TLS is disabled */ tls?: TLSConfig | boolean | null; /** - * Channel credentials, create using the factory methods defined {@link https://grpc.github.io/grpc/node/grpc.credentials.html | here} + * gRPC channel credentials. + * + * `ChannelCredentials` are things like SSL credentials that can be used to secure a connection. + * There may be only one `ChannelCredentials`. They can be created using some of the factory + * methods defined {@link https://grpc.github.io/grpc/node/grpc.credentials.html | here} + * + * Specifying a prebuilt `ChannelCredentials` should only be required for advanced use cases. + * For simple TLS use cases, using the {@link tls} property is recommended. To register + * `CallCredentials` (eg. metadata-based authentication), use the {@link callCredentials} property. * * Either {@link tls} or this may be specified for configuring TLS */ credentials?: grpc.ChannelCredentials; + /** + * gRPC call credentials. + * + * `CallCredentials` generaly modify metadata; they can be attached to a connection to affect all method + * calls made using that connection. They can be created using some of the factory methods defined + * {@link https://grpc.github.io/grpc/node/grpc.credentials.html | here} + * + * If `callCredentials` are specified, they will be composed with channel credentials + * (either the one created implicitely by using the {@link tls} option, or the one specified + * explicitly through {@link credentials}). Notice that gRPC doesn't allow registering + * `callCredentials` on insecure connections. + */ + callCredentials?: grpc.CallCredentials[]; + /** * GRPC Channel arguments * @@ -84,7 +110,9 @@ export interface ConnectionOptions { connectTimeout?: Duration; } -export type ConnectionOptionsWithDefaults = Required> & { +export type ConnectionOptionsWithDefaults = Required< + Omit +> & { connectTimeoutMs: number; }; @@ -114,7 +142,7 @@ function addDefaults(options: ConnectionOptions): ConnectionOptionsWithDefaults * - Add default port to address if port not specified */ function normalizeGRPCConfig(options?: ConnectionOptions): ConnectionOptions { - const { tls: tlsFromConfig, credentials, ...rest } = options || {}; + const { tls: tlsFromConfig, credentials, callCredentials, ...rest } = options || {}; if (rest.address) { // eslint-disable-next-line prefer-const let [host, port] = rest.address.split(':', 2); @@ -128,10 +156,9 @@ function normalizeGRPCConfig(options?: ConnectionOptions): ConnectionOptions { } return { ...rest, - credentials: grpc.credentials.createSsl( - tls.serverRootCACertificate, - tls.clientCertPair?.key, - tls.clientCertPair?.crt + credentials: grpc.credentials.combineChannelCredentials( + grpc.credentials.createSsl(tls.serverRootCACertificate, tls.clientCertPair?.key, tls.clientCertPair?.crt), + ...(callCredentials ?? []) ), channelArgs: { ...rest.channelArgs, @@ -144,7 +171,13 @@ function normalizeGRPCConfig(options?: ConnectionOptions): ConnectionOptions { }, }; } else { - return rest; + return { + ...rest, + credentials: grpc.credentials.combineChannelCredentials( + credentials ?? grpc.credentials.createInsecure(), + ...(callCredentials ?? []) + ), + }; } } diff --git a/packages/test/src/test-client-connection.ts b/packages/test/src/test-client-connection.ts index df897f2c1..8c3fa530a 100644 --- a/packages/test/src/test-client-connection.ts +++ b/packages/test/src/test-client-connection.ts @@ -1,5 +1,6 @@ import util from 'node:util'; import path from 'node:path'; +import fs from 'node:fs/promises'; import test from 'ava'; import * as grpc from '@grpc/grpc-js'; import * as protoLoader from '@grpc/proto-loader'; @@ -20,6 +21,23 @@ async function bindLocalhost(server: grpc.Server): Promise { return await util.promisify(server.bindAsync.bind(server))('127.0.0.1:0', grpc.ServerCredentials.createInsecure()); } +async function bindLocalhostTls(server: grpc.Server): Promise { + const caCert = await fs.readFile(path.resolve(__dirname, `../tls_certs/test-ca.crt`)); + const serverChainCert = await fs.readFile(path.resolve(__dirname, `../tls_certs/test-server-chain.crt`)); + const serverKey = await fs.readFile(path.resolve(__dirname, `../tls_certs/test-server.key`)); + const credentials = grpc.ServerCredentials.createSsl( + caCert, + [ + { + cert_chain: serverChainCert, + private_key: serverKey, + }, + ], + true + ); + return await util.promisify(server.bindAsync.bind(server))('localhost:0', credentials); +} + test('withMetadata / withDeadline set the CallContext for RPC call', async (t) => { const server = new grpc.Server(); let gotTestHeaders = false; @@ -32,7 +50,7 @@ test('withMetadata / withDeadline set the CallContext for RPC call', async (t) = temporal.api.workflowservice.v1.IRegisterNamespaceRequest, temporal.api.workflowservice.v1.IRegisterNamespaceResponse >, - callback: grpc.sendUnaryData + callback: grpc.sendUnaryData ) { const [testValue] = call.metadata.get('test'); const [otherValue] = call.metadata.get('otherKey'); @@ -111,7 +129,7 @@ test('grpc retry passes request and headers on retry, propagates responses', asy temporal.api.workflowservice.v1.IDescribeWorkflowExecutionRequest, temporal.api.workflowservice.v1.IDescribeWorkflowExecutionResponse >, - callback: grpc.sendUnaryData + callback: grpc.sendUnaryData ) { const { namespace } = call.request; if (typeof namespace === 'string') { @@ -172,3 +190,89 @@ test('Default keepalive settings are set while maintaining user provided channel // User setting overrides default t.is(channelArgs['grpc.keepalive_permit_without_calls'], 0); }); + +test('Can configure TLS + call credentials', async (t) => { + const meta = Array(); + + const server = new grpc.Server(); + + server.addService(workflowServiceProtoDescriptor.temporal.api.workflowservice.v1.WorkflowService.service, { + getSystemInfo( + call: grpc.ServerUnaryCall< + temporal.api.workflowservice.v1.IGetSystemInfoRequest, + temporal.api.workflowservice.v1.IGetSystemInfoResponse + >, + callback: grpc.sendUnaryData + ) { + const [aValue] = call.metadata.get('a'); + const [authorizationValue] = call.metadata.get('authorization'); + if (typeof aValue === 'string' && typeof authorizationValue === 'string') { + meta.push([aValue, authorizationValue]); + } + + const response: temporal.api.workflowservice.v1.IGetSystemInfoResponse = { + serverVersion: 'test', + capabilities: undefined, + }; + callback(null, response); + }, + + describeWorkflowExecution( + call: grpc.ServerUnaryCall< + temporal.api.workflowservice.v1.IDescribeWorkflowExecutionRequest, + temporal.api.workflowservice.v1.IDescribeWorkflowExecutionResponse + >, + callback: grpc.sendUnaryData + ) { + const [aValue] = call.metadata.get('a'); + const [authorizationValue] = call.metadata.get('authorization'); + if (typeof aValue === 'string' && typeof authorizationValue === 'string') { + meta.push([aValue, authorizationValue]); + } + + const response: temporal.api.workflowservice.v1.IDescribeWorkflowExecutionResponse = { + workflowExecutionInfo: { execution: { workflowId: 'test' } }, + }; + callback(null, response); + }, + }); + const port = await bindLocalhostTls(server); + server.start(); + + let callNumber = 0; + const oauth2Client: grpc.OAuth2Client = { + getRequestHeaders: async () => { + const accessToken = `oauth2-access-token-${++callNumber}`; + return { authorization: `Bearer ${accessToken}` }; + }, + }; + + // Default interceptor config with backoff factor of 1 to speed things up + // const interceptor = makeGrpcRetryInterceptor(defaultGrpcRetryOptions({ factor: 1 })); + const conn = await Connection.connect({ + address: `localhost:${port}`, + metadata: { a: 'bc' }, + tls: { + serverRootCACertificate: await fs.readFile(path.resolve(__dirname, `../tls_certs/test-ca.crt`)), + clientCertPair: { + crt: await fs.readFile(path.resolve(__dirname, `../tls_certs/test-client-chain.crt`)), + key: await fs.readFile(path.resolve(__dirname, `../tls_certs/test-client.key`)), + }, + serverNameOverride: 'Server', + }, + callCredentials: [grpc.credentials.createFromGoogleCredential(oauth2Client)], + }); + + // Make three calls + await conn.workflowService.describeWorkflowExecution({ namespace: 'a' }); + await conn.workflowService.describeWorkflowExecution({ namespace: 'b' }); + await conn.workflowService.describeWorkflowExecution({ namespace: 'c' }); + + // Check that both connection level metadata and call credentials metadata are sent correctly + t.deepEqual(meta, [ + ['bc', 'Bearer oauth2-access-token-1'], + ['bc', 'Bearer oauth2-access-token-2'], + ['bc', 'Bearer oauth2-access-token-3'], + ['bc', 'Bearer oauth2-access-token-4'], + ]); +}); diff --git a/packages/test/tls_certs/test-ca.crt b/packages/test/tls_certs/test-ca.crt new file mode 100644 index 000000000..7ac36971b --- /dev/null +++ b/packages/test/tls_certs/test-ca.crt @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIUK/OctBa1W2oRcLVgf08rSIeRuE4wDQYJKoZIhvcNAQEL +BQAwQTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMSUwIwYDVQQDDBwgVGVzdCBS +b290IENBIC0gRE8gTk9UIFRSVVNUMB4XDTIzMTAxNjIxMDcwMVoXDTMzMTAxMzIx +MDcwMVowQTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMSUwIwYDVQQDDBwgVGVz +dCBSb290IENBIC0gRE8gTk9UIFRSVVNUMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAx6jqT2kK4dFoqj+4rWAdLO6h3rTfsh1PTYBiPZrZCvi3+DEoZRi8 +yy8XkJzH4wZ43EG0Q52CYVNKAM9auh9ahu5g00h4kOCL3hjVIsG9Pnw9k/ArXNcC +WRC3R5Gv18cDq9U8mDxJk4P6d3Tx0iWEZ5/+6dEtSWlIWhHFj1zU+VoCB0FLvQrr +tbPYzV8DCkXo62h78EssFQbz4Fqs5htpRN4EUESofQKq3VOlKzgq1NyJsxw9xILY +sYeHo9PVMbPtGvPY118qQWGS3/eCAVmHknOYWVYjk1TRYNgE/EdoD82Psm1Xcf7+ +/2NQnGsQgGUvls68Q2kVzJbybxmlAF8u188DPy5qHrn3hAViDFKfbjOuW/GoF5uA +ZHrCAlG+zrhAEdDo5gtCE2MFE2J+mk7R29VOqDOy7IElJwNkh8NXrIdsWwkLTPSo +hXFjZkh2yxqmHJp2mEZoI7mrPEjpTkmIGKq/QQXh4e/GAKUmA/bYUGXeAWRQNH+P +/mygHYRJXtN7ulx1vAb2WlV8fuy9X3cqW95B3pLNwpcD5nwVrLGcMWtZd5kKJifW +ZzDlxEfGcubIGHCqyhw90nNJwsrRMGpTIl4c174MrYqy/t3uPwhmsg5tipedn5xn +GqQ1TO2jpOuiBMv1x4ccsg5cV28ZGawWIpp+KpYY+HcgZ+W3SNm6zYUCAwEAAaNT +MFEwHQYDVR0OBBYEFPlVgrLSBFw9+0jg8CZdAzLBNhSuMB8GA1UdIwQYMBaAFPlV +grLSBFw9+0jg8CZdAzLBNhSuMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggIBAGI7XXLC3qwHM01H6vlmmCkgeQMcHCeCaQSxB77039R1gBizX1SXMHmi +I5ThmmOAGaXHgQkT6js2j4nUqAZV++bNWAJVmHnMIGKbLMH5VAOx0XczENllpAnb +5jK/vE5Zz473DERY7Tj3YHE+JeD+XIBDz0ngcCHMJo3yig05kl2Oq29QvseUAwiQ +mZ5Rt3LmnG2q/21JykELqB/eowFoKqAt/Y+KIISJH1gKtZNKUP2LUCZsR76AUcsD +UqZ2FnpuD6c1zW3vOx3r3g3iAdDOy0NvVJbTsfPDMxL2Kxq+f7m0JiifJ3lmdDfd +2tjP4O2ZwC8lgKS2T/HYaHZ8zL06DYaVceG5u1f0qit+ubTDYXp47bk5LEiGDhxo +m7kKoxAuh2vzf2QRO70QfJKScMY5/mKPq5Pku4DkW2N7m9X1HabDeo82sGzNlj2E +UxDs2p1gPuIy/USF/PC06TYDuUkbYQCS6YZSwnw2XZ7mOZgprsiOROSuhFuNTODM +yQLdw2i/Z/PaSzeDjf2JNf0Rou/Qmfc8wbtQ8rKIm9J24a6Tg0odHTw49ipr4d5e +jRNXZCHoVmQg9ar6vSf+ncz9Iw9iAhZqPV9RL5Jr7IdYVEqRjt96vsITwF5s2A+0 +d3pn2Qj1WDucVFguQqJM+5BIcMg4RjJEaDDRYDSMHGTW1e3ACt3O +-----END CERTIFICATE----- diff --git a/packages/test/tls_certs/test-client-chain.crt b/packages/test/tls_certs/test-client-chain.crt new file mode 100644 index 000000000..9f34caece --- /dev/null +++ b/packages/test/tls_certs/test-client-chain.crt @@ -0,0 +1,61 @@ +-----BEGIN CERTIFICATE----- +MIIFMzCCAxugAwIBAgIUEIHOmHY61jeOj4iMtUsM2CgIcmcwDQYJKoZIhvcNAQEL +BQAwQTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMSUwIwYDVQQDDBwgVGVzdCBS +b290IENBIC0gRE8gTk9UIFRSVVNUMB4XDTIzMTAxNjIxMDk1MVoXDTMzMTAxMzIx +MDk1MVowETEPMA0GA1UEAwwGQ2xpZW50MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAvxSSdvnEvhCoRs+rN82BE/bUi/5bPtQJJdo4Qb2CDosu0Yqn2JgP +k6dnIYn7/PXqsH+xNANQuFP9CQl54k4WtKAhq6J1krhyTDo0D8F6+jz/CisThVYF +bfU4Uos7qiRxIZ8Iqek3RPsWvfd4FLrys4rW5cQ3bzlN3soH0TxPifYv1M9kVl3m +7IkJIGzsQQcbMmt8/Fu9gfAJyWCx1BSVKdcWGtoKddok7FWlMcENE1h130r8oNvP +CnMJ1pYTRAx+oKdeTK+88gxCt4LlhMmr6UHHJ/1NrjsLtouYw/gImeikkefEW23g +b6irKlJCYu3H+MntclbkJYEHp92KIP3BNQ5NI2d5Tig8YyJLsQ00VzTe+RK9dvsN +4ocD4aXToSvFRJaS/a6J35rKt7+5gN+v4dX7G2s3d/39HkKhBeVegXecCl4oUIRl +3l4xaIXKNCHLho5g+S7wxa+xW+uwI7Z1LnxxpN77XhEEISjQQ/B8qYdrE86tibr8 +TQ6j0lQ1kj3foRDc2nfi+rq0trgIry2GRKP0KJ41nLtS0S2EUPiaBjV8Pgm0N3Um +mErrxsb1dRDNPMNpIq/sc9Xs0j5QqeZ5EoKYOwc21n+KpArZAq+wQ2BBsjzIA4Fb +lmdCHF+mIE2TFmJ9K2wdpXaGN62LFGLO2eA2HDoUAJhKZWtSGz8W4cECAwEAAaNT +MFEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUwR2Ye/aZnGUGN93QEXRc6g+r +/30wHwYDVR0jBBgwFoAU+VWCstIEXD37SODwJl0DMsE2FK4wDQYJKoZIhvcNAQEL +BQADggIBAIaoNgVnmlxUY51sC2SxDrFN+k0P9dAF+ZAmtY+S6dsQP9YevLWb0l+i +PtI66uIuZ+2YdGsyuNecudstNw4+pA2gGow8ZzuPE4VH9HfQZBFNv9dK7e1rWbol +XpChLDvjFJtmuyPJeafeEs6u3xbjPVcQ7VPSGQbe0n7LhrFRFn+hRdyHVXBzcuUJ +Z4/FQs52P0ontgR707Jc4+xNcEUgkCSmNbxenRGr6NPIA/C6fIOZ4qQiNK4qAtOe +k9nQsc5Dda1XbrfYOaNY5wmme7jQFqJtvbS+JuAAjWuPfyw28mUJ7PwlQGo/lU15 +DzV+eUw7OEXBygpy9YXE6rDb96cbx8Ne7065gzq24Ucl5bo+tCnoONNv8Pvb/NHM +Ewy2RYXSq5iTtxdiRE4J1PW1dxEAddBhvT6kf0Rr0rZqIdmtEVZOoLDu3c96wbB4 +9EzfqIQOGbnEgCvneRL3VMFeezVksMdRR6XTvsdZlhYcxjkmNdbQOh9pPebnn9cm +5mJiGoRvv0Hmhwsiecs9fSVgWY0qZm/2m8bfxueAgbSVjy7Cd7gqiKB//s8Ws1bi +mhWOd5CTifpjjqxX9SBFei+Z2BB04DD107Hkf4d/DRbKk29FNFjjrVzpsvItvwud +1bhj31jtdKvcs7dNKOwRF38jE6dDM9x12SFTviCR/WLQs3+tomQB +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIUK/OctBa1W2oRcLVgf08rSIeRuE4wDQYJKoZIhvcNAQEL +BQAwQTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMSUwIwYDVQQDDBwgVGVzdCBS +b290IENBIC0gRE8gTk9UIFRSVVNUMB4XDTIzMTAxNjIxMDcwMVoXDTMzMTAxMzIx +MDcwMVowQTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMSUwIwYDVQQDDBwgVGVz +dCBSb290IENBIC0gRE8gTk9UIFRSVVNUMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAx6jqT2kK4dFoqj+4rWAdLO6h3rTfsh1PTYBiPZrZCvi3+DEoZRi8 +yy8XkJzH4wZ43EG0Q52CYVNKAM9auh9ahu5g00h4kOCL3hjVIsG9Pnw9k/ArXNcC +WRC3R5Gv18cDq9U8mDxJk4P6d3Tx0iWEZ5/+6dEtSWlIWhHFj1zU+VoCB0FLvQrr +tbPYzV8DCkXo62h78EssFQbz4Fqs5htpRN4EUESofQKq3VOlKzgq1NyJsxw9xILY +sYeHo9PVMbPtGvPY118qQWGS3/eCAVmHknOYWVYjk1TRYNgE/EdoD82Psm1Xcf7+ +/2NQnGsQgGUvls68Q2kVzJbybxmlAF8u188DPy5qHrn3hAViDFKfbjOuW/GoF5uA +ZHrCAlG+zrhAEdDo5gtCE2MFE2J+mk7R29VOqDOy7IElJwNkh8NXrIdsWwkLTPSo +hXFjZkh2yxqmHJp2mEZoI7mrPEjpTkmIGKq/QQXh4e/GAKUmA/bYUGXeAWRQNH+P +/mygHYRJXtN7ulx1vAb2WlV8fuy9X3cqW95B3pLNwpcD5nwVrLGcMWtZd5kKJifW +ZzDlxEfGcubIGHCqyhw90nNJwsrRMGpTIl4c174MrYqy/t3uPwhmsg5tipedn5xn +GqQ1TO2jpOuiBMv1x4ccsg5cV28ZGawWIpp+KpYY+HcgZ+W3SNm6zYUCAwEAAaNT +MFEwHQYDVR0OBBYEFPlVgrLSBFw9+0jg8CZdAzLBNhSuMB8GA1UdIwQYMBaAFPlV +grLSBFw9+0jg8CZdAzLBNhSuMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggIBAGI7XXLC3qwHM01H6vlmmCkgeQMcHCeCaQSxB77039R1gBizX1SXMHmi +I5ThmmOAGaXHgQkT6js2j4nUqAZV++bNWAJVmHnMIGKbLMH5VAOx0XczENllpAnb +5jK/vE5Zz473DERY7Tj3YHE+JeD+XIBDz0ngcCHMJo3yig05kl2Oq29QvseUAwiQ +mZ5Rt3LmnG2q/21JykELqB/eowFoKqAt/Y+KIISJH1gKtZNKUP2LUCZsR76AUcsD +UqZ2FnpuD6c1zW3vOx3r3g3iAdDOy0NvVJbTsfPDMxL2Kxq+f7m0JiifJ3lmdDfd +2tjP4O2ZwC8lgKS2T/HYaHZ8zL06DYaVceG5u1f0qit+ubTDYXp47bk5LEiGDhxo +m7kKoxAuh2vzf2QRO70QfJKScMY5/mKPq5Pku4DkW2N7m9X1HabDeo82sGzNlj2E +UxDs2p1gPuIy/USF/PC06TYDuUkbYQCS6YZSwnw2XZ7mOZgprsiOROSuhFuNTODM +yQLdw2i/Z/PaSzeDjf2JNf0Rou/Qmfc8wbtQ8rKIm9J24a6Tg0odHTw49ipr4d5e +jRNXZCHoVmQg9ar6vSf+ncz9Iw9iAhZqPV9RL5Jr7IdYVEqRjt96vsITwF5s2A+0 +d3pn2Qj1WDucVFguQqJM+5BIcMg4RjJEaDDRYDSMHGTW1e3ACt3O +-----END CERTIFICATE----- diff --git a/packages/test/tls_certs/test-client.key b/packages/test/tls_certs/test-client.key new file mode 100644 index 000000000..84685e896 --- /dev/null +++ b/packages/test/tls_certs/test-client.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +Zr1g+Q1GkJfMU7DDI5U9/RYdKJbu9A== +-----END PRIVATE KEY----- diff --git a/packages/test/tls_certs/test-server-chain.crt b/packages/test/tls_certs/test-server-chain.crt new file mode 100644 index 000000000..9664d966d --- /dev/null +++ b/packages/test/tls_certs/test-server-chain.crt @@ -0,0 +1,61 @@ +-----BEGIN CERTIFICATE----- +MIIFMzCCAxugAwIBAgIUEIHOmHY61jeOj4iMtUsM2CgIcmYwDQYJKoZIhvcNAQEL +BQAwQTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMSUwIwYDVQQDDBwgVGVzdCBS +b290IENBIC0gRE8gTk9UIFRSVVNUMB4XDTIzMTAxNjIxMDkzMFoXDTMzMTAxMzIx +MDkzMFowETEPMA0GA1UEAwwGU2VydmVyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAs4YklEqx9G4JDZXVwrgxxibcW2Ko5yL4Yk1nwdrXKBZFfFEEOyHt +yNCDS+0EKRgkgk8yuu5aGerphjjJaM7KbtTh60wPIb/GyXwfDLZ9y1RcAIUdOXRh +4120PYRUxuQ7Zm7VpLdt8uKz8Ybd19z1wfuPzX8kPYnN5OSv6OK8jSEJ4IBsiyOW +8e508czzxk+euhvI7cXqtxxMY9TEyRpIK3xCxrQK8Fr0kkPeLTCFG9A1zhilJc4q +Jc5xoiFBspNyAB+dqlIKckaS8wCLx/qhr/NvIQidwhIpi5Le4CYsDdj+A5rotuRI +pZOZN+RuOpfLvZyD8miLfxw67D3mTfkvmUHfHnCqHlXaQDpEA7vR8vJs1OTPAeMY +zT2k9Y7yml9/vwlVkbeigESB3qvHDSXLH7ZMAReCtu9wepDqetTyqbuEFX5DF7IT +TSuehU8oelWXUmTKyvc/DRPKN2HwkssznBGVtR+xovtuLFWGneUWqbZRG85Qb/yC +0ifedR/Ln9xNNgDEQmZC1FxDDE1JqpxGgk3WUapVg9ypTkRL+DGDvgoImeF4YjP6 +755rQLESVwlWASODhSSmcT44BCpEFJZYR15ew5fuv0MSO46cyNkAPGcE5l/g9g2T +QkEMC1C2PmIa0f6b2V/ktuTnoTznx7oo5pV/oj7nij5htcXhanazOlMCAwEAAaNT +MFEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUzHsdxgDMfI0ix7iUvL1Jod68 +DJAwHwYDVR0jBBgwFoAU+VWCstIEXD37SODwJl0DMsE2FK4wDQYJKoZIhvcNAQEL +BQADggIBACiR4cPJg4WKp1/pKv5POymFx4U2KDeLLSK8FwX+vxDMGCnZzluZ3xmf +XVPDBfwLfKVByhUZiqWxgUr/E+Kyb8uaMAXvobocRmZJeI2xxV0GKbUM04vmX6FZ +9IJfcDcGOa+Vz4x7vHDBQi1XWB1GfgtaE4oPpIE07Pr+DuvonTYASPZRzCGqFxgm +smqsAtk40bk2t4mEJWUv27YtTqPlb2tpnPZFWJolr/GMnyxf3biRT8Zi9+K/Y2jc +GwFiQE0Eob4BgXDLGJQuaC+FxRbftn2YPyUKwtIbiGtqNCGvRDi1UqyTSv7MUa/a +Hbv7S4CcmqedZVP4ShmCDtAryaF+5H/ezHSBGKdyiXQEPWXipMPtOrh9BbHCAAPa +rfX6Zny/Y2ulNQDlG7VeI8xvg2fAp5PwVR0jIIzzDPD6xssrIZwh3DFG93XCAXgI +aKSSA6Gq8bgXtWG8GfvOeH5PXS5XFkqcyPA1wPHPuZ5eXVDcjiY7b/6VH2mLn+a1 +q4vLqiBNXLODIkZ9H3NHtFg7pZbQO1ZfwOQOhbqZOMszdcOlMBgfRSv/i3KFEL6V +4VLTJDKoBja3WO9livbK5Hsh9nbamPdddj3142B9d7uDDpAZJoZT29Y/kEJhENrd +ZksfN5dboSY1EmYG3iSzm0VnNEY8e9vSIGtZAXQDgLh7Gus9pGbw +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIUK/OctBa1W2oRcLVgf08rSIeRuE4wDQYJKoZIhvcNAQEL +BQAwQTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMSUwIwYDVQQDDBwgVGVzdCBS +b290IENBIC0gRE8gTk9UIFRSVVNUMB4XDTIzMTAxNjIxMDcwMVoXDTMzMTAxMzIx +MDcwMVowQTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMSUwIwYDVQQDDBwgVGVz +dCBSb290IENBIC0gRE8gTk9UIFRSVVNUMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAx6jqT2kK4dFoqj+4rWAdLO6h3rTfsh1PTYBiPZrZCvi3+DEoZRi8 +yy8XkJzH4wZ43EG0Q52CYVNKAM9auh9ahu5g00h4kOCL3hjVIsG9Pnw9k/ArXNcC +WRC3R5Gv18cDq9U8mDxJk4P6d3Tx0iWEZ5/+6dEtSWlIWhHFj1zU+VoCB0FLvQrr +tbPYzV8DCkXo62h78EssFQbz4Fqs5htpRN4EUESofQKq3VOlKzgq1NyJsxw9xILY +sYeHo9PVMbPtGvPY118qQWGS3/eCAVmHknOYWVYjk1TRYNgE/EdoD82Psm1Xcf7+ +/2NQnGsQgGUvls68Q2kVzJbybxmlAF8u188DPy5qHrn3hAViDFKfbjOuW/GoF5uA +ZHrCAlG+zrhAEdDo5gtCE2MFE2J+mk7R29VOqDOy7IElJwNkh8NXrIdsWwkLTPSo +hXFjZkh2yxqmHJp2mEZoI7mrPEjpTkmIGKq/QQXh4e/GAKUmA/bYUGXeAWRQNH+P +/mygHYRJXtN7ulx1vAb2WlV8fuy9X3cqW95B3pLNwpcD5nwVrLGcMWtZd5kKJifW +ZzDlxEfGcubIGHCqyhw90nNJwsrRMGpTIl4c174MrYqy/t3uPwhmsg5tipedn5xn +GqQ1TO2jpOuiBMv1x4ccsg5cV28ZGawWIpp+KpYY+HcgZ+W3SNm6zYUCAwEAAaNT +MFEwHQYDVR0OBBYEFPlVgrLSBFw9+0jg8CZdAzLBNhSuMB8GA1UdIwQYMBaAFPlV +grLSBFw9+0jg8CZdAzLBNhSuMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggIBAGI7XXLC3qwHM01H6vlmmCkgeQMcHCeCaQSxB77039R1gBizX1SXMHmi +I5ThmmOAGaXHgQkT6js2j4nUqAZV++bNWAJVmHnMIGKbLMH5VAOx0XczENllpAnb +5jK/vE5Zz473DERY7Tj3YHE+JeD+XIBDz0ngcCHMJo3yig05kl2Oq29QvseUAwiQ +mZ5Rt3LmnG2q/21JykELqB/eowFoKqAt/Y+KIISJH1gKtZNKUP2LUCZsR76AUcsD +UqZ2FnpuD6c1zW3vOx3r3g3iAdDOy0NvVJbTsfPDMxL2Kxq+f7m0JiifJ3lmdDfd +2tjP4O2ZwC8lgKS2T/HYaHZ8zL06DYaVceG5u1f0qit+ubTDYXp47bk5LEiGDhxo +m7kKoxAuh2vzf2QRO70QfJKScMY5/mKPq5Pku4DkW2N7m9X1HabDeo82sGzNlj2E +UxDs2p1gPuIy/USF/PC06TYDuUkbYQCS6YZSwnw2XZ7mOZgprsiOROSuhFuNTODM +yQLdw2i/Z/PaSzeDjf2JNf0Rou/Qmfc8wbtQ8rKIm9J24a6Tg0odHTw49ipr4d5e +jRNXZCHoVmQg9ar6vSf+ncz9Iw9iAhZqPV9RL5Jr7IdYVEqRjt96vsITwF5s2A+0 +d3pn2Qj1WDucVFguQqJM+5BIcMg4RjJEaDDRYDSMHGTW1e3ACt3O +-----END CERTIFICATE----- diff --git a/packages/test/tls_certs/test-server.key b/packages/test/tls_certs/test-server.key new file mode 100644 index 000000000..6a531d273 --- /dev/null +++ b/packages/test/tls_certs/test-server.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +V0JlS7F6lsvsBl6s9ZijedutJJX2Sw== +-----END PRIVATE KEY-----