Skip to content

Commit 2cd8a38

Browse files
feat: Add the ability to inject a ca certificate for use in gRPC and gRPC Web (#753)
Signed-off-by: instamenta <[email protected]> Signed-off-by: Jan Milenkov <[email protected]> Co-authored-by: Jeromy Cannon <[email protected]>
1 parent 6930dab commit 2cd8a38

17 files changed

+512
-17
lines changed

src/commands/flags.ts

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,60 @@ export const hederaExplorerVersion: CommandFlag = {
708708
}
709709
}
710710

711+
//* ------------- Node Proxy Certificates ------------- !//
712+
713+
export const grpcTlsCertificatePath: CommandFlag = {
714+
constName: 'grpcTlsCertificatePath',
715+
name: 'grpc-tls-cert',
716+
definition: {
717+
describe:
718+
'TLS Certificate path for the gRPC ' +
719+
'(e.g. "node1=/Users/username/node1-grpc.cert" ' +
720+
'with multiple nodes comma seperated)',
721+
defaultValue: '',
722+
type: 'string'
723+
}
724+
}
725+
726+
export const grpcWebTlsCertificatePath: CommandFlag = {
727+
constName: 'grpcWebTlsCertificatePath',
728+
name: 'grpc-web-tls-cert',
729+
definition: {
730+
describe:
731+
'TLS Certificate path for gRPC Web ' +
732+
'(e.g. "node1=/Users/username/node1-grpc-web.cert" ' +
733+
'with multiple nodes comma seperated)',
734+
defaultValue: '',
735+
type: 'string'
736+
}
737+
}
738+
739+
export const grpcTlsKeyPath: CommandFlag = {
740+
constName: 'grpcTlsKeyPath',
741+
name: 'grpc-tls-key',
742+
definition: {
743+
describe:
744+
'TLS Certificate key path for the gRPC ' +
745+
'(e.g. "node1=/Users/username/node1-grpc.key" ' +
746+
'with multiple nodes comma seperated)',
747+
defaultValue: '',
748+
type: 'string'
749+
}
750+
}
751+
752+
export const grpcWebTlsKeyPath: CommandFlag = {
753+
constName: 'grpcWebTlsKeyPath',
754+
name: 'grpc-web-tls-key',
755+
definition: {
756+
describe:
757+
'TLC Certificate key path for gRPC Web ' +
758+
'(e.g. "node1=/Users/username/node1-grpc-web.key" ' +
759+
'with multiple nodes comma seperated)',
760+
defaultValue: '',
761+
type: 'string'
762+
}
763+
}
764+
711765
export const allFlags: CommandFlag[] = [
712766
accountId,
713767
amount,
@@ -773,7 +827,11 @@ export const allFlags: CommandFlag[] = [
773827
mirrorNodeVersion,
774828
hederaExplorerVersion,
775829
inputDir,
776-
outputDir
830+
outputDir,
831+
grpcTlsCertificatePath,
832+
grpcWebTlsCertificatePath,
833+
grpcTlsKeyPath,
834+
grpcWebTlsKeyPath,
777835
]
778836

779837
/** Resets the definition.disablePrompt for all flags */

src/commands/network.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,21 @@
1414
* limitations under the License.
1515
*
1616
*/
17-
1817
import { ListrEnquirerPromptAdapter } from '@listr2/prompt-adapter-enquirer'
1918
import chalk from 'chalk'
2019
import { Listr } from 'listr2'
2120
import { SoloError, IllegalArgumentError, MissingArgumentError } from '../core/errors.ts'
2221
import { BaseCommand } from './base.ts'
2322
import * as flags from './flags.ts'
24-
import type { KeyManager, PlatformInstaller, ProfileManager } from '../core/index.ts'
2523
import { constants, Templates } from '../core/index.ts'
2624
import * as prompts from './prompts.ts'
2725
import * as helpers from '../core/helpers.ts'
2826
import path from 'path'
2927
import { addDebugOptions, validatePath } from '../core/helpers.ts'
3028
import fs from 'fs'
31-
import { type NodeAlias, type NodeAliases } from '../types/aliases.ts'
32-
import { type Opts } from '../types/index.ts'
29+
import type { CertificateManager, KeyManager, PlatformInstaller, ProfileManager } from '../core/index.ts'
30+
import type { NodeAlias, NodeAliases } from '../types/aliases.ts'
31+
import type { Opts } from '../types/index.ts'
3332

3433
export interface NetworkDeployConfigClass {
3534
applicationEnv: string
@@ -48,14 +47,19 @@ export interface NetworkDeployConfigClass {
4847
nodeAliases: NodeAliases
4948
stagingDir: string
5049
stagingKeysDir: string
51-
valuesArg: string
50+
valuesArg: string,
51+
grpcTlsCertificatePath: string,
52+
grpcWebTlsCertificatePath: string,
53+
grpcTlsKeyPath: string,
54+
grpcWebTlsKeyPath: string,
5255
getUnusedConfigs: () => string[]
5356
}
5457

5558
export class NetworkCommand extends BaseCommand {
5659
private readonly keyManager: KeyManager
5760
private readonly platformInstaller: PlatformInstaller
5861
private readonly profileManager: ProfileManager
62+
private readonly certificateManager: CertificateManager
5963
private profileValuesFile?: string
6064

6165
constructor (opts: Opts) {
@@ -65,7 +69,9 @@ export class NetworkCommand extends BaseCommand {
6569
if (!opts || !opts.keyManager) throw new IllegalArgumentError('An instance of core/KeyManager is required', opts.keyManager)
6670
if (!opts || !opts.platformInstaller) throw new IllegalArgumentError('An instance of core/PlatformInstaller is required', opts.platformInstaller)
6771
if (!opts || !opts.profileManager) throw new MissingArgumentError('An instance of core/ProfileManager is required', opts.downloader)
72+
if (!opts || !opts.certificateManager) throw new MissingArgumentError('An instance of core/CertificateManager is required', opts.certificateManager)
6873

74+
this.certificateManager = opts.certificateManager
6975
this.keyManager = opts.keyManager
7076
this.platformInstaller = opts.platformInstaller
7177
this.profileManager = opts.profileManager
@@ -97,7 +103,11 @@ export class NetworkCommand extends BaseCommand {
97103
flags.quiet,
98104
flags.releaseTag,
99105
flags.settingTxt,
100-
flags.valuesFile
106+
flags.valuesFile,
107+
flags.grpcTlsCertificatePath,
108+
flags.grpcWebTlsCertificatePath,
109+
flags.grpcTlsKeyPath,
110+
flags.grpcWebTlsKeyPath,
101111
]
102112
}
103113

@@ -160,7 +170,11 @@ export class NetworkCommand extends BaseCommand {
160170
flags.persistentVolumeClaims,
161171
flags.profileName,
162172
flags.profileFile,
163-
flags.settingTxt
173+
flags.settingTxt,
174+
flags.grpcTlsCertificatePath,
175+
flags.grpcWebTlsCertificatePath,
176+
flags.grpcTlsKeyPath,
177+
flags.grpcWebTlsKeyPath,
164178
])
165179

166180
await prompts.execute(task, this.configManager, NetworkCommand.DEPLOY_FLAGS_LIST)
@@ -230,6 +244,18 @@ export class NetworkCommand extends BaseCommand {
230244
return lease.buildAcquireTask(task)
231245
}
232246
},
247+
{
248+
title: 'Copy gRPC TLS Certificates',
249+
task: (ctx, parentTask) =>
250+
self.certificateManager.buildCopyTlsCertificatesTasks(
251+
parentTask,
252+
ctx.config.grpcTlsCertificatePath,
253+
ctx.config.grpcWebTlsCertificatePath,
254+
ctx.config.grpcTlsKeyPath,
255+
ctx.config.grpcWebTlsKeyPath
256+
),
257+
skip: (ctx) => !ctx.config.grpcTlsCertificatePath && !ctx.config.grpcWebTlsCertificatePath
258+
},
233259
{
234260
title: 'Check if cluster setup chart is installed',
235261
task: async (ctx, task) => {

src/commands/node/configs.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,10 @@ export interface NodeAddConfigClass {
401401
treasuryKey: PrivateKey
402402
stagingDir: string
403403
stagingKeysDir: string
404+
grpcTlsCertificatePath: string,
405+
grpcWebTlsCertificatePath: string,
406+
grpcTlsKeyPath: string,
407+
grpcWebTlsKeyPath: string,
404408
getUnusedConfigs: () => string[]
405409
}
406410

src/commands/node/flags.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,11 @@ const COMMON_ADD_REQUIRED_NO_PROMPT_FLAGS = [
113113
flags.chainId,
114114
flags.debugNodeAlias,
115115
flags.soloChartVersion,
116-
flags.persistentVolumeClaims
116+
flags.persistentVolumeClaims,
117+
flags.grpcTlsCertificatePath,
118+
flags.grpcWebTlsCertificatePath,
119+
flags.grpcTlsKeyPath,
120+
flags.grpcWebTlsKeyPath,
117121
]
118122

119123
const COMMON_ADD_OPTIONAL_FLAGS = [

src/commands/node/handlers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ export class NodeCommandHandlers {
137137
this.tasks.checkPVCsEnabled(),
138138
this.tasks.identifyExistingNodes(),
139139
this.tasks.determineNewNodeAccountNumber(),
140+
this.tasks.copyGrpcTlsCertificates(),
140141
this.tasks.generateGossipKey(),
141142
this.tasks.generateGrpcTlsKey(),
142143
this.tasks.loadSigningKeyCertificate(),

src/commands/node/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export class NodeCommand extends BaseCommand {
4242
if (!opts || !opts.keyManager) throw new IllegalArgumentError('An instance of core/KeyManager is required', opts.keyManager)
4343
if (!opts || !opts.accountManager) throw new IllegalArgumentError('An instance of core/AccountManager is required', opts.accountManager)
4444
if (!opts || !opts.profileManager) throw new IllegalArgumentError('An instance of ProfileManager is required', opts.profileManager)
45+
if (!opts || !opts.certificateManager) throw new IllegalArgumentError('An instance of CertificateManager is required', opts.certificateManager)
4546

4647
this.accountManager = opts.accountManager
4748
this._portForwards = []
@@ -55,6 +56,7 @@ export class NodeCommand extends BaseCommand {
5556
k8: opts.k8,
5657
keyManager: opts.keyManager,
5758
chartManager: opts.chartManager,
59+
certificateManager: opts.certificateManager,
5860
parent: this
5961
})
6062

src/commands/node/tasks.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
* limitations under the License.
1515
*
1616
*/
17-
18-
1917
import {
2018
type ChartManager,
2119
type ConfigManager,
@@ -26,7 +24,8 @@ import {
2624
Task,
2725
Templates,
2826
Zippy,
29-
type AccountManager
27+
type AccountManager,
28+
type CertificateManager
3029
} from '../../core/index.ts'
3130
import {
3231
DEFAULT_NETWORK_NODE_NAME,
@@ -82,12 +81,13 @@ export class NodeCommandTasks {
8281
private readonly k8: K8
8382
private readonly parent: NodeCommand
8483
private readonly chartManager: ChartManager
84+
private readonly certificateManager: CertificateManager
8585

8686
private readonly prepareValuesFiles: any
8787

8888
constructor (opts: { logger: SoloLogger; accountManager: AccountManager; configManager: ConfigManager,
8989
k8: K8, platformInstaller: PlatformInstaller, keyManager: KeyManager, profileManager: ProfileManager,
90-
chartManager: ChartManager, parent: NodeCommand}
90+
chartManager: ChartManager, certificateManager: CertificateManager, parent: NodeCommand}
9191
) {
9292
if (!opts || !opts.accountManager) throw new IllegalArgumentError('An instance of core/AccountManager is required', opts.accountManager as any)
9393
if (!opts || !opts.configManager) throw new Error('An instance of core/ConfigManager is required')
@@ -96,6 +96,8 @@ export class NodeCommandTasks {
9696
if (!opts || !opts.platformInstaller) throw new IllegalArgumentError('An instance of core/PlatformInstaller is required', opts.platformInstaller)
9797
if (!opts || !opts.keyManager) throw new IllegalArgumentError('An instance of core/KeyManager is required', opts.keyManager)
9898
if (!opts || !opts.profileManager) throw new IllegalArgumentError('An instance of ProfileManager is required', opts.profileManager)
99+
if (!opts || !opts.certificateManager) throw new IllegalArgumentError('An instance of CertificateManager is required', opts.certificateManager)
100+
99101

100102
this.accountManager = opts.accountManager
101103
this.configManager = opts.configManager
@@ -106,6 +108,7 @@ export class NodeCommandTasks {
106108
this.profileManager = opts.profileManager
107109
this.keyManager = opts.keyManager
108110
this.chartManager = opts.chartManager
111+
this.certificateManager = opts.certificateManager
109112
this.prepareValuesFiles = opts.parent.prepareValuesFiles.bind(opts.parent)
110113
}
111114

@@ -414,6 +417,20 @@ export class NodeCommandTasks {
414417
}, (ctx: any) => !ctx.config.generateTlsKeys)
415418
}
416419

420+
copyGrpcTlsCertificates () {
421+
return new Task('Copy gRPC TLS Certificates',
422+
(ctx: { config: NodeAddConfigClass }, parentTask: ListrTaskWrapper<any, any, any>) =>
423+
this.certificateManager.buildCopyTlsCertificatesTasks(
424+
parentTask,
425+
ctx.config.grpcTlsCertificatePath,
426+
ctx.config.grpcWebTlsCertificatePath,
427+
ctx.config.grpcTlsKeyPath,
428+
ctx.config.grpcWebTlsKeyPath
429+
),
430+
(ctx: any) => !ctx.config.grpcTlsCertificatePath && !ctx.config.grpcWebTlsCertificatePath
431+
)
432+
}
433+
417434
_loadPermCertificate (certFullPath: string) {
418435
const certPem = fs.readFileSync(certFullPath).toString()
419436
const decodedDers = x509.PemConverter.decode(certPem)

src/commands/prompts.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,40 @@ export async function promptOutputDir (task: ListrTaskWrapper<any, any, any>, in
462462
flags.outputDir.name)
463463
}
464464

465+
//! ------------- Node Proxy Certificates ------------- !//
466+
467+
export async function promptGrpcTlsCertificatePath (task: ListrTaskWrapper<any, any, any>, input: any) {
468+
return await promptText(task, input,
469+
flags.grpcTlsCertificatePath.definition.defaultValue,
470+
'Enter node alias and path to TLS certificate for gRPC (ex. nodeAlias=path )',
471+
null,
472+
flags.grpcTlsCertificatePath.name)
473+
}
474+
475+
export async function promptGrpcWebTlsCertificatePath (task: ListrTaskWrapper<any, any, any>, input: any) {
476+
return await promptText(task, input,
477+
flags.grpcWebTlsCertificatePath.definition.defaultValue,
478+
'Enter node alias and path to TLS certificate for gGRPC web (ex. nodeAlias=path )',
479+
null,
480+
flags.grpcWebTlsCertificatePath.name)
481+
}
482+
483+
export async function promptGrpcTlsKeyPath (task: ListrTaskWrapper<any, any, any>, input: any) {
484+
return await promptText(task, input,
485+
flags.grpcTlsKeyPath.definition.defaultValue,
486+
'Enter node alias and path to TLS certificate key for gRPC (ex. nodeAlias=path )',
487+
null,
488+
flags.grpcTlsKeyPath.name)
489+
}
490+
491+
export async function promptGrpcWebTlsKeyPath (task: ListrTaskWrapper<any, any, any>, input: any) {
492+
return await promptText(task, input,
493+
flags.grpcWebTlsKeyPath.definition.defaultValue,
494+
'Enter node alias and path to TLS certificate key for gGRPC Web (ex. nodeAlias=path )',
495+
null,
496+
flags.grpcWebTlsKeyPath.name)
497+
}
498+
465499
export function getPromptMap (): Map<string, Function> {
466500
return new Map()
467501
.set(flags.accountId.name, promptAccountId)
@@ -506,6 +540,12 @@ export function getPromptMap (): Map<string, Function> {
506540
.set(flags.hederaExplorerVersion, promptHederaExplorerVersion)
507541
.set(flags.inputDir.name, promptInputDir)
508542
.set(flags.outputDir.name, promptOutputDir)
543+
544+
//! Node Proxy Certificates
545+
.set(flags.grpcTlsCertificatePath.name, promptGrpcTlsCertificatePath)
546+
.set(flags.grpcWebTlsCertificatePath.name, promptGrpcWebTlsCertificatePath)
547+
.set(flags.grpcTlsKeyPath.name, promptGrpcTlsKeyPath)
548+
.set(flags.grpcWebTlsKeyPath.name, promptGrpcWebTlsKeyPath)
509549
}
510550

511551
// build the prompt registry

0 commit comments

Comments
 (0)