tests/e2e: add forward auth e2e test (#11374)

* add nginx forward_auth e2e tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add envoy

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* cleanup

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* remove even more duplicate code

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* cleanup more

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add traefik static config

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* more cleanup, don't generate dex config cause they support env variables

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* use default dex entrypoint to use templating

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* remove options that are always set as default

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix compose flag

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add caddy

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* merge python files

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* use whoami api to check better

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix envoy config

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* set invalidation flow

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix logout checks

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens L.
2024-10-16 18:01:59 +02:00
committed by GitHub
parent c4caef4c38
commit 89f251d559
24 changed files with 678 additions and 302 deletions

View File

@ -180,7 +180,7 @@ jobs:
uses: ./.github/actions/setup
- name: Setup e2e env (chrome, etc)
run: |
docker compose -f tests/e2e/docker-compose.yml up -d
docker compose -f tests/e2e/docker-compose.yml up -d --quiet-pull
- id: cache-web
uses: actions/cache@v4
with:

View File

@ -0,0 +1,21 @@
http://localhost {
# directive execution order is only as stated if enclosed with route.
route {
# always forward outpost path to actual outpost
reverse_proxy /outpost.goauthentik.io/* http://ak-test-outpost:9000
# forward authentication to outpost
forward_auth http://ak-test-outpost:9000 {
uri /outpost.goauthentik.io/auth/caddy
# capitalization of the headers is important, otherwise they will be empty
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version
# optional, in this config trust all private ranges, should probably be set to the outposts IP
trusted_proxies private_ranges
}
# actual site configuration below, for example
reverse_proxy ak-whoami
}
}

View File

@ -0,0 +1,99 @@
# yaml-language-server: $schema=https://github.com/jcchavezs/envoy-config-schema/releases/download/v1.21.0/v3_Bootstrap.json
static_resources:
listeners:
- name: main_listener
address:
socket_address:
address: 0.0.0.0
port_value: 10000
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
upgrade_configs:
- upgrade_type: websocket
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
http_filters:
- name: envoy.filters.http.ext_authz
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
transport_api_version: V3
http_service:
path_prefix: /outpost.goauthentik.io/auth/envoy
server_uri:
uri: http://ak-test-outpost:9000
cluster: authentik_outpost
timeout: 0.25s
authorization_request:
allowed_headers:
patterns:
- exact: "cookie"
ignore_case: true
authorization_response:
allowed_upstream_headers:
patterns:
- exact: "set-cookie"
ignore_case: true
- prefix: "x-authentik-"
ignore_case: true
allowed_client_headers_on_success:
patterns:
- exact: "cookie"
ignore_case: true
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["localhost"]
routes:
- match:
prefix: "/outpost.goauthentik.io"
route:
cluster: authentik_outpost
- match:
prefix: "/"
route:
cluster: whoami
- name: local_service
domains: ["*"]
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
disabled: true
routes:
- match:
prefix: "/"
route:
cluster: authentik_outpost
clusters:
- name: authentik_outpost
type: LOGICAL_DNS
load_assignment:
cluster_name: authentik_outpost
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: ak-test-outpost
port_value: 9000
- name: whoami
type: LOGICAL_DNS
load_assignment:
cluster_name: whoami
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: ak-whoami
port_value: 80

View File

@ -0,0 +1,59 @@
server {
listen 80;
server_name _;
# Increase buffer size for large headers
# This is needed only if you get 'upstream sent too big header while reading response
# header from upstream' error when trying to access an application protected by goauthentik
proxy_buffers 8 16k;
proxy_buffer_size 32k;
location / {
proxy_pass http://ak-whoami;
proxy_set_header Host $host;
##############################
# authentik-specific config
##############################
auth_request /outpost.goauthentik.io/auth/nginx;
error_page 401 = @goauthentik_proxy_signin;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
# translate headers from the outposts back to the actual upstream
auth_request_set $authentik_username $upstream_http_x_authentik_username;
auth_request_set $authentik_groups $upstream_http_x_authentik_groups;
auth_request_set $authentik_email $upstream_http_x_authentik_email;
auth_request_set $authentik_name $upstream_http_x_authentik_name;
auth_request_set $authentik_uid $upstream_http_x_authentik_uid;
proxy_set_header X-authentik-username $authentik_username;
proxy_set_header X-authentik-groups $authentik_groups;
proxy_set_header X-authentik-email $authentik_email;
proxy_set_header X-authentik-name $authentik_name;
proxy_set_header X-authentik-uid $authentik_uid;
}
# all requests to /outpost.goauthentik.io must be accessible without authentication
location /outpost.goauthentik.io {
proxy_pass http://ak-test-outpost:9000/outpost.goauthentik.io;
# ensure the host of this vserver matches your external URL you've configured
# in authentik
proxy_set_header Host $host;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
add_header Set-Cookie $auth_cookie;
auth_request_set $auth_cookie $upstream_http_set_cookie;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}
# Special location for when the /auth endpoint returns a 401,
# redirect to the /start URL which initiates SSO
location @goauthentik_proxy_signin {
internal;
add_header Set-Cookie $auth_cookie;
return 302 /outpost.goauthentik.io/start?rd=$request_uri;
# For domain level, use the below error_page to redirect to your authentik server with the full redirect path
# return 302 https://localhost/outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri;
}
}

View File

@ -0,0 +1,57 @@
# yaml-language-server: $schema=https://json.schemastore.org/traefik-v2.json
api:
insecure: true
debug: true
log:
level: debug
accessLog:
filePath: /dev/stdout
entryPoints:
web:
address: ":80"
# Re-use the same config file to define everything
providers:
file:
filename: /etc/traefik/traefik.yml
http:
middlewares:
authentik:
forwardAuth:
address: http://ak-test-outpost:9000/outpost.goauthentik.io/auth/traefik
trustForwardHeader: true
authResponseHeaders:
- X-authentik-username
- X-authentik-groups
- X-authentik-email
- X-authentik-name
- X-authentik-uid
- X-authentik-jwt
- X-authentik-meta-jwks
- X-authentik-meta-outpost
- X-authentik-meta-provider
- X-authentik-meta-app
- X-authentik-meta-version
routers:
default-router:
rule: "Host(`localhost`)"
middlewares:
- authentik
priority: 10
service: app
default-router-auth:
rule: "Host(`localhost`) && PathPrefix(`/outpost.goauthentik.io/`)"
priority: 15
service: authentik
services:
app:
loadBalancer:
servers:
- url: http://ak-whoami
authentik:
loadBalancer:
servers:
- url: http://ak-test-outpost:9000/outpost.goauthentik.io

View File

@ -0,0 +1,23 @@
---
enablePasswordDB: true
issuer: http://127.0.0.1:5556/dex
logger:
level: debug
staticClients:
- id: example-app
name: Example App
redirectURIs:
- {{ .Env.AK_REDIRECT_URL }}
secret: {{ .Env.AK_CLIENT_SECRET }}
staticPasswords:
- email: admin@example.com
# hash for 'password', for testing
hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
username: admin
storage:
config:
file: "/tmp/dex.db"
type: sqlite3
web:
http: 0.0.0.0:5556

View File

@ -3,8 +3,6 @@
from dataclasses import asdict
from time import sleep
from docker.client import DockerClient, from_env
from docker.models.containers import Container
from guardian.shortcuts import assign_perm
from ldap3 import ALL, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, SUBTREE, Connection, Server
from ldap3.core.exceptions import LDAPInvalidCredentialsResult
@ -24,29 +22,18 @@ from tests.e2e.utils import SeleniumTestCase, retry
class TestProviderLDAP(SeleniumTestCase):
"""LDAP and Outpost e2e tests"""
ldap_container: Container
def tearDown(self) -> None:
super().tearDown()
self.output_container_logs(self.ldap_container)
self.ldap_container.kill()
def start_ldap(self, outpost: Outpost) -> Container:
def start_ldap(self, outpost: Outpost):
"""Start ldap container based on outpost created"""
client: DockerClient = from_env()
container = client.containers.run(
self.run_container(
image=self.get_container_image("ghcr.io/goauthentik/dev-ldap"),
detach=True,
ports={
"3389": "3389",
"6636": "6636",
},
environment={
"AUTHENTIK_HOST": self.live_server_url,
"AUTHENTIK_TOKEN": outpost.token.key,
},
)
return container
def _prepare(self) -> User:
"""prepare user, provider, app and container"""
@ -68,7 +55,7 @@ class TestProviderLDAP(SeleniumTestCase):
)
outpost.providers.add(ldap)
self.ldap_container = self.start_ldap(outpost)
self.start_ldap(outpost)
# Wait until outpost healthcheck succeeds
healthcheck_retries = 0

View File

@ -1,7 +1,6 @@
"""test OAuth Provider flow"""
from time import sleep
from typing import Any
from docker.types import Healthcheck
from selenium.webdriver.common.by import By
@ -24,22 +23,17 @@ class TestProviderOAuth2Github(SeleniumTestCase):
self.client_id = generate_id()
self.client_secret = generate_key()
super().setUp()
def get_container_specs(self) -> dict[str, Any] | None:
"""Setup client grafana container which we test OAuth against"""
return {
"image": "grafana/grafana:7.1.0",
"detach": True,
"ports": {
self.run_container(
image="grafana/grafana:7.1.0",
ports={
"3000": "3000",
},
"auto_remove": True,
"healthcheck": Healthcheck(
healthcheck=Healthcheck(
test=["CMD", "wget", "--spider", "http://localhost:3000"],
interval=5 * 1_000 * 1_000_000,
start_period=1 * 1_000 * 1_000_000,
),
"environment": {
environment={
"GF_AUTH_GITHUB_ENABLED": "true",
"GF_AUTH_GITHUB_ALLOW_SIGN_UP": "true",
"GF_AUTH_GITHUB_CLIENT_ID": self.client_id,
@ -54,7 +48,7 @@ class TestProviderOAuth2Github(SeleniumTestCase):
"GF_AUTH_GITHUB_API_URL": self.url("authentik_providers_oauth2_root:github-user"),
"GF_LOG_LEVEL": "debug",
},
}
)
@retry()
@apply_blueprint(

View File

@ -1,7 +1,6 @@
"""test OAuth2 OpenID Provider flow"""
from time import sleep
from typing import Any
from docker.types import Healthcheck
from selenium.webdriver.common.by import By
@ -32,21 +31,17 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
self.client_secret = generate_key()
self.app_slug = generate_id(20)
super().setUp()
def get_container_specs(self) -> dict[str, Any] | None:
return {
"image": "grafana/grafana:7.1.0",
"detach": True,
"auto_remove": True,
"healthcheck": Healthcheck(
self.run_container(
image="grafana/grafana:7.1.0",
ports={
"3000": "3000",
},
healthcheck=Healthcheck(
test=["CMD", "wget", "--spider", "http://localhost:3000"],
interval=5 * 1_000 * 1_000_000,
start_period=1 * 1_000 * 1_000_000,
),
"ports": {
"3000": "3000",
},
"environment": {
environment={
"GF_AUTH_GENERIC_OAUTH_ENABLED": "true",
"GF_AUTH_GENERIC_OAUTH_CLIENT_ID": self.client_id,
"GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET": self.client_secret,
@ -60,7 +55,7 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
),
"GF_LOG_LEVEL": "debug",
},
}
)
@retry()
@apply_blueprint(

View File

@ -3,8 +3,6 @@
from json import loads
from time import sleep
from docker import DockerClient, from_env
from docker.models.containers import Container
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
@ -34,13 +32,11 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
self.application_slug = generate_id()
super().setUp()
def setup_client(self) -> Container:
def setup_client(self):
"""Setup client oidc-test-client container which we test OIDC against"""
sleep(1)
client: DockerClient = from_env()
container = client.containers.run(
self.run_container(
image="ghcr.io/beryju/oidc-test-client:2.1",
detach=True,
ports={
"9009": "9009",
},
@ -50,8 +46,6 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
"OIDC_PROVIDER": f"{self.live_server_url}/application/o/{self.application_slug}/",
},
)
self.wait_for_container(container)
return container
@retry()
@apply_blueprint(
@ -91,7 +85,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
slug=self.application_slug,
provider=provider,
)
self.container = self.setup_client()
self.setup_client()
self.driver.get("http://localhost:9009")
sleep(2)
@ -140,7 +134,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
slug=self.application_slug,
provider=provider,
)
self.container = self.setup_client()
self.setup_client()
self.driver.get("http://localhost:9009")
self.login()
@ -210,7 +204,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
slug=self.application_slug,
provider=provider,
)
self.container = self.setup_client()
self.setup_client()
self.driver.get("http://localhost:9009")
self.login()
@ -287,7 +281,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
)
PolicyBinding.objects.create(target=app, policy=negative_policy, order=0)
self.container = self.setup_client()
self.setup_client()
self.driver.get("http://localhost:9009")
self.login()
self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "header > h1")))

View File

@ -3,8 +3,6 @@
from json import loads
from time import sleep
from docker import DockerClient, from_env
from docker.models.containers import Container
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
@ -34,13 +32,11 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
self.application_slug = "test"
super().setUp()
def setup_client(self) -> Container:
def setup_client(self):
"""Setup client oidc-test-client container which we test OIDC against"""
sleep(1)
client: DockerClient = from_env()
container = client.containers.run(
self.run_container(
image="ghcr.io/beryju/oidc-test-client:2.1",
detach=True,
ports={
"9009": "9009",
},
@ -50,8 +46,6 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
"OIDC_PROVIDER": f"{self.live_server_url}/application/o/{self.application_slug}/",
},
)
self.wait_for_container(container)
return container
@retry()
@apply_blueprint(
@ -93,7 +87,7 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
slug=self.application_slug,
provider=provider,
)
self.container = self.setup_client()
self.setup_client()
self.driver.get("http://localhost:9009/implicit/")
sleep(2)
@ -142,7 +136,7 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
slug=self.application_slug,
provider=provider,
)
self.container = self.setup_client()
self.setup_client()
self.driver.get("http://localhost:9009/implicit/")
self.wait.until(ec.title_contains("authentik"))
@ -194,7 +188,7 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
slug=self.application_slug,
provider=provider,
)
self.container = self.setup_client()
self.setup_client()
self.driver.get("http://localhost:9009/implicit/")
self.wait.until(ec.title_contains("authentik"))
@ -268,7 +262,7 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
)
PolicyBinding.objects.create(target=app, policy=negative_policy, order=0)
self.container = self.setup_client()
self.setup_client()
self.driver.get("http://localhost:9009/implicit/")
self.wait.until(ec.title_contains("authentik"))
self.login()

View File

@ -2,14 +2,12 @@
from base64 import b64encode
from dataclasses import asdict
from json import loads
from sys import platform
from time import sleep
from typing import Any
from unittest.case import skip, skipUnless
from channels.testing import ChannelsLiveServerTestCase
from docker.client import DockerClient, from_env
from docker.models.containers import Container
from selenium.webdriver.common.by import By
from authentik.blueprints.tests import apply_blueprint, reconcile_app
@ -25,38 +23,26 @@ from tests.e2e.utils import SeleniumTestCase, retry
class TestProviderProxy(SeleniumTestCase):
"""Proxy and Outpost e2e tests"""
proxy_container: Container
def tearDown(self) -> None:
super().tearDown()
self.output_container_logs(self.proxy_container)
self.proxy_container.kill()
def get_container_specs(self) -> dict[str, Any] | None:
return {
"image": "traefik/whoami:latest",
"detach": True,
"ports": {
def setUp(self):
super().setUp()
self.run_container(
image="traefik/whoami:latest",
ports={
"80": "80",
},
"auto_remove": True,
}
)
def start_proxy(self, outpost: Outpost) -> Container:
def start_proxy(self, outpost: Outpost):
"""Start proxy container based on outpost created"""
client: DockerClient = from_env()
container = client.containers.run(
self.run_container(
image=self.get_container_image("ghcr.io/goauthentik/dev-proxy"),
detach=True,
ports={
"9000": "9000",
},
environment={
"AUTHENTIK_HOST": self.live_server_url,
"AUTHENTIK_TOKEN": outpost.token.key,
},
)
return container
@retry()
@apply_blueprint(
@ -99,7 +85,7 @@ class TestProviderProxy(SeleniumTestCase):
outpost.providers.add(proxy)
outpost.build_user_permissions(outpost.user)
self.proxy_container = self.start_proxy(outpost)
self.start_proxy(outpost)
# Wait until outpost healthcheck succeeds
healthcheck_retries = 0
@ -112,13 +98,15 @@ class TestProviderProxy(SeleniumTestCase):
sleep(0.5)
sleep(5)
self.driver.get("http://localhost:9000")
self.driver.get("http://localhost:9000/api")
self.login()
sleep(1)
full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text
self.assertIn(f"X-Authentik-Username: {self.user.username}", full_body_text)
self.assertIn("X-Foo: bar", full_body_text)
body = loads(full_body_text)
self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username])
self.assertEqual(body["headers"]["X-Foo"], ["bar"])
self.driver.get("http://localhost:9000/outpost.goauthentik.io/sign_out")
sleep(2)
@ -173,7 +161,7 @@ class TestProviderProxy(SeleniumTestCase):
outpost.providers.add(proxy)
outpost.build_user_permissions(outpost.user)
self.proxy_container = self.start_proxy(outpost)
self.start_proxy(outpost)
# Wait until outpost healthcheck succeeds
healthcheck_retries = 0
@ -186,14 +174,16 @@ class TestProviderProxy(SeleniumTestCase):
sleep(0.5)
sleep(5)
self.driver.get("http://localhost:9000")
self.driver.get("http://localhost:9000/api")
self.login()
sleep(1)
full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text
self.assertIn(f"X-Authentik-Username: {self.user.username}", full_body_text)
body = loads(full_body_text)
self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username])
auth_header = b64encode(f"{cred}:{cred}".encode()).decode()
self.assertIn(f"Authorization: Basic {auth_header}", full_body_text)
self.assertEqual(body["headers"]["Authorization"], [f"Basic {auth_header}"])
self.driver.get("http://localhost:9000/outpost.goauthentik.io/sign_out")
sleep(2)

View File

@ -0,0 +1,227 @@
"""Proxy and Outpost e2e tests"""
from json import loads
from pathlib import Path
from time import sleep
from selenium.webdriver.common.by import By
from authentik.blueprints.tests import apply_blueprint, reconcile_app
from authentik.core.models import Application
from authentik.flows.models import Flow
from authentik.lib.generators import generate_id
from authentik.outposts.models import Outpost, OutpostType
from authentik.providers.proxy.models import ProxyMode, ProxyProvider
from tests.e2e.utils import SeleniumTestCase, retry
class TestProviderProxyForward(SeleniumTestCase):
"""Proxy and Outpost e2e tests"""
def setUp(self):
super().setUp()
self.run_container(
image="traefik/whoami:latest",
name="ak-whoami",
)
def start_outpost(self, outpost: Outpost):
"""Start proxy container based on outpost created"""
self.run_container(
image=self.get_container_image("ghcr.io/goauthentik/dev-proxy"),
ports={
"9000": "9000",
},
environment={
"AUTHENTIK_TOKEN": outpost.token.key,
},
name="ak-test-outpost",
)
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
@apply_blueprint(
"default/flow-default-provider-authorization-implicit-consent.yaml",
"default/flow-default-provider-invalidation.yaml",
)
@apply_blueprint(
"system/providers-oauth2.yaml",
"system/providers-proxy.yaml",
)
@reconcile_app("authentik_crypto")
def prepare(self):
proxy: ProxyProvider = ProxyProvider.objects.create(
name=generate_id(),
mode=ProxyMode.FORWARD_SINGLE,
authorization_flow=Flow.objects.get(
slug="default-provider-authorization-implicit-consent"
),
invalidation_flow=Flow.objects.get(slug="default-provider-invalidation-flow"),
internal_host=f"http://{self.host}",
external_host="http://localhost",
)
# Ensure OAuth2 Params are set
proxy.set_oauth_defaults()
proxy.save()
# we need to create an application to actually access the proxy
Application.objects.create(name=generate_id(), slug=generate_id(), provider=proxy)
outpost: Outpost = Outpost.objects.create(
name=generate_id(),
type=OutpostType.PROXY,
)
outpost.providers.add(proxy)
outpost.build_user_permissions(outpost.user)
self.start_outpost(outpost)
# Wait until outpost healthcheck succeeds
healthcheck_retries = 0
while healthcheck_retries < 50: # noqa: PLR2004
if len(outpost.state) > 0:
state = outpost.state[0]
if state.last_seen:
break
healthcheck_retries += 1
sleep(0.5)
sleep(5)
@retry()
def test_traefik(self):
"""Test traefik"""
local_config_path = (
Path(__file__).parent / "proxy_forward_auth" / "traefik_single" / "config-static.yaml"
)
self.run_container(
image="docker.io/library/traefik:3.1",
ports={
"80": "80",
},
volumes={
local_config_path: {
"bind": "/etc/traefik/traefik.yml",
}
},
)
self.prepare()
self.driver.get("http://localhost/api")
self.login()
sleep(1)
full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text
body = loads(full_body_text)
self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username])
self.driver.get("http://localhost/outpost.goauthentik.io/sign_out")
sleep(2)
flow_executor = self.get_shadow_root("ak-flow-executor")
session_end_stage = self.get_shadow_root("ak-stage-session-end", flow_executor)
title = session_end_stage.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
self.assertIn("You've logged out of", title)
@retry()
def test_nginx(self):
"""Test nginx"""
self.prepare()
# Start nginx last so all hosts are resolvable, otherwise nginx exits
self.run_container(
image="docker.io/library/nginx:1.27",
ports={
"80": "80",
},
volumes={
f"{Path(__file__).parent / "proxy_forward_auth" / "nginx_single" / "nginx.conf"}": {
"bind": "/etc/nginx/conf.d/default.conf",
}
},
)
self.driver.get("http://localhost/api")
self.login()
sleep(1)
full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text
body = loads(full_body_text)
self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username])
self.driver.get("http://localhost/outpost.goauthentik.io/sign_out")
sleep(2)
flow_executor = self.get_shadow_root("ak-flow-executor")
session_end_stage = self.get_shadow_root("ak-stage-session-end", flow_executor)
title = session_end_stage.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
self.assertIn("You've logged out of", title)
@retry()
def test_envoy(self):
"""Test envoy"""
self.run_container(
image="docker.io/envoyproxy/envoy:v1.25-latest",
ports={
"10000": "80",
},
volumes={
f"{Path(__file__).parent / "proxy_forward_auth" / "envoy_single" / "envoy.yaml"}": {
"bind": "/etc/envoy/envoy.yaml",
}
},
)
self.prepare()
self.driver.get("http://localhost/api")
self.login()
sleep(1)
full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text
body = loads(full_body_text)
self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username])
self.driver.get("http://localhost/outpost.goauthentik.io/sign_out")
sleep(2)
flow_executor = self.get_shadow_root("ak-flow-executor")
session_end_stage = self.get_shadow_root("ak-stage-session-end", flow_executor)
title = session_end_stage.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
self.assertIn("You've logged out of", title)
@retry()
def test_caddy(self):
"""Test caddy"""
local_config_path = (
Path(__file__).parent / "proxy_forward_auth" / "caddy_single" / "Caddyfile"
)
self.run_container(
image="docker.io/library/caddy:2.8",
ports={
"80": "80",
},
volumes={
local_config_path: {
"bind": "/etc/caddy/Caddyfile",
}
},
)
self.prepare()
self.driver.get("http://localhost/api")
self.login()
sleep(1)
full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text
body = loads(full_body_text)
self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username])
self.driver.get("http://localhost/outpost.goauthentik.io/sign_out")
sleep(2)
flow_executor = self.get_shadow_root("ak-flow-executor")
session_end_stage = self.get_shadow_root("ak-stage-session-end", flow_executor)
title = session_end_stage.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
self.assertIn("You've logged out of", title)

View File

@ -3,8 +3,6 @@
from dataclasses import asdict
from time import sleep
from docker.client import DockerClient, from_env
from docker.models.containers import Container
from pyrad.client import Client
from pyrad.dictionary import Dictionary
from pyrad.packet import AccessAccept, AccessReject, AccessRequest
@ -21,30 +19,19 @@ from tests.e2e.utils import SeleniumTestCase, retry
class TestProviderRadius(SeleniumTestCase):
"""Radius Outpost e2e tests"""
radius_container: Container
def setUp(self):
super().setUp()
self.shared_secret = generate_key()
def tearDown(self) -> None:
super().tearDown()
self.output_container_logs(self.radius_container)
self.radius_container.kill()
def start_radius(self, outpost: Outpost) -> Container:
def start_radius(self, outpost: Outpost):
"""Start radius container based on outpost created"""
client: DockerClient = from_env()
container = client.containers.run(
self.run_container(
image=self.get_container_image("ghcr.io/goauthentik/dev-radius"),
detach=True,
ports={"1812/udp": "1812/udp"},
environment={
"AUTHENTIK_HOST": self.live_server_url,
"AUTHENTIK_TOKEN": outpost.token.key,
},
)
return container
def _prepare(self) -> User:
"""prepare user, provider, app and container"""
@ -62,7 +49,7 @@ class TestProviderRadius(SeleniumTestCase):
)
outpost.providers.add(radius)
self.radius_container = self.start_radius(outpost)
self.start_radius(outpost)
# Wait until outpost healthcheck succeeds
healthcheck_retries = 0

View File

@ -3,8 +3,6 @@
from json import loads
from time import sleep
from docker import DockerClient, from_env
from docker.models.containers import Container
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
@ -22,11 +20,8 @@ from tests.e2e.utils import SeleniumTestCase, retry
class TestProviderSAML(SeleniumTestCase):
"""test SAML Provider flow"""
container: Container
def setup_client(self, provider: SAMLProvider, force_post: bool = False) -> Container:
def setup_client(self, provider: SAMLProvider, force_post: bool = False):
"""Setup client saml-sp container which we test SAML against"""
client: DockerClient = from_env()
metadata_url = (
self.url(
"authentik_api:samlprovider-metadata",
@ -36,9 +31,8 @@ class TestProviderSAML(SeleniumTestCase):
)
if force_post:
metadata_url += f"&force_binding={SAML_BINDING_POST}"
container = client.containers.run(
self.run_container(
image="ghcr.io/beryju/saml-test-sp:1.1",
detach=True,
ports={
"9009": "9009",
},
@ -48,8 +42,6 @@ class TestProviderSAML(SeleniumTestCase):
"SP_METADATA_URL": metadata_url,
},
)
self.wait_for_container(container)
return container
@retry()
@apply_blueprint(
@ -85,7 +77,7 @@ class TestProviderSAML(SeleniumTestCase):
slug="authentik-saml",
provider=provider,
)
self.container = self.setup_client(provider)
self.setup_client(provider)
self.driver.get("http://localhost:9009")
self.login()
self.wait_for_url("http://localhost:9009/")
@ -153,7 +145,7 @@ class TestProviderSAML(SeleniumTestCase):
slug="authentik-saml",
provider=provider,
)
self.container = self.setup_client(provider)
self.setup_client(provider)
self.driver.get("http://localhost:9009")
self.login()
@ -236,7 +228,7 @@ class TestProviderSAML(SeleniumTestCase):
slug="authentik-saml",
provider=provider,
)
self.container = self.setup_client(provider, True)
self.setup_client(provider, True)
self.driver.get("http://localhost:9009")
self.login()
@ -319,7 +311,7 @@ class TestProviderSAML(SeleniumTestCase):
slug="authentik-saml",
provider=provider,
)
self.container = self.setup_client(provider)
self.setup_client(provider)
self.driver.get(
self.url(
"authentik_providers_saml:sso-init",
@ -397,7 +389,7 @@ class TestProviderSAML(SeleniumTestCase):
provider=provider,
)
PolicyBinding.objects.create(target=app, policy=negative_policy, order=0)
self.container = self.setup_client(provider)
self.setup_client(provider)
self.driver.get("http://localhost:9009/")
self.login()
@ -444,7 +436,7 @@ class TestProviderSAML(SeleniumTestCase):
slug="authentik-saml",
provider=provider,
)
self.container = self.setup_client(provider)
self.setup_client(provider)
self.driver.get("http://localhost:9009")
self.login()
self.wait_for_url("http://localhost:9009/")

View File

@ -1,7 +1,5 @@
"""test LDAP Source"""
from typing import Any
from django.db.models import Q
from ldap3.core.exceptions import LDAPSessionTerminatedByServerError
@ -22,22 +20,18 @@ class TestSourceLDAPSamba(SeleniumTestCase):
def setUp(self):
self.admin_password = generate_key()
super().setUp()
def get_container_specs(self) -> dict[str, Any] | None:
return {
"image": "ghcr.io/beryju/test-samba-dc:latest",
"detach": True,
"cap_add": ["SYS_ADMIN"],
"ports": {
self.samba = self.run_container(
image="ghcr.io/beryju/test-samba-dc:latest",
cap_add=["SYS_ADMIN"],
ports={
"389": "389/tcp",
},
"auto_remove": True,
"environment": {
environment={
"SMB_DOMAIN": "test.goauthentik.io",
"SMB_NETBIOS": "goauthentik",
"SMB_ADMIN_PASSWORD": self.admin_password,
},
}
)
@retry(exceptions=[LDAPSessionTerminatedByServerError])
@apply_blueprint(
@ -148,7 +142,7 @@ class TestSourceLDAPSamba(SeleniumTestCase):
UserLDAPSynchronizer(source).sync_full()
username = "bob"
password = generate_id()
result = self.container.exec_run(
result = self.samba.exec_run(
["samba-tool", "user", "setpassword", username, "--newpassword", password]
)
self.assertEqual(result.exit_code, 0)
@ -163,7 +157,7 @@ class TestSourceLDAPSamba(SeleniumTestCase):
self.assertTrue(user.check_password(password))
# Set new password
new_password = generate_id()
result = self.container.exec_run(
result = self.samba.exec_run(
["samba-tool", "user", "setpassword", username, "--newpassword", new_password]
)
self.assertEqual(result.exit_code, 0)

View File

@ -56,14 +56,10 @@ class TestSourceOAuth1(SeleniumTestCase):
self.client_secret = generate_key()
self.source_slug = generate_id()
super().setUp()
def get_container_specs(self) -> dict[str, Any] | None:
return {
"image": "ghcr.io/beryju/oauth1-test-server:v1.1",
"detach": True,
"ports": {"5000": "5001"},
"auto_remove": True,
"environment": {
self.run_container(
image="ghcr.io/beryju/oauth1-test-server:v1.1",
ports={"5000": "5001"},
environment={
"OAUTH1_CLIENT_ID": self.client_id,
"OAUTH1_CLIENT_SECRET": self.client_secret,
"OAUTH1_REDIRECT_URI": self.url(
@ -71,7 +67,7 @@ class TestSourceOAuth1(SeleniumTestCase):
source_slug=self.source_slug,
),
},
}
)
def create_objects(self):
"""Create required objects"""

View File

@ -3,15 +3,12 @@
from json import loads
from pathlib import Path
from time import sleep
from typing import Any
from docker.models.containers import Container
from docker.types import Healthcheck
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.wait import WebDriverWait
from yaml import safe_dump
from authentik.blueprints.tests import apply_blueprint
from authentik.core.models import User
@ -21,69 +18,35 @@ from authentik.sources.oauth.models import OAuthSource
from authentik.stages.identification.models import IdentificationStage
from tests.e2e.utils import SeleniumTestCase, retry
CONFIG_PATH = "/tmp/dex.yml" # nosec
class TestSourceOAuth2(SeleniumTestCase):
"""test OAuth Source flow"""
container: Container
def setUp(self):
self.client_secret = generate_key()
self.slug = generate_id()
self.prepare_dex_config()
super().setUp()
def prepare_dex_config(self):
"""Since Dex does not document which environment
variables can be used to configure clients"""
config = {
"enablePasswordDB": True,
"issuer": "http://127.0.0.1:5556/dex",
"logger": {"level": "debug"},
"staticClients": [
{
"id": "example-app",
"name": "Example App",
"redirectURIs": [
self.url(
"authentik_sources_oauth:oauth-client-callback",
source_slug=self.slug,
)
],
"secret": self.client_secret,
}
],
"staticPasswords": [
{
"email": "admin@example.com",
# hash for password
"hash": "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W",
"userID": "08a8684b-db88-4b73-90a9-3cd1661f5466",
"username": "admin",
}
],
"storage": {"config": {"file": "/tmp/dex.db"}, "type": "sqlite3"}, # nosec
"web": {"http": "0.0.0.0:5556"},
}
with open(CONFIG_PATH, "w+", encoding="utf8") as _file:
safe_dump(config, _file)
def get_container_specs(self) -> dict[str, Any] | None:
return {
"image": "ghcr.io/dexidp/dex:v2.28.1",
"detach": True,
"ports": {"5556": "5556"},
"auto_remove": True,
"command": "dex serve /config.yml",
"healthcheck": Healthcheck(
self.run_container(
image="ghcr.io/dexidp/dex:v2.28.1",
ports={"5556": "5556"},
healthcheck=Healthcheck(
test=["CMD", "wget", "--spider", "http://localhost:5556/dex/healthz"],
interval=5 * 1_000 * 1_000_000,
start_period=1 * 1_000 * 1_000_000,
),
"volumes": {str(Path(CONFIG_PATH).absolute()): {"bind": "/config.yml", "mode": "ro"}},
}
environment={
"AK_REDIRECT_URL": self.url(
"authentik_sources_oauth:oauth-client-callback",
source_slug=self.slug,
),
"AK_CLIENT_SECRET": self.client_secret,
},
volumes={
f"{Path(__file__).parent / "sources_oauth2_dex" / "dex.yaml"}": {
"bind": "/etc/dex/config.docker.yaml",
}
},
)
def create_objects(self):
"""Create required objects"""

View File

@ -2,7 +2,6 @@
from pathlib import Path
from time import sleep
from typing import Any
from docker.types import Healthcheck
from selenium.webdriver.common.by import By
@ -77,19 +76,15 @@ class TestSourceSAML(SeleniumTestCase):
def setUp(self):
self.slug = generate_id()
super().setUp()
def get_container_specs(self) -> dict[str, Any] | None:
return {
"image": "kristophjunge/test-saml-idp:1.15",
"detach": True,
"ports": {"8080": "8080"},
"auto_remove": True,
"healthcheck": Healthcheck(
self.run_container(
image="kristophjunge/test-saml-idp:1.15",
ports={"8080": "8080"},
healthcheck=Healthcheck(
test=["CMD", "curl", "http://localhost:8080"],
interval=5 * 1_000 * 1_000_000,
start_period=1 * 1_000 * 1_000_000,
),
"volumes": {
volumes={
str(
(Path(__file__).parent / Path("test-saml-idp/saml20-sp-remote.php")).absolute()
): {
@ -97,7 +92,7 @@ class TestSourceSAML(SeleniumTestCase):
"mode": "ro",
}
},
"environment": {
environment={
"SIMPLESAMLPHP_SP_ENTITY_ID": "entity-id",
"SIMPLESAMLPHP_SP_NAME_ID_FORMAT": (
"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
@ -107,7 +102,7 @@ class TestSourceSAML(SeleniumTestCase):
self.url("authentik_sources_saml:acs", source_slug=self.slug)
),
},
}
)
@retry()
@apply_blueprint(

View File

@ -2,7 +2,6 @@
from pprint import pformat
from time import sleep
from typing import Any
from docker.types import Healthcheck
@ -20,22 +19,18 @@ class TestSourceSCIM(SeleniumTestCase):
def setUp(self):
self.slug = generate_id()
super().setUp()
def get_container_specs(self) -> dict[str, Any] | None:
return {
"image": (
self.run_container(
image=(
"ghcr.io/suvera/scim2-compliance-test-utility@sha256:eca913bb73"
"c46892cd1fb2dfd2fef1c5881e6abc5cb0eec7e92fb78c1b933ece"
),
"detach": True,
"ports": {"8080": "8080"},
"auto_remove": True,
"healthcheck": Healthcheck(
ports={"8080": "8080"},
healthcheck=Healthcheck(
test=["CMD", "curl", "http://localhost:8080"],
interval=5 * 1_000 * 1_000_000,
start_period=1 * 1_000 * 1_000_000,
),
}
)
@retry()
def test_scim_conformance(self):

View File

@ -9,6 +9,7 @@ from os import environ
from sys import stderr
from time import sleep
from typing import Any
from unittest.case import TestCase
from django.apps import apps
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
@ -19,6 +20,7 @@ from django.urls import reverse
from docker import DockerClient, from_env
from docker.errors import DockerException
from docker.models.containers import Container
from docker.models.networks import Network
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException, TimeoutException, WebDriverException
from selenium.webdriver.common.by import By
@ -31,6 +33,7 @@ from structlog.stdlib import get_logger
from authentik.core.api.users import UserSerializer
from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user
from authentik.lib.generators import generate_id
RETRIES = int(environ.get("RETRIES", "3"))
IS_CI = "CI" in environ
@ -54,9 +57,32 @@ def get_local_ip() -> str:
return ip_addr
class DockerTestCase:
class DockerTestCase(TestCase):
"""Mixin for dealing with containers"""
max_healthcheck_attempts = 30
__client: DockerClient
__network: Network
__label_id = generate_id()
def setUp(self) -> None:
self.__client = from_env()
self.__network = self.docker_client.networks.create(name=f"authentik-test-{generate_id()}")
@property
def docker_client(self) -> DockerClient:
return self.__client
@property
def docker_network(self) -> Network:
return self.__network
@property
def docker_labels(self) -> dict:
return {"io.goauthentik.test": self.__label_id}
def wait_for_container(self, container: Container):
"""Check that container is health"""
attempt = 0
@ -67,47 +93,30 @@ class DockerTestCase:
return container
sleep(1)
attempt += 1
if attempt >= 30: # noqa: PLR2004
if attempt >= self.max_healthcheck_attempts:
self.failureException("Container failed to start")
class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
"""StaticLiveServerTestCase which automatically creates a Webdriver instance"""
host = get_local_ip()
container: Container | None = None
wait_timeout: int
user: User
def setUp(self):
if IS_CI:
print("::group::authentik Logs", file=stderr)
super().setUp()
apps.get_app_config("authentik_tenants").ready()
self.wait_timeout = 60
self.driver = self._get_driver()
self.driver.implicitly_wait(30)
self.wait = WebDriverWait(self.driver, self.wait_timeout)
self.logger = get_logger()
self.user = create_test_admin_user()
if specs := self.get_container_specs():
self.container = self._start_container(specs)
def get_container_image(self, base: str) -> str:
"""Try to pull docker image based on git branch, fallback to main if not found."""
client: DockerClient = from_env()
image = f"{base}:gh-main"
try:
branch_image = f"{base}:{get_docker_tag()}"
client.images.pull(branch_image)
self.docker_client.images.pull(branch_image)
return branch_image
except DockerException:
client.images.pull(image)
self.docker_client.images.pull(image)
return image
def _start_container(self, specs: dict[str, Any]) -> Container:
client: DockerClient = from_env()
container = client.containers.run(**specs)
def run_container(self, **specs: dict[str, Any]) -> Container:
if "network_mode" not in specs:
specs["network"] = self.__network.name
specs["labels"] = self.docker_labels
specs["detach"] = True
specs["auto_remove"] = True
if hasattr(self, "live_server_url"):
specs.setdefault("environment", {})
specs["environment"]["AUTHENTIK_HOST"] = self.live_server_url
container = self.docker_client.containers.run(**specs)
container.reload()
state = container.attrs.get("State", {})
if "Health" not in state:
@ -117,25 +126,53 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
def output_container_logs(self, container: Container | None = None):
"""Output the container logs to our STDOUT"""
_container = container or self.container
if IS_CI:
image = _container.image
image = container.image
tags = image.tags[0] if len(image.tags) > 0 else str(image)
print(f"::group::Container logs - {tags}")
for log in _container.logs().decode().split("\n"):
for log in container.logs().decode().split("\n"):
print(log)
if IS_CI:
print("::endgroup::")
def get_container_specs(self) -> dict[str, Any] | None:
"""Optionally get container specs which will launched on setup, wait for the container to
be healthy, and deleted again on tearDown"""
return None
def tearDown(self):
containers = self.docker_client.containers.list(
filters={"label": ",".join(f"{x}={y}" for x, y in self.docker_labels.items())}
)
for container in containers:
self.output_container_logs(container)
try:
container.kill()
except DockerException:
pass
self.__network.remove()
class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
"""StaticLiveServerTestCase which automatically creates a Webdriver instance"""
host = get_local_ip()
wait_timeout: int
user: User
def setUp(self):
if IS_CI:
print("::group::authentik Logs", file=stderr)
apps.get_app_config("authentik_tenants").ready()
self.wait_timeout = 60
self.driver = self._get_driver()
self.driver.implicitly_wait(30)
self.wait = WebDriverWait(self.driver, self.wait_timeout)
self.logger = get_logger()
self.user = create_test_admin_user()
super().setUp()
def _get_driver(self) -> WebDriver:
count = 0
try:
return webdriver.Chrome()
opts = webdriver.ChromeOptions()
opts.add_argument("--disable-search-engine-choice-screen")
return webdriver.Chrome(options=opts)
except WebDriverException:
pass
while count < RETRIES:
@ -151,18 +188,15 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
raise ValueError(f"Webdriver failed after {RETRIES}.")
def tearDown(self):
super().tearDown()
if IS_CI:
print("::endgroup::", file=stderr)
super().tearDown()
if IS_CI:
print("::group::Browser logs")
for line in self.driver.get_log("browser"):
print(line["message"])
if IS_CI:
print("::endgroup::")
if self.container:
self.output_container_logs()
self.container.kill()
self.driver.quit()
def wait_for_url(self, desired_url):

View File

@ -6,8 +6,6 @@ from tempfile import mkdtemp
import pytest
import yaml
from channels.testing import ChannelsLiveServerTestCase
from docker import DockerClient, from_env
from docker.models.containers import Container
from docker.types.healthcheck import Healthcheck
from authentik.core.tests.utils import create_test_flow
@ -27,11 +25,11 @@ from tests.e2e.utils import DockerTestCase, get_docker_tag
class OutpostDockerTests(DockerTestCase, ChannelsLiveServerTestCase):
"""Test Docker Controllers"""
def _start_container(self, ssl_folder: str) -> Container:
client: DockerClient = from_env()
container = client.containers.run(
def setUp(self):
super().setUp()
self.ssl_folder = mkdtemp()
self.run_container(
image="library/docker:dind",
detach=True,
network_mode="host",
privileged=True,
healthcheck=Healthcheck(
@ -41,18 +39,11 @@ class OutpostDockerTests(DockerTestCase, ChannelsLiveServerTestCase):
),
environment={"DOCKER_TLS_CERTDIR": "/ssl"},
volumes={
f"{ssl_folder}/": {
f"{self.ssl_folder}/": {
"bind": "/ssl",
}
},
)
self.wait_for_container(container)
return container
def setUp(self):
super().setUp()
self.ssl_folder = mkdtemp()
self.container = self._start_container(self.ssl_folder)
# Ensure that local connection have been created
outpost_connection_discovery()
self.provider: ProxyProvider = ProxyProvider.objects.create(
@ -91,7 +82,6 @@ class OutpostDockerTests(DockerTestCase, ChannelsLiveServerTestCase):
def tearDown(self) -> None:
super().tearDown()
self.container.kill()
try:
rmtree(self.ssl_folder)
except PermissionError:

View File

@ -6,8 +6,6 @@ from tempfile import mkdtemp
import pytest
import yaml
from channels.testing.live import ChannelsLiveServerTestCase
from docker import DockerClient, from_env
from docker.models.containers import Container
from docker.types.healthcheck import Healthcheck
from authentik.core.tests.utils import create_test_flow
@ -27,11 +25,11 @@ from tests.e2e.utils import DockerTestCase, get_docker_tag
class TestProxyDocker(DockerTestCase, ChannelsLiveServerTestCase):
"""Test Docker Controllers"""
def _start_container(self, ssl_folder: str) -> Container:
client: DockerClient = from_env()
container = client.containers.run(
def setUp(self):
super().setUp()
self.ssl_folder = mkdtemp()
self.run_container(
image="library/docker:dind",
detach=True,
network_mode="host",
privileged=True,
healthcheck=Healthcheck(
@ -41,18 +39,11 @@ class TestProxyDocker(DockerTestCase, ChannelsLiveServerTestCase):
),
environment={"DOCKER_TLS_CERTDIR": "/ssl"},
volumes={
f"{ssl_folder}/": {
f"{self.ssl_folder}/": {
"bind": "/ssl",
}
},
)
self.wait_for_container(container)
return container
def setUp(self):
super().setUp()
self.ssl_folder = mkdtemp()
self.container = self._start_container(self.ssl_folder)
# Ensure that local connection have been created
outpost_connection_discovery()
self.provider: ProxyProvider = ProxyProvider.objects.create(
@ -91,7 +82,6 @@ class TestProxyDocker(DockerTestCase, ChannelsLiveServerTestCase):
def tearDown(self) -> None:
super().tearDown()
self.container.kill()
try:
rmtree(self.ssl_folder)
except PermissionError:

View File

@ -32,7 +32,7 @@ http:
app:
loadBalancer:
servers:
- url: http://ipp.internal
- url: http://ip.internal
authentik:
loadBalancer:
servers: