outposts: move local connection check to task, run every 60 minutes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
aa0e8edb8b
commit
b8a566f4a0
|
@ -1,17 +1,8 @@
|
||||||
"""authentik outposts app config"""
|
"""authentik outposts app config"""
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from os import R_OK, access
|
|
||||||
from os.path import expanduser
|
|
||||||
from pathlib import Path
|
|
||||||
from socket import gethostname
|
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
import yaml
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
from django.db import ProgrammingError
|
from django.db import ProgrammingError
|
||||||
from docker.constants import DEFAULT_UNIX_SOCKET
|
|
||||||
from kubernetes.config.incluster_config import SERVICE_TOKEN_FILENAME
|
|
||||||
from kubernetes.config.kube_config import KUBE_CONFIG_DEFAULT_LOCATION
|
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
@ -27,49 +18,8 @@ class AuthentikOutpostConfig(AppConfig):
|
||||||
def ready(self):
|
def ready(self):
|
||||||
import_module("authentik.outposts.signals")
|
import_module("authentik.outposts.signals")
|
||||||
try:
|
try:
|
||||||
AuthentikOutpostConfig.init_local_connection()
|
from authentik.outposts.tasks import outpost_local_connection
|
||||||
|
|
||||||
|
outpost_local_connection.delay()
|
||||||
except ProgrammingError:
|
except ProgrammingError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def init_local_connection():
|
|
||||||
"""Check if local kubernetes or docker connections should be created"""
|
|
||||||
from authentik.outposts.models import (
|
|
||||||
DockerServiceConnection,
|
|
||||||
KubernetesServiceConnection,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Explicitly check against token filename, as thats
|
|
||||||
# only present when the integration is enabled
|
|
||||||
if Path(SERVICE_TOKEN_FILENAME).exists():
|
|
||||||
LOGGER.debug("Detected in-cluster Kubernetes Config")
|
|
||||||
if not KubernetesServiceConnection.objects.filter(local=True).exists():
|
|
||||||
LOGGER.debug("Created Service Connection for in-cluster")
|
|
||||||
KubernetesServiceConnection.objects.create(
|
|
||||||
name="Local Kubernetes Cluster", local=True, kubeconfig={}
|
|
||||||
)
|
|
||||||
# For development, check for the existence of a kubeconfig file
|
|
||||||
kubeconfig_path = expanduser(KUBE_CONFIG_DEFAULT_LOCATION)
|
|
||||||
if Path(kubeconfig_path).exists():
|
|
||||||
LOGGER.debug("Detected kubeconfig")
|
|
||||||
kubeconfig_local_name = f"k8s-{gethostname()}"
|
|
||||||
if not KubernetesServiceConnection.objects.filter(
|
|
||||||
name=kubeconfig_local_name
|
|
||||||
).exists():
|
|
||||||
LOGGER.debug("Creating kubeconfig Service Connection")
|
|
||||||
with open(kubeconfig_path, "r") as _kubeconfig:
|
|
||||||
KubernetesServiceConnection.objects.create(
|
|
||||||
name=kubeconfig_local_name,
|
|
||||||
kubeconfig=yaml.safe_load(_kubeconfig),
|
|
||||||
)
|
|
||||||
unix_socket_path = urlparse(DEFAULT_UNIX_SOCKET).path
|
|
||||||
socket = Path(unix_socket_path)
|
|
||||||
if socket.exists() and access(socket, R_OK):
|
|
||||||
LOGGER.debug("Detected local docker socket")
|
|
||||||
if len(DockerServiceConnection.objects.filter(local=True)) == 0:
|
|
||||||
LOGGER.debug("Created Service Connection for docker")
|
|
||||||
DockerServiceConnection.objects.create(
|
|
||||||
name="Local Docker connection",
|
|
||||||
local=True,
|
|
||||||
url=unix_socket_path,
|
|
||||||
)
|
|
||||||
|
|
|
@ -17,4 +17,9 @@ CELERY_BEAT_SCHEDULE = {
|
||||||
"schedule": crontab(minute="*/5"),
|
"schedule": crontab(minute="*/5"),
|
||||||
"options": {"queue": "authentik_scheduled"},
|
"options": {"queue": "authentik_scheduled"},
|
||||||
},
|
},
|
||||||
|
"outpost_local_connection": {
|
||||||
|
"task": "authentik.outposts.tasks.outpost_local_connection",
|
||||||
|
"schedule": crontab(minute="*/60"),
|
||||||
|
"options": {"queue": "authentik_scheduled"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,20 @@
|
||||||
"""outpost tasks"""
|
"""outpost tasks"""
|
||||||
|
from os import R_OK, access
|
||||||
|
from os.path import expanduser
|
||||||
|
from pathlib import Path
|
||||||
|
from socket import gethostname
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
import yaml
|
||||||
from asgiref.sync import async_to_sync
|
from asgiref.sync import async_to_sync
|
||||||
from channels.layers import get_channel_layer
|
from channels.layers import get_channel_layer
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.db.models.base import Model
|
from django.db.models.base import Model
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
|
from docker.constants import DEFAULT_UNIX_SOCKET
|
||||||
|
from kubernetes.config.incluster_config import SERVICE_TOKEN_FILENAME
|
||||||
|
from kubernetes.config.kube_config import KUBE_CONFIG_DEFAULT_LOCATION
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
|
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
|
||||||
|
@ -185,3 +194,42 @@ def _outpost_single_update(outpost: Outpost, layer=None):
|
||||||
for state in OutpostState.for_outpost(outpost):
|
for state in OutpostState.for_outpost(outpost):
|
||||||
LOGGER.debug("sending update", channel=state.uid, outpost=outpost)
|
LOGGER.debug("sending update", channel=state.uid, outpost=outpost)
|
||||||
async_to_sync(layer.send)(state.uid, {"type": "event.update"})
|
async_to_sync(layer.send)(state.uid, {"type": "event.update"})
|
||||||
|
|
||||||
|
|
||||||
|
@CELERY_APP.task()
|
||||||
|
def outpost_local_connection():
|
||||||
|
"""Checks the local environment and create Service connections."""
|
||||||
|
# Explicitly check against token filename, as thats
|
||||||
|
# only present when the integration is enabled
|
||||||
|
if Path(SERVICE_TOKEN_FILENAME).exists():
|
||||||
|
LOGGER.debug("Detected in-cluster Kubernetes Config")
|
||||||
|
if not KubernetesServiceConnection.objects.filter(local=True).exists():
|
||||||
|
LOGGER.debug("Created Service Connection for in-cluster")
|
||||||
|
KubernetesServiceConnection.objects.create(
|
||||||
|
name="Local Kubernetes Cluster", local=True, kubeconfig={}
|
||||||
|
)
|
||||||
|
# For development, check for the existence of a kubeconfig file
|
||||||
|
kubeconfig_path = expanduser(KUBE_CONFIG_DEFAULT_LOCATION)
|
||||||
|
if Path(kubeconfig_path).exists():
|
||||||
|
LOGGER.debug("Detected kubeconfig")
|
||||||
|
kubeconfig_local_name = f"k8s-{gethostname()}"
|
||||||
|
if not KubernetesServiceConnection.objects.filter(
|
||||||
|
name=kubeconfig_local_name
|
||||||
|
).exists():
|
||||||
|
LOGGER.debug("Creating kubeconfig Service Connection")
|
||||||
|
with open(kubeconfig_path, "r") as _kubeconfig:
|
||||||
|
KubernetesServiceConnection.objects.create(
|
||||||
|
name=kubeconfig_local_name,
|
||||||
|
kubeconfig=yaml.safe_load(_kubeconfig),
|
||||||
|
)
|
||||||
|
unix_socket_path = urlparse(DEFAULT_UNIX_SOCKET).path
|
||||||
|
socket = Path(unix_socket_path)
|
||||||
|
if socket.exists() and access(socket, R_OK):
|
||||||
|
LOGGER.debug("Detected local docker socket")
|
||||||
|
if len(DockerServiceConnection.objects.filter(local=True)) == 0:
|
||||||
|
LOGGER.debug("Created Service Connection for docker")
|
||||||
|
DockerServiceConnection.objects.create(
|
||||||
|
name="Local Docker connection",
|
||||||
|
local=True,
|
||||||
|
url=unix_socket_path,
|
||||||
|
)
|
||||||
|
|
|
@ -13,13 +13,13 @@ from selenium.webdriver.common.by import By
|
||||||
from authentik import __version__
|
from authentik import __version__
|
||||||
from authentik.core.models import Application
|
from authentik.core.models import Application
|
||||||
from authentik.flows.models import Flow
|
from authentik.flows.models import Flow
|
||||||
from authentik.outposts.apps import AuthentikOutpostConfig
|
|
||||||
from authentik.outposts.models import (
|
from authentik.outposts.models import (
|
||||||
DockerServiceConnection,
|
DockerServiceConnection,
|
||||||
Outpost,
|
Outpost,
|
||||||
OutpostConfig,
|
OutpostConfig,
|
||||||
OutpostType,
|
OutpostType,
|
||||||
)
|
)
|
||||||
|
from authentik.outposts.tasks import outpost_local_connection
|
||||||
from authentik.providers.proxy.models import ProxyProvider
|
from authentik.providers.proxy.models import ProxyProvider
|
||||||
from tests.e2e.utils import SeleniumTestCase, apply_migration, object_manager, retry
|
from tests.e2e.utils import SeleniumTestCase, apply_migration, object_manager, retry
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ class TestProviderProxyConnect(ChannelsLiveServerTestCase):
|
||||||
@object_manager
|
@object_manager
|
||||||
def test_proxy_connectivity(self):
|
def test_proxy_connectivity(self):
|
||||||
"""Test proxy connectivity over websocket"""
|
"""Test proxy connectivity over websocket"""
|
||||||
AuthentikOutpostConfig.init_local_connection()
|
outpost_local_connection()
|
||||||
proxy: ProxyProvider = ProxyProvider.objects.create(
|
proxy: ProxyProvider = ProxyProvider.objects.create(
|
||||||
name="proxy_provider",
|
name="proxy_provider",
|
||||||
authorization_flow=Flow.objects.get(
|
authorization_flow=Flow.objects.get(
|
||||||
|
|
|
@ -12,9 +12,9 @@ from docker.types.healthcheck import Healthcheck
|
||||||
from authentik import __version__
|
from authentik import __version__
|
||||||
from authentik.crypto.models import CertificateKeyPair
|
from authentik.crypto.models import CertificateKeyPair
|
||||||
from authentik.flows.models import Flow
|
from authentik.flows.models import Flow
|
||||||
from authentik.outposts.apps import AuthentikOutpostConfig
|
|
||||||
from authentik.outposts.controllers.docker import DockerController
|
from authentik.outposts.controllers.docker import DockerController
|
||||||
from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostType
|
from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostType
|
||||||
|
from authentik.outposts.tasks import outpost_local_connection
|
||||||
from authentik.providers.proxy.models import ProxyProvider
|
from authentik.providers.proxy.models import ProxyProvider
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ class OutpostDockerTests(TestCase):
|
||||||
self.ssl_folder = mkdtemp()
|
self.ssl_folder = mkdtemp()
|
||||||
self.container = self._start_container(self.ssl_folder)
|
self.container = self._start_container(self.ssl_folder)
|
||||||
# Ensure that local connection have been created
|
# Ensure that local connection have been created
|
||||||
AuthentikOutpostConfig.init_local_connection()
|
outpost_local_connection()
|
||||||
self.provider: ProxyProvider = ProxyProvider.objects.create(
|
self.provider: ProxyProvider = ProxyProvider.objects.create(
|
||||||
name="test",
|
name="test",
|
||||||
internal_host="http://localhost",
|
internal_host="http://localhost",
|
||||||
|
|
|
@ -3,11 +3,11 @@ from django.test import TestCase
|
||||||
|
|
||||||
from authentik.flows.models import Flow
|
from authentik.flows.models import Flow
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.outposts.apps import AuthentikOutpostConfig
|
|
||||||
from authentik.outposts.controllers.k8s.base import NeedsUpdate
|
from authentik.outposts.controllers.k8s.base import NeedsUpdate
|
||||||
from authentik.outposts.controllers.k8s.deployment import DeploymentReconciler
|
from authentik.outposts.controllers.k8s.deployment import DeploymentReconciler
|
||||||
from authentik.outposts.controllers.kubernetes import KubernetesController
|
from authentik.outposts.controllers.kubernetes import KubernetesController
|
||||||
from authentik.outposts.models import KubernetesServiceConnection, Outpost, OutpostType
|
from authentik.outposts.models import KubernetesServiceConnection, Outpost, OutpostType
|
||||||
|
from authentik.outposts.tasks import outpost_local_connection
|
||||||
from authentik.providers.proxy.models import ProxyProvider
|
from authentik.providers.proxy.models import ProxyProvider
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ class OutpostKubernetesTests(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
# Ensure that local connection have been created
|
# Ensure that local connection have been created
|
||||||
AuthentikOutpostConfig.init_local_connection()
|
outpost_local_connection()
|
||||||
self.provider: ProxyProvider = ProxyProvider.objects.create(
|
self.provider: ProxyProvider = ProxyProvider.objects.create(
|
||||||
name="test",
|
name="test",
|
||||||
internal_host="http://localhost",
|
internal_host="http://localhost",
|
||||||
|
|
|
@ -12,8 +12,8 @@ from docker.types.healthcheck import Healthcheck
|
||||||
from authentik import __version__
|
from authentik import __version__
|
||||||
from authentik.crypto.models import CertificateKeyPair
|
from authentik.crypto.models import CertificateKeyPair
|
||||||
from authentik.flows.models import Flow
|
from authentik.flows.models import Flow
|
||||||
from authentik.outposts.apps import AuthentikOutpostConfig
|
|
||||||
from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostType
|
from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostType
|
||||||
|
from authentik.outposts.tasks import outpost_local_connection
|
||||||
from authentik.providers.proxy.controllers.docker import DockerController
|
from authentik.providers.proxy.controllers.docker import DockerController
|
||||||
from authentik.providers.proxy.models import ProxyProvider
|
from authentik.providers.proxy.models import ProxyProvider
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ class TestProxyDocker(TestCase):
|
||||||
self.ssl_folder = mkdtemp()
|
self.ssl_folder = mkdtemp()
|
||||||
self.container = self._start_container(self.ssl_folder)
|
self.container = self._start_container(self.ssl_folder)
|
||||||
# Ensure that local connection have been created
|
# Ensure that local connection have been created
|
||||||
AuthentikOutpostConfig.init_local_connection()
|
outpost_local_connection()
|
||||||
self.provider: ProxyProvider = ProxyProvider.objects.create(
|
self.provider: ProxyProvider = ProxyProvider.objects.create(
|
||||||
name="test",
|
name="test",
|
||||||
internal_host="http://localhost",
|
internal_host="http://localhost",
|
||||||
|
|
|
@ -3,8 +3,8 @@ import yaml
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from authentik.flows.models import Flow
|
from authentik.flows.models import Flow
|
||||||
from authentik.outposts.apps import AuthentikOutpostConfig
|
|
||||||
from authentik.outposts.models import KubernetesServiceConnection, Outpost, OutpostType
|
from authentik.outposts.models import KubernetesServiceConnection, Outpost, OutpostType
|
||||||
|
from authentik.outposts.tasks import outpost_local_connection
|
||||||
from authentik.providers.proxy.controllers.kubernetes import ProxyKubernetesController
|
from authentik.providers.proxy.controllers.kubernetes import ProxyKubernetesController
|
||||||
from authentik.providers.proxy.models import ProxyProvider
|
from authentik.providers.proxy.models import ProxyProvider
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ class TestProxyKubernetes(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# Ensure that local connection have been created
|
# Ensure that local connection have been created
|
||||||
AuthentikOutpostConfig.init_local_connection()
|
outpost_local_connection()
|
||||||
|
|
||||||
def test_kubernetes_controller_static(self):
|
def test_kubernetes_controller_static(self):
|
||||||
"""Test Kubernetes Controller"""
|
"""Test Kubernetes Controller"""
|
||||||
|
|
Reference in New Issue