Skip to content

Instantly share code, notes, and snippets.

@dstar4138
Last active February 4, 2021 00:45
Show Gist options
  • Save dstar4138/754c384d086d0b07ba3b072acc8f3957 to your computer and use it in GitHub Desktop.
Save dstar4138/754c384d086d0b07ba3b072acc8f3957 to your computer and use it in GitHub Desktop.
Authelia/Traefik ForwardAuth DokuWiki Auth Plugin.
<?php
/**
* ForwardAuth DokuWiki Auth Plugin.
*
* @licence Public Domain, use how you wish, I don't caare.
* @author Alexander Dean-Kennedy
* @version 0.0.1
*/
// must be run within Dokuwiki
if(!defined('DOKU_INC')) die();
/**
* Provides support for using ForwardAuth middlewares in reverse
* proxies for providing authentication to DokuWiki docker containers.
*
* NOTE: This is an extremely simple implementation that only focuses
* on looking-up HTTP Headers and does not support logout or even
* header-validation.
*
* USAGE: Configure via conf/local.php like so:
*
* $conf['authtype'] = "forwardauth";
* $conf['plugin']['forwardauth']['userHeader'] = 'Remote-User';
*
* // Optionally, you can also provide header names for the following:
* $conf['plugin']['forwardauth']['nameHeader'] = 'Remote-Name';
* $conf['plugin']['forwardauth']['mailHeader'] = 'Remote-Email';
* $conf['plugin']['forwardauth']['groupsHeader'] = 'Remote-Groups';
*
* By default, if no value is provided for the name, we will default to
* the value provided by 'userHeader'. Thus setting both the User's
* Name and the username to the value of 'userHeader'.
*
* If no headers are provided, we default to the header names used by
* Authelia, as it is what I use. Referenced here:
* https://github.com/authelia/authelia/blob/
* 7c6a86882f93515a148cadcce8eccd77c3f24433
* /internal/handlers/const.go#L20-L23
**/
class auth_plugin_forwardauth extends DokuWiki_Auth_Plugin
{
// Defaults for Authelia.
const USER_HEADER = "Remote-User";
const NAME_HEADER = "Remote-Name";
const MAIL_HEADER = "Remote-Mail";
const GRPS_HEADER = "Remote-Groups";
public function __construct()
{
parent::__construct();
$this->cando['external'] = true; // Assumes redirect already happened.
$this->cando['logout'] = false; // Logout happens elsewhere.
}
public function trustExternal($user, $pass, $sticky = false) {
// We assume $user is ALWAYS nil and overwrite with header value.
$data = $this->getUserData($user);
if ($data) {
return $this->fillGlobals($data);
}
return false;
}
private function fillGlobals($data) {
global $USERINFO;
$USERINFO['name'] = $data['user'];
$USERINFO['mail'] = $data['mail'];
$USERINFO['grps'] = $data['groups'];
$_SERVER['REMOTE_USER'] = $data['user'];
$_SESSION[DOKU_COOKIE]['auth']['user'] = $data['user'];
$_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO;
return true;
}
public function getUserData($user, $requireGroups = true)
{
// If no user is provided, no auth was done. Admin will
// need to fix their ForwardAuth in the reverse proxy.
// For example, setting the ForwardAuth middleware.
// I do not believe there is a course of action we can
// perform here as we don't know the middleware path.
$user = $this->getAuthUser();
if (empty($user)) { return false; }
$data = array();
$data['user'] = $user;
$data['name'] = $this->getAuthName();
$data['mail'] = $this->getAuthMail();
$data['groups'] = $this->getAuthGroups();
return $data;
}
/* Extract header values from HTTP Request. */
private function getAuthUser() {
return sane_lookup($this->getAuthUserHeader());
}
private function getAuthMail() {
return sane_lookup($this->getAuthMailHeader());
}
private function getAuthName() {
$name = sane_lookup($this->getAuthNameHeader());
if (empty($name)) { return $this->getAuthUser(); }
return $name;
}
private function getAuthGroups() {
$groups = sane_lookup($this->getAuthGroupsHeader());
return explode(',', $groups);
}
/* Generate PHP HTTP Header names from Config values. */
private function getAuthUserHeader() {
return header_to_php_name(
plugin_conf('userHeader', self::USER_HEADER)
);
}
private function getAuthNameHeader() {
return header_to_php_name(
plugin_conf('nameHeader', self::NAME_HEADER)
);
}
private function getAuthMailHeader() {
return header_to_php_name(
plugin_conf('mailHeader', self::MAIL_HEADER)
);
}
private function getAuthGroupsHeader() {
return header_to_php_name(
plugin_conf('groupsHeader', self::GRPS_HEADER)
);
}
}
function plugin_conf($name, $default="") {
global $conf;
if (!array_key_exists('plugin', $conf)) { return $default; }
if (!array_key_exists('forwardauth', $conf['plugin'])) { return $default; }
if (!array_key_exists($name, $conf['plugin']['forwardauth'])) { return $default; }
return $conf['plugin']['forwardauth'][$name];
}
function sane_lookup($header) {
// Simplify header lookup by making sure it is set.
return ($header === '') ? "" : $_SERVER[$header];
}
function header_to_php_name($header) {
// Headers are all uppercase, and all '-' must be underscores.
return ($header === '') ? "" : "HTTP_" . strtoupper(str_replace('-', '_', $header));
}
version: '3.7'
networks:
traefik:
external:
name: traefik
# Assumes authelia is already installed/running as a middleware in traefik
services:
dokuwiki:
image: 'docker.io/bitnami/dokuwiki:20200729-debian-10'
volumes: [ '/home/docker/data/dokuwiki:/bitnami/dokuwiki' ]
networks: [ traefik ]
restart: unless-stopped
environment:
DOKUWIKI_USERNAME: admin
DOKUWIKI_FULL_NAME: Administrator
DOKUWIKI_PASSWORD: admin
DOKUWIKI_EMAIL: admin@example.com
DOKUWIKI_WIKI_NAME: Example Wiki
labels:
traefik.enable: "true"
traefik.http.routers.dokuwiki.tls: "true"
traefik.http.routers.dokuwiki.middlewares: "authelia@docker"
traefik.http.routers.dokuwiki.rule: "Host(`doku.example.com`)"
traefik.http.services.dokuwiki.loadbalancer.server.port: 8080
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment