OAuth2 for Nginx

Recently, I needed a way to put authentication in front of an nginx instance that would allow logging in through oauth2/openid connect. Luckily, a coworker of mine had already done something similar so I knew what components I’d need:

I originally wanted to also create a little test-setup inside docker-compose. Sadly, that would have required installing some sidecar components into the nginx container or something similar. But given that my target setup was running inside a VM anyway where I could use nginx to just proxy other local services, I went with the following setup.

OAuth2 will be provided by dex in this little setup simply because we can easily “fake” a user account using its staticPasswords setting. You can start a local dex instance with the following command:

$ /usr/bin/docker run --rm --name dex -p 5556:5556 \
  -v /var/lib/dex:/var/lib/dex \
  -v /etc/dex/dex.yaml:/etc/dex/dex.yaml \
  quay.io/coreos/dex:v2.10.0 serve /etc/dex/dex.yaml

This assumes that there is a /etc/dex/dex.yaml file with the following content:

---
issuer: http://{{ fqdn }}/dex
web:
  http: 0.0.0.0:5556
storage:
  type: sqlite3
  config:
    file: /var/lib/dex/dex.db
enablePasswordDB: true
staticClients:
  - id: "oauth2_proxy"
    redirectURIs:
      - "http://{{ fqdn }}/oauth2/callback"
    name: "OAuth Proxy"
    secret: "{{ dex_client_secret }}"
staticPasswords:
- username: "{{ team_username }}"
  email: "{{ team_email }}"
  hash: "{{ team_password_bcrypted }}"
  userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"

We’ve also defined a static client here to allow oauth2-proxy to be able to connect to dex.

On the same host, start oauth2-proxy pointing to this dex installation as backend:

/usr/bin/docker run --rm --name jenkins-oauth2-proxy -p 4180:4180 kautsig/oauth2-proxy:latest \
  -client-id oauth2_proxy \
  -client-secret "{{ dex_client_secret }}" \
  -cookie-secret "{{ oauth2_cookie_secret }}" \
  -email-domain "*" \
  -provider "oidc" \
  -redirect-url "http://{{ fqdn }}/oauth2/callback" \
  -oidc-issuer-url "http://{{ fqdn }}/dex" \
  -cookie-secure=false \
  -set-xauthrequest \
  -http-address "0.0.0.0:4180"

Now, all that’s left is the nginx instance where we want to have the authentication:

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    location /dex/ {
        proxy_pass http://localhost:5556/dex/;
        proxy_set_header   Host              $host;
        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;
    }
    location /oauth2/ {
        proxy_pass http://localhost:4180/oauth2/;
        proxy_set_header   Host              $host;
        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;
    }

    location / {
        auth_request /oauth2/auth;
        error_page 401 = /oauth2/start;
        # If I want, I can now forward the auth information to a proxied
        # service.
        auth_request_set $user $upstream_http_x_auth_request_user;
        proxy_set_header X-Forwarded-User $user;
    }
}

By now you’ve probably noticed all those {{ variable_name }} placeholders in the code-samples above. In order to test this setup I’ve created a little Vagrant box that uses Ansible for the provisioning. You can find the result on Gitlab.

Running vagrant up will give you a demo server on 192.168.50.4 where you can log in using team@team.com as e-mail and team as password.

Please note that this is just a basic configuration. Cleanup etc. have been left out in order to keep the example itself concise.

Do you want to give me feedback about this article? Please send it to comments@zerokspot.com.