From 38cc414382c38f814d274530fc3edfa5af9c0b92 Mon Sep 17 00:00:00 2001 From: Brad Deibert Date: Tue, 2 Jun 2026 14:38:13 -0700 Subject: [PATCH 1/8] add event_delivery_mode to script config, exit early when push mode active --- package/default/script.conf | 1 + src/bitwarden_event_logs.py | 4 ++++ src/config.py | 4 +++- src/models.py | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/package/default/script.conf b/package/default/script.conf index 53e6133..6779e09 100644 --- a/package/default/script.conf +++ b/package/default/script.conf @@ -2,3 +2,4 @@ apiUrl=https://api.bitwarden.com identityUrl=https://identity.bitwarden.com loggingLevel=INFO +eventDeliveryMode=poll \ No newline at end of file diff --git a/src/bitwarden_event_logs.py b/src/bitwarden_event_logs.py index de3012c..7abaf9b 100644 --- a/src/bitwarden_event_logs.py +++ b/src/bitwarden_event_logs.py @@ -41,6 +41,10 @@ def create_event_logs_writer(self): return EventLogsWriter(self.bitwarden_api, self.checkpoint, self.settings_config) def run(self): + if self.settings_config.event_delivery_mode == 'push': + self.logger.info('using push event delivery mode, no polling necessary. exiting.') + return + for next_request, events in self.event_logs_writer.read_events(): self.event_logs_writer.write_events(events) diff --git a/src/config.py b/src/config.py index 7960c8c..17b0308 100644 --- a/src/config.py +++ b/src/config.py @@ -84,11 +84,13 @@ def __parse_settings_config(cls, settings: Optional[Dict[str, Dict[str, Any]]]) identity_url = identity_url.strip(" \"") start_date = datetime_from_str(settings_config.get('startDate', None)) + event_delivery_mode = settings_config.get('eventDeliveryMode', 'poll') return SettingsConfig(api_url=secure_url(api_url), identity_url=secure_url(identity_url), start_date=start_date, - logging_level=settings_config.get('loggingLevel', None)) + logging_level=settings_config.get('loggingLevel', None), + event_delivery_mode=event_delivery_mode) @classmethod def __parse_bitwarden_api_key(cls, bitwarden_api_key: Optional[str]) -> BitwardenApiKey: diff --git a/src/models.py b/src/models.py index bf7e2d7..d86217b 100644 --- a/src/models.py +++ b/src/models.py @@ -9,6 +9,7 @@ class SettingsConfig: identity_url: str start_date: Optional[datetime] = None logging_level: Optional[str] = None + event_delivery_mode: str = 'poll' @dataclass From 694d3de8eed6d7d7ca3dd8ec4af5c308ec8718c5 Mon Sep 17 00:00:00 2001 From: Brad Deibert Date: Tue, 2 Jun 2026 15:02:10 -0700 Subject: [PATCH 2/8] update .spec to match script.conf file --- package/README/script.conf.spec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package/README/script.conf.spec b/package/README/script.conf.spec index 1dc04bf..8781ad4 100644 --- a/package/README/script.conf.spec +++ b/package/README/script.conf.spec @@ -2,4 +2,5 @@ apiUrl= identityUrl= startDate= -loggingLevel= \ No newline at end of file +loggingLevel= +eventDeliveryMode= \ No newline at end of file From d3d5917f1f682c3ffa43e3a19ac89b8bd73a9d40 Mon Sep 17 00:00:00 2001 From: Brad Deibert Date: Tue, 2 Jun 2026 17:20:08 -0700 Subject: [PATCH 3/8] add delivery mode radio to setup form, hide poll fields when push selected --- ui/src/app/app.component.html | 341 ++++++++++++++++++---------------- ui/src/app/app.component.ts | 13 ++ 2 files changed, 198 insertions(+), 156 deletions(-) diff --git a/ui/src/app/app.component.html b/ui/src/app/app.component.html index 5afe25a..4ecfe6c 100644 --- a/ui/src/app/app.component.html +++ b/ui/src/app/app.component.html @@ -24,178 +24,207 @@

-

Bitwarden API Key

-

- Your - API key - can be found in the Bitwarden organization Admin Console. -

-
- -
- -
- @if ( - setupForm.controls.clientId.invalid && - setupForm.controls.clientId.touched - ) { -
Client Id is required
- } -
-
- -
- -
- @if ( - setupForm.controls.clientSecret.invalid && - setupForm.controls.clientSecret.touched - ) { -
Client Secret is required
- } -
+

