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:
Jens Langhammer 2021-04-21 11:34:48 +02:00
parent aa0e8edb8b
commit b8a566f4a0
8 changed files with 66 additions and 63 deletions

View File

@ -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,
)

View File

@ -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"},
},
} }

View File

@ -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,
)

View File

@ -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(

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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"""