From 334e2c466faf0580904aa989b751038e570d96a5 Mon Sep 17 00:00:00 2001 From: "Jens L." Date: Sat, 1 Feb 2025 03:35:56 +0100 Subject: [PATCH] lifecycle: much improved debugging experience (#12804) * lifecycle: much improved debugging experience Signed-off-by: Jens Langhammer * format Signed-off-by: Jens Langhammer * add start debug launch configs Signed-off-by: Jens Langhammer * only install dev deps in container Signed-off-by: Jens Langhammer * add pathMappings Signed-off-by: Jens Langhammer * use debugger variable to enable only debugger without debug mode enabled Signed-off-by: Jens Langhammer * fix path map Signed-off-by: Jens Langhammer --------- Signed-off-by: Jens Langhammer --- .vscode/extensions.json | 7 +- .vscode/launch.json | 66 ++++++++++++++++--- .../core/management/commands/dev_server.py | 2 + authentik/core/management/commands/worker.py | 6 +- authentik/lib/config.py | 2 +- authentik/lib/debug.py | 26 ++++++++ authentik/lib/default.yml | 3 +- internal/debug/debug.go | 1 - lifecycle/ak | 8 ++- lifecycle/gunicorn.conf.py | 7 +- 10 files changed, 103 insertions(+), 25 deletions(-) create mode 100644 authentik/lib/debug.py diff --git a/.vscode/extensions.json b/.vscode/extensions.json index f8c3b72e66..7704f91467 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,6 +2,7 @@ "recommendations": [ "bashmish.es6-string-css", "bpruitt-goddard.mermaid-markdown-syntax-highlighting", + "charliermarsh.ruff", "dbaeumer.vscode-eslint", "EditorConfig.EditorConfig", "esbenp.prettier-vscode", @@ -10,12 +11,12 @@ "Gruntfuggly.todo-tree", "mechatroner.rainbow-csv", "ms-python.black-formatter", - "charliermarsh.ruff", + "ms-python.black-formatter", + "ms-python.debugpy", "ms-python.python", "ms-python.vscode-pylance", - "ms-python.black-formatter", "redhat.vscode-yaml", "Tobermory.es6-string-html", - "unifiedjs.vscode-mdx" + "unifiedjs.vscode-mdx", ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index 24a9ab936c..3eeacd0d29 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,26 +2,76 @@ "version": "0.2.0", "configurations": [ { - "name": "Python: PDB attach Server", - "type": "python", + "name": "Debug: Attach Server Core", + "type": "debugpy", "request": "attach", "connect": { "host": "localhost", - "port": 6800 + "port": 9901 }, - "justMyCode": true, + "pathMappings": [ + { + "localRoot": "${workspaceFolder}", + "remoteRoot": "/" + } + ], "django": true }, { - "name": "Python: PDB attach Worker", - "type": "python", + "name": "Debug: Attach Worker", + "type": "debugpy", "request": "attach", "connect": { "host": "localhost", - "port": 6900 + "port": 9901 }, - "justMyCode": true, + "pathMappings": [ + { + "localRoot": "${workspaceFolder}", + "remoteRoot": "/" + } + ], "django": true + }, + { + "name": "Debug: Start Server Router", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/cmd/server", + "cwd": "${workspaceFolder}" + }, + { + "name": "Debug: Start LDAP Outpost", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/cmd/ldap", + "cwd": "${workspaceFolder}" + }, + { + "name": "Debug: Start Proxy Outpost", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/cmd/proxy", + "cwd": "${workspaceFolder}" + }, + { + "name": "Debug: Start RAC Outpost", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/cmd/rac", + "cwd": "${workspaceFolder}" + }, + { + "name": "Debug: Start Radius Outpost", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/cmd/radius", + "cwd": "${workspaceFolder}" } ] } diff --git a/authentik/core/management/commands/dev_server.py b/authentik/core/management/commands/dev_server.py index e9a6af4fa7..417f2f2cf5 100644 --- a/authentik/core/management/commands/dev_server.py +++ b/authentik/core/management/commands/dev_server.py @@ -5,6 +5,7 @@ from typing import TextIO from daphne.management.commands.runserver import Command as RunServer from daphne.server import Server +from authentik.lib.debug import start_debug_server from authentik.root.signals import post_startup, pre_startup, startup @@ -13,6 +14,7 @@ class SignalServer(Server): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + start_debug_server() def ready_callable(): pre_startup.send(sender=self) diff --git a/authentik/core/management/commands/worker.py b/authentik/core/management/commands/worker.py index 06d32839b2..8b3ed9346c 100644 --- a/authentik/core/management/commands/worker.py +++ b/authentik/core/management/commands/worker.py @@ -9,6 +9,7 @@ from django.db import close_old_connections from structlog.stdlib import get_logger from authentik.lib.config import CONFIG +from authentik.lib.debug import start_debug_server from authentik.root.celery import CELERY_APP LOGGER = get_logger() @@ -28,10 +29,7 @@ class Command(BaseCommand): def handle(self, **options): LOGGER.debug("Celery options", **options) close_old_connections() - if CONFIG.get_bool("remote_debug"): - import debugpy - - debugpy.listen(("0.0.0.0", 6900)) # nosec + start_debug_server() worker: Worker = CELERY_APP.Worker( no_color=False, quiet=True, diff --git a/authentik/lib/config.py b/authentik/lib/config.py index f395abcab5..a609633bd5 100644 --- a/authentik/lib/config.py +++ b/authentik/lib/config.py @@ -422,4 +422,4 @@ if __name__ == "__main__": if len(argv) < 2: # noqa: PLR2004 print(dumps(CONFIG.raw, indent=4, cls=AttrEncoder)) else: - print(CONFIG.get(argv[1])) + print(CONFIG.get(argv[-1])) diff --git a/authentik/lib/debug.py b/authentik/lib/debug.py new file mode 100644 index 0000000000..76d7422b6a --- /dev/null +++ b/authentik/lib/debug.py @@ -0,0 +1,26 @@ +from structlog.stdlib import get_logger + +from authentik.lib.config import CONFIG + +LOGGER = get_logger() + + +def start_debug_server(**kwargs) -> bool: + """Attempt to start a debugpy server in the current process. + Returns true if the server was started successfully, otherwise false""" + if not CONFIG.get_bool("debug") and not CONFIG.get_bool("debugger"): + return + try: + import debugpy + except ImportError: + LOGGER.warning( + "Failed to import debugpy. debugpy is not included " + "in the default release dependencies and must be installed manually" + ) + return False + + listen: str = CONFIG.get("listen.listen_debug_py", "127.0.0.1:9901") + host, _, port = listen.rpartition(":") + debugpy.listen((host, int(port)), **kwargs) # nosec + LOGGER.debug("Starting debug server", host=host, port=port) + return True diff --git a/authentik/lib/default.yml b/authentik/lib/default.yml index 492af91f37..683c06ac45 100644 --- a/authentik/lib/default.yml +++ b/authentik/lib/default.yml @@ -21,6 +21,7 @@ listen: listen_radius: 0.0.0.0:1812 listen_metrics: 0.0.0.0:9300 listen_debug: 0.0.0.0:9900 + listen_debug_py: 0.0.0.0:9901 trusted_proxy_cidrs: - 127.0.0.0/8 - 10.0.0.0/8 @@ -57,7 +58,7 @@ cache: # transport_options: "" debug: false -remote_debug: false +debugger: false log_level: info diff --git a/internal/debug/debug.go b/internal/debug/debug.go index 7921462499..ddd90ffa9e 100644 --- a/internal/debug/debug.go +++ b/internal/debug/debug.go @@ -15,7 +15,6 @@ import ( func EnableDebugServer() { l := log.WithField("logger", "authentik.go_debugger") if !config.Get().Debug { - l.Info("not enabling debug server, set `AUTHENTIK_DEBUG` to `true` to enable it.") return } h := mux.NewRouter() diff --git a/lifecycle/ak b/lifecycle/ak index 44dc480707..efc636682b 100755 --- a/lifecycle/ak +++ b/lifecycle/ak @@ -55,6 +55,10 @@ function cleanup { } function prepare_debug { + # Only attempt to install debug dependencies if we're running in a container + if [ ! -d /ak-root ]; then + return + fi export DEBIAN_FRONTEND=noninteractive apt-get update apt-get install -y --no-install-recommends krb5-kdc krb5-user krb5-admin-server libkrb5-dev gcc @@ -63,7 +67,7 @@ function prepare_debug { chown authentik:authentik /unittest.xml } -if [[ "${AUTHENTIK_REMOTE_DEBUG}" == "true" ]]; then +if [[ "$(python -m authentik.lib.config debugger 2> /dev/null)" == "True" ]]; then prepare_debug fi @@ -92,7 +96,7 @@ elif [[ "$1" == "test-all" ]]; then elif [[ "$1" == "healthcheck" ]]; then run_authentik healthcheck $(cat $MODE_FILE) elif [[ "$1" == "dump_config" ]]; then - exec python -m authentik.lib.config + exec python -m authentik.lib.config $@ elif [[ "$1" == "debug" ]]; then exec sleep infinity else diff --git a/lifecycle/gunicorn.conf.py b/lifecycle/gunicorn.conf.py index a3ffe7489a..07d226ea6c 100644 --- a/lifecycle/gunicorn.conf.py +++ b/lifecycle/gunicorn.conf.py @@ -13,6 +13,7 @@ from prometheus_client.values import MultiProcessValue from authentik import get_full_version from authentik.lib.config import CONFIG +from authentik.lib.debug import start_debug_server from authentik.lib.logging import get_logger_config from authentik.lib.utils.http import get_http_session from authentik.lib.utils.reflection import get_env @@ -146,9 +147,5 @@ if not CONFIG.get_bool("disable_startup_analytics", False): except Exception: # nosec pass -if CONFIG.get_bool("remote_debug"): - import debugpy - - debugpy.listen(("0.0.0.0", 6800)) # nosec - +start_debug_server() run_migrations()