Configuring multiple SAML apps with mod_auth_mellon in Apache? - apache

We have a requirement to support SAML for SSO (Okta and Google). I have been able to set up my own custom SAML application in Google and configure mellon in apache. However, we have a requirement to configure SAML in Okta for customers and SAML in Google for our internal users.
#################################################################################
# Global configuration for mod_auth_mellon.
# This configuration is shared by every virtual server and location in this instance of apache.
#################################################################################
# MellonCacheSize sets the maximum number of sessions which can be active at once. When mod_auth_mellon reaches this limit, it will begin removing # the least recently used sessions. The server must be restarted before any changes to this option takes effect.
# Default: MellonCacheSize 100
MellonCacheSize 100
# MellonLockFile is the full path to a file used for synchronizing access to the session data. The path should only be used by one instance of apache at a time.The server must be restarted before any changes to this option takes effect.
# Default: MellonLockFile "/var/run/mod_auth_mellon.lock"
MellonLockFile "/var/run/mod_auth_mellon.lock"
# MellonPostCount is the maximum amount of saved POST requests
# Default: MellonPostCount 100
MellonPostCount 100
###########################################################################
# End of global configuration for mod_auth_mellon.
###########################################################################
<Location />
MellonEnable "info"
Require valid-user
AuthType "Mellon"
MellonVariable "cookie"
MellonSamlResponseDump On
MellonSPPrivateKeyFile /etc/apache2/googlesaml/mellon.key
MellonSPCertFile /etc/apache2/googlesaml/mellon.crt
MellonSPMetadataFile /etc/apache2/googlesaml/mellon_metadata.xml
MellonIdPMetadataFile /etc/apache2/googlesaml/GoogleIDPMetadata.xml
MellonEndpointPath /mellon
MellonDefaultLoginPath /
RequestHeader set MELLON_NAME_ID %{MELLON_NAME_ID}e
</Location>
<VirtualHost *:443>
ServerName host_name
DocumentRoot /var/www/html
SSLEngine on
SSLCertificateFile /etc/ssl/certs/server.pem
SSLCertificateKeyFile /etc/ssl/private/private.key
<Location />
AuthType Mellon
MellonEnable auth
Require valid-user
</Location>
<Location /protected>
AuthType Mellon
MellonEnable auth
Require valid-user
</Location>
</VirtualHost>
How can we differentiate incoming request between Okta and Google (SAML) as Location /> directive can be configured by only either one of SAML provider.

The mod_auth_mellon module only applies SAML to a specific <Location />...</Location>, so you would have to configure a location for each idP provider.
<VirtualHost *:443>
ServerName host_name
DocumentRoot /var/www/html
SSLEngine on
SSLCertificateFile /etc/ssl/certs/server.pem
SSLCertificateKeyFile /etc/ssl/private/private.key
# GoogleSaml
<Location />
MellonEnable "info"
Require valid-user
AuthType "GoogleSaml"
MellonVariable "cookie"
MellonSamlResponseDump On
MellonSPPrivateKeyFile /etc/apache2/googlesaml/mellon.key
MellonSPCertFile /etc/apache2/googlesaml/mellon.crt
MellonSPMetadataFile /etc/apache2/googlesaml/mellon_metadata.xml
MellonIdPMetadataFile /etc/apache2/googlesaml/GoogleIDPMetadata.xml
MellonEndpointPath /mellon
MellonDefaultLoginPath /
RequestHeader set MELLON_NAME_ID %{MELLON_NAME_ID}e
</Location>
# Okta
<Location /protected>
Require valid-user
AuthType "OktaSaml"
MellonEnable "auth"
MellonDecoder "none"
MellonVariable "cookie"
MellonSecureCookie On
MellonUser "NAME_ID"
MellonSetEnv "e-mail" "mail"
MellonEndpointPath "/endpoint"
MellonDefaultLoginPath "/"
MellonSessionLength 300
MellonSPPrivateKeyFile /etc/apache2/mellon/http_192.168.14.130_okta.key
MellonSPCertFile /etc/apache2/mellon/http_192.168.14.130_okta.cert
MellonIdPMetadataFile /etc/apache2/mellon/metadata
MellonSamlResponseDump On
MellonSessionDump On
</Location>
</VirtualHost>
If you want to do this dynamically based on the user's headers, I wouldn't recommend mod_auth_mellon, having your application serve up the authentication would make more sense.
Hope this helps.

