Protecting your Internal Services with Nginx and Oauth2

When you have multiple internal services in your company, some of which your coworkers need to access, using a SSO mechanism is good practice, so that people don’t need to manage multiple passwords.
One of the most common SSO mechanism is Oauth, for example if your company uses Google Apps.

In our infrastructure in my current startup, we have multiple services such as pgWeb (postgres UI), Concourse web UI and other internal tools that people in our company need to use.
All those services run in Docker containers, and are interconnected via a VPN. I could issue OpenVPN keys to everyone (so that they can access the services directly on the VPN), but that would be a pain to manage, and it is a pain for users to remember VPN IP addresses to enter in their browser.

So here is how we expose and protect all our internal services. We create a public subdomain (DNS) for each of the service. Each subdomain points to the same server, our central server. On our central server, we have an Nginx docker container listening on port 80, and depending on the subdomain in the request, routes it to the right container.

Then, we use the awesome Nginx auth_request feature. This means that Nginx can verify authentication in a subrequest to a 3rd party service. For this 3rd-party service, we use bitly Oauth2 proxy (https://github.com/bitly/oauth2_proxy), inside a docker container. We also created an oauth2 client in google cloud, and we use this oauth2 client id, secret and redirect-url.
Instead of forwarding all traffic through this proxy, we can just send a subrequest to it, in Nginx config.

Then, we configured our Nginx load balancer to use this Oauth2 proxy as an authentication backend. When authentication is validated, the proxy_pass sends the traffic to its destination.

Concretely, here is how it looks like:

docker-compose.yml for oauth2 proxy + nginx:


version: '2'
services:
load-balancer:
image: nginx
ports: ["80:80"]
volumes: ["./example.conf:/etc/nginx/conf.d/example.conf:ro"]
web-server:
image: nginx
volumes: ["./index.html:/usr/share/nginx/html/index.html"]
#NOTE the –upstream settings is useless (we do not proxy requests through oauth2-proxy, we only use it as authentication backend of nginx) but it must be present
authproxy:
build: .
command: /usr/bin/oauth2_proxy –upstream=http://127.0.0.1:80 –http-address="0.0.0.0:4180" –redirect-url=${REDIRECT_URL} –email-domain=${EMAIL_DOMAIN} –cookie-secret=${COOKIE_SECRET} –client-secret=${OAUTH2_SECRET} –client-id=${OAUTH2_ID} –cookie-secure=false

Check the full github repo: https://github.com/francoisruty/fruty_nginx-oauth
to see:
– the nginx .conf file
– the Dockerfile for oauth2 proxy
– the README to test this dev setup on your laptop in 5min

Thanks to bitly Oauth2 proxy and Nginx auth_request feature, you can, with just 2 containers (Nginx “front” web server with all incoming traffic going through it, and Oauth2 proxy), protect all your internal services behind Oauth2 authentication, at the cost of adding, for each service to protect, a block in Nginx config.

7 thoughts on “Protecting your Internal Services with Nginx and Oauth2

  1. Hi There, very nice article about a very useful topic. If there was a hosted example on github.com, it would be really useful. Because the blanks (…) in the commands like

    command: /usr/bin/oauth2_proxy –upstream=… –http-address=”0.0.0.0:4180″ –redirect-url=… –email-domain=… –cookie-secret=… –client-secret=${OAUTH2_SECRET} –client-id=${OAUTH2_KEY} -cookie-domain=…

    I suppose are to be filled up per use case, but a sample would be really nice

    Thanks for writing!

    Like

    1. Hi Mihir, thanks for your comment! Here is more information:

      –upstream= where you want to forward (URL) once login is successful
      –redirect-url= your oauth client callback url
      –email-domain= choose here the email domain you want to allow. for example: “yourcompany.com” so that only email @yourcompany.com will be able to log in
      –cookie-secret= choose here a complicated string
      -cookie-domain= your domain name

      Like

  2. Great article!

    Is it possible to use this to not only authenticate users but also request access (scopes) from Google so my backend server could call Google APIs? I’m thinking if I could use this to be the front end authentication for my web app. For that to work for me I would need to request the following scope:
    https://www.googleapis.com/auth/youtube.force-ssl

    Also, would my back end application have access to the profile scope of the Google user (Name, email, etc)?

    Thanks,

    Nick

    Like

    1. Hello, thanks for your message!
      I don’t know if being authenticated with Oauth allows you to query various Google APIs.
      However, Oauth authentication gives you info about the user, yes! Just add a few auth_request_set in Nginx config, and then parse the forwarded info with your backend!

      For example, in your nginx config block:

      location / {
      auth_request /oauth2/auth;
      error_page 401 = /oauth2/sign_in;

      # Once authenticated, sets cookies and headers for upstream parsing
      auth_request_set $email $upstream_http_x_auth_request_email;
      auth_request_set $user $upstream_http_x_auth_request_user;
      auth_request_set $token $upstream_http_x_auth_request_access_token;
      add_header X-Email $email;
      proxy_set_header X-Email $email;

      add_header X-User $user;
      proxy_set_header X-User $user;

      auth_request_set $auth_cookie $upstream_http_set_cookie;
      add_header Set-Cookie $auth_cookie;

      proxy_http_version 1.1;
      proxy_connect_timeout 300;
      proxy_send_timeout 30;
      proxy_read_timeout 300;

      proxy_pass_request_headers on;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection “upgrade”;

      proxy_pass http://EXAMPLE_BACKEND_SERVICE;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Scheme $scheme;
      }

      Like

      1. The only difference from using Google just for authentication and using a Google API is what scope you pass it. Is Nginx able to pass Google an array of scopes?

        Like

      2. When you proxy_pass you can add any meta to your request (see my previous reply about forwarding oauth profile info) so that would include token stuff and such. So yes I think this should be possible

        Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s