misc: addressed comments

This commit is contained in:
Sheen Capadngan
2025-09-11 00:45:27 +08:00
parent 1dcdb90c62
commit 71ff01daf2
9 changed files with 105 additions and 38 deletions
+3 -2
View File
@@ -4,6 +4,7 @@ import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography"; import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { writeLimit } from "@app/server/config/rateLimiter"; import { writeLimit } from "@app/server/config/rateLimiter";
import { slugSchema } from "@app/server/lib/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -19,7 +20,7 @@ export const registerRelayRouter = async (server: FastifyZodProvider) => {
schema: { schema: {
body: z.object({ body: z.object({
host: z.string(), host: z.string(),
name: z.string() name: slugSchema({ min: 1, max: 32, field: "name" })
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -69,7 +70,7 @@ export const registerRelayRouter = async (server: FastifyZodProvider) => {
schema: { schema: {
body: z.object({ body: z.object({
host: z.string(), host: z.string(),
name: z.string() name: slugSchema({ min: 1, max: 32, field: "name" })
}), }),
response: { response: {
200: z.object({ 200: z.object({
+3 -2
View File
@@ -2,6 +2,7 @@ import z from "zod";
import { GatewaysV2Schema } from "@app/db/schemas"; import { GatewaysV2Schema } from "@app/db/schemas";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { slugSchema } from "@app/server/lib/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -20,8 +21,8 @@ export const registerGatewayV2Router = async (server: FastifyZodProvider) => {
url: "/", url: "/",
schema: { schema: {
body: z.object({ body: z.object({
relayName: z.string(), relayName: slugSchema({ min: 1, max: 32, field: "relayName" }),
name: z.string() name: slugSchema({ min: 1, max: 32, field: "name" })
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -1,3 +1,5 @@
import { Knex } from "knex";
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { GatewaysV2Schema, TableName, TGatewaysV2 } from "@app/db/schemas"; import { GatewaysV2Schema, TableName, TGatewaysV2 } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors"; import { DatabaseError } from "@app/lib/errors";
@@ -10,7 +12,7 @@ export const gatewayV2DalFactory = (db: TDbClient) => {
const find = async (filter: TFindFilter<TGatewaysV2>, { offset, limit, sort, tx }: TFindOpt<TGatewaysV2> = {}) => { const find = async (filter: TFindFilter<TGatewaysV2>, { offset, limit, sort, tx }: TFindOpt<TGatewaysV2> = {}) => {
try { try {
const query = (tx || db)(TableName.GatewayV2) const query = (tx || db.replicaNode())(TableName.GatewayV2)
// eslint-disable-next-line @typescript-eslint/no-misused-promises // eslint-disable-next-line @typescript-eslint/no-misused-promises
.where(buildFindFilter(filter, TableName.GatewayV2)) .where(buildFindFilter(filter, TableName.GatewayV2))
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.GatewayV2}.identityId`) .join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.GatewayV2}.identityId`)
@@ -39,5 +41,20 @@ export const gatewayV2DalFactory = (db: TDbClient) => {
} }
}; };
return { ...orm, find }; const findById = async (id: string, tx?: Knex) => {
try {
const doc = await (tx || db.replicaNode())(TableName.GatewayV2)
.join(TableName.Organization, `${TableName.GatewayV2}.orgId`, `${TableName.Organization}.id`)
.where(`${TableName.GatewayV2}.id`, id)
.select(selectAllTableCols(TableName.GatewayV2))
.select(db.ref("name").withSchema(TableName.Organization).as("orgName"))
.first();
return doc;
} catch (error) {
throw new DatabaseError({ error, name: `${TableName.GatewayV2}: Find by id` });
}
};
return { ...orm, find, findById };
}; };
@@ -394,6 +394,7 @@ export const gatewayV2ServiceFactory = ({
const relayCredentials = await relayService.getCredentialsForClient({ const relayCredentials = await relayService.getCredentialsForClient({
relayId: gateway.relayId, relayId: gateway.relayId,
orgId: gateway.orgId, orgId: gateway.orgId,
orgName: gateway.orgName,
gatewayId gatewayId
}); });
+14 -7
View File
@@ -13,6 +13,7 @@ import {
import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { KmsDataKey } from "@app/services/kms/kms-types"; import { KmsDataKey } from "@app/services/kms/kms-types";
import { verifyHostInputValidity } from "../dynamic-secret/dynamic-secret-fns";
import { createSshCert, createSshKeyPair } from "../ssh/ssh-certificate-authority-fns"; import { createSshCert, createSshKeyPair } from "../ssh/ssh-certificate-authority-fns";
import { SshCertType } from "../ssh/ssh-certificate-authority-types"; import { SshCertType } from "../ssh/ssh-certificate-authority-types";
import { SshCertKeyAlgorithm } from "../ssh-certificate/ssh-certificate-types"; import { SshCertKeyAlgorithm } from "../ssh-certificate/ssh-certificate-types";
@@ -689,6 +690,7 @@ export const relayServiceFactory = ({
const $generateRelayClientCredentials = async ({ const $generateRelayClientCredentials = async ({
gatewayId, gatewayId,
orgId, orgId,
orgName,
relayPkiClientCaCertificate, relayPkiClientCaCertificate,
relayPkiClientCaPrivateKey, relayPkiClientCaPrivateKey,
relayPkiServerCaCertificate, relayPkiServerCaCertificate,
@@ -696,6 +698,7 @@ export const relayServiceFactory = ({
}: { }: {
gatewayId: string; gatewayId: string;
orgId: string; orgId: string;
orgName: string;
relayPkiClientCaCertificate: Buffer; relayPkiClientCaCertificate: Buffer;
relayPkiClientCaPrivateKey: Buffer; relayPkiClientCaPrivateKey: Buffer;
relayPkiServerCaCertificate: Buffer; relayPkiServerCaCertificate: Buffer;
@@ -742,7 +745,7 @@ export const relayServiceFactory = ({
const clientCert = await x509.X509CertificateGenerator.create({ const clientCert = await x509.X509CertificateGenerator.create({
serialNumber: clientCertSerialNumber, serialNumber: clientCertSerialNumber,
subject: `O=${orgId},OU=relay-client,CN=${gatewayId}`, subject: `O=${orgName}-${orgId},OU=relay-client,CN=${gatewayId}`,
issuer: relayClientCaCert.subject, issuer: relayClientCaCert.subject,
notAfter: clientCertExpiration, notAfter: clientCertExpiration,
notBefore: clientCertIssuedAt, notBefore: clientCertIssuedAt,
@@ -833,10 +836,12 @@ export const relayServiceFactory = ({
const getCredentialsForClient = async ({ const getCredentialsForClient = async ({
relayId, relayId,
orgId, orgId,
orgName,
gatewayId gatewayId
}: { }: {
relayId: string; relayId: string;
orgId: string; orgId: string;
orgName: string;
gatewayId: string; gatewayId: string;
}) => { }) => {
const relay = await relayDAL.findOne({ const relay = await relayDAL.findOne({
@@ -849,11 +854,14 @@ export const relayServiceFactory = ({
}); });
} }
await verifyHostInputValidity(relay.host);
if (relay.orgId === null) { if (relay.orgId === null) {
const instanceCAs = await $getInstanceCAs(); const instanceCAs = await $getInstanceCAs();
const relayCertificateCredentials = await $generateRelayClientCredentials({ const relayCertificateCredentials = await $generateRelayClientCredentials({
gatewayId, gatewayId,
orgId, orgId,
orgName,
relayPkiClientCaCertificate: instanceCAs.instanceRelayPkiClientCaCertificate, relayPkiClientCaCertificate: instanceCAs.instanceRelayPkiClientCaCertificate,
relayPkiClientCaPrivateKey: instanceCAs.instanceRelayPkiClientCaPrivateKey, relayPkiClientCaPrivateKey: instanceCAs.instanceRelayPkiClientCaPrivateKey,
relayPkiServerCaCertificate: instanceCAs.instanceRelayPkiServerCaCertificate, relayPkiServerCaCertificate: instanceCAs.instanceRelayPkiServerCaCertificate,
@@ -870,6 +878,7 @@ export const relayServiceFactory = ({
const relayCertificateCredentials = await $generateRelayClientCredentials({ const relayCertificateCredentials = await $generateRelayClientCredentials({
gatewayId, gatewayId,
orgId, orgId,
orgName,
relayPkiClientCaCertificate: orgCAs.relayPkiClientCaCertificate, relayPkiClientCaCertificate: orgCAs.relayPkiClientCaCertificate,
relayPkiClientCaPrivateKey: orgCAs.relayPkiClientCaPrivateKey, relayPkiClientCaPrivateKey: orgCAs.relayPkiClientCaPrivateKey,
relayPkiServerCaCertificate: orgCAs.relayPkiServerCaCertificate, relayPkiServerCaCertificate: orgCAs.relayPkiServerCaCertificate,
@@ -896,6 +905,8 @@ export const relayServiceFactory = ({
let relay: TRelays; let relay: TRelays;
const isOrgRelay = identityId && orgId; const isOrgRelay = identityId && orgId;
await verifyHostInputValidity(host);
if (isOrgRelay) { if (isOrgRelay) {
relay = await relayDAL.transaction(async (tx) => { relay = await relayDAL.transaction(async (tx) => {
const existingRelay = await relayDAL.findOne( const existingRelay = await relayDAL.findOne(
@@ -907,9 +918,7 @@ export const relayServiceFactory = ({
); );
if (existingRelay && (existingRelay.host !== host || existingRelay.name !== name)) { if (existingRelay && (existingRelay.host !== host || existingRelay.name !== name)) {
throw new BadRequestError({ return relayDAL.updateById(existingRelay.id, { host, name }, tx);
message: "Org relay with this machine identity already exists."
});
} }
if (!existingRelay) { if (!existingRelay) {
@@ -937,9 +946,7 @@ export const relayServiceFactory = ({
); );
if (existingRelay && existingRelay.host !== host) { if (existingRelay && existingRelay.host !== host) {
throw new BadRequestError({ return relayDAL.updateById(existingRelay.id, { host }, tx);
message: "Instance relay with this name already exists with a different host"
});
} }
if (!existingRelay) { if (!existingRelay) {
+13
View File
@@ -22,6 +22,13 @@ The Infisical gateway provides secure access to private resources using modern T
The gateway system uses SSH reverse tunnels over TCP, eliminating firewall complexity and providing excellent performance for enterprise environments. The gateway system uses SSH reverse tunnels over TCP, eliminating firewall complexity and providing excellent performance for enterprise environments.
<Warning>
**Deprecation and Migration Notice:** The legacy `infisical gateway` command (v1) will be removed in a future release. Please migrate to `infisical gateway start` (Gateway v2).
If you are moving from Gateway v1 to Gateway v2, this is NOT a drop-in switch. Gateway v2 creates new gateway instances with new gateway IDs. You must update any existing resources that reference gateway IDs (for example: dynamic secret configs, app connections, or other gateway-bound resources) to point to the new Gateway v2 gateway ID. Until you update those references, traffic will continue to target the old v1 gateway.
</Warning>
## Subcommands & flags ## Subcommands & flags
<Accordion title="infisical gateway start" defaultOpen="true"> <Accordion title="infisical gateway start" defaultOpen="true">
@@ -361,6 +368,9 @@ sudo systemctl disable infisical-gateway # Disable auto-start on boot
**This command is deprecated and will be removed in a future release.** **This command is deprecated and will be removed in a future release.**
Please migrate to `infisical gateway start` for the new TCP-based SSH tunnel architecture. Please migrate to `infisical gateway start` for the new TCP-based SSH tunnel architecture.
**Migration required:** If you are currently using Gateway v1 (via `infisical gateway`), moving to Gateway v2 is not in-place. Gateway v2 provisions new gateway instances with new gateway IDs. Update any resources that reference a gateway ID (for example: dynamic secret configs, app connections, or other gateway-bound resources) to use the new Gateway v2 gateway ID. Until you update those references, traffic will continue to target the old v1 gateway.
</Warning> </Warning>
Run the legacy Infisical gateway in the foreground. The gateway will connect to the relay service and maintain a persistent connection. Run the legacy Infisical gateway in the foreground. The gateway will connect to the relay service and maintain a persistent connection.
@@ -585,6 +595,9 @@ The Infisical CLI supports multiple authentication methods. Below are the availa
**This command is deprecated and will be removed in a future release.** **This command is deprecated and will be removed in a future release.**
Please migrate to `infisical gateway systemd install` for the new TCP-based SSH tunnel architecture with enhanced security and better performance. Please migrate to `infisical gateway systemd install` for the new TCP-based SSH tunnel architecture with enhanced security and better performance.
**Migration required:** If you previously installed Gateway v1 via `infisical gateway install`, moving to Gateway v2 is not in-place. Gateway v2 provisions new gateway instances with new gateway IDs. Update any resources that reference a gateway ID (for example: dynamic secret configs, app connections, or other gateway-bound resources) to use the new Gateway v2 gateway ID. Until you update those references, traffic will continue to target the old v1 gateway.
</Warning> </Warning>
Install and enable the legacy gateway as a systemd service. This command must be run with sudo on Linux. Install and enable the legacy gateway as a systemd service. This command must be run with sudo on Linux.
+5 -5
View File
@@ -1,6 +1,6 @@
--- ---
title: "infisical relay" title: "infisical relay"
description: "Relay-related commands for Infisical including proxy components" description: "Relay-related commands for Infisical"
--- ---
<Tabs> <Tabs>
@@ -33,7 +33,7 @@ infisical relay start --type=<type> --host=<host> --name=<name> --auth-method=<a
<Accordion title="--type"> <Accordion title="--type">
The type of relay to run. Must be either 'instance' or 'org'. The type of relay to run. Must be either 'instance' or 'org'.
- **`instance`**: Shared relay server that can be used by all organizations on your Infisical instance. Set up by the instance administrator. Uses `INFISICAL_PROXY_AUTH_SECRET` environment variable for authentication, which must be configured by the instance admin. - **`instance`**: Shared relay server that can be used by all organizations on your Infisical instance. Set up by the instance administrator. Uses `INFISICAL_RELAY_AUTH_SECRET` environment variable for authentication, which must be configured by the instance admin.
- **`org`**: Dedicated relay server that individual organizations deploy and manage in their own infrastructure. Provides enhanced security, custom geographic placement, and compliance benefits. Uses standard Infisical authentication methods. - **`org`**: Dedicated relay server that individual organizations deploy and manage in their own infrastructure. Provides enhanced security, custom geographic placement, and compliance benefits. Uses standard Infisical authentication methods.
```bash ```bash
@@ -41,7 +41,7 @@ infisical relay start --type=<type> --host=<host> --name=<name> --auth-method=<a
infisical relay start --type=org --host=192.168.1.100 --name=my-org-relay infisical relay start --type=org --host=192.168.1.100 --name=my-org-relay
# Instance relay (configured by instance admin) # Instance relay (configured by instance admin)
INFISICAL_PROXY_AUTH_SECRET=<secret> infisical relay start --type=instance --host=10.0.1.50 --name=shared-relay INFISICAL_RELAY_AUTH_SECRET=<secret> infisical relay start --type=instance --host=10.0.1.50 --name=shared-relay
``` ```
</Accordion> </Accordion>
@@ -75,14 +75,14 @@ infisical relay start --type=<type> --host=<host> --name=<name> --auth-method=<a
Deploy your own relay server in your infrastructure for enhanced security and reduced latency. Supports all standard Infisical authentication methods documented below. Deploy your own relay server in your infrastructure for enhanced security and reduced latency. Supports all standard Infisical authentication methods documented below.
**Instance Relays (`--type=instance`):** **Instance Relays (`--type=instance`):**
Shared relay servers that serve all organizations on your Infisical instance. For Infisical Cloud, these are already running and ready to use. For self-hosted deployments, they're set up by the instance administrator. Authentication is handled via the `INFISICAL_PROXY_AUTH_SECRET` environment variable. Shared relay servers that serve all organizations on your Infisical instance. For Infisical Cloud, these are already running and ready to use. For self-hosted deployments, they're set up by the instance administrator. Authentication is handled via the `INFISICAL_RELAY_AUTH_SECRET` environment variable.
```bash ```bash
# Organization relay with Universal Auth (customer-deployed) # Organization relay with Universal Auth (customer-deployed)
infisical relay start --type=org --host=192.168.1.100 --name=my-org-relay --auth-method=universal-auth --client-id=<client-id> --client-secret=<client-secret> infisical relay start --type=org --host=192.168.1.100 --name=my-org-relay --auth-method=universal-auth --client-id=<client-id> --client-secret=<client-secret>
# Instance relay (configured by instance admin) # Instance relay (configured by instance admin)
INFISICAL_PROXY_AUTH_SECRET=<secret> infisical relay start --type=instance --host=10.0.1.50 --name=shared-relay INFISICAL_RELAY_AUTH_SECRET=<secret> infisical relay start --type=instance --host=10.0.1.50 --name=shared-relay
``` ```
### Authentication Methods ### Authentication Methods
@@ -68,26 +68,35 @@ Gateway ↔ Relay Server communication uses SSH certificate authentication:
- Gateway validates certificate against appropriate SSH Server CA - Gateway validates certificate against appropriate SSH Server CA
- Ensures gateway connects to legitimate relay infrastructure - Ensures gateway connects to legitimate relay infrastructure
### 3. Application Traffic Security ### 3. Platform-to-Gateway Direct Connection
End-to-end encryption for application data: The platform establishes secure direct connections with gateways through a **TLS-pinned tunnel** mechanism:
1. **mTLS Layer**: 1. **TLS-Pinned Tunnel Establishment**:
- Infisical platform establishes mTLS connections directly with gateways - Gateway initiates outbound connection to platform through SSH reverse tunnel
- Uses Organization Gateway certificates for authentication - Platform establishes direct mTLS connection with gateway using Organization Gateway certificates
- Application traffic is encrypted end-to-end between platform and gateway - TLS certificate pinning ensures the connection is bound to the specific gateway identity
- No inbound connections required - all communication flows through the outbound tunnel
2. **SSH Tunnel Layer**: 2. **Connection Flow**:
- mTLS-encrypted application traffic travels through SSH reverse tunnels ```
- Creates double encryption: mTLS payload within SSH tunnel Platform ←→ [SSH Reverse Tunnel] ←→ Gateway
- Relay servers cannot decrypt either encryption layer ```
3. **Traffic Isolation**: - Gateway maintains persistent outbound SSH tunnel to relay server
- Each gateway maintains separate SSH tunnels - Platform connects directly to gateway through this tunnel
- Organization's private keys never leave their environment - TLS handshake occurs over the SSH tunnel, establishing mTLS connection
- Complete cryptographic isolation between organizations - Application traffic flows through the TLS-pinned tunnel
3. **Security Benefits**:
- **No inbound connections**: Gateway never needs to accept incoming connections
- **Certificate-based authentication**: Uses Organization Gateway certificates for mutual TLS
- **Double encryption**: TLS traffic within SSH tunnel provides layered security
- **Relay server isolation**: Relay cannot decrypt either TLS or application data
- **Tenant isolation**: Each organization's traffic flows through separate authenticated channels
## Tenant Isolation ## Tenant Isolation
@@ -102,6 +102,13 @@ Once authenticated, the Gateway establishes an SSH reverse tunnel to the specifi
<Tabs> <Tabs>
<Tab title="Production (systemd)"> <Tab title="Production (systemd)">
For production deployments on Linux, install the Gateway as a systemd service: For production deployments on Linux, install the Gateway as a systemd service:
<Warning>
**Gateway v2:** The `infisical gateway systemd install` command deploys the new Gateway v2 component.
If you are migrating from Gateway v1 (legacy `infisical gateway install` command), this is not in-place. Gateway v2 provisions new gateway instances with new gateway IDs. Update any resources that reference a gateway ID (for example: dynamic secret configs, app connections, or other gateway-bound resources) to use the new Gateway v2 gateway ID.
</Warning>
```bash ```bash
sudo infisical gateway systemd install --token <your-machine-identity-token> --domain <your-infisical-domain> --name <gateway-name> --relay <relay-name> sudo infisical gateway systemd install --token <your-machine-identity-token> --domain <your-infisical-domain> --name <gateway-name> --relay <relay-name>
sudo systemctl start infisical-gateway sudo systemctl start infisical-gateway
@@ -369,8 +376,13 @@ Once authenticated, the Gateway establishes an SSH reverse tunnel to the specifi
</Accordion> </Accordion>
</AccordionGroup> </AccordionGroup>
### Install the Infisical Gateway Helm Chart ### Install the Infisical Gateway Helm Chart
<Warning>
**Version mapping:** Helm chart versions `>= 1.0.0` contain the new Gateway v2 component. Helm chart versions `<= 0.0.5` contain the legacy Gateway v1 component.
If you are moving from Gateway v1 (chart `<= 0.0.5`) to Gateway v2 (chart `>= 1.0.0`), this is not in-place. Gateway v2 provisions new gateway instances with new gateway IDs. Update any resources that reference a gateway ID (for example: dynamic secret configs, app connections, or other gateway-bound resources) to use the new Gateway v2 gateway ID.
</Warning>
```bash ```bash
helm install infisical-gateway infisical-helm-charts/infisical-gateway helm install infisical-gateway infisical-helm-charts/infisical-gateway
``` ```
@@ -385,11 +397,17 @@ Once authenticated, the Gateway establishes an SSH reverse tunnel to the specifi
You should see the following output which indicates the gateway is running as expected. You should see the following output which indicates the gateway is running as expected.
```bash ```bash
$ kubectl logs deployment/infisical-gateway $ kubectl logs deployment/infisical-gateway
INF Starting gateway 12:43AM INF Starting gateway
INF Starting gateway certificate renewal goroutine 12:43AM INF Starting gateway certificate renewal goroutine
INF Successfully registered gateway and received certificates 12:43AM INF Successfully registered gateway and received certificates
INF Connecting to relay server infisical-start on 152.42.218.156:2222... 12:43AM INF Connecting to relay server infisical-start on 152.42.218.156:2222...
INF Relay connection established for gateway 12:43AM INF Relay connection established for gateway
12:43AM INF Received incoming connection, starting TLS handshake
12:43AM INF TLS handshake completed successfully
12:43AM INF Negotiated ALPN protocol: infisical-ping
12:43AM INF Starting ping handler
12:43AM INF Ping handler completed
12:43AM INF Gateway is reachable by Infisical
``` ```
</Tab> </Tab>