Skip to content

Commit d232ead

Browse files
committed
events: add option to configure webhook CA
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
1 parent f043a38 commit d232ead

File tree

7 files changed

+107
-15
lines changed

7 files changed

+107
-15
lines changed

authentik/events/api/notification_transports.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class Meta:
6363
"mode",
6464
"mode_verbose",
6565
"webhook_url",
66+
"webhook_ca",
6667
"webhook_mapping_body",
6768
"webhook_mapping_headers",
6869
"email_subject_prefix",
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Generated by Django 5.2.12 on 2026-03-10 10:40
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
("authentik_crypto", "0006_certificatekeypair_cert_expiry_and_more"),
11+
("authentik_events", "0016_alter_event_action"),
12+
]
13+
14+
operations = [
15+
migrations.AddField(
16+
model_name="notificationtransport",
17+
name="webhook_ca",
18+
field=models.ForeignKey(
19+
default=None,
20+
help_text="When set, the selected ceritifcate is used to validate the certificate of the webhook server.",
21+
null=True,
22+
on_delete=django.db.models.deletion.SET_DEFAULT,
23+
to="authentik_crypto.certificatekeypair",
24+
),
25+
),
26+
]

authentik/events/models.py

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
SESSION_KEY_IMPERSONATE_USER,
2929
)
3030
from authentik.core.models import ExpiringModel, Group, PropertyMapping, User
31+
from authentik.crypto.models import CertificateKeyPair
3132
from authentik.events.context_processors.base import get_context_processors
3233
from authentik.events.utils import (
3334
cleanse_dict,
@@ -41,6 +42,7 @@
4142
from authentik.lib.utils.errors import exception_to_dict
4243
from authentik.lib.utils.http import get_http_session
4344
from authentik.lib.utils.time import timedelta_from_string
45+
from authentik.outposts.docker_tls import DockerInlineTLS
4446
from authentik.policies.models import PolicyBindingModel
4547
from authentik.root.middleware import ClientIPMiddleware
4648
from authentik.root.ws.consumer import build_user_group
@@ -326,6 +328,16 @@ class NotificationTransport(TasksModel, SerializerModel):
326328
email_template = models.TextField(default=EmailTemplates.EVENT_NOTIFICATION)
327329

328330
webhook_url = models.TextField(blank=True, validators=[DomainlessURLValidator()])
331+
webhook_ca = models.ForeignKey(
332+
CertificateKeyPair,
333+
null=True,
334+
default=None,
335+
on_delete=models.SET_DEFAULT,
336+
help_text=_(
337+
"When set, the selected ceritifcate is used to "
338+
"validate the certificate of the webhook server."
339+
),
340+
)
329341
webhook_mapping_body = models.ForeignKey(
330342
"NotificationWebhookMapping",
331343
on_delete=models.SET_DEFAULT,
@@ -409,21 +421,29 @@ def send_webhook(self, notification: Notification) -> list[str]:
409421
notification=notification,
410422
)
411423
)
412-
try:
413-
response = get_http_session().post(
414-
self.webhook_url,
415-
json=default_body,
416-
headers=headers,
417-
)
418-
response.raise_for_status()
419-
except RequestException as exc:
420-
raise NotificationTransportError(
421-
exc.response.text if exc.response else str(exc)
422-
) from exc
423-
return [
424-
response.status_code,
425-
response.text,
426-
]
424+
425+
def send(**kwargs):
426+
try:
427+
response = get_http_session().post(
428+
self.webhook_url,
429+
json=default_body,
430+
headers=headers,
431+
**kwargs,
432+
)
433+
response.raise_for_status()
434+
except RequestException as exc:
435+
raise NotificationTransportError(
436+
exc.response.text if exc.response else str(exc)
437+
) from exc
438+
return [
439+
response.status_code,
440+
response.text,
441+
]
442+
443+
if self.webhook_ca:
444+
with DockerInlineTLS(self.webhook_ca) as tls:
445+
return send(verify=tls.verify)
446+
return send()
427447

428448
def send_webhook_slack(self, notification: Notification) -> list[str]:
429449
"""Send notification to slack or slack-compatible endpoints"""

authentik/outposts/docker_tls.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ def __init__(
2727
self.authentication_kp = authentication_kp
2828
self._paths = []
2929

30+
def __enter__(self):
31+
return self.write()
32+
33+
def __exit__(self, exc_type, exc, tb):
34+
self.cleanup()
35+
3036
def write_file(self, name: str, contents: str) -> str:
3137
"""Wrapper for mkstemp that uses fdopen"""
3238
path = Path(gettempdir(), name)

blueprints/schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8236,6 +8236,12 @@
82368236
"type": "string",
82378237
"title": "Webhook url"
82388238
},
8239+
"webhook_ca": {
8240+
"type": "string",
8241+
"format": "uuid",
8242+
"title": "Webhook ca",
8243+
"description": "When set, the selected ceritifcate is used to validate the certificate of the webhook server."
8244+
},
82398245
"webhook_mapping_body": {
82408246
"type": "string",
82418247
"format": "uuid",

schema.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43552,6 +43552,12 @@ components:
4355243552
webhook_url:
4355343553
type: string
4355443554
format: uri
43555+
webhook_ca:
43556+
type: string
43557+
format: uuid
43558+
nullable: true
43559+
description: When set, the selected ceritifcate is used to validate the
43560+
certificate of the webhook server.
4355543561
webhook_mapping_body:
4355643562
type: string
4355743563
format: uuid
@@ -43595,6 +43601,12 @@ components:
4359543601
webhook_url:
4359643602
type: string
4359743603
format: uri
43604+
webhook_ca:
43605+
type: string
43606+
format: uuid
43607+
nullable: true
43608+
description: When set, the selected ceritifcate is used to validate the
43609+
certificate of the webhook server.
4359843610
webhook_mapping_body:
4359943611
type: string
4360043612
format: uuid
@@ -49297,6 +49309,12 @@ components:
4929749309
webhook_url:
4929849310
type: string
4929949311
format: uri
49312+
webhook_ca:
49313+
type: string
49314+
format: uuid
49315+
nullable: true
49316+
description: When set, the selected ceritifcate is used to validate the
49317+
certificate of the webhook server.
4930049318
webhook_mapping_body:
4930149319
type: string
4930249320
format: uuid

web/src/admin/events/TransportForm.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import "#components/ak-switch-input";
33
import "#elements/forms/HorizontalFormElement";
44
import "#elements/forms/Radio";
55
import "#elements/forms/SearchSelect/index";
6+
import "#admin/common/ak-crypto-certificate-search";
67

78
import { DEFAULT_CONFIG } from "#common/api/config";
89

@@ -142,6 +143,20 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
142143
?required=${this.showWebhook}
143144
>
144145
</ak-hidden-text-input>
146+
<ak-form-element-horizontal
147+
?hidden=${!this.showWebhook}
148+
label=${msg("Webhook Certificate Authority")}
149+
name="webhookCa"
150+
>
151+
<ak-crypto-certificate-search
152+
.certificate=${this.instance?.webhookCa}
153+
></ak-crypto-certificate-search>
154+
<p class="pf-c-form__helper-text">
155+
${msg(
156+
"Keypair used to validate the certificate of the webhook endpoint. When not configured, the standard CA bundle is used.",
157+
)}
158+
</p>
159+
</ak-form-element-horizontal>
145160
<ak-form-element-horizontal
146161
?hidden=${!this.showWebhook}
147162
label=${msg("Webhook Body Mapping")}

0 commit comments

Comments
 (0)