Nginx handler: API completed and config files examples added

environments/ppa-mbqj77/deployments/1
François-Xavier Deltombe 10 years ago
parent c768ed9d75
commit 9cfb59ad81
  1. 7
      Makefile
  2. 6
      _example/etc/handler-nginx.conf
  3. 25
      _example/etc/nginx-access-control
  4. 12
      _example/etc/nginx-fcgi-accounting
  5. 12
      _example/etc/nginx-http-accounting
  6. 70
      _example/etc/test-nginx.conf
  7. 12
      lemonldap-ng-handler/lib/Lemonldap/NG/Handler.pm
  8. 209
      lemonldap-ng-handler/lib/Lemonldap/NG/Handler/API/Nginx.pm
  9. 13
      lemonldap-ng-handler/lib/Lemonldap/NG/Handler/SharedConf.pm

@ -277,6 +277,11 @@ install_site: install_manager_site install_portal_site install_handler_site inst
cp -f _example/etc/handler-apache$(APACHEVERSION).conf $(RCONFDIR); \
cp -f _example/etc/manager-apache$(APACHEVERSION).conf $(RCONFDIR); \
cp -f _example/etc/test-apache$(APACHEVERSION).conf $(RCONFDIR); \
cp -f _example/etc/handler-nginx.conf $(RCONFDIR); \
cp -f _example/etc/test-nginx.conf $(RCONFDIR); \
cp -f _example/etc/nginx-access-control $(RCONFDIR); \
cp -f _example/etc/nginx-http-accounting $(RCONFDIR); \
cp -f _example/etc/nginx-fcgi-accounting $(RCONFDIR); \
cp -f _example/etc/for_etc_hosts $(RCONFDIR); \
fi
@$(PERL) -i -pe 's/__DNSDOMAIN__/$(DNSDOMAIN)/g; \
@ -287,7 +292,7 @@ install_site: install_manager_site install_portal_site install_handler_site inst
s#__TESTDIR__#$(TESTDIR)/#g; \
s#__VHOSTLISTEN__#$(VHOSTLISTEN)#g; \
s#__DEFDOCDIR__#$(DEFDOCDIR)/#g; \
s#__FRDOCDIR__#$(FRDOCDIR)/#g;' $(RCONFDIR)/*apache*.conf
s#__FRDOCDIR__#$(FRDOCDIR)/#g;' $(RCONFDIR)/*apache*.conf $(RCONFDIR)/*nginx*.conf
@$(PERL) -i -pe 's/__DNSDOMAIN__/$(DNSDOMAIN)/g' $(RCONFDIR)/for_etc_hosts
# Fix a lost of rights on the main directory
@chmod 755 $(RBINDIR) $(RDOCUMENTROOT) $(REXAMPLESDIR) $(RHANDLERDIR) $(RPORTALSKINSDIR) $(RMANAGERSITEDIR) $(RTOOLSDIR) $(RCONFDIR) $(RDATADIR)

@ -4,6 +4,12 @@
# Load LemonLDAP::NG Handler
perl_require Lemonldap/NG/Handler.pm;
perl_set $lmstatus Lemonldap::NG::Handler::handler;
# Log format similar to "combined", but with remote_user found by LL::NG
log_format lm_combined '$remote_addr - $lmremote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
# Common error page and security parameters
error_page 500 http://auth.__DNSDOMAIN__/?lmError=500;

@ -0,0 +1,25 @@
# Following directives must be included in server blocks
# or location blocks in order to run LL::NG access control
# Vars used by LL::NG handler
set $lmremote_user "";
set $lmlocation "";
# Apply status code computed by LL::NG handler
if ($lmstatus = 302) { return 302 $lmlocation; }
if ($lmstatus = 401) { return 401; }
if ($lmstatus = 403) { return 403; }
if ($lmstatus = 500) { return 500; }
if ($lmstatus = 503) { return 503; }
# Security: prevent clients from sending custom headers
# that could interfere with headers added by LL::NG handler
# For example, if LL::NG handler adds a request header "Auth-User",
# request header "Auth-User" sent by a malicious client would be
# overwritten by LL::NG handler, but "Auth_User" and "auth-user" would not,
# and Nginx does not permit case-sensitive header comparison.
# If $lmparanoid is set to 1, any suspicious request header would result in a 403 error;
# if set to 0, a warning will be sent in error logs;
# default value is 0.
#set $lmparanoid 1;