Event Delivery Mode

+ + + + -

Bitwarden Server Url

-

- Self-hosted Bitwarden servers may need to reconfigure their - installation's URL. -

-
- -
- + + + + @if (!isPushEventDelivery()) { +

Bitwarden API Key

+

+ Your + API key - - - - -

-
- + can be found in the Bitwarden organization Admin Console. +

+
+ +
+ +
+ @if ( + setupForm.controls.clientId.invalid && + setupForm.controls.clientId.touched + ) { +
Client Id is required
+ }
- @if ( - setupForm.controls.serverUrl.invalid && - setupForm.controls.serverUrl.touched - ) { -
- @if (setupForm.controls.serverUrl.hasError("required")) { - Server Url is required - } @else if (setupForm.controls.serverUrl.hasError("insecureUrl")) { - URLs starting with 'http://' are considered insecure and are not - allowed in Splunk. Please use 'https://' instead. - } @else { - Invalid Url - } +
+ +
+
- } -
+ @if ( + setupForm.controls.clientSecret.invalid && + setupForm.controls.clientSecret.touched + ) { +
Client Secret is required
+ } +
-

Splunk Index

-

Choose a Splunk index for the Bitwarden event logs.

-
- -
- + + + + +
+
+ +
+ @if ( + setupForm.controls.serverUrl.invalid && + setupForm.controls.serverUrl.touched + ) { +
+ @if (setupForm.controls.serverUrl.hasError("required")) { + Server Url is required + } @else if ( + setupForm.controls.serverUrl.hasError("insecureUrl") + ) { + URLs starting with 'http://' are considered insecure and are not + allowed in Splunk. Please use 'https://' instead. + } @else { + Invalid Url } - } @else { - - } - +
+ }
-
- -
- @if ( - setupForm.hasError("indexRequired") && - (setupForm.controls.index.touched || - setupForm.controls.indexOverride.touched) - ) { -
Index is required
- } -
-

Event Logs

-

- Choose the earliest Bitwarden event date to retrieve. The default is 1 - year before today's date.
- This only works for first time setup. Make sure you have no other - Bitwarden events to avoid duplications. -

-
- +

Splunk Index

+

Choose a Splunk index for the Bitwarden event logs.

+ +
+ +
+ @if ( + setupForm.hasError("indexRequired") && + (setupForm.controls.index.touched || + setupForm.controls.indexOverride.touched) + ) { +
Index is required
+ }
-
+ +

Event Logs

+

+ Choose the earliest Bitwarden event date to retrieve. The default is 1 + year before today's date.
+ This only works for first time setup. Make sure you have no other + Bitwarden events to avoid duplications. +

+
+ +
+
+ +
+
+
+ } @if (setupForm.invalid && setupForm.touched) {
; @@ -59,6 +61,7 @@ export class AppComponent { startDate: [""], index: [""], indexOverride: [""], + eventDeliveryMode: ["poll"], }, { validators: indexRequiredValidator(), @@ -104,6 +107,16 @@ export class AppComponent { } }); + // To hide polling setup input fields, keep boolean signal for push delivery mode + this.setupForm.controls.eventDeliveryMode.valueChanges + .pipe( + startWith(this.setupForm.controls.eventDeliveryMode.value), + takeUntilDestroyed(), + ) + .subscribe((eventDeliveryMode) => { + this.isPushEventDelivery.set(eventDeliveryMode === "push"); + }); + // Load indexes const indexesObservable = from(splunkService.getAllIndexes()).pipe( map((indexes) => indexes.map((index) => index.name)), From d89db9f4a998d7446271505406ebbab55a1481f4 Mon Sep 17 00:00:00 2001 From: Brad Deibert Date: Tue, 2 Jun 2026 17:27:18 -0700 Subject: [PATCH 4/8] add padding to polling inputs --- ui/src/app/app.component.html | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ui/src/app/app.component.html b/ui/src/app/app.component.html index 4ecfe6c..aaaf4df 100644 --- a/ui/src/app/app.component.html +++ b/ui/src/app/app.component.html @@ -108,13 +108,13 @@

Bitwarden Server Url

Self-hosted Bitwarden servers may need to reconfigure their installation's URL.

