From 38b9a1d2c484036f819694913ffef4553f11d0e6 Mon Sep 17 00:00:00 2001 From: Daniel Berteaud Date: Sun, 6 Nov 2022 16:42:46 +0100 Subject: [PATCH] Support Traefik forwardAuth --- .../lib/Lemonldap/NG/Handler/Main/Run.pm | 32 ++++++++++++++----- .../lib/Lemonldap/NG/Handler/Server/Nginx.pm | 29 ++++++++++------- 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Run.pm b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Run.pm index 0a521e958..58ee91feb 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Run.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Main/Run.pm @@ -12,6 +12,22 @@ use MIME::Base64; use URI::Escape; use Lemonldap::NG::Common::Session; +$ENV{LLNG_HANDLER_ENGINE} ||= 'nginx'; + +our $reqVar = { + + # Depending on the web engine using the handler, variables where the original + # host and requested URI can vary. This hashref set the variables for the supported web engine. + nginx => { + Host => 'HTTP_HOST', + Uri => 'REQUEST_URI' + }, + traefik => { + Host => 'HTTP_X_FORWARDED_HOST', + Uri => 'HTTP_X_FORWARDED_URI' + } +}; + # Methods that must be overloaded sub handler { @@ -123,7 +139,7 @@ sub run { } # Authentication process - my $uri = $req->{env}->{REQUEST_URI}; + my $uri = $req->{env}->{ $ENV{LLNG_HANDLER_ENGINE} }->{Uri} }; my ($cond); ( $cond, $protection ) = $class->conditionSub($rule) if ($rule); @@ -246,7 +262,7 @@ sub run { # if the cookie was fetched, a log is sent by retrieveSession() $class->updateStatus( $req, $id ? 'EXPIRED' : 'REDIRECT' ); - return $class->goToPortal( $req, $req->{env}->{REQUEST_URI} ); + return $class->goToPortal( $req, $req->{env}->{ $ENV{LLNG_HANDLER_ENGINE} }->{Uri} } ); } } @@ -271,10 +287,10 @@ sub updateStatus { my ( $class, $req, $action, $user, $url ) = @_; my $statusPipe = $class->tsv->{statusPipe} or return; $user ||= $req->{env}->{REMOTE_ADDR}; - $url ||= $req->{env}->{REQUEST_URI}; + $url ||= $req->{env}->{ $reqVar->{ $ENV{LLNG_HANDLER_ENGINE} }->{Uri} }; eval { $statusPipe->print( - "$user => " . $req->{env}->{HTTP_HOST} . "$url $action\n" ); + "$user => " . $req->{env}->{ $reqVar->{ $ENV{LLNG_HANDLER_ENGINE} }->{Host} } . "$url $action\n" ); }; } @@ -396,7 +412,7 @@ sub grant { # @return Constant $class->FORBIDDEN sub forbidden { my ( $class, $req, $session, $vhost ) = @_; - my $uri = $req->{env}->{REQUEST_URI}; + my $uri = $req->{env}->{ $ENV{LLNG_HANDLER_ENGINE} }->{Uri} }; my $portal = $class->tsv->{portal}->(); $portal = ( $portal =~ m#^https?://([^/]*).*# )[0]; $portal =~ s/:\d+$//; @@ -679,7 +695,7 @@ sub _isHttps { # @return URL sub _buildUrl { my ( $class, $req, $s ) = @_; - my $realvhost = $req->{env}->{HTTP_HOST}; + my $realvhost = $req->{env}->{ $reqVar->{ $ENV{LLNG_HANDLER_ENGINE} }->{Host} }; my $vhost = $class->resolveAlias($req); my $_https = $class->_isHttps( $req, $vhost ); my $portString = $class->_getPort( $req, $vhost ); @@ -772,7 +788,7 @@ sub cleanHeaders { # returns vhost whose current hostname is an alias sub resolveAlias { my ( $class, $req ) = @_; - my $vhost = ref $req ? $req->{env}->{HTTP_HOST} : $req; + my $vhost = ref $req ? $req->{env}->{ $reqVar->{ $ENV{LLNG_HANDLER_ENGINE} }->{Uri} } : $req; $vhost =~ s/:\d+//; return $class->tsv->{vhostAlias}->{$vhost} @@ -796,7 +812,7 @@ sub abort { # If abort is called without a valid request, fall to die eval { - my $uri = $req->{env}->{REQUEST_URI}; + my $uri = $req->{env}->{ $ENV{LLNG_HANDLER_ENGINE} }->{Uri} }; $class->logger->error($msg); diff --git a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Server/Nginx.pm b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Server/Nginx.pm index 6d5fa6872..dd27cf559 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Server/Nginx.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Server/Nginx.pm @@ -1,5 +1,5 @@ -# PSGI authentication package written for Nginx. It replace -# Lemonldap::NG::Handler::Server to manage Nginx behaviour +# PSGI authentication package written for Nginx or similar servers. +# It replace Lemonldap::NG::Handler::Server to manage Nginx behaviour package Lemonldap::NG::Handler::Server::Nginx; use strict; @@ -10,6 +10,8 @@ our $VERSION = '2.0.6'; extends 'Lemonldap::NG::Handler::PSGI'; +$ENV{LLNG_HANDLER_ENGINE} ||= 'nginx'; + sub init { my $self = shift; $self->api('Lemonldap::NG::Handler::Server::Main'); @@ -34,8 +36,9 @@ sub _run { my $res = $self->_logAuthTrace( Lemonldap::NG::Common::PSGI::Request->new($req) ); - # Transform 302 responses in 401 since Nginx refuse it - if ( $res->[0] == 302 or $res->[0] == 303 ) { + # Transform 302 responses in 401 since Nginx refuse it. Other engines (like Traefik) + # must receive the original code + if ( ($res->[0] == 302 or $res->[0] == 303) and $ENV{LLNG_HANDLER_ENGINE} eq 'nginx') { $res->[0] = 401; } return $res; @@ -69,14 +72,16 @@ sub handler { my @convertedHdrs = ( 'Content-Length' => 0, Cookie => ( $req->env->{HTTP_COOKIE} // '' ) ); my $i = 0; - while ( my ( $k, $v ) = splice( @{ $req->{respHeaders} }, 0, 2 ) ) { - if ( $k =~ /^(?:Deleteheader\d+|Lm-Remote-(?:User|Custom)|Cookie)$/ ) { - push @convertedHdrs, $k, $v; - } - else { - $i++; - push @convertedHdrs, "Headername$i", $k, "Headervalue$i", $v, $k, - $v; + if ( $ENV{LLNG_HANDLER_ENGINE} eq 'nginx') { + while ( my ( $k, $v ) = splice( @{ $req->{respHeaders} }, 0, 2 ) ) { + if ( $k =~ /^(?:Deleteheader\d+|Lm-Remote-(?:User|Custom)|Cookie)$/ ) { + push @convertedHdrs, $k, $v; + } + else { + $i++; + push @convertedHdrs, "Headername$i", $k, "Headervalue$i", $v, $k, + $v; + } } } return [ 200, \@convertedHdrs, [] ];