@fjall/components-infrastructure 0.102.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/lambda-assets/cert-generator/asset/index.js +17948 -0
- package/dist/lib/lambda-assets/cert-generator/asset/package.json +4 -0
- package/dist/lib/patterns/aws/clickhouseDatabase.d.ts +37 -0
- package/dist/lib/patterns/aws/clickhouseDatabase.js +120 -19
- package/dist/lib/patterns/aws/clickhouseTls/index.d.ts +1 -0
- package/dist/lib/patterns/aws/clickhouseTls/index.js +1 -0
- package/dist/lib/patterns/aws/clickhouseTls/types.d.ts +48 -0
- package/dist/lib/resources/aws/database/clickhouseConstants.d.ts +21 -0
- package/dist/lib/resources/aws/database/clickhouseConstants.js +21 -0
- package/dist/lib/resources/aws/database/clickhouseSecurityGroup.d.ts +2 -0
- package/dist/lib/resources/aws/database/clickhouseSecurityGroup.js +2 -0
- package/dist/lib/resources/aws/database/clickhouseUserData.d.ts +21 -0
- package/dist/lib/resources/aws/database/clickhouseUserData.js +48 -3
- package/dist/lib/resources/aws/database/clickhouseXmlRenderer.d.ts +1 -1
- package/dist/lib/resources/aws/database/clickhouseXmlRenderer.js +1 -1
- package/dist/lib/resources/aws/secrets/index.d.ts +2 -0
- package/dist/lib/resources/aws/secrets/index.js +2 -0
- package/dist/lib/resources/aws/secrets/tlsCaSecret.d.ts +13 -0
- package/dist/lib/resources/aws/secrets/tlsCaSecret.js +15 -0
- package/dist/lib/resources/aws/secrets/tlsServerSecret.d.ts +15 -0
- package/dist/lib/resources/aws/secrets/tlsServerSecret.js +17 -0
- package/dist/lib/resources/aws/utilities/index.d.ts +1 -0
- package/dist/lib/resources/aws/utilities/index.js +1 -0
- package/dist/lib/resources/aws/utilities/tlsCertGenerator.d.ts +33 -0
- package/dist/lib/resources/aws/utilities/tlsCertGenerator.js +67 -0
- package/package.json +7 -5
- package/dist/lib/config/aws/__t17fixture.js +0 -3
- package/dist/lib/config/aws/__t17fixtureType.d.ts +0 -2
- package/dist/lib/config/aws/__t17fixtureType.js +0 -1
- package/dist/lib/config/aws/eventBus.d.ts +0 -7
- package/dist/lib/config/aws/eventBus.js +0 -21
- package/dist/lib/config/aws/identityCenterGroupMembership.d.ts +0 -10
- package/dist/lib/config/aws/identityCenterGroupMembership.js +0 -102
- package/dist/lib/config/aws/securityBaseline.d.ts +0 -15
- package/dist/lib/config/aws/securityBaseline.js +0 -27
- package/dist/lib/patterns/aws/_eslint_test_tmp/leak.d.ts +0 -1
- package/dist/lib/patterns/aws/_eslint_test_tmp/leak.js +0 -4
- package/dist/lib/patterns/aws/managedIdentityCenter.d.ts +0 -4
- package/dist/lib/patterns/aws/managedIdentityCenter.js +0 -19
- package/dist/lib/patterns/aws/subdomainHostedZone.d.ts +0 -9
- package/dist/lib/patterns/aws/subdomainHostedZone.js +0 -34
- package/dist/lib/resources/aws/analytics/clickhouse.d.ts +0 -15
- package/dist/lib/resources/aws/analytics/clickhouse.js +0 -310
- package/dist/lib/resources/aws/analytics/clickhouseAlarms.d.ts +0 -49
- package/dist/lib/resources/aws/analytics/clickhouseAlarms.js +0 -140
- package/dist/lib/resources/aws/analytics/clickhouseConstants.d.ts +0 -73
- package/dist/lib/resources/aws/analytics/clickhouseConstants.js +0 -89
- package/dist/lib/resources/aws/analytics/clickhouseSecurityGroup.d.ts +0 -13
- package/dist/lib/resources/aws/analytics/clickhouseSecurityGroup.js +0 -28
- package/dist/lib/resources/aws/analytics/clickhouseTypes.d.ts +0 -59
- package/dist/lib/resources/aws/analytics/clickhouseTypes.js +0 -1
- package/dist/lib/resources/aws/analytics/clickhouseUserData.d.ts +0 -6
- package/dist/lib/resources/aws/analytics/clickhouseUserData.js +0 -299
- package/dist/lib/resources/aws/analytics/index.d.ts +0 -4
- package/dist/lib/resources/aws/analytics/index.js +0 -2
- package/dist/lib/resources/aws/compute/__tmp__/regression-shape.d.ts +0 -2
- package/dist/lib/resources/aws/compute/__tmp__/regression-shape.js +0 -11
- package/dist/lib/resources/aws/messaging/defaultEventBus.d.ts +0 -7
- package/dist/lib/resources/aws/messaging/defaultEventBus.js +0 -21
- package/dist/lib/resources/aws/networking/domain.d.ts +0 -13
- package/dist/lib/resources/aws/networking/domain.js +0 -100
- package/dist/lib/synth_dump.d.ts +0 -1
- package/dist/lib/synth_dump.js +0 -42
- package/dist/lib/utils/bastionFactory.d.ts +0 -10
- package/dist/lib/utils/bastionFactory.js +0 -29
- package/dist/lib/utils/constructMap.d.ts +0 -33
- package/dist/lib/utils/constructMap.js +0 -154
- package/dist/lib/utils/dnsRecords.d.ts +0 -4
- package/dist/lib/utils/dnsRecords.js +0 -104
- /package/dist/lib/{config/aws/__t17fixture.d.ts → patterns/aws/clickhouseTls/types.js} +0 -0
|
@@ -4,6 +4,8 @@ import { type IBucket } from "aws-cdk-lib/aws-s3";
|
|
|
4
4
|
import { Construct } from "constructs";
|
|
5
5
|
import { Secret } from "../../resources/aws/secrets/secret.js";
|
|
6
6
|
import { type ClickHouseSchemaAdmin, type ProfileSpec } from "../../resources/aws/database/clickhouseSchemas.js";
|
|
7
|
+
import { type ISecret } from "aws-cdk-lib/aws-secretsmanager";
|
|
8
|
+
import type { ClickHouseTlsOptions } from "./clickhouseTls/index.js";
|
|
7
9
|
import { type ClickHouseMigrationsConfig, type IClickHouseDatabase } from "./interfaces/database.js";
|
|
8
10
|
import { type ISecurityGroupConnector } from "./interfaces/connector.js";
|
|
9
11
|
import { type MigrationContributions } from "./interfaces/migrationContributor.js";
|
|
@@ -104,6 +106,15 @@ export interface ClickHouseDatabaseProps {
|
|
|
104
106
|
* records in `_schema_migrations.ch_version`.
|
|
105
107
|
*/
|
|
106
108
|
migrations?: ClickHouseMigrationsConfig;
|
|
109
|
+
/**
|
|
110
|
+
* TLS configuration. Default: `{ mode: "self-signed" }` — mints an ECDSA
|
|
111
|
+
* P-256 CA + leaf cert at deploy via a Lambda custom resource, stores both
|
|
112
|
+
* PEMs in Secrets Manager, and the EC2 user-data materialises them at boot
|
|
113
|
+
* for the CH server to read. Set `mode: "imported"` to bring your own CA;
|
|
114
|
+
* `mode: "none"` (acknowledgement literal required) opts out of TLS.
|
|
115
|
+
* See ./clickhouseTls/types.ts.
|
|
116
|
+
*/
|
|
117
|
+
tls?: ClickHouseTlsOptions;
|
|
107
118
|
}
|
|
108
119
|
/**
|
|
109
120
|
* ClickHouse analytics database wrapper implementing IClickHouseDatabase.
|
|
@@ -121,6 +132,25 @@ export declare class ClickHouseDatabase extends Construct implements IClickHouse
|
|
|
121
132
|
readonly additionalTcpPorts: number[];
|
|
122
133
|
readonly id: string;
|
|
123
134
|
readonly connections: Connections;
|
|
135
|
+
/**
|
|
136
|
+
* CA cert secret. Self-signed: minted by `TlsCertGenerator`. Imported: the
|
|
137
|
+
* user-supplied ISecret. `undefined` when `tls.mode === "none"`.
|
|
138
|
+
* SecretString shape: raw PEM (single cert, no JSON wrapping).
|
|
139
|
+
*/
|
|
140
|
+
readonly tlsCaSecret: ISecret | undefined;
|
|
141
|
+
/**
|
|
142
|
+
* Server cert + key secret. Self-signed: minted by `TlsCertGenerator`.
|
|
143
|
+
* Imported: the user-supplied ISecret. `undefined` when `tls.mode === "none"`.
|
|
144
|
+
* SecretString shape: JSON `{ "cert": "<leaf-pem>", "key": "<key-pem>" }`.
|
|
145
|
+
*/
|
|
146
|
+
readonly tlsServerSecret: ISecret | undefined;
|
|
147
|
+
/**
|
|
148
|
+
* SHA-256 of the CA cert PEM. CFN token in self-signed mode (from the cert
|
|
149
|
+
* generator custom resource Data attribute). Plain string in imported mode
|
|
150
|
+
* when supplied. `undefined` when `tls.mode === "none"` or when imported
|
|
151
|
+
* without a digest. Surface on consumers as a task-replacement trigger.
|
|
152
|
+
*/
|
|
153
|
+
readonly caCertSha256: string | undefined;
|
|
124
154
|
constructor(scope: Construct, id: string, props: ClickHouseDatabaseProps);
|
|
125
155
|
/**
|
|
126
156
|
* Returns the Fjall `Secret` wrapper for the named user. Throws with a
|
|
@@ -138,6 +168,13 @@ export declare class ClickHouseDatabase extends Construct implements IClickHouse
|
|
|
138
168
|
getHttpPort(): number;
|
|
139
169
|
getNativePort(): number;
|
|
140
170
|
getHostEndpoint(): string;
|
|
171
|
+
/**
|
|
172
|
+
* Canonical URL for the HTTP(S) interface. Scheme is `https` when TLS is
|
|
173
|
+
* active (default), `http` only when `tls: { mode: "none", … }` is set.
|
|
174
|
+
* Port matches: HTTPS (8443) under TLS, HTTP (8123) otherwise.
|
|
175
|
+
*/
|
|
176
|
+
getUrl(): string;
|
|
177
|
+
/** @deprecated Alias for {@link getUrl}. Removed in a follow-up landing. */
|
|
141
178
|
getHttpUrl(): string;
|
|
142
179
|
getNativeUrl(): string;
|
|
143
180
|
getDatabaseName(): string;
|
|
@@ -20,7 +20,8 @@ import { buildClickHouseEntrypointWrapper, buildClickHouseUserData, generateUser
|
|
|
20
20
|
import { toPascalCase } from "../../utils/capitaliseString.js";
|
|
21
21
|
import { ClickHouseSchemaAdminSchema, ManagedPasswordNameSchema, ProfileSpecSchema, ClickHouseDefaultProfiles, PROFILE_NAME_PATTERN } from "../../resources/aws/database/clickhouseSchemas.js";
|
|
22
22
|
import { inferAmiHardwareType } from "../../resources/aws/compute/ecsConstants.js";
|
|
23
|
-
import { CLICKHOUSE_DATABASE_NAME, DEFAULT_CLICKHOUSE_INSTANCE_TYPE, CLICKHOUSE_IMAGE, CLICKHOUSE_EBS_VOLUME_SIZE_GB, CLICKHOUSE_EBS_IOPS, CLICKHOUSE_EBS_THROUGHPUT_MBPS, CLICKHOUSE_TASK_MEMORY_MIB, CLICKHOUSE_HTTP_PORT, CLICKHOUSE_NATIVE_PORT, CLICKHOUSE_PROMETHEUS_PORT, CLICKHOUSE_DATA_MOUNT_PATH, CLICKHOUSE_SECRET_OPTIONS, CLICKHOUSE_SERVER_ROLE_TAG, clickHouseUserSecretName, CLICKHOUSE_HEALTH_CHECK, CLICKHOUSE_STOP_TIMEOUT_SECONDS, CLICKHOUSE_EBS_DEVICE_NAME, CLICKHOUSE_CONFIG_SUBDIR, CLICKHOUSE_USERS_SUBDIR, userPasswordEnvName, OPTIMISE_FINAL_SCHEDULE, REPLACING_MERGE_TREE_TABLES, OPTIMISE_MV_TABLES, CLICKHOUSE_CLOUDMAP_SERVICE_NAME, CLICKHOUSE_SERVER_CONTAINER_NAME, OPTIMISE_TASK_MEMORY_MIB, OPTIMISE_TASK_CPU_UNITS, BACKUP_SCHEDULE, BACKUP_TASK_MEMORY_MIB, BACKUP_TASK_CPU_UNITS, BACKUP_RETENTION_DAYS } from "../../resources/aws/database/clickhouseConstants.js";
|
|
23
|
+
import { CLICKHOUSE_DATABASE_NAME, DEFAULT_CLICKHOUSE_INSTANCE_TYPE, CLICKHOUSE_IMAGE, CLICKHOUSE_EBS_VOLUME_SIZE_GB, CLICKHOUSE_EBS_IOPS, CLICKHOUSE_EBS_THROUGHPUT_MBPS, CLICKHOUSE_TASK_MEMORY_MIB, CLICKHOUSE_HTTP_PORT, CLICKHOUSE_HTTPS_PORT, CLICKHOUSE_NATIVE_PORT, CLICKHOUSE_TCP_SECURE_PORT, CLICKHOUSE_TLS_CERT_MOUNT_PATH, CLICKHOUSE_PROMETHEUS_PORT, CLICKHOUSE_DATA_MOUNT_PATH, CLICKHOUSE_SECRET_OPTIONS, CLICKHOUSE_SERVER_ROLE_TAG, clickHouseUserSecretName, CLICKHOUSE_HEALTH_CHECK, CLICKHOUSE_STOP_TIMEOUT_SECONDS, CLICKHOUSE_EBS_DEVICE_NAME, CLICKHOUSE_CONFIG_SUBDIR, CLICKHOUSE_USERS_SUBDIR, userPasswordEnvName, OPTIMISE_FINAL_SCHEDULE, REPLACING_MERGE_TREE_TABLES, OPTIMISE_MV_TABLES, CLICKHOUSE_CLOUDMAP_SERVICE_NAME, CLICKHOUSE_SERVER_CONTAINER_NAME, OPTIMISE_TASK_MEMORY_MIB, OPTIMISE_TASK_CPU_UNITS, BACKUP_SCHEDULE, BACKUP_TASK_MEMORY_MIB, BACKUP_TASK_CPU_UNITS, BACKUP_RETENTION_DAYS } from "../../resources/aws/database/clickhouseConstants.js";
|
|
24
|
+
import { TlsCertGenerator } from "../../resources/aws/utilities/tlsCertGenerator.js";
|
|
24
25
|
import { EcsCompute } from "./computeEcs.js";
|
|
25
26
|
/**
|
|
26
27
|
* Resolve the ECS desired task count for the ClickHouse service.
|
|
@@ -81,9 +82,30 @@ function createClickHouseUserSecret(scope, name) {
|
|
|
81
82
|
export class ClickHouseDatabase extends Construct {
|
|
82
83
|
databaseType = "ClickHouse";
|
|
83
84
|
connectorType = "relational";
|
|
84
|
-
additionalTcpPorts
|
|
85
|
+
additionalTcpPorts;
|
|
85
86
|
id;
|
|
86
87
|
connections;
|
|
88
|
+
/**
|
|
89
|
+
* CA cert secret. Self-signed: minted by `TlsCertGenerator`. Imported: the
|
|
90
|
+
* user-supplied ISecret. `undefined` when `tls.mode === "none"`.
|
|
91
|
+
* SecretString shape: raw PEM (single cert, no JSON wrapping).
|
|
92
|
+
*/
|
|
93
|
+
tlsCaSecret;
|
|
94
|
+
/**
|
|
95
|
+
* Server cert + key secret. Self-signed: minted by `TlsCertGenerator`.
|
|
96
|
+
* Imported: the user-supplied ISecret. `undefined` when `tls.mode === "none"`.
|
|
97
|
+
* SecretString shape: JSON `{ "cert": "<leaf-pem>", "key": "<key-pem>" }`.
|
|
98
|
+
*/
|
|
99
|
+
tlsServerSecret;
|
|
100
|
+
/**
|
|
101
|
+
* SHA-256 of the CA cert PEM. CFN token in self-signed mode (from the cert
|
|
102
|
+
* generator custom resource Data attribute). Plain string in imported mode
|
|
103
|
+
* when supplied. `undefined` when `tls.mode === "none"` or when imported
|
|
104
|
+
* without a digest. Surface on consumers as a task-replacement trigger.
|
|
105
|
+
*/
|
|
106
|
+
caCertSha256;
|
|
107
|
+
#httpPort;
|
|
108
|
+
#nativePort;
|
|
87
109
|
#users;
|
|
88
110
|
#schemaAdmin;
|
|
89
111
|
#managedPasswordNames;
|
|
@@ -157,7 +179,46 @@ export class ClickHouseDatabase extends Construct {
|
|
|
157
179
|
(props.backupRetentionDays < 1 || props.backupRetentionDays > 3650)) {
|
|
158
180
|
throw new Error(`ClickHouseDatabase: backupRetentionDays must be between 1 and 3650; got ${props.backupRetentionDays}.`);
|
|
159
181
|
}
|
|
160
|
-
const
|
|
182
|
+
const tlsOptions = props.tls ?? {
|
|
183
|
+
mode: "self-signed"
|
|
184
|
+
};
|
|
185
|
+
const tlsActive = tlsOptions.mode !== "none";
|
|
186
|
+
const httpPort = tlsActive ? CLICKHOUSE_HTTPS_PORT : CLICKHOUSE_HTTP_PORT;
|
|
187
|
+
const nativePort = tlsActive
|
|
188
|
+
? CLICKHOUSE_TCP_SECURE_PORT
|
|
189
|
+
: CLICKHOUSE_NATIVE_PORT;
|
|
190
|
+
let tlsCaSecret;
|
|
191
|
+
let tlsServerSecret;
|
|
192
|
+
let caCertSha256;
|
|
193
|
+
if (tlsOptions.mode === "self-signed") {
|
|
194
|
+
const namespaceName = App.getInstance().getNamespace().namespaceName;
|
|
195
|
+
const hostname = `${CLICKHOUSE_CLOUDMAP_SERVICE_NAME}.${namespaceName}`;
|
|
196
|
+
const certGen = new TlsCertGenerator(this, "TlsCertGenerator", {
|
|
197
|
+
appName: App.getInstance().getName(),
|
|
198
|
+
hostname,
|
|
199
|
+
...(tlsOptions.caValidityYears !== undefined && {
|
|
200
|
+
caValidityYears: tlsOptions.caValidityYears
|
|
201
|
+
}),
|
|
202
|
+
...(tlsOptions.leafValidityYears !== undefined && {
|
|
203
|
+
leafValidityYears: tlsOptions.leafValidityYears
|
|
204
|
+
})
|
|
205
|
+
});
|
|
206
|
+
tlsCaSecret = certGen.caSecret.secret;
|
|
207
|
+
tlsServerSecret = certGen.serverSecret.secret;
|
|
208
|
+
caCertSha256 = certGen.caCertSha256;
|
|
209
|
+
}
|
|
210
|
+
else if (tlsOptions.mode === "imported") {
|
|
211
|
+
tlsCaSecret = tlsOptions.caCertSecret;
|
|
212
|
+
tlsServerSecret = tlsOptions.serverCertSecret;
|
|
213
|
+
caCertSha256 = tlsOptions.caCertSha256;
|
|
214
|
+
}
|
|
215
|
+
this.tlsCaSecret = tlsCaSecret;
|
|
216
|
+
this.tlsServerSecret = tlsServerSecret;
|
|
217
|
+
this.caCertSha256 = caCertSha256;
|
|
218
|
+
this.additionalTcpPorts = [nativePort];
|
|
219
|
+
this.#httpPort = httpPort;
|
|
220
|
+
this.#nativePort = nativePort;
|
|
221
|
+
const securityGroup = createClickHouseSecurityGroup(this, vpc, nativePort);
|
|
161
222
|
const userSecrets = new Map();
|
|
162
223
|
for (const name of allUserNames) {
|
|
163
224
|
userSecrets.set(name, createClickHouseUserSecret(this, name));
|
|
@@ -200,6 +261,13 @@ export class ClickHouseDatabase extends Construct {
|
|
|
200
261
|
bucketName: coldTierBucket.bucketName,
|
|
201
262
|
region: Stack.of(this).region
|
|
202
263
|
}
|
|
264
|
+
}),
|
|
265
|
+
tlsActive,
|
|
266
|
+
...(tlsActive &&
|
|
267
|
+
tlsCaSecret !== undefined &&
|
|
268
|
+
tlsServerSecret !== undefined && {
|
|
269
|
+
caSecretArn: tlsCaSecret.secretArn,
|
|
270
|
+
serverSecretArn: tlsServerSecret.secretArn
|
|
203
271
|
})
|
|
204
272
|
}));
|
|
205
273
|
// Source.data wraps the content in a zip; the default `extract: true`
|
|
@@ -239,9 +307,10 @@ export class ClickHouseDatabase extends Construct {
|
|
|
239
307
|
"--host",
|
|
240
308
|
clickHouseHost,
|
|
241
309
|
"--port",
|
|
242
|
-
String(
|
|
310
|
+
String(nativePort),
|
|
243
311
|
"--user",
|
|
244
312
|
schemaAdmin.name,
|
|
313
|
+
...(tlsActive ? ["--secure", "--accept-invalid-certificate"] : []),
|
|
245
314
|
"--query",
|
|
246
315
|
`${optimiseQuery};`
|
|
247
316
|
],
|
|
@@ -263,7 +332,7 @@ export class ClickHouseDatabase extends Construct {
|
|
|
263
332
|
"sh",
|
|
264
333
|
"-c",
|
|
265
334
|
// Password via CLICKHOUSE_PASSWORD env, not --password on argv (argv → /proc/<pid>/cmdline).
|
|
266
|
-
`STAMP=$(date +%Y%m%d-%H%M%S) && clickhouse-client --host ${clickHouseHost} --port ${
|
|
335
|
+
`STAMP=$(date +%Y%m%d-%H%M%S) && clickhouse-client --host ${clickHouseHost} --port ${nativePort} --user ${schemaAdmin.name}${tlsActive ? " --secure --accept-invalid-certificate" : ""} --query "BACKUP DATABASE ${CLICKHOUSE_DATABASE_NAME} TO S3('${backupDestUrl}weekly-$STAMP/')"`
|
|
267
336
|
],
|
|
268
337
|
secrets: {
|
|
269
338
|
CLICKHOUSE_PASSWORD: EcsSecret.fromSecretsManager(adminSecret.secret, "password")
|
|
@@ -331,12 +400,12 @@ export class ClickHouseDatabase extends Construct {
|
|
|
331
400
|
secretsImport: clickHouseContainerSecrets,
|
|
332
401
|
portMappings: [
|
|
333
402
|
{
|
|
334
|
-
containerPort:
|
|
335
|
-
hostPort:
|
|
403
|
+
containerPort: httpPort,
|
|
404
|
+
hostPort: httpPort
|
|
336
405
|
},
|
|
337
406
|
{
|
|
338
|
-
containerPort:
|
|
339
|
-
hostPort:
|
|
407
|
+
containerPort: nativePort,
|
|
408
|
+
hostPort: nativePort
|
|
340
409
|
},
|
|
341
410
|
{
|
|
342
411
|
containerPort: CLICKHOUSE_PROMETHEUS_PORT,
|
|
@@ -360,12 +429,24 @@ export class ClickHouseDatabase extends Construct {
|
|
|
360
429
|
hostSourcePath: `${CLICKHOUSE_DATA_MOUNT_PATH}/${CLICKHOUSE_USERS_SUBDIR}`,
|
|
361
430
|
mountPath: "/etc/clickhouse-server/users.d",
|
|
362
431
|
readOnly: true
|
|
363
|
-
}
|
|
432
|
+
},
|
|
433
|
+
...(tlsActive
|
|
434
|
+
? [
|
|
435
|
+
{
|
|
436
|
+
name: "clickhouse-certs",
|
|
437
|
+
hostSourcePath: `${CLICKHOUSE_DATA_MOUNT_PATH}/server-certs`,
|
|
438
|
+
mountPath: CLICKHOUSE_TLS_CERT_MOUNT_PATH,
|
|
439
|
+
readOnly: true
|
|
440
|
+
}
|
|
441
|
+
]
|
|
442
|
+
: [])
|
|
364
443
|
],
|
|
365
444
|
healthCheck: {
|
|
366
445
|
command: [
|
|
367
446
|
"CMD-SHELL",
|
|
368
|
-
|
|
447
|
+
tlsActive
|
|
448
|
+
? `wget -q --no-check-certificate -O /dev/null https://127.0.0.1:${httpPort}/ping || exit 1`
|
|
449
|
+
: `wget -q -O /dev/null http://127.0.0.1:${httpPort}/ping || exit 1`
|
|
369
450
|
],
|
|
370
451
|
interval: CLICKHOUSE_HEALTH_CHECK.INTERVAL_SECONDS,
|
|
371
452
|
timeout: CLICKHOUSE_HEALTH_CHECK.TIMEOUT_SECONDS,
|
|
@@ -390,6 +471,10 @@ export class ClickHouseDatabase extends Construct {
|
|
|
390
471
|
instanceRole.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMManagedInstanceCore"));
|
|
391
472
|
backupBucket.grantRead(instanceRole, "config/*");
|
|
392
473
|
adminSecret.secret.grantRead(instanceRole);
|
|
474
|
+
if (tlsCaSecret !== undefined)
|
|
475
|
+
tlsCaSecret.grantRead(instanceRole);
|
|
476
|
+
if (tlsServerSecret !== undefined)
|
|
477
|
+
tlsServerSecret.grantRead(instanceRole);
|
|
393
478
|
const adminSecretName = clickHouseUserSecretName(schemaAdmin.name);
|
|
394
479
|
// Password via CLICKHOUSE_CLIENT_PASSWORD env, not --password on argv
|
|
395
480
|
// (argv → /proc/<pid>/cmdline). `jq -r .password` on an empty pipeline
|
|
@@ -404,7 +489,7 @@ export class ClickHouseDatabase extends Construct {
|
|
|
404
489
|
`ADMIN_PASSWORD=$(aws secretsmanager get-secret-value --secret-id "${adminSecretName}" --query SecretString --output text | jq -r .password)`,
|
|
405
490
|
`if [ -z "$ADMIN_PASSWORD" ] || [ "$ADMIN_PASSWORD" = "null" ]; then echo "fjall:reload:status=failed reason=password-fetch" >&2; exit 1; fi`,
|
|
406
491
|
`mv /var/lib/clickhouse/users.d/fjall.xml.new /var/lib/clickhouse/users.d/fjall.xml`,
|
|
407
|
-
`docker exec -i -e CLICKHOUSE_CLIENT_PASSWORD="$ADMIN_PASSWORD" "$CONTAINER" clickhouse-client --port
|
|
492
|
+
`docker exec -i -e CLICKHOUSE_CLIENT_PASSWORD="$ADMIN_PASSWORD" "$CONTAINER" clickhouse-client${tlsActive ? " --secure --accept-invalid-certificate" : ""} --port ${nativePort} --user ${schemaAdmin.name} -q "SYSTEM RELOAD USERS"`
|
|
408
493
|
].join("\n");
|
|
409
494
|
const region = Stack.of(this).region;
|
|
410
495
|
const account = Stack.of(this).account;
|
|
@@ -456,15 +541,21 @@ export class ClickHouseDatabase extends Construct {
|
|
|
456
541
|
reloadCustomResource.node.addDependency(usersConfigDeployment);
|
|
457
542
|
this.connections = new Connections({
|
|
458
543
|
securityGroups: [securityGroup],
|
|
459
|
-
defaultPort: Port.tcp(
|
|
544
|
+
defaultPort: Port.tcp(httpPort)
|
|
460
545
|
});
|
|
461
546
|
const declareOutput = (name, value) => {
|
|
462
547
|
const output = new CfnOutput(this, name, { value });
|
|
463
548
|
output.overrideLogicalId(name);
|
|
464
549
|
};
|
|
465
550
|
declareOutput("Endpoint", clickHouseHost);
|
|
466
|
-
declareOutput("HttpPort", String(
|
|
467
|
-
declareOutput("NativePort", String(
|
|
551
|
+
declareOutput("HttpPort", String(httpPort));
|
|
552
|
+
declareOutput("NativePort", String(nativePort));
|
|
553
|
+
if (tlsActive) {
|
|
554
|
+
declareOutput("TlsActive", "true");
|
|
555
|
+
}
|
|
556
|
+
if (caCertSha256 !== undefined) {
|
|
557
|
+
declareOutput("CaCertSha256", caCertSha256);
|
|
558
|
+
}
|
|
468
559
|
declareOutput("DatabaseName", CLICKHOUSE_DATABASE_NAME);
|
|
469
560
|
for (const [name, secret] of userSecrets) {
|
|
470
561
|
declareOutput(`${toPascalCase(name)}SecretArn`, secret.secret.secretArn);
|
|
@@ -497,19 +588,29 @@ export class ClickHouseDatabase extends Construct {
|
|
|
497
588
|
return this.#users.get(name);
|
|
498
589
|
}
|
|
499
590
|
getHttpPort() {
|
|
500
|
-
return
|
|
591
|
+
return this.#httpPort;
|
|
501
592
|
}
|
|
502
593
|
getNativePort() {
|
|
503
|
-
return
|
|
594
|
+
return this.#nativePort;
|
|
504
595
|
}
|
|
505
596
|
getHostEndpoint() {
|
|
506
597
|
return `${CLICKHOUSE_CLOUDMAP_SERVICE_NAME}.${App.getInstance().getNamespace().namespaceName}`;
|
|
507
598
|
}
|
|
599
|
+
/**
|
|
600
|
+
* Canonical URL for the HTTP(S) interface. Scheme is `https` when TLS is
|
|
601
|
+
* active (default), `http` only when `tls: { mode: "none", … }` is set.
|
|
602
|
+
* Port matches: HTTPS (8443) under TLS, HTTP (8123) otherwise.
|
|
603
|
+
*/
|
|
604
|
+
getUrl() {
|
|
605
|
+
const scheme = this.tlsCaSecret !== undefined ? "https" : "http";
|
|
606
|
+
return `${scheme}://${this.getHostEndpoint()}:${this.#httpPort}`;
|
|
607
|
+
}
|
|
608
|
+
/** @deprecated Alias for {@link getUrl}. Removed in a follow-up landing. */
|
|
508
609
|
getHttpUrl() {
|
|
509
|
-
return
|
|
610
|
+
return this.getUrl();
|
|
510
611
|
}
|
|
511
612
|
getNativeUrl() {
|
|
512
|
-
return `tcp://${this.getHostEndpoint()}:${this
|
|
613
|
+
return `tcp://${this.getHostEndpoint()}:${this.#nativePort}`;
|
|
513
614
|
}
|
|
514
615
|
getDatabaseName() {
|
|
515
616
|
return CLICKHOUSE_DATABASE_NAME;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ISecret } from "aws-cdk-lib/aws-secretsmanager";
|
|
2
|
+
/**
|
|
3
|
+
* Self-signed mode (default). The construct mints an ECDSA P-256 CA + leaf
|
|
4
|
+
* cert at stack deploy via a custom-resource Lambda. Bumping
|
|
5
|
+
* `caValidityYears` / `leafValidityYears` triggers re-issuance on the next
|
|
6
|
+
* deploy.
|
|
7
|
+
*/
|
|
8
|
+
export interface ClickHouseSelfSignedTls {
|
|
9
|
+
readonly mode: "self-signed";
|
|
10
|
+
readonly caValidityYears?: number;
|
|
11
|
+
readonly leafValidityYears?: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Imported mode. Caller owns the CA + server-cert secrets in Secrets
|
|
15
|
+
* Manager. `serverCertSecret`'s SecretString MUST be JSON of the shape
|
|
16
|
+
* `{ "cert": "<pem>", "key": "<pem>" }`. `caCertSha256` is optional — when
|
|
17
|
+
* supplied the construct surfaces it as a CFN env-var token so consumers
|
|
18
|
+
* can wire it into containers for rotation-triggers-task-replacement
|
|
19
|
+
* parity with self-signed mode. Omit it and rotation requires manual task
|
|
20
|
+
* restart (documented in `aiDocs/patterns/clickhouse-tls-pattern.md § "Imported mode"`).
|
|
21
|
+
*/
|
|
22
|
+
export interface ClickHouseImportedTls {
|
|
23
|
+
readonly mode: "imported";
|
|
24
|
+
readonly caCertSecret: ISecret;
|
|
25
|
+
readonly serverCertSecret: ISecret;
|
|
26
|
+
readonly caCertSha256?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Plaintext escape hatch. Disables TLS entirely. The acknowledgement is a
|
|
30
|
+
* literal-string type so misuse fails at compile time — passing any other
|
|
31
|
+
* string fails `tsc --noEmit`.
|
|
32
|
+
*/
|
|
33
|
+
export interface ClickHouseNoTls {
|
|
34
|
+
readonly mode: "none";
|
|
35
|
+
readonly acknowledgement: "I understand plaintext-only ClickHouse fails SOC2 DP5";
|
|
36
|
+
}
|
|
37
|
+
export type ClickHouseTlsOptions = ClickHouseSelfSignedTls | ClickHouseImportedTls | ClickHouseNoTls;
|
|
38
|
+
/**
|
|
39
|
+
* The resolved TLS surface a `ClickHouseDatabase` carries after dispatch.
|
|
40
|
+
* Every field is `undefined` in `mode: "none"`. `certGeneratorFunction`
|
|
41
|
+
* is `undefined` in `mode: "imported"`. `caCertSha256` is `undefined` in
|
|
42
|
+
* `mode: "none"` AND in `mode: "imported"` unless the caller supplied it.
|
|
43
|
+
*/
|
|
44
|
+
export interface ClickHouseTlsResolved {
|
|
45
|
+
readonly caSecret: ISecret | undefined;
|
|
46
|
+
readonly serverSecret: ISecret | undefined;
|
|
47
|
+
readonly caCertSha256: string | undefined;
|
|
48
|
+
}
|
|
@@ -53,6 +53,27 @@ export declare const CLICKHOUSE_TASK_MEMORY_MIB = 3072;
|
|
|
53
53
|
export declare const CLICKHOUSE_HTTP_PORT = 8123;
|
|
54
54
|
export declare const CLICKHOUSE_NATIVE_PORT = 9000;
|
|
55
55
|
export declare const CLICKHOUSE_PROMETHEUS_PORT = 9363;
|
|
56
|
+
/** TLS-mode ClickHouse ports.
|
|
57
|
+
* HTTPS replaces 8123; secure native protocol replaces 9000.
|
|
58
|
+
* See aiDocs/patterns/clickhouse-tls-pattern.md § "The contract". */
|
|
59
|
+
export declare const CLICKHOUSE_HTTPS_PORT = 8443;
|
|
60
|
+
export declare const CLICKHOUSE_TCP_SECURE_PORT = 9440;
|
|
61
|
+
/** Mount path inside the main ClickHouse container for the materialised
|
|
62
|
+
* TLS cert + key. The init container writes to a shared task-scoped volume
|
|
63
|
+
* mounted here read-only on the main container. */
|
|
64
|
+
export declare const CLICKHOUSE_TLS_CERT_MOUNT_PATH = "/etc/clickhouse-server/certs";
|
|
65
|
+
/** Task-scoped Docker volume name shared between the TLS init container
|
|
66
|
+
* (writer) and the main ClickHouse container (reader). */
|
|
67
|
+
export declare const CLICKHOUSE_TLS_CERT_VOLUME_NAME = "tls-certs";
|
|
68
|
+
/** UID:GID the official ClickHouse image runs as. Init container `chown`s
|
|
69
|
+
* the materialised cert files to this UID so the server can read them. */
|
|
70
|
+
export declare const CLICKHOUSE_UID = 101;
|
|
71
|
+
/** Digest-pinned alpine image used by the TLS init container. The init
|
|
72
|
+
* container needs `jq` (extracts `cert`+`key` from the server-cert JSON
|
|
73
|
+
* secret); alpine ships it via `apk add` — but pinning the digest avoids
|
|
74
|
+
* fetching `:latest` on every deploy. Bump in lockstep with renovate
|
|
75
|
+
* alerts; CI verifies the digest resolves. */
|
|
76
|
+
export declare const ALPINE_INIT_CONTAINER_IMAGE = "public.ecr.aws/docker/library/alpine:3.20@sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d";
|
|
56
77
|
/** EBS device name for the data volume (must match user data script). */
|
|
57
78
|
export declare const CLICKHOUSE_EBS_DEVICE_NAME = "/dev/xvdf";
|
|
58
79
|
/** EBS mount path on the EC2 host. */
|
|
@@ -53,6 +53,27 @@ export const CLICKHOUSE_TASK_MEMORY_MIB = 3072;
|
|
|
53
53
|
export const CLICKHOUSE_HTTP_PORT = 8123;
|
|
54
54
|
export const CLICKHOUSE_NATIVE_PORT = 9000;
|
|
55
55
|
export const CLICKHOUSE_PROMETHEUS_PORT = 9363;
|
|
56
|
+
/** TLS-mode ClickHouse ports.
|
|
57
|
+
* HTTPS replaces 8123; secure native protocol replaces 9000.
|
|
58
|
+
* See aiDocs/patterns/clickhouse-tls-pattern.md § "The contract". */
|
|
59
|
+
export const CLICKHOUSE_HTTPS_PORT = 8443;
|
|
60
|
+
export const CLICKHOUSE_TCP_SECURE_PORT = 9440;
|
|
61
|
+
/** Mount path inside the main ClickHouse container for the materialised
|
|
62
|
+
* TLS cert + key. The init container writes to a shared task-scoped volume
|
|
63
|
+
* mounted here read-only on the main container. */
|
|
64
|
+
export const CLICKHOUSE_TLS_CERT_MOUNT_PATH = "/etc/clickhouse-server/certs";
|
|
65
|
+
/** Task-scoped Docker volume name shared between the TLS init container
|
|
66
|
+
* (writer) and the main ClickHouse container (reader). */
|
|
67
|
+
export const CLICKHOUSE_TLS_CERT_VOLUME_NAME = "tls-certs";
|
|
68
|
+
/** UID:GID the official ClickHouse image runs as. Init container `chown`s
|
|
69
|
+
* the materialised cert files to this UID so the server can read them. */
|
|
70
|
+
export const CLICKHOUSE_UID = 101;
|
|
71
|
+
/** Digest-pinned alpine image used by the TLS init container. The init
|
|
72
|
+
* container needs `jq` (extracts `cert`+`key` from the server-cert JSON
|
|
73
|
+
* secret); alpine ships it via `apk add` — but pinning the digest avoids
|
|
74
|
+
* fetching `:latest` on every deploy. Bump in lockstep with renovate
|
|
75
|
+
* alerts; CI verifies the digest resolves. */
|
|
76
|
+
export const ALPINE_INIT_CONTAINER_IMAGE = "public.ecr.aws/docker/library/alpine:3.20@sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d";
|
|
56
77
|
/** EBS device name for the data volume (must match user data script). */
|
|
57
78
|
export const CLICKHOUSE_EBS_DEVICE_NAME = "/dev/xvdf";
|
|
58
79
|
/** EBS mount path on the EC2 host. */
|
|
@@ -10,5 +10,7 @@ import { SecurityGroup } from "../networking/securityGroup.js";
|
|
|
10
10
|
*
|
|
11
11
|
* Self-referencing native-port ingress is kept so the optimise/backup
|
|
12
12
|
* scheduled tasks (which share this SG) can reach the ClickHouse server.
|
|
13
|
+
* Under TLS modes, the secure native port (9440) is the self-referencing
|
|
14
|
+
* port; under `mode: "none"` the plaintext native port stays.
|
|
13
15
|
*/
|
|
14
16
|
export declare function createClickHouseSecurityGroup(scope: Construct, vpc: IVpc, nativePort: number): SecurityGroup;
|
|
@@ -9,6 +9,8 @@ import { SecurityGroup } from "../networking/securityGroup.js";
|
|
|
9
9
|
*
|
|
10
10
|
* Self-referencing native-port ingress is kept so the optimise/backup
|
|
11
11
|
* scheduled tasks (which share this SG) can reach the ClickHouse server.
|
|
12
|
+
* Under TLS modes, the secure native port (9440) is the self-referencing
|
|
13
|
+
* port; under `mode: "none"` the plaintext native port stays.
|
|
12
14
|
*/
|
|
13
15
|
export function createClickHouseSecurityGroup(scope, vpc, nativePort) {
|
|
14
16
|
const sg = new SecurityGroup(scope, "ClickHouseSecurityGroup", {
|
|
@@ -19,6 +19,27 @@ export interface BuildClickHouseUserDataOptions {
|
|
|
19
19
|
bucketName: string;
|
|
20
20
|
region: string;
|
|
21
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* When true, the server config emits `<https_port>`, `<tcp_port_secure>`,
|
|
24
|
+
* and an `<openSSL><server>` block referencing the cert files materialised
|
|
25
|
+
* at `CLICKHOUSE_TLS_CERT_MOUNT_PATH` by the user-data bootstrap step.
|
|
26
|
+
* Plaintext `<http_port>` is omitted to avoid mixed-protocol exposure.
|
|
27
|
+
* Default: false. When true, `caSecretArn` and `serverSecretArn` MUST be
|
|
28
|
+
* supplied so the bootstrap step can materialise the cert PEMs.
|
|
29
|
+
*/
|
|
30
|
+
tlsActive?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Secrets Manager ARN holding the CA cert as raw PEM. Required when
|
|
33
|
+
* `tlsActive` is true. Fetched at instance boot via the EC2 instance role
|
|
34
|
+
* and written to `${dataMount}/server-certs/ca.crt`.
|
|
35
|
+
*/
|
|
36
|
+
caSecretArn?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Secrets Manager ARN holding the server cert + key as JSON
|
|
39
|
+
* `{ "cert": "<pem>", "key": "<pem>" }`. Required when `tlsActive` is true.
|
|
40
|
+
* Fetched at instance boot and written to `${dataMount}/server-certs/server.{crt,key}`.
|
|
41
|
+
*/
|
|
42
|
+
serverSecretArn?: string;
|
|
22
43
|
}
|
|
23
44
|
export declare function generateServerConfigXml(options: BuildClickHouseUserDataOptions): string;
|
|
24
45
|
export interface GenerateUsersConfigXmlOptions {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { CLICKHOUSE_DATA_MOUNT_PATH, CLICKHOUSE_EBS_DEVICE_NAME, CLICKHOUSE_CONFIG_SUBDIR, CLICKHOUSE_USERS_SUBDIR, CLICKHOUSE_HTTP_PORT, CLICKHOUSE_PROMETHEUS_PORT, clickHousePasswordSha256Snippet } from "./clickhouseConstants.js";
|
|
1
|
+
import { CLICKHOUSE_DATA_MOUNT_PATH, CLICKHOUSE_EBS_DEVICE_NAME, CLICKHOUSE_CONFIG_SUBDIR, CLICKHOUSE_USERS_SUBDIR, CLICKHOUSE_HTTP_PORT, CLICKHOUSE_HTTPS_PORT, CLICKHOUSE_TCP_SECURE_PORT, CLICKHOUSE_TLS_CERT_MOUNT_PATH, CLICKHOUSE_PROMETHEUS_PORT, clickHousePasswordSha256Snippet } from "./clickhouseConstants.js";
|
|
2
2
|
import { renderUsersXml } from "./clickhouseXmlRenderer.js";
|
|
3
3
|
export function generateServerConfigXml(options) {
|
|
4
4
|
const { backupBucketName, backupBucketRegion, coldTier } = options;
|
|
5
|
+
const tlsActive = options.tlsActive ?? false;
|
|
5
6
|
const storageBlock = coldTier !== undefined
|
|
6
7
|
? ` <storage_configuration>
|
|
7
8
|
<!-- Same CH 26 rule as the no-cold-tier branch: a <local_ssd> disk
|
|
@@ -145,7 +146,19 @@ export function generateServerConfigXml(options) {
|
|
|
145
146
|
<number_of_free_entries_in_pool_to_lower_max_size_of_merge>1</number_of_free_entries_in_pool_to_lower_max_size_of_merge>
|
|
146
147
|
<number_of_free_entries_in_pool_to_execute_optimize_entire_partition>1</number_of_free_entries_in_pool_to_execute_optimize_entire_partition>
|
|
147
148
|
</merge_tree>
|
|
148
|
-
|
|
149
|
+
${tlsActive
|
|
150
|
+
? ` <https_port>${CLICKHOUSE_HTTPS_PORT}</https_port>
|
|
151
|
+
<tcp_port_secure>${CLICKHOUSE_TCP_SECURE_PORT}</tcp_port_secure>
|
|
152
|
+
<openSSL>
|
|
153
|
+
<server>
|
|
154
|
+
<certificateFile>${CLICKHOUSE_TLS_CERT_MOUNT_PATH}/server.crt</certificateFile>
|
|
155
|
+
<privateKeyFile>${CLICKHOUSE_TLS_CERT_MOUNT_PATH}/server.key</privateKeyFile>
|
|
156
|
+
<verificationMode>relaxed</verificationMode>
|
|
157
|
+
<disableProtocols>sslv2,sslv3,tlsv1,tlsv1_1</disableProtocols>
|
|
158
|
+
<preferServerCiphers>true</preferServerCiphers>
|
|
159
|
+
</server>
|
|
160
|
+
</openSSL>`
|
|
161
|
+
: ` <http_port>${CLICKHOUSE_HTTP_PORT}</http_port>`}
|
|
149
162
|
<custom_settings_prefixes>current_</custom_settings_prefixes>
|
|
150
163
|
<!-- HTTP keep-alive window. Must exceed @clickhouse/client idle_socket_ttl (15 s)
|
|
151
164
|
so the client always closes the socket first. Prevents ECONNRESET on reuse. -->
|
|
@@ -247,6 +260,38 @@ function compactUserDataScript(script) {
|
|
|
247
260
|
*/
|
|
248
261
|
export function buildClickHouseUserData(options) {
|
|
249
262
|
const serverConfigXml = generateServerConfigXml(options);
|
|
263
|
+
const tlsActive = options.tlsActive ?? false;
|
|
264
|
+
if (tlsActive &&
|
|
265
|
+
(options.caSecretArn === undefined || options.serverSecretArn === undefined)) {
|
|
266
|
+
throw new Error("buildClickHouseUserData: tlsActive=true requires both caSecretArn and serverSecretArn");
|
|
267
|
+
}
|
|
268
|
+
const tlsBootstrap = tlsActive
|
|
269
|
+
? `
|
|
270
|
+
# Materialise TLS certs from Secrets Manager. The EC2 instance role carries
|
|
271
|
+
# the secretsmanager:GetSecretValue grant; certs land on the EBS-backed mount
|
|
272
|
+
# at \`$MOUNT_POINT/server-certs/\` and are bind-mounted read-only into the
|
|
273
|
+
# CH container at /etc/clickhouse-server/certs. Cert rotation triggers task
|
|
274
|
+
# replacement via the CA_CERT_SHA256 env on dependent services (the cert
|
|
275
|
+
# generator emits the digest as a CFN Data attribute).
|
|
276
|
+
mkdir -p "$MOUNT_POINT/server-certs"
|
|
277
|
+
|
|
278
|
+
# jq is needed to extract cert + key fields from the server bundle JSON.
|
|
279
|
+
# AL2023 ECS-optimised AMIs ship without jq; install it if missing.
|
|
280
|
+
command -v jq >/dev/null 2>&1 || dnf install -y jq || yum install -y jq
|
|
281
|
+
|
|
282
|
+
# Fetch CA (raw PEM SecretString). Quoting prevents word-splitting on multi-line PEM.
|
|
283
|
+
CA_PEM=$(aws secretsmanager get-secret-value --secret-id "${options.caSecretArn ?? ""}" --query SecretString --output text)
|
|
284
|
+
printf '%s\\n' "$CA_PEM" > "$MOUNT_POINT/server-certs/ca.crt"
|
|
285
|
+
|
|
286
|
+
# Fetch server bundle (JSON { "cert": "<pem>", "key": "<pem>" }).
|
|
287
|
+
SERVER_JSON=$(aws secretsmanager get-secret-value --secret-id "${options.serverSecretArn ?? ""}" --query SecretString --output text)
|
|
288
|
+
printf '%s' "$SERVER_JSON" | jq -r .cert > "$MOUNT_POINT/server-certs/server.crt"
|
|
289
|
+
printf '%s' "$SERVER_JSON" | jq -r .key > "$MOUNT_POINT/server-certs/server.key"
|
|
290
|
+
|
|
291
|
+
chown -R 101:101 "$MOUNT_POINT/server-certs"
|
|
292
|
+
chmod 600 "$MOUNT_POINT/server-certs/server.key"
|
|
293
|
+
`
|
|
294
|
+
: "";
|
|
250
295
|
const script = `#!/bin/bash
|
|
251
296
|
set -euo pipefail
|
|
252
297
|
|
|
@@ -333,7 +378,7 @@ if [ "$USERS_CONFIG_RETRIEVED" != "1" ]; then
|
|
|
333
378
|
fi
|
|
334
379
|
|
|
335
380
|
chown -R 101:101 "$MOUNT_POINT"
|
|
336
|
-
`;
|
|
381
|
+
${tlsBootstrap}`;
|
|
337
382
|
return compactUserDataScript(script);
|
|
338
383
|
}
|
|
339
384
|
/**
|
|
@@ -45,7 +45,7 @@ export interface RenderUsersXmlOptions {
|
|
|
45
45
|
*
|
|
46
46
|
* **Schema-admin-only emission.** Only the schema admin lands in the XML.
|
|
47
47
|
* Managed-password workload users are created at runtime by the migration
|
|
48
|
-
* helper (`@fjall/clickhouse
|
|
48
|
+
* helper (`@fjall/clickhouse § provisionUsersFromEnv`) — they
|
|
49
49
|
* authenticate via plaintext from `USER_<NAME>_PASSWORD` env vars at runtime
|
|
50
50
|
* and bind their profiles via customer SQL.
|
|
51
51
|
*
|
|
@@ -73,7 +73,7 @@ ${body}
|
|
|
73
73
|
*
|
|
74
74
|
* **Schema-admin-only emission.** Only the schema admin lands in the XML.
|
|
75
75
|
* Managed-password workload users are created at runtime by the migration
|
|
76
|
-
* helper (`@fjall/clickhouse
|
|
76
|
+
* helper (`@fjall/clickhouse § provisionUsersFromEnv`) — they
|
|
77
77
|
* authenticate via plaintext from `USER_<NAME>_PASSWORD` env vars at runtime
|
|
78
78
|
* and bind their profiles via customer SQL.
|
|
79
79
|
*
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Construct } from "constructs";
|
|
2
|
+
import { Secret } from "./secret.js";
|
|
3
|
+
export interface TlsCaSecretProps {
|
|
4
|
+
/** Drives the deterministic secret name `fjall-<appName>-clickhouse-tls-ca`. */
|
|
5
|
+
readonly appName: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* ClickHouse TLS CA cert (public — clients pin against this). Retained on
|
|
9
|
+
* delete so client-side trust anchors survive accidental stack teardown.
|
|
10
|
+
*/
|
|
11
|
+
export declare class TlsCaSecret extends Secret {
|
|
12
|
+
constructor(scope: Construct, id: string, props: TlsCaSecretProps);
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { RemovalPolicy } from "aws-cdk-lib";
|
|
2
|
+
import { Secret } from "./secret.js";
|
|
3
|
+
/**
|
|
4
|
+
* ClickHouse TLS CA cert (public — clients pin against this). Retained on
|
|
5
|
+
* delete so client-side trust anchors survive accidental stack teardown.
|
|
6
|
+
*/
|
|
7
|
+
export class TlsCaSecret extends Secret {
|
|
8
|
+
constructor(scope, id, props) {
|
|
9
|
+
super(scope, id, {
|
|
10
|
+
secretName: `fjall-${props.appName}-clickhouse-tls-ca`,
|
|
11
|
+
description: "ClickHouse TLS CA cert (public — clients pin against this)"
|
|
12
|
+
});
|
|
13
|
+
this.secret.applyRemovalPolicy(RemovalPolicy.RETAIN);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Construct } from "constructs";
|
|
2
|
+
import { Secret } from "./secret.js";
|
|
3
|
+
export interface TlsServerSecretProps {
|
|
4
|
+
/** Drives the deterministic secret name `fjall-<appName>-clickhouse-tls-server`. */
|
|
5
|
+
readonly appName: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* ClickHouse TLS server cert + key (JSON: `{ cert, key }`). Retained on
|
|
9
|
+
* delete so cert material survives accidental stack teardown; the private
|
|
10
|
+
* key only leaves Secrets Manager via ECS executionRole + the TLS init
|
|
11
|
+
* container that materialises it onto the shared volume.
|
|
12
|
+
*/
|
|
13
|
+
export declare class TlsServerSecret extends Secret {
|
|
14
|
+
constructor(scope: Construct, id: string, props: TlsServerSecretProps);
|
|
15
|
+
}
|