@ -0,0 +1,12 @@
# Directives to send user data to FastCGI protected apps
# and hide LL::NG cookie
set $cookie "";
fastcgi_param COOKIE $cookie;
# Add here HTTP headers defined in LL::NG manager
# For example, if you set an exported header 'Auth-User',
set $lm_auth_user "";
fastcgi_param AUTH_USER $lm_auth_user;
# That is: variable containing header value is obtained from lowercase
# header name, - replaced with _, and preceded from $lm_

@ -0,0 +1,12 @@
# Directives to send user data to HTTP protected apps
# and hide LL::NG cookie
set $cookie "";
proxy_set_header Cookie $cookie;
# Add here HTTP headers defined in LL::NG manager
# For example, if you set an exported header 'Auth-User',
set $lm_auth_user "";
proxy_set_header Auth-User $lm_auth_user;
# That is: variable containing header value is obtained from lowercase
# header name, - replaced with _, and preceded from $lm_

@ -8,43 +8,17 @@ server {
server_name test1.__DNSDOMAIN__;
location / {
# Trigger Lemonldap::NG access control
auth_request /auth;
# Since auth_request only understands 200 or 403 but not 302,
# redirect user to portal is done through 403
error_page 403 @maybe302;
# Prepare user data transmission to protected HTTP app
include lemonldap-ng/http-accounting;
# Hide cookie and send data about user to apps
set $lm_headers "";
proxy_set_header "Cookie" $lm_headers;
# Alternatively, you can set headers carrying user data
# one by one, by setting Nginx vars lm_* corresponding
# to exported headers as defined in Lemonldap::NG manager
# (in lower case, e.g. "Auth-User" => $lm_auth_user),
# plus var $lm_cookie to remove from request header
# Lemonldap::NG cookie but no other cookie
#set $lm_cookie "";
#set $lm_auth_user "";
#proxy_set_header "Cookie" $lm_cookie;
#proxy_set_header "Auth-User" $lm_auth_user;
# Trigger Lemonldap::NG access control
include lemonldap-ng/access-control;
# Transfer request to backend
proxy_pass http://target.__DNSDOMAIN__/;
}
# Redirect user to Lemonldap::NG portal if $portalURL is set
set $portalURL "";
location @maybe302 {
if ($portalURL) {
rewrite .* $portalURL redirect;
}
return 403;
}
# Subrequest to run Lemonldap::NG access control
location = /auth {
perl Lemonldap::NG::Handler::run;
}
access_log /var/log/nginx/access.log lm_combined;
}
# Sample FastCGI application
@ -53,23 +27,11 @@ server {
server_name test2.__DNSDOMAIN__;
location / {
# Trigger Lemonldap::NG access control
auth_request /auth;
# Since auth_request only understands 200 or 403 but not 302,
# redirect user to portal is done through 403
error_page 403 @maybe302;
# Prepare user data transmission to protected FastCGI app
include lemonldap-ng/fcgi-accounting;
# Hide cookie and send data about user to apps
# You have to set headers carrying user,
# by setting Nginx vars lm_* corresponding
# to exported headers as defined in Lemonldap::NG manager
# (in lower case, e.g. "Auth-User" => $lm_auth_user),
# plus var $lm_cookie to remove from request header
# Lemonldap::NG cookie but no other cookie
set $lm_cookie "";
set $lm_auth_user "";
fastcgi_param HTTP_COOKIE $lm_cookie;
fastcgi_param HTTP_AUTH_USER $lm_auth_user;
# Trigger Lemonldap::NG access control
include lemonldap-ng/access-control;
# Transfer request to backend - assume fcgiwrap is installed
root __TESTDIR__;
@ -78,17 +40,5 @@ server {
fastcgi_pass unix:/var/run/fcgiwrap.socket;
}
# Redirect user to Lemonldap::NG portal if $portalURL is set
set $portalURL "";
location @maybe302 {
if ($portalURL) {
rewrite .* $portalURL redirect;
}
return 403;
}
# Subrequest to run Lemonldap::NG access control
location = /auth {
perl Lemonldap::NG::Handler::run;
}
access_log /var/log/nginx/access.log lm_combined;
}

@ -10,6 +10,18 @@ our $VERSION = '2.0.0';
use Lemonldap::NG::Handler::SharedConf;
@ISA = qw(Lemonldap::NG::Handler::SharedConf);
sub handler {
my ( $class, $request ) = ( __PACKAGE__, shift );
Lemonldap::NG::Handler::API->newRequest($request);
$class->run();
}
sub logout {
my ( $class, $request ) = ( __PACKAGE__, shift );
Lemonldap::NG::Handler::API->newRequest($request);
$class->unlog();
}
__PACKAGE__->init();
1;

@ -1,52 +1,215 @@
package Lemonldap::NG::Handler::API::Nginx;
our $VERSION = '1.4.0';
our $VERSION = '2.0.0';
use constant FORBIDDEN => 403;
use constant REDIRECT => 302;
use constant OK => 0;
use constant DECLINED => -1;
use constant DONE => -2;
use constant SERVER_ERROR => 500;
use constant AUTH_REQUIRED => 401;
use constant MAINTENANCE => 503;
my $request; # Nginx object for current request
## @method void thread_share(string $variable)
# share or not the variable (if authorized by specific module)
# @param $variable the name of the variable to share
# not applicable with Nginx
sub thread_share {
my ( $class, $variable ) = @_;
}
# nothing to do in Nginx
## @method void setServerSignature(string sign)
# modifies web server signature
# @param $sign String to add to server signature
sub setServerSignature {
my ( $class, $sign ) = @_;
# TODO
}
sub set_user {
my ( $class, $r, $user ) = @_;
sub newRequest {
my ( $class, $r ) = @_;
$request = $r;
$Lemonldap::NG::API::mode = 'Nginx';
}
# Nginx perl API does not (yet ?) allow to set $remote_user var
# So one tries to set the variable $user instead
$r->variable( "user", $user )
if ( defined $r->variable("user") );
## @method void lmLog(string $msg, string $level)
# logs message $msg to Apache logs with loglevel $level
# @param $msg string message to log
# @param $level string loglevel
sub lmLog {
my ( $class, $msg, $level ) = @_;
# TODO
}
## @method void set_user(string user)
# sets remote_user
# @param user string username
sub set_user {
my ( $class, $user ) = @_;
$request->variable('lmremote_user', $user);
}
## @method string header_in(string header)
# returns request header value
# @param header string request header
# @return request header value
sub header_in {
my ( $class, $r, $header ) = @_;
return $r->header_in($header);
my ( $class, $header ) = @_;
$header ||= $class; # to use header_in as a method or as a function
return $request->header_in($header);
}
## @method void set_header_in(hash headers)
# sets or modifies request headers
# @param headers hash containing header names => header value
sub set_header_in {
my ( $class, $r, %headers ) = @_;
my ( $class, %headers ) = @_;
while ( my ( $h, $v ) = each %headers ) {
# TODO
if ( $h =~ /cookie/i ) {
# TODO: check that variable $lmcookie is defined,
# else warn that LL::NG cookie will not be removed
$request->variable( 'lmcookie', $v );
} else {
# TODO: check that header is not yet set, else throw warning
# or reject request if mode paranoid is set
# TODO: check that variable nginxName($h) is defined,
# else warn that header will not be sent
$request->variable( nginxName($h), $v );
}
}
}
## @method void unset_header_in(array headers)
# removes request headers
# @param headers array with header names to remove
sub unset_header_in {
my ( $class, $r, @headers ) = @_;
foreach my $h (@headers) {
# TODO
my ( $class, @headers ) = @_;
foreach my $h1 (@headers) {
# TODO: check that header is not yet set, else throw warning
$request->variable( nginxName($h), '' );
}
}
## @method void set_header_out(hash headers)
# sets response headers
# @param headers hash containing header names => header value
sub set_header_out {
my ( $class, $r, %headers ) = @_;
my ( $class, %headers ) = @_;
while ( my ( $h, $v ) = each %headers ) {
# TODO
if ( $h =~ /location/i ) {
$request->variable( 'lmlocation', $v );
} else {
$request->header_out( $h, $v );
}
}
}
## @method string hostname()
# returns host, as set by full URI or Host header
# @return host string Host value
sub hostname {
my $class = shift;
return $request->variable('host');
}
## @method string remote_ip
# returns client IP address
# @return IP_Addr string client IP
sub remote_ip {
my $class = shift;
return $request->variable('remote_addr');
}
## @method boolean is_initial_req
# returns true unless the current request is a subrequest
# @return is_initial_req boolean
sub is_initial_req {
my $class = shift;
return 1;
}
## @method string args(string args)
# gets the query string
# @return args string Query string
sub args {
my $class = shift;
return $request->args();
}
## @method string uri
# returns the path portion of the URI, normalized, i.e. :
# * URL decoded (characters encoded as %XX are decoded,
# except ? in order not to merge path and query string)
# * references to relative path components "." and ".." are resolved
# * two or more adjacent slashes are merged into a single slash
# @return path portion of the URI, normalized
sub uri {
my $class = shift;
return $request->uri();
}
## @method string uri_with_args
# returns the URI, with arguments and with path portion normalized
# @return URI with normalized path portion
sub uri_with_args {
my $class = shift;
return uri() . ( $request->args ? "?" . $request->args : "" );
}
## @method string unparsed_uri
# returns the full original request URI, with arguments
# @return full original request URI, with arguments
sub unparsed_uri {
my $class = shift;
return $request->variable('request_uri');
}
## @method string get_server_port
# returns the port the server is receiving the current request on
# @return port string server port
sub get_server_port {
my $class = shift;
return $request->variable('server_port');
}
## @method string method
# returns the method the request is sent with
# @return port string server port
sub method {
my $class = shift;
return $request->request_method;
}
## @method void print(string data)
# write data in HTTP response body
# @param data Text to add in response body
sub print {
my ( $class, $data ) = @_;
$request->print($data);
}
## @method void addToHtmlHead(string data)
# add data at end of html head: not feasible with Nginx
# @param data Text to add in html head
sub addToHtmlHead {
my ( $class, $data ) = @_;
# TODO: throw error log
}
## @method void setPostParams(hashref $params)
# add or modify parameters in POST request body: not feasible with Nginx
# @param $params hashref containing name => value
sub setPostParams {
my ( $class, $params ) = @_;
# TODO: throw error log
}
sub nginxName {
my $h = lc(shift);
$h =~ s/-/_/g;
return "lm_$h";
}
1;

@ -126,19 +126,6 @@ sub statusInit {
# MAIN
# NB: function attribute ': method' requested to get $class
sub handler : method {
my ( $class, $request ) = @_;
Lemonldap::NG::Handler::API->newRequest($request);
$class->run();
}
sub logout : method {
my ( $class, $request ) = @_;
Lemonldap::NG::Handler::API->newRequest($request);
$class->unlog();
}
## @rmethod int run
# Check configuration and launch Lemonldap::NG::Handler::Main::run().
# Each $checkTime, the Apache child verify if its configuration is the same

Loading…
Cancel
Save