outposts: set cookies for a domain to authenticate an entire domain (#971)
* outposts: initial cookie domain implementation Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web/admin: add cookie domain setting Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * providers/proxy: replace forward_auth_mode with general mode Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web/admin: rebuild proxy provider form Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * providers/proxy: re-add forward_auth_mode for backwards compat Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web/admin: fix data.mode not being set Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * root: always set log level to debug when testing Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * providers/proxy: use new mode attribute Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * providers/proxy: only ingress /akprox on forward_domain Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * providers/proxy: fix lint error Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web/admin: fix error on ProxyProviderForm when not using proxy mode Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web/admin: fix default for outpost form's type missing Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web/admin: add additional desc for proxy modes Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * outposts: fix service account permissions not always being updated Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * outpost/proxy: fix redirecting to incorrect host for domain mode Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web: improve error handling for network errors Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * outpost: fix image naming not matching main imaeg Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * outposts/proxy: fix redirects for domain mode and traefik Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web: fix colour for paragraphs Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web/flows: fix consent stage not showing permissions correctly Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * website/docs: add domain-level docs Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * website/docs: fix broken links Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * outposts/proxy: remove dead code Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web/flows: fix missing id for #header-text Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
@ -14,7 +14,7 @@ import { ModelForm } from "../../elements/forms/ModelForm";
|
||||
export class OutpostForm extends ModelForm<Outpost, string> {
|
||||
|
||||
@property()
|
||||
type!: OutpostTypeEnum;
|
||||
type: OutpostTypeEnum = OutpostTypeEnum.Proxy;
|
||||
|
||||
loadInstance(pk: string): Promise<Outpost> {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesRetrieve({
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import { CryptoApi, FlowsApi, FlowsInstancesListDesignationEnum, ProvidersApi, ProxyProvider } from "authentik-api";
|
||||
import { CryptoApi, FlowsApi, FlowsInstancesListDesignationEnum, ProvidersApi, ProxyMode, ProxyProvider } from "authentik-api";
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement, property } from "lit-element";
|
||||
import { css, CSSResult, customElement, property } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||
import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
import PFToggleGroup from "@patternfly/patternfly/components/ToggleGroup/toggle-group.css";
|
||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
import "../../../elements/forms/HorizontalFormElement";
|
||||
@ -13,12 +16,20 @@ import { first } from "../../../utils";
|
||||
@customElement("ak-provider-proxy-form")
|
||||
export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> {
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return super.styles.concat(PFToggleGroup, PFContent, PFSpacing, css`
|
||||
.pf-c-toggle-group {
|
||||
justify-content: center;
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
loadInstance(pk: number): Promise<ProxyProvider> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersProxyRetrieve({
|
||||
id: pk,
|
||||
}).then(provider => {
|
||||
this.showHttpBasic = first(provider.basicAuthEnabled, true);
|
||||
this.showInternalServer = first(!provider.forwardAuthMode, true);
|
||||
this.mode = first(provider.mode, ProxyMode.Proxy);
|
||||
return provider;
|
||||
});
|
||||
}
|
||||
@ -26,8 +37,8 @@ export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> {
|
||||
@property({type: Boolean})
|
||||
showHttpBasic = true;
|
||||
|
||||
@property({type: Boolean})
|
||||
showInternalServer = true;
|
||||
@property({attribute: false})
|
||||
mode: ProxyMode = ProxyMode.Proxy;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
if (this.instance) {
|
||||
@ -38,6 +49,7 @@ export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> {
|
||||
}
|
||||
|
||||
send = (data: ProxyProvider): Promise<ProxyProvider> => {
|
||||
data.mode = this.mode;
|
||||
if (this.instance) {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersProxyUpdate({
|
||||
id: this.instance.pk || 0,
|
||||
@ -68,26 +80,94 @@ export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> {
|
||||
</ak-form-element-horizontal>`;
|
||||
}
|
||||
|
||||
renderInternalServer(): TemplateResult {
|
||||
if (!this.showInternalServer) {
|
||||
return html``;
|
||||
renderModeSelector(): TemplateResult {
|
||||
return html`
|
||||
<div class="pf-c-toggle-group__item">
|
||||
<button class="pf-c-toggle-group__button ${this.mode === ProxyMode.Proxy ? "pf-m-selected" : ""}" type="button" @click=${() => {
|
||||
this.mode = ProxyMode.Proxy;
|
||||
}}>
|
||||
<span class="pf-c-toggle-group__text">${t`Proxy`}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pf-c-divider pf-m-vertical" role="separator"></div>
|
||||
<div class="pf-c-toggle-group__item">
|
||||
<button class="pf-c-toggle-group__button ${this.mode === ProxyMode.ForwardSingle ? "pf-m-selected" : ""}" type="button" @click=${() => {
|
||||
this.mode = ProxyMode.ForwardSingle;
|
||||
}}>
|
||||
<span class="pf-c-toggle-group__text">${t`Forward auth (single application)`}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pf-c-divider pf-m-vertical" role="separator"></div>
|
||||
<div class="pf-c-toggle-group__item">
|
||||
<button class="pf-c-toggle-group__button ${this.mode === ProxyMode.ForwardDomain ? "pf-m-selected" : ""}" type="button" @click=${() => {
|
||||
this.mode = ProxyMode.ForwardDomain;
|
||||
}}>
|
||||
<span class="pf-c-toggle-group__text">${t`Forward auth (domain level)`}</span>
|
||||
</button>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
renderSettings(): TemplateResult {
|
||||
switch (this.mode) {
|
||||
case ProxyMode.Proxy:
|
||||
return html`
|
||||
<p class="pf-u-mb-xl">
|
||||
${t`This provider will behave like a transparent reverse-proxy, except requests must be authenticated. If your upstream application uses HTTPS, make sure to connect to the outpost using HTTPS as well.`}
|
||||
</p>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`External host`}
|
||||
?required=${true}
|
||||
name="externalHost">
|
||||
<input type="text" value="${ifDefined(this.instance?.externalHost)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`The external URL you'll access the application at. Include any non-standard port.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Internal host`}
|
||||
?required=${true}
|
||||
name="internalHost">
|
||||
<input type="text" value="${ifDefined(this.instance?.internalHost)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Upstream host that the requests are forwarded to.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="internalHostSslValidation">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.internalHostSslValidation, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Internal host SSL Validation`}
|
||||
</label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`Validate SSL Certificates of upstream servers.`}</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
case ProxyMode.ForwardSingle:
|
||||
return html`
|
||||
<p class="pf-u-mb-xl">
|
||||
${t`Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /akprox must be routed to the outpost (when using a manged outpost, this is done for you).`}
|
||||
</p>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`External host`}
|
||||
?required=${true}
|
||||
name="externalHost">
|
||||
<input type="text" value="${ifDefined(this.instance?.externalHost)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`The external URL you'll access the application at. Include any non-standard port.`}</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
case ProxyMode.ForwardDomain:
|
||||
return html`
|
||||
<p class="pf-u-mb-xl">
|
||||
${t`Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application.`}
|
||||
</p>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`External host`}
|
||||
?required=${true}
|
||||
name="externalHost">
|
||||
<input type="text" value="${first(this.instance?.externalHost, window.location.origin)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`The external URL you'll authenticate at. Can be the same domain as authentik.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Cookie domain`}
|
||||
name="cookieDomain">
|
||||
<input type="text" value="${ifDefined(this.instance?.cookieDomain)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Optionally set this to your parent domain, if you want authentication and authorization to happen on a domain level. If you're running applications as app1.domain.tld, app2.domain.tld, set this to 'domain.tld'.`}</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
}
|
||||
return html`<ak-form-element-horizontal
|
||||
label=${t`Internal host`}
|
||||
?required=${true}
|
||||
name="internalHost">
|
||||
<input type="text" value="${ifDefined(this.instance?.internalHost)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`Upstream host that the requests are forwarded to.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="internalHostSslValidation">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.internalHostSslValidation, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Internal host SSL Validation`}
|
||||
</label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">${t`Validate SSL Certificates of upstream servers.`}</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
@ -115,35 +195,16 @@ export class ProxyProviderFormPage extends ModelForm<ProxyProvider, number> {
|
||||
<p class="pf-c-form__helper-text">${t`Flow used when authorizing this provider.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-form-group .expanded=${true}>
|
||||
<span slot="header">
|
||||
${t`Protocol settings`}
|
||||
</span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`External host`}
|
||||
?required=${true}
|
||||
name="externalHost">
|
||||
<input type="text" value="${ifDefined(this.instance?.externalHost)}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">${t`The external URL you'll access the application at. Include any non-standard port.`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="forwardAuthMode">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.forwardAuthMode, false)} @change=${(ev: Event) => {
|
||||
const el = ev.target as HTMLInputElement;
|
||||
this.showInternalServer = !el.checked;
|
||||
}}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Enable forward-auth mode`}
|
||||
</label>
|
||||
</div>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Enable this if you don't want to use this provider as a proxy, and want to use it with Traefik's forwardAuth or nginx's auth_request.`}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
${this.renderInternalServer()}
|
||||
<div class="pf-c-card pf-m-selectable pf-m-selected">
|
||||
<div class="pf-c-card__body">
|
||||
<div class="pf-c-toggle-group">
|
||||
${this.renderModeSelector()}
|
||||
</div>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<div class="pf-c-card__footer">
|
||||
${this.renderSettings()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ak-form-group>
|
||||
<span slot="header">
|
||||
|
||||
@ -20,6 +20,7 @@ import "./ProxyProviderForm";
|
||||
import { ProvidersApi, ProxyProvider } from "authentik-api";
|
||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||
import { EVENT_REFRESH } from "../../../constants";
|
||||
import { convertToTitle } from "../../../utils";
|
||||
|
||||
@customElement("ak-provider-proxy-view")
|
||||
export class ProxyProviderViewPage extends LitElement {
|
||||
@ -115,18 +116,11 @@ export class ProxyProviderViewPage extends LitElement {
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text">${t`Forward auth`}</span>
|
||||
<span class="pf-c-description-list__text">${t`Mode`}</span>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
${this.provider.forwardAuthMode ?
|
||||
html`<span class="pf-c-button__icon pf-m-start">
|
||||
<i class="fas fa-check-circle" aria-hidden="true"></i>
|
||||
</span>${t`Yes`}`:
|
||||
html`<span class="pf-c-button__icon pf-m-start">
|
||||
<i class="fas fa-times-circle" aria-hidden="true"></i>
|
||||
</span>${t`No`}`
|
||||
}
|
||||
${convertToTitle(this.provider.mode || "")}
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user