Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions authentik/brands/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ class CurrentBrandSerializer(PassiveSerializer):

matched_domain = CharField(source="domain")
branding_title = CharField()
branding_logo = CharField()
branding_favicon = CharField()
branding_logo = CharField(source="branding_logo_url")
branding_favicon = CharField(source="branding_favicon_url")
ui_footer_links = ListField(
child=FooterLinkSerializer(),
read_only=True,
Expand Down
13 changes: 13 additions & 0 deletions authentik/brands/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from authentik.crypto.models import CertificateKeyPair
from authentik.flows.models import Flow
from authentik.lib.config import CONFIG
from authentik.lib.models import SerializerModel

LOGGER = get_logger()
Expand Down Expand Up @@ -60,6 +61,18 @@ class Brand(SerializerModel):
)
attributes = models.JSONField(default=dict, blank=True)

def branding_logo_url(self) -> str:
"""Get branding_logo with the correct prefix"""
if self.branding_logo.startswith("/static"):
return CONFIG.get("web.path", "/")[:-1] + self.branding_logo
return self.branding_logo

def branding_favicon_url(self) -> str:
"""Get branding_favicon with the correct prefix"""
if self.branding_favicon.startswith("/static"):
return CONFIG.get("web.path", "/")[:-1] + self.branding_favicon
return self.branding_favicon

