Build x-original-url from forwarded headers
This is to allow broader support for proxies. In particular, this allows support with Traefik. This patch also includes some examples of configuration with Traefik.pull/355/head
parent
36d65c284e
commit
0922b3c215
|
@ -0,0 +1,61 @@
|
||||||
|
###############################################################
|
||||||
|
# Authelia minimal configuration #
|
||||||
|
###############################################################
|
||||||
|
|
||||||
|
logs_level: debug
|
||||||
|
|
||||||
|
authentication_backend:
|
||||||
|
file:
|
||||||
|
path: /etc/authelia/users_database.yml
|
||||||
|
|
||||||
|
session:
|
||||||
|
secret: unsecure_session_secret
|
||||||
|
domain: example.com
|
||||||
|
|
||||||
|
# Configuration of the storage backend used to store data and secrets. i.e. totp data
|
||||||
|
storage:
|
||||||
|
local:
|
||||||
|
path: /etc/authelia/storage
|
||||||
|
|
||||||
|
# TOTP Issuer Name
|
||||||
|
#
|
||||||
|
# This will be the issuer name displayed in Google Authenticator
|
||||||
|
# See: https://github.com/google/google-authenticator/wiki/Key-Uri-Format for more info on issuer names
|
||||||
|
totp:
|
||||||
|
issuer: example.com
|
||||||
|
|
||||||
|
# Access Control
|
||||||
|
#
|
||||||
|
# Access control is a set of rules you can use to restrict user access to certain
|
||||||
|
# resources.
|
||||||
|
access_control:
|
||||||
|
# Default policy can either be `bypass`, `one_factor`, `two_factor` or `deny`.
|
||||||
|
default_policy: deny
|
||||||
|
|
||||||
|
rules:
|
||||||
|
- domain: traefik.example.com
|
||||||
|
policy: two_factor
|
||||||
|
- domain: who.example.com
|
||||||
|
policy: two_factor
|
||||||
|
|
||||||
|
# Configuration of the authentication regulation mechanism.
|
||||||
|
regulation:
|
||||||
|
# Set it to 0 to disable max_retries.
|
||||||
|
max_retries: 3
|
||||||
|
|
||||||
|
# The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window.
|
||||||
|
find_time: 120
|
||||||
|
|
||||||
|
# The length of time before a banned user can login again.
|
||||||
|
ban_time: 300
|
||||||
|
|
||||||
|
# Default redirection URL
|
||||||
|
#
|
||||||
|
# Note: this parameter is optional. If not provided, user won't
|
||||||
|
# be redirected upon successful authentication.
|
||||||
|
#default_redirection_url: https://authelia.example.domain
|
||||||
|
|
||||||
|
notifier:
|
||||||
|
# For testing purpose, notifications can be sent in a file
|
||||||
|
filesystem:
|
||||||
|
filename: /tmp/authelia/notification.txt
|
|
@ -0,0 +1,38 @@
|
||||||
|
version: '2'
|
||||||
|
services:
|
||||||
|
traefik:
|
||||||
|
image: traefik
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- ./traefik.toml:/etc/traefik/traefik.toml
|
||||||
|
- /etc/traefik
|
||||||
|
labels:
|
||||||
|
- traefik.frontend.rule=Host:traefik.example.com
|
||||||
|
- traefik.port=8080
|
||||||
|
- traefik.frontend.auth.forward.address=https://auth.example.com/api/verify?rd=https://auth.example.com
|
||||||
|
- traefik.frontend.auth.forward.tls.insecureSkipVerify=true
|
||||||
|
|
||||||
|
authelia:
|
||||||
|
# image: clems4ever/authelia:latest
|
||||||
|
build:
|
||||||
|
context: ../..
|
||||||
|
dockerfile: Dockerfile.dev
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./config.minimal.yml:/etc/authelia/config.yml:ro
|
||||||
|
- ./users_database.yml:/etc/authelia/users_database.yml:rw
|
||||||
|
- /tmp/authelia:/tmp/authelia
|
||||||
|
environment:
|
||||||
|
- NODE_TLS_REJECT_UNAUTHORIZED=1
|
||||||
|
labels:
|
||||||
|
- traefik.frontend.rule=Host:auth.example.com
|
||||||
|
|
||||||
|
whoami:
|
||||||
|
image: emilevauge/whoami
|
||||||
|
labels:
|
||||||
|
- traefik.frontend.rule=Host:who.example.com
|
||||||
|
- traefik.frontend.auth.forward.address=https://auth.example.com/api/verify?rd=https://auth.example.com
|
||||||
|
- traefik.frontend.auth.forward.tls.insecureSkipVerify=true
|
|
@ -0,0 +1,30 @@
|
||||||
|
defaultEntryPoints = ["http", "https"]
|
||||||
|
# logLevel = "DEBUG"
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
[entryPoints.http.redirect]
|
||||||
|
entryPoint = "https"
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
[entryPoints.api]
|
||||||
|
address = ":8080"
|
||||||
|
|
||||||
|
[api]
|
||||||
|
# This is exposed via a subdomain and a proxy
|
||||||
|
entryPoint = "api"
|
||||||
|
dashboard = true
|
||||||
|
|
||||||
|
[docker]
|
||||||
|
# Docker server endpoint. Can be a tcp or a unix socket endpoint.
|
||||||
|
endpoint = "unix:///var/run/docker.sock"
|
||||||
|
# network = "traefik_default"
|
||||||
|
|
||||||
|
# Default domain used.
|
||||||
|
# Can be overridden by setting the "traefik.domain" label on a container.
|
||||||
|
domain = "localhost"
|
||||||
|
|
||||||
|
# Enable watch docker changes
|
||||||
|
watch = true
|
|
@ -0,0 +1,29 @@
|
||||||
|
###############################################################
|
||||||
|
# Users Database #
|
||||||
|
###############################################################
|
||||||
|
|
||||||
|
# This file can be used if you do not have an LDAP set up.
|
||||||
|
|
||||||
|
# List of users
|
||||||
|
users:
|
||||||
|
john:
|
||||||
|
password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/"
|
||||||
|
email: john.doe@authelia.com
|
||||||
|
groups:
|
||||||
|
- admins
|
||||||
|
- dev
|
||||||
|
|
||||||
|
harry:
|
||||||
|
password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/"
|
||||||
|
emails: harry.potter@authelia.com
|
||||||
|
groups: []
|
||||||
|
|
||||||
|
bob:
|
||||||
|
password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/"
|
||||||
|
email: bob.dylan@authelia.com
|
||||||
|
groups:
|
||||||
|
- dev
|
||||||
|
|
||||||
|
james:
|
||||||
|
password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/"
|
||||||
|
email: james.dean@authelia.com
|
|
@ -9,8 +9,8 @@ import { AuthenticationSessionHandler }
|
||||||
from "../../AuthenticationSessionHandler";
|
from "../../AuthenticationSessionHandler";
|
||||||
import { AuthenticationSession }
|
import { AuthenticationSession }
|
||||||
from "../../../../types/AuthenticationSession";
|
from "../../../../types/AuthenticationSession";
|
||||||
import GetHeader from "../../utils/GetHeader";
|
|
||||||
import HasHeader from "../..//utils/HasHeader";
|
import HasHeader from "../..//utils/HasHeader";
|
||||||
|
import { RequestUrlGetter } from "../../utils/RequestUrlGetter";
|
||||||
|
|
||||||
|
|
||||||
async function verifyWithSelectedMethod(req: Express.Request, res: Express.Response,
|
async function verifyWithSelectedMethod(req: Express.Request, res: Express.Response,
|
||||||
|
@ -31,7 +31,7 @@ async function verifyWithSelectedMethod(req: Express.Request, res: Express.Respo
|
||||||
* @param res The response to write Redirect header to.
|
* @param res The response to write Redirect header to.
|
||||||
*/
|
*/
|
||||||
function setRedirectHeader(req: Express.Request, res: Express.Response) {
|
function setRedirectHeader(req: Express.Request, res: Express.Response) {
|
||||||
const originalUrl = GetHeader(req, Constants.HEADER_X_ORIGINAL_URL);
|
const originalUrl = RequestUrlGetter.getOriginalUrl(req);
|
||||||
res.set(Constants.HEADER_REDIRECT, originalUrl);
|
res.set(Constants.HEADER_REDIRECT, originalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,11 @@ import { ServerVariables } from "../../ServerVariables";
|
||||||
import { URLDecomposer } from "../../utils/URLDecomposer";
|
import { URLDecomposer } from "../../utils/URLDecomposer";
|
||||||
import { Level } from "../../authentication/Level";
|
import { Level } from "../../authentication/Level";
|
||||||
import GetHeader from "../../utils/GetHeader";
|
import GetHeader from "../../utils/GetHeader";
|
||||||
import { HEADER_X_ORIGINAL_URL, HEADER_PROXY_AUTHORIZATION } from "../../../../../shared/constants";
|
import { HEADER_PROXY_AUTHORIZATION } from "../../../../../shared/constants";
|
||||||
import setUserAndGroupsHeaders from "./SetUserAndGroupsHeaders";
|
import setUserAndGroupsHeaders from "./SetUserAndGroupsHeaders";
|
||||||
import CheckAuthorizations from "./CheckAuthorizations";
|
import CheckAuthorizations from "./CheckAuthorizations";
|
||||||
import { Level as AuthorizationLevel } from "../../authorization/Level";
|
import { Level as AuthorizationLevel } from "../../authorization/Level";
|
||||||
|
import { RequestUrlGetter } from "../../utils/RequestUrlGetter";
|
||||||
|
|
||||||
export default async function(req: Express.Request, res: Express.Response,
|
export default async function(req: Express.Request, res: Express.Response,
|
||||||
vars: ServerVariables)
|
vars: ServerVariables)
|
||||||
|
@ -38,7 +39,7 @@ export default async function(req: Express.Request, res: Express.Response,
|
||||||
const password = splittedToken[1];
|
const password = splittedToken[1];
|
||||||
const groupsAndEmails = await vars.usersDatabase.checkUserPassword(username, password);
|
const groupsAndEmails = await vars.usersDatabase.checkUserPassword(username, password);
|
||||||
|
|
||||||
const uri = GetHeader(req, HEADER_X_ORIGINAL_URL);
|
const uri = RequestUrlGetter.getOriginalUrl(req);
|
||||||
const urlDecomposition = URLDecomposer.fromUrl(uri);
|
const urlDecomposition = URLDecomposer.fromUrl(uri);
|
||||||
|
|
||||||
CheckAuthorizations(vars.authorizer, urlDecomposition.domain, urlDecomposition.path,
|
CheckAuthorizations(vars.authorizer, urlDecomposition.domain, urlDecomposition.path,
|
||||||
|
|
|
@ -3,13 +3,10 @@ import { ServerVariables } from "../../ServerVariables";
|
||||||
import { AuthenticationSession }
|
import { AuthenticationSession }
|
||||||
from "../../../../types/AuthenticationSession";
|
from "../../../../types/AuthenticationSession";
|
||||||
import { URLDecomposer } from "../../utils/URLDecomposer";
|
import { URLDecomposer } from "../../utils/URLDecomposer";
|
||||||
import GetHeader from "../../utils/GetHeader";
|
|
||||||
import {
|
|
||||||
HEADER_X_ORIGINAL_URL,
|
|
||||||
} from "../../../../../shared/constants";
|
|
||||||
import setUserAndGroupsHeaders from "./SetUserAndGroupsHeaders";
|
import setUserAndGroupsHeaders from "./SetUserAndGroupsHeaders";
|
||||||
import CheckAuthorizations from "./CheckAuthorizations";
|
import CheckAuthorizations from "./CheckAuthorizations";
|
||||||
import CheckInactivity from "./CheckInactivity";
|
import CheckInactivity from "./CheckInactivity";
|
||||||
|
import { RequestUrlGetter } from "../../utils/RequestUrlGetter";
|
||||||
|
|
||||||
|
|
||||||
export default async function (req: Express.Request, res: Express.Response,
|
export default async function (req: Express.Request, res: Express.Response,
|
||||||
|
@ -19,7 +16,7 @@ export default async function (req: Express.Request, res: Express.Response,
|
||||||
throw new Error("No cookie detected.");
|
throw new Error("No cookie detected.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const originalUrl = GetHeader(req, HEADER_X_ORIGINAL_URL);
|
const originalUrl = RequestUrlGetter.getOriginalUrl(req);
|
||||||
|
|
||||||
if (!originalUrl) {
|
if (!originalUrl) {
|
||||||
throw new Error("Cannot detect the original URL from headers.");
|
throw new Error("Cannot detect the original URL from headers.");
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import Constants = require("../../../../../shared/constants");
|
||||||
|
import Express = require("express");
|
||||||
|
import GetHeader from "../../utils/GetHeader";
|
||||||
|
import HasHeader from "../..//utils/HasHeader";
|
||||||
|
|
||||||
|
export class RequestUrlGetter {
|
||||||
|
static getOriginalUrl(req: Express.Request): string {
|
||||||
|
|
||||||
|
if HasHeader(req, Constants.HEADER_X_ORIGINAL_URL) {
|
||||||
|
return GetHeader(req, Constants.HEADER_X_ORIGINAL_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
const proto = GetHeader(req, Constants.HEADER_X_FORWARDED_PROTO);
|
||||||
|
const host = GetHeader(req, Constants.HEADER_X_FORWARDED_HOST);
|
||||||
|
const port = GetHeader(req, Constants.HEADER_X_FORWARDED_PORT);
|
||||||
|
const uri = GetHeader(req, Constants.HEADER_X_FORWARDED_URI);
|
||||||
|
|
||||||
|
return "${proto}://${host}:${port}${uri}";
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,10 @@ export const REDIRECT_QUERY_PARAM = "rd";
|
||||||
export const HEADER_X_TARGET_URL = "x-target-url";
|
export const HEADER_X_TARGET_URL = "x-target-url";
|
||||||
|
|
||||||
export const HEADER_X_ORIGINAL_URL = "x-original-url";
|
export const HEADER_X_ORIGINAL_URL = "x-original-url";
|
||||||
|
export const HEADER_X_FORWARDED_PROTO = "x-forwarded-proto";
|
||||||
|
export const HEADER_X_FORWARDED_HOST = "x-forwarded-host";
|
||||||
|
export const HEADER_X_FORWARDED_PORT = "x-forwarded-port";
|
||||||
|
export const HEADER_X_FORWARDED_URI = "x-forwarded-uri";
|
||||||
export const HEADER_PROXY_AUTHORIZATION = "proxy-authorization";
|
export const HEADER_PROXY_AUTHORIZATION = "proxy-authorization";
|
||||||
export const HEADER_REDIRECT = "redirect";
|
export const HEADER_REDIRECT = "redirect";
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue