diff --git a/redisinsight/api/migration/1650278664000-sni.ts b/redisinsight/api/migration/1650278664000-sni.ts new file mode 100644 index 0000000000..b8cdf63454 --- /dev/null +++ b/redisinsight/api/migration/1650278664000-sni.ts @@ -0,0 +1,20 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class sni1650278664000 implements MigrationInterface { + name = 'sni1650278664000' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "temporary_database_instance" ("id" varchar PRIMARY KEY NOT NULL, "host" varchar NOT NULL, "port" integer NOT NULL, "name" varchar NOT NULL, "username" varchar, "password" varchar, "tls" boolean NOT NULL, "verifyServerCert" boolean NOT NULL, "lastConnection" datetime, "caCertId" varchar, "clientCertId" varchar, "connectionType" varchar NOT NULL DEFAULT ('STANDALONE'), "nodes" varchar, "nameFromProvider" varchar, "sentinelMasterName" varchar, "sentinelMasterUsername" varchar, "sentinelMasterPassword" varchar, "provider" varchar DEFAULT ('UNKNOWN'), "modules" varchar NOT NULL DEFAULT ('[]'), "db" integer, "encryption" varchar, "tlsServername" varchar, CONSTRAINT "FK_3b9b625266c00feb2d66a9f36e4" FOREIGN KEY ("clientCertId") REFERENCES "client_certificate" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_d1bc747b5938e22b4b708d8e9a5" FOREIGN KEY ("caCertId") REFERENCES "ca_certificate" ("id") ON DELETE SET NULL ON UPDATE NO ACTION)`); + await queryRunner.query(`INSERT INTO "temporary_database_instance"("id", "host", "port", "name", "username", "password", "tls", "verifyServerCert", "lastConnection", "caCertId", "clientCertId", "connectionType", "nodes", "nameFromProvider", "sentinelMasterName", "sentinelMasterUsername", "sentinelMasterPassword", "provider", "modules", "db", "encryption") SELECT "id", "host", "port", "name", "username", "password", "tls", "verifyServerCert", "lastConnection", "caCertId", "clientCertId", "connectionType", "nodes", "nameFromProvider", "sentinelMasterName", "sentinelMasterUsername", "sentinelMasterPassword", "provider", "modules", "db", "encryption" FROM "database_instance"`); + await queryRunner.query(`DROP TABLE "database_instance"`); + await queryRunner.query(`ALTER TABLE "temporary_database_instance" RENAME TO "database_instance"`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "database_instance" RENAME TO "temporary_database_instance"`); + await queryRunner.query(`CREATE TABLE "database_instance" ("id" varchar PRIMARY KEY NOT NULL, "host" varchar NOT NULL, "port" integer NOT NULL, "name" varchar NOT NULL, "username" varchar, "password" varchar, "tls" boolean NOT NULL, "verifyServerCert" boolean NOT NULL, "lastConnection" datetime, "caCertId" varchar, "clientCertId" varchar, "connectionType" varchar NOT NULL DEFAULT ('STANDALONE'), "nodes" varchar, "nameFromProvider" varchar, "sentinelMasterName" varchar, "sentinelMasterUsername" varchar, "sentinelMasterPassword" varchar, "provider" varchar DEFAULT ('UNKNOWN'), "modules" varchar NOT NULL DEFAULT ('[]'), "db" integer, "encryption" varchar, CONSTRAINT "FK_3b9b625266c00feb2d66a9f36e4" FOREIGN KEY ("clientCertId") REFERENCES "client_certificate" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, CONSTRAINT "FK_d1bc747b5938e22b4b708d8e9a5" FOREIGN KEY ("caCertId") REFERENCES "ca_certificate" ("id") ON DELETE SET NULL ON UPDATE NO ACTION)`); + await queryRunner.query(`INSERT INTO "database_instance"("id", "host", "port", "name", "username", "password", "tls", "verifyServerCert", "lastConnection", "caCertId", "clientCertId", "connectionType", "nodes", "nameFromProvider", "sentinelMasterName", "sentinelMasterUsername", "sentinelMasterPassword", "provider", "modules", "db", "encryption") SELECT "id", "host", "port", "name", "username", "password", "tls", "verifyServerCert", "lastConnection", "caCertId", "clientCertId", "connectionType", "nodes", "nameFromProvider", "sentinelMasterName", "sentinelMasterUsername", "sentinelMasterPassword", "provider", "modules", "db", "encryption" FROM "temporary_database_instance"`); + await queryRunner.query(`DROP TABLE "temporary_database_instance"`); + } + +} diff --git a/redisinsight/api/migration/index.ts b/redisinsight/api/migration/index.ts index 07e4cfce5e..9a52e0fead 100644 --- a/redisinsight/api/migration/index.ts +++ b/redisinsight/api/migration/index.ts @@ -12,6 +12,7 @@ import { databaseDbIndex1634219846022 } from './1634219846022-database-db-index' import { encryption1634557312500 } from './1634557312500-encryption'; import { commandExecution1641795882696 } from './1641795882696-command-execution'; import { pluginState1641805606399 } from './1641805606399-plugin-state'; +import { sni1650278664000 } from "./1650278664000-sni"; export default [ initialMigration1614164490968, @@ -28,4 +29,5 @@ export default [ encryption1634557312500, commandExecution1641795882696, pluginState1641805606399, + sni1650278664000, ]; diff --git a/redisinsight/api/src/modules/core/models/database-instance.entity.ts b/redisinsight/api/src/modules/core/models/database-instance.entity.ts index bff2ec3d40..17478b5fd1 100644 --- a/redisinsight/api/src/modules/core/models/database-instance.entity.ts +++ b/redisinsight/api/src/modules/core/models/database-instance.entity.ts @@ -103,6 +103,13 @@ export class DatabaseInstanceEntity { @Column({ nullable: false }) tls: boolean; + @ApiPropertyOptional({ + description: 'SNI servername', + type: String, + }) + @Column({ nullable: true }) + tlsServername?: string; + @ApiProperty({ description: 'The certificate returned by the server needs to be verified.', type: Boolean, diff --git a/redisinsight/api/src/modules/core/services/redis/redis.service.ts b/redisinsight/api/src/modules/core/services/redis/redis.service.ts index 56f1c93373..ad40dd2ff9 100644 --- a/redisinsight/api/src/modules/core/services/redis/redis.service.ts +++ b/redisinsight/api/src/modules/core/services/redis/redis.service.ts @@ -295,6 +295,7 @@ export class RedisService { config = { rejectUnauthorized: tls.verifyServerCert, checkServerIdentity: () => undefined, + servername: tls.servername || undefined, }; if (tls.caCertId || tls.newCaCert) { const caCertConfig = await this.getCaCertConfig(tls); diff --git a/redisinsight/api/src/modules/instances/dto/database-instance.dto.ts b/redisinsight/api/src/modules/instances/dto/database-instance.dto.ts index b2e2b32f18..4ecc9333bc 100644 --- a/redisinsight/api/src/modules/instances/dto/database-instance.dto.ts +++ b/redisinsight/api/src/modules/instances/dto/database-instance.dto.ts @@ -139,6 +139,15 @@ export class BasicTlsDto { @IsString({ always: true }) @IsOptional() clientCertPairId?: string; + + @ApiPropertyOptional({ + description: 'SNI servername', + type: String, + }) + @Type(() => String) + @IsString() + @IsOptional() + servername?: string; } export class TlsDto extends BasicTlsDto { diff --git a/redisinsight/api/src/modules/shared/services/instances-business/instances-business.service.ts b/redisinsight/api/src/modules/shared/services/instances-business/instances-business.service.ts index 3bb16061ce..e9dde61dcc 100644 --- a/redisinsight/api/src/modules/shared/services/instances-business/instances-business.service.ts +++ b/redisinsight/api/src/modules/shared/services/instances-business/instances-business.service.ts @@ -575,6 +575,9 @@ export class InstancesBusinessService { ...rest, }); database.tls = !!tls; + if (tls?.servername) { + database.tlsServername = tls.servername; + } if (storeCert && database.tls) { database.verifyServerCert = tls.verifyServerCert; if (tls.newCaCert) { diff --git a/redisinsight/api/src/modules/shared/utils/database-entity-converter.ts b/redisinsight/api/src/modules/shared/utils/database-entity-converter.ts index c3ea37a227..e84b8833f0 100644 --- a/redisinsight/api/src/modules/shared/utils/database-entity-converter.ts +++ b/redisinsight/api/src/modules/shared/utils/database-entity-converter.ts @@ -5,6 +5,7 @@ export const convertEntityToDto = (database: DatabaseInstanceEntity): DatabaseIn if (database) { const { tls, + tlsServername, verifyServerCert, caCert, clientCert, @@ -31,7 +32,7 @@ export const convertEntityToDto = (database: DatabaseInstanceEntity): DatabaseIn }; } if (tls) { - result.tls = { verifyServerCert: verifyServerCert || false }; + result.tls = { verifyServerCert: verifyServerCert || false, servername: tlsServername }; if (caCert) { result.tls.caCertId = caCert.id; } diff --git a/redisinsight/api/test/api/instance/GET-instance.test.ts b/redisinsight/api/test/api/instance/GET-instance.test.ts index aa282479e7..c054020518 100644 --- a/redisinsight/api/test/api/instance/GET-instance.test.ts +++ b/redisinsight/api/test/api/instance/GET-instance.test.ts @@ -18,6 +18,7 @@ const responseSchema = Joi.array().items(Joi.object().keys({ lastConnection: Joi.date().allow(null).required(), provider: Joi.string().required(), tls: Joi.object().keys({ + servername: Joi.string().allow(null), verifyServerCert: Joi.boolean().required(), caCertId: Joi.string(), clientCertPairId: Joi.string(), diff --git a/redisinsight/api/test/api/instance/POST-instance.test.ts b/redisinsight/api/test/api/instance/POST-instance.test.ts index c5d34938a7..4d10ec4805 100644 --- a/redisinsight/api/test/api/instance/POST-instance.test.ts +++ b/redisinsight/api/test/api/instance/POST-instance.test.ts @@ -16,6 +16,7 @@ const responseSchema = Joi.object().keys({ lastConnection: Joi.date().allow(null).required(), provider: Joi.string().valid('LOCALHOST', 'UNKNOWN', 'RE_CLOUD', 'RE_CLUSTER').required(), tls: Joi.object().keys({ + servername: Joi.string().allow(null), verifyServerCert: Joi.boolean().required(), caCertId: Joi.string(), clientCertPairId: Joi.string(),