SSO with Nginx and Vouch Proxy
Vouch Proxy works as an authentication gateway for Nginx.
This provides an easy and reliable way to add the authentication mechanism to any web application.
System requirement
- Nginx with “auth_request” module
Any Nginx flavor of Debian package should have this module. - Quay.io
Vouch Proxy is unavailable as a Debian package, but it provides the docker image through Quay.io.
The case
- The application is running on
app.example.com
- Vouch Proxy server is on
vouch.example.com
- Vouch Proxy runs as a docker image listening port 9090
- The application accepts HTTPS
- Use Google Account as the IdP
Configuration
Nginx as a reverse proxy for Vouch Proxy
Make a site configuration in /etc/nginx/sites-available/
and enable it. Nginx works as a reverse proxy to pass the connection to Vouch Proxy.
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name vouch.example.com;
# Enable SSL
include snippets/ssl_example.com.conf;
# Proxy to your Vouch instance
location / {
proxy_set_header Host vouch.example.com;
proxy_set_header X-Forwarded-Proto https;
proxy_pass http://localhost:9090;
}
}
Vouch Proxy configuration file
Make /etc/vouch/config/config.yml
. This is based on the config examples.
# Vouch Proxy configuration
# bare minimum to get Vouch Proxy running with google
vouch:
domains:
- example.com
# set allowAllUsers: true to use Vouch Proxy to just accept anyone who can authenticate with Google
allowAllUsers: true
cookie:
# allow the jwt/cookie to be set into http://yourdomain.com (defaults to true, requiring https://yourdomain.com)
secure: false
# vouch.cookie.domain must be set when enabling allowAllUsers
domain: example.com
oauth:
provider: google
# get credentials from...
# https://console.developers.google.com/apis/credentials
client_id: xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com
client_secret: xxxxxxxxxxxxxxxxxxxxxxxx
# Google may require callback_urls (redirect URIs) to be 'https'
callback_urls:
- https://vouch.example.com/auth
preferredDomain: example.com # be careful with this option, it may conflict with chrome on Android
# endpoints are set from https://godoc.org/golang.org/x/oauth2/google
Download latest Vouch Proxy image
Pull the docker image from Quay.io and run it with the config file.
docker run -d \
-p 9090:9090 \
--name vouch-proxy \
-v /etc/vouch/config:/config \
quay.io/vouch/vouch-proxy
-d
to use detached mode (run in the background)-p 9090:9090
to connect port 9090 on the server to the docker image port 9090. If you want to use port 1090 on the server, the option will be-p 1090:9090
--name vouch-proxy
to use a short name for this image-v /etc/vouch/config:/config
to let the image use/etc/vouch/config
host directory as/config
directory
Stop and Start the image
The short name vouch-proxy
should work now.
docker stop vouch-proxy
docker start vouch-proxy
You need to restart the image to reload config.yml file.
Remove image
When upgrading, remove the image to get the latest one.
docker rm vouch-proxy
docker rmi quay.io/vouch/vouch-proxy
Configure the web application
Let Nginx use auth_request to redirect the connection to Vouch Proxy.
Prepare the snippe for authentication
Make a snippet /etc/nginx/snippets/vouch.conf
# Any request to this server will first be sent to this URL
auth_request /vouch-validate;
# Get the authorized user name (email address)
auth_request_set $auth_user $upstream_http_x_vouch_user;
location = /vouch-validate {
internal;
# This address is where Vouch will be listening on
proxy_pass http://127.0.0.1:9090/validate;
proxy_pass_request_body off; # no need to send the POST body
proxy_set_header Content-Length "";
proxy_set_header Host $http_host; # This is required according to the Vouch-Proxy official example
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# these return values are passed to the @error401 call
auth_request_set $auth_resp_jwt $upstream_http_x_vouch_jwt;
auth_request_set $auth_resp_err $upstream_http_x_vouch_err;
auth_request_set $auth_resp_failcount $upstream_http_x_vouch_failcount;
}
error_page 401 = @error401;
# If the user is not logged in, redirect them to Vouch's login URL
location @error401 {
return 302 https://vouch.example.com/login?url=$scheme://$http_host$request_uri&vouch-failcount=$auth_resp_failcount&X-Vouch-Token=$auth_resp_jwt&error=$auth_resp_err;
}
Configure the application site
Set up a site for app.example.com
in /etc/nginx/sites-available
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name app.example.com;
# Enable SSL
include snippets/ssl_example.com.conf;
# The location and index files depend on your server policy
root /var/www/app.example.com;
index index.php;
# Enable authentication via Vouch-Proxy
include snippets/vouch.conf;
# In this case, assume the app is based on php.
location ~ \.php($|/) {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php-fpm.sock;
# This line is required to get the user name in PHP
fastcgi_param REMOTE_USER $auth_user;
}
# For the TCP proxy case (with user name: $auth_user)
#location / {
# proxy_set_header Remote-User $auth_user;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_set_header X-Forwarded-Ssl on;
# proxy_set_header X-Forwarded-Port $server_port;
# proxy_set_header Host $http_host;
# proxy_pass http://127.0.0.1:8080;
#}
}
- User information is available as an HTTP header. Use the following codes to get them from the web app.
- PHP:
$_SERVER['REMOTE_USER']
- Ruby on Rails:
request.env["HTTP_REMOTE_USER"]
- PHP:
Enabale configurations
Reload Nginx to enable the site configurations.
sudo systemctl reload nginx
Tips
Logs for troubleshooting
Nginx access log is available at /var/log/nginx
Vouch Proxy log is in the image.
docker logs vouch-proxy
tail
like command is,
docker logs --follow vouch-proxy
If you need debug log for Vouch Proxy, set vouch.jwt.logLevel to debug
in config.yml
allowAllUsers
The default configuration offers to specify the domains.
For example,
- Application: app.example.com
- Vouch Proxy: vouch.example.com
- Users’ mail addresses domain:
@mail.example.jp
config.yml
should be
vouch:
domains:
- example.com
- example.jp
If the users’ mail addresses have a variety of domains, you have to list all of them to permit access.
This style works when you need to restrict users based on mail domains.
Instead of listing the domains, allowAllUsers: true
is an option to accept anybody who is authenticated on the IdP side.
Even with this style, you still need to specify which domain to use for the cookies. This will be the domain to be protected, i.e., callback and application domain.
vouch:
allowAllUsers: true
cookie:
domain: example.com
Less frequent authentication
If you feel there are too frequent redirections to the OIDC provider, you can extend the expiration of jwt.
Extend vouch.jwt.maxAge
and vouch.cookie.maxAge
.
In my case, it’s 900 minutes (15 hours) to aim for the “once a day on the business hours”.
vouch:
cookie:
maxAge: 900
jwt:
maxAge: 900
AzureAD config sample
To use AzureAD, ask AAD admins to register the web application and receive client_id and client_secret.
# Vouch Proxy configuration
# bare minimum to get Vouch Proxy running with Azure AD
# https://github.com/vouch/vouch-proxy/issues/290
vouch:
# set allowAllUsers: true to use Vouch Proxy to just accept anyone who can authenticate to Azure AD
allowAllUsers: true
cookie:
# allow the jwt/cookie to be set into http://yourdomain.com (defaults to true, requiring https://yourdomain.com)
# secure: false
# vouch.cookie.domain must be set when enabling allowAllUsers
domain: example.com
oauth:
provider: azure
client_id: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
client_secret: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
auth_url: https://login.microsoftonline.com/cccccccccccccccccccccccccccccc/oauth2/v2.0/authorize
token_url: https://login.microsoftonline.com/cccccccccccccccccccccccccccccc/oauth2/v2.0/token
scopes:
- openid
- email
- profile
callback_url: https://vouch.example.com/auth
azure_token: id_token # access_token and id_token supported