Skip to content
This repository has been archived by the owner on Mar 3, 2023. It is now read-only.

Showmax/showmax-nginx-sso-cookie-auth

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 

Repository files navigation

Showmax SSO cookie nginx module

This is a LUA module for nginx which will verify authentication cookie presented in requests. If cookie is missing or is invalid, it will send a redirect to SSO (single-sign-on) service.

Requirements

Please make sure that your NGINX installation contains support for LUA files. For example Debian includes LUA in nginx-extras package (nginx-light, nginx, nginx-full won't work).

Using module inside nginx

It caters for domain it is running, so no configuration is necessary. Use is thus super simple, this is example of grafana service:

	location / {
		access_by_lua_file /opt/showmax/nginx-sso-cookie-auth/sso_cookie_auth.lua;

		proxy_pass http://127.0.0.1:6081;
		proxy_set_header Host $http_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 "https";
	}

So adding

		access_by_lua_file /opt/showmax/nginx-sso-cookie-auth/sso_cookie_auth.lua;

to your location configuration should be enough to get you protected (+ you have to install this package).

Overriding return host

Return URL is normally taken from nginx and you don't need to care about it. Sometimes you need to able to override it. Especially when you are behind other proxy. You can do it with

           set $sso_return_url 'https://kibana.showmax.cc';

Require certain audience

Part of the authentication data is also audience. For list of values, please check the SSO project.

           set $sso_allowed_audience 'showmax';

Will require to have Showmax account to get access. You don't need to specify this though as it is default value. So you don't need to change anything in your configuration if it is in front of internal service.

If you don't care for audience (aka looking for valid account only) use any as value:

           set $sso_allowed_audience 'any';

You can also specify multiple values. They are treated as having OR between then. So for example

           set $sso_allowed_audience 'showmax, recombee';

Will give access to account which is either in showmax, or in recombee or both (first match will win anyway).

Note: I was thinking (and initially implemented AND option). But it turned out, that OR would be more useful, at least for now. We can add it later with e.g. sso_required_audience option.

Authentication data

We are now passing authentication cookie Showmax-Auth-Data which contains JSON with additional data. You can find description of the fields in https://git.showmax.cc/ops/ops-sso project. Some of data are copied for convenience into request headers. Those are:

  • X-Forwarded-User == uid
  • X-Forwarded-Audience == list of authenticated audience values from SSO

Getting module ready

As you can see, module has multiple configuration options hard-coded. We have decided not to go with external configuration file, as that would tremendously complicate deployment (you would need to set Lua paths etc). Our deployment looks like this:

  • we pull this repository as upstream branch into our internal repo, which is following the gbp standards.
  • Debian patch queue is used for replacing placeholders with actual configuration.
  • resulting Debian package is taken by Puppet and deployed to appropriate hosts

Limit allowed domains

There is a variable called sso_domain_match. It's purpose is to make sure, that you are allowing only domains you cater for as also to allow support for multiple domains with the same deployed module. The line reads as follows in our deployment:

local sso_domain_match = ngx.re.match(ngx.var.host, "showmax\.(cc|io|com)$")

Signing keys

Module is designed to work with multiple domains. Thus signing keys stored in keys variable is a hash, where key is the signing domain. For example configuration for two TLDs will look like:

local keys = {}
keys["showmax.cc"]  = "PheeFahsiTh1Tooreemuegh9gie7epho0Lahjoh9tia1ceef0neiFi4WeiPiD9ah"
keys["showmax.io"]  = "ail9iegheiTh2shooyie3roediagash4aelaishahchoghah0gae7rao0dohch6p"

Reading auth cookies

You need to set names of cookies which are going to be set by your SSO server (see down below). Module expects to get cookie values in cookie_auth_data and cookie_auth_sign variables. So simple fetch the appropriate cookies via nginx interface:

local cookie_auth_data = ngx.unescape_uri(ngx.var.cookie_showmaxAuthData)
local cookie_auth_sign = ngx.unescape_uri(ngx.var.cookie_showmaxAuthSign)

Generating auth cookies

Authentication cookie is generated by SSO service, which is simple Sinatra application in our case. This app is quite environment specific, so we are not really pushing to anybody. In our case it consults our LDAP for username/password and then does a TOTP (second factor authentication) check.

The actual code, which is setting cookies on our side looks like this (exact copy and past from the source):

      expiration = Time.now + 24 * 3600
      msg = expiration.to_i.to_s

      data = Base64.urlsafe_encode64(MultiJson.dump({
        exp:         expiration.to_i,
        auth_source: session[:auth_source],
        uid:         session[:uid],
        dn:          session[:dn],
        email:       session[:email],
        name:        session[:name],
        aud:         session[:aud],
      }))
      data_sign = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), settings.cookie_secret, data)

      response.set_cookie("showmaxAuthData", {
        value:  data,
        domain: settings.cookie_domain,
        path:   '/',
        httponly: true,
        secure: true,
      })

      response.set_cookie("showmaxAuthSign", {
        value:  data_sign,
        domain: settings.cookie_domain,
        path:   '/',
        httponly: true,
        secure: true,
      })

Where settings.cookie_domain resolves to appropriate domain, such as .showmax.cc. You can also see use of session object. Which is an encrypted session between SSO server and the client. It contains information such as uid etc. You can replace this with your database for example. settings.cookie_secret is the same secret you would set to keys for this domain (.showmax.cc in this case) in Lua module.

Images behind SSO

Placeholder image will be returned for request believed to be requesting an image (based on Accept header). We have implemented this as we got quite a lot of confused users, when they have been embedding SSO protected images into their documents.

Placeholder image is an Public Domain Fire Ball Icon.

About

Simple cookie based authentication (lua) module for nginx

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages