diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index b2f2865ef5..f9d0227038 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -16,18 +16,18 @@ runs: sudo apt-get update sudo apt-get install --no-install-recommends -y libpq-dev openssl libxmlsec1-dev pkg-config gettext - name: Setup python and restore poetry - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version-file: "pyproject.toml" cache: "poetry" - name: Setup node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version-file: web/package.json cache: "npm" cache-dependency-path: web/package-lock.json - name: Setup go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version-file: "go.mod" - name: Setup dependencies diff --git a/authentik/blueprints/apps.py b/authentik/blueprints/apps.py index 9d7718916d..eeef482764 100644 --- a/authentik/blueprints/apps.py +++ b/authentik/blueprints/apps.py @@ -8,6 +8,8 @@ from django.apps import AppConfig from django.db import DatabaseError, InternalError, ProgrammingError from structlog.stdlib import BoundLogger, get_logger +from authentik.root.signals import startup + class ManagedAppConfig(AppConfig): """Basic reconciliation logic for apps""" @@ -23,9 +25,12 @@ class ManagedAppConfig(AppConfig): def ready(self) -> None: self.import_related() + startup.connect(self._on_startup_callback, dispatch_uid=self.label) + return super().ready() + + def _on_startup_callback(self, sender, **_): self._reconcile_global() self._reconcile_tenant() - return super().ready() def import_related(self): """Automatically import related modules which rely on just being imported diff --git a/authentik/blueprints/tests/__init__.py b/authentik/blueprints/tests/__init__.py index 1dd22a453d..69ded34098 100644 --- a/authentik/blueprints/tests/__init__.py +++ b/authentik/blueprints/tests/__init__.py @@ -39,7 +39,7 @@ def reconcile_app(app_name: str): def wrapper(*args, **kwargs): config = apps.get_app_config(app_name) if isinstance(config, ManagedAppConfig): - config.ready() + config._on_startup_callback(None) return func(*args, **kwargs) return wrapper diff --git a/authentik/core/management/commands/dev_server.py b/authentik/core/management/commands/dev_server.py index 81981deff7..e9a6af4fa7 100644 --- a/authentik/core/management/commands/dev_server.py +++ b/authentik/core/management/commands/dev_server.py @@ -1,10 +1,34 @@ """custom runserver command""" +from typing import TextIO + from daphne.management.commands.runserver import Command as RunServer +from daphne.server import Server + +from authentik.root.signals import post_startup, pre_startup, startup + + +class SignalServer(Server): + """Server which signals back to authentik when it finished starting up""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def ready_callable(): + pre_startup.send(sender=self) + startup.send(sender=self) + post_startup.send(sender=self) + + self.ready_callable = ready_callable class Command(RunServer): """custom runserver command, which doesn't show the misleading django startup message""" - def on_bind(self, server_port): - pass + server_cls = SignalServer + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # Redirect standard stdout banner from Daphne into the void + # as there are a couple more steps that happen before startup is fully done + self.stdout = TextIO() diff --git a/authentik/lib/utils/reflection.py b/authentik/lib/utils/reflection.py index 3d13ca89f4..b4eba76333 100644 --- a/authentik/lib/utils/reflection.py +++ b/authentik/lib/utils/reflection.py @@ -3,12 +3,14 @@ import os from importlib import import_module from pathlib import Path +from tempfile import gettempdir from django.conf import settings -from kubernetes.config.incluster_config import SERVICE_HOST_ENV_NAME from authentik.lib.config import CONFIG +SERVICE_HOST_ENV_NAME = "KUBERNETES_SERVICE_HOST" + def all_subclasses(cls, sort=True): """Recursively return all subclassess of cls""" @@ -55,7 +57,7 @@ def get_env() -> str: return "dev" if SERVICE_HOST_ENV_NAME in os.environ: return "kubernetes" - if Path("/tmp/authentik-mode").exists(): # nosec + if (Path(gettempdir()) / "authentik-mode").exists(): return "compose" if "AK_APPLIANCE" in os.environ: return os.environ["AK_APPLIANCE"] diff --git a/authentik/outposts/apps.py b/authentik/outposts/apps.py index 24798c269d..a7680a9aa5 100644 --- a/authentik/outposts/apps.py +++ b/authentik/outposts/apps.py @@ -45,14 +45,14 @@ class AuthentikOutpostConfig(ManagedAppConfig): outpost.managed = MANAGED_OUTPOST outpost.save() return - outpost, updated = Outpost.objects.update_or_create( + outpost, created = Outpost.objects.update_or_create( defaults={ "type": OutpostType.PROXY, "name": MANAGED_OUTPOST_NAME, }, managed=MANAGED_OUTPOST, ) - if updated: + if created: if KubernetesServiceConnection.objects.exists(): outpost.service_connection = KubernetesServiceConnection.objects.first() elif DockerServiceConnection.objects.exists(): diff --git a/authentik/root/asgi.py b/authentik/root/asgi.py index 4c14429103..4aaddf83d6 100644 --- a/authentik/root/asgi.py +++ b/authentik/root/asgi.py @@ -60,7 +60,18 @@ class RouteNotFoundMiddleware: raise exc -application = SentryAsgiMiddleware( +class AuthentikAsgi(SentryAsgiMiddleware): + """Root ASGI App wrapper""" + + def call_startup(self): + from authentik.root.signals import post_startup, pre_startup, startup + + pre_startup.send(sender=self) + startup.send(sender=self) + post_startup.send(sender=self) + + +application = AuthentikAsgi( ProtocolTypeRouter( { "http": get_asgi_application(), diff --git a/authentik/root/signals.py b/authentik/root/signals.py new file mode 100644 index 0000000000..51543dbd03 --- /dev/null +++ b/authentik/root/signals.py @@ -0,0 +1,26 @@ +from datetime import timedelta + +from django.core.signals import Signal +from django.dispatch import receiver +from django.utils.timezone import now +from structlog.stdlib import get_logger + +# Signal dispatched before actual startup trigger +pre_startup = Signal() +# Signal dispatched which should trigger all startup logic +startup = Signal() +# Signal dispatched after the startup logic +post_startup = Signal() + +LOGGER = get_logger() + + +@receiver(pre_startup) +def pre_startup_log(sender, **_): + sender._start_time = now() + + +@receiver(post_startup) +def post_startup_log(sender, **_): + took: timedelta = now() - sender._start_time + LOGGER.info("authentik Core Worker finished starting", took_s=took.total_seconds()) diff --git a/authentik/root/test_runner.py b/authentik/root/test_runner.py index eddb884b73..30b68b5ecc 100644 --- a/authentik/root/test_runner.py +++ b/authentik/root/test_runner.py @@ -9,6 +9,7 @@ from django.test.runner import DiscoverRunner from authentik.lib.config import CONFIG from authentik.lib.sentry import sentry_init +from authentik.root.signals import post_startup, pre_startup, startup from tests.e2e.utils import get_docker_tag # globally set maxDiff to none to show full assert error @@ -46,6 +47,10 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover CONFIG.set("error_reporting.send_pii", True) sentry_init() + pre_startup.send(sender=self, mode="test") + startup.send(sender=self, mode="test") + post_startup.send(sender=self, mode="test") + @classmethod def add_arguments(cls, parser: ArgumentParser): """Add more pytest-specific arguments""" diff --git a/internal/gounicorn/gounicorn.go b/internal/gounicorn/gounicorn.go index 0f328c9439..a3692f3106 100644 --- a/internal/gounicorn/gounicorn.go +++ b/internal/gounicorn/gounicorn.go @@ -96,18 +96,15 @@ func (g *GoUnicorn) healthcheck() { g.log.Debug("starting healthcheck") // Default healthcheck is every 1 second on startup // once we've been healthy once, increase to 30 seconds - for range time.Tick(time.Second) { + for range time.NewTicker(time.Second).C { if g.Healthcheck() { g.alive = true - g.log.Info("backend is alive, backing off with healthchecks") + g.log.Debug("backend is alive, backing off with healthchecks") g.HealthyCallback() break } g.log.Debug("backend not alive yet") } - for range time.Tick(30 * time.Second) { - g.Healthcheck() - } } func (g *GoUnicorn) Reload() { diff --git a/internal/web/proxy.go b/internal/web/proxy.go index 56f81a0726..ccc9bd0eb0 100644 --- a/internal/web/proxy.go +++ b/internal/web/proxy.go @@ -32,9 +32,6 @@ func (ws *WebServer) configureProxy() { } rp.ErrorHandler = ws.proxyErrorHandler rp.ModifyResponse = ws.proxyModifyResponse - ws.m.Path("/-/health/live/").HandlerFunc(sentry.SentryNoSample(func(rw http.ResponseWriter, r *http.Request) { - rw.WriteHeader(204) - })) ws.m.PathPrefix("/").HandlerFunc(sentry.SentryNoSample(func(rw http.ResponseWriter, r *http.Request) { if !ws.g.IsRunning() { ws.proxyErrorHandler(rw, r, errors.New("authentik starting")) diff --git a/lifecycle/ak b/lifecycle/ak index a1df54b8c9..ad0e37ee30 100755 --- a/lifecycle/ak +++ b/lifecycle/ak @@ -7,7 +7,6 @@ function log { function wait_for_db { python -m lifecycle.wait_for_db - python -m lifecycle.migrate log "Bootstrap completed" } @@ -65,7 +64,6 @@ if [[ "${AUTHENTIK_REMOTE_DEBUG}" == "true" ]]; then fi if [[ "$1" == "server" ]]; then - wait_for_db set_mode "server" # If we have bootstrap credentials set, run bootstrap tasks outside of main server # sync, so that we can sure the first start actually has working bootstrap @@ -75,7 +73,6 @@ if [[ "$1" == "server" ]]; then fi run_authentik elif [[ "$1" == "worker" ]]; then - wait_for_db set_mode "worker" check_if_root "python -m manage worker" elif [[ "$1" == "worker-status" ]]; then diff --git a/lifecycle/gunicorn.conf.py b/lifecycle/gunicorn.conf.py index e608c94413..4ba9487021 100644 --- a/lifecycle/gunicorn.conf.py +++ b/lifecycle/gunicorn.conf.py @@ -2,13 +2,11 @@ import os from hashlib import sha512 -from multiprocessing import cpu_count from os import makedirs from pathlib import Path from tempfile import gettempdir from typing import TYPE_CHECKING -from kubernetes.config.incluster_config import SERVICE_HOST_ENV_NAME from prometheus_client.values import MultiProcessValue from authentik import get_full_version @@ -17,11 +15,18 @@ 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 from authentik.root.install_id import get_install_id_raw +from lifecycle.migrate import run_migrations +from lifecycle.wait_for_db import wait_for_db from lifecycle.worker import DjangoUvicornWorker if TYPE_CHECKING: + from gunicorn.app.wsgiapp import WSGIApplication from gunicorn.arbiter import Arbiter + from authentik.root.asgi import AuthentikAsgi + +wait_for_db() + _tmp = Path(gettempdir()) worker_class = "lifecycle.worker.DjangoUvicornWorker" worker_tmp_dir = str(_tmp.joinpath("authentik_worker_tmp")) @@ -35,17 +40,14 @@ bind = f"unix://{str(_tmp.joinpath('authentik-core.sock'))}" os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings") os.environ.setdefault("PROMETHEUS_MULTIPROC_DIR", prometheus_tmp_dir) +preload = True + max_requests = 1000 max_requests_jitter = 50 logconfig_dict = get_logger_config() -# if we're running in kubernetes, use fixed workers because we can scale with more pods -# otherwise (assume docker-compose), use as much as we can -if SERVICE_HOST_ENV_NAME in os.environ: - default_workers = 2 -else: - default_workers = max(cpu_count() * 0.25, 1) + 1 # Minimum of 2 workers +default_workers = 2 workers = CONFIG.get_int("web.workers", default_workers) threads = CONFIG.get_int("web.threads", 4) @@ -100,6 +102,18 @@ def pre_fork(server: "Arbiter", worker: DjangoUvicornWorker): worker._worker_id = _next_worker_id(server) +def post_worker_init(worker: DjangoUvicornWorker): + """Notify ASGI app that its started up""" + # Only trigger startup DB logic on first worker + # Startup code that imports code or is otherwise needed in every worker + # does not use this signal, so we can skip this safely + if worker._worker_id != 1: + return + app: "WSGIApplication" = worker.app + root_app: "AuthentikAsgi" = app.callable + root_app.call_startup() + + if not CONFIG.get_bool("disable_startup_analytics", False): env = get_env() should_send = env not in ["dev", "ci"] @@ -129,3 +143,5 @@ if CONFIG.get_bool("remote_debug"): import debugpy debugpy.listen(("0.0.0.0", 6800)) # nosec + +run_migrations() diff --git a/lifecycle/migrate.py b/lifecycle/migrate.py index 1dac197119..42504c872f 100755 --- a/lifecycle/migrate.py +++ b/lifecycle/migrate.py @@ -68,7 +68,7 @@ def release_lock(cursor: Cursor): cursor.execute("SELECT pg_advisory_unlock(%s)", (ADV_LOCK_UID,)) -if __name__ == "__main__": +def run_migrations(): conn = connect( dbname=CONFIG.get("postgresql.name"), user=CONFIG.get("postgresql.user"), @@ -117,3 +117,7 @@ if __name__ == "__main__": ) finally: release_lock(curr) + + +if __name__ == "__main__": + run_migrations() diff --git a/lifecycle/wait_for_db.py b/lifecycle/wait_for_db.py index 005846a2c2..9447ead084 100755 --- a/lifecycle/wait_for_db.py +++ b/lifecycle/wait_for_db.py @@ -11,52 +11,61 @@ from redis.exceptions import RedisError from authentik.lib.config import CONFIG -CONFIG.log("info", "Starting authentik bootstrap") -# Sanity check, ensure SECRET_KEY is set before we even check for database connectivity -if CONFIG.get("secret_key") is None or len(CONFIG.get("secret_key")) == 0: - CONFIG.log("info", "----------------------------------------------------------------------") - CONFIG.log("info", "Secret key missing, check https://goauthentik.io/docs/installation/.") - CONFIG.log("info", "----------------------------------------------------------------------") - sysexit(1) +def check_postgres(): + while True: + try: + conn = connect( + dbname=CONFIG.get("postgresql.name"), + user=CONFIG.get("postgresql.user"), + password=CONFIG.get("postgresql.password"), + host=CONFIG.get("postgresql.host"), + port=CONFIG.get_int("postgresql.port"), + sslmode=CONFIG.get("postgresql.sslmode"), + sslrootcert=CONFIG.get("postgresql.sslrootcert"), + sslcert=CONFIG.get("postgresql.sslcert"), + sslkey=CONFIG.get("postgresql.sslkey"), + ) + conn.cursor() + break + except OperationalError as exc: + sleep(1) + CONFIG.log("info", f"PostgreSQL connection failed, retrying... ({exc})") + CONFIG.log("info", "PostgreSQL connection successful") -while True: - try: - conn = connect( - dbname=CONFIG.get("postgresql.name"), - user=CONFIG.get("postgresql.user"), - password=CONFIG.get("postgresql.password"), - host=CONFIG.get("postgresql.host"), - port=CONFIG.get_int("postgresql.port"), - sslmode=CONFIG.get("postgresql.sslmode"), - sslrootcert=CONFIG.get("postgresql.sslrootcert"), - sslcert=CONFIG.get("postgresql.sslcert"), - sslkey=CONFIG.get("postgresql.sslkey"), - ) - conn.cursor() - break - except OperationalError as exc: - sleep(1) - CONFIG.log("info", f"PostgreSQL connection failed, retrying... ({exc})") -CONFIG.log("info", "PostgreSQL connection successful") +def check_redis(): + REDIS_PROTOCOL_PREFIX = "redis://" + if CONFIG.get_bool("redis.tls", False): + REDIS_PROTOCOL_PREFIX = "rediss://" + REDIS_URL = ( + f"{REDIS_PROTOCOL_PREFIX}:" + f"{quote_plus(CONFIG.get('redis.password'))}@{quote_plus(CONFIG.get('redis.host'))}:" + f"{CONFIG.get_int('redis.port')}/{CONFIG.get('redis.db')}" + ) + while True: + try: + redis = Redis.from_url(REDIS_URL) + redis.ping() + break + except RedisError as exc: + sleep(1) + CONFIG.log("info", f"Redis Connection failed, retrying... ({exc})", redis_url=REDIS_URL) + CONFIG.log("info", "Redis Connection successful") -REDIS_PROTOCOL_PREFIX = "redis://" -if CONFIG.get_bool("redis.tls", False): - REDIS_PROTOCOL_PREFIX = "rediss://" -REDIS_URL = ( - f"{REDIS_PROTOCOL_PREFIX}:" - f"{quote_plus(CONFIG.get('redis.password'))}@{quote_plus(CONFIG.get('redis.host'))}:" - f"{CONFIG.get_int('redis.port')}/{CONFIG.get('redis.db')}" -) -while True: - try: - redis = Redis.from_url(REDIS_URL) - redis.ping() - break - except RedisError as exc: - sleep(1) - CONFIG.log("info", f"Redis Connection failed, retrying... ({exc})", redis_url=REDIS_URL) -CONFIG.log("info", "Redis Connection successful") -CONFIG.log("info", "Finished authentik bootstrap") +def wait_for_db(): + CONFIG.log("info", "Starting authentik bootstrap") + # Sanity check, ensure SECRET_KEY is set before we even check for database connectivity + if CONFIG.get("secret_key") is None or len(CONFIG.get("secret_key")) == 0: + CONFIG.log("info", "----------------------------------------------------------------------") + CONFIG.log("info", "Secret key missing, check https://goauthentik.io/docs/installation/.") + CONFIG.log("info", "----------------------------------------------------------------------") + sysexit(1) + check_postgres() + check_redis() + CONFIG.log("info", "Finished authentik bootstrap") + + +if __name__ == "__main__": + wait_for_db() diff --git a/lifecycle/worker.py b/lifecycle/worker.py index ad4c1ebdb6..9d5c09fe88 100644 --- a/lifecycle/worker.py +++ b/lifecycle/worker.py @@ -12,3 +12,5 @@ class DjangoUvicornWorker(UvicornWorker): "lifespan": "off", "ws": "wsproto", } + + _worker_id: int diff --git a/manage.py b/manage.py index c665dab08d..a55ddba0fe 100755 --- a/manage.py +++ b/manage.py @@ -5,6 +5,10 @@ import sys import warnings from defusedxml import defuse_stdlib +from django.utils.autoreload import DJANGO_AUTORELOAD_ENV + +from lifecycle.migrate import run_migrations +from lifecycle.wait_for_db import wait_for_db warnings.filterwarnings("ignore", "SelectableGroups dict interface") warnings.filterwarnings( @@ -20,6 +24,17 @@ defuse_stdlib() if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings") + wait_for_db() + if ( + len(sys.argv) > 1 + # Explicitly only run migrate for server and worker + # `bootstrap_tasks` is a special case as that command might be triggered by the `ak` + # script to pre-run certain tasks for an automated install + and sys.argv[1] in ["dev_server", "worker", "bootstrap_tasks"] + # and don't run if this is the child process of a dev_server + and os.environ.get(DJANGO_AUTORELOAD_ENV, None) is None + ): + run_migrations() try: from django.core.management import execute_from_command_line except ImportError as exc: diff --git a/poetry.lock b/poetry.lock index 905ca28a1c..8f68aeef2a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3637,6 +3637,106 @@ idna = ["idna"] mypy = ["idna", "mypy", "types-pyopenssl"] tests = ["coverage[toml] (>=5.0.2)", "pytest"] +[[package]] +name = "setproctitle" +version = "1.3.3" +description = "A Python module to customize the process title" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setproctitle-1.3.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:897a73208da48db41e687225f355ce993167079eda1260ba5e13c4e53be7f754"}, + {file = "setproctitle-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c331e91a14ba4076f88c29c777ad6b58639530ed5b24b5564b5ed2fd7a95452"}, + {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbbd6c7de0771c84b4aa30e70b409565eb1fc13627a723ca6be774ed6b9d9fa3"}, + {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c05ac48ef16ee013b8a326c63e4610e2430dbec037ec5c5b58fcced550382b74"}, + {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1342f4fdb37f89d3e3c1c0a59d6ddbedbde838fff5c51178a7982993d238fe4f"}, + {file = "setproctitle-1.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc74e84fdfa96821580fb5e9c0b0777c1c4779434ce16d3d62a9c4d8c710df39"}, + {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9617b676b95adb412bb69645d5b077d664b6882bb0d37bfdafbbb1b999568d85"}, + {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6a249415f5bb88b5e9e8c4db47f609e0bf0e20a75e8d744ea787f3092ba1f2d0"}, + {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:38da436a0aaace9add67b999eb6abe4b84397edf4a78ec28f264e5b4c9d53cd5"}, + {file = "setproctitle-1.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:da0d57edd4c95bf221b2ebbaa061e65b1788f1544977288bdf95831b6e44e44d"}, + {file = "setproctitle-1.3.3-cp310-cp310-win32.whl", hash = "sha256:a1fcac43918b836ace25f69b1dca8c9395253ad8152b625064415b1d2f9be4fb"}, + {file = "setproctitle-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:200620c3b15388d7f3f97e0ae26599c0c378fdf07ae9ac5a13616e933cbd2086"}, + {file = "setproctitle-1.3.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:334f7ed39895d692f753a443102dd5fed180c571eb6a48b2a5b7f5b3564908c8"}, + {file = "setproctitle-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:950f6476d56ff7817a8fed4ab207727fc5260af83481b2a4b125f32844df513a"}, + {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:195c961f54a09eb2acabbfc90c413955cf16c6e2f8caa2adbf2237d1019c7dd8"}, + {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f05e66746bf9fe6a3397ec246fe481096664a9c97eb3fea6004735a4daf867fd"}, + {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b5901a31012a40ec913265b64e48c2a4059278d9f4e6be628441482dd13fb8b5"}, + {file = "setproctitle-1.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64286f8a995f2cd934082b398fc63fca7d5ffe31f0e27e75b3ca6b4efda4e353"}, + {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:184239903bbc6b813b1a8fc86394dc6ca7d20e2ebe6f69f716bec301e4b0199d"}, + {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:664698ae0013f986118064b6676d7dcd28fefd0d7d5a5ae9497cbc10cba48fa5"}, + {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e5119a211c2e98ff18b9908ba62a3bd0e3fabb02a29277a7232a6fb4b2560aa0"}, + {file = "setproctitle-1.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:417de6b2e214e837827067048f61841f5d7fc27926f2e43954567094051aff18"}, + {file = "setproctitle-1.3.3-cp311-cp311-win32.whl", hash = "sha256:6a143b31d758296dc2f440175f6c8e0b5301ced3b0f477b84ca43cdcf7f2f476"}, + {file = "setproctitle-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a680d62c399fa4b44899094027ec9a1bdaf6f31c650e44183b50d4c4d0ccc085"}, + {file = "setproctitle-1.3.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d4460795a8a7a391e3567b902ec5bdf6c60a47d791c3b1d27080fc203d11c9dc"}, + {file = "setproctitle-1.3.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bdfd7254745bb737ca1384dee57e6523651892f0ea2a7344490e9caefcc35e64"}, + {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:477d3da48e216d7fc04bddab67b0dcde633e19f484a146fd2a34bb0e9dbb4a1e"}, + {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ab2900d111e93aff5df9fddc64cf51ca4ef2c9f98702ce26524f1acc5a786ae7"}, + {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:088b9efc62d5aa5d6edf6cba1cf0c81f4488b5ce1c0342a8b67ae39d64001120"}, + {file = "setproctitle-1.3.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6d50252377db62d6a0bb82cc898089916457f2db2041e1d03ce7fadd4a07381"}, + {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:87e668f9561fd3a457ba189edfc9e37709261287b52293c115ae3487a24b92f6"}, + {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:287490eb90e7a0ddd22e74c89a92cc922389daa95babc833c08cf80c84c4df0a"}, + {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:4fe1c49486109f72d502f8be569972e27f385fe632bd8895f4730df3c87d5ac8"}, + {file = "setproctitle-1.3.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4a6ba2494a6449b1f477bd3e67935c2b7b0274f2f6dcd0f7c6aceae10c6c6ba3"}, + {file = "setproctitle-1.3.3-cp312-cp312-win32.whl", hash = "sha256:2df2b67e4b1d7498632e18c56722851ba4db5d6a0c91aaf0fd395111e51cdcf4"}, + {file = "setproctitle-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:f38d48abc121263f3b62943f84cbaede05749047e428409c2c199664feb6abc7"}, + {file = "setproctitle-1.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:816330675e3504ae4d9a2185c46b573105d2310c20b19ea2b4596a9460a4f674"}, + {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68f960bc22d8d8e4ac886d1e2e21ccbd283adcf3c43136161c1ba0fa509088e0"}, + {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e6e7adff74796ef12753ff399491b8827f84f6c77659d71bd0b35870a17d8f"}, + {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53bc0d2358507596c22b02db079618451f3bd720755d88e3cccd840bafb4c41c"}, + {file = "setproctitle-1.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad6d20f9541f5f6ac63df553b6d7a04f313947f550eab6a61aa758b45f0d5657"}, + {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c1c84beab776b0becaa368254801e57692ed749d935469ac10e2b9b825dbdd8e"}, + {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:507e8dc2891021350eaea40a44ddd887c9f006e6b599af8d64a505c0f718f170"}, + {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b1067647ac7aba0b44b591936118a22847bda3c507b0a42d74272256a7a798e9"}, + {file = "setproctitle-1.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2e71f6365744bf53714e8bd2522b3c9c1d83f52ffa6324bd7cbb4da707312cd8"}, + {file = "setproctitle-1.3.3-cp37-cp37m-win32.whl", hash = "sha256:7f1d36a1e15a46e8ede4e953abb104fdbc0845a266ec0e99cc0492a4364f8c44"}, + {file = "setproctitle-1.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9a402881ec269d0cc9c354b149fc29f9ec1a1939a777f1c858cdb09c7a261df"}, + {file = "setproctitle-1.3.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ff814dea1e5c492a4980e3e7d094286077054e7ea116cbeda138819db194b2cd"}, + {file = "setproctitle-1.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:accb66d7b3ccb00d5cd11d8c6e07055a4568a24c95cf86109894dcc0c134cc89"}, + {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554eae5a5b28f02705b83a230e9d163d645c9a08914c0ad921df363a07cf39b1"}, + {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a911b26264dbe9e8066c7531c0591cfab27b464459c74385b276fe487ca91c12"}, + {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2982efe7640c4835f7355fdb4da313ad37fb3b40f5c69069912f8048f77b28c8"}, + {file = "setproctitle-1.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df3f4274b80709d8bcab2f9a862973d453b308b97a0b423a501bcd93582852e3"}, + {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:af2c67ae4c795d1674a8d3ac1988676fa306bcfa1e23fddb5e0bd5f5635309ca"}, + {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:af4061f67fd7ec01624c5e3c21f6b7af2ef0e6bab7fbb43f209e6506c9ce0092"}, + {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:37a62cbe16d4c6294e84670b59cf7adcc73faafe6af07f8cb9adaf1f0e775b19"}, + {file = "setproctitle-1.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a83ca086fbb017f0d87f240a8f9bbcf0809f3b754ee01cec928fff926542c450"}, + {file = "setproctitle-1.3.3-cp38-cp38-win32.whl", hash = "sha256:059f4ce86f8cc92e5860abfc43a1dceb21137b26a02373618d88f6b4b86ba9b2"}, + {file = "setproctitle-1.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ab92e51cd4a218208efee4c6d37db7368fdf182f6e7ff148fb295ecddf264287"}, + {file = "setproctitle-1.3.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c7951820b77abe03d88b114b998867c0f99da03859e5ab2623d94690848d3e45"}, + {file = "setproctitle-1.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5bc94cf128676e8fac6503b37763adb378e2b6be1249d207630f83fc325d9b11"}, + {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f5d9027eeda64d353cf21a3ceb74bb1760bd534526c9214e19f052424b37e42"}, + {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e4a8104db15d3462e29d9946f26bed817a5b1d7a47eabca2d9dc2b995991503"}, + {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c32c41ace41f344d317399efff4cffb133e709cec2ef09c99e7a13e9f3b9483c"}, + {file = "setproctitle-1.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbf16381c7bf7f963b58fb4daaa65684e10966ee14d26f5cc90f07049bfd8c1e"}, + {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e18b7bd0898398cc97ce2dfc83bb192a13a087ef6b2d5a8a36460311cb09e775"}, + {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69d565d20efe527bd8a9b92e7f299ae5e73b6c0470f3719bd66f3cd821e0d5bd"}, + {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ddedd300cd690a3b06e7eac90ed4452348b1348635777ce23d460d913b5b63c3"}, + {file = "setproctitle-1.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:415bfcfd01d1fbf5cbd75004599ef167a533395955305f42220a585f64036081"}, + {file = "setproctitle-1.3.3-cp39-cp39-win32.whl", hash = "sha256:21112fcd2195d48f25760f0eafa7a76510871bbb3b750219310cf88b04456ae3"}, + {file = "setproctitle-1.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:5a740f05d0968a5a17da3d676ce6afefebeeeb5ce137510901bf6306ba8ee002"}, + {file = "setproctitle-1.3.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6b9e62ddb3db4b5205c0321dd69a406d8af9ee1693529d144e86bd43bcb4b6c0"}, + {file = "setproctitle-1.3.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e3b99b338598de0bd6b2643bf8c343cf5ff70db3627af3ca427a5e1a1a90dd9"}, + {file = "setproctitle-1.3.3-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ae9a02766dad331deb06855fb7a6ca15daea333b3967e214de12cfae8f0ef5"}, + {file = "setproctitle-1.3.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:200ede6fd11233085ba9b764eb055a2a191fb4ffb950c68675ac53c874c22e20"}, + {file = "setproctitle-1.3.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0d3a953c50776751e80fe755a380a64cb14d61e8762bd43041ab3f8cc436092f"}, + {file = "setproctitle-1.3.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5e08e232b78ba3ac6bc0d23ce9e2bee8fad2be391b7e2da834fc9a45129eb87"}, + {file = "setproctitle-1.3.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1da82c3e11284da4fcbf54957dafbf0655d2389cd3d54e4eaba636faf6d117a"}, + {file = "setproctitle-1.3.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:aeaa71fb9568ebe9b911ddb490c644fbd2006e8c940f21cb9a1e9425bd709574"}, + {file = "setproctitle-1.3.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:59335d000c6250c35989394661eb6287187854e94ac79ea22315469ee4f4c244"}, + {file = "setproctitle-1.3.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3ba57029c9c50ecaf0c92bb127224cc2ea9fda057b5d99d3f348c9ec2855ad3"}, + {file = "setproctitle-1.3.3-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d876d355c53d975c2ef9c4f2487c8f83dad6aeaaee1b6571453cb0ee992f55f6"}, + {file = "setproctitle-1.3.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:224602f0939e6fb9d5dd881be1229d485f3257b540f8a900d4271a2c2aa4e5f4"}, + {file = "setproctitle-1.3.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d7f27e0268af2d7503386e0e6be87fb9b6657afd96f5726b733837121146750d"}, + {file = "setproctitle-1.3.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5e7266498cd31a4572378c61920af9f6b4676a73c299fce8ba93afd694f8ae7"}, + {file = "setproctitle-1.3.3-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33c5609ad51cd99d388e55651b19148ea99727516132fb44680e1f28dd0d1de9"}, + {file = "setproctitle-1.3.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:eae8988e78192fd1a3245a6f4f382390b61bce6cfcc93f3809726e4c885fa68d"}, + {file = "setproctitle-1.3.3.tar.gz", hash = "sha256:c913e151e7ea01567837ff037a23ca8740192880198b7fbb90b16d181607caae"}, +] + +[package.extras] +test = ["pytest"] + [[package]] name = "setuptools" version = "69.0.3" @@ -4536,4 +4636,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "~3.12" -content-hash = "04ef13e2692c158e5eda1e89876a215746c56a891a161d8434d808dc12c0fc7a" +content-hash = "c5a36b528980277b07f80200da251a2bea31cc2b7d5438250706f23a825f3628" diff --git a/pyproject.toml b/pyproject.toml index 5f89c13153..248bde74c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -133,6 +133,7 @@ pyyaml = "*" requests-oauthlib = "*" sentry-sdk = "*" service_identity = "*" +setproctitle = "*" structlog = "*" swagger-spec-validator = "*" tenant-schemas-celery = "*" diff --git a/web/src/elements/rbac/RoleObjectPermissionTable.ts b/web/src/elements/rbac/RoleObjectPermissionTable.ts index 18da770242..6dcdeafeb7 100644 --- a/web/src/elements/rbac/RoleObjectPermissionTable.ts +++ b/web/src/elements/rbac/RoleObjectPermissionTable.ts @@ -112,9 +112,7 @@ export class RoleAssignedObjectPermissionTable extends Table uperm.codename === perm.codename).length > 0; baseRow.push( html`${granted - ? html`` : html`X`} `, diff --git a/website/docs/installation/configuration.mdx b/website/docs/installation/configuration.mdx index 964362dbe2..c189567be0 100644 --- a/website/docs/installation/configuration.mdx +++ b/website/docs/installation/configuration.mdx @@ -328,8 +328,7 @@ Requires authentik 2022.9 Configure how many gunicorn worker processes should be started (see https://docs.gunicorn.org/en/stable/design.html). -If running in Kubernetes, the default value is set to 2 and should in most cases not be changed, as scaling can be done with multiple pods running the web server. -Otherwise, authentik will use 1 worker for each 4 CPU cores + 1. A value below 2 workers is not recommended. +Defaults to 2. A value below 2 workers is not recommended. In environments where scaling with multiple replicas of the authentik server is not possible, this number can be increased to handle higher loads. ### `AUTHENTIK_WEB__THREADS`