From c9136a23bf525b113359f8c6f137fecc84146ab1 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Tue, 9 Sep 2025 01:30:45 +0800 Subject: [PATCH] misc: updated proxy terminology to relay --- backend/src/@types/fastify.d.ts | 4 +- backend/src/@types/knex.d.ts | 40 +- ...1627_add-gateway-v2-pki-and-ssh-configs.ts | 106 +- backend/src/db/schemas/gateways-v2.ts | 2 +- backend/src/db/schemas/index.ts | 6 +- .../src/db/schemas/instance-proxy-config.ts | 38 - .../src/db/schemas/instance-relay-config.ts | 38 + backend/src/db/schemas/models.ts | 6 +- backend/src/db/schemas/org-proxy-config.ts | 31 - backend/src/db/schemas/org-relay-config.ts | 31 + .../src/db/schemas/{proxies.ts => relays.ts} | 8 +- backend/src/ee/routes/v1/index.ts | 4 +- .../v1/{proxy-router.ts => relay-router.ts} | 18 +- .../dynamic-secret/providers/kubernetes.ts | 4 +- .../dynamic-secret/providers/sql-database.ts | 4 +- .../services/gateway-v2/gateway-v2-service.ts | 68 +- .../proxy/instance-proxy-config-dal.ts | 11 - .../ee/services/proxy/org-proxy-config-dal.ts | 11 - backend/src/ee/services/proxy/proxy-dal.ts | 11 - backend/src/ee/services/proxy/proxy-fns.ts | 5 - .../src/ee/services/proxy/proxy-service.ts | 1008 ----------------- .../relay/instance-relay-config-dal.ts | 11 + .../ee/services/relay/org-relay-config-dal.ts | 11 + backend/src/ee/services/relay/relay-dal.ts | 11 + backend/src/ee/services/relay/relay-fns.ts | 5 + .../src/ee/services/relay/relay-service.ts | 1008 +++++++++++++++++ backend/src/keystore/keystore.ts | 4 +- backend/src/lib/config/env.ts | 2 +- backend/src/lib/gateway-v2/gateway-v2.ts | 80 +- .../server/plugins/auth/inject-identity.ts | 2 +- backend/src/server/routes/index.ts | 28 +- .../github/github-connection-fns.ts | 4 +- .../shared/sql/sql-connection-fns.ts | 4 +- .../identity-kubernetes-auth-service.ts | 4 +- 34 files changed, 1314 insertions(+), 1314 deletions(-) delete mode 100644 backend/src/db/schemas/instance-proxy-config.ts create mode 100644 backend/src/db/schemas/instance-relay-config.ts delete mode 100644 backend/src/db/schemas/org-proxy-config.ts create mode 100644 backend/src/db/schemas/org-relay-config.ts rename backend/src/db/schemas/{proxies.ts => relays.ts} (63%) rename backend/src/ee/routes/v1/{proxy-router.ts => relay-router.ts} (83%) delete mode 100644 backend/src/ee/services/proxy/instance-proxy-config-dal.ts delete mode 100644 backend/src/ee/services/proxy/org-proxy-config-dal.ts delete mode 100644 backend/src/ee/services/proxy/proxy-dal.ts delete mode 100644 backend/src/ee/services/proxy/proxy-fns.ts delete mode 100644 backend/src/ee/services/proxy/proxy-service.ts create mode 100644 backend/src/ee/services/relay/instance-relay-config-dal.ts create mode 100644 backend/src/ee/services/relay/org-relay-config-dal.ts create mode 100644 backend/src/ee/services/relay/relay-dal.ts create mode 100644 backend/src/ee/services/relay/relay-fns.ts create mode 100644 backend/src/ee/services/relay/relay-service.ts diff --git a/backend/src/@types/fastify.d.ts b/backend/src/@types/fastify.d.ts index 2b997eb46..b0115c926 100644 --- a/backend/src/@types/fastify.d.ts +++ b/backend/src/@types/fastify.d.ts @@ -32,8 +32,8 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio import { TPitServiceFactory } from "@app/ee/services/pit/pit-service"; import { TProjectTemplateServiceFactory } from "@app/ee/services/project-template/project-template-types"; import { TProjectUserAdditionalPrivilegeServiceFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types"; -import { TProxyServiceFactory } from "@app/ee/services/proxy/proxy-service"; import { RateLimitConfiguration, TRateLimitServiceFactory } from "@app/ee/services/rate-limit/rate-limit-types"; +import { TRelayServiceFactory } from "@app/ee/services/relay/relay-service"; import { TSamlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-types"; import { TScimServiceFactory } from "@app/ee/services/scim/scim-types"; import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service"; @@ -305,7 +305,7 @@ declare module "fastify" { bus: TEventBusService; sse: TServerSentEventsService; identityAuthTemplate: TIdentityAuthTemplateServiceFactory; - proxy: TProxyServiceFactory; + relay: TRelayServiceFactory; gatewayV2: TGatewayV2ServiceFactory; }; // this is exclusive use for middlewares in which we need to inject data diff --git a/backend/src/@types/knex.d.ts b/backend/src/@types/knex.d.ts index da75c8d94..5888ac7a8 100644 --- a/backend/src/@types/knex.d.ts +++ b/backend/src/@types/knex.d.ts @@ -182,9 +182,9 @@ import { TIncidentContacts, TIncidentContactsInsert, TIncidentContactsUpdate, - TInstanceProxyConfig, - TInstanceProxyConfigInsert, - TInstanceProxyConfigUpdate, + TInstanceRelayConfig, + TInstanceRelayConfigInsert, + TInstanceRelayConfigUpdate, TIntegrationAuths, TIntegrationAuthsInsert, TIntegrationAuthsUpdate, @@ -242,9 +242,9 @@ import { TOrgMemberships, TOrgMembershipsInsert, TOrgMembershipsUpdate, - TOrgProxyConfig, - TOrgProxyConfigInsert, - TOrgProxyConfigUpdate, + TOrgRelayConfig, + TOrgRelayConfigInsert, + TOrgRelayConfigUpdate, TOrgRoles, TOrgRolesInsert, TOrgRolesUpdate, @@ -299,12 +299,12 @@ import { TProjectUserMembershipRoles, TProjectUserMembershipRolesInsert, TProjectUserMembershipRolesUpdate, - TProxies, - TProxiesInsert, - TProxiesUpdate, TRateLimit, TRateLimitInsert, TRateLimitUpdate, + TRelays, + TRelaysInsert, + TRelaysUpdate, TResourceMetadata, TResourceMetadataInsert, TResourceMetadataUpdate, @@ -1269,22 +1269,22 @@ declare module "knex/types/tables" { TRemindersRecipientsInsert, TRemindersRecipientsUpdate >; - [TableName.InstanceProxyConfig]: KnexOriginal.CompositeTableType< - TInstanceProxyConfig, - TInstanceProxyConfigInsert, - TInstanceProxyConfigUpdate - >; - [TableName.OrgProxyConfig]: KnexOriginal.CompositeTableType< - TOrgProxyConfig, - TOrgProxyConfigInsert, - TOrgProxyConfigUpdate - >; [TableName.OrgGatewayConfigV2]: KnexOriginal.CompositeTableType< TOrgGatewayConfigV2, TOrgGatewayConfigV2Insert, TOrgGatewayConfigV2Update >; - [TableName.Proxy]: KnexOriginal.CompositeTableType; [TableName.GatewayV2]: KnexOriginal.CompositeTableType; + [TableName.InstanceRelayConfig]: KnexOriginal.CompositeTableType< + TInstanceRelayConfig, + TInstanceRelayConfigInsert, + TInstanceRelayConfigUpdate + >; + [TableName.OrgRelayConfig]: KnexOriginal.CompositeTableType< + TOrgRelayConfig, + TOrgRelayConfigInsert, + TOrgRelayConfigUpdate + >; + [TableName.Relay]: KnexOriginal.CompositeTableType; } } diff --git a/backend/src/db/migrations/20250825131627_add-gateway-v2-pki-and-ssh-configs.ts b/backend/src/db/migrations/20250825131627_add-gateway-v2-pki-and-ssh-configs.ts index 179d7aa2d..3c825b08d 100644 --- a/backend/src/db/migrations/20250825131627_add-gateway-v2-pki-and-ssh-configs.ts +++ b/backend/src/db/migrations/20250825131627_add-gateway-v2-pki-and-ssh-configs.ts @@ -4,68 +4,68 @@ import { TableName } from "../schemas"; import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils"; export async function up(knex: Knex): Promise { - if (!(await knex.schema.hasTable(TableName.InstanceProxyConfig))) { - await knex.schema.createTable(TableName.InstanceProxyConfig, (t) => { + if (!(await knex.schema.hasTable(TableName.InstanceRelayConfig))) { + await knex.schema.createTable(TableName.InstanceRelayConfig, (t) => { t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); t.timestamps(true, true, true); - // Root CA for proxy PKI - t.binary("encryptedRootProxyPkiCaPrivateKey").notNullable(); - t.binary("encryptedRootProxyPkiCaCertificate").notNullable(); + // Root CA for relay PKI + t.binary("encryptedRootRelayPkiCaPrivateKey").notNullable(); + t.binary("encryptedRootRelayPkiCaCertificate").notNullable(); - // Instance CA for proxy PKI - t.binary("encryptedInstanceProxyPkiCaPrivateKey").notNullable(); - t.binary("encryptedInstanceProxyPkiCaCertificate").notNullable(); - t.binary("encryptedInstanceProxyPkiCaCertificateChain").notNullable(); + // Instance CA for relay PKI + t.binary("encryptedInstanceRelayPkiCaPrivateKey").notNullable(); + t.binary("encryptedInstanceRelayPkiCaCertificate").notNullable(); + t.binary("encryptedInstanceRelayPkiCaCertificateChain").notNullable(); - // Instance client/server intermediates for proxy PKI - t.binary("encryptedInstanceProxyPkiClientCaPrivateKey").notNullable(); - t.binary("encryptedInstanceProxyPkiClientCaCertificate").notNullable(); - t.binary("encryptedInstanceProxyPkiClientCaCertificateChain").notNullable(); - t.binary("encryptedInstanceProxyPkiServerCaPrivateKey").notNullable(); - t.binary("encryptedInstanceProxyPkiServerCaCertificate").notNullable(); - t.binary("encryptedInstanceProxyPkiServerCaCertificateChain").notNullable(); + // Instance client/server intermediates for relay PKI + t.binary("encryptedInstanceRelayPkiClientCaPrivateKey").notNullable(); + t.binary("encryptedInstanceRelayPkiClientCaCertificate").notNullable(); + t.binary("encryptedInstanceRelayPkiClientCaCertificateChain").notNullable(); + t.binary("encryptedInstanceRelayPkiServerCaPrivateKey").notNullable(); + t.binary("encryptedInstanceRelayPkiServerCaCertificate").notNullable(); + t.binary("encryptedInstanceRelayPkiServerCaCertificateChain").notNullable(); - // Org Parent CAs for proxy - t.binary("encryptedOrgProxyPkiCaPrivateKey").notNullable(); - t.binary("encryptedOrgProxyPkiCaCertificate").notNullable(); - t.binary("encryptedOrgProxyPkiCaCertificateChain").notNullable(); + // Org Parent CAs for relay + t.binary("encryptedOrgRelayPkiCaPrivateKey").notNullable(); + t.binary("encryptedOrgRelayPkiCaCertificate").notNullable(); + t.binary("encryptedOrgRelayPkiCaCertificateChain").notNullable(); - // Instance SSH CAs for proxy - t.binary("encryptedInstanceProxySshClientCaPrivateKey").notNullable(); - t.binary("encryptedInstanceProxySshClientCaPublicKey").notNullable(); - t.binary("encryptedInstanceProxySshServerCaPrivateKey").notNullable(); - t.binary("encryptedInstanceProxySshServerCaPublicKey").notNullable(); + // Instance SSH CAs for relay + t.binary("encryptedInstanceRelaySshClientCaPrivateKey").notNullable(); + t.binary("encryptedInstanceRelaySshClientCaPublicKey").notNullable(); + t.binary("encryptedInstanceRelaySshServerCaPrivateKey").notNullable(); + t.binary("encryptedInstanceRelaySshServerCaPublicKey").notNullable(); }); - await createOnUpdateTrigger(knex, TableName.InstanceProxyConfig); + await createOnUpdateTrigger(knex, TableName.InstanceRelayConfig); } - // Org-level proxy configuration (one-to-one with organization) - if (!(await knex.schema.hasTable(TableName.OrgProxyConfig))) { - await knex.schema.createTable(TableName.OrgProxyConfig, (t) => { + // Org-level relay configuration (one-to-one with organization) + if (!(await knex.schema.hasTable(TableName.OrgRelayConfig))) { + await knex.schema.createTable(TableName.OrgRelayConfig, (t) => { t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); t.timestamps(true, true, true); t.uuid("orgId").notNullable().unique(); t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE"); - // Org-scoped proxy PKI (client + server) - t.binary("encryptedProxyPkiClientCaPrivateKey").notNullable(); - t.binary("encryptedProxyPkiClientCaCertificate").notNullable(); - t.binary("encryptedProxyPkiClientCaCertificateChain").notNullable(); - t.binary("encryptedProxyPkiServerCaPrivateKey").notNullable(); - t.binary("encryptedProxyPkiServerCaCertificate").notNullable(); - t.binary("encryptedProxyPkiServerCaCertificateChain").notNullable(); + // Org-scoped relay PKI (client + server) + t.binary("encryptedRelayPkiClientCaPrivateKey").notNullable(); + t.binary("encryptedRelayPkiClientCaCertificate").notNullable(); + t.binary("encryptedRelayPkiClientCaCertificateChain").notNullable(); + t.binary("encryptedRelayPkiServerCaPrivateKey").notNullable(); + t.binary("encryptedRelayPkiServerCaCertificate").notNullable(); + t.binary("encryptedRelayPkiServerCaCertificateChain").notNullable(); - // Org-scoped proxy SSH (client + server) - t.binary("encryptedProxySshClientCaPrivateKey").notNullable(); - t.binary("encryptedProxySshClientCaPublicKey").notNullable(); - t.binary("encryptedProxySshServerCaPrivateKey").notNullable(); - t.binary("encryptedProxySshServerCaPublicKey").notNullable(); + // Org-scoped relay SSH (client + server) + t.binary("encryptedRelaySshClientCaPrivateKey").notNullable(); + t.binary("encryptedRelaySshClientCaPublicKey").notNullable(); + t.binary("encryptedRelaySshServerCaPrivateKey").notNullable(); + t.binary("encryptedRelaySshServerCaPublicKey").notNullable(); }); - await createOnUpdateTrigger(knex, TableName.OrgProxyConfig); + await createOnUpdateTrigger(knex, TableName.OrgRelayConfig); } if (!(await knex.schema.hasTable(TableName.OrgGatewayConfigV2))) { @@ -87,8 +87,8 @@ export async function up(knex: Knex): Promise { await createOnUpdateTrigger(knex, TableName.OrgGatewayConfigV2); } - if (!(await knex.schema.hasTable(TableName.Proxy))) { - await knex.schema.createTable(TableName.Proxy, (t) => { + if (!(await knex.schema.hasTable(TableName.Relay))) { + await knex.schema.createTable(TableName.Relay, (t) => { t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); t.timestamps(true, true, true); @@ -102,7 +102,7 @@ export async function up(knex: Knex): Promise { t.string("ip").notNullable(); }); - await createOnUpdateTrigger(knex, TableName.Proxy); + await createOnUpdateTrigger(knex, TableName.Relay); } if (!(await knex.schema.hasTable(TableName.GatewayV2))) { @@ -116,8 +116,8 @@ export async function up(knex: Knex): Promise { t.uuid("identityId").notNullable().unique(); t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE"); - t.uuid("proxyId"); - t.foreign("proxyId").references("id").inTable(TableName.Proxy).onDelete("SET NULL"); + t.uuid("relayId"); + t.foreign("relayId").references("id").inTable(TableName.Relay).onDelete("SET NULL"); t.string("name").notNullable().unique(); @@ -129,11 +129,11 @@ export async function up(knex: Knex): Promise { } export async function down(knex: Knex): Promise { - await dropOnUpdateTrigger(knex, TableName.OrgProxyConfig); - await knex.schema.dropTableIfExists(TableName.OrgProxyConfig); + await dropOnUpdateTrigger(knex, TableName.OrgRelayConfig); + await knex.schema.dropTableIfExists(TableName.OrgRelayConfig); - await dropOnUpdateTrigger(knex, TableName.InstanceProxyConfig); - await knex.schema.dropTableIfExists(TableName.InstanceProxyConfig); + await dropOnUpdateTrigger(knex, TableName.InstanceRelayConfig); + await knex.schema.dropTableIfExists(TableName.InstanceRelayConfig); await dropOnUpdateTrigger(knex, TableName.OrgGatewayConfigV2); await knex.schema.dropTableIfExists(TableName.OrgGatewayConfigV2); @@ -141,6 +141,6 @@ export async function down(knex: Knex): Promise { await dropOnUpdateTrigger(knex, TableName.GatewayV2); await knex.schema.dropTableIfExists(TableName.GatewayV2); - await dropOnUpdateTrigger(knex, TableName.Proxy); - await knex.schema.dropTableIfExists(TableName.Proxy); + await dropOnUpdateTrigger(knex, TableName.Relay); + await knex.schema.dropTableIfExists(TableName.Relay); } diff --git a/backend/src/db/schemas/gateways-v2.ts b/backend/src/db/schemas/gateways-v2.ts index c3226aa61..6aff8a168 100644 --- a/backend/src/db/schemas/gateways-v2.ts +++ b/backend/src/db/schemas/gateways-v2.ts @@ -13,7 +13,7 @@ export const GatewaysV2Schema = z.object({ updatedAt: z.date(), orgId: z.string().uuid(), identityId: z.string().uuid(), - proxyId: z.string().uuid().nullable().optional(), + relayId: z.string().uuid().nullable().optional(), name: z.string(), heartbeat: z.date().nullable().optional() }); diff --git a/backend/src/db/schemas/index.ts b/backend/src/db/schemas/index.ts index 5311265b5..e1e0fe7d4 100644 --- a/backend/src/db/schemas/index.ts +++ b/backend/src/db/schemas/index.ts @@ -58,7 +58,7 @@ export * from "./identity-token-auths"; export * from "./identity-ua-client-secrets"; export * from "./identity-universal-auths"; export * from "./incident-contacts"; -export * from "./instance-proxy-config"; +export * from "./instance-relay-config"; export * from "./integration-auths"; export * from "./integrations"; export * from "./internal-certificate-authorities"; @@ -79,7 +79,7 @@ export * from "./org-bots"; export * from "./org-gateway-config"; export * from "./org-gateway-config-v2"; export * from "./org-memberships"; -export * from "./org-proxy-config"; +export * from "./org-relay-config"; export * from "./org-roles"; export * from "./organizations"; export * from "./pki-alerts"; @@ -99,8 +99,8 @@ export * from "./project-templates"; export * from "./project-user-additional-privilege"; export * from "./project-user-membership-roles"; export * from "./projects"; -export * from "./proxies"; export * from "./rate-limit"; +export * from "./relays"; export * from "./resource-metadata"; export * from "./saml-configs"; export * from "./scim-tokens"; diff --git a/backend/src/db/schemas/instance-proxy-config.ts b/backend/src/db/schemas/instance-proxy-config.ts deleted file mode 100644 index 369ae381a..000000000 --- a/backend/src/db/schemas/instance-proxy-config.ts +++ /dev/null @@ -1,38 +0,0 @@ -// Code generated by automation script, DO NOT EDIT. -// Automated by pulling database and generating zod schema -// To update. Just run npm run generate:schema -// Written by akhilmhdh. - -import { z } from "zod"; - -import { zodBuffer } from "@app/lib/zod"; - -import { TImmutableDBKeys } from "./models"; - -export const InstanceProxyConfigSchema = z.object({ - id: z.string().uuid(), - createdAt: z.date(), - updatedAt: z.date(), - encryptedRootProxyPkiCaPrivateKey: zodBuffer, - encryptedRootProxyPkiCaCertificate: zodBuffer, - encryptedInstanceProxyPkiCaPrivateKey: zodBuffer, - encryptedInstanceProxyPkiCaCertificate: zodBuffer, - encryptedInstanceProxyPkiCaCertificateChain: zodBuffer, - encryptedInstanceProxyPkiClientCaPrivateKey: zodBuffer, - encryptedInstanceProxyPkiClientCaCertificate: zodBuffer, - encryptedInstanceProxyPkiClientCaCertificateChain: zodBuffer, - encryptedInstanceProxyPkiServerCaPrivateKey: zodBuffer, - encryptedInstanceProxyPkiServerCaCertificate: zodBuffer, - encryptedInstanceProxyPkiServerCaCertificateChain: zodBuffer, - encryptedOrgProxyPkiCaPrivateKey: zodBuffer, - encryptedOrgProxyPkiCaCertificate: zodBuffer, - encryptedOrgProxyPkiCaCertificateChain: zodBuffer, - encryptedInstanceProxySshClientCaPrivateKey: zodBuffer, - encryptedInstanceProxySshClientCaPublicKey: zodBuffer, - encryptedInstanceProxySshServerCaPrivateKey: zodBuffer, - encryptedInstanceProxySshServerCaPublicKey: zodBuffer -}); - -export type TInstanceProxyConfig = z.infer; -export type TInstanceProxyConfigInsert = Omit, TImmutableDBKeys>; -export type TInstanceProxyConfigUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/instance-relay-config.ts b/backend/src/db/schemas/instance-relay-config.ts new file mode 100644 index 000000000..8b18ef0f5 --- /dev/null +++ b/backend/src/db/schemas/instance-relay-config.ts @@ -0,0 +1,38 @@ +// Code generated by automation script, DO NOT EDIT. +// Automated by pulling database and generating zod schema +// To update. Just run npm run generate:schema +// Written by akhilmhdh. + +import { z } from "zod"; + +import { zodBuffer } from "@app/lib/zod"; + +import { TImmutableDBKeys } from "./models"; + +export const InstanceRelayConfigSchema = z.object({ + id: z.string().uuid(), + createdAt: z.date(), + updatedAt: z.date(), + encryptedRootRelayPkiCaPrivateKey: zodBuffer, + encryptedRootRelayPkiCaCertificate: zodBuffer, + encryptedInstanceRelayPkiCaPrivateKey: zodBuffer, + encryptedInstanceRelayPkiCaCertificate: zodBuffer, + encryptedInstanceRelayPkiCaCertificateChain: zodBuffer, + encryptedInstanceRelayPkiClientCaPrivateKey: zodBuffer, + encryptedInstanceRelayPkiClientCaCertificate: zodBuffer, + encryptedInstanceRelayPkiClientCaCertificateChain: zodBuffer, + encryptedInstanceRelayPkiServerCaPrivateKey: zodBuffer, + encryptedInstanceRelayPkiServerCaCertificate: zodBuffer, + encryptedInstanceRelayPkiServerCaCertificateChain: zodBuffer, + encryptedOrgRelayPkiCaPrivateKey: zodBuffer, + encryptedOrgRelayPkiCaCertificate: zodBuffer, + encryptedOrgRelayPkiCaCertificateChain: zodBuffer, + encryptedInstanceRelaySshClientCaPrivateKey: zodBuffer, + encryptedInstanceRelaySshClientCaPublicKey: zodBuffer, + encryptedInstanceRelaySshServerCaPrivateKey: zodBuffer, + encryptedInstanceRelaySshServerCaPublicKey: zodBuffer +}); + +export type TInstanceRelayConfig = z.infer; +export type TInstanceRelayConfigInsert = Omit, TImmutableDBKeys>; +export type TInstanceRelayConfigUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/models.ts b/backend/src/db/schemas/models.ts index 87ea9f8e5..23da64e62 100644 --- a/backend/src/db/schemas/models.ts +++ b/backend/src/db/schemas/models.ts @@ -181,10 +181,10 @@ export enum TableName { ReminderRecipient = "reminders_recipients", // gateway v2 - InstanceProxyConfig = "instance_proxy_config", - OrgProxyConfig = "org_proxy_config", + InstanceRelayConfig = "instance_relay_config", + OrgRelayConfig = "org_relay_config", OrgGatewayConfigV2 = "org_gateway_config_v2", - Proxy = "proxies", + Relay = "relays", GatewayV2 = "gateways_v2" } diff --git a/backend/src/db/schemas/org-proxy-config.ts b/backend/src/db/schemas/org-proxy-config.ts deleted file mode 100644 index 8b854ffc2..000000000 --- a/backend/src/db/schemas/org-proxy-config.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Code generated by automation script, DO NOT EDIT. -// Automated by pulling database and generating zod schema -// To update. Just run npm run generate:schema -// Written by akhilmhdh. - -import { z } from "zod"; - -import { zodBuffer } from "@app/lib/zod"; - -import { TImmutableDBKeys } from "./models"; - -export const OrgProxyConfigSchema = z.object({ - id: z.string().uuid(), - createdAt: z.date(), - updatedAt: z.date(), - orgId: z.string().uuid(), - encryptedProxyPkiClientCaPrivateKey: zodBuffer, - encryptedProxyPkiClientCaCertificate: zodBuffer, - encryptedProxyPkiClientCaCertificateChain: zodBuffer, - encryptedProxyPkiServerCaPrivateKey: zodBuffer, - encryptedProxyPkiServerCaCertificate: zodBuffer, - encryptedProxyPkiServerCaCertificateChain: zodBuffer, - encryptedProxySshClientCaPrivateKey: zodBuffer, - encryptedProxySshClientCaPublicKey: zodBuffer, - encryptedProxySshServerCaPrivateKey: zodBuffer, - encryptedProxySshServerCaPublicKey: zodBuffer -}); - -export type TOrgProxyConfig = z.infer; -export type TOrgProxyConfigInsert = Omit, TImmutableDBKeys>; -export type TOrgProxyConfigUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/org-relay-config.ts b/backend/src/db/schemas/org-relay-config.ts new file mode 100644 index 000000000..1752da76a --- /dev/null +++ b/backend/src/db/schemas/org-relay-config.ts @@ -0,0 +1,31 @@ +// Code generated by automation script, DO NOT EDIT. +// Automated by pulling database and generating zod schema +// To update. Just run npm run generate:schema +// Written by akhilmhdh. + +import { z } from "zod"; + +import { zodBuffer } from "@app/lib/zod"; + +import { TImmutableDBKeys } from "./models"; + +export const OrgRelayConfigSchema = z.object({ + id: z.string().uuid(), + createdAt: z.date(), + updatedAt: z.date(), + orgId: z.string().uuid(), + encryptedRelayPkiClientCaPrivateKey: zodBuffer, + encryptedRelayPkiClientCaCertificate: zodBuffer, + encryptedRelayPkiClientCaCertificateChain: zodBuffer, + encryptedRelayPkiServerCaPrivateKey: zodBuffer, + encryptedRelayPkiServerCaCertificate: zodBuffer, + encryptedRelayPkiServerCaCertificateChain: zodBuffer, + encryptedRelaySshClientCaPrivateKey: zodBuffer, + encryptedRelaySshClientCaPublicKey: zodBuffer, + encryptedRelaySshServerCaPrivateKey: zodBuffer, + encryptedRelaySshServerCaPublicKey: zodBuffer +}); + +export type TOrgRelayConfig = z.infer; +export type TOrgRelayConfigInsert = Omit, TImmutableDBKeys>; +export type TOrgRelayConfigUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/proxies.ts b/backend/src/db/schemas/relays.ts similarity index 63% rename from backend/src/db/schemas/proxies.ts rename to backend/src/db/schemas/relays.ts index 508c4d25e..d29f2438f 100644 --- a/backend/src/db/schemas/proxies.ts +++ b/backend/src/db/schemas/relays.ts @@ -7,7 +7,7 @@ import { z } from "zod"; import { TImmutableDBKeys } from "./models"; -export const ProxiesSchema = z.object({ +export const RelaysSchema = z.object({ id: z.string().uuid(), createdAt: z.date(), updatedAt: z.date(), @@ -17,6 +17,6 @@ export const ProxiesSchema = z.object({ ip: z.string() }); -export type TProxies = z.infer; -export type TProxiesInsert = Omit, TImmutableDBKeys>; -export type TProxiesUpdate = Partial, TImmutableDBKeys>>; +export type TRelays = z.infer; +export type TRelaysInsert = Omit, TImmutableDBKeys>; +export type TRelaysUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/ee/routes/v1/index.ts b/backend/src/ee/routes/v1/index.ts index d1232e5e8..cdaa2f7f4 100644 --- a/backend/src/ee/routes/v1/index.ts +++ b/backend/src/ee/routes/v1/index.ts @@ -23,8 +23,8 @@ import { registerOrgRoleRouter } from "./org-role-router"; import { registerPITRouter } from "./pit-router"; import { registerProjectRoleRouter } from "./project-role-router"; import { registerProjectRouter } from "./project-router"; -import { registerProxyRouter } from "./proxy-router"; import { registerRateLimitRouter } from "./rate-limit-router"; +import { registerRelayRouter } from "./relay-router"; import { registerSamlRouter } from "./saml-router"; import { registerScimRouter } from "./scim-router"; import { registerSecretApprovalPolicyRouter } from "./secret-approval-policy-router"; @@ -80,7 +80,7 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => { ); await server.register(registerGatewayRouter, { prefix: "/gateways" }); - await server.register(registerProxyRouter, { prefix: "/proxies" }); + await server.register(registerRelayRouter, { prefix: "/relays" }); await server.register(registerGithubOrgSyncRouter, { prefix: "/github-org-sync-config" }); await server.register( diff --git a/backend/src/ee/routes/v1/proxy-router.ts b/backend/src/ee/routes/v1/relay-router.ts similarity index 83% rename from backend/src/ee/routes/v1/proxy-router.ts rename to backend/src/ee/routes/v1/relay-router.ts index 3fe225ab2..a04791797 100644 --- a/backend/src/ee/routes/v1/proxy-router.ts +++ b/backend/src/ee/routes/v1/relay-router.ts @@ -7,12 +7,12 @@ import { writeLimit } from "@app/server/config/rateLimiter"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; -export const registerProxyRouter = async (server: FastifyZodProvider) => { +export const registerRelayRouter = async (server: FastifyZodProvider) => { const appCfg = getConfig(); server.route({ method: "POST", - url: "/register-instance-proxy", + url: "/register-instance-relay", config: { rateLimit: writeLimit }, @@ -39,8 +39,8 @@ export const registerProxyRouter = async (server: FastifyZodProvider) => { onRequest: (req, _, next) => { const authHeader = req.headers.authorization; - if (appCfg.PROXY_AUTH_SECRET && authHeader) { - const expectedHeader = `Bearer ${appCfg.PROXY_AUTH_SECRET}`; + if (appCfg.RELAY_AUTH_SECRET && authHeader) { + const expectedHeader = `Bearer ${appCfg.RELAY_AUTH_SECRET}`; if ( authHeader.length === expectedHeader.length && crypto.nativeCrypto.timingSafeEqual(Buffer.from(authHeader), Buffer.from(expectedHeader)) @@ -50,11 +50,11 @@ export const registerProxyRouter = async (server: FastifyZodProvider) => { } throw new UnauthorizedError({ - message: "Invalid proxy auth secret" + message: "Invalid relay auth secret" }); }, handler: async (req) => { - return server.services.proxy.registerProxy({ + return server.services.relay.registerRelay({ ...req.body }); } @@ -62,7 +62,7 @@ export const registerProxyRouter = async (server: FastifyZodProvider) => { server.route({ method: "POST", - url: "/register-org-proxy", + url: "/register-org-relay", config: { rateLimit: writeLimit }, @@ -89,10 +89,10 @@ export const registerProxyRouter = async (server: FastifyZodProvider) => { onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]), handler: async (req) => { throw new BadRequestError({ - message: "Org proxy registration is not yet supported" + message: "Org relay registration is not yet supported" }); - return server.services.proxy.registerProxy({ + return server.services.relay.registerRelay({ ...req.body, identityId: req.permission.id, orgId: req.permission.orgId diff --git a/backend/src/ee/services/dynamic-secret/providers/kubernetes.ts b/backend/src/ee/services/dynamic-secret/providers/kubernetes.ts index e60b11576..a7b69d882 100644 --- a/backend/src/ee/services/dynamic-secret/providers/kubernetes.ts +++ b/backend/src/ee/services/dynamic-secret/providers/kubernetes.ts @@ -79,9 +79,9 @@ export const KubernetesProvider = ({ ); }, { - proxyIp: gatewayV2ConnectionDetails.proxyIp, + relayIp: gatewayV2ConnectionDetails.relayIp, gateway: gatewayV2ConnectionDetails.gateway, - proxy: gatewayV2ConnectionDetails.proxy, + relay: gatewayV2ConnectionDetails.relay, protocol: inputs.reviewTokenThroughGateway ? GatewayProxyProtocol.Http : GatewayProxyProtocol.Tcp, httpsAgent: inputs.httpsAgent } diff --git a/backend/src/ee/services/dynamic-secret/providers/sql-database.ts b/backend/src/ee/services/dynamic-secret/providers/sql-database.ts index 331a0cb25..59355ab7c 100644 --- a/backend/src/ee/services/dynamic-secret/providers/sql-database.ts +++ b/backend/src/ee/services/dynamic-secret/providers/sql-database.ts @@ -201,9 +201,9 @@ export const SqlDatabaseProvider = ({ await gatewayCallback("localhost", port); }, { - proxyIp: gatewayV2ConnectionDetails.proxyIp, + relayIp: gatewayV2ConnectionDetails.relayIp, gateway: gatewayV2ConnectionDetails.gateway, - proxy: gatewayV2ConnectionDetails.proxy, + relay: gatewayV2ConnectionDetails.relay, protocol: GatewayProxyProtocol.Tcp } ); diff --git a/backend/src/ee/services/gateway-v2/gateway-v2-service.ts b/backend/src/ee/services/gateway-v2/gateway-v2-service.ts index 8fb53fa20..6ec379854 100644 --- a/backend/src/ee/services/gateway-v2/gateway-v2-service.ts +++ b/backend/src/ee/services/gateway-v2/gateway-v2-service.ts @@ -3,7 +3,7 @@ import net from "node:net"; import { ForbiddenError } from "@casl/ability"; import * as x509 from "@peculiar/x509"; -import { TProxies } from "@app/db/schemas"; +import { TRelays } from "@app/db/schemas"; import { PgSqlLock } from "@app/keystore/keystore"; import { crypto } from "@app/lib/crypto"; import { DatabaseErrorCode } from "@app/lib/error-codes"; @@ -24,9 +24,9 @@ import { KmsDataKey } from "@app/services/kms/kms-types"; import { TLicenseServiceFactory } from "../license/license-service"; import { OrgPermissionGatewayActions, OrgPermissionSubjects } from "../permission/org-permission"; import { TPermissionServiceFactory } from "../permission/permission-service-types"; -import { TProxyDALFactory } from "../proxy/proxy-dal"; -import { isInstanceProxy } from "../proxy/proxy-fns"; -import { TProxyServiceFactory } from "../proxy/proxy-service"; +import { TRelayDALFactory } from "../relay/relay-dal"; +import { isInstanceRelay } from "../relay/relay-fns"; +import { TRelayServiceFactory } from "../relay/relay-service"; import { GATEWAY_ACTOR_OID, GATEWAY_ROUTING_INFO_OID } from "./gateway-v2-constants"; import { TGatewayV2DALFactory } from "./gateway-v2-dal"; import { TOrgGatewayConfigV2DALFactory } from "./org-gateway-config-v2-dal"; @@ -35,9 +35,9 @@ type TGatewayV2ServiceFactoryDep = { orgGatewayConfigV2DAL: Pick; licenseService: Pick; kmsService: TKmsServiceFactory; - proxyService: TProxyServiceFactory; + relayService: TRelayServiceFactory; gatewayV2DAL: TGatewayV2DALFactory; - proxyDAL: TProxyDALFactory; + relayDAL: TRelayDALFactory; permissionService: TPermissionServiceFactory; }; @@ -47,9 +47,9 @@ export const gatewayV2ServiceFactory = ({ orgGatewayConfigV2DAL, licenseService, kmsService, - proxyService, + relayService, gatewayV2DAL, - proxyDAL, + relayDAL, permissionService }: TGatewayV2ServiceFactoryDep) => { const $validateIdentityAccessToGateway = async (orgId: string, actorId: string, actorAuthMethod: ActorAuthMethod) => { @@ -285,9 +285,9 @@ export const gatewayV2ServiceFactory = ({ throw new NotFoundError({ message: `Gateway Config for org ${gateway.orgId} not found.` }); } - if (!gateway.proxyId) { + if (!gateway.relayId) { throw new BadRequestError({ - message: "Gateway is not associated with a proxy" + message: "Gateway is not associated with a relay" }); } @@ -392,23 +392,23 @@ export const gatewayV2ServiceFactory = ({ const gatewayClientCertPrivateKey = crypto.nativeCrypto.KeyObject.from(clientKeys.privateKey); - const proxyCredentials = await proxyService.getCredentialsForClient({ - proxyId: gateway.proxyId, + const relayCredentials = await relayService.getCredentialsForClient({ + relayId: gateway.relayId, orgId: gateway.orgId, gatewayId }); return { - proxyIp: proxyCredentials.proxyIp, + relayIp: relayCredentials.relayIp, gateway: { clientCertificate: clientCert.toString("pem"), clientPrivateKey: gatewayClientCertPrivateKey.export({ format: "pem", type: "pkcs8" }).toString(), serverCertificateChain: constructPemChainFromCerts([gatewayServerCaCert, rootGatewayCaCert]) }, - proxy: { - clientCertificate: proxyCredentials.clientCertificate, - clientPrivateKey: proxyCredentials.clientPrivateKey, - serverCertificateChain: proxyCredentials.serverCertificateChain + relay: { + clientCertificate: relayCredentials.clientCertificate, + clientPrivateKey: relayCredentials.clientPrivateKey, + serverCertificateChain: relayCredentials.serverCertificateChain } }; }; @@ -417,27 +417,27 @@ export const gatewayV2ServiceFactory = ({ orgId, actorId, actorAuthMethod, - proxyName, + relayName, name }: { orgId: string; actorId: string; actorAuthMethod: ActorAuthMethod; - proxyName: string; + relayName: string; name: string; }) => { await $validateIdentityAccessToGateway(orgId, actorId, actorAuthMethod); const orgCAs = await $getOrgCAs(orgId); - let proxy: TProxies; - if (isInstanceProxy(proxyName)) { - proxy = await proxyDAL.findOne({ name: proxyName }); + let relay: TRelays; + if (isInstanceRelay(relayName)) { + relay = await relayDAL.findOne({ name: relayName }); } else { - proxy = await proxyDAL.findOne({ orgId, name: proxyName }); + relay = await relayDAL.findOne({ orgId, name: relayName }); } - if (!proxy) { - throw new NotFoundError({ message: `Proxy ${proxyName} not found` }); + if (!relay) { + throw new NotFoundError({ message: `Relay ${relayName} not found` }); } try { @@ -447,7 +447,7 @@ export const gatewayV2ServiceFactory = ({ orgId, name, identityId: actorId, - proxyId: proxy.id + relayId: relay.id } ], ["identityId"] @@ -507,24 +507,24 @@ export const gatewayV2ServiceFactory = ({ extensions: gatewayServerCertExtensions }); - const proxyCredentials = await proxyService.getCredentialsForGateway({ - proxyName, + const relayCredentials = await relayService.getCredentialsForGateway({ + relayName, orgId, gatewayId: gateway.id }); return { gatewayId: gateway.id, - proxyIp: proxyCredentials.proxyIp, + relayIp: relayCredentials.relayIp, pki: { serverCertificate: gatewayServerCertificate.toString("pem"), serverPrivateKey: gatewayServerCertPrivateKey.export({ format: "pem", type: "pkcs8" }).toString(), clientCertificateChain: constructPemChainFromCerts([gatewayClientCaCert, rootGatewayCaCert]) }, ssh: { - clientCertificate: proxyCredentials.clientSshCert, - clientPrivateKey: proxyCredentials.clientSshPrivateKey, - serverCAPublicKey: proxyCredentials.serverCAPublicKey + clientCertificate: relayCredentials.clientSshCert, + clientPrivateKey: relayCredentials.clientSshPrivateKey, + serverCAPublicKey: relayCredentials.serverCAPublicKey } }; } catch (err) { @@ -613,9 +613,9 @@ export const gatewayV2ServiceFactory = ({ }, { protocol: GatewayProxyProtocol.Ping, - proxyIp: gatewayV2ConnectionDetails.proxyIp, + relayIp: gatewayV2ConnectionDetails.relayIp, gateway: gatewayV2ConnectionDetails.gateway, - proxy: gatewayV2ConnectionDetails.proxy + relay: gatewayV2ConnectionDetails.relay } ); diff --git a/backend/src/ee/services/proxy/instance-proxy-config-dal.ts b/backend/src/ee/services/proxy/instance-proxy-config-dal.ts deleted file mode 100644 index 4a128daf3..000000000 --- a/backend/src/ee/services/proxy/instance-proxy-config-dal.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { TDbClient } from "@app/db"; -import { TableName } from "@app/db/schemas"; -import { ormify } from "@app/lib/knex"; - -export type TInstanceProxyConfigDALFactory = ReturnType; - -export const instanceProxyConfigDalFactory = (db: TDbClient) => { - const orm = ormify(db, TableName.InstanceProxyConfig); - - return orm; -}; diff --git a/backend/src/ee/services/proxy/org-proxy-config-dal.ts b/backend/src/ee/services/proxy/org-proxy-config-dal.ts deleted file mode 100644 index f15dd823b..000000000 --- a/backend/src/ee/services/proxy/org-proxy-config-dal.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { TDbClient } from "@app/db"; -import { TableName } from "@app/db/schemas"; -import { ormify } from "@app/lib/knex"; - -export type TOrgProxyConfigDALFactory = ReturnType; - -export const orgProxyConfigDalFactory = (db: TDbClient) => { - const orm = ormify(db, TableName.OrgProxyConfig); - - return orm; -}; diff --git a/backend/src/ee/services/proxy/proxy-dal.ts b/backend/src/ee/services/proxy/proxy-dal.ts deleted file mode 100644 index a1570f2a8..000000000 --- a/backend/src/ee/services/proxy/proxy-dal.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { TDbClient } from "@app/db"; -import { TableName } from "@app/db/schemas"; -import { ormify } from "@app/lib/knex"; - -export type TProxyDALFactory = ReturnType; - -export const proxyDalFactory = (db: TDbClient) => { - const orm = ormify(db, TableName.Proxy); - - return orm; -}; diff --git a/backend/src/ee/services/proxy/proxy-fns.ts b/backend/src/ee/services/proxy/proxy-fns.ts deleted file mode 100644 index 58ad60832..000000000 --- a/backend/src/ee/services/proxy/proxy-fns.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const INSTANCE_PROXY_PREFIX = "infisical-"; - -export const isInstanceProxy = (proxyName: string) => { - return proxyName.startsWith(INSTANCE_PROXY_PREFIX); -}; diff --git a/backend/src/ee/services/proxy/proxy-service.ts b/backend/src/ee/services/proxy/proxy-service.ts deleted file mode 100644 index ae6ae3383..000000000 --- a/backend/src/ee/services/proxy/proxy-service.ts +++ /dev/null @@ -1,1008 +0,0 @@ -import * as x509 from "@peculiar/x509"; - -import { TProxies } from "@app/db/schemas"; -import { PgSqlLock } from "@app/keystore/keystore"; -import { crypto } from "@app/lib/crypto"; -import { BadRequestError, NotFoundError } from "@app/lib/errors"; -import { constructPemChainFromCerts, prependCertToPemChain } from "@app/services/certificate/certificate-fns"; -import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types"; -import { - createSerialNumber, - keyAlgorithmToAlgCfg -} from "@app/services/certificate-authority/certificate-authority-fns"; -import { TKmsServiceFactory } from "@app/services/kms/kms-service"; -import { KmsDataKey } from "@app/services/kms/kms-types"; - -import { createSshCert, createSshKeyPair } from "../ssh/ssh-certificate-authority-fns"; -import { SshCertType } from "../ssh/ssh-certificate-authority-types"; -import { SshCertKeyAlgorithm } from "../ssh-certificate/ssh-certificate-types"; -import { TInstanceProxyConfigDALFactory } from "./instance-proxy-config-dal"; -import { TOrgProxyConfigDALFactory } from "./org-proxy-config-dal"; -import { TProxyDALFactory } from "./proxy-dal"; -import { isInstanceProxy } from "./proxy-fns"; - -export type TProxyServiceFactory = ReturnType; - -const INSTANCE_PROXY_CONFIG_UUID = "00000000-0000-0000-0000-000000000000"; - -export const proxyServiceFactory = ({ - instanceProxyConfigDAL, - orgProxyConfigDAL, - proxyDAL, - kmsService -}: { - instanceProxyConfigDAL: TInstanceProxyConfigDALFactory; - orgProxyConfigDAL: TOrgProxyConfigDALFactory; - proxyDAL: TProxyDALFactory; - kmsService: TKmsServiceFactory; -}) => { - const $getInstanceCAs = async () => { - const instanceConfig = await instanceProxyConfigDAL.transaction(async (tx) => { - const existingInstanceProxyConfig = await instanceProxyConfigDAL.findById(INSTANCE_PROXY_CONFIG_UUID); - if (existingInstanceProxyConfig) return existingInstanceProxyConfig; - - await tx.raw("SELECT pg_advisory_xact_lock(?)", [PgSqlLock.InstanceProxyConfigInit()]); - - const alg = keyAlgorithmToAlgCfg(CertKeyAlgorithm.RSA_2048); - const rootCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); - - // generate root CA - const rootCaSerialNumber = createSerialNumber(); - const rootCaSkObj = crypto.nativeCrypto.KeyObject.from(rootCaKeys.privateKey); - const rootCaIssuedAt = new Date(); - const rootCaExpiration = new Date(new Date().setFullYear(2045)); - const rootCaCert = await x509.X509CertificateGenerator.createSelfSigned({ - name: `O=Infisical,CN=Infisical Instance Root Proxy CA`, - serialNumber: rootCaSerialNumber, - notBefore: rootCaIssuedAt, - notAfter: rootCaExpiration, - signingAlgorithm: alg, - keys: rootCaKeys, - extensions: [ - // eslint-disable-next-line no-bitwise - new x509.KeyUsagesExtension(x509.KeyUsageFlags.keyCertSign | x509.KeyUsageFlags.cRLSign, true), - await x509.SubjectKeyIdentifierExtension.create(rootCaKeys.publicKey) - ] - }); - - // generate org proxy CA - const orgProxyCaSerialNumber = createSerialNumber(); - const orgProxyCaIssuedAt = new Date(); - const orgProxyCaExpiration = new Date(new Date().setFullYear(2045)); - const orgProxyCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); - const orgProxyCaSkObj = crypto.nativeCrypto.KeyObject.from(orgProxyCaKeys.privateKey); - const orgProxyCaCert = await x509.X509CertificateGenerator.create({ - serialNumber: orgProxyCaSerialNumber, - subject: `O=Infisical,CN=Infisical Organization Proxy CA`, - issuer: rootCaCert.subject, - notBefore: orgProxyCaIssuedAt, - notAfter: orgProxyCaExpiration, - signingKey: rootCaKeys.privateKey, - publicKey: orgProxyCaKeys.publicKey, - signingAlgorithm: alg, - extensions: [ - new x509.KeyUsagesExtension( - // eslint-disable-next-line no-bitwise - x509.KeyUsageFlags.keyCertSign | - x509.KeyUsageFlags.cRLSign | - x509.KeyUsageFlags.digitalSignature | - x509.KeyUsageFlags.keyEncipherment, - true - ), - new x509.BasicConstraintsExtension(true, 2, true), - await x509.AuthorityKeyIdentifierExtension.create(rootCaCert, false), - await x509.SubjectKeyIdentifierExtension.create(orgProxyCaKeys.publicKey) - ] - }); - const orgProxyCaChain = constructPemChainFromCerts([rootCaCert]); - - // generate instance proxy CA - const instanceProxyCaSerialNumber = createSerialNumber(); - const instanceProxyCaIssuedAt = new Date(); - const instanceProxyCaExpiration = new Date(new Date().setFullYear(2045)); - const instanceProxyCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); - const instanceProxyCaSkObj = crypto.nativeCrypto.KeyObject.from(instanceProxyCaKeys.privateKey); - const instanceProxyCaCert = await x509.X509CertificateGenerator.create({ - serialNumber: instanceProxyCaSerialNumber, - subject: `O=Infisical,CN=Infisical Instance Proxy CA`, - issuer: rootCaCert.subject, - notBefore: instanceProxyCaIssuedAt, - notAfter: instanceProxyCaExpiration, - signingKey: rootCaKeys.privateKey, - publicKey: instanceProxyCaKeys.publicKey, - signingAlgorithm: alg, - extensions: [ - new x509.KeyUsagesExtension( - // eslint-disable-next-line no-bitwise - x509.KeyUsageFlags.keyCertSign | - x509.KeyUsageFlags.cRLSign | - x509.KeyUsageFlags.digitalSignature | - x509.KeyUsageFlags.keyEncipherment, - true - ), - new x509.BasicConstraintsExtension(true, 1, true), - await x509.AuthorityKeyIdentifierExtension.create(rootCaCert, false), - await x509.SubjectKeyIdentifierExtension.create(instanceProxyCaKeys.publicKey) - ] - }); - const instanceProxyCaChain = constructPemChainFromCerts([rootCaCert]); - - // generate instance proxy client CA - const instanceProxyClientCaSerialNumber = createSerialNumber(); - const instanceProxyClientCaIssuedAt = new Date(); - const instanceProxyClientCaExpiration = new Date(new Date().setFullYear(2045)); - const instanceProxyClientCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); - const instanceProxyClientCaSkObj = crypto.nativeCrypto.KeyObject.from(instanceProxyClientCaKeys.privateKey); - const instanceProxyClientCaCert = await x509.X509CertificateGenerator.create({ - serialNumber: instanceProxyClientCaSerialNumber, - subject: `O=Infisical,CN=Infisical Instance Proxy Client CA`, - issuer: instanceProxyCaCert.subject, - notBefore: instanceProxyClientCaIssuedAt, - notAfter: instanceProxyClientCaExpiration, - signingKey: instanceProxyCaKeys.privateKey, - publicKey: instanceProxyClientCaKeys.publicKey, - signingAlgorithm: alg, - extensions: [ - new x509.KeyUsagesExtension( - // eslint-disable-next-line no-bitwise - x509.KeyUsageFlags.keyCertSign | - x509.KeyUsageFlags.cRLSign | - x509.KeyUsageFlags.digitalSignature | - x509.KeyUsageFlags.keyEncipherment, - true - ), - new x509.BasicConstraintsExtension(true, 0, true), - await x509.AuthorityKeyIdentifierExtension.create(instanceProxyCaCert, false), - await x509.SubjectKeyIdentifierExtension.create(instanceProxyClientCaKeys.publicKey) - ] - }); - const instanceProxyClientCaChain = constructPemChainFromCerts([instanceProxyCaCert, rootCaCert]); - - // generate instance proxy server CA - const instanceProxyServerCaSerialNumber = createSerialNumber(); - const instanceProxyServerCaIssuedAt = new Date(); - const instanceProxyServerCaExpiration = new Date(new Date().setFullYear(2045)); - const instanceProxyServerCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); - const instanceProxyServerCaSkObj = crypto.nativeCrypto.KeyObject.from(instanceProxyServerCaKeys.privateKey); - const instanceProxyServerCaCert = await x509.X509CertificateGenerator.create({ - serialNumber: instanceProxyServerCaSerialNumber, - subject: `O=Infisical,CN=Infisical Instance Proxy Server CA`, - issuer: instanceProxyCaCert.subject, - notBefore: instanceProxyServerCaIssuedAt, - notAfter: instanceProxyServerCaExpiration, - signingKey: instanceProxyCaKeys.privateKey, - publicKey: instanceProxyServerCaKeys.publicKey, - signingAlgorithm: alg, - extensions: [ - new x509.KeyUsagesExtension( - // eslint-disable-next-line no-bitwise - x509.KeyUsageFlags.keyCertSign | - x509.KeyUsageFlags.cRLSign | - x509.KeyUsageFlags.digitalSignature | - x509.KeyUsageFlags.keyEncipherment, - true - ), - new x509.BasicConstraintsExtension(true, 0, true), - await x509.AuthorityKeyIdentifierExtension.create(instanceProxyCaCert, false), - await x509.SubjectKeyIdentifierExtension.create(instanceProxyServerCaKeys.publicKey) - ] - }); - const instanceProxyServerCaChain = constructPemChainFromCerts([instanceProxyCaCert, rootCaCert]); - - const instanceSshServerCaKeyPair = await createSshKeyPair(SshCertKeyAlgorithm.RSA_2048); - const instanceSshClientCaKeyPair = await createSshKeyPair(SshCertKeyAlgorithm.RSA_2048); - - const encryptWithRoot = kmsService.encryptWithRootKey(); - - // root proxy CA - const encryptedRootProxyPkiCaPrivateKey = encryptWithRoot( - Buffer.from( - rootCaSkObj.export({ - type: "pkcs8", - format: "der" - }) - ) - ); - const encryptedRootProxyPkiCaCertificate = encryptWithRoot(Buffer.from(rootCaCert.rawData)); - - // org proxy CA - const encryptedOrgProxyPkiCaPrivateKey = encryptWithRoot( - Buffer.from( - orgProxyCaSkObj.export({ - type: "pkcs8", - format: "der" - }) - ) - ); - const encryptedOrgProxyPkiCaCertificate = encryptWithRoot(Buffer.from(orgProxyCaCert.rawData)); - const encryptedOrgProxyPkiCaCertificateChain = encryptWithRoot(Buffer.from(orgProxyCaChain)); - - // instance proxy CA - const encryptedInstanceProxyPkiCaPrivateKey = encryptWithRoot( - Buffer.from( - instanceProxyCaSkObj.export({ - type: "pkcs8", - format: "der" - }) - ) - ); - const encryptedInstanceProxyPkiCaCertificate = encryptWithRoot(Buffer.from(instanceProxyCaCert.rawData)); - const encryptedInstanceProxyPkiCaCertificateChain = encryptWithRoot(Buffer.from(instanceProxyCaChain)); - - // instance proxy client CA - const encryptedInstanceProxyPkiClientCaPrivateKey = encryptWithRoot( - Buffer.from( - instanceProxyClientCaSkObj.export({ - type: "pkcs8", - format: "der" - }) - ) - ); - const encryptedInstanceProxyPkiClientCaCertificate = encryptWithRoot( - Buffer.from(instanceProxyClientCaCert.rawData) - ); - const encryptedInstanceProxyPkiClientCaCertificateChain = encryptWithRoot( - Buffer.from(instanceProxyClientCaChain) - ); - - // instance proxy server CA - const encryptedInstanceProxyPkiServerCaPrivateKey = encryptWithRoot( - Buffer.from( - instanceProxyServerCaSkObj.export({ - type: "pkcs8", - format: "der" - }) - ) - ); - const encryptedInstanceProxyPkiServerCaCertificate = encryptWithRoot( - Buffer.from(instanceProxyServerCaCert.rawData) - ); - const encryptedInstanceProxyPkiServerCaCertificateChain = encryptWithRoot( - Buffer.from(instanceProxyServerCaChain) - ); - - const encryptedInstanceProxySshClientCaPublicKey = encryptWithRoot( - Buffer.from(instanceSshClientCaKeyPair.publicKey) - ); - const encryptedInstanceProxySshClientCaPrivateKey = encryptWithRoot( - Buffer.from(instanceSshClientCaKeyPair.privateKey) - ); - - const encryptedInstanceProxySshServerCaPublicKey = encryptWithRoot( - Buffer.from(instanceSshServerCaKeyPair.publicKey) - ); - const encryptedInstanceProxySshServerCaPrivateKey = encryptWithRoot( - Buffer.from(instanceSshServerCaKeyPair.privateKey) - ); - - return instanceProxyConfigDAL.create({ - // @ts-expect-error id is kept as fixed for idempotence and to avoid race condition - id: INSTANCE_PROXY_CONFIG_UUID, - encryptedRootProxyPkiCaPrivateKey, - encryptedRootProxyPkiCaCertificate, - encryptedInstanceProxyPkiCaPrivateKey, - encryptedInstanceProxyPkiCaCertificate, - encryptedInstanceProxyPkiCaCertificateChain, - encryptedInstanceProxyPkiClientCaPrivateKey, - encryptedInstanceProxyPkiClientCaCertificate, - encryptedInstanceProxyPkiClientCaCertificateChain, - encryptedInstanceProxyPkiServerCaPrivateKey, - encryptedInstanceProxyPkiServerCaCertificate, - encryptedInstanceProxyPkiServerCaCertificateChain, - encryptedOrgProxyPkiCaPrivateKey, - encryptedOrgProxyPkiCaCertificate, - encryptedOrgProxyPkiCaCertificateChain, - encryptedInstanceProxySshClientCaPublicKey, - encryptedInstanceProxySshClientCaPrivateKey, - encryptedInstanceProxySshServerCaPublicKey, - encryptedInstanceProxySshServerCaPrivateKey - }); - }); - - // decrypt the instance config - const decryptWithRoot = kmsService.decryptWithRootKey(); - - // decrypt root proxy CA - const rootProxyPkiCaPrivateKey = decryptWithRoot(instanceConfig.encryptedRootProxyPkiCaPrivateKey); - const rootProxyPkiCaCertificate = decryptWithRoot(instanceConfig.encryptedRootProxyPkiCaCertificate); - - // decrypt org proxy CA - const orgProxyPkiCaPrivateKey = decryptWithRoot(instanceConfig.encryptedOrgProxyPkiCaPrivateKey); - const orgProxyPkiCaCertificate = decryptWithRoot(instanceConfig.encryptedOrgProxyPkiCaCertificate); - const orgProxyPkiCaCertificateChain = decryptWithRoot(instanceConfig.encryptedOrgProxyPkiCaCertificateChain); - - // decrypt instance proxy CA - const instanceProxyPkiCaPrivateKey = decryptWithRoot(instanceConfig.encryptedInstanceProxyPkiCaPrivateKey); - const instanceProxyPkiCaCertificate = decryptWithRoot(instanceConfig.encryptedInstanceProxyPkiCaCertificate); - const instanceProxyPkiCaCertificateChain = decryptWithRoot( - instanceConfig.encryptedInstanceProxyPkiCaCertificateChain - ); - - // decrypt instance proxy client CA - const instanceProxyPkiClientCaPrivateKey = decryptWithRoot( - instanceConfig.encryptedInstanceProxyPkiClientCaPrivateKey - ); - const instanceProxyPkiClientCaCertificate = decryptWithRoot( - instanceConfig.encryptedInstanceProxyPkiClientCaCertificate - ); - const instanceProxyPkiClientCaCertificateChain = decryptWithRoot( - instanceConfig.encryptedInstanceProxyPkiClientCaCertificateChain - ); - - // decrypt instance proxy server CA - const instanceProxyPkiServerCaPrivateKey = decryptWithRoot( - instanceConfig.encryptedInstanceProxyPkiServerCaPrivateKey - ); - const instanceProxyPkiServerCaCertificate = decryptWithRoot( - instanceConfig.encryptedInstanceProxyPkiServerCaCertificate - ); - const instanceProxyPkiServerCaCertificateChain = decryptWithRoot( - instanceConfig.encryptedInstanceProxyPkiServerCaCertificateChain - ); - - // decrypt SSH keys - const instanceProxySshClientCaPublicKey = decryptWithRoot( - instanceConfig.encryptedInstanceProxySshClientCaPublicKey - ); - const instanceProxySshClientCaPrivateKey = decryptWithRoot( - instanceConfig.encryptedInstanceProxySshClientCaPrivateKey - ); - const instanceProxySshServerCaPublicKey = decryptWithRoot( - instanceConfig.encryptedInstanceProxySshServerCaPublicKey - ); - const instanceProxySshServerCaPrivateKey = decryptWithRoot( - instanceConfig.encryptedInstanceProxySshServerCaPrivateKey - ); - - return { - rootProxyPkiCaPrivateKey, - rootProxyPkiCaCertificate, - orgProxyPkiCaPrivateKey, - orgProxyPkiCaCertificate, - orgProxyPkiCaCertificateChain, - instanceProxyPkiCaPrivateKey, - instanceProxyPkiCaCertificate, - instanceProxyPkiCaCertificateChain, - instanceProxyPkiClientCaPrivateKey, - instanceProxyPkiClientCaCertificate, - instanceProxyPkiClientCaCertificateChain, - instanceProxyPkiServerCaPrivateKey, - instanceProxyPkiServerCaCertificate, - instanceProxyPkiServerCaCertificateChain, - instanceProxySshClientCaPublicKey, - instanceProxySshClientCaPrivateKey, - instanceProxySshServerCaPublicKey, - instanceProxySshServerCaPrivateKey - }; - }; - - const $getOrgCAs = async (orgId: string) => { - const instanceCAs = await $getInstanceCAs(); - const { encryptor: orgKmsEncryptor, decryptor: orgKmsDecryptor } = await kmsService.createCipherPairWithDataKey({ - type: KmsDataKey.Organization, - orgId - }); - - const orgProxyConfig = await orgProxyConfigDAL.transaction(async (tx) => { - const existingOrgProxyConfig = await orgProxyConfigDAL.findOne( - { - orgId - }, - tx - ); - - if (existingOrgProxyConfig) { - return existingOrgProxyConfig; - } - - await tx.raw("SELECT pg_advisory_xact_lock(?)", [PgSqlLock.OrgProxyConfigInit(orgId)]); - - const alg = keyAlgorithmToAlgCfg(CertKeyAlgorithm.RSA_2048); - const orgProxyCaCert = new x509.X509Certificate(instanceCAs.orgProxyPkiCaCertificate); - const rootProxyCaCert = new x509.X509Certificate(instanceCAs.rootProxyPkiCaCertificate); - const orgProxyCaSkObj = crypto.nativeCrypto.createPrivateKey({ - key: instanceCAs.orgProxyPkiCaPrivateKey, - format: "der", - type: "pkcs8" - }); - const orgProxyCaPrivateKey = await crypto.nativeCrypto.subtle.importKey( - "pkcs8", - orgProxyCaSkObj.export({ format: "der", type: "pkcs8" }), - alg, - true, - ["sign"] - ); - - // generate org proxy client CA - const orgProxyClientCaSerialNumber = createSerialNumber(); - const orgProxyClientCaIssuedAt = new Date(); - const orgProxyClientCaExpiration = new Date(new Date().setFullYear(2045)); - const orgProxyClientCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); - const orgProxyClientCaSkObj = crypto.nativeCrypto.KeyObject.from(orgProxyClientCaKeys.privateKey); - const orgProxyClientCaCert = await x509.X509CertificateGenerator.create({ - serialNumber: orgProxyClientCaSerialNumber, - subject: `O=${orgId},CN=Infisical Org Proxy Client CA`, - issuer: orgProxyCaCert.subject, - notBefore: orgProxyClientCaIssuedAt, - notAfter: orgProxyClientCaExpiration, - signingKey: orgProxyCaPrivateKey, - publicKey: orgProxyClientCaKeys.publicKey, - signingAlgorithm: alg, - extensions: [ - new x509.KeyUsagesExtension( - // eslint-disable-next-line no-bitwise - x509.KeyUsageFlags.keyCertSign | - x509.KeyUsageFlags.cRLSign | - x509.KeyUsageFlags.digitalSignature | - x509.KeyUsageFlags.keyEncipherment, - true - ), - new x509.BasicConstraintsExtension(true, 0, true), - await x509.AuthorityKeyIdentifierExtension.create(orgProxyCaCert, false), - await x509.SubjectKeyIdentifierExtension.create(orgProxyClientCaKeys.publicKey) - ] - }); - const orgProxyClientCaChain = constructPemChainFromCerts([orgProxyCaCert, rootProxyCaCert]); - - // generate org SSH CA - const orgSshServerCaKeyPair = await createSshKeyPair(SshCertKeyAlgorithm.RSA_2048); - const orgSshClientCaKeyPair = await createSshKeyPair(SshCertKeyAlgorithm.RSA_2048); - - // generate org proxy server CA - const orgProxyServerCaSerialNumber = createSerialNumber(); - const orgProxyServerCaIssuedAt = new Date(); - const orgProxyServerCaExpiration = new Date(new Date().setFullYear(2045)); - const orgProxyServerCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); - const orgProxyServerCaSkObj = crypto.nativeCrypto.KeyObject.from(orgProxyServerCaKeys.privateKey); - const orgProxyServerCaCert = await x509.X509CertificateGenerator.create({ - serialNumber: orgProxyServerCaSerialNumber, - subject: `O=${orgId},CN=Infisical Org Proxy Server CA`, - issuer: orgProxyCaCert.subject, - notBefore: orgProxyServerCaIssuedAt, - notAfter: orgProxyServerCaExpiration, - signingKey: orgProxyCaPrivateKey, - publicKey: orgProxyServerCaKeys.publicKey, - signingAlgorithm: alg, - extensions: [ - new x509.KeyUsagesExtension( - // eslint-disable-next-line no-bitwise - x509.KeyUsageFlags.keyCertSign | - x509.KeyUsageFlags.cRLSign | - x509.KeyUsageFlags.digitalSignature | - x509.KeyUsageFlags.keyEncipherment, - true - ), - new x509.BasicConstraintsExtension(true, 0, true), - await x509.AuthorityKeyIdentifierExtension.create(orgProxyCaCert, false), - await x509.SubjectKeyIdentifierExtension.create(orgProxyServerCaKeys.publicKey) - ] - }); - const orgProxyServerCaChain = constructPemChainFromCerts([orgProxyCaCert, rootProxyCaCert]); - - const encryptedProxyPkiClientCaPrivateKey = orgKmsEncryptor({ - plainText: Buffer.from( - orgProxyClientCaSkObj.export({ - type: "pkcs8", - format: "der" - }) - ) - }).cipherTextBlob; - const encryptedProxyPkiClientCaCertificate = orgKmsEncryptor({ - plainText: Buffer.from(orgProxyClientCaCert.rawData) - }).cipherTextBlob; - - const encryptedProxyPkiClientCaCertificateChain = orgKmsEncryptor({ - plainText: Buffer.from(orgProxyClientCaChain) - }).cipherTextBlob; - - const encryptedProxyPkiServerCaPrivateKey = orgKmsEncryptor({ - plainText: Buffer.from( - orgProxyServerCaSkObj.export({ - type: "pkcs8", - format: "der" - }) - ) - }).cipherTextBlob; - const encryptedProxyPkiServerCaCertificate = orgKmsEncryptor({ - plainText: Buffer.from(orgProxyServerCaCert.rawData) - }).cipherTextBlob; - const encryptedProxyPkiServerCaCertificateChain = orgKmsEncryptor({ - plainText: Buffer.from(orgProxyServerCaChain) - }).cipherTextBlob; - - const encryptedProxySshClientCaPublicKey = orgKmsEncryptor({ - plainText: Buffer.from(orgSshClientCaKeyPair.publicKey) - }).cipherTextBlob; - const encryptedProxySshClientCaPrivateKey = orgKmsEncryptor({ - plainText: Buffer.from(orgSshClientCaKeyPair.privateKey) - }).cipherTextBlob; - - const encryptedProxySshServerCaPublicKey = orgKmsEncryptor({ - plainText: Buffer.from(orgSshServerCaKeyPair.publicKey) - }).cipherTextBlob; - const encryptedProxySshServerCaPrivateKey = orgKmsEncryptor({ - plainText: Buffer.from(orgSshServerCaKeyPair.privateKey) - }).cipherTextBlob; - - return orgProxyConfigDAL.create({ - orgId, - encryptedProxyPkiClientCaPrivateKey, - encryptedProxyPkiClientCaCertificate, - encryptedProxyPkiClientCaCertificateChain, - encryptedProxyPkiServerCaPrivateKey, - encryptedProxyPkiServerCaCertificate, - encryptedProxyPkiServerCaCertificateChain, - encryptedProxySshClientCaPublicKey, - encryptedProxySshClientCaPrivateKey, - encryptedProxySshServerCaPublicKey, - encryptedProxySshServerCaPrivateKey - }); - }); - - const proxyPkiClientCaPrivateKey = orgKmsDecryptor({ - cipherTextBlob: orgProxyConfig.encryptedProxyPkiClientCaPrivateKey - }); - const proxyPkiClientCaCertificate = orgKmsDecryptor({ - cipherTextBlob: orgProxyConfig.encryptedProxyPkiClientCaCertificate - }); - const proxyPkiClientCaCertificateChain = orgKmsDecryptor({ - cipherTextBlob: orgProxyConfig.encryptedProxyPkiClientCaCertificateChain - }); - - const proxyPkiServerCaPrivateKey = orgKmsDecryptor({ - cipherTextBlob: orgProxyConfig.encryptedProxyPkiServerCaPrivateKey - }); - const proxyPkiServerCaCertificate = orgKmsDecryptor({ - cipherTextBlob: orgProxyConfig.encryptedProxyPkiServerCaCertificate - }); - const proxyPkiServerCaCertificateChain = orgKmsDecryptor({ - cipherTextBlob: orgProxyConfig.encryptedProxyPkiServerCaCertificateChain - }); - - const proxySshClientCaPublicKey = orgKmsDecryptor({ - cipherTextBlob: orgProxyConfig.encryptedProxySshClientCaPublicKey - }); - const proxySshClientCaPrivateKey = orgKmsDecryptor({ - cipherTextBlob: orgProxyConfig.encryptedProxySshClientCaPrivateKey - }); - - const proxySshServerCaPublicKey = orgKmsDecryptor({ - cipherTextBlob: orgProxyConfig.encryptedProxySshServerCaPublicKey - }); - const proxySshServerCaPrivateKey = orgKmsDecryptor({ - cipherTextBlob: orgProxyConfig.encryptedProxySshServerCaPrivateKey - }); - - return { - proxyPkiClientCaPrivateKey, - proxyPkiClientCaCertificate, - proxyPkiClientCaCertificateChain, - proxyPkiServerCaPrivateKey, - proxyPkiServerCaCertificate, - proxyPkiServerCaCertificateChain, - proxySshClientCaPublicKey, - proxySshClientCaPrivateKey, - proxySshServerCaPublicKey, - proxySshServerCaPrivateKey - }; - }; - - const $generateProxyServerCredentials = async ({ - ip, - orgId, - proxyPkiServerCaCertificate, - proxyPkiServerCaPrivateKey, - proxyPkiClientCaCertificate, - proxyPkiClientCaCertificateChain, - proxySshClientCaPublicKey, - proxySshServerCaPrivateKey - }: { - ip: string; - proxyPkiServerCaCertificate: Buffer; - proxyPkiServerCaPrivateKey: Buffer; - proxyPkiClientCaCertificateChain: Buffer; - proxyPkiClientCaCertificate: Buffer; - proxySshServerCaPrivateKey: Buffer; - proxySshClientCaPublicKey: Buffer; - orgId?: string; - }) => { - const alg = keyAlgorithmToAlgCfg(CertKeyAlgorithm.RSA_2048); - const proxyServerCaCert = new x509.X509Certificate(proxyPkiServerCaCertificate); - const proxyClientCaCert = new x509.X509Certificate(proxyPkiClientCaCertificate); - const proxyServerCaSkObj = crypto.nativeCrypto.createPrivateKey({ - key: proxyPkiServerCaPrivateKey, - format: "der", - type: "pkcs8" - }); - - const proxyServerCaPrivateKey = await crypto.nativeCrypto.subtle.importKey( - "pkcs8", - proxyServerCaSkObj.export({ format: "der", type: "pkcs8" }), - alg, - true, - ["sign"] - ); - - const proxyServerKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); - const proxyServerCertIssuedAt = new Date(); - const proxyServerCertExpireAt = new Date(new Date().setMonth(new Date().getMonth() + 1)); - const proxyServerCertPrivateKey = crypto.nativeCrypto.KeyObject.from(proxyServerKeys.privateKey); - - const proxyServerCertExtensions: x509.Extension[] = [ - new x509.BasicConstraintsExtension(false), - await x509.AuthorityKeyIdentifierExtension.create(proxyServerCaCert, false), - await x509.SubjectKeyIdentifierExtension.create(proxyServerKeys.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], - true - ), - new x509.ExtendedKeyUsageExtension([x509.ExtendedKeyUsage[CertExtendedKeyUsage.SERVER_AUTH]], true), - // san - new x509.SubjectAlternativeNameExtension([{ type: "ip", value: ip }], false) - ]; - - const proxyServerSerialNumber = createSerialNumber(); - const proxyServerCertificate = await x509.X509CertificateGenerator.create({ - serialNumber: proxyServerSerialNumber, - subject: `CN=${ip},O=${orgId ?? "Infisical"},OU=Proxy`, - issuer: proxyServerCaCert.subject, - notBefore: proxyServerCertIssuedAt, - notAfter: proxyServerCertExpireAt, - signingKey: proxyServerCaPrivateKey, - publicKey: proxyServerKeys.publicKey, - signingAlgorithm: alg, - extensions: proxyServerCertExtensions - }); - - // generate proxy server SSH certificate - const keyAlgorithm = SshCertKeyAlgorithm.RSA_2048; - const { publicKey: proxyServerSshPublicKey, privateKey: proxyServerSshPrivateKey } = - await createSshKeyPair(keyAlgorithm); - - const proxyServerSshCert = await createSshCert({ - caPrivateKey: proxySshServerCaPrivateKey.toString("utf8"), - clientPublicKey: proxyServerSshPublicKey, - keyId: "proxy-server", - principals: [`${ip}:2222`], - certType: SshCertType.HOST, - requestedTtl: "30d" - }); - - return { - pki: { - serverCertificate: proxyServerCertificate.toString("pem"), - serverPrivateKey: proxyServerCertPrivateKey.export({ format: "pem", type: "pkcs8" }).toString(), - clientCertificateChain: prependCertToPemChain( - proxyClientCaCert, - proxyPkiClientCaCertificateChain.toString("utf8") - ) - }, - ssh: { - serverCertificate: proxyServerSshCert.signedPublicKey, - serverPrivateKey: proxyServerSshPrivateKey, - clientCAPublicKey: proxySshClientCaPublicKey.toString("utf8") - } - }; - }; - - const $generateProxyClientCredentials = async ({ - gatewayId, - orgId, - proxyPkiClientCaCertificate, - proxyPkiClientCaPrivateKey, - proxyPkiServerCaCertificate, - proxyPkiServerCaCertificateChain - }: { - gatewayId: string; - orgId: string; - proxyPkiClientCaCertificate: Buffer; - proxyPkiClientCaPrivateKey: Buffer; - proxyPkiServerCaCertificate: Buffer; - proxyPkiServerCaCertificateChain: Buffer; - }) => { - const alg = keyAlgorithmToAlgCfg(CertKeyAlgorithm.RSA_2048); - const proxyClientCaCert = new x509.X509Certificate(proxyPkiClientCaCertificate); - const proxyServerCaCert = new x509.X509Certificate(proxyPkiServerCaCertificate); - const proxyClientCaSkObj = crypto.nativeCrypto.createPrivateKey({ - key: proxyPkiClientCaPrivateKey, - format: "der", - type: "pkcs8" - }); - - const importedProxyClientCaPrivateKey = await crypto.nativeCrypto.subtle.importKey( - "pkcs8", - proxyClientCaSkObj.export({ format: "der", type: "pkcs8" }), - alg, - true, - ["sign"] - ); - - const clientCertIssuedAt = new Date(); - const clientCertExpiration = new Date(new Date().getTime() + 5 * 60 * 1000); - const clientKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); - const clientCertPrivateKey = crypto.nativeCrypto.KeyObject.from(clientKeys.privateKey); - 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({ - serialNumber: clientCertSerialNumber, - subject: `O=${orgId},OU=proxy-client,CN=${gatewayId}`, - issuer: proxyClientCaCert.subject, - notAfter: clientCertExpiration, - notBefore: clientCertIssuedAt, - signingKey: importedProxyClientCaPrivateKey, - publicKey: clientKeys.publicKey, - signingAlgorithm: alg, - extensions - }); - - return { - clientCertificate: clientCert.toString("pem"), - clientPrivateKey: clientCertPrivateKey.export({ format: "pem", type: "pkcs8" }).toString(), - serverCertificateChain: prependCertToPemChain( - proxyServerCaCert, - proxyPkiServerCaCertificateChain.toString("utf8") - ) - }; - }; - - const getCredentialsForGateway = async ({ - proxyName, - orgId, - gatewayId - }: { - proxyName: string; - orgId: string; - gatewayId: string; - }) => { - let proxy: TProxies | null; - if (isInstanceProxy(proxyName)) { - proxy = await proxyDAL.findOne({ - name: proxyName - }); - } else { - proxy = await proxyDAL.findOne({ - orgId, - name: proxyName - }); - } - - if (!proxy) { - throw new NotFoundError({ - message: "Proxy not found" - }); - } - - const keyAlgorithm = SshCertKeyAlgorithm.RSA_2048; - const { publicKey: proxyClientSshPublicKey, privateKey: proxyClientSshPrivateKey } = - await createSshKeyPair(keyAlgorithm); - - if (isInstanceProxy(proxyName)) { - const instanceCAs = await $getInstanceCAs(); - const proxyClientSshCert = await createSshCert({ - caPrivateKey: instanceCAs.instanceProxySshServerCaPrivateKey.toString("utf8"), - clientPublicKey: proxyClientSshPublicKey, - keyId: `client-${proxyName}`, - principals: [gatewayId], - certType: SshCertType.USER, - requestedTtl: "30d" - }); - - return { - proxyIp: proxy.ip, - clientSshCert: proxyClientSshCert.signedPublicKey, - clientSshPrivateKey: proxyClientSshPrivateKey, - serverCAPublicKey: instanceCAs.instanceProxySshServerCaPublicKey.toString("utf8") - }; - } - - const orgCAs = await $getOrgCAs(orgId); - const proxyClientSshCert = await createSshCert({ - caPrivateKey: orgCAs.proxySshServerCaPrivateKey.toString("utf8"), - clientPublicKey: proxyClientSshPublicKey, - keyId: `proxy-client-${proxy.id}`, - principals: [gatewayId], - certType: SshCertType.USER, - requestedTtl: "30d" - }); - - return { - proxyIp: proxy.ip, - clientSshCert: proxyClientSshCert.signedPublicKey, - clientSshPrivateKey: proxyClientSshPrivateKey, - serverCAPublicKey: orgCAs.proxySshServerCaPublicKey.toString("utf8") - }; - }; - - const getCredentialsForClient = async ({ - proxyId, - orgId, - gatewayId - }: { - proxyId: string; - orgId: string; - gatewayId: string; - }) => { - const proxy = await proxyDAL.findOne({ - id: proxyId - }); - - if (!proxy) { - throw new NotFoundError({ - message: "Proxy not found" - }); - } - - if (isInstanceProxy(proxy.name)) { - const instanceCAs = await $getInstanceCAs(); - const proxyCertificateCredentials = await $generateProxyClientCredentials({ - gatewayId, - orgId, - proxyPkiClientCaCertificate: instanceCAs.instanceProxyPkiClientCaCertificate, - proxyPkiClientCaPrivateKey: instanceCAs.instanceProxyPkiClientCaPrivateKey, - proxyPkiServerCaCertificate: instanceCAs.instanceProxyPkiServerCaCertificate, - proxyPkiServerCaCertificateChain: instanceCAs.instanceProxyPkiServerCaCertificateChain - }); - - return { - ...proxyCertificateCredentials, - proxyIp: proxy.ip - }; - } - - const orgCAs = await $getOrgCAs(orgId); - const proxyCertificateCredentials = await $generateProxyClientCredentials({ - gatewayId, - orgId, - proxyPkiClientCaCertificate: orgCAs.proxyPkiClientCaCertificate, - proxyPkiClientCaPrivateKey: orgCAs.proxyPkiClientCaPrivateKey, - proxyPkiServerCaCertificate: orgCAs.proxyPkiServerCaCertificate, - proxyPkiServerCaCertificateChain: orgCAs.proxyPkiServerCaCertificateChain - }); - - return { - ...proxyCertificateCredentials, - proxyIp: proxy.ip - }; - }; - - const registerProxy = async ({ - ip, - name, - identityId, - orgId - }: { - ip: string; - name: string; - identityId?: string; - orgId?: string; - }) => { - let proxy: TProxies; - const isOrgProxy = identityId && orgId; - - if (isOrgProxy) { - if (isInstanceProxy(name)) { - throw new BadRequestError({ - message: "Org proxy name cannot start with 'infisical-'. This is reserved for internal use." - }); - } - - proxy = await proxyDAL.transaction(async (tx) => { - const existingProxy = await proxyDAL.findOne( - { - identityId, - orgId - }, - tx - ); - - if (existingProxy && (existingProxy.ip !== ip || existingProxy.name !== name)) { - throw new BadRequestError({ - message: "Org proxy with this machine identity already exists." - }); - } - - if (!existingProxy) { - return proxyDAL.create( - { - ip, - name, - identityId, - orgId - }, - tx - ); - } - - return existingProxy; - }); - } else { - if (!isInstanceProxy(name)) { - throw new BadRequestError({ - message: "Instance proxy name must start with 'infisical-'." - }); - } - - proxy = await proxyDAL.transaction(async (tx) => { - const existingProxy = await proxyDAL.findOne( - { - name - }, - tx - ); - - if (existingProxy && existingProxy.ip !== ip) { - throw new BadRequestError({ - message: "Instance proxy with this name already exists with a different IP address" - }); - } - - if (!existingProxy) { - return proxyDAL.create( - { - ip, - name - }, - tx - ); - } - - return existingProxy; - }); - } - - if (isInstanceProxy(name)) { - const instanceCAs = await $getInstanceCAs(); - return $generateProxyServerCredentials({ - ip, - proxyPkiServerCaCertificate: instanceCAs.instanceProxyPkiServerCaCertificate, - proxyPkiServerCaPrivateKey: instanceCAs.instanceProxyPkiServerCaPrivateKey, - proxyPkiClientCaCertificate: instanceCAs.instanceProxyPkiClientCaCertificate, - proxyPkiClientCaCertificateChain: instanceCAs.instanceProxyPkiClientCaCertificateChain, - proxySshServerCaPrivateKey: instanceCAs.instanceProxySshServerCaPrivateKey, - proxySshClientCaPublicKey: instanceCAs.instanceProxySshClientCaPublicKey - }); - } - - if (proxy.orgId) { - const orgCAs = await $getOrgCAs(proxy.orgId); - return $generateProxyServerCredentials({ - ip, - orgId: proxy.orgId, - proxyPkiServerCaCertificate: orgCAs.proxyPkiServerCaCertificate, - proxyPkiServerCaPrivateKey: orgCAs.proxyPkiServerCaPrivateKey, - proxyPkiClientCaCertificate: orgCAs.proxyPkiClientCaCertificate, - proxyPkiClientCaCertificateChain: orgCAs.proxyPkiClientCaCertificateChain, - proxySshServerCaPrivateKey: orgCAs.proxySshServerCaPrivateKey, - proxySshClientCaPublicKey: orgCAs.proxySshClientCaPublicKey - }); - } - - throw new BadRequestError({ - message: "Unhandled proxy type" - }); - }; - - return { - registerProxy, - getCredentialsForGateway, - getCredentialsForClient - }; -}; diff --git a/backend/src/ee/services/relay/instance-relay-config-dal.ts b/backend/src/ee/services/relay/instance-relay-config-dal.ts new file mode 100644 index 000000000..6db3b93e7 --- /dev/null +++ b/backend/src/ee/services/relay/instance-relay-config-dal.ts @@ -0,0 +1,11 @@ +import { TDbClient } from "@app/db"; +import { TableName } from "@app/db/schemas"; +import { ormify } from "@app/lib/knex"; + +export type TInstanceRelayConfigDALFactory = ReturnType; + +export const instanceRelayConfigDalFactory = (db: TDbClient) => { + const orm = ormify(db, TableName.InstanceRelayConfig); + + return orm; +}; diff --git a/backend/src/ee/services/relay/org-relay-config-dal.ts b/backend/src/ee/services/relay/org-relay-config-dal.ts new file mode 100644 index 000000000..7da35b9dc --- /dev/null +++ b/backend/src/ee/services/relay/org-relay-config-dal.ts @@ -0,0 +1,11 @@ +import { TDbClient } from "@app/db"; +import { TableName } from "@app/db/schemas"; +import { ormify } from "@app/lib/knex"; + +export type TOrgRelayConfigDALFactory = ReturnType; + +export const orgRelayConfigDalFactory = (db: TDbClient) => { + const orm = ormify(db, TableName.OrgRelayConfig); + + return orm; +}; diff --git a/backend/src/ee/services/relay/relay-dal.ts b/backend/src/ee/services/relay/relay-dal.ts new file mode 100644 index 000000000..9107e0807 --- /dev/null +++ b/backend/src/ee/services/relay/relay-dal.ts @@ -0,0 +1,11 @@ +import { TDbClient } from "@app/db"; +import { TableName } from "@app/db/schemas"; +import { ormify } from "@app/lib/knex"; + +export type TRelayDALFactory = ReturnType; + +export const relayDalFactory = (db: TDbClient) => { + const orm = ormify(db, TableName.Relay); + + return orm; +}; diff --git a/backend/src/ee/services/relay/relay-fns.ts b/backend/src/ee/services/relay/relay-fns.ts new file mode 100644 index 000000000..f33210798 --- /dev/null +++ b/backend/src/ee/services/relay/relay-fns.ts @@ -0,0 +1,5 @@ +export const INSTANCE_RELAY_PREFIX = "infisical-"; + +export const isInstanceRelay = (relayName: string) => { + return relayName.startsWith(INSTANCE_RELAY_PREFIX); +}; diff --git a/backend/src/ee/services/relay/relay-service.ts b/backend/src/ee/services/relay/relay-service.ts new file mode 100644 index 000000000..90e1e02ee --- /dev/null +++ b/backend/src/ee/services/relay/relay-service.ts @@ -0,0 +1,1008 @@ +import * as x509 from "@peculiar/x509"; + +import { TRelays } from "@app/db/schemas"; +import { PgSqlLock } from "@app/keystore/keystore"; +import { crypto } from "@app/lib/crypto"; +import { BadRequestError, NotFoundError } from "@app/lib/errors"; +import { constructPemChainFromCerts, prependCertToPemChain } from "@app/services/certificate/certificate-fns"; +import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types"; +import { + createSerialNumber, + keyAlgorithmToAlgCfg +} from "@app/services/certificate-authority/certificate-authority-fns"; +import { TKmsServiceFactory } from "@app/services/kms/kms-service"; +import { KmsDataKey } from "@app/services/kms/kms-types"; + +import { createSshCert, createSshKeyPair } from "../ssh/ssh-certificate-authority-fns"; +import { SshCertType } from "../ssh/ssh-certificate-authority-types"; +import { SshCertKeyAlgorithm } from "../ssh-certificate/ssh-certificate-types"; +import { TInstanceRelayConfigDALFactory } from "./instance-relay-config-dal"; +import { TOrgRelayConfigDALFactory } from "./org-relay-config-dal"; +import { TRelayDALFactory } from "./relay-dal"; +import { isInstanceRelay } from "./relay-fns"; + +export type TRelayServiceFactory = ReturnType; + +const INSTANCE_RELAY_CONFIG_UUID = "00000000-0000-0000-0000-000000000000"; + +export const relayServiceFactory = ({ + instanceRelayConfigDAL, + orgRelayConfigDAL, + relayDAL, + kmsService +}: { + instanceRelayConfigDAL: TInstanceRelayConfigDALFactory; + orgRelayConfigDAL: TOrgRelayConfigDALFactory; + relayDAL: TRelayDALFactory; + kmsService: TKmsServiceFactory; +}) => { + const $getInstanceCAs = async () => { + const instanceConfig = await instanceRelayConfigDAL.transaction(async (tx) => { + const existingInstanceRelayConfig = await instanceRelayConfigDAL.findById(INSTANCE_RELAY_CONFIG_UUID); + if (existingInstanceRelayConfig) return existingInstanceRelayConfig; + + await tx.raw("SELECT pg_advisory_xact_lock(?)", [PgSqlLock.InstanceRelayConfigInit()]); + + const alg = keyAlgorithmToAlgCfg(CertKeyAlgorithm.RSA_2048); + const rootCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); + + // generate root CA + const rootCaSerialNumber = createSerialNumber(); + const rootCaSkObj = crypto.nativeCrypto.KeyObject.from(rootCaKeys.privateKey); + const rootCaIssuedAt = new Date(); + const rootCaExpiration = new Date(new Date().setFullYear(2045)); + const rootCaCert = await x509.X509CertificateGenerator.createSelfSigned({ + name: `O=Infisical,CN=Infisical Instance Root Relay CA`, + serialNumber: rootCaSerialNumber, + notBefore: rootCaIssuedAt, + notAfter: rootCaExpiration, + signingAlgorithm: alg, + keys: rootCaKeys, + extensions: [ + // eslint-disable-next-line no-bitwise + new x509.KeyUsagesExtension(x509.KeyUsageFlags.keyCertSign | x509.KeyUsageFlags.cRLSign, true), + await x509.SubjectKeyIdentifierExtension.create(rootCaKeys.publicKey) + ] + }); + + // generate org relay CA + const orgRelayCaSerialNumber = createSerialNumber(); + const orgRelayCaIssuedAt = new Date(); + const orgRelayCaExpiration = new Date(new Date().setFullYear(2045)); + const orgRelayCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); + const orgRelayCaSkObj = crypto.nativeCrypto.KeyObject.from(orgRelayCaKeys.privateKey); + const orgRelayCaCert = await x509.X509CertificateGenerator.create({ + serialNumber: orgRelayCaSerialNumber, + subject: `O=Infisical,CN=Infisical Organization Relay CA`, + issuer: rootCaCert.subject, + notBefore: orgRelayCaIssuedAt, + notAfter: orgRelayCaExpiration, + signingKey: rootCaKeys.privateKey, + publicKey: orgRelayCaKeys.publicKey, + signingAlgorithm: alg, + extensions: [ + new x509.KeyUsagesExtension( + // eslint-disable-next-line no-bitwise + x509.KeyUsageFlags.keyCertSign | + x509.KeyUsageFlags.cRLSign | + x509.KeyUsageFlags.digitalSignature | + x509.KeyUsageFlags.keyEncipherment, + true + ), + new x509.BasicConstraintsExtension(true, 2, true), + await x509.AuthorityKeyIdentifierExtension.create(rootCaCert, false), + await x509.SubjectKeyIdentifierExtension.create(orgRelayCaKeys.publicKey) + ] + }); + const orgRelayCaChain = constructPemChainFromCerts([rootCaCert]); + + // generate instance relay CA + const instanceRelayCaSerialNumber = createSerialNumber(); + const instanceRelayCaIssuedAt = new Date(); + const instanceRelayCaExpiration = new Date(new Date().setFullYear(2045)); + const instanceRelayCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); + const instanceRelayCaSkObj = crypto.nativeCrypto.KeyObject.from(instanceRelayCaKeys.privateKey); + const instanceRelayCaCert = await x509.X509CertificateGenerator.create({ + serialNumber: instanceRelayCaSerialNumber, + subject: `O=Infisical,CN=Infisical Instance Relay CA`, + issuer: rootCaCert.subject, + notBefore: instanceRelayCaIssuedAt, + notAfter: instanceRelayCaExpiration, + signingKey: rootCaKeys.privateKey, + publicKey: instanceRelayCaKeys.publicKey, + signingAlgorithm: alg, + extensions: [ + new x509.KeyUsagesExtension( + // eslint-disable-next-line no-bitwise + x509.KeyUsageFlags.keyCertSign | + x509.KeyUsageFlags.cRLSign | + x509.KeyUsageFlags.digitalSignature | + x509.KeyUsageFlags.keyEncipherment, + true + ), + new x509.BasicConstraintsExtension(true, 1, true), + await x509.AuthorityKeyIdentifierExtension.create(rootCaCert, false), + await x509.SubjectKeyIdentifierExtension.create(instanceRelayCaKeys.publicKey) + ] + }); + const instanceRelayCaChain = constructPemChainFromCerts([rootCaCert]); + + // generate instance relay client CA + const instanceRelayClientCaSerialNumber = createSerialNumber(); + const instanceRelayClientCaIssuedAt = new Date(); + const instanceRelayClientCaExpiration = new Date(new Date().setFullYear(2045)); + const instanceRelayClientCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); + const instanceRelayClientCaSkObj = crypto.nativeCrypto.KeyObject.from(instanceRelayClientCaKeys.privateKey); + const instanceRelayClientCaCert = await x509.X509CertificateGenerator.create({ + serialNumber: instanceRelayClientCaSerialNumber, + subject: `O=Infisical,CN=Infisical Instance Relay Client CA`, + issuer: instanceRelayCaCert.subject, + notBefore: instanceRelayClientCaIssuedAt, + notAfter: instanceRelayClientCaExpiration, + signingKey: instanceRelayCaKeys.privateKey, + publicKey: instanceRelayClientCaKeys.publicKey, + signingAlgorithm: alg, + extensions: [ + new x509.KeyUsagesExtension( + // eslint-disable-next-line no-bitwise + x509.KeyUsageFlags.keyCertSign | + x509.KeyUsageFlags.cRLSign | + x509.KeyUsageFlags.digitalSignature | + x509.KeyUsageFlags.keyEncipherment, + true + ), + new x509.BasicConstraintsExtension(true, 0, true), + await x509.AuthorityKeyIdentifierExtension.create(instanceRelayCaCert, false), + await x509.SubjectKeyIdentifierExtension.create(instanceRelayClientCaKeys.publicKey) + ] + }); + const instanceRelayClientCaChain = constructPemChainFromCerts([instanceRelayCaCert, rootCaCert]); + + // generate instance relay server CA + const instanceRelayServerCaSerialNumber = createSerialNumber(); + const instanceRelayServerCaIssuedAt = new Date(); + const instanceRelayServerCaExpiration = new Date(new Date().setFullYear(2045)); + const instanceRelayServerCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); + const instanceRelayServerCaSkObj = crypto.nativeCrypto.KeyObject.from(instanceRelayServerCaKeys.privateKey); + const instanceRelayServerCaCert = await x509.X509CertificateGenerator.create({ + serialNumber: instanceRelayServerCaSerialNumber, + subject: `O=Infisical,CN=Infisical Instance Relay Server CA`, + issuer: instanceRelayCaCert.subject, + notBefore: instanceRelayServerCaIssuedAt, + notAfter: instanceRelayServerCaExpiration, + signingKey: instanceRelayCaKeys.privateKey, + publicKey: instanceRelayServerCaKeys.publicKey, + signingAlgorithm: alg, + extensions: [ + new x509.KeyUsagesExtension( + // eslint-disable-next-line no-bitwise + x509.KeyUsageFlags.keyCertSign | + x509.KeyUsageFlags.cRLSign | + x509.KeyUsageFlags.digitalSignature | + x509.KeyUsageFlags.keyEncipherment, + true + ), + new x509.BasicConstraintsExtension(true, 0, true), + await x509.AuthorityKeyIdentifierExtension.create(instanceRelayCaCert, false), + await x509.SubjectKeyIdentifierExtension.create(instanceRelayServerCaKeys.publicKey) + ] + }); + const instanceRelayServerCaChain = constructPemChainFromCerts([instanceRelayCaCert, rootCaCert]); + + const instanceSshServerCaKeyPair = await createSshKeyPair(SshCertKeyAlgorithm.RSA_2048); + const instanceSshClientCaKeyPair = await createSshKeyPair(SshCertKeyAlgorithm.RSA_2048); + + const encryptWithRoot = kmsService.encryptWithRootKey(); + + // root relay CA + const encryptedRootRelayPkiCaPrivateKey = encryptWithRoot( + Buffer.from( + rootCaSkObj.export({ + type: "pkcs8", + format: "der" + }) + ) + ); + const encryptedRootRelayPkiCaCertificate = encryptWithRoot(Buffer.from(rootCaCert.rawData)); + + // org relay CA + const encryptedOrgRelayPkiCaPrivateKey = encryptWithRoot( + Buffer.from( + orgRelayCaSkObj.export({ + type: "pkcs8", + format: "der" + }) + ) + ); + const encryptedOrgRelayPkiCaCertificate = encryptWithRoot(Buffer.from(orgRelayCaCert.rawData)); + const encryptedOrgRelayPkiCaCertificateChain = encryptWithRoot(Buffer.from(orgRelayCaChain)); + + // instance relay CA + const encryptedInstanceRelayPkiCaPrivateKey = encryptWithRoot( + Buffer.from( + instanceRelayCaSkObj.export({ + type: "pkcs8", + format: "der" + }) + ) + ); + const encryptedInstanceRelayPkiCaCertificate = encryptWithRoot(Buffer.from(instanceRelayCaCert.rawData)); + const encryptedInstanceRelayPkiCaCertificateChain = encryptWithRoot(Buffer.from(instanceRelayCaChain)); + + // instance relay client CA + const encryptedInstanceRelayPkiClientCaPrivateKey = encryptWithRoot( + Buffer.from( + instanceRelayClientCaSkObj.export({ + type: "pkcs8", + format: "der" + }) + ) + ); + const encryptedInstanceRelayPkiClientCaCertificate = encryptWithRoot( + Buffer.from(instanceRelayClientCaCert.rawData) + ); + const encryptedInstanceRelayPkiClientCaCertificateChain = encryptWithRoot( + Buffer.from(instanceRelayClientCaChain) + ); + + // instance relay server CA + const encryptedInstanceRelayPkiServerCaPrivateKey = encryptWithRoot( + Buffer.from( + instanceRelayServerCaSkObj.export({ + type: "pkcs8", + format: "der" + }) + ) + ); + const encryptedInstanceRelayPkiServerCaCertificate = encryptWithRoot( + Buffer.from(instanceRelayServerCaCert.rawData) + ); + const encryptedInstanceRelayPkiServerCaCertificateChain = encryptWithRoot( + Buffer.from(instanceRelayServerCaChain) + ); + + const encryptedInstanceRelaySshClientCaPublicKey = encryptWithRoot( + Buffer.from(instanceSshClientCaKeyPair.publicKey) + ); + const encryptedInstanceRelaySshClientCaPrivateKey = encryptWithRoot( + Buffer.from(instanceSshClientCaKeyPair.privateKey) + ); + + const encryptedInstanceRelaySshServerCaPublicKey = encryptWithRoot( + Buffer.from(instanceSshServerCaKeyPair.publicKey) + ); + const encryptedInstanceRelaySshServerCaPrivateKey = encryptWithRoot( + Buffer.from(instanceSshServerCaKeyPair.privateKey) + ); + + return instanceRelayConfigDAL.create({ + // @ts-expect-error id is kept as fixed for idempotence and to avoid race condition + id: INSTANCE_RELAY_CONFIG_UUID, + encryptedRootRelayPkiCaPrivateKey, + encryptedRootRelayPkiCaCertificate, + encryptedInstanceRelayPkiCaPrivateKey, + encryptedInstanceRelayPkiCaCertificate, + encryptedInstanceRelayPkiCaCertificateChain, + encryptedInstanceRelayPkiClientCaPrivateKey, + encryptedInstanceRelayPkiClientCaCertificate, + encryptedInstanceRelayPkiClientCaCertificateChain, + encryptedInstanceRelayPkiServerCaPrivateKey, + encryptedInstanceRelayPkiServerCaCertificate, + encryptedInstanceRelayPkiServerCaCertificateChain, + encryptedOrgRelayPkiCaPrivateKey, + encryptedOrgRelayPkiCaCertificate, + encryptedOrgRelayPkiCaCertificateChain, + encryptedInstanceRelaySshClientCaPublicKey, + encryptedInstanceRelaySshClientCaPrivateKey, + encryptedInstanceRelaySshServerCaPublicKey, + encryptedInstanceRelaySshServerCaPrivateKey + }); + }); + + // decrypt the instance config + const decryptWithRoot = kmsService.decryptWithRootKey(); + + // decrypt root relay CA + const rootRelayPkiCaPrivateKey = decryptWithRoot(instanceConfig.encryptedRootRelayPkiCaPrivateKey); + const rootRelayPkiCaCertificate = decryptWithRoot(instanceConfig.encryptedRootRelayPkiCaCertificate); + + // decrypt org relay CA + const orgRelayPkiCaPrivateKey = decryptWithRoot(instanceConfig.encryptedOrgRelayPkiCaPrivateKey); + const orgRelayPkiCaCertificate = decryptWithRoot(instanceConfig.encryptedOrgRelayPkiCaCertificate); + const orgRelayPkiCaCertificateChain = decryptWithRoot(instanceConfig.encryptedOrgRelayPkiCaCertificateChain); + + // decrypt instance relay CA + const instanceRelayPkiCaPrivateKey = decryptWithRoot(instanceConfig.encryptedInstanceRelayPkiCaPrivateKey); + const instanceRelayPkiCaCertificate = decryptWithRoot(instanceConfig.encryptedInstanceRelayPkiCaCertificate); + const instanceRelayPkiCaCertificateChain = decryptWithRoot( + instanceConfig.encryptedInstanceRelayPkiCaCertificateChain + ); + + // decrypt instance relay client CA + const instanceRelayPkiClientCaPrivateKey = decryptWithRoot( + instanceConfig.encryptedInstanceRelayPkiClientCaPrivateKey + ); + const instanceRelayPkiClientCaCertificate = decryptWithRoot( + instanceConfig.encryptedInstanceRelayPkiClientCaCertificate + ); + const instanceRelayPkiClientCaCertificateChain = decryptWithRoot( + instanceConfig.encryptedInstanceRelayPkiClientCaCertificateChain + ); + + // decrypt instance relay server CA + const instanceRelayPkiServerCaPrivateKey = decryptWithRoot( + instanceConfig.encryptedInstanceRelayPkiServerCaPrivateKey + ); + const instanceRelayPkiServerCaCertificate = decryptWithRoot( + instanceConfig.encryptedInstanceRelayPkiServerCaCertificate + ); + const instanceRelayPkiServerCaCertificateChain = decryptWithRoot( + instanceConfig.encryptedInstanceRelayPkiServerCaCertificateChain + ); + + // decrypt SSH keys + const instanceRelaySshClientCaPublicKey = decryptWithRoot( + instanceConfig.encryptedInstanceRelaySshClientCaPublicKey + ); + const instanceRelaySshClientCaPrivateKey = decryptWithRoot( + instanceConfig.encryptedInstanceRelaySshClientCaPrivateKey + ); + const instanceRelaySshServerCaPublicKey = decryptWithRoot( + instanceConfig.encryptedInstanceRelaySshServerCaPublicKey + ); + const instanceRelaySshServerCaPrivateKey = decryptWithRoot( + instanceConfig.encryptedInstanceRelaySshServerCaPrivateKey + ); + + return { + rootRelayPkiCaPrivateKey, + rootRelayPkiCaCertificate, + orgRelayPkiCaPrivateKey, + orgRelayPkiCaCertificate, + orgRelayPkiCaCertificateChain, + instanceRelayPkiCaPrivateKey, + instanceRelayPkiCaCertificate, + instanceRelayPkiCaCertificateChain, + instanceRelayPkiClientCaPrivateKey, + instanceRelayPkiClientCaCertificate, + instanceRelayPkiClientCaCertificateChain, + instanceRelayPkiServerCaPrivateKey, + instanceRelayPkiServerCaCertificate, + instanceRelayPkiServerCaCertificateChain, + instanceRelaySshClientCaPublicKey, + instanceRelaySshClientCaPrivateKey, + instanceRelaySshServerCaPublicKey, + instanceRelaySshServerCaPrivateKey + }; + }; + + const $getOrgCAs = async (orgId: string) => { + const instanceCAs = await $getInstanceCAs(); + const { encryptor: orgKmsEncryptor, decryptor: orgKmsDecryptor } = await kmsService.createCipherPairWithDataKey({ + type: KmsDataKey.Organization, + orgId + }); + + const orgRelayConfig = await orgRelayConfigDAL.transaction(async (tx) => { + const existingOrgRelayConfig = await orgRelayConfigDAL.findOne( + { + orgId + }, + tx + ); + + if (existingOrgRelayConfig) { + return existingOrgRelayConfig; + } + + await tx.raw("SELECT pg_advisory_xact_lock(?)", [PgSqlLock.OrgRelayConfigInit(orgId)]); + + const alg = keyAlgorithmToAlgCfg(CertKeyAlgorithm.RSA_2048); + const orgRelayCaCert = new x509.X509Certificate(instanceCAs.orgRelayPkiCaCertificate); + const rootRelayCaCert = new x509.X509Certificate(instanceCAs.rootRelayPkiCaCertificate); + const orgRelayCaSkObj = crypto.nativeCrypto.createPrivateKey({ + key: instanceCAs.orgRelayPkiCaPrivateKey, + format: "der", + type: "pkcs8" + }); + const orgRelayCaPrivateKey = await crypto.nativeCrypto.subtle.importKey( + "pkcs8", + orgRelayCaSkObj.export({ format: "der", type: "pkcs8" }), + alg, + true, + ["sign"] + ); + + // generate org relay client CA + const orgRelayClientCaSerialNumber = createSerialNumber(); + const orgRelayClientCaIssuedAt = new Date(); + const orgRelayClientCaExpiration = new Date(new Date().setFullYear(2045)); + const orgRelayClientCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); + const orgRelayClientCaSkObj = crypto.nativeCrypto.KeyObject.from(orgRelayClientCaKeys.privateKey); + const orgRelayClientCaCert = await x509.X509CertificateGenerator.create({ + serialNumber: orgRelayClientCaSerialNumber, + subject: `O=${orgId},CN=Infisical Org Relay Client CA`, + issuer: orgRelayCaCert.subject, + notBefore: orgRelayClientCaIssuedAt, + notAfter: orgRelayClientCaExpiration, + signingKey: orgRelayCaPrivateKey, + publicKey: orgRelayClientCaKeys.publicKey, + signingAlgorithm: alg, + extensions: [ + new x509.KeyUsagesExtension( + // eslint-disable-next-line no-bitwise + x509.KeyUsageFlags.keyCertSign | + x509.KeyUsageFlags.cRLSign | + x509.KeyUsageFlags.digitalSignature | + x509.KeyUsageFlags.keyEncipherment, + true + ), + new x509.BasicConstraintsExtension(true, 0, true), + await x509.AuthorityKeyIdentifierExtension.create(orgRelayCaCert, false), + await x509.SubjectKeyIdentifierExtension.create(orgRelayClientCaKeys.publicKey) + ] + }); + const orgRelayClientCaChain = constructPemChainFromCerts([orgRelayCaCert, rootRelayCaCert]); + + // generate org SSH CA + const orgSshServerCaKeyPair = await createSshKeyPair(SshCertKeyAlgorithm.RSA_2048); + const orgSshClientCaKeyPair = await createSshKeyPair(SshCertKeyAlgorithm.RSA_2048); + + // generate org relay server CA + const orgRelayServerCaSerialNumber = createSerialNumber(); + const orgRelayServerCaIssuedAt = new Date(); + const orgRelayServerCaExpiration = new Date(new Date().setFullYear(2045)); + const orgRelayServerCaKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); + const orgRelayServerCaSkObj = crypto.nativeCrypto.KeyObject.from(orgRelayServerCaKeys.privateKey); + const orgRelayServerCaCert = await x509.X509CertificateGenerator.create({ + serialNumber: orgRelayServerCaSerialNumber, + subject: `O=${orgId},CN=Infisical Org Relay Server CA`, + issuer: orgRelayCaCert.subject, + notBefore: orgRelayServerCaIssuedAt, + notAfter: orgRelayServerCaExpiration, + signingKey: orgRelayCaPrivateKey, + publicKey: orgRelayServerCaKeys.publicKey, + signingAlgorithm: alg, + extensions: [ + new x509.KeyUsagesExtension( + // eslint-disable-next-line no-bitwise + x509.KeyUsageFlags.keyCertSign | + x509.KeyUsageFlags.cRLSign | + x509.KeyUsageFlags.digitalSignature | + x509.KeyUsageFlags.keyEncipherment, + true + ), + new x509.BasicConstraintsExtension(true, 0, true), + await x509.AuthorityKeyIdentifierExtension.create(orgRelayCaCert, false), + await x509.SubjectKeyIdentifierExtension.create(orgRelayServerCaKeys.publicKey) + ] + }); + const orgRelayServerCaChain = constructPemChainFromCerts([orgRelayCaCert, rootRelayCaCert]); + + const encryptedRelayPkiClientCaPrivateKey = orgKmsEncryptor({ + plainText: Buffer.from( + orgRelayClientCaSkObj.export({ + type: "pkcs8", + format: "der" + }) + ) + }).cipherTextBlob; + const encryptedRelayPkiClientCaCertificate = orgKmsEncryptor({ + plainText: Buffer.from(orgRelayClientCaCert.rawData) + }).cipherTextBlob; + + const encryptedRelayPkiClientCaCertificateChain = orgKmsEncryptor({ + plainText: Buffer.from(orgRelayClientCaChain) + }).cipherTextBlob; + + const encryptedRelayPkiServerCaPrivateKey = orgKmsEncryptor({ + plainText: Buffer.from( + orgRelayServerCaSkObj.export({ + type: "pkcs8", + format: "der" + }) + ) + }).cipherTextBlob; + const encryptedRelayPkiServerCaCertificate = orgKmsEncryptor({ + plainText: Buffer.from(orgRelayServerCaCert.rawData) + }).cipherTextBlob; + const encryptedRelayPkiServerCaCertificateChain = orgKmsEncryptor({ + plainText: Buffer.from(orgRelayServerCaChain) + }).cipherTextBlob; + + const encryptedRelaySshClientCaPublicKey = orgKmsEncryptor({ + plainText: Buffer.from(orgSshClientCaKeyPair.publicKey) + }).cipherTextBlob; + const encryptedRelaySshClientCaPrivateKey = orgKmsEncryptor({ + plainText: Buffer.from(orgSshClientCaKeyPair.privateKey) + }).cipherTextBlob; + + const encryptedRelaySshServerCaPublicKey = orgKmsEncryptor({ + plainText: Buffer.from(orgSshServerCaKeyPair.publicKey) + }).cipherTextBlob; + const encryptedRelaySshServerCaPrivateKey = orgKmsEncryptor({ + plainText: Buffer.from(orgSshServerCaKeyPair.privateKey) + }).cipherTextBlob; + + return orgRelayConfigDAL.create({ + orgId, + encryptedRelayPkiClientCaPrivateKey, + encryptedRelayPkiClientCaCertificate, + encryptedRelayPkiClientCaCertificateChain, + encryptedRelayPkiServerCaPrivateKey, + encryptedRelayPkiServerCaCertificate, + encryptedRelayPkiServerCaCertificateChain, + encryptedRelaySshClientCaPublicKey, + encryptedRelaySshClientCaPrivateKey, + encryptedRelaySshServerCaPublicKey, + encryptedRelaySshServerCaPrivateKey + }); + }); + + const relayPkiClientCaPrivateKey = orgKmsDecryptor({ + cipherTextBlob: orgRelayConfig.encryptedRelayPkiClientCaPrivateKey + }); + const relayPkiClientCaCertificate = orgKmsDecryptor({ + cipherTextBlob: orgRelayConfig.encryptedRelayPkiClientCaCertificate + }); + const relayPkiClientCaCertificateChain = orgKmsDecryptor({ + cipherTextBlob: orgRelayConfig.encryptedRelayPkiClientCaCertificateChain + }); + + const relayPkiServerCaPrivateKey = orgKmsDecryptor({ + cipherTextBlob: orgRelayConfig.encryptedRelayPkiServerCaPrivateKey + }); + const relayPkiServerCaCertificate = orgKmsDecryptor({ + cipherTextBlob: orgRelayConfig.encryptedRelayPkiServerCaCertificate + }); + const relayPkiServerCaCertificateChain = orgKmsDecryptor({ + cipherTextBlob: orgRelayConfig.encryptedRelayPkiServerCaCertificateChain + }); + + const relaySshClientCaPublicKey = orgKmsDecryptor({ + cipherTextBlob: orgRelayConfig.encryptedRelaySshClientCaPublicKey + }); + const relaySshClientCaPrivateKey = orgKmsDecryptor({ + cipherTextBlob: orgRelayConfig.encryptedRelaySshClientCaPrivateKey + }); + + const relaySshServerCaPublicKey = orgKmsDecryptor({ + cipherTextBlob: orgRelayConfig.encryptedRelaySshServerCaPublicKey + }); + const relaySshServerCaPrivateKey = orgKmsDecryptor({ + cipherTextBlob: orgRelayConfig.encryptedRelaySshServerCaPrivateKey + }); + + return { + relayPkiClientCaPrivateKey, + relayPkiClientCaCertificate, + relayPkiClientCaCertificateChain, + relayPkiServerCaPrivateKey, + relayPkiServerCaCertificate, + relayPkiServerCaCertificateChain, + relaySshClientCaPublicKey, + relaySshClientCaPrivateKey, + relaySshServerCaPublicKey, + relaySshServerCaPrivateKey + }; + }; + + const $generateRelayServerCredentials = async ({ + ip, + orgId, + relayPkiServerCaCertificate, + relayPkiServerCaPrivateKey, + relayPkiClientCaCertificate, + relayPkiClientCaCertificateChain, + relaySshClientCaPublicKey, + relaySshServerCaPrivateKey + }: { + ip: string; + relayPkiServerCaCertificate: Buffer; + relayPkiServerCaPrivateKey: Buffer; + relayPkiClientCaCertificateChain: Buffer; + relayPkiClientCaCertificate: Buffer; + relaySshServerCaPrivateKey: Buffer; + relaySshClientCaPublicKey: Buffer; + orgId?: string; + }) => { + const alg = keyAlgorithmToAlgCfg(CertKeyAlgorithm.RSA_2048); + const relayServerCaCert = new x509.X509Certificate(relayPkiServerCaCertificate); + const relayClientCaCert = new x509.X509Certificate(relayPkiClientCaCertificate); + const relayServerCaSkObj = crypto.nativeCrypto.createPrivateKey({ + key: relayPkiServerCaPrivateKey, + format: "der", + type: "pkcs8" + }); + + const relayServerCaPrivateKey = await crypto.nativeCrypto.subtle.importKey( + "pkcs8", + relayServerCaSkObj.export({ format: "der", type: "pkcs8" }), + alg, + true, + ["sign"] + ); + + const relayServerKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); + const relayServerCertIssuedAt = new Date(); + const relayServerCertExpireAt = new Date(new Date().setMonth(new Date().getMonth() + 1)); + const relayServerCertPrivateKey = crypto.nativeCrypto.KeyObject.from(relayServerKeys.privateKey); + + const relayServerCertExtensions: x509.Extension[] = [ + new x509.BasicConstraintsExtension(false), + await x509.AuthorityKeyIdentifierExtension.create(relayServerCaCert, false), + await x509.SubjectKeyIdentifierExtension.create(relayServerKeys.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], + true + ), + new x509.ExtendedKeyUsageExtension([x509.ExtendedKeyUsage[CertExtendedKeyUsage.SERVER_AUTH]], true), + // san + new x509.SubjectAlternativeNameExtension([{ type: "ip", value: ip }], false) + ]; + + const relayServerSerialNumber = createSerialNumber(); + const relayServerCertificate = await x509.X509CertificateGenerator.create({ + serialNumber: relayServerSerialNumber, + subject: `CN=${ip},O=${orgId ?? "Infisical"},OU=Relay`, + issuer: relayServerCaCert.subject, + notBefore: relayServerCertIssuedAt, + notAfter: relayServerCertExpireAt, + signingKey: relayServerCaPrivateKey, + publicKey: relayServerKeys.publicKey, + signingAlgorithm: alg, + extensions: relayServerCertExtensions + }); + + // generate relay server SSH certificate + const keyAlgorithm = SshCertKeyAlgorithm.RSA_2048; + const { publicKey: relayServerSshPublicKey, privateKey: relayServerSshPrivateKey } = + await createSshKeyPair(keyAlgorithm); + + const relayServerSshCert = await createSshCert({ + caPrivateKey: relaySshServerCaPrivateKey.toString("utf8"), + clientPublicKey: relayServerSshPublicKey, + keyId: "relay-server", + principals: [`${ip}:2222`], + certType: SshCertType.HOST, + requestedTtl: "30d" + }); + + return { + pki: { + serverCertificate: relayServerCertificate.toString("pem"), + serverPrivateKey: relayServerCertPrivateKey.export({ format: "pem", type: "pkcs8" }).toString(), + clientCertificateChain: prependCertToPemChain( + relayClientCaCert, + relayPkiClientCaCertificateChain.toString("utf8") + ) + }, + ssh: { + serverCertificate: relayServerSshCert.signedPublicKey, + serverPrivateKey: relayServerSshPrivateKey, + clientCAPublicKey: relaySshClientCaPublicKey.toString("utf8") + } + }; + }; + + const $generateRelayClientCredentials = async ({ + gatewayId, + orgId, + relayPkiClientCaCertificate, + relayPkiClientCaPrivateKey, + relayPkiServerCaCertificate, + relayPkiServerCaCertificateChain + }: { + gatewayId: string; + orgId: string; + relayPkiClientCaCertificate: Buffer; + relayPkiClientCaPrivateKey: Buffer; + relayPkiServerCaCertificate: Buffer; + relayPkiServerCaCertificateChain: Buffer; + }) => { + const alg = keyAlgorithmToAlgCfg(CertKeyAlgorithm.RSA_2048); + const relayClientCaCert = new x509.X509Certificate(relayPkiClientCaCertificate); + const relayServerCaCert = new x509.X509Certificate(relayPkiServerCaCertificate); + const relayClientCaSkObj = crypto.nativeCrypto.createPrivateKey({ + key: relayPkiClientCaPrivateKey, + format: "der", + type: "pkcs8" + }); + + const importedRelayClientCaPrivateKey = await crypto.nativeCrypto.subtle.importKey( + "pkcs8", + relayClientCaSkObj.export({ format: "der", type: "pkcs8" }), + alg, + true, + ["sign"] + ); + + const clientCertIssuedAt = new Date(); + const clientCertExpiration = new Date(new Date().getTime() + 5 * 60 * 1000); + const clientKeys = await crypto.nativeCrypto.subtle.generateKey(alg, true, ["sign", "verify"]); + const clientCertPrivateKey = crypto.nativeCrypto.KeyObject.from(clientKeys.privateKey); + const clientCertSerialNumber = createSerialNumber(); + + // Build standard extensions + const extensions: x509.Extension[] = [ + new x509.BasicConstraintsExtension(false), + await x509.AuthorityKeyIdentifierExtension.create(relayClientCaCert, 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({ + serialNumber: clientCertSerialNumber, + subject: `O=${orgId},OU=relay-client,CN=${gatewayId}`, + issuer: relayClientCaCert.subject, + notAfter: clientCertExpiration, + notBefore: clientCertIssuedAt, + signingKey: importedRelayClientCaPrivateKey, + publicKey: clientKeys.publicKey, + signingAlgorithm: alg, + extensions + }); + + return { + clientCertificate: clientCert.toString("pem"), + clientPrivateKey: clientCertPrivateKey.export({ format: "pem", type: "pkcs8" }).toString(), + serverCertificateChain: prependCertToPemChain( + relayServerCaCert, + relayPkiServerCaCertificateChain.toString("utf8") + ) + }; + }; + + const getCredentialsForGateway = async ({ + relayName, + orgId, + gatewayId + }: { + relayName: string; + orgId: string; + gatewayId: string; + }) => { + let relay: TRelays | null; + if (isInstanceRelay(relayName)) { + relay = await relayDAL.findOne({ + name: relayName + }); + } else { + relay = await relayDAL.findOne({ + orgId, + name: relayName + }); + } + + if (!relay) { + throw new NotFoundError({ + message: "Relay not found" + }); + } + + const keyAlgorithm = SshCertKeyAlgorithm.RSA_2048; + const { publicKey: relayClientSshPublicKey, privateKey: relayClientSshPrivateKey } = + await createSshKeyPair(keyAlgorithm); + + if (isInstanceRelay(relayName)) { + const instanceCAs = await $getInstanceCAs(); + const relayClientSshCert = await createSshCert({ + caPrivateKey: instanceCAs.instanceRelaySshClientCaPrivateKey.toString("utf8"), + clientPublicKey: relayClientSshPublicKey, + keyId: `client-${relayName}`, + principals: [gatewayId], + certType: SshCertType.USER, + requestedTtl: "30d" + }); + + return { + relayIp: relay.ip, + clientSshCert: relayClientSshCert.signedPublicKey, + clientSshPrivateKey: relayClientSshPrivateKey, + serverCAPublicKey: instanceCAs.instanceRelaySshServerCaPublicKey.toString("utf8") + }; + } + + const orgCAs = await $getOrgCAs(orgId); + const relayClientSshCert = await createSshCert({ + caPrivateKey: orgCAs.relaySshClientCaPrivateKey.toString("utf8"), + clientPublicKey: relayClientSshPublicKey, + keyId: `relay-client-${relay.id}`, + principals: [gatewayId], + certType: SshCertType.USER, + requestedTtl: "30d" + }); + + return { + relayIp: relay.ip, + clientSshCert: relayClientSshCert.signedPublicKey, + clientSshPrivateKey: relayClientSshPrivateKey, + serverCAPublicKey: orgCAs.relaySshServerCaPublicKey.toString("utf8") + }; + }; + + const getCredentialsForClient = async ({ + relayId, + orgId, + gatewayId + }: { + relayId: string; + orgId: string; + gatewayId: string; + }) => { + const relay = await relayDAL.findOne({ + id: relayId + }); + + if (!relay) { + throw new NotFoundError({ + message: "Relay not found" + }); + } + + if (isInstanceRelay(relay.name)) { + const instanceCAs = await $getInstanceCAs(); + const relayCertificateCredentials = await $generateRelayClientCredentials({ + gatewayId, + orgId, + relayPkiClientCaCertificate: instanceCAs.instanceRelayPkiClientCaCertificate, + relayPkiClientCaPrivateKey: instanceCAs.instanceRelayPkiClientCaPrivateKey, + relayPkiServerCaCertificate: instanceCAs.instanceRelayPkiServerCaCertificate, + relayPkiServerCaCertificateChain: instanceCAs.instanceRelayPkiServerCaCertificateChain + }); + + return { + ...relayCertificateCredentials, + relayIp: relay.ip + }; + } + + const orgCAs = await $getOrgCAs(orgId); + const relayCertificateCredentials = await $generateRelayClientCredentials({ + gatewayId, + orgId, + relayPkiClientCaCertificate: orgCAs.relayPkiClientCaCertificate, + relayPkiClientCaPrivateKey: orgCAs.relayPkiClientCaPrivateKey, + relayPkiServerCaCertificate: orgCAs.relayPkiServerCaCertificate, + relayPkiServerCaCertificateChain: orgCAs.relayPkiServerCaCertificateChain + }); + + return { + ...relayCertificateCredentials, + relayIp: relay.ip + }; + }; + + const registerRelay = async ({ + ip, + name, + identityId, + orgId + }: { + ip: string; + name: string; + identityId?: string; + orgId?: string; + }) => { + let relay: TRelays; + const isOrgRelay = identityId && orgId; + + if (isOrgRelay) { + if (isInstanceRelay(name)) { + throw new BadRequestError({ + message: "Org relay name cannot start with 'infisical-'. This is reserved for internal use." + }); + } + + relay = await relayDAL.transaction(async (tx) => { + const existingRelay = await relayDAL.findOne( + { + identityId, + orgId + }, + tx + ); + + if (existingRelay && (existingRelay.ip !== ip || existingRelay.name !== name)) { + throw new BadRequestError({ + message: "Org relay with this machine identity already exists." + }); + } + + if (!existingRelay) { + return relayDAL.create( + { + ip, + name, + identityId, + orgId + }, + tx + ); + } + + return existingRelay; + }); + } else { + if (!isInstanceRelay(name)) { + throw new BadRequestError({ + message: "Instance relay name must start with 'infisical-'." + }); + } + + relay = await relayDAL.transaction(async (tx) => { + const existingRelay = await relayDAL.findOne( + { + name + }, + tx + ); + + if (existingRelay && existingRelay.ip !== ip) { + throw new BadRequestError({ + message: "Instance relay with this name already exists with a different IP address" + }); + } + + if (!existingRelay) { + return relayDAL.create( + { + ip, + name + }, + tx + ); + } + + return existingRelay; + }); + } + + if (isInstanceRelay(name)) { + const instanceCAs = await $getInstanceCAs(); + return $generateRelayServerCredentials({ + ip, + relayPkiServerCaCertificate: instanceCAs.instanceRelayPkiServerCaCertificate, + relayPkiServerCaPrivateKey: instanceCAs.instanceRelayPkiServerCaPrivateKey, + relayPkiClientCaCertificate: instanceCAs.instanceRelayPkiClientCaCertificate, + relayPkiClientCaCertificateChain: instanceCAs.instanceRelayPkiClientCaCertificateChain, + relaySshServerCaPrivateKey: instanceCAs.instanceRelaySshServerCaPrivateKey, + relaySshClientCaPublicKey: instanceCAs.instanceRelaySshClientCaPublicKey + }); + } + + if (relay.orgId) { + const orgCAs = await $getOrgCAs(relay.orgId); + return $generateRelayServerCredentials({ + ip, + orgId: relay.orgId, + relayPkiServerCaCertificate: orgCAs.relayPkiServerCaCertificate, + relayPkiServerCaPrivateKey: orgCAs.relayPkiServerCaPrivateKey, + relayPkiClientCaCertificate: orgCAs.relayPkiClientCaCertificate, + relayPkiClientCaCertificateChain: orgCAs.relayPkiClientCaCertificateChain, + relaySshServerCaPrivateKey: orgCAs.relaySshServerCaPrivateKey, + relaySshClientCaPublicKey: orgCAs.relaySshClientCaPublicKey + }); + } + + throw new BadRequestError({ + message: "Unhandled relay type" + }); + }; + + return { + registerRelay, + getCredentialsForGateway, + getCredentialsForClient + }; +}; diff --git a/backend/src/keystore/keystore.ts b/backend/src/keystore/keystore.ts index d7a28da96..13d0b1d1e 100644 --- a/backend/src/keystore/keystore.ts +++ b/backend/src/keystore/keystore.ts @@ -14,9 +14,9 @@ export const PgSqlLock = { CreateProject: (orgId: string) => pgAdvisoryLockHashText(`create-project:${orgId}`), CreateFolder: (envId: string, projectId: string) => pgAdvisoryLockHashText(`create-folder:${envId}-${projectId}`), SshInit: (projectId: string) => pgAdvisoryLockHashText(`ssh-bootstrap:${projectId}`), - InstanceProxyConfigInit: () => pgAdvisoryLockHashText("instance-proxy-config-init"), + InstanceRelayConfigInit: () => pgAdvisoryLockHashText("instance-relay-config-init"), OrgGatewayV2Init: (orgId: string) => pgAdvisoryLockHashText(`org-gateway-v2-init:${orgId}`), - OrgProxyConfigInit: (orgId: string) => pgAdvisoryLockHashText(`org-proxy-config-init:${orgId}`), + OrgRelayConfigInit: (orgId: string) => pgAdvisoryLockHashText(`org-relay-config-init:${orgId}`), IdentityLogin: (identityId: string, nonce: string) => pgAdvisoryLockHashText(`identity-login:${identityId}:${nonce}`) } as const; diff --git a/backend/src/lib/config/env.ts b/backend/src/lib/config/env.ts index 6e926b21e..2d8781305 100644 --- a/backend/src/lib/config/env.ts +++ b/backend/src/lib/config/env.ts @@ -233,7 +233,7 @@ const envSchema = z GATEWAY_RELAY_REALM: zpStr(z.string().optional()), GATEWAY_RELAY_AUTH_SECRET: zpStr(z.string().optional()), - PROXY_AUTH_SECRET: zpStr(z.string().optional()), + RELAY_AUTH_SECRET: zpStr(z.string().optional()), DYNAMIC_SECRET_ALLOW_INTERNAL_IP: zodStrBool.default("false"), DYNAMIC_SECRET_AWS_ACCESS_KEY_ID: zpStr(z.string().optional()).default( diff --git a/backend/src/lib/gateway-v2/gateway-v2.ts b/backend/src/lib/gateway-v2/gateway-v2.ts index b8623095f..e41560e86 100644 --- a/backend/src/lib/gateway-v2/gateway-v2.ts +++ b/backend/src/lib/gateway-v2/gateway-v2.ts @@ -11,26 +11,26 @@ import { BadRequestError } from "../errors"; import { GatewayProxyProtocol } from "../gateway/types"; import { logger } from "../logger"; -interface IGatewayProxyServer { +interface IGatewayRelayServer { server: net.Server; port: number; cleanup: () => Promise; - getProxyError: () => string; + getRelayError: () => string; } -const createProxyConnection = async ({ - proxyIp, +const createRelayConnection = async ({ + relayIp, clientCertificate, clientPrivateKey, serverCertificateChain }: { - proxyIp: string; + relayIp: string; clientCertificate: string; clientPrivateKey: string; serverCertificateChain: string; }): Promise => { - const [targetHost] = await verifyHostInputValidity(proxyIp); - const [, portStr] = proxyIp.split(":"); + const [targetHost] = await verifyHostInputValidity(relayIp); + const [, portStr] = relayIp.split(":"); const port = parseInt(portStr, 10) || 8443; const serverCAs = splitPemChain(serverCertificateChain); @@ -47,7 +47,7 @@ const createProxyConnection = async ({ return new Promise((resolve, reject) => { try { const socket = tls.connect(tlsOptions, () => { - logger.info("Proxy TLS connection established successfully"); + logger.info("Relay TLS connection established successfully"); resolve(socket); }); @@ -75,11 +75,11 @@ const createProxyConnection = async ({ }; const createGatewayConnection = async ( - proxyConn: net.Socket, + relayConn: net.Socket, gateway: { clientCertificate: string; clientPrivateKey: string; serverCertificateChain: string } ): Promise => { const tlsOptions: tls.ConnectionOptions = { - socket: proxyConn, + socket: relayConn, cert: gateway.clientCertificate, key: gateway.clientPrivateKey, ca: splitPemChain(gateway.serverCertificateChain), @@ -119,20 +119,20 @@ const createGatewayConnection = async ( }); }; -const setupProxyServer = async ({ +const setupRelayServer = async ({ protocol, - proxyIp, + relayIp, gateway, - proxy, + relay, httpsAgent }: { protocol: GatewayProxyProtocol; - proxyIp: string; + relayIp: string; gateway: { clientCertificate: string; clientPrivateKey: string; serverCertificateChain: string }; - proxy: { clientCertificate: string; clientPrivateKey: string; serverCertificateChain: string }; + relay: { clientCertificate: string; clientPrivateKey: string; serverCertificateChain: string }; httpsAgent?: https.Agent; -}): Promise => { - const proxyErrorMsg: string[] = []; +}): Promise => { + const relayErrorMsg: string[] = []; return new Promise((resolve, reject) => { const server = net.createServer(); @@ -143,16 +143,16 @@ const setupProxyServer = async ({ 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 1: Connect to relay with TLS + const relayConn = await createRelayConnection({ + relayIp, + clientCertificate: relay.clientCertificate, + clientPrivateKey: relay.clientPrivateKey, + serverCertificateChain: relay.serverCertificateChain }); - // Stage 2: Establish mTLS connection to gateway through the proxy - const gatewayConn = await createGatewayConnection(proxyConn, gateway); + // Stage 2: Establish mTLS connection to gateway through the relay + const gatewayConn = await createGatewayConnection(relayConn, gateway); let command = ""; @@ -191,22 +191,22 @@ const setupProxyServer = async ({ // Handle connection closure clientConn.on("close", () => { - proxyConn.destroy(); + relayConn.destroy(); gatewayConn.destroy(); }); - proxyConn.on("close", () => { + relayConn.on("close", () => { clientConn.destroy(); gatewayConn.destroy(); }); gatewayConn.on("close", () => { clientConn.destroy(); - proxyConn.destroy(); + relayConn.destroy(); }); } catch (err) { const errorMsg = err instanceof Error ? err.message : String(err); - proxyErrorMsg.push(errorMsg); + relayErrorMsg.push(errorMsg); clientConn.destroy(); } })(); @@ -234,7 +234,7 @@ const setupProxyServer = async ({ logger.debug("Error closing server:", err instanceof Error ? err.message : String(err)); } }, - getProxyError: () => proxyErrorMsg.join(",") + getRelayError: () => relayErrorMsg.join(",") }); }); }); @@ -244,19 +244,19 @@ export const withGatewayV2Proxy = async ( callback: (port: number) => Promise, options: { protocol: GatewayProxyProtocol; - proxyIp: string; + relayIp: string; gateway: { clientCertificate: string; clientPrivateKey: string; serverCertificateChain: string }; - proxy: { clientCertificate: string; clientPrivateKey: string; serverCertificateChain: string }; + relay: { clientCertificate: string; clientPrivateKey: string; serverCertificateChain: string }; httpsAgent?: https.Agent; } ): Promise => { - const { protocol, proxyIp, gateway, proxy, httpsAgent } = options; + const { protocol, relayIp, gateway, relay, httpsAgent } = options; - const { port, cleanup, getProxyError } = await setupProxyServer({ + const { port, cleanup, getRelayError } = await setupRelayServer({ protocol, - proxyIp, + relayIp, gateway, - proxy, + relay, httpsAgent }); @@ -264,12 +264,12 @@ export const withGatewayV2Proxy = async ( // Execute the callback with the allocated port return await callback(port); } catch (err) { - const proxyErrorMessage = getProxyError(); - if (proxyErrorMessage) { - logger.error("Proxy error:", proxyErrorMessage); + const relayErrorMessage = getRelayError(); + if (relayErrorMessage) { + logger.error("Relay error:", relayErrorMessage); } logger.error("Gateway error:", err instanceof Error ? err.message : String(err)); - let errorMessage = proxyErrorMessage || (err instanceof Error ? err.message : String(err)); + let errorMessage = relayErrorMessage || (err instanceof Error ? err.message : String(err)); if (axios.isAxiosError(err) && (err.response?.data as { message?: string })?.message) { errorMessage = (err.response?.data as { message: string }).message; } diff --git a/backend/src/server/plugins/auth/inject-identity.ts b/backend/src/server/plugins/auth/inject-identity.ts index 0126a4129..a24a09c81 100644 --- a/backend/src/server/plugins/auth/inject-identity.ts +++ b/backend/src/server/plugins/auth/inject-identity.ts @@ -122,7 +122,7 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => { } // Authentication is handled on a route-level - if (req.url === "/api/v1/proxies/register-instance-proxy") { + if (req.url === "/api/v1/proxies/register-instance-relay") { return; } diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index 66718b78f..a20099d66 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -73,12 +73,12 @@ import { projectTemplateDALFactory } from "@app/ee/services/project-template/pro import { projectTemplateServiceFactory } from "@app/ee/services/project-template/project-template-service"; import { projectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal"; import { projectUserAdditionalPrivilegeServiceFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-service"; -import { instanceProxyConfigDalFactory } from "@app/ee/services/proxy/instance-proxy-config-dal"; -import { orgProxyConfigDalFactory } from "@app/ee/services/proxy/org-proxy-config-dal"; -import { proxyDalFactory } from "@app/ee/services/proxy/proxy-dal"; -import { proxyServiceFactory } from "@app/ee/services/proxy/proxy-service"; import { rateLimitDALFactory } from "@app/ee/services/rate-limit/rate-limit-dal"; import { rateLimitServiceFactory } from "@app/ee/services/rate-limit/rate-limit-service"; +import { instanceRelayConfigDalFactory } from "@app/ee/services/relay/instance-relay-config-dal"; +import { orgRelayConfigDalFactory } from "@app/ee/services/relay/org-relay-config-dal"; +import { relayDalFactory } from "@app/ee/services/relay/relay-dal"; +import { relayServiceFactory } from "@app/ee/services/relay/relay-service"; import { samlConfigDALFactory } from "@app/ee/services/saml-config/saml-config-dal"; import { samlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-service"; import { scimDALFactory } from "@app/ee/services/scim/scim-dal"; @@ -948,9 +948,9 @@ export const registerRoutes = async ( const pkiSubscriberDAL = pkiSubscriberDALFactory(db); const pkiTemplatesDAL = pkiTemplatesDALFactory(db); - const instanceProxyConfigDAL = instanceProxyConfigDalFactory(db); - const orgProxyConfigDAL = orgProxyConfigDalFactory(db); - const proxyDAL = proxyDalFactory(db); + const instanceRelayConfigDAL = instanceRelayConfigDalFactory(db); + const orgRelayConfigDAL = orgRelayConfigDalFactory(db); + const relayDAL = relayDalFactory(db); const gatewayV2DAL = gatewayV2DalFactory(db); const orgGatewayConfigV2DAL = orgGatewayConfigV2DalFactory(db); @@ -1073,20 +1073,20 @@ export const registerRoutes = async ( keyStore }); - const proxyService = proxyServiceFactory({ - instanceProxyConfigDAL, - orgProxyConfigDAL, - proxyDAL, + const relayService = relayServiceFactory({ + instanceRelayConfigDAL, + orgRelayConfigDAL, + relayDAL, kmsService }); const gatewayV2Service = gatewayV2ServiceFactory({ kmsService, licenseService, - proxyService, + relayService, orgGatewayConfigV2DAL, gatewayV2DAL, - proxyDAL, + relayDAL, permissionService }); @@ -2138,7 +2138,7 @@ export const registerRoutes = async ( reminder: reminderService, bus: eventBusService, sse: sseService, - proxy: proxyService, + relay: relayService, gatewayV2: gatewayV2Service }); diff --git a/backend/src/services/app-connection/github/github-connection-fns.ts b/backend/src/services/app-connection/github/github-connection-fns.ts index f55fb0eb2..8ed6afad0 100644 --- a/backend/src/services/app-connection/github/github-connection-fns.ts +++ b/backend/src/services/app-connection/github/github-connection-fns.ts @@ -105,9 +105,9 @@ export const requestWithGitHubGateway = async ( }, { protocol: GatewayProxyProtocol.Tcp, - proxyIp: gatewayConnectionDetails.proxyIp, + relayIp: gatewayConnectionDetails.relayIp, gateway: gatewayConnectionDetails.gateway, - proxy: gatewayConnectionDetails.proxy + relay: gatewayConnectionDetails.relay } ); } diff --git a/backend/src/services/app-connection/shared/sql/sql-connection-fns.ts b/backend/src/services/app-connection/shared/sql/sql-connection-fns.ts index 636d59de5..eb0609d5b 100644 --- a/backend/src/services/app-connection/shared/sql/sql-connection-fns.ts +++ b/backend/src/services/app-connection/shared/sql/sql-connection-fns.ts @@ -142,9 +142,9 @@ export const executeWithPotentialGateway = async ( }, { protocol: GatewayProxyProtocol.Tcp, - proxyIp: platformConnectionDetails.proxyIp, + relayIp: platformConnectionDetails.relayIp, gateway: platformConnectionDetails.gateway, - proxy: platformConnectionDetails.proxy + relay: platformConnectionDetails.relay } ); } diff --git a/backend/src/services/identity-kubernetes-auth/identity-kubernetes-auth-service.ts b/backend/src/services/identity-kubernetes-auth/identity-kubernetes-auth-service.ts index 6b0955dc8..97e86e8fd 100644 --- a/backend/src/services/identity-kubernetes-auth/identity-kubernetes-auth-service.ts +++ b/backend/src/services/identity-kubernetes-auth/identity-kubernetes-auth-service.ts @@ -114,9 +114,9 @@ export const identityKubernetesAuthServiceFactory = ({ }, { protocol: inputs.reviewTokenThroughGateway ? GatewayProxyProtocol.Http : GatewayProxyProtocol.Tcp, - proxyIp: gatewayV2ConnectionDetails.proxyIp, + relayIp: gatewayV2ConnectionDetails.relayIp, gateway: gatewayV2ConnectionDetails.gateway, - proxy: gatewayV2ConnectionDetails.proxy, + relay: gatewayV2ConnectionDetails.relay, httpsAgent } );