-
- +
+ Date: Wed, 3 Jun 2026 11:31:16 -0700 Subject: [PATCH 5/8] add text for HEC setup --- ui/src/app/app.component.html | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/ui/src/app/app.component.html b/ui/src/app/app.component.html index aaaf4df..546e1c5 100644 --- a/ui/src/app/app.component.html +++ b/ui/src/app/app.component.html @@ -33,7 +33,7 @@

Event Delivery Mode

id="poll" value="poll" /> - + @@ -44,10 +44,23 @@

Event Delivery Mode

id="push" value="push" /> - +
- @if (!isPushEventDelivery()) { + @if (isPushEventDelivery()) { +

+ To complete setup for receiving event logs with the HEC Event + Collector, follow the instructions in the + + Bitwarden Splunk documentation. +

+ } @else {

Bitwarden API Key

Your From e5855424d330721a7a5314ebfdcfeebb112513e0 Mon Sep 17 00:00:00 2001 From: Brad Deibert Date: Wed, 3 Jun 2026 16:27:10 -0700 Subject: [PATCH 6/8] remove validators for poll fields when push selected --- ui/src/app/app.component.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts index 650fd98..5c87587 100644 --- a/ui/src/app/app.component.ts +++ b/ui/src/app/app.component.ts @@ -114,7 +114,26 @@ export class AppComponent { takeUntilDestroyed(), ) .subscribe((eventDeliveryMode) => { - this.isPushEventDelivery.set(eventDeliveryMode === "push"); + const isPush = eventDeliveryMode === "push"; + this.isPushEventDelivery.set(isPush); + + // disable form validators when push is selected, reapply when polling is selected + if (isPush) { + this.setupForm.controls.clientId.clearValidators(); + this.setupForm.controls.clientSecret.clearValidators(); + this.setupForm.controls.serverUrl.clearValidators(); + this.setupForm.setValidators(null); + this.setupForm.controls.clientId.updateValueAndValidity(); + this.setupForm.controls.clientSecret.updateValueAndValidity(); + this.setupForm.controls.serverUrl.updateValueAndValidity(); + this.setupForm.updateValueAndValidity(); + } else { + this.setupForm.controls.clientId.setValidators(Validators.required); + this.setupForm.controls.clientSecret.setValidators( + Validators.required, + ); + this.setupForm.setValidators(indexRequiredValidator()); + } }); // Load indexes From e62e0df1872aae47b2ca04fdce580bb2bb0af08f Mon Sep 17 00:00:00 2001 From: Brad Deibert Date: Wed, 3 Jun 2026 16:39:35 -0700 Subject: [PATCH 7/8] update setup form logic to save push configuration --- ui/src/app/app.component.ts | 106 +++++++++++++++++------------- ui/src/models/bitwarden-splunk.ts | 5 +- ui/src/models/setup-form.ts | 1 + 3 files changed, 65 insertions(+), 47 deletions(-) diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts index 5c87587..66e1bb5 100644 --- a/ui/src/app/app.component.ts +++ b/ui/src/app/app.component.ts @@ -160,54 +160,12 @@ export class AppComponent { const formValue = this.setupForm.value as SetupForm; try { - // Store secrets - await this.bitwardenSplunkService.upsertApiKey( - formValue.clientId, - formValue.clientSecret, - ); - - // Update inputs.conf - const index = formValue.indexOverride - ? formValue.indexOverride - : formValue.index; - console.debug("Index", index); - await this.bitwardenSplunkService.updateInputsConfigurationFile({ - index, - }); - - // Update script.conf - let apiUrl: string; - let identityUrl: string; - if (this.isServerUrlBitwardenCloud(formValue.serverUrlType)) { - const serverHost = - formValue.serverUrlType === "bitwarden.com" - ? "bitwarden.com" - : "bitwarden.eu"; - apiUrl = `https://api.${serverHost}`; - identityUrl = `https://identity.${serverHost}`; + if (formValue.eventDeliveryMode === "poll") { + await this.submitPollingConfiguration(formValue); } else { - const containsProtocol = /^https?:\/\//.test(formValue.serverUrl); - const serverUrl = new URL( - containsProtocol - ? formValue.serverUrl - : "https://" + formValue.serverUrl, - ); - - if (!serverUrl.pathname.endsWith("/")) { - serverUrl.pathname = serverUrl.pathname + "/"; - } - - apiUrl = serverUrl.href + "api"; - identityUrl = serverUrl.href + "identity"; + await this.submitPushConfiguration(formValue); } - console.debug("Bitwarden urls", apiUrl, identityUrl); - await this.bitwardenSplunkService.updateScriptConfigurationFile({ - apiUrl, - identityUrl, - startDate: formValue.startDate, - }); - // Complete setup await this.bitwardenSplunkService.updateAppConfigurationFile(true); await this.bitwardenSplunkService.reloadApp(); @@ -226,6 +184,63 @@ export class AppComponent { } } + private async submitPollingConfiguration(formValue: SetupForm) { + // Store secrets + await this.bitwardenSplunkService.upsertApiKey( + formValue.clientId, + formValue.clientSecret, + ); + + // Update inputs.conf + const index = formValue.indexOverride + ? formValue.indexOverride + : formValue.index; + console.debug("Index", index); + await this.bitwardenSplunkService.updateInputsConfigurationFile({ + index, + }); + + // Update script.conf + let apiUrl: string; + let identityUrl: string; + if (this.isServerUrlBitwardenCloud(formValue.serverUrlType)) { + const serverHost = + formValue.serverUrlType === "bitwarden.com" + ? "bitwarden.com" + : "bitwarden.eu"; + apiUrl = `https://api.${serverHost}`; + identityUrl = `https://identity.${serverHost}`; + } else { + const containsProtocol = /^https?:\/\//.test(formValue.serverUrl); + const serverUrl = new URL( + containsProtocol + ? formValue.serverUrl + : "https://" + formValue.serverUrl, + ); + + if (!serverUrl.pathname.endsWith("/")) { + serverUrl.pathname = serverUrl.pathname + "/"; + } + + apiUrl = serverUrl.href + "api"; + identityUrl = serverUrl.href + "identity"; + } + + console.debug("Bitwarden urls", apiUrl, identityUrl); + await this.bitwardenSplunkService.updateScriptConfigurationFile({ + apiUrl, + identityUrl, + startDate: formValue.startDate, + eventDeliveryMode: formValue.eventDeliveryMode, + }); + } + + private async submitPushConfiguration(formValue: SetupForm) { + await this.bitwardenSplunkService.updateScriptConfigurationFile({ + eventDeliveryMode: formValue.eventDeliveryMode, + }); + } + private loadConfiguration(indexesObservable: Observable) { combineLatest([ indexesObservable, @@ -252,6 +267,7 @@ export class AppComponent { if ( scriptConfiguration !== undefined && + scriptConfiguration.apiUrl !== undefined && URL.canParse(scriptConfiguration.apiUrl) ) { const apiUrl = new URL(scriptConfiguration.apiUrl); diff --git a/ui/src/models/bitwarden-splunk.ts b/ui/src/models/bitwarden-splunk.ts index dce269c..97d5731 100644 --- a/ui/src/models/bitwarden-splunk.ts +++ b/ui/src/models/bitwarden-splunk.ts @@ -3,7 +3,8 @@ export type InputsConfiguration = { }; export type ScriptsConfiguration = { - apiUrl: string; - identityUrl: string; + apiUrl?: string; + identityUrl?: string; startDate?: string; + eventDeliveryMode?: "poll" | "push"; }; diff --git a/ui/src/models/setup-form.ts b/ui/src/models/setup-form.ts index ee8206b..170a334 100644 --- a/ui/src/models/setup-form.ts +++ b/ui/src/models/setup-form.ts @@ -8,4 +8,5 @@ export type SetupForm = { startDate: string; index: string; indexOverride: string; + eventDeliveryMode: "poll" | "push"; }; From 9e88a49ee532ab595b305e00f0a733181ee6348a Mon Sep 17 00:00:00 2001 From: Brad Deibert Date: Thu, 11 Jun 2026 15:52:24 -0700 Subject: [PATCH 8/8] expose HEC port on dev containers --- dev/docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/docker-compose.yml b/dev/docker-compose.yml index adfcc1b..ddbf352 100644 --- a/dev/docker-compose.yml +++ b/dev/docker-compose.yml @@ -8,6 +8,7 @@ services: ports: - "8001:8000" - "8089:8089" + - "8088:8088" environment: SPLUNK_GENERAL_TERMS: "--accept-sgt-current-at-splunk-com" SPLUNK_START_ARGS: "--accept-license" @@ -19,6 +20,7 @@ services: ports: - "8001:8000" - "8089:8089" + - "8088:8088" environment: SPLUNK_START_ARGS: "--accept-license" SPLUNK_PASSWORD: password