mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 23:27:14 +00:00
feat: half-way done through integrating with platform
This commit is contained in:
@@ -0,0 +1,33 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.DynamicSecret, "gatewayV2Id"))) {
|
||||||
|
await knex.schema.alterTable(TableName.DynamicSecret, (table) => {
|
||||||
|
table.uuid("gatewayV2Id");
|
||||||
|
table.foreign("gatewayV2Id").references("id").inTable(TableName.GatewayV2).onDelete("SET NULL");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "gatewayV2Id"))) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (table) => {
|
||||||
|
table.uuid("gatewayV2Id");
|
||||||
|
table.foreign("gatewayV2Id").references("id").inTable(TableName.GatewayV2).onDelete("SET NULL");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.DynamicSecret, "gatewayV2Id")) {
|
||||||
|
await knex.schema.alterTable(TableName.DynamicSecret, (table) => {
|
||||||
|
table.dropColumn("gatewayV2Id");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "gatewayV2Id")) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (table) => {
|
||||||
|
table.dropColumn("gatewayV2Id");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,7 +29,8 @@ export const DynamicSecretsSchema = z.object({
|
|||||||
encryptedInput: zodBuffer,
|
encryptedInput: zodBuffer,
|
||||||
projectGatewayId: z.string().uuid().nullable().optional(),
|
projectGatewayId: z.string().uuid().nullable().optional(),
|
||||||
gatewayId: z.string().uuid().nullable().optional(),
|
gatewayId: z.string().uuid().nullable().optional(),
|
||||||
usernameTemplate: z.string().nullable().optional()
|
usernameTemplate: z.string().nullable().optional(),
|
||||||
|
gatewayV2Id: z.string().uuid().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TDynamicSecrets = z.infer<typeof DynamicSecretsSchema>;
|
export type TDynamicSecrets = z.infer<typeof DynamicSecretsSchema>;
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ export const IdentityKubernetesAuthsSchema = z.object({
|
|||||||
encryptedKubernetesCaCertificate: zodBuffer.nullable().optional(),
|
encryptedKubernetesCaCertificate: zodBuffer.nullable().optional(),
|
||||||
gatewayId: z.string().uuid().nullable().optional(),
|
gatewayId: z.string().uuid().nullable().optional(),
|
||||||
accessTokenPeriod: z.coerce.number().default(0),
|
accessTokenPeriod: z.coerce.number().default(0),
|
||||||
tokenReviewMode: z.string().default("api")
|
tokenReviewMode: z.string().default("api"),
|
||||||
|
gatewayV2Id: z.string().uuid().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIdentityKubernetesAuths = z.infer<typeof IdentityKubernetesAuthsSchema>;
|
export type TIdentityKubernetesAuths = z.infer<typeof IdentityKubernetesAuthsSchema>;
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
metadata,
|
metadata,
|
||||||
usernameTemplate
|
usernameTemplate
|
||||||
}) => {
|
}) => {
|
||||||
|
let isGatewayV1 = true;
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
@@ -129,6 +130,10 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!gateway) {
|
||||||
|
isGatewayV1 = false;
|
||||||
|
}
|
||||||
|
|
||||||
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -163,7 +168,8 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
defaultTTL,
|
defaultTTL,
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
name,
|
name,
|
||||||
gatewayId: selectedGatewayId,
|
gatewayId: isGatewayV1 ? selectedGatewayId : undefined,
|
||||||
|
gatewayV2Id: isGatewayV1 ? undefined : selectedGatewayId,
|
||||||
usernameTemplate
|
usernameTemplate
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
@@ -274,20 +280,27 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
const updatedInput = await selectedProvider.validateProviderInputs(newInput, { projectId });
|
const updatedInput = await selectedProvider.validateProviderInputs(newInput, { projectId });
|
||||||
|
|
||||||
let selectedGatewayId: string | null = null;
|
let selectedGatewayId: string | null = null;
|
||||||
|
let isGatewayV1 = true;
|
||||||
if (updatedInput && typeof updatedInput === "object" && "gatewayId" in updatedInput && updatedInput?.gatewayId) {
|
if (updatedInput && typeof updatedInput === "object" && "gatewayId" in updatedInput && updatedInput?.gatewayId) {
|
||||||
const gatewayId = updatedInput.gatewayId as string;
|
const gatewayId = updatedInput.gatewayId as string;
|
||||||
|
|
||||||
const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: actorOrgId });
|
const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: actorOrgId });
|
||||||
if (!gateway) {
|
const [gatewayv2] = await gatewayV2DAL.find({ id: gatewayId, orgId: actorOrgId });
|
||||||
|
|
||||||
|
if (!gateway && !gatewayv2) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Gateway with ID ${gatewayId} not found`
|
message: `Gateway with ID ${gatewayId} not found`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!gateway) {
|
||||||
|
isGatewayV1 = false;
|
||||||
|
}
|
||||||
|
|
||||||
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
gateway.orgId,
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
@@ -297,7 +310,7 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
OrgPermissionSubjects.Gateway
|
OrgPermissionSubjects.Gateway
|
||||||
);
|
);
|
||||||
|
|
||||||
selectedGatewayId = gateway.id;
|
selectedGatewayId = gateway?.id ?? gatewayv2?.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isConnected = await selectedProvider.validateConnection(newInput, { projectId });
|
const isConnected = await selectedProvider.validateConnection(newInput, { projectId });
|
||||||
@@ -313,7 +326,8 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
defaultTTL,
|
defaultTTL,
|
||||||
name: newName ?? name,
|
name: newName ?? name,
|
||||||
status: null,
|
status: null,
|
||||||
gatewayId: selectedGatewayId,
|
gatewayId: isGatewayV1 ? selectedGatewayId : null,
|
||||||
|
gatewayV2Id: isGatewayV1 ? null : selectedGatewayId,
|
||||||
usernameTemplate
|
usernameTemplate
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export const buildDynamicSecretProviders = ({
|
|||||||
gatewayService,
|
gatewayService,
|
||||||
gatewayV2Service
|
gatewayV2Service
|
||||||
}: TBuildDynamicSecretProviderDTO): Record<DynamicSecretProviders, TDynamicProviderFns> => ({
|
}: TBuildDynamicSecretProviderDTO): Record<DynamicSecretProviders, TDynamicProviderFns> => ({
|
||||||
[DynamicSecretProviders.SqlDatabase]: SqlDatabaseProvider({ gatewayService }),
|
[DynamicSecretProviders.SqlDatabase]: SqlDatabaseProvider({ gatewayService, gatewayV2Service }),
|
||||||
[DynamicSecretProviders.Cassandra]: CassandraProvider(),
|
[DynamicSecretProviders.Cassandra]: CassandraProvider(),
|
||||||
[DynamicSecretProviders.AwsIam]: AwsIamProvider(),
|
[DynamicSecretProviders.AwsIam]: AwsIamProvider(),
|
||||||
[DynamicSecretProviders.Redis]: RedisDatabaseProvider(),
|
[DynamicSecretProviders.Redis]: RedisDatabaseProvider(),
|
||||||
|
|||||||
@@ -64,7 +64,11 @@ export const KubernetesProvider = ({
|
|||||||
},
|
},
|
||||||
gatewayCallback: (host: string, port: number, httpsAgent?: https.Agent) => Promise<T>
|
gatewayCallback: (host: string, port: number, httpsAgent?: https.Agent) => Promise<T>
|
||||||
): Promise<T> => {
|
): Promise<T> => {
|
||||||
const gatewayV2ConnectionDetails = await gatewayV2Service.getPlatformConnectionDetailsByGatewayId(inputs.gatewayId);
|
const gatewayV2ConnectionDetails = await gatewayV2Service.getPlatformConnectionDetailsByGatewayId({
|
||||||
|
gatewayId: inputs.gatewayId,
|
||||||
|
targetHost: inputs.targetHost,
|
||||||
|
targetPort: inputs.targetPort
|
||||||
|
});
|
||||||
if (gatewayV2ConnectionDetails) {
|
if (gatewayV2ConnectionDetails) {
|
||||||
const callbackResult = await withGatewayV2Proxy(
|
const callbackResult = await withGatewayV2Proxy(
|
||||||
async (port) => {
|
async (port) => {
|
||||||
@@ -77,7 +81,9 @@ export const KubernetesProvider = ({
|
|||||||
{
|
{
|
||||||
proxyIp: gatewayV2ConnectionDetails.proxyIp,
|
proxyIp: gatewayV2ConnectionDetails.proxyIp,
|
||||||
gateway: gatewayV2ConnectionDetails.gateway,
|
gateway: gatewayV2ConnectionDetails.gateway,
|
||||||
proxy: gatewayV2ConnectionDetails.proxy
|
proxy: gatewayV2ConnectionDetails.proxy,
|
||||||
|
protocol: inputs.reviewTokenThroughGateway ? GatewayProxyProtocol.Http : GatewayProxyProtocol.Tcp,
|
||||||
|
httpsAgent: inputs.httpsAgent
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -379,8 +385,18 @@ export const KubernetesProvider = ({
|
|||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
let errorMessage = error instanceof Error ? error.message : "Unknown error";
|
let errorMessage = error instanceof Error ? error.message : "Unknown error";
|
||||||
if (axios.isAxiosError(error) && (error.response?.data as { message: string })?.message) {
|
if (axios.isAxiosError(error)) {
|
||||||
errorMessage = (error.response?.data as { message: string }).message;
|
if (error.response) {
|
||||||
|
let { message } = error?.response?.data as unknown as { message?: string };
|
||||||
|
|
||||||
|
if (!message && typeof error.response.data === "string") {
|
||||||
|
message = error.response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
errorMessage = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sanitizedErrorMessage = sanitizeString({
|
const sanitizedErrorMessage = sanitizeString({
|
||||||
@@ -629,8 +645,18 @@ export const KubernetesProvider = ({
|
|||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
let errorMessage = error instanceof Error ? error.message : "Unknown error";
|
let errorMessage = error instanceof Error ? error.message : "Unknown error";
|
||||||
if (axios.isAxiosError(error) && (error.response?.data as { message: string })?.message) {
|
if (axios.isAxiosError(error)) {
|
||||||
errorMessage = (error.response?.data as { message: string }).message;
|
if (error.response) {
|
||||||
|
let { message } = error?.response?.data as unknown as { message?: string };
|
||||||
|
|
||||||
|
if (!message && typeof error.response.data === "string") {
|
||||||
|
message = error.response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
errorMessage = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sanitizedErrorMessage = sanitizeString({
|
const sanitizedErrorMessage = sanitizeString({
|
||||||
@@ -766,8 +792,18 @@ export const KubernetesProvider = ({
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
let errorMessage = error instanceof Error ? error.message : "Unknown error";
|
let errorMessage = error instanceof Error ? error.message : "Unknown error";
|
||||||
if (axios.isAxiosError(error) && (error.response?.data as { message: string })?.message) {
|
if (axios.isAxiosError(error)) {
|
||||||
errorMessage = (error.response?.data as { message: string }).message;
|
if (error.response) {
|
||||||
|
let { message } = error?.response?.data as unknown as { message?: string };
|
||||||
|
|
||||||
|
if (!message && typeof error.response.data === "string") {
|
||||||
|
message = error.response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
errorMessage = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sanitizedErrorMessage = sanitizeString({
|
const sanitizedErrorMessage = sanitizeString({
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ import { crypto } from "@app/lib/crypto/cryptography";
|
|||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { sanitizeString } from "@app/lib/fn";
|
import { sanitizeString } from "@app/lib/fn";
|
||||||
import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
|
import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
|
||||||
|
import { withGatewayV2Proxy } from "@app/lib/gateway-v2/gateway-v2";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||||
|
|
||||||
import { TGatewayServiceFactory } from "../../gateway/gateway-service";
|
import { TGatewayServiceFactory } from "../../gateway/gateway-service";
|
||||||
|
import { TGatewayV2ServiceFactory } from "../../gateway-v2/gateway-v2-service";
|
||||||
import { verifyHostInputValidity } from "../dynamic-secret-fns";
|
import { verifyHostInputValidity } from "../dynamic-secret-fns";
|
||||||
import { DynamicSecretSqlDBSchema, PasswordRequirements, SqlProviders, TDynamicProviderFns } from "./models";
|
import { DynamicSecretSqlDBSchema, PasswordRequirements, SqlProviders, TDynamicProviderFns } from "./models";
|
||||||
import { compileUsernameTemplate } from "./templateUtils";
|
import { compileUsernameTemplate } from "./templateUtils";
|
||||||
@@ -128,9 +130,13 @@ const generateUsername = (provider: SqlProviders, usernameTemplate?: string | nu
|
|||||||
|
|
||||||
type TSqlDatabaseProviderDTO = {
|
type TSqlDatabaseProviderDTO = {
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO): TDynamicProviderFns => {
|
export const SqlDatabaseProvider = ({
|
||||||
|
gatewayService,
|
||||||
|
gatewayV2Service
|
||||||
|
}: TSqlDatabaseProviderDTO): TDynamicProviderFns => {
|
||||||
const validateProviderInputs = async (inputs: unknown) => {
|
const validateProviderInputs = async (inputs: unknown) => {
|
||||||
const providerInputs = await DynamicSecretSqlDBSchema.parseAsync(inputs);
|
const providerInputs = await DynamicSecretSqlDBSchema.parseAsync(inputs);
|
||||||
|
|
||||||
@@ -183,6 +189,26 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
providerInputs: z.infer<typeof DynamicSecretSqlDBSchema>,
|
providerInputs: z.infer<typeof DynamicSecretSqlDBSchema>,
|
||||||
gatewayCallback: (host: string, port: number) => Promise<void>
|
gatewayCallback: (host: string, port: number) => Promise<void>
|
||||||
) => {
|
) => {
|
||||||
|
const gatewayV2ConnectionDetails = await gatewayV2Service.getPlatformConnectionDetailsByGatewayId({
|
||||||
|
gatewayId: providerInputs.gatewayId as string,
|
||||||
|
targetHost: providerInputs.host,
|
||||||
|
targetPort: providerInputs.port
|
||||||
|
});
|
||||||
|
|
||||||
|
if (gatewayV2ConnectionDetails) {
|
||||||
|
return withGatewayV2Proxy(
|
||||||
|
async (port) => {
|
||||||
|
await gatewayCallback("localhost", port);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
proxyIp: gatewayV2ConnectionDetails.proxyIp,
|
||||||
|
gateway: gatewayV2ConnectionDetails.gateway,
|
||||||
|
proxy: gatewayV2ConnectionDetails.proxy,
|
||||||
|
protocol: GatewayProxyProtocol.Tcp
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const relayDetails = await gatewayService.fnGetGatewayClientTlsByGatewayId(providerInputs.gatewayId as string);
|
const relayDetails = await gatewayService.fnGetGatewayClientTlsByGatewayId(providerInputs.gatewayId as string);
|
||||||
const [relayHost, relayPort] = relayDetails.relayAddress.split(":");
|
const [relayHost, relayPort] = relayDetails.relayAddress.split(":");
|
||||||
await withGatewayProxy(
|
await withGatewayProxy(
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export const GATEWAY_ROUTING_INFO_OID = "1.3.6.1.4.1.12345.100.1";
|
||||||
|
export const GATEWAY_ACTOR_OID = "1.3.6.1.4.1.12345.100.2";
|
||||||
@@ -15,14 +15,17 @@ import {
|
|||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
|
|
||||||
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
import { TProxyDALFactory } from "../proxy/proxy-dal";
|
import { TProxyDALFactory } from "../proxy/proxy-dal";
|
||||||
import { isInstanceProxy } from "../proxy/proxy-fns";
|
import { isInstanceProxy } from "../proxy/proxy-fns";
|
||||||
import { TProxyServiceFactory } from "../proxy/proxy-service";
|
import { TProxyServiceFactory } from "../proxy/proxy-service";
|
||||||
|
import { GATEWAY_ACTOR_OID, GATEWAY_ROUTING_INFO_OID } from "./gateway-v2-constants";
|
||||||
import { TGatewayV2DALFactory } from "./gateway-v2-dal";
|
import { TGatewayV2DALFactory } from "./gateway-v2-dal";
|
||||||
import { TOrgGatewayConfigV2DALFactory } from "./org-gateway-config-v2-dal";
|
import { TOrgGatewayConfigV2DALFactory } from "./org-gateway-config-v2-dal";
|
||||||
|
|
||||||
type TGatewayV2ServiceFactoryDep = {
|
type TGatewayV2ServiceFactoryDep = {
|
||||||
orgGatewayConfigV2DAL: Pick<TOrgGatewayConfigV2DALFactory, "findOne" | "create" | "transaction" | "findById">;
|
orgGatewayConfigV2DAL: Pick<TOrgGatewayConfigV2DALFactory, "findOne" | "create" | "transaction" | "findById">;
|
||||||
|
licenseService: Pick<TLicenseServiceFactory, "onPremFeatures" | "getPlan">;
|
||||||
kmsService: TKmsServiceFactory;
|
kmsService: TKmsServiceFactory;
|
||||||
proxyService: TProxyServiceFactory;
|
proxyService: TProxyServiceFactory;
|
||||||
gatewayV2DAL: TGatewayV2DALFactory;
|
gatewayV2DAL: TGatewayV2DALFactory;
|
||||||
@@ -33,6 +36,7 @@ export type TGatewayV2ServiceFactory = ReturnType<typeof gatewayV2ServiceFactory
|
|||||||
|
|
||||||
export const gatewayV2ServiceFactory = ({
|
export const gatewayV2ServiceFactory = ({
|
||||||
orgGatewayConfigV2DAL,
|
orgGatewayConfigV2DAL,
|
||||||
|
licenseService,
|
||||||
kmsService,
|
kmsService,
|
||||||
proxyService,
|
proxyService,
|
||||||
gatewayV2DAL,
|
gatewayV2DAL,
|
||||||
@@ -231,7 +235,15 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
return gateways;
|
return gateways;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPlatformConnectionDetailsByGatewayId = async (gatewayId: string) => {
|
const getPlatformConnectionDetailsByGatewayId = async ({
|
||||||
|
gatewayId,
|
||||||
|
targetHost,
|
||||||
|
targetPort
|
||||||
|
}: {
|
||||||
|
gatewayId: string;
|
||||||
|
targetHost: string;
|
||||||
|
targetPort: number;
|
||||||
|
}) => {
|
||||||
const gateway = await gatewayV2DAL.findById(gatewayId);
|
const gateway = await gatewayV2DAL.findById(gatewayId);
|
||||||
if (!gateway) {
|
if (!gateway) {
|
||||||
return;
|
return;
|
||||||
@@ -248,12 +260,12 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// const orgLicensePlan = await licenseService.getPlan(orgGatewayConfig.orgId);
|
const orgLicensePlan = await licenseService.getPlan(orgGatewayConfig.orgId);
|
||||||
// if (!orgLicensePlan.gateway) {
|
if (!orgLicensePlan.gateway) {
|
||||||
// throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
// message: "Please upgrade your instance to Infisical's Enterprise plan to use gateways."
|
message: "Please upgrade your instance to Infisical's Enterprise plan to use gateways."
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
|
|
||||||
const { decryptor: orgKmsDecryptor } = await kmsService.createCipherPairWithDataKey({
|
const { decryptor: orgKmsDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
type: KmsDataKey.Organization,
|
type: KmsDataKey.Organization,
|
||||||
@@ -274,6 +286,12 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const gatewayServerCaCert = new x509.X509Certificate(
|
||||||
|
orgKmsDecryptor({
|
||||||
|
cipherTextBlob: orgGatewayConfig.encryptedGatewayServerCaCertificate
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const gatewayClientCaPrivateKey = orgKmsDecryptor({
|
const gatewayClientCaPrivateKey = orgKmsDecryptor({
|
||||||
cipherTextBlob: orgGatewayConfig.encryptedGatewayClientCaPrivateKey
|
cipherTextBlob: orgGatewayConfig.encryptedGatewayClientCaPrivateKey
|
||||||
});
|
});
|
||||||
@@ -297,6 +315,23 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
const clientKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
|
const clientKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
|
||||||
const clientCertSerialNumber = createSerialNumber();
|
const clientCertSerialNumber = createSerialNumber();
|
||||||
|
|
||||||
|
const routingInfo = {
|
||||||
|
targetHost,
|
||||||
|
targetPort
|
||||||
|
};
|
||||||
|
|
||||||
|
const routingExtension = new x509.Extension(
|
||||||
|
GATEWAY_ROUTING_INFO_OID,
|
||||||
|
false,
|
||||||
|
Buffer.from(JSON.stringify(routingInfo))
|
||||||
|
);
|
||||||
|
|
||||||
|
const actorExtension = new x509.Extension(
|
||||||
|
GATEWAY_ACTOR_OID,
|
||||||
|
false,
|
||||||
|
Buffer.from(JSON.stringify({ type: ActorType.PLATFORM }))
|
||||||
|
);
|
||||||
|
|
||||||
const clientCert = await x509.X509CertificateGenerator.create({
|
const clientCert = await x509.X509CertificateGenerator.create({
|
||||||
serialNumber: clientCertSerialNumber,
|
serialNumber: clientCertSerialNumber,
|
||||||
subject: `O=${orgGatewayConfig.orgId},OU=gateway-client,CN=${ActorType.PLATFORM}:${gatewayId}`,
|
subject: `O=${orgGatewayConfig.orgId},OU=gateway-client,CN=${ActorType.PLATFORM}:${gatewayId}`,
|
||||||
@@ -318,16 +353,18 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
x509.KeyUsageFlags[CertKeyUsage.KEY_AGREEMENT],
|
x509.KeyUsageFlags[CertKeyUsage.KEY_AGREEMENT],
|
||||||
true
|
true
|
||||||
),
|
),
|
||||||
new x509.ExtendedKeyUsageExtension([x509.ExtendedKeyUsage[CertExtendedKeyUsage.CLIENT_AUTH]], true)
|
new x509.ExtendedKeyUsageExtension([x509.ExtendedKeyUsage[CertExtendedKeyUsage.CLIENT_AUTH]], true),
|
||||||
|
routingExtension,
|
||||||
|
actorExtension
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
const gatewayClientCertPrivateKey = crypto.nativeCrypto.KeyObject.from(clientKeys.privateKey);
|
const gatewayClientCertPrivateKey = crypto.nativeCrypto.KeyObject.from(clientKeys.privateKey);
|
||||||
|
|
||||||
const proxyCredentials = await proxyService.getCredentialsForClient({
|
const proxyCredentials = await proxyService.getCredentialsForClient({
|
||||||
proxyId: gateway.proxyId,
|
proxyId: gateway.proxyId,
|
||||||
orgId: gateway.orgId,
|
orgId: gateway.orgId,
|
||||||
gatewayId,
|
gatewayId
|
||||||
actor: ActorType.PLATFORM
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -335,8 +372,7 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
gateway: {
|
gateway: {
|
||||||
clientCertificate: clientCert.toString("pem"),
|
clientCertificate: clientCert.toString("pem"),
|
||||||
clientPrivateKey: gatewayClientCertPrivateKey.export({ format: "pem", type: "pkcs8" }).toString(),
|
clientPrivateKey: gatewayClientCertPrivateKey.export({ format: "pem", type: "pkcs8" }).toString(),
|
||||||
clientCertificateChain: constructPemChainFromCerts([gatewayClientCaCert, rootGatewayCaCert]),
|
serverCertificateChain: constructPemChainFromCerts([gatewayServerCaCert, rootGatewayCaCert])
|
||||||
serverCA: rootGatewayCaCert.toString("pem")
|
|
||||||
},
|
},
|
||||||
proxy: {
|
proxy: {
|
||||||
clientCertificate: proxyCredentials.clientCertificate,
|
clientCertificate: proxyCredentials.clientCertificate,
|
||||||
@@ -385,6 +421,7 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
const alg = keyAlgorithmToAlgCfg(CertKeyAlgorithm.RSA_2048);
|
const alg = keyAlgorithmToAlgCfg(CertKeyAlgorithm.RSA_2048);
|
||||||
const gatewayServerCaCert = new x509.X509Certificate(orgCAs.gatewayServerCaCertificate);
|
const gatewayServerCaCert = new x509.X509Certificate(orgCAs.gatewayServerCaCertificate);
|
||||||
const rootGatewayCaCert = new x509.X509Certificate(orgCAs.rootGatewayCaCertificate);
|
const rootGatewayCaCert = new x509.X509Certificate(orgCAs.rootGatewayCaCertificate);
|
||||||
|
const gatewayClientCaCert = new x509.X509Certificate(orgCAs.gatewayClientCaCertificate);
|
||||||
|
|
||||||
const gatewayServerCaSkObj = crypto.nativeCrypto.createPrivateKey({
|
const gatewayServerCaSkObj = crypto.nativeCrypto.createPrivateKey({
|
||||||
key: orgCAs.gatewayServerCaPrivateKey,
|
key: orgCAs.gatewayServerCaPrivateKey,
|
||||||
@@ -414,7 +451,12 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
x509.KeyUsageFlags[CertKeyUsage.DIGITAL_SIGNATURE] | x509.KeyUsageFlags[CertKeyUsage.KEY_ENCIPHERMENT],
|
x509.KeyUsageFlags[CertKeyUsage.DIGITAL_SIGNATURE] | x509.KeyUsageFlags[CertKeyUsage.KEY_ENCIPHERMENT],
|
||||||
true
|
true
|
||||||
),
|
),
|
||||||
new x509.ExtendedKeyUsageExtension([x509.ExtendedKeyUsage[CertExtendedKeyUsage.SERVER_AUTH]], true)
|
new x509.ExtendedKeyUsageExtension([x509.ExtendedKeyUsage[CertExtendedKeyUsage.SERVER_AUTH]], true),
|
||||||
|
new x509.SubjectAlternativeNameExtension([
|
||||||
|
{ type: "dns", value: "localhost" },
|
||||||
|
{ type: "ip", value: "127.0.0.1" },
|
||||||
|
{ type: "ip", value: "::1" }
|
||||||
|
])
|
||||||
];
|
];
|
||||||
|
|
||||||
const gatewayServerSerialNumber = createSerialNumber();
|
const gatewayServerSerialNumber = createSerialNumber();
|
||||||
@@ -441,9 +483,8 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
proxyIp: proxyCredentials.proxyIp,
|
proxyIp: proxyCredentials.proxyIp,
|
||||||
pki: {
|
pki: {
|
||||||
serverCertificate: gatewayServerCertificate.toString("pem"),
|
serverCertificate: gatewayServerCertificate.toString("pem"),
|
||||||
serverCertificateChain: constructPemChainFromCerts([gatewayServerCaCert, rootGatewayCaCert]),
|
|
||||||
serverPrivateKey: gatewayServerCertPrivateKey.export({ format: "pem", type: "pkcs8" }).toString(),
|
serverPrivateKey: gatewayServerCertPrivateKey.export({ format: "pem", type: "pkcs8" }).toString(),
|
||||||
clientCA: rootGatewayCaCert.toString("pem")
|
clientCertificateChain: constructPemChainFromCerts([gatewayClientCaCert, rootGatewayCaCert])
|
||||||
},
|
},
|
||||||
ssh: {
|
ssh: {
|
||||||
clientCertificate: proxyCredentials.clientSshCert,
|
clientCertificate: proxyCredentials.clientSshCert,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { TProxies } from "@app/db/schemas";
|
|||||||
import { PgSqlLock } from "@app/keystore/keystore";
|
import { PgSqlLock } from "@app/keystore/keystore";
|
||||||
import { crypto } from "@app/lib/crypto";
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
|
||||||
import { constructPemChainFromCerts, prependCertToPemChain } from "@app/services/certificate/certificate-fns";
|
import { constructPemChainFromCerts, prependCertToPemChain } from "@app/services/certificate/certificate-fns";
|
||||||
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
||||||
import {
|
import {
|
||||||
@@ -689,7 +688,6 @@ export const proxyServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const $generateProxyClientCredentials = async ({
|
const $generateProxyClientCredentials = async ({
|
||||||
actor,
|
|
||||||
gatewayId,
|
gatewayId,
|
||||||
orgId,
|
orgId,
|
||||||
proxyPkiClientCaCertificate,
|
proxyPkiClientCaCertificate,
|
||||||
@@ -697,7 +695,6 @@ export const proxyServiceFactory = ({
|
|||||||
proxyPkiServerCaCertificate,
|
proxyPkiServerCaCertificate,
|
||||||
proxyPkiServerCaCertificateChain
|
proxyPkiServerCaCertificateChain
|
||||||
}: {
|
}: {
|
||||||
actor: ActorType;
|
|
||||||
gatewayId: string;
|
gatewayId: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
proxyPkiClientCaCertificate: Buffer;
|
proxyPkiClientCaCertificate: Buffer;
|
||||||
@@ -728,29 +725,32 @@ export const proxyServiceFactory = ({
|
|||||||
const clientCertPrivateKey = crypto.nativeCrypto.KeyObject.from(clientKeys.privateKey);
|
const clientCertPrivateKey = crypto.nativeCrypto.KeyObject.from(clientKeys.privateKey);
|
||||||
const clientCertSerialNumber = createSerialNumber();
|
const clientCertSerialNumber = createSerialNumber();
|
||||||
|
|
||||||
|
// Build standard extensions
|
||||||
|
const extensions: x509.Extension[] = [
|
||||||
|
new x509.BasicConstraintsExtension(false),
|
||||||
|
await x509.AuthorityKeyIdentifierExtension.create(proxyClientCaCert, false),
|
||||||
|
await x509.SubjectKeyIdentifierExtension.create(clientKeys.publicKey),
|
||||||
|
new x509.CertificatePolicyExtension(["2.5.29.32.0"]), // anyPolicy
|
||||||
|
new x509.KeyUsagesExtension(
|
||||||
|
// eslint-disable-next-line no-bitwise
|
||||||
|
x509.KeyUsageFlags[CertKeyUsage.DIGITAL_SIGNATURE] |
|
||||||
|
x509.KeyUsageFlags[CertKeyUsage.KEY_ENCIPHERMENT] |
|
||||||
|
x509.KeyUsageFlags[CertKeyUsage.KEY_AGREEMENT],
|
||||||
|
true
|
||||||
|
),
|
||||||
|
new x509.ExtendedKeyUsageExtension([x509.ExtendedKeyUsage[CertExtendedKeyUsage.CLIENT_AUTH]], true)
|
||||||
|
];
|
||||||
|
|
||||||
const clientCert = await x509.X509CertificateGenerator.create({
|
const clientCert = await x509.X509CertificateGenerator.create({
|
||||||
serialNumber: clientCertSerialNumber,
|
serialNumber: clientCertSerialNumber,
|
||||||
subject: `O=${orgId},OU=proxy-client,CN=${actor}:${gatewayId}`,
|
subject: `O=${orgId},OU=proxy-client,CN=${gatewayId}`,
|
||||||
issuer: proxyClientCaCert.subject,
|
issuer: proxyClientCaCert.subject,
|
||||||
notAfter: clientCertExpiration,
|
notAfter: clientCertExpiration,
|
||||||
notBefore: clientCertIssuedAt,
|
notBefore: clientCertIssuedAt,
|
||||||
signingKey: importedProxyClientCaPrivateKey,
|
signingKey: importedProxyClientCaPrivateKey,
|
||||||
publicKey: clientKeys.publicKey,
|
publicKey: clientKeys.publicKey,
|
||||||
signingAlgorithm: alg,
|
signingAlgorithm: alg,
|
||||||
extensions: [
|
extensions
|
||||||
new x509.BasicConstraintsExtension(false),
|
|
||||||
await x509.AuthorityKeyIdentifierExtension.create(proxyClientCaCert, false),
|
|
||||||
await x509.SubjectKeyIdentifierExtension.create(clientKeys.publicKey),
|
|
||||||
new x509.CertificatePolicyExtension(["2.5.29.32.0"]), // anyPolicy
|
|
||||||
new x509.KeyUsagesExtension(
|
|
||||||
// eslint-disable-next-line no-bitwise
|
|
||||||
x509.KeyUsageFlags[CertKeyUsage.DIGITAL_SIGNATURE] |
|
|
||||||
x509.KeyUsageFlags[CertKeyUsage.KEY_ENCIPHERMENT] |
|
|
||||||
x509.KeyUsageFlags[CertKeyUsage.KEY_AGREEMENT],
|
|
||||||
true
|
|
||||||
),
|
|
||||||
new x509.ExtendedKeyUsageExtension([x509.ExtendedKeyUsage[CertExtendedKeyUsage.CLIENT_AUTH]], true)
|
|
||||||
]
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -834,13 +834,11 @@ export const proxyServiceFactory = ({
|
|||||||
const getCredentialsForClient = async ({
|
const getCredentialsForClient = async ({
|
||||||
proxyId,
|
proxyId,
|
||||||
orgId,
|
orgId,
|
||||||
gatewayId,
|
gatewayId
|
||||||
actor
|
|
||||||
}: {
|
}: {
|
||||||
proxyId: string;
|
proxyId: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
gatewayId: string;
|
gatewayId: string;
|
||||||
actor: ActorType;
|
|
||||||
}) => {
|
}) => {
|
||||||
const proxy = await proxyDAL.findOne({
|
const proxy = await proxyDAL.findOne({
|
||||||
id: proxyId
|
id: proxyId
|
||||||
@@ -855,7 +853,6 @@ export const proxyServiceFactory = ({
|
|||||||
if (isInstanceProxy(proxy.name)) {
|
if (isInstanceProxy(proxy.name)) {
|
||||||
const instanceCAs = await $getInstanceCAs();
|
const instanceCAs = await $getInstanceCAs();
|
||||||
const proxyCertificateCredentials = await $generateProxyClientCredentials({
|
const proxyCertificateCredentials = await $generateProxyClientCredentials({
|
||||||
actor,
|
|
||||||
gatewayId,
|
gatewayId,
|
||||||
orgId,
|
orgId,
|
||||||
proxyPkiClientCaCertificate: instanceCAs.instanceProxyPkiClientCaCertificate,
|
proxyPkiClientCaCertificate: instanceCAs.instanceProxyPkiClientCaCertificate,
|
||||||
@@ -872,7 +869,6 @@ export const proxyServiceFactory = ({
|
|||||||
|
|
||||||
const orgCAs = await $getOrgCAs(orgId);
|
const orgCAs = await $getOrgCAs(orgId);
|
||||||
const proxyCertificateCredentials = await $generateProxyClientCredentials({
|
const proxyCertificateCredentials = await $generateProxyClientCredentials({
|
||||||
actor,
|
|
||||||
gatewayId,
|
gatewayId,
|
||||||
orgId,
|
orgId,
|
||||||
proxyPkiClientCaCertificate: orgCAs.proxyPkiClientCaCertificate,
|
proxyPkiClientCaCertificate: orgCAs.proxyPkiClientCaCertificate,
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ import {
|
|||||||
import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal";
|
import { TSecretVersionV2DALFactory } from "@app/services/secret-v2-bridge/secret-version-dal";
|
||||||
import { TSecretVersionV2TagDALFactory } from "@app/services/secret-v2-bridge/secret-version-tag-dal";
|
import { TSecretVersionV2TagDALFactory } from "@app/services/secret-v2-bridge/secret-version-tag-dal";
|
||||||
|
|
||||||
|
import { TGatewayV2ServiceFactory } from "../gateway-v2/gateway-v2-service";
|
||||||
import { awsIamUserSecretRotationFactory } from "./aws-iam-user-secret/aws-iam-user-secret-rotation-fns";
|
import { awsIamUserSecretRotationFactory } from "./aws-iam-user-secret/aws-iam-user-secret-rotation-fns";
|
||||||
import { oktaClientSecretRotationFactory } from "./okta-client-secret/okta-client-secret-rotation-fns";
|
import { oktaClientSecretRotationFactory } from "./okta-client-secret/okta-client-secret-rotation-fns";
|
||||||
import { TSecretRotationV2DALFactory } from "./secret-rotation-v2-dal";
|
import { TSecretRotationV2DALFactory } from "./secret-rotation-v2-dal";
|
||||||
@@ -110,6 +111,7 @@ export type TSecretRotationV2ServiceFactoryDep = {
|
|||||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update" | "updateById">;
|
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update" | "updateById">;
|
||||||
folderCommitService: Pick<TFolderCommitServiceFactory, "createCommit">;
|
folderCommitService: Pick<TFolderCommitServiceFactory, "createCommit">;
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TSecretRotationV2ServiceFactory = ReturnType<typeof secretRotationV2ServiceFactory>;
|
export type TSecretRotationV2ServiceFactory = ReturnType<typeof secretRotationV2ServiceFactory>;
|
||||||
@@ -153,7 +155,8 @@ export const secretRotationV2ServiceFactory = ({
|
|||||||
queueService,
|
queueService,
|
||||||
folderCommitService,
|
folderCommitService,
|
||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
gatewayService
|
gatewayService,
|
||||||
|
gatewayV2Service
|
||||||
}: TSecretRotationV2ServiceFactoryDep) => {
|
}: TSecretRotationV2ServiceFactoryDep) => {
|
||||||
const $queueSendSecretRotationStatusNotification = async (secretRotation: TSecretRotationV2Raw) => {
|
const $queueSendSecretRotationStatusNotification = async (secretRotation: TSecretRotationV2Raw) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
@@ -467,7 +470,8 @@ export const secretRotationV2ServiceFactory = ({
|
|||||||
} as TSecretRotationV2WithConnection,
|
} as TSecretRotationV2WithConnection,
|
||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
gatewayService
|
gatewayService,
|
||||||
|
gatewayV2Service
|
||||||
);
|
);
|
||||||
|
|
||||||
// even though we have a db constraint we want to check before any rotation of credentials is attempted
|
// even though we have a db constraint we want to check before any rotation of credentials is attempted
|
||||||
@@ -831,7 +835,8 @@ export const secretRotationV2ServiceFactory = ({
|
|||||||
} as TSecretRotationV2WithConnection,
|
} as TSecretRotationV2WithConnection,
|
||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
gatewayService
|
gatewayService,
|
||||||
|
gatewayV2Service
|
||||||
);
|
);
|
||||||
|
|
||||||
const generatedCredentials = await decryptSecretRotationCredentials({
|
const generatedCredentials = await decryptSecretRotationCredentials({
|
||||||
@@ -915,7 +920,8 @@ export const secretRotationV2ServiceFactory = ({
|
|||||||
} as TSecretRotationV2WithConnection,
|
} as TSecretRotationV2WithConnection,
|
||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
gatewayService
|
gatewayService,
|
||||||
|
gatewayV2Service
|
||||||
);
|
);
|
||||||
|
|
||||||
const updatedRotation = await rotationFactory.rotateCredentials(
|
const updatedRotation = await rotationFactory.rotateCredentials(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { TAppConnectionDALFactory } from "@app/services/app-connection/app-conne
|
|||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { SecretsOrderBy } from "@app/services/secret/secret-types";
|
import { SecretsOrderBy } from "@app/services/secret/secret-types";
|
||||||
|
|
||||||
|
import { TGatewayV2ServiceFactory } from "../gateway-v2/gateway-v2-service";
|
||||||
import {
|
import {
|
||||||
TAuth0ClientSecretRotation,
|
TAuth0ClientSecretRotation,
|
||||||
TAuth0ClientSecretRotationGeneratedCredentials,
|
TAuth0ClientSecretRotationGeneratedCredentials,
|
||||||
@@ -253,7 +254,8 @@ export type TRotationFactory<
|
|||||||
secretRotation: T,
|
secretRotation: T,
|
||||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update" | "updateById">,
|
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update" | "updateById">,
|
||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">,
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">
|
||||||
) => {
|
) => {
|
||||||
issueCredentials: TRotationFactoryIssueCredentials<C, P>;
|
issueCredentials: TRotationFactoryIssueCredentials<C, P>;
|
||||||
revokeCredentials: TRotationFactoryRevokeCredentials<C>;
|
revokeCredentials: TRotationFactoryRevokeCredentials<C>;
|
||||||
|
|||||||
+2
-1
@@ -41,7 +41,7 @@ const ORACLE_PASSWORD_REQUIREMENTS = {
|
|||||||
export const sqlCredentialsRotationFactory: TRotationFactory<
|
export const sqlCredentialsRotationFactory: TRotationFactory<
|
||||||
TSqlCredentialsRotationWithConnection,
|
TSqlCredentialsRotationWithConnection,
|
||||||
TSqlCredentialsRotationGeneratedCredentials
|
TSqlCredentialsRotationGeneratedCredentials
|
||||||
> = (secretRotation, _appConnectionDAL, _kmsService, gatewayService) => {
|
> = (secretRotation, _appConnectionDAL, _kmsService, gatewayService, gatewayV2Service) => {
|
||||||
const {
|
const {
|
||||||
connection,
|
connection,
|
||||||
parameters: { username1, username2 },
|
parameters: { username1, username2 },
|
||||||
@@ -67,6 +67,7 @@ export const sqlCredentialsRotationFactory: TRotationFactory<
|
|||||||
credentials: finalCredentials
|
credentials: finalCredentials
|
||||||
},
|
},
|
||||||
gatewayService,
|
gatewayService,
|
||||||
|
gatewayV2Service,
|
||||||
(client) => operation(client)
|
(client) => operation(client)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,278 @@
|
|||||||
|
import net from "node:net";
|
||||||
|
import tls from "node:tls";
|
||||||
|
|
||||||
|
import https from "https";
|
||||||
|
|
||||||
|
import { splitPemChain } from "@app/services/certificate/certificate-fns";
|
||||||
|
|
||||||
|
import { BadRequestError } from "../errors";
|
||||||
|
import { GatewayProxyProtocol } from "../gateway/types";
|
||||||
|
import { logger } from "../logger";
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODOs:
|
||||||
|
- Add heartbeat tracking to gateway connection
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface IGatewayProxyServer {
|
||||||
|
server: net.Server;
|
||||||
|
port: number;
|
||||||
|
cleanup: () => Promise<void>;
|
||||||
|
getProxyError: () => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const createProxyConnection = async ({
|
||||||
|
proxyIp,
|
||||||
|
clientCertificate,
|
||||||
|
clientPrivateKey,
|
||||||
|
serverCertificateChain
|
||||||
|
}: {
|
||||||
|
proxyIp: string;
|
||||||
|
clientCertificate: string;
|
||||||
|
clientPrivateKey: string;
|
||||||
|
serverCertificateChain: string;
|
||||||
|
}): Promise<net.Socket> => {
|
||||||
|
const [host, portStr] = proxyIp.split(":");
|
||||||
|
const port = parseInt(portStr, 10) || 443;
|
||||||
|
|
||||||
|
const serverCAs = splitPemChain(serverCertificateChain);
|
||||||
|
const tlsOptions: tls.ConnectionOptions = {
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
cert: clientCertificate,
|
||||||
|
key: clientPrivateKey,
|
||||||
|
ca: serverCAs,
|
||||||
|
minVersion: "TLSv1.2",
|
||||||
|
rejectUnauthorized: true
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const socket = tls.connect(tlsOptions, () => {
|
||||||
|
logger.info("Proxy TLS connection established successfully");
|
||||||
|
resolve(socket);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("error", (err: Error) => {
|
||||||
|
reject(new Error(`TLS connection error: ${err.message}`));
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("close", (hadError: boolean) => {
|
||||||
|
logger.error(`TLS connection closed${hadError ? " with error" : ""}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("timeout", () => {
|
||||||
|
logger.error(`TLS connection timeout after 30 seconds`);
|
||||||
|
socket.destroy();
|
||||||
|
reject(new Error("TLS connection timeout"));
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.setTimeout(30000);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
reject(new Error(`Failed to create TLS connection: ${error instanceof Error ? error.message : String(error)}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const createGatewayConnection = async (
|
||||||
|
proxyConn: net.Socket,
|
||||||
|
gateway: { clientCertificate: string; clientPrivateKey: string; serverCertificateChain: string }
|
||||||
|
): Promise<net.Socket> => {
|
||||||
|
const tlsOptions: tls.ConnectionOptions = {
|
||||||
|
socket: proxyConn,
|
||||||
|
cert: gateway.clientCertificate,
|
||||||
|
key: gateway.clientPrivateKey,
|
||||||
|
ca: splitPemChain(gateway.serverCertificateChain),
|
||||||
|
minVersion: "TLSv1.2",
|
||||||
|
maxVersion: "TLSv1.3",
|
||||||
|
rejectUnauthorized: true
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const gatewaySocket = tls.connect(tlsOptions, () => {
|
||||||
|
if (!gatewaySocket.authorized) {
|
||||||
|
const error = gatewaySocket.authorizationError;
|
||||||
|
gatewaySocket.destroy();
|
||||||
|
reject(new Error(`Gateway TLS authorization failed: ${error?.message}`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Gateway mTLS connection established successfully");
|
||||||
|
resolve(gatewaySocket);
|
||||||
|
});
|
||||||
|
|
||||||
|
gatewaySocket.on("error", (err: Error) => {
|
||||||
|
reject(new Error(`Failed to establish gateway mTLS: ${err.message}`));
|
||||||
|
});
|
||||||
|
|
||||||
|
gatewaySocket.setTimeout(30000);
|
||||||
|
gatewaySocket.on("timeout", () => {
|
||||||
|
gatewaySocket.destroy();
|
||||||
|
reject(new Error("Gateway connection timeout"));
|
||||||
|
});
|
||||||
|
} catch (error: unknown) {
|
||||||
|
reject(
|
||||||
|
new Error(`Failed to create gateway TLS connection: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const setupProxyServer = async ({
|
||||||
|
protocol,
|
||||||
|
proxyIp,
|
||||||
|
gateway,
|
||||||
|
proxy,
|
||||||
|
httpsAgent
|
||||||
|
}: {
|
||||||
|
protocol: GatewayProxyProtocol;
|
||||||
|
proxyIp: string;
|
||||||
|
gateway: { clientCertificate: string; clientPrivateKey: string; serverCertificateChain: string };
|
||||||
|
proxy: { clientCertificate: string; clientPrivateKey: string; serverCertificateChain: string };
|
||||||
|
httpsAgent?: https.Agent;
|
||||||
|
}): Promise<IGatewayProxyServer> => {
|
||||||
|
const proxyErrorMsg: string[] = [];
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const server = net.createServer();
|
||||||
|
|
||||||
|
server.on("connection", (clientConn) => {
|
||||||
|
void (async () => {
|
||||||
|
try {
|
||||||
|
clientConn.setKeepAlive(true, 30000);
|
||||||
|
clientConn.setNoDelay(true);
|
||||||
|
|
||||||
|
// Stage 1: Connect to proxy relay with TLS
|
||||||
|
const proxyConn = await createProxyConnection({
|
||||||
|
proxyIp,
|
||||||
|
clientCertificate: proxy.clientCertificate,
|
||||||
|
clientPrivateKey: proxy.clientPrivateKey,
|
||||||
|
serverCertificateChain: proxy.serverCertificateChain
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stage 2: Establish mTLS connection to gateway through the proxy
|
||||||
|
const gatewayConn = await createGatewayConnection(proxyConn, gateway);
|
||||||
|
|
||||||
|
let command = "";
|
||||||
|
|
||||||
|
// Send protocol data to gateway
|
||||||
|
if (protocol === GatewayProxyProtocol.Http) {
|
||||||
|
command += "FORWARD-HTTP";
|
||||||
|
// extract ca certificate from httpsAgent if present
|
||||||
|
if (httpsAgent) {
|
||||||
|
const agentOptions = httpsAgent.options;
|
||||||
|
if (agentOptions && agentOptions.ca) {
|
||||||
|
const caCert = Array.isArray(agentOptions.ca) ? agentOptions.ca.join("\n") : agentOptions.ca;
|
||||||
|
const caB64 = Buffer.from(caCert as string).toString("base64");
|
||||||
|
command += ` ca=${caB64}`;
|
||||||
|
|
||||||
|
const rejectUnauthorized = agentOptions.rejectUnauthorized !== false;
|
||||||
|
command += ` verify=${rejectUnauthorized}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
command += "\n";
|
||||||
|
} else if (protocol === GatewayProxyProtocol.Tcp) {
|
||||||
|
command += `FORWARD-TCP\n`;
|
||||||
|
} else {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Invalid protocol: ${protocol as string}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
gatewayConn.write(Buffer.from(command));
|
||||||
|
|
||||||
|
// Bidirectional data forwarding
|
||||||
|
clientConn.pipe(gatewayConn);
|
||||||
|
gatewayConn.pipe(clientConn);
|
||||||
|
|
||||||
|
// Handle connection closure
|
||||||
|
clientConn.on("close", () => {
|
||||||
|
proxyConn.destroy();
|
||||||
|
gatewayConn.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
proxyConn.on("close", () => {
|
||||||
|
clientConn.destroy();
|
||||||
|
gatewayConn.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
gatewayConn.on("close", () => {
|
||||||
|
clientConn.destroy();
|
||||||
|
proxyConn.destroy();
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
||||||
|
proxyErrorMsg.push(errorMsg);
|
||||||
|
clientConn.destroy();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on("error", (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(0, () => {
|
||||||
|
const address = server.address();
|
||||||
|
if (!address || typeof address === "string") {
|
||||||
|
server.close();
|
||||||
|
reject(new Error("Failed to get server port"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Gateway proxy started on port ${address.port}`);
|
||||||
|
resolve({
|
||||||
|
server,
|
||||||
|
port: address.port,
|
||||||
|
cleanup: async () => {
|
||||||
|
try {
|
||||||
|
server.close();
|
||||||
|
} catch (err) {
|
||||||
|
console.debug("Error closing server:", err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getProxyError: () => proxyErrorMsg.join(",")
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const withGatewayV2Proxy = async <T>(
|
||||||
|
callback: (port: number) => Promise<T>,
|
||||||
|
options: {
|
||||||
|
protocol: GatewayProxyProtocol;
|
||||||
|
proxyIp: string;
|
||||||
|
gateway: { clientCertificate: string; clientPrivateKey: string; serverCertificateChain: string };
|
||||||
|
proxy: { clientCertificate: string; clientPrivateKey: string; serverCertificateChain: string };
|
||||||
|
httpsAgent?: https.Agent;
|
||||||
|
}
|
||||||
|
): Promise<T> => {
|
||||||
|
const { protocol, proxyIp, gateway, proxy, httpsAgent } = options;
|
||||||
|
|
||||||
|
const { port, cleanup, getProxyError } = await setupProxyServer({
|
||||||
|
protocol,
|
||||||
|
proxyIp,
|
||||||
|
gateway,
|
||||||
|
proxy,
|
||||||
|
httpsAgent
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Execute the callback with the allocated port
|
||||||
|
return await callback(port);
|
||||||
|
} catch (err) {
|
||||||
|
const proxyErrorMessage = getProxyError();
|
||||||
|
if (proxyErrorMessage) {
|
||||||
|
logger.error("Proxy error:", proxyErrorMessage);
|
||||||
|
}
|
||||||
|
logger.error("Gateway error:", err instanceof Error ? err.message : String(err));
|
||||||
|
|
||||||
|
const errorMessage = proxyErrorMessage || (err instanceof Error ? err.message : String(err));
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
} finally {
|
||||||
|
// Ensure cleanup happens regardless of success or failure
|
||||||
|
await cleanup();
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1463,6 +1463,22 @@ export const registerRoutes = async (
|
|||||||
smtpService
|
smtpService
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const proxyService = proxyServiceFactory({
|
||||||
|
instanceProxyConfigDAL,
|
||||||
|
orgProxyConfigDAL,
|
||||||
|
proxyDAL,
|
||||||
|
kmsService
|
||||||
|
});
|
||||||
|
|
||||||
|
const gatewayV2Service = gatewayV2ServiceFactory({
|
||||||
|
kmsService,
|
||||||
|
licenseService,
|
||||||
|
proxyService,
|
||||||
|
orgGatewayConfigV2DAL,
|
||||||
|
gatewayV2DAL,
|
||||||
|
proxyDAL
|
||||||
|
});
|
||||||
|
|
||||||
const identityService = identityServiceFactory({
|
const identityService = identityServiceFactory({
|
||||||
permissionService,
|
permissionService,
|
||||||
identityDAL,
|
identityDAL,
|
||||||
@@ -1517,6 +1533,7 @@ export const registerRoutes = async (
|
|||||||
permissionService,
|
permissionService,
|
||||||
licenseService
|
licenseService
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityUaService = identityUaServiceFactory({
|
const identityUaService = identityUaServiceFactory({
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
@@ -1533,6 +1550,8 @@ export const registerRoutes = async (
|
|||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService,
|
||||||
gatewayService,
|
gatewayService,
|
||||||
|
gatewayV2Service,
|
||||||
|
gatewayV2DAL,
|
||||||
gatewayDAL,
|
gatewayDAL,
|
||||||
kmsService
|
kmsService
|
||||||
});
|
});
|
||||||
@@ -1628,21 +1647,6 @@ export const registerRoutes = async (
|
|||||||
identityAuthTemplateDAL
|
identityAuthTemplateDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const proxyService = proxyServiceFactory({
|
|
||||||
instanceProxyConfigDAL,
|
|
||||||
orgProxyConfigDAL,
|
|
||||||
proxyDAL,
|
|
||||||
kmsService
|
|
||||||
});
|
|
||||||
|
|
||||||
const gatewayV2Service = gatewayV2ServiceFactory({
|
|
||||||
kmsService,
|
|
||||||
proxyService,
|
|
||||||
orgGatewayConfigV2DAL,
|
|
||||||
gatewayV2DAL,
|
|
||||||
proxyDAL
|
|
||||||
});
|
|
||||||
|
|
||||||
const dynamicSecretProviders = buildDynamicSecretProviders({
|
const dynamicSecretProviders = buildDynamicSecretProviders({
|
||||||
gatewayService,
|
gatewayService,
|
||||||
gatewayV2Service
|
gatewayV2Service
|
||||||
@@ -1791,7 +1795,9 @@ export const registerRoutes = async (
|
|||||||
kmsService,
|
kmsService,
|
||||||
licenseService,
|
licenseService,
|
||||||
gatewayService,
|
gatewayService,
|
||||||
gatewayDAL
|
gatewayV2Service,
|
||||||
|
gatewayDAL,
|
||||||
|
gatewayV2DAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretSyncService = secretSyncServiceFactory({
|
const secretSyncService = secretSyncServiceFactory({
|
||||||
@@ -1890,7 +1896,8 @@ export const registerRoutes = async (
|
|||||||
secretQueueService,
|
secretQueueService,
|
||||||
queueService,
|
queueService,
|
||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
gatewayService
|
gatewayService,
|
||||||
|
gatewayV2Service
|
||||||
});
|
});
|
||||||
|
|
||||||
const certificateAuthorityService = certificateAuthorityServiceFactory({
|
const certificateAuthorityService = certificateAuthorityServiceFactory({
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
} from "@app/ee/services/app-connections/oci";
|
} from "@app/ee/services/app-connections/oci";
|
||||||
import { getOracleDBConnectionListItem, OracleDBConnectionMethod } from "@app/ee/services/app-connections/oracledb";
|
import { getOracleDBConnectionListItem, OracleDBConnectionMethod } from "@app/ee/services/app-connections/oracledb";
|
||||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||||
|
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
@@ -213,7 +214,8 @@ export const decryptAppConnectionCredentials = async ({
|
|||||||
|
|
||||||
export const validateAppConnectionCredentials = async (
|
export const validateAppConnectionCredentials = async (
|
||||||
appConnection: TAppConnectionConfig,
|
appConnection: TAppConnectionConfig,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">
|
||||||
): Promise<TAppConnection["credentials"]> => {
|
): Promise<TAppConnection["credentials"]> => {
|
||||||
const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TAppConnectionCredentialsValidator> = {
|
const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TAppConnectionCredentialsValidator> = {
|
||||||
[AppConnection.AWS]: validateAwsConnectionCredentials as TAppConnectionCredentialsValidator,
|
[AppConnection.AWS]: validateAwsConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||||
@@ -257,7 +259,7 @@ export const validateAppConnectionCredentials = async (
|
|||||||
[AppConnection.Netlify]: validateNetlifyConnectionCredentials as TAppConnectionCredentialsValidator
|
[AppConnection.Netlify]: validateNetlifyConnectionCredentials as TAppConnectionCredentialsValidator
|
||||||
};
|
};
|
||||||
|
|
||||||
return VALIDATE_APP_CONNECTION_CREDENTIALS_MAP[appConnection.app](appConnection, gatewayService);
|
return VALIDATE_APP_CONNECTION_CREDENTIALS_MAP[appConnection.app](appConnection, gatewayService, gatewayV2Service);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAppConnectionMethodName = (method: TAppConnection["method"]) => {
|
export const getAppConnectionMethodName = (method: TAppConnection["method"]) => {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { ociConnectionService } from "@app/ee/services/app-connections/oci/oci-c
|
|||||||
import { ValidateOracleDBConnectionCredentialsSchema } from "@app/ee/services/app-connections/oracledb";
|
import { ValidateOracleDBConnectionCredentialsSchema } from "@app/ee/services/app-connections/oracledb";
|
||||||
import { TGatewayDALFactory } from "@app/ee/services/gateway/gateway-dal";
|
import { TGatewayDALFactory } from "@app/ee/services/gateway/gateway-dal";
|
||||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||||
|
import { TGatewayV2DALFactory } from "@app/ee/services/gateway-v2/gateway-v2-dal";
|
||||||
|
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import {
|
import {
|
||||||
OrgPermissionAppConnectionActions,
|
OrgPermissionAppConnectionActions,
|
||||||
@@ -109,7 +111,9 @@ export type TAppConnectionServiceFactoryDep = {
|
|||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">;
|
||||||
gatewayDAL: Pick<TGatewayDALFactory, "find">;
|
gatewayDAL: Pick<TGatewayDALFactory, "find">;
|
||||||
|
gatewayV2DAL: Pick<TGatewayV2DALFactory, "find">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TAppConnectionServiceFactory = ReturnType<typeof appConnectionServiceFactory>;
|
export type TAppConnectionServiceFactory = ReturnType<typeof appConnectionServiceFactory>;
|
||||||
@@ -160,7 +164,9 @@ export const appConnectionServiceFactory = ({
|
|||||||
kmsService,
|
kmsService,
|
||||||
licenseService,
|
licenseService,
|
||||||
gatewayService,
|
gatewayService,
|
||||||
gatewayDAL
|
gatewayV2Service,
|
||||||
|
gatewayDAL,
|
||||||
|
gatewayV2DAL
|
||||||
}: TAppConnectionServiceFactoryDep) => {
|
}: TAppConnectionServiceFactoryDep) => {
|
||||||
const listAppConnectionsByOrg = async (actor: OrgServiceActor, app?: AppConnection) => {
|
const listAppConnectionsByOrg = async (actor: OrgServiceActor, app?: AppConnection) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
@@ -264,7 +270,8 @@ export const appConnectionServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: actor.orgId });
|
const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: actor.orgId });
|
||||||
if (!gateway) {
|
const [gatewayV2] = await gatewayV2DAL.find({ id: gatewayId, orgId: actor.orgId });
|
||||||
|
if (!gateway && !gatewayV2) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Gateway with ID ${gatewayId} not found for org`
|
message: `Gateway with ID ${gatewayId} not found for org`
|
||||||
});
|
});
|
||||||
@@ -286,7 +293,8 @@ export const appConnectionServiceFactory = ({
|
|||||||
orgId: actor.orgId,
|
orgId: actor.orgId,
|
||||||
gatewayId
|
gatewayId
|
||||||
} as TAppConnectionConfig,
|
} as TAppConnectionConfig,
|
||||||
gatewayService
|
gatewayService,
|
||||||
|
gatewayV2Service
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -319,7 +327,8 @@ export const appConnectionServiceFactory = ({
|
|||||||
gatewayId
|
gatewayId
|
||||||
} as TAppConnectionConfig,
|
} as TAppConnectionConfig,
|
||||||
(platformCredentials) => createConnection(platformCredentials),
|
(platformCredentials) => createConnection(platformCredentials),
|
||||||
gatewayService
|
gatewayService,
|
||||||
|
gatewayV2Service
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
connection = await createConnection(validatedCredentials);
|
connection = await createConnection(validatedCredentials);
|
||||||
@@ -415,7 +424,8 @@ export const appConnectionServiceFactory = ({
|
|||||||
method,
|
method,
|
||||||
gatewayId
|
gatewayId
|
||||||
} as TAppConnectionConfig,
|
} as TAppConnectionConfig,
|
||||||
gatewayService
|
gatewayService,
|
||||||
|
gatewayV2Service
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!updatedCredentials)
|
if (!updatedCredentials)
|
||||||
@@ -456,7 +466,8 @@ export const appConnectionServiceFactory = ({
|
|||||||
gatewayId
|
gatewayId
|
||||||
} as TAppConnectionConfig,
|
} as TAppConnectionConfig,
|
||||||
(platformCredentials) => updateConnection(platformCredentials),
|
(platformCredentials) => updateConnection(platformCredentials),
|
||||||
gatewayService
|
gatewayService,
|
||||||
|
gatewayV2Service
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
updatedConnection = await updateConnection(updatedCredentials);
|
updatedConnection = await updateConnection(updatedCredentials);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
TValidateOracleDBConnectionCredentialsSchema
|
TValidateOracleDBConnectionCredentialsSchema
|
||||||
} from "@app/ee/services/app-connections/oracledb";
|
} from "@app/ee/services/app-connections/oracledb";
|
||||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||||
|
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
||||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||||
import { TSqlConnectionConfig } from "@app/services/app-connection/shared/sql/sql-connection-types";
|
import { TSqlConnectionConfig } from "@app/services/app-connection/shared/sql/sql-connection-types";
|
||||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
@@ -401,13 +402,15 @@ export type TListAwsConnectionIamUsers = {
|
|||||||
|
|
||||||
export type TAppConnectionCredentialsValidator = (
|
export type TAppConnectionCredentialsValidator = (
|
||||||
appConnection: TAppConnectionConfig,
|
appConnection: TAppConnectionConfig,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">
|
||||||
) => Promise<TAppConnection["credentials"]>;
|
) => Promise<TAppConnection["credentials"]>;
|
||||||
|
|
||||||
export type TAppConnectionTransitionCredentialsToPlatform = (
|
export type TAppConnectionTransitionCredentialsToPlatform = (
|
||||||
appConnection: TAppConnectionConfig,
|
appConnection: TAppConnectionConfig,
|
||||||
callback: (credentials: TAppConnection["credentials"]) => Promise<TAppConnectionRaw>,
|
callback: (credentials: TAppConnection["credentials"]) => Promise<TAppConnectionRaw>,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">
|
||||||
) => Promise<TAppConnectionRaw>;
|
) => Promise<TAppConnectionRaw>;
|
||||||
|
|
||||||
export type TAppConnectionBaseConfig = {
|
export type TAppConnectionBaseConfig = {
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ import knex, { Knex } from "knex";
|
|||||||
|
|
||||||
import { verifyHostInputValidity } from "@app/ee/services/dynamic-secret/dynamic-secret-fns";
|
import { verifyHostInputValidity } from "@app/ee/services/dynamic-secret/dynamic-secret-fns";
|
||||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||||
|
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
||||||
import {
|
import {
|
||||||
TSqlCredentialsRotationGeneratedCredentials,
|
TSqlCredentialsRotationGeneratedCredentials,
|
||||||
TSqlCredentialsRotationWithConnection
|
TSqlCredentialsRotationWithConnection
|
||||||
} from "@app/ee/services/secret-rotation-v2/shared/sql-credentials/sql-credentials-rotation-types";
|
} from "@app/ee/services/secret-rotation-v2/shared/sql-credentials/sql-credentials-rotation-types";
|
||||||
import { BadRequestError, DatabaseError } from "@app/lib/errors";
|
import { BadRequestError, DatabaseError } from "@app/lib/errors";
|
||||||
import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
|
import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
|
||||||
|
import { withGatewayV2Proxy } from "@app/lib/gateway-v2/gateway-v2";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
import { TAppConnectionRaw, TSqlConnection } from "@app/services/app-connection/app-connection-types";
|
import { TAppConnectionRaw, TSqlConnection } from "@app/services/app-connection/app-connection-types";
|
||||||
@@ -104,12 +106,49 @@ export const getSqlConnectionClient = async (appConnection: Pick<TSqlConnection,
|
|||||||
export const executeWithPotentialGateway = async <T>(
|
export const executeWithPotentialGateway = async <T>(
|
||||||
config: TSqlConnectionConfig,
|
config: TSqlConnectionConfig,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">,
|
||||||
operation: (client: Knex) => Promise<T>
|
operation: (client: Knex) => Promise<T>
|
||||||
): Promise<T> => {
|
): Promise<T> => {
|
||||||
const { credentials, app, gatewayId } = config;
|
const { credentials, app, gatewayId } = config;
|
||||||
|
|
||||||
if (gatewayId && gatewayService) {
|
if (gatewayId && gatewayService && gatewayV2Service) {
|
||||||
const [targetHost] = await verifyHostInputValidity(credentials.host, true);
|
const [targetHost] = await verifyHostInputValidity(credentials.host, true);
|
||||||
|
const platformConnectionDetails = await gatewayV2Service.getPlatformConnectionDetailsByGatewayId({
|
||||||
|
gatewayId,
|
||||||
|
targetHost,
|
||||||
|
targetPort: credentials.port
|
||||||
|
});
|
||||||
|
|
||||||
|
if (platformConnectionDetails) {
|
||||||
|
return withGatewayV2Proxy(
|
||||||
|
async (proxyPort) => {
|
||||||
|
const client = knex({
|
||||||
|
client: SQL_CONNECTION_CLIENT_MAP[app],
|
||||||
|
connection: {
|
||||||
|
database: credentials.database,
|
||||||
|
port: proxyPort,
|
||||||
|
host: "localhost",
|
||||||
|
user: credentials.username,
|
||||||
|
password: credentials.password,
|
||||||
|
connectionTimeoutMillis: EXTERNAL_REQUEST_TIMEOUT,
|
||||||
|
...getConnectionConfig({ app, credentials })
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
return await operation(client);
|
||||||
|
} finally {
|
||||||
|
await client.destroy();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol: GatewayProxyProtocol.Tcp,
|
||||||
|
proxyIp: platformConnectionDetails.proxyIp,
|
||||||
|
gateway: platformConnectionDetails.gateway,
|
||||||
|
proxy: platformConnectionDetails.proxy
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const relayDetails = await gatewayService.fnGetGatewayClientTlsByGatewayId(gatewayId);
|
const relayDetails = await gatewayService.fnGetGatewayClientTlsByGatewayId(gatewayId);
|
||||||
const [relayHost, relayPort] = relayDetails.relayAddress.split(":");
|
const [relayHost, relayPort] = relayDetails.relayAddress.split(":");
|
||||||
|
|
||||||
@@ -161,10 +200,11 @@ export const executeWithPotentialGateway = async <T>(
|
|||||||
|
|
||||||
export const validateSqlConnectionCredentials = async (
|
export const validateSqlConnectionCredentials = async (
|
||||||
config: TSqlConnectionConfig,
|
config: TSqlConnectionConfig,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
await executeWithPotentialGateway(config, gatewayService, async (client) => {
|
await executeWithPotentialGateway(config, gatewayService, gatewayV2Service, async (client) => {
|
||||||
await client.raw(config.app === AppConnection.OracleDB ? `SELECT 1 FROM DUAL` : `Select 1`);
|
await client.raw(config.app === AppConnection.OracleDB ? `SELECT 1 FROM DUAL` : `Select 1`);
|
||||||
});
|
});
|
||||||
return config.credentials;
|
return config.credentials;
|
||||||
@@ -191,14 +231,15 @@ export const SQL_CONNECTION_ALTER_LOGIN_STATEMENT: Record<
|
|||||||
export const transferSqlConnectionCredentialsToPlatform = async (
|
export const transferSqlConnectionCredentialsToPlatform = async (
|
||||||
config: TSqlConnectionConfig,
|
config: TSqlConnectionConfig,
|
||||||
callback: (credentials: TSqlConnectionConfig["credentials"]) => Promise<TAppConnectionRaw>,
|
callback: (credentials: TSqlConnectionConfig["credentials"]) => Promise<TAppConnectionRaw>,
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
|
||||||
|
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">
|
||||||
) => {
|
) => {
|
||||||
const { credentials, app } = config;
|
const { credentials, app } = config;
|
||||||
|
|
||||||
const newPassword = alphaNumericNanoId(32);
|
const newPassword = alphaNumericNanoId(32);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await executeWithPotentialGateway(config, gatewayService, (client) => {
|
return await executeWithPotentialGateway(config, gatewayService, gatewayV2Service, (client) => {
|
||||||
return client.transaction(async (tx) => {
|
return client.transaction(async (tx) => {
|
||||||
await tx.raw(
|
await tx.raw(
|
||||||
...SQL_CONNECTION_ALTER_LOGIN_STATEMENT[app]({ username: credentials.username, password: newPassword })
|
...SQL_CONNECTION_ALTER_LOGIN_STATEMENT[app]({ username: credentials.username, password: newPassword })
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import RE2 from "re2";
|
|||||||
import { IdentityAuthMethod, TIdentityKubernetesAuthsUpdate } from "@app/db/schemas";
|
import { IdentityAuthMethod, TIdentityKubernetesAuthsUpdate } from "@app/db/schemas";
|
||||||
import { TGatewayDALFactory } from "@app/ee/services/gateway/gateway-dal";
|
import { TGatewayDALFactory } from "@app/ee/services/gateway/gateway-dal";
|
||||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||||
|
import { TGatewayV2DALFactory } from "@app/ee/services/gateway-v2/gateway-v2-dal";
|
||||||
|
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import {
|
import {
|
||||||
OrgPermissionGatewayActions,
|
OrgPermissionGatewayActions,
|
||||||
@@ -21,6 +23,7 @@ import { getConfig } from "@app/lib/config/env";
|
|||||||
import { crypto } from "@app/lib/crypto";
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { GatewayHttpProxyActions, GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
|
import { GatewayHttpProxyActions, GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
|
||||||
|
import { withGatewayV2Proxy } from "@app/lib/gateway-v2/gateway-v2";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
|
|
||||||
@@ -54,11 +57,15 @@ type TIdentityKubernetesAuthServiceFactoryDep = {
|
|||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
gatewayService: TGatewayServiceFactory;
|
gatewayService: TGatewayServiceFactory;
|
||||||
|
gatewayV2Service: TGatewayV2ServiceFactory;
|
||||||
gatewayDAL: Pick<TGatewayDALFactory, "find">;
|
gatewayDAL: Pick<TGatewayDALFactory, "find">;
|
||||||
|
gatewayV2DAL: Pick<TGatewayV2DALFactory, "find">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TIdentityKubernetesAuthServiceFactory = ReturnType<typeof identityKubernetesAuthServiceFactory>;
|
export type TIdentityKubernetesAuthServiceFactory = ReturnType<typeof identityKubernetesAuthServiceFactory>;
|
||||||
|
|
||||||
|
const GATEWAY_AUTH_DEFAULT_HOST = "https://kubernetes.default.svc.cluster.local";
|
||||||
|
|
||||||
export const identityKubernetesAuthServiceFactory = ({
|
export const identityKubernetesAuthServiceFactory = ({
|
||||||
identityKubernetesAuthDAL,
|
identityKubernetesAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
@@ -66,7 +73,9 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService,
|
||||||
gatewayService,
|
gatewayService,
|
||||||
|
gatewayV2Service,
|
||||||
gatewayDAL,
|
gatewayDAL,
|
||||||
|
gatewayV2DAL,
|
||||||
kmsService
|
kmsService
|
||||||
}: TIdentityKubernetesAuthServiceFactoryDep) => {
|
}: TIdentityKubernetesAuthServiceFactoryDep) => {
|
||||||
const $gatewayProxyWrapper = async <T>(
|
const $gatewayProxyWrapper = async <T>(
|
||||||
@@ -79,6 +88,42 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
},
|
},
|
||||||
gatewayCallback: (host: string, port: number, httpsAgent?: https.Agent) => Promise<T>
|
gatewayCallback: (host: string, port: number, httpsAgent?: https.Agent) => Promise<T>
|
||||||
): Promise<T> => {
|
): Promise<T> => {
|
||||||
|
const gatewayV2ConnectionDetails = await gatewayV2Service.getPlatformConnectionDetailsByGatewayId({
|
||||||
|
gatewayId: inputs.gatewayId,
|
||||||
|
targetHost: inputs.targetHost ?? GATEWAY_AUTH_DEFAULT_HOST,
|
||||||
|
targetPort: inputs.targetPort ?? 443
|
||||||
|
});
|
||||||
|
|
||||||
|
if (gatewayV2ConnectionDetails) {
|
||||||
|
let httpsAgent: https.Agent | undefined;
|
||||||
|
if (!inputs.reviewTokenThroughGateway) {
|
||||||
|
httpsAgent = new https.Agent({
|
||||||
|
ca: inputs.caCert,
|
||||||
|
rejectUnauthorized: Boolean(inputs.caCert)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const callbackResult = await withGatewayV2Proxy(
|
||||||
|
async (port) => {
|
||||||
|
const res = await gatewayCallback(
|
||||||
|
inputs.reviewTokenThroughGateway ? "http://localhost" : "https://localhost",
|
||||||
|
port,
|
||||||
|
httpsAgent
|
||||||
|
);
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
protocol: inputs.reviewTokenThroughGateway ? GatewayProxyProtocol.Http : GatewayProxyProtocol.Tcp,
|
||||||
|
proxyIp: gatewayV2ConnectionDetails.proxyIp,
|
||||||
|
gateway: gatewayV2ConnectionDetails.gateway,
|
||||||
|
proxy: gatewayV2ConnectionDetails.proxy,
|
||||||
|
httpsAgent
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return callbackResult;
|
||||||
|
}
|
||||||
|
|
||||||
const relayDetails = await gatewayService.fnGetGatewayClientTlsByGatewayId(inputs.gatewayId);
|
const relayDetails = await gatewayService.fnGetGatewayClientTlsByGatewayId(inputs.gatewayId);
|
||||||
const [relayHost, relayPort] = relayDetails.relayAddress.split(":");
|
const [relayHost, relayPort] = relayDetails.relayAddress.split(":");
|
||||||
|
|
||||||
@@ -277,7 +322,7 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
let data: TCreateTokenReviewResponse | undefined;
|
let data: TCreateTokenReviewResponse | undefined;
|
||||||
|
|
||||||
if (identityKubernetesAuth.tokenReviewMode === IdentityKubernetesAuthTokenReviewMode.Gateway) {
|
if (identityKubernetesAuth.tokenReviewMode === IdentityKubernetesAuthTokenReviewMode.Gateway) {
|
||||||
if (!identityKubernetesAuth.gatewayId) {
|
if (!identityKubernetesAuth.gatewayId && !identityKubernetesAuth.gatewayV2Id) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Gateway ID is required when token review mode is set to Gateway"
|
message: "Gateway ID is required when token review mode is set to Gateway"
|
||||||
});
|
});
|
||||||
@@ -285,7 +330,7 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
|
|
||||||
data = await $gatewayProxyWrapper(
|
data = await $gatewayProxyWrapper(
|
||||||
{
|
{
|
||||||
gatewayId: identityKubernetesAuth.gatewayId,
|
gatewayId: (identityKubernetesAuth.gatewayV2Id ?? identityKubernetesAuth.gatewayId) as string,
|
||||||
reviewTokenThroughGateway: true
|
reviewTokenThroughGateway: true
|
||||||
},
|
},
|
||||||
tokenReviewCallbackThroughGateway
|
tokenReviewCallbackThroughGateway
|
||||||
@@ -304,17 +349,18 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
|
|
||||||
const [k8sHost, k8sPort] = kubernetesHost.split(":");
|
const [k8sHost, k8sPort] = kubernetesHost.split(":");
|
||||||
|
|
||||||
data = identityKubernetesAuth.gatewayId
|
data =
|
||||||
? await $gatewayProxyWrapper(
|
identityKubernetesAuth.gatewayId || identityKubernetesAuth.gatewayV2Id
|
||||||
{
|
? await $gatewayProxyWrapper(
|
||||||
gatewayId: identityKubernetesAuth.gatewayId,
|
{
|
||||||
targetHost: k8sHost,
|
gatewayId: (identityKubernetesAuth.gatewayV2Id ?? identityKubernetesAuth.gatewayId) as string,
|
||||||
targetPort: k8sPort ? Number(k8sPort) : 443,
|
targetHost: k8sHost,
|
||||||
reviewTokenThroughGateway: false
|
targetPort: k8sPort ? Number(k8sPort) : 443,
|
||||||
},
|
reviewTokenThroughGateway: false
|
||||||
tokenReviewCallbackRaw
|
},
|
||||||
)
|
tokenReviewCallbackRaw
|
||||||
: await tokenReviewCallbackRaw();
|
)
|
||||||
|
: await tokenReviewCallbackRaw();
|
||||||
} else {
|
} else {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: `Invalid token review mode: ${identityKubernetesAuth.tokenReviewMode}`
|
message: `Invalid token review mode: ${identityKubernetesAuth.tokenReviewMode}`
|
||||||
@@ -490,14 +536,20 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
return extractIPDetails(accessTokenTrustedIp.ipAddress);
|
return extractIPDetails(accessTokenTrustedIp.ipAddress);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let isGatewayV1 = true;
|
||||||
if (gatewayId) {
|
if (gatewayId) {
|
||||||
const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: identityMembershipOrg.orgId });
|
const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: identityMembershipOrg.orgId });
|
||||||
if (!gateway) {
|
const [gatewayV2] = await gatewayV2DAL.find({ id: gatewayId, orgId: identityMembershipOrg.orgId });
|
||||||
|
if (!gateway && !gatewayV2) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Gateway with ID ${gatewayId} not found`
|
message: `Gateway with ID ${gatewayId} not found`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!gateway) {
|
||||||
|
isGatewayV1 = false;
|
||||||
|
}
|
||||||
|
|
||||||
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -528,7 +580,8 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
accessTokenMaxTTL,
|
accessTokenMaxTTL,
|
||||||
accessTokenTTL,
|
accessTokenTTL,
|
||||||
accessTokenNumUsesLimit,
|
accessTokenNumUsesLimit,
|
||||||
gatewayId,
|
gatewayId: isGatewayV1 ? gatewayId : null,
|
||||||
|
gatewayV2Id: isGatewayV1 ? null : gatewayId,
|
||||||
accessTokenTrustedIps: JSON.stringify(reformattedAccessTokenTrustedIps),
|
accessTokenTrustedIps: JSON.stringify(reformattedAccessTokenTrustedIps),
|
||||||
encryptedKubernetesTokenReviewerJwt: tokenReviewerJwt
|
encryptedKubernetesTokenReviewerJwt: tokenReviewerJwt
|
||||||
? encryptor({ plainText: Buffer.from(tokenReviewerJwt) }).cipherTextBlob
|
? encryptor({ plainText: Buffer.from(tokenReviewerJwt) }).cipherTextBlob
|
||||||
@@ -608,14 +661,21 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
return extractIPDetails(accessTokenTrustedIp.ipAddress);
|
return extractIPDetails(accessTokenTrustedIp.ipAddress);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let isGatewayV1 = true;
|
||||||
if (gatewayId) {
|
if (gatewayId) {
|
||||||
const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: identityMembershipOrg.orgId });
|
const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: identityMembershipOrg.orgId });
|
||||||
if (!gateway) {
|
const [gatewayV2] = await gatewayV2DAL.find({ id: gatewayId, orgId: identityMembershipOrg.orgId });
|
||||||
|
|
||||||
|
if (!gateway && !gatewayV2) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Gateway with ID ${gatewayId} not found`
|
message: `Gateway with ID ${gatewayId} not found`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!gateway) {
|
||||||
|
isGatewayV1 = false;
|
||||||
|
}
|
||||||
|
|
||||||
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -629,13 +689,18 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const shouldUpdateGatewayId = Boolean(gatewayId);
|
||||||
|
const gatewayIdValue = isGatewayV1 ? gatewayId : null;
|
||||||
|
const gatewayV2IdValue = isGatewayV1 ? null : gatewayId;
|
||||||
|
|
||||||
const updateQuery: TIdentityKubernetesAuthsUpdate = {
|
const updateQuery: TIdentityKubernetesAuthsUpdate = {
|
||||||
kubernetesHost,
|
kubernetesHost,
|
||||||
tokenReviewMode,
|
tokenReviewMode,
|
||||||
allowedNamespaces,
|
allowedNamespaces,
|
||||||
allowedNames,
|
allowedNames,
|
||||||
allowedAudience,
|
allowedAudience,
|
||||||
gatewayId,
|
gatewayId: shouldUpdateGatewayId ? gatewayIdValue : undefined,
|
||||||
|
gatewayV2Id: shouldUpdateGatewayId ? gatewayV2IdValue : undefined,
|
||||||
accessTokenMaxTTL,
|
accessTokenMaxTTL,
|
||||||
accessTokenTTL,
|
accessTokenTTL,
|
||||||
accessTokenNumUsesLimit,
|
accessTokenNumUsesLimit,
|
||||||
@@ -730,7 +795,13 @@ export const identityKubernetesAuthServiceFactory = ({
|
|||||||
}).toString();
|
}).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ...identityKubernetesAuth, caCert, tokenReviewerJwt, orgId: identityMembershipOrg.orgId };
|
return {
|
||||||
|
...identityKubernetesAuth,
|
||||||
|
caCert,
|
||||||
|
tokenReviewerJwt,
|
||||||
|
orgId: identityMembershipOrg.orgId,
|
||||||
|
gatewayId: identityKubernetesAuth.gatewayId ?? identityKubernetesAuth.gatewayV2Id
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const revokeIdentityKubernetesAuth = async ({
|
const revokeIdentityKubernetesAuth = async ({
|
||||||
|
|||||||
Reference in New Issue
Block a user