diff --git a/backend/src/ee/routes/v1/audit-log-stream-routers/audit-log-stream-router.ts b/backend/src/ee/routes/v1/audit-log-stream-routers/audit-log-stream-router.ts index bf10e48bf..e0ac0b6af 100644 --- a/backend/src/ee/routes/v1/audit-log-stream-routers/audit-log-stream-router.ts +++ b/backend/src/ee/routes/v1/audit-log-stream-routers/audit-log-stream-router.ts @@ -1,5 +1,9 @@ import { z } from "zod"; +import { + CriblProviderListItemSchema, + SanitizedCriblProviderSchema +} from "@app/ee/services/audit-log-stream/cribl/cribl-provider-schemas"; import { CustomProviderListItemSchema, SanitizedCustomProviderSchema @@ -19,13 +23,15 @@ import { AuthMode } from "@app/services/auth/auth-type"; const SanitizedAuditLogStreamSchema = z.union([ SanitizedCustomProviderSchema, SanitizedDatadogProviderSchema, - SanitizedSplunkProviderSchema + SanitizedSplunkProviderSchema, + SanitizedCriblProviderSchema ]); const ProviderOptionsSchema = z.discriminatedUnion("provider", [ CustomProviderListItemSchema, DatadogProviderListItemSchema, - SplunkProviderListItemSchema + SplunkProviderListItemSchema, + CriblProviderListItemSchema ]); export const registerAuditLogStreamRouter = async (server: FastifyZodProvider) => { diff --git a/backend/src/ee/routes/v1/audit-log-stream-routers/index.ts b/backend/src/ee/routes/v1/audit-log-stream-routers/index.ts index ea3d48b30..f40a82d89 100644 --- a/backend/src/ee/routes/v1/audit-log-stream-routers/index.ts +++ b/backend/src/ee/routes/v1/audit-log-stream-routers/index.ts @@ -1,4 +1,9 @@ import { LogProvider } from "@app/ee/services/audit-log-stream/audit-log-stream-enums"; +import { + CreateCriblProviderLogStreamSchema, + SanitizedCriblProviderSchema, + UpdateCriblProviderLogStreamSchema +} from "@app/ee/services/audit-log-stream/cribl/cribl-provider-schemas"; import { CreateCustomProviderLogStreamSchema, SanitizedCustomProviderSchema, @@ -47,5 +52,14 @@ export const AUDIT_LOG_STREAM_REGISTER_ROUTER_MAP: Record { + registerAuditLogStreamEndpoints({ + server, + provider: LogProvider.Cribl, + sanitizedResponseSchema: SanitizedCriblProviderSchema, + createSchema: CreateCriblProviderLogStreamSchema, + updateSchema: UpdateCriblProviderLogStreamSchema + }); } }; diff --git a/backend/src/ee/services/audit-log-stream/audit-log-stream-enums.ts b/backend/src/ee/services/audit-log-stream/audit-log-stream-enums.ts index 9c03dc2bd..78233f774 100644 --- a/backend/src/ee/services/audit-log-stream/audit-log-stream-enums.ts +++ b/backend/src/ee/services/audit-log-stream/audit-log-stream-enums.ts @@ -1,5 +1,6 @@ export enum LogProvider { + Cribl = "cribl", + Custom = "custom", Datadog = "datadog", - Splunk = "splunk", - Custom = "custom" + Splunk = "splunk" } diff --git a/backend/src/ee/services/audit-log-stream/audit-log-stream-factory.ts b/backend/src/ee/services/audit-log-stream/audit-log-stream-factory.ts index f70dc02a0..21d629a53 100644 --- a/backend/src/ee/services/audit-log-stream/audit-log-stream-factory.ts +++ b/backend/src/ee/services/audit-log-stream/audit-log-stream-factory.ts @@ -1,5 +1,6 @@ import { LogProvider } from "./audit-log-stream-enums"; import { TAuditLogStreamCredentials, TLogStreamFactory } from "./audit-log-stream-types"; +import { CriblProviderFactory } from "./cribl/cribl-provider-factory"; import { CustomProviderFactory } from "./custom/custom-provider-factory"; import { DatadogProviderFactory } from "./datadog/datadog-provider-factory"; import { SplunkProviderFactory } from "./splunk/splunk-provider-factory"; @@ -9,5 +10,6 @@ type TLogStreamFactoryImplementation = TLogStreamFactory = { [LogProvider.Datadog]: DatadogProviderFactory as TLogStreamFactoryImplementation, [LogProvider.Splunk]: SplunkProviderFactory as TLogStreamFactoryImplementation, - [LogProvider.Custom]: CustomProviderFactory as TLogStreamFactoryImplementation + [LogProvider.Custom]: CustomProviderFactory as TLogStreamFactoryImplementation, + [LogProvider.Cribl]: CriblProviderFactory as TLogStreamFactoryImplementation }; diff --git a/backend/src/ee/services/audit-log-stream/audit-log-stream-fns.ts b/backend/src/ee/services/audit-log-stream/audit-log-stream-fns.ts index 87a7736a3..d03a5c8a7 100644 --- a/backend/src/ee/services/audit-log-stream/audit-log-stream-fns.ts +++ b/backend/src/ee/services/audit-log-stream/audit-log-stream-fns.ts @@ -3,14 +3,18 @@ import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { KmsDataKey } from "@app/services/kms/kms-types"; import { TAuditLogStream, TAuditLogStreamCredentials } from "./audit-log-stream-types"; +import { getCriblProviderListItem } from "./cribl/cribl-provider-fns"; import { getCustomProviderListItem } from "./custom/custom-provider-fns"; import { getDatadogProviderListItem } from "./datadog/datadog-provider-fns"; import { getSplunkProviderListItem } from "./splunk/splunk-provider-fns"; export const listProviderOptions = () => { - return [getDatadogProviderListItem(), getSplunkProviderListItem(), getCustomProviderListItem()].sort((a, b) => - a.name.localeCompare(b.name) - ); + return [ + getDatadogProviderListItem(), + getSplunkProviderListItem(), + getCustomProviderListItem(), + getCriblProviderListItem() + ].sort((a, b) => a.name.localeCompare(b.name)); }; export const encryptLogStreamCredentials = async ({ diff --git a/backend/src/ee/services/audit-log-stream/audit-log-stream-types.ts b/backend/src/ee/services/audit-log-stream/audit-log-stream-types.ts index d0f78f632..1ef33befe 100644 --- a/backend/src/ee/services/audit-log-stream/audit-log-stream-types.ts +++ b/backend/src/ee/services/audit-log-stream/audit-log-stream-types.ts @@ -1,16 +1,18 @@ import { TAuditLogs } from "@app/db/schemas"; import { LogProvider } from "./audit-log-stream-enums"; +import { TCriblProvider, TCriblProviderCredentials } from "./cribl/cribl-provider-types"; import { TCustomProvider, TCustomProviderCredentials } from "./custom/custom-provider-types"; import { TDatadogProvider, TDatadogProviderCredentials } from "./datadog/datadog-provider-types"; import { TSplunkProvider, TSplunkProviderCredentials } from "./splunk/splunk-provider-types"; -export type TAuditLogStream = TDatadogProvider | TSplunkProvider | TCustomProvider; +export type TAuditLogStream = TDatadogProvider | TSplunkProvider | TCustomProvider | TCriblProvider; export type TAuditLogStreamCredentials = | TDatadogProviderCredentials | TSplunkProviderCredentials - | TCustomProviderCredentials; + | TCustomProviderCredentials + | TCriblProviderCredentials; export type TCreateAuditLogStreamDTO = { provider: LogProvider; diff --git a/backend/src/ee/services/audit-log-stream/cribl/cribl-provider-factory.ts b/backend/src/ee/services/audit-log-stream/cribl/cribl-provider-factory.ts new file mode 100644 index 000000000..2e4eef93b --- /dev/null +++ b/backend/src/ee/services/audit-log-stream/cribl/cribl-provider-factory.ts @@ -0,0 +1,58 @@ +import { RawAxiosRequestHeaders } from "axios"; + +import { request } from "@app/lib/config/request"; +import { BadRequestError } from "@app/lib/errors"; +import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator"; + +import { AUDIT_LOG_STREAM_TIMEOUT } from "../../audit-log/audit-log-queue"; +import { TLogStreamFactoryStreamLog, TLogStreamFactoryValidateCredentials } from "../audit-log-stream-types"; +import { TCriblProviderCredentials } from "./cribl-provider-types"; + +export const CriblProviderFactory = () => { + const validateCredentials: TLogStreamFactoryValidateCredentials = async ({ + credentials + }) => { + const { url, token } = credentials; + + await blockLocalAndPrivateIpAddresses(url); + + const streamHeaders: RawAxiosRequestHeaders = { + "Content-Type": "application/json", + Authorization: `Bearer ${token}` + }; + + await request + .post(url, JSON.stringify({ ping: "ok" }), { + headers: streamHeaders, + timeout: AUDIT_LOG_STREAM_TIMEOUT, + signal: AbortSignal.timeout(AUDIT_LOG_STREAM_TIMEOUT) + }) + .catch((err) => { + throw new BadRequestError({ message: `Failed to connect with Cribl: ${(err as Error)?.message}` }); + }); + + return credentials; + }; + + const streamLog: TLogStreamFactoryStreamLog = async ({ credentials, auditLog }) => { + const { url, token } = credentials; + + await blockLocalAndPrivateIpAddresses(url); + + const streamHeaders: RawAxiosRequestHeaders = { + "Content-Type": "application/json", + Authorization: `Bearer ${token}` + }; + + await request.post(url, JSON.stringify(auditLog), { + headers: streamHeaders, + timeout: AUDIT_LOG_STREAM_TIMEOUT, + signal: AbortSignal.timeout(AUDIT_LOG_STREAM_TIMEOUT) + }); + }; + + return { + validateCredentials, + streamLog + }; +}; diff --git a/backend/src/ee/services/audit-log-stream/cribl/cribl-provider-fns.ts b/backend/src/ee/services/audit-log-stream/cribl/cribl-provider-fns.ts new file mode 100644 index 000000000..f8b82509a --- /dev/null +++ b/backend/src/ee/services/audit-log-stream/cribl/cribl-provider-fns.ts @@ -0,0 +1,8 @@ +import { LogProvider } from "../audit-log-stream-enums"; + +export const getCriblProviderListItem = () => { + return { + name: "Cribl" as const, + provider: LogProvider.Cribl as const + }; +}; diff --git a/backend/src/ee/services/audit-log-stream/cribl/cribl-provider-schemas.ts b/backend/src/ee/services/audit-log-stream/cribl/cribl-provider-schemas.ts new file mode 100644 index 000000000..8c2a51f32 --- /dev/null +++ b/backend/src/ee/services/audit-log-stream/cribl/cribl-provider-schemas.ts @@ -0,0 +1,34 @@ +import { z } from "zod"; + +import { LogProvider } from "../audit-log-stream-enums"; +import { BaseProviderSchema } from "../audit-log-stream-schemas"; + +export const CriblProviderCredentialsSchema = z.object({ + url: z.string().url().trim().min(1).max(255), + token: z.string().trim().min(21).max(255) +}); + +const BaseCriblProviderSchema = BaseProviderSchema.extend({ provider: z.literal(LogProvider.Cribl) }); + +export const CriblProviderSchema = BaseCriblProviderSchema.extend({ + credentials: CriblProviderCredentialsSchema +}); + +export const SanitizedCriblProviderSchema = BaseCriblProviderSchema.extend({ + credentials: CriblProviderCredentialsSchema.pick({ + url: true + }) +}); + +export const CriblProviderListItemSchema = z.object({ + name: z.literal("Cribl"), + provider: z.literal(LogProvider.Cribl) +}); + +export const CreateCriblProviderLogStreamSchema = z.object({ + credentials: CriblProviderCredentialsSchema +}); + +export const UpdateCriblProviderLogStreamSchema = z.object({ + credentials: CriblProviderCredentialsSchema +}); diff --git a/backend/src/ee/services/audit-log-stream/cribl/cribl-provider-types.ts b/backend/src/ee/services/audit-log-stream/cribl/cribl-provider-types.ts new file mode 100644 index 000000000..6577f1bff --- /dev/null +++ b/backend/src/ee/services/audit-log-stream/cribl/cribl-provider-types.ts @@ -0,0 +1,7 @@ +import { z } from "zod"; + +import { CriblProviderCredentialsSchema, CriblProviderSchema } from "./cribl-provider-schemas"; + +export type TCriblProvider = z.infer; + +export type TCriblProviderCredentials = z.infer; diff --git a/backend/src/keystore/keystore.ts b/backend/src/keystore/keystore.ts index 45589e529..a6135e0ca 100644 --- a/backend/src/keystore/keystore.ts +++ b/backend/src/keystore/keystore.ts @@ -1,9 +1,10 @@ +import { Cluster, Redis } from "ioredis"; + import { buildRedisFromConfig, TRedisConfigKeys } from "@app/lib/config/redis"; import { pgAdvisoryLockHashText } from "@app/lib/crypto/hashtext"; import { applyJitter } from "@app/lib/dates"; import { delay as delayMs } from "@app/lib/delay"; import { ExecutionResult, Redlock, Settings } from "@app/lib/red-lock"; -import { Redis, Cluster } from "ioredis"; export const PgSqlLock = { BootUpMigration: 2023, diff --git a/frontend/public/images/integrations/Cribl.png b/frontend/public/images/integrations/Cribl.png new file mode 100644 index 000000000..9e8282368 Binary files /dev/null and b/frontend/public/images/integrations/Cribl.png differ diff --git a/frontend/src/helpers/auditLogStreams.ts b/frontend/src/helpers/auditLogStreams.ts index 00d2b9e51..eee2fc8da 100644 --- a/frontend/src/helpers/auditLogStreams.ts +++ b/frontend/src/helpers/auditLogStreams.ts @@ -8,6 +8,7 @@ export const AUDIT_LOG_STREAM_PROVIDER_MAP: Record< LogProvider, { name: string; image?: string; icon?: IconDefinition; size?: number } > = { + [LogProvider.Cribl]: { name: "Cribl", image: "Cribl.png", size: 60 }, [LogProvider.Custom]: { name: "Custom", icon: faCode }, [LogProvider.Datadog]: { name: "Datadog", image: "Datadog.png" }, [LogProvider.Splunk]: { name: "Splunk", image: "Splunk.png", size: 65 } @@ -19,8 +20,8 @@ export function getProviderUrl( ) { switch (logStream.provider) { case LogProvider.Custom: - return logStream.credentials.url; case LogProvider.Datadog: + case LogProvider.Cribl: return logStream.credentials.url; case LogProvider.Splunk: return `https://${logStream.credentials.hostname}:8088/services/collector/event`; diff --git a/frontend/src/hooks/api/auditLogStreams/enums.ts b/frontend/src/hooks/api/auditLogStreams/enums.ts index 9c03dc2bd..78233f774 100644 --- a/frontend/src/hooks/api/auditLogStreams/enums.ts +++ b/frontend/src/hooks/api/auditLogStreams/enums.ts @@ -1,5 +1,6 @@ export enum LogProvider { + Cribl = "cribl", + Custom = "custom", Datadog = "datadog", - Splunk = "splunk", - Custom = "custom" + Splunk = "splunk" } diff --git a/frontend/src/hooks/api/auditLogStreams/types/index.ts b/frontend/src/hooks/api/auditLogStreams/types/index.ts index d3dffe6a9..a360cd677 100644 --- a/frontend/src/hooks/api/auditLogStreams/types/index.ts +++ b/frontend/src/hooks/api/auditLogStreams/types/index.ts @@ -1,4 +1,5 @@ import { LogProvider } from "../enums"; +import { TCriblProviderLogStream } from "./providers/cribl-provider"; import { TCustomProviderLogStream } from "./providers/custom-provider"; import { TDatadogProviderLogStream } from "./providers/datadog-provider"; import { TSplunkProviderLogStream } from "./providers/splunk-provider"; @@ -6,9 +7,11 @@ import { TSplunkProviderLogStream } from "./providers/splunk-provider"; export type TAuditLogStream = | TCustomProviderLogStream | TDatadogProviderLogStream - | TSplunkProviderLogStream; + | TSplunkProviderLogStream + | TCriblProviderLogStream; export type TAuditLogStreamProviderMap = { + [LogProvider.Cribl]: TCriblProviderLogStream; [LogProvider.Custom]: TCustomProviderLogStream; [LogProvider.Datadog]: TDatadogProviderLogStream; [LogProvider.Splunk]: TSplunkProviderLogStream; diff --git a/frontend/src/hooks/api/auditLogStreams/types/providers/cribl-provider.ts b/frontend/src/hooks/api/auditLogStreams/types/providers/cribl-provider.ts new file mode 100644 index 000000000..aeda0390a --- /dev/null +++ b/frontend/src/hooks/api/auditLogStreams/types/providers/cribl-provider.ts @@ -0,0 +1,10 @@ +import { LogProvider } from "../../enums"; +import { TRootProviderLogStream } from "./root-provider"; + +export type TCriblProviderLogStream = TRootProviderLogStream & { + provider: LogProvider.Cribl; + credentials: { + url: string; + token: string; + }; +}; diff --git a/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/AuditLogStreamForm.tsx b/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/AuditLogStreamForm.tsx index 1af5e8d1a..6de5a0ecf 100644 --- a/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/AuditLogStreamForm.tsx +++ b/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/AuditLogStreamForm.tsx @@ -6,6 +6,7 @@ import { TAuditLogStream } from "@app/hooks/api/types"; import { DiscriminativePick } from "@app/types"; import { AuditLogStreamHeader } from "../components/AuditLogStreamHeader"; +import { CriblProviderAuditLogStreamForm } from "./CriblProviderAuditLogStreamForm"; import { CustomProviderAuditLogStreamForm } from "./CustomProviderAuditLogStreamForm"; import { DatadogProviderAuditLogStreamForm } from "./DatadogProviderAuditLogStreamForm"; import { SplunkProviderAuditLogStreamForm } from "./SplunkProviderAuditLogStreamForm"; @@ -44,6 +45,8 @@ const CreateForm = ({ provider, onComplete }: CreateFormProps) => { }; switch (provider) { + case LogProvider.Cribl: + return ; case LogProvider.Custom: return ; case LogProvider.Datadog: @@ -83,6 +86,10 @@ const UpdateForm = ({ auditLogStream, onComplete }: UpdateFormProps) => { }; switch (auditLogStream.provider) { + case LogProvider.Cribl: + return ( + + ); case LogProvider.Custom: return ( diff --git a/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/CriblProviderAuditLogStreamForm.tsx b/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/CriblProviderAuditLogStreamForm.tsx new file mode 100644 index 000000000..b839ddac8 --- /dev/null +++ b/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/CriblProviderAuditLogStreamForm.tsx @@ -0,0 +1,108 @@ +import { Controller, FormProvider, useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; + +import { Button, FormControl, Input, ModalClose, SecretInput } from "@app/components/v2"; +import { LogProvider } from "@app/hooks/api/auditLogStreams/enums"; +import { TCriblProviderLogStream } from "@app/hooks/api/auditLogStreams/types/providers/cribl-provider"; + +type Props = { + auditLogStream?: TCriblProviderLogStream; + onSubmit: (formData: FormData) => void; +}; + +const formSchema = z.object({ + provider: z.literal(LogProvider.Cribl), + credentials: z.object({ + url: z.string().url().trim().min(1).max(255), + token: z.string().trim().min(21).max(255) + }) +}); + +type FormData = z.infer; + +export const CriblProviderAuditLogStreamForm = ({ auditLogStream, onSubmit }: Props) => { + const isUpdate = Boolean(auditLogStream); + + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: auditLogStream ?? { + provider: LogProvider.Cribl + } + }); + + const { + handleSubmit, + control, + formState: { isSubmitting, isDirty } + } = form; + + return ( + +
+ ( + + To derive your Stream URL: Obtain your Cribl hostname (e.g. cribl.example.com), + Infisical HTTP data source port (e.g. 20000), and HTTP event API path (e.g. + /infisical). +
+
+ If your Infisical Data Source has TLS enabled, then use the https protocol. + + } + > + +
+ )} + /> + ( + + onChange(e.target.value)} + /> + + )} + /> +
+ + + + +
+ +
+ ); +};