I have tried below config and it works for openidc and mellon both. Apparently, this scenario would be helpful for those willing to configure Okta (mellon) and google sso for internal IDP.
<Location />
MellonEndpointPath /mellon/
MellonSPMetadataFile /etc/apache2/saml/mellon_metadata.xml
MellonSPPrivateKeyFile /etc/apache2/saml/mellon.key
MellonSPCertFile /etc/apache2/saml/mellon.crt
MellonIdPMetadataFile /etc/apache2/saml/idp_metadata.xml
MellonVariable "mellon_cookie"
MellonDefaultLoginPath /
MellonSecureCookie on
</Location>
<VirtualHost *:443>
ServerName zzz.xxxx.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/xxxxx_prod.pem
SSLCertificateKeyFile /etc/ssl/private/xxxxx.com.key
OIDCResponseType "id_token"
OIDCScope "openid email profile"
OIDCProviderMetadataURL https://accounts.google.com/.well-known/openid-configuration
OIDCRedirectURI "https://zzz.xxxx..com/openidc_callback"
OIDCDiscoverURL https://zzz.xxxx.com/idp-discovery.html
<Location /uliya>
AuthType "mellon"
Require valid-user
MellonEnable "auth"
</Location>
<Location /transport>
AuthType openid-connect
Require valid-user
OIDCUnAuthAction auth
</Location>
<Location "/idp-page.html">
Require all granted
</Location>
</VirtualHost>

Related

Example to support both SAML and OpenIDC

I have a requirement to support both OIDC(openidc) and Mellon(Saml) in our application.We have created two apps in Okta for testing the flow.
OIDC App
SAML App
httpd.conf looks something like below :
<IfModule mod_ssl.c>
<Location />
MellonVariable "cookie"
MellonEnable "auth"
MellonEndpointPath /mellon/
MellonSPMetadataFile /etc/apache2/saml/mellon_metadata.xml
MellonSPPrivateKeyFile /etc/apache2/saml/mellon.key
MellonSPCertFile /etc/apache2/saml/mellon.crt
MellonIdPMetadataFile /etc/apache2/saml/idp_metadata.xml
</Location>
<VirtualHost _default_:443>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
#Enable/Disable SSL for this virtual host.
SSLEngine on
SSLCertificateFile /etc/ssl/certs/server.pem
SSLCertificateKeyFile /etc/ssl/private/private.key
OIDCScope "openid email profile"
OIDCClientID "xxxx"
OIDCClientSecret "xxxxx"
OIDCCryptoPassphrase "xxxx"
OIDCMetadataDir "/var/cache/apache2/mod_auth_openidc/metadata"
OIDCRedirectURI "https://apachesso.example.com/callback"
OIDCResponseType "code"
<Location /uliya>
<If "%{REQUEST_URI} =~ /callback=/">
AuthType openid-connect
Require valid-user
</If>
<Else>
AuthType "Mellon"
Require valid-user
MellonEnable "auth"
</Else>
</Location>
</VirtualHost>
<VirtualHost *:443>
<Location /uliya>
AuthType openid-connect
require valid-user
</Location>
</VirtualHost>
<VirtualHost *:443>
<Location /transport>
AuthType "Mellon"
MellonEnable auth
Require valid-user
</Location>
</VirtualHost>
</IfModule>
The goal is that, the request to https://apachesso.example.com/uliya should go through openid-connect Auth Flow and request to https://apachesso.example.com/transport should go through mellon flow.
However, with above configuration all the request authentication goes to Mellon Plugin by default and below config doesnt take effect.
<Location /uliya>
AuthType openid-connect
Require valid-user
</Location>
Is it possible to get both these plugins to work together?
Just don't use any authentication directives on "/", but use mod_auth_openidc directives on "/uliya" (including setting OIDCRedirectURI to /uliya/redirect_uri" and use mod_mellon directives only on "/transport".

Apache 2.4 reverse proxy setup cannot impose basic authentication

I have apache2.4 set up and when visiting any apache served web sites basic authentication works great.
Now I have one more webserver running from an other service at port 8000 and I wanted to setup apache as a reverse proxy hoping that it can also impose and handle basic authentication there as well...but instead for asking for user and password it just serves the website unprotected.
my setup is:
<VirtualHost *:8000>
ProxyPreserveHost On
ProxyPass / http://192.168.0.101:8000/
ProxyPassReverse / http://192.168.0.101:8000/
<Location />
AuthType Basic
AuthName "Authorization"
AuthUserFile /etc/htpasswd/.htpasswd
require valid-user
</Location>
</VirtualHost>
what am i doing wrong?
Update:
solution found by marked answer:
<VirtualHost *:8000>
ProxyPreserveHost On
<Location />
ProxyPass http://192.168.0.101:8000/
ProxyPassReverse http://192.168.0.101:8000/
AuthType Basic
AuthName "Authorization"
AuthUserFile /etc/htpasswd/.htpasswd
require valid-user
</Location>
</VirtualHost>
Also make sure that apache is configured to listen to that port and also if the proxied server is local it is not running at the same port as listened one
The problem is that Apache doesn't 'link' Proxypass / http://example.com and <Location /> - even though they both try to work with /. This means that Proxypass is handling requests for '/' first, and the Location section is never being used.
You need to move the Proxy config inside the Location, dropping the path, e.g.:
<VirtualHost *:8000>
ProxyPreserveHost On
<Location />
ProxyPass http://192.168.0.101:8000/
ProxyPassReverse http://192.168.0.101:8000/
AuthType Basic
AuthName "Authorization"
AuthUserFile /etc/htpasswd/.htpasswd
require valid-user
</Location>
</VirtualHost>

