From 53e2b2c784f25a0b637e1df2145c1a9affc42723 Mon Sep 17 00:00:00 2001 From: Jens L Date: Sun, 23 May 2021 20:29:34 +0200 Subject: [PATCH 1/8] Prometheus metrics (#914) * admin: add worker metrics Signed-off-by: Jens Langhammer * admin: add version metrics Signed-off-by: Jens Langhammer * events: add gauge for system tasks Signed-off-by: Jens Langhammer * outposts: add gauge for last hello and connection status Signed-off-by: Jens Langhammer * root: re-add prometheus metrics to database Signed-off-by: Jens Langhammer * root: allow access to metrics without credentials when debug is on Signed-off-by: Jens Langhammer * root: add UpdatingGauge to auto-set value on load Signed-off-by: Jens Langhammer * flows: add metrics for cache and building Signed-off-by: Jens Langhammer * policies: add metrics for policy engine Signed-off-by: Jens Langhammer * events: add histogram for task durations Signed-off-by: Jens Langhammer * events: revert to gauge because values are updated on export view Signed-off-by: Jens Langhammer * core: add gauge to count all models Signed-off-by: Jens Langhammer * events: add metrics for events Signed-off-by: Jens Langhammer --- authentik/admin/api/tasks.py | 2 +- authentik/admin/api/workers.py | 6 ++++- authentik/admin/tasks.py | 20 ++++++++++++++- authentik/core/apps.py | 13 ++++++++++ authentik/core/signals.py | 13 +++++++++- authentik/events/apps.py | 12 +++++++++ authentik/events/models.py | 17 ++++++++++++- authentik/events/monitored_tasks.py | 38 ++++++++++++++++++++++++++--- authentik/flows/planner.py | 18 +++++++++++++- authentik/outposts/channels.py | 30 +++++++++++++++++++++++ authentik/policies/engine.py | 20 ++++++++++++++- authentik/policies/models.py | 26 ++++++++++++++++---- authentik/policies/process.py | 22 ++++++++++++++++- authentik/root/monitoring.py | 33 +++++++++++++++++++++++-- authentik/root/settings.py | 6 ++++- schema.yml | 2 +- 16 files changed, 258 insertions(+), 20 deletions(-) diff --git a/authentik/admin/api/tasks.py b/authentik/admin/api/tasks.py index cfce015ee..dd6932cc2 100644 --- a/authentik/admin/api/tasks.py +++ b/authentik/admin/api/tasks.py @@ -22,7 +22,7 @@ class TaskSerializer(PassiveSerializer): task_name = CharField() task_description = CharField() - task_finish_timestamp = DateTimeField(source="finish_timestamp") + task_finish_timestamp = DateTimeField(source="finish_time") status = ChoiceField( source="result.status.name", diff --git a/authentik/admin/api/workers.py b/authentik/admin/api/workers.py index 4433fecd9..ff9b7c5e2 100644 --- a/authentik/admin/api/workers.py +++ b/authentik/admin/api/workers.py @@ -1,5 +1,6 @@ """authentik administration overview""" from drf_spectacular.utils import extend_schema, inline_serializer +from prometheus_client import Gauge from rest_framework.fields import IntegerField from rest_framework.permissions import IsAdminUser from rest_framework.request import Request @@ -8,6 +9,8 @@ from rest_framework.views import APIView from authentik.root.celery import CELERY_APP +GAUGE_WORKERS = Gauge("authentik_admin_workers", "Currently connected workers") + class WorkerView(APIView): """Get currently connected worker count.""" @@ -19,4 +22,5 @@ class WorkerView(APIView): ) def get(self, request: Request) -> Response: """Get currently connected worker count.""" - return Response({"count": len(CELERY_APP.control.ping(timeout=0.5))}) + count = len(CELERY_APP.control.ping(timeout=0.5)) + return Response({"count": count}) diff --git a/authentik/admin/tasks.py b/authentik/admin/tasks.py index dcb52601a..26fe1bbe3 100644 --- a/authentik/admin/tasks.py +++ b/authentik/admin/tasks.py @@ -1,13 +1,15 @@ """authentik admin tasks""" import re +from os import environ from django.core.cache import cache from django.core.validators import URLValidator from packaging.version import parse +from prometheus_client import Info from requests import RequestException, get from structlog.stdlib import get_logger -from authentik import __version__ +from authentik import ENV_GIT_HASH_KEY, __version__ from authentik.events.models import Event, EventAction from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus from authentik.root.celery import CELERY_APP @@ -17,6 +19,18 @@ VERSION_CACHE_KEY = "authentik_latest_version" VERSION_CACHE_TIMEOUT = 8 * 60 * 60 # 8 hours # Chop of the first ^ because we want to search the entire string URL_FINDER = URLValidator.regex.pattern[1:] +PROM_INFO = Info("authentik_version", "Currently running authentik version") + + +def _set_prom_info(): + """Set prometheus info for version""" + PROM_INFO.info( + { + "version": __version__, + "latest": cache.get(VERSION_CACHE_KEY, ""), + "build_hash": environ.get(ENV_GIT_HASH_KEY, ""), + } + ) @CELERY_APP.task(bind=True, base=MonitoredTask) @@ -36,6 +50,7 @@ def update_latest_version(self: MonitoredTask): TaskResultStatus.SUCCESSFUL, ["Successfully updated latest Version"] ) ) + _set_prom_info() # Check if upstream version is newer than what we're running, # and if no event exists yet, create one. local_version = parse(__version__) @@ -53,3 +68,6 @@ def update_latest_version(self: MonitoredTask): except (RequestException, IndexError) as exc: cache.set(VERSION_CACHE_KEY, "0.0.0", VERSION_CACHE_TIMEOUT) self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc)) + + +_set_prom_info() diff --git a/authentik/core/apps.py b/authentik/core/apps.py index fe5660cca..ef222669f 100644 --- a/authentik/core/apps.py +++ b/authentik/core/apps.py @@ -2,6 +2,10 @@ from importlib import import_module from django.apps import AppConfig +from django.db import ProgrammingError + +from authentik.core.signals import GAUGE_MODELS +from authentik.lib.utils.reflection import get_apps class AuthentikCoreConfig(AppConfig): @@ -15,3 +19,12 @@ class AuthentikCoreConfig(AppConfig): def ready(self): import_module("authentik.core.signals") import_module("authentik.core.managed") + try: + for app in get_apps(): + for model in app.get_models(): + GAUGE_MODELS.labels( + model_name=model._meta.model_name, + app=model._meta.app_label, + ).set(model.objects.count()) + except ProgrammingError: + pass diff --git a/authentik/core/signals.py b/authentik/core/signals.py index ec7675fe2..089ed7bb1 100644 --- a/authentik/core/signals.py +++ b/authentik/core/signals.py @@ -1,20 +1,31 @@ """authentik core signals""" from django.core.cache import cache from django.core.signals import Signal +from django.db.models import Model from django.db.models.signals import post_save from django.dispatch import receiver +from prometheus_client import Gauge # Arguments: user: User, password: str password_changed = Signal() +GAUGE_MODELS = Gauge( + "authentik_models", "Count of various objects", ["model_name", "app"] +) + @receiver(post_save) # pylint: disable=unused-argument -def post_save_application(sender, instance, created: bool, **_): +def post_save_application(sender: type[Model], instance, created: bool, **_): """Clear user's application cache upon application creation""" from authentik.core.api.applications import user_app_cache_key from authentik.core.models import Application + GAUGE_MODELS.labels( + model_name=sender._meta.model_name, + app=sender._meta.app_label, + ).set(sender.objects.count()) + if sender != Application: return if not created: # pragma: no cover diff --git a/authentik/events/apps.py b/authentik/events/apps.py index ad9e7d205..f0eb77c9c 100644 --- a/authentik/events/apps.py +++ b/authentik/events/apps.py @@ -1,7 +1,10 @@ """authentik events app""" +from datetime import timedelta from importlib import import_module from django.apps import AppConfig +from django.db import ProgrammingError +from django.utils.timezone import datetime class AuthentikEventsConfig(AppConfig): @@ -13,3 +16,12 @@ class AuthentikEventsConfig(AppConfig): def ready(self): import_module("authentik.events.signals") + try: + from authentik.events.models import Event + + date_from = datetime.now() - timedelta(days=1) + + for event in Event.objects.filter(created__gte=date_from): + event._set_prom_metrics() + except ProgrammingError: + pass diff --git a/authentik/events/models.py b/authentik/events/models.py index cc41586af..55bb64d98 100644 --- a/authentik/events/models.py +++ b/authentik/events/models.py @@ -11,6 +11,7 @@ from django.http import HttpRequest from django.utils.timezone import now from django.utils.translation import gettext as _ from geoip2.errors import GeoIP2Error +from prometheus_client import Gauge from requests import RequestException, post from structlog.stdlib import get_logger @@ -28,6 +29,11 @@ from authentik.policies.models import PolicyBindingModel from authentik.stages.email.utils import TemplateEmailMessage LOGGER = get_logger("authentik.events") +GAUGE_EVENTS = Gauge( + "authentik_events", + "Events in authentik", + ["action", "user_username", "app", "client_ip"], +) def default_event_duration(): @@ -169,6 +175,14 @@ class Event(ExpiringModel): except GeoIP2Error as exc: LOGGER.warning("Failed to add geoIP Data to event", exc=exc) + def _set_prom_metrics(self): + GAUGE_EVENTS.labels( + action=self.action, + user_username=self.user.get("username"), + app=self.app, + client_ip=self.client_ip, + ).set(self.created.timestamp()) + def save(self, *args, **kwargs): if self._state.adding: LOGGER.debug( @@ -178,7 +192,8 @@ class Event(ExpiringModel): client_ip=self.client_ip, user=self.user, ) - return super().save(*args, **kwargs) + super().save(*args, **kwargs) + self._set_prom_metrics() @property def summary(self) -> str: diff --git a/authentik/events/monitored_tasks.py b/authentik/events/monitored_tasks.py index 456eb2468..d3a269aed 100644 --- a/authentik/events/monitored_tasks.py +++ b/authentik/events/monitored_tasks.py @@ -2,14 +2,22 @@ from dataclasses import dataclass, field from datetime import datetime from enum import Enum +from timeit import default_timer from traceback import format_tb from typing import Any, Optional from celery import Task from django.core.cache import cache +from prometheus_client import Gauge from authentik.events.models import Event, EventAction +GAUGE_TASKS = Gauge( + "authentik_system_tasks", + "System tasks and their status", + ["task_name", "task_uid", "status"], +) + class TaskResultStatus(Enum): """Possible states of tasks""" @@ -43,7 +51,9 @@ class TaskInfo: """Info about a task run""" task_name: str - finish_timestamp: datetime + start_timestamp: float + finish_timestamp: float + finish_time: datetime result: TaskResult @@ -73,12 +83,25 @@ class TaskInfo: """Delete task info from cache""" return cache.delete(f"task_{self.task_name}") + def set_prom_metrics(self): + """Update prometheus metrics""" + start = default_timer() + if hasattr(self, "start_timestamp"): + start = self.start_timestamp + duration = max(self.finish_timestamp - start, 0) + GAUGE_TASKS.labels( + task_name=self.task_name, + task_uid=self.result.uid or "", + status=self.result.status, + ).set(duration) + def save(self, timeout_hours=6): """Save task into cache""" key = f"task_{self.task_name}" if self.result.uid: key += f"_{self.result.uid}" self.task_name += f"_{self.result.uid}" + self.set_prom_metrics() cache.set(key, self, timeout=timeout_hours * 60 * 60) @@ -98,6 +121,7 @@ class MonitoredTask(Task): self._uid = None self._result = TaskResult(status=TaskResultStatus.ERROR, messages=[]) self.result_timeout_hours = 6 + self.start = default_timer() def set_uid(self, uid: str): """Set UID, so in the case of an unexpected error its saved correctly""" @@ -117,7 +141,9 @@ class MonitoredTask(Task): TaskInfo( task_name=self.__name__, task_description=self.__doc__, - finish_timestamp=datetime.now(), + start_timestamp=self.start, + finish_timestamp=default_timer(), + finish_time=datetime.now(), result=self._result, task_call_module=self.__module__, task_call_func=self.__name__, @@ -133,7 +159,9 @@ class MonitoredTask(Task): TaskInfo( task_name=self.__name__, task_description=self.__doc__, - finish_timestamp=datetime.now(), + start_timestamp=self.start, + finish_timestamp=default_timer(), + finish_time=datetime.now(), result=self._result, task_call_module=self.__module__, task_call_func=self.__name__, @@ -151,3 +179,7 @@ class MonitoredTask(Task): def run(self, *args, **kwargs): raise NotImplementedError + + +for task in TaskInfo.all().values(): + task.set_prom_metrics() diff --git a/authentik/flows/planner.py b/authentik/flows/planner.py index f66f02dc6..718f64cfa 100644 --- a/authentik/flows/planner.py +++ b/authentik/flows/planner.py @@ -4,6 +4,7 @@ from typing import Any, Optional from django.core.cache import cache from django.http import HttpRequest +from prometheus_client import Histogram from sentry_sdk.hub import Hub from sentry_sdk.tracing import Span from structlog.stdlib import BoundLogger, get_logger @@ -14,6 +15,7 @@ from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableExce from authentik.flows.markers import ReevaluateMarker, StageMarker from authentik.flows.models import Flow, FlowStageBinding, Stage from authentik.policies.engine import PolicyEngine +from authentik.root.monitoring import UpdatingGauge LOGGER = get_logger() PLAN_CONTEXT_PENDING_USER = "pending_user" @@ -21,6 +23,16 @@ PLAN_CONTEXT_SSO = "is_sso" PLAN_CONTEXT_REDIRECT = "redirect" PLAN_CONTEXT_APPLICATION = "application" PLAN_CONTEXT_SOURCE = "source" +GAUGE_FLOWS_CACHED = UpdatingGauge( + "authentik_flows_cached", + "Cached flows", + update_func=lambda: len(cache.keys("flow_*")), +) +HIST_FLOWS_PLAN_TIME = Histogram( + "authentik_flows_plan_time", + "Duration to build a plan for a flow", + ["flow_slug"], +) def cache_key(flow: Flow, user: Optional[User] = None) -> str: @@ -146,6 +158,7 @@ class FlowPlanner: ) plan = self._build_plan(user, request, default_context) cache.set(cache_key(self.flow, user), plan) + GAUGE_FLOWS_CACHED.update() if not plan.stages and not self.allow_empty_flows: raise EmptyFlowException() return plan @@ -158,7 +171,9 @@ class FlowPlanner: ) -> FlowPlan: """Build flow plan by checking each stage in their respective order and checking the applied policies""" - with Hub.current.start_span(op="flow.planner.build_plan") as span: + with Hub.current.start_span( + op="flow.planner.build_plan" + ) as span, HIST_FLOWS_PLAN_TIME.labels(flow_slug=self.flow.slug).time(): span: Span span.set_data("flow", self.flow) span.set_data("user", user) @@ -202,6 +217,7 @@ class FlowPlanner: marker = ReevaluateMarker(binding=binding, user=user) if stage: plan.append(stage, marker) + HIST_FLOWS_PLAN_TIME.labels(flow_slug=self.flow.slug) self._logger.debug( "f(plan): finished building", ) diff --git a/authentik/outposts/channels.py b/authentik/outposts/channels.py index d0dad47b8..3e46dceed 100644 --- a/authentik/outposts/channels.py +++ b/authentik/outposts/channels.py @@ -8,11 +8,21 @@ from channels.exceptions import DenyConnection from dacite import from_dict from dacite.data import Data from guardian.shortcuts import get_objects_for_user +from prometheus_client import Gauge from structlog.stdlib import get_logger from authentik.core.channels import AuthJsonConsumer from authentik.outposts.models import OUTPOST_HELLO_INTERVAL, Outpost, OutpostState +GAUGE_OUTPOSTS_CONNECTED = Gauge( + "authentik_outposts_connected", "Currently connected outposts", ["outpost", "uid"] +) +GAUGE_OUTPOSTS_LAST_UPDATE = Gauge( + "authentik_outposts_last_update", + "Last update from any outpost", + ["outpost", "uid", "version"], +) + LOGGER = get_logger() @@ -44,6 +54,8 @@ class OutpostConsumer(AuthJsonConsumer): last_uid: Optional[str] = None + first_msg = False + def connect(self): super().connect() uuid = self.scope["url_route"]["kwargs"]["pk"] @@ -68,6 +80,10 @@ class OutpostConsumer(AuthJsonConsumer): if self.channel_name in state.channel_ids: state.channel_ids.remove(self.channel_name) state.save() + GAUGE_OUTPOSTS_CONNECTED.labels( + outpost=self.outpost.name, + uid=self.last_uid, + ).dec() LOGGER.debug( "removed outpost instance from cache", outpost=self.outpost, @@ -78,15 +94,29 @@ class OutpostConsumer(AuthJsonConsumer): msg = from_dict(WebsocketMessage, content) uid = msg.args.get("uuid", self.channel_name) self.last_uid = uid + state = OutpostState.for_instance_uid(self.outpost, uid) if self.channel_name not in state.channel_ids: state.channel_ids.append(self.channel_name) state.last_seen = datetime.now() + + if not self.first_msg: + GAUGE_OUTPOSTS_CONNECTED.labels( + outpost=self.outpost.name, + uid=self.last_uid, + ).inc() + self.first_msg = True + if msg.instruction == WebsocketMessageInstruction.HELLO: state.version = msg.args.get("version", None) state.build_hash = msg.args.get("buildHash", "") elif msg.instruction == WebsocketMessageInstruction.ACK: return + GAUGE_OUTPOSTS_LAST_UPDATE.labels( + outpost=self.outpost.name, + uid=self.last_uid or "", + version=state.version or "", + ).set_to_current_time() state.save(timeout=OUTPOST_HELLO_INTERVAL * 1.5) response = WebsocketMessage(instruction=WebsocketMessageInstruction.ACK) diff --git a/authentik/policies/engine.py b/authentik/policies/engine.py index b8ac67fa2..99f27b1d5 100644 --- a/authentik/policies/engine.py +++ b/authentik/policies/engine.py @@ -5,6 +5,7 @@ from typing import Iterator, Optional from django.core.cache import cache from django.http import HttpRequest +from prometheus_client import Histogram from sentry_sdk.hub import Hub from sentry_sdk.tracing import Span from structlog.stdlib import BoundLogger, get_logger @@ -18,8 +19,19 @@ from authentik.policies.models import ( ) from authentik.policies.process import PolicyProcess, cache_key from authentik.policies.types import PolicyRequest, PolicyResult +from authentik.root.monitoring import UpdatingGauge CURRENT_PROCESS = current_process() +GAUGE_POLICIES_CACHED = UpdatingGauge( + "authentik_policies_cached", + "Cached Policies", + update_func=lambda: len(cache.keys("policy_*")), +) +HIST_POLICIES_BUILD_TIME = Histogram( + "authentik_policies_build_time", + "Execution times complete policy result to an object", + ["object_name", "object_type", "user"], +) class PolicyProcessInfo: @@ -92,7 +104,13 @@ class PolicyEngine: def build(self) -> "PolicyEngine": """Build wrapper which monitors performance""" - with Hub.current.start_span(op="policy.engine.build") as span: + with Hub.current.start_span( + op="policy.engine.build" + ) as span, HIST_POLICIES_BUILD_TIME.labels( + object_name=self.__pbm, + object_type=f"{self.__pbm._meta.app_label}.{self.__pbm._meta.model_name}", + user=self.request.user, + ).time(): span: Span span.set_data("pbm", self.__pbm) span.set_data("request", self.request) diff --git a/authentik/policies/models.py b/authentik/policies/models.py index 084a0ca9f..380e6eaa6 100644 --- a/authentik/policies/models.py +++ b/authentik/policies/models.py @@ -111,14 +111,30 @@ class PolicyBinding(SerializerModel): return PolicyBindingSerializer - def __str__(self) -> str: - suffix = "" + @property + def target_type(self) -> str: + """Get the target type this binding is applied to""" if self.policy: - suffix = f"Policy {self.policy.name}" + return "policy" if self.group: - suffix = f"Group {self.group.name}" + return "group" if self.user: - suffix = f"User {self.user.name}" + return "user" + return "invalid" + + @property + def target_name(self) -> str: + """Get the target name this binding is applied to""" + if self.policy: + return self.policy.name + if self.group: + return self.group.name + if self.user: + return self.user.name + return "invalid" + + def __str__(self) -> str: + suffix = f"{self.target_type.title()} {self.target_name}" try: return f"Binding from {self.target} #{self.order} to {suffix}" except PolicyBinding.target.RelatedObjectDoesNotExist: # pylint: disable=no-member diff --git a/authentik/policies/process.py b/authentik/policies/process.py index 263881ca5..cdf859c7f 100644 --- a/authentik/policies/process.py +++ b/authentik/policies/process.py @@ -5,6 +5,7 @@ from traceback import format_tb from typing import Optional from django.core.cache import cache +from prometheus_client import Histogram from sentry_sdk.hub import Hub from sentry_sdk.tracing import Span from structlog.stdlib import get_logger @@ -19,6 +20,18 @@ TRACEBACK_HEADER = "Traceback (most recent call last):\n" FORK_CTX = get_context("fork") PROCESS_CLASS = FORK_CTX.Process +HIST_POLICIES_EXECUTION_TIME = Histogram( + "authentik_policies_execution_time", + "Execution times for single policies", + [ + "binding_order", + "binding_target_type", + "binding_target_name", + "object_name", + "object_type", + "user", + ], +) def cache_key(binding: PolicyBinding, request: PolicyRequest) -> str: @@ -121,7 +134,14 @@ class PolicyProcess(PROCESS_CLASS): """Task wrapper to run policy checking""" with Hub.current.start_span( op="policy.process.execute", - ) as span: + ) as span, HIST_POLICIES_EXECUTION_TIME.labels( + binding_order=self.binding.order, + binding_target_type=self.binding.target_type, + binding_target_name=self.binding.target_name, + object_name=self.request.obj, + object_type=f"{self.request.obj._meta.app_label}.{self.request.obj._meta.model_name}", + user=str(self.request.user), + ).time(): span: Span span.set_data("policy", self.binding.policy) span.set_data("request", self.request) diff --git a/authentik/root/monitoring.py b/authentik/root/monitoring.py index 755ff9842..c7b97ac94 100644 --- a/authentik/root/monitoring.py +++ b/authentik/root/monitoring.py @@ -1,5 +1,6 @@ """Metrics view""" from base64 import b64encode +from typing import Callable from django.conf import settings from django.db import connections @@ -8,8 +9,30 @@ from django.http import HttpRequest, HttpResponse from django.views import View from django_prometheus.exports import ExportToDjangoView from django_redis import get_redis_connection +from prometheus_client import Gauge from redis.exceptions import RedisError +from authentik.admin.api.workers import GAUGE_WORKERS +from authentik.events.monitored_tasks import TaskInfo +from authentik.root.celery import CELERY_APP + + +class UpdatingGauge(Gauge): + """Gauge which fetches its own value from an update function. + + Update function is called on instantiate""" + + def __init__(self, *args, update_func: Callable, **kwargs): + super().__init__(*args, **kwargs) + self._update_func = update_func + self.update() + + def update(self): + """Set value from update function""" + val = self._update_func() + if val: + self.set(val) + class MetricsView(View): """Wrapper around ExportToDjangoView, using http-basic auth""" @@ -20,12 +43,18 @@ class MetricsView(View): auth_type, _, given_credentials = auth_header.partition(" ") credentials = f"monitor:{settings.SECRET_KEY}" expected = b64encode(str.encode(credentials)).decode() - - if auth_type != "Basic" or given_credentials != expected: + authed = auth_type == "Basic" and given_credentials == expected + if not authed and not settings.DEBUG: response = HttpResponse(status=401) response["WWW-Authenticate"] = 'Basic realm="authentik-monitoring"' return response + count = len(CELERY_APP.control.ping(timeout=0.5)) + GAUGE_WORKERS.set(count) + + for task in TaskInfo.all().values(): + task.set_prom_metrics() + return ExportToDjangoView(request) diff --git a/authentik/root/settings.py b/authentik/root/settings.py index 039e6bef6..3b1e92be2 100644 --- a/authentik/root/settings.py +++ b/authentik/root/settings.py @@ -256,7 +256,7 @@ CHANNEL_LAYERS = { DATABASES = { "default": { - "ENGINE": "django.db.backends.postgresql", + "ENGINE": "django_prometheus.db.backends.postgresql", "HOST": CONFIG.y("postgresql.host"), "NAME": CONFIG.y("postgresql.name"), "USER": CONFIG.y("postgresql.user"), @@ -334,6 +334,10 @@ CELERY_RESULT_BACKEND = ( DBBACKUP_STORAGE = "django.core.files.storage.FileSystemStorage" DBBACKUP_STORAGE_OPTIONS = {"location": "./backups" if DEBUG else "/backups"} DBBACKUP_FILENAME_TEMPLATE = "authentik-backup-{datetime}.sql" +DBBACKUP_CONNECTOR_MAPPING = { + "django_prometheus.db.backends.postgresql": "dbbackup.db.postgresql.PgDumpConnector", +} + if CONFIG.y("postgresql.s3_backup"): DBBACKUP_STORAGE = "storages.backends.s3boto3.S3Boto3Storage" DBBACKUP_STORAGE_OPTIONS = { diff --git a/schema.yml b/schema.yml index a1446dc34..3b0837b7a 100644 --- a/schema.yml +++ b/schema.yml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: title: authentik - version: 2021.5.3 + version: 2021.5.4 description: Making authentication simple. contact: email: hello@beryju.org From c98e4196bd27eb901e27f7b435cc964aa74a8a8c Mon Sep 17 00:00:00 2001 From: CuBiC Date: Sun, 23 May 2021 22:49:31 +0200 Subject: [PATCH 2/8] website/docs: ingress nginx auth headers (#916) Extend example how to pass through auth headers from authentik if using ingress nginx as forward auth. --- website/docs/outposts/proxy.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/outposts/proxy.mdx b/website/docs/outposts/proxy.mdx index 08e04504a..e12ca02a4 100644 --- a/website/docs/outposts/proxy.mdx +++ b/website/docs/outposts/proxy.mdx @@ -90,6 +90,7 @@ metadata: annotations: nginx.ingress.kubernetes.io/auth-url: http://*external host that you configured in authentik*:4180/akprox/auth?nginx nginx.ingress.kubernetes.io/auth-signin: http://*external host that you configured in authentik*:4180/akprox/start?rd=$escaped_request_uri + nginx.ingress.kubernetes.io/auth-response-headers: X-Auth-Username,X-Forwarded-Email,X-Forwarded-Preferred-Username,X-Forwarded-User nginx.ingress.kubernetes.io/auth-snippet: | proxy_set_header X-Forwarded-Host $http_host; ``` From 2ec4b4ec98ec41fbd3a937fb927768aeda006e6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 12:48:23 +0200 Subject: [PATCH 3/8] build(deps): bump django-guardian from 2.3.0 to 2.4.0 (#923) --- Pipfile.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 6fd177258..e9bc7ee70 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -124,10 +124,10 @@ }, "botocore": { "hashes": [ - "sha256:466ab5eac5e5735d573e83e84194585cac4e804d2b91b7bbe0351bcaff10df32", - "sha256:b2a71043378687dc891997669830e8b61eaea656981059dbd4898825659df639" + "sha256:37105b9434d73f9c4d4960ee54c8eb129120f4c6681eb16edf483f03c5e2326d", + "sha256:e74775f9e64e975787d76390fc5ac5aba875d726bb9ece3b7bd900205b430389" ], - "version": "==1.20.77" + "version": "==1.20.78" }, "cachetools": { "hashes": [ @@ -342,11 +342,11 @@ }, "django-guardian": { "hashes": [ - "sha256:0e70706c6cda88ddaf8849bddb525b8df49de05ba0798d4b3506049f0d95cbc8", - "sha256:ed2de26e4defb800919c5749fb1bbe370d72829fbd72895b6cf4f7f1a7607e1b" + "sha256:440ca61358427e575323648b25f8384739e54c38b3d655c81d75e0cd0d61b697", + "sha256:c58a68ae76922d33e6bdc0e69af1892097838de56e93e78a8361090bcd9f89a0" ], "index": "pypi", - "version": "==2.3.0" + "version": "==2.4.0" }, "django-model-utils": { "hashes": [ @@ -574,10 +574,10 @@ }, "kombu": { "hashes": [ - "sha256:6dc509178ac4269b0e66ab4881f70a2035c33d3a622e20585f965986a5182006", - "sha256:f4965fba0a4718d47d470beeb5d6446e3357a62402b16c510b6a2f251e05ac3c" + "sha256:01481d99f4606f6939cdc9b637264ed353ee9e3e4f62cfb582324142c41a572d", + "sha256:e2dedd8a86c9077c350555153825a31e456a0dc20c15d5751f00137ec9c75f0a" ], - "version": "==5.0.2" + "version": "==5.1.0" }, "kubernetes": { "hashes": [ @@ -1151,10 +1151,10 @@ }, "websocket-client": { "hashes": [ - "sha256:5051b38a2f4c27fbd7ca077ebb23ec6965a626ded5a95637f36be1b35b6c4f81", - "sha256:57f876f1af4731cacb806cf54d02f5fbf75dee796053b9a5b94fd7c1d9621db9" + "sha256:3e2bf58191d4619b161389a95bdce84ce9e0b24eb8107e7e590db682c2d0ca81", + "sha256:abf306dc6351dcef07f4d40453037e51cc5d9da2ef60d0fc5d0fe3bcda255372" ], - "version": "==1.0.0" + "version": "==1.0.1" }, "websockets": { "hashes": [ From f887850b956793d222763293f0758a72eb3ad939 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 12:48:45 +0200 Subject: [PATCH 4/8] build(deps): bump github.com/getsentry/sentry-go in /outpost (#922) --- outpost/go.mod | 4 ++-- outpost/go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/outpost/go.mod b/outpost/go.mod index a6bbf30a4..4d9254fb5 100644 --- a/outpost/go.mod +++ b/outpost/go.mod @@ -5,7 +5,7 @@ go 1.16 require ( github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/coreos/go-oidc v2.2.1+incompatible - github.com/getsentry/sentry-go v0.10.0 + github.com/getsentry/sentry-go v0.11.0 github.com/go-ldap/ldap/v3 v3.3.0 github.com/go-openapi/analysis v0.20.1 // indirect github.com/go-openapi/errors v0.20.0 // indirect @@ -38,7 +38,7 @@ require ( go.mongodb.org/mongo-driver v1.5.2 // indirect golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect - golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558 + golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558 // indirect golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 // indirect google.golang.org/appengine v1.6.7 // indirect gopkg.in/ini.v1 v1.62.0 // indirect diff --git a/outpost/go.sum b/outpost/go.sum index 8ce40262d..1984e11b7 100644 --- a/outpost/go.sum +++ b/outpost/go.sum @@ -130,8 +130,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= -github.com/getsentry/sentry-go v0.10.0 h1:6gwY+66NHKqyZrdi6O2jGdo7wGdo9b3B69E01NFgT5g= -github.com/getsentry/sentry-go v0.10.0/go.mod h1:kELm/9iCblqUYh+ZRML7PNdCvEuw24wBvJPYyi86cws= +github.com/getsentry/sentry-go v0.11.0 h1:qro8uttJGvNAMr5CLcFI9CHR0aDzXl0Vs3Pmw/oTPg8= +github.com/getsentry/sentry-go v0.11.0/go.mod h1:KBQIxiZAetw62Cj8Ri964vAEWVdgfaUCn30Q3bCvANo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= From b8f98881fa5799c98fc1e25370e7606b6d997eeb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 12:48:53 +0200 Subject: [PATCH 5/8] build(deps): bump rollup from 2.48.0 to 2.49.0 in /web (#919) --- web/package-lock.json | 14 +++++++------- web/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 37f58f509..9682ea6b8 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -48,7 +48,7 @@ "lit-html": "^1.4.1", "moment": "^2.29.1", "rapidoc": "^9.0.0", - "rollup": "^2.48.0", + "rollup": "^2.49.0", "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-copy": "^3.4.0", "rollup-plugin-cssimport": "^1.0.2", @@ -6411,9 +6411,9 @@ } }, "node_modules/rollup": { - "version": "2.48.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.48.0.tgz", - "integrity": "sha512-wl9ZSSSsi5579oscSDYSzGn092tCS076YB+TQrzsGuSfYyJeep8eEWj0eaRjuC5McuMNmcnR8icBqiE/FWNB1A==", + "version": "2.49.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.49.0.tgz", + "integrity": "sha512-UnrCjMXICx9q0jF8L7OYs7LPk95dW0U5UYp/VANnWqfuhyr66FWi/YVlI34Oy8Tp4ZGLcaUDt4APJm80b9oPWQ==", "bin": { "rollup": "dist/bin/rollup" }, @@ -12914,9 +12914,9 @@ } }, "rollup": { - "version": "2.48.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.48.0.tgz", - "integrity": "sha512-wl9ZSSSsi5579oscSDYSzGn092tCS076YB+TQrzsGuSfYyJeep8eEWj0eaRjuC5McuMNmcnR8icBqiE/FWNB1A==", + "version": "2.49.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.49.0.tgz", + "integrity": "sha512-UnrCjMXICx9q0jF8L7OYs7LPk95dW0U5UYp/VANnWqfuhyr66FWi/YVlI34Oy8Tp4ZGLcaUDt4APJm80b9oPWQ==", "requires": { "fsevents": "~2.3.1" } diff --git a/web/package.json b/web/package.json index a2503c608..d4827a397 100644 --- a/web/package.json +++ b/web/package.json @@ -77,7 +77,7 @@ "lit-html": "^1.4.1", "moment": "^2.29.1", "rapidoc": "^9.0.0", - "rollup": "^2.48.0", + "rollup": "^2.49.0", "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-copy": "^3.4.0", "rollup-plugin-cssimport": "^1.0.2", From 0e89353ac9463ec7c754023337ae1968440c0241 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 12:49:03 +0200 Subject: [PATCH 6/8] build(deps): bump boto3 from 1.17.77 to 1.17.78 (#924) --- Pipfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index e9bc7ee70..8b176ae28 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -116,11 +116,11 @@ }, "boto3": { "hashes": [ - "sha256:02835bcad77a5fda1fc376a824323779301ddf88f04a0ac16044d980f350c4a3", - "sha256:b434170484348b870e3624069ca577d38e52ace0229d0619d8368454bb66ad3b" + "sha256:1a87855123df1f18081a5fb8c1abde28d0096a03f6f3ebb06bcfb77cdffdae5e", + "sha256:2a5caee63d45fbdcc85e710c7f4146112f5d10b22fd0176643d2f2914cce54df" ], "index": "pypi", - "version": "==1.17.77" + "version": "==1.17.78" }, "botocore": { "hashes": [ From e49fb3295f3000a0c5b492dfd1c4810487a8f7d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 12:49:36 +0200 Subject: [PATCH 7/8] build(deps): bump eslint from 7.26.0 to 7.27.0 in /web (#920) --- web/package-lock.json | 52 ++++++++++++++++++++++++++++++++++--------- web/package.json | 2 +- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 9682ea6b8..9653460a8 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -39,7 +39,7 @@ "chartjs-adapter-moment": "^1.0.0", "codemirror": "^5.61.1", "construct-style-sheets-polyfill": "^2.4.16", - "eslint": "^7.26.0", + "eslint": "^7.27.0", "eslint-config-google": "^0.14.0", "eslint-plugin-custom-elements": "0.0.2", "eslint-plugin-lit": "^1.4.1", @@ -3540,9 +3540,9 @@ } }, "node_modules/eslint": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.26.0.tgz", - "integrity": "sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.27.0.tgz", + "integrity": "sha512-JZuR6La2ZF0UD384lcbnd0Cgg6QJjiCwhMD6eU4h/VGPcVGwawNNzKU41tgokGXnfjOOyI6QIffthhJTPzzuRA==", "dependencies": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.1", @@ -3552,12 +3552,14 @@ "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", @@ -3569,7 +3571,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.21", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -3578,7 +3580,7 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^6.0.4", + "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -3715,6 +3717,17 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -5257,6 +5270,11 @@ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -10633,9 +10651,9 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.26.0.tgz", - "integrity": "sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.27.0.tgz", + "integrity": "sha512-JZuR6La2ZF0UD384lcbnd0Cgg6QJjiCwhMD6eU4h/VGPcVGwawNNzKU41tgokGXnfjOOyI6QIffthhJTPzzuRA==", "requires": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.1", @@ -10645,12 +10663,14 @@ "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", @@ -10662,7 +10682,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.21", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -10671,7 +10691,7 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^6.0.4", + "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -10714,6 +10734,11 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -12003,6 +12028,11 @@ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", diff --git a/web/package.json b/web/package.json index d4827a397..1bd00fd1f 100644 --- a/web/package.json +++ b/web/package.json @@ -68,7 +68,7 @@ "chartjs-adapter-moment": "^1.0.0", "codemirror": "^5.61.1", "construct-style-sheets-polyfill": "^2.4.16", - "eslint": "^7.26.0", + "eslint": "^7.27.0", "eslint-config-google": "^0.14.0", "eslint-plugin-custom-elements": "0.0.2", "eslint-plugin-lit": "^1.4.1", From 0062872e18cdb962c87af3156164cf0e276c920c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 12:51:24 +0200 Subject: [PATCH 8/8] build(deps): bump celery from 5.0.5 to 5.1.0 (#921) --- Pipfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 8b176ae28..bda3c0e6e 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -156,11 +156,11 @@ }, "celery": { "hashes": [ - "sha256:5e8d364e058554e83bbb116e8377d90c79be254785f357cb2cec026e79febe13", - "sha256:f4efebe6f8629b0da2b8e529424de376494f5b7a743c321c8a2ddc2b1414921c" + "sha256:1329de1edeaf734ef859e630cb42df2c116d53e59d2f46433b13aed196e85620", + "sha256:65f061c04578cf189cd7352c192e1a79fdeb370b916bff792bcc769560e81184" ], "index": "pypi", - "version": "==5.0.5" + "version": "==5.1.0" }, "certifi": { "hashes": [