@property
def serializer(self) -> Serializer:
from authentik.brands.api import BrandSerializer
Expand Down
3 changes: 3 additions & 0 deletions authentik/core/templates/base/header_js.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
versionFamily: "{{ version_family }}",
versionSubdomain: "{{ version_subdomain }}",
build: "{{ build }}",
api: {
base: "{{ base_url }}",
},
};
window.addEventListener("DOMContentLoaded", () => {
{% for message in messages %}
Expand Down
4 changes: 2 additions & 2 deletions authentik/core/templates/base/skeleton.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>{% block title %}{% trans title|default:brand.branding_title %}{% endblock %}</title>
<link rel="icon" href="{{ brand.branding_favicon }}">
<link rel="shortcut icon" href="{{ brand.branding_favicon }}">
<link rel="icon" href="{{ brand.branding_favicon_url }}">
<link rel="shortcut icon" href="{{ brand.branding_favicon_url }}">
{% block head_before %}
{% endblock %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}">
Expand Down
6 changes: 3 additions & 3 deletions authentik/core/templates/login/base_full.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{% load i18n %}

{% block head_before %}
<link rel="prefetch" href="/static/dist/assets/images/flow_background.jpg" />
<link rel="prefetch" href="{% static 'dist/assets/images/flow_background.jpg' %}" />
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/theme-dark.css' %}" media="(prefers-color-scheme: dark)">
{% include "base/header_js.html" %}
Expand All @@ -13,7 +13,7 @@
{% block head %}
<style>
:root {
--ak-flow-background: url("/static/dist/assets/images/flow_background.jpg");
--ak-flow-background: url("{% static 'dist/assets/images/flow_background.jpg' %}");
--pf-c-background-image--BackgroundImage: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage-2x: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage--sm: var(--ak-flow-background);
Expand Down Expand Up @@ -50,7 +50,7 @@
<div class="ak-login-container">
<main class="pf-c-login__main">
<div class="pf-c-login__main-header pf-c-brand ak-brand">
<img src="{{ brand.branding_logo }}" alt="authentik Logo" />
<img src="{{ brand.branding_logo_url }}" alt="authentik Logo" />
</div>
<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
Expand Down
2 changes: 2 additions & 0 deletions authentik/core/views/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from authentik.api.v3.config import ConfigView
from authentik.brands.api import CurrentBrandSerializer
from authentik.flows.models import Flow
from authentik.lib.config import CONFIG


class InterfaceView(TemplateView):
Expand All @@ -24,6 +25,7 @@ def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
kwargs["version_subdomain"] = f"version-{LOCAL_VERSION.major}-{LOCAL_VERSION.minor}"
kwargs["build"] = get_build_hash()
kwargs["url_kwargs"] = self.kwargs
kwargs["base_url"] = self.request.build_absolute_uri(CONFIG.get("web.path", "/"))
return super().get_context_data(**kwargs)


Expand Down
4 changes: 2 additions & 2 deletions authentik/enterprise/providers/rac/templates/if/rac.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<script src="{% static 'dist/enterprise/rac/index.js' %}?version={{ version }}" type="module"></script>
<meta name="theme-color" content="#18191a" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
<link rel="icon" href="{{ tenant.branding_favicon }}">
<link rel="shortcut icon" href="{{ tenant.branding_favicon }}">
<link rel="icon" href="{{ tenant.branding_favicon_url }}">
<link rel="shortcut icon" href="{{ tenant.branding_favicon_url }}">
{% include "base/header_js.html" %}
{% endblock %}

Expand Down
1 change: 1 addition & 0 deletions authentik/lib/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ web:
# No default here as it's set dynamically
# workers: 2
threads: 4
path: /

worker:
concurrency: 2
Expand Down
3 changes: 2 additions & 1 deletion authentik/lib/sentry.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from authentik.lib.utils.reflection import get_env

LOGGER = get_logger()
_root_path = CONFIG.get("web.path", "/")


class SentryIgnoredException(Exception):
Expand Down Expand Up @@ -89,7 +90,7 @@ def traces_sampler(sampling_context: dict) -> float:
path = sampling_context.get("asgi_scope", {}).get("path", "")
_type = sampling_context.get("asgi_scope", {}).get("type", "")
# Ignore all healthcheck routes
if path.startswith("/-/health") or path.startswith("/-/metrics"):
if path.startswith(f"{_root_path}-/health") or path.startswith(f"{_root_path}-/metrics"):
return 0
if _type == "websocket":
return 0
Expand Down
4 changes: 3 additions & 1 deletion authentik/root/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
# Custom user model
AUTH_USER_MODEL = "authentik_core.User"

CSRF_COOKIE_PATH = LANGUAGE_COOKIE_PATH = SESSION_COOKIE_PATH = CONFIG.get("web.path", "/")

CSRF_COOKIE_NAME = "authentik_csrf"
CSRF_HEADER_NAME = "HTTP_X_AUTHENTIK_CSRF"
LANGUAGE_COOKIE_NAME = "authentik_language"
Expand Down Expand Up @@ -389,7 +391,7 @@
# https://docs.djangoproject.com/en/2.1/howto/static-files/

STATICFILES_DIRS = [BASE_DIR / Path("web")]
STATIC_URL = "/static/"
STATIC_URL = CONFIG.get("web.path", "/") + "static/"

STORAGES = {
"staticfiles": {
Expand Down
9 changes: 6 additions & 3 deletions authentik/root/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from structlog.stdlib import get_logger

from authentik.core.views import error
from authentik.lib.config import CONFIG
from authentik.lib.utils.reflection import get_apps
from authentik.root.monitoring import LiveView, MetricsView, ReadyView

Expand All @@ -14,7 +15,7 @@
handler404 = error.NotFoundView.as_view()
handler500 = error.ServerErrorView.as_view()

urlpatterns = []
_urlpatterns = []

for _authentik_app in get_apps():
mountpoints = None
Expand All @@ -35,16 +36,18 @@
namespace=namespace,
),
)
urlpatterns.append(_path)
_urlpatterns.append(_path)
LOGGER.debug(
"Mounted URLs",
app_name=_authentik_app.name,
app_mountpoint=mountpoint,
namespace=namespace,
)

urlpatterns += [
_urlpatterns += [
path("-/metrics/", MetricsView.as_view(), name="metrics"),
path("-/health/live/", LiveView.as_view(), name="health-live"),
path("-/health/ready/", ReadyView.as_view(), name="health-ready"),
]

urlpatterns = [path(CONFIG.get("web.path", "/")[1:], include(_urlpatterns))]
14 changes: 12 additions & 2 deletions authentik/root/websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

from importlib import import_module

from channels.routing import URLRouter
from django.urls import path
from structlog.stdlib import get_logger

from authentik.lib.config import CONFIG
from authentik.lib.utils.reflection import get_apps

LOGGER = get_logger()

websocket_urlpatterns = []
_websocket_urlpatterns = []
for _authentik_app in get_apps():
try:
api_urls = import_module(f"{_authentik_app.name}.urls")
Expand All @@ -17,8 +20,15 @@
if not hasattr(api_urls, "websocket_urlpatterns"):
continue
urls: list = api_urls.websocket_urlpatterns
websocket_urlpatterns.extend(urls)
_websocket_urlpatterns.extend(urls)
LOGGER.debug(
"Mounted Websocket URLs",
app_name=_authentik_app.name,
)

websocket_urlpatterns = [
path(
CONFIG.get("web.path", "/")[1:],
URLRouter(_websocket_urlpatterns),
),
]
2 changes: 1 addition & 1 deletion cmd/server/healthcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func checkServer() int {
h := &http.Client{
Transport: web.NewUserAgentTransport("goauthentik.io/healthcheck", http.DefaultTransport),
}
url := fmt.Sprintf("http://%s/-/health/live/", config.Get().Listen.HTTP)
url := fmt.Sprintf("http://%s%s-/health/live/", config.Get().Listen.HTTP, config.Get().Web.Path)
res, err := h.Head(url)
if err != nil {
log.WithError(err).Warning("failed to send healthcheck request")
Expand Down
2 changes: 1 addition & 1 deletion cmd/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ var rootCmd = &cobra.Command{
ex := common.Init()
defer common.Defer()

u, err := url.Parse(fmt.Sprintf("http://%s", config.Get().Listen.HTTP))
u, err := url.Parse(fmt.Sprintf("http://%s%s", config.Get().Listen.HTTP, config.Get().Web.Path))
if err != nil {
panic(err)
}
Expand Down
5 changes: 5 additions & 0 deletions internal/config/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Config struct {
// Config for both core and outposts
Debug bool `yaml:"debug" env:"AUTHENTIK_DEBUG, overwrite"`
Listen ListenConfig `yaml:"listen" env:", prefix=AUTHENTIK_LISTEN__"`
Web WebConfig `yaml:"web" env:", prefix=AUTHENTIK_WEB__"`

// Outpost specific config
// These are only relevant for proxy/ldap outposts, and cannot be set via YAML
Expand Down Expand Up @@ -71,3 +72,7 @@ type OutpostConfig struct {
Discover bool `yaml:"discover" env:"DISCOVER, overwrite"`
DisableEmbeddedOutpost bool `yaml:"disable_embedded_outpost" env:"DISABLE_EMBEDDED_OUTPOST, overwrite"`
}

type WebConfig struct {
Path string `yaml:"path" env:"PATH, overwrite"`
}
17 changes: 11 additions & 6 deletions internal/outpost/ak/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ type APIController struct {
func NewAPIController(akURL url.URL, token string) *APIController {
rsp := sentry.StartSpan(context.Background(), "authentik.outposts.init")

config := api.NewConfiguration()
config.Host = akURL.Host
config.Scheme = akURL.Scheme
config.HTTPClient = &http.Client{
apiConfig := api.NewConfiguration()
apiConfig.Host = akURL.Host
apiConfig.Scheme = akURL.Scheme
apiConfig.HTTPClient = &http.Client{
Transport: web.NewUserAgentTransport(
constants.OutpostUserAgent(),
web.NewTracingTransport(
Expand All @@ -66,10 +66,15 @@ func NewAPIController(akURL url.URL, token string) *APIController {
),
),
}
config.AddDefaultHeader("Authorization", fmt.Sprintf("Bearer %s", token))
apiConfig.Servers = api.ServerConfigurations{
{
URL: fmt.Sprintf("%sapi/v3", akURL.Path),
},
}
apiConfig.AddDefaultHeader("Authorization", fmt.Sprintf("Bearer %s", token))

// create the API client, with the transport
apiClient := api.NewAPIClient(config)
apiClient := api.NewAPIClient(apiConfig)

log := log.WithField("logger", "authentik.outpost.ak-api-controller")

Expand Down
5 changes: 3 additions & 2 deletions internal/outpost/ak/api_ws.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
)

func (ac *APIController) initWS(akURL url.URL, outpostUUID string) error {
pathTemplate := "%s://%s/ws/outpost/%s/?%s"
pathTemplate := "%s://%s%sws/outpost/%s/?%s"
query := akURL.Query()
query.Set("instance_uuid", ac.instanceUUID.String())
scheme := strings.ReplaceAll(akURL.Scheme, "http", "ws")
Expand All @@ -37,7 +37,7 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID string) error {
},
}

ws, _, err := dialer.Dial(fmt.Sprintf(pathTemplate, scheme, akURL.Host, outpostUUID, akURL.Query().Encode()), header)
ws, _, err := dialer.Dial(fmt.Sprintf(pathTemplate, scheme, akURL.Host, akURL.Path, outpostUUID, akURL.Query().Encode()), header)
if err != nil {
ac.logger.WithError(err).Warning("failed to connect websocket")
return err
Expand Down Expand Up @@ -83,6 +83,7 @@ func (ac *APIController) reconnectWS() {
u := url.URL{
Host: ac.Client.GetConfig().Host,
Scheme: ac.Client.GetConfig().Scheme,
Path: strings.ReplaceAll(ac.Client.GetConfig().Servers[0].URL, "api/v3", ""),
}
attempt := 1
for {
Expand Down
2 changes: 1 addition & 1 deletion internal/web/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (ws *WebServer) runMetricsServer() {
).ServeHTTP(rw, r)

// Get upstream metrics
re, err := http.NewRequest("GET", fmt.Sprintf("%s/-/metrics/", ws.ul.String()), nil)
re, err := http.NewRequest("GET", fmt.Sprintf("%s%s-/metrics/", ws.upstreamURL.String(), config.Get().Web.Path), nil)
if err != nil {
l.WithError(err).Warning("failed to get upstream metrics")
return
Expand Down
9 changes: 5 additions & 4 deletions internal/web/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import (
"time"

"github.com/prometheus/client_golang/prometheus"
"goauthentik.io/internal/config"
"goauthentik.io/internal/utils/sentry"
)

func (ws *WebServer) configureProxy() {
// Reverse proxy to the application server
director := func(req *http.Request) {
req.URL.Scheme = ws.ul.Scheme
req.URL.Host = ws.ul.Host
req.URL.Scheme = ws.upstreamURL.Scheme
req.URL.Host = ws.upstreamURL.Host
if _, ok := req.Header["User-Agent"]; !ok {
// explicitly disable User-Agent so it's not set to default value
req.Header.Set("User-Agent", "")
Expand All @@ -32,10 +33,10 @@ 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) {
ws.mainRouter.PathPrefix(config.Get().Web.Path).Path("/-/health/live/").HandlerFunc(sentry.SentryNoSample(func(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(204)
Comment thread
rissson marked this conversation as resolved.
}))
ws.m.PathPrefix("/").HandlerFunc(sentry.SentryNoSample(func(rw http.ResponseWriter, r *http.Request) {
ws.mainRouter.PathPrefix(config.Get().Web.Path).HandlerFunc(sentry.SentryNoSample(func(rw http.ResponseWriter, r *http.Request) {
if !ws.g.IsRunning() {
ws.proxyErrorHandler(rw, r, errors.New("authentik starting"))
return
Expand Down
Loading