mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
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.
This commit is contained in:
parent
36d65c284e
commit
0922b3c215
61
example/traefik/config.minimal.yml
Normal file
61
example/traefik/config.minimal.yml
Normal file
|
@ -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
|
38
example/traefik/docker-compose.yml
Normal file
38
example/traefik/docker-compose.yml
Normal file
|
@ -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
|
30
example/traefik/traefik.toml
Normal file
30
example/traefik/traefik.toml
Normal file
|
@ -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
|
29
example/traefik/users_database.yml
Normal file
29
example/traefik/users_database.yml
Normal file
|
@ -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.");
|
||||||
|
|
20
server/src/lib/utils/RequestUrlGetter.ts
Normal file
20
server/src/lib/utils/RequestUrlGetter.ts
Normal file
|
@ -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
Block a user