outposts: create traefikmiddleware if forwardAuth is enabled

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-05-07 00:01:35 +02:00
parent 241d790e69
commit ea7f9f291f
4 changed files with 167 additions and 4 deletions

View file

@ -42,7 +42,7 @@ class KubernetesObjectReconciler(Generic[T]):
def __init__(self, controller: "KubernetesController"): def __init__(self, controller: "KubernetesController"):
self.controller = controller self.controller = controller
self.namespace = controller.outpost.config.kubernetes_namespace self.namespace = controller.outpost.config.kubernetes_namespace
self.logger = get_logger() self.logger = get_logger().bind(type=self.__class__.__name__)
@property @property
def name(self) -> str: def name(self) -> str:
@ -79,7 +79,7 @@ class KubernetesObjectReconciler(Generic[T]):
self.delete(current) self.delete(current)
else: else:
self.logger.debug("No old found, creating") self.logger.debug("No old found, creating")
self.logger.debug("Created") self.logger.debug("Creating")
self.create(reference) self.create(reference)
except NeedsUpdate: except NeedsUpdate:
self.logger.debug("Updating") self.logger.debug("Updating")

View file

@ -0,0 +1,160 @@
"""Kubernetes Traefik Middleware Reconciler"""
from dataclasses import asdict, dataclass, field
from typing import TYPE_CHECKING
from dacite import from_dict
from kubernetes.client import ApiextensionsV1Api, CustomObjectsApi
from authentik.outposts.controllers.base import FIELD_MANAGER
from authentik.outposts.controllers.k8s.base import (
Disabled,
KubernetesObjectReconciler,
NeedsUpdate,
)
from authentik.providers.proxy.models import ProxyProvider
if TYPE_CHECKING:
from authentik.outposts.controllers.kubernetes import KubernetesController
@dataclass
class TraefikMiddlewareSpecForwardAuth:
"""traefik middleware forwardAuth spec"""
address: str
# pylint: disable=invalid-name
authResponseHeaders: list[str]
# pylint: disable=invalid-name
trustForwardHeader: bool
@dataclass
class TraefikMiddlewareSpec:
"""Traefik middleware spec"""
# pylint: disable=invalid-name
forwardAuth: TraefikMiddlewareSpecForwardAuth
@dataclass
class TraefikMiddlewareMetadata:
"""Traefik Middleware metadata"""
name: str
namespace: str
labels: dict = field(default_factory=dict)
@dataclass
class TraefikMiddleware:
"""Traefik Middleware"""
# pylint: disable=invalid-name
apiVersion: str
kind: str
metadata: TraefikMiddlewareMetadata
spec: TraefikMiddlewareSpec
CRD_NAME = "middlewares.traefik.containo.us"
CRD_GROUP = "traefik.containo.us"
CRD_VERSION = "v1alpha1"
CRD_PLURAL = "middlewares"
class TraefikMiddlewareReconciler(KubernetesObjectReconciler[TraefikMiddleware]):
"""Kubernetes Traefik Middleware Reconciler"""
def __init__(self, controller: "KubernetesController") -> None:
super().__init__(controller)
self.api_ex = ApiextensionsV1Api(controller.client)
self.api = CustomObjectsApi(controller.client)
def _crd_exists(self) -> bool:
"""Check if the traefik middleware exists"""
return bool(
len(
self.api_ex.list_custom_resource_definition(
field_selector=f"metadata.name={CRD_NAME}"
).items
)
)
def reconcile(self, current: TraefikMiddleware, reference: TraefikMiddleware):
super().reconcile(current, reference)
if current.spec.forwardAuth.address != reference.spec.forwardAuth.address:
raise NeedsUpdate()
def get_reference_object(self) -> TraefikMiddleware:
"""Get deployment object for outpost"""
if not ProxyProvider.objects.filter(
outpost__in=[self.controller.outpost],
forward_auth_mode=True,
).exists():
raise Disabled()
if not self._crd_exists():
raise Disabled()
return TraefikMiddleware(
apiVersion=f"{CRD_GROUP}/{CRD_VERSION}",
kind="Middleware",
metadata=TraefikMiddlewareMetadata(
name=self.name,
namespace=self.namespace,
labels=self.get_object_meta().labels,
),
spec=TraefikMiddlewareSpec(
forwardAuth=TraefikMiddlewareSpecForwardAuth(
address=f"http://{self.name}:4180/akprox/auth?traefik",
authResponseHeaders=[
"Set-Cookie",
"X-Auth-Username",
"X-Forwarded-Email",
"X-Forwarded-Preferred-Username",
"X-Forwarded-User",
],
trustForwardHeader=True,
)
),
)
def create(self, reference: TraefikMiddleware):
return self.api.create_namespaced_custom_object(
group=CRD_GROUP,
version=CRD_VERSION,
plural=CRD_PLURAL,
namespace=self.namespace,
body=asdict(reference),
field_manager=FIELD_MANAGER,
)
def delete(self, reference: TraefikMiddleware):
return self.api.delete_namespaced_custom_object(
group=CRD_GROUP,
version=CRD_VERSION,
namespace=self.namespace,
plural=CRD_PLURAL,
name=self.name,
)
def retrieve(self) -> TraefikMiddleware:
return from_dict(
TraefikMiddleware,
self.api.get_namespaced_custom_object(
group=CRD_GROUP,
version=CRD_VERSION,
namespace=self.namespace,
plural=CRD_PLURAL,
name=self.name,
),
)
def update(self, current: TraefikMiddleware, reference: TraefikMiddleware):
return self.api.patch_namespaced_custom_object(
group=CRD_GROUP,
version=CRD_VERSION,
namespace=self.namespace,
plural=CRD_PLURAL,
name=self.name,
body=asdict(reference),
field_manager=FIELD_MANAGER,
)

View file

@ -3,6 +3,9 @@ from authentik.outposts.controllers.base import DeploymentPort
from authentik.outposts.controllers.kubernetes import KubernetesController from authentik.outposts.controllers.kubernetes import KubernetesController
from authentik.outposts.models import KubernetesServiceConnection, Outpost from authentik.outposts.models import KubernetesServiceConnection, Outpost
from authentik.providers.proxy.controllers.k8s.ingress import IngressReconciler from authentik.providers.proxy.controllers.k8s.ingress import IngressReconciler
from authentik.providers.proxy.controllers.k8s.traefik import (
TraefikMiddlewareReconciler,
)
class ProxyKubernetesController(KubernetesController): class ProxyKubernetesController(KubernetesController):
@ -15,4 +18,6 @@ class ProxyKubernetesController(KubernetesController):
DeploymentPort(4443, "https", "tcp"), DeploymentPort(4443, "https", "tcp"),
] ]
self.reconcilers["ingress"] = IngressReconciler self.reconcilers["ingress"] = IngressReconciler
self.reconcilers["traefik_middleware"] = TraefikMiddlewareReconciler
self.reconcile_order.append("ingress") self.reconcile_order.append("ingress")
self.reconcile_order.append("traefik_middleware")

View file

@ -10658,8 +10658,6 @@ paths:
description: '' description: ''
schema: schema:
$ref: '#/definitions/RedirectChallenge' $ref: '#/definitions/RedirectChallenge'
'404':
description: Token not found
'400': '400':
description: Invalid input. description: Invalid input.
schema: schema: