import { z } from "zod"; import { removeTrailingSlash } from "../fn"; import { CustomLogger } from "../logger/logger"; import { zpStr } from "../zod"; export const GITLAB_URL = "https://gitlab.com"; // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any -- If `process.pkg` is set, and it's true, then it means that the app is currently running in a packaged environment (a binary) export const IS_PACKAGED = (process as any)?.pkg !== undefined; const zodStrBool = z .string() .optional() .transform((val) => val === "true"); const databaseReadReplicaSchema = z .object({ DB_CONNECTION_URI: z.string().describe("Postgres read replica database connection string"), DB_ROOT_CERT: zpStr(z.string().optional().describe("Postgres read replica database certificate string")) }) .array() .optional(); const envSchema = z .object({ INFISICAL_PLATFORM_VERSION: zpStr(z.string().optional()), PORT: z.coerce.number().default(IS_PACKAGED ? 8080 : 4000), DISABLE_SECRET_SCANNING: z .enum(["true", "false"]) .default("false") .transform((el) => el === "true"), REDIS_URL: zpStr(z.string()), HOST: zpStr(z.string().default("localhost")), DB_CONNECTION_URI: zpStr(z.string().describe("Postgres database connection string")).default( `postgresql://${process.env.DB_USER}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}` ), AUDIT_LOGS_DB_CONNECTION_URI: zpStr( z.string().describe("Postgres database connection string for Audit logs").optional() ), AUDIT_LOGS_DB_ROOT_CERT: zpStr( z.string().describe("Postgres database base64-encoded CA cert for Audit logs").optional() ), MAX_LEASE_LIMIT: z.coerce.number().default(10000), DB_ROOT_CERT: zpStr(z.string().describe("Postgres database base64-encoded CA cert").optional()), DB_HOST: zpStr(z.string().describe("Postgres database host").optional()), DB_PORT: zpStr(z.string().describe("Postgres database port").optional()).default("5432"), DB_USER: zpStr(z.string().describe("Postgres database username").optional()), DB_PASSWORD: zpStr(z.string().describe("Postgres database password").optional()), DB_NAME: zpStr(z.string().describe("Postgres database name").optional()), DB_READ_REPLICAS: zpStr(z.string().describe("Postgres read replicas").optional()), BCRYPT_SALT_ROUND: z.number().default(12), NODE_ENV: z.enum(["development", "test", "production"]).default("production"), SALT_ROUNDS: z.coerce.number().default(10), INITIAL_ORGANIZATION_NAME: zpStr(z.string().optional()), // TODO(akhilmhdh): will be changed to one ENCRYPTION_KEY: zpStr(z.string().optional()), ROOT_ENCRYPTION_KEY: zpStr(z.string().optional()), HTTPS_ENABLED: zodStrBool, // smtp options SMTP_HOST: zpStr(z.string().optional()), SMTP_IGNORE_TLS: zodStrBool.default("false"), SMTP_REQUIRE_TLS: zodStrBool.default("true"), SMTP_TLS_REJECT_UNAUTHORIZED: zodStrBool.default("true"), SMTP_PORT: z.coerce.number().default(587), SMTP_USERNAME: zpStr(z.string().optional()), SMTP_PASSWORD: zpStr(z.string().optional()), SMTP_FROM_ADDRESS: zpStr(z.string().optional()), SMTP_FROM_NAME: zpStr(z.string().optional().default("Infisical")), COOKIE_SECRET_SIGN_KEY: z .string() .min(32) .default("#5VihU%rbXHcHwWwCot5L3vyPsx$7dWYw^iGk!EJg2bC*f$PD$%KCqx^R@#^LSEf"), // Ensure that the SITE_URL never ends with a trailing slash SITE_URL: zpStr(z.string().transform((val) => (val ? removeTrailingSlash(val) : val))).optional(), // Telemetry TELEMETRY_ENABLED: zodStrBool.default("true"), POSTHOG_HOST: zpStr(z.string().optional().default("https://app.posthog.com")), POSTHOG_PROJECT_API_KEY: zpStr(z.string().optional().default("phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE")), LOOPS_API_KEY: zpStr(z.string().optional()), // jwt options AUTH_SECRET: zpStr(z.string()).default(process.env.JWT_AUTH_SECRET), // for those still using old JWT_AUTH_SECRET JWT_AUTH_LIFETIME: zpStr(z.string().default("10d")), JWT_SIGNUP_LIFETIME: zpStr(z.string().default("15m")), JWT_REFRESH_LIFETIME: zpStr(z.string().default("90d")), JWT_INVITE_LIFETIME: zpStr(z.string().default("1d")), JWT_MFA_LIFETIME: zpStr(z.string().default("5m")), JWT_PROVIDER_AUTH_LIFETIME: zpStr(z.string().default("15m")), // Oauth CLIENT_ID_GOOGLE_LOGIN: zpStr(z.string().optional()), CLIENT_SECRET_GOOGLE_LOGIN: zpStr(z.string().optional()), CLIENT_ID_GITHUB_LOGIN: zpStr(z.string().optional()), CLIENT_SECRET_GITHUB_LOGIN: zpStr(z.string().optional()), CLIENT_ID_GITLAB_LOGIN: zpStr(z.string().optional()), CLIENT_SECRET_GITLAB_LOGIN: zpStr(z.string().optional()), CLIENT_GITLAB_LOGIN_URL: zpStr( z .string() .optional() .default(process.env.URL_GITLAB_LOGIN ?? GITLAB_URL) ), // fallback since URL_GITLAB_LOGIN has been renamed DEFAULT_SAML_ORG_SLUG: zpStr(z.string().optional()).default(process.env.NEXT_PUBLIC_SAML_ORG_SLUG), // integration client secrets // heroku CLIENT_ID_HEROKU: zpStr(z.string().optional()), CLIENT_SECRET_HEROKU: zpStr(z.string().optional()), // vercel CLIENT_ID_VERCEL: zpStr(z.string().optional()), CLIENT_SECRET_VERCEL: zpStr(z.string().optional()), CLIENT_SLUG_VERCEL: zpStr(z.string().optional()), // netlify CLIENT_ID_NETLIFY: zpStr(z.string().optional()), CLIENT_SECRET_NETLIFY: zpStr(z.string().optional()), // bit bucket CLIENT_ID_BITBUCKET: zpStr(z.string().optional()), CLIENT_SECRET_BITBUCKET: zpStr(z.string().optional()), // gcp secret manager CLIENT_ID_GCP_SECRET_MANAGER: zpStr(z.string().optional()), CLIENT_SECRET_GCP_SECRET_MANAGER: zpStr(z.string().optional()), // github oauth CLIENT_ID_GITHUB: zpStr(z.string().optional()), CLIENT_SECRET_GITHUB: zpStr(z.string().optional()), // github app CLIENT_ID_GITHUB_APP: zpStr(z.string().optional()), CLIENT_SECRET_GITHUB_APP: zpStr(z.string().optional()), CLIENT_PRIVATE_KEY_GITHUB_APP: zpStr(z.string().optional()), CLIENT_APP_ID_GITHUB_APP: z.coerce.number().optional(), CLIENT_SLUG_GITHUB_APP: zpStr(z.string().optional()), // azure CLIENT_ID_AZURE: zpStr(z.string().optional()), CLIENT_SECRET_AZURE: zpStr(z.string().optional()), // aws CLIENT_ID_AWS_INTEGRATION: zpStr(z.string().optional()), CLIENT_SECRET_AWS_INTEGRATION: zpStr(z.string().optional()), // gitlab CLIENT_ID_GITLAB: zpStr(z.string().optional()), CLIENT_SECRET_GITLAB: zpStr(z.string().optional()), URL_GITLAB_URL: zpStr(z.string().optional().default(GITLAB_URL)), // SECRET-SCANNING SECRET_SCANNING_WEBHOOK_PROXY: zpStr(z.string().optional()), SECRET_SCANNING_WEBHOOK_SECRET: zpStr(z.string().optional()), SECRET_SCANNING_GIT_APP_ID: zpStr(z.string().optional()), SECRET_SCANNING_PRIVATE_KEY: zpStr(z.string().optional()), SECRET_SCANNING_ORG_WHITELIST: zpStr(z.string().optional()), // LICENSE LICENSE_SERVER_URL: zpStr(z.string().optional().default("https://portal.infisical.com")), LICENSE_SERVER_KEY: zpStr(z.string().optional()), LICENSE_KEY: zpStr(z.string().optional()), LICENSE_KEY_OFFLINE: zpStr(z.string().optional()), // GENERIC STANDALONE_MODE: z .enum(["true", "false"]) .transform((val) => val === "true" || IS_PACKAGED) .optional(), INFISICAL_CLOUD: zodStrBool.default("false"), MAINTENANCE_MODE: zodStrBool.default("false"), CAPTCHA_SECRET: zpStr(z.string().optional()), CAPTCHA_SITE_KEY: zpStr(z.string().optional()), INTERCOM_ID: zpStr(z.string().optional()), // TELEMETRY OTEL_TELEMETRY_COLLECTION_ENABLED: zodStrBool.default("false"), OTEL_EXPORT_OTLP_ENDPOINT: zpStr(z.string().optional()), OTEL_OTLP_PUSH_INTERVAL: z.coerce.number().default(30000), OTEL_COLLECTOR_BASIC_AUTH_USERNAME: zpStr(z.string().optional()), OTEL_COLLECTOR_BASIC_AUTH_PASSWORD: zpStr(z.string().optional()), OTEL_EXPORT_TYPE: z.enum(["prometheus", "otlp"]).optional(), PYLON_API_KEY: zpStr(z.string().optional()), DISABLE_AUDIT_LOG_GENERATION: zodStrBool.default("false"), SSL_CLIENT_CERTIFICATE_HEADER_KEY: zpStr(z.string().optional()).default("x-ssl-client-cert"), WORKFLOW_SLACK_CLIENT_ID: zpStr(z.string().optional()), WORKFLOW_SLACK_CLIENT_SECRET: zpStr(z.string().optional()), ENABLE_MSSQL_SECRET_ROTATION_ENCRYPT: zodStrBool.default("true"), // HSM HSM_LIB_PATH: zpStr(z.string().optional()), HSM_PIN: zpStr(z.string().optional()), HSM_KEY_LABEL: zpStr(z.string().optional()), HSM_SLOT: z.coerce.number().optional().default(0), USE_PG_QUEUE: zodStrBool.default("false"), SHOULD_INIT_PG_QUEUE: zodStrBool.default("false"), /* Gateway----------------------------------------------------------------------------- */ GATEWAY_INFISICAL_STATIC_IP_ADDRESS: zpStr(z.string().optional()), GATEWAY_RELAY_ADDRESS: zpStr(z.string().optional()), GATEWAY_RELAY_REALM: zpStr(z.string().optional()), GATEWAY_RELAY_AUTH_SECRET: zpStr(z.string().optional()), /* ----------------------------------------------------------------------------- */ /* App Connections ----------------------------------------------------------------------------- */ // aws INF_APP_CONNECTION_AWS_ACCESS_KEY_ID: zpStr(z.string().optional()), INF_APP_CONNECTION_AWS_SECRET_ACCESS_KEY: zpStr(z.string().optional()), // github oauth INF_APP_CONNECTION_GITHUB_OAUTH_CLIENT_ID: zpStr(z.string().optional()), INF_APP_CONNECTION_GITHUB_OAUTH_CLIENT_SECRET: zpStr(z.string().optional()), // github app INF_APP_CONNECTION_GITHUB_APP_CLIENT_ID: zpStr(z.string().optional()), INF_APP_CONNECTION_GITHUB_APP_CLIENT_SECRET: zpStr(z.string().optional()), INF_APP_CONNECTION_GITHUB_APP_PRIVATE_KEY: zpStr(z.string().optional()), INF_APP_CONNECTION_GITHUB_APP_SLUG: zpStr(z.string().optional()), INF_APP_CONNECTION_GITHUB_APP_ID: zpStr(z.string().optional()), // gcp app INF_APP_CONNECTION_GCP_SERVICE_ACCOUNT_CREDENTIAL: zpStr(z.string().optional()), // azure app INF_APP_CONNECTION_AZURE_CLIENT_ID: zpStr(z.string().optional()), INF_APP_CONNECTION_AZURE_CLIENT_SECRET: zpStr(z.string().optional()), // datadog SHOULD_USE_DATADOG_TRACER: zodStrBool.default("false"), DATADOG_PROFILING_ENABLED: zodStrBool.default("false"), DATADOG_ENV: zpStr(z.string().optional().default("prod")), DATADOG_SERVICE: zpStr(z.string().optional().default("infisical-core")), DATADOG_HOSTNAME: zpStr(z.string().optional()), /* CORS ----------------------------------------------------------------------------- */ CORS_ALLOWED_ORIGINS: zpStr( z .string() .optional() .transform((val) => { if (!val) return undefined; return JSON.parse(val) as string[]; }) ), CORS_ALLOWED_HEADERS: zpStr( z .string() .optional() .transform((val) => { if (!val) return undefined; return JSON.parse(val) as string[]; }) ) }) // To ensure that basic encryption is always possible. .refine( (data) => Boolean(data.ENCRYPTION_KEY) || Boolean(data.ROOT_ENCRYPTION_KEY), "Either ENCRYPTION_KEY or ROOT_ENCRYPTION_KEY must be defined." ) .transform((data) => ({ ...data, DB_READ_REPLICAS: data.DB_READ_REPLICAS ? databaseReadReplicaSchema.parse(JSON.parse(data.DB_READ_REPLICAS)) : undefined, isCloud: Boolean(data.LICENSE_SERVER_KEY), isSmtpConfigured: Boolean(data.SMTP_HOST), isRedisConfigured: Boolean(data.REDIS_URL), isDevelopmentMode: data.NODE_ENV === "development", isProductionMode: data.NODE_ENV === "production" || IS_PACKAGED, isSecretScanningConfigured: Boolean(data.SECRET_SCANNING_GIT_APP_ID) && Boolean(data.SECRET_SCANNING_PRIVATE_KEY) && Boolean(data.SECRET_SCANNING_WEBHOOK_SECRET), isHsmConfigured: Boolean(data.HSM_LIB_PATH) && Boolean(data.HSM_PIN) && Boolean(data.HSM_KEY_LABEL) && data.HSM_SLOT !== undefined, samlDefaultOrgSlug: data.DEFAULT_SAML_ORG_SLUG, SECRET_SCANNING_ORG_WHITELIST: data.SECRET_SCANNING_ORG_WHITELIST?.split(",") })); export type TEnvConfig = Readonly>; let envCfg: TEnvConfig; export const getConfig = () => envCfg; // cannot import singleton logger directly as it needs config to load various transport export const initEnvConfig = (logger?: CustomLogger) => { const parsedEnv = envSchema.safeParse(process.env); if (!parsedEnv.success) { (logger ?? console).error("Invalid environment variables. Check the error below"); (logger ?? console).error(parsedEnv.error.issues); process.exit(-1); } envCfg = Object.freeze(parsedEnv.data); return envCfg; }; export const formatSmtpConfig = () => { return { host: envCfg.SMTP_HOST, port: envCfg.SMTP_PORT, auth: envCfg.SMTP_USERNAME && envCfg.SMTP_PASSWORD ? { user: envCfg.SMTP_USERNAME, pass: envCfg.SMTP_PASSWORD } : undefined, secure: envCfg.SMTP_PORT === 465, from: `"${envCfg.SMTP_FROM_NAME}" <${envCfg.SMTP_FROM_ADDRESS}>`, ignoreTLS: envCfg.SMTP_IGNORE_TLS, requireTLS: envCfg.SMTP_REQUIRE_TLS, tls: { rejectUnauthorized: envCfg.SMTP_TLS_REJECT_UNAUTHORIZED } }; };