mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 21:51:56 +00:00
Merge branch 'heads/main' into daniel/fips-initative
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import { TSecretSyncListItem } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
export const RAILWAY_SYNC_LIST_OPTION: TSecretSyncListItem = {
|
||||
name: "Railway",
|
||||
destination: SecretSync.Railway,
|
||||
connection: AppConnection.Railway,
|
||||
canImportSecrets: true
|
||||
};
|
||||
@@ -0,0 +1,124 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
|
||||
import { RailwayPublicAPI } from "@app/services/app-connection/railway/railway-connection-public-client";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
|
||||
import { SecretSyncError } from "../secret-sync-errors";
|
||||
import { TSecretMap } from "../secret-sync-types";
|
||||
import { TRailwaySyncWithCredentials } from "./railway-sync-types";
|
||||
|
||||
export const RailwaySyncFns = {
|
||||
async getSecrets(secretSync: TRailwaySyncWithCredentials): Promise<TSecretMap> {
|
||||
try {
|
||||
const config = secretSync.destinationConfig;
|
||||
|
||||
const variables = await RailwayPublicAPI.getVariables(secretSync.connection, {
|
||||
projectId: config.projectId,
|
||||
environmentId: config.environmentId,
|
||||
serviceId: config.serviceId || undefined
|
||||
});
|
||||
|
||||
const entries = {} as TSecretMap;
|
||||
|
||||
for (const [key, value] of Object.entries(variables)) {
|
||||
// Skip importing private railway variables
|
||||
// eslint-disable-next-line no-continue
|
||||
if (key.startsWith("RAILWAY_")) continue;
|
||||
|
||||
entries[key] = {
|
||||
value
|
||||
};
|
||||
}
|
||||
|
||||
return entries;
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
message: "Failed to import secrets from Railway"
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
async syncSecrets(secretSync: TRailwaySyncWithCredentials, secretMap: TSecretMap) {
|
||||
const {
|
||||
environment,
|
||||
syncOptions: { disableSecretDeletion, keySchema }
|
||||
} = secretSync;
|
||||
const railwaySecrets = await this.getSecrets(secretSync);
|
||||
const config = secretSync.destinationConfig;
|
||||
|
||||
for await (const key of Object.keys(secretMap)) {
|
||||
try {
|
||||
const existing = railwaySecrets[key];
|
||||
|
||||
if (existing === undefined || existing.value !== secretMap[key].value) {
|
||||
await RailwayPublicAPI.upsertVariable(secretSync.connection, {
|
||||
input: {
|
||||
projectId: config.projectId,
|
||||
environmentId: config.environmentId,
|
||||
serviceId: config.serviceId || undefined,
|
||||
name: key,
|
||||
value: secretMap[key].value ?? ""
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: key
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (disableSecretDeletion) return;
|
||||
|
||||
for await (const key of Object.keys(railwaySecrets)) {
|
||||
try {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(key, environment?.slug || "", keySchema)) continue;
|
||||
|
||||
if (!secretMap[key]) {
|
||||
await RailwayPublicAPI.deleteVariable(secretSync.connection, {
|
||||
input: {
|
||||
projectId: config.projectId,
|
||||
environmentId: config.environmentId,
|
||||
serviceId: config.serviceId || undefined,
|
||||
name: key
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: key
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async removeSecrets(secretSync: TRailwaySyncWithCredentials, secretMap: TSecretMap) {
|
||||
const existing = await this.getSecrets(secretSync);
|
||||
const config = secretSync.destinationConfig;
|
||||
|
||||
for await (const secret of Object.keys(existing)) {
|
||||
try {
|
||||
if (secret in secretMap) {
|
||||
await RailwayPublicAPI.deleteVariable(secretSync.connection, {
|
||||
input: {
|
||||
projectId: config.projectId,
|
||||
environmentId: config.environmentId,
|
||||
serviceId: config.serviceId || undefined,
|
||||
name: secret
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: secret
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretSyncs } from "@app/lib/api-docs";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import {
|
||||
BaseSecretSyncSchema,
|
||||
GenericCreateSecretSyncFieldsSchema,
|
||||
GenericUpdateSecretSyncFieldsSchema
|
||||
} from "@app/services/secret-sync/secret-sync-schemas";
|
||||
import { TSyncOptionsConfig } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
const RailwaySyncDestinationConfigSchema = z.object({
|
||||
projectId: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, "Railway project ID required")
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.RAILWAY.projectId),
|
||||
projectName: z.string().trim().describe(SecretSyncs.DESTINATION_CONFIG.RAILWAY.projectName),
|
||||
environmentId: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, "Railway environment ID required")
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.RAILWAY.environmentId),
|
||||
environmentName: z.string().trim().describe(SecretSyncs.DESTINATION_CONFIG.RAILWAY.environmentName),
|
||||
serviceId: z.string().optional().describe(SecretSyncs.DESTINATION_CONFIG.RAILWAY.serviceId),
|
||||
serviceName: z.string().optional().describe(SecretSyncs.DESTINATION_CONFIG.RAILWAY.serviceName)
|
||||
});
|
||||
|
||||
const RailwaySyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: true };
|
||||
|
||||
export const RailwaySyncSchema = BaseSecretSyncSchema(SecretSync.Railway, RailwaySyncOptionsConfig).extend({
|
||||
destination: z.literal(SecretSync.Railway),
|
||||
destinationConfig: RailwaySyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const CreateRailwaySyncSchema = GenericCreateSecretSyncFieldsSchema(
|
||||
SecretSync.Railway,
|
||||
RailwaySyncOptionsConfig
|
||||
).extend({
|
||||
destinationConfig: RailwaySyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const UpdateRailwaySyncSchema = GenericUpdateSecretSyncFieldsSchema(
|
||||
SecretSync.Railway,
|
||||
RailwaySyncOptionsConfig
|
||||
).extend({
|
||||
destinationConfig: RailwaySyncDestinationConfigSchema.optional()
|
||||
});
|
||||
|
||||
export const RailwaySyncListItemSchema = z.object({
|
||||
name: z.literal("Railway"),
|
||||
connection: z.literal(AppConnection.Railway),
|
||||
destination: z.literal(SecretSync.Railway),
|
||||
canImportSecrets: z.literal(true)
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import z from "zod";
|
||||
|
||||
import { TRailwayConnection } from "@app/services/app-connection/railway";
|
||||
|
||||
import { CreateRailwaySyncSchema, RailwaySyncListItemSchema, RailwaySyncSchema } from "./railway-sync-schemas";
|
||||
|
||||
export type TRailwaySyncListItem = z.infer<typeof RailwaySyncListItemSchema>;
|
||||
|
||||
export type TRailwaySync = z.infer<typeof RailwaySyncSchema>;
|
||||
|
||||
export type TRailwaySyncInput = z.infer<typeof CreateRailwaySyncSchema>;
|
||||
|
||||
export type TRailwaySyncWithCredentials = TRailwaySync & {
|
||||
connection: TRailwayConnection;
|
||||
};
|
||||
|
||||
export type TRailwaySecret = {
|
||||
createdAt: string;
|
||||
environmentId?: string | null;
|
||||
id: string;
|
||||
isSealed: boolean;
|
||||
name: string;
|
||||
serviceId?: string | null;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type TRailwayVariablesGraphResponse = {
|
||||
data: {
|
||||
variables: Record<string, string>;
|
||||
};
|
||||
};
|
||||
@@ -21,7 +21,8 @@ export enum SecretSync {
|
||||
Flyio = "flyio",
|
||||
GitLab = "gitlab",
|
||||
CloudflarePages = "cloudflare-pages",
|
||||
Zabbix = "zabbix"
|
||||
Zabbix = "zabbix",
|
||||
Railway = "railway"
|
||||
}
|
||||
|
||||
export enum SecretSyncInitialSyncBehavior {
|
||||
|
||||
@@ -39,6 +39,8 @@ import { HC_VAULT_SYNC_LIST_OPTION, HCVaultSyncFns } from "./hc-vault";
|
||||
import { HEROKU_SYNC_LIST_OPTION, HerokuSyncFns } from "./heroku";
|
||||
import { HUMANITEC_SYNC_LIST_OPTION } from "./humanitec";
|
||||
import { HumanitecSyncFns } from "./humanitec/humanitec-sync-fns";
|
||||
import { RAILWAY_SYNC_LIST_OPTION } from "./railway/railway-sync-constants";
|
||||
import { RailwaySyncFns } from "./railway/railway-sync-fns";
|
||||
import { RENDER_SYNC_LIST_OPTION, RenderSyncFns } from "./render";
|
||||
import { SECRET_SYNC_PLAN_MAP } from "./secret-sync-maps";
|
||||
import { TEAMCITY_SYNC_LIST_OPTION, TeamCitySyncFns } from "./teamcity";
|
||||
@@ -70,7 +72,8 @@ const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
|
||||
[SecretSync.Flyio]: FLYIO_SYNC_LIST_OPTION,
|
||||
[SecretSync.GitLab]: GITLAB_SYNC_LIST_OPTION,
|
||||
[SecretSync.CloudflarePages]: CLOUDFLARE_PAGES_SYNC_LIST_OPTION,
|
||||
[SecretSync.Zabbix]: ZABBIX_SYNC_LIST_OPTION
|
||||
[SecretSync.Zabbix]: ZABBIX_SYNC_LIST_OPTION,
|
||||
[SecretSync.Railway]: RAILWAY_SYNC_LIST_OPTION
|
||||
};
|
||||
|
||||
export const listSecretSyncOptions = () => {
|
||||
@@ -240,6 +243,8 @@ export const SecretSyncFns = {
|
||||
return CloudflarePagesSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Zabbix:
|
||||
return ZabbixSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Railway:
|
||||
return RailwaySyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled sync destination for sync secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||
@@ -335,6 +340,9 @@ export const SecretSyncFns = {
|
||||
case SecretSync.Zabbix:
|
||||
secretMap = await ZabbixSyncFns.getSecrets(secretSync);
|
||||
break;
|
||||
case SecretSync.Railway:
|
||||
secretMap = await RailwaySyncFns.getSecrets(secretSync);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled sync destination for get secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||
@@ -414,6 +422,8 @@ export const SecretSyncFns = {
|
||||
return CloudflarePagesSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Zabbix:
|
||||
return ZabbixSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Railway:
|
||||
return RailwaySyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled sync destination for remove secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||
|
||||
@@ -24,7 +24,8 @@ export const SECRET_SYNC_NAME_MAP: Record<SecretSync, string> = {
|
||||
[SecretSync.Flyio]: "Fly.io",
|
||||
[SecretSync.GitLab]: "GitLab",
|
||||
[SecretSync.CloudflarePages]: "Cloudflare Pages",
|
||||
[SecretSync.Zabbix]: "Zabbix"
|
||||
[SecretSync.Zabbix]: "Zabbix",
|
||||
[SecretSync.Railway]: "Railway"
|
||||
};
|
||||
|
||||
export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||
@@ -50,7 +51,8 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||
[SecretSync.Flyio]: AppConnection.Flyio,
|
||||
[SecretSync.GitLab]: AppConnection.GitLab,
|
||||
[SecretSync.CloudflarePages]: AppConnection.Cloudflare,
|
||||
[SecretSync.Zabbix]: AppConnection.Zabbix
|
||||
[SecretSync.Zabbix]: AppConnection.Zabbix,
|
||||
[SecretSync.Railway]: AppConnection.Railway
|
||||
};
|
||||
|
||||
export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
|
||||
@@ -76,5 +78,6 @@ export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
|
||||
[SecretSync.Flyio]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.GitLab]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.CloudflarePages]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Zabbix]: SecretSyncPlanType.Regular
|
||||
[SecretSync.Zabbix]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Railway]: SecretSyncPlanType.Regular
|
||||
};
|
||||
|
||||
@@ -94,6 +94,12 @@ import {
|
||||
THumanitecSyncListItem,
|
||||
THumanitecSyncWithCredentials
|
||||
} from "./humanitec";
|
||||
import {
|
||||
TRailwaySync,
|
||||
TRailwaySyncInput,
|
||||
TRailwaySyncListItem,
|
||||
TRailwaySyncWithCredentials
|
||||
} from "./railway/railway-sync-types";
|
||||
import {
|
||||
TRenderSync,
|
||||
TRenderSyncInput,
|
||||
@@ -138,7 +144,8 @@ export type TSecretSync =
|
||||
| TFlyioSync
|
||||
| TGitLabSync
|
||||
| TCloudflarePagesSync
|
||||
| TZabbixSync;
|
||||
| TZabbixSync
|
||||
| TRailwaySync;
|
||||
|
||||
export type TSecretSyncWithCredentials =
|
||||
| TAwsParameterStoreSyncWithCredentials
|
||||
@@ -163,7 +170,8 @@ export type TSecretSyncWithCredentials =
|
||||
| TFlyioSyncWithCredentials
|
||||
| TGitLabSyncWithCredentials
|
||||
| TCloudflarePagesSyncWithCredentials
|
||||
| TZabbixSyncWithCredentials;
|
||||
| TZabbixSyncWithCredentials
|
||||
| TRailwaySyncWithCredentials;
|
||||
|
||||
export type TSecretSyncInput =
|
||||
| TAwsParameterStoreSyncInput
|
||||
@@ -188,7 +196,8 @@ export type TSecretSyncInput =
|
||||
| TFlyioSyncInput
|
||||
| TGitLabSyncInput
|
||||
| TCloudflarePagesSyncInput
|
||||
| TZabbixSyncInput;
|
||||
| TZabbixSyncInput
|
||||
| TRailwaySyncInput;
|
||||
|
||||
export type TSecretSyncListItem =
|
||||
| TAwsParameterStoreSyncListItem
|
||||
@@ -213,7 +222,8 @@ export type TSecretSyncListItem =
|
||||
| TFlyioSyncListItem
|
||||
| TGitLabSyncListItem
|
||||
| TCloudflarePagesSyncListItem
|
||||
| TZabbixSyncListItem;
|
||||
| TZabbixSyncListItem
|
||||
| TRailwaySyncListItem;
|
||||
|
||||
export type TSyncOptionsConfig = {
|
||||
canImportSecrets: boolean;
|
||||
|
||||
Reference in New Issue
Block a user