Nexus 3.3 ignores base URL settings

Trying to set up Nexus 3.3.2-02 and Jetty appears to ignore the HTTPS in the base URL config. Nexus hits the landing page but hangs at "Initializing" and fails to load static content.
I have added the base path capability to Nexus and have triple checked that it is using the correct URL. However if I load up the file static/rapture/bootstrap.js it is replacing HTTPS in the base URL with HTTP.
This is where I can see the switch occurring if I load the boostrap.js directly ...
https://[removed]/nexus3/static/rapture/bootstrap.js
Ext.Loader.setConfig({
enabled: false
});
Ext.app.addNamespaces('NX.coreui');
Ext.app.addNamespaces('NX.proui');
Ext.ns('NX');
NX.global = (function() {
if (window !== undefined) {
return window;
}
if (global !== undefined) {
return global;
}
Ext.Error.raise('Unable to determine global object');
}());
Ext.ns('NX.app');
NX.app.baseUrl = 'http://[removed]/nexus3';
NX.app.urlSuffix = '_v=3.3.2-02';
etc/nexus-default.properties:
# Jetty section
application-port=8091
application-host=0.0.0.0
nexus-args=${jetty.etc}/jetty.xml,${jetty.etc}/jetty-
http.xml,${jetty.etc}/jetty-requestlog.xml
nexus-context-path=/nexus3
# Nexus section
nexus-edition=nexus-oss-edition
nexus-features=\
nexus-oss-feature
The proxying here works for existing Nexus v2 and seems to be working for Nexus v3 ...
apache2.conf
<VirtualHost *:443>
########################
# SSL config
########################
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/[removed]/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/[removed]/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/[removed]/chain.pem
ServerName [removed]
########################
# Proxy config
########################
ProxyRequests Off
ProxyVia Off
ProxyPreserveHost On
AllowEncodedSlashes On
<Proxy *>
Order deny,allow
Allow from all
# Use following line instead of the previous two on Apache >= 2.4
#Require all granted
</Proxy>
########################
# Nexus config
########################
<Location /nexus>
ProxyPass http://localhost:8090/nexus nocanon
ProxyPassReverse /nexus
</Location>
<Location /nexus/>
ProxyPass http://localhost:8090/nexus/ nocanon
ProxyPassReverse /nexus/
</Location>
<Location /nexus/*>
AuthType Basic
AuthName "Nexus"
Require valid-user
AuthBasicProvider file
AuthUserFile "/etc/apache2/gerrit-users"
Require valid-user
</Location>
########################
# Nexus3 config
########################
<Location /nexus3>
ProxyPass http://localhost:8091/nexus3 nocanon
ProxyPassReverse /nexus3
</Location>
<Location /nexus3/>
ProxyPass http://localhost:8091/nexus3/ nocanon
ProxyPassReverse /nexus3/
</Location>
<Location /nexus3/*>
AuthType Basic
AuthName "Nexus"
Require valid-user
AuthBasicProvider file
AuthUserFile "/etc/apache2/gerrit-users"
Require valid-user
</Location>
</VirtualHost>
You need to set the "X-Forwarded-Proto" header in Apache as described here:
http://books.sonatype.com/nexus-book/reference3/install.html#_example_reverse_proxy_ssl_termination_at_base_path

How to combine proxy and basic auth in Apache

How do you combine basic auth with a reverse proxy in Apache?
I have an Apache site currently configured to use basic auth with an htpasswd file using this config:
<VirtualHost *:80>
# Requires: a2enmod proxy_http
ProxyPass / http://127.0.0.1:8010/
<Location />
AuthType Basic
AuthName "Sensitive"
AuthUserFile /usr/local/myproject/htpasswd
Require valid-user
</Location>
</VirtualHost>
Apache is acting as a wrapper around a Buildbot server being served on port :8010. However, this app has been upgraded so it now requires the use of websockets. The suggested Apache configuration is:
<VirtualHost *:80>
<Location /ws>
ProxyPass ws://127.0.0.1:8010/ws
ProxyPassReverse ws://127.0.0.1:8010/ws
</Location>
ProxyPass /ws !
ProxyPass / http://127.0.0.1:8010/
ProxyPassReverse / http://127.0.0.1:8010/
</VirtualHost>
However, this doesn't use any authentication. I tried re-adding my <Location /> section from the previous config, so I now have:
<VirtualHost *:80>
<Location />
AuthType Basic
AuthName "Sensitive"
AuthUserFile /usr/local/myproject/htpasswd
Require valid-user
</Location>
<Location /ws>
ProxyPass ws://127.0.0.1:8010/ws
ProxyPassReverse ws://127.0.0.1:8010/ws
</Location>
ProxyPass /ws !
ProxyPass / http://127.0.0.1:8010/
ProxyPassReverse / http://127.0.0.1:8010/
</VirtualHost>
and although Apache now correctly prompts for my username+password, the Buildbot still isn't given authenticated username and still renders for an anonymous user.
How do I fix this config to pass the username (I believe the REMOTE_USER header) through to the web app behind the reverse proxy?

set up gerrit with http authentication

I am trying to configure gerrit with http baisc authentication , my httpd config is
<VirtualHost *:8081>
ServerName localhost
ProxyRequests Off
ProxyVia Off
ProxyPreserveHost On
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
<Location "/login/">
AuthType Basic
AuthName "Gerrit Code Review"
AuthBasicProvider file
AuthUserFile /usr/local/apache/passwd/passwords
Require valid-user
</Location>
ProxyPass / http://localhost:8081/
</VirtualHost>
and my gerrit.config is
[gerrit]
basePath = git
canonicalWebUrl = http://localhost:8081/
[database]
type = mysql
hostname = localhost
database = reviewdb
username = gerrit
[auth]
type = HTTP
[sendemail]
smtpServer = localhost
smtpUser = gerrit
[container]
user = gerrit
javaHome = /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre
[sshd]
listenAddress = *:29418
[httpd]
listenUrl = proxy-http://*:8081/
[cache]
directory = cache
i am not sure where am i going wrong but the accessing http://x.x.x.x:8081 says
The HTTP server did not provide the username in the Authorization header when it forwarded the request to Gerrit Code Review.
If the HTTP server is Apache HTTPd, check the proxy configuration includes an authorization directive with the proper location, ensuring it ends with '/':
my gerrit runs on the inbuild jetty countainer and my OS is centos 6.4
where am i going wrong.?
Okay. Actually I was creating a virtual host on port 8081 and my Jetty (that comes along with gerrit) was also listening to the same port,my configuration remained almost the same but these are the additional steps :-
Add a new port to your selinux (which has some basic ports defined initially) or you can disable it if security is not an issue.
tell httpd to listen to this port(in my case i added 8082) ,so add the line listen <port-no> in your http conf file
Change the virtual host to your port number
now your virtualhost is set on port 8082
<VirtualHost *:8082>
ServerName localhost
ProxyRequests Off
ProxyVia Off
ProxyPreserveHost On
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
<Location "/login/">
AuthType Basic
AuthName "Gerrit Code Review"
AuthBasicProvider file
AuthUserFile /usr/local/apache/passwd/passwords
Require valid-user
</Location>
ProxyPass / http://localhost:8081/
</VirtualHost>
change the canonical url to port 8082 (so that it redirects it to same port)
finally restart the apache and Gerrit (access your-host:8082).
Gerrit it expecting the authentication to be provided. It does not allow anonymous access when you use HTTP authentication.
For this to work you need to authenticate at the root and your Location block should look like this:
<Location "/">
AuthType Basic
AuthName "Gerrit Code Review"
AuthBasicProvider file
AuthUserFile /usr/local/apache/passwd/passwords
Require valid-user
</Location>
There are a few issues with your configuration:
Apache and try to listen on the same port 8081, this is not possible
You ProxyPass is not the best, it will create some small issues. These issues are:
Unable to to create projects names with a slash in it like: main/sub
When reviewing files the check mark will not appear next to the file to show it as reviewed, again this is related to the forward slash not being properly processed
It is most common to use a subfolder and not the root, I guess that works better with the reverse proxy
This is my recommended configuration for you:
<VirtualHost *:80>
ServerName localhost
ProxyRequests Off
ProxyVia Off
ProxyPreserveHost On
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
<Location "/">
AuthType Basic
AuthName "Gerrit Code Review"
AuthBasicProvider file
AuthUserFile /usr/local/apache/passwd/passwords
Require valid-user
</Location>
AllowEncodedSlashes On
ProxyPass /r http://localhost:8081/r nocanon
</VirtualHost>
Ofcourse don't forget to amend the gerrit.config, the canonicalWebUrl is what you type in the address bar, not what apache uses to find gerrit.
[gerrit]
basePath = git
canonicalWebUrl = http://localhost:8082/r
To prevent the apache default page from showing add a index.php in your root folder that will redirect your browser to the sub path:
<?php
header('Location: http://localhost:8082/r/');
?>